Skip to content

feat: Assign unattributed Claude Code sessions to agents #143

Description

@edspencer

Background

With the recent attribution-based session filtering (commits d8c4df6, e4f8a3f on all-chats), the Fleet view now only shows sessions that are attributed to each specific agent. This is correct — when multiple agents share the same working directory, we don't want native CLI sessions duplicated under every agent.

However, this means unattributed sessions (created via claude CLI directly, not through herdctl) are only visible in the global recent sessions list and All Chats view. Users who have pre-existing Claude Code conversations they want to manage through herdctl have no way to associate them with an agent.

Feature: Assign to Agent

Allow users to assign unattributed Claude Code sessions to a specific agent from the web UI. This is a metadata-only operation — it creates a herdctl session file in .herdctl/sessions/ that maps the existing session ID to the chosen agent. No JSONL files are modified.

Once assigned:

  • The session appears under the agent in the Fleet sidebar
  • The session history is fully viewable (already works today for read-only sessions)
  • The user can continue the conversation through the web UI — the SDK picks up the existing JSONL by session ID, loads conversation history into context, and continues seamlessly

UI Flow

Entry Point

When viewing an unattributed session in the chat view, show an "Assign to Agent" button (exact placement TBD — likely in the session header area).

Dialog

Clicking the button opens a dialog with:

  1. Agent Picker — dropdown or select showing agents that share the same working directory as this session. When multiple agents resolve to the same directory (common pattern: agents in subdirectories with working_directory: ../../), the user picks which one to assign to.

  2. Session Table — a table of all unattributed sessions in the same working directory, with three columns:

    Column Description
    ☐ (checkbox) Row-level selection. Header checkbox selects/deselects all.
    Session Name customNameautoNamepreview"Untitled session" fallback chain
    Date Relative time (e.g., "2 hours ago") or absolute date
    • The session that triggered the dialog should be scrolled into view with its checkbox pre-checked
    • All other sessions start unchecked
  3. Action Buttons — two side-by-side submit buttons:

    Button Behavior
    Assign Selected to Agent Assigns only the checked sessions to the selected agent
    Assign All to Agent Shortcut — assigns every unattributed session in the directory to the selected agent (equivalent to select-all + assign selected)

Backend

For each session being assigned:

  • Create a platform session YAML file in .herdctl/sessions/ mapping sessionId → agentName with origin: "web"
  • Invalidate the session discovery cache for the affected working directory
  • The session will immediately appear under the agent in the Fleet sidebar on next load

Technical Notes

  • The attribution system (session-attribution.tsbuildAttributionIndex()) already reads .herdctl/sessions/ platform files. No changes needed to the attribution layer — just create the files.
  • SessionDiscoveryService.getAllSessions() already identifies unattributed sessions (those with origin: "native" and no agentName). The dialog's session list can be populated from this.
  • Agent list for the picker: filter FleetManager.getAgents() to those whose resolved workingDirectory matches the session's directory.
  • The getAgentSessions() attribution filter (commit e4f8a3f) means newly assigned sessions will immediately appear under the correct agent without any additional changes.

Out of Scope (for now)

  • Un-assigning / reassigning sessions (can be done later)
  • Auto-assigning based on heuristics (git branch matching, etc.)
  • Assigning sessions from the All Chats page (start with the single-session chat view entry point)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions