Skip to content

packages coding agent tools

Zachary BENSALEM edited this page Aug 15, 2026 · 1 revision

Agent tools

Active contributors: Mario Zechner, kt, Armin Ronacher

The tools are the actions the model can invoke during a turn. The default built-in tool is ipython, a persistent Jupyter kernel that also hosts %%bash shell cells; bash and edit tool definitions exist as factories and are wired in by custom runtimes (via baseToolsOverride in packages/coding-agent/src/core/agent-session.ts) or by the TUI renderer for replay. All built-in tool factories and the registry live in packages/coding-agent/src/core/tools/, and the kernel implementation lives in packages/coding-agent/src/core/kernel/.

Purpose

  • Give the model a small, safe tool surface: a persistent Python kernel (ipython), shell execution (bash), and exact-text file edits (edit), plus extension-defined custom tools.
  • Stream partial output to clients while a tool runs and deliver a final result (or error) when it finishes.
  • Keep tool implementations replaceable: BashOperations, EditOperations, and IpythonKernelProvisioner are the seams for remote or custom execution.

Directory layout

packages/coding-agent/src/core/tools/
├── index.ts              # tool registry: createTool, createToolDefinition, createAllToolDefinitions
├── ipython.ts            # ipython tool + IpythonKernelProvisioner
├── ipython-cell-code.ts  # parseIpythonBashCell (%%bash cell handling)
├── bash.ts               # bash tool definition + BashOperations + createLocalBashOperations
├── edit.ts               # edit tool definition + EditOperations
├── edit-diff.ts          # fuzzy matching, edit application, unified diff generation
├── code-preview.ts       # previewBashCommand: classify and preview command/code text
├── file-mutation-queue.ts# withFileMutationQueue: serialize writes per file
├── output-accumulator.ts # bounded-memory streaming output with temp-file fallback
├── truncate.ts           # DEFAULT_MAX_BYTES / DEFAULT_MAX_LINES, truncateHead/Tail/Line
├── path-utils.ts         # resolveToCwd
├── render-utils.ts       # getTextOutput, invalidArgText, shortenPath
└── tool-definition-wrapper.ts # wrapToolDefinition (AgentTool <-> ToolDefinition)

packages/coding-agent/src/core/kernel/
├── index.ts              # KernelManager (Jupyter over ZeroMQ), host request bridge
├── bootstrap.ts          # ensureKernelPython: builds the kernel venv (ipykernel + prime-agent-runtime)
├── boot-gate.ts          # withKernelBootPermit: bounded concurrent kernel boots
├── fork-server.ts        # Linux forkserver fast path for kernel spawn
├── fork-server-script.ts # Python side of the forkserver
├── state-snapshot.ts     # dill-based namespace snapshot + restore
└── bootstrap-cli.ts      # CLI entry for test bootstrap

packages/coding-agent/src/core/
├── bash-executor.ts      # executeBashWithOperations: user-initiated bash (AgentSession.executeBash)
└── exec.ts               # execCommand: shared shell execution for extensions and custom tools

Key abstractions

Type / function Full path One-line description
createAllToolDefinitions packages/coding-agent/src/core/tools/index.ts Built-in registry; returns the ipython tool definition (the default active tool)
createIpythonToolDefinition / createIpythonTool packages/coding-agent/src/core/tools/ipython.ts The ipython tool: executes Python or %%bash cells in a persistent kernel
IpythonKernelProvisioner packages/coding-agent/src/core/tools/ipython.ts Owns lazy create, start, bootstrap, prewarm, kill, and dispose of one session's kernel
KernelManager packages/coding-agent/src/core/kernel/index.ts Jupyter kernel client over ZeroMQ: start, execute, shutdown, restart, kill, snapshots
ensureKernelPython packages/coding-agent/src/core/kernel/bootstrap.ts Ensures a kernel venv with ipykernel and prime-agent-runtime, then runs an rlm bootstrap cell
withKernelBootPermit packages/coding-agent/src/core/kernel/boot-gate.ts Semaphore bounding concurrent kernel startups
createBashToolDefinition / createBashTool packages/coding-agent/src/core/tools/bash.ts The bash tool: streams a shell command with truncation and timeout
BashOperations packages/coding-agent/src/core/tools/bash.ts Pluggable exec backend; createLocalBashOperations is the local shell default
createEditToolDefinition / createEditTool packages/coding-agent/src/core/tools/edit.ts The edit tool: exact-text replacement with a rendered diff
applyEditsToNormalizedContent packages/coding-agent/src/core/tools/edit-diff.ts Applies edits with exact-then-fuzzy matching and overlap checks
computeEditsDiff packages/coding-agent/src/core/tools/edit-diff.ts Computes a preview diff without applying the edit
withFileMutationQueue packages/coding-agent/src/core/tools/file-mutation-queue.ts Serializes read-modify-write operations per file path
previewBashCommand packages/coding-agent/src/core/tools/code-preview.ts Classifies text as bash or python and returns a redacted one-line preview
executeBashWithOperations packages/coding-agent/src/core/bash-executor.ts Streaming bash execution for user-initiated commands (!, !!)
execCommand packages/coding-agent/src/core/exec.ts Simple spawn wrapper (stdout/stderr/code) for extensions and custom tools

How it works

Registration

The registry in packages/coding-agent/src/core/tools/index.ts currently produces one built-in tool, ipython (createAllToolDefinitions returns { ipython }). AgentSession._buildRuntime in packages/coding-agent/src/core/agent-session.ts constructs an IpythonKernelProvisioner per session, builds the base tool definitions, then merges them with extension-registered tools and SDK custom tools into a definition registry; only names in the active set are exposed to the model, defaulting to ["ipython"] unless baseToolsOverride provides others. createBashTool and createEditTool are exported from packages/coding-agent/src/core/tools/index.ts for callers that want those tools with a custom cwd, and the interactive renderer (packages/coding-agent/src/modes/interactive/components/tool-execution.ts) uses createBashToolDefinition and createEditToolDefinition to render and replay built-in bash and edit calls.

The IPython tool

The ipython tool is the default and the most complex. Its execute in packages/coding-agent/src/core/tools/ipython.ts routes the cell through IpythonKernelProvisioner.ensure(), which lazily starts the kernel on first use (or joins an in-flight or prewarmed startup). KernelManager.start in packages/coding-agent/src/core/kernel/index.ts resolves the Python interpreter via ensureKernelPython (which builds a venv containing ipykernel and prime-agent-runtime when needed), connects to the kernel's ZeroMQ shell, iopub, and control channels, and on Linux can fork a pre-imported kernel through packages/coding-agent/src/core/kernel/fork-server.ts instead of a full cold boot. After start, the provisioner restores the prior session's namespace from the dill snapshot in packages/coding-agent/src/core/kernel/state-snapshot.ts and runs the rlm bootstrap cell.

Cell execution goes through KernelManager.execute, serialized by an execution queue. Cells can make typed host requests back to the TypeScript session over a Jupyter comm (HOST_COMM_TARGET), which is how rlm.run, goal management, and skills call into the session. A cell that ignores an interrupt triggers a busy-kernel choice (wait, kill and restart, or cancel). Results carry stdout, stderr, result, tracebacks on error, attachments, diffs, and sent agent messages; the tool returns text plus optional image blocks. The tool is marked executionMode: "sequential" because the kernel is single-threaded.

The bash tool and user bash

The model-facing bash tool in packages/coding-agent/src/core/tools/bash.ts runs a command through BashOperations.exec (defaulting to createLocalBashOperations, which spawns the configured shell, streams stdout/stderr, and kills the process tree on abort or timeout). Output accumulates in packages/coding-agent/src/core/tools/output-accumulator.ts, which keeps a bounded tail and spills full output to a temp file; truncation metadata (lines shown, full path) is attached to the result details. Streamed chunks are emitted through the tool's onUpdate callback.

User-initiated bash is separate. AgentSession.executeBash and runUserBash in packages/coding-agent/src/core/agent-session.ts back the ! and !! prefixes, using executeBashWithOperations in packages/coding-agent/src/core/bash-executor.ts, which sanitizes output and stores full output to a temp file when truncated. Commands run with !! are excluded from LLM context. packages/coding-agent/src/core/exec.ts provides a smaller execCommand (no streaming, returns stdout/stderr/code) for extensions and custom tools.

The edit tool

The edit tool in packages/coding-agent/src/core/tools/edit.ts takes a path and one or more exact-text replacements. applyEditsToNormalizedContent in packages/coding-agent/src/core/tools/edit-diff.ts normalizes line endings and BOM, matches each oldText exactly first and then with fuzzy matching (trailing whitespace, Unicode quotes and dashes normalized), rejects non-unique or overlapping edits, applies replacements in reverse offset order, and restores the original line endings. The tool returns a unified diff with line numbers via generateDiffString, and the TUI pre-renders the diff before execution using computeEditsDiff. Writes go through withFileMutationQueue so concurrent edits to the same file serialize.

Tool-call event flow

The model's tool calls surface to clients as three agent events emitted by the Agent loop in packages/agent: tool_execution_start (call id, name, args), tool_execution_update (partial output while running), and tool_execution_end (final result and isError). Tools push partial output through their onUpdate callback; the bash tool throttles these updates at 100ms and the ipython tool streams cell output chunks. Clients render this as a per-call lifecycle: input-streaming while the call renders and streams, output-available when partial or final output exists, and output-error when the call ends with isError (see Glossary). Extensions observe the same calls through the tool_call and tool_result events in packages/coding-agent/src/core/extensions/types.ts, where the input is typed per tool (BashToolCallEvent, EditToolCallEvent, IpythonToolCallEvent, CustomToolCallEvent).

sequenceDiagram
    participant A as Agent (pi-agent-core)
    participant AS as AgentSession
    participant T as ipython tool
    participant P as IpythonKernelProvisioner
    participant K as KernelManager
    participant PY as IPython kernel
    participant S as Subscribers (clients)

    A->>T: execute(toolCallId, {code})
    T->>P: ensure() (lazy start or prewarm)
    P->>K: start (venv bootstrap, rlm runtime)
    K->>PY: execute cell over ZMQ
    PY-->>K: stdout / result / error (or host request)
    K-->>T: ExecuteResult
    T-->>A: content + details (status, diffs, attachments)
    A-->>AS: tool_execution_end event
    AS-->>S: AgentSessionEvent (tool result)
    Note over A,S: bash and ipython stream partial output via onUpdate -> tool_execution_update
Loading

Integration points

  • AgentSession builds and owns the provisioner in _buildRuntime (packages/coding-agent/src/core/agent-session.ts), prewarming the kernel when configured or when a resume has a snapshot, and disposing it on session dispose so a final snapshot flushes.
  • web/server consumes IpythonKernelProvisioner for kernel readiness and restarts; see Web server.
  • Extensions add tools via defineTool and the tool_call / tool_result hooks; see Extensions.
  • The SDK exports createBashTool, createEditTool, createIpythonTool, and withFileMutationQueue for custom tool setups (e.g. custom cwd) from packages/coding-agent/src/core/sdk.ts.
  • Kernel host requests are how the Python runtime (prime-agent-runtime) calls back into the session for rlm.run and skills; see RLM runtime and prime-agent-runtime.

Entry points for modification

  • Add a built-in tool: add a factory in packages/coding-agent/src/core/tools/, register it in packages/coding-agent/src/core/tools/index.ts, and wire it in _buildRuntime in packages/coding-agent/src/core/agent-session.ts.
  • Change kernel startup or the Python environment: packages/coding-agent/src/core/kernel/bootstrap.ts (venv and rlm bootstrap) and packages/coding-agent/src/core/kernel/index.ts (kernel spawn, execute, dispose).
  • Change how output is truncated or streamed: packages/coding-agent/src/core/tools/output-accumulator.ts and packages/coding-agent/src/core/tools/truncate.ts.
  • Change edit matching or diff rendering: packages/coding-agent/src/core/tools/edit-diff.ts.
  • Add a remote execution backend: implement BashOperations or EditOperations and pass them to createBashTool / createEditTool.

Key source files

File Purpose
packages/coding-agent/src/core/tools/index.ts Tool registry and factories
packages/coding-agent/src/core/tools/ipython.ts The ipython tool and IpythonKernelProvisioner
packages/coding-agent/src/core/kernel/index.ts KernelManager, host request comm bridge
packages/coding-agent/src/core/kernel/bootstrap.ts Kernel venv bootstrap and rlm runtime cell
packages/coding-agent/src/core/kernel/boot-gate.ts Concurrent boot semaphore
packages/coding-agent/src/core/kernel/state-snapshot.ts Kernel namespace snapshot and restore
packages/coding-agent/src/core/kernel/fork-server.ts Linux forkserver fast kernel spawn
packages/coding-agent/src/core/tools/bash.ts The bash tool and BashOperations
packages/coding-agent/src/core/tools/edit.ts The edit tool and EditOperations
packages/coding-agent/src/core/tools/edit-diff.ts Edit application and diff generation
packages/coding-agent/src/core/bash-executor.ts User-initiated bash execution
packages/coding-agent/src/core/exec.ts Shared execCommand for extensions and custom tools

Related pages

  • Coding agent - package overview
  • Session runtime - how the session runs tools and persists results
  • Extensions - custom tools and tool_call / tool_result hooks
  • Skills - Python skills that run in the kernel and use host requests
  • RLM runtime - recursive subagents spawned from the kernel
  • Daemon - kernels in worker processes

Clone this wiki locally