Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/opencode/src/session/compaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion packages/opencode/src/session/llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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] : []),
]
Expand Down
19 changes: 18 additions & 1 deletion packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 9 additions & 2 deletions packages/opencode/src/session/summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
27 changes: 21 additions & 6 deletions packages/opencode/src/session/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,44 @@ 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:`,
`<env>`,
` Working directory: ${Instance.directory}`,
` Is directory a git repo: ${project.vcs === "git" ? "yes" : "no"}`,
` Platform: ${process.platform}`,
` Today's date: ${new Date().toDateString()}`,
`</env>`,
)

// files info was disabled during permission rework
if (options?.files !== false && false)
parts.push(
`<files>`,
` ${
project.vcs === "git" && false
project.vcs === "git"
? await Ripgrep.tree({
cwd: Instance.directory,
limit: 200,
})
: ""
}`,
`</files>`,
].join("\n"),
]
)

return [parts.join("\n")]
}
}
10 changes: 7 additions & 3 deletions packages/opencode/src/tool/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,24 @@ export const TaskTool = Tool.define("task", async (ctx) => {
async execute(params: z.infer<typeof parameters>, 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")
Expand Down