Getting Started: platform concepts, deployment, and the feature surface of a running Everruns
---
# Everruns Documentation
> Tutorials, how-to guides, reference, and explanation for building and operating Everruns agents.
Source:
Everruns is a **durable agentic harness engine** built on Rust. It provides APIs for managing agents, sessions, and long-running tasks, with real-time SSE event streaming and PostgreSQL-backed durability. Build directly inside a Rust application with the [Everruns Framework](https://docs.everruns.com/framework/), or use the Platform and SDK documentation for a durable deployment and remote clients. To skip operating the Platform entirely, use the hosted edition at [Everruns Cloud](https://app.everruns.com). ## What kind of help do you need? The documentation is organised by what you’re trying to do. [Everruns Cloud](https://app.everruns.com) — Use the hosted Platform without running it yourself. Open in early access, free for now, bring your own model provider keys. [Framework](https://docs.everruns.com/framework/) — Build and run agents in a Rust process with the application-facing everruns crate. [Learn — Tutorials](https://docs.everruns.com/tutorials/run-an-agent/) — Step-by-step lessons that end with a running agent. Start here if you're new. [Do — How-to guides](https://docs.everruns.com/how-to/) — Task-oriented recipes for common problems. Use these when you know what you want to build. [Understand — Explanation](https://docs.everruns.com/explanation/) — Background and design rationale. Read these when reference docs aren't enough on their own. [Look up — Reference](https://docs.everruns.com/api/) — API endpoints, event types, capability catalog, CLI flags, environment variables. The dry stuff. ## Getting started fast 1. Choose [Everruns Cloud](https://app.everruns.com) for the hosted Platform, the [Framework](https://docs.everruns.com/framework/) for an in-process Rust application, or [Docker Compose](https://docs.everruns.com/getting-started/docker-compose/) to run the full Platform yourself. 2. Run the [Framework quickstart](https://docs.everruns.com/framework/quickstart/) or follow the [SDK tutorial](https://docs.everruns.com/tutorials/building-agents-using-sdk/). 3. Browse [How-to guides](https://docs.everruns.com/how-to/) to do something specific. ## Popular destinations [Core concepts](https://docs.everruns.com/explanation/concepts/) — Harness, agent, session, capability, event — what they are and how they compose. [Capabilities catalog](https://docs.everruns.com/capabilities/) — Every built-in capability with tools, parameters, and dependencies. [Event reference](https://docs.everruns.com/event-reference/) — Every event type in the Everruns event protocol, with payloads. [REST API reference](https://docs.everruns.com/api/) — OpenAPI-generated reference for every endpoint. [Everruns Framework](https://docs.everruns.com/framework/) — Application-facing Rust agents, models, tools, sessions, events, and extension points. [SDKs](https://docs.everruns.com/features/sdk/) — Official client libraries for Rust, Python, and TypeScript. [CLI](https://docs.everruns.com/features/cli/) — Command-line interface for managing agents, sessions, and conversations. [Architecture](https://docs.everruns.com/explanation/architecture/) — Control plane, workers, durable execution — and why. [Environment variables](https://docs.everruns.com/sre/environment-variables/) — Every configuration knob for the control plane and workers. ## More features [Agent triggers](https://docs.everruns.com/features/agent-triggers/) — Wake an agent on a recurring schedule and inspect its runs. [Session participants](https://docs.everruns.com/features/session-participants/) — Invite agents into a shared session and address one for a turn. [Agent and user memory](https://docs.everruns.com/features/memory-scopes/) — Learn the persistence, mount paths, and privacy rules for scoped memory.
---
# Author an agent blueprint
> Contribute a code-defined specialist agent from a capability, with a typed configuration contract the spawn path enforces
Source:
An **agent blueprint** is a code-defined specialist agent: a baked-in prompt, a set of private tools, a model-selection strategy, an iteration bound, and a narrow configuration surface. A host agent delegates to it through the ordinary [sub-agents](https://docs.everruns.com/capabilities/sub-agents/) tool without gaining access to its internals. Reach for a blueprint when work needs different tools, instructions, or model economics than the parent agent — repository scouting, catalog benchmarking, any job where the parent should get the answer without carrying the tools that produced it. Blueprints are not persisted user-created agents; they ship with a capability and are available wherever that capability is enabled. ## Contribute the blueprint A capability contributes blueprints by implementing `agent_blueprints()`. The returned `AgentBlueprint` carries everything the child runtime needs:
```rust
fn agent_blueprints(&self) -> Vec {
vec![AgentBlueprint {
id: "repo_scout",
name: "Repo Scout",
description: "Search repositories for code, files, and issues. \
Read-only agent for codebase exploration.",
model: BlueprintModel::Fixed("claude-haiku-4-5-20251001".to_string()),
system_prompt: REPO_SCOUT_PROMPT,
tools: vec![Box::new(SearchCodeTool), Box::new(ReadFileTool)],
max_turns: Some(15),
config_schema: Some(json_schema_for::()),
}]
}
```
The `description` is what a parent agent reads when deciding whether to delegate, so write it as a routing decision: what the blueprint is for, and when to pick it. `model` chooses one of three strategies. `Fixed` pins a model the host cannot override, which suits specialist work with a known cost/quality target. `Default` names a model the validated config may override. `Inherit` takes the parent’s model, for work that genuinely needs the parent’s characteristics. Tools listed here are **private**. They are instantiated only for the blueprint’s own child session and never appear in the host agent’s tool list. ## Define configuration as a type Derive the config schema from a Rust struct rather than writing JSON by hand. The struct is the single source of truth: field set, bounds, defaults, and descriptions all reach the spawning agent from one place.
```rust
use everruns_capability::json_schema_for;
use everruns_capability::schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// The same ceiling the search tools apply to their own arguments.
const MAX_REPOS: u32 = 50;
/// Configuration for the repository scout.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields, default)]
#[schemars(crate = "everruns_capability::schemars")]
pub struct RepoScoutConfig {
/// Maximum number of repositories to scan.
#[schemars(range(min = 1, max = MAX_REPOS))]
pub max_repos: u32,
}
impl Default for RepoScoutConfig {
fn default() -> Self {
Self { max_repos: 10 }
}
}
```
Three habits keep the contract honest: * **Express each bound once.** Write it as a constant shared by the schema attribute and the code that enforces it at runtime, so a schema edit cannot drift from the clamp it describes. * **Write doc comments for the caller.** They become the schema descriptions the spawning agent reads. Implementation notes belong in ordinary `//` comments. * **Close the object** with `deny_unknown_fields` unless the blueprint genuinely accepts open configuration. Requiring configuration is a matter of leaving a field without a default: fields serde cannot default become `required` in the derived schema, and the spawn path rejects a call that omits them. ## What the spawn path enforces Configuration is validated against the derived schema **before** a child session exists, so a declared bound constrains the host rather than merely advising the model. A spawn is rejected when: * a value violates the schema — out of range, wrong type, missing a required property; * the config carries a key the schema does not define (with `deny_unknown_fields`); * the blueprint declares no schema at all but config was supplied. The error returned to the calling agent names the violations and includes the schema, so a model can usually correct its own call and retry. Validation governs what a **host** may configure. It is not a substitute for a tool checking its own arguments: keep the runtime clamps in the blueprint’s tools, so a misbehaving child agent stays inside the same envelope. Configuration can only select behavior the blueprint intentionally exposes. It cannot replace the system prompt, inject tools, bypass model policy, or expand capability permissions. ## Delegation and lifetime A spawned blueprint session is a real durable session. It takes part in the same message, event, task, workspace, cancellation, and recovery infrastructure as any other sub-agent; the difference is only how its runtime is assembled. The session persists the blueprint identity and its validated config, so a worker can reconstruct the same runtime after a retry or handoff. The blueprint itself stays a stateless template. Follow-ups address the durable session, not the blueprint. ## Worked examples Two blueprints ship in-tree and are worth reading as references: * [`integrations/github/src/lib.rs`](https://github.com/everruns/everruns/blob/main/integrations/github/src/lib.rs) — the minimal case: one config field, a pattern constant shared with the runtime check that enforces it. * [`integrations/openrouter/src/model_scout.rs`](https://github.com/everruns/everruns/blob/main/integrations/openrouter/src/model_scout.rs) — the fuller case: numeric bounds tied to runtime constants, a nested config type, and a spend budget. ## Related * [Sub-agents](https://docs.everruns.com/capabilities/sub-agents/) — the delegation tool blueprints are invoked through * [GitHub Scout](https://docs.everruns.com/capabilities/github-scout/) — a shipped blueprint from the caller’s side
---
# Budgets
> Cap session spending in dollars, tokens, or custom credits, with soft pause thresholds and automatic enforcement.
Source:
Budgets let you cap how much a session (or agent, user, or organization) can spend. When a session hits its budget, Everruns stops scheduling further LLM calls. This prevents runaway costs from long-running or misbehaving agents.  ## How It Works After every LLM generation, Everruns computes the cost and debits it from any active budgets for that session. In subagent trees, session-scoped budgets are shared at the root session, so child and grandchild turns spend from the same pool. If the balance reaches zero, the session stops. 1. **LLM call completes**: Everruns extracts token counts (input + output). 2. **Compute debit**: Converts tokens to the budget’s currency: * `usd`, uses per-model pricing (cost per million tokens for input/output) * `tokens`, raw token count * `credits`, 1 credit = 1,000 tokens * Custom currencies fall back to token count 3. **Debit ledger**: Appends an immutable ledger entry and updates the balance. 4. **Evaluate rules**: Checks thresholds: * Balance at 20% of limit → warning event * Spending exceeds soft limit → session pauses * Balance reaches zero → session stops Enforcement is **post-hoc**: the check runs after each LLM call, not before. This avoids blocking the hot path. The last generation may slightly overshoot the limit, this is expected and by design. ## Currencies | Currency | Unit | How cost is calculated | | --------- | ----------------------- | ----------------------------------------------------------------------------- | | `usd` | US dollars | Per-model pricing from model profiles (input/output rates per million tokens) | | `tokens` | Raw tokens | Direct count of input + output tokens | | `credits` | 1 credit = 1,000 tokens | Token count divided by 1,000 | | Custom | Any string | Falls back to raw token count | USD budgets use real per-model pricing. A $10 budget on GPT-4o will last much longer than $10 on Claude Opus, because the per-token cost differs. ## Soft Limits and Pausing A **soft limit** pauses the session before hitting the hard stop. This is useful in interactive sessions where a human can decide to top up or stop. 1. Spending exceeds `soft_limit` → budget status becomes `paused` 2. The worker detects the pause between atoms → stops scheduling the next LLM call 3. Session transitions to `paused` state 4. User can resume by increasing the limit, topping up, or calling the resume endpoint For headless sessions (no human watching), the hard limit fires at balance zero and terminates the turn. ## Stacked Budgets Multiple budgets can apply to the same session. The **most restrictive** budget wins. This lets you combine different types of limits: * A **$10 USD session budget** caps dollar cost * A **2M token budget** caps total token usage regardless of model pricing Budget stacking is enforced across the session hierarchy: root session, app channel, app, agent, user, and organization. The most restrictive matching budget wins. ## CLI Usage Set a budget when creating a session:
```bash
# $10 USD budget (currency defaults to usd)
everruns sessions create --budget-limit 10
# Explicit currency
everruns sessions create --budget-limit usd:10
# With soft limit — pauses at $8, hard stop at $10
everruns sessions create --budget-limit usd:10 --budget-soft-limit usd:8
# Token budget
everruns sessions create --budget-limit tokens:2000000
# Stacked — both limits, whichever hits first
everruns sessions create --budget-limit usd:10 --budget-limit tokens:2000000
```
## MCP Usage The `agent_run` MCP tool accepts budget parameters directly:
```json
{
"name": "agent_run",
"arguments": {
"message": "Analyze this codebase",
"agent_id": "agent_abc123",
"budget_limit": 10.00,
"budget_currency": "usd",
"budget_soft_limit": 8.00
}
}
```
Budget operations are also available as catalog commands via the `execute` tool:
```bash
# Create a budget for an existing session
create_budget --subject_type session --subject_id ses_xxx \
--currency usd --limit 10 --soft_limit 8
# Check a session's budget status
check_session_budgets --session_id ses_xxx
# Top up an exhausted budget
top_up_budget --budget_id bdg_xxx --amount 5 --description "Extra allowance"
# List all budgets for a session
list_session_budgets --session_id ses_xxx
```
## API ### Budget CRUD
```plaintext
POST /v1/budgets Create budget
GET /v1/budgets List budgets (?subject_type=&subject_id=)
GET /v1/budgets/{id} Get budget with current balance
PATCH /v1/budgets/{id} Update limit / soft_limit / status
DELETE /v1/budgets/{id} Soft-delete (sets status=disabled)
```
### Budget operations
```plaintext
POST /v1/budgets/{id}/top-up Add credits (negative ledger entry)
GET /v1/budgets/{id}/ledger Paginated ledger entries (?limit=&offset=)
GET /v1/budgets/{id}/check Check budget status
```
### Session shortcuts
```plaintext
GET /v1/sessions/{id}/budgets List budgets for this session
GET /v1/sessions/{id}/budget-check Check all budgets (session + hierarchy)
POST /v1/sessions/{id}/resume Resume paused budgets
```
### Create a session budget
```bash
curl -X POST https://your-instance/api/v1/budgets \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject_type": "session",
"subject_id": "ses_01abc...",
"currency": "usd",
"limit": 10.00,
"soft_limit": 8.00
}'
```
### Top up an exhausted budget
```bash
curl -X POST https://your-instance/api/v1/budgets/{budget_id}/top-up \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 5.00,
"description": "Emergency top-up"
}'
```
If the budget was `paused` or `exhausted` and now has positive balance, it automatically reactivates. ## Events Subscribe to budget events via SSE to react in real time: | Event | When | Key data | | ------------------ | ------------------------------- | ------------------------------------------- | | `budget.warning` | Balance at 20% of limit | `budget_id`, `balance`, `limit`, `currency` | | `budget.paused` | Spending exceeds soft limit | `budget_id`, `balance`, `soft_limit` | | `budget.exhausted` | Balance reaches zero | `budget_id`, `balance`, `limit` | | `budget.resumed` | User resumes after pause/top-up | `budget_id`, `balance`, `limit` | ## Agent Awareness The `budgeting` capability is included in the **Generic harness** by default. Any session using the Generic harness automatically gets budget-aware behavior: * The agent’s system prompt includes a “Budget Awareness” section with guidelines for efficient output when budget is constrained * The agent gets a `check_budget` tool to query remaining balance before expensive operations When budget is running low, a budget-aware agent will prioritize completing current tasks efficiently rather than exploring new directions. ## Budget Lifecycle 
---
# Context Compaction
> How Everruns manages LLM context windows through automatic compaction strategies, observation masking, summarization, and hierarchical memory tiers
Source:
Long-running agent sessions accumulate messages until they exceed the model’s context window. When that happens, the LLM rejects the request. **Context compaction** automatically reduces the conversation size so the agent can keep working without losing important information. Everruns provides multiple compaction strategies that can be combined. The default `auto` strategy cascades through all of them in order, from cheapest (free) to most expensive (LLM call), stopping as soon as the context fits.  ## How It Works Compaction operates at two points: 1. **Proactively**: before each LLM call, Everruns estimates the token count. If it exceeds a configurable budget threshold (default 85% of the model’s context window), compaction runs *before* the call is made. This avoids the latency of a failed request. 2. **Reactively**: if the LLM still returns a `RequestTooLarge` error (estimation can undercount), the compaction cascade runs and the request is retried automatically. In both cases, the same cascade of strategies executes:  The UI shows a divider between messages whenever compaction happens: > **Context compacted** · 142 → 38 messages · observation\_masking+summarization Click the divider to see the cascade details, which strategies ran, how many messages each step produced, and the time taken. ## Strategies ### Auto (default) Runs all strategies in order. Stops as soon as context fits. This is the recommended setting for most use cases. ### Observation Masking Replaces old tool outputs with compact summaries while keeping the message structure intact. This is free (no LLM call) and preserves tool call IDs for tracing. Two summary formats: | Format | Example | When to use | | -------------------- | ----------------------------------------------------------- | --------------------------------- | | `one_line` (default) | `[read_file → 47 lines, 2340 bytes]` | Most cases, minimal footprint | | `head_tail` | First 3 lines + `... (14 lines omitted) ...` + last 3 lines | When partial output context helps | The most recent N tool outputs are always kept verbatim (default: 5). ### Native Provider Compaction Delegates compaction to the LLM provider’s own endpoint. Currently supported by OpenAI’s Responses API (`/responses/compact`). When available, this can be more intelligent than generic strategies since the provider understands its own tokenization. Everruns sends either a stateful response handle or a standalone transcript to the compact endpoint, never both. The returned ordered context is encrypted at rest as a durable checkpoint and reused across later turns and process restarts. Each request combines the latest checkpoint for the exact provider/model with raw messages written after its source boundary; changing provider or model falls back to raw history. Compaction never deletes or rewrites session events. Public `context.compacted` events contain only counts, timing, strategy, and an optional checkpoint identifier. Provider-native encrypted context remains confined to the internal provider and storage paths. ### Summarization Uses an LLM to generate a structured summary of older messages. The summary replaces those messages in context and is wrapped in `[CONVERSATION_SUMMARY]` tags so subsequent compactions can re-summarize it. You can configure: * Which model to use (default: same as the agent) * What information to preserve (decisions, files modified, errors, etc.) * Custom instructions appended to the summarization prompt ### Aggressive Trim Last resort. Drops the oldest messages to fit within the token budget. The system prompt and the most recent messages are always preserved. This is lossy, dropped messages cannot be recovered unless Infinity Context is enabled. ## Generic Harness Defaults The built-in **Generic** harness enables both `compaction` and `infinity_context` by default. Together they keep long sessions unbounded without manual configuration. | Capability | Role | Default in Generic | | ---------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | **Infinity Context** | Limits how many messages are loaded from the database into the prompt; provides `query_history` for retrieval | `context_budget_tokens: 100000`, `min_recent_messages: 10` | | **Context Compaction** | Reduces the size of messages that *are* in the prompt, masking tool outputs, summarizing, or trimming | `strategy: auto`, `proactive: true`, `budget_percent: 0.85` | The flow for a long-running Generic session:  No configuration is needed, creating a session with the Generic harness gives you this behavior out of the box. To customize, override either capability’s config on the agent or session level. ## Configuration Compaction is a capability configured per agent or harness via `AgentCapabilityConfig`. ### Default (auto strategy, proactive)
```json
{
"capabilities": ["compaction"]
}
```
### Custom strategy and budget
```json
{
"capabilities": [
{
"ref": "compaction",
"config": {
"strategy": "auto",
"proactive": true,
"budget_percent": 0.85
}
}
]
}
```
### Observation masking only (no LLM calls)
```json
{
"capabilities": [
{
"ref": "compaction",
"config": {
"strategy": "observation_masking",
"observation_masking": {
"keep_recent_tool_outputs": 10,
"summary_format": "head_tail"
}
}
}
]
}
```
### Summarization with a cheaper model
```json
{
"capabilities": [
{
"ref": "compaction",
"config": {
"strategy": "summarization",
"summarization": {
"model": "claude-haiku-4-5-20251001",
"preserve": ["decisions", "files_modified", "errors", "api_keys"],
"instructions": "Focus on architecture decisions and API contract changes"
}
}
}
]
}
```
### Full configuration with memory tiers
```json
{
"capabilities": [
{
"ref": "compaction",
"config": {
"strategy": "auto",
"proactive": true,
"budget_percent": 0.80,
"observation_masking": {
"keep_recent_tool_outputs": 5,
"summary_format": "one_line"
},
"summarization": {
"model": null,
"preserve": ["decisions", "files_modified", "errors", "current_plan"],
"instructions": null
},
"memory_tiers": {
"hot_messages": 20,
"warm_messages": 100
}
}
}
]
}
```
## Configuration Reference ### Top-level | Field | Type | Default | Description | | ---------------- | ------- | -------- | -------------------------------------------------------------------------------- | | `strategy` | string | `"auto"` | Compaction strategy: `auto`, `native`, `observation_masking`, or `summarization` | | `proactive` | boolean | `true` | Compact before hitting context limits (recommended) | | `budget_percent` | float | `0.85` | Trigger proactive compaction at this fraction of the context window | For `auto` and `native`, proactive pressure invokes provider-native compaction when the driver supports it and stores the result as a durable checkpoint. The driver’s effective model context window takes precedence over the built-in profile, so external drivers can report their actual limit. Stateful `previous_response_id` requests skip local proactive pressure checks because their request body is only a delta over provider-held context; reactive too-large recovery remains available. A native result must materially reduce provider-reported tokens, or serialized bytes when token usage is unavailable: at least 5%, with a 32-unit floor for small measurements. Smaller results do not install or replace a checkpoint and do not emit `context.compacted`. A newly installed checkpoint is not proactively replaced again until a meaningful raw-message suffix has accumulated. Failed and no-op native attempts are also held behind a retry watermark until estimated input grows by both 4,096 tokens and 5%, avoiding repeated compact calls against the same source. Branch and rollback selection ignore watermarks from a different transcript lineage. When it is re-armed, the next native compact request preserves the prior opaque checkpoint items in order before appending that suffix, for both proactive and reactive compaction. ### Observation Masking | Field | Type | Default | Description | | -------------------------- | ------- | ------------ | ---------------------------------------------------------- | | `keep_recent_tool_outputs` | integer | `5` | Number of recent tool outputs to keep verbatim | | `summary_format` | string | `"one_line"` | How to summarize masked outputs: `one_line` or `head_tail` | ### Summarization | Field | Type | Default | Description | | -------------- | -------------- | ----------------------------------------------------------- | --------------------------------------------------------- | | `model` | string \| null | `null` | Model for summarization. Null = same as the agent’s model | | `preserve` | string\[] | `["decisions", "files_modified", "errors", "current_plan"]` | Information categories to preserve in summaries | | `instructions` | string \| null | `null` | Custom instructions appended to the summarization prompt | ### Memory Tiers | Field | Type | Default | Description | | --------------- | ------- | ------- | --------------------------------------------------------------- | | `hot_messages` | integer | `20` | Recent messages kept verbatim (full content) | | `warm_messages` | integer | `100` | Older messages with observation masking applied to tool outputs | Messages beyond hot + warm are in the **cold tier**: replaced with a conversation summary. If [Infinity Context](https://docs.everruns.com/capabilities/infinity-context/) is enabled, cold-tier messages remain queryable via `query_history`. ## Memory Tier Diagram  ## Combining with Infinity Context Compaction and [Infinity Context](https://docs.everruns.com/capabilities/infinity-context/) are complementary: * **Infinity Context** limits how many messages are loaded from the database into the prompt, and provides `query_history` for retrieval. * **Compaction** reduces the size of messages that *are* in the prompt, making tool outputs smaller, summarizing old turns, or trimming when nothing else works. For long-running sessions, enable both:
```json
{
"capabilities": [
"infinity_context",
{
"ref": "compaction",
"config": {
"strategy": "auto",
"proactive": true
}
}
]
}
```
With both active, the flow is: 1. Infinity Context limits messages loaded (e.g., last 100 messages) 2. Compaction masks old tool outputs in those messages 3. If still over budget, summarization or trim kicks in 4. Cold-tier messages remain accessible via `query_history` ## Events Compaction emits two SSE events: | Event | When | Key fields | | -------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `context.compacting` | Semantic compaction starts | `reason` (proactive\_budget, request\_too\_large, manual), `strategy`, `messages_before`, optional `tokens_before` / `bytes_before` | | `context.compacted` | Material semantic reduction completes | `strategy_used`, `messages_before`, `messages_after`, optional before/after token or byte metrics, `duration_ms`, `steps[]`, optional `checkpoint_id` | Each step in the cascade is recorded with its strategy name, resulting message count, and duration. Provider-encrypted native compact content is never included in these public events. Observation masking alone remains an outbound model-view optimization and does not emit `context.compacted` because it installs no semantic checkpoint. ## Best Practices * **Start with defaults.** The `auto` strategy with `proactive: true` handles most cases well. * **Lower `budget_percent`** (e.g., 0.70) if your agents use large tool outputs frequently, this gives more headroom before the context fills. * **Increase `keep_recent_tool_outputs`** if your agent often references recent tool results across multiple turns. * **Use a cheaper model for summarization** (e.g., Haiku) to reduce cost and latency when the summarization step runs. * **Enable Infinity Context** alongside compaction for sessions that run for hours or days. * **Customize `preserve`** to match your agent’s domain, if your agent tracks database schemas or API contracts, add those to the preserve list. ## See Also * [Infinity Context](https://docs.everruns.com/capabilities/infinity-context/), Message history windowing and retrieval * [Capabilities Overview](https://docs.everruns.com/capabilities/), How capabilities are configured * [Harnesses](https://docs.everruns.com/features/harnesses/), Where capability configs are applied * [Events](https://docs.everruns.com/features/events/), SSE event streaming reference
---
# Embedding Everruns
> Choose between the application-facing Framework and low-level host composition.
Source:
For an application that runs agents in its own Rust process, start with the [Everruns Framework](https://docs.everruns.com/framework/) and the `everruns` crate. Its offline [quickstart](https://docs.everruns.com/framework/quickstart/) needs no server, worker, database, network, or credentials. Low-level embedding is for applications that are themselves execution hosts: servers, evaluation harnesses, research runtimes, or specialized systems that must replace backend stores, platform definitions, or orchestration phases. Those hosts compose `everruns` with `everruns-host` and the focused crates they need. The useful low-level boundary, security obligations, and crate-selection guidance now live in [Custom backends](https://docs.everruns.com/framework/custom-backends/).
---
# ID Schema
> How Everruns formats and validates public resource identifiers, Stripe-style prefixed IDs.
Source:
Every resource in the Everruns API, agents, sessions, skills, knowledge bases, and so on, is identified by a **prefixed public ID**. The prefix tells you at a glance what kind of resource you are looking at; the suffix is an opaque 32-character token. This pattern was popularized by Stripe (`cus_`, `sub_`, `pi_`). Treat the suffix as a meaningless string, do not parse it, sort by it, or infer information from it. The only guarantees the API makes about an ID are its format, its uniqueness within an organization, and its stability over the lifetime of the resource. ## Format All resource identifiers use the same shape:
```plaintext
{prefix}_{32-hex-chars}
```
* `{prefix}` is a short lowercase token that identifies the resource type (for example `agent`, `session`, `skill`). * `_` separates the prefix from the suffix. * `{32-hex-chars}` is an opaque 32-character lowercase hexadecimal token. Example:
```plaintext
agent_5c7f3a91b24e48d6a0e91f3b7c4d2e85
```
Identifiers match a fixed pattern: the resource’s prefix, an underscore, then exactly 32 lowercase hexadecimal characters. For an `agent`, the literal regex is `^agent_[0-9a-f]{32}$`. The API rejects malformed values with `400 Bad Request`. ## Treat the suffix as opaque The suffix carries no public meaning. In particular: * **Do not sort by it.** Use `created_at` (or whatever timestamp field the resource exposes) for chronological ordering. * **Do not infer age, ordering, or shard placement from it.** The encoding may change without notice. * **Do not parse it as a UUID.** The hex format is a transport convenience; future resources may use different internal schemes while keeping the same wire format. This decoupling is intentional. The internal database key for a resource and its public ID are deliberately separate concepts, clients only ever see the public ID. ## Client-Supplied IDs For resources that accept client-supplied IDs on create, you can pass your own `id` as long as it matches the format above and uses the correct prefix for the resource type. If you omit `id`, the server assigns one. A subset of resources additionally expose `PUT /v1/{resource}/{id}` as an upsert: the same call creates the resource if it does not exist (`201 Created`) and updates it in place if it does (`200 OK`). Where supported, this makes idempotent provisioning straightforward, replay the same `PUT` and the end state is identical. The OpenAPI reference is the source of truth for which resources support this; not every resource has a `PUT` route. ## Serialization IDs are always serialized as JSON strings. The field name is `id` for the resource itself and `{resource}_id` when referenced from another resource:
```json
{
"id": "agent_5c7f3a91b24e48d6a0e91f3b7c4d2e85",
"session_id": "session_2b8a4d12c673491fae058b7d9c1f6a40"
}
```
## Prefix Reference The prefix is part of the public contract for each resource. The most common ones are listed below; the canonical list lives in the OpenAPI specification. The prefix in the table below is the token that appears before the `_` separator, an `agent` resource has IDs that start with `agent_`. | Resource | Prefix | | -------------- | ---------- | | Agent | `agent` | | Agent version | `agentver` | | Session | `session` | | Skill | `skill` | | Knowledge base | `kb` | | Memory | `mem` | | MCP server | `mcp` | | Schedule | `sched` | | Image | `img` | | User | `user` | | Organization | `org` | ## Design Notes | Question | Answer | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Why prefixed IDs? | They make IDs self-describing and prevent accidentally passing, say, an agent ID where a session ID is expected. | | Why opaque suffixes? | Decoupling the wire format from internal storage gives the platform room to evolve without breaking clients, and prevents callers from leaning on accidental properties (ordering, creation time) that aren’t part of the contract. | | Why lowercase hex? | Case-insensitive matching, URL-safe, easy to copy and paste. | | Are IDs unique across organizations? | Each ID is unique within its owning organization. The same `id` value could in principle appear in two different orgs, but you only ever see IDs scoped to orgs you belong to. |
---
# Memory model
> How Workspace and Memory relate, the two-tier model for what an agent can read and write.
Source:
Everruns separates **where the agent works** from **what the agent remembers across runs**. That split is the whole memory model: * **Workspace**: the active working area for a session. Per-run, singleton, ephemeral by default. Mounted at `/workspace`. * **Memory**: org-scoped, named stores. Durable, governed, mountable into Workspaces. RO by default. These two tiers are intentional. Workspace is where an agent does its current task; Memory is where the org persists state it wants reused across tasks. For the shipped organization, agent, and user ownership tiers, including the private `/memory/user` and automatic `/memory/agent` mounts, see [Agent and user memory](https://docs.everruns.com/features/memory-scopes/). ## The two-axis grid Scope × surface, with the same surfaces appearing on both sides:
```text
SESSION ORG
(Workspace — per run) (Memory — durable, shared)
┌────────────────────────┬────────────────────────┐
Files │ workdir, scratch │ shared docs, code │
├────────────────────────┼────────────────────────┤
Tables │ session SQL db │ shared datasets (TBD) │
├────────────────────────┼────────────────────────┤
KV │ run state, notes │ facts, prefs (TBD) │
├────────────────────────┼────────────────────────┤
Secrets │ per-run creds │ org credentials (TBD) │
└────────────────────────┴────────────────────────┘
```
Today only the **Files** surface exists on both sides; Tables exist on the Workspace side (session SQL DB). Tabular, KV, secrets, and structured surfaces on Memory are durable design intent, see `knowledge/runtime-resources/memory.md`. ## Org → Session: Mount A Memory does not enter a Workspace automatically. The `memory` capability declares which Memories are mounted, where, and with what access mode.
```text
ORG MEMORIES (named, many)
┌──────────────┬──────────────┬──────────────┐
│ mem:crm │ mem:legal-kb │ mem:pricing │ ...
└──────┬───────┴──────┬───────┴──────┬───────┘
│ │ │ per-mount RO | RW
▼ ▼ ▼
┌─────────────────────────────────────────┐
│ SESSION WORKSPACE │
│ │
│ /workspace ← native files │
│ /workspace/mnt/... ← Memory mounts │
└─────────────────────────────────────────┘
```
A Memory can be mounted into any number of sessions concurrently. The mount is snapshotted at session creation, so later archival or rename of the Memory does not destabilize a running session. ## Session-eye view What the running agent actually sees:
```text
SESSION WORKSPACE (one run)
┌────────────────────────────────────────────────────────┐
│ │
│ NATIVE (lives and dies with the session) │
│ ┌──────────┬──────────┐ │
│ │ files │ tables │ │
│ │ /workspace session SQL db │
│ └──────────┴──────────┘ │
│ │
│ MOUNTED (projected from org Memories) │
│ ┌────────────────────────────────────────────────┐ │
│ │ /workspace/mnt/docs ← mem:design [ro] │ │
│ │ /workspace/mnt/orders ← mem:crm [rw] │ │
│ │ /workspace/mnt/legal ← mem:legal-kb [ro] │ │
│ └────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
```
The same tools (`read_file`, `list_directory`, `grep_files`, `bashkit_shell`) traverse native and mounted paths uniformly. Writes to read-only mounts return clear errors; writes to read-write mounts write through to the underlying Memory and are audited. ## Why two tiers, not one A single “everything is Memory” abstraction was considered and rejected for two reasons: 1. **Lifecycle and governance differ.** Workspace files are intermediate, agents probe, edit, discard. Memory is durable shared state with audit, lifecycle (active/archived/deleted), and trust boundaries. Forcing one set of policies on both was wrong for both. 2. **Naming clarity.** “Memory” anthropomorphizes what the agent recalls across tasks. “Workspace” describes the desk it’s working on. Mixing the two names (“session memory” vs “org memory”) forced every sentence to carry a qualifier. External validation: Anthropic’s Claude Managed Agents settled on essentially the same split, workspace files (per-run) plus Memory Stores (`/mnt/memory/`, durable, named, RO/RW mountable). The terminology in this doc mirrors that convention deliberately. ## Invariants * **Scope.** Sessions cannot see other sessions. Memories are the only sharing point. * **Default access.** Memory mounts default to read-only. Read-write is explicit and audited. * **Snapshot mounts.** Mount config is captured at session creation. Archiving a Memory after that surfaces an error rather than disappearing files. * **Source-backed Memories are read-only.** Memories synced from GitHub/Git cannot be mounted read-write; their contents are replaced atomically on sync. * **Indexes are not surfaces.** Vector search or embeddings are an index *over* a surface, not a new surface. Surfaces are addressable storage. ## What is *not* Memory These exist in Everruns but live outside this model: * **Transcripts and events**: the conversation history is the session’s append-only log, not a memory surface. * **Sandboxes**: managed compute environments (`knowledge/runtime-resources/session-sandbox.md`) are a separate primitive from storage. * **Knowledge Bases** (`knowledge/runtime-resources/knowledge-bases.md`), curated entries with stable citation IDs, agent reads via `search_knowledge`. Likely folds into a future “structured” surface of Memory; today it stays separate. ## Mapping to other systems | Concept | Everruns | Claude Managed Agents | Letta / MemGPT | | ---------------------------- | ------------------------- | --------------------- | --------------- | | Per-run scratch | Workspace | Memory Tool | Working memory | | Durable named store | Memory | Memory Store | Archival memory | | Mount access control | RO / RW per mount | RO / RW per attach | n/a | | Background consolidation | , | Dreaming | Reflection | | Multi-surface (files/tables) | Files today; more planned | Files only | Text blocks | ## Further reading * [Agent and user memory](https://docs.everruns.com/features/memory-scopes/), scoped memory mounts, access, and privacy defaults * [`knowledge/runtime-resources/memory.md`](https://github.com/everruns/everruns/blob/main/knowledge/runtime-resources/memory.md), durable design intent for the Memory tier * [`knowledge/runtime-resources/workspace.md`](https://github.com/everruns/everruns/blob/main/knowledge/runtime-resources/workspace.md), Workspace specification (file surface, mount point, git VCS) * [`knowledge/runtime-resources/knowledge-bases.md`](https://github.com/everruns/everruns/blob/main/knowledge/runtime-resources/knowledge-bases.md), curated org knowledge, the curation-first sibling of Memory
---
# Network Access Control
> Control which hosts and URLs agents can reach using layered allowlists and blocklists on harnesses, agents, and sessions
Source:
By default, agents with the `web_fetch` capability can access any public URL. **Network access lists** let you restrict which hosts and URLs an agent can reach, giving you fine-grained control over outbound network access. Access lists are configured at three levels, harness, agent, and session, and each layer can only make access *more* restrictive, never less. ## Configuration A network access list has two fields: | Field | Purpose | Default | | --------- | ---------------------------------------------- | --------------------- | | `allowed` | If non-empty, only matching URLs are permitted | `[]` (no restriction) | | `blocked` | Always denied, even if matched by `allowed` | `[]` | ### Pattern Format Patterns support three formats: | Format | Example | Matches | | --------------- | ----------------------------- | ------------------------------ | | Exact domain | `api.example.com` | Any URL on that exact domain | | Wildcard domain | `*.example.com` | The domain and all subdomains | | URL prefix | `https://api.example.com/v1/` | URLs starting with that prefix | Domain matching is case-insensitive. Blocked patterns always take precedence over allowed patterns. ### API Examples Set a network access list when creating or updating an agent:
```json
POST /v1/agents
{
"name": "Research Agent",
"system_prompt": "You are a research assistant.",
"network_access": {
"allowed": ["*.github.com", "api.openai.com"],
"blocked": ["evil.example.com"]
}
}
```
Restrict a specific session further:
```json
POST /v1/sessions
{
"agent_id": "agent_...",
"network_access": {
"blocked": ["internal.corp"]
}
}
```
To clear a network access list on update, send an empty object:
```json
PATCH /v1/agents/{id}
{
"network_access": {}
}
```
## Layer Merging Network access lists merge across three layers using restrictive semantics, each layer can only narrow what the parent allows:
```plaintext
Harness (baseline)
∩ Agent (can only restrict further)
∩ Session (can only restrict further)
```
The merge rules: | Field | Rule | Rationale | | --------- | ------------------------------------------------------------------------ | ------------------------------------------------- | | `allowed` | **Intersection**: child entries kept only if covered by a parent pattern | Child cannot grant access the parent didn’t allow | | `blocked` | **Union**: all blocked patterns combined | Child cannot un-block a parent’s block | If a child layer doesn’t set `allowed`, it inherits the parent’s list unchanged. ### Example Given this configuration:
```plaintext
Harness: allowed: ["*.github.com", "*.openai.com"]
Agent: allowed: ["api.github.com"], blocked: ["evil.com"]
Session: blocked: ["malware.github.com"]
```
The effective policy for the session is: * **Allowed**: `["api.github.com"]`, kept because it’s a subset of `*.github.com`; `*.openai.com` dropped because the agent didn’t include it * **Blocked**: `["evil.com", "malware.github.com"]`, union of all layers Only `api.github.com` is reachable, and both `evil.com` and `malware.github.com` are explicitly denied. ### Harness Inheritance If a harness inherits from a parent harness (via `parent_harness_id`), the network access list is merged through the inheritance chain before any agent or session layer is applied. The same intersection/union rules apply. ## Enforcement The merged network access list is checked in `web_fetch` before every HTTP request. If a URL doesn’t match the effective policy, the tool returns an error:
```plaintext
URL blocked by network access policy: https://blocked-domain.com/path
```
Standard SSRF protections (blocking private IPs, loopback, cloud metadata endpoints) are always enforced regardless of the network access list. ## Capabilities Affected | Capability | Enforced | Notes | | --------------- | -------- | --------------------------------------------- | | `web_fetch` | Yes | Checked before every HTTP request | | `bashkit_shell` | N/A | No network builtins (curl/wget not available) |
---
# Physical Architecture
> The physical components that make up an Everruns deployment, control plane, workers, PostgreSQL, NATS JetStream, Valkey, and the reverse proxy, and how they fit together.
Source:
The [getting-started architecture](https://docs.everruns.com/getting-started/architecture/) page describes the *logical* shape of Everruns: a control plane, a worker tier, and a shared database. This page goes one level deeper and describes the *physical* components an operator actually deploys, what each one is for, when it is optional, and how data flows between them.  ## Components at a glance | Component | Role | Required | Default port | | ---------------------- | -------------------------------------------------------------------------- | --------------------------- | ------------------------ | | Reverse proxy | TLS termination and route fan-out for `/api`, `/mcp`, `/.well-known/*`, UI | Yes (or equivalent ingress) | 443 | | Control plane (server) | REST API, SSE event streams, gRPC server for workers, owns all state | Yes | 9301 (HTTP), 9001 (gRPC) | | Worker pool | Stateless executors of the agentic loop (input → reason → act) | Yes | , (outbound only) | | PostgreSQL 17 | Durable storage for agents, sessions, events, durable task queue | Yes | 5432 | | NATS JetStream | Push-based ephemeral event delivery and task notifications | Optional | 4222 | | Valkey | Distributed sliding-window rate limiting across control-plane instances | Optional | 6379 | | Management UI | Operator interface for agent and provider configuration | Optional | , (served by proxy) | Workers never talk to PostgreSQL, NATS, or Valkey directly. Every read and write goes through the control plane’s gRPC service on port 9001. This is what lets workers run with no database credentials, no encryption keys, and no awareness of the data tier. ## PostgreSQL, the only required stateful component PostgreSQL is the single source of truth for everything Everruns persists. There is no in-memory cache that needs warming, no secondary store that needs syncing, and no analytics database to keep consistent. If you back up PostgreSQL, you back up the entire system. What lives in PostgreSQL: * Agents, sessions, messages, and durable events * The durable task queue used by the worker tier (claimed via `SKIP LOCKED`) * Encrypted LLM provider credentials, MCP server registrations, capability config * Per-session virtual filesystems, knowledge bases, and the event log used for SSE replay Operational requirements: * **PostgreSQL 17.** UUIDv7 is implemented via a custom SQL function; PG 18’s native `uuidv7()` will be adopted once it is widely available on managed services. * **Direct connection for `LISTEN/NOTIFY`.** Pooled or proxied endpoints (PgBouncer, Neon `-pooler`, RDS Proxy) interleave notification frames with query traffic. Set `DATABASE_URL` to the pooled endpoint for normal queries and `DATABASE_UNPOOLED_URL` to a direct session-scoped endpoint for listeners. Startup fails fast if the configured listener URL looks pooled. * **Pool sizing.** With `EXPECTED_INSTANCES=N` set, each instance sizes its pool so that `pool × instances` stays under 80% of `PG_MAX_CONNECTIONS`. * **Migrations.** Auto-applied on server startup via embedded sqlx migrations, protected by a PostgreSQL advisory lock so multiple control-plane instances can boot together without racing. See [`docs/sre/environment-variables.md`](https://docs.everruns.com/sre/environment-variables/) for the full list of database-related variables. ## NATS JetStream, optional push delivery NATS is not required, but turning it on materially reduces PostgreSQL write pressure and SSE tail latency for busy deployments. Without NATS, Everruns uses PostgreSQL for both storage *and* delivery: ephemeral events persist to PG and SSE clients poll PG with `LISTEN/NOTIFY` wakeups; workers are notified of new tasks the same way. This works, and it is the default. The cost is write amplification, every streaming-token delta lands in PG even though no client will ever re-read it. With `NATS_URL` set and JetStream enabled, Everruns rewires two hot paths: * **Ephemeral event delivery.** Delta events (`output.message.delta`, `reason.thinking.delta`, `tool.output.delta`, `llm.generation`) skip PostgreSQL entirely and flow only through NATS JetStream. SSE streams subscribe to per-session subjects with short-term retention. Durable events (`output.message.completed`, `turn.started`, `tool.completed`, etc.) still persist to PG so SSE reconnection via `since_id` continues to work, missed deltas are acceptable because the completed event carries the full content. * **Task notifications.** `task.available.{activity_type}` subjects replace PG NOTIFY for worker wakeup, dropping notification latency from \~30 ms to \~1 ms. NATS is fail-graceful: if the connection fails at startup, the control plane logs a warning and falls back to the PG-backed paths. Only the control plane connects to NATS, workers still talk to the server via gRPC. ## Valkey, optional distributed rate limiting Valkey is a Redis-compatible key-value store (a Linux Foundation fork of Redis). Everruns uses it for exactly one thing: sliding-window rate limiting that is coordinated across control-plane instances. When `VALKEY_URL` is not set, rate limiting falls back to an in-memory governor, accurate per-instance, but with N instances behind a load balancer a single IP can consume up to N× the intended budget. Set `VALKEY_URL` when you run more than one control-plane instance and need a shared budget. Connection details: * Accepts `redis://`, `rediss://` (TLS), `valkey://`, `valkeys://` (TLS) schemes * Uses atomic Lua scripts for sliding-window counters * **Fail-open:** if Valkey is unreachable, the rate limiter allows the request rather than rejecting traffic on a side-channel outage * Only the control plane connects to Valkey; workers do not need access ## Worker pool, no shared state Workers are the most operationally boring component in the deployment. They have: * No database connection * No encryption key * No NATS or Valkey access * No durable local state They claim a task from the control plane over gRPC, fetch the turn context in a single batched call, run the agentic loop (LLM calls, tool execution), and stream events back. If a worker crashes mid-task, the heartbeat stops, the control plane reclaims the task after 30 seconds, and another worker picks it up. Add workers for throughput; remove them to save cost. See [Worker authentication](https://docs.everruns.com/sre/runbooks/durable-mode-setup/) for the `WORKER_GRPC_AUTH_TOKEN` and optional mTLS setup that secures this internal channel. ## Reverse proxy contract A reverse proxy (or platform ingress that enforces the same routes) is mandatory in production: | Route | Destination | Notes | | ---------------- | ------------- | ----------------------------------- | | `/api/*` | Control plane | Disable proxy buffering for SSE | | `/mcp` | Control plane | Do **not** rewrite under `/api` | | `/.well-known/*` | Control plane | OAuth discovery; do **not** rewrite | | `/health` | Control plane | Health check target | | Everything else | UI | If UI is deployed; otherwise 404 | TLS terminates at the proxy. Worker gRPC traffic stays on the private network, never expose port 9001 publicly. See [`local/Caddyfile`](https://github.com/everruns/everruns/blob/main/local/Caddyfile) and [`examples/docker-compose-full.yaml`](https://github.com/everruns/everruns/blob/main/examples/docker-compose-full.yaml) for working configurations. ## Development modes The same binaries collapse into smaller deployments for local work: * **`DEV_MODE=true` (in-memory).** No PostgreSQL, no Docker. Execution runs in-process inside the server binary; the gRPC server is disabled. Data is lost on restart. Useful for UI iteration and API development. * **`just start-all` (full local).** Brings up PostgreSQL, Valkey, and NATS as local processes (no Docker required) and starts the server + worker against them. Mirrors production wiring on a single machine. * **Docker Compose.** The production-shaped topology in one machine; see [Docker Compose](https://docs.everruns.com/getting-started/docker-compose/). ## Multi-instance deployment Multiple control-plane instances can run behind a load balancer with no session affinity: | Concern | How it stays correct | | -------------------- | ----------------------------------------------------------------------------------------- | | Database connections | `EXPECTED_INSTANCES=N` divides the pool so `pool × instances ≤ 80% of PG_MAX_CONNECTIONS` | | SSE delivery | `LISTEN/NOTIFY` or NATS subjects fan out to every instance; reconnects are idempotent | | Task claiming | `SKIP LOCKED` on the durable task queue partitions work naturally | | Migrations | PostgreSQL advisory lock prevents concurrent runs | | Rate limits | Valkey-backed sliding-window counters are shared; in-memory falls back to per-instance | Workers do not require coordination, add as many as you need, in as many regions as you need, as long as they can reach the control-plane gRPC port. ## Further reading * [Architecture (Getting Started)](https://docs.everruns.com/getting-started/architecture/), the logical model * [Environment Variables](https://docs.everruns.com/sre/environment-variables/), every knob and its default * [Docker Compose](https://docs.everruns.com/getting-started/docker-compose/), a production-shaped local setup * [Custom backends](https://docs.everruns.com/framework/custom-backends/), low-level execution-host composition
---
# Read Tools
> Parameters, pagination, content-type defaults, and exec output retrieval for the read-category tools.
Source:
Agents spend most of their context window on tool results. A single `cat` of a large file can consume thousands of tokens that crowd out reasoning space. The read-category tools are built to return the part the agent asked for and nothing else. Three tools form the read category: | Tool | Purpose | | ---------------- | ---------------------------------------------------------------------------------- | | `read_file` | Read file content with offset/limit pagination and line numbers | | `grep_files` | Search file contents by regex — returns matching lines with paths and line numbers | | `list_directory` | List files and directories with metadata (size, type) | Together they implement a **search → locate → read** workflow: `grep_files` finds where something is, `read_file` reads the relevant section, and `list_directory` provides structural context. ## read\_file ### Parameters | Parameter | Type | Default | Description | | --------- | ------- | ------------ | --------------------------------------------------- | | `path` | string | *(required)* | Absolute file path (e.g., `/workspace/src/main.rs`) | | `offset` | integer | 0 | Starting line number (0-indexed) | | `limit` | integer | 2000 | Maximum lines to return | ### Response
```json
{
"path": "/workspace/src/main.rs",
"content": "1|use std::io;\n2|use serde::Serialize;\n3|\n4|fn main() {\n...",
"total_lines": 450,
"lines_shown": { "start": 1, "end": 450 },
"truncated": false,
"content_type": "source",
"read_mode": "offset",
"size_bytes": 12480,
"content_hash": "sha256:a1b2c3..."
}
```
Key response fields: * **`content`** — Line-numbered output in compact `N|content` format. The `N|` prefix uses minimal bytes while giving agents precise line references for edits. * **`total_lines`** — Total lines in the file. Use this to know if more content exists beyond the current window. * **`lines_shown`** — 1-based start/end of the returned window. * **`truncated`** — `true` if more content exists beyond the returned window. * **`content_type`** — Detected content type (e.g., `log`, `csv`, `minified`, `source`). Reflects the heuristic applied. * **`read_mode`** — `tail` when reading log files from the end; `offset` otherwise. * **`content_hash`** — SHA-256 hash of the file content, used by `edit_file` for compare-and-set safety. ### Content-Type Defaults When the agent doesn’t specify `offset` or `limit`, `read_file` adjusts defaults based on file extension: | Content Type | Extensions | Default Limit | Read Mode | Rationale | | ------------ | --------------------------------- | ------------- | ------------------- | ------------------------------------------------------ | | Source code | `.rs`, `.ts`, `.js`, `.py`, etc. | 2000 lines | From offset | Standard — agent usually needs context around a region | | Log files | `.log`, `.out` | 500 lines | **From end (tail)** | Errors cluster at the end — recent output matters most | | CSV/TSV data | `.csv`, `.tsv` | 100 lines | From offset | Schema + sample rows; header always included | | Config | `.json`, `.yaml`, `.yml`, `.toml` | 2000 lines | From offset | Usually need full structure | | Minified | `.min.js`, `.min.css` | 20 lines | From offset | Single-line files would blow token budgets | | Text | `.md`, `.txt`, `.rst` | 2000 lines | From offset | Standard | **Explicit parameters override only the defaults they control.** An explicit `limit` overrides the content-type default limit. An explicit `offset` does **not** change the default limit by itself; it only controls where reading starts, and for log files it disables the default tail-biased read mode in favor of reading from that offset. If both `offset` and `limit` are provided, both behaviors are explicitly controlled. For CSV files, the header row (line 1) is always included in the response, even when reading with an offset. This ensures the agent always has column names for context. ### Image and Binary Handling * **Images** (`.png`, `.jpg`, `.gif`, `.webp`) are returned as **native image content blocks**, not text. The agent sees the image directly. * **Binary files** (base64-encoded) are returned with `"encoding": "base64"` and no line formatting. * A **hard byte cap** (50 KB) acts as a safety net for pathological files like minified bundles. ## Pagination For files larger than the default window, page through with `offset` and `limit`:
```json
// First read — gets lines 1-2000
{ "path": "/workspace/big_file.rs" }
// Response shows total_lines: 5000, truncated: true
// Continue reading:
{ "path": "/workspace/big_file.rs", "offset": 2000, "limit": 2000 }
// Gets lines 2001-4000. Continue until truncated: false.
```
The 2000-line default covers most source files entirely in a single read. For the rare cases where files are larger, the `total_lines` field tells the agent exactly how much remains. ## Keeping read results small The system prompt guides agents toward efficient reading patterns: 1. **Search before read** — Use `grep_files` to find relevant lines, then `read_file` with a targeted `offset` around the match. This avoids reading thousands of irrelevant lines. 2. **Don’t re-read** — Files already in conversation context don’t need to be read again. The agent should reference prior reads. 3. **Check `total_lines`** — When a read is truncated, the agent knows how much remains and can decide whether to continue or search within the unread portion. 4. **Use offset to continue** — After a truncated read, use `offset` to pick up where the previous read left off. ## Exec Output as Readable Files When sandbox tools (`bash`, `daytona_exec`, `e2b_exec`, etc.) produce output, the inline result is truncated based on the `output` verbosity parameter (default: `concise` \~2 KiB). But the **full output is always persisted** to the session filesystem. ### How It Works The `tool_output_persistence` capability (included in the Generic harness) writes full output before truncation: * **stdout** → `/.outputs/{tool_call_id}.stdout` * **stderr** → `/.outputs/{tool_call_id}.stderr` The truncated inline result includes metadata pointing to the persisted files:
```json
{
"stdout": "[truncated to 2 KiB — full output saved]",
"stderr": "",
"exit_code": 0,
"success": true,
"full_output": "/.outputs/call_abc123.stdout",
"total_lines": 8450,
"output_files": [
"/.outputs/call_abc123.stdout",
"/.outputs/call_abc123.stderr"
]
}
```
The agent can then `read_file` the persisted output selectively:
```json
// Read last 100 lines of build output
{ "path": "/.outputs/call_abc123.stdout", "offset": 8350, "limit": 100 }
```
### Priority-Aware Truncation When output is truncated, the system preserves error-relevant content. Lines matching error patterns (`error`, `Error`, `FAILED`, `panicked`, stack traces) are prioritized over noise like `Compiling...` or `Downloading...`. This means the inline 2 KiB concise output is more likely to contain the diagnostically useful parts. ### Output Verbosity Modes All exec tools accept an `output` parameter: | Mode | Budget | Use case | | --------- | --------- | ---------------------------------- | | `silent` | \~200 B | Fire-and-forget commands | | `concise` | \~2 KiB | Builds, installs (**default**) | | `normal` | \~8 KiB | General debugging | | `verbose` | \~16 KiB | Test failures, error investigation | | `full` | unlimited | When the agent needs every line | ## Content Hash and Edit Safety Every `read_file` response includes a `content_hash` (SHA-256). The `edit_file` tool requires this hash via its `expected_hash` parameter — if the file has changed since the read, the edit is rejected. This **compare-and-set** mechanism prevents stale edits in long conversations where the agent may have read a file many turns ago and the file has since been modified (by another tool call or external process).
```json
// read_file returns content_hash
{ "content_hash": "sha256:a1b2c3..." }
// edit_file uses it as expected_hash — rejected if file changed
{
"path": "/workspace/src/main.rs",
"expected_hash": "sha256:a1b2c3...",
"old_text": "fn main() {",
"new_text": "fn main() -> Result<()> {"
}
```
If an edit is rejected due to a stale hash, the agent should re-read the file to get the current content and hash, then retry the edit. ## Structural Outlines When `read_file` returns a truncated result, the response includes a **structural outline** of the unread portions — function/class/method signatures without bodies. The agent can orient itself in any file with a single partial read.
```plaintext
1|use std::collections::HashMap;
2|use serde::Serialize;
... (lines read normally) ...
100| let result = process(&input);
--- Outline of lines 101-500 (not shown) ---
// L105: fn process(input: &Input) -> Result