Squad.Agents.AI 0.3.0: first-class subagent observability (OnSubagentTrace + OTel ActivitySource) - #1265
Conversation
🛫 PR Readiness Check
PR Scope: 🔧 Infrastructure
|
| Status | Check | Details |
|---|---|---|
| ✅ | Single commit | 1 commit — clean history |
| ✅ | 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 | No Copilot review threads |
| ❌ | CI passing | 6 check(s) still running |
Files Changed (6 files, +568 −3)
| File | +/− |
|---|---|
src/Squad.Agents.AI/Squad.Agents.AI.csproj |
+2 −1 |
src/Squad.Agents.AI/SquadAgent.cs |
+21 −2 |
src/Squad.Agents.AI/SquadAgentDiagnostics.cs |
+124 −0 |
src/Squad.Agents.AI/SquadAgentOptions.cs |
+44 −0 |
src/Squad.Agents.AI/SquadSubagentTraceMapper.cs |
+136 −0 |
test/Squad.Agents.AI.Tests/SquadSubagentTraceTests.cs |
+241 −0 |
Total: +568 −3
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
🟡 Impact Analysis — PR #1265Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affectedroot (5 files)
tests (1 file)
This report is generated automatically for every PR. See #733 for details. |
Adds a typed OnSubagentTrace callback + OpenTelemetry ActivitySource so consumers
can see subagent dispatch (the coordinator's 'task' tool spawning specialist
sub-agents and their replies) without writing their own polymorphic dispatch over
the raw GitHub.Copilot SessionEvent hierarchy.
The previous 0.2.x surface required consumers to write the same boilerplate the
aspire-squad-resource demo had to (CopilotSessionTraceMapper.cs): switch on every
event subtype, unwrap subagent context, manage Activity lifetime. 0.3.0 makes that
the SDK's job.
New public surface:
* SquadAgentOptions.OnSubagentTrace (Action<SquadAgentTraceEvent>)
Set this to subscribe to subagent lifecycle (Selected / Started / Completed /
Failed), assistant messages from coordinator AND subagents, tool start/complete,
and SessionIdle. Setting OnSubagentTrace implicitly turns on
SessionConfig.IncludeSubAgentStreamingEvents so subagent replies actually flow
up to the parent session (otherwise they stay inside the subagent session and
never reach the callback).
* SquadAgentTraceEvent record — typed envelope (Kind, RawEventType, Timestamp,
SdkAgentId, SubagentName, SubagentDisplayName, ToolCallId, Content, Success,
RawEvent). Carries the original SessionEvent on RawEvent for advanced consumers
but exposes everything else through neutral primitive types so the callback
signature has no transitive dependency on GitHub.Copilot.SDK.
* SquadAgentTraceEventKind enum — categorises the SessionEvent into the well-known
cases that downstream observability surfaces want.
* SquadAgentDiagnostics.ActivitySourceName ('Microsoft.Agents.AI.Squad') +
SquadAgentDiagnostics.ActivitySource — one Activity per subagent dispatch is
opened on SubagentStartedEvent and disposed on the matching
SubagentCompletedEvent / SubagentFailedEvent, tagged with squad.subagent.name,
squad.subagent.display_name, squad.subagent.sdk_agent_id, and
squad.subagent.reply_preview (a short truncated copy of the subagent's
assistant message). Hosts that .AddSource(SquadAgentDiagnostics.ActivitySourceName)
on their OpenTelemetry tracer get these spans in their backend — the Aspire
dashboard renders them in the trace view automatically.
Internal:
* SquadSubagentTraceMapper — wires SessionEvent -> SquadAgentTraceEvent and the
Activity lifecycle. Held by SquadAgent and disposed during DisposeAsync to
drain any subagent activities that never received a matching Completed event
(e.g. session ended mid-dispatch).
* InternalsVisibleTo=Squad.Agents.AI.Tests so the mapper can be unit tested
directly without spinning a real CLI session.
Tests: 54/54 passing across net8, net9, net10 (+8 new tests for the
observability surface including Activity lifetime, tag propagation, mid-session
disposal, and consumer-callback exception isolation).
Verified end-to-end against the tamresearch1 Star Trek squad: the OnSubagentTrace
callback observed two parallel subagent dispatches (Picard, Data), captured each
of their replies attributed to the right SdkAgentId, and the matching
'squad.subagent Picard' / 'squad.subagent Data' OTel spans opened and closed
cleanly with the reply tagged on each span.
Version bumped 0.2.0 -> 0.3.0 to signal the new public surface.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cdb4dc5 to
60933b9
Compare
There was a problem hiding this comment.
Pull request overview
This PR evolves the Squad.Agents.AI .NET package to expose first-class subagent observability by mapping Copilot SDK SessionEvents into a typed SquadAgentTraceEvent callback surface and emitting OpenTelemetry spans via a dedicated ActivitySource, while also updating routing/config wiring for newer Copilot SDK APIs.
Changes:
- Introduces
SquadAgentOptions.OnSubagentTrace,SquadAgentTraceEvent(Kind/Content/etc.), andSquadAgentDiagnostics.ActivitySourceNamefor typed tracing + OTel integration. - Adds an internal
SquadSubagentTraceMapperto translateSessionEvent→ typed envelope and manage oneActivityper subagent dispatch. - Updates SDK integration details (WorkingDirectory/Connection-based routing) and refreshes tests + package version (
0.3.0).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/Squad.Agents.AI/SquadAgent.cs |
Wires .squad config discovery, adds subagent trace mapper hookup, and updates Copilot client routing/connection logic for SDK 1.0.0-style APIs. |
src/Squad.Agents.AI/SquadAgentOptions.cs |
Switches to GitHub.Copilot types and adds the OnSubagentTrace public surface + docs. |
src/Squad.Agents.AI/SquadAgentDiagnostics.cs |
Adds diagnostics constants (ActivitySourceName), event kind enum, and the typed trace envelope record. |
src/Squad.Agents.AI/SquadSubagentTraceMapper.cs |
Implements event mapping + OTel Activity lifecycle per subagent dispatch. |
src/Squad.Agents.AI/Squad.Agents.AI.csproj |
Bumps package version to 0.3.0, adds InternalsVisibleTo, and updates package references. |
test/Squad.Agents.AI.Tests/SquadSubagentTraceTests.cs |
New hermetic tests for typed mapping + Activity lifecycle. |
test/Squad.Agents.AI.Tests/SquadBYOKTests.cs |
Updates BYOK routing-gate assertions to the new Connection model. |
test/Squad.Agents.AI.Tests/SquadAgentSessionConfigTests.cs |
Minor update removing an SDK namespace dependency and simplifying typing. |
test/Squad.Agents.AI.Tests/SquadAgentRoutingTests.cs |
Updates routing assertions to reflect WorkingDirectory/Connection args helpers. |
src/Squad.Agents.AI/samples/Squad.Agents.AI.Sample/Program.cs |
Improves error visibility and tightens CLI-missing detection; adds trailing whitespace that should be removed. |
.gitignore |
Ignores VS IDE artifacts. |
Comments suppressed due to low confidence (2)
src/Squad.Agents.AI/SquadAgent.cs:250
- The code comment says the SDK default connection is only overridden when the consumer supplies a custom CLI path or extra args, but
combinedCliArgsincludes Squad's default--allow-all, so this condition will almost always override the SDK's default connection. Either the condition or the comment should be updated so they match.
// Only override the SDK's default child-process connection when the consumer
// supplied a custom CLI path or any extra CLI args. Otherwise let the SDK
// resolve its own bundled binary (downloaded via the build/ targets).
if (!string.IsNullOrEmpty(options.CliPath) || combinedCliArgs.Count > 0)
{
clientOptions.Connection = RuntimeConnection.ForStdio(options.CliPath, combinedCliArgs);
}
src/Squad.Agents.AI/SquadAgent.cs:242
--allow-allis injected by default when the host hasn't explicitly opted into any permission-opening flag. This bypasses the Copilot CLI's tool/path/url gates for every consumer of this library, which is a significant security change compared to only honoring explicitly providedCliArgs. Consider making this behavior opt-in (or gated behind a dedicated option) so hosted / multi-tenant scenarios don't accidentally run with full permissions.
// ── CLI permission flags ───────────────────────────────────────────
// The Copilot CLI enforces THREE independent permission gates:
// 1. Tools (--allow-all-tools) — which tool kinds may run
// 2. Paths (--allow-all-paths) — which filesystem paths may be read/written
// 3. URLs (--allow-all-urls) — which URLs may be fetched
//
// The SDK-level `OnPermissionRequest` handler only covers the SDK protocol;
// each CLI gate is a separate verification step. We pass `--allow-all` by
// default so the SquadAgent can actually drive tool calls end-to-end against
// the entire Squad workspace. Hosts that want stricter behavior can replace
// this via SquadAgentOptions.CliArgs / the ConfigureCopilotClient delegate.
// ───────────────────────────────────────────────────────────────────
// Skip our `--allow-all` default if the host already opted in via any of the
// CLI's permission-opening flags (or the omnibus `--yolo` alias). Comparison is
// case-insensitive because `copilot --help` documents the flags in lowercase but
// CLI argument parsers on Windows are commonly forgiving of case differences.
var combinedCliArgs = new List<string>();
bool hostAlreadyOpenedPermissions = options.CliArgs.Any(a =>
string.Equals(a, "--allow-all", StringComparison.OrdinalIgnoreCase) ||
string.Equals(a, "--allow-all-tools", StringComparison.OrdinalIgnoreCase) ||
string.Equals(a, "--allow-all-paths", StringComparison.OrdinalIgnoreCase) ||
string.Equals(a, "--allow-all-urls", StringComparison.OrdinalIgnoreCase) ||
string.Equals(a, "--yolo", StringComparison.OrdinalIgnoreCase));
if (!hostAlreadyOpenedPermissions)
{
combinedCliArgs.Add("--allow-all");
}
combinedCliArgs.AddRange(options.CliArgs);
| // Wire OnSubagentTrace before ConfigureSession so consumer-supplied ConfigureSession | ||
| // callbacks can still override or chain behind our defaults (e.g. compose multiple OnEvent | ||
| // handlers, replace IncludeSubAgentStreamingEvents, etc.). | ||
| SquadSubagentTraceMapper? traceMapper = null; | ||
| if (options.OnSubagentTrace is not null) | ||
| { | ||
| traceMapper = new SquadSubagentTraceMapper(options.OnSubagentTrace); | ||
| sessionConfig.IncludeSubAgentStreamingEvents = true; | ||
| sessionConfig.OnEvent = traceMapper.OnSessionEvent; | ||
| } |
| /// <summary> | ||
| /// The SDK agent identifier (e.g. <c>toolu_vrtx_01Fc3uBTKapUoDDMnPUSe2ww</c>) the event is attributed to. | ||
| /// Null for events emitted by the root coordinator; non-null for subagent-scoped events when | ||
| /// <see cref="SquadAgentOptions.IncludeSubAgentStreamingEvents"/> is on. | ||
| /// </summary> | ||
| public string? SdkAgentId { get; init; } |
| public enum SquadAgentTraceEventKind | ||
| { | ||
| /// <summary>An event that does not match any of the well-known categories below.</summary> | ||
| Other = 0, | ||
|
|
| /// Inactive subagent <c>Activity</c> instances are tracked by their <c>SdkAgentId</c> so concurrent | ||
| /// dispatches each get their own span. The activity is kept alive in a <see cref="System.Collections.Concurrent.ConcurrentDictionary{TKey, TValue}"/> | ||
| /// for the duration of the subagent run; if the session ends without a matching completion event | ||
| /// (e.g. an unhandled exception in the SDK), <see cref="DisposeAll"/> drains the dictionary so spans | ||
| /// still terminate cleanly. |
| // We bound the preview to a sane length even though backends are free to truncate further. | ||
| if (!string.IsNullOrEmpty(envelope.Content)) | ||
| { | ||
| var preview = envelope.Content!.Length > 512 | ||
| ? envelope.Content.Substring(0, 512) + "..." | ||
| : envelope.Content; | ||
| liveActivity.SetTag("squad.subagent.reply_preview", preview); | ||
| } |
| /// <item><see cref="ConfigureSession"/> may still override <see cref="GitHub.Copilot.SessionConfigBase.OnEvent"/> | ||
| /// or <see cref="GitHub.Copilot.SessionConfigBase.IncludeSubAgentStreamingEvents"/>; consumers | ||
| /// that need a stacked event handler should call <c>OnSubagentTrace</c> from inside their | ||
| /// <see cref="ConfigureSession"/> callback to compose the two.</item> | ||
| /// </list> |
… into ApiApp Squad.Agents.AI 0.3.0-preview.5 (released today from bradygaster/squad#1265) adds a first-class subagent observability surface: a typed OnSubagentTrace callback + an OpenTelemetry ActivitySource named 'Microsoft.Agents.AI.Squad' that emits one span per task-tool dispatch with squad.subagent.name / squad.subagent.display_name / squad.subagent.sdk_agent_id / squad.subagent.reply_preview tags. This commit: * Bumps Squad.Agents.AI from 0.2.0-preview.4 to 0.3.0-preview.5 in Directory.Packages.props. * Wires the new ActivitySource into the ApiApp's OpenTelemetry tracer (one line on top of the AddServiceDefaults() configuration) so every subagent dispatch shows up as its own span in the Aspire dashboard's Traces view, with the subagent name + a preview of its reply attached as span tags. * Adds OnSubagentTrace logging in the ApiApp so subagent lifecycle (spawn, reply, complete) also surfaces in the dashboard's Structured Logs view — both signals are correlated by Activity.Current so the OTel span and the structured log share the same trace id. * Adds a new POST /dispatch endpoint that issues an explicit task-tool dispatch prompt ('use the task tool to dispatch two parallel subagents...'). Hitting this endpoint from the dashboard produces a multi-span trace showing the coordinator + each subagent as separate spans — the headline visibility demo. * Existing POST /ask 3-turn endpoint kept for the session-memory demo. Verified locally: AppHost boots clean, the 'research-squad' resource registers 2 agents (ralph, picard from the sample-squad team), and the ApiApp builds + runs with the new observability wiring against Squad.Agents.AI 0.3.0-preview.5 restored from nuget.org. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… into ApiApp Squad.Agents.AI 0.3.0-preview.5 (released today from bradygaster/squad#1265) adds a first-class subagent observability surface: a typed OnSubagentTrace callback + an OpenTelemetry ActivitySource named 'Microsoft.Agents.AI.Squad' that emits one span per task-tool dispatch with squad.subagent.name / squad.subagent.display_name / squad.subagent.sdk_agent_id / squad.subagent.reply_preview tags. This commit: * Bumps Squad.Agents.AI from 0.2.0-preview.4 to 0.3.0-preview.5 in Directory.Packages.props. * Wires the new ActivitySource into the ApiApp's OpenTelemetry tracer (one line on top of the AddServiceDefaults() configuration) so every subagent dispatch shows up as its own span in the Aspire dashboard's Traces view, with the subagent name + a preview of its reply attached as span tags. * Adds OnSubagentTrace logging in the ApiApp so subagent lifecycle (spawn, reply, complete) also surfaces in the dashboard's Structured Logs view — both signals are correlated by Activity.Current so the OTel span and the structured log share the same trace id. * Adds a new POST /dispatch endpoint that issues an explicit task-tool dispatch prompt ('use the task tool to dispatch two parallel subagents...'). Hitting this endpoint from the dashboard produces a multi-span trace showing the coordinator + each subagent as separate spans — the headline visibility demo. * Existing POST /ask 3-turn endpoint kept for the session-memory demo. Verified locally: AppHost boots clean, the 'research-squad' resource registers 2 agents (ralph, picard from the sample-squad team), and the ApiApp builds + runs with the new observability wiring against Squad.Agents.AI 0.3.0-preview.5 restored from nuget.org. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
) * fix: auto-scaffold Fact Checker agent during init and cast (#1222) The Fact Checker role landed in v0.10.0 (#789) with catalog entry, charter template, skill, AGENT_TEMPLATES map entry, and template manifest entry — but was never wired into the user-facing onboarding flow. Users running 'squad init' got Scribe/Ralph/Rai but never saw Fact Checker as a default or cast option. This mirrors how Rai was wired: - init.ts: adds 'fact-checker' to the default agents: array passed to sdkInitSquad() - cast.ts: adds factCheckerMember(), factCheckerCharter(), hasFactChecker branches in castTeam(), and the roster banner line Smoke-tested locally: 'squad init' in a clean repo now produces .squad/agents/fact-checker/charter.md alongside scribe/ralph/Rai. Closes #1222 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(upgrade): auto-scaffold Rai + Fact Checker on squad upgrade (#1222) Extends the #1222 fix to the third code path. \squad upgrade\ was intentionally silent on agents (preserves user state). For users upgrading from v0.9.x or earlier (no Rai) or v0.10.0 (no fact-checker), this means they'd never get the built-in agents unless they re-ran \squad init\ (which would overwrite other state). Adds \�nsureBuiltinAgents()\ to \ unEnsureChecks()\. Idempotent — only scaffolds when the agent directory is absent. Never overwrites existing charters or history files. Sources content from the shipped \ emplates/{Rai,fact-checker}-charter.md\ templates (already present via TEMPLATE_MANIFEST). Scribe and Ralph are intentionally NOT scaffolded by upgrade — they predate this fix in every squad, and their charters are inlined in cast.ts (no shipped template file). Smoke tested locally: - Set up a simulated v0.9.4 squad (scribe + ralph only) - Ran \squad upgrade\ → 'scaffolded 2 built-in agent(s): Rai, fact-checker' - Ran upgrade again → no re-scaffold (idempotent) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(permissions): use 'approve-once' for Copilot CLI v1.0.54+ contract (#1192) The Copilot CLI post-v1.0.54 changed the permission handler contract to expect 'approve-once' instead of 'approved'. Update the handler, type definition, and error hint to match the new contract. Closes #1191 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: Squad.Agents.AI - Microsoft Agent Framework adapter for the Squad CLI (#1207) * feat: Squad.Agents.AI community NuGet for MAF integration Squad CLI as Microsoft.Extensions.AI IChatClient, composing GitHub.Copilot.SDK via AsAIAgent() from Microsoft.Agents.AI.GitHub.Copilot 1.7.0-preview. Closes Track A of the Q1-Q7 design lock (see tamresearch1 .squad/decisions.md Decisions 441, 443, 444, 447). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: add PR body for #3 * fix(SquadAgent): inherit AIAgent (was IChatClient force-cast) - SquadAgent now properly inherits from Microsoft.Agents.AI.AIAgent - Removed (IChatClient)(object)agent force-cast - Overrides all AIAgent abstract members (CreateSessionCoreAsync, RunCoreAsync, etc.) - DI registration now registers AIAgent (not IChatClient) - README updated to use AIAgent.RunAsync API - No more abstraction inversion; AIAgent is the correct layer Fixes the architectural error identified by Tamir. * docs(SquadAgent): rewrite README — prerequisites, Hello World, troubleshooting, preview callout Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(SquadAgent): GitHubTokenProvider callback + redact options ToString Adds async token provider pattern for production scenarios (KeyVault/MSI integration). - GitHubTokenProvider property takes precedence over GitHubToken - GitHubToken marked [JsonIgnore] to prevent serialization leaks - SquadAgentOptions.ToString() redacts GitHubToken field - Updated CreateCopilotClient to resolve token from provider first Mitigates P0 #3: token leakage via ILogger structured-log calls, IOptions snapshots, and serializers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(SquadAgent): document GitHubTokenProvider callback for production token management Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(SquadAgent): bind ConnectionStrings__squad via IConfigureOptions + add smoke tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(README): replace removed WithTeamRoot with positional teamRoot ctor WithTeamRoot was deleted in commit 35767c90 in favor of mandatory positional teamRoot constructor argument on AddSquad. The Aspire example in the Squad.Agents.AI README still showed the old fluent API, which would now fail at compile time for anyone copy-pasting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(squad-agents-ai): add routing integration tests Closes the routing-verification gap identified during squad-squad onboarding: the API surface existed but routing semantics weren't functionally tested. New tests verify persona pass-through, boundary-instruction injection on first turn, WorkingDirectory isolation (Decision 452a), and CopilotClientOptions-based routing (Decision 447). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci(squad-agents-ai): add .NET build/test/pack workflow PR #3 CI was Node/docs-only — adding the .NET gate so green actually reflects the package code. Matrix on ubuntu + windows, restore/build/test/pack, uploads TestResults and nupkg artifacts. Closes the build-verification gap identified during squad-squad onboarding (see .squad/decisions.md adoption record, 2026-06-02). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci(squad-agents-ai): NuGet publish workflow + Dependabot config - .github/workflows/squad-agents-ai-release.yml: workflow_dispatch and tag-driven publish to nuget.org with --skip-duplicate idempotency, fail-fast on missing NUGET_API_KEY secret, optional GitHub Release on tag - .github/dependabot.yml: nuget (src + test) + github-actions, weekly, M.A.AI major allowed, OpenTelemetry major deferred (per Decision 602) Closes the release-pipeline + supply-chain-tracking gaps identified during squad-squad onboarding. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(squad-agents-ai): release-ready docs + .csproj packaging metadata - README updates / XML docs on public surface - CHANGELOG.md with [0.1.0-preview] - 2026-06-02 entry - .csproj: Description, RepositoryUrl, Authors, PackageTags, PackageReadmeFile - Verified via dotnet pack — .nupkg contains README, LICENSE, xml-docs Closes the docs-readiness gap for v0.1-preview publish. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci(squad-agents-ai): switch release triggers to dev/main branch-driven Per Tamir's release-strategy directive (decisions.md 2026-06-02): - dev merges → prerelease publish (suffix scheme mirrors Squad CLI) - main merges → stable publish - workflow_dispatch retained as manual escape hatch - tag-driven trigger removed (branches are the source of truth) Version derivation pattern adapted from the Squad CLI's existing release workflow. --skip-duplicate retained for idempotent reruns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs+fix: PR #3 review pass — hygiene, XML docs, cliArgs, multi-named connections - Rewrite the package README and PR validation flow around the AIAgent surface and ambient Copilot authentication. - Keep public docs free of internal process references and remove obsolete deferral language. - Preserve connection-string cliArgs through CopilotClientOptions and cover the behavior with a routing test. - Add named connection-string lookup via AddSquadAgent("name") using ConnectionStrings:squad-{name}. - Document the new public overloads and verify the package builds without warnings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: Round 2 — keyed DI, BYOK delegate, routing gate, security hardening - Add ConfigureCopilotClient delegate on SquadAgentOptions for BYOK - Add routing gate: snapshot/restore Cwd/CliPath/CliArgs after delegate (Picard C1) - Add 4 AddKeyedSquadAgent overloads with .NET 8+ keyed DI - Fix Environment credential leak: [JsonIgnore] on Environment, GitHubTokenProvider, ConfigureCopilotClient - ToString() redacts token-pattern keys (TOKEN/KEY/SECRET/HMAC/PASSWORD/CREDENTIAL) - Add 21 new tests (43 total): security redaction, keyed DI, BYOK routing gate - Update README: streaming, keyed DI, BYOK, security sections Complies with: Picard C1-C4, Worf SC-1 through SC-8. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add Squad.Agents.AI sample app demonstrating DI, keyed DI, BYOK, and streaming - samples/squad-agents-ai-sample/Program.cs: four runnable flows Flow 1 -- AddSquadAgent + RunAsync (basic DI) Flow 2 -- AddKeyedSquadAgent x2 + GetRequiredKeyedService<SquadAgent> Flow 3 -- ConfigureCopilotClient delegate (BYOK token + env var injection) Flow 4 -- RunStreamingAsync with await foreach token-by-token output - samples/squad-agents-ai-sample/Squad.Agents.AI.Sample.csproj: net10.0, project reference to src/Squad.Agents.AI, Microsoft.Extensions.Hosting 10.0.0 - samples/squad-agents-ai-sample/README.md: prerequisites, run commands, per-flow walkthrough, troubleshooting table - .github/workflows/squad-agents-ai-ci.yml: adds paths trigger and restore + build steps for the sample (no run step -- requires live CLI) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(Squad.Agents.AI): co-locate sample under src/ and consolidate README - Moves the sample app from samples/squad-agents-ai-sample/ to src/Squad.Agents.AI/samples/Squad.Agents.AI.Sample/ so it lives alongside the package it demonstrates. - Folds the sample's standalone README into the package README, giving consumers one canonical doc for both the API and the runnable demo. - Adds <Compile Remove="samples/**/*.cs" /> to Squad.Agents.AI.csproj so the library's wildcard glob does not pick up Program.cs in the co-located samples subdirectory. - Updates the .csproj project reference, and CI workflow paths to match the new layout. - Verified end-to-end: dotnet build, dotnet test (43/43 passing), and a sample sanity-check run (clear CLI-not-found error, no stack trace). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(Squad.Agents.AI): remove outdated draft PR body file The standalone pr-body.md was an early draft authored before the live PR description took its final shape. The PR body on GitHub is the canonical source; this file is dead weight and would confuse maintainers reviewing the diff. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(Squad.Agents.AI): address PR #1207 reviewer feedback (12 items) - Snapshot CliArgs by value, not reference, so in-place mutation by SDK consumers is also caught by the routing guard. - Validate name/connectionName/serviceKey is non-empty in all DI registration overloads; previously null/whitespace produced invalid connection-string keys. - Replace ghp_-prefixed placeholder in the sample with a clearly-fake token to avoid tripping secret-scanning and to remove a real-token lookalike. - Remove brittle 'PR #3' references from README and CHANGELOG; describe the feature without tying to a specific PR thread. - Update NuGet metadata and README links to point to bradygaster/squad (canonical repo) instead of the tamirdresher fork. - Multi-target the test project to match the package's target framework set so CI exercises every framework the package ships against. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(Squad.Agents.AI): adopt DelegatingAIAgent base + MAF ctor pattern (PR #1207 r2) Addresses westey-m's review feedback: - Extend Microsoft.Agents.AI.DelegatingAIAgent — drops ~70 lines of manual Core* overrides; pass-through is provided by the base class. - Adopt MAF constructor pattern: \(string squadFolderPath, SquadAgentOptions? options = null, ILoggerFactory? loggerFactory = null)\. Required settings on the constructor, options optional, ILoggerFactory stays on ctor for DI injection. - Routing-guard, IAsyncDisposable, and security posture preserved. - Add Squad.Agents.AI.slnx solution for easy IDE open. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Reno (Copilot) <reno@clawpilotsquad.dev> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <Copilot@users.noreply.github.com> Co-authored-by: tamirdresher <tamirdresher@users.noreply.github.com> * ci(Squad.Agents.AI): switch NuGet publish to Trusted Publishing (OIDC) (#1240) Replace the long-lived NUGET_API_KEY repo secret with NuGet/login@v1 OIDC token exchange (1-hour API key) per the modern Trusted Publishing flow: https://learn.microsoft.com/nuget/nuget-org/trusted-publishing Why --- - No long-lived credentials stored in the repo. - Token is scoped to this workflow + repo + branch via the OIDC subject claim and the Trusted Publishing policy registered on nuget.org. - Eliminates the chicken-and-egg between needing admin to set NUGET_API_KEY and needing the package live to validate the workflow. What changed ------------ - Add `id-token: write` to the publish job (required for OIDC). - Drop the `Verify NuGet API key` step. - Drop the `--api-key ` reference to secrets.NUGET_API_KEY. - Add `NuGet/login@v1` step that exchanges the OIDC token for a short-lived API key, exposed via `steps.nuget-login.outputs.NUGET_API_KEY`. - Add a fail-fast check for the new `vars.NUGET_USER` repository variable (non-sensitive; the nuget.org profile name that performs the exchange). - Update the file header documentation to reflect the new flow and link to the Trusted Publishing setup page. Required configuration before first publish ------------------------------------------- 1. Create the `Squad` organization on nuget.org and add owners. 2. Configure a Trusted Publishing policy at https://www.nuget.org/account/trusted-publishing owned by the Squad org: Repository Owner: bradygaster Repository: squad Workflow File: squad-agents-ai-release.yml Environment: (empty) 3. Set repository variable NUGET_USER (Settings → Secrets and variables → Actions → Variables) to a Squad-org-member's nuget.org profile name (NOT email). Variable, not secret — the username is non-sensitive. Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(Squad.Agents.AI): expose SessionConfig + default OnPermissionRequest (#1252) Squad.Agents.AI 0.1.0-preview.2 calls `CopilotClient.AsAIAgent(instructions, name)` which leaves `SessionConfig.OnPermissionRequest` unset. The first call to `SquadAgent.CreateSessionAsync()` therefore throws: System.ArgumentException: An OnPermissionRequest handler is required when creating a session. For example, to allow all permissions, use CreateSessionAsync(new() { OnPermissionRequest = PermissionHandler.ApproveAll }); There was no public way for consumers to fix it from the outside — `ConfigureCopilotClient` only exposes `CopilotClientOptions`, not the per-session `SessionConfig` that owns the permission handler. What changed ------------ - `SquadAgent` now switches to the `AsAIAgent(client, sessionConfig, ...)` overload and constructs a `SessionConfig` with: - `OnPermissionRequest = PermissionHandler.ApproveAll` (sensible default for a host-process Squad adapter; the host already chose to instantiate Squad and is responsible for sandboxing). - `WorkingDirectory` defaulting to the resolved `Cwd` / `SquadFolderPath`. - `SystemMessage = new SystemMessageConfig { Content = Instructions }` when `SquadAgentOptions.Instructions` is set. - `SquadAgentOptions.ConfigureSession: Action<SessionConfig>?` is new — runs after Squad applies its defaults so a consumer can swap in a stricter permission handler, pin the model, restrict tools, etc. Tests ----- - New `SquadAgentSessionConfigTests` (4 cases) exercising the new surface: `ConfigureSession` is settable, runs against the live SessionConfig, can replace the permission handler, and can set `AvailableTools`. - All existing 43 tests still pass per TFM (141 total across net8/9/10). Verified -------- - The end-to-end consumer smoke test in `C:\Users\tamirdresher\source\repos\squad-agents-ai-consume-test` previously had to fall back to the raw SDK because of the missing handler. With this change, `SquadAgent.CreateSessionAsync()` returns successfully and `RunAsync` drives real 3-turn conversations against `.squad/`-init'd teams. Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Squad.Agents.AI 0.2.0: bump to MAF 1.10.0-rc1 / SDK 1.0.0 GA — file-IO works (#1259) * Squad.Agents.AI: bump to MAF 1.10.0-rc1 / SDK 1.0.0 GA — file-IO now works Microsoft.Agents.AI.GitHub.Copilot bumped from 1.7.0-preview to 1.10.0-rc1, which transitively brings GitHub.Copilot.SDK 1.0.0 GA. SDK 1.0.0 reshaped CopilotClientOptions and SessionConfig (the breaking namespace + property renames are mirrored below), but in return Copilot CLI 1.0.61 talks to the SDK over the new ACP-style protocol — meaning the agent's view/grep/powershell tools finally work end-to-end without 'permission errors' or 'content exclusion policy' hallucinations. SDK 1.0.0 API migrations applied: * Namespace GitHub.Copilot.SDK -> GitHub.Copilot. * CopilotClientOptions.Cwd -> WorkingDirectory. * CopilotClientOptions.CliPath + .CliArgs collapsed into a single CopilotClientOptions.Connection (RuntimeConnection). We now build the Connection via RuntimeConnection.ForStdio(CliPath, CliArgs) only when the consumer supplied either a custom CLI path or extra CLI args; otherwise the SDK's default child-process connection is used and the bundled copilot.exe (downloaded by the SDK's build/ targets) is invoked. * SessionConfig.ConfigDir -> SessionConfig.ConfigDirectory. * PermissionRequestHandler is no longer a named delegate type; we use type inference where the test code referenced it. Public Squad.Agents.AI surface is intentionally unchanged: SquadAgentOptions still exposes Cwd, CliPath, CliArgs, ConfigureSession, ConfigureCopilotClient. We translate to the SDK 1.0.0 shape internally. Routing gate (Picard Condition 1 / Worf SC-3) updated to snapshot and restore WorkingDirectory + Connection instead of the old Cwd / CliPath / CliArgs trio. A delegate that REPLACES Connection (e.g. via RuntimeConnection.ForStdio(...)) is reverted, mirroring the previous CliPath / CliArgs hijack tests. Verified end-to-end against a real .squad-initialised team root: the coordinator successfully reads .squad/team.md and enumerates every cast member with their role. The dotnet test suite passes 46/46 across net8.0, net9.0, and net10.0 (-1 vs baseline because the old CliPath and CliArgs hijack tests collapsed into a single Connection hijack test, which is the right granularity for SDK 1.0.0). Version bumped 0.1.0-preview -> 0.2.0 to surface the MAF/SDK transitive bump. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address Copilot review feedback (#1259) - Remove duplicate 'using GitHub.Copilot' in SquadAgentSessionConfigTests (regex replace inadvertently doubled the directive when migrating from the old GitHub.Copilot.SDK namespace). - Drop hardcoded teamRoot override I left behind in the sample Program.cs during the CLI-not-found debug session. The sample now correctly reads SQUAD_TEAM_ROOT (or falls back to CWD) as documented. - Fix mismatched comment in SquadAgent: the SDK-protocol section now correctly references --allow-all (matching the flag actually injected in CreateCopilotClient), not the narrower --allow-all-tools. - Detect more existing permission-opening flags before injecting our default --allow-all so a host that opts in via --allow-all-paths, --allow-all-urls, or the omnibus --yolo no longer gets --allow-all prepended on top. Comparison is now case-insensitive. Updated the connection-string test that asserted the old over-eager behavior. Also add .vs/, *.user, *.userprefs to .gitignore so VS solution junk doesn't surface in git status. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: address pre-existing Copilot review comments from PR #1212 (#1258) - Workflow permissions: add issues: write to squad-pr-nudge, squad-impact, and squad-repo-health workflows that call issues.createComment - Logic bug: fix ahead_by → behind_by in pr-nudge stale branch check - Logic bug: fix PR_LABELS fallback producing string instead of null - Script fix: check legacy statuses for failure/error in checkCIStatus() - Script fix: truncation row column count mismatch in pr-readiness.mjs - Script fix: validate JSON.parse result is array before using as labels - Script fix: isNodeBuiltin now validates node: prefix against known builtins - YAML escaping: use JSON.stringify for skill descriptions in apm.yml Closes #1213 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: expose memory tools via MCP + route writes through governed pipeline (#1251) - Expose memory.classify, memory.write, memory.search, memory.promote, memory.delete, memory.audit through the squad_state MCP server (#1244) - Pin squad_state to user-level ~/.copilot/mcp-config.json during init/upgrade for external `copilot -p` mode compatibility (#1247) - Update squad.agent.md directive-capture and decision-recording instructions to route through memory.write instead of raw squad_state_write to the drop-box (#1246) Closes #1244 Closes #1247 Closes #1246 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(skills): update path references from .squad/skills/ to .copilot/skills/ (#1260) The CLI and SDK write skills to .copilot/skills/ by default, but docs still referenced .squad/skills/. Update all documentation to use the canonical .copilot/skills/ path and add a note about legacy fallback. Closes #1241 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(init): prompt to add @copilot as a team member during squad init (#1262) Add an interactive prompt during squad init that asks users if they want to add @copilot (the GitHub Copilot coding agent) as an autonomous team member. If accepted, adds the Coding Agent section to team.md and copies copilot-instructions.md into the project. Non-interactive mode skips silently with a hint to run `squad copilot enable` later. Closes #1147 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: align skills path + state-backend upgrade flow (#1241, #1226) (#1249) - Update all docs references from .squad/skills/ to .copilot/skills/ - Note that both paths are scanned at read time but .copilot/skills/ is write default - List all 6 git hooks (add pre-commit and post-commit to docs) - Correct 'read-only reference' claim about migrated files - Add recovery section for pre-commit hook refusal scenarios Closes #1241 Closes #1226 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Squad.Agents.AI 0.3.0: first-class subagent observability (#1265) Adds a typed OnSubagentTrace callback + OpenTelemetry ActivitySource so consumers can see subagent dispatch (the coordinator's 'task' tool spawning specialist sub-agents and their replies) without writing their own polymorphic dispatch over the raw GitHub.Copilot SessionEvent hierarchy. The previous 0.2.x surface required consumers to write the same boilerplate the aspire-squad-resource demo had to (CopilotSessionTraceMapper.cs): switch on every event subtype, unwrap subagent context, manage Activity lifetime. 0.3.0 makes that the SDK's job. New public surface: * SquadAgentOptions.OnSubagentTrace (Action<SquadAgentTraceEvent>) Set this to subscribe to subagent lifecycle (Selected / Started / Completed / Failed), assistant messages from coordinator AND subagents, tool start/complete, and SessionIdle. Setting OnSubagentTrace implicitly turns on SessionConfig.IncludeSubAgentStreamingEvents so subagent replies actually flow up to the parent session (otherwise they stay inside the subagent session and never reach the callback). * SquadAgentTraceEvent record — typed envelope (Kind, RawEventType, Timestamp, SdkAgentId, SubagentName, SubagentDisplayName, ToolCallId, Content, Success, RawEvent). Carries the original SessionEvent on RawEvent for advanced consumers but exposes everything else through neutral primitive types so the callback signature has no transitive dependency on GitHub.Copilot.SDK. * SquadAgentTraceEventKind enum — categorises the SessionEvent into the well-known cases that downstream observability surfaces want. * SquadAgentDiagnostics.ActivitySourceName ('Microsoft.Agents.AI.Squad') + SquadAgentDiagnostics.ActivitySource — one Activity per subagent dispatch is opened on SubagentStartedEvent and disposed on the matching SubagentCompletedEvent / SubagentFailedEvent, tagged with squad.subagent.name, squad.subagent.display_name, squad.subagent.sdk_agent_id, and squad.subagent.reply_preview (a short truncated copy of the subagent's assistant message). Hosts that .AddSource(SquadAgentDiagnostics.ActivitySourceName) on their OpenTelemetry tracer get these spans in their backend — the Aspire dashboard renders them in the trace view automatically. Internal: * SquadSubagentTraceMapper — wires SessionEvent -> SquadAgentTraceEvent and the Activity lifecycle. Held by SquadAgent and disposed during DisposeAsync to drain any subagent activities that never received a matching Completed event (e.g. session ended mid-dispatch). * InternalsVisibleTo=Squad.Agents.AI.Tests so the mapper can be unit tested directly without spinning a real CLI session. Tests: 54/54 passing across net8, net9, net10 (+8 new tests for the observability surface including Activity lifetime, tag propagation, mid-session disposal, and consumer-callback exception isolation). Verified end-to-end against the tamresearch1 Star Trek squad: the OnSubagentTrace callback observed two parallel subagent dispatches (Picard, Data), captured each of their replies attributed to the right SdkAgentId, and the matching 'squad.subagent Picard' / 'squad.subagent Data' OTel spans opened and closed cleanly with the reply tagged on each span. Version bumped 0.2.0 -> 0.3.0 to signal the new public surface. Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(lockfile): sync package-lock with package.json after v0.10.0 bump (#1266) After the v0.10.0 stable release on 2026-06-07, the root package-lock.json still recorded packages/squad-cli@0.9.6-preview.15 and packages/squad-sdk@0.9.6-preview.13 for the workspace entries. The package.json files were updated correctly during the release, but the lockfile workspace metadata was not regenerated. This causes `npm ci --ignore-scripts` to fail in the `sdk-exports-validation` CI job whenever package.json versions diverge from these stale lockfile entries (which happens on every build that runs `scripts/bump-build.mjs`). Confirmed on PR #1257. This commit regenerates only the workspace version metadata (`packages/squad-cli` and `packages/squad-sdk`) — no dependency trees are touched. Verified locally on Windows + Node v23.5.0: - `npm ci --ignore-scripts` at repo root: ✅ exit 0 (was already passing on dev because root package.json/lockfile match; failure mode is the workspace-entry mismatch surfacing under specific build conditions) - `npm install --ignore-scripts`: no further drift produced - Full `npm test` suite: 6535 passed / 134 failed / 60 skipped — the 134 failures are all pre-existing Windows file-locking flakiness (EBUSY/ENOTEMPTY/hook-timeout); identical failure mode and similar count on the immediate pre-merge commit cc37a2f7 (125 failures pre-merge, 134 post-merge — diff is within flake noise). - Targeted re-run of the 5 "newly failing" files in isolation: 93/93 passing — confirms the bulk-run failures are concurrency-induced flakes, not regressions from the recent merges (#1251, #1258, #1260, #1262, #1249). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(bump-build): also update package-lock workspace entries (#1267) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: release pipeline version pinning (#1203, #1204) (#1250) * fix: release pipeline version pinning (#1203, #1204) - Lower SDK dependency floor from >=0.10.0 to >=0.9.0 so the CLI tarball resolves against the last published SDK when current version isn't yet on the registry (Closes #1203) - Add isLocalOrUnpublishedVersion guard so local dev builds and versions with build metadata (+) fall back to @insider instead of writing unresolvable version strings into MCP config (Closes #1204) - Extend resolveSquadStateMcpSpec to short-circuit for build-metadata versions - Add CI step to verify SDK dependency is resolvable before CLI publish Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: reset accidentally-bumped version 0.10.0-build.2 -> 0.10.0 Pre-publish version guard rejects -build.N suffixes (release pipeline policy). The bump was made by an unintended local 'npm run build' run before commit. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamir.dresher@gmail.com> Co-authored-by: Copilot <tamirdresher@users.noreply.github.com> * chore: include CHANGELOG.md in published npm tarball (#1257) * chore: include CHANGELOG.md in published npm tarball Add CHANGELOG.md to the files array in both squad-cli and squad-sdk package.json files so changelogs are included in published npm tarballs. This enables offline what's-new prompts and removes the need for GitHub API calls to show release notes. Closes #1171 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: reset accidentally-bumped versions to 0.10.0 Pre-publish version guard rejects -build.N suffixes (release pipeline policy). Both SDK and CLI package.json had -build.4 from a local 'npm run build' run before commit. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamir.dresher@gmail.com> Co-authored-by: Copilot <tamirdresher@users.noreply.github.com> * Squad.Agents.AI 0.4.0: default-on subagent observability + Aspire-style connection-string lookup (#1271) Two small but high-impact changes that remove ~30 lines of boilerplate from every consumer (Aspire and otherwise) and make the OpenTelemetry story self-explanatory. ## 1. EmitSubagentActivities (default true) — telemetry independent of callback Today, the per-subagent OpenTelemetry Activity emission is a side-effect of setting `OnSubagentTrace`. A host that just wants `squad.subagent {Name}` spans in their backend has to wire a callback they don't need. `Microsoft.Agents.AI.Squad` is silent until then. 0.4.0 makes activity emission the default: * New `SquadAgentOptions.EmitSubagentActivities` (defaults to `true`). * `SquadAgent` installs `SquadSubagentTraceMapper` whenever `EmitSubagentActivities || OnSubagentTrace != null`, so spans flow with zero extra wiring. * `OnSubagentTrace` becomes a pure customisation hook (logging, dashboards, metrics) — independent of telemetry. Set `EmitSubagentActivities = false` to opt out of built-in spans when you want to handle telemetry yourself. Plus richer span shape: every lifecycle phase is now an `ActivityEvent` on the live subagent span (visible as annotated markers on the timeline in Aspire / Jaeger / etc.): * `squad.subagent.start` — on SubagentStarted * `squad.subagent.message` — on AssistantMessage (with message_preview tag) * `squad.subagent.completed` — on SubagentCompleted * `squad.subagent.failed` — on SubagentFailed Net effect for a consumer: builder.Services.AddOpenTelemetry() .WithTracing(t => t.AddSource(SquadAgentDiagnostics.ActivitySourceName)); builder.Services.AddSquadAgent(o => o.SquadFolderPath = "/team"); …and the dashboard lights up. No callback wiring, no Activity.Current?.AddEvent plumbing in the host. ## 2. Aspire-style connection-string lookup (with legacy fallback) Aspire injects connection strings under the literal resource name — e.g. an AppHost that calls `builder.AddSquad("research-squad", ...)` exposes `ConnectionStrings:research-squad` to the consumer. The 0.3.0 SDK only looked at `ConnectionStrings:squad-research-squad` (prefixed), so Aspire consumers had to manually call `Configuration.GetConnectionString(name)`, parse the URI, and feed `SquadFolderPath` into the configure callback themselves. 0.4.0 tries the literal name first and falls back to the legacy prefixed form: | Style | Example | Lookup | |--------------------------------------|---------------------------------------------|--------------------------------------------| | Aspire-style direct (tried first) | `AddSquadAgent("research-squad")` | `ConnectionStrings:research-squad` | | Legacy prefixed fallback | `AddSquadAgent("research")` | `ConnectionStrings:squad-research` | Both work. Existing consumers using `ConnectionStrings:squad-{name}` continue unchanged; new Aspire consumers get the natural one-line registration. ## Tests 54 → 64 tests, all passing on net8.0/9.0/10.0. New `SquadAgentDefaultObservabilityTests` covers: * Default-on activity emission (without consumer callback) * Opt-out path (`EmitSubagentActivities = false`) — span suppression + callback still fires * Each ActivityEvent name (`start` / `message` / `completed` / `failed`) * Connection-string precedence: Aspire-direct preferred, prefixed fallback used, same rule applies for keyed registrations Existing `SquadSubagentTraceTests` and the new class share an `[Collection("SquadActivityListeners")]` so they run serially — process-global `ActivityListener` state caused cross-test pollution otherwise. ## Files * `src/Squad.Agents.AI/SquadAgentOptions.cs` — new `EmitSubagentActivities` property + reworked `OnSubagentTrace` XML doc to clarify independence. * `src/Squad.Agents.AI/SquadAgent.cs` — install trace mapper when telemetry OR callback is requested. * `src/Squad.Agents.AI/SquadSubagentTraceMapper.cs` — accept `emitActivities` flag; gate `StartActivity`/`Dispose` on it; add `ActivityEvent` annotations at every lifecycle boundary. * `src/Squad.Agents.AI/SquadAgentOptionsConfigurator.cs` — accept a list of candidate connection-string names; first non-empty wins. * `src/Squad.Agents.AI/SquadServiceCollectionExtensions.cs` — new `GetConnectionStringNames` returns `[name, "squad-"+name]` so both Aspire and legacy conventions resolve. * `src/Squad.Agents.AI/Squad.Agents.AI.csproj` — bump 0.3.0 → 0.4.0. * `src/Squad.Agents.AI/README.md` — new "Subagent observability" section, updated "Aspire / configuration path" section, two new option rows in the Key Options table. ## Backward compatibility Fully backward compatible. The two-arg `SquadSubagentTraceMapper` constructor defaults `emitActivities` to `true`, `EmitSubagentActivities` defaults to `true`, and the legacy `ConnectionStrings:squad-{name}` lookup still resolves. Net change for an existing consumer that had OnSubagentTrace set: nothing (mapper runs in both 0.3.0 and 0.4.0 because OnSubagentTrace is non-null). Net change for a consumer that did NOT set OnSubagentTrace but did AddSource: they now get spans they always asked for. Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Squad.Agents.AI 0.5.0: auto-inject --agent squad (CLI parity by default) (#1275) * Squad.Agents.AI 0.5.0: auto-inject --agent squad (CLI parity by default) 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> * amend: use SessionConfig.Agent instead of --agent CLI 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> --------- Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Squad.Agents.AI 0.5.1: fix 0.5.0 regression — use --agent CLI flag (not 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> * Add squad registry add/list/remove for discovery-only peer squads (#1291) Closes #1290. Adds CLI surface for managing .squad/squad-registry.json, symmetric to squad upstream. Registry entries are discovery-only (visible to squad discover and squad delegate) but do NOT trigger inheritance of the peer squad's skills/decisions/wisdom/routing. Previously users had to hand-edit .squad/squad-registry.json — even the squad discover empty-state hint told them to "create a squad-registry.json" manually. This adds proper commands: squad registry add <name> <path> # validates manifest, refuses duplicate squad registry list # show all registered peers squad registry remove <name> # remove by name Also fixes a subtle path-semantics confusion: readManifest() now accepts BOTH the repo root AND a path with a trailing .squad segment. The docs and SKILL.md showed the .squad-suffixed form but the code previously joined .squad/manifest.json onto whatever you gave it, so the suffixed form silently failed empirical reproduction (discover returned nothing). Test coverage: 18 new tests in cross-squad-registry.test.ts covering the dual-path readManifest fix, registry round-trip, add/list/remove behavior including duplicate-name and invalid-manifest rejection, and end-to-end integration with discoverSquads. Also updates: - cross-squad SKILL.md (canonical + 2 template mirrors) to document the registry vs upstream distinction explicitly - squad discover empty-state hint to mention squad registry add - squad help text with the new commands - SDK exports for the new registry helpers + RegistryEntry/ AddRegistryEntryResult types End-to-end verified locally: - squad registry add (both repo-root and .squad-suffixed paths) - squad registry list (rich output) - squad registry remove (success + missing-name warning) - squad discover picks up registry entries with source=registry - 50/50 tests pass (32 existing + 18 new) - npm run build clean Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(cli): stop writing squad_state to ~/.copilot/mcp-config.json on every init/upgrade (#1296) (#1298) * fix(cli): stop writing squad_state to ~/.copilot/mcp-config.json on every init/upgrade (#1296) squad init (init.ts:408) and squad upgrade (upgrade.ts:738) unconditionally called ensureSquadStateMcpInUserConfig, writing squad_state_<hash> to ~/.copilot/mcp-config.json keyed by a stable project-path hash. Each new squad init accumulated another entry in HOME with no garbage collection. This contradicted the explicit iter-8 design intent documented at packages/squad-cli/src/cli/core/mcp-root.ts:1-27, which says iter-8 stops writing to HOME and writes squad_state ONLY to repo-root .mcp.json. The repo-root .mcp.json writes (init.ts:403 / upgrade.ts:728) already cover all documented Copilot CLI launch modes - copilot and copilot -p both walk up from cwd to find .mcp.json. Out-of-tree copilot -p invocations should use --additional-mcp-config @.mcp.json (already documented at init.ts:494). Changes: * Removed the unconditional ensureSquadStateMcpInUserConfig call from init.ts:408 and upgrade.ts:738. Replaced both with comments explaining iter-8 + #1296. * Removed the now-unused import from both files. * Kept the function definition at mcp-root.ts:178-228 - a future squad doctor --mcp-prune cleanup helper may want to inspect HOME. Tests: * New regression test in test/cli/init.test.ts: "should NOT write any squad_state entries to ~/.copilot/mcp-config.json (#1296)". Isolates the developer's real HOME by setting USERPROFILE/HOME to a temp dir before init, asserts no squad_state* keys appear under temp HOME. * All 40 existing init tests still pass. npm run lint clean. Closes #1296 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: comments reference actual Copilot CLI version (≥1.0.59), drop hardcoded test line numbers Reviewer follow-ups on #1298 (closes #1296): 1. The comments in init.ts/upgrade.ts/mcp-root.ts referenced 'Copilot CLI 5.3+' as the version that auto-loads .mcp.json. The real shipping CLI is at 1.0.62 (5.3 was a typo'd projection from review). Updated all 5 occurrences to '≥1.0.59' — the lowest version where the .mcp.json walk-up behavior is documented. 2. The init.test.ts regression comment hard-coded line numbers (init.ts:408, upgrade.ts:738) that will go stale on any unrelated edit to those files. Rewrote the comment to identify the call by function name (ensureSquadStateMcpInUserConfig) instead — durable against re-orderings. Verified: regression: #1296 test still passes (1/16 in init.test). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(init): post-init package.json script tip includes --agent squad The post-init tip showing how to add a non-interactive `squad:copilot` script to package.json was: "squad:copilot": "copilot --additional-mcp-config @.mcp.json" This omits `--agent squad`, so users who copy/paste it get a generic Copilot CLI session that doesn't load the Squad coordinator, team.md, casting, or MCP-wired memory/state tools — only the additional MCP config gets loaded. Same underlying issue surfaced in the cross-squad- communication SKILL.md sweep (squad/wire-cross-squad-skill commit 6b0eac21): anywhere we spawn `copilot` into a Squad-initialised repo, we must pass `--agent squad`. Single-line fix: "squad:copilot": "copilot --agent squad --additional-mcp-config @.mcp.json" Verified: a fresh `squad init` smoke test now prints the corrected tip line verbatim. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> * fix(sdk): wire team.md/routing.md/casting state on \squad preset apply\ (#1288) (#1293) * fix(sdk): wire team.md/routing.md/casting state on `squad preset apply` (#1288) `squad preset apply <name>` only copied agent charters into .squad/agents/. It left .squad/team.md `## Members` empty, .squad/routing.md missing `## Work Type -> Agent` rows, and never created .squad/casting/registry.json, history.json, or policy.json. Net result: the coordinator's mode-switch check saw an empty Members table and treated every session as Init Mode, proposing to re-scaffold the team the user already applied. This change adds a merge-friendly scaffold module (packages/squad-sdk/src/presets/scaffold.ts) that runs after charters are copied and: * writes/updates team.md `## Members` (creates from scratch if missing; appends new rows to an existing table while preserving the surrounding Coordinator / Project Context sections; idempotent on repeat apply) * writes/updates routing.md `## Work Type -> Agent` (creates or appends) * writes/merges casting/registry.json (universe = `preset:<name>`) * appends a snapshot to casting/history.json + a universe_usage_history entry * creates casting/policy.json with defaults only if missing (never clobbers) Agents with `status: 'error'` are excluded from wiring; agents with `status: 'skipped'` (already exist in target) ARE wired so the team reflects user intent. Scaffolder failure is reported as a synthetic error result without masking the per-agent install results. Verified: * npm run lint passes * test/presets.test.ts: 28/28 pass including 4 new regression tests - wires preset agents into team.md ## Members (#1288) - merges preset agents into an existing team.md without duplicating rows - writes casting registry.json, history.json, and policy.json (#1288) - appends routing rows for preset agents to routing.md (#1288) Out of scope (tracked separately): deduplicating these writers with the equivalent fresh-write versions in packages/squad-cli/src/cli/core/cast.ts. Closes #1288 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(presets): role-aware Status cell + non-colliding sentinel for synthetic scaffold error Two reviewer follow-ups on #1293 (closes #1288): 1. Members-table Status was hardcoded to '✅ Active' for every preset agent. Presets that ship one of the always-on built-ins (Scribe, Ralph, Rai, Fact Checker) would render with the wrong status label compared to a fresh cast: '✅ Active' instead of '📋 Silent' / '🔄 Monitor' / '🛡️ RAI' / '🔍 Verifier'. Added a small statusForRole() helper that mirrors the role→status mapping in cast.ts:652-655 (case-insensitive role matching to tolerate preset authors who lowercase the role string). Built-in role names get their canonical labels; everything else falls back to '✅ Active'. Added a regression test asserting the labels for a preset that ships scribe/ralph/rai/fact-checker + one regular agent. 2. Synthetic scaffold-failure result row used 'agent: presetName' for its 'agent' field. If the preset itself happens to include an agent literally named after the preset ('squad preset apply geektime' on a preset whose roster has a 'geektime' agent), the consumer of PresetApplyResult[] could not distinguish the synthetic scaffold- level error from a real per-agent install error. Replaced with the angle-bracketed sentinel '<scaffold>' (which validateName() rejects, so it can never collide with a real agent name) and moved the preset name into the human-readable reason string so consumers don't lose that context. Verified: 29/29 preset tests pass (28 existing + 1 new role-status test). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> * fix(docs): tell coordinator to roster Fact Checker on first-time cast (#1299) (#1300) * fix(docs): tell coordinator to roster Fact Checker on first-time cast (#1299) squad init correctly creates .squad/agents/fact-checker/ on disk (per merged PR #1223). But when the user opens copilot --agent squad and the coordinator runs first-time casting, it OMITS Fact Checker from the team.md ## Members table while including Scribe, Ralph, and Rai. Root cause: .squad-templates/squad.agent.md had two gaps: 1. Line 56 said "team size (typically 4-5 + Scribe)" — naming only Scribe 2. Rai had a dedicated ## Rai section with explicit "Rai always appears in team.md" instruction — Fact Checker had no equivalent section So the model added Rai (because instructed to) but had no instruction to add Fact Checker, even though the agent dir was scaffolded on disk. Fix: * Update team-size line to name all 4 always-on built-ins: Scribe + Ralph + Rai + Fact Checker * Add full ## Fact Checker — Verification & Devil's Advocate section mirroring the Rai pattern: roster-entry instruction, dual operating mode (per #789 + #1254), trigger phrase table, confidence ratings, DA brief structure, boundaries, state location Sync via sync-templates.mjs --sync propagates squad.agent.md changes to all 4 mirror targets: .squad-templates/, templates/, packages/squad-cli/ templates/, packages/squad-sdk/templates/, .github/agents/. Tests: new test/squad-agent-roster.test.ts runs against all 4 template targets and asserts: * The "Determine team size" line names all 4 built-ins * A ## Fact Checker section exists with "always appears in team.md" * The section declares dual operating mode (anchors #789 + #1254 design so a future PR can't accidentally split Fact Checker and Devil's Advocate again — cf. closed PR #1294) * Existing Ralph + Rai sections still present 16/16 pass. Closes #1299 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * doc(squad.agent.md): clarify Fact Checker is exempt from casting + correct on-demand reference path Reviewer follow-ups on #1300: 1. Team-size phrasing — the line read 'typically 4-5 + Scribe + Ralph + Rai + Fact Checker' which a model could parse as arithmetic (4-5 + 4 = 8-9, but it could also collapse). Rewrote it to make the composition explicit: '4-5 cast (user-domain) agents + 4 always-on built-ins = 8-9 total roster entries'. 2. Cast-exemption parity — Scribe, Ralph, and Rai each have an explicit 'exempt from casting' bullet but Fact Checker did not. Added the matching bullet right after Rai's. 3. Bad on-demand reference path — the FC section pointed at '.squad/templates/fact-checker-charter.md'. That file IS shipped (TEMPLATE_MANIFEST destination 'templates/fact-checker-charter.md') but only AFTER 'squad init' or 'squad upgrade' has populated .squad/templates/. A reader of squad.agent.md on an un-initialized repo (or in .github/agents/ on the cloud agent surface) would follow a dead link. Repointed to the '.squad/agents/fact-checker/charter.md' instance that ensureBuiltinAgents creates as part of the same init/upgrade path — that's where the rich charter actually lives at runtime per #1299 + #1301. All 4 mirrored copies re-synced via scripts/sync-templates.mjs. fact-checker-role.test.ts: 8/8 pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> * fix(sdk): plumb Fact Checker like Rai — rich charter at init + .squad/fact-checker/ state dir (#1299 deep) (#1301) * fix(sdk): plumb Fact Checker like Rai — rich charter at init + .squad/fact-checker/ state dir (#1299 deep) PR #1300 fixed the documentation gap so the coordinator knows to roster Fact Checker. This PR fixes the structural gap behind it. Per user testing 2026-06-13: even after #1300 the actual agent on disk was still "a name on disk with a 21-line placeholder". Three structural problems: 1. squad init never used the rich {role}-charter.md templates. Both Rai and fact-checker got 478-byte generic stubs from generateCharter(). Rich templates only ran via squad upgrade's ensureBuiltinAgents path. 2. fact-checker had no state dir. Rai gets .squad/rai/{policy.md, audit-trail.md} via init.ts lines 879-941. fact-checker had nothing equivalent. 3. fact-checker-charter.md was only in packages/squad-cli/templates/ — missing from .squad-templates/ (canonical source) AND packages/ squad-sdk/templates/. SDK init's getSDKTemplatesDir() resolves to the SDK templates dir, so even if init tried to read the rich charter, the file wasn't there. Fix (4 parts): Part 1 - Rich charter at init (benefits BOTH Rai and fact-checker): * SDK init.ts agent loop now looks up {templatesDir}/{role}-charter.md for each agent and uses that as charter.md content if it exists. Falls back to generateCharter() for user-defined agents. * Result: fresh squad init produces .squad/agents/Rai/charter.md at 4525 bytes (full Rai charter) and fact-checker/charter.md at 3024 bytes (full FC charter). Previously both were 478-byte stubs. Part 2 - .squad/fact-checker/ state dir mirroring .squad/rai/: * New block in init.ts (right after the Rai seeding) creates .squad/fact-checker/policy.md (from templates/fact-checker-policy.md or inline fallback) and audit-trail.md. * New .squad-templates/fact-checker-policy.md (~6KB) is the canonical authority for dual-mode operating rules per #789 + #1254: - Mode 1 Verification: ✅/⚠️/❌/🔍 confidence rating taxonomy - Mode 2 Devil's Advocate: required brief structure - Hard anti-fabrication rules - Advisory by default with narrow blocking exceptions - Audit trail rules (succinct, never raw source) Part 3 - Fix .squad-templates/ distribution gap: * Copied fact-checker-charter.md into .squad-templates/ so sync-templates.mjs propagates it to all 4 mirror targets including packages/squad-sdk/templates/. This unblocks Part 1. Part 4 - Plumbing: * .gitattributes: .squad/fact-checker/audit-trail.md merge=union * TEMPLATE_MANIFEST: fact-checker-policy.md * squad.agent.md Files Catalog: 2 new rows for FC state files Tests: 3 new regression tests in test/init.test.ts (28/28 pass total). npm run lint clean. Composability: This PR builds on #1300 (which adds the ## Fact Checker section to squad.agent.md and the team-size line fix). Both PRs modify squad.agent.md in disjoint regions and merge in either order. Full plumbing requires BOTH to land. Closes #1299 (deep fix; #1300 was the surface fix) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(sdk): lowercase fallback for rich-charter template lookup + sync .github/agents Two reviewer follow-ups on #1301 (#1299 deep): 1. Case-sensitive FS bug in rich-charter lookup The lookup tried '\-charter.md' and '\-charter.md' only. For Rai (role='Rai', name='Rai') this becomes 'Rai-charter.md', but the actual file shipped lowercase ('rai-charter.md'). On Windows the lookup succeeded because the filesystem is case-insensitive; on Linux CI it silently missed and fell back to the 478-byte generic stub — exactly the regression #1299 was trying to fix. Reproduced by 'should use the rich Rai-charter.md template at init' failing with 'expected 476 to be greater than 1000' on GitHub Actions. Add toLowerCase() candidates after the exact-case ones. De-dupe via a Set so we don't double-stat when role and name are already lowercase (fact-checker case). Guard each candidate against blank keys. 2. Template-sync parity The canonical .squad-templates/squad.agent.md gained two Fact Checker rows in the Files Catalog but the mirrored .github/agents/squad.agent.md copy was never re-synced, so the template-sync.test.ts byte-for-byte parity check would have fired. Run 'node scripts/sync-templates.mjs --sync' to regenerate. Verified: vitest 'rich Rai-charter' passes locally after the fix (previously failing on Linux CI run 27464079078). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> * fix(skills): rename squad disambiguation skill to squad-help (supersedes #1297) (#1302) * fix(skills): rename disambiguation skill 'squad' -> 'squad-help' (supersedes #1297) PR #1297 added a disambiguation skill named 'squad' so models calling skill(Squad) would get a redirect. After local end-to-end testing on 2026-06-13: skill ships to disk correctly but never shows up in Copilot CLI's /skills list. Root cause (verified against Copilot CLI source 1.0.62-2 app.js): 1. Copilot CLI's skill schema is {name, description, source, baseDir, allowedTools, pluginName, pluginVersion} (line 989). Frontmatter fields triggers:, domain:, confidence:, license: are silently ignored. 2. Skill loader returns {skills, warnings, errors} (line 4427). Skills that fail to load are reported as errors. 3. A skill named 'squad' collides with the Copilot agent named 'Squad' (registered at .github/agents/squad.agent.md). The agent wins; the skill is hidden from /skills. Fix: * Rename 'squad' -> 'squad-help' (avoids the agent-name collision; still descriptive enough for natural-language match when user says 'how do I use squad' or 'squad help') * SKILL.md content: name: 'squad-help', removed unused triggers:/domain:/ confidence:/source:/license: fields, added allowedTools: [], rewrote description: to be self-explanatory, added explicit note that /squad slash command does NOT exist (slash commands are CLI built-ins, not auto-mapped from skills) * MANIFEST_SKILL_NAMES in sdk-init.ts: 'squad' -> 'squad-help' * New TEMPLATE_MANIFEST entry in templates.ts for squad-help (so squad upgrade also propagates the skill - that code path uses TEMPLATE_MANIFEST instead of MANIFEST_SKILL_NAMES) Tests: new test asserts .copilot/skills/squad-help/SKILL.md exists with right frontmatter; explicit regression guard against re-introducing name: 'squad'. 26/26 init tests pass. npm run lint clean. Supersedes #1297. Out of scope (separate issue worth filing): squad upgrade synced only 10 of 16 installed skills - TEMPLATE_MANIFEST (used by upgrade) is out of sync with MANIFEST_SKILL_NAMES (used by init). Skills from PRs #1292 + #1295 (tiered-memory, iterative-retrieval, reflect, cross-squad, cross-squad-communication) have entries in MANIFEST_SKILL_NAMES but not TEMPLATE_MANIFEST. Follow-up will fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(skill): add confidence + domain frontmatter to squad-help Reviewer follow-up on #1302: the isSkillContent() classifier in sharing/consult.ts:990 requires BOTH name: AND confidence: in the frontmatter to recognize a file as a skill. Without confidence:, squad-help would not be detected as a skill in cross-squad merge / share / promote flows — it would be misclassified as a generic markdown decision. The Copilot CLI itself silently ignores custom frontmatter fields (per sdk/index.js decompile — only name/description/allowedTools/ user-invocable are read), so adding confidence: high and domain: squad-onboarding is safe at the CLI surface and necessary at the SDK surface. Applied identically to all 3 mirrored copies of squad-help/SKILL.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.…
…resource for Squad AI-agent teams (#1394) * feat: add CommunityToolkit.Aspire.Hosting.Squad Introduces a new hosting integration that lets a .NET Aspire AppHost model a Squad (https://github.com/bradygaster/squad) AI-agent team as a first-class Aspire resource. Closes #1393 What's included --------------- - `src/CommunityToolkit.Aspire.Hosting.Squad/` — the package - `SquadResource : Resource, IResourceWithConnectionString` Auto-discovers the agent roster from `.squad/team.md` (table and bullet-list formats), only keeping names that map to an existing `.squad/agents/{name}/charter.md` to filter typos. Exposes `squad://resource/{name}?teamRoot={...}&agents={csv}&protocol=maf-1.0` so downstream services can consume the team via `.WithReference`. - `SquadBuilderExtensions.AddSquad(name, teamRoot)` decorated with `[AspireExport]` (ATS-compatible). - `SquadLifecycleHook` publishes Spawning -> Active transitions on the dashboard and seeds `SquadDashboardProperties` so the roster, protocol version, and team root show on the resource row. - Dashboard commands wired via `WithCommand` (open Copilot CLI on the team root, etc.). - Public API surface file at `src/CommunityToolkit.Aspire.Hosting.Squad/api/CommunityToolkit.Aspire.Hosting.Squad.cs` per repo convention. - `tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/` — 7 xUnit unit tests covering: resource registration, default roster fallback, guard clauses, table-format roster parsing, bullet-format roster parsing, and connection-string shape. All pass locally on net10.0. - `examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost/` — minimal AppHost example with a self-contained `sample-squad/.squad/` fixture so the example builds and runs without external state. - Solution + root README updates (table row, link refs). Out of scope (planned follow-ups, kept separate for reviewability) ------------------------------------------------------------------ - OpenTelemetry signals (agent spawns, token usage, session metrics). Depends on the Squad telemetry contract still being designed at bradygaster/squad#1144. - Direct `Squad.Agents.AI` consumer-side extension that constructs an MAF `AIAgent` from a referenced squad. The connection-string shape here is the integration point; consumer-side wiring is a separate package design. Local verification ------------------ - `dotnet build` on all three new projects (Release) — 0 warnings, 0 errors - 7/7 tests pass via Microsoft Testing Platform on net10.0 - Example AppHost builds cleanly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(examples): wire WithReference + Squad.Agents.AI into the example The original example AppHost only registered a SquadResource. Per the PR review request from #1394, the example now also demonstrates end-to-end consumption: a downstream ApiApp project receives the squad://... connection string via .WithReference(squad), parses the team root, and uses Squad.Agents.AI to drive a real 3-turn conversation against the referenced team. What's added ------------ - examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/ - ASP.NET minimal API; Program.cs reads ConnectionStrings:research-squad (squad://... format), extracts teamRoot, registers SquadAgent via AddSquadAgent, exposes POST /ask that runs a 3-turn conversation through AgentSession (demonstrates session memory across turns). - examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ServiceDefaults/ - Standard Aspire ServiceDefaults shape (OTel + service discovery + health checks), mirroring the Java example layout. - examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost/ - Updated to AddProject<...>("squad-api").WithReference(researchSquad) - Directory.Packages.props - Added Squad.Agents.AI 0.1.0-preview.3 (centralized version), the preview that ships the SessionConfig/OnPermissionRequest fix needed to drive a real conversation. See bradygaster/squad#1252. - CommunityToolkit.Aspire.slnx - 2 new projects added (ApiApp, ServiceDefaults). - src/CommunityToolkit.Aspire.Hosting.Squad/README.md - New "Consume the team from a service project" section pointing callers at the new examples/squad/ runnable. Local verification ------------------ - dotnet build (Release) of all 5 squad-related projects: 0 warn, 0 err - All 7 unit tests still pass on net10.0 (MTP) - Squad.Agents.AI 0.1.0-preview.3 restores from nuget.org cleanly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bump Squad.Agents.AI to 0.2.0-preview.4 + workaround GitHub.Copilot.SDK direct ref Squad.Agents.AI 0.2.0-preview.4 (released earlier today from bradygaster/squad#1259) brings the package onto Microsoft.Agents.AI.GitHub.Copilot 1.10.0-rc1, which transitively pulls GitHub.Copilot.SDK 1.0.0 GA. The earlier 0.1.0-preview.3 was wedged on SDK 1.0.0-beta.2 + a CLI protocol the current copilot CLI no longer speaks — verified by running the example ApiApp in this branch against a real .squad-initialised team root and seeing the agent successfully read .squad/team.md and dispatch work to multiple specialists. Also add a direct PackageReference to GitHub.Copilot.SDK 1.0.0. This is a temporary workaround: the SDK ships its CLI-binary-download MSBuild targets under build/ (only auto-imported for projects with a *direct* PackageReference), not buildTransitive/. microsoft/agent-framework#6457 (merged 2026-06-10) fixes this at the MAF adapter level — once a MAF preview ships with that change, the direct GitHub.Copilot.SDK ref can be dropped from Directory.Packages.props and the ApiApp csproj. Both edit sites carry an inline comment explaining the lifecycle. Verified: the hosting library, the ApiApp, and the AppHost all build clean, and copilot.exe lands at the expected 'bin/{cfg}/{tfm}/runtimes/{rid}/native/' path in the ApiApp output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bump to Squad.Agents.AI 0.3.0-preview.5 + wire subagent observability into ApiApp Squad.Agents.AI 0.3.0-preview.5 (released today from bradygaster/squad#1265) adds a first-class subagent observability surface: a typed OnSubagentTrace callback + an OpenTelemetry ActivitySource named 'Microsoft.Agents.AI.Squad' that emits one span per task-tool dispatch with squad.subagent.name / squad.subagent.display_name / squad.subagent.sdk_agent_id / squad.subagent.reply_preview tags. This commit: * Bumps Squad.Agents.AI from 0.2.0-preview.4 to 0.3.0-preview.5 in Directory.Packages.props. * Wires the new ActivitySource into the ApiApp's OpenTelemetry tracer (one line on top of the AddServiceDefaults() configuration) so every subagent dispatch shows up as its own span in the Aspire dashboard's Traces view, with the subagent name + a preview of its reply attached as span tags. * Adds OnSubagentTrace logging in the ApiApp so subagent lifecycle (spawn, reply, complete) also surfaces in the dashboard's Structured Logs view — both signals are correlated by Activity.Current so the OTel span and the structured log share the same trace id. * Adds a new POST /dispatch endpoint that issues an explicit task-tool dispatch prompt ('use the task tool to dispatch two parallel subagents...'). Hitting this endpoint from the dashboard produces a multi-span trace showing the coordinator + each subagent as separate spans — the headline visibility demo. * Existing POST /ask 3-turn endpoint kept for the session-memory demo. Verified locally: AppHost boots clean, the 'research-squad' resource registers 2 agents (ralph, picard from the sample-squad team), and the ApiApp builds + runs with the new observability wiring against Squad.Agents.AI 0.3.0-preview.5 restored from nuget.org. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * examples/squad: replace stub sample-squad with two real squads (research + dev) The previous bundled sample-squad/ had stub one-line charters with nothing to dispatch to, so /ask returned coordinator role-play and /dispatch produced no subagent spans in the Aspire dashboard. Replace it with two real Squad teams created via 'squad init --no-workflows' and autonomously cast by the Squad coordinator running under 'copilot --yolo': research-squad/ Matrix-cast AI/ML research team Morpheus (Lead) - Trinity (ML/Data) - Oracle (Eval) - Tank (Tester) Stack: Python, PyTorch/TensorFlow, Jupyter, MLflow dev-squad/ Simpsons-cast full-stack development team Lisa (Lead) - Marge (Frontend) - Frink (Backend) - Comic Book Guy (Tester) Both squads have full .squad/ scaffolding: team.md, per-agent charter.md and history.md, casting registry, decisions ledger, ceremonies, routing, and the .copilot/ skills the agents read. Runtime state (orchestration-log/, log/, sessions/, decisions/inbox/) is excluded via the per-squad .gitignore that 'squad init' generates. AppHost wires both as Aspire resources via .AddSquad('research-squad', ...) and .AddSquad('dev-squad', ...), and references both from the ApiApp via .WithReference(researchSquad).WithReference(devSquad). Aspire injects two connection strings under ConnectionStrings:research-squad and ConnectionStrings:dev-squad. ApiApp rewritten to: - Register one SquadAgent per squad via AddKeyedSquadAgent(serviceKey, ...) using the keyed-DI overload shipped in Squad.Agents.AI 0.1.0-preview.2 - Resolve the right agent per request via [FromKeyedServices] / ?squad= query - /ask?squad=research|dev runs a 3-turn conversation against the picked team - /dispatch?squad=research|dev sends an explicit 'use the task tool to dispatch two parallel subagents' prompt so we reliably see real subagent spawns (not inline role-play) and one squad.subagent {Name} OTel span per spawn in the Aspire dashboard Traces view - OnSubagentTrace forwards subagent start/done/message to console so the structured logs line up with the OTel trace timeline AppHost launchSettings.json also gains DCP_IDE_REQUEST_TIMEOUT_SECONDS=300 to prevent the 120s DCP/VS run-session race that falls back to bare dotnet.exe and produces 'Usage: dotnet [path-to-application]' in the ApiApp logs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * examples/squad: fix ?squad=research|dev keyed-DI lookup + clean AppHost comments The ApiApp registered SquadAgent under keys 'research-squad' and 'dev-squad' (the Aspire resource names) but the /ask and /dispatch endpoints take ?squad=research|dev (short form). The dictionary lookup mismatched and both endpoints returned {"error":"Unknown squad 'research'..."}. Fix: - Register each keyed SquadAgent under the short name ('research', 'dev') - Resolve the longer 'research-squad' / 'dev-squad' name only when reading the connection string (which IS keyed by the Aspire resource name) - Drop the unused ResearchSquad/DevSquad const Also clean up the duplicate using + stale comment block in AppHost/Program.cs left over from the previous edit. Verified end-to-end with a live run: - Both squads come up Active in the dashboard (research: 7 agents discovered; dev: 6 agents discovered) - POST /dispatch?squad=research returns the Morpheus + Trinity answers verbatim - POST /dispatch?squad=dev returns the Lisa + Frink answers verbatim - ApiApp spawns one copilot.exe child per request (real subagent dispatch, not inline role-play) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * examples/squad: ship .github/agents/squad.agent.md inside each squad folder When 'squad init' is run from a folder that has a parent git repo (i.e. our case — the Squad CLI's monorepo detection kicked in because Aspire-1 is a git repo), the CLI places .github/agents/squad.agent.md at the GIT ROOT instead of inside the squad folder. That made the example squads non-self-contained: anyone cloning this repo and pointing SquadFolderPath at research-squad/ or dev-squad/ would have no coordinator agent file (Copilot resolves .github/agents/ relative to the consumer's git root, not ours). For an in-repo example that ships as a finished artifact, the squads need to own their own coordinator. Copy the same squad.agent.md (v0.9.6-insider.3, 71594 bytes) into each squad's own .github/agents/ directory. Also expand each .gitignore to cover Copilot CLI SDK runtime files that get created when a SquadAgent serves a request: .squad/session-state/ .squad/session-store.db .squad/session-store.db-shm .squad/session-store.db-wal The bundled .gitignore from 'squad init' already covered .squad/sessions/ but those SDK-side files use slightly different paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * examples/squad: switch ApiApp to ILogger + emit subagent activity events Two problems with the previous Console.WriteLine approach: 1. Console.WriteLine output did not consistently surface in the Aspire dashboard when the SDK fired OnSubagentTrace from its background event pump (different thread, no captured stdout context). The user reported empty Console Logs even though the callback was firing. 2. The SDK's per-subagent OTel spans (Microsoft.Agents.AI.Squad activity source) may end up as orphan traces — the SDK's Channel-based event pump can fire on a thread where Activity.Current has been cleared by the AsyncLocal flow, so StartActivity has no parent and the span becomes a new root trace detached from the HTTP request's trace. Fixes: - Use ILogger<>'s structured logging via Microsoft.Extensions.Options' Configure<ILoggerFactory> overload, which injects the fully-built ILoggerFactory into the SquadAgentOptions post-configure step (runs after the host is built, before any RunAsync call). Each subagent start / message / completed lands in the Aspire dashboard's Structured Logs view with the squad name, subagent name, and message preview as queryable fields. - Add an app-owned ActivitySource ("Squad.Hosting.ApiApp") and wrap each /ask and /dispatch handler in a "squad.dispatch {endpoint} {squad}" span. This guarantees a single, named root span per request that the user's trace search can land on, even when the SDK's per-subagent spans become orphans. - Inside OnSubagentTrace, also call Activity.Current?.AddEvent(...) so each subagent lifecycle event (start / message / completed) is attached as an OpenTelemetry ActivityEvent to whatever span is current at callback time (the SDK's subagent span when open, the dispatch wrapper otherwise). Events show up as labelled annotations on the span timeline in the Aspire dashboard trace detail view. - Wire the new app source into AddOpenTelemetry().WithTracing(...) alongside the existing Microsoft.Agents.AI.Squad source. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * examples/squad: upgrade to Squad.Agents.AI 0.4.0-preview.6 + simplify ApiApp wiring 0.4.0 (bradygaster/squad#1271, merged today) ships two changes that remove the boilerplate this example needed in 0.3.0: 1. AddKeyedSquadAgent("research-squad") now resolves ConnectionStrings:research-squad (Aspire-injected) directly, with a fallback to the legacy ConnectionStrings:squad-research-squad form. No more manual builder.Configuration.GetConnectionString() + URI parse + feeding SquadFolderPath through a configure callback. 2. EmitSubagentActivities defaults to true. Just call .AddSource(SquadAgentDiagnostics.ActivitySourceName) on the tracer and "squad.subagent {Name}" spans appear in the Aspire dashboard's Traces view with lifecycle ActivityEvents (start / message / completed / failed) annotated on the timeline — without setting OnSubagentTrace at all. OnSubagentTrace becomes a pure customisation hook now, kept here only to surface per-subagent ILogger lines in the Structured Logs view. ApiApp Program.cs: - Drop the foreach that called GetConnectionString + ParseSquadTeamRoot (16 lines) — replaced by a single AddKeyedSquadAgent("{resource}") call per squad, with a separate AddOptions<>().Configure<ILoggerFactory>(...) hop for the OnSubagentTrace ILogger callback. - Drop the local ParseSquadTeamRoot helper — the SDK does the parsing. - Drop the squadTeamRoots dictionary — was a leaky abstraction of the SDK's connection-string handling. Replaced with squadKeysByShortName that just maps the query-param short name ("research") to the keyed-DI key ("research-squad"). Bumped Squad.Agents.AI 0.3.0-preview.5 → 0.4.0-preview.6 in Directory.Packages.props. End-to-end verified live against the published 0.4.0-preview.6 package: - Both squads register cleanly with no manual connection-string handling - POST /dispatch?squad=research returned the Morpheus + Trinity replies verbatim (real task-tool subagent dispatch) - Smaller, cleaner Program.cs that closely mirrors the README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * examples/squad: pass --agent squad to CLI + drop terse Instructions override When running 'copilot --agent squad' interactively in a terminal, the coordinator loads .github/agents/squad.agent.md (1023 lines: eager execution, parallel fan-out, dispatch via task tool) as its system prompt and follows that contract. When the same Squad team is invoked through Squad.Agents.AI's SquadAgent, the underlying copilot.exe child process was using its default agent — so the coordinator had no instructions to fan out or dispatch, and the terse 'Be concise' Instructions override we were passing actively encouraged it to role-play the team in a single reply instead of spawning real subagents. Fix: - Pass --agent squad in CliArgs so the CLI selects squad.agent.md as the agent definition for the spawned session - Drop the per-squad Instructions override (was overriding the full squad.agent.md system prompt with one terse line) Verified live against Squad.Agents.AI 0.4.0-preview.6: - POST /ask?squad=dev with a Full Mode prompt ('Use the task tool to dispatch each of Lisa, Marge, Frink and Comic Book Guy in parallel ...') produced the eager-execution narration ('Waiting for all four agents to return. Still waiting for Marge.') that only squad.agent.md emits, AND returned four distinct subagent replies — proof that --agent squad is active and the task tool is dispatching. Note: 'team, i want every member to say its name' still gets a single coordinator reply because squad.agent.md classifies that as Direct Mode ('Who's on the team?' → answer from team.md, no spawn). For the observability demo, use either POST /dispatch?squad=X or send a Full Mode prompt to /ask. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * examples/squad: bump Squad.Agents.AI 0.5.0-preview.7 -> 0.5.1-preview.8 0.5.0-preview.7 introduced a regression: setting sessionConfig.Agent ='squad' caused a runtime failure ('Custom agent ''squad'' not found') because the SDK property looks up the SDK's CustomAgents registry, NOT the on-disk .github/agents/*.agent.md files. 0.5.1-preview.8 (bradygaster/squad#1277) reverts to the original --agent CLI flag approach which actually reads the on-disk agent definition the same way 'copilot --agent squad' does interactively. Verified live: POST /dispatch?squad=dev now returns distinct subagent voices (Lisa: 'A good software architecture makes change boring ...'; Frink: 'A well-designed API's most important property is a clear, stable contract that makes correct usage obvious and reliable—glavin!'), which confirms (a) --agent squad is now being passed through, (b) squad.agent.md is loaded as the coordinator's system prompt, and (c) real subagent dispatch via the task tool is happening (not coordinator role-play). The ApiApp's Program.cs already drops the manual --agent CLI workaround from the previous 0.5.0 upgrade commit, so this is a version-only bump. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * examples/squad: collapse /dispatch into /ask + fix nested-project build break ApiApp: - Drop /dispatch. The endpoint was just /ask with a hardcoded "Use the task tool to dispatch ..." prompt; same SquadAgent.RunAsync path, same coordinator, same span/log surface — the only difference was the prompt. Removing it makes the API surface honest: there's one endpoint, the caller owns the prompt, the coordinator picks the mode (Direct / Lightweight / Full). - GET / now returns a copy-paste "sample_prompts" menu (roster_recall_direct_mode, multi_turn_memory, dispatch_full_mode_research, dispatch_full_mode_dev), each shaped like the /ask body so the user can paste straight through to reproduce every mode and see what the corresponding spans look like. - Drop the unused AskRequest.Default sample (the / index now owns sample prompts). - Rename the wrapper span "squad.dispatch ask {squad}" -> "squad.ask {squad}". AppHost build break: Earlier interactive sessions created scratch C# projects inside the squads (e.g. research-squad/UserNameApp/, dev-squad/UserNameApp/) when the user asked the team to "write a program in C3 / C#". The Aspire AppHost SDK's default <Compile>, <Content>, <None>, and <EmbeddedResource> globs sweep any .cs / .csproj at any depth under the AppHost project folder, which caused two cascading build errors on the next F5: CS8802: Only one compilation unit can have top-level statements. CS0579: Duplicate AssemblyInfo attributes. Fix in two layers: 1. AppHost.csproj: explicit <Compile|Content|None|EmbeddedResource Remove> on research-squad\** and dev-squad\**. The squads are scaffolding-only (.squad/, .github/, .copilot/, .mcp.json) and should never participate in the AppHost compile graph. 2. Each squad's .gitignore: append *.csproj / *.fsproj / *.vbproj / bin/ / obj/ guards so future scratch projects don't get committed either. Also delete the existing UserNameApp/ scratch dirs from both squads. Squad.slnx: New focused solution file — just src/CommunityToolkit.Aspire.Hosting.Squad, the AppHost, the ApiApp, the ServiceDefaults, and the tests. Trims the noise out of F5 / VS solution explorer for anyone who clones the example and only wants to run the Squad bits without loading the rest of the Aspire repo's projects. Verified live against Squad.Agents.AI 0.5.1-preview.8: - GET / lists 4 sample_prompts including the full-mode dispatch prompts - POST /ask?squad=dev with the dispatch_full_mode_dev sample produced Lisa + Frink replies with their distinct specialist voices, confirming real subagent dispatch through the task tool. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * review: address Copilot PR review comments on PR #1394 Five inline review comments from the Copilot reviewer, all addressed: 1. SquadResource.cs: Cast MatchCollection to IEnumerable<Match> Explicit .Cast<Match>() on both Regex.Matches results so the LINQ Concat/Select binding is unambiguous across TFMs (older targets expose MatchCollection only as non-generic IEnumerable). No behavior change on net10.0 where it already implements IList<Match>; defensive clarity only. 2. SquadBuilderExtensions.StartConsoleWindow: drop fragile cmd /c start invocation The previous approach used 'cmd /c start <windowTitle> powershell.exe ...', which fails or misbehaves when the title contains spaces (e.g., 'Copilot - research-squad'): cmd's quote-stripping causes 'start' to parse the title fragment as part of the command. Now launches powershell.exe directly with UseShellExecute=true and sets the title from inside the new shell via \System.Management.Automation.Internal.Host.InternalHost.UI.RawUI.WindowTitle, removing all cmd/start parsing fragility while preserving the friendly window title. 3. api/*.cs: regenerated to match the real public signature The hand-typed surface declared 'string teamRoot' (required) but the implementation exposes 'string? teamRoot = null'. Re-ran GenAPIGenerateReferenceAssemblySource; the file now correctly shows the nullable optional parameter and also picks up SquadTeamAnnotation, which the previous version had missed. 4. tests/AssemblyInfo.cs: removed InternalsVisibleTo on the test assembly grants visibility to itself (no-op). The production csproj already wires InternalsVisibleTo to the test assembly correctly; the test-side file was duplicate dead config. 5. tests/SquadResourceCreationTests.cs: deterministic temp cleanup Class now implements IDisposable and tracks every temp dir created by the helpers (instance-scoped _tempRoots). Dispose() best-effort deletes them recursively so test residue no longer accumulates in %TEMP% across runs. Tests: 7/7 pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * review: address Aaron's PR feedback on #1394 - Bump AppHost SDK reference from Aspire.AppHost.Sdk/13.4.0 to 13.4.3 (matches the post-13.4.3 upgrade Aaron sync'ed into main via #1397) - Add hard PackageReference Include="MessagePack" to the hosting integration, AppHost, and Tests projects to pull the patched 2.5.301 pinned centrally in Directory.Packages.props instead of the vulnerable transitive version flowing through Aspire.Hosting (CVE-2026-48109 / GHSA-hv8m-jj95-wg3x) - Remove root-level Squad.slnx (per request — the unified Aspire.slnx covers it) Build clean (0 warnings, 0 errors) on all three Squad projects. MessagePack 2.5.301 verified as the resolved version via dotnet list package --include-transitive in AppHost, hosting, and tests projects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(squad): raise line coverage 43% -> 79% (above the 60% gate) Adds three test files covering the previously-untested classes: - SquadLifecycleHookTests (12 tests, ~78% line / 100% branch) - Tests the hook in isolation with a CapturingEventing test double, a real ResourceNotificationService, and synthesised BeforeStart / AfterResourcesCreated / ResourceStopped events. No DCP / Dashboard runtime required. - Asserts state transitions Spawning -> Active -> Finished, no-ops for non-Squad ResourceStopped and zero-squad BeforeStart, ArgumentNullException on null eventing, and graceful degradation when SquadDashboardProperties raises an IOException. - SquadDashboardPropertiesTests (7 tests, 100% line / 100% branch) - CreateStatic shape + null guard. - CreateWithLiveStats covers: no inbox dir, multiple .md files in inbox, decisions.md first-non-blank line, long-line truncation at 80 chars, whitespace-only decisions.md keeping the default 'none'. - SquadBuilderCommandsTests (7 tests) - Exercises each dashboard command (refresh-agents, open-team-root, open-copilot-cli, check-inbox) by invoking the ExecuteCommand delegate on the resource's ResourceCommandAnnotation. - Covers the non-Windows branch of LaunchCopilotCli (the OS-bound spawn paths themselves are marked [ExcludeFromCodeCoverage]). Also: - Marks StartWindowsTerminal / StartConsoleWindow with [ExcludeFromCodeCoverage] - they hand off to wt.exe / powershell.exe and can only be exercised by a real desktop session. Argument-list building is simple and visible; the launch itself is OS-bound. - Adds ProjectReference to CommunityToolkit.Aspire.Testing so we have the shared test infra available (matches every other integration test project in this repo). Local verification: - 28/28 tests pass (3-of-3 runs, no flake) - dotnet build clean (0 warnings, 0 errors) on hosting, AppHost, tests - Coverage measured via Microsoft.Testing.Extensions.CodeCoverage: Total lines 353, covered 279, line rate 79.0% (was 43%). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: update Squad.Agents.AI to stable 0.5.1 Now that bradygaster/squad PR #1313 merged and NuGet 0.5.1 (stable) is published, switch from preview to the stable release. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback: cross-platform CLI, primary ctors, remove api file - Remove Windows-only guard from LaunchCopilotCli; add Linux/macOS support - Convert SquadLifecycleHook to primary constructor - Convert SquadTeamAnnotation to primary constructor - Delete auto-generated api/CommunityToolkit.Aspire.Hosting.Squad.cs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix test: non-Windows now attempts CLI launch (cross-platform) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(deps): bump Squad.Agents.AI to 0.5.1-preview.16 (delegation fix) Bumps the centrally-managed Squad.Agents.AI version from 0.5.1 to 0.5.1-preview.16 so the squad ApiApp example actually delegates work to its sub-agents via the `task` tool. With 0.5.1, the dispatcher squads always reply directly to the user instead of invoking sub-agents — the SDK was suppressing the dispatch event. This was fixed upstream in microsoft/agents-ai-squad PR #1378. Verified end-to-end on bradygaster/squad-with-aspire PR #46 (same one-line bump on a sibling consumer of Squad.Agents.AI) — sub-agent traces and child spans now appear correctly in the Aspire dashboard. Side effect: 0.5.1-preview.16 requires GitHub.Copilot.SDK >= 1.0.3, so the direct-pin workaround for GitHub.Copilot.SDK is bumped from 1.0.0 to 1.0.3 as well (also brings the Copilot CLI runtime to 1.0.64-1, which is what the SDK expects). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com> Co-authored-by: Aaron Powell <me@aaron-powell.com>
* fix: auto-scaffold Fact Checker agent during init and cast (#1222)
The Fact Checker role landed in v0.10.0 (#789) with catalog entry,
charter template, skill, AGENT_TEMPLATES map entry, and template
manifest entry — but was never wired into the user-facing onboarding
flow. Users running 'squad init' got Scribe/Ralph/Rai but never saw
Fact Checker as a default or cast option.
This mirrors how Rai was wired:
- init.ts: adds 'fact-checker' to the default agents: array passed
to sdkInitSquad()
- cast.ts: adds factCheckerMember(), factCheckerCharter(),
hasFactChecker branches in castTeam(), and the roster banner line
Smoke-tested locally: 'squad init' in a clean repo now produces
.squad/agents/fact-checker/charter.md alongside scribe/ralph/Rai.
Closes #1222
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(upgrade): auto-scaffold Rai + Fact Checker on squad upgrade (#1222)
Extends the #1222 fix to the third code path. \squad upgrade\ was
intentionally silent on agents (preserves user state). For users
upgrading from v0.9.x or earlier (no Rai) or v0.10.0 (no fact-checker),
this means they'd never get the built-in agents unless they re-ran
\squad init\ (which would overwrite other state).
Adds \�nsureBuiltinAgents()\ to \
unEnsureChecks()\. Idempotent —
only scaffolds when the agent directory is absent. Never overwrites
existing charters or history files. Sources content from the shipped
\ emplates/{Rai,fact-checker}-charter.md\ templates (already present
via TEMPLATE_MANIFEST).
Scribe and Ralph are intentionally NOT scaffolded by upgrade — they
predate this fix in every squad, and their charters are inlined in
cast.ts (no shipped template file).
Smoke tested locally:
- Set up a simulated v0.9.4 squad (scribe + ralph only)
- Ran \squad upgrade\ → 'scaffolded 2 built-in agent(s): Rai, fact-checker'
- Ran upgrade again → no re-scaffold (idempotent)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(permissions): use 'approve-once' for Copilot CLI v1.0.54+ contract (#1192)
The Copilot CLI post-v1.0.54 changed the permission handler contract to
expect 'approve-once' instead of 'approved'. Update the handler, type
definition, and error hint to match the new contract.
Closes #1191
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: Squad.Agents.AI - Microsoft Agent Framework adapter for the Squad CLI (#1207)
* feat: Squad.Agents.AI community NuGet for MAF integration
Squad CLI as Microsoft.Extensions.AI IChatClient, composing
GitHub.Copilot.SDK via AsAIAgent() from Microsoft.Agents.AI.GitHub.Copilot 1.7.0-preview.
Closes Track A of the Q1-Q7 design lock (see tamresearch1 .squad/decisions.md
Decisions 441, 443, 444, 447).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: add PR body for #3
* fix(SquadAgent): inherit AIAgent (was IChatClient force-cast)
- SquadAgent now properly inherits from Microsoft.Agents.AI.AIAgent
- Removed (IChatClient)(object)agent force-cast
- Overrides all AIAgent abstract members (CreateSessionCoreAsync, RunCoreAsync, etc.)
- DI registration now registers AIAgent (not IChatClient)
- README updated to use AIAgent.RunAsync API
- No more abstraction inversion; AIAgent is the correct layer
Fixes the architectural error identified by Tamir.
* docs(SquadAgent): rewrite README — prerequisites, Hello World, troubleshooting, preview callout
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(SquadAgent): GitHubTokenProvider callback + redact options ToString
Adds async token provider pattern for production scenarios (KeyVault/MSI integration).
- GitHubTokenProvider property takes precedence over GitHubToken
- GitHubToken marked [JsonIgnore] to prevent serialization leaks
- SquadAgentOptions.ToString() redacts GitHubToken field
- Updated CreateCopilotClient to resolve token from provider first
Mitigates P0 #3: token leakage via ILogger structured-log calls, IOptions snapshots, and serializers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(SquadAgent): document GitHubTokenProvider callback for production token management
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(SquadAgent): bind ConnectionStrings__squad via IConfigureOptions + add smoke tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(README): replace removed WithTeamRoot with positional teamRoot ctor
WithTeamRoot was deleted in commit 35767c90 in favor of mandatory positional
teamRoot constructor argument on AddSquad. The Aspire example in the
Squad.Agents.AI README still showed the old fluent API, which would now
fail at compile time for anyone copy-pasting.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(squad-agents-ai): add routing integration tests
Closes the routing-verification gap identified during
squad-squad onboarding: the API surface existed but
routing semantics weren't functionally tested. New tests
verify persona pass-through, boundary-instruction injection
on first turn, WorkingDirectory isolation (Decision 452a),
and CopilotClientOptions-based routing (Decision 447).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ci(squad-agents-ai): add .NET build/test/pack workflow
PR #3 CI was Node/docs-only — adding the .NET gate so green
actually reflects the package code. Matrix on ubuntu + windows,
restore/build/test/pack, uploads TestResults and nupkg artifacts.
Closes the build-verification gap identified during squad-squad
onboarding (see .squad/decisions.md adoption record, 2026-06-02).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ci(squad-agents-ai): NuGet publish workflow + Dependabot config
- .github/workflows/squad-agents-ai-release.yml: workflow_dispatch
and tag-driven publish to nuget.org with --skip-duplicate idempotency,
fail-fast on missing NUGET_API_KEY secret, optional GitHub Release on tag
- .github/dependabot.yml: nuget (src + test) + github-actions, weekly,
M.A.AI major allowed, OpenTelemetry major deferred (per Decision 602)
Closes the release-pipeline + supply-chain-tracking gaps identified
during squad-squad onboarding.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(squad-agents-ai): release-ready docs + .csproj packaging metadata
- README updates / XML docs on public surface
- CHANGELOG.md with [0.1.0-preview] - 2026-06-02 entry
- .csproj: Description, RepositoryUrl, Authors, PackageTags, PackageReadmeFile
- Verified via dotnet pack — .nupkg contains README, LICENSE, xml-docs
Closes the docs-readiness gap for v0.1-preview publish.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ci(squad-agents-ai): switch release triggers to dev/main branch-driven
Per Tamir's release-strategy directive (decisions.md 2026-06-02):
- dev merges → prerelease publish (suffix scheme mirrors Squad CLI)
- main merges → stable publish
- workflow_dispatch retained as manual escape hatch
- tag-driven trigger removed (branches are the source of truth)
Version derivation pattern adapted from the Squad CLI's existing
release workflow. --skip-duplicate retained for idempotent reruns.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs+fix: PR #3 review pass — hygiene, XML docs, cliArgs, multi-named connections
- Rewrite the package README and PR validation flow around the AIAgent surface and ambient Copilot authentication.
- Keep public docs free of internal process references and remove obsolete deferral language.
- Preserve connection-string cliArgs through CopilotClientOptions and cover the behavior with a routing test.
- Add named connection-string lookup via AddSquadAgent("name") using ConnectionStrings:squad-{name}.
- Document the new public overloads and verify the package builds without warnings.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: Round 2 — keyed DI, BYOK delegate, routing gate, security hardening
- Add ConfigureCopilotClient delegate on SquadAgentOptions for BYOK
- Add routing gate: snapshot/restore Cwd/CliPath/CliArgs after delegate (Picard C1)
- Add 4 AddKeyedSquadAgent overloads with .NET 8+ keyed DI
- Fix Environment credential leak: [JsonIgnore] on Environment, GitHubTokenProvider, ConfigureCopilotClient
- ToString() redacts token-pattern keys (TOKEN/KEY/SECRET/HMAC/PASSWORD/CREDENTIAL)
- Add 21 new tests (43 total): security redaction, keyed DI, BYOK routing gate
- Update README: streaming, keyed DI, BYOK, security sections
Complies with: Picard C1-C4, Worf SC-1 through SC-8.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: add Squad.Agents.AI sample app demonstrating DI, keyed DI, BYOK, and streaming
- samples/squad-agents-ai-sample/Program.cs: four runnable flows
Flow 1 -- AddSquadAgent + RunAsync (basic DI)
Flow 2 -- AddKeyedSquadAgent x2 + GetRequiredKeyedService<SquadAgent>
Flow 3 -- ConfigureCopilotClient delegate (BYOK token + env var injection)
Flow 4 -- RunStreamingAsync with await foreach token-by-token output
- samples/squad-agents-ai-sample/Squad.Agents.AI.Sample.csproj: net10.0,
project reference to src/Squad.Agents.AI, Microsoft.Extensions.Hosting 10.0.0
- samples/squad-agents-ai-sample/README.md: prerequisites, run commands,
per-flow walkthrough, troubleshooting table
- .github/workflows/squad-agents-ai-ci.yml: adds paths trigger and
restore + build steps for the sample (no run step -- requires live CLI)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor(Squad.Agents.AI): co-locate sample under src/ and consolidate README
- Moves the sample app from samples/squad-agents-ai-sample/ to
src/Squad.Agents.AI/samples/Squad.Agents.AI.Sample/ so it lives
alongside the package it demonstrates.
- Folds the sample's standalone README into the package README, giving
consumers one canonical doc for both the API and the runnable demo.
- Adds <Compile Remove="samples/**/*.cs" /> to Squad.Agents.AI.csproj
so the library's wildcard glob does not pick up Program.cs in the
co-located samples subdirectory.
- Updates the .csproj project reference, and CI workflow paths to match
the new layout.
- Verified end-to-end: dotnet build, dotnet test (43/43 passing), and a
sample sanity-check run (clear CLI-not-found error, no stack trace).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore(Squad.Agents.AI): remove outdated draft PR body file
The standalone pr-body.md was an early draft authored before the live PR description took its final shape. The PR body on GitHub is the canonical source; this file is dead weight and would confuse maintainers reviewing the diff.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(Squad.Agents.AI): address PR #1207 reviewer feedback (12 items)
- Snapshot CliArgs by value, not reference, so in-place mutation by SDK consumers
is also caught by the routing guard.
- Validate name/connectionName/serviceKey is non-empty in all DI registration
overloads; previously null/whitespace produced invalid connection-string keys.
- Replace ghp_-prefixed placeholder in the sample with a clearly-fake token to
avoid tripping secret-scanning and to remove a real-token lookalike.
- Remove brittle 'PR #3' references from README and CHANGELOG; describe the
feature without tying to a specific PR thread.
- Update NuGet metadata and README links to point to bradygaster/squad (canonical
repo) instead of the tamirdresher fork.
- Multi-target the test project to match the package's target framework set so
CI exercises every framework the package ships against.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor(Squad.Agents.AI): adopt DelegatingAIAgent base + MAF ctor pattern (PR #1207 r2)
Addresses westey-m's review feedback:
- Extend Microsoft.Agents.AI.DelegatingAIAgent — drops ~70 lines of
manual Core* overrides; pass-through is provided by the base class.
- Adopt MAF constructor pattern: \(string squadFolderPath,
SquadAgentOptions? options = null, ILoggerFactory? loggerFactory = null)\.
Required settings on the constructor, options optional, ILoggerFactory
stays on ctor for DI injection.
- Routing-guard, IAsyncDisposable, and security posture preserved.
- Add Squad.Agents.AI.slnx solution for easy IDE open.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Reno (Copilot) <reno@clawpilotsquad.dev>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
Co-authored-by: tamirdresher <tamirdresher@users.noreply.github.com>
* ci(Squad.Agents.AI): switch NuGet publish to Trusted Publishing (OIDC) (#1240)
Replace the long-lived NUGET_API_KEY repo secret with NuGet/login@v1 OIDC
token exchange (1-hour API key) per the modern Trusted Publishing flow:
https://learn.microsoft.com/nuget/nuget-org/trusted-publishing
Why
---
- No long-lived credentials stored in the repo.
- Token is scoped to this workflow + repo + branch via the OIDC subject claim
and the Trusted Publishing policy registered on nuget.org.
- Eliminates the chicken-and-egg between needing admin to set NUGET_API_KEY
and needing the package live to validate the workflow.
What changed
------------
- Add `id-token: write` to the publish job (required for OIDC).
- Drop the `Verify NuGet API key` step.
- Drop the `--api-key ` reference to secrets.NUGET_API_KEY.
- Add `NuGet/login@v1` step that exchanges the OIDC token for a short-lived
API key, exposed via `steps.nuget-login.outputs.NUGET_API_KEY`.
- Add a fail-fast check for the new `vars.NUGET_USER` repository variable
(non-sensitive; the nuget.org profile name that performs the exchange).
- Update the file header documentation to reflect the new flow and link to
the Trusted Publishing setup page.
Required configuration before first publish
-------------------------------------------
1. Create the `Squad` organization on nuget.org and add owners.
2. Configure a Trusted Publishing policy at
https://www.nuget.org/account/trusted-publishing owned by the Squad org:
Repository Owner: bradygaster
Repository: squad
Workflow File: squad-agents-ai-release.yml
Environment: (empty)
3. Set repository variable NUGET_USER (Settings → Secrets and variables →
Actions → Variables) to a Squad-org-member's nuget.org profile name
(NOT email). Variable, not secret — the username is non-sensitive.
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(Squad.Agents.AI): expose SessionConfig + default OnPermissionRequest (#1252)
Squad.Agents.AI 0.1.0-preview.2 calls `CopilotClient.AsAIAgent(instructions, name)`
which leaves `SessionConfig.OnPermissionRequest` unset. The first call to
`SquadAgent.CreateSessionAsync()` therefore throws:
System.ArgumentException: An OnPermissionRequest handler is required when
creating a session. For example, to allow all permissions, use
CreateSessionAsync(new() { OnPermissionRequest = PermissionHandler.ApproveAll });
There was no public way for consumers to fix it from the outside —
`ConfigureCopilotClient` only exposes `CopilotClientOptions`, not the per-session
`SessionConfig` that owns the permission handler.
What changed
------------
- `SquadAgent` now switches to the `AsAIAgent(client, sessionConfig, ...)` overload
and constructs a `SessionConfig` with:
- `OnPermissionRequest = PermissionHandler.ApproveAll` (sensible default for
a host-process Squad adapter; the host already chose to instantiate Squad
and is responsible for sandboxing).
- `WorkingDirectory` defaulting to the resolved `Cwd` / `SquadFolderPath`.
- `SystemMessage = new SystemMessageConfig { Content = Instructions }` when
`SquadAgentOptions.Instructions` is set.
- `SquadAgentOptions.ConfigureSession: Action<SessionConfig>?` is new — runs
after Squad applies its defaults so a consumer can swap in a stricter
permission handler, pin the model, restrict tools, etc.
Tests
-----
- New `SquadAgentSessionConfigTests` (4 cases) exercising the new surface:
`ConfigureSession` is settable, runs against the live SessionConfig, can
replace the permission handler, and can set `AvailableTools`.
- All existing 43 tests still pass per TFM (141 total across net8/9/10).
Verified
--------
- The end-to-end consumer smoke test in
`C:\Users\tamirdresher\source\repos\squad-agents-ai-consume-test` previously
had to fall back to the raw SDK because of the missing handler. With this
change, `SquadAgent.CreateSessionAsync()` returns successfully and
`RunAsync` drives real 3-turn conversations against `.squad/`-init'd teams.
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Squad.Agents.AI 0.2.0: bump to MAF 1.10.0-rc1 / SDK 1.0.0 GA — file-IO works (#1259)
* Squad.Agents.AI: bump to MAF 1.10.0-rc1 / SDK 1.0.0 GA — file-IO now works
Microsoft.Agents.AI.GitHub.Copilot bumped from 1.7.0-preview to 1.10.0-rc1,
which transitively brings GitHub.Copilot.SDK 1.0.0 GA. SDK 1.0.0 reshaped
CopilotClientOptions and SessionConfig (the breaking namespace + property
renames are mirrored below), but in return Copilot CLI 1.0.61 talks to the
SDK over the new ACP-style protocol — meaning the agent's view/grep/powershell
tools finally work end-to-end without 'permission errors' or 'content
exclusion policy' hallucinations.
SDK 1.0.0 API migrations applied:
* Namespace GitHub.Copilot.SDK -> GitHub.Copilot.
* CopilotClientOptions.Cwd -> WorkingDirectory.
* CopilotClientOptions.CliPath + .CliArgs collapsed into a single
CopilotClientOptions.Connection (RuntimeConnection). We now build the
Connection via RuntimeConnection.ForStdio(CliPath, CliArgs) only when the
consumer supplied either a custom CLI path or extra CLI args; otherwise
the SDK's default child-process connection is used and the bundled
copilot.exe (downloaded by the SDK's build/ targets) is invoked.
* SessionConfig.ConfigDir -> SessionConfig.ConfigDirectory.
* PermissionRequestHandler is no longer a named delegate type; we use
type inference where the test code referenced it.
Public Squad.Agents.AI surface is intentionally unchanged: SquadAgentOptions
still exposes Cwd, CliPath, CliArgs, ConfigureSession, ConfigureCopilotClient.
We translate to the SDK 1.0.0 shape internally.
Routing gate (Picard Condition 1 / Worf SC-3) updated to snapshot and restore
WorkingDirectory + Connection instead of the old Cwd / CliPath / CliArgs trio.
A delegate that REPLACES Connection (e.g. via RuntimeConnection.ForStdio(...))
is reverted, mirroring the previous CliPath / CliArgs hijack tests.
Verified end-to-end against a real .squad-initialised team root: the
coordinator successfully reads .squad/team.md and enumerates every cast
member with their role. The dotnet test suite passes 46/46 across net8.0,
net9.0, and net10.0 (-1 vs baseline because the old CliPath and CliArgs
hijack tests collapsed into a single Connection hijack test, which is the
right granularity for SDK 1.0.0).
Version bumped 0.1.0-preview -> 0.2.0 to surface the MAF/SDK transitive bump.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address Copilot review feedback (#1259)
- Remove duplicate 'using GitHub.Copilot' in SquadAgentSessionConfigTests
(regex replace inadvertently doubled the directive when migrating from
the old GitHub.Copilot.SDK namespace).
- Drop hardcoded teamRoot override I left behind in the sample Program.cs
during the CLI-not-found debug session. The sample now correctly reads
SQUAD_TEAM_ROOT (or falls back to CWD) as documented.
- Fix mismatched comment in SquadAgent: the SDK-protocol section now
correctly references --allow-all (matching the flag actually injected
in CreateCopilotClient), not the narrower --allow-all-tools.
- Detect more existing permission-opening flags before injecting our
default --allow-all so a host that opts in via --allow-all-paths,
--allow-all-urls, or the omnibus --yolo no longer gets --allow-all
prepended on top. Comparison is now case-insensitive. Updated the
connection-string test that asserted the old over-eager behavior.
Also add .vs/, *.user, *.userprefs to .gitignore so VS solution junk
doesn't surface in git status.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address pre-existing Copilot review comments from PR #1212 (#1258)
- Workflow permissions: add issues: write to squad-pr-nudge, squad-impact,
and squad-repo-health workflows that call issues.createComment
- Logic bug: fix ahead_by → behind_by in pr-nudge stale branch check
- Logic bug: fix PR_LABELS fallback producing string instead of null
- Script fix: check legacy statuses for failure/error in checkCIStatus()
- Script fix: truncation row column count mismatch in pr-readiness.mjs
- Script fix: validate JSON.parse result is array before using as labels
- Script fix: isNodeBuiltin now validates node: prefix against known builtins
- YAML escaping: use JSON.stringify for skill descriptions in apm.yml
Closes #1213
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: expose memory tools via MCP + route writes through governed pipeline (#1251)
- Expose memory.classify, memory.write, memory.search, memory.promote,
memory.delete, memory.audit through the squad_state MCP server (#1244)
- Pin squad_state to user-level ~/.copilot/mcp-config.json during
init/upgrade for external `copilot -p` mode compatibility (#1247)
- Update squad.agent.md directive-capture and decision-recording
instructions to route through memory.write instead of raw
squad_state_write to the drop-box (#1246)
Closes #1244
Closes #1247
Closes #1246
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(skills): update path references from .squad/skills/ to .copilot/skills/ (#1260)
The CLI and SDK write skills to .copilot/skills/ by default, but docs
still referenced .squad/skills/. Update all documentation to use the
canonical .copilot/skills/ path and add a note about legacy fallback.
Closes #1241
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(init): prompt to add @copilot as a team member during squad init (#1262)
Add an interactive prompt during squad init that asks users if they
want to add @copilot (the GitHub Copilot coding agent) as an autonomous
team member. If accepted, adds the Coding Agent section to team.md and
copies copilot-instructions.md into the project.
Non-interactive mode skips silently with a hint to run
`squad copilot enable` later.
Closes #1147
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: align skills path + state-backend upgrade flow (#1241, #1226) (#1249)
- Update all docs references from .squad/skills/ to .copilot/skills/
- Note that both paths are scanned at read time but .copilot/skills/ is write default
- List all 6 git hooks (add pre-commit and post-commit to docs)
- Correct 'read-only reference' claim about migrated files
- Add recovery section for pre-commit hook refusal scenarios
Closes #1241
Closes #1226
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Squad.Agents.AI 0.3.0: first-class subagent observability (#1265)
Adds a typed OnSubagentTrace callback + OpenTelemetry ActivitySource so consumers
can see subagent dispatch (the coordinator's 'task' tool spawning specialist
sub-agents and their replies) without writing their own polymorphic dispatch over
the raw GitHub.Copilot SessionEvent hierarchy.
The previous 0.2.x surface required consumers to write the same boilerplate the
aspire-squad-resource demo had to (CopilotSessionTraceMapper.cs): switch on every
event subtype, unwrap subagent context, manage Activity lifetime. 0.3.0 makes that
the SDK's job.
New public surface:
* SquadAgentOptions.OnSubagentTrace (Action<SquadAgentTraceEvent>)
Set this to subscribe to subagent lifecycle (Selected / Started / Completed /
Failed), assistant messages from coordinator AND subagents, tool start/complete,
and SessionIdle. Setting OnSubagentTrace implicitly turns on
SessionConfig.IncludeSubAgentStreamingEvents so subagent replies actually flow
up to the parent session (otherwise they stay inside the subagent session and
never reach the callback).
* SquadAgentTraceEvent record — typed envelope (Kind, RawEventType, Timestamp,
SdkAgentId, SubagentName, SubagentDisplayName, ToolCallId, Content, Success,
RawEvent). Carries the original SessionEvent on RawEvent for advanced consumers
but exposes everything else through neutral primitive types so the callback
signature has no transitive dependency on GitHub.Copilot.SDK.
* SquadAgentTraceEventKind enum — categorises the SessionEvent into the well-known
cases that downstream observability surfaces want.
* SquadAgentDiagnostics.ActivitySourceName ('Microsoft.Agents.AI.Squad') +
SquadAgentDiagnostics.ActivitySource — one Activity per subagent dispatch is
opened on SubagentStartedEvent and disposed on the matching
SubagentCompletedEvent / SubagentFailedEvent, tagged with squad.subagent.name,
squad.subagent.display_name, squad.subagent.sdk_agent_id, and
squad.subagent.reply_preview (a short truncated copy of the subagent's
assistant message). Hosts that .AddSource(SquadAgentDiagnostics.ActivitySourceName)
on their OpenTelemetry tracer get these spans in their backend — the Aspire
dashboard renders them in the trace view automatically.
Internal:
* SquadSubagentTraceMapper — wires SessionEvent -> SquadAgentTraceEvent and the
Activity lifecycle. Held by SquadAgent and disposed during DisposeAsync to
drain any subagent activities that never received a matching Completed event
(e.g. session ended mid-dispatch).
* InternalsVisibleTo=Squad.Agents.AI.Tests so the mapper can be unit tested
directly without spinning a real CLI session.
Tests: 54/54 passing across net8, net9, net10 (+8 new tests for the
observability surface including Activity lifetime, tag propagation, mid-session
disposal, and consumer-callback exception isolation).
Verified end-to-end against the tamresearch1 Star Trek squad: the OnSubagentTrace
callback observed two parallel subagent dispatches (Picard, Data), captured each
of their replies attributed to the right SdkAgentId, and the matching
'squad.subagent Picard' / 'squad.subagent Data' OTel spans opened and closed
cleanly with the reply tagged on each span.
Version bumped 0.2.0 -> 0.3.0 to signal the new public surface.
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore(lockfile): sync package-lock with package.json after v0.10.0 bump (#1266)
After the v0.10.0 stable release on 2026-06-07, the root package-lock.json
still recorded packages/squad-cli@0.9.6-preview.15 and
packages/squad-sdk@0.9.6-preview.13 for the workspace entries. The
package.json files were updated correctly during the release, but the
lockfile workspace metadata was not regenerated.
This causes `npm ci --ignore-scripts` to fail in the
`sdk-exports-validation` CI job whenever package.json versions diverge
from these stale lockfile entries (which happens on every build that
runs `scripts/bump-build.mjs`). Confirmed on PR #1257.
This commit regenerates only the workspace version metadata
(`packages/squad-cli` and `packages/squad-sdk`) — no dependency
trees are touched. Verified locally on Windows + Node v23.5.0:
- `npm ci --ignore-scripts` at repo root: ✅ exit 0 (was already passing
on dev because root package.json/lockfile match; failure mode is the
workspace-entry mismatch surfacing under specific build conditions)
- `npm install --ignore-scripts`: no further drift produced
- Full `npm test` suite: 6535 passed / 134 failed / 60 skipped — the
134 failures are all pre-existing Windows file-locking flakiness
(EBUSY/ENOTEMPTY/hook-timeout); identical failure mode and similar
count on the immediate pre-merge commit cc37a2f7 (125 failures
pre-merge, 134 post-merge — diff is within flake noise).
- Targeted re-run of the 5 "newly failing" files in isolation: 93/93
passing — confirms the bulk-run failures are concurrency-induced
flakes, not regressions from the recent merges (#1251, #1258, #1260,
#1262, #1249).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(bump-build): also update package-lock workspace entries (#1267)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: release pipeline version pinning (#1203, #1204) (#1250)
* fix: release pipeline version pinning (#1203, #1204)
- Lower SDK dependency floor from >=0.10.0 to >=0.9.0 so the CLI tarball
resolves against the last published SDK when current version isn't yet
on the registry (Closes #1203)
- Add isLocalOrUnpublishedVersion guard so local dev builds and versions
with build metadata (+) fall back to @insider instead of writing
unresolvable version strings into MCP config (Closes #1204)
- Extend resolveSquadStateMcpSpec to short-circuit for build-metadata versions
- Add CI step to verify SDK dependency is resolvable before CLI publish
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: reset accidentally-bumped version 0.10.0-build.2 -> 0.10.0
Pre-publish version guard rejects -build.N suffixes (release pipeline
policy). The bump was made by an unintended local 'npm run build' run
before commit.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tamir Dresher <tamir.dresher@gmail.com>
Co-authored-by: Copilot <tamirdresher@users.noreply.github.com>
* chore: include CHANGELOG.md in published npm tarball (#1257)
* chore: include CHANGELOG.md in published npm tarball
Add CHANGELOG.md to the files array in both squad-cli and squad-sdk
package.json files so changelogs are included in published npm tarballs.
This enables offline what's-new prompts and removes the need for
GitHub API calls to show release notes.
Closes #1171
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: reset accidentally-bumped versions to 0.10.0
Pre-publish version guard rejects -build.N suffixes (release pipeline
policy). Both SDK and CLI package.json had -build.4 from a local
'npm run build' run before commit.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tamir Dresher <tamir.dresher@gmail.com>
Co-authored-by: Copilot <tamirdresher@users.noreply.github.com>
* Squad.Agents.AI 0.4.0: default-on subagent observability + Aspire-style connection-string lookup (#1271)
Two small but high-impact changes that remove ~30 lines of boilerplate from every
consumer (Aspire and otherwise) and make the OpenTelemetry story self-explanatory.
## 1. EmitSubagentActivities (default true) — telemetry independent of callback
Today, the per-subagent OpenTelemetry Activity emission is a side-effect of setting
`OnSubagentTrace`. A host that just wants `squad.subagent {Name}` spans in their
backend has to wire a callback they don't need. `Microsoft.Agents.AI.Squad` is
silent until then.
0.4.0 makes activity emission the default:
* New `SquadAgentOptions.EmitSubagentActivities` (defaults to `true`).
* `SquadAgent` installs `SquadSubagentTraceMapper` whenever
`EmitSubagentActivities || OnSubagentTrace != null`, so spans flow with zero
extra wiring.
* `OnSubagentTrace` becomes a pure customisation hook (logging, dashboards,
metrics) — independent of telemetry. Set `EmitSubagentActivities = false` to
opt out of built-in spans when you want to handle telemetry yourself.
Plus richer span shape: every lifecycle phase is now an
`ActivityEvent` on the live subagent span (visible as annotated markers on the
timeline in Aspire / Jaeger / etc.):
* `squad.subagent.start` — on SubagentStarted
* `squad.subagent.message` — on AssistantMessage (with message_preview tag)
* `squad.subagent.completed` — on SubagentCompleted
* `squad.subagent.failed` — on SubagentFailed
Net effect for a consumer:
builder.Services.AddOpenTelemetry()
.WithTracing(t => t.AddSource(SquadAgentDiagnostics.ActivitySourceName));
builder.Services.AddSquadAgent(o => o.SquadFolderPath = "/team");
…and the dashboard lights up. No callback wiring, no Activity.Current?.AddEvent
plumbing in the host.
## 2. Aspire-style connection-string lookup (with legacy fallback)
Aspire injects connection strings under the literal resource name —
e.g. an AppHost that calls `builder.AddSquad("research-squad", ...)` exposes
`ConnectionStrings:research-squad` to the consumer. The 0.3.0 SDK only looked at
`ConnectionStrings:squad-research-squad` (prefixed), so Aspire consumers had to
manually call `Configuration.GetConnectionString(name)`, parse the URI, and feed
`SquadFolderPath` into the configure callback themselves.
0.4.0 tries the literal name first and falls back to the legacy prefixed form:
| Style | Example | Lookup |
|--------------------------------------|---------------------------------------------|--------------------------------------------|
| Aspire-style direct (tried first) | `AddSquadAgent("research-squad")` | `ConnectionStrings:research-squad` |
| Legacy prefixed fallback | `AddSquadAgent("research")` | `ConnectionStrings:squad-research` |
Both work. Existing consumers using `ConnectionStrings:squad-{name}` continue
unchanged; new Aspire consumers get the natural one-line registration.
## Tests
54 → 64 tests, all passing on net8.0/9.0/10.0.
New `SquadAgentDefaultObservabilityTests` covers:
* Default-on activity emission (without consumer callback)
* Opt-out path (`EmitSubagentActivities = false`) — span suppression + callback
still fires
* Each ActivityEvent name (`start` / `message` / `completed` / `failed`)
* Connection-string precedence: Aspire-direct preferred, prefixed fallback used,
same rule applies for keyed registrations
Existing `SquadSubagentTraceTests` and the new class share an
`[Collection("SquadActivityListeners")]` so they run serially — process-global
`ActivityListener` state caused cross-test pollution otherwise.
## Files
* `src/Squad.Agents.AI/SquadAgentOptions.cs` — new `EmitSubagentActivities`
property + reworked `OnSubagentTrace` XML doc to clarify independence.
* `src/Squad.Agents.AI/SquadAgent.cs` — install trace mapper when telemetry OR
callback is requested.
* `src/Squad.Agents.AI/SquadSubagentTraceMapper.cs` — accept
`emitActivities` flag; gate `StartActivity`/`Dispose` on it; add
`ActivityEvent` annotations at every lifecycle boundary.
* `src/Squad.Agents.AI/SquadAgentOptionsConfigurator.cs` — accept a list of
candidate connection-string names; first non-empty wins.
* `src/Squad.Agents.AI/SquadServiceCollectionExtensions.cs` — new
`GetConnectionStringNames` returns `[name, "squad-"+name]` so both Aspire
and legacy conventions resolve.
* `src/Squad.Agents.AI/Squad.Agents.AI.csproj` — bump 0.3.0 → 0.4.0.
* `src/Squad.Agents.AI/README.md` — new "Subagent observability" section,
updated "Aspire / configuration path" section, two new option rows in the
Key Options table.
## Backward compatibility
Fully backward compatible. The two-arg `SquadSubagentTraceMapper` constructor
defaults `emitActivities` to `true`, `EmitSubagentActivities` defaults to
`true`, and the legacy `ConnectionStrings:squad-{name}` lookup still resolves.
Net change for an existing consumer that had OnSubagentTrace set: nothing (mapper
runs in both 0.3.0 and 0.4.0 because OnSubagentTrace is non-null). Net change
for a consumer that did NOT set OnSubagentTrace but did AddSource: they now get
spans they always asked for.
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Squad.Agents.AI 0.5.0: auto-inject --agent squad (CLI parity by default) (#1275)
* Squad.Agents.AI 0.5.0: auto-inject --agent squad (CLI parity by default)
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>
* amend: use SessionConfig.Agent instead of --agent CLI 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>
---------
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Squad.Agents.AI 0.5.1: fix 0.5.0 regression — use --agent CLI flag (not 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>
* Add squad registry add/list/remove for discovery-only peer squads (#1291)
Closes #1290.
Adds CLI surface for managing .squad/squad-registry.json, symmetric to
squad upstream. Registry entries are discovery-only (visible to
squad discover and squad delegate) but do NOT trigger inheritance of
the peer squad's skills/decisions/wisdom/routing.
Previously users had to hand-edit .squad/squad-registry.json — even the
squad discover empty-state hint told them to "create a squad-registry.json"
manually. This adds proper commands:
squad registry add <name> <path> # validates manifest, refuses duplicate
squad registry list # show all registered peers
squad registry remove <name> # remove by name
Also fixes a subtle path-semantics confusion: readManifest() now accepts
BOTH the repo root AND a path with a trailing .squad segment. The docs
and SKILL.md showed the .squad-suffixed form but the code previously
joined .squad/manifest.json onto whatever you gave it, so the suffixed
form silently failed empirical reproduction (discover returned nothing).
Test coverage: 18 new tests in cross-squad-registry.test.ts covering
the dual-path readManifest fix, registry round-trip, add/list/remove
behavior including duplicate-name and invalid-manifest rejection, and
end-to-end integration with discoverSquads.
Also updates:
- cross-squad SKILL.md (canonical + 2 template mirrors) to document the
registry vs upstream distinction explicitly
- squad discover empty-state hint to mention squad registry add
- squad help text with the new commands
- SDK exports for the new registry helpers + RegistryEntry/
AddRegistryEntryResult types
End-to-end verified locally:
- squad registry add (both repo-root and .squad-suffixed paths)
- squad registry list (rich output)
- squad registry remove (success + missing-name warning)
- squad discover picks up registry entries with source=registry
- 50/50 tests pass (32 existing + 18 new)
- npm run build clean
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(cli): stop writing squad_state to ~/.copilot/mcp-config.json on every init/upgrade (#1296) (#1298)
* fix(cli): stop writing squad_state to ~/.copilot/mcp-config.json on every init/upgrade (#1296)
squad init (init.ts:408) and squad upgrade (upgrade.ts:738) unconditionally
called ensureSquadStateMcpInUserConfig, writing squad_state_<hash> to
~/.copilot/mcp-config.json keyed by a stable project-path hash. Each new
squad init accumulated another entry in HOME with no garbage collection.
This contradicted the explicit iter-8 design intent documented at
packages/squad-cli/src/cli/core/mcp-root.ts:1-27, which says iter-8 stops
writing to HOME and writes squad_state ONLY to repo-root .mcp.json.
The repo-root .mcp.json writes (init.ts:403 / upgrade.ts:728) already cover
all documented Copilot CLI launch modes - copilot and copilot -p both walk
up from cwd to find .mcp.json. Out-of-tree copilot -p invocations should
use --additional-mcp-config @.mcp.json (already documented at init.ts:494).
Changes:
* Removed the unconditional ensureSquadStateMcpInUserConfig call from
init.ts:408 and upgrade.ts:738. Replaced both with comments explaining
iter-8 + #1296.
* Removed the now-unused import from both files.
* Kept the function definition at mcp-root.ts:178-228 - a future
squad doctor --mcp-prune cleanup helper may want to inspect HOME.
Tests:
* New regression test in test/cli/init.test.ts: "should NOT write any
squad_state entries to ~/.copilot/mcp-config.json (#1296)". Isolates
the developer's real HOME by setting USERPROFILE/HOME to a temp dir
before init, asserts no squad_state* keys appear under temp HOME.
* All 40 existing init tests still pass. npm run lint clean.
Closes #1296
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: comments reference actual Copilot CLI version (≥1.0.59), drop hardcoded test line numbers
Reviewer follow-ups on #1298 (closes #1296):
1. The comments in init.ts/upgrade.ts/mcp-root.ts referenced
'Copilot CLI 5.3+' as the version that auto-loads .mcp.json. The
real shipping CLI is at 1.0.62 (5.3 was a typo'd projection from
review). Updated all 5 occurrences to '≥1.0.59' — the lowest
version where the .mcp.json walk-up behavior is documented.
2. The init.test.ts regression comment hard-coded line numbers
(init.ts:408, upgrade.ts:738) that will go stale on any unrelated
edit to those files. Rewrote the comment to identify the call by
function name (ensureSquadStateMcpInUserConfig) instead — durable
against re-orderings.
Verified: regression: #1296 test still passes (1/16 in init.test).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(init): post-init package.json script tip includes --agent squad
The post-init tip showing how to add a non-interactive `squad:copilot`
script to package.json was:
"squad:copilot": "copilot --additional-mcp-config @.mcp.json"
This omits `--agent squad`, so users who copy/paste it get a generic
Copilot CLI session that doesn't load the Squad coordinator, team.md,
casting, or MCP-wired memory/state tools — only the additional MCP
config gets loaded. Same underlying issue surfaced in the cross-squad-
communication SKILL.md sweep (squad/wire-cross-squad-skill commit
6b0eac21): anywhere we spawn `copilot` into a Squad-initialised repo,
we must pass `--agent squad`.
Single-line fix:
"squad:copilot": "copilot --agent squad --additional-mcp-config @.mcp.json"
Verified: a fresh `squad init` smoke test now prints the corrected
tip line verbatim.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
* fix(sdk): wire team.md/routing.md/casting state on \squad preset apply\ (#1288) (#1293)
* fix(sdk): wire team.md/routing.md/casting state on `squad preset apply` (#1288)
`squad preset apply <name>` only copied agent charters into .squad/agents/.
It left .squad/team.md `## Members` empty, .squad/routing.md missing
`## Work Type -> Agent` rows, and never created .squad/casting/registry.json,
history.json, or policy.json. Net result: the coordinator's mode-switch check
saw an empty Members table and treated every session as Init Mode, proposing
to re-scaffold the team the user already applied.
This change adds a merge-friendly scaffold module
(packages/squad-sdk/src/presets/scaffold.ts) that runs after charters are
copied and:
* writes/updates team.md `## Members` (creates from scratch if missing;
appends new rows to an existing table while preserving the surrounding
Coordinator / Project Context sections; idempotent on repeat apply)
* writes/updates routing.md `## Work Type -> Agent` (creates or appends)
* writes/merges casting/registry.json (universe = `preset:<name>`)
* appends a snapshot to casting/history.json + a universe_usage_history entry
* creates casting/policy.json with defaults only if missing (never clobbers)
Agents with `status: 'error'` are excluded from wiring; agents with
`status: 'skipped'` (already exist in target) ARE wired so the team
reflects user intent. Scaffolder failure is reported as a synthetic error
result without masking the per-agent install results.
Verified:
* npm run lint passes
* test/presets.test.ts: 28/28 pass including 4 new regression tests
- wires preset agents into team.md ## Members (#1288)
- merges preset agents into an existing team.md without duplicating rows
- writes casting registry.json, history.json, and policy.json (#1288)
- appends routing rows for preset agents to routing.md (#1288)
Out of scope (tracked separately): deduplicating these writers with the
equivalent fresh-write versions in packages/squad-cli/src/cli/core/cast.ts.
Closes #1288
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(presets): role-aware Status cell + non-colliding sentinel for synthetic scaffold error
Two reviewer follow-ups on #1293 (closes #1288):
1. Members-table Status was hardcoded to '✅ Active' for every preset
agent. Presets that ship one of the always-on built-ins (Scribe,
Ralph, Rai, Fact Checker) would render with the wrong status label
compared to a fresh cast: '✅ Active' instead of '📋 Silent' /
'🔄 Monitor' / '🛡️ RAI' / '🔍 Verifier'.
Added a small statusForRole() helper that mirrors the role→status
mapping in cast.ts:652-655 (case-insensitive role matching to
tolerate preset authors who lowercase the role string). Built-in
role names get their canonical labels; everything else falls back
to '✅ Active'. Added a regression test asserting the labels for a
preset that ships scribe/ralph/rai/fact-checker + one regular agent.
2. Synthetic scaffold-failure result row used 'agent: presetName'
for its 'agent' field. If the preset itself happens to include an
agent literally named after the preset ('squad preset apply geektime'
on a preset whose roster has a 'geektime' agent), the consumer of
PresetApplyResult[] could not distinguish the synthetic scaffold-
level error from a real per-agent install error.
Replaced with the angle-bracketed sentinel '<scaffold>' (which
validateName() rejects, so it can never collide with a real agent
name) and moved the preset name into the human-readable reason
string so consumers don't lose that context.
Verified: 29/29 preset tests pass (28 existing + 1 new role-status test).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
* fix(docs): tell coordinator to roster Fact Checker on first-time cast (#1299) (#1300)
* fix(docs): tell coordinator to roster Fact Checker on first-time cast (#1299)
squad init correctly creates .squad/agents/fact-checker/ on disk (per
merged PR #1223). But when the user opens copilot --agent squad and the
coordinator runs first-time casting, it OMITS Fact Checker from the
team.md ## Members table while including Scribe, Ralph, and Rai.
Root cause: .squad-templates/squad.agent.md had two gaps:
1. Line 56 said "team size (typically 4-5 + Scribe)" — naming only Scribe
2. Rai had a dedicated ## Rai section with explicit "Rai always appears
in team.md" instruction — Fact Checker had no equivalent section
So the model added Rai (because instructed to) but had no instruction to
add Fact Checker, even though the agent dir was scaffolded on disk.
Fix:
* Update team-size line to name all 4 always-on built-ins: Scribe + Ralph
+ Rai + Fact Checker
* Add full ## Fact Checker — Verification & Devil's Advocate section
mirroring the Rai pattern: roster-entry instruction, dual operating
mode (per #789 + #1254), trigger phrase table, confidence ratings, DA
brief structure, boundaries, state location
Sync via sync-templates.mjs --sync propagates squad.agent.md changes to
all 4 mirror targets: .squad-templates/, templates/, packages/squad-cli/
templates/, packages/squad-sdk/templates/, .github/agents/.
Tests: new test/squad-agent-roster.test.ts runs against all 4 template
targets and asserts:
* The "Determine team size" line names all 4 built-ins
* A ## Fact Checker section exists with "always appears in team.md"
* The section declares dual operating mode (anchors #789 + #1254 design
so a future PR can't accidentally split Fact Checker and Devil's
Advocate again — cf. closed PR #1294)
* Existing Ralph + Rai sections still present
16/16 pass.
Closes #1299
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* doc(squad.agent.md): clarify Fact Checker is exempt from casting + correct on-demand reference path
Reviewer follow-ups on #1300:
1. Team-size phrasing — the line read 'typically 4-5 + Scribe + Ralph +
Rai + Fact Checker' which a model could parse as arithmetic
(4-5 + 4 = 8-9, but it could also collapse). Rewrote it to make the
composition explicit: '4-5 cast (user-domain) agents + 4 always-on
built-ins = 8-9 total roster entries'.
2. Cast-exemption parity — Scribe, Ralph, and Rai each have an
explicit 'exempt from casting' bullet but Fact Checker did not.
Added the matching bullet right after Rai's.
3. Bad on-demand reference path — the FC section pointed at
'.squad/templates/fact-checker-charter.md'. That file IS shipped
(TEMPLATE_MANIFEST destination 'templates/fact-checker-charter.md')
but only AFTER 'squad init' or 'squad upgrade' has populated
.squad/templates/. A reader of squad.agent.md on an
un-initialized repo (or in .github/agents/ on the cloud agent
surface) would follow a dead link. Repointed to the
'.squad/agents/fact-checker/charter.md' instance that
ensureBuiltinAgents creates as part of the same init/upgrade
path — that's where the rich charter actually lives at runtime
per #1299 + #1301.
All 4 mirrored copies re-synced via scripts/sync-templates.mjs.
fact-checker-role.test.ts: 8/8 pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
* fix(sdk): plumb Fact Checker like Rai — rich charter at init + .squad/fact-checker/ state dir (#1299 deep) (#1301)
* fix(sdk): plumb Fact Checker like Rai — rich charter at init + .squad/fact-checker/ state dir (#1299 deep)
PR #1300 fixed the documentation gap so the coordinator knows to roster
Fact Checker. This PR fixes the structural gap behind it. Per user
testing 2026-06-13: even after #1300 the actual agent on disk was still
"a name on disk with a 21-line placeholder".
Three structural problems:
1. squad init never used the rich {role}-charter.md templates. Both Rai
and fact-checker got 478-byte generic stubs from generateCharter().
Rich templates only ran via squad upgrade's ensureBuiltinAgents path.
2. fact-checker had no state dir. Rai gets .squad/rai/{policy.md,
audit-trail.md} via init.ts lines 879-941. fact-checker had nothing
equivalent.
3. fact-checker-charter.md was only in packages/squad-cli/templates/ —
missing from .squad-templates/ (canonical source) AND packages/
squad-sdk/templates/. SDK init's getSDKTemplatesDir() resolves to the
SDK templates dir, so even if init tried to read the rich charter,
the file wasn't there.
Fix (4 parts):
Part 1 - Rich charter at init (benefits BOTH Rai and fact-checker):
* SDK init.ts agent loop now looks up {templatesDir}/{role}-charter.md
for each agent and uses that as charter.md content if it exists. Falls
back to generateCharter() for user-defined agents.
* Result: fresh squad init produces .squad/agents/Rai/charter.md at
4525 bytes (full Rai charter) and fact-checker/charter.md at 3024
bytes (full FC charter). Previously both were 478-byte stubs.
Part 2 - .squad/fact-checker/ state dir mirroring .squad/rai/:
* New block in init.ts (right after the Rai seeding) creates
.squad/fact-checker/policy.md (from templates/fact-checker-policy.md
or inline fallback) and audit-trail.md.
* New .squad-templates/fact-checker-policy.md (~6KB) is the canonical
authority for dual-mode operating rules per #789 + #1254:
- Mode 1 Verification: ✅/⚠️/❌/🔍 confidence rating taxonomy
- Mode 2 Devil's Advocate: required brief structure
- Hard anti-fabrication rules
- Advisory by default with narrow blocking exceptions
- Audit trail rules (succinct, never raw source)
Part 3 - Fix .squad-templates/ distribution gap:
* Copied fact-checker-charter.md into .squad-templates/ so
sync-templates.mjs propagates it to all 4 mirror targets including
packages/squad-sdk/templates/. This unblocks Part 1.
Part 4 - Plumbing:
* .gitattributes: .squad/fact-checker/audit-trail.md merge=union
* TEMPLATE_MANIFEST: fact-checker-policy.md
* squad.agent.md Files Catalog: 2 new rows for FC state files
Tests: 3 new regression tests in test/init.test.ts (28/28 pass total).
npm run lint clean.
Composability: This PR builds on #1300 (which adds the ## Fact Checker
section to squad.agent.md and the team-size line fix). Both PRs modify
squad.agent.md in disjoint regions and merge in either order. Full
plumbing requires BOTH to land.
Closes #1299 (deep fix; #1300 was the surface fix)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(sdk): lowercase fallback for rich-charter template lookup + sync .github/agents
Two reviewer follow-ups on #1301 (#1299 deep):
1. Case-sensitive FS bug in rich-charter lookup
The lookup tried '\-charter.md' and '\-charter.md'
only. For Rai (role='Rai', name='Rai') this becomes 'Rai-charter.md', but
the actual file shipped lowercase ('rai-charter.md'). On Windows the lookup
succeeded because the filesystem is case-insensitive; on Linux CI it silently
missed and fell back to the 478-byte generic stub — exactly the regression
#1299 was trying to fix. Reproduced by 'should use the rich Rai-charter.md
template at init' failing with 'expected 476 to be greater than 1000' on
GitHub Actions.
Add toLowerCase() candidates after the exact-case ones. De-dupe via a Set
so we don't double-stat when role and name are already lowercase
(fact-checker case). Guard each candidate against blank keys.
2. Template-sync parity
The canonical .squad-templates/squad.agent.md gained two Fact Checker rows
in the Files Catalog but the mirrored .github/agents/squad.agent.md copy
was never re-synced, so the template-sync.test.ts byte-for-byte parity
check would have fired. Run 'node scripts/sync-templates.mjs --sync' to
regenerate.
Verified: vitest 'rich Rai-charter' passes locally after the fix
(previously failing on Linux CI run 27464079078).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
* fix(skills): rename squad disambiguation skill to squad-help (supersedes #1297) (#1302)
* fix(skills): rename disambiguation skill 'squad' -> 'squad-help' (supersedes #1297)
PR #1297 added a disambiguation skill named 'squad' so models calling
skill(Squad) would get a redirect. After local end-to-end testing on
2026-06-13: skill ships to disk correctly but never shows up in Copilot
CLI's /skills list.
Root cause (verified against Copilot CLI source 1.0.62-2 app.js):
1. Copilot CLI's skill schema is {name, description, source, baseDir,
allowedTools, pluginName, pluginVersion} (line 989). Frontmatter fields
triggers:, domain:, confidence:, license: are silently ignored.
2. Skill loader returns {skills, warnings, errors} (line 4427). Skills
that fail to load are reported as errors.
3. A skill named 'squad' collides with the Copilot agent named 'Squad'
(registered at .github/agents/squad.agent.md). The agent wins; the
skill is hidden from /skills.
Fix:
* Rename 'squad' -> 'squad-help' (avoids the agent-name collision; still
descriptive enough for natural-language match when user says 'how do
I use squad' or 'squad help')
* SKILL.md content: name: 'squad-help', removed unused triggers:/domain:/
confidence:/source:/license: fields, added allowedTools: [], rewrote
description: to be self-explanatory, added explicit note that /squad
slash command does NOT exist (slash commands are CLI built-ins, not
auto-mapped from skills)
* MANIFEST_SKILL_NAMES in sdk-init.ts: 'squad' -> 'squad-help'
* New TEMPLATE_MANIFEST entry in templates.ts for squad-help (so
squad upgrade also propagates the skill - that code path uses
TEMPLATE_MANIFEST instead of MANIFEST_SKILL_NAMES)
Tests: new test asserts .copilot/skills/squad-help/SKILL.md exists with
right frontmatter; explicit regression guard against re-introducing
name: 'squad'. 26/26 init tests pass. npm run lint clean.
Supersedes #1297.
Out of scope (separate issue worth filing): squad upgrade synced only
10 of 16 installed skills - TEMPLATE_MANIFEST (used by upgrade) is out
of sync with MANIFEST_SKILL_NAMES (used by init). Skills from PRs #1292
+ #1295 (tiered-memory, iterative-retrieval, reflect, cross-squad,
cross-squad-communication) have entries in MANIFEST_SKILL_NAMES but
not TEMPLATE_MANIFEST. Follow-up will fix.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(skill): add confidence + domain frontmatter to squad-help
Reviewer follow-up on #1302: the isSkillContent() classifier in
sharing/consult.ts:990 requires BOTH name: AND confidence: in the
frontmatter to recognize a file as a skill. Without confidence:,
squad-help would not be detected as a skill in cross-squad merge /
share / promote flows — it would be misclassified as a generic
markdown decision.
The Copilot CLI itself silently ignores custom frontmatter fields
(per sdk/index.js decompile — only name/description/allowedTools/
user-invocable are read), so adding confidence: high and domain:
squad-onboarding is safe at the CLI surface and necessary at the
SDK surface.
Applied identically to all 3 mirrored copies of squad-help/SKILL.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
* fix(prompt): co…
Summary
Adds a first-class subagent observability surface to
Squad.Agents.AIso consumers can see the coordinator'stask-tool dispatch (when the coordinator spawns specialist sub-agents and synthesises their replies) without writing their own polymorphic dispatch over the rawGitHub.Copilot.SessionEventhierarchy.The previous 0.2.x required exactly the boilerplate the aspire-squad-resource demo had to ship: switch on every event subtype, unwrap subagent context, manage
Activitylifetime. 0.3.0 makes that the SDK's job.Bumps Squad.Agents.AI to
0.3.0.New public surface
SquadAgentOptions.OnSubagentTraceSessionConfig.IncludeSubAgentStreamingEventsso subagent replies flow up to the parent session.SquadAgentTraceEventrecordKind,RawEventType,Timestamp,SdkAgentId,SubagentName,SubagentDisplayName,ToolCallId,Content,Success,RawEvent. The raw SDK event is preserved onRawEventfor advanced consumers but everything else is plain primitives.SquadAgentTraceEventKindenumSubagentSelected/Started/Completed/Failed,AssistantMessage,ToolStart/Complete,SessionIdle.SquadAgentDiagnostics.ActivitySourceName("Microsoft.Agents.AI.Squad").AddSource(...)on their OpenTelemetry tracer. OneActivityper subagent dispatch is opened onSubagentStartedEvent, tagged withsquad.subagent.name,squad.subagent.display_name,squad.subagent.sdk_agent_id,squad.subagent.reply_preview, and closed on the matching completion event.Internal
SquadSubagentTraceMapper(internal,InternalsVisibleTo=Squad.Agents.AI.Tests) — wiresSessionEvent->SquadAgentTraceEventand theActivitylifecycle. Held bySquadAgentand disposed inDisposeAsyncto drain subagent activities that never received a matching completed event (e.g. session ended mid-dispatch).Verification
54/54 tests passing on net8.0/net9.0/net10.0 (+8 new tests covering the observability surface: typed envelope mapping, Activity lifetime, tag propagation, mid-session disposal, callback exception isolation, unknown-event filtering).
End-to-end against the
tamresearch1Star Trek squad, asking the coordinator to dispatch two parallel subagents via thetasktool. Captured trace shows both the typed callback AND the OTel spans firing on real subagent dispatch:Release plan
Merging to
devtriggerssquad-agents-ai-release.yml. Dev branch produces0.3.0-preview.{run_number}.