web: choices interaction card (AskUserQuestion) for the widget + web-chat example - #478
Merged
Conversation
…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 detectedLatest commit: 9ab1a00 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
choicesRich 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 achoicesinteraction_required.Solution
A web-SDK card + example wiring. No protocol/client change —
submitInteraction()already speaks every kind.ChoicesCard(typescript/src/react/components/ChoicesCard.tsx, exported from@smooai/smooth-operator/react): renders each question'sheader, prompt, and option chips — radios whenmultiSelectis 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 callsonSubmit; a Decline button callsonDecline(caller sendsdeclined: true). Per-questioninteraction_invaliderrors re-render with the turn still parked. Accessible:fieldset/legendper question, labelled native radios/checkboxes, keyboard-navigable, focus moves into the card on mount. Styled with the SDK'ssmooth-chat__*+--smooth-*CSS-variable convention.interactionCardsmap (kind→ card) is exported;choicesis registered there. Registering a card is how a client declares the kind's render capability.choice_chipscapability in everycreate_conversation_session(supports: ['choice_chips']), surfaces the parked interaction from the turn stream (mirroring thewrite_confirmation_requiredapproval bar), and renders the card in the overlay slot above the composer, themed to the example's dark palette via--smooth-*token overrides.typescript/src/generated/types.tsfromspec/(addsChoicesSpec/ChoicesValues/ChoicesPayload; also picks up the already-merged optional-agentId+choice_chipsspec 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
typescript/test/react/choices-card.test.tsx, 7 cases): render a sample spec, select single/multi options + Other, assert the builtsubmitInteractionpayload matches the schemaValues; submit-gating; decline. Full SDK suite green (53/53).@smooai/smooth-operatortypecheck + build green; web-chat example typecheck +vite buildgreen.choicessupport shipping in each engine (separate PRs). Verified via unit tests + a mocked interaction flow through the example hook.🤖 Generated with Claude Code