summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorJames Long <[email protected]>2026-04-28 16:49:13 -0400
committerGitHub <[email protected]>2026-04-28 16:49:13 -0400
commit9209c04370c11c9afc8138f01576ecdc0afbe695 (patch)
treef2dc6474c9c92f9ac84a045ee25fb970c6e35613 /packages
parent379e7f3f20956cdc609b466c046d7a3f11251eb0 (diff)
downloadopencode-9209c04370c11c9afc8138f01576ecdc0afbe695.tar.gz
opencode-9209c04370c11c9afc8138f01576ecdc0afbe695.zip
feat(core): filter sessions by path and add setting to disable (#24849)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/cli/cmd/tui/app.tsx12
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx13
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/sync.tsx34
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/session.ts4
-rw-r--r--packages/opencode/src/server/routes/instance/session.ts9
-rw-r--r--packages/opencode/src/session/session.ts15
-rw-r--r--packages/opencode/test/cli/cmd/tui/sync.test.tsx149
-rw-r--r--packages/opencode/test/server/session-list.test.ts139
-rw-r--r--packages/sdk/js/src/v2/gen/sdk.gen.ts4
-rw-r--r--packages/sdk/js/src/v2/gen/types.gen.ts8
10 files changed, 360 insertions, 27 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx
index 833c8dc8c..703da1b59 100644
--- a/packages/opencode/src/cli/cmd/tui/app.tsx
+++ b/packages/opencode/src/cli/cmd/tui/app.tsx
@@ -737,6 +737,18 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
},
},
{
+ title: kv.get("session_directory_filter_enabled", true)
+ ? "Disable session directory filtering"
+ : "Enable session directory filtering",
+ value: "app.toggle.session_directory_filter",
+ category: "System",
+ onSelect: async (dialog) => {
+ kv.set("session_directory_filter_enabled", !kv.get("session_directory_filter_enabled", true))
+ await sync.session.refresh()
+ dialog.clear()
+ },
+ },
+ {
title: kv.get("diff_wrap_mode", "word") === "word" ? "Disable diff wrapping" : "Enable diff wrapping",
value: "app.toggle.diffwrap",
category: "System",
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
index 576098178..72d60767b 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
@@ -32,11 +32,14 @@ export function DialogSessionList() {
const [toDelete, setToDelete] = createSignal<string>()
const [search, setSearch] = createDebouncedSignal("", 150)
- const [searchResults, { refetch }] = createResource(search, async (query) => {
- if (!query) return undefined
- const result = await sdk.client.session.list({ search: query, limit: 30 })
- return result.data ?? []
- })
+ const [searchResults, { refetch }] = createResource(
+ () => ({ query: search(), filter: sync.session.query() }),
+ async (input) => {
+ if (!input.query) return undefined
+ const result = await sdk.client.session.list({ search: input.query, limit: 30, ...input.filter })
+ return result.data ?? []
+ },
+ )
const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
const sessions = createMemo(() => searchResults() ?? sync.data.session)
diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx
index 7b18d7f4e..24609dd81 100644
--- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx
@@ -30,6 +30,8 @@ import { useArgs } from "./args"
import { batch, onMount } from "solid-js"
import * as Log from "@opencode-ai/core/util/log"
import { emptyConsoleState, type ConsoleState } from "@/config/console-state"
+import path from "path"
+import { useKV } from "./kv"
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
name: "Sync",
@@ -107,10 +109,27 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const event = useEvent()
const project = useProject()
const sdk = useSDK()
+ const kv = useKV()
const fullSyncedSessions = new Set<string>()
let syncedWorkspace = project.workspace.current()
+ function sessionListQuery(): { scope?: "project"; path?: string } {
+ if (!kv.get("session_directory_filter_enabled", true)) return { scope: "project" }
+ if (!project.data.instance.path.worktree || !project.data.instance.path.directory) return { scope: "project" }
+ return {
+ path: path
+ .relative(path.resolve(project.data.instance.path.worktree), project.data.instance.path.directory)
+ .replaceAll("\\", "/"),
+ }
+ }
+
+ function listSessions() {
+ return sdk.client.session
+ .list({ start: Date.now() - 30 * 24 * 60 * 60 * 1000, ...sessionListQuery() })
+ .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)))
+ }
+
event.subscribe((event) => {
switch (event.type) {
case "server.instance.disposed":
@@ -360,10 +379,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
fullSyncedSessions.clear()
syncedWorkspace = workspace
}
- const start = Date.now() - 30 * 24 * 60 * 60 * 1000
- const sessionListPromise = sdk.client.session
- .list({ start: start })
- .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)))
+ const projectPromise = project.sync()
+ const sessionListPromise = projectPromise.then(() => listSessions())
// blocking - include session.list when continuing a session
const providersPromise = sdk.client.config.providers({ workspace }, { throwOnError: true })
@@ -374,7 +391,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
.catch(() => emptyConsoleState)
const agentsPromise = sdk.client.app.agents({ workspace }, { throwOnError: true })
const configPromise = sdk.client.config.get({ workspace }, { throwOnError: true })
- const projectPromise = project.sync()
const blockingRequests: Promise<unknown>[] = [
providersPromise,
providerListPromise,
@@ -479,11 +495,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
if (match.found) return store.session[match.index]
return undefined
},
+ query() {
+ return sessionListQuery()
+ },
async refresh() {
- const start = Date.now() - 30 * 24 * 60 * 60 * 1000
- const list = await sdk.client.session
- .list({ start })
- .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)))
+ const list = await listSessions()
setStore("session", reconcile(list))
},
status(sessionID: string) {
diff --git a/packages/opencode/src/server/routes/instance/httpapi/session.ts b/packages/opencode/src/server/routes/instance/httpapi/session.ts
index 9001ae49d..6ea19f19e 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/session.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/session.ts
@@ -45,6 +45,8 @@ const QueryBoolean = Schema.Literals(["true", "false"]).pipe(
)
const ListQuery = Schema.Struct({
directory: Schema.optional(Schema.String),
+ scope: Schema.optional(Schema.Literals(["project"])),
+ path: Schema.optional(Schema.String),
roots: Schema.optional(QueryBoolean),
start: Schema.optional(Schema.NumberFromString),
search: Schema.optional(Schema.String),
@@ -444,6 +446,8 @@ export const sessionHandlers = HttpApiBuilder.group(SessionApi, "session", (hand
Array.from(
Session.list({
directory: ctx.query.directory,
+ scope: ctx.query.scope,
+ path: ctx.query.path,
roots: ctx.query.roots,
start: ctx.query.start,
search: ctx.query.search,
diff --git a/packages/opencode/src/server/routes/instance/session.ts b/packages/opencode/src/server/routes/instance/session.ts
index 8a7752e34..410d8bba0 100644
--- a/packages/opencode/src/server/routes/instance/session.ts
+++ b/packages/opencode/src/server/routes/instance/session.ts
@@ -62,7 +62,11 @@ export const SessionRoutes = lazy(() =>
validator(
"query",
z.object({
- directory: z.string().optional().meta({ description: "Filter sessions by project directory" }),
+ directory: z.string().optional().meta({ description: "Filter sessions by directory" }),
+ // TODO: in 2.0 remove `scope` and `directory` and default
+ // to list all sessions for a project
+ scope: z.enum(["project"]).optional().meta({ description: "List all sessions for the current project" }),
+ path: z.string().optional().meta({ description: "Filter sessions by project-relative path" }),
roots: QueryBoolean.optional().meta({ description: "Only return root sessions (no parentID)" }),
start: z.coerce
.number()
@@ -76,7 +80,8 @@ export const SessionRoutes = lazy(() =>
const query = c.req.valid("query")
const sessions: Session.Info[] = []
for await (const session of Session.list({
- directory: query.directory,
+ directory: query.scope === "project" ? undefined : query.directory,
+ path: query.path,
roots: queryBoolean(query.roots),
start: query.start,
search: query.search,
diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts
index f5b6279bd..45b8f0078 100644
--- a/packages/opencode/src/session/session.ts
+++ b/packages/opencode/src/session/session.ts
@@ -18,6 +18,7 @@ import { desc } from "drizzle-orm"
import { like } from "drizzle-orm"
import { inArray } from "drizzle-orm"
import { lt } from "drizzle-orm"
+import { or } from "drizzle-orm"
import { SyncEvent } from "../sync"
import type { SQL } from "drizzle-orm"
import { PartTable, SessionTable } from "./session.sql"
@@ -759,6 +760,8 @@ export const defaultLayer = layer.pipe(Layer.provide(Bus.layer), Layer.provide(S
export function* list(input?: {
directory?: string
+ scope?: "project"
+ path?: string
workspaceID?: WorkspaceID
roots?: boolean
start?: number
@@ -771,7 +774,17 @@ export function* list(input?: {
if (input?.workspaceID) {
conditions.push(eq(SessionTable.workspace_id, input.workspaceID))
}
- if (!Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
+ if (input?.path !== undefined) {
+ if (input.path) {
+ const conds = [eq(SessionTable.path, input.path), like(SessionTable.path, `${input.path}/%`)]
+
+ conditions.push(
+ input.directory
+ ? or(...conds, and(isNull(SessionTable.path), eq(SessionTable.directory, input.directory))!)!
+ : or(...conds)!,
+ )
+ }
+ } else if (input?.scope !== "project" && !Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
if (input?.directory) {
conditions.push(eq(SessionTable.directory, input.directory))
}
diff --git a/packages/opencode/test/cli/cmd/tui/sync.test.tsx b/packages/opencode/test/cli/cmd/tui/sync.test.tsx
new file mode 100644
index 000000000..993484d3c
--- /dev/null
+++ b/packages/opencode/test/cli/cmd/tui/sync.test.tsx
@@ -0,0 +1,149 @@
+/** @jsxImportSource @opentui/solid */
+import { describe, expect, test } from "bun:test"
+import { testRender } from "@opentui/solid"
+import { onMount } from "solid-js"
+import { Global } from "@opencode-ai/core/global"
+import { ArgsProvider } from "../../../../src/cli/cmd/tui/context/args"
+import { ExitProvider } from "../../../../src/cli/cmd/tui/context/exit"
+import { KVProvider, useKV } from "../../../../src/cli/cmd/tui/context/kv"
+import { ProjectProvider } from "../../../../src/cli/cmd/tui/context/project"
+import { SDKProvider, type EventSource } from "../../../../src/cli/cmd/tui/context/sdk"
+import { SyncProvider, useSync } from "../../../../src/cli/cmd/tui/context/sync"
+import { tmpdir } from "../../../fixture/fixture"
+
+const worktree = "/tmp/opencode"
+const directory = `${worktree}/packages/opencode`
+
+async function wait(fn: () => boolean, timeout = 2000) {
+ const start = Date.now()
+ while (!fn()) {
+ if (Date.now() - start > timeout) throw new Error("timed out waiting for condition")
+ await Bun.sleep(10)
+ }
+}
+
+function json(data: unknown) {
+ return new Response(JSON.stringify(data), {
+ headers: { "content-type": "application/json" },
+ })
+}
+
+function eventSource(): EventSource {
+ return {
+ subscribe: async () => () => {},
+ }
+}
+
+function createFetch() {
+ const session = [] as URL[]
+ const fetch = (async (input: RequestInfo | URL) => {
+ const url = new URL(input instanceof Request ? input.url : String(input))
+ if (url.pathname === "/session") session.push(url)
+
+ switch (url.pathname) {
+ case "/agent":
+ case "/command":
+ case "/experimental/workspace":
+ case "/experimental/workspace/status":
+ case "/formatter":
+ case "/lsp":
+ return json([])
+ case "/config":
+ case "/experimental/resource":
+ case "/mcp":
+ case "/provider/auth":
+ case "/session/status":
+ return json({})
+ case "/config/providers":
+ return json({ providers: {}, default: {} })
+ case "/experimental/console":
+ return json({ consoleManagedProviders: [], switchableOrgCount: 0 })
+ case "/path":
+ return json({ home: "", state: "", config: "", worktree, directory })
+ case "/project/current":
+ return json({ id: "proj_test" })
+ case "/provider":
+ return json({ all: [], default: {}, connected: [] })
+ case "/session":
+ return json([])
+ case "/vcs":
+ return json({ branch: "main" })
+ }
+
+ throw new Error(`unexpected request: ${url.pathname}`)
+ }) as typeof globalThis.fetch
+
+ return { fetch, session }
+}
+
+async function mount() {
+ const calls = createFetch()
+ let sync!: ReturnType<typeof useSync>
+ let kv!: ReturnType<typeof useKV>
+ let done!: () => void
+ const ready = new Promise<void>((resolve) => {
+ done = resolve
+ })
+
+ const app = await testRender(() => (
+ <ArgsProvider>
+ <ExitProvider>
+ <KVProvider>
+ <SDKProvider url="http://test" directory={directory} fetch={calls.fetch} events={eventSource()}>
+ <ProjectProvider>
+ <SyncProvider>
+ <Probe
+ onReady={(ctx) => {
+ sync = ctx.sync
+ kv = ctx.kv
+ done()
+ }}
+ />
+ </SyncProvider>
+ </ProjectProvider>
+ </SDKProvider>
+ </KVProvider>
+ </ExitProvider>
+ </ArgsProvider>
+ ))
+
+ await ready
+ await wait(() => sync.status === "complete")
+ return { app, kv, sync, session: calls.session }
+}
+
+function Probe(props: { onReady: (ctx: { kv: ReturnType<typeof useKV>; sync: ReturnType<typeof useSync> }) => void }) {
+ const kv = useKV()
+ const sync = useSync()
+
+ onMount(() => {
+ props.onReady({ kv, sync })
+ })
+
+ return <box />
+}
+
+describe("tui sync", () => {
+ test("refresh scopes sessions by default and lists project sessions when disabled", async () => {
+ const previous = Global.Path.state
+ await using tmp = await tmpdir()
+ Global.Path.state = tmp.path
+ await Bun.write(`${tmp.path}/kv.json`, "{}")
+ const { app, kv, sync, session } = await mount()
+
+ try {
+ expect(kv.get("session_directory_filter_enabled", true)).toBe(true)
+ expect(session.at(-1)?.searchParams.get("scope")).toBeNull()
+ expect(session.at(-1)?.searchParams.get("path")).toBe("packages/opencode")
+
+ kv.set("session_directory_filter_enabled", false)
+ await sync.session.refresh()
+
+ expect(session.at(-1)?.searchParams.get("scope")).toBe("project")
+ expect(session.at(-1)?.searchParams.get("path")).toBeNull()
+ } finally {
+ app.renderer.destroy()
+ Global.Path.state = previous
+ }
+ })
+})
diff --git a/packages/opencode/test/server/session-list.test.ts b/packages/opencode/test/server/session-list.test.ts
index 2e2945d07..cbdda6b42 100644
--- a/packages/opencode/test/server/session-list.test.ts
+++ b/packages/opencode/test/server/session-list.test.ts
@@ -4,8 +4,15 @@ import { Instance } from "../../src/project/instance"
import { Session as SessionNs } from "@/session/session"
import * as Log from "@opencode-ai/core/util/log"
import { tmpdir } from "../fixture/fixture"
+import { Flag } from "@opencode-ai/core/flag/flag"
+import { mkdir } from "fs/promises"
+import path from "path"
+import { Database } from "@/storage/db"
+import { SessionTable } from "@/session/session.sql"
+import { eq } from "drizzle-orm"
void Log.init({ print: false })
+const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
@@ -19,28 +26,140 @@ const svc = {
}
afterEach(async () => {
+ Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces
await Instance.disposeAll()
})
describe("session.list", () => {
- test("filters by directory", async () => {
+ test("does not filter by directory when directory is omitted", async () => {
+ Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
await using tmp = await tmpdir({ git: true })
+ await mkdir(path.join(tmp.path, "packages", "opencode"), { recursive: true })
+ await mkdir(path.join(tmp.path, "packages", "app"), { recursive: true })
+
await Instance.provide({
directory: tmp.path,
fn: async () => {
- const first = await svc.create({})
+ const root = await svc.create({ title: "root" })
- await using other = await tmpdir({ git: true })
- const second = await Instance.provide({
- directory: other.path,
- fn: async () => svc.create({}),
+ const parent = await Instance.provide({
+ directory: path.join(tmp.path, "packages"),
+ fn: async () => svc.create({ title: "parent" }),
+ })
+ const current = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "opencode"),
+ fn: async () => svc.create({ title: "current" }),
+ })
+ const sibling = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "app"),
+ fn: async () => svc.create({ title: "sibling" }),
})
- const sessions = [...svc.list({ directory: tmp.path })]
- const ids = sessions.map((s) => s.id)
+ const ids = [...svc.list()].map((s) => s.id)
+ expect(ids).toContain(root.id)
+ expect(ids).toContain(parent.id)
+ expect(ids).toContain(current.id)
+ expect(ids).toContain(sibling.id)
+ },
+ })
+ })
+
+ test("filters by directory when directory is provided", async () => {
+ Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
+ await using tmp = await tmpdir({ git: true })
+ await mkdir(path.join(tmp.path, "packages", "opencode"), { recursive: true })
+ await mkdir(path.join(tmp.path, "packages", "app"), { recursive: true })
+
+ await Instance.provide({
+ directory: tmp.path,
+ fn: async () => {
+ const root = await svc.create({ title: "root" })
+
+ const parent = await Instance.provide({
+ directory: path.join(tmp.path, "packages"),
+ fn: async () => svc.create({ title: "parent" }),
+ })
+ const current = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "opencode"),
+ fn: async () => svc.create({ title: "current" }),
+ })
+ const sibling = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "app"),
+ fn: async () => svc.create({ title: "sibling" }),
+ })
+
+ const ids = [...svc.list({ directory: path.join(tmp.path, "packages", "opencode") })].map((s) => s.id)
+ expect(ids).not.toContain(root.id)
+ expect(ids).not.toContain(parent.id)
+ expect(ids).toContain(current.id)
+ expect(ids).not.toContain(sibling.id)
+ },
+ })
+ })
+
+ test("filters by path and ignores directory when path is provided", async () => {
+ Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
+ await using tmp = await tmpdir({ git: true })
+ await mkdir(path.join(tmp.path, "packages", "opencode", "src", "deep"), { recursive: true })
+ await mkdir(path.join(tmp.path, "packages", "app"), { recursive: true })
+
+ await Instance.provide({
+ directory: tmp.path,
+ fn: async () => {
+ const parent = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "opencode"),
+ fn: async () => svc.create({ title: "parent" }),
+ })
+ const current = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "opencode", "src"),
+ fn: async () => svc.create({ title: "current" }),
+ })
+ const deeper = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "opencode", "src", "deep"),
+ fn: async () => svc.create({ title: "deeper" }),
+ })
+ const sibling = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "app"),
+ fn: async () => svc.create({ title: "sibling" }),
+ })
+
+ const pathIDs = [
+ ...svc.list({ directory: path.join(tmp.path, "packages", "app"), path: "packages/opencode/src" }),
+ ].map((s) => s.id)
+ expect(pathIDs).not.toContain(parent.id)
+ expect(pathIDs).toContain(current.id)
+ expect(pathIDs).toContain(deeper.id)
+ expect(pathIDs).not.toContain(sibling.id)
+ },
+ })
+ })
+
+ test("falls back to directory when filtering legacy sessions without path", async () => {
+ Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
+ await using tmp = await tmpdir({ git: true })
+ await mkdir(path.join(tmp.path, "packages", "opencode", "src"), { recursive: true })
+ await mkdir(path.join(tmp.path, "packages", "app"), { recursive: true })
+
+ await Instance.provide({
+ directory: tmp.path,
+ fn: async () => {
+ const current = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "opencode", "src"),
+ fn: async () => svc.create({ title: "legacy-current" }),
+ })
+ const sibling = await Instance.provide({
+ directory: path.join(tmp.path, "packages", "app"),
+ fn: async () => svc.create({ title: "legacy-sibling" }),
+ })
+
+ Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, current.id)).run())
+ Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sibling.id)).run())
- expect(ids).toContain(first.id)
- expect(ids).not.toContain(second.id)
+ const pathIDs = [
+ ...svc.list({ directory: path.join(tmp.path, "packages", "opencode", "src"), path: "packages/opencode/src" }),
+ ].map((s) => s.id)
+ expect(pathIDs).toContain(current.id)
+ expect(pathIDs).not.toContain(sibling.id)
},
})
})
diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts
index 1dafe88d1..2da7c865d 100644
--- a/packages/sdk/js/src/v2/gen/sdk.gen.ts
+++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts
@@ -1647,6 +1647,8 @@ export class Session2 extends HeyApiClient {
parameters?: {
directory?: string
workspace?: string
+ scope?: "project"
+ path?: string
roots?: boolean | "true" | "false"
start?: number
search?: string
@@ -1661,6 +1663,8 @@ export class Session2 extends HeyApiClient {
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
+ { in: "query", key: "scope" },
+ { in: "query", key: "path" },
{ in: "query", key: "roots" },
{ in: "query", key: "start" },
{ in: "query", key: "search" },
diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts
index ed9954aec..03742e039 100644
--- a/packages/sdk/js/src/v2/gen/types.gen.ts
+++ b/packages/sdk/js/src/v2/gen/types.gen.ts
@@ -3290,6 +3290,14 @@ export type SessionListData = {
directory?: string
workspace?: string
/**
+ * List all sessions for the current project
+ */
+ scope?: "project"
+ /**
+ * Filter sessions by project-relative path
+ */
+ path?: string
+ /**
* Only return root sessions (no parentID)
*/
roots?: boolean | "true" | "false"