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

@ -3,25 +3,25 @@ import { Money } from "@opencode-ai/schema/money"
import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { Credential } from "@opencode-ai/core/credential"
import { EventV2 } from "@opencode-ai/core/event"
import { Bus } from "@opencode-ai/core/bus"
import { Integration } from "@opencode-ai/core/integration"
import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin"
import { Model } from "@opencode-ai/core/model"
import { Plugin } from "@opencode-ai/core/plugin"
import { PluginHost } from "@opencode-ai/core/plugin/host"
import { OpencodePlugin } from "@opencode-ai/core/plugin/provider/opencode"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { Provider } from "@opencode-ai/core/provider"
import { testEffect } from "../lib/effect"
import { PluginTestLayer } from "./fixture"
const it = testEffect(PluginTestLayer)
const addPlugin = Effect.fn(function* () {
const plugin = yield* PluginV2.Service
const plugin = yield* Plugin.Service
const host = yield* PluginHost.make(plugin)
const events = yield* EventV2.Service
const bus = yield* Bus.Service
const integration = yield* Integration.Service
yield* OpencodePlugin.effect(host).pipe(
Effect.provideService(EventV2.Service, events),
Effect.provideService(Bus.Service, bus),
Effect.provideService(Integration.Service, integration),
)
})
@ -215,18 +215,18 @@ describe("OpencodePlugin", () => {
const credentials = yield* Credential.Service
const catalog = yield* Catalog.Service
yield* catalog.transform((draft) => {
draft.provider.update(ProviderV2.ID.make("remote"), () => {})
draft.model.update(ProviderV2.ID.make("remote"), ModelV2.ID.make("model"), (model) => {
draft.provider.update(Provider.ID.make("remote"), () => {})
draft.model.update(Provider.ID.make("remote"), Model.ID.make("model"), (model) => {
model.variants = [
{
id: ModelV2.VariantID.make("custom"),
id: Model.VariantID.make("custom"),
settings: {},
headers: { "x-custom": "true" },
body: { custom: true },
},
]
})
draft.model.update(ProviderV2.ID.make("remote"), ModelV2.ID.make("stale"), () => {})
draft.model.update(Provider.ID.make("remote"), Model.ID.make("stale"), () => {})
})
yield* credentials.create({
integrationID: Integration.ID.make("opencode"),
@ -240,44 +240,44 @@ describe("OpencodePlugin", () => {
yield* addPlugin()
expect(authorization).toEqual(["Bearer secret"])
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("remote")))
const provider = required(yield* catalog.provider.get(Provider.ID.make("remote")))
expect(provider).toMatchObject({
name: "Remote",
integrationID: "opencode",
package: ProviderV2.aisdk("@ai-sdk/openai-compatible"),
package: Provider.aisdk("@ai-sdk/openai-compatible"),
settings: { baseURL: `${server.url.origin}/v1`, custom: "value" },
headers: { "x-org-id": "org" },
})
expect(yield* (yield* Integration.Service).get(Integration.ID.make("remote"))).toBeUndefined()
const model = required(yield* catalog.model.get(ProviderV2.ID.make("remote"), ModelV2.ID.make("model")))
const model = required(yield* catalog.model.get(Provider.ID.make("remote"), Model.ID.make("model")))
expect(model).toMatchObject({
name: "Remote Model",
family: "remote",
capabilities: { tools: true, input: ["text", "image"], output: ["text"] },
cost: [{ input: 1, output: 2, cache: { read: 0.1, write: 0 } }],
limit: { context: 1000, output: 100 },
package: ProviderV2.aisdk("@ai-sdk/openai-compatible"),
package: Provider.aisdk("@ai-sdk/openai-compatible"),
settings: { baseURL: `${server.url.origin}/v1`, custom: "value", temperature: 0.5 },
headers: { "x-org-id": "org" },
})
expect(model.variants).toEqual([
{
id: ModelV2.VariantID.make("custom"),
id: Model.VariantID.make("custom"),
settings: {},
headers: { "x-custom": "true" },
body: { custom: true },
},
{
id: ModelV2.VariantID.make("high"),
id: Model.VariantID.make("high"),
settings: { temperature: 0.2 },
headers: {},
},
])
expect(
required(yield* catalog.model.get(ProviderV2.ID.make("remote"), ModelV2.ID.make("disabled"))).enabled,
required(yield* catalog.model.get(Provider.ID.make("remote"), Model.ID.make("disabled"))).enabled,
).toBe(false)
expect(yield* catalog.model.get(ProviderV2.ID.make("remote"), ModelV2.ID.make("stale"))).toBeDefined()
expect(yield* catalog.model.get(Provider.ID.make("remote"), Model.ID.make("stale"))).toBeDefined()
}),
({ server }) => Effect.promise(() => server.stop(true)),
),
@ -288,14 +288,14 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
package: ProviderV2.aisdk("test-provider"),
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = ModelV2.Info.make({
...ModelV2.Info.default(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
package: ProviderV2.aisdk("test-provider"),
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
@ -304,8 +304,8 @@ describe("OpencodePlugin", () => {
})
})
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBe("public")
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(false)
expect(required(yield* catalog.provider.get(Provider.ID.opencode)).settings?.apiKey).toBe("public")
expect(required(yield* catalog.model.get(Provider.ID.opencode, Model.ID.make("paid"))).enabled).toBe(false)
}),
),
)
@ -315,14 +315,14 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
package: ProviderV2.aisdk("test-provider"),
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = ModelV2.Info.make({
...ModelV2.Info.default(provider.id, ModelV2.ID.make("free")),
modelID: ModelV2.ID.make("free"),
package: ProviderV2.aisdk("test-provider"),
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("free")),
modelID: Model.ID.make("free"),
package: Provider.aisdk("test-provider"),
cost: cost(0),
})
catalog.provider.update(provider.id, () => {})
@ -331,8 +331,8 @@ describe("OpencodePlugin", () => {
})
})
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBe("public")
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("free"))).enabled).toBe(true)
expect(required(yield* catalog.provider.get(Provider.ID.opencode)).settings?.apiKey).toBe("public")
expect(required(yield* catalog.model.get(Provider.ID.opencode, Model.ID.make("free"))).enabled).toBe(true)
}),
),
)
@ -342,14 +342,14 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
package: ProviderV2.aisdk("test-provider"),
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = ModelV2.Info.make({
...ModelV2.Info.default(provider.id, ModelV2.ID.make("output-only")),
modelID: ModelV2.ID.make("output-only"),
package: ProviderV2.aisdk("test-provider"),
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("output-only")),
modelID: Model.ID.make("output-only"),
package: Provider.aisdk("test-provider"),
cost: cost(0, 1),
})
catalog.provider.update(provider.id, () => {})
@ -358,8 +358,8 @@ describe("OpencodePlugin", () => {
})
})
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBe("public")
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("output-only"))).enabled).toBe(
expect(required(yield* catalog.provider.get(Provider.ID.opencode)).settings?.apiKey).toBe("public")
expect(required(yield* catalog.model.get(Provider.ID.opencode, Model.ID.make("output-only"))).enabled).toBe(
true,
)
}),
@ -371,14 +371,14 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
package: ProviderV2.aisdk("test-provider"),
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = ModelV2.Info.make({
...ModelV2.Info.default(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
package: ProviderV2.aisdk("test-provider"),
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
@ -387,8 +387,8 @@ describe("OpencodePlugin", () => {
})
})
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBeUndefined()
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
expect(required(yield* catalog.provider.get(Provider.ID.opencode)).settings?.apiKey).toBeUndefined()
expect(required(yield* catalog.model.get(Provider.ID.opencode, Model.ID.make("paid"))).enabled).toBe(true)
}),
),
)
@ -405,14 +405,14 @@ describe("OpencodePlugin", () => {
})
})
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
package: ProviderV2.aisdk("test-provider"),
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
})
const model = ModelV2.Info.make({
...ModelV2.Info.default(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
package: ProviderV2.aisdk("test-provider"),
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
@ -421,8 +421,8 @@ describe("OpencodePlugin", () => {
})
})
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBeUndefined()
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
expect(required(yield* catalog.provider.get(Provider.ID.opencode)).settings?.apiKey).toBeUndefined()
expect(required(yield* catalog.model.get(Provider.ID.opencode, Model.ID.make("paid"))).enabled).toBe(true)
}),
),
)
@ -432,15 +432,15 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
package: ProviderV2.aisdk("test-provider"),
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.opencode),
package: Provider.aisdk("test-provider"),
settings: { apiKey: "configured" },
})
const model = ModelV2.Info.make({
...ModelV2.Info.default(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
package: ProviderV2.aisdk("test-provider"),
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, (draft) => {
@ -452,8 +452,8 @@ describe("OpencodePlugin", () => {
})
})
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBe("configured")
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
expect(required(yield* catalog.provider.get(Provider.ID.opencode)).settings?.apiKey).toBe("configured")
expect(required(yield* catalog.model.get(Provider.ID.opencode, Model.ID.make("paid"))).enabled).toBe(true)
}),
),
)
@ -463,14 +463,14 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.openai),
package: ProviderV2.aisdk("test-provider"),
const provider = Provider.Info.make({
...Provider.Info.empty(Provider.ID.openai),
package: Provider.aisdk("test-provider"),
})
const model = ModelV2.Info.make({
...ModelV2.Info.default(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
package: ProviderV2.aisdk("test-provider"),
const model = Model.Info.make({
...Model.Info.default(provider.id, Model.ID.make("paid")),
modelID: Model.ID.make("paid"),
package: Provider.aisdk("test-provider"),
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
@ -479,8 +479,8 @@ describe("OpencodePlugin", () => {
})
})
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.openai)).settings?.apiKey).toBeUndefined()
expect(required(yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("paid"))).enabled).toBe(true)
expect(required(yield* catalog.provider.get(Provider.ID.openai)).settings?.apiKey).toBeUndefined()
expect(required(yield* catalog.model.get(Provider.ID.openai, Model.ID.make("paid"))).enabled).toBe(true)
}),
),
)
@ -488,17 +488,17 @@ describe("OpencodePlugin", () => {
it.effect("prefers gpt-5-nano as the opencode small model", () =>
Effect.gen(function* () {
const catalog = yield* Catalog.Service
const providerID = ProviderV2.ID.opencode
const providerID = Provider.ID.opencode
yield* catalog.transform((catalog) => {
catalog.provider.update(providerID, () => {})
catalog.model.update(providerID, ModelV2.ID.make("cheap-mini"), (model) => {
catalog.model.update(providerID, Model.ID.make("cheap-mini"), (model) => {
model.capabilities.input = ["text"]
model.capabilities.output = ["text"]
model.cost = [...cost(1, 1)]
model.time.released = Date.now()
})
catalog.model.update(providerID, ModelV2.ID.make("gpt-5-nano"), (model) => {
catalog.model.update(providerID, Model.ID.make("gpt-5-nano"), (model) => {
model.capabilities.input = ["text"]
model.capabilities.output = ["text"]
model.cost = [...cost(10, 10)]
@ -508,7 +508,7 @@ describe("OpencodePlugin", () => {
const selected = yield* catalog.model.small(providerID)
expect(selected?.id).toBe(ModelV2.ID.make("gpt-5-nano"))
expect(selected?.id).toBe(Model.ID.make("gpt-5-nano"))
}),
)
})