Merge pull request #4450 from code-yeongyu/fix-4447-subagent-task-deny
fix(plugin-handlers): explicit task deny for read-only subagents to prevent UI freeze (#4447)
This commit is contained in:
@@ -0,0 +1,100 @@
|
|||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
|
import { describe, expect, it } from "bun:test"
|
||||||
|
import type { OhMyOpenCodeConfig } from "../config"
|
||||||
|
import { OhMyOpenCodeConfigSchema } from "../config"
|
||||||
|
import { applyToolConfig } from "./tool-config-handler"
|
||||||
|
|
||||||
|
type TestAgent = {
|
||||||
|
permission?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
const TASK_DENIED_SUBAGENTS = [
|
||||||
|
"librarian",
|
||||||
|
"explore",
|
||||||
|
"oracle",
|
||||||
|
"multimodal-looker",
|
||||||
|
"metis",
|
||||||
|
"momus",
|
||||||
|
] as const
|
||||||
|
|
||||||
|
const TASK_ALLOWED_AGENT_NAMES = [
|
||||||
|
"sisyphus",
|
||||||
|
"atlas",
|
||||||
|
"hephaestus",
|
||||||
|
"sisyphus-junior",
|
||||||
|
] as const
|
||||||
|
|
||||||
|
function createParams(agentNames: readonly string[]): {
|
||||||
|
readonly config: Record<string, unknown>
|
||||||
|
readonly pluginConfig: OhMyOpenCodeConfig
|
||||||
|
readonly agentResult: Record<string, TestAgent>
|
||||||
|
} {
|
||||||
|
const agentResult: Record<string, TestAgent> = {}
|
||||||
|
for (const agentName of agentNames) {
|
||||||
|
agentResult[agentName] = { permission: {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
config: { tools: {}, permission: {} },
|
||||||
|
pluginConfig: OhMyOpenCodeConfigSchema.parse({}),
|
||||||
|
agentResult,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requirePermission(
|
||||||
|
agentResult: Record<string, TestAgent>,
|
||||||
|
agentName: string,
|
||||||
|
): Record<string, unknown> {
|
||||||
|
const permission = agentResult[agentName]?.permission
|
||||||
|
if (!permission) {
|
||||||
|
throw new Error(`Missing permission for ${agentName}`)
|
||||||
|
}
|
||||||
|
return permission
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("applyToolConfig task permission hard denials", () => {
|
||||||
|
describe("#given read-only and specialist subagents", () => {
|
||||||
|
describe("#when applying tool config", () => {
|
||||||
|
for (const agentName of TASK_DENIED_SUBAGENTS) {
|
||||||
|
it(`#then should explicitly deny task for ${agentName}`, () => {
|
||||||
|
const params = createParams([agentName])
|
||||||
|
|
||||||
|
applyToolConfig(params)
|
||||||
|
|
||||||
|
const permission = requirePermission(params.agentResult, agentName)
|
||||||
|
expect(permission.task).toBe("deny")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#given librarian search permissions", () => {
|
||||||
|
describe("#when applying tool config", () => {
|
||||||
|
it("#then should keep grep_app allowed while task is denied", () => {
|
||||||
|
const params = createParams(["librarian"])
|
||||||
|
|
||||||
|
applyToolConfig(params)
|
||||||
|
|
||||||
|
const permission = requirePermission(params.agentResult, "librarian")
|
||||||
|
expect(permission["grep_app_*"]).toBe("allow")
|
||||||
|
expect(permission.task).toBe("deny")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#given primary and executor agents", () => {
|
||||||
|
describe("#when applying tool config", () => {
|
||||||
|
for (const agentName of TASK_ALLOWED_AGENT_NAMES) {
|
||||||
|
it(`#then should keep task allowed for ${agentName}`, () => {
|
||||||
|
const params = createParams([agentName])
|
||||||
|
|
||||||
|
applyToolConfig(params)
|
||||||
|
|
||||||
|
const permission = requirePermission(params.agentResult, agentName)
|
||||||
|
expect(permission.task).toBe("allow")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -4,6 +4,15 @@ import { isTaskSystemEnabled } from "../shared";
|
|||||||
|
|
||||||
type AgentWithPermission = { permission?: Record<string, unknown> };
|
type AgentWithPermission = { permission?: Record<string, unknown> };
|
||||||
|
|
||||||
|
const TASK_DENIED_SUBAGENT_KEYS = [
|
||||||
|
"librarian",
|
||||||
|
"explore",
|
||||||
|
"oracle",
|
||||||
|
"multimodal-looker",
|
||||||
|
"metis",
|
||||||
|
"momus",
|
||||||
|
] as const;
|
||||||
|
|
||||||
function getConfigQuestionPermission(): string | null {
|
function getConfigQuestionPermission(): string | null {
|
||||||
const configContent = process.env.OPENCODE_CONFIG_CONTENT;
|
const configContent = process.env.OPENCODE_CONFIG_CONTENT;
|
||||||
if (!configContent) return null;
|
if (!configContent) return null;
|
||||||
@@ -21,6 +30,12 @@ function agentByKey(agentResult: Record<string, unknown>, key: string): AgentWit
|
|||||||
| undefined;
|
| undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function denyTaskForAgent(agentResult: Record<string, unknown>, key: string): void {
|
||||||
|
const agent = agentByKey(agentResult, key);
|
||||||
|
if (!agent) return;
|
||||||
|
agent.permission = { ...agent.permission, task: "deny" };
|
||||||
|
}
|
||||||
|
|
||||||
export function applyToolConfig(params: {
|
export function applyToolConfig(params: {
|
||||||
config: Record<string, unknown>;
|
config: Record<string, unknown>;
|
||||||
pluginConfig: OhMyOpenCodeConfig;
|
pluginConfig: OhMyOpenCodeConfig;
|
||||||
@@ -59,6 +74,10 @@ export function applyToolConfig(params: {
|
|||||||
isCliRunMode ? "deny" :
|
isCliRunMode ? "deny" :
|
||||||
"allow";
|
"allow";
|
||||||
|
|
||||||
|
for (const agentKey of TASK_DENIED_SUBAGENT_KEYS) {
|
||||||
|
denyTaskForAgent(params.agentResult, agentKey);
|
||||||
|
}
|
||||||
|
|
||||||
const librarian = agentByKey(params.agentResult, "librarian");
|
const librarian = agentByKey(params.agentResult, "librarian");
|
||||||
if (librarian) {
|
if (librarian) {
|
||||||
librarian.permission = { ...librarian.permission, "grep_app_*": "allow" };
|
librarian.permission = { ...librarian.permission, "grep_app_*": "allow" };
|
||||||
|
|||||||
Reference in New Issue
Block a user