Conversation
pkexec's message quotes the command line with a backtick and a closing apostrophe, so a command that contains its own quotes (bash -c 'echo hi') fell through the label parser and the full raw sentence overflowed the screen. Parse the message by its fixed tail instead, and render the request inside the card: a "Run as root" / "Run as <user>" title, then the program in the accent color with its arguments wrapping below. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vSTTwVkpo2cevibWvt5iB
pkexec joins the command line without escaping it, so an argument can carry line breaks or bidi controls. With the 4-line cap on the arguments, line breaks pushed the rest of the command out of the prompt, and a right-to-left override rendered harmless text as a different command. Escape those characters in the program, arguments and target user, and show the arguments uncapped, since pkexec already limits them to about 80 bytes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SNhNwBijbgSaQ3KLmKbGbo
pkexec's message says what will run, not who asked for it, and Quickshell's polkit agent doesn't pass on polkit's subject pid. The prompt now asks omarchy-polkit-caller, which finds the pkexec behind the prompt in /proc and names the processes above it, as in "omarchy-update ← bash ← foot". The requester chooses its pkexec's name, options and program, so none of those are trusted. Every root process this user started counts as the pkexec unless the message pkexec would show for its command can't end the way the shown one does, and two such processes mean no answer. The one left must render exactly the shown message; the helper then also returns the command as pkexec received it, shell-quoted. Processes choose their own names, so the line reads "Not verified: requested by …". Control, line-separator, bidi and zero-width characters are stripped and long names cut, so nothing can hide that or break up the line. The lookup runs under a short timeout and a result that arrives after a newer prompt started is ignored, so a slow or stuck lookup only ever leaves the line out.
Pressing Tab on a pkexec prompt, or clicking the hint, asks the default coding agent what the command likely does, through omarchy-agent-explain. The answer shows in the prompt as that agent's guess. Nothing is sent until the user asks, and the full command from omarchy-polkit-caller is used when the lookup found it. Whatever asked for root wrote that command, so it goes in as fenced data with invisible characters removed and every @ shown as a fullwidth one, since some agents attach "@path" as a file. The agent runs one turn with no tools, MCP servers, hooks or context files; only agents that can run that way get the hint. The agent also runs inside bubblewrap under a Landlock scope. It sees the system and its own install read-only, a private home and /tmp, only agent-related environment variables and no inherited files, and it can't reach abstract unix sockets such as X11's or signal anything outside. It works on a copy of its login, and a refreshed login goes back only if it is one small JSON object and the real login hasn't changed meanwhile. bubblewrap joins the base packages, with a migration for existing installs.
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.
Builds on #11983 (which builds on #11795); the change for review is the last commit, "Explain polkit commands with the default coding agent on request".
What
On a pkexec prompt, pressing
Tabor clicking the hint asks your default coding agent what the command likely does. The answer shows in the prompt as Claude's guess, which may be wrong: ... (or Codex's, or Pi's). Nothing is sent until you ask.omarchy agent explain "<command>"does the same from a terminal.Claude Code, Codex and Pi are supported, because they can run one turn with no tools. Other default agents get no hint.
Treating the command as hostile
Whatever asked for root wrote that command, so:
@shown as a fullwidth one (some agents attach@pathas a file before any tool runs). The prompt tells the agent never to follow instructions in it and to call out text claiming to be safe.omarchy-polkit-calleris used when the lookup found it; otherwise the agent is told quoting was lost or part of the command is missing.Sandbox
The agent also runs inside bubblewrap under a Landlock scope:
/usr,/etc,/opt, mise's installs and its own binary read-only, a private home and/tmp, and nothing of your files or/run/user.ANTHROPIC_*or*_API_KEY) reach it, and no inherited file descriptors.bubblewrapjoins the base packages, with a migration for existing installs (it's already there as a dependency of glycin).Known limits
/usror/optas more than a single file may fail inside the sandbox; the prompt then shows the agent's error.Tests
test/shell.d/agent-explain-test.sh: flags, prompt fencing and cleaning, locale-safe caps, errors and timeouts, stopping, mise installs, and a real bubblewrap section (skipped where it can't run) checking hidden files, blocked abstract sockets, environment filtering, closed descriptors, and login copy-back against broken, doubled, oversized, unreadable, locked, symlinked, hard-linked and swapped copies.polkit-caller-test,polkit-test,qml-text-format-test,bin-style-testand./test/clipass.Screenshots
Before (requester details from #11983):
After — explanation available on request:
While asking the default agent:
With the answer (long explanations are limited to seven lines and end with an ellipsis):
🤖 Generated with Claude Code
https://claude.ai/code/session_015vSTTwVkpo2cevibWvt5iB