Squad.Agents.AI 0.5.0: auto-inject --agent squad (CLI parity by default)#1275
Merged
tamirdresher merged 2 commits intoJun 11, 2026
Conversation
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>
Contributor
🟡 Impact Analysis — PR #1275Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affectedroot (4 files)
tests (1 file)
This report is generated automatically for every PR. See #733 for details. |
Contributor
🛫 PR Readiness Check
PR Scope: 🔧 Infrastructure
|
| 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.
Contributor
There was a problem hiding this comment.
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}inSquadAgentwhen 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
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>
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The whole point of
SquadAgentis 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 thetasktool, so it role-played responses inline.Concretely: this SDK call
did NOT behave the same as running
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--agentflag — same discovery semantics, same selection logic.squad.agent.mdexists at.github/agents/sessionConfig.Agent = ""squad""squad.agent.mdmissing (team not yet Squad-initialized)sessionConfig.Agentunset (= 0.4.x behavior)AgentFileName = ""data""anddata.agent.mdexistssessionConfig.Agent = ""data""AgentFileName = nullsessionConfig.AgentunsetsessionConfig.AgentinsideConfigureSessionConfigureSessioncallback runs after this default, so it always winsNew
SquadAgentOptions.AgentFileName(default""squad"") controls the lookup.Why
SessionConfig.Agentinstead of--agentCLI argsThe first version of this change appended
--agent squadtoCliArgs. That worked, but it was the wrong layer — turns outSessionConfigBasealready exposes anAgent(string) property that the SDK uses for exactly this purpose. Using the SDK-native API is cleaner:ConfigureSessioncallback (runs after our default is applied)EnableConfigDiscovery+ConfigDirectory)Net effect
After 0.5.0, this:
behaves identically to:
…without the consumer doing anything. For Aspire consumers (or anyone wrapping a Squad team), the friction of remembering to manually pass
--agent squadgoes 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 againstSessionConfig.Agent):AgentFileNameis""squad""Agent = ""squad""whensquad.agent.mdexistsAgentunset when the file is missing (graceful degradation)AgentFileName=""data""setsAgent = ""data""whendata.agent.mdexistsAgentFileName=nullleavesAgentunsetAgentFileName="" ""leavesAgentunsetConfigureSessioncan override the auto-set value (proves the override path works)Backward compatibility
Fully backward-compatible:
C:\squad-team-rootpath → the file-existence check leavesAgentunset → no test changes needed.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.sessionConfig.AgentinsideConfigureSession: their explicit value continues to win (callback runs after our default).Real-world motivation
The
CommunityToolkit.Aspire.Hosting.Squadexample (CommunityToolkit/Aspire#1394) hit this exact issue:POST /askwith ""team, do X"" returned coordinator role-play instead of dispatching real subagents. The current workaround there is:With 0.5.0 it collapses to:
…and the wrapped coordinator behaves exactly like
copilot --agent squad.