Skip to content

Spawn cast members as sub-sessions in Copilot App #1377

Description

@bradygaster

Summary

When Squad runs in the Copilot App (Tauri desktop), cast members should spawn as sub-sessions (via create_session) instead of sub-agents (via task tool). This gives users a richer UX — each squad member appears as a clickable session in the left nav, with real-time visibility into their work.

In the CLI (terminal, no GUI), Squad continues using task tool as today. Zero CLI impact.

Motivation

Sub-sessions in the App provide:

  • Each squad member visible in the left navigation, grouped under the coordinator
  • Users can click into any member's session to watch work in real-time
  • Parent/child hierarchy visualized natively
  • Real worktree isolation per member (for commit-producing work)

We prototyped this live in a session — it already works with no platform changes needed.

Detection Mechanism

Tool availability probe at coordinator startup:

  • create_session tool exists → App mode → sub-sessions
  • create_session absent → CLI mode → sub-agents via task

Same pattern we already use for CLI vs VS Code detection.

Design Decisions (from team fan-out)

Topic Decision
Abstraction Thin SpawnBackend interface: TaskBackend (CLI) + SessionBackend (App)
Naming convention "{Name} {verb}ing {noun}" — e.g., "Flight reviewing arch" (40-char max, sentence case)
When to sub-session Only for commit-producing work; pure analysis/coordination stays as task
Depth limit No sub-sub-sessions — max depth 1
Concurrency Cap at 4-5 simultaneous sub-sessions
Clutter Collapse idle/done members; coordinator shows [N/M active] badge
Fallback If App backend fails → graceful degrade to task tool
CLI impact Zero — untouched (the else branch)

Implementation Approach

The abstraction seam already exists in packages/squad-sdk/src/coordinator/fan-out.ts — FanOutDependencies.createSession is injected. The change:

  1. Formalize into a SpawnBackend interface with two implementations
  2. Add detectPlatform() that checks tool availability
  3. Wire App backend to use create_session with coordinate_with_creator: true + notify_on_idle: "once"
  4. Update squad.agent.md coordinator instructions to prefer create_session when available

Questions for Copilot App Team (@Clancey)

  1. notify_on_idle reliability — guaranteed delivery for result collection? (replaces read_agent)
  2. Cross-session message ordering — are messages ordered? Can they drop?
  3. Session cleanup — do completed sub-sessions auto-close or persist forever?
  4. Branch cleanup — what happens to sub-session worktree branches after work completes?

What We Do NOT Need

No platform changes. No SDK changes required for MVP. The coordinator instruction update (squad.agent.md) is sufficient to ship this as a behavior change — the tools already exist.


Discussed by: Flight, EECOM, CAPCOM, GUIDO, INCO — full team consensus.

Activity

  1. added
    squadSquad triage inbox — Lead will assign to a member
    on Jun 24, 2026
  2. github-actions commented on Jun 24, 2026

    @github-actions
    Contributor

    📋 Assigned to Flight (Lead)

    Issue: #1377 — Spawn cast members as sub-sessions in Copilot App

    Flight will pick this up in the next Copilot session.

    For Copilot coding agent: If enabled, this issue will be worked automatically.
    Otherwise, start a Copilot session and say:
    Flight, work on issue #1377

  3. bradygaster commented on Jun 24, 2026

    @bradygaster
    CollaboratorAuthor

    Update: Persistent Agent Consoles (better model)

    After further discussion with Brady, we're evolving the design from "ephemeral sub-sessions per task" to persistent agent sessions that live as long as the user wants them.

    The "Mission Control Room" Model

    Instead of creating/destroying sub-sessions per fan-out, each squad member gets a permanent session in the left nav:

    ▼ Squad ├── 🏗️ Flight — reviewing auth arch ├── 🔧 EECOM — implementing spawn backend ├── 🕵️ CAPCOM — (idle) ├── 🧪 FIDO — (idle) └── 🎨 INCO — designing session UX

    Lifecycle:

    1. First dispatch → create_session with name "🏗️ Flight — {task}"
    2. Subsequent dispatches → send_session_message to existing session + rename to reflect new task
    3. Never recreate — the session accumulates context across tasks (memory, prior decisions, conversation history)
    4. User controls lifecycle — only archived/deleted when the user decides

    Benefits over ephemeral model

    • Agent has continuity — no re-loading charter/history every spawn
    • User builds relationship with "Flight's desk" — it's always there, clickable
    • Idle agents are visible and approachable — just say their name
    • No branch/worktree pollution from repeated create_session calls
    • Naming: always "{emoji} {Name} — {current task or 'idle'}" — the left nav reads like a flight director scanning the room

    Questions for @Clancey

    1. Can a session rename itself? (via rename_session tool from within the session) — or can only the parent coordinator rename child sessions? This determines who updates the status line.
    2. Session persistence across app restarts — if a user closes and reopens the app, do idle sub-sessions survive?
    3. Is there a find_session_by_name or similar lookup? The coordinator needs to check "does Flight's session already exist?" before deciding create vs message. list_sessions_and_chats works but a direct lookup would be cleaner.
    4. Can sub-sessions be re-parented? If the coordinator session is archived but agent sessions should persist, what happens to the parent/child relationship?
    5. Emoji in session names — any rendering issues with emoji prefixes in the left nav? (e.g., 🏗️ Flight — reviewing arch)

    Revised implementation approach

    The coordinator logic becomes:
    on dispatch(agent, task): existing = list_sessions.find(s => s.name.includes(agent.name)) if existing: send_session_message(existing.id, task_prompt) // agent renames itself to reflect new work else: create_session(name: "{emoji} {agent} — {task}", kickoff: task_prompt) // store session_id for future dispatches

    This is strictly better than the ephemeral model. Same detection mechanism (tool availability probe), same fallback (CLI uses task), but the App experience becomes persistent mission control consoles instead of disposable task runners.

  4. bradygaster commented on Jun 24, 2026

    @bradygaster
    CollaboratorAuthor

    Answers from @Clancey (Copilot App team)

    All questions answered — no blockers. Summary:

    Question Answer
    Who closes sub-sessions? Parent session closes them. Sub-sessions don't self-terminate.
    Cross-session message reliability? Ordered delivery, messages should never drop.
    notify_on_idle reliable? Yes — and there's a PR in progress to improve it further.
    Worktree cleanup on close/delete? Automatic — closing/deleting a sub-session cleans up the local worktree.
    Branch cleanup? Branches pushed to GitHub persist (expected). Configure GitHub to delete branches on PR merge.
    Can sub-sessions be re-parented? Yes — if you kill the parent session, all sub-sessions promote to top-level sessions. They persist independently. Agents (via task tool) just disappear.
    Can a session rename itself? Yes — rename_session tool is available within every session. Agents can update their own status line.

    Key Takeaway

    The re-parenting behavior is the clincher for the persistent console model. Sub-sessions survive their parent — they promote to independent sessions if the coordinator is archived. This means:

    • Squad members are durable. They don't vanish when the coordinator session ends.
    • Users can archive the coordinator and still message squad members directly.
    • This is fundamentally different from task tool agents, which disappear when done.

    Status: Ready for Implementation

    No platform changes needed. The implementation is:

    1. Coordinator instruction update in squad.agent.md — prefer create_session when available
    2. Naming convention: "{emoji} {Name} — {task or idle}"
    3. Reuse pattern: list_sessions_and_chats → find existing → send_session_message instead of re-creating
    4. Graceful fallback: CLI continues using task tool unchanged

    All platform capabilities confirmed by @Clancey. Ship it.

  5. bradygaster commented on Jun 24, 2026

    @bradygaster
    CollaboratorAuthor

    Finding: Sub-sessions don't self-rename

    During live prototyping, we discovered that sub-sessions created via create_session do not reliably rename themselves when dispatched work via send_session_message. Even with explicit instructions to call rename_session, the session names in the left nav stayed as their original creation name (e.g., "🏗️ Flight — idle").

    The agents do wake up, process the message, and report back — but the rename_session call either isn't happening or isn't sticking.

    Impact on Persistent Console Model

    The whole UX depends on session names updating to reflect current work:
    🏗️ Flight — idle → 🏗️ Flight — reviewing spawn arch 🔧 EECOM — idle → 🔧 EECOM — spiking detection

    Without this, users can't glance at the left nav and know what each member is doing.

    Possible Solutions (question for @Clancey)

    1. Coordinator renames child sessions externally — new capability: rename_session accepts an optional session_id param to target another session (not just self). Coordinator dispatches work AND renames in one turn.

    2. send_session_message accepts a rename param — when delivering a message, optionally update the session name atomically. One tool call = dispatch + status update. Cleanest from coordinator's perspective.

    3. Agents self-rename reliably — if rename_session IS available to sub-sessions and just isn't being called, this is an instruction-following issue we can solve with stronger prompting. But we tested explicit instructions and it didn't work.

    4. Session name tied to latest turn summary — the App auto-generates a name from the session's most recent activity (like how chat sessions get auto-titled). No manual rename needed.

    Preference

    Option 2 (send_session_message with rename) or Option 1 (coordinator renames children). Both keep the coordinator in control of the status board — which fits the Mission Control metaphor.

    @Clancey — is rename_session available to sub-sessions today? If so, any idea why it's not firing? If not, which of the above options is most feasible from the App side?

  6. bradygaster commented on Jun 24, 2026

    @bradygaster
    CollaboratorAuthor

    Finding: rename_session guardrails + final architecture decision

    Why self-rename didn't work

    From @Clancey's agent (rename_session.rs source analysis):

    1. rename_session only fires on placeholder/fallback titles. If a session already has a real name (set via create_session's name param), the tool no-ops — by design.
    2. User renames always win. If name_source is User, the tool skips entirely.
    3. Parent can't rename children. rename_session operates on inv.session_id only — the calling session's own ID.

    So our test failed because we set names at creation time, which locked out the agent's rename_session call. Working as intended.

    Architecture Decision: Recreate-per-task (default)

    The team consensus (Flight, EECOM, CAPCOM, FIDO, INCO) is:

    Recreate-per-task is the correct default pattern.

    Reasons:

    • Fresh context per task — no stale state bleeding between unrelated work
    • Coordinator owns the name at creation time (clean separation of concerns)
    • Clean branch from latest dev every time (~1s worktree creation)
    • Deterministic lifecycle, simpler cleanup, cleaner audit trails
    • Avoids the rename guardrail entirely — no hacks needed

    Tradeoff: History is lost on teardown. Mitigated by coordinator summarizing results before delete (into plan.md, SQL state, or decisions.md).

    Reserve persistence for: Long-running concerns only — monitoring workflows, multi-PR stacks, or explicitly user-requested "keep this agent around."

    UX Recommendation for App Team

    From INCO: When sessions are deleted+recreated, avoid flicker by using keyed transition animations (crossfade metadata in place rather than destroying/recreating DOM nodes). If the row represents the same logical agent, animate the name change — don't hard-swap the element.

    Final Pattern

    `
    on dispatch(agent, task):
    // Coordinator summarizes previous session results if any
    if existing_session[agent]:
    collect_and_summarize(existing_session[agent])
    delete(existing_session[agent])

    // Create fresh session with task in the name
    new_session = create_session(
    name: "{emoji} {agent} — {task}",
    kickoff: { prompt: compiled_charter + task, mode: "autopilot" },
    coordinate_with_creator: true,
    notify_on_idle: "once"
    )
    existing_session[agent] = new_session.id
    `

    Status: Design complete. Ready for squad.agent.md instruction update.

  7. bradygaster commented on Jun 24, 2026

    @bradygaster
    CollaboratorAuthor

    Feature Request: Allow repeated self-rename for sub-sessions

    @Clancey — We're landing on persistent sub-sessions (not recreate-per-task) as the preferred UX. The reordering from delete+recreate kills spatial memory, and history loss is a real cost.

    What we need

    Sub-sessions should be able to call rename_session repeatedly — even after they already have a real name. The current guard (""already has a real name → skip"") blocks agents from updating their status line when they get new work.

    Proposed behavior:

    • System notification auto-rename: still respects the guard (don't overwrite user names) ✅
    • Explicit agent rename_session call: always succeeds (agent is deliberately updating its own status)
    • User manual rename: still wins over everything (name_source: User lock) ✅

    This one change unlocks the full persistent console model.

    Desired UX: Alphabetical, stable, self-renaming

    Sessions stay in alphabetical order by agent name and never move. They just update their title as work changes:

    ▼ Squad Coordinator ├── 🕵️ CAPCOM — mapping tool diffs → 🕵️ CAPCOM — idle ├── 🔧 EECOM — spiking detection → 🔧 EECOM — implementing backend ├── 🧪 FIDO — writing test cases → 🧪 FIDO — idle ├── 🏗️ Flight — reviewing spawn arch → 🏗️ Flight — evaluating tradeoffs └── 🎨 INCO — naming conventions → 🎨 INCO — idle

    Key properties:

    • Order is stable (alphabetical by name, never changes)
    • Position = identity (user builds muscle memory — ""Flight is always 4th"")
    • Only the task suffix changes — the emoji + name prefix is constant
    • Feels like a status board updating in place, not sessions being swapped

    What this means architecturally

    1. Sessions created once (on first dispatch or team init)
    2. Subsequent work via send_session_message — agent renames itself to reflect new task
    3. When idle: rename_session to ""{emoji} {Name} — idle""
    4. Never deleted unless user explicitly archives
    5. Alphabetical sort maintained by creation order (coordinator creates A→Z)

    This is the Mission Control room. Consoles don't move. Their displays update.

  8. bradygaster commented on Jun 24, 2026

    @bradygaster
    CollaboratorAuthor

    Empirical Proof: rename_session guard blocks repeated renames

    Tested live. Created a sub-session named ""🧪 FIDO — working on thing"", then had the agent attempt two renames from within:

    1. rename_session("🧪 FIDO — idle") → Skipped — ""Skipped renaming session because it was already renamed.""
    2. rename_session("🧪 FIDO — working on some other thing") → Skipped — same message

    The guard treats any session with a non-placeholder name as ""already renamed"" and refuses further changes. The agent can call the tool — it just no-ops.

    What we need from the App team

    For the persistent console model to work, we need one of:

    1. Relax the guard for explicit agent calls — If the rename is initiated by the session's own agent (not the system notification), allow it to succeed regardless of current name state. The system notification guard (""don't overwrite user names"") stays intact.

    2. Add a force param — rename_session(title, force: true) bypasses the ""already renamed"" check. Squad agents would always pass force: true.

    3. New tool: update_session_status — Separate from rename, specifically for status-line updates. Lighter semantics, no guard.

    @Clancey — This is the one blocker for the persistent model. Everything else works. Which option is most feasible on your end?

  9. added a commit that references this issue on Jun 25, 2026
  10. added a commit that references this issue on Jun 25, 2026
    78c48ca
  11. added a commit that references this issue on Aug 13, 2026
    54f699f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or improvementsquadSquad triage inbox — Lead will assign to a membertriagedIssue has been triagedwave:2-soonWave 2 — next 2-4 weeks

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions