From 23bd29533f3d2ebdfe426398d1c2f47a439995d3 Mon Sep 17 00:00:00 2001 From: Evgeny Zotov Date: Sun, 5 Apr 2026 23:33:24 +0200 Subject: [PATCH 1/3] fix(session): accept directory when creating sessions --- .../opencode/src/server/routes/session.ts | 4 +++- packages/opencode/src/session/index.ts | 5 +++- .../test/server/session-actions.test.ts | 23 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/server/routes/session.ts b/packages/opencode/src/server/routes/session.ts index c33c5e989b37..4c715625dd6a 100644 --- a/packages/opencode/src/server/routes/session.ts +++ b/packages/opencode/src/server/routes/session.ts @@ -206,9 +206,11 @@ export const SessionRoutes = lazy(() => }, }), validator("json", Session.create.schema.optional()), + validator("query", z.object({ directory: z.string().optional() }).optional()), async (c) => { const body = c.req.valid("json") ?? {} - const session = await Session.create(body) + const query = c.req.valid("query") + const session = await Session.create({ ...body, ...query }) return c.json(session) }, ) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 65032de96252..b096fb84c276 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -314,6 +314,7 @@ export namespace Session { export interface Interface { readonly create: (input?: { + directory?: string parentID?: SessionID title?: string permission?: Permission.Ruleset @@ -493,12 +494,13 @@ export namespace Session { }).pipe(Effect.withSpan("Session.updatePart")) const create = Effect.fn("Session.create")(function* (input?: { + directory?: string parentID?: SessionID title?: string permission?: Permission.Ruleset workspaceID?: WorkspaceID }) { - const directory = yield* InstanceState.directory + const directory = input?.directory ?? (yield* InstanceState.directory) return yield* createNext({ parentID: input?.parentID, directory, @@ -688,6 +690,7 @@ export namespace Session { export const create = fn( z .object({ + directory: z.string().optional(), parentID: SessionID.zod.optional(), title: z.string().optional(), permission: Info.shape.permission, diff --git a/packages/opencode/test/server/session-actions.test.ts b/packages/opencode/test/server/session-actions.test.ts index e6dba676ce35..4d6f9df0dda2 100644 --- a/packages/opencode/test/server/session-actions.test.ts +++ b/packages/opencode/test/server/session-actions.test.ts @@ -35,6 +35,29 @@ async function user(sessionID: SessionID, text: string) { } describe("session action routes", () => { + test("create route accepts directory query param", async () => { + await using tmp = await tmpdir({ git: true }) + const dir = `${tmp.path}/custom-worktree` + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const app = Server.Default() + + const res = await app.request(`/session?directory=${encodeURIComponent(dir)}`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ title: "custom-dir" }), + }) + + expect(res.status).toBe(200) + const session = (await res.json()) as Session.Info + expect(session.directory).toBe(dir) + + await Session.remove(session.id) + }, + }) + }) + test("abort route calls SessionPrompt.cancel", async () => { await using tmp = await tmpdir({ git: true }) await Instance.provide({ From 1a667e93f1177625a83cfa67a1acc10a1b58d9eb Mon Sep 17 00:00:00 2001 From: Evgeny Zotov Date: Mon, 6 Apr 2026 00:27:53 +0200 Subject: [PATCH 2/3] fix(session): normalize create directory override --- packages/opencode/src/server/routes/session.ts | 2 +- packages/opencode/src/session/index.ts | 3 ++- packages/opencode/test/server/session-actions.test.ts | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/server/routes/session.ts b/packages/opencode/src/server/routes/session.ts index 4c715625dd6a..6fdd65812bb3 100644 --- a/packages/opencode/src/server/routes/session.ts +++ b/packages/opencode/src/server/routes/session.ts @@ -206,7 +206,7 @@ export const SessionRoutes = lazy(() => }, }), validator("json", Session.create.schema.optional()), - validator("query", z.object({ directory: z.string().optional() }).optional()), + validator("query", z.object({ directory: z.string().optional() })), async (c) => { const body = c.req.valid("json") ?? {} const query = c.req.valid("query") diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index b096fb84c276..5b1eed7d40b5 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -27,6 +27,7 @@ import { Snapshot } from "@/snapshot" import { ProjectID } from "../project/schema" import { WorkspaceID } from "../control-plane/schema" import { SessionID, MessageID, PartID } from "./schema" +import { Filesystem } from "@/util/filesystem" import type { Provider } from "@/provider/provider" import { ModelID, ProviderID } from "@/provider/schema" @@ -500,7 +501,7 @@ export namespace Session { permission?: Permission.Ruleset workspaceID?: WorkspaceID }) { - const directory = input?.directory ?? (yield* InstanceState.directory) + const directory = input?.directory ? Filesystem.resolve(input.directory) : yield* InstanceState.directory return yield* createNext({ parentID: input?.parentID, directory, diff --git a/packages/opencode/test/server/session-actions.test.ts b/packages/opencode/test/server/session-actions.test.ts index 4d6f9df0dda2..be0eb9f151e6 100644 --- a/packages/opencode/test/server/session-actions.test.ts +++ b/packages/opencode/test/server/session-actions.test.ts @@ -1,3 +1,4 @@ +import { mkdir } from "node:fs/promises" import { afterEach, describe, expect, mock, spyOn, test } from "bun:test" import { Instance } from "../../src/project/instance" import { Server } from "../../src/server/server" @@ -38,6 +39,7 @@ describe("session action routes", () => { test("create route accepts directory query param", async () => { await using tmp = await tmpdir({ git: true }) const dir = `${tmp.path}/custom-worktree` + await mkdir(dir, { recursive: true }) await Instance.provide({ directory: tmp.path, fn: async () => { From e1bf330418289d272a1fbe2fcb1466ea642f8fa8 Mon Sep 17 00:00:00 2001 From: Evgeny Zotov Date: Mon, 6 Apr 2026 01:10:26 +0200 Subject: [PATCH 3/3] test(session): compare canonicalized directory in route test --- packages/opencode/test/server/session-actions.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/opencode/test/server/session-actions.test.ts b/packages/opencode/test/server/session-actions.test.ts index be0eb9f151e6..596d256a13cd 100644 --- a/packages/opencode/test/server/session-actions.test.ts +++ b/packages/opencode/test/server/session-actions.test.ts @@ -6,6 +6,7 @@ import { Session } from "../../src/session" import { ModelID, ProviderID } from "../../src/provider/schema" import { MessageID, PartID, type SessionID } from "../../src/session/schema" import { SessionPrompt } from "../../src/session/prompt" +import { Filesystem } from "../../src/util/filesystem" import { Log } from "../../src/util/log" import { tmpdir } from "../fixture/fixture" @@ -53,7 +54,7 @@ describe("session action routes", () => { expect(res.status).toBe(200) const session = (await res.json()) as Session.Info - expect(session.directory).toBe(dir) + expect(session.directory).toBe(Filesystem.resolve(dir)) await Session.remove(session.id) },