feat(plugin): add namespaced hook API (#33416)

This commit is contained in:
Dax 2026-06-22 19:06:57 -04:00 committed by GitHub
commit 909a1a6d78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
150 changed files with 3276 additions and 3906 deletions

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const AlibabaPlugin = define({
id: "alibaba",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/alibaba") return
const mod = yield* Effect.promise(() => import("@ai-sdk/alibaba"))

View file

@ -1,6 +1,6 @@
import { Effect } from "effect"
import type { LanguageModelV3 } from "@ai-sdk/provider"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
type MantleSDK = {
@ -78,8 +78,7 @@ export const AmazonBedrockPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (!["@ai-sdk/amazon-bedrock", "@ai-sdk/amazon-bedrock/mantle"].includes(evt.package)) return
const options = { ...evt.options }
@ -112,8 +111,7 @@ export const AmazonBedrockPlugin = define({
evt.sdk = mod.createAmazonBedrock(options)
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.amazonBedrock) return
if (evt.model.api.type === "aisdk" && evt.model.api.package === "@ai-sdk/amazon-bedrock/mantle") {

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const AnthropicPlugin = define({
id: "anthropic",
@ -16,8 +16,7 @@ export const AnthropicPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/anthropic") return
const mod = yield* Effect.promise(() => import("@ai-sdk/anthropic"))

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
function selectLanguage(sdk: any, modelID: string, useChat: boolean) {
@ -28,8 +28,7 @@ export const AzurePlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/azure") return
if (evt.model.providerID === ProviderV2.ID.azure) {
@ -47,8 +46,7 @@ export const AzurePlugin = define({
evt.sdk = mod.createAzure(evt.options)
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.azure) return
evt.language = selectLanguage(evt.sdk, evt.model.api.id, Boolean(evt.options.useCompletionUrls))
@ -74,8 +72,7 @@ export const AzureCognitiveServicesPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("azure-cognitive-services")) return
evt.language = selectLanguage(evt.sdk, evt.model.api.id, Boolean(evt.options.useCompletionUrls))

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const CerebrasPlugin = define({
id: "cerebras",
@ -15,8 +15,7 @@ export const CerebrasPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/cerebras") return
const mod = yield* Effect.promise(() => import("@ai-sdk/cerebras"))

View file

@ -1,13 +1,12 @@
import os from "os"
import { InstallationVersion } from "../../installation/version"
import { Effect, Option, Schema } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const CloudflareAIGatewayPlugin = define({
id: "cloudflare-ai-gateway",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "ai-gateway-provider") return
if (evt.options.baseURL) return

View file

@ -1,7 +1,7 @@
import os from "os"
import { InstallationVersion } from "../../installation/version"
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
const providerID = ProviderV2.ID.make("cloudflare-workers-ai")
@ -21,8 +21,7 @@ export const CloudflareWorkersAIPlugin = define({
})
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.model.providerID !== providerID) return
if (evt.package !== "@ai-sdk/openai-compatible") return
@ -38,8 +37,7 @@ export const CloudflareWorkersAIPlugin = define({
)
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== providerID) return
evt.language = evt.sdk.languageModel(evt.model.api.id)

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const CoherePlugin = define({
id: "cohere",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/cohere") return
const mod = yield* Effect.promise(() => import("@ai-sdk/cohere"))

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const DeepInfraPlugin = define({
id: "deepinfra",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/deepinfra") return
const mod = yield* Effect.promise(() => import("@ai-sdk/deepinfra"))

View file

@ -1,18 +1,19 @@
import { Effect } from "effect"
import { pathToFileURL } from "url"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { Npm } from "../../npm"
export const DynamicProviderPlugin = define({
id: "dynamic-provider",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
const npm = yield* Npm.Service
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.sdk) return
const installedPath = evt.package.startsWith("file://")
? evt.package
: (yield* ctx.npm.add(evt.package).pipe(Effect.orDie)).entrypoint
: (yield* npm.add(evt.package).pipe(Effect.orDie)).entrypoint
if (!installedPath) throw new Error(`Package ${evt.package} has no import entrypoint`)
const mod = yield* Effect.promise(async () => {

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const GatewayPlugin = define({
id: "gateway",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/gateway") return
const mod = yield* Effect.promise(() => import("@ai-sdk/gateway"))

View file

@ -1,6 +1,6 @@
import { Effect } from "effect"
import { ModelV2 } from "../../model"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
function shouldUseResponses(modelID: string) {
@ -25,16 +25,14 @@ export const GithubCopilotPlugin = define({
})
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/github-copilot") return
const mod = yield* Effect.promise(() => import("../../github-copilot/copilot-provider"))
evt.sdk = mod.createOpenaiCompatible(evt.options)
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.githubCopilot) return
if (evt.sdk.responses === undefined && evt.sdk.chat === undefined) {

View file

@ -1,14 +1,13 @@
import os from "os"
import { InstallationVersion } from "../../installation/version"
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
export const GitLabPlugin = define({
id: "gitlab",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "gitlab-ai-provider") return
const mod = yield* Effect.promise(() => import("gitlab-ai-provider"))
@ -32,8 +31,7 @@ export const GitLabPlugin = define({
})
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.gitlab) return
const featureFlags =

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
function resolveProject(options: Record<string, any>) {
@ -84,8 +84,7 @@ export const GoogleVertexPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.model.providerID === ProviderV2.ID.googleVertex && evt.package.includes("@ai-sdk/openai-compatible")) {
evt.options.fetch = authFetch(evt.options.fetch)
@ -104,8 +103,7 @@ export const GoogleVertexPlugin = define({
})
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.googleVertex) return
evt.language = evt.sdk.languageModel(String(evt.model.api.id).trim())
@ -139,8 +137,7 @@ export const GoogleVertexAnthropicPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/google-vertex/anthropic") return
const mod = yield* Effect.promise(() => import("@ai-sdk/google-vertex/anthropic"))
@ -166,8 +163,7 @@ export const GoogleVertexAnthropicPlugin = define({
})
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("google-vertex-anthropic")) return
evt.language = evt.sdk.languageModel(String(evt.model.api.id).trim())

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const GooglePlugin = define({
id: "google",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/google") return
const mod = yield* Effect.promise(() => import("@ai-sdk/google"))

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const GroqPlugin = define({
id: "groq",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/groq") return
const mod = yield* Effect.promise(() => import("@ai-sdk/groq"))

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const KiloPlugin = define({
id: "kilo",

View file

@ -1,9 +1,11 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { Integration } from "../../integration"
export const LLMGatewayPlugin = define({
id: "llmgateway",
effect: Effect.fn(function* (ctx) {
const integrations = yield* Integration.Service
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
@ -11,7 +13,7 @@ export const LLMGatewayPlugin = define({
if (item.provider.api.type !== "aisdk") continue
if (item.provider.api.package !== "@ai-sdk/openai-compatible") continue
if (item.provider.api.url !== "https://api.llmgateway.io/v1") continue
if (!(yield* ctx.integration.get(item.provider.id))) continue
if (!(yield* integrations.get(Integration.ID.make(item.provider.id)))) continue
evt.provider.update(item.provider.id, (provider) => {
provider.request.headers["HTTP-Referer"] = "https://opencode.ai/"
provider.request.headers["X-Title"] = "opencode"

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const MistralPlugin = define({
id: "mistral",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/mistral") return
const mod = yield* Effect.promise(() => import("@ai-sdk/mistral"))

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const NvidiaPlugin = define({
id: "nvidia",

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const OpenAICompatiblePlugin = define({
id: "openai-compatible",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.sdk) return
if (!evt.package.includes("@ai-sdk/openai-compatible")) return

View file

@ -1,6 +1,6 @@
import { Effect } from "effect"
import { ModelV2 } from "../../model"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
import { Integration } from "../../integration"
import { browser, headless } from "./openai-auth"
@ -27,16 +27,14 @@ export const OpenAIPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/openai") return
const mod = yield* Effect.promise(() => import("@ai-sdk/openai"))
evt.sdk = mod.createOpenAI(evt.options)
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.openai) return
evt.language = evt.sdk.responses(evt.model.api.id)

View file

@ -1,16 +1,18 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
import { Integration } from "../../integration"
export const OpencodePlugin = define({
id: "opencode",
effect: Effect.fn(function* (ctx) {
const integrations = yield* Integration.Service
let hasKey = false
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
const item = evt.provider.get(ProviderV2.ID.opencode)
if (!item) return
const integration = yield* ctx.integration.get(item.provider.id)
const integration = yield* integrations.get(Integration.ID.make(item.provider.id))
hasKey = Boolean(
process.env.OPENCODE_API_KEY || integration?.connections.length || item.provider.request.body.apiKey,
)

View file

@ -1,6 +1,6 @@
import { Effect } from "effect"
import { ModelV2 } from "../../model"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const OpenRouterPlugin = define({
id: "openrouter",
@ -25,8 +25,7 @@ export const OpenRouterPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@openrouter/ai-sdk-provider") return
const mod = yield* Effect.promise(() => import("@openrouter/ai-sdk-provider"))

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const PerplexityPlugin = define({
id: "perplexity",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/perplexity") return
const mod = yield* Effect.promise(() => import("@ai-sdk/perplexity"))

View file

@ -1,13 +1,14 @@
import { Effect } from "effect"
import { pathToFileURL } from "url"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { Npm } from "../../npm"
import { ProviderV2 } from "../../provider"
export const SapAICorePlugin = define({
id: "sap-ai-core",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
const npm = yield* Npm.Service
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("sap-ai-core")) return
const serviceKey =
@ -17,7 +18,7 @@ export const SapAICorePlugin = define({
const installedPath = evt.package.startsWith("file://")
? evt.package
: (yield* ctx.npm.add(evt.package).pipe(Effect.orDie)).entrypoint
: (yield* npm.add(evt.package).pipe(Effect.orDie)).entrypoint
if (!installedPath) throw new Error(`Package ${evt.package} has no import entrypoint`)
const mod = yield* Effect.promise(async () => {
@ -35,8 +36,7 @@ export const SapAICorePlugin = define({
)
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("sap-ai-core")) return
evt.language = evt.sdk(evt.model.api.id)

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
type FetchLike = (url: string | URL | Request, init?: RequestInit) => Promise<Response>
@ -67,8 +67,7 @@ export function cortexFetch(upstream: FetchLike = fetch) {
export const SnowflakeCortexPlugin = define({
id: "snowflake-cortex",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("snowflake-cortex")) return
const token =

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const TogetherAIPlugin = define({
id: "togetherai",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/togetherai") return
const mod = yield* Effect.promise(() => import("@ai-sdk/togetherai"))

View file

@ -1,11 +1,10 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const VenicePlugin = define({
id: "venice",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "venice-ai-sdk-provider") return
const mod = yield* Effect.promise(() => import("venice-ai-sdk-provider"))

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const VercelPlugin = define({
id: "vercel",
@ -16,8 +16,7 @@ export const VercelPlugin = define({
}
}),
)
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/vercel") return
const mod = yield* Effect.promise(() => import("@ai-sdk/vercel"))

View file

@ -1,20 +1,18 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
export const XAIPlugin = define({
id: "xai",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.hook(
"sdk",
yield* ctx.aisdk.sdk(
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/xai") return
const mod = yield* Effect.promise(() => import("@ai-sdk/xai"))
evt.sdk = mod.createXai(evt.options)
}),
)
yield* ctx.aisdk.hook(
"language",
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("xai")) return
evt.language = evt.sdk.responses(evt.model.api.id)

View file

@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define } from "@opencode-ai/plugin/v2/effect"
import { define } from "../internal"
export const ZenmuxPlugin = define({
id: "zenmux",