diff --git a/packages/core/src/plugin/host.ts b/packages/core/src/plugin/host.ts index 49f177b8e74..a8ffc72c656 100644 --- a/packages/core/src/plugin/host.ts +++ b/packages/core/src/plugin/host.ts @@ -401,6 +401,8 @@ export const make = Effect.fn("PluginHost.make")(function* ( input?.location ?? Location.Ref.make({ directory: location.directory, workspaceID: location.workspaceID }), }), get: (input) => runtime.session.get(input.sessionID), + switchAgent: runtime.session.switchAgent, + switchModel: runtime.session.switchModel, prompt: runtime.session.prompt, generate: (input) => runtime.session.generate(input).pipe(Effect.map((text) => ({ text }))), command: runtime.session.command, diff --git a/packages/core/test/plugin/host.ts b/packages/core/test/plugin/host.ts index b6c850cc7db..c64321c329a 100644 --- a/packages/core/test/plugin/host.ts +++ b/packages/core/test/plugin/host.ts @@ -117,6 +117,8 @@ export function host(overrides: Overrides = {}): Plugin.Context { hook: overrides.session?.hook ?? (() => Effect.die("unused session.hook")), create: overrides.session?.create ?? (() => Effect.die("unused session.create")), get: overrides.session?.get ?? (() => Effect.die("unused session.get")), + switchAgent: overrides.session?.switchAgent ?? (() => Effect.die("unused session.switchAgent")), + switchModel: overrides.session?.switchModel ?? (() => Effect.die("unused session.switchModel")), prompt: overrides.session?.prompt ?? (() => Effect.die("unused session.prompt")), generate: overrides.session?.generate ?? (() => Effect.die("unused session.generate")), command: overrides.session?.command ?? (() => Effect.die("unused session.command")), diff --git a/packages/core/test/plugin/promise.test.ts b/packages/core/test/plugin/promise.test.ts index 6b4fa707542..eafd5c5518f 100644 --- a/packages/core/test/plugin/promise.test.ts +++ b/packages/core/test/plugin/promise.test.ts @@ -133,6 +133,8 @@ describe("fromPromise", () => { expect(input.continue).toBe(true) return Effect.void }, + switchAgent: (input) => Effect.sync(() => seen.push(input)), + switchModel: (input) => Effect.sync(() => seen.push(input)), rename: (input) => Effect.sync(() => seen.push(input)), wait: (input) => Effect.sync(() => seen.push(input)), }, @@ -144,6 +146,13 @@ describe("fromPromise", () => { setup: async (ctx) => { expect(await ctx.session.interrupt({ sessionID: "ses_success", continue: true })).toBeUndefined() await expect(ctx.session.interrupt({ sessionID: "ses_failure" })).rejects.toThrow("interrupt failed") + expect(await ctx.session.switchAgent({ sessionID: "ses_success", agent: "build" })).toBeUndefined() + expect( + await ctx.session.switchModel({ + sessionID: "ses_success", + model: { providerID: "openai", id: "gpt-5" }, + }), + ).toBeUndefined() expect(await ctx.session.rename({ sessionID: "ses_success", title: "Renamed" })).toBeUndefined() expect(await ctx.session.wait({ sessionID: "ses_success" })).toBeUndefined() }, @@ -151,6 +160,11 @@ describe("fromPromise", () => { ).effect(host) expect(seen).toEqual([ + { sessionID: Session.ID.make("ses_success"), agent: Agent.ID.make("build") }, + { + sessionID: Session.ID.make("ses_success"), + model: { providerID: Provider.ID.make("openai"), id: Model.ID.make("gpt-5") }, + }, { sessionID: Session.ID.make("ses_success"), title: "Renamed" }, { sessionID: Session.ID.make("ses_success") }, ]) diff --git a/packages/plugin/src/effect/session.ts b/packages/plugin/src/effect/session.ts index 41ea2918903..965aaea3508 100644 --- a/packages/plugin/src/effect/session.ts +++ b/packages/plugin/src/effect/session.ts @@ -47,7 +47,17 @@ export interface SessionHooks { export type SessionDomain = Pick< SessionApi, - "create" | "get" | "prompt" | "generate" | "command" | "synthetic" | "interrupt" | "rename" | "wait" + | "create" + | "get" + | "switchAgent" + | "switchModel" + | "prompt" + | "generate" + | "command" + | "synthetic" + | "interrupt" + | "rename" + | "wait" > & { readonly hook: ModelHooks } diff --git a/packages/plugin/src/promise/adapter.ts b/packages/plugin/src/promise/adapter.ts index 5e20d72f5d3..a0459205037 100644 --- a/packages/plugin/src/promise/adapter.ts +++ b/packages/plugin/src/promise/adapter.ts @@ -309,6 +309,8 @@ export function fromPromise(plugin: Plugin) { ), create: adaptApiMethod(SessionEndpoints["session.create"], host.session.create), get: adaptApiMethod(SessionEndpoints["session.get"], host.session.get), + switchAgent: adaptApiMethod(SessionEndpoints["session.switchAgent"], host.session.switchAgent), + switchModel: adaptApiMethod(SessionEndpoints["session.switchModel"], host.session.switchModel), prompt: adaptApiMethod(SessionEndpoints["session.prompt"], host.session.prompt), generate: adaptApiMethod(SessionEndpoints["session.generate"], host.session.generate), command: adaptApiMethod(SessionEndpoints["session.command"], host.session.command), diff --git a/packages/plugin/src/promise/session.ts b/packages/plugin/src/promise/session.ts index d4ea141154a..e87a5e0441f 100644 --- a/packages/plugin/src/promise/session.ts +++ b/packages/plugin/src/promise/session.ts @@ -47,7 +47,17 @@ export interface SessionHooks { export type SessionDomain = Pick< SessionApi, - "create" | "get" | "prompt" | "generate" | "command" | "synthetic" | "interrupt" | "rename" | "wait" + | "create" + | "get" + | "switchAgent" + | "switchModel" + | "prompt" + | "generate" + | "command" + | "synthetic" + | "interrupt" + | "rename" + | "wait" > & { readonly hook: ModelHooks }