Skip to content

dotnet-server: Rich Interactions runtime + choices kind (AskUserQuestion) - #484

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

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

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Problem

Wave 2 of the polyglot Rich Interactions effort. The Rust reference merged (#475); the .NET (C#) server — Brent's tsx-dev-agent host — needs the same runtime + the choices kind. Until now the C# server had only the write-confirmation park/resume, not the generalized kind-agnostic interaction framework.

Solution

Mirror the Rust reference (interaction.rs + tools/interaction.rs + choices.rs + the server wiring) idiomatically in C#, reusing the existing ConfirmationRegistry park/resume pattern generalized.

Framework (dotnet/server/src/Interactions.cs)

  • IInteractionKind / InteractionCatalog — the extension seam + kind catalog (default = choices).
  • InteractionParkRegistry — session-keyed park store with peek vs resolve (an invalid submit re-prompts without consuming the park), the interaction analog of ConfirmationRegistry.
  • RequestInteractionTool / SubmitInteractionTool (both AIFunction) — the per-kind request_<kind> raise tool (rich → park + interaction_required; fallback → conversational directive) and the generic submit_interaction fallback tool.

The choices kind (dotnet/server/src/ChoicesKind.cs, mirrors choices.rs)

  • request_choices (1–4 questions, header ≤12 unique, 2–4 options, optional multiSelect), ChoicesValidator.Validate (all-answered; labels ∈ options; single = one label XOR other, multi = ≥1; blank other dropped; one-pass errors), fallback directive, capability id choice_chips.

Server wiring

  • ProtocolEvents.InteractionRequired / InteractionInvalid — double-nested data.data envelopes matching spec/events/*.schema.json.
  • submit_interaction dispatch in FrameDispatcher (validate → resume; invalid → retryable interaction_invalid, turn stays parked; decline path; INTERACTION_MISMATCH / NO_PENDING_INTERACTION; ownership-scoped).
  • Per-connection supports capture at create_conversation_session drives the rich-vs-fallback branch; teardown/cancel unpark the park.
  • The catalog is a DI-provided capability (AddSmoothOperatorServer registers InteractionCatalog.Default), so direct FrameDispatcher construction stays unchanged.

Verification

  • Validator unit tests (ChoicesInteractionTests, 14): ported from choices.rs's #[cfg(test)] block, plus the shared choices_spec / choices_values / choices_payload conformance fixtures fed through the C# validator (canonical payload must equal the fixture).
  • Park/resume integration tests (SubmitInteractionTests, 5) over the real WS harness: raise → interaction_requiredsubmit_interaction → resume; invalid → stays parked → resubmit; decline; text-only fallback (no capability → no card, conversational directive); no-pending error.
  • Full solution Release build + dotnet test: server unit 371, integration 90, conformance 36 (1 skipped), host 6 — all green. spec/interactions/choices.schema.json conformance already validated by the shared ConformanceTests.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YbN45JeWDbcjvFqGJvmVD3

…ion)

Wave 2 of the polyglot Rich Interactions effort — mirror the merged Rust
reference (#475) on the .NET (C#) server, Brent's tsx-dev-agent host.

Framework (generalizes the write-confirmation park/resume kind-agnostically):
- IInteractionKind / InteractionCatalog — the kind catalog (default = choices).
- InteractionParkRegistry — session-keyed park store (peek vs resolve, so an
  invalid submit re-prompts without consuming the park), the interaction analog
  of ConfirmationRegistry.
- RequestInteractionTool / SubmitInteractionTool — the per-kind request_<kind>
  raise tool (rich = park + interaction_required; fallback = conversational
  directive) and the generic submit_interaction fallback tool.
- ProtocolEvents.InteractionRequired / InteractionInvalid — the double-nested
  data.data envelopes matching spec/events/*.schema.json.

The choices kind (mirrors choices.rs exactly): request_choices (1–4 questions,
header ≤12 unique, 2–4 options, optional multiSelect), validate_choices
(all-answered; labels ∈ options; single = one label XOR other, multi = ≥1; blank
other dropped; one-pass errors), fallback directive, capability id choice_chips.

Server wiring: submit_interaction dispatch (validate → resume, invalid →
retryable interaction_invalid, decline path, INTERACTION_MISMATCH /
NO_PENDING_INTERACTION guards, ownership-scoped); per-connection `supports`
capture at create_conversation_session drives the rich-vs-fallback branch;
teardown/cancel unpark. The catalog is a DI-provided capability
(AddSmoothOperatorServer registers InteractionCatalog.Default), so direct
FrameDispatcher construction is unchanged.

Tests: xUnit validator unit tests (ported from choices.rs, + the shared
choices_* conformance fixtures through the C# validator) and a WS park/resume
integration suite (raise → interaction_required → submit → resume; invalid →
stays parked → resubmit; decline; text-only fallback; no-pending error).

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: 0ffc075

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 Patch
@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 fdd94bb 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