cmux NEXT: a guest link an agent or script asks for waits for the owner's Allow (#30263)
Fixes #30263.
Problem. agent.share (Share Chat with Guest, #27959) made a guest link for any caller. An agent (MCP), a script or the CLI could mint a link and get the URL back with its secret. Whoever opens that link reads the chat's whole transcript, tool output and reasoning included. The run also spent the owner's cmux install token with no one asking them. Approving guest prompts doesn't help: it guards what a guest sends, not what a guest reads.
Change. A run the owner didn't start in the app waits for the owner's answer. That covers the CLI, MCP, a script, a page, another client, an agent's run naming its own chat, and any control socket run, including one that claims origin: user (on the socket that's only a claim, viaControlSocket). The app shows a cmux dialog naming the chat, how long the link lasts and how many times it works. Only Allow goes on to the owner's token and the daemon. Declining, closing the dialog or not answering in time makes no link and returns share.declined. The owner's own palette or menu run is their consent, so nothing changes for it.
AgentShareHost.shareAskOwner(_:), a new requirement whose default answers no (fail closed).AppServicespresents it withCmuxDialogCenterand dismisses it afterpersonDeadline - 10s, so the waiting caller always gets an answer.agent.shareis markedwaitsForPerson, soaction.runwithwaitallows the person deadline (200 s) instead of 40 s.- The dialog strings and
share.declinedare added toAgentShare.xcstringsin all nine app locales.
Commits. The first commit adds only the failing test (AgentShareConsentTests): a CLI, MCP, script, page, remote or socket-user run spends the owner's token on today's base. The second commit adds the fix.
Kept. cmux share chat and the MCP tool keep working. I didn't use isPersonOnly, because it would have removed those surfaces, which the design asked for.
Risk. The diff is contained to agent.share's action, host, strings and catalog flag. It is a security fix with a behaviour change for scripted callers, so it takes a different-model review.
Follow-up. A composite action that nests agent.share inside a socket run claiming user would see origin user with viaControlSocket false, because ActionRunScope doesn't carry the socket bit. No composite does that today.
Agent session: agent session
š¤ Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes #30263. agent.share now waits for the owner's Allow before creating a guest link. Previously any caller (MCP, script, CLI, page, or control socket run) could mint a link whose secret exposed the chat's entire transcript and spent the owner's install token. Runs the owner didn't start in the app now show a consent dialog; only Allow creates the link. Declining, closing, or timing out returns share.declined and makes no link. The owner's own run in the app is their consent and asks nothing.
Changes
- Adds
AgentShareHost.shareAskOwner(_:), which fails closed unless implemented. Consent is judged byActionInvocation.isPersonsOwnRun, so runs that name a calling app, terminal, or agent session also ask. - Presents the dialog via
CmuxDialogCenter, marked user-only so the debug socket can't answer it; Allow is a deliberate click, not the Return key. The sheet closes viaDemandTimerbefore the person deadline so the caller always gets an answer. - Marks
agent.shareaswaitsForPersonand adds the dialog strings plusshare.declinedtoAgentShare.xcstringsin all nine app locales. - The CLI and
cmux mcpnow wait 210 s (the person deadline) forshare chatandagent.share, so a late Allow reaches the caller instead of a link nobody receives.