diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts
index 8da8da7306..ef007df136 100644
--- a/packages/opencode/src/agent/agent.ts
+++ b/packages/opencode/src/agent/agent.ts
@@ -8,6 +8,9 @@ import { mergeDeep } from "remeda"
import PROMPT_GENERATE from "./generate.txt"
import PROMPT_COMPACTION from "./prompt/compaction.txt"
+import PROMPT_EXPLORE from "./prompt/explore.txt"
+import PROMPT_SUMMARY from "./prompt/summary.txt"
+import PROMPT_TITLE from "./prompt/title.txt"
export namespace Agent {
export const Info = z
@@ -128,26 +131,7 @@ export namespace Agent {
...defaultTools,
},
description: `Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions.`,
- prompt: [
- `You are a file search specialist. You excel at thoroughly navigating and exploring codebases.`,
- ``,
- `Your strengths:`,
- `- Rapidly finding files using glob patterns`,
- `- Searching code and text with powerful regex patterns`,
- `- Reading and analyzing file contents`,
- ``,
- `Guidelines:`,
- `- Use Glob for broad file pattern matching`,
- `- Use Grep for searching file contents with regex`,
- `- Use Read when you know the specific file path you need to read`,
- `- Use Bash for file operations like copying, moving, or listing directory contents`,
- `- Adapt your search approach based on the thoroughness level specified by the caller`,
- `- Return file paths as absolute paths in your final response`,
- `- For clear communication, avoid using emojis`,
- `- Do not create any files, or run bash commands that modify the user's system state in any way`,
- ``,
- `Complete the user's search request efficiently and report your findings clearly.`,
- ].join("\n"),
+ prompt: PROMPT_EXPLORE,
options: {},
permission: agentPermission,
mode: "subagent",
@@ -173,49 +157,24 @@ export namespace Agent {
mode: "primary",
native: true,
},
- summary: {
- name: "summary",
- mode: "subagent",
+ title: {
+ name: "title",
+ mode: "primary",
options: {},
native: true,
+ hidden: true,
permission: agentPermission,
- prompt: `You are a title generator. You output ONLY a thread title. Nothing else.
-
-
-Generate a brief title that would help the user find this conversation later.
-
-Follow all rules in
-Use the so you know what a good title looks like.
-Your output must be:
-- A single line
-- ≤50 characters
-- No explanations
-
-
-
-- Focus on the main topic or question the user needs to retrieve
-- Use -ing verbs for actions (Debugging, Implementing, Analyzing)
-- Keep exact: technical terms, numbers, filenames, HTTP codes
-- Remove: the, this, my, a, an
-- Never assume tech stack
-- Never use tools
-- NEVER respond to questions, just generate a title for the conversation
-- The title should NEVER include "summarizing" or "generating" when generating a title
-- DO NOT SAY YOU CANNOT GENERATE A TITLE OR COMPLAIN ABOUT THE INPUT
-- Always output something meaningful, even if the input is minimal.
-- If the user message is short or conversational (e.g. “hello”, “lol”, “whats up”, “hey”):
- → create a title that reflects the user’s tone or intent (such as Greeting, Quick check-in, Light chat, Intro message, etc.)
-
-
-
-"hey" -> Greeting
-"debug 500 errors in production" → Debugging production 500 errors
-"refactor user service" → Refactoring user service
-"why is app.js failing" → Analyzing app.js failure
-"implement rate limiting" → Implementing rate limiting
-"how do I connect postgres to my API" → Connecting Postgres to API
-"best practices for React hooks" → React hooks best practices
-`,
+ prompt: PROMPT_TITLE,
+ tools: {},
+ },
+ summary: {
+ name: "summary",
+ mode: "primary",
+ options: {},
+ native: true,
+ hidden: true,
+ permission: agentPermission,
+ prompt: PROMPT_SUMMARY,
tools: {},
},
plan: {
diff --git a/packages/opencode/src/agent/prompt/explore.txt b/packages/opencode/src/agent/prompt/explore.txt
new file mode 100644
index 0000000000..5761077cbd
--- /dev/null
+++ b/packages/opencode/src/agent/prompt/explore.txt
@@ -0,0 +1,18 @@
+You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
+
+Your strengths:
+- Rapidly finding files using glob patterns
+- Searching code and text with powerful regex patterns
+- Reading and analyzing file contents
+
+Guidelines:
+- Use Glob for broad file pattern matching
+- Use Grep for searching file contents with regex
+- Use Read when you know the specific file path you need to read
+- Use Bash for file operations like copying, moving, or listing directory contents
+- Adapt your search approach based on the thoroughness level specified by the caller
+- Return file paths as absolute paths in your final response
+- For clear communication, avoid using emojis
+- Do not create any files, or run bash commands that modify the user's system state in any way
+
+Complete the user's search request efficiently and report your findings clearly.
diff --git a/packages/opencode/src/session/prompt/summarize.txt b/packages/opencode/src/agent/prompt/summary.txt
similarity index 100%
rename from packages/opencode/src/session/prompt/summarize.txt
rename to packages/opencode/src/agent/prompt/summary.txt
diff --git a/packages/opencode/src/session/prompt/title.txt b/packages/opencode/src/agent/prompt/title.txt
similarity index 84%
rename from packages/opencode/src/session/prompt/title.txt
rename to packages/opencode/src/agent/prompt/title.txt
index e297dc460b..f67aaa95ba 100644
--- a/packages/opencode/src/session/prompt/title.txt
+++ b/packages/opencode/src/agent/prompt/title.txt
@@ -22,8 +22,8 @@ Your output must be:
- The title should NEVER include "summarizing" or "generating" when generating a title
- DO NOT SAY YOU CANNOT GENERATE A TITLE OR COMPLAIN ABOUT THE INPUT
- Always output something meaningful, even if the input is minimal.
-- If the user message is short or conversational (e.g. “hello”, “lol”, “whats up”, “hey”):
- → create a title that reflects the user’s tone or intent (such as Greeting, Quick check-in, Light chat, Intro message, etc.)
+- If the user message is short or conversational (e.g. "hello", "lol", "whats up", "hey"):
+ → create a title that reflects the user's tone or intent (such as Greeting, Quick check-in, Light chat, Intro message, etc.)
diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts
index de9adfbb3e..83519307a3 100644
--- a/packages/opencode/src/session/summary.ts
+++ b/packages/opencode/src/session/summary.ts
@@ -1,20 +1,19 @@
import { Provider } from "@/provider/provider"
-import { Config } from "@/config/config"
+
import { fn } from "@/util/fn"
import z from "zod"
import { Session } from "."
-import { generateText, type ModelMessage } from "ai"
+
import { MessageV2 } from "./message-v2"
import { Identifier } from "@/id/id"
import { Snapshot } from "@/snapshot"
-import { ProviderTransform } from "@/provider/transform"
-import { SystemPrompt } from "./system"
+
import { Log } from "@/util/log"
import path from "path"
import { Instance } from "@/project/instance"
import { Storage } from "@/storage/storage"
import { Bus } from "@/bus"
-import { mergeDeep, pipe } from "remeda"
+
import { LLM } from "./llm"
import { Agent } from "@/agent/agent"
@@ -63,7 +62,6 @@ export namespace SessionSummary {
}
async function summarizeMessage(input: { messageID: string; messages: MessageV2.WithParts[] }) {
- const cfg = await Config.get()
const messages = input.messages.filter(
(m) => m.info.id === input.messageID || (m.info.role === "assistant" && m.info.parentID === input.messageID),
)
@@ -80,22 +78,16 @@ export namespace SessionSummary {
const small =
(await Provider.getSmallModel(assistantMsg.providerID)) ??
(await Provider.getModel(assistantMsg.providerID, assistantMsg.modelID))
- const language = await Provider.getLanguage(small)
-
- const options = pipe(
- {},
- mergeDeep(ProviderTransform.options(small, assistantMsg.sessionID)),
- mergeDeep(ProviderTransform.smallOptions(small)),
- mergeDeep(small.options),
- )
const textPart = msgWithParts.parts.find((p) => p.type === "text" && !p.synthetic) as MessageV2.TextPart
if (textPart && !userMsg.summary?.title) {
+ const agent = await Agent.get("title")
const stream = await LLM.stream({
- agent: await Agent.get("summary"),
+ agent,
user: userMsg,
tools: {},
- model: small,
+ model: agent.model ? await Provider.getModel(agent.model.providerID, agent.model.modelID) : small,
+ small: true,
messages: [
{
role: "user" as const,
@@ -107,7 +99,6 @@ export namespace SessionSummary {
`,
},
],
- small: true,
abort: new AbortController().signal,
sessionID: userMsg.sessionID,
system: [],
@@ -133,34 +124,30 @@ export namespace SessionSummary {
}
}
}
- const result = await generateText({
- model: language,
- maxOutputTokens: 100,
- providerOptions: ProviderTransform.providerOptions(small, options),
+ const summaryAgent = await Agent.get("summary")
+ const stream = await LLM.stream({
+ agent: summaryAgent,
+ user: userMsg,
+ tools: {},
+ model: summaryAgent.model
+ ? await Provider.getModel(summaryAgent.model.providerID, summaryAgent.model.modelID)
+ : small,
+ small: true,
messages: [
- ...SystemPrompt.summarize(small.providerID).map(
- (x): ModelMessage => ({
- role: "system",
- content: x,
- }),
- ),
...MessageV2.toModelMessage(messages),
{
- role: "user",
+ role: "user" as const,
content: `Summarize the above conversation according to your system prompts.`,
},
],
- headers: small.headers,
- experimental_telemetry: {
- isEnabled: cfg.experimental?.openTelemetry,
- metadata: {
- userId: cfg.username ?? "unknown",
- sessionId: assistantMsg.sessionID,
- },
- },
- }).catch(() => {})
+ abort: new AbortController().signal,
+ sessionID: userMsg.sessionID,
+ system: [],
+ retries: 3,
+ })
+ const result = await stream.text
if (result) {
- userMsg.summary.body = result.text
+ userMsg.summary.body = result
}
}
await Session.updateMessage(userMsg)
diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts
index 5f3cfb1411..e15185b38b 100644
--- a/packages/opencode/src/session/system.ts
+++ b/packages/opencode/src/session/system.ts
@@ -14,8 +14,7 @@ import PROMPT_BEAST from "./prompt/beast.txt"
import PROMPT_GEMINI from "./prompt/gemini.txt"
import PROMPT_ANTHROPIC_SPOOF from "./prompt/anthropic_spoof.txt"
import PROMPT_COMPACTION from "./prompt/compaction.txt"
-import PROMPT_SUMMARIZE from "./prompt/summarize.txt"
-import PROMPT_TITLE from "./prompt/title.txt"
+
import PROMPT_CODEX from "./prompt/codex.txt"
import type { Provider } from "@/provider/provider"
@@ -118,22 +117,4 @@ export namespace SystemPrompt {
)
return Promise.all(found).then((result) => result.filter(Boolean))
}
-
- export function summarize(providerID: string) {
- switch (providerID) {
- case "anthropic":
- return [PROMPT_SUMMARIZE]
- default:
- return [PROMPT_SUMMARIZE]
- }
- }
-
- export function title(providerID: string) {
- switch (providerID) {
- case "anthropic":
- return [PROMPT_TITLE]
- default:
- return [PROMPT_TITLE]
- }
- }
}