2026-01-05 05:28:25 +09:00
|
|
|
import { supportsNewPermissionSystem } from "./opencode-version"
|
2026-01-05 04:26:26 +09:00
|
|
|
|
2026-01-12 19:46:47 +11:00
|
|
|
export { supportsNewPermissionSystem }
|
|
|
|
|
|
2026-01-05 04:26:26 +09:00
|
|
|
export type PermissionValue = "ask" | "allow" | "deny"
|
|
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
export interface LegacyToolsFormat {
|
|
|
|
|
tools: Record<string, boolean>
|
2026-01-05 04:26:26 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
export interface NewPermissionFormat {
|
|
|
|
|
permission: Record<string, PermissionValue>
|
2026-01-05 04:26:26 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
export type VersionAwareRestrictions = LegacyToolsFormat | NewPermissionFormat
|
2026-01-05 04:26:26 +09:00
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
export function createAgentToolRestrictions(
|
|
|
|
|
denyTools: string[]
|
|
|
|
|
): VersionAwareRestrictions {
|
|
|
|
|
if (supportsNewPermissionSystem()) {
|
|
|
|
|
return {
|
|
|
|
|
permission: Object.fromEntries(
|
|
|
|
|
denyTools.map((tool) => [tool, "deny" as const])
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-05 04:26:26 +09:00
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
return {
|
|
|
|
|
tools: Object.fromEntries(denyTools.map((tool) => [tool, false])),
|
|
|
|
|
}
|
2026-01-05 04:26:26 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-16 15:02:55 +09:00
|
|
|
/**
|
|
|
|
|
* Common tools that should be denied when using allowlist approach.
|
|
|
|
|
* Used for legacy fallback when `*: deny` pattern is not supported.
|
|
|
|
|
*/
|
|
|
|
|
const COMMON_TOOLS_TO_DENY = [
|
|
|
|
|
"write",
|
|
|
|
|
"edit",
|
|
|
|
|
"bash",
|
|
|
|
|
"task",
|
|
|
|
|
"sisyphus_task",
|
|
|
|
|
"call_omo_agent",
|
|
|
|
|
"webfetch",
|
|
|
|
|
"glob",
|
|
|
|
|
"grep",
|
|
|
|
|
"lsp_diagnostics",
|
|
|
|
|
"lsp_prepare_rename",
|
|
|
|
|
"lsp_rename",
|
|
|
|
|
"ast_grep_search",
|
|
|
|
|
"ast_grep_replace",
|
|
|
|
|
"session_list",
|
|
|
|
|
"session_read",
|
|
|
|
|
"session_search",
|
|
|
|
|
"session_info",
|
|
|
|
|
"background_output",
|
|
|
|
|
"background_cancel",
|
|
|
|
|
"skill",
|
|
|
|
|
"skill_mcp",
|
|
|
|
|
"look_at",
|
|
|
|
|
"todowrite",
|
|
|
|
|
"todoread",
|
|
|
|
|
"interactive_bash",
|
|
|
|
|
] as const
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates tool restrictions that ONLY allow specified tools.
|
|
|
|
|
* All other tools are denied by default.
|
|
|
|
|
*
|
|
|
|
|
* Uses `*: deny` pattern for new permission system,
|
|
|
|
|
* falls back to explicit deny list for legacy systems.
|
|
|
|
|
*/
|
|
|
|
|
export function createAgentToolAllowlist(
|
|
|
|
|
allowTools: string[]
|
|
|
|
|
): VersionAwareRestrictions {
|
|
|
|
|
if (supportsNewPermissionSystem()) {
|
|
|
|
|
return {
|
|
|
|
|
permission: {
|
|
|
|
|
"*": "deny" as const,
|
|
|
|
|
...Object.fromEntries(
|
|
|
|
|
allowTools.map((tool) => [tool, "allow" as const])
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Legacy fallback: explicitly deny common tools except allowed ones
|
|
|
|
|
const allowSet = new Set(allowTools)
|
|
|
|
|
const denyTools = COMMON_TOOLS_TO_DENY.filter((tool) => !allowSet.has(tool))
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
tools: Object.fromEntries(denyTools.map((tool) => [tool, false])),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
export function migrateToolsToPermission(
|
|
|
|
|
tools: Record<string, boolean>
|
2026-01-05 04:26:26 +09:00
|
|
|
): Record<string, PermissionValue> {
|
|
|
|
|
return Object.fromEntries(
|
|
|
|
|
Object.entries(tools).map(([key, value]) => [
|
|
|
|
|
key,
|
2026-01-05 05:28:25 +09:00
|
|
|
value ? ("allow" as const) : ("deny" as const),
|
2026-01-05 04:26:26 +09:00
|
|
|
])
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
export function migratePermissionToTools(
|
2026-01-05 04:26:26 +09:00
|
|
|
permission: Record<string, PermissionValue>
|
2026-01-05 05:28:25 +09:00
|
|
|
): Record<string, boolean> {
|
2026-01-05 04:26:26 +09:00
|
|
|
return Object.fromEntries(
|
|
|
|
|
Object.entries(permission)
|
|
|
|
|
.filter(([, value]) => value !== "ask")
|
2026-01-05 05:28:25 +09:00
|
|
|
.map(([key, value]) => [key, value === "allow"])
|
2026-01-05 04:26:26 +09:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
export function migrateAgentConfig(
|
|
|
|
|
config: Record<string, unknown>
|
|
|
|
|
): Record<string, unknown> {
|
|
|
|
|
const result = { ...config }
|
2026-01-05 04:26:26 +09:00
|
|
|
|
2026-01-05 05:28:25 +09:00
|
|
|
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
|
|
|
|
|
}
|
2026-01-05 04:26:26 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
}
|