mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-05 00:23:10 +00:00
refactor: simplify task tool subagent filtering (#7165)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
fe57d7bb38
commit
34c9d106ee
@@ -37,7 +37,7 @@ import { SessionSummary } from "./summary"
|
||||
import { NamedError } from "@opencode-ai/util/error"
|
||||
import { fn } from "@/util/fn"
|
||||
import { SessionProcessor } from "./processor"
|
||||
import { TaskTool, filterSubagents, TASK_DESCRIPTION } from "@/tool/task"
|
||||
import { TaskTool } from "@/tool/task"
|
||||
import { Tool } from "@/tool/tool"
|
||||
import { PermissionNext } from "@/permission/next"
|
||||
import { SessionStatus } from "./status"
|
||||
@@ -383,7 +383,7 @@ export namespace SessionPrompt {
|
||||
sessionID: sessionID,
|
||||
abort,
|
||||
callID: part.callID,
|
||||
extra: { userInvokedAgents: [task.agent] },
|
||||
extra: { bypassAgentCheck: true },
|
||||
async metadata(input) {
|
||||
await Session.updatePart({
|
||||
...part,
|
||||
@@ -545,11 +545,9 @@ export namespace SessionPrompt {
|
||||
abort,
|
||||
})
|
||||
|
||||
// Track agents explicitly invoked by user via @ autocomplete
|
||||
const userInvokedAgents = msgs
|
||||
.filter((m) => m.info.role === "user")
|
||||
.flatMap((m) => m.parts.filter((p) => p.type === "agent") as MessageV2.AgentPart[])
|
||||
.map((p) => p.name)
|
||||
// Check if user explicitly invoked an agent via @ in this turn
|
||||
const lastUserMsg = msgs.findLast((m) => m.info.role === "user")
|
||||
const bypassAgentCheck = lastUserMsg?.parts.some((p) => p.type === "agent") ?? false
|
||||
|
||||
const tools = await resolveTools({
|
||||
agent,
|
||||
@@ -557,7 +555,7 @@ export namespace SessionPrompt {
|
||||
model,
|
||||
tools: lastUser.tools,
|
||||
processor,
|
||||
userInvokedAgents,
|
||||
bypassAgentCheck,
|
||||
})
|
||||
|
||||
if (step === 1) {
|
||||
@@ -646,7 +644,7 @@ export namespace SessionPrompt {
|
||||
session: Session.Info
|
||||
tools?: Record<string, boolean>
|
||||
processor: SessionProcessor.Info
|
||||
userInvokedAgents: string[]
|
||||
bypassAgentCheck: boolean
|
||||
}) {
|
||||
using _ = log.time("resolveTools")
|
||||
const tools: Record<string, AITool> = {}
|
||||
@@ -656,7 +654,7 @@ export namespace SessionPrompt {
|
||||
abort: options.abortSignal!,
|
||||
messageID: input.processor.message.id,
|
||||
callID: options.toolCallId,
|
||||
extra: { model: input.model, userInvokedAgents: input.userInvokedAgents },
|
||||
extra: { model: input.model, bypassAgentCheck: input.bypassAgentCheck },
|
||||
agent: input.agent.name,
|
||||
metadata: async (val: { title?: string; metadata?: any }) => {
|
||||
const match = input.processor.partFromToolCall(options.toolCallId)
|
||||
@@ -800,28 +798,6 @@ export namespace SessionPrompt {
|
||||
tools[key] = item
|
||||
}
|
||||
|
||||
// Regenerate task tool description with filtered subagents
|
||||
if (tools.task) {
|
||||
const all = await Agent.list().then((x) => x.filter((a) => a.mode !== "primary"))
|
||||
const filtered = filterSubagents(all, input.agent.permission)
|
||||
|
||||
// If no subagents are permitted, remove the task tool entirely
|
||||
if (filtered.length === 0) {
|
||||
delete tools.task
|
||||
} else {
|
||||
const description = TASK_DESCRIPTION.replace(
|
||||
"{agents}",
|
||||
filtered
|
||||
.map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`)
|
||||
.join("\n"),
|
||||
)
|
||||
tools.task = {
|
||||
...tools.task,
|
||||
description,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tools
|
||||
}
|
||||
|
||||
|
||||
@@ -12,35 +12,37 @@ import { defer } from "@/util/defer"
|
||||
import { Config } from "../config/config"
|
||||
import { PermissionNext } from "@/permission/next"
|
||||
|
||||
export { DESCRIPTION as TASK_DESCRIPTION }
|
||||
const parameters = z.object({
|
||||
description: z.string().describe("A short (3-5 words) description of the task"),
|
||||
prompt: z.string().describe("The task for the agent to perform"),
|
||||
subagent_type: z.string().describe("The type of specialized agent to use for this task"),
|
||||
session_id: z.string().describe("Existing Task session to continue").optional(),
|
||||
command: z.string().describe("The command that triggered this task").optional(),
|
||||
})
|
||||
|
||||
export function filterSubagents(agents: Agent.Info[], ruleset: PermissionNext.Ruleset) {
|
||||
return agents.filter((a) => PermissionNext.evaluate("task", a.name, ruleset).action !== "deny")
|
||||
}
|
||||
|
||||
export const TaskTool = Tool.define("task", async () => {
|
||||
export const TaskTool = Tool.define("task", async (ctx) => {
|
||||
const agents = await Agent.list().then((x) => x.filter((a) => a.mode !== "primary"))
|
||||
|
||||
// Filter agents by permissions if agent provided
|
||||
const caller = ctx?.agent
|
||||
const accessibleAgents = caller
|
||||
? agents.filter((a) => PermissionNext.evaluate("task", a.name, caller.permission).action !== "deny")
|
||||
: agents
|
||||
|
||||
const description = DESCRIPTION.replace(
|
||||
"{agents}",
|
||||
agents
|
||||
accessibleAgents
|
||||
.map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`)
|
||||
.join("\n"),
|
||||
)
|
||||
return {
|
||||
description,
|
||||
parameters: z.object({
|
||||
description: z.string().describe("A short (3-5 words) description of the task"),
|
||||
prompt: z.string().describe("The task for the agent to perform"),
|
||||
subagent_type: z.string().describe("The type of specialized agent to use for this task"),
|
||||
session_id: z.string().describe("Existing Task session to continue").optional(),
|
||||
command: z.string().describe("The command that triggered this task").optional(),
|
||||
}),
|
||||
async execute(params, ctx) {
|
||||
parameters,
|
||||
async execute(params: z.infer<typeof parameters>, ctx) {
|
||||
const config = await Config.get()
|
||||
|
||||
const userInvokedAgents = (ctx.extra?.userInvokedAgents ?? []) as string[]
|
||||
// Skip permission check when invoked from a command subtask (user already approved by invoking the command)
|
||||
if (!ctx.extra?.bypassAgentCheck && !userInvokedAgents.includes(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],
|
||||
|
||||
Reference in New Issue
Block a user