Repository navigation
Ask the user native questions and approvals on the Rust backend - #432
Merged
Merged
Conversation
A Claude lead (c8b8b265, 2026-10-22) called AskUserQuestion. The bridge sent item/tool/requestUserInput, the Rust account worker answered -32601 "Provider callback is not supported by this runtime", and the agent went on without the user's answer. The worker sent only item/tool/call and the VM token refresh to handlers. Every other provider request got the unsupported answer, although the card code and the /api/answer runtime existed: the worker never admitted a card. - The account worker admits each native approval or question of its account session as a pending request card: command, file and permission approvals, item/tool/requestUserInput and MCP elicitation. The card owns the answer, and the worker retires the request's callback record. A worker's question still gets the orchestrator refusal. A replay after a restart finds the same card and sends nothing. - The user's answer goes out once through the existing /api/answer runtime. The registry reply port matches the session by account and provider generation. A replacement worker of that generation has a new connection id, and the bridge still waits (the cause of #423 too). - serverRequest/resolved finds the cards that the Rust worker stores. It matched only the connection name that Python used. - The callback context accepts a turn that waits in "approval", so other tool calls of that turn work. A request without a turn id uses the thread's active turn. - A card of an ended provider generation expires at worker start. - Requests that a Linux VM actor relays keep the unsupported answer: the card answer path writes only to the account session. Tests: account_worker::tests::native_requests (fake provider; failed before the fix with the live -32601 answer) joins the short tier. Tests that used an approval without a thread as a stand-in for an unsupported callback now use an unknown method, or assert the new refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Symptom
Live build 0ec04e4, 2026-10-22: the Claude lead c8b8b265 (a single-agent chat on the host) called AskUserQuestion with two questions. The chat showed the options and then "Error: Tool permission request failed: Error: Provider callback is not supported by this runtime". The agent continued without the user's answer.
Cause
account_worker::start_callbacksent onlyitem/tool/calland the VM token refresh to handlers. Every other provider request went tocallback::response, whosedispatchalways returnedUnsupported(-32601). The card code inprovider_callbacks::cardsand the/api/answerruntime already existed, but the worker never admitted a card.Fix
provider_callbacks::admit_native). The card owns the answer, and the worker retires the request's callback record. A replay after a restart finds the same card and sends nothing./api/answerruntime.BrokerCallbackReplyPortnow usesRetainedSessionPort::deliver_callback_reply, which matches the session by account and provider generation. A replacement worker of that generation has a new connection id, and the provider process still waits (the cause of End the turn and its commands when Studio stops or deletes an agent #423 too).serverRequest/resolvedfinds the cards that the Rust worker stores. It matched only theconnection-<generation>name that Python used.approval, so the other tool calls of that turn still work. A request without a turn id (an MCP elicitation) uses the thread's active turn.agent_management::stop) and change the epoch, so a late answer is refused. The bridge drops its own wait on interrupt.Provider callbacks that the Rust worker answered Unsupported and Python supported
item/tool/requestUserInput(Claude AskUserQuestion, Codex)item/commandExecution/requestApproval(Claude tool approvals, Codex)item/fileChange/requestApprovalitem/permissions/requestApprovalmcpServer/elicitation/requestcurrentTime/read{currentTimeAt}account/chatgptAuthTokens/refreshis not a gap: Python answered it only for VM connections, and Rust has its own VM path.Tests
account_worker::tests::native_requests(fake provider, real account worker,CallbackRuntime::answeras/api/answercalls it). All five original cases failed before the fix with the live answer:{"error":{"code":-32601,"data":{"kind":"unsupported_callback"},"message":"Provider callback is not supported by this runtime"}}.approval, the answer reaches the provider once, and a repeated answer replays without a writeserverRequest/resolvedcloses a second one, and it takes no answerfacade.mjs).callback_handoff, the callback burst), or assert the new refusal (crash matrix scenarios 9 and 10, renamed inmutations.json).cargo nextest run -p studio-runtime-services(2000 tests) passed except two that the environment caused and that pass alone:startup_recovery_corpusneedsCODEX_NODEbecause its oracle runs withPATH=/usr/local/bin:/usr/bin:/bin, and abrowser_host_flowport race.cargo nextest run -p studio-runtime-server --bins: 107 passed. runtime-http callback, question and answer tests: 5 passed.-D warnings: the runtime-services lib and lib tests are clean. The only findings are unused imports in two files this PR does not touch:runtime-services/tests/provider_session_task.rs:717andruntime-server/tests/integration.rs:8.item/tool/requestUserInput; the web card showed the question; Answer, "APFS clone", Send answer; the provider's turn continued, and/api/questionsreturned the card asansweredwith that answer.questions-ux-ui.spec.mjsfails at line 76: it waits for a card with idasync-question, which only the Python fixtures seed; the Rust UI fixture has no such seed.question-history-ui.spec.mjspasses.just test-faston 6b723c8 (rebased on bae1e04): passed, 545 s.Not covered
currentTime/read(table above).runtime_agent_callback_context_v2(statusrunningonly) is now unused; the lookup usesruntime_agent_thread. I left the schema unchanged during the freeze.serverRequest/resolvedarrives before the worker admits the card, the card stays pending. The bridge resolves only after its approval timeout, so this needs a resolution within milliseconds of the request.