refactor(plugin): scope context hook to session (#37175)
Co-authored-by: Dax Raad <d@ironbay.co>
This commit is contained in:
parent
f2f5eb6f16
commit
f92d84746b
19 changed files with 85 additions and 97 deletions
|
|
@ -1,15 +1,15 @@
|
||||||
export * as PluginHooks from "./hooks"
|
export * as PluginHooks from "./hooks"
|
||||||
|
|
||||||
import type { AIHooks } from "@opencode-ai/plugin/v2/effect/ai"
|
|
||||||
import type { AISDKHooks } from "@opencode-ai/plugin/v2/effect/aisdk"
|
import type { AISDKHooks } from "@opencode-ai/plugin/v2/effect/aisdk"
|
||||||
|
import type { SessionHooks } from "@opencode-ai/plugin/v2/effect/session"
|
||||||
import type { ToolHooks } from "@opencode-ai/plugin/v2/effect/tool"
|
import type { ToolHooks } from "@opencode-ai/plugin/v2/effect/tool"
|
||||||
import { Context, Effect, Layer, Scope } from "effect"
|
import { Context, Effect, Layer, Scope } from "effect"
|
||||||
import { makeLocationNode } from "../effect/app-node"
|
import { makeLocationNode } from "../effect/app-node"
|
||||||
import { State } from "../state"
|
import { State } from "../state"
|
||||||
|
|
||||||
export interface Domains {
|
export interface Domains {
|
||||||
readonly ai: AIHooks
|
|
||||||
readonly aisdk: AISDKHooks
|
readonly aisdk: AISDKHooks
|
||||||
|
readonly session: SessionHooks
|
||||||
readonly tool: ToolHooks
|
readonly tool: ToolHooks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,9 +80,6 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
ai: {
|
|
||||||
hook: (name, callback) => hooks.register("ai", name, callback),
|
|
||||||
},
|
|
||||||
aisdk: {
|
aisdk: {
|
||||||
hook: (name, callback) => {
|
hook: (name, callback) => {
|
||||||
if (name === "sdk") {
|
if (name === "sdk") {
|
||||||
|
|
@ -370,6 +367,7 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
session: {
|
session: {
|
||||||
|
hook: (name, callback) => hooks.register("session", name, callback),
|
||||||
create: (input) =>
|
create: (input) =>
|
||||||
runtime.session.create({
|
runtime.session.create({
|
||||||
id: input?.id,
|
id: input?.id,
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,6 @@ export function fromPromise(plugin: Plugin) {
|
||||||
transform: transform(host.agent),
|
transform: transform(host.agent),
|
||||||
reload: () => run(host.agent.reload()),
|
reload: () => run(host.agent.reload()),
|
||||||
},
|
},
|
||||||
ai: {
|
|
||||||
hook: (name, callback) =>
|
|
||||||
register(host.ai.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
|
|
||||||
},
|
|
||||||
aisdk: {
|
aisdk: {
|
||||||
hook: (name, callback) =>
|
hook: (name, callback) =>
|
||||||
register(host.aisdk.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
|
register(host.aisdk.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
|
||||||
|
|
@ -166,6 +162,8 @@ export function fromPromise(plugin: Plugin) {
|
||||||
register(host.tool.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
|
register(host.tool.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
|
||||||
},
|
},
|
||||||
session: {
|
session: {
|
||||||
|
hook: (name, callback) =>
|
||||||
|
register(host.session.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
|
||||||
create: (input) =>
|
create: (input) =>
|
||||||
run(
|
run(
|
||||||
host.session.create(
|
host.session.create(
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
isContextOverflowFailure,
|
isContextOverflowFailure,
|
||||||
type ProviderErrorEvent,
|
type ProviderErrorEvent,
|
||||||
} from "@opencode-ai/ai"
|
} from "@opencode-ai/ai"
|
||||||
import type { AIHooks } from "@opencode-ai/plugin/v2/effect/ai"
|
import type { SessionHooks } from "@opencode-ai/plugin/v2/effect/session"
|
||||||
import { SessionError } from "@opencode-ai/schema/session-error"
|
import { SessionError } from "@opencode-ai/schema/session-error"
|
||||||
import { Money } from "@opencode-ai/schema/money"
|
import { Money } from "@opencode-ai/schema/money"
|
||||||
import { Cause, Effect, Exit, Fiber, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
|
import { Cause, Effect, Exit, Fiber, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
|
||||||
|
|
@ -216,7 +216,7 @@ const layer = Layer.effect(
|
||||||
toolChoice: isLastStep ? "none" : undefined,
|
toolChoice: isLastStep ? "none" : undefined,
|
||||||
})
|
})
|
||||||
const availableTools = new Map(request.tools.map((tool) => [tool.name, tool]))
|
const availableTools = new Map(request.tools.map((tool) => [tool.name, tool]))
|
||||||
const requestEvent: AIHooks["request"] = {
|
const contextEvent: SessionHooks["context"] = {
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
agent: agent.id,
|
agent: agent.id,
|
||||||
model: resolved.ref,
|
model: resolved.ref,
|
||||||
|
|
@ -228,11 +228,11 @@ const layer = Layer.effect(
|
||||||
}
|
}
|
||||||
// Plugins may reshape the draft but cannot advertise tools excluded by
|
// Plugins may reshape the draft but cannot advertise tools excluded by
|
||||||
// permissions, registration state, or the selected agent's step limit.
|
// permissions, registration state, or the selected agent's step limit.
|
||||||
yield* hooks.trigger("ai", "request", requestEvent)
|
yield* hooks.trigger("session", "context", contextEvent)
|
||||||
const hookedRequest = LLM.updateRequest(request, {
|
const hookedRequest = LLM.updateRequest(request, {
|
||||||
system: requestEvent.system,
|
system: contextEvent.system,
|
||||||
messages: requestEvent.messages,
|
messages: contextEvent.messages,
|
||||||
tools: Object.entries(requestEvent.tools).flatMap(([name, tool]) => {
|
tools: Object.entries(contextEvent.tools).flatMap(([name, tool]) => {
|
||||||
const registered = availableTools.get(name)
|
const registered = availableTools.get(name)
|
||||||
if (!registered) return []
|
if (!registered) return []
|
||||||
return [{ ...registered, description: tool.description, inputSchema: tool.input }]
|
return [{ ...registered, description: tool.description, inputSchema: tool.input }]
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ export const Plugin = {
|
||||||
)
|
)
|
||||||
.pipe(Effect.orDie)
|
.pipe(Effect.orDie)
|
||||||
|
|
||||||
yield* ctx.ai.hook("request", (event) =>
|
yield* ctx.session.hook("context", (event) =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
const usePatch =
|
const usePatch =
|
||||||
event.model.providerID.toLowerCase() === "openai" || event.model.id.toLowerCase().includes("gpt")
|
event.model.providerID.toLowerCase() === "openai" || event.model.id.toLowerCase().includes("gpt")
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ export const Plugin = {
|
||||||
)
|
)
|
||||||
.pipe(Effect.orDie)
|
.pipe(Effect.orDie)
|
||||||
|
|
||||||
yield* ctx.ai.hook("request", (event) =>
|
yield* ctx.session.hook("context", (event) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const tool = event.tools[name]
|
const tool = event.tools[name]
|
||||||
if (!tool) return
|
if (!tool) return
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export const registerToolPlugin = <R>(plugin: {
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const tools = yield* Tools.Service
|
const tools = yield* Tools.Service
|
||||||
const context = host({
|
const context = host({
|
||||||
ai: {
|
session: {
|
||||||
hook: () => Effect.succeed({ dispose: Effect.void }),
|
hook: () => Effect.succeed({ dispose: Effect.void }),
|
||||||
},
|
},
|
||||||
tool: {
|
tool: {
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,17 @@ const layer = PluginHooks.node.implementation as Layer.Layer<PluginHooks.Service
|
||||||
const it = testEffect(layer)
|
const it = testEffect(layer)
|
||||||
|
|
||||||
describe("PluginHooks", () => {
|
describe("PluginHooks", () => {
|
||||||
it.effect("registers scoped AI hooks and triggers them sequentially", () =>
|
it.effect("registers scoped session hooks and triggers them sequentially", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const hooks = yield* PluginHooks.Service
|
const hooks = yield* PluginHooks.Service
|
||||||
const seen: string[] = []
|
const seen: string[] = []
|
||||||
yield* hooks.register("ai", "request", (event) =>
|
yield* hooks.register("session", "context", (event) =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
seen.push("first")
|
seen.push("first")
|
||||||
event.system.push(SystemPart.make("second"))
|
event.system.push(SystemPart.make("second"))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
yield* hooks.register("ai", "request", (event) =>
|
yield* hooks.register("session", "context", (event) =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
seen.push(event.system[1]?.text ?? "missing")
|
seen.push(event.system[1]?.text ?? "missing")
|
||||||
event.messages = [Message.user("changed")]
|
event.messages = [Message.user("changed")]
|
||||||
|
|
@ -37,7 +37,7 @@ describe("PluginHooks", () => {
|
||||||
tools: {},
|
tools: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(yield* hooks.trigger("ai", "request", event)).toBe(event)
|
expect(yield* hooks.trigger("session", "context", event)).toBe(event)
|
||||||
expect(seen).toEqual(["first", "second"])
|
expect(seen).toEqual(["first", "second"])
|
||||||
expect(event.messages).toEqual([Message.user("changed")])
|
expect(event.messages).toEqual([Message.user("changed")])
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||||
import type { IntegrationEnvMethod, IntegrationKeyMethod, IntegrationOAuthMethod } from "@opencode-ai/sdk/v2/types"
|
import type { IntegrationEnvMethod, IntegrationKeyMethod, IntegrationOAuthMethod } from "@opencode-ai/sdk/v2/types"
|
||||||
import { Effect, Stream } from "effect"
|
import { Effect, Stream } from "effect"
|
||||||
|
|
||||||
type Overrides = Partial<Omit<PluginContext, "options">>
|
type Overrides = Partial<Omit<PluginContext, "options" | "session">> & {
|
||||||
|
readonly session?: Partial<PluginContext["session"]>
|
||||||
|
}
|
||||||
|
|
||||||
export function host(overrides: Overrides = {}): PluginContext {
|
export function host(overrides: Overrides = {}): PluginContext {
|
||||||
return {
|
return {
|
||||||
|
|
@ -18,9 +20,6 @@ export function host(overrides: Overrides = {}): PluginContext {
|
||||||
transform: () => Effect.die("unused agent.transform"),
|
transform: () => Effect.die("unused agent.transform"),
|
||||||
reload: () => Effect.die("unused agent.reload"),
|
reload: () => Effect.die("unused agent.reload"),
|
||||||
},
|
},
|
||||||
ai: overrides.ai ?? {
|
|
||||||
hook: () => Effect.die("unused ai.hook"),
|
|
||||||
},
|
|
||||||
aisdk: overrides.aisdk ?? {
|
aisdk: overrides.aisdk ?? {
|
||||||
hook: () => Effect.die("unused aisdk.hook"),
|
hook: () => Effect.die("unused aisdk.hook"),
|
||||||
},
|
},
|
||||||
|
|
@ -80,12 +79,13 @@ export function host(overrides: Overrides = {}): PluginContext {
|
||||||
transform: () => Effect.die("unused tool.transform"),
|
transform: () => Effect.die("unused tool.transform"),
|
||||||
hook: () => Effect.die("unused tool.hook"),
|
hook: () => Effect.die("unused tool.hook"),
|
||||||
},
|
},
|
||||||
session: overrides.session ?? {
|
session: {
|
||||||
create: () => Effect.die("unused session.create"),
|
hook: overrides.session?.hook ?? (() => Effect.die("unused session.hook")),
|
||||||
get: () => Effect.die("unused session.get"),
|
create: overrides.session?.create ?? (() => Effect.die("unused session.create")),
|
||||||
prompt: () => Effect.die("unused session.prompt"),
|
get: overrides.session?.get ?? (() => Effect.die("unused session.get")),
|
||||||
command: () => Effect.die("unused session.command"),
|
prompt: overrides.session?.prompt ?? (() => Effect.die("unused session.prompt")),
|
||||||
interrupt: () => Effect.die("unused session.interrupt"),
|
command: overrides.session?.command ?? (() => Effect.die("unused session.command")),
|
||||||
|
interrupt: overrides.session?.interrupt ?? (() => Effect.die("unused session.interrupt")),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { SessionV2 } from "@opencode-ai/core/session"
|
||||||
import { SessionMessage } from "@opencode-ai/core/session/message"
|
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||||
import { ToolRegistry } from "@opencode-ai/core/tool/registry"
|
import { ToolRegistry } from "@opencode-ai/core/tool/registry"
|
||||||
import { Plugin } from "@opencode-ai/plugin/v2"
|
import { Plugin } from "@opencode-ai/plugin/v2"
|
||||||
import type { AIHooks } from "@opencode-ai/plugin/v2/effect/ai"
|
import type { SessionHooks } from "@opencode-ai/plugin/v2/effect/session"
|
||||||
import { Model } from "@opencode-ai/schema/model"
|
import { Model } from "@opencode-ai/schema/model"
|
||||||
import { Provider } from "@opencode-ai/schema/provider"
|
import { Provider } from "@opencode-ai/schema/provider"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
|
|
@ -77,24 +77,24 @@ describe("fromPromise", () => {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.effect("forwards AI request hooks", () =>
|
it.effect("forwards session context hooks", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const plugin = yield* PluginV2.Service
|
const plugin = yield* PluginV2.Service
|
||||||
const hooks = yield* PluginHooks.Service
|
const hooks = yield* PluginHooks.Service
|
||||||
const host = yield* PluginHost.make(plugin)
|
const host = yield* PluginHost.make(plugin)
|
||||||
yield* PluginPromise.fromPromise(
|
yield* PluginPromise.fromPromise(
|
||||||
Plugin.define({
|
Plugin.define({
|
||||||
id: "promise-ai-request",
|
id: "promise-session-context",
|
||||||
setup: async (ctx) => {
|
setup: async (ctx) => {
|
||||||
await ctx.ai.hook("request", (event) => {
|
await ctx.session.hook("context", (event) => {
|
||||||
event.system.push(SystemPart.make("Promise hook"))
|
event.system.push(SystemPart.make("Promise hook"))
|
||||||
delete event.tools.echo
|
delete event.tools.echo
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).effect(host)
|
).effect(host)
|
||||||
const event: AIHooks["request"] = {
|
const event: SessionHooks["context"] = {
|
||||||
sessionID: SessionV2.ID.make("ses_promise_ai_request"),
|
sessionID: SessionV2.ID.make("ses_promise_session_context"),
|
||||||
agent: AgentV2.ID.make("build"),
|
agent: AgentV2.ID.make("build"),
|
||||||
model: Model.Ref.make({ providerID: Provider.ID.make("test"), id: Model.ID.make("model") }),
|
model: Model.Ref.make({ providerID: Provider.ID.make("test"), id: Model.ID.make("model") }),
|
||||||
system: [SystemPart.make("Initial")],
|
system: [SystemPart.make("Initial")],
|
||||||
|
|
@ -102,7 +102,7 @@ describe("fromPromise", () => {
|
||||||
tools: { echo: { description: "Echo", input: { type: "object" } } },
|
tools: { echo: { description: "Echo", input: { type: "object" } } },
|
||||||
}
|
}
|
||||||
|
|
||||||
yield* hooks.trigger("ai", "request", event)
|
yield* hooks.trigger("session", "context", event)
|
||||||
|
|
||||||
expect(event.system.map((part) => part.text)).toEqual(["Initial", "Promise hook"])
|
expect(event.system.map((part) => part.text)).toEqual(["Initial", "Promise hook"])
|
||||||
expect(event.tools).toEqual({})
|
expect(event.tools).toEqual({})
|
||||||
|
|
|
||||||
|
|
@ -777,11 +777,11 @@ const verifyPartialFlushOnInterruption = (kind: FragmentKind) =>
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("SessionRunnerLLM", () => {
|
describe("SessionRunnerLLM", () => {
|
||||||
it.effect("applies AI request hooks without exposing unavailable tools", () =>
|
it.effect("applies session context hooks without exposing unavailable tools", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const session = yield* setup
|
const session = yield* setup
|
||||||
const hooks = yield* PluginHooks.Service
|
const hooks = yield* PluginHooks.Service
|
||||||
yield* hooks.register("ai", "request", (event) =>
|
yield* hooks.register("session", "context", (event) =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
event.system = [SystemPart.make("Hooked system")]
|
event.system = [SystemPart.make("Hooked system")]
|
||||||
event.messages = [Message.user("Hooked message")]
|
event.messages = [Message.user("Hooked message")]
|
||||||
|
|
|
||||||
|
|
@ -80,11 +80,11 @@ yield *
|
||||||
|
|
||||||
Hooks run sequentially in registration order. Later hooks observe mutations made by earlier hooks.
|
Hooks run sequentially in registration order. Later hooks observe mutations made by earlier hooks.
|
||||||
|
|
||||||
AI request context is mutable immediately before provider dispatch:
|
Session context is mutable immediately before provider dispatch:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
yield *
|
yield *
|
||||||
ctx.ai.hook("request", (event) =>
|
ctx.session.hook("context", (event) =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
event.tools.read.description = "Read a file using narrow line ranges."
|
event.tools.read.description = "Read a file using narrow line ranges."
|
||||||
delete event.tools.write
|
delete event.tools.write
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
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 AIRequest {
|
|
||||||
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 AIHooks {
|
|
||||||
readonly request: AIRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AIDomain {
|
|
||||||
readonly hook: Hooks<AIHooks>
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,6 @@ import type { PluginApi } from "@opencode-ai/client/effect/api"
|
||||||
import type { Effect, Scope } from "effect"
|
import type { Effect, Scope } from "effect"
|
||||||
import type { PluginOptions } from "../options.js"
|
import type { PluginOptions } from "../options.js"
|
||||||
import type { AgentDomain } from "./agent.js"
|
import type { AgentDomain } from "./agent.js"
|
||||||
import type { AIDomain } from "./ai.js"
|
|
||||||
import type { AISDKDomain } from "./aisdk.js"
|
import type { AISDKDomain } from "./aisdk.js"
|
||||||
import type { CatalogDomain } from "./catalog.js"
|
import type { CatalogDomain } from "./catalog.js"
|
||||||
import type { CommandDomain } from "./command.js"
|
import type { CommandDomain } from "./command.js"
|
||||||
|
|
@ -16,7 +15,6 @@ import type { ToolDomain } from "./tool.js"
|
||||||
export interface Context {
|
export interface Context {
|
||||||
readonly options: PluginOptions
|
readonly options: PluginOptions
|
||||||
readonly agent: AgentDomain
|
readonly agent: AgentDomain
|
||||||
readonly ai: AIDomain
|
|
||||||
readonly aisdk: AISDKDomain
|
readonly aisdk: AISDKDomain
|
||||||
readonly catalog: CatalogDomain
|
readonly catalog: CatalogDomain
|
||||||
readonly command: CommandDomain
|
readonly command: CommandDomain
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,24 @@
|
||||||
import type { SessionApi } from "@opencode-ai/client/effect/api"
|
import type { SessionApi } from "@opencode-ai/client/effect/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 type SessionDomain = Pick<SessionApi<unknown>, "create" | "get" | "prompt" | "command" | "interrupt">
|
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<unknown>, "create" | "get" | "prompt" | "command" | "interrupt"> & {
|
||||||
|
readonly hook: Hooks<SessionHooks>
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,10 @@ await ctx.aisdk.hook("language", (event) => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
AI request context is mutable immediately before provider dispatch:
|
Session context is mutable immediately before provider dispatch:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
await ctx.ai.hook("request", (event) => {
|
await ctx.session.hook("context", (event) => {
|
||||||
event.tools.read.description = "Read a file using narrow line ranges."
|
event.tools.read.description = "Read a file using narrow line ranges."
|
||||||
delete event.tools.write
|
delete event.tools.write
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
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 AIRequest {
|
|
||||||
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 AIHooks {
|
|
||||||
readonly request: AIRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AIDomain {
|
|
||||||
readonly hook: Hooks<AIHooks>
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import type { PluginApi } from "@opencode-ai/client/promise/api"
|
import type { PluginApi } from "@opencode-ai/client/promise/api"
|
||||||
import type { PluginOptions } from "../options.js"
|
import type { PluginOptions } from "../options.js"
|
||||||
import type { AgentDomain } from "./agent.js"
|
import type { AgentDomain } from "./agent.js"
|
||||||
import type { AIDomain } from "./ai.js"
|
|
||||||
import type { AISDKDomain } from "./aisdk.js"
|
import type { AISDKDomain } from "./aisdk.js"
|
||||||
import type { CatalogDomain } from "./catalog.js"
|
import type { CatalogDomain } from "./catalog.js"
|
||||||
import type { CommandDomain } from "./command.js"
|
import type { CommandDomain } from "./command.js"
|
||||||
|
|
@ -15,7 +14,6 @@ import type { ToolDomain } from "./tool.js"
|
||||||
export interface Context {
|
export interface Context {
|
||||||
readonly options: PluginOptions
|
readonly options: PluginOptions
|
||||||
readonly agent: AgentDomain
|
readonly agent: AgentDomain
|
||||||
readonly ai: AIDomain
|
|
||||||
readonly aisdk: AISDKDomain
|
readonly aisdk: AISDKDomain
|
||||||
readonly catalog: CatalogDomain
|
readonly catalog: CatalogDomain
|
||||||
readonly command: CommandDomain
|
readonly command: CommandDomain
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,24 @@
|
||||||
import type { SessionApi } from "@opencode-ai/client/promise/api"
|
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 type SessionDomain = Pick<SessionApi, "create" | "get" | "prompt" | "command" | "interrupt">
|
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" | "command" | "interrupt"> & {
|
||||||
|
readonly hook: Hooks<SessionHooks>
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue