Skip to content

fix(cache): content-address prompt_cache_key so recurring cron jobs reuse the warm prefix - #52295

Merged
teknium1 merged 1 commit into
mainfrom
fix/cron-cache-key-content-hash
Jun 25, 2026
Merged

fix(cache): content-address prompt_cache_key so recurring cron jobs reuse the warm prefix#52295
teknium1 merged 1 commit into
mainfrom
fix/cron-cache-key-content-hash

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Recurring cron jobs now share one stable, content-addressed prompt_cache_key across fires instead of going cache-cold every tick.

Root cause: cron builds session_id as cron_<job_id>_<timestamp>, and the Codex/Responses transport used session_id directly as prompt_cache_key. The timestamp changed the key on every fire, so the static prefix (agent identity + tool schemas) was re-tokenised cold each run. Closes #51395.

Changes

  • agent/transports/codex.py: new _content_cache_key(instructions, tools) returning pck_<sha256[:24]> of the static prefix (tools sorted by name; \x00 separator avoids instruction/tool boundary collisions). prompt_cache_key (standard body field + xAI extra_body) now uses it, falling back to session_id only when there is no static content.
  • session_id is unchanged for transcript isolation, logs, and the session-scope routing headers (session_id / x-client-request-id / x-grok-conv-id) — those keep their per-fire identity. Only the cache key is content-addressed.
  • Tests updated to assert the new contract.

Scope note

This is the minimal version. The cache key is a routing hint, never a correctness boundary — a stale/shared key can only cause a miss, never a wrong result. The win is real but bounded by provider cache TTL (~5-60 min), so it primarily helps sub-TTL-interval jobs; longer cadences are cold by the next fire regardless. Kept deliberately small for that reason.

Validation

Before After
Two fires, same job (diff timestamp) different key, cold same pck_ key, warm
Cache key value raw session_id content hash
Different job prompt n/a different key (no collision)
Codex transcript headers session_id session_id (unchanged)

E2E-verified with real transport imports; tests/agent/transports/test_codex_transport.py 60/60 green.

Credit

Closes the cluster on #51395. Fix scoped by @spiky02plateau (#51396, issue author); content-hash key naming from @JoaoMarcos44 (#51585). Both credited as co-authors.

Infographic

stable-prompt-cache-recurring-jobs

…euse the warm prefix

Recurring cron jobs were prompt-cache-cold on every fire. session_id is
built as cron_<job_id>_<timestamp>, and the Codex/Responses transport used
session_id directly as prompt_cache_key — so the timestamp changed the cache
key on every run and the static prefix (agent identity + tool schemas) was
re-paid each tick.

Derive prompt_cache_key from a SHA-256 of the static prefix (instructions +
sorted tool schemas) instead. Repeated fires of the same job share one
content-addressed key (pck_<hash>) and reuse the warm prefix within the
provider's cache TTL. The key changes exactly when the prefix changes —
edit the job's prompt or toolset and it re-keys; leave it alone and it stays
stable.

session_id is left untouched for transcript isolation, log correlation, and
the Codex/xAI session-scope routing headers (session_id, x-client-request-id,
x-grok-conv-id) — those are the per-fire identity, not the cache key. Only the
prompt_cache_key body field (standard OpenAI/Codex path and the xAI extra_body
field) is content-addressed.

Closes #51395.

Co-authored-by: spiky02plateau <spiky02plateau@users.noreply.github.com>
Co-authored-by: JoaoMarcos44 <JoaoMarcos44@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: fix/cron-cache-key-content-hash vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11261 on HEAD, 11259 on base (🆕 +2)

🆕 New issues (2):

Rule Count
unresolved-attribute 2
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`
run_agent.py:2984: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`

✅ Fixed issues (1):

Rule Count
invalid-assignment 1
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`

Unchanged: 5940 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P0 Critical — data loss, security, crash loop sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) labels Jun 25, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved

Clean fix for content-addressed prompt cache key in cron jobs. The solution correctly hashes the static prefix (instructions + tools) to produce a stable cache key across fires, while preserving session_id for transcript isolation. Good test coverage.

Changes:

  • agent/transports/codex.py: New _content_cache_key function, updated build_kwargs to use content-addressed key
  • tests/agent/transports/test_codex_transport.py: Updated tests for new cache key behavior

Well-documented with clear before/after table. LGTM.

Reviewed by Hermes Agent

@teknium1
teknium1 merged commit 7a65800 into main Jun 25, 2026
27 checks passed
@teknium1
teknium1 deleted the fix/cron-cache-key-content-hash branch June 25, 2026 04:46
pai-scaffolde pushed a commit to pai-scaffolde/hermes-agent that referenced this pull request Jun 28, 2026
…euse the warm prefix (NousResearch#52295)

Recurring cron jobs were prompt-cache-cold on every fire. session_id is
built as cron_<job_id>_<timestamp>, and the Codex/Responses transport used
session_id directly as prompt_cache_key — so the timestamp changed the cache
key on every run and the static prefix (agent identity + tool schemas) was
re-paid each tick.

Derive prompt_cache_key from a SHA-256 of the static prefix (instructions +
sorted tool schemas) instead. Repeated fires of the same job share one
content-addressed key (pck_<hash>) and reuse the warm prefix within the
provider's cache TTL. The key changes exactly when the prefix changes —
edit the job's prompt or toolset and it re-keys; leave it alone and it stays
stable.

session_id is left untouched for transcript isolation, log correlation, and
the Codex/xAI session-scope routing headers (session_id, x-client-request-id,
x-grok-conv-id) — those are the per-fire identity, not the cache key. Only the
prompt_cache_key body field (standard OpenAI/Codex path and the xAI extra_body
field) is content-addressed.

Closes NousResearch#51395.

Co-authored-by: spiky02plateau <spiky02plateau@users.noreply.github.com>
Co-authored-by: JoaoMarcos44 <JoaoMarcos44@users.noreply.github.com>
teddyjfpender added a commit to teddyjfpender/superforecasting-agent that referenced this pull request Jun 30, 2026
…the warm prefix

Port of NousResearch/hermes-agent 7a65800 (NousResearch#52295). Every cron fire (cron_{job}_{ts}) and parallel quorum
panelist got a unique session_id prompt_cache_key, defeating provider prefix-cache reuse of the large static
soul/protocol/tool prefix. Now the key is _content_cache_key(instructions, tools) = pck_<sha256[:24]> so runs
sharing the prefix warm each other (xAI extra_body path too); codex-backend headers keep session_id for
per-fire identity. 52 codex-transport tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…euse the warm prefix (NousResearch#52295)

Recurring cron jobs were prompt-cache-cold on every fire. session_id is
built as cron_<job_id>_<timestamp>, and the Codex/Responses transport used
session_id directly as prompt_cache_key — so the timestamp changed the cache
key on every run and the static prefix (agent identity + tool schemas) was
re-paid each tick.

Derive prompt_cache_key from a SHA-256 of the static prefix (instructions +
sorted tool schemas) instead. Repeated fires of the same job share one
content-addressed key (pck_<hash>) and reuse the warm prefix within the
provider's cache TTL. The key changes exactly when the prefix changes —
edit the job's prompt or toolset and it re-keys; leave it alone and it stays
stable.

session_id is left untouched for transcript isolation, log correlation, and
the Codex/xAI session-scope routing headers (session_id, x-client-request-id,
x-grok-conv-id) — those are the per-fire identity, not the cache key. Only the
prompt_cache_key body field (standard OpenAI/Codex path and the xAI extra_body
field) is content-addressed.

Closes NousResearch#51395.

Co-authored-by: spiky02plateau <spiky02plateau@users.noreply.github.com>
Co-authored-by: JoaoMarcos44 <JoaoMarcos44@users.noreply.github.com>
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
…euse the warm prefix (NousResearch#52295)

Recurring cron jobs were prompt-cache-cold on every fire. session_id is
built as cron_<job_id>_<timestamp>, and the Codex/Responses transport used
session_id directly as prompt_cache_key — so the timestamp changed the cache
key on every run and the static prefix (agent identity + tool schemas) was
re-paid each tick.

Derive prompt_cache_key from a SHA-256 of the static prefix (instructions +
sorted tool schemas) instead. Repeated fires of the same job share one
content-addressed key (pck_<hash>) and reuse the warm prefix within the
provider's cache TTL. The key changes exactly when the prefix changes —
edit the job's prompt or toolset and it re-keys; leave it alone and it stays
stable.

session_id is left untouched for transcript isolation, log correlation, and
the Codex/xAI session-scope routing headers (session_id, x-client-request-id,
x-grok-conv-id) — those are the per-fire identity, not the cache key. Only the
prompt_cache_key body field (standard OpenAI/Codex path and the xAI extra_body
field) is content-addressed.

Closes NousResearch#51395.

Co-authored-by: spiky02plateau <spiky02plateau@users.noreply.github.com>
Co-authored-by: JoaoMarcos44 <JoaoMarcos44@users.noreply.github.com>
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…euse the warm prefix (NousResearch#52295)

Recurring cron jobs were prompt-cache-cold on every fire. session_id is
built as cron_<job_id>_<timestamp>, and the Codex/Responses transport used
session_id directly as prompt_cache_key — so the timestamp changed the cache
key on every run and the static prefix (agent identity + tool schemas) was
re-paid each tick.

Derive prompt_cache_key from a SHA-256 of the static prefix (instructions +
sorted tool schemas) instead. Repeated fires of the same job share one
content-addressed key (pck_<hash>) and reuse the warm prefix within the
provider's cache TTL. The key changes exactly when the prefix changes —
edit the job's prompt or toolset and it re-keys; leave it alone and it stays
stable.

session_id is left untouched for transcript isolation, log correlation, and
the Codex/xAI session-scope routing headers (session_id, x-client-request-id,
x-grok-conv-id) — those are the per-fire identity, not the cache key. Only the
prompt_cache_key body field (standard OpenAI/Codex path and the xAI extra_body
field) is content-addressed.

Closes NousResearch#51395.

Co-authored-by: spiky02plateau <spiky02plateau@users.noreply.github.com>
Co-authored-by: JoaoMarcos44 <JoaoMarcos44@users.noreply.github.com>
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…euse the warm prefix (NousResearch#52295)

Recurring cron jobs were prompt-cache-cold on every fire. session_id is
built as cron_<job_id>_<timestamp>, and the Codex/Responses transport used
session_id directly as prompt_cache_key — so the timestamp changed the cache
key on every run and the static prefix (agent identity + tool schemas) was
re-paid each tick.

Derive prompt_cache_key from a SHA-256 of the static prefix (instructions +
sorted tool schemas) instead. Repeated fires of the same job share one
content-addressed key (pck_<hash>) and reuse the warm prefix within the
provider's cache TTL. The key changes exactly when the prefix changes —
edit the job's prompt or toolset and it re-keys; leave it alone and it stays
stable.

session_id is left untouched for transcript isolation, log correlation, and
the Codex/xAI session-scope routing headers (session_id, x-client-request-id,
x-grok-conv-id) — those are the per-fire identity, not the cache key. Only the
prompt_cache_key body field (standard OpenAI/Codex path and the xAI extra_body
field) is content-addressed.

Closes NousResearch#51395.

Co-authored-by: spiky02plateau <spiky02plateau@users.noreply.github.com>
Co-authored-by: JoaoMarcos44 <JoaoMarcos44@users.noreply.github.com>
JoaoMarcos44 added a commit to JoaoMarcos44/hermes-agent that referenced this pull request Aug 5, 2026
…ing key

Nous Portal and OpenRouter provider profiles pin every turn of a
session to the same upstream endpoint (body["session_id"]) so
Anthropic/Vertex/Bedrock cache_control breakpoints stay warm. That key
came straight from get_conversation_context() or session_id with no
normalization, so cron re-fires (cron_<job_id>_<timestamp>, no
parent_session_id to walk) got a fresh key every run and never pinned
to the same endpoint -- the same NousResearch#51395/NousResearch#52295 class of bug the
original prompt_cache_key fix addressed, just on a route NousResearch#78959 didn't
touch.

Reuses _cache_scope_from_session_id() (no new logic) and leaves the
Portal's conversation= analytics tag and the xAI x-grok-conv-id header
untouched, since those need per-fire identity, not cache affinity.
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…euse the warm prefix (NousResearch#52295)

Recurring cron jobs were prompt-cache-cold on every fire. session_id is
built as cron_<job_id>_<timestamp>, and the Codex/Responses transport used
session_id directly as prompt_cache_key — so the timestamp changed the cache
key on every run and the static prefix (agent identity + tool schemas) was
re-paid each tick.

Derive prompt_cache_key from a SHA-256 of the static prefix (instructions +
sorted tool schemas) instead. Repeated fires of the same job share one
content-addressed key (pck_<hash>) and reuse the warm prefix within the
provider's cache TTL. The key changes exactly when the prefix changes —
edit the job's prompt or toolset and it re-keys; leave it alone and it stays
stable.

session_id is left untouched for transcript isolation, log correlation, and
the Codex/xAI session-scope routing headers (session_id, x-client-request-id,
x-grok-conv-id) — those are the per-fire identity, not the cache key. Only the
prompt_cache_key body field (standard OpenAI/Codex path and the xAI extra_body
field) is content-addressed.

Closes NousResearch#51395.

Co-authored-by: spiky02plateau <spiky02plateau@users.noreply.github.com>
Co-authored-by: JoaoMarcos44 <JoaoMarcos44@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P0 Critical — data loss, security, crash loop sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recurring cron jobs are prompt-cache-cold on every fire (session_id is the cache key)

3 participants