Repository navigation
Description
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_sessiontool exists → App mode → sub-sessionscreate_sessionabsent → CLI mode → sub-agents viatask
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:
- Formalize into a
SpawnBackendinterface with two implementations - Add
detectPlatform()that checks tool availability - Wire App backend to use
create_sessionwithcoordinate_with_creator: true+notify_on_idle: "once" - Update
squad.agent.mdcoordinator instructions to prefercreate_sessionwhen available
Questions for Copilot App Team (@Clancey)
notify_on_idlereliability — guaranteed delivery for result collection? (replacesread_agent)- Cross-session message ordering — are messages ordered? Can they drop?
- Session cleanup — do completed sub-sessions auto-close or persist forever?
- 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
github-actions commentedon Jun 24, 2026
📋 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.
bradygaster commentedon Jun 24, 2026
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 UXLifecycle:
create_sessionwith name"🏗️ Flight — {task}"send_session_messageto existing session + rename to reflect new taskBenefits over ephemeral model
create_sessioncalls"{emoji} {Name} — {current task or 'idle'}"— the left nav reads like a flight director scanning the roomQuestions for @Clancey
rename_sessiontool from within the session) — or can only the parent coordinator rename child sessions? This determines who updates the status line.find_session_by_nameor similar lookup? The coordinator needs to check "does Flight's session already exist?" before deciding create vs message.list_sessions_and_chatsworks but a direct lookup would be cleaner.🏗️ 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 dispatchesThis 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.bradygaster commentedon Jun 24, 2026
Answers from @Clancey (Copilot App team)
All questions answered — no blockers. Summary:
notify_on_idlereliable?tasktool) just disappear.rename_sessiontool 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:
tasktool agents, which disappear when done.Status: Ready for Implementation
No platform changes needed. The implementation is:
squad.agent.md— prefercreate_sessionwhen available"{emoji} {Name} — {task or idle}"list_sessions_and_chats→ find existing →send_session_messageinstead of re-creatingtasktool unchangedAll platform capabilities confirmed by @Clancey. Ship it.
bradygaster commentedon Jun 24, 2026
Finding: Sub-sessions don't self-rename
During live prototyping, we discovered that sub-sessions created via
create_sessiondo not reliably rename themselves when dispatched work viasend_session_message. Even with explicit instructions to callrename_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_sessioncall 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 detectionWithout this, users can't glance at the left nav and know what each member is doing.
Possible Solutions (question for @Clancey)
Coordinator renames child sessions externally — new capability:
rename_sessionaccepts an optionalsession_idparam to target another session (not just self). Coordinator dispatches work AND renames in one turn.send_session_messageaccepts arenameparam — when delivering a message, optionally update the session name atomically. One tool call = dispatch + status update. Cleanest from coordinator's perspective.Agents self-rename reliably — if
rename_sessionIS 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.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_messagewithrename) 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_sessionavailable 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?bradygaster commentedon Jun 24, 2026
Finding: rename_session guardrails + final architecture decision
Why self-rename didn't work
From @Clancey's agent (
rename_session.rssource analysis):rename_sessiononly fires on placeholder/fallback titles. If a session already has a real name (set viacreate_session'snameparam), the tool no-ops — by design.name_sourceisUser, the tool skips entirely.rename_sessionoperates oninv.session_idonly — the calling session's own ID.So our test failed because we set names at creation time, which locked out the agent's
rename_sessioncall. 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:
devevery time (~1s worktree creation)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.mdinstruction update.bradygaster commentedon Jun 24, 2026
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_sessionrepeatedly — 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:
rename_sessioncall: always succeeds (agent is deliberately updating its own status)name_source: Userlock) ✅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 — idleKey properties:
What this means architecturally
send_session_message— agent renames itself to reflect new taskrename_sessionto ""{emoji} {Name} — idle""This is the Mission Control room. Consoles don't move. Their displays update.
bradygaster commentedon Jun 24, 2026
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:
rename_session("🧪 FIDO — idle")→ Skipped — ""Skipped renaming session because it was already renamed.""rename_session("🧪 FIDO — working on some other thing")→ Skipped — same messageThe 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:
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.
Add a
forceparam —rename_session(title, force: true)bypasses the ""already renamed"" check. Squad agents would always passforce: true.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?
fix(coordinator): address #1385 review follow-ups (#1390)
feat(coordinator): spawn cast members as sub-sessions in Copilot App (g…
chore: promote dev -> main (v0.12.0) (#1698)