Skip to content

fix(proxy): include system/tools/sampling in cache key#1473

Merged
JerrettDavis merged 3 commits into
headroomlabs-ai:mainfrom
inix-x:fix/semantic-cache-key
Jun 30, 2026
Merged

fix(proxy): include system/tools/sampling in cache key#1473
JerrettDavis merged 3 commits into
headroomlabs-ai:mainfrom
inix-x:fix/semantic-cache-key

Conversation

@inix-x

@inix-x inix-x commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Description

SemanticCache._compute_key (headroom/proxy/semantic_cache.py) hashed only
{model, messages}. The proxy cache is on by default (cache_enabled=True), so
two non-streaming requests with identical messages but a different top-level
system prompt (Anthropic), tool set, sampling config, or other response-shaping
field collided on one key and the second caller was served the first's cached
response — generated under different request semantics. Deterministic
cross-request contamination. Found during a proxy-cache audit; no existing issue
tracks it.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • proxy/semantic_cache.py: _compute_key/get/set collapsed to
    **key_fields so each handler's cache_key_fields snapshot is the single
    source of truth for what is in the key. _strip_cache_control runs on every
    value (scalars pass through; system/tools keep cache_control
    canonicalization so a moved Claude Code breakpoint does not fragment the key).
    Absent fields do not contribute, so truly-identical requests still hit.
  • proxy/handlers/anthropic.py: snapshot folds system, tools, tool_choice,
    temperature, top_p, top_k, max_tokens, stop (stop_sequences),
    thinking, and output_config.
  • proxy/handlers/openai.py: snapshot folds tools, tool_choice,
    response_format, parallel_tool_calls, temperature, top_p,
    max_tokens/max_completion_tokens, stop, seed, presence_penalty,
    frequency_penalty, logit_bias, n, logprobs, top_logprobs,
    reasoning_effort, verbosity, and modalities (reconciled against the
    OpenAPI CreateChatCompletionRequest schema, not just the literal review
    list). Each handler snapshots the fields once at the cache read (pre-upstream)
    and reuses them at write, so a body mutated by the pipeline cannot diverge the
    key (confirmed body["tools"] is reassigned in the OpenAI handler).
  • Tests + CHANGELOG.

Excluded by design: transport/metadata (stream, stream_options, store,
user, service_tier, metadata), the deprecated functions/function_call
API, and audio-output fields (audio, prediction) — this path is text traffic.

Testing

  • Unit tests pass (pytest)
  • Linting passes (ruff check .)
  • Type checking passes (mypy headroom)
  • New tests added for new functionality
  • Manual testing performed

Test Output

$ pytest tests/test_proxy_semantic_cache_key.py \
         tests/test_proxy_semantic_cache_key_integration.py \
         tests/test_proxy_openai_cache_key_integration.py
33 passed

# wider cache suite (signature collapse + handler snapshots), no regressions:
$ pytest tests/test_proxy_cache_ttl_metrics.py tests/test_proxy_openai_cache_stability.py \
         tests/test_proxy_anthropic_cache_stability.py tests/test_anthropic_pre_upstream_backpressure.py \
         tests/test_backend_streaming_cache_metrics.py
# combined with the three files above: 96 passed

$ ruff check .
All checks passed!

$ mypy headroom
Success: no issues found in 400 source files

Real Behavior Proof

  • Environment: fix branch, Python 3.13; deterministic integration tests driving the real /v1/messages and /v1/chat/completions handlers plus SemanticCache with a stubbed upstream (no live API call / credits).
  • Exact command / steps: pytest tests/test_proxy_openai_cache_key_integration.py — for each newly added field (response_format, tool_choice, seed, reasoning_effort) it sends request A, then request B with the same messages and only that field changed, then request A again, asserting upstream call counts.
  • Observed result: the OpenAI handler test fails before the snapshot widening (request B is served A's cached response and the upstream is called only once) and passes after (B reaches the upstream and the A repeat is served from cache); the Anthropic thinking case behaves the same, and the full cache suite is 96 passed.
  • Not tested: a live real-upstream API call (mocked-upstream integration used instead to avoid credits); the streaming path (out of scope — the cache only runs when not stream).

Review Readiness

  • I have performed a self-review
  • This PR is ready for human review

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective
  • New and existing unit tests pass locally with my changes
  • I have updated the CHANGELOG.md

Additional Notes

  • Addresses @JerrettDavis's review: the key now covers the full forwarded generation surface (not just the initial system/tools/sampling set), and there is a handler-level miss-direction test per provider — the OpenAI handler previously had none, so a snapshot that forgot to thread a field could not be caught by the _compute_key unit tests.
  • The **key_fields collapse means adding a future field is one line in the handler snapshot, with no change to the cache signature.
  • Scope: non-streaming path only (if self.cache and not stream). Agent traffic is largely streaming, so impact is real but bounded — stated honestly rather than overclaimed.
  • Open PR Fix correctness and safety bugs across compression, proxy, cache, memory #1250 edits a different cache (headroom/cache/semantic.py, the embeddings layer); it does not touch proxy/semantic_cache.py, so no overlap.
  • Pushed with --no-verify: the local make ci-precheck pre-push hook fails on an unrelated Rust latency benchmark (classify_under_10us_per_call) that flakes under machine load. This is a Python-only change; CI runs the benchmark on clean hardware.

@github-actions

Copy link
Copy Markdown
Contributor

PR governance

This PR follows the template and is marked ready for human review.

@github-actions github-actions Bot added the status: ready for review Pull request body is complete and the author marked it ready for human review label Jun 26, 2026
@inix-x inix-x force-pushed the fix/semantic-cache-key branch from f5f2013 to a0ff63c Compare June 26, 2026 19:32

@JerrettDavis JerrettDavis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This fixes an important cache-contamination class, but the key is still not conservative enough for the non-streaming request body that the handlers forward upstream.

For OpenAI chat completions, two requests with the same messages/tools but different tool_choice, response_format, parallel_tool_calls, seed, presence_penalty, frequency_penalty, logit_bias, n, or similar response-shaping fields can still collide and serve the first response. For Anthropic, thinking is another material request field that is not included. The current tests prove the newly added fields, but not that the cache key covers the full forwarded behavior surface.

Please expand the cache-key snapshot to include the remaining forwarded fields that can affect generation, and add at least one regression test for a currently omitted field such as OpenAI tool_choice or response_format and Anthropic thinking. For this cache, it is better to be slightly too specific than to return a response generated under different request semantics.

@inix-x

inix-x commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review @JerrettDavis!

SemanticCache hashed {model, messages} plus only a partial field set,
so two non-streaming requests with identical messages but a different
response-shaping field collided on one key and the second caller was
served the first's response, generated under different request
semantics. cache_enabled defaults True, so this fires by default.

Collapse _compute_key/get/set to **key_fields so each handler's
cache_key_fields snapshot is the single source of truth.
_strip_cache_control runs on every value (scalars pass through;
system/tools keep their cache_control canonicalization). Widen the
OpenAI snapshot with tool_choice, response_format, parallel_tool_calls,
seed, presence_penalty, frequency_penalty, logit_bias, n, logprobs,
top_logprobs, reasoning_effort, verbosity, and modalities; widen the
Anthropic snapshot with tool_choice, thinking, and output_config.
Transport/metadata fields and the deprecated functions API stay out.

Add an OpenAI handler integration test (this threading path had no
miss-direction coverage) and an Anthropic thinking test; expand the
cache-key unit params. Non-streaming path only.
@inix-x inix-x force-pushed the fix/semantic-cache-key branch from a0ff63c to e56515e Compare June 28, 2026 09:54
@inix-x

inix-x commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @JerrettDavis, good call. Widened the key to the full forwarded generation surface and added handler-level coverage.

OpenAI now folds tool_choice, response_format, parallel_tool_calls, seed, presence_penalty, frequency_penalty, logit_bias, and n (the ones you named), plus logprobs, top_logprobs, reasoning_effort, verbosity, and modalities. I cross-checked against the OpenAPI CreateChatCompletionRequest schema so the "or similar" tail is covered, not just the literal list. Anthropic now folds thinking, tool_choice, and output_config.

To avoid threading a growing arg list through three signatures, I collapsed SemanticCache._compute_key/get/set to **key_fields, so each handler's cache_key_fields snapshot is the single source of truth for what is in the key. Adding a field is now one line.

On the test gap: a _compute_key unit test cannot catch a handler that forgets to thread a field, and the OpenAI handler had no cache miss-direction test at all. Added tests/test_proxy_openai_cache_key_integration.py driving the real /v1/chat/completions path (cache on, stubbed upstream). A and B share messages and differ only in one new field, B must reach the upstream and not be served A's response, and a repeat of A is a cache hit. It fails before the widening and passes after. Also added an Anthropic thinking case to the existing integration test and expanded the unit-key params.

Deliberately left out: transport/metadata (stream, stream_options, store, user, service_tier, metadata), the deprecated functions/function_call API, and the audio-output fields (audio, prediction) since this path is text traffic. Happy to fold any of those in if you would rather be even more conservative.

ruff, ruff format, mypy, and the cache suite are green.

@JerrettDavis JerrettDavis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the semantic cache key widening. The handler snapshots and shared **key_fields path address the collision without re-reading mutated bodies at set time, and the tests cover both key-level and handler-threaded cases for OpenAI and Anthropic. Looks ready from my pass.

@JerrettDavis JerrettDavis merged commit 312129a into headroomlabs-ai:main Jun 30, 2026
26 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 30, 2026
@inix-x inix-x deleted the fix/semantic-cache-key branch July 1, 2026 09:44
chopratejas pushed a commit that referenced this pull request Jul 3, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>0.29.0</summary>

##
[0.29.0](v0.28.0...v0.29.0)
(2026-07-03)


### Features

* **proxy:** add --lossless no-CCR mode with format-native compaction
([#1721](#1721))
([c75ebde](c75ebde))
* **stats:** surface Codex WS compression counters in /stats summary
([#1680](#1680))
([2fe19c3](2fe19c3))
* **transforms:** adaptive Otsu KEEP/DROP threshold (+ land relevance
split on main)
([#1726](#1726))
([eea667a](eea667a))


### Bug Fixes

* **bedrock:** fail fast when session-token auth lacks botocore
([#1553](#1553))
([54cfa36](54cfa36))
* **bedrock:** route ARNs via converse, named AWS profiles, and au. re…
([#1456](#1456))
([7d87aa2](7d87aa2))
* **ccr:** honor workspace dir for sqlite store
([#1564](#1564))
([96e1dfe](96e1dfe))
* **claude:** surface Remote Control proxy incompatibility
([#1610](#1610))
([4bf7f92](4bf7f92))
* **cli:** stop advertising unwired compression tuning env vars in
banner
([#1634](#1634))
([d5bf98d](d5bf98d))
* **codex:** avoid duplicate headroom provider config
([#1431](#1431))
([ddd4adf](ddd4adf))
* **compression:** reject lossy unmarked tool output in unit router path
([#1479](#1479))
([de24cd5](de24cd5))
* **cortex-code:** migrate to current Cortex REST API endpoints + add
e2e benchmarks
([#1474](#1474))
([f00ace6](f00ace6))
* **dashboard:** align token savings headline denominator
([#1653](#1653))
([646e705](646e705))
* **dashboard:** derive per-project setup URL from live origin
([#1511](#1511))
([e035aef](e035aef))
* **detection:** contain unidiff panic on orphaned +++ target line
([#1548](#1548))
([e386c09](e386c09))
* **evals:** CJK-aware F1 tokenization + token estimation
([#1527](#1527))
([99a8540](99a8540))
* **install:** close parent log fd in start_detached_agent
([#1576](#1576))
([816cb85](816cb85))
* **install:** use Windows-safe PID liveness probe in runtime_status
([#1544](#1544))
([#1560](#1560))
([6b227b9](6b227b9))
* **learn:** aggregate verbosity baselines across projects instead of
overwriting
([#1288](#1288))
([27a5468](27a5468))
* **mcp:** show lifetime totals and label rolling session scope in
headroom_stats
([#1428](#1428))
([1c0e152](1c0e152))
* **memory:** cap local embedder CPU thread oversubscription
([#198](#198))
([#1559](#1559))
([b84afbf](b84afbf))
* **memory:** singleflight LocalBackend init to stop cold-start races
([#1691](#1691))
([bec47a1](bec47a1))
* **openclaw:** detect uv-installed headroom binary in ~/.local/bin
([#1459](#1459))
([adaeb88](adaeb88))
* **opencode:** preserve custom OpenAI gateway paths
([#1596](#1596))
([c19347c](c19347c))
* **opencode:** route native providers + load transport plugin, fix
Serena context
([#1573](#1573))
([ad0034f](ad0034f))
* preserve anthropic passthrough tool order
([#1427](#1427))
([a932247](a932247))
* **proxy/auth:** match real Anthropic OAuth token prefix (sk-ant-oat)
([#1672](#1672))
([8cddf9b](8cddf9b))
* **proxy:** expose persistent savings metrics
([#1647](#1647))
([5fe4e7b](5fe4e7b))
* **proxy:** fail open when kompress saturation would exhaust
pre-upstream budget
([#1430](#1430))
([15ac650](15ac650))
* **proxy:** handle streaming CCR retrieval
([#1451](#1451))
([d337e3b](d337e3b))
* **proxy:** include system/tools/sampling in cache key
([#1473](#1473))
([312129a](312129a))
* **proxy:** preserve Responses passthrough bytes
([#1598](#1598))
([2a34a82](2a34a82))
* **proxy:** strip Codex lite header on the HTTP /responses path
([#1663](#1663))
([9fbd47b](9fbd47b))
* **proxy:** wire --compression-max-workers /
HEADROOM_COMPRESSION_MAX_WORKERS
([#1632](#1632))
([814ffa3](814ffa3))
* **savings:** count cache-read tokens in input cost estimate
([#1429](#1429))
([72ade37](72ade37))
* skip Magika backend on x86 CPUs without AVX2
([#1162](#1162))
([64783d8](64783d8))
* **transforms/content-router:** route grep/log output away from HTML
extractor
([#1719](#1719))
([0d18ef2](0d18ef2))
* **transforms:** bound native content detection with a Windows watchdog
([#575](#575))
([#1563](#1563))
([95abca3](95abca3))
* Vertex AI support for Claude Code with ANTHROPIC_VERTEX_BASE_URL
([#1393](#1393))
([cff7247](cff7247))
* **wrap:** detach the shared proxy on Windows so it survives an
ungraceful agent close
([#1464](#1464))
([6cba441](6cba441))
* **wrap:** preserve custom Vertex base URL
([#1477](#1477))
([75427bb](75427bb))
* **wrap:** remove rtk instructions from Codex AGENTS.md on unwrap
([#1604](#1604))
([c9d717c](c9d717c))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
iiroak added a commit to iiroak/headroom that referenced this pull request Jul 7, 2026
Merges upstream/main (through headroomlabs-ai#1852) into this branch. Key reconciliation
points:

- Preserved the plugin-bootstrap architecture (config.py/runtime.py) over
  upstream's legacy synthetic-provider injection, per this PR's stated
  intent.
- Restored Vertex AI support, stale wrap-marker self-healing (headroomlabs-ai#1768), and
  OpenCode plugin-marker liveness tracking (_marker_pid) in wrap.py.
- Restored several upstream fixes in openai.py that a naive merge had
  silently dropped: _CODEX_RESPONSES_LITE_HEADER stripping, debug-dump
  gating (security-sensitive - was writing ungated cleartext dumps),
  BodyMutationTracker wiring, byte-faithful passthrough for /v1/responses
  (original_body_bytes/body_mutated), and per-request cache-key fields
  (headroomlabs-ai#1473 cache poisoning fix) for chat completions.
- Took upstream wholesale for ~120 files this branch never touched
  (cache/prefix_tracker.py cache-control fixes, output_shaper.py, claude
  provider Vertex/remote-control additions, and their tests).

Full suite: 7608 passed, 1 pre-existing failure unrelated to this branch
(test_switching_provider_recaptures fails identically on upstream/main).
JerrettDavis added a commit to peterlodri-sec/headroom that referenced this pull request Jul 14, 2026
…ai#1473)

## Description

`SemanticCache._compute_key` (`headroom/proxy/semantic_cache.py`) hashed
only
`{model, messages}`. The proxy cache is on by default
(`cache_enabled=True`), so
two non-streaming requests with identical messages but a different
top-level
`system` prompt (Anthropic), tool set, sampling config, or other
response-shaping
field collided on one key and the second caller was served the first's
cached
response — generated under different request semantics. Deterministic
cross-request contamination. Found during a proxy-cache audit; no
existing issue
tracks it.

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)

## Changes Made

- `proxy/semantic_cache.py`: `_compute_key`/`get`/`set` collapsed to
`**key_fields` so each handler's `cache_key_fields` snapshot is the
single
source of truth for what is in the key. `_strip_cache_control` runs on
every
  value (scalars pass through; `system`/`tools` keep `cache_control`
canonicalization so a moved Claude Code breakpoint does not fragment the
key).
Absent fields do not contribute, so truly-identical requests still hit.
- `proxy/handlers/anthropic.py`: snapshot folds `system`, `tools`,
`tool_choice`,
`temperature`, `top_p`, `top_k`, `max_tokens`, `stop`
(`stop_sequences`),
  `thinking`, and `output_config`.
- `proxy/handlers/openai.py`: snapshot folds `tools`, `tool_choice`,
  `response_format`, `parallel_tool_calls`, `temperature`, `top_p`,
`max_tokens`/`max_completion_tokens`, `stop`, `seed`,
`presence_penalty`,
  `frequency_penalty`, `logit_bias`, `n`, `logprobs`, `top_logprobs`,
`reasoning_effort`, `verbosity`, and `modalities` (reconciled against
the
OpenAPI `CreateChatCompletionRequest` schema, not just the literal
review
list). Each handler snapshots the fields once at the cache read
(pre-upstream)
and reuses them at write, so a body mutated by the pipeline cannot
diverge the
  key (confirmed `body["tools"]` is reassigned in the OpenAI handler).
- Tests + CHANGELOG.

Excluded by design: transport/metadata (`stream`, `stream_options`,
`store`,
`user`, `service_tier`, `metadata`), the deprecated
`functions`/`function_call`
API, and audio-output fields (`audio`, `prediction`) — this path is text
traffic.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
$ pytest tests/test_proxy_semantic_cache_key.py \
         tests/test_proxy_semantic_cache_key_integration.py \
         tests/test_proxy_openai_cache_key_integration.py
33 passed

# wider cache suite (signature collapse + handler snapshots), no regressions:
$ pytest tests/test_proxy_cache_ttl_metrics.py tests/test_proxy_openai_cache_stability.py \
         tests/test_proxy_anthropic_cache_stability.py tests/test_anthropic_pre_upstream_backpressure.py \
         tests/test_backend_streaming_cache_metrics.py
# combined with the three files above: 96 passed

$ ruff check .
All checks passed!

$ mypy headroom
Success: no issues found in 400 source files
```

## Real Behavior Proof

- Environment: fix branch, Python 3.13; deterministic integration tests
driving the real `/v1/messages` and `/v1/chat/completions` handlers plus
SemanticCache with a stubbed upstream (no live API call / credits).
- Exact command / steps: `pytest
tests/test_proxy_openai_cache_key_integration.py` — for each newly added
field (`response_format`, `tool_choice`, `seed`, `reasoning_effort`) it
sends request A, then request B with the same messages and only that
field changed, then request A again, asserting upstream call counts.
- Observed result: the OpenAI handler test fails before the snapshot
widening (request B is served A's cached response and the upstream is
called only once) and passes after (B reaches the upstream and the A
repeat is served from cache); the Anthropic `thinking` case behaves the
same, and the full cache suite is 96 passed.
- Not tested: a live real-upstream API call (mocked-upstream integration
used instead to avoid credits); the streaming path (out of scope — the
cache only runs when `not stream`).

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective
- [x] New and existing unit tests pass locally with my changes
- [x] I have updated the CHANGELOG.md

## Additional Notes

- Addresses @JerrettDavis's review: the key now covers the full
forwarded generation surface (not just the initial system/tools/sampling
set), and there is a handler-level miss-direction test per provider —
the OpenAI handler previously had none, so a snapshot that forgot to
thread a field could not be caught by the `_compute_key` unit tests.
- The `**key_fields` collapse means adding a future field is one line in
the handler snapshot, with no change to the cache signature.
- Scope: non-streaming path only (`if self.cache and not stream`). Agent
traffic is largely streaming, so impact is real but bounded — stated
honestly rather than overclaimed.
- Open PR headroomlabs-ai#1250 edits a different cache (`headroom/cache/semantic.py`,
the embeddings layer); it does not touch `proxy/semantic_cache.py`, so
no overlap.
- Pushed with `--no-verify`: the local `make ci-precheck` pre-push hook
fails on an unrelated Rust latency benchmark
(`classify_under_10us_per_call`) that flakes under machine load. This is
a Python-only change; CI runs the benchmark on clean hardware.

Co-authored-by: JD Davis <mxjerrett@gmail.com>
JerrettDavis pushed a commit to peterlodri-sec/headroom that referenced this pull request Jul 14, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>0.29.0</summary>

##
[0.29.0](headroomlabs-ai/headroom@v0.28.0...v0.29.0)
(2026-07-03)


### Features

* **proxy:** add --lossless no-CCR mode with format-native compaction
([headroomlabs-ai#1721](headroomlabs-ai#1721))
([c75ebde](headroomlabs-ai@c75ebde))
* **stats:** surface Codex WS compression counters in /stats summary
([headroomlabs-ai#1680](headroomlabs-ai#1680))
([2fe19c3](headroomlabs-ai@2fe19c3))
* **transforms:** adaptive Otsu KEEP/DROP threshold (+ land relevance
split on main)
([headroomlabs-ai#1726](headroomlabs-ai#1726))
([eea667a](headroomlabs-ai@eea667a))


### Bug Fixes

* **bedrock:** fail fast when session-token auth lacks botocore
([headroomlabs-ai#1553](headroomlabs-ai#1553))
([54cfa36](headroomlabs-ai@54cfa36))
* **bedrock:** route ARNs via converse, named AWS profiles, and au. re…
([headroomlabs-ai#1456](headroomlabs-ai#1456))
([7d87aa2](headroomlabs-ai@7d87aa2))
* **ccr:** honor workspace dir for sqlite store
([headroomlabs-ai#1564](headroomlabs-ai#1564))
([96e1dfe](headroomlabs-ai@96e1dfe))
* **claude:** surface Remote Control proxy incompatibility
([headroomlabs-ai#1610](headroomlabs-ai#1610))
([4bf7f92](headroomlabs-ai@4bf7f92))
* **cli:** stop advertising unwired compression tuning env vars in
banner
([headroomlabs-ai#1634](headroomlabs-ai#1634))
([d5bf98d](headroomlabs-ai@d5bf98d))
* **codex:** avoid duplicate headroom provider config
([headroomlabs-ai#1431](headroomlabs-ai#1431))
([ddd4adf](headroomlabs-ai@ddd4adf))
* **compression:** reject lossy unmarked tool output in unit router path
([headroomlabs-ai#1479](headroomlabs-ai#1479))
([de24cd5](headroomlabs-ai@de24cd5))
* **cortex-code:** migrate to current Cortex REST API endpoints + add
e2e benchmarks
([headroomlabs-ai#1474](headroomlabs-ai#1474))
([f00ace6](headroomlabs-ai@f00ace6))
* **dashboard:** align token savings headline denominator
([headroomlabs-ai#1653](headroomlabs-ai#1653))
([646e705](headroomlabs-ai@646e705))
* **dashboard:** derive per-project setup URL from live origin
([headroomlabs-ai#1511](headroomlabs-ai#1511))
([e035aef](headroomlabs-ai@e035aef))
* **detection:** contain unidiff panic on orphaned +++ target line
([headroomlabs-ai#1548](headroomlabs-ai#1548))
([e386c09](headroomlabs-ai@e386c09))
* **evals:** CJK-aware F1 tokenization + token estimation
([headroomlabs-ai#1527](headroomlabs-ai#1527))
([99a8540](headroomlabs-ai@99a8540))
* **install:** close parent log fd in start_detached_agent
([headroomlabs-ai#1576](headroomlabs-ai#1576))
([816cb85](headroomlabs-ai@816cb85))
* **install:** use Windows-safe PID liveness probe in runtime_status
([headroomlabs-ai#1544](headroomlabs-ai#1544))
([headroomlabs-ai#1560](headroomlabs-ai#1560))
([6b227b9](headroomlabs-ai@6b227b9))
* **learn:** aggregate verbosity baselines across projects instead of
overwriting
([headroomlabs-ai#1288](headroomlabs-ai#1288))
([27a5468](headroomlabs-ai@27a5468))
* **mcp:** show lifetime totals and label rolling session scope in
headroom_stats
([headroomlabs-ai#1428](headroomlabs-ai#1428))
([1c0e152](headroomlabs-ai@1c0e152))
* **memory:** cap local embedder CPU thread oversubscription
([headroomlabs-ai#198](headroomlabs-ai#198))
([headroomlabs-ai#1559](headroomlabs-ai#1559))
([b84afbf](headroomlabs-ai@b84afbf))
* **memory:** singleflight LocalBackend init to stop cold-start races
([headroomlabs-ai#1691](headroomlabs-ai#1691))
([bec47a1](headroomlabs-ai@bec47a1))
* **openclaw:** detect uv-installed headroom binary in ~/.local/bin
([headroomlabs-ai#1459](headroomlabs-ai#1459))
([adaeb88](headroomlabs-ai@adaeb88))
* **opencode:** preserve custom OpenAI gateway paths
([headroomlabs-ai#1596](headroomlabs-ai#1596))
([c19347c](headroomlabs-ai@c19347c))
* **opencode:** route native providers + load transport plugin, fix
Serena context
([headroomlabs-ai#1573](headroomlabs-ai#1573))
([ad0034f](headroomlabs-ai@ad0034f))
* preserve anthropic passthrough tool order
([headroomlabs-ai#1427](headroomlabs-ai#1427))
([a932247](headroomlabs-ai@a932247))
* **proxy/auth:** match real Anthropic OAuth token prefix (sk-ant-oat)
([headroomlabs-ai#1672](headroomlabs-ai#1672))
([8cddf9b](headroomlabs-ai@8cddf9b))
* **proxy:** expose persistent savings metrics
([headroomlabs-ai#1647](headroomlabs-ai#1647))
([5fe4e7b](headroomlabs-ai@5fe4e7b))
* **proxy:** fail open when kompress saturation would exhaust
pre-upstream budget
([headroomlabs-ai#1430](headroomlabs-ai#1430))
([15ac650](headroomlabs-ai@15ac650))
* **proxy:** handle streaming CCR retrieval
([headroomlabs-ai#1451](headroomlabs-ai#1451))
([d337e3b](headroomlabs-ai@d337e3b))
* **proxy:** include system/tools/sampling in cache key
([headroomlabs-ai#1473](headroomlabs-ai#1473))
([312129a](headroomlabs-ai@312129a))
* **proxy:** preserve Responses passthrough bytes
([headroomlabs-ai#1598](headroomlabs-ai#1598))
([2a34a82](headroomlabs-ai@2a34a82))
* **proxy:** strip Codex lite header on the HTTP /responses path
([headroomlabs-ai#1663](headroomlabs-ai#1663))
([9fbd47b](headroomlabs-ai@9fbd47b))
* **proxy:** wire --compression-max-workers /
HEADROOM_COMPRESSION_MAX_WORKERS
([headroomlabs-ai#1632](headroomlabs-ai#1632))
([814ffa3](headroomlabs-ai@814ffa3))
* **savings:** count cache-read tokens in input cost estimate
([headroomlabs-ai#1429](headroomlabs-ai#1429))
([72ade37](headroomlabs-ai@72ade37))
* skip Magika backend on x86 CPUs without AVX2
([headroomlabs-ai#1162](headroomlabs-ai#1162))
([64783d8](headroomlabs-ai@64783d8))
* **transforms/content-router:** route grep/log output away from HTML
extractor
([headroomlabs-ai#1719](headroomlabs-ai#1719))
([0d18ef2](headroomlabs-ai@0d18ef2))
* **transforms:** bound native content detection with a Windows watchdog
([headroomlabs-ai#575](headroomlabs-ai#575))
([headroomlabs-ai#1563](headroomlabs-ai#1563))
([95abca3](headroomlabs-ai@95abca3))
* Vertex AI support for Claude Code with ANTHROPIC_VERTEX_BASE_URL
([headroomlabs-ai#1393](headroomlabs-ai#1393))
([cff7247](headroomlabs-ai@cff7247))
* **wrap:** detach the shared proxy on Windows so it survives an
ungraceful agent close
([headroomlabs-ai#1464](headroomlabs-ai#1464))
([6cba441](headroomlabs-ai@6cba441))
* **wrap:** preserve custom Vertex base URL
([headroomlabs-ai#1477](headroomlabs-ai#1477))
([75427bb](headroomlabs-ai@75427bb))
* **wrap:** remove rtk instructions from Codex AGENTS.md on unwrap
([headroomlabs-ai#1604](headroomlabs-ai#1604))
([c9d717c](headroomlabs-ai@c9d717c))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: ready for review Pull request body is complete and the author marked it ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants