fix(ai-credits): allow Copilot auto model through the AI-credits pre-flight guard - #6811
Conversation
The checkUnknownModelRejection guard was rejecting requests with model: 'auto' for the Copilot provider when maxAiCredits was active and no defaultAiCreditsPricing was configured. 'auto' is a Copilot-specific sentinel that the Copilot API resolves to a concrete priced model at runtime. AI credits are tracked correctly once the response returns the resolved model name. Blocking 'auto' pre-flight breaks isolated agent runtimes (gVisor, docker-sbx) where /reflect is unreachable and the harness cannot substitute the model alias before sending the request. Non-Copilot providers are unaffected: 'auto' remains rejected for them since it is not a recognized sentinel outside the Copilot API. Closes #6810
auto model through the AI-credits pre-flight guard
Keep unknown-model AI credit checks fail-closed while making the gh-aw /reflect endpoint reachable from gVisor chroots and sbx microVMs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 044e97c9-67c4-42bb-a7b9-7d7cb4255c1c
There was a problem hiding this comment.
Pull request overview
This PR attempts to restore API-proxy reflection access in isolated gVisor and sbx runtimes.
Changes:
- Injects static internal-service host mappings for gVisor chroots.
- Adds an sbx
api-proxyalias and fail-closed/reflectpreflight. - Adds coverage for the new runtime behavior.
Show a summary per file
| File | Description |
|---|---|
src/services/agent-volumes/volume-builder.ts |
Passes internal host mappings to chroot generation. |
src/services/agent-volumes/hosts-file.ts |
Adds internal services to generated hosts files. |
src/services/agent-volumes-chroot-hosts.test.ts |
Tests gVisor API-proxy mapping. |
src/sbx-manager.ts |
Adds sbx reflection preflight. |
src/sbx-manager.test.ts |
Tests the preflight command and failure. |
src/compose-generator.ts |
Supplies static runtime host mappings. |
src/commands/main-action.ts |
Runs the sbx preflight before the agent. |
src/commands/main-action.test.ts |
Verifies preflight invocation. |
src/commands/main-action.test-utils.ts |
Adds preflight mock support. |
src/commands/main-action-coverage-gaps.test.ts |
Tests fail-closed startup behavior. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 10/10 changed files
- Comments generated: 3
- Review effort level: Balanced
| // gh-aw fetches reflection data from the fixed api-proxy hostname. The | ||
| // microVM reaches the sidecar through its published host ports, so install | ||
| // that alias and prove the real endpoint before launching the agent. | ||
| if (config.enableApiProxy) { | ||
| logger.info('[sbx] Polling api-proxy health via host.docker.internal...'); | ||
| const healthCmd = [ | ||
| 'for i in $(seq 1 30); do', | ||
| ` if curl -sf --max-time 2 http://${SBX_HOST_DOCKER_INTERNAL}:10000/health >/dev/null 2>&1; then`, | ||
| ' echo "api-proxy healthy after ${i}s"; exit 0;', | ||
| ' fi;', | ||
| ' sleep 1;', | ||
| 'done;', | ||
| 'echo "api-proxy health timeout"; exit 1', | ||
| ].join(' '); | ||
|
|
||
| const healthResult = await execInSandbox(sbxName, healthCmd, { | ||
| timeoutMinutes: 1, | ||
| workDir: config.containerWorkDir, | ||
| environment: sbxEnvironment, | ||
| }); | ||
| if (healthResult.exitCode !== 0) { | ||
| logger.warn('[sbx] api-proxy health check failed — proceeding anyway'); | ||
| } | ||
| logger.info('[sbx] Verifying api-proxy /reflect access...'); | ||
| await assertSbxApiProxyReflect( |
| return trimmed.split(/\s+/).slice(1).includes(hostname); | ||
| }); | ||
| if (!alreadyPresent) { | ||
| hostsContent += `${ip}\t${hostname}\n`; |
| logger.info('[sbx] Verifying api-proxy /reflect access...'); | ||
| await assertSbxApiProxyReflect( | ||
| sbxName, | ||
| sbxEnvironment, | ||
| config.containerWorkDir, | ||
| ); |
|
@copilot address review feedback |
Addressed all three review comments in commit
|
|
✅ Smoke Copilot BYOK completed. Copilot BYOK mode operational. 🔓 |
|
🛡️ Smoke Copilot Network Isolation confirmed the egress allowlist is enforced. ✅ |
|
✅ Smoke Copilot BYOK AOAI (Entra) completed. Copilot AOAI BYOK (Entra) mode operational. 🔓 |
|
✅ Smoke Claude passed |
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
|
🔌 Smoke Services — All services reachable! ✅ |
|
✅ Security Guard completed successfully! Security review complete for PR #6811. The change to |
|
✅ Smoke Copilot BYOK AOAI (api-key) completed. Copilot AOAI BYOK (api-key) mode operational. 🔓 |
|
🔌 Smoke Services — All services reachable! ✅ |
|
✅ Smoke Copilot BYOK completed. Copilot BYOK mode operational. 🔓 |
|
✅ Smoke Gemini completed. All facets verified. 💎 Testing direct tool call for list_pull_requests |
|
🚀 Security Guard has started processing this pull request |
|
✅ Build Test Suite completed successfully! |
|
✅ Contribution Check completed successfully! Contribution check complete: PR #6811 appears to meet the applicable CONTRIBUTING.md guidelines. It includes tests for the new behavior, updates documentation, keeps new source in the expected directories, and the description references a related issue. |
|
📰 VERDICT: Smoke Docker Sbx has concluded. All systems operational. This is a developing story. 🎤 |
|
✅ Smoke Copilot BYOK AOAI (Entra) completed. Copilot AOAI BYOK (Entra) mode operational. 🔓 |
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
|
Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded. |
|
✅ Smoke Claude passed |
|
Smoke Test: Copilot BYOK (Direct) Mode ✅ MCP connectivity verified Running in direct BYOK mode via Status: PASS
|
|
EGRESS_RESULT allow=pass deny=pass ✅ Allowed domain (github.com) reachable: Overall: PASS
Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "example.com"See Network Configuration for more information.
|
|
Smoke Test: Copilot Engine —
Overall: PASS
|
Smoke Test: Claude Engine Validation
Overall result: PASS ✅
|
Smoke Test Results: GitHub Actions Services Connectivity
Overall: FAIL —
|
|
Running in direct BYOK mode (AWF_AUTH_TYPE=github-oidc + AWF_AUTH_AZURE_* + COPILOT_PROVIDER_BASE_URL) via api-proxy → Azure OpenAI (Foundry, o4-mini-aw) authenticated via Microsoft Entra Overall: PASS
|
Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "registry.npmjs.org"See Network Configuration for more information.
|
Smoke Test: Gemini Engine Validation
Overall Status: FAIL
|
Chroot Version Comparison Results
Overall: FAILED — Node.js version mismatch between host and chroot environments.
|
Smoke Test: API Proxy OpenTelemetry Tracing
Overall: 4/5 as expected for current development state; 1 real gap identified (env var forwarding), consistent with why no spans were exported in diagnostics. No new issue filed — this appears to be known in-progress work already tracked by this smoke test.
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — PASS Notes
|
|
Smoke Test: Docker Sbx —
Overall: PASS
|
When
maxAiCreditsis active and nodefaultAiCreditsPricingis set,checkUnknownModelRejectionwas blocking every request withmodel: autofor the Copilot provider (HTTP 400). In the default AWF runtime this is hidden because the harness resolvesautovia/reflectbefore sending; in isolated runtimes (gVisor, docker-sbx)/reflectis unreachable, soautohits the guard and the workflow fails before a single token is consumed.Changes
guards/ai-credits-guard.js— Add an early-return incheckUnknownModelRejectionforprovider === PROVIDER_COPILOT && model === 'auto'. The Copilot API resolvesautoto a concrete priced model at runtime; credit accounting happens correctly from the response's model field, making pre-flight rejection both unnecessary and harmful.guards/ai-credits-guard.test.js— Split the former "rejects Copilot auto" assertion into two tests: one confirming Copilotautonow passes, one confirming non-Copilotautois still rejected (it is not a recognized sentinel outside the Copilot API).server.token-guards.test.js— Update the integration-level test to assert that a Copilotautorequest reaches the upstream rather than being blocked with 400.