Skip to content

ts-server: Rich Interactions runtime + choices kind (AskUserQuestion) - #483

Merged
brentrager merged 1 commit into
mainfrom
choices-interaction-ts
Aug 18, 2026
Merged

ts-server: Rich Interactions runtime + choices kind (AskUserQuestion)#483
brentrager merged 1 commit into
mainfrom
choices-interaction-ts

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

What

Wave 2 of the full-polyglot Rich Interactions effort: ports the kind-agnostic Rich Interactions runtime + the choices kind (AskUserQuestion) to the TypeScript server, mirroring the merged Rust reference (PR #475).

Framework → TS mapping

Rust reference TS
smooth-operator/src/interaction.rs (InteractionKind, InteractionRegistry, InteractionRequest/Outcome/FieldError) typescript/server/src/interaction.ts
smooth-operator/src/tools/interaction.rs (RequestInteractionTool, SubmitInteractionTool, park channel) interaction.ts (requestInteractionTool, submitInteractionTool, InteractionParkRegistry)
smooth-operator/src/choices.rs (validate_choices, ChoicesKind, parse_questions) typescript/server/src/choices.ts
server interaction_required / interaction_invalid builders protocol.ts (interactionRequired, interactionInvalid)
handle_submit_interaction + PendingInteraction map frameDispatcher.ts (handleSubmitInteraction, interactionPark)
session_capabilities from supports supports persisted on the session (in-memory + Postgres), read in handleSendMessage

The raise tool parks the turn by awaiting a promise inside execute (the engine already runs turns as background tasks, so the reader stays free for submit_interaction) — the TS-idiomatic analog of the Rust tool awaiting a tokio channel. No engine changes.

The kind

request_choices — 1–4 questions (each a short ≤12-char unique header, 2–4 options, optional multiSelect) + a reason. validateChoices mirrors choices.rs one-for-one: every question answered, each label offered, single-select takes exactly one pick (label XOR other), multi-select ≥1, blank other dropped, one-pass errors. Capability id choice_chips. Invalid submits emit a retryable interaction_invalid and keep the turn parked. Text-only channels get the conversational fallback directive. The server hosts choices by default.

Tests

  • test/choices.test.ts — validator + parseQuestions contract + ChoicesKind wiring + protocol builders, cross-checked against the shared spec/conformance/fixtures.json choices fixtures (validate(spec, values) == payload.values).
  • test/submit-interaction.test.ts — WS integration over the real server: raise → interaction_requiredsubmit_interaction → resume; invalid → interaction_invalid → resubmit; INTERACTION_MISMATCH; NO_PENDING_INTERACTION; text-only fallback (no interaction_required).

cd typescript/server && pnpm build && pnpm test357 passed (38 files). Anchor guard: no stamped trees touched (TS server is versioned directly).

Changeset: @smooai/smooth-operator-server (minor).

🤖 Generated with Claude Code

Port the kind-agnostic Rich Interactions framework and the `choices` kind
to the TypeScript server, mirroring the merged Rust reference (PR #475).

Framework (interaction.ts): the InteractionKind seam, InteractionRegistry
of hosted kinds, a session-keyed InteractionParkRegistry (the interaction
analog of the write-confirmation registry), and the per-kind request_<kind>
raise tool + generic submit_interaction fallback tool. A raise on a session
that declared the kind's render capability parks the turn (the raise tool
awaits inside execute), the server emits interaction_required, and a
submit_interaction action validates + resumes (invalid -> retryable
interaction_invalid, never terminal). A text-only channel degrades to the
kind's conversational fallback directive.

choices kind (choices.ts): request_choices (1-4 questions, header <=12
unique, 2-4 options, optional multiSelect), validate_choices rules mirrored
one-for-one, capability choice_chips. Session `supports` is now persisted
(in-memory + Postgres) and gates rich-vs-fallback per kind; the server hosts
choices by default.

Tests: validator/parse/kind unit tests + a WS park/resume integration test
(raise -> interaction_required -> submit_interaction -> resume; invalid ->
interaction_invalid -> resubmit; mismatch; no-pending; text-only fallback),
all cross-checked against the shared spec/conformance choices fixtures.

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: b6ddbd2

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

This PR includes changesets to release 1 package
Name Type
@smooai/smooth-operator-server Minor

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 4da8e73 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