Skip to content
Chock
The tools (3 of 30)

The tools

The agent has 24 tools, and every one that touches the machine goes through the sandbox: read_file, list_directory, glob, grep, write_file, edit_file, run_command, read_memory and write_memory. Only run_command takes a command. The rest name a path, a pattern, or the text to write, because a person can review a change and cannot review a shell line. sandbox.md says what the boundary is made of.

read_image reads a picture out of the workspace, a screenshot, a diagram, a rendered chart, and gives it to the model as an image. It is the one tool that is not always offered, because a model that cannot read a picture never hears the name, so it cannot spend a turn calling it: the wire format must have a shape for an image, and the provider instance must say it takes one, which is the .capabilities = .{ .images = true } block of that instance in your configuration. The kind of file is read from the content and never from the name, so a text file called plot.png is refused. At most 3145728 bytes.

A foreground run_command is stopped when it reaches the session’s time limit. A call can ask for less with timeout_ms, and never for more: a number past the session’s own limit changes nothing. Anything longer than a couple of minutes belongs in the background instead.

read_guidance reads a document compiled into Chock, so it reaches nothing on the machine at all. ask_user puts one question to the person who started the session, and it grants nothing: a yes there permits no act. set_title names the session, so chock sessions reads as more than a list of identifiers.

read_skill reads one skill: somebody’s written procedure for a task, found on disk at session start. It is offered only to a session that found at least one, and the answer says who wrote it, because a skill from a repository or a package is a stranger’s writing. skills.md is the whole of it.

The last ten are described here or on a page of their own:

Tool What it asks for Page
provide_tool a program the session has not got below
serve_port a port of its own, reachable from your machine below
nix_eval what one Nix expression says nix.md
nix_build one attribute of a flake, built nix.md
spawn_agent a subagent subagents.md
restrict_self a promise the agent cannot take back policy.md
fetch_url one page over http or https actions.md
web_search one query against a search engine search.md
request_action your work carried back into your repository approvals.md
update_plan a task list you can watch running.md

Every call is gated

Every ordinary tool call carries an action name and is put to the same arbiter every other approval goes through. The shipped defaults answer allow for almost all of those names, so a project with no chock.zon of its own gains no prompt, and a project rule of ask on one of them reaches a person mid session. actions.md has every name and what ships with it.

request_action is the one act an agent can ask for by name, and it takes workspace.apply and nothing else. An agent that believes it is finished calls it, and your policy, or you, answer. It never moves a branch of yours: the work lands on a ref of the session’s own, which you read with git log and take with git merge. An agent that has made no commit is told so and nobody is asked, because only a commit is carried back. status.md lists what an agent still cannot ask for.

A program the agent has not got

An agent in a sandbox that wants a program it has not got runs apt install or npm install -g. Neither can work here, because Chock is built on Nix: the agent names a package, Chock realises it, and the program is on the PATH of every tool call after that one.

$ run_command {"argv":["rg","token","notes.txt"]}
! rg was not found on the host PATH ... To get it, call provide_tool with the
  package name, which is often not the program name.
$ provide_tool {"program":"ripgrep"}
  ripgrep is now in this session's toolchain, from nixpkgs#ripgrep.
$ run_command {"argv":["rg","token","notes.txt"]}
  hello token here

It is off until you turn it on

Realising a package runs nix build on your machine, outside the sandbox, so it is checked against the nix.build policy key. The answer decides whether the tool exists at all, so it is read before the tool list the model sees is built, which is before the session starts. A project with no chock.zon answers ask for every key, and an ask at that moment reaches nobody, so the tool is not offered. Turn it on with a rule:

.{
    .policy = .{
        .rules = .{
            .{ .action = "nix.build", .decision = .allow },
        },
    },
}

This is not asked about later. The answer is read once, at the start, and a session that started without the tool does not gain it.

What a name may be

The agent names a package and never anything else. A name holds letters, digits, -, _, + and . and nothing more, so it cannot be a path, a URL, or a flake reference, and where names are looked up is your own Nix flake registry.

A program provisioned this way lasts for that session only. What a project needs every time belongs in flake.nix. See toolchains.md.

Where it came from

A provisioned program is realised by the Nix on your machine, which either builds it here or copies it from a store. Which of the two happened, and where from, is a record Nix keeps. A project can demand that record:

.{
    .nix = .{
        .provenance = .{
            .required = true,
            .from = .{"https://cache.flakehub.com"},
            .built_by = .{"builder-one"},
        },
    },
}
Field What it asks of a path
required it says where it came from, rather than nothing
from every copy in the record names one of these stores
built_by every build in the record ran on one of these machines

A field left out asks nothing, and a block with no fields asks nothing at all, so Nix is never questioned and a session pays no call for it. Each field is read over the whole record: a path built on one machine and then copied through two caches holds one link for each step, and every link has to answer.

A path that fails keeps the program out of the toolchain, and the agent is told what the path says beside what the project asked for. Three cases are refusals rather than warnings: a path Nix holds no record of, a build whose record names no machine while built_by names which machines may build, and a Nix that cannot answer at all. The last one needs Determinate Nix 3.16 or later, which is the first to hold both this record and the flag that asks for it.

What is checked is the path the program is, and not the whole closure it links against. A tool’s dependencies arrive from whichever stores your Nix is set to use, so one store named for every path in a closure would refuse every ordinary nixpkgs build. .from is a statement about the tool.

Your own chock.zon and the project’s are read the way every other field in the nix block is read: the project’s block wins when it has one. An org bundle cannot set this.

A port you can open in a browser

A web project’s agent starts a dev server and the person wants to look at the page. serve_port asks for that: the agent starts the server in a background run_command, then names the port it is listening on.

$ run_command {"argv":["npm","run","dev"],"background":true}
  started in the background
$ serve_port {"port":3000}
  http://127.0.0.1:3000 now reaches port 3000 inside this session's background
  commands.

The port is answered under net.serve.3000, so the project’s policy decides, and where it says ask a person does. actions.md has the rule and what a refusal says. The tool is offered only where a rule could permit a port, the same way provide_tool appears only where nix.build is answered: a project that says nothing about serving is not offered it at all.

A server in a foreground call cannot be published. That call holds the turn until the command ends, so there is no moment at which the page exists and somebody could open it.

The tool is Linux only. On macOS a session says that no port is open, because the path inward is a network namespace the platform does not have.

What it costs

A request holds the turn and has no deadline. nix build on a cache miss is minutes, and the turn waits for it, because the answer has to reach the mount set of the very next tool call, not the model. A line is printed before the wait so the terminal is not silent, and Ctrl-C reaches the nix child the same way it reaches a subagent. A build that never ends holds the session, which status.md lists as an open item.