feat(plugin): add session request hook

This commit is contained in:
Aiden Cline
2026-07-17 17:02:06 -05:00
parent 987242b3e8
commit d08eecd7f0
5 changed files with 92 additions and 1 deletions
+32 -1
View File
@@ -4,6 +4,7 @@ import { Agent } from "@opencode-ai/schema/agent"
import { Model } from "@opencode-ai/schema/model"
import { Provider } from "@opencode-ai/schema/provider"
import { Session } from "@opencode-ai/schema/session"
import type { SessionHooks } from "@opencode-ai/plugin/v2/effect/session"
import { Effect, Layer } from "effect"
import { PluginHooks } from "../src/plugin/hooks"
import { testEffect } from "./lib/effect"
@@ -28,7 +29,7 @@ describe("PluginHooks", () => {
event.messages = [Message.user("changed")]
}),
)
const event = {
const event: SessionHooks["context"] = {
sessionID: Session.ID.make("ses_hooks"),
agent: Agent.ID.make("build"),
model: Model.Ref.make({ providerID: Provider.ID.make("test"), id: Model.ID.make("model") }),
@@ -42,4 +43,34 @@ describe("PluginHooks", () => {
expect(event.messages).toEqual([Message.user("changed")])
}),
)
it.effect("registers session request hooks and triggers them sequentially", () =>
Effect.gen(function* () {
const hooks = yield* PluginHooks.Service
yield* hooks.register("session", "request", (event) =>
Effect.sync(() => {
event.headers.authorization = "Bearer changed"
delete event.body.max_output_tokens
event.body.store = false
}),
)
yield* hooks.register("session", "request", (event) =>
Effect.sync(() => {
event.headers = { ...event.headers, "x-store": String(event.body.store) }
event.body = { input: event.body.input ?? [] }
}),
)
const event: SessionHooks["request"] = {
sessionID: Session.ID.make("ses_request_hooks"),
agent: Agent.ID.make("build"),
model: Model.Ref.make({ providerID: Provider.ID.make("test"), id: Model.ID.make("model") }),
headers: { authorization: "Bearer original" },
body: { input: ["hello"], max_output_tokens: 1024 },
}
expect(yield* hooks.trigger("session", "request", event)).toBe(event)
expect(event.headers).toEqual({ authorization: "Bearer changed", "x-store": "false" })
expect(event.body).toEqual({ input: ["hello"] })
}),
)
})
+31
View File
@@ -218,6 +218,37 @@ describe("fromPromise", () => {
}),
)
it.effect("forwards session request hooks", () =>
Effect.gen(function* () {
const plugin = yield* PluginV2.Service
const hooks = yield* PluginHooks.Service
const host = yield* PluginHost.make(plugin)
yield* PluginPromise.fromPromise(
Plugin.define({
id: "promise-session-request",
setup: async (ctx) => {
await ctx.session.hook("request", (event) => {
event.headers["x-plugin"] = "promise"
delete event.body.max_output_tokens
})
},
}),
).effect(host)
const event: SessionHooks["request"] = {
sessionID: SessionV2.ID.make("ses_promise_session_request"),
agent: AgentV2.ID.make("build"),
model: Model.Ref.make({ providerID: Provider.ID.make("test"), id: Model.ID.make("model") }),
headers: {},
body: { max_output_tokens: 1024 },
}
yield* hooks.trigger("session", "request", event)
expect(event.headers).toEqual({ "x-plugin": "promise" })
expect(event.body).toEqual({})
}),
)
it.effect("disposes a hook registration on request", () =>
Effect.gen(function* () {
const agents = yield* AgentV2.Service
+4
View File
@@ -4,8 +4,11 @@ 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 { SessionRequest } from "../session.js"
import type { Hooks } from "./registration.js"
export type { RequestBody, RequestValue, SessionRequest } from "../session.js"
export interface SessionContext {
readonly sessionID: Session.ID
readonly agent: Agent.ID
@@ -17,6 +20,7 @@ export interface SessionContext {
export interface SessionHooks {
readonly context: SessionContext
readonly request: SessionRequest
}
export type SessionDomain = Pick<
@@ -4,8 +4,11 @@ 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 { SessionRequest } from "../session.js"
import type { Hooks } from "./registration.js"
export type { RequestBody, RequestValue, SessionRequest } from "../session.js"
export interface SessionContext {
readonly sessionID: Session.ID
readonly agent: Agent.ID
@@ -17,6 +20,7 @@ export interface SessionContext {
export interface SessionHooks {
readonly context: SessionContext
readonly request: SessionRequest
}
export type SessionDomain = Pick<
+21
View File
@@ -0,0 +1,21 @@
import type { Agent } from "@opencode-ai/schema/agent"
import type { Model } from "@opencode-ai/schema/model"
import type { Session } from "@opencode-ai/schema/session"
export type RequestValue =
| null
| boolean
| number
| string
| Array<RequestValue>
| { [key: string]: RequestValue }
export type RequestBody = Record<string, RequestValue>
export interface SessionRequest {
readonly sessionID: Session.ID
readonly agent: Agent.ID
readonly model: Model.Ref
headers: Record<string, string>
body: RequestBody
}