Rich output rendering: GFM markdown, tool-result blocks, card blocks

Three layers:

1. GFM markdown — add remark-gfm so tables, task lists, strikethrough,
   autolinks render properly. Style table elements (overflow-aware
   container, muted header, divider rows). Render `[ ]` task list items
   as visible checkboxes.

2. Structured tool-result rendering — new `tool-result-renderers.tsx`
   dispatches by tool name to render a small UI block beneath each
   ToolCallCard:
   - list_tenants → table with status pills + plan column
   - get_tenant → tenant detail card
   - get_platform_stats → KPI tiles (total + per-status)
   - list_audit_log → timeline rows with actor_type + action
   - list_users → user list with role chips
   - suspend_tenant / activate_tenant → tenant card with action confirm
   ToolCallCard collapses by default — operators expand for raw JSON.

3. Custom ```card``` blocks the LLM can emit inline:
   - {"kind":"pill","status":"…"} — status pill
   - {"kind":"stat","label":"…","value":…} — stat tile
   - {"kind":"callout","tone":"info|warning|danger|success",…} — callout
   Malformed blocks fall through to the prose unchanged. Client strips
   well-formed blocks from prose and renders them as components.

Domain primer updated to teach the model the card schemas and remind it
NOT to re-render tool-result data as markdown tables (that's done
automatically — it should add commentary only).

Layers are independent: 1 + 2 always work; 3 is purely additive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jules
2026-05-01 20:39:06 +10:00
parent b9a163c7cc
commit 45fa130951
6 changed files with 758 additions and 12 deletions

View File

@@ -32,4 +32,12 @@ Things to keep in mind when assisting:
- Quotas / rate cards / billing config errors usually surface as 402/403 from /api/v1 endpoints — diagnose by checking the tenant's billing-config and api-metering quotas.
- The reference Phoenix app lives at \`reference/arcadia-app/\` in the workspace; its OpenAPI spec is at /api/openapi (sync via \`node ../lib-arcadia-client/scripts/sync-spec.mjs\`).
When the user asks something that maps to a tool, call it. When they ask about a concept, explain it from this primer in plain language. Write tools (suspend_tenant, activate_tenant) prompt the operator with an inline confirm card before they actually run — you do not need to ask in prose first; just call the tool and the user will see the confirmation UI. If the user denies a write, do not retry it; ask what they'd like to do differently.`
When the user asks something that maps to a tool, call it. When they ask about a concept, explain it from this primer in plain language. Write tools (suspend_tenant, activate_tenant) prompt the operator with an inline confirm card before they actually run — you do not need to ask in prose first; just call the tool and the user will see the confirmation UI. If the user denies a write, do not retry it; ask what they'd like to do differently.
Rich output (optional, use when it helps):
- You may use full GitHub-Flavoured Markdown — tables, task lists, code fences, etc.
- For inline UI accents, you may emit fenced \`\`\`card\`\`\` blocks containing one JSON object. The client renders them as small components and strips the block from your prose. Schemas:
- Status pill: \`{"kind":"pill","status":"active"|"suspended"|"deactivated","label"?:"text"}\`
- Stat tile: \`{"kind":"stat","label":"Tenants","value":42}\`
- Callout: \`{"kind":"callout","tone":"info"|"warning"|"danger"|"success","title"?:"…","body"?:"…"}\`
Tool results already render as rich tables/cards/timelines automatically — DO NOT re-render that data with markdown tables or cards. Refer to it as "above" and add only commentary.`