Skip to content

Commit 6e16050

Browse files
disable read tool limit and offset
1 parent d4d637f commit 6e16050

3 files changed

Lines changed: 168 additions & 0 deletions

File tree

index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
createChatMessageTransformHandler,
88
createCommandExecuteHandler,
99
createSystemPromptHandler,
10+
createToolDefinitionHandler,
1011
} from "./lib/hooks"
1112
import { configureClientAuth, isSecureMode } from "./lib/auth"
1213

@@ -60,6 +61,7 @@ const plugin: Plugin = (async (ctx) => {
6061
config,
6162
ctx.directory,
6263
),
64+
"tool.definition": createToolDefinitionHandler(logger) as any,
6365
tool: {
6466
...(config.tools.compress.permission !== "deny" && {
6567
compress: createCompressTool({

lib/hooks.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import { handleSweepCommand } from "./commands/sweep"
1414
import { handleManualToggleCommand, handleManualTriggerCommand } from "./commands/manual"
1515
import { ensureSessionInitialized } from "./state/state"
1616
import { cacheSystemPromptTokens } from "./ui/utils"
17+
import { scrubReadToolDefinition } from "./tools/scrub"
18+
import { clog, C } from "./compress-logger"
19+
import z from "zod"
1720

1821
const INTERNAL_AGENT_SIGNATURES = [
1922
"You are a title generator",
@@ -128,6 +131,87 @@ export function createChatMessageTransformHandler(
128131
}
129132
}
130133

134+
function zodTypeName(schema: z.ZodTypeAny): string {
135+
const def = (schema as { _def?: { typeName?: string } })._def
136+
if (def?.typeName) {
137+
return def.typeName
138+
}
139+
140+
if (schema.constructor?.name) {
141+
return schema.constructor.name
142+
}
143+
144+
return "unknown"
145+
}
146+
147+
function describeZod(schema: z.ZodTypeAny): {
148+
type: string
149+
description?: string
150+
optional?: boolean
151+
} {
152+
if (schema instanceof z.ZodOptional) {
153+
const inner = schema._def.innerType as z.ZodTypeAny
154+
const base = describeZod(inner)
155+
return {
156+
...base,
157+
optional: true,
158+
}
159+
}
160+
161+
const def = (schema as { _def?: { description?: string } })._def
162+
const description = typeof def?.description === "string" ? def.description : undefined
163+
return {
164+
type: zodTypeName(schema),
165+
description,
166+
}
167+
}
168+
169+
function formatParameters(parameters: unknown): unknown {
170+
if (!(parameters instanceof z.ZodObject)) {
171+
return parameters
172+
}
173+
174+
const shape = parameters.shape
175+
const properties = Object.fromEntries(
176+
Object.entries(shape).map(([key, value]) => {
177+
return [key, describeZod(value as z.ZodTypeAny)]
178+
}),
179+
)
180+
181+
return {
182+
type: "object",
183+
properties,
184+
}
185+
}
186+
187+
export function createToolDefinitionHandler(logger: Logger) {
188+
return async (input: { toolID: string }, output: { description: string; parameters: any }) => {
189+
if (input.toolID !== "read") {
190+
return
191+
}
192+
193+
const stats = scrubReadToolDefinition(output)
194+
const parameters = formatParameters(output.parameters)
195+
const descriptionLines = output.description.split("\n")
196+
197+
clog.info(C.COMPRESS, "Read tool definition (scrubbed)", {
198+
toolID: input.toolID,
199+
descriptionLines,
200+
parameters,
201+
descriptionLinesRemoved: stats.descriptionLinesRemoved,
202+
removedDescriptionLines: stats.removedDescriptionLines,
203+
removedSchemaProperties: stats.removedSchemaProperties,
204+
})
205+
206+
logger.debug("Read tool definition (scrubbed)", {
207+
toolID: input.toolID,
208+
descriptionLinesRemoved: stats.descriptionLinesRemoved,
209+
removedDescriptionLines: stats.removedDescriptionLines,
210+
removedSchemaProperties: stats.removedSchemaProperties,
211+
})
212+
}
213+
}
214+
131215
export function createCommandExecuteHandler(
132216
client: any,
133217
state: SessionState,

lib/tools/scrub.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import z from "zod"
2+
3+
export interface ReadToolScrubStats {
4+
modified: boolean
5+
descriptionLinesRemoved: number
6+
removedDescriptionLines: string[]
7+
removedSchemaProperties: string[]
8+
}
9+
10+
const READ_DESCRIPTION_MARKERS = [
11+
"Read a file or directory from the local filesystem.",
12+
"Usage:",
13+
"- The filePath parameter should be an absolute path.",
14+
]
15+
16+
const READ_DESCRIPTION_REMOVALS = [
17+
"By default, this tool returns up to 2000 lines",
18+
"offset parameter",
19+
"larger offset",
20+
"Use 'offset'",
21+
]
22+
23+
function isReadDescription(text: string): boolean {
24+
return READ_DESCRIPTION_MARKERS.every((marker) => text.includes(marker))
25+
}
26+
27+
function shouldRemoveReadDescriptionLine(line: string): boolean {
28+
return READ_DESCRIPTION_REMOVALS.some((fragment) => line.includes(fragment))
29+
}
30+
31+
function scrubReadDescription(text: string, stats: ReadToolScrubStats): string {
32+
if (!isReadDescription(text)) {
33+
return text
34+
}
35+
36+
const lines = text.split("\n")
37+
const removed = lines.filter((line) => shouldRemoveReadDescriptionLine(line))
38+
const filtered = lines.filter((line) => !shouldRemoveReadDescriptionLine(line))
39+
40+
if (filtered.length === lines.length) {
41+
return text
42+
}
43+
44+
stats.descriptionLinesRemoved += removed.length
45+
stats.removedDescriptionLines.push(...removed)
46+
stats.modified = true
47+
return filtered.join("\n")
48+
}
49+
50+
function scrubReadParameters(schema: unknown, stats: ReadToolScrubStats): unknown {
51+
if (!(schema instanceof z.ZodObject)) {
52+
return schema
53+
}
54+
55+
const shape = schema.shape
56+
const nextEntries = Object.entries(shape).filter(([key]) => key !== "offset" && key !== "limit")
57+
if (nextEntries.length === Object.keys(shape).length) {
58+
return schema
59+
}
60+
61+
const removed = Object.keys(shape).filter((key) => key === "offset" || key === "limit")
62+
stats.removedSchemaProperties.push(...removed)
63+
stats.modified = true
64+
return z.object(Object.fromEntries(nextEntries))
65+
}
66+
67+
export function scrubReadToolDefinition(output: {
68+
description: string
69+
parameters: unknown
70+
}): ReadToolScrubStats {
71+
const stats: ReadToolScrubStats = {
72+
modified: false,
73+
descriptionLinesRemoved: 0,
74+
removedDescriptionLines: [],
75+
removedSchemaProperties: [],
76+
}
77+
78+
output.description = scrubReadDescription(output.description, stats)
79+
output.parameters = scrubReadParameters(output.parameters, stats)
80+
81+
return stats
82+
}

0 commit comments

Comments
 (0)