Skip to content

Cloud: bind repository tools to the chat session - #30434

Merged
teamleaderleo merged 6 commits into
feat-cmux-nextfrom
lane/cloud-repo-session-binding
Nov 1, 2026
Merged

teamleaderleo merged 6 commits into
feat-cmux-nextfrom
lane/cloud-repo-session-binding

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Leo asked for “this kind of recovery” when attaching a repository to a Cloud chat.

Repository agent tools now require the protected chat ID to match the caller authenticated by the acpmux initialize handshake. The tool environment includes the caller ID, and daemon actions reject a presented ACP launch credential targeting another chat.

Follow-up 1 to #30361, plus installation-link hardening from the recovery review. Every link source now uses one HTTPS/github.com validator, and a valid Mac-synced link takes precedence over connector and seed responses. Unsafe links never become card destinations; a bad seed link still consults the connector so a valid cached link can recover. Existing same-user local CLI access and authenticated Mac links remain authorized; this change binds presented agent identities, without claiming raw same-UID sockets isolate processes. Tests cover missing/forged tokens, immutable first-initialize binding, foreign chat refusal before CLI execution, and local/remote routing.

Validation: Rust fleet checks pending; formatting and static prepush pass.

Agent session: agent session


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


Summary by cubic

Follow-up to #30361 that binds Cloud repository agent tools to the chat session proven by the acpmux initialize handshake. Repository tool calls now require the acpmux caller to match the chat session; missing or mismatched callers are refused before any CLI runs. The first-initialize binding is immutable, daemon actions reject an ACP launch credential targeting another chat, and CLI calls attach a launch credential that routes them back to their originating session.

Installation-link hardening

  • One validator checks every link source for HTTPS, the exact github.com host, no credentials or non-default port, and no control characters.
  • A Mac-synced link takes precedence over connector and seed responses; unsafe links never become card destinations.
  • A bad seed link still consults the connector so a cached valid link can recover.
  • Old stored unsafe links are ignored when read, and stale unsafe cache data falls back to a validated connector link.

Merged from the base branch: the Cloud start-chat checklist was reverted (clone and setup progress fields removed), attached repository rows in Cloud Session Info are deduplicated, and the Cloud repository UI and recovery are localized. Tests cover missing and forged tokens, immutable binding, foreign-chat refusal before CLI execution, and unsafe link rejection across every recovery path.

Written for commit 964ceb9. Summary will update on new commits.

View guided diff Turn on auto-fix

@teamleaderleo teamleaderleo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review (claude-opus-5-5-medium): SHIP at 24f8991

@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.

2 issues found across 15 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="cmux-tui/crates/cmux-tui-core/src/cloud_repos/engine.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/engine.rs:163">
P2: An unsafe connector `install_url` makes `list` fail outright, so local and recent repos disappear from the picker. Drop the `connect` field instead of returning an error, matching how `seed_failure` treats invalid links.</violation>
</file>

<file name="cmux-tui/crates/acpmux/src/cloud_repo_mcp.rs">

<violation number="1" location="cmux-tui/crates/acpmux/src/cloud_repo_mcp.rs:210">
P3: The unauthenticated-caller message always says "retry Add repository", even when the failing call is `list_repos`. Use wording that fits any repository tool.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment on lines +163 to 166
if !valid_install_url(&url) {
return Err(install_link_required());
}
result.set("connect", json!({"install_url":url}));

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: An unsafe connector install_url makes list fail outright, so local and recent repos disappear from the picker. Drop the connect field instead of returning an error, matching how seed_failure treats invalid links.

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/cloud_repos/engine.rs, line 163:

<comment>An unsafe connector `install_url` makes `list` fail outright, so local and recent repos disappear from the picker. Drop the `connect` field instead of returning an error, matching how `seed_failure` treats invalid links.</comment>

<file context>
@@ -133,6 +160,9 @@ pub(crate) fn list(backend: &impl Backend, query: Option<&str>) -> Result<Value,
     let catalog = backend.connector(query)?;
     let mut result = json!({"repos":merge(sources.into_iter().chain(catalog.repos), query)});
     if let Some(url) = catalog.install_url {
+        if !valid_install_url(&url) {
+            return Err(install_link_required());
+        }
</file context>
Suggested change
if !valid_install_url(&url) {
return Err(install_link_required());
}
result.set("connect", json!({"install_url":url}));
if valid_install_url(&url) {
result.set("connect", json!({"install_url":url}));
}

{
if caller != Some(session) {
return error_result(
"Repository tools could not verify this chat. Restart the chat session, then retry Add repository.".to_owned(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The unauthenticated-caller message always says "retry Add repository", even when the failing call is list_repos. Use wording that fits any repository tool.

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/acpmux/src/cloud_repo_mcp.rs, line 210:

<comment>The unauthenticated-caller message always says "retry Add repository", even when the failing call is `list_repos`. Use wording that fits any repository tool.</comment>

<file context>
@@ -190,7 +191,25 @@ fn args_object(params: &Value) -> Result<Value, Value> {
+{
+    if caller != Some(session) {
+        return error_result(
+            "Repository tools could not verify this chat. Restart the chat session, then retry Add repository.".to_owned(),
+        );
+    }
</file context>
Suggested change
"Repository tools could not verify this chat. Restart the chat session, then retry Add repository.".to_owned(),
"Repository tools could not verify this chat. Restart the chat session, then retry the repository request.".to_owned(),

@teamleaderleo
teamleaderleo force-pushed the lane/cloud-repo-session-binding branch from 24f8991 to 8306a56 Compare November 1, 2026 07:50
@teamleaderleo
teamleaderleo force-pushed the lane/cloud-repo-session-binding branch from 95a278e to b0f0646 Compare November 1, 2026 11:41
@teamleaderleo
teamleaderleo force-pushed the lane/cloud-repo-session-binding branch from b0f0646 to 6bf7cb2 Compare November 1, 2026 11:51
@teamleaderleo
teamleaderleo force-pushed the lane/cloud-repo-recovery branch from f0c74d1 to e03d9d9 Compare November 1, 2026 11:52
@teamleaderleo
teamleaderleo force-pushed the lane/cloud-repo-session-binding branch from 6bf7cb2 to 54c63f9 Compare November 1, 2026 11:53

@teamleaderleo teamleaderleo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review (claude-opus-5-5-medium): SHIP at 54c63f9

Review (subagent claude-opus-5-5 medium): SHIP at 54c63f9

#30434 binding, rebase review. Old: f0c74d1..6bf7cb2 (5 commits). New: e03d9d9..54c63f9 (6 commits). I compared the added/removed lines of both whole diffs.

Differences, classified:

  • Commits 1 (session binding), 3, 4 and 5 are patch-identical (= in range-diff).
  • (a) Commit 2 (install-link validation): value["error_code"].as_str().or_else(...), response["install_url"].as_str(), args["install_url"].as_str() and load(mux)?.0["install_url"].as_str() are now get().and_then(Value::as_str). Removed-line context now shows base's get() form instead of the old [] form. Each .filter(valid_install_url) is kept on all four paths: seed error, connector response, sync args, stored link.
  • (a) New commit 54c63f9 converts the PR's remaining index reads: engine::seed_failure org/install_url, runtime failure.0["code"] == "repo.no_access" → get("code").and_then(as_str) == Some(..) (equivalent), and server/cloud_repos.rs permitted request["session"].as_str() → request.get("session").and_then(Value::as_str) (equivalent; still None for a missing or non-string session, which still compares unequal to the bound id).
  • No (b) or (c) items.

Security binding: intact at the new head. server/cloud_repos.rs permitted still requires a Unix or Remote transport and a frame principal. It keeps cloud.repo.local/remember Unix-only, and refuses a non-string or empty credential. It allows an AcpSession actor only when request.session == id, and an Err actor is refused. The test cloud_repo_agent_credential_cannot_target_another_chat (other-chat → remote_denied; null/12/"forged"/"" credentials refused) is unchanged. The acpmux side (cloud_repo_mcp caller-token forward, client.rs, requests.rs, agent_tools wiring and its tests) is patch-identical.

Base not reverted: no file in the 12 base commits after e987ea5 overlaps the stack, and the stack-file diff against the current base equals the three-dot diff.

Follow-ups: none new. I did not run a compile.

An error occurred while trying to automatically change base from lane/cloud-repo-recovery to feat-cmux-next November 1, 2026 12:31
@teamleaderleo
teamleaderleo force-pushed the lane/cloud-repo-session-binding branch from 54c63f9 to 964ceb9 Compare November 1, 2026 12:32

@teamleaderleo teamleaderleo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review (claude-opus-5-5-medium): SHIP at 964ceb9

Review (claude-opus-5-5 medium): SHIP at 964ceb9 (#30434)
Carried from 54c63f9 (r-rebase-30434.md). It is a clean rebase onto the #30417 squash 869cc3a: git range-diff shows all six commits as =, so the binding and credential gate are unchanged.

@teamleaderleo
teamleaderleo changed the base branch from lane/cloud-repo-recovery to feat-cmux-next November 1, 2026 12:33
@teamleaderleo
teamleaderleo merged commit 71f7bae into feat-cmux-next Nov 1, 2026
114 of 120 checks passed
@teamleaderleo
teamleaderleo deleted the lane/cloud-repo-session-binding branch November 1, 2026 12:34
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 1, 2026
@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)

Shipped in:

This branch was previously deployed

3 inactive (2 outdated) deployments
ci-routing-agent — 964ceb9a Deployed Nov 1, 2026 by teamleaderleo via wait for same-key tree publisher #96151
artifacts — 6bf7cb27 Deployed Nov 1, 2026 by teamleaderleo via add the Windows daemon to the tree #94812
sentry-dsyms — 6bf7cb27 Deployed Nov 1, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #94812
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