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")