Repository navigation
Remote chats: model, mode and option picks reach the owning machine - #30001
Conversation
A chat on another machine (a Cloud VM, an SSH host) refused every model, mode and option pick with "session/set_model is not available for a chat on another machine". The owning daemon now serves agent-session-set-model, -set-mode and -set-config (agent-session-settings-v1): ACP session/set_model, session/set_mode and session/set_config_option on the tab's own pinned session, refused for observers (read_only) and for teammates and link guests (not_trusted), ids bounded to the model-id charset. The app's RemoteAcpmuxWire forwards the three methods through AgentSessionRemoteClient and returns acpmux's answer unchanged; an older daemon answers agent_session.settings_unsupported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
There was a problem hiding this comment.
3 issues found across 12 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="Packages/macOS/CmuxNext/Sources/CmuxNextDaemon/Connection/DaemonEndpoint.swift">
<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextDaemon/Connection/DaemonEndpoint.swift:318">
P2: Adding `agentSessionSettings` to the `.optional` tier makes the static capability check fail. `agent-session-settings-v1` is advertised only inside the runtime `serves_agent_session_attach()` block, so `__daemon-capabilities` never lists it. Add it to the `runtime_only` set in `scripts/cmux-next/check-daemon-capabilities.sh`, next to the other `agent-session-*` entries.</violation>
</file>
<file name="Packages/macOS/CmuxNext/Tests/CmuxNextAgentPaneTests/RemoteAcpmuxSettingsTests.swift">
<violation number="1" location="Packages/macOS/CmuxNext/Tests/CmuxNextAgentPaneTests/RemoteAcpmuxSettingsTests.swift:87">
P2: This exact call-order assertion is flaky because `RemoteAcpmuxWire` executes each request in a separate task; concurrent scheduling can reorder the model, mode, and config calls even when all replies are correct. Compare the calls as an order-independent set, or await each request before asserting ordering.</violation>
</file>
<file name="cmux-tui/crates/cmux-tui-core/src/server/agent_session_attach_tests.rs">
<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/server/agent_session_attach_tests.rs:936">
P2: The new teammate and link-guest refusal for `set-model`, `set-mode` and `set-config` has no test. Add these three verbs to the teammate frame list in `agent_session_shares_tests.rs` and assert `agent_session.not_trusted`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| case agentSessionPresence = "agent-session-presence-v1" | ||
| /// `agent-session-set-model`, `-set-mode` and `-set-config`: the person's model, mode and | ||
| /// option picks in a chat on that machine. Without it those picks say to update its cmux-tui. | ||
| case agentSessionSettings = "agent-session-settings-v1" |
There was a problem hiding this comment.
P2: Adding agentSessionSettings to the .optional tier makes the static capability check fail. agent-session-settings-v1 is advertised only inside the runtime serves_agent_session_attach() block, so __daemon-capabilities never lists it. Add it to the runtime_only set in scripts/cmux-next/check-daemon-capabilities.sh, next to the other agent-session-* entries.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextDaemon/Connection/DaemonEndpoint.swift, line 318:
<comment>Adding `agentSessionSettings` to the `.optional` tier makes the static capability check fail. `agent-session-settings-v1` is advertised only inside the runtime `serves_agent_session_attach()` block, so `__daemon-capabilities` never lists it. Add it to the `runtime_only` set in `scripts/cmux-next/check-daemon-capabilities.sh`, next to the other `agent-session-*` entries.</comment>
<file context>
@@ -313,6 +313,9 @@ public enum DaemonCapability: String, CaseIterable, Sendable {
case agentSessionPresence = "agent-session-presence-v1"
+ /// `agent-session-set-model`, `-set-mode` and `-set-config`: the person's model, mode and
+ /// option picks in a chat on that machine. Without it those picks say to update its cmux-tui.
+ case agentSessionSettings = "agent-session-settings-v1"
/// Page tabs on the store: the `page` source of a conversation tab (App Store, Settings,
/// Debug Settings), so they move and split like any tab (cmux-tui/spec/commands.md).
</file context>
| let options = (frames.reply(4)?["result"] as? [String: Any])?["configOptions"] as? [[String: Any]] | ||
| #expect(options?.first?["currentValue"] as? String == "high") | ||
| for id in 5...7 { #expect(Self.code(frames, id) == RemoteAcpmuxWire.unsupported) } | ||
| #expect(client.calls.withLock { $0 } == ["attach", "model claude-opus-5-5", "mode acceptEdits", "config effort=high"]) |
There was a problem hiding this comment.
P2: This exact call-order assertion is flaky because RemoteAcpmuxWire executes each request in a separate task; concurrent scheduling can reorder the model, mode, and config calls even when all replies are correct. Compare the calls as an order-independent set, or await each request before asserting ordering.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Tests/CmuxNextAgentPaneTests/RemoteAcpmuxSettingsTests.swift, line 87:
<comment>This exact call-order assertion is flaky because `RemoteAcpmuxWire` executes each request in a separate task; concurrent scheduling can reorder the model, mode, and config calls even when all replies are correct. Compare the calls as an order-independent set, or await each request before asserting ordering.</comment>
<file context>
@@ -0,0 +1,113 @@
+ let options = (frames.reply(4)?["result"] as? [String: Any])?["configOptions"] as? [[String: Any]]
+ #expect(options?.first?["currentValue"] as? String == "high")
+ for id in 5...7 { #expect(Self.code(frames, id) == RemoteAcpmuxWire.unsupported) }
+ #expect(client.calls.withLock { $0 } == ["attach", "model claude-opus-5-5", "mode acceptEdits", "config effort=high"])
+ }
+
</file context>
| assert!(sent.get("id").is_none(), "cancel is a notification"); | ||
| } | ||
|
|
||
| // MARK: Settings |
There was a problem hiding this comment.
P2: The new teammate and link-guest refusal for set-model, set-mode and set-config has no test. Add these three verbs to the teammate frame list in agent_session_shares_tests.rs and assert agent_session.not_trusted.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui-core/src/server/agent_session_attach_tests.rs, line 936:
<comment>The new teammate and link-guest refusal for `set-model`, `set-mode` and `set-config` has no test. Add these three verbs to the teammate frame list in `agent_session_shares_tests.rs` and assert `agent_session.not_trusted`.</comment>
<file context>
@@ -925,6 +933,86 @@ fn cancel_goes_to_the_pinned_session() {
assert!(sent.get("id").is_none(), "cancel is a notification");
}
+// MARK: Settings
+
+/// A chat on another machine: the person's model, mode and option picks go to acpmux on the tab's
</file context>
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by Shipped in:
|
|
Review (subagent claude-opus-5-5 medium): SHIP at f25b419 No blocking findings. The new case in the wire takes the frames that hit the refusal; the daemon pins the session from its own record ( Follow-ups:
|
|
Found 2 test failures on Blacksmith runners: Failures
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo's bug (his DEV build, a chat on a Cloud machine): picking a model in the chat's model picker shows
and the Mode picker would not open on a Cloud chat.
What
A chat whose acpmux runs on another machine (a Cloud VM, an SSH host) goes through
RemoteAcpmuxWire. That wire served only start, prompt, cancel, permission answers, trust and presence, so every model, mode and option pick fell through to itsremote.unsupportedrefusal.agent-session-set-model {surface, model_id},agent-session-set-mode {surface, mode_id}andagent-session-set-config {surface, config_id, value}, advertised asagent-session-settings-v1alongsideagent-session-attach-v1. Each sends the matching ACP call (session/set_model,session/set_mode,session/set_config_option) to acpmux on the tab's own pinned session. The verbs take no session id param (deny_unknown_fields). Each returns acpmux's result unchanged, including set_config_option'sconfigOptions, which the page reads. Ids and values must be 1-256 characters of the model-id charset, the same rule as the app'sAcpmuxPaneMethods.isModelID. An observer attachment getsagent_session.read_only. A teammate or link guest getsagent_session.not_trusted; the proxy scope allowlists don't list these verbs either. Spec:cmux-tui/spec/commands.md.AgentSessionRemoteClientgainssetModel,setModeandsetConfig. By default they throwagent_session.settings_unsupported("update cmux on that machine...").AgentSessionAttachClientcalls the new verbs only when the daemon advertises the capability.RemoteAcpmuxWireforwards the three methods only for its own session (names(sessionId)), refuses them on an observer as read-only, and returns acpmux's answer. The logic lives in Rust; the Swift side only forwards.agentSessionSettingsis added as an optionalDaemonCapability, andplans/cmux-next/daemon-capabilities.jsonis regenerated._meta.cmuxGestureticket. The wire never sends_meta.Mode picker
The access chip's
AccessMenu(ComposerPickers.tsx) marks a mode as refused when picking it fails. Once every other mode is refused, the chip shows a fixed label instead of a menu. On a remote chat everysession/set_modefailed with the same refusal, so after one pick the chip stopped opening. Now that set_mode is forwarded, picks succeed and the chip stays a menu. No webview change is needed.Risk
Only remote chats are affected. If the remote machine runs an older daemon, the person sees a clear "update cmux on that machine" error instead of the old refusal. Local chats don't change.
Tests
agent_sessiontests pass and clippy-D warningsis clean. New:model_mode_and_option_picks_go_to_the_pinned_sessioncovers the capability, the not-attached refusal, pinned params, acpmux's answer passed back unchanged, and bad ids and params (including_metaand a leading-). Also added: read-only refusals on an observer for the three verbs, and refusals for the remote relay and untrusted connections throughevery_verb.RemoteAcpmuxSettingsTests(new) covers forwarding for this chat only, configOptions passed back, the older-daemon error and the observer read-only refusal. It passes, as doRemoteAcpmuxPresenceTests,RemoteAcpmuxWireAnswerTests,RemoteAcpmuxObserverTestsandAgentSessionAttachClientPresenceTests.Agent session: agent session
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Fixes model, mode, and option picks in chats that run on another machine (a Cloud VM, an SSH host) so they reach that machine's acpmux instead of failing with "session/set_model is not available for a chat on another machine".
agent-session-set-model,agent-session-set-mode, andagent-session-set-config(agent-session-settings-v1), mapping them to acpmux'ssession/set_model,session/set_mode, andsession/set_config_optionon the chat's own session. Observers getagent_session.read_only; teammates and link guests getagent_session.not_trusted.RemoteAcpmuxWireonly for the chat's own session, returning acpmux's answer unchanged (includingconfigOptions). An older daemon answers with a clear "update cmux on that machine" error.session/set_modeon a remote chat failed, so the chip showed a fixed label instead of a menu.Migration
Written for commit f25b419. Summary will update on new commits.