From 5d929f78c353f446f7d3e7959c99b0522e2131f1 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 26 May 2026 15:33:08 -0500 Subject: [PATCH] refactor(opencode): simplify tool output disable config --- packages/opencode/src/config/config.ts | 15 ++------------- packages/opencode/src/tool/truncate.ts | 4 ++-- packages/opencode/test/config/config.test.ts | 15 +++++---------- packages/opencode/test/tool/shell.test.ts | 2 +- packages/opencode/test/tool/truncation.test.ts | 2 +- packages/sdk/js/src/v2/gen/types.gen.ts | 8 +------- packages/web/src/content/docs/config.mdx | 10 ++-------- 7 files changed, 14 insertions(+), 42 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 327dea464..09d43e244 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -49,13 +49,7 @@ const log = Log.create({ service: "config" }) // Custom merge function that concatenates array fields instead of replacing them // Keep remeda's deep conditional merge type out of hot config-loading paths; TS profiling showed it dominates here. function mergeConfig(target: Info, source: Info): Info { - const merged = mergeDeep(target, source) as Info - if (!target.tool_output || !source.tool_output) return merged - if (target.tool_output.truncate !== false && source.tool_output.truncate !== false) return merged - - // Disabled truncation and custom limits are separate config modes; the later layer selects the mode. - merged.tool_output = source.tool_output - return merged + return mergeDeep(target, source) as Info } function mergeConfigConcatArrays(target: Info, source: Info): Info { @@ -260,13 +254,8 @@ export const Info = Schema.Struct({ ), tool_output: Schema.optional( Schema.Union([ + Schema.Literal(false).annotate({ description: "Disable tool output truncation" }), Schema.Struct({ - truncate: Schema.Literal(false).annotate({ description: "Disable tool output truncation" }), - }).annotate({ parseOptions: { onExcessProperty: "error" } }), - Schema.Struct({ - truncate: Schema.optional(Schema.Literal(true)).annotate({ - description: "Enable truncating tool output that exceeds the configured limits (default: true)", - }), max_lines: Schema.optional(PositiveInt).annotate({ description: "Maximum lines of tool output before it is truncated and saved to disk (default: 2000)", }), diff --git a/packages/opencode/src/tool/truncate.ts b/packages/opencode/src/tool/truncate.ts index c92d8e31a..d76eacb92 100644 --- a/packages/opencode/src/tool/truncate.ts +++ b/packages/opencode/src/tool/truncate.ts @@ -42,7 +42,7 @@ export interface Interface { readonly output: (text: string, options?: Options, agent?: Agent.Info) => Effect.Effect /** * Resolved truncation limits from `tool_output` in opencode config. - * Returns `None` when the user has disabled truncation (`tool_output.truncate: false`), + * Returns `None` when the user has disabled truncation (`tool_output: false`), * in which case callers should pass output through without enforcing thresholds. */ readonly limits: () => Effect.Effect> @@ -81,7 +81,7 @@ export const layer = Layer.effect( if (Option.isNone(configSvc)) return Option.some({ maxLines: MAX_LINES, maxBytes: MAX_BYTES }) const cfg = yield* configSvc.value.get().pipe(Effect.catch(() => Effect.succeed(undefined))) const tool_output = cfg?.tool_output - if (tool_output?.truncate === false) return Option.none() + if (tool_output === false) return Option.none() return Option.some({ maxLines: tool_output?.max_lines ?? MAX_LINES, maxBytes: tool_output?.max_bytes ?? MAX_BYTES, diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index a2ac219c1..b99fafc05 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -1292,25 +1292,20 @@ test("config parser preserves permission order while rejecting unknown top-level } }) -test("tool_output only accepts thresholds when truncation is enabled", () => { - expect(ConfigParse.schema(Config.Info, { tool_output: { truncate: false } }, "test").tool_output).toEqual({ - truncate: false, - }) +test("tool_output accepts thresholds or disables truncation", () => { + expect(ConfigParse.schema(Config.Info, { tool_output: false }, "test").tool_output).toBe(false) expect( ConfigParse.schema(Config.Info, { tool_output: { max_lines: 200, max_bytes: 8192 } }, "test").tool_output, ).toEqual({ max_lines: 200, max_bytes: 8192, }) - expect(() => - ConfigParse.schema(Config.Info, { tool_output: { truncate: false, max_lines: 200, max_bytes: 8192 } }, "test"), - ).toThrow() }) it.effect("project tool_output limits replace disabled global truncation", () => withConfigTree( { - global: { tool_output: { truncate: false } }, + global: { tool_output: false }, project: { tool_output: { max_lines: 200 } }, }, Effect.gen(function* () { @@ -1323,10 +1318,10 @@ it.effect("project disabled tool_output replaces global limits", () => withConfigTree( { global: { tool_output: { max_lines: 200, max_bytes: 8192 } }, - project: { tool_output: { truncate: false } }, + project: { tool_output: false }, }, Effect.gen(function* () { - expect((yield* Config.use.get()).tool_output).toEqual({ truncate: false }) + expect((yield* Config.use.get()).tool_output).toBe(false) }), ), ) diff --git a/packages/opencode/test/tool/shell.test.ts b/packages/opencode/test/tool/shell.test.ts index 919a10b5c..6eef878a1 100644 --- a/packages/opencode/test/tool/shell.test.ts +++ b/packages/opencode/test/tool/shell.test.ts @@ -1207,7 +1207,7 @@ describe("tool.shell truncation", () => { it.live("does not truncate output when tool_output is disabled", () => Effect.gen(function* () { - const tmp = yield* tmpdirScoped({ config: { tool_output: { truncate: false } } }) + const tmp = yield* tmpdirScoped({ config: { tool_output: false } }) yield* runIn( tmp, Effect.gen(function* () { diff --git a/packages/opencode/test/tool/truncation.test.ts b/packages/opencode/test/tool/truncation.test.ts index dd0ca558b..4fecac671 100644 --- a/packages/opencode/test/tool/truncation.test.ts +++ b/packages/opencode/test/tool/truncation.test.ts @@ -166,7 +166,7 @@ describe("Truncate", () => { }), ) - const disabledIt = configuredIt({ tool_output: { truncate: false } }) + const disabledIt = configuredIt({ tool_output: false }) disabledIt.live("does not truncate output when disabled", () => Effect.gen(function* () { const content = "a".repeat(Truncate.MAX_BYTES + 1) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 1cbfa799a..a3a400190 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1298,14 +1298,8 @@ export type Config = { * Configure tool output truncation. When output exceeds either limit, the full text is written to the truncation directory and a preview is returned. */ tool_output?: + | false | { - /** - * Disable tool output truncation - */ - truncate: false - } - | { - truncate?: true max_lines?: number max_bytes?: number } diff --git a/packages/web/src/content/docs/config.mdx b/packages/web/src/content/docs/config.mdx index f6927384c..7aa9ff893 100644 --- a/packages/web/src/content/docs/config.mdx +++ b/packages/web/src/content/docs/config.mdx @@ -361,32 +361,26 @@ You can control when tool output is truncated using the `tool_output` option. Wh { "$schema": "https://opencode.ai/config.json", "tool_output": { - "truncate": true, "max_lines": 2000, "max_bytes": 51200 } } ``` -- `truncate` - Truncate tool output when it exceeds a configured threshold (default: `true`). - `max_lines` - Maximum number of lines before output is truncated (default: `2000`). - `max_bytes` - Maximum size in bytes before output is truncated (default: `51200`). These thresholds apply to output handled by OpenCode's shared truncation layer, including MCP and plugin tool output. Individual tools that page or cap their own results can have separate limits. -To disable shared tool output truncation, set `truncate` to `false`: +To disable shared tool output truncation, set `tool_output` to `false`: ```json title="opencode.json" { "$schema": "https://opencode.ai/config.json", - "tool_output": { - "truncate": false - } + "tool_output": false } ``` -`truncate: false` cannot be combined with `max_lines` or `max_bytes`; thresholds only apply when truncation is enabled. - --- ### Models