Pi runtime and agents
DRS uses the Pi SDK (@earendil-works/pi-coding-agent) as its sole agent runtime. By default, the SDK runs in-process inside the CLI. The RuntimeClient in src/runtime/client.ts wraps it and exposes a session-based API used by the workflow engine and the run-agent command.
In-process runtime
src/runtime/client.ts builds a runtime configuration record, then calls createPiInProcessServer in src/pi/sdk.ts. The returned server advertises server.url: 'pi://in-process' and a client that implements the session API.
The runtime configuration is built from the DRS config and includes:
- Tools (
src/runtime/client.tsandsrc/pi/sdk.ts): a fixed allowlist ofRead,Glob,Grep,Bash,write_json_output, and optionallyWrite,Edit,skill,git_diff,read_artifact,drs_check, andwrite_artifact_outputwhen enabled per agent. - Agent entries (
src/runtime/client.ts): one entry per loaded agent with resolvedmodel,prompt,description,color, andtools. - Custom providers (
src/runtime/client.ts,src/pi/sdk.ts):pi.provider.*entries fromdrs.config.yamlare registered with the PiModelRegistry. - Model overrides (
src/runtime/client.ts): per-agent model overrides fromagents.overrides.<id>.model,agents.namespaces.<namespace>.model, environment variables, and review-specific overrides. - Skills (
src/runtime/client.ts): per-agent skill lists resolved bysrc/lib/config.tsand attached to the runtime asagentSkills. - Fix checks (
src/pi/sdk.ts):fix.checksbecome thedrs_checktool, filtered bymatchPathsagainst changed files. - Trace collector (
src/runtime/client.ts): when--traceis enabled, traces are attached to sessions and persisted as workflow artifacts. - Agent permissions (
src/lib/agent-permissions.ts): optional workflow-node policies become Pi tool definitions with runtime-enforced filesystem roots, allow/deny patterns, symbolic-link rejection, and shell isolation.
Agent loading
src/runtime/agent-loader.ts discovers agent Markdown files:
- Project overrides:
.drs/agents/<namespace>/<name>/agent.md. - Packaged built-ins:
.pi/agents/<namespace>/<name>.md.
The first definition wins, so projects can override a packaged agent without replacing the whole file. A project can also add context without overriding the agent prompt by placing a context.md next to agent.md. Global project context is loaded from .drs/context.md and injected into every agent prompt.
An agent id is always fully qualified: <namespace>/<name>. The built-in review agent is review/unified-reviewer. Other namespaces include task, describe, and visual.
Model resolution
src/lib/config.ts resolves the effective model for an agent using this precedence:
- Explicit model from the CLI (
--model). - Environment variable
DRS_AGENT_<NAMESPACE>_<NAME>_MODELor the legacyREVIEW_AGENT_*form. agents.overrides.<id>.model.agents.namespaces.<namespace>.model.agents.default.model(orDRS_DEFAULT_MODEL).
For the unified reviewer, review.unified.model and REVIEW_UNIFIED_MODEL take precedence. For the describer, describe.model or DESCRIBE_MODEL is checked first.
Skills
Skills are Markdown files in SKILL.md format. DRS searches for them in agents.paths.skills if configured, otherwise in .drs/skills, .agents/skills, and .pi/skills (in that order) when those directories exist. The same skill name in an earlier path wins. DRS no longer ships bundled skill installation or synchronization commands; projects provide skill files directly or manage them outside the CLI.
Agents declare skills in their frontmatter or in the config under agents.default.skills, agents.namespaces.<namespace>.skills, or agents.overrides.<id>.skills. The Pi runtime loads the skill content via the read tool and injects it into the agent context.
Custom tools
src/pi/sdk.ts registers custom ToolDefinition objects that are not part of the base Pi SDK:
write_json_output— always enabled; validates and writes a JSON describe-output artifact.write_artifact_output— enabled per-agent whentools.write_artifact_outputis true; writes a self-contained HTML artifact.git_diff— enabled per-agent whentools.git_diffis true; returns a capped git diff for a single file.read_artifact— enabled per-agent whentools.read_artifactis true; returns a review artifact manifest or a specific finding.drs_check— enabled whenfix.checksis configured; runs the matching validation commands.
These tools are how the review agent emits structured output and how the fix agent reads and verifies review artifacts.
When an agent workflow node declares filesystem permissions, src/pi/sdk.ts supplies same-name policy-aware definitions for Pi's built-in read, write, and edit tools plus a scoped delete_file tool. Pi natively limits tools by name but does not expose path allowlists, so DRS uses Pi's pluggable tool operations to enforce paths inside tool execution. DRS custom tools (write_json_output, write_artifact_output, read_artifact, and git_diff) use the same authorizer when permissions are present. Unrestricted bash and drs_check tools are removed from scoped sessions, and the resource loader disables Pi extensions (noExtensions) so an agent cannot load resources outside the policy boundary. Restricted reads also remove aggregate grep, find, ls, and git_diff tools rather than risk traversing or exposing a denied descendant; omit the read rule when the agent needs unrestricted repository evidence.
The authorizer in src/lib/agent-permissions.ts resolves each requested path against the allowed roots, checks allow and deny patterns with minimatch, and rejects symbolic links and multiply-linked write targets. Repository-root reads are allowed even when the working directory itself is the read root; mutations of the working directory root remain denied. ~ and file:// prefixes are normalized before authorization, and paths containing Unicode whitespace are sanitized. Proposed writes are validated by configured afterMutation validators before the file is committed; after a successful write, edit, or deletion the validator returns full bundle-level feedback that the agent can use to repair issues in the same run. After an agent run with write or delete permissions, the post-run comparison returns a structured change set (added, modified, deleted) while rejecting any residual changes outside the same policy.
Session lifecycle
RuntimeClient exposes:
createSession— creates a session and sends the initial prompt.streamMessages— polls the Pi session until the last assistant message is marked complete.waitForCompletion— collects all messages into an array.closeSession— disposes the session.shutdown— closes the in-process server.
Timeouts are controlled by pi.runtime.operationTimeoutMs, pi.runtime.streamTimeoutMs, and pi.runtime.streamPollIntervalMs, with environment overrides (DRS_RUNTIME_*).
Cost reporting
When the runtime reports usage, src/runtime/client.ts uses pricing.models.<model> as a fallback if the runtime cost is zero. Prices are in USD per 1M tokens.
See also
- Architecture for the system context.
- Configuration for model and runtime settings.
- Workflow engine for how agents are invoked from workflows.
- Review workflows for the review agent pipeline.