diff --git a/src/agents/builtin-agents/hephaestus-agent.ts b/src/agents/builtin-agents/hephaestus-agent.ts index a32064c63..fc7b3d571 100644 --- a/src/agents/builtin-agents/hephaestus-agent.ts +++ b/src/agents/builtin-agents/hephaestus-agent.ts @@ -8,6 +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" export function maybeCreateHephaestusConfig(input: { disabledAgents: string[] @@ -89,6 +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) + } + const gptDeny = getGptApplyPatchPermission(resolvedModel) if (Object.keys(gptDeny).length > 0 && hephaestusConfig.permission) { Object.assign(hephaestusConfig.permission, gptDeny) diff --git a/src/agents/builtin-agents/sisyphus-agent.test.ts b/src/agents/builtin-agents/sisyphus-agent.test.ts index e7289f6c0..b5d14895a 100644 --- a/src/agents/builtin-agents/sisyphus-agent.test.ts +++ b/src/agents/builtin-agents/sisyphus-agent.test.ts @@ -1,3 +1,5 @@ +/// + import { describe, expect, test } from "bun:test"; import { maybeCreateSisyphusConfig } from "./sisyphus-agent"; import type { AgentOverrides } from "../types"; @@ -12,7 +14,7 @@ describe("maybeCreateSisyphusConfig", () => { model: "openai/gpt-5.4", permission: { apply_patch: "allow", - }, + } as Record, }, }; const mergedCategories: Record = {}; @@ -46,7 +48,7 @@ describe("maybeCreateSisyphusConfig", () => { model: "anthropic/claude-opus-4-7", permission: { apply_patch: "allow", - }, + } as Record, }, }; const mergedCategories: Record = {}; @@ -73,6 +75,74 @@ describe("maybeCreateSisyphusConfig", () => { }); }); + describe("#given Opus 4.7 model with user override allowing grep and glob", () => { + test("#when config is created #then grep and glob are still denied", () => { + // given + const agentOverrides: AgentOverrides = { + sisyphus: { + model: "anthropic/claude-opus-4-7", + permission: { + grep: "allow", + glob: "allow", + } as Record, + }, + }; + const mergedCategories: Record = {}; + + // when + const config = maybeCreateSisyphusConfig({ + disabledAgents: [], + agentOverrides, + availableModels: new Set(["anthropic/claude-opus-4-7"]), + systemDefaultModel: "anthropic/claude-opus-4-7", + isFirstRunNoCache: false, + availableAgents: [], + availableSkills: [], + availableCategories: [], + mergedCategories, + useTaskSystem: false, + }); + + // then + expect(config?.permission).toHaveProperty("grep", "deny"); + expect(config?.permission).toHaveProperty("glob", "deny"); + }); + }); + + describe("#given GPT 5.5 model with user override allowing grep and glob", () => { + test("#when config is created #then grep and glob are still denied", () => { + // given + const agentOverrides: AgentOverrides = { + sisyphus: { + model: "openai/gpt-5.5", + permission: { + grep: "allow", + glob: "allow", + } as Record, + }, + }; + const mergedCategories: Record = {}; + + // when + const config = maybeCreateSisyphusConfig({ + disabledAgents: [], + agentOverrides, + availableModels: new Set(["openai/gpt-5.5"]), + systemDefaultModel: "openai/gpt-5.5", + 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 @@ -81,7 +151,7 @@ describe("maybeCreateSisyphusConfig", () => { model: "openai/gpt-4o", permission: { apply_patch: "allow", - }, + } as Record, }, }; const mergedCategories: Record = {}; diff --git a/src/agents/builtin-agents/sisyphus-agent.ts b/src/agents/builtin-agents/sisyphus-agent.ts index 97aef5f61..1b111d6cd 100644 --- a/src/agents/builtin-agents/sisyphus-agent.ts +++ b/src/agents/builtin-agents/sisyphus-agent.ts @@ -8,6 +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" export function maybeCreateSisyphusConfig(input: { disabledAgents: string[] @@ -83,6 +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) + } + const gptDeny = getGptApplyPatchPermission(resolvedModel) if (Object.keys(gptDeny).length > 0 && sisyphusConfig.permission) { Object.assign(sisyphusConfig.permission, gptDeny) diff --git a/src/agents/frontier-tool-schema-guard.ts b/src/agents/frontier-tool-schema-guard.ts new file mode 100644 index 000000000..028943dd8 --- /dev/null +++ b/src/agents/frontier-tool-schema-guard.ts @@ -0,0 +1,12 @@ +import { isGpt5_5Model } from "./types" + +function isOpus47Model(model: string): boolean { + const modelName = model.includes("/") ? (model.split("/").pop() ?? model) : model + return modelName.toLowerCase().includes("claude-opus-4-7") +} + +export function getFrontierToolSchemaPermission(model: string): Record { + return isOpus47Model(model) || isGpt5_5Model(model) + ? { grep: "deny" as const, glob: "deny" as const } + : {} +} diff --git a/src/agents/hephaestus/agent.test.ts b/src/agents/hephaestus/agent.test.ts index f7d1087f6..22f1d8c81 100644 --- a/src/agents/hephaestus/agent.test.ts +++ b/src/agents/hephaestus/agent.test.ts @@ -1,3 +1,5 @@ +/// + import { describe, expect, test } from "bun:test"; import { getHephaestusPromptSource, @@ -321,7 +323,7 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => { model: "openai/gpt-5.4", permission: { apply_patch: "allow", - }, + } as Record, }, }; const mergedCategories: Record = {}; @@ -355,7 +357,7 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => { model: "anthropic/claude-opus-4-7", permission: { apply_patch: "allow", - }, + } as Record, }, }; const mergedCategories: Record = {}; @@ -389,7 +391,7 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => { model: "openai/gpt-4o", permission: { apply_patch: "allow", - }, + } as Record, }, }; const mergedCategories: Record = {}; @@ -414,4 +416,72 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => { expect(config?.permission).toHaveProperty("apply_patch", "deny"); }); }); + + describe("#given Opus 4.7 model with user override allowing grep and glob", () => { + test("#when config is created #then grep and glob are still denied", () => { + // given + const agentOverrides: AgentOverrides = { + hephaestus: { + model: "anthropic/claude-opus-4-7", + permission: { + grep: "allow", + glob: "allow", + } as Record, + }, + }; + const mergedCategories: Record = {}; + + // when + const config = maybeCreateHephaestusConfig({ + disabledAgents: [], + agentOverrides, + availableModels: new Set(["anthropic/claude-opus-4-7"]), + systemDefaultModel: "anthropic/claude-opus-4-7", + isFirstRunNoCache: false, + availableAgents: [], + availableSkills: [], + availableCategories: [], + mergedCategories, + useTaskSystem: false, + }); + + // then + expect(config?.permission).toHaveProperty("grep", "deny"); + expect(config?.permission).toHaveProperty("glob", "deny"); + }); + }); + + describe("#given GPT 5.5 model with user override allowing grep and glob", () => { + test("#when config is created #then grep and glob are still denied", () => { + // given + const agentOverrides: AgentOverrides = { + hephaestus: { + model: "openai/gpt-5.5", + permission: { + grep: "allow", + glob: "allow", + } as Record, + }, + }; + const mergedCategories: Record = {}; + + // when + const config = maybeCreateHephaestusConfig({ + disabledAgents: [], + agentOverrides, + availableModels: new Set(["openai/gpt-5.5"]), + systemDefaultModel: "openai/gpt-5.5", + isFirstRunNoCache: false, + availableAgents: [], + availableSkills: [], + availableCategories: [], + mergedCategories, + useTaskSystem: false, + }); + + // then + expect(config?.permission).toHaveProperty("grep", "deny"); + expect(config?.permission).toHaveProperty("glob", "deny"); + }); + }); }); diff --git a/src/agents/hephaestus/agent.ts b/src/agents/hephaestus/agent.ts index b348f30b7..3aa773bac 100644 --- a/src/agents/hephaestus/agent.ts +++ b/src/agents/hephaestus/agent.ts @@ -9,6 +9,7 @@ import type { } from "../dynamic-agent-prompt-builder"; import { categorizeTools, buildAgentIdentitySection } from "../dynamic-agent-prompt-builder"; import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard"; +import { getFrontierToolSchemaPermission } from "../frontier-tool-schema-guard"; import { buildHephaestusPrompt as buildGptPrompt } from "./gpt"; import { buildHephaestusPrompt as buildGpt53CodexPrompt } from "./gpt-5-3-codex"; @@ -139,6 +140,7 @@ export function createHephaestusAgent( permission: { question: "allow", call_omo_agent: "deny", + ...getFrontierToolSchemaPermission(model), ...getGptApplyPatchPermission(model), } as AgentConfig["permission"], reasoningEffort: "medium", diff --git a/src/agents/sisyphus.ts b/src/agents/sisyphus.ts index 21a36b8a0..d0bfb0979 100644 --- a/src/agents/sisyphus.ts +++ b/src/agents/sisyphus.ts @@ -13,6 +13,7 @@ import { buildGpt54SisyphusPrompt } from "./sisyphus/gpt-5-4"; import { buildGpt55SisyphusPrompt } from "./sisyphus/gpt-5-5"; import { buildTaskManagementSection } from "./sisyphus/default"; import { getGptApplyPatchPermission } from "./gpt-apply-patch-guard"; +import { getFrontierToolSchemaPermission } from "./frontier-tool-schema-guard"; const MODE: AgentMode = "primary"; export const SISYPHUS_PROMPT_METADATA: AgentPromptMetadata = { @@ -501,6 +502,7 @@ export function createSisyphusAgent( permission: { question: "allow", call_omo_agent: "deny", + ...getFrontierToolSchemaPermission(model), ...getGptApplyPatchPermission(model), } as AgentConfig["permission"], reasoningEffort: "medium", @@ -527,6 +529,7 @@ export function createSisyphusAgent( permission: { question: "allow", call_omo_agent: "deny", + ...getFrontierToolSchemaPermission(model), ...getGptApplyPatchPermission(model), } as AgentConfig["permission"], reasoningEffort: "medium", @@ -567,6 +570,7 @@ export function createSisyphusAgent( const permission = { question: "allow", call_omo_agent: "deny", + ...getFrontierToolSchemaPermission(model), ...getGptApplyPatchPermission(model), } as AgentConfig["permission"]; const base = { diff --git a/src/agents/tool-restrictions.test.ts b/src/agents/tool-restrictions.test.ts index 3ae7bfcfe..1d0fed4fd 100644 --- a/src/agents/tool-restrictions.test.ts +++ b/src/agents/tool-restrictions.test.ts @@ -1,3 +1,5 @@ +/// + import { describe, test, expect } from "bun:test" import { createOracleAgent } from "./oracle" import { createLibrarianAgent } from "./librarian" @@ -6,6 +8,7 @@ import { createMomusAgent } from "./momus" import { createMetisAgent } from "./metis" import { createAtlasAgent } from "./atlas" import { createSisyphusAgent } from "./sisyphus" +import { createHephaestusAgent } from "./hephaestus" const TEST_MODEL = "anthropic/claude-sonnet-4-5" @@ -131,4 +134,47 @@ describe("read-only agent tool restrictions", () => { expect(claudePermission["apply_patch"]).toBeUndefined() }) }) + + describe("Sisyphus and Hephaestus frontier tool schema restrictions", () => { + test("deny grep and glob for Opus 4.7 and GPT 5.5 models", () => { + // given + const frontierAgents = [ + createSisyphusAgent("anthropic/claude-opus-4-7"), + createSisyphusAgent("openai/gpt-5.5"), + createHephaestusAgent("anthropic/claude-opus-4-7"), + createHephaestusAgent("openai/gpt-5.5"), + ] + + // when + const permissions = frontierAgents.map( + (agent) => (agent.permission ?? {}) as Record, + ) + + // then + for (const permission of permissions) { + expect(permission.grep).toBe("deny") + expect(permission.glob).toBe("deny") + } + }) + + test("keeps grep and glob available for other models", () => { + // given + const otherAgents = [ + createSisyphusAgent("anthropic/claude-sonnet-4-5"), + createSisyphusAgent("openai/gpt-5.4"), + createHephaestusAgent("openai/gpt-5.4"), + ] + + // when + const permissions = otherAgents.map( + (agent) => (agent.permission ?? {}) as Record, + ) + + // then + for (const permission of permissions) { + expect(permission.grep).toBeUndefined() + expect(permission.glob).toBeUndefined() + } + }) + }) })