Skip to content

web: choices interaction card (AskUserQuestion) for the widget + web-chat example - #478

Merged
brentrager merged 1 commit into
mainfrom
choices-card-web
Aug 18, 2026
Merged

web: choices interaction card (AskUserQuestion) for the widget + web-chat example#478
brentrager merged 1 commit into
mainfrom
choices-card-web

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Problem

The choices Rich Interaction kind (structured multiple-choice ask, modeled on Claude Code's AskUserQuestion) landed on the Rust reference (#475) with its spec + validator, but there was no web renderer — a capable client had nothing to draw when the server emits a choices interaction_required.

Solution

A web-SDK card + example wiring. No protocol/client changesubmitInteraction() already speaks every kind.

  • ChoicesCard (typescript/src/react/components/ChoicesCard.tsx, exported from @smooai/smooth-operator/react): renders each question's header, prompt, and option chips — radios when multiSelect is false, checkboxes when true — plus an always-available free-text "Other" input per question (the ever-present AskUserQuestion escape hatch). Submit builds the canonical { answers: [{ header, options?, other? }] } and calls onSubmit; a Decline button calls onDecline (caller sends declined: true). Per-question interaction_invalid errors re-render with the turn still parked. Accessible: fieldset/legend per question, labelled native radios/checkboxes, keyboard-navigable, focus moves into the card on mount. Styled with the SDK's smooth-chat__* + --smooth-* CSS-variable convention.
  • Registry: a minimal interactionCards map (kind → card) is exported; choices is registered there. Registering a card is how a client declares the kind's render capability.
  • Web-chat example: declares the choice_chips capability in every create_conversation_session (supports: ['choice_chips']), surfaces the parked interaction from the turn stream (mirroring the write_confirmation_required approval bar), and renders the card in the overlay slot above the composer, themed to the example's dark palette via --smooth-* token overrides.
  • Regenerates typescript/src/generated/types.ts from spec/ (adds ChoicesSpec / ChoicesValues / ChoicesPayload; also picks up the already-merged optional-agentId + choice_chips spec descriptions).

Submit payload shape

{ "answers": [
  { "header": "Plan",     "options": ["Starter"] },        // single-select
  { "header": "Features", "options": ["Analytics","SSO"] }, // multi-select
  { "header": "Plan",     "other": "Enterprise" }           // free-text "Other"
]}

Verification

  • Unit tests (typescript/test/react/choices-card.test.tsx, 7 cases): render a sample spec, select single/multi options + Other, assert the built submitInteraction payload matches the schema Values; submit-gating; decline. Full SDK suite green (53/53).
  • @smooai/smooth-operator typecheck + build green; web-chat example typecheck + vite build green.
  • Not run against a live non-Rust server — end-to-end against those depends on server-side choices support shipping in each engine (separate PRs). Verified via unit tests + a mocked interaction flow through the example hook.

🤖 Generated with Claude Code

…chat example

Add a web renderer for the `choices` Rich Interaction kind. `ChoicesCard`
(exported from `@smooai/smooth-operator/react`) renders each question's header,
prompt, and option chips — radios for single-select, checkboxes for multi — plus
an always-available free-text "Other" escape hatch per question. Submit builds
the canonical `{ answers: [{ header, options?, other? }] }` values and resumes the
parked turn through the existing `submitInteraction()` verb; Decline sends
`declined: true`; `interaction_invalid` re-renders per-question errors with the
turn still parked. A minimal `interactionCards` registry (kind -> card) is
exported and `choices` is registered there.

The web-chat example declares the `choice_chips` capability in
`create_conversation_session` and renders the card in its overlay slot above the
composer (mirroring the write-confirmation approval bar), themed to the example's
dark palette via `--smooth-*` token overrides.

Regenerates `src/generated/types.ts` from spec/ (adds ChoicesSpec/Values/Payload;
also picks up already-merged optional-agentId + choice_chips descriptions). No
protocol/client change — the generic submit_interaction verb already speaks every
kind. Unit-tested: sample spec renders, single/multi-select + Other build the
schema Values shape, submit gating, decline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YbN45JeWDbcjvFqGJvmVD3
@changeset-bot

changeset-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9ab1a00

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@smooai/smooth-operator Minor
@smooai/smooth-operator-web-chat-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@brentrager
brentrager merged commit baaf07a into main Aug 18, 2026
2 checks passed
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