Compare commits

...

1 Commits

Author SHA1 Message Date
Kit Langton 5910c5049e feat(plugin): expose session rename 2026-07-11 14:28:30 +00:00
8 changed files with 51 additions and 6 deletions
+1
View File
@@ -378,6 +378,7 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
get: (input) => runtime.session.get(input.sessionID),
prompt: runtime.session.prompt,
command: runtime.session.command,
rename: runtime.session.rename,
interrupt: (input) => runtime.session.interrupt(input.sessionID),
},
} satisfies Plugin.Context
+12 -2
View File
@@ -70,7 +70,8 @@ export function fromPromise(plugin: Plugin) {
catalog: {
provider: {
list: (input) => run(host.catalog.provider.list(input)),
get: (input) => run(host.catalog.provider.get({ ...input, providerID: Provider.ID.make(input.providerID) })),
get: (input) =>
run(host.catalog.provider.get({ ...input, providerID: Provider.ID.make(input.providerID) })),
},
model: {
list: (input) => run(host.catalog.model.list(input)),
@@ -96,7 +97,9 @@ export function fromPromise(plugin: Plugin) {
),
connect: {
key: (input) =>
run(host.integration.connect.key({ ...input, integrationID: Integration.ID.make(input.integrationID) })),
run(
host.integration.connect.key({ ...input, integrationID: Integration.ID.make(input.integrationID) }),
),
oauth: (input) =>
run(
host.integration.connect.oauth({
@@ -208,6 +211,13 @@ export function fromPromise(plugin: Plugin) {
resume: input.resume ?? undefined,
}),
),
rename: (input) =>
run(
host.session.rename({
...input,
sessionID: Session.ID.make(input.sessionID),
}),
),
interrupt: (input) => run(host.session.interrupt({ sessionID: Session.ID.make(input.sessionID) })),
},
}
+2 -1
View File
@@ -11,7 +11,7 @@ import { SessionV2 } from "../session"
export interface Interface {
readonly session: Pick<
SessionV2.Interface,
"get" | "create" | "messages" | "prompt" | "command" | "resume" | "interrupt" | "synthetic"
"get" | "create" | "messages" | "prompt" | "command" | "rename" | "resume" | "interrupt" | "synthetic"
>
readonly job: Pick<Job.Interface, "start" | "wait" | "block" | "background" | "cancel">
readonly location: {
@@ -51,6 +51,7 @@ export const layerWithCell = (cell: Cell) =>
messages: (input) => require(cell, (runtime) => runtime.session.messages(input)),
prompt: (input) => require(cell, (runtime) => runtime.session.prompt(input)),
command: (input) => require(cell, (runtime) => runtime.session.command(input)),
rename: (input) => require(cell, (runtime) => runtime.session.rename(input)),
resume: (sessionID) => require(cell, (runtime) => runtime.session.resume(sessionID)),
interrupt: (sessionID) => require(cell, (runtime) => runtime.session.interrupt(sessionID)),
synthetic: (input) => require(cell, (runtime) => runtime.session.synthetic(input)),
+1
View File
@@ -82,6 +82,7 @@ export function host(overrides: Overrides = {}): PluginContext {
get: () => Effect.die("unused session.get"),
prompt: () => Effect.die("unused session.prompt"),
command: () => Effect.die("unused session.command"),
rename: () => Effect.die("unused session.rename"),
interrupt: () => Effect.die("unused session.interrupt"),
},
}
+32
View File
@@ -4,6 +4,7 @@ import { AgentV2 } from "@opencode-ai/core/agent"
import { PluginV2 } from "@opencode-ai/core/plugin"
import { PluginHost } from "@opencode-ai/core/plugin/host"
import { PluginPromise } from "@opencode-ai/core/plugin/promise"
import { PluginRuntime } from "@opencode-ai/core/plugin/runtime"
import { SessionV2 } from "@opencode-ai/core/session"
import { SessionMessage } from "@opencode-ai/core/session/message"
import { ToolRegistry } from "@opencode-ai/core/tool/registry"
@@ -43,6 +44,37 @@ describe("fromPromise", () => {
}),
)
it.effect("forwards session rename", () =>
Effect.gen(function* () {
const plugin = yield* PluginV2.Service
const runtime = yield* PluginRuntime.Service
const renamed: { sessionID: SessionV2.ID; title: string }[] = []
const host = yield* PluginHost.make(plugin).pipe(
Effect.provideService(
PluginRuntime.Service,
PluginRuntime.Service.of({
...runtime,
session: {
...runtime.session,
rename: (input) =>
Effect.sync(() => {
renamed.push(input)
}),
},
}),
),
)
const promisePlugin = Plugin.define({
id: "promise-session-rename",
setup: (ctx) => ctx.session.rename({ sessionID: "ses_plugin_rename", title: "Updated scope" }),
})
yield* PluginPromise.fromPromise(promisePlugin).effect(host)
expect(renamed).toEqual([{ sessionID: SessionV2.ID.make("ses_plugin_rename"), title: "Updated scope" }])
}),
)
it.effect("loads a promise plugin and registers a transform hook", () =>
Effect.gen(function* () {
const agents = yield* AgentV2.Service
+1 -1
View File
@@ -177,7 +177,7 @@ and plugin options.
| `ctx.integration` | `list`, `get`, `connect`, `attempt`, `transform`, `reload`, and connection lookup/resolution |
| `ctx.plugin` | `list` currently active plugin IDs |
| `ctx.reference` | `list`, `transform`, `reload` |
| `ctx.session` | `create`, `get`, `prompt`, `command`, `interrupt`, and `hook` |
| `ctx.session` | `create`, `get`, `prompt`, `command`, `rename`, `interrupt`, and `hook` |
| `ctx.skill` | `list`, `transform`, `reload` |
| `ctx.tool` | `transform` and `hook` |
| `ctx.aisdk` | `hook` |
+1 -1
View File
@@ -1,3 +1,3 @@
import type { SessionApi } from "@opencode-ai/client/effect/api"
export type SessionDomain = Pick<SessionApi<unknown>, "create" | "get" | "prompt" | "command" | "interrupt">
export type SessionDomain = Pick<SessionApi<unknown>, "create" | "get" | "prompt" | "command" | "rename" | "interrupt">
+1 -1
View File
@@ -1,3 +1,3 @@
import type { SessionApi } from "@opencode-ai/client/promise/api"
export type SessionDomain = Pick<SessionApi, "create" | "get" | "prompt" | "command" | "interrupt">
export type SessionDomain = Pick<SessionApi, "create" | "get" | "prompt" | "command" | "rename" | "interrupt">