Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions packages/types/src/tool-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,35 @@
* Tool parameter type definitions for native protocol
*/

export interface LineRange {
start: number
end: number
/**
* Configuration for indentation-aware block extraction
*/
export interface IndentationConfig {
/** The line to anchor the block expansion from (defaults to offset) */
anchorLine?: number
/** Maximum indentation depth to collect; 0 = unlimited */
maxLevels?: number
/** Whether to include sibling blocks at same indentation level */
includeSiblings?: boolean
/** Whether to include comment headers above the anchor block */
includeHeader?: boolean
/** Hard cap on returned lines (defaults to limit) */
maxLines?: number
}

/**
* Read mode for file content extraction
*/
export type ReadMode = "slice" | "indentation"

export interface FileEntry {
path: string
lineRanges?: LineRange[]
/** 1-indexed line number to start reading from (default: 1) */
offset?: number
/** Reading mode: "slice" for simple reading, "indentation" for smart block extraction */
mode?: ReadMode
/** Configuration for indentation mode */
indentation?: IndentationConfig
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FileEntry in tool-params.ts still defines limit?: number, but ReadFileTool intentionally ignores entry.limit (uses maxReadFileLine only). This is a contract mismatch: models may send limit expecting it to work, and it will be silently ignored. Either document that limit is ignored (and remove it from schema/types), or wire it through and clamp to maxReadFileLine.

Fix it with Roo Code or mention @roomote and request a fix.


export interface Coordinate {
Expand Down
88 changes: 61 additions & 27 deletions src/core/assistant-message/NativeToolCallParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,40 +298,74 @@ export class NativeToolCallParser {
}

/**
* Convert raw file entries from API (with line_ranges) to FileEntry objects
* (with lineRanges). Handles multiple formats for compatibility:
* Convert raw file entries from API to FileEntry objects.
* Supports the new slice/indentation API:
*
* New tuple format: { path: string, line_ranges: [[1, 50], [100, 150]] }
* Object format: { path: string, line_ranges: [{ start: 1, end: 50 }] }
* Legacy string format: { path: string, line_ranges: ["1-50"] }
* { path: string, offset?: number, mode?: "slice" | "indentation", indentation?: {...} }
*
* Returns: { path: string, lineRanges: [{ start: 1, end: 50 }] }
* Note: limit is intentionally not exposed to models - it's controlled by the maxReadFileLine setting.
*/
private static convertFileEntries(files: any[]): FileEntry[] {
return files.map((file: any) => {
const entry: FileEntry = { path: file.path }
if (file.line_ranges && Array.isArray(file.line_ranges)) {
entry.lineRanges = file.line_ranges
.map((range: any) => {
// Handle tuple format: [start, end]
if (Array.isArray(range) && range.length >= 2) {
return { start: Number(range[0]), end: Number(range[1]) }
}
// Handle object format: { start: number, end: number }
if (typeof range === "object" && range !== null && "start" in range && "end" in range) {
return { start: Number(range.start), end: Number(range.end) }
}
// Handle legacy string format: "1-50"
if (typeof range === "string") {
const match = range.match(/^(\d+)-(\d+)$/)
if (match) {
return { start: parseInt(match[1], 10), end: parseInt(match[2], 10) }
}
}
return null
})
.filter(Boolean)

// Map offset parameter
if (file.offset !== undefined) {
const offset = Number(file.offset)
if (!isNaN(offset) && offset > 0) {
entry.offset = offset
}
}

// Map mode parameter
if (file.mode === "slice" || file.mode === "indentation") {
entry.mode = file.mode
}

// Map indentation configuration
if (file.indentation && typeof file.indentation === "object") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createReadFileTool() defines indentation config keys as anchorLine/maxLevels/includeSiblings/includeHeader, but convertFileEntries() only reads indentation.anchor_line/max_levels/etc, so a tool call that follows the schema will silently drop indentation settings. Consider accepting both casings (camelCase + snake_case) or aligning the schema and examples to the parser.

Fix it with Roo Code or mention @roomote and request a fix.

const indent = file.indentation
const indentConfig: FileEntry["indentation"] = {}

// anchorLine
if (indent.anchorLine !== undefined) {
const anchorLine = Number(indent.anchorLine)
if (!isNaN(anchorLine) && anchorLine > 0) {
indentConfig.anchorLine = anchorLine
}
}

// maxLevels
if (indent.maxLevels !== undefined) {
const maxLevels = Number(indent.maxLevels)
if (!isNaN(maxLevels) && maxLevels >= 0) {
indentConfig.maxLevels = maxLevels
}
}

// includeSiblings
if (indent.includeSiblings !== undefined) {
indentConfig.includeSiblings = Boolean(indent.includeSiblings)
}

// includeHeader
if (indent.includeHeader !== undefined) {
indentConfig.includeHeader = Boolean(indent.includeHeader)
}

// maxLines
if (indent.maxLines !== undefined) {
const maxLines = Number(indent.maxLines)
if (!isNaN(maxLines) && maxLines > 0) {
indentConfig.maxLines = maxLines
}
}

if (Object.keys(indentConfig).length > 0) {
entry.indentation = indentConfig
}
}

return entry
})
}
Expand Down
Loading
Loading