Skip to content

feat(timeline): agent response cards + timeline UI unification - #256

Draft
tyreseluo wants to merge 11 commits into
mainfrom
feat/agent-response-cards-ui-unify
Draft

feat(timeline): agent response cards + timeline UI unification#256
tyreseluo wants to merge 11 commits into
mainfrom
feat/agent-response-cards-ui-unify

Conversation

@tyreseluo

Copy link
Copy Markdown

Summary

Adds a full agent-response card system to the timeline for Octos AI-agent output, and unifies the timeline widgets onto the RBX_* design tokens.

Octos agent output now renders as native, structured cards instead of plain text bubbles. All cards ride the single org.octos.app wire channel (dispatched by type in render_octos_app_card), with graceful fallback to body text for unknown types.

Card renderers

Card What it shows
tool_call / tool_activity a single tool invocation / a per-turn list of tool calls
diff_view unified diff, green/red lines
task_tree nested sub-agent task list
pipeline staged workflow flow
run_summary per-turn token / cost / reasoning footer
user_question prompt + option buttons (reuses the org.octos.actions loop)
plan checklist with per-item status
goal objective + token-budget bar
artifact agent output files
image / visual / chart an embedded mxc:// image + caption
weather / mission_room mini-app cards
swarm_event swarm-supervisor harness events (octos's own org.octos.swarm_event key)

Card colors come from the RBX_* tokens via a new utils::vec4_to_hex (→ HTML <font color>); glyphs are restricted to the message font stack's real coverage; costs parse as number-or-string (Matrix canonical JSON forbids floats).

UI unification

Migrates the timeline widgets (reactions, link preview, context menu, jump-to-bottom, edited indicator, streaming, tombstone, upload progress, RobrixIconButton, read receipts) onto the semantic RBX_* design tokens (teal RBX_ACCENT primary). Adds dark code-syntax tokens.

Notable fixes

  • Condense-render bug: an org.octos.app card is now detected up-front and forced into the full Message template, so consecutive bot cards no longer collapse into CondensedMessage and fall back to plain body text.
  • Glyph tofu: plan/goal glyphs restricted to the font stack's coverage (ballot/shade chars → check/half/open circle + blocks).
  • CJK in inline <code>: uses a CJK-capable code font so Chinese in code isn't tofu.

Docs

Adds the agent-message-response-cards design doc + task spec, and an octos-side emitter design note.

Related

The octos-side emitter (which auto-projects runtime events → these cards over Matrix) is a separate change on the octos repo (branch feat/matrix-app-card-emitter), auto-emitting tool_activity · plan · run_summary · artifact · diff_view, all coalesced per turn.

Testing

Builds green; merged with latest main (6 conflicts resolved); unit tests pass. Cards verified end-to-end against a local Octos gateway.

🤖 Generated with Claude Code

tyreseluo and others added 11 commits July 13, 2026 16:37
Design and implement four Octos/agent response-message UI styles in the
room timeline (design: docs/superpowers/specs/2026-07-10-agent-message-response-cards.md):

- CodeOutputCard (§4.7): dark RBX_CODE_BG syntax-highlighted panel for
  fenced bot code, plus the dark syntax token set in design_tokens.rs
- ApprovalCard (§4.6): amber RBX_WARNING recipe with Pending/Critical
  badges and a tool/expiry meta line
- Streaming indicator (§7): teal LoadingSpinner shown while a reply is
  live; removes the trailing U+25CF cursor and keeps live streams
  redrawing so it animates in full-snapshot markdown mode too
- AgentMessageCard v1 (§4.5): parsed status -> active StepChip, RBX_*
  answer card, "Bot" badge, avatar online dot
- agent_render_state scaffold (AgentCardKind) + unit tests

Also fixes media downloads: the File/Audio/Video download button now
routes through the working DownloadAndSaveFile path (was DownloadMediaToFile
via get_media_content, which fails on non-ASCII Content-Disposition headers,
so only the inline link worked); the redundant inline mxc link is dropped and
the button is retinted to RBX_ACCENT.

Note: room_screen.rs also carries its share of the RBX design-token
unification (message text/rows/@mention/action buttons/notices/thread
summary/read-marker/dividers/scrims), bundled here since it is one file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Migrate the room-timeline off legacy styles.rs COLOR_* / hardcoded hex to
the RBX_* design-token system (teal accent), from a parallel UI audit.

styles.rs token re-points (one place, many surfaces):
- message/username/timestamp text -> RBX_FG_PRIMARY / RBX_FG_TERTIARY
- notice + small-state text -> RBX_FG_SECONDARY / RBX_FG_TERTIARY
- link hover -> RBX_ACCENT; unread badge -> RBX_ACCENT
- link-preview surfaces -> RBX_BG_SURFACE_SUBTLE / RBX_BG_HOVER

Shared widgets:
- reaction pills: green self-state / yellow hover -> teal RBX_ACCENT(_SOFT)
  selection chip + RBX_NEUTRAL_BG / RBX_STROKE_SOFT
- jump-to-bottom FAB + unread badge -> teal accent
- context menu -> RBX surface/scrim/danger tokens
- link preview: raw #0000EE link -> RBX_LINK, card chrome -> RBX
- (edited) marker: brand purple -> RBX_FG_TERTIARY
- inline code/quote bg, mention pill, image placeholder -> RBX inset tokens
- tombstone footer, upload progress, read-receipt count -> RBX

Visual-only; no logic changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The base RobrixIconButton still used legacy blue (COLOR_ACTIVE_PRIMARY),
so every icon button that doesn't override its color inherited blue.
Retint the base to the teal design-token recipe (mirrors SettingsPrimaryButton):

- draw_bg color/hover/down -> RBX_ACCENT / RBX_ACCENT_HOVER / RBX_ACCENT_PRESSED
- draw_icon / draw_text -> RBX_FG_ON_ACCENT
- border_radius 4.0 -> RBX_RADIUS_SM

App-wide: all default icon buttons (settings, modals, timeline) are now teal.
The RobrixPositive/Negative derived buttons keep their own green/red overrides.
Visual-only; no logic changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Octos ships a purpose-built agent-to-app card channel over Matrix
(`content["org.octos.app"] = {type, version, initial_state}`, designed
for the robrix2 consumer), but robrix was only handling a nonexistent
`org.octos.splash_card` key and never rendered it.

Add a client-side type registry that renders known app types natively and
falls back to the plain message body for unknown/invalid types:

- mission_room: goal + phase badge + task/agent/pending/decision/blocker
  lists (tolerant of free-form item shapes); shared org.octos.actions
  buttons render below via the existing action_button_row
- weather: location + condition glyph + temperature

The card uses the RBX design tokens (surface + tight radius + accent
badge). Rendered only for full (non-condensed) messages so a condensed
row without the slot never blanks. Supersedes the dead splash_card path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the org.octos.app renderer (A1) with eight agent-output card types,
each dispatched by `type` into the shared octos_app_card slot:

  tool_call · diff_view · task_tree · pipeline · run_summary ·
  user_question · plan · goal

- Colors come from the RBX_* tokens via a new utils::vec4_to_hex helper
  (→ HTML <font color>), never hardcoded hex.
- diff_view mirrors octos DiffPreview; task_tree/plan mirror the task &
  plan records; goal shows a token-budget bar; run_summary is a per-turn
  cost/reasoning footer (cost parsed as number-or-string, since Matrix
  canonical JSON forbids floats).
- user_question styles the prompt; its clickable options reuse the
  existing org.octos.actions button + action_response loop.
- Glyphs are restricted to the message font stack's real coverage
  (IBMPlexSans + LXGWWenKai + NotoColorEmoji) to avoid tofu.

Also fix a latent rendering bug: an org.octos.app message is now detected
up-front (bot senders only) and forced into the full Message template, so
consecutive bot cards no longer collapse into CondensedMessage and fall
back to plain body text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Capture how octos should project runtime UI-protocol events (tool/*,
DiffPreview, task/updated, workflow phases, token_cost_update,
user_question/requested, plan/updated, session goal) into org.octos.app
Matrix cards via a projection bridge — distinct from the LLM-called
send_app_card tool. Includes per-type source→initial_state maps and the
Matrix-canonical-JSON no-floats constraint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add two more agent-output cards to the timeline:

- artifact (org.octos.app type=artifact) — a list of agent output files,
  each with a kind glyph, kind/status tail, and optional path. Mirrors
  octos UiAgentArtifact / TaskArtifactRecord.
- swarm_event — octos's OWN wire key org.octos.swarm_event (envelope
  schema octos.harness.event.v1), which octos already emits into
  swarm-supervisor rooms. Renders one harness event styled by kind
  (progress / phase / artifact / validator_result / retry / failure).
  Detected up-front alongside org.octos.app so it also renders in the
  full (non-condensed) Message template.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the emitter design doc: robrix now renders octos's already-emitted
org.octos.swarm_event key, and the swarm progress field (an f64) must be
sent as an integer percent — a fractional value fails Matrix canonical
JSON, so octos's own swarm_event send 400s otherwise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Align the agent-response-cards branch with upstream main (15 commits,
including #255 Cherry-style message meta band + the makepad fork switch).

Conflicts (all in src/home/room_screen.rs) resolved:
- Keep both our AgentCardKind scaffold and main's clipboard_text_for_message_body.
- bot_badge background -> RBX_ACCENT_SOFT (main) for contrast with the
  RBX_ACCENT badge label.
- Remove our bot_metadata_footer from both message templates: superseded by
  main's MessageMetaBand / band_metadata; no Rust references the footer widgets.
- Streaming branch: keep both the ① streaming indicator and band_metadata =
  stream_meta.
- Keep both the compute_agent_render_state and invite-result unit tests.
- Visual card: org.octos.app type=image/visual/chart embeds an mxc image in
  the card frame, reusing the native image pipeline (async fetch + viewer).
- tool_activity card: a per-turn LIST of tool calls, rendering octos's
  coalesced tool_activity instead of one tool_call card per tool.
- Render fixes: plan/goal glyphs restricted to the message font stack's real
  coverage (ballot/shade chars were tofu -> check/half/open circle + blocks);
  inline <code> now uses a CJK-capable font so Chinese in code isn't tofu;
  plan items also accept a `description` field (octos update_plan / Codex shape).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ls card

Merge the per-turn tool_call / tool_activity / run_summary cards into a single
`org.octos.run` run detail, rendered as a collapsible strip BELOW the bot answer
(keeping the bot card) instead of as separate replacement cards. The superseded
render fns are kept for backward-compat with already-sent messages.

- Add expand/collapse toggle + per-event expanded state that survives PortalList
  virtualization (`expanded_octos_card_event_ids`, `toggle_octos_card_expanded`).
- `populate_octos_app_card` gains `collapsible` / `expanded` / `keep_bot_card`.
- `render_run_details_app_card`: collapsed "N tools" summary (+ "M failed"
  badge), expands to per-tool ✓/✗ list with truncated errors and a token/cost
  footer folded in from run_summary.
- Clear a stale typing notice locally once a message row is appended (appservice
  bots can lag the typing=false EDU).
- Robustness: char-boundary slice in `format_approval_expiry`, `saturating_add`
  on attacker-supplied token totals, depth cap in `count_task_states` to match
  the rendered tree, independent diff-truncation notices.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tyreseluo
tyreseluo marked this pull request as draft August 5, 2026 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant