Skip to content

fix(api-proxy): prevent stream_options injection into OpenAI Responses API requests#3805

Merged
lpcox merged 13 commits into
mainfrom
copilot/api-proxy-fix-stream-options
May 25, 2026
Merged

fix(api-proxy): prevent stream_options injection into OpenAI Responses API requests#3805
lpcox merged 13 commits into
mainfrom
copilot/api-proxy-fix-stream-options

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 25, 2026

injectStreamOptions() was injecting stream_options: { include_usage: true } into Responses API (/responses) requests, which the API rejects outright. Codex CLI sends the path without a leading slash (api.path="responses"), bypassing the existing regex guard and crashing all Codex workflows with exit code 1.

Changes

  • body-transform.js

    • Make leading slash optional in the path guard regex:
      // Before — requires leading slash, misses "responses" bare path
      if (/^\/(?:v\d+\/)?responses(?:\/|$)/.test(pathOnly)) return null;
      
      // After — handles both "/responses" and "responses"
      if (/^\/?(?:v\d+\/)?responses(?:\/|$)/.test(pathOnly)) return null;
    • Add a body-shape secondary guard: if the parsed body has input but no messages array (canonical Responses API shape), skip injection regardless of path:
      if (parsed.input !== undefined && !Array.isArray(parsed.messages)) return null;
  • body-transform.test.js

    • Add cases for bare paths (responses, v1/responses, v1/responses?foo=1)
    • Add case for body-shape guard on an unrecognised path
    • Add case confirming guard doesn't fire when both input and messages are present

Copilot AI changed the title [WIP] Fix injection of stream_options in OpenAI Responses API requests fix(api-proxy): prevent stream_options injection into OpenAI Responses API requests May 25, 2026
Copilot finished work on behalf of lpcox May 25, 2026 19:05
Copilot AI requested a review from lpcox May 25, 2026 19:05
@lpcox lpcox marked this pull request as ready for review May 25, 2026 19:06
Copilot AI review requested due to automatic review settings May 25, 2026 19:06
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 96.54% 96.58% 📈 +0.04%
Statements 96.38% 96.42% 📈 +0.04%
Functions 97.99% 97.99% ➡️ +0.00%
Branches 90.78% 90.82% 📈 +0.04%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/config-writer.ts 89.3% → 90.9% (+1.65%) 89.3% → 90.9% (+1.65%)

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the API proxy’s request body transformation logic to avoid injecting stream_options: { include_usage: true } into OpenAI Responses API (/responses) requests, which reject that field and can break streaming workflows.

Changes:

  • Broaden the /responses path guard in injectStreamOptions() to also match paths that omit a leading / (and still handle versioned forms).
  • Add a secondary “body-shape” guard: when the JSON body has input but no messages[], treat it as Responses-API-shaped and skip injection even if the path is unexpected.
  • Extend unit tests to cover bare-path variants and the new body-shape behavior.
Show a summary per file
File Description
containers/api-proxy/body-transform.js Adjusts stream options injection guards to reliably exclude Responses API requests (path + body-shape).
containers/api-proxy/body-transform.test.js Adds test coverage for bare-path responses variants and the new body-shape guard logic.

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: 0

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Cherry-picks the postprocess script fix that replaces standalone
--skip-pull with --build-local, ensuring CI builds all containers
(including api-proxy) from source code. This is critical for testing
the stream_options.include_usage fix on this branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

The real issue was model selection (gpt-5.5 not callable by our API key
tier), not request body incompatibilities. Remove all three transforms
added in this PR:
- stripUnrecognizedToolTypes
- stripEncryptedInclude
- stripReasoningEffort

Hardcode gpt-5.4 as the fallback model in the lock file since Codex
CLI defaults to gpt-5.5 which hangs indefinitely with our key.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lpcox lpcox force-pushed the copilot/api-proxy-fix-stream-options branch from 74d37bd to e53047b Compare May 25, 2026 23:06
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

The CI API key cannot inference against gpt-5.5 (Codex CLI's default) —
requests hang indefinitely with no error response. Use gpt-5.4 via the
engine.model frontmatter field so the lock file hardcodes the model.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results: Claude

  • ✅ GitHub API: 2 recent PRs fetched
  • ✅ GitHub check (Playwright): PASS
  • ✅ File verify: smoke-test-claude-26424429137.txt exists

Total: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Smoke Test: API Proxy OpenTelemetry Tracing

Scenario Result Notes
1. Module Loading otel.js loads successfully; exports: startRequestSpan, setTokenAttributes, endSpan, endSpanError, shutdown, isEnabled
2. Test Suite 33/33 tests passed (otel.test.js) — spans, token attrs, parent context, exporters, shutdown
3. Env Var Forwarding api-proxy-service.ts forwards OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, GITHUB_AW_OTEL_TRACE_ID, GITHUB_AW_OTEL_PARENT_SPAN_ID, OTEL_SERVICE_NAME
4. Token Tracker Integration onUsage callback exists in token-tracker-http.js (line 237) as the OTEL hook point
5. OTEL Diagnostics Graceful degradation confirmed — file fallback to /var/log/api-proxy/otel.jsonl when OTLP endpoint unset; no network traffic without opt-in

All scenarios pass. ✅

📡 OTel tracing validated by Smoke OTel Tracing

@github-actions
Copy link
Copy Markdown
Contributor

🔬 Smoke Test Results

Test Status
GitHub MCP connectivity
GitHub.com HTTP connectivity ⚠️ N/A (pre-step vars not expanded)
File write/read ⚠️ N/A (pre-step vars not expanded)

PR: fix(api-proxy): prevent stream_options injection into OpenAI Responses API requests
Author: @Copilot | Assignees: @lpcox, @Copilot

Overall: PARTIAL — MCP ✅, pre-computed test data unavailable (template substitution did not occur)

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test: Copilot BYOK (Offline) Mode

Test Result
GitHub MCP (list PRs) ✅ PR #3803 "Filter unresolvable model aliases from /reflect and models.json"
GitHub.com connectivity ⚠️ Pre-step data unavailable (template vars not expanded)
File write/read ⚠️ Pre-step data unavailable (template vars not expanded)
BYOK inference (this response)

Running in BYOK offline mode (COPILOT_OFFLINE=true) via api-proxy → api.githubcopilot.com.

Author: @Copilot · Assignees: @lpcox, @Copilot

Overall: PASS (core BYOK path verified; pre-step outputs were not interpolated)

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

Chroot Runtime Version Comparison

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.15.0 v22.22.3
Go go1.22.12 go1.22.12

Result: Not all tests passed. Python and Node.js versions differ between host and chroot environments. Go matches.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

fix(api-proxy): prevent stream_options injection into OpenAI Responses API requests
Filter unresolvable model aliases from /reflect and models.json
chore: upgrade all workflows to gh-aw v0.75.4

✅ Playwright
✅ File write
❌ Build (TS5107)
Overall: FAIL

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

  • GitHub MCP Testing: ❌ (mcpscripts not found)
  • GitHub.com Connectivity: ❌ (SSL error 35)
  • File Writing Testing: ✅
  • Bash Tool Testing: ✅

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

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test: GitHub Actions Services Connectivity

Check Result
Redis PING ❌ Timeout/unreachable
PostgreSQL pg_isready ❌ Timeout/unreachable
PostgreSQL SELECT 1 ❌ Timeout/unreachable

Overall: FAILhost.docker.internal is not reachable from this environment. Service containers may not be running or the hostname is not resolvable.

🔌 Service connectivity validated by Smoke Services

@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 issue #3805 · sonnet46 1.3M ·

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.

api-proxy: stream_options.include_usage injected into OpenAI Responses API requests

3 participants