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:
YeonGyu-Kim
2026-01-05 05:28:25 +09:00
committed by GitHub
parent 8e8199970d
commit 5b0fb83cdf
9 changed files with 232 additions and 170 deletions
+50 -44
View File
@@ -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