go-server: Rich Interactions runtime + choices kind (AskUserQuestion) - #486
Merged
Conversation
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 detectedLatest commit: 7be88ff 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 |
brentrager
enabled auto-merge (squash)
August 18, 2026 22:55
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.
What
Ports the Rich Interactions framework runtime + the
choiceskind (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
InteractionKindiface,InteractionKindscatalog, per-connection park/resumeInteractionRegistry(analog ofConfirmationRegistry)go/server/interaction.gochoiceskind:request_choicesraise tool schema,validateChoices, fallback directive (capabilitychoice_chips) — mirrorschoices.rsgo/server/choices.gogo/server/turn_runner.gosupportscapture atcreate_conversation_session+ thesubmit_interactiondispatcher action (validate → resume, or retryableinteraction_invalid)go/server/dispatcher.gointeraction_required/interaction_invalidevent buildersgo/server/protocol.gogo/server/server.goRich path: the agent calls
request_choices→ the tool parks the turn (blocks awaitingsubmit_interaction) while the server emitsinteraction_required→ the client submits → server validates via the kind → the parked raise resumes with the canonical payload. Invalid submits emit a retryableinteraction_invalidand the turn stays parked.Fallback path: a session that did not declare
choice_chipsgets the kind's conversational fallback directive as the tool result — no park, nointeraction_required.The
choiceskindrequest_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 XORother), multi-select ≥1, blankotherdropped, all errors reported one-pass. Every question carries the implicit free-textotherescape hatch.Tests
choices_test.go): single/multi/Other, unknown-label → field error, required + single-answer rules, format-only degradation, parse-contract enforcement.choices_valuesvalidated againstchoices_specequalschoices_payloadfromspec/conformance/fixtures.json— the same contract the Rust reference checks.interaction_e2e_test.go): rich raise →interaction_required→submit_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_interactiontool (so a text-channel model can submit a structured answer). Forchoicesthis has no host-side attach effect, so the Go fallback stops at the conversational directive — the model collects and continues in text. The richsubmit_interactionaction (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