fix: proper OpenCode v1.1.1 permission migration (#490)
* fix: implement proper version-aware permission format for OpenCode v1.1.1 - Rewrite permission-compat.ts with runtime version detection - createAgentToolRestrictions() returns correct format per version - v1.1.1+ uses permission format, older uses tools format - Add migrateToolsToPermission/migratePermissionToTools helpers - Update test suite for new API 🤖 Generated with assistance of OhMyOpenCode https://github.com/code-yeongyu/oh-my-opencode * fix: update all agents to use createAgentToolRestrictions() - Replace hardcoded tools: { X: false } format with version-aware utility - All agents now use createAgentToolRestrictions([...]) - Ensures compatibility with both old and new OpenCode versions 🤖 Generated with assistance of OhMyOpenCode https://github.com/code-yeongyu/oh-my-opencode * fix: add runtime migration for user agent configs in config-handler Migrate tools/permission format in user/project/plugin agent configs based on detected OpenCode version at load time. 🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import type { AgentPromptMetadata } from "./types"
|
||||
import { createAgentToolRestrictions } from "../shared/permission-compat"
|
||||
|
||||
const DEFAULT_MODEL = "google/gemini-3-flash-preview"
|
||||
|
||||
@@ -15,12 +16,14 @@ export const DOCUMENT_WRITER_PROMPT_METADATA: AgentPromptMetadata = {
|
||||
export function createDocumentWriterAgent(
|
||||
model: string = DEFAULT_MODEL
|
||||
): AgentConfig {
|
||||
const restrictions = createAgentToolRestrictions(["background_task"])
|
||||
|
||||
return {
|
||||
description:
|
||||
"A technical writer who crafts clear, comprehensive documentation. Specializes in README files, API docs, architecture docs, and user guides. MUST BE USED when executing documentation tasks from ai-todo list plans.",
|
||||
mode: "subagent" as const,
|
||||
model,
|
||||
tools: { background_task: false },
|
||||
...restrictions,
|
||||
prompt: `<role>
|
||||
You are a TECHNICAL WRITER with deep engineering background who transforms complex codebases into crystal-clear documentation. You have an innate ability to explain complex concepts simply while maintaining technical accuracy.
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import type { AgentPromptMetadata } from "./types"
|
||||
import { createAgentToolRestrictions } from "../shared/permission-compat"
|
||||
|
||||
const DEFAULT_MODEL = "opencode/grok-code"
|
||||
|
||||
@@ -24,14 +25,19 @@ export const EXPLORE_PROMPT_METADATA: AgentPromptMetadata = {
|
||||
}
|
||||
|
||||
export function createExploreAgent(model: string = DEFAULT_MODEL): AgentConfig {
|
||||
const restrictions = createAgentToolRestrictions([
|
||||
"write",
|
||||
"edit",
|
||||
"background_task",
|
||||
])
|
||||
|
||||
return {
|
||||
description:
|
||||
'Contextual grep for codebases. Answers "Where is X?", "Which file has Y?", "Find the code that does Z". Fire multiple in parallel for broad searches. Specify thoroughness: "quick" for basic, "medium" for moderate, "very thorough" for comprehensive analysis.',
|
||||
mode: "subagent" as const,
|
||||
model,
|
||||
temperature: 0.1,
|
||||
tools: { write: false, background_task: false },
|
||||
permission: { edit: "deny" as const },
|
||||
...restrictions,
|
||||
prompt: `You are a codebase search specialist. Your job: find files and code, return actionable results.
|
||||
|
||||
## Your Mission
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import type { AgentPromptMetadata } from "./types"
|
||||
import { createAgentToolRestrictions } from "../shared/permission-compat"
|
||||
|
||||
const DEFAULT_MODEL = "google/gemini-3-pro-preview"
|
||||
|
||||
@@ -21,12 +22,14 @@ export const FRONTEND_PROMPT_METADATA: AgentPromptMetadata = {
|
||||
export function createFrontendUiUxEngineerAgent(
|
||||
model: string = DEFAULT_MODEL
|
||||
): AgentConfig {
|
||||
const restrictions = createAgentToolRestrictions(["background_task"])
|
||||
|
||||
return {
|
||||
description:
|
||||
"A designer-turned-developer who crafts stunning UI/UX even without design mockups. Code may be a bit messy, but the visual output is always fire.",
|
||||
mode: "subagent" as const,
|
||||
model,
|
||||
tools: { background_task: false },
|
||||
...restrictions,
|
||||
prompt: `# Role: Designer-Turned-Developer
|
||||
|
||||
You are a designer who learned to code. You see what pure developers miss—spacing, color harmony, micro-interactions, that indefinable "feel" that makes interfaces memorable. Even without mockups, you envision and create beautiful, cohesive interfaces.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import type { AgentPromptMetadata } from "./types"
|
||||
import { createAgentToolRestrictions } from "../shared/permission-compat"
|
||||
|
||||
const DEFAULT_MODEL = "anthropic/claude-sonnet-4-5"
|
||||
|
||||
@@ -21,13 +22,19 @@ export const LIBRARIAN_PROMPT_METADATA: AgentPromptMetadata = {
|
||||
}
|
||||
|
||||
export function createLibrarianAgent(model: string = DEFAULT_MODEL): AgentConfig {
|
||||
const restrictions = createAgentToolRestrictions([
|
||||
"write",
|
||||
"edit",
|
||||
"background_task",
|
||||
])
|
||||
|
||||
return {
|
||||
description:
|
||||
"Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search. MUST BE USED when users ask to look up code in remote repositories, explain library internals, or find usage examples in open source.",
|
||||
mode: "subagent" as const,
|
||||
model,
|
||||
temperature: 0.1,
|
||||
tools: { write: false, edit: false, background_task: false },
|
||||
...restrictions,
|
||||
prompt: `# THE LIBRARIAN
|
||||
|
||||
You are **THE LIBRARIAN**, a specialized open-source codebase understanding agent.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import type { AgentPromptMetadata } from "./types"
|
||||
import { createAgentToolRestrictions } from "../shared/permission-compat"
|
||||
|
||||
const DEFAULT_MODEL = "google/gemini-3-flash"
|
||||
|
||||
@@ -13,13 +14,20 @@ export const MULTIMODAL_LOOKER_PROMPT_METADATA: AgentPromptMetadata = {
|
||||
export function createMultimodalLookerAgent(
|
||||
model: string = DEFAULT_MODEL
|
||||
): AgentConfig {
|
||||
const restrictions = createAgentToolRestrictions([
|
||||
"write",
|
||||
"edit",
|
||||
"bash",
|
||||
"background_task",
|
||||
])
|
||||
|
||||
return {
|
||||
description:
|
||||
"Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text. Extracts specific information or summaries from documents, describes visual content. Use when you need analyzed/extracted data rather than literal file contents.",
|
||||
mode: "subagent" as const,
|
||||
model,
|
||||
temperature: 0.1,
|
||||
tools: { write: false, edit: false, bash: false, background_task: false },
|
||||
...restrictions,
|
||||
prompt: `You interpret media files that cannot be read as plain text.
|
||||
|
||||
Your job: examine the attached file and extract ONLY what was requested.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import type { AgentPromptMetadata } from "./types"
|
||||
import { isGptModel } from "./types"
|
||||
import { createAgentToolRestrictions } from "../shared/permission-compat"
|
||||
|
||||
const DEFAULT_MODEL = "openai/gpt-5.2"
|
||||
|
||||
@@ -97,22 +98,28 @@ Organize your final answer in three tiers:
|
||||
Your response goes directly to the user with no intermediate processing. Make your final message self-contained: a clear recommendation they can act on immediately, covering both what to do and why.`
|
||||
|
||||
export function createOracleAgent(model: string = DEFAULT_MODEL): AgentConfig {
|
||||
const restrictions = createAgentToolRestrictions([
|
||||
"write",
|
||||
"edit",
|
||||
"task",
|
||||
"background_task",
|
||||
])
|
||||
|
||||
const base = {
|
||||
description:
|
||||
"Expert technical advisor with deep reasoning for architecture decisions, code analysis, and engineering guidance.",
|
||||
mode: "subagent" as const,
|
||||
model,
|
||||
temperature: 0.1,
|
||||
tools: { write: false, task: false, background_task: false },
|
||||
permission: { edit: "deny" as const },
|
||||
...restrictions,
|
||||
prompt: ORACLE_SYSTEM_PROMPT,
|
||||
}
|
||||
} as AgentConfig
|
||||
|
||||
if (isGptModel(model)) {
|
||||
return { ...base, reasoningEffort: "medium", textVerbosity: "high" }
|
||||
return { ...base, reasoningEffort: "medium", textVerbosity: "high" } as AgentConfig
|
||||
}
|
||||
|
||||
return { ...base, thinking: { type: "enabled", budgetTokens: 32000 } }
|
||||
return { ...base, thinking: { type: "enabled", budgetTokens: 32000 } } as AgentConfig
|
||||
}
|
||||
|
||||
export const oracleAgent = createOracleAgent()
|
||||
|
||||
@@ -21,6 +21,7 @@ import { loadAllPluginComponents } from "../features/claude-code-plugin-loader";
|
||||
import { createBuiltinMcps } from "../mcp";
|
||||
import type { OhMyOpenCodeConfig } from "../config";
|
||||
import { log } from "../shared";
|
||||
import { migrateAgentConfig } from "../shared/permission-compat";
|
||||
import { PLAN_SYSTEM_PROMPT, PLAN_PERMISSION } from "../agents/plan-prompt";
|
||||
import type { ModelCacheState } from "../plugin-state";
|
||||
|
||||
@@ -95,13 +96,32 @@ export function createConfigHandler(deps: ConfigHandlerDeps) {
|
||||
config.model as string | undefined
|
||||
);
|
||||
|
||||
const userAgents = (pluginConfig.claude_code?.agents ?? true)
|
||||
const rawUserAgents = (pluginConfig.claude_code?.agents ?? true)
|
||||
? loadUserAgents()
|
||||
: {};
|
||||
const projectAgents = (pluginConfig.claude_code?.agents ?? true)
|
||||
const rawProjectAgents = (pluginConfig.claude_code?.agents ?? true)
|
||||
? loadProjectAgents()
|
||||
: {};
|
||||
const pluginAgents = pluginComponents.agents;
|
||||
const rawPluginAgents = pluginComponents.agents;
|
||||
|
||||
const userAgents = Object.fromEntries(
|
||||
Object.entries(rawUserAgents).map(([k, v]) => [
|
||||
k,
|
||||
v ? migrateAgentConfig(v as Record<string, unknown>) : v,
|
||||
])
|
||||
);
|
||||
const projectAgents = Object.fromEntries(
|
||||
Object.entries(rawProjectAgents).map(([k, v]) => [
|
||||
k,
|
||||
v ? migrateAgentConfig(v as Record<string, unknown>) : v,
|
||||
])
|
||||
);
|
||||
const pluginAgents = Object.fromEntries(
|
||||
Object.entries(rawPluginAgents).map(([k, v]) => [
|
||||
k,
|
||||
v ? migrateAgentConfig(v as Record<string, unknown>) : v,
|
||||
])
|
||||
);
|
||||
|
||||
const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
|
||||
const builderEnabled =
|
||||
@@ -162,15 +182,20 @@ export function createConfigHandler(deps: ConfigHandlerDeps) {
|
||||
: plannerSisyphusBase;
|
||||
}
|
||||
|
||||
const filteredConfigAgents = configAgent
|
||||
? Object.fromEntries(
|
||||
Object.entries(configAgent).filter(([key]) => {
|
||||
const filteredConfigAgents = configAgent
|
||||
? Object.fromEntries(
|
||||
Object.entries(configAgent)
|
||||
.filter(([key]) => {
|
||||
if (key === "build") return false;
|
||||
if (key === "plan" && replacePlan) return false;
|
||||
return true;
|
||||
})
|
||||
)
|
||||
: {};
|
||||
.map(([key, value]) => [
|
||||
key,
|
||||
value ? migrateAgentConfig(value as Record<string, unknown>) : value,
|
||||
])
|
||||
)
|
||||
: {};
|
||||
|
||||
config.agent = {
|
||||
...agentConfig,
|
||||
|
||||
@@ -1,161 +1,158 @@
|
||||
import { describe, test, expect } from "bun:test"
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
|
||||
import {
|
||||
createToolDenyList,
|
||||
permissionValueToBoolean,
|
||||
booleanToPermissionValue,
|
||||
convertToolsToPermission,
|
||||
convertPermissionToTools,
|
||||
createAgentRestrictions,
|
||||
createAgentToolRestrictions,
|
||||
migrateToolsToPermission,
|
||||
migratePermissionToTools,
|
||||
migrateAgentConfig,
|
||||
} from "./permission-compat"
|
||||
import { setVersionCache, resetVersionCache } from "./opencode-version"
|
||||
|
||||
describe("permission-compat", () => {
|
||||
describe("createToolDenyList", () => {
|
||||
test("creates tools config with all values false", () => {
|
||||
// #given a list of tool names
|
||||
const tools = ["write", "edit", "task"]
|
||||
beforeEach(() => {
|
||||
resetVersionCache()
|
||||
})
|
||||
|
||||
// #when creating deny list
|
||||
const result = createToolDenyList(tools)
|
||||
afterEach(() => {
|
||||
resetVersionCache()
|
||||
})
|
||||
|
||||
// #then all values are false
|
||||
expect(result).toEqual({ write: false, edit: false, task: false })
|
||||
describe("createAgentToolRestrictions", () => {
|
||||
test("returns permission format for v1.1.1+", () => {
|
||||
// #given version is 1.1.1
|
||||
setVersionCache("1.1.1")
|
||||
|
||||
// #when creating restrictions
|
||||
const result = createAgentToolRestrictions(["write", "edit"])
|
||||
|
||||
// #then returns permission format
|
||||
expect(result).toEqual({
|
||||
permission: { write: "deny", edit: "deny" },
|
||||
})
|
||||
})
|
||||
|
||||
test("returns empty object for empty array", () => {
|
||||
// #given empty array
|
||||
// #when creating deny list
|
||||
const result = createToolDenyList([])
|
||||
test("returns tools format for versions below 1.1.1", () => {
|
||||
// #given version is below 1.1.1
|
||||
setVersionCache("1.0.150")
|
||||
|
||||
// #then returns empty object
|
||||
expect(result).toEqual({})
|
||||
// #when creating restrictions
|
||||
const result = createAgentToolRestrictions(["write", "edit"])
|
||||
|
||||
// #then returns tools format
|
||||
expect(result).toEqual({
|
||||
tools: { write: false, edit: false },
|
||||
})
|
||||
})
|
||||
|
||||
test("assumes new format when version unknown", () => {
|
||||
// #given version is null
|
||||
setVersionCache(null)
|
||||
|
||||
// #when creating restrictions
|
||||
const result = createAgentToolRestrictions(["write"])
|
||||
|
||||
// #then returns permission format (assumes new version)
|
||||
expect(result).toEqual({
|
||||
permission: { write: "deny" },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("permissionValueToBoolean", () => {
|
||||
test("converts allow to true", () => {
|
||||
expect(permissionValueToBoolean("allow")).toBe(true)
|
||||
})
|
||||
|
||||
test("converts deny to false", () => {
|
||||
expect(permissionValueToBoolean("deny")).toBe(false)
|
||||
})
|
||||
|
||||
test("converts ask to false", () => {
|
||||
expect(permissionValueToBoolean("ask")).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("booleanToPermissionValue", () => {
|
||||
test("converts true to allow", () => {
|
||||
expect(booleanToPermissionValue(true)).toBe("allow")
|
||||
})
|
||||
|
||||
test("converts false to deny", () => {
|
||||
expect(booleanToPermissionValue(false)).toBe("deny")
|
||||
})
|
||||
})
|
||||
|
||||
describe("convertToolsToPermission", () => {
|
||||
test("converts boolean tools config to permission format", () => {
|
||||
// #given tools config with booleans
|
||||
describe("migrateToolsToPermission", () => {
|
||||
test("converts boolean tools to permission values", () => {
|
||||
// #given tools config
|
||||
const tools = { write: false, edit: true, bash: false }
|
||||
|
||||
// #when converting to permission
|
||||
const result = convertToolsToPermission(tools)
|
||||
// #when migrating
|
||||
const result = migrateToolsToPermission(tools)
|
||||
|
||||
// #then converts to permission values
|
||||
expect(result).toEqual({ write: "deny", edit: "allow", bash: "deny" })
|
||||
})
|
||||
|
||||
test("handles empty tools config", () => {
|
||||
// #given empty config
|
||||
// #when converting
|
||||
const result = convertToolsToPermission({})
|
||||
|
||||
// #then returns empty object
|
||||
expect(result).toEqual({})
|
||||
// #then converts correctly
|
||||
expect(result).toEqual({
|
||||
write: "deny",
|
||||
edit: "allow",
|
||||
bash: "deny",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("convertPermissionToTools", () => {
|
||||
test("converts permission to boolean tools config", () => {
|
||||
describe("migratePermissionToTools", () => {
|
||||
test("converts permission to boolean tools", () => {
|
||||
// #given permission config
|
||||
const permission = { write: "deny" as const, edit: "allow" as const }
|
||||
|
||||
// #when converting to tools
|
||||
const result = convertPermissionToTools(permission)
|
||||
// #when migrating
|
||||
const result = migratePermissionToTools(permission)
|
||||
|
||||
// #then converts to boolean values
|
||||
// #then converts correctly
|
||||
expect(result).toEqual({ write: false, edit: true })
|
||||
})
|
||||
|
||||
test("excludes ask values", () => {
|
||||
// #given permission with ask value
|
||||
// #given permission with ask
|
||||
const permission = {
|
||||
write: "deny" as const,
|
||||
edit: "ask" as const,
|
||||
bash: "allow" as const,
|
||||
}
|
||||
|
||||
// #when converting
|
||||
const result = convertPermissionToTools(permission)
|
||||
// #when migrating
|
||||
const result = migratePermissionToTools(permission)
|
||||
|
||||
// #then ask is excluded
|
||||
expect(result).toEqual({ write: false, bash: true })
|
||||
})
|
||||
})
|
||||
|
||||
describe("createAgentRestrictions", () => {
|
||||
test("creates restrictions with denied tools", () => {
|
||||
// #given deny tools list
|
||||
const config = { denyTools: ["write", "task"] }
|
||||
|
||||
// #when creating restrictions
|
||||
const result = createAgentRestrictions(config)
|
||||
|
||||
// #then creates tools config
|
||||
expect(result).toEqual({ tools: { write: false, task: false } })
|
||||
})
|
||||
|
||||
test("creates restrictions with permission", () => {
|
||||
// #given permission config
|
||||
describe("migrateAgentConfig", () => {
|
||||
test("migrates tools to permission for v1.1.1+", () => {
|
||||
// #given v1.1.1 and config with tools
|
||||
setVersionCache("1.1.1")
|
||||
const config = {
|
||||
permission: { edit: "deny" as const, bash: "ask" as const },
|
||||
model: "test",
|
||||
tools: { write: false, edit: false },
|
||||
}
|
||||
|
||||
// #when creating restrictions
|
||||
const result = createAgentRestrictions(config)
|
||||
// #when migrating
|
||||
const result = migrateAgentConfig(config)
|
||||
|
||||
// #then creates permission config
|
||||
expect(result).toEqual({
|
||||
permission: { edit: "deny", bash: "ask" },
|
||||
})
|
||||
// #then converts to permission
|
||||
expect(result.tools).toBeUndefined()
|
||||
expect(result.permission).toEqual({ write: "deny", edit: "deny" })
|
||||
expect(result.model).toBe("test")
|
||||
})
|
||||
|
||||
test("combines tools and permission", () => {
|
||||
// #given both deny tools and permission
|
||||
test("migrates permission to tools for old versions", () => {
|
||||
// #given old version and config with permission
|
||||
setVersionCache("1.0.150")
|
||||
const config = {
|
||||
denyTools: ["task"],
|
||||
permission: { edit: "deny" as const },
|
||||
model: "test",
|
||||
permission: { write: "deny" as const, edit: "deny" as const },
|
||||
}
|
||||
|
||||
// #when creating restrictions
|
||||
const result = createAgentRestrictions(config)
|
||||
// #when migrating
|
||||
const result = migrateAgentConfig(config)
|
||||
|
||||
// #then includes both
|
||||
expect(result).toEqual({
|
||||
tools: { task: false },
|
||||
permission: { edit: "deny" },
|
||||
})
|
||||
// #then converts to tools
|
||||
expect(result.permission).toBeUndefined()
|
||||
expect(result.tools).toEqual({ write: false, edit: false })
|
||||
})
|
||||
|
||||
test("returns empty object when no config provided", () => {
|
||||
// #given empty config
|
||||
// #when creating restrictions
|
||||
const result = createAgentRestrictions({})
|
||||
test("preserves other config fields", () => {
|
||||
// #given config with other fields
|
||||
setVersionCache("1.1.1")
|
||||
const config = {
|
||||
model: "test",
|
||||
temperature: 0.5,
|
||||
prompt: "hello",
|
||||
tools: { write: false },
|
||||
}
|
||||
|
||||
// #then returns empty object
|
||||
expect(result).toEqual({})
|
||||
// #when migrating
|
||||
const result = migrateAgentConfig(config)
|
||||
|
||||
// #then preserves other fields
|
||||
expect(result.model).toBe("test")
|
||||
expect(result.temperature).toBe(0.5)
|
||||
expect(result.prompt).toBe("hello")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,72 +1,78 @@
|
||||
import { supportsNewPermissionSystem as checkNewPermissionSystem } from "./opencode-version"
|
||||
import { supportsNewPermissionSystem } from "./opencode-version"
|
||||
|
||||
export type PermissionValue = "ask" | "allow" | "deny"
|
||||
export type BashPermission = PermissionValue | Record<string, PermissionValue>
|
||||
|
||||
export interface StandardPermission {
|
||||
edit?: PermissionValue
|
||||
bash?: BashPermission
|
||||
webfetch?: PermissionValue
|
||||
doom_loop?: PermissionValue
|
||||
external_directory?: PermissionValue
|
||||
export interface LegacyToolsFormat {
|
||||
tools: Record<string, boolean>
|
||||
}
|
||||
|
||||
export interface ToolsConfig {
|
||||
[toolName: string]: boolean
|
||||
export interface NewPermissionFormat {
|
||||
permission: Record<string, PermissionValue>
|
||||
}
|
||||
|
||||
export interface AgentPermissionConfig {
|
||||
permission?: StandardPermission
|
||||
tools?: ToolsConfig
|
||||
export type VersionAwareRestrictions = LegacyToolsFormat | NewPermissionFormat
|
||||
|
||||
export function createAgentToolRestrictions(
|
||||
denyTools: string[]
|
||||
): VersionAwareRestrictions {
|
||||
if (supportsNewPermissionSystem()) {
|
||||
return {
|
||||
permission: Object.fromEntries(
|
||||
denyTools.map((tool) => [tool, "deny" as const])
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tools: Object.fromEntries(denyTools.map((tool) => [tool, false])),
|
||||
}
|
||||
}
|
||||
|
||||
export { checkNewPermissionSystem as supportsNewPermissionSystemFromCompat }
|
||||
|
||||
export function createToolDenyList(toolNames: string[]): ToolsConfig {
|
||||
return Object.fromEntries(toolNames.map((name) => [name, false]))
|
||||
}
|
||||
|
||||
export function permissionValueToBoolean(value: PermissionValue): boolean {
|
||||
return value === "allow"
|
||||
}
|
||||
|
||||
export function booleanToPermissionValue(value: boolean): PermissionValue {
|
||||
return value ? "allow" : "deny"
|
||||
}
|
||||
|
||||
export function convertToolsToPermission(
|
||||
tools: ToolsConfig
|
||||
export function migrateToolsToPermission(
|
||||
tools: Record<string, boolean>
|
||||
): Record<string, PermissionValue> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(tools).map(([key, value]) => [
|
||||
key,
|
||||
booleanToPermissionValue(value),
|
||||
value ? ("allow" as const) : ("deny" as const),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
export function convertPermissionToTools(
|
||||
export function migratePermissionToTools(
|
||||
permission: Record<string, PermissionValue>
|
||||
): ToolsConfig {
|
||||
): Record<string, boolean> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(permission)
|
||||
.filter(([, value]) => value !== "ask")
|
||||
.map(([key, value]) => [key, permissionValueToBoolean(value)])
|
||||
.map(([key, value]) => [key, value === "allow"])
|
||||
)
|
||||
}
|
||||
|
||||
export function createAgentRestrictions(config: {
|
||||
denyTools?: string[]
|
||||
permission?: StandardPermission
|
||||
}): AgentPermissionConfig {
|
||||
const result: AgentPermissionConfig = {}
|
||||
export function migrateAgentConfig(
|
||||
config: Record<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
const result = { ...config }
|
||||
|
||||
if (config.denyTools && config.denyTools.length > 0) {
|
||||
result.tools = createToolDenyList(config.denyTools)
|
||||
}
|
||||
|
||||
if (config.permission) {
|
||||
result.permission = config.permission
|
||||
if (supportsNewPermissionSystem()) {
|
||||
if (result.tools && typeof result.tools === "object") {
|
||||
const existingPermission =
|
||||
(result.permission as Record<string, PermissionValue>) || {}
|
||||
const migratedPermission = migrateToolsToPermission(
|
||||
result.tools as Record<string, boolean>
|
||||
)
|
||||
result.permission = { ...migratedPermission, ...existingPermission }
|
||||
delete result.tools
|
||||
}
|
||||
} else {
|
||||
if (result.permission && typeof result.permission === "object") {
|
||||
const existingTools = (result.tools as Record<string, boolean>) || {}
|
||||
const migratedTools = migratePermissionToTools(
|
||||
result.permission as Record<string, PermissionValue>
|
||||
)
|
||||
result.tools = { ...migratedTools, ...existingTools }
|
||||
delete result.permission
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user