Skip to content
Prev Previous commit
fix: improve handling of large files and context limits
  • Loading branch information
shift committed Aug 23, 2025
commit 20cde4e345cbb599345fbbc937c4bff3f6aebaca
7 changes: 6 additions & 1 deletion packages/opencode/src/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,12 @@ export namespace Session {
if (previous && previous.tokens) {
const tokens =
previous.tokens.input + previous.tokens.cache.read + previous.tokens.cache.write + previous.tokens.output
if (model.info.limit.context && tokens > Math.max((model.info.limit.context - outputLimit) * 0.9, 0)) {
// Calculate token thresholds
const contextLimit = model.info.limit.context || 0
const safetyBuffer = contextLimit * 0.1 // 10% safety buffer
const triggerThreshold = contextLimit - outputLimit - safetyBuffer

if (contextLimit && tokens > triggerThreshold) {
state().autoCompacting.set(input.sessionID, true)

await summarize({
Expand Down
33 changes: 29 additions & 4 deletions packages/opencode/src/tool/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { App } from "../app/app"
import { Filesystem } from "../util/filesystem"

const DEFAULT_READ_LIMIT = 2000
const MAX_READ_LINES = 10000
const MAX_LINE_LENGTH = 2000

export const ReadTool = Tool.define("read", {
Expand Down Expand Up @@ -49,27 +50,51 @@ export const ReadTool = Tool.define("read", {
throw new Error(`File not found: ${filepath}`)
}

const limit = params.limit ?? DEFAULT_READ_LIMIT
const offset = params.offset || 0
const isImage = isImageFile(filepath)
if (isImage) throw new Error(`This is an image file of type: ${isImage}\nUse a different tool to process images`)
const isBinary = await isBinaryFile(filepath, file)
if (isBinary) throw new Error(`Cannot read binary file: ${filepath}`)

const lines = await file.text().then((text) => text.split("\n"))
const raw = lines.slice(offset, offset + limit).map((line) => {

// Handle large files by providing information about size
const totalLines = lines.length
const requestedLimit = params.limit ?? DEFAULT_READ_LIMIT
const actualLimit = Math.min(requestedLimit, MAX_READ_LINES)
const offset = params.offset || 0

if (offset >= totalLines) {
throw new Error(`Offset ${offset} is beyond the file length of ${totalLines} lines`)
}

const raw = lines.slice(offset, offset + actualLimit).map((line) => {
return line.length > MAX_LINE_LENGTH ? line.substring(0, MAX_LINE_LENGTH) + "..." : line
})

const content = raw.map((line, index) => {
return `${(index + offset + 1).toString().padStart(5, "0")}| ${line}`
})

const preview = raw.slice(0, 20).join("\n")

let output = "<file>\n"

// Add file stats if it's a large file
if (totalLines > MAX_READ_LINES) {
output += `Note: This is a large file (${totalLines.toLocaleString()} lines). Showing lines ${offset + 1} to ${
offset + actualLimit
} of ${totalLines.toLocaleString()}.\n\n`
}

output += content.join("\n")

if (lines.length > offset + content.length) {
output += `\n\n(File has more lines. Use 'offset' parameter to read beyond line ${offset + content.length})`
const remainingLines = lines.length - (offset + content.length)
output += `\n\n(${remainingLines.toLocaleString()} more lines. Use 'offset' parameter to read beyond line ${
offset + content.length
})`
}

output += "\n</file>"

// just warms the lsp client
Expand Down