fix(api-proxy): copy token-tracker-shared + otel modules into image (fixes AIC=0)#5254
Conversation
PR #4780 (released in v0.27.3) extracted token-tracker-shared.js but did not add it to the Dockerfile COPY list. Because the image copies files individually by name (no bundler), require('./token-tracker-shared') threw MODULE_NOT_FOUND in the container. The graceful-degradation guard in proxy-request.js then silently stubbed trackTokenUsage to a no-op, so the container booted and served traffic normally but never wrote token-usage.jsonl -- causing AI-credit accounting to report 0 for every run since v0.27.3. models.json still appeared because model-discovery.js does not depend on the missing module. otel-exporters.js and otel-serialization.js (required by otel.js) were likewise missing and silently disabled OTEL tracing via its own graceful-degradation guard. Add all three files to the COPY list and add a guard test that computes the require closure from server.js and asserts every local module is present in the Dockerfile, preventing this recurring class of bug. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
There was a problem hiding this comment.
Pull request overview
Fixes the api-proxy container image’s incomplete COPY list so that runtime-required modules (token tracking + OpenTelemetry) are actually present in the built image, preventing silent subsystem disablement (notably the AIC=0 regression).
Changes:
- Add previously-missing runtime modules (
token-tracker-shared.js,otel-exporters.js,otel-serialization.js) to the api-proxy Docker imageCOPYlist. - Add a Jest guard test that computes the
require()closure fromserver.jsand asserts every reachable local module is covered by the DockerfileCOPYinstructions.
Show a summary per file
| File | Description |
|---|---|
containers/api-proxy/Dockerfile |
Extends the explicit COPY list to include token-tracker shared helpers and OTEL support modules required at runtime. |
containers/api-proxy/dockerfile-copy-coverage.test.js |
Adds a CI guard to detect missing Dockerfile-copied modules by analyzing local require() reachability from server.js. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 1
| const missing = [...closure] | ||
| .map((abs) => path.relative(ROOT, abs)) | ||
| .filter((rel) => !rel.startsWith('node_modules')) | ||
| .filter((rel) => !isCopied(rel)) | ||
| .sort(); |
|
@copilot address review feedback |
|
⏳ Copilot review left inline comments. @lpcox To proceed:
|
…e-copy-coverage test
Fixed in the latest commit: |
|
❌ Smoke Claude failed |
|
✅ Contribution Check completed successfully! |
|
Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded. |
|
🔑 Smoke Copilot PAT PAT auth validated. All systems operational. ✅ |
|
✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟 |
|
✅ Smoke Copilot BYOK AOAI (api-key) completed. Copilot AOAI BYOK (api-key) mode operational. 🔓 |
|
🔌 Smoke Services — All services reachable! ✅ |
|
❌ Smoke Copilot BYOK AOAI (Entra) reports failed. AOAI BYOK (Entra) mode investigation needed... |
|
📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅ |
|
❌ Smoke Copilot BYOK reports failed. BYOK mode investigation needed... |
|
✅ Build Test Suite completed successfully! |
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
|
✅ Smoke Gemini completed. All facets verified. 💎 Testing safeoutputs |
🔬 Smoke Test: Copilot PAT Auth — PASS
PR: fix(api-proxy): copy token-tracker-shared + otel modules into image (fixes AIC=0) Overall: ✅ PASS
|
|
fix(api-proxy): copy token-tracker-shared + otel modules into image (fixes AIC=0) - ✅ MCP tool connectivity - ✅ PASS
|
🔍 Smoke Test: API Proxy OpenTelemetry Tracing
All scenarios pass. The fix in this PR (adding
|
|
Reviewed merged PRs:
Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "registry.npmjs.org"See Network Configuration for more information.
|
Chroot Smoke Test Results
Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot environments.
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Smoke Test: Services Connectivity
Overall: FAIL —
|
Smoke Test: Gemini Engine Validation - PASS
Overall status: PASS Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "localhost"See Network Configuration for more information.
|
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
|
🤖 Smoke Test Results — PASS
PR: fix(api-proxy): copy token-tracker-shared + otel modules into image (fixes AIC=0) Overall: PASS
|
Root cause of AIC=0
The api-proxy Docker image copies source files individually by name (no bundler — see
Dockerfile). PR #4780 ("extract shared token-tracker budget helpers", first released in v0.27.3) introducedtoken-tracker-shared.js, required bytoken-tracker-http.jsandtoken-tracker-ws.js, but never added it to the Dockerfile COPY list.In the running container:
require('./token-tracker')→token-tracker-http.js→require('./token-tracker-shared')throwsMODULE_NOT_FOUND.proxy-request.jscatchesMODULE_NOT_FOUNDand stubstrackTokenUsageto a no-op.token-usage.jsonlis ever written, so AI-credit accounting reports 0 for every run since v0.27.3.models.jsonstill appears (written bymodel-discovery.js, which doesn't depend on the missing module), which is why the log dir looked healthy.Reproduction (deterministic)
Matches every observation from the real example run (github/gh-aw run 27768989380, image
api-proxy:0.27.4): 73 upstream Copilot CONNECTs from the api-proxy, valid responses with usage returned to the CLI,models.jsonpresent,token-usage.jsonlabsent.Audit: 3 files missing
Computing the full
requireclosure fromserver.jsagainst the COPY list found three silently-disabled modules:token-tracker-shared.jsotel-exporters.jsotel-serialization.jsFix
dockerfile-copy-coverage.test.js: computes the require closure fromserver.jsand asserts every local module is present in the Dockerfile. This is the second occurrence of this exact bug class (OIDC modules were missed the same way previously), so the guard fails fast in CI.Tests
Relationship to #5253
#5253 fixes a separate cache-split fidelity bug in usage parsing. This PR is the actual AIC=0 root cause — without it, the parser fixed in #5253 is never even invoked inside the container.
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com