How the AI SDK Tool Layer Handles Pagination for `ls`, `find`, and `grep`
The AI SDK tool layer handles pagination by accepting a standard { limit?, cursor? } object in each tool's payload, then applying result slicing after the core filesystem operation completes—either through workspace-level pagination for directory listings or via a shared applyPagination helper for search operations.
Pagination in AI-powered filesystem tools prevents overwhelming responses when querying large codebases. In the @cloudflare/computer package, the AI SDK tool layer implements a consistent pagination contract across the three primary exploration tools: ls, find, and grep. This article examines how each tool processes pagination parameters, where the slicing logic resides, and how the cursor-based system enables efficient result navigation.
How Pagination Flows Through the Tool Architecture
The AI SDK constructs its tool set in packages/computer/src/tools/ai.ts. Each filesystem tool—ls, find, and grep—receives pagination options through the tool-call payload and applies them at a specific stage of execution.
The pagination object follows a standard interface:
interface PaginationOptions {
limit?: number; // Maximum entries to return
cursor?: string; // Opaque token from previous response
}
Tools return results with a cursor field when more data exists, enabling iterative page retrieval.
ls Tool: Workspace-Level Pagination
The ls tool delegates pagination directly to the workspace filesystem.
Implementation location: createListTool in packages/computer/src/fs/list.ts
When the AI client calls ls with pagination parameters, the tool invokes:
workspace.list(path, { limit, cursor })
The workspace's list method returns an object shaped as { entries, cursor }, where the cursor field contains an opaque token for the next page. This pushes pagination responsibility to the storage layer, which can optimize based on its internal indexing.
Example: Paginated directory listing
await sdk.runTool("ls", {
path: "/my/project",
pagination: { limit: 10 } // Returns up to 10 entries and a next-page cursor
});
The response includes entries (the directory items) and optionally cursor for subsequent requests.
find Tool: Merge-Then-Slice Pattern
The find tool implements a different pattern: collect all results, merge pending state, then paginate.
Implementation location: createFindTool in packages/computer/src/fs/find.ts
Execution follows three stages:
- Filesystem walk — Recursively searches directories matching the query
- Pending file merge — Combines any staged/uncommitted files into the result set
- Pagination application — Slices results using
applyPagination(pagination, results)
The applyPagination helper (exported from packages/computer/src/fs/pagination.ts) handles the actual slicing and cursor generation.
Example: Paginated file search
await sdk.runTool("find", {
path: "/my/project",
query: "*.js",
pagination: {
limit: 25,
cursor: "eyJvZmZzZXQiOjI1fQ==" // Opaque cursor from previous response
}
});
The test suite in packages/computer/src/fs/find.test.ts verifies that pending files merge before pagination applies, ensuring uncommitted changes appear in correct page positions.
grep Tool: Filter-Then-Slice Pattern
The grep tool adds a pre-pagination filtering step for glob patterns.
Implementation location: createGrepTool in packages/computer/src/fs/grep.ts
Execution order:
- Glob filtering — Applies
includepattern to narrow candidate files - Regex execution — Runs pattern match against filtered files
- Pagination application — Uses
applyPagination(pagination, matches)to slice results
This ensures pagination operates on the actual match set, not the raw file list.
Example: Paginated content search
await sdk.runTool("grep", {
path: "/my/project",
pattern: "TODO",
include: "**/*.ts", // Pre-filters to TypeScript files only
pagination: { limit: 50 } // Returns first 50 matches with cursor
});
The test in packages/computer/src/fs/grep.test.ts confirms that include filtering occurs before pagination, preventing empty or partial pages when glob patterns exclude many files.
Shared Pagination Helper: applyPagination
Both find and grep rely on applyPagination from packages/computer/src/fs/pagination.ts. This utility:
- Accepts the pagination options object and full result array
- Calculates slice bounds from
limitandcursor - Returns the paginated subset plus a new
cursor(orundefinedif exhausted)
Using a centralized helper ensures consistent cursor encoding, edge-case handling, and behavior across search tools.
Test Coverage for Pagination Propagation
The AI SDK test suite validates pagination at multiple integration points:
| Test file | Coverage |
|---|---|
packages/computer/src/tools/ai.test.ts |
Verifies pagination options pass through tool-call payload to underlying filesystem operations |
packages/computer/src/fs/find.test.ts |
Confirms pending files merge before pagination slicing |
packages/computer/src/fs/grep.test.ts |
Validates include filtering precedes pagination application |
These tests ensure the pagination contract remains stable across tool implementations.
Summary
lsdelegates pagination toworkspace.list()for storage-layer optimizationfindmerges pending files, then appliesapplyPagination()to slice resultsgrepfilters byincludeglob first, then appliesapplyPagination()to matches- All three tools accept the same
{ limit?, cursor? }interface for consistent client usage - Cursor generation is handled by the shared
applyPaginationhelper inpackages/computer/src/fs/pagination.ts
Frequently Asked Questions
What format does the pagination cursor use?
The cursor is an opaque base64-encoded string representing the offset state. Clients treat it as an opaque token—decode internals are managed by applyPagination in packages/computer/src/fs/pagination.ts. Passing the returned cursor to the next request continues pagination from the correct position.
Why does ls use workspace-level pagination while find and grep slice after collection?
ls operates on indexed directory structures where the storage layer can efficiently paginate without loading full listings. find and grep perform recursive filesystem traversals that must complete to produce accurate results—particularly for pending file merge and glob filtering—making post-operation slicing necessary for correctness.
How do I know if more results exist after a paginated request?
Check the cursor field in the response. If present and non-null, additional pages exist. If cursor is undefined or omitted, the result set is exhausted. The test suite in packages/computer/src/tools/ai.test.ts validates this propagation behavior.
Can I use pagination without specifying a limit?
Yes—omit the limit parameter to receive default-sized pages. However, explicit limit values are recommended for predictable response sizes when integrating with AI context windows that have token constraints.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →