Skip to content

Squad.Agents.AI 0.5.0: auto-inject --agent squad (CLI parity by default)#1275

Merged
tamirdresher merged 2 commits into
bradygaster:devfrom
tamirdresher:feat/squad-agents-ai-default-coordinator-agent
Jun 11, 2026
Merged

Squad.Agents.AI 0.5.0: auto-inject --agent squad (CLI parity by default)#1275
tamirdresher merged 2 commits into
bradygaster:devfrom
tamirdresher:feat/squad-agents-ai-default-coordinator-agent

Conversation

@tamirdresher

@tamirdresher tamirdresher commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

The whole point of SquadAgent is to wrap a Squad coordinator team — but the 0.4.x SDK started the wrapped session with the SDK's built-in generic agent. The coordinator therefore had no instructions to eager-execute, fan out, or dispatch via the task tool, so it role-played responses inline.

Concretely: this SDK call

builder.Services.AddSquadAgent(o => o.SquadFolderPath = teamRoot);

did NOT behave the same as running

copilot --agent squad

interactively against the same team root.

What changes

0.5.0 makes SessionConfig.Agent = ""squad"" the SDK default. SessionConfigBase.Agent (string) is the SDK's first-class equivalent of the CLI's --agent flag — same discovery semantics, same selection logic.

Scenario What the SDK does
Default — squad.agent.md exists at .github/agents/ Sets sessionConfig.Agent = ""squad""
squad.agent.md missing (team not yet Squad-initialized) Leaves sessionConfig.Agent unset (= 0.4.x behavior)
Consumer sets AgentFileName = ""data"" and data.agent.md exists Sets sessionConfig.Agent = ""data""
Consumer sets AgentFileName = null Leaves sessionConfig.Agent unset
Consumer overrides sessionConfig.Agent inside ConfigureSession The ConfigureSession callback runs after this default, so it always wins

New SquadAgentOptions.AgentFileName (default ""squad"") controls the lookup.

Why SessionConfig.Agent instead of --agent CLI args

The first version of this change appended --agent squad to CliArgs. That worked, but it was the wrong layer — turns out SessionConfigBase already exposes an Agent (string) property that the SDK uses for exactly this purpose. Using the SDK-native API is cleaner:

  • No CLI argv munging
  • No ""did the consumer already pass --agent?"" detection
  • Override mechanism is the natural ConfigureSession callback (runs after our default is applied)
  • Discovery still uses the SDK's own machinery (EnableConfigDiscovery + ConfigDirectory)

Net effect

After 0.5.0, this:

builder.Services.AddSquadAgent(o => o.SquadFolderPath = teamRoot);

behaves identically to:

copilot --agent squad

…without the consumer doing anything. For Aspire consumers (or anyone wrapping a Squad team), the friction of remembering to manually pass --agent squad goes away.

Tests

64 → 71 tests, all passing on net8.0 / net9.0 / net10.0.

New SquadAgentDefaultAgentTests (per-test temp dir scaffolds or omits the agent file deterministically, then asserts against SessionConfig.Agent):

  • Default AgentFileName is ""squad""
  • Auto-sets Agent = ""squad"" when squad.agent.md exists
  • Leaves Agent unset when the file is missing (graceful degradation)
  • Custom AgentFileName=""data"" sets Agent = ""data"" when data.agent.md exists
  • AgentFileName=null leaves Agent unset
  • AgentFileName="" "" leaves Agent unset
  • ConfigureSession can override the auto-set value (proves the override path works)

Backward compatibility

Fully backward-compatible:

  • Existing routing tests use a non-existent C:\squad-team-root path → the file-existence check leaves Agent unset → no test changes needed.
  • Existing consumers that worked at 0.4.x without configuring SessionConfig.Agent: if the team is Squad-initialized, they now get the coordinator agent automatically (which is what they would have wanted). If the team is NOT Squad-initialized, behavior is unchanged.
  • Existing consumers that set sessionConfig.Agent inside ConfigureSession: their explicit value continues to win (callback runs after our default).

Real-world motivation

The CommunityToolkit.Aspire.Hosting.Squad example (CommunityToolkit/Aspire#1394) hit this exact issue: POST /ask with ""team, do X"" returned coordinator role-play instead of dispatching real subagents. The current workaround there is:

builder.Services.AddKeyedSquadAgent(""research-squad"", opts =>
{
    opts.AgentName = ""research-squad"";
    opts.CliArgs.Add(""--agent"");
    opts.CliArgs.Add(""squad"");
});

With 0.5.0 it collapses to:

builder.Services.AddKeyedSquadAgent(""research-squad"");

…and the wrapped coordinator behaves exactly like copilot --agent squad.

The whole point of SquadAgent is to wrap a Squad coordinator team — but the
0.4.x SDK launched the underlying copilot.exe with the CLI's built-in generic
agent. The coordinator therefore had no instructions to eager-execute, fan
out, or dispatch via the task tool, so it role-played responses inline.

Concretely: this SDK call

  builder.Services.AddSquadAgent(o => o.SquadFolderPath = teamRoot);

did NOT behave the same as running

  copilot --agent squad

interactively against the same team root. Consumers had to remember to add
`opts.CliArgs.Add(""--agent""); opts.CliArgs.Add(""squad"");` themselves, which
is an SDK leak — the class is literally called SquadAgent.

0.5.0 makes --agent squad the SDK default:

* New `SquadAgentOptions.AgentFileName` (defaults to `""squad""`).
* On client construction, SquadAgent looks for
  `{teamRoot}/.github/agents/{AgentFileName}.agent.md`. If it exists,
  `--agent {AgentFileName}` is auto-prepended to the CLI args.
* If the file is missing (folder not Squad-initialized), the inject is
  silently skipped and a Debug log line explains why. The CLI then starts
  with its default agent, which is what 0.4.x did anyway.
* If the consumer already supplied `--agent X` in `CliArgs`, the explicit
  value wins and we do NOT add a second one.
* Set `AgentFileName = null` (or whitespace) to opt out entirely.

Net effect: SquadAgent.RunAsync now matches `copilot --agent squad` for
any Squad-initialized team root, without the consumer doing anything.

## Tests

64 -> 71 tests, all passing on net8.0/9.0/10.0.

New `SquadAgentDefaultAgentFlagTests` (uses a per-test temp dir to scaffold
or omit the agent file deterministically):
* Default AgentFileName is ""squad""
* Auto-inject when squad.agent.md exists
* No inject when the file is missing (graceful degradation)
* Explicit --agent in CliArgs wins (no second --agent added)
* Custom AgentFileName=""data"" injects --agent data when data.agent.md exists
* AgentFileName=null opts out entirely
* AgentFileName=whitespace opts out entirely

Backward compatibility: existing routing tests use a non-existent
`C:\squad-team-root` path, so the file-existence check silently skips the
inject — those tests continue to pass with no changes.

## Files

* `src/Squad.Agents.AI/SquadAgentOptions.cs` — new `AgentFileName`
  property with XML doc covering the default, the opt-out, and the
  not-yet-initialized fallback.
* `src/Squad.Agents.AI/SquadAgent.cs` — auto-inject logic in
  `CreateCopilotClient` (after the `--allow-all` block, before
  `options.CliArgs` are appended) with file-existence + already-supplied
  guards and a Debug log when the file is missing.
* `src/Squad.Agents.AI/Squad.Agents.AI.csproj` — bump 0.4.0 -> 0.5.0.
* `src/Squad.Agents.AI/README.md` — new ""Coordinator agent selection""
  section with the precedence table; `AgentFileName` row added to Key
  Options table.
* `+test/Squad.Agents.AI.Tests/SquadAgentDefaultAgentFlagTests.cs`

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 11, 2026 15:04
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🟡 Impact Analysis — PR #1275

Risk tier: 🟡 MEDIUM

📊 Summary

Metric Count
Files changed 5
Files added 1
Files modified 4
Files deleted 0
Modules touched 2

🎯 Risk Factors

  • 5 files changed (≤5 → LOW)
  • 2 modules touched (2-4 → MEDIUM)

📦 Modules Affected

root (4 files)
  • src/Squad.Agents.AI/README.md
  • src/Squad.Agents.AI/Squad.Agents.AI.csproj
  • src/Squad.Agents.AI/SquadAgent.cs
  • src/Squad.Agents.AI/SquadAgentOptions.cs
tests (1 file)
  • test/Squad.Agents.AI.Tests/SquadAgentDefaultAgentTests.cs

This report is generated automatically for every PR. See #733 for details.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

ℹ️ This comment updates on each push. Last checked: commit 304ab35

PR Scope: 🔧 Infrastructure

⚠️ 3 item(s) to address before review

Status Check Details
Single commit 2 commits — consider squashing before review
Not in draft Ready for review
Branch up to date Up to date with dev
Copilot review No Copilot review yet — it may still be processing
Changeset present No source files changed — changeset not required
Scope clean No .squad/ or docs/proposals/ files
No merge conflicts No merge conflicts
Copilot threads resolved 0 active Copilot thread(s) resolved (1 outdated skipped)
CI passing 4 check(s) failing: test, samples-build, sdk-exports-validation, Policy Gates

Files Changed (5 files, +269 −1)

File +/−
src/Squad.Agents.AI/README.md +13 −0
src/Squad.Agents.AI/Squad.Agents.AI.csproj +1 −1
src/Squad.Agents.AI/SquadAgent.cs +31 −0
src/Squad.Agents.AI/SquadAgentOptions.cs +30 −0
test/Squad.Agents.AI.Tests/SquadAgentDefaultAgentTests.cs +194 −0

Total: +269 −1


This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Squad.Agents.AI SDK to default to the Squad coordinator agent by auto-injecting --agent {AgentFileName} (default "squad") when an appropriate .github/agents/{name}.agent.md file exists, bringing SDK behavior in line with copilot --agent squad.

Changes:

  • Add SquadAgentOptions.AgentFileName (default "squad") and document opt-out / explicit override behavior.
  • Auto-inject --agent {AgentFileName} in SquadAgent when the agent file exists and the host didn’t already supply --agent.
  • Add tests for default agent flag behavior; bump package version to 0.5.0; update README.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/Squad.Agents.AI.Tests/SquadAgentDefaultAgentFlagTests.cs Adds coverage for the new default --agent injection behavior and edge cases.
src/Squad.Agents.AI/SquadAgentOptions.cs Introduces and documents AgentFileName option (default "squad").
src/Squad.Agents.AI/SquadAgent.cs Implements conditional --agent {AgentFileName} auto-injection based on file existence and explicit args.
src/Squad.Agents.AI/Squad.Agents.AI.csproj Bumps package version from 0.4.0 to 0.5.0.
src/Squad.Agents.AI/README.md Documents the new coordinator agent selection default and adds the new option to the options table.

return;
}
}
Assert.Fail($"Expected `--agent {expectedName}` in CLI args; got: [{string.Join(", ", args)}]");
GitHub.Copilot SDK's SessionConfigBase exposes an Agent (string) property
that is the first-class equivalent of the Copilot CLI's --agent flag. It
discovers and loads .github/agents/{name}.agent.md exactly the same way
the CLI does, but without us having to munge CliArgs.

Switch the 0.5.0 default-coordinator-agent implementation:

- SquadAgent now sets sessionConfig.Agent = options.AgentFileName (default
  ""squad"") right after constructing the SessionConfig, before
  ConfigureSession runs.
- Drop the --agent CliArgs hack (we no longer need to detect ""did the
  consumer already pass --agent?"" because ConfigureSession naturally wins
  over our default).
- Tests now assert against sessionConfig.Agent via reflection over the
  inner DelegatingAIAgent — exactly what consumers using ConfigureSession
  would see.
- README ""Coordinator agent selection"" section reworded to say
  ""sets SessionConfig.Agent"" instead of ""auto-adds --agent"".

71/71 tests still pass on net8.0/9.0/10.0. The fifth new test
(ConfigureSession_CanOverrideAutoSetAgent) explicitly proves the
ConfigureSession callback can replace the auto-set value, which is the
clean override path now that --agent CliArgs is gone.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tamirdresher tamirdresher merged commit dfc5b94 into bradygaster:dev Jun 11, 2026
19 of 23 checks passed
tamirdresher added a commit that referenced this pull request Jun 11, 2026
…ot SessionConfig.Agent) (#1277)

0.5.0 (#1275) replaced the previous --agent CliArgs approach with
sessionConfig.Agent = options.AgentFileName, on the theory that the SDK
property was the first-class equivalent of the CLI's --agent flag.

It is not. SessionConfig.Agent looks up the name in the SDK's CustomAgents
registry (programmatic agent definitions, never populated by SquadAgent),
NOT in .github/agents/*.agent.md files on disk. The result was a runtime
error on every RunAsync call against a Squad-initialised team:

  Communication error with Copilot CLI: Request session.create failed with
  message: Custom agent 'squad' not found

Verified at GitHub.Copilot.SDK 1.0.0:

* SessionConfigBase.Agent (string) — selects from CustomAgents
* SessionConfigBase.CustomAgents (IList<CustomAgentConfig>) —
  programmatically defined inline agents (Name, Prompt, Tools, Skills,
  Model, etc.). Empty by default.
* The CLI's --agent flag is currently the only path that reads
  .github/agents/{name}.agent.md on disk.

0.5.1 reverts to the original CliArgs implementation:

* SquadAgent now auto-prepends '--agent {AgentFileName}' to combinedCliArgs
  (back to what 0.5.0 originally proposed before the SessionConfig.Agent
  detour).
* The file-existence check at {teamRoot}/.github/agents/{name}.agent.md
  still gates the inject so non-Squad-initialized folders degrade
  gracefully (no --agent passed -> CLI uses default agent).
* The 'consumer already supplied --agent in CliArgs' guard is back so the
  SDK does not add a duplicate.

Tests:

* New SquadAgentDefaultAgentFlagTests covers all seven cases via reflection
  over Connection.Args (the CLI-args path the SDK actually uses):
  default 'squad' value, auto-inject when file exists, no inject when
  missing, explicit --agent wins, custom AgentFileName works,
  AgentFileName=null/whitespace opts out.
* The older SquadAgentDefaultAgentTests (which targeted SessionConfig.Agent)
  is removed since that property does NOT do what we wanted.

71/71 tests passing on net8.0/9.0/10.0.

Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants