feat: a herdr pane says which Claude account its agent is running as - #568
feat: a herdr pane says which Claude account its agent is running as#568JSmithRobotics wants to merge 4 commits into
Conversation
Reviewer's GuideThis PR adds explicit, per-launch Claude account selection backed by externally managed profiles, exposes those profiles and their actual account identities, forwards the selection through Sequence diagram for per-launch Claude profile forwardingsequenceDiagram
participant User
participant DL as dl
participant Profiles as ProfileDirectory
participant Claude as ClaudeSession
User->>DL: launch --claude-profile work
DL->>Profiles: read work/.credentials.json
alt credential found
Profiles-->>DL: access token
DL->>Claude: start session with CLAUDE_CODE_OAUTH_TOKEN
else missing or invalid credential
Profiles-->>DL: no credential
DL-->>User: refuse launch
end
Sequence diagram for profile reporting and pane inheritancesequenceDiagram
participant AgentPane as AgentPane
participant Herdr
participant DL as dl
participant NewPane as AdjacentPane
DL->>AgentPane: inspect argv for --claude-profile
AgentPane-->>DL: profile name or none
DL->>Herdr: report-metadata profile=name
DL->>Herdr: clear-token profile when no profile
NewPane->>Herdr: inspect agent pane processes
Herdr-->>NewPane: workspace and profile
NewPane->>DL: launch shell with inherited profile
DL->>NewPane: start shell under selected profile
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| - | - | Generic CLI Secret | d900d74 | rust/devlaunch-core/src/flows/session_manager.rs | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
d0317de to
56d270d
Compare
56d270d to
1c16315
Compare
db4d3e3 to
c920883
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…with `--herdr-shell` re-enters as `dl <ws>`, and it was passing `claude_profile: None`. So a session started with `--claude-profile work` got a sibling pane authenticated as the *default* account, which is the one way this feature could mislead quietly: the pane looks like the agent's shell and is signed in as somebody else. Read from the live sibling rather than remembered, which is what lets it exist at all. A profile is named per launch and deliberately not stored with the workspace, so there is nothing on disk to inherit; the running process is the only record. `pane_destination` is already asking herdr what the tab's panes are running, so the profile costs no round trip -- `profile_among` reads the same `PaneProcessInfo` that `workspace_among` does, and reads it off the *same* pane, so a tab holding two sessions cannot pair one session's workspace with another's account. Two properties of the reader are load-bearing, and one case decides both. `aid <ws> add --claude-profile support to the docs` becomes a dl line whose prompt is a single argument holding that text, and the ssh transport carries the same string as one payload argument. Elements are compared whole, so neither is an argv element equal to `--claude-profile` and neither is read as a flag; and the scan stops at the first bare `--`, which says it a second way and cheaply. A real flag before the `--` is still read, so the stop is a stop and not a refusal. Deliberately not gated on the program name. `dl` on a host may be `dl`, `dl-next` or an absolute path, and matching that family by prefix is exactly the fuzzy test that made `ssh_host` necessary -- a sibling pane running `ssh -F myconf.devpod somehost` once made the pane shell claim a workspace called `myconf`. The pane-level coupling is the stronger guard: the profile is asked only of a pane that has already named a workspace, so an unrelated pane is never consulted. `PaneDestination::Workspace` becomes a struct variant and moves the public-API snapshot.
`dl --claude-profiles` names the account behind each profile, but only where somebody thinks to look. herdr's sidebar is where the panes actually are, and it had the directory name or nothing. So a launch that resolved a `Reporting` also reports the profile as a display-only metadata token, which herdr renders as `$profile` in an agent row. A token rather than `--display-agent`: that field is the agent name `report-agent` sets and the one herdr picks a detection manifest by, so writing a profile into it would corrupt state detection to gain a label. **Clearing matters as much as setting**, and is why this is called with `None` too. A pane is reused. A launch naming `work` followed by one naming nothing would otherwise leave the sidebar asserting an account the running session is not using -- the exact mislabelling `--claude-profiles` exists to prevent, moved somewhere more visible and more trusted. Display only, so it cannot disturb the idle/working/blocked the Claude Code hook reports through the forwarded socket. And reported then tolerated, like everything else in this flow: a herdr that is gone, too old for `report-metadata` or merely slow costs the label and never the session. The signature carries most of that guarantee, since there is no outcome to branch on. Syntax verified against herdr 0.8.2 rather than guessed: the socket-API docs name the method `pane.report_metadata` and document no CLI wrapper, so herdr was installed from conda-forge and asked. `pane report-metadata [OPTIONS] --source <ID> <PANE_ID>`, with `--token NAME=VALUE` and `--clear-token NAME`. The pane id is given first, matching the `report-agent` invocation in HOOK that was measured against a live herdr. A guessed argv would have been a feature that never worked and never said so, since every call in this subsystem ends `>/dev/null 2>&1`. `devlaunch:claude` was a bare literal in HOOK and is now a named constant with the diff test the standing rule asks for.
One row removed and three added, and the removal is the interesting half: `PaneDestination::Workspace` stops being a tuple variant and becomes a struct variant carrying `workspace_id` and `claude_profile`. That is a breaking change to anything that constructs or matches it positionally, and it is in the tripwire file rather than the promise file, so the snapshot is the only place it shows. It is in-tree only today -- `dl` is the sole caller -- which is exactly why it is worth writing down: nothing else would have said so. The variant grew a field rather than gaining a sibling because the profile is a property of the destination, not a different kind of destination: a pane opened beside an agent inherits the account that agent is running as, and a pane opened anywhere else has no account to inherit. Regenerated with `scripts/public-api-snapshots.sh` on nightly 1.100.0 (2026-09-03) and cargo-public-api 0.52.0, the pin the script names.
Two behaviours, and the second is the one worth the words: a pane opened beside an agent inherits that agent's account, where before it opened in the right workspace under the wrong login. A workspace is not an account.
1c16315 to
f9cfd93
Compare
c920883 to
b60c6da
Compare
|
Superseded by #573, which is the same branch opened from this repository rather than from a fork. Moved because a cross-fork pull request's base has to be a branch in the base repository, so the stack needed its parents pushed here anyway -- which left three branch names living on two remotes, two of them serving as one PR's head and another's base at the same time. Every update then had to reach both remotes or a diff would quietly misrepresent itself. Same commits, one ref each, and #570 to #573 are now a native GitHub stack. |
Two behaviours, and the second is the one that was actually wrong.
A pane's row says which account the agent in it is running as
Reported to herdr as the display label
profile=<name>. Aprofile is chosen per launch and forwarded per session, so two tabs side by side
can be two different accounts with nothing on screen to tell them apart -- and the
failure that matters here is not noticing.
Reported under devlaunch's own source name, so it sits beside herdr's own labels
rather than overwriting one, and cleared rather than left stale when a launch
forwards the default login.
A pane opened beside an agent inherits that agent's profile
The pane shell shipped in 0.29.0 already opens in the workspace its tab holds. A
workspace is not an account, so it read the default login while the agent one
pane over ran as another -- the right container under the wrong identity, which is
the same class of mistake profiles exist to prevent.
It is read from the agent's own argv rather than from a note kept anywhere, for the
reason the pane shell keeps nothing: the argv is what is true, and a record would be
a second copy of it that can go stale.
Two details in that read, both deliberate:
--. A prompt that containsthe words
--claude-profile workis a prompt.and gating on it would be a second thing to keep in step with the transport.
Nothing is inherited across a boundary
default login.
profile reaches the next session and never the one on screen. Worth stating
because a label makes the opposite look plausible.
Public surface
One row removed and three added, and the removal is the interesting half:
PaneDestination::Workspacestops being a tuple variant and becomes a structvariant carrying
workspace_idandclaude_profile.That is a breaking change to anything constructing or matching it
positionally, and it lives in the tripwire file rather than the promise file, so
the snapshot is the only place it shows.
dlis the sole caller today, which isexactly why it is worth writing down: nothing else would have said so.
The variant grew a field rather than gaining a sibling because the profile is a
property of the destination, not a different kind of destination.
🤖 Generated with Claude Code
https://claude.ai/code/session_01AdSFnBdxie6TosHVmjLY28
Summary by Sourcery
Show Claude account context in herdr and carry the active agent profile into adjacent panes.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests:
Chores: