Merge remote-tracking branch 'origin/v2' into search-integration
# Conflicts: # packages/client/src/promise/generated/client.ts # packages/client/src/promise/generated/types.ts # packages/client/test/promise.test.ts # packages/core/src/plugin/host.ts # packages/core/src/plugin/internal.ts # packages/core/src/plugin/promise.ts # packages/core/test/plugin/host.ts # packages/plugin/src/v2/effect/index.ts # packages/plugin/src/v2/effect/integration.ts # packages/plugin/src/v2/promise/index.ts # packages/plugin/src/v2/promise/integration.ts # packages/protocol/src/client.ts # packages/schema/src/index.ts # packages/sdk/js/src/v2/gen/sdk.gen.ts
This commit is contained in:
commit
a6acc2397d
374 changed files with 15254 additions and 10009 deletions
|
|
@ -475,6 +475,7 @@ export type TuiHostSlotMap = {
|
|||
}
|
||||
sidebar_footer: {
|
||||
session_id: string
|
||||
directory: string
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ Both use the same low-level scoped registration registry, but consumers invoke t
|
|||
|
||||
```ts
|
||||
ctx.tool.transform(...) // replayed to build effective tool registry state
|
||||
ctx.tool.hook(...) // invoked at a live tool operation boundary
|
||||
ctx.tool.hook(...) // invoked at a live tool operation boundary
|
||||
```
|
||||
|
||||
The shared low-level machinery owns registration order, scope cleanup, disposal, and snapshots. Each domain owns when its transforms or runtime hooks execute.
|
||||
|
|
|
|||
|
|
@ -5,15 +5,13 @@ The Effect plugin API grants plugins two in-process capabilities:
|
|||
- `hook` installs behavior at an OpenCode extension point.
|
||||
- `reload` reruns every transform hook for a stateful domain.
|
||||
|
||||
The public server client will be exposed separately. It is intentionally not part of `PluginContext` yet.
|
||||
|
||||
## Defining A Plugin
|
||||
|
||||
```ts
|
||||
import { define } from "@opencode-ai/plugin/v2/effect"
|
||||
import { Plugin } from "@opencode-ai/plugin/v2/effect"
|
||||
import { Effect } from "effect"
|
||||
|
||||
export const Plugin = define({
|
||||
export default Plugin.define({
|
||||
id: "example",
|
||||
effect: Effect.fn(function* (ctx) {
|
||||
yield* ctx.catalog.transform((catalog) => {
|
||||
|
|
@ -25,7 +23,7 @@ export const Plugin = define({
|
|||
})
|
||||
```
|
||||
|
||||
Plugin setup registers hooks imperatively. It does not return a hook object.
|
||||
Plugin setup registers hooks imperatively through each domain's `hook` method.
|
||||
|
||||
Configuration supplied for the plugin is available as `ctx.options`.
|
||||
|
||||
|
|
@ -64,7 +62,8 @@ Runtime hooks intercept live operations rather than rebuilding domain state:
|
|||
|
||||
```ts
|
||||
yield *
|
||||
ctx.aisdk.sdk(
|
||||
ctx.aisdk.hook(
|
||||
"sdk",
|
||||
Effect.fn(function* (event) {
|
||||
if (event.package !== "@ai-sdk/xai") return
|
||||
const mod = yield* Effect.promise(() => import("@ai-sdk/xai"))
|
||||
|
|
@ -73,7 +72,7 @@ yield *
|
|||
)
|
||||
|
||||
yield *
|
||||
ctx.aisdk.language((event) => {
|
||||
ctx.aisdk.hook("language", (event) => {
|
||||
if (event.model.providerID !== "xai") return
|
||||
event.language = event.sdk.responses(event.model.api.id)
|
||||
})
|
||||
|
|
@ -81,6 +80,16 @@ yield *
|
|||
|
||||
Hooks run sequentially in registration order. Later hooks observe mutations made by earlier hooks.
|
||||
|
||||
Session request context is mutable immediately before provider dispatch:
|
||||
|
||||
```ts
|
||||
yield *
|
||||
ctx.session.hook("request", (event) => {
|
||||
event.tools.read.description = "Read a file using narrow line ranges."
|
||||
delete event.tools.write
|
||||
})
|
||||
```
|
||||
|
||||
## Reloading A Domain
|
||||
|
||||
When data captured by a transform changes, reload the affected domain:
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import type { AgentApi } from "@opencode-ai/client/effect/api"
|
||||
import type { AgentV2Info } from "@opencode-ai/sdk/v2/types"
|
||||
import type { AgentInfo } from "@opencode-ai/sdk/v2/types"
|
||||
import type { Effect } from "effect"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export interface AgentDraft {
|
||||
list(): readonly AgentV2Info[]
|
||||
get(id: string): AgentV2Info | undefined
|
||||
list(): readonly AgentInfo[]
|
||||
get(id: string): AgentInfo | undefined
|
||||
default(id: string | undefined): void
|
||||
update(id: string, update: (agent: AgentV2Info) => void): void
|
||||
update(id: string, update: (agent: AgentInfo) => void): void
|
||||
remove(id: string): void
|
||||
}
|
||||
|
||||
export interface AgentHooks extends AgentApi<unknown> {
|
||||
readonly transform: TransformHook<AgentDraft>
|
||||
export interface AgentDomain extends AgentApi<unknown> {
|
||||
readonly transform: Transform<AgentDraft>
|
||||
readonly reload: () => Effect.Effect<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { LanguageModelV3 } from "@ai-sdk/provider"
|
|||
import type { Model } from "@opencode-ai/schema/model"
|
||||
import type { Hooks } from "./registration.js"
|
||||
|
||||
export type AISDKHooks = Hooks<{
|
||||
export interface AISDKHooks {
|
||||
sdk: {
|
||||
readonly model: Model.Info
|
||||
readonly package: string
|
||||
|
|
@ -15,4 +15,8 @@ export type AISDKHooks = Hooks<{
|
|||
readonly options: Record<string, any>
|
||||
language?: LanguageModelV3
|
||||
}
|
||||
}>
|
||||
}
|
||||
|
||||
export interface AISDKDomain {
|
||||
readonly hook: Hooks<AISDKHooks>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { ModelV2Info, ProviderV2Info } from "@opencode-ai/sdk/v2/types"
|
||||
import type { ModelInfo, ProviderV2Info } from "@opencode-ai/sdk/v2/types"
|
||||
import type { CatalogApi } from "@opencode-ai/client/effect/api"
|
||||
import type { Effect } from "effect"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export interface CatalogProviderRecord {
|
||||
readonly provider: ProviderV2Info
|
||||
readonly models: ReadonlyMap<string, ModelV2Info>
|
||||
readonly models: ReadonlyMap<string, ModelInfo>
|
||||
}
|
||||
|
||||
export interface CatalogDraft {
|
||||
|
|
@ -16,8 +16,8 @@ export interface CatalogDraft {
|
|||
remove(providerID: string): void
|
||||
}
|
||||
readonly model: {
|
||||
get(providerID: string, modelID: string): ModelV2Info | undefined
|
||||
update(providerID: string, modelID: string, update: (model: ModelV2Info) => void): void
|
||||
get(providerID: string, modelID: string): ModelInfo | undefined
|
||||
update(providerID: string, modelID: string, update: (model: ModelInfo) => void): void
|
||||
remove(providerID: string, modelID: string): void
|
||||
readonly default: {
|
||||
get(): { providerID: string; modelID: string } | undefined
|
||||
|
|
@ -26,7 +26,7 @@ export interface CatalogDraft {
|
|||
}
|
||||
}
|
||||
|
||||
export interface CatalogHooks extends CatalogApi<unknown> {
|
||||
readonly transform: TransformHook<CatalogDraft>
|
||||
export interface CatalogDomain extends CatalogApi<unknown> {
|
||||
readonly transform: Transform<CatalogDraft>
|
||||
readonly reload: () => Effect.Effect<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import type { CommandV2Info } from "@opencode-ai/sdk/v2/types"
|
||||
import type { CommandInfo } from "@opencode-ai/sdk/v2/types"
|
||||
import type { CommandApi } from "@opencode-ai/client/effect/api"
|
||||
import type { Effect } from "effect"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export interface CommandDraft {
|
||||
list(): readonly CommandV2Info[]
|
||||
get(name: string): CommandV2Info | undefined
|
||||
update(name: string, update: (command: CommandV2Info) => void): void
|
||||
list(): readonly CommandInfo[]
|
||||
get(name: string): CommandInfo | undefined
|
||||
update(name: string, update: (command: CommandInfo) => void): void
|
||||
remove(name: string): void
|
||||
}
|
||||
|
||||
export interface CommandHooks extends CommandApi<unknown> {
|
||||
readonly transform: TransformHook<CommandDraft>
|
||||
export interface CommandDomain extends CommandApi<unknown> {
|
||||
readonly transform: Transform<CommandDraft>
|
||||
readonly reload: () => Effect.Effect<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
import type { PluginOptions } from "../options.js"
|
||||
import type { AgentHooks } from "./agent.js"
|
||||
import type { AISDKHooks } from "./aisdk.js"
|
||||
import type { CatalogHooks } from "./catalog.js"
|
||||
import type { CommandHooks } from "./command.js"
|
||||
import type { EventHooks } from "./event.js"
|
||||
import type { IntegrationHooks } from "./integration.js"
|
||||
import type { PluginDomain } from "./plugin.js"
|
||||
import type { ReferenceHooks } from "./reference.js"
|
||||
import type { SkillHooks } from "./skill.js"
|
||||
import type { ToolDomain } from "./tool.js"
|
||||
import type { SessionHooks } from "./runtime.js"
|
||||
|
||||
export interface PluginContext {
|
||||
readonly options: PluginOptions
|
||||
readonly agent: AgentHooks
|
||||
readonly aisdk: AISDKHooks
|
||||
readonly catalog: CatalogHooks
|
||||
readonly command: CommandHooks
|
||||
readonly event: EventHooks
|
||||
readonly integration: IntegrationHooks
|
||||
readonly plugin: PluginDomain
|
||||
readonly reference: ReferenceHooks
|
||||
readonly skill: SkillHooks
|
||||
readonly tool: ToolDomain
|
||||
readonly session: SessionHooks
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import type { EventApi } from "@opencode-ai/client/effect/api"
|
||||
|
||||
export interface EventHooks extends Pick<EventApi<unknown>, "subscribe"> {}
|
||||
export interface EventDomain extends Pick<EventApi<unknown>, "subscribe"> {}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,4 @@
|
|||
export type { PluginContext } from "./context.js"
|
||||
export { define } from "./plugin.js"
|
||||
export type { Plugin, PluginDomain } from "./plugin.js"
|
||||
export type { AgentDraft, AgentHooks } from "./agent.js"
|
||||
export type { AISDKHooks } from "./aisdk.js"
|
||||
export type { CatalogDraft, CatalogHooks, CatalogProviderRecord } from "./catalog.js"
|
||||
export type { CommandDraft, CommandHooks } from "./command.js"
|
||||
export type { EventHooks } from "./event.js"
|
||||
export type {
|
||||
IntegrationDraft,
|
||||
IntegrationDefinition,
|
||||
IntegrationHooks,
|
||||
IntegrationMethodDefinition,
|
||||
IntegrationMethodRegistration,
|
||||
IntegrationOAuthMethodDefinition,
|
||||
IntegrationSearchDefinition,
|
||||
} from "./integration.js"
|
||||
export type { ReferenceDraft, ReferenceHooks } from "./reference.js"
|
||||
export type { SkillDraft, SkillHooks } from "./skill.js"
|
||||
export * as Tool from "./tool.js"
|
||||
export type { ToolDomain, ToolDraft, ToolExecuteBeforeEvent, ToolExecuteAfterEvent } from "./tool.js"
|
||||
export type { SessionHooks } from "./runtime.js"
|
||||
export * as Plugin from "./plugin.js"
|
||||
|
||||
export { Agent } from "@opencode-ai/schema/agent"
|
||||
export { Command } from "@opencode-ai/schema/command"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import type {
|
|||
import type { IntegrationApi } from "@opencode-ai/client/effect/api"
|
||||
import type { Search } from "@opencode-ai/schema/search"
|
||||
import type { Effect, Scope } from "effect"
|
||||
import type { Registration, TransformHook } from "./registration.js"
|
||||
import type { Registration, Transform } from "./registration.js"
|
||||
|
||||
export type IntegrationOAuthAuthorization = {
|
||||
readonly url: string
|
||||
|
|
@ -80,9 +80,9 @@ export interface IntegrationDraft {
|
|||
}
|
||||
}
|
||||
|
||||
export interface IntegrationHooks extends IntegrationApi<unknown> {
|
||||
export interface IntegrationDomain extends IntegrationApi<unknown> {
|
||||
readonly register: (definition: IntegrationDefinition) => Effect.Effect<Registration, never, Scope.Scope>
|
||||
readonly transform: TransformHook<IntegrationDraft>
|
||||
readonly transform: Transform<IntegrationDraft>
|
||||
readonly reload: () => Effect.Effect<void>
|
||||
readonly connection: {
|
||||
readonly active: (integrationID: string) => Effect.Effect<ConnectionInfo | undefined>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,37 @@
|
|||
import type { PluginApi } from "@opencode-ai/client/effect/api"
|
||||
import type { Effect, Scope } from "effect"
|
||||
import type { PluginContext } from "./context.js"
|
||||
import type { PluginOptions } from "../options.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"
|
||||
|
||||
export interface Context {
|
||||
readonly options: PluginOptions
|
||||
readonly agent: AgentDomain
|
||||
readonly aisdk: AISDKDomain
|
||||
readonly catalog: CatalogDomain
|
||||
readonly command: CommandDomain
|
||||
readonly event: EventDomain
|
||||
readonly integration: IntegrationDomain
|
||||
readonly plugin: PluginApi<unknown>
|
||||
readonly reference: ReferenceDomain
|
||||
readonly session: SessionDomain
|
||||
readonly skill: SkillDomain
|
||||
readonly tool: ToolDomain
|
||||
}
|
||||
|
||||
export interface Plugin<R = Scope.Scope> {
|
||||
readonly id: string
|
||||
readonly effect: (context: PluginContext) => Effect.Effect<void, never, R>
|
||||
readonly effect: (context: Context) => Effect.Effect<void, never, R>
|
||||
}
|
||||
|
||||
export function define<R = Scope.Scope>(plugin: Plugin<R>) {
|
||||
return plugin
|
||||
}
|
||||
|
||||
export interface PluginDomain extends PluginApi<unknown> {}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { ReferenceGitSource, ReferenceLocalSource } from "@opencode-ai/sdk/v2/types"
|
||||
import type { ReferenceApi } from "@opencode-ai/client/effect/api"
|
||||
import type { Effect } from "effect"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export interface ReferenceDraft {
|
||||
add(name: string, source: ReferenceLocalSource | ReferenceGitSource): void
|
||||
|
|
@ -9,7 +9,7 @@ export interface ReferenceDraft {
|
|||
list(): readonly (readonly [string, ReferenceLocalSource | ReferenceGitSource])[]
|
||||
}
|
||||
|
||||
export interface ReferenceHooks extends ReferenceApi<unknown> {
|
||||
readonly transform: TransformHook<ReferenceDraft>
|
||||
export interface ReferenceDomain extends ReferenceApi<unknown> {
|
||||
readonly transform: Transform<ReferenceDraft>
|
||||
readonly reload: () => Effect.Effect<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ export interface Registration {
|
|||
readonly dispose: Effect.Effect<void>
|
||||
}
|
||||
|
||||
export type Hooks<Spec> = {
|
||||
readonly [Name in keyof Spec]: (
|
||||
callback: (input: Spec[Name]) => Effect.Effect<void> | void,
|
||||
) => Effect.Effect<Registration, never, Scope.Scope>
|
||||
}
|
||||
export type Hooks<Spec> = <Name extends keyof Spec>(
|
||||
name: Name,
|
||||
callback: (input: Spec[Name]) => Effect.Effect<void>,
|
||||
) => Effect.Effect<Registration, never, Scope.Scope>
|
||||
|
||||
export type TransformHook<Input> = (callback: (input: Input) => void) => Effect.Effect<Registration, never, Scope.Scope>
|
||||
export type Transform<Input> = (callback: (input: Input) => void) => Effect.Effect<Registration, never, Scope.Scope>
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
import type { SessionApi } from "@opencode-ai/client/effect/api"
|
||||
|
||||
export interface SessionHooks
|
||||
extends Pick<SessionApi<unknown>, "create" | "get" | "prompt" | "command" | "interrupt"> {}
|
||||
25
packages/plugin/src/v2/effect/session.ts
Normal file
25
packages/plugin/src/v2/effect/session.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import type { SessionApi } from "@opencode-ai/client/effect/api"
|
||||
import type { Message, SystemPart } from "@opencode-ai/llm"
|
||||
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 SessionRequestBeforeEvent {
|
||||
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 request: SessionRequestBeforeEvent
|
||||
}
|
||||
|
||||
export interface SessionDomain
|
||||
extends Pick<SessionApi<unknown>, "create" | "get" | "prompt" | "command" | "interrupt"> {
|
||||
readonly hook: Hooks<SessionHooks>
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import type { SkillV2Source } from "@opencode-ai/sdk/v2/types"
|
||||
import type { SkillSource } from "@opencode-ai/sdk/v2/types"
|
||||
import type { SkillApi } from "@opencode-ai/client/effect/api"
|
||||
import type { Effect } from "effect"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export interface SkillDraft {
|
||||
source(source: SkillV2Source): void
|
||||
list(): readonly SkillV2Source[]
|
||||
source(source: SkillSource): void
|
||||
list(): readonly SkillSource[]
|
||||
}
|
||||
|
||||
export interface SkillHooks extends SkillApi<unknown> {
|
||||
readonly transform: TransformHook<SkillDraft>
|
||||
export interface SkillDomain extends SkillApi<unknown> {
|
||||
readonly transform: Transform<SkillDraft>
|
||||
readonly reload: () => Effect.Effect<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Agent } from "@opencode-ai/schema/agent"
|
|||
import { Session } from "@opencode-ai/schema/session"
|
||||
import { SessionMessage } from "@opencode-ai/schema/session-message"
|
||||
import { Effect, JsonSchema, Schema, type Scope } from "effect"
|
||||
import type { Hooks } from "./registration.js"
|
||||
import type { Hooks, Transform } from "./registration.js"
|
||||
|
||||
export interface Context {
|
||||
readonly sessionID: Session.ID
|
||||
|
|
@ -253,7 +253,12 @@ export interface ToolDraft {
|
|||
add(name: string, tool: AnyTool, options?: RegisterOptions): void
|
||||
}
|
||||
|
||||
export interface ToolDomain {
|
||||
readonly transform: (callback: (draft: ToolDraft) => void) => Effect.Effect<void, RegistrationError, Scope.Scope>
|
||||
readonly execute: Hooks<{ before: ToolExecuteBeforeEvent; after: ToolExecuteAfterEvent }>
|
||||
export interface ToolHooks {
|
||||
readonly "execute.before": ToolExecuteBeforeEvent
|
||||
readonly "execute.after": ToolExecuteAfterEvent
|
||||
}
|
||||
|
||||
export interface ToolDomain {
|
||||
readonly transform: Transform<ToolDraft>
|
||||
readonly hook: Hooks<ToolHooks>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# OpenCode V2 Promise Plugin API
|
||||
|
||||
The Promise plugin API is the async/await equivalent of `@opencode-ai/plugin/v2/effect`. It grants plugins the same two in-process capabilities:
|
||||
The Promise plugin API at `@opencode-ai/plugin/v2` is the async/await equivalent of `@opencode-ai/plugin/v2/effect`. It grants plugins the same two in-process capabilities:
|
||||
|
||||
- `hook` installs behavior at an OpenCode extension point.
|
||||
- `reload` reruns every transform hook for a stateful domain.
|
||||
|
|
@ -10,9 +10,9 @@ The only difference from the Effect API is the async boundary: hook callbacks, h
|
|||
## Defining A Plugin
|
||||
|
||||
```ts
|
||||
import { define } from "@opencode-ai/plugin/v2/promise"
|
||||
import { Plugin } from "@opencode-ai/plugin/v2"
|
||||
|
||||
export const Plugin = define({
|
||||
export default Plugin.define({
|
||||
id: "example",
|
||||
setup: async (ctx) => {
|
||||
await ctx.catalog.transform((catalog) => {
|
||||
|
|
@ -24,7 +24,7 @@ export const Plugin = define({
|
|||
})
|
||||
```
|
||||
|
||||
Plugin setup registers hooks imperatively. It does not return a hook object.
|
||||
Plugin setup registers hooks imperatively through each domain's `hook` method.
|
||||
|
||||
Configuration supplied for the plugin is available as `ctx.options`.
|
||||
|
||||
|
|
@ -64,18 +64,43 @@ ctx.skill.transform
|
|||
Runtime hooks intercept live operations:
|
||||
|
||||
```ts
|
||||
await ctx.aisdk.sdk(async (event) => {
|
||||
await ctx.aisdk.hook("sdk", async (event) => {
|
||||
if (event.package !== "@ai-sdk/xai") return
|
||||
const mod = await import("@ai-sdk/xai")
|
||||
event.sdk = mod.createXai(event.options)
|
||||
})
|
||||
|
||||
await ctx.aisdk.language((event) => {
|
||||
await ctx.aisdk.hook("language", (event) => {
|
||||
if (event.model.providerID !== "xai") return
|
||||
event.language = event.sdk.responses(event.model.api.id)
|
||||
})
|
||||
```
|
||||
|
||||
Session request context is mutable immediately before provider dispatch:
|
||||
|
||||
```ts
|
||||
await ctx.session.hook("request", (event) => {
|
||||
event.tools.read.description = "Read a file using narrow line ranges."
|
||||
delete event.tools.write
|
||||
})
|
||||
```
|
||||
|
||||
Promise tools use the same schemas and registration model as Effect tools, with async executors:
|
||||
|
||||
```ts
|
||||
import { Schema } from "effect"
|
||||
import { Tool } from "@opencode-ai/plugin/v2/tool"
|
||||
|
||||
const echo = Tool.make({
|
||||
description: "Echo text",
|
||||
input: Schema.Struct({ text: Schema.String }),
|
||||
output: Schema.Struct({ text: Schema.String }),
|
||||
execute: async ({ text }) => ({ text }),
|
||||
})
|
||||
|
||||
await ctx.tool.transform((tools) => tools.add("echo", echo))
|
||||
```
|
||||
|
||||
## Reloading A Domain
|
||||
|
||||
When data captured by a transform changes, reload the affected domain:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { AgentApi } from "@opencode-ai/client/promise/api"
|
||||
import type { AgentDraft } from "../effect/agent.js"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export type { AgentDraft }
|
||||
|
||||
export interface AgentHooks extends AgentApi {
|
||||
readonly transform: TransformHook<AgentDraft>
|
||||
export interface AgentDomain extends AgentApi {
|
||||
readonly transform: Transform<AgentDraft>
|
||||
readonly reload: () => Promise<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { LanguageModelV3 } from "@ai-sdk/provider"
|
|||
import type { Model } from "@opencode-ai/schema/model"
|
||||
import type { Hooks } from "./registration.js"
|
||||
|
||||
export type AISDKHooks = Hooks<{
|
||||
export interface AISDKHooks {
|
||||
sdk: {
|
||||
readonly model: Model.Info
|
||||
readonly package: string
|
||||
|
|
@ -15,4 +15,8 @@ export type AISDKHooks = Hooks<{
|
|||
readonly options: Record<string, any>
|
||||
language?: LanguageModelV3
|
||||
}
|
||||
}>
|
||||
}
|
||||
|
||||
export interface AISDKDomain {
|
||||
readonly hook: Hooks<AISDKHooks>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { CatalogApi } from "@opencode-ai/client/promise/api"
|
||||
import type { CatalogDraft, CatalogProviderRecord } from "../effect/catalog.js"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export type { CatalogDraft, CatalogProviderRecord }
|
||||
|
||||
export interface CatalogHooks extends CatalogApi {
|
||||
readonly transform: TransformHook<CatalogDraft>
|
||||
export interface CatalogDomain extends CatalogApi {
|
||||
readonly transform: Transform<CatalogDraft>
|
||||
readonly reload: () => Promise<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { CommandApi } from "@opencode-ai/client/promise/api"
|
||||
import type { CommandDraft } from "../effect/command.js"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export type { CommandDraft }
|
||||
|
||||
export interface CommandHooks extends CommandApi {
|
||||
readonly transform: TransformHook<CommandDraft>
|
||||
export interface CommandDomain extends CommandApi {
|
||||
readonly transform: Transform<CommandDraft>
|
||||
readonly reload: () => Promise<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
import type { PluginOptions } from "../options.js"
|
||||
import type { AgentHooks } from "./agent.js"
|
||||
import type { AISDKHooks } from "./aisdk.js"
|
||||
import type { CatalogHooks } from "./catalog.js"
|
||||
import type { CommandHooks } from "./command.js"
|
||||
import type { EventHooks } from "./event.js"
|
||||
import type { IntegrationHooks } from "./integration.js"
|
||||
import type { PluginDomain } from "./plugin.js"
|
||||
import type { ReferenceHooks } from "./reference.js"
|
||||
import type { SessionHooks } from "./runtime.js"
|
||||
import type { SkillHooks } from "./skill.js"
|
||||
|
||||
export interface PluginContext {
|
||||
readonly options: PluginOptions
|
||||
readonly agent: AgentHooks
|
||||
readonly aisdk: AISDKHooks
|
||||
readonly catalog: CatalogHooks
|
||||
readonly command: CommandHooks
|
||||
readonly event: EventHooks
|
||||
readonly integration: IntegrationHooks
|
||||
readonly plugin: PluginDomain
|
||||
readonly reference: ReferenceHooks
|
||||
readonly session: SessionHooks
|
||||
readonly skill: SkillHooks
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import type { EventApi } from "@opencode-ai/client/promise/api"
|
||||
|
||||
export interface EventHooks extends Pick<EventApi, "subscribe"> {}
|
||||
export interface EventDomain extends Pick<EventApi, "subscribe"> {}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,5 @@
|
|||
export type { PluginContext } from "./context.js"
|
||||
export type { PluginOptions } from "../options.js"
|
||||
export { define } from "./plugin.js"
|
||||
export type { Plugin, PluginDomain } from "./plugin.js"
|
||||
export type { AgentDraft, AgentHooks } from "./agent.js"
|
||||
export type { AISDKHooks } from "./aisdk.js"
|
||||
export type { CatalogDraft, CatalogHooks, CatalogProviderRecord } from "./catalog.js"
|
||||
export type { CommandDraft, CommandHooks } from "./command.js"
|
||||
export type { EventHooks } from "./event.js"
|
||||
export type {
|
||||
IntegrationDraft,
|
||||
IntegrationDefinition,
|
||||
IntegrationHooks,
|
||||
IntegrationMethodDefinition,
|
||||
IntegrationMethodRegistration,
|
||||
IntegrationOAuthAuthorization,
|
||||
IntegrationOAuthMethodDefinition,
|
||||
IntegrationSearchDefinition,
|
||||
} from "./integration.js"
|
||||
export type { ReferenceDraft, ReferenceHooks } from "./reference.js"
|
||||
export type { SessionHooks } from "./runtime.js"
|
||||
export type { SkillDraft, SkillHooks } from "./skill.js"
|
||||
export * as Plugin from "./plugin.js"
|
||||
|
||||
export { Agent } from "@opencode-ai/schema/agent"
|
||||
export { Command } from "@opencode-ai/schema/command"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { IntegrationApi } from "@opencode-ai/client/promise/api"
|
||||
import type { IntegrationMethodRegistration } from "../effect/integration.js"
|
||||
import type { IntegrationDraft, IntegrationMethodRegistration } from "../effect/integration.js"
|
||||
import type {
|
||||
CredentialOAuth,
|
||||
CredentialValue,
|
||||
|
|
@ -9,9 +9,9 @@ import type {
|
|||
IntegrationOAuthMethod,
|
||||
} from "@opencode-ai/sdk/v2/types"
|
||||
import type { Search } from "@opencode-ai/schema/search"
|
||||
import type { Registration, TransformHook } from "./registration.js"
|
||||
import type { Registration, Transform } from "./registration.js"
|
||||
|
||||
export type { IntegrationMethodRegistration }
|
||||
export type { IntegrationDraft, IntegrationMethodRegistration }
|
||||
|
||||
export type IntegrationOAuthAuthorization = {
|
||||
readonly url: string
|
||||
|
|
@ -50,11 +50,9 @@ export interface IntegrationDefinition {
|
|||
readonly search?: IntegrationSearchDefinition
|
||||
}
|
||||
|
||||
export type IntegrationDraft = import("../effect/integration.js").IntegrationDraft
|
||||
|
||||
export interface IntegrationHooks extends IntegrationApi {
|
||||
export interface IntegrationDomain extends IntegrationApi {
|
||||
readonly register: (definition: IntegrationDefinition) => Promise<Registration>
|
||||
readonly transform: TransformHook<IntegrationDraft>
|
||||
readonly transform: Transform<IntegrationDraft>
|
||||
readonly reload: () => Promise<void>
|
||||
readonly connection: {
|
||||
readonly active: (integrationID: string) => Promise<import("@opencode-ai/sdk/v2/types").ConnectionInfo | undefined>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,36 @@
|
|||
import type { PluginApi } from "@opencode-ai/client/promise/api"
|
||||
import type { PluginContext } from "./context.js"
|
||||
import type { PluginOptions } from "../options.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"
|
||||
|
||||
export interface Context {
|
||||
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
|
||||
}
|
||||
|
||||
export interface Plugin {
|
||||
readonly id: string
|
||||
readonly setup: (context: PluginContext) => Promise<void> | void
|
||||
readonly setup: (context: Context) => Promise<void> | void
|
||||
}
|
||||
|
||||
export function define(plugin: Plugin) {
|
||||
return plugin
|
||||
}
|
||||
|
||||
export interface PluginDomain extends PluginApi {}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { ReferenceApi } from "@opencode-ai/client/promise/api"
|
||||
import type { ReferenceDraft } from "../effect/reference.js"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export type { ReferenceDraft }
|
||||
|
||||
export interface ReferenceHooks extends ReferenceApi {
|
||||
readonly transform: TransformHook<ReferenceDraft>
|
||||
export interface ReferenceDomain extends ReferenceApi {
|
||||
readonly transform: Transform<ReferenceDraft>
|
||||
readonly reload: () => Promise<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ export interface Registration {
|
|||
readonly dispose: () => Promise<void>
|
||||
}
|
||||
|
||||
export type Hooks<Spec> = {
|
||||
readonly [Name in keyof Spec]: (callback: (input: Spec[Name]) => Promise<void> | void) => Promise<Registration>
|
||||
}
|
||||
export type Hooks<Spec> = <Name extends keyof Spec>(
|
||||
name: Name,
|
||||
callback: (input: Spec[Name]) => Promise<void> | void,
|
||||
) => Promise<Registration>
|
||||
|
||||
export type TransformHook<Input> = (callback: (input: Input) => void) => Promise<Registration>
|
||||
export type Transform<Input> = (callback: (input: Input) => void) => Promise<Registration>
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
import type { SessionApi } from "@opencode-ai/client/promise/api"
|
||||
|
||||
export interface SessionHooks extends Pick<SessionApi, "create" | "get" | "prompt" | "command" | "interrupt"> {}
|
||||
24
packages/plugin/src/v2/promise/session.ts
Normal file
24
packages/plugin/src/v2/promise/session.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import type { SessionApi } from "@opencode-ai/client/promise/api"
|
||||
import type { Message, SystemPart } from "@opencode-ai/llm"
|
||||
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 SessionRequestBeforeEvent {
|
||||
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 request: SessionRequestBeforeEvent
|
||||
}
|
||||
|
||||
export interface SessionDomain extends Pick<SessionApi, "create" | "get" | "prompt" | "command" | "interrupt"> {
|
||||
readonly hook: Hooks<SessionHooks>
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import type { SkillApi } from "@opencode-ai/client/promise/api"
|
||||
import type { SkillDraft } from "../effect/skill.js"
|
||||
import type { TransformHook } from "./registration.js"
|
||||
import type { Transform } from "./registration.js"
|
||||
|
||||
export type { SkillDraft }
|
||||
|
||||
export interface SkillHooks extends SkillApi {
|
||||
readonly transform: TransformHook<SkillDraft>
|
||||
export interface SkillDomain extends SkillApi {
|
||||
readonly transform: Transform<SkillDraft>
|
||||
readonly reload: () => Promise<void>
|
||||
}
|
||||
|
|
|
|||
110
packages/plugin/src/v2/promise/tool.ts
Normal file
110
packages/plugin/src/v2/promise/tool.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
export * as Tool from "./tool.js"
|
||||
|
||||
import { Tool } from "../effect/tool.js"
|
||||
import type { ToolOutput, ToolResultValue } from "@opencode-ai/llm"
|
||||
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 { Effect, type JsonSchema, type Schema } from "effect"
|
||||
import type { Hooks, Transform } from "./registration.js"
|
||||
|
||||
export type Context = Tool.Context
|
||||
export type SchemaType<A> = Tool.SchemaType<A>
|
||||
export type Definition<Input extends SchemaType<any>, Output extends SchemaType<any>> = Tool.Definition<Input, Output>
|
||||
export type AnyTool = Tool.AnyTool
|
||||
export const Failure = Tool.Failure
|
||||
export type Failure = Tool.Failure
|
||||
export const RegistrationError = Tool.RegistrationError
|
||||
export type RegistrationError = Tool.RegistrationError
|
||||
export type Content = Tool.Content
|
||||
export type DynamicOutput = Tool.DynamicOutput
|
||||
|
||||
type Config<
|
||||
Input extends SchemaType<any>,
|
||||
Output extends SchemaType<any>,
|
||||
Structured extends SchemaType<any> = Output,
|
||||
> = {
|
||||
readonly description: string
|
||||
readonly input: Input
|
||||
readonly output: Output
|
||||
readonly structured?: Structured
|
||||
readonly toStructuredOutput?: (input: {
|
||||
readonly input: Schema.Schema.Type<Input>
|
||||
readonly output: Output["Encoded"]
|
||||
}) => Schema.Schema.Type<Structured>
|
||||
readonly execute: (
|
||||
input: Schema.Schema.Type<Input>,
|
||||
context: Context,
|
||||
) => Promise<Schema.Schema.Type<Output>>
|
||||
readonly toModelOutput?: (input: {
|
||||
readonly input: Schema.Schema.Type<Input>
|
||||
readonly output: Output["Encoded"]
|
||||
}) => ReadonlyArray<Content>
|
||||
}
|
||||
|
||||
type DynamicConfig = {
|
||||
readonly description: string
|
||||
readonly jsonSchema: JsonSchema.JsonSchema
|
||||
readonly outputSchema?: JsonSchema.JsonSchema
|
||||
readonly execute: (input: unknown, context: Context) => Promise<DynamicOutput>
|
||||
}
|
||||
|
||||
export function make<
|
||||
Input extends SchemaType<any>,
|
||||
Output extends SchemaType<any>,
|
||||
Structured extends SchemaType<any> = Output,
|
||||
>(config: Config<Input, Output, Structured>): Definition<Input, Structured>
|
||||
export function make(config: DynamicConfig): AnyTool
|
||||
export function make(config: Config<any, any, any> | DynamicConfig): AnyTool {
|
||||
if ("jsonSchema" in config)
|
||||
return Tool.make({
|
||||
...config,
|
||||
execute: (input, context) => Effect.promise(() => config.execute(input, context)),
|
||||
})
|
||||
return Tool.make({
|
||||
...config,
|
||||
execute: (input, context) => Effect.promise(() => config.execute(input, context)),
|
||||
})
|
||||
}
|
||||
|
||||
export const withPermission = Tool.withPermission
|
||||
|
||||
export interface ToolExecuteBeforeEvent {
|
||||
readonly tool: string
|
||||
readonly sessionID: Session.ID
|
||||
readonly agent: Agent.ID
|
||||
readonly assistantMessageID: SessionMessage.ID
|
||||
readonly toolCallID: string
|
||||
input: unknown
|
||||
}
|
||||
|
||||
export interface ToolExecuteAfterEvent {
|
||||
readonly tool: string
|
||||
readonly sessionID: Session.ID
|
||||
readonly agent: Agent.ID
|
||||
readonly assistantMessageID: SessionMessage.ID
|
||||
readonly toolCallID: string
|
||||
readonly input: unknown
|
||||
result: ToolResultValue
|
||||
output?: ToolOutput
|
||||
outputPaths?: ReadonlyArray<string>
|
||||
}
|
||||
|
||||
export interface RegisterOptions {
|
||||
readonly group?: string
|
||||
readonly deferred?: boolean
|
||||
}
|
||||
|
||||
export interface ToolDraft {
|
||||
add(name: string, tool: AnyTool, options?: RegisterOptions): void
|
||||
}
|
||||
|
||||
export interface ToolHooks {
|
||||
readonly "execute.before": ToolExecuteBeforeEvent
|
||||
readonly "execute.after": ToolExecuteAfterEvent
|
||||
}
|
||||
|
||||
export interface ToolDomain {
|
||||
readonly transform: Transform<ToolDraft>
|
||||
readonly hook: Hooks<ToolHooks>
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue