From bda798b16759aaa6a6f9394e722b157f6f824553 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:29:28 -0500 Subject: [PATCH] fix(core): drop session ID and order instructions for prompt-cache reuse (#51960) --- packages/core/src/instructions/builtins.ts | 24 +++++++++---------- packages/core/src/session/context.ts | 13 ++++++---- .../core/test/instructions/builtins.test.ts | 17 ++++++------- packages/core/test/session-generate.test.ts | 2 +- packages/core/test/session-runner.test.ts | 8 +++---- 5 files changed, 31 insertions(+), 33 deletions(-) diff --git a/packages/core/src/instructions/builtins.ts b/packages/core/src/instructions/builtins.ts index 47c8e5c7515..fab4b8dd8cb 100644 --- a/packages/core/src/instructions/builtins.ts +++ b/packages/core/src/instructions/builtins.ts @@ -2,13 +2,12 @@ export * as InstructionBuiltIns from "./builtins.js" import { makeLocationNode } from "@opencode/util/effect/app-node" import { Context, DateTime, Effect, Layer, Schema } from "effect" -import type { Session } from "@opencode/schema/session" import { Global } from "@opencode/util/global" import { Location } from "../location.js" import { Instructions } from "./index.js" export interface Interface { - readonly load: (sessionID: Session.ID) => Effect.Effect + readonly load: () => Effect.Effect } export class Service extends Context.Service()("@opencode/InstructionBuiltIns") {} @@ -19,16 +18,24 @@ const layer = Layer.effect( const global = yield* Global.Service const location = yield* Location.Service return Service.of({ - load: (sessionID) => + load: () => Effect.succeed( Instructions.combine([ + Instructions.make({ + key: Instructions.Key.make("core/date"), + codec: Schema.toCodecJson(Schema.String), + read: DateTime.nowAsDate.pipe(Effect.map((date) => date.toDateString())), + render: { + initial: (date) => `Today's date: ${date}`, + changed: (_previous, date) => `Today's date is now: ${date}`, + }, + }), Instructions.make({ key: Instructions.Key.make("core/environment"), codec: Schema.toCodecJson(Schema.String), read: Effect.sync(() => [ "", - ` Current conversation session ID: ${sessionID}`, ` Working directory: ${location.directory}`, ` Workspace root folder: ${location.project.directory}`, ` Is directory a git repo: ${location.vcs?.type === "git" ? "yes" : "no"}`, @@ -44,15 +51,6 @@ const layer = Layer.effect( ["The environment you are running in is now:", environment].join("\n"), }, }), - Instructions.make({ - key: Instructions.Key.make("core/date"), - codec: Schema.toCodecJson(Schema.String), - read: DateTime.nowAsDate.pipe(Effect.map((date) => date.toDateString())), - render: { - initial: (date) => `Today's date: ${date}`, - changed: (_previous, date) => `Today's date is now: ${date}`, - }, - }), ]), ), }) diff --git a/packages/core/src/session/context.ts b/packages/core/src/session/context.ts index 24c71d8fe60..e8cf2455bfa 100644 --- a/packages/core/src/session/context.ts +++ b/packages/core/src/session/context.ts @@ -132,7 +132,7 @@ const layer = Layer.effect( const loaded = yield* Effect.all( { tools: registry.snapshot(permissions), - builtins: builtins.load(sessionID), + builtins: builtins.load(), discovery: discovery.load(), skills: skillInstructions.load(permissions), references: referenceInstructions.load(), @@ -144,13 +144,16 @@ const layer = Layer.effect( return { session, agent: { ...agent, info: agent.info }, + // Ordered from most to least shared across sessions so the baseline stays a reusable + // prompt-cache prefix: user-level catalog and guidance, then project instructions, then + // the date and environment, which vary by day and directory. instructions: Instructions.combine([ - loaded.builtins, CodeModeInstructions.make(loaded.tools.codeModeCatalog), - loaded.discovery, - loaded.skills, - loaded.references, loaded.mcp, + loaded.references, + loaded.skills, + loaded.discovery, + loaded.builtins, loaded.entries, ]), tools: loaded.tools, diff --git a/packages/core/test/instructions/builtins.test.ts b/packages/core/test/instructions/builtins.test.ts index c6e7df1b8e6..005abe45dc6 100644 --- a/packages/core/test/instructions/builtins.test.ts +++ b/packages/core/test/instructions/builtins.test.ts @@ -8,7 +8,6 @@ import { FSUtil } from "@opencode/util/fs-util" import { Global } from "@opencode/util/global" import { AbsolutePath } from "@opencode/core/schema" import { InstructionBuiltIns } from "@opencode/core/instructions/builtins" -import { SessionSchema } from "@opencode/core/session/schema" import { location } from "../fixture/location" import { testEffect } from "../lib/effect" import { readInitial, readUpdate } from "../lib/instructions" @@ -16,7 +15,6 @@ import { readInitial, readUpdate } from "../lib/instructions" const directory = AbsolutePath.make(FSUtil.resolve("/repo/packages/core")) const projectDirectory = AbsolutePath.make(FSUtil.resolve("/repo")) const timestamp = Date.parse("2026-06-03T12:00:00.000Z") -const sessionID = SessionSchema.ID.make("ses_builtin_test") const temporary = os.tmpdir() const localDate = (time: number) => new Date(time).toDateString() const locationLayer = Layer.succeed( @@ -40,21 +38,20 @@ describe("InstructionBuiltIns", () => { Effect.gen(function* () { yield* TestClock.setTime(timestamp) const context = yield* InstructionBuiltIns.Service - const initialized = yield* readInitial(yield* context.load(sessionID)) + const initialized = yield* readInitial(yield* context.load()) expect(initialized.text).toBe( [ + `Today's date: ${localDate(timestamp)}`, + "", "Here is some useful information about the environment you are running in:", "", - ` Current conversation session ID: ${sessionID}`, ` Working directory: ${directory}`, ` Workspace root folder: ${projectDirectory}`, " Is directory a git repo: yes", ` Platform: ${process.platform}`, ` Prefer ${temporary} over generic system temporary directories such as /tmp; it is pre-created and approved for external access.`, "", - "", - `Today's date: ${localDate(timestamp)}`, ].join("\n"), ) }), @@ -64,10 +61,10 @@ describe("InstructionBuiltIns", () => { Effect.gen(function* () { yield* TestClock.setTime(timestamp) const context = yield* InstructionBuiltIns.Service - const initialized = yield* readInitial(yield* context.load(sessionID)) + const initialized = yield* readInitial(yield* context.load()) yield* TestClock.setTime(timestamp + 24 * 60 * 60 * 1000) - const refreshed = yield* readUpdate(yield* context.load(sessionID), initialized) + const refreshed = yield* readUpdate(yield* context.load(), initialized) expect(refreshed.text).toBe(`Today's date is now: ${localDate(timestamp + 24 * 60 * 60 * 1000)}`) }), @@ -77,10 +74,10 @@ describe("InstructionBuiltIns", () => { Effect.gen(function* () { yield* TestClock.setTime(timestamp) const context = yield* InstructionBuiltIns.Service - const initialized = yield* readInitial(yield* context.load(sessionID)) + const initialized = yield* readInitial(yield* context.load()) yield* TestClock.setTime(timestamp + 60 * 60 * 1000) - expect((yield* readUpdate(yield* context.load(sessionID), initialized)).changed).toBe(false) + expect((yield* readUpdate(yield* context.load(), initialized)).changed).toBe(false) }), ) }) diff --git a/packages/core/test/session-generate.test.ts b/packages/core/test/session-generate.test.ts index ee63a20ad17..1e3f3aeeddd 100644 --- a/packages/core/test/session-generate.test.ts +++ b/packages/core/test/session-generate.test.ts @@ -236,7 +236,7 @@ const setup = Effect.gen(function* () { db, bus, session, - instructions: yield* instructionBuiltIns.load(sessionID), + instructions: yield* instructionBuiltIns.load(), instances: Instance.Service.of({ // Generation only exercises the Location's model context. provide: () => diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts index baede7cf602..5023d5c397e 100644 --- a/packages/core/test/session-runner.test.ts +++ b/packages/core/test/session-runner.test.ts @@ -87,7 +87,7 @@ import { permissionLayer } from "./lib/permission" import { agentHost, modelHost, host, noProviders } from "./plugin/host" import { CodeModeInstructions } from "@opencode/core/codemode/instructions" -const emptyCodeMode = `\n\n${CodeModeInstructions.render({ total: 0, shown: 0, namespaces: [] })}` +const emptyCodeMode = `${CodeModeInstructions.render({ total: 0, shown: 0, namespaces: [] })}\n\n` type ToolBarrier = { readonly count: number readonly started: Deferred.Deferred @@ -1875,8 +1875,8 @@ describe("SessionRunnerLLM", () => { yield* s.runPrompt("Second") expect(s.requests.map((request) => request.system.map((part) => part.text))).toEqual([ - [defaultSystem, fakeIdentity, "Initial context\n\nBuild skills"], - [defaultSystem, fakeIdentity, "Initial context\n\nBuild skills"], + [defaultSystem, fakeIdentity, "Build skills\n\nInitial context"], + [defaultSystem, fakeIdentity, "Build skills\n\nInitial context"], ]) expect(systemTexts(s.requests[1])).toContainEqual(expect.stringContaining("Reviewer skills")) }) @@ -1898,7 +1898,7 @@ describe("SessionRunnerLLM", () => { yield* s.runPrompt("First") expect(s.requests.map((request) => request.system.map((part) => part.text))).toEqual([ - [defaultSystem, fakeIdentity, "Initial context\n\nBuild skills"], + [defaultSystem, fakeIdentity, "Build skills\n\nInitial context"], ]) })