Files
oh-my-opencode/src/hooks/tool-output-truncator.ts
T
Sisyphus 0f090bcbc6 fix(webfetch): apply aggressive truncation for webfetch outputs (#434)
Root cause: DEFAULT_TARGET_MAX_TOKENS (50k tokens ~200k chars) was too high
for webfetch outputs. Web pages can be large but most content doesn't exceed
this limit, so truncation rarely triggered.

Changes:
- Add WEBFETCH_MAX_TOKENS = 10k tokens (~40k chars) for web content
- Introduce TOOL_SPECIFIC_MAX_TOKENS map for per-tool limits
- webfetch/WebFetch now use aggressive 10k token limit
- Other tools continue using default 50k token limit
- Add comprehensive tests for truncation behavior

Fixes #195

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
2026-01-03 12:09:34 +09:00

65 lines
1.7 KiB
TypeScript

import type { PluginInput } from "@opencode-ai/plugin"
import type { ExperimentalConfig } from "../config/schema"
import { createDynamicTruncator } from "../shared/dynamic-truncator"
const DEFAULT_MAX_TOKENS = 50_000 // ~200k chars
const WEBFETCH_MAX_TOKENS = 10_000 // ~40k chars - web pages need aggressive truncation
const TRUNCATABLE_TOOLS = [
"grep",
"Grep",
"safe_grep",
"glob",
"Glob",
"safe_glob",
"lsp_find_references",
"lsp_document_symbols",
"lsp_workspace_symbols",
"lsp_diagnostics",
"ast_grep_search",
"interactive_bash",
"Interactive_bash",
"skill_mcp",
"webfetch",
"WebFetch",
]
const TOOL_SPECIFIC_MAX_TOKENS: Record<string, number> = {
webfetch: WEBFETCH_MAX_TOKENS,
WebFetch: WEBFETCH_MAX_TOKENS,
}
interface ToolOutputTruncatorOptions {
experimental?: ExperimentalConfig
}
export function createToolOutputTruncatorHook(ctx: PluginInput, options?: ToolOutputTruncatorOptions) {
const truncator = createDynamicTruncator(ctx)
const truncateAll = options?.experimental?.truncate_all_tool_outputs ?? false
const toolExecuteAfter = async (
input: { tool: string; sessionID: string; callID: string },
output: { title: string; output: string; metadata: unknown }
) => {
if (!truncateAll && !TRUNCATABLE_TOOLS.includes(input.tool)) return
try {
const targetMaxTokens = TOOL_SPECIFIC_MAX_TOKENS[input.tool] ?? DEFAULT_MAX_TOKENS
const { result, truncated } = await truncator.truncate(
input.sessionID,
output.output,
{ targetMaxTokens }
)
if (truncated) {
output.output = result
}
} catch {
// Graceful degradation - don't break tool execution
}
}
return {
"tool.execute.after": toolExecuteAfter,
}
}