bruteSourceBruteTruncation

module Truncation

Universal tool output truncation.

Every tool result passes through Truncation.truncate() before entering the LLM context. This is the primary guard against context window explosion — even if a tool has no internal limits, this module caps the output to a safe size.

Existing features (ref: opencode truncate.ts):

  1. Line + byte dual cap — truncate when output exceeds MAX_LINES (2000) or MAX_BYTES (50 KB), whichever is hit first.
  2. Head mode (default) — keep the first N lines / bytes. Used for most tool output where the beginning is most relevant.
  3. Tail mode — keep the last N lines / bytes. Used for shell output where errors and summaries appear at the end.
  4. Overflow to disk — when truncating, write the full text to a file under TRUNCATION_DIR (e.g. ~/.local/share/brute/tool-output/). Return a preview + hint pointing to the saved file.
  5. Hint message — when truncated, append a contextual hint: "Full output saved to: (path). Use Read with offset/limit to view specific sections."
  6. Configurable limits — allow overriding MAX_LINES / MAX_BYTES via per-call options.
  7. Retention cleanup — purge saved output files older than a configurable retention period from a truncation directory.
  8. Per-line truncation — truncate individual lines longer than MAX_LINE_LENGTH (2000 chars) with a suffix.

Definitions

def self.truncate(text, max_lines: MAX_LINES, max_bytes: MAX_BYTES, direction: :head, truncation_dir: nil)

Truncate text to fit within line and byte limits.

Returns the text unchanged if it fits. Otherwise returns a truncated preview with a hint message.

Signature

parameter text String

the tool output to truncate

parameter max_lines Integer

maximum number of lines to keep

parameter max_bytes Integer

maximum byte size to keep

parameter direction Symbol

which end to keep

parameter truncation_dir String, nil

directory to save full output when truncating

returns String

the (possibly truncated) text

Implementation

def self.truncate(text, max_lines: MAX_LINES, max_bytes: MAX_BYTES, direction: :head, truncation_dir: nil)
  return text if text.nil? || text.empty?

  # Per-line truncation first — cap individual lines
  lines = text.lines.map { |line| truncate_line(line) }
  text = lines.join

  return text if lines.size <= max_lines && text.bytesize <= max_bytes

  # Determine how many lines we can keep within both caps
  kept = direction == :tail ? lines.last(max_lines) : lines.first(max_lines)

  # Enforce byte cap
  result_lines = []
  bytes = 0
  kept.each do |line|
    break if bytes + line.bytesize > max_bytes
    result_lines << line
    bytes += line.bytesize
  end

  result = result_lines.join
  total = lines.size
  shown = result_lines.size

  # Overflow to disk — save the full output so it can be inspected later
  saved_path = save_to_disk(text, truncation_dir)

  hint = "\n#{TRUNCATION_MARKER} showing #{shown} of #{total} lines]"
  if saved_path
    hint += "\nFull output saved to: #{saved_path}. Use Read with offset/limit to view specific sections."
  end
  result + hint
end

def self.already_truncated?(text)

Check whether text already contains a truncation marker.

Implementation

def self.already_truncated?(text)
  text.include?(TRUNCATION_MARKER)
end

def self.truncate_line(line, max: MAX_LINE_LENGTH)

Truncate a single line if it exceeds MAX_LINE_LENGTH.

Implementation

def self.truncate_line(line, max: MAX_LINE_LENGTH)
  return line if line.length <= max
  line[0, max] + "... (line truncated to #{max} chars)\n"
end

def self.cleanup!(dir, retention_days: 7)

Purge files older than retention_days from the given directory.

Implementation

def self.cleanup!(dir, retention_days: 7)
  return unless File.directory?(dir)
  cutoff = Time.now - (retention_days * 86400)
  Dir.glob(File.join(dir, "*")).each do |path|
    File.delete(path) if File.file?(path) && File.mtime(path) < cutoff
  end
end

def self.save_to_disk(text, truncation_dir)

Save text to a file in truncation_dir. Returns the file path, or nil.

Implementation

def self.save_to_disk(text, truncation_dir)
  dir = truncation_dir || TRUNCATION_DIR
  return nil unless dir
  FileUtils.mkdir_p(dir)
  path = File.join(dir, "tool_#{SecureRandom.hex(8)}.txt")
  File.write(path, text)
  path
rescue => _e
  nil
end