Description
SystemPrompt.environment() emits Today's date: ${new Date().toDateString()} at packages/opencode/src/session/system.ts:70. env is the first element of the system array built in session/prompt.ts (const system = [...env, ...instructions, ...]), so the date ends up in the first role: "system" message.
applyCaching() in provider/transform.ts:323 takes the first two system messages (.slice(0, 2)) and attaches cacheControl: { type: "ephemeral" }. The date is therefore inside the cache-controlled prefix.
toDateString() rolls over at local midnight. A session that stays within the cache TTL but spans midnight sends a different system prefix on the next request — cache miss, and the whole system block is re-billed as a write instead of a read. It's the only daily-changing token in that block (model id, cwd, platform, git status are all session-stable), so caching is defeated exactly for the long-running sessions that benefit most.
Steps to reproduce
- Use a caching-capable model (e.g. Anthropic-family).
- Keep a session active across local midnight, within the cache TTL.
- The next request reports a cache write for the system prefix rather than a read.
Suggested fix
Keep the date out of the cached prefix — e.g. move it from environment() into the trailing user message (after the cache breakpoint). The model still receives the date, but the cached system bytes become date-invariant.
Operating System
Platform-independent
Description
SystemPrompt.environment()emitsToday's date: ${new Date().toDateString()}at packages/opencode/src/session/system.ts:70.envis the first element of thesystemarray built in session/prompt.ts (const system = [...env, ...instructions, ...]), so the date ends up in the firstrole: "system"message.applyCaching()in provider/transform.ts:323 takes the first two system messages (.slice(0, 2)) and attachescacheControl: { type: "ephemeral" }. The date is therefore inside the cache-controlled prefix.toDateString()rolls over at local midnight. A session that stays within the cache TTL but spans midnight sends a different system prefix on the next request — cache miss, and the whole system block is re-billed as a write instead of a read. It's the only daily-changing token in that block (model id, cwd, platform, git status are all session-stable), so caching is defeated exactly for the long-running sessions that benefit most.Steps to reproduce
Suggested fix
Keep the date out of the cached prefix — e.g. move it from
environment()into the trailing user message (after the cache breakpoint). The model still receives the date, but the cached system bytes become date-invariant.Operating System
Platform-independent