feat: drive a chat from claude.ai with Remote Control (5.8.0) - #66
Merged
Merged
Conversation
The release workflow reads the version from build.gradle.kts, and the README states it twice as user-facing text: the badge and the name of the artifact buildPlugin writes. All three move together or the README describes a zip that no build produces.
The bump missed a fourth place the version is written. PluginIdentity is the one the plugin announces on every outbound request, and the descriptor cannot be read at runtime without an internal API this build refuses to ship, so the constant is hand-kept and PluginIdentityTest is what keeps it honest. It went red on 5.7.0 against a 5.7.1 build, which is the test doing its job.
The slash command is closed in the mode this plugin launches, and answers "isn't available in this environment". The control request behind it is not: the binary takes a remote_control subtype carrying an enabled flag, and runs the bridge itself with the session credential. That is the same shape as the btw command, which is a side_question request rather than the command it looks like. The reply rides SessionControlClient.send instead of SessionQueries.ask, because a success comes back with no payload at all. Ask decodes the payload whether the request succeeded or failed, so a toggle built on it would paint itself on after a refusal and never say so. Two ways in, one path: the composer's phone button and the settings menu row both emit the same settingsToggle key, so there is one destination to keep honest rather than two that can disagree.
The binary answers the enable request with an error when the account or the organisation has Remote Control switched off, so the host state was already right. What lied was the page. The settings row flipped itself on the click, before the host had answered. That is correct for every other row in this menu, because they write to the settings document and cannot be refused; this is the first one whose destination can say no. Rows may now declare themselves host-owned, and the page leaves those alone until the state it is sent says otherwise. The composer button had no way to fail. It stayed off after a refusal with the reason reachable only by reading the transcript, which is the same as failing silently for anyone who was watching the button they just pressed. The refusal now travels to the page and the button wears it.
Remote Control is a new capability, not a repair, so the release is a minor. The number is written in four places and the release workflow greps only the first of them, which is why the other three are moved in the same commit. The changelog section is the one the release job extracts for the GitHub Release, and the release notes are what the Marketplace shows as What's New. Both name what the feature needs to work, because the refusal a user is most likely to meet is an account or organisation that has Remote Control off.
Approving a call from the phone left the card sitting in the chat forever. With Remote Control on, the binary forwards the prompt to claude.ai as well, and when the remote answers it withdraws the one it left here — over a top-level control_cancel_request frame carrying the request_id it echoes. The parser had no entry for that type, so it fell through to Other and was dropped, and nothing ever took the card down. The withdrawal writes no reply. The binary already has its answer and is no longer waiting, so answering a request that was retracted would be inventing a second verdict. It closes the review diff the same way a rejection does, because a card that leaves without closing it strands the pane. This is the connected surface the feature commit missed: enabling Remote Control adds an inbound frame the plugin never had to understand before.
checkDrift had not been run since the tools moved: it recorded SDK 0.3.233 and binary 2.1.233 against a machine running 0.3.250 and 2.1.250. A drift gate whose baseline is stale reports on a protocol nobody is speaking. The detector updates the tools itself before reporting, which is what moved the lockfile here. The vendored SDK is protocol reference and ships to nobody, and package.json already allowed the newer one, so the range is unchanged. The surface came back fully covered once the withdrawal frame was modelled.
Its own section in the user guide and an entry at the top of What's new. Says what the feature needs before it will work, because the account and organisation gates are the refusal a reader is most likely to meet, and says plainly that the guard still decides first on this machine — connecting a phone changes who can answer a permission, never what the chat may do.
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.
Adds Remote Control to a chat, and cuts the 5.8.0 release.
What it does
A chat running in the IDE can be connected to claude.ai or the Claude mobile app, and keeps
executing on this machine the whole time. Two controls, both wired to the same destination: a
phone button in the chat's button row, left of the guard's shield, and a Remote control row in
the settings menu.
How, and why it is not the slash command
The slash command is closed in the mode this plugin launches the binary in, and answers
"isn't available in this environment". The control request behind it is not: the binary takes a
remote_control subtype carrying an enabled flag, and runs the bridge itself with the session
credential. Same shape as the btw command, which is a side_question request rather than the
command it looks like. Neither subtype is declared in the vendored SDK types, so both were
verified against the binary, as the protocol map prescribes.
The reply rides SessionControlClient.send rather than SessionQueries.ask, because a success
comes back with no payload at all and ask decodes the payload whether the request succeeded or
failed. Built on ask, the toggle would paint itself on after a refusal and never say so.
Neither control turns itself on until the binary has said yes. Settings rows may now declare
themselves host-owned, and the page leaves those alone instead of flipping them on the click,
which is correct for every other row in that menu because they write to the settings document
and cannot be refused.
Gates, run locally on this tree
and the licences directory
Validated by hand
Installed and exercised in PyCharm. The refusal path was reproduced deliberately against an
account without Remote Control, and both controls report it rather than failing quietly.