From fbcdb531c5e6775c27aa02597441a2b5f691e58a Mon Sep 17 00:00:00 2001 From: Yi LIU Date: Wed, 17 Jun 2026 09:31:21 +0800 Subject: [PATCH 1/3] fix(opencode): move volatile date out of cached system prefix environment() emitted the daily-changing date as the first entry in the system[] array, which applyCaching() marks with cacheControl. A session crossing midnight within the cache TTL invalidated the whole system prefix. Carry the date in the trailing user message instead, past the cache breakpoint, so the cached system bytes are date-invariant. Fixes #949 --- packages/opencode/src/session/prompt.ts | 18 ++++++++++++++++++ packages/opencode/src/session/system.ts | 13 ++++++++++++- packages/opencode/test/session/system.test.ts | 14 ++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 99aefe46b..af9cf2c31 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -969,6 +969,24 @@ export namespace SessionPrompt { await Plugin.trigger("experimental.chat.messages.transform", {}, { messages: msgs }) + // altimate_change start — upstream_fix: carry the date in the trailing user message + // so it stays out of the cache-controlled system prefix (see session/system.ts). + const lastUserForDate = msgs.findLast((m) => m.info.role === "user") + if (lastUserForDate) { + lastUserForDate.parts = [ + ...lastUserForDate.parts, + { + type: "text" as const, + id: PartID.ascending(), + sessionID, + messageID: lastUserForDate.info.id, + text: `\n\n${SystemPrompt.currentDate()}`, + synthetic: true, + }, + ] + } + // altimate_change end + // Build system prompt, adding structured output instruction if needed const skills = await SystemPrompt.skills(agent) // altimate_change start - unified context-aware injection for memory + training diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index 475d595c1..f468d4b64 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -75,7 +75,12 @@ export namespace SystemPrompt { ` Workspace root folder: ${Instance.worktree}`, ` Is directory a git repo: ${project.vcs === "git" ? "yes" : "no"}`, ` Platform: ${process.platform}`, - ` Today's date: ${new Date().toDateString()}`, + // altimate_change start — upstream_fix: move volatile date out of cached system prefix + // The date changes daily but environment() is the first entry in the system[] + // array, which applyCaching() marks with cacheControl. A session crossing midnight + // within the cache TTL would otherwise invalidate the whole system prefix. The date + // is appended to the trailing user message instead (see session/prompt.ts). + // altimate_change end ``, ``, ` ${ @@ -91,6 +96,12 @@ export namespace SystemPrompt { ] } + // altimate_change start — upstream_fix: date carried outside the cached system prefix + export function currentDate() { + return `Today's date is ${new Date().toDateString()}.` + } + // altimate_change end + export async function skills(agent: Agent.Info) { if (PermissionNext.disabled(["skill"], agent.permission).has("skill")) return diff --git a/packages/opencode/test/session/system.test.ts b/packages/opencode/test/session/system.test.ts index a6568b947..c6a2dc55d 100644 --- a/packages/opencode/test/session/system.test.ts +++ b/packages/opencode/test/session/system.test.ts @@ -160,4 +160,18 @@ description: ${description} process.env.OPENCODE_TEST_HOME = home } }) + + test("environment() keeps the volatile date out of the cached system prefix", async () => { + await using tmp = await tmpdir({ git: true }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const [env] = await SystemPrompt.environment(makeModel({ apiId: "claude-3-7-sonnet" })) + expect(env).toMatch(//) + expect(env).not.toMatch(/Today's date/) + expect(env).not.toContain(new Date().toDateString()) + expect(SystemPrompt.currentDate()).toContain(new Date().toDateString()) + }, + }) + }) }) From 3e55e85570524b82c2834df5169325bd810e40e0 Mon Sep 17 00:00:00 2001 From: Yi LIU Date: Wed, 17 Jun 2026 09:35:38 +0800 Subject: [PATCH 2/3] test(opencode): capture today's date once to avoid midnight flake --- packages/opencode/test/session/system.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/opencode/test/session/system.test.ts b/packages/opencode/test/session/system.test.ts index c6a2dc55d..c465a9895 100644 --- a/packages/opencode/test/session/system.test.ts +++ b/packages/opencode/test/session/system.test.ts @@ -166,11 +166,12 @@ description: ${description} await Instance.provide({ directory: tmp.path, fn: async () => { + const today = new Date().toDateString() const [env] = await SystemPrompt.environment(makeModel({ apiId: "claude-3-7-sonnet" })) expect(env).toMatch(//) expect(env).not.toMatch(/Today's date/) - expect(env).not.toContain(new Date().toDateString()) - expect(SystemPrompt.currentDate()).toContain(new Date().toDateString()) + expect(env).not.toContain(today) + expect(SystemPrompt.currentDate()).toContain(today) }, }) }) From c659612b80f0fbb620687c5613ac16fc315e47d5 Mon Sep 17 00:00:00 2001 From: sumleo Date: Wed, 17 Jun 2026 11:06:50 +0800 Subject: [PATCH 3/3] test(opencode): freeze system time in date cache test to remove midnight race --- packages/opencode/test/session/system.test.ts | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/opencode/test/session/system.test.ts b/packages/opencode/test/session/system.test.ts index c465a9895..802eda4a3 100644 --- a/packages/opencode/test/session/system.test.ts +++ b/packages/opencode/test/session/system.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect, setSystemTime, test } from "bun:test" import path from "path" import { Agent } from "../../src/agent/agent" import { Instance } from "../../src/project/instance" @@ -162,17 +162,25 @@ description: ${description} }) test("environment() keeps the volatile date out of the cached system prefix", async () => { - await using tmp = await tmpdir({ git: true }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const today = new Date().toDateString() - const [env] = await SystemPrompt.environment(makeModel({ apiId: "claude-3-7-sonnet" })) - expect(env).toMatch(//) - expect(env).not.toMatch(/Today's date/) - expect(env).not.toContain(today) - expect(SystemPrompt.currentDate()).toContain(today) - }, - }) + // Freeze the clock so the captured `today` and the `new Date()` inside + // currentDate() read the same instant — otherwise the assertion can race + // across midnight. + setSystemTime(new Date("2026-06-17T12:00:00.000Z")) + try { + await using tmp = await tmpdir({ git: true }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const today = new Date().toDateString() + const [env] = await SystemPrompt.environment(makeModel({ apiId: "claude-3-7-sonnet" })) + expect(env).toMatch(//) + expect(env).not.toMatch(/Today's date/) + expect(env).not.toContain(today) + expect(SystemPrompt.currentDate()).toContain(today) + }, + }) + } finally { + setSystemTime() + } }) })