fix(probe): let T3 Code's Claude chats join Relay on Linux

Merged
KH
12d ago
+167-77

Stacked on #2041. On Linux, T3 Code's Claude chats still looked like automation to the core, so they never joined the roster. Fixes #2045 for Linux; Windows is not covered.

is_t3_app now also matches T3 Code's Linux main executable:

  • .deb: /opt/T3 Code (<Channel>)/t3code. The stable and preview builds install as T3 Code (Beta), nightly as T3 Code (Nightly). Channel names use the same rule as the macOS bundles.
  • AppImage: $TMPDIR/.mount_<random>/t3code, where the running AppImage mounts itself (/tmp by default).

T3's backend server runs from the same executable (ELECTRON_RUN_AS_NODE) and starts each Claude chat, so the walk matches at the chat's parent. The other guards from #2041 are unchanged: same UID, kind: interactive, stream-json input, no terminal, no -p/--print, and the walk stops at an intermediate or unreadable Claude session. The existing chat and claude -p tests now run against the macOS, .deb and AppImage executables. There is a new test for a parent owned by another user, and the path test has Linux cases that should be rejected: helpers, /usr/bin/t3code, ~/.local/bin/t3code, a non-/opt install, an extracted squashfs-root and an empty mount id.

Evidence (linuyc, Arch, x86_64; T3 Code 0.1.6 AppImage, Claude Code 2.1.280):

  • Ran live: the stable AppImage, checked against latest-linux.yml, on Hyprland in a hidden special workspace (--ozone-platform=headless segfaults in Electron). A real Claude chat sent from the UI ran with this ancestry, read from /proc:
    claude --output-format stream-json --verbose --input-format stream-json … (no -p)   tty_nr=0, fd0=socket
      └ /tmp/.mount_T3-CodmajGcH/t3code --require …/compileCache.cjs …/apps/server/dist/bin.mjs   (backend server)
        └ /tmp/.mount_T3-CodmajGcH/t3code --user-data-dir=…   (main)
          └ Hyprland
    
    Registry record: kind: interactive, entrypoint: sdk-ts. A temporary test that was not committed ran is_t3_chat with the real SystemProcesses on that pid. The result was true, and false with kind: sdk.
  • Taken from the packages, not run: the .deb path. The stable, preview and nightly .deb files all checked out against their release manifests. Their postinst and .desktop files point to /opt/T3 Code (Beta)/t3code (stable and preview) or /opt/T3 Code (Nightly)/t3code. The nightly AppImage also contains t3code and runs through the same AppImage runtime, but I didn't run it.

Not covered:

  • The Nix package (t3code-desktop): Electron there runs from the Nix store, so it won't match.
  • T3's web/CLI server (t3): it runs under node, so it can't be told apart from an SDK run.
  • Matching an AppImage mount by name is no stronger than macOS's ~/Applications check. The process guards are what keep automation out.

Tests: relay::claude 170 passed, relay::local 91 passed (1 ignored), cargo fmt --check is clean, and clippy reports nothing in the touched files. Clippy --all-targets stops on an existing non_octal_unix_permissions error in opencode_existing/native_tests.rs, which this PR doesn't touch. PROBE_VERSION is not bumped.

🤖 Generated with Claude Code


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


Agent Relay sessions

  • claude session ed0bac14 · opened via gh pr create · last active 2026-10-31