Skip to content

go-server: Rich Interactions runtime + choices kind (AskUserQuestion) - #486

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

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

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

What

Ports the Rich Interactions framework runtime + the choices kind (modeled on Claude Code's AskUserQuestion) to the Go LocalServer, mirroring the Rust reference (PR #475). Wave 2 of the polyglot rollout (Python/TS have already landed).

The Go server already had the park/resume machinery for write-confirmation HITL (ConfirmationRegistry, HumanGate); Rich Interactions is that same pattern generalized to be kind-agnostic. This builds on it.

How it maps onto Go

Concern File
Kind-agnostic framework: InteractionKind iface, InteractionKinds catalog, per-connection park/resume InteractionRegistry (analog of ConfirmationRegistry) go/server/interaction.go
The choices kind: request_choices raise tool schema, validateChoices, fallback directive (capability choice_chips) — mirrors choices.rs go/server/choices.go
Per-turn raise-tool registration (rich park vs conversational fallback, gated by declared capability); deferred toolCall chunk so ordering is deterministic go/server/turn_runner.go
supports capture at create_conversation_session + the submit_interaction dispatcher action (validate → resume, or retryable interaction_invalid) go/server/dispatcher.go
interaction_required / interaction_invalid event builders go/server/protocol.go
Teardown rejects parked interactions (fail-open to continue) go/server/server.go

Rich path: the agent calls request_choices → the tool parks the turn (blocks awaiting submit_interaction) while the server emits interaction_required → the client submits → server validates via the kind → the parked raise resumes with the canonical payload. Invalid submits emit a retryable interaction_invalid and the turn stays parked.

Fallback path: a session that did not declare choice_chips gets the kind's conversational fallback directive as the tool result — no park, no interaction_required.

The choices kind

request_choices { questions, reason }: 1–4 questions, each { question, header (≤12 chars, unique), options (2–4 { label, description? }), multiSelect? }. Validation: every question answered, each label offered, single-select = exactly one pick (label XOR other), multi-select ≥1, blank other dropped, all errors reported one-pass. Every question carries the implicit free-text other escape hatch.

Tests

  • Validator unit tests (choices_test.go): single/multi/Other, unknown-label → field error, required + single-answer rules, format-only degradation, parse-contract enforcement.
  • Shared fixture validation: choices_values validated against choices_spec equals choices_payload from spec/conformance/fixtures.json — the same contract the Rust reference checks.
  • WS park/resume integration (interaction_e2e_test.go): rich raise → interaction_requiredsubmit_interaction → resume; invalid-stays-parked → corrected-resume; and the no-capability conversational fallback.

cd go/server && go build ./... && go test ./... is green (also -race).

Deferred (vs the Rust reference)

The Rust fallback path also registers a generic agent-facing submit_interaction tool (so a text-channel model can submit a structured answer). For choices this has no host-side attach effect, so the Go fallback stops at the conversational directive — the model collects and continues in text. The rich submit_interaction action (the primary, tested contract) is fully implemented. Adding the agent-facing tool later is a small, isolated follow-up if a kind needs a structured fallback submit.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YbN45JeWDbcjvFqGJvmVD3

Port the Rich Interactions framework runtime + the `choices` kind to the Go
LocalServer, mirroring the Rust reference (PR #475). Wave 2 of the polyglot
rollout.

- interaction.go: kind-agnostic framework — InteractionKind interface, the
  InteractionKinds catalog, and a per-connection park/resume InteractionRegistry
  (the analog of the write-confirmation ConfirmationRegistry).
- choices.go: the `choices` kind (request_choices raise tool, validateChoices,
  fallback directive; capability choice_chips), mirroring choices.rs.
- turn_runner.go: registers one raise tool per hosted kind — a kind whose
  declared capability parks the turn (emit interaction_required, block awaiting
  submit_interaction), the rest degrade to the conversational fallback. The
  raise tool's toolCall chunk is deferred + emitted before the park (as the
  confirmation path does) so ordering is deterministic.
- dispatcher.go: captures `supports` at create_conversation_session and adds the
  submit_interaction action — validate via the kind, invalid -> retryable
  interaction_invalid (turn stays parked), valid -> canonicalize + resume.
- protocol.go: interaction_required / interaction_invalid event builders.
- server.go: teardown rejects parked interactions (fail-open to continue).

Tests: validator unit tests + shared choices-fixture validation, and a WS
park/resume integration suite (rich resume, invalid-stays-parked, and the
no-capability conversational fallback).

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

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 enabled auto-merge (squash) August 18, 2026 22:55
@brentrager
brentrager merged commit bdcab4e into main Aug 18, 2026
3 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