Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

read_file

Reads a page of lines from a text file, numbered, and tells the model how to fetch the next page. The file is streamed, so reading the first page of a multi-gigabyte log costs only the bytes up to the end of that page.

Arguments

NameTypeRequiredMeaning
pathstringyesPath of the file.
offsetintegerno1-based line number to start from. Default 1.
limitintegernoMaximum lines to return. Default 100.

Result

Numbered lines, then a marker.

1	# wisp
2
3	An on-device, tool-using AI microharness ...
[more: call again with offset 4]

The final line is either [more: call again with offset N] or [end of file]. A range past the end yields (no lines in range) and [end of file].

Approval

Paths go through the approval gate’s rule classifier as if they were cat '<path>', the path shell-quoted, with # resolves to '<real path>' after it when following links leads to a file the rules rate higher: credential-like paths (.ssh, .aws/credentials, .netrc, keys), by the name given or the file it leads to and in any case (.SSH/ID_RSA), are rated dangerous and ask, or are refused where nobody can answer, exactly as the command would be. Ordinary files pass without a model call. Refusals come back as error: read not approved: ….

The gate is the only control on reads. The read happens in wisp’s own process with your permissions: there is no sandbox and no confinement to the launch directory, unlike run_command’s writes, so the model can read anything you can, your home directory included (trust.md). Prefer --tool to leave read_file out of a conversation that should not read at all.

Limits

LimitDefaultConfigure
Bytes of line content per pageThe tool-output budget: an eighth of the model’s window, 4 KiB at least, 64 KiB at most (ADR 0050); the page ends early and the marker points at the next lineFollows the model’s window; not a config.json setting
Line lengthA single line longer than the budget is cut to the budget, on a character boundary; the rest of the line is skipped by bytes, never read into memory, so a file with no newlines costs one pagesame
Binary filesRejected if the first chunk contains a NUL bytenot configurable
FIFOs, devices, socketsRefused before they are opened (not a regular file: /dev/null is a character device), since reading one can wait forever; only regular files are read, as the condensing tools since 0.21.1not configurable
Directories, missing files, offset/limit below 1Errors
Wildcards (*, ?, [) in a path that does not existerror: read_file takes one path, not a pattern: …; list matches with run_command, e.g. ls …, so the model lists the files instead of retrying the path. A file really named with a wildcard character is read.

A line ends at \n, \r\n, or a lone \r (a classic Mac OS file), so CRLF, CR-only, and mixed files are numbered line by line, exactly as edit_file’s line counts them; the returned lines never include \n or \r.

It reads forward only: there is no search, no tail, and no binary content. To find a line in a large file or see its end, have the model use run_command with grep -n or tail, then read_file at the offset it learns. The file is opened close-on-exec, so no command the model runs meanwhile inherits the handle.

Implementation

FileReader and LineScanner in harness/Sources/WispCore/Tools/FileReader.swift, tested in FileReaderTests including chunk-boundary and early-stop cases; ReadFileTool is the model-facing wrapper.