| name | propose-cards |
| description | Surface typed UI cards in the console chat via the propose_card tool. Use when the user needs to take a configuration action (set up a channel, configure a webhook, install a skill) or when you want to post a standout notice. Covers when to use a card vs. prose, which kind to pick, and how the tool blocks until the user decides. |
| requiresTools | ["propose_card"] |
Propose Cards
The console chat renders typed cards inline when you call propose_card. A card is a small UI that either shows an informational update or presents a CTA button that, when the user approves, commits a specific change against the console API.
You don't render cards in prose — you call propose_card with a discriminated kind, and the UI takes care of the rest. Interactive kinds block your turn until the user clicks Approve or Dismiss, so the tool's return value is the outcome (applied / dismissed / failed) you should plan the next turn around.
Choosing a Kind
| Situation | kind |
|---|
| User wants to set up Slack / Telegram / AgentMail / AgentPhone / API channel | channel_setup |
| User needs to point Telegram at the edge's public webhook URL | webhook_configure |
User wants to install a skill — you have the skill body and a skills/... storage key | skill_enable |
| You need a standout status line (success / warning / note) that isn't a decision | info |
Use prose, not a card, when:
- You're answering a question, explaining a concept, or summarizing.
- The user already applied the change and you're confirming the outcome.
- The needed action is outside the console's jurisdiction (e.g. rotating credentials — tell them to go to
/console).
Blocking Semantics
Interactive kinds (everything except info) block your turn until the user clicks Approve or Dismiss. You can only have one card pending at a time, and the user cannot type new prompts while it's pending. Plan accordingly:
- Don't stack cards. If two decisions are needed, emit the first, wait for the result, then emit the second — not both at once.
- Honor the outcome. If the return is
status: "dismissed", do not re-propose the same card unprompted. Ask the user what they want to change.
- If it failed, surface the detail. The
detail field explains why the commit failed. Show it to the user so they can fix credentials, config, etc.
kind: "info" does not block. It resolves immediately; use it to post status updates that stand out from prose.
Picking Fields Per Kind
channel_setup — pass channel and any non-secret values under prefill (display names, webhook paths). Never put tokens or secrets in prefill. The user types secrets into the real setup form, which opens when they click the CTA.
webhook_configure — pass target: "telegram". The card fetches the expected and current URLs from the edge and shows the diff.
skill_enable — skillKey must start with skills/ and include a filename. The user sees the full skill body as a code diff before approving.
info — only title, body, and optional severity (info | warn | success). No CTA.
Out of Jurisdiction
Do not try to use cards for these:
- Credentials — tokens, API keys, signing secrets. Human-only; point the user at
/console.
- Workflows and crons — those land via file writes in
.agent/workflows/ and .agent/crons/, not cards.
- Self-config changes — use
get_setup + patch_setup (see the self-config skill). Cards are for operator-facing actions, not for agent self-modification.
Examples
User asks to connect Slack:
{
"kind": "channel_setup",
"channel": "slack",
"title": "Connect Slack",
"summary": "Set up Slack so the agent can receive messages from your workspace."
}
On applied, confirm and offer the next step. On dismissed, ask what they want to change rather than retrying.
Standout confirmation that isn't a decision:
{
"kind": "info",
"severity": "success",
"title": "Slack connected",
"body": "Incoming messages will now route through the main agent."
}
Return Shape
Every propose_card call returns:
{ "cardId": "channel-abc-123", "status": "applied", "detail": "Slack bot verified" }
status: "applied" — the user clicked the CTA and the commit succeeded.
status: "dismissed" — the user dismissed the card, or closed the setup dialog without verifying.
status: "failed" — the CTA was clicked but the commit threw; detail explains why.
Read this and plan your next step from it; do not ask the user to repeat what the outcome was.