Skip to content

Remote chats: model, mode and option picks reach the owning machine - #30001

Merged
teamleaderleo merged 1 commit into
feat-cmux-nextfrom
fix/remote-chat-set-model
Nov 1, 2026
Merged

teamleaderleo merged 1 commit into
feat-cmux-nextfrom
fix/remote-chat-set-model

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Leo's bug (his DEV build, a chat on a Cloud machine): picking a model in the chat's model picker shows

Couldn't switch to claude-opus-5-5: session/set_model is not available for a chat on another machine

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 its remote.unsupported refusal.

  • cmux-tui (the daemon on the machine that runs the chat). New verbs agent-session-set-model {surface, model_id}, agent-session-set-mode {surface, mode_id} and agent-session-set-config {surface, config_id, value}, advertised as agent-session-settings-v1 alongside agent-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's configOptions, which the page reads. Ids and values must be 1-256 characters of the model-id charset, the same rule as the app's AcpmuxPaneMethods.isModelID. An observer attachment gets agent_session.read_only. A teammate or link guest gets agent_session.not_trusted; the proxy scope allowlists don't list these verbs either. Spec: cmux-tui/spec/commands.md.
  • App. AgentSessionRemoteClient gains setModel, setMode and setConfig. By default they throw agent_session.settings_unsupported ("update cmux on that machine..."). AgentSessionAttachClient calls the new verbs only when the daemon advertises the capability. RemoteAcpmuxWire forwards 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. agentSessionSettings is added as an optional DaemonCapability, and plans/cmux-next/daemon-capabilities.json is regenerated.
  • The host's existing rules still run before the wire: the gesture rule for set_mode and set_config_option, the full-access rule, and removal of the _meta.cmuxGesture ticket. 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 every session/set_mode failed 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

  • Rust (fleet Linux): 158 agent_session tests pass and clippy -D warnings is clean. New: model_mode_and_option_picks_go_to_the_pinned_session covers the capability, the not-attached refusal, pinned params, acpmux's answer passed back unchanged, and bad ids and params (including _meta and a leading -). Also added: read-only refusals on an observer for the three verbs, and refusals for the remote relay and untrusted connections through every_verb.
  • Swift (fleet Mac): RemoteAcpmuxSettingsTests (new) covers forwarding for this chat only, configOptions passed back, the older-daemon error and the observer read-only refusal. It passes, as do RemoteAcpmuxPresenceTests, RemoteAcpmuxWireAnswerTests, RemoteAcpmuxObserverTests and AgentSessionAttachClientPresenceTests.

Agent session: agent session

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with 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".

  • The daemon on the owning machine now serves agent-session-set-model, agent-session-set-mode, and agent-session-set-config (agent-session-settings-v1), mapping them to acpmux's session/set_model, session/set_mode, and session/set_config_option on the chat's own session. Observers get agent_session.read_only; teammates and link guests get agent_session.not_trusted.
  • The app advertises the capability and forwards the three calls through RemoteAcpmuxWire only for the chat's own session, returning acpmux's answer unchanged (including configOptions). An older daemon answers with a clear "update cmux on that machine" error.
  • Fixes the mode picker closing after one failed pick: previously every session/set_mode on a remote chat failed, so the chip showed a fixed label instead of a menu.

Migration

  • Remote machines running an older daemon show the settings-unsupported error until their cmux-tui is updated; local chats are unaffected.

Written for commit f25b419. Summary will update on new commits.

View guided diff Turn on auto-fix

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

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review f25b419: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr30001 -f ref=fix/remote-chat-set-model -f platform=macos -f backend_mode=local

Not verified: macOS jobs did not run: compile and app tests were skipped on f25b419344; ci-status is green without them.

CI passes on f25b419344 (run 41272591037 attempt 1).
macOS jobs did not run: compile and app tests were skipped: changes skipped, Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

Shipped in:

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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 (deny_unknown_fields refuses a sessionId or _meta); observers get read_only and teammates and link guests not_trusted (the proxy allowlist leaves the verbs out too); the remote relay gate denies them; ids use the same isModelID rule; an older daemon gives a clear update message.

Follow-ups:

  • [follow-up] A setting frame on the Swift wire with a missing or empty value gets remote.unsupported; a bad-request code would be accurate.
  • [follow-up] Config values outside the setting-id charset (spaces, for example) get a bare bad request; widen the rule if a harness reports such values.
  • [follow-up] Add a test that sends a ticketed set_mode through AgentPaneTransport to RemoteAcpmuxWire for a remote chat, and confirm a mode pick on a Cloud chat in a DEV build.

@blacksmith-sh

blacksmith-sh Bot commented Nov 1, 2026

Copy link
Copy Markdown

Found 2 test failures on Blacksmith runners:

Failures

Test View Logs
test_runtime_added_app_capabilities_are_runtime_only (main.StaticCapabilityRuntimeO
nlyTests.test_runtime_added_app_capabilities_are_runtime_only)/
test_runtime_added_app_capabilities_are_runtime_only (main.StaticCapabilityRuntimeO
nlyTests.test_runtime_added_app_capabilities_are_runtime_only)
View Logs
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)/
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo
teamleaderleo merged commit 88115cb into feat-cmux-next Nov 1, 2026
254 of 276 checks passed
@teamleaderleo
teamleaderleo deleted the fix/remote-chat-set-model branch November 1, 2026 04:09
@cmux-bot cmux-bot Bot removed the stack-blocker label Nov 1, 2026

This branch was previously deployed

3 inactive deployments
artifacts — f25b4193 Deployed Nov 1, 2026 by teamleaderleo via add the Windows daemon to the tree #92848
sentry-dsyms — f25b4193 Deployed Nov 1, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #92848
ci-routing-agent — f25b4193 Deployed Nov 1, 2026 by teamleaderleo via namespace autofix #10573
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant