Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157import { describe, expect } from "bun:test"import { OpenAIChat } from "@opencode/ai/protocols"import { Agent } from "@opencode/schema/agent"import { Money } from "@opencode/schema/money"import { Session } from "@opencode/schema/session"import type { SessionRequestKind } from "@opencode/plugin/effect/session"import { Location } from "@opencode/core/location"import { PluginHooks } from "@opencode/core/plugin/hooks"import { Project } from "@opencode/core/project"import { AbsolutePath } from "@opencode/core/schema"import { SessionModelRequest } from "@opencode/core/session/model-request"import { SessionModelTransport } from "@opencode/core/session/model-transport"import { SessionRunnerModel } from "@opencode/core/session/runner/model"import { DateTime, Effect, Stream } from "effect"import { HttpClientRequest, HttpClientResponse } from "effect/unstable/http"import { testEffect } from "./lib/effect"import { PluginTestLayer } from "./plugin/fixture"
const it = testEffect(PluginTestLayer)
const KINDS: ReadonlyArray<SessionRequestKind> = ["primary", "compaction", "title", "generate"]
const session = Session.Info.make({ id: Session.ID.make("ses_hook_kind"), projectID: Project.ID.global, cost: Money.USD.zero, tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, time: { created: DateTime.makeUnsafe(0), updated: DateTime.makeUnsafe(0) }, location: Location.Ref.make({ directory: AbsolutePath.make("/project") }),})const model = SessionRunnerModel.resolved(OpenAIChat.route.model({ id: "gpt-5.5", provider: "test" }), { capabilities: { tools: true, input: ["text"], output: ["text"] }, cost: [], limit: { context: 200_000, output: 32_000 },})const transport = SessionModelTransport.Service.of({ bind: () => ({ execute: () => Effect.die("unused WebSocket execution") }), close: () => Effect.void, closeAll: Effect.void,})
describe("SessionModelRequest HTTP hooks", () => { it.effect("tags every Session request kind on http.request and http.response", () => Effect.gen(function* () { const hooks = yield* PluginHooks.Service const seen: Array<{ hook: string; kind: SessionRequestKind; agent: Agent.ID }> = [] yield* hooks.register("session", "http.request", (event) => Effect.sync(() => { seen.push({ hook: "request", kind: event.kind, agent: event.agent }) }), ) yield* hooks.register("session", "http.response", (event) => Effect.sync(() => { seen.push({ hook: "response", kind: event.kind, agent: event.agent }) }), ) const requests = yield* SessionModelRequest.Service.pipe(Effect.provide(SessionModelRequest.layer))
for (const kind of KINDS) { const prepared = yield* requests[kind]({ session, agent: Agent.ID.make("build"), model, system: [], messages: [], }) const http = prepared.options.http if (!http) throw new Error(`Expected HTTP middleware for ${kind}`) yield* http(HttpClientRequest.post("https://example.test/v1/chat/completions"), (request) => Effect.succeed(HttpClientResponse.fromWeb(request, new Response("{}", { status: 200 }))), ) }
expect(seen).toEqual( KINDS.flatMap((kind) => [ { hook: "request", kind, agent: Agent.ID.make("build") }, { hook: "response", kind, agent: Agent.ID.make("build") }, ]), ) }).pipe(Effect.provideService(SessionModelTransport.Service, transport)), )
it.effect("offers the WebSocket executor alongside HTTP hooks and routes the WebSocket hooks", () => Effect.gen(function* () { const hooks = yield* PluginHooks.Service const seen: string[] = [] yield* hooks.register("session", "http.request", () => Effect.sync(() => void seen.push("http.request"))) yield* hooks.register("session", "experimental.ws.handshake", (event) => Effect.sync(() => { seen.push(`handshake:${event.kind}:${event.url}`) event.headers.authorization = "Bearer minted" delete event.headers["api-key"] }), ) yield* hooks.register("session", "experimental.ws.send", (event) => Effect.sync(() => { seen.push(`send:${event.kind}:${event.frame}`) event.frame = `${event.frame}+plugin` }), ) yield* hooks.register("session", "experimental.ws.receive", (event) => Effect.sync(() => { seen.push(`receive:${event.kind}:${event.frame}`) event.frame = event.frame.toUpperCase() }), ) const bound: Array<{ url: string; headers: Record<string, string> }> = [] const frames: string[] = [] const websocketTransport = SessionModelTransport.Service.of({ bind: (_sessionID, interceptor) => ({ execute: () => Effect.gen(function* () { if (!interceptor?.handshake || !interceptor.send || !interceptor.receive) throw new Error("Expected a full WebSocket interceptor") bound.push( yield* interceptor.handshake({ url: "wss://example.test/v1/responses", headers: { "api-key": "k" } }), ) frames.push(yield* interceptor.send("create")) frames.push(yield* interceptor.receive("created")) return { frames: Stream.empty, complete: Effect.void } }), }), close: () => Effect.void, closeAll: Effect.void, }) const requests = yield* SessionModelRequest.Service.pipe( Effect.provide(SessionModelRequest.layer), Effect.provideService(SessionModelTransport.Service, websocketTransport), ) const prepared = yield* requests.primary({ session, agent: Agent.ID.make("build"), model: SessionRunnerModel.resolved(OpenAIChat.route.model({ id: "gpt-5.5", provider: "test" }), { capabilities: { tools: true, input: ["text"], output: ["text"] }, cost: [], limit: { context: 200_000, output: 32_000 }, transport: "websocket", }), system: [], messages: [], webSocket: "session", })
expect(prepared.options.http).toBeDefined() expect(prepared.options.webSocket).toBeDefined() yield* prepared.options.webSocket!.execute({} as never) expect(bound).toEqual([{ url: "wss://example.test/v1/responses", headers: { authorization: "Bearer minted" } }]) expect(frames).toEqual(["create+plugin", "CREATED"]) expect(seen).toEqual([ "handshake:primary:wss://example.test/v1/responses", "send:primary:create", "receive:primary:created", ]) }), )})