Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

findLast is already used elsewhere in this file (the reminder-wrapping code just above), so the build target already supports it - kept it for consistency.

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,
},
]
}
Comment on lines +974 to +987

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

loop() already throws "No user message found" earlier if msgs has no user entry, so there's always a trailing user message to append to at this point. The date can't be silently dropped.

// 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
Expand Down
13 changes: 12 additions & 1 deletion packages/opencode/src/session/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
`</env>`,
`<directories>`,
` ${
Expand All @@ -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

Expand Down
25 changes: 24 additions & 1 deletion packages/opencode/test/session/system.test.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -160,4 +160,27 @@ description: ${description}
process.env.OPENCODE_TEST_HOME = home
}
})

test("environment() keeps the volatile date out of the cached system prefix", async () => {
// 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(/<env>/)
expect(env).not.toMatch(/Today's date/)
expect(env).not.toContain(today)
expect(SystemPrompt.currentDate()).toContain(today)
},
})
} finally {
setSystemTime()
}
})
Comment on lines +164 to +185

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch - captured the date once at the top of the test so all three assertions compare the same value.

})
Loading