Where Workflow Lives: Tools, Extensions, and Packages

Pi's flexibility becomes concrete at the policy surface: the place where a model receives instructions, discovers capabilities, and asks for effects in a repository. This chapter examines how Pi separates tools, hooks, resources, and packages so you can place a workflow requirement in the right layer. The practical question is not “how do I add a feature?” but “does this requirement change what the model can do, whether a proposed effect is allowed, how the user sees it, or what survives the session?”

That question builds directly on the provider boundary. A model can be swapped without changing the agent loop, but a model still needs tools and context to do useful coding work. Pi supplies defaults, then exposes enough of the surrounding machinery for a project or application to reshape them. The design is powerful because it avoids a fork. It is also consequential because each added tool or instruction becomes part of the system's behavior and trust surface.

A tool is an effect; a hook is a policy

A tool is the part of Pi that the model can call. Its definition gives the model a name and description, and its TypeBox schema describes the arguments that are valid. Its implementation performs work and returns content for the model plus optional details for the interface, state management, or usage accounting. In the coding-agent package, the familiar defaults cover reading, writing, editing, and shell execution. Read-only search and listing tools can be enabled when a workflow needs discovery without modification.

The distinction between definition and implementation matters. The model sees a description and schema; the host owns the function that interprets the validated arguments. Pi can therefore keep one tool-facing protocol while allowing the implementation to use local files, a remote machine, an SSH connection, or a container-backed operation. A remote implementation does not make the tool intrinsically safer. It changes where the effect occurs and which host must enforce its boundary.

Before execution, the agent core locates the registered tool and validates the model's arguments. A beforeToolCall hook can then inspect the validated call and block it with a reason. This is where a project-specific policy belongs: refuse writes to a protected path, ask for confirmation before a destructive command, or require a condition that the generic tool definition cannot express. If a hook blocks the call, Pi reports an error tool result to the model rather than hiding the decision from the conversation.

An afterToolCall hook serves a different purpose. It runs after the implementation returns and can replace the result's content, details, usage, or error state before the result is delivered and stored. A tool can return structured details for a custom renderer while sending a short explanation to the model. An extension can normalize the result of a wrapped tool. The hook does not undo an effect that already happened; it changes how the outcome is represented to later parts of the harness.

That ordering gives a simple placement rule. If the model needs a new capability, define a tool. If an existing capability needs a gate or a result policy, use a hook. If the user needs to see the outcome differently, use a renderer. If the behavior needs a reusable instruction rather than an executable effect, use a skill or prompt resource. Keeping those jobs separate prevents a tool from becoming an opaque bundle of authorization, presentation, and memory logic.

Concurrency makes the placement rule more important. Pi can execute independent tool calls in parallel, after preparing them in assistant source order.