Skip to content

Fix Copilot SDK model routing and cross-family sub-agents - #67509

Merged
pelikhan merged 11 commits into
mainfrom
copilot/fix-copilot-sdk-issues
Oct 10, 2026
Merged

pelikhan merged 11 commits into
mainfrom
copilot/fix-copilot-sdk-issues

Conversation

Copilot AI commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Routed SDK sessions passed bare models, bypassing BYOK inference under org billing. Declared sub-agents were ignored, and one provider-wide wire API prevented GPT and Claude agents from sharing a session. - Model routing - Split Copilot providers into Responses and Chat Completions routes using /reflect endpoint capabilities. - Qualify routed, fallback, reflected, and sub-agent models; preserve explicit API selections. - Leave non-Copilot endpoints and CLI mode unchanged. - Declared agents - Load inline and repository agent files as SDK customAgents, preserving name, description, prompt, and tools. - Qualify concrete models; aliases inherit the session model. Unknown agent models warn before inheriting. - Observability - Capture subagent.selected and model.call_final_result, including BYOK status. - Normalize qualified model identities for audit spend attribution. - Require smoke evidence of completed delegation on the expected model and proxy endpoint, rather than model-name replies. Example routing within one session: text Main: copilot-responses/gpt-5.6-luna → /responses Sub-agent: copilot-completions/claude-haiku-4.5 → /chat/completions - Fixes #67507

ADR: docs/adr/67509-split-copilot-sdk-providers-by-wire-api.md

@SivaKesava1

Copy link
Copy Markdown
Collaborator

@copilot Scope for this PR (#67507):

We'll test the PR in our sandbox with gh-aw's stock SDK driver (no engine.driver) and post the results here.

Copilot AI commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Scope for this PR (#67507):

Understood. I’ll keep changes limited to the Copilot SDK mode fix and leave CLI wire API selection out of scope. I’ll only address checks that fail because of this PR after comparing them with main; I’ll await the stock SDK driver results before making any further changes.

Copilot AI and others added 2 commits October 10, 2026 20:42
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix routing issues in Copilot SDK mode Fix Copilot SDK model routing and cross-family sub-agents Oct 10, 2026
@SivaKesava1
SivaKesava1 marked this pull request as ready for review October 10, 2026 20:56
Copilot AI balanced review requested due to automatic review settings October 10, 2026 20:56
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Lean already. Ship.

Generated by Ponytail Reviewer for #67509

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Relative engine paths are misresolved, and smoke validation rejects valid token-usage records lacking a status field.

3 open findings
What changed in this PR

Routes Copilot SDK sessions and sub-agents through provider-qualified BYOK endpoints while improving attribution and delegation evidence.

Changes:

  • Splits Copilot Responses and Chat Completions routing.
  • Loads declared custom agents with qualified models.
  • Captures model-selection evidence and strengthens smoke validation.

Security review found no permission or supply-chain regression; scanners were not run in this environment.

File Description
pkg/​cli/​token_usage_subagent_session_test.go Tests cross-family attribution.
pkg/​cli/​testdata/​subagent_attribution/​copilot-qualified-cross-family/​aw_session.jsonl Adds attribution fixture.
pkg/​cli/​model_identity.go Normalizes qualified model identities.
pkg/​cli/​model_identity_test.go Tests identity normalization.
docs/​src/​content/​docs/​reference/​engines.md Documents SDK routing and agents.
actions/​setup/​js/​unified_session_payload.test.cjs Tests final-result persistence.
actions/​setup/​js/​unified_session_payload.cjs Scopes final model events.
actions/​setup/​js/​smoke_copilot_sub_agents.test.cjs Tests smoke evidence checks.
actions/​setup/​js/​extract_inline_sub_agents.test.cjs Tests custom-agent loading.
actions/​setup/​js/​extract_inline_sub_agents.cjs Loads repository and inline agents.
actions/​setup/​js/​copilot_workflow_events.test.cjs Tests selection-event projection.
actions/​setup/​js/​copilot_workflow_events.cjs Adds sub-agent selection fields.
actions/​setup/​js/​copilot_sdk_session.test.cjs Tests SDK session qualification.
actions/​setup/​js/​copilot_sdk_session.cjs Configures models and custom agents.
actions/​setup/​js/​copilot_sdk_multi_provider.test.cjs Tests provider qualification.
actions/​setup/​js/​copilot_sdk_multi_provider.cjs Resolves qualified model IDs.
actions/​setup/​js/​copilot_sdk_driver.test.cjs Tests driver routing and events.
actions/​setup/​js/​copilot_sdk_driver.cjs Passes qualified SDK configuration.
actions/​setup/​js/​copilot_harness.test.cjs Tests routed child environment.
actions/​setup/​js/​copilot_harness.cjs Builds split-provider environment.
actions/​setup/​js/​awf_reflect.test.cjs Tests endpoint-aware reflection.
actions/​setup/​js/​awf_reflect.cjs Splits Copilot providers by API.
.github/​workflows/​smoke-copilot-sub-agents.md Adds delegation evidence assertions.
.github/​workflows/​smoke-copilot-sub-agents.lock.yml Regenerates compiled workflow.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/smoke-copilot-sub-agents.md
Comment thread actions/setup/js/copilot_sdk_driver.cjs Outdated
Comment thread actions/setup/js/copilot_sdk_session.cjs Outdated
@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (163 new lines in business-logic directories, >100 threshold) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/67509-split-copilot-sdk-providers-by-wire-api.md — review and complete it before merging.

🔒 This PR cannot merge until an ADR is linked in the PR body.

📋 Evidence used
  • has_implementation_label: false; default_business_additions: 163 (threshold 100) → enforcement required by code volume
  • No .design-gate.yml present — default business directories used (pkg/, actions/ logic under test, etc.)
  • PR body contains no ADR link or ADR section; linked issue reference is Fixes #67507 (no ADR content required from it for this gate)
  • docs/adr/ contains no file for PR 67509 (latest existing: 67425-prefer-compatible-local-actionlint-before-docker.md)
  • Decision inferred from actions/setup/js/awf_reflect.cjs (copilot-${wireApi} provider split, inferWireApiForModel), copilot_sdk_multi_provider.cjs, copilot_sdk_session.cjs, and pkg/cli/model_identity.go (copilotSDKModelProvider normalization)
📋 What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI couldn't infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-67509: Split Copilot SDK Providers by Wire API and Qualify Every Routed Model

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · opus50 · 47.1 AIC · ⌖ 50.8 AIC · ⊞ 1.8K · ◷
Comment /review to run again

@SivaKesava1

Copy link
Copy Markdown
Collaborator

@copilot Tested c60e1f3 in our sandbox with gh-aw's stock SDK driver (no engine.driver), AWF v0.28.50, Copilot CLI 1.0.90 sidecar, SDK 1.0.16, org billing (copilot-requests: write, no Copilot token).

Case Result
Routed gpt-5.6-luna main, declared file-summarizer on claude-haiku-4.5 ✅ Session created with model="copilot-responses/gpt-5.6-luna" providers=2; sub-agent runs on copilot-completions/claude-haiku-4.5; AWF token-usage.jsonl: Haiku /chat/completions 200 ×2, Luna /responses 200; all jobs green
Reverse: model: claude-sonnet-5 (not routed), file-summarizer on gpt-5.4-mini ✅ Session copilot-completions/claude-sonnet-5; sub-agent copilot-responses/gpt-5.4-mini; AWF: Sonnet /chat/completions 200 ×3, gpt-5.4-mini /responses 200 ×2; all jobs green

The same routed workflow on main (2b5c6d4b8f) still fails with No GitHub OAuth token or Copilot HMAC key provided, so the core fix works.

Two attribution issues in gh aw audit for SDK runs:

  1. The sub-agent row uses the per-call display name, not the agent name. The agent_usage table lists subagent readme-summarizer in one run and subagent summarize-readme in the other. Those are the name values the main agent chose for that task call. The events carry agentName: file-summarizer, agentDisplayName: readme-summarizer. In CLI mode the same workflow shows subagent file-summarizer. The customAgents built from .agent.md files don't set displayName. Setting it to the agent's name, keying audit rows on agentName, or both, would make sub-agent spend group by the declared agent across runs.
  2. Completions are double-counted. Each SDK sub-agent invocation produces two subagent.completed events, the second with cancelled: true, so audit shows instances 1, outcome 2/0/0. The duplicate should count as the same invocation.

Please keep these within this PR's scope (SDK mode and its attribution). The PR is still marked [WIP]; mark it ready when you're done and we'll re-run both cases.

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

Peek at saved memory (pr-code-quality-reviewer)
reviewed_at: 2026-10-10T21:02:10.918Z
review_event: REQUEST_CHANGES
top_themes:
  - unsupported Copilot wire-api overrides
  - fatal parsing of SDK server args
  - ambiguous duplicate model qualification
files_reviewed:
  - actions/setup/js/awf_reflect.cjs
  - actions/setup/js/copilot_harness.cjs
  - actions/setup/js/copilot_sdk_driver.cjs
  - actions/setup/js/copilot_sdk_multi_provider.cjs
  - actions/setup/js/copilot_sdk_session.cjs
  - actions/setup/js/extract_inline_sub_agents.cjs
  - actions/setup/js/copilot_workflow_events.cjs
  - actions/setup/js/unified_session_payload.cjs
  - pkg/cli/model_identity.go
comment_count: 3

Note

This comment is managed by comment memory.

Expand the saved memory block to view or edit the persistent context for this thread.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 80.2 AIC · ⌖ 7.49 AIC · ⊞ 19.6K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict

Requesting changes: the new routing path still has blocking edge cases that can send a model to an unsupported wire API, abort SDK runs on malformed sidecar args, and silently pick the wrong provider when model ids are duplicated.

### Blocking themes
  • unsupported COPILOT_PROVIDER_WIRE_API overrides can bypass reflect capability data and reproduce 400s at runtime
  • GH_AW_COPILOT_SDK_SERVER_ARGS is now parsed as fatal in the SDK path even though the harness treats it as recoverable
  • multi-provider qualification still picks the first duplicate model id instead of forcing an explicit provider

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 80.2 AIC · ⌖ 7.49 AIC · ⊞ 19.6K
Comment /review to run again

Comment thread actions/setup/js/awf_reflect.cjs Outdated
Comment thread actions/setup/js/copilot_sdk_session.cjs Outdated
Comment thread actions/setup/js/copilot_sdk_multi_provider.cjs Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /codebase-design to the model-routing and cross-family sub-agent logic. No blocking issues; left two observations.

📋 Key Themes & Highlights

Key Themes

  • Inconsistent failure mode for unqualifiable models: session-level model qualification now throws (copilot_sdk_session.cjs, copilot_sdk_driver.cjs, copilot_harness.cjs), but sub-agent-level qualification only logs a warning and silently falls back to the session model. This re-introduces a softer version of the "declared sub-agents were ignored" bug the PR title calls out.
  • Duplicated Copilot-provider-name matching: the Copilot-family provider-name pattern is independently encoded in awf_reflect.cjs (list literal), copilot_sdk_multi_provider.cjs (regex), and pkg/cli/model_identity.go (regex). Low risk today, but a future wire-API addition needs synchronized edits in three places.

Positive Highlights

  • ✅ Excellent test coverage: new unit tests for qualifyModelForMultiProvider, resolveMultiProviderFromReflect's responses/completions split, cross-family custom-agent qualification in copilot_sdk_driver.test.cjs, and Go-side normalizeModelProvider/attribution merging all pass (go test ./pkg/cli/... verified green).
  • ✅ The smoke workflow's new post-step assertions require real evidence (proxy request logs with model/endpoint/status) rather than trusting model-name replies — a solid anti-flakiness design per /diagnosing-bugs principles.
  • ✅ Backward-compatible model-string handling (copilot/<model> legacy prefix, bare model ids, fully-qualified provider/model) is deliberately preserved and tested.

@copilot please address the review comments above.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 98.4 AIC · ⌖ 13.7 AIC · ⊞ 10.3K
Comment /matt to run again

Comment thread actions/setup/js/copilot_sdk_session.cjs
Comment thread actions/setup/js/copilot_sdk_multi_provider.cjs
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

Copilot AI commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Tested c60e1f3 in our sandbox with gh-aw's stock SDK driver (no engine.driver), AWF v0.28.50, Copilot CLI 1.0.90 sidecar, SDK 1.0.16, org billing (copilot-requests: write, no Copilot to...

Fixed in eef25f9: SDK custom agents use their declared names for display and audit attribution; cancelled cleanup completions no longer count an already-completed or failed invocation again. CLI behavior is unchanged.

All 111 focused SDK tests, targeted Go attribution tests, and the final lint/impacted-test gate passed. Secret scanning and a separate read-only review passed. Automated review was unavailable, and CodeQL skipped analysis due to database size.

PR #67509 is already non-draft and ready for your sandbox rerun.

@SivaKesava1

Copy link
Copy Markdown
Collaborator

@copilot /pr-finisher

Re-tested eef25f9 in our sandbox with gh-aw's stock SDK driver (AWF v0.28.50, CLI 1.0.90 sidecar, SDK 1.0.16, org billing). Both directions pass with all jobs green, and the audit fixes work:

  • Routed gpt-5.6-luna main with file-summarizer on claude-haiku-4.5: AWF token-usage.jsonl shows Haiku /chat/completions 200 ×2 and Luna /responses 200 ×4. Audit shows subagent file-summarizer | 1 | 1/0/0 | 2.
  • claude-sonnet-5 main with file-summarizer on gpt-5.4-mini: Sonnet /chat/completions 200 ×3, gpt-5.4-mini /responses 200 ×2. Audit shows subagent file-summarizer | 1 | 1/0/0 | 2.

Before finishing, please handle the eight open review threads. Reply on each thread with what you changed (we'll resolve them). Our decision per thread:

  1. Blocking: engine.cwd resolved twice (copilot_sdk_driver.cjs:142 and copilot_sdk_session.cjs:165, same root cause). Resolve a relative GH_AW_ENGINE_CWD once, against GITHUB_WORKSPACE, and pass that absolute path as the SDK workingDirectory. Test: workspace /w, engine.cwd: packages/app, driver process started in /w/packages/app. The SDK must get /w/packages/app (not /w/packages/app/packages/app), and repository agents under it are found.
  2. Blocking: explicit wire API override can force an unsupported endpoint (awf_reflect.cjs:1030). In the split-provider setup, each model's provider must come from its /reflect supported_endpoints. If COPILOT_PROVIDER_WIRE_API names an endpoint the session model doesn't support, fail at startup before the sidecar starts, with a message naming the model, the override and the supported endpoints (the same wording as Select Copilot wire APIs from AWF metadata and surface model mismatches #67487's Model endpoint mismatch: …). Don't send the first request and get a 400. Test: gpt-5.6-luna (supports /responses only) with COPILOT_PROVIDER_WIRE_API=completions must fail at startup with that message.
  3. Blocking: malformed GH_AW_COPILOT_SDK_SERVER_ARGS is now fatal (copilot_sdk_session.cjs:100). Keep it best-effort like the harness: log a warning and fall back to the defaults. Test: malformed JSON and a non-array value both start a session and log the warning.
  4. Do, but don't fail the run: unknown sub-agent model (copilot_sdk_session.cjs:367). A concrete model that the org's catalog doesn't offer shouldn't break workflows that run today. Instead of only a log line, write a structured event to usage/aw_session.jsonl (for example subagent.model_unavailable with the agent name, the declared model and the model used), and show it as an audit finding, so the fallback is no longer silent. Aliases such as small keep inheriting quietly. Test: an agent with model: not-a-model produces the event and the audit finding.
  5. Do: ambiguous model qualification (copilot_sdk_multi_provider.cjs:76). When a bare model id exists under more than one provider, don't pick one. Report an error asking for an explicit <provider>/<model>. Test with the same id under two providers.
  6. Fix or explain: smoke status check (smoke-copilot-sub-agents.md:116). Real AWF v0.28.50 token-usage.jsonl records do include status (200 in all our runs), but the new copilot-qualified-cross-family fixture leaves it out. Either add status to the fixture so it matches real records, or treat a missing status as unknown rather than failed. Don't let the check fail on fixture-shaped data.
  7. Follow-up, optional: provider-name regex duplication (copilot_sdk_multi_provider.cjs:71). Reuse the existing Copilot provider-name helper if that's a small change. Otherwise reply on the thread and leave it.

Also:

Copilot AI and others added 2 commits October 10, 2026 21:50
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

Copilot AI commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot /pr-finisher

Re-tested eef25f9 in our sandbox with gh-aw's stock SDK driver (AWF v0.28.50, CLI 1.0.90 sidecar, SDK 1.0.16, org billing). Both directions pass with all jobs green, and the a...

Addressed all eight threads in 5d71db9 / 369ae80 and replied individually; left resolution to you. SDK-only fixes cover cwd anchoring, startup endpoint mismatch, best-effort args with headless defaults, visible unavailable-model fallback, ambiguity errors, shared provider-name helper, and fixture status. Also corrected the SDK display-name assertion causing this PR’s JS failure; CLI wire selection is unchanged.

647 focused JS tests, focused Go audit tests, final validation gate, secret scan, and separate read-only review passed. Automated review was unavailable; CodeQL reported no alerts with Go analysis skipped as unchanged.

ADR is Proposed with gh-aw maintainers as deciders and records decisions 2/4. Remaining blocker: the progress tool does not edit the PR body, and no authorized PR-body editor is available here. Please add this link: https://github.com/github/gh-aw/blob/copilot/fix-copilot-sdk-issues/docs/adr/67509-split-copilot-sdk-providers-by-wire-api.md

PR is already non-draft. Doc Build - Deploy remains a known main failure: the same drive-memory → cache-memory broken link appears in main run 38072203191 and PR run 38087467140; left unchanged. Final CI on the new HEAD is unverified. Human thread resolution/approval and the ADR body link remain before merge.

@SivaKesava1

Copy link
Copy Markdown
Collaborator

Verified 369ae80 for review:

  • Sandbox, gh-aw's stock SDK driver (AWF v0.28.50, CLI 1.0.90 sidecar, SDK 1.0.16, org billing, no Copilot token), all jobs green:
    • routed gpt-5.6-luna main with file-summarizer on claude-haiku-4.5: AWF logs Haiku /chat/completions 200 ×2 and Luna /responses 200 ×4; audit subagent file-summarizer | 1 | 1/0/0.
    • claude-sonnet-5 main with file-summarizer on gpt-5.4-mini: Sonnet /chat/completions 200 ×3, gpt-5.4-mini /responses 200 ×2; audit subagent file-summarizer | 1 | 1/0/0.
    • The same routed workflow on main (2b5c6d4b8f) fails with No GitHub OAuth token or Copilot HMAC key provided.
  • Tests (Linux): 650 JS tests across the 10 touched suites and the Go audit/attribution tests pass.
  • Review: all eight review threads were addressed with tests, and we resolved them. The ADR is completed (Proposed) and linked in the PR body. The remaining CHANGES_REQUESTED is the code-quality bot's review from before those fixes.

Scope: SDK mode only (engine.copilot-sdk: true). CLI mode still uses one wire API per session (github/copilot-cli#5103); #67485/#67487 adds the CLI-mode warning and fail-fast.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Copilot SDK mode: routed model passed unqualified, declared sub-agents never loaded, and one wire API shared by every Copilot model

4 participants