fix(hooks): restore grep truncation by removing unused grep-output-truncator (#120)
The grep-output-truncator hook was never registered in index.ts, so grep
output was not being truncated since commit 03a4501 which removed grep/Grep
from tool-output-truncator's TRUNCATABLE_TOOLS list.
- Remove unused grep-output-truncator.ts
- Add "grep" and "Grep" back to tool-output-truncator's TRUNCATABLE_TOOLS
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,131 +0,0 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
const ANTHROPIC_ACTUAL_LIMIT = 200_000
|
||||
const CHARS_PER_TOKEN_ESTIMATE = 4
|
||||
const TARGET_MAX_TOKENS = 50_000
|
||||
|
||||
interface AssistantMessageInfo {
|
||||
role: "assistant"
|
||||
tokens: {
|
||||
input: number
|
||||
output: number
|
||||
reasoning: number
|
||||
cache: { read: number; write: number }
|
||||
}
|
||||
}
|
||||
|
||||
interface MessageWrapper {
|
||||
info: { role: string } & Partial<AssistantMessageInfo>
|
||||
}
|
||||
|
||||
function estimateTokens(text: string): number {
|
||||
return Math.ceil(text.length / CHARS_PER_TOKEN_ESTIMATE)
|
||||
}
|
||||
|
||||
function truncateToTokenLimit(output: string, maxTokens: number): { result: string; truncated: boolean } {
|
||||
const currentTokens = estimateTokens(output)
|
||||
|
||||
if (currentTokens <= maxTokens) {
|
||||
return { result: output, truncated: false }
|
||||
}
|
||||
|
||||
const lines = output.split("\n")
|
||||
|
||||
if (lines.length <= 3) {
|
||||
const maxChars = maxTokens * CHARS_PER_TOKEN_ESTIMATE
|
||||
return {
|
||||
result: output.slice(0, maxChars) + "\n\n[Output truncated due to context window limit]",
|
||||
truncated: true,
|
||||
}
|
||||
}
|
||||
|
||||
const headerLines = lines.slice(0, 3)
|
||||
const contentLines = lines.slice(3)
|
||||
|
||||
const headerText = headerLines.join("\n")
|
||||
const headerTokens = estimateTokens(headerText)
|
||||
const availableTokens = maxTokens - headerTokens - 50
|
||||
|
||||
if (availableTokens <= 0) {
|
||||
return {
|
||||
result: headerText + "\n\n[Content truncated due to context window limit]",
|
||||
truncated: true,
|
||||
}
|
||||
}
|
||||
|
||||
let resultLines: string[] = []
|
||||
let currentTokenCount = 0
|
||||
|
||||
for (const line of contentLines) {
|
||||
const lineTokens = estimateTokens(line + "\n")
|
||||
if (currentTokenCount + lineTokens > availableTokens) {
|
||||
break
|
||||
}
|
||||
resultLines.push(line)
|
||||
currentTokenCount += lineTokens
|
||||
}
|
||||
|
||||
const truncatedContent = [...headerLines, ...resultLines].join("\n")
|
||||
const removedCount = contentLines.length - resultLines.length
|
||||
|
||||
return {
|
||||
result: truncatedContent + `\n\n[${removedCount} more lines truncated due to context window limit]`,
|
||||
truncated: true,
|
||||
}
|
||||
}
|
||||
|
||||
export function createGrepOutputTruncatorHook(ctx: PluginInput) {
|
||||
const GREP_TOOLS = ["grep", "Grep", "safe_grep"]
|
||||
|
||||
const toolExecuteAfter = async (
|
||||
input: { tool: string; sessionID: string; callID: string },
|
||||
output: { title: string; output: string; metadata: unknown }
|
||||
) => {
|
||||
if (!GREP_TOOLS.includes(input.tool)) return
|
||||
|
||||
const { sessionID } = input
|
||||
|
||||
try {
|
||||
const response = await ctx.client.session.messages({
|
||||
path: { id: sessionID },
|
||||
})
|
||||
|
||||
const messages = (response.data ?? response) as MessageWrapper[]
|
||||
|
||||
const assistantMessages = messages
|
||||
.filter((m) => m.info.role === "assistant")
|
||||
.map((m) => m.info as AssistantMessageInfo)
|
||||
|
||||
if (assistantMessages.length === 0) return
|
||||
|
||||
// Use only the last assistant message's input tokens
|
||||
// This reflects the ACTUAL current context window usage (post-compaction)
|
||||
const lastAssistant = assistantMessages[assistantMessages.length - 1]
|
||||
const lastTokens = lastAssistant.tokens
|
||||
const totalInputTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0)
|
||||
|
||||
const remainingTokens = ANTHROPIC_ACTUAL_LIMIT - totalInputTokens
|
||||
|
||||
const maxOutputTokens = Math.min(
|
||||
remainingTokens * 0.5,
|
||||
TARGET_MAX_TOKENS
|
||||
)
|
||||
|
||||
if (maxOutputTokens <= 0) {
|
||||
output.output = "[Output suppressed - context window exhausted]"
|
||||
return
|
||||
}
|
||||
|
||||
const { result, truncated } = truncateToTokenLimit(output.output, maxOutputTokens)
|
||||
if (truncated) {
|
||||
output.output = result
|
||||
}
|
||||
} catch {
|
||||
// Graceful degradation
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"tool.execute.after": toolExecuteAfter,
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ export { createContextWindowMonitorHook } from "./context-window-monitor";
|
||||
export { createSessionNotification } from "./session-notification";
|
||||
export { createSessionRecoveryHook, type SessionRecoveryHook, type SessionRecoveryOptions } from "./session-recovery";
|
||||
export { createCommentCheckerHooks } from "./comment-checker";
|
||||
export { createGrepOutputTruncatorHook } from "./grep-output-truncator";
|
||||
export { createToolOutputTruncatorHook } from "./tool-output-truncator";
|
||||
export { createDirectoryAgentsInjectorHook } from "./directory-agents-injector";
|
||||
export { createDirectoryReadmeInjectorHook } from "./directory-readme-injector";
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { createDynamicTruncator } from "../shared/dynamic-truncator"
|
||||
|
||||
// Note: "grep" and "Grep" are handled by dedicated grep-output-truncator.ts
|
||||
const TRUNCATABLE_TOOLS = [
|
||||
"grep",
|
||||
"Grep",
|
||||
"safe_grep",
|
||||
"glob",
|
||||
"Glob",
|
||||
|
||||
Reference in New Issue
Block a user