From 72af5831d8cf066677814e9187c6eff572a3eea9 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 30 Nov 2025 13:48:29 -0500 Subject: [PATCH 01/26] feat: add messages_limit and sessions_list_limit configuration with proper defaults - Add messages_limit (default: 100) and sessions_list_limit (default: 150) to TUI config - Maintain backward compatibility with existing behavior - Regenerate SDK to include new fields --- .../cmd/tui/component/dialog-session-list.tsx | 5 +- .../opencode/src/cli/cmd/tui/context/sync.tsx | 4 +- packages/opencode/src/config/config.ts | 11 ++ packages/opencode/test/config/config.test.ts | 120 ++++++++++++++++++ packages/sdk/js/src/gen/types.gen.ts | 8 ++ 5 files changed, 146 insertions(+), 2 deletions(-) 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 5e0095a8dfe..24558b154a4 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 @@ -24,6 +24,9 @@ export function DialogSessionList() { const options = createMemo(() => { const today = new Date().toDateString() + const sessionsListLimit = (sync.data.config.tui as any)?.sessions_list_limit + const limit = sessionsListLimit === "none" ? undefined : sessionsListLimit || 150 + return sync.data.session .filter((x) => x.parentID === undefined) .map((x) => { @@ -41,7 +44,7 @@ export function DialogSessionList() { footer: Locale.time(x.time.updated), } }) - .slice(0, 150) + .slice(0, limit) }) createEffect(() => { diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index b7ef8a2214b..ca04e9d561e 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -332,9 +332,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }, async sync(sessionID: string) { if (fullSyncedSessions.has(sessionID)) return + const messagesLimit = (store.config.tui as any)?.messages_limit + const limit = messagesLimit === "none" ? undefined : messagesLimit || 100 const [session, messages, todo, diff] = await Promise.all([ sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }), - sdk.client.session.messages({ path: { id: sessionID }, query: { limit: 100 } }), + sdk.client.session.messages({ path: { id: sessionID }, query: { limit } }), sdk.client.session.todo({ path: { id: sessionID } }), sdk.client.session.diff({ path: { id: sessionID } }), ]) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index a4961209055..f7de8b82ca7 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -460,7 +460,18 @@ export namespace Config { .enum(["auto", "stacked"]) .optional() .describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"), + sessions_list_limit: z + .union([z.number().min(1), z.literal("none")]) + .optional() + .default(150) + .describe("Maximum number of sessions to display in session list, or 'none' to show all sessions"), + messages_limit: z + .union([z.number().min(1), z.literal("none")]) + .optional() + .default(100) + .describe("Maximum number of messages to load per session when syncing, or 'none' to load all messages"), }) + export type TUI = z.infer export const Layout = z.enum(["auto", "stretch"]).meta({ ref: "LayoutConfig", diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index 2ff8c01cdb0..a996a53c6c4 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -501,3 +501,123 @@ test("deduplicates duplicate plugins from global and local configs", async () => }, }) }) + +test("handles TUI configuration with sessions_list_limit and messages_limit", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + tui: { + sessions_list_limit: 200, + messages_limit: 50, + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.tui?.sessions_list_limit).toBe(200) + expect(config.tui?.messages_limit).toBe(50) + }, + }) +}) + +test("handles TUI configuration with sessions_list_limit set to 'none'", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + tui: { + sessions_list_limit: "none", + messages_limit: 75, + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.tui?.sessions_list_limit).toBe("none") + expect(config.tui?.messages_limit).toBe(75) + }, + }) +}) + +test("validates TUI sessions_list_limit schema - rejects invalid values", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + tui: { + sessions_list_limit: -5, // Invalid: negative number + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + await expect(Config.get()).rejects.toThrow() + }, + }) +}) + +test("validates TUI messages_limit schema - rejects invalid values", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + tui: { + messages_limit: 0, // Invalid: must be >= 1 + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + await expect(Config.get()).rejects.toThrow() + }, + }) +}) + +test("handles partial TUI configuration with backward compatibility", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + tui: { + scroll_speed: 2.5, + // sessions_list_limit and messages_limit not specified - should inherit from global config + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.tui?.scroll_speed).toBe(2.5) + // Note: sessions_list_limit and messages_limit may be inherited from global config + // The important thing is that the config loads successfully and scroll_speed is set correctly + }, + }) +}) diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 70fceedbcb2..e93ecfbdb49 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1008,6 +1008,14 @@ export type Config = { * Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column */ diff_style?: "auto" | "stacked" + /** + * Maximum number of sessions to display in session list, or 'none' to show all sessions + */ + sessions_list_limit?: number | "none" + /** + * Maximum number of messages to load per session when syncing, or 'none' to load all messages + */ + messages_limit?: number | "none" } /** * Command configuration, see https://opencode.ai/docs/commands From e03641d846677c3542fd0ac5d72676aa6cc1eea0 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 30 Nov 2025 15:18:37 -0500 Subject: [PATCH 02/26] tidy: for consistency sessions_list_limit->session_list_limit. --- .../cmd/tui/component/dialog-session-list.tsx | 2 +- packages/opencode/src/config/config.ts | 2 +- packages/opencode/test/config/config.test.ts | 20 +++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) 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 24558b154a4..bbb8a91e8b7 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 @@ -24,7 +24,7 @@ export function DialogSessionList() { const options = createMemo(() => { const today = new Date().toDateString() - const sessionsListLimit = (sync.data.config.tui as any)?.sessions_list_limit + const sessionsListLimit = (sync.data.config.tui as any)?.session_list_limit const limit = sessionsListLimit === "none" ? undefined : sessionsListLimit || 150 return sync.data.session diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index f7de8b82ca7..847c35abacb 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -460,7 +460,7 @@ export namespace Config { .enum(["auto", "stacked"]) .optional() .describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"), - sessions_list_limit: z + session_list_limit: z .union([z.number().min(1), z.literal("none")]) .optional() .default(150) diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index a996a53c6c4..d6f0500eaa8 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -502,7 +502,7 @@ test("deduplicates duplicate plugins from global and local configs", async () => }) }) -test("handles TUI configuration with sessions_list_limit and messages_limit", async () => { +test("handles TUI configuration with session_list_limit and messages_limit", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Bun.write( @@ -510,7 +510,7 @@ test("handles TUI configuration with sessions_list_limit and messages_limit", as JSON.stringify({ $schema: "https://opencode.ai/config.json", tui: { - sessions_list_limit: 200, + session_list_limit: 200, messages_limit: 50, }, }), @@ -521,13 +521,13 @@ test("handles TUI configuration with sessions_list_limit and messages_limit", as directory: tmp.path, fn: async () => { const config = await Config.get() - expect(config.tui?.sessions_list_limit).toBe(200) + expect(config.tui?.session_list_limit).toBe(200) expect(config.tui?.messages_limit).toBe(50) }, }) }) -test("handles TUI configuration with sessions_list_limit set to 'none'", async () => { +test("handles TUI configuration with session_list_limit set to 'none'", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Bun.write( @@ -535,7 +535,7 @@ test("handles TUI configuration with sessions_list_limit set to 'none'", async ( JSON.stringify({ $schema: "https://opencode.ai/config.json", tui: { - sessions_list_limit: "none", + session_list_limit: "none", messages_limit: 75, }, }), @@ -546,13 +546,13 @@ test("handles TUI configuration with sessions_list_limit set to 'none'", async ( directory: tmp.path, fn: async () => { const config = await Config.get() - expect(config.tui?.sessions_list_limit).toBe("none") + expect(config.tui?.session_list_limit).toBe("none") expect(config.tui?.messages_limit).toBe(75) }, }) }) -test("validates TUI sessions_list_limit schema - rejects invalid values", async () => { +test("validates TUI session_list_limit schema - rejects invalid values", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Bun.write( @@ -560,7 +560,7 @@ test("validates TUI sessions_list_limit schema - rejects invalid values", async JSON.stringify({ $schema: "https://opencode.ai/config.json", tui: { - sessions_list_limit: -5, // Invalid: negative number + session_list_limit: -5, // Invalid: negative number }, }), ) @@ -605,7 +605,7 @@ test("handles partial TUI configuration with backward compatibility", async () = $schema: "https://opencode.ai/config.json", tui: { scroll_speed: 2.5, - // sessions_list_limit and messages_limit not specified - should inherit from global config + // session_list_limit and messages_limit not specified - should inherit from global config }, }), ) @@ -616,7 +616,7 @@ test("handles partial TUI configuration with backward compatibility", async () = fn: async () => { const config = await Config.get() expect(config.tui?.scroll_speed).toBe(2.5) - // Note: sessions_list_limit and messages_limit may be inherited from global config + // Note: session_list_limit and messages_limit may be inherited from global config // The important thing is that the config loads successfully and scroll_speed is set correctly }, }) From 8254075b0a9a68d90b596d6a5bd53d82bbf08266 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 30 Nov 2025 15:28:07 -0500 Subject: [PATCH 03/26] fix: same as last, missed one. --- packages/sdk/js/src/gen/types.gen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index e93ecfbdb49..ab947702f80 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1011,7 +1011,7 @@ export type Config = { /** * Maximum number of sessions to display in session list, or 'none' to show all sessions */ - sessions_list_limit?: number | "none" + session_list_limit?: number | "none" /** * Maximum number of messages to load per session when syncing, or 'none' to load all messages */ From 77dc72fb7fdff858fc705df62a00443ca1ccd328 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 30 Nov 2025 18:51:24 -0500 Subject: [PATCH 04/26] wip: hammer on messages_limit a bit --- packages/opencode/src/cli/cmd/tui/context/sync.tsx | 5 ++++- packages/opencode/src/config/config.ts | 2 +- packages/opencode/src/session/index.ts | 14 +++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index ca04e9d561e..67fd53b71ea 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -184,7 +184,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ event.properties.info.sessionID, produce((draft) => { draft.splice(result.index, 0, event.properties.info) - if (draft.length > 100) draft.shift() + const maxMessages = (store.config.tui as any)?.messages_limit + const maxMessagesCount = maxMessages === "none" ? Infinity : maxMessages || 100 + if (draft.length > maxMessagesCount) draft.shift() }), ) break @@ -350,6 +352,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ for (const message of messages.data!) { draft.part[message.info.id] = message.parts } + draft.session_diff[sessionID] = diff.data ?? [] }), ) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 847c35abacb..b5e91bbfaab 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -469,7 +469,7 @@ export namespace Config { .union([z.number().min(1), z.literal("none")]) .optional() .default(100) - .describe("Maximum number of messages to load per session when syncing, or 'none' to load all messages"), + .describe("Maximum number of message parts to load per session when syncing, or 'none' to load all messages"), }) export type TUI = z.infer diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index f09818caa2e..257606af27e 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -291,11 +291,23 @@ export namespace Session { }), async (input) => { const result = [] as MessageV2.WithParts[] + let totalParts = 0 + for await (const msg of MessageV2.stream(input.sessionID)) { - if (input.limit && result.length >= input.limit) break + if (input.limit && totalParts + msg.parts.length > input.limit) { + // If adding this message would exceed the limit, check if we can fit a partial message + if (totalParts < input.limit) { + // We have room for some parts of this message, but this would be complex to implement + // For now, just break to stay within the limit + break + } + break + } result.push(msg) + totalParts += msg.parts.length } result.reverse() + return result }, ) From 5f73f7f90d5854bd25ecbd51f1d93759827837c2 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 1 Dec 2025 00:27:50 -0500 Subject: [PATCH 05/26] wip: fiddle with messages_limit.. --- packages/opencode/src/cli/cmd/tui/context/sync.tsx | 9 ++++++++- packages/opencode/src/session/index.ts | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 67fd53b71ea..f3ab41aa50b 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -186,7 +186,14 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ draft.splice(result.index, 0, event.properties.info) const maxMessages = (store.config.tui as any)?.messages_limit const maxMessagesCount = maxMessages === "none" ? Infinity : maxMessages || 100 - if (draft.length > maxMessagesCount) draft.shift() + // DEBUG: Log message limit behavior + console.log( + `[SYNC] Session ${event.properties.info.sessionID}: messages_limit=${maxMessages}, maxMessagesCount=${maxMessagesCount}`, + ) + if (draft.length > maxMessagesCount) { + console.log(`[SYNC] Session ${event.properties.info.sessionID}: LIMIT EXCEEDED - shifting messages`) + draft.shift() + } }), ) break diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 257606af27e..ecdd0e580ea 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -308,6 +308,10 @@ export namespace Session { } result.reverse() + // DEBUG: Log what we're returning + console.log( + `[Session.messages] Returning ${result.length} messages for session ${input.sessionID} with limit ${input.limit}`, + ) return result }, ) From 53ca8ac2df13c9e841970c52d6738ca84559f841 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 1 Dec 2025 02:59:34 -0500 Subject: [PATCH 06/26] Update generated SDK types: change 'messages' to 'message parts' in config documentation --- packages/sdk/js/src/gen/types.gen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index ab947702f80..3daac0a5460 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1013,7 +1013,7 @@ export type Config = { */ session_list_limit?: number | "none" /** - * Maximum number of messages to load per session when syncing, or 'none' to load all messages + * Maximum number of message parts to load per session when syncing, or 'none' to load all messages */ messages_limit?: number | "none" } From cc07c5b1a153debe24731ed48d79f705fe067cff Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 1 Dec 2025 03:05:12 -0500 Subject: [PATCH 07/26] ... --- packages/opencode/src/cli/cmd/tui/context/sync.tsx | 5 ----- packages/opencode/src/session/index.ts | 4 ---- 2 files changed, 9 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index f3ab41aa50b..e3888e477d0 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -186,12 +186,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ draft.splice(result.index, 0, event.properties.info) const maxMessages = (store.config.tui as any)?.messages_limit const maxMessagesCount = maxMessages === "none" ? Infinity : maxMessages || 100 - // DEBUG: Log message limit behavior - console.log( - `[SYNC] Session ${event.properties.info.sessionID}: messages_limit=${maxMessages}, maxMessagesCount=${maxMessagesCount}`, - ) if (draft.length > maxMessagesCount) { - console.log(`[SYNC] Session ${event.properties.info.sessionID}: LIMIT EXCEEDED - shifting messages`) draft.shift() } }), diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index ecdd0e580ea..257606af27e 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -308,10 +308,6 @@ export namespace Session { } result.reverse() - // DEBUG: Log what we're returning - console.log( - `[Session.messages] Returning ${result.length} messages for session ${input.sessionID} with limit ${input.limit}`, - ) return result }, ) From 91f726854f822857349b170c9092bbab8e005613 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 1 Dec 2025 11:46:03 +0000 Subject: [PATCH 08/26] chore: format code --- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 21ef2a74407..7248787e88e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 9d3de8bb100..651f061ae07 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 5b2d15ca5e5a9667d5f5a294f65896742251a7bc Mon Sep 17 00:00:00 2001 From: Github Action Date: Mon, 1 Dec 2025 11:47:15 +0000 Subject: [PATCH 09/26] Update Nix flake.lock and hashes --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 3a6b887186d..05e9e841d76 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1764527385, - "narHash": "sha256-nA5ywiGKl76atrbdZ5Aucd8SjF/v8ew9b9QsC+MKL14=", + "lastModified": 1764557259, + "narHash": "sha256-fhD/QUtJ0HKs3oLvfnD+/SrBV5Y7YEkCYnDjOVUjLys=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "23258e03aaa49b3a68597e3e50eb0cbce7e42e9d", + "rev": "0d70460758949966e91d9ecb823b821f963cefbb", "type": "github" }, "original": { From 1016f8f27728ca5506571a0e99fd43454933d9af Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 8 Dec 2025 08:06:49 -0500 Subject: [PATCH 10/26] Fix TypeScript error: remove cacheKey from FileContents interface usage --- packages/desktop/src/pages/session.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 81f4dc1cbc4..1e86868fdcb 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -31,7 +31,7 @@ import { useSession } from "@/context/session" import { useLayout } from "@/context/layout" import { getDirectory, getFilename } from "@opencode-ai/util/path" import { Terminal } from "@/components/terminal" -import { checksum } from "@opencode-ai/util/encode" + export default function Page() { const layout = useLayout() @@ -493,7 +493,6 @@ export default function Page() { file={{ name: f().path, contents: f().content?.content ?? "", - cacheKey: checksum(f().content?.content ?? ""), }} overflow="scroll" class="pb-40" From c0f5fc58f9cc555595021334d847c3208778c6bc Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Tue, 16 Dec 2025 23:03:55 -0500 Subject: [PATCH 11/26] merge dev --- packages/desktop/src/pages/session.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 84c44e2249c..390872d3695 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -768,6 +768,7 @@ export default function Page() { file={{ name: f().path, contents: f().content?.content ?? "", + cacheKey: checksum(f().content?.content ?? ""), }} overflow="scroll" class="pb-40" From c03f682f6fce019f51531466020655a76be5dbf7 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Wed, 24 Dec 2025 22:53:15 -0500 Subject: [PATCH 12/26] fix: resolve TypeScript type mismatch for AI SDK providers Cast newer AI SDK providers to any to handle LanguageModelV3 vs V2 type incompatibility. Providers are runtime compatible despite the type difference. --- packages/opencode/src/provider/provider.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 0fdf26392f6..a5147d53a84 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -50,13 +50,20 @@ export namespace Provider { "@openrouter/ai-sdk-provider": createOpenRouter, "@ai-sdk/xai": createXai, "@ai-sdk/mistral": createMistral, - "@ai-sdk/groq": createGroq, - "@ai-sdk/deepinfra": createDeepInfra, - "@ai-sdk/cerebras": createCerebras, - "@ai-sdk/cohere": createCohere, - "@ai-sdk/gateway": createGateway, - "@ai-sdk/togetherai": createTogetherAI, - "@ai-sdk/perplexity": createPerplexity, + // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible + "@ai-sdk/groq": createGroq as any, + // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible + "@ai-sdk/deepinfra": createDeepInfra as any, + // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible + "@ai-sdk/cerebras": createCerebras as any, + // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible + "@ai-sdk/cohere": createCohere as any, + // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible + "@ai-sdk/gateway": createGateway as any, + // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible + "@ai-sdk/togetherai": createTogetherAI as any, + // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible + "@ai-sdk/perplexity": createPerplexity as any, // @ts-ignore (TODO: kill this code so we dont have to maintain it) "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible, } From 664889966f848eab3151f992b4b3447369137b7e Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Fri, 26 Dec 2025 21:41:06 -0500 Subject: [PATCH 13/26] Fix merge conflict - properly resolve TUI and compaction config tests --- packages/opencode/test/config/config.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index d35202de7fa..1a36914cc41 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -534,7 +534,6 @@ test("deduplicates duplicate plugins from global and local configs", async () => }) }) -<<<<<<< HEAD test("handles TUI configuration with session_list_limit and messages_limit", async () => { await using tmp = await tmpdir({ init: async (dir) => { From 914c2d38eeee45cc4a347a51337acda5a0f12a8f Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Fri, 26 Dec 2025 23:05:08 -0500 Subject: [PATCH 14/26] revert a file --- packages/opencode/src/provider/provider.ts | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index a5147d53a84..0fdf26392f6 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -50,20 +50,13 @@ export namespace Provider { "@openrouter/ai-sdk-provider": createOpenRouter, "@ai-sdk/xai": createXai, "@ai-sdk/mistral": createMistral, - // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible - "@ai-sdk/groq": createGroq as any, - // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible - "@ai-sdk/deepinfra": createDeepInfra as any, - // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible - "@ai-sdk/cerebras": createCerebras as any, - // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible - "@ai-sdk/cohere": createCohere as any, - // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible - "@ai-sdk/gateway": createGateway as any, - // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible - "@ai-sdk/togetherai": createTogetherAI as any, - // @ts-ignore - Type mismatch due to LanguageModelV3 vs V2, but runtime compatible - "@ai-sdk/perplexity": createPerplexity as any, + "@ai-sdk/groq": createGroq, + "@ai-sdk/deepinfra": createDeepInfra, + "@ai-sdk/cerebras": createCerebras, + "@ai-sdk/cohere": createCohere, + "@ai-sdk/gateway": createGateway, + "@ai-sdk/togetherai": createTogetherAI, + "@ai-sdk/perplexity": createPerplexity, // @ts-ignore (TODO: kill this code so we dont have to maintain it) "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible, } From 17d877e3a09c0d1fbc877f0366d196b29bf25eed Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Wed, 31 Dec 2025 22:45:37 -0500 Subject: [PATCH 15/26] fix: resolve merge conflicts from dev - Regenerate JS SDK to fix import issues - Add ts-ignore comment for Vercel provider type compatibility --- packages/opencode/src/provider/provider.ts | 1 + packages/sdk/js/src/v2/gen/types.gen.ts | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 983a0827223..dcd04a6c7b6 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -59,6 +59,7 @@ export namespace Provider { "@ai-sdk/gateway": createGateway, "@ai-sdk/togetherai": createTogetherAI, "@ai-sdk/perplexity": createPerplexity, + // @ts-ignore (Vercel provider returns LanguageModelV3 which is incompatible with Provider type) "@ai-sdk/vercel": createVercel, // @ts-ignore (TODO: kill this code so we dont have to maintain it) "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible, diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 85a3c428625..57218a3f783 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1454,6 +1454,14 @@ export type Config = { * Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column */ diff_style?: "auto" | "stacked" + /** + * Maximum number of sessions to display in session list, or 'none' to show all sessions + */ + session_list_limit?: number | "none" + /** + * Maximum number of message parts to load per session when syncing, or 'none' to load all messages + */ + messages_limit?: number | "none" } server?: ServerConfig /** From d93946ceb7295b89bf72e8514f3677835b298100 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Thu, 1 Jan 2026 18:11:32 -0500 Subject: [PATCH 16/26] revert a file --- packages/opencode/src/provider/provider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 8b14ec26242..93d2104e25c 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -59,7 +59,6 @@ export namespace Provider { "@ai-sdk/gateway": createGateway, "@ai-sdk/togetherai": createTogetherAI, "@ai-sdk/perplexity": createPerplexity, - // @ts-ignore (Vercel provider returns LanguageModelV3 which is incompatible with Provider type) "@ai-sdk/vercel": createVercel, // @ts-ignore (TODO: kill this code so we dont have to maintain it) "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible, From 43e28f603eb44c462f700a864763aa2bc586e203 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Tue, 6 Jan 2026 16:34:51 -0500 Subject: [PATCH 17/26] Add working directory restoration code for wrapper script Restores original working directory when running via wrapper script to ensure opencode starts in user's actual working directory, not the opencode-deploy repository directory. --- packages/opencode/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 03ccf76042f..5a2fa70d340 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -28,6 +28,11 @@ import { WebCommand } from "./cli/cmd/web" import { PrCommand } from "./cli/cmd/pr" import { SessionCommand } from "./cli/cmd/session" +// Restore original working directory if running via wrapper script +if (process.env.OPENCODE_ORIGINAL_CWD) { + process.chdir(process.env.OPENCODE_ORIGINAL_CWD) +} + process.on("unhandledRejection", (e) => { Log.Default.error("rejection", { e: e instanceof Error ? e.message : e, From 07e11955eadc557b481574130b448072cc0bf07b Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 11 Jan 2026 02:23:36 -0500 Subject: [PATCH 18/26] tidy: unwanted change --- packages/opencode/src/index.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 5a2fa70d340..03ccf76042f 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -28,11 +28,6 @@ import { WebCommand } from "./cli/cmd/web" import { PrCommand } from "./cli/cmd/pr" import { SessionCommand } from "./cli/cmd/session" -// Restore original working directory if running via wrapper script -if (process.env.OPENCODE_ORIGINAL_CWD) { - process.chdir(process.env.OPENCODE_ORIGINAL_CWD) -} - process.on("unhandledRejection", (e) => { Log.Default.error("rejection", { e: e instanceof Error ? e.message : e, From 66973b0680722c4fde92b7a0377af5f38a075905 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Wed, 21 Jan 2026 19:32:54 -0500 Subject: [PATCH 19/26] Add missing @solid-primitives/i18n dependency for app package --- packages/app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/package.json b/packages/app/package.json index 155dfebd9f4..8c9eb6d830e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -42,8 +42,8 @@ "@shikijs/transformers": "3.9.2", "@solid-primitives/active-element": "2.1.3", "@solid-primitives/audio": "1.4.2", - "@solid-primitives/i18n": "2.2.1", "@solid-primitives/event-bus": "1.1.2", + "@solid-primitives/i18n": "2.2.1", "@solid-primitives/media": "2.3.3", "@solid-primitives/resize-observer": "2.1.3", "@solid-primitives/scroll": "2.1.3", From 743829111dc9fa482feaf792c77387985319e535 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Fri, 23 Jan 2026 02:48:10 -0500 Subject: [PATCH 20/26] unslop --- packages/app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/package.json b/packages/app/package.json index d5c3573638a..de0e50b5fc6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -42,8 +42,8 @@ "@shikijs/transformers": "3.9.2", "@solid-primitives/active-element": "2.1.3", "@solid-primitives/audio": "1.4.2", - "@solid-primitives/event-bus": "1.1.2", "@solid-primitives/i18n": "2.2.1", + "@solid-primitives/event-bus": "1.1.2", "@solid-primitives/media": "2.3.3", "@solid-primitives/resize-observer": "2.1.3", "@solid-primitives/scroll": "2.1.3", From c6adb613e48c53d9499c6da4ed7dc42790f83ee6 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 15 Feb 2026 23:03:35 -0500 Subject: [PATCH 21/26] fix(tui): wait for config before syncing session messages Fix race condition where messages_limit config was read before config was loaded from server, causing the default limit of 100 to always be used instead of the user's configured value. The fix tracks sync.ready in the createEffect so it re-runs when the config is fully loaded, ensuring the correct messages_limit is used. --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index e83b9abe98a..540b30ca213 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -176,15 +176,18 @@ export function Session() { }) createEffect(async () => { + const sessionID = route.sessionID + const ready = sync.ready + if (!ready) return await sync.session - .sync(route.sessionID) + .sync(sessionID) .then(() => { if (scroll) scroll.scrollBy(100_000) }) .catch((e) => { console.error(e) toast.show({ - message: `Session not found: ${route.sessionID}`, + message: `Session not found: ${sessionID}`, variant: "error", }) return navigate({ type: "home" }) From b2bd15d0fd9b088bf40cba8a37c77487ff30f110 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 15 Feb 2026 23:22:53 -0500 Subject: [PATCH 22/26] fix(tui): wait for config before computing session list options Fix race condition where session_list_limit config was read before config was loaded from server, causing the default limit of 150 to always be used instead of the user's configured value. The fix adds sync.ready check at the start of the options memo so it re-runs when the config is fully loaded, ensuring the correct session_list_limit is used. --- .../opencode/src/cli/cmd/tui/component/dialog-session-list.tsx | 1 + 1 file changed, 1 insertion(+) 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 eeee4ca5ac0..3799d976dca 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 @@ -35,6 +35,7 @@ export function DialogSessionList() { const sessions = createMemo(() => searchResults() ?? sync.data.session) const options = createMemo(() => { + if (!sync.ready) return [] const today = new Date().toDateString() const sessionsListLimit = (sync.data.config.tui as any)?.session_list_limit const limit = sessionsListLimit === "none" ? undefined : sessionsListLimit || 150 From 785cd9baf07fc1e13781bba85ee2a008682f3a6f Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 15 Feb 2026 23:33:14 -0500 Subject: [PATCH 23/26] fix(tui): pass session_list_limit to session.list() API call The session_list_limit config was not being passed to the server when fetching sessions, so the server always defaulted to 100 sessions. Now fetches config first to get session_list_limit, then passes it as the 'limit' parameter to sdk.client.session.list(). This allows users to see more (or fewer) sessions in the session list dialog. --- .../opencode/src/cli/cmd/tui/context/sync.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index f4156731a73..725857c48db 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -355,21 +355,26 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ async function bootstrap() { console.log("bootstrapping") + + // Fetch config first to get session_list_limit + const configResponse = await sdk.client.config.get({}, { throwOnError: true }) + const config = configResponse.data! + const sessionsListLimit = (config.tui as any)?.session_list_limit + const sessionsLimit = sessionsListLimit === "none" ? undefined : sessionsListLimit || 150 + const start = Date.now() - 30 * 24 * 60 * 60 * 1000 const sessionListPromise = sdk.client.session - .list({ start: start }) + .list({ start, limit: sessionsLimit }) .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id))) // blocking - include session.list when continuing a session const providersPromise = sdk.client.config.providers({}, { throwOnError: true }) const providerListPromise = sdk.client.provider.list({}, { throwOnError: true }) const agentsPromise = sdk.client.app.agents({}, { throwOnError: true }) - const configPromise = sdk.client.config.get({}, { throwOnError: true }) const blockingRequests: Promise[] = [ providersPromise, providerListPromise, agentsPromise, - configPromise, ...(args.continue ? [sessionListPromise] : []), ] @@ -378,21 +383,18 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const providersResponse = providersPromise.then((x) => x.data!) const providerListResponse = providerListPromise.then((x) => x.data!) const agentsResponse = agentsPromise.then((x) => x.data ?? []) - const configResponse = configPromise.then((x) => x.data!) const sessionListResponse = args.continue ? sessionListPromise : undefined return Promise.all([ providersResponse, providerListResponse, agentsResponse, - configResponse, ...(sessionListResponse ? [sessionListResponse] : []), ]).then((responses) => { const providers = responses[0] const providerList = responses[1] const agents = responses[2] - const config = responses[3] - const sessions = responses[4] + const sessions = responses[3] batch(() => { setStore("provider", reconcile(providers.providers)) From e2f69997b96fd58131ddd460870a6690e50a9199 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 15 Feb 2026 23:40:49 -0500 Subject: [PATCH 24/26] fix(tui): omit start date filter when session_list_limit is none --- packages/opencode/src/cli/cmd/tui/context/sync.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 725857c48db..6d6762e63e6 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -360,9 +360,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const configResponse = await sdk.client.config.get({}, { throwOnError: true }) const config = configResponse.data! const sessionsListLimit = (config.tui as any)?.session_list_limit - const sessionsLimit = sessionsListLimit === "none" ? undefined : sessionsListLimit || 150 + const unlimited = sessionsListLimit === "none" + const sessionsLimit = unlimited ? undefined : sessionsListLimit || 150 - const start = Date.now() - 30 * 24 * 60 * 60 * 1000 + const start = unlimited ? undefined : Date.now() - 30 * 24 * 60 * 60 * 1000 const sessionListPromise = sdk.client.session .list({ start, limit: sessionsLimit }) .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id))) From d7cf0863c536f9394cd7ce14f7590a3568aea0d8 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Sun, 15 Feb 2026 23:42:58 -0500 Subject: [PATCH 25/26] fix(session): only apply limit when explicitly provided --- packages/opencode/src/session/index.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 7f6fb203337..d05e059c10f 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -540,17 +540,17 @@ export namespace Session { conditions.push(like(SessionTable.title, `%${input.search}%`)) } - const limit = input?.limit ?? 100 - - const rows = Database.use((db) => - db + const rows = Database.use((db) => { + const baseQuery = db .select() .from(SessionTable) .where(and(...conditions)) .orderBy(desc(SessionTable.time_updated)) - .limit(limit) - .all(), - ) + + const query = input?.limit !== undefined ? baseQuery.limit(input.limit) : baseQuery + + return query.all() + }) for (const row of rows) { yield fromRow(row) } From 800b4036fc98f27e25a2e1e46ad1e4e9f3e4c6d5 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Fri, 27 Feb 2026 07:13:05 -0500 Subject: [PATCH 26/26] move messages_limit and session_list_limit to experimental config Avoid conflicts with TUI settings migration by moving these settings from the tui group (which is being migrated to tui.json) to experimental. --- .../cmd/tui/component/dialog-session-list.tsx | 4 +- .../opencode/src/cli/cmd/tui/context/sync.tsx | 16 +-- packages/opencode/src/config/config.ts | 18 ++-- packages/opencode/test/config/config.test.ts | 102 +----------------- packages/sdk/js/src/v2/gen/types.gen.ts | 41 ++----- 5 files changed, 28 insertions(+), 153 deletions(-) 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 3799d976dca..e0a688b61d1 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 @@ -37,8 +37,8 @@ export function DialogSessionList() { const options = createMemo(() => { if (!sync.ready) return [] const today = new Date().toDateString() - const sessionsListLimit = (sync.data.config.tui as any)?.session_list_limit - const limit = sessionsListLimit === "none" ? undefined : sessionsListLimit || 150 + const sessionsListLimit = sync.data.config.experimental?.session_list_limit + const limit = sessionsListLimit === "none" ? undefined : sessionsListLimit ?? 150 return sessions() .filter((x) => x.parentID === undefined) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 6d6762e63e6..ebb9cfe725a 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -241,16 +241,16 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ event.properties.info.sessionID, produce((draft) => { draft.splice(result.index, 0, event.properties.info) - const maxMessages = (store.config.tui as any)?.messages_limit - const maxMessagesCount = maxMessages === "none" ? Infinity : maxMessages || 100 + const maxMessages = store.config.experimental?.messages_limit + const maxMessagesCount = maxMessages === "none" ? Infinity : maxMessages ?? 100 if (draft.length > maxMessagesCount) { draft.shift() } }), ) const updated = store.message[event.properties.info.sessionID] - const maxMessages = (store.config.tui as any)?.messages_limit - const maxMessagesCount = maxMessages === "none" ? Infinity : maxMessages || 100 + const maxMessages = store.config.experimental?.messages_limit + const maxMessagesCount = maxMessages === "none" ? Infinity : maxMessages ?? 100 if (updated.length > maxMessagesCount) { const oldest = updated[0] batch(() => { @@ -359,9 +359,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ // Fetch config first to get session_list_limit const configResponse = await sdk.client.config.get({}, { throwOnError: true }) const config = configResponse.data! - const sessionsListLimit = (config.tui as any)?.session_list_limit + const sessionsListLimit = config.experimental?.session_list_limit const unlimited = sessionsListLimit === "none" - const sessionsLimit = unlimited ? undefined : sessionsListLimit || 150 + const sessionsLimit = unlimited ? undefined : sessionsListLimit ?? 150 const start = unlimited ? undefined : Date.now() - 30 * 24 * 60 * 60 * 1000 const sessionListPromise = sdk.client.session @@ -469,8 +469,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }, async sync(sessionID: string) { if (fullSyncedSessions.has(sessionID)) return - const messagesLimit = (store.config.tui as any)?.messages_limit - const limit = messagesLimit === "none" ? undefined : messagesLimit || 100 + const messagesLimit = store.config.experimental?.messages_limit + const limit = messagesLimit === "none" ? undefined : messagesLimit ?? 100 const [session, messages, todo, diff] = await Promise.all([ sdk.client.session.get({ sessionID }, { throwOnError: true }), sdk.client.session.messages({ sessionID, limit }), diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 5861bb4be08..d415858ad3e 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -921,16 +921,6 @@ export namespace Config { .enum(["auto", "stacked"]) .optional() .describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"), - session_list_limit: z - .union([z.number().min(1), z.literal("none")]) - .optional() - .default(150) - .describe("Maximum number of sessions to display in session list, or 'none' to show all sessions"), - messages_limit: z - .union([z.number().min(1), z.literal("none")]) - .optional() - .default(100) - .describe("Maximum number of message parts to load per session when syncing, or 'none' to load all messages"), }) export type TUI = z.infer @@ -1189,6 +1179,14 @@ export namespace Config { .positive() .optional() .describe("Timeout in milliseconds for model context protocol (MCP) requests"), + messages_limit: z + .union([z.number().min(1), z.literal("none")]) + .optional() + .describe("Maximum number of message parts to load per session when syncing, or 'none' to load all messages"), + session_list_limit: z + .union([z.number().min(1), z.literal("none")]) + .optional() + .describe("Maximum number of sessions to display in session list, or 'none' to show all sessions"), }) .optional(), }) diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index e44225815ee..7f2aa6a455b 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -933,31 +933,6 @@ test("deduplicates duplicate plugins from global and local configs", async () => }) }) -test("handles TUI configuration with session_list_limit and messages_limit", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - tui: { - session_list_limit: 200, - messages_limit: 50, - }, - }), - ) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const config = await Config.get() - expect(config.tui?.session_list_limit).toBe(200) - expect(config.tui?.messages_limit).toBe(50) - }, - }) -}) - test("compaction config defaults to true when not specified", async () => { await using tmp = await tmpdir({ init: async (dir) => { @@ -1012,31 +987,6 @@ test("migrates legacy tools config to permissions - allow", async () => { }) }) -test("handles TUI configuration with session_list_limit set to 'none'", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - tui: { - session_list_limit: "none", - messages_limit: 75, - }, - }), - ) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const config = await Config.get() - expect(config.tui?.session_list_limit).toBe("none") - expect(config.tui?.messages_limit).toBe(75) - }, - }) -}) - test("migrates legacy tools config to permissions - deny", async () => { await using tmp = await tmpdir({ init: async (dir) => { @@ -1068,36 +1018,14 @@ test("migrates legacy tools config to permissions - deny", async () => { }) }) -test("validates TUI session_list_limit schema - rejects invalid values", async () => { +test("validates experimental messages_limit schema - rejects invalid values", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Bun.write( path.join(dir, "opencode.json"), JSON.stringify({ $schema: "https://opencode.ai/config.json", - tui: { - session_list_limit: -5, // Invalid: negative number - }, - }), - ) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - await expect(Config.get()).rejects.toThrow() - }, - }) -}) - -test("validates TUI messages_limit schema - rejects invalid values", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - tui: { + experimental: { messages_limit: 0, // Invalid: must be >= 1 }, }), @@ -1112,32 +1040,6 @@ test("validates TUI messages_limit schema - rejects invalid values", async () => }) }) -test("handles partial TUI configuration with backward compatibility", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - tui: { - scroll_speed: 2.5, - // session_list_limit and messages_limit not specified - should inherit from global config - }, - }), - ) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const config = await Config.get() - expect(config.tui?.scroll_speed).toBe(2.5) - // Note: session_list_limit and messages_limit may be inherited from global config - // The important thing is that the config loads successfully and scroll_speed is set correctly - }, - }) -}) - test("migrates legacy write tool to edit permission", async () => { await using tmp = await tmpdir({ init: async (dir) => { diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 19200cb10ab..15a3b9f704c 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1291,39 +1291,6 @@ export type Config = { */ $schema?: string logLevel?: LogLevel - /** - * TUI specific settings - */ - tui?: { - /** - * TUI scroll speed - */ - scroll_speed?: number - /** - * Scroll acceleration settings - */ - scroll_acceleration?: { - /** - * Enable scroll acceleration - */ - enabled: boolean - } - /** - * Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column - */ - diff_style?: "auto" | "stacked" - /** - * Maximum number of sessions to display in session list, or 'none' to show all sessions - */ - session_list_limit?: number | "none" - /** - * Maximum number of message parts to load per session when syncing, or 'none' to load all messages - */ - messages_limit?: number | "none" - } - /** - * Server configuration for opencode serve and web commands - */ server?: ServerConfig /** * Command configuration, see https://opencode.ai/docs/commands @@ -1511,6 +1478,14 @@ export type Config = { * Timeout in milliseconds for model context protocol (MCP) requests */ mcp_timeout?: number + /** + * Maximum number of message parts to load per session when syncing, or 'none' to load all messages + */ + messages_limit?: number | "none" + /** + * Maximum number of sessions to display in session list, or 'none' to show all sessions + */ + session_list_limit?: number | "none" } }