Skip to content

fix(api-proxy): track token usage for aborted streaming responses#6517

Merged
lpcox merged 2 commits into
mainfrom
fix-token-tracker-aborted-stream
Jul 23, 2026
Merged

fix(api-proxy): track token usage for aborted streaming responses#6517
lpcox merged 2 commits into
mainfrom
fix-token-tracker-aborted-stream

Conversation

@lpcox

@lpcox lpcox commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Problem

The verify_token_usage smoke check is flaky and fails intermittently with:

No token-usage records found. The agent produced no model requests, or the api-proxy failed to record usage.

(seen most recently on run 29975349162 for PR #6514, unrelated to that PR's changes.)

Root cause

In containers/api-proxy/token-tracker-http.js, wireListeners() only finalizes token usage on a clean proxyRes 'end' event. Streaming (SSE) clients such as Codex/OpenAI /responses routinely tear down the socket right after receiving the final event, so the upstream response emits 'aborted'/'close' but never 'end'. When that happens, finalizeHttpTracking never runs — no TRACK_END audit entry is written and the usage that was already accumulated per-chunk (state.streamingUsage) is silently dropped.

Evidence from the artifacts of the failing run vs. a passing main run:

Run TRACK_START TRACK_END token-usage.jsonl records
PR (failed) 16 0 0
main (passed) 14 7 7

Even on passing runs only ~half the streams finalize; the check happens to pass because at least one does. When zero finalize, it fails.

Fix

Finalize token tracking on a premature 'aborted'/'close' as a fallback, so aborted SSE streams still record the usage accumulated so far. Guards:

  • endedCleanly — the fallback is a no-op after a clean 'end', so we never finalize before a decompressor has flushed on the normal path.
  • finalizeOnce — a once-guard ensures usage is written at most once, even when both 'end' and 'close' fire.

Tests

Added two cases to token-tracker.http.test.js:

  • Records usage from a streaming response that closes ('aborted' + 'close') without a clean 'end'.
  • Does not double-count usage when 'close' follows a clean 'end'.

Full api-proxy suite passes (1491 tests).

Note

This is independent of PR #6514 (gVisor retry logic) — that PR only surfaced the flake. This is a standalone fix to the token tracker + its CI sanity check.

The HTTP token tracker only finalized usage on a clean `proxyRes` 'end'
event. Streaming (SSE) clients such as Codex/OpenAI `/responses` routinely
tear down the socket after receiving the final event, so the upstream
response emits 'aborted'/'close' but never 'end'. In that case
`finalizeHttpTracking` never ran and the usage accumulated per-chunk was
silently dropped — no `TRACK_END` and no `token-usage.jsonl` record.

This is why the `verify_token_usage` smoke check is flaky: on a passing run
only ~half the streams finalize (e.g. 7 of 14), and when zero finalize the
check fails with "No token-usage records found".

Finalize on a premature 'aborted'/'close' as a fallback, guarded so it is a
no-op after a clean 'end' (and won't finalize before a decompressor flushes).
A once-guard ensures usage is never written twice.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 79b3e12a-b35f-47e3-adb5-c0178e7eb873
Copilot AI review requested due to automatic review settings July 23, 2026 04:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds fallback token-usage finalization for prematurely closed streaming responses.

Changes:

  • Handles aborted and close response events with once-only finalization.
  • Adds tests for aborted streams and duplicate finalization.
Show a summary per file
File Description
containers/api-proxy/token-tracker-http.js Adds premature-close finalization; compressed streams require correction.
containers/api-proxy/token-tracker.http.test.js Tests aborted and clean-close behavior.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread containers/api-proxy/token-tracker-http.js
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 98.99% 98.99% ➡️ +0.00%
Statements 98.92% 98.91% 📉 -0.01%
Functions 99.40% 99.41% ➡️ +0.01%
Branches 95.34% 95.30% 📉 -0.04%
📁 Per-file Coverage Changes (3 files)
File Lines (Before → After) Statements (Before → After)
src/container-lifecycle.ts 100.0% → 98.6% (-1.36%) 100.0% → 98.1% (-1.93%)
src/commands/main-action.ts 92.6% → 92.6% (+0.05%) 92.0% → 92.1% (+0.05%)
src/log-directory-setup.ts 96.2% → 100.0% (+3.78%) 96.3% → 100.0% (+3.71%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Security Guard completed successfully!

Security review complete for PR #6517. Changes are in containers/api-proxy/token-tracker-http.js to fix flaky token usage tracking for aborted streaming responses. No security concerns identified: no firewall weakening, no network policy changes, no capabilities added, no secrets exposure, no input validation issues. Changes are confined to token tracking observability logic with appropriate guards to prevent double-counting.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Smoke Gemini completed. All facets verified. 💎

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Smoke Copilot BYOK AOAI (Entra) completed. Copilot AOAI BYOK (Entra) mode operational. 🔓

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Smoke Copilot BYOK AOAI (api-key) completed. Copilot AOAI BYOK (api-key) mode operational. 🔓

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Smoke Claude passed

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Contribution Check completed successfully!

Contribution check complete for PR #6517: no guideline issues found in the provided context. The PR includes tests for the new behavior, the description explains the problem/root cause/fix clearly, and the files are organized appropriately under containers/api-proxy.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Build Test Suite completed successfully!

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

🛡️ Smoke Copilot Network Isolation confirmed the egress allowlist is enforced. ✅

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

🔑 Smoke Copilot PAT PAT auth validated. All systems operational. ✅

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

🔌 Smoke Services — All services reachable! ✅

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Smoke Copilot BYOK completed. Copilot BYOK mode operational. 🔓

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

📰 VERDICT: Smoke Docker Sbx has concluded. All systems operational. This is a developing story. 🎤

@github-actions

Copy link
Copy Markdown
Contributor

@lpcox — Smoke test results for this PR:

EGRESS_RESULT allow=pass deny=pass

  • ✅ Allowed domain (api.github.com) → allowed=200
  • ✅ Blocked domain (example.com) → OK: example.com was blocked

Overall: PASS

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • example.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "example.com"

See Network Configuration for more information.

🛡️ Egress verdict from Smoke Copilot Network Isolation
Add label ready-for-aw to run again

@github-actions github-actions Bot added the smoke-copilot-network-isolation Copilot network-isolation egress smoke test label Jul 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Claude Engine Validation

Check Result
API Status ✅ PASS
GH Check ✅ PASS
File Status ✅ PASS

Overall Result: PASS

Generated by Smoke Claude for #6517 · 54.6 AIC · ⊞ 3.2K ·
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🔬 Smoke Test: Copilot PAT Auth — PASS

Test Result
GitHub MCP connectivity
GitHub.com HTTP (200)
File write/read

Overall: PASS · Auth mode: PAT (COPILOT_GITHUB_TOKEN)

/cc @lpcox

🔑 PAT report filed by Smoke Copilot PAT
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🔬 Smoke Test Results

Test Status
GitHub MCP connectivity ⚠️ filtered by secrecy policy
GitHub.com HTTP ⚠️ pre-step data unavailable (template vars not expanded)
File write/read ⚠️ pre-step data unavailable (template vars not expanded)

Overall: INCONCLUSIVE — Pre-computed test data (steps.smoke-data.outputs.*) was not interpolated before agent invocation; raw ${{ }} expressions were passed as literals. MCP read was filtered by secrecy policy.

cc @lpcox

📰 BREAKING: Report filed by Smoke Copilot
Add label ready-for-aw to run again

@github-actions github-actions Bot mentioned this pull request Jul 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Copilot BYOK (Direct Mode) ✅ PASS

Test Results:

  • ✅ GitHub.com Connectivity: HTTP 200
  • ✅ File Write/Read: /tmp/gh-aw/agent/smoke-test-copilot-byok.txt
  • ✅ BYOK Inference: Running in direct BYOK mode (COPILOT_PROVIDER_API_KEY) via api-proxy → api.githubcopilot.com

All tests passed. Agent credentials properly isolated.

🔑 BYOK report filed by Smoke Copilot BYOK
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Gemini Engine Smoke Test Results

Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • localhost

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"

See Network Configuration for more information.

💎 Faceted by Smoke Gemini
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: API Proxy OpenTelemetry Tracing

Scenario Result Detail
1. Module Loading otel.js loads successfully; exports: startRequestSpan, setTokenAttributes, setBudgetAttributes, endSpan, endSpanError, shutdown, isEnabled + internal helpers
2. Test Suite 39/39 tests passed (otel.test.js) — ProxyAwareOtlpExporter, FileSpanExporter, shutdown all green
3. Env Var Forwarding OTEL_* vars auto-forwarded to api-proxy container via agent-environment-credentials.ts (verified by test coverage at lines 320–351)
4. Token Tracker Integration onUsage callback present in token-tracker-http.js (line 386) as the OTEL hook point
5. OTEL Diagnostics No unexpected errors; graceful degradation confirmed when endpoint not configured

All scenarios pass.

📡 OTel tracing validated by Smoke OTel Tracing
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test Results

  • Redis PING: ❌ (host.docker.internal name resolution failed)
  • PostgreSQL pg_isready: ❌ (no response)
  • PostgreSQL SELECT 1: ❌ (name resolution failed)

Overall: FAILhost.docker.internal is not resolvable in this environment.

🔌 Service connectivity validated by Smoke Services
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

@lpcox
Smoke test (Direct BYOK Mode) results:

  • GitHub MCP tool check: ❌ MCP error 0: resource filtered by policy
  • GitHub.com connectivity: ✅
  • File I/O: ✅
  • BYOK inference: ✅
    Running in direct BYOK mode (COPILOT_PROVIDER_API_KEY + COPILOT_PROVIDER_BASE_URL) via api-proxy → Azure OpenAI (Foundry, o4-mini-aw)
    Overall: FAIL

🔑 BYOK (AOAI api-key) report filed by Smoke Copilot BYOK AOAI (api-key)
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.13 ✅ YES
Node.js v24.18.0 v22.23.1 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall: ❌ FAILED — Node.js version mismatch detected (host: v24.18.0, chroot: v22.23.1).

Tested by Smoke Chroot
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx All passed ✅ PASS
Node.js execa All passed ✅ PASS
Node.js p-limit All passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Generated by Build Test Suite for #6517 · 35.1 AIC · ⊞ 8.2K ·
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

@lpcox
${{ steps.smoke-data.outputs.SMOKE_PR_DATA }}
• MCP connectivity: ✅
• GitHub.com connectivity: ✅
• File write/read: ✅
• BYOK inference: ✅
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

🪪 BYOK (AOAI Entra) report filed by Smoke Copilot BYOK AOAI (Entra)
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🔮 The oracle has spoken through Smoke Codex
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🔥 Smoke Test Results — Docker Sbx

Test Result
GitHub MCP connectivity ✅ Connected (secrecy filter applied)
GitHub.com HTTP ⚠️ Pre-step data not resolved
File write/read ⚠️ Pre-step data not resolved

Overall: ⚠️ PARTIAL — MCP reachable; pre-computed step outputs were not injected (template variables unresolved).

@lpcox

📰 BREAKING: Report filed by Smoke Docker Sbx
Add label ready-for-aw to run again

@lpcox
lpcox merged commit 621e903 into main Jul 23, 2026
139 of 140 checks passed
@lpcox
lpcox deleted the fix-token-tracker-aborted-stream branch July 23, 2026 04:35
lpcox added a commit that referenced this pull request Jul 24, 2026
…x /responses) (#6542)

* fix(api-proxy): finalize token usage on downstream close

Codex/OpenAI /responses SSE clients read until the terminal
`response.completed` event, then immediately tear down the DOWNSTREAM
socket and end their turn — taking the whole awf sandbox (including the
api-proxy) down before the UPSTREAM keep-alive socket (proxyRes) ever
emits 'end'/'close'/'aborted'. Because `proxyRes.pipe(res)` does not
propagate a downstream close back to the upstream stream, none of the
existing finalization triggers fired: audits from Smoke Codex runs show
12 TRACK_START / 0 TRACK_END and an empty token-usage.jsonl, failing
verify_token_usage.

The aborted-stream fallback added in #6517 only watches proxyRes, which
is the wrong stream for this case. Thread the downstream `res` through
setupTokenTracking -> trackTokenUsage and add `res.on('close')` as an
additional finalization trigger. The usage is already parsed per-chunk
before the client bails, so this recovers it. The existing
finalizeOnce/endedCleanly guards keep it a no-op on clean completions,
so no double counting.

Adds regression tests covering downstream-close-without-upstream-end and
the clean-end-then-downstream-close no-double-count case.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6bfb1733-2a26-4202-8db1-b99d2b85dab8

* Guard downstream close finalization until SSE usage is observed

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Copilot-Session: 6bfb1733-2a26-4202-8db1-b99d2b85dab8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants