Better FS Tools
Read tool

Limits

Set how much one read can return, with limits for lines, bytes, and line length, and a token budget.

A read stops at the first limit it gets to. The result then holds the next call in result.continuation.next, and the continue note gives the same call to the model.

Change the limits

Set limits with the values you want to change. The other limits keep their defaults:

import { nodeFileSystem } from "@better-fs-tools/node";
import { createReadTool } from "@better-fs-tools/read";

const cwd = process.cwd();
const fs = nodeFileSystem({ cwd, allowedRoots: [cwd] });
const read = createReadTool({
  fs,
  limits: { maxLines: 500, maxViewBytes: 8 * 1024 },
});
LimitDefaultSets
maxLines2,000The most lines in one read. This is also the default limit and the largest limit
maxViewBytes50 KiBThe most bytes of text in one read
maxCharsPerLine2,000The characters of a line before the tool cuts it
maxScanBytes64 MiBThe bytes the tool scans to count the lines and hash the file
sampleBytes8 KiBThe bytes that classifiers get. Not more than maxScanBytes
maxDirectoryEntries200The most entries in a folder listing
maxSuggestions5The most suggested names when a file is missing
maxConvertBytes64 MiBThe bytes that a converter can read
maxMediaBytes5 MiBThe bytes of images in one result

Each value must be a positive whole number. createReadTool throws TypeError for an unknown limit or a value it cannot use:

TypeError: Unknown read limit: maxLine

defaultReadLimits holds the defaults.

See which limit stopped a read

result.truncation tells you why a read stopped. With the limits above, a read of a 5,000-line log stops at the byte limit:

const result = await read({ path: "logs/server.log" });

if (result.status === "ok") {
  result.truncation; // { truncated: true, reasons: ["bytes"], primary: "bytes" }
  result.continuation.next; // { path: "logs/server.log", offset: 231, limit: 500 }
}

The reasons are lines, bytes, budget, line-length, and scan-limit. primary is the reason that stopped the read.

When the model asks for more lines than maxLines, the tool reads maxLines lines and adds a note:

[read:clamped] The requested line count of 5000 is over the maximum, so at most 500 lines are shown.

Cut long lines

A line longer than maxCharsPerLine is cut, and the tool adds a line-clamped note. The read does not stop:

const read = createReadTool({ fs, limits: { maxCharsPerLine: 80 } });
1|var a=1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+… [line truncated at 80 chars]

[read:line-clamped] Source line 1 exceeded 80 characters and was clamped.

ReadLine.clamped is true for a cut line. The formatter option clampMarker sets the marker. See Customise formatting.

Set a token budget

Set budget to stop a read at a token count. The default is null, with no budget. charsPerToken() counts each line as its length divided by ratio, plus one token:

import { charsPerToken } from "@better-fs-tools/read";

const read = createReadTool({
  fs,
  budget: charsPerToken({ ratio: 4, max: 2_000 }),
});

The read stops before the first line that does not fit:

[read:continue] Output stopped at the view budget. Continue with {"path":"logs/server.log","offset":201,"limit":2000}.

The budget never stops the first line of a read, so a read always returns at least one line.

To count tokens your own way, set budget to an object with an id, a max, and a measure(text) function. The tool calls measure once for each line, so it must be synchronous and fast:

const read = createReadTool({
  fs,
  budget: { id: "words", max: 50, measure: (text) => text.split(/\s+/).filter(Boolean).length },
});

Refuse large files

The limits cut a read short. To refuse a read of a whole large file, use the sizeCeiling() authorizer. See Permissions.

On this page