fix(init): home-folder pin guard, Pi banner, probe copy, OpenCode dirs, Grok project scope
Five small relay init / probe-core fixes found in end-to-end testing. Each one was reproduced first with an isolated HOME, AGENT_RELAY_HOME, XDG dirs and AGENT_RELAY_CORE_SOCKET.
1. Home-folder guard (the important one)
Root cause. There were two problems:
relay initin~resolved the project to~, so it wrote~/.agentworkforce/relay/workspace-key.jsoninto the relay home. That folder also holdsworkspaces.json,cloud-auth.json, fleet files and binaries. Reproduced:workspace-key.jsonshowed up next toworkspaces.json.findProjectRootcounts.agentworkforce/relayas a project marker, and every user has that folder in~. So any plain folder under~(no git or package marker) also resolved to~(reproduced:~/notes -> ~). Once a stray pin is in~, the pin check matches first for every such folder.
Fix.
@agent-relay/configgetsrelayHomeDir,isRelayHomeDir,assertNotRelayHomeandRelayHomeProjectError. A data folder counts as a relay home when either:- it is
AGENT_RELAY_HOME(default~/.agentworkforce/relay), compared as canonical paths: symlinks resolved, and the same dev/inode when both exist; or - it holds a
workspaces.json, which no project folder does. This catches the real home's relay folder whenHOMEis isolated.
- it is
findProjectRootno longer treats a relay home as a marker or as a pinned subproject.- All pin writers go through
withProjectWorkspaceKeyLock, which now asserts first. That coverswriteProjectWorkspaceKey,...PreservingSession,pinProjectWorkspaceSession, enrollment pins,node upand broker-lifecycle.writeProjectWorkspaceTargetIfSelectionCurrentis best-effort, so it returnsfalseinstead of throwing. relay initchecks before it starts any server or writes anything. The error says: "Run this inside a project folder (cd into the project first), or set AGENT_RELAY_PROJECT to the project folder."- No
--projectflag was added.AGENT_RELAY_PROJECTalready does this job. persistWorkspaceSessionwith a name and the relay home as the project folder skips the pin and still updates the machine-wide store. Sorelay workspace switch Xfrom~keeps working, without a pin. A bare key, which only ever pins a project, is refused.
2. Pi extension banner
Root cause. The banner always said "Enable Receives messages for this session in Agent Relay", even when auto_activate and pi_auto_activate (both turned on by relay init --tools pi) make the session join by itself.
Fix.
- The core keeps
~/.agentworkforce/desktop/pi-auto-joinin sync on every reconcile pass. The marker exists when a workspace is set andactivation_applies(Pi)is true. It is written only where Pi has run and is removed otherwise. - The extension reads the marker at
session_start. When it is there, the banner says "This session joins Agent Relay automatically and receives messages." - On the desktop, where Pi auto-activation is off by default, there is no marker and the text is unchanged.
3. ~/.local/bin/agent-relay-probe after uninstall
Root cause. account::with_account gives every production core a command_line_copy. That includes the --cli-managed core that relay init starts, so it copied itself to ~/.local/bin. Reproduced: the copy was byte-identical to $AGENT_RELAY_HOME/bin/agent-relay-probe.
Fix.
with_accounttakes acommand_line_copyflag, anddaemon::servepasses!cli_managed. The desktop is unchanged.relay initstill pins core 15.0.3 until the next core release. So a fullrelay init --uninstallalso removes~/.local/bin/agent-relay-probewhen it is a regular file, byte-for-byte equal to the core init installs. A symlink, any other build (such as the desktop's) and a partial uninstall (--scope projector--tools) all leave it alone.
4. Empty ~/.config/opencode
Root cause. It is not the CLI. The core's OpenCode harness detect() runs opencode --version, and OpenCode creates its XDG config, data, cache and state folders on every start, --version included. Confirmed by running opencode --version alone under an empty HOME.
Fix.
system_versionruns the probe withXDG_CONFIG_HOME,XDG_DATA_HOME,XDG_CACHE_HOMEandXDG_STATE_HOMEpointed at a throwaway tempdir, which is removed afterwards. The version output is unchanged.- This takes effect with the next core release that
relay initpins. - I did not add an uninstall cleanup for folders an older core already created. Those folders are not empty (
~/.cache/opencode/version), and init cannot tell them apart from a real OpenCode install.
5. Real ~/.grok/config.toml read during uninstall
Root cause. Same as item 1. The test project sat under the real home, and the real home's .agentworkforce/relay made it the project root. So the project-scope path <projectRoot>/.grok/config.toml was the real ~/.grok/config.toml. Reproduced: "Grok CLI left as is: .grok/config.toml … /realhome/.grok/config.toml".
Fix.
- The project root is now resolved correctly (item 1), so the real file is no longer looked at.
relay init --uninstallin a folder whose project is a relay home skips the project scope.--scope projectthere says there is no project.- Reading the file was otherwise harmless: uninstall only removes entries that carry init's managed marker, and an unmarked one is reported as "left as is". It still should not look outside the selected scope, so this is now fixed.
Tests
- New tests:
- config: the relay home is not a marker;
workspaces.jsondetection; symlinked and not-yet-created paths. - cloud: every pin writer refuses the relay home and writes nothing.
- workspace-session: a named switch from
~writes no pin; a bare key is refused. - init: refuses in
~and through a symlink, starting nothing; uninstall in~skips the project scope; the probe copy is removed or kept as described in item 3. - Pi
extension.test.mjsbanner test, and a Rust test for the marker sync. - OpenCode version-probe isolation test.
- The
with_accounttest checks the copy is set for the desktop and not for a cli-managed core.
- config: the relay home is not a marker;
- Vitest (
packages/cli,packages/config,packages/cloud): the last full local run before the final merges had 3 failures out of 3227.ci-standalone-smokeand twoagent-relay-mcp.autonametests timed out under load. Other full runs also failedintegration-relayfile-contractand a PTY replay test.ci-standalone-smoke,integration-relayfile-contractand the replay test's folder passed when rerun alone. agent-relay-mcp.autonametimeouts were partly caused by this PR, and failed the macOStestjob at 5cea9561a.isRelayHomeDircanonicalized missing paths, which is slow on macOS's automounted/home(about 170 ms per call locally). Fixed by skipping the check when no pin was read and by not walking missing paths when one side exists; a canonical result for two missing paths is cached per process. That fix is inside merge commit b5b68f8ba. Side effect: if a missing ancestor later becomes a symlink in the same process, the cached result is stale.tsc --noEmitpasses for cli, cloud and config. Lint has 0 errors.- Probe, run with an empty
HOMEas CI does:- default features: lib 2602 passed.
history_flush::a_failing_receiver_ends_at_the_deadline_with_records_pendingfailed in both local runs, anddestination::consecutive_sends_share_a_connection_and_reread_a_rotated_session_on_401failed in one of two. Both are outside the code this PR changes, but neither was compared against main locally. - Later runs covered only
relay_agents(114 passed in both feature configs) and targeted lib tests, not the whole suite. --no-default-features --features production-binaries: all passed.
- default features: lib 2602 passed.
- E2E in an isolated
HOME:- init in
~refuses and writes nothing. - init in
~/notespins~/notes. - uninstall removes the stale
~/.local/bincopy. - the "real home" uninstall scenario no longer reads its
.grok/config.toml.
- init in
Stays out of the init --hosted join command and Codex session-start areas, which are being changed in parallel.
Takes effect later
Items 2, 3 (the stop-copying half) and 4 are core changes. relay init still installs core 15.0.3, so they reach users only after a core release and a PROBE_VERSION bump.
CI notes
- E2E
Failed to start broker: The operation was aborted due to timeouthit Ubuntu at 9946ed6ad and macOS at b5b68f8ba. Local--daemon-onlypassed and both jobs passed at 93a424d4b, but the root cause was not established. The message matches theAbortSignal.timeoutinpackages/harness-driver/src/transport.ts. relay_agents::auto_activation_puts_new_live_sessions_on_the_relayfailed on macOS at 93a424d4b. This PR adds a store read to that loop. The test now waits for the store, which treats a suspected read-after-registration race; it could not be reproduced locally (15/15 passed before the change).
🤖 Generated with Claude Code
Agent Relay sessions
claudesession8ab900cc· opened viagh pr create· last active 2026-10-31