refactor(core): consolidate tool architecture

This commit is contained in:
Dax Raad 2026-07-26 20:08:55 -04:00
commit 8db7487c89
466 changed files with 9405 additions and 11071 deletions

View file

@ -8,7 +8,7 @@ import { PositiveInt } from "../schema"
export const Color = Schema.String.check(Schema.isPattern(/^#[0-9a-fA-F]{6}$/))
export class Info extends Schema.Class<Info>("ConfigV2.Agent")({
export class Info extends Schema.Class<Info>("Config.Agent")({
model: ConfigModel.Selection.pipe(Schema.optional),
request: ConfigProvider.Request.pipe(Schema.optional),
system: Schema.String.pipe(Schema.optional),

View file

@ -3,13 +3,13 @@ export * as ConfigAttachments from "./attachments"
import { Schema } from "effect"
import { PositiveInt } from "../schema"
export class Image extends Schema.Class<Image>("ConfigV2.Attachments.Image")({
export class Image extends Schema.Class<Image>("Config.Attachments.Image")({
auto_resize: Schema.Boolean.pipe(Schema.optional),
max_width: PositiveInt.pipe(Schema.optional),
max_height: PositiveInt.pipe(Schema.optional),
max_base64_bytes: PositiveInt.pipe(Schema.optional),
}) {}
export class Info extends Schema.Class<Info>("ConfigV2.Attachments")({
export class Info extends Schema.Class<Info>("Config.Attachments")({
image: Image.pipe(Schema.optional),
}) {}

View file

@ -3,7 +3,7 @@ export * as ConfigCommand from "./command"
import { Schema } from "effect"
import { ConfigModel } from "./model"
export class Info extends Schema.Class<Info>("ConfigV2.Command")({
export class Info extends Schema.Class<Info>("Config.Command")({
template: Schema.String,
description: Schema.String.pipe(Schema.optional),
agent: Schema.String.pipe(Schema.optional),

View file

@ -3,11 +3,11 @@ export * as ConfigCompaction from "./compaction"
import { Schema } from "effect"
import { NonNegativeInt } from "../schema"
export class Keep extends Schema.Class<Keep>("ConfigV2.Compaction.Keep")({
export class Keep extends Schema.Class<Keep>("Config.Compaction.Keep")({
tokens: NonNegativeInt.pipe(Schema.optional),
}) {}
export class Info extends Schema.Class<Info>("ConfigV2.Compaction")({
export class Info extends Schema.Class<Info>("Config.Compaction")({
auto: Schema.Boolean.pipe(Schema.optional),
keep: Keep.pipe(Schema.optional),
buffer: NonNegativeInt.pipe(Schema.optional),

View file

@ -2,7 +2,7 @@ export * as ConfigFormatter from "./formatter"
import { Schema } from "effect"
export class Entry extends Schema.Class<Entry>("ConfigV2.Formatter.Entry")({
export class Entry extends Schema.Class<Entry>("Config.Formatter.Entry")({
disabled: Schema.Boolean.pipe(Schema.optional),
command: Schema.String.pipe(Schema.Array, Schema.optional),
environment: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional),

View file

@ -6,7 +6,7 @@ export const Disabled = Schema.Struct({
disabled: Schema.Literal(true),
})
export class Server extends Schema.Class<Server>("ConfigV2.LSP.Server")({
export class Server extends Schema.Class<Server>("Config.LSP.Server")({
command: Schema.String.pipe(Schema.Array),
extensions: Schema.String.pipe(Schema.Array, Schema.optional),
disabled: Schema.Boolean.pipe(Schema.optional),

View file

@ -15,7 +15,7 @@ export const Remote = Mcp.RemoteConfig
export type Remote = Mcp.RemoteConfig
export const Server = Mcp.ServerConfig
export class Info extends Schema.Class<Info>("ConfigV2.MCP")({
export class Info extends Schema.Class<Info>("Config.MCP")({
timeout: Timeout.pipe(Schema.optional),
servers: Schema.Record(Schema.String, Server).pipe(Schema.optional),
}) {}

View file

@ -2,7 +2,7 @@ export * as ConfigPlugin from "./plugin"
import { Schema } from "effect"
export class Entry extends Schema.Class<Entry>("ConfigV2.Plugin.Entry")({
export class Entry extends Schema.Class<Entry>("Config.Plugin.Entry")({
package: Schema.String,
options: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
}) {}

View file

@ -1,9 +1,9 @@
export * as ConfigAgentPlugin from "./agent"
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
import { define } from "@opencode-ai/plugin/effect/plugin"
import path from "path"
import { Effect, Option, Schema, Stream } from "effect"
import { AgentV2 } from "../../agent"
import { Agent } from "../../agent"
import { Config } from "../../config"
import { ConfigAgent } from "../agent"
import { ConfigMarkdown } from "../markdown"
@ -11,10 +11,10 @@ import { FSUtil } from "@opencode-ai/util/fs-util"
import { ConfigAgentV1 } from "../../v1/config/agent"
import { ConfigMigrateV1 } from "../../v1/config/migrate"
import { Global } from "@opencode-ai/util/global"
import { PermissionV2 } from "../../permission"
import { Permission } from "../../permission"
import type { LocationMutation } from "../../location-mutation"
import type { ReadTool } from "../../tool/read"
import type { EditTool } from "../../tool/edit"
import type { ReadTool } from "../../tool/plugin/read"
import type { EditTool } from "../../tool/plugin/edit"
const legacySources = [
{ pattern: "{agent,agents}/**/*.md", primary: false },
@ -74,14 +74,14 @@ export const Plugin = define({
global.home,
)
const configuredDefault = Config.latest(loaded.documents, "default_agent")
if (configuredDefault !== undefined) draft.default(AgentV2.ID.make(configuredDefault))
if (configuredDefault !== undefined) draft.default(Agent.ID.make(configuredDefault))
for (const current of draft.list()) {
draft.update(current.id, (agent) => agent.permissions.push(...permissions))
}
for (const document of loaded.documents) {
for (const [id, item] of Object.entries(document.info.agents ?? {})) {
const agentID = AgentV2.ID.make(id)
const agentID = Agent.ID.make(id)
if (item.disabled) {
draft.remove(agentID)
continue
@ -126,7 +126,7 @@ export const Plugin = define({
}),
})
function expandPermissions(rules: PermissionV2.Ruleset, home: string): PermissionV2.Ruleset {
function expandPermissions(rules: Permission.Ruleset, home: string): Permission.Ruleset {
// Expand only resources tools resolve as filesystem paths. Bash resources are raw shell text:
// rewriting `$HOME/private/**` would miss `$HOME/private/key`, and safe expansion needs shell-aware parsing.
return rules.map((rule) =>

View file

@ -1,9 +1,9 @@
export * as ConfigCommandPlugin from "./command"
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
import { define } from "@opencode-ai/plugin/effect/plugin"
import path from "path"
import { Effect, Option, Schema, Stream } from "effect"
import { CommandV2 } from "../../command"
import { Command } from "../../command"
import { Config } from "../../config"
import { FSUtil } from "@opencode-ai/util/fs-util"
import { ConfigCommand } from "../command"

View file

@ -1,6 +1,6 @@
export * as ConfigPolicyPlugin from "./policy"
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
import { define } from "@opencode-ai/plugin/effect/plugin"
import { Effect, Stream } from "effect"
import { Config } from "../../config"
import { Wildcard } from "../../util/wildcard"

View file

@ -1,10 +1,10 @@
export * as ConfigProviderPlugin from "./provider"
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
import { define } from "@opencode-ai/plugin/effect/plugin"
import { Money } from "@opencode-ai/schema/money"
import { Effect, Stream } from "effect"
import { Config } from "../../config"
import { ProviderV2 } from "../../provider"
import { Provider } from "../../provider"
export const Plugin = define({
id: "opencode.config.provider",
@ -49,9 +49,9 @@ export const Plugin = define({
if (item.name !== undefined) provider.name = item.name
if (item.package !== undefined) provider.package = item.package
if (item.settings !== undefined)
provider.settings = ProviderV2.mergeOverlay(provider.settings, item.settings)
if (item.headers !== undefined) provider.headers = ProviderV2.mergeHeaders(provider.headers, item.headers)
if (item.body !== undefined) provider.body = ProviderV2.mergeOverlay(provider.body, item.body)
provider.settings = Provider.mergeOverlay(provider.settings, item.settings)
if (item.headers !== undefined) provider.headers = Provider.mergeHeaders(provider.headers, item.headers)
if (item.body !== undefined) provider.body = Provider.mergeOverlay(provider.body, item.body)
})
for (const [id, config] of Object.entries(item.models ?? {})) {
catalog.model.update(providerID, id, (model) => {
@ -62,9 +62,9 @@ export const Plugin = define({
model.compatibility = { ...model.compatibility, ...config.compatibility }
if (config.package !== undefined) model.package = config.package
if (config.settings !== undefined)
model.settings = ProviderV2.mergeOverlay(model.settings, config.settings)
if (config.headers !== undefined) model.headers = ProviderV2.mergeHeaders(model.headers, config.headers)
if (config.body !== undefined) model.body = ProviderV2.mergeOverlay(model.body, config.body)
model.settings = Provider.mergeOverlay(model.settings, config.settings)
if (config.headers !== undefined) model.headers = Provider.mergeHeaders(model.headers, config.headers)
if (config.body !== undefined) model.body = Provider.mergeOverlay(model.body, config.body)
if (config.capabilities !== undefined) {
model.capabilities = {
tools: config.capabilities.tools,
@ -81,10 +81,10 @@ export const Plugin = define({
model.variants.push(existing)
}
if (variant.settings !== undefined)
existing.settings = ProviderV2.mergeOverlay(existing.settings, variant.settings)
existing.settings = Provider.mergeOverlay(existing.settings, variant.settings)
if (variant.headers !== undefined)
existing.headers = ProviderV2.mergeHeaders(existing.headers, variant.headers)
if (variant.body !== undefined) existing.body = ProviderV2.mergeOverlay(existing.body, variant.body)
existing.headers = Provider.mergeHeaders(existing.headers, variant.headers)
if (variant.body !== undefined) existing.body = Provider.mergeOverlay(existing.body, variant.body)
}
}
if (config.cost !== undefined) {

View file

@ -1,6 +1,6 @@
export * as ConfigReferencePlugin from "./reference"
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
import { define } from "@opencode-ai/plugin/effect/plugin"
import path from "path"
import { Effect, Stream } from "effect"
import { Config } from "../../config"

View file

@ -1,11 +1,11 @@
export * as ConfigSkillPlugin from "./skill"
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
import { define } from "@opencode-ai/plugin/effect/plugin"
import path from "path"
import { Effect, Stream } from "effect"
import { Config } from "../../config"
import { AbsolutePath } from "../../schema"
import { SkillV2 } from "../../skill"
import { Skill } from "../../skill"
import { Global } from "@opencode-ai/util/global"
import { Location } from "../../location"
@ -23,7 +23,7 @@ export const Plugin = define({
const items = loaded.entries.flatMap((entry) => (entry.type === "document" ? (entry.info.skills ?? []) : []))
for (const directory of [...claude, ...agents]) {
draft.source(
SkillV2.DirectorySource.make({
Skill.DirectorySource.make({
type: "directory",
path: AbsolutePath.make(path.join(directory, "skills")),
}),
@ -31,10 +31,10 @@ export const Plugin = define({
}
for (const directory of directories) {
draft.source(
SkillV2.DirectorySource.make({ type: "directory", path: AbsolutePath.make(path.join(directory, "skill")) }),
Skill.DirectorySource.make({ type: "directory", path: AbsolutePath.make(path.join(directory, "skill")) }),
)
draft.source(
SkillV2.DirectorySource.make({
Skill.DirectorySource.make({
type: "directory",
path: AbsolutePath.make(path.join(directory, "skills")),
}),
@ -42,12 +42,12 @@ export const Plugin = define({
}
for (const item of items) {
if (URL.canParse(item) && /^(https?:)$/.test(new URL(item).protocol)) {
draft.source(SkillV2.UrlSource.make({ type: "url", url: item }))
draft.source(Skill.UrlSource.make({ type: "url", url: item }))
continue
}
const expanded = item.startsWith("~/") ? path.join(global.home, item.slice(2)) : item
draft.source(
SkillV2.DirectorySource.make({
Skill.DirectorySource.make({
type: "directory",
path: AbsolutePath.make(path.isAbsolute(expanded) ? expanded : path.join(location.directory, expanded)),
}),

View file

@ -1,6 +1,6 @@
export * as ConfigWebSearchPlugin from "./websearch"
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
import { define } from "@opencode-ai/plugin/effect/plugin"
import { Effect, Stream } from "effect"
import { Config } from "../../config"

View file

@ -2,7 +2,7 @@ export * as ConfigProvider from "./provider"
import { Schema } from "effect"
import { Money } from "@opencode-ai/schema/money"
import { ModelV2 } from "../model"
import { Capabilities, Compatibility, Family, ID, VariantID } from "../model"
const JsonRecord = Schema.Record(Schema.String, Schema.Json)
@ -12,17 +12,17 @@ export const Overlays = {
body: JsonRecord.pipe(Schema.optional),
}
export class Request extends Schema.Class<Request>("ConfigV2.Provider.Request")({
export class Request extends Schema.Class<Request>("Config.Provider.Request")({
headers: Overlays.headers,
body: Overlays.body,
}) {}
class Cache extends Schema.Class<Cache>("ConfigV2.Model.Cost.Cache")({
class Cache extends Schema.Class<Cache>("Config.Model.Cost.Cache")({
read: Money.USDPerMillionTokens.pipe(Schema.optional),
write: Money.USDPerMillionTokens.pipe(Schema.optional),
}) {}
class Cost extends Schema.Class<Cost>("ConfigV2.Model.Cost")({
class Cost extends Schema.Class<Cost>("Config.Model.Cost")({
tier: Schema.Struct({
type: Schema.Literal("context"),
size: Schema.Int,
@ -32,22 +32,22 @@ class Cost extends Schema.Class<Cost>("ConfigV2.Model.Cost")({
cache: Cache.pipe(Schema.optional),
}) {}
class Limit extends Schema.Class<Limit>("ConfigV2.Model.Limit")({
class Limit extends Schema.Class<Limit>("Config.Model.Limit")({
context: Schema.Int.pipe(Schema.optional),
input: Schema.Int.pipe(Schema.optional),
output: Schema.Int.pipe(Schema.optional),
}) {}
class Model extends Schema.Class<Model>("ConfigV2.Model")({
modelID: ModelV2.ID.pipe(Schema.optional),
family: ModelV2.Family.pipe(Schema.optional),
class Model extends Schema.Class<Model>("Config.Model")({
modelID: ID.pipe(Schema.optional),
family: Family.pipe(Schema.optional),
name: Schema.String.pipe(Schema.optional),
compatibility: ModelV2.Compatibility.pipe(Schema.optional),
compatibility: Compatibility.pipe(Schema.optional),
package: Schema.String.pipe(Schema.optional),
...Overlays,
capabilities: ModelV2.Capabilities.pipe(Schema.optional),
capabilities: Capabilities.pipe(Schema.optional),
variants: Schema.Struct({
id: ModelV2.VariantID,
id: VariantID,
...Overlays,
}).pipe(Schema.Array, Schema.optional),
cost: Schema.Union([Cost, Cost.pipe(Schema.Array)]).pipe(Schema.optional),
@ -55,7 +55,7 @@ class Model extends Schema.Class<Model>("ConfigV2.Model")({
limit: Limit.pipe(Schema.optional),
}) {}
export class Info extends Schema.Class<Info>("ConfigV2.Provider")({
export class Info extends Schema.Class<Info>("Config.Provider")({
name: Schema.String.pipe(Schema.optional),
env: Schema.String.pipe(Schema.Array, Schema.optional),
package: Schema.String.pipe(Schema.optional),

View file

@ -2,14 +2,14 @@ export * as ConfigReference from "./reference"
import { Schema } from "effect"
export class Git extends Schema.Class<Git>("ConfigV2.Reference.Git")({
export class Git extends Schema.Class<Git>("Config.Reference.Git")({
repository: Schema.String,
branch: Schema.String.pipe(Schema.optional),
description: Schema.String.pipe(Schema.optional),
hidden: Schema.Boolean.pipe(Schema.optional),
}) {}
export class Local extends Schema.Class<Local>("ConfigV2.Reference.Local")({
export class Local extends Schema.Class<Local>("Config.Reference.Local")({
path: Schema.String,
description: Schema.String.pipe(Schema.optional),
hidden: Schema.Boolean.pipe(Schema.optional),

View file

@ -3,7 +3,7 @@ export * as ConfigToolOutput from "./tool-output"
import { Schema } from "effect"
import { PositiveInt } from "../schema"
export class Info extends Schema.Class<Info>("ConfigV2.ToolOutput")({
export class Info extends Schema.Class<Info>("Config.ToolOutput")({
max_lines: PositiveInt.pipe(Schema.optional),
max_bytes: PositiveInt.pipe(Schema.optional),
}) {}

View file

@ -2,7 +2,7 @@ export * as ConfigWarming from "./warming"
import { Schema } from "effect"
export class Info extends Schema.Class<Info>("ConfigV2.Warming")({
export class Info extends Schema.Class<Info>("Config.Warming")({
prompt: Schema.String.pipe(Schema.optional).annotate({
description: "Prompt sent for keep-alive requests",
}),

View file

@ -2,6 +2,6 @@ export * as ConfigWatcher from "./watcher"
import { Schema } from "effect"
export class Info extends Schema.Class<Info>("ConfigV2.Watcher")({
export class Info extends Schema.Class<Info>("Config.Watcher")({
ignore: Schema.String.pipe(Schema.Array, Schema.optional),
}) {}