From 467248535e03f3b724ac22fde667ddeea6abb011 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 27 Apr 2026 13:51:16 +0900 Subject: [PATCH] fix(agents): clear stale frontier tool denies Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/agents/builtin-agents/hephaestus-agent.ts | 11 +-- .../builtin-agents/sisyphus-agent.test.ts | 69 +++++++++++++++++++ src/agents/builtin-agents/sisyphus-agent.ts | 11 +-- src/agents/frontier-tool-schema-guard.ts | 27 ++++++++ src/agents/hephaestus/agent.test.ts | 69 +++++++++++++++++++ 5 files changed, 177 insertions(+), 10 deletions(-) diff --git a/src/agents/builtin-agents/hephaestus-agent.ts b/src/agents/builtin-agents/hephaestus-agent.ts index fc7b3d571..38fc5a63e 100644 --- a/src/agents/builtin-agents/hephaestus-agent.ts +++ b/src/agents/builtin-agents/hephaestus-agent.ts @@ -8,7 +8,7 @@ import { applyEnvironmentContext } from "./environment-context" import { applyCategoryOverride, mergeAgentConfig } from "./agent-overrides" import { applyModelResolution, getFirstFallbackModel } from "./model-resolution" import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard" -import { getFrontierToolSchemaPermission } from "../frontier-tool-schema-guard" +import { applyFrontierToolSchemaPermission } from "../frontier-tool-schema-guard" export function maybeCreateHephaestusConfig(input: { disabledAgents: string[] @@ -90,10 +90,11 @@ export function maybeCreateHephaestusConfig(input: { } const resolvedModel = hephaestusConfig.model ?? "" - const frontierDeny = getFrontierToolSchemaPermission(resolvedModel) - if (Object.keys(frontierDeny).length > 0 && hephaestusConfig.permission) { - Object.assign(hephaestusConfig.permission, frontierDeny) - } + hephaestusConfig.permission = applyFrontierToolSchemaPermission( + hephaestusConfig.permission, + resolvedModel, + hephaestusOverride?.permission + ) const gptDeny = getGptApplyPatchPermission(resolvedModel) if (Object.keys(gptDeny).length > 0 && hephaestusConfig.permission) { diff --git a/src/agents/builtin-agents/sisyphus-agent.test.ts b/src/agents/builtin-agents/sisyphus-agent.test.ts index b5d14895a..1a799f303 100644 --- a/src/agents/builtin-agents/sisyphus-agent.test.ts +++ b/src/agents/builtin-agents/sisyphus-agent.test.ts @@ -143,6 +143,75 @@ describe("maybeCreateSisyphusConfig", () => { }); }); + describe("#given frontier default model with category override to non-frontier model", () => { + test("#when config is created #then stale grep and glob denies are cleared", () => { + // given + const agentOverrides: AgentOverrides = { + sisyphus: { + category: "non-frontier", + }, + }; + const mergedCategories: Record = { + "non-frontier": { + model: "openai/gpt-5.4", + }, + }; + + // when + const config = maybeCreateSisyphusConfig({ + disabledAgents: [], + agentOverrides, + availableModels: new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.4"]), + systemDefaultModel: "anthropic/claude-opus-4-7", + isFirstRunNoCache: false, + availableAgents: [], + availableSkills: [], + availableCategories: [], + mergedCategories, + useTaskSystem: false, + }); + + // then + expect(config?.model).toBe("openai/gpt-5.4"); + expect(config?.permission).not.toHaveProperty("grep"); + expect(config?.permission).not.toHaveProperty("glob"); + }); + }); + + describe("#given non-frontier model with user override denying grep and glob", () => { + test("#when config is created #then explicit user denies are preserved", () => { + // given + const agentOverrides: AgentOverrides = { + sisyphus: { + model: "openai/gpt-5.4", + permission: { + grep: "deny", + glob: "deny", + } as Record, + }, + }; + const mergedCategories: Record = {}; + + // when + const config = maybeCreateSisyphusConfig({ + disabledAgents: [], + agentOverrides, + availableModels: new Set(["openai/gpt-5.4"]), + systemDefaultModel: "openai/gpt-5.4", + isFirstRunNoCache: false, + availableAgents: [], + availableSkills: [], + availableCategories: [], + mergedCategories, + useTaskSystem: false, + }); + + // then + expect(config?.permission).toHaveProperty("grep", "deny"); + expect(config?.permission).toHaveProperty("glob", "deny"); + }); + }); + describe("#given generic GPT model with user override allowing apply_patch", () => { test("#when config is created #then apply_patch is still denied", () => { // given diff --git a/src/agents/builtin-agents/sisyphus-agent.ts b/src/agents/builtin-agents/sisyphus-agent.ts index 1b111d6cd..ea1b82bf1 100644 --- a/src/agents/builtin-agents/sisyphus-agent.ts +++ b/src/agents/builtin-agents/sisyphus-agent.ts @@ -8,7 +8,7 @@ import { applyOverrides } from "./agent-overrides" import { applyModelResolution, getFirstFallbackModel } from "./model-resolution" import { createSisyphusAgent } from "../sisyphus" import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard" -import { getFrontierToolSchemaPermission } from "../frontier-tool-schema-guard" +import { applyFrontierToolSchemaPermission } from "../frontier-tool-schema-guard" export function maybeCreateSisyphusConfig(input: { disabledAgents: string[] @@ -84,10 +84,11 @@ export function maybeCreateSisyphusConfig(input: { sisyphusConfig = applyOverrides(sisyphusConfig, sisyphusOverride, mergedCategories, directory) const resolvedModel = sisyphusConfig.model ?? "" - const frontierDeny = getFrontierToolSchemaPermission(resolvedModel) - if (Object.keys(frontierDeny).length > 0 && sisyphusConfig.permission) { - Object.assign(sisyphusConfig.permission, frontierDeny) - } + sisyphusConfig.permission = applyFrontierToolSchemaPermission( + sisyphusConfig.permission, + resolvedModel, + sisyphusOverride?.permission + ) const gptDeny = getGptApplyPatchPermission(resolvedModel) if (Object.keys(gptDeny).length > 0 && sisyphusConfig.permission) { diff --git a/src/agents/frontier-tool-schema-guard.ts b/src/agents/frontier-tool-schema-guard.ts index 028943dd8..4c6a0bd55 100644 --- a/src/agents/frontier-tool-schema-guard.ts +++ b/src/agents/frontier-tool-schema-guard.ts @@ -1,4 +1,9 @@ +import type { AgentConfig } from "@opencode-ai/sdk" import { isGpt5_5Model } from "./types" +import type { PermissionValue } from "../shared/permission-compat" + +const FRONTIER_TOOL_SCHEMA_NAMES = ["grep", "glob"] as const +type MutablePermission = Record> function isOpus47Model(model: string): boolean { const modelName = model.includes("/") ? (model.split("/").pop() ?? model) : model @@ -10,3 +15,25 @@ export function getFrontierToolSchemaPermission(model: string): Record 0) { + Object.assign(nextPermission, frontierDeny) + return nextPermission as AgentConfig["permission"] + } + + for (const toolName of FRONTIER_TOOL_SCHEMA_NAMES) { + if (explicitPermissionMap?.[toolName] === "deny") continue + delete nextPermission[toolName] + } + return nextPermission as AgentConfig["permission"] +} diff --git a/src/agents/hephaestus/agent.test.ts b/src/agents/hephaestus/agent.test.ts index 22f1d8c81..165f1e095 100644 --- a/src/agents/hephaestus/agent.test.ts +++ b/src/agents/hephaestus/agent.test.ts @@ -484,4 +484,73 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => { expect(config?.permission).toHaveProperty("glob", "deny"); }); }); + + describe("#given frontier default model with category override to non-frontier model", () => { + test("#when config is created #then stale grep and glob denies are cleared", () => { + // given + const agentOverrides: AgentOverrides = { + hephaestus: { + category: "non-frontier", + }, + }; + const mergedCategories: Record = { + "non-frontier": { + model: "openai/gpt-5.4", + }, + }; + + // when + const config = maybeCreateHephaestusConfig({ + disabledAgents: [], + agentOverrides, + availableModels: new Set(["openai/gpt-5.5", "openai/gpt-5.4"]), + systemDefaultModel: "openai/gpt-5.5", + isFirstRunNoCache: false, + availableAgents: [], + availableSkills: [], + availableCategories: [], + mergedCategories, + useTaskSystem: false, + }); + + // then + expect(config?.model).toBe("openai/gpt-5.4"); + expect(config?.permission).not.toHaveProperty("grep"); + expect(config?.permission).not.toHaveProperty("glob"); + }); + }); + + describe("#given non-frontier model with user override denying grep and glob", () => { + test("#when config is created #then explicit user denies are preserved", () => { + // given + const agentOverrides: AgentOverrides = { + hephaestus: { + model: "openai/gpt-5.4", + permission: { + grep: "deny", + glob: "deny", + } as Record, + }, + }; + const mergedCategories: Record = {}; + + // when + const config = maybeCreateHephaestusConfig({ + disabledAgents: [], + agentOverrides, + availableModels: new Set(["openai/gpt-5.4"]), + systemDefaultModel: "openai/gpt-5.4", + isFirstRunNoCache: false, + availableAgents: [], + availableSkills: [], + availableCategories: [], + mergedCategories, + useTaskSystem: false, + }); + + // then + expect(config?.permission).toHaveProperty("grep", "deny"); + expect(config?.permission).toHaveProperty("glob", "deny"); + }); + }); });