Skip to content

Commit cc86049

Browse files
refactor(read_file): Codex-inspired read_file refactor EXT-617 (#10981)
1 parent 0f43cc9 commit cc86049

62 files changed

Lines changed: 3330 additions & 4435 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/vscode-e2e/src/suite/tools/read-file.test.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ suite.skip("Roo Code read_file Tool", function () {
376376
}
377377
})
378378

379-
test("Should read file with line range", async function () {
379+
test("Should read file with slice offset/limit", async function () {
380380
const api = globalThis.api
381381
const messages: ClineMessage[] = []
382382
let taskCompleted = false
@@ -446,7 +446,7 @@ suite.skip("Roo Code read_file Tool", function () {
446446
alwaysAllowReadOnly: true,
447447
alwaysAllowReadOnlyOutsideWorkspace: true,
448448
},
449-
text: `Use the read_file tool to read the file "${fileName}" and show me what's on lines 2, 3, and 4. The file contains lines like "Line 1", "Line 2", etc. Assume the file exists and you can read it directly.`,
449+
text: `Use the read_file tool to read the file "${fileName}" using slice mode with offset=2 and limit=3 (1-based offset). The file contains lines like "Line 1", "Line 2", etc. After reading, show me the three lines you read.`,
450450
})
451451

452452
// Wait for task completion
@@ -455,9 +455,8 @@ suite.skip("Roo Code read_file Tool", function () {
455455
// Verify tool was executed
456456
assert.ok(toolExecuted, "The read_file tool should have been executed")
457457

458-
// Verify the tool returned the correct lines (when line range is used)
458+
// Verify the tool returned the correct lines (offset=2, limit=3 -> lines 2-4)
459459
if (toolResult && (toolResult as string).includes(" | ")) {
460-
// The result includes line numbers
461460
assert.ok(
462461
(toolResult as string).includes("2 | Line 2"),
463462
"Tool result should include line 2 with line number",

packages/cloud/src/__tests__/CloudSettingsService.parsing.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ describe("CloudSettingsService - Response Parsing", () => {
8181
version: 2,
8282
defaultSettings: {
8383
maxOpenTabsContext: 10,
84-
maxReadFileLine: 1000,
8584
},
8685
allowList: {
8786
allowAll: false,

packages/types/src/cloud.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ export const organizationDefaultSettingsSchema = globalSettingsSchema
9595
.pick({
9696
enableCheckpoints: true,
9797
maxOpenTabsContext: true,
98-
maxReadFileLine: true,
9998
maxWorkspaceFiles: true,
10099
showRooIgnoredFiles: true,
101100
terminalCommandDelay: true,
@@ -107,7 +106,6 @@ export const organizationDefaultSettingsSchema = globalSettingsSchema
107106
.merge(
108107
z.object({
109108
maxOpenTabsContext: z.number().int().nonnegative().optional(),
110-
maxReadFileLine: z.number().int().gte(-1).optional(),
111109
maxWorkspaceFiles: z.number().int().nonnegative().optional(),
112110
terminalCommandDelay: z.number().int().nonnegative().optional(),
113111
terminalShellIntegrationTimeout: z.number().int().nonnegative().optional(),

packages/types/src/global-settings.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ export const globalSettingsSchema = z.object({
118118
allowedMaxCost: z.number().nullish(),
119119
autoCondenseContext: z.boolean().optional(),
120120
autoCondenseContextPercent: z.number().optional(),
121-
maxConcurrentFileReads: z.number().optional(),
122121

123122
/**
124123
* Whether to include current time in the environment details
@@ -172,7 +171,6 @@ export const globalSettingsSchema = z.object({
172171
maxWorkspaceFiles: z.number().optional(),
173172
showRooIgnoredFiles: z.boolean().optional(),
174173
enableSubfolderRules: z.boolean().optional(),
175-
maxReadFileLine: z.number().optional(),
176174
maxImageFileSize: z.number().optional(),
177175
maxTotalImageSize: z.number().optional(),
178176

@@ -382,7 +380,6 @@ export const EVALS_SETTINGS: RooCodeSettings = {
382380
maxWorkspaceFiles: 200,
383381
maxGitStatusFiles: 20,
384382
showRooIgnoredFiles: true,
385-
maxReadFileLine: -1, // -1 to enable full file reading.
386383

387384
includeDiagnosticMessages: true,
388385
maxDiagnosticMessages: 50,

packages/types/src/telemetry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export enum TelemetryEventName {
7373
CODE_INDEX_ERROR = "Code Index Error",
7474
TELEMETRY_SETTINGS_CHANGED = "Telemetry Settings Changed",
7575
MODEL_CACHE_EMPTY_RESPONSE = "Model Cache Empty Response",
76+
READ_FILE_LEGACY_FORMAT_USED = "Read File Legacy Format Used",
7677
}
7778

7879
/**
@@ -203,6 +204,7 @@ export const rooCodeTelemetryEventSchema = z.discriminatedUnion("type", [
203204
TelemetryEventName.TAB_SHOWN,
204205
TelemetryEventName.MODE_SETTINGS_CHANGED,
205206
TelemetryEventName.CUSTOM_MODE_CREATED,
207+
TelemetryEventName.READ_FILE_LEGACY_FORMAT_USED,
206208
]),
207209
properties: telemetryPropertiesSchema,
208210
}),

packages/types/src/tool-params.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,96 @@
22
* Tool parameter type definitions for native protocol
33
*/
44

5+
/**
6+
* Read mode for the read_file tool.
7+
* - "slice": Simple offset/limit reading (default)
8+
* - "indentation": Semantic block extraction based on code structure
9+
*/
10+
export type ReadFileMode = "slice" | "indentation"
11+
12+
/**
13+
* Indentation-mode configuration for the read_file tool.
14+
*/
15+
export interface IndentationParams {
16+
/** 1-based line number to anchor indentation extraction (defaults to offset) */
17+
anchor_line?: number
18+
/** Maximum indentation levels to include above anchor (0 = unlimited) */
19+
max_levels?: number
20+
/** Include sibling blocks at the same indentation level */
21+
include_siblings?: boolean
22+
/** Include file header (imports, comments at top) */
23+
include_header?: boolean
24+
/** Hard cap on lines returned for indentation mode */
25+
max_lines?: number
26+
}
27+
28+
/**
29+
* Parameters for the read_file tool (new format).
30+
*
31+
* NOTE: This is the canonical, single-file-per-call shape.
32+
*/
33+
export interface ReadFileParams {
34+
/** Path to the file, relative to workspace */
35+
path: string
36+
/** Reading mode: "slice" (default) or "indentation" */
37+
mode?: ReadFileMode
38+
/** 1-based line number to start reading from (slice mode, default: 1) */
39+
offset?: number
40+
/** Maximum number of lines to read (default: 2000) */
41+
limit?: number
42+
/** Indentation-mode configuration (only used when mode === "indentation") */
43+
indentation?: IndentationParams
44+
}
45+
46+
// ─── Legacy Format Types (Backward Compatibility) ─────────────────────────────
47+
48+
/**
49+
* Line range specification for legacy read_file format.
50+
* Represents a contiguous range of lines [start, end] (1-based, inclusive).
51+
*/
552
export interface LineRange {
653
start: number
754
end: number
855
}
956

57+
/**
58+
* File entry for legacy read_file format.
59+
* Supports reading multiple disjoint line ranges from a single file.
60+
*/
1061
export interface FileEntry {
62+
/** Path to the file, relative to workspace */
1163
path: string
64+
/** Optional list of line ranges to read (if omitted, reads entire file) */
1265
lineRanges?: LineRange[]
1366
}
1467

68+
/**
69+
* Legacy parameters for the read_file tool (pre-refactor format).
70+
* Supports reading multiple files in a single call with optional line ranges.
71+
*
72+
* @deprecated Use ReadFileParams instead. This format is maintained for
73+
* backward compatibility with existing chat histories.
74+
*/
75+
export interface LegacyReadFileParams {
76+
/** Array of file entries to read */
77+
files: FileEntry[]
78+
/** Discriminant flag for type narrowing */
79+
_legacyFormat: true
80+
}
81+
82+
/**
83+
* Union type for read_file tool parameters.
84+
* Supports both new single-file format and legacy multi-file format.
85+
*/
86+
export type ReadFileToolParams = ReadFileParams | LegacyReadFileParams
87+
88+
/**
89+
* Type guard to check if params are in legacy format.
90+
*/
91+
export function isLegacyReadFileParams(params: ReadFileToolParams): params is LegacyReadFileParams {
92+
return "_legacyFormat" in params && params._legacyFormat === true
93+
}
94+
1595
export interface Coordinate {
1696
x: number
1797
y: number

packages/types/src/vscode-extension-host.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ export interface ExtensionMessage {
6565
| "remoteBrowserEnabled"
6666
| "ttsStart"
6767
| "ttsStop"
68-
| "maxReadFileLine"
6968
| "fileSearchResults"
7069
| "toggleApiConfigPin"
7170
| "acceptInput"
@@ -304,7 +303,6 @@ export type ExtensionState = Pick<
304303
| "ttsSpeed"
305304
| "soundEnabled"
306305
| "soundVolume"
307-
| "maxConcurrentFileReads"
308306
| "terminalOutputPreviewSize"
309307
| "terminalShellIntegrationTimeout"
310308
| "terminalShellIntegrationDisabled"
@@ -355,7 +353,6 @@ export type ExtensionState = Pick<
355353
maxWorkspaceFiles: number // Maximum number of files to include in current working directory details (0-500)
356354
showRooIgnoredFiles: boolean // Whether to show .rooignore'd files in listings
357355
enableSubfolderRules: boolean // Whether to load rules from subdirectories
358-
maxReadFileLine: number // Maximum number of lines to read from a file before truncating
359356
maxImageFileSize: number // Maximum size of image files to process in MB
360357
maxTotalImageSize: number // Maximum total size for all images in a single read operation in MB
361358

@@ -818,6 +815,7 @@ export interface ClineSayTool {
818815
isProtected?: boolean
819816
additionalFileCount?: number // Number of additional files in the same read_file request
820817
lineNumber?: number
818+
startLine?: number // Starting line for read_file operations (for navigation on click)
821819
query?: string
822820
batchFiles?: Array<{
823821
path: string

src/__tests__/command-mentions.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ describe("Command Mentions", () => {
3636
false, // showRooIgnoredFiles
3737
true, // includeDiagnosticMessages
3838
50, // maxDiagnosticMessages
39-
undefined, // maxReadFileLine
4039
)
4140
}
4241

src/api/providers/__tests__/bedrock-native-tools.spec.ts

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -135,23 +135,18 @@ describe("AwsBedrockHandler Native Tool Calling", () => {
135135
parameters: {
136136
type: "object",
137137
properties: {
138-
files: {
139-
type: "array",
140-
items: {
141-
type: "object",
142-
properties: {
143-
path: { type: "string" },
144-
line_ranges: {
145-
type: ["array", "null"],
146-
items: { type: "integer" },
147-
description: "Optional line ranges",
148-
},
138+
path: { type: "string" },
139+
indentation: {
140+
type: ["object", "null"],
141+
properties: {
142+
anchor_line: {
143+
type: ["integer", "null"],
144+
description: "Optional anchor line",
149145
},
150-
required: ["path", "line_ranges"],
151146
},
152147
},
153148
},
154-
required: ["files"],
149+
required: ["path"],
155150
},
156151
},
157152
},
@@ -167,15 +162,14 @@ describe("AwsBedrockHandler Native Tool Calling", () => {
167162
expect(executeCommandSchema.properties.cwd.type).toBeUndefined()
168163
expect(executeCommandSchema.properties.cwd.description).toBe("Working directory (optional)")
169164

170-
// Second tool: line_ranges should be transformed from type: ["array", "null"] to anyOf
171-
// with items moved inside the array variant (required by GPT-5-mini strict schema validation)
165+
// Second tool: nested nullable object should be transformed from type: ["object", "null"] to anyOf
172166
const readFileSchema = bedrockTools[1].toolSpec.inputSchema.json as any
173-
const lineRanges = readFileSchema.properties.files.items.properties.line_ranges
174-
expect(lineRanges.anyOf).toEqual([{ type: "array", items: { type: "integer" } }, { type: "null" }])
175-
expect(lineRanges.type).toBeUndefined()
176-
// items should now be inside the array variant, not at root
177-
expect(lineRanges.items).toBeUndefined()
178-
expect(lineRanges.description).toBe("Optional line ranges")
167+
const indentation = readFileSchema.properties.indentation
168+
expect(indentation.anyOf).toBeDefined()
169+
expect(indentation.type).toBeUndefined()
170+
// Object-level schema properties are preserved at the root, not inside the anyOf object variant
171+
expect(indentation.additionalProperties).toBe(false)
172+
expect(indentation.properties.anchor_line.anyOf).toEqual([{ type: "integer" }, { type: "null" }])
179173
})
180174

181175
it("should filter non-function tools", () => {

0 commit comments

Comments
 (0)