fix(core): restore v2 model prompts (#34275)

Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot] 2026-06-28 14:47:20 -05:00 committed by GitHub
commit 6067019434
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 1000 additions and 24 deletions

View file

@ -122,6 +122,7 @@ describe("AgentV2", () => {
"summary",
"title",
])
expect((yield* agent.get(AgentV2.defaultID))?.system).toBeUndefined()
for (const item of agents) {
expect(item.permissions.some((rule) => rule.action === "bash" && rule.effect !== "deny")).toBe(false)
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,21 @@
import { describe, expect, test } from "bun:test"
import { Model } from "@opencode-ai/llm"
import * as OpenAIChat from "@opencode-ai/llm/protocols/openai-chat"
import { SessionRunnerSystemPrompt } from "@opencode-ai/core/session/runner/system-prompt"
const prompt = (id: string) =>
SessionRunnerSystemPrompt.provider(Model.make({ id, provider: "test", route: OpenAIChat.route }))
describe("SessionRunnerSystemPrompt", () => {
test("selects the legacy provider-family prompts from the model id", () => {
expect(prompt("gpt-5")).toContain("You are OpenCode, You and the user share the same workspace")
expect(prompt("gpt-4.1")).toContain("THE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH")
expect(prompt("o3")).toContain("THE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH")
expect(prompt("gpt-5-codex")).toContain("## Editing constraints")
expect(prompt("gemini-2.5-pro")).toContain("# Core Mandates")
expect(prompt("claude-sonnet-4")).toContain("# Professional objectivity")
expect(prompt("kimi-k2")).toContain("# Prompt and Tool Use")
expect(prompt("trinity")).toContain("what command should I run to list files")
expect(prompt("llama-3.3")).toContain("You are opencode, an interactive CLI tool")
})
})

View file

@ -32,6 +32,7 @@ import { SessionRunCoordinator } from "@opencode-ai/core/session/run-coordinator
import { SessionRunner } from "@opencode-ai/core/session/runner"
import * as SessionRunnerLLM from "@opencode-ai/core/session/runner/llm"
import { SessionRunnerModel } from "@opencode-ai/core/session/runner/model"
import { SessionRunnerSystemPrompt } from "@opencode-ai/core/session/runner/system-prompt"
import { ToolRegistry } from "@opencode-ai/core/tool/registry"
import { ToolOutputStore } from "@opencode-ai/core/tool-output-store"
import { ApplicationTools } from "@opencode-ai/core/tool/application-tools"
@ -96,6 +97,7 @@ const client = Layer.succeed(
}),
)
const model = Model.make({ id: "fake-model", provider: "fake", route: OpenAIChat.route })
const defaultSystem = SessionRunnerSystemPrompt.provider(model)
const replacementModel = Model.make({ id: "replacement", provider: "fake", route: OpenAIChat.route })
const compactModel = Model.make({
id: "compact",
@ -779,8 +781,8 @@ describe("SessionRunnerLLM", () => {
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
["Initial context"],
["Initial context"],
[defaultSystem, "Initial context"],
[defaultSystem, "Initial context"],
])
expect(requests[1]?.messages.map((message) => message.role)).toEqual(["user", "user", "system"])
expect(requests[1]?.messages.at(-1)?.content).toEqual([{ type: "text", text: "Changed context" }])
@ -799,6 +801,49 @@ describe("SessionRunnerLLM", () => {
}),
)
it.effect("uses the selected model family prompt when the agent does not override it", () =>
Effect.gen(function* () {
yield* setup
currentModel = Model.make({ id: "gpt-5", provider: "openai", route: OpenAIChat.route })
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = fragmentFixture("text", "text-provider-prompt", ["Done"]).completeEvents
yield* session.resume(sessionID)
expect(requests.at(-1)?.system.map((part) => part.text)).toEqual([
expect.stringContaining("You are OpenCode, You and the user share the same workspace"),
"Initial context",
])
}),
)
it.effect("uses the selected model family prompt when the agent system override is empty", () =>
Effect.gen(function* () {
yield* setup
currentModel = Model.make({ id: "gpt-5", provider: "openai", route: OpenAIChat.route })
const agent = yield* AgentV2.Service
yield* agent.transform((editor) =>
editor.update(AgentV2.ID.make("build"), (agent) => {
agent.system = ""
agent.mode = "primary"
}),
)
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = fragmentFixture("text", "text-empty-agent-system", ["Done"]).completeEvents
yield* session.resume(sessionID)
expect(requests.at(-1)?.system.map((part) => part.text)).toEqual([
expect.stringContaining("You are OpenCode, You and the user share the same workspace"),
"Initial context",
])
}),
)
it.effect("includes the effective default agent system before durable context", () =>
Effect.gen(function* () {
yield* setup
@ -951,8 +996,8 @@ describe("SessionRunnerLLM", () => {
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
["Initial context\n\nBuild skills"],
["Initial context\n\nBuild skills"],
[defaultSystem, "Initial context\n\nBuild skills"],
[defaultSystem, "Initial context\n\nBuild skills"],
])
expect(systemTexts(requests[1]!)).toContainEqual(expect.stringContaining("Reviewer skills"))
}),
@ -985,7 +1030,7 @@ describe("SessionRunnerLLM", () => {
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
["Initial context\n\nBuild skills"],
[defaultSystem, "Initial context\n\nBuild skills"],
])
}),
)
@ -1014,7 +1059,9 @@ describe("SessionRunnerLLM", () => {
response = []
yield* session.resume(sessionID)
expect(requests.map((request) => request.model)).toEqual([model])
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([["Initial context"]])
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
[defaultSystem, "Initial context"],
])
}),
)
@ -1063,9 +1110,9 @@ describe("SessionRunnerLLM", () => {
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
["Initial context"],
["Initial context"],
["Initial context"],
[defaultSystem, "Initial context"],
[defaultSystem, "Initial context"],
[defaultSystem, "Initial context"],
])
expect(requests[1]?.messages.map((message) => message.role)).toEqual(["user", "user", "system"])
expect(requests[2]?.messages.filter((message) => message.role === "system")).toHaveLength(2)
@ -1109,9 +1156,9 @@ describe("SessionRunnerLLM", () => {
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
["Initial context"],
["Initial context"],
["Initial context"],
[defaultSystem, "Initial context"],
[defaultSystem, "Initial context"],
[defaultSystem, "Initial context"],
])
}),
)
@ -1146,8 +1193,8 @@ describe("SessionRunnerLLM", () => {
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
["Initial context"],
["Replacement context"],
[defaultSystem, "Initial context"],
[defaultSystem, "Replacement context"],
])
yield* replaySessionProjection(sessionID)
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Third" }), resume: false })
@ -1375,7 +1422,7 @@ describe("SessionRunnerLLM", () => {
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Third" }), resume: false })
yield* session.resume(sessionID)
expect(requests.at(-1)?.system.map((part) => part.text)).toEqual(["Initial context"])
expect(requests.at(-1)?.system.map((part) => part.text)).toEqual([defaultSystem, "Initial context"])
expect(systemTexts(requests.at(-1)!)).toContain("Changed context")
}),
)
@ -1574,8 +1621,8 @@ describe("SessionRunnerLLM", () => {
expect(requests.map((request) => request.model)).toEqual([model, replacementModel])
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
["Initial context"],
["Initial context"],
[defaultSystem, "Initial context"],
[defaultSystem, "Initial context"],
])
expect(systemTexts(requests[1]!)).toContain("Replacement context")
}),