mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
fix(core): drop session ID and order instructions for prompt-cache reuse (#51960)
This commit is contained in:
@@ -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<Instructions.List>
|
||||
readonly load: () => Effect.Effect<Instructions.List>
|
||||
}
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@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(() =>
|
||||
[
|
||||
"<env>",
|
||||
` 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}`,
|
||||
},
|
||||
}),
|
||||
]),
|
||||
),
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:",
|
||||
"<env>",
|
||||
` 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.`,
|
||||
"</env>",
|
||||
"",
|
||||
`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)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -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: () =>
|
||||
|
||||
@@ -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<void>
|
||||
@@ -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"],
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user