pull/6319/head
Dax Raad 2025-12-29 11:59:17 -05:00
parent 8ca4343053
commit aeca8051d5
4 changed files with 51 additions and 37 deletions

View File

@ -42,20 +42,19 @@ export namespace Agent {
const state = Instance.state(async () => {
const cfg = await Config.get()
const permission: PermissionNext.Ruleset = PermissionNext.merge(
PermissionNext.fromConfig({
"*": "allow",
doom_loop: "ask",
external_directory: "ask",
}),
PermissionNext.fromConfig(cfg.permission ?? {}),
)
const defaults = PermissionNext.fromConfig({
"*": "allow",
doom_loop: "ask",
external_directory: "ask",
})
const user = PermissionNext.fromConfig(cfg.permission ?? {})
const result: Record<string, Info> = {
build: {
name: "build",
options: {},
permission,
permission: PermissionNext.merge(defaults, user),
mode: "primary",
native: true,
},
@ -63,13 +62,14 @@ export namespace Agent {
name: "plan",
options: {},
permission: PermissionNext.merge(
permission,
defaults,
PermissionNext.fromConfig({
edit: {
"*": "deny",
".opencode/plan/*.md": "allow",
},
}),
user,
),
mode: "primary",
native: true,
@ -78,11 +78,12 @@ export namespace Agent {
name: "general",
description: `General-purpose agent for researching complex questions and executing multi-step tasks. Use this agent to execute multiple units of work in parallel.`,
permission: PermissionNext.merge(
permission,
defaults,
PermissionNext.fromConfig({
todoread: "deny",
todowrite: "deny",
}),
user,
),
options: {},
mode: "subagent",
@ -92,7 +93,7 @@ export namespace Agent {
explore: {
name: "explore",
permission: PermissionNext.merge(
permission,
defaults,
PermissionNext.fromConfig({
"*": "deny",
grep: "allow",
@ -104,6 +105,7 @@ export namespace Agent {
codesearch: "allow",
read: "allow",
}),
user,
),
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: PROMPT_EXPLORE,
@ -117,9 +119,13 @@ export namespace Agent {
native: true,
hidden: true,
prompt: PROMPT_COMPACTION,
permission: PermissionNext.fromConfig({
"*": "deny",
}),
permission: PermissionNext.merge(
defaults,
PermissionNext.fromConfig({
"*": "deny",
}),
user,
),
options: {},
},
title: {
@ -128,9 +134,13 @@ export namespace Agent {
options: {},
native: true,
hidden: true,
permission: PermissionNext.fromConfig({
"*": "deny",
}),
permission: PermissionNext.merge(
defaults,
PermissionNext.fromConfig({
"*": "deny",
}),
user,
),
prompt: PROMPT_TITLE,
},
summary: {
@ -139,9 +149,13 @@ export namespace Agent {
options: {},
native: true,
hidden: true,
permission: PermissionNext.fromConfig({
"*": "deny",
}),
permission: PermissionNext.merge(
defaults,
PermissionNext.fromConfig({
"*": "deny",
}),
user,
),
prompt: PROMPT_SUMMARY,
},
}
@ -156,7 +170,7 @@ export namespace Agent {
item = result[key] = {
name: key,
mode: "all",
permission,
permission: PermissionNext.merge(defaults, user),
options: {},
native: false,
}

View File

@ -161,10 +161,9 @@ export function PermissionPrompt(props: { request: PermissionRequest }) {
</Match>
<Match when={props.request.permission === "task"}>
<TextBody
icon="◉"
title={
`${Locale.titlecase(input().subagent_type as string)} Task "` + (input().description ?? "") + `"`
}
icon="#"
title={`${Locale.titlecase((input().subagent_type as string) ?? "Unknown")} Task`}
description={"◉ " + input().description}
/>
</Match>
<Match when={props.request.permission === "webfetch"}>

View File

@ -153,7 +153,7 @@ export namespace SessionProcessor {
JSON.stringify(p.state.input) === JSON.stringify(value.input),
)
) {
const agent = await Agent.get(input.assistantMessage.mode)
const agent = await Agent.get(input.assistantMessage.agent)
await PermissionNext.ask({
permission: "doom_loop",
patterns: [value.toolName],

View File

@ -241,6 +241,7 @@ export namespace SessionPrompt {
using _ = defer(() => cancel(sessionID))
let step = 0
const session = await Session.get(sessionID)
while (true) {
SessionStatus.set(sessionID, { type: "busy" })
log.info("loop", { step, sessionID })
@ -277,7 +278,7 @@ export namespace SessionPrompt {
step++
if (step === 1)
ensureTitle({
session: await Session.get(sessionID),
session,
modelID: lastUser.model.modelID,
providerID: lastUser.model.providerID,
message: msgs.find((m) => m.info.role === "user")!,
@ -519,7 +520,7 @@ export namespace SessionPrompt {
})
const tools = await resolveTools({
agent,
sessionID,
session,
model,
tools: lastUser.tools,
processor,
@ -589,7 +590,7 @@ export namespace SessionPrompt {
async function resolveTools(input: {
agent: Agent.Info
model: Provider.Model
sessionID: string
session: Session.Info
tools?: Record<string, boolean>
processor: SessionProcessor.Info
}) {
@ -606,7 +607,7 @@ export namespace SessionPrompt {
"tool.execute.before",
{
tool: item.id,
sessionID: input.sessionID,
sessionID: input.session.id,
callID: options.toolCallId,
},
{
@ -614,7 +615,7 @@ export namespace SessionPrompt {
},
)
const ctx: Tool.Context = {
sessionID: input.sessionID,
sessionID: input.session.id,
abort: options.abortSignal!,
messageID: input.processor.message.id,
callID: options.toolCallId,
@ -640,7 +641,7 @@ export namespace SessionPrompt {
async ask(req) {
await PermissionNext.ask({
...req,
sessionID: input.sessionID,
sessionID: input.session.parentID ?? input.session.id,
tool: { messageID: input.processor.message.id, callID: options.toolCallId },
ruleset: input.agent.permission,
})
@ -651,7 +652,7 @@ export namespace SessionPrompt {
"tool.execute.after",
{
tool: item.id,
sessionID: input.sessionID,
sessionID: input.session.id,
callID: options.toolCallId,
},
result,
@ -676,7 +677,7 @@ export namespace SessionPrompt {
"tool.execute.before",
{
tool: key,
sessionID: input.sessionID,
sessionID: input.session.id,
callID: opts.toolCallId,
},
{
@ -689,7 +690,7 @@ export namespace SessionPrompt {
"tool.execute.after",
{
tool: key,
sessionID: input.sessionID,
sessionID: input.session.id,
callID: opts.toolCallId,
},
result,
@ -704,7 +705,7 @@ export namespace SessionPrompt {
} else if (contentItem.type === "image") {
attachments.push({
id: Identifier.ascending("part"),
sessionID: input.sessionID,
sessionID: input.session.id,
messageID: input.processor.message.id,
type: "file",
mime: contentItem.mimeType,