Repository navigation
fix(probe): name the probe by a space-free link so Codex rules match the macOS app - #2024
Conversation
… and hints Codex's execpolicy matches a command only when its program is a plain, unquoted word (shell-command/src/bash.rs, Codex 0.163: the command_name must be a `word` node; quoted arguments are read as one word and match). The macOS app's probe, /Applications/Agent Relay.app/Contents/Helpers/ Agent Relay Helper, must be quoted in every hint, so the rules from #2019 never matched and Codex asked for every read and reply. While the Codex hook or the Claude Code hook is on and the probe's path needs quoting, the core keeps a link at ~/.agentworkforce/desktop/bin/agent-relay-probe (-dev for Agent Relay Dev) and names it in the Codex rules, the Claude allow rules, the pointers and every reply hint. The socket stays a quoted argument, which Codex matches. - The link is pointed at the running probe again whenever a hook turns on and at each core start; a probe moved to a plain path drops it. - It is used only when its folder (made 0700) and that folder's parent are real folders owned by this user and not group/world-writable, the core recorded making it (probe-alias.json in its data), and it points at exactly the running probe. Anything else there is never replaced or used; the hints and rules then name the real path as before. - Turning both hooks off (or relay init --uninstall) removes the link and its folder once empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Probe alias lifecycle crates/probe/src/relay/probe_alias.rs, crates/probe/src/relay.rs |
A new ProbeAlias module manages alias eligibility, ownership and permission checks, link creation, active-path publication, and cleanup. |
Hook rule integration crates/probe/src/relay/local/plugins.rs, crates/probe/src/relay/codex/stop_hook.rs, crates/probe/src/relay/claude/session_hook.rs, crates/probe/src/relay/codex/stop_hook.rs, crates/probe/src/relay/claude/session_hook/permission_tests.rs, docs/probe-move.md, CHANGELOG.md |
Local plugin settings pass a shared alias to the Codex and Claude hooks. The hooks use it in rules and manage it during enablement, refresh, and removal. Unix tests cover rule generation and cleanup; the docs describe the alias behavior. |
Active probe command construction crates/probe/src/relay/outbound/fetch.rs, crates/probe/src/relay/outbound.rs, crates/probe/src/relay/message_pointer.rs, crates/probe/src/relay/local/terminal.rs |
Probe executable lookup prefers the active alias. Socket-bound command construction resolves the executable when needed, and terminal setup borrows the configured reader value. |
Priority:
Estimated code review effort: 4 (Complex) | ~45 minutes
Change: Bug fix
Sequence Diagram(s)
sequenceDiagram
participant plugins_with_options
participant ProbeAlias
participant CodexStopHookSettings
participant CodexSandboxRules
plugins_with_options->>ProbeAlias: Create shared alias
plugins_with_options->>CodexStopHookSettings: Configure alias
CodexStopHookSettings->>ProbeAlias: Acquire Codex alias
CodexStopHookSettings->>CodexSandboxRules: Write rules with alias probe path
Possibly related PRs
- AgentWorkforce/relay#2021: Adds Claude permission rules and probe-based reply commands, which this change updates to use the alias when required.
- AgentWorkforce/relay#2019: Adds Codex sandbox rules and command hints, which this change updates to use the alias when required.
- AgentWorkforce/relay#2023: Tests Claude and Codex init and uninstall flows that relate to the hook path and cleanup behavior changed here.
Suggested reviewers: khaliqgant, willwashburn
Merge Risk: 
Low · up to 44ebd
-
Disabling the last hook while an agent is still running can break that agent’s report command. The PR remains mergeable with awareness of this narrow lifecycle limitation.
Pre-merge checks: 
3 · 
2
-
Failed checks: 2 warnings Passed checks: 3 passedFull details: Docstring Coverage
-
Explanation
Docstring coverage is 73.08% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 26 functions across 10 files. (2 skipped: 2 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
Full details: Description check
-
Explanation
The description explains the problem, fix, tests, safety behavior, cleanup behavior, and end-to-end results. However, it omits the required
## Summary,## Test Plan, and## RelayFlow Proofstructure. The RelayFlow Proof is incomplete because it does not provide a change type or exactly one RelayFlow case undertests/relayflows/cases/<case-id>/.Resolution
Add the required template sections. Preserve the existing content under
## Summaryand## Test Plan, mark the completed tests, and add## RelayFlow Proofwith one valid change type (featureorbugfix) and exactly one case path undertests/relayflows/cases/<case-id>/. Add## Screenshotsor state that screenshots are not applicable.
-
Finishing Touches: 💡 1
-
📝 Generate docstrings: 💡
-
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
A rabbit checks the probe-path trail,
A tidy link avoids the quote,
The hooks now share a careful route,
They leave no link when turned away,
And socket-bound commands find their way,
The rabbit hops, then guards the gate.
Comment @coderabbitai help to get the list of available commands.
[Medium impact] The PR appears safe to merge, with no outstanding findings.
|
… pointers Review on #2024: - probe-alias.json now records the link's target and inode, so a link put in its place (even one to the same probe) is never overwritten on refresh or removed on turn-off. - Hints check the link again on every use (`active` goes through `usable`), so one deleted or replaced since is never handed out. - A pointer handed out through the link that has not been read yet keeps the link after both hooks go, until it is read or expires; hints name the probe's own path again at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
End-to-end evidence (head bd55e97)Isolated env:
Codex 0.163.0, default permissions (
Claude Code live run: blocked. |
Review on #2024: - The record keeps the link being replaced while the app moves, so a core stopped between recording the new link and renaming it into place still knows the old one as its own and finishes the move at the next start. - Keeping the link for unread pointers is reverted: reading the pointer hands out reply commands naming the link too, and pointers for one message in two sessions shared an entry, so any read-based retention still broke commands while leaving the link after the person turned Agent Relay off. Turning both hooks off removes the link as asked; hints name the probe's own path again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
Codex sessions still asked to approve every relayed read and reply whenever the Agent Relay core runs from a path with spaces — always the case for the macOS desktop app (
/Applications/Agent Relay.app/Contents/Helpers/Agent Relay Helper).Codex's execpolicy only reads a shell command whose program is a plain, unquoted word (
codex-rs/shell-command/src/bash.rsatrust-v0.163.0,parse_plain_command_from_node:command_namemust be awordnode; otherwisecommands_for_exec_policyfalls back to the wholebash -lc …and no prefix rule matches). The hints must quote a spaced probe path, so the #2019 rules never matched.Quoted arguments are fine: the same parser accepts
string/raw_stringargument nodes as one word each, so--socket '/…/Application Support/…/relay.sock'matches the rule as written. The socket therefore needs no alias.Fix
New
relay::probe_alias::ProbeAlias:codex_stop_hookorclaude_session_hookis on and the probe's path needs shell quoting, the core keeps a symlink at~/.agentworkforce/desktop/bin/agent-relay-probe(agent-relay-probe-devfor Agent Relay Dev) and names it in the Codex rules, the Claudepermissions.allowrules, therelay readpointers and every reply/agents/attachment hint (Codex, Claude Code, OpenCode, Grok — all go throughoutbound::fetch::probe_executable).0700) and that folder's parent are real directories (not symlinks) owned by this user and not group/world-writable, the core recorded creating it (probe-alias.jsonin its app-data dir), andread_linkis exactly the running probe. Anything else at the name — a link someone else made (even to this probe), a file, a symlinked folder, a writable folder — is never replaced or used, and everything falls back to today's quoted real path. Rules still allow only the same two (Codex) / five (Claude) command shapes.relay init --uninstall), the link goes, with its folder once empty, and the record.Claude Code already matched the quoted path literally (#2021); its rules use the link too so they keep matching the hints, which now name the link.
Tests
probe_alias::tests: link created0700for a spaced path; no link for a plain path; moved probe retargets the link and a plain-path move removes it; refuses others' links (to elsewhere and to this probe), a plain file, a writable folder, a writable parent, a symlinked folder, and a link changed behind its back; link stays while one user remains and goes (with its empty folder and record) after the last.stop_hook: rules and hints name the link, socket stays quoted, refresh recreates a missing link, turning off restores~/.codexexactly and removes the link.permission_tests: Claude rules name the link, the pointer/reply/agents hints through the link are covered by the rules, turning off restores~/.claudeand removes the link.End to end
Isolated HOME/AGENT_RELAY_HOME/INSTALL_DIR/XDG/CODEX_HOME under
/tmp/arc7, locally built probe copied to/tmp/arc7/Agent Relay Test.app/Contents/Helpers/Agent Relay Helper(AGENT_RELAY_PROBE_PATH), real Codex 0.163.0 in its default permissions ("Workspace (granular)"):relay initwrote the link and the rules naming it; a DM fromalicewas answered by Codex via/tmp/arc7/.agentworkforce/desktop/bin/agent-relay-probe relay socket-request --socket '…/Application Support/…/relay.sock' …with no approval prompt;relay message inbox check --as aliceshowed the reply.relay init --uninstall+--stopremoved the link, its folder, the rules, hooks.json and ~/.claude. Details (and why the live Claude Code run is still pending: the 1Password read of the setup token timed out) are in the e2e comment below. Review follow-up: the record also keeps the link's target and inode, so a link put in its place is never overwritten or removed; hints re-check the link on every use; and an interrupted move is finished at the next start. Hints handed out before both hooks were turned off that name the link stop working once it is removed (they would have needed approval without the rules anyway).🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.