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

@ -0,0 +1,17 @@
import type { AgentApi } from "@opencode-ai/client/promise/api"
import type { Agent } from "@opencode-ai/schema/agent"
import type { Transform } from "./registration.js"
import type { DeepMutable } from "./types.js"
export interface AgentDraft {
list(): readonly DeepMutable<Agent.Info>[]
get(id: string): DeepMutable<Agent.Info> | undefined
default(id: string | undefined): void
update(id: string, update: (agent: DeepMutable<Agent.Info>) => void): void
remove(id: string): void
}
export interface AgentDomain extends AgentApi {
readonly transform: Transform<AgentDraft>
readonly reload: () => Promise<void>
}

View file

@ -0,0 +1,22 @@
import type { LanguageModelV3 } from "@ai-sdk/provider"
import type { Model } from "@opencode-ai/schema/model"
import type { Hooks } from "./registration.js"
export interface AISDKHooks {
sdk: {
readonly model: Model.Info
readonly package: string
readonly options: Record<string, any>
sdk?: any
}
language: {
readonly model: Model.Info
readonly sdk: any
readonly options: Record<string, any>
language?: LanguageModelV3
}
}
export interface AISDKDomain {
readonly hook: Hooks<AISDKHooks>
}

View file

@ -0,0 +1,33 @@
import type { CatalogApi } from "@opencode-ai/client/promise/api"
import type { Model } from "@opencode-ai/schema/model"
import type { Provider } from "@opencode-ai/schema/provider"
import type { Transform } from "./registration.js"
import type { DeepMutable } from "./types.js"
export interface CatalogProviderRecord {
readonly provider: DeepMutable<Provider.Info>
readonly models: ReadonlyMap<string, DeepMutable<Model.Info>>
}
export interface CatalogDraft {
readonly provider: {
list(): readonly CatalogProviderRecord[]
get(providerID: string): CatalogProviderRecord | undefined
update(providerID: string, update: (provider: DeepMutable<Provider.Info>) => void): void
remove(providerID: string): void
}
readonly model: {
get(providerID: string, modelID: string): DeepMutable<Model.Info> | undefined
update(providerID: string, modelID: string, update: (model: DeepMutable<Model.Info>) => void): void
remove(providerID: string, modelID: string): void
readonly default: {
get(): { providerID: string; modelID: string } | undefined
set(providerID: string, modelID: string): void
}
}
}
export interface CatalogDomain extends CatalogApi {
readonly transform: Transform<CatalogDraft>
readonly reload: () => Promise<void>
}

View file

@ -0,0 +1,15 @@
import type { CommandApi } from "@opencode-ai/client/promise/api"
import type { CommandInfo } from "@opencode-ai/client"
import type { Transform } from "./registration.js"
export interface CommandDraft {
list(): readonly CommandInfo[]
get(name: string): CommandInfo | undefined
update(name: string, update: (command: CommandInfo) => void): void
remove(name: string): void
}
export interface CommandDomain extends CommandApi {
readonly transform: Transform<CommandDraft>
readonly reload: () => Promise<void>
}

View file

@ -0,0 +1,3 @@
import type { EventApi } from "@opencode-ai/client/promise/api"
export interface EventDomain extends Pick<EventApi, "subscribe"> {}

View file

@ -0,0 +1,13 @@
export type { PluginOptions } from "../options.js"
export * as Plugin from "./plugin.js"
export { Agent } from "@opencode-ai/schema/agent"
export { Command } from "@opencode-ai/schema/command"
export { Connection } from "@opencode-ai/schema/connection"
export { Credential } from "@opencode-ai/schema/credential"
export { Integration } from "@opencode-ai/schema/integration"
export { Model } from "@opencode-ai/schema/model"
export { Provider } from "@opencode-ai/schema/provider"
export { Reference } from "@opencode-ai/schema/reference"
export { Skill } from "@opencode-ai/schema/skill"
export { WebSearch } from "@opencode-ai/schema/websearch"

View file

@ -0,0 +1,64 @@
import type {
ConnectionInfo,
IntegrationCommandMethod,
IntegrationEnvMethod,
IntegrationKeyMethod,
IntegrationMethod,
IntegrationOAuthMethod,
} from "@opencode-ai/client"
import type { IntegrationApi } from "@opencode-ai/client/promise/api"
import { Credential } from "@opencode-ai/schema/credential"
import type { Transform } from "./registration.js"
type IntegrationInputs = Record<string, string>
type IntegrationRef = { id: string; name: string }
export type IntegrationOAuthAuthorization = {
readonly url: string
readonly instructions: string
readonly expiresAt?: number
} & (
| {
readonly mode: "auto"
readonly callback: Promise<Credential.OAuth>
}
| {
readonly mode: "code"
readonly callback: (code: string) => Promise<Credential.OAuth>
}
)
export type IntegrationOAuthMethodRegistration = {
readonly integrationID: string
readonly method: IntegrationOAuthMethod
readonly authorize: (inputs: IntegrationInputs) => Promise<IntegrationOAuthAuthorization>
readonly refresh?: (credential: Credential.OAuth) => Promise<Credential.OAuth>
readonly label?: (credential: Credential.OAuth) => string | undefined
}
export type IntegrationMethodRegistration =
| IntegrationOAuthMethodRegistration
| { readonly integrationID: string; readonly method: IntegrationCommandMethod }
| { readonly integrationID: string; readonly method: IntegrationKeyMethod }
| { readonly integrationID: string; readonly method: IntegrationEnvMethod }
export interface IntegrationDraft {
list(): readonly IntegrationRef[]
get(id: string): IntegrationRef | undefined
update(id: string, update: (integration: IntegrationRef) => void): void
remove(id: string): void
readonly method: {
list(integrationID: string): readonly IntegrationMethod[]
update(input: IntegrationMethodRegistration): void
remove(integrationID: string, method: IntegrationMethod): void
}
}
export interface IntegrationDomain extends Omit<IntegrationApi, "wellknown"> {
readonly transform: Transform<IntegrationDraft>
readonly reload: () => Promise<void>
readonly connection: {
readonly active: (integrationID: string) => Promise<ConnectionInfo | undefined>
readonly resolve: (connection: ConnectionInfo) => Promise<Credential.Value | undefined>
}
}

View file

@ -0,0 +1,42 @@
import type { PluginApi } from "@opencode-ai/client/promise/api"
import type { PluginOptions } from "../options.js"
import type { App } from "../app.js"
import type { AgentDomain } from "./agent.js"
import type { AISDKDomain } from "./aisdk.js"
import type { CatalogDomain } from "./catalog.js"
import type { CommandDomain } from "./command.js"
import type { EventDomain } from "./event.js"
import type { IntegrationDomain } from "./integration.js"
import type { ReferenceDomain } from "./reference.js"
import type { SessionDomain } from "./session.js"
import type { SkillDomain } from "./skill.js"
import type { ToolDomain } from "./tool.js"
import type { WebSearchDomain } from "./websearch.js"
export interface Context {
readonly app: App
readonly options: PluginOptions
readonly agent: AgentDomain
readonly aisdk: AISDKDomain
readonly catalog: CatalogDomain
readonly command: CommandDomain
readonly event: EventDomain
readonly integration: IntegrationDomain
readonly plugin: PluginApi
readonly reference: ReferenceDomain
readonly session: SessionDomain
readonly skill: SkillDomain
readonly tool: ToolDomain
readonly websearch: WebSearchDomain
}
export type Cleanup = () => Promise<void> | void
export interface Plugin {
readonly id: string
readonly setup: (context: Context) => Promise<Cleanup | void> | Cleanup | void
}
export function define(plugin: Plugin) {
return plugin
}

View file

@ -0,0 +1,14 @@
import type { ReferenceApi } from "@opencode-ai/client/promise/api"
import type { ReferenceGitSource, ReferenceLocalSource } from "@opencode-ai/client"
import type { Transform } from "./registration.js"
export interface ReferenceDraft {
add(name: string, source: ReferenceLocalSource | ReferenceGitSource): void
remove(name: string): void
list(): readonly (readonly [string, ReferenceLocalSource | ReferenceGitSource])[]
}
export interface ReferenceDomain extends ReferenceApi {
readonly transform: Transform<ReferenceDraft>
readonly reload: () => Promise<void>
}

View file

@ -0,0 +1,10 @@
export interface Registration {
readonly dispose: () => Promise<void>
}
export type Hooks<Spec> = <Name extends keyof Spec>(
name: Name,
callback: (input: Spec[Name]) => Promise<void> | void,
) => Promise<Registration>
export type Transform<Input> = (callback: (input: Input) => void) => Promise<Registration>

View file

@ -0,0 +1,27 @@
import type { SessionApi } from "@opencode-ai/client/promise/api"
import type { Message, SystemPart } from "@opencode-ai/ai"
import type { Agent } from "@opencode-ai/schema/agent"
import type { Model } from "@opencode-ai/schema/model"
import type { Session } from "@opencode-ai/schema/session"
import type { JsonSchema } from "effect"
import type { Hooks } from "./registration.js"
export interface SessionContext {
readonly sessionID: Session.ID
readonly agent: Agent.ID
readonly model: Model.Ref
system: Array<SystemPart>
messages: Array<Message>
tools: Record<string, { description: string; input: JsonSchema.JsonSchema }>
}
export interface SessionHooks {
readonly context: SessionContext
}
export type SessionDomain = Pick<
SessionApi,
"create" | "get" | "prompt" | "generate" | "command" | "synthetic" | "interrupt"
> & {
readonly hook: Hooks<SessionHooks>
}

View file

@ -0,0 +1,13 @@
import type { SkillApi } from "@opencode-ai/client/promise/api"
import type { Skill } from "@opencode-ai/schema/skill"
import type { Transform } from "./registration.js"
export interface SkillDraft {
source(source: Skill.Source): void
list(): readonly Skill.Source[]
}
export interface SkillDomain extends SkillApi {
readonly transform: Transform<SkillDraft>
readonly reload: () => Promise<void>
}

View file

@ -0,0 +1,62 @@
export { CallID, Error } from "@opencode-ai/schema/tool"
export type { Metadata, Options, Result } from "@opencode-ai/schema/tool"
import { Tool } from "@opencode-ai/schema/tool"
import type { Agent } from "@opencode-ai/schema/agent"
import type { Session } from "@opencode-ai/schema/session"
import type { SessionMessage } from "@opencode-ai/schema/session-message"
import type { Hooks, Transform } from "./registration.js"
export interface ToolContext extends Omit<Tool.Context, "progress"> {
readonly progress: (update: Tool.Metadata) => Promise<void>
}
export type Info<
Input extends Tool.ValueSchema<any> = Tool.ValueSchema<any>,
Output extends Tool.ValueSchema<any> | undefined = Tool.ValueSchema<any> | undefined,
> = Omit<Tool.Info<Input, Output>, "execute"> & {
readonly execute: (
input: Parameters<Tool.Info<Input, Output>["execute"]>[0],
context: ToolContext,
) => Promise<Tool.Result<Output>>
}
interface ToolDraft {
add<
Input extends Tool.ValueSchema<any>,
Output extends Tool.ValueSchema<any> | undefined,
>(tool: Info<Input, Output>): void
}
interface ToolHooks {
readonly "execute.before": {
readonly tool: string
readonly sessionID: Session.ID
readonly agent: Agent.ID
readonly messageID: SessionMessage.ID
readonly callID: Tool.CallID
input: unknown
}
readonly "execute.after": {
readonly tool: string
readonly sessionID: Session.ID
readonly agent: Agent.ID
readonly messageID: SessionMessage.ID
readonly callID: Tool.CallID
readonly input: unknown
} & (
| {
readonly status: "completed"
result: Tool.Result
}
| {
readonly status: "error"
error: Tool.Error
}
)
}
export interface ToolDomain {
readonly transform: Transform<ToolDraft>
readonly hook: Hooks<ToolHooks>
}

View file

@ -0,0 +1,9 @@
export type DeepMutable<A> = A extends (...args: never[]) => unknown
? A
: A extends ReadonlyMap<infer K, infer V>
? Map<DeepMutable<K>, DeepMutable<V>>
: A extends ReadonlyArray<infer I>
? DeepMutable<I>[]
: A extends object
? { -readonly [K in keyof A]: DeepMutable<A[K]> }
: A

View file

@ -0,0 +1,25 @@
import type { WebSearch } from "@opencode-ai/schema/websearch"
import type { WebSearchApi } from "@opencode-ai/client/promise/api"
import type { Transform } from "./registration.js"
export interface WebSearchDefinition {
readonly id: string
readonly name: string
readonly execute: (
input: WebSearch.ProviderInput,
context: { readonly signal: AbortSignal },
) => Promise<readonly WebSearch.Result[]>
}
export interface WebSearchDomain extends WebSearchApi {
readonly transform: Transform<WebSearchDraft>
readonly reload: () => Promise<void>
}
export interface WebSearchDraft {
add(definition: WebSearchDefinition): void
readonly default: {
get(): string | undefined
set(providerID: string): void
}
}