diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts
index fb382530291..5095a8bb768 100644
--- a/packages/opencode/src/session/compaction.ts
+++ b/packages/opencode/src/session/compaction.ts
@@ -97,7 +97,8 @@ export namespace SessionCompaction {
auto: boolean
}) {
const userMessage = input.messages.findLast((m) => m.info.id === input.parentID)!.info as MessageV2.User
- const agent = await Agent.get("compaction")
+ const msgAgent = await Agent.get(userMessage.agent)
+ const agent = await Agent.get(msgAgent.options.agents?.compaction ?? "compaction")
const model = agent.model
? await Provider.getModel(agent.model.providerID, agent.model.modelID)
: await Provider.getModel(userMessage.model.providerID, userMessage.model.modelID)
diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts
index d651308032e..4e6b58801d8 100644
--- a/packages/opencode/src/session/llm.ts
+++ b/packages/opencode/src/session/llm.ts
@@ -67,13 +67,16 @@ export namespace LLM {
const isCodex = provider.id === "openai" && auth?.type === "oauth"
const system = []
+ const customSystem = input.agent.options.system
+ ? await SystemPrompt.environment(input.model, input.agent.options.system)
+ : input.system
system.push(
[
// use agent prompt otherwise provider prompt
// For Codex sessions, skip SystemPrompt.provider() since it's sent via options.instructions
...(input.agent.prompt ? [input.agent.prompt] : isCodex ? [] : SystemPrompt.provider(input.model)),
// any custom prompt passed into this call
- ...input.system,
+ ...customSystem,
// any custom prompt from last user message
...(input.user.system ? [input.user.system] : []),
]
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index 23ca473541c..ac610d20d64 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -1773,7 +1773,24 @@ NOTE: At any point in time through this workflow you should feel free to ask the
const subtaskParts = firstRealUser.parts.filter((p) => p.type === "subtask") as MessageV2.SubtaskPart[]
const hasOnlySubtaskParts = subtaskParts.length > 0 && firstRealUser.parts.every((p) => p.type === "subtask")
- const agent = await Agent.get("title")
+ const msgAgent = await Agent.get(firstRealUser.info.agent)
+ const titleAgent = msgAgent.options.agents?.title ?? "title"
+ if (titleAgent === "none") {
+ const textPart = firstRealUser.parts.find((part) => part.type === "text" && !part.synthetic) as
+ | MessageV2.TextPart
+ | undefined
+ const text = textPart?.text?.trim()
+ if (!text) return
+ const title = text.length > 50 ? text.slice(0, 50) + "..." : text
+ return Session.update(
+ input.session.id,
+ (draft) => {
+ draft.title = title
+ },
+ { touch: false },
+ )
+ }
+ const agent = await Agent.get(titleAgent)
if (!agent) return
const model = await iife(async () => {
if (agent.model) return await Provider.getModel(agent.model.providerID, agent.model.modelID)
diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts
index 91a520a9bdf..2932d9154b4 100644
--- a/packages/opencode/src/session/summary.ts
+++ b/packages/opencode/src/session/summary.ts
@@ -132,8 +132,15 @@ export namespace SessionSummary {
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")
- if (!agent) return
+ const msgAgent = await Agent.get(userMsg.agent)
+ const titleAgent = msgAgent.options.agents?.title ?? "title"
+ if (titleAgent === "none") {
+ const title = textPart.text.length > 50 ? textPart.text.slice(0, 50) + "..." : textPart.text
+ userMsg.summary.title = title
+ await Session.updateMessage(userMsg)
+ return
+ }
+ const agent = await Agent.get(titleAgent)
const stream = await LLM.stream({
agent,
user: userMsg,
diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts
index d34a086fe44..d84fc590450 100644
--- a/packages/opencode/src/session/system.ts
+++ b/packages/opencode/src/session/system.ts
@@ -24,11 +24,20 @@ export namespace SystemPrompt {
return [PROMPT_ANTHROPIC_WITHOUT_TODO]
}
- export async function environment(model: Provider.Model) {
+ export async function environment(
+ model: Provider.Model,
+ options?: { model_id?: boolean; env?: boolean; files?: boolean },
+ ) {
const project = Instance.project
- return [
- [
+ const parts: string[] = []
+
+ if (options?.model_id !== false)
+ parts.push(
`You are powered by the model named ${model.api.id}. The exact model ID is ${model.providerID}/${model.api.id}`,
+ )
+
+ if (options?.env !== false)
+ parts.push(
`Here is some useful information about the environment you are running in:`,
``,
` Working directory: ${Instance.directory}`,
@@ -36,9 +45,14 @@ export namespace SystemPrompt {
` Platform: ${process.platform}`,
` Today's date: ${new Date().toDateString()}`,
``,
+ )
+
+ // files info was disabled during permission rework
+ if (options?.files !== false && false)
+ parts.push(
``,
` ${
- project.vcs === "git" && false
+ project.vcs === "git"
? await Ripgrep.tree({
cwd: Instance.directory,
limit: 200,
@@ -46,7 +60,8 @@ export namespace SystemPrompt {
: ""
}`,
``,
- ].join("\n"),
- ]
+ )
+
+ return [parts.join("\n")]
}
}
diff --git a/packages/opencode/src/tool/task.ts b/packages/opencode/src/tool/task.ts
index c87add638aa..afdbf9e2dd4 100644
--- a/packages/opencode/src/tool/task.ts
+++ b/packages/opencode/src/tool/task.ts
@@ -41,20 +41,24 @@ export const TaskTool = Tool.define("task", async (ctx) => {
async execute(params: z.infer, ctx) {
const config = await Config.get()
+ // Resolve subagent type through calling agent's mapping
+ const callingAgent = ctx.agent ? await Agent.get(ctx.agent) : undefined
+ const resolvedSubagentType = callingAgent?.options.agents?.[params.subagent_type] ?? params.subagent_type
+
// Skip permission check when user explicitly invoked via @ or command subtask
if (!ctx.extra?.bypassAgentCheck) {
await ctx.ask({
permission: "task",
- patterns: [params.subagent_type],
+ patterns: [resolvedSubagentType],
always: ["*"],
metadata: {
description: params.description,
- subagent_type: params.subagent_type,
+ subagent_type: resolvedSubagentType,
},
})
}
- const agent = await Agent.get(params.subagent_type)
+ const agent = await Agent.get(resolvedSubagentType)
if (!agent) throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`)
const hasTaskPermission = agent.permission.some((rule) => rule.permission === "task")