From fc051e49eea4c786b3121a7b514d2ab3b2ea1cbf Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:26:45 -0500 Subject: [PATCH] feat(core): add pinned session move tool (#46862) Co-authored-by: thdxr <826656+thdxr@users.noreply.github.com> Co-authored-by: rekram1-node --- packages/core/src/plugin/internal.ts | 2 + packages/core/src/tool/plugin/opencode.ts | 52 +++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 packages/core/src/tool/plugin/opencode.ts diff --git a/packages/core/src/plugin/internal.ts b/packages/core/src/plugin/internal.ts index 7af3cc0c8f8..fab8581fd5d 100644 --- a/packages/core/src/plugin/internal.ts +++ b/packages/core/src/plugin/internal.ts @@ -63,6 +63,7 @@ import { PatchTool } from "../tool/plugin/patch.js" import { EditTool } from "../tool/plugin/edit.js" import { GlobTool } from "../tool/plugin/glob.js" import { GrepTool } from "../tool/plugin/grep.js" +import { OpenCodeTools } from "../tool/plugin/opencode.js" import { QuestionTool } from "../tool/plugin/question.js" import { ReadToolFileSystem } from "../tool/read-filesystem.js" import { ReadTool } from "../tool/plugin/read.js" @@ -204,6 +205,7 @@ const pre = [ EditTool.Plugin, GlobTool.Plugin, GrepTool.Plugin, + OpenCodeTools.Plugin, QuestionTool.Plugin, ReadTool.Plugin, ShellTool.Plugin, diff --git a/packages/core/src/tool/plugin/opencode.ts b/packages/core/src/tool/plugin/opencode.ts new file mode 100644 index 00000000000..4673239a2cc --- /dev/null +++ b/packages/core/src/tool/plugin/opencode.ts @@ -0,0 +1,52 @@ +export * as OpenCodeTools from "./opencode.js" + +import { ToolFailure } from "@opencode-ai/ai" +import type { Context } from "@opencode-ai/plugin/effect/plugin" +import { AbsolutePath } from "@opencode-ai/schema/schema" +import { Session } from "@opencode-ai/schema/session" +import { Effect, Schema } from "effect" + +export const MoveInput = Schema.Struct({ + sessionID: Schema.optionalKey(Session.ID).annotate({ description: "Omit to move the current session." }), + directory: AbsolutePath.check(Schema.isMinLength(1)).annotate({ + description: "Destination directory, relative to the target session's directory or absolute. Supports ~.", + }), +}) + +const MoveOutput = Schema.Struct({ sessionID: Session.ID, directory: AbsolutePath }) + +export const Plugin = { + id: "opencode.tools", + effect: Effect.fn("OpenCodeTools.Plugin")(function* (ctx: Context) { + yield* ctx.tool + .transform((draft) => { + draft.namespace({ name: "opencode", description: "OpenCode session and runtime tools." }) + draft.add({ + name: "session_move", + description: + "Move a session to another directory, or omit sessionID to move the current session. The current session moves at the next safe boundary; do not run destination-dependent tools in the same execute call.", + input: MoveInput, + output: MoveOutput, + options: { namespace: "opencode", codemode: true, pinned: true }, + execute: (input, context) => + Effect.gen(function* () { + const sessionID = input.sessionID ?? context.sessionID + yield* ctx.session.move({ + sessionID, + directory: input.directory, + delivery: "steer", + }) + return { + output: { sessionID, directory: input.directory }, + content: `Moved session ${sessionID} to ${input.directory}.`, + } + }).pipe( + Effect.mapError( + (error) => new ToolFailure({ message: `Unable to move session to ${input.directory}`, error }), + ), + ), + }) + }) + .pipe(Effect.orDie) + }), +}