This is the full developer documentation for Everruns
---
# Everruns Documentation
> Tutorials, how-to guides, reference, and explanation for building and operating Everruns agents.
Source:
# Documentation
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.
Popular[Framework quickstart](https://everruns.com/docs/framework/quickstart/)[Docker Compose](https://everruns.com/docs/getting-started/docker-compose/)[Tools](https://everruns.com/docs/how-to/equip-agents-with-tools/)[Sessions API](https://everruns.com/docs/api/operations/tags/sessions/)
## Choose how you run Everruns
[Start here compares the three →](https://everruns.com/docs/getting-started/introduction/)
[Framework](https://everruns.com/docs/framework/quickstart/)
Build and run agents inside a Rust process with the everruns crate. Nothing else to deploy.
* [Quickstart](https://everruns.com/docs/framework/quickstart/)
* [Agents and tools](https://everruns.com/docs/framework/agents/)
* [Sessions](https://everruns.com/docs/framework/sessions/)
* [Models and providers](https://everruns.com/docs/framework/models-and-providers/)
`$ cargo add everruns`
[Self-hosted](https://everruns.com/docs/getting-started/docker-compose/)
Run the Platform (API, workers, PostgreSQL, UI) on infrastructure you control, starting with Docker Compose.
* [Docker Compose quickstart](https://everruns.com/docs/getting-started/docker-compose/)
* [Configure a model provider](https://everruns.com/docs/getting-started/docker-compose/#configure-llm-provider)
* [Environment variables](https://everruns.com/docs/sre/environment-variables/)
* [Architecture](https://everruns.com/docs/explanation/architecture/)
`$ docker compose up -d`
[Everruns Cloud](https://everruns.com/docs/getting-started/cloud/)
Use the Platform at app.everruns.com without running it. A built-in model provider and $5 of starter credit; your own provider keys are optional.
* [Get started with Cloud](https://everruns.com/docs/getting-started/cloud/)
* [Personal access tokens](https://everruns.com/docs/getting-started/cloud/#3-create-a-personal-access-token)
* [Credit and the spend cap](https://everruns.com/docs/getting-started/cloud/#credit-and-the-spend-cap)
* [Bring your own keys](https://everruns.com/docs/getting-started/cloud/#bring-your-own-keys-optional)
`https://app.everruns.com`
## What kind of help do you need?
[LearnTutorialsStep-by-step lessons that end with a running agent. Start here if you're new.](https://everruns.com/docs/tutorials/run-an-agent/)[DoHow-to guidesTask-oriented recipes for common problems, when you know what you want to build.](https://everruns.com/docs/how-to/)[UnderstandExplanationBackground and design rationale: durable execution, sessions, and harnesses.](https://everruns.com/docs/explanation/)[Look upReferenceAPI endpoints, event types, the capability catalog, CLI flags, and environment variables.](https://everruns.com/docs/api/)
---
# 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, so the same dollar limit buys far more turns on a small model than on a frontier one.
These session and agent budgets are limits you set. They are separate from the prepaid credit balance of an organization on Everruns Cloud, which pays for the built-in model provider and stops it at zero balance.
## 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 **Worker harness** by default. Any session using the Worker 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.
## Worker Harness Defaults
The built-in **Worker** harness enables both `compaction` and `infinity_context` by default. Together they bound the active context while retaining older history without manual configuration.
| Capability | Role | Default in Worker |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| **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 Worker session:

No configuration is needed, creating a session with the Worker 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://everruns.com/docs/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://everruns.com/docs/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://everruns.com/docs/capabilities/infinity-context/), Message history windowing and retrieval
* [Capabilities Overview](https://everruns.com/docs/capabilities/), How capabilities are configured
* [Harnesses](https://everruns.com/docs/features/harnesses/), Where capability configs are applied
* [Events](https://everruns.com/docs/features/events/), SSE event streaming reference
---
# 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` |
| Session | `session` |
| Skill | `skill` |
| Knowledge base | `kb` |
| Memory | `mem` |
| MCP server | `mcp` |
| Schedule | `sched` |
| Image | `img` |
| Agent avatar | `avatar` |
| 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. |
---
# 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.
These lists narrow what one agent may reach. The deployment-wide policy (`EVERRUNS_EGRESS_POLICY`), per-org grants and the optional tenant egress proxy sit underneath them; see [Outbound traffic](https://everruns.com/docs/advanced/physical-architecture/#outbound-traffic).
## 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://everruns.com/docs/explanation/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: each leases a session and runs its whole turn | 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) |
| Tenant egress proxy | Gives tenant outbound traffic its own address, per-org limits and usage counts | Optional | 4750 (proxy), 4751 (health) |
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 session leases workers run turns under, and the durable task queue for background work (both 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://everruns.com/docs/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://everruns.com/docs/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.
## Outbound traffic
The control plane and workers both make outbound calls, and Everruns splits them in two:
* **Platform traffic** is what the deployment itself owns: LLM providers, the utility model, system email, sandbox provider APIs. It always leaves directly, from the deployment’s own address.
* **Tenant traffic** is what an agent, a capability, an MCP server or an integration chooses: web fetches, tool calls to third-party APIs, remote MCP servers. Every such request goes through one boundary, the egress service, which applies the policy below and writes an audit line with its org, session and decision (`everruns::egress::audit` log target).
### Egress policy
`EVERRUNS_EGRESS_POLICY` decides which public hosts tenant traffic may reach:
| Mode | Behavior |
| ---------------- | ---------------------------------------------------------------------------------------------------- |
| `open` (default) | Any public host. |
| `curated-writes` | GET and HEAD reach any public host; writes, MCP and integration traffic need the built-in allowlist. |
| `curated-all` | Every request needs the allowlist. |
In both curated modes a built-in deny list is checked first, URLs are capped at 2048 characters and IP-literal hosts are refused. An organization can be granted extra hosts or open egress for its own traffic; see [Network Access Control](https://everruns.com/docs/advanced/network-access/). Private, loopback and cloud metadata addresses are refused in every mode, with DNS pinned so a lookup cannot be swapped between the check and the connection.
### Tenant egress proxy
Set `EVERRUNS_EGRESS_PROXY_URL` and `EVERRUNS_EGRESS_PROXY_SECRET` on the control plane and workers, and tenant traffic leaves through `everruns-egress-proxy` instead of directly. Platform traffic never uses it. You get two things:
* **Its own outbound address.** Give the proxy host a separate public IP. If a tenant’s traffic gets that address blocked somewhere, model calls, email and billing keep working, and the address can be replaced without touching the rest of the deployment.
* **Per-org limits and usage.** Each request carries a five-minute token, signed with the shared secret, that names its org and session. The proxy verifies the token, resolves the destination again and refuses non-public addresses and SMTP ports, applies per-org caps, and counts bytes and requests per org and session each minute.
The binary ships in the server image. Limits are off unless you set them, and usage goes to the log (`everruns::egress::usage` target). A control endpoint (`EGRESS_PROXY_CONTROL_URL`) can supply limits per org and receive usage instead. A proxy URL with a missing or short secret fails closed: tenant requests error rather than leave directly. Variables: [Tenant egress proxy](https://everruns.com/docs/sre/environment-variables/#tenant-egress-proxy).
```bash
# Proxy host
EVERRUNS_EGRESS_PROXY_SECRET=<32+ random bytes>
EGRESS_PROXY_ORG_RPM=600 # optional caps
EGRESS_PROXY_ORG_BYTES_PER_DAY=5000000000
everruns-egress-proxy
# Control plane and workers
EVERRUNS_EGRESS_PROXY_URL=http://egress-proxy:4750
EVERRUNS_EGRESS_PROXY_SECRET=
```
## Development modes
The same binaries collapse into smaller deployments for local work:
* **`DEV_MODE=true` (single process).** Nothing to install or run beside the binary: the server starts a temporary PostgreSQL of its own and deletes it on exit. 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://everruns.com/docs/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 |
| Turn and task claiming | `SKIP LOCKED` on session leases and the durable task queue partitions work naturally; a session’s fenced lease keeps one worker on it |
| 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://everruns.com/docs/explanation/architecture/), the logical model
* [Environment Variables](https://everruns.com/docs/sre/environment-variables/), every knob and its default
* [Network Access Control](https://everruns.com/docs/advanced/network-access/), per-agent and per-org outbound rules
* [Docker Compose](https://everruns.com/docs/getting-started/docker-compose/), a production-shaped local setup
* [Custom backends](https://everruns.com/docs/framework/custom-backends/), low-level execution-host composition
---
# Tool Output Pipeline
> How Everruns processes tool results from execution to the model, verbosity budgets, distillation, persistence, hard limits, and where the full output lives
Source:
Agents generate most of their context from **tool output**: shell commands, file reads, web fetches, SQL queries, and MCP tool calls. Left unmanaged, a single verbose command (`git diff`, a 10,000-row query, an installer log) can blow past the model’s context window, drive up cost, and bury the signal the agent actually needs.
Everruns runs every tool result through a **multi-stage pipeline** that shrinks what the model sees while keeping the full original recoverable. This page explains each stage, the order they run in, and, crucially, *where the full output goes* so the agent can get it back.
## The big picture
```plaintext
tool runs
│
▼
raw output ──────────────────────────────────────────────┐
│ │ (full, lossless)
▼ │
1. Verbosity budget (exec/sandbox tools only) │
│ auto/concise/normal/verbose/full │
▼ │
2. Capability hooks │
│ • Tool Output Distillation (non-exec tools) │
▼ ▼
3. Final infrastructure hooks /outputs/
│ • Persist Output (exec tools → VFS) {tool_call_id}.stdout
│ • Output Hard Limit (64 KiB ceiling) {tool_call_id}.stderr
▼ ▲
inline result → stored in the session, │
shown to the model next turn ──────────────────────┘
(read_file recovers the full original)
```
Two ideas run through the whole pipeline:
1. **Storage stays lossless.** Whatever the model sees inline, the full output is written to the session filesystem (the *destination*). The inline view always carries a pointer back to it.
2. **Each stage shrinks, none deletes.** Truncation, distillation, and masking only change the *view*. The agent can always `read_file` the persisted original.
## Stage 1: Verbosity budget (exec tools)
Exec and sandbox tools (`bash`, `*_exec`, sandboxed shells) clean their output (strip ANSI, collapse carriage returns) and apply a **verbosity budget** before returning. The mode is configurable per call; the default is `auto`:
* **Success (`exit_code == 0`)** → collapse to a compact summary (\~512 bytes), because the full log is persisted (Stage 3) and the agent rarely needs it inline.
* **Failure (non-zero exit)** → keep a larger diagnostic window (\~8 KiB) so the error stays debuggable in-loop.
The full pre-truncation output is stashed on the result as `raw_output` for the persistence hook to consume. Non-exec tools (MCP, web fetch, client tools) do **not** have a verbosity budget, that gap is what Stage 2 exists for.
## Stage 2: Tool Output Distillation (non-exec tools)
[Tool Output Distillation](https://everruns.com/docs/capabilities/) targets the tools Stage 1 doesn’t: **MCP tools and `web_fetch`**, whose results otherwise enter history verbatim. It runs as a capability hook, so it executes *before* the final hooks.
For a large non-exec result, distillation produces a compact, **content-aware** inline view:
| Output shape | What you get inline |
| ---------------- | ---------------------------------------------------------------------------- |
| Large JSON array | Schema-preserving sample: the first few rows + `[… N more items elided …]` |
| Long string | Head + tail window (both ends preserved), with a byte-elision marker |
| Unified diff | A diffstat-style summary: file + hunk headers and `+added / -removed` counts |
| Nested object | Each oversized field distilled; small fields untouched |
Before it replaces anything, distillation **persists the full original** to the session filesystem (same destination as Stage 3) and injects a recovery pointer. If persistence fails, or the session has no filesystem, it restores the verbatim output rather than leave a lossy result the agent can’t recover. **Reversibility is never sacrificed.**
Distillation is on by default in **Worker** and the harnesses built on it. Every transform is deterministic, so identical output distills identically and the model provider’s prompt cache keeps hitting across turns.
## Stage 3: Persistence and the hard limit
Two infrastructure hooks always run last, in order:
1. **Persist Output**: for tools that declare the `persist_output` hint (exec/sandbox), writes the full `raw_output` to the session VFS. When content is absent from the inline result, it adds a recovery pointer; complete inline results keep the retained file internal and do not invite a redundant read. It **skips** if a result already carries `output_files` (e.g. distillation already persisted it), so the two never double-write.
2. **Output Hard Limit**: a final, unremovable 64 KiB ceiling. By the time it runs, the result has usually already been budgeted or distilled, so it rarely fires; it’s a backstop against pathological cases.
## The destination, where full output lives
Everything the pipeline elides is recoverable from the **session filesystem**:
```plaintext
/outputs/{tool_call_id}.stdout ← full standard output
/outputs/{tool_call_id}.stderr ← full standard error (when present)
```
When the inline result omits persisted content, it carries the recovery pointer in `output_files` and `full_output`, plus a human-readable note telling the model to use `read_file` (with `offset`/`limit`) for the missing detail. Complete inline output has no model-facing pointer. Persisted streams are capped at 1 MiB each. Deleting the session cascades and removes them.
This is the key to aggressive shrinking: because the original is one `read_file` away, the inline view can be small without the agent losing the ability to drill in.
## How this relates to compaction
The pipeline above operates on **individual tool results at capture time**. [Context Compaction](https://everruns.com/docs/advanced/compaction/) operates **later**, across the whole conversation, when it approaches the context window, masking or summarizing older messages at serialization time.
They compose cleanly:
* The pipeline keeps each result lean as it’s produced.
* Compaction further masks older results when the *accumulated* history grows too large.
* [Infinity Context](https://everruns.com/docs/capabilities/) adds a `query_history` tool to retrieve older *messages* that scrolled out of the window.
Together: the pipeline controls per-result size, compaction controls total-history size, and both keep the full record recoverable.
## Summary
| Stage | Applies to | Effect | Destination of full output |
| ----------------- | -------------------------- | -------------------------------------------------------- | --------------------------------------------- |
| Verbosity budget | exec/sandbox | Compact summary on success, diagnostic window on failure | `raw_output` → persisted in Stage 3 |
| Distillation | MCP / web fetch / non-exec | Content-aware compact view | `/outputs/{id}.stdout` |
| Persist Output | `persist_output` tools | Lossless write + pointer | `/outputs/{id}.{stdout,stderr}` |
| Output Hard Limit | all | 64 KiB ceiling backstop | (already persisted) |
The agent always sees a lean view and can always recover the full original with `read_file`.
---
# Built-ins Overview
> Built-in harness types and capabilities that ship with Everruns. Harnesses define reusable behavior; Agent Sandbox Template bindings select compute.
Source:
Everruns ships with built-in **harness types** and **capabilities** that provide the foundation for agent sessions.
## Harnesses
A harness defines reusable session behavior: system prompt, default model, starter files, network policy, and bundled capabilities. Every session is assigned a harness. An Agent [Sandbox Template binding](https://everruns.com/docs/features/sandbox-templates/) independently selects the filesystem and compute target for new sessions.
| Harness | Description | Capabilities |
| -------------------------------------------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------- |
| [Base](https://everruns.com/docs/built-ins/harnesses/base/) | System essentials every agent needs | Compaction, error disclosure, tool-call repair, loop detection, approval guidance |
| [Conversation](https://everruns.com/docs/built-ins/harnesses/conversation/) | Default for dialogue, no workspace | Base + structured questions, message timestamps |
| [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) | Worker without compute | Base + files, AGENTS.md, skills, long context, budgeting, subagents, tasks |
| [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) | Worker with the Bashkit virtual shell | Worker + Bashkit shell, fixed Sandbox Template |
| [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/) | Worker with a full sandbox | Worker + the shell of a container or managed Sandbox Template |
| [Worker Base (deprecated)](https://everruns.com/docs/built-ins/harnesses/worker-base/) | Deprecated legacy bundle | Files, bash, project instructions |
| [Generic (deprecated)](https://everruns.com/docs/built-ins/harnesses/generic/) | Deprecated legacy bundle | 25 configured |
| [Data Analyst](https://everruns.com/docs/built-ins/harnesses/data-analyst/) | SQL databases, charts, persistent memory | Bashkit Worker + data capabilities; available as a built-in example |
[Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) is a managed Agent bound to Bashkit Worker.

See the [Harnesses feature guide](https://everruns.com/docs/features/harnesses/) for harness selection, API management, and the prompt stack model.
## Harness Examples
Harness examples are adoptable templates. Import them when you want a preconfigured starting point, then customize the resulting org-owned harness.
| Example | Import Name | Description |
| ------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Coding | `coding` | Sandbox Worker + coding behavior and GitHub Scout; the Agent Sandbox Template binding selects Daytona, a container, or another full sandbox |
| Data Analyst | `data-analyst` | Bashkit Worker + SQL databases, charts, persistent memory, and curated data knowledge |
## Capabilities
Capabilities are modular units that extend what an agent can do. Each can contribute tools, system prompt additions, and UI features.
Browse the full [Capabilities reference](https://everruns.com/docs/capabilities/) for the complete list organized by category.
---
# Base Harness
> The root of the built-in harness tree, carrying the system essentials every agent needs to run reliably.
Source:
The **Base** harness is the root of the [built-in harness tree](https://everruns.com/docs/features/harnesses/#built-in-harnesses). It carries only what every agent needs to run reliably, and adds no workspace, shell or chat tools.
## When to Use
* Custom tool composition on a reliable foundation
* Testing individual capabilities without a workspace or delegation
* Building your own harness family with Base as the parent
## Configuration
| Property | Value |
| ----------------- | ---------------------------------------------------------------------------------------------------------- |
| **Name** | `base` |
| **Parent** | None |
| **Capabilities** | Context compaction, error disclosure, tool-call repair, loop detection, parallel tool calls, soft approval |
| **System Prompt** | None. When no harness, agent or session supplies a prompt, the runtime uses “You are a helpful assistant.” |
| **Default Model** | None (inherits from agent or organization) |
Soft approval is guidance, not a permission wall. It adds the approval tools and costs nothing on safe work; turn it off per agent with `{"mode": "off"}`.
## Usage
Assign the Base harness when creating an agent or session:
```bash
curl -X POST http://localhost:9300/api/v1/agents \
-H "Content-Type: application/json" \
-d '{
"name": "Minimal Agent",
"harness_name": "base",
"capabilities": ["web_fetch"]
}'
```
The agent’s own capabilities are added on top. In this example `web_fetch` is the only domain tool.
```rust
let harness = everruns::Harness::base();
```
For a harness with no capabilities at all, build one with `Harness::builder` in the Framework or create a custom harness without a parent on the Platform.
## See Also
* [Conversation Harness](https://everruns.com/docs/built-ins/harnesses/conversation/), the default for simple dialogue
* [Worker Harness](https://everruns.com/docs/built-ins/harnesses/worker/), the worker kit without compute
* [Harnesses feature guide](https://everruns.com/docs/features/harnesses/), the tree and how to choose
---
# Bashkit Worker Harness
> Worker plus the Bashkit virtual shell, sealed to one recoverable Bashkit virtual workspace.
Source:
**Bashkit Worker** extends [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) with the Bashkit shell and fixes the primary Sandbox to Everruns’ managed Bashkit Virtual Workspace.
| | |
| ------------------- | ----------------------------------------------- |
| **Name** | `bashkit-worker` |
| **Parent** | `worker` |
| **Adds** | `bashkit_shell` |
| **Sandbox policy** | Fixed; Agent and Session overrides are rejected |
| **Primary Sandbox** | Managed Bashkit Virtual Workspace |
Use Bashkit Worker for support, operations, and general assistants that need a durable virtual filesystem and Bash-like commands but do not need native packages, processes, ports, or PTYs. It needs no sandbox provider, and the logical Sandbox and workspace survive replacement of runtime compute. [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) runs on it.
Choose [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/) when an agent needs a real machine, or [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) when it needs no shell. Add `sandbox_fleet` separately when an Agent should keep its primary workspace while creating and operating multiple explicitly addressed resource Sandboxes.
Custom Harnesses may inherit from Bashkit Worker, but they inherit the sealed Sandbox policy as well. Renaming the child or changing its capabilities does not make the primary Sandbox overridable.
```rust
let harness = everruns::Harness::bashkit_worker();
```
---
# Conversation Harness
> The default harness for simple dialogue, with no workspace and no shell.
Source:
Conversation inherits [Base](https://everruns.com/docs/built-ins/harnesses/base/) and adds the two chat affordances, `ask_user` for structured questions and `message_metadata` so the model sees when each message was sent. It provides no filesystem, bash, storage, delegation or web search tools. Add [Web Search](https://everruns.com/docs/capabilities/web-search/) on the agent when an assistant should search.
It is the organization default. Use it for chat assistants, support bots and Q\&A. Dad Jokes uses this harness plus `current_time`. Add task-specific capabilities on the agent when needed.
Workers do not inherit Conversation: [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) builds on Base directly, so an unattended worker does not pick up chat affordances it has no one to use with.
Choose `conversation` with the API’s `harness_name` or the CLI’s `--harness` option.
```rust
let harness = everruns::Harness::conversation();
```
Framework constructors load the same capability data as the hosted presets. Enable the host integrations required by the tools you use; a preset does not compile optional integrations into your application.
See [Harnesses](https://everruns.com/docs/features/harnesses/) for selection and inheritance.
---
# Data Analyst Harness
> Data analysis harness with SQL databases, persistent memory, interactive charts, and a structured analysis pipeline inspired by OpenAI's Dash.
Source:
The **Data Analyst** harness extends the [Bashkit Worker harness](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) with capabilities for data analysis: SQL databases, cross-session agent memory, rich visualization via OpenUI, and a curated knowledge scaffold. Its system prompt implements a structured 6-step analysis pipeline inspired by [OpenAI’s Kepler data agent](https://openai.com/index/inside-our-in-house-data-agent/) and the open-source [Dash](https://github.com/agno-agi/dash) project.
## When to Use
* Natural-language data analysis (ask questions, get SQL + charts)
* Interactive data exploration with visualization
* Agents that learn from corrections and remember them across sessions
* Analytics workflows grounded in curated knowledge bases (table docs, business rules, validated SQL)
## Configuration
| Property | Value |
| ----------------- | ------------------------------------------ |
| **Type** | `data-analyst` |
| **System Prompt** | Structured 6-step analysis pipeline |
| **Default Model** | None (inherits from agent or organization) |
## Analysis Pipeline
The system prompt guides the agent through six steps on every data question:
1. **Recall**: Read `/memory/agent/` for corrections, column mappings, and business definitions from earlier sessions
2. **Inspect**: Use `sql_schema` to verify table structure before writing SQL
3. **Plan**: State the query plan: tables, joins, filters, expected grain, and potential pitfalls
4. **Execute & Validate**: Run the query, then validate (zero rows? duplicates? NULL aggregations?). Self-correct if results look wrong
5. **Visualize**: Summarize findings in plain language, then render charts and tables via OpenUI
6. **Learn**: Write corrections and patterns to a file under `/memory/agent/` for future sessions
This mirrors the six-layer context pattern described in [OpenAI’s data agent blog post](https://openai.com/index/inside-our-in-house-data-agent/) and implemented by [Dash](https://github.com/agno-agi/dash).
## Bundled Capabilities
All [Bashkit Worker harness capabilities](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) plus:
| Capability | What it provides |
| -------------------- | --------------------------------------------------------------------------------------------------------- |
| Session SQL Database | `sql_execute`, `sql_query`, `sql_schema`, session-scoped SQLite databases that auto-create on first write |
| OpenUI | Rich interactive charts, tables, dashboards, and KPI cards rendered inline in chat |
| Todo List | `write_todos`, track multi-step analysis tasks |
| Data Knowledge | Mounts `/knowledge/` scaffold with directories for table docs, business rules, and validated SQL patterns |
## Knowledge Files
The harness mounts a `/knowledge/` directory scaffold in every session:
```plaintext
/knowledge/
index.md
tables/index.md # Add one .md per table: columns, types, gotchas
business/index.md # Add metric definitions, business rules, domain terms
queries/index.md # Add validated .sql files as reusable templates
```
These files are read-only scaffolds. Populate them with your organization’s curated knowledge to ground the agent’s SQL generation in reality. The agent reads these files before writing any SQL query.
Combined with [agent memory](https://everruns.com/docs/features/memory-scopes/), which the server mounts at `/memory/agent` in every session of the agent and which the agent edits with its file tools, this implements the layered context pattern:
| Layer | Source |
| ----------------------- | ---------------------------------------- |
| Table usage & schema | `sql_schema` tool + `/knowledge/tables/` |
| Business annotations | `/knowledge/business/` + AGENTS.md |
| Validated queries | `/knowledge/queries/` |
| Institutional knowledge | MCP servers (Slack, Notion, Confluence) |
| Learning memory | Files under `/memory/agent/` |
| Runtime context | `sql_query` / `sql_execute` |
## Example Session
```plaintext
User: Load this CSV and tell me which product category has the highest revenue
Agent: [reads /memory/agent/] [inspects any existing schema]
[creates table, imports data]
[runs SELECT category, SUM(revenue) ... GROUP BY category]
[validates: 5 categories, no NULLs, totals match]
[renders bar chart via OpenUI]
[appends to /memory/agent/corrections.md: "revenue is net of refunds"]
```
## See Also
* [Bashkit Worker Harness](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/), the parent harness this extends
* [Capabilities overview](https://everruns.com/docs/features/capabilities/), full capability catalog including memory and OpenUI
* [Harnesses feature guide](https://everruns.com/docs/features/harnesses/), harness selection and API management
---
# Generic Harness (deprecated)
> The default harness, bundling core capabilities for general-purpose agent sessions.
Source:
The **Generic** harness is deprecated. Existing agents, sessions and child harnesses retain its legacy behavior. Choose [Conversation](https://everruns.com/docs/built-ins/harnesses/conversation/) for dialogue, [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) for files, skills and delegation, or [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) or [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/) for a shell. Generic is not an alias for Worker. It configures 25 capabilities. Together they cover file operations, command execution, web access, memory, budgeting, context management, durable tool output, citations, and runtime safeguards.
## Legacy use
* General-purpose assistants
* Coding and scripting tasks
* Research workflows
* Any session where you want a solid set of defaults
## Configuration
| Property | Value |
| ----------------- | ------------------------------------------------------------- |
| **Type** | `generic` |
| **System Prompt** | Instruction hierarchy only; the agent’s own prompt follows it |
| **Default Model** | None (inherits from agent or organization) |
## Bundled Capabilities
| Capability | What it provides |
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [File System](https://everruns.com/docs/capabilities/file-system/) | Read, write, edit, list, grep, stat, and delete files in the session workspace (`/workspace`) |
| [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/) | Sandboxed bash shell for running commands, scripts, and text processing |
| [Web Fetch](https://everruns.com/docs/capabilities/web-fetch/) | Fetch web content with file download support |
| [Storage](https://everruns.com/docs/capabilities/session-storage/) | Key/value store for general data and encrypted secret storage |
| [Session](https://everruns.com/docs/capabilities/session/) | Access session metadata and manage session title |
| [Session Schedules](https://everruns.com/docs/capabilities/session-schedules/) | Create and manage cron-style schedules that wake the session |
| [AGENTS.md](https://everruns.com/docs/capabilities/agent-instructions/) | Reads AGENTS.md from workspace and injects project-level instructions |
| [Agent Skills](https://everruns.com/docs/capabilities/agent-skills/) | Discover and activate skills from `/.agents/skills/` |
| [Infinity Context](https://everruns.com/docs/capabilities/infinity-context/) | Trims older messages from the live prompt while exposing earlier history via `query_history` |
| [Auto Tool Search](https://everruns.com/docs/capabilities/tool-search/#auto-tool-search) | Defers tool schema loading to reduce prompt size, using the provider’s native tool search where available |
| [Context Compaction](https://everruns.com/docs/advanced/compaction/) | Auto-compacts context at 85% budget via cascading strategies |
| [Budgeting](https://everruns.com/docs/capabilities/budgeting/) | Budget awareness in the system prompt and a `check_budget` tool, which currently returns a placeholder; use the REST budget-check endpoint for detailed status |
| [Self-Budget](https://everruns.com/docs/capabilities/self-budget/) | Prompt-only guidance for reasoning about a user-requested indicative budget using session usage data |
| [Ask User](https://everruns.com/docs/capabilities/ask-user/) | Ask the user 1–4 structured questions, or collect a credential, and wait for the answer |
| Soft Approval | Prompt-level gate asking permission before a destructive, irreversible, or outward-facing action |
| Tool Output Persistence | Persists full tool output to `/.outputs/` before truncation for lossless retrieval |
| Tool Output Distillation | Distills large tool output before it enters the context |
| [Retrieval Citations](https://everruns.com/docs/capabilities/citation-retrieval/) | Claim-level citations for answers drawn from retrieval feeds |
| [Citation Verification](https://everruns.com/docs/capabilities/citation-verification/) | Stamps faithfulness verdicts on citations (deterministic `heuristic` mode by default) |
| [Parallel Tool Calls](https://everruns.com/docs/capabilities/parallel-tool-calls/) | Prefers parallel tool calls where the provider supports them |
| [Message Metadata](https://everruns.com/docs/capabilities/message-metadata/) | Annotates messages with timestamps |
| Human Intent | Adds model-authored intent narration to each tool call for UI rendering |
| BTW | Ephemeral side-question command for the current session |
| Tool Loop Detection | Detects repeated tool loops and injects a warning to break them |
| Error Disclosure | Shows full provider error detail (`detailed` mode) |
Infinity Context and Context Compaction work together to bound the active context while retaining older history. See [Context Compaction](https://everruns.com/docs/advanced/compaction/#worker-harness-defaults) for details.
## See Also
* [Base Harness](https://everruns.com/docs/built-ins/harnesses/base/), empty harness for full control
* [Platform Chat Harness](https://everruns.com/docs/built-ins/harnesses/platform-chat/), focused operator chat built on Base
* [Harnesses feature guide](https://everruns.com/docs/features/harnesses/), harness selection and API management
---
# Platform Chat
> The managed Agent for Everruns platform conversations, running on Bashkit Worker.
Source:
**Platform Chat** is a managed Agent that helps you operate your Everruns organization. It runs on [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/), which supplies a sealed, recoverable Bashkit virtual workspace. The Agent supplies its identity, instructions, platform access, introduction, conversation starters, and durable operator memory.
Platform Chat is built in and read-only: you cannot edit, archive, or delete it from the UI or the API. Use **Copy** on its page to make an editable Agent from it.
## Chat and side conversations
The sidebar’s **Chat** entry always opens your permanent conversation. It cannot be renamed, unpinned, archived, deleted, or reassigned. **New chat** opens a fresh side conversation with the same Agent. Its session is created when you send the first message; it starts without copied conversation history or workspace files.
**All chats** lists your side conversations. Playground conversations and runs with other agents stay outside Chat. Use Playground to select an Agent or harness and test its behavior.
## Chat beside your pages
Organization owners and admins can enable **Chat mode** in **Settings > Features**. Open an app page, then click **Chat** or press **Command-J** (**Ctrl-J** on Windows and Linux). The permanent conversation opens beside the current page. **Back to app** restores the full page and keeps the conversation and composer draft.
Each message shares the current page’s identity with Chat. On an Agent page, Chat can read the live form, including unsaved instructions and configuration, and apply requested edits to that draft. Gold markers identify **Draft edited by Chat**. Review the result, then use **Save changes** to persist it or **Discard** to reset the draft. Draft **Undo** restores the preceding form values, including your earlier manual edits, and refuses to overwrite later manual edits. If you change the form while Chat is thinking, its stale patch fails so it can read the current draft again. Chat does not edit credentials or connection grants through this form; sandbox policy uses its dedicated editor.
For separately requested saved-resource operations, Platform Chat still uses platform commands. Page context does not disable those operations, and a failed form edit is never permission to bypass review by saving directly. Agent saves attributed to the conversation receive persisted-change markers and **Undo all**. That Undo creates a history entry and checks the saved revision; unsupported inverses fail without saving. If a saved definition changes while your draft is dirty, the page keeps the draft and blocks Save until you discard it and review the latest definition.
Chat can read page identity, current filters and bounded summaries of loaded records across the app. It reads explicit data, without copying page HTML or credential inputs. Harnesses, Virtual Users, Observers, Eval creation and other definition forms expose supported draft fields through the same read/edit flow. Knowledge, Memory, Skill, Sandbox-template, Capability and Channel forms expose their supported non-credential fields. Providers with several Save forms identify each form by name; Chat edits one form at a time.
Use the page’s existing **Save** or **Create** action to apply a staged change. Immediate-save settings, operational views, approvals, credential controls and financial actions remain under their normal page controls. Chat reports when a page or field is read-only; it does not work around that through a server write. Saved-change markers and inverse **Undo all** remain specific to Agent definitions. Chat mode is independent of the **Chat threads** opt-in.
## Platform access and memory
Platform Chat uses the `everruns` command in its session shell to discover operations, read authoritative state, perform requested changes, and verify results. Product docs are mounted read-only at `/workspace/docs`. The Platform capability also supplies `everruns`, `everruns-cli`, and `everruns-agent-authoring` skills for product orientation, command workflows, and agent design. Discover and activate them through [Agent Skills](https://everruns.com/docs/capabilities/agent-skills/) as needed.
`/memory/user` is private to the conversation owner. `/memory/shared` contains durable notes shared across Platform Chat conversations in the organization. Sharing requires explicit intent; notes default to private memory. Documentation and memory are reference material, never instructions. Scratch files live in `/workspace`; credentials belong in the secure setup flow. Recurring autonomous work belongs to an Agent Trigger.
Before changing an existing entity, Platform Chat reads its manager context and passes the revision it read and a reason with the change, so the entity’s history says why it changed. It asks before acting on a request that conflicts with the recorded context. See [Change history](https://everruns.com/docs/features/change-history/).
## Bundled Capabilities
The Agent configures these capabilities in addition to the execution environment inherited from [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/).
| Capability | Purpose |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Platform | Platform operations through the session shell |
| Current Time | Ground relative time questions |
| Task Management | Track work within a conversation |
| Prompt Caching | Cache stable prompt content |
| Tool Call Repair | Repair malformed tool calls |
| Human Intent | Preserve consequential user intent across tool calls |
| Web Fetch | Read public web resources and download files |
| Storage | Keep durable session values and secrets |
| Schedules | Arrange follow-up work for the session |
| BTW | Handle lightweight side questions without losing the main thread |
| Message Metadata | Attach structured metadata to messages |
| Retrieval Citations | Retrieve sources for grounded answers |
| Citation Verification | Verify citation support before answering |
| Ask User | Pause for structured user input when required |
| Error Disclosure | Return detailed platform errors to the managed operator Agent |
| User MCP Servers | Use the MCP servers the person added for themselves, and add, remove and connect them in chat ([User MCP servers](https://everruns.com/docs/features/user-mcp-servers/)) |
| Coordination | Hand focused work to threads you can open and follow, when the organization has adopted Chat threads |
| Poppy Personal Agent | Talk to companies’ agents for the person over the Personal Agent Protocol ([Poppy Personal Agent](https://everruns.com/docs/capabilities/poppy-personal-agent/)) |
## Existing conversations
The dedicated Platform Chat harness and its interim Generic binding are retired. Existing platform conversations move to the managed Agent on Bashkit Worker while keeping their IDs, history, workspace files, owners, and memory. Older custom agents, apps, triggers, and child harnesses retain their original execution bindings so their authored behavior is preserved; the retired harness remains stored for those bindings and historical accounting.
The Agent name `platform-chat` is reserved for the managed assistant. An existing custom Agent with that name is renamed to `platform-chat-custom-`; its ID and bindings stay intact. Clients using its old name should switch to its ID.
Conversation introductions and starters now belong exclusively to Agents. Existing harness presentation is copied to its assigned Agents when those Agent fields are empty. Harness presentation fields are deprecated and no longer accepted on writes.
---
# Sandbox Worker Harness
> Worker plus a full sandbox shell, for agents that need real processes, packages and builds.
Source:
**Sandbox Worker** extends [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) and requires a full sandbox: a container or a managed provider such as Daytona, E2B or Modal. The Agent’s Sandbox policy chooses which one.
| | |
| ------------------- | ----------------------------------------------------------- |
| **Name** | `sandbox-worker` |
| **Parent** | `worker` |
| **Sandbox policy** | Required; must name a container or managed Sandbox Template |
| **Primary Sandbox** | Whatever the Agent’s policy selects |
Use it for coding agents, data work that installs packages, browser automation and long builds. The [Coding](https://everruns.com/docs/how-to/customize-a-harness/) harness example builds on it.
Sandbox Worker never falls back to Bashkit:
* Saving an Agent whose Sandbox policy can only resolve to Bashkit is rejected.
* An Agent with no Sandbox policy saves, but starting a Session on it fails with a message that names the missing policy.
Choose [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) for a virtual shell that needs no provider. Custom Harnesses that inherit from Sandbox Worker inherit the requirement too.
Sandbox Worker is a Platform harness. In the Framework, express the same need as an Environment requirement on your harness and bind an Environment that provides it.
See [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/) for configuring providers.
---
# Worker Harness
> The worker kit with no compute, for agents that work through tools, MCP servers and files.
Source:
Worker inherits [Base](https://everruns.com/docs/built-ins/harnesses/base/) and adds everything a working agent needs except a shell: session files, project instructions (`AGENTS.md`), session tools, durable and distilled tool output, skills, history retrieval, automatic tool discovery, budget awareness, todo lists, subagents, task controls and [Web Search](https://everruns.com/docs/capabilities/web-search/) on the organization’s default provider.
It runs no code. Choose it for agents that act through tools and MCP servers: triage and integration agents over Linear, Slack or GitHub, research agents over knowledge and files, and organizations that want no code execution at all. It needs no sandbox provider.
Worker is also the parent of the two shell workers:
* [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) adds the Bashkit virtual shell.
* [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/) adds a full sandbox.
An Agent on Worker may still set a Sandbox policy; the selected Sandbox Template then supplies the shell. Pick one of the shell workers when the agent always needs one.
Subagents create separate child sessions. Ordinary children inherit the parent configuration; blueprints provide specialist behavior. Depth and root-tree task limits bound delegation. Assigning this harness requires permission to assign its high-risk capabilities.
Web fetch, secret/KV tools, organization memory mounts and session scheduling remain opt-in. Recurring agent execution is configured with Agent Triggers.
Choose `worker` with the API’s `harness_name` or the CLI’s `--harness` option.
```rust
let harness = everruns::Harness::worker();
```
Framework constructors load the same capability data as the hosted presets. Enable the host integrations required by the tools you use; a preset does not compile optional integrations into your application. Delegation also needs a host with subagent and task backends. The default in-memory framework host does not supply these services; unregistered capability references remain inert.
See [Harnesses](https://everruns.com/docs/features/harnesses/) for selection and inheritance.
---
# Worker Base Harness (deprecated)
> Retired level kept for existing agents; new agents choose Worker, Bashkit Worker or Sandbox Worker.
Source:
**Worker Base** was the middle of the old single chain (Base, Conversation, Worker Base, Worker). The [harness tree](https://everruns.com/docs/features/harnesses/#built-in-harnesses) replaced it: its files and project instructions moved into [Worker](https://everruns.com/docs/built-ins/harnesses/worker/), and its shell moved into [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) and [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/).
The row stays active and managed, with its name, ID, parent (Conversation) and capabilities unchanged, so agents, sessions and adopted harness examples bound to it keep working. Selectors hide it until you choose Show deprecated.
| Instead of Worker Base | Choose |
| --------------------------- | ------------------------------------------------------------------------------- |
| Files and bash, no provider | [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) |
| Files and a real machine | [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/) |
| Files and tools, no shell | [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) |
In the Framework, `Harness::worker_base()` is deprecated and returns `Harness::bashkit_worker()`.
---
# 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 { ... }
// L180: fn validate(output: &Output) -> bool { ... }
// L220: struct Config { ... }
// L235: impl Config { ... }
// L236: fn load(path: &str) -> Result { ... }
// L270: fn merge(&mut self, other: &Config) { ... }
// L310: #[cfg(test)] mod tests { ... }
```
### Supported Languages
| Language | Extensions |
| ---------- | ----------------------------- |
| Rust | `.rs` |
| TypeScript | `.ts`, `.tsx`, `.mts` |
| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` |
| Python | `.py` |
### What Gets Outlined
* **Functions/methods** — name, parameters, return type
* **Structs/classes/enums** — name
* **Impl blocks / trait implementations** — with nested methods
* **Modules** — including `#[cfg(test)]` detection
* **Interfaces and type aliases** (TypeScript)
For unsupported languages, no outline is generated — the response still works, just without structural context for unread portions.
---
# Capabilities Overview
> Capabilities give an agent tools, system prompt fragments, and execution features. Index of every built-in capability.
Source:
Capabilities are modular units that extend what an agent can do. Each capability can contribute:
* **Tools**: callable functions the agent can invoke during conversations
* **System prompt additions**: context and instructions prepended to the agent’s prompt
* **Features**: UI elements unlocked when the capability is active (e.g., Workspace tab)
Agents compose capabilities; enable only what you need.
## Capability Reference
Every capability a production deployment offers, with the number of tools it adds by default. Rows marked **dev-only** are registered only at the `dev` deployment grade. A `FEATURE_*` variable identifies a [rollout grade override](https://everruns.com/docs/sre/environment-variables/#feature-rollout-grades); organisation enrolment also applies before assigning or using gated capabilities.
### Core
Fundamental capabilities for file operations, command execution, web access, session management, time awareness, task tracking, scheduling, and agent coordination.
| Capability | ID | Tools |
| ---------------------------------------------------------------------------------------------------- | --------------------------- | ----------------------------------------------------------- |
| [File System](https://everruns.com/docs/capabilities/file-system/) | `session_file_system` | 10 |
| [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/) | `bashkit_shell` | 1 |
| [Tools in Shell](https://everruns.com/docs/capabilities/tools-in-shell/) | `tools_in_shell` | 1 (`tools`, hidden from the model; it is the shell command) |
| [Host Shell](https://everruns.com/docs/capabilities/host-shell/) | `host_shell` | 1 (Framework-only) |
| [Session](https://everruns.com/docs/capabilities/session/) | `session` | 2 |
| [Automatic session titles](https://everruns.com/docs/capabilities/automatic-session-titles/) | `automatic_session_titles` | 0 |
| [Storage](https://everruns.com/docs/capabilities/session-storage/) | `session_storage` | 2 |
| [Web Fetch](https://everruns.com/docs/capabilities/web-fetch/) | `web_fetch` | 1 |
| [Web Search](https://everruns.com/docs/capabilities/web-search/) | `web_search` | 1 |
| [Current Time](https://everruns.com/docs/capabilities/current-time/) | `current_time` | 1 |
| [Message Metadata](https://everruns.com/docs/capabilities/message-metadata/) | `message_metadata` | 0 |
| [Ask User](https://everruns.com/docs/capabilities/ask-user/) | `ask_user` | 1 |
| [Task Management](https://everruns.com/docs/capabilities/task-management/) | `stateless_todo_list` | 2 |
| [Schedules](https://everruns.com/docs/capabilities/session-schedules/) | `session_schedule` | 3 |
| [Auto-Continue After Usage Limit](https://everruns.com/docs/capabilities/usage-limit-auto-continue/) | `usage_limit_auto_continue` | 0 |
| [AGENTS.md](https://everruns.com/docs/capabilities/agent-instructions/) | `agent_instructions` | 0 |
| [Agent Skills](https://everruns.com/docs/capabilities/agent-skills/) | `skills` | 2; `FEATURE_SKILLS` grade |
| Channel Thread Context | `channel_context` | 0 |
| System Commands | `system_commands` | 0 |
### Orchestration
Delegating work to other sessions and running it in the background.
| Capability | ID | Tools |
| ------------------------------------------------------------------------------------ | ---------------------- | --------------------------------------------------------- |
| [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/) | `subagents` | 0 (contributes the `spawn_agent` delegation target) |
| Session Tasks | `session_tasks` | 5 |
| Background Execution | `background_execution` | 1 |
| [Agent Handoff](https://everruns.com/docs/capabilities/agent-handoff/) | `agent_handoff` | 0 (contributes the `agent` `spawn_agent` target) |
| [A2A Agent Delegation](https://everruns.com/docs/capabilities/a2a-agent-delegation/) | `a2a_agent_delegation` | 0 (contributes the `external_a2a` `spawn_agent` target) |
| AG-UI Agent Delegation | `ag_ui_delegation` | 0 (contributes the `external_ag_ui` `spawn_agent` target) |
| [Coordination](https://everruns.com/docs/framework/coordination/) | `coordination` | 5 |
### Sandboxes
Cloud and container sandbox environments for isolated code execution.
| Capability | ID | Tools |
| -------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------- |
| Managed Environment | `session_sandbox` | 8 |
| [Sandbox Desktop](https://everruns.com/docs/capabilities/computer-use/#desktop-in-a-sandbox) | `sandbox_desktop` | 1 (added by a Sandbox Template with a desktop) |
| [Sandbox Fleet](https://everruns.com/docs/capabilities/sandbox-fleet/) | `sandbox_fleet` | 8 |
| [Daytona](https://everruns.com/docs/capabilities/daytona/) | `daytona` | 10 |
| [E2B](https://everruns.com/docs/capabilities/e2b/) | `e2b` | 6 |
| [Container Sandbox](https://everruns.com/docs/capabilities/container-sandbox/) | `container_sandbox` | 8 (needs `FEATURE_CONTAINER_SANDBOX=prod`) |
| [Docker Container](https://everruns.com/docs/capabilities/docker/) | `docker_container` | 5 (off by default, needs `FEATURE_DOCKER_CAPABILITY` rollout grade) |
### Browser
Browser automation and web interaction capabilities.
| Capability | ID | Tools |
| -------------------------------------------------------------------- | -------------- | ------------------------------------------- |
| [Browserless](https://everruns.com/docs/capabilities/browserless/) | `browserless` | 7 |
| [Browser Use](https://everruns.com/docs/capabilities/browser-use/) | `browser_use` | 1; `FEATURE_BROWSERLESS_BROWSER_USE` grade |
| [Computer Use](https://everruns.com/docs/capabilities/computer-use/) | `computer_use` | 1; `FEATURE_BROWSERLESS_COMPUTER_USE` grade |
### Data and knowledge
Structured data, knowledge retrieval, and memory.
| Capability | ID | Tools |
| -------------------------------------------------------------------------------------- | ----------------------- | ---------------------------- |
| [SQL Database](https://everruns.com/docs/capabilities/sql-database/) | `session_sql_database` | 3 |
| [Retrieval Citations](https://everruns.com/docs/capabilities/citation-retrieval/) | `citation_retrieval` | 0 |
| [Citation Verification](https://everruns.com/docs/capabilities/citation-verification/) | `citation_verification` | 0 |
| [Web Citations](https://everruns.com/docs/capabilities/citation-web/) | `citation_web` | 0 |
| [Data Knowledge](https://everruns.com/docs/capabilities/data-knowledge/) | `data_knowledge` | 0 |
| [Knowledge](https://everruns.com/docs/capabilities/knowledge/) | `knowledge` | 2; `FEATURE_KNOWLEDGE` grade |
| [Memory](https://everruns.com/docs/capabilities/memory/) | `memory` | 0; `FEATURE_MEMORY` grade |
### Media
Image generation and editing workflows.
| Capability | ID | Tools |
| ------------------------------------------------------------------------------------------ | --------------- | ----- |
| [OpenAI Image Generation](https://everruns.com/docs/capabilities/openai-image-generation/) | `gpt_image_gen` | 2 |
### Models and provider tools
Provider-executed tools and model selection.
| Capability | ID | Tools |
| ------------------------------------------------------------------------------------------ | --------------------------- | ------------------------------------ |
| [OpenAI Server Tools](https://everruns.com/docs/capabilities/openai-server-tools/) | `openai_server_tools` | 0 |
| [OpenRouter Server Tools](https://everruns.com/docs/capabilities/openrouter-server-tools/) | `openrouter_server_tools` | 0 |
| Model Scout | `model_scout` | 0 |
| OpenRouter Workspace | `openrouter_workspace` | 2 |
| OpenAI Agents API Runtime | `openai_agents_api_runtime` | 0; `FEATURE_OPENAI_AGENTS_API` grade |
### Integrations
External-service capabilities and blueprint-backed workflows.
| Capability | ID | Tools |
| ------------------------------------------------------------------------------------ | -------------------- | ------------------------------------ |
| [GitHub](https://everruns.com/docs/capabilities/github/) | `github` | 6 (7 with `allow_pull_requests`) |
| [GitHub Scout](https://everruns.com/docs/capabilities/github-scout/) | `github_scout` | 0 |
| [Slack](https://everruns.com/docs/capabilities/slack/) | `slack` | 4 |
| Cursor | `cursor` | 9 |
| [Agentic Resource Discovery](https://everruns.com/docs/integrations/ard/) | `resource_discovery` | 3 |
| [AgentID Sign-In](https://everruns.com/docs/integrations/agentid/) | `agentid` | 1 |
| [Brave Search](https://everruns.com/docs/integrations/brave-search/) | `brave_search` | 1 |
| [DuckDuckGo](https://everruns.com/docs/integrations/duckduckgo/) | `duckduckgo` | 1 |
| [Parallel Search and Fetch](https://everruns.com/docs/integrations/parallel/) | `parallel_search` | 0 (2 hosted MCP tools from Parallel) |
| [Jev Decisions](https://everruns.com/docs/integrations/typesafe/) | `jev` | 1 |
| [User MCP Servers](https://everruns.com/docs/capabilities/user-mcp-servers/) | `user_mcp` | 6 (only with `manage` on) |
| [Poppy Personal Agent](https://everruns.com/docs/capabilities/poppy-personal-agent/) | `poppy_client` | 5 |
### Platform
Agent self-management and platform control.
| Capability | ID | Tools |
| ------------------------------------------------------------ | ---------- | ----- |
| [Platform](https://everruns.com/docs/capabilities/platform/) | `platform` | 3 |
### Generative UI
Structured UI the agent renders in the chat.
| Capability | ID | Tools |
| ---------- | -------- | ----- |
| OpenUI | `openui` | 0 |
| A2UI | `a2ui` | 0 |
### Optimization
Performance and cost optimization for LLM interactions.
| Capability | ID | Tools |
| ---------------------------------------------------------------------------------------- | --------------------- | ----------------------------------------------------------- |
| [Infinity Context](https://everruns.com/docs/capabilities/infinity-context/) | `infinity_context` | 1 |
| [Context Compaction](https://everruns.com/docs/advanced/compaction/) | `compaction` | 0 |
| [Dreaming](https://everruns.com/docs/features/dreaming/) | `dreaming` | 0; `FEATURE_DREAMING` grade; organization Adoption opt-in |
| [Auto Tool Search](https://everruns.com/docs/capabilities/tool-search/#auto-tool-search) | `auto_tool_search` | 0 (adds `tool_search` on models without native tool search) |
| [OpenAI Tool Search](https://everruns.com/docs/capabilities/tool-search/#hosted-openai) | `openai_tool_search` | 0 |
| [Claude Tool Search](https://everruns.com/docs/capabilities/tool-search/#hosted-claude) | `claude_tool_search` | 0 |
| [Tool Search](https://everruns.com/docs/capabilities/tool-search/#client-side) | `tool_search` | 1 |
| [Budgeting](https://everruns.com/docs/capabilities/budgeting/) | `budgeting` | 1 |
| [Self-Budget](https://everruns.com/docs/capabilities/self-budget/) | `self_budget` | 0 |
| [Parallel Tool Calls](https://everruns.com/docs/capabilities/parallel-tool-calls/) | `parallel_tool_calls` | 0 |
| Native Async Tools | `native_async_tools` | 0 |
| [Prompt Caching](https://everruns.com/docs/capabilities/prompt-caching/) | `prompt_caching` | 0 |
### Safety
Streaming-output guardrails and runtime safety nets.
| Capability | ID | Tools |
| ------------------------------------------------------------------------------------------ | ------------------------- | ----- |
| [Prompt Canary Guardrail](https://everruns.com/docs/capabilities/prompt-canary-guardrail/) | `prompt_canary_guardrail` | 0 |
| [Tool Call Repair](https://everruns.com/docs/capabilities/tool-call-repair/) | `tool_call_repair` | 0 |
| [Output Truncation](https://everruns.com/docs/capabilities/output-truncation/) | `output_truncation` | 0 |
| [Guardrails](https://everruns.com/docs/capabilities/guardrails/) | `guardrails` | 0 |
| [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/) | `tool_approval` | 0 |
| [Dry Run](https://everruns.com/docs/capabilities/dry-run/) | `dry_run` | 0 |
| Progress Guard | `progress_guard` | 0 |
| [Soft Approval](https://everruns.com/docs/capabilities/soft-approval/) | `soft_approval` | 3 |
| [Tool Loop Detection](https://everruns.com/docs/capabilities/loop-detection/) | `loop_detection` | 0 |
The [`guardrails`](https://everruns.com/docs/capabilities/guardrails/) capability runs config-driven checks over model output and tool activity, blocking or logging per check. Checks can be deterministic (regex, blocklist, tool-call patterns) or model-backed, an `llm_judge` policy or a `moderation` decision, plus delegation to an external guardrail over scoped MCP. Each check binds a rule to a stage (`output`, `tool_use`, `tool_output`) with an `on_fail` of `block` or `log`; model-backed and MCP checks send a bounded excerpt off the sync path and fail open. Use advisory mode and the `POST /v1/capabilities/guardrails/dry-run` endpoint to tune against false positives before enforcing. For ready-made starting points, list the gallery at `GET /v1/capabilities/guardrails/examples`, each preset carries a `data_egress` signal (`none` vs. `utility_llm`), and drop a preset’s `config` into the agent’s `guardrails` capability config.
### Automation
Run shell commands at lifecycle and tool events. Block, mutate, or audit agent actions from outside the model.
| Capability | ID | Tools |
| ---------------------------------------------------------------- | ------------ | ----- |
| [User Hooks](https://everruns.com/docs/capabilities/user-hooks/) | `user_hooks` | 0 |
Harnesses also compose capabilities that are settings rather than features: Human Intent, BTW, Error Disclosure, and Tool Output Persistence and Distillation. The canonical [harness levels](https://everruns.com/docs/features/harnesses/) and [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) harness pages describe them.
## Quick Start
### Enable via API
```bash
curl -X POST http://localhost:9300/api/v1/agents \
-H "Content-Type: application/json" \
-d '{
"name": "My Agent",
"system_prompt": "You are a helpful assistant.",
"capabilities": ["session_file_system", "bashkit_shell", "web_fetch"]
}'
```
### Enable via UI
1. Navigate to the Agent detail page
2. Open the **Capabilities** section
3. Toggle capabilities on/off
4. Reorder with drag handles (order affects system prompt priority)
5. Save
### List available capabilities
```bash
curl http://localhost:9300/api/v1/capabilities
```
### Create a declarative capability
Declarative capabilities are persisted capability definitions made from data: system prompt text, scoped MCP servers, text file mounts, and skill packages. They use a public resource ID like `cap_...` and a stable capability reference like `declarative:research_pack`.
```bash
curl -X POST http://localhost:9300/api/v1/capabilities \
-H "Content-Type: application/json" \
-d '{
"definition": {
"name": "research_pack",
"display_name": "Research Pack",
"description": "Default research behavior and resources.",
"system_prompt": "Prefer primary sources and cite them clearly.",
"risk_level": "low"
}
}'
```
Agents and harnesses can use the canonical reference:
```json
{ "ref": "declarative:research_pack" }
```
For convenience, agent and harness write APIs also accept the plain unique name when it matches a declarative capability:
```json
{ "ref": "research_pack" }
```
## Key Concepts
### Dependencies
Some capabilities depend on others. Dependencies are resolved automatically at runtime, you don’t need to manually add them.
| Capability | Depends On |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/) | [File System](https://everruns.com/docs/capabilities/file-system/) |
| [Tools in Shell](https://everruns.com/docs/capabilities/tools-in-shell/) | [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/) |
| [Host Shell](https://everruns.com/docs/capabilities/host-shell/) | [File System](https://everruns.com/docs/capabilities/file-system/) |
| [Platform](https://everruns.com/docs/capabilities/platform/) | [Agent Skills](https://everruns.com/docs/capabilities/agent-skills/), [File System](https://everruns.com/docs/capabilities/file-system/) (when embedded docs are enabled) |
| [Agent Skills](https://everruns.com/docs/capabilities/agent-skills/) | [File System](https://everruns.com/docs/capabilities/file-system/) |
| [GitHub Scout](https://everruns.com/docs/capabilities/github-scout/) | [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/) |
| Managed Environment | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [Sandbox Desktop](https://everruns.com/docs/capabilities/computer-use/#desktop-in-a-sandbox) | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [Sandbox Fleet](https://everruns.com/docs/capabilities/sandbox-fleet/) | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [E2B](https://everruns.com/docs/capabilities/e2b/) | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [Daytona](https://everruns.com/docs/capabilities/daytona/) | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [Browserless](https://everruns.com/docs/capabilities/browserless/) | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [Browser Use](https://everruns.com/docs/capabilities/browser-use/) | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [Computer Use](https://everruns.com/docs/capabilities/computer-use/) | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [OpenAI Image Generation](https://everruns.com/docs/capabilities/openai-image-generation/) | [File System](https://everruns.com/docs/capabilities/file-system/) |
| [Data Knowledge](https://everruns.com/docs/capabilities/data-knowledge/) | [File System](https://everruns.com/docs/capabilities/file-system/) |
| [Knowledge](https://everruns.com/docs/capabilities/knowledge/) | [File System](https://everruns.com/docs/capabilities/file-system/) |
| [Memory](https://everruns.com/docs/capabilities/memory/) | [File System](https://everruns.com/docs/capabilities/file-system/) |
| [Dreaming](https://everruns.com/docs/features/dreaming/) | [File System](https://everruns.com/docs/capabilities/file-system/) |
| [Container Sandbox](https://everruns.com/docs/capabilities/container-sandbox/) | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| [A2A Agent Delegation](https://everruns.com/docs/capabilities/a2a-agent-delegation/) | Session Tasks |
| AG-UI Agent Delegation | Session Tasks |
### Features
Capabilities declare UI features they contribute. The session aggregates features from all active capabilities to decide which UI tabs to render.
| Feature | UI Element | Contributed By |
| -------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `file_system` | Workspace tab | [File System](https://everruns.com/docs/capabilities/file-system/), [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/), [Host Shell](https://everruns.com/docs/capabilities/host-shell/) |
| `secrets` | Storage tab | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| `key_value` | Storage tab | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| `schedules` | Schedules tab | [Schedules](https://everruns.com/docs/capabilities/session-schedules/) |
| `sql_database` | Database tab | [SQL Database](https://everruns.com/docs/capabilities/sql-database/) |
| `subagents` | Subagents tab | [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/) |
| `citations` | Inline citation chips + Sources strip | [Retrieval Citations](https://everruns.com/docs/capabilities/citation-retrieval/), [Citation Verification](https://everruns.com/docs/capabilities/citation-verification/) |
### Ordering
Capabilities are applied in the order configured on the agent. Earlier capabilities’ system prompt additions appear first. Place the most important context-setting capabilities first.
## See Also
* [Concepts](https://everruns.com/docs/getting-started/concepts/), how capabilities fit into the Harness → Agent → Session model
* [API Reference](https://everruns.com/docs/api/), full API documentation
* [MCP Servers](https://everruns.com/docs/features/mcp/), external tool servers as virtual capabilities
---
# A2A Agent Delegation
> Delegate work to configured external agents over the A2A protocol through spawn_agent, in the foreground or as a background session task.
Source:
| | |
| ---------------- | ---------------------- |
| **ID** | `a2a_agent_delegation` |
| **Category** | Orchestration |
| **Features** | `agent_runs` |
| **Dependencies** | `session_tasks` |
| **Risk** | High |
| **Availability** | Experimental |
A2A Agent Delegation lets an agent hand work to agents that run outside Everruns and speak the [A2A protocol](https://everruns.com/docs/features/a2a/). The agents it may call are listed in the capability’s configuration, so the model can only reach URLs you configured. The capability adds the `external_a2a` target type to the shared `spawn_agent` tool, the same dispatcher that [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/) and [Agent Handoff](https://everruns.com/docs/capabilities/agent-handoff/) extend.
**Experimental.** On the platform, adding the capability to an agent is the opt-in; there is no separate feature flag. In the [Framework](https://everruns.com/docs/framework/), turn on the `a2a` Cargo feature of `everruns` (it implies `local`) and add the capability by ID. See [A2A](https://everruns.com/docs/features/a2a/#a2a-in-the-framework).
## Configuration
```json
{
"ref": "a2a_agent_delegation",
"config": {
"agents": [
{
"id": "research",
"name": "Research agent",
"description": "Researches a topic and returns notes.",
"base_url": "https://agents.example.com/research"
}
]
}
}
```
| Field | Required | Description |
| ------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | Yes | Stable key the model passes as `target.id` |
| `name` | Yes | Human-readable name, shown in the system prompt |
| `description` | No | What the agent does, shown in the system prompt |
| `base_url` | One of `base_url` or `agent_card` | Where the Agent Card is fetched from (`/.well-known/agent-card.json`) |
| `agent_card` | One of `base_url` or `agent_card` | An inline Agent Card, used instead of discovery |
| `headers` | No | Static headers sent to the A2A endpoint. They are stored in the config, so they must not hold secrets |
| `preferred_binding` | No | `JSONRPC` or `HTTP+JSON` |
| `poll_interval_ms` | No | How often to poll the remote task, 100 to 60000. Default 1000 |
| `allow_local_urls` | No | Allows localhost and private addresses for local development. Honored only when `DEPLOYMENT_GRADE=dev`; rejected otherwise. Default `false` |
Without `allow_local_urls` (or outside `DEPLOYMENT_GRADE=dev`), `base_url` and every interface URL in an inline Agent Card must pass the safe-URL check, which refuses localhost, private ranges, and metadata addresses. At request time the same URLs are DNS-pinned and redirects are disabled. A session’s network access policy is also applied to the endpoint before and after the Agent Card is resolved.
## `spawn_agent`
| Parameter | Required | Description |
| -------------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `instructions` | Yes | Instructions sent to the external agent |
| `target` | Yes | `{ "type": "external_a2a", "id": "" }`. `target.external_agent_id` is a deprecated spelling of `id` |
| `mode` | No | `foreground` (default) or `background` |
| `wait_timeout_secs` | No | How long to wait for the remote task, 1 to 86400. Default 300 |
| `wake_on_completion` | No | Wake the session when a background run finishes. Default `true` |
| `result_schema` | No | JSON Schema for a required structured result from the external agent |
`message_schema` is not supported: a remote agent cannot call `report_task_progress`, so passing one fails. Text results are truncated to 8192 characters.
`foreground` waits for the remote task and returns its reply. `background` returns a `task_id` at once. When task tracking is available, each run is a session task of kind `external_agent`, so `wait_task` collects the result, `message_task` answers a remote task that asks for input, and `cancel_task` stops it.
## See also
* [A2A](https://everruns.com/docs/features/a2a/), serving agents over A2A and the delegation overview
* [Agent Handoff](https://everruns.com/docs/capabilities/agent-handoff/), delegation to first-party agents
* [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/), the session task tools
---
# Agent Handoff
> Delegate work to configured first-party agents with spawn_agent, as a background task, a blocking call, or an invited participant in the current session.
Source:
| | |
| ---------------- | ---------------- |
| **ID** | `agent_handoff` |
| **Category** | Orchestration |
| **Features** | `agent_handoffs` |
| **Dependencies** | None |
| **Risk** | High |
| **Availability** | Experimental |
Agent Handoff lets an agent hand work to other first-party Everruns agents. Each target is configured ahead of time with an agent and a harness, so the model can only pick from that list. The capability adds the `agent` target type to the shared `spawn_agent` tool, the same dispatcher that [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/) and [A2A Agent Delegation](https://everruns.com/docs/capabilities/a2a-agent-delegation/) extend. The child runs as its own session, or joins the current session as a member.
**Experimental.** Adding the capability to an agent is the opt-in; there is no separate feature flag.
For delegation to agents outside Everruns, see [A2A Agent Delegation](https://everruns.com/docs/capabilities/a2a-agent-delegation/). For ad hoc child sessions that use the parent’s own configuration, see [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/).
## Configuration
```json
{
"ref": "agent_handoff",
"config": {
"targets": [
{
"id": "billing",
"name": "Billing agent",
"description": "Answers invoice and refund questions",
"agent_id": "agent_...",
"harness_id": "harness_...",
"required_connections": ["github"]
}
]
}
}
```
| Field | Required | Description |
| ---------------------- | -------- | ---------------------------------------------------------------- |
| `id` | Yes | Stable key the model passes as `target.id` |
| `name` | Yes | Human-readable name, shown in the system prompt |
| `description` | No | What the target does, shown in the system prompt |
| `agent_id` | Yes | Public ID of the target agent |
| `harness_id` | Yes | Public ID of the harness the target runs on |
| `required_connections` | No | Provider connections that must exist before the handoff starts |
| `required_scopes` | No | Non-secret scope labels recorded for audit and resource metadata |
The system prompt lists every configured target by name, ID, and description.
## `spawn_agent`
| Parameter | Required | Description |
| ---------------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `name` | Yes | Name for this delegated run |
| `instructions` | Yes | Instructions for the target. Must not contain credentials |
| `target` | Yes | `{ "type": "agent", "id": "" }` |
| `mode` | No | `background`, `foreground`, or `invite` |
| `public_context` | No | Non-secret structured context, appended to the instructions |
| `result_schema` | No | JSON Schema for the child’s final result. The child must call `report_result` before the task can succeed |
| `message_schema` | No | JSON Schema for structured progress messages. The child gets `report_task_progress` |
### Modes
| Mode | Behavior |
| ------------ | -------------------------------------------------------------------------------------- |
| `background` | Default when session task tracking is available. Returns a `task_id` right away |
| `foreground` | Default otherwise. Blocks until the handoff finishes and returns the result |
| `invite` | Adds the target agent as a member of the current session instead of starting a new one |
A background handoff is tracked as a session task of kind `agent_handoff`, so the parent manages it with the session task tools (`list_tasks`, `get_task`, `message_task`, `cancel_task`, `wait_task`). Background mode fails when task tracking is not available.
Invite mode does not create a child task, so it rejects `result_schema` and `message_schema`. It also refuses the invite when the target’s configuration conflicts with the current session’s: the same capability with a different config, the same mounted file path with different contents, or the same MCP server name with a different definition.
### Connections
When a target lists `required_connections` and the session cannot resolve a connection for one of those providers, `spawn_agent` returns a `connection_required` result naming the provider. The client collects the credentials through the Connections flow. The system prompt tells the agent never to ask for tokens in chat or pass them in tool arguments.
## See also
* [A2A Agent Delegation](https://everruns.com/docs/capabilities/a2a-agent-delegation/), delegation to external agents
* [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/), child sessions from the parent’s own configuration, and the session task tools
---
# AGENTS.md
> Project instructions resolved hierarchically from workspace files such as AGENTS.md and injected as the leading user message on every turn.
Source:
| | |
| ---------------- | -------------------- |
| **ID** | `agent_instructions` |
| **Category** | Core |
| **Features** | None |
| **Dependencies** | None |
Resolves project instruction files hierarchically, from the session filesystem root down to the working directory, and injects them as the leading user-role message on every turn. By default it reads `AGENTS.md`, Everruns’ implementation of the [`AGENTS.md`](https://agents.md/) open convention. Configure `files` when an agent should also resolve another file such as `CLAUDE.md` at every hierarchy level.
Workspace files are untrusted third-party content, so they never enter the system prompt: harness safety instructions always take precedence, and file edits never invalidate the cache-stable system prefix.
## Tools
None. This capability only contributes conversation context, never system prompt.
## How it works
1. The agent receives a message.
2. Before the model call, the capability resolves the configured filenames from the filesystem root down to the working directory. A `docs/AGENTS.md` applies to work under `docs/`; deeper files override shallower ones on conflict. Files in sibling subtrees are never loaded.
3. The assembled block opens with a trust framing header and wraps each file in `` tags, broadest scope first.
4. The block is injected as the leading user-role message: visible to the model, re-resolved every turn, and below system instructions in precedence.
Edits during a session apply on the next turn, with no restart. If a configured file doesn’t exist, the agent operates normally.
## Prompt order
Every turn the model sees, top to bottom:
1. **System prompt**: harness safety instructions, tool guidance, role (cache-stable).
2. **Conversation context**: the resolved `AGENTS.md` hierarchy, broadest scope first.
3. **Conversation history and the latest user message**.
Explicit user instructions win over project files on conflict; system instructions always win over both.
## Config
```json
{
"files": ["AGENTS.md", "CLAUDE.md"]
}
```
`files` is optional. When omitted, Everruns resolves `AGENTS.md` at every level from the root to the working directory. A config may list at most 16 files.
## Limits
* **32 KiB per file**. Longer content is truncated with a warning.
* **128 KiB total per turn**, which bounds hierarchy depth. Deeper files past the budget are omitted with an explicit note.
* At most 16 instruction files resolved per turn.
* Plain Markdown, no required sections.
## Notes
* Works with [File System](https://everruns.com/docs/capabilities/file-system/) tools, so an agent can update its own instructions.
* Missing configured files are ignored, with no error.
## Do something
* [Use AGENTS.md for project instructions](https://everruns.com/docs/how-to/use-agents-md/): full guide with examples.
* [Equip an agent with tools](https://everruns.com/docs/how-to/equip-agents-with-tools/): adding capabilities generally.
## See also
* [File System](https://everruns.com/docs/capabilities/file-system/): manage the AGENTS.md file.
* [Agent Skills](https://everruns.com/docs/capabilities/agent-skills/): another way to inject specialized instructions.
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Agent Skills
> Discover and activate portable skill packages from the session workspace at runtime.
Source:
| | |
| ---------------- | ---------------------------------------------------------------------------- |
| **ID** | `skills` |
| **Category** | Core |
| **Features** | None |
| **Dependencies** | [`session_file_system`](https://everruns.com/docs/capabilities/file-system/) |
Discover and activate skills from `/.agents/skills/` in the session filesystem. Skills are portable instruction packages following the [Agent Skills](https://agentskills.io/) open specification.
## Tools
### `list_skills`
Scan `/.agents/skills/` for available skills. Returns names and descriptions only (\~100 tokens per skill).
### `activate_skill`
Load a skill’s full instructions by name.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ---------------------------------------------------- |
| `name` | string | yes | Skill name (directory name under `/.agents/skills/`) |
Returns: full SKILL.md content and list of bundled files.
## How It Works
Skills use progressive disclosure to keep context efficient:
1. **Discovery** (\~100 tokens), `list_skills` returns only names and descriptions
2. **Activation** (<5000 tokens), `activate_skill` loads the full SKILL.md instructions
3. **Resources** (on-demand), bundled files accessible via [File System](https://everruns.com/docs/capabilities/file-system/) tools
## Workspace layout
```plaintext
/.agents/skills/
deploy/
SKILL.md
templates/
k8s-deploy.yaml
code-review/
SKILL.md
```
## Notes
* Skills are per-session (uploaded to session filesystem)
* Path traversal protection on skill names
* Invalid SKILL.md files are reported but don’t block discovery of other skills
* For organization-wide skills, see the [Skills Registry](https://everruns.com/docs/features/skills/#skills-registry)
## See Also
* [Agent Skills feature guide](https://everruns.com/docs/features/skills/), detailed skills documentation
* [Skills Registry](https://everruns.com/docs/features/skills/#skills-registry), API-managed skills
* [AGENTS.md](https://everruns.com/docs/capabilities/agent-instructions/), simpler alternative for project context
* [File System](https://everruns.com/docs/capabilities/file-system/), upload skill files
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Ask User
> Let an agent ask the user a small batch of structured questions, and wait for the answer, instead of guessing or ending the turn in prose.
Source:
| | |
| ---------------- | ---------- |
| **ID** | `ask_user` |
| **Category** | Core |
| **Features** | None |
| **Dependencies** | None |
Gives the agent one tool for collecting decisions it cannot make on its own. Instead of guessing, or ending the turn with a paragraph of questions and hoping the user answers all of them, it asks 1–4 structured questions and waits.
The user sees a card with the questions. Choice questions show described options, and text questions show a free-form field. Answering resumes the turn.
Opt-in on the canonical levels. Included in deprecated [Generic](https://everruns.com/docs/built-ins/harnesses/generic/) and [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) harnesses. Not on [Base](https://everruns.com/docs/built-ins/harnesses/base/), which has no interactive surface.
## When to enable it
Enable it for agents that work *with* a person: anything where the agent’s first guess about intent, scope, or preference is likely to be wrong and expensive to undo.
Leave it off for agents that run unattended on a schedule or a trigger. It will not hang them — a client that cannot render a choice gets the model’s declared defaults immediately, while text and secret questions are declined — but an agent nobody is watching should be built to decide, not to ask.
## Not a consent gate
This is the boundary worth being clear about before enabling both:
| | `ask_user` | `request_approval` |
| --------- | ---------------------------------------- | --------------------------- |
| For | Decisions and preferences | Permission to act |
| Example | “Which environment?” | “May I delete this bucket?” |
| No answer | Choice defaults; text and secret decline | Stays unresolved |
Choice questions **auto-resolve**. A choice nobody answers falls back to the option the model marked as recommended. Text and secret questions are skipped instead because they have no default value. None of these outcomes grants permission, so a destructive, irreversible, or outward-facing action must go through `request_approval` (the `soft_approval` capability), whose wait does not auto-resolve.
Both are enabled together on the interactive harnesses for exactly this reason: the agent needs somewhere to put a preference so it stops putting permission questions there. The system prompt states the rule, but the capability pairing is what makes it followable.
## Question kinds
**Choice** — 2 to 6 options, single- or multi-select, optionally with a free-text “Something else” path. Options are ordered most-applicable-first, because that is the order a fallback follows.
**Text** — one free-form answer with no options. Use it for open questions such as a branch name. An unanswered text question is declined rather than submitted as an empty answer.
**Secret** — collects a credential. The value is stored encrypted in [session storage](https://everruns.com/docs/capabilities/session-storage/) and the agent receives a reference (`session:MY_TOKEN`), never the value itself, so it cannot reach the conversation history or the agent’s context. Tools resolve the reference by name. A secret question never auto-resolves: there is no such thing as a default credential, so an unanswered one is declined and proceeding without it becomes the agent’s explicit decision.
Use the secret kind rather than asking for a key in chat. A key typed into an ordinary message stays in the session history in plain text.
## Tools
| Tool | Description |
| ---------- | ------------------------------------------------------------------------------------------ |
| `ask_user` | Ask 1–4 choice or free-form questions, or collect one credential, and wait for the answer. |
## Configuration
None. Limits are fixed by the contract:
| Limit | Value |
| --------------------------- | ------------------------------------------------------- |
| Questions per call | 1–4 (a secret question must be alone) |
| Options per choice question | 2–6 |
| Timeout | 300 seconds, which the agent may shorten but not extend |
## What the agent is told
The result says what was chosen **and who chose it** — a person, a timeout, or an unattended fallback. An agent that reads a fallback as a considered answer will act with more confidence than the answer deserves, so provenance travels with it.
A declined question is a finished decision. The agent must not ask it again.
## See Also
* [Session Storage](https://everruns.com/docs/capabilities/session-storage/), where a collected secret is kept
* [Implementing a responder](https://everruns.com/docs/framework/ask-user/), for embedding applications
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Automatic session titles
> Automatically name conversations and update titles when the primary topic changes, independently of the agent's tools.
Source:
| | |
| ---------------- | -------------------------- |
| **ID** | `automatic_session_titles` |
| **Category** | Session |
| **Tools** | None |
| **Dependencies** | None |
Enable **Automatic session titles** on a harness, agent, or session. The platform checks the conversation after a completed turn without waiting on the title model before delivering the agent’s answer. The `session` capability and its `write_session_title` tool are optional.
## Model selection
By default, titles use the organization’s **Small model**, configured under **Models → Defaults**. If that setting is empty, titles inherit the session’s model, including its agent, harness, and organization defaults.
In capability settings, choose **Use session model** to always inherit the session model, or select a specific model and provider. An unavailable selected model skips generation; it never switches accounts or uses deployment utility credentials. Title requests count toward session usage and budgets.
```json
{
"ref": "automatic_session_titles",
"config": {
"model_id": "session",
"retitle": true
}
}
```
Omit `model_id` to use the organization Small model default. Set `retitle` to `false` to generate only the initial title.
## When titles change
The first check follows the first completed turn with a user message. Greetings or insufficient context can leave the session untitled. Later checks require at least five minutes and new user messages: 4 after a changed title, then 8, 16, and 32 between checks if the current title stays useful. The interval stays at 32 until a title changes. Failed attempts also back off. Autonomous turn continuations do not count as new user messages.
One bounded model request decides whether to keep the title or replace it. There is no separate intent-classification request on every turn. The title model sees a limited recent text transcript and no tool results or attachments. Generation is best effort: an outage leaves the existing title in place.
## Explicit titles take priority
A title supplied at creation, renamed through the API or UI, or written with `write_session_title` becomes explicit and is never overwritten automatically. Writing the same text explicitly also takes ownership. An explicit rename wins even when a title request is already running.
The existing [Session capability](https://everruns.com/docs/capabilities/session/) remains available for agent-directed naming. Leave its `auto_title` flag off when using this capability; enabling that flag asks the main agent to name the session explicitly, which takes priority over automatic maintenance. Title text changes emit the usual `session.title.updated` event.
---
# Bashkit Shell
> Run Bash commands in a sandboxed interpreter with process isolation, resource limits, streaming output, and workspace-only filesystem access.
Source:
| | |
| ---------------- | ---------------------------------------------------------------------------- |
| **ID** | `bashkit_shell` (legacy alias: `virtual_bash`) |
| **Category** | Execution |
| **Risk** | High, assignment requires an org **Admin** |
| **Features** | `file_system` (enables the Workspace tab) |
| **Dependencies** | [`session_file_system`](https://everruns.com/docs/capabilities/file-system/) |
Execute bash commands in a sandboxed environment with no access to the host system. The session filesystem is mounted at `/workspace`, so commands read and write the same files as the [File System](https://everruns.com/docs/capabilities/file-system/) tools.
## About Bashkit
This capability runs on [**Bashkit**](https://bashkit.sh), a virtual Bash interpreter written in Rust. It executes shell scripts in-process, with no real shell, no subprocess spawning, and no host access, which makes it suitable for running untrusted scripts in multi-tenant agent environments. Browse the source on [GitHub](https://github.com/everruns/bashkit).
Bashkit provides:
* **The shell language**: variables, parameter expansion, command substitution, arithmetic, pipelines, redirections, control flow, functions, arrays, globs, and here-documents.
* **Built-in commands** implemented in Rust: file I/O and navigation (`cat`, `ls`, `find`, `cp`, `mv`), text processing (`grep`, `sed`, `awk`, `jq`, `sort`), archives (`tar`, `gzip`), and network commands (`curl`, `wget`).
* **A pluggable virtual filesystem**: Everruns plugs in the session file store, so there is no copy step before or after a command.
* **Resource limits** on command count, loop iterations, and function depth, so a runaway script cannot exhaust shared infrastructure.
Because the interpreter is sandboxed by construction, bash here is **not** a shell-out to the host: there is no `/bin/bash` process, no direct network stack, and no filesystem beyond the session workspace. Outbound HTTP for `curl`/`wget` is off by default and can be enabled per agent (see **Outbound HTTP** below).
## Tools
### `bash`
Execute a shell command (or a multi-line script).
| Parameter | Type | Required | Description |
| ------------- | ------- | -------- | -------------------------------------------------------- |
| `commands` | string | yes | Shell command(s) to execute |
| `working_dir` | string | no | Working directory (default: `/workspace`) |
| `timeout_ms` | integer | no | Timeout in milliseconds (default: `30000`, max: `60000`) |
| `output` | string | no | Output verbosity (`auto`, `normal`, …; default: `auto`) |
Returns `stdout`, `stderr`, `exit_code`, and a `success` flag. Output streams live to the UI and CLI via `tool.output.delta` events while the command runs. On timeout, any partial output captured so far is returned alongside the error.
This tool also supports background execution, long scripts can run detached and report progress without blocking the agent loop.
## Filesystem
The interpreter exposes a single mount:
* **`/workspace`** maps to the session file store. Reads and writes are live, files created by bash are immediately visible to the File System tools and vice versa.
* Paths outside `/workspace` (for example `/etc`, `/home/agent`, `/tmp`) do not exist and cannot be written.
* Symlinks are unsupported; `chmod` is a no-op (the session filesystem does not track Unix permissions, and files are executable by default).
Default environment: `HOME=/home/agent`, `SHELL=/bin/bash`, `PATH=/usr/local/bin:/usr/bin:/bin`, `WORKSPACE=/workspace`, user and host `everruns`.
## Resource limits
Every invocation runs under fixed limits to prevent runaway scripts:
| Limit | Value |
| -------------------- | ------------------------------------- |
| Max commands per run | 1,000 |
| Max loop iterations | 10,000 |
| Max function depth | 100 |
| Max script size | 1 MB |
| Max memory | 10 MB |
| Parser timeout | 5 s |
| Wall-clock timeout | `timeout_ms` (default 30 s, max 60 s) |
## Outbound HTTP (optional)
Set the capability config `{"enable_http": true}` to let scripts use `curl` and `wget`. Every request, including each redirect hop, is routed through the platform egress boundary, where the agent/session network access list and the deployment-wide system allowlist are enforced. Policy denials surface as curl’s native `access denied` failure (exit code 7). Without the flag, the interpreter has no network path at all.
### API keys the agent never sees
A **bound secret** is an API key stored with the URL it may be sent to and how (`Authorization: Bearer`, or a named header). With outbound HTTP on, the shell sees `$NAME` as a placeholder, and the platform swaps in the real key only in that header and only for requests to the bound URL:
```bash
curl -sS https://api.example.com/v1/items -H "Authorization: Bearer $EXAMPLE_API_KEY"
```
The model can never read the value: printing `$NAME` shows the placeholder, a request to any other host (including a redirect target) carries the placeholder, and the key is redacted if a response echoes it back. The bound URL still has to pass the agent’s network access list.
Set one for every session of an agent on the agent’s **Credentials** tab: **Add binding**, choose **HTTP API**, and give the shell variable, the URL and how it is sent. Platform Chat or the CLI can declare it without a value, and you paste the value on that tab:
```bash
everruns agents credentials create --agent-id agent_01h9 \
--env-name EXAMPLE_API_KEY --url https://api.example.com --label 'Example API key'
```
Set one for a single session with `bindings` on `PUT /v1/sessions/{session_id}/storage/secrets`. Over MCP, `session_set_secret` takes `url`, `auth`, `header` and `scope` and collects the value on a secure page, so it never passes through the MCP client.
## Security
* **Sandboxed**: no direct network access (outbound HTTP is opt-in and egress-routed), no host filesystem, no subprocess spawning.
* **High risk**: because it exposes arbitrary scripted code execution, assigning `bashkit_shell` to an agent requires an org **Admin**. Existing agents that already had it keep working; the gate applies to new assignments only.
* Built-in observability hooks emit structured `tracing` events per builtin and on interpreter errors (tagged with the session ID) without logging argument values or command output.
## Notes
* Commands operate on the same `/workspace` as [File System](https://everruns.com/docs/capabilities/file-system/) tools.
* Built-in commands support ` --help`, and many also support ` --version`.
* Common builtins: `cd`, `ls`, `cat`, `echo`, `grep`, `head`, `tail`, `sed`, `find`, plus shell features like pipes, redirections, and command substitution. `grep` is backed by the session’s indexed search.
## See Also
* [Bashkit project](https://bashkit.sh), the interpreter powering this capability
* [File System](https://everruns.com/docs/capabilities/file-system/), file operations on the same workspace
* [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/), background and parallel execution
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Browser Use
> Let an agent work in a browser through the page structure, with element refs, form filling, and tabs, on any model.
Source:
| | |
| ---------------- | ---------------------------------------------------------- |
| **ID** | `browser_use` |
| **Category** | Browser |
| **Features** | Leased resources |
| **Dependencies** | `session_storage` |
| **Risk** | High (the model reads and acts on whatever the page shows) |
| **Availability** | Experimental |
Browser use lets an agent work in a web browser through what the page is made of, not only what it looks like. The agent reads the page as an accessibility tree, where every element it can act on carries a ref such as `ref_3`. It then clicks, hovers, scrolls to, or fills that element by ref. It can still take screenshots and act on viewport coordinates when the structure is not enough, and it can open, switch, and close tabs.
The `browser` tool is a regular function tool, so it works with any model. Screenshots and `zoom` need a model that accepts images; the other actions return text. It follows the action vocabulary of Claude’s `browser_toolset_20260801`. On Claude models that take that toolset, the agent uses Claude’s own browser tool instead, which those models are trained on, and reads the open tabs from the toolset’s browser state. The actions, the safety rules, and the limits are the same either way.
The browser is the session’s persistent [Browserless](https://everruns.com/docs/capabilities/browserless/) browser. Connect Browserless from **Settings → Connections** first. The agent shares that browser with the Browserless tools and with [Computer Use](https://everruns.com/docs/capabilities/computer-use/), so a page one opens is a page the others see.
## Tools
### `browser`
Performs one action. Every successful result carries a `browser_state` object: every open tab with its `tab_id`, title and URL, which tab is active, and in `state_changes` the tabs opened since the previous result.
A target is either `{"type": "ref", "ref": "ref_3"}` or `{"type": "coordinate", "x": 412, "y": 230}`, in pixels of the viewport.
| `action` | Parameters | What it does |
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `read_page` | optional `filter` (`interactive` or `all`), `depth`, `ref` | Read the page as an accessibility tree with refs. By default it lists what is in the viewport; `interactive` lists only the controls; `all` includes the rest of the page; `ref` reads one element’s subtree |
| `find` | `query` | Up to 20 elements that best match a description such as `search box` or `sign up button` |
| `get_page_text` | none | The page’s text, main content first |
| `form_input` | `target` (a ref), `value` (text, a choice, or `true`/`false` for a checkbox) | Set a field’s value and fire its `input` and `change` events |
| `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click` | `target`, optional `modifiers` such as `shift` | Click the element’s center or the point |
| `hover`, `mouse_move` | `target` (`mouse_move` takes a coordinate) | Move the pointer |
| `left_click_drag` | `from`, `target` | Press, drag, and release |
| `left_mouse_down`, `left_mouse_up` | `target` | Press or release the left button there |
| `scroll` | `target`, `scroll_direction`, optional `scroll_amount` (1 to 10, default 3) | Scroll at that point |
| `scroll_to` | `target` (a ref) | Scroll the element into view |
| `type` | `text` | Type at the keyboard focus |
| `key` | `text` (space-separated keys or combos, such as `Tab Tab Enter` or `ctrl+a`), optional `repeat` | Press keys |
| `hold_key` | `text`, `duration` (seconds, up to 30) | Hold keys down, then release them |
| `wait` | `duration` (seconds, up to 30) | Pause |
| `screenshot` | none | Capture the viewport |
| `zoom` | `region` (`[x0, y0, x1, y1]`) | That part of the viewport, enlarged to the viewport size |
| `navigate` | `url`, or `back`, `forward`, `reload` | Load a page or step through history |
| `new_tab`, `list_tabs` | none | Open a blank tab and make it current, or list the tabs |
| `switch_tab`, `close_tab` | `tab_id` | Make a tab current, or close it |
Any action also takes an optional `tab_id` to act on that tab; it becomes the current tab.
Example calls:
```json
{ "action": "find", "query": "email field" }
{ "action": "form_input", "target": { "type": "ref", "ref": "ref_4" }, "value": "ada@example.com" }
{ "action": "left_click", "target": { "type": "ref", "ref": "ref_7" } }
```
Refs belong to the page they were read from. After a navigation, or when the page removes the element, using one returns an error that tells the agent to read the page again; a ref is never applied to a different element.
`javascript_exec`, `file_upload`, `read_console`, and `read_network` are not offered. A call that asks for one is refused.
## Configuration
| Field | Default | Description |
| ------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `viewport_width` | `1280` | Viewport width in pixels (320 to 1920) |
| `viewport_height` | `800` | Viewport height in pixels (320 to 1200) |
| `max_actions_per_session` | `500` | Hard cap on actions in one session |
| `native_tools` | `true` | Use Claude’s native browser toolset on models that have one. When off, every model uses the `browser` function tool. |
When Claude sends several browser actions in one turn and one fails, the later ones are not run; each answers that an earlier action failed.
## Safety
* **The model reads everything on the page.** Text, tab titles, and URLs can try to steer the agent. The capability tells the model to treat them as untrusted data and to stop and ask before typing credentials, making purchases, sending messages, or confirming irreversible actions. Add [`tool_approval`](https://everruns.com/docs/capabilities/tool-approval/) when a person must approve every `browser` call before it runs.
* **Refs stay out of the page.** Everruns keeps the table that maps refs to elements; the page cannot read or move it, so a ref read as “Cancel” cannot be pointed at “Buy”.
* **Page scripts cannot tamper with reads and fills.** `get_page_text` and `form_input` run in a separate script world, so a page that overrides built-in functions does not change what they read or write.
* **Keep credentials out of reach.** Do not give a browser-use agent a browser that is signed in to accounts it should not use.
* **Egress.** `navigate` takes only http and https addresses; an address without a scheme is read as https. It refuses private and internal addresses and follows the session’s network access list. Everruns makes every request the page makes, in every tab, and applies the same rules to each one. If a page navigates somewhere blocked on its own, it is reset to a blank page and the action reports an error.
* **Budget.** `max_actions_per_session` stops runaway loops.
## Availability
Browser use is experimental and ships behind the `browserless_browser_use` feature flag (`FEATURE_BROWSERLESS_BROWSER_USE` sets its rollout grade).
---
# Browserless
> Headless browser automation through Browserless for screenshots, DOM reading, scraping, page interaction, and persistent sessions for login-protected pages.
Source:
| | |
| ---------------- | ---------------------------------------------------------------------------- |
| **ID** | `browserless` |
| **Category** | Browser |
| **Features** | None |
| **Dependencies** | [`session_storage`](https://everruns.com/docs/capabilities/session-storage/) |
Cloud browser automation powered by [Browserless](https://www.browserless.io/). Agents can navigate pages, take screenshots, read rendered DOM content, scrape structured data, and interact with pages using click, type, keyboard, mouse, and touch events.
## Set up
1. In the [Browserless Dashboard](https://www.browserless.io/account/home), open **API Keys** in your account settings and copy your API token.
2. In Everruns, open **Settings** > **Connections**, find **Browserless**, select **Connect**, and paste the token.
Once connected, agents with the Browserless capability can use the tools below.
## Two operating modes
**Stateless (default).** Each tool call opens a fresh browser that is destroyed after the response. Nothing persists between calls, and no cleanup is needed. Use it for one-shot screenshots or scraping.
**Persistent session (CDP).** `browserless_open_browser` creates a browser over the Chrome DevTools Protocol that stays alive between tool calls, keeping login state, cookies, and navigation history. Tools use the persistent browser automatically when one is active. A persistent browser expires after 60 seconds of inactivity by default; call `browserless_close_browser` when done for immediate cleanup.
Both modes connect over the Chrome DevTools Protocol, so the Browserless token needs WebSocket (CDP) access. A Browserless cloud token without it falls back to the Browserless REST API for the stateless tools; see [Security](#security).
A typical flow for a login-protected page:
1. `browserless_open_browser` with the login page URL.
2. `browserless_interact` to fill credentials and submit the form.
3. `browserless_navigate` to browse authenticated pages.
4. `browserless_screenshot` to capture the authenticated page state.
5. `browserless_close_browser` to release resources.
## Tools
### `browserless_open_browser`
Open a persistent browser session via CDP.
| Parameter | Type | Required | Description |
| ------------ | ------- | -------- | --------------------------------------------------------------- |
| `url` | string | no | Initial URL to navigate to |
| `timeout_ms` | integer | no | How long the browser stays alive between calls (default: 60000) |
### `browserless_close_browser`
Close the persistent browser session and release resources. Takes no parameters.
### `browserless_navigate`
Navigate to a URL and return page metadata (title, links, headings, meta tags).
| Parameter | Type | Required | Description |
| ------------------- | ------- | -------- | ------------------------------------------- |
| `url` | string | yes | The URL to navigate to |
| `wait_for_selector` | string | no | Wait for this CSS selector to appear |
| `wait_for_timeout` | integer | no | Wait this many milliseconds after page load |
### `browserless_screenshot`
Take a PNG screenshot of a page. Returns base64-encoded image data.
| Parameter | Type | Required | Description |
| ------------------- | ------- | -------- | ---------------------------------------------------- |
| `url` | string | yes | The URL to screenshot |
| `full_page` | boolean | no | Capture the full scrollable page (default: true) |
| `selector` | string | no | CSS selector to screenshot a specific element |
| `wait_for_selector` | string | no | Wait for this CSS selector before taking screenshot |
| `wait_for_timeout` | integer | no | Wait this many milliseconds before taking screenshot |
### `browserless_content`
Get the fully rendered HTML content (DOM) of a page, including JavaScript-rendered content.
| Parameter | Type | Required | Description |
| ------------------- | ------- | -------- | --------------------------------------------------------------- |
| `url` | string | yes | The URL to read |
| `wait_for_selector` | string | no | Wait for this CSS selector before reading content |
| `wait_for_timeout` | integer | no | Wait this many milliseconds before reading content |
| `best_attempt` | boolean | no | Continue even if async events fail or time out (default: false) |
### `browserless_scrape`
Extract structured data from a page using CSS selectors. Returns JSON with matched elements.
| Parameter | Type | Required | Description |
| ------------------- | ------- | -------- | ------------------------------------------- |
| `url` | string | yes | The URL to scrape |
| `elements` | array | yes | Array of `{selector}` objects to extract |
| `wait_for_selector` | string | no | Wait for this CSS selector before scraping |
| `wait_for_timeout` | integer | no | Wait this many milliseconds before scraping |
### `browserless_interact`
Navigate to a URL, then perform a sequence of actions.
| Parameter | Type | Required | Description |
| ------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `url` | string | yes | The initial URL to navigate to |
| `steps` | array | yes | Ordered list of interaction steps |
| `return_screenshot` | boolean | no | Return a base64 screenshot after all steps (default: false) |
| `return_content` | boolean | no | Return DOM content after all steps (default: false). With both flags true, both are returned; with neither, DOM content is returned |
**Supported step actions:**
| Action | Key Parameters | Description |
| ------------------- | --------------------- | -------------------------------------- |
| `click` | `selector` or `x`,`y` | Click element or coordinates |
| `type` | `selector`, `value` | Type text into input field |
| `keyboard` | `key` | Press a key (Enter, Tab, Escape, etc.) |
| `mouse_move` | `x`, `y` | Move mouse to coordinates |
| `touch` | `selector` | Tap element (mobile touch simulation) |
| `scroll` | `value` | Scroll page by pixel amount |
| `wait` | `wait_ms` | Wait for milliseconds |
| `wait_for_selector` | `selector`, `wait_ms` | Wait for element to appear |
| `navigate` | `value` | Navigate to a different URL |
## Uses
* **Accessibility testing**: navigate pages, read the DOM, check ARIA attributes and heading structure.
* **Regression testing and visual QA**: take before and after screenshots and verify content after changes.
* **Login flows**: authenticate in a persistent session and test protected pages.
* **Web scraping**: extract structured data from a website.
## Security
* API tokens are encrypted at rest (AES-256-GCM envelope encryption).
* Browser sessions are isolated on Browserless servers.
* The browser has no network access of its own. Everruns makes every request the page makes, including each redirect hop, scripts, and images. Private, loopback, link-local, and cloud metadata addresses are refused, both as URLs and as DNS answers, and the session’s network access list applies to every request. A blocked page load returns an error, never the page.
* WebSockets and other connections a page opens outside HTTP requests are refused.
* A relayed response is limited to 10 MB.
* The REST fallback for Browserless cloud tokens without CDP access runs in Browserless’ network and checks only the URL you pass. A self-hosted Browserless never falls back.
* CDP session state stores only the WebSocket endpoint (no secrets), scoped per session.
* Large DOM responses are truncated to 100KB to prevent context flooding.
## See also
* [Browserless documentation](https://docs.browserless.io/)
* [Integrations](https://everruns.com/docs/integrations/): every vendor integration.
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Budgeting
> Expose active budgets to the agent so it can check the remaining balance and adjust its own spending.
Source:
| | |
| ---------------- | -------------- |
| **ID** | `budgeting` |
| **Category** | Cost Control |
| **Features** | `budgeting` |
| **Included in** | Worker harness |
| **Dependencies** | None |
Makes an agent aware of its budget constraints. The agent receives budget information in its system prompt and can proactively check remaining balance before expensive operations.
## Tools
### `check_budget`
Query the budget status for the current session.
> **Note:** The current implementation returns a placeholder response indicating whether budgets are configured. Full budget data (balance, limit, status per budget) requires worker-side tool interception, which is planned for a future iteration. In the meantime, use the REST API (`GET /v1/sessions/{id}/budget-check`) for detailed budget status.
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ---------------------- |
| *(none)* | | | No parameters required |
## Behavior
When the `budgeting` capability is enabled:
1. **System prompt injection**: The agent’s system prompt includes a “Budget Awareness” section with the current budget status and guidelines for efficient output.
2. **Self-regulation**: When budget is running low, the agent prioritizes completing current tasks efficiently rather than exploring new directions or generating verbose output.
3. **Proactive checking**: The agent can call `check_budget` before starting expensive operations (large code generation, multi-step tool chains) to decide whether to proceed or ask the user.
## Related
* [Budgets](https://everruns.com/docs/advanced/budgets/), full budgeting system documentation (limits, currencies, API, CLI)
---
# Retrieval Citations
> Attach claim-level citations to an agent's answer from its knowledge retrieval results, so each grounded sentence links back to the source that supports it.
Source:
| | |
| ---------------- | -------------------- |
| **ID** | `citation_retrieval` |
| **Category** | Knowledge |
| **Features** | `citations` |
| **Dependencies** | None |
Turn the sources an agent retrieves into **claim-level provenance** on its answer. When the agent grounds a reply in a knowledge search, `citation_retrieval` links each grounded sentence to the passage that backs it, and the UI renders those links as inline numbered chips with a hover preview and a deduped **Sources** strip.
It contributes **no tools** and **no system prompt**, and it never rewrites the model’s answer. After the agent responds, it inspects the turn’s retrieval results, aligns each retrieved passage to the sentence it best supports, and attaches a citation there. Alignment is deterministic token overlap, no extra model call, so it is model- and provider-agnostic.
## How it looks
Grounded sentences get an inline numbered chip. Hovering a chip previews the source (title, snippet, and link); a deduped **Sources** strip sits below the message. When [Citation Verification](https://everruns.com/docs/capabilities/citation-verification/) is also enabled, each source carries a faithfulness badge.

## Feed
Reads the results of the agent’s knowledge retrieval tools:
| Tool | Source shape |
| ------------------ | ---------------------------------------------------------------------------------------- |
| `search_index` | Knowledge-index chunks (`kchk_…`), `source_uri`, `document_title`, `snippet`, `location` |
| `search_knowledge` | Knowledge-base entries (`kbe_…`), `resource`, `title`, `snippet` |
Both shapes are normalized into the shared citation envelope. A retrieval result with no usable snippet is skipped, there is nothing to align a claim to.
> **This capability is a no-op on its own.** It cites what the agent retrieves, so it only produces citations when the agent also has a retrieval capability (a knowledge index or knowledge base) that surfaces `search_index` / `search_knowledge` results. With no retrieval feed, there is nothing to cite and the answer is unchanged.
## Configuration
| Field | Type | Default | Description |
| ------------- | ------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `min_overlap` | number (0–1) | `0.5` | Minimum token-overlap ratio for a retrieved passage to attach to a sentence. Overlap is `shared tokens / passage tokens`, so `0.5` means at least half the passage’s distinctive words appear in the sentence. |
Raise `min_overlap` for stricter, higher-precision attachment (fewer chips, each more defensible); lower it for broader coverage when the model paraphrases retrieved text.
## Notes
* **No answer rewrite**: the streamed answer is never changed; annotations attach to sentence spans after generation.
* **Alignment is lexical**: a sentence that paraphrases a source heavily enough to fall below `min_overlap` will not be cited. This favors precision over recall.
* **Opt-in**: add `citation_retrieval` to a canonical harness to emit citations from a retrieval feed. Deprecated Generic includes it.
* **Persistence**: annotations ride the message in the event log, so they survive reload, forking, and session export, no separate store.
* **Org scoping**: sources are derived only from already-authorized retrieval results, so a citation can never reference a document the requesting org cannot read.
## See Also
* [Citation Verification](https://everruns.com/docs/capabilities/citation-verification/), stamp a faithfulness verdict on each citation
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Citation Verification
> Verify that each cited source actually supports the claim it is attached to, stamping a faithfulness verdict on every citation produced by any feed.
Source:
| | |
| ---------------- | ----------------------- |
| **ID** | `citation_verification` |
| **Category** | Knowledge |
| **Features** | `citations` |
| **Guardrail** | Yes |
| **Dependencies** | None |
Check that each citation is **faithful**: that the source it points to actually supports the sentence it is attached to, and stamp a verdict on it. It is a guardrail capability, decoupled from the feeds: it consumes the citations collected during a turn (from [Retrieval Citations](https://everruns.com/docs/capabilities/citation-retrieval/) or any future feed) and verifies them uniformly, so any feed can be paired with any verifier.
It contributes **no tools** and **no system prompt**. The verdict renders in the UI as a badge on each source.
## Verdicts
Each citation gets one of three verdicts, shown as a badge on the chip preview and in the **Sources** strip:
| Verdict | Meaning |
| ------------- | ---------------------------------------------------------------------------------- |
| `entailed` | The source supports the claim. Rendered as a green **verified** badge. |
| `unsupported` | The source does not support the claim. Rendered as an amber **unsupported** badge. |
| `uncertain` | Support could not be established. Rendered as an **unverified** badge. |

## Modes
| Mode | Cost | Behavior |
| --------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `heuristic` (default) | Free | Deterministic lexical entailment, token overlap between the claim span and the source snippet. No model call. A weak but honest baseline, strongest on verbatim citations. |
| `llm` | One utility-model call per citation | A utility-model judgement per claim/source pair (claim = hypothesis, snippet = premise). More accurate; falls back to the heuristic when no utility model is configured. |
## Configuration
| Field | Type | Default | Description |
| ----------- | ------------------------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `mode` | `"heuristic"` \| `"llm"` | `"heuristic"` | Verification strategy (see above). |
| `threshold` | number (0–1) | `0.5` | Entailment threshold for the heuristic verdict, the fraction of the claim’s distinctive tokens the source must cover to be `entailed`. |
## Notes
* **Feed-agnostic**: verifies citations from any feed via the shared render contract, so evals can hold the feed fixed and vary only the verifier.
* **Opt-in**: add `citation_verification` in `heuristic` mode to a canonical harness to verify citations with no model cost. Deprecated Generic includes it.
* **`llm` mode needs a utility model**: with none configured it degrades gracefully to the heuristic rather than failing.
* **No new data egress**: the verifier reasons only over text the feed already retrieved.
## See Also
* [Retrieval Citations](https://everruns.com/docs/capabilities/citation-retrieval/), the feed that produces the citations this verifies
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Web Citations
> Attach claim-level citations to an agent's answer from its web search results, whether the web_search tool or the model's own search found them.
Source:
| | |
| ---------------- | -------------- |
| **ID** | `citation_web` |
| **Category** | Knowledge |
| **Features** | `citations` |
| **Dependencies** | None |
Turn the pages an agent found with [Web Search](https://everruns.com/docs/capabilities/web-search/) into **claim-level provenance** on its answer. Each sentence the model backed with a search result links to that page, and the chat renders the links as the same inline numbered chips, hover preview, and **Sources** strip that [Retrieval Citations](https://everruns.com/docs/capabilities/citation-retrieval/) use.
It contributes **no tools** and **no system prompt**, and it never rewrites the model’s answer. After the agent responds, it reads the turn’s search results and the citations the model provider returned, and attaches a citation to each cited sentence. No extra model call is made.
## Feed
| Source | How a citation is found |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `web_search` function results (free search, Brave, Parallel) | The tool asks the model to cite each claim with a result’s `url` or `id` (`w3f9a1c`). A URL in the answer that matches a result, or a result id, cites the sentence it sits in. A citation alone after its sentence, such as `Source: https://…`, cites the sentence before it. |
| Native search: Anthropic | Text blocks with `web_search_result_location` citations; the block is the cited claim. |
| Native search: OpenAI, OpenRouter | `url_citation` annotations on the answer text. |
| Native search: Gemini | Grounding supports and their sources. |
| `brave_web_search` results | Matched by URL, like `web_search` results. |
Matching is exact, never fuzzy. A URL matches when it names the same page: scheme and host compare without case, and a `#fragment` or trailing slash does not count. The path and query must be equal. A provider’s citation keeps its place only while the text it covered is still there in the final answer; otherwise it is dropped rather than attached to the wrong words.
Each citation carries the page `url`, its title, a snippet (the result’s snippet, or the provider’s quoted text), and the result id as `external_id`. Native citations get the same id the `web_search` tool would give the page.
> **This capability is a no-op on its own.** It cites what the agent searched, so it only produces citations when the agent also has [Web Search](https://everruns.com/docs/capabilities/web-search/) (or `brave_web_search`). Answers without search results are unchanged.
## Configuration
None.
## Notes
* **Opt-in**: add `citation_web` next to `web_search`. The built-in harness presets turn on Web Search but not citations.
* **Verification**: pair it with [Citation Verification](https://everruns.com/docs/capabilities/citation-verification/) to stamp a faithfulness verdict on each citation, exactly as for retrieval citations.
* **No answer rewrite**: the streamed answer is never changed; annotations attach to spans after generation.
* **Persistence**: annotations ride the message in the event log, so they survive reload, forking, and session export.
* **Untrusted sources**: a citation links to a public web page the search returned; the page content is not checked beyond what the verification capability does.
## See Also
* [Web Search](https://everruns.com/docs/capabilities/web-search/)
* [Retrieval Citations](https://everruns.com/docs/capabilities/citation-retrieval/)
* [Citation Verification](https://everruns.com/docs/capabilities/citation-verification/)
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Computer Use
> Let an agent see a browser through screenshots and operate it with clicks, typing, scrolling, and key presses, with any model that accepts images.
Source:
| | |
| ---------------- | ----------------------------------------------------------- |
| **ID** | `computer_use` |
| **Category** | Browser |
| **Features** | Leased resources |
| **Dependencies** | `session_storage` |
| **Risk** | High (the model sees and acts on whatever the screen shows) |
| **Availability** | Experimental |
Computer use lets an agent work a screen the way a person does. It takes a screenshot, decides where to click or what to type, and gets a new screenshot after each action. It works on pages that selector-based browser tools cannot handle, such as canvas apps, heavy single-page apps, and custom widgets.
The capability is provider-neutral. It is a regular function tool that returns images, so it works with OpenAI, Anthropic, Gemini, and any other model that accepts images in tool results. On models with a native computer tool (OpenAI GPT-5.4 and later through the Responses API, and recent Claude models with `computer_toolset_20260801`), the agent uses the provider’s own tool instead, which those models are trained on. The display, the actions, and the limits are the same either way.
The display is a Chromium page on a [Browserless](https://everruns.com/docs/capabilities/browserless/) browser. Connect Browserless from **Settings → Connections** first. The agent shares one persistent browser per session with the Browserless tools, so a page opened by `browserless_open_browser` is the page `computer` sees.
## Tools
### `computer`
Performs one action and returns a screenshot of the display afterwards. Coordinates are pixels in the latest screenshot, with the origin at the top left.
| `action` | Parameters | What it does |
| --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `screenshot` | none | Capture the display |
| `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click` | `coordinate` (optional `[x, y]`, defaults to the cursor), `text` (optional modifiers such as `shift` or `ctrl+shift`) | Click |
| `left_click_drag` | `start_coordinate`, `coordinate`, `text` (optional modifiers held during the drag) | Press, drag, and release |
| `left_mouse_down`, `left_mouse_up` | none | Press or release the left button at the cursor, for drags that need several moves |
| `mouse_move` | `coordinate` | Hover without clicking |
| `cursor_position` | none | Report where the cursor is, as text |
| `scroll` | `scroll_direction` (`up`, `down`, `left`, `right`), `scroll_amount` (wheel clicks, 1 to 50), optional `coordinate` | Scroll |
| `type` | `text` | Type text at the keyboard focus |
| `key` | `text` (a key or combo such as `Return`, `Tab`, `ctrl+a`), optional `repeat` | Press keys |
| `hold_key` | `text` (a key or combo), `duration` (seconds, up to 30) | Hold keys down, then release them |
| `wait` | `duration` (seconds, up to 30) | Pause |
| `zoom` | `region` (`[x0, y0, x1, y1]`) | Return that part of the screen enlarged to the display size, to read small text |
| `navigate` | `url` | Load a page |
Example call:
```json
{ "action": "left_click", "coordinate": [412, 230] }
```
## Configuration
| Field | Default | Description |
| ------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `display_width` | `1280` | Display width in pixels (320 to 1920) |
| `display_height` | `800` | Display height in pixels (320 to 1200) |
| `screenshot_after_action` | `true` | Return a screenshot after every action. When off, only `screenshot` returns an image. |
| `max_actions_per_session` | `300` | Hard cap on actions in one session, screenshots included |
| `native_tools` | `true` | Use the provider’s native computer tool on models that have one. When off, every model uses the `computer` function tool. |
Screenshots are billed as image tokens. A smaller display, or turning off `screenshot_after_action`, lowers the cost per step.
## Safety
* **The model sees everything on the screen.** Text on a page can try to steer the agent. The capability tells the model to treat screen contents as untrusted and to stop and ask before typing credentials, making purchases, sending messages, or confirming irreversible actions. Add the `soft_approval` capability when you want those confirmations recorded, and the [`tool_approval`](https://everruns.com/docs/capabilities/tool-approval/) capability when a person must approve every `computer` call before it runs.
* **Soft approval, no built-in hard gate.** No `computer` call waits for a person by default, whether it clicks, types, presses Enter, navigates, or carries a provider safety check. Approval rests on the stop-and-ask rule above, recorded with `soft_approval`. Add `tool_approval` when every call must be approved before it runs.
* **Keep credentials out of reach.** Do not give a computer-use agent a browser that is signed in to accounts it should not use.
* **Egress.** `navigate` refuses private and internal addresses and follows the session’s network access list. Everruns makes every request the page makes and applies the same rules to each one, including redirects and DNS answers. If a page navigates somewhere blocked on its own, the page is reset to a blank page and the action reports an error.
* **Budget.** `max_actions_per_session` stops runaway loops.
Each screenshot shows as a thumbnail on the tool call in the session view; click it for the full frame.
## Desktop in a Sandbox
To give the agent a full Linux desktop instead of a browser page, turn on **Desktop** in a Daytona or E2B [Sandbox Template](https://everruns.com/docs/features/sandbox-templates/#desktop). The Agent then gets the same `computer` tool, driving the desktop of the Session’s own Sandbox, so the shell, the files and the desktop are one machine, and the template’s outbound network rule covers the desktop too. It takes the same configuration as `computer_use`.
* There is no `navigate` action: the agent opens a browser on the desktop and types the address.
* A template with a desktop replaces `computer_use`: both provide the `computer` tool. Saving an agent, harness, or session with `computer_use` and `sandbox_desktop` together fails with an error naming the two.
* On Daytona, every action goes through the sandbox’s Computer Use API, so typed text and key names reach the desktop as data and never pass through a shell, and `zoom` returns the region at its own size.
---
# Container Sandbox
> Self-hosted Linux container per session through the Docker Engine API, with shell execution, file I/O, and file transfer to session storage.
Source:
| | |
| ---------------- | ------------------------------------------------------------------ |
| **ID** | `container_sandbox` |
| **Category** | Execution |
| **Features** | `leased_resources` |
| **Dependencies** | [Storage](https://everruns.com/docs/capabilities/session-storage/) |
| **Risk** | High |
Container Sandbox gives an agent a Linux container to run code in. The platform talks to a Docker Engine you operate over its REST API, so no sandbox vendor is involved. Each session gets at most one container, on its own Docker network.
**Off by default.** Set `FEATURE_CONTAINER_SANDBOX=prod` on the server and on every worker to register the capability and enable it by default for organisations. Use `dev` for local development or `preview`/`adoption` for explicit enrolment. Each capability has its own grade; the Docker flag does not enable the sandbox.
## Setup
1. Run a Docker Engine the server and workers can reach.
2. Set `CONTAINER_SANDBOX_DOCKER_HOST` to its `http://` or `https://` URL, on the server and every worker.
3. Set `FEATURE_CONTAINER_SANDBOX=prod` in the same places.
4. Add `container_sandbox` to a custom agent or harness. For coding work, inherit [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/) and optionally add [GitHub Scout](https://everruns.com/docs/capabilities/github-scout/).
Without `CONTAINER_SANDBOX_DOCKER_HOST` the client defaults to `unix:///var/run/docker.sock`. The client cannot use a Unix socket yet, so every tool call then fails with an error asking for an `http(s)://` host. This is deliberate: it never falls back to an unauthenticated TCP endpoint. Use `https://` with mutual TLS for any Docker daemon that is not on loopback, and never expose a plaintext Docker port on a network.
## Tools
| Tool | Parameters | What it does |
| -------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `sandbox_create` | `image` (optional) | Creates the session’s network and container and starts it |
| `sandbox_exec` | `command`, `working_dir`, `output` | Runs a shell command and returns stdout, stderr, and the exit code |
| `sandbox_read_file` | `path`, `offset`, `limit` | Reads a text file as a line window (`limit` defaults to 2000 lines). For a binary file it returns only the size |
| `sandbox_write_file` | `path`, `content` | Writes text to a file, replacing it in full |
| `sandbox_upload` | `session_path`, `container_path` | Copies a file from session storage into the container, byte for byte |
| `sandbox_download` | `container_path`, `session_path` | Copies a file out of the container into session storage, where it outlives the container |
| `sandbox_list` | none | Lists the session’s sandbox with its state |
| `sandbox_manage` | `action`: `stop`, `start`, or `remove` | `stop` keeps the filesystem, `start` runs it again, `remove` deletes the container, its network, and every file that was not downloaded |
The capability’s system prompt describes the lifecycle (`sandbox_create`, then exec and file tools, then `sandbox_manage` with `remove`) and tells the agent to remove the sandbox when it is done.
## Container defaults
| Setting | Value |
| ----------------- | ------------------------------------------------------ |
| Image | `ubuntu:24.04`, unless `sandbox_create` passes `image` |
| Working directory | `/workspace` |
| Memory limit | 2 GiB |
| CPU limit | 1 CPU |
| Process limit | 256 |
| Runtime | The Docker daemon’s default runtime (normally `runc`) |
| Network | A bridge network per session, named after the session |
These values are fixed in the platform. The capability takes no configuration, and the image is the only setting a tool call can change. Exec output and file transfers are capped at 8 MiB per response, and a single file transfer at 4 MiB.
Each create, exec, and start refreshes a 20-minute lease on the container in the session’s leased resources.
## Security
* Each session’s container runs on its own Docker network, and container and network names are derived from the session ID.
* Memory, CPU, and process limits are applied through cgroups.
* The Docker socket is never mounted into a container.
* No egress filtering is applied. A container can reach whatever its bridge network routes to, including private address ranges and cloud metadata endpoints. Restrict egress at the network or firewall layer.
* `runc` shares the host kernel. For untrusted or multi-tenant workloads, configure a hardened default runtime on the Docker daemon, such as `sysbox-runc`, gVisor, or Kata.
## See also
* [Daytona](https://everruns.com/docs/capabilities/daytona/) and [E2B](https://everruns.com/docs/capabilities/e2b/), hosted sandbox providers
* [Storage](https://everruns.com/docs/capabilities/session-storage/), where uploaded and downloaded files live
* [Docker Container](https://everruns.com/docs/capabilities/docker/), an older experimental Docker capability
---
# Current Time
> Read the current date and time in a chosen format and timezone.
Source:
| | |
| ---------------- | -------------- |
| **ID** | `current_time` |
| **Category** | Core |
| **Features** | None |
| **Dependencies** | None |
Provides a tool to get the current date and time. Supports multiple formats and timezones.
## Tools
### `get_current_time`
Get the current date and time.
| Parameter | Type | Required | Description |
| ---------- | ------ | -------- | --------------------------------------------------------- |
| `timezone` | string | no | IANA timezone (e.g., `America/New_York`, `Europe/London`) |
| `format` | string | no | Output format: `iso8601`, `unix`, `human` |
## See Also
* [Schedules](https://everruns.com/docs/capabilities/session-schedules/), schedule future tasks
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Data Knowledge
> Mount a read-only /knowledge/ Open Knowledge Format scaffold for table docs, business rules, and validated SQL, used by the Data Analyst harness.
Source:
| | |
| ---------------- | ------------------------------------------------------------------ |
| **ID** | `data_knowledge` |
| **Category** | Data |
| **Tools** | None |
| **Features** | `file_system` |
| **Dependencies** | [File System](https://everruns.com/docs/capabilities/file-system/) |
| **Risk** | Low |
The `data_knowledge` capability mounts a read-only `/knowledge/` directory into the session and tells the agent to read it before writing SQL. The directory is shaped as an [Open Knowledge Format](https://everruns.com/docs/how-to/share-knowledge-with-okf/) (OKF) bundle: Markdown files with YAML frontmatter, navigated through `index.md` files.
The [Data Analyst](https://everruns.com/docs/built-ins/harnesses/data-analyst/) harness includes it.
## What gets mounted
```text
/knowledge/
index.md # OKF root index (okf_version "0.1")
tables/index.md # how to document a table or data source
business/index.md # how to document metrics and business rules
queries/index.md # how to document validated query patterns
```
Each `index.md` explains what belongs in its directory. The mount is a fixed, read-only scaffold built into the capability: the agent cannot write to it, and the capability has no configuration for adding your own files to it.
To give the agent your organization’s curated data knowledge, put it in a [Knowledge Base](https://everruns.com/docs/capabilities/knowledge-base/) (OKF bundles import directly) or in an organization [Memory](https://everruns.com/docs/capabilities/memory/) mounted under `/workspace`.
## System prompt
The capability adds one instruction: curated data knowledge is mounted at `/knowledge/{tables,business,queries}`, and the agent should read it before writing SQL and treat it as ground truth for schema semantics, metrics, and validated queries.
## Configuration
None.
```json
{ "ref": "data_knowledge" }
```
## See also
* [Data Analyst harness](https://everruns.com/docs/built-ins/harnesses/data-analyst/)
* [Share knowledge with OKF](https://everruns.com/docs/how-to/share-knowledge-with-okf/)
* [Knowledge Base](https://everruns.com/docs/capabilities/knowledge-base/)
---
# Daytona
> Run agent code in Daytona cloud sandboxes with command execution, file access, git, workspace downloads, and session-scoped lifecycle controls.
Source:
| | |
| ---------------- | ---------------------------------------------------------------------------- |
| **ID** | `daytona` |
| **Category** | Sandboxes |
| **Features** | None |
| **Dependencies** | [`session_storage`](https://everruns.com/docs/capabilities/session-storage/) |
Run code in cloud sandboxes powered by [Daytona](https://www.daytona.io/). An agent can create several isolated Linux environments per session, each with network access, then execute commands, manage files, clone repositories, and download results. The capability is marked experimental and may change.
New fleet workflows
This provider-specific capability remains for compatibility and advanced Daytona operations. Use [`sandbox_fleet`](https://everruns.com/docs/capabilities/sandbox-fleet/) for new workflows that should use logical Everruns Sandbox IDs and remain independent of the provider-facing tool namespace.
## Set up
1. In the [Daytona Dashboard](https://app.daytona.io), open **API Keys** in your account settings, select **Create New API Key**, and copy the key.
2. In Everruns, open **Settings → Connections**, find **Daytona**, select **Connect**, and paste the key.
Once connected, agents with the Daytona capability can use the tools below.
## Tools
### `daytona_create_sandbox`
Create and start a new sandbox.
| Parameter | Type | Required | Description |
| ------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `title` | string | no | Sandbox name |
| `size` | string | no | `small` (1 vCPU, 1 GiB RAM, 3 GiB disk, default), `medium` (2 vCPU, 4 GiB, 8 GiB), or `large` (4 vCPU, 8 GiB, 10 GiB) |
| `snapshot` | string | no | Daytona snapshot name; overrides `size` |
| `auto_stop_minutes` | integer | no | Minutes of inactivity before auto-stop, 1 to 60 (default 5) |
| `upload_files` | array | no | `{session_path, sandbox_path}` pairs to copy from session storage |
### `daytona_exec`
Run a shell command in a sandbox and wait for it.
| Parameter | Type | Required | Description |
| ------------ | ------- | -------- | ---------------------------------------------- |
| `sandbox_id` | string | yes | Target sandbox |
| `command` | string | yes | Shell command to execute |
| `cwd` | string | no | Working directory (default: sandbox workspace) |
| `timeout` | integer | no | Timeout in milliseconds (default 300000) |
### `daytona_read_file`
Read a file from a sandbox.
| Parameter | Type | Required | Description |
| ------------ | ------- | -------- | -------------------------------------------- |
| `sandbox_id` | string | yes | Target sandbox |
| `path` | string | yes | File path |
| `offset` | integer | no | Zero-based line offset to start reading from |
| `limit` | integer | no | Maximum number of lines to return |
### `daytona_write_file`
Write a file to a sandbox.
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | -------------- |
| `sandbox_id` | string | yes | Target sandbox |
| `path` | string | yes | File path |
| `content` | string | yes | File content |
### `daytona_download_workspace`
Download a sandbox directory to session storage.
| Parameter | Type | Required | Description |
| -------------- | ------ | -------- | ------------------------------------------------------ |
| `sandbox_id` | string | yes | Target sandbox |
| `sandbox_path` | string | no | Root path in the sandbox (default: workspace path) |
| `session_path` | string | no | Destination in session storage (default: `/workspace`) |
### `daytona_list_snapshots`
List available Daytona snapshots (names, CPU, memory, and disk specs, state). Use a snapshot name as the `snapshot` parameter of `daytona_create_sandbox`. Takes no parameters.
### `daytona_list_sandboxes`
List all sandboxes for the current session. Takes no parameters.
### `daytona_manage_sandbox`
Stop or delete a sandbox.
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | ------------------ |
| `sandbox_id` | string | yes | Target sandbox |
| `action` | string | yes | `stop` or `delete` |
### `daytona_git_clone`
Clone a git repository into a sandbox. Uses your connected GitHub credentials for private repositories.
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | ----------------------------------------------------------- |
| `sandbox_id` | string | yes | Target sandbox |
| `repo_url` | string | yes | Repository URL or `user/repo` shorthand |
| `branch` | string | no | Branch to clone (default: the default branch) |
| `path` | string | no | Clone destination (default: `/home/daytona//`) |
### `daytona_git_credentials`
Configure git credentials in a sandbox so later `git push`, `pull`, and `fetch` commands run through `daytona_exec` authenticate.
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | -------------- |
| `sandbox_id` | string | yes | Target sandbox |
### `daytona_api_call` (opt-in)
Call any Daytona REST endpoint directly, for operations the dedicated tools do not cover. Only available when the capability config sets `enable_api_calling` to `true`. Authentication headers are injected automatically, and the Daytona OpenAPI spec is mounted at `/daytona/openapi.yaml`.
| Parameter | Type | Required | Description |
| --------- | --------------- | -------- | -------------------------------------------------------------------------------------- |
| `method` | string | yes | `GET`, `POST`, `PUT`, `PATCH`, or `DELETE` |
| `path` | string | yes | Management API path (`/sandbox/...`) or Toolbox API path (`/toolbox/{sandbox_id}/...`) |
| `body` | object or array | no | JSON request body |
## Config
```json
{
"enable_api_calling": true
}
```
`enable_api_calling` is the only setting, and it defaults to `false`.
## Git
* `daytona_git_clone` uses your connected GitHub account, so private repositories clone without extra setup. When no person is in the run, or the person has not connected GitHub, it uses the agent’s GitHub connection.
* For push, pull, or fetch, call `daytona_git_credentials` once after creating the sandbox, then run git commands with `daytona_exec`.
* Tokens written into the sandbox only reach repository contents: `daytona_git_clone`’s token covers the cloned repository alone, and `daytona_git_credentials` covers contents of every repository the connection reaches. Neither can open issues or review pull requests.
* Repository arguments accept `user/repo` shorthand instead of full URLs.
## Sandbox lifecycle
Sandboxes auto-stop after 5 minutes of inactivity by default as a safety net. Delete sandboxes when you are done: stopping only pauses a sandbox, and it stays visible in your Daytona dashboard.
All sandbox-scoped tools require a `sandbox_id`. The exceptions are `daytona_create_sandbox`, `daytona_list_sandboxes`, and `daytona_list_snapshots`.
## Security
* API keys are encrypted at rest (AES-256-GCM envelope encryption).
* Each sandbox is isolated from other sandboxes and the host.
* Git credentials are short-lived and scoped to the sandbox.
* Sandbox state is stored in encrypted session secrets.
## See also
* [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/#desktop): start the Session Sandbox with a desktop the agent drives with the `computer` tool.
* [Storage](https://everruns.com/docs/capabilities/session-storage/): API key and state persistence.
* [Daytona documentation](https://www.daytona.io/docs)
* [Integrations](https://everruns.com/docs/integrations/): every vendor integration.
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Docker Container Sandbox
> Run agent commands and manage files in a Docker container tied to the session. Self-hosted alternative to cloud sandbox providers.
Source:
| | |
| ---------------- | ------------------ |
| **ID** | `docker_container` |
| **Category** | Sandboxes |
| **Features** | None |
| **Dependencies** | None |
Run commands and manage files in a Docker container tied to the session. The container is lazily started on first use and persists for the session duration. A self-hosted alternative to cloud sandbox providers like Daytona or E2B.
> **Status:** Experimental; availability follows the configured feature rollout grade.
It is off by default. Use `FEATURE_DOCKER_CAPABILITY=dev` for local development, or select `preview`, `adoption`, or `prod` to control hosted rollout. See [feature rollout grades](https://everruns.com/docs/sre/environment-variables/#feature-rollout-grades). It may change significantly in future releases.
## Tools
### `docker_exec`
Execute a command inside the Docker container.
| Parameter | Type | Required | Description |
| ------------ | ------- | -------- | ----------------------- |
| `command` | string | yes | Shell command to run |
| `cwd` | string | no | Working directory |
| `timeout_ms` | integer | no | Timeout in milliseconds |
Returns stdout, stderr, and exit code. Container is started automatically if not already running.
### `docker_read_file`
Read a text file from the container filesystem.
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | --------------------------------- |
| `path` | string | yes | File path |
| `offset` | integer | no | Line offset to start reading from |
| `limit` | integer | no | Maximum number of lines to return |
### `docker_write_file`
Write a text file into the container filesystem.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ------------ |
| `path` | string | yes | File path |
| `content` | string | yes | File content |
### `docker_logs`
Retrieve recent logs from the Docker container.
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | ----------------------------- |
| `tail` | integer | no | Number of log lines to return |
### `docker_stop`
Stop the Docker container for this session.
## Configuration
Configure the Docker container via the capability settings:
| Setting | Description |
| ------- | ----------------------------------------- |
| `image` | Docker image to use (e.g. `ubuntu:24.04`) |
| `env` | Environment variables to inject |
| `binds` | Host path mounts |
## Notes
* Only one container runs per session
* The container is stopped when the session ends or `docker_stop` is called
* Docker Engine must be accessible from the server running Everruns
## See Also
* [Container Sandbox](https://everruns.com/docs/capabilities/container-sandbox/), the self-hosted Docker Engine sandbox
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Dry Run
> Try an agent without its side effects. Read-only tools run for real; every other tool call returns a simulated result and changes nothing.
Source:
| | |
| ---------------- | --------- |
| **ID** | `dry_run` |
| **Category** | Safety |
| **Tools** | None |
| **Features** | None |
| **Dependencies** | None |
Dry run lets you see what an agent would do before it does it. A session with this capability runs tools that only read, and answers every other tool call with a simulated result. Nothing is sent, written, or deleted.
The **Test run** button on an agent’s page starts a session with dry run on.
## What runs for real
A tool runs for real when it declares itself read-only and nothing marks it destructive or requiring approval. MCP tools declare this through their own annotations or through a [risk label](https://everruns.com/docs/reference/cli/mcp-servers/) set on the connection. Every tool that does not declare itself read-only is simulated: a dry run errs on the side of not acting.
## What a simulated call returns
The call is recorded as a successful result with a `dry_run` object that names the tool and its arguments:
```json
{
"dry_run": {
"simulated": true,
"tool": "visti_send",
"arguments": { "text": "main CI is red" },
"note": "Dry run: `visti_send` was not executed. Nothing was changed."
}
}
```
The agent is told it is in a dry run. It carries on as if simulated calls worked, so the rest of the run is exercised, and its final answer lists the actions it would have taken. The session trace marks simulated calls.
## Configuration
Dry run has no settings. Add it to a session’s capabilities:
```json
{ "capabilities": [{ "ref": "dry_run" }] }
```
---
# E2B Sandboxes
> Run agent code in isolated E2B cloud sandboxes with your own API key, command execution, file access, and session-scoped lifecycle management.
Source:
| | |
| ---------------- | ---------------------------------------------------------------------------- |
| **ID** | `e2b` |
| **Category** | Sandboxes |
| **Features** | None |
| **Dependencies** | [`session_storage`](https://everruns.com/docs/capabilities/session-storage/) |
Run code in cloud sandboxes powered by [E2B](https://e2b.dev/docs). An agent can create, pause, resume, delete, and work in several isolated Linux sandboxes per session. Sandboxes are tracked per session.
You bring your own E2B API key. There is no platform-owned or environment-variable fallback, so sandbox costs and quotas stay in your own E2B account.
## Set up
1. In the [E2B Dashboard](https://e2b.dev/dashboard), create an API key and copy it.
2. In Everruns, open **Settings** > **Connections**, find **E2B**, select **Connect**, and paste the key.
Every E2B operation requires a user-provided key. If none is configured, the agent shows an inline connection prompt.
## Tools
### `e2b_create_sandbox`
Create a new E2B sandbox. Returns `sandbox_id` and connection details.
| Parameter | Type | Required | Description |
| ----------------- | ------- | -------- | ----------------------------------------------------------------- |
| `title` | string | no | Human-readable sandbox name |
| `template` | string | no | E2B template ID (default `base`) |
| `timeout_seconds` | integer | no | Sandbox TTL in seconds (default `3600`) |
| `env_vars` | object | no | Environment variables injected into the sandbox |
| `upload_files` | array | no | `{session_path, sandbox_path}` pairs to copy from session storage |
### `e2b_exec`
Execute a shell command in a sandbox, capturing stdout, stderr, and the exit code.
| Parameter | Type | Required | Description |
| ------------ | ------- | -------- | ------------------------------- |
| `sandbox_id` | string | yes | Target sandbox |
| `command` | string | yes | Shell command to run |
| `cwd` | string | no | Working directory |
| `timeout_ms` | integer | no | Command timeout in milliseconds |
### `e2b_read_file`
Read a text file from a sandbox filesystem.
| Parameter | Type | Required | Description |
| ------------ | ------- | -------- | --------------------------------- |
| `sandbox_id` | string | yes | Target sandbox |
| `path` | string | yes | File path |
| `offset` | integer | no | Line offset to start reading from |
| `limit` | integer | no | Maximum number of lines to return |
### `e2b_write_file`
Write a text file into a sandbox filesystem.
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | -------------- |
| `sandbox_id` | string | yes | Target sandbox |
| `path` | string | yes | File path |
| `content` | string | yes | File content |
### `e2b_list_sandboxes`
List all E2B sandboxes created in the current session. Takes no parameters.
### `e2b_manage_sandbox`
Pause, resume, or delete an E2B sandbox.
| Parameter | Type | Required | Description |
| ----------------- | ------- | -------- | ------------------------------ |
| `sandbox_id` | string | yes | Target sandbox |
| `action` | string | yes | `pause`, `resume`, or `delete` |
| `timeout_seconds` | integer | no | Timeout to apply when resuming |
## How it works
E2B exposes two surfaces, and the capability uses both:
* **Management API** (`api.e2b.app`): sandbox lifecycle, metadata, and timeout control.
* **envd sandbox endpoint**: in-sandbox file access and command execution.
Per-sandbox state (sandbox ID, domain, access token, workspace path, timeout) is stored in encrypted session secrets and registered as a leased resource, so orphaned sandboxes are cleaned up on the worker side. Every sandbox is tagged with Everruns ownership metadata (session, harness, org, and agent IDs) for dashboard traceability and audit review.
File operations target the sandbox filesystem, not the session workspace. Each sandbox has internet access. Use `e2b_manage_sandbox` to delete sandboxes when you are done.
## Security
* API keys resolve fresh from your user connection on each tool call. They are never stored in sandbox state or env vars, and never appear in tool output.
* envd access tokens are session-scoped and stored only in encrypted session secrets (AES-256-GCM envelope encryption).
* Sandbox isolation depends on E2B’s runtime boundaries plus Everruns session-scoped secret lookups.
* E2B timeouts and auto-pause, plus Everruns leased-resource cleanup, limit resource leaks.
## See also
* [Storage](https://everruns.com/docs/capabilities/session-storage/): API key and state persistence.
* [E2B documentation](https://e2b.dev/docs)
* [Integrations](https://everruns.com/docs/integrations/): every vendor integration.
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# File System
> Read, write, search, and manage files in an isolated per-session workspace, with glob, grep, and directory operations.
Source:
| | |
| ---------------- | ------------------------------------- |
| **ID** | `session_file_system` |
| **Category** | File Operations |
| **Features** | `file_system` (enables Workspace tab) |
| **Dependencies** | None |
Provides tools to access and manipulate files in the session workspace. Each session has an isolated filesystem rooted at `/workspace`. Files persist for the session duration. `read_file` and `write_file` return a `content_hash` (`sha256:...`) so agents can make freshness-checked `edit_file` calls.
## Tools
### `read_file`
Read the contents of a file. Successful responses include `content_hash`.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ---------------------------------------------- |
| `path` | string | yes | Absolute path (e.g., `/workspace/src/main.py`) |
### `read_many_files`
Read 2-10 independent files in one ordered call. Only batch paths known before the call; if one file reveals another path, read it in a later call. Results keep per-file success or error details. Images return metadata only; read them individually with `read_file`.
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | ---------------------------------------------------------- |
| `paths` | array | yes | Ordered list of 2-10 absolute paths |
| `offset` | integer | no | Starting line applied to every file (0-indexed, default 0) |
| `limit` | integer | no | Max lines per file |
### `write_file`
Create or overwrite a file. Parent directories are created automatically. Successful responses include `content_hash`.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ------------- |
| `path` | string | yes | Absolute path |
| `content` | string | yes | File content |
### `edit_file`
Apply one or more exact text replacements to an existing text file. This tool is text-only, requires the current `content_hash` from `read_file` or `write_file`, and uses compare-and-set semantics so concurrent writes fail cleanly instead of clobbering newer content.
| Parameter | Type | Required | Description |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `path` | string | yes | Absolute path to an existing text file |
| `expected_hash` | string | yes | Current `content_hash` (`sha256:...`) |
| `edits` | array | yes | One or more `{ old_text, new_text }` replacements matched against the original file. Use a single-element array for one replacement. |
Legacy top-level `old_text`/`new_text` are still accepted for backward compatibility, they are folded into `edits[]`, but new callers should always use `edits[]`.
### `list_directory`
List files and directories at a given path.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | -------------- |
| `path` | string | yes | Directory path |
### `grep_files`
Search file contents with regex patterns.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ------------------------------------------- |
| `pattern` | string | yes | Regex pattern |
| `path` | string | no | Directory to search (default: `/workspace`) |
### `glob`
List files recursively under `/workspace` whose relative paths match a glob. Results are sorted and bounded.
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | ---------------------------------------------- |
| `pattern` | string | yes | Glob relative to `/workspace` |
| `limit` | integer | no | Maximum matches, from 1 to 1000 (default: 200) |
### `grep`
Search UTF-8 files recursively under `/workspace` with a Rust regular expression. This is the provider-neutral search name used by managed Environments; `grep_files` remains available for existing file-system agents.
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | ---------------------------------------------- |
| `pattern` | string | yes | Rust regular expression |
| `glob` | string | no | Restrict searched files with a glob |
| `offset` | integer | no | Skip this many matches (default: 0) |
| `limit` | integer | no | Maximum matches, from 1 to 1000 (default: 200) |
### `delete_file`
Delete a file or directory.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | -------------- |
| `path` | string | yes | Path to delete |
### `stat_file`
Get file metadata (size, type, timestamps).
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ------------ |
| `path` | string | yes | Path to stat |
## `edit_file` request example
```json
{
"path": "/workspace/app.py",
"expected_hash": "sha256:1c4d...",
"edits": [
{
"old_text": "return 'Hello, World!'",
"new_text": "return 'Hello from Everruns!'"
}
]
}
```
## Notes
* All paths must be under `/workspace`
* Files are session-scoped, no cross-session access
* Parent directories are auto-created on write
* `edit_file` only works on text files and rejects binary/base64 content
* `edit_file` applies all replacements against the original file content and rejects ambiguous or overlapping matches
* `edit_file` preserves the file’s existing BOM and newline style (`LF`, `CRLF`, or `CR`)
* `edit_file` returns a unified diff capped to a bounded size; oversized diffs are truncated and marked as such
* Shared filesystem with [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/) (same `/workspace`)
## See Also
* [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/), execute commands against these files
* [Storage](https://everruns.com/docs/capabilities/session-storage/), key/value and secret storage
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# GitHub
> Read and review GitHub pull requests with inline comments, keep one comment per pull request up to date, and file deduplicated issues.
Source:
| | |
| ---------------- | ------------ |
| **ID** | `github` |
| **Category** | Integrations |
| **Features** | None |
| **Dependencies** | None |
The GitHub capability gives an agent what it needs to work on one pull request: read it, read its diff, review it with inline comments, and leave a comment it can keep editing. It can also file findings as issues without duplicating them. Pair it with a GitHub trigger to build a pull request reviewer, or with a schedule trigger and a sandbox to build a security scanner. The [PR Reviewer and Security Scanner templates](https://everruns.com/docs/how-to/set-up-review-and-security-agents/) do both for you.
## Tools
| Tool | What it does |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `get_github_pull_request` | Title, description, author, state, branches, size, and up to 100 changed files. |
| `get_github_pull_request_diff` | The unified diff, cut to `max_bytes` (default 60,000, at most 200,000). `truncated` says whether it was cut. |
| `github_checkout_pull_request` | Checks the pull request out into the session’s [managed sandbox](https://everruns.com/docs/features/sandbox-templates/), private repositories included. See [Check out a pull request](#check-out-a-pull-request). |
| `upsert_github_comment` | Posts a Markdown comment, or edits the one it posted earlier with the same `marker` (default `summary`). |
| `submit_github_pull_request_review` | Submits one review (`COMMENT` or `REQUEST_CHANGES`; it cannot approve) with a summary and inline comments on changed lines. |
| `upsert_github_issue` | Files a finding as an issue keyed by a `fingerprint`; updates the open issue for the same fingerprint, leaves a closed one closed. |
| `create_github_pull_request` | Opens a pull request (draft by default) from a branch already pushed to the same repository. Only offered when `allow_pull_requests` is on. |
The pull request tools take `repo` (`owner/repo`) and `number`.
### Check out a pull request
`github_checkout_pull_request` fetches the pull request’s head and its base branch into `/workspace/` (or `path`, which must stay inside `/workspace`) and leaves the head checked out at a detached HEAD, so `bash` can build, test and diff it:
```json
{
"repo": "acme/app",
"number": 42
}
```
It returns `path`, `head_sha`, `base_ref` and `base_sha`; `depth` (default 100) sets how much history is fetched. It needs the Managed Sandbox (`session_sandbox`) capability on a Sandbox Worker, and says so otherwise.
Checkout requires a GitHub App installation. The trusted resolver must supply installation-mint evidence that the token covers exactly this repository, grants only **Contents: read**, **Pull requests: read** and GitHub’s implicit **Metadata: read**, and expires within one hour. Missing evidence, personal OAuth tokens and `GITHUB_TOKEN` session-secret fallbacks are refused before any credential reaches the sandbox. This also fails closed with older control planes. For personal-token workflows, use `github_get_pull_request` and `github_get_pull_request_diff` to review on the trusted host, or connect an App installation for checkout.
The tool hands the admitted token with **Contents: read** and **Pull requests: read** for that one repository to a single `git fetch` through a temporary file it removes before returning, so the token is not in `.git/config`, in later `bash` commands, or in sandbox checkpoints. Plain `git fetch` in `bash` therefore works only for public repositories; call the tool again to refresh a checkout. Concurrent code in the sandbox can read the temporary file; the enforced repository, permission and expiry limits bound this accepted exposure.
### Reviews without repeats
Each inline comment from `submit_github_pull_request_review` starts with a hidden `` line. Pass a short, stable `key` per finding (such as `null-deref-parse-config`); a key the agent already posted on the pull request is skipped, so a review after a new push only adds what is new. A second review of the same head commit is skipped entirely, which makes webhook redeliveries harmless. Comments on lines that are not part of the diff would make GitHub reject the whole review, so the tool moves them into the review summary instead.
Only comments, reviews and issues written by a bot count as the agent’s own, so a person cannot hide a finding by pasting a marker into their own comment.
## Settings
| Setting | Default | Effect |
| --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `acts_as` | `agent` | Whose GitHub identity the tools use: `agent`, `person`, or `person_or_agent`. See [Authentication](#authentication). |
| `allow_pull_requests` | `false` | Adds `create_github_pull_request`. Pushing the branch also needs the GitHub App’s **Contents: write** permission. |
| `private_issues_only` | `false` | `upsert_github_issue` refuses public repositories, so a finding is never disclosed in a public issue. |
`upsert_github_comment` starts the comment with a hidden `` line and looks for it on the next run, so an agent that runs on every push updates one comment instead of adding a new one each time. Use a different marker for each kind of comment.
## Authentication
The tools act on GitHub as the identity `acts_as` names; only `person_or_agent` ever uses a second one:
* `agent` (default) uses the agent’s own GitHub connection: its GitHub App when you connected one, so comments are posted as the agent’s bot and the tools reach only the repositories the App is installed on. An agent that chose **Use the Everruns app** acts as the shared Everruns App’s bot on the account an org admin linked. Use it for bots, and for anything triggered without a person.
* `person` uses the GitHub connection of the person talking to the agent. Use it for a personal agent. A triggered run has no person, so it gets nothing.
* `person_or_agent` uses the person’s connection when they have one, otherwise the agent’s.
Each call gets a short-lived token limited to the one repository it names and the permissions that tool needs: the read tools ask for **Pull requests: read**, the checkout tool for **Contents: read** and **Pull requests: read**, the comment tool for **Issues** and **Pull requests: write**, the review tool for **Pull requests: write**, the issue tool for **Issues: write**, and `create_github_pull_request` for **Pull requests: write** and **Contents: read**. Without a connection for that identity the tools return `connection_required`.
The session’s network access policy must allow `https://api.github.com/` when one is set.
---
# GitHub Scout
> Blueprint-only GitHub repository exploration capability that spawns read-only scout subagents.
Source:
| | |
| ---------------- | ----------------------------------------------------------------- |
| **ID** | `github_scout` |
| **Category** | Integrations |
| **Features** | None |
| **Dependencies** | [`subagents`](https://everruns.com/docs/capabilities/sub-agents/) |
GitHub Scout lets an agent spawn a specialist subagent for read-only GitHub repository exploration. The host agent receives `spawn_agent` with `target.type: "subagent"` through the dependency on `subagents`; monitoring and steering are handled by the generic `session_tasks` tools (`list_tasks`, `get_task`, `message_task`, `cancel_task`). The GitHub API tools stay private inside the spawned `github_scout` blueprint session.
## How to Use
Enable the `github_scout` capability on an agent or harness. Then spawn the blueprint:
```json
{
"name": "spawn_agent",
"arguments": {
"name": "Scout",
"instructions": "Find where authentication middleware is implemented.",
"target": { "type": "subagent" },
"blueprint": "github_scout",
"config": {
"repos": ["fastify/fastify"]
}
}
}
```
The optional `repos` config scopes GitHub searches to `owner/repo` repositories. Config is validated against the blueprint’s schema before the child session is created: entries that are not `owner/repo` and unrecognized config keys are rejected with a schema error instead of being silently ignored.
## Private Blueprint Tools
These tools are available only inside the GitHub Scout child session:
| Tool | Description |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `search_github_code` | Search code with GitHub code search qualifiers such as `repo:`, `path:`, `language:`, `symbol:`, and `filename:` |
| `read_github_file` | Read a UTF-8 file from a repository by `repo`, `path`, and optional `ref` |
| `search_github_issues` | Search issues and pull requests with qualifiers such as `repo:`, `is:issue`, `is:pr`, `state:`, `author:`, and `label:` |
## Authentication
GitHub Scout uses the existing GitHub user connection. In local and compatibility flows, tools can also use a `GITHUB_TOKEN` session secret. Missing credentials return a connection prompt instead of asking for tokens in chat.
## Included Examples
The adoptable **Coding (Daytona)** and **Coding (Container)** harness examples include `github_scout`, so coding agents based on those examples can delegate GitHub repository lookup work to Scout.
## See Also
* [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/), lifecycle tools used to spawn and manage Scout
* [Author an agent blueprint](https://everruns.com/docs/framework/agent-blueprints/), how Scout and blueprints like it are built
* [Capabilities Overview](https://everruns.com/docs/capabilities/), full capability catalog
---
# Guardrails
> Config-driven checks that constrain agent behavior, inspecting model output and tool activity, then blocking or logging when content matches a rule.
Source:
| | |
| ---------------- | ------------------------------------------ |
| **ID** | `guardrails` |
| **Category** | Safety |
| **Tools** | None |
| **Dependencies** | Utility LLM (only for model-backed checks) |
| **Risk** | Low |
Guardrails are checks that constrain what an agent does. Where most [capabilities](https://everruns.com/docs/capabilities/) *grant* an ability, a guardrail *restricts* one: it inspects model output and tool activity, then **blocks** or **logs** when content matches a rule.
Guardrails are opt-in. An agent with no guardrails is a fully supported configuration, there is no org-mandated enforcement layer. A [harness](https://everruns.com/docs/features/harnesses/) can attach guardrail capabilities as soft defaults that flow to every agent built on it, and an author can still remove them. This is the platform stance: guardrails are a default posture, not a cage.
The `guardrails` capability holds no rules of its own. Its per-agent config is a declarative list of checks plus a mode; the capability compiles that config and contributes the matching runtime hooks. An empty config (or the capability being absent) contributes nothing, with zero added latency.
## Concepts
A **check** binds a **rule** to a **stage** with an **on-fail action**.
### Stages
| Stage | What it sees |
| ------------- | ---------------------------------------------------------------------- |
| `output` | Streamed assistant text |
| `tool_use` | A tool call before it executes, the tool name and serialized arguments |
| `tool_output` | A tool result before it enters model context |
`tool_output` is the trust boundary for untrusted external content (web pages, MCP responses); indirect-injection and secret-leakage checks belong there.
### Rules
| Rule (`type`) | What it matches | Valid stages | Execution |
| -------------- | -------------------------------------------------------------------- | --------------- | ------------------- |
| `regex` | Any of the patterns matches the stage text | all | in-process, sync |
| `blocklist` | Any word/phrase appears as a substring (case-insensitive by default) | all | in-process, sync |
| `tool_pattern` | The tool name matches a `*`-wildcard glob | `tool_use` only | in-process, sync |
| `llm_judge` | A natural-language policy, evaluated by a system model | all | async, model-backed |
| `mcp` | Decision delegated to an external guardrail served over scoped MCP | all | async, off-platform |
| `moderation` | The finalized message scored against content categories | `output` only | async, model-backed |
Deterministic rules (`regex`, `blocklist`, `tool_pattern`) run in the streaming and per-tool-call hot path, linear-time, no I/O, with hard limits on check count, entries, and lengths so an authored pattern can never wedge a worker. Model-backed and MCP rules run only in the async hook path (and, for `output`, on a post-generation end-of-message boundary), never on the sync hot path. On `output` they inspect the finished message once, after streaming, and a block replaces the whole message; an `mcp` check there receives `tool: null`.
### Engines
The two model-backed types, `llm_judge` and `moderation`, choose which system model answers them with `engine`:
* **`utility_llm`** (the default) prompts your org’s utility model for a verdict — `allow`/`block` for a judge, 0-100 scores per category for moderation. One request per check.
* **`jev`** asks [Jev](https://everruns.com/docs/integrations/typesafe/), TypeSafe’s System One model, a typed question and gets a calibrated probability back. The `threshold` you configure (a percentage, default 50) decides the verdict, and every jev check on a stage is answered in a **single** request. It needs `UTILITY_TYPESAFE_API_KEY` on the deployment, or `UTILITY_DECISION_DRIVER=llm` to have the utility model answer the same questions (a yes/no label rather than a calibrated probability, so the threshold then only checks which answer it picked).
```json
{
"stage": "tool_use",
"type": "llm_judge",
"engine": "jev",
"threshold": 70,
"prompt": "Block any tool call that deletes customer records."
}
```
Two reasons to prefer `jev` once your deployment has a key configured. It is cheaper on latency: four judge checks on a tool call cost one round trip instead of four. And the verdict is yours — the model reports how likely a violation is, your threshold decides what to do about it, and there is no written verdict to misparse. For moderation it also reads the *tail* of the distribution rather than a score: content that is probably fine but 30% likely to be a clear violation trips a 30% threshold, where an averaged score would hide it.
`utility_llm` stays the default, so existing configs are unchanged. Both engines fail open, honor `on_fail` and advisory mode identically, and send the same bounded excerpt. A check set to `jev` in a deployment with no TypeSafe key configured is skipped with a warning.
#### Use your organization’s decision model
By default, `jev` checks use the deployment’s decision model. An organization can answer them with its own model instead: pick a **Default decision model** on the Models page, then set **System decisions** to **This organization’s model** (or `PATCH /v1/orgs/{org}` with `"system_decisions": "organization"`). The checks then run on your provider account, count against the session’s budget, and show up in its usage, the same as the `jev_decision` tool.
Once you opt in, the deployment’s model is never used for your checks. If your default decision model is missing, disabled, or failing, `jev` checks fail open with a warning. Switch back with `"system_decisions": "deployment"`.
### On-fail
* `block`, suppresses the matched content: an `output`/`tool_output` block replaces the content with a notice; a `tool_use` block refuses the call and feeds the reason back to the model, which can self-correct. The model’s original tokens are never persisted.
* `log`, records the hit and continues.
An optional per-check `replacement` customizes the block notice or user-facing refusal message.
### Mode: active vs. advisory
A config-level `mode` is `active` (default) or `advisory`. **Advisory downgrades every hit to `log`**: checks run and are recorded, but nothing is blocked. Advisory is how you tune a guardrail against false positives before enforcing it. Mode is per attachment, so the same catalog entry can be advisory on one agent and active on another.
## Config shape
Config is a `GuardrailsConfig` stored under the `guardrails` capability in the agent’s config. Field names are `snake_case`; each check names its rule with a `type` tag alongside the shared `stage` / `on_fail` / `replacement` fields:
```json
{
"mode": "active",
"checks": [
{
"id": "no-secrets-in-output",
"stage": "output",
"on_fail": "block",
"replacement": "[Response withheld: appears to contain a credential.]",
"type": "regex",
"patterns": ["AKIA[0-9A-Z]{16}", "ghp_[A-Za-z0-9]{36}"]
},
{
"id": "no-shell",
"stage": "tool_use",
"on_fail": "block",
"type": "tool_pattern",
"tools": ["bash*", "*exec*"]
}
]
}
```
The `id` is optional but recommended, it is surfaced in reason codes and logs.
## Data egress and failure behavior
* **Deterministic checks** (`regex`, `blocklist`, `tool_pattern`) run entirely in-process; no data leaves the platform.
* **`llm_judge` and `moderation`** send a bounded content excerpt to a system model: with `engine: "utility_llm"`, your org’s *own* configured utility LLM, the same provider the agent already uses; with `engine: "jev"`, the deployment’s decision model, or your organization’s default decision model when **System decisions** is set to it. Either way it is an operator-configured destination, not a per-agent one.
* **`mcp`** sends a bounded content excerpt to an external, operator-configured MCP guardrail endpoint. Tenant scoping is enforced by the host’s per-session scoped-MCP resolver, so a config can only reach servers scoped to its own session/org.
Every async check is bounded (10 s timeout; at most 4 utility-LLM calls per tool call or finalized message, and one batched request for all `jev` checks on a stage) and **fails open**: a timeout, error, or unparseable verdict defaults to `allow`. A guardrail outage, or a hostile MCP endpoint, can only ever *allow*, never make execution more permissive than the no-guardrail baseline in a way that blocks a healthy turn. Model-backed checks flow through utility-LLM accounting, not the session model budget.
## Tuning: dry-run and advisory
Two surfaces let you tune checks before enforcing them:
* **`POST /v1/capabilities/guardrails/dry-run`** evaluates a config against sample text for a given stage, with no session and nothing persisted. It returns the triggered checks (id, rule type, effective action, reason code, matched excerpt) and whether the content would be blocked. It runs only deterministic checks, it never makes a network call, so it is the fast false-positive tuning loop for `regex`/`blocklist`/`tool_pattern`.
* **Advisory mode** runs the full set (including model-backed checks) against real traffic in `log`-only form, so you can review what *would* have been blocked before switching to `active`.
## The gallery: ready-made presets
Rather than authoring checks from scratch, list the **guardrail gallery**: a read-only catalogue of adoptable presets:
```plaintext
GET /v1/capabilities/guardrails/examples
```
Each listing carries a full `config` plus trust metadata so a picker can show what a preset does before adoption:
| Field | Meaning |
| ------------- | ------------------------------------------------------------------------------------------- |
| `check_types` | The rule-type composition (e.g. `["regex"]`, `["llm_judge"]`) |
| `stages` | Which stages the preset’s checks run in |
| `data_egress` | `none` for deterministic presets; `utility_llm` when a preset contains a model-backed check |
`data_egress` is **derived from the check types**, not hand-authored, so it stays correct as presets mix deterministic and model-backed checks. Adoption is client-side config composition: drop a preset’s `config` into the agent’s `guardrails` capability config (merging or replacing checks). The capability settings on an agent or harness do this in the editor: add a preset to append its checks, or add a check and set its rule, stage, and what happens on a match. There is no new persisted resource and no import endpoint. Noisy presets (PII, prompt-injection heuristics) ship `log`-only so they are safe to adopt active and tune before switching individual checks to `block`.
Shipped presets include secret detection, a model-backed secret-leak judge, PII detection, a profanity starter, dangerous-shell blocking, shell-access blocking, and prompt-injection heuristics.
### Worked example: deterministic vs. model-backed secret guardrails
Two presets guard the same risk, a secret reaching output or leaving through a tool, by complementary means.
**`secret-detection`** matches known credential *formats* by pattern. It is in-process and reports no egress:
```json
{
"name": "secret-detection",
"check_types": ["regex"],
"stages": ["output", "tool_output"],
"data_egress": "none"
}
```
It blocks well-known formats (AWS, GitHub, Slack, Google keys, PEM private keys) in model output and in tool results before they reach context. High-precision, safe to run active.
**`secret-leak-judge`** catches secrets by *intent*, including an opaque value whose form is unknown at config time (e.g. one freshly read from a secrets manager), which a regex structurally cannot see:
```json
{
"name": "secret-leak-judge",
"check_types": ["llm_judge"],
"stages": ["tool_use", "tool_output"],
"data_egress": "utility_llm"
}
```
Its `llm_judge` policy blocks a tool call (or result) that would print, echo, log, or transmit secret material in cleartext, while allowing comparisons that reveal only a hash, fingerprint, or redacted form. Because it sits on `tool_use` with `on_fail: block`, a blocked call is **recoverable**: the refusal reason is fed back and the model self-corrects to a safe form (comparing a hash instead of printing the secret). It is model-backed, so `data_egress` is `utility_llm`, adopters are correctly warned that a bounded excerpt leaves the generating path for evaluation. Run it advisory first to tune false positives.
The two are meant to be layered: pattern-matching for known formats, the judge for everything else.
## Reason codes
Every block or log carries a stable `guardrail.` code (e.g. `guardrail.regex`, `guardrail.llm_judge`, `guardrail.moderation`). Clients localize copy from the code rather than the human-readable text. (The separate [Prompt Canary Guardrail](https://everruns.com/docs/capabilities/prompt-canary-guardrail/) capability uses its own `system_prompt_leak` code.)
## Endpoints
| Method | Path | Description |
| ------ | -------------------------------------- | -------------------------------------------------------- |
| `GET` | `/v1/capabilities/guardrails/examples` | List adoptable gallery presets with trust metadata |
| `POST` | `/v1/capabilities/guardrails/dry-run` | Evaluate a config against sample text; nothing persisted |
Both are gated by the same `capability.view` policy as other capability reads.
## Related
* [Prompt Canary Guardrail](https://everruns.com/docs/capabilities/prompt-canary-guardrail/), a narrow streaming-output guardrail for naive system-prompt leakage.
* [Agent Checks](https://everruns.com/docs/features/agent-checks/), advisory, config-*time* review of an agent’s setup. Distinct from guardrails, which enforce at *runtime*.
---
# Host Shell
> Run bash commands on the machine hosting the agent, bounded by a kernel policy (Landlock and seccomp on Linux, Seatbelt on macOS).
Source:
| | |
| ---------------- | -------------------------------------------------------------------------------------------------------- |
| **ID** | `host_shell` |
| **Category** | Execution |
| **Risk** | High |
| **Features** | `file_system` (enables the Workspace tab) |
| **Dependencies** | [`session_file_system`](https://everruns.com/docs/capabilities/file-system/), backed by a real directory |
**Availability:** Framework (embedder) only. Not available in self-hosted Platform or Everruns Cloud.
Run bash commands as real child processes on the machine the agent is running on. Unlike [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/), the toolchain is real: compilers, package managers and test runners work. A kernel policy bounds what those processes may write and whether they may reach the network.
## When to use this instead of Bashkit
Both capabilities contribute a tool named `bash` over the session workspace, so enable one or the other, not both.
| | Bashkit Shell | Host Shell |
| ------------------ | ------------------------------------ | ------------------------------------ |
| Where commands run | In-process interpreter | This machine, as child processes |
| Native binaries | No | Yes |
| Workspace | Session filesystem (virtual or real) | Must be a real directory |
| Boundary | The interpreter, by construction | Landlock + seccomp, or Seatbelt |
| Network | Off, or egress-routed HTTP | Denied unless containment is removed |
Given a virtual session filesystem, `host_shell` refuses and says so rather than inventing a host path.
## Tools
### `bash`
Execute a shell command or a multi-line script.
| Parameter | Type | Required | Description |
| --------------------- | ------ | -------- | ------------------------------------------------------- |
| `command` | string | yes | Shell command(s) to execute |
| `working_dir` | string | no | Directory to run in (default: the workspace root) |
| `sandbox_permissions` | string | no | `use_default` or `require_escalated` |
| `justification` | string | no | User-facing reason for `require_escalated` |
| `output` | string | no | Output verbosity (`auto`, `normal`, …; default: `auto`) |
`commands` is accepted as an alias for `command`, so an agent written against Bashkit Shell keeps working when the backend is swapped.
Returns `stdout`, `stderr`, `exit_code`, `success`, and the `containment` that was in force. A failure that the containment would explain is flagged with `containment_denial: "likely"`. Output streams live to the UI and CLI while the command runs, and long scripts can run detached.
Every call is a fresh non-interactive `bash -lc` (PowerShell on Windows) rooted at the workspace, so no working directory, variable or export survives between calls.
## Containment
Configured per agent, never by the model.
| Mode | Reads | Writes | Network |
| --------------------------- | ---------- | ------------------------------------------------- | ------- |
| `read-only` | the host | private temp only | denied |
| `workspace-write` (default) | the host | workspace, `/tmp`, private temp, configured roots | denied |
| `danger-full-access` | everything | everything | allowed |
Host **reads** are allowed in every contained mode, for toolchain compatibility. The policy stops writes and network exfiltration; it does not stop a command reading unrelated files on the machine. That is the threat model, stated rather than implied.
The environment a command inherits is an allowlist: `PATH`, locale, and toolchain variables survive; `HOME` and `TMPDIR` are replaced with a private per-process directory, and everything else, including every API key and agent socket path, is dropped.
Two platform differences are deliberate:
* `.git` below the workspace is read-only on macOS. Landlock path rules are additive and cannot subtract it, so Linux permits Git metadata writes inside the workspace.
* Windows has no containment implementation. Every mode there runs uncontained, and the capability says so.
On macOS and Linux, containment fails closed: if the OS primitive is unavailable, the command returns a setup error and is not retried on the host.
## Configuration
```json
{
"containment": "workspace-write",
"approval": "never",
"writable_roots": ["/var/cache/agent"],
"foreground_timeout_secs": 120,
"background_timeout_secs": 86400,
"max_output_bytes": 1048576
}
```
An unknown `containment` or `approval` name is rejected rather than defaulted, so a misspelled boundary cannot resolve to a wider one.
`writable_roots` adds directories a build needs to write beyond the workspace, a package cache for example. It is ignored at `read-only`.
## Approvals
| Policy | When a person is asked |
| ----------------- | --------------------------------------------------------------------------------- |
| `never` (default) | never; a request to escalate is refused |
| `on-failure` | when a command fails in a way the containment would explain |
| `on-request` | when the model sets `sandbox_permissions: require_escalated` with a justification |
| `untrusted` | for anything outside a small read-only command set |
That set is one literal command whose options cannot start another program. `rg --pre`, `rg -z`, and `rg --hostname-bin` ask for approval. `git status` asks unless the same command disables repository fsmonitor and hooks (`-c core.fsmonitor=` and `-c core.hooksPath=/dev/null`), because those settings run code during status.
Every policy except `never` needs the host to supply an approval gate. Without one, a policy that would ask refuses instead: an unattended worker has nobody to ask, and a refusal is more honest than a silent escalation.
Regardless of policy, a command that visibly signals the agent’s own process is refused before it is spawned.
## Deployment
This is an embedder capability, not a hosted-product one. It ships in `everruns-core` (`host` feature) behind the `host-shell` feature (also reachable as `host-shell` on the `everruns` facade) and is deliberately absent from the hosted catalog: handing agents arbitrary host processes is something a CLI host, a CI runner, or an operator’s own box opts into, not something a shared multi-tenant worker should offer.
On Linux the kernel policy is applied by a helper process, selected with the `launcher` config key:
| `launcher` | Meaning |
| ---------------------------- | -------------------------------------------------------------- |
| `"discover"` (default) | find `everruns-sandbox-exec` beside the binary, then on `PATH` |
| `{"helper": ""}` | run that binary |
| `{"reexec_self": [""]}` | re-exec this binary with those leading arguments |
`everruns-core` (`host` feature) ships `everruns-sandbox-exec` under the same feature, but cargo does not build a dependency’s binaries, so a single-binary host will not find one beside it. Such a host routes the arguments into `everruns_core::host::containment::worker::run_from_args` from its own `main` and selects `reexec_self`. See the [Host Shell Agent example](https://everruns.com/docs/framework/examples/host-shell-agent/).
## See Also
* [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/), the sandboxed interpreter
* [File System](https://everruns.com/docs/capabilities/file-system/), file operations on the same workspace
* [Daytona](https://everruns.com/docs/capabilities/daytona/) and [E2B](https://everruns.com/docs/capabilities/e2b/), real binaries on someone else’s machine
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Infinity Context
> Trim live prompt history and query older messages on demand, so conversation length is not bounded by the context window.
Source:
| | |
| ---------------- | ------------------ |
| **ID** | `infinity_context` |
| **Category** | Optimization |
| **Features** | None |
| **Dependencies** | None |
Limits the live prompt to recent conversation history while keeping older messages accessible through `query_history`.
This is useful for long-running sessions where the agent should stay responsive without losing access to earlier decisions, identifiers, or requirements.
## Tools
| Tool | Purpose |
| --------------- | ------------------------------------------------------------ |
| `query_history` | Search or retrieve earlier messages from the current session |
## How It Works
1. A message filter caps the number of recent messages sent to the model.
2. If older messages are excluded, the model sees a system notice telling it to use `query_history`.
3. The `query_history` tool can keyword-search history or fetch a specific absolute message range.
## Configuration
Default configuration:
```json
{
"capabilities": ["infinity_context"]
}
```
Custom budget:
```json
{
"capabilities": [
{
"ref": "infinity_context",
"config": {
"context_budget_tokens": 80000,
"min_recent_messages": 12
}
}
]
}
```
| Field | Type | Default | Description |
| ----------------------- | ------- | -------- | ------------------------------------------------------------- |
| `context_budget_tokens` | integer | `100000` | Approximate token budget reserved for message history |
| `min_recent_messages` | integer | `10` | Minimum recent messages to keep even when the budget is tight |
## Limitations
* Search is keyword-based, not semantic
* The tool reads full session history; it does not currently restrict itself to only the trimmed portion
* Budgeting uses a heuristic message-count estimate, not model-specific tokenization
## See Also
* [Context Compaction](https://everruns.com/docs/advanced/compaction/), Complementary capability that reduces the size of messages in the prompt; see [Worker Harness Defaults](https://everruns.com/docs/advanced/compaction/#worker-harness-defaults) for how they work together
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
* [Harnesses](https://everruns.com/docs/features/harnesses/)
---
# Knowledge
> Attach versioned file collections to agents, browse originals and extracted text, and optionally index them for keyword or hybrid retrieval.
Source:
Knowledge brings notes, uploaded files, repository trees and Everruns documentation into one collection model. Indexing is optional. Files remain available to browse, read and grep when indexing is off.
Knowledge is an Adoption feature, available on development and hosted deployments and disabled by default. An organization owner/admin or platform operator can enable the organization’s **Knowledge** feature. Then open **Knowledge** and create a collection. Upload files or a folder with its relative paths, write Markdown with optional OKF frontmatter, or connect a source. GitHub sources reuse existing organization or virtual-user GitHub connections. Public HTTPS Git repositories work without a GitHub connection. Source roots must not overlap, and a connected source cannot overwrite a managed file.
## Attach to an agent
Add the **Knowledge** capability and select collections in the agent editor, or use this capability reference:
```json
{
"ref": "knowledge",
"config": {
"collections": ["everruns-docs"],
"automatic": false,
"limit": 8
}
}
```
`everruns-docs` is predefined, requires no credentials, and contains a snapshot of the documentation bundled with the server. Indexing starts off. Managers may enable it like any other collection.
The agent sees read-only mounts:
* `/knowledge/index.md`: the attached collection catalog.
* `/knowledge//`: original files, with their hierarchy preserved.
* `/knowledge-text//`: extracted text where available.
The existing **Data Knowledge** capability keeps its read-only `/knowledge/{tables,business,queries}` scaffold. Its index and the collection catalog share `/knowledge/index.md`; collection ID subtrees always use live collection permissions, including when a collection is detached. Directory listings and grep scoped to a Knowledge path include both sources.
Read indexes first, then individual files as needed. An authored `index.md` is preserved; collections without one receive a bounded generated index. Use `browse_knowledge` or `knowledge files` to page through larger directories without enabling indexing. Request metadata only when needed; metadata pages are separately bounded. Knowledge bindings belong to the calling session, including when several sessions share a workspace. Sharing workspace files does not share Knowledge attachments.
## Ownership and attachment scope
Collections, their indexes and search-provider accounts belong to an organization. Users manage them through organization permissions; a personal GitHub connection supplies source credentials, not a private user index.
Attach collections through the **Knowledge** capability on a harness, agent or session. Configuration follows the existing capability precedence: harness, then agent, then session. A higher layer replaces the lower layer’s configuration for that capability, so list every collection the session should retain. Collections are shared rather than copied or reindexed for each agent or session.
Runtime tools, Bashkit search, automatic retrieval and mounts use the calling session’s effective collection bindings and live file visibility. There are no separate user-, agent- or session-owned Knowledge indexes, and an agent does not automatically search every collection in its organization. Sharing a workspace does not share its sessions’ Knowledge bindings.
## Search and grep
Choose **Off**, **Keyword**, or **Hybrid** in collection settings. Hybrid needs an enabled embedding model and a persistent Turbopuffer backend. Keyword indexing works with Turbopuffer or the local PostgreSQL full-text fallback. PostgreSQL stores catalog rows, fragment offsets and local lexical postings, not canonical passage text or binary payloads. Collections using a remote indexer keep their postings there.
The `search_knowledge` tool accepts a query, optional collection subset, path prefix, user metadata filter, result limit and `lexical_only` flag. Requested collections are always intersected with the session’s current bindings. Returned passages carry immutable file/version IDs, source locators and extracted-text offsets.
Bashkit uses the same retrieval service:
```bash
knowledge files --collection kn_0123456789abcdef0123456789abcdef --path /policies --limit 50
knowledge search --limit 5 'refund policy'
knowledge search --collection kn_0123456789abcdef0123456789abcdef --lexical 'retention'
knowledge search --metadata '{"team":"support"}' 'refund policy'
grep -n 'retention' /knowledge/kn_0123456789abcdef0123456789abcdef/policies.md
```
Exact grep is independent of the index. Scope it to `/knowledge-text` to search extracted documents. Scans and returned data are bounded; narrow the path if a scan exceeds its budget.
With `automatic: true`, Everruns retrieves a small amount of relevant evidence for the incoming user message before model execution. Evidence is retained by input ID for replay, checked against live visibility, and added as source data. Retrieval has a five-second timeout and does not prevent a turn from running when unavailable. Query embedding calls use session usage events; ingestion embeddings are attributed to the organization.
## Content and metadata
Original bytes are retained for every format. Markkit extracts HTML, DOCX and spreadsheets. Text and code retain their original UTF-8 representation. Markdown frontmatter, including OKF fields and unknown extensions, is preserved alongside user annotations. Links and file paths remain source-relative. OKF remains a directory of original files: concept Markdown, reserved `index.md` navigation, `log.md` history and binary assets all round-trip through uploads/downloads or a Git source. Unknown frontmatter fields remain intact, including BOM/CRLF files; there is no conversion into fixed entry kinds. `GET /v1/knowledge/{collection_id}/okf` validates a page of OKF v0.2 metadata and reserved-file structure. Follow `next_cursor` to validate the rest of a large catalog. This checks metadata requirements, not every Markdown link or authored index entry.
Unsupported binaries, including PDFs, images, audio and video, remain downloadable. This release does not provide PDF extraction, OCR, transcription or multimodal embeddings. Files exceeding the extraction budget remain stored and report that state rather than disappearing.
Collection rules independently exclude paths from ingestion, indexing or agent reads. File controls can disable indexing or agent reads without removing the original. Managers can annotate connected files; their source content stays read-only.
## Incremental processing and storage
Sync compares Git blob identities before uploading content artifacts. Changed files receive immutable versions; unchanged files retain their version and citations. Extraction is reused for metadata-only edits, and embeddings are cached by organization, model profile and passage hash. Source deletions apply only after a successful complete traversal. Leased jobs renew during source fetches and indexing, retry interrupted work, and index generations publish after the catalog’s visibility checks pass.
Uploads and downloads stream in bounded segments. Content uses the configured S3-compatible blob store; local development falls back to `KNOWLEDGE_STORAGE_ROOT` (default `.local/knowledge`). Use shared object storage for multiple server replicas. Back up the database and content store together.
Organization search accounts reference encrypted, write-only organization service credentials. A revoked or invalid selected credential fails closed; it never falls back to the deployment account. Embedding provider credentials are configured separately in the model catalog.
## Current bounds
File catalogs paginate at up to 500 rows. Agent directory listings cap at 500 children, file reads at 1 MiB, extraction at 8 MiB, and exact grep at 5 MiB of scanned content, 5,000 candidate files and a 512 KiB per-file ceiling. Streamed originals have a 1 TiB per-file transfer ceiling; this is a safety limit, not a tested capacity claim. Git fetching streams shallow snapshot packs directly into the object store, with the existing 512 MiB pack limit. A synthetic million-file catalog and bounded streamed-original fixture qualify catalog/transfer behavior; these do not establish end-to-end terabyte indexing throughput. Source refresh is explicit. Retained versions currently have no automatic expiry; monitor content storage and keep it with database backups. Additional source adapters and document extractors can use the same collection boundary.
Management APIs are under `/v1/knowledge` and `/v1/knowledge-indexers`; the generated command catalog exposes the corresponding `everruns knowledge` commands. Organization settings permissions govern management, and explicit agent/session bindings govern runtime reads. A read-only personal access token can browse, download, validate metadata and search; file changes, source refresh and configuration require a full-access token.
## Deployment storage
Knowledge originals and extracted text need shared, persistent content storage on hosted replicas. `KNOWLEDGE_STORAGE_BACKEND=s3` uses the existing `STORAGE_S3_*` configuration for Knowledge alone, even while `STORAGE_BLOB_BACKEND=db` keeps workspace files and image bytes inline in PostgreSQL. It does not backfill or move existing files. The default `inherit` uses the existing shared blob backend or the local development fallback. Do not use the local fallback on ephemeral hosted containers. An invalid explicit S3 configuration fails startup; storage errors never silently switch to local disk. Organization adoption and collection permissions remain separate.
---
# Knowledge Base
> The undeployed split knowledge features have been consolidated into Knowledge.
Source:
Use [Knowledge](https://everruns.com/docs/capabilities/knowledge/) for notes, files, source trees and optional indexing. The former `knowledge_base` capability is no longer registered.
---
# Knowledge Index
> The undeployed split knowledge features have been consolidated into Knowledge.
Source:
Use [Knowledge](https://everruns.com/docs/capabilities/knowledge/) for notes, files, source trees and optional indexing. The former `knowledge_index` capability is no longer registered.
---
# Tool Loop Detection
> Detects an agent repeating the same tool calls, results, failed edits, or file reads, and injects a warning that tells it to change approach.
Source:
| | |
| ---------------- | ---------------- |
| **ID** | `loop_detection` |
| **Tools** | None |
| **Features** | None |
| **Dependencies** | None |
Tool loop detection watches the conversation for an agent that keeps doing the same thing without progress. When it finds a loop, it adds a short system message after the latest input telling the model what it is repeating and what to do instead: change the arguments, look at a different source, or report the blocker.
It does not stop the turn, block a call, or change any tool result. The warning is rebuilt from the conversation each time messages are loaded for the model, so it is never stored in session history, and later requests replay the same prompt prefix.
Conversation and both Worker levels include it, as do deprecated Generic and Platform Chat.
## What counts as a loop
The checks run in this order, and the first match produces the warning:
| Pattern | Triggers at |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| A mutating tool (`edit_file`, `write_file`, `delete_file`, `bash`, or an MCP tool ending in `__` plus one of those names) failing the same way with identical arguments, in consecutive calls | `mutating_failure_threshold`, default 2 |
| The same tool call returning the same result, in consecutive calls since the last user message | `threshold`, default 3 |
| Repeated reads of the same file (`read_file`, `read_many_files`) at the same offset, mixed with reads of other ranges of that file | `threshold`, default 3 |
| The same batch of tool calls with identical arguments in consecutive assistant messages, regardless of call order inside the batch | `threshold`, default 3 |
The lower threshold for failed mutations interrupts a broken edit or command before a third identical attempt with side effects.
## Configuration
```json
{
"ref": "loop_detection",
"config": { "threshold": 3, "mutating_failure_threshold": 2 }
}
```
| Field | Default | Description |
| ---------------------------- | ------- | ----------------------------------------------------------------------------------- |
| `threshold` | `3` | Repetitions of the same call batch, call result, or read range that count as a loop |
| `mutating_failure_threshold` | `2` | Identical failed results from a mutating tool that count as a loop |
Both must be integers of at least 1.
In the [Framework](https://everruns.com/docs/framework/advanced-capabilities/), add it by ID:
```rust
use everruns::CapabilityRef;
use serde_json::json;
let agent = Agent::builder()
// ...
.capability(CapabilityRef::new("loop_detection").config(json!({ "threshold": 4 })))
.build()?;
```
## See also
* [Tool Call Repair](https://everruns.com/docs/capabilities/tool-call-repair/), recovery for malformed tool-call arguments
* [Conversation harness](https://everruns.com/docs/built-ins/harnesses/conversation/), the default foundation that includes it
---
# Memory
> Mount named organization Memories into the session workspace, read-only by default or read-write as shared working memory across sessions.
Source:
| | |
| ---------------- | ------------------------------------------------------------------ |
| **ID** | `memory` |
| **Category** | Memory |
| **Tools** | None |
| **Features** | `file_system` |
| **Dependencies** | [File System](https://everruns.com/docs/capabilities/file-system/) |
| **Risk** | Medium |
The `memory` capability mounts organization Memories into a session’s `/workspace`. A Memory is a named, durable file store owned by the organization. Once mounted, the agent reads and writes it with the ordinary [file tools](https://everruns.com/docs/capabilities/file-system/), so the capability adds no tools of its own.
Use it for reference material that many sessions share, such as runbooks or product documentation, or for working files that should outlive one session.
This capability selects **organization** Memories only. Agent memory (`/memory/agent`) and user memory (`/memory/user`) are mounted by the server without this capability. [Agent and User Memory](https://everruns.com/docs/features/memory-scopes/) explains the three scopes and their privacy rules.
## Configuration
```json
{
"ref": "memory",
"config": {
"mounts": [
{
"memory": "mem_0193f0c2a1b27c4e8f5d6a7b8c9d0e1f",
"path": "/workspace/runbooks",
"mode": "readonly"
}
]
}
}
```
| Field | Required | Description |
| ----------------- | -------- | --------------------------------------------------------- |
| `mounts[].memory` | Yes | Memory ID, `mem_` followed by 32 lowercase hex characters |
| `mounts[].path` | Yes | Absolute path under `/workspace` where the Memory appears |
| `mounts[].mode` | No | `readonly` (default) or `readwrite` |
An empty config, or no `mounts`, is valid and mounts nothing.
Validation rejects:
* a path outside `/workspace`
* two mounts with the same path
* overlapping paths, where one mount path is a parent of another (`/workspace/data` and `/workspace/data/inner`)
The `/memory/*` namespace is reserved for the server-managed scopes and cannot be used as a mount path here.
## Access modes
* `readonly` mounts reject writes with an error from the file tools.
* `readwrite` mounts write through to the durable Memory, so later sessions see the change.
* Memories synced from GitHub or Git are always read-only, whatever mode the mount asks for.
The mount set is captured when the session is created. Archiving or renaming a Memory later does not change what a running session mounted.
## Managing Memories
Create Memories and edit their files in the **Memory** page of the UI or through the `/v1/memories` API. The [API reference](https://everruns.com/docs/api/) has the request shapes.
## See also
* [Agent and User Memory](https://everruns.com/docs/features/memory-scopes/), the three memory scopes
* [Knowledge Base](https://everruns.com/docs/capabilities/knowledge-base/), curated entries the agent searches instead of reading files
* [Knowledge Index](https://everruns.com/docs/capabilities/knowledge-index/), semantic search over a synced source
* [File System](https://everruns.com/docs/capabilities/file-system/), the tools that read and write mounted files
---
# Message Metadata
> Annotate user and agent messages with metadata such as their timestamp when they are sent to the LLM, so agents can reason about timing and gaps between messages.
Source:
| | |
| ---------------- | ------------------ |
| **ID** | `message_metadata` |
| **Category** | Core |
| **Features** | None |
| **Dependencies** | None |
Annotates user and agent messages with metadata, currently each message’s timestamp (UTC), when building the LLM request. The model sees each message prefixed with an annotation like:
```plaintext
[time 2026-06-11T09:15:42Z] What changed since yesterday?
```
For user messages the timestamp is when the message was received; for agent messages, when the reply was generated.
This lets agents reason about timing: how long ago something was said, gaps between messages, and whether earlier statements are stale.
Opt-in on the canonical levels. Included in deprecated Generic and Platform Chat.
Annotations are applied only to the prompt-facing view of the conversation. Stored messages are never modified, and timestamps are stable across turns so prompt caching is unaffected. A short system prompt addition explains the annotation format to the model and instructs it not to emit annotations in its replies.
## Tools
None.
## Configuration
| Field | Type | Default | Description |
| -------- | ----- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fields` | array | `["timestamp"]` | Metadata fields to render, in order. Supported: `timestamp`. An empty array disables annotations. More fields (e.g. the LLM model) will be added over time. |
User and agent messages are always annotated; system and tool-result messages never are.
## See Also
* [Current Time](https://everruns.com/docs/capabilities/current-time/), tool to get the current wall-clock time
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# OpenAI Image Generation
> Generate and edit raster images with OpenAI's GPT Image API, persist artifacts, and save outputs into the session workspace.
Source:
| | |
| ---------------- | ---------------------------------------------------------------------------- |
| **ID** | `gpt_image_gen` |
| **Category** | Media |
| **Features** | None |
| **Dependencies** | [`session_file_system`](https://everruns.com/docs/capabilities/file-system/) |
Generate new raster images and edit existing ones with OpenAI’s ChatGPT Images 2.0 API model, `gpt-image-2`, by default. The capability also supports Meta’s Muse image model (`muse-image-1.0`) through Meta or OpenRouter providers.
Capability config supports both model selection and a default quality used when the tool call does not specify one:
```json
{
"model": "gpt-image-2",
"default_quality": "medium",
"partial_images": 1,
"fallback": "auto"
}
```
If you need the previous generation model for compatibility, set `"model": "gpt-image-1"`. To use the Muse image model instead, set `"model": "muse-image-1.0"` and configure a Meta provider (served as `muse-image-1.0`) or an OpenRouter provider (served as `meta/muse-image`).
When no OpenAI or Azure OpenAI credentials are configured but a Meta or OpenRouter provider is available, the capability falls back to the Muse image model if `fallback` is `"auto"` (the default). Set `fallback` to `"off"` to require OpenAI or Azure OpenAI credentials for GPT image models instead.
The default quality is `medium`. That keeps latency and reliability reasonable for `gpt-image-2` while still producing polished outputs.
The default `partial_images` value is `1`. For single-image requests, the capability emits `tool.progress` status updates while waiting for the final image. Set it to `0` to disable progress updates, or up to `3` for more feedback at higher token cost.
This capability resolves credentials server-side, persists durable image artifacts, and can also write generated outputs into the session filesystem under `/workspace/.outputs/images/`.
## Credential Resolution
The capability never reads provider credentials from session secrets or environment variables. Resolution order:
1. Default OpenAI provider credentials from the control plane
2. Default Azure OpenAI provider credentials from the control plane
3. Default Meta or OpenRouter provider credentials from the control plane (Muse image model)
## Network Policy
Image requests leave through the platform’s outbound network boundary, like web fetch and MCP. The provider base URL must be allowed by the session’s network access policy and, when enabled, the system allowlist. Its hostname must resolve to public addresses only, and the connection is pinned to those addresses. Redirects from the image API are treated as errors and never followed, so the provider key is only sent to the configured host.
## Tools
### `generate_image`
Generate one or more images from a prompt.
| Parameter | Type | Required | Description |
| -------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `prompt` | string | yes | Image generation prompt |
| `size` | enum | no | `1024x1024`, `1536x1024`, `1024x1536`, `auto` |
| `quality` | enum | no | `low`, `medium`, `high`, `auto`. Defaults to capability `default_quality`, which defaults to `medium` |
| `background` | enum | no | `transparent`, `opaque`, `auto` |
| `format` | enum | no | `png`, `jpeg`, `webp` |
| `count` | integer | no | Number of images to generate (1-10) |
| `save_to_session_fs` | boolean | no | Save images into the session filesystem |
| `output_dir` | string | no | Filesystem output directory (default `/workspace/.outputs/images`) |
| `filename_prefix` | string | no | Prefix for artifact and file names |
| `persist_artifact` | boolean | no | Persist into durable image storage (default `true`) |
### `edit_image`
Edit one or more existing images using a prompt.
| Parameter | Type | Required | Description |
| -------------------- | ------- | ----------- | ----------------------------------------------------------------------------------------------------- |
| `prompt` | string | yes | Editing prompt |
| `image_id` | string | conditional | Durable image artifact ID to use as an edit source |
| `path` | string | conditional | Session filesystem path to use as an edit source |
| `size` | enum | no | `1024x1024`, `1536x1024`, `1024x1536`, `auto` |
| `quality` | enum | no | `low`, `medium`, `high`, `auto`. Defaults to capability `default_quality`, which defaults to `medium` |
| `background` | enum | no | `transparent`, `opaque`, `auto` |
| `format` | enum | no | `png`, `jpeg`, `webp` |
| `count` | integer | no | Number of images to produce (1-10) |
| `save_to_session_fs` | boolean | no | Save outputs into the session filesystem |
| `output_dir` | string | no | Filesystem output directory (default `/workspace/.outputs/images`) |
| `filename_prefix` | string | no | Prefix for artifact and file names |
| `persist_artifact` | boolean | no | Persist into durable image storage (default `true`) |
At least one of `image_id` or `path` is required. When both are present, both source images are sent to the edit request.
## Result Shape
Both tools return:
* Native image blocks for direct model consumption
* Structured JSON with:
* `artifact_id` when durable storage is enabled
* `session_file` when workspace save is enabled
* `media_type`, `filename`, `size_bytes`
* `revised_prompt` when OpenAI returns one
## Notes
* Transparent background requires `png` or `webp` output
* High quality can take substantially longer than medium or low on `gpt-image-2`
* Single-image requests emit progress updates by default; multi-image batches still wait for the final response
* Each additional streamed update adds extra image output tokens on the OpenAI side, so higher `partial_images` values trade cost for better perceived latency
* `generate_image` and `edit_image` stay fully exposed even when OpenAI `tool_search` is enabled, so large tool lists do not defer their schemas
* Session file edits must be `png`, `jpg`, `jpeg`, or `webp`
* Edit sources larger than 50 MB are rejected before the API call
* Saved workspace files are written as base64-encoded binary files
## See Also
* [File System](https://everruns.com/docs/capabilities/file-system/), read and reuse workspace images
* [Storage](https://everruns.com/docs/capabilities/session-storage/), store per-session OpenAI overrides
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# OpenAI Server Tools
> Enable OpenAI's hosted tools (web search, code interpreter, hosted shell, file search, remote MCP) for agents on the OpenAI or Azure OpenAI Responses API.
Source:
| | |
| ---------------- | ------------------------------------------------------------ |
| **ID** | `openai_server_tools` |
| **Category** | Tools |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | High (grants provider-executed web reach and code execution) |
Enables [OpenAI’s hosted tools](https://platform.openai.com/docs/guides/tools) on agents whose model runs on the [OpenAI](https://everruns.com/docs/providers/openai/) or Azure OpenAI provider. OpenAI runs a hosted tool inside the same response: the model decides to search, OpenAI performs the search, and the answer comes back grounded in the results with its sources cited. The agent loop never dispatches the call, so there is no extra round-trip through Everruns.
| Tool | What OpenAI runs |
| ------------------ | ---------------------------------------------------- |
| `web_search` | Searches the web and cites the pages it used |
| `code_interpreter` | Python in an OpenAI-managed container |
| `shell` | Shell commands in an OpenAI-managed container |
| `file_search` | Retrieval over your OpenAI vector stores |
| `mcp` | Calls to remote MCP servers you list, after approval |
## Tools
None. This capability configures the OpenAI request, it does not provide client-side tools. Hosted calls appear in the session’s activity (see [Activity and cost](#activity-and-cost)); their results stay inside OpenAI’s response and reach the conversation only through the answer.
Code interpreter and shell run in OpenAI’s container, not in the session’s Everruns sandbox: files they create are not in the session file system. Use Everruns’ own execution capabilities when the agent’s work must land there.
## Configuration
### Enable web search
```json
{
"capabilities": [
{
"ref": "openai_server_tools",
"config": { "tools": ["web_search"] }
}
]
}
```
### Tune web search
```json
{
"capabilities": [
{
"ref": "openai_server_tools",
"config": {
"tools": ["web_search"],
"web_search_context_size": "high",
"web_search_allowed_domains": ["openai.com", "everruns.com"],
"web_search_user_location": { "country": "US", "city": "Chicago", "timezone": "America/Chicago" }
}
}
]
}
```
### Code, shell, and file search
```json
{
"capabilities": [
{
"ref": "openai_server_tools",
"config": {
"tools": ["code_interpreter", "shell", "file_search"],
"container_memory_limit": "4g",
"file_search_vector_store_ids": ["vs_abc123"],
"file_search_max_results": 8
}
}
]
}
```
### Remote MCP
```json
{
"capabilities": [
{
"ref": "openai_server_tools",
"config": {
"tools": ["mcp"],
"mcp_servers": [
{ "server_label": "deepwiki", "server_url": "https://mcp.deepwiki.com/mcp" },
{
"server_label": "docs",
"server_url": "https://docs.example.com/mcp",
"allowed_tools": ["search"],
"require_approval": "never"
}
]
}
}
]
}
```
OpenAI connects to each server and asks before every call. The turn pauses with an approval card in the chat that shows the server, the tool, and the exact arguments; **Approve** runs the call and **Deny** lets the model answer without it. API clients see the request as a `tool.call_requested` event with an `openai_mcp_approval` call and answer it through `POST /v1/sessions/{id}/tool-results` with `{"approve": true}` or `{"approve": false}`. Any other result, an error included, denies.
A server can skip approval with `"require_approval": "never"`, but only for an explicit `allowed_tools` list, so every unattended call is a tool you named.
A `server_url` must be `https` and must not carry a username or password, so it suits public servers. A server that needs an API key or an OAuth connection is named by `mcp_server`, the name of an MCP server registered in Everruns, instead of a URL:
```json
{ "server_label": "linear", "mcp_server": "linear" }
```
Its URL and credentials come from the MCP server registration and the session’s connections on every call, exactly as for the agent’s own MCP tools, and never appear in agent config, events, or logs. If the connection is missing, the turn fails with a message naming where to connect it. Servers that bind secrets to tool parameters cannot be used this way, because OpenAI calls them directly.
Config rules:
* `tools`, array of hosted tool names: `web_search`, `code_interpreter`, `shell`, `file_search`, `mcp`. Other names are rejected on write.
* `web_search_context_size`, `low`, `medium`, or `high`: how much retrieved context the model may use per search. OpenAI defaults to `medium`.
* `web_search_allowed_domains`, bare domains (no scheme or path) that limit where results may come from.
* `web_search_user_location`, optional `country` (two-letter ISO code), `region`, `city`, and `timezone` (IANA name) used to localize results.
* `container_memory_limit`, `1g`, `4g`, `16g`, or `64g`: memory for the container that runs `code_interpreter` and `shell`. OpenAI defaults to `1g`; larger containers cost more.
* `file_search_vector_store_ids`, OpenAI vector store ids (`vs_...`) in the same OpenAI account as the provider’s API key. Required when `file_search` is on.
* `file_search_max_results`, 1 to 50 results per search.
* `mcp_servers`, at least one server when `mcp` is on. Each has a unique `server_label` (letters, digits, `-`, `_`), either an `https` `server_url` or a registered `mcp_server` name, optional `allowed_tools`, and `require_approval` (`always`, the default, or `never`).
Each option only takes effect when its tool is in `tools`.
## Provider support
Hosted tools run only on the OpenAI and Azure OpenAI Responses API. On any other provider, including OpenAI-compatible gateways, the turn fails with a message that names the provider, instead of answering without the tools. Switch the agent’s model or remove the capability. For OpenRouter models, use [OpenRouter Server Tools](https://everruns.com/docs/capabilities/openrouter-server-tools/).
Each model decides which hosted tools it supports; OpenAI rejects a request that asks a model for a tool it does not offer, and that error is shown on the turn.
## Security
Hosted tools send conversation content to OpenAI-side tools. Web search gives the model **provider-executed web reach**, code interpreter and shell give it **code execution in an OpenAI container**, and file search reads whatever the listed vector stores hold. Remote MCP sends the model’s tool arguments to the servers you list, which are third parties to both OpenAI and Everruns; approval shows each call before it happens. OpenAI performs the requests, so Everruns’ own egress controls do not apply, the same data-exfiltration class as client-side [Web Fetch](https://everruns.com/docs/capabilities/web-fetch/). The capability is rated **High risk** and uses the admin-only assignment gate. Grant it only to agents you trust with outbound web access and code execution, list only vector stores whose contents every user of the agent may see, and list only MCP servers you trust with the conversation.
## Activity and cost
Each hosted call shows up in the session’s activity as it happens, through the `tool.hosted_call` event, with a short detail: the search query, the first line of code, the shell commands, the file search query, or the MCP server and tool. It is not an Everruns tool call: nothing is dispatched and no tool result is stored.
OpenAI bills hosted tool calls per call on top of tokens. Session usage adds them to the estimated cost at OpenAI’s list price: $10 per 1,000 web searches on GPT-5 and newer reasoning models, $25 per 1,000 on GPT-4o and GPT-4.1, and $2.50 per 1,000 file searches. Code interpreter and shell are billed by OpenAI per container session rather than per call, so they are not in the estimate; check OpenAI’s usage dashboard for them. File search also bills vector store storage.
## See Also
* [OpenAI provider](https://everruns.com/docs/providers/openai/)
* [OpenRouter Server Tools](https://everruns.com/docs/capabilities/openrouter-server-tools/), the OpenRouter counterpart
* [Web Fetch](https://everruns.com/docs/capabilities/web-fetch/), the client-executed equivalent
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# OpenRouter Server Tools
> Enable OpenRouter's server tools (web search, web fetch, datetime, image generation and more), which OpenRouter runs before it returns the final answer.
Source:
| | |
| ---------------- | ----------------------------------------- |
| **ID** | `openrouter_server_tools` |
| **Category** | Tools |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | High (grants provider-executed web reach) |
Enables [OpenRouter’s provider-executed “server tools”](https://openrouter.ai/docs/guides/features/server-tools) (beta). Unlike normal [function tools](https://everruns.com/docs/features/capabilities/), these run **server-side by OpenRouter**: it loops internally and returns the final answer, so the agent loop never dispatches them. This capability contributes *request intent*, not executable tools, the selected tools are compiled into the OpenRouter request’s `tools` array as provider-executed entries. The concrete implementation lives in the focused `openrouter` feature of `everruns-integrations` crate; core carries only the provider-neutral routing contract.
This is the OpenRouter counterpart to client-executed web access like [Web Fetch](https://everruns.com/docs/capabilities/web-fetch/): the difference is *who runs the tool*. With server tools, OpenRouter performs the search or fetch and folds the results into the same generation, no extra round-trip through Everruns. Use it when your agents run on the [OpenRouter provider](https://everruns.com/docs/providers/openrouter/) and you want built-in web reach without wiring up a separate search [integration](https://everruns.com/docs/integrations/).
## Tools
None, this capability configures the OpenRouter request, it does not provide client-side tools. The model invokes server tools during the generation and OpenRouter executes them; the only client-visible artifact is the final answer.
## Available server tools
OpenRouter exposes these server tools. Enable any subset:
| Tool | Name | What it does |
| ---------------- | ------------------ | ------------------------------------------------------------------------------------------ |
| Web Search | `web_search` | Searches the web and grounds the answer in results. Accepts an optional `max_results` cap. |
| Web Fetch | `web_fetch` | Fetches and reads a URL the model chooses. |
| Date & Time | `datetime` | Gives the model the current date and time. |
| Image Generation | `image_generation` | Generates images inline. |
| Apply Patch | `apply_patch` | Applies code patches. |
| Fusion | `fusion` | OpenRouter’s Fusion tool. |
| Advisor | `advisor` | OpenRouter’s Advisor tool. |
| Subagent | `subagent` | Delegates to an OpenRouter-run subagent. |
`web_search` is the only server tool that takes parameters today (`web_search_max_results`). Availability of each tool depends on the upstream model and OpenRouter’s beta rollout, see [OpenRouter’s server-tools docs](https://openrouter.ai/docs/guides/features/server-tools) for the current list.
## How it works
1. **Capability config → request intent**: the tools you enable are compiled into the OpenRouter routing config and serialized by the OpenRouter driver into the request’s `tools` array as `{"type":"openrouter:…"}` entries.
2. **OpenRouter executes server-side**: when the model decides to call a server tool, OpenRouter runs it, loops internally, and returns the final answer. The agent loop never sees an intermediate tool call.
3. **No-op off OpenRouter**: non-OpenRouter providers ignore the routing config entirely. Enabling this capability on a non-OpenRouter agent is a harmless no-op, so it is safe to leave on for agents that may switch providers.
## Configuration
### Enable web search
```json
{
"capabilities": [
{
"ref": "openrouter_server_tools",
"config": { "tools": ["web_search"] }
}
]
}
```
### Enable several tools and cap web-search results
```json
{
"capabilities": [
{
"ref": "openrouter_server_tools",
"config": {
"tools": ["web_search", "web_fetch", "datetime"],
"web_search_max_results": 5
}
}
]
}
```
Config rules:
* `tools`, array of server-tool names from the table above. Unknown names are rejected on write. Duplicates are de-duplicated.
* `web_search_max_results`, positive integer; only decorates `web_search`. It is ignored for every other tool and rejected when `< 1`.
## Security
Enabling server tools grants the model **provider-executed web reach** (`web_search` / `web_fetch`). OpenRouter performs these requests, so Everruns’ own egress controls do not apply, the same data-exfiltration class as client-side [Web Fetch](https://everruns.com/docs/capabilities/web-fetch/). The capability is therefore rated **High risk** and gated behind the same admin-only trust check as other outbound-web capabilities. Grant it only to agents you trust with outbound web access.
## Limitations
* **OpenRouter only**: this is an OpenRouter request extension. Other providers ignore it (no error, no behavior change).
* **Beta**: server tools are an OpenRouter beta; tool availability varies by upstream model and may change.
* **Provider-side execution**: because OpenRouter runs the tools, their activity does not appear as Everruns tool calls. Inspect them in OpenRouter’s [dashboard logs](https://everruns.com/docs/providers/openrouter/#logs-traces-and-observability) instead.
## See Also
* [OpenRouter provider](https://everruns.com/docs/providers/openrouter/), configure the provider these tools run on, plus OAuth, actual-cost reporting, and logs
* [OpenRouter server-tools docs](https://openrouter.ai/docs/guides/features/server-tools), official OpenRouter guide
* [Web Fetch](https://everruns.com/docs/capabilities/web-fetch/), the client-executed equivalent
* [Integrations overview](https://everruns.com/docs/integrations/), search and web integrations as an alternative
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Output Truncation
> Chooses what a turn does when a model response hits the output token limit in the middle of a tool call, retry with a notice, fail, or carry on.
Source:
| | |
| ---------------- | ------------------- |
| **ID** | `output_truncation` |
| **Category** | Safety |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | Low |
A model response that reaches its output token limit (finish reason `length`) can stop in the middle of a tool call. Everruns never runs such a call, and never runs it with empty (`{}`) arguments in place of the ones the model did not finish. This capability chooses what the turn does next. On OpenAI Responses and Bedrock it also applies when a finished call arrives with arguments that are not valid JSON.
Every agent gets the default policy, `continue`, without adding the capability. Add it only to change the policy or the retry limit.
## Tools
None, this capability only configures the turn loop.
## How It Works
When a response loses tool calls this way:
* Calls in the same response whose arguments arrived complete still run.
* The cut-off calls do not run. Every provider drops them: Anthropic, OpenAI (Chat Completions and Responses), Gemini, and Bedrock.
* The policy decides the turn’s next step:
| Policy | What happens |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `continue` (default) | The model gets a short notice that its output hit the limit and the call was not run, and the turn runs another model call so it can retry with less output. After `max_retries` cut-off responses in a row, the turn fails with an error. |
| `fail` | The turn ends with an error. |
| `off` | The turn carries on as if the model had not asked for the cut-off calls: it ends on the partial answer, with stop reason `max_tokens`. |
The `continue` notice appears in the conversation the model sees, right after the cut-off response and any results of the calls that did run. It is not stored as a message and is not shown in the chat. A clean model response, or a new user message, resets the count of consecutive cut-off responses.
Responses that are refused or filtered (`content_filter`, `refusal`) are not retried: retrying does not fix them.
## Config
```json
{
"capabilities": [
{
"ref": "output_truncation",
"config": { "policy": "continue", "max_retries": 2 }
}
]
}
```
| Field | Type | Default | Description |
| ------------- | ----------------------------- | ---------- | ------------------------------------------------------------------- |
| `policy` | `continue` \| `fail` \| `off` | `continue` | What the turn does when a response loses tool calls |
| `max_retries` | integer, 0 to 10 | `2` | Cut-off responses in a row `continue` retries before the turn fails |
When the capability is set on both the harness and the agent, the agent’s config wins.
## Observability
Each `llm.generation` event records how the response ended:
* `finish_reasons`;
* `tool_calls_dropped`;
* `truncation_gate`, set to `retried` or `failed` when the policy acted.
The same value is exported as the `everruns.llm.truncation_gate` span attribute and counted in the `everruns_llm_truncation_gate_total` metric. See the [event reference](https://everruns.com/docs/event-reference/#llmgeneration).
## Related
* [Tool Call Repair](https://everruns.com/docs/capabilities/tool-call-repair/), recovery for tool-call arguments that are almost valid JSON
* [Tool Loop Detection](https://everruns.com/docs/capabilities/loop-detection/)
---
# Parallel Tool Calls
> Controls whether the agent requests multiple tool calls per turn and runs them concurrently, prefer parallel, avoid (serialize), or leave the provider default.
Source:
| | |
| ---------------- | --------------------- |
| **ID** | `parallel_tool_calls` |
| **Category** | Optimization |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | Low |
Controls the agent’s request-level preference for parallel (multiple per turn) tool calls, and whether the local tool scheduler runs a batch concurrently.
Most providers emit several tool calls in a single turn by default, and Everruns runs independent tool calls concurrently. This capability makes that behavior explicit and configurable: turn it up to actively request batching of independent reads and searches, or turn it down to force strictly one tool call at a time.
## Tools
None, this capability only configures the outbound LLM request and the local tool scheduler.
## How It Works
The capability resolves a `mode` into a request-level preference that threads through two places:
1. **The LLM request.** On providers that expose a wire control, the preference is sent on the request:
* **OpenAI** (Chat Completions and Responses), and the OpenAI-compatible **MAI** and **Fireworks** providers, the top-level `parallel_tool_calls` boolean.
* **OpenRouter**: forwarded on the Responses body; ignored by routed providers that do not support it.
* **Anthropic**: `tool_choice.disable_parallel_tool_use` (sent only when the request carries tools).
* **Gemini** and **Bedrock** have no equivalent request control, so nothing is sent. The local scheduler (below) still honors the preference.
2. **The local tool scheduler.** `avoid` forces the scheduler to run the turn’s tool calls strictly sequentially. This applies to **every** provider, so `avoid` is honored even where there is no wire control.
Whether the preference is sent on the wire is gated per provider/model: a driver that cannot express it omits the field rather than risking an API error.
## Config
```json
{
"capabilities": [
{
"ref": "parallel_tool_calls",
"config": { "mode": "prefer" }
}
]
}
```
### Modes
| Mode | Provider request | Local scheduler |
| ------------------ | ---------------------------------------------- | ------------------------------------- |
| `prefer` (default) | Request parallel tool calls where supported | Concurrent (class-aware, the default) |
| `avoid` | Ask for one tool call per turn where supported | Serialized |
| `none` | Omit, provider default | Concurrent (class-aware, the default) |
When the capability is enabled without an explicit `mode`, the default is `prefer`. `none` is equivalent to not enabling the capability; it is useful to neutralize a preference inherited from a parent harness.
**Base** and every harness built on it, plus the deprecated **Generic** and **Worker Base**, enable this capability with `mode: "prefer"` by default.
## Precedence
An explicit `parallel_tool_calls` field set directly on a harness, agent, or session is a lower-level escape hatch and takes precedence over this capability.
## When To Use
* **`prefer`**: workloads that issue many independent reads or searches per turn benefit from batching (faster turns, fewer round-trips).
* **`avoid`**: when tool calls must be observed and applied one at a time, or when a model produces lower-quality parallel batches for your workload.
## Limitations
* **Provider gating.** `prefer` only changes the wire request on providers with a control for it (OpenAI/Anthropic families). Elsewhere providers already parallelize by default, so `prefer` is a no-op on the wire.
* **Durable mode.** A harness/agent-level `mode` other than the default (`prefer`) is applied with full fidelity in the in-process runtime; in durable worker mode, harness/agent capability config falls back to the default, set the mode at the session level, or use the explicit `parallel_tool_calls` field, to override durably. (This matches other config-bearing capabilities.)
## See Also
* [Capabilities](https://everruns.com/docs/features/capabilities/), the extension model this capability plugs into
* [Agentic Loop](https://everruns.com/docs/explanation/agentic-loop/), how the runtime schedules a batch of tool calls
---
# Platform
> Discover, inspect, and manage Everruns resources through the command catalog.
Source:
| | |
| ---------------- | ------------------------------------------------------------------------------------------------ |
| **ID** | `platform` |
| **Category** | Platform |
| **Risk** | High |
| **Tools** | `discover`, `query`, `execute` by default; `everruns` in the session shell with `surface: shell` |
| **Dependencies** | `skills`; `session_file_system` when embedded docs are enabled |
| **Mounts** | `/workspace/docs`, the Everruns documentation, read-only, when embedded docs are enabled |
The Platform capability gives an agent the same catalog-backed command surface as Everruns’ `/mcp` endpoint. Operations come from the server’s registered command inventory, so models can discover current names and schemas instead of guessing them or relying on a separate handwritten API.
Platform Chat includes this capability by default. Other agents and harnesses must be assigned it explicitly. Because `execute` can mutate platform resources, the capability is high-risk and follows the normal admin-only assignment rule.
## Interfaces and built-in skills
Platform Chat uses `{"ref":"platform","config":{"surface":"shell"}}`. This exposes the same operations through `everruns ` in its shell, with progressive `--help`, JSON output, pipes, and workspace redirection. It hides the three tools described below. Omitting `surface` retains those tools. Both interfaces use the same command authorization and documentation mounts.
Workspace builds also ship three read-only skills with this capability:
| Skill | Purpose |
| -------------------------- | ------------------------------------------------------------------------------------------------ |
| `everruns` | Product concepts, Cloud and self-hosted entry points, and documentation links |
| `everruns-cli` | Command usage, scripting examples, and differences between installed CLI, session shell, and MCP |
| `everruns-agent-authoring` | Agent design, configuration, dependencies, and behavioral verification |
The capability enables [Agent Skills](https://everruns.com/docs/capabilities/agent-skills/) for discovery and activation. Use `list_skills` and `activate_skill`; full instructions load on demand from `/workspace/.agents/skills//SKILL.md`. Follow the current session interface when a skill shows an external MCP example. Adding skills grants no additional command permissions. Like embedded docs, these product files are included from the repository at build time; standalone crate builds without the repository skills tree omit them.
The external [MCP endpoint](https://everruns.com/docs/features/mcp/#skills-for-mcp-clients) serves these three skills plus `everruns-mcp`. Rust development guidance is maintained separately in the installable `everruns-framework` skill.
## Tools
### `discover`
Search for operations by name, category, description, or schema terms. Results include command metadata, read-only decision, and output-shape hints. Searches with multiple matches omit schemas and return a refinement hint to keep the result compact. A query that exactly matches a command name returns only that command with its schemas and `bash_usage`, a copyable invocation with the exact supported flags. It also includes bounded `output_fields` paths for building `jq` filters without guessing field names. If expanded schemas would make the result too large, the response omits them with a notice while retaining the authoritative scripting summaries. Use `all: true` only when you truly need to list the entire scriptable catalog, not for a task-specific lookup.
```json
{ "query": "models" }
```
Once you find a command, discover its exact name before invoking it:
```json
{ "query": "create_agent" }
```
For commands with a `cli` spelling, use command-tree `--help` as directed by discovery. Flat-only builtins have no `--help`; use their `bash_usage` and returned schema. Pass array and object values as JSON text, for example `--capabilities '[{"ref":"mcp:..."}]'`.
Unknown flags are rejected before a command runs. This prevents misspelled security-sensitive options, such as an authentication flag, from being silently ignored during a mutation.
### `query`
Run a bounded Bashkit script with only read-only Everruns commands available as builtins. It supports pipes, variables, loops, conditionals, and `jq`.
```json
{ "commands": "list_models | jq '.data[] | {id, model_id, display_name}'" }
```
Commands with mutations or open-world side effects are not available in `query`. Use it to inspect current state and validate changes.
### `execute`
Run a bounded Bashkit script with the full scriptable command catalog. Use it for requested create, update, delete, and other mutating operations.
```json
{
"commands": "create_agent --name 'support-agent' --system_prompt 'Help users.' --default_model_id 'model_...'"
}
```
`execute` is not transactional. If a later command in a script fails, earlier commands may already have succeeded. Inspect the resulting state with `query` before retrying.
MCP server command results include both their public resource `id` and a derived `capability_ref` in the `mcp:` form accepted by Agent capability configuration. Capture JSON results and use `jq` to pass dependent IDs or capability references to later commands in the same script. No separate MCP attachment operation is needed.
## Scope and authorization
Platform tools are always bound to the current session’s organization. Their schemas do not accept `organization_id`, and an injected override is rejected. The server resolves the session’s human owner for every distributed call and applies that caller’s normal command permissions. Attaching this capability does not grant authority the owner does not already have.
## Autonomous workflows
For recurring autonomous work, create an Agent and an Agent Trigger. Do not use a schedule on the Platform Chat session: that would wake the management chat, not provision an independently owned worker workflow.
Credentials are not transferred from Platform Chat session secrets into a new Agent. Configure integrations through their supported Agent-scoped credential or connection flow; do not paste credentials into command scripts.
## Platform documentation
When the build embeds the product documentation, this capability mounts it at `/workspace/docs` as a read-only virtual filesystem, served from memory with no database writes per session. Agents browse it with the standard file tools (`read_file`, `list_directory`, `grep`) or with `cat`, `ls`, and `grep` through Bashkit Shell.
Key sections:
* `/workspace/docs/getting-started/`, introduction, concepts, architecture
* `/workspace/docs/features/`, SDK, CLI, UI, events, harnesses, capabilities
* `/workspace/docs/capabilities/`, per-capability reference
* `/workspace/docs/integrations/`, external integrations (Slack, Daytona, etc.)
* `/workspace/docs/advanced/`, budgets, compaction, embedding, network access
* `/workspace/docs/sre/`, environment variables, runbooks
## See also
* [Platform Chat harness](https://everruns.com/docs/built-ins/harnesses/platform-chat/)
* [MCP](https://everruns.com/docs/features/mcp/)
* [Agent Triggers](https://everruns.com/docs/features/agent-triggers/)
* [Platform Management](https://everruns.com/docs/capabilities/platform-management/), the removed predecessor
---
# Platform Management (removed)
> Removed capability. Its management tools are superseded by the catalog-backed Platform capability.
Source:
> **This capability has been removed.** Use the [Platform capability](https://everruns.com/docs/capabilities/platform/) instead. Agents and harnesses that still reference `platform_management` keep running, but the capability contributes no tools and no system prompt.
| | |
| --------------- | -------------------------------------------------------------- |
| **ID** | `platform_management` |
| **Category** | Platform |
| **Status** | Retired |
| **Tools** | None |
| **Replacement** | [`platform`](https://everruns.com/docs/capabilities/platform/) |
## Why it was removed
Its tools were hand-written alongside the API rather than derived from it, so they covered only harnesses, agents, apps, and sessions, and their schemas drifted as the platform grew. The `platform` capability exposes the same surface through `discover`, `query`, and `execute` over the server’s registered command catalog, which is the same inventory behind Everruns MCP, so it cannot drift.
## What to do
Replace the capability on any agent or harness that still lists it:
1. Open the agent or harness. A removed capability is flagged in its capability list.
2. Remove `platform_management` and add `platform`. Both are high-risk, so an admin performs the change.
3. Prompts that named the old tools should name the new flow instead: find the command with `discover`, read state with `query`, mutate with `execute`.
## Tool mapping
| Removed tool | Replacement |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `read_capabilities` | `list_capabilities` / `get_capability` |
| `read_harnesses` | `list_harnesses` / `get_harness` |
| `manage_harnesses` | `create_harness`, `update_harness`, `delete_harness`, `copy_harness` |
| `read_agents` | `list_agents` / `get_agent` |
| `manage_agents` | `create_agent`, `update_agent`, `delete_agent` |
| `read_apps` | `list_apps` / `get_app` (read-only archive); `list_agent_channels` / `get_agent_channel` for live endpoints |
| `manage_apps` | No equivalent. Apps are retired; endpoints belong to an agent. |
| `manage_app_channels` | `create_agent_channel`, `update_agent_channel`, `delete_agent_channel`, `publish_agent_channel`, `unpublish_agent_channel` |
| `read_sessions` | `list_sessions` / `get_session` |
| `manage_sessions` | `create_session`, `delete_session` |
| `session_send_message` | `create_message` |
| `session_read_messages` | `list_messages` |
| `session_context_report` | `get_session_context_report` |
| `session_read_response` | No equivalent. It blocked until the turn finished; poll `list_messages` instead. |
Run `discover` for the exact current schema of any command in that table rather than assuming the flags.
## Platform documentation
The embedded documentation mount at `/workspace/docs` moved to the [Platform capability](https://everruns.com/docs/capabilities/platform/) unchanged.
## See also
* [Platform](https://everruns.com/docs/capabilities/platform/), the replacement
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Poppy Personal Agent
> Let an agent talk to companies' agents for the person it works for, over the Personal Agent Protocol (Poppy).
Source:
| | |
| ---------------- | -------------- |
| **ID** | `poppy_client` |
| **Category** | Integrations |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | Medium |
The [Personal Agent Protocol](https://personalagentprotocol.org) (“Poppy”, draft 0.1) lets a person’s own agent do business with a company: find it, start a conversation with its agent, and hand off to a person there. With this capability an Everruns agent is that personal agent. Any company that publishes `/.well-known/poppy.json` on its domain works, with nothing to register.
This page covers the personal side. To make an Everruns agent the company that personal agents talk to, and for how both ends are tested, see [Personal Agent Protocol (Poppy)](https://everruns.com/docs/features/poppy/).
## When to enable
* A personal assistant such as Platform Chat, where people ask the agent to check on an order, a booking or a support case with a company.
Leave it off for agents that should not contact outside companies.
## Configuration
None.
```json
{ "ref": "poppy_client", "config": {} }
```
## Tools
| Tool | What it does |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `poppy_discover` | Looks up a company by domain and says what it offers |
| `poppy_send` | Sends a message to the company’s agent and waits briefly for the reply. The first message starts the conversation; `new_conversation` starts another |
| `poppy_read` | Reads replies that arrived since the last answer |
| `poppy_handoff` | Asks the company for a person |
| `poppy_close` | Ends the conversation |
## Identity and privacy
Every company sees the deployment as one personal agent named “Everruns”. Its client metadata is published at `/v1/poppy/agent.json` and its public key at `/v1/poppy/jwks.json`, under the API base URL. Each person gets a different random User ID at each company, so two companies cannot tell they talk to the same person. Nothing else about the person, the organization or the conversation is sent, beyond the messages themselves.
Keys and Session Tokens stay on the server, encrypted at rest. The agent’s tools only see what the company answered. Requests go through the organization’s egress allowlist.
## In the Framework
A Framework app is its own personal agent. Turn on the `poppy` feature, give the agent `PoppyPersonalAgent`, and serve its two documents at the HTTPS address you named:
```rust
use everruns::poppy::{PoppyKey, PoppyPersonalAgent};
let poppy = PoppyPersonalAgent::new("https://agent.example.com", PoppyKey::generate()?);
let metadata = poppy.metadata("My assistant"); // serve at /v1/poppy/agent.json
let jwks = poppy.jwks(); // serve at /v1/poppy/jwks.json
let agent = Agent::builder()
.name("personal-agent")
.model(model)
.capability(poppy)
.build()?;
```
The tools are the same. The app keeps the person’s User IDs, keys and Session Tokens in memory, so a restart is a new person to every company. See [the example](https://github.com/everruns/everruns/tree/main/examples/poppy-personal-agent).
## Limits
* Sessions are signed out: the person does not sign in to the company yet, so a company that needs an account says so, and the agent tells the person.
* Company APIs and operations are not used; only the conversation.
---
# Prompt Caching
> Turns on provider prompt caching for Anthropic, OpenAI, Gemini, Bedrock, OpenRouter, and Mistral requests, and records the cache mode on each llm.generation event.
Source:
| | |
| ---------------- | ---------------- |
| **ID** | `prompt_caching` |
| **Category** | Optimization |
| **Tools** | None |
| **Features** | None |
| **Dependencies** | None |
Prompt caching lets a provider reuse the unchanged start of a request (system prompt, tool definitions, earlier conversation) instead of processing it again on every model call. Cached input is usually cheaper and faster. This capability sets the request options each driver needs. It adds no tools and no prompt text.
Drivers that support caching translate the setting into their provider’s request fields. A driver or model without support ignores it, and the request proceeds unchanged.
The [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) harness includes it.
## What each provider gets
| Provider | With `strategy: auto` (default) | With `strategy: explicit` |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Anthropic | `cache_control` breakpoints on the system prompt, the last tool definition, and the two most recent stable conversation messages, four in total | Breakpoints on the system prompt and the last tool definition only, so the conversation is never written to cache |
| OpenAI | A `prompt_cache_key` derived from the session (or agent, harness, or organization), model, instructions, and tools. On models whose profile supports cache options, also `prompt_cache_options` with mode `implicit` and a 30 minute TTL | `prompt_cache_key`, and on supporting models `prompt_cache_options` with mode `explicit`: the instructions move into a developer message marked as the cache breakpoint, so only that prefix is written to cache |
| Gemini | The provider’s default behavior, such as implicit caching on supported models | Same as `auto` |
| Bedrock (Claude, Nova) | `cachePoint` blocks after the system prompt, after the tool definitions (Claude only; Nova does not cache tools), and on the two most recent messages | `cachePoint` blocks after the system prompt and tools only |
| OpenRouter, Claude models | A top-level `cache_control`, which turns on Anthropic’s automatic caching through OpenRouter. Other OpenRouter models keep their vendor’s implicit caching | Nothing extra: automatic caching cannot exclude the conversation |
| Mistral | A `prompt_cache_key` derived from the session (or agent, harness, or organization) and model, so requests sharing a prefix reach the same cache | Same as `auto` |
On Gemini, setting `gemini_cached_content` sends that existing cached content resource in the request’s `cachedContent` field.
On OpenAI with the explicit mode, the driver replays the full transcript instead of chaining with `previous_response_id`, so the developer prefix is not appended again on each continuation.
Use `explicit` when many short, unrelated sessions share long instructions or tools, such as a support agent: caching every conversation would cost more in cache writes than it saves in reads.
## Tool changes during a session
Tool definitions sit at the front of the cached prompt, so a tool list that changes mid-session (a tool revealed by [Tool Search](https://everruns.com/docs/capabilities/tool-search/), for example) used to throw the whole cache away. Sessions now record their tool list on the first model call and each later change in the conversation. On Claude Opus 5.5, Opus 5, Opus 4.8, Sonnet 5.5, Haiku 5.5 and Fable 5.1 through the Claude API, and on GPT-6 Astra, Sol, Luna and 6.1 Sol and GPT-5.6 Sol, Terra and Luna through the OpenAI API, the request keeps the first tool list and sends each change in the conversation, so the cache survives it. GPT-5.5 and GPT-5.4 keep the cache when tools are added; a revealed tool there still costs one cache write. Instructions a capability adds when it is switched on mid-session travel the same way on those models: the request keeps the first system prompt and sends the new instructions in the conversation. Other models and endpoints receive the current tool list, which costs one cache write per change. A tool that goes away there (a capability switched off, for example) stays in the list while the cache is warm and nothing else changes, and a call to it is refused; it leaves with the next change, or once the session has been idle longer than the cache retention.
## Retention
`retention` sets how long the provider keeps a cached prefix after its last use. Each driver rounds it to the nearest value its provider supports.
| Provider | `default`, `5m` | `30m` | `1h` |
| ------------------------- | ---------------- | ---------------- | ---------------------------------------- |
| Anthropic | 5 minutes | 5 minutes | 1 hour (`ttl: "1h"` on every breakpoint) |
| Bedrock, Claude models | 5 minutes | 5 minutes | 1 hour (`ttl` on every cache point) |
| OpenRouter, Claude models | 5 minutes | 5 minutes | 1 hour (`ttl: "1h"` on `cache_control`) |
| OpenAI with cache options | 30 minutes | 30 minutes | 30 minutes |
| Others | Provider default | Provider default | Provider default |
A longer retention costs more per write: Anthropic bills a 1 hour cache write at 2x the input rate instead of 1.25x, and cost estimates include that. Choose `1h` for agents whose turns are often more than five minutes apart, such as agents that wait on a person or run on a schedule, where a 5 minute cache would have expired before the next turn.
## Configuration
```json
{
"ref": "prompt_caching",
"config": { "strategy": "auto", "retention": "1h" }
}
```
| Field | Default | Description |
| ----------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `strategy` | `auto` | `auto` lets each driver pick its provider behavior. `explicit` caches only the stable prefix (instructions, and tools on Anthropic) on providers that support it. |
| `retention` | `default` | `default`, `5m`, `30m`, or `1h`. Rounded to what the provider supports. |
| `gemini_cached_content` | none | An existing Gemini cached content resource name, `cachedContents/{id}` |
## Observability
Each `llm.generation` event records the request in `request_options.prompt_cache`: whether caching was enabled, the strategy, and a `provider_mode` naming what the driver did, and the `retention` it requested after rounding. The modes are `cache_control` (Anthropic), `implicit`, `explicit`, or `prompt_cache_key` (OpenAI), and `implicit` or `cached_content` (Gemini). Cache read and write token counts, when the provider reports them, appear in the event’s token usage.
In a session’s Trace view, each model call shows its prompt split into cache read, cache write and uncached tokens. When a call reuses less than half of what the previous call left cached, the trace marks a cache reset and names the likely cause: the model, the tool definitions, the system prompt or earlier messages changed, or the session sat idle longer than the cache retention. A long prompt sent with caching off is marked as one that caching would help. A call that changed the tool list lists the tools added, updated or removed.
## See also
* [Platform Chat harness](https://everruns.com/docs/built-ins/harnesses/platform-chat/)
* [OpenTelemetry](https://everruns.com/docs/observability/opentelemetry/), which exports prompt-cache token counts per call
* [Tool Search](https://everruns.com/docs/capabilities/tool-search/), another request-level optimization
---
# Prompt Canary Guardrail
> Streaming output guardrail that withholds the assistant message when the model echoes the first sentence of its system prompt back to the user.
Source:
| | |
| ---------------- | ------------------------- |
| **ID** | `prompt_canary_guardrail` |
| **Category** | Safety |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | Low |
Detects naive system-prompt leakage during streaming. At the start of each assistant message, the capability extracts the first qualifying sentence of the assembled system prompt and uses it as a canary needle. If the model’s accumulated output ever contains that needle, streaming aborts, the client is told to discard everything it accumulated, and a canned refusal becomes the persisted assistant message. The original tokens are never stored or replayed on subsequent turns.
This is intentionally narrow: a single substring match against one normalized needle. It catches obvious prompt-extraction attempts (“repeat your instructions”, “what are you told to do?”) without trying to be a general-purpose data-loss-prevention layer.
## Tools
None, this capability hooks the streaming output via the capability framework’s output-guardrail extension point.
## How It Works
1. **Arming**: At the start of each assistant message stream, the capability walks sentence boundaries in the assembled system prompt and picks the first sentence whose normalized form is **≥ 30 characters**. This skips short generic openers like “You are a helpful assistant.” in favor of an agent-specific identifying sentence
2. **Normalization**: Both sides of the comparison are lowercased, and runs of whitespace are collapsed to a single space, so the canary survives reformatting (extra spaces, capitalization drift, line wrapping)
3. **Streaming check**: After every text delta, the canary runs a substring scan over the accumulated assistant text. The check is synchronous and cheap, no I/O, no allocations beyond the normalized buffer
4. **Block on match**: When the needle appears in the accumulated output, the stream is aborted, the offending pending delta is suppressed, and `output.message.replaced` is emitted with `reason_code: "system_prompt_leak"`. The replacement text becomes the persisted assistant message
When the system prompt is too short or too generic to produce a needle ≥ 30 characters, the capability declines to arm for that stream and is a no-op.
## Streaming Timeline With a Trip
```plaintext
output.message.started
│
▼
output.message.delta ← model text accumulating ("Sure, my instructions are: …")
│
▼ (canary trips on the next delta — pending text is suppressed)
output.message.replaced
│ (UI discards what it accumulated, shows replacement)
▼
output.message.completed ← persisted message body = replacement
```
## Configuration
### Default
```json
{
"capabilities": ["prompt_canary_guardrail"]
}
```
Replacement text defaults to:
> \[Response withheld: the model attempted to reveal protected instructions.]
### Custom replacement
```json
{
"capabilities": [
{
"ref": "prompt_canary_guardrail",
"config": { "replacement": "I can't share my system instructions." }
}
]
}
```
## When To Enable
Use this capability when:
* You ship agents with proprietary, brand-specific, or compliance-relevant system prompts that should not be revealed verbatim to end users
* You want a cheap, deterministic defense against the most common prompt-extraction prompts
* You can tolerate a generic refusal in place of the model’s response when the canary trips
Do **not** rely on this for:
* General-purpose data-loss prevention (PII, secrets in tool output, etc.), those need their own surfaces
* Defense against paraphrased or summarized prompt leaks, the canary only catches verbatim or near-verbatim copies of the first sentence
* Tool output or extended-thinking surfaces, the canary only inspects assistant text
## Limitations
* **Verbatim-only**: a model that paraphrases (“My role is to act as an internal pricing oracle…”) will not trip the canary
* **First-sentence-only**: if the model leaks a *later* sentence of the system prompt, the canary won’t catch it. Consider rewriting prompts so the most identifying claim is the opening sentence
* **No partial matching**: the substring must appear in full. Truncated leaks (cut off mid-sentence) pass through
## See Also
* [Events](https://everruns.com/docs/features/events/), the streaming event protocol that carries `output.message.replaced`
* [Capabilities](https://everruns.com/docs/features/capabilities/), the extension model these guardrails plug into
---
# Sandbox Fleet
> Create and operate multiple explicitly addressed resource Sandboxes without changing a session's primary Sandbox.
Source:
| | |
| -------------- | ---------------------------------------------------------------------------- |
| **ID** | `sandbox_fleet` |
| **Category** | Sandboxes |
| **Tools** | 8 |
| **Dependency** | [`session_storage`](https://everruns.com/docs/capabilities/session-storage/) |
Sandbox Fleet gives an Agent provider-neutral tools for parallel or isolated compute. Daytona backs the capability today, but the model sees only Everruns logical Sandbox IDs. Provider resource IDs, credentials, and translation stay in the trusted control plane.
These are **resource Sandboxes**. They never replace the session’s primary Sandbox: ordinary shell and file tools continue to use the primary Sandbox configured by the Harness and Agent.
| Tool | Purpose |
| -------------------- | ------------------------------------------------------------------ |
| `sandbox_create` | Create a resource Sandbox and return its session-scoped logical ID |
| `sandbox_exec` | Run a command in an explicitly named Sandbox |
| `sandbox_read_file` | Read a file from an explicitly named Sandbox |
| `sandbox_write_file` | Write a file in an explicitly named Sandbox |
| `sandbox_list` | List the current session’s resource Sandboxes |
| `sandbox_inspect` | Inspect one resource Sandbox without exposing provider bindings |
| `sandbox_checkpoint` | Copy its workspace into durable session file storage |
| `sandbox_manage` | Stop or delete it |
Every operation after creation requires `sandbox_id`. An ID from another session is rejected. Delete resource Sandboxes when the work is complete.
The older [`daytona`](https://everruns.com/docs/capabilities/daytona/) capability remains available for existing Agents and provider-specific operations. New provider-neutral fleet workflows should use `sandbox_fleet`.
---
# Self-Budget
> Prompt-only guidance for agents to reason about a user-requested indicative budget using session usage data. Distinct from the platform-enforced `budgeting` capability.
Source:
| | |
| ---------------- | -------------- |
| **ID** | `self_budget` |
| **Category** | System |
| **Features** | *(none)* |
| **Tools** | *(none)* |
| **Included in** | Worker harness |
| **Dependencies** | None |
Teaches the agent how to self-manage an **indicative** budget that the user mentions in conversation, for example, “you have $7” or “keep this under 20k tokens”. The capability contributes prompt text only; it adds no tools and performs no enforcement.
For platform-enforced budgets (authoritative limits that pause or stop sessions automatically), use the separate [`budgeting`](https://everruns.com/docs/capabilities/budgeting/) capability.
## How It Works
`self_budget` is prompt-only. When the capability is enabled the agent’s system prompt gets a “Self-Managed Budget” section that explains:
* The self-budget is an **agent-managed soft target**, not a hard limit.
* Session usage metadata (exposed via `get_session_info`) is the source of truth for current spend.
* The agent decides when to start tracking, when to re-check, and when to stop.
* As the target tightens, the agent should adapt, shorter outputs, fewer retries, narrower exploration, fewer redundant tool calls.
* The agent avoids claiming exact cost certainty when only token counts or partial pricing are available.
* The agent distinguishes between platform-enforced budgets and user-requested indicative budgets when reporting progress.
There is no `self_budget` tool. Usage data comes from `get_session_info`, which is provided by the [`session`](https://everruns.com/docs/capabilities/session/) capability (bundled by default in the Worker harness).
## Self-Budget vs Budgeting
| Aspect | `self_budget` | `budgeting` |
| ----------- | ----------------------------------- | --------------------------------------------------- |
| What it is | Agent-managed soft target | Platform-enforced limit |
| Tools | None | `check_budget` |
| Enforcement | None (prompt guidance only) | Session is paused/stopped automatically |
| Data source | `get_session_info` cumulative usage | Budgets table / ledger |
| Use case | User says “you have $7” in chat | Org/session has a configured budget in the platform |
The two capabilities are non-conflicting and can run together. The Worker harness includes both.
## Related
* [Budgeting](https://everruns.com/docs/capabilities/budgeting/), platform-enforced budgets with the `check_budget` tool
* [Session](https://everruns.com/docs/capabilities/session/), provides `get_session_info`, the usage data source
* [Budgets](https://everruns.com/docs/advanced/budgets/), full budgeting system documentation
---
# Session
> Inspect and update the current session's metadata, including its ID, title, agent name, locale, and usage.
Source:
| | |
| ---------------- | --------- |
| **ID** | `session` |
| **Category** | Session |
| **Features** | None |
| **Dependencies** | None |
Tools to read and update session metadata like title and agent information.
## Automatic titles
For naming outside the agent loop, enable [Automatic session titles](https://everruns.com/docs/capabilities/automatic-session-titles/) and leave `auto_title` off. Explicit tool writes take priority over automatic titles, including same-text writes.
Agent-directed title maintenance is opt-in. Set `auto_title` to `true` in the capability configuration to have the agent create a concise 3–7 word title before handling the first substantive request. The title is a required pre-work update. The agent updates it later, also before other work or a response, only when the conversation’s primary theme materially changes, not for minor follow-ups or subtopics. Title writes update session metadata and do not count as project or workspace file changes.
Title changes emit `session.title.updated` with the previous and new title. A repeated write of the current title is a no-op and emits no event.
## Tools
### `get_session_info`
Get current session metadata.
Returns: session ID, title, locale, agent name, and token usage (input, output, cache read, cache creation, and total tokens) when available.
### `write_session_title`
Update the session title.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ----------------- |
| `title` | string | yes | New session title |
## See Also
* [Storage](https://everruns.com/docs/capabilities/session-storage/), persist data within the session
* [Schedules](https://everruns.com/docs/capabilities/session-schedules/), schedule future tasks
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Schedules
> Schedule one-shot and recurring cron-based tasks within a session.
Source:
| | |
| ---------------- | ----------------------------------- |
| **ID** | `session_schedule` |
| **Category** | Core |
| **Features** | `schedules` (enables Schedules tab) |
| **Dependencies** | None |
Schedule future tasks within the current session. Supports one-shot (run once at a specific time) and recurring (cron expression) schedules.
## Tools
### `create_schedule`
Create a new scheduled task.
| Parameter | Type | Required | Description |
| ----------------- | ------ | ----------- | ---------------------------------------- |
| `message` | string | yes | The message/task to execute |
| `scheduled_at` | string | conditional | ISO 8601 datetime for one-shot schedules |
| `cron_expression` | string | conditional | Cron expression for recurring schedules |
Provide either `scheduled_at` or `cron_expression`, not both.
### `cancel_schedule`
Cancel an active schedule.
| Parameter | Type | Required | Description |
| ------------- | ------ | -------- | ---------------------------- |
| `schedule_id` | string | yes | ID of the schedule to cancel |
### `list_schedules`
List all schedules for the current session.
## Recurring background tasks
Recurrence is built on schedules, there is no separate “recurring task” object to configure. A recurring (`cron_expression`) schedule either delivers a scheduled turn to the session, or, when paired with a background **monitor** task, runs a probe on each fire and records the result on the task’s thread. This composition (recurring schedule + monitor) is the supported way to run periodic background work, there is no separate recurring-task primitive to configure.
## Notes
* Maximum 5 active schedules per session
* Cron uses standard 5-field format (minute, hour, day, month, weekday), with weekdays counted 0-6 from Sunday (7 is also Sunday, so `5-7` is Friday through Sunday and `0 18 * * 0` is Sundays at 18:00), read in the schedule’s timezone
* A recurring schedule fires at most once every 5 minutes by default. Admission rejects invalid cron and expressions with fewer than two future occurrences, because their minimum interval cannot be verified. Use a one-shot schedule for a single future run
* An invalid cron expression or timezone, or a schedule over a limit, fails the tool call with the reason, so the agent can correct it
* Scheduled messages are sent to the session as if the user sent them
* Use [Current Time](https://everruns.com/docs/capabilities/current-time/) to determine “now” before scheduling
## See Also
* [Current Time](https://everruns.com/docs/capabilities/current-time/), get current time for scheduling context
* [Session](https://everruns.com/docs/capabilities/session/), session metadata
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Session Storage
> Session-scoped key/value storage and encrypted secret storage.
Source:
| | |
| ---------------- | -------------------------------------------- |
| **ID** | `session_storage` |
| **Category** | Storage |
| **Features** | `secrets`, `key_value` (enables Storage tab) |
| **Dependencies** | None |
Two storage mechanisms scoped to the current session:
* **Key/Value store**: plain-text storage for general data
* **Secret store**: AES-256-GCM encrypted storage for sensitive data
## Tools
### `kv_store`
Manage plain-text key/value pairs.
| Parameter | Type | Required | Description |
| ----------- | ------ | ----------- | ----------------------------------- |
| `operation` | enum | yes | `set`, `get`, `delete`, or `list` |
| `key` | string | conditional | Required for `set`, `get`, `delete` |
| `value` | string | conditional | Required for `set` |
### `secret_store`
Manage encrypted secrets. Values are encrypted at rest and write-only: an agent can store, list and delete secrets, but no tool reads a value back.
| Parameter | Type | Required | Description |
| ----------- | ------ | ----------- | ------------------------------- |
| `operation` | enum | yes | `set`, `delete`, or `list` |
| `name` | string | conditional | Required for `set` and `delete` |
| `value` | string | conditional | Required for `set` |
## Notes
* Data is session-scoped, no cross-session access
* `set` uses upsert semantics (overwrites existing keys)
* Secret operations require `SECRETS_ENCRYPTION_KEY` to be configured
* `list` returns keys/names only (not values) for secrets
* The Storage tab can create, replace, and delete values, but never reads a value back
* A session secret is available only to that session. It does not follow an Agent Trigger that creates a session per invocation.
* No secret value ever reaches the model. `secret_store get` answers the same refusal for every name. Capabilities that need a key (GitHub, Brave Search, sandboxes and others) read their own secret on the server. A key the agent’s shell sends to an HTTP API is a bound secret, described next; an MCP tool-parameter credential goes on the Agent’s **Credentials** tab.
* A **bound secret** (stored with a URL to send it to) is write-only: `secret_store list` shows its name, URL and how to use it, while `get`, `set` and `delete` refuse it. The Bashkit shell uses it as `$NAME` in HTTP requests to that URL; see [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/#api-keys-the-agent-never-sees). To give every session and trigger of an agent the same key, add it as an **HTTP API** credential on the Agent’s **Credentials** tab.
## See Also
* [Session](https://everruns.com/docs/capabilities/session/), session metadata
* [File System](https://everruns.com/docs/capabilities/file-system/), file-based storage alternative
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Slack
> Deploy an Everruns agent as a Slack bot through an Agent-owned channel, and let it react, update messages, upload files, and look up users with the slack capability.
Source:
Start with [Build Agents for Slack](https://everruns.com/docs/integrations/slack/) for examples, a walkthrough, and setup. This page covers configuration and the Slack actions reference.
Everruns connects an Agent to Slack in two parts:
* A **Slack channel** owned by the Agent receives Slack Events API requests, routes each conversation to a session, and posts the Agent’s responses back to Slack. This is how users talk to the Agent in Slack.
* The **`slack` capability** lets the Agent act in that conversation beyond replying: reactions, message updates, file uploads, and user lookups. See [Slack actions](#slack-actions).
## What You Get
* **Conversational agents in Slack**: Users interact with the Agent in channels, threads, direct messages, or Slack’s agent pane.
* **Session routing**: Conversations map to sessions by thread, channel, or user.
* **Secure webhooks**: Everruns verifies requests with Slack’s signing secret.
* **Async responses**: Everruns acknowledges Slack immediately and posts the Agent’s response when it is ready.
* **Per-channel Slack bots**: Each Slack channel has its own Slack app, credentials, identity, and lifecycle.
## Before You Start
* Create an active Agent.
* Give Everruns a public HTTPS origin. Set `PUBLIC_APP_URL` to that origin and restart Everruns.
* Ask a Slack workspace administrator for permission to install an app.
* An organization administrator connects your Slack workspace once; see [Connect a Slack Workspace](#connect-a-slack-workspace).
Slack cannot verify `localhost`. For local development, expose Everruns through a public HTTPS tunnel before you create the Slack app.
## Connect a Slack Workspace
Every Agent you put in Slack gets its own Slack app, with its own name and its own entry in Slack’s Agents menu. Everruns creates those apps for you in your workspace. To do that it needs a Slack configuration token for the workspace, which an organization administrator adds once:
1. Open **Settings** > **Slack workspaces**.
2. Select **Open api.slack.com/apps**, signed in to the workspace you want your Agents in.
3. Scroll to **Your App Configuration Tokens**, select **Generate Token**, and pick the workspace.
4. Copy the **Refresh Token** (it starts `xoxe-1-`) and paste it into Everruns. Do not copy the access token above it.
Everruns connects the workspace as soon as you paste the token, rotates it immediately, and stores only the encrypted replacement. You do not need to repeat this for each Agent.
The token can create and change any Slack app in that workspace, and your Slack workspace may require an administrator to generate it. An organization can connect more than one workspace.
If your deployment has a Slack manager app (see [Slack Manager App](https://everruns.com/docs/sre/environment-variables/#slack-manager-app)), **Settings** > **Slack workspaces** shows **Connect with Slack** instead: approve Everruns in Slack, pick the workspace, and nothing is copied or pasted. **Use a configuration token instead** keeps the steps above available. Each Agent’s app still asks for its own Slack approval when you add it.
**Disconnecting a workspace** does not affect Agents already in it: each has its own Slack app and keeps working. Until you connect the workspace again, you cannot add Agents to it or update their Slack apps.
## Connect an Agent to Slack
### 1. Create a Slack Channel
1. Open the Agent.
2. Select **Integrations**.
3. Select **Add channel**.
4. Select **Slack**.
5. If your organization has connected more than one Slack workspace, choose one.
6. Choose the session strategy. Leave the Slack credentials empty.
7. Select **Save channel**.
Everruns opens the channel editor after it saves the channel.
### 2. Publish the Channel
Select **Publish** in the channel editor. Publishing makes only this channel live.
Publish before you create the Slack app. The generated Slack manifest contains the channel’s Request URL, and Slack verifies that URL when it creates the app.
### 3. Add the Agent to Slack
Select **Add to Slack** in the channel editor. Everruns creates the Agent’s Slack app in the chosen workspace and opens Slack’s consent screen with that workspace selected. Approve it, and Everruns stores the app’s signing secret, bot token, and workspace ID on this channel. The channel then shows **Live in Slack** with an **Open in Slack** link.
If your Slack workspace requires administrators to approve apps, Slack sends an approval request instead of installing straight away.
Deployments without `SECRETS_ENCRYPTION_KEY` do not create Slack apps. If Everruns reports that one-click setup is unavailable:
1. Return to the Agent’s **Integrations** tab.
2. Expand the live Slack channel.
3. Select **Create Slack app**.
4. Review Slack’s pre-filled manifest and select **Create**.
5. Install the app to your workspace.
6. Copy the **Signing Secret** from **Basic Information**.
7. Copy the **Bot User OAuth Token** (`xoxb-...`) from **OAuth & Permissions**.
8. Select **Configure** on the channel, enter both values, and select **Save**.
The manifest already contains the bot scopes, event subscriptions, interactivity URL, and canonical channel Request URL:
```text
https://your-everruns-host/api/v1/channels/{channel_id}/slack/events
```
Do not replace `{channel_id}` with an Agent ID or an App ID.
### 4. Invite and Test
1. In Slack, enter `/invite @botname` in a channel.
2. Mention the bot or send it a direct message.
3. Return to the Agent’s **Integrations** tab and expand the Slack channel.
4. Confirm that the setup checklist records the first message.
## Configure a Slack App Manually
Use this flow only when you cannot use **Add to Slack** or **Create Slack app**.
1. Create and publish a Slack channel from the Agent’s **Integrations** tab.
2. Copy its Request URL from the expanded channel row.
3. In [Slack API Apps](https://api.slack.com/apps), select **Create New App** > **From scratch**.
4. Add these bot token scopes under **OAuth & Permissions**:
* `chat:write`
* `channels:history`
* `groups:history`
* `im:history`
* `mpim:history`
* `app_mentions:read`
* `users:read`
5. Add these bot events under **Event Subscriptions**:
* `message.channels`
* `message.groups`
* `message.im`
* `message.mpim`
* `app_mention`
6. Paste the channel Request URL into Slack’s **Request URL** field.
7. Install the Slack app to your workspace.
8. Copy the signing secret and bot token into the channel’s **Configure manually** fields.
9. Save the channel.
## Existing Installs
Existing Slack installs that use `/v1/apps/{app_id}/…` URLs continue to work. Everruns keeps those routes as permanent compatibility aliases. New installs use `/v1/channels/{channel_id}/…`, which is the canonical channel-owned form.
## Channel Configuration
| Field | Required | Description |
| ----------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `signing_secret` | Before use | Slack app signing secret for HMAC-SHA256 verification. It can be empty while you create the channel, but the channel rejects all Slack requests until it is set. |
| `bot_token` | Before use | Bot User OAuth Token (`xoxb-...`) for sending responses. It can be empty while you create the channel. |
| `channel_id` | No | Restrict the channel to one channel, such as `C0123456789`. |
| `team_id` | No | Slack workspace ID. |
| `session_strategy` | No | `per_thread` by default, `per_channel`, or `per_user`. |
| `agent_surface_enabled` | No | `false` by default. Also serve Slack’s agent pane; see [Agent Surface](#agent-surface). |
## Agent Surface
Slack apps can also appear as an **agent**, a dedicated assistant pane separate from channel conversations. Enabling `agent_surface_enabled` adds this pane without changing channel replies.
The generated manifest adds:
* the `features.agent_view` block with an `agent_description` derived from the Agent;
* the `assistant:write` bot scope; and
* the `app_home_opened`, `app_context_changed`, `agent_session_stopped`, and `agent_session_title_changed` bot events.
Enabling the pane on an existing Slack app requires a reinstall because a configuration change cannot grant the new `assistant:write` OAuth scope. Generate a fresh manifest, update the Slack app, and reinstall it. Channel replies continue while you do this.
Session strategy in the pane is always `per_thread`. The configured strategy still applies to channel conversations.
### Streaming Replies
Replies in the agent pane render as the Agent produces them. Channel threads receive one finished message instead of token-by-token updates.
## How the Agent Replies
How the Agent talks in Slack is set on the Agent, not on the channel. See the Agent’s **Communication** setting and [Explicit communication](https://everruns.com/docs/features/explicit-communication/).
| Agent communication | What people see in Slack |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Direct** (default) | Every assistant response is published. Replies stream in the agent pane. |
| **Explicit** | The Agent posts only what it sends with `send_message`. Its other assistant text stays in Everruns as working notes. It can call `no_reply` to choose not to answer. |
With explicit communication, `send_message` posts into the thread that started the turn, as this channel’s bot, at most once per call. The Agent cannot choose another channel or thread. Messages can be up to 12,000 characters of Markdown. Success means Slack accepted the post; the `conversation.message` event carries `platform`, `channel` and `message_ref`. For Slack, `message_ref` is the message timestamp: use it as `timestamp` with `slack_update_message` when the Slack actions capability is enabled. If delivery is uncertain, the error says so; the Agent should not resend blindly because the message may already be visible.
A turn started by a session schedule (the Agent’s `create_schedule`) has no Slack message of its own. Its `send_message` goes to the conversation of the session’s latest Slack message: inline in a direct message, top-level in a `per_channel` channel, and in the thread for a `per_thread` session. A session that has never received a Slack message cannot post.
To run on a clock in a conversation from the start, before anyone has written, give a schedule trigger a Slack target instead: see [Run in a Slack conversation](https://everruns.com/docs/features/agent-triggers/#run-in-a-slack-conversation).
The agent pane shows a working status while the turn runs. Approval cards and task progress appear independently of the Agent’s messages. If a turn ends without anything posted to the thread, or an explicit Agent’s turn fails or is cancelled, Everruns posts one short status line with a link to the session. An explicit Agent that calls `no_reply` chose silence, so its thread gets no status line.
## Session Strategies
| Strategy | Behavior | Tag Pattern |
| ------------- | ---------------------------------------- | -------------------------- |
| `per_thread` | Each Slack thread is a separate session. | `slack:thread:{thread_ts}` |
| `per_channel` | One session serves the channel. | `slack:channel:{channel}` |
| `per_user` | One session serves each Slack user. | `slack:user:{user}` |
Use `per_thread` for most Slack bots.
## How It Works
### Architecture

### Message Flow

**Inbound path:**
1. Slack posts a message event to `/v1/channels/{channel_id}/slack/events`.
2. The channel verifies the signing secret and rejects duplicates.
3. Everruns acknowledges Slack within three seconds.
4. The channel finds or creates a session from the configured strategy.
5. Everruns creates a user message and starts an Agent turn.
**Outbound path:**
6. The Slack delivery dispatcher watches the turn.
7. For a Direct Agent, the RuntimeAgent emits completed output messages and the dispatcher posts them.
8. For an Explicit Agent, `send_message` posts messages directly and returns delivery receipts.
9. Automatic delivery retries transient failures with exponential backoff. `send_message` returns errors without retrying an uncertain send.
10. The dispatcher unregisters when the turn completes, fails, or is cancelled.
## Troubleshooting
### URL Verification Failed
* Confirm that the channel is published.
* Confirm that `PUBLIC_APP_URL` is a public HTTPS origin and that Everruns restarted after the value changed.
* Confirm that the Request URL contains `/v1/channels/{channel_id}/slack/events`.
### Bot Does Not Respond
* Confirm that the channel is published and enabled.
* Invite the bot to the channel with `/invite @botname`.
* Confirm that the Slack app has the required bot events and the `chat:write` scope.
* Confirm that the channel has the correct signing secret and bot token.
### Request Verification Failed
* Confirm that the channel’s signing secret matches the value under the Slack app’s **Basic Information** page.
* Confirm that the Everruns server clock is accurate.
## Approvals
When an Agent pauses for approval, Slack renders **Approve** and **Decline** buttons in the thread. Only the person whose message the Agent is answering can use them.
The generated manifest already points Slack interactivity at this channel. A Slack app created before interactive approvals existed must save a fresh manifest once.
## Task Progress
When an Agent delegates work, Slack shows one **Tasks** message and updates it as workers finish. The message lists up to five tasks and summarizes larger groups by count.
## Slack actions
| | |
| ---------------- | --------------- |
| **ID** | `slack` |
| **Category** | Integrations |
| **Features** | `slack_actions` |
| **Dependencies** | None |
An agent published to a Slack channel can reply in its thread. The `slack` capability lets it do the rest, as the same bot the workspace invited: react to a message, rewrite one it posted, share a file, or resolve a user ID to a name.
No second credential. The tools resolve the channel’s own bot token server-side, so there is nothing extra to provision, scope, or rotate.
### Requirements
The tools only work in a session a Slack message created. An agent that has the capability enabled but is running from the API, a schedule, or another channel has no Slack channel to act as, and every tool returns an error saying so rather than acting as some other channel’s bot.
Where an agent carries two Slack channels, each with its own bot, the tools act as the channel that created the session.
Your Slack app needs the scope for each action you use: `reactions:write` for reactions, `chat:write` for updates, `files:write` for uploads, and `users:read` for lookups. Slack answers a missing scope with an error the agent sees.
### Tools
#### `slack_add_reaction`
Slack messages include their exact channel and timestamp in the agent’s context. The agent uses that reference to target a reaction; a message’s date or rounded timestamp cannot identify it.
If a bot was installed before reaction permission was included, reconnect the Slack app and approve `reactions:write`. Deploying an updated manifest does not add permissions to an existing bot token.
Add an emoji reaction to a message. The cheapest acknowledgement available — prefer it over posting “working on it”.
| Parameter | Type | Required | Description |
| ----------- | ------ | -------- | --------------------------------------- |
| `channel` | string | yes | Channel ID the message is in |
| `timestamp` | string | yes | The message’s `ts` |
| `name` | string | yes | Emoji name without colons (e.g. `eyes`) |
Reacting with an emoji that is already there succeeds; the result says `already_reacted`.
#### `slack_update_message`
Rewrite a message this bot posted. Use it to turn a status message into its result instead of posting a second message.
| Parameter | Type | Required | Description |
| ----------- | ------ | -------- | -------------------------------------- |
| `channel` | string | yes | Channel ID the message is in |
| `timestamp` | string | yes | The `ts` of the bot message to rewrite |
| `text` | string | yes | Replacement text; Markdown is rendered |
Only messages this bot posted can be updated.
#### `slack_lookup_user`
Resolve a Slack user ID to that person’s display name, real name, timezone, and whether they are a bot.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ---------------------------------------------------- |
| `user_id` | string | yes | Slack user ID (the `<@U…>` mention form is accepted) |
Returns only those addressing fields. Email, phone, and title are not exposed to the agent.
#### `slack_upload_file`
Share a file into the conversation. Use it for reports, diffs, and logs too long to read in a message.
| Parameter | Type | Required | Description |
| ----------------- | ------ | -------- | ------------------------------------------------ |
| `channel` | string | yes | Channel ID to share into |
| `filename` | string | yes | Filename shown in Slack, including its extension |
| `content` | string | yes | The file’s text content |
| `thread_ts` | string | yes | Thread timestamp of the current conversation |
| `initial_comment` | string | no | Message posted alongside the file |
Content is capped at 8 MiB.
### Notes
* Posting to an arbitrary channel is deliberately not offered. The blast radius of “anywhere the bot is” is wider than “the thread that asked”. The control plane verifies the channel and thread against trusted session metadata before sending an action to Slack.
* A retired or disabled channel stops acting immediately, even for a session it created earlier.
* Slack rate limits reach the agent with Slack’s own retry advice rather than as a generic failure.
* The [Slack MCP server](https://everruns.com/docs/features/mcp/) stays supported for anything this does not cover. This removes the second credential for the common cases; it does not replace MCP.
## See also
* [Publish an Agent to Slack](https://everruns.com/docs/how-to/publish-to-slack/): a step-by-step guide.
* [Channels](https://everruns.com/docs/features/channels/): every channel type and the channel lifecycle.
* [Slack API Documentation](https://api.slack.com/docs) and the [Slack Events API](https://api.slack.com/events-api).
---
# Soft Approval
> Prompt-level consent gate. The agent pauses and asks before destructive or outward-facing actions, and runs safe work without interruption.
Source:
| | |
| ---------------- | ---------------------------------------------------------- |
| **ID** | `soft_approval` |
| **Category** | Safety |
| **Tools** | `request_approval`, `record_approval`, `set_approval_mode` |
| **Features** | None |
| **Dependencies** | None |
Soft approval asks the agent to stop and get the user’s consent before a critical action, and to run everything else without asking. It works through the system prompt and three tools. It is guidance for the model, not an enforcement point: a call the model makes still runs.
When a call must not run without a person’s decision, use [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/) instead. That gate holds the call until someone approves it, whatever the model does.
[Base](https://everruns.com/docs/built-ins/harnesses/base/) and every harness built on it, the deprecated [Generic](https://everruns.com/docs/built-ins/harnesses/generic/) and the [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) include soft approval at the `normal` level.
## Levels
| Mode | The agent asks before |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `off` | Nothing. The capability adds no prompt. |
| `normal` (default) | Destructive or irreversible actions (deleting files or data, dropping records, force-push, history rewrites) and outward-facing ones (pushing, publishing, opening pull requests, sending messages or mail, deploying, spending money). Ordinary edits and local, reversible changes proceed. |
| `protective` | Any action that changes state: writing or deleting files, commits and pushes, installing or removing packages, requests with side effects, changes to shared resources, and any command that is not plainly read-only. |
At every level except `off`, the prompt tells the agent to plan, batch the safe steps, and never pause on read-only inspection.
## Configuration
```json
{
"ref": "soft_approval",
"config": { "mode": "normal" }
}
```
`mode` accepts `off`, `normal`, or `protective`, and the synonyms listed under [`set_approval_mode`](#set_approval_mode). Any other value fails validation.
## Tools
### `request_approval`
The agent calls it at a critical action, then ends its turn. The call is the pause: it tells the user the agent is waiting.
| Parameter | Type | Required | Description |
| ---------- | ------ | -------- | ----------------------------------------------------------------------- |
| `action` | string | Yes | The action awaiting approval, for example “delete the staging database” |
| `question` | string | No | One short question for the user. Defaults to “Go ahead?” |
The result carries `awaiting_approval: true`, the action and question, and the turn and input message it was asked in.
### `record_approval`
After the user says yes, the agent calls it with what was approved, then carries the action out. A plain affirmative counts as approval; a hesitant or negative reply does not.
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ----------------------------------------------------------------------------------------- |
| `action` | string | Yes | The specific action the user approved |
| `detail` | string | No | Extra context such as the command or scope. It is logged, so it must not contain secrets. |
The result records the turn and input message the consent was given in. An auditor can join that message to the host’s own record of who sent it.
One approval covers one action. A category the user pre-authorizes (“no need to ask for commits”), or a workflow the user invoked by name, covers the actions it names; the agent records that grant once.
### `set_approval_mode`
When the user asks the agent to be more or less careful, the agent calls it with `mode`. It accepts `protective`, `normal`, and `off`, plus synonyms such as `paranoid`, `careful`, `default`, and `yolo`. The new level overrides the configured `mode` for that session from the next turn on.
## Relation to Ask User
[Ask User](https://everruns.com/docs/capabilities/ask-user/#not-a-consent-gate) questions auto-resolve, so an answer to them never grants permission. Soft approval’s wait does not auto-resolve. The interactive harnesses enable both, so the agent has a place for preference questions and a separate place for consent.
## See also
* [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/), the hard approval gate
* [Ask User](https://everruns.com/docs/capabilities/ask-user/), structured questions to the user
* [Guardrails](https://everruns.com/docs/capabilities/guardrails/), config-driven checks over output and tool activity
---
# SQL Database
> Session-scoped SQLite databases: create tables, run queries, and persist relational data per session.
Source:
| | |
| ---------------- | ---------------------- |
| **ID** | `session_sql_database` |
| **Category** | Data |
| **Features** | `sql_database` |
| **Dependencies** | None |
Session-scoped SQLite databases for structured data storage. Create tables, insert data, and run queries, all isolated to the current session.
## Tools
### `sql_execute`
Run DDL/DML statements (CREATE TABLE, INSERT, UPDATE, DELETE).
| Parameter | Type | Required | Description |
| ---------- | ------ | -------- | ---------------------------------------------------------------- |
| `database` | string | yes | Database name (alphanumeric and underscores); created if missing |
| `sql` | string | yes | SQL statement to execute |
### `sql_query`
Run SELECT queries. Results limited to 1000 rows.
| Parameter | Type | Required | Description |
| ---------- | ------ | -------- | ------------- |
| `database` | string | yes | Database name |
| `sql` | string | yes | SELECT query |
### `sql_schema`
Introspect the database schema, list tables, columns, and types.
| Parameter | Type | Required | Description |
| ---------- | ------ | -------- | ----------------------------------- |
| `database` | string | yes | Database name |
| `table` | string | no | Table name; omit to list all tables |
## Notes
* Database is session-scoped, destroyed when the session ends
* SELECT queries return at most 1000 rows
* Standard SQLite SQL syntax
## See Also
* [Storage](https://everruns.com/docs/capabilities/session-storage/), simpler key/value alternative
* [File System](https://everruns.com/docs/capabilities/file-system/), file-based data storage
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Sub Agents
> Spawn subagents that run tasks in isolated context windows, through the generic session task tools.
Source:
| | |
| ---------------- | ----------- |
| **ID** | `subagents` |
| **Category** | Core |
| **Features** | `subagents` |
| **Dependencies** | None |
Spawn subagents for parallel task execution. Each subagent runs in its own isolated context window, allowing the parent agent to delegate verbose or independent tasks without cluttering the main conversation. Subagents inherit the parent’s harness and agent configuration but operate with their own message history.
## Tools
### `spawn_agent`
Sessions with `subagents` expose `target.type: "subagent"` in the shared `spawn_agent` dispatcher. If first-party handoffs or external A2A delegation are also active, the same tool advertises those target types too. The dispatcher returns a `task_id` for the generic session task tools and moves Everruns toward one delegation surface across subagents, first-party agent handoffs, and external A2A agents.
Create and start a new subagent by calling `spawn_agent` with `target.type: "subagent"`. By default the subagent runs in the background: the tool returns immediately with a `task_id`, the parent agent keeps working, and the session is notified when the subagent finishes. Use the `task_id` with the generic session task tools to monitor, message, or cancel the subagent.
| Parameter | Type | Required | Description |
| -------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | yes | Human-readable name for the subagent. Must be unique within the session. |
| `instructions` | string | yes | Instructions for what the subagent should do. This becomes the subagent’s initial prompt. |
| `target.type` | string | yes | Must be `subagent`. |
| `mode` | string | no | `background` (default) returns immediately with a `task_id`; `foreground` blocks until the subagent completes and returns its result inline. |
| `blueprint` | string | no | Optional specialist blueprint ID, such as `github_scout`, that supplies its own prompt, model, and private tools. |
| `config` | object | no | Blueprint-specific configuration, validated against the blueprint’s schema before the child session is created. Only valid when `blueprint` is set. |
## Managing subagents after spawn
Use the generic `session_tasks` tools to monitor and steer subagents after spawning. The `task_id` is returned by `spawn_agent`.
* `list_tasks` with `kind: "subagent"`, list all subagent tasks and their status
* `get_task` with the task ID, get detailed status and result for a specific subagent
* `message_task`, send a steering message or additional context to a running subagent
* `cancel_task`, request cooperative cancellation of a subagent
* `wait_task`, block until a subagent reaches a terminal or interrupted state
## Notes
* **Governed spawning**: subagents can spawn nested subagents up to `max_subagent_depth` (default 2); set it to 0 to block subagent spawning. Each root session also has `max_active_descendant_tasks` (default 16) and `max_total_descendant_tasks` (default 200) caps to bound wide fan-out and repeated spawn loops.
* **Shared budget pool**: nested subagents spend from the root session’s session-scoped budget.
* **Background mode (default)**: spawning returns immediately with a `task_id`. The final result lands on the task record (`summary` via `get_task`), and the parent session is woken when the subagent reaches a terminal state. Background runs are capped at 6 hours.
* **Foreground mode**: `mode: "foreground"` blocks until the subagent completes and returns its result inline. Foreground execution has a 5-minute timeout.
* **Inherited configuration**: subagents inherit the parent’s harness and agent configuration.
* **Blueprints**: specialist blueprints can run with their own prompt, model, and private tools while still using the same subagent lifecycle.
## See Also
* [Agent handoff](https://everruns.com/docs/capabilities/agent-handoff/), task monitoring and control (`list_tasks`, `get_task`, `message_task`, `cancel_task`, `wait_task`)
* [Author an agent blueprint](https://everruns.com/docs/framework/agent-blueprints/), contributing a specialist agent with a typed configuration contract
* [GitHub Scout](https://everruns.com/docs/capabilities/github-scout/), blueprint-only GitHub repository exploration
* [A2A](https://everruns.com/docs/features/a2a/), delegating to external agents with `target.type: "external_a2a"`
* [Session](https://everruns.com/docs/capabilities/session/), session metadata and lifecycle
* [Platform](https://everruns.com/docs/capabilities/platform/), agent and platform configuration
* [Capabilities Overview](https://everruns.com/docs/capabilities/), full list of available capabilities
---
# Task Management
> Structured task lists for tracking multi-step work within a session.
Source:
| | |
| ---------------- | --------------------- |
| **ID** | `stateless_todo_list` |
| **Category** | Core |
| **Features** | None |
| **Dependencies** | None |
Enables agents to create and manage structured task lists. State is maintained in conversation history, each tool call sends the complete list.
## Tools
### `write_todos`
Create or update the complete task list. Each call replaces the entire list.
| Parameter | Type | Required | Description |
| --------- | ----- | -------- | -------------------------------------------------- |
| `todos` | array | yes | Array of `{ content, status, activeForm }` objects |
Task statuses: `pending`, `in_progress`, `completed`.
### `report_done`
Report finished work once, before the final reply: what was done, how it was checked, and links to what it produced. The session’s work report shows this summary instead of the first sentence of the reply, and a coordinated worker’s `complete_assignment` is read the same way.
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | --------------------------------------------- |
| `summary` | string | yes | One or two sentences, leading with the result |
| `validation` | string | no | How the work was checked |
| `outcome` | string | no | `done` (default) or `failed` |
| `artifacts` | array | no | Up to 10 `{ name, type, url?, path? }` links |
## Notes
* **Stateless**: no database table; state lives in conversation history
* Each `write_todos` call must include the **complete** list (not incremental updates)
* Best practice: exactly one task `in_progress` at a time
* Only mark a task `completed` when fully done (tests pass, no errors)
* `activeForm` is the present-continuous label shown during execution (e.g., “Running tests”)
## See Also
* [Session](https://everruns.com/docs/capabilities/session/), session metadata management
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Tool Approval
> Hold back risky tool calls until a person approves them. The call does not run until someone says yes.
Source:
| | |
| ---------------- | --------------- |
| **ID** | `tool_approval` |
| **Category** | Safety |
| **Tools** | None |
| **Dependencies** | None |
A hard approval gate. Before a risky tool runs, the turn pauses and a person decides. Unlike [soft approval](https://everruns.com/docs/capabilities/ask-user/#not-a-consent-gate), which asks the model to pause, this gate does not depend on the model: a call nobody approved does not run.
## What gets gated
Each tool declares what it does, and the gate reads those declarations rather than guessing from names:
| Mode | Asks before |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `normal` (default) | Tools that declare themselves destructive or outward-facing (they reach outside the session), and tools that declare they require approval |
| `protective` | Anything not declared read-only. A tool with no declarations counts as one that changes state |
| `off` | Nothing |
A tool cannot avoid the gate by declaring itself read-only and outward-facing at the same time.
## Configuration
```json
{
"ref": "tool_approval",
"config": { "mode": "normal", "timeout_seconds": 900 }
}
```
| Field | Default | Description |
| ----------------- | -------- | --------------------------------------------------------------------------------- |
| `mode` | `normal` | `off`, `normal`, or `protective` |
| `timeout_seconds` | `900` | How long a request waits for an answer before it counts as rejected (60 to 86400) |
## Answering a request
When a call is held back, the session waits with status `waiting_for_tool_results` and emits a `tool.call_requested` event containing one `approve_tool_call` call per held-back call. Its arguments show the tool, the arguments it would run with, why it was gated, and the deadline. The session UI renders these as approval cards.
Answer them with:
```bash
curl -X POST "$EVERRUNS_API/v1/sessions/$SESSION_ID/tool-approvals" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"decisions": [{"tool_call_id": "tool_approval_toolu_01...", "decision": "allow"}]}'
```
| Decision | Effect |
| --------------- | ----------------------------------------------- |
| `allow` | This exact call may run, once |
| `allow_always` | Every call of this tool in this session may run |
| `reject` | This call does not run |
| `reject_always` | No call of this tool runs in this session |
All requests from one pause are answered together. A request left out of the submission is not approved. Answering requires permission to manage the session.
After an answer the turn resumes and the agent calls the tool again. An `allow` covers only the call with exactly the arguments that were shown; different arguments are asked about again.
If nobody answers before the deadline, the requests count as rejected and the turn resumes. Nothing is remembered, so the agent can ask again later.
## When to enable it
Enable it for agents that can send messages, spend money, change production systems, or browse with [computer use](https://everruns.com/docs/capabilities/computer-use/): anything where one wrong call is expensive.
An agent that runs unattended on a schedule or trigger will wait for the full timeout on every gated call and then carry on without it. Use `off`, or leave the capability off, for agents nobody is watching.
---
# Tool Call Repair
> Detects and repairs malformed tool-call arguments from the model, recovering the turn instead of surfacing a raw parse error.
Source:
| | |
| ---------------- | ------------------ |
| **ID** | `tool_call_repair` |
| **Category** | Safety |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | Low |
Opt-in recovery net for malformed tool calls. Models occasionally emit tool-call arguments that are not clean JSON, wrapped in a Markdown code fence, surrounded by prose, with trailing commas or single quotes, or with values typed as strings where the schema wants numbers. Without repair, such a call either surfaces a parse error or silently collapses to empty arguments and fails downstream. This capability salvages the call so the turn proceeds.
**Disabled by default.** The capability is registered so agents can enable it, but contributes nothing unless explicitly selected. With it off, behavior is byte-for-byte unchanged.
## Tools
None, the capability intercepts inside the `reason` step, after the model’s tool calls are finalized and before the assistant message is built.
## How It Works
1. **Deterministic local salvage**: A pure function runs over each call’s `arguments`: it unwraps fenced code blocks and strips surrounding prose, removes trailing commas, normalizes single quotes to double quotes, and coerces string-typed known keys to the type declared by the tool’s JSON schema (e.g. `"42"` → `42` for an integer property). Optional keys the model filled with `null` or `"null"` where the schema does not accept it are dropped, since leaving the key out is what the model meant. An already-valid call is a no-op.
2. **Bounded corrective re-prompt**: When local salvage cannot recover a usable object, the capability allows up to `max_reprompts` attempts per call (default
1. before falling through to the normal error path. The re-prompt is realized by the agent loop: the unrepaired call proceeds to today’s error path and the model retries on the next iteration. The per-call cap guarantees there is no infinite repair loop.
3. **Observability**: Each malformed call emits one `tool.call_repaired` event carrying an outcome label: `local-salvage`, `re-prompt`, or `gave-up`.
## Configuration
### Default
```json
{
"capabilities": ["tool_call_repair"]
}
```
### Custom re-prompt cap
```json
{
"capabilities": [
{
"ref": "tool_call_repair",
"config": { "max_reprompts": 2 }
}
]
}
```
`max_reprompts` accepts `0`–`5`. `0` means “salvage locally or fall straight through to the error path with no re-prompt”.
## When To Enable
Use this capability when:
* You run models or providers that occasionally wrap tool arguments in prose or code fences, or emit lenient JSON (single quotes, trailing commas)
* You want a malformed call to recover the turn rather than waste an iteration on a raw parse error
## Limitations
* **Verbatim JSON only**: salvage extracts an embedded JSON object; it does not invent missing required fields or guess intent from natural language
* **Bounded input**: argument blobs larger than 256 KiB are treated as un-salvageable without parsing (a denial-of-service guard against runaway model output)
* **No deep type checking**: coercion handles top-level `integer` / `number` / `boolean` string values; full schema validation remains the tool’s job
## See Also
* [Events](https://everruns.com/docs/features/events/), the streaming event protocol that carries `tool.call_repaired`
* [Capabilities](https://everruns.com/docs/features/capabilities/), the extension model this capability plugs into
---
# Tool Search
> Deferred tool loading for agents with many tools, using the provider's hosted tool search on OpenAI and Claude models and a client-side search everywhere else.
Source:
Agents with many tools spend a large part of every prompt on tool parameter schemas. Tool search sends only tool names and descriptions up front and loads a tool’s full schema when the model needs it. Tool calls and results work the same way; only how schemas reach the model changes.
Four capabilities provide it:
| ID | Mechanism | Works on | On an unsupported model |
| -------------------- | -------------------------------------- | ------------------------------------------------------ | -------------------------------- |
| `auto_tool_search` | Picks one of the three below per model | Any model | Falls back to client-side |
| `openai_tool_search` | OpenAI hosted tool search | GPT-5.4 and newer (`gpt-5.4*`, `gpt-5.5*`, `gpt-5.6*`) | Does nothing (full schemas sent) |
| `claude_tool_search` | Anthropic hosted tool search | Claude 4 and newer, first-party API | Does nothing (full schemas sent) |
| `tool_search` | Client-side `tool_search` tool | Any model | Not applicable |
Use `auto_tool_search` unless you know the model and want one mechanism explicitly. It is what the [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) harness uses. Do not combine `auto_tool_search` with any of the other three on the same agent; it already provides all three paths.
All four capabilities have category Optimization, no features, and no dependencies.
## Auto tool search
`auto_tool_search` resolves to one of three underlying mechanisms based on the model:
* **Models with native OpenAI tool search** (GPT-5.4 and newer) → the hosted mechanism described in [OpenAI Tool Search](#hosted-openai): namespaces + `defer_loading` + a `{"type": "tool_search"}` activator. No extra tool is added; the provider handles search server-side.
* **Models with native Claude tool search** (Opus 4, Sonnet 4.6, Haiku 4.5, and Fable 5 and newer) → the hosted mechanism described in [Claude Tool Search](#hosted-claude): per-tool `defer_loading` + a `tool_search_tool_bm25_20251119` server tool. No extra tool is added; the provider handles search server-side.
* **All other models** (Gemini, OpenAI Completions, Claude/GPT reached via a gateway that doesn’t implement the hosted format, …) → the client-side mechanism described in [Tool Search](#client-side): schemas are stripped to stubs and a `tool_search` tool loads them back on demand.
The choice is made when the agent’s capabilities are assembled, once the model is known. You don’t have to know in advance which provider an agent will use.
The dispatch looks at the **model id** (matched against the first-party OpenAI/Anthropic profiles), not the transport. In practice that handles the common gateway cases: a Claude model served via Amazon Bedrock or OpenRouter carries a distinct id (`anthropic.claude-…`, `anthropic/claude-…`) that doesn’t match the bare first-party profile, so `auto_tool_search` resolves to the client-side mechanism there.
> **Edge case:** if a masked transport presents a *bare* first-party id that does resolve (e.g. a `gpt-5.4` served through an OpenAI-compatible gateway), `auto_tool_search` picks the hosted capability, but the driver then suppresses the hosted wire format for that transport, so full schemas are sent with **no** client-side fallback (a missed optimization, not a failure). If you run a first-party model id through such a gateway, add the [Tool Search](#client-side) capability explicitly to force client-side deferral.
### Tools
One, the client-side `tool_search` tool, added only on models without native support. On models with native tool search, no tool is added and the provider’s hosted search is used instead.
### Configuration
```json
{
"capabilities": ["auto_tool_search"]
}
```
The default threshold is 15 tools. To change it:
```json
{
"capabilities": [
{
"ref": "auto_tool_search",
"config": { "threshold": 10 }
}
]
}
```
The threshold (minimum tool count before deferral activates) applies to every mechanism. Set it to `1` to always activate when the capability is present.
## Deferrable policy
Each tool has a `deferrable` policy that controls whether its schema can be deferred:
| Policy | Behavior |
| ----------- | ------------------------------------------------------------------------- |
| `never` | Full schema always sent (use for high-frequency tools like `write_todos`) |
| `automatic` | Deferred when tool_search is active and above threshold (default) |
| `always` | Always deferred when tool_search is active |
## Hosted: OpenAI
`openai_tool_search` enables [OpenAI’s tool_search](https://platform.openai.com/docs/guides/tool-search). It configures the LLM driver and adds no tool.
1. **Threshold check**: tool_search only activates when the total tool count meets or exceeds the threshold (default: 15)
2. **Namespace grouping**: tools are grouped by their capability’s category into [namespace](https://platform.openai.com/docs/api-reference/responses/create#responses-create-tools) entries, giving the model semantic structure for discovery. Wire namespace `name` values are normalized to provider-safe identifiers (for example `File Operations` → `File_Operations`); descriptions keep the human-readable category label
3. **Deferred schemas**: tools marked as deferrable have `defer_loading: true` set, meaning only name + description are sent upfront
4. **`tool_search` entry**: a `{"type": "tool_search"}` activator is appended to the tools array, enabling the model’s built-in tool search index
5. **Transparent execution**: tool calls and results work identically; the only difference is how tools are presented to the model
### Model support
Tool search requires model-level support. Currently supported:
| Model family | Supported |
| ---------------- | ----------------------------------- |
| `gpt-5.6*` | Yes |
| `gpt-5.5*` | Yes |
| `gpt-5.4*` | Yes |
| All other models | No (capability is silently ignored) |
When the capability is enabled but the model doesn’t support tool_search, the feature is silently skipped, no errors, no behavior change.
Configure it like `auto_tool_search`, with `"ref": "openai_tool_search"` and an optional `threshold`.
### Limitations
* **OpenAI only**: tool_search is an OpenAI Responses API feature; other providers ignore this capability.
* **Supported OpenAI models only**: earlier OpenAI models don’t support tool_search.
* **No client-side tools**: currently only applies to built-in (server-executed) tools.
See the [OpenAI Responses API tools parameter](https://platform.openai.com/docs/api-reference/responses/create#responses-create-tools) for the namespace and tool_search types.
## Hosted: Claude
`claude_tool_search` enables [Anthropic’s hosted tool search](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool). It configures the LLM driver and adds no tool.
1. **Threshold check**: tool search only activates when the total tool count meets or exceeds the threshold (default: 15). Below the threshold, full schemas are sent as usual.
2. **Deferred schemas**: every deferrable tool gets `defer_loading: true`, so only its name and description reach the model upfront. Anthropic defers each tool individually (there is no namespace grouping).
3. **Hosted search tool**: a `tool_search_tool_bm25_20251119` server tool is added to the request. The model issues a natural-language query against the catalog (tool names, descriptions, argument names, and argument descriptions) and Anthropic returns the 3–5 most relevant tools, expanding them into full definitions inline.
4. **Transparent execution**: the model then calls a discovered tool with a normal `tool_use`; tool calls and results work identically. The only difference is how tools are presented to the model.
Because the hosted search tool is itself never deferred, Anthropic’s requirement that *at least one tool be non-deferred* is always satisfied, even when every function tool is deferrable.
Keeping the 3 to 5 most frequently used tools non-deferred (policy `never`) avoids a search round-trip before the agent’s first hot-path call.
### Model support
Tool search requires model-level support. Per Anthropic, it is available on:
| Model family | Supported |
| -------------------------------------------------------- | ----------------------------------- |
| Opus 5.5 / 5 (`claude-opus-5-5`, `claude-opus-5`) | Yes |
| Opus 4.x (`claude-opus-4*`) | Yes |
| Sonnet 5.5 / 5 (`claude-sonnet-5-5`, `claude-sonnet-5`) | Yes |
| Sonnet 4.6 (`claude-sonnet-4-6`) | Yes |
| Haiku 5.5 / 4.5 (`claude-haiku-5-5`, `claude-haiku-4-5`) | Yes |
| Fable 5.1 / 5 (`claude-fable-5-1`, `claude-fable-5`) | Yes |
| Retired pre-4 Claude models | No (capability is silently ignored) |
When the capability is enabled but the model doesn’t support tool search, the feature is **silently skipped, full tool schemas are sent as usual**. This standalone capability does *not* add a client-side fallback: on an unsupported model it simply does nothing (no error, no behavior change). For automatic fallback to client-side deferral, use [Auto Tool Search](#auto-tool-search) (or add the [Tool Search](#client-side) capability explicitly).
Claude models reached through a non–first-party transport don’t get hosted tool search either, because those transports don’t implement the hosted format:
* **Amazon Bedrock**: this integration uses the ConverseStream API; Anthropic’s server-side tool search on Bedrock is only available via the InvokeModel API.
* **OpenRouter**: its stateless OpenAI-compatible endpoint accepts but does not implement Anthropic’s hosted tool search.
With `claude_tool_search` alone, those transports also send full schemas; pair with [Auto Tool Search](#auto-tool-search) to get client-side deferral there instead.
Configure it like `auto_tool_search`, with `"ref": "claude_tool_search"` and an optional `threshold`.
## Client-side
`tool_search` implements tool search entirely client-side, so it works with Gemini, OpenAI Completions, models reached through gateways that don’t implement hosted search, and any other provider.
For large MCP catalogs, `tool_search` can use bounded schema disclosure. Set `bounded: true`; the optional `max_active_tools`, `max_active_bytes`, and `max_result_bytes` settings adjust the built-in limits for active native schemas and search responses. Legacy behavior remains the default unless bounded mode is enabled. See [Large MCP catalogs and tool surfaces](https://everruns.com/docs/features/large-mcp-catalogs/) for the complete configuration and runtime behavior.
### Tools
* **`tool_search`**: search the available tools by keyword and load their full parameter schemas. It is never deferred itself.
### How it works
The diagram below traces one deferred tool through a full round-trip, from schema stripping at context-assembly time, through the `tool_search` call, to calling the real tool with its restored parameters.

1. **Threshold check**: deferral only activates when the total tool count meets or exceeds the threshold (default: 15). Below it, full schemas are sent unchanged.
2. **Schema stripping**: a tool-definition hook replaces the parameter schema of every deferrable tool with a minimal open-object stub (name + description survive). The shared prompt carries the search instruction once instead of repeating it in every stub. This runs when the runtime agent is built, so the model never receives the full schemas upfront.
3. **`tool_search` tool**: a real tool is added to the agent. When the model calls it with a query, the tool inspects its sibling tools and returns the full JSON parameter schemas of the matches.
4. **Progressive disclosure**: `tool_search` also records the matched tools as *revealed*. The hook re-runs on every reasoning iteration, so on the next step the revealed tools are advertised with their full, authoritative schema on the *registered* definition. This is what lets a structured tool caller actually pass arguments to a previously deferred tool, rather than only reading its schema as text.
5. **System-prompt guidance**: a short note instructs the model to call `tool_search` before using a tool whose parameters it has not yet loaded.
6. **Transparent execution**: the underlying tools stay registered and executable. Tool calls and results work identically; only how schemas reach the model changes.
### Never-defer allowlist
`DeferrablePolicy::Never` is set by the tool’s *owner*. An embedder that composes tools it does not own (for example file/shell tools from another crate) can instead keep specific tools fully loaded by name:
* Programmatically: `ToolSearchCapability::with_never_defer(["read_file", "bash", ...])`.
* By configuration: a `never_defer` array (merged with any programmatic list).
Allowlisted tools behave exactly like `DeferrablePolicy::Never` tools, their full schema is always sent, so the agent is never forced through a `tool_search` round-trip before its first read/edit/shell call.
### Legacy search ranking and result bounding
The behavior in this section describes the default, legacy generic search. Bounded mode uses the server-aware index and active/result byte limits described in [Large MCP catalogs](https://everruns.com/docs/features/large-mcp-catalogs/). In bounded mode, a source-only query returns a server refinement rather than arbitrary operation schemas.
Because there is no hosted semantic index, `tool_search` ranks matches client-side with a deliberately simple, predictable scheme:
* **Field-weighted keyword overlap**: each whitespace-separated query term scores **3** if it appears in a tool’s *name* and **1** if it only appears in the *description*. A name hit is a far stronger signal of intent than an incidental word in prose, so it dominates.
* **Exact-name bonus**: a query that is exactly a tool name gets a large bonus (**+100**), so “load this specific tool” always ranks that tool first. The deferred stub tells the model to query the exact tool name, so this is the common path. Wrapping punctuation is stripped first, so a quoted or backticked name (`"read_file"`, `` `read_file` ``) still matches.
* **Top-band cutoff**: only results scoring at least **half the top score** are returned, trimming weak tail matches so a loose query does not drag in loosely related tools.
* **Result cap**: at most **8** tools are returned per call. Every returned tool is also *revealed* (its full schema is un-deferred for the rest of the session), so the cap bounds both the response payload and how much of the catalogue a single search can permanently un-defer.
* **Visible-tool scoping**: the search only considers tools visible in the current turn (the turn-scoped allowlist), so it never reveals a tool the agent could not otherwise call.
* **No-match fallback**: if nothing matches, the tool returns the catalogue of available tool *names* (not schemas) so the model can refine its query instead of dead-ending. An empty query lists tools so the model can browse.
The session reveal set that drives progressive disclosure is itself bounded: it is keyed per session and evicts the oldest sessions past a fixed cap, so reveals never leak across sessions or grow without limit (an evicted session simply re-runs `tool_search`).
### Configuration
```json
{
"capabilities": ["tool_search"]
}
```
The activation threshold defaults to 15 tools (`DEFAULT_TOOL_SEARCH_THRESHOLD`). Both the threshold and a never-defer allowlist can be set via capability config:
```json
{
"capabilities": [
{
"ref": "tool_search",
"config": {
"threshold": 20,
"never_defer": ["read_file", "write_file", "edit_file", "list_directory", "grep_files", "bash"]
}
}
]
}
```
`reveal` (experimental) chooses how a found tool becomes callable. With `redefine` (the default), its full schema replaces the placeholder in the tool list from the next step. With `in_result`, the tool list never changes: the model calls the tool using the schema the search returned, and every call is still checked against the tool’s real schema before it runs. On models that cannot carry tool changes inside the conversation, `in_result` keeps the prompt cache through a search. Some models call badly against a placeholder schema: in our tests Muse Spark sent numbers and objects as strings and never recovered, while GPT-6 Luna and Mistral Large 4 called as well as with `redefine`. Use it only with a model you have checked.
### Benchmarks
Deferral only touches how tool *parameter schemas* reach the model, names and descriptions still go out in full, so the savings scale with how many tools an agent carries and how rich their schemas are.
Measured on a representative 19-tool generic-agent surface (file, shell, web-fetch, session, storage, todo, time, scheduling, and subagent tools, plus `tool_search` itself), comparing the serialized tool list the driver sends to the model **with and without** deferral on the first turn:
| Metric | Full schemas | Deferred (first turn) | Saving |
| --------------------------- | ------------------------- | ----------------------- | --------------- |
| Tool list sent to model | \~9.1 KB (\~2,270 tokens) | \~3.0 KB (\~740 tokens) | **67% smaller** |
| Parameter-schema bytes only | \~7.2 KB | \~1.0 KB | **86% smaller** |
Token figures use the \~4-chars-per-token rule of thumb for JSON. 18 of the 19 tools were deferred (`tool_search` keeps its schema). Net savings grow with tool count: an agent with dozens of MCP tools defers proportionally more.
Reproduce this benchmark with:
```bash
cargo test -p everruns-core --features builtins --lib benchmark_prompt_size_reduction -- --nocapture
```
The trade-off is one extra `tool_search` round-trip per deferred tool before its first use; for many-tool agents the upfront token savings dominate.
### Limitations
* **Server-executed tools**: the search reads schemas from the worker-side tool registry. This includes built-in tools and MCP server tools (MCP tools are registered as first-class registry tools). Client-side tools that are not registered worker-side are not returned by `tool_search` (their stripped definition is still sent so the model knows they exist).
* **Extra round-trip**: loading a schema costs one `tool_search` call before the first use of a deferred tool. The token savings outweigh this for agents with many tools.
## See also
* [OpenAI Tool Search documentation](https://platform.openai.com/docs/guides/tool-search)
* [Anthropic tool search tool documentation](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool)
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
---
# Tools in Shell
> Call an agent's tools, MCP servers included, from the Bashkit shell with one tools command, so one script can chain several calls and return only what matters.
Source:
| | |
| -------------- | ---------------------------------------------------------------------- |
| **ID** | `tools_in_shell` |
| **Category** | Execution |
| **Risk** | High, assignment requires an org **Admin** |
| **Depends on** | [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/) |
| **Rollout** | Experimental; adding the capability is the opt-in |
Tools in Shell adds a `tools` command inside the [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/). In the default `cli` mode, eligible operations move out of the model’s direct tool list. `hybrid` mode keeps those operations available both through the shell and as native tools, with native schemas eager by default. To defer optional native schemas, also enable bounded `tool_search`. One script can call several tools, filter results with `jq`, and print only what the model needs:
```bash
tools github list-pulls '{"repo":"acme/api","state":"open"}' \
| jq -r '.[] | select(.draft | not) | .number' \
| while read n; do
tools github get-pull "repo=acme/api" "number=$n" | jq -c '{number, title, mergeable}'
done
```
## The `tools` command
| Command | What it does |
| ----------------------------------------------------- | -------------------------------------------------------------- |
| `tools --help` | Lists MCP servers (with their tool counts) and the other tools |
| `tools --help` | Lists one MCP server’s tools |
| `tools --help`, `tools --help` | Shows the tool’s input schema |
| `tools search ` | Finds tools by name and description |
| `tools '{...}'` | Calls an MCP tool |
| `tools '{...}'` | Calls any other tool |
### Surface mode
The default `mode: "cli"` routes eligible operations through the shell. Set `mode: "hybrid"` to keep the CLI route while allowing native discovery and calls. Hybrid mode alone keeps native schemas eager. If `tool_search` is also configured with `bounded: true`, it can reveal optional native schemas within its configured limits; eager owner-required schemas remain visible. The shell source inventory is available from `tools --help` and does not change the static Bash tool description.
```json
{
"ref": "tools_in_shell",
"config": { "mode": "hybrid" }
}
```
The CLI and bounded native search use the same authorized, server-aware index. Legacy native `tool_search` remains unchanged unless configured with `bounded: true`. See [Large MCP catalogs](https://everruns.com/docs/features/large-mcp-catalogs/) for discovery bounds, search configuration, and cache and authorization behavior.
MCP tools are grouped by server (`mcp_github__list_pulls` is `tools github list-pulls`); every other tool is a top-level command (`web_fetch` is `tools web-fetch`). Hyphens and underscores are interchangeable. The list is rebuilt on every shell call, so an MCP server added during a session shows up on the next one.
**Input** is the same JSON object the tool takes as a direct call. It can come from stdin (`jq -n '{repo: "a/b"}' | tools github list-pulls`, or `-`), from a JSON argument, or from `key=value` and `--key value` pairs, which merge over the JSON. `--flag` alone is `true`. A value is read as JSON unless the tool’s schema says the key is a string, so `number=7` is a number and `repo=42` stays text.
**Output** is the tool’s result on stdout: a text result as is, anything else as JSON. Images a tool returns are saved under `tool-images/` in the working directory, and stderr names the files.
**Errors** are one JSON line on stderr with exit code 1, so a script can branch on them:
```json
{"error":{"code":"needs_approval","message":"...","retryable":false}}
```
| Code | Meaning |
| --------------------- | ------------------------------------------------------------------------------------------------------- |
| `unknown_command` | No such server or tool |
| `invalid_input` | The input is not a JSON object or does not match the tool’s schema |
| `tool_error` | The tool ran and failed |
| `denied` | A guardrail or hook blocked the call |
| `needs_approval` | The call needs a person’s approval; the script stops here (see [Approvals](#approvals)) |
| `stopped` | An earlier call stopped the script, so this one did not run |
| `connection_required` | The tool or its MCP server needs a connection the person has not made |
| `call_limit` | More than 50 tool calls in one shell call |
| `unavailable` | The command cannot run here, or a server could not load yet (`retryable` says whether a later step can) |
## Which tools move into the shell
A tool moves behind `tools` when calling it only does something and returns data: MCP tools, integration tools, search and fetch, platform and data tools. These stay direct tool calls:
* the `bash` and `lua` tools themselves;
* tools that pause or shape the turn: `ask_user`, approvals, `spawn_agent`, session tasks, `write_todos`, and client-side tools;
* tools whose result the model must see as an image, such as screenshots, `read_file`, `computer` and `browser`;
* structured file editing (`write_file`, `edit_file`);
* tools listed in the `keep_visible` config.
MCP servers set to “Load tools on demand” move behind `tools` too. `tools --help` lists them as `(not loaded)`; the first command that names one, such as `tools docs --help` or `tools docs search-docs q=refunds`, or a `tools search` that matches deferred server descriptions, loads its tools inside the same shell call. One `tools search` loads at most eight matching deferred servers; the others stay `(not loaded)` for a later search. A load adds that server’s tools atomically and respects the combined 10,000-tool/32 MiB shell catalog limit. Search, help and calls use only tools in the current turn’s visible scope, and loaded child tools retain the scope of their server placeholder. From the next step on a loaded server is listed like any other.
Every call from a script goes through the same checks as a direct call: the turn’s guardrails and hooks run before it, the tool’s schema is checked, and the post-tool hooks see the result before the script does.
## Approvals
Approval-gated and destructive tools are reachable from the shell too, and [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/) judges each call from a script the same way it judges a direct call. Most scripts never ask: reads, searches, and tools a person already allowed “always” just run.
Many MCP tools say nothing about whether they change anything. When the deployment has a decision service configured, such a tool is rated once the first time a script calls it: if it looks like it changes or deletes something outside the session, Tool Approval treats it as destructive and asks before it runs. The approval card then says the tool declares nothing about its risk and only looks like it changes things. A rating only ever adds a question; it never removes one, and tools that declare their own risk are not rated. An agent can turn ratings off with the `rate_unlabeled_tools` setting; such tools are then judged by what they declare, which is nothing, so in Tool Approval’s `normal` mode they run without asking.
When a call written out in full in the script, such as `tools github delete-branch branch=fix-x`, needs a person’s answer, the script does not start at all: the `bash` result says so (`"before_start": true` under `stopped`, `done` is empty), the person is asked about that call, and after they answer the agent can run the same script again. Calls whose input is built while the script runs, or that read their input from stdin, are checked as they run instead.
To see what a script would do before running it, pass it to `tools plan`. It runs nothing and prints each `tools` call it can see, with its input and risk:
```bash
tools plan <<'EOF'
tools github list-pulls state=open
tools github delete-branch branch=fix-x
EOF
```
```json
{
"calls": [
{"tool": "tools github list-pulls", "input": {"state": "open"}, "risk": "read_only", "where": "script"},
{"tool": "tools github delete-branch", "input": {"branch": "fix-x"}, "risk": "needs_approval", "why": "destructive", "where": "script"}
],
"complete": true
}
```
`risk` is `read_only`, `changes` (runs without asking), `needs_approval`, `blocked`, or `checked_at_run_time` when the input is built while the script runs. A `needs_approval` call carries `why`, the reason the approval card will give: `destructive`, `open_world`, `mutating`, `policy`, or `rated_changes` when the tool declares nothing and was rated as changing things. `complete` is `false` when the script hides calls from reading, for example with `eval`. The agent uses it to describe a script accurately before asking a person about it.
When a call needs a person’s answer while the script runs, it is not made and the script stops there. Nothing after it runs, and the script is never resumed or run again, because part of its work is already done and may not be safe to repeat. The `bash` result reports what happened, and the person is asked about the stopped call in the same step:
```json
{
"exit_code": 1,
"success": false,
"tools": {
"done": [
{"tool": "tools linear create-issue", "input": {"title": "Flaky test"},
"ok": true, "result": {"id": "EVE-1201"}}
],
"read_only_calls": 3,
"stopped": {
"reason": "needs_approval",
"approval": "requested",
"call": {"tool": "tools github delete-branch", "input": {"branch": "fix-x"}}
},
"not_reached": "the stopped call and everything after it; write a new script for the rest"
}
}
```
`done` lists every call that may have changed something; read-only calls are counted. After the person answers, the agent writes a new script for what is left. A one-off approval covers that exact call (the tool and its input) once, so the new script can make it. The same report appears when a script stops at the call limit, or exits with an error after changing something.
## In the session timeline
The model sees one `bash` call. Each tool the script called through `tools` is also recorded in the session as a [`tool.nested_call`](https://everruns.com/docs/event-reference/) event under that `bash` call, with its command, a short preview of its input, whether it completed, failed, was refused or stopped the script for approval, and how long it ran. The chat shows these as rows under the shell call. They never reach the model.
## Saved scripts
An agent can keep a script it wrote and run it again later as one command:
```bash
cat <<'EOF' | tools scripts save triage-prs --description 'Label new pull requests.' \
--input-schema '{"type":"object","properties":{"repo":{"type":"string"}},"required":["repo"]}'
repo=$(jq -r .repo)
tools github list-pulls repo="$repo" state=open | jq -r '.[].number' | while read -r n; do
tools github add-labels repo="$repo" number="$n" labels='["triage"]'
done
EOF
tools scripts # list the agent's saved scripts
tools scripts triage-prs repo=a/b # run one
```
* The input is checked against the script’s schema and arrives on stdin as JSON; what the script prints is its result.
* A saved script runs with the tools and identity of the agent turn that runs it, and its `tools` calls follow the same rules: approvals, guardrails, and the 50-call limit of the shell call it runs in. A call that needs approval stops the caller’s script too.
* Its shell has the shell builtins and `tools`; `curl` and the `everruns` command are not available inside it.
* Saved scripts can call each other, up to four deep.
* Saving needs the `manage_scripts` setting. Every agent with the capability can run its saved scripts. Scripts are also managed through the `/v1/agents/{agent_id}/scripts` API and the `everruns agents scripts` command.
* A schedule or webhook [trigger](https://everruns.com/docs/features/agent-triggers/#run-a-saved-script) can run a saved script without a model call.
## Tool search
In the default `cli` mode, Tools in Shell replaces tool search. When both are enabled, the tool search capabilities (`tool_search`, `auto_tool_search`, `openai_tool_search`, `claude_tool_search`) contribute nothing. In `hybrid` mode, bounded `tool_search` can reveal native schemas while Tools in Shell provides the same operations through `tools`. See [Large MCP catalogs and tool surfaces](https://everruns.com/docs/features/large-mcp-catalogs/) for the shared registry model. [Agent Checks](https://everruns.com/docs/features/agent-checks/) report a redundant capability as a `capabilities.superseded` suggestion when it is not active for the selected mode.
## Configuration
```json
{
"capabilities": [
{ "ref": "tools_in_shell", "config": { "mode": "cli", "keep_visible": ["web_fetch"] } }
]
}
```
| Field | Type | Default | Meaning |
| ---------------------- | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode` | string | `cli` | `cli` routes eligible tools through the `tools` command; `hybrid` also retains native tool discovery where configured. |
| `keep_visible` | string array | `[]` | Tools the model can still call directly. They stay callable from the shell too. |
| `manage_scripts` | boolean | `false` | Let the agent save scripts with `tools scripts save`. |
| `rate_unlabeled_tools` | boolean | `true` | Ask the decision service whether a tool that declares nothing about its risk changes things, and ask for approval before it if so. Off: such tools run without asking in Tool Approval’s `normal` mode. |
---
# Auto-Continue After Usage Limit
> When an LLM subscription/plan usage limit is reached, automatically resume the interrupted work shortly after the limit resets.
Source:
| | |
| ---------------- | --------------------------- |
| **ID** | `usage_limit_auto_continue` |
| **Category** | Core |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | Low |
Some providers cap usage per subscription window rather than per minute. When a plan usage limit is hit, for example the ChatGPT/Codex `429` `usage_limit_reached` response, the turn fails and the session goes idle until the limit resets, often hours later. This capability makes the session resume on its own once the window clears, so long-running work is not silently stranded.
It contributes **no tools**. The behavior is encapsulated behind a reusable platform boundary, the capability supplies an *LLM error hook* (an in-process capability hook, the same family as tool-call hooks and message filters) that the agent runtime invokes generically when a turn fails with a terminal error. The runtime has no special-casing for usage limits; any capability can provide the same kind of error-recovery hook.
## How It Works
1. **Decision**: The provider error is classified as `provider_usage_limit_reached`, which captures the absolute reset time (`resets_at`, unix seconds) reported by the provider. This is driver-agnostic: any driver whose error body carries the `usage_limit_reached` wording is covered.
2. **Scheduling**: When the capability is enabled and a reset time is present, a one-shot session schedule is created to fire `delay_seconds` after the reset. When it fires, the configured `prompt` is injected as a user message and the interrupted work resumes.
3. **Message copy**: The user-facing error reads *“You’re out of LLM usage limits. Your usage limit resets at \.”* When (and only when) a continuation was actually scheduled, it appends *“We’ll continue work automatically once it resets.”*, so the copy never promises a resumption that will not happen. Without the capability, the same error stays generic and no continuation is scheduled.
## Configuration
### Default
```json
{
"capabilities": ["usage_limit_auto_continue"]
}
```
Defaults: continuation fires **120 seconds** after the reported reset with the prompt **“Continue tasks”**.
### Custom delay and prompt
```json
{
"capabilities": [
{
"ref": "usage_limit_auto_continue",
"config": {
"delay_seconds": 300,
"prompt": "Resume the migration where you left off."
}
}
]
}
```
| Field | Type | Default | Description |
| --------------- | ----------------- | ------------------ | ---------------------------------------------------------------------------------------------------------- |
| `delay_seconds` | integer (0–86400) | `120` | How long to wait after the reset before resuming. A small buffer avoids racing the provider’s reset clock. |
| `prompt` | string | `"Continue tasks"` | Message injected to resume work when the limit resets. |
## When To Enable
Use this capability for agents running long, autonomous, or scheduled work on provider plans that enforce usage-window limits, where a multi-hour stall would otherwise require a human to manually restart the session.
## Limitations
* Requires a provider whose error reports a concrete reset time; without one the error copy stays generic and no continuation is scheduled.
* The continuation is delivered through the session schedule machinery, so it requires both a session schedule store and a schedule poller in the deployment. The hosted platform provides both. The default embedded runtime provides neither, so this capability is **not** part of the runtime-safe preset, an embedder must wire a `SessionScheduleStore` (e.g. via a schedule-store factory) and run a poller for it to take effect; otherwise its error hook is a no-op and the error copy stays generic.
## See Also
* [Schedules](https://everruns.com/docs/capabilities/session-schedules/), the scheduling machinery the continuation reuses
* [Capabilities](https://everruns.com/docs/features/capabilities/), the capability model
---
# User Hooks
> Run user-authored shell commands at lifecycle and tool events. Block, mutate, or audit agent actions from outside the model.
Source:
| | |
| ---------------- | ------------ |
| **ID** | `user_hooks` |
| **Category** | Automation |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | High |
User Hooks let you inject shell commands at six well-defined points in the agent execution lifecycle. The runtime hands each hook a structured JSON payload through environment variables (`$EVERRUNS_HOOK_PAYLOAD_JSON` plus a copy at `$EVERRUNS_HOOK_PAYLOAD_PATH` on the session VFS) and reads a structured decision back from stdout. (Delivery is via env vars rather than process stdin because the `bashkit_shell` interpreter runs the script in-process and exposes no process stdin.) Hooks can run silently (logging only), mutate the inputs they observe, or block the action outright.
This is the same pattern you’ll recognize from Claude Code hooks, Git hooks, and pre/post-tool middleware in agent SDKs, applied as a first-class capability so any agent can adopt it, any hook bundle can be shared across an organization, and every invocation lands in the audit log.
## When to enable
* **Security gates**: block any `bash` call matching `rm -rf /` before the sandbox sees it.
* **Format-on-write**: run `cargo fmt` or `prettier` after every `edit_file`.
* **Audit / observability**: POST every tool call to your SIEM.
* **Project bootstrapping**: at `session_start`, clone a repo, install deps, or seed a workspace.
* **CI-style validation**: at `turn_end`, run tests and surface failures back to the model on the next turn.
## Risk
High. The capability accepts arbitrary shell commands from config. Even though the commands run inside the session’s `bashkit_shell` sandbox (no host filesystem, no host network beyond the session’s egress policy), the assignment gate is admin-only, anyone who can configure this capability can run arbitrary code in the session sandbox on every agent action.
## Events
Six events. Two (`pre_tool_use`, `user_prompt_submit`) can block; the rest are advisory-only. **`pre_tool_use` and `post_tool_use` fire today**; the other four events ship their schema/validation here but their runtime wire-in lands in follow-up changes (see “What’s not yet wired” below).
| Event | Fires at | Can block? | Mutation surface |
| -------------------- | ------------------------------------ | ---------- | -------------------- |
| `session_start` | New session created | no | none |
| `user_prompt_submit` | User message accepted, before reason | yes | user message text |
| `pre_tool_use` | Each tool call, before execution | yes | `ToolCall.arguments` |
| `post_tool_use` | Each tool call, after execution | no | `ToolResult` |
| `turn_end` | Turn finishes | no | none |
| `session_end` | Session close/archive | no | none |
See [Bash hook contract](#bash-hook-contract) for block and mutation responses.
## Configuration
The capability accepts two top-level fields:
```json
{
"capabilities": [
{
"ref": "user_hooks",
"config": {
"hooks": [ /* UserHookSpec entries */ ],
"disabled_contributions": [ /* HookId strings to mute */ ]
}
}
]
}
```
### `UserHookSpec`
```jsonc
{
"id": "fmt_after_edit", // optional; defaults to "{event}_{idx}"
"event": "post_tool_use",
"matcher": { "tool_name": "edit_file" }, // tool events only
"executor": {
"type": "bash",
"command": "scripts/fmt.sh",
"env": { "FMT_PROFILE": "ci" }
},
"timeout_ms": 5000, // 100..30_000
"on_error": "warn", // "block" | "allow" | "warn"
"description": "Run formatter after edit_file"
}
```
#### Matcher
The `matcher` block applies only to `pre_tool_use` and `post_tool_use`. Setting it on lifecycle events is rejected at validation time.
| Field | Meaning |
| ---------------- | ------------------------------------------------------ |
| `tool_name` | Exact tool name match |
| `tool_name_glob` | Restricted glob: `a\|b\|c` alternation or trailing `*` |
| `args_jsonpath` | Dot-path into `ToolCall.arguments` (e.g. `$.command`) |
| `match_regex` | Fires when extracted value matches this regex |
| `deny_regex` | Inverse, fires when extracted value matches this regex |
`match_regex` and `deny_regex` are mutually exclusive. Regex flavor is the Rust `regex` crate (no look-around, no backreferences).
#### `on_error`
What to do when the executor itself fails (timeout, non-JSON output, sandbox error):
* `block`, treat as `{"decision": "block", "reason": "hook failed"}`. Use for security-critical hooks.
* `allow`, log + continue.
* `warn`, log + emit `hook.warning` event + continue. **Default.**
## Bash hook contract
The bash executor is modeled after Claude Code hooks with one departure: the payload is delivered via env vars (and a session-VFS file) rather than stdin, because the bashkit interpreter doesn’t expose process-level stdin to user scripts.
### Input, env vars + VFS file
Every hook invocation sets these env vars:
| Var | Meaning |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `EVERRUNS_HOOK_PAYLOAD_JSON` | Full payload, JSON-encoded as one string. Read with `jq -n 'env.EVERRUNS_HOOK_PAYLOAD_JSON \| fromjson'`. |
| `EVERRUNS_HOOK_PAYLOAD_PATH` | Path on the session VFS to a file containing the same JSON. Cleaned up after the hook returns. Use for `cat "$EVERRUNS_HOOK_PAYLOAD_PATH" \| jq` workflows. |
| `EVERRUNS_HOOK_EVENT` | One of `session_start`, `user_prompt_submit`, `pre_tool_use`, `post_tool_use`, `turn_end`, `session_end`. |
| `EVERRUNS_HOOK_ID` | Stable hook id (`{capability_id}:{name}` or `user:{name}`). |
| `EVERRUNS_HOOK_SESSION_ID` | Current session id. |
| `EVERRUNS_HOOK_TURN_ID` | Set when a turn is in flight. |
| `EVERRUNS_HOOK_TOOL_NAME` | Set for `pre_tool_use` / `post_tool_use`. |
| `EVERRUNS_HOOK_TOOL_CALL_ID` | Set for `pre_tool_use` / `post_tool_use`. |
Payload envelope:
```json
{
"event": "pre_tool_use",
"hook_id": "user:guard_rm",
"session_id": "ses_…",
"turn_id": "trn_…",
"org_id": "org_…",
"agent_id": "agt_…",
"ts": "2026-05-28T12:34:56.789Z",
"data": {
"tool_name": "bash",
"tool_call_id": "call_…",
"arguments": { "command": "ls -la" }
}
}
```
### Output, stdout
Three accepted shapes, tried in this order:
1. **Empty stdout**: exit 0 = allow; non-zero = block (stderr surfaced as the block reason). This is the Git-hook escape hatch.
2. **JSON decision**: stdout starts with `{`:
```json
{
"decision": "allow" | "mutate" | "block",
"reason": "string shown in audit/UI",
"user_message": "string surfaced to the user when blocking",
"patch": { /* event-specific mutation */ }
}
```
3. **Anything else** → executor error → `on_error` policy applies.
### Limits
* **Timeout**: configurable per hook; default 5 s, max 30 s.
* **Output size**: 64 KiB total (stdout + stderr).
* **Sandbox**: runs through `bashkit_shell` against the session VFS. No host shell. Inherits the session’s egress policy.
* **stderr**: captured into the audit log; never shown to the model unless `decision == "block"` with no `reason`.
## Composition
Hooks chain in capability-declaration order, then array order within each capability. The first `block` decision wins; mutations from earlier hooks survive even if a later hook blocks.
Tool approval and `tool_use` guardrails always run after your `pre_tool_use` hooks, whatever order the capabilities are declared in. They judge the arguments your hooks produced, so a `mutate` decision cannot turn an approved call into one nobody approved, and an approval request shows the rewritten arguments. A hook that blocks a call means no one is asked to approve it.
When a `mutate` decision changes the arguments of a call that then runs, the `tool.completed` event records the arguments that ran in `executed_arguments`, next to the original call on `tool.started`.
Capabilities other than `user_hooks` can ship hook bundles, see [Hook bundles from other capabilities](#hook-bundles-from-other-capabilities). To mute a bundled hook, list its `HookId` under `disabled_contributions`:
```json
{
"capabilities": [
{ "ref": "rust_quality_pack" },
{ "ref": "user_hooks", "config": {
"disabled_contributions": ["rust_quality_pack:fmt_after_edit"]
} }
]
}
```
`HookId` format:
* Capability contribution: `{capability_id}:{name}`
* User config: `user:{name}`
## Hook bundles from other capabilities
Any built-in capability can contribute hook specs to your agent by overriding `Capability::user_hooks_with_config` and returning a list of `UserHookSpec`s. The `guarded-bash-demo` seed agent is a live example: its `user_hooks` capability config ships a `pre_tool_use` hook that refuses destructive `rm -rf` invocations before the bash tool ever runs, with no extra setup.
Capability-contributed hooks ride the trust gate of enabling the contributing capability, so admin assignment rules still apply. Operators can mute any individual contribution via `disabled_contributions` on a sibling `user_hooks` capability config.
> Declarative-capability hook bundles (one POST to register, many agents to reuse) are on the roadmap and are not yet wired into the declarative capability schema.
See the [hook bundle examples](https://github.com/everruns/everruns/tree/main/examples/hook-bundles) for ready-to-paste user-config bundle JSON.
## Examples
### Block `rm -rf` from the bash tool
```json
{
"capabilities": [
{
"ref": "bashkit_shell"
},
{
"ref": "user_hooks",
"config": {
"hooks": [
{
"id": "guard_rm",
"event": "pre_tool_use",
"matcher": {
"tool_name": "bash",
"args_jsonpath": "$.commands",
"deny_regex": "(?:^|;|&&|\\|)\\s*rm\\s+-rf\\b"
},
"executor": {
"type": "bash",
"command": "echo '{\"decision\":\"block\",\"reason\":\"rm -rf is not allowed\",\"user_message\":\"That command is blocked by policy.\"}'"
},
"on_error": "block",
"description": "Reject destructive rm invocations"
}
]
}
}
]
}
```
### Format Rust files after every edit
```json
{
"capabilities": [
{ "ref": "session_file_system" },
{ "ref": "bashkit_shell" },
{
"ref": "user_hooks",
"config": {
"hooks": [
{
"id": "fmt_rs",
"event": "post_tool_use",
"matcher": {
"tool_name": "edit_file",
"args_jsonpath": "$.path",
"match_regex": "\\.rs$"
},
"executor": {
"type": "bash",
"command": "cargo fmt --check 2>&1 || cargo fmt"
},
"on_error": "warn"
}
]
}
}
]
}
```
### Seed a workspace on session start
```json
{
"capabilities": [
{ "ref": "session_file_system" },
{ "ref": "bashkit_shell" },
{
"ref": "user_hooks",
"config": {
"hooks": [
{
"id": "init",
"event": "session_start",
"executor": {
"type": "bash",
"command": "echo 'session bootstrapped' > /workspace/.bootstrap && echo '{}'"
},
"on_error": "warn",
"description": "Drop a sentinel file the agent can read"
}
]
}
}
]
}
```
### Block a user prompt that pastes a secret
`user_prompt_submit` is the only lifecycle event that can **block**: a `block` decision aborts the turn before the LLM runs. The original prompt text arrives in `data.message`. Here the hook reads it, and if it looks like a pasted private key, rejects the turn with a message shown to the user. `on_error: "block"` makes the hook fail closed.
```json
{
"capabilities": [
{ "ref": "bashkit_shell" },
{
"ref": "user_hooks",
"config": {
"hooks": [
{
"id": "block_secrets_in_prompt",
"event": "user_prompt_submit",
"executor": {
"type": "bash",
"command": "msg=$(echo \"$EVERRUNS_HOOK_PAYLOAD_JSON\" | jq -r '.data.message'); if printf '%s' \"$msg\" | grep -qiE 'BEGIN (RSA|OPENSSH|EC|DSA) PRIVATE KEY'; then echo '{\"decision\":\"block\",\"reason\":\"prompt contains a private key\",\"user_message\":\"Your message looks like it contains a private key and was blocked. Remove the secret and resend.\"}'; else echo '{}'; fi"
},
"timeout_ms": 5000,
"on_error": "block",
"description": "Reject prompts that paste a private key"
}
]
}
}
]
}
```
`user_prompt_submit` can also **mutate** the prompt instead of blocking, emit `{"decision":"mutate","patch":{"message":""}}` and the turn proceeds with the rewritten text. For example, to prepend a house style reminder:
```jsonc
{
"id": "prepend_style_note",
"event": "user_prompt_submit",
"executor": {
"type": "bash",
"command": "echo \"$EVERRUNS_HOOK_PAYLOAD_JSON\" | jq -c '{decision:\"mutate\",patch:{message:(\"[reminder: follow the house style guide]\\n\" + .data.message)}}'"
},
"on_error": "warn"
}
```
### Log every completed turn
`turn_end` is advisory, its decision is ignored, so use it for side effects like metrics or audit trails. The payload’s `data.success` reports whether the turn finished cleanly.
```json
{
"capabilities": [
{ "ref": "session_file_system" },
{ "ref": "bashkit_shell" },
{
"ref": "user_hooks",
"config": {
"hooks": [
{
"id": "log_turn_end",
"event": "turn_end",
"executor": {
"type": "bash",
"command": "echo \"$EVERRUNS_HOOK_PAYLOAD_JSON\" | jq -r '.ts + \" turn \" + .turn_id + \" success=\" + (.data.success | tostring)' >> /workspace/.turn-log; echo '{}'"
},
"timeout_ms": 3000,
"on_error": "warn",
"description": "Append one line per completed turn to /workspace/.turn-log"
}
]
}
}
]
}
```
## Observability
Today, hook decisions and errors are recorded in the server logs via `tracing`: blocks, ignored post-hook blocks, mutations, `on_error` outcomes, and `disabled_contributions` mutings are logged with the resolved `hook_id` and the `tool_call_id`.
**Planned (deferred):** structured `hook.invoked` / `hook.completed` / `hook.blocked` / `hook.warning` events emitted through the same observability pipeline as tool events (Braintrust, OTel). These are not emitted yet, see the spec’s observability section.
## Event firing
All six events fire:
* **`pre_tool_use`**: before each tool call; can block or mutate the call.
* **`post_tool_use`**: after each tool call; can mutate the result.
* **`session_start`**: after a session is created (mounts + initial files in place); advisory.
* **`session_end`**: when a session is deleted, before VFS eviction; advisory.
* **`user_prompt_submit`**: on the first reason iteration, before the LLM is consulted; can block (aborts the turn with a user-facing message) or mutate the user message text.
* **`turn_end`**: when a turn reaches a terminal outcome; advisory.
Note on `user_prompt_submit` blocking: the API persists the user message and runs the turn asynchronously, so a block does not reject the HTTP request, it aborts the turn. The session shows a `turn.failed` outcome carrying the hook’s `user_message`.
## See also
* [Capabilities](https://everruns.com/docs/capabilities/), compose tools and runtime behavior
* [Guardrails](https://everruns.com/docs/capabilities/guardrails/), runtime safeguards
* [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/), the sandbox that runs hook commands
---
# User MCP Servers
> Give an agent the MCP servers of the person chatting with it, signed in as that person, and optionally let it add and connect them in chat.
Source:
| | |
| ---------------- | ------------ |
| **ID** | `user_mcp` |
| **Category** | Integrations |
| **Features** | None |
| **Dependencies** | None |
| **Risk** | Medium |
Adds the [user MCP servers](https://everruns.com/docs/features/user-mcp-servers/) of the person who sent the current message to the agent’s MCP servers for that turn. Each server signs in as that person. With `use` alone the capability adds no tools of its own; the tools come from the person’s servers. With `manage` on, the agent can also change the person’s list in chat.
## When to enable
* A personal assistant such as Platform Chat, where each person brings their own Linear, Notion or internal servers.
* Any agent people talk to directly and should be able to extend for themselves without editing the agent.
Leave it off for agents that run unattended or in shared channels: they get nothing from it.
## Configuration
| Setting | Default | Effect |
| ------------------- | ------- | ----------------------------------------------------------------------------------------------- |
| `use` | `true` | Add the person’s enabled servers to each turn. |
| `manage` | `false` | Give the agent the tools below to change the person’s list. |
| `allow_custom_urls` | `false` | With `manage`, let the agent add a server by URL, not only from the organization’s MCP catalog. |
```json
{ "ref": "user_mcp", "config": { "use": true, "manage": true } }
```
## Tools
Added only when `manage` is on. They act on the list of the person who sent the message, the same list as **Settings > My MCP servers**, and never on the organization catalog.
| Tool | What it does | Shared approval |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `list_user_mcp_servers` | Lists the person’s servers and those added to this conversation only: enabled, signed in, and whether this agent skips one because its own server has the same name | No |
| `add_user_mcp_server` | Adds a catalog server (`{"catalog": "linear"}`), or a server by URL when `allow_custom_urls` is on. With `"scope": "chat"` it is added to this conversation only | In normal/protective mode |
| `remove_user_mcp_server` | Removes a server, from this conversation or from the list | In protective mode |
| `enable_user_mcp_server` | Turns a server on | In normal/protective mode |
| `disable_user_mcp_server` | Turns a server off without removing it | In protective mode |
| `connect_mcp_server` | Shows the person a Connect card for a server that needs a sign-in | No |
`add_user_mcp_server` and `enable_user_mcp_server` declare that they require approval through the agent’s shared [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/) policy. With `tool_approval` enabled in `normal` or `protective` mode, they wait for approval in chat. With the capability absent or its mode set to `off`, they run without a hard approval gate. `user_mcp` adds no separate gate or approval setting. Other management tools follow the same mode and their tool hints; `remove` and `disable` only take tools away.
OAuth sign-in remains a separate authorization step, regardless of the tool approval mode.
A server added or enabled in a turn is usable from the person’s next message.
### This conversation only
`add_user_mcp_server` with `"scope": "chat"` adds the server to the current conversation instead of the person’s list. It joins every later turn of this conversation, loads on demand like the person’s other servers, and never appears in **Settings > My MCP servers** or in their other conversations. `remove_user_mcp_server` takes it away from the next message. The same rules apply as for the list: the shared approval policy, catalog servers only unless `allow_custom_urls` is on, and no keys or headers. A catalog server signs in as the person, as it would from their list. A custom server that signs in with OAuth cannot be added this way, because its sign-in needs the list entry; add it to the list instead. A name that one of the agent’s own servers, or a resource attached through [Resource Discovery](https://everruns.com/docs/integrations/ard/), already uses in this conversation is refused.
In Chat, an **MCP** button in the conversation header lists these servers, with their host and whether you have signed in, and removes one with a click. The same list is `GET /v1/sessions/{session_id}/mcp-servers`, and `DELETE /v1/sessions/{session_id}/mcp-servers/{name}` removes one. `connect_mcp_server` never sees a credential: the person signs in in their own browser, as with any Connect card.
`connect_mcp_server` is also added on its own, without `manage` and without the capability being configured, when one of the agent’s own MCP servers acts as `user` or `user_or_service`, so the agent can offer the sign-in before a call fails. For a server that acts as the agent (`service`), the card leads to the agent’s **MCP servers** sheet, where only someone allowed to manage MCP servers can authorize it; anyone else is told to ask an admin. An agent server set to `connectInChat: never` gets no card: the tool returns the same settings link for the agent to pass on (see [Connecting from chat](https://everruns.com/docs/features/mcp/#connecting-from-chat)). Servers added in chat cannot carry API keys or headers; the person adds those in Settings.
## Behaviour
* Only the person who sent the message counts, never the session owner or the agent’s own account. Unattended runs get no servers.
* Sessions with more than one person get no servers.
* An agent or capability server with the same name wins; the person’s server is skipped. `list_user_mcp_servers` and the agent’s **MCP servers** sheet report it.
* The manage tools refuse to run without a person (unattended runs) and in sessions with more than one person, with a message saying so.
* A server that fails validation is skipped on its own; the rest still load.
* The person’s servers load on demand by default: the agent sees one line per server (`mcp_`) and loads a server’s tools through `tool_search`, or by calling that line, when it needs them. They are callable from its next step and stay loaded for the rest of the conversation (see [Loading tools on demand](https://everruns.com/docs/features/mcp/#loading-tools-on-demand)). Turning off **Load tools on demand** on a server in **Settings > My MCP servers** (`deferred: false` when adding or updating it under `/v1/virtual-users/{id}/mcp-servers`) lists its tools at the start of every turn instead. Servers added for one conversation only always load on demand.
## Risk
Medium. The agent can call any tool on servers the person chose, with the person’s login. Tool approval and the agent’s other guardrails apply to these tools as to any MCP tool.
With `manage`, adding a server is how a prompt injection would try to send the person’s data somewhere new. Enable Tool Approval in `normal` or `protective` mode to hold add and enable calls for the person’s approval. Leaving approval absent or off allows these calls to run autonomously. Custom URLs remain off by default, and every URL goes through the same address checks as the API.
---
# Web Fetch
> Fetch a URL and convert its HTML to markdown or plain text, with SSRF protection and structured fetchers for common developer and research sources.
Source:
| | |
| ---------------- | ----------- |
| **ID** | `web_fetch` |
| **Category** | Network |
| **Features** | None |
| **Dependencies** | None |
Fetch content from URLs and convert HTML to markdown or plain text. Powered by [FetchKit](https://github.com/everruns/fetchkit) with built-in SSRF protection, low-noise extraction, bounded crawl discovery, and structured fetchers for common developer and research sources.
## Tools
### `web_fetch`
Fetch a URL and return its content.
| Parameter | Type | Required | Description |
| ------------------- | ------- | -------- | ----------------------------------------------------------------------- |
| `url` | string | yes | URL to fetch |
| `method` | string | no | `GET` (default), `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`, or `OPTIONS` |
| `headers` | object | no | Request headers, such as `Authorization` |
| `body` | string | no | Raw request body |
| `json` | any | no | JSON request body; sets `Content-Type: application/json` |
| `form` | object | no | Form fields, sent as `application/x-www-form-urlencoded` |
| `as_markdown` | boolean | no | Convert HTML to markdown |
| `as_text` | boolean | no | Convert HTML to plain text |
| `content_focus` | string | no | Extraction mode: `full`, `main`, `readable`, or `agent` |
| `crawl` | boolean | no | Discover and fetch a bounded set of same-origin pages |
| `max_pages` | integer | no | Maximum crawl pages, including the seed (default: 5, maximum: 20) |
| `if_none_match` | string | no | ETag for a conditional request |
| `if_modified_since` | string | no | Last-Modified value for a conditional request |
| `save_to_file` | string | no | Workspace destination when file download is enabled for the capability |
Returns: content body, status code, metadata, quality signals, redirect history, and crawl summaries when requested.
### API requests
A method other than `GET` or `HEAD`, or any of `headers`, `body`, `json`, or `form`, sends one plain HTTP request instead of a page fetch. The result is the status code, response headers, and the response text as received, without markdown conversion. Redirects are returned rather than followed. This lets an agent follow a service’s own instructions for agents, such as an `auth.md` agent registration followed by a token request:
```json
{"url": "https://example.com/oauth/token", "method": "POST",
"form": {"grant_type": "client_credentials", "client_id": "..."}}
```
API requests need the host’s egress service, so they pass the same network access and system egress policy checks as fetches. On deployments that restrict egress (such as Everruns Cloud), plain reads can reach any public site, while requests that send data (a method other than GET or HEAD, or a body) only reach the platform’s list of approved services. Request and response bodies are limited to 256 KB, and the request times out after 30 seconds.
If your organization needs to send data to its own services on such a deployment, a platform administrator can allow it to extend the list. Org admins then add their hosts under **Settings > Organization > Outbound allowlist**, one per line (`api.example.com`, `*.example.com`, or an `https://example.com/path/` prefix, up to 50, public hostnames only). The extra hosts apply to your organization’s traffic only, changes take effect within a minute, and the platform’s blocked hosts stay blocked.
## Notes
* **Timeouts**: 1s for first byte, 30s for body. Partial content returned on body timeout.
* **Binary content**: Images, PDFs, etc. return metadata only (content type, size), not the binary data.
* **Focused extraction**: Use `content_focus: "agent"` for FetchKit’s lowest-noise extraction strategy.
* **No JavaScript rendering**: Web Fetch returns the server’s response as delivered. Pages that build their content client-side need a browser capability such as [Browserless](https://everruns.com/docs/capabilities/browserless/).
* **Crawl scope**: Crawl discovery stays on the seed URL’s origin and enforces FetchKit’s page limit.
* **SSRF protection**: Private IPs (loopback, RFC1918, link-local, CGNAT) are blocked by default with DNS pinning to prevent rebinding attacks.
* **Excessive newlines**: Automatically filtered from converted content.
## See Also
* [File System](https://everruns.com/docs/capabilities/file-system/), save fetched content to workspace
* [Capabilities Overview](https://everruns.com/docs/capabilities/)
## Write-only credentials
Use `$NAME` in the exact header or form field authorized by a credential binding. `credential_usage` explicitly selects `service` (default) or the verified `user`. It never falls back to another identity. For example:
```json
{
"url": "https://mail.example/api/messages",
"method": "POST",
"credential_usage": "user",
"headers": { "Authorization": "Bearer $MAIL_TOKEN" },
"json": { "to": "recipient@example.com", "subject": "Hello" }
}
```
To capture an authentication response without showing its token to the model, declare every expected secret field:
```json
{
"url": "https://mail.example/oauth/token",
"method": "POST",
"credential_usage": "user",
"form": { "grant_type": "refresh_token", "refresh_token": "$MAIL_REFRESH" },
"capture_secrets": [
{
"name": "MAIL_TOKEN",
"from": "/access_token",
"scope": "virtual_user",
"binding": {
"url": "https://mail.example/api",
"auth": { "type": "bearer" }
},
"expires_in_from": "/expires_in"
}
]
}
```
The user approves persistent storage, replacement, and cross-origin bindings before any request is sent. The result contains `$MAIL_TOKEN` instead of the value. Up to eight captures save together; malformed JSON, duplicate keys, missing fields, interrupted/oversized responses, or storage failures withhold the response and save no partial values. Credential requests have a 256 KiB response limit and cannot crawl, render, extract, or save files.
Bindings support bearer headers, named headers, and named form fields (`{"type":"form_field","name":"refresh_token"}`). References in URLs, arbitrary bodies, JSON values, or mixed strings are rejected. Redirects are returned without following them. Known literal, JSON-escaped, and form-encoded token echoes are redacted before the result enters chat or events. Declare all claim tokens, assertions, access tokens, and refresh tokens the service returns; arbitrary encodings such as base64 are outside this redaction guarantee.
Manage personal credentials in **Settings > Connections**, service credentials on their virtual user, agent credentials on the agent’s **Credentials** tab, organization credentials in **Settings > Organization**, and session credentials from the session’s **Credentials** action. Values are write-only. Expired or revoked bindings fail with renewal/reconnect guidance.
---
# Web Search
> One web_search tool answered by the search provider you choose, with free search as the fallback.
Source:
| | |
| ---------------- | ------------ |
| **ID** | `web_search` |
| **Category** | Network |
| **Features** | None |
| **Dependencies** | None |
Web Search gives an agent one `web_search` tool. Where a search goes is a setting on the capability, not a different tool: the agent keeps the same tool, prompts and history when you switch providers or models. The Worker harnesses include it, so a new worker agent can search with no key and no setup; a Conversation agent adds it on the agent.
## Providers
| Setting | What answers | Cost |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| **Organization default** (`org_default`) | The organization’s default provider (see below); Automatic for a new organization | Depends on the provider |
| **Automatic** (`auto`) | The organization’s Automatic order: the model’s own search, then the organization’s search accounts, then the deployment’s own search when it offers one, then free search | Depends on the provider that answers |
| **Model provider search** (`native`) | The model’s own search, on OpenAI, Anthropic, Gemini and OpenRouter models that have it (see below). Other models use free search. | Billed by the model provider |
| **Brave Search** (`brave`) | The Brave Search API with a user, session or application key, else the organization’s first Brave account | Billed on the Brave key |
| **Parallel** (`parallel`) | The Parallel Search API (`fast` mode) with a user, session or application key, else the organization’s first Parallel account | Billed on the Parallel key, about $1 per 1,000 searches |
| **A search account** (`account`, with `account_id`) | One organization search account, such as “Brave Search · Team”, “Parallel · Research” or a [Cloudflare Web Search](https://everruns.com/docs/integrations/cloudflare-web-search/) account on Ceramic, Exa or Linkup | Billed on that account’s key, or for Cloudflare on its AI Gateway credits ($0.25 per 1,000 searches on Ceramic) |
| **Free search** (`free`) | DuckDuckGo, then DuckDuckGo Lite, then Mojeek, then Wikipedia | Free |
Free search is the last fallback of every setting: when the chosen provider has no key, rejects it, is rate limited or is unavailable, free search answers and the run records why. Set `fallback` to `none` to use only the chosen provider.
## Model provider search
When model provider search comes first in an agent’s plan, the model searches with its provider’s own search instead of calling `web_search`: the provider runs the searches inside the model call and the answer cites its sources. This is decided on every turn, for the model running that turn and the organization’s current defaults, so a session that switches models keeps searching:
| Model provider | Native search |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| OpenAI, Azure OpenAI, ChatGPT Plan (framework hosts) | OpenAI web search, on models that have it (GPT-4o, GPT-4.1, GPT-5 and later, o3, o4-mini) |
| OpenRouter | The model vendor’s own search, on models OpenRouter runs it for (OpenAI GPT-4.1 and GPT-5 and later, Claude, Gemini 3, Grok 4, Perplexity) |
| Anthropic | Anthropic web search, on Claude 3.5 Haiku, Claude 3.7 Sonnet and Claude 4 and later, up to 5 searches per model call |
| Gemini | Grounding with Google Search, on Gemini 2.0 Flash, 2.5 and 3 and later, when `web_search` is the agent’s only tool |
| Others | None: the next provider in the plan answers |
The agent’s domain filters and the organization’s blocked domains go with each request. A provider that cannot apply them leaves the `web_search` tool in place, and the next provider in the plan answers: OpenAI search cannot exclude sites, so an agent or organization with blocked domains searches with its next provider on OpenAI models. Anthropic takes allowed or blocked sites but not both at once, and Gemini takes neither. Gemini also cannot yet search natively next to the agent’s other tools, so an agent with more tools than `web_search` searches with its next provider on Gemini models. On OpenRouter, native search is only requested for models that have it, so OpenRouter’s own fallback search never answers in place of the plan.
Each native search is billed by the model provider: about $10 per 1,000 searches on OpenAI and Anthropic, $14 per 1,000 search queries on Gemini 3 and $35 per 1,000 grounded answers on Gemini 2.x. The model call records it as a `hosted_tool` cost component (`web_search_call`, with the number of searches) and budgets count it. In chat, a native search shows as a **Searching the web for ”…”** row.
The OpenAI and OpenRouter server tools capabilities keep their own `web_search` option. When one of them already turns on its provider’s search, that configuration is used.
## In the UI
**Registers > Web search** has two tabs, like Models:
* **Providers**: a card per way the organization can search. **Model provider search** lists which connected model providers offer it and its price; each search account shows whether its key is set and its place in Automatic; a deployment that pays for a search provider itself (Everruns search on Everruns Cloud) shows it with its price; **Free search** is always there. Admins connect an account with **Connect search provider**, and test, rotate or disconnect it from its card.
* **Defaults**: the default provider, the Automatic order (move entries up or down; free search stays last), **Allow model provider search** with its cost, what happens when a provider fails, results per search, and blocked domains.
In the agent editor, expand **Web search** under Capabilities to pick the provider (Organization default, Automatic, Model provider search, a search account or Free search). **How this agent will search** says, for the agent’s default model, which provider answers first, what it costs and what comes next. Results per search, freshness, fallback and domain filters follow.
In chat, each search is one **Searched ”…”** row with the result count, the provider that answered, the duration, and a note when an earlier provider failed. Expand it to see the top results, then open **Details and raw response** for every attempt and each provider’s verbatim response. In Session Trace, a `web_search` step lists its attempts, with a failed one in red.
## Organization search accounts and defaults
An organization admin connects search accounts as organization connections (`POST /v1/orgs/{org}/organization-connections/providers/brave_search`, `.../parallel` or `.../cloudflare_web_search`), the same way sandbox provider accounts are connected. Every agent of the organization can then search with them; the key never reaches the agent.
`PATCH /v1/orgs/{org}` with `web_search_defaults` sets the organization’s defaults:
| Field | Default | Description |
| -------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `provider` | `auto` | What `org_default` agents use: `auto`, `native`, `brave`, `parallel`, `account` (with `account_id`) or `free` |
| `automatic_order` | `["native", "free"]` | Order of Automatic: `native`, search account ids and `hosted` (the deployment’s own search, when it offers one). Free search is always last; entries left out keep their place (native first, other accounts after the listed ones, `hosted` just before free search) |
| `allow_native` | `true` | Whether agents may use the model provider’s own search (billed by the model provider) |
| `fallback` | `next` | Fallback for agents that do not set their own |
| `results_per_search` | 8 | Results per search when neither the model nor the agent asks, 1 to 20 |
| `blocked_domains` | none | Sites no search returns, on top of each agent’s `blocked_domains` |
Changes reach running sessions from their next search, within 30 seconds. `GET /v1/search-providers` lists what the organization can search with, and `POST /v1/search-providers/preview` shows the plan an agent’s setting resolves to for a model.
For `brave`, a key is read from the user’s Brave Search connection, then the `BRAVE_SEARCH_API_KEY` session secret, then (Framework only) the `BRAVE_SEARCH_API_KEY` environment variable, then the organization’s first Brave Search account. `parallel` reads keys the same way: the user’s Parallel connection, then the `PARALLEL_API_KEY` session secret, then (Framework only) the `PARALLEL_API_KEY` environment variable, then the organization’s first Parallel account.
Parallel searches its own web index and returns longer excerpts than Brave, so results carry an `excerpt` the model can quote. It applies the agent’s allowed or blocked sites and freshness itself.
A [Cloudflare Web Search](https://everruns.com/docs/integrations/cloudflare-web-search/) account searches the index it was connected with (Ceramic, Exa or Linkup) and returns at most 10 results. The API has no site or date filters, so allowed and blocked sites are applied to its results and freshness is not.
Free search is best effort. Search engines sometimes answer automated traffic with a bot check; free search treats that as a failure and moves to the next source instead of reporting no results. Answers are cached for ten minutes, and requests to one search engine are spaced out.
## Settings
| Field | Default | Description |
| ----------------- | ------------------ | ---------------------------------------------------------------------- |
| `provider` | `org_default` | One of the settings above |
| `account_id` | none | The search account, with `provider: account` |
| `fallback` | organization’s | `next` falls back to the next provider, then free search; `none` stops |
| `limit` | organization’s (8) | Results per search, 1 to 20 |
| `freshness` | none | `day`, `week`, `month` or `year` |
| `allowed_domains` | none | Only results from these sites |
| `blocked_domains` | none | Never results from these sites |
| `show_provider` | `false` | Tell the model which provider answered and why |
```json
{"ref": "web_search", "config": {"provider": "brave", "blocked_domains": ["pinterest.com"]}}
```
## One search per agent
Provider-specific search capabilities stay available for agents built around one provider: Brave Search, DuckDuckGo, Parallel Search and Fetch, Parallel Paid Search, and the OpenAI and OpenRouter server tools with their `web_search` tool on. Each says whose search it is. An agent that has two of these, or one of them next to an MCP server’s `web_search` tool, gets a `capabilities.duplicate_web_search` warning from [Agent Checks](https://everruns.com/docs/features/agent-checks/). Two search tools make the model guess which one to use. The warning never blocks a save.
## Tools
### `web_search`
| Parameter | Type | Required | Description |
| ----------- | --------- | -------- | ------------------------------------------------------------------- |
| `query` | string | yes | What to search for |
| `limit` | integer | no | How many results, 1 to 20 |
| `freshness` | string | no | `day`, `week`, `month` or `year` |
| `domains` | string\[] | no | Only results from these sites; cannot widen the agent’s own filters |
| `topic` | string | no | `dev` also searches GitHub, npm and crates.io (free search) |
Returns the query and a list of results, each with an `id`, `title`, `url`, `site`, and when known `published`, `snippet` and `excerpt`. The model cites a claim with the result’s `url` or `id`; an `id` stays the same for the same URL throughout a session. Free search adds an `answer` when an encyclopedia abstract matches.
To show those citations as numbered source chips in the chat, also add [Web Citations](https://everruns.com/docs/capabilities/citation-web/) (`citation_web`). It covers model provider search too, from the citations the provider returns.
Which provider answered, every attempt with its reason, and each provider’s raw response are kept on the tool call for people (the run’s details and Session Trace). The model sees them only with `show_provider`.
## Framework
Enable the `web-search` feature and add the capability:
```rust
use everruns::WebSearch;
let agent = Agent::builder()
// ...
.capability(WebSearch::auto())
.build()?;
```
`WebSearch::brave().no_fallback()`, `WebSearch::parallel()`, `WebSearch::free()`, `.limit(5)` and `.blocked_domains(["pinterest.com"])` set the other options. The builder writes only config; put a Brave key in `BRAVE_SEARCH_API_KEY` and a Parallel key in `PARALLEL_API_KEY`.
---
# Changelog
> What changed in Everruns Cloud, the self-hosted Platform, the Framework, SDKs, CLI, Yolop, Mira and Bashkit, one short dated entry per change.
Source:
AllFeatureImprovementFixBreakingRelease
Major only
## October 2026
190 changes · 23 releases
1. October 26, 2026
1. ### [Agents see the screenshots and images their tools return](https://everruns.com/docs/changelog/2026-10-26-tool-images-reach-model/)
Images from tool results, such as computer-use screenshots, reach the model as images again instead of as truncated base64 text.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
2. ### [Everruns 0.49.0: Thousands of MCP tools, GitHub sync and a Helm chart](https://everruns.com/docs/changelog/2026-10-26-everruns-0-49-0/)
MCP servers with 2,000+ tools and MCP Tasks, Agent source synced with GitHub, scoped credentials, Knowledge, and a Helm chart with everruns docker up.
[](https://everruns.com/docs/changelog/2026-10-26-everruns-0-49-0/)
ReleaseMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
2. October 25, 2026
1. ### [Edit GitHub-synced Agents from either side](https://everruns.com/docs/changelog/2026-10-25-two-way-agent-github-sync/)
Enable two-way Agent source sync to commit saved Everruns edits to GitHub, retain local changes on errors, and review conflicts before replacing them.
[](https://everruns.com/docs/changelog/2026-10-25-two-way-agent-github-sync/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
2. ### [Sandboxes are disposable, their records are kept](https://everruns.com/docs/changelog/2026-10-25-sandbox-retention/)
Paused Sandboxes are deleted after a day, a Delete idle action drops disposable ones at once, and every Sandbox record is now kept forever.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
3. ### [Give a Daytona or E2B sandbox a desktop](https://everruns.com/docs/changelog/2026-10-25-sandbox-desktop/)
A Sandbox Template option starts the Session's Sandbox with a Linux desktop the agent sees and operates with the computer tool, next to its shell and files.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
4. ### [Personal settings are separate pages](https://everruns.com/docs/changelog/2026-10-25-personal-settings-pages/)
Profile, Connections, MCP servers, and AI clients each have their own Settings page, replacing the combined My agent experience page.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)
5. ### [Live event delivery recovers after a NATS restart](https://everruns.com/docs/changelog/2026-10-25-nats-stream-recovery/)
Session streaming restores its JetStream replay buffer after a broker restart, without an API restart or duplicate retries after uncertain acknowledgements.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
6. ### [Install the self-hosted Platform on Kubernetes with Helm](https://everruns.com/docs/changelog/2026-10-25-helm-chart/)
An official Helm chart installs the self-hosted Platform on Kubernetes, ships as an OCI chart with each release, and keeps its secrets across upgrades.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
7. ### [Chat folds a thread report into one line](https://everruns.com/docs/changelog/2026-10-25-folded-thread-reports/)
When a thread finishes, asks, or fails, Chat shows one folded line. Open it to read the report. The coordinator still gets the full text.
[](https://everruns.com/docs/changelog/2026-10-25-folded-thread-reports/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
8. ### [Run Everruns on your machine with one command](https://everruns.com/docs/changelog/2026-10-25-everruns-docker-up/)
everruns docker up asks a few questions, starts the self-hosted Platform in Docker and signs the CLI in; running it again is safe.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
9. ### [Limit outbound network for Daytona and E2B sandboxes](https://everruns.com/docs/changelog/2026-10-25-daytona-network-rules/)
Daytona and E2B Sandbox Templates can block the network or allow only listed domains, enforced by the provider together with the Session's network access list.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
10. ### [Bashkit Python builds on Windows again](https://everruns.com/docs/changelog/2026-10-25-bashkit-windows-python/)
Bashkit's embedded Python no longer crashes the Rust compiler on Windows, while preserving module alignment and Python in the hosted integration catalog.
Fix[ Bashkit](https://everruns.com/docs/changelog/bashkit/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
3. October 24, 2026
1. ### [Work reports show what an agent is doing and got done](https://everruns.com/docs/changelog/2026-10-24-work-reports/)
Each turn gets one report of its plan, steps, what it waits on and its result, shown in chat and the sessions list and returned by the API.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
2. ### [Publish a dedicated Work Chat for an Agent](https://everruns.com/docs/changelog/2026-10-24-hosted-work-chat/)
Self-hosted deployments can publish an Agent with separate consumer sign-in, a workspace panel and a choice of conversation interfaces.
[](https://everruns.com/docs/changelog/2026-10-24-hosted-work-chat/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
3. ### [Agent actions and searchable Playground setup](https://everruns.com/docs/changelog/2026-10-24-agent-actions-playground-search/)
Find users and agents in Playground setup, connect GitHub from the Agent menu, and distinguish dry runs from normal Playground chats before starting.
[](https://everruns.com/docs/changelog/2026-10-24-agent-actions-playground-search/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
4. October 23, 2026
1. ### [Knowledge unifies files and optional indexed search](https://everruns.com/docs/changelog/2026-10-23-unified-knowledge-files/)
Mount notes, source trees and binary originals through one Knowledge feature, with Markkit text extraction and optional keyword or hybrid retrieval.
[](https://everruns.com/docs/changelog/2026-10-23-unified-knowledge-files/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
2. ### [Sync Agent source continuously from GitHub](https://everruns.com/docs/changelog/2026-10-23-sync-agent-source-github/)
Connect an Agent to a GitHub repository package, review its changes, and apply future updates automatically while keeping source fields read-only.
[](https://everruns.com/docs/changelog/2026-10-23-sync-agent-source-github/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
3. ### [MCP tool calls can run longer than 60 seconds](https://everruns.com/docs/changelog/2026-10-23-mcp-tool-timeout/)
Each MCP server can set how long one tool call may run, from 1 second to 30 minutes, instead of the fixed 60-second limit.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
4. ### [MCP Tasks run one agent turn per task](https://everruns.com/docs/changelog/2026-10-23-mcp-tasks-per-turn/)
Agent runs over /mcp follow the published MCP Tasks extension, with one task per message, the reply as the task result, and official client support.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
5. ### [Agents wait on MCP servers that run tools as tasks](https://everruns.com/docs/changelog/2026-10-23-mcp-tasks-client/)
Everruns agents now accept MCP Tasks from the servers they call, checking on long tool calls until they finish instead of hitting the request timeout.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
6. ### [Chat mode keeps Agent edits beside the conversation](https://everruns.com/docs/changelog/2026-10-23-chat-beside-agent-pages/)
Opt into Chat mode to edit the live Agent form with Platform Chat, preserve unsaved work, and review changes before saving.
[](https://everruns.com/docs/changelog/2026-10-23-chat-beside-agent-pages/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
5. October 21, 2026
1. ### [Large MCP catalogs use bounded search and schemas](https://everruns.com/docs/changelog/2026-10-21-large-mcp-catalogs/)
MCP catalogs load across pages, search server and schema text within current access, and reveal bounded tool schemas through native calls, CLI, or both.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
6. October 19, 2026
1. ### [Send host-written review completions on Framework sessions](https://everruns.com/docs/changelog/2026-10-19-session-curated-completions/)
Session::complete sends exactly the system prompts and messages a host chooses, tool-less, through the session's current provider and model.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
2. ### [Credentials work across users, agents and sessions](https://everruns.com/docs/changelog/2026-10-19-scoped-credentials-secure-capture/)
Bind write-only credentials to a user, agent, session or organization, grant agents explicit access, and capture API tokens with exact-request consent.
[](https://everruns.com/docs/changelog/2026-10-19-scoped-credentials-secure-capture/)
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
3. ### [Harness capabilities keep their name and details](https://everruns.com/docs/changelog/2026-10-19-harness-capability-metadata/)
A harness row for a capability hidden by a rollout flag now shows its name, icon, and description, and links to the capability page.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
4. ### [Gemini citations survive every continuation hop](https://everruns.com/docs/changelog/2026-10-19-gemini-continuation-grounding/)
Long Gemini responses retain citations from each grounded request and count repeated searches across hops while preserving combined token usage.
Fix[ Framework](https://everruns.com/docs/changelog/framework/)[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
5. ### [Run commands and hot-install capabilities on live sessions](https://everruns.com/docs/changelog/2026-10-19-engine-session-controls/)
Framework sessions run slash commands, activate host-registered capabilities and switch model, effort and MCP servers without a runtime handle.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
6. ### [Bring host capabilities and providers into Engine sessions](https://everruns.com/docs/changelog/2026-10-19-engine-host-services/)
Embedding hosts attach their own capabilities, provider resolution, retry budgets, MCP credentials, request attribution and observers to Engine sessions.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
7. October 18, 2026
1. ### [Yolop searches with the shared web_search tool](https://everruns.com/docs/changelog/2026-10-18-yolop-web-search/)
Yolop's default harness now has web_search: the model's own search first, then Brave or Parallel with a key, then free search.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Tenant egress proxy for self-hosted deployments](https://everruns.com/docs/changelog/2026-10-18-tenant-egress-proxy/)
Self-hosted deployments can send agent outbound traffic through everruns-egress-proxy for its own outbound address, per-org caps and per-org usage counts.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
3. ### [Sessions no longer hang when a worker stalls before a turn](https://everruns.com/docs/changelog/2026-10-18-stalled-worker-sessions-recover/)
A session whose worker stalls before its turn starts, or after it ends, is now picked up by another worker within minutes instead of staying active forever.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
4. ### [Schedule triggers can run in a Slack conversation](https://everruns.com/docs/changelog/2026-10-18-schedule-triggers-in-slack-conversations/)
A schedule trigger can now run in a direct message or channel of the Agent's own Slack channel, sharing that conversation's session from the first run.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
5. ### [Turns no longer fail when a provider asks to wait too long](https://everruns.com/docs/changelog/2026-10-18-provider-retry-after-budget/)
A provider rate limit whose Retry-After exceeds the retry budget now falls back to backoff and retries, instead of failing the turn at once.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
6. ### [MCP client authorization follows callback redirects](https://everruns.com/docs/changelog/2026-10-18-mcp-client-authorization-redirects/)
Approving an MCP client now completes even when its callback forwards the browser to another site, app, or local port after receiving a code.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
7. ### [Connecting GitHub as yourself acts as you, not the shared bot](https://everruns.com/docs/changelog/2026-10-18-github-person-sign-in/)
Your own GitHub connection now signs you in to GitHub and acts as you; shared Everruns App installations move to organization links.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
8. October 17, 2026
1. ### [OAuth MCP servers with path-specific metadata now connect](https://everruns.com/docs/changelog/2026-10-17-mcp-oauth-discovery-fallbacks/)
The MCP connection check and sign-in now find OAuth metadata at every location the MCP authorization spec allows, so these servers no longer read unreachable.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
2. ### [Framework hosts can brand ChatGPT login completion](https://everruns.com/docs/changelog/2026-10-17-custom-chatgpt-login-pages/)
The ChatGPT Plan loopback helper now accepts custom HTML for its successful callback page while retaining its existing plain-text default.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
3. ### [ChatGPT Plan removed from Platform setup](https://everruns.com/docs/changelog/2026-10-17-chatgpt-plan-ui-removal/)
The Platform UI no longer offers ChatGPT Plan sign-in or its models because its browser callback is unavailable to remote users.
[](https://everruns.com/docs/changelog/2026-10-17-chatgpt-plan-ui-removal/)
Fix[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
4. ### [Automatic session titles with a shared small model](https://everruns.com/docs/changelog/2026-10-17-automatic-session-titles/)
Enable automatic session naming, choose a dedicated model and provider, and keep explicit titles protected while topic checks back off over time.
[](https://everruns.com/docs/changelog/2026-10-17-automatic-session-titles/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
9. October 16, 2026
1. ### [New providers: Mistral, Fireworks, Vercel, Cloudflare, MAI](https://everruns.com/docs/changelog/2026-10-16-yolop-new-providers/)
Yolop setup and --provider now reach Mistral, Fireworks, Vercel, Cloudflare, and MAI, with Google on the native Gemini driver.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.21.0
2. ### [Meta Muse sessions continue after tool calls in Yolop](https://everruns.com/docs/changelog/2026-10-16-yolop-meta-response-ids/)
Yolop keeps Meta Muse sessions running after tool calls by replaying context when a response ID cannot be used for server-side continuation.
Fix[ Yolop](https://everruns.com/docs/changelog/yolop/)
3. ### [MCP setup verifies login without restarting](https://everruns.com/docs/changelog/2026-10-16-yolop-mcp-setup-verification/)
Yolop verifies MCP connections after browser sign-in, reports pending authorization honestly, and loads newly installed tools in the same session.
Fix[ Yolop](https://everruns.com/docs/changelog/yolop/)
4. ### [Control Yolop continuation budgets and trace stop reasons](https://everruns.com/docs/changelog/2026-10-16-yolop-continuation-budget/)
Yolop supports five automatic continuations, configurable limits, durable stop and retry reasons, and OpenTelemetry budget diagnostics.
Improvement[ Yolop](https://everruns.com/docs/changelog/yolop/)
5. ### [Run Everruns provider drivers with WASM host fetch](https://everruns.com/docs/changelog/2026-10-16-wasm-provider-host-fetch/)
Use shared OpenAI and OpenRouter chat drivers in Workers and Durable Objects with an injected HTTP adapter, including streaming and tool calls.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
6. ### [User MCP management respects approval settings](https://everruns.com/docs/changelog/2026-10-16-user-mcp-approval-policy/)
Adding and enabling personal MCP servers now follow the agent's shared Tool Approval mode, preserving URL restrictions and separate OAuth consent.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
7. ### [Slack workspace consent stays with its initiating browser](https://everruns.com/docs/changelog/2026-10-16-slack-workspace-browser-binding/)
Slack manager workspace connections now require the initiating browser and a single-use transaction, with independent tabs and safe consent retries.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
8. ### [Deno Deploy sandbox integration removed](https://everruns.com/docs/changelog/2026-10-16-remove-deno-deploy/)
The experimental Deno Deploy sandbox provider, its deno\_\* tools, the deno-coder demo agent, and related docs are removed.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
9. ### [Everruns shares product and workflow skills](https://everruns.com/docs/changelog/2026-10-16-platform-operating-skills/)
Find product concepts, CLI examples, agent-authoring workflows, and Rust guidance through shared skills in Everruns and its coding-agent plugin.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
10. ### [Connect your coding agent from the Everruns UI](https://everruns.com/docs/changelog/2026-10-16-coding-agent-onboarding/)
Copy one setup prompt for Claude Code, Codex, Cursor or Gemini CLI, with deployment-aware instructions and model-provider setup in its existing flow.
[](https://everruns.com/docs/changelog/2026-10-16-coding-agent-onboarding/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
11. ### [Agent session lists now match their session count](https://everruns.com/docs/changelog/2026-10-16-agent-session-history/)
The agent Sessions tab includes archived conversations, with archive labels, channel information and separate creation and last-message times on cards.
[](https://everruns.com/docs/changelog/2026-10-16-agent-session-history/)
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
12. ### [Find failed agent turns from the Agents list](https://everruns.com/docs/changelog/2026-10-16-agent-failure-investigation/)
Agent failure counts open sessions filtered by failure time, preserve recovered sessions, and link to the exact failed turn in the existing Trace view.
[](https://everruns.com/docs/changelog/2026-10-16-agent-failure-investigation/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
10. October 15, 2026
1. ### [React session streams can suspend while idle](https://everruns.com/docs/changelog/2026-10-15-optional-idle-session-streams/)
Opt in to closing settled session event streams in the React UI client, with durable replay when sending, answering approvals, or returning to the tab.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
11. October 14, 2026
1. ### [Search model catalogs and test a model from Yolop](https://everruns.com/docs/changelog/2026-10-14-yolop-model-discovery/)
Yolop searches provider catalogs, reports model ping latency and errors, and accepts qualified model references without silently choosing another model.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Tool events expose scoped worker CPU observations](https://everruns.com/docs/changelog/2026-10-14-tool-worker-cpu-observations/)
Native hosts report worker-thread CPU consumed while polling a tool executor, separately from elapsed time and remote or child-process work.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
3. ### [A Slack integration overview for building Agents](https://everruns.com/docs/changelog/2026-10-14-slack-integration-overview/)
The Slack integration page now brings together a real product walkthrough, English conversation examples, Agent-building steps, and setup links.
[](https://everruns.com/docs/changelog/2026-10-14-slack-integration-overview/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
4. ### [Recurring schedules reject unverified cron intervals](https://everruns.com/docs/changelog/2026-10-14-session-cron-interval-validation/)
Session schedules now reject invalid cron and insufficient future occurrences at admission, with the same parser used to calculate triggers.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
5. ### [PACT delegated turns discover the user's MCP tools](https://everruns.com/docs/changelog/2026-10-14-pact-delegated-mcp-tools/)
PACT delegated turns now discover account tools with the user's session grant, so scoped order lookups and approved cancellations reach the company API.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
6. ### [OpenTelemetry fixes export and cached-token totals](https://everruns.com/docs/changelog/2026-10-14-opentelemetry-export-and-accounting/)
OpenTelemetry fixes background export, cached-token totals, repeated tool-call IDs, error privacy and Framework shutdown failure reporting.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
7. ### [Local stress tests use successful offline tool workloads](https://everruns.com/docs/changelog/2026-10-14-local-stress-testing/)
Fix local proxy connection failures during stress tests, seed successful offline tool workloads, and improve latency and failure reporting.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
8. ### [More reliable Framework agent sessions](https://everruns.com/docs/changelog/2026-10-14-local-session-reliability/)
Local background tasks recover after owner loss, MCP discovery pauses failed authentication, and provider tool calls preserve optional arguments.
Fix[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
9. ### [Gemini setups prefer Argon and include it in favourites](https://everruns.com/docs/changelog/2026-10-14-gemini-argon-default/)
Gemini 4 Argon is the preferred default for new Gemini setups and an enabled favourite in the server model catalogue. Existing defaults stay selected.
[](https://everruns.com/docs/changelog/2026-10-14-gemini-argon-default/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
10. ### [Framework and serve keep files in the session](https://everruns.com/docs/changelog/2026-10-14-framework-serve-attachments/)
Session::attach keeps a file in the session's /attachments folder, serve's message endpoint takes a files list, and embedded Slack files are kept too.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
11. ### [Concurrent session creation reuses one runtime account](https://everruns.com/docs/changelog/2026-10-14-concurrent-session-account-creation/)
Simultaneous first sessions for the same management account now reuse one runtime identity, avoiding intermittent conflicts during session creation.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
12. ### [Monitor agent execution and actor leases at fleet scale](https://everruns.com/docs/changelog/2026-10-14-agent-execution-monitoring/)
Follow thousands of sessions through model iterations and tools, map orgs to workers, and inspect actor leases in live Monitoring views.
[](https://everruns.com/docs/changelog/2026-10-14-agent-execution-monitoring/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
12. October 13, 2026
1. ### [Yolop Linux sandbox blocks asynchronous socket paths](https://everruns.com/docs/changelog/2026-10-13-yolop-linux-network-policy/)
Linux sandboxed shell modes deny io_uring entry syscalls while preserving direct socket rules, Unix sockets and synchronous toolchain operations.
Fix[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Web search no longer fails on some result snippets](https://everruns.com/docs/changelog/2026-10-13-web-search-snippet-crash/)
A Brave or free search whose result text had an ampersand a few characters before a dash, curly quote or accented letter no longer fails.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
3. ### [Visti and Stend join the product footer](https://everruns.com/docs/changelog/2026-10-13-visti-stend-footer-links/)
The Everruns landing site and documentation now link to Visti and Stend in their shared Products footer, making both sites easier to find.
[](https://everruns.com/docs/changelog/2026-10-13-visti-stend-footer-links/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
4. ### [Schedule triggers read cron weekdays from Sunday as 0](https://everruns.com/docs/changelog/2026-10-13-trigger-cron-weekdays-from-sunday/)
A 5-field cron such as 0 9 \* \* 1-5 on an Agent trigger or durable schedule now runs Monday to Friday, and 0 18 \* \* 0 runs on Sundays.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
5. ### [Tool calls cut off by a crash are rerun when it is safe](https://everruns.com/docs/changelog/2026-10-13-tool-calls-survive-crashes/)
After a worker crash, calls that never started, read-only and idempotent calls run again, and MCP servers get a stable idempotency key.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
6. ### [Slack agents answer DMs and keep the files people send](https://everruns.com/docs/changelog/2026-10-13-slack-agents-dms-and-files/)
One-click Slack apps open a Messages tab, answer a direct message in the DM, store files sent with a message, and use the agent's avatar as the app icon.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
7. ### [Everruns SDKs drop the agent version methods](https://everruns.com/docs/changelog/2026-10-13-sdk-agent-versions-removed/)
The agent version endpoints are gone from the API, so the Rust, Python and TypeScript SDKs remove their version methods. Use change history instead.
Breaking[ SDK](https://everruns.com/docs/changelog/sdk/)
8. ### [Remote workers run turns without an encryption key on the API](https://everruns.com/docs/changelog/2026-10-13-remote-workers-without-encryption-key/)
A standalone gRPC worker no longer fails every turn when the API has no SECRETS_ENCRYPTION_KEY; it skips checkpoints like the in-process worker.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
9. ### [MCP resource reads enforce conversation attachments](https://everruns.com/docs/changelog/2026-10-13-mcp-resource-attachment-scope/)
Resource listing and reading retain the conversation's attached-server policy when the runtime binds a request to its current input message.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
10. ### [Gemini 4 Argon continues long responses across requests](https://everruns.com/docs/changelog/2026-10-13-gemini-argon-long-decode/)
Gemini 4 Argon can continue beyond a single stream, preserving the total output budget and combining text, reasoning, and billed usage across requests.
[](https://everruns.com/docs/changelog/2026-10-13-gemini-argon-long-decode/)
Feature[ Framework](https://everruns.com/docs/changelog/framework/)[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
11. ### [Everruns 0.48.0: Agents in Slack and a stable Agent API](https://everruns.com/docs/changelog/2026-10-13-everruns-0-48-0/)
Sessions run as durable actors instead of workflows, the Agent API calls one agent from code, agents get one web search tool, and Slack agents take DMs.
[](https://everruns.com/docs/changelog/2026-10-13-everruns-0-48-0/)
ReleaseMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
12. ### [E2B desktop computer use keeps the pointer where it was moved](https://everruns.com/docs/changelog/2026-10-13-e2b-desktop-pointer-reset/)
On an E2B desktop, a mouse move or held button no longer snaps back to the screen center when the desktop session has not connected yet.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
13. ### [Dev mode PostgreSQL requires a non-root user](https://everruns.com/docs/changelog/2026-10-13-dev-mode-postgres-user/)
Development mode now uses the PostgreSQL lifecycle library and cleans up its temporary database when the owning server process exits.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)In 0.49.0
14. ### [Attachments stay readable after compaction, from every channel](https://everruns.com/docs/changelog/2026-10-13-attachments-survive-compaction/)
Files sent to an agent are kept in the session's /attachments folder, so the agent can reopen them after compaction, whichever channel they came from.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
15. ### [Stored attachments download safely in browsers](https://everruns.com/docs/changelog/2026-10-13-attachments-download-safely/)
Stored files now download as inert binary attachments while agents retain their original bytes and media types across Slack, AG-UI and A2A.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
16. ### [Answers cut off by a provider error are retried](https://everruns.com/docs/changelog/2026-10-13-answers-cut-off-are-retried/)
When a model stream breaks after some answer text, the runtime retries it instead of keeping the cut-off text as the answer.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.49.0
17. ### [Agents can no longer read secrets back](https://everruns.com/docs/changelog/2026-10-13-agents-cannot-read-secrets/)
Session secrets are now write-only to agents: secret_store stores, lists and deletes them, but no tool returns a value to the model.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.49.0
13. October 12, 2026
1. ### [Hard approvals preserve the full command](https://everruns.com/docs/changelog/2026-10-12-yolop-complete-hard-approvals/)
Yolop rejects oversized approval text, preserves complete commands through thread relays, and denies requests from older hosts after an upgrade.
Fix[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Sessions no longer attach to another workspace](https://everruns.com/docs/changelog/2026-10-12-workspace-attach-retired/)
Every session now gets its own workspace: create-session refuses workspace_id, and POST /v1/workspaces and everruns workspaces create are removed.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)
3. ### [Web search answers cite their sources, sentence by sentence](https://everruns.com/docs/changelog/2026-10-12-web-search-citations/)
The Web Citations capability links each sentence an agent backs with a web search result to that page, from the web_search tool or the model's own search.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
4. ### [Fixes for triggered sandboxes, blocked tools and agent prompts](https://everruns.com/docs/changelog/2026-10-12-trigger-sandbox-blocked-tools-prompts/)
Trigger-started sessions use the organization sandbox account, tools blocked on a connection report a failure, and agent prompts lose the generic opener.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)
5. ### [Recovered tool failures show as issues, not red errors](https://everruns.com/docs/changelog/2026-10-12-tool-failures-shown-as-issues/)
A failed tool call the agent carries on from now shows as an amber issue in chat, and events mark its severity so clients can tell it from a failed turn.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
6. ### [Tool reveals keep the prompt cache on current Claude and GPT-6 models](https://everruns.com/docs/changelog/2026-10-12-tool-changes-keep-prompt-cache/)
Tool search reveals keep the cached tool list on current Claude models and on GPT-6 and GPT-5.6 through the OpenAI API, and Session Trace shows each change.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
7. ### [Tool-call repair drops null placeholders on optional parameters](https://everruns.com/docs/changelog/2026-10-12-tool-call-null-placeholders/)
When a model fills optional tool parameters with null or the string null, tool-call repair now leaves them out instead of letting a strict tool reject the call.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)
8. ### [Agent sessions now run as actors instead of workflows](https://everruns.com/docs/changelog/2026-10-12-sessions-run-as-actors/)
One worker leases a session and runs its whole turn from the session's event log, replacing the per-step turn workflow, its queue hops and checkpoints.
ImprovementMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
9. ### [Session Trace names prompt cache resets and their cause](https://everruns.com/docs/changelog/2026-10-12-session-trace-cache-resets/)
When a model call loses the prompt cache the previous call built, the trace now says so and names the likely cause, such as changed tools or an idle gap.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
10. ### [Agent-login MCP tool calls now show in chats people start](https://everruns.com/docs/changelog/2026-10-12-service-mcp-tool-calls-recorded/)
Tool calls through an MCP server acting as the agent now reach sessions people start, including the connect prompt shown before an admin signs in.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)
11. ### [Scheduled turns in a Slack conversation post to it](https://everruns.com/docs/changelog/2026-10-12-scheduled-turns-post-to-slack/)
A session schedule that fires in a Slack-bound session can now send_message to that Slack conversation instead of failing with no Slack session.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
12. ### [Agents now see why a session schedule cron was rejected](https://everruns.com/docs/changelog/2026-10-12-schedule-cron-errors-reach-agent/)
An invalid cron or timezone in create_schedule now returns the reason to the agent instead of an internal error, and Sunday crons obey the interval.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
13. ### [Sandboxes page with tabs, and tab links you can share](https://everruns.com/docs/changelog/2026-10-12-sandboxes-tabs/)
Sandboxes, Templates and provider accounts are now tabs of one Sandboxes page, and the Models, Web search and Sandboxes tabs each have their own URL.
[](https://everruns.com/docs/changelog/2026-10-12-sandboxes-tabs/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
14. ### [Sandbox file tools all read the managed Sandbox workspace](https://everruns.com/docs/changelog/2026-10-12-sandbox-file-tools-one-workspace/)
With a managed Sandbox, read_many_files and list_directory now read the Sandbox's /workspace, so files the agent cloned there no longer read as not found.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
15. ### [Create read-only personal access tokens](https://everruns.com/docs/changelog/2026-10-12-read-only-personal-access-tokens/)
A personal access token can now be read-only: it reads every organization you belong to, and is refused anything that creates, changes or runs.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
16. ### [Sessions recover from provider limits and follow the agent's model](https://everruns.com/docs/changelog/2026-10-12-provider-limit-recovery/)
A session retries after a short provider rate limit, explains OpenRouter credit shortfalls, and follows the agent's default model unless one was set.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
17. ### [Prompt caching on Bedrock, OpenRouter Claude and Mistral](https://everruns.com/docs/changelog/2026-10-12-prompt-caching-bedrock-openrouter-mistral/)
Prompt caching now writes cache points on Bedrock Claude and Nova, turns on caching for Claude through OpenRouter, and routes Mistral by cache key.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
18. ### [Prompt caching gets a retention setting and a prompt-only mode](https://everruns.com/docs/changelog/2026-10-12-prompt-cache-retention/)
Ask for one hour Anthropic cache entries with retention 1h, and use the explicit strategy on Claude to cache tools and instructions but not the conversation.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
19. ### [Parallel joins Brave as a Web Search provider](https://everruns.com/docs/changelog/2026-10-12-parallel-web-search/)
Connect a Parallel API key as a search account, and the web_search tool sends searches to Parallel's Search API, with excerpts the model can quote.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
20. ### [Web search: one tool, search providers and organization defaults](https://everruns.com/docs/changelog/2026-10-12-one-web-search-tool/)
Agents search the web with one web_search tool. Admins connect search providers and set defaults, each agent picks its own, and free search is the fallback.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
21. ### [Muse Wonder, Meta's new top model, is available in Everruns](https://everruns.com/docs/changelog/2026-10-12-muse-wonder/)
Meta's Muse Wonder joins the model catalog with pricing, a 1M-token context and its model card, and every Muse model gets a reasoning effort picker up to Max.
[](https://everruns.com/docs/changelog/2026-10-12-muse-wonder/)
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
22. ### [Model card links on the Models page](https://everruns.com/docs/changelog/2026-10-12-model-card-links/)
Models with a published vendor model card or system card now link to it from the Models page and carry the link in their model profile.
[](https://everruns.com/docs/changelog/2026-10-12-model-card-links/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
23. ### [API reference groups endpoints and shows support](https://everruns.com/docs/changelog/2026-10-12-grouped-api-reference/)
The API reference puts Agent API first, groups management endpoints, and shows support, availability and replacements for deprecated routes.
[](https://everruns.com/docs/changelog/2026-10-12-grouped-api-reference/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)
24. ### [GitHub triggers narrow CI events by branch and workflow](https://everruns.com/docs/changelog/2026-10-12-github-trigger-ci-filters/)
The GitHub trigger form takes the branches and workflow names a CI trigger accepts, offers one session for every event, and the agent builder fills both in.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
25. ### [GitHub tools act as one chosen identity, with narrowed tokens](https://everruns.com/docs/changelog/2026-10-12-github-tools-act-as/)
A new acts_as setting picks whose GitHub identity the tools use, and each call gets a token limited to one repository and the permissions it needs.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
26. ### [Check out private pull requests into the managed sandbox](https://everruns.com/docs/changelog/2026-10-12-github-checkout-pull-request/)
The new github_checkout_pull_request tool fetches a pull request into the managed sandbox with a read-only token that never stays in the sandbox.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
27. ### [Org admins link the Everruns GitHub App, agents choose it explicitly](https://everruns.com/docs/changelog/2026-10-12-everruns-github-app-org/)
Org admins link Everruns GitHub App installations in Settings, and each agent chooses between its own GitHub App and a linked account.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)
28. ### [Docs moved to everruns.com/docs](https://everruns.com/docs/changelog/2026-10-12-docs-move-to-everruns-com/)
The documentation now lives at everruns.com/docs, and every docs.everruns.com link, Markdown file and schema redirects to the same page there.
Improvement[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
29. ### [Cloudflare Web Search joins as a Web Search provider](https://everruns.com/docs/changelog/2026-10-12-cloudflare-web-search/)
Connect a Cloudflare account as a search account and the web_search tool searches Ceramic, Exa or Linkup through the Cloudflare Web Search API.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
30. ### [Adopt agent examples and list provider accounts from the CLI](https://everruns.com/docs/changelog/2026-10-12-cli-agent-examples-provider-accounts/)
The everruns CLI and MCP can list and adopt agent examples such as pr-reviewer, and list an organization's provider accounts without their credentials.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)
31. ### [Browser agents see their screenshots and keep their page](https://everruns.com/docs/changelog/2026-10-12-browser-agents-screenshots-focus/)
Browser screenshots now reach the model, Browserless tools no longer race on one page, and key presses report where focus landed.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
32. ### [API keys a Bashkit agent uses but never sees](https://everruns.com/docs/changelog/2026-10-12-api-keys-agent-never-sees/)
Bind an API key to one URL and the Bashkit shell can call that API with $NAME, while the model only ever sees a placeholder and never the key.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
33. ### [Agents can sync provider models with the Platform capability](https://everruns.com/docs/changelog/2026-10-12-agents-sync-provider-models/)
sync_provider_models now works from an agent, and provider create and update from an agent discover models and set the organization's default model.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
34. ### [Agents can cap output tokens per response](https://everruns.com/docs/changelog/2026-10-12-agent-max-output-tokens/)
A per-agent Max output tokens setting caps every model response, so agents keep running on a provider balance too low for the model's full output limit.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
35. ### [An agent's GitHub App gets its own section, name and avatar step](https://everruns.com/docs/changelog/2026-10-12-agent-github-section/)
GitHub moves out of Triggers into its own section on the agent's Integrations tab, new Apps take the agent's name, and a step helps set the App's avatar.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
36. ### [Choose where an agent's GitHub App lives, and manage it from the CLI](https://everruns.com/docs/changelog/2026-10-12-agent-github-org-cli/)
Connect GitHub suggests the organizations your team already uses for an agent's App, an uninstalled App can start over, and the CLI manages it.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
37. ### [Set an agent's avatar from the CLI and MCP](https://everruns.com/docs/changelog/2026-10-12-agent-avatar-cli-mcp/)
The set_agent_avatar command takes a curated preset id or a base64 image, so an agent building another agent over MCP can give it a picture.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
14. October 11, 2026
1. ### [Yolop 0.21.0 release](https://everruns.com/docs/changelog/2026-10-11-yolop-0-21-0/)
Code search works without ripgrep installed, coordinators run threads as separate processes, and new setups default to the latest model on each provider.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [SDK 0.3.0 release](https://everruns.com/docs/changelog/2026-10-11-sdk-0-3-0/)
SDKs default to the Everruns Cloud API URL, record why a change was made, and add an AgentClient for calling one agent through the Agent API.
Release[ SDK](https://everruns.com/docs/changelog/sdk/)
3. ### [Framework apps act as Poppy personal agents](https://everruns.com/docs/changelog/2026-10-11-poppy-framework-personal-agents/)
PoppyPersonalAgent makes a Framework app a person's own agent over the Personal Agent Protocol, with the same tools Platform Chat uses and a runnable example.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)
4. ### [Load Everruns workflow skills through MCP](https://everruns.com/docs/changelog/2026-10-11-mcp-workflow-skills/)
The Everruns MCP endpoint serves two workflow skills for running sessions and authoring agents, with metadata and verified file manifests.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
5. ### [Call one agent from code with an Agent API key](https://everruns.com/docs/changelog/2026-10-11-agent-api-channel/)
An Agent API channel gives an application a key that reaches one agent's sessions, with streaming, approvals, end users, browser origins and spend limits.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
15. October 10, 2026
1. ### [New setups default to the latest model on every provider](https://everruns.com/docs/changelog/2026-10-10-yolop-latest-default-models/)
New Yolop setups default to gpt-6.1-sol, claude-opus-5-5, muse-spark-1.3 and gemini-3.8-flash; a saved model setting or the -m flag still takes priority.
Improvement[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.21.0
2. ### [Coordinators run threads as separate processes](https://everruns.com/docs/changelog/2026-10-10-yolop-coordination-threads/)
Yolop now uses the Everruns coordination capability: each thread runs as a headless Yolop process in its own worktree and shows on a THREADS board in the TUI.
FeatureMajor[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.21.0
3. ### [Code search works on machines without ripgrep](https://everruns.com/docs/changelog/2026-10-10-yolop-built-in-rg-search/)
Yolop ships a built-in rg search as yolop utils rg and, when no system ripgrep is found, as an agent tool backed by the same engine as its search tool.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.21.0
4. ### [Tools in Shell runs an agent's tools from Bashkit scripts](https://everruns.com/docs/changelog/2026-10-10-tools-in-shell/)
The tools_in_shell capability moves most tools into one tools shell command, so a script can chain calls, preview them with tools plan and ask approval first.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
5. ### [Session Trace view replaces Transcript and Timeline](https://everruns.com/docs/changelog/2026-10-10-session-trace-view/)
Sessions open on a Trace tab that lays out turns and steps with timing bars, an inspector for each step's input, output and model request, and event search.
[](https://everruns.com/docs/changelog/2026-10-10-session-trace-view/)
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
6. ### [Session SQL databases survive server restarts](https://everruns.com/docs/changelog/2026-10-10-session-sql-databases-persist/)
Session SQL databases are now stored in PostgreSQL page by page, so a server restart no longer loses them and every server instance sees the same data.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
7. ### [serve keeps daemon data in S3 and can require API keys](https://everruns.com/docs/changelog/2026-10-10-serve-bucket-store-and-auth/)
serve start --store s3:// keeps a daemon's data in an object store, agent routes can require API keys or OIDC, and serve resumes cut-off sessions at boot.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
8. ### [Everruns SDKs add AgentClient to call one agent with a key](https://everruns.com/docs/changelog/2026-10-10-sdk-agent-client/)
The Python, TypeScript and Rust SDKs gain AgentClient, which calls a single agent through the Agent Execution API with an agent key, including SSE streaming.
FeatureMajor[ SDK](https://everruns.com/docs/changelog/sdk/)In 0.3.0
9. ### [Everruns agents act as Poppy personal agents](https://everruns.com/docs/changelog/2026-10-10-poppy-personal-agents/)
The poppy_client capability lets an Everruns agent find a company's agent through the Personal Agent Protocol, talk to it and hand off to a person there.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
10. ### [Add your own MCP servers and use them in chat](https://everruns.com/docs/changelog/2026-10-10-my-mcp-servers/)
People add personal MCP servers in Settings, and agents with the User MCP Servers capability use them, including servers connected in the middle of a chat.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
11. ### [Label MCP tools as read only or changes things](https://everruns.com/docs/changelog/2026-10-10-mcp-tool-risk-labels/)
Save a risk label on each tool of an organization MCP server to decide whether it asks for approval, with model-suggested labels a person can accept.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
12. ### [Built-in harnesses reorganized as a tree by use case](https://everruns.com/docs/changelog/2026-10-10-harness-tree/)
Base, Conversation, Worker, Bashkit Worker and Sandbox Worker now form a tree of built-in harnesses, and existing Worker bindings move by sandbox policy.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
13. ### [Pick and configure guardrail checks in the agent editor](https://everruns.com/docs/changelog/2026-10-10-guardrail-checks-editor/)
The Guardrails settings panel lets authors add presets or single checks and set their stage and action, and model-backed checks can now judge the final reply.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
14. ### [GitHub triggers wake agents on CI results](https://everruns.com/docs/changelog/2026-10-10-github-ci-triggers/)
GitHub triggers now receive workflow run and check suite completions, and a changes-only filter wakes the agent only when a workflow's CI result changes.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
15. ### [Explicit communication lets agents choose when to reply](https://everruns.com/docs/changelog/2026-10-10-explicit-communication/)
An agent's Communication setting can be Explicit, so assistant text stays as private notes and the agent speaks only through send_message or stays silent.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
16. ### [Outbound network policy modes, audit log and org allowlists](https://everruns.com/docs/changelog/2026-10-10-egress-policy-controls/)
Operators choose open or curated outbound access, every outbound request is audited with its org and session, and admins can widen the allowlist for one org.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
17. ### [Everruns Cloud verify-email screen sends a new link again](https://everruns.com/docs/changelog/2026-10-10-cloud-verify-email-resend-fix/)
The email verification step on Everruns Cloud now offers Sign in again, which sends a fresh link, replacing a resend action that never sent any mail.
Fix[ Cloud](https://everruns.com/docs/changelog/cloud/)
18. ### [Everruns Cloud shows spend per model and agent on a Usage tab](https://everruns.com/docs/changelog/2026-10-10-cloud-usage-tab-credit/)
A Usage tab breaks down Everruns Cloud charges by day, model and agent with token counts and CSV export, and a credit chip and banner warn on low credit.
Feature[ Cloud](https://everruns.com/docs/changelog/cloud/)
19. ### [Everruns CLI help shows a worked example for every command](https://everruns.com/docs/changelog/2026-10-10-cli-worked-examples/)
Every everruns command now prints a worked example in --help, command groups describe themselves, and a generated reference documents each command group.
Improvement[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
20. ### [ChatGPT plan connections no longer need a feature flag](https://everruns.com/docs/changelog/2026-10-10-chatgpt-plan-no-flag/)
The ChatGPT provider now shows in Connect provider on every deployment, including Everruns Cloud, because signing in is already personal to each user.
[](https://everruns.com/docs/changelog/2026-10-10-chatgpt-plan-no-flag/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
21. ### [Rate agent replies and branch a chat from any message](https://everruns.com/docs/changelog/2026-10-10-chat-feedback-and-branching/)
Agent replies in Chat get Good, Bad and Branch into new chat actions, and each message shows one status row from sending until the answer arrives.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.48.0
22. ### [api_endpoint channels convert to Agent API channels](https://everruns.com/docs/changelog/2026-10-10-api-endpoint-channels-converted/)
Existing api_endpoint channels become Agent API channels at startup with the same keys, and old request shapes now return api_endpoint_retired or HTTP 410.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
23. ### [Agents must give a reason for every change by default](https://everruns.com/docs/changelog/2026-10-10-agent-change-reasons-required/)
Require reasons from agents is now on by default, so an agent's change without a reason fails with reason_required; organizations can turn the flag off.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.48.0
24. ### [Agent budgets stop turns before each model call](https://everruns.com/docs/changelog/2026-10-10-agent-budgets-enforced/)
Agent budgets are now checked before every model call in the native agent loop, so an exhausted budget stops the turn with a typed reason and budget events.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.48.0
16. October 9, 2026
1. ### [Opt-in apply_patch editing for OpenAI models](https://everruns.com/docs/changelog/2026-10-09-yolop-native-apply-patch/)
The native_edit_tools capability gives OpenAI models in Yolop the Codex apply_patch tool, and every yolop subcommand now has a description and an example.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.20.0
2. ### [Yolop 0.20.0 release](https://everruns.com/docs/changelog/2026-10-09-yolop-0-20-0/)
OpenAI models can edit with apply_patch as an opt-in, integrations prefer known CLIs, and command help includes runnable examples.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
3. ### [Voice channels let people call an agent](https://everruns.com/docs/changelog/2026-10-09-voice-channels/)
A voice channel lets people talk to an agent over WebRTC while it keeps its own model, tools and instructions, on the platform and in the Framework.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.47.0
4. ### [Models and providers move into one registry page](https://everruns.com/docs/changelog/2026-10-09-models-providers-registry/)
Registries → Models now holds Models, Providers and Defaults tabs, so connecting a provider, choosing its models and setting org defaults happen in one place.
[](https://everruns.com/docs/changelog/2026-10-09-models-providers-registry/)
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.47.0
5. ### [Everruns 0.47.0: Codex session reliability](https://everruns.com/docs/changelog/2026-10-09-everruns-0-47-0/)
An Agents home and a New agent page with an agent builder, a fix for ChatGPT plan sessions failing with HTTP 400, and a list of connected AI clients.
[](https://everruns.com/docs/changelog/2026-10-09-everruns-0-47-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
6. ### [Everruns 0.46.0: Agents home](https://everruns.com/docs/changelog/2026-10-09-everruns-0-46-0/)
Save shell scripts on agents and run them with tools scripts, and the Agents home shows run activity, setup alerts and a shared Channels view.
[](https://everruns.com/docs/changelog/2026-10-09-everruns-0-46-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
7. ### [Everruns 0.45.0: Tools in Shell](https://everruns.com/docs/changelog/2026-10-09-everruns-0-45-0/)
Agents get a browser tool on Browserless, coordinator threads arrive in Platform Chat and the Framework, and agents can run tools from the shell.
[](https://everruns.com/docs/changelog/2026-10-09-everruns-0-45-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
8. ### [See and revoke AI clients connected to Everruns](https://everruns.com/docs/changelog/2026-10-09-connected-ai-clients/)
Settings → My agent experience lists the AI clients you connected to Everruns over MCP OAuth, shows when each was last used, and lets you revoke one.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.47.0
9. ### [app.everruns.com serves llms.txt and auth.md for AI agents](https://everruns.com/docs/changelog/2026-10-09-cloud-agent-discovery-routes/)
AI agents and crawlers can discover Everruns Cloud from app.everruns.com itself through llms.txt, auth.md, Link headers, robots.txt and a sitemap.
Improvement[ Cloud](https://everruns.com/docs/changelog/cloud/)
10. ### [Fix ChatGPT plan sessions failing with HTTP 400](https://everruns.com/docs/changelog/2026-10-09-chatgpt-plan-http-400-fix/)
Sessions that use a ChatGPT plan connection no longer fail with HTTP 400, because Everruns stops sending request-body metadata the Codex backend rejects.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.47.0
11. ### [Agents home and a New agent page with an agent builder](https://everruns.com/docs/changelog/2026-10-09-agents-home-new-agent/)
The Agents page now shows run activity, channels and setup problems for each agent, and New agent drafts an agent from a description, an example or an import.
[](https://everruns.com/docs/changelog/2026-10-09-agents-home-new-agent/)
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.47.0
12. ### [Save shell scripts on agents and run them from triggers](https://everruns.com/docs/changelog/2026-10-09-agent-saved-scripts/)
Agents keep named shell scripts that run in Bashkit with their tools, and schedule or webhook triggers can run one with the event payload and no model call.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.46.0
17. October 8, 2026
1. ### [Everruns SDKs record why a change was made](https://everruns.com/docs/changelog/2026-10-08-sdk-change-reasons/)
All three Everruns SDKs add with_reason, a derived client that sends a change reason the server stores in the changed entity's change history.
Feature[ SDK](https://everruns.com/docs/changelog/sdk/)In 0.3.0
2. ### [Everruns 0.44.0: A2A 1.0 HTTP+JSON and PACT](https://everruns.com/docs/changelog/2026-10-08-everruns-0-44-0/)
Agent versions are retired in favor of change history, A2A channels serve HTTP+JSON and the PACT profiles, and MCP works inside conversations.
[](https://everruns.com/docs/changelog/2026-10-08-everruns-0-44-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
3. ### [Desktop computer use on E2B and Daytona sandboxes](https://everruns.com/docs/changelog/2026-10-08-desktop-computer-use/)
Experimental capabilities run the computer tool on a full Linux desktop in an E2B or Daytona sandbox, now with zoom, cursor, mouse button and hold-key actions.
[](https://everruns.com/docs/changelog/2026-10-08-desktop-computer-use/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)
4. ### [Coordinator threads in Platform Chat and the Framework](https://everruns.com/docs/changelog/2026-10-08-coordinator-threads/)
Platform Chat and Framework agents can hand focused work to child threads and keep a board of what is working, what needs you and what is ready for review.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.45.0
5. ### [Browser tool for agents on Browserless](https://everruns.com/docs/changelog/2026-10-08-browser-use-capability/)
The experimental browser_use capability lets an agent read pages as an accessibility tree, act on elements by ref, fill forms and manage tabs in Browserless.
[](https://everruns.com/docs/changelog/2026-10-08-browser-use-capability/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.45.0
6. ### [History, completion and bash -i work like bash](https://everruns.com/docs/changelog/2026-10-08-bashkit-interactive-shell-features/)
Bashkit adds history and fc, complete and compgen, bind, prompt expansion and an interactive bash -i child shell, each with state kept per interpreter.
Feature[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
18. October 7, 2026
1. ### [Everruns 0.43.0 release](https://everruns.com/docs/changelog/2026-10-07-everruns-0-43-0/)
The 18 deprecated integration shim crates are removed after their forwarding release; integrations stay available from everruns-integrations.
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
2. ### [Claude Haiku 5.5 model added for Anthropic providers](https://everruns.com/docs/changelog/2026-10-07-claude-haiku-5-5/)
The claude-haiku-5-5 model and its 1M-context twin are seeded as an enabled favorite, with adaptive thinking, tool search and the computer toolset.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.44.0
3. ### [An in-process terminal and TerminalTool for agents](https://everruns.com/docs/changelog/2026-10-07-bashkit-terminal-tool/)
A terminal feature runs a Bashkit session in an in-memory terminal with vi, less and more, and TerminalTool lets an LLM agent type keys across named tabs.
FeatureMajor[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
4. ### [A make builtin and 21 everyday commands](https://everruns.com/docs/changelog/2026-10-07-bashkit-make-everyday-commands/)
Bashkit gains a GNU make 4.3 subset that runs recipes in the sandbox shell, plus nohup, flock, getconf, free and 17 other commands that scripts often call.
Feature[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
5. ### [Real CPython 3.14 runs as python3 in a sandbox](https://everruns.com/docs/changelog/2026-10-07-bashkit-cpython-python3/)
An opt-in cpython feature makes python3 in Bashkit real CPython 3.14 in a WebAssembly sandbox, with the stdlib and HTTP through Bashkit's network policy.
FeatureMajor[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
6. ### [Background jobs and pipeline stages run concurrently](https://everruns.com/docs/changelog/2026-10-07-bashkit-concurrent-jobs-pipelines/)
Bashkit now runs cmd & jobs alongside the shell and streams pipeline stages into each other with SIGPIPE, adding jobs, wait -n, kill and related builtins.
Improvement[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
7. ### [awk is rewritten to follow gawk 5.2](https://everruns.com/docs/changelog/2026-10-07-bashkit-awk-follows-gawk/)
Bashkit's awk, gawk, mawk and nawk builtins now target gawk 5.2, with POSIX awk, common gawk extensions, and system() and pipes through the sandbox shell.
Improvement[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
8. ### [Sign in to channels and Public Chat with AgentID](https://everruns.com/docs/changelog/2026-10-07-agentid-sign-in/)
A2A, AG-UI and Public Chat channels accept AgentID tokens, Public Chat shows Continue with AgentID, and agents can sign in to apps with their AgentMail inbox.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.44.0
9. ### [A2A channels serve HTTP+JSON and the PACT profiles](https://everruns.com/docs/changelog/2026-10-07-a2a-http-json-and-pact/)
Every A2A channel also answers the A2A 1.0 HTTP+JSON binding, and a channel can accept personal agents through the PACT Identity and Delegated profiles.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.44.0
19. October 6, 2026
1. ### [Everruns 0.42.0 release](https://everruns.com/docs/changelog/2026-10-06-everruns-0-42-0/)
Agents get avatars from presets or an uploaded image, sandboxes can start from templates, and a Sandboxes fleet view shows every running sandbox.
[](https://everruns.com/docs/changelog/2026-10-06-everruns-0-42-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
2. ### [Change history with reasons and restore for every entity](https://everruns.com/docs/changelog/2026-10-06-change-history-with-reasons/)
Every configuration change records who made it, through which surface and why, with numbered revisions you can compare and restore from the History menu.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.42.0
3. ### [Sessions start with a default root filesystem](https://everruns.com/docs/changelog/2026-10-06-bashkit-default-root-filesystem/)
Every Bashkit session now has read-only /etc, /proc and /usr/bin entries, so scripts that read os-release or cpuinfo or call /usr/bin/env find what they expect.
Feature[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
4. ### [Agent versions are retired in favor of change history](https://everruns.com/docs/changelog/2026-10-06-agent-versions-retired/)
Agent publishing, default versions and version pins are removed; channels, triggers and sessions now run the agent's current configuration instead.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.44.0
20. October 5, 2026
1. ### [Sandbox Templates and a Sandboxes fleet view](https://everruns.com/docs/changelog/2026-10-05-sandbox-templates-fleet-view/)
Reusable Sandbox Templates with immutable revisions decide where an agent's commands run, and a fleet page lists every sandbox in the organization.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.42.0
2. ### [Agents get avatars from presets or an uploaded image](https://everruns.com/docs/changelog/2026-10-05-agent-avatars/)
Give an agent an avatar from 25 curated presets or a cropped upload, and it shows in the console, on the A2A Agent Card and as the Slack app icon.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.42.0
21. October 4, 2026
1. ### [Yolop 0.19.0 release](https://everruns.com/docs/changelog/2026-10-04-yolop-0-19-0/)
GPT-6.1 Sol, the GPT-6 family and Claude 5.5 models are available, and ChatGPT sign-in goes through the open-source route.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Framework crates consolidated into everruns-core and the facade](https://everruns.com/docs/changelog/2026-10-04-framework-crates-consolidated/)
Engine, host, builtins, MCP, AG-UI, driver and contract crates merged into fewer packages, so Framework users update dependencies and imports when upgrading.
Breaking[ Framework](https://everruns.com/docs/changelog/framework/)In 0.41.0
3. ### [Everruns 0.41.0 release](https://everruns.com/docs/changelog/2026-10-04-everruns-0-41-0/)
Framework crates are consolidated into everruns-core and the everruns facade, and the deprecated forwarding shims are removed. Read the upgrade notes.
[](https://everruns.com/docs/changelog/2026-10-04-everruns-0-41-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
4. ### [Everruns 0.40.0 release](https://everruns.com/docs/changelog/2026-10-04-everruns-0-40-0/)
Engine, host, builtins, MCP and AG-UI become everruns-core features, operators can enroll adoption features, and the MCP card reports the version.
[](https://everruns.com/docs/changelog/2026-10-04-everruns-0-40-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
5. ### [Everruns 0.39.0 release](https://everruns.com/docs/changelog/2026-10-04-everruns-0-39-0/)
Chat threads keep conversations and ongoing work beside Chat, and Slack permission problems appear in Health with steps to recover.
[](https://everruns.com/docs/changelog/2026-10-04-everruns-0-39-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
22. October 3, 2026
1. ### [Slack permission issues appear in Health with recovery steps](https://everruns.com/docs/changelog/2026-10-03-slack-health-issues-recovery/)
Slack installations missing a permission or credential raise a persistent issue in Settings > Health, with guided recovery, per-user snoozing and rechecks.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.39.0
2. ### [Playground for testing agents, and Chat as one permanent conversation](https://everruns.com/docs/changelog/2026-10-03-playground-agent-testing/)
Agent testing moves to a shared Playground where members pick the agent and the user it runs as, while Chat becomes your permanent Platform Chat conversation.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.38.0
3. ### [Feature flag grade preview renamed to internal for operators](https://everruns.com/docs/changelog/2026-10-03-feature-grades-preview-internal/)
Self-hosted operators must replace FEATURE\_\*=preview with internal, as rollout grades now set availability, organization defaults and who may enable a feature.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)In 0.38.0
4. ### [Everruns 0.38.0: Personal ChatGPT plan connections](https://everruns.com/docs/changelog/2026-10-03-everruns-0-38-0/)
Connect a personal ChatGPT plan as a model provider, Conversation becomes the default harness with Generic deprecated, and custom hosts get stock subagents.
[](https://everruns.com/docs/changelog/2026-10-03-everruns-0-38-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
5. ### [Everruns 0.37.0: Approval history in context](https://everruns.com/docs/changelog/2026-10-03-everruns-0-37-0/)
Approval history shows in chat and on a new Approvals audit page, agent channels are visible at a glance, and navigation keeps your place.
[](https://everruns.com/docs/changelog/2026-10-03-everruns-0-37-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
6. ### [Everruns 0.36.0: Choose an Agent Environment](https://everruns.com/docs/changelog/2026-10-03-everruns-0-36-0/)
Agents can choose their environment, a broken Slack installation can be repaired in place, and session storage gets its own permissions.
[](https://everruns.com/docs/changelog/2026-10-03-everruns-0-36-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
7. ### [Everruns 0.35.0: Safer sandbox tools](https://everruns.com/docs/changelog/2026-10-03-everruns-0-35-0/)
A computer use capability lets agents operate a browser screen, sandbox tools are safer, questions are answered in chat, and every org gets Platform Chat.
[](https://everruns.com/docs/changelog/2026-10-03-everruns-0-35-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
8. ### [Conversation becomes the default harness and Generic is deprecated](https://everruns.com/docs/changelog/2026-10-03-conversation-default-harness/)
Built-in harnesses form progressive presets from Base to Worker, Conversation is the new default, and agents that inherited Generic are pinned to it on upgrade.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.38.0
9. ### [Computer use capability lets agents operate a browser screen](https://everruns.com/docs/changelog/2026-10-03-computer-use-capability/)
The experimental computer_use capability drives a Browserless page with clicks, typing and screenshots, using native OpenAI or Anthropic tools when available.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.35.0
10. ### [Connect a personal ChatGPT plan as a model provider](https://everruns.com/docs/changelog/2026-10-03-chatgpt-plan-provider/)
Self-hosted users can sign in with ChatGPT from provider settings to run on their own ChatGPT plan, with account status, reconnect controls and private models.
[](https://everruns.com/docs/changelog/2026-10-03-chatgpt-plan-provider/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.38.0
11. ### [Chat threads keep conversations and ongoing work beside Chat](https://everruns.com/docs/changelog/2026-10-03-chat-threads-workspace/)
Organizations can opt into Chat threads, a panel beside the permanent Chat for searching conversations, following background work and resolving threads.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.39.0
12. ### [Approval history in chat and a new Approvals audit page](https://everruns.com/docs/changelog/2026-10-03-approval-history-audit-page/)
Chats show approval requests and recorded consent as clear entries, and organization admins get an Approvals page that filters approvals across sessions.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.37.0
23. October 2, 2026
1. ### [ChatGPT sign-in through the open-source route](https://everruns.com/docs/changelog/2026-10-02-yolop-open-source-chatgpt-sign-in/)
Yolop's codex provider now signs in with OpenAI's open-source Sign in with ChatGPT by default and runs turns on the public Responses API on your ChatGPT plan.
[](https://everruns.com/docs/changelog/2026-10-02-yolop-open-source-chatgpt-sign-in/)
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.19.0
2. ### [Host Everruns serve apps on Amazon Bedrock AgentCore Runtime](https://everruns.com/docs/changelog/2026-10-02-serve-on-bedrock-agentcore/)
The experimental everruns-serve-agentcore crate runs a serve app on AgentCore Runtime with AG-UI streaming, a WebSocket transport and Bedrock models.
[](https://everruns.com/docs/changelog/2026-10-02-serve-on-bedrock-agentcore/)
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.34.1
3. ### [Everruns SDKs default to the Everruns Cloud API URL](https://everruns.com/docs/changelog/2026-10-02-sdk-default-cloud-base-url/)
An Everruns SDK client created with only EVERRUNS_API_KEY now reaches Everruns Cloud instead of a placeholder URL; EVERRUNS_API_URL still overrides it.
Fix[ SDK](https://everruns.com/docs/changelog/sdk/)In 0.3.0
4. ### [everruns-durable workflow engine published on crates.io](https://everruns.com/docs/changelog/2026-10-02-everruns-durable-crate-published/)
The durable workflow engine behind Everruns ships as its own crate with timers, child workflows and a bundled idempotent PostgreSQL schema you can migrate.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)SupersededIn 0.34.1
5. ### [Everruns 0.34.2: Execution kernel in a JavaScript isolate](https://everruns.com/docs/changelog/2026-10-02-everruns-0-34-2/)
Cloudflare AI Gateway and Vercel AI Gateway become model providers, Workers AI models are discoverable, and the kernel runs in a JavaScript isolate.
[](https://everruns.com/docs/changelog/2026-10-02-everruns-0-34-2/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
6. ### [Everruns 0.34.1 release](https://everruns.com/docs/changelog/2026-10-02-everruns-0-34-1/)
The everruns-durable workflow engine is published on crates.io, and Everruns serve apps can be hosted on Amazon Bedrock AgentCore Runtime.
[](https://everruns.com/docs/changelog/2026-10-02-everruns-0-34-1/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
7. ### [Everruns 0.34.0: AG-UI 1.0](https://everruns.com/docs/changelog/2026-10-02-everruns-0-34-0/)
PR reviewer and security scanner templates for GitHub, MCP Events with session webhooks and triggers, and AG-UI 1.0 and A2A 1.0 support.
[](https://everruns.com/docs/changelog/2026-10-02-everruns-0-34-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
8. ### [Cloudflare AI Gateway and Vercel AI Gateway model providers](https://everruns.com/docs/changelog/2026-10-02-cloudflare-vercel-ai-gateway/)
Agents can run models through Cloudflare AI Gateway or Vercel AI Gateway, and the Cloudflare provider lists Workers AI text-generation models automatically.
[](https://everruns.com/docs/changelog/2026-10-02-cloudflare-vercel-ai-gateway/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.34.2
9. ### [AG-UI 1.0 endpoint and AG-UI streaming for Framework sessions](https://everruns.com/docs/changelog/2026-10-02-ag-ui-1-0-protocol/)
The AG-UI channel moves to protocol 1.0 with interrupts, resume, frontend tools and subagents, and any Framework session can serve AG-UI from your own server.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.34.0
10. ### [A2A 1.0 endpoint with task results, streaming and push notifications](https://everruns.com/docs/changelog/2026-10-02-a2a-1-0-protocol/)
Everruns A2A channels negotiate protocol 1.0, return agent output as task artifacts, support ListTasks and SubscribeToTask, and send push notifications.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.34.0
24. October 1, 2026
1. ### [GPT-6.1 Sol, the GPT-6 family and Claude 5.5 models](https://everruns.com/docs/changelog/2026-10-01-yolop-gpt-6-claude-5-5-models/)
Yolop's model menus list GPT-6.1 Sol with a speed setting for service tiers, the rest of the GPT-6 family, Claude Opus 5.5, Sonnet 5.5 and Fable 5.1.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.19.0
2. ### [Connect several Slack workspaces per organization](https://everruns.com/docs/changelog/2026-10-01-slack-workspaces-per-organization/)
An organization connects its Slack workspaces once in Settings, and adding an agent to Slack becomes picking a workspace and approving one consent screen.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.34.0
3. ### [OpenAI structured output and background mode for long reasoning](https://everruns.com/docs/changelog/2026-10-01-openai-structured-output-background/)
OpenAI drivers enforce JSON Schema structured output, and xhigh or max reasoning calls run as background responses that resume after a drop or worker restart.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.34.0
4. ### [MCP Events add session webhooks and triggers on MCP server events](https://everruns.com/docs/changelog/2026-10-01-mcp-events-webhooks-triggers/)
With the experimental MCP Events flag, MCP clients subscribe to signed session webhooks, and agents wake on events published by their own MCP servers.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.34.0
5. ### [PR reviewer and security scanner agent templates for GitHub](https://everruns.com/docs/changelog/2026-10-01-github-review-security-templates/)
Two guided agent templates use the agent's GitHub App to review pull requests with inline comments and to run a weekly scan that files GitHub issues.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.34.0
6. ### [Everruns 0.33.0 release](https://everruns.com/docs/changelog/2026-10-01-everruns-0-33-0/)
The agent page is one view-and-edit workspace, the Everruns MCP server renders as an MCP App in ChatGPT and Codex, and triggers react to agent events.
[](https://everruns.com/docs/changelog/2026-10-01-everruns-0-33-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
## September 2026
45 changes · 18 releases
1. September 30, 2026
1. ### [OpenAI hosted code interpreter, shell, file search and remote MCP](https://everruns.com/docs/changelog/2026-09-30-openai-hosted-tools-remote-mcp/)
The openai_server_tools capability adds code interpreter, hosted shell, file search and approval-gated remote MCP next to web search on OpenAI Responses agents.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.33.0
2. ### [GitHub pull request triggers with a per-agent GitHub App](https://everruns.com/docs/changelog/2026-09-30-github-pull-request-triggers/)
Each agent can create and install its own GitHub App by clicks, read pull requests and diffs, post comments, and start sessions from pull request events.
[](https://everruns.com/docs/changelog/2026-09-30-github-pull-request-triggers/)
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.33.0
3. ### [Claude Sonnet 5.5 and GPT-6.1 Sol with fast and ultrafast tiers](https://everruns.com/docs/changelog/2026-09-30-claude-sonnet-5-5-gpt-6-1-sol/)
Claude Sonnet 5.5 becomes the favorite Sonnet model, GPT-6.1 Sol adds fast and ultrafast speed tiers, and the sunset Claude Sonnet 4.5 profile is removed.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.33.0
2. September 29, 2026
1. ### [Everruns MCP server renders as an MCP App in ChatGPT and Codex](https://everruns.com/docs/changelog/2026-09-29-mcp-app-chatgpt-codex/)
MCP App hosts such as ChatGPT and Codex show a live session view, answer agent questions in a form, approve pending actions and open an Everruns panel.
[](https://everruns.com/docs/changelog/2026-09-29-mcp-app-chatgpt-codex/)
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.33.0
2. ### [Agent page rebuilt as one view-and-edit workspace](https://everruns.com/docs/changelog/2026-09-29-agent-page-workspace/)
The agent page shows the system prompt as the page with settings in a side column, and you edit everything in place with one Save or Discard.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.33.0
3. September 26, 2026
1. ### [Everruns 0.32.0: Per-organization Slack app provisioning](https://everruns.com/docs/changelog/2026-09-26-everruns-0-32-0/)
Ask User questions reach MCP, A2A and Tasks clients, and Framework engines gain event listeners, OpenTelemetry export and an app framework.
[](https://everruns.com/docs/changelog/2026-09-26-everruns-0-32-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
4. September 25, 2026
1. ### [Yolop 0.18.4 release](https://everruns.com/docs/changelog/2026-09-25-yolop-0-18-4/)
ACP advertises user-invocable skills to clients and announces finished background work even when the wake turn itself stays silent.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Framework engines get event listeners and OpenTelemetry export](https://everruns.com/docs/changelog/2026-09-25-framework-event-listeners-opentelemetry/)
Register a listener once per Engine to receive events from every session it owns, and export Framework sessions to OpenTelemetry or Braintrust.
[](https://everruns.com/docs/changelog/2026-09-25-framework-event-listeners-opentelemetry/)
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.32.0
3. ### [Ask User questions reach MCP, A2A and Tasks clients](https://everruns.com/docs/changelog/2026-09-25-ask-user-mcp-a2a-clients/)
A session waiting on ask_user now shows its question to MCP clients as a form elicitation, to A2A callers as a typed data part, and to Tasks clients.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.32.0
5. September 24, 2026
1. ### [Everruns 0.31.0: Ask User over MCP and A2A](https://everruns.com/docs/changelog/2026-09-24-everruns-0-31-0/)
Claude always-thinking models no longer return 400s or empty answers, and Ask User lets an agent pause mid-run to ask you a question.
[](https://everruns.com/docs/changelog/2026-09-24-everruns-0-31-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
2. ### [Ask User lets agents pause and ask you questions mid-run](https://everruns.com/docs/changelog/2026-09-24-ask-user-agent-questions/)
Agents with the ask_user capability pause a run to ask one to four choice, text or secret questions, shown as an inline card that you answer in place.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.31.0
6. September 23, 2026
1. ### [Everruns 0.30.0: Ask User](https://everruns.com/docs/changelog/2026-09-23-everruns-0-30-0/)
Agent endpoints and triggers replace Apps, Slack agents ask for approval with buttons and show task progress, and agents can ask the user questions.
[](https://everruns.com/docs/changelog/2026-09-23-everruns-0-30-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
2. ### [Claude always-thinking models no longer return 400s or empty answers](https://everruns.com/docs/changelog/2026-09-23-claude-always-thinking-fixes/)
The Anthropic driver rejects prefill early, maps an explicit none effort to low, and keeps requests append-only on Opus 5.5 and other always-thinking models.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.31.0
7. September 22, 2026
1. ### [Everruns Cloud sign-in failures return to the login page](https://everruns.com/docs/changelog/2026-09-22-cloud-sign-in-failure-recovery/)
A failed Everruns Cloud sign-in now lands on the login page with the reason and a way to try again, instead of a raw JSON error on the callback URL.
Fix[ Cloud](https://everruns.com/docs/changelog/cloud/)
2. ### [Claude Opus 5.5, GPT-6 Sol and GPT-6 Luna models added](https://everruns.com/docs/changelog/2026-09-22-claude-opus-5-5-gpt-6-models/)
Claude Opus 5.5 becomes the recommended Opus model, and GPT-6 Luna replaces GPT-5.6 Terra as the platform default model and the default utility model.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.30.0
3. ### [Bashkit 0.18.2 release](https://everruns.com/docs/changelog/2026-09-22-bashkit-0-18-2/)
sed is rewritten to follow GNU sed 4.9 so scripts written for Linux behave the same, Windows paths resolve correctly, and releases are hardened.
Release[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
8. September 21, 2026
1. ### [sed is rewritten to follow GNU sed 4.9](https://everruns.com/docs/changelog/2026-09-21-bashkit-gnu-aligned-sed/)
Bashkit rewrote its sed after an audit found 46 differences from GNU sed 4.9, adding missing commands, GNU address ranges, case conversion and atomic -i.
Improvement[ Bashkit](https://everruns.com/docs/changelog/bashkit/)In 0.18.2
9. September 19, 2026
1. ### [Install an Everruns agent in Slack with one click](https://everruns.com/docs/changelog/2026-09-19-slack-one-click-install/)
Connecting an agent to Slack no longer needs credentials copied from api.slack.com: Everruns creates the Slack app and takes its credentials from OAuth.
Feature[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Platform](https://everruns.com/docs/changelog/platform/)In 0.30.0
2. ### [MCP catalog presets, acts-as identity and personal connections](https://everruns.com/docs/changelog/2026-09-19-mcp-catalog-connections-acts-as/)
MCP servers gain an org catalog of presets, an actsAs setting for whose sign-in a server uses, an agent MCP tab and a place to revoke your own sign-ins.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.30.0
3. ### [Host Shell gives Framework agents a kernel-contained Bash](https://everruns.com/docs/changelog/2026-09-19-framework-host-shell/)
The new host_shell capability lets an embedded Everruns agent run real host processes inside a Seatbelt or Landlock policy that fails closed.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.30.0
4. ### [Set spend budgets on agent endpoints from the Integrations tab](https://everruns.com/docs/changelog/2026-09-19-agent-endpoint-budgets/)
Agent and endpoint budgets can be managed on the agent's Integrations tab, and sessions are attributed to the endpoint they arrived through.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.30.0
10. September 18, 2026
1. ### [Yolop 0.18.3 release](https://everruns.com/docs/changelog/2026-09-18-yolop-0-18-3/)
yolop coordination spawn-workers prints validated launch commands for each worker, and yolop_spawn routes agent and session spawns separately.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Slack agents ask approval with buttons and show task progress](https://everruns.com/docs/changelog/2026-09-18-slack-approvals-progress-actions/)
Agents deployed to Slack can take approvals through Approve and Decline buttons, show live sub-task progress and act as the channel's own bot.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.30.0
3. ### [Everruns 0.29.0: Platform Chat v2 on a unified command line](https://everruns.com/docs/changelog/2026-09-18-everruns-0-29-0/)
Framework code calls models directly with Model::complete, one everruns command line serves the CLI, MCP and agent shells, and MCP catalogs can be scoped.
[](https://everruns.com/docs/changelog/2026-09-18-everruns-0-29-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
4. ### [Agent endpoints and triggers replace Apps in Everruns](https://everruns.com/docs/changelog/2026-09-18-agent-endpoints-replace-apps/)
App management is retired: an agent's endpoints and triggers now live on its Integrations tab, with an org-wide Exposures view and per-agent suspend.
BreakingMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.30.0
11. September 17, 2026
1. ### [Soft approval: agents pause before destructive or outward actions](https://everruns.com/docs/changelog/2026-09-17-soft-approval-capability/)
The new soft_approval capability, on by default for Generic and Platform Chat, has agents ask before risky actions and records who approved in the audit log.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.29.0
2. ### [Guardrails can use typed judgments as their check engine](https://everruns.com/docs/changelog/2026-09-17-guardrails-typed-judgment-engine/)
LLM judge and moderation guardrails can run on calibrated probabilities from TypeSafe judgments, batching every check on a stage into one request.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)In 0.29.0
3. ### [One everruns command line for the CLI, MCP and agent shells](https://everruns.com/docs/changelog/2026-09-17-everruns-command-line-tree/)
Every Everruns operation now has one noun-verb spelling with --help that works the same in the everruns CLI, the /mcp endpoint and an agent's shell.
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.29.0
4. ### [ScriptedTool accepts clap-based builtins](https://everruns.com/docs/changelog/2026-09-17-bashkit-scripted-tool-clap-builtins/)
ScriptedToolBuilder::builtin registers raw-argv builtins next to ToolDef tools, and closed schemas now reject unknown flags with an error naming valid ones.
Feature[ Bashkit](https://everruns.com/docs/changelog/bashkit/)In 0.18.1
5. ### [Bashkit 0.18.1 release](https://everruns.com/docs/changelog/2026-09-17-bashkit-0-18-1/)
ScriptedTool accepts clap-based builtins, and closed schemas reject unknown flags instead of passing them through to the script.
Release[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
12. September 16, 2026
1. ### [The Everruns UI supports dark mode with a theme setting](https://everruns.com/docs/changelog/2026-09-16-ui-dark-mode-theme/)
The Everruns web UI can now be shown in light, dark or system theme, chosen from the user menu and remembered without a flash on navigation.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.28.0
2. ### [Framework direct model calls with Model::complete](https://everruns.com/docs/changelog/2026-09-16-framework-direct-model-calls/)
The Everruns Framework adds a promoted path for one prompt and one answer, and each driver now reads its vendor's standard credential environment variables.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.29.0
3. ### [Everruns 0.28.0: Agent exposure management](https://everruns.com/docs/changelog/2026-09-16-everruns-0-28-0/)
The Everruns UI gains dark mode with a theme setting, plus agent exposure management and sessions and budgets attributed to the endpoint that started them.
[](https://everruns.com/docs/changelog/2026-09-16-everruns-0-28-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
13. September 15, 2026
1. ### [Everruns agents run in Slack's agent pane](https://everruns.com/docs/changelog/2026-09-15-slack-agent-pane/)
A Slack channel can now serve Slack's agent pane, with streamed replies, a stop button, live status and thread context, alongside channel replies.
[](https://everruns.com/docs/changelog/2026-09-15-slack-agent-pane/)
FeatureMajor[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.27.0
2. ### [Everruns 0.27.0: Slack agent surface](https://everruns.com/docs/changelog/2026-09-15-everruns-0-27-0/)
Sessions show where their commands run and what they may touch, and Everruns agents run in Slack's agent pane with session environments.
[](https://everruns.com/docs/changelog/2026-09-15-everruns-0-27-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
3. ### [Everruns Cloud adds a built-in model provider with starter credit](https://everruns.com/docs/changelog/2026-09-15-cloud-built-in-model-provider/)
New Everruns Cloud organizations run agents without provider keys, paid from prepaid credit that starts at $5 and is topped up in Settings > Billing.
FeatureMajor[ Cloud](https://everruns.com/docs/changelog/cloud/)
14. September 14, 2026
1. ### [Yolop 0.18.2 release](https://everruns.com/docs/changelog/2026-09-14-yolop-0-18-2/)
Setup checks provider auth before finishing model selection, and the CLI attaches through shell descendants so managed sessions stay reachable.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Sessions show where their commands run and what they may touch](https://everruns.com/docs/changelog/2026-09-14-session-environment-panel/)
Each session now declares its execution environment, readable through the API and shown in an Environment panel on the session Workspace tab.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.27.0
3. ### [Everruns Cloud loads Sessions, Harnesses and Skills pages faster](https://everruns.com/docs/changelog/2026-09-14-cloud-faster-page-navigation/)
Moving between the Sessions, Harnesses and Skills pages on Everruns Cloud no longer stalls for seconds while the server renders the page.
Improvement[ Cloud](https://everruns.com/docs/changelog/cloud/)
15. September 13, 2026
1. ### [Everruns 0.26.0: PDF input with file attachments](https://everruns.com/docs/changelog/2026-09-13-everruns-0-26-0/)
Servers publish an MCP server card and auth.md for agent discovery, and an LLM provider connects with one pasted key that is checked before saving.
[](https://everruns.com/docs/changelog/2026-09-13-everruns-0-26-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
16. September 12, 2026
1. ### [Yolop 0.18.1 release](https://everruns.com/docs/changelog/2026-09-12-yolop-0-18-1/)
Named profiles can be managed from the CLI and the control plane, and configured MCP servers are listed in the agent system prompt.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Send a message and wait for the turn result in one request](https://everruns.com/docs/changelog/2026-09-12-wait-for-message-completion/)
The sessions API can now hold a message request open until the turn finishes, and Framework runs accept a timeout, for callers that do not consume SSE.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.26.0
3. ### [Attach PDF files to agent messages](https://everruns.com/docs/changelog/2026-09-12-pdf-file-attachments/)
Messages can now carry PDF attachments, uploaded through a new files API and passed to each provider in its native document format.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.26.0
17. September 11, 2026
1. ### [Session search and self-configuration move to CLI commands](https://everruns.com/docs/changelog/2026-09-11-yolop-sessions-cli-capability-ids/)
Yolop 0.18.0 replaces search_sessions and the configuration tools with yolop sessions, mcp and connectors commands and drops the yolop\_ ID prefix.
Breaking[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.18.0
2. ### [Yolop 0.18.0 release](https://everruns.com/docs/changelog/2026-09-11-yolop-0-18-0/)
Manage and log in to MCP servers from the CLI, and session search and self-configuration move to CLI commands, with fixes listed in the release notes.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
3. ### [Session schedules no longer fire twice on multi-replica deployments](https://everruns.com/docs/changelog/2026-09-11-session-schedules-no-double-fire/)
Everruns servers running more than one replica now claim each due session schedule atomically, so a scheduled message is sent once.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.26.0
4. ### [platform_management capability retired in favor of platform](https://everruns.com/docs/changelog/2026-09-11-retire-platform-management-capability/)
Capabilities gain a deprecated and retired lifecycle, and platform_management is the first one retired: it stays registered but no longer contributes tools.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.25.0
5. ### [Every Everruns user starts in a pinned Platform Chat thread](https://everruns.com/docs/changelog/2026-09-11-platform-chat-thread-every-user/)
New users now land in a precreated, pinned Platform Chat thread instead of an empty Chats list, and Platform Chat gains more built-in capabilities.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.25.0
6. ### [Image generation adds the Muse image model with a fallback](https://everruns.com/docs/changelog/2026-09-11-muse-image-model-fallback/)
The image generation capability can now use Meta's Muse image model through Meta or OpenRouter, and falls back to it when only those credentials exist.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.26.0
7. ### [Connect an LLM provider with one pasted key, checked before saving](https://everruns.com/docs/changelog/2026-09-11-llm-provider-quick-connect/)
LLM provider setup now offers a one-click grid of common providers, verifies the API key with the provider, and enables models and a default for you.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.26.0
8. ### [Everruns 0.25.0: Hierarchical AGENTS.md as conversation context](https://everruns.com/docs/changelog/2026-09-11-everruns-0-25-0/)
AGENTS.md files resolve hierarchically as conversation context, and every Everruns user starts in a pinned Platform Chat thread with more built-in tools.
[](https://everruns.com/docs/changelog/2026-09-11-everruns-0-25-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
9. ### [Everruns servers publish an MCP server card and auth.md](https://everruns.com/docs/changelog/2026-09-11-agent-discovery-documents/)
Every Everruns deployment now serves an MCP Server Card and an auth.md document, derived from live config, so agents can discover how to connect.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.26.0
18. September 10, 2026
1. ### [Manage and log in to MCP servers from the CLI](https://everruns.com/docs/changelog/2026-09-10-yolop-mcp-cli-login/)
The yolop mcp command now covers show, enable, disable and an OAuth login for remote MCP servers, so MCP setup no longer needs the TUI or config files.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.18.0
2. ### [Yolop 0.17.3 release](https://everruns.com/docs/changelog/2026-09-10-yolop-0-17-3/)
Turn activity compacts into one summary row, yolop mcp covers show and disable, cargo install works again, and web_fetch reaches public URLs.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
3. ### [Read-only host directory mounts, also over the C ABI](https://everruns.com/docs/changelog/2026-09-10-bashkit-read-only-host-mounts/)
Bashkit's Python and Node bindings can mount a host directory read-only, and the C API gains allowlisted host-directory mounts on a running session.
Feature[ Bashkit](https://everruns.com/docs/changelog/bashkit/)In 0.18.0
4. ### [Bashkit 0.18.0 release](https://everruns.com/docs/changelog/2026-09-10-bashkit-0-18-0/)
Python, Node and C ABI callers can mount host directories read-only, with security hardening and C ABI improvements across the bindings.
Release[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
5. ### [AGENTS.md files resolve hierarchically as conversation context](https://everruns.com/docs/changelog/2026-09-10-agents-md-hierarchical-context/)
Agent instructions now load AGENTS.md from the workspace root down to the working directory and place them below harness safety instructions.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.25.0
19. September 9, 2026
1. ### [Everruns 0.24.0: \~3.5x faster agent startup](https://everruns.com/docs/changelog/2026-09-09-everruns-0-24-0/)
The first message of a new chat no longer drops or shows out of order, and the CLI plugins, skills and knowledge-bases commands work again.
[](https://everruns.com/docs/changelog/2026-09-09-everruns-0-24-0/)
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
20. September 8, 2026
1. ### [Framework agents start about 3.5 times faster](https://everruns.com/docs/changelog/2026-09-08-faster-framework-agent-startup/)
Building an agent and creating a session in the Everruns Framework now takes roughly a third of the time, with no change to steady-state behavior.
Improvement[ Framework](https://everruns.com/docs/changelog/framework/)In 0.24.0
21. September 4, 2026
1. ### [GPT-6 Astra, Claude Fable 5.1 and Muse Spark 1.3 model profiles](https://everruns.com/docs/changelog/2026-09-04-gpt-6-astra-fable-muse-spark/)
Everruns adds model profiles for OpenAI GPT-6 Astra, Claude Fable 5.1 and Muse Spark 1.3, seeds them for new organizations and covers them in live tests.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.24.0
22. September 3, 2026
1. ### [Turn activity compacts into one summary row by default](https://everruns.com/docs/changelog/2026-09-03-yolop-compact-work-default/)
Fullscreen Yolop turns now collapse narration and tool output into one summary row that Ctrl+O expands; pass --no-compact-work to keep the full transcript.
Improvement[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.17.3
2. ### [MCP URL elicitation pauses for consent in the Everruns UI](https://everruns.com/docs/changelog/2026-09-03-mcp-url-elicitation-consent/)
When an MCP tool asks a person to finish a step in the browser, the turn now pauses on a consent card and the secret never enters the model context.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.24.0
3. ### [OpenTelemetry traces follow Gen-AI and OpenInference conventions](https://everruns.com/docs/changelog/2026-09-03-genai-openinference-trace-conventions/)
Agent turn traces now carry the OpenTelemetry Gen-AI agent attributes and OpenInference attributes on the same spans, selectable per deployment.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.24.0
## August 2026
34 changes · 15 releases
1. August 31, 2026
1. ### [Yolop 0.17.2 release](https://everruns.com/docs/changelog/2026-08-31-yolop-0-17-2/)
Yolop 0.17.2 is a maintenance release with dependency updates and internal fixes and no user-facing change. Every pull request is in the release notes.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Tool calls are validated against their schema before running](https://everruns.com/docs/changelog/2026-08-31-tool-argument-validation-strict/)
Everruns checks every tool call's arguments against the tool's JSON Schema and enables strict schemas for compatible OpenAI Chat and Responses tools.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.24.0
3. ### [Only organization admins can rename an organization](https://everruns.com/docs/changelog/2026-08-31-org-updates-require-admin/)
Every PATCH /v1/orgs/{org} request now requires an organization admin, closing a path where members could rename non-default organizations.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.24.0
4. ### [Organization default model can fall back to the platform default](https://everruns.com/docs/changelog/2026-08-31-org-default-model-platform-fallback/)
An organization's default model is now an explicit override that admins can clear, falling back to a platform default shown in Models and Settings.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.24.0
5. ### [Everruns CLI plugins, skills and knowledge-bases commands work again](https://everruns.com/docs/changelog/2026-08-31-cli-plugins-skills-paths-fix/)
The everruns plugins, skills and knowledge-bases commands called unversioned API paths and got 404 responses; they now use the /v1/ routes.
Fix[ CLI](https://everruns.com/docs/changelog/cli/)In 0.24.0
2. August 27, 2026
1. ### [Yolop 0.17.1 release](https://everruns.com/docs/changelog/2026-08-27-yolop-0-17-1/)
Run models on your machine with the local provider, plus recovery from repeated tool failures, batch file reads, and a lighter default binary.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [First message of a new chat no longer drops or shows out of order](https://everruns.com/docs/changelog/2026-08-27-first-chat-message-fix/)
A new chat thread now replays from its first event when it subscribes, so the opening message is delivered and appears above later replies.
Fix[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.24.0
3. ### [Everruns ID adds a sign-up page and branded account emails](https://everruns.com/docs/changelog/2026-08-27-everruns-id-sign-up-page/)
Creating an Everruns Cloud account now has its own sign-up page, and the verification and password-reset emails use the Everruns layout with a fallback link.
Improvement[ Cloud](https://everruns.com/docs/changelog/cloud/)
3. August 25, 2026
1. ### [Messages API reports reasoning in order and the message phase](https://everruns.com/docs/changelog/2026-08-25-reasoning-ordered-message-phase/)
Session messages now carry ordered reasoning parts plus phase and phase_source, so clients can tell intermediate commentary from the final answer.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.22.0
2. ### [Everruns 0.22.0: Reasoning modeled as ordered artifacts](https://everruns.com/docs/changelog/2026-08-25-everruns-0-22-0/)
Agents read several workspace files in one read_many_files call with bounded batches, and the global chat session endpoints are removed from the API.
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
3. ### [Global chat session endpoints removed from the Everruns API](https://everruns.com/docs/changelog/2026-08-25-chat-singleton-endpoints-removed/)
POST /v1/sessions/chat and POST /v1/sessions/chat/voice are gone; create a chat thread as an ordinary session with POST /v1/sessions instead.
Breaking[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.22.0
4. August 24, 2026
1. ### [Agents read several workspace files in one read_many_files call](https://everruns.com/docs/changelog/2026-08-24-read-many-files-tool/)
The File System capability adds read_many_files, which reads 2 to 10 independent files in one ordered call and halves model round trips for discovery.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)[ Framework](https://everruns.com/docs/changelog/framework/)In 0.22.0
5. August 22, 2026
1. ### [Session pages show a generated run summary and tab counts](https://everruns.com/docs/changelog/2026-08-22-session-run-summary-tab-counts/)
A finished run gets a one-sentence summary in the session header, and the Work, Events and Workspace tabs show how many items each holds.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.21.0
2. ### [Everruns 0.21.0 release](https://everruns.com/docs/changelog/2026-08-22-everruns-0-21-0/)
Binaries are about 20% smaller, chats start with any harness, provider connections carry custom headers, and finished chats can be archived.
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
3. ### [Bashkit 0.17.1 release](https://everruns.com/docs/changelog/2026-08-22-bashkit-0-17-1/)
The native @everruns/bashkit npm package publishes again after stalling at 0.16.0, so Node users get the 0.17 features and fixes.
Release[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
4. ### [Bashkit 0.17.0 release](https://everruns.com/docs/changelog/2026-08-22-bashkit-0-17-0/)
TLS backend and timezone data become explicit features, Bashkit runs as a WebAssembly component without JS or WASI, and host-boundary escapes are closed.
Release[ Bashkit](https://everruns.com/docs/changelog/bashkit/)
5. ### [Archive finished chats and show them again on demand](https://everruns.com/docs/changelog/2026-08-22-archive-chats/)
Archiving hides a chat thread from the Chats list without deleting it, and a Show archived filter and the archive API bring it back when needed.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.21.0
6. August 21, 2026
1. ### [Run models on your machine with the local provider](https://everruns.com/docs/changelog/2026-08-21-yolop-local-inference-provider/)
An experimental local provider runs a model inside the Yolop binary, with Metal and CUDA release builds and weights fetched by yolop weights pull.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.17.1
2. ### [Yolop 0.17.0 release](https://everruns.com/docs/changelog/2026-08-21-yolop-0-17-0/)
Config and data directories can be relocated, model switching uses a curated list you manage, and compiled extensions install from prebuilt binaries.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
3. ### [Provider connections carry custom headers and cache diagnostics](https://everruns.com/docs/changelog/2026-08-21-provider-connection-custom-headers/)
An organization can set extra HTTP headers and a prompt cache diagnostics opt-in on a provider connection, applied to every model call made through it.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.21.0
4. ### [Start a chat with any harness, not only an agent](https://everruns.com/docs/changelog/2026-08-21-chat-with-harnesses/)
The New chat picker lists active harnesses, including Generic, Base and custom ones, next to agents, so a chat no longer needs a placeholder agent.
Feature[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.21.0
5. ### [Runs as a WebAssembly component without JS or WASI](https://everruns.com/docs/changelog/2026-08-21-bashkit-wasm-component-no-wasi/)
Bashkit can run as a component-model WebAssembly guest in plain wasmtime or a Hyperlight micro-VM, with no JavaScript engine and no WASI needed.
Feature[ Bashkit](https://everruns.com/docs/changelog/bashkit/)In 0.17.0
6. ### [TLS backend and timezone data become explicit features](https://everruns.com/docs/changelog/2026-08-21-bashkit-tls-tzdata-features/)
Bashkit 0.17 builds without default features must pick a rustls crypto backend for http_client and enable tzdata to use named time zones.
Breaking[ Bashkit](https://everruns.com/docs/changelog/bashkit/)In 0.17.0
7. August 20, 2026
1. ### [Compiled extensions install from prebuilt binaries](https://everruns.com/docs/changelog/2026-08-20-yolop-extension-prebuilt-binaries/)
yolop extensions install now downloads a compiled extension's server binary for your platform and verifies its SHA-256 checksum before writing anything.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.17.0
2. ### [Model switching uses a curated list you manage](https://everruns.com/docs/changelog/2026-08-20-yolop-curated-model-list/)
/model in Yolop now opens an ordered cross-provider model list managed with yolop models, and the local weights commands move to yolop weights.
Breaking[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.17.0
8. August 19, 2026
1. ### [Yolop 0.16.0 release](https://everruns.com/docs/changelog/2026-08-19-yolop-0-16-0/)
Yolop sets itself up as a Buzz Desktop harness in one command, extensions are managed from the extensions CLI, and profiles can define a whole agent.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
2. ### [Everruns CLI, server and worker binaries are about 20% smaller](https://everruns.com/docs/changelog/2026-08-19-smaller-release-binaries/)
Release builds now strip symbols and the CLI boxes its command dispatch, cutting the CLI by 22% and the server and worker binaries by 18 to 19%.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ CLI](https://everruns.com/docs/changelog/cli/)In 0.21.0
3. ### [Everruns Rust SDK can use native TLS for smaller binaries](https://everruns.com/docs/changelog/2026-08-19-sdk-rust-native-tls-feature/)
TLS in the Everruns Rust SDK is now a Cargo feature: keep the default rustls-tls or pick native-tls, which cuts a minimal client from 6.5 MiB to 3.0 MiB.
Improvement[ SDK](https://everruns.com/docs/changelog/sdk/)In 0.2.1
4. ### [SDK 0.2.1 release](https://everruns.com/docs/changelog/2026-08-19-sdk-0-2-1/)
The Rust SDK can use native TLS for smaller binaries and moves to reqwest 0.13, with dependency and security updates across all three SDKs.
Release[ SDK](https://everruns.com/docs/changelog/sdk/)
5. ### [Register a capability on a running Framework runtime](https://everruns.com/docs/changelog/2026-08-19-register-capabilities-running-runtime/)
A host holding a shared InProcessRuntime can add a capability discovered after startup and activate it in live sessions without rebuilding the runtime.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.20.0
6. ### [Everruns 0.20.0: Register capabilities on a running runtime](https://everruns.com/docs/changelog/2026-08-19-everruns-0-20-0/)
A2A delegation in the everruns crate becomes an opt-in feature, and a running Framework runtime can register new capabilities without a restart.
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
9. August 18, 2026
1. ### [Memory can be scoped to a repository](https://everruns.com/docs/changelog/2026-08-18-yolop-repository-scoped-memory/)
The remember, recall and forget tools in Yolop take a global or repository scope, and repository memory is shared across all linked git worktrees.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.16.0
2. ### [Config and data directories can be relocated](https://everruns.com/docs/changelog/2026-08-18-yolop-relocatable-config-dirs/)
New --config-dir and --data-dir flags, or YOLOP_CONFIG_DIR and YOLOP_DATA_DIR, move Yolop's global settings and session data without changing HOME.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.17.0
3. ### [Local sessions can hand work to each other](https://everruns.com/docs/changelog/2026-08-18-yolop-coordinate-local-sessions/)
The session_coordination capability lets a coordinator Yolop session dispatch durable tasks to opted-in local sessions and wake when they finish.
FeatureMajor[ Yolop](https://everruns.com/docs/changelog/yolop/)SupersededIn 0.16.0
4. ### [Everruns 0.19.0: First-class sandbox and checkpoint records](https://everruns.com/docs/changelog/2026-08-18-everruns-0-19-0/)
Sandboxes and checkpoints become first-class records, giving durable runs an explicit, addressable place to execute and resume from.
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
5. ### [A2A delegation in the everruns crate is now an opt-in feature](https://everruns.com/docs/changelog/2026-08-18-a2a-delegation-opt-in/)
Outbound A2A delegation moved behind a new a2a Cargo feature that is off by default, so local runtime builds no longer compile a second HTTP and TLS stack.
Breaking[ Framework](https://everruns.com/docs/changelog/framework/)In 0.20.0
10. August 17, 2026
1. ### [Profiles can define a whole agent](https://everruns.com/docs/changelog/2026-08-17-yolop-profiles-define-agents/)
A Yolop profile can now carry its own capabilities, MCP servers, skills directory and standing instructions, so one profile flag selects an agent with a job.
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.16.0
2. ### [Extension management moves to the extensions CLI](https://everruns.com/docs/changelog/2026-08-17-yolop-extensions-cli-only/)
Model-callable extension management tools are removed from Yolop; install, enable, disable and reload extensions with the yolop extensions command instead.
Breaking[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.16.0
11. August 16, 2026
1. ### [Everruns 0.18.0: The 0.18 neutral-kernel boundary](https://everruns.com/docs/changelog/2026-08-16-everruns-0-18-0/)
Chats is on for every organization with pinned chats, session recordings split into Transcript, Timeline and Events, and everruns-core becomes a neutral kernel.
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
12. August 15, 2026
1. ### [Set up as a Buzz Desktop harness in one command](https://everruns.com/docs/changelog/2026-08-15-yolop-buzz-desktop-harness/)
The yolop into buzz command writes a custom harness file for Buzz Desktop, so Buzz can start Yolop over ACP without anyone editing harness JSON by hand.
[](https://everruns.com/docs/changelog/2026-08-15-yolop-buzz-desktop-harness/)
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.16.0
2. ### [Yolop 0.15.2 release](https://everruns.com/docs/changelog/2026-08-15-yolop-0-15-2/)
OpenRouter connects with a browser sign-in, and Terra and Luna model variants join the OpenAI and Codex selectors ahead of Sol.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
3. ### [Yolop 0.15.1 release](https://everruns.com/docs/changelog/2026-08-15-yolop-0-15-1/)
The Meta Model API joins the supported providers, and terminal text selection survives Ctrl and Ctrl+C, on the tuika 0.8.0 toolkit.
Release[ Yolop](https://everruns.com/docs/changelog/yolop/)
4. ### [everruns-core 0.18 becomes a neutral execution kernel](https://everruns.com/docs/changelog/2026-08-15-everruns-core-neutral-kernel/)
Hosted platform records and services moved out of everruns-core into everruns-platform and sibling crates, and the everruns-runtime crate was removed.
BreakingMajor[ Framework](https://everruns.com/docs/changelog/framework/)In 0.18.0
13. August 14, 2026
1. ### [OpenRouter connects with a browser sign-in](https://everruns.com/docs/changelog/2026-08-14-yolop-openrouter-browser-login/)
Yolop's /setup can sign in to OpenRouter in the browser with PKCE and store the minted API key, so you no longer paste or export a key by hand.
[](https://everruns.com/docs/changelog/2026-08-14-yolop-openrouter-browser-login/)
Feature[ Yolop](https://everruns.com/docs/changelog/yolop/)In 0.15.2
2. ### [Framework workspaces gain multiple isolated Git heads](https://everruns.com/docs/changelog/2026-08-14-framework-multi-head-workspaces/)
Framework sessions can bind to one named head of a workspace, such as a local Git worktree, with fork, archive and destroy as explicit operations.
Feature[ Framework](https://everruns.com/docs/changelog/framework/)In 0.18.0
3. ### [Knowledge index embedding spend is metered and ledgered](https://everruns.com/docs/changelog/2026-08-14-embedding-spend-metered/)
Query embeddings made by search_index now count toward session cost and budgets, and index-sync embeddings are recorded against the organization.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.18.0
14. August 10, 2026
1. ### [Session recordings split into Transcript, Timeline and Events](https://everruns.com/docs/changelog/2026-08-10-session-transcript-timeline-events/)
A session page now offers three full-width views of a run: a read-only conversation replay, a curated execution history and the raw event ledger.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.18.0
2. ### [Everruns 0.17.26: Live, steerable sessions](https://everruns.com/docs/changelog/2026-08-10-everruns-0-17-26/)
Live, steerable sessions, Sessions as the operational list, advanced capability authoring, and recovery for Daytona workspaces.
Release[ Platform](https://everruns.com/docs/changelog/platform/)[ Framework](https://everruns.com/docs/changelog/framework/)[ CLI](https://everruns.com/docs/changelog/cli/)
3. ### [Chats is on for every organization, with pinned chats](https://everruns.com/docs/changelog/2026-08-10-chats-for-every-organization/)
Chats no longer depends on a feature flag, so every organization gets the chat list, search and new-chat flow, and threads can be pinned to the top.
Improvement[ Platform](https://everruns.com/docs/changelog/platform/)[ Cloud](https://everruns.com/docs/changelog/cloud/)In 0.18.0
---
# Chats is on for every organization, with pinned chats
> Chats no longer depends on a feature flag, so every organization gets the chat list, search and new-chat flow, and threads can be pinned to the top.
Source:
Chats is now core functionality. The chat API, the **Chats** sidebar entry, search, the new-chat flow and chat threads work in every organization without a setting in **Settings → Features**, and the Experimental badge is gone. Voice keeps its own separate setting.
Chat threads can also be pinned: use the pin button in a thread header to keep it at the top of the Chats list and the sidebar.
See [Web UI](https://everruns.com/docs/features/ui/), [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) and [#3100](https://github.com/everruns/everruns/pull/3100).
---
# Everruns 0.17.26: Live, steerable sessions
> Live, steerable sessions, Sessions as the operational list, advanced capability authoring, and recovery for Daytona workspaces.
Source:
## Highlights
* **Live, steerable sessions**: Sessions are now live and steerable, with canonical session events, session history and resume, and a session work and wake API ([#3095](https://github.com/everruns/everruns/pull/3095), [#3090](https://github.com/everruns/everruns/pull/3090), [#3089](https://github.com/everruns/everruns/pull/3089), [#3083](https://github.com/everruns/everruns/pull/3083)).
* **Sessions as the operational list**: The Sessions surface was rebuilt as the operational list, recording session source and offering a filterable list with facet counts ([#3078](https://github.com/everruns/everruns/pull/3078), [#3073](https://github.com/everruns/everruns/pull/3073)).
* **Advanced capability authoring**: New advanced capability authoring API with typed lifecycle hooks, a workspace access policy, unified capability configuration, and separated provider and model configuration ([#3084](https://github.com/everruns/everruns/pull/3084), [#3086](https://github.com/everruns/everruns/pull/3086), [#3085](https://github.com/everruns/everruns/pull/3085), [#3096](https://github.com/everruns/everruns/pull/3096), [#3093](https://github.com/everruns/everruns/pull/3093)).
* **Daytona workspace recovery**: Sandboxes recover Daytona workspaces after instance loss ([#3092](https://github.com/everruns/everruns/pull/3092)).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.17.26) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#01726---2026-08-10) for every pull request in this version.
---
# Session recordings split into Transcript, Timeline and Events
> A session page now offers three full-width views of a run: a read-only conversation replay, a curated execution history and the raw event ledger.
Source:
A session page now shows a run in three separate tabs instead of one combined Timeline:
* **Transcript** is the default view: a full-width, read-only replay of the conversation.
* **Timeline** is the execution history, with noisy worker details collapsed.
* **Events** is the complete raw event ledger.
Session links, global search and **Fork into chat** open the Transcript. Work, Workspace and Cost behave as before.
See [Web UI](https://everruns.com/docs/features/ui/) and [#3113](https://github.com/everruns/everruns/pull/3113).
---
# Knowledge index embedding spend is metered and ledgered
> Query embeddings made by search_index now count toward session cost and budgets, and index-sync embeddings are recorded against the organization.
Source:
Embedding calls are now part of usage reporting. Each query embedding the `search_index` tool makes emits an `llm.generation` event, so it reaches the session and agent totals and is debited from budgets like chat generations. Usage is recorded even when a search matches nothing.
Knowledge index sync, which embeds every indexed chunk in the background, now writes its spend to the usage ledger against the organization. Sync spend is not debited from any user’s budget, because no session triggered it.
A session budget now covers its knowledge searches too:
```bash
everruns sessions create --agent support --budget-limit usd:10
```
See [Knowledge Index](https://everruns.com/docs/capabilities/knowledge-index/), [Budgets](https://everruns.com/docs/advanced/budgets/), [#3149](https://github.com/everruns/everruns/pull/3149) and [#3163](https://github.com/everruns/everruns/pull/3163).
## Update 2026-10-23
The undeployed split knowledge features have been consolidated into [Knowledge](https://everruns.com/docs/capabilities/knowledge/). Its query embeddings retain session usage events; background indexing remains organization-attributed.
---
# Framework workspaces gain multiple isolated Git heads
> Framework sessions can bind to one named head of a workspace, such as a local Git worktree, with fork, archive and destroy as explicit operations.
Source:
The `everruns` crate now has public workspace and environment types, so several sessions can work on one repository without sharing files. A workspace has named heads; each session binds permanently to exactly one head and resumes on that same head.
With the `local` feature, a local Git-worktree provider creates each head as a worktree from a revision. Heads are isolated by default; opt into a shared head explicitly. Fork, checkpoint, archive and destroy are explicit calls, and dropping a value never deletes a worktree. The provider interface is open for other backends.
```rust
let backend = Arc::new(LocalGitWorkspace::new(state)?);
let workspace = Workspace::open(backend, repository.to_string_lossy()).await?;
let head = workspace.head("feature").from_revision("main").create().await?;
let session = engine.create(agent).workspace(head).start().await?;
```
See [Workspaces and environments](https://everruns.com/docs/framework/workspaces-and-environments/) and [#3166](https://github.com/everruns/everruns/pull/3166).
---
# OpenRouter connects with a browser sign-in
> Yolop's /setup can sign in to OpenRouter in the browser with PKCE and store the minted API key, so you no longer paste or export a key by hand.
Source:

OpenRouter can now be connected from the browser. In `/setup`, pick OpenRouter and then **Sign in with browser**: Yolop opens `openrouter.ai/auth`, waits on a loopback callback, exchanges the code for an API key and stores it the same way as a pasted key. ACP clients see the method as `openrouter_browser`.
A pasted key and `OPENROUTER_API_KEY` still work, and the environment variable wins when both are set.
See [Providers and models](https://github.com/everruns/yolop#providers-and-models) and [#567](https://github.com/everruns/yolop/pull/567).
---
# everruns-core 0.18 becomes a neutral execution kernel
> Hosted platform records and services moved out of everruns-core into everruns-platform and sibling crates, and the everruns-runtime crate was removed.
Source:
In 0.18, `everruns-core` keeps only the execution kernel. Persisted records and hosted services (sessions, agents and agent versions, harnesses, evals and observers, connectors, system email, feature flags, OAuth and credential infrastructure) moved to `everruns-platform`, `everruns-host`, `everruns-provider` and other owning crates. The logic-free `everruns-runtime` compatibility crate is removed, and engines now own the session lifecycle on one execution path for in-process and durable runs.
REST and gRPC shapes and the stored schema are unchanged; code that imports from `everruns_core` needs new paths. Self-hosted servers need no change. For example, host composition moved:
0.17
```rust
use everruns_core::PlatformDefinition;
// 0.18
use everruns_host::HostComposition;
```
See [Upgrade notes: 0.18](https://everruns.com/docs/framework/upgrade-notes/), [#3160](https://github.com/everruns/everruns/pull/3160), [#3101](https://github.com/everruns/everruns/pull/3101) and [#3179](https://github.com/everruns/everruns/pull/3179).
## Update 2026-10-04
Several crates named here were later consolidated: `everruns-platform` became `everruns-capabilities` in 0.39, and `everruns-host` became an `everruns-core` feature in 0.40. See [Framework crates consolidated into everruns-core and the facade](https://everruns.com/docs/changelog/2026-10-04-framework-crates-consolidated/).
---
# Yolop 0.15.1 release
> The Meta Model API joins the supported providers, and terminal text selection survives Ctrl and Ctrl+C, on the tuika 0.8.0 toolkit.
Source:
## Highlights
* Model selection is honest about what is reachable: ACP exposes only connected models, duplicate model names resolve uniquely, and provider model catalogs are listable over ACP.
* The Meta Model API joins the supported providers.
* Long-running turns behave better under stress, stall recovery shares the watchdog’s budget, blocked completion status stays hidden, and workspace grep scans stream instead of buffering.
* Terminal selection survives Ctrl and Ctrl+C, on tuika 0.8.0 with matching satellite crates.
* Skill management (`search_skills` / `install_skill` / `delete_skill`) is now actually enabled in the coding harness, matching what the docs already promised.
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.15.1) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0151---2026-08-15) for every pull request in this version.
---
# Yolop 0.15.2 release
> OpenRouter connects with a browser sign-in, and Terra and Luna model variants join the OpenAI and Codex selectors ahead of Sol.
Source:
## Highlights
* OpenRouter can be authenticated from the browser: `--provider openrouter` runs a PKCE login instead of requiring a hand-pasted key.
* Terra and Luna model variants join the OpenAI and Codex selectors, listed ahead of Sol.
* Malformed tool calls no longer reach execution, every provider’s model-authored arguments are validated against the authoritative JSON Schema first, and `progress_checkpoint` stays callable when the progress gate demands it.
* ACP recovers provider authentication mid-session rather than stalling on an expired credential.
* The tmux gallery render is now gated on every PR by a real terminal implementation, so the release spec’s terminal matrix reserves the human walk for GUI emulators.
* Tracing output no longer scribbles over terminal rows the TUI owns.
## In the changelog
* [OpenRouter connects with a browser sign-in](https://everruns.com/docs/changelog/2026-08-14-yolop-openrouter-browser-login/)
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.15.2) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0152---2026-08-15) for every pull request in this version.
---
# Set up as a Buzz Desktop harness in one command
> The yolop into buzz command writes a custom harness file for Buzz Desktop, so Buzz can start Yolop over ACP without anyone editing harness JSON by hand.
Source:

`yolop into buzz` writes a custom harness file for Buzz Desktop, next to the existing `yolop into zed` and `yolop into paseo` commands. Buzz then starts Yolop over ACP.
```bash
yolop into buzz
```
Running the command again keeps your `env` values and any extra fields in the file; `--force` replaces the file completely.
See [In your editor, over ACP](https://github.com/everruns/yolop#in-your-editor-over-acp) and [#579](https://github.com/everruns/yolop/pull/579).
---
# Everruns 0.18.0: The 0.18 neutral-kernel boundary
> Chats is on for every organization with pinned chats, session recordings split into Transcript, Timeline and Events, and everruns-core becomes a neutral kernel.
Source:
## Highlights
* **The 0.18 neutral-kernel boundary**: `everruns-core` is now a neutral execution kernel. Hosted platform records, sessions, agents and agent versions, harnesses, evals and observers, connectors, system email, feature flags, and OAuth/credential infrastructure, moved out to `everruns-platform` (and sibling crates), and the logic-free `everruns-runtime` compatibility crate was removed. REST/gRPC shapes and stored schema are unchanged; see the [0.18 migration guide](https://github.com/everruns/everruns/pull/3148) and the **Breaking** notes below ([#3160](https://github.com/everruns/everruns/pull/3160), [#3101](https://github.com/everruns/everruns/pull/3101)).
* **Engine-owned sessions and unified execution**: Engines now own the session lifecycle, and a single execution path serves both in-process and durable runs ([#3179](https://github.com/everruns/everruns/pull/3179), [#3173](https://github.com/everruns/everruns/pull/3173), [#3167](https://github.com/everruns/everruns/pull/3167)).
* **Multi-head workspaces**: Framework workspaces now support multiple heads ([#3166](https://github.com/everruns/everruns/pull/3166)).
* **Metered embedding spend**: Retrieval and knowledge-index embedding spend is now metered and ledgered against the organization ([#3149](https://github.com/everruns/everruns/pull/3149), [#3163](https://github.com/everruns/everruns/pull/3163)).
* **Chats as core functionality**: Chats is always present in navigation and search for every organization with no feature opt-in, and now supports chat pinning.
## In the changelog
* [Chats is on for every organization, with pinned chats](https://everruns.com/docs/changelog/2026-08-10-chats-for-every-organization/)
* [Session recordings split into Transcript, Timeline and Events](https://everruns.com/docs/changelog/2026-08-10-session-transcript-timeline-events/)
* [Knowledge index embedding spend is metered and ledgered](https://everruns.com/docs/changelog/2026-08-14-embedding-spend-metered/)
* [Framework workspaces gain multiple isolated Git heads](https://everruns.com/docs/changelog/2026-08-14-framework-multi-head-workspaces/)
* [everruns-core 0.18 becomes a neutral execution kernel](https://everruns.com/docs/changelog/2026-08-15-everruns-core-neutral-kernel/)
Before upgrading, read the breaking changes: [everruns-core 0.18 becomes a neutral execution kernel](https://everruns.com/docs/changelog/2026-08-15-everruns-core-neutral-kernel/).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.18.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0180---2026-08-16) for every pull request in this version.
---
# Extension management moves to the extensions CLI
> Model-callable extension management tools are removed from Yolop; install, enable, disable and reload extensions with the yolop extensions command instead.
Source:
Yolop no longer gives the model tools to install, enable, disable or reload extensions. Use `yolop extensions` instead: `list`, `install`, `remove`, `enable`, `disable`, `reload`, `doctor`, `secret` and `scaffold`. Run from a session’s own shell, `enable`, `disable` and `reload` act on that running session. Run from another terminal, they change global settings, and `reload` is rejected because there is no session to restart.
```bash
yolop extensions install crates.io:yolop-extension-lsp
```
`/extensions`, and the commands and tools that installed extensions contribute, are unchanged.
See [Extensions](https://github.com/everruns/yolop/blob/main/docs/extensions.md) and [#587](https://github.com/everruns/yolop/pull/587).
---
# Profiles can define a whole agent
> A Yolop profile can now carry its own capabilities, MCP servers, skills directory and standing instructions, so one profile flag selects an agent with a job.
Source:
A named profile used to switch only execution settings, such as the provider. It can now also decide what the agent can do:
* `capabilities` and `capabilities_mode`: capability entries layered after the global list or replacing it, including which extensions are enabled.
* `mcp` and `mcp_mode`: MCP servers merged by name over the global set, or replacing it.
* `instructions` or `instructions_file`: text appended to the system prompt.
* `skills_dir`: an extra skills scope, by default `profiles//skills/`.
Start a session with `yolop --profile `. Credentials stay global. A profile at `profiles/triage.toml` under the config directory:
```toml
instructions = "You are the triage agent."
mcp_mode = "replace"
[[capabilities]]
ref = "ext:triage"
[mcp.servers.linear]
type = "http"
url = "https://profile.example/mcp"
```
See [Session coordination](https://github.com/everruns/yolop/blob/main/docs/session-coordination.md) for a profile example and [#586](https://github.com/everruns/yolop/pull/586).
---
# A2A delegation in the everruns crate is now an opt-in feature
> Outbound A2A delegation moved behind a new a2a Cargo feature that is off by default, so local runtime builds no longer compile a second HTTP and TLS stack.
Source:
The `local` feature of the `everruns` crate no longer turns on A2A delegation. Applications that delegate to remote A2A agents must enable the new `a2a` feature:
```toml
everruns = { version = "*", features = ["local", "a2a"] }
```
Builds without it skip the A2A client and its HTTP and TLS dependencies, and the A2A delegation capability is not registered.
See [A2A](https://everruns.com/docs/framework/a2a/), [A2A agent delegation](https://everruns.com/docs/capabilities/a2a-agent-delegation/) and [#3221](https://github.com/everruns/everruns/pull/3221).
---
# Everruns 0.19.0: First-class sandbox and checkpoint records
> Sandboxes and checkpoints become first-class records, giving durable runs an explicit, addressable place to execute and resume from.
Source:
## Highlights
* **First-class sandbox and checkpoint records**: Sandboxes and checkpoints are now first-class records, giving durable runs an explicit, addressable execution substrate ([#3197](https://github.com/everruns/everruns/pull/3197)).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.19.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0190---2026-08-18) for every pull request in this version.
---
# Local sessions can hand work to each other
> The session_coordination capability lets a coordinator Yolop session dispatch durable tasks to opted-in local sessions and wake when they finish.
Source:
Yolop can coordinate work across separately running sessions in the same project. A coordinator profile discovers opted-in worker sessions, reserves one, dispatches a durable task and is woken when the worker finishes. A worker receives the assignment as a wake and settles it with validation notes and artifact references.
Agents and people drive it with `yolop coordination` (`list`, `status`, `dispatch`, `complete`, `accept`, `drain`), which acts on the live session, and a matching `/coordination` command.
```bash
yolop coordination list --json
```
See [CLI essentials](https://github.com/everruns/yolop#cli-essentials), [#588](https://github.com/everruns/yolop/pull/588) and [#594](https://github.com/everruns/yolop/pull/594).
---
# Config and data directories can be relocated
> New --config-dir and --data-dir flags, or YOLOP_CONFIG_DIR and YOLOP_DATA_DIR, move Yolop's global settings and session data without changing HOME.
Source:
Yolop’s global settings directory and its data directory can now be moved with `--config-dir` and `--data-dir`, or with the `YOLOP_CONFIG_DIR` and `YOLOP_DATA_DIR` environment variables. The value names Yolop’s own directory, so nothing appends another `yolop` folder, and a relative path resolves against the directory Yolop started in.
```bash
yolop --config-dir ./tmp/yolop-config --data-dir ./tmp/yolop-data
```
Isolated runs, or several Yolop identities on one machine, no longer need a different `HOME`.
See [CLI essentials](https://github.com/everruns/yolop#cli-essentials) and [#600](https://github.com/everruns/yolop/pull/600).
---
# Memory can be scoped to a repository
> The remember, recall and forget tools in Yolop take a global or repository scope, and repository memory is shared across all linked git worktrees.
Source:
Yolop’s `remember`, `recall` and `forget` tools take a `scope` argument: `global`, the default as before, or `repository`. Repository memory is stored in `.yolop/memories.json` under the main repository root, so every linked worktree of that repository reads the same notes. Global memory still follows you across projects.
The agent picks the scope when it calls the tool, and the system prompt labels global and repository memory separately. Any other scope value is rejected. A `remember` call that saves a note for the current repository:
```json
{
"title": "Test command",
"memory": "Run cargo test --workspace before pushing.",
"scope": "repository"
}
```
See [Skills, memory, and hooks](https://github.com/everruns/yolop#skills-memory-and-hooks) and [#584](https://github.com/everruns/yolop/pull/584).
---
# Everruns 0.20.0: Register capabilities on a running runtime
> A2A delegation in the everruns crate becomes an opt-in feature, and a running Framework runtime can register new capabilities without a restart.
Source:
## Highlights
* **Register capabilities on a running runtime**: A host holding `Arc` can now make a capability discovered after composition resolvable without rebuilding the runtime, via `register_capability`/`is_capability_registered`. Registration stays separate from activation, and readers always see a consistent registry snapshot ([#3224](https://github.com/everruns/everruns/pull/3224)).
* **A2A delegation is now opt-in**: Outbound A2A delegation moved behind a new `a2a` Cargo feature, off by default, so the standard build no longer drags a second HTTP/TLS stack into consumers that only run the local runtime. Hosts that delegate to remote A2A agents enable the `a2a` feature explicitly ([#3221](https://github.com/everruns/everruns/pull/3221)).
## In the changelog
* [A2A delegation in the everruns crate is now an opt-in feature](https://everruns.com/docs/changelog/2026-08-18-a2a-delegation-opt-in/)
* [Register a capability on a running Framework runtime](https://everruns.com/docs/changelog/2026-08-19-register-capabilities-running-runtime/)
Before upgrading, read the breaking changes: [A2A delegation in the everruns crate is now an opt-in feature](https://everruns.com/docs/changelog/2026-08-18-a2a-delegation-opt-in/).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.20.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0200---2026-08-19) for every pull request in this version.
---
# Register a capability on a running Framework runtime
> A host holding a shared InProcessRuntime can add a capability discovered after startup and activate it in live sessions without rebuilding the runtime.
Source:
An `InProcessRuntime` can now accept capabilities after composition, for example an extension a user installs mid-conversation:
```rust
runtime.register_capability(Arc::new(capability))?;
runtime.is_capability_registered("ext:name");
```
Registration only makes the id resolvable. Sessions still enable it with `activate_capability`, so sessions that never activate it see no change. A duplicate id or a colliding alias is rejected and the existing capability stays. Readers always see a consistent registry snapshot.
See [Advanced capabilities](https://everruns.com/docs/framework/advanced-capabilities/) and [#3224](https://github.com/everruns/everruns/pull/3224).
---
# SDK 0.2.1 release
> The Rust SDK can use native TLS for smaller binaries and moves to reqwest 0.13, with dependency and security updates across all three SDKs.
Source:
## Highlights
* Rust SDK footprint trimmed for consumer binaries: leaner tokio/futures/reqwest feature sets, and TLS is now a Cargo feature (default `rustls-tls`, or `native-tls` for a much smaller binary).
* Rust SDK upgraded to reqwest 0.13.
* Dependency and security updates across all SDKs.
* Documentation polish: Everruns logo in the README, plainer punctuation in prose.
## In the changelog
* [Everruns Rust SDK can use native TLS for smaller binaries](https://everruns.com/docs/changelog/2026-08-19-sdk-rust-native-tls-feature/)
See the [GitHub release](https://github.com/everruns/sdk/releases/tag/v0.2.1) and the [full release notes](https://github.com/everruns/sdk/blob/main/CHANGELOG.md#021---2026-08-19) for every pull request in this version.
---
# Everruns Rust SDK can use native TLS for smaller binaries
> TLS in the Everruns Rust SDK is now a Cargo feature: keep the default rustls-tls or pick native-tls, which cuts a minimal client from 6.5 MiB to 3.0 MiB.
Source:
TLS in the `everruns-sdk` crate is now a Cargo feature. The default `rustls-tls` bundles rustls and needs no system TLS library. `native-tls` links the platform TLS stack instead, which needs OpenSSL or the platform equivalent on the build and target machines.
```toml
everruns-sdk = { version = "0.2", default-features = false, features = ["native-tls"] }
```
On a minimal client that creates a session and streams events, a stripped release build is 6.5 MiB with `rustls-tls` and 3.0 MiB with `native-tls`. Leaner tokio, futures and reqwest feature sets also trim the default build, and the SDK moves to reqwest 0.13.
See [SDKs](https://everruns.com/docs/features/sdk/), [#110](https://github.com/everruns/sdk/pull/110) and [#109](https://github.com/everruns/sdk/pull/109).
---
# Everruns CLI, server and worker binaries are about 20% smaller
> Release builds now strip symbols and the CLI boxes its command dispatch, cutting the CLI by 22% and the server and worker binaries by 18 to 19%.
Source:
Shipped release artifacts are smaller. Symbols are stripped from release builds, and each `everruns` CLI subcommand runs behind a boxed future so command code is no longer duplicated into the entry point.
The CLI binary went from 18.1 MB to 14.0 MB (22.4%). The server and worker binaries are 18.3% and 19.0% smaller from stripping alone. Behavior is unchanged. Builds from source get the same result from the workspace release profile:
```toml
[profile.release]
strip = true
```
See [CLI](https://everruns.com/docs/features/cli/) and [#3227](https://github.com/everruns/everruns/pull/3227).
---
# Yolop 0.16.0 release
> Yolop sets itself up as a Buzz Desktop harness in one command, extensions are managed from the extensions CLI, and profiles can define a whole agent.
Source:
## Highlights
* Sessions can hand work to each other: the built-in `session_coordination` capability discovers opt-in local sessions in the same project, reserves one, dispatches a durable task, and wakes the coordinator on completion, all through `yolop coordination`.
* Extension and coordination administration moved from model tools to an attached CLI. `yolop extensions ...` and `yolop coordination ...` run against the live session over one-shot anonymous pipes, so enable, disable, and reload affect the session you are in without spending model context on tool schemas.
* Profiles now define a whole agent, not just execution settings: `capabilities`, `mcp`, `skills_dir`, and standing `instructions` are all selectable per run.
* `run_command` covers the entire command registry on every host. The nine-command allowlist is gone, and ACP and `--print` sessions get command dispatch for the first time.
* An experimental in-process `local` provider runs a model inside the binary, with `yolop models pull` owning the weights. Off in default features so source builds stay fast; the release binaries carry it.
* Memory is scoped: `remember`, `recall`, and `forget` take `global` (the default) or `repository`, with repository memory shared across linked worktrees.
* Editor integration widens: `yolop into buzz` writes a Buzz Desktop harness, and ACP clients can be handed an opt-in loopback setup page (`--acp-setup-page`) so API-key providers have an in-conversation path.
* Copying wrapped text out of the transcript no longer inserts newlines that were never there, on tuika 0.10.
## In the changelog
* [Set up as a Buzz Desktop harness in one command](https://everruns.com/docs/changelog/2026-08-15-yolop-buzz-desktop-harness/)
* [Extension management moves to the extensions CLI](https://everruns.com/docs/changelog/2026-08-17-yolop-extensions-cli-only/)
* [Profiles can define a whole agent](https://everruns.com/docs/changelog/2026-08-17-yolop-profiles-define-agents/)
* [Local sessions can hand work to each other](https://everruns.com/docs/changelog/2026-08-18-yolop-coordinate-local-sessions/)
* [Memory can be scoped to a repository](https://everruns.com/docs/changelog/2026-08-18-yolop-repository-scoped-memory/)
Before upgrading, read the breaking changes: [Extension management moves to the extensions CLI](https://everruns.com/docs/changelog/2026-08-17-yolop-extensions-cli-only/).
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.16.0) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0160---2026-08-19) for every pull request in this version.
---
# Model switching uses a curated list you manage
> /model in Yolop now opens an ordered cross-provider model list managed with yolop models, and the local weights commands move to yolop weights.
Source:
Switching models in Yolop now offers an ordered list across providers, instead of whatever one provider’s models API returns. `/model` and the status bar open the list, and **Browse all models…** still reaches the per-provider catalog. Each entry pairs a provider with a model, plus an optional effort and label, so one pick switches both. Manage the list with `yolop models list`, `add`, `rm`, `move`, `use` and `reset`.
**Upgrade notes:** the local weights commands moved to `yolop weights`, so `yolop models pull` is now `yolop weights pull`. The per-provider `models` config key is now `default_models`; an existing `models` table is still read.
```bash
# Before
yolop models pull
# After
yolop weights pull
```
See [Providers and models](https://github.com/everruns/yolop#providers-and-models) and [#618](https://github.com/everruns/yolop/pull/618).
---
# Compiled extensions install from prebuilt binaries
> yolop extensions install now downloads a compiled extension's server binary for your platform and verifies its SHA-256 checksum before writing anything.
Source:
Extensions whose server is a compiled program now install end to end. A package can declare `capabilityServer.binaries`, a URL template over `{name}`, `{version}` and `{target}`. When the server command is not already available, `yolop extensions install` downloads the archive for your machine’s target triple and places the binary in the package’s `bin/` directory.
The archive’s `.sha256` file is required: an archive without one, or with a mismatched checksum, is refused and nothing is written. `yolop-extension-logfire` 0.2.0 is the first extension published this way. Its manifest declares:
```json
{
"yolop": {
"capabilityServer": {
"command": "yolop-extension-logfire",
"binaries": "https://github.com/everruns/yolop/releases/download/yolop-extension-logfire-v{version}/yolop-extension-logfire-{version}-{target}.tar.gz"
}
}
}
```
See [Extensions](https://github.com/everruns/yolop/blob/main/docs/extensions.md) and [#620](https://github.com/everruns/yolop/pull/620).
---
# TLS backend and timezone data become explicit features
> Bashkit 0.17 builds without default features must pick a rustls crypto backend for http_client and enable tzdata to use named time zones.
Source:
Bashkit 0.17.0 changes two Cargo features. Default builds are unchanged; only builds with `default-features = false` need to act.
* `http_client` no longer implies a rustls crypto backend. Add `ring` (the previous behavior, on by default) or `aws-lc-rs` for a FIPS-capable backend. Enabling neither is a compile error.
* Timezone data is behind the `tzdata` feature. Without it, `date` with `TZ=` exits 1 instead of printing a UTC time.
```toml
bashkit = { version = "0.17", default-features = false, features = ["http_client", "ring", "tzdata"] }
```
Non-ASCII domain names in HTTP allowlists are now rejected rather than normalized; use their punycode form.
See [Networking](https://github.com/everruns/bashkit/blob/main/docs/networking.md), [#2317](https://github.com/everruns/bashkit/pull/2317) and [#2321](https://github.com/everruns/bashkit/pull/2321).
---
# Runs as a WebAssembly component without JS or WASI
> Bashkit can run as a component-model WebAssembly guest in plain wasmtime or a Hyperlight micro-VM, with no JavaScript engine and no WASI needed.
Source:
Bashkit can now run as a WebAssembly component that exposes shell execution over WIT, in a host with no JavaScript engine and no WASI implementation. That covers plain wasmtime and the `no_std` wasmtime that hyperlight-wasm compiles into a Hyperlight micro-VM guest. `examples/hyperlight/` builds such a guest and runs a script through it:
```bash
rustup target add wasm32-unknown-unknown
cargo install wasm-tools
./examples/hyperlight/build-and-run.sh
```
Dependencies that assumed a JavaScript host, such as the clock, timers and entropy source, moved behind a new `wasm_js` feature that the JS packages enable and other builds leave off. The `bashkit` crate also builds for `wasm32-wasip2`, checked in CI.
See the [Hyperlight example](https://github.com/everruns/bashkit/tree/main/examples/hyperlight), [#2311](https://github.com/everruns/bashkit/pull/2311) and [#2310](https://github.com/everruns/bashkit/pull/2310).
---
# Start a chat with any harness, not only an agent
> The New chat picker lists active harnesses, including Generic, Base and custom ones, next to agents, so a chat no longer needs a placeholder agent.
Source:
**New chat** now shows two labelled groups, **Harnesses** and **Agents**. Pick any active harness, such as Generic, Base or one of your custom harnesses, to start a chat bound directly to it. Before, the picker listed agents plus a single hard-coded Platform Chat option, so chatting with a harness meant creating an otherwise empty agent.
The thread names the harness as its counterpart and appears in the Chats list like any other chat.
See [Harnesses](https://everruns.com/docs/features/harnesses/), [Generic harness](https://everruns.com/docs/built-ins/harnesses/generic/) and [#3262](https://github.com/everruns/everruns/pull/3262).
---
# Provider connections carry custom headers and cache diagnostics
> An organization can set extra HTTP headers and a prompt cache diagnostics opt-in on a provider connection, applied to every model call made through it.
Source:
A provider connection now has request options that apply to every call through it, such as a header a gateway requires.
**In the UI:** open a provider in **Settings → Providers** and use the **Advanced** card to add **Custom headers** or turn on **Prompt cache diagnostics**.
**In the API:** the provider settings accept the same options. Transport headers such as `host` and `content-length` are rejected, the list is limited to 16 headers of up to 2 KB each, and header values are redacted in diagnostics. The Anthropic, Gemini, Chat Completions and Open Responses drivers apply them.
See [Providers](https://everruns.com/docs/providers/), [#3232](https://github.com/everruns/everruns/pull/3232) and [#3240](https://github.com/everruns/everruns/pull/3240).
---
# Yolop 0.17.0 release
> Config and data directories can be relocated, model switching uses a curated list you manage, and compiled extensions install from prebuilt binaries.
Source:
## Highlights
* Compiled extensions install end to end. A package may declare `capabilityServer.binaries`, and `yolop extensions install` fetches the prebuilt server for the host’s target triple, verifying its `.sha256` before writing anything. `yolop-extension-logfire` 0.2.0 is the first extension published with prebuilt servers.
* The Logfire exporter emits a real trace tree: spans nest by the host’s `parent_span_id`, carry the Gen-AI semantic conventions (`gen_ai.request.model`, token counts, tool names), and give thinking its own paired span.
* Model switching serves a curated, cross-provider list the user owns, administered by `yolop models list|add|rm|move|use|reset`, with “Browse all models…” still reaching the per-provider catalog.
* An opt-in `--compact-work` mode keeps each active turn on one mutable summary row, expandable with `Ctrl+O`.
* The global config and data directories are relocatable through `--config-dir` / `--data-dir` or `YOLOP_CONFIG_DIR` / `YOLOP_DATA_DIR`, so isolating a run no longer means moving `HOME`.
* Local inference gains `metal` and `cuda` backends and a default model that runs at small-model speed (Qwen3-30B-A3B-Instruct, Q4 GGUF), both gated in CI.
## In the changelog
* [Config and data directories can be relocated](https://everruns.com/docs/changelog/2026-08-18-yolop-relocatable-config-dirs/)
* [Model switching uses a curated list you manage](https://everruns.com/docs/changelog/2026-08-20-yolop-curated-model-list/)
* [Compiled extensions install from prebuilt binaries](https://everruns.com/docs/changelog/2026-08-20-yolop-extension-prebuilt-binaries/)
Before upgrading, read the breaking changes: [Model switching uses a curated list you manage](https://everruns.com/docs/changelog/2026-08-20-yolop-curated-model-list/).
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.17.0) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0170---2026-08-21) for every pull request in this version.
---
# Run models on your machine with the local provider
> An experimental local provider runs a model inside the Yolop binary, with Metal and CUDA release builds and weights fetched by yolop weights pull.
Source:
`--provider local` runs a model inside the Yolop process: no server, no API key and no network during a turn. The default model is Qwen3-30B-A3B-Instruct in Q4 GGUF. Fetch the weights first, since a turn never waits on a download:
```bash
yolop weights pull
yolop --provider local
```
The provider is experimental. The engine ships in separate release archives, `yolop--metal.tar.gz` and `yolop-x86_64-unknown-linux-gnu-cuda.tar.gz`. The default archive, which Homebrew installs, leaves it out, so the Linux tarball dropped from 29.9 MB to 18.2 MB. From source, build with the `metal` or `cuda` feature.
See [Providers and models](https://github.com/everruns/yolop#providers-and-models), [#581](https://github.com/everruns/yolop/pull/581), [#599](https://github.com/everruns/yolop/pull/599) and [#623](https://github.com/everruns/yolop/pull/623).
---
# Archive finished chats and show them again on demand
> Archiving hides a chat thread from the Chats list without deleting it, and a Show archived filter and the archive API bring it back when needed.
Source:
Archive a chat when you are done with it. The session and transcript stay intact and reachable by URL; the thread just leaves the list.
**In the UI:** use the archive control on a row in **Chats**, and the **Show archived** filter to list archived threads. The sidebar’s **All chats** link stays visible even when no threads are listed.
**In the API:** `PUT /v1/sessions/{id}/archive` archives and `DELETE /v1/sessions/{id}/archive` restores. Both are idempotent. Session lists hide archived sessions unless you pass `include_archived=true`, which widens facet counts too.
See [Web UI](https://everruns.com/docs/features/ui/) and [#3265](https://github.com/everruns/everruns/pull/3265).
---
# Bashkit 0.17.0 release
> TLS backend and timezone data become explicit features, Bashkit runs as a WebAssembly component without JS or WASI, and host-boundary escapes are closed.
Source:
## Highlights
* **Bashkit runs as a plain wasm component, with no JS engine and no WASI.**
* **Untrusted input costs bounded work everywhere it is parsed.** Script
* **Sandbox escapes closed at the host boundary.** The host bridge no longer
* \*\*The always-on dependency graph is 21 crates smaller and the browser wasm
* \*\*Every cargo lockfile in the tree is audited, clearing 16 wasmtime
## In the changelog
* [TLS backend and timezone data become explicit features](https://everruns.com/docs/changelog/2026-08-21-bashkit-tls-tzdata-features/)
* [Runs as a WebAssembly component without JS or WASI](https://everruns.com/docs/changelog/2026-08-21-bashkit-wasm-component-no-wasi/)
Before upgrading, read the breaking changes: [TLS backend and timezone data become explicit features](https://everruns.com/docs/changelog/2026-08-21-bashkit-tls-tzdata-features/).
See the [GitHub release](https://github.com/everruns/bashkit/releases/tag/v0.17.0) and the [full release notes](https://github.com/everruns/bashkit/blob/main/CHANGELOG.md#0170---2026-08-22) for every pull request in this version.
---
# Bashkit 0.17.1 release
> The native @everruns/bashkit npm package publishes again after stalling at 0.16.0, so Node users get the 0.17 features and fixes.
Source:
## Highlights
* **The native npm package ships again.** `@everruns/bashkit` stalled at 0.16.0
See the [GitHub release](https://github.com/everruns/bashkit/releases/tag/v0.17.1) and the [full release notes](https://github.com/everruns/bashkit/blob/main/CHANGELOG.md#0171---2026-08-22) for every pull request in this version.
---
# Everruns 0.21.0 release
> Binaries are about 20% smaller, chats start with any harness, provider connections carry custom headers, and finished chats can be archived.
Source:
## Highlights
* **Chat with harnesses, archive chats, and generated run summaries**: Sessions gained interactive chats against harnesses, the ability to archive chats and toggle showing all of them, and automatically generated run summaries, with count badges on session detail tabs ([#3262](https://github.com/everruns/everruns/pull/3262), [#3265](https://github.com/everruns/everruns/pull/3265), [#3260](https://github.com/everruns/everruns/pull/3260), [#3266](https://github.com/everruns/everruns/pull/3266)).
* **Connection-level custom provider headers**: Providers can now carry custom headers at the connection level, with cache diagnostics to see how they resolve; header values are redacted in diagnostics ([#3232](https://github.com/everruns/everruns/pull/3232), [#3240](https://github.com/everruns/everruns/pull/3240)).
* **Leaner supply chain and smaller binaries**: Owning 114 small third-party contracts in-tree removed those dependencies from the build, and stripping plus CLI dispatch boxing shrank shipped binaries by \~22% ([#3245](https://github.com/everruns/everruns/pull/3245), [#3227](https://github.com/everruns/everruns/pull/3227)).
## In the changelog
* [Everruns CLI, server and worker binaries are about 20% smaller](https://everruns.com/docs/changelog/2026-08-19-smaller-release-binaries/)
* [Start a chat with any harness, not only an agent](https://everruns.com/docs/changelog/2026-08-21-chat-with-harnesses/)
* [Provider connections carry custom headers and cache diagnostics](https://everruns.com/docs/changelog/2026-08-21-provider-connection-custom-headers/)
* [Archive finished chats and show them again on demand](https://everruns.com/docs/changelog/2026-08-22-archive-chats/)
* [Session pages show a generated run summary and tab counts](https://everruns.com/docs/changelog/2026-08-22-session-run-summary-tab-counts/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.21.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0210---2026-08-22) for every pull request in this version.
---
# Session pages show a generated run summary and tab counts
> A finished run gets a one-sentence summary in the session header, and the Work, Events and Workspace tabs show how many items each holds.
Source:
When a run finishes, Everruns writes a one-sentence summary of what it did, or which step failed and why, and shows it under the session title instead of the start time. The summary is also on the session as `run_summary`. It needs a utility model (`UTILITY_*` keys); without one, the header keeps the start time. Summary spend does not count against the session budget.
The **Work**, **Events** and **Workspace** tabs now carry counts. Empty tabs show no badge, and large counts are shortened, for example `12.4K`.
See [Web UI](https://everruns.com/docs/features/ui/), [Environment variables](https://everruns.com/docs/sre/environment-variables/), [#3260](https://github.com/everruns/everruns/pull/3260) and [#3266](https://github.com/everruns/everruns/pull/3266).
---
# Agents read several workspace files in one read_many_files call
> The File System capability adds read_many_files, which reads 2 to 10 independent files in one ordered call and halves model round trips for discovery.
Source:
Agents with the File System capability get a `read_many_files` tool. It reads 2 to 10 independent session-workspace files in one call, returns them in order with a per-file result or error, and stops below a 60 KiB combined output. `read_file` is unchanged. A call’s arguments:
```json
{
"paths": [
"/workspace/README.md",
"/workspace/Cargo.toml",
"/workspace/src/lib.rs"
],
"limit": 200
}
```
Models that emit one tool call per response no longer need one model round per file. In the PR’s tests, three independent reads took 2 model calls instead of 4 and about half the input tokens. Loop detection recognizes repeated batch reads.
See [File System](https://everruns.com/docs/capabilities/file-system/) and [#3275](https://github.com/everruns/everruns/pull/3275).
---
# Global chat session endpoints removed from the Everruns API
> POST /v1/sessions/chat and POST /v1/sessions/chat/voice are gone; create a chat thread as an ordinary session with POST /v1/sessions instead.
Source:
The two endpoints that looked up a per-user Platform Chat session by tag are removed. A chat thread is now an ordinary session created with `POST /v1/sessions` and bound to an agent.
After upgrading, `POST /v1/sessions/chat` returns `405` and `POST /v1/sessions/chat/voice` returns `404`. Sessions already tagged `global-chat` stay listed and readable through the normal session routes, and the Platform Chat harness is unchanged.
```bash
# Before (removed)
curl -X POST http://localhost:9300/api/v1/sessions/chat
# After
curl -X POST http://localhost:9300/api/v1/sessions \
-H "Content-Type: application/json" \
-d '{"agent_id": "agent_..."}'
```
See [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) and [#3278](https://github.com/everruns/everruns/pull/3278).
---
# Everruns 0.22.0: Reasoning modeled as ordered artifacts
> Agents read several workspace files in one read_many_files call with bounded batches, and the global chat session endpoints are removed from the API.
Source:
## Highlights
* **Reasoning modeled as ordered artifacts**: Model reasoning is now captured as ordered artifacts with a published classification, so reasoning steps replay in the order they were produced and their kind is visible to consumers ([#3276](https://github.com/everruns/everruns/pull/3276)).
* **Bounded batch file reads**: The filesystem capability gained bounded batch reads, letting agents fetch several files in a single guarded call instead of one round-trip per file ([#3275](https://github.com/everruns/everruns/pull/3275)).
## In the changelog
* [Agents read several workspace files in one read_many_files call](https://everruns.com/docs/changelog/2026-08-24-read-many-files-tool/)
* [Global chat session endpoints removed from the Everruns API](https://everruns.com/docs/changelog/2026-08-25-chat-singleton-endpoints-removed/)
* [Messages API reports reasoning in order and the message phase](https://everruns.com/docs/changelog/2026-08-25-reasoning-ordered-message-phase/)
Before upgrading, read the breaking changes: [Global chat session endpoints removed from the Everruns API](https://everruns.com/docs/changelog/2026-08-25-chat-singleton-endpoints-removed/).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.22.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0220---2026-08-25) for every pull request in this version.
---
# Messages API reports reasoning in order and the message phase
> Session messages now carry ordered reasoning parts plus phase and phase_source, so clients can tell intermediate commentary from the final answer.
Source:
Model reasoning is now stored as ordered parts of a message instead of two flat strings. `GET /v1/sessions/{session_id}/messages` returns reasoning as ordered `reasoning` content parts, with provider replay state removed, and adds `phase` (`commentary` or `final_answer`) and `phase_source` (`provider` or `derived`).
```json
{
"id": "message_...",
"role": "agent",
"phase": "final_answer",
"phase_source": "provider",
"content": [
{
"type": "reasoning",
"provider": "anthropic",
"text": {
"kind": "plain",
"text": "Check the config first."
}
},
{
"type": "text",
"text": "The config is valid."
}
]
}
```
Replay to providers is also more accurate: Anthropic thinking blocks keep their own signatures, including `redacted_thinking`; OpenAI reasoning items keep the ids the provider issued; and the Gemini driver now requests, parses and replays thoughts.
See [Event reference](https://everruns.com/docs/event-reference/), [#3276](https://github.com/everruns/everruns/pull/3276) and [#3279](https://github.com/everruns/everruns/pull/3279).
---
# Everruns ID adds a sign-up page and branded account emails
> Creating an Everruns Cloud account now has its own sign-up page, and the verification and password-reset emails use the Everruns layout with a fallback link.
Source:
The Everruns ID login page now links to a dedicated sign-up page, so creating an [Everruns Cloud](https://app.everruns.com) account is an explicit step. Entering an unused email on the login form still creates an account.
Verification and password-reset emails now carry the Everruns logo, an action button, a visible fallback link and an expiry note, with a complete plain-text version. A login started directly at Everruns ID, rather than from the app, now finishes on app.everruns.com instead of the ID landing page.
See [Everruns Cloud quickstart](https://everruns.com/docs/getting-started/cloud/).
---
# First message of a new chat no longer drops or shows out of order
> A new chat thread now replays from its first event when it subscribes, so the opening message is delivered and appears above later replies.
Source:
In a fresh chat thread, the first message could be missed by the live stream and then render below every later message. The session SSE stream now accepts `after_sequence`: `after_sequence=0` replays the session from its first event before going live, and the UI sends it when it holds no events yet. `since_id` still handles reconnects; passing both returns `400`.
The chat composer is also locked when no explicit or default model resolves, with the model menu left enabled to fix it.
See [Stream events](https://everruns.com/docs/how-to/stream-events/), [#3296](https://github.com/everruns/everruns/pull/3296) and [#3295](https://github.com/everruns/everruns/pull/3295).
---
# Yolop 0.17.1 release
> Run models on your machine with the local provider, plus recovery from repeated tool failures, batch file reads, and a lighter default binary.
Source:
## Highlights
* Releases ship two binaries per target. `yolop-.tar.gz` is the default-features build the Homebrew formula points at, and no longer carries the local-inference engine; `yolop--metal.tar.gz` and `yolop-x86_64-unknown-linux-gnu-cuda.tar.gz` carry the engine with the backend that target can use. The Linux tarball drops from 29.9 MB to 18.2 MB. Extension servers are now built for every target too, not just the last one the matrix collapsed onto.
* The runtime recovers from failure loops instead of repeating them: consecutive equivalent invocation, missing-command, wrong-path, and usage failures on an unchanged workspace now require a different action or a bounded progress checkpoint, and a checkpoint is a real trajectory transition rather than a hook-only backstop.
* File discovery is batch-native. `read_many_files` is advertised eagerly, so a model can read independent known paths in one call even when the provider emits one tool call per turn, and `repo_map` keeps its compact parameter schema in the first-turn profile.
* Input routing is one ordered ownership table, so a paste while the sandbox-approval prompt owns the screen no longer lands in the composer behind it.
* ACP prompts continue once after the provider exhausts its stream-stall retries, replaying from the durable transcript so settled tools are not re-run.
* The everruns family moves to core 0.19 and provider 0.20, where reasoning is ordered `ContentPart::Reasoning` artifacts rather than a flat message field, and tuika to 0.11.0.
## In the changelog
* [Run models on your machine with the local provider](https://everruns.com/docs/changelog/2026-08-21-yolop-local-inference-provider/)
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.17.1) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0171---2026-08-27) for every pull request in this version.
---
# Everruns CLI plugins, skills and knowledge-bases commands work again
> The everruns plugins, skills and knowledge-bases commands called unversioned API paths and got 404 responses; they now use the /v1/ routes.
Source:
The `everruns plugins`, `everruns skills` and `everruns knowledge-bases` commands sent requests to `/plugins`, `/skills` and `/knowledge-bases`, which the server does not serve, so they failed with 404. They now call `/v1/plugins`, `/v1/skills` and `/v1/knowledge-bases` for both list and per-item requests.
Knowledge now uses the unified `everruns knowledge` command group; the undeployed `knowledge-bases` group has been retired. See [CLI: plugins](https://everruns.com/docs/reference/cli/plugins/), [CLI: skills](https://everruns.com/docs/reference/cli/skills/), [CLI: Knowledge](https://everruns.com/docs/reference/cli/knowledge/), the [Knowledge guide](https://everruns.com/docs/capabilities/knowledge/) and [#3326](https://github.com/everruns/everruns/pull/3326).
---
# Organization default model can fall back to the platform default
> An organization's default model is now an explicit override that admins can clear, falling back to a platform default shown in Models and Settings.
Source:
A new organization no longer stores a concrete default model. When none is set, the platform default applies, and the Models and Organization settings pages label that state **Platform default** with the model name.
To go back to the platform default after choosing a model, select the platform default option, or send `"default_model_id": null` in `PATCH /v1/orgs/{org}`. Omitting the field leaves the setting unchanged.
```bash
curl -X PATCH http://localhost:9300/api/v1/orgs/$ORG \
-H "Content-Type: application/json" \
-d '{"default_model_id": null}'
everruns models default get
```
See [CLI: models](https://everruns.com/docs/reference/cli/models/), [#3114](https://github.com/everruns/everruns/pull/3114) and [#3321](https://github.com/everruns/everruns/pull/3321).
---
# Only organization admins can rename an organization
> Every PATCH /v1/orgs/{org} request now requires an organization admin, closing a path where members could rename non-default organizations.
Source:
Organization updates now check for an admin on every request. Before, a name-only `PATCH /v1/orgs/{org}`, the request the settings page autosave sends, skipped the admin check, so a non-admin member could rename a non-default organization. Such a request now returns `403` and nothing is saved.
Admins see no change. See [#3315](https://github.com/everruns/everruns/pull/3315).
---
# Tool calls are validated against their schema before running
> Everruns checks every tool call's arguments against the tool's JSON Schema and enables strict schemas for compatible OpenAI Chat and Responses tools.
Source:
Malformed tool calls no longer reach tool implementations. Before a tool runs, its arguments are validated against the tool’s registered JSON Schema. Missing, wrong-type or unknown arguments return a structured error the model sees, so it can correct the call on the next turn. Tool call repair still runs first. A `read_file` call with a numeric `path` gets:
```json
{
"code": "invalid_tool_arguments",
"tool": "read_file",
"issues": [
{
"instance_path": "/path",
"message": "42 is not of type \"string\"",
"schema_path": "/properties/path/type"
}
]
}
```
For OpenAI Chat Completions and Open Responses, compatible tools are now sent with strict schemas. A schema that cannot be converted safely is sent non-strict, as before.
See [Tool Call Repair](https://everruns.com/docs/capabilities/tool-call-repair/) and [#3336](https://github.com/everruns/everruns/pull/3336).
---
# Yolop 0.17.2 release
> Yolop 0.17.2 is a maintenance release with dependency updates and internal fixes and no user-facing change. Every pull request is in the release notes.
Source:
Yolop 0.17.2 is a maintenance release with fixes and internal changes and no user-facing entry of its own.
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.17.2) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0172---2026-08-31) for every pull request in this version.
---
# OpenTelemetry traces follow Gen-AI and OpenInference conventions
> Agent turn traces now carry the OpenTelemetry Gen-AI agent attributes and OpenInference attributes on the same spans, selectable per deployment.
Source:
OpenTelemetry traces of agent turns now follow the current Gen-AI agent and inference conventions and carry OpenInference attributes on the same spans. The turn root carries the agent identity, chat spans have real durations, and thinking is nested under its model call.
Choose the vocabularies with `EVERRUNS_TRACE_CONVENTIONS` (default `gen_ai,openinference`):
```bash
# OpenInference only, for example for Arize Phoenix
export EVERRUNS_TRACE_CONVENTIONS=openinference
```
See [OpenTelemetry](https://everruns.com/docs/observability/opentelemetry/) and [Framework observability](https://everruns.com/docs/framework/observability/). [#3353](https://github.com/everruns/everruns/pull/3353)
## Update 2026-10-14
[OpenTelemetry export and accounting fixes](https://everruns.com/docs/changelog/2026-10-14-opentelemetry-export-and-accounting/) correct cached-token totals, exporter batching, tool identity, error privacy and shutdown reporting.
---
# MCP URL elicitation pauses for consent in the Everruns UI
> When an MCP tool asks a person to finish a step in the browser, the turn now pauses on a consent card and the secret never enters the model context.
Source:
An MCP server can now ask a person to finish something in their browser, such as authorizing a charge, pasting an API key or clicking a consent screen, and Everruns handles it as both MCP client and MCP server. The value never passes through the MCP client, the model’s context or the event log.
**In the UI:** a tool call that stops on a URL elicitation pauses the turn on a confirmation card. The consent is single use and bound to the server, tool and domain you were shown.
**For MCP clients of Everruns:** the `/mcp` endpoint gains `session_set_secret` and `connect`, which point you at a form Everruns serves instead of taking the value.
See [MCP URL elicitation](https://everruns.com/docs/features/mcp-url-elicitation/) and [Complete a URL elicitation](https://everruns.com/docs/how-to/complete-a-url-elicitation/). [#3354](https://github.com/everruns/everruns/pull/3354)
---
# Turn activity compacts into one summary row by default
> Fullscreen Yolop turns now collapse narration and tool output into one summary row that Ctrl+O expands; pass --no-compact-work to keep the full transcript.
Source:
In the fullscreen TUI, each running turn now shows one summary row instead of appending every narration line and tool call to the transcript. Press `Ctrl+O` to expand or collapse the details of the current or latest turn. Completed, failed and cancelled turns get distinct markers, and the final answer stays a separate entry below the summary.
To keep the expanded transcript, start Yolop with:
```bash
yolop --no-compact-work
```
The mode was opt-in with `--compact-work` in 0.17.0 and became the default in 0.17.3.
See [CLI essentials](https://github.com/everruns/yolop#cli-essentials), [#612](https://github.com/everruns/yolop/pull/612) and [#658](https://github.com/everruns/yolop/pull/658).
---
# GPT-6 Astra, Claude Fable 5.1 and Muse Spark 1.3 model profiles
> Everruns adds model profiles for OpenAI GPT-6 Astra, Claude Fable 5.1 and Muse Spark 1.3, seeds them for new organizations and covers them in live tests.
Source:
Three new models are available with full profiles (pricing, limits, modalities):
* **OpenAI GPT-6 Astra** (`gpt-6-astra`), seeded as an enabled favorite. Its reasoning effort accepts a new `max` tier above `xhigh`.
* **Claude Fable 5.1** (`claude-fable-5-1` and its 1M-context variant), seeded as enabled favorites with adaptive thinking.
* **Muse Spark 1.3** and its Contributor tier on the Meta Model API. Muse Spark 1.2 stays selectable.
New organizations get them on the Models page automatically. List the favorites from the CLI:
```bash
everruns models list --favorites-only true
```
See [Providers](https://everruns.com/docs/providers/), [Meta](https://everruns.com/docs/providers/meta/) and [Anthropic](https://everruns.com/docs/providers/anthropic/). [#3363](https://github.com/everruns/everruns/pull/3363), [#3351](https://github.com/everruns/everruns/pull/3351), [#3352](https://github.com/everruns/everruns/pull/3352)
---
# Framework agents start about 3.5 times faster
> Building an agent and creating a session in the Everruns Framework now takes roughly a third of the time, with no change to steady-state behavior.
Source:
Agent startup to ready in the Everruns Framework is about 3.5 times faster. In release builds, `main` to ready dropped from 1.9-2.2 ms to 0.50-0.64 ms and `Agent::build` from 1.0-1.2 ms to 0.14-0.26 ms.
Two costs left the startup path: provider HTTP clients are now resolved per request instead of in each driver constructor, and a session’s event broadcast channel is allocated when the first subscriber arrives rather than with the session. A subscriber still never receives events sent before it subscribed. No code changes are needed: the same engine and session calls start faster.
```rust
let engine = Engine::new();
let session = engine
.create(agent)
.harness(Harness::conversation())
.start()
.await?;
let response = session.send_and_wait("Tell me a joke.").await?;
```
See [Agents](https://everruns.com/docs/framework/agents/). [#3486](https://github.com/everruns/everruns/pull/3486)
---
# Everruns 0.24.0: ~3.5x faster agent startup
> The first message of a new chat no longer drops or shows out of order, and the CLI plugins, skills and knowledge-bases commands work again.
Source:

## Highlights
* **\~3.5x faster agent startup**: Agent startup-to-ready latency cut by roughly 3.5x ([#3486](https://github.com/everruns/everruns/pull/3486)).
* **MCP URL-mode elicitation with a consent pause**: MCP elicitation now works in URL mode on both sides, with an explicit consent pause surfaced in the UI ([#3354](https://github.com/everruns/everruns/pull/3354)).
* **Gen-AI and OpenInference trace conventions**: Observability now emits Gen-AI agent and OpenInference trace conventions for standard agent tracing ([#3353](https://github.com/everruns/everruns/pull/3353)).
* **Expanded model catalog**: Added OpenAI GPT-6 Astra, Claude Fable 5.1, and Muse Spark 1.3 profiles with live-matrix coverage ([#3363](https://github.com/everruns/everruns/pull/3363), [#3351](https://github.com/everruns/everruns/pull/3351), [#3352](https://github.com/everruns/everruns/pull/3352)).
* **Broad provider wire-contract hardening**: A large sweep of driver and provider fixes plus expanded contract tests across Anthropic, OpenAI, Gemini, OpenRouter, Bedrock, MAI, Meta, and Fireworks, tightening streaming, usage, reasoning, and request-shaping behavior.
## In the changelog
* [First message of a new chat no longer drops or shows out of order](https://everruns.com/docs/changelog/2026-08-27-first-chat-message-fix/)
* [Everruns CLI plugins, skills and knowledge-bases commands work again](https://everruns.com/docs/changelog/2026-08-31-cli-plugins-skills-paths-fix/)
* [Organization default model can fall back to the platform default](https://everruns.com/docs/changelog/2026-08-31-org-default-model-platform-fallback/)
* [Only organization admins can rename an organization](https://everruns.com/docs/changelog/2026-08-31-org-updates-require-admin/)
* [Tool calls are validated against their schema before running](https://everruns.com/docs/changelog/2026-08-31-tool-argument-validation-strict/)
* [OpenTelemetry traces follow Gen-AI and OpenInference conventions](https://everruns.com/docs/changelog/2026-09-03-genai-openinference-trace-conventions/)
* [MCP URL elicitation pauses for consent in the Everruns UI](https://everruns.com/docs/changelog/2026-09-03-mcp-url-elicitation-consent/)
* [GPT-6 Astra, Claude Fable 5.1 and Muse Spark 1.3 model profiles](https://everruns.com/docs/changelog/2026-09-04-gpt-6-astra-fable-muse-spark/)
* [Framework agents start about 3.5 times faster](https://everruns.com/docs/changelog/2026-09-08-faster-framework-agent-startup/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.24.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0240---2026-09-09) for every pull request in this version.
---
# AGENTS.md files resolve hierarchically as conversation context
> Agent instructions now load AGENTS.md from the workspace root down to the working directory and place them below harness safety instructions.
Source:
`AGENTS.md` content no longer goes into the system prompt. The agent instructions capability resolves the configured files hierarchically, from the filesystem root to the working directory, with deeper files winning and sibling directories out of scope. The result is inserted as the leading user-role message of each turn, behind a header that marks it as workspace content.
This keeps untrusted repository instructions below harness safety instructions and keeps the system prompt stable for caching. Resolution is bounded to 16 files, 32 KiB each and 128 KiB in total. To resolve another file at every level, set `files` on the capability:
```json
{
"files": [
"AGENTS.md",
"CLAUDE.md"
]
}
```
See [Agent instructions](https://everruns.com/docs/capabilities/agent-instructions/) and [Use AGENTS.md](https://everruns.com/docs/how-to/use-agents-md/). [#3501](https://github.com/everruns/everruns/pull/3501)
---
# Bashkit 0.18.0 release
> Python, Node and C ABI callers can mount host directories read-only, with security hardening and C ABI improvements across the bindings.
Source:
## Highlights
* **Security hardening.** The Rust update takes `russh` 0.63.1 security fixes
* **C ABI improvements.** The C ABI exposes host-directory mounts, bringing native
## In the changelog
* [Read-only host directory mounts, also over the C ABI](https://everruns.com/docs/changelog/2026-09-10-bashkit-read-only-host-mounts/)
See the [GitHub release](https://github.com/everruns/bashkit/releases/tag/v0.18.0) and the [full release notes](https://github.com/everruns/bashkit/blob/main/CHANGELOG.md#0180---2026-09-10) for every pull request in this version.
---
# Read-only host directory mounts, also over the C ABI
> Bashkit's Python and Node bindings can mount a host directory read-only, and the C API gains allowlisted host-directory mounts on a running session.
Source:
**Read-only mounts.** Python `Bash.mount()` and `BashTool.mount()` take `read_only=True`, and Node `mount()` takes `readOnly`. Reads work; writes, `cp`, `mv`, `mkdir`, `rm` and `chmod` inside the mounted tree fail, and `reset()` keeps the protection.
```python
bash.mount("/corpus", corpus, read_only=True)
```
**C ABI mounts.** The C API can mount real host directories, through `mounts` in the config or `bashkit_mount` and `bashkit_unmount` on a running session. Each root must resolve under an `allowed_mount_paths` prefix and pass the sensitive-path denylist. With no allowlist, every mount is rejected.
See [Live mounts](https://github.com/everruns/bashkit/blob/main/crates/bashkit/docs/live_mounts.md), [C API](https://github.com/everruns/bashkit/blob/main/docs/c-api.md), [#2393](https://github.com/everruns/bashkit/pull/2393) and [#2371](https://github.com/everruns/bashkit/pull/2371).
---
# Yolop 0.17.3 release
> Turn activity compacts into one summary row, yolop mcp covers show and disable, cargo install works again, and web_fetch reaches public URLs.
Source:
## Highlights
* Fullscreen turns compact their activity by default. `--no-compact-work` keeps the expanded narration and tool rows for anyone who prefers the old projection.
* `yolop mcp` is a complete configuration surface: `mcp show ` inspects global, workspace, or effective scope, and `disable` is its own command instead of `enable --disable`.
* Fresh `cargo install yolop` works again. Every `everruns-*` requirement is pinned exactly, so a from-scratch resolve picks the set that was actually built and reviewed, and that set is now the everruns 0.20 facade release.
* Reasoning effort resolves from every source that knows about it, curated registry, then provider-advertised parameters, then the reasoning-required families, so a model the registry has never seen keeps its `/effort` control and repairs a mandated-reasoning rejection once on its own.
* The runtime stops doing work twice: a background task the foreground turn already reported no longer wakes a duplicate turn, and the environment context reports the shell’s real network access instead of a stale no-network claim.
* `web_fetch` reaches public URLs again, and MCP restart/reconnect/reload land on the typed `mcp reload` command.
## In the changelog
* [Turn activity compacts into one summary row by default](https://everruns.com/docs/changelog/2026-09-03-yolop-compact-work-default/)
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.17.3) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0173---2026-09-10) for every pull request in this version.
---
# Manage and log in to MCP servers from the CLI
> The yolop mcp command now covers show, enable, disable and an OAuth login for remote MCP servers, so MCP setup no longer needs the TUI or config files.
Source:
`yolop mcp` now covers MCP server configuration from a terminal: `list`, `show`, `add`, `remove`, `enable`, `disable` and `login`. `show ` prints the global, workspace or effective entry, and `disable` is its own command instead of `enable --disable`.
`yolop mcp login ` runs the OAuth flow for a remote server: it prints the authorize URL, opens the browser, waits for the callback and saves the tokens. Before, that flow existed only as `/mcp login` inside the TUI.
```bash
yolop mcp login linear
```
See [MCP servers](https://github.com/everruns/yolop#mcp-servers), [#661](https://github.com/everruns/yolop/pull/661) and [#677](https://github.com/everruns/yolop/pull/677).
---
# Everruns servers publish an MCP server card and auth.md
> Every Everruns deployment now serves an MCP Server Card and an auth.md document, derived from live config, so agents can discover how to connect.
Source:
An Everruns server now describes itself to agents. `/.well-known/mcp/server-card.json` is an MCP Server Card (SEP-1649), and `/auth.md` explains both credential paths: OAuth for the MCP endpoint and personal access tokens for the REST API. Both are generated from the running server’s configuration, so a self-hosted deployment describes itself correctly. Under `AUTH_MODE=none` they say no credentials are required.
```bash
curl -fsS https://your-host/auth.md | head -1
curl -fsS https://your-host/.well-known/mcp/server-card.json
```
The descriptions of `create_agent`, `create_session` and `create_message` also state their argument constraints, so an agent gets them right on the first call.
See [Authentication](https://everruns.com/docs/sre/runbooks/authentication/). [#3533](https://github.com/everruns/everruns/pull/3533), [#3530](https://github.com/everruns/everruns/pull/3530)
---
# Everruns 0.25.0: Hierarchical AGENTS.md as conversation context
> AGENTS.md files resolve hierarchically as conversation context, and every Everruns user starts in a pinned Platform Chat thread with more built-in tools.
Source:

## Highlights
* **Hierarchical AGENTS.md as conversation context**: Agent instructions now resolve `AGENTS.md` files hierarchically and are injected as the leading user-role message of each turn, keeping untrusted workspace instructions below harness safety instructions in the instruction hierarchy ([#3501](https://github.com/everruns/everruns/pull/3501)).
* **Platform Chat for every user**: Each user gets a precreated, pinned Platform Chat thread, and the Platform Chat capability set is widened so it can drive more of the control plane ([#3508](https://github.com/everruns/everruns/pull/3508), [#3507](https://github.com/everruns/everruns/pull/3507)).
* **Capability deprecation lifecycle**: Capabilities can now be marked `Deprecated` (still functional, surfaces warn) or `Retired` (inert and hidden), and the `platform_management` capability was retired via the new lifecycle ([#3513](https://github.com/everruns/everruns/pull/3513)).
* **Unified `everruns` command tree for MCP and CLI**: A noun-verb command tree is now exposed over MCP, and a host can name the root token of its own command tree from the CLI ([#3490](https://github.com/everruns/everruns/pull/3490), [#3496](https://github.com/everruns/everruns/pull/3496)).
* **Safer provider onboarding and model management**: Provider API keys are validated before being stored, a configured key reliably produces a usable org, discovered models are adopted instead of conflicting on explicit create, and disabled models can be re-enabled ([#3516](https://github.com/everruns/everruns/pull/3516), [#3515](https://github.com/everruns/everruns/pull/3515), [#3517](https://github.com/everruns/everruns/pull/3517), [#3512](https://github.com/everruns/everruns/pull/3512)).
## In the changelog
* [AGENTS.md files resolve hierarchically as conversation context](https://everruns.com/docs/changelog/2026-09-10-agents-md-hierarchical-context/)
* [Every Everruns user starts in a pinned Platform Chat thread](https://everruns.com/docs/changelog/2026-09-11-platform-chat-thread-every-user/)
* [platform_management capability retired in favor of platform](https://everruns.com/docs/changelog/2026-09-11-retire-platform-management-capability/)
Before upgrading, read the breaking changes: [platform_management capability retired in favor of platform](https://everruns.com/docs/changelog/2026-09-11-retire-platform-management-capability/).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.25.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0250---2026-09-11) for every pull request in this version.
---
# Connect an LLM provider with one pasted key, checked before saving
> LLM provider setup now offers a one-click grid of common providers, verifies the API key with the provider, and enables models and a default for you.
Source:
**In the UI:** a **Connect** grid offers one tile each for Anthropic, OpenAI, Gemini, OpenRouter and Meta, so connecting one takes a pasted key. Providers that need more than a key stay behind **Another provider**. Keys are checked as you type, and only an outright rejection is shown. Once a key is saved, Everruns discovers the provider’s models, enables a shortlist, stars a few favorites and sets a default model if none exists.
**In the API and CLI:** `POST /v1/providers/check-credentials` checks a key without storing it:
```bash
everruns providers check-credentials check --provider-type openai --api-key "$OPENAI_API_KEY"
```
Disabled models can be re-enabled. See [Providers](https://everruns.com/docs/providers/) and [CLI providers](https://everruns.com/docs/reference/cli/providers/). [#3526](https://github.com/everruns/everruns/pull/3526), [#3516](https://github.com/everruns/everruns/pull/3516), [#3515](https://github.com/everruns/everruns/pull/3515)
---
# Image generation adds the Muse image model with a fallback
> The image generation capability can now use Meta's Muse image model through Meta or OpenRouter, and falls back to it when only those credentials exist.
Source:
The image generation capability supports `muse-image-1.0`, served through a Meta provider or through OpenRouter. A new `fallback` option controls what happens when a GPT image model is configured but only Meta or OpenRouter credentials exist: `auto` (the default) uses Muse, `off` keeps GPT strictly on OpenAI or Azure OpenAI.
```json
{
"config": {
"fallback": "auto"
}
}
```
Credentials now come only from configured providers. The capability no longer reads `OPENAI_API_KEY` or `OPENAI_BASE_URL` from session secrets.
See [OpenAI image generation](https://everruns.com/docs/capabilities/openai-image-generation/). [#3523](https://github.com/everruns/everruns/pull/3523)
---
# Every Everruns user starts in a pinned Platform Chat thread
> New users now land in a precreated, pinned Platform Chat thread instead of an empty Chats list, and Platform Chat gains more built-in capabilities.
Source:
A new user used to land on **Chats** with an empty list and a counterpart picker. The app now creates a thread on the built-in Platform Chat harness on first entry, pins it, and ends onboarding in it. An existing thread is reused and an archived one is not recreated.
Platform Chat also runs on a wider capability set, adding current time, message timestamps, a to-do list, prompt caching, tool call repair and parallel tool calls. Existing organizations pick these up without a migration.
See [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/). [#3508](https://github.com/everruns/everruns/pull/3508), [#3507](https://github.com/everruns/everruns/pull/3507)
---
# platform_management capability retired in favor of platform
> Capabilities gain a deprecated and retired lifecycle, and platform_management is the first one retired: it stays registered but no longer contributes tools.
Source:
Built-in capabilities now have a two-step removal lifecycle. A `deprecated` capability keeps working while catalogs warn. A `retired` one stays registered so agents that reference it still start, but it contributes no tools, prompt, mounts or hooks, and is hidden from catalogs unless `include_retired` is set.
`platform_management` is retired. Its 14 hand-written tools are replaced by the `platform` capability, which derives the same surface from the command catalog. Agents that still list `platform_management` should switch to `platform`:
```toml
# Before
capabilities = ["platform_management"]
# After
capabilities = ["platform"]
```
See [Platform Management (removed)](https://everruns.com/docs/capabilities/platform-management/) and [Platform](https://everruns.com/docs/capabilities/platform/). [#3513](https://github.com/everruns/everruns/pull/3513)
---
# Session schedules no longer fire twice on multi-replica deployments
> Everruns servers running more than one replica now claim each due session schedule atomically, so a scheduled message is sent once.
Source:
On a deployment with more than one server replica, two replicas could both pick up the same due session schedule and send its message twice. Schedules are now claimed atomically, so exactly one replica fires each one. Single-replica deployments were not affected, and no configuration change is needed.
See [Session schedules](https://everruns.com/docs/capabilities/session-schedules/). [#3527](https://github.com/everruns/everruns/pull/3527)
---
# Yolop 0.18.0 release
> Manage and log in to MCP servers from the CLI, and session search and self-configuration move to CLI commands, with fixes listed in the release notes.
Source:
## Highlights
* `yolop sessions` CLI replaces the `search_sessions` model tool, with `search` and `list` subcommands.
* Capability names unify behind CLI routes: `yolop` prefix stripped to bare IDs, MCP/provider/model self-configuration moves through CLI control routes.
* First-class env-context entries with `--env-context` flag and Paseo detection.
* OpenRouter attestation-gate guidance, account gates surfaced as links, plus `yolop mcp login` for remote OAuth servers.
* Root-managed cancellation with automatic conflict choice, and upstream v0.25.0 cone batch adoption.
## In the changelog
* [Manage and log in to MCP servers from the CLI](https://everruns.com/docs/changelog/2026-09-10-yolop-mcp-cli-login/)
* [Session search and self-configuration move to CLI commands](https://everruns.com/docs/changelog/2026-09-11-yolop-sessions-cli-capability-ids/)
Before upgrading, read the breaking changes: [Session search and self-configuration move to CLI commands](https://everruns.com/docs/changelog/2026-09-11-yolop-sessions-cli-capability-ids/).
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.18.0) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0180---2026-09-11) for every pull request in this version.
---
# Session search and self-configuration move to CLI commands
> Yolop 0.18.0 replaces search_sessions and the configuration tools with yolop sessions, mcp and connectors commands and drops the yolop_ ID prefix.
Source:
Yolop 0.18.0 removes several model tools and routes the same work through CLI commands that the agent runs in its shell:
* `search_sessions` is replaced by `yolop sessions search` and `yolop sessions list`. The `session_history` capability ID is now `sessions`.
* `list_mcp_servers`, `set_reasoning_effort`, `search_models`, `set_model`, `set_provider` and the connector tools are removed. Use `yolop mcp`, the provider and model commands, and the new `yolop connectors list|get|connect|disconnect`.
* Capability IDs drop the `yolop_` prefix: `yolop_approval` is now `approval`.
**Upgrade note:** settings overrides that use the old capability IDs fail validation. Rename them to the bare IDs:
```toml
# Before, in settings.toml
[[capabilities]]
ref = "session_history"
# After
[[capabilities]]
ref = "sessions"
```
See [CLI essentials](https://github.com/everruns/yolop#cli-essentials), [#691](https://github.com/everruns/yolop/pull/691) and [#693](https://github.com/everruns/yolop/pull/693).
---
# Attach PDF files to agent messages
> Messages can now carry PDF attachments, uploaded through a new files API and passed to each provider in its native document format.
Source:
Agents can read PDF documents sent with a message.
**In the UI:** the chat composer offers PDF attachments when the selected model accepts PDF input, and shows each attachment as a file chip.
**In the API:** upload a PDF with `POST /v1/files` (PDF only, up to 32 MB), then reference it as a `file` content part in the message. `GET /v1/files`, `GET /v1/files/{id}` and `DELETE` manage stored files. Everruns converts the attachment for each provider, including Anthropic documents, Gemini inline data, Bedrock documents and OpenAI file inputs.
See [UI](https://everruns.com/docs/features/ui/). [#3535](https://github.com/everruns/everruns/pull/3535)
---
# Send a message and wait for the turn result in one request
> The sessions API can now hold a message request open until the turn finishes, and Framework runs accept a timeout, for callers that do not consume SSE.
Source:
Callers that want a result instead of an event stream can now wait for it.
**In the API:** `POST /v1/sessions/{id}/messages?wait=true` returns `200` with the turn result when the turn completes or fails, or `202` with partial messages when `timeout_ms` (default 120 s, at most 600 s) expires. Without `wait` it still returns `201` immediately, so existing clients are unaffected.
```bash
curl -X POST "http://localhost:9300/api/v1/sessions/$SESSION_ID/messages?wait=true&timeout_ms=60000" \
-H "Authorization: Bearer $EVERRUNS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": {"role": "user", "content": [{"type": "text", "text": "Hello!"}]}}'
```
**In code:** `RunOptions::timeout` bounds `Session::run_with`; when it expires the in-process turn is cancelled and its outcome returned.
See [Sessions](https://everruns.com/docs/framework/sessions/) and [Stream events](https://everruns.com/docs/how-to/stream-events/). [#3541](https://github.com/everruns/everruns/pull/3541)
---
# Yolop 0.18.1 release
> Named profiles can be managed from the CLI and the control plane, and configured MCP servers are listed in the agent system prompt.
Source:
## Highlights
* Named profiles can now be managed from the CLI and control plane (#698)
* Configured MCP servers are listed in the agent system prompt (#700)
* OpenRouter 402 billing pauses now guide users to wait or add credits, and validation errors are actionable (#701, #705)
* Compaction checkpoints stamp rows and trace Codex compaction (#703)
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.18.1) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0181---2026-09-12) for every pull request in this version.
---
# Everruns 0.26.0: PDF input with file attachments
> Servers publish an MCP server card and auth.md for agent discovery, and an LLM provider connects with one pasted key that is checked before saving.
Source:

## Highlights
* **PDF input with file attachments**: Agents can now accept PDF documents as file attachments alongside text input ([#3535](https://github.com/everruns/everruns/pull/3535)).
* **Muse image model**: A new Muse image-generation model with a configurable fallback ([#3523](https://github.com/everruns/everruns/pull/3523)).
* **Quick-connect provider grid**: Connecting an LLM provider is now a one-click grid in the UI ([#3526](https://github.com/everruns/everruns/pull/3526)).
* **Native async tool execution on durable workers**: Long-running tool calls execute natively on durable workers instead of blocking a turn ([#3466](https://github.com/everruns/everruns/pull/3466)).
* **Synchronous wait-for-completion**: A non-streaming wait-for-completion path spanning drivers, the sessions API, and the framework ([#3541](https://github.com/everruns/everruns/pull/3541)).
* **Agent discovery documents**: The server serves agent discovery documents, and create_agent/session/message descriptions surface their argument constraints ([#3533](https://github.com/everruns/everruns/pull/3533), [#3530](https://github.com/everruns/everruns/pull/3530)).
## In the changelog
* [Everruns servers publish an MCP server card and auth.md](https://everruns.com/docs/changelog/2026-09-11-agent-discovery-documents/)
* [Connect an LLM provider with one pasted key, checked before saving](https://everruns.com/docs/changelog/2026-09-11-llm-provider-quick-connect/)
* [Image generation adds the Muse image model with a fallback](https://everruns.com/docs/changelog/2026-09-11-muse-image-model-fallback/)
* [Session schedules no longer fire twice on multi-replica deployments](https://everruns.com/docs/changelog/2026-09-11-session-schedules-no-double-fire/)
* [Attach PDF files to agent messages](https://everruns.com/docs/changelog/2026-09-12-pdf-file-attachments/)
* [Send a message and wait for the turn result in one request](https://everruns.com/docs/changelog/2026-09-12-wait-for-message-completion/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.26.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0260---2026-09-13) for every pull request in this version.
---
# Everruns Cloud loads Sessions, Harnesses and Skills pages faster
> Moving between the Sessions, Harnesses and Skills pages on Everruns Cloud no longer stalls for seconds while the server renders the page.
Source:
Opening **Sessions**, **Harnesses** or **Skills** in [Everruns Cloud](https://app.everruns.com) could take several seconds, with `/sessions` observed at about 6 seconds, even though the underlying API calls answered in under 100 ms. Server rendering of these pages now fetches its data directly instead of going back out through the public edge, and hovering a sidebar link no longer starts that work before you click.
See [Everruns Cloud quickstart](https://everruns.com/docs/getting-started/cloud/).
---
# Sessions show where their commands run and what they may touch
> Each session now declares its execution environment, readable through the API and shown in an Environment panel on the session Workspace tab.
Source:
A session now has an explicit environment: where its commands run (compute) and what they may touch (containment).
**In the UI:** the session **Workspace** tab gains an **Environment** panel.
**In the API:** `GET /v1/sessions/{id}/environment` and `GET /v1/environment-targets` are read-only views of the same data. The feature is enabled wherever sandboxes are.
**In code:** the Framework’s `Environment` gains named compute and containment members, with `HostCompute` behind the `host-compute` feature.
See [Workspaces and environments](https://everruns.com/docs/framework/workspaces-and-environments/). [#3599](https://github.com/everruns/everruns/pull/3599)
---
# Yolop 0.18.2 release
> Setup checks provider auth before finishing model selection, and the CLI attaches through shell descendants so managed sessions stay reachable.
Source:
## Highlights
* Setup validates provider auth before completing model selection (#709)
* CLI attaches through shell descendants so managed sessions stay reachable (#711)
* Muse xhigh is accepted on cold startup (#712)
* README rewrite and control-plane docs keep the public surface accurate (#707, #715)
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.18.2) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0182---2026-09-14) for every pull request in this version.
---
# Everruns Cloud adds a built-in model provider with starter credit
> New Everruns Cloud organizations run agents without provider keys, paid from prepaid credit that starts at $5 and is topped up in Settings > Billing.
Source:
New organizations on [Everruns Cloud](https://app.everruns.com) come with a built-in model provider, so an agent can run before you have any provider API keys. Its models included GPT 5.6 Sol, Claude Sonnet 4.6, Muse Spark 1.3 and Muse Spark 1.3 Contributor. A user’s first organization receives $5 of starter credit, once per account and only with a verified email.
**Settings** > **Billing** shows available credit, the starter and purchased split, and what running work holds. Owners and admins buy more credit there; every member can see the balance. At zero balance the built-in provider refuses new calls, while models on your own provider keys keep working.
See [Everruns Cloud quickstart](https://everruns.com/docs/getting-started/cloud/).
---
# Everruns 0.27.0: Slack agent surface
> Sessions show where their commands run and what they may touch, and Everruns agents run in Slack's agent pane with session environments.
Source:

## Highlights
* **Slack agent surface**: Agents now run natively in Slack. The agent surface is enabled end to end: replies stream token-by-token into the pane, a stop button cancels the running turn, agent status and the thread title show while a turn runs, thread context is persisted and fed to the agent, and replies post as markdown blocks with correlation metadata ([#3560](https://github.com/everruns/everruns/pull/3560), [#3564](https://github.com/everruns/everruns/pull/3564), [#3568](https://github.com/everruns/everruns/pull/3568), [#3581](https://github.com/everruns/everruns/pull/3581), [#3567](https://github.com/everruns/everruns/pull/3567), [#3563](https://github.com/everruns/everruns/pull/3563)).
* **Session environments**: A session now declares where its commands run and what they may touch, giving agents an explicit execution environment ([#3599](https://github.com/everruns/everruns/pull/3599)).
* **Endpoint-ID ingress routing**: Ingress routes mount on endpoint IDs, with reserved routing tag prefixes to keep session routing unambiguous ([#3593](https://github.com/everruns/everruns/pull/3593), [#3589](https://github.com/everruns/everruns/pull/3589)).
* **Agent-owned exposure replaces Apps**: The App abstraction is retired from the product surface in favor of agent-owned exposure ([#3590](https://github.com/everruns/everruns/pull/3590), [#3585](https://github.com/everruns/everruns/pull/3585)).
## In the changelog
* [Sessions show where their commands run and what they may touch](https://everruns.com/docs/changelog/2026-09-14-session-environment-panel/)
* [Everruns agents run in Slack’s agent pane](https://everruns.com/docs/changelog/2026-09-15-slack-agent-pane/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.27.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0270---2026-09-15) for every pull request in this version.
---
# Everruns agents run in Slack's agent pane
> A Slack channel can now serve Slack's agent pane, with streamed replies, a stop button, live status and thread context, alongside channel replies.
Source:

Set `agent_surface_enabled` on a Slack channel and its app also appears as an agent in Slack’s dedicated assistant pane, while `@mentions` in channels keep working. In the pane, replies stream as the agent writes them, the stop button cancels the running turn, and the agent’s status and thread title show while a turn runs. Thread context is persisted and given to the agent, and replies post as markdown blocks.
The generated manifest declares the agent feature and fills Slack’s suggested prompts from the agent’s conversation starters, or the harness’s. An existing app must update its manifest and reinstall to gain the pane.
See [Slack](https://everruns.com/docs/capabilities/slack/#agent-surface) and [Publish to Slack](https://everruns.com/docs/how-to/publish-to-slack/). [#3560](https://github.com/everruns/everruns/pull/3560), [#3564](https://github.com/everruns/everruns/pull/3564), [#3606](https://github.com/everruns/everruns/pull/3606)
---
# Everruns 0.28.0: Agent exposure management
> The Everruns UI gains dark mode with a theme setting, plus agent exposure management and sessions and budgets attributed to the endpoint that started them.
Source:

## Highlights
* **Agent exposure management**: An agent’s endpoints and triggers now have one home, and a new cross-agent Exposures view lists exposure across all agents. Publish moves to the endpoint, and an agent’s exposure can be suspended ([#3624](https://github.com/everruns/everruns/pull/3624), [#3627](https://github.com/everruns/everruns/pull/3627), [#3621](https://github.com/everruns/everruns/pull/3621)).
* **Endpoint-attributed sessions and budgets**: Sessions and budgets now attribute to the endpoint they arrive through, and Slack and FCP endpoint budgets bind and enforce their limits. Previously configured endpoint budgets that were inert will begin enforcing ([#3623](https://github.com/everruns/everruns/pull/3623), [#3625](https://github.com/everruns/everruns/pull/3625)).
* **Channels re-parent from App to Agent**: Channels move from the retired App abstraction to the agent, with grandfathered apps synthesized into agents automatically ([#3618](https://github.com/everruns/everruns/pull/3618), [#3598](https://github.com/everruns/everruns/pull/3598)).
## In the changelog
* [The Everruns UI supports dark mode with a theme setting](https://everruns.com/docs/changelog/2026-09-16-ui-dark-mode-theme/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.28.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0280---2026-09-16) for every pull request in this version.
---
# Framework direct model calls with Model::complete
> The Everruns Framework adds a promoted path for one prompt and one answer, and each driver now reads its vendor's standard credential environment variables.
Source:
Work that is one prompt and one answer no longer needs an agent. `Model::complete` covers the one-line case, and `Model::completion` adds system messages, replayed context, per-call controls, streaming and a typed `CompletionError`.
```rust
let model = Model::new("gpt-6.1-sol", OpenAI::from_env()?);
let answer = model.complete("Name the three primary colors.").await?;
```
Each driver now declares its own credential variables, following the vendor’s SDK, for example `GEMINI_API_KEY` or `GOOGLE_API_KEY`, the AWS variables for Bedrock, and `LLAMA_API_KEY` for Meta. Provider model catalogs are also exposed on the facade crate.
See [Direct model calls](https://everruns.com/docs/framework/direct-model-calls/) and [Models and providers](https://everruns.com/docs/framework/models-and-providers/). [#3642](https://github.com/everruns/everruns/pull/3642), [#3668](https://github.com/everruns/everruns/pull/3668)
---
# The Everruns UI supports dark mode with a theme setting
> The Everruns web UI can now be shown in light, dark or system theme, chosen from the user menu and remembered without a flash on navigation.
Source:
The Everruns UI has a dark theme. Open the user menu in the sidebar and pick **Theme**: light, dark or system. The choice is stored in a cookie, so pages render in the right theme on the server and navigation does not flash. Scrollbars and native controls follow the theme too.
Before this change, a machine set to dark mode got a partly dark interface; the whole UI now switches together, with semantic status colors that work in both themes.
See [UI](https://everruns.com/docs/features/ui/). [#3637](https://github.com/everruns/everruns/pull/3637), [#3639](https://github.com/everruns/everruns/pull/3639)
---
# Bashkit 0.18.1 release
> ScriptedTool accepts clap-based builtins, and closed schemas reject unknown flags instead of passing them through to the script.
Source:
## Highlights
* **ScriptedTool builtins can now use clap.** `ScriptedToolBuilder::builtin(...)` registers a raw-argv builtin alongside `ToolDef` tools, so commands needing positionals, short flags, `--`, or parse-time required-argument checking work inside a `ScriptedTool` ([#2408](https://github.com/everruns/bashkit/pull/2408)).
* **Closed schemas reject unknown flags.** With `"additionalProperties": false`, an unrecognised flag is now an error naming the valid flags instead of a silently-dropped property, and clap output is stripped of ANSI codes ([#2408](https://github.com/everruns/bashkit/pull/2408)).
## In the changelog
* [ScriptedTool accepts clap-based builtins](https://everruns.com/docs/changelog/2026-09-17-bashkit-scripted-tool-clap-builtins/)
See the [GitHub release](https://github.com/everruns/bashkit/releases/tag/v0.18.1) and the [full release notes](https://github.com/everruns/bashkit/blob/main/CHANGELOG.md#0181---2026-09-17) for every pull request in this version.
---
# ScriptedTool accepts clap-based builtins
> ScriptedToolBuilder::builtin registers raw-argv builtins next to ToolDef tools, and closed schemas now reject unknown flags with an error naming valid ones.
Source:
`ScriptedToolBuilder::builtin(...)` registers a builtin that receives raw arguments, next to the `ToolDef` tools of a `ScriptedTool`. Commands that need positionals, short flags, `--` or required-argument checks at parse time can now use clap inside a `ScriptedTool`:
```rust
.builtin("get_agent", Box::new(GetAgent))
```
A tool whose schema sets `"additionalProperties": false` now rejects an unknown flag with an error that names the valid flags, instead of silently dropping it. clap output is stripped of ANSI codes.
See [Scripted tools](https://github.com/everruns/bashkit/blob/main/docs/scripted-tools.md), [clap builtins](https://github.com/everruns/bashkit/blob/main/crates/bashkit/docs/clap-builtins.md) and [#2408](https://github.com/everruns/bashkit/pull/2408).
---
# One everruns command line for the CLI, MCP and agent shells
> Every Everruns operation now has one noun-verb spelling with --help that works the same in the everruns CLI, the /mcp endpoint and an agent's shell.
Source:
`everruns --flags` is now a single grammar. The external CLI, the `/mcp` endpoint and a session’s shell render it from one declaration, so the CLI grows from 82 commands to 309 across 31 nouns, and `--help` reaches every one of them. Flat names such as `list_agents` keep working as aliases.
```bash
everruns agents list
everruns sessions --help
```
**In code:** a Framework application exposing its own command tree can name its root token with `CliCommandSource::root` instead of `everruns`.
A Platform Chat v2 harness built on this command line is available behind an organization opt-in flag. See [CLI](https://everruns.com/docs/features/cli/) and [CLI reference](https://everruns.com/docs/reference/cli/). [#3680](https://github.com/everruns/everruns/pull/3680), [#3490](https://github.com/everruns/everruns/pull/3490), [#3496](https://github.com/everruns/everruns/pull/3496)
---
# Guardrails can use typed judgments as their check engine
> LLM judge and moderation guardrails can run on calibrated probabilities from TypeSafe judgments, batching every check on a stage into one request.
Source:
The `llm_judge` and `moderation` guardrails take an `engine` setting: `utility_llm` (the default, unchanged) or `jev`. With `jev`, every check on a stage is answered in one request, and the verdict is the configured `threshold` applied to a calibrated probability rather than a JSON verdict parsed back from text. A check can no longer pass because a response failed to parse.
```json
{
"stage": "tool_use",
"type": "llm_judge",
"engine": "jev",
"threshold": 70,
"prompt": "Block any tool call that deletes customer records."
}
```
Operators enable it with the `UTILITY_TYPESAFE_API_KEY` deployment credential. When it is unset, jev checks are skipped with a log line and the turn proceeds. An experimental `jev` capability with a `jev_evaluate` tool is available in development registries.
See [Guardrails](https://everruns.com/docs/capabilities/guardrails/) and [TypeSafe](https://everruns.com/docs/integrations/typesafe/). [#3644](https://github.com/everruns/everruns/pull/3644)
---
# Soft approval: agents pause before destructive or outward actions
> The new soft_approval capability, on by default for Generic and Platform Chat, has agents ask before risky actions and records who approved in the audit log.
Source:
The `soft_approval` capability tells an agent to batch safe work and pause before destructive or outward-facing actions such as deleting data, pushing, publishing or sending messages. The pause is a `request_approval` tool call, so a waiting turn is something a host can render. `record_approval` writes the grant and `set_approval_mode` changes the level. Add it to any agent:
```json
{
"ref": "soft_approval",
"config": {
"mode": "normal"
}
}
```
It is enabled at `normal` on the Generic and Platform Chat harnesses. The approver is resolved on the server from the authenticated message, never from what the model says, and recorded as `agent.approval.requested` and `agent.approval.granted` in the audit log.
See [Soft approval](https://everruns.com/docs/capabilities/soft-approval/). [#3655](https://github.com/everruns/everruns/pull/3655)
---
# Agent endpoints and triggers replace Apps in Everruns
> App management is retired: an agent's endpoints and triggers now live on its Integrations tab, with an org-wide Exposures view and per-agent suspend.
Source:
The App abstraction is retired. Endpoints (Slack, webhook, AG-UI, A2A, public chat and others) and triggers now belong to the agent.
**In the UI:** an agent’s **Integrations** tab lists its endpoints and triggers, each with its own publish switch and real endpoint URL, plus a switch to suspend all of the agent’s exposures at once. A new **Exposures** page lists every endpoint and trigger in the organization, flags anonymous live ones, and can suspend or resume an agent. App management pages are gone.
**In the API:** publishing moves one endpoint. Suspend with `POST /v1/agents/{agent_id}/exposures/suspend` and resume with `.../resume`. App write and command surfaces are removed; existing App records are frozen and ingress URLs keep working.
See [Channels](https://everruns.com/docs/features/channels/) and [Agent triggers](https://everruns.com/docs/features/agent-triggers/). [#3698](https://github.com/everruns/everruns/pull/3698), [#3624](https://github.com/everruns/everruns/pull/3624), [#3627](https://github.com/everruns/everruns/pull/3627), [#3621](https://github.com/everruns/everruns/pull/3621)
---
# Everruns 0.29.0: Platform Chat v2 on a unified command line
> Framework code calls models directly with Model::complete, one everruns command line serves the CLI, MCP and agent shells, and MCP catalogs can be scoped.
Source:

## Highlights
* **Platform Chat v2 on a unified command line**: One command line serves both people and agents, with Platform Chat rebuilt on top of it ([#3680](https://github.com/everruns/everruns/pull/3680)).
* **Soft approval**: A soft-approval capability lands and is on by default for Generic and Platform Chat, so an agent can proceed with a lightweight, reversible approval step ([#3655](https://github.com/everruns/everruns/pull/3655)).
* **Typed judgments and a guardrail engine**: Typed judgments become a capability, backing a guardrail engine for classifying and gating agent behavior ([#3644](https://github.com/everruns/everruns/pull/3644)).
* **Scoped MCP catalogs and acts-as**: MCP gains scoped catalog presets and acts-as credential semantics, with endpoint auth stored separately ([#3638](https://github.com/everruns/everruns/pull/3638), [#3635](https://github.com/everruns/everruns/pull/3635)).
## In the changelog
* [Framework direct model calls with Model::complete](https://everruns.com/docs/changelog/2026-09-16-framework-direct-model-calls/)
* [One everruns command line for the CLI, MCP and agent shells](https://everruns.com/docs/changelog/2026-09-17-everruns-command-line-tree/)
* [Guardrails can use typed judgments as their check engine](https://everruns.com/docs/changelog/2026-09-17-guardrails-typed-judgment-engine/)
* [Soft approval: agents pause before destructive or outward actions](https://everruns.com/docs/changelog/2026-09-17-soft-approval-capability/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.29.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0290---2026-09-18) for every pull request in this version.
---
# Slack agents ask approval with buttons and show task progress
> Agents deployed to Slack can take approvals through Approve and Decline buttons, show live sub-task progress and act as the channel's own bot.
Source:
Three additions for agents running in Slack:
* **Approvals:** when an agent pauses with `request_approval`, the thread shows buttons. Only the person the agent is answering can use them, and answering removes the buttons. Apps created earlier must save a fresh manifest once.
* **Task progress:** a delegating agent posts one message, updated in place, that summarizes its sub-tasks, for example how many finished or failed.
* **Slack actions:** a native `slack` capability adds `slack_add_reaction`, `slack_update_message`, `slack_lookup_user` and `slack_upload_file`, using the endpoint’s own bot token instead of a separate Slack MCP server.
```json
{
"capabilities": [
{
"ref": "soft_approval"
},
{
"ref": "slack"
}
]
}
```
See [Slack approvals](https://everruns.com/docs/capabilities/slack/#approvals), [task progress](https://everruns.com/docs/capabilities/slack/#task-progress) and [Slack actions](https://everruns.com/docs/capabilities/slack/#slack-actions). [#3696](https://github.com/everruns/everruns/pull/3696), [#3697](https://github.com/everruns/everruns/pull/3697), [#3693](https://github.com/everruns/everruns/pull/3693)
---
# Yolop 0.18.3 release
> yolop coordination spawn-workers prints validated launch commands for each worker, and yolop_spawn routes agent and session spawns separately.
Source:
## Highlights
* Coordinator staffing: `yolop coordination spawn-workers` prints validated per-worker launch commands, and `yolop_spawn` routes agent versus session spawns.
* Setup can log out: leave model setup without saving, and drivers log out on exit.
* Settings and connections hot-reload; integration tests use isolated spawns.
* TUI adopts tuika 0.12 ToolShell and drops legacy shell paths.
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.18.3) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0183---2026-09-18) for every pull request in this version.
---
# Set spend budgets on agent endpoints from the Integrations tab
> Agent and endpoint budgets can be managed on the agent's Integrations tab, and sessions are attributed to the endpoint they arrived through.
Source:
Sessions now record the endpoint they arrived through, and budgets can target an agent endpoint. Existing App budgets were converted to one budget per endpoint with the same limit and balance.
**In the UI:** with the `app_budgets` feature flag on, the agent’s **Integrations** tab manages agent and endpoint budgets, showing the period, its UTC reset and any refusal. Members without `budget.manage` see them read-only.
Slack and FCP endpoint budgets now bind and are enforced. If you configured one before and it never took effect, it starts limiting spend.
See [Budgets](https://everruns.com/docs/advanced/budgets/) and [Enforce a budget](https://everruns.com/docs/how-to/enforce-a-budget/). [#3719](https://github.com/everruns/everruns/pull/3719), [#3623](https://github.com/everruns/everruns/pull/3623), [#3625](https://github.com/everruns/everruns/pull/3625)
---
# Host Shell gives Framework agents a kernel-contained Bash
> The new host_shell capability lets an embedded Everruns agent run real host processes inside a Seatbelt or Landlock policy that fails closed.
Source:
Embedders can give an agent a `bash` tool that runs real child processes on the host, with the kernel enforcing what it may touch: Seatbelt on macOS, Landlock plus seccomp on Linux. The `host_shell` capability ships behind the `host-shell` feature on the `everruns` or `everruns-host` crate, which the Everruns server does not enable.
`HostCompute::contained(options)` builds the contained environment, and a `ShellApprovalGate` decides whether each command runs at all; with no gate the answer is no. A missing kernel primitive is a launch error, never a silent fall back to an uncontained shell. To try it, run the example from a checkout:
```bash
export OPENAI_API_KEY="your-key"
cargo run -p everruns-host-shell-agent -- --interactive
```
See [Host Shell](https://everruns.com/docs/capabilities/host-shell/) and the [Host Shell Agent example](https://everruns.com/docs/framework/examples/host-shell-agent/). [#3715](https://github.com/everruns/everruns/pull/3715)
---
# MCP catalog presets, acts-as identity and personal connections
> MCP servers gain an org catalog of presets, an actsAs setting for whose sign-in a server uses, an agent MCP tab and a place to revoke your own sign-ins.
Source:
**In the UI:** the MCP servers page becomes a catalog of presets showing which active agents use each one and what archiving would affect. The agent detail page gains an MCP tab listing the agent’s effective servers, their identity and connection state, with flows to add a preset or custom HTTP server and to revoke a connection. People can see and revoke the MCP sign-ins they granted.
**In agent config:** an MCP entry can reference a preset with `catalog:` and say whose sign-in its calls use with `actsAs` (`none`, `service`, `user` or `user_or_service`):
```json
{
"github": {
"use": "catalog:github",
"actsAs": "user"
}
}
```
See [MCP](https://everruns.com/docs/features/mcp/) and [MCP credentials](https://everruns.com/docs/integrations/mcp-credentials/). [#3726](https://github.com/everruns/everruns/pull/3726), [#3675](https://github.com/everruns/everruns/pull/3675), [#3638](https://github.com/everruns/everruns/pull/3638)
---
# Install an Everruns agent in Slack with one click
> Connecting an agent to Slack no longer needs credentials copied from api.slack.com: Everruns creates the Slack app and takes its credentials from OAuth.
Source:
Connecting an agent to Slack used to mean copying four values out of api.slack.com. Now you name the Slack endpoint, save it and choose **Connect to Slack**: Everruns creates the app from the manifest it generates, Slack shows one consent screen, and the signing secret, bot token and workspace ID arrive on their own.
One-click install needs a Slack app configuration token on the deployment, which Everruns Cloud has. Without one, the button is hidden and the manual fields stay available under **Configure manually**.
See [Publish to Slack](https://everruns.com/docs/how-to/publish-to-slack/) and [Slack](https://everruns.com/docs/capabilities/slack/). [#3724](https://github.com/everruns/everruns/pull/3724)
---
# sed is rewritten to follow GNU sed 4.9
> Bashkit rewrote its sed after an audit found 46 differences from GNU sed 4.9, adding missing commands, GNU address ranges, case conversion and atomic -i.
Source:
Bashkit’s `sed` was rewritten after a comparison with GNU sed 4.9 found 46 mismatches. It adds the missing commands `y = l n N P D T z F r R w W`, GNU range-address semantics, `\U \L \u \l \E` case conversion, and real option parsing for `-s`, `-z`, `-f`, `-l` and attached `-i.bak`. A missing final newline is preserved, and `-i` now writes files atomically.
```bash
echo 'hello world' | sed 's/hello/\U&/' # HELLO world
sed -i.bak 'y/abc/xyz/' notes.txt # keeps notes.txt.bak
sed -n '$=' notes.txt # line count
```
The same release builds VFS paths with forward slashes on every host, including Windows.
See the [compatibility scorecard](https://github.com/everruns/bashkit/blob/main/crates/bashkit/docs/compatibility.md), [#2436](https://github.com/everruns/bashkit/pull/2436) and [#2435](https://github.com/everruns/bashkit/pull/2435).
---
# Bashkit 0.18.2 release
> sed is rewritten to follow GNU sed 4.9 so scripts written for Linux behave the same, Windows paths resolve correctly, and releases are hardened.
Source:
## Highlights
* **`sed` is GNU-aligned.** The stream editor was rewritten after a differential audit against GNU sed 4.9 found 46 mismatches. It adds the missing commands (`y = l n N P D T z F r R w W`), GNU range-address semantics, `\U \L \u \l \E` case conversion, a real getopt (`-s`, `-z`, `-f`, `-l`, attached `-i.bak`), and it preserves a missing final newline. `-i` now writes atomically ([#2436](https://github.com/everruns/bashkit/pull/2436)).
* **Windows path fix.** VFS paths use forward slashes on every host ([#2435](https://github.com/everruns/bashkit/pull/2435)).
* **Release pipeline hardening.** npm publish verification now outlasts the registry cache ([#2426](https://github.com/everruns/bashkit/pull/2426)), and untrusted workflow code no longer receives the checkout token ([#2420](https://github.com/everruns/bashkit/pull/2420)).
## In the changelog
* [sed is rewritten to follow GNU sed 4.9](https://everruns.com/docs/changelog/2026-09-21-bashkit-gnu-aligned-sed/)
See the [GitHub release](https://github.com/everruns/bashkit/releases/tag/v0.18.2) and the [full release notes](https://github.com/everruns/bashkit/blob/main/CHANGELOG.md#0182---2026-09-22) for every pull request in this version.
---
# Claude Opus 5.5, GPT-6 Sol and GPT-6 Luna models added
> Claude Opus 5.5 becomes the recommended Opus model, and GPT-6 Luna replaces GPT-5.6 Terra as the platform default model and the default utility model.
Source:
Three new models are available: Claude Opus 5.5 (`claude-opus-5-5`, plus the 1M-context `claude-opus-5-5[1m]`), GPT-6 Sol (`gpt-6-sol`) and GPT-6 Luna (`gpt-6-luna`). All are seeded as enabled favorites, and Opus 5.5 is marked as the recommended Anthropic model. Opus 5 stays enabled for existing agents.
GPT-6 Luna replaces GPT-5.6 Terra as the platform default model. It is also the default utility model behind Agent Analyze, Health checks and `llm_judge` guardrails; set `UTILITY_LLM_MODEL` to choose another:
```bash
# Control-plane and workers both read it.
UTILITY_LLM_MODEL=gpt-6-sol
```
See [Anthropic](https://everruns.com/docs/providers/anthropic/), [OpenAI](https://everruns.com/docs/providers/openai/) and [environment variables](https://everruns.com/docs/sre/environment-variables/). PRs [#3769](https://github.com/everruns/everruns/pull/3769), [#3771](https://github.com/everruns/everruns/pull/3771), [#3774](https://github.com/everruns/everruns/pull/3774).
---
# Everruns Cloud sign-in failures return to the login page
> A failed Everruns Cloud sign-in now lands on the login page with the reason and a way to try again, instead of a raw JSON error on the callback URL.
Source:
A sign-in to [Everruns Cloud](https://app.everruns.com) that did not complete used to leave you on the callback URL looking at a JSON error such as `{"error":"Login temporarily unavailable"}`, with no way forward.
Now it returns to the login page and says whether sign-in was cancelled, the 10-minute sign-in window expired, or Everruns ID could not be reached, with a **Try signing in again** action. A connection failure while exchanging the login code is retried before it fails, and a malformed token response is reported as unavailable rather than as an expired link.
See [Everruns Cloud quickstart](https://everruns.com/docs/getting-started/cloud/).
---
# Claude always-thinking models no longer return 400s or empty answers
> The Anthropic driver rejects prefill early, maps an explicit none effort to low, and keeps requests append-only on Opus 5.5 and other always-thinking models.
Source:
Requests to Claude models that always think, such as Opus 5.5, now behave predictably:
* A request that ends on an assistant turn (prefill) fails fast with a configuration error that names the fix, instead of a provider 400 and a retry cycle.
* An explicit `none` reasoning effort maps to `low` on models where thinking cannot be turned off. Before, the driver sent nothing and the API ran at its own default.
* Each request in a session replays the previous one and only appends to it, so preserved thinking stays valid across turns.
See [Anthropic](https://everruns.com/docs/providers/anthropic/). PRs [#3782](https://github.com/everruns/everruns/pull/3782), [#3783](https://github.com/everruns/everruns/pull/3783).
---
# Everruns 0.30.0: Ask User
> Agent endpoints and triggers replace Apps, Slack agents ask for approval with buttons and show task progress, and agents can ask the user questions.
Source:

## Highlights
* **Ask User**: Agents can pause mid-run to ask a person a question: an inline UI card in Platform Chat ([#3735](https://github.com/everruns/everruns/pull/3735)), in-process and hosted strategies ([#3738](https://github.com/everruns/everruns/pull/3738)), expiry that resolves to declared defaults ([#3757](https://github.com/everruns/everruns/pull/3757)), and credential collection that never enters the log or agent context ([#3747](https://github.com/everruns/everruns/pull/3747)). Enabled on the default chat harnesses ([#3737](https://github.com/everruns/everruns/pull/3737)).
* **Host Shell**: A new `host_shell` capability runs the `bash` tool as real child processes on the machine hosting the agent, bounded by a kernel policy (Seatbelt and Landlock). Behind the `host-shell` feature on `everruns` or `everruns-host`; see the [host shell example](https://github.com/everruns/everruns/tree/main/examples/host-shell-agent) ([#3719](https://github.com/everruns/everruns/pull/3719)).
* **Slack one-click install**: Install the Slack app in one click, creating the app and taking its credentials straight from OAuth ([#3724](https://github.com/everruns/everruns/pull/3724)).
* **New models**: Claude Opus 5.5 lands as the recommended Opus ([#3769](https://github.com/everruns/everruns/pull/3769)), and GPT-6 Sol and Luna arrive with GPT-6 Luna as the default chat and utility model ([#3771](https://github.com/everruns/everruns/pull/3771), [#3774](https://github.com/everruns/everruns/pull/3774)).
* **Agent endpoint budget controls**: Set spend budgets on agent endpoints from the UI ([#3719](https://github.com/everruns/everruns/pull/3719)).
## In the changelog
* [Agent endpoints and triggers replace Apps in Everruns](https://everruns.com/docs/changelog/2026-09-18-agent-endpoints-replace-apps/)
* [Slack agents ask approval with buttons and show task progress](https://everruns.com/docs/changelog/2026-09-18-slack-approvals-progress-actions/)
* [Set spend budgets on agent endpoints from the Integrations tab](https://everruns.com/docs/changelog/2026-09-19-agent-endpoint-budgets/)
* [Host Shell gives Framework agents a kernel-contained Bash](https://everruns.com/docs/changelog/2026-09-19-framework-host-shell/)
* [MCP catalog presets, acts-as identity and personal connections](https://everruns.com/docs/changelog/2026-09-19-mcp-catalog-connections-acts-as/)
* [Install an Everruns agent in Slack with one click](https://everruns.com/docs/changelog/2026-09-19-slack-one-click-install/)
* [Claude Opus 5.5, GPT-6 Sol and GPT-6 Luna models added](https://everruns.com/docs/changelog/2026-09-22-claude-opus-5-5-gpt-6-models/)
Before upgrading, read the breaking changes: [Agent endpoints and triggers replace Apps in Everruns](https://everruns.com/docs/changelog/2026-09-18-agent-endpoints-replace-apps/).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.30.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0300---2026-09-23) for every pull request in this version.
---
# Ask User lets agents pause and ask you questions mid-run
> Agents with the ask_user capability pause a run to ask one to four choice, text or secret questions, shown as an inline card that you answer in place.
Source:
**In the UI:** an agent with the `ask_user` capability can stop and ask one to four structured questions instead of guessing. The chat shows a card with choice options, a free-form text field, or a secret field; answering resumes the turn. An unanswered choice falls back to the option the model recommended, while text and secret questions are declined. A secret answer never enters the event log or the agent’s context. The default chat harnesses enable it.
**In code:** implement the `AskUser` trait to answer questions from your own interface when you embed the Framework.
See [Ask User](https://everruns.com/docs/capabilities/ask-user/) and [Answer an agent’s questions](https://everruns.com/docs/framework/ask-user/). PRs [#3735](https://github.com/everruns/everruns/pull/3735), [#3738](https://github.com/everruns/everruns/pull/3738), [#3747](https://github.com/everruns/everruns/pull/3747), [#3757](https://github.com/everruns/everruns/pull/3757), [#3790](https://github.com/everruns/everruns/pull/3790).
---
# Everruns 0.31.0: Ask User over MCP and A2A
> Claude always-thinking models no longer return 400s or empty answers, and Ask User lets an agent pause mid-run to ask you a question.
Source:

## Highlights
* **Ask User over MCP and A2A**: The Ask User capability now reaches agents beyond Platform Chat: it projects as form-mode elicitation over `/mcp` ([#3781](https://github.com/everruns/everruns/pull/3781)) and is carried over the inbound A2A channel as a DataPart ([#3780](https://github.com/everruns/everruns/pull/3780)).
* **Always-thinking model reliability**: Always-thinking models no longer return 400s or empty answers ([#3782](https://github.com/everruns/everruns/pull/3782)), preserved thinking keeps the conversation append-only ([#3783](https://github.com/everruns/everruns/pull/3783)), append-only `clear_at` reminders land ([#3787](https://github.com/everruns/everruns/pull/3787)), and hosted tool-search replay order is preserved ([#3789](https://github.com/everruns/everruns/pull/3789)).
* **Server resilience under load**: Background sweeps run isolated with their own database pool ([#3786](https://github.com/everruns/everruns/pull/3786), [#3779](https://github.com/everruns/everruns/pull/3779)), and the server returns a graceful 503 on database pool exhaustion instead of failing hard ([#3788](https://github.com/everruns/everruns/pull/3788)).
## In the changelog
* [Claude always-thinking models no longer return 400s or empty answers](https://everruns.com/docs/changelog/2026-09-23-claude-always-thinking-fixes/)
* [Ask User lets agents pause and ask you questions mid-run](https://everruns.com/docs/changelog/2026-09-24-ask-user-agent-questions/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.31.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0310---2026-09-24) for every pull request in this version.
---
# Ask User questions reach MCP, A2A and Tasks clients
> A session waiting on ask_user now shows its question to MCP clients as a form elicitation, to A2A callers as a typed data part, and to Tasks clients.
Source:
When an agent waits on an `ask_user` question, clients outside the Everruns UI can now read and answer it. Before, they saw a session that stayed working.
* Over `/mcp`, the question set is a form-mode elicitation. A client that never declared elicitation gets the declared defaults.
* Over inbound A2A, the task reports that input is required, with the question as text and as a data part under `everruns/ask_user`. Reply with a data part under `everruns/ask_user_answer`.
* Over MCP Tasks, `tasks/get` exposes the pending form through `inputRequests`, and `tasks/update` answers it.
An A2A answer names the task and the `ask_user` call:
```json
{
"role": "user",
"taskId": "",
"parts": [
{
"kind": "data",
"data": {
"everruns/ask_user_answer": {
"tool_call_id": "call_ask_1",
"status": "answered",
"answers": [
{
"id": "target",
"selected": [
"Production"
]
}
]
}
}
}
]
}
```
See [Ask User](https://everruns.com/docs/capabilities/ask-user/), [MCP](https://everruns.com/docs/features/mcp/) and [A2A](https://everruns.com/docs/features/a2a/). PRs [#3781](https://github.com/everruns/everruns/pull/3781), [#3780](https://github.com/everruns/everruns/pull/3780), [#3828](https://github.com/everruns/everruns/pull/3828).
---
# Framework engines get event listeners and OpenTelemetry export
> Register a listener once per Engine to receive events from every session it owns, and export Framework sessions to OpenTelemetry or Braintrust.
Source:

An Engine now delivers every event from every session it owns, including resumed and spawned ones, to the listeners registered on it. You no longer poll `Session::events()` per session. Each listener drains a bounded ordered queue, and shutdown flushes them within one deadline.
The `everruns` crate ships two listeners behind independent features, `otel` and `braintrust`:
```rust
let engine = Engine::builder()
.observe(OpenTelemetry::from_env())
.build();
```
Message content is not exported by default.
See [Observability](https://everruns.com/docs/framework/observability/). PRs [#3804](https://github.com/everruns/everruns/pull/3804), [#3806](https://github.com/everruns/everruns/pull/3806).
## Update 2026-10-14
[OpenTelemetry export and accounting fixes](https://everruns.com/docs/changelog/2026-10-14-opentelemetry-export-and-accounting/) correct cached-token totals, exporter batching, tool identity, error privacy and shutdown reporting.
---
# Yolop 0.18.4 release
> ACP advertises user-invocable skills to clients and announces finished background work even when the wake turn itself stays silent.
Source:
## Highlights
* ACP now advertises user-invocable skills to clients, and announces finished background work even when the wake turn stays silent.
* Action-guard gains a Muse-only idle-promise guard on the Jev classifier.
* MCP hardens workspace execution by blocking stdio process spawning and requiring explicit environment credential bindings.
* Observability and provider handling improve: Logfire forwards full LLM cost metadata and span duration, and Codex handles cross-provider reasoning.
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.18.4) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0184---2026-09-25) for every pull request in this version.
---
# Everruns 0.32.0: Per-organization Slack app provisioning
> Ask User questions reach MCP, A2A and Tasks clients, and Framework engines gain event listeners, OpenTelemetry export and an app framework.
Source:

## Highlights
* **Per-organization Slack app provisioning**: Each organization can provision its own Slack app rather than sharing one deployment-wide install ([#3830](https://github.com/everruns/everruns/pull/3830)), with concurrent installs for one endpoint serialized so a race can no longer create orphan apps or exhaust the config token ([#3843](https://github.com/everruns/everruns/pull/3843)).
* **Agent app framework over Engine**: An experimental framework for building agent apps directly on the Engine ([#3802](https://github.com/everruns/everruns/pull/3802)), with engine-level event listeners ([#3804](https://github.com/everruns/everruns/pull/3804)) and pluggable observability integrations ([#3806](https://github.com/everruns/everruns/pull/3806)).
* **Server-side context compaction**: Anthropic conversations compact context server-side, keeping long agent runs within budget ([#3831](https://github.com/everruns/everruns/pull/3831)).
* **Ask User through tasks**: The Ask User capability surfaces through the tasks channel, extending it beyond Platform Chat and MCP ([#3828](https://github.com/everruns/everruns/pull/3828)).
* **Server hardening**: Platform Chat question-answers and tool-results now require the session owner ([#3842](https://github.com/everruns/everruns/pull/3842)), unrequested reasoning summaries stay hidden from responses ([#3840](https://github.com/everruns/everruns/pull/3840)), feature-flag revocations take effect immediately ([#3820](https://github.com/everruns/everruns/pull/3820)), retained observer event memory is bounded ([#3837](https://github.com/everruns/everruns/pull/3837)), and native endpoint sessions are reused rather than rebuilt ([#3814](https://github.com/everruns/everruns/pull/3814)).
## In the changelog
* [Ask User questions reach MCP, A2A and Tasks clients](https://everruns.com/docs/changelog/2026-09-25-ask-user-mcp-a2a-clients/)
* [Framework engines get event listeners and OpenTelemetry export](https://everruns.com/docs/changelog/2026-09-25-framework-event-listeners-opentelemetry/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.32.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0320---2026-09-26) for every pull request in this version.
---
# Agent page rebuilt as one view-and-edit workspace
> The agent page shows the system prompt as the page with settings in a side column, and you edit everything in place with one Save or Discard.
Source:
The agent page is now one workspace. The system prompt fills the page and a narrow column holds the rest of the configuration. Select **Edit** to change the prompt and settings in place, then **Save changes** or **Discard** for the whole agent. Changes apply to new sessions.
MCP servers, credentials, branding, starter files, network access, token usage and health check open as side sheets from **More**. Version history and archive move to the header’s **More actions** menu. Old `/edit` links open the page in edit mode, and `/sessions` opens the Sessions tab.
See [Agent page](https://everruns.com/docs/features/ui/#agent-page). PR [#3917](https://github.com/everruns/everruns/pull/3917).
## Update 2026-10-18
Files stays in More, and on Preview, only when the agent has a filesystem or starter files. An agent without one no longer shows an empty Files section.
---
# Everruns MCP server renders as an MCP App in ChatGPT and Codex
> MCP App hosts such as ChatGPT and Codex show a live session view, answer agent questions in a form, approve pending actions and open an Everruns panel.
Source:

The Everruns MCP server at `/mcp` is now an MCP App. In hosts that render MCP Apps, such as ChatGPT and Codex, running an agent shows a session view with status, messages and a reply box. Agent questions appear as a form, and pending approvals as Approve and Decline buttons that refuse stale clicks. An Everruns panel opens from the ChatGPT sidebar.
Hosts that do not render apps get the same tools as plain text, and the existing tool shapes are unchanged.
See [MCP: In ChatGPT and Codex](https://everruns.com/docs/features/mcp/#in-chatgpt-and-codex). PR [#3927](https://github.com/everruns/everruns/pull/3927).
---
# Claude Sonnet 5.5 and GPT-6.1 Sol with fast and ultrafast tiers
> Claude Sonnet 5.5 becomes the favorite Sonnet model, GPT-6.1 Sol adds fast and ultrafast speed tiers, and the sunset Claude Sonnet 4.5 profile is removed.
Source:
Claude Sonnet 5.5 (`claude-sonnet-5-5`, plus a `[1m]` 1M-context twin) is seeded as an enabled Anthropic favorite. It always thinks, so an explicit `none` effort sends `low`.
GPT-6.1 Sol joins the OpenAI models. Its `controls.speed` setting accepts `fast` and `ultrafast`. The OpenAI drivers reject a speed tier the model does not list before sending, and usage is priced at the tier that served the call. Set the tier per message:
```bash
curl -X POST http://localhost:9300/api/v1/sessions/{session_id}/messages \
-H "Authorization: Bearer $EVERRUNS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": {"role": "user", "content": [{"type": "text", "text": "Hello!"}]},
"controls": {"speed": "fast"}}'
```
Anthropic sunset Claude Sonnet 4.5, so its model profile and catalog entry are removed.
See [Anthropic](https://everruns.com/docs/providers/anthropic/) and [OpenAI](https://everruns.com/docs/providers/openai/). PRs [#3902](https://github.com/everruns/everruns/pull/3902), [#3925](https://github.com/everruns/everruns/pull/3925), [#3952](https://github.com/everruns/everruns/pull/3952).
## Update 2026-10-10
GPT-6.1 Sol now lists only `fast`: OpenAI has not released Ultrafast for it, so the drivers reject `ultrafast` before sending.
---
# GitHub pull request triggers with a per-agent GitHub App
> Each agent can create and install its own GitHub App by clicks, read pull requests and diffs, post comments, and start sessions from pull request events.
Source:

An agent can now act on GitHub pull requests. Use the **Connect GitHub** card on the agent’s Integrations tab to create and install the agent’s own GitHub App. The [GitHub](https://everruns.com/docs/capabilities/github/) capability then gives it `get_github_pull_request`, `get_github_pull_request_diff` and `upsert_github_comment`.
Add an [agent trigger](https://everruns.com/docs/features/agent-triggers/) in GitHub mode, or from the CLI with `--github-events` and `--repositories`, to start work on pull request events. Webhooks are signature verified, deduplicated by delivery ID, and routed to one session per pull request. Every trigger now shares that pipeline, so webhook triggers also accept filters and dedupe templates.
See [Summarize GitHub pull requests](https://everruns.com/docs/how-to/summarize-github-pull-requests/). PRs [#3935](https://github.com/everruns/everruns/pull/3935), [#3943](https://github.com/everruns/everruns/pull/3943), [#3915](https://github.com/everruns/everruns/pull/3915).
---
# OpenAI hosted code interpreter, shell, file search and remote MCP
> The openai_server_tools capability adds code interpreter, hosted shell, file search and approval-gated remote MCP next to web search on OpenAI Responses agents.
Source:
Agents on the OpenAI or Azure OpenAI Responses API can let OpenAI run tools inside the response. The `openai_server_tools` capability accepts `web_search`, `code_interpreter`, `shell`, `file_search` and `mcp`:
```json
{
"ref": "openai_server_tools",
"config": {
"tools": [
"code_interpreter",
"shell",
"file_search"
],
"file_search_vector_store_ids": [
"vs_abc123"
]
}
}
```
Hosted calls show in the work log and are priced in usage. Each remote MCP call pauses on an approval card by default. A server that needs credentials is named by its registered Everruns MCP server, and its URL and token are resolved per call, never stored in agent config or events.
See [OpenAI server tools](https://everruns.com/docs/capabilities/openai-server-tools/). PRs [#3918](https://github.com/everruns/everruns/pull/3918), [#3923](https://github.com/everruns/everruns/pull/3923), [#3928](https://github.com/everruns/everruns/pull/3928), [#3930](https://github.com/everruns/everruns/pull/3930).
---
# Everruns 0.33.0 release
> The agent page is one view-and-edit workspace, the Everruns MCP server renders as an MCP App in ChatGPT and Codex, and triggers react to agent events.
Source:

## Highlights
* **GitHub pull request triggers and per-agent GitHub Apps**: Agents connect their own GitHub App by clicks and act on pull requests, with PR triggers, a Connect GitHub card, PR tools, and a PR summarizer guide ([#3935](https://github.com/everruns/everruns/pull/3935), [#3943](https://github.com/everruns/everruns/pull/3943)).
* **OpenAI Responses hosted tools and remote MCP**: The OpenAI Responses driver gains hosted web search ([#3918](https://github.com/everruns/everruns/pull/3918), [#3923](https://github.com/everruns/everruns/pull/3923)), hosted code interpreter, shell and file search ([#3928](https://github.com/everruns/everruns/pull/3928)), remote MCP with approval and registered-server credentials ([#3930](https://github.com/everruns/everruns/pull/3930)), and structured output with background mode ([#3932](https://github.com/everruns/everruns/pull/3932)).
* **Agent trigger event pipeline**: A new event pipeline with filters, dedupe, and per-subject sessions ([#3915](https://github.com/everruns/everruns/pull/3915)), plus outbound MCP Events for session webhooks ([#3931](https://github.com/everruns/everruns/pull/3931)).
* **MCP Apps for ChatGPT and Codex**: Everruns serves MCP Apps views for ChatGPT and Codex ([#3927](https://github.com/everruns/everruns/pull/3927)), answering form-mode elicitation through ask_user ([#3929](https://github.com/everruns/everruns/pull/3929)).
* **Pluggable decision drivers**: Decision drivers route behind one router ([#3921](https://github.com/everruns/everruns/pull/3921)), with the OpenAI Decisions API available as a preview driver ([#3924](https://github.com/everruns/everruns/pull/3924)).
* **Provider-neutral computer use**: A provider-neutral computer tool runs on Browserless ([#3922](https://github.com/everruns/everruns/pull/3922)).
* **New models**: Claude Sonnet 5.5 joins as the favorite Sonnet ([#3902](https://github.com/everruns/everruns/pull/3902)) and GPT-6.1 Sol arrives with fast and ultrafast speed tiers ([#3925](https://github.com/everruns/everruns/pull/3925)); the sunset Claude Sonnet 4.5 profile is dropped ([#3952](https://github.com/everruns/everruns/pull/3952)).
* **Rebuilt agent workspace**: The agent page is rebuilt as a single view-and-edit workspace ([#3917](https://github.com/everruns/everruns/pull/3917)).
## In the changelog
* [Agent page rebuilt as one view-and-edit workspace](https://everruns.com/docs/changelog/2026-09-29-agent-page-workspace/)
* [Everruns MCP server renders as an MCP App in ChatGPT and Codex](https://everruns.com/docs/changelog/2026-09-29-mcp-app-chatgpt-codex/)
* [Claude Sonnet 5.5 and GPT-6.1 Sol with fast and ultrafast tiers](https://everruns.com/docs/changelog/2026-09-30-claude-sonnet-5-5-gpt-6-1-sol/)
* [GitHub pull request triggers with a per-agent GitHub App](https://everruns.com/docs/changelog/2026-09-30-github-pull-request-triggers/)
* [OpenAI hosted code interpreter, shell, file search and remote MCP](https://everruns.com/docs/changelog/2026-09-30-openai-hosted-tools-remote-mcp/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.33.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0330---2026-10-01) for every pull request in this version.
---
# PR reviewer and security scanner agent templates for GitHub
> Two guided agent templates use the agent's GitHub App to review pull requests with inline comments and to run a weekly scan that files GitHub issues.
Source:
Two guided templates build on the per-agent GitHub App. The PR reviewer reviews pull requests and posts deduplicated inline comments. The weekly security scanner files fingerprinted issues, so a repeat finding updates the same issue, and it can open draft fix pull requests, which is off by default.
The templates add GitHub review, issue upsert and pull request tools, enforced by the GitHub capability’s configuration, and a guided setup page.
See [Set up review and security agents](https://everruns.com/docs/how-to/set-up-review-and-security-agents/). PR [#3980](https://github.com/everruns/everruns/pull/3980).
---
# MCP Events add session webhooks and triggers on MCP server events
> With the experimental MCP Events flag, MCP clients subscribe to signed session webhooks, and agents wake on events published by their own MCP servers.
Source:
Organizations that turn on the experimental **MCP Events** flag get events in both directions.
* An MCP client calls `events/subscribe` on `/mcp` to receive signed webhooks for `session.completed`, `session.failed` and `session.input_required`. Callbacks must be public HTTPS URLs and are verified before the first event.
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "events/subscribe",
"params": {
"name": "session.completed",
"arguments": {},
"delivery": {
"mode": "webhook",
"url": "https://example.com/hooks/everruns",
"secret": "whsec_"
}
}
}
```
* An agent trigger with `trigger_type: "mcp_event"` subscribes to an event one of the agent’s own MCP servers publishes, such as a tracker announcing new issues, and starts a session when it arrives.
See [MCP](https://everruns.com/docs/features/mcp/) and [Agent triggers](https://everruns.com/docs/features/agent-triggers/). PRs [#3931](https://github.com/everruns/everruns/pull/3931), [#3970](https://github.com/everruns/everruns/pull/3970).
---
# OpenAI structured output and background mode for long reasoning
> OpenAI drivers enforce JSON Schema structured output, and xhigh or max reasoning calls run as background responses that resume after a drop or worker restart.
Source:
A direct model call can require a reply that matches a JSON Schema. Everruns sends it as `text.format` on the OpenAI Responses API and `response_format` on Chat Completions. A driver that cannot enforce the schema refuses the call instead of dropping it. In the Framework:
```rust
use everruns::{Model, OpenAI};
use everruns::llm::ResponseFormat;
use serde_json::json;
let schema = json!({"type": "object", "properties": {"sentiment": {"type": "string"}},
"required": ["sentiment"], "additionalProperties": false});
let answer = Model::new("gpt-6.1-sol", OpenAI::from_env()?)
.completion()
.user("I love it")
.response_format(ResponseFormat::json_schema("sentiment", schema))
.text()
.await?;
```
Calls at `xhigh` and `max` reasoning effort on OpenAI and Azure run as background responses. If the stream drops or the worker restarts, Everruns picks the response back up instead of paying for it twice. A cancelled turn cancels the response, and the `openai/background` driver option overrides the policy.
See [OpenAI](https://everruns.com/docs/providers/openai/). PRs [#3932](https://github.com/everruns/everruns/pull/3932), [#3969](https://github.com/everruns/everruns/pull/3969).
---
# Connect several Slack workspaces per organization
> An organization connects its Slack workspaces once in Settings, and adding an agent to Slack becomes picking a workspace and approving one consent screen.
Source:
An organization administrator connects a Slack workspace once from **Settings > Slack workspaces** by pasting a Slack configuration token. An organization can connect more than one workspace.
Each agent you put in Slack gets its own Slack app. In the agent’s Slack channel editor, choose a workspace if there is more than one and select **Add to Slack**. Everruns creates the app and opens Slack’s consent screen with that workspace selected. The channel then shows **Live in Slack** with an **Open in Slack** link.
See [Connect a Slack workspace](https://everruns.com/docs/capabilities/slack/#connect-a-slack-workspace) and [Publish to Slack](https://everruns.com/docs/how-to/publish-to-slack/). PRs [#3830](https://github.com/everruns/everruns/pull/3830), [#3977](https://github.com/everruns/everruns/pull/3977).
---
# GPT-6.1 Sol, the GPT-6 family and Claude 5.5 models
> Yolop's model menus list GPT-6.1 Sol with a speed setting for service tiers, the rest of the GPT-6 family, Claude Opus 5.5, Sonnet 5.5 and Fable 5.1.
Source:
Yolop’s model suggestions for OpenAI and OpenRouter now include `gpt-6.1-sol`, `gpt-6-sol`, `gpt-6-luna` and `gpt-6-astra`. Anthropic and OpenRouter suggestions add `claude-opus-5-5`, `claude-sonnet-5-5` and `claude-fable-5-1`, with their 1M-context variants where the registry has them.
A new `speed` setting picks OpenAI’s service tier for models that offer one, and a tier the model does not offer is skipped:
```bash
yolop config set speed fast
```
`YOLOP_SPEED` sets it for one run.
See [Providers and models](https://github.com/everruns/yolop#providers-and-models), [#804](https://github.com/everruns/yolop/pull/804) and [#805](https://github.com/everruns/yolop/pull/805).
---
# A2A 1.0 endpoint with task results, streaming and push notifications
> Everruns A2A channels negotiate protocol 1.0, return agent output as task artifacts, support ListTasks and SubscribeToTask, and send push notifications.
Source:
**On the platform:** A2A channels now speak A2A 1.0, chosen by the `A2A-Version` header, with 0.3 still available. Completed tasks carry the agent’s answer as artifacts, so clients such as the `a2a` CLI read the result back. `SendMessage` waits for the result by default; pass `returnImmediately` to follow the task with `GetTask`, `SubscribeToTask` or a push notification. `ListTasks` is supported, and push notification configs receive the full task when it settles.
```bash
CARD="$EVERRUNS/v1/channels/$CHANNEL_ID/a2a/.well-known/agent-card.json"
a2a card get -a "$CARD"
a2a send -a "$CARD" --auth "Bearer $KEY" "Summarize the A2A spec in three bullets."
```
**In code:** the `a2a` feature of `everruns-serve` answers A2A 1.0 for every top-level agent at `POST /v1/e/{agent}/a2a`, the same paths an Everruns A2A endpoint uses.
See [A2A](https://everruns.com/docs/features/a2a/) and [Framework A2A](https://everruns.com/docs/framework/a2a/). PRs [#3999](https://github.com/everruns/everruns/pull/3999), [#4010](https://github.com/everruns/everruns/pull/4010), [#4013](https://github.com/everruns/everruns/pull/4013), [#4014](https://github.com/everruns/everruns/pull/4014).
---
# AG-UI 1.0 endpoint and AG-UI streaming for Framework sessions
> The AG-UI channel moves to protocol 1.0 with interrupts, resume, frontend tools and subagents, and any Framework session can serve AG-UI from your own server.
Source:
**On the platform:** AG-UI channels speak AG-UI 1.0. Clients such as CopilotKit get interrupts and resume for questions and approvals, token usage, frontend tools, subagents and run metadata.
**In code:** the `ag-ui` feature lets any Rust host stream a Framework session to an AG-UI client without the Everruns server. `Session::ag_ui(input)` returns the run as AG-UI 1.0 events, and `ask_user` questions and tool approvals become interrupts. A durable thread store keeps history between runs, and the `ag-ui-axum` feature adds a ready-made route:
```bash
cargo add everruns --features ag-ui-axum
```
See [AG-UI channels](https://everruns.com/docs/features/channels/#ag-ui) and [Serve AG-UI](https://everruns.com/docs/framework/ag-ui/). PRs [#3965](https://github.com/everruns/everruns/pull/3965), [#3967](https://github.com/everruns/everruns/pull/3967), [#3972](https://github.com/everruns/everruns/pull/3972), [#3979](https://github.com/everruns/everruns/pull/3979), [#3976](https://github.com/everruns/everruns/pull/3976), [#3998](https://github.com/everruns/everruns/pull/3998), [#4000](https://github.com/everruns/everruns/pull/4000).
---
# Cloudflare AI Gateway and Vercel AI Gateway model providers
> Agents can run models through Cloudflare AI Gateway or Vercel AI Gateway, and the Cloudflare provider lists Workers AI text-generation models automatically.
Source:

Two gateway providers are available. **Cloudflare** reaches many upstream providers and Cloudflare’s own Workers AI models through one account token and account ID. **Vercel AI Gateway** uses its OpenAI-compatible Responses endpoint and syncs its model list.
The Cloudflare provider lists Workers AI text-generation models with their context window and tool support. Third-party model IDs on Cloudflare are still added by hand, because no endpoint lists what an account can bill.
This also fixes a token dropped from Cloudflare streams when the model emitted a purely numeric token.
See [Cloudflare](https://everruns.com/docs/providers/cloudflare/) and [Vercel](https://everruns.com/docs/providers/vercel/). PRs [#3996](https://github.com/everruns/everruns/pull/3996), [#4021](https://github.com/everruns/everruns/pull/4021).
---
# Everruns 0.34.0: AG-UI 1.0
> PR reviewer and security scanner templates for GitHub, MCP Events with session webhooks and triggers, and AG-UI 1.0 and A2A 1.0 support.
Source:

## Highlights
* **AG-UI 1.0**: A new `everruns-ag-ui` crate carries the AG-UI 1.0 wire types ([#3964](https://github.com/everruns/everruns/pull/3964)), and the AG-UI endpoint moves to protocol 1.0 with interrupts, resume, token usage, frontend tools, subagents and run metadata ([#3965](https://github.com/everruns/everruns/pull/3965), [#3967](https://github.com/everruns/everruns/pull/3967), [#3972](https://github.com/everruns/everruns/pull/3972), [#3979](https://github.com/everruns/everruns/pull/3979)). Any framework session serves AG-UI behind the `ag-ui` feature, with a durable thread store and a ready-made axum handler ([#3976](https://github.com/everruns/everruns/pull/3976), [#3998](https://github.com/everruns/everruns/pull/3998), [#4000](https://github.com/everruns/everruns/pull/4000)), `everruns-serve` exposes an AG-UI channel ([#3978](https://github.com/everruns/everruns/pull/3978)), and a consumer pipeline and SSE client ship with an upstream conformance corpus ([#3982](https://github.com/everruns/everruns/pull/3982)).
* **A2A 1.0**: The A2A endpoint speaks A2A 1.0 and returns task results ([#3999](https://github.com/everruns/everruns/pull/3999)), with ListTasks, SubscribeToTask ([#4010](https://github.com/everruns/everruns/pull/4010)) and push notifications ([#4013](https://github.com/everruns/everruns/pull/4013)); `everruns-serve` serves every agent over A2A behind an `a2a` feature ([#4014](https://github.com/everruns/everruns/pull/4014)).
* **Amazon Bedrock AgentCore hosting**: The new `everruns-serve-agentcore` crate hosts agents on AgentCore Runtime, with Bedrock through the execution role, WebSocket transport and durable approvals ([#3990](https://github.com/everruns/everruns/pull/3990), [#4012](https://github.com/everruns/everruns/pull/4012)).
* **OpenAI Agents API backend**: An opt-in durable OpenAI Agents API backend ([#3957](https://github.com/everruns/everruns/pull/3957)) projects events, usage and cost ([#3961](https://github.com/everruns/everruns/pull/3961)), supports session lifecycle and portability ([#3963](https://github.com/everruns/everruns/pull/3963)), and enforces Everruns policy at tool boundaries ([#3959](https://github.com/everruns/everruns/pull/3959)).
* **Native computer use with hard approval**: Native OpenAI and Anthropic computer tools run behind a hard tool-approval gate ([#3968](https://github.com/everruns/everruns/pull/3968), [#3956](https://github.com/everruns/everruns/pull/3956)).
* **Slack workspaces per organization**: Organizations connect Slack workspaces and pick one per agent ([#3977](https://github.com/everruns/everruns/pull/3977)).
* **Agent version pinning**: Endpoints and triggers can pin a specific agent version ([#3975](https://github.com/everruns/everruns/pull/3975)).
* **More ways to reach models**: Cloudflare and Vercel AI Gateway drivers ([#3996](https://github.com/everruns/everruns/pull/3996)), an opt-in Responses WebSocket transport for OpenAI ([#3981](https://github.com/everruns/everruns/pull/3981)), and OpenAI background responses that re-attach after a worker restart ([#3969](https://github.com/everruns/everruns/pull/3969)).
* **GitHub and MCP triggers**: PR reviewer and security scanner templates on the GitHub App ([#3980](https://github.com/everruns/everruns/pull/3980)), and agents that wake on inbound MCP events ([#3970](https://github.com/everruns/everruns/pull/3970)).
* **`everruns-durable` on crates.io**: The durable workflow engine fires timers, runs child workflows and ships with a bundled PostgreSQL schema ([#3994](https://github.com/everruns/everruns/pull/3994), [#4008](https://github.com/everruns/everruns/pull/4008)).
## In the changelog
* [PR reviewer and security scanner agent templates for GitHub](https://everruns.com/docs/changelog/2026-10-01-github-review-security-templates/)
* [MCP Events add session webhooks and triggers on MCP server events](https://everruns.com/docs/changelog/2026-10-01-mcp-events-webhooks-triggers/)
* [OpenAI structured output and background mode for long reasoning](https://everruns.com/docs/changelog/2026-10-01-openai-structured-output-background/)
* [Connect several Slack workspaces per organization](https://everruns.com/docs/changelog/2026-10-01-slack-workspaces-per-organization/)
* [A2A 1.0 endpoint with task results, streaming and push notifications](https://everruns.com/docs/changelog/2026-10-02-a2a-1-0-protocol/)
* [AG-UI 1.0 endpoint and AG-UI streaming for Framework sessions](https://everruns.com/docs/changelog/2026-10-02-ag-ui-1-0-protocol/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.34.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0340---2026-10-02) for every pull request in this version.
---
# Everruns 0.34.1 release
> The everruns-durable workflow engine is published on crates.io, and Everruns serve apps can be hosted on Amazon Bedrock AgentCore Runtime.
Source:

## In the changelog
* [everruns-durable workflow engine published on crates.io](https://everruns.com/docs/changelog/2026-10-02-everruns-durable-crate-published/)
* [Host Everruns serve apps on Amazon Bedrock AgentCore Runtime](https://everruns.com/docs/changelog/2026-10-02-serve-on-bedrock-agentcore/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.34.1) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0341---2026-10-02) for every pull request in this version.
---
# Everruns 0.34.2: Execution kernel in a JavaScript isolate
> Cloudflare AI Gateway and Vercel AI Gateway become model providers, Workers AI models are discoverable, and the kernel runs in a JavaScript isolate.
Source:

## Highlights
* **Execution kernel in a JavaScript isolate**: the engine’s execution kernel runs inside a JavaScript isolate, with a celld engine cell ([#4024](https://github.com/everruns/everruns/pull/4024))
* **Workers AI model discovery**: the Cloudflare driver lists Workers AI models, and Chat Completions streams keep tokens sent as JSON numbers ([#4021](https://github.com/everruns/everruns/pull/4021))
## In the changelog
* [Cloudflare AI Gateway and Vercel AI Gateway model providers](https://everruns.com/docs/changelog/2026-10-02-cloudflare-vercel-ai-gateway/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.34.2) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0342---2026-10-02) for every pull request in this version.
---
# everruns-durable workflow engine published on crates.io
> The durable workflow engine behind Everruns ships as its own crate with timers, child workflows and a bundled idempotent PostgreSQL schema you can migrate.
Source:
`everruns-durable`, the generic durable workflow engine with no agent concepts, is now published on crates.io.
It ships its own idempotent PostgreSQL schema through `PostgresWorkflowEventStore::migrate` and `SCHEMA_SQL`, tested to match what the server migrations build. Timers and child workflows run as engine tasks: `ActivityOptions::start_delay` holds a task until it is due, and parents get `on_child_workflow_completed` and `on_child_workflow_failed`.
```bash
cargo add everruns-durable
```
See [Durable execution](https://everruns.com/docs/explanation/durable-execution/). PRs [#3994](https://github.com/everruns/everruns/pull/3994), [#4008](https://github.com/everruns/everruns/pull/4008).
---
# Everruns SDKs default to the Everruns Cloud API URL
> An Everruns SDK client created with only EVERRUNS_API_KEY now reaches Everruns Cloud instead of a placeholder URL; EVERRUNS_API_URL still overrides it.
Source:
The Python, TypeScript and Rust SDKs defaulted to the placeholder `https://custom.example.com/api`, so a client created with only `EVERRUNS_API_KEY` could not reach anything. The default is now `https://app.everruns.com/api`.
Self-hosted deployments keep pointing the client at their server with `EVERRUNS_API_URL`:
```bash
export EVERRUNS_API_URL=https://everruns.example.internal/api
```
See [SDKs](https://everruns.com/docs/features/sdk/) and [#114](https://github.com/everruns/sdk/pull/114).
---
# Host Everruns serve apps on Amazon Bedrock AgentCore Runtime
> The experimental everruns-serve-agentcore crate runs a serve app on AgentCore Runtime with AG-UI streaming, a WebSocket transport and Bedrock models.
Source:

The new `everruns-serve-agentcore` crate hosts a serve app on Amazon Bedrock AgentCore Runtime. It answers `GET /ping` and `POST /invocations`, which take an AG-UI `RunAgentInput` or `{"prompt": ...}` and stream AG-UI events, plus a `/ws` WebSocket transport, with serve’s `/v1` API alongside.
The session log and agent workspace live on AgentCore session storage. Bedrock models use the runtime’s execution role when `AWS_REGION` is set, and parked approvals and `ask_user` questions survive a restart.
See [Serve on AgentCore](https://everruns.com/docs/framework/serve-agentcore/). PRs [#3990](https://github.com/everruns/everruns/pull/3990), [#4012](https://github.com/everruns/everruns/pull/4012).
---
# ChatGPT sign-in through the open-source route
> Yolop's codex provider now signs in with OpenAI's open-source Sign in with ChatGPT by default and runs turns on the public Responses API on your ChatGPT plan.
Source:

A browser sign-in for `--provider codex` now uses OpenAI’s open-source Sign in with ChatGPT by default. Yolop registers its own client for your account on the first sign-in and runs turns on the public Responses API, still billed to your ChatGPT plan. It is a preview route, so a few request options (output token cap, temperature, `speed`) do not apply.
To keep the Codex login, set the route and sign in again:
```bash
yolop config set chatgpt_sign_in codex
```
`YOLOP_CHATGPT_SIGN_IN=codex` does the same. A device login uses the Codex route unless `open-source` is set.
See [Providers and models](https://github.com/everruns/yolop#providers-and-models), [#806](https://github.com/everruns/yolop/pull/806) and [#808](https://github.com/everruns/yolop/pull/808).
---
# Approval history in chat and a new Approvals audit page
> Chats show approval requests and recorded consent as clear entries, and organization admins get an Approvals page that filters approvals across sessions.
Source:
Approval activity no longer looks like ordinary tool calls. In a chat, `request_approval` and `record_approval` show as approval entries with the action, the approver’s name, and a link to the message that gave consent.
Organization admins and owners also get an **Approvals** page under **Operational**. It filters all activity, recorded approvals, or requests, and links each entry back to its session. It reads the existing approval audit records, so earlier history appears too.
See [Soft approval](https://everruns.com/docs/capabilities/soft-approval/). PR [#4110](https://github.com/everruns/everruns/pull/4110).
---
# Chat threads keep conversations and ongoing work beside Chat
> Organizations can opt into Chat threads, a panel beside the permanent Chat for searching conversations, following background work and resolving threads.
Source:
Organization owners and admins can turn on **Chat threads** in **Settings > Features**. With it on, the sidebar holds only Chat, and a **Threads** panel beside the permanent conversation lists your other conversations and the work running in the background.
Create a **New thread**, search conversations, and open work from its card, where you can answer pending input or stop it. **Resolve thread** puts a conversation away without losing history, and **Reopen thread** continues it. The panel becomes a drawer on mobile. Turning the feature off restores the previous Chat and keeps conversations and their URLs.
See [Chat and Threads](https://everruns.com/docs/features/ui/#chat-and-threads). PR [#4146](https://github.com/everruns/everruns/pull/4146).
---
# Connect a personal ChatGPT plan as a model provider
> Self-hosted users can sign in with ChatGPT from provider settings to run on their own ChatGPT plan, with account status, reconnect controls and private models.
Source:

## Update 2026-10-17
ChatGPT Plan was [removed from Platform setup](https://everruns.com/docs/changelog/2026-10-17-chatgpt-plan-ui-removal/). The framework driver remains available.
ChatGPT now appears in the provider grid. Open **Models > Providers > Connect provider > ChatGPT > Sign in with ChatGPT**, approve plan use in ChatGPT, and return to Everruns. The connection belongs to you, not the organization: its models are available only to you, and the detail page shows account status, quota guidance, and reconnect and disconnect controls.
Self-hosted deployments use OpenAI’s Sign in with ChatGPT for open-source applications. The ChatGPT plan transport is now part of `everruns-drivers`.
See [ChatGPT plan](https://everruns.com/docs/features/chatgpt/). PR [#4136](https://github.com/everruns/everruns/pull/4136).
---
# Computer use capability lets agents operate a browser screen
> The experimental computer_use capability drives a Browserless page with clicks, typing and screenshots, using native OpenAI or Anthropic tools when available.
Source:
The experimental `computer_use` capability lets an agent work a screen the way a person does. Its `computer` tool takes an action (click, type, key, scroll, drag, navigate or screenshot) and returns a screenshot, so any image-capable model can use it. The screen is the session’s Browserless browser.
On models with a native computer tool, the agent uses the provider’s tool instead; set `native_tools` to `false` to always use the function tool. `max_actions_per_session` caps actions per session, and navigation passes the egress guard. No call waits for a person by default: add `soft_approval` to record confirmations, or `tool_approval` to approve every call.
```toml
capabilities = [
{ ref = "computer_use", config = { native_tools = false, max_actions_per_session = 100 } },
]
```
See [Computer use](https://everruns.com/docs/capabilities/computer-use/). PRs [#3922](https://github.com/everruns/everruns/pull/3922), [#3968](https://github.com/everruns/everruns/pull/3968), [#4064](https://github.com/everruns/everruns/pull/4064).
---
# Conversation becomes the default harness and Generic is deprecated
> Built-in harnesses form progressive presets from Base to Worker, Conversation is the new default, and agents that inherited Generic are pinned to it on upgrade.
Source:
Built-in harnesses now grow in steps: Base, Conversation, Worker Base and Worker. Conversation is the platform default. Web access, secrets, scheduling, citations and memory mounts stay opt-in.
Generic is deprecated but unchanged. Selectors hide it behind **Show deprecated**. On upgrade, agents that inherited Generic as the default are pinned to it, so their behavior does not change. A custom harness whose name collides with a new built-in keeps its ID and definition, but its slug moves to `-custom`; update callers that look it up by name:
```bash
# Before: a custom harness named "conversation"
everruns agents update agent_xxx --harness conversation
# After
everruns agents update agent_xxx --harness conversation-custom
```
**In code:** `Harness::base()`, `conversation()`, `worker_base()` and `worker()` build the same presets.
See [Harnesses](https://everruns.com/docs/features/harnesses/) and the [upgrade notes](https://everruns.com/docs/framework/upgrade-notes/). PR [#4133](https://github.com/everruns/everruns/pull/4133).
---
# Everruns 0.35.0: Safer sandbox tools
> A computer use capability lets agents operate a browser screen, sandbox tools are safer, questions are answered in chat, and every org gets Platform Chat.
Source:

## Highlights
* **Safer sandbox tools**: HTTP egress fails closed on empty DNS pins, and forged session secrets cannot redirect Docker tools ([#4058](https://github.com/everruns/everruns/pull/4058), [#4061](https://github.com/everruns/everruns/pull/4061)).
* **Interactive questions in chat**: AskUser runs through interactive turns so agents can ask for input while working ([#4044](https://github.com/everruns/everruns/pull/4044)).
* **Platform Chat for every organization**: the shell-powered Platform Chat harness becomes the default for every organization ([#4060](https://github.com/everruns/everruns/pull/4060)).
## In the changelog
* [Computer use capability lets agents operate a browser screen](https://everruns.com/docs/changelog/2026-10-03-computer-use-capability/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.35.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0350---2026-10-03) for every pull request in this version.
---
# Everruns 0.36.0: Choose an Agent Environment
> Agents can choose their environment, a broken Slack installation can be repaired in place, and session storage gets its own permissions.
Source:

## Highlights
* **Choose an Agent Environment**: Author named Bashkit or Daytona profiles and select the profile when starting a Chat or Playground session.
* **Slack installation repair**: Add to Slack works for draft endpoints, requests declared bot scopes, and shows compact errors with retry.
* **Session storage permissions**: Listing stored values or secret names requires session view; denied callers receive no private data.
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.36.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0360---2026-10-03) for every pull request in this version.
---
# Everruns 0.37.0: Approval history in context
> Approval history shows in chat and on a new Approvals audit page, agent channels are visible at a glance, and navigation keeps your place.
Source:

## Highlights
* **Approval history in context**: Chats show approval requests and recorded consent; organization admins and owners can filter existing spoken-consent audit history ([#4110](https://github.com/everruns/everruns/pull/4110)).
* **Agent channels at a glance**: Compact agent cards show transport and live, draft or disabled status, with links to Integrations ([#4109](https://github.com/everruns/everruns/pull/4109)).
* **Navigation keeps your place**: Sidebar selection updates as navigation starts, and agent tabs survive refresh through their URL ([#4108](https://github.com/everruns/everruns/pull/4108), [#4111](https://github.com/everruns/everruns/pull/4111)).
* **Slack installation returns to setup**: Valid OAuth callbacks return to the owning agent’s endpoint editor; invalid callbacks use the agents page ([#4107](https://github.com/everruns/everruns/pull/4107)).
## In the changelog
* [Approval history in chat and a new Approvals audit page](https://everruns.com/docs/changelog/2026-10-03-approval-history-audit-page/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.37.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0370---2026-10-03) for every pull request in this version.
---
# Everruns 0.38.0: Personal ChatGPT plan connections
> Connect a personal ChatGPT plan as a model provider, Conversation becomes the default harness with Generic deprecated, and custom hosts get stock subagents.
Source:

## Highlights
* **Personal ChatGPT plan connections**: Self-hosted deployments can enable personal account sign-in and discover its available models; connections stay scoped to the signed-in user ([#4136](https://github.com/everruns/everruns/pull/4136)).
* **Stock subagents for custom hosts**: Library hosts can run the stock subagent and handoff tools through a store of portable runtime definitions, without constructing control-plane records ([#4078](https://github.com/everruns/everruns/pull/4078)).
## In the changelog
* [Connect a personal ChatGPT plan as a model provider](https://everruns.com/docs/changelog/2026-10-03-chatgpt-plan-provider/)
* [Conversation becomes the default harness and Generic is deprecated](https://everruns.com/docs/changelog/2026-10-03-conversation-default-harness/)
* [Feature flag grade preview renamed to internal for operators](https://everruns.com/docs/changelog/2026-10-03-feature-grades-preview-internal/)
* [Playground for testing agents, and Chat as one permanent conversation](https://everruns.com/docs/changelog/2026-10-03-playground-agent-testing/)
Before upgrading, read the breaking changes: [Conversation becomes the default harness and Generic is deprecated](https://everruns.com/docs/changelog/2026-10-03-conversation-default-harness/), [Feature flag grade preview renamed to internal for operators](https://everruns.com/docs/changelog/2026-10-03-feature-grades-preview-internal/).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.38.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0380---2026-10-03) for every pull request in this version.
---
# Feature flag grade preview renamed to internal for operators
> Self-hosted operators must replace FEATURE_*=preview with internal, as rollout grades now set availability, organization defaults and who may enable a feature.
Source:
Each `FEATURE_` variable now takes a rollout grade: `dev`, `internal`, `adoption`, `prod` or `off`. `internal` features stay off until a platform operator enables them for an organization, `adoption` features are opt-in for organization owners and admins, and `prod` features are on by default.
Upgrade your configuration:
* `FEATURE_*=preview` becomes `internal`.
* `true` becomes `adoption` to keep organization opt-in, and `false` becomes `off`.
```bash
# Before
FEATURE_PUBLIC_CHAT=preview
# After
FEATURE_PUBLIC_CHAT=internal
```
Invalid values, including the old ones, disable the feature. The separate `DEPLOYMENT_GRADE=preview` is unchanged.
See [Environment variables](https://everruns.com/docs/sre/environment-variables/). PRs [#4115](https://github.com/everruns/everruns/pull/4115), [#4137](https://github.com/everruns/everruns/pull/4137).
---
# Playground for testing agents, and Chat as one permanent conversation
> Agent testing moves to a shared Playground where members pick the agent and the user it runs as, while Chat becomes your permanent Platform Chat conversation.
Source:
**Playground** is where an organization tests its agents. Members start a conversation as their linked virtual user; owners and admins can pick another active end user. Conversations are shared across the organization, can be searched, filtered by agent or user, and archived, and each links to its session. On an agent page, **Test in Playground** opens setup with that agent selected.
**Chat** now always uses the managed Platform Chat agent. The sidebar opens your permanent conversation, **New chat** starts a side conversation, and **All chats** lists only your personal conversations. Platform Chat runs on the shell-based implementation for every organization.
See [Chat and Threads](https://everruns.com/docs/features/ui/#chat-and-threads) and [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/). PRs [#4060](https://github.com/everruns/everruns/pull/4060), [#4076](https://github.com/everruns/everruns/pull/4076), [#4121](https://github.com/everruns/everruns/pull/4121), [#4129](https://github.com/everruns/everruns/pull/4129).
## Update 2026-10-24
Setup now supports search. **Test in Playground** opens normal setup; its existing **Dry run** checkbox selects simulated actions. See [Agent actions and searchable Playground setup](https://everruns.com/docs/changelog/2026-10-24-agent-actions-playground-search/).
---
# Slack permission issues appear in Health with recovery steps
> Slack installations missing a permission or credential raise a persistent issue in Settings > Health, with guided recovery, per-user snoozing and rechecks.
Source:
Adding scopes to new Slack app manifests does not repair bots that are already installed. Everruns now checks existing Slack installations and records one issue for each missing permission or credential problem.
Review issues in **Settings > Health**, beside the channel’s settings, and under **Action required** in the notification menu. Managed apps get a repair that extends the app’s manifest before Slack asks for consent again; manually configured apps get scope, reinstall and token guidance. Reading or snoozing an issue keeps it active until a fresh check passes. From the CLI, use [`everruns health-issues`](https://everruns.com/docs/reference/cli/health-issues/).
See [Resolve installation issues](https://everruns.com/docs/how-to/publish-to-slack/#resolve-installation-issues). PR [#4134](https://github.com/everruns/everruns/pull/4134).
---
# Everruns 0.39.0 release
> Chat threads keep conversations and ongoing work beside Chat, and Slack permission problems appear in Health with steps to recover.
Source:

## In the changelog
* [Chat threads keep conversations and ongoing work beside Chat](https://everruns.com/docs/changelog/2026-10-03-chat-threads-workspace/)
* [Slack permission issues appear in Health with recovery steps](https://everruns.com/docs/changelog/2026-10-03-slack-health-issues-recovery/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.39.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0390---2026-10-04) for every pull request in this version.
---
# Everruns 0.40.0 release
> Engine, host, builtins, MCP and AG-UI become everruns-core features, operators can enroll adoption features, and the MCP card reports the version.
Source:

Everruns 0.40.0 is a maintenance release with fixes and internal changes and no user-facing entry of its own.
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.40.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0400---2026-10-04) for every pull request in this version.
---
# Everruns 0.41.0 release
> Framework crates are consolidated into everruns-core and the everruns facade, and the deprecated forwarding shims are removed. Read the upgrade notes.
Source:

## In the changelog
* [Framework crates consolidated into everruns-core and the facade](https://everruns.com/docs/changelog/2026-10-04-framework-crates-consolidated/)
Before upgrading, read the breaking changes: [Framework crates consolidated into everruns-core and the facade](https://everruns.com/docs/changelog/2026-10-04-framework-crates-consolidated/).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.41.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0410---2026-10-04) for every pull request in this version.
---
# Framework crates consolidated into everruns-core and the facade
> Engine, host, builtins, MCP, AG-UI, driver and contract crates merged into fewer packages, so Framework users update dependencies and imports when upgrading.
Source:
The published crate set shrank to 37. Each old crate shipped one deprecated forwarding release before removal:
* Vendor driver crates moved into `everruns-drivers`, one feature per vendor (0.35).
* Provider, capability and model-profile APIs moved into `everruns-contracts` (0.37).
* `everruns-platform` became `everruns-capabilities` (0.39).
* Engine, host, builtins, MCP and AG-UI became `everruns-core` features (0.40), and their shims were removed in 0.41.
```toml
everruns-drivers = { version = "0.41", features = ["openai"] }
```
No capability was removed, and published shim versions are not yanked.
See the [upgrade notes](https://everruns.com/docs/framework/upgrade-notes/). PRs [#4035](https://github.com/everruns/everruns/pull/4035), [#4077](https://github.com/everruns/everruns/pull/4077), [#4081](https://github.com/everruns/everruns/pull/4081), [#4083](https://github.com/everruns/everruns/pull/4083), [#4084](https://github.com/everruns/everruns/pull/4084).
---
# Yolop 0.19.0 release
> GPT-6.1 Sol, the GPT-6 family and Claude 5.5 models are available, and ChatGPT sign-in goes through the open-source route.
Source:
## Highlights
* Move Yolop to the published Everruns 0.41 crate family and remove direct dependencies on retired shims while preserving runtime capabilities.
* Add GPT-6.1 speed tiers, GPT-6 and Claude 5.x model options, and open-source Sign in with ChatGPT for the Codex provider.
* Strengthen approval, extension, filesystem, image, and workspace boundaries.
* Improve ACP, TUI, and runtime behavior, including retries for expired response chains.
## In the changelog
* [GPT-6.1 Sol, the GPT-6 family and Claude 5.5 models](https://everruns.com/docs/changelog/2026-10-01-yolop-gpt-6-claude-5-5-models/)
* [ChatGPT sign-in through the open-source route](https://everruns.com/docs/changelog/2026-10-02-yolop-open-source-chatgpt-sign-in/)
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.19.0) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0190---2026-10-04) for every pull request in this version.
---
# Agents get avatars from presets or an uploaded image
> Give an agent an avatar from 25 curated presets or a cropped upload, and it shows in the console, on the A2A Agent Card and as the Slack app icon.
Source:
**In the UI:** open an agent’s **Branding** sheet. Drop a PNG, JPEG, GIF or WebP image on the avatar and position the square crop, or use **Choose preset** to search 25 curated avatars and save one with **Use avatar**.
**In code:** `PUT /v1/agents/{agent_id}/avatar` uploads an image as the multipart field `file`, and `PUT /v1/agents/{agent_id}/avatar/preset` selects a preset. The agent’s `avatar` field lists public, immutable image URLs. The A2A Agent Card carries it as `iconUrl`, and one-click Slack apps use it as their icon.
See [Web UI](https://everruns.com/docs/features/ui/) and [#4198](https://github.com/everruns/everruns/pull/4198), [#4220](https://github.com/everruns/everruns/pull/4220).
---
# Sandbox Templates and a Sandboxes fleet view
> Reusable Sandbox Templates with immutable revisions decide where an agent's commands run, and a fleet page lists every sandbox in the organization.
Source:
A Sandbox Template names where an agent’s commands run: the Bashkit Virtual Workspace or a managed provider such as Daytona or E2B, with its credential source, durability and lifecycle. Editing a template creates a new immutable revision, and existing sessions keep the revision they started with. An agent’s sandbox policy decides whether a session can pick or override the template.
**In the UI:** open **Sandbox Templates** and select **New Sandbox Template**. The **Sandboxes** page lists every sandbox across providers and states, with a lifecycle timeline, a **Needs attention** filter and pause, resume and delete in the detail drawer. Both sit in the collapsible **Sandboxes** sidebar group.
**In code:** `GET /v1/sandboxes` lists the fleet; `/stats` and `/timeline` back the page.
See [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/) and [#4199](https://github.com/everruns/everruns/pull/4199), [#4246](https://github.com/everruns/everruns/pull/4246), [#4262](https://github.com/everruns/everruns/pull/4262).
---
# Agent versions are retired in favor of change history
> Agent publishing, default versions and version pins are removed; channels, triggers and sessions now run the agent's current configuration instead.
Source:
Agent versions, with publishing, semver, a default version and version pins, are gone. A migration copies every saved version into the agent’s change history as a revision. Channels, triggers, apps and session participants now run the agent’s current configuration, and each new session records the `agent_revision` it started on.
Version endpoints, CLI verbs and fields such as `agent_version_id` and `default_version_id` are removed. To go back to an earlier configuration, use **History → Restore this point** on the agent page or `everruns history restore`:
```bash
# Before
everruns agents versions list --agent agent_01h9
# After
everruns history list agent_01h9
everruns history restore agent_01h9 --revision 7 --reason "Back to the pre-launch prompt"
```
See [Agent revisions replace agent versions](https://everruns.com/docs/features/change-history/#agent-revisions-replace-agent-versions) and [#4312](https://github.com/everruns/everruns/pull/4312).
---
# Sessions start with a default root filesystem
> Every Bashkit session now has read-only /etc, /proc and /usr/bin entries, so scripts that read os-release or cpuinfo or call /usr/bin/env find what they expect.
Source:
Every `Bash` session now starts with a synthetic root filesystem: a read-only `/etc` (`os-release`, `passwd`, `group`, `hostname`, `hosts`, `shells`, `timezone`), static `/proc` files (`cpuinfo`, `meminfo`, `version`, `loadavg`), `/root` and `/dev/zero`. `/bin` and `/usr/bin` list the builtins, so a `/usr/bin/env` shebang runs the builtin and `type`, `which` and `command -v` report `/usr/bin/NAME`.
Session files take precedence, and the layer uses no quota and is never snapshotted. Turn it off with:
```rust
let bash = Bash::builder().rootfs(false).build();
```
See [Filesystem](https://github.com/everruns/bashkit/blob/main/docs/filesystem.md) and [#2525](https://github.com/everruns/bashkit/pull/2525).
---
# Change history with reasons and restore for every entity
> Every configuration change records who made it, through which surface and why, with numbered revisions you can compare and restore from the History menu.
Source:
Every mutating command now records one history entry: the entity, the action, the changed fields, the actor, the surface, the session and an optional reason. Configuration changes also create numbered revisions you can compare and restore. Secrets appear only as changed or unchanged.
**In the UI:** open the **⋯** menu on an entity’s page. **History** lists changes with their reasons, and **Restore this point** asks for a reason. **Manager notes** holds context agents read before changing the entity.
**In code:** pass a reason with `--reason` on the CLI or the `Everruns-Change-Reason` header, and read history with `everruns history list`, `show`, `diff` and `restore`.
See [Change history](https://everruns.com/docs/features/change-history/), the [history CLI reference](https://everruns.com/docs/reference/cli/history/) and [#4228](https://github.com/everruns/everruns/pull/4228), [#4264](https://github.com/everruns/everruns/pull/4264), [#4272](https://github.com/everruns/everruns/pull/4272).
---
# Everruns 0.42.0 release
> Agents get avatars from presets or an uploaded image, sandboxes can start from templates, and a Sandboxes fleet view shows every running sandbox.
Source:

## In the changelog
* [Agents get avatars from presets or an uploaded image](https://everruns.com/docs/changelog/2026-10-05-agent-avatars/)
* [Sandbox Templates and a Sandboxes fleet view](https://everruns.com/docs/changelog/2026-10-05-sandbox-templates-fleet-view/)
* [Change history with reasons and restore for every entity](https://everruns.com/docs/changelog/2026-10-06-change-history-with-reasons/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.42.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0420---2026-10-06) for every pull request in this version.
---
# A2A channels serve HTTP+JSON and the PACT profiles
> Every A2A channel also answers the A2A 1.0 HTTP+JSON binding, and a channel can accept personal agents through the PACT Identity and Delegated profiles.
Source:
Every A2A channel URL now also serves the A2A 1.0 HTTP+JSON binding: `message:send`, `message:stream`, task get, list, cancel and subscribe, push notification configs and the extended card. The Agent Card lists the new interface, and auth, rate limits and signing are shared with JSON-RPC.
```bash
curl -sS -X POST "$EVERRUNS/v1/channels/$CHANNEL_ID/a2a/message:send" \
-H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/a2a+json' \
-d '{"message": {"role": "ROLE_USER", "messageId": "msg-1",
"parts": [{"text": "Summarize the A2A spec in three bullets."}]}}'
```
Add `pact` to an A2A channel’s config to accept personal agents under the PACT 1.0 Identity profile, with each conversation bound to the calling user. With delegation on, the channel runs an OAuth device-code sign-in so a personal agent can act as a company user through the configured MCP server, and delegated replies carry a signed receipt.
See [A2A](https://everruns.com/docs/features/a2a/), [Personal agents (PACT)](https://everruns.com/docs/features/a2a/#personal-agents-pact) and [#4322](https://github.com/everruns/everruns/pull/4322), [#4328](https://github.com/everruns/everruns/pull/4328), [#4350](https://github.com/everruns/everruns/pull/4350), [#4352](https://github.com/everruns/everruns/pull/4352).
Watch PACT account consent, a refused cancellation, and a signed receipt in a local walkthrough with synthetic store data:
[PACT account consent and signed receipts in Everruns](https://www.youtube-nocookie.com/embed/9FZiYv5PsWY?rel=0)
[Watch on YouTube](https://www.youtube.com/watch?v=9FZiYv5PsWY).
---
# Sign in to channels and Public Chat with AgentID
> A2A, AG-UI and Public Chat channels accept AgentID tokens, Public Chat shows Continue with AgentID, and agents can sign in to apps with their AgentMail inbox.
Source:
Channels can accept [AgentID](https://www.agentid.com), a sign-in for AI agents. A2A, AG-UI and Public Chat channels take AgentID bearer tokens, and each agent that signs in becomes its own end user in the organization.
To let agents sign in to a Public Chat channel, register an AgentID client, set `AGENTID_CLIENT_ID` and `AGENTID_CLIENT_SECRET` on the server, and set the channel’s sign-in to **AgentID**. The chat page then shows **Continue with AgentID**.
```bash
# Server environment; the AgentID client's redirect URI is
# {API base URL}/v1/agentid/callback
AGENTID_CLIENT_ID=your-agentid-client-id
AGENTID_CLIENT_SECRET=your-agentid-client-secret
```
Going the other way, the experimental **AgentID sign-in** capability gives an agent the `agentid_authorize` tool, which approves an app’s AgentID sign-in for the agent’s AgentMail inbox.
See [Sign in with AgentID](https://everruns.com/docs/features/channels/#sign-in-with-agentid), [AgentID](https://everruns.com/docs/integrations/agentid/) and [#4326](https://github.com/everruns/everruns/pull/4326), [#4334](https://github.com/everruns/everruns/pull/4334), [#4337](https://github.com/everruns/everruns/pull/4337).
---
# awk is rewritten to follow gawk 5.2
> Bashkit's awk, gawk, mawk and nawk builtins now target gawk 5.2, with POSIX awk, common gawk extensions, and system() and pipes through the sandbox shell.
Source:
Bashkit’s `awk` was rewritten to target gawk 5.2, Debian’s `awk`. It covers POSIX awk plus the gawk extensions scripts commonly use; `system()`, output pipes and `cmd | getline` run through the sandbox shell. Number output, `printf`, the `rand()` sequence, leftmost-longest regex matching, diagnostics and options follow gawk. `gawk`, `mawk` and `nawk` run the same builtin.
```bash
echo "2026-10-07" | gawk '{ print gensub(/([0-9]+)-([0-9]+)-([0-9]+)/, "\\3.\\2.\\1", "g") }'
echo hello | awk '{ "echo world" | getline w; print $0, w }'
awk 'BEGIN { system("echo from the sandbox shell") }'
```
On Bashkit’s Debian-oracle awk tests, the pass rate went from 51.5% to 93.8%. Resource caps still apply, and `split` and record splitting count against the memory cap.
See the [compatibility scorecard](https://github.com/everruns/bashkit/blob/main/crates/bashkit/docs/compatibility.md), [#2583](https://github.com/everruns/bashkit/pull/2583) and [#2561](https://github.com/everruns/bashkit/pull/2561).
---
# Background jobs and pipeline stages run concurrently
> Bashkit now runs cmd & jobs alongside the shell and streams pipeline stages into each other with SIGPIPE, adding jobs, wait -n, kill and related builtins.
Source:
`cmd &` now runs concurrently with the shell, so `sleep 1 & sleep 1 & wait` takes one second instead of two. New `jobs`, `disown`, `fg`, `bg`, `ps`, `pgrep` and `pkill` builtins, `wait -n`, `wait -p VAR` and jobspecs work, and `kill` ends jobs. Live jobs are capped by `ExecutionLimits::max_background_jobs`.
Pipeline stages that run shell code, such as loops, groups and functions, now stream into each other through a pipe with backpressure. When a reader stops, the writer gets SIGPIPE and exits with 141, so `while :; do echo x; done | head -n 2` prints two lines as in bash.
```bash
sleep 1 & sleep 1 & wait # about one second
sleep 5 & jobs; kill %1
while :; do echo x; done | head -n 2
```
Both are on by default; `BashBuilder::concurrent_jobs(false)` turns them off.
See the [compatibility scorecard](https://github.com/everruns/bashkit/blob/main/crates/bashkit/docs/compatibility.md), [#2527](https://github.com/everruns/bashkit/pull/2527) and [#2541](https://github.com/everruns/bashkit/pull/2541).
---
# Real CPython 3.14 runs as python3 in a sandbox
> An opt-in cpython feature makes python3 in Bashkit real CPython 3.14 in a WebAssembly sandbox, with the stdlib and HTTP through Bashkit's network policy.
Source:
With the off-by-default `cpython` feature, `python` and `python3` in Bashkit are CPython 3.14: the full language, the pure-Python standard library plus modules such as `json`, `re`, `sqlite3` and `asyncio`, and CPython’s command line (`-c`, `-m`, files, stdin). Before, `python3` was Monty, a Python subset.
```rust
let mut bash = Bash::builder().cpython().build();
```
CPython runs as a WebAssembly guest on wasmtime, started per call from a precompiled snapshot, with limits on time, memory, output and the VFS. `urllib.request`, `http.client` and Bashkit’s own `requests` and `httpx` modules make HTTP calls through the same allowlist and credential injection as `curl`.
See [CPython](https://github.com/everruns/bashkit/blob/main/crates/bashkit/docs/cpython.md), [#2515](https://github.com/everruns/bashkit/pull/2515), [#2568](https://github.com/everruns/bashkit/pull/2568) and [#2572](https://github.com/everruns/bashkit/pull/2572).
---
# A make builtin and 21 everyday commands
> Bashkit gains a GNU make 4.3 subset that runs recipes in the sandbox shell, plus nohup, flock, getconf, free and 17 other commands that scripts often call.
Source:
`make` is now a builtin: a GNU make 4.3 subset with every assignment flavor, conditionals, `define`, `include`, explicit, pattern and suffix rules, functions such as `foreach`, `call`, `wildcard` and `shell`, and the options `-f -C -n -k -q -s -i -B -e -j` (`-j` runs sequentially). Recipes run through the sandbox shell, and messages and exit codes follow GNU make.
```bash
printf 'greet:\n\t@echo hello from $(NAME)\n' > Makefile
make greet NAME=bashkit # hello from bashkit
make -n greet NAME=bashkit # prints the recipe without running it
```
Twenty-one commands that used to report “command not found” are added: `arch`, `sum`, `shasum`, `egrep`, `fgrep`, `link`, `unlink`, `chgrp`, `nohup`, `nice`, `flock`, `getconf`, `tty`, `sync`, `hostid`, `groups`, `logname`, `users`, `who`, `uptime` and `free`. `cmd N>file` with N of 3 or more now opens only fd N.
See the [compatibility scorecard](https://github.com/everruns/bashkit/blob/main/crates/bashkit/docs/compatibility.md), [#2551](https://github.com/everruns/bashkit/pull/2551) and [#2546](https://github.com/everruns/bashkit/pull/2546).
---
# An in-process terminal and TerminalTool for agents
> A terminal feature runs a Bashkit session in an in-memory terminal with vi, less and more, and TerminalTool lets an LLM agent type keys across named tabs.
Source:
The off-by-default `terminal` feature wraps a Bashkit session in an in-memory terminal with `vi` and interactive `less` and `more`. `TerminalTool` turns it into one agent tool call: the model sends keys in Vim notation and gets back the screen, the activity (`prompt`, `continuation`, `running` or `exited`) and the commands that finished.
```rust
let mut tool = TerminalTool::new(Bash::builder());
let out = tool.call(json!({"input": "vi /tmp/n.txt"})).await?;
```
`TerminalTool::with_sessions` adds up to 8 named tabs that share the filesystem and keep running in the background. Python (`bashkit.Terminal`) and Node (`Terminal` in `@everruns/bashkit`) expose the same calls.
See [Terminal](https://github.com/everruns/bashkit/blob/main/docs/terminal.md), [#2509](https://github.com/everruns/bashkit/pull/2509), [#2518](https://github.com/everruns/bashkit/pull/2518), [#2521](https://github.com/everruns/bashkit/pull/2521), [#2523](https://github.com/everruns/bashkit/pull/2523) and [#2548](https://github.com/everruns/bashkit/pull/2548).
---
# Claude Haiku 5.5 model added for Anthropic providers
> The claude-haiku-5-5 model and its 1M-context twin are seeded as an enabled favorite, with adaptive thinking, tool search and the computer toolset.
Source:
Anthropic providers now offer `claude-haiku-5-5` and its `[1m]` long-context twin. The model is seeded as an enabled favorite, so it appears in model pickers without extra setup.
Its profile has tiered pricing, up to 128K output tokens, always-on adaptive thinking with medium effort by default, preserved thinking, tool search and the computer toolset. Select it in an agent’s model picker or reference `claude-haiku-5-5` wherever a model ID is accepted, such as the compaction summarization model:
```json
{
"ref": "compaction",
"config": {
"strategy": "summarization",
"summarization": {
"model": "claude-haiku-5-5"
}
}
}
```
See [Anthropic](https://everruns.com/docs/providers/anthropic/) and [#4359](https://github.com/everruns/everruns/pull/4359).
---
# Everruns 0.43.0 release
> The 18 deprecated integration shim crates are removed after their forwarding release; integrations stay available from everruns-integrations.
Source:
Everruns 0.43.0 is a maintenance release with fixes and internal changes and no user-facing entry of its own.
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.43.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0430---2026-10-07) for every pull request in this version.
---
# History, completion and bash -i work like bash
> Bashkit adds history and fc, complete and compgen, bind, prompt expansion and an interactive bash -i child shell, each with state kept per interpreter.
Source:
Bashkit now implements bash’s interactive builtins, with state kept per interpreter:
* `history` and `fc` under `set -o history` or `bash -i`, with `HISTSIZE`, `HISTFILESIZE`, `histappend` and `HISTFILE` in the VFS. `fc` reruns commands but opens no editor.
* `complete`, `compgen` and `compopt` with all actions.
* `bind` with bash’s default emacs bindings.
* `${x@P}` prompt decoding.
* A `bash -i` child shell with `PS1`, `PS2` and `PS4` defaults, `~/.bashrc` or `--rcfile`, and `--norc`.
```bash
set -o history
echo one
history
compgen -W "start stop status" st
```
The Oils spec pass rate went from 89.0% to 92.8%.
See the [compatibility scorecard](https://github.com/everruns/bashkit/blob/main/crates/bashkit/docs/compatibility.md) and [#2648](https://github.com/everruns/bashkit/pull/2648).
---
# Browser tool for agents on Browserless
> The experimental browser_use capability lets an agent read pages as an accessibility tree, act on elements by ref, fill forms and manage tabs in Browserless.
Source:

The `browser_use` capability gives an agent a `browser` tool on a Browserless browser. Pages are read as an accessibility tree with element refs, and the agent acts by ref or viewport coordinate, fills forms, finds text and works across tabs. Traffic goes through the Everruns network guard.
On Claude models that support Anthropic’s native browser toolset, the capability uses it directly; set `native_tools: false` to keep the function tool.
Browser use is experimental and ships behind the `browserless_browser_use` feature flag.
See [Browser use](https://everruns.com/docs/capabilities/browser-use/) and [#4375](https://github.com/everruns/everruns/pull/4375), [#4376](https://github.com/everruns/everruns/pull/4376).
---
# Coordinator threads in Platform Chat and the Framework
> Platform Chat and Framework agents can hand focused work to child threads and keep a board of what is working, what needs you and what is ready for review.
Source:
**In the UI:** with **Chat threads** turned on in **Settings → Features**, Platform Chat can hand work to threads, which are child sessions. A **Threads** button lists conversations and ongoing work, and background work shows its progress, questions and failures. Custom agents use the same pattern through the `coordination` capability.
**In code:** the Framework’s `everruns::coordination` capability runs threads as sessions of the same engine. Threads wake the coordinator with automatic updates, and `coordination::threads` returns each thread’s status, such as `NeedsYou`, `Working` or `ReadyForReview`.
See [Chat and threads](https://everruns.com/docs/features/ui/#chat-and-threads), [Coordination](https://everruns.com/docs/framework/coordination/) and [#4372](https://github.com/everruns/everruns/pull/4372), [#4377](https://github.com/everruns/everruns/pull/4377).
---
# Desktop computer use on E2B and Daytona sandboxes
> Experimental capabilities run the computer tool on a full Linux desktop in an E2B or Daytona sandbox, now with zoom, cursor, mouse button and hold-key actions.
Source:

Agents can now operate a full Linux desktop instead of only a browser page. The experimental `computer_use_desktop` capability runs the provider-neutral `computer` tool on an E2B desktop sandbox, and `computer_use_daytona` runs it on a Daytona sandbox through Daytona’s Computer Use API. The session creates the desktop on first use, keeps it between calls and deletes it when its lease ends.
The `computer` tool also gained zoom, cursor position, mouse button down and up, hold-key and modified drag actions on Daytona, E2B and Browserless.
Connect E2B or Daytona first, then add one computer-use capability to the agent; only one can provide the `computer` tool.
See [Computer use](https://everruns.com/docs/capabilities/computer-use/) and [#4244](https://github.com/everruns/everruns/pull/4244), [#4365](https://github.com/everruns/everruns/pull/4365), [#4367](https://github.com/everruns/everruns/pull/4367).
---
# Everruns 0.44.0: A2A 1.0 HTTP+JSON and PACT
> Agent versions are retired in favor of change history, A2A channels serve HTTP+JSON and the PACT profiles, and MCP works inside conversations.
Source:

## Highlights
* **A2A 1.0 HTTP+JSON and PACT**: A2A channels serve the HTTP+JSON binding, the PACT Identity profile, and PACT Delegated sign-in, step-up and receipts ([#4322](https://github.com/everruns/everruns/pull/4322), [#4328](https://github.com/everruns/everruns/pull/4328), [#4350](https://github.com/everruns/everruns/pull/4350), [#4352](https://github.com/everruns/everruns/pull/4352))
* **AgentID sign-in**: Public chat and channels accept AgentID, and agents sign in with their AgentMail inbox ([#4334](https://github.com/everruns/everruns/pull/4334), [#4326](https://github.com/everruns/everruns/pull/4326), [#4337](https://github.com/everruns/everruns/pull/4337))
* **MCP in conversations**: Users manage their own MCP servers, connect them mid-conversation, and deferred servers load tools on demand ([#4315](https://github.com/everruns/everruns/pull/4315), [#4343](https://github.com/everruns/everruns/pull/4343), [#4345](https://github.com/everruns/everruns/pull/4345), [#4349](https://github.com/everruns/everruns/pull/4349))
* **Claude Haiku 5.5** and Daytona desktop computer use ([#4359](https://github.com/everruns/everruns/pull/4359), [#4365](https://github.com/everruns/everruns/pull/4365))
## In the changelog
* [Agent versions are retired in favor of change history](https://everruns.com/docs/changelog/2026-10-06-agent-versions-retired/)
* [A2A channels serve HTTP+JSON and the PACT profiles](https://everruns.com/docs/changelog/2026-10-07-a2a-http-json-and-pact/)
* [Sign in to channels and Public Chat with AgentID](https://everruns.com/docs/changelog/2026-10-07-agentid-sign-in/)
* [Claude Haiku 5.5 model added for Anthropic providers](https://everruns.com/docs/changelog/2026-10-07-claude-haiku-5-5/)
Before upgrading, read the breaking changes: [Agent versions are retired in favor of change history](https://everruns.com/docs/changelog/2026-10-06-agent-versions-retired/).
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.44.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0440---2026-10-08) for every pull request in this version.
---
# Everruns SDKs record why a change was made
> All three Everruns SDKs add with_reason, a derived client that sends a change reason the server stores in the changed entity's change history.
Source:
Rust, Python and TypeScript clients gain `with_reason` (`withReason` in TypeScript). It returns a derived client that sends the reason in the `Everruns-Change-Reason` header with every request, and the server stores it in the changed entity’s history. The original client is unchanged, and a blank reason sends no header.
```python
await client.with_reason("retire the unused agent").agents.delete("agent_123")
```
The server rejects a reason longer than 1000 characters, one with control characters other than newline and tab, or one that looks like a credential, with HTTP 400 `invalid_change_reason`.
See [Change history](https://everruns.com/docs/features/change-history/), [SDKs](https://everruns.com/docs/features/sdk/) and [#115](https://github.com/everruns/sdk/pull/115).
---
# Save shell scripts on agents and run them from triggers
> Agents keep named shell scripts that run in Bashkit with their tools, and schedule or webhook triggers can run one with the event payload and no model call.
Source:
An agent can own saved scripts: a name, a description, an optional input schema and a shell body. Manage them with `everruns agents scripts` or under `/v1/agents/{agent_id}/scripts`; every change is in the agent’s history.
Inside the shell, `tools scripts` lists and runs them with the caller’s tools and identity, and with `manage_scripts` on the agent can save new ones.
A schedule or webhook trigger can run a script without asking the model:
```json
{
"script": {
"script": "triage-prs",
"input": {
"pr": "{{payload.pull_request}}"
}
}
}
```
Input strings are templates over the event, so a webhook payload reaches the script.
See [Saved scripts](https://everruns.com/docs/capabilities/tools-in-shell/#saved-scripts), [Agent triggers](https://everruns.com/docs/features/agent-triggers/) and [#4390](https://github.com/everruns/everruns/pull/4390), [#4400](https://github.com/everruns/everruns/pull/4400), [#4403](https://github.com/everruns/everruns/pull/4403), [#4423](https://github.com/everruns/everruns/pull/4423).
---
# Agents home and a New agent page with an agent builder
> The Agents page now shows run activity, channels and setup problems for each agent, and New agent drafts an agent from a description, an example or an import.
Source:

[](./agent-builder@2x.mp4)
**In the UI:** the **Agents** page shows each agent’s channels and triggers, 24-hour run bars and a **Needs attention** list for setup problems. Its **Channels** view lists every channel in the organization with publish and unpublish, people reached and fastest first reply. It replaces the Exposures page; `/exposures` now redirects there.
**New agent** offers **Describe**, **Example**, **Blank** and **Import**. Describe drafts the agent with the utility model, and **Create** makes the agent with its draft channels and schedule trigger.
**In code:** `everruns agents draft` runs the same agent builder from the CLI.
See [Web UI](https://everruns.com/docs/features/ui/), [Channels](https://everruns.com/docs/features/channels/) and [#4399](https://github.com/everruns/everruns/pull/4399), [#4448](https://github.com/everruns/everruns/pull/4448), [#4457](https://github.com/everruns/everruns/pull/4457).
---
# Fix ChatGPT plan sessions failing with HTTP 400
> Sessions that use a ChatGPT plan connection no longer fail with HTTP 400, because Everruns stops sending request-body metadata the Codex backend rejects.
Source:
Sessions running on a ChatGPT plan connection could fail every model call with HTTP 400, because requests to the Codex backend carried metadata in the request body that it does not accept.
Everruns now leaves that metadata out of the request body while keeping the session HTTP header and host observability metadata. No configuration change is needed; existing ChatGPT connections work after upgrading.
See [ChatGPT plan](https://everruns.com/docs/features/chatgpt/) and [#4464](https://github.com/everruns/everruns/pull/4464).
---
# app.everruns.com serves llms.txt and auth.md for AI agents
> AI agents and crawlers can discover Everruns Cloud from app.everruns.com itself through llms.txt, auth.md, Link headers, robots.txt and a sitemap.
Source:
An agent pointed at [app.everruns.com](https://app.everruns.com) can now find out how to use it from the app host itself. `/llms.txt`, `/auth.md`, `/robots.txt` and `/sitemap.xml` are served there, a request for `/` with `Accept: text/markdown` returns `llms.txt`, and `/` carries `Link` headers pointing to the API catalog, `llms.txt`, `auth.md` and the OpenAPI document.
```bash
curl -H "Accept: text/markdown" https://app.everruns.com/
```
MCP connect dialogs also show the Everruns icon for the app.
See [Use in AI Tools](https://everruns.com/docs/getting-started/use-in-ai-tools/).
---
# See and revoke AI clients connected to Everruns
> Settings → My agent experience lists the AI clients you connected to Everruns over MCP OAuth, shows when each was last used, and lets you revoke one.
Source:
AI tools that connect to Everruns over MCP OAuth, such as a coding assistant using the Everruns MCP server, now show up as connected clients.
**In the UI:** open **Settings → My agent experience** to see each client and when it was last used, and revoke any you no longer use. Revoking deletes the client’s refresh tokens, and its next request is refused.
**In code:** `GET /v1/user/connected-clients` lists them and `DELETE` revokes one. Every MCP request checks that its grant is still live.
See [Use Everruns in AI tools](https://everruns.com/docs/getting-started/use-in-ai-tools/) and [#4461](https://github.com/everruns/everruns/pull/4461).
## Update 2026-10-25
Connected AI clients are their own page, **Settings > AI clients**. See [Personal settings pages](https://everruns.com/docs/changelog/2026-10-25-personal-settings-pages/).
---
# Everruns 0.45.0: Tools in Shell
> Agents get a browser tool on Browserless, coordinator threads arrive in Platform Chat and the Framework, and agents can run tools from the shell.
Source:

## Highlights
* **Tools in Shell**: Call visible tools from a Bashkit script, load deferred MCP servers, and pause for approvals before the script starts ([#4385](https://github.com/everruns/everruns/pull/4385), [#4386](https://github.com/everruns/everruns/pull/4386), [#4388](https://github.com/everruns/everruns/pull/4388)).
* **Browser tools**: Provider-neutral Browserless browser tools and a native Claude browser toolset adapter ([#4375](https://github.com/everruns/everruns/pull/4375), [#4376](https://github.com/everruns/everruns/pull/4376)).
* **Coordinator threads**: Coordinate child threads in Framework applications and Platform Chat ([#4377](https://github.com/everruns/everruns/pull/4377), [#4372](https://github.com/everruns/everruns/pull/4372)).
## In the changelog
* [Browser tool for agents on Browserless](https://everruns.com/docs/changelog/2026-10-08-browser-use-capability/)
* [Coordinator threads in Platform Chat and the Framework](https://everruns.com/docs/changelog/2026-10-08-coordinator-threads/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.45.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0450---2026-10-09) for every pull request in this version.
---
# Everruns 0.46.0: Agents home
> Save shell scripts on agents and run them with tools scripts, and the Agents home shows run activity, setup alerts and a shared Channels view.
Source:

## Highlights
* **Agents home**: Enable the Agents home feature for run activity, setup alerts and a shared Channels view ([#4399](https://github.com/everruns/everruns/pull/4399)).
* **Agent scripts**: Save and manage reusable shell scripts on agents through the API, CLI and MCP, then run them with `tools scripts` using the caller’s tools and identity ([#4390](https://github.com/everruns/everruns/pull/4390), [#4400](https://github.com/everruns/everruns/pull/4400)).
## In the changelog
* [Save shell scripts on agents and run them from triggers](https://everruns.com/docs/changelog/2026-10-09-agent-saved-scripts/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.46.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0460---2026-10-09) for every pull request in this version.
---
# Everruns 0.47.0: Codex session reliability
> An Agents home and a New agent page with an agent builder, a fix for ChatGPT plan sessions failing with HTTP 400, and a list of connected AI clients.
Source:

## Highlights
* **Codex session reliability**: Legacy Codex sessions keep host observability metadata and the session HTTP header while omitting unsupported request-body metadata, fixing ChatGPT sessions that failed with HTTP 400 ([#4464](https://github.com/everruns/everruns/pull/4464)).
## In the changelog
* [Agents home and a New agent page with an agent builder](https://everruns.com/docs/changelog/2026-10-09-agents-home-new-agent/)
* [Fix ChatGPT plan sessions failing with HTTP 400](https://everruns.com/docs/changelog/2026-10-09-chatgpt-plan-http-400-fix/)
* [See and revoke AI clients connected to Everruns](https://everruns.com/docs/changelog/2026-10-09-connected-ai-clients/)
* [Models and providers move into one registry page](https://everruns.com/docs/changelog/2026-10-09-models-providers-registry/)
* [Voice channels let people call an agent](https://everruns.com/docs/changelog/2026-10-09-voice-channels/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.47.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0470---2026-10-09) for every pull request in this version.
---
# Models and providers move into one registry page
> Registries → Models now holds Models, Providers and Defaults tabs, so connecting a provider, choosing its models and setting org defaults happen in one place.
Source:

Providers moved out of Settings into **Registries → Models**, which has three tabs. **Models** lists every model from every connected provider and flags new ones a sync discovered for review. **Providers** connects a provider in one sheet: driver, name and key, then the models to enable, with recommended ones ticked. **Defaults** sets the organization’s default model, decision model and default providers per service.
Provider names are now unique within an organization. Managing providers and models needs the `org:models:manage` permission, which replaces the separate provider permission.
See [Models](https://everruns.com/docs/features/ui/#models), [Providers](https://everruns.com/docs/providers/) and [#4458](https://github.com/everruns/everruns/pull/4458).
---
# Voice channels let people call an agent
> A voice channel lets people talk to an agent over WebRTC while it keeps its own model, tools and instructions, on the platform and in the Framework.
Source:
Voice is now a channel type. A speech-to-speech model only listens and speaks; the agent, with its own model and tools, writes every answer. Speech starts sentence by sentence while the answer streams, short fillers cover silence, and talking over the agent stops it. A call and a text chat on one session share a transcript.
**In the UI:** an organization administrator turns on **Voice** in **Settings → Features**. Then add a voice channel from the agent’s **Integrations** tab and use its talk button. Speech uses an OpenAI provider.
**In code:** `everruns::voice` puts a voice channel in front of any Framework session, and serve’s `voice` feature gives every top-level agent browser calls.
See [Voice](https://everruns.com/docs/features/voice/), [Framework voice](https://everruns.com/docs/framework/voice/) and [#4394](https://github.com/everruns/everruns/pull/4394), [#4402](https://github.com/everruns/everruns/pull/4402), [#4406](https://github.com/everruns/everruns/pull/4406).
---
# Yolop 0.20.0 release
> OpenAI models can edit with apply_patch as an opt-in, integrations prefer known CLIs, and command help includes runnable examples.
Source:
## Highlights
* Prefer known CLIs for integrations, add runnable help examples, and offer opt-in model-native `apply_patch`.
* Report live program status in the TUI and keep modals aligned with the active theme.
* Recover unfinished work across hosts and resume interrupted ACP response bodies once.
* Improve Codex authentication refresh, provider failure messages, and recovery from unknown tools and progress-guard rejections.
* Upgrade the Everruns crate family to 0.44.0 and Tuika to 0.13.0.
## In the changelog
* [Opt-in apply_patch editing for OpenAI models](https://everruns.com/docs/changelog/2026-10-09-yolop-native-apply-patch/)
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.20.0) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0200---2026-10-09) for every pull request in this version.
---
# Opt-in apply_patch editing for OpenAI models
> The native_edit_tools capability gives OpenAI models in Yolop the Codex apply_patch tool, and every yolop subcommand now has a description and an example.
Source:
A new opt-in capability, `native_edit_tools`, gives OpenAI models Codex’s `apply_patch` tool. Claude models keep `edit_file`, which already matches their `str_replace` semantics, and the default file-edit tools stay available. Enable it like any capability, in settings or a profile:
```toml
[[capabilities]]
ref = "native_edit_tools"
```
Every `yolop` subcommand’s help now has a description and a worked example, and the system prompt prefers well-known CLIs such as `git` and `gh` over improvised scripts.
See [Tools](https://github.com/everruns/yolop/blob/main/docs/features/tools.md) and [#827](https://github.com/everruns/yolop/pull/827).
---
# Agent budgets stop turns before each model call
> Agent budgets are now checked before every model call in the native agent loop, so an exhausted budget stops the turn with a typed reason and budget events.
Source:
An agent budget is now enforced before every model call in the native loop through a shared budget gate, the same gate the Agents API backend uses. When a budget is exhausted, the turn stops with a typed reason and emits budget events, instead of continuing to spend.
Budget summaries also carry the budget ID, so a client can tell which of several stacked budgets stopped the turn. To continue, top up the exhausted budget with the budget API or CLI.
See [Budgets](https://everruns.com/docs/advanced/budgets/) and [#4477](https://github.com/everruns/everruns/pull/4477).
---
# Agents must give a reason for every change by default
> Require reasons from agents is now on by default, so an agent's change without a reason fails with reason_required; organizations can turn the flag off.
Source:
A change an agent makes without a reason is now refused by default: the command fails with the `reason_required` code and a `retry` action, and the agent retries with `--reason`. Built-in tools that change sessions on the agent’s behalf, such as `spawn_agent` and handoffs, give their own reasons, so they keep working.
```bash
# Before: accepted from an agent. After: refused with reason_required
everruns agents update agent_01h9 --model gpt-6.1-sol
# After: the agent retries with a reason
everruns agents update agent_01h9 --model gpt-6.1-sol --reason "Support asked for faster answers"
```
This is the **Require reasons from agents** feature flag, now on by default. An organization can turn it off to let such changes through with a warning, which the `everruns_entity_changes_without_reason_total` metric counts. People in the UI and scripts calling the API may still leave the reason out.
See [Reasons](https://everruns.com/docs/features/change-history/#reasons) and [#4271](https://github.com/everruns/everruns/pull/4271), [#4552](https://github.com/everruns/everruns/pull/4552).
---
# api_endpoint channels convert to Agent API channels
> Existing api_endpoint channels become Agent API channels at startup with the same keys, and old request shapes now return api_endpoint_retired or HTTP 410.
Source:
The `api_endpoint` channel type is removed. At startup, each existing `api_endpoint` channel converts to an Agent API (`api`) channel: its keys are imported with the same hash, its sessions are retagged, and its settings map to the new channel.
Clients must move to the Agent API routes under `/v1/channels/{channel_id}`. Old request shapes get `400 api_endpoint_retired` pointing at the Agent API docs, and `/v1/apps/{app}/api/{chan}` routes return `410`.
```bash
# Before: an App-scoped api_endpoint route, now 410 Gone
curl -X POST "$EVERRUNS/v1/apps/$APP_ID/api/$CHANNEL_ID/sessions" \
-H "Authorization: Bearer $EVERRUNS_AGENT_KEY"
# After: the Agent API route, same key
curl -X POST "$EVERRUNS/v1/channels/$CHANNEL_ID/sessions" \
-H "Authorization: Bearer $EVERRUNS_AGENT_KEY"
```
See [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/), [Agent API](https://everruns.com/docs/features/channels/#agent-api) and [#4527](https://github.com/everruns/everruns/pull/4527).
---
# Rate agent replies and branch a chat from any message
> Agent replies in Chat get Good, Bad and Branch into new chat actions, and each message shows one status row from sending until the answer arrives.
Source:
**In the UI:** each agent reply in Chat has **Good** and **Bad** ratings, kept per person, and **Branch into new chat**, which copies the conversation up to the end of that reply’s turn. Every message you send shows one status row (Sending, Queued, Thinking, Working, Worked for) and **Retry** if it was not delivered.
**In code:** `POST /v1/sessions/{id}/messages` accepts an optional `client_message_id`. Sending the same one again returns the stored message instead of starting a second turn, and the response reports whether the message started, steered or resumed a turn. The CLI takes `--client-message-id`.
See [Chat and threads](https://everruns.com/docs/features/ui/#chat-and-threads) and [#4502](https://github.com/everruns/everruns/pull/4502), [#4494](https://github.com/everruns/everruns/pull/4494), [#4480](https://github.com/everruns/everruns/pull/4480).
---
# ChatGPT plan connections no longer need a feature flag
> The ChatGPT provider now shows in Connect provider on every deployment, including Everruns Cloud, because signing in is already personal to each user.
Source:

## Update 2026-10-17
ChatGPT Plan was [removed from Platform setup](https://everruns.com/docs/changelog/2026-10-17-chatgpt-plan-ui-removal/). The framework driver remains available.
Connecting a personal ChatGPT plan used to depend on an organization feature flag that hosted deployments kept off. The flag is gone: connecting the provider is the opt-in, and each connection already belongs only to the user who signs in.
Open **Models → Providers → Connect provider → ChatGPT → Sign in with ChatGPT**, approve plan use in ChatGPT, then pick a discovered model in the chat model picker. The composer shows **Using your ChatGPT plan**. Other users, shared defaults and Playground cannot use your connection.
See [ChatGPT plan](https://everruns.com/docs/features/chatgpt/) and [#4329](https://github.com/everruns/everruns/pull/4329), [#4547](https://github.com/everruns/everruns/pull/4547).
---
# Everruns CLI help shows a worked example for every command
> Every everruns command now prints a worked example in --help, command groups describe themselves, and a generated reference documents each command group.
Source:
`everruns --help` now ends with a worked example, every command group describes itself, and about 150 more flags have a help line. Commands whose flat form took a bare ID now take it in the tree form too.
```bash
everruns mcp-servers tools --help
```
The same help is rendered into a new **Reference → CLI Commands** section, one page per command group generated from the binary itself, so the docs and the installed CLI say the same thing.
See the [CLI reference](https://everruns.com/docs/reference/cli/), [CLI](https://everruns.com/docs/features/cli/) and [#4433](https://github.com/everruns/everruns/pull/4433).
---
# Everruns Cloud shows spend per model and agent on a Usage tab
> A Usage tab breaks down Everruns Cloud charges by day, model and agent with token counts and CSV export, and a credit chip and banner warn on low credit.
Source:
**Settings** > **Usage** shows what your organization was charged on [Everruns Cloud](https://app.everruns.com): per service, per day, per model and per agent, with input and output tokens next to the dollars, the credit activity, and a CSV export. Agents that use your own provider keys are not billed here, and the tab says so.
A credit chip under the sidebar shows the balance and links to **Billing**. A banner appears on every page when credit is low (under $1), below zero, or used up, asking members to contact an admin and admins to top up.
See [Credit and the spend cap](https://everruns.com/docs/getting-started/cloud/#credit-and-the-spend-cap).
---
# Everruns Cloud verify-email screen sends a new link again
> The email verification step on Everruns Cloud now offers Sign in again, which sends a fresh link, replacing a resend action that never sent any mail.
Source:
On [Everruns Cloud](https://app.everruns.com), the screen that asks you to verify your email had a resend action that reported success but sent nothing, so an expired verification link left you stuck.
The screen now offers **Sign in again**. Signing in again at Everruns ID sends a fresh verification link if the old one expired, and you continue to onboarding once the address is verified.
See [Everruns Cloud quickstart](https://everruns.com/docs/getting-started/cloud/).
---
# Outbound network policy modes, audit log and org allowlists
> Operators choose open or curated outbound access, every outbound request is audited with its org and session, and admins can widen the allowlist for one org.
Source:
Self-hosted deployments set `EVERRUNS_EGRESS_POLICY` to `open`, `curated-writes` or `curated-all`. In `curated-writes`, plain GET and HEAD reads reach public hosts while writes, MCP and integration traffic still need the allowlist; a curated deny list applies first in both curated modes. The system allowlist grew to about 510 host patterns.
Every outbound request is logged on the `everruns::egress::audit` target with its decision, host, status, organization and session.
A platform administrator can let an organization extend the allowlist with up to 50 host patterns, or grant it open egress. Manage this with `everruns orgs egress-allowlist`.
```bash
# Server environment
EVERRUNS_EGRESS_POLICY=curated-writes
# Platform administrator, then organization admin
everruns orgs egress-allowlist grant set --org org_01h9 --granted true --reason 'Approved in security review'
everruns orgs egress-allowlist set --org org_01h9 --patterns api.example.com --reason 'Agents call our ticketing API'
```
See [Network access](https://everruns.com/docs/advanced/network-access/), the [orgs CLI reference](https://everruns.com/docs/reference/cli/orgs/) and [#4432](https://github.com/everruns/everruns/pull/4432), [#4437](https://github.com/everruns/everruns/pull/4437), [#4452](https://github.com/everruns/everruns/pull/4452), [#4534](https://github.com/everruns/everruns/pull/4534).
---
# Explicit communication lets agents choose when to reply
> An agent's Communication setting can be Explicit, so assistant text stays as private notes and the agent speaks only through send_message or stays silent.
Source:
Every agent now has a **Communication** setting. **Direct**, the default, keeps today’s behavior: the assistant text is the reply. **Explicit** treats assistant text as private working notes; the agent talks only by calling `send_message` and can call `no_reply` to stay silent.
This fits Slack channels and conversations with several people, where the agent should answer only when it has something to add. The setting is on the agent in the web UI, the API, agent packages, the Framework builder and the CLI, and it replaces Slack’s per-endpoint reply mode. Every sent message is recorded as a `conversation.message` event.
agent.toml
```toml
schema_version = 1
name = "release-coordinator"
instructions = "You coordinate releases for the team."
communication = "explicit"
```
See [Explicit communication](https://everruns.com/docs/features/explicit-communication/) and [#4447](https://github.com/everruns/everruns/pull/4447).
---
# GitHub triggers wake agents on CI results
> GitHub triggers now receive workflow run and check suite completions, and a changes-only filter wakes the agent only when a workflow's CI result changes.
Source:
A GitHub trigger now fires on `workflow_run` and `check_suite` completions, with a normalized `github.ci` context for the message and each workflow as its own subject. New agent GitHub Apps request read access to actions and checks; the agent’s GitHub card lists what an older App is missing.
Turn on **Only when the CI result changes** in the GitHub trigger form to wake the agent only when the outcome for a workflow and branch changes, for example from passing to failing, instead of on every run. Triggers expose this as the `changes_only` filter.
See [Agent triggers](https://everruns.com/docs/features/agent-triggers/), [Summarize GitHub pull requests](https://everruns.com/docs/how-to/summarize-github-pull-requests/) and [#4541](https://github.com/everruns/everruns/pull/4541), [#4546](https://github.com/everruns/everruns/pull/4546).
---
# Pick and configure guardrail checks in the agent editor
> The Guardrails settings panel lets authors add presets or single checks and set their stage and action, and model-backed checks can now judge the final reply.
Source:
**In the UI:** the Guardrails capability settings on an agent or harness is now an editor instead of a raw form. Add a gallery preset to append its checks, or add a single check and set its rule, stage and what happens on a match. Choose whether a policy judge or moderation check is answered by the utility model or the organization’s decision model. The mode reads **Active** or **Advisory**.
**For agents:** `llm_judge` and `mcp` checks are now valid on the output stage. They run once on the finalized assistant message, with the same caps, advisory downgrade and fail-open behavior as the tool stages.
See [Guardrails](https://everruns.com/docs/capabilities/guardrails/) and [#4459](https://github.com/everruns/everruns/pull/4459), [#4542](https://github.com/everruns/everruns/pull/4542).
---
# Built-in harnesses reorganized as a tree by use case
> Base, Conversation, Worker, Bashkit Worker and Sandbox Worker now form a tree of built-in harnesses, and existing Worker bindings move by sandbox policy.
Source:
The built-in harnesses now form a tree where each layer answers one question. **Base** carries the system essentials. **Conversation** (the default) adds chat features with no workspace, and **Worker** adds files, skills and delegation with no compute. **Bashkit Worker** and the new **Sandbox Worker** split Worker by where commands run, and each enforces that rule on agents and sessions.
Organizations are migrated once: agents bound to the old Worker move to Bashkit Worker or Sandbox Worker based on their sandbox policy. Worker Base and Generic stay as deprecated harnesses for existing bindings.
```bash
everruns harnesses list
everruns agents update agent_01h9 --harness sandbox-worker --reason "Needs a real machine for builds"
```
See [Built-in harnesses](https://everruns.com/docs/features/harnesses/#built-in-harnesses) and [#4484](https://github.com/everruns/everruns/pull/4484).
---
# Label MCP tools as read only or changes things
> Save a risk label on each tool of an organization MCP server to decide whether it asks for approval, with model-suggested labels a person can accept.
Source:
Everruns cannot see inside a remote tool, so most MCP tools ask for approval by default. A saved label now decides it: **Read only** never asks, **Changes things** always asks. A label wins over the server’s own hints and survives tool list changes.
**In the UI:** open **Settings > MCP catalog** and click **Tools** on a server. **Suggest labels** asks a decision model to judge unlabeled tools; nothing changes until someone picks a choice or clicks **Use suggestion**.
**In code:** `PUT /v1/mcp-servers/{id}/tools/{tool_name}/label` with `{"label": "read_only"}`, or `everruns mcp-servers label-tool` and `suggest-labels` in the [mcp-servers CLI](https://everruns.com/docs/reference/cli/mcp-servers/). Label changes are in the server’s history.
See [Which tools ask before they run](https://everruns.com/docs/features/mcp/#which-tools-ask-before-they-run) and [#4475](https://github.com/everruns/everruns/pull/4475), [#4498](https://github.com/everruns/everruns/pull/4498).
---
# Add your own MCP servers and use them in chat
> People add personal MCP servers in Settings, and agents with the User MCP Servers capability use them, including servers connected in the middle of a chat.
Source:
**In the UI:** open **Settings > My agent experience > My MCP servers** and choose **Add server**, from the organization catalog or by URL. Signing in to a catalog server as yourself also adds it to this list. In Chat, the **MCP** button lists a conversation’s chat-only servers.
**For agents:** with the **User MCP Servers** capability (`user_mcp`), each turn adds the enabled servers of the person chatting. Platform Chat has it on, with `manage` so it can add and connect servers mid-conversation under the agent’s approval policy. Personal servers load their tools on demand unless you turn off **Load tools on demand**.
See [User MCP servers](https://everruns.com/docs/features/user-mcp-servers/), the [capability](https://everruns.com/docs/capabilities/user-mcp-servers/) and [#4295](https://github.com/everruns/everruns/pull/4295), [#4315](https://github.com/everruns/everruns/pull/4315), [#4345](https://github.com/everruns/everruns/pull/4345), [#4381](https://github.com/everruns/everruns/pull/4381), [#4496](https://github.com/everruns/everruns/pull/4496).
## Update 2026-10-16
Adding or enabling a server follows [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/). See [the fix](https://everruns.com/docs/changelog/2026-10-16-user-mcp-approval-policy/).
## Update 2026-10-25
The list is **Settings > MCP servers**. See [Personal settings pages](https://everruns.com/docs/changelog/2026-10-25-personal-settings-pages/).
---
# Everruns agents act as Poppy personal agents
> The poppy_client capability lets an Everruns agent find a company's agent through the Personal Agent Protocol, talk to it and hand off to a person there.
Source:
The [Personal Agent Protocol](https://personalagentprotocol.org) (“Poppy”) lets a person’s own agent do business with a company: find it, start a conversation with its agent, and hand off to a person. Add the `poppy_client` capability and an Everruns agent becomes that personal agent for any company that publishes `/.well-known/poppy.json`, with nothing to register. Platform Chat has it.
```json
{
"ref": "poppy_client",
"config": {}
}
```
The deployment holds one personal agent identity with pairwise user IDs, and keys and session tokens stay in the control plane. A `poppy` channel serves the company side of [draft 0.1](https://personalagentprotocol.org/docs/spec): any personal agent can reach a published agent once the company redirects its `/.well-known/poppy.json` to the channel, as described in [Personal Agent Protocol (Poppy)](https://everruns.com/docs/features/poppy/).
See [Poppy personal agent](https://everruns.com/docs/capabilities/poppy-personal-agent/) and [#4479](https://github.com/everruns/everruns/pull/4479), [#4549](https://github.com/everruns/everruns/pull/4549), [#4558](https://github.com/everruns/everruns/pull/4558).
## Update 2026-10-11
Framework apps can be personal agents too. See [Framework apps act as Poppy personal agents](https://everruns.com/docs/changelog/2026-10-11-poppy-framework-personal-agents/).
---
# Everruns SDKs add AgentClient to call one agent with a key
> The Python, TypeScript and Rust SDKs gain AgentClient, which calls a single agent through the Agent Execution API with an agent key, including SSE streaming.
Source:
`AgentClient` in the Python, TypeScript and Rust SDKs calls one agent through the Agent Execution API with an agent key (`evr_ak_...`). It reads `EVERRUNS_AGENT_URL` and `EVERRUNS_AGENT_KEY` and covers `run()`, sessions, messages, events with SSE streaming, cancel, questions, tool approvals, runtime tokens, `for_end_user` and idempotency keys.
```python
from everruns_sdk import AgentClient
agent = AgentClient()
print(await agent.run("What is the status of order 42?"))
```
Calling an agent through the management client’s `sessions.create` and `messages.create` still works but is deprecated: Python warns once, and TypeScript marks the methods `@deprecated`.
See [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/), [SDKs](https://everruns.com/docs/features/sdk/) and [#116](https://github.com/everruns/sdk/pull/116).
---
# serve keeps daemon data in S3 and can require API keys
> serve start --store s3:// keeps a daemon's data in an object store, agent routes can require API keys or OIDC, and serve resumes cut-off sessions at boot.
Source:
A serve daemon can now keep its data directory in an S3 bucket or any S3-compatible store with conditional writes:
```bash
./revenue-analyst start --store s3://bucket/prefix
```
A daemon started elsewhere with the same store waits out a killed holder’s lease, restores the data and carries on.
serve apps can also require a credential on agent routes: static API keys from code, `serve.toml` or `SERVE_API_KEYS`, OIDC, or OAuth 2.0 introspection. The agent card stays public, and the A2A Agent Card lists the accepted methods as security schemes. `eval --against` reaches a protected app with the credential in `SERVE_CREDENTIAL`. At boot, serve resumes sessions active in the last seven days and re-runs an unfinished tool call only when that is safe.
See [Keep the data in a bucket](https://everruns.com/docs/framework/serve/#keep-the-data-in-a-bucket), [Authentication](https://everruns.com/docs/framework/serve/#authentication) and [#4515](https://github.com/everruns/everruns/pull/4515), [#4518](https://github.com/everruns/everruns/pull/4518), [#4485](https://github.com/everruns/everruns/pull/4485), [#4648](https://github.com/everruns/everruns/pull/4648).
---
# Session SQL databases survive server restarts
> Session SQL databases are now stored in PostgreSQL page by page, so a server restart no longer loses them and every server instance sees the same data.
Source:
Databases created with the SQL database capability lived only in one server process’s memory, so a restart lost them and other server instances never saw them.
The server now keeps an in-memory SQLite cache per database and, after every write, stores only the changed 4 KB pages in PostgreSQL in one transaction. A server that does not have the database cached rebuilds it from the stored pages, and each commit is a compare-and-set so concurrent writers cannot overwrite each other. Agents keep using `sql_execute`, `sql_query` and `sql_schema` as before.
See [SQL database](https://everruns.com/docs/capabilities/sql-database/) and [#4553](https://github.com/everruns/everruns/pull/4553).
---
# Session Trace view replaces Transcript and Timeline
> Sessions open on a Trace tab that lays out turns and steps with timing bars, an inspector for each step's input, output and model request, and event search.
Source:

**In the UI:** a session now opens on **Trace**, which replaces the Transcript and Timeline tabs. A turn rail with a minimap leads to each turn’s steps: model calls, tool calls, approvals, sub-agents and messages, with waterfall timing bars. The step inspector shows input, output, the full model request and raw events. Batches and sub-agents expand inline, search jumps to a hit, and `j` and `k` move between steps. The view, error filter and selected step live in the URL.
**In code:** read the same data from `/v1/sessions/{id}/trace`, or with `everruns sessions trace get` and `everruns sessions trace turns list`.
See [Web UI](https://everruns.com/docs/features/ui/#sessions), the [sessions CLI reference](https://everruns.com/docs/reference/cli/sessions/) and [#4481](https://github.com/everruns/everruns/pull/4481), [#4500](https://github.com/everruns/everruns/pull/4500), [#4503](https://github.com/everruns/everruns/pull/4503), [#4556](https://github.com/everruns/everruns/pull/4556).
---
# Tools in Shell runs an agent's tools from Bashkit scripts
> The tools_in_shell capability moves most tools into one tools shell command, so a script can chain calls, preview them with tools plan and ask approval first.
Source:
The **Tools in Shell** capability (`tools_in_shell`) moves most of an agent’s tools, MCP servers included, from the model’s tool list into one `tools` command in the Bashkit shell. One script can call several tools and filter results with `jq` before the model sees them:
```bash
tools github list-pulls '{"repo":"acme/api","state":"open"}' | jq -r '.[].number'
```
Every call goes through the turn’s tool policy. A script that would make a call needing approval is held before it starts. `tools plan` lists a script’s calls and their risk without running anything. Tools with no risk hints are rated once, which **Rate unlabeled tools** can turn off. Each call shows in the session timeline under its shell call.
See [Tools in Shell](https://everruns.com/docs/capabilities/tools-in-shell/) and [#4385](https://github.com/everruns/everruns/pull/4385), [#4388](https://github.com/everruns/everruns/pull/4388), [#4435](https://github.com/everruns/everruns/pull/4435), [#4439](https://github.com/everruns/everruns/pull/4439), [#4443](https://github.com/everruns/everruns/pull/4443), [#4501](https://github.com/everruns/everruns/pull/4501).
---
# Code search works on machines without ripgrep
> Yolop ships a built-in rg search as yolop utils rg and, when no system ripgrep is found, as an agent tool backed by the same engine as its search tool.
Source:
Yolop now includes a ripgrep-compatible search. `yolop utils rg` supports a common flag subset (`-n`, `-i`, `-S`, `-F`, `-w`, `-g`, `-l`, `-c`, `--files`, `-C`, `-A`, `-B`, `--hidden`, `--no-ignore` and more) with rg-style exit codes:
```bash
yolop utils rg -n hello .
yolop utils rg --files -g '*.txt' .
```
When no `rg` is found on `PATH` at startup, the agent also gets an `rg` tool on the same engine: read-only, scoped to the workspace and with bounded output. Machines with ripgrep installed keep using it.
See [Tools](https://github.com/everruns/yolop/blob/main/docs/features/tools.md) and [#839](https://github.com/everruns/yolop/pull/839).
---
# Coordinators run threads as separate processes
> Yolop now uses the Everruns coordination capability: each thread runs as a headless Yolop process in its own worktree and shows on a THREADS board in the TUI.
Source:
Yolop’s session coordination now uses the Everruns `coordination` capability, the same one Everruns Platform Chat runs, in place of Yolop’s own worker pool. Each thread runs as a headless `yolop --serve` process in its own worktree, supervised and restarted by its coordinator, so threads keep working while the coordinator is closed.
Enable `coordination` in a profile and start a session with it. The TUI opens a **THREADS** board in the activity rail; Enter on a row opens that thread, and quitting it returns to the coordinator. A thread’s sandbox escalations are asked in the coordinator’s approval prompt. `yolop sessions live` lists running sessions and threads.
```bash
yolop -C /path/to/repo --coordinator
yolop sessions live
yolop sessions send "Rebase on main before you push."
```
See [Session coordination](https://github.com/everruns/yolop/blob/main/docs/session-coordination.md), [#835](https://github.com/everruns/yolop/pull/835) and [#837](https://github.com/everruns/yolop/pull/837).
---
# New setups default to the latest model on every provider
> New Yolop setups default to gpt-6.1-sol, claude-opus-5-5, muse-spark-1.3 and gemini-3.8-flash; a saved model setting or the -m flag still takes priority.
Source:
Yolop’s default models move to the newest on each provider: `gpt-6.1-sol` for `openai`, `codex` and `openrouter`, `claude-opus-5-5` for `anthropic`, `muse-spark-1.3` for `meta` and `gemini-3.8-flash` for `google`. The default model menu and the setup picker list the GPT-6, Claude 5.5, Muse 1.3 and Gemini 3.8 families.
A model saved in your settings, or passed with `-m`, is unaffected.
```bash
yolop --provider anthropic -p "fix the failing test" # claude-opus-5-5
yolop config model set gpt-6.1-sol # saved choice wins
yolop -m gpt-6.1-sol -p "fix the failing test" # so does -m
```
See [Providers and models](https://github.com/everruns/yolop#providers-and-models) and [#834](https://github.com/everruns/yolop/pull/834).
---
# Call one agent from code with an Agent API key
> An Agent API channel gives an application a key that reaches one agent's sessions, with streaming, approvals, end users, browser origins and spend limits.
Source:
**In the UI:** add an **Agent API** channel to an agent, publish it, and create a key in its **Agent keys** card. The secret (`evr_ak_…`) is shown once. An application holding it can call this one agent and nothing else; keys rotate with an overlap window and can be revoked.
**In code:** routes under `/v1/channels/{channel_id}` start sessions, send messages, follow events over SSE, answer questions, approve tools and cancel turns. A key sees only the sessions it started. Channel settings add end users through your own identity provider, browser origins, `Idempotency-Key` retries, a daily spend limit per caller, and access for organization members.
See [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/), [Agent API](https://everruns.com/docs/features/agent-api/) and [#4438](https://github.com/everruns/everruns/pull/4438), [#4490](https://github.com/everruns/everruns/pull/4490), [#4508](https://github.com/everruns/everruns/pull/4508), [#4511](https://github.com/everruns/everruns/pull/4511), [#4514](https://github.com/everruns/everruns/pull/4514), [#4516](https://github.com/everruns/everruns/pull/4516).
---
# Load Everruns workflow skills through MCP
> The Everruns MCP endpoint serves two workflow skills for running sessions and authoring agents, with metadata and verified file manifests.
Source:
Hosts supporting the MCP Skills extension can discover and load workflow guidance directly from the Everruns endpoint. Use `skills/list` under MCP `2026-07-28`, then load a skill through your host’s skill loader. `everruns-mcp` guides platform and session work; `everruns-agent-authoring` guides creating and revising agents.
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "skills/get",
"params": {
"uri": "skill://everruns-mcp/SKILL.md"
}
}
```
Each entry includes a complete file manifest with SHA-256 digests. The endpoint serves only these two workflows; the Rust Framework skill remains separately installable. See [Skills for MCP clients](https://everruns.com/docs/features/mcp/#skills-for-mcp-clients) for discovery, file reads, and host verification.
## Update 2026-10-16
The catalog now also includes `everruns` for product concepts and `everruns-cli` for command usage and examples. Rust development guidance is separately installable as `everruns-framework`.
---
# Framework apps act as Poppy personal agents
> PoppyPersonalAgent makes a Framework app a person's own agent over the Personal Agent Protocol, with the same tools Platform Chat uses and a runnable example.
Source:
A Framework app can now be a person’s own agent over the [Personal Agent Protocol](https://personalagentprotocol.org/docs/spec) (“Poppy”). Turn on the `poppy` feature of the `everruns` crate and give the agent `PoppyPersonalAgent`. It gets the same five tools as Platform Chat: discover, send, read, handoff and close.
```rust
use everruns::poppy::{PoppyKey, PoppyPersonalAgent};
let poppy = PoppyPersonalAgent::new("https://agent.example.com", PoppyKey::generate()?);
let agent = Agent::builder()
.name("personal-agent")
.model(model)
.capability(poppy.clone())
.build()?;
// Serve poppy.metadata("My assistant") at /v1/poppy/agent.json
// and poppy.jwks() at /v1/poppy/jwks.json.
```
The app signs with its own key and keeps the person’s User IDs and Session Tokens in memory. The server and the Framework now share one protocol client, so both behave the same. The [`poppy-personal-agent` example](https://github.com/everruns/everruns/tree/main/examples/poppy-personal-agent) runs on `gpt-6.1-sol` and is tested against [pi-poppy](https://github.com/richardanaya/pi-poppy)’s independent company.
See [Poppy Personal Agent](https://everruns.com/docs/capabilities/poppy-personal-agent/) and [Personal Agent Protocol (Poppy)](https://everruns.com/docs/features/poppy/), and [#4577](https://github.com/everruns/everruns/pull/4577), [#4566](https://github.com/everruns/everruns/pull/4566).
---
# SDK 0.3.0 release
> SDKs default to the Everruns Cloud API URL, record why a change was made, and add an AgentClient for calling one agent through the Agent API.
Source:
## Highlights
* New `AgentClient` in the Python, TypeScript, and Rust SDKs for calling one agent through the Agent Execution API with an agent key (`EVERRUNS_AGENT_URL` / `EVERRUNS_AGENT_KEY`). It covers `run()`, sessions, events and SSE streaming, cancel, questions, tool approvals, runtime tokens, `for_end_user`, and idempotency keys. The management client is now deprecated in favor of `AgentClient`.
* Change reasons: all SDKs gain `with_reason` / `withReason`, a derived client that sends the `Everruns-Change-Reason` header so the server records the reason in entity change history.
* The default base URL is now Everruns Cloud (`https://app.everruns.com/api`) instead of a placeholder, so a client created with only `EVERRUNS_API_KEY` works. `EVERRUNS_API_URL` still overrides it for self-hosted.
* Docs: how to connect an AG-UI client to an Everruns endpoint.
* OpenAPI examples no longer name the sunset Claude Sonnet 4.5.
## In the changelog
* [Everruns SDKs default to the Everruns Cloud API URL](https://everruns.com/docs/changelog/2026-10-02-sdk-default-cloud-base-url/)
* [Everruns SDKs record why a change was made](https://everruns.com/docs/changelog/2026-10-08-sdk-change-reasons/)
* [Everruns SDKs add AgentClient to call one agent with a key](https://everruns.com/docs/changelog/2026-10-10-sdk-agent-client/)
See the [GitHub release](https://github.com/everruns/sdk/releases/tag/v0.3.0) and the [full release notes](https://github.com/everruns/sdk/blob/main/CHANGELOG.md#030---2026-10-11) for every pull request in this version.
---
# Yolop 0.21.0 release
> Code search works without ripgrep installed, coordinators run threads as separate processes, and new setups default to the latest model on each provider.
Source:
## Highlights
* Treat everruns coordination threads as yolop processes and close the thread follow-ups.
* Seed connector defaults from the upstream catalog and register E2B tools.
* Default to the latest model on every provider.
* Search with the built-in rg fallback on machines without ripgrep, and ask structured questions across hosts.
## In the changelog
* [Code search works on machines without ripgrep](https://everruns.com/docs/changelog/2026-10-10-yolop-built-in-rg-search/)
* [Coordinators run threads as separate processes](https://everruns.com/docs/changelog/2026-10-10-yolop-coordination-threads/)
* [New setups default to the latest model on every provider](https://everruns.com/docs/changelog/2026-10-10-yolop-latest-default-models/)
See the [GitHub release](https://github.com/everruns/yolop/releases/tag/v0.21.0) and the [full release notes](https://github.com/everruns/yolop/blob/main/CHANGELOG.md#0210---2026-10-11) for every pull request in this version.
---
# Set an agent's avatar from the CLI and MCP
> The set_agent_avatar command takes a curated preset id or a base64 image, so an agent building another agent over MCP can give it a picture.
Source:
Agent avatars could only be set from the UI or the multipart upload route. Two commands now cover the CLI and MCP (see [`everruns agents`](https://everruns.com/docs/reference/cli/agents/)): `list_avatar_presets` lists the curated catalog, and `set_agent_avatar` sets either a preset or your own PNG, JPEG, GIF or WebP image, sent as base64 and at most 1 MB decoded. The agent’s one-click Slack apps get the new icon, as with an upload.
```bash
everruns agents avatar presets
everruns agents avatar set agent_01h9 --preset familiars-patch --reason 'Brand the triage agent'
base64 logo.png > logo.b64
everruns agents avatar set agent_01h9 --image-base64 @logo.b64 --reason 'Use the team logo'
```
Over REST the same command is `POST /v1/agents/{agent_id}/avatar/set`.
---
# Choose where an agent's GitHub App lives, and manage it from the CLI
> Connect GitHub suggests the organizations your team already uses for an agent's App, an uninstalled App can start over, and the CLI manages it.
Source:
An agent’s GitHub App can only be installed on the account that created it, so **Connect GitHub** on the agent’s Integrations tab now asks where to create it: **Personal account**, one of the GitHub organizations your Everruns organization already connected (from other agents’ Apps and GitHub connections), or **Another organization…**. This replaces the free-text organization field.
If the App was created on the wrong account, the card now has **… > Start over**, which forgets the uninstalled App so the next connect can choose again.
The same connection is now available from the CLI and the MCP `execute` tool:
```bash
everruns agents github get --agent-id agent_01h9
everruns agents github connect --agent-id agent_01h9 --owner-org acme
everruns agents github disconnect --agent-id agent_01h9
```
`connect` prints the install link once the App exists; creating the App still finishes in the browser. `disconnect` uninstalls an installed App, or starts over when it was never installed. See [Summarize GitHub pull requests](https://everruns.com/docs/how-to/summarize-github-pull-requests/) for a full agent setup.
---
# An agent's GitHub App gets its own section, name and avatar step
> GitHub moves out of Triggers into its own section on the agent's Integrations tab, new Apps take the agent's name, and a step helps set the App's avatar.
Source:
**In the UI:** the agent’s **Integrations** tab now has a **GitHub** section between **Channels** and **Triggers**. It shows the agent’s bot login, such as `pr-summarizer[bot]`, whether it is the agent’s own App, the account and repositories it is installed on, and links to add and list GitHub triggers.
GitHub offers no way to set an App’s logo for it, so a **Set the avatar** step offers the agent’s avatar as a download and links to the App’s settings page.
New Apps are named after the agent, with a random suffix only when the name is taken.
**In the API:** `GET /v1/agents/{agent_id}/github` adds three fields:
```json
{
"kind": "agent_app",
"bot_login": "pr-summarizer[bot]",
"app_settings_url": "https://github.com/organizations/acme/settings/apps/pr-summarizer"
}
```
See [Summarize GitHub pull requests](https://everruns.com/docs/how-to/summarize-github-pull-requests/).
---
# Agents can cap output tokens per response
> A per-agent Max output tokens setting caps every model response, so agents keep running on a provider balance too low for the model's full output limit.
Source:
Set **Max output tokens** in the agent’s settings, next to the default model, to cap how many tokens the model may write in one response. Leave it empty to keep the provider default.
```bash
everruns agents update agent_01h9 --max-output-tokens 4096 --reason 'Fit the OpenRouter balance'
```
Everruns sends the value as the request’s `max_tokens` and lowers it to the model’s own output limit when that is known. Without it [OpenRouter](https://everruns.com/docs/providers/openrouter/) reserves the model’s full output limit against your credits and returns HTTP 402 (“can only afford N tokens”) on a low balance; a ceiling below that number lets the call through. The field is `max_output_tokens` on the agent API, and agent packages export and import it.
---
# Agents can sync provider models with the Platform capability
> sync_provider_models now works from an agent, and provider create and update from an agent discover models and set the organization's default model.
Source:
An agent with the [Platform capability](https://everruns.com/docs/capabilities/platform/) can now run `sync_provider_models`. Before, the command answered “Model sync is not available on this endpoint” when an agent ran it, although it worked over the REST API and MCP.
Creating or updating a provider from an agent now also discovers the provider’s models and sets the organization’s default model when none is set, the same as the Providers API.
See [#4405](https://github.com/everruns/everruns/pull/4405).
---
# API keys a Bashkit agent uses but never sees
> Bind an API key to one URL and the Bashkit shell can call that API with $NAME, while the model only ever sees a placeholder and never the key.
Source:
**In the UI:** on an agent’s **Credentials** tab, **Add binding** now offers **HTTP API**: a shell variable, the HTTPS URL it may be sent to, and whether it goes as a bearer token or a named header. With outbound HTTP on, the agent’s shell calls the API with `$NAME`. The platform swaps the real key into that header only for requests to the bound URL and redacts it if a response echoes it back.
**In the API:** [session secrets](https://everruns.com/docs/capabilities/session-storage/) accept `bindings` on `PUT /v1/sessions/{session_id}/storage/secrets`, and the MCP tool `session_set_secret` takes `url`, `auth` and `scope`, collecting the value on a secure page.
See [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/#api-keys-the-agent-never-sees).
## Update 2026-10-19
[Scoped credentials](https://everruns.com/docs/changelog/2026-10-19-scoped-credentials-secure-capture/) add virtual-user and organization ownership, explicit agent grants, form-field bindings and Web Fetch response capture. Hosted Bashkit resolves authorization again for each credential-bearing send. Secure value updates now require the observed resource revision; ordinary unbound session secrets retain their existing lifecycle.
---
# Browser agents see their screenshots and keep their page
> Browser screenshots now reach the model, Browserless tools no longer race on one page, and key presses report where focus landed.
Source:
Screenshots from the `browser` and `browserless_screenshot` tools now reach the model. Before, any image over 64 KiB (almost every screenshot) was silently dropped; images now have their own 5 MiB budget, and a dropped image is named in the tool result.
Browserless tools and the `browser` tool share one browser per session, and now run one at a time instead of racing on the same page. `browserless_content`, `browserless_scrape` and `browserless_screenshot` read in a tab of their own, so they no longer move the page the agent is working in.
A `browser` key press now reports where focus landed (`Pressed Tab. Focus is on link "Vision".`), and `read_page` marks the focused element, which makes keyboard and accessibility testing practical.
Connecting a connector that is turned off for the organization now says so, and where an admin turns it on, instead of reporting an unknown connector.
---
# Adopt agent examples and list provider accounts from the CLI
> The everruns CLI and MCP can list and adopt agent examples such as pr-reviewer, and list an organization's provider accounts without their credentials.
Source:
Agent examples could only be adopted from the UI. `everruns agents examples list` shows them, and `adopt` creates an agent from one by name (see [`everruns agents`](https://everruns.com/docs/reference/cli/agents/)). Organization admins can list sandbox provider and web search accounts with `everruns orgs provider-accounts list` (see [`everruns orgs`](https://everruns.com/docs/reference/cli/orgs/)); it returns names, providers and ids, never credentials.
```bash
everruns agents examples adopt pr-reviewer --reason 'Review pull requests'
everruns orgs provider-accounts list --org org_01h9
```
A flag renamed for the CLI now also accepts its parameter’s own kebab-case spelling, so `--agent-id` works where `--agent` is documented.
---
# Cloudflare Web Search joins as a Web Search provider
> Connect a Cloudflare account as a search account and the web_search tool searches Ceramic, Exa or Linkup through the Cloudflare Web Search API.
Source:
[Cloudflare Web Search](https://everruns.com/docs/integrations/cloudflare-web-search/) is now a Web Search provider next to Brave Search and Parallel. Connect a Cloudflare account in **Registers > Web search** with an API token, the account ID and the index to search: Ceramic ($0.25 per 1,000 searches, zero data retention), Exa or Linkup. Searches run through your AI Gateway, so they appear in its logs, and bill to its credits or to an Exa or Linkup key you stored there.
Agents keep their one `web_search` tool. Pick the account in an agent’s Web search settings or place it in the Automatic order, and a failed search falls through to the next provider as usual.
Connecting through the API takes the same fields as the form:
```bash
curl -X POST "$EVERRUNS_URL/v1/orgs/$ORG/organization-connections/providers/cloudflare_web_search" \
-H "Authorization: Bearer $EVERRUNS_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "Team", "api_key": "'"$CLOUDFLARE_API_TOKEN"'",
"account_id": "'"$CLOUDFLARE_ACCOUNT_ID"'", "index": "ceramic"}'
```
See [Web Search](https://everruns.com/docs/capabilities/web-search/).
---
# Docs moved to everruns.com/docs
> The documentation now lives at everruns.com/docs, and every docs.everruns.com link, Markdown file and schema redirects to the same page there.
Source:
The documentation moved from docs.everruns.com to [everruns.com/docs](https://everruns.com/docs/). Every old address, including `llms.txt`, the per-page Markdown files and the schemas, permanently redirects to the same path there, so existing links keep working. A request with `Accept: text/markdown` returns any docs page as Markdown; see [Use in AI tools](https://everruns.com/docs/getting-started/use-in-ai-tools/) for the text sets.
```bash
curl -H "Accept: text/markdown" https://everruns.com/docs/framework/
curl -sI https://docs.everruns.com/llms.txt # 301 to everruns.com/docs/llms.txt
```
The docs share the everruns.com header, footer and search, and were redesigned to match. A docs bar with the sections, search and a light, dark or system theme switch stays at the top. Articles open with a breadcrumb, a summary, reading time and last update, with “On this page” beside the text. The docs home starts with search and the three ways to run Everruns.
---
# Org admins link the Everruns GitHub App, agents choose it explicitly
> Org admins link Everruns GitHub App installations in Settings, and each agent chooses between its own GitHub App and a linked account.
Source:
The shared Everruns GitHub App now belongs to the organization. An org admin links a GitHub account in **Settings > Organization > Everruns GitHub App**, and an agent without a GitHub identity can pick **Use the Everruns app** next to **Create this agent’s own GitHub App**.
```bash
# The linked account ids are in `everruns_app_installations`
everruns agents github get --agent-id "$AGENT_ID"
everruns agents github use-everruns-app --agent-id "$AGENT_ID" --connection-id "$LINK_ID"
```
The agent then comments as the shared bot, and its pull request triggers fire from the shared App’s webhook. Nothing falls back to the shared App: unlinking an account removes it from every agent that chose it, and members can no longer attach an installation to an agent directly.
See [Summarize GitHub pull requests](https://everruns.com/docs/how-to/summarize-github-pull-requests/).
---
# Check out private pull requests into the managed sandbox
> The new github_checkout_pull_request tool fetches a pull request into the managed sandbox with a read-only token that never stays in the sandbox.
Source:
A review agent with the [GitHub capability](https://everruns.com/docs/capabilities/github/) and a managed sandbox can now check out a pull request from a private repository and build, test or diff it with `bash`. Before, `git clone` in the sandbox only worked for public repositories.
```json
{
"repo": "acme/app",
"number": 42
}
```
`github_checkout_pull_request` fetches the head and base branch into `/workspace/app` and returns the commit hashes. The token it uses reads one repository, is passed through a temporary file removed after the fetch, and never lands in the git config, later shell commands or sandbox checkpoints.
## Update 2026-10-12
Checkout now requires verified installation-token scope from the trusted resolver: one repository, read-only permissions, and at most one hour remaining. Personal OAuth tokens, session-secret fallbacks and older resolver responses without scope evidence are refused. Connect a GitHub App installation for checkout; personal-token workflows can still use the host-side pull request and diff tools.
---
# GitHub tools act as one chosen identity, with narrowed tokens
> A new acts_as setting picks whose GitHub identity the tools use, and each call gets a token limited to one repository and the permissions it needs.
Source:
The [GitHub capability](https://everruns.com/docs/capabilities/github/)’s tools now act on GitHub as exactly one identity, set by the new `acts_as` setting: `agent` (the default), `person`, or `person_or_agent`. Before, a run started by a person who had connected GitHub used that person’s connection, so a review bot could post as the Everruns app instead of as itself.
```json
{
"ref": "github",
"config": {
"acts_as": "person"
}
}
```
Every call also gets a short-lived token limited to the repository it names and the permissions that tool needs, so reading a diff no longer holds write access. Memory and knowledge index syncs from GitHub get read-only tokens for their one repository, and Daytona git tokens only reach repository contents.
---
# GitHub triggers narrow CI events by branch and workflow
> The GitHub trigger form takes the branches and workflow names a CI trigger accepts, offers one session for every event, and the agent builder fills both in.
Source:
When a GitHub trigger listens to CI events, its form now has **Branches** and **Workflows** fields. List `main` and `CI` to wake the agent only for the CI workflow on main; leave a field empty to accept every branch or workflow. The trigger stores them as filter conditions on the normalized CI event:
```json
{
"filter": {
"conditions": [
{
"path": "github.ci.branch",
"any_of": [
"main"
]
},
{
"path": "github.ci.workflow",
"any_of": [
"CI"
]
}
]
}
}
```
The session mode list adds **One session for everything**, so a watcher that reacts to every CI result keeps one ongoing conversation.
When you describe a CI watcher to the agent builder and name a branch or workflow, the trigger it sets up arrives with those fields filled in.
See [Agent triggers](https://everruns.com/docs/features/agent-triggers/) and [#4607](https://github.com/everruns/everruns/pull/4607).
---
# API reference groups endpoints and shows support
> The API reference puts Agent API first, groups management endpoints, and shows support, availability and replacements for deprecated routes.
Source:
The API reference starts with the [Agent API](https://everruns.com/docs/features/agent-api/) and groups management endpoints by domain. Each endpoint places request and response examples beside its schemas, with language and status-code pickers.
Support labels distinguish Preview and Experimental APIs. Internal operator routes are available through **Show internal APIs**. Deprecated routes remain reachable and point to a replacement when one exists; an unannounced removal date stays unannounced.
Search finds endpoints by name, path or operation ID. Existing reference URLs remain valid, and group pages appear in the sitemap and as Markdown for agents. Duplicate operation IDs no longer hide distinct endpoints. Grouping, support and migration guidance are generated from OpenAPI.

---
# Model card links on the Models page
> Models with a published vendor model card or system card now link to it from the Models page and carry the link in their model profile.
Source:

Each model on the **Models** page now shows a **Model card** link next to its release date when the vendor has published one: Anthropic’s system cards for Claude, OpenAI’s system cards for GPT-6 and GPT-5.6, and Meta’s card for Muse Wonder. The card is where the vendor documents evaluations, safety testing and known limits.
The same link is in the model profile the API returns:
```json
{
"name": "Muse Wonder",
"family": "muse-wonder",
"model_card_url": "https://research.meta.ai/static/muse-wonder-model-card"
}
```
Models without a published card carry no link.
See [Providers](https://everruns.com/docs/providers/). [#4645](https://github.com/everruns/everruns/pull/4645)
---
# Muse Wonder, Meta's new top model, is available in Everruns
> Meta's Muse Wonder joins the model catalog with pricing, a 1M-token context and its model card, and every Muse model gets a reasoning effort picker up to Max.
Source:

Meta shipped **Muse Wonder** (`muse-wonder-1.0`, preview) today, and you can put it to work in Everruns right away. Wonder is built for the long jobs: extended agent runs, changes that span many files, and orchestrating sub-agents. It reads 1,048,576 tokens of context plus images, audio, video and PDFs. It costs $4.00 input, $0.20 cached and $20.00 output per million tokens. Add a [Meta](https://everruns.com/docs/providers/meta/) provider, sync models, and Wonder shows up with a link to Meta’s model card.
Want it to think harder? Every Muse model now has a reasoning effort picker, Minimal through Max (Muse Spark 1.2 stops at Extra High). Set it per message:
```bash
curl -X POST http://localhost:9300/api/v1/sessions/{session_id}/messages \
-H "Authorization: Bearer $EVERRUNS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": {"role": "user", "content": [{"type": "text", "text": "Plan the move of our billing service to the new ledger"}]},
"controls": {"reasoning": {"effort": "max"}}}'
```
---
# Web search: one tool, search providers and organization defaults
> Agents search the web with one web_search tool. Admins connect search providers and set defaults, each agent picks its own, and free search is the fallback.
Source:
Agents now search with one `web_search` tool, whichever provider answers. Keyless free search is the fallback. Automatic uses a model’s native search first, billed by that provider and counted on the model call.
**In the UI:** **Registers > Web search** lists providers and organization defaults: provider, Automatic order, fallback, result count and blocked domains. Agents choose how to search; chat shows each attempt and its response. Duplicate search tools get a warning.
**In code:** use `WebSearch::auto()` or a Platform capability config:
```json
{
"ref": "web_search",
"config": {
"provider": "brave",
"blocked_domains": [
"pinterest.com"
]
}
}
```
Hosts paying for search can install per-call wallet permits: stable engine identity, fenced dispatch, sanitized settlement, result replay and journal receipts for callback recovery. Uncertain spend stays with host reconciliation.
See [Web Search](https://everruns.com/docs/capabilities/web-search/).
---
# Parallel joins Brave as a Web Search provider
> Connect a Parallel API key as a search account, and the web_search tool sends searches to Parallel's Search API, with excerpts the model can quote.
Source:
[Parallel](https://everruns.com/docs/integrations/parallel/) is now a Web Search provider next to Brave Search. Connect a Parallel API key as an organization search account in **Registers > Web search**, then pick it in an agent’s Web search settings or place it in the Automatic order. The agent keeps its one `web_search` tool; searches go to Parallel’s Search API in its fast mode and come back with longer excerpts the model can quote. Allowed sites, blocked sites and freshness are applied by Parallel itself.
In the Framework, `WebSearch::parallel()` reads the `PARALLEL_API_KEY` environment variable, and Automatic tries Parallel after Brave when a key is set:
```rust
let agent = Agent::builder()
.capability(WebSearch::parallel().limit(5))
.build()?;
```
See [Web Search](https://everruns.com/docs/capabilities/web-search/).
---
# Prompt caching gets a retention setting and a prompt-only mode
> Ask for one hour Anthropic cache entries with retention 1h, and use the explicit strategy on Claude to cache tools and instructions but not the conversation.
Source:
The [Prompt Caching](https://everruns.com/docs/capabilities/prompt-caching/) capability takes a `retention` setting: `default`, `5m`, `30m` or `1h`. On Claude models, `1h` keeps cache entries for an hour, which suits agents that pause between turns longer than the five minute default. One hour writes cost twice the input rate, and cost estimates now include that.
```json
{
"ref": "prompt_caching",
"config": {
"retention": "1h"
}
}
```
The `explicit` strategy now works on Claude too: it caches the tool definitions and system prompt but not the conversation, for agents whose history changes from call to call.
---
# Prompt caching on Bedrock, OpenRouter Claude and Mistral
> Prompt caching now writes cache points on Bedrock Claude and Nova, turns on caching for Claude through OpenRouter, and routes Mistral by cache key.
Source:
The [Prompt Caching](https://everruns.com/docs/capabilities/prompt-caching/) capability now covers three more providers. Before, these requests went out without caching and paid the full input rate on every call.
* **Bedrock**: Claude and Nova models get `cachePoint` blocks after the system prompt, the tools (Claude only) and the latest messages. Cache reads and writes now show up in usage and cost.
* **OpenRouter**: Claude models get Anthropic’s automatic caching, with the `retention` setting honored.
* **Mistral**: requests carry a session-scoped `prompt_cache_key`, so calls that share a prefix reach the same cache.
The same capability config applies on all three; `retention` is honored on Bedrock and OpenRouter Claude models:
```json
{
"ref": "prompt_caching",
"config": {
"strategy": "auto",
"retention": "1h"
}
}
```
---
# Sessions recover from provider limits and follow the agent's model
> A session retries after a short provider rate limit, explains OpenRouter credit shortfalls, and follows the agent's default model unless one was set.
Source:
When a provider refuses a turn and asks for a wait of up to 10 minutes, such as OpenRouter’s in-flight spending limit, the session schedules one retry after that wait and says so in chat, at most 3 times an hour. An OpenRouter 402 “can only afford Y tokens” now reports `provider_quota_exhausted` with the token counts and tells you to add credits or lower max output tokens. See [handling errors](https://everruns.com/docs/how-to/handle-errors-and-cancellation/).
A new session no longer copies the agent’s default model, so changing the agent’s model reaches its existing sessions. To pin one session:
```bash
everruns sessions update session_01h9 --model-id model_01933b5a00007000800000000000001 --reason "Provider limit on the current model"
```
---
# Create read-only personal access tokens
> A personal access token can now be read-only: it reads every organization you belong to, and is refused anything that creates, changes or runs.
Source:
**In the UI:** open **Settings** > **Personal access tokens**, create a token, and pick **Read-only** instead of **Full access**. The token list shows each token’s access.
**In the API:** pass `"scopes": ["read"]` when creating a token:
```bash
curl -X POST "$EVERRUNS_URL/api/v1/auth/personal-access-tokens" \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "ci-reader", "scopes": ["read"], "expires_in_days": 90}'
```
A read-only token has read access to all your organizations, so treat it like a password. It cannot create, change, run or delete anything, manage tokens, or download the full account export; those requests fail with `403` and `"code": "insufficient_scope"`. `GET /v1/auth/me` reports the token’s `token_scope`.
**In the CLI:** [`everruns login --token`](https://everruns.com/docs/features/cli/#configure) accepts a read-only token, `everruns whoami` shows its access, and a refused command says the token is read-only.
See [Token access](https://everruns.com/docs/features/ui/#token-access).
---
# Sandbox file tools all read the managed Sandbox workspace
> With a managed Sandbox, read_many_files and list_directory now read the Sandbox's /workspace, so files the agent cloned there no longer read as not found.
Source:
On a Sandbox Worker with a managed Sandbox such as Daytona, `read_file` read the Sandbox while `read_many_files` and `list_directory` still read the Session’s own files. A repository the agent had just cloned looked missing:
```json
{
"path": "/workspace/yolop/AGENTS.md",
"error": "File not found: /workspace/yolop/AGENTS.md"
}
```
Every file tool now works on the Sandbox’s `/workspace`, and tools that only reach the Session’s files are no longer offered there. Skills and saved tool output stay readable through a fallback. See [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/).
---
# Sandboxes page with tabs, and tab links you can share
> Sandboxes, Templates and provider accounts are now tabs of one Sandboxes page, and the Models, Web search and Sandboxes tabs each have their own URL.
Source:

**Sandboxes** is now one page organized like **Models**: a **Sandboxes** tab with the fleet table and timeline, a **Templates** tab for [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/), and a **Provider accounts** tab for admins. The sidebar has a single Sandboxes entry under Building instead of a collapsed group of three.
Tabs on the Models, Web search and Sandboxes pages are now real paths, such as `/models/providers`, `/web-search/defaults` and `/sandboxes/templates`, so a tab can be bookmarked, shared or opened in a new browser tab. Old links keep working: `?tab=` links, `/sandbox-templates` and `/sandbox-provider-accounts` redirect to the new paths.
[#4653](https://github.com/everruns/everruns/pull/4653)
---
# Agents now see why a session schedule cron was rejected
> An invalid cron or timezone in create_schedule now returns the reason to the agent instead of an internal error, and Sunday crons obey the interval.
Source:
When an agent called `create_schedule` with an invalid cron expression or an unknown timezone, the tool failed with “Internal server error”, so the model could not tell what to fix and the server logged it as an error. The tool now returns the reason, such as `Invalid IANA timezone 'Mars/Base'`, and the agent can retry with a corrected schedule.
Standard 5-field crons that name weekdays by number, such as `0 18 * * 0` for Sunday evenings, are read the same way everywhere, including in embedded Framework schedules. The 5-minute minimum interval now also applies to them.
See [Session schedules](https://everruns.com/docs/capabilities/session-schedules/). [#4615](https://github.com/everruns/everruns/pull/4615)
## Update 2026-10-14
[Recurring cron interval validation](https://everruns.com/docs/changelog/2026-10-14-session-cron-interval-validation/) now rejects invalid cron and expressions without enough future occurrences to verify their minimum interval. Admission and trigger calculation share one parser.
---
# Scheduled turns in a Slack conversation post to it
> A session schedule that fires in a Slack-bound session can now send_message to that Slack conversation instead of failing with no Slack session.
Source:
An Agent that set itself a schedule with `create_schedule` inside a Slack conversation, such as a 07:00 briefing in a direct message, could not reach Slack when the schedule fired: `send_message` failed because the scheduled input carries no Slack message, so the post stayed in the session.
A scheduled turn now posts to the conversation of the session’s latest Slack message. In a direct message it posts inline, in a `per_channel` session it posts a new top-level message rather than into the last thread, and a `per_thread` session keeps its thread. The target comes only from signed Slack input, never from the schedule’s text, and the turn still shows as scheduled. A session that has never received a Slack message behaves as before. See [How the Agent Replies](https://everruns.com/docs/capabilities/slack/#how-the-agent-replies).
Watch the [Kids Duties walkthrough](https://www.youtube.com/watch?v=mbbYFif9juY): a saved run and actual 07:00 Slack briefing, with English narration and captions.
---
# Agent-login MCP tool calls now show in chats people start
> Tool calls through an MCP server acting as the agent now reach sessions people start, including the connect prompt shown before an admin signs in.
Source:
Before this fix, a tool call through an MCP server set to `actsAs: service` could vanish from a chat, API or MCP session a person started: the call ran, but the server refused to store its `tool.started` and `tool.completed` events, so the transcript never showed it.
The event now records the agent’s own identity as the actor and the person who asked as the initiator. When no admin has signed the agent in yet, the call still shows up with its connection-required result and no actor, so the prompt to authorize the agent reaches the session. See [MCP credentials](https://everruns.com/docs/integrations/mcp-credentials/).
---
# Session Trace names prompt cache resets and their cause
> When a model call loses the prompt cache the previous call built, the trace now says so and names the likely cause, such as changed tools or an idle gap.
Source:
**In the UI:** the cache bar on a model call in Session Trace now warns when the call reused less than half of what the previous call left cached, with how many tokens were lost and the likely cause: the model, tool definitions or system prompt changed, earlier messages were rewritten (for example by compaction), or the session sat idle past the cache retention. A long prompt sent with caching off gets a hint to turn on [Prompt Caching](https://everruns.com/docs/capabilities/prompt-caching/).
**In code:** the trace request usage carries `cache_reset` (`reason`, `lost_tokens`, `previous_cached_tokens`, `idle_seconds`) and `caching_would_help`:
```json
{
"usage": {
"cache_reset": {
"reason": "tools_changed",
"lost_tokens": 18420,
"previous_cached_tokens": 18420,
"idle_seconds": 42
}
}
}
```
---
# Agent sessions now run as actors instead of workflows
> One worker leases a session and runs its whole turn from the session's event log, replacing the per-step turn workflow, its queue hops and checkpoints.
Source:
Each agent session is now an actor. Storing a message marks the session runnable in the same transaction, one worker leases it and runs the whole turn, and the session’s event log is the only turn state. Before, every step of a turn was its own task on a durable queue, checkpointed after each step, with steering sent as signals.

A cancel ends only its own turn, a message sent after it always gets an answer, and a brief database error no longer fails a turn. Turns ran exactly once in chaos runs with 2 servers, 3 workers and repeated kills. [Durable execution](https://everruns.com/docs/explanation/durable-execution/) explains the model.
Framework apps drop the experimental `durable` feature ([upgrade notes](https://everruns.com/docs/framework/upgrade-notes/)):
```toml
# Before
everruns = { version = "0.47", features = ["durable"] }
# After
everruns = { version = "0.48", features = ["local"] }
```
---
# Tool-call repair drops null placeholders on optional parameters
> When a model fills optional tool parameters with null or the string null, tool-call repair now leaves them out instead of letting a strict tool reject the call.
Source:
Some models write `null` or `"null"` for every optional parameter they mean to skip. Strict tools, such as MCP servers that validate arguments against their schema, rejected those calls and the model had to retry. With the Tool Call Repair capability on, an optional parameter whose schema does not accept null is now left out of the call, so the first attempt goes through. Required parameters, and parameters that accept null, are passed as written.
See [Tool Call Repair](https://everruns.com/docs/capabilities/tool-call-repair/) and [#4674](https://github.com/everruns/everruns/pull/4674).
---
# Tool reveals keep the prompt cache on current Claude and GPT-6 models
> Tool search reveals keep the cached tool list on current Claude models and on GPT-6 and GPT-5.6 through the OpenAI API, and Session Trace shows each change.
Source:
**For agents:** sessions record their first tool list and each later change. On current Claude models through the Claude API, and on GPT-6 and GPT-5.6 through the OpenAI API, the request keeps that first tool list and sends each change (a tool revealed by tool search, or instructions a capability adds mid-session) in the conversation, so the next call still reads the cached prompt (see [Prompt Caching](https://everruns.com/docs/capabilities/prompt-caching/#tool-changes-during-a-session)). Elsewhere, a removed tool stays listed until the next change or cache expiry, and calls to it are refused.
**In the UI:** a model call in Session Trace lists the tools it added, updated or removed. A cache reset is blamed on the tools only when the tools sent to the provider changed.
**In code:** recorded changes are `everruns_contracts::tool_changes::ToolChanges`, stored on each answer and on `llm.generation` as `metadata.tool_changes`; the trace request summary carries `tool_changes`:
```json
{
"tool_changes": {
"baseline_tools": null,
"added": [
"web_fetch"
],
"redefined": [],
"removed": []
}
}
```
---
# Recovered tool failures show as issues, not red errors
> A failed tool call the agent carries on from now shows as an amber issue in chat, and events mark its severity so clients can tell it from a failed turn.
Source:
**In the UI:** when a tool call fails and the agent keeps going, the chat work log shows an amber “1 issue” marker instead of a red “1 error”, and the failed tool rows use warning styling. Red stays for work that stopped, such as a failed turn.
**In code:** a failed `tool.completed` event carries `"severity": "issue"`, and `turn.completed` carries `issue_count` with the number of failed tool calls the turn absorbed. A failed turn is still reported as `turn.failed`. The act headline now reads “with 1 issue”. The OpenAPI spec and the [event reference](https://everruns.com/docs/event-reference/) document both fields; see [Events](https://everruns.com/docs/features/events/) for how clients read them.
---
# Fixes for triggered sandboxes, blocked tools and agent prompts
> Trigger-started sessions use the organization sandbox account, tools blocked on a connection report a failure, and agent prompts lose the generic opener.
Source:
A session started by a trigger has no user, so a Daytona or E2B template set to the **Personal** provider account failed to start. It now uses the organization’s provider account when there is exactly one, as described in [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/).
The **Provider account** picker now says in plain words whose connection each choice uses and who pays, labels every account with its provider, such as **Daytona · Personal** or **Daytona · Reviews**, and links to **Provider accounts** when the organization has no account for that provider yet.
A tool call stopped by a missing connection now fails instead of returning an empty success, so the agent reads that it did not run:
```json
{
"connection_required": "daytona",
"error": "This tool did not run: its daytona connection is not set up. The person has to connect daytona, then try again."
}
```
The [Base harness](https://everruns.com/docs/built-ins/harnesses/base/) no longer puts “You are a helpful assistant.” ahead of an agent’s own prompt.
---
# Web search answers cite their sources, sentence by sentence
> The Web Citations capability links each sentence an agent backs with a web search result to that page, from the web_search tool or the model's own search.
Source:
Add **Web Citations** (`citation_web`) to an agent that has Web Search, and each sentence the model backs with a search result links to that page. Chat shows the numbered source chips, hover preview and **Sources** strip that retrieval citations already use.
```json
{
"capabilities": [
{
"ref": "web_search"
},
{
"ref": "citation_web"
}
]
}
```
It reads the URLs and result ids the model cites from `web_search` results, and the citations OpenAI, Anthropic, Gemini and OpenRouter return when the model searches on its own. Matching is exact, and a citation that cannot be placed on the answer’s text is dropped. The annotations are on the message for the API and evals, and Citation Verification checks them like any other citation.
See [Web Citations](https://everruns.com/docs/capabilities/citation-web/).
---
# Sessions no longer attach to another workspace
> Every session now gets its own workspace: create-session refuses workspace_id, and POST /v1/workspaces and everruns workspaces create are removed.
Source:
Every session now gets its own workspace. Creating a session with `workspace_id` returns `400`, and `POST /v1/workspaces` and `everruns workspaces create` are removed. Attaching was gated only by the org-wide workspace permission, so a new session on any agent could be pointed at another session’s files.
```http
POST /v1/sessions
{"agent_id": "agt_...", "workspace_id": "wsp_..."}
400 Bad Request
workspace_id is no longer supported: every session gets its own workspace.
Copy files between sessions through the Files API instead.
```
Sessions that were attached to a shared workspace before this change keep it and its files. Listing, reading, renaming and archiving workspaces, and the `/v1/workspaces/{id}/fs` file routes, are unchanged. To move files between sessions, read them from one session and write them to the other through the Files API.
See [`everruns workspaces`](https://everruns.com/docs/reference/cli/workspaces/) and [#4673](https://github.com/everruns/everruns/pull/4673).
---
# Hard approvals preserve the full command
> Yolop rejects oversized approval text, preserves complete commands through thread relays, and denies requests from older hosts after an upgrade.
Source:
Hard approvals preserve the complete command and reason through the thread relay. Commands or reasons above 32 KiB are denied rather than shortened for review, including after a session grant.
Ask the agent to split oversized work into smaller commands. After upgrading, restart older thread hosts and submit pending approval requests again. Approval once and approval for the session keep their existing sandbox scopes.
See [Yolop approvals](https://github.com/everruns/yolop/blob/main/docs/features/approvals.md) for the hard approval limits and controls.
---
# Agents can no longer read secrets back
> Session secrets are now write-only to agents: secret_store stores, lists and deletes them, but no tool returns a value to the model.
Source:
Secrets an agent stores with [`secret_store`](https://everruns.com/docs/capabilities/session-storage/) are now write-only. The agent can set, list and delete them, but `get` refuses every name, so a key no longer reaches the transcript, tool events, traces or the workspace.
Before, a `secret_store` call like this returned the value:
```json
{
"operation": "get",
"name": "OPENAI_API_KEY"
}
```
```json
{
"operation": "get",
"name": "OPENAI_API_KEY",
"value": "sk-...",
"found": true
}
```
Now the same call fails with a tool error for every name, stored or not:
```text
Secret values are write-only: no tool can read one back. ...
```
Capabilities that need a key, such as GitHub, Brave Search and the sandboxes, keep working: they read their own secret on the server. An agent that read a key to put it in a curl command should use a bound secret instead: add it as an **HTTP API** credential on the agent’s **Credentials** tab and send it from the shell, as described in [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/#api-keys-the-agent-never-sees):
```bash
curl -sS https://api.example.com/v1/items -H "Authorization: Bearer $API_KEY"
```
---
# Answers cut off by a provider error are retried
> When a model stream breaks after some answer text, the runtime retries it instead of keeping the cut-off text as the answer.
Source:
When a provider stream breaks after the model has written part of its answer, and before it has called a tool, the runtime now retries the call with the usual backoff instead of keeping the cut-off text as the answer. The retry streams under the same message id and first repeats `output.message.started` for it, so a client knows to drop the text it collected:
```text
output.message.started message_01
output.message.delta message_01 accumulated: "Here are the"
output.message.started message_01
output.message.delta message_01 accumulated: "Here are the links."
```
A client that renders `accumulated` needs no change. See [The agentic loop](https://everruns.com/docs/explanation/agentic-loop/).
---
# Stored attachments download safely in browsers
> Stored files now download as inert binary attachments while agents retain their original bytes and media types across Slack, AG-UI and A2A.
Source:
Stored attachments now download when you open their file URL, including HTML, script files, PDFs and zip archives. The download endpoint returns an inert binary media type and retains its filename and security headers, so received files cannot become active documents on the application’s origin.
Agents still receive the same bytes and media metadata, and file listings keep the original media type. Authentication and organization membership are still required. See [Channels](https://everruns.com/docs/features/channels/#download-received-files) for the download policy.
---
# Attachments stay readable after compaction, from every channel
> Files sent to an agent are kept in the session's /attachments folder, so the agent can reopen them after compaction, whichever channel they came from.
Source:
Files sent to an agent no longer disappear from its view when a long conversation is compacted. Each file is copied into the session’s `/attachments` folder, and the model sees every file by name and path, so it can open one again with `read_file`. PDFs reopen as documents, and a zip unpacks to a read-only folder next to the archive.
Files now arrive the same way from Chat, Slack, AG-UI, Public Chat, A2A, the Agent API and the MCP tools. A file that cannot be taken becomes a note the agent reads:
```text
[Attachment report.pdf not received: 31 MB is over the 25 MB limit]
```
Public Chat visitors are not signed in, so Public Chat takes at most 3 files of up to 10 MB, sent inline rather than as links.
See [Files people send](https://everruns.com/docs/features/channels/#files-people-send), and [Send files](https://everruns.com/docs/how-to/call-your-agent-from-code/#send-files) for the Agent API.
---
# Dev mode PostgreSQL requires a non-root user
> Development mode now uses the PostgreSQL lifecycle library and cleans up its temporary database when the owning server process exits.
Source:
Development mode now requires a non-root user. Previously, starting it as root switched the database process to `nobody`. Run the server under a regular account:
```bash
DEV_MODE=true ./everruns-server
```
Privileged environments can instead use external PostgreSQL with `DEV_MODE=false`. Temporary clusters now stop and remove their private data even when the owning process is forcibly terminated. Database queries, migrations, and isolated test fixtures continue to use real PostgreSQL. See [environment variables](https://everruns.com/docs/sre/environment-variables/#dev_mode) for setup details.
---
# E2B desktop computer use keeps the pointer where it was moved
> On an E2B desktop, a mouse move or held button no longer snaps back to the screen center when the desktop session has not connected yet.
Source:
With the experimental `computer_use_desktop` capability, the virtual display on an E2B desktop reset itself whenever no other program was connected to it, which happens right after the desktop starts. A `mouse_move` then reported success while the pointer sat back at the screen center, and a held mouse button or key could be released between actions. The display now keeps its state between actions, so moves, drags and held keys land where the agent put them. See [Computer use](https://everruns.com/docs/capabilities/computer-use/).
---
# Everruns 0.48.0: Agents in Slack and a stable Agent API
> Sessions run as durable actors instead of workflows, the Agent API calls one agent from code, agents get one web search tool, and Slack agents take DMs.
Source:

## Highlights
* **Durable actor model replaces the workflow model**: Each session runs as a durable actor: a worker leases the session and runs the whole turn from its journal instead of per-step durable workflows, so turns survive worker and control-plane failures and a cancel ends only its own turn ([changelog](https://everruns.com/docs/changelog/2026-10-12-sessions-run-as-actors/), [#4608](https://github.com/everruns/everruns/pull/4608), [#4629](https://github.com/everruns/everruns/pull/4629), [#4642](https://github.com/everruns/everruns/pull/4642), [#4663](https://github.com/everruns/everruns/pull/4663)).
* **Stable Agent API**: Call one agent from code with an Agent API key: create sessions, stream replies, answer its questions and approve its tools, with end users, browser origins and per-caller daily spending limits ([changelog](https://everruns.com/docs/changelog/2026-10-11-agent-api-channel/), [#4490](https://github.com/everruns/everruns/pull/4490), [#4508](https://github.com/everruns/everruns/pull/4508), [#4511](https://github.com/everruns/everruns/pull/4511), [#4514](https://github.com/everruns/everruns/pull/4514), [#4516](https://github.com/everruns/everruns/pull/4516), [#4527](https://github.com/everruns/everruns/pull/4527)).
* **Web search tools**: One `web_search` tool backed by free search, Brave, Parallel, Cloudflare Web Search or the model provider’s native search, with organization search accounts and defaults and sentence-level citations ([changelog](https://everruns.com/docs/changelog/2026-10-12-one-web-search-tool/), [#4551](https://github.com/everruns/everruns/pull/4551), [#4605](https://github.com/everruns/everruns/pull/4605), [#4611](https://github.com/everruns/everruns/pull/4611), [#4614](https://github.com/everruns/everruns/pull/4614), [#4621](https://github.com/everruns/everruns/pull/4621), [#4625](https://github.com/everruns/everruns/pull/4625), [#4635](https://github.com/everruns/everruns/pull/4635), [#4643](https://github.com/everruns/everruns/pull/4643)).
* **Agents in Slack**: One-click Slack apps take direct messages and answer them in the DM, store files sent with a message in the session, post scheduled turns to the conversation and use the agent’s avatar as the app icon ([changelog](https://everruns.com/docs/changelog/2026-10-13-slack-agents-dms-and-files/), [#4545](https://github.com/everruns/everruns/pull/4545), [#4597](https://github.com/everruns/everruns/pull/4597), [#4603](https://github.com/everruns/everruns/pull/4603), [#4641](https://github.com/everruns/everruns/pull/4641), [#4677](https://github.com/everruns/everruns/pull/4677)).
## Upgrade notes
* `api_endpoint` channels convert to Agent API channels at startup with the same keys. Old request shapes return `400 api_endpoint_retired` and `/v1/apps/{app}/api/{channel}` routes return `410`; move clients to `/v1/channels/{channel_id}` ([#4527](https://github.com/everruns/everruns/pull/4527)).
* Agents must give a reason for every change by default; an organization can turn the **Require reasons from agents** flag off to let such changes through with a warning ([#4552](https://github.com/everruns/everruns/pull/4552)).
## In the changelog
* [Agent budgets stop turns before each model call](https://everruns.com/docs/changelog/2026-10-10-agent-budgets-enforced/)
* [Agents must give a reason for every change by default](https://everruns.com/docs/changelog/2026-10-10-agent-change-reasons-required/)
* [api_endpoint channels convert to Agent API channels](https://everruns.com/docs/changelog/2026-10-10-api-endpoint-channels-converted/)
* [Rate agent replies and branch a chat from any message](https://everruns.com/docs/changelog/2026-10-10-chat-feedback-and-branching/)
* [ChatGPT plan connections no longer need a feature flag](https://everruns.com/docs/changelog/2026-10-10-chatgpt-plan-no-flag/)
* [Everruns CLI help shows a worked example for every command](https://everruns.com/docs/changelog/2026-10-10-cli-worked-examples/)
* [Outbound network policy modes, audit log and org allowlists](https://everruns.com/docs/changelog/2026-10-10-egress-policy-controls/)
* [Explicit communication lets agents choose when to reply](https://everruns.com/docs/changelog/2026-10-10-explicit-communication/)
* [GitHub triggers wake agents on CI results](https://everruns.com/docs/changelog/2026-10-10-github-ci-triggers/)
* [Pick and configure guardrail checks in the agent editor](https://everruns.com/docs/changelog/2026-10-10-guardrail-checks-editor/)
* [Built-in harnesses reorganized as a tree by use case](https://everruns.com/docs/changelog/2026-10-10-harness-tree/)
* [Label MCP tools as read only or changes things](https://everruns.com/docs/changelog/2026-10-10-mcp-tool-risk-labels/)
* [Add your own MCP servers and use them in chat](https://everruns.com/docs/changelog/2026-10-10-my-mcp-servers/)
* [Everruns agents act as Poppy personal agents](https://everruns.com/docs/changelog/2026-10-10-poppy-personal-agents/)
* [serve keeps daemon data in S3 and can require API keys](https://everruns.com/docs/changelog/2026-10-10-serve-bucket-store-and-auth/)
* [Session SQL databases survive server restarts](https://everruns.com/docs/changelog/2026-10-10-session-sql-databases-persist/)
* [Session Trace view replaces Transcript and Timeline](https://everruns.com/docs/changelog/2026-10-10-session-trace-view/)
* [Tools in Shell runs an agent’s tools from Bashkit scripts](https://everruns.com/docs/changelog/2026-10-10-tools-in-shell/)
* [Call one agent from code with an Agent API key](https://everruns.com/docs/changelog/2026-10-11-agent-api-channel/)
* [Load Everruns workflow skills through MCP](https://everruns.com/docs/changelog/2026-10-11-mcp-workflow-skills/)
* [Set an agent’s avatar from the CLI and MCP](https://everruns.com/docs/changelog/2026-10-12-agent-avatar-cli-mcp/)
* [Choose where an agent’s GitHub App lives, and manage it from the CLI](https://everruns.com/docs/changelog/2026-10-12-agent-github-org-cli/)
* [An agent’s GitHub App gets its own section, name and avatar step](https://everruns.com/docs/changelog/2026-10-12-agent-github-section/)
* [Agents can cap output tokens per response](https://everruns.com/docs/changelog/2026-10-12-agent-max-output-tokens/)
* [Agents can sync provider models with the Platform capability](https://everruns.com/docs/changelog/2026-10-12-agents-sync-provider-models/)
* [API keys a Bashkit agent uses but never sees](https://everruns.com/docs/changelog/2026-10-12-api-keys-agent-never-sees/)
* [Browser agents see their screenshots and keep their page](https://everruns.com/docs/changelog/2026-10-12-browser-agents-screenshots-focus/)
* [Cloudflare Web Search joins as a Web Search provider](https://everruns.com/docs/changelog/2026-10-12-cloudflare-web-search/)
* [Docs moved to everruns.com/docs](https://everruns.com/docs/changelog/2026-10-12-docs-move-to-everruns-com/)
* [Check out private pull requests into the managed sandbox](https://everruns.com/docs/changelog/2026-10-12-github-checkout-pull-request/)
* [GitHub tools act as one chosen identity, with narrowed tokens](https://everruns.com/docs/changelog/2026-10-12-github-tools-act-as/)
* [GitHub triggers narrow CI events by branch and workflow](https://everruns.com/docs/changelog/2026-10-12-github-trigger-ci-filters/)
* [Model card links on the Models page](https://everruns.com/docs/changelog/2026-10-12-model-card-links/)
* [Muse Wonder, Meta’s new top model, is available in Everruns](https://everruns.com/docs/changelog/2026-10-12-muse-wonder/)
* [Web search: one tool, search providers and organization defaults](https://everruns.com/docs/changelog/2026-10-12-one-web-search-tool/)
* [Parallel joins Brave as a Web Search provider](https://everruns.com/docs/changelog/2026-10-12-parallel-web-search/)
* [Prompt caching gets a retention setting and a prompt-only mode](https://everruns.com/docs/changelog/2026-10-12-prompt-cache-retention/)
* [Prompt caching on Bedrock, OpenRouter Claude and Mistral](https://everruns.com/docs/changelog/2026-10-12-prompt-caching-bedrock-openrouter-mistral/)
* [Sessions recover from provider limits and follow the agent’s model](https://everruns.com/docs/changelog/2026-10-12-provider-limit-recovery/)
* [Create read-only personal access tokens](https://everruns.com/docs/changelog/2026-10-12-read-only-personal-access-tokens/)
* [Sandbox file tools all read the managed Sandbox workspace](https://everruns.com/docs/changelog/2026-10-12-sandbox-file-tools-one-workspace/)
* [Sandboxes page with tabs, and tab links you can share](https://everruns.com/docs/changelog/2026-10-12-sandboxes-tabs/)
* [Agents now see why a session schedule cron was rejected](https://everruns.com/docs/changelog/2026-10-12-schedule-cron-errors-reach-agent/)
* [Scheduled turns in a Slack conversation post to it](https://everruns.com/docs/changelog/2026-10-12-scheduled-turns-post-to-slack/)
* [Session Trace names prompt cache resets and their cause](https://everruns.com/docs/changelog/2026-10-12-session-trace-cache-resets/)
* [Agent sessions now run as actors instead of workflows](https://everruns.com/docs/changelog/2026-10-12-sessions-run-as-actors/)
* [Tool reveals keep the prompt cache on current Claude and GPT-6 models](https://everruns.com/docs/changelog/2026-10-12-tool-changes-keep-prompt-cache/)
* [Recovered tool failures show as issues, not red errors](https://everruns.com/docs/changelog/2026-10-12-tool-failures-shown-as-issues/)
* [Web search answers cite their sources, sentence by sentence](https://everruns.com/docs/changelog/2026-10-12-web-search-citations/)
* [Slack agents answer DMs and keep the files people send](https://everruns.com/docs/changelog/2026-10-13-slack-agents-dms-and-files/)
* [Web search no longer fails on some result snippets](https://everruns.com/docs/changelog/2026-10-13-web-search-snippet-crash/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.48.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0480---2026-10-13) for every pull request in this version.
---
# Gemini 4 Argon continues long responses across requests
> Gemini 4 Argon can continue beyond a single stream, preserving the total output budget and combining text, reasoning, and billed usage across requests.
Source:

Everruns now supports long decode on Gemini 4 Argon. Raise the output budget above 65,536 tokens to let one model response continue across multiple requests. The default stays at 65,536; input, thinking, and output share the unified 1,048,576-token context window.
In an agent package:
```toml
schema_version = 1
name = "deep-reasoning"
instructions = "Work through the problem carefully."
max_output_tokens = 100000
[model]
provider = "gemini"
model = "gemini-4-argon"
```
Text and reasoning stream as one response. Usage combines every completed hop, including thinking and repeated input. Invalid continuation tokens or a failed hop fail the call. Cancellation stops local continuation requests.
Argon also gains its built-in model profile and low, medium, and high thinking levels. See [Google Gemini](https://everruns.com/docs/providers/gemini/) for configuration, host timeouts, and recovery limits.
## Update 2026-10-14
Argon is also an enabled favourite and the preferred default for new Gemini setups. Read the [defaults and favourites update](https://everruns.com/docs/changelog/2026-10-14-gemini-argon-default/).
## Update 2026-10-19
Grounded responses now preserve citations and search usage across continuation hops. Read the [grounding fix](https://everruns.com/docs/changelog/2026-10-19-gemini-continuation-grounding/).
---
# MCP resource reads enforce conversation attachments
> Resource listing and reading retain the conversation's attached-server policy when the runtime binds a request to its current input message.
Source:
MCP resource listing and reading now retain the conversation’s attached-server policy through tool registration and execution. The same policy applies when the runtime binds a request to the input message that started the turn.
Allowed resources continue to use the server’s configured sign-in and network rules. Deferred servers remain available when attached. Attach a server to the conversation before asking the agent to read its resources; see [Reading a server’s resources](https://everruns.com/docs/features/mcp/#reading-a-servers-resources).
---
# Remote workers run turns without an encryption key on the API
> A standalone gRPC worker no longer fails every turn when the API has no SECRETS_ENCRYPTION_KEY; it skips checkpoints like the in-process worker.
Source:
A self-hosted API started without `SECRETS_ENCRYPTION_KEY` refused the encrypted compaction checkpoint reads of a standalone gRPC worker, so every turn on that worker failed with `processing_error`. The worker now asks the API at startup, and again on a refused read, whether it can store checkpoints. Without a key it runs turns without compaction checkpoints, native async tool journals or the OpenAI Agents API runtime, the same way the in-process worker already did, and logs a warning once. Set the key to keep those features; see [Environment variables](https://everruns.com/docs/sre/environment-variables/).
---
# Everruns SDKs drop the agent version methods
> The agent version endpoints are gone from the API, so the Rust, Python and TypeScript SDKs remove their version methods. Use change history instead.
Source:
The server replaced the `/v1/agents/{id}/versions` endpoints with the generic [change history](https://everruns.com/docs/features/change-history/), so the SDKs no longer offer `list_versions`, `create_version`, `set_default_version`, `diff_versions`, `fork_version` or `rollback_version` (camelCase in TypeScript). Code that called them failed against current servers anyway.
Before:
```text
await client.agents.list_versions("agent_123")
```
After, read the agent’s history over REST until the SDKs wrap it:
```bash
curl -H "Authorization: Bearer $EVERRUNS_API_KEY" \
https://app.everruns.com/api/v1/history/agent_123
```
`MessageInput` now only takes the `user` role; send tool results with `create_tool_results`.
---
# Slack agents answer DMs and keep the files people send
> One-click Slack apps open a Messages tab, answer a direct message in the DM, store files sent with a message, and use the agent's avatar as the app icon.
Source:
An agent added to Slack with the one-click app now works as a direct-message contact. The generated app enables the Messages tab, so people can DM the bot, and a top-level DM gets its answer inline in the DM rather than in a thread. A message written inside a thread still gets its reply in that thread.
Files sent with a Slack message reach the agent. Everruns downloads them with the bot token through the organization’s egress policy, up to 10 files of 25 MiB each, and stores them read-only in the session:
```text
/attachments//quarterly-report.pdf
```
Changing the agent’s avatar also updates the Slack app icon. See [Connect an Agent to Slack](https://everruns.com/docs/capabilities/slack/#connect-an-agent-to-slack).
Watch [Kids Duties in Everruns and Slack](https://www.youtube.com/watch?v=mbbYFif9juY) for a real configuration, saved execution and Slack conversation. The original messages are Ukrainian; narration and captions are English.
[Slack integration: Kids Duties agent in Everruns](https://www.youtube-nocookie.com/embed/mbbYFif9juY?rel=0)
---
# Tool calls cut off by a crash are rerun when it is safe
> After a worker crash, calls that never started, read-only and idempotent calls run again, and MCP servers get a stable idempotency key.
Source:
When a worker stops in the middle of a turn, the turn resumes on another worker, and fewer tool calls now end as “interrupted”:
* A call that had not started yet runs, even one that must run at most once, such as sending an email: such a call starts only after its start is recorded.
* A call to a tool marked read-only or idempotent runs again. This includes MCP tools that declare `readOnlyHint` or `idempotentHint`.
* Every MCP tool call carries an idempotency key that stays the same when the call runs again, so a server that honors it can return the first result instead of acting twice:
```json
{
"method": "tools/call",
"params": {
"name": "send_email",
"arguments": {},
"_meta": {
"io.everruns/idempotency-key": "9b6c0f3e-5d1a-5e4b-8f2a-0c7d4e6a1b23"
}
}
}
```
A short outage of the control plane also no longer stops a running turn, as long as it ends within the lease time.
See [Durable Execution](https://everruns.com/docs/explanation/durable-execution/).
---
# Schedule triggers read cron weekdays from Sunday as 0
> A 5-field cron such as 0 9 * * 1-5 on an Agent trigger or durable schedule now runs Monday to Friday, and 0 18 * * 0 runs on Sundays.
Source:
Agent schedule triggers and durable schedules read the weekday field of a 5-field cron one day off: `0 9 * * 1-5` ran Sunday to Thursday, and `0 18 * * 0` was rejected. Weekdays now count 0-6 from Sunday, with 7 also Sunday, the same as session schedules and standard cron.
Existing triggers keep the days they run on; to switch one, enter its 5-field cron again. The cron editor now labels a stored 7-field expression with the days it actually runs, so an older `0 0 9 * * 1-5 *` reads “Sunday through Thursday”. See [Agent triggers](https://everruns.com/docs/features/agent-triggers/).
`#[schedule]` crons in an `everruns-serve` app read 5-field weekdays the same way, locally and when deployed. See [Serve](https://everruns.com/docs/framework/serve/).
---
# Visti and Stend join the product footer
> The Everruns landing site and documentation now link to Visti and Stend in their shared Products footer, making both sites easier to find.
Source:
The Everruns landing site and [documentation](https://everruns.com/docs/getting-started/introduction/) now include [Visti](https://visti.sh) and [Stend](https://stend.sh) in the **Products** column of the footer. Scroll to the bottom of any page and select either product to open its site in a new tab. The same product links appear on both sites through their shared footer.

## Update 2026-10-13
[Tuika](https://tuika.dev/) now appears alongside Visti and Stend in the **Products** column on both the landing site and documentation. Select **Tuika** in either footer to open its site in a new tab.

---
# Web search no longer fails on some result snippets
> A Brave or free search whose result text had an ampersand a few characters before a dash, curly quote or accented letter no longer fails.
Source:
A [Web Search](https://everruns.com/docs/capabilities/web-search/) call through Brave Search or free search could fail outright when a result’s title or snippet held an `&` followed about ten characters later by a character outside plain ASCII, such as a dash, a curly quote or an accented letter. Those results are now read like any other, and the search returns them.
---
# Yolop Linux sandbox blocks asynchronous socket paths
> Linux sandboxed shell modes deny io_uring entry syscalls while preserving direct socket rules, Unix sockets and synchronous toolchain operations.
Source:
Linux shell containment now denies all three io_uring entry syscalls in `read-only` and `workspace-write` mode, closing an asynchronous path outside the direct socket filter. Internet and packet socket rules remain in place; direct Unix sockets and ordinary synchronous toolchain operations remain available.
Enable workspace containment through the existing setting:
```toml
sandbox_mode = "workspace-write"
```
Programs requiring io_uring need a synchronous fallback. Host reads, local Unix sockets and shared temporary files retain their documented limits. See [Yolop shell sandboxing](https://github.com/everruns/yolop/blob/main/docs/features/sandboxing/sandboxing.md) for modes and platform requirements.
---
# Monitor agent execution and actor leases at fleet scale
> Follow thousands of sessions through model iterations and tools, map orgs to workers, and inspect actor leases in live Monitoring views.
Source:
**Monitoring** shows the execution fleet moving through runnable work, model iterations, tools, and waiting. Flow is the default; Density shows changes over time. Snapshots refresh every second, with pause and replay of observed history.

The Workers tab maps organizations to worker ownership. Inspect an actor’s lease expiry, last renewal, fence, cursors, and ownership history; Metrics provides the conventional counters alongside these views.

Organization admins opt in through **Settings → Features → Monitoring**. Their view stays within that org; cross-org monitoring requires platform access. See the [Monitoring guide](https://everruns.com/docs/observability/monitoring/).
---
# Concurrent session creation reuses one runtime account
> Simultaneous first sessions for the same management account now reuse one runtime identity, avoiding intermittent conflicts during session creation.
Source:
Creating several sessions at once for a management account’s first use in an organization now reuses the same runtime account. Previously, competing requests could return HTTP 409 with `already_exists` while creating that account’s default identity binding, even though each request was creating a separate session.
No client changes are required. Each successful request still creates its own session, while the runtime account remains scoped to the same organization and verified identity. Revoked identity bindings remain unavailable.
---
# Framework and serve keep files in the session
> Session::attach keeps a file in the session's /attachments folder, serve's message endpoint takes a files list, and embedded Slack files are kept too.
Source:
Files sent to a Framework or serve agent now stay readable for the whole conversation. `Session::attach` writes a file read-only to `/attachments//` in the session’s own files and returns the part that sends it. The agent can open it again with `read_file` after compaction, and a zip unpacks into a folder next to it.
```rust,no_run
let report = session
.attach("report.pdf", Some("application/pdf"), std::fs::read("report.pdf")?)
.await?;
let mut message = InputMessage::user("Summarize the attached report.");
message.content.push(report);
session.run(message).await?;
```
A serve app’s message endpoint takes the same `files` list as the Agent API, and embedded Slack files are kept the same way. See [Send files](https://everruns.com/docs/framework/sessions/#send-files) and the [serve wire API](https://everruns.com/docs/framework/serve/#wire-api).
---
# Gemini setups prefer Argon and include it in favourites
> Gemini 4 Argon is the preferred default for new Gemini setups and an enabled favourite in the server model catalogue. Existing defaults stay selected.
Source:

**In the UI:** Gemini 4 Argon now appears as an enabled favourite in the server’s built-in Google Gemini model catalogue. Its model profile supplies context limits, pricing, and reasoning controls in the model picker.
**In the API:** the favourites list includes Argon:
```http
GET /v1/models?favorites_only=true
```
When a new Gemini setup needs an organisation default, it prefers Argon over other Gemini models. An existing working default stays selected, and other providers retain their selection policy. See [Google Gemini](https://everruns.com/docs/providers/gemini/) for configuration and long decode support.
---
# More reliable Framework agent sessions
> Local background tasks recover after owner loss, MCP discovery pauses failed authentication, and provider tool calls preserve optional arguments.
Source:
Framework hosts built with `LocalRuntimeBuilder` now settle background tasks whose executor is known lost. Recovery checks ownership and the current attempt atomically, preserves live and unknown owners, and never replays interrupted commands. Losing an owner process reports an unverified execution outcome rather than confirming cancellation. Terminal recovery notifications persist across restarts. See [local host deployment](https://everruns.com/docs/framework/deployment/#advanced-local-hosts).
Background execution checks cancellation before starting a tool. Task lists keep diagnostic summaries compact, with full details available through `get_task`.
MCP discovery pauses authentication failures until reload or changed configuration; transient failures retry after a short cooldown. Rejected tool calls restore their full schemas on the next step. Strict provider tool calls remove only optional null placeholders before recording or approval. OpenRouter daily limits fail without short retries and preserve the supplied reset time.
---
# Local stress tests use successful offline tool workloads
> Fix local proxy connection failures during stress tests, seed successful offline tool workloads, and improve latency and failure reporting.
Source:
Local proxy connections use IPv4 loopback, fixing HTTP 502 responses during concurrent request bursts. Failed API connection attempts have bounded retries; POSTs already received by the API are not retried. Local setup installs Caddy 2.11.4, and startup rejects older versions that lose POST bodies on failed dials.
The simulated load driver uses a seeded session file for its default research workload, exercising three rounds of successful offline tools. Deferred tools also retain their original argument schemas in llmsim. The driver checks that the selected model uses llmsim, waits for SSE connectivity, reports p95 and average latency, and returns a failing exit status for incomplete runs. Bounded HTTP connection pools keep large runs from stalling the load generator and inflating client measurements.
Run `python3 scripts/load-test.py --model `. Keep the scenario mix with results: the file workload differs from earlier runs that chose arbitrary read tools.
---
# OpenTelemetry fixes export and cached-token totals
> OpenTelemetry fixes background export, cached-token totals, repeated tool-call IDs, error privacy and Framework shutdown failure reporting.
Source:
OpenTelemetry exports now use an HTTP client compatible with background batching. Repeated provider tool-call IDs stay isolated across sessions and turns, while prompt totals include cache reads and writes. Errors before a provider stream opens produce model spans.
With content capture off, error status and exception messages carry safe classifications. Detailed error text follows the same opt-in as messages and tool payloads; see [OpenTelemetry](https://everruns.com/docs/observability/opentelemetry/).
[Framework observers](https://everruns.com/docs/framework/observability/) report exporter flush failures and keep shutdown deadlines responsive. Pass an explicit tracer provider to have Engine shutdown flush it, then use the telemetry guard’s async shutdown before exiting.
---
# PACT delegated turns discover the user's MCP tools
> PACT delegated turns now discover account tools with the user's session grant, so scoped order lookups and approved cancellations reach the company API.
Source:
A [PACT delegated turn](https://everruns.com/docs/features/a2a/#personal-agents-pact) could hold a valid account token but fail to discover its MCP tools when the customer had only a session grant. Discovery now uses that grant’s user identity for the configured server, so account tools are available without creating a persistent connection.
The grant must belong to the user invoking the turn. Another user’s grant, an unattended turn, or a grant for another MCP server cannot supply that identity. The company API continues to check the granted scope on each action.
The walkthrough shows order lookup with read-only permission, cancellation refused before approval, and a verified signed receipt after approval. It uses a local demo client and synthetic store data.
[PACT account consent and signed receipts in Everruns](https://www.youtube-nocookie.com/embed/9FZiYv5PsWY?rel=0)
[Watch on YouTube](https://www.youtube.com/watch?v=9FZiYv5PsWY).
---
# Recurring schedules reject unverified cron intervals
> Session schedules now reject invalid cron and insufficient future occurrences at admission, with the same parser used to calculate triggers.
Source:
Recurring session schedules now reject cron expressions whose minimum interval cannot be verified. Invalid cron and expressions with fewer than two future occurrences return an actionable rejection before schedule storage, instead of passing the interval check without evidence.
Admission and trigger calculation use the same cron parser in the hosted Platform and embedded Framework. Standard weekday numbering, supported cron forms, timezone interpretation and schedule-count limits are preserved. For a single future run, use a one-shot schedule with `scheduled_at` instead of a finite recurring cron.
See [Session schedules](https://everruns.com/docs/capabilities/session-schedules/).
---
# A Slack integration overview for building Agents
> The Slack integration page now brings together a real product walkthrough, English conversation examples, Agent-building steps, and setup links.
Source:
[Build Agents for Slack](https://everruns.com/docs/integrations/slack/) now opens an integration overview with a real Kids Duties walkthrough, English recreations of its Slack conversation and daily briefing, and steps for building and publishing an Agent. The page previously redirected to the capability reference.
Use **Build a Slack Agent** to reach the building steps, switch between the two conversation examples, or follow **Publish an existing Agent** to the installation guide. The detailed Slack reference remains available for scopes, session routing, actions and troubleshooting. Slack navigation now uses the recognizable Slack mark.
## Update 2026-10-14
Example tabs now align consistently, with layout and spacing isolated from article typography.

---
# Tool events expose scoped worker CPU observations
> Native hosts report worker-thread CPU consumed while polling a tool executor, separately from elapsed time and remote or child-process work.
Source:
Read `worker_thread_cpu_ns` on `tool.completed` events to inspect native worker CPU separately from elapsed tool duration:
```json
{
"type": "tool.completed",
"data": {
"tool_call_id": "call_1",
"tool_name": "read_file",
"success": true,
"status": "success",
"duration_ms": 250,
"worker_thread_cpu_ns": 1800000
}
}
```
Linux and macOS hosts measure CPU around each executor-future poll, excluding async suspension. Clock-bracketing overhead is included; child processes, separately spawned or blocking work, and remote compute are outside the scope. An absent field means unobserved, not zero. See the [Event Reference](https://everruns.com/docs/event-reference/) for the observation contract.
---
# Search model catalogs and test a model from Yolop
> Yolop searches provider catalogs, reports model ping latency and errors, and accepts qualified model references without silently choosing another model.
Source:
Search a provider’s model catalog without leaving Yolop, then send a small ping to check the exact model before adding it to your menu.
```bash
yolop config models search nemotron --provider openrouter
yolop config models test openrouter/nvidia/nemotron-3-ultra-550b-a55b:free
```
Search results include provider and model arguments for `config models add`, names, and context lengths when available. The test uses the selected provider and reasoning settings, sends no tools or session history, reports latency, and exits nonzero on failure. Provider charges may apply.
`-m` accepts a menu label or `provider/model` reference. Invalid explicit selections fail without substituting another model, including when a fetched catalog does not contain the requested model.
See [Providers and models](https://github.com/everruns/yolop#providers-and-models).
---
# React session streams can suspend while idle
> Opt in to closing settled session event streams in the React UI client, with durable replay when sending, answering approvals, or returning to the tab.
Source:
React UI embeddings can now close settled session event streams with `followIdle: false`:
```tsx
const { data: events, resume } = useEvents(sessionId, { followIdle: false });
```
Message sends, approval answers, and tool results reopen the stream from its durable cursor. Returning to the tab, reconnecting the network, or calling `resume()` catches up events received while suspended. Active turns, parked tools, pending sign-ins, and unfinished background tasks keep following.
Continuous following remains the default for schedules, channels, and external updates. Idle suspension affects this event hook only; independent subscriptions remain open. See [Events](https://everruns.com/docs/features/events/) for custom mutation handling and the external-update tradeoff. Resource savings have not been measured.
## Update 2026-10-15
Optimistic sends now reconcile with stored inputs before rendering, preventing a transient duplicate message or turn-status row when an event arrives before its request acknowledgement.
---
# Find failed agent turns from the Agents list
> Agent failure counts open sessions filtered by failure time, preserve recovered sessions, and link to the exact failed turn in the existing Trace view.
Source:

Click **1 failed** on the Agents list to see sessions with failed turns during the chart’s 24-hour window. Hover or keyboard-focus the chart to see its window and hourly scale.
The filtered Sessions tab includes older sessions and sessions that later recovered, with separate counts for failed turns and affected sessions. Each card shows the latest matching error and a **View failure** link to its exact turn in Trace. Session badges distinguish a failed last turn, completion and cancellation, while a running turn keeps its live status.
See [Investigate failed turns](https://everruns.com/docs/observability/#investigate-failed-turns).
---
# Agent session lists now match their session count
> The agent Sessions tab includes archived conversations, with archive labels, channel information and separate creation and last-message times on cards.
Source:
An agent’s **Sessions** tab now includes archived conversations, matching the session count shown in its tab badge. Archived cards carry an **Archived** label, so older conversations remain distinguishable from current ones.
Session cards also show the channel, creation time and last-message time. Hover over a time to see its exact date and time. The last-message time tracks user messages and completed assistant replies; editing a session or recording tool activity does not change it. Sessions without messages say **No messages yet**.

---
# Connect your coding agent from the Everruns UI
> Copy one setup prompt for Claude Code, Codex, Cursor or Gemini CLI, with deployment-aware instructions and model-provider setup in its existing flow.
Source:

Choose **Connect your coding agent** from the user menu, the Agents first-use state or after organisation setup. Copy the prompt into Claude Code, Codex, Cursor or Gemini CLI to follow the maintained setup instructions. Agent icons identify compatible tools; everyone uses the same prompt.
The prompt preserves your deployment’s MCP endpoint and authentication mode. Your agent follows its live documentation and discovery metadata, then guides you through browser sign-in when needed. Model-provider setup stays in the existing onboarding flow, with **Add a model provider** available after a skip.
See [Use in AI Tools](https://everruns.com/docs/getting-started/use-in-ai-tools/) for manual installation and managing connected clients.
---
# Everruns shares product and workflow skills
> Find product concepts, CLI examples, agent-authoring workflows, and Rust guidance through shared skills in Everruns and its coding-agent plugin.
Source:
The general `everruns` skill now introduces product concepts, documentation, and companion workflows. `everruns-cli` covers command usage and examples; `everruns-framework` holds Rust development guidance. The existing MCP and agent-authoring guides remain focused on their workflows. The MCP guide covers connection setup and lists the four skills exposed by the MCP server, with detailed [Cloud authentication guidance](https://everruns.com/auth.md).
The [Platform capability](https://everruns.com/docs/capabilities/platform/) supplies the general, CLI, and authoring skills as read-only files loaded on demand:
```json
{
"name": "everruns-cli"
}
```
Pass this argument to `activate_skill`. Both Platform interfaces receive the same skills. MCP clients can discover the general and CLI guides alongside the existing MCP workflows. The [coding-agent plugin](https://everruns.com/docs/getting-started/use-in-ai-tools/) distributes all five shared skills, including Framework references, and keeps its additional task guides for running, debugging, evaluating, and shipping agents.
---
# Deno Deploy sandbox integration removed
> The experimental Deno Deploy sandbox provider, its deno_* tools, the deno-coder demo agent, and related docs are removed.
Source:
The experimental Deno Deploy sandbox provider is removed. The `deno_*` sandbox tools, the Deno connector and feature flag, leased-resource cleanup for Deno sandboxes, and the `deno-coder` demo agent are gone, along with the Deno rows in the capability catalog and docs.
Existing Deno connections and sessions that reference `deno_*` tools will no longer resolve; use Daytona, E2B, Sprites, Modal, or the container sandbox instead.
See the [capabilities overview](https://everruns.com/docs/capabilities/) for the supported sandbox providers.
```bash
# Deno connections no longer resolve; use a supported sandbox provider
everruns connections set daytona
everruns connections list
```
---
# Slack workspace consent stays with its initiating browser
> Slack manager workspace connections now require the initiating browser and a single-use transaction, with independent tabs and safe consent retries.
Source:
Connecting a workspace through a configured Slack manager app now binds consent to the browser that started it. Each connection attempt expires after thirty minutes and can finish only once, including when two callbacks arrive together. Separate tabs keep their own attempts.
Complete Slack consent in the same browser. If consent expires, is cancelled, or fails during exchange, start a new connection attempt. A callback opened in another browser cannot connect the workspace or invalidate the original attempt. See [Publish an Agent to Slack](https://everruns.com/docs/how-to/publish-to-slack/) for setup guidance.
---
# User MCP management respects approval settings
> Adding and enabling personal MCP servers now follow the agent's shared Tool Approval mode, preserving URL restrictions and separate OAuth consent.
Source:
Agents with [User MCP Servers](https://everruns.com/docs/capabilities/user-mcp-servers/) management enabled now honor the shared [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/) settings when adding or enabling a server. Normal and protective modes ask first. With Tool Approval absent or set to `off`, management runs without a hidden approval gate.
Agents with management disabled also stop advertising management tools to the model. Management still acts only for the person chatting. Catalog restrictions, custom-URL validation and separate OAuth consent continue to apply.
---
# Run Everruns provider drivers with WASM host fetch
> Use shared OpenAI and OpenRouter chat drivers in Workers and Durable Objects with an injected HTTP adapter, including streaming and tool calls.
Source:
OpenAI-compatible and OpenRouter chat drivers now run on `wasm32-unknown-unknown`. Applications supply an HTTP adapter backed by host `fetch`; the existing drivers handle request encoding, provider options, streaming responses, tool calls, retries and provider errors.
```rust
use std::sync::Arc;
use everruns_contracts::http_transport::HttpTransport;
use everruns_drivers::openrouter::OpenRouterChatDriver;
fn driver(fetch: Arc) -> OpenRouterChatDriver {
OpenRouterChatDriver::new().with_transport(fetch)
}
```
The celld engine example uses the shared driver with a Workers-compatible adapter. Integration tests exercise model and tool-call exchanges inside both workerd Worker handlers and Durable Objects. Native applications keep their existing HTTP client unless they inject a transport.
See [Models and Providers](https://everruns.com/docs/framework/models-and-providers/#wasm-and-host-fetch) for the adapter contract and supported chat drivers.
---
# Control Yolop continuation budgets and trace stop reasons
> Yolop supports five automatic continuations, configurable limits, durable stop and retry reasons, and OpenTelemetry budget diagnostics.
Source:
Yolop allows five automatic continuation turns by default, with configurable turn, token, and elapsed-time limits. Ask Yolop to change the budget or use:
```bash
yolop config set continuation_max_turns 10
yolop config set continuation_max_tokens 512000
yolop config set continuation_max_seconds 3600
```
The first reached limit pauses work and asks for approval to resume with a fresh budget, without prescribing reply words. Named profiles can override the limits; attached changes apply at the next continuation check without resetting usage.
Decisions, retry reasons, budget usage, and exhausted limits survive restarts in private session logs. Find earlier stops with:
```bash
yolop sessions search --query "continuation exhausted"
```
Enabled trace extensions receive the same diagnostics. The Logfire exporter emits OpenTelemetry budget spans with decision attributes correlated to the completed turn. See [Plans and completion](https://github.com/everruns/yolop/blob/main/docs/features/tools.md#plans-and-completion).
---
# MCP setup verifies login without restarting
> Yolop verifies MCP connections after browser sign-in, reports pending authorization honestly, and loads newly installed tools in the same session.
Source:
MCP setup now distinguishes saved configuration, pending browser authorization, and a working connection. Requests such as Cloudflare’s published setup prompt continue through sign-in and verification. Newly installed servers become available in the same session without restarting.
```sh
yolop mcp test cloudflare
```
The command lists the server’s tools or exits with an authentication or transport error. OAuth still requires your consent in the browser; saving a server does not imply that sign-in succeeded. See the [MCP server guide](https://github.com/everruns/yolop#mcp-servers) for supported transports and configuration scopes.
---
# Meta Muse sessions continue after tool calls in Yolop
> Yolop keeps Meta Muse sessions running after tool calls by replaying context when a response ID cannot be used for server-side continuation.
Source:
Some Meta Muse responses carry IDs that Meta will not accept as a `previous_response_id`. A Yolop session could finish a tool call, then stop with HTTP 400 when it tried to continue from that ID.
Yolop now replays the saved conversation for those responses. Responses with IDs that Meta accepts continue using server-side conversation state. The session history and selected model stay the same.
See [Providers and models](https://github.com/everruns/yolop#providers-and-models) for Meta setup and model selection.
---
# New providers: Mistral, Fireworks, Vercel, Cloudflare, MAI
> Yolop setup and --provider now reach Mistral, Fireworks, Vercel, Cloudflare, and MAI, with Google on the native Gemini driver.
Source:
Yolop can now talk to five more providers. In `/setup` or with `--provider`, pick **Mistral**, **Fireworks AI**, **Vercel AI Gateway**, **Cloudflare Workers AI**, or **Microsoft MAI**, each with its own API key flow. Cloudflare also asks for the account ID and MAI for the deployment endpoint, stored alongside the key.
Google moved to the native Gemini driver under the same `GEMINI_API_KEY` or `GOOGLE_API_KEY`, so existing setups keep working with no changes.
```shell
export MISTRAL_API_KEY=...
yolop --provider mistral -p "summarize this repo"
```
See [Providers and models](https://github.com/everruns/yolop#providers-and-models) and [#860](https://github.com/everruns/yolop/pull/860).
---
# Automatic session titles with a shared small model
> Enable automatic session naming, choose a dedicated model and provider, and keep explicit titles protected while topic checks back off over time.
Source:
**In the UI:** Enable **Automatic session titles** on a harness, agent, or session. Under **Models → Defaults**, choose a shared **Small model** for lightweight work. Capability settings can inherit that default, use the session model, or pin a model and provider.


**In code:** Attach the capability without enabling title tools:
```json
{
"ref": "automatic_session_titles",
"config": {
"retitle": true
}
}
```
[Automatic session titles](https://everruns.com/docs/capabilities/automatic-session-titles/) run outside the answer path. Sparse checks combine keep-or-rename decisions in one request, with a cooldown and increasing message intervals. Explicit titles always win, including renames made while generation is running. The [organization small-model default](https://everruns.com/docs/providers/#organization-small-model-default) is independent of the main chat and Decisions models.
---
# ChatGPT Plan removed from Platform setup
> The Platform UI no longer offers ChatGPT Plan sign-in or its models because its browser callback is unavailable to remote users.
Source:

ChatGPT Plan no longer appears in the Platform provider setup, provider pages, model lists, or chat controls. Its open-source sign-in flow requires a browser callback on `127.0.0.1`, which cannot reach a remote Everruns server. Existing connections remain stored, and the framework driver remains available to custom hosts. See the [ChatGPT Plan framework guide](https://everruns.com/docs/features/chatgpt/) for supported usage.
---
# Framework hosts can brand ChatGPT login completion
> The ChatGPT Plan loopback helper now accepts custom HTML for its successful callback page while retaining its existing plain-text default.
Source:
Framework hosts can show their own branded completion page after a successful ChatGPT Plan login. The loopback helper keeps its existing plain-text response unless the host supplies HTML, so current integrations do not need to change.
```rust
use everruns_drivers::chatgpt::login::LoginAttempt;
fn brand_login(attempt: LoginAttempt, html: String) -> LoginAttempt {
attempt.with_success_page_html(html)
}
```
The custom page is returned only after the callback path and state match the pending attempt. See the [ChatGPT Plan framework guide](https://everruns.com/docs/features/chatgpt/) for the loopback sign-in flow.
---
# OAuth MCP servers with path-specific metadata now connect
> The MCP connection check and sign-in now find OAuth metadata at every location the MCP authorization spec allows, so these servers no longer read unreachable.
Source:
Some remote MCP servers that sign in with OAuth showed **The server’s sign-in service did not answer correctly** in the MCP catalog, and their sign-in failed. Everruns looked for OAuth metadata in only one place per server. A server that published it only at its endpoint path (for example `/.well-known/oauth-protected-resource/mcp`), only as OpenID Connect configuration, or not at all was reported as unreachable.
The connection check and sign-in now try each location the MCP authorization spec lists, in order. A server with no protected-resource metadata is treated as its own sign-in service. Select **Check again** on an affected catalog entry to record the new result. See [Everruns as an MCP client](https://everruns.com/docs/features/mcp/#everruns-as-an-mcp-client).
---
# Connecting GitHub as yourself acts as you, not the shared bot
> Your own GitHub connection now signs you in to GitHub and acts as you; shared Everruns App installations move to organization links.
Source:
Connecting GitHub under your own connections now signs you in to GitHub, and tools that act as you use your own GitHub access. Previously that connection installed the shared Everruns App, so your actions showed up as the shared bot. Tokens refresh on their own; if GitHub refuses a refresh, connect GitHub again.
```text
Before: GET /v1/user/connections/github/authorize → install the Everruns App (acts as everruns[bot])
After: GET /v1/user/connections/github/authorize → sign in to GitHub (acts as you)
```
Existing shared App installations move to organization links: each one becomes a link in the organization of whoever installed it first, and an agent that used one now uses that link. If you connected GitHub for yourself before this change, connect it again to act as yourself. Org admins manage links in **Settings > Organization > Everruns GitHub App**. See [Summarize GitHub pull requests](https://everruns.com/docs/how-to/summarize-github-pull-requests/).
---
# MCP client authorization follows callback redirects
> Approving an MCP client now completes even when its callback forwards the browser to another site, app, or local port after receiving a code.
Source:
When you approve an MCP client such as Cursor, the browser can now follow the client’s callback through additional redirects. Previously the authorization code could reach the registered callback while the browser stayed on the Everruns consent page, making **Authorize client** appear to do nothing.
The consent page still validates the registered callback and binds approval to your signed-in session. To connect or retry a client, follow [Use Everruns in AI tools](https://everruns.com/docs/getting-started/use-in-ai-tools/).
---
# Turns no longer fail when a provider asks to wait too long
> A provider rate limit whose Retry-After exceeds the retry budget now falls back to backoff and retries, instead of failing the turn at once.
Source:
When a model provider answered a rate limit or outage with a `Retry-After` longer than the 30-second retry budget, for example OpenRouter asking for 45 seconds, the turn failed within milliseconds without a single retry. The request now falls back to exponential backoff and retries inside the budget.
When the budget does run out, the error reports the time actually spent next to the budget, rather than only the configured budget:
```text
Before: retry time budget exhausted after 0 retries over 30.0s
After: retry time budget exhausted after 1 retries in 12.4s of a 30.0s budget
```
This applies to every HTTP driver: OpenAI Responses, OpenAI Chat Completions, Anthropic and Gemini.
---
# Schedule triggers can run in a Slack conversation
> A schedule trigger can now run in a direct message or channel of the Agent's own Slack channel, sharing that conversation's session from the first run.
Source:
A schedule trigger used to run in a session of its own, with no way to reach the people who talk to the Agent in Slack. An Agent that briefs someone every morning had to schedule itself from inside the conversation, so nothing ran until that person had written first.
A schedule trigger now takes a Slack target: one of the Agent’s own Slack channels and a conversation on it, `slack:user:{user_id}` for a direct message or `slack:channel:{channel_id}` for top-level posts. Each run lands in the session a message from that conversation would, and its replies post there. The first run opens the direct message, even before the person has written. Only someone who can manage the Agent can set a target. A channel that can no longer post fails the run visibly instead of falling back to a session of its own. See [Run in a Slack conversation](https://everruns.com/docs/features/agent-triggers/#run-in-a-slack-conversation).
```json
{
"trigger_type": "schedule",
"cron_expression": "0 7 * * *",
"timezone": "America/Chicago",
"message": "Morning briefing",
"target_channel_id": "appchan_01933b5a000070008000000000000001",
"target_binding_key": "slack:user:U0123ABCD"
}
```
---
# Sessions no longer hang when a worker stalls before a turn
> A session whose worker stalls before its turn starts, or after it ends, is now picked up by another worker within minutes instead of staying active forever.
Source:
A message could leave a session **active** with nothing running: the worker that picked it up stalled on a control-plane call before the turn started, and kept its claim on the session alive indefinitely. No turn events appeared, and the session’s sandbox stayed up because idle cleanup skips active sessions.
Workers now give up a session that makes no progress before its turn starts or after it ends, and every worker call to the control plane has a deadline. Another worker then runs the turn; one that keeps stalling is stopped with a processing error. Both bounds are configurable on workers:
```bash
WORKER_ACTOR_STALL_TIMEOUT_SECS=300
WORKER_GRPC_REQUEST_TIMEOUT_SECS=120
```
See [Environment variables](https://everruns.com/docs/sre/environment-variables/).
---
# Tenant egress proxy for self-hosted deployments
> Self-hosted deployments can send agent outbound traffic through everruns-egress-proxy for its own outbound address, per-org caps and per-org usage counts.
Source:
The server image now ships `everruns-egress-proxy`. Point the control plane and workers at it with `EVERRUNS_EGRESS_PROXY_URL` and a shared `EVERRUNS_EGRESS_PROXY_SECRET`, and traffic that agents, capabilities, MCP servers and integrations send leaves through it. Calls the deployment makes itself, such as LLM providers and email, keep leaving directly.
Run the proxy on a host with its own public address, and an address blocked because of one tenant’s traffic does not take model calls down with it. The proxy checks a short-lived token naming the org and session of every request, refuses private addresses and SMTP ports, and applies the per-org caps you set (requests per minute, per host, open connections, bytes per day; all off by default). It logs bytes and requests per org and session every minute, or sends them to a control endpoint that can also supply caps per org.
```bash
# Proxy host
EVERRUNS_EGRESS_PROXY_SECRET=<32+ random bytes>
EGRESS_PROXY_ORG_RPM=600
everruns-egress-proxy
# Control plane and workers
EVERRUNS_EGRESS_PROXY_URL=http://egress-proxy:4750
EVERRUNS_EGRESS_PROXY_SECRET=
```
See [Outbound traffic](https://everruns.com/docs/advanced/physical-architecture/#outbound-traffic) and [Tenant egress proxy variables](https://everruns.com/docs/sre/environment-variables/#tenant-egress-proxy).
---
# Yolop searches with the shared web_search tool
> Yolop's default harness now has web_search: the model's own search first, then Brave or Parallel with a key, then free search.
Source:
Yolop now ships the same `web_search` tool as Everruns agents. On OpenAI, ChatGPT plan, Anthropic, Gemini, and OpenRouter sessions it uses the model’s own search. Otherwise it uses Brave Search or Parallel when `BRAVE_SEARCH_API_KEY` or `PARALLEL_API_KEY` is set, and free search when neither is.
It replaces `duckduckgo_instant_answer` and `free_web_search` in the default harness. The DuckDuckGo lookup stays available as an opt-in capability. Pin a provider or filter domains in `settings.toml`:
```toml
[[capabilities]]
ref = "web_search"
provider = "brave" # or auto | native | free | parallel
blocked_domains = ["pinterest.com"]
```
See [Web tools](https://github.com/everruns/yolop/blob/main/docs/features/tools.md#web) and [#863](https://github.com/everruns/yolop/pull/863).
---
# Bring host capabilities and providers into Engine sessions
> Embedding hosts attach their own capabilities, provider resolution, retry budgets, MCP credentials, request attribution and observers to Engine sessions.
Source:
An application that already owns guarded capabilities, credential resolution or event observers can now keep them when `Engine` runs its sessions. `everruns::hosting::services::HostServices` carries Core capabilities with pre-tool hooks, model views and slash commands, host provider resolution, retry and stream-stall budgets, call-time MCP credentials, and credential-free request metadata such as the app version and OpenRouter attribution that every provider request carries.
```rust
use everruns::hosting::services::HostServices;
use everruns::{Agent, BuildError, Model};
fn agent(services: HostServices) -> Result {
Agent::builder()
.instructions("You maintain this repository.")
.model(Model::on("team-openai", "gpt-6.1-sol"))
.host_services(services)
.build()
}
```
Collisions with Framework capabilities, conflicting provider declarations and request metadata that claims a correlation id such as `session_id` fail the build, and the session filesystem stays the one the Environment chose. See [Host Services](https://everruns.com/docs/framework/host-services/).
---
# Run commands and hot-install capabilities on live sessions
> Framework sessions run slash commands, activate host-registered capabilities and switch model, effort and MCP servers without a runtime handle.
Source:
A host driving a Framework `Session` can now discover and run capability slash commands, register an extension on its `Engine` and activate or deactivate it per session, and change model, reasoning effort and MCP servers while the session is live. Commands work from a tool of the running turn, and changes apply from the next model call.
```rust
use everruns::{CapabilityRef, ReasoningEffort, Session};
async fn tune(session: &Session) -> Result<(), everruns::SessionControlError> {
session.activate_capability(CapabilityRef::new("btw")).await?;
session.run_command("/btw", Some("which files changed?")).await?;
session.set_reasoning_effort(Some(ReasoningEffort::High)).await?;
Ok(())
}
```
Unavailable selections fail without falling back or changing saved state. See [Session Controls](https://everruns.com/docs/framework/session-controls/).
---
# Gemini citations survive every continuation hop
> Long Gemini responses retain citations from each grounded request and count repeated searches across hops while preserving combined token usage.
Source:
Long responses using Gemini’s native Google Search now retain citations from every grounded continuation request. Previously, a later grounded request could replace earlier sources and supported answer spans. Citation indices now stay with their own request, including when different hops repeat the same answer text.
Search usage counts a query again when another hop performs it, while repeated metadata within one hop stays deduplicated. A final hop without grounding keeps earlier citations. Token accounting and continuation limits are unchanged. See [Google Gemini](https://everruns.com/docs/providers/gemini/) for long-decode configuration.
---
# Harness capabilities keep their name and details
> A harness row for a capability hidden by a rollout flag now shows its name, icon, and description, and links to the capability page.
Source:
A harness such as Worker can include [Agent Skills](https://everruns.com/docs/capabilities/agent-skills/) while that capability’s rollout flag is off. The catalog then omitted it, so the harness page showed the bare id `skills` with no name, icon, or description.
Management lists and the capability page now return that metadata anyway. Each row on a [harness](https://everruns.com/docs/features/harnesses/) links the name and More details to the capability page. The discovery catalog still hides a capability whose flag is off, and saving a harness still refuses to turn one on.
```json
{
"id": "skills",
"name": "Agent Skills",
"description": "Discover and activate skills from the session filesystem.",
"icon": "wand",
"feature_flag": "skills"
}
```
---
# Credentials work across users, agents and sessions
> Bind write-only credentials to a user, agent, session or organization, grant agents explicit access, and capture API tokens with exact-request consent.
Source:
Credentials can belong to virtual users, agents, sessions or organizations. Open **Credentials** on an agent or session, or manage personal and organization credentials in Settings. Organization access requires explicit grants. Values stay write-only; revocation blocks future participating requests.

[Web Fetch](https://everruns.com/docs/capabilities/web-fetch/) can capture several JSON response fields directly into encrypted storage. Persistent capture asks for approval of the exact request, destination and acting identity. Subsequent Web Fetch and Bashkit HTTP calls use references such as `$MAIL_TOKEN`; injected values and recognized response echoes are removed before tool results reach the agent.
The same ownership and access rules apply to [MCP parameter credentials](https://everruns.com/docs/integrations/mcp-credentials/). Unattended user OAuth keeps its sign-in guidance. Existing GitHub connections retain their installation selection and repository limits.
The [CLI credential commands](https://everruns.com/docs/reference/cli/credential-bindings/) manage metadata and revocation; values are entered through the secure Credentials form.
**API change:** credential value updates now require the observed `revision` to prevent stale writes.
---
# Send host-written review completions on Framework sessions
> Session::complete sends exactly the system prompts and messages a host chooses, tool-less, through the session's current provider and model.
Source:
A host that reviews a Framework session’s work, such as checking whether a task is complete, can now send its own completion request instead of one built from the session’s full conversation. `Session::complete` sends exactly the request’s system prompts and messages with no tools, through the provider and model the session’s next model call would use. It tags the call with a `purpose` and leaves history untouched.
```rust
use everruns::{CompletionRequest, Session};
async fn review(session: &Session, evidence: String) -> Result {
session
.complete(
CompletionRequest::new()
.system_prompt("Decide whether the task is complete.")
.user(evidence)
.purpose("completion_review"),
)
.await
}
```
Reserved metadata keys and tool messages are rejected, and an unavailable provider fails without fallback. See [Session Controls](https://everruns.com/docs/framework/session-controls/).
---
# Large MCP catalogs use bounded search and schemas
> MCP catalogs load across pages, search server and schema text within current access, and reveal bounded tool schemas through native calls, CLI, or both.
Source:
Everruns now discovers every page of a large MCP catalog and searches its server descriptions, tool descriptions, and parameter schemas within the current agent’s access. Bounded `tool_search` reveals selected schemas as the agent needs them. The same operations can be called as native tools, through the session’s `tools` command, or through both surfaces:
```json
{
"capabilities": [
{
"ref": "tool_search",
"config": {
"bounded": true
}
},
{
"ref": "tools_in_shell",
"config": {
"mode": "hybrid"
}
}
]
}
```
Search and invocation both check current authorization, so a disclosed schema does not preserve access after a server is removed or its connection is revoked. CLI search loads at most eight matching deferred servers per query; other servers stay pending, and search, help, and calls remain within the current turn’s visible scope. Custom Framework egress adapters must support `send_stream` for discovery. See [Large MCP catalogs and tool surfaces](https://everruns.com/docs/features/large-mcp-catalogs/), [Tool Search](https://everruns.com/docs/capabilities/tool-search/), and [Tools in Shell](https://everruns.com/docs/capabilities/tools-in-shell/).
---
# Chat mode keeps Agent edits beside the conversation
> Opt into Chat mode to edit the live Agent form with Platform Chat, preserve unsaved work, and review changes before saving.
Source:
Enable **Chat mode** in **Settings > Features**. Open an Agent and press **Command-J** or **Ctrl-J**. Ask “Make this agent’s instructions suitable for children.” Chat reads your unsaved form and stages a draft with **Undo**. Review it, then use **Save changes** or **Discard**. Version checks protect concurrent edits. Back to app preserves the conversation and draft. Saved Agent changes support revision-checked **Undo all**; see [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/).
[](./walkthrough.mp4)
Fictional demo data; synthetic narration; waiting periods shortened.
## Update 2026-10-24
Page context follows every app page, including filters and loaded records. Chat stages supported fields in Harnesses, Virtual Users, Observers, Knowledge and other forms. Use each form’s existing Save or Create action. Credentials and immediate actions keep their normal controls. Hover navigation icons for explanations, or open **Pages** for a labeled menu.

---
# Agents wait on MCP servers that run tools as tasks
> Everruns agents now accept MCP Tasks from the servers they call, checking on long tool calls until they finish instead of hitting the request timeout.
Source:
Everruns now declares the MCP Tasks extension when an agent calls a tool on an MCP server. A server that supports it can answer a slow call, such as a build or a report, with a task instead of holding the request open:
```json
{
"resultType": "task",
"taskId": "build-7f3a",
"status": "working",
"pollIntervalMs": 2000
}
```
The agent checks on the task at the interval the server suggests, for up to 30 minutes, and gets the tool’s result when it finishes. The server’s status message shows on the tool call in chat and in the session’s events while it runs. A question the task asks goes to the agent’s user like any other [MCP form question](https://everruns.com/docs/features/mcp/#long-running-tools), and the answer goes back with `tasks/update`. If the turn is cancelled or the wait runs out, Everruns cancels the task on the server.
---
# MCP Tasks run one agent turn per task
> Agent runs over /mcp follow the published MCP Tasks extension, with one task per message, the reply as the task result, and official client support.
Source:
`agent_run` and `session_send_message` over `/mcp` now return a standard MCP Tasks task when the client declares the Tasks extension. Each message is its own task, so a finished task keeps its reply and never moves on to a later turn. Before, the task id was the session, and its status changed with every new message.
`tasks/get` returns the agent’s reply as the tool result:
```json
{
"resultType": "complete",
"taskId": "org_….019a….019a…",
"status": "completed",
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": "The release notes are ready."
}
]
}
}
```
A failed turn is a tool error with a status message, and an Ask User question appears as a form to answer with `tasks/update`. The responses are checked against the official MCP TypeScript client in CI. See [Long agent turns as tasks](https://everruns.com/docs/features/mcp/#long-agent-turns-as-tasks).
---
# MCP tool calls can run longer than 60 seconds
> Each MCP server can set how long one tool call may run, from 1 second to 30 minutes, instead of the fixed 60-second limit.
Source:
Until now a call to an MCP tool failed if the server had not answered within 60 seconds. Each server can now set its own limit with `tool_timeout_ms`, from 1 second to 30 minutes, on an agent’s attachment or on a catalog entry (**Tool call timeout** in the catalog form). An attachment that uses a catalog entry inherits the entry’s limit unless it sets its own. `timeout`, the key other `mcpServers` config files use, is also accepted:
```json
{
"mcpServers": {
"ci": {
"type": "http",
"url": "https://ci.example.com/mcp",
"tool_timeout_ms": 600000
}
}
}
```
Servers without the setting keep the 60-second limit. See [MCP](https://everruns.com/docs/features/mcp/).
---
# Sync Agent source continuously from GitHub
> Connect an Agent to a GitHub repository package, review its changes, and apply future updates automatically while keeping source fields read-only.
Source:
Agents can now follow a package in a GitHub repository continuously. Open an Agent, select **Source → Sync from GitHub**, choose the repository, branch and package path, then review changes before enabling sync. For example, point at `agents/support/` containing:
```toml
schema_version = 1
name = "support"
instructions_file = "instructions.md"
```

Pushes and periodic checks apply valid updates automatically. The Source panel shows the applied commit and sync errors. Invalid updates retain the last working definition. Synced fields are read-only across Everruns; edit the repository or disconnect to restore local editing. See [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/#sync-an-agent-continuously-from-github).
## Update 2026-10-24
Connect an unlinked Agent from **More actions → Sync from GitHub**. The Source panel is shown only for a connected source or while its status is loading or unavailable. Connected source status and controls remain visible. See [Agent actions and searchable Playground setup](https://everruns.com/docs/changelog/2026-10-24-agent-actions-playground-search/).
## Update 2026-10-25
Two-way sync is now available as an opt-in. Existing bindings remain read-only. See [two-way Agent sync](https://everruns.com/docs/changelog/2026-10-25-two-way-agent-github-sync/).
---
# Knowledge unifies files and optional indexed search
> Mount notes, source trees and binary originals through one Knowledge feature, with Markkit text extraction and optional keyword or hybrid retrieval.
Source:
[Knowledge](https://everruns.com/docs/capabilities/knowledge/) replaces the two undeployed Knowledge Base and Knowledge Index surfaces. Create a file collection, add notes or uploads, or connect GitHub through an existing connection. Public HTTPS Git sources also work. Indexing can stay off while agents browse read-only mounts and use exact grep.

Original binaries and file hierarchy are retained. Markkit extracts HTML, DOCX and spreadsheets; unsupported formats remain downloadable. Enable keyword or hybrid search when needed, and use the same retrieval service through an agent tool or Bashkit:
```bash
knowledge search --limit 5 'refund policy'
```
Collections preserve frontmatter and annotations, update indexes incrementally, and return immutable citations. Organization search accounts use encrypted service credentials. Knowledge is available as an Adoption feature: disabled by default, with organization owner/admin or platform operator opt-in on development and hosted deployments. Shared workspaces keep Knowledge attachments session-scoped; unsupported binary formats retain their originals.
Hosted deployments can persist Knowledge in S3 without moving existing inline workspace files or image bytes.
---
# Agent actions and searchable Playground setup
> Find users and agents in Playground setup, connect GitHub from the Agent menu, and distinguish dry runs from normal Playground chats before starting.
Source:
Search **Talk as** by user name in [Playground setup](https://everruns.com/docs/features/ui/#agents), including users beyond the first page. The Agent/harness picker also supports search. The selected user stays visible while searching, archived users remain unavailable, and only owners and admins can choose another end user.

On an Agent page, connect a [GitHub source](https://everruns.com/docs/how-to/define-agents-as-files/#sync-an-agent-continuously-from-github) from **More actions → Sync from GitHub**. Unconnected Agents no longer show an empty Source card. **Dry run** and **Test in Playground** keep their distinct setup routes: simulated actions or normal execution. Neither starts a chat immediately.
## Update 2026-10-24
Agent pages now have one **Test in Playground** action. Use the existing **Dry run** checkbox in Playground setup to simulate actions.
---
# Publish a dedicated Work Chat for an Agent
> Self-hosted deployments can publish an Agent with separate consumer sign-in, a workspace panel and a choice of conversation interfaces.
Source:
**Work Chat** is an Agent-owned channel with its own consumer sign-in and a standalone browser app. Publish several channels for the same Agent, each at its own stable hostname on your deployment’s content domain.
Choose multiple conversations or one permanent main conversation with work threads. Single mode requires effective coordinator tools; incompatible publication or later capability changes are rejected. The interface streams replies, follows work and exposes permitted questions, approvals and workspace files. Existing ownership, budgets and read-only protections apply.


```json
{
"channel_type": "work_chat",
"channel_config": {
"interface_mode": "multi_chat"
}
}
```
See [Work Chat](https://everruns.com/docs/features/channels/#work-chat) for configuration and separate browser-host deployment. Hosting requires DNS, TLS and a registered OIDC broker.
---
# Work reports show what an agent is doing and got done
> Each turn gets one report of its plan, steps, what it waits on and its result, shown in chat and the sessions list and returned by the API.
Source:
Every turn now has a work report: what the agent is doing right now and what it got done. Chat pins the plan the agent shared (“Step 2 of 4”) while it works and ends with a result box, such as “Done · 2 files changed” with the agent’s summary and links. The sessions list and run cards show the headline of a session still working. Finished shell steps say what came out (“Ran `cargo test`, 3 of 12 tests failed”), and agents can call `report_done` to write their own summary.
```bash
everruns sessions work get session_01h9
```
The same report is at `GET /v1/sessions/{id}/work` and, in a public view, in an Agent API session read. See [Work reports](https://everruns.com/docs/features/work-reports/).
---
# Bashkit Python builds on Windows again
> Bashkit's embedded Python no longer crashes the Rust compiler on Windows, while preserving module alignment and Python in the hosted integration catalog.
Source:
Windows applications can build Bashkit’s embedded CPython with Rust 1.96.0. Previously, the embedded module’s static alignment made the compiler stop with an illegal instruction before the application compiled. Windows now uses a cached allocation that preserves the 64 KiB alignment Wasmtime needs.
Use Bashkit 0.20.1 or later in the 0.20 series and refresh the application’s lockfile. Everruns’ hosted integration catalog consumes the corrected dependency with Python enabled. Apple keeps its existing allocation path, and ELF hosts keep the aligned static. See the [Bashkit release notes](https://github.com/everruns/bashkit/releases/tag/v0.20.1).
---
# Limit outbound network for Daytona and E2B sandboxes
> Daytona and E2B Sandbox Templates can block the network or allow only listed domains, enforced by the provider together with the Session's network access list.
Source:
Daytona and E2B Sandbox Templates now offer **Outbound network**: open, only listed domains, or blocked. The provider enforces the rule on its own network, so it covers every process in the Sandbox, not only Everruns tools. The Session’s network access list narrows it further, and a resumed Sandbox follows the current list. Sandboxes the agent creates with Daytona or E2B tools, including the desktops, get the Session’s list too.
```json
{
"containment": {
"level": "isolated",
"network": {
"mode": "allowlist",
"allowed_hosts": [
"github.com",
"*.github.com"
]
}
}
}
```
See [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/#outbound-network).
---
# Run Everruns on your machine with one command
> everruns docker up asks a few questions, starts the self-hosted Platform in Docker and signs the CLI in; running it again is safe.
Source:
`everruns docker up` starts the self-hosted Platform on your computer. It checks Docker, asks who signs in (just you, nobody, or accounts), your admin email and an LLM provider, then generates the secrets, waits until the server is healthy and signs the CLI in as profile `local`. Running it again starts what is stopped and leaves the rest alone. `everruns docker upgrade` backs up the database and moves the instance to the CLI’s version; `everruns docker reset` deletes it after you type its name.
```bash
everruns docker up --yes --provider openai # no prompts, key from OPENAI_API_KEY
everruns agents list
```
The example compose file now starts one worker by default; the [Docker Compose guide](https://everruns.com/docs/getting-started/docker-compose/) turns on the `scale` and `metrics` profiles for the previous shape. See [Run Everruns on your machine](https://everruns.com/docs/getting-started/run-locally/).
---
# Chat folds a thread report into one line
> When a thread finishes, asks, or fails, Chat shows one folded line. Open it to read the report. The coordinator still gets the full text.
Source:
Chat shows a thread’s report as one folded line, such as “Releases is done”, instead of a message filled with the report. Open the line to read it. A finish with nothing to add is the line only. The coordinator still receives the full text and answers in the conversation with what matters.

The Threads panel still lists what is working, what needs you, and what is ready for review. See [Chat and threads](https://everruns.com/docs/features/ui/#chat-and-threads) and [Coordination](https://everruns.com/docs/framework/coordination/).
---
# Install the self-hosted Platform on Kubernetes with Helm
> An official Helm chart installs the self-hosted Platform on Kubernetes, ships as an OCI chart with each release, and keeps its secrets across upgrades.
Source:
The `everruns` Helm chart installs the self-hosted Platform on Kubernetes: the API server, workers, the UI, a proxy with the same routes as Docker Compose, and NATS. Bring your own PostgreSQL, or turn on a single bundled one to try it out. Workers can scale on queue depth with KEDA.
```bash
helm install everruns oci://ghcr.io/everruns/charts/everruns \
--namespace everruns --create-namespace \
--set database.url="$DATABASE_URL" --set auth.adminEmail=you@example.com
```
The chart generates the encryption key and other secrets on the first install and keeps them across upgrades and uninstalls. See [Kubernetes](https://everruns.com/docs/getting-started/kubernetes/).
---
# Live event delivery recovers after a NATS restart
> Session streaming restores its JetStream replay buffer after a broker restart, without an API restart or duplicate retries after uncertain acknowledgements.
Source:
Live session updates resume after a NATS restart without requiring an API restart. The API restores the missing in-memory JetStream stream when publishing an event or accepting a new live subscription. Streaming clients reconnect normally; completed messages remain available in durable session history.
Token deltas remain ephemeral. The recovery does not rewrite an existing stream’s subject policy or retry uncertain publish timeouts.
---
# Personal settings are separate pages
> Profile, Connections, MCP servers, and AI clients each have their own Settings page, replacing the combined My agent experience page.
Source:
**In the UI:** personal settings under [Settings](https://everruns.com/docs/features/ui/#virtual-users-and-personal-settings) are separate pages. **Profile** is your name, locale, and timezone in this organization. **Connections** is provider sign-ins and credentials. **MCP servers** is the servers you add for yourself. **AI clients** is the apps you approved to use Everruns as you. Account and personal access tokens are unchanged.
`/settings/connections` opens Connections. Sign-in returns there.
---
# Give a Daytona or E2B sandbox a desktop
> A Sandbox Template option starts the Session's Sandbox with a Linux desktop the agent sees and operates with the computer tool, next to its shell and files.
Source:
Daytona and E2B Sandbox Templates have a **Desktop** switch. With it on, the Session’s Sandbox starts with a Linux desktop and the agent gets the `computer` tool to take screenshots and click, type and scroll on it. The desktop is the same machine as the shell and file tools and follows the template’s outbound network rule. The desktop is running as soon as the Sandbox is, and the shell points at it, so a browser the agent starts from the shell opens on screen. Desktop computer use no longer needs a feature flag or a second sandbox.
```json
{
"target": {
"kind": "managed",
"provider": "e2b",
"options": {
"desktop": true
}
}
}
```
See [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/#desktop).
---
# Sandboxes are disposable, their records are kept
> Paused Sandboxes are deleted after a day, a Delete idle action drops disposable ones at once, and every Sandbox record is now kept forever.
Source:
Paused Sandboxes used to stay with the provider until their Session was deleted, which never happens for Sessions started by triggers. Everruns now deletes a paused Sandbox after one day without use. Pick 3, 7, or 30 days, or **Never**, under **Delete after paused** on the [Sandbox Template](https://everruns.com/docs/features/sandbox-templates/), or set **When idle** to **Delete** to drop it as soon as it goes idle. The Session stays; its next run gets a fresh Sandbox.
```json
{
"lifecycle": {
"idle_after_seconds": 900,
"delete_after_seconds": 604800
}
}
```
Set `delete_after_seconds` to `null` to keep paused Sandboxes.
Only the Sandbox at the provider is deleted. Its record and history stay on the **Sandboxes** page under **Deleted**, and deleted records are no longer purged after 30 days, so `SANDBOX_HISTORY_RETENTION_DAYS` is gone.
A Sandbox whose Session is stuck mid-turn is now paused after six hours without use, instead of running until someone stops it.
---
# Edit GitHub-synced Agents from either side
> Enable two-way Agent source sync to commit saved Everruns edits to GitHub, retain local changes on errors, and review conflicts before replacing them.
Source:
Agents connected to GitHub can now accept edits in Everruns too. Open **Change source**, choose **Sync with GitHub**, preview the package and enable continuous sync. Saved edits are queued and committed to the selected branch. Existing bindings remain read-only until you opt in.

Two-way sync requires Contents write permission on the GitHub App and installation. Branch protection still applies. If both sides change the package, sync pauses and retains your local edits. Review the GitHub version before replacing them, or disconnect to keep local editing.
Write-back preserves instruction and asset paths while manifest edits may normalize formatting. Credentials and publication stay local. See [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/#sync-an-agent-continuously-from-github).
---
# Everruns 0.49.0: Thousands of MCP tools, GitHub sync and a Helm chart
> MCP servers with 2,000+ tools and MCP Tasks, Agent source synced with GitHub, scoped credentials, Knowledge, and a Helm chart with everruns docker up.
Source:

## Highlights
* **Thousands of MCP tools and MCP Tasks**: Agents work with MCP servers of 2,000+ tools: catalogs load across pages, tools are found by search and their schemas revealed within a fixed budget, through native calls, the CLI or both. Agents also accept MCP Tasks, checking on long tool calls until they finish instead of hitting the request timeout, and each MCP server can set its own tool call timeout ([changelog](https://everruns.com/docs/changelog/2026-10-21-large-mcp-catalogs/), [MCP Tasks changelog](https://everruns.com/docs/changelog/2026-10-23-mcp-tasks-client/), [#4870](https://github.com/everruns/everruns/pull/4870), [#4882](https://github.com/everruns/everruns/pull/4882), [#4887](https://github.com/everruns/everruns/pull/4887), [#4888](https://github.com/everruns/everruns/pull/4888), [#4889](https://github.com/everruns/everruns/pull/4889)).
* **Agent source synced with GitHub**: An Agent’s source syncs continuously from a GitHub repository, and in two-way mode edits made in Everruns are committed back to the branch ([changelog](https://everruns.com/docs/changelog/2026-10-25-two-way-agent-github-sync/), [#4869](https://github.com/everruns/everruns/pull/4869), [#4919](https://github.com/everruns/everruns/pull/4919)).
* **Scoped credentials**: Write-only credentials bind to a user, agent, session or organization, agents get explicit access, and API tokens are captured with consent for the exact request ([changelog](https://everruns.com/docs/changelog/2026-10-19-scoped-credentials-secure-capture/), [#4828](https://github.com/everruns/everruns/pull/4828)).
* **Knowledge**: Notes, source trees and binary originals mount through one Knowledge feature, with text extraction and optional keyword or hybrid search ([changelog](https://everruns.com/docs/changelog/2026-10-23-unified-knowledge-files/), [#4886](https://github.com/everruns/everruns/pull/4886), [#4893](https://github.com/everruns/everruns/pull/4893), [#4894](https://github.com/everruns/everruns/pull/4894), [#4900](https://github.com/everruns/everruns/pull/4900)).
* **Automatic session titles**: Sessions get titles automatically from a dedicated small model, and titles set by hand are kept ([changelog](https://everruns.com/docs/changelog/2026-10-17-automatic-session-titles/), [#4802](https://github.com/everruns/everruns/pull/4802)).
* **Helm chart and `everruns docker up`**: `everruns docker up` runs the self-hosted Platform on your machine with Docker in one command and asks how people sign in; `upgrade`, `reset`, `status`, `logs` and `doctor` manage it. A Helm chart published to `oci://ghcr.io/everruns/charts/everruns` installs the Platform on Kubernetes ([changelog](https://everruns.com/docs/changelog/2026-10-25-everruns-docker-up/), [Helm changelog](https://everruns.com/docs/changelog/2026-10-25-helm-chart/), [#4895](https://github.com/everruns/everruns/pull/4895), [#4904](https://github.com/everruns/everruns/pull/4904), [#4906](https://github.com/everruns/everruns/pull/4906), [#4910](https://github.com/everruns/everruns/pull/4910)).
* **Sandbox network policy on Daytona and E2B**: Daytona and E2B Sandbox Templates can block the network or allow only listed domains, enforced by the provider together with the session’s network access list ([changelog](https://everruns.com/docs/changelog/2026-10-25-daytona-network-rules/), [#4913](https://github.com/everruns/everruns/pull/4913), [#4914](https://github.com/everruns/everruns/pull/4914)).
## Upgrade notes
* Session secrets are write-only to agents: `secret_store` sets, lists and deletes, but `get` no longer returns a value ([#4676](https://github.com/everruns/everruns/pull/4676)).
* Development mode (`DEV_MODE=true`) refuses to run as root; run it as a regular user or use external PostgreSQL ([#4722](https://github.com/everruns/everruns/pull/4722)).
* The experimental Deno Deploy sandbox provider and its `deno_*` tools are removed; use Daytona, E2B, Sprites, Modal or the container sandbox ([#4769](https://github.com/everruns/everruns/pull/4769)).
## In the changelog
* [Agents can no longer read secrets back](https://everruns.com/docs/changelog/2026-10-13-agents-cannot-read-secrets/)
* [Answers cut off by a provider error are retried](https://everruns.com/docs/changelog/2026-10-13-answers-cut-off-are-retried/)
* [Stored attachments download safely in browsers](https://everruns.com/docs/changelog/2026-10-13-attachments-download-safely/)
* [Attachments stay readable after compaction, from every channel](https://everruns.com/docs/changelog/2026-10-13-attachments-survive-compaction/)
* [Dev mode PostgreSQL requires a non-root user](https://everruns.com/docs/changelog/2026-10-13-dev-mode-postgres-user/)
* [E2B desktop computer use keeps the pointer where it was moved](https://everruns.com/docs/changelog/2026-10-13-e2b-desktop-pointer-reset/)
* [Gemini 4 Argon continues long responses across requests](https://everruns.com/docs/changelog/2026-10-13-gemini-argon-long-decode/)
* [MCP resource reads enforce conversation attachments](https://everruns.com/docs/changelog/2026-10-13-mcp-resource-attachment-scope/)
* [Remote workers run turns without an encryption key on the API](https://everruns.com/docs/changelog/2026-10-13-remote-workers-without-encryption-key/)
* [Tool calls cut off by a crash are rerun when it is safe](https://everruns.com/docs/changelog/2026-10-13-tool-calls-survive-crashes/)
* [Schedule triggers read cron weekdays from Sunday as 0](https://everruns.com/docs/changelog/2026-10-13-trigger-cron-weekdays-from-sunday/)
* [Visti and Stend join the product footer](https://everruns.com/docs/changelog/2026-10-13-visti-stend-footer-links/)
* [Monitor agent execution and actor leases at fleet scale](https://everruns.com/docs/changelog/2026-10-14-agent-execution-monitoring/)
* [Concurrent session creation reuses one runtime account](https://everruns.com/docs/changelog/2026-10-14-concurrent-session-account-creation/)
* [Framework and serve keep files in the session](https://everruns.com/docs/changelog/2026-10-14-framework-serve-attachments/)
* [Gemini setups prefer Argon and include it in favourites](https://everruns.com/docs/changelog/2026-10-14-gemini-argon-default/)
* [More reliable Framework agent sessions](https://everruns.com/docs/changelog/2026-10-14-local-session-reliability/)
* [Local stress tests use successful offline tool workloads](https://everruns.com/docs/changelog/2026-10-14-local-stress-testing/)
* [OpenTelemetry fixes export and cached-token totals](https://everruns.com/docs/changelog/2026-10-14-opentelemetry-export-and-accounting/)
* [PACT delegated turns discover the user’s MCP tools](https://everruns.com/docs/changelog/2026-10-14-pact-delegated-mcp-tools/)
* [Recurring schedules reject unverified cron intervals](https://everruns.com/docs/changelog/2026-10-14-session-cron-interval-validation/)
* [A Slack integration overview for building Agents](https://everruns.com/docs/changelog/2026-10-14-slack-integration-overview/)
* [Tool events expose scoped worker CPU observations](https://everruns.com/docs/changelog/2026-10-14-tool-worker-cpu-observations/)
* [React session streams can suspend while idle](https://everruns.com/docs/changelog/2026-10-15-optional-idle-session-streams/)
* [Find failed agent turns from the Agents list](https://everruns.com/docs/changelog/2026-10-16-agent-failure-investigation/)
* [Agent session lists now match their session count](https://everruns.com/docs/changelog/2026-10-16-agent-session-history/)
* [Connect your coding agent from the Everruns UI](https://everruns.com/docs/changelog/2026-10-16-coding-agent-onboarding/)
* [Everruns shares product and workflow skills](https://everruns.com/docs/changelog/2026-10-16-platform-operating-skills/)
* [Deno Deploy sandbox integration removed](https://everruns.com/docs/changelog/2026-10-16-remove-deno-deploy/)
* [Slack workspace consent stays with its initiating browser](https://everruns.com/docs/changelog/2026-10-16-slack-workspace-browser-binding/)
* [User MCP management respects approval settings](https://everruns.com/docs/changelog/2026-10-16-user-mcp-approval-policy/)
* [Run Everruns provider drivers with WASM host fetch](https://everruns.com/docs/changelog/2026-10-16-wasm-provider-host-fetch/)
* [Automatic session titles with a shared small model](https://everruns.com/docs/changelog/2026-10-17-automatic-session-titles/)
* [ChatGPT Plan removed from Platform setup](https://everruns.com/docs/changelog/2026-10-17-chatgpt-plan-ui-removal/)
* [Framework hosts can brand ChatGPT login completion](https://everruns.com/docs/changelog/2026-10-17-custom-chatgpt-login-pages/)
* [OAuth MCP servers with path-specific metadata now connect](https://everruns.com/docs/changelog/2026-10-17-mcp-oauth-discovery-fallbacks/)
* [Connecting GitHub as yourself acts as you, not the shared bot](https://everruns.com/docs/changelog/2026-10-18-github-person-sign-in/)
* [MCP client authorization follows callback redirects](https://everruns.com/docs/changelog/2026-10-18-mcp-client-authorization-redirects/)
* [Turns no longer fail when a provider asks to wait too long](https://everruns.com/docs/changelog/2026-10-18-provider-retry-after-budget/)
* [Schedule triggers can run in a Slack conversation](https://everruns.com/docs/changelog/2026-10-18-schedule-triggers-in-slack-conversations/)
* [Sessions no longer hang when a worker stalls before a turn](https://everruns.com/docs/changelog/2026-10-18-stalled-worker-sessions-recover/)
* [Tenant egress proxy for self-hosted deployments](https://everruns.com/docs/changelog/2026-10-18-tenant-egress-proxy/)
* [Bring host capabilities and providers into Engine sessions](https://everruns.com/docs/changelog/2026-10-19-engine-host-services/)
* [Run commands and hot-install capabilities on live sessions](https://everruns.com/docs/changelog/2026-10-19-engine-session-controls/)
* [Gemini citations survive every continuation hop](https://everruns.com/docs/changelog/2026-10-19-gemini-continuation-grounding/)
* [Harness capabilities keep their name and details](https://everruns.com/docs/changelog/2026-10-19-harness-capability-metadata/)
* [Credentials work across users, agents and sessions](https://everruns.com/docs/changelog/2026-10-19-scoped-credentials-secure-capture/)
* [Send host-written review completions on Framework sessions](https://everruns.com/docs/changelog/2026-10-19-session-curated-completions/)
* [Large MCP catalogs use bounded search and schemas](https://everruns.com/docs/changelog/2026-10-21-large-mcp-catalogs/)
* [Chat mode keeps Agent edits beside the conversation](https://everruns.com/docs/changelog/2026-10-23-chat-beside-agent-pages/)
* [Agents wait on MCP servers that run tools as tasks](https://everruns.com/docs/changelog/2026-10-23-mcp-tasks-client/)
* [MCP Tasks run one agent turn per task](https://everruns.com/docs/changelog/2026-10-23-mcp-tasks-per-turn/)
* [MCP tool calls can run longer than 60 seconds](https://everruns.com/docs/changelog/2026-10-23-mcp-tool-timeout/)
* [Sync Agent source continuously from GitHub](https://everruns.com/docs/changelog/2026-10-23-sync-agent-source-github/)
* [Knowledge unifies files and optional indexed search](https://everruns.com/docs/changelog/2026-10-23-unified-knowledge-files/)
* [Agent actions and searchable Playground setup](https://everruns.com/docs/changelog/2026-10-24-agent-actions-playground-search/)
* [Publish a dedicated Work Chat for an Agent](https://everruns.com/docs/changelog/2026-10-24-hosted-work-chat/)
* [Work reports show what an agent is doing and got done](https://everruns.com/docs/changelog/2026-10-24-work-reports/)
* [Limit outbound network for Daytona and E2B sandboxes](https://everruns.com/docs/changelog/2026-10-25-daytona-network-rules/)
* [Run Everruns on your machine with one command](https://everruns.com/docs/changelog/2026-10-25-everruns-docker-up/)
* [Install the self-hosted Platform on Kubernetes with Helm](https://everruns.com/docs/changelog/2026-10-25-helm-chart/)
* [Live event delivery recovers after a NATS restart](https://everruns.com/docs/changelog/2026-10-25-nats-stream-recovery/)
* [Bashkit Python builds on Windows again](https://everruns.com/docs/changelog/2026-10-25-bashkit-windows-python/)
* [Chat folds a thread report into one line](https://everruns.com/docs/changelog/2026-10-25-folded-thread-reports/)
* [Give a Daytona or E2B sandbox a desktop](https://everruns.com/docs/changelog/2026-10-25-sandbox-desktop/)
* [Agents see the screenshots and images their tools return](https://everruns.com/docs/changelog/2026-10-26-tool-images-reach-model/)
* [Sandboxes are disposable, their records are kept](https://everruns.com/docs/changelog/2026-10-25-sandbox-retention/)
* [Edit GitHub-synced Agents from either side](https://everruns.com/docs/changelog/2026-10-25-two-way-agent-github-sync/)
See the [GitHub release](https://github.com/everruns/everruns/releases/tag/v0.49.0) and the [full release notes](https://github.com/everruns/everruns/blob/main/CHANGELOG.md#0490---2026-10-26) for every pull request in this version.
---
# Agents see the screenshots and images their tools return
> Images from tool results, such as computer-use screenshots, reach the model as images again instead of as truncated base64 text.
Source:
When a tool returned an image, such as a `computer` screenshot, a browser capture or an image read from a file, the worker received it as base64 inside the tool result’s text. The text was then truncated, so the model never saw the picture: computer-use agents reported that screenshots failed and fell back to reading the screen through shell tools. Tool results now keep their images as image parts on every path to the model. See [Computer Use](https://everruns.com/docs/capabilities/computer-use/).
---
# Changelog feeds
> RSS feeds for the Everruns changelog, one for every change and one per product, plus the Markdown versions that coding agents can read.
Source:
Every feed carries the full text of each entry, with its product and kind as categories.
| Feed | URL |
| ----------- | ------------------------------------------------------------------------------------- |
| All changes | [/changelog/rss.xml](https://everruns.com/docs/changelog/rss.xml) |
| Cloud | [/changelog/cloud/rss.xml](https://everruns.com/docs/changelog/cloud/rss.xml) |
| Platform | [/changelog/platform/rss.xml](https://everruns.com/docs/changelog/platform/rss.xml) |
| Framework | [/changelog/framework/rss.xml](https://everruns.com/docs/changelog/framework/rss.xml) |
| SDK | [/changelog/sdk/rss.xml](https://everruns.com/docs/changelog/sdk/rss.xml) |
| CLI | [/changelog/cli/rss.xml](https://everruns.com/docs/changelog/cli/rss.xml) |
| Yolop | [/changelog/yolop/rss.xml](https://everruns.com/docs/changelog/yolop/rss.xml) |
| Mira | [/changelog/mira/rss.xml](https://everruns.com/docs/changelog/mira/rss.xml) |
| Bashkit | [/changelog/bashkit/rss.xml](https://everruns.com/docs/changelog/bashkit/rss.xml) |
## For coding agents
Every entry is also served as Markdown: add `index.md` to its address, for example `https://everruns.com/docs/changelog/index.md` for the whole list. The changelog is a section of [llms.txt](https://everruns.com/docs/llms.txt).
To list only what changed after the version you run, add `?since=` to the changelog address, for example `/changelog/?since=0.45.0`.
---
# Event Reference
> All Everruns event types, with schemas and SSE examples for the common ones: input, output, tool, lifecycle, and error events.
Source:
This page lists every event type in the Everruns event protocol and documents the schema of the most common ones. Types without a section below are listed in the table with a short description.
## All event types
| Event type | Description |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`input.message`](#inputmessage) | User message submitted to the session. |
| [`output.message.started`](#outputmessagestarted) | Assistant message started. |
| [`output.message.delta`](#outputmessagedelta) | Streaming chunk of assistant text. |
| [`output.message.completed`](#outputmessagecompleted) | Assistant message finished and persisted. |
| [`conversation.message`](#conversationmessage) | An agent that uses explicit communication sent a message with `send_message`. |
| `output.message.replaced` | Clients discard streamed text for the turn and show a replacement; the next `output.message.completed` carries it. |
| [`turn.started`](#turnstarted) | Turn began. |
| [`turn.completed`](#turncompleted) | Turn finished successfully. |
| [`turn.failed`](#turnfailed) | Turn failed with an error. |
| `turn.sealed` | Turn stopped without success or error (for example repeated crash-reclaims or exhausted work budget); carries a `reason`. |
| [`turn.cancelled`](#turncancelled) | Turn cancelled by the user. |
| [`reason.started`](#reasonstarted) | LLM inference step began. |
| [`reason.completed`](#reasoncompleted) | LLM inference step finished. |
| `reason.recovered` | A reason step was recovered after a failure. |
| [`reason.thinking.started`](#reasonthinkingstarted) | Model thinking started. |
| [`reason.thinking.delta`](#reasonthinkingdelta) | Streaming chunk of model thinking. |
| [`reason.thinking.completed`](#reasonthinkingcompleted) | Model thinking finished. |
| [`reason.item`](#reasonitem) | Provider reasoning artifact (opaque or encrypted items plus safe summary text). |
| [`act.started`](#actstarted) | Tool execution phase began. |
| [`act.completed`](#actcompleted) | Tool execution phase finished. |
| [`tool.started`](#toolstarted) | A tool call began. |
| [`tool.completed`](#toolcompleted) | A tool call finished. |
| `tool.progress` | Progress update from a running tool. |
| `tool.output.delta` | Streaming chunk of tool output. |
| `tool.call_requested` | A client-side tool call is waiting for a result from the client. |
| `tool.call_repaired` | A malformed tool call was repaired, or repair was attempted, by the `tool_call_repair` capability. |
| `tool.hosted_call` | A provider-executed (hosted) tool call changed state. |
| `tool.nested_call` | A tool a shell script called through `tools` ([Tools in Shell](https://everruns.com/docs/capabilities/tools-in-shell/)) finished, was refused, or stopped the script for approval. Carries the parent `bash` call id. |
| `transcript.repaired` | The conversation transcript was repaired before a provider request. |
| `capability.usage` | Usage of a capability by the agent. |
| [`llm.generation`](#llmgeneration) | One LLM call: model, usage, and timing. |
| [`session.started`](#sessionstarted) | Session created. |
| [`session.activated`](#sessionactivated) | Session started working on a turn. |
| [`session.idled`](#sessionidled) | Session returned to idle. |
| `session.title.updated` | Session title changed. |
| `session.model.changed` | Model override differs from the previous turn’s. |
| [`sandbox.instance_lost`](#sandboxinstance_lost) | Managed Sandbox compute disappeared and will be replaced. |
| [`sandbox.recovered`](#sandboxrecovered) | Managed Sandbox compute was replaced and its durable workspace restored. |
| [`environment.instance_lost`](#sandboxinstance_lost) | Legacy compatibility name for `sandbox.instance_lost`. |
| [`environment.recovered`](#sandboxrecovered) | Legacy compatibility name for `sandbox.recovered`. |
| `timer.set` | The session set a timer, such as a schedule. |
| `timer.fired` | A timer’s occurrence was delivered to the session. |
| `timer.cancelled` | A timer will not fire again. |
| `task.created` | Session task created. |
| `task.updated` | Session task changed. |
| `task.message.sent` | Message sent to a session task. |
| `task.message.received` | Message received from a session task. |
| `context.compacting` | Context compaction started. |
| `context.compacted` | Context compaction installed a summary. |
| `context.compaction.skipped` | A compaction evaluation ran but installed nothing. |
| `context.compaction.failed` | A compaction attempt errored before installing. |
| `dreaming.activity` | Background Memory consolidation status, note count, model, usage, and cost; excludes evidence and generated notes. See [Dreaming](https://everruns.com/docs/features/dreaming/). |
| `file.written` | A session file was written. |
| `budget.warning` | Budget crossed its warning threshold. |
| `budget.paused` | Budget paused the session. |
| `budget.exhausted` | Budget has no room left. |
| `budget.resumed` | Budget resumed. |
| `voice.session.started` | Voice session started. |
| `voice.input_transcript.delta` | Streaming chunk of the user’s speech transcript. |
| `voice.input_transcript.completed` | User’s speech transcript finished. |
| `voice.output_transcript.delta` | Streaming chunk of the assistant’s speech transcript. |
| `voice.output_transcript.completed` | Assistant’s speech transcript finished. |
| `voice.session.ended` | Voice session ended. |
| `voice.session.failed` | Voice session failed. |
| `voice.output.interrupted` | The caller talked over a spoken answer; carries what was heard and what was dropped. |
## Input Events
### input.message
Emitted when a user message is submitted to the session.
| Field | Type | Description |
| --------- | ------- | ----------------------- |
| `message` | Message | The user message object |
```json
{
"type": "input.message",
"data": {
"message": {
"id": "message_...",
"role": "user",
"content": [{"type": "text", "text": "Hello!"}],
"created_at": "2024-01-15T10:30:00.000Z"
}
}
}
```
## Output Events
### output.message.started
Emitted when the LLM starts generating a response. This marks the start of generation, not model reasoning: reasoning has its own events (see [Reasoning Events](#reasoning-events)) and its own channel.
| Field | Type | Description |
| --------- | ------- | ----------------------------------------------------------------------------------------------------- |
| `turn_id` | string | Turn ID this output belongs to |
| `model` | string? | Optional model name being used |
| `phase` | string? | Best-effort phase hint: `commentary` or `final_answer`. Absent means unclassified, never “reasoning”. |
```json
{
"type": "output.message.started",
"data": {
"turn_id": "turn_...",
"model": "gpt-6.1-sol"
}
}
```
### output.message.delta
Incremental text update during LLM generation. Events are batched (\~100ms).
| Field | Type | Description |
| ------------- | ------ | ----------------------------- |
| `turn_id` | string | Turn ID this delta belongs to |
| `delta` | string | New text since last delta |
| `accumulated` | string | Total text so far |
```json
{
"type": "output.message.delta",
"data": {
"turn_id": "turn_...",
"delta": "Hello",
"accumulated": "Hello"
}
}
```
### output.message.completed
Emitted when the agent response is complete.
| Field | Type | Description |
| ---------- | -------------- | -------------------------- |
| `message` | Message | The complete agent message |
| `metadata` | ModelMetadata? | Model information |
| `usage` | TokenUsage? | Token usage statistics |
`message.phase` is authoritative for whether this message is intermediate `commentary` or the turn’s `final_answer`, and `message.phase_source` says whether the provider reported that phase (`provider`) or the runtime inferred it from tool-call presence (`derived`). Reasoning artifacts appear as `reasoning` content parts inside `message.content`, in the order the provider emitted them.
```json
{
"type": "output.message.completed",
"data": {
"message": {
"id": "message_...",
"role": "assistant",
"content": [{"type": "text", "text": "Hello! How can I help?"}]
},
"usage": {
"input_tokens": 50,
"output_tokens": 25
}
}
}
```
### conversation.message
Emitted when an agent that uses [explicit communication](https://everruns.com/docs/features/explicit-communication/) sends a message with the `send_message` tool. For these agents this event, not `output.message.completed`, carries what the agent said: their assistant text is recorded as `output.message.completed` with `message.phase: "commentary"` and is working notes, not a reply.
| Field | Type | Description |
| ---------------------- | ------- | ---------------------------------------------------------------------------------------- |
| `message_id` | string | Identifier of the sent message |
| `text` | string | The message as the agent wrote it, in Markdown |
| `tool_call_id` | string | The `send_message` call that sent it |
| `delivery` | object? | Present only when an external platform accepted the message (for example a Slack thread) |
| `delivery.platform` | string | Destination platform, such as `slack` |
| `delivery.channel` | string | Platform channel or conversation identifier |
| `delivery.message_ref` | string | Platform message identifier |
When the conversation is the session itself (web chat, API, A2A, AG-UI, MCP, FCP), `delivery` is absent and this event is the delivery.
```json
{
"type": "conversation.message",
"data": {
"message_id": "message_...",
"text": "The report is ready. Revenue grew 12% quarter over quarter.",
"tool_call_id": "call_abc123",
"delivery": {
"platform": "slack",
"channel": "C0123456789",
"message_ref": "1712345678.000200"
}
}
}
```
## Turn Lifecycle Events
### turn.started
Emitted when a turn begins execution.
| Field | Type | Description |
| ------------------ | ------- | -------------------------------- |
| `turn_id` | string | Turn identifier |
| `input_message_id` | string | Message that triggered this turn |
| `input_content` | string? | Optional input content preview |
```json
{
"type": "turn.started",
"data": {
"turn_id": "turn_...",
"input_message_id": "message_...",
"input_content": "Hello!"
}
}
```
### turn.completed
Emitted when a turn completes successfully.
| Field | Type | Description |
| ------------------------ | ----------- | ---------------------------------------------------------------------------------------------------------------- |
| `turn_id` | string | Turn identifier |
| `iterations` | integer | Number of reason-act iterations |
| `duration_ms` | integer? | Duration in milliseconds |
| `usage` | TokenUsage? | Aggregated token usage |
| `input_content` | string? | Optional input content |
| `final_message_id` | string? | Canonical final assistant message ID |
| `final_answer_preview` | string? | Bounded final answer preview |
| `time_to_first_token_ms` | integer? | First-token latency |
| `tool_call_count` | integer? | Completed tool-call count |
| `llm_call_count` | integer? | LLM generation count |
| `issue_count` | integer? | Failed tool calls the turn recovered from. The turn still succeeded; see [Issues and errors](#issues-and-errors) |
| `status` | string? | Optional completion status |
| `stop_reason` | string? | How the final model call ended (`stop`, `length`, `content_filter`, …) |
```json
{
"type": "turn.completed",
"data": {
"turn_id": "turn_...",
"iterations": 3,
"duration_ms": 1500,
"usage": {
"input_tokens": 500,
"output_tokens": 200
},
"final_message_id": "message_...",
"final_answer_preview": "Done.",
"time_to_first_token_ms": 120,
"tool_call_count": 2,
"llm_call_count": 3,
"issue_count": 0,
"status": "completed",
"stop_reason": "stop"
}
}
```
### turn.failed
Emitted when a turn fails with an error. A failed turn is always an error: the work stopped.
| Field | Type | Description |
| ------------ | ------- | ------------------- |
| `turn_id` | string | Turn identifier |
| `error` | string | Error message |
| `error_code` | string? | Optional error code |
```json
{
"type": "turn.failed",
"data": {
"turn_id": "turn_...",
"error": "The model provider is rate limiting requests. Please try again shortly.",
"error_code": "provider_rate_limited"
}
}
```
### turn.cancelled
Emitted when a turn is cancelled by the user.
| Field | Type | Description |
| --------- | ----------- | ------------------------- |
| `turn_id` | string | Turn identifier |
| `reason` | string? | Cancellation reason |
| `usage` | TokenUsage? | Usage before cancellation |
```json
{
"type": "turn.cancelled",
"data": {
"turn_id": "turn_...",
"reason": "User requested",
"usage": {
"input_tokens": 100,
"output_tokens": 50
}
}
}
```
## Reasoning Events
Model reasoning, as distinct from the `reason.started` / `reason.completed` lifecycle events further down, which mark an LLM *inference step* in the reason/act loop and are unrelated to whether the model reasoned.
These events are emitted by models that expose reasoning (Anthropic Claude with thinking enabled, OpenAI GPT-5.x and o-series with reasoning effort configured, Gemini with a thinking budget, and Chat Completions models that return `reasoning_content`). Everything here belongs to the reasoning channel and must never be rendered as assistant text.
### reason.thinking.started
Emitted when extended thinking begins.
| Field | Type | Description |
| --------- | ------- | ----------- |
| `turn_id` | string | Turn ID |
| `model` | string? | Model name |
```json
{
"type": "reason.thinking.started",
"data": {
"turn_id": "turn_...",
"model": "claude-4-opus"
}
}
```
### reason.thinking.delta
Streams incremental thinking content.
| Field | Type | Description |
| ------------- | ------ | --------------------- |
| `turn_id` | string | Turn ID |
| `delta` | string | New thinking text |
| `accumulated` | string | Total thinking so far |
```json
{
"type": "reason.thinking.delta",
"data": {
"turn_id": "turn_...",
"delta": "Let me think about this...",
"accumulated": "Let me think about this..."
}
}
```
### reason.thinking.completed
Emitted when extended thinking completes.
| Field | Type | Description |
| ---------- | ------ | ------------------------- |
| `turn_id` | string | Turn ID |
| `thinking` | string | Complete thinking content |
```json
{
"type": "reason.thinking.completed",
"data": {
"turn_id": "turn_...",
"thinking": "I need to consider the user's request carefully..."
}
}
```
### reason.item
Emitted when one reasoning artifact completes. One event per provider reasoning block, in emission order.
Carries identity and safe summary text only. The opaque payloads that make the artifact replayable — provider signatures and encrypted reasoning context — are deliberately excluded: they are replay state, not content, and never appear in events or on any API surface.
| Field | Type | Description |
| ------------- | --------- | -------------------------------------------------------------- |
| `turn_id` | string | Turn ID this artifact belongs to |
| `provider` | string | Provider that produced it (`anthropic`, `openai`, `google`) |
| `model` | string? | Model reported by the provider |
| `item_id` | string | Provider-assigned identifier, when the provider issues one |
| `summary` | string\[] | Provider-curated summary segments. Never raw chain-of-thought. |
| `token_count` | integer? | Reasoning tokens attributed to this artifact |
```json
{
"type": "reason.item",
"data": {
"turn_id": "turn_...",
"provider": "openai",
"model": "gpt-6.1-sol",
"item_id": "rs_68a1f...",
"summary": ["Checking the build logs before answering."],
"token_count": 412
}
}
```
## Atom Lifecycle Events
These events mark steps of the reason/act execution loop. `reason.*` here means “the LLM inference step”, not model reasoning — for that see [Reasoning Events](#reasoning-events).
### reason.started
Emitted when LLM inference begins.
| Field | Type | Description |
| ---------- | -------------- | ----------------- |
| `agent_id` | string | Agent ID |
| `metadata` | ModelMetadata? | Model information |
```json
{
"type": "reason.started",
"data": {
"agent_id": "agent_...",
"metadata": {
"model": "gpt-6.1-sol"
}
}
}
```
### reason.completed
Emitted when LLM inference completes.
| Field | Type | Description |
| ----------------- | ----------- | ---------------------------- |
| `success` | boolean | Whether the call succeeded |
| `text_preview` | string? | First 200 chars of response |
| `has_tool_calls` | boolean | Whether tools were requested |
| `tool_call_count` | integer | Number of tool calls |
| `error` | string? | Error if failed |
| `duration_ms` | integer? | Duration |
| `usage` | TokenUsage? | Token usage |
```json
{
"type": "reason.completed",
"data": {
"success": true,
"text_preview": "Hello! I can help you with...",
"has_tool_calls": false,
"tool_call_count": 0,
"duration_ms": 1200,
"usage": {
"input_tokens": 100,
"output_tokens": 50
}
}
}
```
### act.started
Emitted when tool execution batch begins.
| Field | Type | Description |
| ------------ | ------------------ | -------------------- |
| `tool_calls` | ToolCallSummary\[] | Tools to be executed |
```json
{
"type": "act.started",
"data": {
"tool_calls": [
{"id": "tc_1", "name": "get_weather"},
{"id": "tc_2", "name": "search_web"}
]
}
}
```
### act.completed
Emitted when tool execution batch completes.
| Field | Type | Description |
| --------------- | -------- | ------------------------------------------------------------------------------ |
| `completed` | boolean | All tools completed |
| `success_count` | integer | Successful tool calls |
| `error_count` | integer | Failed tool calls. Each is an issue the turn carries on from, not a turn error |
| `duration_ms` | integer? | Total duration |
```json
{
"type": "act.completed",
"data": {
"completed": true,
"success_count": 2,
"error_count": 0,
"duration_ms": 500
}
}
```
### tool.started
Emitted when individual tool execution begins.
| Field | Type | Description |
| ----------- | -------- | ----------------------------- |
| `tool_call` | ToolCall | Full tool call with arguments |
```json
{
"type": "tool.started",
"data": {
"tool_call": {
"id": "tc_1",
"name": "get_weather",
"arguments": {"city": "London"}
}
}
}
```
### tool.completed
Emitted when individual tool execution completes.
| Field | Type | Description |
| ------------------------------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool_call_id` | string | Tool call ID |
| `tool_name` | string | Tool name |
| `success` | boolean | Whether it succeeded |
| `status` | string | “success”, “error”, “timeout”, “cancelled” |
| `result` | ContentPart\[]? | Result content |
| `error` | string? | Error message |
| `severity` | string? | `issue` when the call failed; absent on success. See [Issues and errors](#issues-and-errors) |
| `duration_ms` | integer? | Wall-clock duration |
| `worker_thread_cpu_ns` | integer? | Observed worker-thread CPU during executor-future polls; absent means unknown |
| `executed_arguments` | JSON? | Arguments the tool actually ran with. Present only when a `pre_tool_use` hook rewrote the arguments the model sent (those stay on `tool.started`). Values under credential-named keys such as `password`, `access_token`, or `Authorization` read `[REDACTED]`, and credential-looking text inside other values (bearer tokens, common provider API keys, URL user info) is replaced with `[REDACTED]`. Larger than 8 KiB serialized, it is a truncated JSON string. |
| `executed_arguments_truncated` | boolean? | `true` when `executed_arguments` is truncated |
```json
{
"type": "tool.completed",
"data": {
"tool_call_id": "tc_1",
"tool_name": "get_weather",
"success": true,
"status": "success",
"result": [{"type": "text", "text": "Sunny, 22°C"}],
"duration_ms": 250
}
}
```
Native Linux/macOS hosts measure CPU only while polling the invoked tool executor future, including clock-bracketing overhead. Async waits, hooks, child processes, separately spawned/blocking work and remote compute are outside this scope. Unsupported or failed observations are absent, not zero. This is not total tool CPU, and `duration_ms` remains elapsed wall time.
### Issues and errors
A failure is either an **issue** or an **error**, and the event says which:
* **Issue**: the work recovered. A failed tool call goes back to the model as a result and the turn carries on. Failed `tool.completed` events carry `"severity": "issue"`, and `turn.completed` counts them in `issue_count`. Show issues as warnings.
* **Error**: the work stopped. `turn.failed` and a session task in the `failed` state are errors. Reserve error styling for these.
Do not infer severity from `success: false` alone. Failed `tool.completed` events recorded before `severity` existed omit it; read them as issues.
## LLM Events
### llm.generation
Full visibility into LLM API calls. Emitted after each call.
Background [dreaming](https://everruns.com/docs/features/dreaming/) calls emit usage and timing with `metadata.dreaming_operation_id` on the event envelope. Their messages, tools, output, and captured request are empty to keep private consolidation content out of the session log.
| Field | Type | Description |
| ---------- | ------------------------ | -------------------- |
| `messages` | Message\[] | Messages sent to LLM |
| `tools` | ToolDefinitionSummary\[] | Available tools |
| `output` | LlmGenerationOutput | LLM response |
| `metadata` | LlmGenerationMetadata | Call metadata |
`metadata.finish_reasons` holds the normalized reason (`stop`, `tool_calls`, `length`, `content_filter`). When the call was cut off, `metadata` also carries `provider_finish_reason` (the provider’s own value, such as `max_tokens`) and, when non-zero, `tool_calls_dropped` (tool calls discarded because the response ended mid-call or their arguments were not valid JSON; they never run) and `tool_calls_truncated_executed` (complete calls run from a response that was cut off after them). `truncation_gate` is `retried` or `failed` when the [output truncation](https://everruns.com/docs/capabilities/output-truncation/) policy acted on the dropped calls.
```json
{
"type": "llm.generation",
"data": {
"messages": [...],
"tools": [{"name": "get_weather", "description": "..."}],
"output": {
"text": "Hello!",
"tool_calls": []
},
"metadata": {
"model": "gpt-6.1-sol",
"provider": "openai",
"usage": {"input_tokens": 100, "output_tokens": 50},
"duration_ms": 1200,
"time_to_first_token_ms": 150,
"success": true,
"finish_reasons": ["stop"]
}
}
}
```
## Session Events
### session.started
Emitted when a session begins.
| Field | Type | Description |
| ---------- | ------- | --------------------- |
| `agent_id` | string | Agent ID |
| `model_id` | string? | Model ID if specified |
```json
{
"type": "session.started",
"data": {
"agent_id": "agent_..."
}
}
```
### session.activated
Emitted when a session becomes active (turn started).
| Field | Type | Description |
| ------------------ | ------ | --------------------------- |
| `turn_id` | string | Turn that activated session |
| `input_message_id` | string | Triggering message |
```json
{
"type": "session.activated",
"data": {
"turn_id": "turn_...",
"input_message_id": "message_..."
}
}
```
### session.idled
Emitted when a session becomes idle (turn completed).
| Field | Type | Description |
| ------------ | ----------- | ------------------------ |
| `turn_id` | string | Completed turn |
| `iterations` | integer? | Iterations in turn |
| `usage` | TokenUsage? | Cumulative session usage |
```json
{
"type": "session.idled",
"data": {
"turn_id": "turn_...",
"iterations": 3,
"usage": {
"input_tokens": 1500,
"output_tokens": 800
}
}
}
```
## Sandbox Events
Managed Sandboxes keep a durable logical workspace while their physical compute instance may be replaced. These events make that replacement visible on the session event stream. Provider process state is not restored.
### sandbox.instance_lost
Emitted after Everruns observes that a managed Sandbox’s physical compute instance is gone and before it creates a replacement.
### sandbox.recovered
Emitted after Everruns creates the replacement instance, restores the durable workspace, and persists the new generation.
Both events use the same payload:
| Field | Type | Description |
| ---------------------- | -------- | ---------------------------------------------------------------- |
| `sandbox_id` | string? | Durable logical Sandbox ID when hosted persistence is available. |
| `provider` | string | Compute provider selected by the Sandbox Template. |
| `previous_instance_id` | string | Physical provider resource that disappeared. |
| `current_instance_id` | string? | Replacement resource; present only for `sandbox.recovered`. |
| `generation` | integer? | Current incarnation fence when hosted persistence is available. |
| `process_state_lost` | boolean | Always `true`; processes and memory do not survive replacement. |
```json
{
"type": "sandbox.recovered",
"data": {
"sandbox_id": "sandbox_...",
"provider": "daytona",
"previous_instance_id": "old-provider-instance",
"current_instance_id": "new-provider-instance",
"generation": 2,
"process_state_lost": true
}
}
```
## Subagent Events (retired)
The `subagent.spawned`, `subagent.completed`, `subagent.failed`, and `subagent.cancelled` events have been **retired**. The subagent flow is now modeled as Session Tasks, which emit `task.*` lifecycle events (`task.created`, `task.updated`, `task.message.sent`, `task.message.received`) on the parent session instead. New sessions never emit `subagent.*`.
These event types are no longer produced or part of the supported contract. Historical `subagent.*` events recorded in older session logs remain in storage, but are filtered out of the events and SSE APIs like any unsupported type, they are not returned to consumers (aggregate counters such as `error_count` still include them). Consumers should read `task.*` events going forward.
## Supporting Types
### TokenUsage
Token consumption statistics.
| Field | Type | Description |
| ----------------------- | -------- | ----------------------------------- |
| `input_tokens` | integer | Input/prompt tokens |
| `output_tokens` | integer | Output/completion tokens |
| `cache_read_tokens` | integer? | Tokens read from cache |
| `cache_creation_tokens` | integer? | Tokens written to cache (Anthropic) |
### ModelMetadata
Information about the model used.
| Field | Type | Description |
| ------------- | ------- | -------------------------------- |
| `model` | string | Model name (e.g., “gpt-6.1-sol”) |
| `model_id` | string? | Internal model ID |
| `provider_id` | string? | Internal provider ID |
### ToolCallSummary
Compact tool call representation.
| Field | Type | Description |
| ------ | ------ | ------------ |
| `id` | string | Tool call ID |
| `name` | string | Tool name |
### ToolCall
Full tool call with arguments.
| Field | Type | Description |
| ----------- | ------ | --------------------- |
| `id` | string | Tool call ID |
| `name` | string | Tool name |
| `arguments` | object | Tool arguments (JSON) |
---
# Explanation
> Background on Everruns, why durable execution, why events are the primary store, how the agentic loop and capability layering fit together.
Source:
These pages discuss *why* Everruns is shaped the way it is. They don’t tell you how to do anything (see [How-to guides](https://everruns.com/docs/how-to/)) and they aren’t lookup tables (see [Reference](https://everruns.com/docs/api/)). They’re here to give you a mental model so the other docs make sense.
Read these when:
* You’re evaluating Everruns and want to understand the design.
* A reference page tells you *what* something is but you want to know *why* it exists.
* You’re about to make an architectural decision and want to know which guarantees you can rely on.
## Topics
* [Concepts](https://everruns.com/docs/getting-started/concepts/), a glossary of the entity model: harnesses, agents, sessions, capabilities, and how they compose into a runtime.
* [The agentic loop](https://everruns.com/docs/explanation/agentic-loop/), the reason–act cycle, execution phases, and why turns are bounded.
* [Architecture](https://everruns.com/docs/explanation/architecture/), control plane, workers, and the API-first design.
* [Durable execution](https://everruns.com/docs/explanation/durable-execution/), why agents survive crashes, and the trade-offs of a PostgreSQL-backed engine.
* [Events as the primary store](https://everruns.com/docs/explanation/events/), why an append-only event log is the source of truth, not a side-channel.
* [Command path](https://everruns.com/docs/explanation/command-path/), how the REST API, CLI, MCP and agents all reach the same platform commands, and where they differ.
---
# The agentic loop
> Why agent execution is a bounded reason–act cycle, what execution phases mean, and how multi-step tool flows stay coherent across turns.
Source:
An “agent” is a loop, not a function call. This page explains the loop Everruns runs and why it’s shaped the way it is.
## Reason, then act

Each **turn** is one iteration of:
1. **Reason.** Send the full conversation history (system prompt + messages + previous tool results) to the LLM. The model either produces text or requests tool calls.
2. **Act.** If the model requested tool calls, execute them in parallel. The results become new messages in the conversation.
3. **Loop.** If there were tool calls, go back to step 1. If the model produced a final text response, the turn completes.
A turn is capped at **500 iterations** by default. That cap exists for one reason: a misbehaving prompt can drive the model into an infinite tool-calling spiral, and an unbounded loop costs real money in tokens. The cap is `max_iterations`, set on the agent and overridable per session (session overrides agent, agent overrides the default).
## Why parallel act?
The model often emits several tool calls in one response, “read these three files” or “fetch these two URLs”. Executing them sequentially would serialize independent work for no reason. Everruns runs all tool calls from a single reason step concurrently and only re-enters reason once every call has completed (or failed).
This means tool implementations cannot assume ordering between calls within a single act phase. If two tools must run in order, the model has to emit them across two reason steps (iterations).
## Execution phases
When an assistant message includes tool calls, the model hasn’t given a final answer yet, it’s just narrating its plan. When the message has no tool calls, that’s the final answer.
Everruns labels each assistant message with an **execution phase**: `Commentary` (intermediate, before/between tool calls) or `FinalAnswer` (completed response). Two consumers care:
* **The model.** Some providers (OpenAI Responses API on the GPT-5.4 and GPT-5.5 families) accept and return phase annotations on replayed history. Without them, models can mistake earlier commentary for completed answers and stop early on long flows.
* **The UI.** Phase tells the chat surface whether to keep the “thinking” indicator on or render the message as a final response.
Phases are derived from message state (presence of tool calls) and stored on the message. Providers that don’t accept phases on the wire still get accurate internal tracking.
## Turn lifecycle
Every turn emits these events in order:
```plaintext
turn.started
reason.started → reason.completed (one LLM call)
act.started → act.completed (zero or more tool calls)
... (repeat reason/act until the model produces a final answer or max iterations) ...
turn.completed | turn.failed | turn.cancelled
```
Streaming text and tool calls produce `output.message.delta` and `tool.started` / `tool.completed` events in between. The full event catalog is in the [Event Reference](https://everruns.com/docs/event-reference/).
## Why turns are durable, not in-memory
A naïve implementation of the loop would hold all turn state in worker memory. Everruns doesn’t, because a worker crash mid-turn would lose work the user already paid for.
Instead each step (reason, each tool call) is a separate durable task. The worker persists state after every step. If a worker crashes between steps, the control plane detects the missed heartbeat and re-queues the next task on a different worker. From the application’s perspective: a brief delay, then the stream continues. No retry button required.
This trade-off, paying for a database write on every step, is what makes Everruns a *durable* agentic harness rather than a thin LLM wrapper. See [Durable execution](https://everruns.com/docs/explanation/durable-execution/).
## What happens when the loop “gets stuck”
Four failure modes show up in practice:
* **Runaway tool calls.** The model keeps calling tools without converging. Mitigated by the iteration cap. Once the cap is reached the engine stops scheduling tool calls and the turn ends with `turn.completed` (the `iterations` field shows the count), not `turn.failed`.
* **A tool that hangs.** Tool calls have configurable timeouts. The act phase reports the failure as a tool result so the model can recover on the next reason step.
* **The provider stream breaks.** A dropped connection, a stall or a provider server error is retried with backoff, also after the model has streamed part of its answer, as long as it has not called a tool. The retry emits `output.message.started` again for the same message and streams the answer from the beginning, so render `accumulated` rather than the sum of deltas.
* **The model rejects the prompt as too large.** Context [compaction](https://everruns.com/docs/advanced/compaction/) runs reactively and the request is retried. The conversation continues with older messages compressed.
In all four cases the session stays usable. You don’t lose the conversation; you lose at most one turn.
---
# Architecture
> How Everruns is structured, control plane, workers, REST API, and the design choices behind the API-first, horizontally-scalable, headless approach.
Source:
Everruns is **API-first** and **headless**. The web UI is optional, the SDKs are optional, and the entire platform is reachable through a documented REST API. This page explains why.
## Two processes, one database
A running Everruns deployment is two kinds of process plus PostgreSQL:
* The **control plane** exposes the REST API, owns auth, serves SSE event streams, and persists all state in PostgreSQL.
* **Workers** execute the agentic loop. They claim durable tasks, call LLMs, run tools, and report results back. They hold no long-lived state, restart any worker at any time and nothing breaks.

The control plane can run as several instances behind a load balancer (set `EXPECTED_INSTANCES` so per-instance SSE connection limits are divided across them). Workers scale horizontally: add more for throughput, remove some to save cost. PostgreSQL is the only required piece of stateful infrastructure; Valkey and NATS are optional. [Physical Architecture](https://everruns.com/docs/advanced/physical-architecture/) describes each deployable component, which ones are required, and how they connect.
## Why a separate worker tier?
You could run the agentic loop in the API server process and avoid the extra hop. Everruns doesn’t, for three reasons:
1. **LLM calls are long.** A single turn easily spends 10–60 seconds in network I/O. Tying that to the request thread caps API throughput and makes deploys painful.
2. **Failure isolation.** A misbehaving tool that hangs the worker process should not take down the API. They’re separate concerns and they get separate processes.
3. **Durability.** Workers can crash and the task gets reclaimed by another worker. Tying execution to a request would lose the work the moment the connection drops.
The control plane and workers talk over gRPC inside your VPC. End users only ever see the REST API.
## Why headless
Most agent platforms ship a chat UI and treat the API as a side-channel. Everruns inverts this: the API is the product, and the UI is one client among many (the SDK, the CLI, customer-built apps, and the bundled management UI all consume the same endpoints).
The consequences:
* Every feature has to land in the API before it lands in the UI. There are no “UI-only” features.
* You can ship Everruns as backend infrastructure for a product whose UI looks nothing like ours.
* Multitenancy, auth, and quotas are enforced at the API layer, so you cannot accidentally bypass them by using a different client.
The [management UI](https://everruns.com/docs/features/ui/) exists for operators, configuring providers, browsing sessions, debugging events, not as the primary interaction surface. It is not required in production.
## Why REST + SSE, not WebSockets
Sessions need a bidirectional flow: the client posts messages, the server streams events. WebSockets would be a natural fit, but Everruns uses **REST for writes and SSE for reads**.
* REST is cacheable, debuggable with `curl`, and works through ordinary reverse proxies.
* SSE is a one-way streaming protocol that works through HTTP/1.1, HTTP/2, and HTTP/3 with no special infrastructure.
* The events you’d want to push to the server (cancel, new message) are infrequent enough that a `POST` is the right shape.
Combined with `since_id` resumption and 5-minute connection cycling, clients reconnect routinely and resume without missing events.
## Multitenancy and isolation
Everruns is organization-scoped end-to-end:
* All resources (agents, sessions, capabilities, providers) belong to exactly one organization.
* API keys carry an org membership; the API enforces the boundary on every request.
* Sessions are isolated at the database row level, there is no cross-session filesystem or key-value access.
* Secrets are encrypted at rest with envelope encryption: each value gets its own data key, wrapped by a rotatable deployment-wide key.
## Where the boundaries are
Things the control plane owns: auth, durable task queue, event log, virtual filesystem, key-value storage, capability registry, MCP catalog.
Things workers own: nothing persistent. They borrow the database, run their tasks, and report back.
Things your application owns: the agent definitions, the prompt design, and the channel-specific glue (Slack apps, webhooks, schedules). The platform is intentionally neutral about *what* you build with it.
The platform is also neutral about the model vendor: agents can use OpenAI, Anthropic, Gemini, and the other [providers](https://everruns.com/docs/providers/), and switching one does not change the agent definition.
## Further reading
* [Physical Architecture](https://everruns.com/docs/advanced/physical-architecture/): PostgreSQL, NATS, Valkey, workers, and how they connect.
* [Concepts](https://everruns.com/docs/getting-started/concepts/): the entities the API exposes.
* [Durable execution](https://everruns.com/docs/explanation/durable-execution/): how workers recover a crashed turn.
* [API Reference](https://everruns.com/docs/api/): every endpoint.
---
# Command Path
> How the REST API, the CLI, MCP, the Platform capability and workers all reach the same platform commands, and where each surface differs.
Source:
Everruns has one set of platform commands, such as `create_agent`, `update_harness` or `list_sessions`, and several ways to reach them: the REST API, the `everruns` CLI, the `/mcp` endpoint, the [Platform capability](https://everruns.com/docs/capabilities/platform/), and the `everruns` command inside an agent’s shell. This page shows how each of those surfaces gets to a command, so you can tell what behaves the same everywhere and what does not.

## One command, one run path
Each operation is one command with a name, a parameter schema, an HTTP method and path, and an authorization policy. Every command registers itself in the server’s command registry, and every surface ends at the same `Command::run`. That is where the policy check, tracing and metrics happen, so authorization works the same whichever way a caller arrived.
There are two ways into `Command::run`:
* **Typed, from a REST route.** A handler under `/v1/agents`, `/v1/sessions` and so on builds the command from the request and runs it directly.
* **By name.** Everything else looks the command up by its name and turns the JSON parameters into the typed command, using the schema the command registered. Unknown names, unknown parameters and wrong types fail here, before anything runs.
## Surfaces
| Surface | Who uses it | How it reaches a command |
| ------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| REST API (`/v1/...`) | UI, SDKs, scripts | Typed, from the route |
| `POST /v1/commands/{name}` | the CLI, any HTTP client | By name, with optional `Idempotency-Key` |
| `everruns` CLI | people in a terminal | Parses the command line locally, then calls `POST /v1/commands`; a few commands that need local files or streaming (`login`, `files`, `chat`, `agents create -f`) call REST directly |
| `/mcp` `execute` and `query` | external MCP clients | Runs a shell script in which `everruns ` and the flat names (`create_agent`) are commands; `query` refuses anything that changes state |
| Platform capability | agents with the `discover`, `query`, `execute` tools | The same scripted toolset as `/mcp`, called from the worker over gRPC |
| `everruns` in the agent’s shell | agents in a shell-only harness | Parses the command line in the worker, then sends the command name and parameters over gRPC |
Workers never read the database. When an agent on a worker runs a platform command, the worker sends it to the control plane, which reloads the session, checks again that the session really has the Platform capability, and runs the command as the session’s owner in the session’s organization. It never trusts an identity the worker sends.
## One grammar
The CLI, the scripted toolset and the agent’s shell all parse `everruns --flags` with the same parser, built from a command contract that the server generates from its registry. That is why `everruns agents list --limit 10` means the same thing typed in a terminal and in an agent’s shell: the same flags, the same short options, the same help, and the same errors for an unknown flag or a missing required one. See [CLI](https://everruns.com/docs/features/cli/#platform-commands) for the commands and [Platform](https://everruns.com/docs/capabilities/platform/) for the agent-facing tools.
## Where the surfaces differ
* **Hand-written CLI commands.** A few CLI commands do local work no server command can, such as reading a definition file or syncing files, so they keep their own flags. Every other CLI command comes from the contract.
* **Flat names.** `/mcp` and the Platform capability also accept flat names like `create_agent --name ...`, for scripts written before the noun-verb spelling existed.
* **Read-only mode.** `query` accepts only commands that do not change state; `execute` accepts any command the caller is allowed to run.
* **Retries.** Only `POST /v1/commands` takes an `Idempotency-Key`, because it is the surface a network sits in front of. The CLI sends one on every command and retries dropped connections with the same key. See [CLI retries](https://everruns.com/docs/features/cli/#retries).
On every surface, a command that changes an entity saves the change, its entry in the entity’s history and, over `POST /v1/commands`, the response a retry with the same `Idempotency-Key` gets back, all together. If any of them cannot be saved, the command fails with a server error and nothing changes, so a retry is safe. A few commands that call other services while they run, such as creating a provider (which discovers its models) or installing a plugin, save the change first and its history after.
---
# Durable execution
> Why Everruns persists every step of agent execution to PostgreSQL and runs each session as a leased actor: the guarantees that gives, and the trade-offs.
Source:
The word “durable” in “durable agentic harness engine” means a specific thing: **every step of an agent’s execution survives a process restart**. This page explains why that matters, how it works, and what it costs.
## The problem
An agent turn looks simple from the outside, send a message, get a streamed response. Internally it’s a chain of network calls that each take seconds:
1. Call the LLM provider.
2. Parse tool calls, dispatch them.
3. Wait for each tool to return.
4. Call the LLM again with the results.
5. …repeat…
Anywhere in that chain, the worker process can crash, the container can be reaped, the network can blip. A naïve implementation loses all the work done so far and forces the user to retry. For a 30-second turn that already burned tokens, this is unacceptable.
## The mechanism
Everruns treats each session as an **actor** whose state is its event log. Every message, model response, tool call and tool result is appended to the session’s log in PostgreSQL before anything acts on it. A turn is never held anywhere else: the log is enough to pick it up again.
* Sending a message stores it and marks the session runnable, in one transaction.
* A worker leases the runnable session and runs the whole turn: reason, act, reason again, until the model answers or the turn waits on a person.
* The worker renews its lease every few seconds while the turn runs, and gives it back when the turn ends.

Messages that arrive while a turn runs reach that turn before its next model call. A message that arrives just as the turn ends starts the next turn. A turn waiting on a person (a client-side tool, an approval, a question) holds no worker: the answer is recorded in the log and the session becomes runnable again.
When a worker crashes mid-turn, its lease stops being renewed and expires after about ten seconds. Another worker leases the session, reads the turn’s part of the log and continues the turn where it stopped. The application sees a momentary stall in the SSE stream, then it continues.

A worker that stalls instead of crashing cannot interfere: each lease has a fence number, and a worker whose lease was taken over stops the turn without writing anything more.
## Why a custom engine
The obvious alternative is Temporal (or Cadence, or Restate). Everruns deliberately built its own minimal durable execution instead: session leases and the event log for turns, and `everruns-durable`, a generic, published Rust crate (task queue, event log, schedules, retries, dead letter queue, circuit breakers) with no agent concepts, for the background work around them. The reasoning:
1. **Single dependency.** PostgreSQL is the only required stateful infrastructure (Valkey and NATS are optional). Operators don’t need to run a second cluster with its own ops story.
2. **Co-located with the rest of the platform.** Workflow events and session events live in the same database, in the same transaction when needed. There’s no eventual consistency between “what happened” and “what was reported.”
3. **Tight scope.** Everruns runs agentic workflows specifically, limited fan-out, short-to-medium duration, well-understood failure modes. We don’t need the full Temporal feature set, and the operational surface area of a tightly-scoped engine is much smaller.
The trade-off: no multi-region replication beyond what PostgreSQL itself offers, no language-agnostic SDK (the engine is a Rust library), no visual workflow designer. For agent execution these are not missed.
## Guarantees
What durable execution gives you:
* **No work lost on crash.** If a worker dies, another worker resumes the turn from its log. A model response already recorded is not requested again, and an answer already recorded is not given twice.
* **Persisted tool results.** Tool calls are persisted by their result, not their attempt, so a tool whose result was recorded is not re-run. A call cut off before its result was recorded runs again only when that is safe: its tool declares it read-only or idempotent, or the call never started. A call that must run at most once does not start until its `tool.started` event is stored, so a call without one never ran. Any other unfinished call is recorded as interrupted and the model is told its outcome is unknown. Each call also carries an idempotency key that stays the same when it runs again, sent to MCP servers in the request’s `_meta` as `io.everruns/idempotency-key`, so a server or gateway that honors it can drop the repeat.
* **One worker per session.** A session’s lease keeps two workers from ever running its turn at once.
* **Authoritative history.** Reloading a session reads back the same persisted message sequence, which makes traces and exports authoritative.
What it doesn’t give you:
* **Idempotence of side effects.** A tool that declares itself idempotent is run again after a crash, so if it POSTs to an external API it must make the repeat harmless, for example by forwarding the call’s idempotency key (`ToolContext::idempotency_key`). A tool that declares nothing is never run twice, but a crash after it acted and before its result was recorded leaves its outcome unknown.
* **Real-time latency guarantees.** Persisting every event adds a few milliseconds per write. For agent workloads (already dominated by LLM latency) this is invisible; for hot-loop workloads it would be costly.
## Durability in the Framework
Rust applications that embed the [Framework](https://everruns.com/docs/framework/) get durability the same way: from the session’s event log. With `LocalConfig` the log outlives the process, and a turn a restart cut off continues from it: calls that are safe to repeat run again, the rest are recorded as interrupted. Each session runs as an actor that holds its lease while a turn runs, so processes that share a data directory never run one session at once. See [Framework Architecture](https://everruns.com/docs/framework/architecture/).
## When the database becomes the bottleneck
Everruns is designed to run on a single PostgreSQL primary. Read replicas help for reporting; write-heavy session loads are kept manageable by keeping event payloads compact.
In practice, LLM provider rate limits tend to cap throughput long before the database does.
## Further reading
* [The agentic loop](https://everruns.com/docs/explanation/agentic-loop/), what each step inside a turn looks like.
* [Architecture](https://everruns.com/docs/explanation/architecture/), how the control plane and workers interact.
---
# Events as the primary store
> Why Everruns uses an append-only event log as the source of truth for sessions instead of a conventional message table.
Source:
Most chat systems store messages in a `messages` table and emit events as a side-channel. Everruns inverts this: the **event log is the source of truth**, and messages are derived from events. This page explains why.
## What an event is
An **event** is an immutable record of something that happened in a session. Examples:
* A user submitted a message.
* The LLM produced a token of streaming output.
* A tool was called.
* A tool completed.
* A turn started, completed, failed, or was cancelled.
Every event has a session-local monotonic sequence number, a stable ID (UUID v7), a type in dot notation (`turn.completed`, `output.message.delta`), and a typed payload. Events are appended to the log and never modified or deleted.
The full catalog is in the [Event Reference](https://everruns.com/docs/event-reference/).
## Why not a `messages` table?
A conventional design has:
* A `messages` table for the conversation.
* A separate change-feed (Kafka, NATS, a `pg_notify` channel) for real-time updates.
* A traces table for observability.
This gives you three sources of truth that can disagree. When the SSE stream and the messages table disagree, which one is right? When a message is edited mid-stream, what does the change-feed say? When a tool call is observed but no resulting message is persisted, did it happen?
Everruns avoids the question by making the event log authoritative for all three concerns:
* **Conversation history** is reconstructed from events. A `Message` is a projection of a contiguous range of `output.*` and `input.*` events.
* **The real-time stream** is just a tail of the same log. SSE clients receive events as they’re appended; resumption with `since_id` is a database read against the same table.
* **Tracing and replay** consume the log directly. Every LLM call, tool execution, and state transition is there.
One log, one ordering, one source of truth.
## Consequences
This design has visible consequences in the API:
* **You can’t UPDATE a message.** What you can do is append a new event that supersedes part of the projection. Messages can’t be edited because they don’t exist as rows.
* **Ordering is by sequence number, not timestamp.** Two events with the same wall-clock time still have a strict order. Timestamps are informational.
* **Resumption is cheap.** `since_id` is a primary-key scan. You can disconnect and reconnect to an SSE stream millions of events later and get exactly the missing tail.
* **Audit is free.** Everything that happened in a session is in one table, in order, immutably.
## Compatibility guarantees
Because events are the API contract, Everruns treats them like a public protocol. The compatibility rules are:
| Change | Allowed |
| ------------------------------------------ | ------------------------------------------- |
| Add a new event type | yes |
| Add an optional field to an existing event | yes |
| Add a new enum value to an existing field | yes |
| Remove a field | no, breaking change |
| Change the type of a field | no, breaking change |
| Reuse a sequence number | no, sequence numbers are atomic per session |
Consumers must follow the dual: ignore unknown fields, ignore unknown event types, and treat optional fields as optional. The SDK clients do this automatically.
## What about volume?
A long agent session can produce thousands of events, streaming deltas alone can be hundreds per turn. Two things keep this manageable:
* **Delta events are batched** at \~100ms. The model produces tokens faster than that, but consumers don’t need every token as a separate event.
* **Sessions are bounded by intent, not duration.** Even busy sessions rarely exceed five-figure event counts. PostgreSQL handles that easily.
For very long-running sessions, [context compaction](https://everruns.com/docs/advanced/compaction/) reduces the *prompt* size but does not modify the event log. The full history remains available via [Infinity Context](https://everruns.com/docs/capabilities/infinity-context/) and the event API.
## Further reading
* [Event Reference](https://everruns.com/docs/event-reference/), every event type and payload.
* [The agentic loop](https://everruns.com/docs/explanation/agentic-loop/), what produces events during a turn.
* [How-to: stream events](https://everruns.com/docs/how-to/stream-events/), practical patterns.
---
# A2A (Agent2Agent)
> Let other agents call an Everruns agent over the A2A 1.0 protocol, and let an Everruns agent delegate work to external A2A agents.
Source:
[A2A](https://a2a-protocol.org) is an open protocol for agents to call each other over HTTP. Everruns supports it in both directions:
* **Inbound**: an A2A channel makes an Everruns agent callable by any A2A client, such as another vendor’s agent, the official `a2a` CLI, or the A2A Inspector.
* **Outbound**: the `a2a_agent_delegation` capability lets an Everruns agent hand work to external A2A agents through `spawn_agent`.
Both speak A2A 1.0. The inbound channel also answers A2A 0.3 clients on the same URL.
Framework apps can serve their agents over A2A too. See [A2A in the Framework](#a2a-in-the-framework).
## Expose an agent over A2A
An A2A channel belongs to an agent. Create it through the API with a key you generate. Everruns stores only the key’s SHA-256 hash, so keep the plaintext somewhere safe.
```bash
EVERRUNS=https://your-everruns-host/api
KEY="evra2a_$(openssl rand -hex 32)"
HASH=$(printf %s "$KEY" | sha256sum | cut -d' ' -f1)
curl -sS -X POST "$EVERRUNS/v1/agents/$AGENT_ID/channels" \
-H "Authorization: Bearer $EVERRUNS_API_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"channel_type\": \"a2a\",
\"channel_config\": {
\"session_mode\": \"session_per_invocation\",
\"message\": \"{{a2a.text}}\",
\"agent_card_name\": \"Research agent\",
\"agent_card_description\": \"Researches a topic and returns notes.\",
\"api_key_hash\": \"$HASH\",
\"api_key_prefix\": \"${KEY:0:15}...\"
}
}"
```
The response carries the channel `id`. Publish it so it accepts traffic:
```bash
curl -sS -X POST "$EVERRUNS/v1/agents/$AGENT_ID/channels/$CHANNEL_ID/publish" \
-H "Authorization: Bearer $EVERRUNS_API_KEY"
```
The channel then serves:
| Path | What it is |
| --------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `POST /v1/channels/{channel_id}/a2a` | The A2A JSON-RPC endpoint. Needs `Authorization: Bearer `. |
| `/v1/channels/{channel_id}/a2a/message:send`, `/tasks`, … | The same operations over A2A’s plain HTTP binding (HTTP+JSON). Same key. |
| `GET /v1/channels/{channel_id}/a2a/.well-known/agent-card.json` | The public Agent Card, served only while the channel is live. |
When the agent has an avatar, the Agent Card carries it as `iconUrl`: the 256 px square preset, on the same origin as the card.
### Configuration
| Field | Description |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session_mode` | `session_per_invocation` starts a fresh session for each new task. `shared_session` sends every call into one long-lived session. |
| `message` | Template for the user message the agent receives. `{{a2a.text}}` is the caller’s text parts joined by newlines; `a2a.task_id`, `a2a.context_id`, and `payload` (the raw request params) are also available. |
| `agent_card_name`, `agent_card_description` | Shown in the Agent Card. They default to the agent’s name and description. |
| `rate_limit_per_minute` | Optional per-caller-IP limit for this channel. Over the limit, calls get HTTP 429. |
| `signing_secret` | Optional. When set, every request must also carry an HMAC signature of its body. |
Instead of a generated key, a channel can use the shared channel auth modes: HTTP Basic, OIDC or Google JWT bearer tokens, OAuth2 token introspection, or mTLS. The Agent Card advertises whichever scheme is in use and never includes a credential.
### Call it
Any A2A 1.0 client works. With the official [`a2a` CLI](https://github.com/a2aproject/a2a-go):
```bash
CARD="$EVERRUNS/v1/channels/$CHANNEL_ID/a2a/.well-known/agent-card.json"
a2a card get -a "$CARD"
a2a send -a "$CARD" --auth "Bearer $KEY" "Summarize the A2A spec in three bullets."
a2a send -a "$CARD" --auth "Bearer $KEY" --stream "Explain A2A tasks in one sentence."
```
Or with plain JSON-RPC:
```bash
curl -sS -X POST "$EVERRUNS/v1/channels/$CHANNEL_ID/a2a" \
-H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' \
-H 'A2A-Version: 1.0' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"role": "ROLE_USER",
"messageId": "msg-1",
"parts": [{ "text": "Summarize the A2A spec in three bullets." }]
}
}
}'
```
`SendMessage` waits for the turn to finish and returns the task with the agent’s reply as a `response` artifact. Set `"configuration": { "returnImmediately": true }` to get the task back at once and follow it with `GetTask`, `SubscribeToTask`, or a push notification.
The `A2A-Version` header picks the wire format: `1.0`, or `0.3` for older clients. Without it, 1.0 method names such as `SendMessage` are answered in 1.0 and 0.3 names such as `message/send` in 0.3.
Clients that speak A2A’s plain HTTP binding (HTTP+JSON) use paths below the same URL instead of a JSON-RPC envelope. The Agent Card lists both bindings. This binding is A2A 1.0 only:
```bash
curl -sS -X POST "$EVERRUNS/v1/channels/$CHANNEL_ID/a2a/message:send" \
-H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/a2a+json' \
-d '{
"message": {
"role": "ROLE_USER",
"messageId": "msg-1",
"parts": [{ "text": "Summarize the A2A spec in three bullets." }]
}
}'
```
| HTTP+JSON | Operation |
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| `POST message:send`, `POST message:stream` | `SendMessage`, `SendStreamingMessage` |
| `GET tasks/{id}`, `GET tasks?…`, `POST tasks/{id}:cancel`, `POST tasks/{id}:subscribe` | `GetTask`, `ListTasks`, `CancelTask`, `SubscribeToTask` |
| `POST`/`GET tasks/{id}/pushNotificationConfigs`, `GET`/`DELETE tasks/{id}/pushNotificationConfigs/{configId}` | The push notification config operations |
Errors come back with the matching HTTP status and a `google.rpc.Status` body whose `details[0].reason` names the A2A error, for example `TASK_NOT_FOUND`.
### Supported methods
| A2A 1.0 | A2A 0.3 | Notes |
| -------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `SendMessage` | `message/send` | A `taskId` or `contextId` from an earlier reply continues that conversation. |
| `SendStreamingMessage` | `message/stream` | Server-sent events: the task, then the reply as an artifact, then the final status. Needs `session_mode: session_per_invocation`; a `shared_session` channel answers `-32004`. |
| `GetTask` | `tasks/get` | Current state and the latest turn’s reply. |
| `ListTasks` | `tasks/list` | This channel’s tasks, newest first, filtered by `contextId`, `status`, or `statusTimestampAfter`, with `pageSize` and `pageToken` paging. |
| `SubscribeToTask` | `tasks/resubscribe` | Reattach a stream to a running task. A finished task answers `-32004`. |
| `CancelTask` | `tasks/cancel` | Cancels the running turn. A finished task answers `-32002`. |
| `CreateTaskPushNotificationConfig`, `Get…`, `List…`, `Delete…` | `tasks/pushNotificationConfig/set`, `get`, `list`, `delete` | Webhooks for a task. See below. |
The extended Agent Card is not offered (`-32007`).
A task’s id is the id of the Everruns session that runs it, and its `contextId` is the same value. A caller can only see and continue tasks started through its own channel.
### Push notifications
Register a webhook on a task, or pass one in `SendMessage` as `configuration.taskPushNotificationConfig`:
```json
{
"jsonrpc": "2.0",
"id": 2,
"method": "CreateTaskPushNotificationConfig",
"params": {
"taskId": "",
"id": "my-hook",
"url": "https://hooks.example.com/a2a",
"token": "opaque-value-you-check",
"authentication": { "scheme": "Bearer", "credentials": "hook-secret" }
}
}
```
When the task completes, fails, is canceled, or stops to ask a question, Everruns POSTs the full task (`{ "task": … }`, `Content-Type: application/a2a+json`) to each registered URL. The `token` arrives as `X-A2A-Notification-Token` and `authentication` as `Authorization: {scheme} {credentials}`. Both are stored encrypted and are never returned by `Get` or `List`.
A task holds at most ten configs. URLs must be public `https`; private, loopback, and cloud metadata addresses are rejected. A failed delivery is retried twice and dropped on a 4xx other than 429, so treat `GetTask` as the source of truth.
### Questions and approvals
When the agent asks a question with `ask_user`, the task goes to `input-required`. The status message carries the question as text and as an `everruns/ask_user` data part with a JSON Schema for the answer. Answer it with `SendMessage` on the same `taskId`. A question that asks for a secret reports `auth-required` instead and links to the session, so a person provides the value in Everruns rather than through another agent.
### Personal agents (PACT)
[PACT](https://github.com/openpactprotocol/openpactprotocol) (Personal Agent Consent and Trust) is how a person’s own agent, such as an assistant on their phone, talks to a company’s agent on their behalf. Everruns implements the PACT Identity profile. Add `pact` to an A2A channel’s config to serve it:
```json
{
"pact": {
"audience": "everruns-acme",
"personal_agents": [
{ "issuer": "https://pa.example", "jwks_uri": "https://pa.example/.well-known/jwks.json" }
]
}
}
```
| Field | Description |
| -------------------------------------- | ---------------------------------------------------------------------------------------- |
| `audience` | The value personal agents put in the token’s `aud`. Use the same value on every channel. |
| `personal_agents[].issuer` | The personal agent’s `iss`, matched exactly. Any other issuer is refused. |
| `personal_agents[].jwks_uri` or `jwks` | Where its public keys are: an HTTPS URL, or the JWKS document inline. |
| `personal_agents[].enabled` | `false` refuses that personal agent. Defaults to `true`. |
The channel is then also served at `/v1/channels/{channel_id}/a2a/pact`, with its PACT Agent Card at `/v1/channels/{channel_id}/a2a/pact/.well-known/agent-card.json`. Each request carries a short-lived ES256 or RS256 JWT that the personal agent signs for one of its users (`sub`). `message:send` answers with the agent’s reply as a Message. Its `contextId` continues the conversation for that same user only, and a retried `messageId` returns the stored reply instead of running the agent again. There are no tasks, streaming or push notifications on this URL. The channel’s ordinary A2A URL keeps working with its own key.
#### Acting on the user’s account (PACT Delegated)
With `pact.delegation`, the user can let their personal agent act on their account with your company. The user signs in on your own login page, picks what to allow on an Everruns consent page, and the personal agent receives a delegation token for exactly those permissions (OAuth 2.0 device code).
```json
{
"pact": {
"audience": "everruns-acme",
"personal_agents": [{ "issuer": "https://pa.example", "jwks_uri": "https://pa.example/.well-known/jwks.json" }],
"delegation": {
"brand_name": "Acme",
"login_url": "https://acme.example/pact/login",
"login_issuer": "https://acme.example",
"login_jwks_uri": "https://acme.example/.well-known/jwks.json",
"connected_url": "https://acme.example/pact/connected",
"mcp_server": "acme-account",
"scopes": [
{ "id": "orders:read", "description": "See your orders", "tools": ["mcp_acme_account__list_orders"] },
{ "id": "orders:cancel", "description": "Cancel your orders", "tools": ["mcp_acme_account__cancel_order"] }
]
}
}
}
```
| Field | Description |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `login_url` | Your login page. The user arrives with `return_to` set to the Everruns consent page. After signing them in, your site POSTs a form field `assertion` to that URL: an ES256 or RS256 JWT with `iss`, `aud` (the consent URL), `sub` (your user id), `jti`, `iat`, `exp` (at most five minutes) and `user_code` (from `return_to`). |
| `login_issuer` | The assertion’s `iss`, matched exactly. |
| `login_jwks_uri` or `login_jwks` | The keys you sign assertions with: an HTTPS URL, or the JWKS document inline. |
| `connected_url` | Optional. Where the user lands afterwards, with `status=approved` or `denied`, `scope` and `client` added. |
| `mcp_server` | Optional. The org’s MCP server for your account API. Attach it to the agent with `actsAs: user`; during a delegated turn the user’s token is its bearer token. |
| `scopes[]` | The permissions offered: `id`, the `description` the user sees, and the agent `tools` each one unlocks. |
| `brand_name` | Optional. Your name on the consent page. Defaults to the agent’s name. |
Login and connected pages may use plain `http://` on `localhost` for local development.
The OAuth endpoints live under `/v1/channels/{channel_id}/a2a/pact/oauth/`, with metadata at `.well-known/oauth-authorization-server` and the signing keys at `jwks.json`. Your account API verifies the delegation tokens against those keys: `typ` is `at+jwt`, `iss` is the `oauth` URL, `aud` the endpoint URL, `sub` your user id, and `scope` the granted permissions. Check the scope on every call.
The personal agent sends the token in `X-A2A-User-Delegation: Bearer …` beside its own JWT. The turn then runs as that user. If the agent calls a tool a scope lists and the user has not granted that scope, the answer is a task in `TASK_STATE_AUTH_REQUIRED` whose metadata names the missing scopes (`pact.missingScopes`) and a sign-in link asking for them (`pact.verificationUriComplete`). Every reply under a token carries a signed receipt in `metadata["pact.receipt"]` listing the scoped tools that ran. A conversation started for one of your users does not continue with another user’s token.
## Delegate to external A2A agents
The `a2a_agent_delegation` capability gives an agent a `spawn_agent` target of type `external_a2a`. The agents it may call are fixed in the capability’s configuration, so the model can never reach a URL you did not list:
```json
{
"agents": [
{
"id": "research",
"name": "Research agent",
"description": "Researches a topic and returns notes.",
"base_url": "https://agents.example.com/research"
}
]
}
```
`base_url` is where the Agent Card is resolved (`{base_url}/.well-known/agent-card.json`). The model then calls:
```json
{
"instructions": "Research tide pools and return five bullet notes.",
"target": { "type": "external_a2a", "id": "research" },
"mode": "foreground"
}
```
`foreground` waits for the remote task and returns its reply. `background` returns a `task_id` at once and wakes the session when the remote task finishes; the generic `wait_task`, `message_task`, and `cancel_task` tools work on it. Pass `result_schema` to require a structured result.
URLs are checked before every call: localhost, private ranges, and metadata addresses are refused, resolved addresses are pinned, and redirects are not followed. `allow_local_urls: true` lifts the address check only when `DEPLOYMENT_GRADE=dev`.
> **Status:** On the hosted platform, outbound delegation is experimental and available in Dev environments.
## A2A in the Framework
In a [Framework](https://everruns.com/docs/framework/) app (full guide: [Framework A2A](https://everruns.com/docs/framework/a2a/)):
* To **serve** agents over A2A, turn on the `a2a` feature of `everruns-serve`. Every top-level agent then answers at `POST /v1/channels/{agent}/a2a`, the same path shape as an Everruns channel. See [Serve](https://everruns.com/docs/framework/serve/#a2a).
* To **delegate** to A2A agents, turn on the `a2a` feature of `everruns` and add `CapabilityRef::new("a2a_agent_delegation")` with the config above.
The [A2A example](https://github.com/everruns/everruns/tree/main/examples/serve/a2a) example does both. A `researcher` agent is served over A2A, and a `writer` agent delegates research to it and drafts from its notes. It runs offline by default and on OpenAI when `OPENAI_API_KEY` is set:
```bash
cargo run -p serve-example-a2a --bin researcher # serves on :3000
cargo run -p serve-example-a2a --bin writer -- "tide pools"
```
## See also
* [Sub-agents](https://everruns.com/docs/capabilities/sub-agents/), the shared `spawn_agent` tool.
* [ARD Discovery](https://everruns.com/docs/integrations/ard/), for finding A2A agents at runtime.
* [Channels](https://everruns.com/docs/features/channels/), for channel lifecycle and legacy routes.
## Watch PACT account consent
A personal agent checks an order, receives a request for additional permission when cancellation is refused, and verifies the signed receipt after the customer approves. This is a local walkthrough with a demo client and synthetic store data.
[PACT account consent and signed receipts in Everruns](https://www.youtube-nocookie.com/embed/9FZiYv5PsWY?rel=0)
[Watch on YouTube](https://www.youtube.com/watch?v=9FZiYv5PsWY).
---
# Agent API
> Call one agent from your own code with an agent key. Reference for its base URL, keys, routes, auth, channel settings, streaming, retries, errors and SDKs.
Source:
The **Agent API** exposes one Agent to your own code. An Agent API channel (`channel_type: "api"`) gives the Agent its own **base URL**, and callers use an **agent key** (`evr_ak_…`) to start sessions, send messages, follow events, answer questions and approve tool calls on that Agent. A key reaches only this one channel’s routes. It cannot read or change agents, harnesses, other sessions or anything else in the management API, so you can hand it to an application without handing over your account.
For a step-by-step walkthrough, see [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/). This page is the reference.
## Base URL
Every route on this page is relative to the agent base URL. The channel’s page in the console shows it as the **agent URL**.
| Host | Agent base URL |
| --------------------------------------------------------- | --------------------------------------------------------- |
| Everruns Cloud | `https://app.everruns.com/api/v1/channels/{channel_id}` |
| Self-hosted Platform | `https://your-everruns-host/api/v1/channels/{channel_id}` |
| A [serve](https://everruns.com/docs/framework/serve/) app | `http://localhost:3000/v1/channels/{agent}` |
`{channel_id}` is the channel’s own ID (`appchan_…`), not the Agent’s. In a serve app it is the agent’s name. The shapes are the same on both hosts, so one client works against either; see [The same API on serve](#the-same-api-on-serve).
## Set up a channel
**In the console:**
1. Open the Agent and select the **Integrations** tab.
2. Select **Add channel**, choose **Agent API**, adjust its settings and select **Save channel**.
3. Select **Publish**. A channel serves traffic only while it is live, its Agent is active and the Agent’s exposures are not suspended. See [Channel lifecycle](https://everruns.com/docs/features/channels/#channel-lifecycle).
4. On the channel page, select **Create key** in the **Agent keys** card and copy the secret. It is shown once.
**With the management API**, authenticated as a member who can manage the Agent:
```bash
# Create the channel. Every setting in channel_config is optional.
curl -X POST "$EVERRUNS_API/v1/agents/$AGENT_ID/channels" \
-H "Authorization: Bearer $EVERRUNS_API_KEY" -H "Content-Type: application/json" \
-d '{"channel_type": "api", "channel_config": {"visibility": "activity"}}'
# Publish it (needs the dangerous Agent permission, Owner by default).
curl -X POST "$EVERRUNS_API/v1/agents/$AGENT_ID/channels/$CHANNEL_ID/publish" \
-H "Authorization: Bearer $EVERRUNS_API_KEY"
```
**With the CLI:**
```bash
everruns agents channels create --agent-id "$AGENT_ID" --channel-type api --channel-config '{}'
everruns agents channels publish --agent-id "$AGENT_ID" --channel-id "$CHANNEL_ID"
everruns agents channels keys create --agent-id "$AGENT_ID" --channel-id "$CHANNEL_ID" --name "Support backend"
```
Change settings later with `PATCH /v1/agents/{agent_id}/channels/{channel_id}` and a new `channel_config`. Changing a live channel’s configuration needs the same dangerous Agent permission as publishing.
## Agent keys
An agent key is the credential of one application. Keys belong to the organization, not to the member who created them, so a key keeps working when its creator leaves. Everruns stores only a hash and a display prefix; the secret appears once, in the response that creates or rotates it.
| Method | Path (management API) | What it does |
| ------ | ------------------------------------------------------------------ | ------------------------------------------------------ |
| GET | `/v1/agents/{agent_id}/channels/{channel_id}/keys` | List the channel’s keys, newest first, without secrets |
| POST | `/v1/agents/{agent_id}/channels/{channel_id}/keys` | Create a key |
| POST | `/v1/agents/{agent_id}/channels/{channel_id}/keys/{key_id}/rotate` | Replace the secret, keeping the key’s ID |
| POST | `/v1/agents/{agent_id}/channels/{channel_id}/keys/{key_id}/revoke` | Stop the key for good |
### Create
```bash
curl -X POST "$EVERRUNS_API/v1/agents/$AGENT_ID/channels/$CHANNEL_ID/keys" \
-H "Authorization: Bearer $EVERRUNS_API_KEY" -H "Content-Type: application/json" \
-d '{"name": "Support backend", "permissions": ["end_user"]}'
```
| Field | Required | Meaning |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `name` | Yes | Display name, 1 to 128 characters. Name the application that holds the key, so you know which one to revoke. |
| `expires_at` | No | RFC 3339 time when the key stops working. Must be in the future. Omit for no expiry. |
| `permissions` | No | Every key has `sessions`, the Agent API routes. Add `end_user` to let the key act for your application’s users with the `End-User` header. |
The response is `201` with the key and its `secret`:
```json
{
"id": "agentkey_01933b5a000070008000000000000001",
"channel_id": "appchan_01933b5a000070008000000000000001",
"name": "Support backend",
"prefix": "evr_ak_1a2b3c4d...",
"permissions": ["sessions", "end_user"],
"created_at": "2026-10-13T09:00:00Z",
"secret": "evr_ak_1a2b3c4d…"
}
```
Later reads return the same fields without `secret`, plus `expires_at`, `previous_valid_until`, `last_used_at` (updated at most once a minute) and `revoked_at` when they are set. A channel holds at most 50 live keys; revoke one before creating more.
### Rotate
`POST …/keys/{key_id}/rotate` with an optional `{"overlap_hours": 24}` returns a new `secret` for the same key ID. The replaced secret keeps working for the overlap, from 0 to 168 hours, 24 by default, and `previous_valid_until` says until when. Deploy the new secret within that window. Sessions the key started stay reachable, because they belong to the key’s ID, not its secret.
### Revoke
`POST …/keys/{key_id}/revoke` stops the key immediately, including a previous secret still inside its rotation overlap. A revoked key never works again.
## Authentication
Every request to the Agent API sends `Authorization: Bearer `. The credential decides who the **caller** is, and a caller sees only its own sessions.
| Credential | Caller | How to get it |
| ------------------------------------------------ | --------------------------------------------- | ----------------------------------------------------------- |
| Agent key `evr_ak_…` | The application holding the key | [Agent keys](#agent-keys) |
| Agent key with `end_user`, plus `End-User: ` | One of your application’s users | [End users](#end-users) |
| Runtime token | The end user it was minted for | [Runtime tokens for browsers](#runtime-tokens-for-browsers) |
| A token from your identity provider | The token’s subject, as an end user | [Your identity provider](#your-identity-provider) |
| Personal access token `evr_pat_…` | A member of your organization, as an end user | [Organization members](#organization-members) |
Keys of a retired `api_endpoint` channel (`evr_app_…`) were imported when the channel converted and keep working the same way as agent keys.
### End users
A key acting as itself is your application: every request it sends shares one set of sessions. To keep your users apart, create the key with `"permissions": ["end_user"]` and send `End-User` with each request:
```bash
curl -X POST "$EVERRUNS_AGENT_URL/sessions" \
-H "Authorization: Bearer $EVERRUNS_AGENT_KEY" \
-H "End-User: customer-42"
```
The ID is yours: up to 256 characters, no control characters and no leading or trailing spaces. Everruns creates an end user for it on first sight, scoped to the key, so two applications with different keys never collide on the same ID. Sessions an end user starts belong to that end user: the key acting as itself, or for another user, gets `404` for them. The Agent’s per-user connections (tools that act as the user) use that end user’s own connections, never the key holder’s.
`End-User` from a key without `end_user`, a malformed ID, or `End-User` sent with any credential other than an agent key answers `403`.
### Runtime tokens for browsers
Never ship an agent key in a browser or mobile app. Your backend exchanges it for a short-lived token for one user and hands the app that token:
```bash
curl -X POST "$EVERRUNS_AGENT_URL/runtime-auth" \
-H "Authorization: Bearer $EVERRUNS_AGENT_KEY" \
-H "End-User: customer-42"
```
```json
{
"access_token": "eyJhbGciOi…",
"token_type": "Bearer",
"expires_in": 900,
"virtual_user_id": "identity_01933b5a000070008000000000000001"
}
```
The token lasts 15 minutes, works only on this channel, and cannot be exchanged for another, so a leaked token cannot renew itself. `/runtime-auth` also accepts a token from one of the channel’s `auth_methods`. A key without `End-User` gets `400`: a key acting as itself is an application, not a user.
The page calls the channel with `Authorization: Bearer `. List the page’s origin in [`cors_origins`](#channel-settings) so the browser allows the cross-origin calls. Callers with a runtime token always get the public error codes.
### Your identity provider
`auth_methods` lists up to 5 identity providers whose access tokens the channel accepts besides agent keys: `oidc` (JWTs checked against the issuer’s discovery keys), `google_oidc` (Google Sign-In ID tokens) and `oauth2_introspection` (opaque tokens checked at an RFC 7662 endpoint). Each token’s subject becomes its own end user. Everruns only validates these tokens; it never issues them.
```json
{
"auth_methods": [
{
"mode": "oidc",
"provider": {
"type": "oidc",
"issuer": "https://login.example.com"
},
"requirements": {
"audiences": ["api://support-agent"],
"scopes": ["agent:invoke"]
}
}
]
}
```
`requirements` can also restrict `subjects`, `groups`, email `domains` and exact `claims` values. A token that matches no method answers `401`.
### Organization members
Set `org_members` to `true` to let members of your organization call the Agent with their own personal access token. Each member runs as their own end user with their own connections, as in the console. Only a full-access token counts: a read-only token answers `403` with `insufficient_scope`, because running an Agent is a write. Console session tokens are not accepted.
## Channel settings
Set these in the channel’s `channel_config`, or in the console form. Unknown fields are rejected.
| Setting | Default | Values and effect |
| ----------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `visibility` | `activity` | What event reads and the stream show. `messages`: user input, final assistant text, turn boundaries, and questions or approvals waiting on the caller. `activity`: adds tool start and finish with `tool_activity_text`, never tool names or arguments. `full`: the raw events, including tool names, arguments, results, reasoning and usage, for callers who own both ends. |
| `tool_activity_text` | `Working...` | The text a tool start shows under `activity`. |
| `errors` | `public` | `public`: a failed turn reports only one of the [public error codes](#errors-and-status-codes). `detailed`: the internal error and code, for key-authenticated developers. |
| `session_binding` | `per_user` | `per_user` or `session_per_invocation`. Either way each caller has its own sessions; sessions are never shared between callers. |
| `tool_approvals` | `operator` | Who answers tool calls the Agent’s approval gate holds back. `operator`: someone with access to the Agent in Everruns decides, and the caller sees only that a call waits. `caller`: the caller sees the tool and its arguments and decides through [`tool-approvals`](#questions-and-tool-approvals). |
| `rate_limit_per_minute` | none | Requests per minute per caller and client IP. Unset or `0` turns the channel’s own limit off; the global API limit still applies. |
| `daily_spend_limit_usd` | none | The most one caller (a key acting as itself, or one end user) may spend on this Agent per UTC day, up to 1,000,000. Past it, starting a session or sending a message answers `429` until the next UTC day; reads still work and a running turn finishes. |
| `cors_origins` | none | Up to 20 browser origins, such as `https://app.example.com`, whose pages may call this channel’s routes. Write each exactly as a browser sends it: `https` (or `http` for `localhost`, `127.0.0.1` and `[::1]`), no path, no trailing slash, no default port. These origins reach only this channel’s routes, never the management API. |
| `auth_methods` | none | Up to 5 identity providers. See [Your identity provider](#your-identity-provider). |
| `org_members` | `false` | Accept members’ personal access tokens. See [Organization members](#organization-members). |
## Routes
All routes take `Authorization: Bearer ` and answer JSON, except the stream.
| Method | Path | What it does |
| ------ | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| GET | `/` | The agent card |
| POST | `/sessions` | Start a session |
| GET | `/sessions` | The caller’s sessions, most recently active first |
| GET | `/sessions/{session_id}` | One session, with what it waits on and its latest [work report](https://everruns.com/docs/features/work-reports/#in-the-agent-execution-api) |
| POST | `/sessions/{session_id}/messages` | Send a user message |
| GET | `/sessions/{session_id}/events` | Events, oldest first |
| GET | `/sessions/{session_id}/sse` | Follow events live over SSE |
| POST | `/sessions/{session_id}/question-answers` | Answer the Agent’s questions |
| POST | `/sessions/{session_id}/tool-approvals` | Allow or reject held-back tool calls |
| POST | `/sessions/{session_id}/cancel` | Cancel the running turn |
| POST | `/runtime-auth` | Mint a runtime token for an end user |
A session that belongs to another caller or another channel answers `404`, the same as one that does not exist.
### Get the agent card
`GET /` describes the Agent and which credentials the base URL accepts:
```json
{
"name": "Support agent",
"description": "Answers questions about orders.",
"streaming": true,
"input": {
"text": true,
"images": false,
"files": false
},
"auth": [
{
"type": "agent_key"
},
{
"type": "runtime_token"
},
{
"type": "oidc",
"issuer": "https://login.example.com"
}
],
"links": {
"sessions": "/api/v1/channels/appchan_01933b5a000070008000000000000001/sessions"
}
}
```
`auth` always lists `agent_key` and `runtime_token`, then `personal_access_token` when `org_members` is on, then one `oidc` entry per OpenID Connect issuer and `oauth2` for introspection. `input` says the Agent takes text, images and files ([Send files](https://everruns.com/docs/how-to/call-your-agent-from-code/#send-files)).
### Start a session
`POST /sessions` with an optional body:
```json
{
"title": "Order 42"
}
```
The title is at most 200 characters. An empty body is fine. `metadata` is not supported on Everruns and answers `400`. The session starts idle; send a message to start a turn. The response is `201` with a `Location` header and the session:
```json
{
"id": "session_01933b5a000070008000000000000001",
"title": "Order 42",
"status": "idle",
"created_at": "2026-10-13T09:00:00Z",
"updated_at": "2026-10-13T09:00:00Z"
}
```
`status` is `started`, `active`, `idle`, `waitingforinput` or `failed`. After a turn ends, `last_turn_status` is `completed`, `failed` or `cancelled`.
### List sessions
`GET /sessions?limit=50&page_token=…` returns `{"data": [...], "next_page_token": "…"}`. `limit` is 1 to 200, 50 by default. Pass `next_page_token` back as `page_token` for the next page; it is absent on the last page. A list leaves out `pending_questions` and `pending_approvals`.
### Get a session
`GET /sessions/{session_id}` returns the session plus `pending_questions` and `pending_approvals`: what the session waits on from the caller. See [Questions and tool approvals](#questions-and-tool-approvals).
### Send a message
`POST /sessions/{session_id}/messages` sends a user message. It starts a turn when the session is idle, or steers the running turn.
```json
{
"message": {
"role": "user",
"content": [
{
"type": "text",
"text": "Where is order 42?"
}
]
}
}
```
The role must be `user` and the content one or more `text` parts. The response is `201` with the stored message, including its `id`, `session_id`, `sequence`, `role` and `content`.
### Read events
`GET /sessions/{session_id}/events?after_sequence=0&limit=100` returns `{"data": [...]}`: the session’s events after `after_sequence` (exclusive), oldest first. `limit` is 1 to 500, 100 by default. To page, pass the last event’s `sequence` as the next `after_sequence`. Each event uses the [event envelope](https://everruns.com/docs/event-reference/), filtered by the channel’s [visibility](#what-callers-see).
### Cancel a turn
`POST /sessions/{session_id}/cancel` cancels the running turn and answers `{"status": "cancelled", "message": "Turn cancelled"}`. The stream then shows `turn.cancelled`.
## Streaming
`GET /sessions/{session_id}/sse` follows a session’s events live as Server-Sent Events. It uses the same framing as the session API’s stream (event data shortened here):
```text
event: connected
data: {"status":"connected"}
event: output.message.completed
id: event_01933b5a000070008000000000000009
data: {"id":"event_01933b5a000070008000000000000009","type":"output.message.completed","sequence":9,"data":{"message":{"role":"assistant","content":[{"type":"text","text":"Order 42 shipped yesterday."}]}}}
: heartbeat
event: disconnecting
data: {"reason":"connection_cycle","retry_ms":100}
```
* The SSE `event:` name is the event type, and `data:` is the event envelope after visibility.
* Durable events carry an `id:`. To resume after a drop, reconnect with `since_id` set to the last `id` you received.
* `after_sequence` replays events after that sequence first, then continues live. A client that holds no events yet passes `after_sequence=0` to replay the whole session; without either parameter the stream starts live and can miss a turn that ends quickly.
* A heartbeat comment arrives while nothing happens.
* The server cycles each connection every 5 minutes. It sends `disconnecting` first; reconnect right away with `since_id`.
Open streams are capped per organization and per session; past the cap the stream answers `429`. The SDKs’ `stream_events` handles reconnection for you; see [Stream events](https://everruns.com/docs/how-to/stream-events/).
## What callers see
`visibility` filters both `GET …/events` and the stream, before anything is written.
| Event type | `messages` | `activity` | `full` |
| -------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- |
| `input.message`, `output.message.completed` | Text parts only | Text parts only | Raw |
| `turn.started`, `turn.completed`, `turn.cancelled` | `turn_id` only | `turn_id` only | Raw |
| `turn.failed` | Per `errors` | Per `errors` | Raw, with public errors when `errors` is `public` |
| `tool.call_requested` | Only questions and approvals waiting on the caller | Same | Raw |
| `tool.started`, `tool.completed` | Hidden | `tool_call_id` with `activity` text, or `success` | Raw |
| Everything else (reasoning, usage, deltas) | Hidden | Hidden | Raw |
Under `messages` and `activity`, intermediate assistant commentary is dropped, an event keeps only `id`, `type`, `ts`, `session_id`, `sequence`, `context` and its filtered `data`, and event metadata and tags are removed.
## Questions and tool approvals
When the Agent asks a question with [`ask_user`](https://everruns.com/docs/capabilities/ask-user/), or its [tool approval](https://everruns.com/docs/capabilities/tool-approval/) gate holds a call back, the session waits and `GET /sessions/{session_id}` lists what it waits on. The stream carries the same as a `tool.call_requested` event at every visibility.
```json
{
"id": "session_01933b5a000070008000000000000001",
"status": "waitingforinput",
"created_at": "2026-10-13T09:00:00Z",
"updated_at": "2026-10-13T09:01:00Z",
"pending_questions": [
{
"tool_call_id": "toolu_01933b5a00007000800000000000001",
"questions": [
{
"kind": "choice",
"id": "target",
"header": "Environment",
"question": "Which environment should I check?",
"multi_select": false,
"allow_other": true,
"options": [
{
"label": "Staging",
"description": "The staging cluster",
"default": false
},
{
"label": "Production",
"description": "The live cluster",
"default": false
}
]
}
],
"expires_at": "2026-10-13T09:31:00Z"
}
],
"pending_approvals": [
{
"tool_call_id": "tool_approval_toolu_01933b5a00007000800000000000002",
"answerable": true,
"tool_name": "refund_order",
"arguments": {
"order_id": "42"
},
"risk": "destructive",
"expires_at": "2026-10-13T09:31:00Z"
}
]
}
```
**Answer questions** with `POST /sessions/{session_id}/question-answers`:
```json
{
"tool_call_id": "toolu_01933b5a00007000800000000000001",
"status": "answered",
"answers": [
{
"id": "target",
"selected": ["Staging"]
}
]
}
```
`tool_call_id` is optional when only one question set waits. `status` is `answered` (the default) or `declined`. Each answer names the question `id`, the chosen option labels in `selected`, and free text in `other_text` for a `text` question or a choice that allows another answer. The response is `{"status", "answered_by", "session_status"}` and the turn resumes. A question that asks for a credential (`kind: "secret"`) cannot be answered over this API, only declined; a person completes it in Everruns. Answers that do not match what was asked answer `400`; a session that is not waiting, or a set already answered, answers `409`.
**Approve tool calls** with `POST /sessions/{session_id}/tool-approvals`, on a channel whose `tool_approvals` is `caller`:
```json
{
"decisions": [
{
"tool_call_id": "tool_approval_toolu_01933b5a00007000800000000000002",
"decision": "allow"
}
]
}
```
`decision` is `allow`, `allow_always` (this call and later calls of the same tool in this session), `reject` or `reject_always`. A pending request of the same batch that is left out is settled as not approved; silence never approves. The response lists how each request was settled in `resolved` (`tool_call_id`, `tool`, `outcome`) with the session `status`. When `tool_approvals` is `operator`, approvals show `answerable: false` without the tool name, arguments or risk, and this route answers `403`. A request that expired or was already answered answers `409`.
## Safe retries
`POST /sessions` and `POST /sessions/{session_id}/messages` accept an `Idempotency-Key` header of 1 to 255 printable ASCII characters. A retry with the same key and the same request within 24 hours returns the first response with `Idempotent-Replayed: true` instead of starting a second session or sending the message twice.
* The same key on a different request answers `422` with `idempotency_key_reused`.
* A retry while the first request is still running answers `409` with `idempotency_key_in_progress`.
* Keys belong to the caller, so two applications or two end users never collide on one key.
* Only a success is remembered. A failed request frees its key, so you can fix the request and retry with the same key.
## Errors and status codes
Errors are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details (`application/problem+json`) with `title`, `status` and `detail`, and a `code` where one applies.
| Status | When |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | Invalid body or query (bad `limit` or `page_token`, non-text content, `metadata`), a credential in a question answer, `/runtime-auth` with a key and no `End-User`, or a retired `api_endpoint` request shape (`api_endpoint_retired`) |
| `401` | Missing or invalid credential, including a token none of the channel’s `auth_methods` accepts, or a personal access token when `org_members` is off |
| `403` | The channel is not live, its Agent is inactive or its exposures are suspended (`This agent is not available`); `End-User` not allowed; a read-only personal access token (`insufficient_scope`); or `tool-approvals` on an `operator` channel |
| `404` | No such `api` channel, or a session that is not the caller’s |
| `409` | Nothing to answer, an answer already given, or an `Idempotency-Key` still in progress |
| `422` | An `Idempotency-Key` reused for a different request |
| `429` | The channel’s rate limit, the caller’s daily spending limit, or too many open streams |
The checks run in a fixed order: the channel resolves and is live, then the credential, then the rate limit, then the session. A bad key and a missing channel give the same generic answer, so a caller cannot probe which channels exist.
A turn that fails shows up as a `turn.failed` event rather than an HTTP error. With `errors: "public"`, which callers with a runtime token always get, its `data` carries only `turn_id`, a public `error` message and one of four `error_code` values: `rate_limited`, `service_unavailable`, `request_too_large` or `internal_error`. With `detailed`, it carries the internal `error` and `error_code`.
## SDKs
The Python, TypeScript and Rust SDKs have an `AgentClient` for the Agent API. Use `everruns-sdk` 0.3.1 or later. It reads `EVERRUNS_AGENT_URL` and `EVERRUNS_AGENT_KEY` from the environment, or takes them as arguments, and sends the credential as a Bearer token.
```python
from everruns_sdk import AgentClient
async with AgentClient() as agent:
# One call: start a session, send, follow the stream, return the reply.
print(await agent.run("Where is order 42?"))
# Or step by step, with a safe retry key.
session = await agent.create_session("Order 42", idempotency_key="order-42-start")
await agent.send_message(session["id"], "Where is order 42?", idempotency_key="order-42-msg-1")
async for event in agent.stream_events(session["id"], after_sequence=0):
if event.type == "turn.completed":
break
# Act for one of your users, and mint a token for their browser.
alice = agent.for_end_user("customer-42")
await alice.run("Where is my order?")
token = await alice.runtime_token()
```
```typescript
import { AgentClient } from "@everruns/sdk";
const agent = new AgentClient(); // EVERRUNS_AGENT_URL + EVERRUNS_AGENT_KEY
console.log(await agent.run("Where is order 42?"));
const session = await agent.createSession({ title: "Order 42", idempotencyKey: "order-42-start" });
await agent.sendMessage(session.id, "Where is order 42?", { idempotencyKey: "order-42-msg-1" });
for await (const event of agent.streamEvents(session.id, { afterSequence: 0 })) {
if (event.type === "turn.completed") break;
}
const alice = agent.forEndUser("customer-42");
const token = await alice.runtimeToken();
```
```rust
use everruns_sdk::AgentClient;
let agent = AgentClient::from_env()?; // EVERRUNS_AGENT_URL + EVERRUNS_AGENT_KEY
println!("{}", agent.run("Where is order 42?", None).await?);
let session = agent.create_session(Some("Order 42"), Some("order-42-start")).await?;
agent.send_message(&session.id, "Where is order 42?", Some("order-42-msg-1")).await?;
let alice = agent.for_end_user("customer-42");
let token = alice.runtime_token().await?;
```
The client also has `card`, `list_sessions`, `get_session`, `list_events`, `cancel`, `answer_questions` and `submit_tool_approvals` (camelCase in TypeScript). `run` raises an error when the turn fails. Calling an Agent through the management client’s `sessions.create` and `messages.create` still works but is deprecated. See [SDKs](https://everruns.com/docs/features/sdk/).
## The same API on serve
A [serve](https://everruns.com/docs/framework/serve/) app serves the same routes and shapes for each top-level agent at `/v1/channels/{agent}`, so a client written for one host works against the other: point `EVERRUNS_AGENT_URL` at either. A serve app is one operator’s process rather than a multi-tenant platform, so some parts differ:
* **Credentials.** With no method configured the API is open. Static keys come from `SERVE_API_KEYS` (comma separated) or `[auth]` in `serve.toml`, and `oidc`, `google_oidc` and `oauth2_introspection` methods use the same verifier and requirements as a channel’s `auth_methods`. Keys are not managed through an API, and there is no `End-User`, `/runtime-auth` or personal access token.
* **Callers are not separated.** Every authenticated caller reaches every session of the agent, as an operator would. A session is still reachable only under its own agent’s base URL.
* **Public card.** `GET /v1/channels/{agent}` needs no credential; its `auth` list names the accepted methods, and its `links` add the agent’s AG-UI and A2A routes.
* **No channel settings.** Events are not filtered by visibility, the caller answers tool approvals, and there are no spending or rate limits, browser origins or `Idempotency-Key` handling.
* **Messages.** `content` takes text parts only; send images and other files in `files`, which serve keeps in the session’s `/attachments` folder as the server does.
* **Sessions.** `POST …/sessions` accepts `metadata`, and `GET …/sessions` returns up to `limit` sessions with no `page_token`.
See [Serve](https://everruns.com/docs/framework/serve/#one-agents-api) and the [serve tutorial](https://everruns.com/docs/framework/serve-tutorial/), which calls an agent with `AgentClient`.
## Migrating from `api_endpoint`
The former `api_endpoint` channel type converted to Agent API channels, keeping keys and sessions. Requests in the old shape answer `400` with `api_endpoint_retired`: starting a session no longer takes a message, and a message is an object, not a string. `/v1/apps/{app_id}/api/{channel_id}/…` routes answer `410`. Create the session, then send `{"message": {"role": "user", "content": [...]}}` to `…/messages`.
## See also
* [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/): the step-by-step guide.
* [Channels](https://everruns.com/docs/features/channels/): every channel type and the shared lifecycle.
* [Stream events](https://everruns.com/docs/how-to/stream-events/): reconnection patterns.
* [Handle errors and cancel turns](https://everruns.com/docs/how-to/handle-errors-and-cancellation/).
* [Event Reference](https://everruns.com/docs/event-reference/): every event type.
* [API reference](https://everruns.com/docs/api/): the generated reference for every route.
---
# Agent Checks
> Advisory quality checks for agent configurations, structural problems, completeness gaps, and cost warnings surfaced while you build.
Source:
Agent checks review an agent configuration and surface advisory findings while you build: structural problems (duplicated instructions, conflicting style guidance), completeness gaps (tool references that do not exist), and cost warnings (oversized prompts).
Checks are advisory only. Findings never block saving, publishing, or version creation.
## Where Findings Appear
* **Agent editor → Preview tab**: a Checks card lists findings for the current draft, updating as you edit.
* **API**: `POST /v1/agents/preview` returns a `findings` array alongside the resolved system prompt and tools.
* **MCP / platform commands**: the `preview_agent` and `analyze_agent` commands return the same findings, so agents and automations can review configurations programmatically.
## Findings
Each finding includes:
| Field | Description |
| ---------- | --------------------------------------------------------------------------- |
| `rule_id` | Stable rule identifier, e.g. `prompt.duplicate_paragraphs` |
| `severity` | `warning`, `info`, or `suggestion`, there is no `error`; checks never block |
| `category` | `structure`, `completeness`, `effectiveness`, `safety`, or `cost` |
| `message` | Human-readable explanation |
| `location` | The config field (and byte span, when applicable) the finding points at |
## AI Analysis
The **Analyze** button on the Checks card runs a deeper on-demand review using the platform’s internal utility LLM (requires `UTILITY_OPENAI_API_KEY` or `UTILITY_OPENROUTER_API_KEY` on the deployment). Three scoped checkers run in parallel:
| Rule | What it catches |
| ------------------- | -------------------------------------------------------------------------------------------------------------- |
| `llm.contradiction` | Instructions that cannot both be followed, including conflicts between the prompt and capability contributions |
| `llm.structure` | Redundancy, verbosity, vague instructions, and structure that buries critical rules |
| `llm.tool_guidance` | Prompt guidance that misdescribes available tools or assumes functionality no tool provides |
LLM findings can carry a suggested replacement for the offending text; when the finding is anchored to a span of your prompt, an **Apply fix** button replaces it in place. Analysis is available via `POST /v1/agents/analyze`, which returns built-in and LLM findings merged.
The reviewed prompt is treated strictly as data: checker outputs are bounded, severities are clamped, and findings are advisory text only.
## Health Checks
A **health check** runs the agent for real. It generates a handful of smoke-test cases from the agent’s description, system prompt, and capabilities, runs each as an actual session against the agent’s configured model, and scores the result two ways:
* **Deterministic**: the agent produced a non-empty answer and finished within a turn budget.
* **AI judge**: the platform’s utility LLM grades the agent’s final response against a rubric generated for that case.
A case passes only when both checks pass. The run surfaces a score card (pass rate, passed count, average score, average turns) and a per-case list, each case links to the real session so you can inspect the full conversation, tool calls, and events.
Health checks are asynchronous (they run several real sessions and take a minute or two). Trigger one and poll for the result:
| Method | Path | Description |
| ------ | ---------------------------------------------- | -------------------------------------------------------------------- |
| `POST` | `/v1/agents/{agent_id}/health-checks` | Start a run; returns a pending run with an `id` |
| `GET` | `/v1/agents/{agent_id}/health-checks/{run_id}` | Poll the run; `status` goes `pending → running → completed`/`failed` |
| `GET` | `/v1/agents/{agent_id}/health-checks` | List recent runs for the agent |
Runs are stored per agent and keyed by the resolved config hash. Health checks require the utility LLM (`UTILITY_OPENAI_API_KEY` or `UTILITY_OPENROUTER_API_KEY`) to generate and judge cases, and the agent’s own model must be usable. They are advisory: a low score never blocks anything.
## Built-in Rules
Checks run against the *resolved* configuration, after harness and capability contributions are merged, so they can catch issues that span layers.
| Rule | Severity | What it catches |
| ----------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt.empty` | info | Agent has no system prompt of its own |
| `prompt.very_long` | warning | Authored prompt over 32 KiB, sent on every model turn |
| `prompt.resolved_very_long` | info | Full prompt over 96 KiB after harness/capability contributions |
| `prompt.template_variables` | warning | `{{placeholder}}` text that would reach the model literally |
| `prompt.duplicate_paragraphs` | warning | The same paragraph appears more than once |
| `prompt.restates_contribution` | info | Prompt duplicates text already contributed by the harness or a capability |
| `prompt.conflicting_style` | info | Asks for both brevity and detail without stating conditions |
| `tools.unknown_reference` | info | Prompt references a tool that no enabled tool or capability provides |
| `tools.duplicate_names` | warning | Two tools share a name, so the model cannot distinguish them |
| `capabilities.superseded` | suggestion | An enabled capability does nothing because another enabled capability replaces it (for example tool search alongside [Tools in Shell](https://everruns.com/docs/capabilities/tools-in-shell/)) |
| `capabilities.duplicate_web_search` | warning | The agent can search the web in more than one way, for example [Web search](https://everruns.com/docs/capabilities/web-search/) next to Parallel Search and Fetch or an MCP server’s `web_search` tool, so the model has to guess which one to use |
High-cardinality rules (`prompt.duplicate_paragraphs`, `tools.unknown_reference`, `tools.duplicate_names`) cap how many findings they emit. When the cap is exceeded they add a single companion `info` finding with the rule ID suffixed `.summary` (e.g. `prompt.duplicate_paragraphs.summary`) noting that only the first N were shown, so a large prompt cannot amplify into an unbounded response.
## Roadmap
A later phase adds org-configurable rules: per-rule enable/severity settings for the built-ins plus custom declarative and natural-language-rubric rules.
---
# Agent Triggers
> Run an agent proactively on a recurring schedule, choose session reuse, test it immediately, and inspect recent outcomes.
Source:
Agent triggers let an Agent start work without a user message. A trigger belongs to one Agent, runs on that Agent’s Harness, and sends a configured message when it fires.
| `trigger_type` | Fires when | Set up |
| -------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `schedule` | A cron expression matches | This page |
| `webhook` | An external system calls the trigger’s token-authenticated URL | API (`token`, optional `filter`, `rate_limit_per_minute`) |
| `github` | A GitHub event reaches the Agent’s GitHub App, such as a pull request | [Summarize GitHub pull requests](https://everruns.com/docs/how-to/summarize-github-pull-requests/) |
| `mcp_event` | One of the Agent’s MCP servers emits a subscribed event | [MCP Events](https://everruns.com/docs/features/mcp/) |
The rest of this page covers schedule triggers.
## Create a trigger in the UI
1. Open the agent.
2. Select the **Integrations** tab.
3. Select **Add trigger**.
4. Choose a preset or enter a 5-field cron expression (or a 7-field expression whose year is `*`), then enter an IANA timezone. In 5-field cron, weekdays count 0-6 from Sunday (7 is also Sunday), so `0 9 * * 1-5` runs Monday through Friday. A 7-field expression counts weekdays 1-7 from Sunday, so its Monday through Friday is `2-6` (or `MON-FRI`). The editor shows the schedule in human-readable form and previews the next eight runs. The default timezone is `UTC`.
5. Choose a session mode:
* **Shared session** reuses one durable session for this trigger. Use it when later runs should see the trigger’s previous conversation.
* **New session per run** creates a fresh session every time. Use it when each run should be isolated.
6. Enter the message that starts the run. Messages may use `{{...}}` template values from the agent, trigger, and invocation context.
7. Leave **Enabled** on to activate the schedule, then select **Create trigger**.
The trigger card shows the schedule in human-readable form, its timezone, message, session mode, enabled state, and recent run outcomes.
## Operate and test a trigger
From the **Triggers** section of the **Integrations** tab you can:
* enable or disable the recurring schedule;
* edit its schedule, timezone, session mode, message, or enabled state;
* select **Run now** to start one invocation immediately;
* inspect the most recent durable execution outcomes; or
* delete the trigger.
**Run now** uses the same execution path as a scheduled run. The trigger must be enabled.
## API
Agent triggers are managed below `/v1/agents/{agent_id}/triggers`:
| Method | Path | Purpose |
| -------- | ----------------------------------------------------- | ------------------------- |
| `GET` | `/v1/agents/{agent_id}/triggers` | List triggers |
| `POST` | `/v1/agents/{agent_id}/triggers` | Create a schedule trigger |
| `GET` | `/v1/agents/{agent_id}/triggers/{trigger_id}` | Get one trigger |
| `PATCH` | `/v1/agents/{agent_id}/triggers/{trigger_id}` | Update provided fields |
| `DELETE` | `/v1/agents/{agent_id}/triggers/{trigger_id}` | Delete a trigger |
| `POST` | `/v1/agents/{agent_id}/triggers/{trigger_id}/trigger` | Run it now |
| `GET` | `/v1/agents/{agent_id}/triggers/{trigger_id}/runs` | List recent outcomes |
Schedule create requests accept `trigger_type: "schedule"`, `cron_expression`, `timezone`, `session_mode`, `message`, and `enabled`. `GET /v1/agents/{agent_id}/triggers/{trigger_id}/deliveries` lists recent deliveries, including ones that were filtered out or deduplicated. See the [API reference](https://everruns.com/docs/api/) for current request and response schemas.
## Run a saved script
A schedule or webhook trigger can run one of the Agent’s [saved scripts](https://everruns.com/docs/capabilities/tools-in-shell/#saved-scripts) instead of asking the model. Set `script` on the trigger:
```json
{
"trigger_type": "schedule",
"cron_expression": "0 9 * * 1-5",
"message": "Daily triage",
"script": { "script": "triage-prs", "input": { "repo": "acme/web" } }
}
```
* When the trigger fires, its message is recorded in the session as usual, and the turn runs `tools scripts triage-prs` with the input on stdin. No model call is made.
* The run appears in the session as one `bash` tool call, with its result.
* The Agent needs [Tools in Shell](https://everruns.com/docs/capabilities/tools-in-shell/). The script must exist when the trigger is saved.
* No one is there to answer an approval. A call that needs approval stops the script, and the stop is recorded in the result.
* With `"wake_agent_on_failure": true`, a run that fails or stops hands its result to the Agent’s model, which answers in the same turn. Without it, the run only records the result.
* Input strings are templates over the event, like the message. A string that is only one placeholder passes that value as is, so on a webhook trigger `{"pr": "{{payload.pull_request}}"}` gives the script the request’s `pull_request` object; any other string, such as `"#{{payload.number}}"`, is rendered as text. A missing value becomes `null`.
* To make the trigger ask the model again, update it with `"script": {"script": ""}`.
## Run in a Slack conversation
A schedule trigger can run in a conversation of one of the Agent’s own [Slack](https://everruns.com/docs/capabilities/slack/) channels instead of a session of its own, such as a 07:00 briefing in the direct message where a person already talks to the Agent. Set `target_channel_id` and `target_binding_key` on create or update:
```json
{
"trigger_type": "schedule",
"cron_expression": "0 7 * * *",
"timezone": "America/Chicago",
"message": "Morning briefing",
"target_channel_id": "appchan_01933b5a000070008000000000000001",
"target_binding_key": "slack:user:U0123ABCD"
}
```
* The binding key names the conversation the way the channel keys its sessions: `slack:user:{user_id}` on a `per_user` channel (a direct message with that person) or `slack:channel:{channel_id}` on a `per_channel` channel (a top-level post).
* Each run lands in the session a Slack message from that conversation would, so the run and the conversation share one history. The first run creates and binds it, even before the person has written, and opens the direct message.
* The run posts its replies there like an answer to a Slack message, and the Agent’s `send_message` reaches the same conversation. Posts are top-level, never in a thread.
* Only the Agent’s own Slack channel can be targeted, by someone who can manage the Agent. If the channel is disabled, retired or no longer keys sessions that way, the run fails and its delivery records why. It never falls back to a session of its own.
* `session_mode` does not apply, and the trigger cannot also run a saved script. Update with `"target_channel_id": ""` to remove the target.
## Migrated App Schedules
The retired App model allowed `schedule` channels. Everruns migrated Agent-bound schedules to Agent triggers and preserved their cron expression, timezone, session mode, message, execution identity, and history.
Agent triggers, webhook triggers, and session schedules solve different problems:
* use an **agent trigger** when an agent should wake itself on a recurring schedule;
* use a **webhook trigger** when an external HTTP request should invoke an Agent; and
* use a **session schedule** when the current conversation should continue later.
## See also
* [Slack Integration](https://everruns.com/docs/capabilities/slack/), publish an Agent to an inbound messaging endpoint.
* [Session participants](https://everruns.com/docs/features/session-participants/), understand the host agent used by a trigger-created session.
* [API reference](https://everruns.com/docs/api/), exact trigger schemas and responses.
---
# Capabilities
> Capabilities give an agent tools, system prompt fragments, and session state. Overview with links to the reference.
Source:
A **capability** is a self-contained unit that extends an agent. Each capability can contribute three kinds of thing:
1. **Tools**: functions the agent can invoke during a turn.
2. **System prompt additions**: text prepended to the agent’s prompt that teaches the model when and how to use those tools.
3. **Mount points**: files, directories, or session state the tools need to operate.
Agents *compose* capabilities. Enable as many as you need; leave the rest disabled. The runtime resolves capabilities in topological order (dependencies first) and concatenates their system prompt fragments in the order you configured them on the agent.
## Why capabilities, not just tools
A bare tool registration is a function with a JSON schema. Capabilities exist because real tools need more than that. To use `bashkit_shell` effectively, the agent needs both the `bash` tool *and* the prompt fragment explaining the sandbox model *and* the session filesystem (a dependency). Capabilities bundle those concerns into one enable/disable unit.
See [Why capabilities are first-class](https://everruns.com/docs/getting-started/concepts/#capability) for the full rationale.
## Where capabilities come from
| Source | ID format | Example |
| -------------- | -------------------- | ---------------------------------- |
| Built-in | `snake_case` | `web_fetch`, `session_file_system` |
| MCP server | `mcp:{uuid}` | `mcp:550e8400-...` |
| Registry skill | `skill:{uuid}` | `skill:550e8400-...` |
| Declarative | `declarative:{name}` | `declarative:research_pack` |
All four kinds participate in the same merge, dependency resolution, and tool-name prefixing. The agent doesn’t care where a tool came from.
## Where to attach them
Capabilities can be attached at three layers, and the layers stack additively:
* **Harness**: defaults for every session that uses this harness.
* **Agent**: capabilities for this specific role.
* **Session**: extras for this one conversation.
See [Why three configuration layers](https://everruns.com/docs/getting-started/concepts/#runtimeagent) for how the merge works.
## Browse the catalog
The full list of built-in capabilities, organised by category, lives in the reference:
* **[Capabilities reference](https://everruns.com/docs/capabilities/)**: every capability with ID, tools, parameters, and dependencies.
## Do something
* [Equip an agent with tools](https://everruns.com/docs/how-to/equip-agents-with-tools/), the practical recipe.
* [Give an agent web access](https://everruns.com/docs/how-to/give-an-agent-web-access/), narrower task with network policies.
* [Customize a harness](https://everruns.com/docs/how-to/customize-a-harness/), bundle capabilities at the harness layer.
## See also
* [Concepts](https://everruns.com/docs/getting-started/concepts/), the entity model in full.
---
# Change History and Manager Context
> Record why a change was made, read who changed an entity, and keep notes managers want respected before anyone changes it.
Source:
Every change to an agent, harness, workspace, provider, knowledge base or other managed entity is recorded in that entity’s history. An entry says what changed, who made the change, and through which surface. If an agent made it, the entry also names the session. It can also say why. Each entity can carry **manager context**: notes for the people and agents who change it, such as “owned by support” or “do not switch the model without an eval run”.
## Reasons
A reason is a short note in your own words that travels with the call and is stored on the history entry. It is optional for people and scripts. Agents on the platform are told to always give one.
| Surface | How to pass a reason |
| ------------------- | ------------------------------------------------------- |
| CLI | `--reason "..."` on any command |
| Commands API | `"reason": "..."` in the `/v1/commands/{name}` envelope |
| REST | `everruns-change-reason` header, percent-encoded |
| Agent shell and MCP | `--reason "..."` on the command |
```bash
everruns agents update agent_01h9 --model gpt-6.1 --reason "Support asked for faster answers"
```
A reason is caller text. Everruns stores it as given and does not verify it. Never put a secret in one.
## History
```bash
everruns history list agent_01h9 --limit 10 # one entity, newest first
everruns history org --via-agent agent_01h9 # what one agent changed across the org
```
Each entry carries the action (`created`, `updated`, `deleted`, …), the command that ran, the fields the caller asked to change, the actor, the surface (`api`, `commands`, `mcp`, `platform`, `worker`), and the reason. The REST equivalents are `GET /v1/history/{entity_ref}` and `GET /v1/history`.
Anyone who can see an entity can read its history. The organization-wide view needs audit-log access. Kinds whose ids carry no prefix (`schedule`, `saved_report`, `check_rule`) need `--kind`.
## Revisions and restore
Every change that alters an entity’s configuration makes a new revision, numbered from 1, and keeps a snapshot of the entity as it stood after the change. An update that leaves everything as it was is still recorded, but makes no revision.
```bash
everruns history show agent_01h9 --revision 4 # the agent as it was at revision 4
everruns history diff agent_01h9 --from 4 # what changed since then
everruns history restore agent_01h9 --revision 4 --reason "Revert the prompt change"
```
A restore is a new change, not a rewind. It runs the entity’s own update command with the values from that revision, so the same permissions, validation and manager context checks apply. History records it as `restored`, with the revision it brought back. A restore that cannot bring a field back, for example an optional field the update command cannot clear, returns a warning naming the field.
Secrets are never part of a snapshot. A snapshot records only whether each secret is set and a fingerprint that changes when the secret changes, so a diff can say a key was rotated without showing it. A restore keeps every secret’s current value and warns when it differs from the restored revision. Re-enter the old value yourself if you need it.
The REST equivalents are `GET /v1/history/{entity_ref}/revisions/{revision}`, `GET /v1/history/{entity_ref}/diff?from=N` and `POST /v1/history/{entity_ref}/restore`. Each entity keeps snapshots for its newest 500 revisions. Older entries keep their reason but lose the snapshot.
### Agent revisions replace agent versions
Agents no longer have saved, published or default versions, and channels, triggers and session participants no longer pin one. Every exposure runs the agent’s current configuration. To go back to an earlier configuration, restore the revision you want:
```bash
everruns history list agent_01h9 # find the revision
everruns history restore agent_01h9 --revision 7 --reason "Back to the pre-launch prompt"
```
Each session records the agent revision it started on as `agent_revision`, so `everruns history show --revision N` shows the configuration that ran. Versions saved before the change were moved into the agent’s history as revisions, in the order they were created, with the version’s summary as the reason. A channel or trigger that was pinned to a version now runs the current agent, and the pinned configuration is one restore away.
## Manager context
Manager context is one markdown document per entity (up to 16 KiB) with a revision that increases on every write.
```bash
everruns context get agent_01h9
everruns context append agent_01h9 --text "Answers must stay suitable for children." --reason "Product review"
everruns context set agent_01h9 --content @notes.md --expected-revision 3
everruns context clear agent_01h9 --expected-revision 4
```
Reading or writing an entity’s context requires permission to manage that entity. Writes are recorded in the entity’s history like any other change. A write with `--expected-revision` that no longer matches fails with `manager_context_changed`, so two editors cannot silently overwrite each other. Deleting an entity deletes its context.
The context never reaches the entity’s own runtime. An agent’s sessions do not see their agent’s notes or history, so the notes cannot steer the agent they describe. Knowledge entries, eval cases and sessions have history but no context.
## Acknowledging context before a change
A change can say which context revision its author read, with `--context-revision` (CLI and agent shell), `"context_revision"` (commands envelope) or the `everruns-context-revision` header (REST):
* If the context has changed since that revision, the change is refused with `manager_context_changed`. The error’s `allowed_actions` says to re-read the context and decide again.
* If the entity has context and the change acknowledged none, the change goes through with a warning. The commands envelope returns it in `warnings`, and the CLI and agent shell print it on stderr.
```bash
everruns context get agent_01h9 | jq .revision # 5
everruns agents update agent_01h9 --name "Support bot" \
--context-revision 5 --reason "Rename agreed with support"
```
## In the UI
On an entity’s page, open the **⋯** menu at the end of the header:
* **History** lists the entity’s changes, newest first, with who made each one, the agent session it came through, the surface and the reason. Open an entry to compare it with the current state. Secrets show only as changed or unchanged. **Restore this point** asks for a reason and lists the secrets that keep their current values.
* **Manager notes** shows the entity’s manager context as markdown. Only people who manage the entity see this item. Edit the notes and save them with an optional reason. If someone else saved first, the save is refused and you are asked to reload.
The open sheet is part of the page address (`?sheet=history`, `?sheet=notes`), so a link reopens it. On the agent page, edit mode shows “This agent has manager notes” under the title when the agent has notes. Its save, archive and delete flows have an optional **Reason for this change** field.
## Agents
Platform Chat, the Platform capability and the `/mcp` server all tell agents the same rule: read an entity’s context before changing it, pass `--context-revision` with the revision read and a `--reason` with what the user asked for, and ask the user when a request conflicts with the recorded context. Agents treat context and history as data written by people in the organization, not as instructions.
A change an agent makes without a reason is refused: the command fails with the `reason_required` code and a `retry` action, and the agent retries with `--reason`. Built-in tools such as `spawn_agent` give their own reasons, so they keep working. This is the **Require reasons from agents** feature flag, on by default. An organization can turn it off to let those changes through with a warning that asks the agent to give a reason next time; the `everruns_entity_changes_without_reason_total` metric then counts them, by entity kind. People in the UI and scripts calling the API may always leave the reason out.
## See also
* [CLI](https://everruns.com/docs/features/cli/): global flags and the command surface.
* [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/): the built-in agent that follows these rules.
---
# Channels
> Expose an Agent to Slack, AG-UI clients, A2A agents, FCP callers, and Public Chat visitors through Agent-owned channels, each with its own lifecycle and auth.
Source:
A **channel** is an Agent-owned way for an external caller to reach that Agent and get a reply. Each channel belongs to exactly one Agent and has its own transport configuration, authentication, session routing, and publish state. One Agent can have several channels, and publishing or unpublishing one does not change the others.
Use a channel when an external peer sends a request and waits for a reply. Use an [Agent trigger](https://everruns.com/docs/features/agent-triggers/) when a schedule or event starts Agent work without a reply channel.


## Channel types
| Type | `channel_type` | Who calls it | Canonical route | Guide |
| ----------- | -------------- | ----------------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------- |
| Slack | `slack` | Slack users, through a Slack app | `/v1/channels/{channel_id}/slack/events` | [Slack](https://everruns.com/docs/integrations/slack/) |
| AG-UI | `ag_ui` | Browser and app clients that speak the AG-UI protocol | `/v1/channels/{channel_id}/ag-ui` | [AG-UI](#ag-ui) |
| A2A | `a2a` | Other agents, over the A2A protocol | `/v1/channels/{channel_id}/a2a` | [A2A](https://everruns.com/docs/features/a2a/) |
| FCP | `fcp` | Any HTTP client, text in and text out | `/v1/channels/{channel_id}/fcp` | [FCP](#fcp) |
| Work Chat | `work_chat` | Signed-in consumers using a dedicated work interface | `/v1/channels/{channel_id}` | [Work Chat](#work-chat) |
| Public Chat | `public_chat` | Visitors to a hosted chat website for one Agent | `/v1/channels/{channel_id}/public-chat` | [Public Chat](#public-chat) |
| Voice | `voice` | People talking from a browser or app, over WebRTC | `/v1/agents/{agent_id}/channels/{channel_id}/voice/calls` | [Voice](https://everruns.com/docs/features/voice/) |
| Agent API | `api` | Your own code, with an agent key | `/v1/channels/{channel_id}` | [Agent API](https://everruns.com/docs/features/agent-api/) |
| Poppy | `poppy` | People’s own agents, over the Personal Agent Protocol | `/v1/channels/{channel_id}/poppy` | [Poppy](https://everruns.com/docs/features/poppy/) |
Routes are relative to the API base, for example `https://your-everruns-host/api`. The channel ID in each route is the channel’s own ID, not the Agent’s.
## Download received files
Files received through Slack, AG-UI and A2A remain available to the Agent in its session. Opening a stored file’s `/v1/files/{file_id}` URL downloads its bytes instead of rendering it on the application’s origin. This endpoint uses `Content-Disposition: attachment`, `Content-Type: application/octet-stream` and `X-Content-Type-Options: nosniff` for every stored file, including PDFs and zip archives. File listings retain the original media type. Downloads require authentication and membership in the file’s organization.
## Create a channel
1. Open the Agent and select the **Integrations** tab.
2. Select **Add channel** and choose the channel type.
3. Configure the type-specific fields and select **Save channel**.
4. Select **Publish** to make the channel live.
The same operations are available over the API under `/v1/agents/{agent_id}/channels`, with `publish` and `unpublish` actions per channel. Every channel runs the Agent’s current configuration.
## Channel lifecycle
Each channel has its own lifecycle:
```text
Draft ⇄ Live
Draft → Disabled
Live → Disabled
Disabled → Draft
```
* **Draft**: Configured but does not accept ingress traffic.
* **Live**: Published and able to accept traffic while its Agent is active and exposures are not suspended.
* **Disabled**: Kept for configuration but rejects ingress traffic and does not invoke the Agent.
Publishing and unpublishing require the dangerous Agent permission (Owner by default). The same permission is required to change a live channel’s configuration, including its authentication and secrets, or to disable it. Members who can manage Agents can still edit Draft and Disabled channels.
A channel serves traffic only when all three hold: the channel is live, the Agent is active, and the Agent’s exposures are not suspended. Suspending exposures from the Agent’s **Integrations** tab takes every channel of that Agent offline at once without changing each channel’s state.
Errors from public channels are sanitized so they do not expose internal state, such as whether a channel exists or why it is offline.
## Files people send
Files sent to a channel reach the Agent the same way on every channel. Each one is stored with the message and copied into the session’s `/attachments` folder, where the Agent can open it again later in the conversation, including after compaction.
| Channel | What is taken |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Slack | Files shared in the message, downloaded with the app’s bot token. Files linked from external services are described by name. |
| AG-UI | Image, audio, video and document parts of the user message, inline or by URL. |
| Public Chat | The same parts, inline only: at most 3 files of up to 10 MB, since visitors are not signed in. |
| A2A | File parts, as bytes or a URI. PACT endpoints take text only, as the PACT protocol requires. |
| Agent API | The `files` list of a message, as base64 `data` or a `url` ([Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/#send-files)). A [serve](https://everruns.com/docs/framework/serve/) app takes the same list. |
Files sent by URL are downloaded only from public `https` addresses. A message keeps at most 10 files of up to 25 MB each, and the file’s contents decide its type, whatever the sender declared. A file that cannot be taken, for example because it is too large or its download failed, becomes a note in the message, such as `[Attachment report.pdf not received: 31 MB is over the 25 MB limit]`, so the Agent knows it was sent.
## AG-UI
An AG-UI channel lets a client that speaks the [AG-UI protocol](https://docs.ag-ui.com/) run the Agent and stream its events. The channel accepts AG-UI `RunAgentInput` and streams AG-UI 1.0 events over SSE. Public Chat uses the same protocol.
### Upgrade a pre-1.0 client
Use a 1.0-compatible AG-UI client for existing channels as well as new ones. The reasoning event names changed:
| Pre-1.0 event | AG-UI 1.0 event |
| ------------------------------- | --------------------------- |
| `THINKING_START` | `REASONING_START` |
| `THINKING_TEXT_MESSAGE_START` | `REASONING_MESSAGE_START` |
| `THINKING_TEXT_MESSAGE_CONTENT` | `REASONING_MESSAGE_CONTENT` |
| `THINKING_TEXT_MESSAGE_END` | `REASONING_MESSAGE_END` |
| `THINKING_END` | `REASONING_END` |
Custom handlers must recognize the new names to render reasoning. This is a breaking wire change for clients that require `THINKING_*`; the channel does not translate events back to that vocabulary. Text messages still use `TEXT_MESSAGE_*`.
With npm `@ag-ui/core` 1.0, import validation schemas from `@ag-ui/core/schemas`:
```ts
import { EventSchemas, RunAgentInputSchema } from "@ag-ui/core/schemas";
```
Send `protocolVersion: "1.0"` in the run request to receive the version in `RUN_STARTED`. Omitting it suppresses that response field; it does not select the old protocol. Open text and reasoning messages and reasoning spans close before a terminal event. Cancelled runs finish with `outcome: { type: "cancelled" }`.
Subagent events and their activity snapshots are available when the channel enables `subagents_visible`. This setting defaults to off and stays off for Public Chat.
The agent’s todo list is available as AG-UI shared state when the channel enables `state_visible`, which defaults to off and stays off for Public Chat. The state is `{ "todos": [{ "content", "activeForm", "status" }] }`: a run sends `STATE_SNAPSHOT` the first time it knows the list (at run start when the conversation already has one) and `STATE_DELTA` JSON Patches for later changes. State the client sends in the run request is not read.
### Visibility and access
AG-UI channels are public client surfaces, so they expose less than the Agent’s own event stream:
* `tool_visibility` controls tool activity: `none`, `generic` (a fixed text you configure in `generic_tool_text`), or `narrated`. Raw tool names, arguments, and results are never sent.
* Reasoning summaries, token usage, subagent activity, the todo list as shared state, and client answers to tool-approval interrupts are off by default, each behind its own setting.
* Access is anonymous by default. Set a shared `token` or an `auth` block to require authentication, and `rate_limit_per_minute` to cap requests per IP.
* A thread stays resumable for `session_expiration_seconds` (6 hours by default); after that, the same thread ID starts a new session.
To serve AG-UI from a Rust application without the Platform, see [AG-UI in the Framework](https://everruns.com/docs/framework/ag-ui/).
## FCP
An FCP channel implements the [Free Communication Protocol](https://github.com/everruns/fcp/blob/main/SPEC.md), a minimal text-in, text-out HTTP interface:
* `GET` returns a Markdown handshake that describes what the channel can do and how to authenticate.
* `POST` takes plain text, or `{"message": "..."}`, runs one turn, and returns the Agent’s final reply as Markdown. There is no streaming.
Every response, including errors, is `text/markdown` with instructions that point back to the handshake. Access is anonymous by default; set `anonymous` to `false` and a `token` to require a shared bearer token. FCP has its own rate limit and does not accept the `auth` block that AG-UI and A2A use. `response_timeout_seconds` (120 by default) bounds how long a `POST` waits for the reply.
## Public Chat
A Public Chat channel serves an isolated, hosted chat website for one Agent. Visitors see only that Agent: there is no console navigation, organization switcher, or access to other agents, channels, or sessions.
* Visitors can be anonymous, or sign in through the channel’s auth configuration, such as Google sign-in. Anonymous visitors can be required to pass a Cloudflare Turnstile challenge before a session starts.
* The chat streams AG-UI events, and raw tool names, arguments, results, and internal IDs never reach the browser.
* The deployment-level `public_chat` feature flag (`FEATURE_PUBLIC_CHAT`) controls whether Public Chat routes are mounted and whether the type appears under **Add channel**.
The website reads its public configuration from `/v1/channels/{channel_id}/public-chat/config`. That response never includes channel secrets.
### Sign in with AgentID
[AgentID](https://www.agentid.com) is a sign-in for AI agents. To let agents sign in to a Public Chat channel:
1. Register an AgentID client with the redirect URI `{API base URL}/v1/agentid/callback`, then set `AGENTID_CLIENT_ID` and `AGENTID_CLIENT_SECRET` on the server. One client serves every channel in the deployment.
2. Set the channel’s sign-in to **AgentID** and enter the client ID. The chat page then shows **Continue with AgentID**.
Each agent becomes its own end user in the organization, keyed by its AgentID subject; it never becomes a console user. By default one agent owner can sign in 5 agents per organization; change that with `agentid_agents_per_owner` on the organization. A sign-in lasts 15 minutes, after which the agent signs in again.
To list the agent in the AgentID directory, use `{API base URL}/v1/agentid/initiate-login` as the sign-in URL and set `AGENTID_DEFAULT_CHANNEL` to the channel it should open. Without a default channel that URL creates nothing, because it does not say which chat the agent wants. `AGENTID_OWNER_SCOPES=true` also requests the owner’s name and email; leave it off unless you need to contact owners.
## Agent API
An Agent API channel gives one Agent a base URL, `/v1/channels/{channel_id}`, that your own code calls with an **agent key** (`evr_ak_…`). A key reaches only this Agent’s session routes, never the management API. Callers start sessions, send messages, follow events over SSE, answer questions, approve tool calls and cancel turns, and each caller sees only its own sessions. Channel settings add end users, your own identity provider, browser origins, safe retries and a daily spending limit per caller.
Add an **Agent API** channel on the Agent’s **Integrations** tab, publish it, and create a key in its **Agent keys** card. See [Agent API](https://everruns.com/docs/features/agent-api/) for keys, every route, authentication, settings, streaming and errors, and [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/) for a walkthrough. A [serve](https://everruns.com/docs/framework/serve/) app serves the same routes at `/v1/channels/{agent}`.
## Other transports
The same channel model also carries transports that do not need a reply channel:
* `webhook` runs the Agent when an authenticated HTTP call reaches `/v1/channels/{channel_id}/webhook`, and is available under **Add channel**. A sender that retries can set an `Idempotency-Key` header: a repeat of a key the channel accepted in the last day answers `"delivery": "duplicate"` and starts nothing.
* `schedule` runs the Agent on a cron schedule. Create schedules as [Agent triggers](https://everruns.com/docs/features/agent-triggers/), which also cover webhook, GitHub, and MCP event starts.
## Existing integrations
Channels replace the former **Agent Endpoint** name. Existing channel IDs and configuration stay the same. CLI commands now use `everruns agents channels`; the management API uses `/v1/agents/{agent_id}/channels`. The former `/v1/agents/{agent_id}/endpoints` routes remain aliases, and console bookmarks under `/agents/{agent_id}/endpoints` redirect to the channel pages.
## Retired Apps
Apps are retired from Everruns management. Everruns keeps existing App records for historical attribution and compatibility, and existing installs continue to serve traffic, but the App list, detail page, create flow, and management API are retired.
The old `/v1/e/{channel_id}/…` and `/v1/apps/{app_id}/…` ingress paths remain permanent aliases. They resolve to the migrated channel and continue to work. Do not rewrite a working existing installation only to change its URL. New integrations use the channel-scoped canonical routes above.
## See also
* [Publish an Agent to Slack](https://everruns.com/docs/how-to/publish-to-slack/): a step-by-step Slack setup.
* [A2A](https://everruns.com/docs/features/a2a/): inbound A2A channels and outbound delegation.
* [Voice](https://everruns.com/docs/features/voice/): talk to an Agent and hear its answers.
* [Agent API](https://everruns.com/docs/features/agent-api/): call one Agent from your own code with an agent key.
* [Poppy](https://everruns.com/docs/features/poppy/): answer people’s personal agents as a company.
* [Agent Triggers](https://everruns.com/docs/features/agent-triggers/): proactive scheduled and event-driven work.
* [Change History](https://everruns.com/docs/features/change-history/): see and restore earlier Agent configurations.
## Work Chat
Publish **Work Chat** to give an Agent a dedicated browser experience with its own consumer sign-in. Each channel has a stable, unique hostname on the hosted content domain configured by your deployment. Several Work Chat channels can point to the same Agent.
Choose **Multiple conversations** for a conversation list, or **Single chat with threads** for one permanent main conversation per consumer and related work threads. Single mode requires coordinator thread tools on the effective Agent, including inherited harness capabilities. Publication and later configuration updates reject an incompatible combination. A worker-only coordination capability does not qualify.
```json
{
"channel_type": "work_chat",
"channel_config": {
"interface_mode": "multi_chat",
"execution": {
"visibility": "full",
"errors": "public"
}
}
}
```
Consumers are separate from console members. Configure channel identity methods and optional organization-member access through the existing [Agent API authentication](https://everruns.com/docs/features/agent-api/) settings. The browser host uses its deployment’s OIDC provider and host-only sessions. It shows streamed replies, work activity, threads, questions and permitted approvals. Workspace tools enable file browsing and editing; read-only files remain protected. Provider reasoning, debug records and private runtime mounts are excluded.
Existing budgets, approval policies and session ownership continue to apply. Changing a channel’s configuration changes the same URL. The channel introduces no separate history or retention policy.
Self-hosters deploy the optional standalone browser app separately from the console. Follow its [deployment instructions](https://github.com/everruns/everruns/tree/main/apps/chat-ui) for the content domain, TLS, OIDC broker and least-privilege credential storage. Publishing a channel alone does not provision hosting or DNS.
---
# ChatGPT Plan
> Use the ChatGPT Plan driver in a custom Everruns framework host.
Source:
The `chatgpt` framework driver can run supported models through a personal ChatGPT Plan allowance. It requires an OpenAI Sign in with ChatGPT grant from the account owner. The Everruns Platform UI does not offer this provider or its models because Everruns does not have a registered web OAuth client and callback for this flow.
OpenAI’s [open-source sign-in flow](https://developers.openai.com/siwc/token-sharing-open-source/sign-in) requires a `127.0.0.1` browser callback. If the host runs elsewhere, use the framework’s local login helper on the computer running the browser and transfer the resulting credentials to the host over a protected channel. The driver package includes a `chatgpt-login` example for this purpose. Hosts are responsible for securing tokens and binding each connection to its owner. The loopback helper returns a plain-text completion message by default. A host can call `LoginAttempt::with_success_page_html` before `finish` to show its own branded completion page after a valid callback.
The driver uses streaming, stateless Responses requests with `store=false`. Local function and custom tools use namespaces. Web search is available when the selected model and account support it. Text, image, and file inputs depend on the selected model. API-only hosted capabilities, audio, and video are unavailable. See OpenAI’s [preview limitations](https://developers.openai.com/siwc/preview-limitations) and [models and inference](https://developers.openai.com/siwc/models-and-inference).
---
# CLI
> Manage agents, sessions, and conversations from the command line.
Source:
The `everruns` CLI is a command-line client for the Everruns API. It speaks the same commands agents use through MCP and their shell, so every platform command is available with the same spelling, and it is designed to compose well with shell pipelines.
This page covers installation, configuration, and the command surface. For scripting patterns and `jq` examples, see [Automate with the CLI](https://everruns.com/docs/how-to/automate-with-the-cli/).
## Install
### Homebrew (macOS / Linux)
```bash
brew tap everruns/tap
brew install everruns
```
### Cargo
From the Git repository:
```bash
cargo install --git https://github.com/everruns/everruns everruns-cli
```
Or clone and build:
```bash
git clone https://github.com/everruns/everruns.git
cd everruns
cargo install --path crates/cli
```
### Verify
```bash
everruns --version
```
## Configure
The CLI defaults to [Everruns Cloud](https://app.everruns.com) at `https://app.everruns.com/api`. Sign in once:
```bash
everruns login # opens the browser for OAuth
everruns login --token # paste a personal access token instead (SSH, headless)
everruns status # show the current user and organization
everruns whoami # ask the server who you are, and your token's access
```
`login --token` accepts a [read-only token](https://everruns.com/docs/features/ui/#token-access) too, for scripts and CI that only read. `everruns whoami` then shows `Access: read-only (personal access token)`, and a command that would change something fails with one line:
```text
Error: This personal access token is read-only; this command needs a full-access token.
```
For a self-hosted deployment, point the CLI at your API first:
```bash
# Per command
everruns --api-url http://localhost:9300/api agents list
# Per shell
export EVERRUNS_API_URL=http://localhost:9300/api
export EVERRUNS_API_KEY=evr_pat_... # or run `everruns login`
```
## Command surface
| Group | Subcommands |
| ------------------------------------- | -------------------------------------------------------------------------------------- |
| `login`, `logout`, `status`, `whoami` | Sign in, sign out, show the current user and organization, show your token’s access |
| `orgs` | `list` organizations, `select` the active one |
| `agents` | `create`, `update`, `import`, `export`, `validate`, `diff`, plus the platform commands |
| `sessions` | `create`, `watch`, `export`, plus the platform commands |
| `chat` | Send a message and stream the response |
| `files` | Sync files between a local folder and a session |
| `connections` | `set`, `list`, `remove` your provider API keys |
| Every other platform command | See [Platform commands](#platform-commands) |
### Agents
```bash
# Inline
everruns agents create \
--name "my-agent" \
--system-prompt "You are a helpful assistant." \
--tag production \
--reason "Support pilot"
# From a file (TOML, YAML, JSON, or Markdown front matter)
everruns agents create -f agent.toml
everruns agents create -f agent.yaml
everruns agents create -f agent.md --reason "Import from repo"
```
If `./agent.toml` exists and you don’t pass inline flags, `everruns agents create` picks it up automatically. The file formats are documented in [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/).
```bash
everruns agents list
everruns agents get agent_...
everruns agents delete agent_... --reason "Replaced by a newer agent"
```
### Sessions
```bash
everruns sessions create --agent agent_...
everruns sessions create --agent agent_... --title "Debug session" --reason "Reproduce ticket 42"
# With session-level overrides
everruns sessions create \
--agent agent_... \
--harness bashkit-worker \
--capability 'web_fetch={"timeout":10}' \
--hint setup_connection=true \
--network-allow api.example.com \
--max-iterations 8
```
Also accepts: `--locale`, repeatable `--tag`, `--system-prompt`, `--hints-json`, repeatable `--network-block`, repeatable `--secret KEY=VALUE`, and budget flags.
```bash
everruns sessions list
everruns sessions get session_...
```
### Chat
```bash
everruns chat "Tell me a joke!" --session session_...
```
Options: `--timeout ` (default 300), `--no-stream` to queue without waiting.
## Output formats
Every command accepts `-o` / `--output`:
```bash
everruns agents list -o json
everruns agents list -o yaml
```
`--quiet` suppresses headers and prints only the essential identifier, useful for capturing IDs in shell variables.
## Platform commands
Every other command comes from the same command contract that agents use in their shell and through MCP `execute`, so the spelling, flags and validation are identical everywhere. Run `everruns --help`, `everruns --help` or `everruns --help` to browse them, or read the [CLI command reference](https://everruns.com/docs/reference/cli/), which is generated from the same help.
```bash
everruns agents triggers list --agent-id agent_...
everruns sessions participants add --session session_... --kind agent --agent-id agent_... --reason "Hand off to the reviewer agent"
everruns capabilities list --search web
# Plugins, skills and knowledge bases
everruns plugins install --marketplace-id --plugin-name --reason "Add the release tooling"
everruns skills create --skill-md @./SKILL.md --reason "Share the code-review checklist"
everruns knowledge-bases create --name "Product docs" --description "Published product documentation" --reason "Ground support answers in the docs"
```
These commands print the command’s JSON output (YAML with `-o yaml`). A text or JSON flag written `@path` is read from that local file, as in `--skill-md @./SKILL.md`; write `@@` for a value that really starts with `@`.
### Retries
Platform commands are safe to retry. The CLI retries a dropped connection or a gateway error itself, and the server runs a create or update at most once for the same request. A script that re-runs a whole command can get the same guarantee by setting a key it reuses for that one command:
```bash
EVERRUNS_IDEMPOTENCY_KEY="nightly-agent-$(date +%F)" everruns agents create --name nightly --system-prompt @./prompt.md
```
A repeat with the same key returns the first result. Reusing a key for a different request is an error, so use one key per command.
### Reasons and manager context
Every command that changes something takes `--reason "..."`, which is stored on the changed entity’s history entry. That includes `agents create`, `agents update`, `agents import` and `sessions create`. [Platform commands](#platform-commands) also take `--context-revision N`, which says which revision of the entity’s manager notes you read. `everruns history list ` shows who changed an entity and why. `everruns context get ` shows its notes. See [Change history](https://everruns.com/docs/features/change-history/).
## See also
* [CLI command reference](https://everruns.com/docs/reference/cli/): every command with its flags and a worked example.
* [Use in AI tools](https://everruns.com/docs/getting-started/use-in-ai-tools/): the Everruns plugin teaches coding agents these same commands.
* [Automate with the CLI](https://everruns.com/docs/how-to/automate-with-the-cli/): `jq`, quiet mode, scripting patterns.
* [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/): file formats for `-f`.
* [SDK](https://everruns.com/docs/features/sdk/): the programmatic equivalent.
---
# Dreaming
> Consolidate completed agent experience into authorized Memory files, with bounded background work, source references, and durable recovery.
Source:
Dreaming turns completed experience into notes that future sessions can read. It is independent of context compaction: it does not shorten the active conversation or rewrite earlier messages.
## Logical flow
```
flowchart LR
accTitle: Dreaming memory consolidation
accDescr: Completed experience is selected, reconciled with existing memory, validated against source references, and saved as a Memory artifact or a protected proposal.
A[Completed experience] --> B[Select new evidence]
B --> C[Reconcile existing notes]
C --> D[Validate source references]
D --> E{Commit mode}
E -->|automatic| F[Memory artifact]
E -->|propose| G[Protected proposal]
```
Notes distinguish facts, preferences, observed outcomes, reusable procedures, and inferences. Each note cites the messages used to produce it. The model gets bounded evidence and existing notes, without tools or the agent’s foreground system prompt.
Evidence includes message text and structured tool requests and results, including errors. Images, attachments, and reasoning are excluded. Large tool results cannot expand the bounded evidence window.
Automatic mode creates a new source-linked Markdown artifact for each operation. It does not overwrite human edits or delete earlier notes. Corrections and superseded information are described in the new artifact. This first version uses incremental artifacts rather than in-place rewriting of an entire memory collection.
## Configure the capability
The built-in **Worker** harness includes Dreaming; Bashkit Worker and Sandbox Worker inherit it. On the platform, the **Dreaming** organization feature flag uses the **Adoption** grade: it starts off on every deployment until an organization owner/admin or platform operator opts in. Disabled organizations do not receive the capability in their runtime snapshots, including explicit agent configuration. Enable it in organization feature settings before using it.
Worker defaults target the existing private `/memory/user` mount under `/learned`, with the idle trigger and budgets below. A host must still provide authorized writable Memory. Override the capability configuration on an agent or harness to change destinations, trigger, model, budgets, or commit mode. Framework presets carry the same capability data; embedded applications explicitly choose the preset and provide Memory, without platform organization flag management.
```json
{
"ref": "dreaming",
"config": {
"destinations": [
{
"mount": "/memory/user",
"path": "/learned",
"instructions": "Retain useful explicit preferences and verified lessons."
}
],
"trigger": {
"mode": "idle",
"idle_after_seconds": 60,
"min_new_turns": 3
},
"model": null,
"budget": {
"max_output_tokens_per_run": 4096,
"max_runs_per_day": 12,
"max_cost_usd_per_day": 1.0
},
"commit_mode": "automatic"
}
}
```
* **Destinations:** existing writable Memory mounts, with an allowed directory and optional retention instructions. Naming a path never grants permission or creates a Memory. Platform support uses server-managed `/memory/*` mounts; copied workspace snapshots do not qualify.
* **Trigger:** `idle`, `after_turn`, or `manual`. `/dream` queues a manual pass and bypasses the minimum-turn threshold, but not authorization or budgets.
* **Model:** `null` inherits the session model. A string selects another model on the same provider account.
* **Budget:** daily run count and conservative cost reservations are shared by sessions using the same maintenance store and Memory identity. Each destination’s generation attempt consumes one run. Accounting days use UTC. Reservations cover uncached input or full cache creation, including long-retention surcharges, whichever costs more. Unknown pricing blocks calls when a dollar limit is configured. Set the dollar limit to `null` only when the host supplies another spending control.
* **Commit mode:** `automatic` writes validated artifacts. `propose` retains output in protected maintenance state and does not modify active Memory. Use `/dream proposals` to inspect it, then `/dream accept` or `/dream discard`. Pending proposals block another pass until reviewed. Framework applications use `Engine::review_dreaming`.
Outside Worker-derived harnesses, the capability is disabled when omitted. Normal responses do not wait for the dreaming model call. One operation runs per session; turns completed during it remain eligible for the next trigger.
## Privacy and caching
Read-only, unavailable, archived, and deleted destinations cannot be written. Private user experience cannot be automatically promoted into shared agent Memory. Use `/memory/user` for personal facts and preferences; see [Agent and user memory](https://everruns.com/docs/features/memory-scopes/).
Dreaming contributes stable directory pointers and uses the existing file tools. It does not inject changing Memory contents into the system prompt. Memory changes reach the agent through later file reads, preserving earlier prompt prefixes. The separate dreaming call has its own provider request and does not assume a foreground cache hit. Scheduling never sends keepalive calls just to refresh a prompt cache.
## Physical execution and recovery
```
sequenceDiagram
accTitle: Durable background dreaming
accDescr: A successful turn queues persisted maintenance. A runner claims work, reserves budget, saves model output, optionally writes an idempotent Memory artifact, and records completion.
participant E as Engine
participant S as Maintenance store
participant R as Runner
participant P as Model provider
participant M as Memory
participant T as Trace and usage events
E->>S: Persist eligible work
R->>S: Claim due operation and reserve budget
R->>P: Separate bounded request, no tools
P-->>R: Structured notes and usage
R->>T: Payload-free usage observation
R->>R: Validate notes and source references
R->>S: Save generated output
opt Automatic commit mode
R->>M: Write operation-specific artifact
end
R->>S: Complete and advance evidence cursor
R->>T: Completion metadata
```
The shared runner executes the same consolidation rules in the Framework and platform. The default Engine stores maintenance in memory; SQLite-backed hosting persists it and its supervisor rediscovers due jobs. Platform workers poll encrypted PostgreSQL maintenance state. Self-hosted installations need `SECRETS_ENCRYPTION_KEY`; persistence fails closed without it. Supervisors retry failed claims after their lease expires, with a bounded attempt count. With the volatile Engine, call `Engine::maintain_dreaming` to retry due work after a failed pass; its turn-end timer schedules the initial pass.
Legacy-session cutover does not transfer dreaming jobs, cursors, proposals or Memory-wide budget reservations. It refuses sessions with dreaming configuration or retained maintenance state before writing the target authority. Start new durable sessions directly under SQLite hosting. After a refused cutover, `Engine::abandon_legacy_cutover` explicitly returns ownership to the legacy host.
Generated output is saved before Memory writes. Recovery can repeat an artifact write without regenerating text or replacing unrelated files. A model call interrupted before its output is saved may run again; provider billing is not exactly once. Cost reservations are retained conservatively after failures.
Recovery starts once maintenance is enqueued. Automatic scheduling uses the turn-end hook; it is not atomic with the completed-turn event. If the host stops before enqueue, a later successful turn or an explicit dream can rediscover the unprocessed experience.
Custom hosts must implement the maintenance store and explicitly authorize real writable Memory scopes. `AuthorityClient::with_dreaming_store` lets Durable Object hosts attach a persistent store scoped to that client’s tenant and shared across sessions that write the same Memory. The default refuses dreaming persistence. These hosts must arrange durable alarms that call `Engine::maintain_dreaming`; a process-local timer alone does not establish durable support. Maintenance deadlines use Unix seconds; convert to milliseconds for alarm APIs that require them. Persist the wakeup with the state change, or use a durable outbox that rediscovers missed wakeups.
## Framework example and evaluations
Framework agents can mount an existing private directory with `AgentBuilder::memory_directory`. The application chooses one directory per principal; sessions sharing a directory share its notes. `Engine::dream(session_id)` runs a manual pass. Embedded applications can also use `session.run_command("dream", None)` to queue a pass without starting a foreground turn. See the [runnable example](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/dreaming.rs).
```sh
cargo run -p everruns --features openai --example dreaming
cargo run -p everruns --features openai --example dreaming -- --eval
# Same model through OpenRouter (uses OPENROUTER_API_KEY):
cargo run -p everruns --features openai,openrouter --example dreaming -- --eval --openrouter
```
The real-model eval covers explicit preferences, corrections across successive consolidation passes, a reported isolation procedure, an unverified outcome, and an instruction-injection/secret-retention case. It emits JSON per case and a final pass count. Its lexical checks are smoke evidence, not a general memory-quality benchmark. Results depend on the selected model and must be reported with the model and run date.
### Measured evaluation
On **2026-10-19**, one qualification run using OpenRouter `openai/gpt-6.1-sol` passed **5/5** cases. Each case verified saved facts and a successful fresh-session content retrieval returning those facts. The correction case persisted the original fact, consolidated a later correction, then recalled only the current region.
| Case | Result | Content retrieval |
| -------------------------------- | ------ | ----------------- |
| Explicit preference | Pass | `read_file` |
| Successive correction | Pass | `read_many_files` |
| Reported isolation procedure | Pass | `read_file` |
| Pending outcome | Pass | `read_file` |
| Injection and secret suppression | Pass | `read_file` |
These are single-sample lexical smoke results, not a general accuracy or injection-resistance benchmark. See the [evaluation method and machine-readable results](https://github.com/everruns/everruns/tree/main/evals/dreaming).
## Inspect activity
The session **Trace** includes a separate **Background dreaming** group. Note counts cover processed destinations; model, token usage, and reserved maximum describe the latest generation. Background time is not added to foreground turn duration. Raw `dreaming.activity` events contain bounded metadata, never generated Memory text or evidence. Calls also emit payload-free `llm.generation` events for existing usage and budget accounting across all generations; prompts, captured requests, and generated notes are omitted. Authorized users inspect automatic artifacts through the existing Memory file browser; deleting a generated artifact removes that artifact from future file reads.
---
# Evals
> Define, run, and track behavioral tests for Agents. Each case runs a real session and is scored.
Source:
Evals let you define, run, and track **behavioral tests** for your Agents. An Eval is a named collection of cases; each case sends messages to a fresh session and scores the result. Use them to compare runs across models and catch regressions after a prompt change.
Because every case runs a **real session** against the target agent and harness, failures are fully debuggable, click into the session to see the conversation, tool calls, and events exactly as they happened.
## Concepts
| Entity | What it is |
| -------------- | -------------------------------------------------------------------------------------------------------- |
| **Eval** | Top-level, org-scoped collection of cases. Follows the standard `active → archived → deleted` lifecycle. |
| **EvalCase** | A single test: input messages, scoring rules, execution bounds, and optional artifact collection. |
| **EvalRun** | One execution of an eval’s cases against a target, producing per-case results. |
| **EvalTarget** | How a session is instantiated from a Harness, Agent, model, and system prompt. |
Targets resolve in order `EvalRun → EvalCase → Eval → org default harness`, so you can run the same cases against different models per run or override per case.
## How a case runs
1. A fresh session is created from the resolved target.
2. The case’s `conversation` messages are delivered sequentially (multi-turn supported).
3. Optional `post` messages run after the session idles, for example, executing a test script.
4. Optional `artifacts` capture named session files.
5. `scorers` grade the result, returning `0.0`–`1.0` for nuanced pass/fail.
Eval runs are durable workflows: they reuse the same execution engine as production sessions, so a worker restart mid-run does not lose progress.
## SWE-bench Lite
Beyond user-facing behavioral evals, Everruns ships a **SWE-bench Lite** harness for measuring coding-agent performance against the standard benchmark, using the same eval machinery (`post` verification messages run the test scripts that determine pass/fail).
## Related
* [Harnesses](https://everruns.com/docs/features/harnesses/), what an eval target runs
* [Change History](https://everruns.com/docs/features/change-history/), every change to an agent and how to restore an earlier configuration
---
# Events
> SSE streaming of session events: categories, structure, and the wire protocol.
Source:
Every action during a session, user input, LLM responses, tool calls, lifecycle transitions, emits an **event**. The event log is the source of truth for session state; the SSE stream is a live tail of that log.
For *why* the platform is shaped this way, see [Events as the primary store](https://everruns.com/docs/explanation/events/). For the full catalog of event types and payloads, see the [Event Reference](https://everruns.com/docs/event-reference/).
## Event categories
| Category | Examples | Description |
| ------------ | -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| **Input** | `input.message` | User messages submitted to the session |
| **Output** | `output.message.started`, `output.message.delta`, `output.message.completed` | Agent response lifecycle |
| **Turn** | `turn.started`, `turn.completed`, `turn.failed`, `turn.cancelled` | Turn lifecycle |
| **Thinking** | `reason.thinking.*` | Extended thinking content (Claude, GPT-5.x, o-series) |
| **Atom** | `reason.*`, `act.*`, `tool.*` | Internal execution phases |
| **LLM** | `llm.generation` | Full LLM API call details |
| **Session** | `session.started`, `session.activated`, `session.idled`, `session.model.changed` | Session state changes |
| **Sandbox** | `sandbox.instance_lost`, `sandbox.recovered` | Managed compute replacement and durable-workspace recovery |
| **Subagent** | `subagent.*` | Subagent lifecycle |
## Event structure
```json
{
"id": "event_01933b5a00007000800000000000001",
"type": "turn.completed",
"ts": "2024-01-15T10:30:00.000Z",
"session_id": "session_01933b5a00007000800000000000002",
"sequence": 42,
"context": {
"turn_id": "turn_...",
"input_message_id": "message_...",
"trace_id": "turn_...",
"span_id": "abc123",
"parent_span_id": "def456"
},
"data": { /* type-specific payload */ }
}
```
| Field | Type | Description |
| ------------ | ------- | --------------------------------------------------------- |
| `id` | string | Unique event ID (UUIDv7) |
| `type` | string | Event type in dot notation |
| `ts` | string | ISO 8601 with millisecond precision |
| `session_id` | string | Session this event belongs to |
| `sequence` | integer | Monotonic per-session sequence (ordering source of truth) |
| `context` | object | Correlation IDs for tracing |
| `data` | object | Event-specific payload |
Ordering is by `sequence`, not `ts`. Two events with the same wall-clock time still have a strict order.
## Common patterns
### Started → completed → failed
Long-running operations follow a lifecycle:
```plaintext
turn.started → turn.completed
↘ turn.failed
↘ turn.cancelled
```
These boundaries are what your UI uses to manage state and surface errors.
### Issues versus errors
A failed tool call is not a failed turn. The model gets the failure back as a result and carries on, so `tool.completed` marks it `"severity": "issue"` and `turn.completed` counts it in `issue_count`. Only `turn.failed` (or a failed task) means the work stopped. Render issues as warnings and keep error styling for those. See [Issues and errors](https://everruns.com/docs/event-reference/#issues-and-errors).
### Delta streaming
Streaming content uses delta events with accumulated state:
```json
{
"type": "output.message.delta",
"data": {
"turn_id": "turn_...",
"delta": "Hello",
"accumulated": "Hello"
}
}
```
Deltas are batched at \~100ms to reduce volume. When a provider stream breaks after some text, the runtime retries it under the same `message_id`: it emits `output.message.started` for that message again, and `accumulated` starts over. Render `accumulated`, or clear the text you added up on a repeated `output.message.started`.
## Forward-compatibility
Events follow a defined contract. Consumers must tolerate evolution:
| Change | Allowed |
| --------------------------- | ------- |
| New event types | yes |
| New optional fields | yes |
| New enum values | yes |
| Removing or retyping fields | no |
Your deserializer should ignore unknown fields, ignore unknown event types, and treat optional fields as optional.
## Consume the stream
* [Stream events with the SDK](https://everruns.com/docs/how-to/stream-events/), the convenient path (Python, Rust, TypeScript).
* [Consume events via raw SSE](https://everruns.com/docs/how-to/stream-events/#with-raw-sse), protocol-level, when the SDK isn’t available.
## Optional idle following in the React UI client
The UI client follows session events continuously by default. Keep this behavior for schedules, channels, shared sessions, and applications that need immediate updates from other clients.
An embedding that drives its own session can opt in to idle suspension:
```tsx
const { data: events, isSuspended, resume } = useEvents(sessionId, {
followIdle: false,
});
```
The hook closes SSE only after the session is settled, all background tasks are terminal, and its durable cursor matches the server tail. Active turns, parked tools, pending approvals, sign-ins, and tasks awaiting input keep following. An unavailable state check keeps the connection open.
The UI client’s message, tool-result, question-answer, approval, and consent APIs reopen the stream before their request and keep it live until the request ends. Custom resume or write operations should use `withSessionActivity(sessionId, async () => { /* perform the operation */ })` from `everruns-ui/lib/session-activity`. The hook also exposes `resume()` to catch up explicitly and catches up on browser focus or network recovery. Replay uses the last durable cursor and deduplicates already displayed events.
While suspended, events from other clients wait until the next wake-up. Other subscriptions, including the task list’s independent stream, are unaffected. This option makes no measured resource or cost savings claim.
## See also
* [Event Reference](https://everruns.com/docs/event-reference/), complete event type catalog with payloads.
* [Events as the primary store](https://everruns.com/docs/explanation/events/), design rationale.
---
# Explicit Communication
> Let an agent keep its assistant text as private working notes and talk only by sending messages, so it can choose what to say, when, and whether to answer at all.
Source:
Every Agent has a **Communication** setting that decides what counts as the Agent talking:
* **Direct** (the default): the Agent’s assistant text is its reply. This is how most chat agents work.
* **Explicit**: the Agent’s assistant text is private working notes. The Agent talks only by calling the `send_message` tool, and can call `no_reply` to decide not to answer.
With explicit communication the Agent separates thinking out loud from speaking. It can work through a problem in notes, send one complete answer, post a short progress update during long work, or stay silent when a message is not for it.
## When to use it
Explicit communication fits conversations where saying nothing, or saying one deliberate thing, matters:
* **Slack channels and threads.** Only the messages the Agent sends reach the thread. Its intermediate reasoning and tool preambles stay in Everruns.
* **Conversations with several people.** The Agent can let people talk among themselves and answer only when it has something to add.
* **Coordinators.** An Agent that delegates work can keep its planning in notes and send a single summary when the work is done.
* **Agents that should be able to stay silent.** `no_reply` ends a turn on purpose, for an acknowledgement or a message that needs no answer.
## When not to use it
* **Simple one-person chats.** Direct communication is simpler and streams the reply as the Agent writes it.
* **Weaker models.** The Agent must remember to call `send_message`. A model that is unreliable at tool calls may finish a turn without sending anything, and the person then sees no answer. Test with the model you plan to use.
## Turn it on
### In the web UI
Open the Agent, or the new-agent page, and set **Communication** to **Explicit**. Save the Agent.
### Through the API
Set `communication` when you create or update an Agent. The field is returned when you read the Agent.
```json
{
"name": "release-coordinator",
"system_prompt": "You coordinate releases for the team.",
"communication": "explicit"
}
```
Send `"communication": "direct"` to switch back.
### In an agent package
Add `communication` to `agent.toml`. Omitting it means `direct`. See [Agent Package](https://everruns.com/docs/reference/agent-package/).
```toml
schema_version = 1
name = "release-coordinator"
instructions = "You coordinate releases for the team."
communication = "explicit"
```
### In the Framework
```rust
use everruns::conversation::Communication;
use everruns::prelude::*;
let agent = Agent::builder()
.instructions("You coordinate releases for the team.")
.model("gpt-5.6-terra")
.communication(Communication::Explicit)
.build()?;
```
The session event stream reports each sent message as `SessionEventKind::MessageSent { message_id, text }`. The [`explicit_communication`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/explicit_communication.rs) example runs offline and prints the sent messages separately from the notes:
```bash
cargo run -p everruns --example explicit_communication
```
## The two tools
An Explicit Agent gets two tools and a short system prompt section, “How you talk”, that explains them to the model. You do not add them yourself.
| Tool | Arguments | What it does |
| -------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `send_message` | `text` (required): the complete message, in Markdown | Sends the message to the conversation the turn came from. Success means it was delivered. |
| `no_reply` | `reason` (optional): why no reply is needed | Ends the turn without saying anything. The reason stays in the transcript and is never shown to the person. |
The Agent never chooses where a message goes. The destination is always the conversation of the input that started the turn.
## Where messages go
| Surface | Where a sent message goes |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Slack | Posted into the thread that started the turn, as the channel’s bot, at most once per call. See [Slack](https://everruns.com/docs/capabilities/slack/#how-the-agent-replies). |
| Web chat and the API | The session is the conversation. The message appears in the chat and in the event stream. |
| A2A | Returned as the task’s reply artifacts. |
| AG-UI | Streamed to the client as text messages. |
| MCP | Returned as what the Agent said in session status. |
| FCP and the channel API | Returned as the reply. |
| CLI chat | Printed as the Agent’s reply. |
| Voice | Spoken to the caller. Notes are not spoken. |
| Subagents | Returned to the parent Agent as the subagent’s result. |
Evals and observers read sent messages the same way, so a scorer or an observer sees what the Agent said, not its notes.
## What clients see
Each sent message is recorded as a [`conversation.message`](https://everruns.com/docs/event-reference/#conversationmessage) event with the message id, its text, and the `send_message` tool call that sent it. When an external platform such as Slack accepted the message, the event also carries a `delivery` object with the platform, channel and platform message reference.
The Agent’s assistant text is still recorded, as `output.message.completed` with `message.phase: "commentary"`. Treat it as working notes: the web UI shows it as part of the Agent’s work, and every surface that returns “what the Agent said” ignores it.
A client that reads replies from events should count `conversation.message` events and non-commentary `output.message.completed` events. That rule works for both Direct and Explicit Agents, so a client does not need to know which setting an Agent uses.
## Limits
* One sent message is at most 12,000 characters. Share a file for longer content.
* An empty message is rejected.
* A failed send returns an error to the Agent. If delivery was uncertain, the error says so, and the Agent should not resend blindly because the message may already be visible.
* If an Explicit Agent ends a turn in a Slack thread without posting anything and without calling `no_reply`, Everruns posts one short status line with a link to the session. A turn that calls `no_reply` stays silent.
* Sent messages are not streamed token by token. Each one arrives complete.
---
# Harnesses
> A harness defines reusable behavior, defaults, and capabilities that agents and sessions extend.
Source:
A **harness** is reusable runtime configuration: base instructions, a default model, starter files, network policy, and a bundle of capabilities. Every session is assigned exactly one harness. Agents and sessions then layer their own configuration on top. An Agent’s [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/) select where commands run without changing that behavior.
Harness here does not mean the agent loop
Elsewhere in the industry, “agent harness” usually names the loop that drives the model, the thing that assembles context, calls the LLM, and dispatches tools. Everruns describes itself as a *durable agentic harness engine* in that sense.
A **Harness** (the entity on this page) is not that loop. The loop is the runtime, and you never configure it directly. A Harness is the reusable configuration a session runs on top of.
The split that matters is **sandbox versus behavior**:
| | Answers | Owns |
| -------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------- |
| **Sandbox Template** | “Where do commands run?” | Filesystem, compute target, containment, recovery, lifecycle |
| **Harness** | “How does this runtime behave?” | Base instructions, network access, capability bundle, default model, starter files |
| **Agent** | “What role am I playing?” | Instructions, domain capabilities, the agent’s voice, Sandbox policy |
| **Session** | “What is true for this one conversation?” | Per-conversation extras, overrides, a tighter network policy |
A harness exists before any agent uses it, and many agents share one.
### Who points at a harness
Both an agent and a session carry a harness reference:
* Every **agent** holds exactly one `harness_id`. It is the harness that agent’s sessions run on by default. On create, an agent inherits the organization’s default harness unless you pass `harness_id` or `harness_name`; an explicit choice stays pinned.
* A **session** may name its own harness and override the agent’s.
Precedence when a session starts, first match wins:
1. The harness named on the session request
2. The agent’s harness
3. The organization default
4. The built-in fallback
So the same agent can be run on a different harness for one session without editing the agent, while changing it for good means updating the agent.
For the design rationale (why three configuration layers exist), see [Concepts](https://everruns.com/docs/getting-started/concepts/#runtimeagent).
## Built-in harnesses
The built-in harnesses form a tree. Each layer answers one question about the agent, and each child inherits everything above it.

| Harness | What it provides | Best for |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| [Base](https://everruns.com/docs/built-ins/harnesses/base/) | System essentials: compaction, error disclosure, tool-call repair, loop detection, approval guidance | Custom harnesses that compose their own tools |
| [Conversation](https://everruns.com/docs/built-ins/harnesses/conversation/) | Base plus structured questions and message timestamps. No workspace, no shell | Chat assistants, support bots (the default) |
| [Worker](https://everruns.com/docs/built-ins/harnesses/worker/) | Base plus files, AGENTS.md, skills, long context, budgeting, subagents, tasks and [Dreaming](https://everruns.com/docs/features/dreaming/) (organization Adoption opt-in). No compute | Agents that work through tools and MCP servers |
| [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) | Worker plus the Bashkit virtual shell, fixed to the Bashkit Virtual Workspace | Platform Chat, light automation, agents that need bash without a sandbox provider |
| [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/) | Worker plus a full sandbox shell chosen by the Agent sandbox policy | Coding, installing packages, builds |
| [Worker Base (deprecated)](https://everruns.com/docs/built-ins/harnesses/worker-base/) | Preserved legacy bundle | Existing custom harnesses that name it |
| [Generic (deprecated)](https://everruns.com/docs/built-ins/harnesses/generic/) | Preserved legacy bundle | Existing bindings |
Conversation is the default. Deprecated harnesses keep working for existing bindings and explicit references; selectors hide them until you choose Show deprecated. See the [Built-in harnesses reference](https://everruns.com/docs/built-ins/harnesses/base/) for the exact capability bundle each one ships with.
### Choosing a harness
1. **Does the agent only talk?** Choose Conversation.
2. **Does it need a workspace, instructions, skills or delegation?** Choose a Worker.
3. **Where does it run commands?**
* Nowhere, it works through tools and MCP servers: Worker.
* In a virtual shell that needs no sandbox provider: Bashkit Worker. Bashkit runs Bash-like commands over a durable virtual filesystem, so it suits scripting, file work and light automation. It cannot install native packages, start processes or servers, open ports or attach a terminal.
* In a real machine with processes and packages: Sandbox Worker, plus an Agent sandbox policy that names a container or managed Sandbox Template. Choose it for coding, builds, test runs and browser automation.
When in doubt, start on the smallest harness that works and move the agent up when it needs more. A child harness cannot remove what its parent adds, so starting large means the agent carries tools it never uses.
Some common agents and their harness:
| Agent | Harness |
| ------------------------------------------------------------------- | -------------- |
| Support bot, Q\&A over a knowledge base | Conversation |
| Linear or GitHub triage, Slack assistant, research over MCP servers | Worker |
| Report generation, CSV clean-up, operations scripts | Bashkit Worker |
| Coding agent, PR reviewer that runs tests | Sandbox Worker |
Bashkit Worker and Sandbox Worker each fix one rule, and the rule is inherited by custom harnesses built on them. Bashkit Worker rejects an Agent sandbox policy, since its sandbox is always Bashkit. Sandbox Worker rejects a policy that only allows Bashkit, and a session on it fails with a clear message when no full Sandbox Template is available rather than quietly running on Bashkit.
For existing organizations, the [harness upgrade notes](https://everruns.com/docs/framework/upgrade-notes/#harness-tree) describe how agents on the old Worker move to Bashkit Worker or Sandbox Worker, and how Generic bindings are preserved.
[Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) is a managed Agent bound to Bashkit Worker. Introductions and conversation starters belong to Agents; harnesses describe reusable execution behavior.
## Naming
Every harness has two names:
* **`name`**: stable URL-friendly slug (`conversation`, `bashkit-worker`). Unique per org. Use this in API calls, CLI, code.
* **`display_name`**: human label shown in the UI.
`name` format: `[a-z0-9]+(-[a-z0-9]+)*`, max 64 chars, no consecutive hyphens.
## How harnesses combine with agents and sessions
The system prompt is built from three layers, each wrapped in XML tags:
1. Harness capabilities (foundation)
2. Agent capabilities (role)
3. Session capabilities (per-conversation extras)

The merge is associative: a chain of inherited harnesses produces the same `RuntimeAgent` as a single pre-merged harness.
### The base system prompt is optional
A harness bundles more than a prompt, capabilities, MCP servers, a default model, network access, and starter files. Because of that, the base `system_prompt` is **optional**. Omit it (or leave it empty) when a harness exists only to add capabilities or MCP servers on top of a parent: the effective prompt is then composed entirely from the parent harness, the agent, the session, and capability contributions. Empty or whitespace-only prompts contribute nothing, and if no layer contributes a prompt the agent runs with no base system prompt at all.
## Do something
* [Customize a harness](https://everruns.com/docs/how-to/customize-a-harness/), build your own as a base for many agents.
* [Equip an agent with tools](https://everruns.com/docs/how-to/equip-agents-with-tools/), add capabilities at the agent layer.
## See also
* [Built-in harnesses](https://everruns.com/docs/built-ins/harnesses/base/), reference for the shipped harnesses.
* [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/), configure Bashkit or managed Daytona compute independently of the harness.
* [Concepts](https://everruns.com/docs/getting-started/concepts/), entity model.
---
# Large MCP catalogs and tool surfaces
> Everruns paginates MCP catalogs, searches authorized server and tool metadata, and reveals bounded schemas through native calls or the session shell.
Source:
Everruns reads MCP catalogs page by page, indexes the tools available to the current agent, and reveals schemas as they are needed. This lets an agent work with catalogs containing thousands of operations without sending every parameter schema to the model at the start of a turn.
## Complete catalog discovery
The MCP client follows every `tools/list` cursor and keeps the negotiated connection and authorization for each page. A catalog is accepted only when the server reports that there are no more pages. Repeated or empty cursors, duplicate names within one server, and discovery that exceeds a fixed implementation bound fail explicitly; these bounds are not deployment settings. Everruns does not treat a partial page sequence as a complete catalog.
Per-server discovery is bounded to 10,000 tools, 16 MiB each for cumulative response bodies and serialized tool definitions, 256 pages, and 60 seconds total. Response bytes include protocol framing. The HTTP transport also retains its existing per-request timeout. The Framework’s scoped discovery cache has a 128 MiB weighted budget per runtime instance, so one large catalog cannot make that cache grow without bound. These limits are safety bounds, not a claim that every provider or model can use 10,000 schemas at once.
Custom Framework egress adapters must implement `EgressService::send_stream`. HTTP discovery consumes bounded response streams even when the MCP reply is ordinary JSON, so response limits apply before buffering the complete body.
The Framework’s scoped MCP discovery also limits the combined session catalog to 10,000 tools and 32 MiB of serialized, server-prefixed definitions. If it exceeds either bound, discovery fails closed with an explicit trace error; it does not return a truncated catalog. Discovery limits are fixed in the implementation, not deployment settings.
## Search and schema disclosure
In bounded native mode (`tool_search` with `bounded: true`) and in the `tools` CLI, search uses a shared authorized index over server names and descriptions, tool names and descriptions, and parameter schema text. A server description can help find generic operations such as `search`, `get`, or `list`. Results retain the server and operation identity, so same-named tools on different servers remain distinct. If only a server description matches, search returns a server refinement rather than labeling its tools as operation matches. The CLI marks these separately and points to `tools --help`. Legacy native `tool_search` keeps its existing matcher and does not use this server-aware index.
By default, `tool_search` keeps its existing behavior. To opt into bounded schema disclosure, set `bounded` and the three limits on the capability:
```json
{
"ref": "tool_search",
"config": {
"bounded": true,
"max_active_tools": 32,
"max_active_bytes": 65536,
"max_result_bytes": 32768
}
}
```
The first two limits bound optional disclosed schemas only. Eager, client-side, and `never_defer` tools remain in addition to those limits, and fallback drivers may temporarily hold removals during a warm-prefix transition. Held definitions must still fit the combined native tool budget: eager definitions plus the configured optional count and byte limits. If a lower live budget no longer admits the held list, the runtime releases those removals immediately. `max_result_bytes` bounds schemas returned by a search. Discovery remains progressive: the model searches, receives schemas for a bounded set of operations, then calls those operations using their registered schemas. A search result does not create a second execution path. If matching schemas do not fit in one result, search asks the model to narrow the query instead of presenting omitted schemas as available. Removing an active schema does not remove its earlier transcript history; this feature does not set a cumulative history limit.
Tool search applies the current attachment and authorization rules before returning matches, and the shell catalog is built from the current turn’s visible-tool scope. Search, help, and invocation all stay within that scope; deferred child tools retain the scope of their server placeholder. The runtime checks authority again when an operation is called. Removing a server or revoking its connection therefore makes its operations unavailable even if a prior turn disclosed their names or schemas. When a server’s schema changes, refreshed discovery supplies its current definition; old arguments still go through current schema validation. A transient refresh failure may leave the last successful catalog available under the existing recovery policy. An authentication failure clears the cached catalog, and an incomplete page sequence is never published as a successful refresh.
## Native, CLI, and hybrid access
The same registered operation can be exposed as a native function, through the session’s `tools` command, or through both surfaces. Configure [`tools_in_shell`](https://everruns.com/docs/capabilities/tools-in-shell/) with `mode: "cli"` for the shell surface (the default), or `mode: "hybrid"` to retain native calls alongside the CLI. Hybrid mode alone keeps native schemas eager; to progressively reveal bounded native schemas, also enable bounded [`tool_search`](https://everruns.com/docs/capabilities/tool-search/):
```json
{
"capabilities": [
{ "ref": "tool_search", "config": { "bounded": true } },
{ "ref": "tools_in_shell", "config": { "mode": "hybrid" } }
]
}
```
With both capabilities configured, `tool_search` reveals selected native schemas while the same catalog is searchable and callable through `tools`. The CLI is useful when a script should chain operations or filter intermediate results; native calls are useful for one typed operation. Both routes use the same operation identity, schema validation, authorization, and execution policy.
When a CLI search matches deferred MCP servers, it loads at most eight per query. Other matching servers stay marked as not loaded for a later search. Each loaded server joins the shell catalog atomically; if it would exceed the 10,000-tool or 32 MiB catalog ceiling, none of that server’s tools are added.
MCP servers and plugins join the runtime’s operation registry when attached. Attaching, removing, or redefining one updates what the next search or CLI lookup can see. Existing authorization is checked during search and again during execution, including after a warm cache, session restart, or history compaction. Tool and prompt changes use the runtime’s existing incremental disclosure path; cache behavior can vary by provider and endpoint.
See [MCP](https://everruns.com/docs/features/mcp/) for server setup and [Tool Search](https://everruns.com/docs/capabilities/tool-search/) for provider-specific search modes.
## Run the synthetic example
The repository’s [Rust/Mira study](https://github.com/everruns/everruns/tree/main/evals/mcp-scale) includes a working paginated MCP server and an agent using the Framework’s production discovery and execution paths. From an Everruns checkout, provide `OPENROUTER_API_KEY` and run:
```bash
cd evals/mcp-scale
cargo run -- --offline-catalog
EVERRUNS_EVAL_TRIALS=1 EVERRUNS_EVAL_CONCURRENCY=1 \
cargo run -- --run --sample readonly-ticket \
--filter 'catalog_size=600,servers=1,' --runs-report /tmp/mcp-scale.jsonl
```
This runs the same read-only task through native, CLI, and hybrid surfaces using Muse Spark 1.3 Contributor. The fixture has 600 tools, returns 100 tools per page, and persists isolated synthetic state across MCP process launches. The scorer requires completed operations and checks that no mutation occurred; a model’s final answer alone cannot pass. Change the catalog size to `2000` to exercise a larger catalog. The study README documents the full matrix, stress measurements, and limitations.
---
# Model Context Protocol (MCP)
> Everruns is both an MCP server, exposing its agents and tools to external clients with OAuth 2.1, and an MCP client that registers remote servers as virtual capabilities.
Source:
Everruns speaks the [Model Context Protocol](https://spec.modelcontextprotocol.io) on **both sides**: it exposes its own agents and tools as an MCP server, and it consumes remote MCP servers as agent capabilities.
## Everruns as an MCP server
Every Everruns deployment exposes an authenticated MCP endpoint at `/mcp` so external clients, Claude Desktop, Cursor, VS Code, or another agent, can discover and call your agents and tools over JSON-RPC. It is a standard product surface, with no deployment variable or organization feature toggle.
* **Transport**: JSON-RPC 2.0 over Streamable HTTP (`POST /mcp`). Protocol versions `2026-07-28`, `2025-06-18` and `2025-03-26` are supported.
* **Methods**: `initialize`, `server/discover`, `ping`, `tools/list`, `tools/call`, `resources/list`, `resources/templates/list`, `resources/read`; under `2026-07-28`, `skills/list`, `skills/get`, and `resources/directory/read`, plus `tasks/get`, `tasks/update` and `tasks/cancel` for clients that use [Tasks](#long-agent-turns-as-tasks); and, for organizations that turn on MCP Events, `events/list`, `events/subscribe` and `events/unsubscribe`.
* **Auth**: MCP requests authenticate and resolve an organization before dispatch. OAuth 2.1 uses mandatory PKCE, and MCP access tokens are bound to the exact `/mcp` resource so they cannot be reused against the REST API. Unauthenticated requests fail with `401` and protected-resource discovery metadata is served at `/.well-known/oauth-protected-resource/mcp`.
* **Files**: `agent_run` and `session_send_message` take an optional `files` list, each file as base64 `data` or a public `https` `url`. The files are copied into the session’s `/attachments` folder, as for [channels](https://everruns.com/docs/features/channels/#files-people-send).
* **Entity cards**: under protocol `2025-06-18`, tools like `agent_get_card` return a sandboxed `text/html` MCP App resource at the `ui://` scheme alongside a text summary, so MCP-Apps-aware hosts render rich cards while others fall back to text.
Routing is intentionally split: REST under `/api/*`, MCP OAuth under `/oauth/*`, and MCP JSON-RPC at `/mcp`.
### Skills for MCP clients
The endpoint serves public workflow guidance through the official [MCP Skills extension](https://modelcontextprotocol.io/extensions/skills/overview). With protocol `2026-07-28`, `server/discover` advertises `io.modelcontextprotocol/skills` with directory reading enabled. Use `skills/list` to discover complete entries or `skills/get` to look up a known `SKILL.md` URI directly, then read instructions and supporting files with `resources/read`.
| Skill | Use it for |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `skill://everruns/SKILL.md` | Product concepts, getting started, and documentation links |
| `skill://everruns-cli/SKILL.md` | Command usage and examples across CLI, session shell, and MCP |
| `skill://everruns-mcp/SKILL.md` | Connection setup, finding agents, running sessions, inspecting results, and managing resources through MCP |
| `skill://everruns-agent-authoring/SKILL.md` | Creating or revising agent definitions, selecting capabilities and harnesses, and verifying behavior |
Each entry preserves all authored frontmatter and includes every file’s URI, SHA-256 digest, and byte size. Supporting references resolve relative to the skill directory. `resources/directory/read` lists direct children of a directory, for example `skill://everruns-mcp`, without a trailing slash. Unknown skills, files, directories, and cursors return JSON-RPC `-32602`.
These files ship with the server and are identical for every organization. They use public cache hints, while organization data resources remain private. Requests still require MCP authentication. Organization-managed Skills registry entries, the Rust Framework skill, and repository contributor workflows are not published in this catalog or accessible through its skill resource reads.
Reading a resource does not itself activate a skill. A host supporting the extension loads it through its skill loader, checks the manifest and frontmatter, and applies its approval policy. Older clients can read a known skill URI as an ordinary resource but receive no Skills extension capability.
### Long agent turns as tasks
An agent turn can take minutes. Clients on protocol `2026-07-28` that declare the [MCP Tasks extension](https://modelcontextprotocol.io/extensions/tasks/overview) (`io.modelcontextprotocol/tasks`) get a task back from `agent_run` and `session_send_message` instead of holding the request open, then poll `tasks/get` until the turn ends.
* **One task per message.** Each message starts its own task, and a finished task keeps its own reply, so an old task id never shows a later turn.
* **Results.** A completed task carries the agent’s reply as the tool result, with the session id, the message id and any structured result in `structuredContent`. A turn that failed or was stopped is `completed` with `isError: true` and a status message saying why; a cancelled turn is `cancelled`.
* **Questions.** When the agent asks with Ask User and the client also declares form elicitation, the task is `input_required` and carries the form. Answer it with `tasks/update`; send a follow-up message with `session_send_message`.
* **Cancel.** `tasks/cancel` stops the running turn.
Task ids are checked against the caller’s access on every request: an id for a session the caller cannot read is an unknown task. Clients that do not declare the extension keep getting plain tool results.
### In ChatGPT and Codex
`/mcp` is an [MCP App](https://github.com/modelcontextprotocol/ext-apps): hosts that render MCP Apps, including ChatGPT, Codex and Claude, show a live session view after `agent_run`, answer an agent’s questions in a form, approve or decline a pending action, and open an Everruns panel from the sidebar. Hosts that do not render apps get the same tools as plain text.
### Session events (experimental)
With the **MCP Events** feature flag turned on for an organization, an MCP client can subscribe to webhooks for `session.completed`, `session.failed` and `session.input_required`, filtered by agent or session. Callbacks must be public HTTPS URLs and are verified before the first event. Deliveries are signed per [Standard Webhooks](https://www.standardwebhooks.com/) and carry identifiers and state, not message content: read the session with `session_get_status`.
## Everruns as an MCP client
People can also add MCP servers for themselves; agents with the User MCP Servers capability use them while that person chats. See [User MCP Servers](https://everruns.com/docs/features/user-mcp-servers/).
A server the agent adds for one conversation only ([`"scope": "chat"`](https://everruns.com/docs/capabilities/user-mcp-servers/#this-conversation-only)) shows under the **MCP** button in that conversation’s header, where you can see whether you have signed in and remove it; its tools leave from the next message. Over the API: `GET /v1/sessions/{session_id}/mcp-servers` and `DELETE /v1/sessions/{session_id}/mcp-servers/{name}`.
Register a remote MCP server and its tools appear as a **virtual capability**: auto-discovered, namespaced, and executed alongside built-in capabilities. No code changes are needed to give an agent new tools.
* **Org-managed servers**: organization-scoped `McpServer` records connect over remote HTTP (Streamable HTTP). `stdio` is rejected by the hosted control plane and is only available to single-tenant runtime/CLI hosts. People who manage MCP servers keep these presets in **Settings > Organization > MCP catalog**; a preset does nothing until an agent or a person adds it. MCP sign-ins you authorized for agent servers that act as you are listed in **Settings > MCP servers**.
* **Scoped `mcpServers`**: harnesses, agents, and sessions can embed remote MCP config directly (the remote-server subset of `.mcp.json`) for session-local or agent-local wiring without creating an org-global record.
* **Restricted egress**: on deployments that limit where requests that send data may go (such as Everruns Cloud), MCP traffic only reaches approved services. Once a platform administrator allows it, org admins can add their own MCP hosts under **Settings > Organization > Outbound allowlist**.
* **Tool naming**: discovered tools are namespaced per server so they never collide with built-in capabilities.
* **Protocol compatibility**: the client negotiates the MCP protocol era per server. By default (`auto`) it issues a session-less `2026-07-28` request and transparently falls back to the stateful `initialize` handshake (`2025-11-25`, then `2025-06-18` / `2025-03-26`) for servers that require it, caching the verdict per server. Set the protocol mode to `legacy`, `stable`, or `rc` to pin a specific era and skip negotiation. No setting is needed for the common case.
### Reading a server’s resources
Many MCP servers publish context as [resources](https://modelcontextprotocol.io/specification/2025-11-25/server/resources): files, schemas, documents. Whenever a conversation has an MCP server attached, the agent gets two read-only tools:
* `list_mcp_resources` lists a server’s resources and resource templates, one page at a time.
* `read_mcp_resource` reads one resource by URI, including a URI the agent filled in from a template.
Text is returned to the agent as is, images arrive as images, and other binary content is saved to the session’s files (or only described, where the session has no files) instead of being pasted into the conversation. Content a server marks as meant only for the user stays out of the agent’s context. Resources are read with the same sign-in as the server’s tools, and only servers attached to the conversation can be reached. A server that publishes no resources says so when asked.
### Who a server acts as
Each MCP server attached to an agent says whose sign-in its calls use (`actsAs`, or **Who should this server act as?** on the agent’s **MCP servers** sheet):
| `actsAs` | On the sheet | Calls run as |
| ----------------- | -------------------------------------------------- | ---------------------------------------------------------------------------- |
| `none` | No identity | Nobody: the server needs no sign-in |
| `service` | Service identity | The agent’s own account, signed in once by someone who manages MCP servers |
| `user` | Invoking user | The person chatting, with their own sign-in; nothing when nobody is chatting |
| `user_or_service` | Each user, or the agent if they have not connected | The person chatting when they have signed in, otherwise the agent’s account |
`user_or_service` is never chosen for you. Unattended runs (schedules, triggers) always use the agent’s account, since nobody is chatting. Every MCP tool call records which account it used in its `tool.completed` event (`acted_as`: `user` or `service`), so a fallback to the agent is visible.
When someone adds an OAuth server to the catalog, Everruns checks right away that it can reach the server’s sign-in service and register with it. The catalog row then says **Ready to connect**, or why not, for example “Can’t reach mcp.linear.app: this host is not on the allowed network list”. Saving works either way; **Check again** re-runs the check after you change the network list.
An OAuth server from the catalog can be attached before anyone has signed in to it. The first **Authorize** or **Connect** registers Everruns with the server and signs in; until then the attachment shows **Connection required** and its tools are unavailable. If a sign-in fails, you return to the page you started from with the reason, for example that the server’s host is not on the organization’s allowed network list.
An agent with a server acting as `user` or `user_or_service` gets the `connect_mcp_server` tool, so it can show a **Connect** card in chat before a call fails.
### Connecting from chat
When a call needs a sign-in that is missing, the chat pauses on a **Connect** card (or, for the agent’s own account, an **Authorize** card for someone who manages MCP servers and **Ask an admin** for everyone else). Set `connectInChat` on the attachment (**Ask to connect in chat** on the agent’s **MCP servers** sheet) to choose:
| `connectInChat` | A missing sign-in |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ask` (default) | Pauses the chat with the card |
| `never` | Fails the call with an error naming the server and its settings link (`/settings/connections` for the person’s own sign-in, the agent’s **MCP servers** sheet for the agent’s), and the turn goes on |
`never` suits agents behind channels that cannot show a card, such as a chat bridge. `connect_mcp_server` follows it too and returns the same link instead of a card.
```json
{
"mcpServers": {
"github": { "use": "catalog:github", "actsAs": "user", "connectInChat": "never" }
}
}
```
### Loading tools on demand
Every server’s tools are normally listed at the start of each turn, which costs one `tools/list` round trip per server. Set `deferred: true` on an attachment (**Load tools on demand** on the agent’s **MCP servers** sheet) to skip that: the agent sees one line for the server, `mcp_` with its name and description, and loads the server’s tools when it needs them, by finding it with `tool_search` or calling that line. The tools are callable from the agent’s next step and stay loaded for the rest of the conversation. Off is the default, so existing agents are unchanged. A person’s own servers ([User MCP servers](https://everruns.com/docs/capabilities/user-mcp-servers/)) load on demand by default; a person can turn that off for one of their servers with its **Load tools on demand** switch in **Settings > My MCP servers**.
For catalogs with hundreds or thousands of operations, Everruns paginates discovery and supports bounded search and schema disclosure. See [Large MCP catalogs and tool surfaces](https://everruns.com/docs/features/large-mcp-catalogs/).
```json
{
"mcpServers": {
"linear": { "use": "catalog:linear", "actsAs": "user", "deferred": true }
}
}
```
A catalog entry can take the agent’s account from an existing connection instead of its own sign-in (`service_connection_provider`, **Agent credential** in the catalog form). The seeded `github` entry (`https://api.githubcopilot.com/mcp/`) uses the agent’s GitHub App, so an agent with a GitHub App needs no second GitHub login. A connection is only accepted for servers on its provider’s own hosts, so its tokens cannot be sent anywhere else.
GitHub’s MCP server accepts the App’s installation token, but that token acts as the App, not a person. It reaches only the repositories the App is installed on, with the App’s permissions, and tools about a person (`get_me`, notifications, starred repositories, “my” issues and pull requests) fail. For those, attach the server acting as `user` so the person signs in.
### Long-running tools
A call to an MCP tool fails if the server has not answered within 60 seconds. For a server whose tools take longer, such as one that runs a build or a deployment, set `tool_timeout_ms` on the attachment or catalog entry (**Tool call timeout** in the catalog form, in seconds). It accepts 1 second to 30 minutes. `timeout`, the key common `mcpServers` config files use, works as well. An attachment that uses a catalog entry may set its own timeout. If it does not, it uses the catalog’s timeout.
```json
{
"mcpServers": {
"ci": { "type": "http", "url": "https://ci.example.com/mcp", "tool_timeout_ms": 600000 }
}
}
```
A server that supports the [MCP Tasks extension](https://modelcontextprotocol.io/extensions/tasks/overview) does not need a longer timeout. Everruns declares the extension on every tool call, so such a server can answer with a task instead of holding the request open. The agent then checks on the task until it finishes, for up to 30 minutes. While it runs, the server’s status message (for example “Running tests”) shows on the tool call in chat and in the session’s events. Questions the task asks reach the agent’s user the same way as a server’s [form questions](#when-a-tool-needs-a-person). If the turn is cancelled or the wait runs out, Everruns cancels the task on the server.
### Waking an agent on a server’s events (experimental)
With the **MCP Events** flag on, an agent can also subscribe to events that one of its own MCP servers publishes, such as a tracker announcing new issues. Create an agent trigger with `trigger_type: "mcp_event"`, the attachment’s name in `mcp_server`, the event name from the server’s `events/list` in `mcp_event`, and any subscription arguments in `mcp_event_arguments`:
```bash
curl -X POST "$EVERRUNS_API/v1/agents/$AGENT_ID/triggers" \
-H "Content-Type: application/json" \
-d '{
"trigger_type": "mcp_event",
"mcp_server": "tracker",
"mcp_event": "issue.created",
"mcp_event_arguments": {"team": "eng"},
"message": "Triage {{payload.issue.title}}"
}'
```
Everruns subscribes with the agent’s credential for that attachment (its sign-in to the server, or the connection a catalog entry such as `github` names), a signing secret it generates, and a callback URL of its own; the server must accept webhook delivery. Each signed event starts a run with the event’s `data` as `{{payload}}`. Subscriptions are renewed before they expire and cancelled when the trigger is disabled or deleted. The attachment must act as the agent (`service` or `user_or_service`), not only as the calling user, since a trigger runs as the agent.
## Which tools ask before they run
In the normal approval mode, a tool that can change something asks a person before it runs. Everruns can’t see inside a remote tool, so most MCP tools ask by default, even ones that only read. You can tell Everruns what each tool does:
* **Read only**: the tool only looks things up. It never asks.
* **Changes**: the tool changes or sends something. It always asks.
Your label wins over what the server says about its own tools, and it stays when the server’s tool list changes. A tool with no label follows what the server says about it.
In the app, open **Settings > MCP catalog** and click **Tools** on a server. Each tool shows its description, what the server says about it, and a choice: **Default**, **Read only**, or **Changes things**.
**Suggest labels** asks a decision model to judge every tool that has no label yet. It only fills in a suggestion, such as “Suggested: read only”. Nothing changes until a person picks a choice or clicks **Use suggestion**.
The deployment’s decision model judges, unless your organization set **System decisions** to **This organization’s model**: then your organization’s default decision model judges, on your provider account. If that model is not usable, the button reports an error rather than using the deployment’s.
List a server’s tools with their labels, then set or clear one:
```bash
curl "$EVERRUNS_API/v1/mcp-servers/mcp_01h9/tools"
curl -X PUT "$EVERRUNS_API/v1/mcp-servers/mcp_01h9/tools/search_docs/label" \
-H "Content-Type: application/json" -d '{"label": "read_only"}'
```
Send `{"label": "changes"}` to always ask, or `{"label": null}` to remove the label. Setting a label needs the same permission as editing the server, and it removes the tool’s suggestion. To get suggestions, which come back with the tool list:
```bash
curl -X POST "$EVERRUNS_API/v1/mcp-servers/mcp_01h9/tools/suggest-labels"
```
Labels work on the other places an MCP server can live too:
* **An agent’s or harness’s own server** (one added by URL): put the labels in the server’s `tool_labels`, keyed by the tool’s own name. They win over a catalog server’s saved labels when the entry uses one.
```json
"mcpServers": {
"notes": {
"url": "https://notes.example.com/mcp",
"tool_labels": { "search": "read_only", "publish": "changes" }
}
}
```
* **Your own server** (one you added by URL under **My MCP servers**): click **Tools** on it. Everruns asks the server for its tools with your sign-in. A server you added from the catalog uses the catalog’s labels.
Servers added during a conversation never carry labels, because the agent can add them itself and a label only ever lets a tool run without asking.
## When a tool needs a person
Some MCP tools cannot finish without a human: a payment to authorize, an API key to paste, a consent screen to click. Under protocol `2026-07-28` the server hands back a URL instead of asking for the value, and Everruns holds the turn until someone answers. See [URL mode elicitation](https://everruns.com/docs/features/mcp-url-elicitation/).
A server can also ask the person a few structured questions (form mode elicitation), such as which environment to deploy to. This is off by default: set the server’s **Elicitation** setting (`elicitation_policy`) to `url_and_form` to allow it. The questions appear in the conversation marked as coming from that server, and the answer goes back to the server when the tool runs again. Everruns refuses questions that ask for a password or key, and declines on the person’s behalf if nobody answers in time. `none` stops a server eliciting at all.
## Use Everruns from your AI tools
To give Claude Code, Codex, Cursor or Gemini CLI the MCP server plus skills for building, debugging and shipping agents, install the [`everruns` plugin](https://github.com/everruns/plugins); see [Use in AI tools](https://everruns.com/docs/getting-started/use-in-ai-tools/).
## Related
* [URL mode elicitation](https://everruns.com/docs/features/mcp-url-elicitation/), tool calls that need a person
* [Capabilities](https://everruns.com/docs/features/capabilities/), how virtual capabilities fit the capability system
* [Slack Integration](https://everruns.com/docs/capabilities/slack/), publishing an Agent through a messaging endpoint
---
# URL mode elicitation
> When an MCP server needs a secret, an authorization or a payment, Everruns pauses the turn and a person finishes it in their browser, out of the model's sight.
Source:
Some tool calls cannot be completed by an agent alone. A billing server needs the customer to authorize a charge with their bank; an analytics server needs the user’s own API key; a provider needs an OAuth consent screen clicked. The value involved must never reach the agent: not the MCP client, not the model’s context, not the event log.
MCP’s answer is **URL mode elicitation** (protocol `2026-07-28`). Instead of asking the client for the value, the server answers `tools/call` with a URL and waits. Everruns supports it on both sides.
## As an MCP client: the turn holds
When a tool call comes back with a URL elicitation, Everruns pauses the turn and puts the URL in front of the person, with the domain highlighted:
[](https://everruns.com/docs/videos/mcp-url-elicitation-consent.mp4)
The user asks for a charge, the server needs their bank’s authorization, the turn holds on a consent card, and the tool runs once they come back and confirm.
Consent is collected in **two steps** — *Open link*, then *I’ve finished — continue* — because only the person knows when the interaction on the other side actually finished. Answering the server the moment the tab opens resumes the turn too early, and the server simply asks again.
### Entering a secret
The same flow carries values the agent must never see. Here the server needs the user’s own API key and collects it on its own page:
[](https://everruns.com/docs/videos/mcp-url-elicitation-enter-a-secret.mp4)
The key goes from the user’s browser straight to the provider. The Everruns transcript carries the report, never the key.
### What the client guarantees
* **The capability is declared only when a human can answer.** A host with no way to reach a person declares no `elicitation` capability at all, so a compliant server cannot ask.
* **The URL is validated before anyone sees it**: `https` only (loopback `http` for local development), so a consent surface is never handed a `javascript:` or `file:` URL. The client never fetches it.
* **The domain is shown, and Punycode is flagged.** Internationalized domains are legitimate but can impersonate; the card says so.
* **Consent is single use and bound to one domain.** A server that elicits `pay.example.com`, waits for the click, then elicits somewhere else on the retry gets no reuse of that consent — the user is asked again.
* **A refusal is final.** Declining ends the call and tells the agent to continue without the tool.
### Refusing
[](https://everruns.com/docs/videos/mcp-url-elicitation-decline.mp4)
The user declines, and the agent carries on without the tool instead of asking again.
### One consent, one domain
[](https://everruns.com/docs/videos/mcp-url-elicitation-domain-swap.mp4)
Consent was given for one host. The server elicits a different one on the retry, so the consent is not reused: the user is asked afresh, for the new domain.
## As an MCP server: Everruns serves the form
Everruns’ own `/mcp` endpoint uses the same mechanism when a client asks it to store a secret or connect a provider. `session_set_secret` never accepts a value as a parameter: it answers with a URL to a form Everruns serves, the user types the value there, and the retry confirms it is stored. The MCP client that started the call only ever holds the URL.
The page requires the visitor’s own session on top of the signed link, and refuses anyone but the user the elicitation was minted for — the link alone grants nothing, which is what closes the phishing case the spec warns about.
[](https://everruns.com/docs/videos/mcp-url-elicitation-as-server.mp4)
An MCP client asks Everruns to store a secret, gets a URL back, the user fills in the form Everruns serves, and the retry confirms it is stored.
## Clients that cannot render a card
Pausing is a client capability, declared per session:
```json
{ "hints": { "url_elicitation": true } }
```
The Chat UI declares it automatically. A client that does not gets the older behaviour: the turn continues and the elicitation reaches the user through the tool result, as an actionable `url_elicitation_required` payload with the URL and the server’s reason.
To complete such a call from your own client, see [Complete a URL elicitation over the API](https://everruns.com/docs/how-to/complete-a-url-elicitation/).
## Protocol support
URL mode elicitation is `2026-07-28` only. In earlier eras elicitation is a server-initiated request over a server-to-client stream this transport does not open, so Everruns declares nothing regardless of what the host can do.
## Try it
The [URL elicitation example](https://github.com/everruns/everruns/tree/main/examples/mcp-url-elicitation) has a dependency-free MCP server that elicits, and a script that drives the whole flow over the API.
## Related
* [MCP](https://everruns.com/docs/features/mcp/), Everruns on both sides of the protocol
* [Capabilities](https://everruns.com/docs/features/capabilities/), how MCP servers become agent tools
---
# Agent and User Memory
> Understand organization, agent, and user memory scopes, their automatic mount paths, and the privacy and access rules for each.
Source:
Memory keeps files available across sessions. Everruns provides three ownership scopes so shared organization knowledge, an agent’s learned context, and a user’s preferences do not have to share the same access boundary.
## The three scopes
| Scope | Lifetime and owner | How it enters a session | Access |
| ------------ | --------------------------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| Organization | A named store managed within one organization | Select it in the `memory` capability and choose a path under `/workspace` | Read-only by default; read-write must be selected explicitly |
| Agent | One server-managed store per agent | Automatically mounted for sessions hosted by that agent at `/memory/agent` | Read-write in the hosted session |
| User | One server-managed store per user | Automatically mounted in the owning user’s private, default session workspace at `/memory/user` | Read-write for the owner and the agent working in that session |
Organization memories are appropriate for shared references such as runbooks or product documentation. Agent memory follows one host agent across its sessions. User memory follows one user so preferences and personal context can persist across their sessions.
## Privacy defaults
**User memory is private by default.** Only the session owner may access `/memory/user` through user-facing session file APIs. Other users cannot read or mutate the subtree, and file searches redact matches from it. Internal runtime access lets the agent working for the owner read and update that memory.
User memory is never mounted into a workspace that more than one session shares (only sessions created before shared workspaces were retired can have one).
Agent memory is separate from user memory. It is shared by sessions hosted by the same agent, so do not use `/memory/agent` for user-private data. A guest agent’s private memory is not merged into a shared session’s workspace-wide `/memory/agent` path.
## How automatic mounts work
When a session starts, Everruns lazily creates any missing scoped stores and mounts their current files:
* `/memory/agent` contains the host agent’s persistent files.
* `/memory/user` contains the session owner’s persistent files when the session uses the owner’s private default workspace.
Both automatic mounts are read-write. The regular file tools and Workspace UI traverse them alongside session files, and writes persist to the scoped memory for later sessions.
The `/memory/*` namespace is reserved. You cannot use it for caller-supplied initial files or for mounts configured through the public `memory` capability.
## Workspace and Memory
A session’s **Workspace** at `/workspace` is its working area. Its files and its session SQL database belong to that session, and one session cannot see another’s Workspace. **Memory** is the durable tier, and the only way files are shared across sessions.
The mount configuration is captured when the session is created, so archiving or renaming a Memory later does not change what a running session mounted: an archived Memory surfaces an error rather than files silently disappearing. The same file tools read native and mounted paths, and a write to a read-only mount returns an error.
## Organization memory access
Organization memories are created and managed through the Memory UI and `/v1/memories` API. Add the `memory` capability to select an active organization memory, its mount path, and its access mode.
* Omitted access mode defaults to `readonly`.
* A `readwrite` mount writes through to the durable organization memory.
* Source-backed GitHub or Git memories are always read-only.
* Agent- and user-scoped memories are server-managed and cannot be selected through `memory.mounts`.
See the [API reference](https://everruns.com/docs/api/) for current organization-memory schemas.
## See also
* [Session participants](https://everruns.com/docs/features/session-participants/), host and guest agent behavior in shared sessions.
* [File System capability](https://everruns.com/docs/capabilities/file-system/), tools that access session and mounted files.
## Background consolidation
The optional [Dreaming capability](https://everruns.com/docs/features/dreaming/) consolidates completed experience into authorized Memory files without rewriting the active conversation.
---
# Personal Agent Protocol (Poppy)
> Let people's own agents talk to your Everruns agent over the Personal Agent Protocol, and let an Everruns agent act as a person's agent at other companies.
Source:
The [Personal Agent Protocol](https://personalagentprotocol.org) (“Poppy”) is an open standard for a person’s own agent to do business with a company: find the company by its domain, start a Session, talk to the company’s agent, and ask for a person. Everruns implements [draft 0.1](https://personalagentprotocol.org/docs/spec) on both ends:
* **Company side**: a `poppy` [channel](https://everruns.com/docs/features/channels/) makes an Everruns agent a company’s agent. Any personal agent that follows the spec can reach it, with nothing to register.
* **Personal side**: the [`poppy_client`](https://everruns.com/docs/capabilities/poppy-personal-agent/) capability lets an Everruns agent act for a person at any company that publishes `/.well-known/poppy.json`. Platform Chat has it, and Framework apps get it with `PoppyPersonalAgent`.
Poppy is not A2A or PACT. It is standard OAuth pieces (client metadata documents as agent identity, a JWT bearer grant, DPoP on every request) plus its own conversation API. For agent-to-agent calls between systems you own, use [A2A](https://everruns.com/docs/features/a2a/); for PACT personal agents, see [A2A personal agents](https://everruns.com/docs/features/a2a/#personal-agents-pact).
## Be a company’s agent
Create a `poppy` channel on the agent that should answer, then publish it:
```bash
EVERRUNS=https://your-everruns-host/api
curl -X POST "$EVERRUNS/v1/agents/$AGENT_ID/channels" \
-H "Authorization: Bearer $EVERRUNS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel_type": "poppy",
"channel_config": {
"domains": ["shop.example.com"],
"organization_name": "Example Shop"
}
}'
curl -X POST "$EVERRUNS/v1/agents/$AGENT_ID/channels/$CHANNEL_ID/publish" \
-H "Authorization: Bearer $EVERRUNS_API_KEY"
```
Then redirect your own discovery document to the channel. Everruns serves everything else:
```text
https://shop.example.com/.well-known/poppy.json
-> 302 https://your-everruns-host/api/v1/channels/{channel_id}/poppy/poppy.json
```
| Field | Description |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `domains` | Required. Your domains as lowercase host names, each redirecting its `/.well-known/poppy.json` here. The first is the one `poppy.json` names. |
| `organization_name` | The company name personal agents see. Defaults to the agent’s name. |
| `allowed_agents` | Personal agents allowed to start Sessions, by `client_id`. Absent means any personal agent that proves its identity. |
| `blocked_agents` | Personal agents refused even when `allowed_agents` is absent. |
| `rate_limit_per_minute` | Optional per-IP limit for this channel. |
The channel holds no secrets: Everruns generates the key that signs Session Tokens and keeps it encrypted.
### What the channel serves
Everything is under the channel’s base URL, `/v1/channels/{channel_id}/poppy`, which is also the OAuth issuer:
| Route | What it is |
| --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `GET poppy.json` | The discovery document your redirect points at. |
| `GET .well-known/oauth-authorization-server` | Sign-in server metadata, listing your domains. |
| `POST oauth/token` | Starts or renews a Session (JWT bearer grant, `private_key_jwt`, DPoP). |
| `POST oauth/revoke` | Token revocation (RFC 7009). |
| `POST conversations`, `…/{id}/messages`, `GET …/{id}/events`, `POST …/{id}/handoff`, `…/{id}/close` | The conversation API. |
Each conversation is one Everruns session, owned by the personal agent and the person’s User ID, so you can read it in the session list like any other. Personal agents see only the agent’s replies, never tool calls, reasoning or errors. A retried message returns the stored reply instead of starting a second turn.
## Act for a person at a company
Add the `poppy_client` capability to an agent:
```json
{ "ref": "poppy_client", "config": {} }
```
The agent gets five tools (`poppy_discover`, `poppy_send`, `poppy_read`, `poppy_handoff`, `poppy_close`) and can talk to any company by domain. On Platform and Cloud, the deployment is one personal agent with a different User ID per person at each company, and its keys and Session Tokens stay on the server. In the Framework, the app is its own personal agent. See [Poppy Personal Agent](https://everruns.com/docs/capabilities/poppy-personal-agent/) for both, and [the Framework example](https://github.com/everruns/everruns/tree/main/examples/poppy-personal-agent) for a runnable app.
## Interoperability
Draft 0.1 has no official conformance suite. Both ends are tested against independent implementations:
* [pi-poppy](https://github.com/richardanaya/pi-poppy)’s personal agent, which maps every MUST, SHOULD and MAY of the draft to tests, drives a channel through discovery, a Session, a conversation, handoff, close and revocation.
* Datalayer’s `pap company verify` checks the channel’s discovery.
* Everruns agents with `poppy_client`, on the server and in the Framework, talk to pi-poppy’s company, with its DPoP nonces on.
CI runs the first two and the scripted personal-agent checks on every change. [`scripts/poppy-interop.sh`](https://github.com/everruns/everruns/blob/main/scripts/poppy-interop.sh) also runs the real-model ones when `OPENAI_API_KEY` is set.
## Limits
* Sessions are signed out. Sign-in (`poppy:read` and `poppy:write` scopes, Account Tokens) and Direct Conversations are not implemented yet, so a company that needs an account says so.
* Asking for a person is answered by the agent: no one at the company joins from Everruns yet.
* Company APIs and the operations extension are not used on either side.
* Channels are created over the API; there is no setup screen yet.
## Implementation
* Company side: [`crates/server/src/channels/poppy`](https://github.com/everruns/everruns/tree/main/crates/server/src/channels/poppy) and its configuration in [`record/poppy.rs`](https://github.com/everruns/everruns/blob/main/crates/server/src/domains/agent_channels/record/poppy.rs).
* Personal side: the protocol client in [`crates/capabilities/src/capabilities/poppy_client`](https://github.com/everruns/everruns/tree/main/crates/capabilities/src/capabilities/poppy_client), used by the server’s [`poppy_agent`](https://github.com/everruns/everruns/tree/main/crates/server/src/domains/poppy_agent) and by Framework apps.
## Related PACT walkthrough
Everruns also supports [PACT](https://everruns.com/docs/features/a2a/#personal-agents-pact). This walkthrough demonstrates PACT account consent, cancellation approvals, and signed receipts, using a local demo client and synthetic store data.
[PACT account consent and signed receipts in Everruns](https://www.youtube-nocookie.com/embed/9FZiYv5PsWY?rel=0)
[Watch on YouTube](https://www.youtube.com/watch?v=9FZiYv5PsWY).
---
# Sandbox Templates
> Configure reusable sandbox recipes, bind one to an Agent, and recover Session workspace state after compute loss.
Source:
A **Sandbox Template** is reusable, versioned configuration for filesystem and compute. A new Session pins one immutable template revision into its **primary Sandbox**. The Sandbox is the durable logical resource; its physical Daytona instance or other provider compute may be replaced without changing the Session or Sandbox identity.
Sandbox Templates and Harnesses are separate. A Harness defines behavior and may fix the Sandbox policy or let an Agent offer template bindings. A template selects Bashkit, Daytona, or another execution target without changing the ordinary shell and file tools the model uses.
With a managed provider, every file tool works on the Sandbox’s `/workspace`: `read_file`, `read_many_files`, `write_file`, `edit_file`, `glob`, `grep`, and `list_directory`. Tools that only exist for the Session’s own files, such as `grep_files` and `delete_file`, are not offered. Skills under `/workspace/.agents/skills/` and saved tool output under `/workspace/outputs/` stay readable: when the Sandbox does not hold such a file, the read falls back to the Session’s files.
## Create a Sandbox Template
1. Open **Sandbox Templates** and select **New Sandbox Template**.
2. Give it a stable name and display name.
3. Select Bashkit or an available managed provider: Daytona, E2B, or Modal (when enabled).
4. For a managed provider, choose its **Provider account**: whose connection the Sandbox uses, and so whose provider account pays. Each choice is labelled with the provider, for example **Daytona · Personal**:
* **Personal** uses the connection of the person starting the Session. A Session started by a trigger or a channel has no user, so it uses the organization’s provider account instead, when the organization has exactly one for that provider. With none or several, the Session fails to start and names the problem; pick **Organization account** to choose one.
* **This Agent** uses the Agent Virtual User’s connection for every Session.
* **Organization account** uses one shared account managed by an organization admin under **Sandboxes → Provider accounts**. When the organization has none for the provider, the picker links there to add one.
5. Configure durability, lifecycle, and bootstrap options, then save.
Editing a Sandbox Template creates a new immutable revision. Existing Sessions keep the revision they already reference. Managed templates such as Bashkit Virtual Workspace are provisioned by Everruns and cannot be edited or archived.
## Configure an Agent
1. Open **Agents**, then open the Agent.
2. Under **More**, select **Sandbox**.
3. Add a Sandbox Template. The Agent stores its exact revision, not a moving pointer.
4. Choose the policy mode:
* **Fixed**: every Session gets the single configured Sandbox; Session overrides are rejected.
* **Selectable**: Playground can select one of the Agent’s declared template bindings.
* **Configurable**: Playground may also submit constrained one-off configuration.
5. Choose the default binding when the policy permits multiple choices.
6. Select **Done**, then **Save changes**.
Bashkit is available without a provider connection. Managed providers are shown as unavailable when their integration is not installed. User credentials live under **Settings → Connections**, Agent credentials under the Agent’s Virtual User, and organization credentials under **Sandboxes → Provider Accounts**. A fixed Agent policy fixes this choice; Session creation cannot override it. Do not put credentials in target options or bootstrap commands.
Use Sandbox Templates to select managed provider credentials. Raw `session_sandbox` capability configurations cannot supply credential bindings or API/toolbox endpoint overrides; the API rejects them on Agent, Harness, and Session writes.
The built-in [Bashkit Worker](https://everruns.com/docs/built-ins/harnesses/bashkit-worker/) seals the primary Sandbox to Everruns’ managed Bashkit template. Agents based on it cannot change the policy, and Session creation cannot override it. Use [Sandbox Worker](https://everruns.com/docs/built-ins/harnesses/sandbox-worker/) when an Agent must select Daytona, a container or another full sandbox; it requires one.
## Start a Playground Session
1. Open **Playground** and start a new Session.
2. Select the Agent.
3. For a selectable or configurable Agent, select an allowed **Sandbox**. A fixed Agent shows the resolved Sandbox as read-only.
4. Select **Start Playground chat**.
The Session pins the resolved specification. Editing the Agent or Sandbox Template later affects new Sessions only. Open the Session’s **Workspace** tab to inspect the primary Sandbox ID, pinned template revision, target, containment, capabilities, recovery class, generation, and lifecycle state.
## Idle and retention
**When idle** and **Idle after** decide what happens to a running Sandbox nobody is using: **Checkpoint and stop** or **Stop** pauses it, **Keep running** leaves it on, and **Delete** deletes it. A paused Sandbox resumes on the next command.
The Sandbox at the provider is disposable: if the Session runs again after its Sandbox was deleted, it gets a fresh one from the same template and runs the bootstrap commands again. Pick **Delete** for work that starts from scratch each time, such as reviewing a pull request.
Paused Sandboxes still use provider storage, so Everruns deletes them after **Delete after paused**: one day by default, or 3, 7, or 30 days, or **Never**. Deleting removes the Sandbox and its files at the provider, not the Session. The Sandbox’s record and history stay on the **Sandboxes** page under **Deleted**.
```json
{
"lifecycle": {
"idle_after_seconds": 900,
"idle_action": "checkpoint_and_stop",
"delete_after_seconds": 604800
}
}
```
Set `delete_after_seconds` to `null` to keep paused Sandboxes until the Session is deleted.
## Outbound network
Daytona, E2B and Modal templates can limit what the Sandbox reaches on the network. Under **Outbound network**, choose **Open**, **Only listed domains** (one per line, `*.example.com` for subdomains), or **Blocked**. The provider enforces the rule on its own network, so it holds for every process in the Sandbox.
On Daytona and E2B the Session’s network access list narrows the rule further: the Sandbox reaches only hosts both allow, and a Sandbox resumed later follows the Session’s current list. Sandboxes the agent creates with the provider’s own tools, desktops included, get the Session’s list. Providers filter by host, so a URL rule such as `https://pypi.org/simple/` opens all of `pypi.org` inside the Sandbox.
```json
{
"containment": {
"level": "isolated",
"network": { "mode": "allowlist", "allowed_hosts": ["github.com", "*.github.com"] }
}
}
```
## Desktop
Daytona and E2B templates can start the primary Sandbox with a Linux desktop. Turn on **Desktop** in the template, or set `target.options.desktop` to `true`. The Agent then gets the `computer` tool: it takes screenshots of the desktop and clicks, types and scrolls on it. The desktop runs in the same Sandbox as the shell and file tools, so files the agent writes are on the desktop too, and the template’s outbound network rule covers the desktop’s browser.
To pick the display size or cap the number of actions, pass the computer-use settings instead of `true`:
```json
{
"target": {
"kind": "managed",
"provider": "e2b",
"options": {
"desktop": { "display_width": 1280, "display_height": 800, "max_actions_per_session": 300 }
}
}
}
```
The desktop is the Agent’s only display: a template with a desktop replaces any other computer-use capability the Agent has. On E2B the Sandbox starts from E2B’s `desktop` template unless the template names another one built on it. On Daytona it uses Daytona’s default image, which ships the desktop, so a desktop template cannot also choose a size or snapshot. Every action returns a screenshot, which costs image tokens.
## Recovery after compute loss
A `checkpointed` Sandbox separates durable workspace state from replaceable physical compute. Everruns checkpoints successful mutations for targets with `checkpointed` durability. If a Daytona instance disappears, the next sandbox operation creates replacement compute, restores the workspace, and continues the same Session. E2B and Modal use provider snapshots instead; their UI labels that weaker recovery class explicitly.
Files under the managed workspace survive replacement. Running processes, open terminals, and in-memory process state do not.
## API
Create a reusable template:
```json
POST /v1/sandbox-templates
{
"name": "daytona-build",
"display_name": "Daytona Build",
"spec": {
"target": {
"kind": "managed",
"provider": "daytona",
"credential": { "source": "organization", "connection_id": "..." }
},
"durability": "checkpointed"
}
}
```
Bind exact revisions to an Agent:
```json
{
"sandbox_policy": {
"mode": "selectable",
"default": "scratch",
"templates": {
"scratch": {
"template_revision_id": "sbxtplrev_...",
"target": { "kind": "vfs", "provider": "bashkit" }
},
"build": {
"template_revision_id": "sbxtplrev_...",
"target": { "kind": "managed", "provider": "daytona" },
"durability": "checkpointed"
}
}
}
}
```
Select a declared binding in Playground when the mode is `selectable` or `configurable`:
```json
{
"agent_id": "agent_...",
"sandbox": { "use": "build" }
}
```
Use `GET /v1/sandbox-targets` to discover available targets, `GET /v1/sessions/{session_id}/sandbox` to inspect the resolved Sandbox, and `GET /v1/sandbox-templates` to list reusable templates.
## Resource Sandbox fleets
The primary Sandbox is implicit: ordinary shell and file tools always address it. An Agent with the `sandbox_fleet` capability can additionally create explicitly addressed resource Sandboxes through `sandbox_create`, then use `sandbox_exec`, `sandbox_read_file`, `sandbox_write_file`, `sandbox_inspect`, `sandbox_checkpoint`, and `sandbox_manage`. Fleet operations never change where ordinary shell or file tools run.
---
# SDKs
> Official client libraries for Rust, Python, and TypeScript — typed clients, async APIs, SSE streaming with automatic reconnection.
Source:
The Everruns SDKs are official client libraries for building agent applications. Rust, Python, and TypeScript with a consistent API across all three.
What the SDKs handle for you:
* **Consistent API** across languages
* **Async/await patterns** throughout
* **SSE streaming** with automatic reconnection, heartbeat-based stale detection, and `since_id` resumption
* **Typed models** generated from the OpenAPI spec
* **Sub-client organization** (`agents`, `sessions`, `messages`, `events`, `filesystem`, …)
For a hands-on lesson, see [Build your first agent](https://everruns.com/docs/tutorials/building-agents-using-sdk/) or the minimal [Run an Agent](https://everruns.com/docs/tutorials/run-an-agent/) notebook.
To call one agent from an application without a personal access token, use the SDKs’ `AgentClient` with an agent key instead of the management client. See [Agent API](https://everruns.com/docs/features/agent-api/#sdks).
## Install
* Rust
Requires Rust 1.70+
```bash
cargo add everruns-sdk
```
* Python
Requires Python 3.10+
```bash
pip install everruns-sdk
```
* TypeScript
Requires Node.js 18+
```bash
npm install @everruns/sdk
```
## Authenticate
All SDKs read `EVERRUNS_API_KEY` (a personal access token, `evr_pat_...`) and `EVERRUNS_API_URL` from the environment. Always set the URL: use `https://app.everruns.com/api` for [Everruns Cloud](https://app.everruns.com), or `http:///api` for a self-hosted Platform. Create the token under **Settings** > **Personal Access Tokens**.
* Rust
```rust
use everruns_sdk::Everruns;
let client = Everruns::from_env()?; // EVERRUNS_API_KEY + EVERRUNS_API_URL
let client = Everruns::with_base_url("evr_pat_...", "https://app.everruns.com/api")?;
```
* Python
```python
from everruns_sdk import Everruns
client = Everruns() # EVERRUNS_API_KEY + EVERRUNS_API_URL
client = Everruns(api_key="evr_pat_...", base_url="https://app.everruns.com/api")
```
* TypeScript
```typescript
import { Everruns } from "@everruns/sdk";
const client = new Everruns(); // EVERRUNS_API_KEY + EVERRUNS_API_URL
const client = new Everruns({ apiKey: "evr_pat_...", baseUrl: "https://app.everruns.com/api" });
```
## API coverage
| Resource | Operations |
| ------------------- | -------------------------------------------------------------------------------------- |
| **Agents** | Create, list, get, apply, copy, delete, import, export, stats, versions, health checks |
| **Sessions** | Create, list, get, delete, cancel, resume, pin, export, set secrets, budgets |
| **Messages** | Create, list, submit client-side tool results |
| **Events** | List, stream (SSE) |
| **Capabilities** | List, get, guardrail dry runs |
| **Harnesses** | Create, list, search, get, update, delete |
| **Models** | List, get |
| **Workspaces** | Create, list, get, update, delete |
| **Workspace files** | List, read, create, update, delete, move, copy, grep, stat |
| **Memories** | Create, list, get, update, delete, sync, file operations |
| **Budgets** | Create, list, get, update, delete, top up, ledger, check |
| **Connections** | Set, list, remove |
Everything else (providers, MCP servers, organizations, schedules) is available through the [REST API](https://everruns.com/docs/api/).
## Error handling
| Error type | Description |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `AuthenticationError` | Invalid or missing API key (Python, TypeScript) |
| `NotFoundError` | Resource not found (Python, TypeScript) |
| `RateLimitError` | Rate limit exceeded (Python, TypeScript) |
| `ApiError` | General API error; Rust uses `Error::Api { status, .. }` for all HTTP errors and `Error::Auth` for credential problems |
* Rust
```rust
use everruns_sdk::Error;
match client.agents().get("invalid-id").await {
Ok(agent) => println!("Found: {}", agent.name),
Err(Error::Api { status: 404, message, .. }) => println!("Not found: {}", message),
Err(Error::Auth(msg)) => println!("Auth error: {}", msg),
Err(e) => println!("Other error: {}", e),
}
```
* Python
```python
from everruns_sdk import NotFoundError, AuthenticationError
try:
agent = await client.agents.get("invalid-id")
except NotFoundError as e:
print(f"Not found: {e}")
except AuthenticationError as e:
print(f"Auth error: {e}")
```
* TypeScript
```typescript
import { NotFoundError, AuthenticationError } from "@everruns/sdk";
try {
const agent = await client.agents.get("invalid-id");
} catch (e) {
if (e instanceof NotFoundError) {
console.log(`Not found: ${e.message}`);
} else if (e instanceof AuthenticationError) {
console.log(`Auth error: ${e.message}`);
}
}
```
## Do something
* [Build your first agent](https://everruns.com/docs/tutorials/building-agents-using-sdk/) — guided tutorial.
* [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/) — `AgentClient` with an agent key.
* [Stream events with the SDK](https://everruns.com/docs/how-to/stream-events/) — common streaming patterns.
* [Handle errors and cancel turns](https://everruns.com/docs/how-to/handle-errors-and-cancellation/) — graceful failure paths.
* [Orchestrate multi-agent pipelines](https://everruns.com/docs/how-to/orchestrate-multi-agent-pipelines/) — chain agents together.
## See also
* [API reference](https://everruns.com/docs/api/) — every endpoint, generated from OpenAPI.
* [Agent API](https://everruns.com/docs/features/agent-api/) — the routes `AgentClient` calls.
* [Event Reference](https://everruns.com/docs/event-reference/) — every event type.
* [GitHub Repository](https://github.com/everruns/sdk) — source code and examples.
## Overview video
[Everruns SDK Overview](https://www.youtube.com/embed/1FfGKUTzBzA)
---
# Session Participants
> Invite agents into a shared session, address a specific agent for a turn, and understand host, member, user, join, and leave behavior.
Source:
A session can include more than one agent and more than one user. Session participants record who is present, whether they are the host or a member, and when they joined or left.
## Host and member roles
An agent-backed session has one active host agent. The host supplies the session’s harness and answers user turns by default.
Other agents and users join as members:
* an **agent member** can be addressed for an individual turn;
* a **user member** records a person participating in the session, but cannot be addressed as an agent responder; and
* a member that leaves remains in participant history with a leave time.
Inviting a member agent does not replace the host or the host’s harness. The invited agent contributes its behavior, model defaults, capabilities, and client tools only when a turn is addressed to it.
## Invite an agent in the UI
The session view shows multi-party membership in the **In this session** rail on desktop. It labels each participant as **Host** or **Member** and as **Agent** or **User**.
To add an agent:
1. Open **In this session**.
2. Select **Invite agent**.
3. Choose an agent that is not already active in the session.
The agent joins as a member. The rail also shows participants who have left. Use the remove action on an active member to make it leave; the host cannot leave through the ordinary participant action. If a user who left sends another message, that user rejoins automatically with a new participation interval.
Agents can perform the same operation with invite-mode handoff. An agent with the `agent_handoff` capability calls `spawn_agent` with `mode = invite`. Unlike foreground or background handoff, invite mode joins the target agent to the current session instead of creating a child session.
## Address an agent for one turn
When at least one active member agent is available, the composer shows an **Address** selector:
* **Session host (default)** sends the turn to the host agent.
* Selecting a member agent sends that turn to the selected participant.
Addressing is per turn. It does not change the session’s host or default responder. A participant must still be active and must be an agent; requests that address a user or a participant who already left are rejected.
## API
Participant membership is managed below a session:
| Method | Path | Purpose |
| -------- | --------------------------------------------------------- | ----------------------------------------------- |
| `GET` | `/v1/sessions/{session_id}/participants` | List active and past participants in join order |
| `POST` | `/v1/sessions/{session_id}/participants` | Add an agent or user member |
| `DELETE` | `/v1/sessions/{session_id}/participants/{participant_id}` | Mark a member as having left |
To address an agent, set `addressed_participant_id` when creating a message with `POST /v1/sessions/{session_id}/messages`. Omit it to use the host.
The participant list is the source of truth for join and leave history. Join and leave do not emit dedicated participant events on the session SSE stream. See the [API reference](https://everruns.com/docs/api/) for current schemas and authorization requirements.
## See also
* [Agent triggers](https://everruns.com/docs/features/agent-triggers/), proactive runs start sessions with the trigger’s agent as host.
* [Sub Agents capability](https://everruns.com/docs/capabilities/sub-agents/), foreground, background, and invite handoff modes.
* [Core concepts](https://everruns.com/docs/getting-started/concepts/), sessions, turns, and agents.
---
# Agent Skills
> Portable instruction packages following the agentskills.io open spec, from the session workspace or the organization-wide Skills Registry, loaded on demand.
Source:
**Agent Skills** are portable instruction packages following the [Agent Skills](https://agentskills.io/) open specification. They keep the agent’s context efficient through *progressive disclosure*: the agent sees only short names and descriptions until it activates a skill, at which point the full instructions and bundled resources load.
## Overview video
[Agent Skills Overview](https://www.youtube.com/embed/mNqXP4NA_8U)
## Three-stage disclosure
1. **Discovery** (\~100 tokens per skill): names and descriptions appear in the system prompt under ``.
2. **Activation** (under 5,000 tokens): full SKILL.md instructions load when the agent calls `activate_skill`.
3. **Resources**: bundled files mount at `/workspace/.agents/skills/{name}/` and are read with the normal filesystem tools.
This keeps thousands of skills available to an agent without burning context on the ones it doesn’t need.
## Two ways to use skills
| Source | Where they live | When to use |
| ---------------- | ------------------------------------------------------------- | ---------------------------------------------------- |
| Workspace skills | `/.agents/skills/` in the session VFS | Project-specific skills; per-session experimentation |
| Registry skills | Organization-wide, in the [Skills Registry](#skills-registry) | Shared across agents; managed via API |
The built-in `skills` capability handles discovery and activation for both. Registry skills appear in the capability system as virtual capabilities (`skill:{uuid}`) that mount the skill into `/.agents/skills/{name}/`, where the `skills` capability finds it alongside workspace skills.
## Skills Registry
The **Skills Registry** stores skills at the organization level. Registry skills persist across sessions and can be assigned to any agent like any other capability, with ID `skill:{uuid}`. The organization must have the `skills` feature enabled.
### API surface
| Method | Path | Description |
| ------ | ------------------------- | -------------------------------------------- |
| POST | `/v1/skills` | Create from SKILL.md |
| POST | `/v1/skills/upload` | Create from ZIP archive |
| GET | `/v1/skills` | List |
| GET | `/v1/skills/usage` | How many agents and harnesses use each skill |
| GET | `/v1/skills/{id}` | Get metadata |
| GET | `/v1/skills/{id}/content` | Get full content |
| PATCH | `/v1/skills/{id}` | Update |
| DELETE | `/v1/skills/{id}` | Delete |
| POST | `/v1/skills/validate` | Validate without creating |
See the [API reference](https://everruns.com/docs/api/) for request and response schemas.
### ID formats
| Source | ID | Example |
| --------- | ------------------------------------ | -------------------------------------------- |
| Registry | `skill:{uuid}` | `skill:550e8400-e29b-41d4-a716-446655440000` |
| Workspace | Aggregated under `skills` capability | – |
### Dependencies
Registry skills automatically depend on `session_file_system` so bundled resources can be read after activation. You don’t need to add it explicitly.
### Security
* Archive uploads are validated against path traversal, ZIP bombs, and size limits.
* Skill instructions are returned as tool results, not injected into the system prompt, so they cannot bypass capability isolation.
* Skill names are unique per organization.
* Disabled skills are hidden from listings.
## Do something
* [Package and publish an agent skill](https://everruns.com/docs/how-to/package-a-skill/): author a SKILL.md, bundle scripts, and share it through the registry.
## See also
* [Agent Skills capability reference](https://everruns.com/docs/capabilities/agent-skills/): tools, dependencies, config.
* [SKILL.md format spec](https://agentskills.io/): upstream open spec.
---
# Management UI
> Manage agents, sessions, capabilities, settings, files, and event streams through the optional web interface.
Source:
While Everruns is a headless agent platform designed for API-first integration, it provides an optional management UI for administrative tasks and session monitoring.
## Overview
The management UI is a Next.js application that provides:
* Agent management (create, edit, delete)
* Session monitoring and chat interface
* Capabilities browser
* Settings management (LLM providers, personal access tokens, team members)
* Dashboard with system statistics
Access the UI at `http://localhost:9300` when running locally.
## Navigation
The sidebar provides access to main sections:
| Section | Description |
| ------------- | ------------------------------------------------------------- |
| Dashboard | Overview statistics and quick actions |
| Agents | List, create, and manage agents |
| Virtual users | Manage end-user and service runtime accounts |
| Capabilities | Browse available capabilities |
| Settings | Configure providers, personal access tokens, and team members |
## Chat and Threads
**Chat** opens your permanent conversation with the Platform Chat for managing Everruns. Agent testing lives in Playground.
Organization owners and admins can opt into **Chat threads** in **Settings → Features**. It is an adoption feature, disabled by default. With it enabled, the sidebar contains only Chat, and a **Threads** button opens conversations and ongoing work beside the permanent conversation. Create a **New thread**, search existing conversations, or open work from a **View thread** card. The panel can expand and becomes a full-screen drawer on mobile.
Idle conversations remain **Open**. **Resolve thread** puts one away without losing its history; **Reopen thread** lets you continue. Background work shows its actual progress, input requests, and failures. When a thread reports back, the conversation shows one folded line, such as “Releases is done”. Open the line to read the report. The coordinator still receives the full text and answers in the conversation. Disabling the feature restores the existing Chat interface and preserves conversations and their URLs.
## Dashboard
The dashboard provides an at-a-glance view of your system:
* **Stats Cards**: Total agents, active sessions, and other metrics
* **Recent Agents**: Quick access to recently created or updated agents
* **Quick Actions**: Shortcuts to create agents or browse the agent list
## Agents
### Agent List
The agents page displays all agents in a card grid layout.

Each card shows:
* Agent name and status badge (active/inactive)
* Truncated ID
* Description preview
* Enabled capabilities with icons
* Tags
* Creation date
* Edit button
Click a card to view the agent details, or click the edit icon to modify the agent.
### Agent Page
The agent page reads and edits an agent in one layout. The system prompt fills the wide left pane; a narrow column on the right holds the settings:
* **Harness**, **Capabilities** (compact chips, in precedence order), **Default model**, and **Tags**
* **Updated**, read-only
* **More**: one row each for Branding, MCP servers, Credentials, Service account, Network access, Sandbox, Token usage, and Health check. **Files** is included when the agent has a filesystem (from its capabilities, a dependency, or the harness) or already has starter files. Each row shows its current value and opens a side sheet. Service account saves immediately.
Tabs: **Agent**, **Preview**, **Integrations** (channels and triggers), **Stats**, and **Sessions**. Preview’s Files card follows the same filesystem rule.
#### Avatar
**Branding** starts with the agent’s avatar. Drop a PNG, JPEG, GIF, or WebP image (at most 10 MB) on the avatar, or choose one, then position and zoom the square crop. **Save avatar** uploads it right away, without **Save changes**. The server renders the square once as square and circular PNG presets of 32, 64, 128, 256, and 512 px. Use **Choose preset** to browse 25 image-only avatar tiles across Watchers, Familiars, Totems, Glyphs, and Bloom. Search by name, role, animal, color, or style, and optionally filter by family. Select a card to preview its circular shape, then **Use avatar** to save immediately. Names and roles describe the artwork; they do not configure the agent. You can replace any preset with an uploaded image or remove it.
The avatar shows on agent cards and the agent page, in the A2A Agent Card (`iconUrl`), on the MCP agent card, and as the icon of Slack apps created with one-click setup.
Through the API, `PUT /v1/agents/{agent_id}/avatar` takes the image as the multipart field `file`, and `DELETE` removes it. The agent’s `avatar` field lists the URLs. Preset URLs look like `/v1/avatars/{avatar_id}/circle-64.png`; they are public and cached as immutable, and a new upload gets a new `avatar_id`. `GET /v1/avatar-presets` lists the curated catalog; `PUT /v1/agents/{agent_id}/avatar/preset` selects one with `{"preset_id": "familiars-patch"}`. `GET` on that selection endpoint reports the current preset ID, or `null` for a custom image or no avatar. Preset selection uses the same agent-management permission as upload.
Header actions:
* **Edit**: switch the page into edit mode. The prompt becomes an editor and the settings take input; **Save changes** sends everything at once and **Discard** drops the draft. Changes apply to new sessions only.
* **More actions**: Sync from GitHub, Copy, Export, History, Manager notes, and Archive (or Delete, for an archived agent)
* **Test in Playground**: open Playground setup with this Agent selected and normal execution selected. It does not start a session until you confirm setup. Use the existing **Dry run** checkbox there to simulate actions; tools that only read still run for real.
In Playground setup, search **Talk as** for an end user by name, including users beyond the first page of results. Owners and admins can choose another active end user; other members use their own identity. Search the Agent/harness picker by name or identifier. These choices remain fixed for the chat after it starts.
Use **More > Sandbox** to add named Bashkit or Daytona Sandbox Template bindings. Then select the Agent and Sandbox when starting a **New Playground chat**. Personal Chats always use the managed Platform Chat and do not expose a sandbox selector. See \[Sandbox Templates] (/features/sandbox-templates/) for recovery and lifecycle behavior.
## Sessions
### Session View
Each session has three tabs:
#### Chat Tab
The primary interface for viewing and participating in conversations:
* Message history with user messages (dark bubbles) and agent responses
* Tool call visualization with expandable details
* Tool results displayed inline
* Message input with keyboard shortcuts (Enter to send, Shift+Enter for newline)
* Reasoning effort selector (for models that support extended thinking: Anthropic Claude, OpenAI GPT-5.x, o-series)
#### File System Tab
Browse and manage files associated with the session’s sandboxed environment.
#### Events Tab
View raw session events for debugging:
* Sequence number
* Event type (input.message, output.message.completed, tool.completed, etc.)
* Timestamp
* JSON data payload
### Session Status
Sessions display their current status:
| Status | Badge | Description |
| ------- | --------- | ------------------------------ |
| started | Outline | Newly created, no messages yet |
| idle | Secondary | Ready for input |
| active | Primary | Currently processing |
## Capabilities
The capabilities page lists all available functionality modules:
* **Summary Panel**: Counts by status (available, coming soon, deprecated)
* **Category Tags**: Filter by capability type
* **Capability Cards**: Click to view details including tools and configuration
Each capability card shows:
* Icon and name
* Identifier (for API use)
* Status badge
* Description
* Category tag
## Models
**Registries → Models** has three tabs:
* **Models**: every model from every connected provider, filtered by service, provider and status. Enabled models are shown by default. Choose models for a provider in bulk, and review models a sync discovered.
* **Providers**: connected provider accounts. Connect a provider, paste or rotate its key, sync its models and open its settings. Several providers of one kind can be connected under different names; model pickers show “Model (Provider name)”.
* **Defaults**: the organization’s default model, Small model for lightweight work, default decision model, system decisions source, and default provider for realtime, embeddings, images and rerank.
Connecting, rotating and deleting providers and enabling models need the organization models permission (`org:models:manage`, admins by default). Other members see the page read-only.
## Settings
### Personal access tokens
Manage personal access tokens for programmatic access. Tokens are tied to your user account (not an organization) and inherit access to every organization available to your account:
* Create new personal access tokens, choosing their access
* View existing tokens (values hidden) and each token’s access
* Revoke tokens
#### Token access
Every token has one access level, chosen when you create it. It cannot be changed later; create a new token instead.
| Access | API value | What it can do |
| ------------------------- | ---------- | -------------------------------------------------------------------- |
| **Full access** (default) | `["*"]` | Everything your account can do, in every organization you belong to. |
| **Read-only** | `["read"]` | Read access to all your organizations, and nothing else. |
A read-only token can list and view agents, sessions, messages, files and other resources in every organization you belong to, run read-only CLI commands, and use the read-only tools on the MCP endpoint. Treat it like a password: if it leaks, everything you can read is exposed.
A read-only token can never:
* create, change, run, or delete anything (including sending messages to agents or calling an agent through an Agent API channel);
* create, list, or revoke personal access tokens, or create agent keys or share links;
* change organizations, members, or invitations, or connect providers and other accounts;
* download the full export of your account data (`GET /v1/users/me/export`).
A request outside the token’s access fails with `403 Forbidden`, a `WWW-Authenticate: Bearer error="insufficient_scope"` header, and a problem body with `"code": "insufficient_scope"`. Use a full-access token for that operation. `GET /v1/auth/me` reports the scope of the token you called it with as `token_scope` (`"*"` or `"read"`; `null` when you are signed in another way).
Tokens are created from a signed-in session; a personal access token cannot create another one. Through the API, pass `scopes`:
```bash
curl -X POST "$EVERRUNS_URL/api/v1/auth/personal-access-tokens" \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "ci-reader", "scopes": ["read"], "expires_in_days": 90}'
```
Omitting `scopes` creates a full-access token. Any value other than `["*"]` or `["read"]` is rejected with `400` and `"code": "unsupported_pat_scopes"`.
### Members
View and manage team members (when authentication is enabled).
## Virtual users and personal settings
Open **Virtual users** to create or manage organization-scoped accounts. Choose **End user** for a person using agents or **Service** for an agent account. The detail page has **Overview**, **Connections**, **Linked identities**, and **Sessions** tabs. Usage is fixed at creation. On an agent page, **More → Service account** selects the account.
**Settings → Account** edits your Everruns management profile. **Profile** edits the name, locale, and timezone of your current organization’s default virtual user. **Connections** holds that user’s provider sign-ins and credentials. **MCP servers** holds the servers you add for yourself. **AI clients** lists the apps you approved to use Everruns as you. Switching organizations selects that organization’s runtime account. **Team members** remains management membership administration. `/settings/connections` opens Connections.
End-user connections are private to their owner. Organization management permissions do not grant access to another end user’s credentials. Service account connections require management permissions. Saved credentials are encrypted and never returned by the API.
When upgrading an account that belonged to several organizations, old connections require an explicit destination. Select the destination from **Connections** and move each pending connection once. A destination that already has that provider is rejected rather than overwritten. Accounts with one organization migrate automatically.
Operators upgrading an existing installation should follow the [virtual-user cutover runbook](https://everruns.com/docs/sre/runbooks/virtual-user-cutover/).
---
# User MCP Servers
> People add MCP servers for themselves, sign in once, and agents that opt in get those tools while that person is chatting. Nobody else can use them.
Source:
A **user MCP server** belongs to one person. You add it in your settings, sign in to it once, and every agent that uses your MCP servers gets its tools while you chat with that agent. Other people never see it, and agents never use it when you are not the one talking to them.
This is the third place an MCP server can live:
| Where | Who adds it | Who it signs in as |
| -------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Organization catalog | An admin, under **Settings > Organization > MCP catalog** | Whatever the agent attaching it chooses |
| Agent | Whoever edits the agent | The agent’s service account, the user, or the user with the agent as a fallback, per server ([acts as](https://everruns.com/docs/features/mcp/#who-a-server-acts-as)) |
| **User** | You, for yourself | Always you |

## Add a server
Open **Settings > MCP servers** and choose **Add server**.
* **From catalog**: pick a server your organization already registered, such as Linear. It uses the catalog entry’s OAuth client, so signing in to it here and signing in from an agent that attaches the same entry share one login.
* **By URL**: give it a name and an HTTPS URL, then choose how it signs in:
* **OAuth**: choose **Connect** after adding it. Everruns discovers the server’s OAuth settings and registers a client automatically.
* **API key**: the key is encrypted and never shown again. You can replace it, not read it.
* **None**: for servers that need no sign-in.
The name is the tool prefix agents see (`notes__search`), and it only has to be unique among your own servers: two people can each have a `notes` server. Each person can keep up to 50 servers.
You do not have to add a catalog server before connecting it. Connecting one as yourself anywhere, in these settings, from an agent’s MCP servers sheet, or from a Connect card in a chat, adds it to **My MCP servers** if it is not there yet, so every agent that uses your MCP servers gets it too. Connecting again never adds a second entry; if the name is already used by another of your servers, the new entry is called `-2` (or the next free number).
Turn a server off to hide it from agents without losing your sign-in. Remove it to delete both the server and your sign-in. For a catalog server that sign-in is also the one agents acting as you use for the same catalog entry, so they ask you to connect again. Signing out from the connection alone keeps the server on your list, waiting for you to connect again.
## Let an agent use them
Add the **User MCP Servers** capability (`user_mcp`) to the agent. With its `use` setting on (the default), each turn adds the enabled servers of the person who sent the message. [Platform Chat](https://everruns.com/docs/built-ins/harnesses/platform-chat/) has it on.
The agent gets nothing from it when:
* **nobody is chatting**: a schedule, trigger or other unattended run has no person to act for;
* **more than one person is in the conversation**: in a shared session, one person’s tools would otherwise show up in everyone’s turns;
* **the agent already has a server with that name**: agent and capability servers always win, so an agent’s behaviour cannot be changed by a person adding a server with the same name;
* the server is turned off, or a catalog entry it came from was archived.
An OAuth server works for agents once you sign in to it; until then your list shows it as **Needs sign-in**.
The agent’s **MCP servers** sheet shows a **User servers of the person chatting** group with these settings, and lists any of your own servers the agent skips because one of its servers has the same name.
## Add and connect from chat
With the capability’s `manage` setting on, you can ask the agent instead of opening Settings, for example “add Linear and connect it”. The agent:
1. adds the server from your organization’s catalog, asking you to approve first when the agent’s [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/) mode is `normal` or `protective`;
2. shows a **Connect** card, where you sign in in your own browser; the agent never sees your credentials;
3. can use the server’s tools from your next message.
The **Connect** card also works for the agent’s own servers: an agent with a server that acts as you (`user` or `user_or_service`) can show it without `manage`. A server that acts as the agent sends you to the agent’s **MCP servers** sheet instead, where only someone who manages MCP servers can sign the agent in.
It can also list, turn off, turn on (under the same approval policy) and remove your servers. It can add a server by URL only when the agent’s `allow_custom_urls` setting is on, and never with an API key or headers. These tools only ever change your own list, need you to be the one chatting, and do not work in conversations with several people. Platform Chat has `manage` on. Management adds no hard approval gate when Tool Approval is absent or set to `off`. Signing in through the Connect card remains a separate authorization step.
## API
The same operations are available over the API, for yourself (`me`) or, with virtual-user management permission, for an account you manage:
| Method | Path | Purpose |
| -------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `GET` | `/v1/virtual-users/{id}/mcp-servers` | List your servers and their sign-in state |
| `POST` | `/v1/virtual-users/{id}/mcp-servers` | Add a server (`{"catalog":"linear"}` or `{"name","url","auth_mode"}`) |
| `GET` | `/v1/virtual-users/{id}/mcp-servers/{server_id}` | Read one server |
| `PATCH` | `/v1/virtual-users/{id}/mcp-servers/{server_id}` | Rename, turn on or off, or replace the API key |
| `DELETE` | `/v1/virtual-users/{id}/mcp-servers/{server_id}` | Remove the server and your sign-in |
| `GET` | `/v1/virtual-users/{id}/mcp-servers/{server_id}/tools` | List a custom server’s tools, asked live, with your [risk labels](https://everruns.com/docs/features/mcp/#which-tools-ask-before-they-run) |
| `PUT` | `/v1/virtual-users/{id}/mcp-servers/{server_id}/tools/{tool}/label` | Label a custom server’s tool `read_only` or `changes`, or `null` to clear |
Sign in with `GET /v1/virtual-users/{id}/connections/{provider}/authorize`, where `provider` is the `connection.provider` value a server reports. The URL of a server cannot change; remove it and add it again instead.
```bash
curl -X POST "$EVERRUNS_URL/v1/virtual-users/me/mcp-servers" \
-H "Authorization: Bearer $EVERRUNS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"notes","url":"https://notes.example.com/mcp","auth_mode":"oauth"}'
```
## Security
* Another person’s server answers `404` on every route, and only the owner can start its sign-in. An agent’s own account can never hold a grant for it.
* Custom URLs go through the same address checks as catalog servers, so a user server cannot point at private or internal addresses.
* API keys and header values are write-only.
* A user server never appears in the organization catalog and cannot be attached to an agent by name.
---
# Voice
> Let people talk to an Everruns agent and hear its answers through a voice channel, with the same model, tools and instructions the agent uses for text.
Source:
A **voice channel** lets people talk to an Agent and hear its answers. Voice is a [channel](https://everruns.com/docs/features/channels/) like Slack or AG-UI, so the same Agent can be reachable by text and by voice at once, with one set of instructions, tools and memory. A call and a text chat on the same session share one transcript, so a conversation can move between typing and talking.
> **Status:** Voice is an Adoption feature. An organization administrator turns on **Voice** in **Settings → Features**. Speech uses an OpenAI provider.
## How a call works
A voice channel runs in **delegated** mode. A speech-to-speech model (`gpt-realtime-2` by default) only listens and speaks; the Agent, with its own model and tools, writes every answer.
1. The browser connects to the speech provider over WebRTC. Everruns places the call server-side, so the provider key never reaches the browser.
2. When the caller finishes talking, their words become a user message on the session, marked with `metadata.source = "voice"`.
3. The Agent runs a normal turn. Its answer is spoken sentence by sentence as it streams, so speech starts before the answer is complete.
4. If the turn says nothing for a moment, a short filler line (“One moment.”) is spoken so the line does not feel dead.
5. When the caller talks over the answer, speech stops at once. The transcript records what the caller actually heard.
Tools, approvals, budgets and audit apply to a voice turn exactly as to a text turn. Raw audio is never stored; transcripts are kept like chat messages.
## Create a voice channel
1. Open the Agent and select the **Integrations** tab.
2. Select **Add channel** and choose **Voice**.
3. Pick a voice and, optionally, a greeting, a language hint and a speaking style, then select **Save channel**.
4. Select **Talk to this channel** to test it from the browser.
Over the API, create it like any channel:
```bash
curl -X POST "$EVERRUNS_API/v1/agents/$AGENT_ID/channels" \
-H "Authorization: Bearer $EVERRUNS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel_type": "voice",
"channel_config": {
"voice": "marin",
"greeting": "Hi, you are talking to an AI assistant. How can I help?"
}
}'
```
## Settings
| Field | Default | What it does |
| --------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `mode` | `delegated` | How listening, thinking and speaking are split. The server runs `delegated` channels; `cascaded` (separate speech-to-text and text-to-speech) is available in the [Framework and serve](https://everruns.com/docs/framework/voice/). |
| `model` | `gpt-realtime-2` | Speech model that listens and speaks. |
| `transcription_model` | `gpt-4o-transcribe` | Speech-to-text model for the caller. |
| `voice` | `marin` | Provider voice. |
| `language` | none | Language hint for transcribing the caller, such as `en`. |
| `greeting` | none | Spoken when the call connects. Say that the caller is talking to an AI. |
| `turn_detection` | `server_vad` | `server_vad` ends a turn on a pause; `semantic_vad` waits for a finished thought. |
| `interruption` | `steer` | What talking over the answer does to the running turn: `steer` keeps it running and the next utterance steers it, `cancel` stops it. |
| `filler` | `One moment.` | Line spoken while the Agent works. |
| `filler_after_ms` | `1500` | Silence before the filler is spoken. `0` turns fillers off. |
| `speaking_style` | none | Delivery instructions for the speech model, such as pace or tone. Business rules belong in the Agent. |
The speech provider is the organization’s default for realtime speech. Pass `provider_id` when placing a call to use another configured OpenAI provider.
## Place a call from your own app
Your app creates a WebRTC offer with the microphone track, sends it to Everruns, and applies the SDP answer it gets back.
| Method | Route | Use |
| ------ | ----------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `POST` | `/v1/agents/{agent_id}/channels/{channel_id}/voice/calls` | Call a voice channel. Starts a new session unless `session_id` is given. |
| `POST` | `/v1/sessions/{session_id}/voice/calls` | Talk on an existing session. Uses the Agent’s voice channel, or `channel_id` if given. |
| `POST` | `/v1/sessions/{session_id}/voice/{voice_connection_id}/end` | Hang up. |
```js
const pc = new RTCPeerConnection();
const mic = await navigator.mediaDevices.getUserMedia({ audio: true });
pc.addTrack(mic.getTracks()[0]);
const audio = new Audio();
audio.autoplay = true;
pc.ontrack = (event) => (audio.srcObject = event.streams[0]);
pc.createDataChannel("oai-events");
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const response = await fetch(
`${api}/v1/agents/${agentId}/channels/${channelId}/voice/calls`,
{
method: "POST",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({ sdp: offer.sdp }),
},
);
const { session, voice } = await response.json();
await pc.setRemoteDescription({ type: "answer", sdp: voice.answer_sdp });
```
The response carries the session, so your app can show the transcript by [streaming its events](https://everruns.com/docs/how-to/stream-events/). A call lasts at most one hour.
## Events
A call emits `voice.session.started`, `voice.session.ended` and `voice.session.failed`, transcript events for the caller (`voice.input_transcript.*`) and for what was spoken (`voice.output_transcript.*`), and `voice.output.interrupted` when the caller talks over an answer. See the [event reference](https://everruns.com/docs/event-reference/).
## See also
* [Channels](https://everruns.com/docs/features/channels/): the channel model, lifecycle and publishing.
* [Stream events](https://everruns.com/docs/how-to/stream-events/): follow a session’s transcript live.
* [Build a voice agent](https://everruns.com/docs/framework/voice/): the same voice channel in your own Rust host or a serve app.
---
# Work Reports
> See what an agent's turn is doing right now and what it got done, as one record built from the session's events and shown in chat, the sessions list and the API.
Source:
A **work report** answers two questions about a session: what is the agent doing right now, and what did it get done. Everruns builds it from events the session already records, so every agent has one without prompt changes or an extra model call.
The same report appears in three places:
* **Chat** shows the plan the agent shared (“Step 2 of 4”) above the work log while the turn runs, and a result box when it ends: “Done · 2 files changed”, the agent’s own summary, and links to what it produced.
* **The sessions list, the thread board and run cards** show the headline of a session that is still working, such as “Running `cargo test` 7 of 12”.
* **The API** returns it as JSON.
## What a report says
| Field | What it holds |
| ----------- | ------------------------------------------------------------------------------------------------------------ |
| `state` | `working`, `needs_you`, `done`, `failed` or `stopped` |
| `headline` | One line: the running step, what the work waits on, or the result (“Done in 13s · 2 files changed”) |
| `plan` | The checklist the agent shared, with `done`, `total` and the current item. Absent when the agent shared none |
| `steps` | The turn’s tool calls, newest last, each labelled by what it did (“Ran `cargo test`, 3 of 12 tests failed”) |
| `attention` | What the agent waits on: an approval, a question or another action |
| `result` | Facts measured from the run (files changed, tasks started, issues), a summary, checks, links |
Progress is never invented. “Step N of M” appears only when the agent shared a plan with `write_todos`, `update_checklist` or `update_status`, and a step shows a count only when its tool reported one. Without a plan the report lists steps and draws no bar.
The summary comes from the agent when it called [`report_done`](https://everruns.com/docs/capabilities/task-management/#report_done), and from the first sentence of its reply otherwise. `report_done` also records how the agent checked the work and whether it could finish.
## Read a report
```bash
curl "$EVERRUNS_API/v1/sessions/$SESSION_ID/work" \
-H "Authorization: Bearer $EVERRUNS_API_KEY"
```
```json
{
"report": {
"turn_id": "turn_01933b5a00007000800000000000001",
"state": "done",
"headline": "Done in 2m 13s · 2 files changed",
"steps": [
{
"id": "call_01",
"tool_name": "bash",
"label": "Ran `cargo test`, 12 tests passed",
"state": "done"
}
],
"result": {
"summary": "Fixed the proration rounding and added a test.",
"summary_source": "agent",
"checks": "Ran the 12 billing tests",
"facts": [{ "kind": "files_changed", "value": 2 }]
}
}
}
```
The route returns the latest turn’s report; pass `?turn_id=` for an earlier one. A session that has not run a turn returns `{"report": null}`. From the CLI, run `everruns sessions work get session_01h9`.
## In the Agent Execution API
`GET /v1/channels/{channel_id}/sessions/{session_id}` on an [Agent API](https://everruns.com/docs/features/agent-api/) channel includes the latest turn’s report as `work`. What it shows follows the channel’s visibility:
* `full`: the whole report.
* `activity` and `messages`: the public view. It keeps the state, plan counts, facts, the agent’s summary and checks, and links. It leaves out step labels, plan item titles, approval prompts and file paths, because step labels are tool narration and can quote tool arguments. The headline becomes “Step 3 of 4”, “Working” or “Needs approval” while the work runs.
## Reporting progress from your own tools
A tool that knows how far along it is can send a count with its progress message, and the report shows it on the running step. In the Framework, a tool calls `ctx.progress_of("Rendering frames", 7, 12).await` on its `ToolCallContext`.
---
# Everruns Framework
> Build and run agents inside a Rust application with the application-facing everruns crate.
Source:
The **Everruns Framework** is the application-facing [`everruns`](https://docs.rs/everruns) crate. Use it to describe agents, attach models and tools, run multi-turn sessions, observe events, and embed agent execution directly in a Rust process.
```rust
use everruns::{Agent, Engine, OpenAI};
let agent = Agent::builder()
.instructions("Answer in one short sentence.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.build()?;
let engine = Engine::new();
let turn = engine.create(agent).send_and_wait("Say hello.").await?;
println!("{}", turn.response);
```
No database, server or worker is required: an agent runs inside your process. A model provider is: pick one from [Supported providers](https://everruns.com/docs/framework/models-and-providers/#supported-providers), or use the [test simulator](https://everruns.com/docs/framework/testing-and-simulation/) when writing tests.
## Choose the right surface
| Surface | Use it for |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Framework** | Rust applications that build and run agents in process through `everruns` |
| **Advanced host crates** | Low-level execution-host composition through `everruns-core` (`host` feature) and focused siblings |
| **SDKs** | Remote clients that call a running Everruns server |
| **Platform (self-hosted)** | The control plane, server, workers, UI, and durable deployment you run yourself, for example with [Docker Compose](https://everruns.com/docs/getting-started/docker-compose/) |
| **Everruns Cloud** | The Platform operated for you at [app.everruns.com](https://app.everruns.com), with a built-in model provider and a starter credit; bring-your-own keys are optional. See the [Everruns Cloud quickstart](https://everruns.com/docs/getting-started/cloud/) |
Normal library users should start with the Framework. Hosts that must replace storage or orchestration cross into [custom backends](https://everruns.com/docs/framework/custom-backends/).
## Start here
* [Quickstart](https://everruns.com/docs/framework/quickstart/), install the crate and run one turn against a live model provider.
* [Architecture](https://everruns.com/docs/framework/architecture/), understand Agent, Engine, Session, and the shared immediate/durable execution kernel.
* [Upgrade notes](https://everruns.com/docs/framework/upgrade-notes/), the code changes each breaking release needs.
## Core APIs
* [Agents and tools](https://everruns.com/docs/framework/agents/), instructions, files, workspaces, MCP, plugins, context inspection, and typed function tools through `everruns::tool`.
* [Models and providers](https://everruns.com/docs/framework/models-and-providers/), the model/provider split, every driver that ships today, each driver’s environment variables, provider model catalogs, and custom providers.
* [Direct calls and decisions](https://everruns.com/docs/framework/direct-model-calls/), one prompt and one answer without an agent, or a calibrated number rather than prose.
* [Sessions](https://everruns.com/docs/framework/sessions/), independent multi-turn conversations, bounded history, typed resume, and engine-lifetime or crash-durable persistence.
* [Events and cancellation](https://everruns.com/docs/framework/events-and-cancellation/), observe a live turn, stop work cooperatively, and record bounded canonical event envelopes.
* [Observability](https://everruns.com/docs/framework/observability/), export every session to OpenTelemetry or Braintrust, or register your own event listeners.
* [Workspaces and environments](https://everruns.com/docs/framework/workspaces-and-environments/), bind sessions to isolated or shared heads and configure read and write scopes with secure defaults.
* [Lifecycle hooks](https://everruns.com/docs/framework/lifecycle-hooks/), run awaited application behavior at execution boundaries.
* [Answer agent questions](https://everruns.com/docs/framework/ask-user/), implement `AskUser` so your application answers the agent’s structured questions.
* [Session work and wakes](https://everruns.com/docs/framework/background-work/), immediate and scheduled work with explicit delivery and restart semantics.
* [Coordinator agents](https://everruns.com/docs/framework/coordination/), one session that hands work to threads and hears back from them.
## Extend
* [Capabilities](https://everruns.com/docs/framework/advanced-capabilities/), configure the optional standard policy bundle and open references, or package typed tools with stable metadata and lifecycle context.
* [Capability integrations](https://everruns.com/docs/framework/capability-integrations/), opt into filesystem, shell, web, Lua, and MCP implementation boundaries.
* [Portable and hosted capabilities](https://everruns.com/docs/framework/capability-boundaries/), understand the Framework/Platform implementation boundary.
* [Agent blueprints](https://everruns.com/docs/framework/agent-blueprints/), contribute a code-defined specialist agent from a capability.
* [Host services](https://everruns.com/docs/framework/host-services/), compose a host’s own capabilities, provider resolution, retry budgets, MCP credentials and observers into Engine sessions.
* [Session controls](https://everruns.com/docs/framework/session-controls/), run slash commands, hot-install capabilities, switch model, effort and MCP servers, and query context on a live session.
* [Custom backends](https://everruns.com/docs/framework/custom-backends/), cross into low-level host composition deliberately.
* [Testing and simulation](https://everruns.com/docs/framework/testing-and-simulation/), deterministic tests without credentials.
* [Runnable examples](https://everruns.com/docs/framework/examples/), complete programs maintained with the crate.
## Expose and deploy
* [Deploy a Framework app](https://everruns.com/docs/framework/deployment/), ship the binary or a container with a persistent data directory and environment-provided keys.
* [Move to Platform or Cloud](https://everruns.com/docs/framework/moving-to-platform/), what carries over to a hosted agent and what has to be rebuilt.
* [Serve](https://everruns.com/docs/framework/serve/) (experimental), attribute macros, file-layout discovery and a manifest, served over the Everruns server `/v1` API.
* [Tutorial: Serve an Agent and Call It from Code](https://everruns.com/docs/framework/serve-tutorial/) (experimental), build a serve app, protect it with an API key, and call it with the SDK’s `AgentClient`.
* [Serve AG-UI](https://everruns.com/docs/framework/ag-ui/), stream a session to CopilotKit or any AG-UI 1.0 client from your own HTTP server.
* [A2A](https://everruns.com/docs/framework/a2a/), serve Framework agents to other agents over A2A 1.0, and delegate work to remote A2A agents.
* [Serve on AgentCore](https://everruns.com/docs/framework/serve-agentcore/) (experimental), deploy a serve app to Amazon Bedrock AgentCore Runtime.
* [Serve on celld](https://everruns.com/docs/framework/serve-celld/) (experimental), run a serve app durably on celld, self-hosted Durable Objects.
---
# A2A
> Serve Framework agents to other agents over A2A 1.0, and let a Framework agent delegate work to remote A2A agents.
Source:
[A2A](https://a2a-protocol.org) is an open protocol for agents to call each other over HTTP: a JSON-RPC endpoint plus a public Agent Card that says what the agent is and how to reach it. A Framework app can use it in both directions:
* **Serve**: other agents, the official `a2a` CLI, or the A2A Inspector call your agents. This comes from the `a2a` feature of [`everruns-serve`](https://everruns.com/docs/framework/serve/).
* **Call**: your agent hands work to a remote A2A agent with `spawn_agent`. This comes from the `a2a` feature of `everruns` and the `a2a_agent_delegation` capability.
Both speak A2A 1.0. The routes match an Everruns [A2A endpoint](https://everruns.com/docs/features/a2a/), so a caller can move between a serve app and Everruns by changing the base URL.
## Serve your agents over A2A
Turn on the feature:
```toml
[dependencies]
everruns-serve = { version = "*", features = ["a2a"] }
```
Every top-level agent in the app is then served at:
| Route | What it is |
| ---------------------------------------------------------- | -------------------------------------------------------------- |
| `POST /v1/channels/{agent}/a2a` | A2A 1.0 JSON-RPC. Requests need the `A2A-Version: 1.0` header. |
| `GET /v1/channels/{agent}/a2a/.well-known/agent-card.json` | The Agent Card, built from the agent’s name and `description`. |
Nothing else changes in the agent. A `description` is worth setting, because it is what other agents read on the card:
```rust
use serve::prelude::*;
/// Researches a topic and answers with short factual notes.
#[agent]
fn researcher() -> Agent {
Agent::builder()
.model("openai/gpt-6.1-sol")
.description("Researches a topic and answers with short factual notes.")
.instructions(md!("instructions.md"))
.build()
}
```
Try it with the `a2a` CLI against a dev server:
```bash
CARD=http://localhost:3000/v1/channels/researcher/a2a/.well-known/agent-card.json
a2a card get -a "$CARD"
a2a send -a "$CARD" "Tide pools"
a2a send -a "$CARD" --stream "Coral reefs"
a2a task list -a "$CARD"
```
Pass the full card URL: the card lives under the agent’s route, not at the server root.
### How A2A maps onto serve
* Each A2A `contextId` is one serve session. A follow-up message with the same `contextId` continues the conversation, and the session survives a restart.
* Each A2A task is one turn. A task goes `working`, then delivers the final reply as a `response` artifact, then `completed` (or `failed`).
* `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`, and `SubscribeToTask` are supported.
* A pending tool approval or `ask_user` question keeps the task `working` until it is answered through serve’s `/v1` routes.
### Limits
* Only A2A 1.0 is served. Requests without `A2A-Version: 1.0` are rejected.
* Tasks are kept in memory: after a restart, old task ids are unknown, while their contexts continue.
* The endpoint takes the same credentials as the other serve routes ([Authentication](https://everruns.com/docs/framework/serve/#authentication)). With auth configured, the card’s `securitySchemes` and `securityRequirements` list one scheme per method (`apiKey` for static keys, `oidc` for an OpenID Connect issuer, `oauth2Bearer` for introspection), any one of which is enough; with none, the card lists no scheme and the endpoint is open.
* Push notifications are not offered. Use streaming or `GetTask`.
For a hosted endpoint with API keys, push notifications, and A2A 0.3 support, use an Everruns [A2A endpoint](https://everruns.com/docs/features/a2a/).
## Call remote A2A agents
Turn on the feature and add the capability, listing the agents your agent may call:
```toml
[dependencies]
everruns = { version = "*", features = ["a2a", "local", "openai"] }
```
```rust
use everruns::{Agent, CapabilityRef, Engine, LocalConfig, OpenAI};
use serde_json::json;
let agent = Agent::builder()
.name("writer")
.instructions(
"Before writing, ask the `researcher` agent for notes with `spawn_agent` \
(target type `external_a2a`, id `researcher`, mode `foreground`). \
Then write one paragraph from its notes.",
)
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.capability(CapabilityRef::new("a2a_agent_delegation").config(json!({
"agents": [{
"id": "researcher",
"name": "Researcher",
"description": "Researches a topic and answers with short factual notes.",
"base_url": "https://agents.example.com/v1/channels/researcher/a2a"
}]
})))
// Delegated runs are recorded in session storage.
.local(LocalConfig::new("./writer-state"))
.build()?;
let session = Engine::new().create(agent);
```
The model sees one `spawn_agent` tool with an `external_a2a` target. It can only reach the agents in `agents`; it never supplies a URL.
* `base_url` is where the Agent Card is resolved (`{base_url}/.well-known/agent-card.json`).
* `mode: "foreground"` waits for the remote task and returns its reply as the tool result. `mode: "background"` returns a `task_id` at once and wakes the session when the remote task finishes.
* `result_schema` on `spawn_agent` requires a structured result and validates the remote agent’s first data part against it.
* URLs are checked before each call: localhost, private ranges, and metadata addresses are refused, resolved addresses are pinned, and redirects are not followed. Set `"allow_local_urls": true` on an agent entry only while developing against a server on your machine under `DEPLOYMENT_GRADE=dev`.
The remote agent can be anything that speaks A2A 1.0: a serve app, an Everruns A2A endpoint, or another vendor’s agent.
## Run the example
[A2A example](https://github.com/everruns/everruns/tree/main/examples/serve/a2a) puts both halves together. `researcher` is a serve app served over A2A, and `writer` is an `everruns` agent that delegates research to it and then drafts. It runs offline with scripted models, and on OpenAI when `OPENAI_API_KEY` is set:
```bash
cargo run -p serve-example-a2a --bin researcher # serves on :3000
cargo run -p serve-example-a2a --bin writer -- "tide pools" # in another shell
```
The writer’s `spawn_agent` call goes over real A2A even offline, so the example is also a quick way to check an A2A setup end to end.
## See also
* [Serve](https://everruns.com/docs/framework/serve/#a2a), the serve routes and configuration.
* [A2A](https://everruns.com/docs/features/a2a/), the Everruns A2A endpoint and the hosted delegation capability.
* [Serve AG-UI](https://everruns.com/docs/framework/ag-ui/), the protocol for agent-to-UI rather than agent-to-agent.
---
# Configure and author capabilities
> Use one open AgentBuilder capability entrypoint for typed built-ins, dynamic references, and code-defined packages.
Source:
Every agent capability enters through `AgentBuilder::capability`. The method accepts the public, non-sealed `IntoCapability` contract, so Framework built-ins and third-party packages compose without adding a method or enum variant to `AgentBuilder`.
Cargo features determine which environment-backed implementations a Framework binary contains. See [Capability integrations](https://everruns.com/docs/framework/capability-integrations/) for filesystem, Bashkit, web-fetch, Lua, and MCP boundaries; this page covers agent-level configuration and authoring after an implementation is available.
## Configure capabilities
Use typed values when the Framework exposes a stable configuration, a `capability::Definition` for application code, and `CapabilityRef` when the ID and JSON arrive dynamically:
```rust
use everruns::{
Agent, CapabilityRef, CompactionConfig, OpenAI, ToolSearch,
};
use serde_json::json;
let weather_definition = build_weather_capability();
let agent = Agent::builder()
.instructions("Use configured capabilities when relevant.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.capability(CompactionConfig::new().budget_percent(0.85))
.capability(ToolSearch::automatic())
.capability(weather_definition)
.capability(
CapabilityRef::new("vendor.custom")
.config(json!({ "mode": "database-driven" })),
)
.build()?;
```
`ToolSearch::automatic` uses hosted deferred loading on supported models and the existing provider-neutral client-side implementation everywhere else. Its optional threshold and never-defer allowlist map to the built-in’s real configuration; there are no provider fields on the Framework value.
`CapabilityRef` is the explicit escape hatch for database, plugin, or catalog configuration. Its ID stays open. An unknown ID is retained as a reference but contributes nothing until the selected host or plugin provides that implementation. This is not a function tool: ordinary functions remain on `AgentBuilder::tool` and `#[everruns::tool]`.
The same rule applies to Everruns Platform capability IDs. The default Framework registry does not advertise or execute hosted knowledge, delegation, task, hook, or management capabilities. See [portable and hosted capabilities](https://everruns.com/docs/framework/capability-boundaries/).
JSON capability config is not a credential store. Framework debug output redacts it, but a host may persist or inspect it; pass a provider-owned secret handle rather than API keys or tokens.
Conversion is infallible. `AgentBuilder::build` validates ID syntax and the JSON object boundary, runs known built-in and declarative/plugin validators, and rejects duplicate IDs after built-in alias resolution. A code implementation cannot shadow a built-in or be paired with a second reference of the same ID; registrations never use last-write-wins behavior.
Third-party typed values implement `IntoCapability` using only `everruns`:
```rust
use everruns::{CapabilityRef, CapabilitySpec, IntoCapability};
struct VendorSearch {
index: String,
}
impl IntoCapability for VendorSearch {
fn into_capability(self) -> CapabilitySpec {
CapabilityRef::new("vendor.search")
.config(serde_json::json!({ "index": self.index }))
.into()
}
}
```
No `everruns-core`, registry, store, or host dependency is needed.
## Choose the standard policy bundle
The Framework’s default `builtins` feature links `everruns-core` (`builtins` feature), the backend-neutral implementation bundle for compaction, tool search, budgeting, loop/progress safeguards, prompt caching, tool-call repair, output handling, and guardrails. Linking the package has no registration side effect: each host constructs its registry explicitly, so a custom registry cannot be changed by dependency order.
Applications that want only the open Framework contracts can disable default features and add the integrations they need. The policy bundle owns no network client, process runner, interpreter, database, or hosted service. Output persistence and distillation declare `session_file_system` as a host-provided dependency; enable them only in a composition that supplies that capability. The optional `ui-capabilities` feature also owns the namespaced `everruns_core::builtins::{openui,a2ui}` component catalogs and prompt generators; applications do not need separate UI-protocol crates.
## Choose an authoring level
Use the smallest extension contract that fits the behavior you own.
| Contract | `#[everruns::tool]` | `everruns::capability` |
| ------------------------------------- | ----------------------------- | ----------------------------- |
| Best for | One application function | A reusable capability package |
| Typed input and result | Yes | Yes |
| Generated input schema | Yes | Yes |
| Inspectable output schema | No | Yes |
| Multiple tools | Register functions separately | One stable capability id |
| Capability instructions and metadata | No | Yes |
| Session/workspace identity and locale | No | Curated `Context` accessors |
| Progress events | No | `Context::progress` |
| Child-work cancellation | Turn future only | `Context::cancellation` |
| Backend/store/tenancy access | No | No |
## Ordinary tools
Annotate a typed async function. Its doc comment becomes the description and its arguments become JSON Schema.
```rust
/// Convert Celsius to Fahrenheit.
#[everruns::tool]
async fn fahrenheit(celsius: f64) -> f64 {
celsius * 1.8 + 32.0
}
let agent = everruns::Agent::builder()
.instructions("Use the conversion tool.")
.provider(everruns::OpenAI::from_env()?)
.model("gpt-6.1-sol")
.tool(fahrenheit())
.build()?;
```
Prefer this until you need a capability-level contract.
## Advanced capabilities
An advanced capability is an immutable `capability::Definition`. It owns a stable id, catalog text, optional instructions and JSON metadata, and one or more typed handlers. `AgentBuilder::capability` installs its implementation on the private in-process runtime and activates that stable id once.
```rust
use everruns::{Agent, OpenAI, capability};
#[derive(capability::Deserialize, capability::JsonSchema)]
#[serde(crate = "everruns::capability::serde")]
#[schemars(crate = "everruns::capability::schemars")]
struct LookupInput {
id: String,
}
#[derive(capability::Serialize, capability::JsonSchema)]
#[serde(crate = "everruns::capability::serde")]
#[schemars(crate = "everruns::capability::schemars")]
struct Record {
id: String,
score: f64,
labels: Vec,
}
struct Lookup;
#[capability::async_trait]
impl capability::Handler for Lookup {
type Input = LookupInput;
type Output = Record;
type Error = capability::Error;
fn name(&self) -> &str { "lookup_record" }
fn description(&self) -> &str { "Look up one record by exact id." }
fn hints(&self) -> capability::Hints {
capability::Hints::default()
.readonly(true)
.idempotent(true)
}
async fn execute(
&self,
input: Self::Input,
context: capability::Context,
) -> Result {
context.progress("Looking up the record").await;
if input.id != "rec_42" {
return Err(capability::Error::user(
"record_not_found",
"No record has that id",
).details(capability::serde_json::json!({ "id": input.id })));
}
Ok(Record {
id: input.id,
score: 0.98,
labels: vec!["verified".into()],
})
}
}
let records = capability::Definition::new(
"records",
"Records",
"Application-owned record lookup.",
)
.instructions("Use exact record ids and do not infer missing records.")
.metadata(capability::serde_json::json!({ "owner": "risk" }))
.tool(Lookup);
let agent = Agent::builder()
.instructions("Answer with verified record data.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.capability(records)
.build()?;
```
Both input and output types must satisfy the compile-time protocol bounds. The generated schemas are available through `Definition::tools()[..].spec()` for tests, documentation, or a host catalog. Results are serialized directly to JSON, so structs, arrays, numbers, booleans, and null do not pass through a string conversion.
## Errors
Return `capability::Error::user(code, message)` for an expected domain failure. Add bounded JSON details when they help the model recover. The code, message, and details travel through the model-visible tool-error channel.
Return `capability::Error::internal(code, message)` for diagnostic details that are unsafe to show to the model, such as network internals or implementation bugs. The engine logs the diagnostic and gives the model a generic error; internal details do not cross the model boundary. Never include credential values or other secrets in any error because host logs may retain internal diagnostics.
Custom application error enums can implement `Into` and be used as `Handler::Error`.
## Context, progress, and cancellation
`capability::Context` exposes only stable lifecycle data:
* opaque session and workspace ids;
* the resolved locale, when present;
* best-effort correlated `tool.progress` events;
* a call-scoped cancellation signal.
Observe progress through `Session::events()` and `SessionEventKind::ToolProgress`. Everruns does not currently expose a custom capability result-streaming protocol; return one typed result when execution finishes.
Normal awaited work needs no cancellation branch. Cancelling a turn drops the handler future. Clone `context.cancellation()` only into child tasks, processes, or watchers that might otherwise survive after `execute` is dropped. The signal fires on cancellation and on every other call completion path.
## Security boundary
The advanced SPI intentionally does not export provider credentials, stores, tenant or organization objects, registries, payment authority, filesystem backends, or other host services. Pass application-owned clients or state into your handler struct when constructing the definition. A handler is trusted application code and retains whatever process authority those values provide; `Hints` describe behavior but do not enforce authorization, egress, or approval policy. Apply authorization, egress policy, timeouts, and input bounds at those application boundaries, and never place secrets in capability or tool metadata.
For a complete provider-backed program, run the [`advanced_capability` example](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/advanced_capability.rs).
Some built-ins expect the application to supply behavior rather than configuration. `ask_user` is one: it needs someone to answer, so `AgentBuilder::ask_user` takes a responder instead of a config value. See [Answer agent questions](https://everruns.com/docs/framework/ask-user/).
---
# Serve AG-UI
> Stream a Framework session to CopilotKit or any AG-UI 1.0 client from your own HTTP server, with ask_user questions and tool approvals as interrupts.
Source:
[AG-UI](https://docs.ag-ui.com) is the event protocol between an agent and the application that renders it. CopilotKit and `@ag-ui/client` post a `RunAgentInput` and read back a stream of events, usually as server-sent events. The `ag-ui` feature lets any Rust host answer those requests from a Framework session, without the Everruns server:
```bash
cargo add everruns --features ag-ui
```
`Session::ag_ui(input)` sends the input’s last user message and returns the run as a stream of AG-UI 1.0 events: `RUN_STARTED`, the projected turn, then one `RUN_FINISHED` or `RUN_ERROR`. The projection is the one the Everruns server uses, with the trusted policy: assistant text, reasoning, and token usage are visible, and a failure carries the runtime’s message.
## Mount it on axum
The `ag-ui-axum` feature adds `AgUiHandler`, a ready-made route:
```bash
cargo add everruns --features ag-ui-axum
```
```rust
use axum::Router;
use everruns::ag_ui::{AgUiHandler, AgUiOptions, AgUiThreads, InterruptGate, StaticToken};
use everruns::{Agent, Engine, OpenAI};
#[tokio::main]
async fn main() -> Result<(), Box> {
let gate = InterruptGate::new();
let agent = Agent::builder()
.instructions("You are a helpful assistant.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.ask_user(gate.clone())
.approver(gate.clone())
.build()?;
let handler = AgUiHandler::new(
AgUiThreads::new(Engine::new(), agent),
StaticToken::bearer(std::env::var("AG_UI_TOKEN")?),
)
.options(AgUiOptions::new().gate(gate));
let router = Router::new().route("/ag-ui", handler.route());
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
axum::serve(listener, router).await?;
Ok(())
}
```
Each request is authorized, its `threadId` resolved to a session with `AgUiThreads` (the first run of a thread creates one from your agent, later runs reopen it), and the run streamed as server-sent events: one unnamed `data: ` event per AG-UI event and a `: keepalive` comment every 15 seconds. Everything that can fail does so before the stream opens:
| Status | When |
| ------ | ---------------------------------------------------------------------------------------------- |
| `401` | The authorizer refuses the request. The body is not read. |
| `400` | The body is not a `RunAgentInput`, the `threadId` is invalid, or the run cannot use the input. |
| `500` | The thread store or the session failed. The detail is logged, not returned. |
Errors are `application/problem+json`. Once the stream is open, a failure is the run’s own `RUN_ERROR`. Request bodies are capped by axum’s default 2 MB limit.
### Authorization
`AgUiHandler::new` takes an authorizer, so an open endpoint is a choice you write down:
* `StaticToken::bearer(token)` accepts `Authorization: Bearer `, and `StaticToken::header(name, token)` a custom header. Tokens are compared in constant time; a refusal carries `WWW-Authenticate: Bearer` for the bearer form.
* `Unauthenticated` accepts everything: for local development, or behind a proxy that already authenticates.
* Any `Fn(&HeaderMap) -> Result`, or your own `AgUiAuthorizer` implementation when the check needs to await.
The authorizer returns an `AgUiCaller`. `AgUiCaller::scoped(user_id)` gives each caller its own threads, so one user cannot continue another’s conversation by sending its `threadId`:
```rust
use axum::http::HeaderMap;
use everruns::ag_ui::{AgUiCaller, Unauthorized};
// Your proxy has verified the user and set this header.
let by_user = |headers: &HeaderMap| {
headers
.get("x-user-id")
.and_then(|value| value.to_str().ok())
.map(AgUiCaller::scoped)
.ok_or_else(Unauthorized::new)
};
let handler = AgUiHandler::new(threads, by_user);
```
### Your own route
On another HTTP server, or to resolve sessions yourself, call `Session::ag_ui_with` (or `AgUiThreads::run`) and frame the stream. With `ag-ui-axum`, `sse_response(run)` does the framing; without it, each event is `serde_json::to_string(&event)` in a `data:` line.
The crate’s `ag_ui_axum` example is the handler on the offline simulated model:
```bash
cargo run -p everruns --features ag-ui-axum --example ag_ui_axum
```
Point any AG-UI client at the route:
```ts
import { HttpAgent } from "@ag-ui/client";
const agent = new HttpAgent({
url: "http://127.0.0.1:3000/ag-ui",
headers: { Authorization: `Bearer ${token}` },
});
```
## Threads across restarts
`AgUiThreads::new` keeps its thread map in memory. To keep threads when the process restarts, keep both halves on disk: the sessions, with an agent on the `local` backend, and the map, with `SqliteThreadStore` (both need the `local` feature):
```rust
use everruns::ag_ui::{AgUiThreads, SqliteThreadStore};
use everruns::{Agent, Engine, LocalConfig, OpenAI};
let config = LocalConfig::new("/var/lib/my-app");
let agent = Agent::builder()
.instructions("You are a helpful assistant.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.local(config.clone())
.build()?;
let threads = AgUiThreads::with_store(Engine::new(), agent, SqliteThreadStore::local(&config)?);
```
After a restart the next run of a thread reopens its session, history included. Implement `ThreadStore` to keep the map in your own database instead.
With durable session hosting (`SqliteHosting`), use `AgUiThreads::hosted` and a registered build instead of an Agent value. Each new thread is created through the authority, at an identity derived from the thread, so a retried first run reuses the same session. Threads whose sessions were moved from the `local` backend with `Engine::cut_over_legacy_session` keep answering from those same sessions:
```rust
let threads = AgUiThreads::hosted(engine, build, SqliteThreadStore::local(&config)?);
```
A thread id is chosen by the client, and anyone who knows one continues that conversation. When one server serves several users, scope threads to the authenticated caller: `AgUiCaller::scoped` does it in `AgUiHandler`, and `run_in` on your own route:
```rust
let run = app.threads.run_in(&user_id, input, options).await?;
```
Thread ids must be 1 to 128 characters of `[A-Za-z0-9-_.]`; anything else is `AgUiError::InvalidInput`.
## History the client already has
A client such as CopilotKit keeps the conversation and sends all of it with every run. When a thread is new to the server, for example after a restart without a durable backend, `AgUiThreads` records the input’s earlier user and assistant messages as the new session’s history before the run’s user message, so the agent picks up where the conversation was. Once the session has history, the earlier messages are not read again: the session’s own conversation wins.
Only user and assistant text is seeded (an assistant’s tool calls as text); `tool`, `system`, `developer`, activity and reasoning messages are not. The most recent 256 messages and 512 KiB of text are kept. On a session you resolve yourself, turn it on with `AgUiOptions::seed_history(true)` for a thread’s first run. The client writes these messages, assistant turns included, so treat them as no more trusted than the user message.
## Questions and approvals become interrupts
In the Framework, `ask_user` questions and tool approvals are answered by responders on the agent while the turn waits. `InterruptGate` is a responder for both that answers through AG-UI instead: register it with `.ask_user(...)` and `.approver(...)`, and pass it with `AgUiOptions::gate`. A question or approval then ends the run with the 1.0 interrupt outcome while the turn stays parked in your process:
| Parked on | `reason` | Answer (`resume[].payload`) |
| ----------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `ask_user` | `everruns.ask_user` | `{ "answers": [{ "id", "selected", "other_text" }], "status"? }`, described by the interrupt’s `responseSchema` |
| `ask_user` with a `secret` question | `everruns.secret_required` | none: only abandoning is accepted |
| a tool that needs approval | `tool_approval` | `{ "decision": "allow" \| "allow_always" \| "reject" \| "reject_always" }` |
The interrupt id is the tool call id. The next run sends `resume` entries and no new message; the entries answer the parked requests and the run streams the rest of the same turn. Abandoning an entry (`status: "cancelled"`) declines the question or rejects the call. An interrupt left without an entry is not abandoned: nothing is resolved and the run ends with the interrupts again, as it does when a new message arrives while one is open. An entry that cannot be applied returns `AgUiError::InvalidInput` before the stream opens, and nothing is resolved.
Parked requests live in memory. If the process exits, the waiting turn is left unfinished in the session’s log; after a restart, `Session::resume_interrupted_turn` runs its waiting calls again, so they park on the gate anew and a client’s resume run can answer them (see [session history](https://everruns.com/docs/framework/sessions/#history-and-resume)).
## Policy
`AgUiOptions::policy` takes a `ProjectionPolicy` to hide reasoning or usage, show fixed text while tools run, or map failures to your own error codes:
```rust
use everruns::ag_ui::{AgUiOptions, ProjectionPolicy};
let options = AgUiOptions::new().policy(ProjectionPolicy {
reasoning_visible: false,
usage_visible: false,
tool_activity_text: Some("Working on it...".into()),
..ProjectionPolicy::default()
});
```
## What a run reads from the input
The session owns the conversation, so only the last message is sent, and it must be a user message (its text parts), unless the run carries frontend tool results (below). Earlier messages are read only to seed a new thread (above); `state` and `forwardedProps` are not read. `RUN_STARTED` carries `protocolVersion: "1.0"` only when the request declared a version, so pre-1.0 clients see the stream they expect.
## Frontend tools
The input’s `tools` are tools your page runs, such as a confirmation dialog or a navigation action. Each run makes them the session’s client-side tools, so send them on every run; the latest set wins. When the model calls one, the turn parks and the run streams the call as `TOOL_CALL_START`, `TOOL_CALL_ARGS` and `TOOL_CALL_END` with its name and arguments, then finishes in success with `outcome.pendingToolCallIds`.
Run the calls in the page, then post the next run with their results as trailing `tool` messages (`toolCallId` set, `content` the result, `error` for a failure). The same turn continues and the run streams the rest of it. A result for a call that is not parked is ignored. Until every parked call has a result, nothing is recorded and the run reports the calls again, as it does when a new user message arrives instead.
Definitions are bounded: at most 64 tools, names matching `^[A-Za-z0-9_-]{1,64}$`, descriptions up to 4096 characters, parameter schemas up to 16 KiB, results up to 256 KiB. Names starting with `mcp_` are refused. Parked calls live in memory: if the process exits, they stay unanswered.
`system` and `developer` messages and `context` entries are ignored by default, and a system message after the user message is refused. If your server authenticates whoever posts the input, you can let each run carry instructions:
```rust
use everruns::ag_ui::AgUiOptions;
let options = AgUiOptions::new().input_instructions(true);
```
Each run’s system and developer messages, in order, then its context entries, are then appended to the agent’s instructions for that run. They may appear anywhere in `messages`, including after the user message, which stays the run’s input. The next run replaces them with its own, and a run with none clears them; they never enter the conversation history. Leave this off for callers you do not trust: these messages carry the authority of the system prompt.
A [serve](https://everruns.com/docs/framework/serve/#ag-ui-and-copilotkit) app gets this route built in with its `ag-ui` feature, at `/v1/channels/{agent}/ag-ui`. For a hosted agent with no server code, Everruns serves the same protocol at `/v1/channels/{channel_id}/ag-ui`.
---
# 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://everruns.com/docs/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-5-5".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_contracts::capability::json_schema_for;
use everruns_contracts::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_contracts::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:
* [`crates/integrations/src/github/lib.rs`](https://github.com/everruns/everruns/blob/main/crates/integrations/src/github/lib.rs) — the minimal case: one config field, a pattern constant shared with the runtime check that enforces it.
* [`crates/integrations/src/openrouter/model_scout.rs`](https://github.com/everruns/everruns/blob/main/crates/integrations/src/openrouter/model_scout.rs) — the fuller case: numeric bounds tied to runtime constants, a nested config type, and a spend budget.
## Related
* [Sub-agents](https://everruns.com/docs/capabilities/sub-agents/) — the delegation tool blueprints are invoked through
* [GitHub Scout](https://everruns.com/docs/capabilities/github-scout/) — a shipped blueprint from the caller’s side
---
# Agents and Tools
> Describe an agent with instructions, a model, tools, files, integrations, and an optional workspace, and add typed Rust functions as tools.
Source:
An `Agent` is an immutable, validated application description. Pass it to an application-owned engine to create independent sessions.
```rust
use everruns::{Agent, McpServer, OpenAI};
let agent = Agent::builder()
.name("researcher")
.instructions("Research carefully and cite the evidence you used.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.file("brief.md", "Investigate the supplied question.")
.readonly_file("policy.md", "Never expose secrets.")
.mcp_server(McpServer::http("catalog", "https://example.com/mcp"))
.build()?;
```
Builder validation catches blank instructions, a missing model, duplicate providers, tools, or capabilities, invalid tool schemas, invalid capability IDs/configuration, implementation collisions, and invalid MCP configuration before a session starts. Configure typed built-ins, code-defined packages, and dynamic references through the single `capability(...)` entrypoint; see [Configure and author capabilities](https://everruns.com/docs/framework/advanced-capabilities/).
## Harness foundations
Choose `Harness::base()` for the system essentials only, `conversation()` for dialogue, `worker()` for files, skills, long context, budgeting and task coordination without a shell, or `bashkit_worker()` to add the Bashkit shell. These constructors share the hosted platform’s [preset definitions](https://everruns.com/docs/features/harnesses/).
```rust
let engine = Engine::new();
let session = engine
.create(agent)
.harness(Harness::conversation())
.start()
.await?;
let response = session.send_and_wait("Tell me a joke.").await?;
```
Enable optional host integrations for the tools your application needs. Worker [delegation requires host backends](https://everruns.com/docs/built-ins/harnesses/worker/); the default in-memory host does not supply them. `Harness::generic()` is deprecated and preserves its legacy capabilities. Sessions without a bound harness retain their existing empty foundation.
Presets preserve the framework’s read-only workspace default. For an agent that writes files, explicitly select `Agent::builder().workspace_policy(WorkspacePolicy::read_write())`.
## Files and workspaces
* `file(path, content)` seeds an editable file.
* `readonly_file(path, content)` seeds a file the agent may read but not change.
* `workspace(root)` exposes one trusted real-disk root as `/workspace`.
Choose workspace roots from trusted application configuration. Model output and untrusted request fields must not select executable paths or host directories. The underlying filesystem boundary rejects traversal and symlink escape. Use a [`WorkspacePolicy`](https://everruns.com/docs/framework/workspaces-and-environments/#workspace-security) to configure portable read, write, hidden-path, and recursive-delete restrictions.
## MCP and plugins
`McpServer::http` adds a remote Streamable HTTP server. Headers may be supplied by the host and are redacted from `Debug`. Local-process MCP is separately feature-gated with `mcp-stdio`; its command, arguments, and environment are trusted host configuration.
`AgentBuilder::plugin(path)` loads a local plugin directory and returns a typed error if it cannot be compiled. Non-fatal compiler warnings remain visible in the application-facing session context.
## Inspect effective context
Inspect the next model call before or after a turn:
```rust
let engine = Engine::new();
let session = engine.create(agent);
let context = session.inspect().await?;
println!("messages: {}", context.messages.len());
println!("tools: {}", context.tools.len());
```
Inspection uses the same assembly path as execution, including MCP discovery, plugin prompt contributions, message filters, and model selection.
## Tools
The default-enabled `everruns::tool` macro turns an async Rust function into a typed agent tool. Parameter types produce JSON Schema and call arguments are deserialized before the function runs.
```rust
use everruns::{Agent, OpenAI};
#[everruns::tool]
/// Add two integers.
async fn add(left: i64, right: i64) -> Result {
Ok(left + right)
}
let agent = Agent::builder()
.instructions("Use the add tool for arithmetic.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.tool(add())
.build()?;
```
Use `#[everruns::tool(name = "…", description = "…")]` to override metadata, or `#[tool(rename = "…")]` on a parameter to change its model-facing name. Functions must be async, non-generic, and have plain named parameters.
The published `everruns-macros` package is an implementation crate. Applications should use the re-exported `everruns::tool` macro and should not depend on `everruns-macros` directly.
### Dynamic handlers
`FunctionTool::new` is available when a tool schema is determined at runtime:
```rust
use everruns::FunctionTool;
use serde_json::json;
let echo = FunctionTool::new(
"echo",
"Return the supplied text.",
json!({
"type": "object",
"properties": { "text": { "type": "string" } },
"required": ["text"]
}),
|args: serde_json::Value| async move {
Ok::<_, String>(args["text"].clone())
},
);
```
Prefer the macro for normal typed application tools. Use the dynamic form for schemas obtained from configuration or another protocol.
### Call context and progress
Make the first parameter a `ToolCallContext` (by value or reference) to learn which session, turn, and tool call a handler is serving, and to report progress. Progress arrives on `Session::events()` as `SessionEventKind::ToolProgress`. The context is not a model argument, so it never appears in the schema. The dynamic equivalent is `FunctionTool::with_context`.
```rust
use everruns::ToolCallContext;
/// Export the report.
#[everruns::tool]
async fn export(ctx: &ToolCallContext, pages: u32) -> String {
ctx.progress(format!("rendering {pages} pages")).await;
format!("exported for session {}", ctx.session_id())
}
```
### Approval
Mark a tool with `needs_approval` to ask a host approver before it runs. The bare flag asks on every call. `needs_approval = ` asks only when the rule returns `true`. The rule receives the generated arguments struct, named after the function in PascalCase with an `Args` suffix (`run_sql` becomes `RunSqlArgs`). The approver gets the session id and the tool call, including its id and arguments, so a host serving many sessions can route the request. A rejected call never runs, and the model receives a tool error saying it was rejected. An agent with a gated tool but no `.approver(..)` fails to build.
```rust
use everruns::approval::{ApprovalDecision, ToolApprover, async_trait};
use everruns::{Agent, Model, SessionId, ToolCall, ToolDefinition};
/// Run one SQL statement.
#[everruns::tool(needs_approval = |args: &RunSqlArgs| args.sql.contains("DROP"))]
async fn run_sql(sql: String) -> String {
sql
}
struct Console;
#[async_trait]
impl ToolApprover for Console {
async fn approve(&self, _: SessionId, call: &ToolCall, _: &ToolDefinition) -> ApprovalDecision {
println!("approve {}? {}", call.name, call.arguments);
ApprovalDecision::Reject
}
}
let agent = Agent::builder()
.instructions("Answer with SQL.")
.model(Model::simulated("Done."))
.tool(run_sql())
.approver(Console)
.build()?;
```
`FunctionTool::needs_approval(|args: &Value| ..)` and `FunctionTool::always_needs_approval()` are the dynamic forms.
## File-based agents
Load and export the shared portable package format, including initial files and complete skill directories. See [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/) for validation, diffs, model bindings and runnable examples.
---
# Framework Architecture
> Understand Agent, Engine, Session, pluggable session hosting, and the shared execution kernel.
Source:
Everruns uses one agent runtime across native processes, platform workers and Wasm isolates. Applications define ordinary `Agent` values and use `everruns::Engine` and `Session`. A session hosting profile owns durable admission, canonical state, ownership epochs and activation. It does not reimplement agent planning, tool policy or recovery.
```
flowchart TD
App[Framework application or serve] --> Engine[Engine and Session]
Engine --> Memory[Memory hosting]
Engine --> SQLite[Native SQLite hosting]
Engine --> External[External session hosting]
Memory --> Driver[Shared SessionActorDriver]
SQLite --> Driver
External --> Driver
Platform[Platform API and workers] --> Driver
Driver --> Runtime[Shared Input / Reason / Act runtime]
```
## Public Framework objects
| Object | Responsibility |
| ------------- | --------------------------------------------------------------------------------------------------------------------- |
| `Agent` | Immutable behavior: instructions, model and provider, tools, capabilities, files, and lifecycle hooks |
| `Engine` | Retained behavior registry, session admission, lifecycle, queries and activation through the selected hosting profile |
| `Session` | First-class, engine-bound conversation used for turns, steering, events, cancellation, inspection, and history |
| `Environment` | Session resources, including one exact backend-owned workspace head and typed extensions |
New Framework code creates and resumes sessions through an Engine:
```rust
use everruns::{Agent, Engine, OpenAI};
let agent = Agent::builder()
.instructions("Answer concisely.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.build()?;
let engine = Engine::new();
let session = engine.create(agent);
let session_id = session.session_id();
let turn = session.send_and_wait("Begin.").await?;
assert!(turn.success);
drop(session);
let resumed = engine.resume(session_id).await?;
assert_eq!(resumed.session_id(), session_id);
```
## Shared execution, different authorities
Framework profiles use the same core `SessionActorDriver`, turn loop and event-log recovery. Memory coordinates one process. SQLite commits creation, commands, receipts and wakeups in one database and can run an independent native supervisor. External hosts implement the [public hosting SPI](https://everruns.com/docs/framework/hosting/). The source-only Durable Object reference demonstrates object-local Wasm and a separately authenticated native Engine executor without a Framework vendor dependency.
Admission commits before a send is acknowledged; a turn identity is assigned later. Retain command identities across uncertain replies. Dropping a Session, wait handle or SSE connection does not cancel durable accepted work. Reopen its admission handle to observe assignment, pending client tools, suspension or a finalized outcome. Awaited Framework completion hooks settle before that outcome becomes final.
The Platform server uses its existing transactional PostgreSQL actor authority. Workers lease runnable sessions and drive the same runtime. The Platform API’s internal `TurnBackend` remains a control-plane integration detail.
The generic `everruns-durable` task engine supplies Platform background jobs, worker management and schedules. Agent turns use the shared actor driver.
## Choose a recovery boundary
* **Volatile Framework:** `Engine::new()` is offline and database-free. The creating Engine can resume a dropped Session, but process exit loses it.
* **Native durable Engine:** select `hosting::SqliteHosting`, register the exact `AgentBuild` and Environment factory, and start its supervisor for independent recovery. SQLite durability covers the configured disk’s failure domain.
* **External durable Engine:** implement `SessionHosting`, retain exact builds, and configure independent activation plus explicit host services. The [Durable Object reference](https://everruns.com/docs/framework/durable-objects/) is one source-only implementation, outside the product publish set.
* **Legacy local storage:** explicit `LocalConfig` and custom host backends retain the older facade actor lane and its weaker recovery boundary. Persistent stores alone do not provide coherent session hosting or independent activation.
* **Distributed durable Platform:** server and workers keep each session’s event log in PostgreSQL and lease sessions to workers, so a turn recovers across process or worker loss. Applications call it through the remote API or SDKs rather than configuring the facade Engine.
See [Persistence](https://everruns.com/docs/framework/sessions/#persistence) and [Session History and Resume](https://everruns.com/docs/framework/sessions/#history-and-resume) for the exact application lifecycle.
## Extension boundaries
Normal applications depend on `everruns`. Engine is concrete. Hosting plugins implement `SessionHosting` using `hosting::spi` and supply a coherent authority plus epoch-scoped storage ports. The Framework owns lifecycle execution and outcome projection; plugins never implement another agent loop. Providers implement `ChatDriver`.
Advanced core hosts can still compose the experimental `Execution` and internal turn-backend contracts. Framework applications should use the session hosting boundary, which also covers creation, admission, callbacks, reads and wakeups. Workspaces, compute, secrets, channels and schedules require explicit services with their own lifetime and isolation guarantees. A durable journal does not preserve those resources automatically.
---
# Answer an agent's questions
> Implement the AskUser trait so an embedding application can answer an agent's structured questions from its own interface.
Source:
An agent with the [Ask User](https://everruns.com/docs/capabilities/ask-user/) capability can ask the person it is working with a small batch of structured questions and wait for the answer. In a hosted product the browser renders that card. In an embedding application there is no browser, so the application answers — which is what the `AskUser` trait is for.
```rust
use everruns::ask_user::{Answer, AnsweredBy, AskUser, Outcome, Question, Status, async_trait};
use everruns::{Agent, Model};
struct HouseRules;
#[async_trait]
impl AskUser for HouseRules {
async fn ask(&self, questions: &[Question]) -> Outcome {
let answers = questions
.iter()
.map(|question| Answer {
id: question.id.clone().unwrap_or_default(),
selected: question
.options
.iter()
.find(|option| option.is_default)
.or_else(|| question.options.first())
.map(|option| option.label.clone())
.into_iter()
.collect(),
other_text: None,
secret_ref: None,
})
.collect();
Outcome {
status: Status::Answered,
answered_by: AnsweredBy::Unattended,
answers,
}
}
}
let agent = Agent::builder()
.instructions("Confirm deployment choices before acting.")
.model(Model::simulated("Done."))
.ask_user(HouseRules)
.build()?;
```
`AgentBuilder::ask_user` registers the responder and enables the capability in one call. The responder runs **inside** the tool call, so the turn never parks waiting for an external result — the agent asks, your code answers, and the turn continues.
## Route by session
A host that serves several sessions overrides `ask_in` instead of relying on the default, which ignores the context and calls `ask`. `AskContext` carries the session id, the `ask_user` tool call id, and the turn id when known.
```rust
use everruns::ask_user::{AskContext, AskUser, DefaultsResponder, Outcome, Question, async_trait};
struct Router;
#[async_trait]
impl AskUser for Router {
// Only reached when a caller has no context to give.
async fn ask(&self, questions: &[Question]) -> Outcome {
DefaultsResponder.ask(questions).await
}
async fn ask_in(&self, context: &AskContext, questions: &[Question]) -> Outcome {
// Send `questions` to the client that owns `context.session_id()`.
}
}
```
## Without a responder
`.capability("ask_user")` on its own uses `DefaultsResponder`: it applies the options the model marked as recommended, falls back to the first option, and reports `AnsweredBy::Unattended`. Headless runs resolve immediately rather than waiting out the timeout for somebody who is not there.
A text or secret question has no unattended value. `DefaultsResponder` declines a batch containing either kind rather than returning an empty answer.
## Report who answered
`answered_by` is part of the contract, not decoration:
| Value | Meaning |
| ------------ | --------------------------------------------- |
| `User` | A person actually chose this |
| `Timeout` | The deadline passed and a default was applied |
| `Unattended` | Nobody could be asked; a default was applied |
Report `User` only when a person really answered. An agent that reads a fallback as a considered choice acts with more confidence than the answer earns, and that is the failure this field exists to prevent.
## A worked responder
[Weekend Concierge host](https://github.com/everruns/everruns/tree/main/examples/weekend-concierge-host) implements `TerminalResponder` over stdin: numbered options, comma-separated toggles for a multi-select, a free-text path when the question allows one, and terminal echo turned off for a credential.
```plaintext
[Energy] How much energy does the group have on Friday?
*1. Up for anything — Games, noise, moving around.
2. Low-key — Sitting, talking, snacks.
3. Something else
>
```
Two details in it are worth copying into any responder:
**An empty answer takes the declared default** rather than returning nothing. A question the model asked and nobody addressed is something it cannot distinguish from a deliberate skip.
**A secret answer carries a reference, never a value.** `Answer` has no `value` field at all, so there is no path from a collected credential into the transcript. The host keeps the value; the agent gets `session:MY_TOKEN` and tools resolve it by name.
**A text answer carries its string in `other_text`.** It has no selected option:
```rust
Answer {
id,
selected: Vec::new(),
other_text: Some("feature/open-question".to_string()),
secret_ref: None,
}
```
```rust
Answer {
id,
selected: Vec::new(),
other_text: None,
secret_ref: Some(everruns::ask_user::session_secret_ref("MY_TOKEN")),
}
```
For the smallest possible version, [Ask User example](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/ask_user.rs) runs a responder and the unattended path side by side and prints what each decided.
## Questions are not permission
`ask_user` auto-resolves, so it is for decisions and preferences only. A destructive, irreversible, or outward-facing action needs `request_approval`, whose wait does not auto-resolve. See [the boundary](https://everruns.com/docs/capabilities/ask-user/#not-a-consent-gate).
## See Also
* [Ask User capability](https://everruns.com/docs/capabilities/ask-user/), the contract and its limits
* [Configure and author capabilities](https://everruns.com/docs/framework/advanced-capabilities/)
* [Lifecycle hooks](https://everruns.com/docs/framework/lifecycle-hooks/), for intercepting tool calls rather than answering them
---
# Session work and wakes
> Run immediate and scheduled background work with explicit delivery and restart semantics.
Source:
`everruns::work` lets an application request and handle work owned by a session without importing runtime registries, platform stores, or task-kind constants. The application chooses its own work kinds and JSON payloads.
```rust
use std::time::Duration;
use everruns::work::{TaskOutcome, TaskRequest, WakePolicy, WorkQueue};
use serde_json::json;
let queue = WorkQueue::in_memory();
let work = queue.for_session("session_123");
work.submit(
TaskRequest::new("thumbnail", json!({ "image": "cover.png" }))
.idempotency_key("thumbnail:cover.png")
.wake_policy(WakePolicy::OnCompletion),
).await?;
for delivery in queue.claim_due(Duration::from_secs(30), 16).await? {
// Route on delivery.task.kind and check cancellation before side effects.
queue.finish(
&delivery,
TaskOutcome::success(json!({ "path": "cover-thumb.png" })),
).await?;
}
```
The runnable version is [`session_work.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/session_work.rs).
## Contract
* **Ownership:** every task and wake has one opaque `session_id`. A `SessionWork` handle fixes that owner for all requests and reads.
* **Persistence and restart:** `WorkQueue::in_memory()` is process-local and database-free. State survives replacing the queue only while the same `Arc` is retained. It does not survive a process restart. A host that needs durable recovery supplies a `WorkBackend`.
* **Scheduling:** `Immediate` work is claimable now; `At(SystemTime)` work is not claimable early. Both are one-shot requests. The host owns polling; recurring calendars and schedule runners stay host concerns. There is no hidden scheduler or database in the default build.
* **Delivery:** task and wake claims are leased and at least once. If a process stops before settlement or acknowledgment, the item is claimable after the lease expires. Each retry has a new token and attempt; stale attempts cannot settle newer work.
* **Idempotency:** task keys and direct-wake keys each have a session-scoped namespace. Repeating the same request returns the original task or wake. Reusing a key for different input fails with `IdempotencyConflict`. Workers should also deduplicate external side effects on the stable task id or submission key.
* **Cancellation:** pending work cancels immediately. Running work records cancellation intent; the worker checks the latest task snapshot, stops cooperatively, then reports `TaskOutcome::Canceled`. A task may still succeed if it passes its safe cancellation point first.
* **Wakes:** applications can request an immediate wake directly. A task can also create one atomic completion wake with `WakePolicy::OnCompletion`. Wakes use the same lease/retry/acknowledgment model as tasks.
Durable platform scheduling, retention, distributed polling, and multi-host coordination belong in the host’s `WorkBackend`; they are not enabled by the offline Framework default. The in-memory backend retains accepted payloads until it is dropped and does not enforce admission quotas, so hosted providers must apply tenant authorization, payload limits, quotas, and retention at their own boundary.
---
# Portable and hosted capabilities
> Understand which capabilities run in the Framework and which require the Everruns Platform host.
Source:
The Framework advertises only capabilities that can run through its portable, in-process host contract. A capability reference remains an open value, so an application may retain configuration for an ID supplied by another host or plugin, but an unknown or hosted-only ID contributes no prompt, tools, or behavior in the default Framework runtime.
## Portable Framework capabilities
Portable built-ins use services available from the Framework runtime or from an explicit application integration. Examples include files, session storage, current time, compaction, tool search, skills, and application-authored capabilities. Register application behavior with `#[everruns::tool]` or `everruns::capability` rather than depending on product internals.
## Hosted Platform capabilities
Knowledge Bases and Knowledge Indexes, Memories, subagents and agent handoff, background/session tasks and schedules, user hooks, model scouting, OpenRouter workspace management, citations, and platform-management tools need hosted persistence or orchestration. Their implementations and narrow service contracts live in `everruns-capabilities`; the server and worker product presets register them explicitly.
This boundary prevents the public Framework from promising tools whose stores, tenant scope, workers, or authorization services are absent. It does not change persisted capability IDs or JSON configuration. A specialized low-level host can depend on `everruns-capabilities`, install the required services, and select the hosted registry deliberately.
Moving an agent to the Platform keeps its capability IDs and configuration; see [Moving to Platform or Cloud](https://everruns.com/docs/framework/moving-to-platform/).
For application-owned behavior, continue with [advanced capabilities](https://everruns.com/docs/framework/advanced-capabilities/). For low-level host composition, see [custom backends](https://everruns.com/docs/framework/custom-backends/).
---
# Capability integrations
> Select filesystem, shell, web, Lua, and MCP implementation boundaries without pulling them into the Everruns kernel.
Source:
The Framework separates capability contracts from environment-backed implementations. `everruns-core` defines capability, tool, filesystem, egress, and MCP invocation contracts; focused crates own code that touches an interpreter, network transport, local process, or session filesystem.
This keeps a custom host’s dependency and trust boundaries visible in `Cargo.toml`. It also prevents a core registry from silently granting an execution or network surface.
## Framework features
| `everruns` feature | Default | Implementation | Effect boundary |
| ------------------ | ------: | ---------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `filesystem` | Yes | `everruns-integrations` (`filesystem` feature) | Host-provided, session-scoped filesystem only |
| `bashkit` | No | `everruns-integrations` (`bashkit` feature) | Sandboxed shell; HTTP remains capability-config and egress-policy gated |
| `web-fetch` | No | `everruns-integrations` (`web-fetch` feature) | FetchKit requests through the host egress contract |
| `lua` | No | `everruns-integrations` (`lua` feature) | Vendored Lua 5.4 sandbox; also requires `FEATURE_LUA=prod` at runtime |
| `mcp` | No | `everruns-core` (`mcp` feature) | Remote HTTP MCP through the host egress contract |
| `mcp-stdio` | No | `everruns-core` (`mcp` feature) | Adds local-process MCP servers and implies `mcp` |
| `host-shell` | No | `everruns-core` (`host` feature) | `bash` tool over real host processes, bounded by a kernel policy; implies `host-compute` |
\| `duckduckgo` | No | `everruns-integrations` (`duckduckgo` feature) | DuckDuckGo web search through the host egress contract | | `a2a` | No | `everruns-capabilities` | Outbound A2A delegation to remote agents; implies `local` | | `otel` | No | `everruns-core` (`host` feature) | OpenTelemetry exporter; see [Observability](https://everruns.com/docs/observability/) | | `braintrust` | No | `everruns-core` (`host` feature) | Braintrust exporter; see [Observability](https://everruns.com/docs/observability/) |
The default is offline: the filesystem capability can only use the session-filesystem implementation supplied by the host. Shell, web, Lua, MCP, and local-process transports require explicit features.
```toml
[dependencies]
everruns = { version = "0.34", features = ["bashkit", "web-fetch"] }
```
Enabling an implementation does not activate it on every agent. Add the matching capability reference to the agent, and retain the documented role, network-access, and runtime feature gates. In particular, Bashkit, web fetch, and Lua remain high-risk capabilities in the hosted product.
## Provider integrations
Integration packages can expose typed values through `IntoCapability`. Brave Search supports the ordinary Framework builder:
```rust
use everruns::{Agent, OpenAI};
use everruns_integrations::brave_search::BraveSearch;
let agent = Agent::builder()
.instructions("Search and cite primary sources.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.capability(BraveSearch::from_env()?)
.build()?;
```
Depend on `everruns-integrations` with `default-features = false` and the `brave-search` feature. Add `brave-search-integration` to opt into Platform connector registration; leave it off for a Framework-only dependency. The Framework adapter reads `BRAVE_SEARCH_API_KEY` at construction; `BraveSearch::new` accepts an explicit application-owned key. Keys are retained privately by the client, never placed in capability JSON. Hosted execution continues to resolve connections and session secrets at tool execution time. Both paths use the same search schema and operation. Framework calls use the application’s direct HTTP client.
## Advanced host composition
Advanced embedders select integrations on `everruns` and build the runtime registry through `everruns::batteries::runtime_capability_registry()`:
```toml
[dependencies]
everruns-core = "0.34"
everruns = { version = "0.34", default-features = false, features = ["filesystem", "web-fetch"] }
```
```rust
let registry = everruns::batteries::runtime_capability_registry();
let egress = everruns::batteries::runtime_egress_service();
assert!(registry.has("session_file_system"));
assert!(registry.has("web_fetch"));
assert!(!registry.has("bashkit_shell"));
```
If the host starts from a caller-owned registry, preserve it and apply the same feature-selected integrations with `everruns::batteries::compose_runtime_capability_registry(registry)`.
Hosted server and worker composition uses `everruns_capabilities::capabilities::hosted_capability_registry_for_grade` with the platform’s `environment-capabilities` feature. That preset preserves the hosted catalog while keeping the implementations outside core.
Depend directly on a focused crate when you need its public implementation types. The former core paths move as follows:
| Former public path | New public path |
| ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
| `everruns_core::FileSystemCapability` and filesystem tools | `everruns_integrations_filesystem::*` |
| `everruns_core::BashkitShellCapability`, `BashTool`, and adapter | `everruns_integrations_bashkit::*` |
| `everruns_core::WebFetchCapability`, `WebFetchTool`, and bot-auth helpers | `everruns_integrations_web_fetch::*` |
| `everruns_core::LuaCapability` and `LuaCodeModeCapability` | `everruns_integrations_lua::*` |
| `everruns_core::McpCapability` and MCP capability-ID helpers | `everruns_core::mcp::*` |
| `everruns_core::DirectEgressService` | `everruns_core::host::DirectEgressService` with `direct-egress` |
| `everruns_core::SystemEmailConfig` and Resend types | `everruns_capabilities::*` |
| `everruns_core::ModelScoutCapability` and `OpenRouterWorkspaceCapability` | `everruns_integrations_openrouter::*` |
| `everruns_core::OpenRouterServerToolsCapability` | `everruns_integrations_openrouter::OpenRouterServerToolsCapability` |
| `everruns_core::{HumanIntentCapability, InfinityContextCapability, SkillsCapability, AttachSkillCapability, ToolApprovalCapability}` | `everruns_core::builtins::*` |
| `everruns_core::{OpenUiCapability, A2UiCapability}` | `everruns_core::builtins::*` with `ui-capabilities` |
| `everruns_core::skill::ProcessCommandExecutor` | `everruns_core::host::ProcessCommandExecutor` with the host `process` feature |
Continue with [Configure and author capabilities](https://everruns.com/docs/framework/advanced-capabilities/) for agent-level activation or [Custom backends](https://everruns.com/docs/framework/custom-backends/) for host-level storage and orchestration.
---
# Coordinator agents
> One session hands focused work to threads, hears back from them, and resolves them.
Source:
A coordinator is an agent that does not do the focused work itself. The person talks to one session. That session starts a **thread** for each piece of work, relays follow-ups to it, and reports back when it finishes. Each thread keeps a checklist and ends its work with a summary. Every report wakes the coordinator with an automatic update, so nobody has to poll.
This is the same model as Platform Chat’s threads, running inside your own process.
```rust
use everruns::coordination::{self, Coordination, ThreadStatus};
use everruns::{Agent, Engine, LocalConfig, Model, WorkspacePolicy};
let agent = Agent::builder()
.name("launch-coordinator")
.instructions("Start one thread per piece of work. Tell me when each is ready.")
.model(Model::simulated("On it."))
.capability(Coordination::new().max_active_threads(4))
.workspace_policy(WorkspacePolicy::read_write())
.local(LocalConfig::new("./launch/.everruns").workspace("./launch"))
.build()?;
let engine = Engine::new();
let session = engine.create(agent);
session.run("Write the announcement and draft a pricing FAQ.").await?;
for thread in coordination::threads(&session).await? {
if thread.status == ThreadStatus::ReadyForReview {
println!("{}: {}", thread.title, thread.summary.unwrap_or_default());
}
}
```
Coordination needs the `local` feature and an agent built with [`.local(...)`](https://everruns.com/docs/framework/sessions/): threads, their assignments and their checklists live in the local profile’s SQLite store.
## What the coordinator does
The capability gives the coordinator five tools:
| Tool | Use |
| ---------------- | --------------------------------------------------------------------------------------------- |
| `start_thread` | Start a thread with a title and a brief that says what done looks like. |
| `message_thread` | Relay a follow-up. After a thread finished, this opens a new unit of work on the same thread. |
| `list_threads` | See every thread and where it stands. |
| `get_thread` | Read one thread’s assignments and recent messages. |
| `resolve_thread` | Close a thread once the person accepted its work, or reopen it. |
Threads never talk to each other. Every route goes through the coordinator, so routing has one owner and one record.
## What a thread does
A thread is an ordinary session of the same engine, running the coordinator’s agent. While it works an assignment it gets its own tools in place of the coordinator’s: `update_checklist`, `complete_assignment`, `ask_decision`, `report_to_coordinator` and `redirect_to_coordinator`.
A thread turn that ends without `complete_assignment` flags the assignment as needing attention, so the coordinator always hears back. You can open a thread like any other session with [`Engine::resume`](https://everruns.com/docs/framework/sessions/) and its id from `coordination::threads`.
## Automatic updates
When a thread finishes, asks a question or reports, the coordinator gets a user-role message marked as an automatic update. If the coordinator is idle, the update starts a turn. If it is mid-turn, the update steers that turn. Your application sees the coordinator’s answer in its history and events like any other reply.
## Status
`coordination::threads` returns one entry per thread, from its latest unit of work:
| Status | When |
| ---------------- | ------------------------------------------------------------- |
| `NeedsYou` | The thread asked a question, or its work failed. |
| `Working` | The thread is working. |
| `ReadyForReview` | The thread finished; the coordinator has not resolved it yet. |
| `Open` | The work was cancelled and the thread was left open. |
| `Resolved` | The coordinator resolved the thread. |
## Limits
* Threads run the coordinator’s own agent. A local profile has no agent catalog, so another worker agent is refused.
* A turn your application starts on a thread directly is not settled against its assignment. Send follow-ups through the coordinator.
* The default caps are 8 threads working at once and 100 in total; set them with `max_active_threads` and `max_total_threads`.
## Example
[`examples/coordinator-agent`](https://github.com/everruns/everruns/tree/main/examples/coordinator-agent) runs a launch coordinator with two threads that write their drafts to a shared workspace. `--offline` runs it with a deterministic simulated model; without it, the example uses OpenAI.
---
# Custom Backends
> Decide when a Framework application should cross into low-level execution-host composition.
Source:
Most applications should use `everruns::Agent`, `Model`, and `Session`. That surface deliberately hides stored harness records, platform registries, backend stores, worker phases, and durable scheduling topology.
For durable execution under Framework, implement [`SessionHosting` through the public hosting SPI](https://everruns.com/docs/framework/hosting/). Engine owns the runtime and recovery; the host supplies transactional session authority, epochs and independent activation. This path needs only an `everruns` dependency.
To keep a host’s own capability implementations, provider resolution, retry budgets, MCP credentials or observers while Engine runs the sessions, use [host services](https://everruns.com/docs/framework/host-services/) instead of composing a runtime.
Cross into separate low-level composition only when your application is itself an execution host, for example, a server, evaluation harness, research runtime, or specialized embedder that must replace storage or orchestration components.
## Host-level choices
The low-level crates expose focused contracts for:
* core agent, event, capability, and provider values;
* the shared Input/Reason/Act kernel and sans-I/O turn planner;
* runtime host phases, canonical event history, and in-memory reference stores;
* local SQLite-backed task and schedule state;
* portable session and agent definitions, plus explicitly selected durable deployment components.
A separately composed low-level host depends on `everruns`, `everruns-core` with the `host` feature, and `everruns-contracts` for neutral service contracts. The low-level boundary is `everruns_core::host`; concrete integrations are selected on the facade. It is healthy for such a host to use low-level extension traits; the goal is not to re-export every backend through one facade.
## Two engine boundaries
`everruns::Engine` is a concrete application object that owns the behavior registry, sessions, lifecycle, queries and activation through its hosting profile. It is the normal Framework entrypoint, not an extension trait. Applications do not implement it.
`everruns_core::engine` is the shared execution kernel, enabled by the `engine` feature. Advanced hosts compose its `Execution` contract and serializable `TurnExecution` state machine, `InputAtom`/`ReasonAtom`/`ActAtom`, and phase values. The implementation lives in `everruns_core::host`, and the Platform’s workers drive the same turn loop under a session lease. Both use narrow contracts from `everruns-core`. The engine feature has no dependency on host effects, platform, server, worker, or durable crates. Do not copy state advancement or the phase loop into a custom backend; implement the execution boundary and keep deployment-specific service selection in the host.
Where a turn runs is a separate interface: `everruns_core::host::TurnBackend` starts, cancels, and observes a session’s turns. `InProcessBackend` runs a turn in process. The legacy local/custom-backend facade lane uses `ActorRunner` under the session’s lease, and the Platform’s server marks the session runnable for a worker that leases it and runs the whole turn. Coherent Framework hosting instead enters Engine’s shared session actor path. `TurnBackend` is not the durable Framework extension point. A low-level host that records input through its own event store starts turns from it with `TurnInput::StoredMessage` and continues a parked turn with `TurnInput::RecordedToolResults`, which every backend serves. The trait is public and unsealed but experimental. The facade’s [backend conformance suite](https://github.com/everruns/everruns/tree/main/crates/everruns/tests/backend_conformance) defines the behavior every backend must match.
See [Framework Architecture](https://everruns.com/docs/framework/architecture/) for the complete layer map and the distinction between immediate and durable execution.
Conversation persistence is the one backend with a single write path. Replace it by implementing the canonical `EventLog`/`EventReader` SPI and passing it to `HostBackends::with_event_log`; the required snapshot, continuation, and polling behavior is specified in [Implementing a custom event log](https://everruns.com/docs/framework/events-and-cancellation/#implementing-a-custom-event-log).
## Security boundary
Backend replacement does not relax tenant, credential, filesystem, or tool execution boundaries. Preserve event ordering, credential redaction, workspace containment, and cancellation behavior when adapting the host. A custom backend must fail explicitly when it cannot satisfy a required contract.
---
# Deploying a Framework App
> Ship a Framework app as a binary or container: persistent storage for its data directory, provider keys from the environment, and when to use serve instead.
Source:
A Framework app is your own Rust binary. The `everruns` crate needs no database, message broker, or Everruns server, so deploying it is deploying that binary: build it, give it provider credentials, and, if sessions must outlive the process, give it a directory that persists.
## Choose where state lives
| Engine setup | State | Deploy with |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `Engine::new()` | In memory, lost when the process exits | Any binary or container. Nothing to persist |
| An agent built with `.local(LocalConfig::new(dir))` | Crash-durable events, session catalog, and SQLite task and schedule state under `dir`; agent files under `dir/workspace` unless `.workspace(path)` sets another root | A volume that survives restarts and redeploys |
`LocalConfig` needs the `local` feature. [Sessions](https://everruns.com/docs/framework/sessions/#persistence) covers the full trade-off.
For a deployment that keeps local state:
* **One process per data directory.** The local profile is built for one embedded process at a time. Do not point two replicas or two containers at the same directory. Scale by giving each instance its own directory and routing each session to the instance that owns it. If two processes do share one, a session’s turns still run in only one of them at a time: each turn holds the session’s lease in the local database.
* **Trusted paths only.** Choose the data and workspace directories from deployment configuration, never from model output or request input.
* **Persistent storage.** Mount the data directory from a volume. New state files are created owner-only on Unix; your backups and copies need the same protection, because the event log holds conversation content.
* **Resume after a restart.** Rebuild each agent from the same trusted configuration, then call `engine.attach(session_id, agent)` and `engine.resume(session_id)`. See [Resume after a process restart](https://everruns.com/docs/framework/sessions/#resume-after-a-process-restart).
## Advanced local hosts
Hosts built with `everruns::local::LocalRuntimeBuilder` reconcile orphaned background executions after constructing their runtime and start periodic maintenance. Recovery requires executor evidence that ownership is lost. A missing owner process leaves the execution outcome unverified; interrupted commands are never replayed.
When composing `LocalBackends` directly, retain its recovery-wrapped task registry, bind `task_recovery` to the runtime’s task wake observer, then call `recover_all` and `start_maintenance`. Recovery wakes are delivered at least once across crashes; a durable observer must deduplicate by session, task ID and terminal attempt.
## Pass secrets through the environment
Each provider type reads its key with `from_env`: `OpenAI::from_env()` reads `OPENAI_API_KEY` and `OPENAI_BASE_URL`, `Anthropic::from_env()` reads `ANTHROPIC_API_KEY`, and so on. The full per-driver table is under [Credentials](https://everruns.com/docs/framework/models-and-providers/#credentials). Set those variables from your platform’s secret store (a container secret, a Kubernetes `Secret`, a systemd credential), not in the image.
`from_env` returns an error when a required variable is missing, so a misconfigured deployment fails at startup instead of on the first turn. When your application already resolves the key itself, pass it with `OpenAI::new(key)`. On AWS, `Bedrock::default_chain()` uses the instance or task role, so no key is stored at all.
A minimal service entry point:
```rust
use std::time::Duration;
use everruns::{Agent, Engine, LocalConfig, OpenAI};
// Deployment configuration, not request input.
let data_dir = std::env::var("APP_DATA_DIR").unwrap_or_else(|_| "/data".into());
let agent = Agent::builder()
.instructions("Answer support questions.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.local(LocalConfig::new(data_dir))
.build()?;
let engine = Engine::new();
let session = engine.create(agent);
// ... accept requests and drive sessions ...
tokio::signal::ctrl_c().await?;
engine.shutdown(Duration::from_secs(10)).await;
```
`APP_DATA_DIR` is this example’s own setting; the Framework reads no variable for the data directory. `Engine::shutdown` drains and flushes event listeners such as the OpenTelemetry and Braintrust exporters, so call it before the process exits. See [Observability](https://everruns.com/docs/framework/observability/).
## Build a container
A multi-stage build keeps the toolchain out of the runtime image. This follows the Dockerfile in [AgentCore app](https://github.com/everruns/everruns/tree/main/examples/serve/agentcore):
```dockerfile
FROM rust:1-bookworm AS build
WORKDIR /src
COPY . .
RUN cargo build --release --locked --bin my-agent
FROM gcr.io/distroless/cc-debian12:nonroot
COPY --from=build /src/target/release/my-agent /my-agent
VOLUME /data
ENTRYPOINT ["/my-agent"]
```
The `everruns` crate’s HTTP clients use rustls, so the image needs no OpenSSL. It still needs CA certificates to reach provider APIs; the distroless `cc` image includes them. The `nonroot` user must be able to write the mounted data directory.
Run it with the key from a secret and the data directory on a volume:
```bash
docker run --rm \
-e OPENAI_API_KEY \
-e APP_DATA_DIR=/data \
-v my-agent-data:/data \
my-agent
```
A container without `LocalConfig` needs no volume.
## When to use serve instead
[serve](https://everruns.com/docs/framework/serve/) is an experimental application framework on top of the Framework. Use it instead of a hand-written binary when you want:
* the Everruns `/v1` session API, so the SDKs, CLI, and UI chat view can drive your agents, plus AG-UI and A2A endpoints;
* a manifest of agents, tools, secrets, and models that a host can provision from;
* a `start` command that requires every declared secret, and that fails a model with no real route instead of falling back to the simulator. Hosts route models through `SERVE_GATEWAY_URL` and `SERVE_GATEWAY_KEY` (any OpenAI-compatible gateway) or `OPENROUTER_API_KEY`.
A serve binary listens on `0.0.0.0`, on `PORT` (default 3000). Its state goes to the SQLite file named by `DATABASE_URL`, else to `SERVE_DATA_DIR`, else to `.serve/` in the working directory, or, with `start --store s3://bucket/prefix`, to a bucket that a daemon on another machine can take over (see [Keep the data in a bucket](https://everruns.com/docs/framework/serve/#keep-the-data-in-a-bucket)). Its wire API has no authentication, so run it behind a host that authenticates requests. Amazon Bedrock AgentCore is the one supported deployment target; see [Serve on AgentCore](https://everruns.com/docs/framework/serve-agentcore/).
Keep a plain Framework binary when the agent is embedded in an application that already owns its API, authentication, and storage.
To move to a managed, multi-tenant runtime instead, see [Moving to Platform or Cloud](https://everruns.com/docs/framework/moving-to-platform/).
---
# Direct Model Calls and Decisions
> Call a model once through the Framework's provider edge, or ask Decisions for a number instead of prose, without building an agent.
Source:
Some work is one prompt and one answer: classify a string, draft a summary, extract a field. That needs the provider edge — drivers, endpoints, credentials, retries, error classification — but none of the agent loop around it.
`Model::complete` is the whole API for that case:
```rust
use everruns::{Model, OpenAI};
let model = Model::new("gpt-6.1-sol", OpenAI::from_env()?);
let answer = model.complete("Name the three primary colors.").await?;
println!("{answer}");
```
The model is the same value an [agent](https://everruns.com/docs/framework/agents/) takes, reached through the same [`Provider`](https://everruns.com/docs/framework/models-and-providers/), which can also be asked [which models it offers](https://everruns.com/docs/framework/models-and-providers/#model-catalogs). Nothing is persisted: a direct completion owns no session, no history, and no workspace. Reach for an agent as soon as the work needs tools, multiple turns, durability, or events.
When the answer should be a number rather than prose, such as a probability, a choice among options, or a graded score, use [Decisions](#decisions) instead.
## Testing without a provider
`Model::simulated` returns canned responses from an in-process simulator (`everruns-llmsim`). It is a **test double**, not a local model: it runs no inference and is not a way to use Everruns without a model provider. It exists so tests and examples can assert on agent behavior without a network call or an API key.
Real work always goes through a provider — see [Supported providers](https://everruns.com/docs/framework/models-and-providers/#supported-providers).
```rust
use everruns::Model;
let answer = Model::simulated("4").complete("What is 2 + 2?").await?;
assert_eq!(answer, "4");
```
See [Testing and simulation](https://everruns.com/docs/framework/testing-and-simulation/) for scripted multi-response simulators.
## System messages, context, and controls
`Model::completion` describes the call before sending it. Messages append in call order; each control maps to one provider request field and stays unset unless assigned, so the provider keeps its own defaults.
```rust
use everruns::{Model, ReasoningEffort};
let response = model
.completion()
.system("Answer with a single word.")
.user("What is the capital of France?")
.max_tokens(16)
.reasoning_effort(ReasoningEffort::Low)
.send()
.await?;
println!("{}", response.text);
println!("{:?} tokens", response.metadata.total_tokens);
```
`send` returns the full `LlmResponse` — text, reasoning artifacts, tool calls, and call metadata. `text()` returns only the answer text. Replay prior turns with `.assistant(...)`: the completion carries no history of its own, so context is whatever the call passes.
When the model is a bare provider-visible id, attach the provider on the completion instead of the model:
```rust
use everruns::{Model, OpenAI};
let answer = Model::from("gpt-6.1-sol")
.completion()
.provider(OpenAI::from_env()?)
.user("Summarize this in one line: ...")
.text()
.await?;
```
## Streaming
`stream()` returns the provider’s events as they arrive, ending with a `Done` event carrying the call’s metadata:
```rust
use everruns::{LlmStreamEvent, Model};
use futures::StreamExt;
let mut stream = model.completion().user("Write a haiku.").stream().await?;
while let Some(event) = stream.next().await {
if let LlmStreamEvent::TextDelta(delta) = event? {
print!("{delta}");
}
}
```
What the stream guarantees:
* Every `TextDelta` carries text. Assistant text is their concatenation.
* `ReasoningDelta` streams reasoning live; the `ReasoningItem` that follows repeats the whole block, plus any replay state. Show the deltas and store the item, or use the item alone; adding both duplicates the reasoning.
* The stream ends with exactly one `Done` or `Error`. `Error` keeps the provider’s message, error code, and HTTP status, including errors a gateway reports inside a `200` stream.
* `Done` carries `finish_reason` as the provider sent it, or `None` when it sent none. It is never filled in as `stop`, so a caller can treat a missing reason as a failure.
* Drivers retry `429` and transient `5xx` responses before the first event: up to 2 retries and 30 seconds by default. That time counts against any timeout you put around the call. To own retries yourself, build the driver with `.with_retry_config(everruns::llm::LlmRetryConfig::no_retry())`.
The full contract is on `LlmStreamEvent` in the API reference.
## Errors
`CompletionError` separates configuration mistakes from provider failures:
* `MissingProvider` — the model names an id but nothing says how to reach it.
* `NoMessages` — the completion was sent empty.
* `Call(..)` — the provider call failed, carrying the `AgentLoopError` and its full `LlmError` decision.
The first two are caught before any request leaves the process.
## Going lower
`Completion` is a thin value-first layer over `Provider`, which is public. Applications that already hold a `Provider` — or implement their own [`ChatDriver`](https://everruns.com/docs/framework/models-and-providers/#custom-providers) — can call it directly with `everruns::llm`’s `Message` and `MessageRole`, plus the crate-root `LlmCallConfig` and `LlmResponse` re-exports:
```rust
use everruns::llm::{Message, MessageRole};
use everruns::{LlmCallConfig, Provider};
let response = provider
.chat_completion(
vec![Message::text(MessageRole::User, "What is 2 + 2?")],
&LlmCallConfig::new("gpt-6.1-sol"),
)
.await?;
```
That surface is the driver boundary itself: every field of `LlmCallConfig`, including tool definitions, is available, and nothing is defaulted for you.
When all you have is a base URL, `DriverId::for_base_url` names the driver whose vendor serves it (OpenAI, Azure OpenAI, OpenRouter, Anthropic, Gemini, Fireworks). It returns `None` for a gateway or self-hosted server; those speak the OpenAI-compatible wire, so use `DriverId::OpenAICompletions`.
The runnable version of this section is [`direct_llm.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/direct_llm.rs), which runs offline without an API key.
## Decisions
Some questions have typed answers. *Is this claim supported by the source? How severe is this complaint? Which queue does this ticket belong in?* A chat model answers those in prose, so the call site ends up with a prompt asking for JSON, a parser, and a fallback for when the parse fails.
A decision service answers them as numbers instead, and the decision stays in your code.
This is the counterpart to a direct model call: the same shape, a different contract.
| | `Model` | `Decisions` |
| ------------------- | ----------------- | ---------------------------- |
| you send | messages | state plus typed questions |
| you get back | text | calibrated numbers |
| decides the outcome | the model’s words | your threshold, in your code |
| streams | yes | no — one round trip |
### Quick start
```bash
cargo add everruns --features typesafe
cargo add tokio --features macros,rt-multi-thread
export TYPESAFE_API_KEY=... # a key from typesafe.ai
```
```rust
use everruns::{Decisions, TypeSafeAI};
#[tokio::main]
async fn main() -> Result<(), Box> {
let decisions = Decisions::new("jev-latest", TypeSafeAI::from_env()?);
let text = "CONGRATULATIONS! You've WON $1,000,000. Click here to claim your prize now!";
let spam = decisions.probability("Is this message spam?", text).await?;
println!("spam: {spam:.2}");
if spam > 0.9 {
println!("quarantined");
}
Ok(())
}
```
```text
spam: 0.98
quarantined
```
One number, and your own `> 0.9` decides — the model reports how likely, not what to do. The same call answers `0.03` for “Standup moved to 10am.” and `0.74` for a bare “Claim your prize now!”; the middling ones are what a threshold is for.
`--features typesafe` adds `TypeSafeAI` and the `Jev` capability. Without it `everruns` names no vendor: `Decisions::new` takes any `DecisionsService`.
### OpenAI’s Decisions API
[OpenAI’s Decisions API](https://developers.openai.com/api/docs/guides/decisions) (public beta, model `gpt-6-luna`) answers the same questions. `OpenAIDecisions` in `everruns-integrations` is a `DecisionsService`, so it drops into the same `Decisions`:
```bash
cargo add everruns
cargo add everruns-integrations --features openai-decisions
export OPENAI_API_KEY=...
```
```rust
use everruns::Decisions;
use everruns_integrations::openai_decisions::OpenAIDecisions;
let service = OpenAIDecisions::new(std::env::var("OPENAI_API_KEY")?);
let decisions = Decisions::new("gpt-6-luna", service);
let urgent = decisions
.probability("Does this convey urgency?", "My card was charged twice, fix it today.")
.await?;
println!("urgent: {urgent:.2}");
```
A runnable version that routes a support ticket with all three primitives is [`openai_decisions_triage`](https://github.com/everruns/everruns/blob/main/crates/integrations/examples/openai_decisions_triage.rs):
```bash
OPENAI_API_KEY=... cargo run -p everruns-integrations --features openai-decisions --example openai_decisions_triage
```
All three primitives are native and calibrated, and one request sends every question in one call. Score questions take at most ten levels. On Everruns Platform, set `UTILITY_DECISION_DRIVER=openai` with `UTILITY_OPENAI_API_KEY` to answer guardrail `jev` checks with it (see [environment variables](https://everruns.com/docs/sre/environment-variables/)).
### Three primitives
A decision asks one or more questions about the same state. Each is one of three shapes:
```rust
use everruns::Decisions;
let answers = decisions
.about("I've been on hold for two hours and my card was charged twice.")
.noul("urgent", "Does this convey urgency?")
.score(
"severity",
"How severe is the problem the writer describes?",
[
"A minor annoyance",
"A real problem with their account",
"Serious harm requiring immediate action",
],
)
.choice(
"queue",
"Which team should handle this message?",
["billing", "technical", "sales"],
)
.send()
.await?;
let urgent: f64 = answers.probability("urgent")?;
let queue: &str = answers.selected("queue")?;
```
* **`noul`** — whether something holds, as the probability of yes. A value near 0.5 means yes and no are near-equally likely, not “medium”. ([Noul](https://docs.typesafe.ai/primitives/noul))
* **`choice`** — exactly one option from your set, with the distribution behind it. Needs at least two options. ([Choice](https://docs.typesafe.ai/primitives/choice))
* **`score`** — a position along levels you define, lowest first. Needs at least two levels. ([Score](https://docs.typesafe.ai/primitives/score))
The three are System One’s own, so TypeSafe’s [Primitives](https://docs.typesafe.ai/primitives) documents what each answer means and how to choose between them, and [State](https://docs.typesafe.ai/concepts/state) covers what to put in the `about(...)` value. Everruns names them the same way rather than inventing synonyms.
Questions in one call are answered **in parallel inside a single request**, so asking five costs one round trip, not five. TypeSafe calls leaning on that [speculative fan-out](https://docs.typesafe.ai/patterns/fan-out): ask the questions you *might* need, and let your code decide which ones mattered.
### Ids are yours; instructions are the model’s
The id labels the answer for your code and is never sent to the model. A question whose meaning lives in its id asks nothing:
```rust
// Wrong: the model never sees "is_the_joke_funny".
.noul("is_the_joke_funny", "?")
// Right: the instructions carry the question.
.noul("funny", "Would a general audience laugh at this joke?")
```
The same applies to score levels. Describe concrete situations — “A minor annoyance” reads on its own where “2 out of 5” does not.
### Read the tail, not the average
For “is there any serious hit here” rules, read the probability mass at or above a level rather than the weighted score. Something probably fine but possibly awful must not average into fine:
```rust
// Not: answers.score("severity")? > 1.5
let serious = answers.tail("severity", 2)?;
if serious > 0.3 {
println!("escalated");
}
```
### Errors
`DecisionsError` separates configuration mistakes from service failures, the same split [`CompletionError`](#errors) makes:
* `MissingService` — the decisions was built without a service to reach.
* `NoQuestions` — the decision was sent with nothing to ask.
* `Unconfigured` — the service exists but the deployment never configured its credential, so it would answer nothing.
* `NoSuchAnswer(id)` — you read an id that was not asked, or read an answer as the wrong shape (a `score` as a probability).
* `Call(..)` — the service call failed, carrying the `AgentLoopError`.
The first three are caught before any request leaves the process. `Unconfigured` is worth handling separately: a guardrail treats it as fail-open, but a direct caller usually wants to know the number never arrived rather than read a confident-looking default.
### Going lower
`Decision` is a thin value-first layer over `DecisionsService`, which is public. Applications that already hold a service — or implement their own, over a different vendor or a local model — can call it directly with `everruns`’s `DecisionRequest`, `DecisionQuestion`, and `DecisionAnswer` re-exports:
```rust
use everruns::{DecisionQuestion, DecisionRequest, DecisionsService};
let outcome = service
.evaluate(
DecisionRequest::new("Claim your prize now!")
.ask("spam", DecisionQuestion::noul("Is this message spam?")),
)
.await?;
```
That surface is the contract itself: every question type and the full `DecisionOutcome`, including usage, with nothing defaulted for you. Implementing `DecisionsService` is also how a different decision service (another vendor, or a fine-tuned local model) plugs into the same `Decisions`, guardrails included.
### Choosing a model
The model is named up front, the way `Model::new` names one: the service is transport, and the model is the thing that answers. There is no default to inherit without noticing, because a threshold calibrated against one version is not evidence about the next.
Ids are the provider’s own, so they are spelled the way the vendor spells them. `jev-latest` is TypeSafe’s alias for the current Jev, so it tracks whatever the current version is; an exact id like `jev-1.13.0` pins one, so a vendor update cannot move your thresholds under you. Bare `jev` is not an id the API knows — nothing here rewrites what you pass.
Ask for the alias and read back what answered, which is the id to pin once a threshold is calibrated:
```rust
let version = answers.model(); // "jev-1.13.0" for a "jev-latest" request
```
A single call can name a different model with the same method on the request builder, and it wins for that call:
```rust
let answers = decisions
.about("...")
.noul("urgent", "Does this convey urgency?")
.model("jev-1.13.0")
.send()
.await?;
```
A deployment that must pin a model does so by never exposing the knob in the config an agent writes — not by the type being unable to carry one, because there will be other decision services and other models.
A deployment running the Everruns platform configures a separate `UTILITY_TYPESAFE_API_KEY` for its [guardrails](https://everruns.com/docs/capabilities/guardrails/) — a different account from the one an embedding application holds.
### Giving an agent the decisions
Everything above is agentless: your code asks, your code decides. The other half is letting an *agent* classify as part of its own work — checking a claim against a source before citing it, rating a draft before sending it.
`Jev` is the same decisions as a capability, so an agent gets it as a tool:
```rust
use everruns::{Agent, Engine, Jev, Model, OpenAI};
let agent = Agent::builder()
.name("reviewer")
.instructions(
"You review copy. When asked how something reads, measure it with \
jev_decision and report the numbers rather than judging by eye.",
)
.model(Model::new("gpt-6.1-sol", OpenAI::from_env()?))
.capability(Jev::from_env()?)
.build()?;
let session = Engine::new().create(agent);
let turn = session
.run("Rate this subject line for pushiness: 'Act now before it is too late'")
.await?;
```
The agent calls `jev_decision`, writing its own questions about whatever it is looking at, and gets the same calibrated numbers back. It is the identical tool the hosted [TypeSafe integration](https://everruns.com/docs/integrations/typesafe/) gives platform agents — same name, same schema — so behavior matches whether you embed the Framework or run on Everruns.
Which one to reach for:
| | you decide | the agent decides |
| ------------ | -------------------------------------- | --------------------------------- |
| **who asks** | your code writes the questions | the model writes the questions |
| **use** | `Decisions` | the `Jev` capability |
| **good for** | a policy check, a routing rule, a gate | verification inside a longer task |
### What stays with an agent
A decision owns no session, no history, and no workspace, and runs no tools. Reach for an [agent](https://everruns.com/docs/framework/agents/) as soon as the work needs any of those. Typed output guarantees the interface, not the truth: validate thresholds against your own data and consequences.
### Testing without a credential
`Decisions::simulated` returns a fixed number from an in-process stub. It is a **test double**, not a local decisions: it runs no inference, reads nothing from the state you pass it, and is not a way to classify without a provider. It exists so tests and examples can assert on the code around a decision without a network call or an API key.
Real work always goes through a decision service: `TypeSafeAI` above, or your own `DecisionsService`.
```rust
use everruns::Decisions;
let p = Decisions::simulated(0.93)
.probability("Does this convey urgency?", "Two hours on hold.")
.await?;
assert!(p > 0.9);
```
Because the answer is fixed, a simulated decision proves your threshold logic runs — never that a real decision service would return that number.
The runnable version of this section is [`direct_decisions.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/direct_decisions.rs). It uses the stub by default so it runs with no key; pass `--live` (with `--features typesafe` and `TYPESAFE_API_KEY` set) to send the same questions to a real decision service. [`agent_decisions.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/agent_decisions.rs) does the same for the agent path.
### Shared provider accounts for decisions
`Decisions::from_registry` accepts a `ProviderRegistry` and an explicit `ModelSpec`. Select `ModelSpec::on("typesafe", "jev-1.13.0")` for direct TypeSafe, or `ModelSpec::on("openrouter", "jev-1.13.0")` for OpenRouter. The OpenRouter driver maps that explicit alias to `typesafe/jev-1.13`; other IDs remain opaque. Providers hold authentication once, so their chat and decision drivers use the same account. Returned model IDs report the snapshot that actually answered.
`Jev::with_provider(provider, model_spec)` supplies that same account to a Framework capability. Use its exact provider key in the model spec. Existing `Jev::new` and `Jev::with_client` conveniences remain available for applications holding a direct TypeSafe key.
---
# Durable Object hosting reference
> Run ordinary Everruns Rust agents with durable admission, leases, event logs and alarm-driven recovery owned by a Durable Object.
Source:
The source-only `everruns-durable-object` reference demonstrates the [pluggable session hosting SPI](https://everruns.com/docs/framework/hosting/) beneath `everruns::Engine`. It is independently resolved and is not a published Framework dependency or a first-class Cloudflare service integration. Application code defines ordinary Framework Agents and uses the same Session API on native execution and Wasm inside an object. The reference and deployment APIs remain experimental; their guarantees cover the failure domains exercised by the qualification tests.

Create an Engine with `ObjectHosting::new(client)` and register trusted Agent builds under immutable `AgentBuild` identities. `create_registered` asynchronously commits a session binding at a caller-retained session ID. `Session::send_with_id` commits input, its deduplicated receipt and an independent wakeup obligation. Retain those identities across uncertain transport responses.
Admission precedes turn assignment. Reopen an admission by its message identity to observe pending assignment, a durable wait, suspension or finalized outcome. Dropping a handle or SSE connection does not cancel accepted work. Approval and tool answers name their exact logical turn; cancellation names a retained input. Acceptance of cancellation is separate from reaching a cancelled terminal state.
Framework start/completion hooks run on the executor under the same ownership epoch. Invocation records conservatively handle uncertain external effects; explicitly idempotent hooks can opt into recovery. A turn result becomes final only after its awaited Framework completion hooks settle.
## Native and isolate placements
The [native example](https://github.com/everruns/everruns/tree/main/crates/durable-object/examples) registers an ordinary Agent on Engine and supplies that Engine to an authenticated `Executor`. Activation validates the authoritative binding and retained registry. The [Wasm application](https://github.com/everruns/everruns/tree/main/examples/durable-object-agent) uses the same [Agent definition](https://github.com/everruns/everruns/blob/main/crates/durable-object/examples/shared/agent.rs), which imports only `everruns`. Its Workers `/v1` adapter calls Engine for creation, admission and queries, and uses bounded canonical pages for SSE replay. JavaScript handles transport, storage and alarms; Rust owns agent and tool policy.
[serve on celld](https://everruns.com/docs/framework/serve-celld/) selects this hosting profile and exposes its same Engine for native activation. The celld isolate bootstrap imports the shared Wasm application. There is no application runtime factory or celld step machine to implement.
## Hosting boundaries
Keep API, authority and executor credentials separate. Tenant scope comes from trusted host configuration. Privileged authority credentials stay on trusted hosts; browsers use the user API. Production native authority/executor endpoints require HTTPS. Retain exact builds and Environment references while sessions need them; unavailable behavior suspends rather than silently selecting the latest.
Every canonical/session-state mutation checks an unexpired epoch at the authority. An old owner cannot commit after replacement. External effects still need their own idempotency or reconciliation: a started tool with at-most-once recovery is reported interrupted when its effect is uncertain, and is not blindly repeated. This does not promise exactly-once external effects.
Workspace and compute services are explicit retained Environment factories. A journal does not preserve ephemeral container files, secrets, background tasks, channels or schedules. Unsupported requirements must be refused before admission. The adapter’s default secret backend refuses plaintext storage.
See the [adapter setup and tests](https://github.com/everruns/everruns/tree/main/crates/durable-object) for workerd, Cloudflare and replicated celld qualification. A test qualifies its specific revision, placement and failure domain; local fleet results do not establish production bucket or network-partition guarantees.
---
# Events and Cancellation
> Subscribe to live Framework session events, replay durable events, cancel a turn, and work with the canonical event protocol.
Source:
Subscribe before sending a message to observe its live event projection:
```rust
use everruns::{Agent, Engine, OpenAI};
let agent = Agent::builder()
.instructions("Be concise.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.build()?;
let session = Engine::new().create(agent);
let mut events = session.events();
let pending = session.send("Start.").await?;
while let Some(event) = events.recv().await? {
println!("{}", event.event_type());
if event.kind.is_terminal() {
break;
}
}
let turn = pending.wait().await?;
assert!(turn.success);
```
Known events have typed `SessionEventKind` values. Unknown canonical event types are preserved as `Other` with their payload, so the projection does not silently drop information. The feed is live and non-blocking: `send` starts execution in the session’s background actor, a slow or dropped consumer does not stop the turn, and the feed is not a durable replay API.
Use [lifecycle hooks](https://everruns.com/docs/framework/lifecycle-hooks/) instead when application work must be awaited at an execution boundary or its failure must affect the run.
See [Canonical events](#canonical-events) below for canonical envelopes, live-delta memory bounds, explicit lag handling, ordering, and the durability boundary. Use [Session history and resume](https://everruns.com/docs/framework/sessions/#history-and-resume) to rebuild a bounded persisted transcript after live lag or a process restart.
## Replay durable events
Durable events carry a dense per-session `sequence()` starting at 1. `session.events_after(n)` returns every durable event after sequence `n`, and it keeps working after `Engine::resume` in a new process when the backend persists events (for example `LocalConfig`). `session.events_from(n)` returns an `EventStream` that replays that backlog and then continues live. It never skips or repeats a durable event. Ephemeral deltas have no sequence and pass through live. A client that lagged or reconnected resumes from the last sequence it saw:
```rust
let last_seen = 2;
let mut stream = session.events_from(last_seen).await?;
while let Ok(Some(event)) = stream.try_recv() {
println!("{:?} {}", event.sequence(), event.event_type());
}
```
## Cancel a turn
A message receipt exposes the specific accepting turn, so live applications can cancel without racing against whichever turn is active later:
```rust
let pending = session.send("Start.").await?;
pending.turn().cancel().await?;
let cancelled = pending.wait().await?;
assert!(!cancelled.success);
```
`run_with` retains cancellation-token convenience for request/response calls:
```rust
use everruns::{CancellationToken, RunOptions};
let cancel = CancellationToken::new();
let options = RunOptions::new().cancel_token(cancel.clone());
cancel.cancel();
let turn = session.run_with("Stop before starting.", options).await?;
assert!(!turn.success);
```
Cancellation is cooperative. Cancelling drops the in-flight turn future and tears down tool work through the same runtime path; it does not kill the host process or provide an independent transaction boundary.
## Observability
To send every session’s events to OpenTelemetry or Braintrust, or to your own listener, register it on the Engine; see [Framework observability](https://everruns.com/docs/framework/observability/).
## Canonical events
`Session::events()` installs an in-process subscriber without exposing runtime event buses or core event types. Subscribe before `Session::run()` so the stream sees the turn from its first event.
```rust
use everruns::prelude::*;
let agent = Agent::builder()
.instructions("Answer concisely.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.build()?;
let engine = Engine::new();
let mut session = engine.create(agent);
let mut events = session.events();
let observer = tokio::spawn(async move {
let mut canonical_events = Vec::new();
while let Some(event) = events.recv().await? {
// Recording for replay, so take the canonical envelope: it withholds
// nothing. `as_json()` carries the reviewed projection instead.
canonical_events.push(event.canonical_json().clone());
// Typed rendering for common terminal/service UI concerns.
match event.kind {
SessionEventKind::TextDelta { delta } => print!("{delta}"),
SessionEventKind::ToolStarted { tool_name, .. } => {
eprintln!("starting {tool_name}");
}
SessionEventKind::ToolCompleted {
tool_name,
success,
..
} => eprintln!("{tool_name}: {success}"),
SessionEventKind::TurnFailed { error } => eprintln!("failed: {error}"),
SessionEventKind::TurnCancelled => eprintln!("cancelled"),
_ => {}
}
}
Ok::<_, EventStreamError>(canonical_events)
});
let turn = session.run("hello").await?;
drop(session); // closes the subscriber once buffered events are drained
let recorded = observer.await??;
assert!(!recorded.is_empty());
```
### One protocol, two views
`SessionEventKind` is a convenience projection for application renderers. It promotes assistant output lifecycle and deltas, model reasoning/generation, tool lifecycle/progress/output, and turn terminal states. It is non-exhaustive, so match it with a fallback arm.
`SessionEvent` exposes two surfaces, and which one you want depends on whether you are rendering or recording.
`SessionEvent::as_json()` and `SessionEvent::data()` return the **reviewed** form: the event envelope — id, type, timestamp, optional persisted sequence, correlation context, metadata, tags — with a `data` payload holding only the fields promoted onto `SessionEventKind`. Nothing else reaches it, so a field added inside the runtime cannot become part of the Framework’s public surface, or travel to wherever your application forwards these envelopes, without being promoted first. This is the form to log, forward, or expose to clients.
`SessionEvent::canonical_json()` returns the **canonical** envelope with the complete payload: prompts, tool arguments, tool results, structured assistant messages, and the payloads of event types this version does not recognize. Nothing observable is lost — this is the form for recording, auditing, and replay. It follows the runtime’s internal shape rather than the Framework’s reviewed surface, so treat what you read from it as unstable, and do not forward it anywhere the conversation itself should not go.
Live `output.message.delta` envelopes omit the redundant `data.accumulated` prefix on both surfaces; retaining every growing prefix in a slow subscriber’s buffer would use quadratic memory. Concatenate the typed `TextDelta::delta` values to reconstruct streamed text, or use the subsequent `output.message.completed` event for the complete message. When a provider stream breaks after some text, the runtime retries it under the same message id and sends `OutputStarted` for that id again first: drop the text collected for the message and start over.
Model-generation accounting — model, provider, token counts, cost, and duration — is promoted onto `SessionEventKind::ModelGeneration`, so tracking spend never requires the unstable surface.
This bridge does not define a second wire schema. The canonical event contract, compatibility rules, and lifecycle semantics remain documented in [Events](https://everruns.com/docs/explanation/events/).
### Durability, observation, and derived history
These roles are deliberately separate:
* `EventLog` is the host’s sole durable conversation write authority. It stores complete canonical event envelopes and provides bounded cursor replay.
* `EventSink` is the host’s post-commit, nonblocking live-delivery boundary. `Session::events()` exposes that observation path as an ergonomic `EventStream` subscriber. Neither sink nor subscriber is durable or authoritative.
* `EventHistory` is one read-only message projection rebuilt from `EventLog` replay. It is an index/view, never a second writable message store.
Framework applications read that bounded projection through [`Session::history()`](https://everruns.com/docs/framework/sessions/#history-and-resume). It pages messages from a stable event-log snapshot; it does not maintain or write an independent transcript.
Rebuild a transcript in persisted sequence order from `input.message`, `output.message.completed`, and relevant `tool.completed` events. An `output.message.replaced` event alone creates no history message; the subsequent completed message contains the safe replacement. If a crash leaves a replacement without completion, replay correctly omits that incomplete output. The Framework stream exposes canonical payloads, subject to the live-delta exception above, and introduces no independent writable message history.
Canonical recordings can contain user messages, agent instructions, model inputs, tool arguments, and tool results. Treat them as application data with the same access controls and retention policy as the session itself; do not log them indiscriminately. Model and tool text is untrusted: terminal renderers should strip or escape control sequences, and web renderers should escape it as content rather than interpreting it as markup or commands. Provider credentials are not part of the event protocol.
### Implementing a custom event log
An advanced host can store canonical events itself. `everruns-core` (`host` feature) exposes `EventReader` and `EventLog` as a public SPI: an external crate implements both against its own storage and supplies the result to composition through `HostBackends::with_event_log`. No in-crate access is required, cursors and pages are built with `EventCursor::continuation`, `EventCursor::after`, and `EventPage::new`, which validate the shared invariants.
Three request shapes are distinguished by `EventReadRequest::cursor()`:
* no cursor is an initial read that captures the session’s current high-watermark and reports it as `EventPage::snapshot_high_watermark()`;
* a cursor whose `snapshot_high_watermark()` is `Some` is a continuation pinned to that snapshot, so appends committed later stay invisible and paging neither skips nor duplicates;
* a cursor whose `snapshot_high_watermark()` is `None`, built by `EventCursor::after`, is a poll that captures a fresh snapshot and therefore does observe those later appends.
```rust
use async_trait::async_trait;
use everruns_core::events::{Event, EventRequest};
use everruns_contracts::typed_id::EventId;
use everruns_core::host::{
EventCursor, EventDurability, EventLog, EventLogError, EventPage, EventReadRequest,
EventReader,
};
#[async_trait]
impl EventReader for MyEventLog {
async fn read_page(&self, request: EventReadRequest) -> Result {
let session_id = request.session_id();
let current_high = self.high_watermark(session_id);
let (after, snapshot) = match request.cursor() {
None => (0, current_high),
Some(cursor) => {
if cursor.session_id() != session_id {
return Err(EventLogError::CrossSessionCursor {
detail: "cursor belongs to another session".into(),
});
}
match cursor.snapshot_high_watermark() {
Some(snapshot) if snapshot > current_high => {
return Err(EventLogError::ExpiredCursor {
detail: "cursor snapshot is not available".into(),
});
}
// Pinned continuation, then the polling form.
Some(snapshot) => (cursor.after_sequence(), snapshot),
None => (cursor.after_sequence(), current_high),
}
}
};
let limit = request.limit().get();
let mut events = self.events_in(session_id, after, snapshot, limit + 1);
let has_more = events.len() > limit;
if has_more {
events.pop();
}
let next_cursor = has_more
.then(|| {
let last = events.last().and_then(|event: &Event| event.sequence).unwrap_or(after);
EventCursor::continuation(session_id, last, snapshot)
})
.transpose()?;
EventPage::new(events, next_cursor, snapshot)
}
}
#[async_trait]
impl EventLog for MyEventLog {
async fn append(&self, request: EventRequest) -> Result {
if request.is_ephemeral() {
return Err(EventLogError::InvalidAppend {
detail: "ephemeral events are sink-only".into(),
});
}
// The log owns identity: assign the event id and the next per-session
// sequence, persist, and return the finalized canonical envelope.
let sequence = self.next_sequence(request.session_id);
let event = request.into_event(EventId::new(), sequence);
self.persist(&event)?;
Ok(event)
}
fn durability(&self) -> EventDurability {
EventDurability::CrashDurable
}
}
```
The contract an implementation must uphold:
* an accepted append owns id and sequence assignment and returns the finalized canonical `Event`, visible to the next read of that session;
* durable sequences are unique and strictly increasing per session, and need not be contiguous, gaps are expected when a reader projects an append-only physical log into a filtered logical event sequence;
* a continuation stays pinned to the first page’s high-watermark and cannot observe concurrent appends; a poll cursor can;
* cursor/session mismatches and inconsistent positions return the typed `EventLogError` variants above rather than panicking;
* the log is append-only. There is no truncate, rewind, or mutation contract, and `EventHistory` remains a read-only projection rather than a second writable message store.
The [external event-log example](https://github.com/everruns/everruns/tree/main/crates/everruns/tests/fixtures/external-consumer/event-log) provides a complete implementation in a standalone application.
### Ordering and bounded delivery
A subscriber receives events in channel arrival order and each session has its own stream. The canonical `sequence` field is a replay position, not a live delivery counter: durable events carry `Some(sequence)` and live-only ephemeral events such as streaming deltas carry no sequence. Persisted sequences increase monotonically per session and may have gaps. Ephemeral events do not consume replay positions.
The live stream has a bounded buffer and never applies backpressure to the agent turn. A dropped or slow subscriber cannot stall model or tool execution. If a subscriber falls behind, `recv()` and `try_recv()` return `EventStreamError::Lagged { missed }`; loss is never hidden. The next receive can continue from the oldest retained event, but the renderer must treat its live projection as incomplete.
Streaming deltas are provisional and sink-only. Completed assistant/tool events are authoritative, and an output-replacement event means accumulated text for that message must be discarded. Durable events reach the live sink only after their log append commits; ephemeral events go directly to the sink and never enter history. After live lag, a Framework application can rebuild its persisted transcript with bounded [`Session::history()` pages](https://everruns.com/docs/framework/sessions/#history-and-resume). That projection excludes ephemeral deltas by design. Applications that need raw durable envelopes rather than derived messages can provide and read an `EventLog` through the advanced `everruns-core` (`host` feature) SPI; neither recovery path relies on the in-process subscriber.
### Cancellation and failure
Pass a `CancellationToken` through `RunOptions` to stop a turn. Cancellation produces both a `Turn` with `TurnStopReason::Cancelled` and a correlated `turn.cancelled` event carrying the same `turn_id`.
Runtime failures remain available through `Session::run()`’s outcome/error semantics and the event stream. Subscribe before running and continue draining the stream after the run resolves to retain the terminal failure event and its full structured payload.
The complete runnable example is [`canonical_events.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/canonical_events.rs):
```bash
cargo run -p everruns --example canonical_events
```
---
# Runnable Examples
> Complete Framework programs, maintained and compiled with the everruns crate.
Source:
The [`crates/everruns/examples` catalog](https://github.com/everruns/everruns/tree/main/crates/everruns/examples) contains the maintained public examples. Each imports the `everruns` facade.
## Complete agents
The root-level [`examples`](https://github.com/everruns/everruns/tree/main/examples) catalog contains eight Framework walkthroughs. Each folder includes the program, instructions, fixtures where applicable, and recording scripts. Run them from a repository checkout: their dependencies point to the workspace crates.
`cargo run` uses a real provider and can incur charges. CI tests offline tool behavior and recording logic; it does not establish the quality of a live model’s answer.
| Example | Provider and model | What it does |
| -------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------------------------------------------- |
| [Support Agent](https://everruns.com/docs/framework/examples/support-agent/) | OpenAI `gpt-6.1-sol` | Chooses between MFA recovery, lockout, and browser troubleshooting from facts and policy. |
| [Everruns Support Agent](https://everruns.com/docs/framework/examples/everruns-support-agent/) | Anthropic `claude-opus-5-5` | Searches and reads citable official documentation snapshots. |
| [Coding Review Agent](https://everruns.com/docs/framework/examples/coding-review-agent/) | Anthropic `claude-sonnet-5` | Reads a refund contract and executes a fixed regression before reporting a defect. |
| [Research Agent](https://everruns.com/docs/framework/examples/research-agent/) | OpenRouter `z-ai/glm-5.2` | Searches and fetches primary sources before writing a cited brief. |
| [Incident Commander Agent](https://everruns.com/docs/framework/examples/incident-commander-agent/) | Meta Model API `muse-spark-1.3` | Investigates fixture telemetry and persists an evidence-backed incident update. |
Start with Support for typed tools, Research for reusable capabilities, or Code Review for restricted execution. Each walkthrough shows the agent builder and session loop, explains expected behavior, and documents what remains a fixture.
These are in-memory sessions. For durability itself, use the session-history and workspace examples below. Importable hosted Platform definitions live separately as TOML folders in [`examples/agents`](https://github.com/everruns/everruns/tree/main/examples/agents). See [file-based agents](https://everruns.com/docs/how-to/define-agents-as-files/) for loading, importing and serving them.
## Execution runtimes
| Example | Provider and model | What it does |
| -------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| [Bashkit Repo Agent](https://everruns.com/docs/framework/examples/bashkit-repo-agent/) | OpenAI `gpt-6.1-sol` | Cuts a release in a real repository with the sandboxed Bashkit shell as its only tool, then verifies the result on disk. |
| [Host Shell Agent](https://everruns.com/docs/framework/examples/host-shell-agent/) | OpenAI `gpt-6.1-sol` | Fixes a failing Rust test suite by compiling and running it through real processes inside a kernel-enforced boundary. |
| [Foreman](https://everruns.com/docs/framework/examples/foreman-agent/) | TypeSafe `jev-latest` over an Everruns session, Codex, or yolop | Supervises a live coding session with nine decision questions per reading, and stops, verifies, or finishes it from a deterministic policy. |
## Talking to other companies’ agents
| Example | Provider and model | What it does |
| ------------------------------------------------------------------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`poppy-personal-agent`](https://github.com/everruns/everruns/tree/main/examples/poppy-personal-agent) | OpenAI `gpt-6.1-sol` | Acts as a person’s own agent over the [Personal Agent Protocol](https://everruns.com/docs/features/poppy/): finds a company by its domain, asks its agent about an order, and reports the answer. |
## Serving agents
These run agents as HTTP services with [Serve](https://everruns.com/docs/framework/serve/).
| Example | What it does | Command |
| ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| [`serve/hello`](https://github.com/everruns/everruns/tree/main/examples/serve/hello) | The smallest serve app: one agent, one tool | `cargo run -p serve-example-hello` |
| [`serve/ag-ui`](https://github.com/everruns/everruns/tree/main/examples/serve/ag-ui) | An agent streamed to CopilotKit or any AG-UI client | `cargo run -p serve-example-ag-ui` |
| [`serve/a2a`](https://github.com/everruns/everruns/tree/main/examples/serve/a2a) | Two agents over [A2A](https://everruns.com/docs/framework/a2a/): a researcher served by serve, and a writer that delegates to it. Offline by default | `cargo run -p serve-example-a2a --bin researcher`, then `--bin writer -- "tide pools"` |
| [`serve/voice`](https://github.com/everruns/everruns/tree/main/examples/serve/voice) | A hotel front desk people [call from the browser](https://everruns.com/docs/framework/voice/), with a greeting and a speaking style | `OPENAI_API_KEY=… cargo run -p serve-example-voice` |
## Core crate catalog
| Example | Demonstrates | Command |
| ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| [`capability_configuration.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/capability_configuration.rs) | Typed Compaction and ToolSearch, a code-defined Definition, and a dynamic third-party reference through one entrypoint | `cargo run -p everruns --example capability_configuration` |
| [`model_catalog.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/model_catalog.rs) | Listing a provider’s models, reading their metadata, and running the selection | `cargo run -p everruns --example model_catalog` |
| [`ask_user.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/ask_user.rs) | Structured questions answered by a host responder, and the unattended defaults path, fully offline | `cargo run -p everruns --example ask_user` |
| [`ag_ui_axum.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/ag_ui_axum.rs) | An AG-UI 1.0 run served over axum | `cargo run -p everruns --features ag-ui-axum --example ag_ui_axum` |
| [`workspace_policy.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/workspace_policy.rs) | Safe workspace scopes and trusted starter files, fully offline | `cargo run -p everruns --example workspace_policy` |
| [`direct_llm.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/direct_llm.rs) | One-shot, builder, and streamed model calls with no agent, fully offline | `cargo run -p everruns --example direct_llm` |
| [`direct_decisions.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/direct_decisions.rs) | Typed questions and calibrated answers with no agent, fully offline | `cargo run -p everruns --example direct_decisions` |
| [`agent_decisions.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/agent_decisions.rs) | An agent that classifies with its own questions, offline by default | `cargo run -p everruns --example agent_decisions` |
| [`live_session.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/live_session.rs) | Non-blocking send, automatic steering, and optional waiting, fully offline | `cargo run -p everruns --example live_session` |
| [`hello.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/hello.rs) | Small live-provider agent | `cargo run -p everruns --features openai --example hello` |
| [`production_agent.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/production_agent.rs) | Tools, files, and production-style setup | `cargo run -p everruns --features openai --example production_agent` |
| [`github_monitor.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/github_monitor.rs) | Typed tools and an offline simulation mode | `cargo run -p everruns --features openai --example github_monitor -- --simulate` |
| [`session_work.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/session_work.rs) | Offline session work, leased delivery, and completion wakes | `cargo run -p everruns --example session_work` |
| [`session_history.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/session_history.rs) | Offline durable resume and bounded history pages | `cargo run -p everruns --features local --example session_history` |
| [`engine_sessions.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/engine_sessions.rs) | Concrete Engine ownership, isolated sessions, and engine-scoped resume | `cargo run -p everruns --example engine_sessions` |
| [`workspace_heads.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/workspace_heads.rs) | Isolated Git workspace heads, Environment binding, and durable reopening | `cargo run -p everruns --features local --example workspace_heads -- /path/to/repo /path/to/state` |
| [`canonical_events.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/canonical_events.rs) | Offline bounded recording and typed rendering of live events | `cargo run -p everruns --example canonical_events` |
| [`explicit_communication.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/explicit_communication.rs) | Offline agent with explicit communication: sent messages apart from working notes | `cargo run -p everruns --example explicit_communication` |
| [`framework_observability.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/framework_observability.rs) | Engine-wide OpenTelemetry and optional Braintrust export with explicit shutdown flushing | `cargo run -p everruns --features otel,braintrust --example framework_observability` |
| [`subagents.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/subagents.rs) | Public facade composition for delegated work | `cargo run -p everruns --features openai --example subagents` |
| [`observe_and_cancel.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/observe_and_cancel.rs) | Live events and cancellation | `cargo run -p everruns --features openai --example observe_and_cancel` |
| [`advanced_capability.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/advanced_capability.rs) | Code-defined capability through the unified `capability(...)` entrypoint | `cargo run -p everruns --features openai --example advanced_capability` |
| [`lifecycle_hooks.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/lifecycle_hooks.rs) | Awaited agent, turn, tool, and completion handlers | `cargo run -p everruns --features openai --example lifecycle_hooks` |
Live-provider modes use `gpt-6.1-sol` and require `OPENAI_API_KEY`. `capability_configuration`, `canonical_events`, `direct_llm`, `engine_sessions`, `explicit_communication`, `live_session`, `session_work`, `workspace_heads`, `workspace_policy`, and `session_history` are fully offline; the GitHub monitor also offers a simulated GitHub flow:
```bash
cargo run -p everruns --features openai --example github_monitor -- --simulate
```
For copyable command details and behavior notes, use the [example catalog](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/README.md) next to the source. Examples that demonstrate low-level host internals remain advanced-host examples, not alternative Framework entrypoints.
---
# Bashkit Repo Agent
> Modify and verify a disposable repository through a sandboxed shell.
Source:
[Browse the complete example](https://github.com/everruns/everruns/tree/main/examples/bashkit-repo-agent).
Cut a release in a disposable repository through the sandboxed [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/), then verify every claimed change directly from the host. This is a real `gpt-6.1-sol` agent, not a scripted turn.

## What you learn
How to mount a narrow read-write workspace, give an agent one execution capability, accept an interactive task, and treat independent host assertions as the success condition.
## Scenario and expected outcome
The bundled fixture is a two-crate Cargo workspace with three changelog fragments. The agent must cut release `0.2.0`: update both package versions and the path-dependency pin, create today’s changelog section, preserve the `0.1.0` history, and remove the folded fragments.
After the turn, Rust code reopens the real working copy and fails the process if any invariant is false. A confident model answer cannot make a broken release pass.
## Run it
Clone the repository and run from its root:
```bash
export OPENAI_API_KEY="your-key"
cargo run -p everruns-bashkit-repo-agent
```
Type the task interactively:
```bash
cargo run -p everruns-bashkit-repo-agent -- --interactive
```
The default workspace is temporary and removed on exit. Pass a scratch directory to inspect the result afterwards:
```bash
cargo run -p everruns-bashkit-repo-agent -- /tmp/release-run
cargo run -p everruns-bashkit-repo-agent -- --interactive /tmp/release-run
```
The configured model is `gpt-6.1-sol`. Provider access and funded credits are required; missing credentials, unsuccessful turns, and failed disk assertions exit nonzero.
Never pass a repository you care about: the example materializes its fixture into the target and the agent may change anything inside the mount.
## Build the agent
Read-write access is explicit—the default workspace policy is read-only.
```rust
pub fn build(provider: impl Into, workspace: &Path) -> Result {
Agent::builder()
.name("bashkit-repo-agent")
.instructions(include_str!("resources/instructions.md"))
.provider(provider)
.model(MODEL)
.max_iterations(12)
.workspace(workspace)
.workspace_policy(WorkspacePolicy::read_write())
.capability(BashkitShell::new())
.build()
}
```
## Run and verify
The shared observer displays a bounded shell timeline and waits for a successful turn. The host then verifies the mounted files itself.
```rust
// OPENAI_API_KEY, declared by the OpenAI driver itself.
let agent = agent::build(everruns::OpenAI::from_env()?, &workspace)?;
let engine = Engine::new();
let session = engine.create(agent);
demo::run(&session, &request).await?;
verify_release(&workspace, &release_date)?;
```
Use `session.send_and_wait(&request).await?` when a live tool timeline is not needed.
## How Bashkit behaves
Bashkit interprets shell scripts in-process against `/workspace`. The model gets no host filesystem outside that mount, network, Git credentials, or subprocess execution. Commands, loops, output, and script size are bounded. Repository text is treated as untrusted data rather than agent instructions.
## Validate it
```bash
cargo test -p everruns-bashkit-repo-agent
bash examples/bashkit-repo-agent/demo/record.sh --check
```
These checks validate argument handling, fixture state, host assertions, and the live-demo contract without provider credentials. They do not grade model quality.
## Demo and recording
The screencast types the release task into the interactive binary, displays real `bashkit_shell` calls, and ends with host-side checks over the mutated files. It does not replay prepared output. Read the [captured transcript](https://github.com/everruns/everruns/blob/main/examples/bashkit-repo-agent/demo/transcript.txt) at your own pace.
With VHS, ffmpeg, a VHS-compatible browser, and funded OpenAI credentials:
```bash
bash examples/bashkit-repo-agent/demo/record.sh
```
The script uses `OPENAI_API_KEY` when exported, otherwise Doppler project `everruns-dev`, config `dev`. It updates the GIF and transcript only after the model turn and host verification succeed.
## Adapt it safely
Replace the fixture, request acceptance criteria, and verifier together. Keep the mount disposable and narrow, start read-only unless mutation is required, and verify consequential claims outside the model/tool boundary.
## Boundaries
This demonstrates one sandboxed repository mutation, not a general coding agent. It cannot fetch dependencies, run native programs, commit, or push. The Framework session is in-memory.
---
# Coding Review Agent
> Tool-based code inspection, a tightly scoped execution tool, and distinguishing test failure from tool failure.
Source:
[Browse the complete example](https://github.com/everruns/everruns/tree/main/examples/coding-review-agent).
Review a refund implementation against a written contract, then execute a bundled regression test before reporting a defect. A proposed test is not treated as proof: the agent gets actual compiler and test output.
## What you learn
Tool-based code inspection, a tightly scoped execution tool, and distinguishing test failure from tool failure.
## Scenario and expected outcome
Two refunds of 1,000 cents are requested against a 1,000-cent payment. Each individual call caps its own amount, but the implementation retains no cumulative refunded balance.
The regression fails with `left: 2000` and `right: 1000`. The review should connect that observed failure to the missing cumulative-refund state, describe the over-refund risk, and propose a minimal fix. It must not claim the code was changed or fixed.
## Run it
Install Rust/Cargo (including rustc), clone the repository, and run from its root. These folders are self-contained **within the workspace**: their Cargo manifests reference the local Framework crates, so copying one folder alone is not sufficient.
```bash
git clone https://github.com/everruns/everruns.git
cd everruns
export ANTHROPIC_API_KEY="your-key"
cargo run -p everruns-coding-review-agent
```
The configured model is `claude-sonnet-5`. Provider access and funded credits are required; a model identifier alone does not grant access. Keep keys in your environment, not in source control. Missing variables, provider errors, or unsuccessful turns exit nonzero.
Try a contrasting question:
```bash
cargo run -p everruns-coding-review-agent -- "Read the contract and test. Reproduce the cumulative refund bug and explain the smallest safe fix."
```
## Build the agent
This builder combines the agent instructions, tools, and model. Tools/capabilities supply evidence and actions; the model chooses how to use them.
```rust
let agent = Agent::builder()
.name("coding-review-agent")
.instructions(include_str!("instructions.md"))
.provider(everruns_drivers::anthropic::from_env("anthropic")?)
.model(MODEL)
.max_iterations(12)
.tool(tools::inspect_change())
.tool(tools::run_regression())
.build()?;
```
## Send, observe, and wait
The shared demo helper subscribes before sending, filters events to this turn, shows bounded tool previews, waits for completion, and rejects unsuccessful turns. It changes presentation only; use `session.send_and_wait(question).await?` when you do not need the live tool timeline.
```rust
let engine = Engine::new();
let session = engine.create(agent);
println!("MODEL: {MODEL}");
demo::run(&session, question).await?;
```
This engine is in-memory. It does not demonstrate durable session storage; the Everruns Support example can explain that API, but does not itself persist its session.
## How the tools work
`inspect_change` can read only `sample_payment.rs`, `contract.md`, and `regression.rs`. `run_regression` invokes `rustc --test` on that fixed trusted fixture, runs the temporary binary, and returns its exit code and assertion output. Compilation and execution have timeouts; temporary files are removed automatically.
## Validate the behavior
```bash
cargo test -p everruns-coding-review-agent
python3 examples/coding-review-agent/src/render_demo.py --check
```
The offline test compiles and runs the bundled regression and asserts the observed failure. `cargo test -p everruns-coding-review-agent` passes because it verifies reproduction of the intentionally buggy fixture. A failing subprocess is expected evidence, not a failing example test.
CI runs these offline checks without provider credentials. Live model behavior is evaluated separately; passing tests is not proof of answer quality.
## Demo and recording

Read the [captured transcript](https://github.com/everruns/everruns/blob/main/examples/coding-review-agent/src/demo.txt) at your own pace. The GIF is a paginated replay of an actual provider run, with waiting time removed. It is not interactive and does not show model reasoning. Result excerpts are shortened only for display.
With credentials exported and Python 3, VHS, ffmpeg, and a VHS-compatible browser installed:
```bash
cd examples/coding-review-agent
bash src/record.sh
```
The script captures a successful run, generates correctly wrapped pages and page durations, and renders `src/demo.gif`. It preserves the previous transcript when the provider run fails. To replay an existing transcript without another model call, run `(cd src && python3 render_demo.py && vhs demo.tape)`. `src/demo.txt` retains the displayed output; `.demo-pages/` is generated and ignored. Inspect results before sharing: public/demo data is safe here, but adapting tools may expose private data.
## Adapt it
Replace the fixture with a trusted review checkout and a restricted test selection. Use a sandbox before accepting arbitrary repositories, generated code, or model-selected commands. Add a second verified run after applying a fix in a separate approved workflow.
## Boundaries
This is one deliberately buggy, trusted fixture—not a general-purpose coding agent. The execution tool takes no shell command or user-supplied path, and cannot edit code. Rust including `rustc` must be installed.
---
# Everruns Support Agent
> Search and read citable documentation before answering Framework questions.
Source:
[Browse the complete example](https://github.com/everruns/everruns/tree/main/examples/everruns-support-agent).
Answer Framework questions by searching and reading a small, inspectable corpus of official documentation. The agent retrieves evidence before answering instead of routing keywords to prewritten responses.

## What you learn
A two-step search/read tool interface, citable evidence, and an explicit boundary around the available knowledge.
## Scenario and expected outcome
The default question asks how to resume a durable session after restarting a process. The agent should search for persistence and session history, read the relevant pages, then explain the local catalog, persisted `SessionId`, and agent reattachment requirements with source URLs.
It must not imply that `Engine::new()` survives a restart. When the corpus lacks an answer, it should say so instead of inventing one.
## Run it
Install Rust/Cargo, clone the repository, and run from its root. This folder is self-contained **within the workspace**: its Cargo manifest references local Framework crates, so copying the folder alone is not sufficient.
```bash
git clone https://github.com/everruns/everruns.git
cd everruns
export ANTHROPIC_API_KEY="your-key"
cargo run -p everruns-framework-support-agent
```
The configured model is `claude-opus-5-5`. Provider access and funded credits are required. Keep keys in your environment, not in source control. Missing credentials, provider errors, and unsuccessful turns exit nonzero.
Try another question:
```bash
cargo run -p everruns-framework-support-agent -- "How do I register a custom provider?"
cargo run -p everruns-framework-support-agent -- "How can a tool return a structured error?"
```
Or type a question interactively:
```bash
cargo run -p everruns-framework-support-agent -- --interactive
```
## Build the agent
Tools retrieve evidence; Opus decides what to search, read, and explain.
```rust
pub fn build(provider: impl Into) -> Result {
Agent::builder()
.name("everruns-support-agent")
.instructions(include_str!("resources/instructions.md"))
.provider(provider)
.model(MODEL)
.max_iterations(12)
.tool(tools::search_docs())
.tool(tools::read_doc())
.build()
}
```
## Send, observe, and wait
The shared demo helper subscribes before sending, shows bounded tool previews, waits for completion, and rejects unsuccessful turns. Use `session.send_and_wait(&question).await?` when a live tool timeline is unnecessary.
```rust
// ANTHROPIC_API_KEY, declared by the Anthropic driver itself.
let agent = agent::build(everruns_drivers::anthropic::from_env("anthropic")?)?;
let engine = Engine::new();
let session = engine.create(agent);
println!("MODEL: {}", agent::MODEL);
demo::run(&session, &question).await?;
```
This engine is in-memory. The agent can explain durable sessions from its corpus, but the example itself does not persist its session.
## How the tools work
`search_docs` ranks matches against five bundled pages and returns page IDs, public URLs, and matching excerpts. `read_doc` accepts only those page IDs and returns the complete snapshot. It never accepts an arbitrary filesystem path.
## Validate the behavior
```bash
cargo test -p everruns-framework-support-agent
bash examples/everruns-support-agent/demo/record.sh --check
```
Tests cover content-based search, empty and unmatched queries, complete citable reads, arbitrary-path rejection, and interactive input validation. They do not grade the model’s answer; compare a live response with the expected outcome above.
CI runs the offline checks without provider credentials. Live model behavior is evaluated separately.
## Demo and recording
The screencast types a question into the same interactive binary shown above, then displays the actual Opus tool calls and answer. VHS hides most provider wait time but does not replace the model or tools with scripted output. Read the [captured transcript](https://github.com/everruns/everruns/blob/main/examples/everruns-support-agent/demo/transcript.txt) at your own pace.
With credentials exported and VHS, ffmpeg, and a VHS-compatible browser installed:
```bash
bash examples/everruns-support-agent/demo/record.sh
```
The script uses an exported `ANTHROPIC_API_KEY` when present, otherwise Doppler project `everruns-dev`, config `dev`. It updates `demo/demo.gif` and `demo/transcript.txt` only after a successful turn.
## Adapt it
Replace the bundled pages with a versioned documentation index while retaining separate search and read operations. Attach source/version metadata, restrict reads to authorized documents, and treat retrieved text as untrusted evidence rather than instructions.
## Boundaries
The corpus is a five-page snapshot from 2026-09-08, not a live search of the docs site. Provenance is recorded in the [corpus README](https://github.com/everruns/everruns/blob/main/examples/everruns-support-agent/src/resources/docs/README.md), and the snapshot can lag current APIs.
---
# Foreman
> A decision service supervising a coding agent it never has to stop.
Source:
[Browse the complete example](https://github.com/everruns/everruns/tree/main/examples/foreman-agent).
A fast decision service watching a slow coding agent, and a policy in ordinary Rust deciding what to do about the numbers. A Framework port of [thruwire/foreman](https://github.com/thruwire/foreman), which placed [TypeSafe’s Jev](https://docs.typesafe.ai/introduction) above a Codex worker and asked whether semantic supervision can run *while* the work happens.

## What you learn
How to run a worker session and observe it at the same time: a [`Decisions`](https://everruns.com/docs/framework/direct-model-calls/#decisions) turning bounded evidence into nine probabilities in one request, and a deterministic policy that owns every threshold, every limit, and the closed vocabulary of things the supervisor may do.
## The two loops
The worker keeps its own loop — an Everruns session, or an external CLI in a child process. `session.send` returns a receipt immediately, and a child process is simply left running; either way the supervisory loop reads the evidence beside the live work. Activity is debounced to a floor, and only a worker finishing bypasses it. Nothing stops for the factory to think, and a test holds that claim: it counts readings taken while a worker’s turn is unresolved and fails if supervision waits its turn.
## What it assesses
Five questions describe the job (`implementation_complete`, `tests_sufficient`, `requirements_satisfied`, `needs_verification`, `ready_to_finish`) and four describe the floor right now (`meaningful_progress`, `worker_stuck`, `work_off_track`, `needs_human`). Each is a Noul — the probability that a yes/no statement is true — and all nine ride one request, because questions in a decision are answered independently and in parallel.
## What it may do about them
The decision service only estimates. The policy decides, safety and hard limits before productivity: escalate when a person is needed or the iteration ceiling is reached, stop a worker that is off track or stuck, retry once after a stop, finish when the completion thresholds hold and verification is resolved, start one independent verifier when a check is warranted, otherwise start or continue work. Thresholds and limits are Foreman’s defaults and are overridable through `FOREMAN_*` environment variables.
## What it looks at
Never the repository. One bounded snapshot per reading: worker status, elapsed time, tool calls and output tails, `git status`, a bounded `git diff`, the untracked paths a diff cannot show, recent session events, verification results, and the previous assessment and decision. An unbounded observation would make supervision as slow as the work it is watching.
That snapshot goes to the decision service on every reading, so a bounded slice of the repository leaves the machine on every run — point `--repo` at a private repository only if that is acceptable for it. `demo` works on a fixture it materializes itself, so it carries nothing of yours. The repository content in an observation is also untrusted input to the decision service, and that it can only produce a number is the point: the decision service never names an action, and every action the policy can take is in one readable file.
## Run it
Foreman’s own two entry points, and they mean the same things here:
```bash
cargo run -p everruns-foreman-agent --bin foreman -- demo
foreman run --repo ./my-project --job "Add rate limiting, and test it."
```
Both are real runs — same worker, same decision service, same credentials. The only difference is who chose the repository and the job:
| Command | Repository | Job | Needs |
| --------------- | ------------------------------------------- | ----------------------- | --------------------------------- |
| `foreman demo` | a bundled fixture, in a temporary directory | one it ships with | `TYPESAFE_API_KEY` + the worker’s |
| `foreman run …` | yours, named by `--repo` | yours, named by `--job` | the same |
`demo` exists because a fixed starting state makes the ending checkable: the job names a rate schedule, so at the end the repository either prices by weight or it does not. Those checks run at the bottom of the run and read the files, not the supervisor’s opinion of them.
`demo` writes its fixture into a temporary directory unless `--repo` says otherwise, and `run` never writes a fixture at all — `--repo` is your project, and the only thing that touches it is the worker. It will be modified.
## One supervisor
There is one, and it is always real: a `Decisions`, a budget, one request, nine answers. There is no offline mode and no second supervisor with fabricated numbers — every run asks a vendor the nine questions. CI cannot, so the test suite substitutes a different `DecisionsService`, the Framework’s own seam for answering typed questions without a vendor, rather than adding a branch to the supervisor. The stub receives the observation as JSON exactly as a vendor’s service does and answers from a table keyed by what is on the floor, so the test exercises the whole request path rather than bypassing it.
## The supervisor runs the tests itself
It already runs `git` rather than asking the worker what changed, and `--tests` applies the same reasoning to the suite: a worker reporting its own green tests is a claim, and an independently run result is a fact. The command runs in a networkless Docker container with no inherited host environment, over a read-only repository mount copied to isolated temporary storage. Docker is therefore required when `--tests` is used. The result lands in the observation as `test_results` — the field Foreman declares and never fills — and `tests_sufficient` moves on it. The suite runs once as a baseline before any worker starts, then whenever the floor is quiet; between times the last result is carried with its age, because a stale pass should not read as a fresh one.
## Who does the work
`--worker` picks the crew. All three are watched identically, because what the supervisor reads is a bounded observation and the strongest evidence in one — the repository’s own diff — is gathered by the host either way.
| `--worker` | What runs | Independent verification |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `session` (default) | An Everruns session on the [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/), `meta/muse-spark-1.3-contributor` through OpenRouter | A second session under the default read-only workspace policy |
| `codex` | `codex exec --cd … --sandbox workspace-write --color never --json …`, the line Foreman itself runs | The same CLI with `--sandbox read-only` |
| `yolop` | `yolop -C … -p …`, its one-shot print interface | Mission only — yolop publishes no read-only mode |
Anything else is a template: `--worker-command "mycli --cd {repo} --task {mission}"`, where both placeholders are substituted as whole arguments so no shell sees either.
A session is observed through its own canonical event stream, which arrives already typed. A CLI offers none of that, so an external worker is observed through stdout and stderr, and a JSONL line that names its own `type` counts as a step.
## The floor
The bundled fixture is a small shell project that prices every parcel at one flat rate; the job is to replace that with weight tiers and cover the boundaries. Shell, deliberately: `bash tests/run.sh` needs no framework, no interpreter and no network, so the same suite runs inside the Bashkit sandbox, on the host, and inside an external agent. On the session crew the coding worker mounts it read-write and the verifier mounts the same directory under the default read-only policy, so “independent check” is a property of the mount rather than a request in a prompt. The supervisor runs `git` itself rather than asking the worker what it did, which is also why an external CLI is supervised just as well as a session.
## What the Framework changes
The architecture is Foreman’s; the runtime underneath it is not. A worker is a session rather than a subprocess, so stopping one is a cooperative turn cancellation instead of a signal to a process group. Evidence is the canonical event stream rather than parsed JSONL. A retry is a fresh session over the same workspace. Read-only verification is a workspace policy. Steering exists — sending into a live turn applies at the next iteration boundary — and is deliberately left out of the policy’s vocabulary so the comparison with the original stays honest.
## Limits
This is an architectural experiment, and porting it does not make it a proven one. Decisions accuracy for this use is unproven and the thresholds are uncalibrated: false positives stop useful workers, false negatives let bad work continue. Observations are bounded and therefore incomplete. One coding worker runs at a time, a verifier reports evidence rather than proof, and the session state is in memory.
---
# Host Shell Agent
> Fix a failing Rust test suite by compiling and running it, inside a kernel-enforced boundary.
Source:
[Browse the complete example](https://github.com/everruns/everruns/tree/main/examples/host-shell-agent).
Fix a failing Rust test suite by compiling and running it, through the [Host Shell](https://everruns.com/docs/capabilities/host-shell/) capability: real processes on this machine, bounded by a kernel policy. This is a real `gpt-6.1-sol` agent, not a scripted turn.
## What you learn
How to give an agent the machine it is running on without giving it the machine: one execution capability, a kernel-enforced write and network boundary, and independent host assertions as the success condition.
This is the example the sandboxed [Bashkit Shell](https://everruns.com/docs/capabilities/bashkit-shell/) cannot be. Compiling a crate and running its test binary needs a real toolchain and real child processes; an in-process interpreter over a virtual filesystem has neither.
## Scenario and expected outcome
The bundled fixture is a small zero-dependency crate whose `chunk_count` drops the partial final chunk, so two tests fail. The agent must run the suite, read the failure, fix the source, and re-run until it passes.
Before the agent starts, the example probes the same containment provider the capability uses and prints what it found: a write inside the workspace succeeds, a write outside it is refused by the kernel, an outbound socket is refused by the kernel. Not a description of the boundary, the boundary.
After the turn, Rust code re-runs `cargo test` from the host and fails the process if the suite is still red, if an assertion was edited away, or if a test was marked `#[ignore]`. A confident model answer cannot make a red suite pass.
## Run it
Clone the repository and run from its root:
```bash
export OPENAI_API_KEY="your-key"
cargo run -p everruns-host-shell-agent
```
Type the task interactively, or point it at a directory you keep:
```bash
cargo run -p everruns-host-shell-agent -- --interactive
cargo run -p everruns-host-shell-agent -- /tmp/chunker
```
With no directory, the fixture is materialized into a fresh temporary one and discarded afterwards.
## Requirements
Kernel containment needs macOS, or Linux with Landlock ABI v3 fully enforced (Linux 6.2 or a backport). On a kernel that cannot enforce it, the provider fails closed and the example says so instead of running uncontained.
On Linux the policy is applied by a helper process, and this example is its own: `main` routes a `__sandbox-exec` re-exec into the containment worker and names that through `SandboxLauncher::ReexecSelf`, which is what a single-binary embedder does. Nothing else needs to be on disk.
---
# Incident Commander Agent
> Multi-tool evidence gathering, distinguishing correlation from causation, and a narrowly scoped append-only side effect.
Source:
[Browse the complete example](https://github.com/everruns/everruns/tree/main/examples/incident-commander-agent).
Investigate a fictional checkout alert using contrasting metrics, deployment history, logs, and a runbook; then persist an evidence-backed status update. The agent must investigate before recommending action.
## What you learn
Multi-tool evidence gathering, distinguishing correlation from causation, and a narrowly scoped append-only side effect.
## Scenario and expected outcome
Checkout errors rise from 0.2% to 18.4% after v43 reduces the payment timeout from 2,000 ms to 200 ms. Catalog traffic and database metrics stay normal; sampled logs show requests timing out just beyond the new deadline.
Read metrics, deployments, logs, and the runbook. Identify the timeout reduction as a likely cause, not proven causation. Propose on-call ownership and approval for rollback consideration. Record that conclusion locally without claiming any production mitigation occurred.
## Run it
Install Rust/Cargo, clone the repository, and run from its root. These folders are self-contained **within the workspace**: their Cargo manifests reference the local Framework crates, so copying one folder alone is not sufficient.
```bash
git clone https://github.com/everruns/everruns.git
cd everruns
export MODEL_API_KEY="your-key"
cargo run -p everruns-incident-commander-agent
```
The configured model is `muse-spark-1.3`. Provider access and funded credits are required; a model identifier alone does not grant access. Keep keys in your environment, not in source control. Missing variables, provider errors, or unsuccessful turns exit nonzero.
Try a contrasting question:
```bash
cargo run -p everruns-incident-commander-agent -- "Investigate checkout and explain what evidence argues against a database-wide incident. Record a concise update."
```
## Build the agent
This builder combines the agent instructions, tools, and model. Tools/capabilities supply evidence and actions; the model chooses how to use them.
```rust
let agent = Agent::builder()
.name("incident-commander-agent")
.instructions(include_str!("instructions.md"))
.provider(everruns_drivers::meta::from_env("meta")?)
.model(MODEL)
.max_iterations(12)
.tool(tools::inspect_evidence())
.tool(tools::record_incident_update())
.build()?;
```
## Send, observe, and wait
The shared demo helper subscribes before sending, filters events to this turn, shows bounded tool previews, waits for completion, and rejects unsuccessful turns. It changes presentation only; use `session.send_and_wait(question).await?` when you do not need the live tool timeline.
```rust
let engine = Engine::new();
let session = engine.create(agent);
println!("MODEL: {MODEL}");
demo::run(&session, question).await?;
```
This engine is in-memory. It does not demonstrate durable session storage; the Everruns Support example can explain that API, but does not itself persist its session.
## How the tools work
`inspect_evidence` exposes only four named fixture categories. `record_incident_update` appends a non-empty update of at most 500 UTF-8 bytes to this example’s `src/incident.log`, normalizing newlines. There is deliberately no production rollback tool.
## Validate the behavior
```bash
cargo test -p everruns-incident-commander-agent
python3 examples/incident-commander-agent/src/render_demo.py --check
```
Tests verify that updates survive multiple writes, oversized/empty updates are rejected before a file is created, and evidence access is scoped. A live run should show all evidence reads followed by a persisted update that matches the observed facts.
CI runs these offline checks without provider credentials. Live model behavior is evaluated separately; passing tests is not proof of answer quality.
## Demo and recording

Read the [captured transcript](https://github.com/everruns/everruns/blob/main/examples/incident-commander-agent/src/demo.txt) at your own pace. The GIF is a paginated replay of an actual provider run, with waiting time removed. It is not interactive and does not show model reasoning. Result excerpts are shortened only for display.
With credentials exported and Python 3, VHS, ffmpeg, and a VHS-compatible browser installed:
```bash
cd examples/incident-commander-agent
bash src/record.sh
```
The script captures a successful run, generates correctly wrapped pages and page durations, and renders `src/demo.gif`. It preserves the previous transcript when the provider run fails. To replay an existing transcript without another model call, run `(cd src && python3 render_demo.py && vhs demo.tape)`. `src/demo.txt` retains the displayed output; `.demo-pages/` is generated and ignored. Inspect results before sharing: public/demo data is safe here, but adapting tools may expose private data.
## Adapt it
Replace each fixture with a read-only monitoring/deployment API scoped to the relevant service. Keep investigation separate from action. Add explicit approval and an audit trail before introducing any production mutation.
## Boundaries
All telemetry is fictional. The log is a real local append-only artifact, ignored by Git; it contains exercise text and does not change production. Filesystem permissions and rotation are the application’s responsibility.
---
# Research Agent
> Composing the first-party Brave Search and WebFetch capabilities, handling external evidence, and reporting uncertainty.
Source:
[Browse the complete example](https://github.com/everruns/everruns/tree/main/examples/research-agent).
Search for relevant material, open primary sources, and synthesize a short, cited research brief. The model must read source content rather than treating search snippets as sufficient evidence.
## What you learn
Composing the first-party Brave Search and WebFetch capabilities, handling external evidence, and reporting uncertainty.
## Scenario and expected outcome
The default question asks what durable execution guarantees about retries and external side effects. A useful answer must distinguish replaying recorded results from the risk of retrying an unrecorded side effect.
Read at least two primary sources successfully before answering. Cite those pages, distinguish documented guarantees from inference, and explain why idempotency can still be necessary. If a source is inaccessible, try another and disclose the gap. Search snippets alone do not meet the task.
## Run it
Install Rust/Cargo, clone the repository, and run from its root. These folders are self-contained **within the workspace**: their Cargo manifests reference the local Framework crates, so copying one folder alone is not sufficient.
```bash
git clone https://github.com/everruns/everruns.git
cd everruns
export OPENROUTER_API_KEY="your-key"
export BRAVE_SEARCH_API_KEY="your-key"
cargo run -p everruns-research-agent
```
The configured model is `z-ai/glm-5.2`. Provider access and funded credits are required; a model identifier alone does not grant access. Keep keys in your environment, not in source control. Missing variables, provider errors, or unsuccessful turns exit nonzero.
Try a contrasting question:
```bash
cargo run -p everruns-research-agent -- "Compare retry guarantees in Temporal Activities and Restate. Read official sources and give two findings plus one caveat."
```
## Build the agent
This builder combines the agent instructions, tools, and model. Tools/capabilities supply evidence and actions; the model chooses how to use them.
```rust
let agent = bound_external_calls(
Agent::builder()
.name("research-agent")
.instructions(include_str!("instructions.md"))
.provider(everruns_drivers::openrouter::from_env("openrouter")?)
.model(MODEL),
)
.capability(BraveSearch::from_env()?)
.capability(everruns::WebFetch::new())
.build()?;
// Search and fetched pages are untrusted model input. Keep one injected result from
// multiplying paid provider requests or scheduling a batch of outbound calls.
fn bound_external_calls(builder: AgentBuilder) -> AgentBuilder {
builder
.max_iterations(MAX_ITERATIONS)
.parallel_tool_calls(false)
}
```
## Send, observe, and wait
The shared demo helper subscribes before sending, filters events to this turn, shows bounded tool previews, waits for completion, and rejects unsuccessful turns. It changes presentation only; use `session.send_and_wait(question).await?` when you do not need the live tool timeline.
```rust
let engine = Engine::new();
let session = engine.create(agent);
println!("MODEL: {MODEL}");
demo::run(&session, question).await?;
```
This engine is in-memory. It does not demonstrate durable session storage; the Everruns Support example can explain that API, but does not itself persist its session.
## How the tools work
`BraveSearch::from_env()` provides `brave_web_search`. `WebFetch::new()` provides `web_fetch`, enabled through the Framework `web-fetch` Cargo feature. Search chooses candidates; fetch retrieves page content using the existing integration’s egress controls. Download-to-file is not enabled.
## Validate the behavior
```bash
cargo test -p everruns-research-agent
python3 examples/research-agent/src/render_demo.py --check
```
Offline tests cover evidence-preview rendering and recording pagination; they do not perform web research. Validate a live run by checking successful `web_fetch` results for at least two primary sources and matching the final citations to pages actually read. Network/provider behavior and factual quality are not guaranteed by a green offline test.
CI runs these offline checks without provider credentials. Live model behavior is evaluated separately; passing tests is not proof of answer quality.
## Demo and recording

Read the [captured transcript](https://github.com/everruns/everruns/blob/main/examples/research-agent/src/demo.txt) at your own pace. The GIF is a paginated replay of an actual provider run, with waiting time removed. It is not interactive and does not show model reasoning. Result excerpts are shortened only for display.
With credentials exported and Python 3, VHS, ffmpeg, and a VHS-compatible browser installed:
```bash
cd examples/research-agent
bash src/record.sh
```
The script captures a successful run, generates correctly wrapped pages and page durations, and renders `src/demo.gif`. It preserves the previous transcript when the provider run fails. To replay an existing transcript without another model call, run `(cd src && python3 render_demo.py && vhs demo.tape)`. `src/demo.txt` retains the displayed output; `.demo-pages/` is generated and ignored. Inspect results before sharing: public/demo data is safe here, but adapting tools may expose private data.
## Adapt it
Narrow the research question and source policy, add an evidence store if results need to survive sessions, and validate citation coverage before publishing important findings. Treat fetched text as untrusted data, not as instructions.
## Boundaries
Requires both OpenRouter and Brave Search credentials plus outbound HTTPS. Search and fetch can fail; six agent iterations cap the loop, not the bill. Word limits are instructions, not a hard output validator. This is a small research workflow, not an exhaustive literature review.
---
# Support Agent
> Typed read-only tools, separating facts from policy, and choosing different answers for different inputs.
Source:
[Browse the complete example](https://github.com/everruns/everruns/tree/main/examples/support-agent).
Diagnose a sign-in problem by combining account facts with an explicit recovery policy. The interesting decision is whether the user needs MFA recovery, must wait for a lockout, or should try a clean browser session.

## What you learn
Typed read-only tools, separating facts from policy, and choosing different answers for different inputs.
## Scenario and expected outcome
The default customer reset their password, but has MFA enabled and neither an authenticator nor recovery codes. A password reset alone cannot solve this case.
For `cust_mfa`, direct the user to verified identity recovery, explicitly noting that resetting a password does not disable MFA. Never request passwords or recovery codes. For `cust_locked`, explain the 15-minute wait. For `cust_browser`, suggest a private window and an escalation if it still fails.
## Run it
Install Rust/Cargo, clone the repository, and run from its root. These folders are self-contained **within the workspace**: their Cargo manifests reference the local Framework crates, so copying one folder alone is not sufficient.
```bash
git clone https://github.com/everruns/everruns.git
cd everruns
export OPENAI_API_KEY="your-key"
cargo run -p everruns-support-agent
```
The configured model is `gpt-6.1-sol`. Provider access and funded credits are required; a model identifier alone does not grant access. Keep keys in your environment, not in source control. Missing variables, provider errors, or unsuccessful turns exit nonzero.
Try a contrasting question:
```bash
cargo run -p everruns-support-agent -- "cust_locked reset their password but cannot sign in. What should they do?"
cargo run -p everruns-support-agent -- "cust_browser cannot sign in after a reset. What next?"
```
Or enter a question interactively:
```bash
cargo run -p everruns-support-agent -- --interactive
```
## Build the agent
Tools supply evidence; the model chooses how to use it.
```rust
pub fn build(provider: impl Into) -> Result {
Agent::builder()
.name("support-agent")
.instructions(include_str!("resources/instructions.md"))
.provider(provider)
.model(MODEL)
.max_iterations(12)
.tool(tools::lookup_customer())
.tool(tools::read_support_policy())
.build()
}
```
## Send, observe, and wait
The shared demo helper subscribes before sending, filters events to this turn, shows bounded tool previews, waits for completion, and rejects unsuccessful turns. It changes presentation only; use `session.send_and_wait(question).await?` when you do not need the live tool timeline.
```rust
// OPENAI_API_KEY, declared by the OpenAI driver itself.
let agent = agent::build(everruns::OpenAI::from_env()?)?;
let engine = Engine::new();
let session = engine.create(agent);
println!("MODEL: {}", agent::MODEL);
demo::run(&session, question).await?;
```
This engine is in-memory. It does not demonstrate durable session storage; the Everruns Support example can explain that API, but does not itself persist its session.
## How the tools work
`lookup_customer` reads one of three fictional customer records. It returns facts, not a prewritten recommendation. `read_support_policy` returns the bundled recovery rules. The model combines the two; no tool disables MFA or changes a real account.
## Validate the behavior
```bash
cargo test -p everruns-support-agent
bash examples/support-agent/demo/record.sh --check
```
Tests cover the distinct account states and rejection of unknown IDs. They do not grade the model’s recommendation: compare a live response with the expected outcomes above.
CI runs these offline checks without provider credentials. Live model behavior is evaluated separately; passing tests is not proof of answer quality.
## Demo and recording
The screencast runs the same `cargo run -q -p everruns-support-agent` command shown above. VHS hides most provider wait time but does not replace the model or tools with scripted output. Read the [captured transcript](https://github.com/everruns/everruns/blob/main/examples/support-agent/demo/transcript.txt) at your own pace.
With credentials exported and VHS, ffmpeg, and a VHS-compatible browser installed:
```bash
bash examples/support-agent/demo/record.sh
```
The recording script uses an exported `OPENAI_API_KEY` when present, otherwise Doppler project `everruns-dev`, config `dev`. It runs the real command inside VHS and updates `demo/demo.gif` plus `demo/transcript.txt` only after a successful turn. Public/demo data is safe here, but adapting tools may expose private data.
## Adapt it
Replace the fixture lookup with your authorized customer-data service. Keep policy separate from account facts, scope lookups to the authenticated customer, and return only fields needed for the support decision. Add human approval before any account mutation.
## Boundaries
All customers, policy rules, and support.example.com URLs are fictional. This is a read-only support exercise, not a live help desk.
---
# Host Services
> Compose an embedding host's own capabilities, provider resolution, retry budgets, MCP credentials, request metadata and observers into Engine sessions.
Source:
An embedding host often owns more than an agent definition: a curated set of capability implementations with guards and context controls, its own provider and credential resolution, provider recovery budgets, refreshing MCP tokens, request attribution and event observers. `everruns::hosting::services` composes those into the runtimes `Engine` builds, so the host keeps its behavior and still uses Engine’s sessions, durability and turn runner.
Stability: experimental, together with [session hosting](https://everruns.com/docs/framework/hosting/).
## What host services can and cannot change
`HostServices` is an immutable value attached to an Agent with `AgentBuilder::host_services`. Engine applies it every time it constructs that Agent’s runtime: for new sessions, resumption, durable activations and context inspection.
| Host services contribute | Engine keeps owning |
| ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
| Capabilities written against the Core `Capability` contract, with pre-tool hooks, model views, slash commands and instructions | Session identity, admission, leases and the turn runner |
| Provider resolution through a host `ProviderStore` and `DriverRegistry` | The Environment-selected session filesystem and its containment |
| Provider retry and stream-stall budgets | Canonical history: observers only observe |
| Call-time MCP credentials | Framework capability ids, aliases and tool names |
| Credential-free provider-request metadata and OpenRouter attribution | Request correlation ids such as `session_id` and `turn_id` |
There is no callback that receives a runtime builder, and no runtime handle to drive. A host capability whose id, alias or tool name collides with a Framework capability or another input fails `AgentBuilder::build`, so a host cannot replace, for example, the filesystem capability by registering the same id. Capability tools reach files only through the session filesystem in their `ToolContext`, which is the one the session’s Environment selected.
## Add a host capability
Implement `Capability` for the host’s policy. This one blocks one tool before it runs and adds a reminder to every model call:
```rust
use std::sync::Arc;
use everruns::hosting::services::{
Capability, HostServices, ModelViewContext, ModelViewProvider, PolicyGate,
PreToolUseDecision, PreToolUseHook, RuntimeMessage, ToolCall, ToolContext,
ToolDefinition, async_trait,
};
use everruns::{Agent, Engine, OpenAI};
use serde_json::Value;
struct NoForcePush;
#[async_trait]
impl PreToolUseHook for NoForcePush {
async fn before_exec(
&self,
tool_call: ToolCall,
_definition: &ToolDefinition,
_context: &ToolContext,
) -> PreToolUseDecision {
if tool_call.name == "git_push" && tool_call.arguments["force"] == true {
return PreToolUseDecision::Block {
tool_call,
reason: "force pushes are disabled by policy".into(),
user_message: None,
};
}
PreToolUseDecision::Continue(tool_call)
}
}
struct Reminder;
impl ModelViewProvider for Reminder {
fn apply_model_view(
&self,
mut messages: Vec,
_config: &Value,
_context: &ModelViewContext<'_>,
) -> Vec {
messages.push(RuntimeMessage::system("Keep changes reviewable."));
messages
}
}
struct RepoPolicy;
impl Capability for RepoPolicy {
fn id(&self) -> &str { "repo_policy" }
fn name(&self) -> &str { "Repository policy" }
fn description(&self) -> &str { "Guards repository operations." }
fn system_prompt_addition(&self) -> Option<&str> {
Some("Never rewrite published history.")
}
fn pre_tool_use_hooks(&self) -> Vec> {
vec![Arc::new(PolicyGate(NoForcePush))]
}
fn model_view_provider(&self) -> Option> {
Some(Arc::new(Reminder))
}
}
let agent = Agent::builder()
.instructions("You maintain this repository.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.host_services(HostServices::new().capability(RepoPolicy))
.build()?;
let session = Engine::new().create(agent);
let context = session.inspect().await?;
assert!(context.instructions.contains("Never rewrite published history."));
```
`PolicyGate` marks the hook as a policy decision, so it runs after any hook that rewrites arguments and decides on the call that will execute. A blocked call never reaches its tool; the model receives the reason as the call’s result. Commands a capability contributes appear in `SessionContext::commands`.
`HostServices::capability` registers and activates. `HostServices::register` only registers, and the Agent activates it with an ordinary `.capability("repo_policy")` reference or through its Harness.
## Resolve providers through the host
With `HostProviders`, the host’s `ProviderStore` owns endpoints and credentials and the Agent names its model with `Model::on(provider_key, model_id)`. The store is asked under the calling session’s identity on every provider call, so rotated credentials and provider metadata reach the next call without rebuilding anything.
```rust
use everruns::hosting::services::{HostProviders, HostServices, ProviderStore};
use everruns::{Agent, DriverRegistry, Model};
let mut drivers = DriverRegistry::new();
everruns::drivers::openai::register_driver(&mut drivers);
let agent = Agent::builder()
.instructions("You are concise.")
.model(Model::on("team-openai", "gpt-6.1-sol"))
.host_services(HostServices::new().providers(HostProviders::new(store, drivers)))
.build()?;
```
Selection is never implicit. Building fails with `ConflictingProviders` when a host-resolved model also carries an Agent provider, or when `Model::on` names a different provider than `.provider(..)`. A provider key the host cannot serve fails runtime construction before any model call, rather than falling back to another driver.
## Attribute provider requests
`RequestMetadata` is a small set of credential-free strings that every provider request of the Agent’s sessions carries: turn model calls, out-of-band completions, and sessions Engine rebuilds or resumes, on both the in-process and the hosted lanes. `openrouter_attribution` sets the entries the OpenRouter driver sends as its `HTTP-Referer` and `X-Title` headers; other providers receive them as ordinary request metadata.
```rust
use everruns::hosting::services::{HostServices, RequestMetadata};
use everruns::{Agent, Engine, OpenAI};
let metadata = RequestMetadata::new()
.entry("app", "my-app")
.entry("my_app_version", env!("CARGO_PKG_VERSION"))
.openrouter_attribution("https://my-app.example", "My App");
let agent = Agent::builder()
.instructions("You are concise.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.host_services(HostServices::new().request_metadata(metadata))
.build()?;
let session = Engine::new().create(agent);
let context = session.inspect().await?;
assert_eq!(context.request_metadata["app"], "my-app");
```
`SessionContext::request_metadata` shows the entries as assembled for the next request. The runtime adds its own correlation ids to each request after them. Building fails with `InvalidRequestMetadata` when an entry claims one of those ids (`RequestMetadata::RESERVED_KEYS`: `session_id`, `harness_id`, `turn_id`, `exec_id`, `org_id`, `agent_id`, `model_id`), when a key is not 1 to 64 ASCII letters, digits, `_`, `.`, `-` or `:`, when a value is longer than 512 bytes or holds control characters, or when there are more than nine entries. Providers and request capture see these values, so never put a secret in them.
## Budgets, MCP credentials and observers
```rust
use std::time::Duration;
use everruns::hosting::services::{
HostEvent, HostEventListener, HostServices, LlmRetryConfig, McpAuthProvider,
McpAuthRequest, McpAuthResult, McpCredential, async_trait,
};
use everruns::Engine;
struct Vault;
#[async_trait]
impl McpAuthProvider for Vault {
async fn authorization(
&self,
request: &McpAuthRequest<'_>,
) -> McpAuthResult> {
// Called before every discovery and tool call: return the current token.
Ok(Some(McpCredential::bearer(format!("token-for-{}", request.server_name))))
}
}
struct Audit;
#[async_trait]
impl HostEventListener for Audit {
async fn on_event(&self, event: &HostEvent) {
println!("{} {:?}", event.event_type, event.sequence);
}
}
let services = HostServices::new()
.provider_retry(LlmRetryConfig { max_retries: 4, ..LlmRetryConfig::default() })
.provider_stall_timeout(Duration::from_secs(45))
.mcp_auth(Vault);
let engine = Engine::builder().host_listener(Audit).build();
```
Host listeners receive the same committed canonical envelopes as `EngineBuilder::listener`, in Core event form, on their own bounded queue. A slow, full or failing listener cannot fail a turn or delay other listeners, and nothing a listener does is written to session history. Drops are counted in `Engine::observer_stats`. `McpAuthProvider` requires the `mcp` feature.
## Related
* [Advanced capabilities](https://everruns.com/docs/framework/advanced-capabilities/) for typed capability packages that need no Core contract.
* [Session controls](https://everruns.com/docs/framework/session-controls/) to hot-install, activate and deactivate capabilities and run their commands on a live session.
* [Session hosting](https://everruns.com/docs/framework/hosting/) for durable session authority under Engine.
* [Custom backends](https://everruns.com/docs/framework/custom-backends/) when the host itself replaces execution components.
---
# Pluggable session hosting
> Implement durable hosting beneath Engine without implementing an agent runtime.
Source:
Everruns owns agent execution. A host supplies a coherent session authority through `everruns::hosting::SessionHosting` and a claimed write capability through `SessionEpoch`. Applications define ordinary `Agent` values and call the same `Engine` and `Session` API regardless of the hosting implementation.
Without hosting configuration, `Engine::builder().build()` uses in-memory hosting. An embedded application needs no database, Durable Object or external hosting service. Its sessions live for the lifetime of that process. Durable hosting is an opt-in.
The hosting SPI is experimental. Framework has no dependency on a Cloudflare or celld adapter. [Durable Object examples](https://everruns.com/docs/framework/durable-objects/) demonstrate how an external implementation can use this boundary; they are source-only references, outside the product publish set.
## Responsibilities
| Framework Engine | Hosting implementation | Application/deployment |
| --------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------ |
| Planning, shared actor recovery and tool execution | Immutable session binding and transactional commands | Trusted Agent/build registry |
| Lifecycle hooks and finalized outcomes | Deduplicated input receipts and canonical journal | Retained code and Environment factories |
| Session command rules, approval/client wait interpretation and context projection | Exclusive epochs, fenced storage and bounded snapshot reads | Workspace, compute, secrets and provider transport |
| Execution entry through `Engine::activate` | Independent wakeups, expiry discovery and activation retries | Authentication, authorization and operational recovery |
A hosting implementation must commit input, receipt and wakeup obligation before acknowledging admission. Every execution mutation checks the same unexpired epoch inside the authority transaction. Frontend readers reject mutations. Durable hosts arrange activation independently of frontend connections and process-local queues. An alarm or supervisor calls `Engine::activate(session_id)`; it never implements a second planner or turn loop.
Registered builds and Environment references are immutable. Missing retained code must produce visible suspension instead of falling back to the latest Agent. A durable journal does not make tools, workspace files or external effects durable; those services retain their own guarantees and idempotency requirements.
## Waiting without keeping execution alive
Engine stores safe provider retry deadlines and budgets in the session authority, then releases execution. The host wakes the same session when its deadline is due; an early wake sends no model request, and cancellation can interrupt the wait. Retry limits survive restarting Engine. An explicit provider policy that disables retries remains disabled.
If a stream fails after answer text, a safe retry keeps the message ID and emits `output.message.started` again before parking. Clients replace that attempt’s partial text. A trailing stream error keeps the partial answer when no retry fits the remaining budget. Tool calls and provider-executed effects retain their existing replay restrictions.
`HostingStatus::wake()` reports whether compute is needed now, at a deadline, or only after an external event. Hosts deliver wakeups; Engine retains execution and recovery policy. Default memory hosting uses its local scheduler and needs no external backend. This does not suspend arbitrary running tools or make external effects safe to repeat.
The reference Workers SSE transport uses hibernating WebSocket notifications to read committed events when they change. Its frontend heartbeat does not poll an idle session object. Clients that perform their own polling still generate object activity.
## Implementing a host
Depend on `everruns` with only the features the placement needs. The supported storage/ownership paths are under `everruns::hosting::spi`: canonical events and paging, actor leases/journal access, execution views, compaction checkpoints and session values. `SessionHosting` and `SessionEpoch` describe the semantic contract. Host implementations need no direct `everruns-core` or `everruns-contracts` dependency, and do not construct the Engine runtime.
The host’s required `command` method runs the shared Rust command implementation inside its authority transaction. Capture a bounded `spi::session_commands::CommandView`, call `spi::session_commands::prepare`, and apply its complete plan before releasing the transaction or revision guard. This keeps binding checks, command fingerprints, approval decisions, pending-call validation and partial-wait behavior in Everruns. The host translates the resulting journal, value, receipt and activation writes into its own storage operations. It does not reproduce those decisions.
For a remote authority, send the semantic command to that authority. Run the Rust implementation there, against its locked canonical state and clock; preparing a plan in an executor and sending its effects creates a validation/commit race. The object reference compiles this same command implementation to Wasm and invokes it inside the object’s storage transaction, including when Engine runs on a native executor. JavaScript supplies transaction and wakeup plumbing.
`SessionEpoch::backends()` supplies the low-level storage ports that Engine uses internally. `HostBackends` is an implementation detail of writing a hosting adapter, not Agent configuration. Its in-memory defaults are appropriate for reconstructed definitions and explicitly volatile services. Replace every store whose state your durability claim covers with the authority’s fenced version. Selecting individual stores through legacy Agent configuration is a separate, weaker embedding path and does not establish coherent durable execution.
## Qualifying a host
Enable `hosting-conformance` in the host crate’s **dev-dependencies**. The native suite runs against a fresh test authority using the public Framework SPI:
```rust
async fn qualify(host: impl everruns::hosting::SessionHosting + Clone) {
everruns::hosting::conformance::authority(&host).await;
everruns::hosting::conformance::lease_expiry(&host).await;
everruns::hosting::conformance::engine_wait_recovery(host).await;
}
```
The scenarios check immutable bindings, admission retries/conflicts, bounded snapshot paging, read-only frontend ports, exclusive epochs, lease expiry, stale writes, Engine reconstruction, mixed approval/client waits and atomic retry handling. The same suite runs against built-in memory/SQLite hosts and the independently resolved DO reference. The feature is native test tooling; it does not add a simulated provider to Wasm deployments or certify storage durability.
Also run deployment-specific failure tests: kill the executor or isolate after a committed effect, allow no frontend requests during recovery, and require the original admission to finish through an independent wakeup. Test lease expiry, platform owner loss and retained-build failure according to the claimed failure domain. The reference includes workerd, isolate and native-process harnesses; local emulators alone do not establish production platform guarantees.
## Framework, serve and the platform server
Framework applications and serve both enter `everruns::Engine`; selecting a host does not change their Agent definitions. A DO application is the same Engine and Agent compiled to Wasm, with an object host and host-compatible tools. The celld example supplies that hosting integration, or a native executor when tools need processes and files.
The full Everruns platform server has its own control plane and PostgreSQL actor host. It consumes the same Core actor driver directly, shares the Rust command validation for client results and the approval/question policies, and retains its platform authorization and durable resolution claims. Supporting external hosts does not require routing the whole platform through the Framework facade.
## Deployments and retained behavior
Keep three identities separate:
| Identity | Changes when | Persisted binding |
| -------------------------- | ----------------------------------------------------- | ------------------------ |
| Deployment revision | Worker/container is rebuilt or redeployed | Deployment metadata only |
| `AgentBuild` | Agent behavior or its immutable configuration changes | Exact Agent registration |
| `SessionBinding.framework` | Persisted runtime format becomes incompatible | Runtime format version |
A compatible Engine upgrade can serve several retained Agent definitions in one binary. Register the original definitions alongside the new one; an old key must still construct its original tools, hooks, Harness, Environment and model settings. Do not alias old keys to the current Agent. Retaining a frontend Worker revision alone does not retain the implementation running inside its Durable Objects.
serve’s default durable build identity conservatively includes its executable. It does not automatically declare rebuilt behavior compatible. Hosts can supply an application-owned Engine with retained registrations through `everruns_serve::ServerBuilder::engine`; each old registration still needs its original Agent, Harness and Environment reference. `Server::engine()` exposes that same Engine for authorized compatibility inspection and retry operations.
```rust
use everruns::{Agent, Engine};
use everruns::hosting::AgentBuild;
fn deployment(original: Agent, current: Agent) -> Engine {
Engine::builder()
.register(AgentBuild::new("assistant", "v1"), original)
.register(AgentBuild::new("assistant", "v2"), current)
.build()
}
```
Use `engine.session_compatibility(id).await?` to inspect the required build, unsupported runtime version or missing immutable configuration without starting execution. History and accepted receipts remain readable when code is missing. Activation suspends before running an unsupported format or substituting behavior.
After restoring the exact registration and a compatible Engine, an authorized host can call `engine.retry_session(id, stable_command_id).await?`. This checks compatibility, then atomically checks the unchanged binding and absence of an active epoch, retains the retry receipt and restores the activation obligation. Pending tools, approvals, waits, history and accepted inputs retain their identities. The command refuses suspensions caused by uncertain effects: those require their own reconciliation policy. This privileged method needs application authorization.
The framework does not provide an in-place build migration. A migration would need a separate compatibility and fencing protocol covering active execution, accepted inputs and outstanding effects. Overwriting a build ID or copying only completed messages does not preserve interrupted execution.
---
# Lifecycle Hooks
> Run typed application handlers at agent, turn, tool, and completion boundaries.
Source:
Lifecycle hooks run trusted application code at defined execution boundaries. Register them on `Agent::builder()` when work must finish before execution can continue, or when an application needs a typed failure from a lifecycle action.
```rust
use everruns::prelude::*;
let agent = Agent::builder()
.instructions("You are concise.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.on_agent_start(|context| async move {
println!("starting {}", context.session_id);
})
.on_turn_start(|context| async move {
if context.input.content.is_empty() {
Err("empty input")
} else {
Ok(())
}
})
.on_completion(|context| async move {
println!("turn stopped with {:?}", context.turn.stop_reason);
})
.build()?;
```
Handlers are async `Fn` closures. An infallible handler returns `()`; a fallible handler returns `Result<(), E>` where `E` implements `Display`. Wrap synchronous work in an async block, such as `|context| async move { record(context) }`.
## Hooks or events?
Hooks and [session events](https://everruns.com/docs/framework/events-and-cancellation/) serve different jobs.
| | Lifecycle hooks | Session events |
| --------------- | ---------------------------------------------- | -------------------------------------------- |
| Purpose | Extend execution with application behavior | Observe execution for UI, telemetry, or logs |
| Delivery | Awaited at a lifecycle boundary | Non-blocking stream from `Session::events()` |
| Effect on a run | A pre-effect error may prevent its scoped work | Never changes or delays a run |
| Registration | `AgentBuilder::on_*` | Subscribe on each `Session` |
Do not register a hook merely to mirror the event feed. Use hooks when ordering or failure semantics matter; use events for observation.
## Lifecycle points
Handlers at one lifecycle point run sequentially in builder registration order.
| Builder method | Runs | Error behavior |
| ---------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `on_agent_start` | Before the first turn attempted by each session | The first error returns `RunError::Hook`; the next run retries the complete chain |
| `on_turn_start` | Before every turn enters the runtime | The first error returns `RunError::Hook` and prevents that turn |
| `on_tool_start` | Before a model-requested tool call executes | The first error blocks only that call, skips later start handlers for it, and records a `HookFailure` |
| `on_tool_end` | After a tool call reaches a terminal result, including a blocked call | Errors are isolated, recorded, and do not skip later end handlers |
| `on_completion` | After a non-cancelled runtime turn reaches a terminal outcome | Errors are isolated, recorded, and do not skip later completion handlers |
`Session::inspect()` may materialize a runtime but invokes no lifecycle handler. A successful agent-start chain runs once for that session. If it fails or is cancelled, the next run starts the complete chain again, so external agent-start effects should be idempotent.
Tool-start runs after any earlier execution gates configured by the host. Tool-end runs for every call that reaches a terminal result, including a call blocked by an earlier gate; in that case the Framework tool-start handler might not have run. Independent calls in a parallel tool batch can run their hook chains concurrently.
Completion receives terminal `Turn` values whether `turn.success` is true or false. A runtime error that produces no `Turn`, and a cancelled in-flight turn, do not run completion handlers. Every completion handler receives the same pre-completion `CompletionContext` snapshot.
## Failures and execution effects
Hook contexts are owned, read-only snapshots. A hook cannot rewrite input, tool arguments, tool results, or the returned turn. Errors affect execution only where work has not happened:
* agent-start and turn-start errors prevent the turn and return `RunError::Hook`;
* tool-start errors prevent that one tool call and appear in `Turn::hook_failures`;
* tool-end and completion errors cannot roll back completed work, so they are isolated in `Turn::hook_failures` and the remaining handlers still run.
Each `HookFailure` identifies the lifecycle point and its zero-based handler index. Tool failures also identify the tool and call. A tool-start error shown to the model is deliberately generic; the handler’s detailed message remains application-facing on `HookFailure`.
With no registered hooks, execution behavior is unchanged and `Turn::hook_failures` is empty.
## Cancellation, concurrency, and panics
A token cancelled before `run_with` skips all handlers. Cancellation during agent-start, turn-start, or an in-flight tool chain drops the active handler future, skips the remaining turn work, and does not run completion. Side effects that finished before cancellation are not rolled back. The synthesized cancelled `Turn` does not report partial failures from the dropped in-flight hook chain.
Once the runtime commits a turn, completion handlers finish in order even if that run token is then cancelled. This makes post-turn delivery predictable.
The same `Fn` handlers are shared by every session. Separate sessions, and separate calls in a parallel tool batch, may invoke a handler concurrently. Protect shared mutable state inside the closure and do not depend on failure ordering across parallel tool calls.
The Framework adds no hook timeout and does not catch panics. Apply an application timeout inside a handler when external work must be bounded; let ordinary Rust panic behavior handle programming defects.
## Sensitive data
Lifecycle handlers are trusted in-process application code. Turn and tool contexts can contain user input, model-selected arguments, tool results, or backend error text. Do not log or export whole contexts without applying the same redaction and access controls as the underlying data.
## Runnable example
The focused [`lifecycle_hooks.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/lifecycle_hooks.rs) example registers all five lifecycle points around a typed tool:
```bash
cargo run -p everruns --features openai --example lifecycle_hooks
```
It uses `gpt-6.1-sol` and requires `OPENAI_API_KEY`.
---
# Models and Providers
> Select model identities, attach provider drivers and credentials, list provider catalogs, and write a custom provider.
Source:
The Framework separates **what model to use** from **how to reach it**:
* `.model("id")` selects the provider-visible model with a credential-free string.
* `Provider` supplies the driver, endpoint, and authentication needed by the host.
* An agent currently accepts one provider, configured separately with `.provider(...)`.
This boundary is open: a new provider does not require a new closed enum variant or provider-specific branch in application code. The Framework constructs its execution-facing model specification internally when the agent builds.
This page covers the provider types the `everruns` crate bundles, the drivers that ship today, the environment variables each driver reads, provider model catalogs, and writing your own driver. To call a model once without building an agent, see [Direct model calls](https://everruns.com/docs/framework/direct-model-calls/).
## OpenAI convenience
With the `openai` feature, `OpenAI::from_env` reads `OPENAI_API_KEY` and the optional `OPENAI_BASE_URL`:
```rust
use everruns::{Agent, OpenAI};
let agent = Agent::builder()
.instructions("Be concise.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.build()?;
```
Use `OpenAI::new(key)` when the host already owns an explicitly resolved credential. Never put credentials in a model id, log them as model identity, or select provider behavior with vendor-specific detection.
The `anthropic` and `gemini` features add `Anthropic` and `Gemini` with the same shape, and `openrouter` adds `OpenRouter`:
| Feature | Type | Reads |
| ------------ | ------------ | ------------------------------------------------------- |
| `openai` | `OpenAI` | `OPENAI_API_KEY`, `OPENAI_BASE_URL` |
| `anthropic` | `Anthropic` | `ANTHROPIC_API_KEY` |
| `gemini` | `Gemini` | `GEMINI_API_KEY` or `GOOGLE_API_KEY`, `GEMINI_BASE_URL` |
| `openrouter` | `OpenRouter` | `OPENROUTER_API_KEY`, `OPENROUTER_BASE_URL` |
The `bedrock` feature adds `Bedrock`, which takes either static keys or the AWS default credential chain, so a process with an IAM role (an ECS task, an EC2 instance profile, an Amazon Bedrock AgentCore Runtime execution role) needs no keys. The model id is a Bedrock model id or inference profile:
```rust
use everruns::{Model, providers::bedrock::Bedrock};
// Static keys.
let model = Model::new(
"us.anthropic.claude-sonnet-4-6",
Bedrock::new("AKIA...", "secret", "us-east-1"),
);
// The AWS default chain. Region: `.region(..)`, else AWS_REGION, else
// AWS_DEFAULT_REGION, else us-east-1.
let model = Model::new(
"us.anthropic.claude-sonnet-4-6",
Bedrock::default_chain().region("us-east-1"),
);
```
`from_env` is not OpenAI-specific: every driver declares the variables its own vendor SDK reads, and each driver crate exposes the same entry point. See [Credentials](#credentials) for the per-driver table.
Applications with their own driver pass `Provider::new(key, driver)` to `.provider(...)`; see [Custom providers](#custom-providers).
## Simulated models, for tests
```rust
use everruns::{Agent, Model};
let agent = Agent::builder()
.instructions("Answer deterministically.")
.model(Model::simulated("fixed response"))
.build()?;
```
`Model::simulated` is backed by the focused `everruns-llmsim` crate. It is a **test double that runs no inference** — it replays canned responses so tests can assert on agent behavior without a network call or an API key. It is not a local model and not a way to run Everruns without a provider. Depend on the crate directly when building a low-level host or scripting multi-turn provider behavior; ordinary Framework applications need only `everruns`.
It registers as a driver, but it reaches no network. For real work, pick a provider from [Supported providers](#supported-providers).
## Supported providers
Everruns talks to model vendors through **drivers**. A driver owns one vendor’s wire protocol; a `Provider` pairs typed service drivers with one endpoint and authentication boundary. The set below is what ships today. The boundary is open, so a [custom driver](#custom-providers) is a first-class peer of these.
### Drivers
| Driver | Crate | Wire protocol | Services | Model discovery |
| ------------------------- | --------------------------------- | ------------------------------------------------------ | ------------------------------------- | ---------------------- |
| OpenAI | `everruns-drivers` (`openai`) | OpenAI Responses, OpenAI Decisions API | chat, embeddings, realtime, decisions | yes |
| ChatGPT Plan | `everruns-drivers` (`chatgpt`) | Stateless Responses + open-source OAuth | chat | account-visible models |
| OpenAI (Chat Completions) | `everruns-drivers` (`openai`) | OpenAI Chat Completions | chat | yes |
| Azure OpenAI | `everruns-drivers` (`openai`) | OpenAI Responses | chat | yes |
| Anthropic | `everruns-drivers` (`anthropic`) | Anthropic Messages | chat | yes |
| Google Gemini | `everruns-drivers` (`gemini`) | Gemini `generateContent` | chat | yes |
| AWS Bedrock | `everruns-drivers` (`bedrock`) | Bedrock `ConverseStream` (SigV4) | chat | no |
| OpenRouter | `everruns-drivers` (`openrouter`) | OpenAI Responses-compatible, System One | chat, decisions | yes |
| TypeSafe | `everruns-drivers` (`typesafe`) | System One | decisions | curated Jev profiles |
| Microsoft MAI | `everruns-drivers` (`mai`) | OpenAI Chat Completions, System One (Azure AI Foundry) | chat, decisions | yes |
| Fireworks AI | `everruns-drivers` (`fireworks`) | OpenAI Chat Completions-compatible | chat | yes |
| Meta | `everruns-drivers` (`meta`) | OpenAI Responses-compatible | chat | yes |
| Mistral AI | `everruns-drivers` (`mistral`) | OpenAI Chat Completions-compatible | chat | yes |
| Cloudflare AI Gateway | `everruns-drivers` (`cloudflare`) | OpenAI Chat Completions-compatible | chat | Workers AI only |
| Vercel AI Gateway | `everruns-drivers` (`vercel`) | Open Responses | chat | yes |
| LLM Simulator | `everruns-llmsim` | none — in-process test double | chat, realtime (simulated) | no |
Every chat driver produces an incremental stream — server-sent events for the HTTP protocols, `ConverseStream` for Bedrock — so token-by-token output works everywhere, not just on one vendor. Tool calling and multi-turn tool results work across all of them: each driver normalizes its vendor’s shape into the same typed events, which is why swapping a provider does not change application code.
The environment variables each API driver reads are in [Credentials](#credentials). ChatGPT Plan connections use host-owned OAuth credentials; see [ChatGPT Plan](https://everruns.com/docs/features/chatgpt/). The optional `codex` SDK feature provides the legacy Codex backend for hosts such as Yolop. The Platform UI does not offer ChatGPT Plan because Everruns does not have a registered web OAuth client and callback for that flow.
### Beyond chat
Most drivers implement chat only. Two capabilities go further, and both are OpenAI-only today:
**Embeddings.** The OpenAI driver powers embedding models for knowledge-base retrieval alongside its chat models.
**Realtime voice (WebRTC + WebSocket).** A realtime voice session is negotiated by the platform server, not the Framework. The browser posts its SDP offer to `POST /v1/sessions/{session_id}/voice/calls` and the server answers it; a separate route mints a short-lived client secret from the vendor. The organization’s own API key is used only server-side and never reaches the browser. The server then opens a WebSocket sideband (`wss://…/realtime?call_id=…`) to drive the call and collect transcripts, which land in the session as ordinary events — so a voice turn and a typed turn are the same session, readable through the same history and event streams. This needs the platform server; an embedded Framework process does not expose it.
### Interactive connect
OpenRouter declares an OAuth connect flow, so an operator can choose “Connect with OpenRouter” instead of pasting a key. Every other driver takes a credential directly, entered in Settings or supplied in code.
### Choosing one
Any OpenAI-compatible gateway that speaks Responses or Chat Completions can usually be reached by pointing the matching driver’s `base_url` at it, rather than writing a driver. Write a [custom driver](#custom-providers) when the vendor’s protocol genuinely differs, or when it needs authentication that a bearer token cannot express.
## Credentials
Every provider driver declares the environment variables it reads, on its own descriptor, following **its vendor’s own SDK convention**. There is no Everruns naming scheme to learn: if your shell already runs the `openai` CLI, the AWS CLI, or an Azure service principal, it already configures the matching driver. `OpenAI::from_env()` in the example above reads `OPENAI_API_KEY`, and `OPENAI_BASE_URL` when set.
Each driver crate offers the same entry point, returning a ready `Provider`. The facade bundles OpenAI, Anthropic, Gemini and OpenRouter behind the `openai`, `anthropic`, `gemini` and `openrouter` features (`Anthropic::from_env` and friends), and AWS Bedrock behind `bedrock`, where `Bedrock::default_chain()` resolves credentials the way the AWS SDK does (an IAM role, SSO or a profile, or the `AWS_*` variables) instead of reading declared names; any other driver is a separate crate you add as a dependency:
```rust
use everruns::{Agent, Model};
// Reads FIREWORKS_API_KEY, and FIREWORKS_BASE_URL when set.
let model = Model::new(
"accounts/fireworks/models/llama-v3p1-70b-instruct",
everruns_drivers::fireworks::from_env("fireworks")?,
);
```
### What each driver declares
| Driver | Credential | Endpoint |
| ------------------------- | --------------------------------------------------------------------------------------------------------- | ---------------------------- |
| OpenAI | `OPENAI_API_KEY` | `OPENAI_BASE_URL` |
| OpenAI (Chat Completions) | `OPENAI_API_KEY` | `OPENAI_BASE_URL` |
| Azure OpenAI | `AZURE_OPENAI_API_KEY` | — (see below) |
| Anthropic | `ANTHROPIC_API_KEY` | — (see below) |
| Google Gemini | `GEMINI_API_KEY`, or `GOOGLE_API_KEY` | `GEMINI_BASE_URL` |
| OpenRouter | `OPENROUTER_API_KEY` | `OPENROUTER_BASE_URL` |
| TypeSafe | `TYPESAFE_API_KEY` | `TYPESAFE_BASE_URL` |
| Fireworks AI | `FIREWORKS_API_KEY` | `FIREWORKS_BASE_URL` |
| Meta | `LLAMA_API_KEY`, or `META_API_KEY` | `LLAMA_BASE_URL` |
| Mistral AI | `MISTRAL_API_KEY` | `MISTRAL_BASE_URL` |
| AWS Bedrock | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION` (or `AWS_DEFAULT_REGION`), `AWS_SESSION_TOKEN` | — (the region selects it) |
| Microsoft MAI | `AZURE_AI_API_KEY`, **or** `AZURE_TENANT_ID` + `AZURE_CLIENT_ID` + `AZURE_CLIENT_SECRET` | `AZURE_AI_ENDPOINT` |
| Cloudflare AI Gateway | `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_AI_GATEWAY_ID` | — (derived from the account) |
| Vercel AI Gateway | `AI_GATEWAY_API_KEY` | — (one fixed gateway host) |
A driver is not limited to one key. Bedrock needs four AWS fields; MAI accepts either a resource key or a full Entra ID service principal. Alternates listed with “or” are variables the vendor itself also honors, tried in the order shown — not a second credential.
Anthropic and Azure OpenAI declare no endpoint variable on purpose. A `base_url` here is the *versioned* API root — drivers append bare operation paths to it, and the defaults end in `/v1` — whereas `ANTHROPIC_BASE_URL` and `AZURE_OPENAI_ENDPOINT` name the bare host, because those SDKs add the version segment themselves. Importing either verbatim would resolve to `https://api.anthropic.com/messages` and fail. Point those drivers at a proxy with an explicit `Provider::new(...).base_url(...)`, or through the Settings UI, instead.
A credential resolves whole or not at all. If any required variable is missing the driver is simply not configured from the environment, rather than being half-configured into a provider that fails at its first request. A shell carrying `AWS_REGION` but no AWS keys does not configure Bedrock, and a half-populated Entra block does not configure MAI — the same rule the schema applies to an operator-entered form.
This table is pinned by a test against the drivers’ own declarations, so it cannot drift from what they read.
### Custom drivers
A [custom driver](#custom-providers) declares its variables the same way, on the credential field itself:
```rust
use everruns::{CredentialFormSchema, DriverDescriptor, DriverId, FormField};
DriverDescriptor {
display_name: "Acme".into(),
credential_schema: CredentialFormSchema {
fields: vec![
FormField::password("api_key", "API Key")
.required()
.env("ACME_API_KEY"),
],
instructions_markdown: "Create a key in the Acme console.".into(),
},
base_url_env: Some("ACME_BASE_URL".into()),
..DriverDescriptor::chat_only(DriverId::external("acme"), factory)
}
```
A driver that declares nothing is never configured from the environment, whatever its id is spelled. That is the safe default: the registry’s built-in schema declares no variable, so a driver opts in by naming what its vendor reads.
### Server deployments never read the environment
Credential loading is an injected concern, not something a driver does. A driver only *declares* names; the declaration reads nothing and is simply never consulted on the server.
`EnvCredentialProvider` is the single place in the workspace that pairs a driver’s declarations with a real environment lookup, and it is for standalone, CLI, and development use. The multitenant server resolves credentials from its encrypted database and constructs no `CredentialProvider` at all. This is the fail-closed Key Resolution Contract: a platform-level key reachable from a shared host environment would silently fund tenant execution.
So `OpenAI::from_env`, each driver’s `from_env`, and `EnvCredentialProvider` belong in your own binaries and dev entrypoints. Hosted deployments configure providers through the Settings UI, which renders the same declared schema.
### Resolving credentials yourself
To build the provider without going through a driver crate’s `from_env` — a custom `ProviderKey`, or your own credential source — resolve against the descriptor:
```rust
use everruns::{CredentialProvider, EnvCredentialProvider, provider_from_env};
let driver = everruns_drivers::anthropic::descriptor();
// What this driver reads, for an error message or a setup check.
let names = driver.declared_env_vars();
// The same resolution `from_env` performs.
let provider = provider_from_env(&driver, "primary")?;
// Or inspect the resolved fields first.
if let Some(credentials) = EnvCredentialProvider.resolve(&driver) {
let _ = credentials.api_key();
}
```
`ProviderCredentials` carries every declared field, so multi-field drivers stay expressible; `document()` produces the exact credential shape the server stores, which is why an env-resolved credential and an operator-entered one reach the driver through one path.
## Model catalogs
Selecting a model needs an exact, provider-visible id. Anything that lets a person choose one — a picker, a `--model` flag, a settings page — needs the catalog behind it: which ids this provider serves, what they are called, and what each one supports.
That is the same provider edge an [agent](https://everruns.com/docs/framework/agents/) or a [direct call](https://everruns.com/docs/framework/direct-model-calls/) uses, asked a different question:
```rust
use everruns::{OpenAI, models};
for model in models::list(OpenAI::from_env()?).await? {
println!("{} — {}", model.id(), model.display_name().unwrap_or("?"));
}
```
Ids come back exactly as chat calls expect them, newest first, merged with the model profile registry so a provider that returns bare ids still renders human-readable names and descriptions.
`list` is a provider call: it costs a round trip and catalogs change rarely, so cache the result instead of asking per keystroke.
### What each entry carries
`ModelInfo` separates the id from everything around it. The id is the provider’s own; the rest is display and capability metadata, absent when neither the provider nor the registry knows it.
```rust
use everruns::ModelInfo;
fn describe(model: &ModelInfo) -> String {
let name = model.display_name().unwrap_or(model.id());
let window = model.context_window().unwrap_or_default();
let tools = if model.supports_tools() { "tools" } else { "no tools" };
format!("{name}: {window} tokens, {tools}")
}
```
* `id` — pass back unchanged.
* `display_name`, `description` — for rendering.
* `vendor` — who trained the model, which is not always who serves it: an aggregator offers many vendors’ models.
* `context_window`, `supports_tools`, `supports_reasoning` — the common checks, from the profile registry.
* `profile` — the full `ModelProfile` behind those: limits, per-million-token prices, modalities, and capability flags.
Capability answers come from curated data, so `false` also covers “not in the registry”. Treat them as display hints rather than guarantees.
### From a selection to a run
A selection converts straight back into the `Model` the rest of the API takes, bundled with the provider it was discovered through:
```rust
use everruns::{Agent, OpenAI, models};
let catalog = models::list(OpenAI::from_env()?).await?;
let picked = catalog
.into_iter()
.find(|model| model.supports_tools())
.ok_or("no tool-calling model")?;
let agent = Agent::builder()
.instructions("Be concise.")
.model(picked.model())
.build()?;
```
No string handling, and nothing to reconfigure: the model already knows how to be reached.
### Providers without a catalog
Not every provider can enumerate its models. That is not a failure of the request, so it is a distinct variant rather than an error to log:
```rust
use everruns::{Provider, models};
let ids = match models::list(provider).await {
Ok(catalog) => catalog.iter().map(|model| model.id().to_string()).collect(),
// Keep the application's curated suggestions.
Err(models::CatalogError::NoCatalog) => curated,
Err(error) => return Err(error.into()),
};
```
`CatalogError::Call` carries the provider failure verbatim, with the full `LlmError` decision intact.
### Metadata without a provider call
The profile registry is static data, so a model’s identity can be read offline:
```rust
use everruns::{DriverId, models};
let profile = models::profile(&DriverId::OpenAI, "gpt-6.1-sol");
assert!(profile.is_some());
```
A `Model` that bundles its provider answers the same question directly with `model.profile()`. A bare model id has no profile: nothing says which vendor’s registry to consult.
### Drivers and the vendor behind a provider
A provider’s runtime key is the application’s own name for it, so `Provider::new("my-gateway", ...)` says nothing about which vendor’s models it serves. Declare the driver kind when they differ, and profile lookups resolve against the vendor:
```rust
use everruns::{ChatDriver, DriverId, Provider};
let provider = Provider::new("my-gateway", driver)
.base_url("https://gateway.example/v1")
.with_driver_id(DriverId::OpenAI);
```
Unset, the driver kind falls back to the runtime key, which is the conventional case (`OpenAI::from_env()` and every driver crate’s `from_env` already declare it). A custom driver joins in by implementing `ChatDriver::list_models` and returning `DiscoveredModel` values; returning `None` — the default — is how a driver says it has no catalog. See [Custom providers](#custom-providers).
The runnable version of this section is [`model_catalog.rs`](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/model_catalog.rs), which runs offline without an API key.
## WASM and host fetch
The Chat Completions and Responses protocol drivers, plus OpenAI and OpenRouter chat drivers, build for `wasm32-unknown-unknown`. Enable `http` on `everruns-contracts`, or `openai` / `openrouter` on `everruns-drivers`. The native socket and TLS dependencies are excluded on that target.
Supply an `HttpTransport` backed by your host’s `fetch`. It receives the encoded method, URL, headers and body, and returns the HTTP status, headers and a lazy byte stream. The existing driver still owns provider options, tool replay, request encoding, response parsing, SSE, retry policy and error classification.
```rust
use std::sync::Arc;
use everruns_contracts::{BearerAuth, Provider};
use everruns_contracts::http_transport::HttpTransport;
use everruns_drivers::openrouter::OpenRouterChatDriver;
fn provider(api_key: String, host_fetch: Arc) -> Provider {
Provider::new(
"openrouter",
OpenRouterChatDriver::new().with_transport(host_fetch),
)
.base_url("https://openrouter.ai/api/v1")
.auth(BearerAuth::new(api_key))
}
```
The same `.with_transport(...)` method is available on `OpenAIChatDriver`, `OpenAICompletionsChatDriver`, `OpenAIProtocolChatDriver` and `OpenResponsesProtocolChatDriver`. Native applications may also inject a transport; otherwise they keep the shared HTTP client. WASM requires an adapter and reports an error if none is supplied.
The adapter owns endpoint and DNS policy, redirect refusal, network timeouts, and cancellation when a request or response stream is dropped. Treat it as trusted application code: it handles provider credentials. Return response headers before consuming the body and stream bytes incrementally. Host JS futures must be wrapped for the single-threaded WASM `Send` contract. OpenAI WebSocket, realtime, speech, embeddings and decision transports remain native; this support covers HTTP chat.
The [Workers/Durable Object fetch adapter](https://github.com/everruns/everruns/blob/main/examples/durable-object-agent/src/fetch.rs) shows exact bytes, manual redirects, cancellation and read deadlines. The [workerd integration tests](https://github.com/everruns/everruns/tree/main/tests/wasm-providers) run model/tool/model exchanges in both a Worker and a Durable Object without reimplementing either provider protocol.
## Custom providers
Use a custom provider when an application talks to a model service that the Framework does not configure for you. The extension boundary is the public `ChatDriver` trait plus a `Provider` value. The agent selects that provider’s model with a plain credential-free string id.
At a high level:
```rust
use everruns::{Agent, BuildError, ChatDriver, Provider};
fn agent_for(driver: impl ChatDriver + 'static) -> Result {
Agent::builder()
.instructions("Use the company model gateway.")
.provider(Provider::new("company-gateway", driver))
.model("assistant-v2")
.build()
}
```
A driver implements the streaming chat-completion contract. It receives the resolved endpoint, model-facing messages (`everruns::llm::Message`), and call configuration, and returns an `LlmResponseStream`. Exact trait methods and event shapes live in the [`everruns::ChatDriver` API reference](https://docs.rs/everruns/latest/everruns/trait.ChatDriver.html).
Keep credential lookup and refresh in trusted host/provider configuration. Model ids must remain safe to log, compare, store, and pass across application boundaries. Provider errors should preserve useful decisions without including secrets.
A driver registered through a `DriverDescriptor` also declares which environment variables it reads, on its own credential fields. Declaring is inert — the driver never reads them — and it is what lets a caller resolve the provider from the environment without any central name mapping. See [Credentials](#credentials).
Use focused provider crates when they already implement the protocol you need. Custom backends and provider registry topology belong to [low-level host composition](https://everruns.com/docs/framework/custom-backends/), not ordinary model selection.
---
# Moving to Platform or Cloud
> What carries over when a Framework agent moves to a self-hosted Everruns Platform or Everruns Cloud, what has to be rebuilt, and what stays behind.
Source:
The Framework and the Platform run the same runtime, so an agent’s behavior moves with it: instructions, capabilities and their configuration, files, MCP servers, and loop settings all have Platform counterparts. What does not move is code. A Platform agent is configuration stored by the server, so anything your Framework app expresses as Rust (function tools, custom capabilities, lifecycle closures, an approver) is rebuilt with a Platform mechanism instead.
Everruns Cloud is the hosted Platform. Everything here applies to both, except that Cloud runs the production deployment grade: capabilities marked dev-only, such as [Computer Use](https://everruns.com/docs/capabilities/computer-use/), are not offered there.
## What transfers
| Framework | Platform agent field |
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `.name("support")` | `name`. Must be lowercase letters, digits, and hyphens, unique in the organization |
| `.instructions(...)` | `system_prompt` |
| `.model("gpt-6.1-sol")` | `default_model_id`, the ID of a model configured for the organization. A model name string is not accepted |
| `.capability(...)` | `capabilities`, the same `{ "ref": ..., "config": ... }` shape |
| `.initial_file(...)`, `.file(...)`, `.readonly_file(...)` | `initial_files` |
| `.mcp_server(...)` for a remote server | `mcpServers` |
| `.max_iterations(n)` | `max_iterations` |
| `.parallel_tool_calls(b)` | `parallel_tool_calls` |
| A `Harness` | A Platform harness (`harness_id` or `harness_name`). `Harness::base()`, `conversation()`, `worker()` and `bashkit_worker()` share the Platform preset definitions; `generic()` is deprecated and preserves the legacy bundle |
`CapabilityRef` serializes as `{"ref": id, "config": {...}}`, which is the format of a Platform agent’s `capabilities` entries, so a capability list can be copied across as JSON. Check each ID against the [capability index](https://everruns.com/docs/capabilities/): a few Framework capabilities are Framework-only (for example host shell), and a capability whose page lists only Platform or Cloud adds features the Framework never had.
To create the agent, write it as a file and import it with `POST /v1/agents/import`, the CLI, or an SDK. See [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/).
```rust
use everruns::{Agent, CapabilityRef, OpenAI};
use serde_json::json;
let agent = Agent::builder()
.name("support")
.instructions("Answer support questions.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.capability("current_time")
.capability(CapabilityRef::new("loop_detection").config(json!({ "threshold": 4 })))
.build()?;
```
becomes:
```markdown
---
name: support
capabilities:
- current_time
- ref: loop_detection
config:
threshold: 4
---
Answer support questions.
```
Pick the default model in the Platform after import, from the models the organization has configured.
## What has to be rebuilt
| Framework | On the Platform |
| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Function tools (`.tool(...)`, `#[everruns::tool]`) | Client-side tools in the agent’s `tools` field, which the model calls and your client executes when it receives `tool.call_requested`; or a remote MCP server that exposes the same tools |
| Custom capabilities written with `everruns::capability` | Built-in capabilities, a remote MCP server, or client-side tools. The Platform does not load application code |
| Lifecycle hooks (`.on_turn_start`, `.on_tool_start`, …) | [User Hooks](https://everruns.com/docs/capabilities/user-hooks/), which run shell commands at lifecycle and tool events |
| `.approver(...)` | [Tool Approval](https://everruns.com/docs/capabilities/tool-approval/), answered by a person in the UI or by your client over the API |
| `.ask_user(...)` | [Ask User](https://everruns.com/docs/capabilities/ask-user/), answered by the session’s user |
| stdio MCP servers | Not supported. The Platform connects only to remote MCP servers |
| `OpenAI::from_env()` and other `from_env` credentials | Provider keys set in the organization’s settings and stored encrypted. Drivers do not read them through `from_env` on the server |
[Client-side tools example](https://github.com/everruns/everruns/blob/main/examples/client_side_tools.sh) shows the client-side tool loop over the API.
## What does not transfer
* **Sessions and history.** There is no session import. A Framework session’s event log, whether in memory or under a `LocalConfig` data directory, stays with the Framework app. Platform sessions start fresh; a Platform session can be exported (`GET /v1/sessions/{id}/export`) but not the reverse.
* **Workspace files.** A local workspace directory is not copied. Put files the agent needs at start in `initial_files`.
* **Application wiring.** Event listeners, `Engine` configuration, and your own HTTP layer have no Platform equivalent. Platform sessions are driven through the [API](https://everruns.com/docs/api/), SDKs, CLI, or UI, and observed through its [observability](https://everruns.com/docs/observability/) exporters.
## Keeping both
The same capability IDs and configuration work in both places, so an agent can be developed in the Framework with simulated models and tests, then registered on the Platform. `CapabilityRef` implements `Deserialize`, so one JSON file of capability entries can feed both the Framework builder and the Platform agent.
## See also
* [Portable and hosted capabilities](https://everruns.com/docs/framework/capability-boundaries/)
* [Deploying a Framework app](https://everruns.com/docs/framework/deployment/)
* [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/)
---
# Observability
> Export Framework sessions to OpenTelemetry and Braintrust, register your own event listeners, and flush them on shutdown.
Source:
An Engine delivers every event from every session it owns to the listeners registered on it. The `everruns` crate ships two listeners, OpenTelemetry and Braintrust, each behind its own Cargo feature. Without either feature nothing leaves the process.
```toml
[dependencies]
everruns = { version = "0.34", features = ["openai", "otel", "braintrust"] }
```
The features are independent: enable only the one you export to.
## OpenTelemetry
The `otel` feature adds `everruns::observability::OpenTelemetry`. Register it on the Engine builder:
```rust
use std::time::Duration;
use everruns::observability::{OpenTelemetry, install_otlp_from_env};
use everruns::{Agent, Engine, Model};
let telemetry = install_otlp_from_env();
let provider = telemetry.tracer_provider()
.ok_or("OTEL_EXPORTER_OTLP_ENDPOINT is required")?;
let engine = Engine::builder()
.observe(OpenTelemetry::from_env().tracer_provider(provider))
.build();
let agent = Agent::builder()
.instructions("Answer concisely.")
.model(Model::simulated("Hello."))
.build()?;
engine.create(agent).run("Say hello.").await?;
let report = engine.shutdown(Duration::from_secs(10)).await;
assert!(!report.timed_out);
assert!(report.stats.listeners.iter().all(|listener| listener.flushed));
telemetry.shutdown().await?;
```
Pick the constructor by who owns the tracer provider:
| Constructor | Tracer provider | Content capture |
| ----------------------------------------------------- | ----------------------------------------------------------------------- | -------------------- |
| `OpenTelemetry::global()` | The global provider your application installed | Off |
| `OpenTelemetry::from_env().tracer_provider(provider)` | An `SdkTracerProvider` you pass in. `Engine::shutdown` force-flushes it | Off |
| `OpenTelemetry::from_env()` | The global provider | From the environment |
None of them installs global state. `install_otlp_from_env()` is the one helper that does: it installs an OTLP exporter as the global tracer provider and a console `tracing` subscriber, and returns a `TelemetryGuard`. Keep the guard alive until after `Engine::shutdown`. Skip it when your application already sets up OpenTelemetry.
`install_otlp_from_env()` reads:
| Variable | Default | Effect |
| ----------------------------- | ---------- | --------------------------------------------------------------------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | none | OTLP endpoint. Without it no spans are exported, and only the console subscriber is installed |
| `OTEL_SERVICE_NAME` | `everruns` | Service name on the spans |
| `OTEL_SERVICE_VERSION` | none | Service version |
| `OTEL_ENVIRONMENT` | none | Deployment environment label |
| `OTEL_SDK_DISABLED` | `false` | `true` turns exporting off without unsetting the endpoint |
| `RUST_LOG`, then `LOG_LEVEL` | `info` | Console log filter |
`OpenTelemetry::from_env()` reads `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` (or its older alias `OTEL_RECORD_CONTENT`) and `EVERRUNS_TRACE_CONVENTIONS`. Content capture puts instructions, messages, reasoning, and tool arguments and results on spans; it is off by default. Turn it on in code with `.record_content(true)`.
The span layout and attribute vocabularies are the same as on the platform: see [OpenTelemetry](https://everruns.com/docs/observability/opentelemetry/).
## Causal links across durable turns
Each turn starts an independent trace with a short `durable.turn` anchor. Child messages and task wakes link back to the creating turn. Restarted execution reads the committed anchor without re-exporting history. Sampling remains controlled by your tracer provider.
Attach a caller’s W3C context to an input when your ingress already has one:
```rust
use everruns::{CausalContext, InputMessage};
let causes = CausalContext::from_traceparents([
"00-1234567890abcdef1234567890abcdef-1234567890abcdef-01",
]);
let input = InputMessage::user("Continue the analysis").with_causal_context(&causes);
// session.send(input).await?;
```
Custom tools can use `ToolCallContext::causal_context()` when submitting a child input. `TaskRequest::with_causal_context` and `WakeRequest::with_causal_context` retain the same typed context through work delivery retries and completion wakes. Custom work backends must retain it with their task/wake snapshots and compare requests with `same_submission_as`, which excludes retry telemetry. Built-in child delegates and task wakes propagate it automatically. Batched and steered inputs contribute links to the reason that reads them. Retrying a stable message ID keeps its first committed causes.
Carriers retain at most sixteen distinct trace/span identities. They exclude baggage, tracestate, arbitrary attributes and remote sampling decisions. Invalid entries are ignored. Old journals without carriers continue to work; disabled observers and dropped events can leave trace gaps. Custom hosts that replace the event or child-delivery adapters must forward the optional causal methods. Legacy `WakeRoutes` string receivers carry only text; their fallback runner forwards causal metadata.
## Braintrust
The `braintrust` feature adds `everruns::observability::Braintrust`. `Braintrust::from_env()` returns `None` when Braintrust is disabled or no API key is set, so registration is conditional:
```rust
use std::time::Duration;
use everruns::Engine;
use everruns::observability::Braintrust;
let mut builder = Engine::builder();
if let Some(braintrust) = Braintrust::from_env() {
builder = builder.observe(braintrust);
}
let engine = builder.build();
// ... run sessions ...
let report = engine.shutdown(Duration::from_secs(10)).await;
```
It reads the `BRAINTRUST_*` variables listed on [Braintrust](https://everruns.com/docs/observability/braintrust/). Its final batch is sent before `Engine::shutdown` returns.
## Your own listeners
Implement `EventListener` to receive the same events in application code, for example to write an audit log or update metrics:
```rust
use everruns::approval::async_trait;
use everruns::{Engine, EventFilter, EventListener, SessionEvent};
struct ToolAudit;
#[async_trait]
impl EventListener for ToolAudit {
async fn on_event(&self, event: &SessionEvent) {
println!("{} {}", event.session_id, event.event_type());
}
fn filter(&self) -> EventFilter {
EventFilter::event_types(["tool.started", "tool.completed"])
}
fn name(&self) -> &'static str {
"tool-audit"
}
}
let engine = Engine::builder().listener(ToolAudit).build();
```
| Method | Default | Purpose |
| ---------- | -------------------- | -------------------------------------------------- |
| `on_event` | required | Handle one event, after it is committed |
| `filter` | `EventFilter::all()` | Restrict delivery to the listed event types |
| `flush` | no-op | Write out buffered state during `Engine::shutdown` |
| `name` | `"EventListener"` | Label in observer statistics |
For a quick hook, `Engine::builder().on_event(|event| async move { ... })` registers an async closure that receives each `SessionEvent` by value.
The event types and payloads are listed in [Events](https://everruns.com/docs/features/events/).
## Delivery and shutdown
Each listener has its own bounded queue, holding up to `OBSERVER_QUEUE_CAPACITY` (8192) events and 8 MiB of serialized event data. A slow listener never delays a turn or another listener. When its queue is full, new events for that listener are dropped and counted. A listener that panics is isolated, and the panic is counted.
`engine.observer_stats()` returns those counters per listener (`delivered`, `dropped`, `panics`, `flushed`, `flush_error`), with `dropped()` and `panics()` totals.
`engine.shutdown(deadline)` stops accepting events, drains the queues, and calls `flush` on each listener. Listeners still running at the deadline are cancelled, and the returned `ObserverReport` has `timed_out` set. Call it before the process exits, or the last events may never reach the exporter. An OpenTelemetry exporter failure leaves `flushed=false` and supplies `flush_error`; inspect both that field and `timed_out`.
`OpenTelemetry::global()` and `from_env()` use the application’s global tracer. They cannot force-flush an opaque application-owned provider, so shutdown reports that limitation. Use `with_tracer_provider` when Engine shutdown should flush it. `TelemetryGuard::tracer_provider()` exposes the convenience installer’s provider; its async `shutdown()` runs the SDK shutdown off runtime threads and reports failures. Dropping the guard retains the SDK’s blocking best-effort fallback.
```rust
use std::time::Duration;
use everruns::Engine;
let engine = Engine::builder().build();
let report = engine.shutdown(Duration::from_secs(10)).await;
if report.timed_out || report.stats.dropped() > 0 {
eprintln!("observers lost events: {:?}", report.stats);
}
```
## Runnable example
The [Framework observability example](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/framework_observability.rs) registers both integrations and runs a turn on a simulated model, so it needs no provider key:
```bash
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
cargo run -p everruns --features otel,braintrust \
--example framework_observability
```
## See also
* [Events and Cancellation](https://everruns.com/docs/framework/events-and-cancellation/), per-session event streams
* [Deploying a Framework app](https://everruns.com/docs/framework/deployment/)
* [OpenTelemetry](https://everruns.com/docs/observability/opentelemetry/) and [Braintrust](https://everruns.com/docs/observability/braintrust/), exporter details shared with the platform
---
# Quickstart
> Install everruns and run a real agent against a live model provider in about five minutes.
Source:
## Install
Add the application-facing crate with a model provider:
```bash
cargo add everruns --features openai
cargo add tokio --features macros,rt-multi-thread
export OPENAI_API_KEY=sk-...
```
`--features openai` bundles the OpenAI driver. `everruns` does not re-export tokio, so add it for `#[tokio::main]`. Any other provider is its own crate — see [Supported providers](https://everruns.com/docs/framework/models-and-providers/#supported-providers).
## Run one turn
```rust
use everruns::{Agent, Engine, OpenAI};
#[tokio::main]
async fn main() -> Result<(), Box> {
let agent = Agent::builder()
.instructions("You are a concise assistant.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.build()?;
let engine = Engine::new();
let session = engine.create(agent);
let turn = session.send_and_wait("Explain durable execution in one sentence.").await?;
println!("{}", turn.response);
Ok(())
}
```
The model id stays credential-free: `.model("…")` names the model, and `.provider(…)` supplies the driver, endpoint, and key separately. `OpenAI::from_env` reads `OPENAI_API_KEY` and redacts it from debug output.
`send_and_wait` is the request/response convenience; use `send` when the application needs to stream output or steer a turn while it runs.
## Give it a tool
An agent becomes useful when it can act. Annotate a function and hand it over:
```rust
use everruns::{Agent, OpenAI};
#[everruns::tool]
/// Look up the current stock level for a SKU.
async fn stock_level(sku: String) -> Result {
Ok(inventory_lookup(&sku).await)
}
let agent = Agent::builder()
.instructions("Answer inventory questions. Use the tool rather than guessing.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.tool(stock_level())
.build()?;
```
The macro derives the JSON schema from the signature, so the model sees typed arguments and the code stays ordinary Rust. See [Tools and macros](https://everruns.com/docs/framework/agents/#tools).
## Where to go next
* [Agents](https://everruns.com/docs/framework/agents/) — instructions, files, workspaces, and MCP.
* [Tools and macros](https://everruns.com/docs/framework/agents/#tools) — typed function tools.
* [Sessions](https://everruns.com/docs/framework/sessions/) — multi-turn state and history.
* [Framework architecture](https://everruns.com/docs/framework/architecture/) — how the pieces fit.
## Testing without a provider
Once you are building for real, you will want tests that do not call a model. `Model::simulated` replays a canned response through the same model/provider path:
```rust
use everruns::{Agent, Engine, Model};
let agent = Agent::builder()
.instructions("You are a concise assistant.")
.model(Model::simulated("Everruns is ready."))
.build()?;
let turn = Engine::new().create(agent).send_and_wait("Are you ready?").await?;
assert_eq!(turn.response, "Everruns is ready.");
```
It is a test double, not a local model: it runs no inference, so it proves your wiring rather than any model behavior. Use it in tests and CI, not as a way to run Everruns without a provider. See [Testing and simulation](https://everruns.com/docs/framework/testing-and-simulation/).
---
# Serve (experimental)
> Build a hosted agent app with attribute macros, file-layout discovery and a manifest, served over a subset of the Everruns server /v1 API.
Source:
> **Experimental.** serve is a proof of concept. Its APIs will change and it has no compatibility promise.
**serve** is an application framework on top of the Everruns Framework. You write an app as a set of annotated functions, the build declares what the app needs from its host, and the binary serves the same `/v1` session API as the Everruns server, so the SDKs, CLI and UI chat view can drive it.
serve adds no runtime of its own. Agents, sessions, tools, approvals, [`ask_user`](https://everruns.com/docs/framework/ask-user/), skills and the durable event log all come from the `everruns` crate: serve is a thin layer over [`Engine`](https://everruns.com/docs/framework/architecture/).

```rust
use serve::prelude::*;
#[agent]
fn analyst() -> Agent {
Agent::builder()
.model("anthropic/claude-sonnet-5") // resolved by the host's model gateway
.instructions(md!("instructions.md")) // agent/instructions.md, hot-reloads in dev
.build()
}
/// Run a read-only SQL query against the warehouse.
#[tool(needs_approval = |a: &RunSql| scan_gb(&a.sql) > 50.0)]
async fn run_sql(cx: &Cx, sql: String) -> Result {
let wh = cx.connection::()?; // credentials never reach the model
cx.progress("querying…").await;
Ok(wh.query(&sql)?.truncate(500))
}
serve::assets!();
#[tokio::main]
async fn main() -> serve::Result {
serve::start(App::builder().discover().build()).await
}
```
For a guided start, [Tutorial: Serve an Agent and Call It from Code](https://everruns.com/docs/framework/serve-tutorial/) builds an agent, protects it with an API key, and calls it from the SDKs.
## Install
```sh
cargo add everruns-serve tokio --features tokio/full
cargo add --build everruns-serve-build
```
The package is `everruns-serve` and the library is imported as `serve`. Add a `build.rs` that calls `serve_build::embed()` so `agent/**` and `serve.toml` are compiled into the binary.
## Try it
Both examples run offline against a scripted simulator, so no keys or network are needed. From a checkout of the repository:
```sh
cargo run -p serve-example-revenue-analyst # dev server on :3000 with a live console
cargo run -p serve-example-revenue-analyst -- eval # run the evals in-process
cargo run -p serve-example-revenue-analyst -- manifest
```
Then talk to it with the same requests you would send an Everruns server:
```sh
ID=$(curl -s localhost:3000/v1/sessions -H 'content-type: application/json' -d '{}' | jq -r .id)
curl -s localhost:3000/v1/sessions/$ID/messages -H 'content-type: application/json' \
-d '{"message":{"content":[{"type":"text","text":"What was revenue last week?"}]}}'
curl -N "localhost:3000/v1/sessions/$ID/sse?after_sequence=0"
```
To use a real model, set `OPENROUTER_API_KEY`, or point `SERVE_GATEWAY_URL` and `SERVE_GATEWAY_KEY` at any OpenAI-compatible gateway. With serve’s `bedrock` feature, `bedrock/` models (for example `bedrock/us.anthropic.claude-sonnet-4-6`) call Amazon Bedrock on the AWS default credential chain whenever `AWS_REGION` is set.
| Example | Shows |
| ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| [Hello app](https://github.com/everruns/everruns/tree/main/examples/serve/hello) | The smallest app: one agent, one tool, one eval. |
| [Revenue Analyst](https://github.com/everruns/everruns/tree/main/examples/serve/revenue-analyst) | A tool with approvals, a skill, Slack, a schedule, MCP and typed connections, a subagent, evals and the Bashkit sandbox. |
| [AG-UI app](https://github.com/everruns/everruns/tree/main/examples/serve/ag-ui) | An agent streamed to `@ag-ui/client` and CopilotKit, with an approval as an interrupt. |
| [AgentCore app](https://github.com/everruns/everruns/tree/main/examples/serve/agentcore) | The same kind of app packaged for Amazon Bedrock AgentCore Runtime. |
| [AgentCore workspace app](https://github.com/everruns/everruns/tree/main/examples/serve/agentcore-workspace) | An AgentCore workspace agent with a shell in the microVM and an approval. |
| [A2A example](https://github.com/everruns/everruns/tree/main/examples/serve/a2a) | Two agents over A2A: a served `researcher`, and an `everruns` agent that delegates to it. |
| [Voice example](https://github.com/everruns/everruns/tree/main/examples/serve/voice) | A hotel front desk people call from the browser, with a greeting and a speaking style. |
## Project layout
The file layout says what each piece is:
```text
revenue-analyst/
build.rs # serve_build::embed()
serve.toml # name, sandbox, extra secrets
agent/
instructions.md # always-on prompt
skills/sql-style/SKILL.md
src/
main.rs # serve::assets!() + serve::start(...)
agent.rs # #[agent]
tools/run_sql.rs # #[tool]
channels/slack.rs # #[channel]
schedules/weekly.rs # #[schedule]
connections/linear.rs # #[connection]
subagents/reviewer.rs # #[agent(sub)]
evals/revenue.rs # #[eval]
```
The macros register each item at link time and `discover()` collects them; `build.rs` embeds `agent/**` and `serve.toml` into the binary. `discover()` reports every problem in the app at once, such as duplicate names, a bad cron expression or a filter naming an unknown tool.
## The pieces
| Macro | On | Becomes |
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `#[agent]`, `#[agent(default)]`, `#[agent(sub)]` | `fn() -> Agent` | An agent. New sessions use the default one; a subagent becomes an `ask_` tool on the others. |
| `#[tool]`, `#[tool(needs_approval)]`, `#[tool(needs_approval = \|a: &Args\| …)]` | `async fn([cx: &Cx,] args…) -> Result` | A tool named after the function, described by its doc comment. It also generates an arguments struct (`run_sql` → `RunSql`) with a JSON Schema. |
| `#[channel]` | `fn() -> impl Into` (a driver such as `Slack` or `Webhook`) | `POST /v1/channels/{name}`. Each external thread maps to one session, and replies are delivered back while the turn runs. `agent = "…"` picks the agent. |
| `#[schedule("0 9 * * MON")]` | `async fn(&Cx) -> Result` | A cron entry. A 5-field cron counts weekdays 0-6 from Sunday (7 is also Sunday), so `0 9 * * 1-5` runs Monday through Friday. |
| `#[connection]` | `fn() -> T` or `fn() -> Result` | A value tools read with `cx.connection::()`. An `McpServer` connection is attached to every agent. |
| `#[eval]` | `async fn(&mut EvalCx) -> Result` | A conversation with assertions, run in-process or against a deployment. |
`Cx` is the one context type. Inside a tool it wraps the Framework’s `ToolCallContext` (session, turn and tool call ids, and `progress`) and adds typed connections, secrets and `start_session`. Approval rules are declared on the tool and enforced by the runtime.
## Commands
The commands are built into the app binary, because only the linked binary knows what it registered.
| Command | What it does |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dev` (default) | Serves the API with local SQLite under `.serve/` and a live console. Models fall back to the simulator, and Markdown prompts and skills hot-reload. |
| `start` | Production mode. Every model must route through a gateway, and every declared secret must be set. With `--store s3://bucket/prefix` (or `SERVE_STORE`) the data lives in a bucket; see [Keep the data in a bucket](#keep-the-data-in-a-bucket). |
| `manifest` | Prints the host contract as JSON: agents, models, tool schemas, skills, channels, cron entries, secrets, sandbox, evals and a build id. |
| `eval [--against URL]` | Runs the evals in-process, or against a running deployment as a gate before promotion. Against an app that requires a credential, set `SERVE_CREDENTIAL` (or pass `--credential`) and every request carries it as a bearer token. |
| `deploy` | Prints what a host would provision from the manifest. The one supported deployment target is [Amazon Bedrock AgentCore](https://everruns.com/docs/framework/serve-agentcore/). |
Sessions that an earlier release kept in the data directory’s `everruns/` folder move to the current session store the first time they are opened, with their history and their shared workspace files. Subagent sessions they started move with them and are linked to their parent. A session that cannot move returns an error; it is never served as missing or empty.
## Keep the data in a bucket
`start --store s3://bucket/prefix` keeps a daemon’s data in an S3 bucket, or in any S3-compatible store that supports conditional writes (R2, MinIO, GCS, Tigris, SeaweedFS). The data directory becomes a local working copy:
* At boot the daemon takes the bucket’s lease, then rebuilds the data directory from the bucket. Files the bucket does not have are removed.
* While it runs, every change is copied to the bucket within about a fifth of a second: databases by the pages that changed, other files (the agents’ workspace) whole.
* A daemon started on another machine with the same `--store` waits for the old one’s lease to expire (15 seconds), rebuilds the data directory, and resumes the turns the old one was running. While the old daemon is still alive and renewing, the new one refuses to start. If the old one wakes up after it was replaced, its next write fails and it stops.
* A clean shutdown (Ctrl+C) copies what is left and releases the lease, so the next daemon starts at once.
Credentials, region and endpoint come from the standard `AWS_*` environment variables. For an S3-compatible store set `AWS_ENDPOINT`, and `AWS_ALLOW_HTTP=true` for a plain-HTTP endpoint. Keep the data directory at the same absolute path on every machine (`SERVE_DATA_DIR`), because a session’s workspace records it.
A crash can lose the changes of its last fifth of a second. One daemon holds a bucket prefix at a time; give each daemon its own prefix.
## Wire API
A serve app speaks a subset of the Everruns server `/v1` API, with the same request and response bodies:
* `POST /v1/sessions` and `GET /v1/sessions/{id}`
* `POST /v1/sessions/{id}/messages`, which also steers an active turn. Its `files` list sends files as base64 `data` or a public `url`, as on the [Agent API](https://everruns.com/docs/how-to/call-your-agent-from-code/#send-files); each is kept in the session’s `/attachments` folder
* `POST /v1/sessions/{id}/cancel`
* `GET /v1/sessions/{id}/sse` (resume with `since_id` or `after_sequence`) and `GET /v1/sessions/{id}/events`
* `POST /v1/sessions/{id}/question-answers`, for [`ask_user`](https://everruns.com/docs/framework/ask-user/) questions
* `POST /v1/sessions/{id}/tool-approvals`, to allow or reject pending tool calls
It adds a few routes of its own: `GET /health`, `GET /v1/agent` (the agent card), channel webhooks, the [AG-UI](#ag-ui-and-copilotkit), [A2A](#a2a) and [voice](#voice) routes, and `POST /v1/sessions/{id}/approvals/{tool_call_id}` to approve or deny one pending tool call. Errors are `application/problem+json`.
### One agent’s API
Every top-level agent also has its own base URL, `/v1/channels/{agent}`, with the same session routes rooted under it: `GET` returns the agent’s card, `POST /v1/channels/{agent}/sessions` starts a session that runs that agent, and `/v1/channels/{agent}/sessions/{id}/…` carries messages, events, cancellation, questions and approvals. A session is reachable only under its own agent’s URL. This is the API an Everruns agent exposes through an API channel, so code written against one agent works against either host. Every SDK’s `AgentClient` calls this API; see the [tutorial](https://everruns.com/docs/framework/serve-tutorial/#6-call-it-from-code). The [Agent API](https://everruns.com/docs/features/agent-api/) reference documents the routes and shapes, and [what differs on serve](https://everruns.com/docs/features/agent-api/#the-same-api-on-serve).
### Authentication
By default the wire API has no authentication and no organizations: run it locally, or behind a host that authenticates requests. Configure one or more methods and every agent route requires `Authorization: Bearer `; anything else gets `401` with `WWW-Authenticate: Bearer`. The methods are the ones an Everruns API channel accepts, checked by the same verifier:
* **API keys.** Static keys, compared in constant time. Set them in `SERVE_API_KEYS`, comma separated.
* **OpenID Connect.** JWTs from an issuer, checked against the keys its discovery document publishes, with a required audience.
* **OAuth 2.0 introspection.** Opaque tokens checked at an RFC 7662 endpoint.
Token methods take the same requirements as a channel: audiences, scopes, subjects, groups, email domains and exact claim values. Methods add up from code, `serve.toml` and the environment:
```rust
use serve::auth::AuthMethod;
let server = serve::Server::builder(app, serve::Mode::Start)
.auth([AuthMethod::oidc("https://login.example.com", ["support-agent"])])
.build()?;
```
serve.toml
```toml
[[auth.methods]]
mode = "oidc"
provider = { type = "oidc", issuer = "https://login.example.com" }
requirements = { audiences = ["support-agent"], scopes = ["agent:call"] }
```
```sh
SERVE_API_KEYS=key-for-ci,key-for-the-web-app ./revenue-analyst start
```
`serve.toml` also takes `api_keys = [...]` under `[auth]`, but it is embedded in the binary, so keep real keys in the environment.
A few routes stay public so clients can find the agent: `GET /health`, each agent’s card at `GET /v1/channels/{agent}` (its `auth` list names the accepted methods), the A2A Agent Card (its `securitySchemes` and `securityRequirements` name the same methods, as an Everruns A2A endpoint’s card does) and the voice test page. Messaging channel webhooks stay public too, because Slack and webhook callers cannot send a serve credential; each channel verifies its platform’s own signature. Auth does not separate callers: every authenticated caller can reach every session.
With auth on, the `dev` console’s sample requests and its approval and question hints include `-H 'authorization: Bearer '`; substitute your own key. To run evals against a protected deployment, give `eval` the credential through the environment, which keeps it out of the process list:
```sh
SERVE_CREDENTIAL=key-for-ci ./revenue-analyst eval --against https://preview.example.com
```
## AG-UI and CopilotKit
With the `ag-ui` feature, every top-level agent also serves [AG-UI](https://docs.ag-ui.com) 1.0 clients such as CopilotKit and `@ag-ui/client`:
```sh
cargo add everruns-serve --features ag-ui
```
The route is `POST /v1/channels/{agent}/ag-ui`, the same shape as an Everruns endpoint’s AG-UI route, so a front end moves between a local serve app and Everruns by base URL and id alone:
```ts
import { HttpAgent } from "@ag-ui/client";
const agent = new HttpAgent({ url: "http://localhost:3000/v1/channels/analyst/ag-ui" });
```
Each AG-UI `threadId` maps to one session, which survives a restart. A thread’s first run records the input’s earlier user and assistant messages as the new session’s history; after that, a run sends only the input’s last user message. Each run streams the turn as AG-UI events, with reasoning, token usage and errors visible. A tool approval or an [`ask_user`](https://everruns.com/docs/framework/ask-user/) question ends the run with an interrupt (`tool_approval` or `everruns.ask_user`), and the next run’s `resume` entries answer it and stream the rest of the turn. These are the same pending requests the approvals and `question-answers` routes answer, so either API can resolve them. Input that is not a valid `RunAgentInput` gets `400`, and an unknown agent `404`. The agent card lists each agent’s route under `ag_ui`. See [Serve AG-UI](https://everruns.com/docs/framework/ag-ui/) for the event and interrupt shapes.
## A2A
With the `a2a` feature, every top-level agent also serves other agents over [A2A](https://a2a-protocol.org) 1.0 JSON-RPC:
```sh
cargo add everruns-serve --features a2a
```
The endpoint is `POST /v1/channels/{agent}/a2a` and its Agent Card is `GET /v1/channels/{agent}/a2a/.well-known/agent-card.json`, the shape of an Everruns A2A endpoint. Any A2A 1.0 client works, including the official `a2a` CLI and another Everruns agent’s `a2a_agent_delegation` capability:
```sh
a2a send -a http://localhost:3000/v1/channels/researcher/a2a/.well-known/agent-card.json "Tide pools"
```
Each A2A `contextId` maps to one session, which survives a restart; each task is one turn. A task streams `working`, then the final reply as a `response` artifact, then `completed` (or `failed`). `GetTask`, `ListTasks`, `CancelTask` and `SubscribeToTask` come from the A2A Rust SDK’s request handler; tasks are kept in memory, so old task ids are forgotten on restart while their context continues. Requests need the `A2A-Version: 1.0` header. A pending approval or `ask_user` question keeps the task `working` until the routes above answer it. The agent card lists each agent’s endpoint under `a2a`. [Framework A2A](https://everruns.com/docs/framework/a2a/) covers serving and calling A2A agents, and [A2A example](https://github.com/everruns/everruns/tree/main/examples/serve/a2a) shows a served agent and a second agent that delegates to it.
Sessions survive a restart: the binary rebuilds each agent and resumes the session from the local store. A turn the old process left waiting on an approval or an `ask_user` question waits again: the call runs again, so the request is pending once more under the same tool call id, and answering it finishes the turn. A turn cut off while a tool executed is not re-run. A session pinned to a different build gets `409 Conflict` with an `x-serve-build` header, so a host can route it to the build that owns it.
## Voice
With the `voice` feature, every top-level agent also takes browser calls through a [voice channel](https://everruns.com/docs/framework/voice/):
```sh
cargo add everruns-serve --features voice
```
`POST /v1/channels/{agent}/voice/calls` takes the browser’s WebRTC offer and answers the SDP answer, the call id and the session id; `POST /v1/channels/{agent}/voice/calls/{call_id}/end` hangs up, and `GET /v1/channels/{agent}/voice` is a test page with a **Start call** button. Each utterance is a user message on an ordinary session, so a call can continue a typed conversation. The optional `[voice]` section of `serve.toml` sets the voice, greeting and speaking style for every agent. Speech uses OpenAI with `OPENAI_API_KEY`; without it, `dev` and `eval` use the offline simulator and `start` refuses calls. The agent card lists each agent’s route under `voice.endpoints`. See [Build a voice agent](https://everruns.com/docs/framework/voice/).
## Limitations
* After a restart, an approval answered “always” before it is asked again, and a request parked by a subagent is lost.
* A2A tasks are held in memory, and A2A 0.3 clients are not served.
* A deny note is not passed to the model.
* There is no generic OCI build or Postgres or NATS adapter. [AgentCore](https://everruns.com/docs/framework/serve-agentcore/) is the one supported deployment target.
* The server’s agent, harness, workspace and tool-result routes are not served.
## Amazon Bedrock AgentCore
[`everruns-serve-agentcore`](https://docs.rs/everruns-serve-agentcore) runs a serve app on AgentCore Runtime. Replace `serve::start` with `serve_agentcore::start` in `main`. With no command, the binary then serves AgentCore’s contract on port 8080: `GET /ping` and `POST /invocations`, which takes an AG-UI `RunAgentInput` or `{"prompt": "..."}` and streams AG-UI events. serve’s own commands keep working. See [Serve on AgentCore](https://everruns.com/docs/framework/serve-agentcore/) for deployment, persistence on session storage, tools and models.
## celld
[`everruns-serve-celld`](https://docs.rs/everruns-serve-celld) runs a serve app durably on [celld](https://github.com/denoland/celld), self-hosted Durable Objects. Replace `serve::start` with `serve_celld::start`; the binary then runs in a container that a Durable Object supervises, and the object keeps a snapshot and a request journal so a lost container is restored and its interrupted turn replayed. See [Serve on celld](https://everruns.com/docs/framework/serve-celld/).
The full guide, wire reference and hosting contract live next to the crate in [serve reference guides](https://github.com/everruns/everruns/tree/main/crates/serve/docs).
## File-based agents
Load and export the shared portable package format, including initial files and complete skill directories. See [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/) for validation, diffs, model bindings and runnable examples.
---
# Serve on Amazon Bedrock AgentCore (experimental)
> Deploy a serve app to Amazon Bedrock AgentCore Runtime with everruns-serve-agentcore: sessions on session storage, tools in the microVM, models via the Gateway.
Source:
> **Experimental.** Like [serve](https://everruns.com/docs/framework/serve/), `everruns-serve-agentcore` is a proof of concept with no compatibility promise.
[Amazon Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agents-tools-runtime.html) runs your agent container once per session, each copy in its own microVM, and talks to it over HTTP on port 8080. `everruns-serve-agentcore` (imported as `serve_agentcore`) serves that contract around a [serve](https://everruns.com/docs/framework/serve/) app, so the same app runs under `dev` on a laptop and on AgentCore without changes.
## How it maps
| AgentCore | serve |
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Runtime session (`X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`) | One serve session: the AG-UI thread defaults to the session id |
| `POST /invocations` | An AG-UI 1.0 run of the app’s agent, streamed as server-sent events |
| `GET /ws` (WebSocket) | The same runs: each text message is one invocation body, each AG-UI event one text message back |
| `GET /ping` | `Healthy`, or `HealthyBusy` while a turn runs so AgentCore keeps the microVM up |
| Session storage mount (`/mnt/workspace`) | serve’s SQLite session log and the agent’s workspace |
| The session microVM | The sandbox: `[sandbox] kind = "microvm"` gives the agent a real shell |
| Gateway inference target (`/inference/v1`) | serve’s model gateway (`SERVE_GATEWAY_URL`) |
| Gateway MCP target | A `#[connection]` returning `McpServer` |
serve’s `/v1` wire API and `/health` stay mounted next to the AgentCore routes.
## Quickstart
Start from any serve app and change `main`:
```rust
use serve::prelude::*;
#[tokio::main]
async fn main() -> serve::Result {
serve_agentcore::start(App::builder().discover().build()).await
}
```
```toml
[dependencies]
everruns-serve = { version = "0.33", features = ["ag-ui"] }
everruns-serve-agentcore = "0.33"
```
With no command (what AgentCore runs) or `agentcore`, the binary serves the AgentCore contract in serve’s `start` mode. Every other command (`dev`, `start`, `eval`, `manifest`, `deploy`) is serve’s own.
Try the contract locally with the offline simulator:
```sh
cargo run -p serve-example-agentcore -- agentcore --dev
curl -s localhost:8080/ping
# {"status":"Healthy"}
curl -N localhost:8080/invocations \
-H 'content-type: application/json' \
-H 'x-amzn-bedrock-agentcore-runtime-session-id: local-session-0000000000000000000000001' \
-d '{"prompt":"Where is order A-1001?"}'
# data: {"type":"RUN_STARTED",...}
# ...
# data: {"type":"RUN_FINISHED",...}
```
`/invocations` takes either an AG-UI `RunAgentInput` (what AgentCore’s AG-UI protocol forwards from CopilotKit or `@ag-ui/client`) or `{"prompt": "..."}` (a plain `InvokeAgentRuntime` call). Without a `threadId` and without the session header, it answers `400` with a problem document.
## Deploy
AgentCore runs `linux/arm64` images on port 8080. The [agentcore example](https://github.com/everruns/everruns/tree/main/examples/serve/agentcore) has a distroless Dockerfile; build and push from the repository root:
```sh
docker buildx build --platform linux/arm64 \
-f examples/serve/agentcore/Dockerfile -t "$ECR_REPO:latest" --push .
```
Create the runtime with the AG-UI protocol and session storage:
```sh
aws bedrock-agentcore-control create-agent-runtime \
--agent-runtime-name serve_agentcore_example \
--agent-runtime-artifact "containerConfiguration={containerUri=$ECR_REPO:latest}" \
--role-arn "$EXECUTION_ROLE_ARN" \
--network-configuration networkMode=PUBLIC \
--protocol-configuration serverProtocol=AGUI \
--filesystem-configurations '[{"sessionStorage":{"mountPath":"/mnt/workspace"}}]' \
--environment-variables "SERVE_GATEWAY_URL=$GATEWAY_URL/inference/v1,SERVE_GATEWAY_KEY=$GATEWAY_TOKEN"
```
Use `serverProtocol=HTTP` instead when callers send `{"prompt": ...}`; `/invocations` accepts both either way.
## Invoke
From the CLI:
```sh
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-arn "$AGENT_ARN" \
--runtime-session-id "$(uuidgen)-$(uuidgen)" \
--payload "$(echo -n '{"prompt":"Where is order A-1001?"}' | base64)" \
out.txt && cat out.txt
```
From an AG-UI client, point `HttpAgent` at the runtime’s invocations URL and send the session header, as the [workspace example’s client](https://github.com/everruns/everruns/tree/main/examples/serve/agentcore-workspace/client) does. It also shows the approval round trip: the first run ends with an `interrupt` outcome, and the next run resumes it.
```js
const agent = new HttpAgent({
url: `https://bedrock-agentcore.${region}.amazonaws.com/runtimes/${encodeURIComponent(agentArn)}/invocations?qualifier=DEFAULT`,
headers: {
Authorization: `Bearer ${token}`,
"X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": session,
},
threadId: session,
});
```
Reuse a session id to return to the same microVM, conversation and workspace.
Over AgentCore’s WebSocket transport, connect to the runtime’s `/ws` URL (`wss://bedrock-agentcore..amazonaws.com/runtimes//ws?qualifier=DEFAULT`) with the same session header, and send one invocation body per text message. Each AG-UI event comes back as its own text message, ending with `RUN_FINISHED` or `RUN_ERROR`; send the next body on the same socket for the next turn.
The upgrade request is authenticated like any other call: sign it with SigV4 (service `bedrock-agentcore`), either in the headers or, where the client cannot set upgrade headers (a browser), as a presigned URL that also carries the session id as the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` query parameter. The AWS SDKs have no WebSocket client, so sign the request yourself and hand it to a WebSocket library; the [agentcore-workspace example](https://github.com/everruns/everruns/tree/main/examples/serve/agentcore-workspace) has a Python client (`client/ws.py`) that does both.
## Persistence
AgentCore mounts session storage only when an invocation arrives, not while the container initializes. So `/ping` never touches storage, and the server boots on the first other request. It then picks, in order:
1. `SERVE_DATA_DIR` (or a SQLite `DATABASE_URL`) when set. The workspace is `SERVE_WORKSPACE`, else `workspace/` under the data dir.
2. `/mnt/workspace` when it is mounted: the session log goes to `/mnt/workspace/.serve`, and the agent’s workspace is `/mnt/workspace`.
3. Otherwise a temporary directory, with a warning that nothing survives the microVM stopping.
With session storage, a conversation survives AgentCore stopping the microVM after its idle timeout: the next invocation with the same session id resumes it from the log. That includes a turn parked on an approval or an `ask_user` question: on the new microVM the interrupt is still open, and the client’s resume run answers it as before.
Session storage is an NFS mount that refuses POSIX file locks, so the binary switches SQLite to dot-file locking (`unix-dotfile`) and rollback-journal mode for the whole process, and clears lock files a stopped microVM left behind. That is safe because one process owns a session’s storage.
Session storage is per session, kept for 14 days, and reset when you deploy a new runtime version. For history across sessions and versions, point `DATABASE_URL` at storage you own, such as an S3 Files or EFS mount (both need VPC mode).
## Tools
* **In-process tools.** `#[tool]` functions run in the container like anywhere else, including `needs_approval` tools: over AG-UI the run ends with a `tool_approval` interrupt and the client’s next run answers it.
* **A real shell.** With `[sandbox] kind = "microvm"` in `serve.toml`, each agent gets `bash` and file tools in the session’s workspace, running directly in the container. The microVM is the isolation boundary, so there is no second sandbox inside it. Under `dev` and `eval` the same setting uses the bashkit virtual shell, so local runs stay safe. Install whatever the agent should run (git, python, a compiler) in the image, as the [workspace example](https://github.com/everruns/everruns/tree/main/examples/serve/agentcore-workspace) Dockerfile does.
* **AgentCore Gateway tools.** A Gateway is an MCP server, so connect it like any other:
```rust
#[connection]
fn gateway() -> McpServer {
McpServer::http("https://.gateway.bedrock-agentcore..amazonaws.com/mcp")
.auth(Secret::named("GATEWAY_TOKEN"))
}
```
`start` mode, which AgentCore runs, refuses to boot when `GATEWAY_TOKEN` is unset, so a misconfigured runtime fails its first invocation instead of running without tools.
## Models
Name a Bedrock model as `bedrock/`, where the rest is a Bedrock model id or inference profile:
```rust
Agent::builder()
.model("bedrock/us.anthropic.claude-sonnet-4-6")
```
When `AWS_REGION` or `AWS_DEFAULT_REGION` is set, serve calls Amazon Bedrock directly with the runtime’s execution role: no keys in the image or the environment. Credentials come from the AWS default chain, which on AgentCore is the execution role’s container credentials, and refresh before they expire. The role needs `bedrock:InvokeModelWithResponseStream` on the model; for a cross-region inference profile such as `us.…`, on the profile and on the foundation model in each region it routes to. Pass the region in `--environment-variables` if the runtime does not already provide it. This route is serve’s `bedrock` feature, on by default in `everruns-serve-agentcore`; `default-features = false` drops it and the AWS SDK.
Other `provider/model` ids go through serve’s gateway. Set `SERVE_GATEWAY_URL` to an AgentCore Gateway inference endpoint (`https:///inference/v1`) with targets named after providers (`anthropic`, `openai`), and `anthropic/claude-sonnet-5` routes as-is. Provider API keys then stay in the Gateway’s credential providers instead of the runtime’s environment.
## Configuration
| Variable | Meaning |
| ---------------------------------------- | ----------------------------------------------------------------------- |
| `PORT` | Listen port, default `8080` (AgentCore requires 8080) |
| `SERVE_AGENTCORE_AGENT` | The agent `/invocations` runs, default the app’s default agent |
| `SERVE_DATA_DIR`, `DATABASE_URL` | Where the session log lives. Default: session storage when mounted |
| `SERVE_WORKSPACE` | The agent’s workspace. Default: the session storage mount |
| `SERVE_GATEWAY_URL`, `SERVE_GATEWAY_KEY` | serve’s model gateway |
| `AWS_REGION`, `AWS_DEFAULT_REGION` | Region for `bedrock/` models, which run on the execution role |
## Compared with other AgentCore agents
| | serve-agentcore | Strands / `bedrock-agentcore` SDK |
| --------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------- |
| `/ping`, `/invocations`, busy reporting | Yes | Yes |
| AG-UI protocol | Yes, AG-UI 1.0 including interrupts | Yes |
| `/ws` WebSocket | Yes | Yes |
| MCP and A2A server protocols | Not yet | Yes |
| Conversation persistence | SQLite on session storage, including a turn parked on an approval or question | File or AgentCore Memory session managers |
| Long-term memory (AgentCore Memory) | Not yet | Yes |
| Gateway tools | MCP connection | MCP client |
| Shell and files | Built-in (`sandbox = "microvm"`) | Bring your own tools |
| Code Interpreter, Browser | Not yet | Client libraries |
| Identity (OAuth to third parties) | Secrets from the environment | `@requires_access_token` |
| Offline evals and simulator | Yes (`eval`, `--dev`) | No |
## Limits
* Approvals and `ask_user` questions park a turn without keeping the microVM busy. AgentCore may stop the microVM while one waits; with session storage the next microVM asks it again, but an “always” answer given before the restart is not remembered after it, and a request a subagent parked is lost.
* `#[schedule]`s run in-process, so they only fire while a session’s microVM is up. Use EventBridge to call `InvokeAgentRuntime` on a schedule instead.
* Only `bedrock/…` models use the runtime’s execution role. Other providers need the gateway or their own keys. The role must allow `bedrock:InvokeModelWithResponseStream`; a missing permission surfaces as an `AccessDeniedException` on the first turn.
## Reference
* [`everruns-serve-agentcore` API reference](https://docs.rs/everruns-serve-agentcore)
* [AgentCore AG-UI protocol contract](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui-protocol-contract.html)
* [AgentCore session storage](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-persistent-filesystems.html)
---
# Serve on celld (experimental)
> Run Framework Engine sessions on celld with per-session Durable Object authority and replaceable native execution.
Source:
`everruns-serve-celld` is a source-only reference adapter, excluded from the product publish set. Use the repository example to build it. It hosts ordinary serve agents through `everruns::Engine`; the hosting plugin selects celld Durable Objects for session authority. Native containers and Wasm isolates use the shared Engine path; qualification covers the failure domains described below.
Each tenant/session has its own authority object. That object commits creation, admission receipts, canonical history and execution epochs, and retains alarms for unfinished work. Native containers execute the registered Framework build. They can be replaced without restoring canonical snapshots or replaying accepted HTTP requests.
```rust
#[tokio::main]
async fn main() -> serve::Result {
serve_celld::start(serve::App::builder().discover().build()).await
}
```
Configure `AUTHORITY_URL`, `TENANT_ID`, `AUTHORITY_TOKEN` and a distinct `EXECUTOR_TOKEN` on the native host. Configure user API authentication separately with `SERVE_API_KEYS`. Startup refuses missing authority configuration.
The Worker exposes the ordinary `/v1` routes and addresses authority objects by authenticated tenant and typed session identity. Keep creation and message IDs when retrying uncertain replies. Creation acknowledges the committed binding; a successful send acknowledges durable admission. Turn assignment and completion are observed separately. Disconnecting an event reader does not cancel work.
`SERVE_WORKSPACE_DIR` reopens separate workspace directories by session. OS tools also need an explicit sandbox adapter through `serve_celld::Options::microvm`; remote workspace/compute services can be supplied through `Options::environment`. Commands must be isolated from the Engine process and its authority credentials. The adapter never enables an uncontained shell. Session journal durability does not preserve ephemeral container files. The local catalog/cache can be reconstructed from authority binding metadata. Channel inbox, outbox and scheduling guarantees require separately supported host services.
The former `/cells/` routing and `/celld/snapshot`/`restore` protocol are removed. Existing deployments using that experimental protocol need an explicit migration; their snapshots are not the new authority format.
See the [runnable example](https://github.com/everruns/everruns/tree/main/examples/serve/celld) for setup and deployment. The [isolate example](https://github.com/everruns/everruns/tree/main/examples/celld-engine) uses the same Framework Agent and Engine as Cloudflare, without a container or a celld-specific agent loop. Workerd tests and a local replicated fleet establish only their tested failure modes; production bucket consistency, alarms, build retention and workspace persistence remain deployment responsibilities.
---
# Tutorial: Serve an Agent and Call It from Code
> Build an agent with serve, protect it with an API key, and call it from Python, TypeScript or Rust with the SDK's AgentClient.
Source:
> **Experimental.** [serve](https://everruns.com/docs/framework/serve/) is a proof of concept. Its APIs will change and it has no compatibility promise.
By the end of this tutorial you will have an order-status agent running on your machine as a serve app, with one tool, a real model and an API key in front of it, and a short program in Python, TypeScript or Rust that talks to it with the SDK’s `AgentClient`.
This is a guided lesson, so it makes the choices for you. The [serve reference](https://everruns.com/docs/framework/serve/) covers everything it leaves out.
## What you need
* Rust 1.94 or later, with `cargo`.
* An OpenAI API key in `OPENAI_API_KEY`. The agent uses `openai/gpt-6.1-sol`.
* For the client: Python 3.10 or later, Node.js 22 or later, or Rust.
* `curl` and `jq` for the checks along the way.
## 1. Create the project
```sh
cargo new order-desk
cd order-desk
mkdir -p agent src/tools
```
Replace the dependencies in `Cargo.toml`:
```toml
[dependencies]
everruns-serve = "0.48"
tokio = { version = "1", features = ["full"] }
[build-dependencies]
everruns-serve-build = "0.48"
```
API keys need `everruns-serve` 0.48 or later.
The package is `everruns-serve` and the library is imported as `serve`. Add a `build.rs` next to `Cargo.toml`, so the prompt in `agent/` and `serve.toml` are compiled into the binary:
build.rs
```rust
fn main() {
serve_build::embed();
}
```
Name the app in `serve.toml`:
```toml
name = "order-desk"
```
## 2. Write the agent
The prompt goes in `agent/instructions.md`:
```markdown
You answer customer questions about their orders for an online shop.
When someone asks about an order, call `order_status` with its number and
answer in one or two sentences. If the tool finds no such order, say so and
ask for the number again. Never guess a status.
```
The tool goes in `src/tools/order_status.rs`. The doc comments become the descriptions the model reads, and the function name becomes the tool name:
```rust
use serve::prelude::*;
/// Look up the status of an order by its number.
#[tool]
async fn order_status(
cx: &Cx,
/// The order number, for example 42.
order: u32,
) -> Result {
cx.progress(format!("looking up order {order}")).await;
// A real app would query its order database here.
match order {
42 => Ok("Shipped October 9 with UPS, tracking 1Z999AA10123456784, arrives October 13.".into()),
43 => Ok("Paid. Ships within two business days.".into()),
_ => bail!("there is no order {order}"),
}
}
```
`src/tools/mod.rs` declares it:
```rust
mod order_status;
```
Finally the agent and `main` in `src/main.rs`:
```rust
mod tools;
use serve::prelude::*;
/// Answers questions about orders.
#[agent]
fn support() -> Agent {
Agent::builder()
.model("openai/gpt-6.1-sol")
.instructions(md!("instructions.md"))
.build()
}
serve::assets!();
#[tokio::main]
async fn main() -> serve::Result {
serve::start(serve::App::builder().discover().build()).await
}
```
`#[agent]` and `#[tool]` register the function where it is defined, and `discover()` collects them, so nothing lists the tool by hand. The agent’s doc comment is its description, and its function name, `support`, is its name in URLs.
## 3. Run it
```sh
export OPENAI_API_KEY=sk-...
cargo run
```
The first build takes a few minutes. Then the app prints what it found:
```text
serve · order-desk 0.1.0 · Dev · build b49b68ee33c43b82d
⚠ experimental proof of concept; APIs will change
agent support (default) · openai/gpt-6.1-sol → openai
tool order_status
sandbox none
data .serve
listen http://localhost:3000
```
`→ openai` says the model goes to OpenAI with your key. If it says `simulator`, `OPENAI_API_KEY` is not set in this shell. A set `OPENROUTER_API_KEY` or `SERVE_GATEWAY_URL` takes precedence over `OPENAI_API_KEY`; see [Try it](https://everruns.com/docs/framework/serve/#try-it).
`cargo run` with no command is `dev`: sessions are kept in SQLite under `.serve/`, the terminal shows each turn as it happens, and edits to `instructions.md` apply without a rebuild.
## 4. Talk to it with curl
The agent’s API lives at its own base URL, `/v1/channels/support`. In a second terminal, read its card:
```sh
curl -s localhost:3000/v1/channels/support
```
```json
{"name":"support","description":"Answers questions about orders.","streaming":true,"input":{"text":true,"images":true,"files":true},"auth":[],"links":{"sessions":"/v1/channels/support/sessions"}}
```
Start a session, send a message, and follow the session’s events:
```sh
AGENT=localhost:3000/v1/channels/support
ID=$(curl -s -X POST $AGENT/sessions -H 'content-type: application/json' -d '{}' | jq -r .id)
curl -s -X POST $AGENT/sessions/$ID/messages -H 'content-type: application/json' \
-d '{"message":{"role":"user","content":[{"type":"text","text":"Where is order 42?"}]}}'
curl -N "$AGENT/sessions/$ID/sse?after_sequence=0"
```
The stream shows the turn as server-sent events: `turn.started`, `tool.started` and `tool.completed` for the lookup, `output.message.delta` as the answer streams, and `turn.completed`. The stream stays open for the next turn; stop it with Ctrl+C. The first terminal shows the same turn:
```text
21:42:34 4cc33a ● new session on support
21:42:34 4cc33a ▸ Where is order 42?
21:42:36 4cc33a ⚙ order_status {"order":42}
21:42:36 4cc33a … looking up order 42
21:42:36 4cc33a ✓ order_status
21:42:38 4cc33a ◂ Order 42 shipped on October 9 via UPS and is expected to arrive October 13. Your tracking number is **1Z999AA10123456784**.
```
## 5. Protect it with an API key
Anyone who can reach port 3000 can use the agent so far. Stop the app with Ctrl+C, make a key, and start it in production mode with that key:
```sh
export AGENT_KEY=$(openssl rand -hex 32)
SERVE_API_KEYS=$AGENT_KEY cargo run -- start
```
`start` is production mode: a model with no route is an error instead of a simulator. `SERVE_API_KEYS` takes a comma-separated list, so each application can get its own key and you can rotate one at a time. Keep keys in the environment: `serve.toml` is compiled into the binary.
The startup banner’s sample requests now carry an `authorization` header. Without a key, every session route answers `401`:
```sh
curl -si -X POST localhost:3000/v1/channels/support/sessions -H 'content-type: application/json' -d '{}'
```
```text
HTTP/1.1 401 Unauthorized
www-authenticate: Bearer
...
{"detail":"a valid bearer credential is required","status":401,"title":"Unauthorized"}
```
With the key as a bearer token it works again:
```sh
curl -s -X POST localhost:3000/v1/channels/support/sessions \
-H "authorization: Bearer $AGENT_KEY" -H 'content-type: application/json' -d '{}'
```
The card stays public so clients can find the agent, and its `auth` list now says which credential it takes: `"auth":[{"type":"agent_key"}]`. API keys are one of three methods; [Authentication](https://everruns.com/docs/framework/serve/#authentication) covers OpenID Connect and OAuth 2.0 introspection.
## 6. Call it from code
Every Everruns SDK has an `AgentClient` for one agent’s API. It reads the agent’s base URL from `EVERRUNS_AGENT_URL` and the credential from `EVERRUNS_AGENT_KEY`, and sends the credential as a bearer token. In the terminal where you will run the client:
```sh
export EVERRUNS_AGENT_URL=http://localhost:3000/v1/channels/support
export EVERRUNS_AGENT_KEY=$AGENT_KEY # the key from step 5
```
Pick one language.
### Python
```sh
python3 -m venv .venv
. .venv/bin/activate
pip install "everruns-sdk>=0.3.1"
```
Save this as `ask.py`:
```python
import asyncio
from everruns_sdk import AgentClient
async def main():
async with AgentClient() as agent:
card = await agent.card()
print(f"Agent: {card['name']}")
session = await agent.create_session()
print(await agent.run("Where is order 42?", session["id"]))
print(await agent.run("And order 43?", session["id"]))
asyncio.run(main())
```
```sh
python ask.py
```
```text
Agent: support
Order 42 shipped October 9 via UPS and is expected to arrive October 13. Your tracking number is **1Z999AA10123456784**.
Order 43 is paid and will ship within two business days.
```
### TypeScript
```sh
npm init -y
npm pkg set type=module
npm install @everruns/sdk tsx
```
Save this as `ask.ts`:
```typescript
import { AgentClient } from "@everruns/sdk";
const agent = new AgentClient();
const card = await agent.card();
console.log(`Agent: ${card.name}`);
const session = await agent.createSession();
console.log(await agent.run("Where is order 42?", session.id));
console.log(await agent.run("And order 43?", session.id));
```
```sh
npx tsx ask.ts
```
### Rust
```sh
cargo new ask
cd ask
cargo add everruns-sdk tokio --features tokio/full
```
Replace `src/main.rs`:
```rust
use everruns_sdk::AgentClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let agent = AgentClient::from_env()?;
println!("Agent: {}", agent.card().await?.name);
let session = agent.create_session(None, None).await?;
println!("{}", agent.run("Where is order 42?", Some(&session.id)).await?);
println!("{}", agent.run("And order 43?", Some(&session.id)).await?);
Ok(())
}
```
```sh
cargo run
```
TypeScript and Rust print the same three lines as Python.
`run` sends one message, follows the event stream until the turn ends, and returns the agent’s reply. Passing the session id keeps both questions in one conversation, which is why “And order 43?” needs no more context. Without a session id, `run` starts a new session each time. For more control, the client has the session calls directly: `create_session`, `send_message`, `stream_events`, `list_events`, `cancel`, and the answers to questions and tool approvals.
## What you built
* A serve app with one agent and one tool, running on a real model.
* An API key in front of the agent’s session routes, with the card left public.
* A client that holds only that key and the agent’s URL.
The same client code works against an agent hosted on Everruns: point `EVERRUNS_AGENT_URL` at the agent’s Agent API channel and use an agent key. See [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/) and the [Agent API](https://everruns.com/docs/features/agent-api/) reference.
## Next steps
* Hold a tool call for a person’s approval with `#[tool(needs_approval)]`, and answer it with `submit_tool_approvals`. See [The pieces](https://everruns.com/docs/framework/serve/#the-pieces).
* Check the agent’s behavior with `#[eval]` and `cargo run -- eval`. To run the evals against the protected app, set `SERVE_CREDENTIAL=$AGENT_KEY` and pass `--against http://localhost:3000`.
* Run the app on [Amazon Bedrock AgentCore](https://everruns.com/docs/framework/serve-agentcore/), or keep its data in a bucket with [`start --store`](https://everruns.com/docs/framework/serve/#keep-the-data-in-a-bucket).
---
# Session Controls
> Run slash commands, hot-install capabilities, switch model, reasoning effort and MCP servers, and query session context on a live Engine session.
Source:
A host that drives a `Session`, such as a terminal UI, a print mode or an ACP server, needs more than send and wait. It runs slash commands, installs extensions while the session is live, switches model or reasoning effort, adds MCP servers, and reviews what the agent did. `Session` exposes those operations directly. None of them hands out a runtime, starts a turn or writes to the conversation history.
Stability: experimental.
## Commands
Capabilities contribute slash commands. Every frontend of one session sees the same list, and `run_command` dispatches against it with the context the next model call will use: the same provider, message filters, capability instructions and filesystem.
```rust
use everruns::{Agent, Engine, OpenAI};
let agent = Agent::builder()
.instructions("You maintain this repository.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.capability("btw")
.build()?;
let session = Engine::new().create(agent);
for command in session.commands().await? {
println!("/{} {}", command.name, command.description);
}
// `/btw` answers a side question without interrupting the main task.
let output = session.run_command("/btw", Some("which files changed?")).await?;
println!("{}", output.message);
```
`run_command` runs out of band: no message is appended and no turn starts. It is safe to call from a tool of the running turn, which is how a model-facing `run_command` tool reaches the same registry. An unknown command returns `SessionControlError::Rejected`.
## Hot-installed capabilities
Registering code and activating it are separate steps. `Engine::register_capability` makes a trusted capability (written against the Core `Capability` contract from [host services](https://everruns.com/docs/framework/host-services/)) resolvable for every session the Engine runs. It changes no model surface until a session activates it.
```rust
use everruns::hosting::services::Capability;
use everruns::{CapabilityRef, Engine, Session, SessionControlError};
engine.register_capability(extension)?;
session
.activate_capability(CapabilityRef::new("my_extension"))
.await?;
// Its tools, instructions and commands are part of the next model call.
session.run_command("/my-command", None).await?;
session.deactivate_capability("my_extension").await?;
// Absent from the next model call; its commands are gone too.
```
Activation validates the configuration and dependencies before anything changes, and repeating it is a no-op. A capability the Agent declares cannot be deactivated at the session layer. Registration never overrides: an id or alias the Framework catalog or another registration already serves is rejected.
The activation is the session’s persisted capability overlay. Capability code is never persisted, so after a restart register it again before resuming sessions that use it, as you re-attach the Agent.
## Model, reasoning effort and MCP servers
```rust
use everruns::{McpServer, Model, ReasoningEffort, Session};
session.set_model("gpt-6-luna").await?;
session.set_reasoning_effort(Some(ReasoningEffort::High)).await?;
session
.add_mcp_server(McpServer::http("docs", "https://mcp.example.com/mcp"))
.await?;
// Unavailable selections fail and leave everything as it was.
assert!(session.set_model(Model::on("not-configured", "x")).await.is_err());
```
A plain model id selects a model of the Agent’s provider; `Model::on` names a provider the Agent or its host provider services already serve. A model value that bundles its own provider is refused: a live selection never changes credentials or provider code, and nothing falls back to another provider. Changes apply from the next model call, including later calls of a turn that is running. `reset_model` returns to the Agent’s model, and `refresh_mcp` rediscovers MCP tools after a credential refresh.
## Queries for host review
`command_context` returns the same projection as `inspect` (model, filtered messages, tools, instructions and commands) but answers during an active turn too. `complete_out_of_band` runs a tool-less completion over that context with the session’s provider and selections, for checks such as reviewing whether a task was finished:
```rust
let verdict = session
.complete_out_of_band(Some("Answer yes or no."), "Was the request completed?")
.await?;
```
When the host owns the review policy and its token budget, `complete` sends a request the host curates instead. The provider receives exactly its system prompts and messages, with no tools. The session adds none of its own instructions or conversation, and no message filters or model views apply. What the session does contribute is what it owns: the provider and model its next model call would use (live selections included), the Agent’s call settings, attachment resolution and provider errors.
```rust
use std::time::Duration;
use everruns::{CompletionRequest, ReasoningEffort};
let verdict = session
.complete(
CompletionRequest::new()
.system_prompt("Decide whether the task is complete. Answer yes or no.")
.user(evidence)
.purpose("completion_review")
.reasoning_effort(ReasoningEffort::Low)
.timeout(Duration::from_secs(60)),
)
.await?;
```
To review with filtered session context, read `command_context` and reduce it into your own bounded messages. A request carries 1 to 64 user or assistant messages and up to 8 system prompts. System, tool-call and tool-result messages are rejected.
* **Metadata.** `purpose` and `metadata` add provider metadata: up to 8 entries, keys of lowercase ASCII letters, digits, `_`, `-` or `.`, and values up to 256 bytes. Keys the session stamps from its own authority (`session_id`, `harness_id`, `turn_id`, `exec_id`, `org_id`, `agent_id`, `model_id`) are rejected, not overwritten. When the host also sets request metadata for every model call, the request’s keys win over the host’s, and `session_id` always comes from the session.
* **Reasoning effort.** Without `reasoning_effort` the request uses the session’s current selection. A per-request effort applies to that request only, and an effort the model’s curated profile cannot honor is rejected.
* **No fallback.** A selected provider the session can no longer serve fails the request instead of reaching another provider.
* **Timeouts.** `timeout` ends the request with `SessionControlError::TimedOut`. Dropping the future abandons it as well.
`complete_out_of_band` remains the convenience form: it is a request whose system prompts are the session’s instructions plus yours, and whose messages are the filtered conversation plus your prompt.
None of these append to history, start a turn, take execution ownership or resume interrupted work, and all of them are answered during an active turn, including from a tool of that turn. `history()` remains the bounded way to page the canonical conversation.
## Where the state lives
| Setting | Coherent hosting (default Engine, `EngineBuilder::hosting`) | Custom backends and local profiles |
| -------------------------- | ----------------------------------------------------------- | -------------------------------------------- |
| Capability overlay | Committed through the hosting authority; durable | Persisted in the session record |
| Model and reasoning effort | Committed through the hosting authority; durable | Held by the Engine; re-apply after a restart |
| MCP servers | Held by the Engine; apply from the next activation | Held by the Engine; re-apply after a restart |
| Registered capability code | Held by the Engine; register again after a restart | Same |
MCP definitions can carry credentials, so they are never committed to session state. On coherent hosting, commands run on the query runtime that inspection uses; a change a command makes to the capability overlay is committed, and other session-record writes are not retained.
## Related
* [Host services](https://everruns.com/docs/framework/host-services/) for capabilities and providers every session of an Agent starts with.
* [Session hosting](https://everruns.com/docs/framework/hosting/) for durable session authority under Engine.
---
# Sessions
> Keep conversation history across turns, read and resume Framework sessions, and choose how session state persists.
Source:
An `Agent` is immutable reusable behavior. An `Engine` owns session identity, history, and runtime state. A `Session` is an engine-bound live conversation.
```rust
use everruns::{Agent, Engine, OpenAI};
let agent = Agent::builder()
.instructions("Remember the conversation.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.build()?;
let engine = Engine::new();
let session = engine.create(agent);
let first = session.send_and_wait("My project is Atlas.").await?;
let second = session.send_and_wait("Continue with that project.").await?;
assert!(first.success);
assert!(second.success);
```
`Session` is always a live conversation. `send` accepts a message without waiting for a response. If a turn is active, the message steers that turn; if the previous turn has already finished, it starts a follow-up turn. Admission returns before the executor assigns a turn. The receipt reports `Pending`; query its authoritative assignment to learn whether it started or steered a turn:
```rust
let session = engine.create(agent);
let initial = session.send("Plan my trip.").await?;
let latest = session.send("Prefer trains.").await?;
let result = latest.wait().await?;
if let Some(assignment) = latest.assignment().await? {
println!("turn: {}, steered: {}", assignment.turn_id, assignment.steered);
}
```
Waiting on the latest receipt works before or after assignment. `send_and_wait` (also available as the shorter `run` alias) is request/response convenience over the same live session, not a separate mode.
The first asynchronous operation materializes the in-process host. Later turns reuse it and send accumulated history through the same context-assembly path. Two sessions opened on one engine have different opaque IDs and isolated histories. The engine retains each immutable Agent snapshot, so a session keeps working after the original Agent handle is dropped. Volatile resume is engine-scoped: another `Engine` rejects the id rather than guessing its configuration. A local profile can be attached to another Engine with the trusted Agent snapshot; live Engines for the same profile share its backend bundle.
Conversation isolation does not imply filesystem isolation. The concise `engine.create(agent)` path permanently selects the Agent’s default head before its first inspection or turn; call `session.start().await` to make that selection observable earlier. To fix a session to an isolated project view, bind an [`Environment`](https://everruns.com/docs/framework/workspaces-and-environments/) before execution. A session can never switch heads after it starts.
`Session::inspect` returns the context assembled for the next model call. Use it for application assertions and debugging rather than reaching into runtime records or backend stores.
Keep `Session::session_id()` when the application may need to reopen a conversation. [History and resume](#history-and-resume) below covers typed resume, bounded transcript pages, and cursor snapshots, and [Persistence](#persistence) covers engine-lifetime memory and the crash-durable local profile. See [Workspaces and Environments](https://everruns.com/docs/framework/workspaces-and-environments/) for exact-head resume, isolation, sharing, and lifecycle, and [Events and cancellation](https://everruns.com/docs/framework/events-and-cancellation/) to observe a turn in flight.
### Send files
`Session::attach` keeps a file in the session’s own files at `/attachments//`, read-only, and returns the part that sends it with a message. The model gets images and PDFs as content and other files by name and path, and can open any of them again with `read_file`, including after compaction. A zip is unpacked into a read-only folder next to it.
```rust,no_run
use everruns::InputMessage;
let report = session
.attach("report.pdf", Some("application/pdf"), std::fs::read("report.pdf")?)
.await?;
let mut message = InputMessage::user("Summarize the attached report.");
message.content.push(report);
session.run(message).await?;
```
The bytes decide the file’s type, whatever the caller declared. Embedded channels keep the files people send (a Slack file, for example) the same way.
## History and resume
Every Framework session has a typed `SessionId`. Keep that value when an application may need to reopen the conversation:
```rust
use everruns::{Agent, Engine, OpenAI, SessionId};
let agent = Agent::builder()
.instructions("Remember the conversation.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.build()?;
let engine = Engine::new();
let session = engine.create(agent);
let session_id: SessionId = session.session_id();
session.send_and_wait("My project is Atlas.").await?;
drop(session);
let resumed = engine.resume(session_id).await?;
resumed.send_and_wait("Continue with that project.").await?;
```
`resume` verifies the ID against the engine’s session catalog. It does not infer identity from a non-empty transcript: a valid session can have no messages, and stray events do not create a resumable session. An unknown ID returns a typed not-found error. The resumed session uses the immutable Agent snapshot attached to that engine; it never reconstructs behavior from events.
### Read bounded history
`Session::history` creates an owned query. Calling `page` returns at most 100 messages by default in canonical event-sequence order, oldest first:
```rust
let page = session.history().page().await?;
for message in &page.messages {
println!("{:?}: {}", message.role, message.text());
}
```
Set a smaller or larger page size with `limit`. The maximum is 256 messages; an excessive value returns `HistoryError::InvalidLimit` with the allowed maximum. A page never claims to contain the entire transcript. Continue from its opaque cursor:
```rust
let first = session.history().limit(25)?.page().await?;
if let Some(cursor) = first.next_cursor {
let second = session.history().limit(25)?.after(cursor)?.page().await?;
// `second` continues the same stable snapshot.
}
```
`HistoryCursor` is opaque, session-bound, and safe to store as a string with `Display` and restore with `FromStr`. A cursor fixes the snapshot’s high-water mark: events appended after the first page do not appear midway through that page walk. Start a new query to see them. Passing a malformed, cross-session, expired, or incompatible cursor returns a distinct typed history error. History projection also applies a bounded raw-event replay safety limit; an unusually lifecycle-heavy snapshot that exceeds it returns `HistoryError::HistoryTooLarge` instead of performing an unbounded scan.
For callers that intentionally walk the whole snapshot, `pages` is a lazy convenience that still reads one bounded page at a time:
```rust
let mut pages = session.history().limit(50)?.pages();
while let Some(page) = pages.next_page().await? {
for message in page.messages {
println!("{}", message.text());
}
}
```
After the final page, `next_page` remains fused and returns `None`. It does not re-read the backend or produce repeated empty terminal pages.
## Persistence
Framework history is a read-only projection of canonical events. Normal execution has one write path, the engine’s event log, so a resumed session and a running session cannot disagree about the conversation.
| Deployment | Conversation state | Recovery boundary | Use when |
| --------------------------- | ----------------------------------------------- | ------------------------------------------ | -------------------------------------------- |
| `Engine::new()` | Volatile coherent memory authority | One process | Embedding, tests and short-lived tools |
| Engine with `SqliteHosting` | Transactional local session authority | The configured disk plus native supervisor | Durable single-node Framework services |
| Engine with `ObjectHosting` | Per-session object authority | Platform storage and alarms | Native or Wasm execution on Cloudflare/celld |
| Legacy `LocalConfig` | Local canonical events and catalog | One trusted application process | Existing local-store integrations |
| Everruns Platform | PostgreSQL canonical events and actor authority | Distributed server and workers | Horizontal workers and remote clients |
### Default: engine-lifetime memory
By default, `Engine` owns a volatile session catalog and event log. It retains the immutable Agent snapshot associated with each session and requires no database, server, network connection, credential, or filesystem access.
Dropping a `Session` does not immediately discard its committed history. Reopen it by passing its typed `SessionId` to the engine that created it. A separate engine cannot infer the session’s Agent configuration, and process exit loses volatile history.
This default fits tests, command-line tools, short-lived workers, and applications that deliberately own a higher-level record elsewhere.
### Coherent native and Durable Object hosting
For durable admission and independently recoverable execution, choose a coherent session authority on Engine. Native `hosting::SqliteHosting` owns one transactional database; the [session hosting SPI](https://everruns.com/docs/framework/hosting/) supports external authorities for native or Wasm Engine placement. A source-only Durable Object reference demonstrates the extension. Register immutable `AgentBuild` identities and retained Environment factories before creating sessions. Start the native supervisor or configure platform alarms so recovery does not depend on a client reconnecting. See [Durable Object hosting](https://everruns.com/docs/framework/durable-objects/).
`create_registered` and `send_with_id` accept caller-retained identities for retry after uncertain responses. Admissions, approvals, client results and cancellation are authority commands; the journal and wakeup obligation commit together. Resources such as workspace files and compute still require explicit host services.
### Local: crash-durable events
For local applications, the feature-gated `LocalConfig` adds a crash-durable event log under the configured application data directory. It also supplies a trusted real-disk workspace plus SQLite-backed task and schedule state:
```rust
use everruns::{Agent, Engine, LocalConfig, OpenAI};
let local = LocalConfig::new(".everruns-data").workspace("./workspace");
let agent = Agent::builder()
.instructions("Work inside the configured workspace.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.local(local)
.build()?;
let engine = Engine::new();
let session = engine.create(agent);
```
Enable it with `cargo add everruns --features local`. Select both directories from trusted application configuration. After a restart, rebuild the Agent from trusted application configuration, attach it to a new engine, and resume the committed session by ID. For a session created with an explicit Harness, also deserialize its portable definition and call `Engine::attach_with_harness`; `Engine::attach` remains the no-Harness path.
The local profile is designed for one embedded process at a time. Coordinate process ownership before handing the directory to another application process. Within one process, every live Engine configured with the same local data directory shares one backend bundle, so concurrent Engine values cannot build divergent JSONL indexes or SQLite handles for that profile.
The event-log file format and host backends are not Framework APIs. Do not edit the log or build application writes around its representation. Use `Session::history` for bounded reads and `Engine::resume` to continue a session; see [History and resume](#history-and-resume) for the complete lifecycle.
Applications remain responsible for filesystem permissions, backups, retention, and selecting a data directory that is not controlled by model or request input. New local state files are created owner-only on Unix, but applications must still protect copied files and backups. Message content is application data and may be sensitive even though provider credentials are not written there by Framework configuration.
### Resume after a process restart
Enable `local` and configure a trusted application data directory when sessions must survive a new Agent or process:
```rust
use everruns::{Agent, Engine, LocalConfig, OpenAI};
let build_agent = || -> Result> {
Ok(Agent::builder()
.instructions("Remember the conversation.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.local(LocalConfig::new(".everruns-data"))
.build()?)
};
let first_engine = Engine::new();
let session = first_engine.create(build_agent()?);
session.start().await?;
let session_id = session.session_id();
// In a later process, rebuild trusted behavior before resuming persisted state.
let restarted_engine = Engine::new();
restarted_engine.attach(session_id, build_agent()?).await?;
let resumed = restarted_engine.resume(session_id).await?;
```
The local profile stores a durable session catalog and crash-durable canonical event log alongside its workspace, task, and schedule state. After restarting, build another Agent with the same trusted data directory, call `engine.attach(session_id, agent)`, then `engine.resume(session_id)`. Attachment rejects IDs absent from that Agent’s configured local catalog. A new session is made durable by its first async operation (`run`, `inspect`, or a history page read); merely allocating a synchronous handle does not commit it.
A running turn, or one waiting on a person (a tool approval or an `ask_user` question), lives inside the process, so a process exit leaves it in the log without an end. After resuming, `resumed.interrupted_turn()` reports such a turn and the tool calls it had not finished, and `resumed.resume_interrupted_turn()` continues it in the same turn. A call runs again only when that is safe: it waited on a person, which asks the approver or `ask_user` responder again under the same tool call id, its tool is marked `FunctionTool::idempotent()`, or it never started (the log has no `tool.started` for it). Any other unfinished call is recorded as interrupted (listed in `not_rerun`), so the model learns its outcome is unknown and nothing runs twice. A turn cut off between steps reasons again.
For a session created with an explicit Harness, persist its serialized portable definition, deserialize it after restart, and call `engine.attach_with_harness(session_id, agent, harness)` instead. Harness deserialization validates the definition and generates a new process-local runtime identity.
The local profile is for one embedded process at a time. Do not write or edit its files as application data: messages are a read-only projection of committed events, and the storage formats are not Framework APIs.
History does not contain model credentials or application secrets unless an application deliberately includes them in message content or event metadata. Choose and protect the local data directory accordingly.
### Canonical host persistence
Durable conversation truth belongs to canonical events; history and context are projections of that record. Advanced hosts use `EventLog` and `EventHistory` from `everruns-core` (`host` feature), including `JsonlEventLog` when a local append-only event log is appropriate. Framework applications continue sessions with `Engine::resume` and traverse bounded event-derived pages from `Session::history`.
A host that needs its own storage implements the public `EventLog`/`EventReader` SPI and supplies it through `HostBackends::with_event_log`; see [Implementing a custom event log](https://everruns.com/docs/framework/events-and-cancellation/#implementing-a-custom-event-log).
`JsonlEventLog` bounds startup recovery before indexing: the default accepts at most 128 MiB and 1,000,000 canonical events. Oversize logs fail to open with a typed recovery-limit error instead of allocating or scanning without bound.
Do not design new application persistence around a legacy storage representation.
### Moving legacy sessions to `SqliteHosting`
`Engine::cut_over_legacy_session` (experimental) moves an existing `LocalConfig` or custom-backend session into `SqliteHosting` with the same `SessionId`. The import is lossless and atomic: the full event history keeps its event ids and sequences, and admissions, parked client calls and approvals, compaction checkpoints, session values, finished task records and the workspace binding come along. Retrying after a crash or a lost reply returns the same result. A cutover to a different build fails. `cut_over_legacy_session_with_metadata` also records binding metadata the legacy lane never stored, such as the `parent_session_id` of a child session.
```rust
let profile = SqliteHosting::open("authority.db")?;
let engine = Engine::builder().hosting(profile.clone()).build();
engine.register_agent_with_environment(
build.clone(),
agent.clone(),
None,
"imported-workspace",
ImportedWorkspaceFactory::new(agent, profile),
)?;
let report = engine.cut_over_legacy_session(session_id, &legacy_agent, &build).await?;
let session = engine.resume(session_id).await?;
```
The legacy lane is frozen first. After that, `Engine::attach` with the legacy Agent returns `ResumeError::CutOver` and no legacy turn starts, in any process that shares the store. The JSONL file is never written again; when a tool still needs JSONL, rebuild it from the authority with `SqliteHosting::rebuild_jsonl_export(session_id, path)`, which writes the same line format atomically and can be repeated at any time. If a cutover fails before the authority commits, the session stays frozen until you retry or call `Engine::abandon_legacy_cutover`.
The session must be quiescent. A cutover refuses, without writing anything to the authority, while a legacy turn is running or while the session has a task still running under its legacy owner. Schedules move with the session and fire from `SqliteHosting::supervise`; the legacy lane no longer delivers them. Tasks whose owner is gone move with the session; the authority re-attaches or settles them on the next activation without re-running uncertain work. Session secrets move only when the authority has secret keys (`SqliteHosting::with_secret_keys`, the same `key_id:base64_key` format as `SECRETS_ENCRYPTION_KEY`); they are stored encrypted and the cutover refuses without keys. It also refuses when the target build cannot reopen the session’s workspace head. Use `ImportedWorkspaceFactory` so the authority reopens that head after restarts. The Agent’s in-memory default workspace is not durable on either lane, so its files are not migrated; the report sets `volatile_workspace`.
### Rewinding and branching history
`SqliteHosting` keeps every event forever and tracks which events form the conversation (experimental). A branch is a list of event-sequence ranges made of whole turns. The model context, `Session::history` and `Session::events_after` follow the active branch. `SqliteHosting::export_jsonl` still returns every event with its original id and sequence. New input extends the active branch, so sequences on a branch can have gaps.
```rust
let lineage = engine.history_lineage(session_id).await?;
// Rewind to just after sequence 12; the old branch stays retained.
let ranges = lineage.rewind_ranges(12).expect("12 is on the active branch");
engine
.select_history_branch(session_id, command_id, lineage.revision, "undo-1", Some(ranges))
.await?;
// Redo: reselect the retained branch by name.
engine
.select_history_branch(session_id, redo_id, lineage.revision + 1, "main", None)
.await?;
```
A selection is accepted only when the session is quiescent: no execution, no open turn, no pending tool call or approval, and no unconsumed input. It also checks the revision you read. Anything else is refused, never queued or discarded. Retrying the same command id returns the first reply. Compaction checkpoints follow the branch too. A checkpoint can be used only on a branch that shares the history it summarized, so a summary of an abandoned branch never shows up on another branch. Checkpoints of a shared earlier part stay usable. `MemoryHosting` and other authorities without branch support refuse these calls.
To import saved history that already has branches, convert your own timeline format into a `ConversationLineage` and pass it with `Engine::cut_over_legacy_session_with_options(.., CutoverOptions::new().lineage(lineage))`. The whole journal is imported. The lineage names the active branch, the retained branches and each branch’s compaction checkpoints. A retry must pass the same lineage.
### One process runs a session at a time
Each session runs as an actor: its event log is its state, and a turn holds the session’s lease while it runs. The lease is taken when a turn starts, renewed while it runs (including while it waits on an approval or a question), and given up when the turn ends. With the local profile the leases live in the profile’s SQLite database, so if two processes share a data directory, a turn started in one while the other runs the same session fails with an error saying the session runs in another process. Between turns no process holds a session, so either one can pick it up. A lease lasts 10 seconds unless renewed, so after a crash the restarted process waits at most that long before it resumes a turn the old process was running.
### Platform: distributed durable execution
The Everruns Platform uses the same `everruns-core` (`engine` feature) turn state machine and turn loop as the Framework. The server stores each input and marks the session runnable; a worker leases the session over gRPC and runs the whole turn, and PostgreSQL stores the canonical events. If a worker disappears mid-turn, its lease expires and a later worker resumes the turn from the session’s event log.
The Platform is a deployment boundary, not another configuration mode on `everruns::Engine`. Remote applications use the Platform API or an SDK; product hosts compose the lower-level durable crates. See [Framework Architecture](https://everruns.com/docs/framework/architecture/) for the layer map.
---
# Testing and Simulation
> Test Framework applications deterministically without network access or provider credentials.
Source:
`Model::simulated` is the default testing tool. It uses the normal provider resolution and execution path while returning a fixed response locally. Its implementation comes from the publishable, production-safe `everruns-llmsim` crate; the Framework does not depend on test-support code.
```rust
use everruns::{Agent, Engine, Model};
let agent = Agent::builder()
.instructions("Return the configured result.")
.model(Model::simulated("approved"))
.build()?;
let session = Engine::new().create(agent);
let turn = session.send_and_wait("Review this.").await?;
assert!(turn.success);
assert_eq!(turn.response, "approved");
```
Useful test layers are:
1. Build-time validation tests for agent, tool, model, MCP, and compaction configuration.
2. Offline session tests with `Model::simulated`.
3. Context assertions through `Session::inspect`.
4. Event/cancellation tests through the public session API.
5. A small opt-in live-provider suite for protocol integration.
Keep normal tests credential-free and deterministic. Do not make a live model’s wording or tool choice a unit-test oracle. Temporary directories should own workspace and local-state tests so they do not read or modify developer data.
The runnable programs in [Framework examples](https://everruns.com/docs/framework/examples/) are also compiled in CI using only the public `everruns` facade.
## Scripted and low-level simulation
Use `Model::simulated_with_config` when an application test needs multiple assistant turns, deterministic tool calls, an injected provider error, or request capture:
```rust
use everruns::{Agent, LlmSimConfig, Model};
use everruns_llmsim::{SimToolCall, SimTurn};
let simulation = LlmSimConfig::scripted(vec![
SimTurn::ToolCalls(vec![SimToolCall {
name: "lookup".into(),
arguments: serde_json::json!({"id": 7}),
id: Some("call_lookup".into()),
}]),
SimTurn::Assistant("approved".into()),
]);
let agent = Agent::builder()
.instructions("Use lookup, then report the result.")
.model(Model::simulated_with_config(simulation))
.build()?;
```
Advanced hosts depend on `everruns-llmsim` with its `host` feature for `LlmSimRuntimeExt`. The `.llm_sim(...)` method registers the provider without changing model selection; `.llm_sim_as_default(...)` explicitly selects it when no default was already configured. Use `everruns-test-support` only for testing/demo helpers such as its in-memory agentic loop, writable fixtures, test doubles, and fake capabilities.
---
# Upgrade Notes
> Breaking changes for Framework users, release by release, with the code changes each upgrade needs.
Source:
This page collects the changes a Framework application must make when it upgrades across a release that moved or renamed public Rust APIs. Releases that need no code changes are not listed. For every release, see the [changelog](https://github.com/everruns/everruns/blob/main/CHANGELOG.md).
## 0.49
### `local()` and `backends()` are deprecated
`AgentBuilder::local` and `AgentBuilder::backends` select the older facade lane, which recovers less after a crash than Engine hosting. They still work and now emit a deprecation warning. New code builds the Engine with a hosting profile instead, and existing sessions move over with [`Engine::cut_over_legacy_session`](https://everruns.com/docs/framework/sessions/#moving-legacy-sessions-to-sqlitehosting):
```diff
let agent = Agent::builder().local(LocalConfig::new(dir)) /* … */ .build()?;
let engine = Engine::new();
let agent = Agent::builder() /* … */ .build()?;
let engine = Engine::builder().hosting(SqliteHosting::open(dir.join("authority.db"))?).build();
```
AG-UI threads move with `AgUiThreads::hosted`, and coordination threads run as child sessions of the hosting authority. Sessions written by an older lane still move one by one with `Engine::cut_over_legacy_session`.
### Custom task registries honor recovery guards
`SessionTaskUpdate` adds `expected_recovery`. Code constructing updates with `..Default::default()` needs no change; complete struct literals add `expected_recovery: None`.
A custom `SessionTaskRegistry::update` must compare a supplied guard against the persisted task under the same lock or transaction as the write. On a mismatch, return `Ok(None)` before applying the update. Returning the unchanged task as a successful update would let a stale recoverer publish a completion notification. The local SQLite and server PostgreSQL registries enforce this contract.
### MCP calls take an idempotency key
`McpClient::call`, `McpClient::call_as_tool_result` and `McpTransport::call_tool` take a new last argument, the call’s idempotency key, which goes in the request’s `_meta` as `io.everruns/idempotency-key`. Pass `None` to send none. A custom `McpTransport` adds the parameter; a tool reads the current call’s key from `ToolContext::idempotency_key`.
```diff
client.call(&connection, "search", args).await?;
client.call(&connection, "search", args, None).await?;
```
## 0.48
### Durable turns option removed
The experimental `durable` feature of the `everruns` crate and `everruns::durable::Backend` are removed, with no replacement flag. Every `Engine` session now runs as an actor that holds the session’s lease while a turn runs, and durability comes from the session’s event log: with `LocalConfig` a turn a restart cut off resumes from the log. Drop `durable` from the crate’s features and the `.backend(durable::Backend::...)` call from `Engine::builder()`. See [Sessions](https://everruns.com/docs/framework/sessions/#one-process-runs-a-session-at-a-time).
```toml
# Before
everruns = { version = "0.47", features = ["durable"] }
# After
everruns = { version = "0.48", features = ["local"] }
```
### Durable crates
* `everruns-durable` no longer has a `workflows` feature: `Workflow`, `Activity`, `WorkflowExecutor`, timers and child workflows are removed. The task queue, event log, schedules, retries, dead letter queue and circuit breakers stay. Remove `features = ["workflows"]`.
* `everruns-durable-engine` is retired with no new home. Its published versions remain usable.
### Harness tree
The built-in harnesses form a tree by use case instead of one chain. Base now carries the system essentials, Conversation and Worker both build on it, and Bashkit Worker builds on Worker. See [Harnesses](https://everruns.com/docs/features/harnesses/).
* `Harness::worker()` no longer includes a shell. It is the worker kit (files, project instructions, skills, long context, budgeting, subagents, tasks) with no compute. Use `Harness::bashkit_worker()` for the previous files-and-bash behavior.
* `Harness::worker_base()` is deprecated and returns `Harness::bashkit_worker()`.
* `Harness::base()` is no longer empty: it adds compaction, error disclosure, tool-call repair, loop detection and parallel tool calls. Build a custom harness with `Harness::builder` for an empty foundation.
* Agent packages may name `bashkit-worker`; `worker-base` still loads and binds Bashkit Worker.
Hosted organizations are migrated once on upgrade. The old Worker included a shell, so everything bound to it moves to the worker that matches its compute:
* Agents with a container or managed Agent sandbox policy move to Sandbox Worker and keep their policy.
* Every other Agent, the org default, custom child harnesses, triggers and sessions move to Bashkit Worker. A Bashkit-only Agent sandbox policy is cleared because Bashkit Worker fixes it.
* Existing sessions keep the compute they started with.
* `worker-base` stays as a deprecated row with its old capabilities, so custom harnesses that name it keep working.
## 0.46
### Integration-catalog package retired
`everruns-integrations-catalog` published its final deprecated forwarding release at 0.45.0. Its source is removed from the workspace and there is no 0.46.0 release of that package. When upgrading platform dependencies to 0.46, use `everruns-capabilities::integrations_catalog` with `hosted-integration-catalog`, as described below. Hosted registration and integration features are preserved; previously published shim versions remain usable and are not yanked.
## 0.45
### Hosted integration registration moves into capabilities
Replace the `everruns-integrations-catalog` dependency with `everruns-capabilities` and enable its `hosted-integration-catalog` feature. Change imports from `everruns_integrations_catalog::` to `everruns_capabilities::integrations_catalog::`. The public registration API, integration set, registration order, and deployment-grade feature gates are preserved. The old package shipped its deprecated forwarding release at 0.45.0 before source retirement in 0.46. Its published versions remain usable.
Framework applications using `everruns` and its selected integration features need no changes. The hosted integration set remains opt-in.
## 0.42
### SQLite connections move to `everruns::sqlite`
The embedded SQLite wrapper moved out of `everruns-durable` into the `everruns` facade, so the `local` feature no longer compiles the durable engine. `everruns-durable`’s `sqlite` feature and `sqlite` module are removed. Replace `everruns-durable = { features = ["sqlite"] }` with `everruns = { features = ["local"] }` and `everruns_durable::sqlite` with `everruns::sqlite`; the functions and types are unchanged. `everruns_durable::UpdateField` still names `UpdateField`; the `everruns_durable::update_field` module is no longer public.
## 0.41
### Embedded SQLite ownership
`everruns::local::SqliteDb` retains its shared callback API, including callbacks typed as `&rusqlite::Connection`, `rusqlite::params!`, and `OptionalExtension`. Connections now open through `everruns-durable`’s optional `sqlite` feature. Framework applications keep the same local stores and schemas; no data migration is needed. Hosts opening independent handles should use `everruns_durable::sqlite::open` or `open_in_memory` (`everruns::sqlite` from 0.42). To add application tables to the Framework database, retain `SqliteDb` and use its `with_conn` callbacks.
### Retired forwarding crates
The five forwarding packages `everruns-engine`, `everruns-host`, `everruns-builtins`, `everruns-mcp`, and `everruns-ag-ui` are removed from source after their single deprecated 0.40 release. Keep the canonical core imports and feature selections described below; published deprecated versions remain usable.
## 0.40
### Consolidated execution modules
The planned release moves the execution, host, built-in, MCP, AG-UI, and A2A modules into `everruns-core`. Its default features remain portable; opt into the modules your host uses. The five former packages will ship one deprecated forwarding release before removal in the next platform release. Existing behavior and protocol formats will be preserved. This step is gated on the complete 0.39 publication, including its deprecated `everruns-platform` shim, before that shim is removed.
| Deprecated crate prefix | Canonical import | Core feature |
| ----------------------- | --------------------------- | ------------ |
| `everruns_engine::` | `everruns_core::engine::` | `engine` |
| `everruns_host::` | `everruns_core::host::` | `host` |
| `everruns_builtins::` | `everruns_core::builtins::` | `builtins` |
| `everruns_mcp::` | `everruns_core::mcp::` | `mcp` |
| `everruns_ag_ui::` | `everruns_core::ag_ui::` | `ag-ui` |
After upgrading, select concrete integrations on `everruns`, and use `everruns::batteries::runtime_capability_registry()` and `everruns::batteries::runtime_egress_service()` for the matching composition. The core host module accepts explicitly injected services. A custom shell hook host supplies a `BashHookDispatcherFactory` through `HostBackends`; the facade injects the Bashkit factory when its `bashkit` feature is enabled. Utility-model bootstrap configuration will live in `everruns::utility_llm` behind `utility-llm`. A2A protocol and outbound client APIs will be available from `everruns_core::a2a` with the `a2a` feature.
## 0.39
### Hosted capabilities and server records
Replace the `everruns-platform` dependency with `everruns-capabilities` and keep the hosted capability features your application uses. Change hosted registry and runtime-store imports from `everruns_platform::` to `everruns_capabilities::`. The platform name receives one deprecated forwarding release before removal.
Library hosts use `everruns_core::AgentDefinition`, `HarnessDefinition`, and `ExecutionSession` for execution configuration and session views. Persisted agent, harness, session, model, provider, skill, and other management records are owned by the server and are no longer exposed by a library crate. Store adapters resolve authorization, versions, inheritance, and lifecycle before returning portable values. Extensions continue to implement the service interfaces in `everruns-contracts`.
HTTP API paths and serialized responses are unchanged by this ownership move; HTTP clients need no migration.
## 0.38
### Runtime store migration
Custom hosts implementing `everruns_platform::PlatformStore` now return `everruns_core::AgentDefinition`, an inheritance-resolved `everruns_core::HarnessDefinition`, `everruns_core::ExecutionSession`, and `everruns_contracts::typed_id::SessionParticipantId`. Resolve authorization, record projection, harness inheritance, and lifecycle validation in the host adapter. The raw `get_harness_chain` method is removed.
`PlatformStoreSubagentDelegate` remains available to attach this runtime store to a `ToolContext`; the stock `SubagentCapability` needs no host-specific override. `LocalPlatformStore` directly forwards its runner’s portable session views.
The deprecated provider, capability, and model-profile shims are removed after their final release. Depend on `everruns-contracts`; replace `everruns_provider::` with `everruns_contracts::`, `everruns_capability::` with `everruns_contracts::capability::`, and `everruns_model_profiles::` with `everruns_contracts::model_profile_data::`.
### Harness levels
`Harness::base()`, `Harness::conversation()`, `Harness::worker()` and `Harness::bashkit_worker()` share the same capability definitions as the hosted harnesses. `Harness::generic()` is deprecated; it keeps its previous capabilities instead of silently switching to Worker.
Choose the smallest foundation and add agent-specific capabilities. Enable the required host integrations separately. Existing `engine.create(agent)` behavior is unchanged when no harness is bound.
On the hosted platform, upgrades pin existing agents that inherited a Generic org default to Generic before selecting Conversation as the new default. Explicit bindings, sessions and custom defaults remain unchanged. The upgrade runs once; a later explicit choice to use Generic as the org default is respected.
If a custom harness already uses a new built-in name, it keeps its ID, display name, configuration and bindings. Its addressable name becomes `-custom`, with a numeric suffix if needed. Update API or CLI calls that select that custom harness by name; calls using its ID keep working.
## 0.18
0.18 narrows `everruns-core` to the neutral execution kernel. Types that were persisted control-plane records, hosted service contracts, product composition or concrete integrations moved to the crate that owns them. The behaviour, the wire formats and the stored schema are unchanged, only the import paths.
### Retain an Engine for sessions
The Framework exposes a concrete application execution owner. The 0.18 API removes `agent.session()` and `agent.resume(id)`; applications retain the engine that owns session identity and resume authority:
```diff
let session = agent.session();
use everruns::Engine;
let engine = Engine::new();
let session = engine.create(agent);
let id = session.session_id();
let resumed = agent.resume(id).await?;
let resumed = engine.resume(id).await?;
```
`Engine` volatile resume is deliberately process-local and engine-scoped. The old `InMemoryEngine` name remains a type alias. Engine retains the immutable Agent snapshot and exact Environment/WorkspaceHead; it does not serialize a Scale-compatible Agent definition.
For a locally persisted session after process restart, rebuild the Agent from trusted application configuration, call `engine.attach(id, agent).await?`, and then `engine.resume(id).await?`. Attachment verifies the persisted session catalog before accepting the behavior snapshot; credentials and closures are never serialized.
This affects you if your Rust code imports from `everruns_core` directly. If you use the `everruns` facade, most of this is invisible: the facade re-exports what applications need, and where a moved type is part of that surface it is re-exported from its new home under the same name.
### The quickest path
Most migrations are a find-and-replace of a crate prefix. Compile, read the unresolved-import errors, and look each symbol up in the tables below.
```bash
cargo build 2>&1 | grep -E "unresolved import|no .* in"
```
Add whichever crates the table points you at:
```toml
everruns-platform = "0.18" # persisted records, hosted service contracts
everruns-host = "0.18" # execution composition and host wiring
everruns-provider = "0.18" # provider SPI, typed IDs, sqlx impls
everruns-capability = "0.18" # capability identity/configuration contract
everruns-mcp = "0.18" # MCP adapter and the OAuth protocol client
everruns-llmsim = "0.18" # deterministic production-safe simulator
```
The earlier `everruns-session-services` preview package was consolidated into `everruns-host`; use `everruns_host::session_services` for its namespaced API.
### Composition
The single biggest change for embedders. `PlatformDefinition` no longer exists.
| 0.17 | 0.18 |
| ------------------------------------------------------ | --------------------------------------------------- |
| `everruns_core::PlatformDefinition` | `everruns_host::HostComposition` |
| `everruns_core::PlatformDefinitionBuilder` | `everruns_host::HostCompositionBuilder` |
| `everruns_server::oss_platform_definition()` | `everruns_server::oss_host_composition()` |
| `everruns_server::oss_platform_definition_for_grade()` | `everruns_server::oss_host_composition_for_grade()` |
| `everruns_worker::default_platform_definition()` | `everruns_worker::default_host_composition()` |
| `ServerAppBuilder::platform_definition(..)` | `ServerAppBuilder::host_composition(..)` |
| `WorkerAppBuilder::platform_definition(..)` | `WorkerAppBuilder::host_composition(..)` |
```diff
use everruns_core::PlatformDefinition;
use everruns_host::HostComposition;
let platform = PlatformDefinition::builder()
let composition = HostComposition::builder()
.capability_registry(capabilities)
.driver_registry(drivers)
.build();
ServerAppBuilder::new().platform_definition(platform)
ServerAppBuilder::new().host_composition(composition)
```
The type is otherwise identical, same fields, same builder methods. It moved to the layer that executes a turn, because selecting a deployment’s capabilities and drivers is composition rather than kernel configuration.
#### Input/Reason/Act execution kernel
Concrete phase execution moved out of `everruns-core`. Import phase algorithms and their I/O values from `everruns-engine`; keep neutral effect contracts in core.
| 0.17 | 0.18 |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
| `everruns_core::atoms::{InputAtom, InputAtomInput, InputAtomResult}` | `everruns_engine::{InputAtom, InputAtomInput, InputAtomResult}` |
| `everruns_core::atoms::{ReasonAtom, ReasonInput, ReasonResult}` | `everruns_engine::{ReasonAtom, ReasonInput, ReasonResult}` |
| `everruns_core::atoms::{ActAtom, ActInput, ActResult, ToolCallResult}` | `everruns_engine::{ActAtom, ActInput, ActResult, ToolCallResult}` |
| `everruns_core::atoms::AtomContext` | `everruns_core::ExecutionContext` (also re-exported by `everruns-engine`) |
| `everruns_core::atoms::{PreToolUseHook, PostToolExecHook, PreToolUseDecision}` | `everruns_core::tool_hooks::*` |
The generic `Atom` trait was removed; it had no production dynamic-dispatch use. Call the concrete executor’s inherent async `execute` method. There is no core compatibility module in 0.18. Serialized phase payloads retain the same fields, so durable records remain readable even though the Rust ownership path changed.
#### Turn context and command completion
Store-backed turn preparation now belongs to `everruns-host`. Core keeps the secret-free execution snapshot, pure context transformations, and narrow effects used by custom hosts.
| 0.17 | 0.18 |
| ---------------------------------------------------- | ---------------------------------------------------- |
| `everruns_core::assemble_turn_context` | `everruns_host::assemble_turn_context` |
| `everruns_core::inspect_turn_context` | `everruns_host::inspect_turn_context` |
| `everruns_core::load_execution_snapshot` | `everruns_host::load_execution_snapshot` |
| `everruns_core::load_execution_snapshot_for_session` | `everruns_host::load_execution_snapshot_for_session` |
| `everruns_core::StoreCommandHost` | `everruns_host::StoreCommandHost` |
`everruns_engine::ReasonAtom::new` no longer accepts harness, agent, session, and provider stores or a driver registry. Construct an `everruns_host::StoreTurnContextResolver` from those host services, then pass that resolver plus the narrow message, capability, and event effects to the atom. Hosts that already loaded a `ResolvedExecutionSnapshot` should call `everruns_host::assemble_turn_context_from_snapshot` and execute the atom with the resulting `AssembledTurnContext`; this avoids a second store load.
For a fully custom host, implement the neutral `everruns_core::TurnContextResolver`, or provide already-resolved `ResolvedTurnContextInput` to `everruns_core::assemble_resolved_turn_context`. That input contains a secret-free model/provider identity and an opaque ready driver; provider keys and endpoints are never serializable kernel values.
`CommandTurnContext` now exposes `session_id` directly instead of an `ExecutionSession`. Commands retain the same filtered messages, effective prompt, locale, model, streaming, and error-decision behavior without receiving a session record.
### Persisted records
These are database and API records. Execution consumes a portable projection of each; the stored row is control-plane state.
| 0.17 (`everruns_core::`) | 0.18 |
| ----------------------------------------------------------------------------------------- | -------------------------------- |
| `Agent`, `AgentVersion`, `AgentStatus`, `AgentVersionChangeKind` | `everruns_platform::` |
| `Harness`, `HarnessStatus`, `BuiltInHarnessDefinition`, `BuiltInHarnessRole` | `everruns_platform::` |
| `Session`, `SessionStatus`, `SessionSource`, `SessionActivity`, `SessionParticipant` | `everruns_platform::` |
| `Workspace`, `WorkspaceStatus` | `everruns_platform::workspace::` |
| `Eval`, `EvalCase`, `EvalRun`, `EvalCaseResult`, `EvalRunDataset`, `EvalTarget`, `Scorer` | `everruns_platform::` |
| `Observer`, `ObserverMatch`, `LlmJudgeConfig`, `TraceScore` | `everruns_platform::` |
| `FeatureFlags`, `FeatureFlagMap`, `FeatureFlagDefinition` | `everruns_platform::` |
| `Budget`, `LedgerEntry` | `everruns_platform::` |
If you were reading a stored record to run a turn, you probably want the portable projection instead, `AgentDefinition`, `HarnessDefinition` and `ExecutionSession` all stay in `everruns_core`, produced at the platform loading boundary by `Agent::execution_definition`, `Harness::execution_definition` and `Session::execution_session`.
### Hosted service contracts
| 0.17 (`everruns_core::`) | 0.18 |
| ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `session_sqldb::*`, `SessionSqlDbStore`, `DatabaseInfo`, `SqlQueryResult`, `SqlExecuteResult`, `TableSchema`, `ColumnSchema`, `SessionSqlDbError` | `everruns_platform::session_sqldb::` |
| `traits::SessionMutator` | `everruns_host::SessionMutator` (also re-exported by platform) |
| `session_sandbox::*`, config, state, instance, exec/file payloads, `SessionSandboxProvider`, `SessionSandboxProviderPlugin` | `everruns_platform::session_sandbox::` |
| `Connector`, `ConnectorRegistry`, `ConnectorPlugin` | `everruns_platform::connector::` |
| `EmailSender`, `EmailMessage`, `SystemEmailConfig`, `ResendEmailSender` | `everruns_platform::email::` |
| `OAuthClient`, `TokenSet`, `PkcePair` | `everruns_mcp::oauth::protocol::` |
Neutral per-turn contracts remain in `everruns-core`, but the catch-all `everruns_core::traits` module is gone. Import from the owning concern instead, for example `everruns_core::tool_context::ToolContext`, `everruns_core::session_files::SessionFileSystem`, or `everruns_core::provider_resolution::ProviderStore`. The deployment-owned `SessionFileSystemFactory` and its context now come from `everruns-host`.
Two of these also changed how a capability *reaches* the service. `sqldb_store` and `session_mutator` are no longer fields on `ToolContext`; they resolve from the type-keyed extension bag:
```diff
let Some(store) = &context.sqldb_store else { ... };
let Some(store) = context.extensions.get::() else { ... };
let store = &store.0;
```
If you implement a custom host, install them the way `everruns-host` does:
```rust
extensions.insert(Arc::new(SessionSqlDbStoreExt(store)));
extensions.insert(Arc::new(SessionMutatorExt(mutator)));
```
### Capabilities and implementations
| what | 0.18 home |
| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Knowledge Bases and Indexes, Memories, delegation, subagents, background and scheduled work, user hooks, citations, model scouting, platform management | `everruns_platform::capabilities::` |
| Session info and session storage | `everruns_host::session_services::capabilities::` (also re-exported by platform) |
| Session SQL database and session sandbox | `everruns_platform::capabilities::` |
| `spawn_background` and its runtime, event sink, admission permits, reattach | `everruns_platform::background_run::` |
| Portable built-ins, human intent, infinity context, skills, UI prompts, compaction, tool search | `everruns_builtins::` |
| `everruns_openui::{PromptOptions, default_library, generate_prompt}` | `everruns_builtins::openui::{PromptOptions, default_library, generate_prompt}` |
| `everruns_a2ui::{PromptOptions, default_catalog, generate_prompt}` | `everruns_builtins::a2ui::{PromptOptions, default_catalog, generate_prompt}` |
| OpenRouter workspace, model scout, and provider-executed server tools | `everruns_integrations_openrouter::` |
| Filesystem, shell, web fetch, Lua | `everruns_integrations_*` |
| MCP adapter | `everruns_mcp::` |
| In-process HTTP egress transport | `everruns_host::DirectEgressService` |
| Telemetry init, exporter event listeners, `CompositeEventListener` | `everruns_host::observability::` (feature `observability`) |
| `llmsim` driver, configs, scripted turns, registry helpers, host-builder extension | `everruns_llmsim::` |
| in-memory agentic loop, writable test doubles, fixture capabilities | `everruns_test_support::` |
| `everruns_core::in_memory::{InMemoryAgentStore, InMemoryHarnessStore, InMemorySessionStore, InMemoryProviderStore}` | `everruns_host::{InMemoryAgentStore, InMemoryHarnessStore, InMemorySessionStore, InMemoryProviderStore}` |
| `everruns_core::in_memory::{InMemoryMessageRetriever, InMemoryEventEmitter}` | `everruns_test_support::{InMemoryMessageRetriever, InMemoryEventEmitter}` for isolated deterministic tests |
Product presets compose these explicitly. Core registries are now empty by default: use `everruns_host::runtime_capability_registry()` for the Framework preset or `everruns_platform::capabilities::hosted_capability_registry()` for the hosted product catalog.
Hosted conversation history has no writable message-store replacement. Append canonical events through `everruns_host::EventLog` / `HostEventEmitter` and read messages through `EventHistory`. This avoids message/event dual writes and keeps resume and replay behavior identical across in-memory and durable hosts.
`everruns-test-support` continues to re-export its 0.17 simulator paths during the 0.18 migration, so existing test suites can upgrade without an immediate import rewrite. Treat that as a migration bridge: production code, new tests, and low-level hosts should depend on `everruns-llmsim` directly. The application-facing `everruns::Model::simulated` and `Model::simulated_with_config` APIs are unchanged.
### Features
| 0.17 | 0.18 |
| -------------------------------------- | ------------------------------------------------------------------------- |
| `everruns-core/sqlx` | removed, use `everruns-provider` with `features = ["sqlx"]` |
| `everruns-core/embedded-platform-docs` | removed, it gated nothing; use `everruns-platform/embedded-platform-docs` |
| `everruns-platform/sqlx` | removed, it forwarded to core’s and nothing enabled it |
| `everruns-core/llm-tests` | removed, use the `everruns-llm-tests` package for live provider tests |
`everruns-core` now has an empty default feature set. OpenAPI derives remain available only with `features = ["openapi"]`; structural outlines remain available only with `features = ["tree-sitter-outlines"]`. Neither subtree is present in a default core build.
Concrete provider protocol and utility-model implementations also moved to their effectful owners:
| 0.17 (`everruns_core::`) | 0.18 |
| ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| `OpenAIProtocolChatDriver`, `openai_protocol` | `everruns_contracts::` |
| `OpenResponsesProtocolChatDriver`, `openresponses_protocol` | `everruns_contracts::` |
| `driver_helpers`, `stream_reconnect` | `everruns_contracts::` |
| `OpenAiUtilityLlmService` (now `ProviderUtilityLlmService`), `SystemUtilityLlmConfig`, `UTILITY_OPENAI_API_KEY_ENV` | `everruns_host::` with `features = ["utility-llm"]` |
Core no longer initializes Rustls. Provider HTTP clients install the workspace crypto provider when they are first constructed, while server, worker, and CLI startup owners install it eagerly. Custom binaries that combine TLS stacks can depend on `everruns-provider` with `features = ["tls-aws-lc-rs"]` and call `everruns_contracts::install_default_crypto_provider()` once during startup; the call is idempotent and safe under concurrent initialization.
### Provider and typed-ID imports
Provider-owned modules are no longer compatibility-exported by `everruns-core`. Low-level consumers must add `everruns-provider` directly. This keeps credentials and concrete driver assembly out of the neutral kernel and makes the dependency owner visible in `Cargo.toml`.
There are two common compiler-error shapes:
1. **The module moved to another crate.** Add that crate and change the prefix.
2. **The module stayed public, but its root convenience re-export was removed.** Keep the dependency and qualify the symbol through its module.
The second case produces the misleading-looking `no X in the root` error. It does not necessarily mean the type moved. These replacements are deliberately literal so they can be applied with ordinary search-and-replace:
| before | after |
| ---------------------------------- | ---------------------------------------------------- |
| `everruns_core::ProviderStore` | `everruns_core::provider_resolution::ProviderStore` |
| `everruns_core::SessionStore` | `everruns_core::execution_loading::SessionStore` |
| `everruns_core::MessageRetriever` | `everruns_core::message_retriever::MessageRetriever` |
| `everruns_core::SessionFileSystem` | `everruns_core::session_files::SessionFileSystem` |
| 0.17 core path | 0.18 direct path |
| --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `everruns_core::driver_registry::*` | `everruns_contracts::driver_registry::*` |
| `everruns_core::model::*` | `everruns_contracts::model::*` |
| `everruns_core::model_profiles::*` | `everruns_contracts::model_profiles::*` |
| `everruns_core::model_spec::ModelSpec` | `everruns_contracts::model_spec::ModelSpec` |
| `everruns_core::provider::*` | `everruns_contracts::provider::*` |
| `everruns_core::runtime_provider::*` | `everruns_contracts::runtime_provider::*` |
| `everruns_core::typed_id::*` | `everruns_contracts::typed_id::*` |
| `everruns_core::error::*` | `everruns_contracts::error::*` |
| `everruns_core::tool_types::*` | `everruns_contracts::tool_types::*` |
| `everruns_core::capability_types::{CapabilityId, CapabilityRef, CapabilityError}` | `everruns_contracts::capability::{CapabilityId, CapabilityRef, CapabilityError}` |
| `everruns_core::AgentCapabilityConfig` | `everruns_contracts::capability::CapabilityRef` |
| core plugin capability ID/validation helpers | the same symbol in `everruns_capability` |
| `everruns_core::ExecutionPhase` or `message::ExecutionPhase` | `everruns_contracts::execution_phase::ExecutionPhase` |
| `everruns_core::ToolResultImage` or `tools::ToolResultImage` | `everruns_contracts::tool_types::ToolResultImage` |
| other root-level provider symbols | the same root symbol in `everruns_provider` |
The credential-bearing `everruns_core::ResolvedModel` is removed. Store and transport boundaries now resolve two separate values:
* `ModelSpec`, safe to serialize and pass through the kernel; and
* a host-owned runtime `Provider` (or internal `ProviderConfig`) containing endpoint and authentication state.
`ProviderStore::get_model_spec` and `get_default_model_spec` return only the first value. Hosts obtain provider configuration separately and join it only while constructing a non-serializable driver/provider execution value.
`ProviderStore::get_provider_config` no longer has a default implementation. Every custom host must state where credentials live: return its resolved `ProviderConfig`, or explicitly return `None` when the provider was registered directly in the host registry or is selected but not configured. A missing credential no longer prevents turn-context/command assembly; the constructed driver rejects the first model/list/compact operation locally, before network I/O. This keeps recovery commands reachable without turning an empty token into an outbound authorization header.
### Simulator and compaction naming
`LlmSimRuntimeExt::llm_sim` now only registers/replaces the simulator provider. It never changes the selected model. Existing compact test setups that relied on implicit selection should use the explicit name:
```diff
builder.llm_sim(config)
builder.llm_sim_as_default(config)
```
When a builder already calls `default_model(...)`, keep `.llm_sim(config)`; the selected model is preserved regardless of method order.
`everruns_builtins::CompactionConfig` is the sole application-facing policy builder. The expanded implementation value previously available as `everruns_builtins::compaction::CompactionConfig` is now `everruns_builtins::compaction::RuntimeCompactionConfig` (also re-exported at the crate root). This makes an unqualified `CompactionConfig` unambiguous.
The public core test/backend conveniences are gone as well:
| removed core value | replacement |
| ----------------------------------- | ------------------------------------------------------------------- |
| `EchoTool`, `FailingTool` | define the small test `Tool` locally, or use test-support executors |
| `InMemoryCompactionCheckpointStore` | `everruns_host::InMemoryCompactionCheckpointStore` |
### What deliberately did not move
Worth knowing so you do not go looking:
* **`SessionTask`, `TaskMessage`** and the task registry stay in `everruns_core`. They are turn-execution vocabulary, `wake_queue` decides mid-turn wakes from a task’s wake policy, and they appear in the canonical `task.created` / `task.updated` / `task.message.*` event payloads.
* **`SessionSchedule`, `SessionScheduleStore`** stay. A portable built-in (`usage_limit_auto_continue`) schedules an auto-resume after a provider usage limit, and it sits below platform in the dependency graph.
* Schedule quota and minimum-interval environment variables are no longer read by core. Local/server adapters resolve deployment policy and call the parameterized core validation helpers.
* **`SessionResourceRegistry`** stays. `resource_ownership` and the portable skills capabilities consume it.
* **`SessionFileSystem`, `SessionStorageStore`** and the other neutral store contracts stay. Core owns the contract; hosts own the backend.
The rule these follow: whether something belongs in the kernel is decided by whether a portable execution path consumes it during a turn, not by whether it is persisted. All four above are persisted, and all four are essential for execution.
### Getting unstuck
If a symbol is not in these tables, import it from the crate that defines it; `everruns-core` no longer acts as a compatibility facade for provider-owned APIs. Framework applications can continue to prefer the higher-level `everruns` facade. The crate-level docs on `everruns-core` record where each remaining family lives and why.
---
# Build a Voice Agent
> Let people talk to a Framework agent and hear its answers, in your own Rust host or as a serve app, with the same agent, tools and transcript as text.
Source:
A voice agent is an ordinary agent with a **voice channel** in front of it. The channel listens to the caller, turns each finished utterance into a user message, and speaks the agent’s answer as it streams. The agent itself does not change: the same session can take typed messages and calls, and both land in one transcript.
> **Status:** experimental. `everruns::voice` carries no compatibility promise yet, and the speech provider is OpenAI.
## Two modes
Your agent, with its own model and tools, writes every answer in both modes. They differ in what listens and speaks, and in where the audio goes.
| Mode | Listens and speaks | Audio path | Use it for |
| ----------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------- |
| **Delegated** (default) | A speech-to-speech model, `gpt-realtime-2` | Browser to OpenAI over WebRTC; never through your server | Browser calls with the lowest latency |
| **Cascaded** | A speech-to-text model (`gpt-4o-transcribe`) and a text-to-speech model (`gpt-4o-mini-tts`) | Through your process as PCM | Native and mobile apps, your own transport, picking each model separately |
## How a delegated call works
1. The browser sends a WebRTC offer with its microphone to your server. Your server places the call with OpenAI and returns the SDP answer, so the API key never reaches the browser. Audio then flows directly between the browser and OpenAI.
2. When the caller stops talking, the transcript becomes a user message with `metadata.source = "voice"`. If a turn is already running, the message steers it, as a typed message would.
3. The answer is spoken sentence by sentence while it streams. A filler line (“One moment.”) covers a turn that stays silent for a while.
4. When the caller talks over the answer, speech stops at once. With `Interruption::Cancel` the running turn is cancelled too.
This is the same voice loop the Everruns server runs for its [voice channels](https://everruns.com/docs/features/voice/), so a call behaves the same in your host, in serve, and on the platform.
## In your own host
```bash
cargo add everruns --features voice,openai
```
```rust
use everruns::providers::openai::OpenAI;
use everruns::voice::{Interruption, VoiceChannel};
let voice = VoiceChannel::delegated(OpenAI::from_env()?.realtime())
.voice("marin")
.greeting("Hi, you are talking to an AI assistant. How can I help?")
.speaking_style("Warm and unhurried.")
.interruption(Interruption::Steer);
// `offer_sdp` is the browser's WebRTC offer, posted to your route.
let call = voice.accept_webrtc(&session, &offer_sdp).await?;
let answer_sdp = call.answer_sdp().to_string(); // send back to the browser
// Follow the call: what the caller said, what was spoken, interruptions.
let mut events = call.events();
// Hang up from the server, or wait for the caller to.
let summary = call.end().await?;
```
`VoiceCall` keeps the call running until you end it, the caller hangs up, or the value is dropped. Keep it somewhere your hang-up route can find it, keyed by `call.call_id()`. Use `accept_webrtc_as` to pass OpenAI a stable, privacy-preserving end-user id for abuse monitoring.
Write the agent’s instructions for speech: short sentences, no markdown, lists or links, numbers said the way a person says them. Delivery (pace, tone) belongs in `speaking_style`; business rules stay in the agent.
### Cascaded calls
A cascaded channel takes audio through your process: 16-bit little-endian PCM, mono, 24 kHz, in both directions. You connect it to the caller over any transport, such as a WebSocket or a native app’s audio stream.
```rust
use everruns::providers::openai::OpenAI;
use everruns::voice::{AudioOut, VoiceChannel};
let voice = VoiceChannel::cascaded(OpenAI::from_env()?.speech())
.speech_model("gpt-4o-mini-tts")
.voice("marin")
.greeting("Hi, you are talking to an AI assistant. How can I help?");
let (call, mut audio) = voice.accept_audio(&session).await?;
// Caller audio in: forward microphone frames as they arrive.
audio.input.send(frame).await?;
// Speech out, already paced at playback speed.
while let Some(out) = audio.output.recv().await {
match out {
AudioOut::Audio(pcm) => play(pcm), // after what came before
AudioOut::Clear => drop_unplayed(), // the caller talked over it
}
}
```
Dropping `audio.input` hangs up, as does `call.end()`. Each spoken sentence is synthesized while the previous one plays, and when the caller talks over the answer you get `AudioOut::Clear` at once. The call’s events and summary work as in delegated mode.
### Settings
| Builder | Default | What it does |
| --------------------- | ------------------- | ------------------------------------------------------------------------------- |
| `model` | `gpt-realtime-2` | Speech-to-speech model of a delegated channel. |
| `transcription_model` | `gpt-4o-transcribe` | Speech-to-text model for the caller, in both modes. |
| `speech_model` | `gpt-4o-mini-tts` | Text-to-speech model of a cascaded channel. |
| `voice` | `marin` | Provider voice. |
| `language` | none | Language hint for transcribing the caller, such as `en`. |
| `greeting` | none | Spoken when the call connects. Say that the caller is talking to an AI. |
| `turn_detection` | `ServerVad` | `ServerVad` ends a turn on a pause; `SemanticVad` waits for a finished thought. |
| `interruption` | `Steer` | Talking over the answer: `Steer` keeps the turn running, `Cancel` stops it. |
| `filler` | `One moment.` | Line spoken while the agent works. |
| `filler_after` | 1.5 s | Silence before the filler. Zero turns fillers off. |
| `speaking_style` | none | Delivery instructions for the speech model. |
`VoiceChannel::with_config` takes a whole `VoiceChannelConfig`, the same type a platform voice channel stores, so settings move between the two unchanged.
### Test without a microphone
`Realtime::simulated()` places calls on an offline simulator. Play the caller with `SimulatedCall`:
```rust
use everruns::voice::{Realtime, SimulatedCall, VoiceChannel};
let call = VoiceChannel::delegated(Realtime::simulated())
.greeting("Hello!")
.accept_webrtc(&session, "v=0")
.await?;
let caller = SimulatedCall::find(call.call_id()).unwrap();
caller.say("What is the weather?");
let spoken = caller.wait_spoken(2, std::time::Duration::from_secs(5)).await;
assert_eq!(spoken[0], "Hello!");
```
Pair it with [`Model::simulated`](https://everruns.com/docs/framework/testing-and-simulation/) and a whole call runs in a unit test. For cascaded channels, `Speech::simulated()` treats audio as text: each input frame is one utterance, and each output frame is the UTF-8 text that was spoken.
## As a serve app
With the `voice` feature, every top-level agent of a [serve](https://everruns.com/docs/framework/serve/) app takes calls:
```bash
cargo add everruns-serve --features voice
```
```toml
# serve.toml: how calls sound. Every field is optional.
[voice]
voice = "marin"
greeting = "Hi, you are talking to an AI assistant. How can I help?"
speaking_style = "Warm and unhurried."
```
Run `OPENAI_API_KEY=… cargo run -- dev` and open `http://127.0.0.1:3000/v1/channels/{agent}/voice` for a test page with a **Start call** button. Your own page uses two routes:
| Method | Route | Use |
| ------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| `POST` | `/v1/channels/{agent}/voice/calls` | Place a call: `{ "sdp": "…", "session_id"?: "…", "conversation"?: "…" }`. Answers `session_id`, `call_id` and `answer_sdp`. |
| `POST` | `/v1/channels/{agent}/voice/calls/{call_id}/end` | Hang up. Answers what the call did. |
```js
const pc = new RTCPeerConnection();
const mic = await navigator.mediaDevices.getUserMedia({ audio: true });
pc.addTrack(mic.getTracks()[0]);
const audio = new Audio();
audio.autoplay = true;
pc.ontrack = (event) => (audio.srcObject = event.streams[0]);
pc.createDataChannel("oai-events");
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const response = await fetch("/v1/channels/concierge/voice/calls", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sdp: offer.sdp }),
});
const { session_id, call_id, answer_sdp } = await response.json();
await pc.setRemoteDescription({ type: "answer", sdp: answer_sdp });
```
Each call lands in a session of the agent’s voice channel:
* With `conversation`, your key for the caller (a user id, a phone number), every call with that key continues the same session.
* With `session_id`, the call continues that session, such as a chat that started typed.
* With neither, the call starts a new session.
Follow the transcript at `/v1/sessions/{session_id}/sse` like any session.
### Cascaded calls over a WebSocket
Set `mode = "cascaded"` and the app takes calls over a WebSocket instead:
```toml
[voice]
mode = "cascaded"
speech_model = "gpt-4o-mini-tts"
voice = "marin"
```
Connect to `ws://…/v1/channels/{agent}/voice/ws` (add `?conversation=…` or `?session_id=…` to continue a conversation). Binary messages carry 16-bit PCM, mono, 24 kHz, both ways. Text messages are JSON from serve:
| `type` | When |
| ------------- | ------------------------------------------------------------- |
| `call` | First message: `session_id`, `call_id`, `sample_rate`. |
| `clear` | The caller talked over the answer; drop audio not played yet. |
| `transcript` | A finished line: `role` (`user` or `assistant`) and `text`. |
| `interrupted` | Speech was cut off; `heard` is what was spoken. |
| `ended` | The call finished, with its counts. |
Closing the socket hangs up. The test page uses this transport when the app is cascaded. An app takes one transport: the other route answers `400` and names the right one.
Without `OPENAI_API_KEY`, `dev` and `eval` place calls on the offline simulator (`model = "sim"` forces it, or `speech_model = "sim"` for a cascaded app), and `start` refuses calls. Calls are unauthenticated like every serve route, so put a public app behind your own auth.
The [voice example](https://github.com/everruns/everruns/tree/main/examples/serve/voice) is a hotel front desk you can call.
## On the platform
The Everruns server has [voice channels](https://everruns.com/docs/features/voice/) with the same settings, behind the Voice feature flag. A Framework agent [moved to the platform](https://everruns.com/docs/framework/moving-to-platform/) keeps its voice settings by storing the same `VoiceChannelConfig` on a `voice` channel. The server runs delegated channels only, so a cascaded agent switches to delegated when it moves.
## Not yet
* Native mode, where the speech model answers by itself and calls the agent’s tools.
* Phone numbers.
---
# Workspaces and Environments
> Isolate writable project heads, bind them to sessions, reopen them safely, and configure safe read and write scopes.
Source:
An `Agent` describes behavior. A `Session` owns conversation continuity. An `Environment` fixes the execution resources for that session, beginning with one `WorkspaceHead`.
A `Workspace` is logical project lineage, not a directory alias. Each `WorkspaceHead` is a stable, backend-owned mutable view of that lineage. All heads present the same portable `/workspace` namespace even when a backend implements them as Git worktrees, remote volumes, or another storage system.
## Isolated local Git heads
Enable the `local` feature to use the public Git-worktree backend:
```rust
use std::sync::Arc;
use everruns::{
Agent, Engine, LocalGitWorkspace, OpenAI, Workspace, WorkspacePolicy,
};
let backend = Arc::new(LocalGitWorkspace::new(state)?);
let workspace = Workspace::open(backend, repository.to_string_lossy()).await?;
let head = workspace
.head("feature")
.from_revision("main")
.create()
.await?;
let agent = Agent::builder()
.instructions("Work in the selected project head.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.workspace_policy(WorkspacePolicy::read_write())
.build()?;
let engine = Engine::new();
let session = engine.create(agent).workspace(head).start().await?;
assert!(session.workspace_head().is_some());
```
Head creation is isolated by default. The Framework rejects binding the same isolated head to a second session. Opt into a shared mutable head with `workspace.head("shared").shared().create()`. A shared real-disk head does not become isolated: Framework compare-and-set writes report stale-content conflicts within the host process, and backend status reports Git conflict and dirty metadata. Coordinate other writers at the application or backend layer.
Use `head.fork("name").await` to create an isolated head from the current checkpoint. `checkpoint`, `status`, `archive`, and `destroy` are explicit lifecycle operations. Dropping a head, session, agent, workspace, or backend never deletes a worktree or branch. The local backend’s explicit `destroy` removes the worktree and retains its Git branch. Archive blocks later reopen; it does not revoke a filesystem handle already owned by a running session.
## Exact resume
`start()` persists the backend’s credential-free opaque binding before the session can execute. `Engine::resume` asks the recorded backend to reopen that exact workspace and head. It returns a structured `ResumeError` when the backend is missing, the head is unavailable, the binding is corrupt, or the backend returns a different identity. It never substitutes an empty or different head.
After a process restart, the Agent attached to a durable session created from an explicit backend must register that backend with `AgentBuilder::workspace_backend`. The backend used by a live Environment is remembered automatically in that Agent snapshot. The default memory backend and `AgentBuilder::workspace(path)` shorthand backend are registered by the Framework itself.
## Compatibility window
Use `WorkspaceBackend`, `WorkspaceBackendId`, `LocalGitWorkspace`, `AgentBuilder::workspace_backend`, and `WorkspaceHead::backend` in new code. The provider-named types and methods remain as deprecated forwarding aliases.
Existing error matches keep their behavior during the deprecation window. Framework and built-in backend paths continue to emit `BuildError::DuplicateWorkspaceProvider`, `ResumeError::WorkspaceProviderUnavailable`, `SessionEnvironmentError::ProviderConflict`, `WorkspaceError::ProviderUnavailable`, and `WorkspaceError::Provider`. Their replacements are `BuildError::DuplicateWorkspaceBackend`, `ResumeError::WorkspaceBackendUnavailable`, `SessionEnvironmentError::BackendConflict`, `WorkspaceError::BackendUnavailable`, and `WorkspaceError::Backend`. Match both names while migrating. New `WorkspaceBackend` implementations should return the backend-named `WorkspaceError` variants.
Persisted `WorkspaceBinding::provider_id`, SQLite columns, and existing `workspace-provider` state directory names do not change in this migration.
## Workspace, roots, policy, and sandbox
These concepts are deliberately separate:
| Concept | Meaning |
| ----------------- | ----------------------------------------------------------------------- |
| `Workspace` | Logical project or lineage |
| `WorkspaceHead` | One reopenable mutable view selected for a session |
| `/workspace` | Stable model-visible path presented by the head filesystem |
| Additional roots | Extra named mounts in `WorkspaceRootSet`; never heads or lineage |
| `WorkspacePolicy` | Portable read/write authorization composed over the selected filesystem |
| Sandbox | A process/compute isolation boundary; not provided by path policy alone |
The Environment carries its head plus an open type-keyed extension boundary for future compute or network resources. Backends implement the async `WorkspaceBackend` trait directly; there is no backend enum or vendor switch. Every head supplies the existing `SessionFileSystem`, so file tools, seeded files, containment checks, mounts, and `WorkspacePolicy` remain one stack. When a compute resource needs the selected filesystem, attach it with `EnvironmentBuilder::workspace_extension`; its constructor receives the exact head that Framework file tools will use.
For a simple application, `engine.create(agent)` is the concise path. Its first `send` or `inspect` selects the default head automatically; optional `session.start().await` selects it earlier without running a turn. `AgentBuilder::workspace(path)` is shorthand for one explicitly shared local directory across that Agent’s sessions; it does not create isolated heads. The shorthand is still a first-class shared head and its exact canonical path binding is persisted for resume. Choose an Environment when isolation, forking, or backend-specific lifecycle matters.
## Workspace security
`WorkspacePolicy` is the portable security boundary for files visible to an in-process agent. Applications configure it through `everruns`; they do not need `RealDiskFileStore`, `HostBackends`, or a runtime-owned blocklist.
```rust
use everruns::{Agent, OpenAI, WorkspacePolicy};
fn build(root: &std::path::Path) -> Result> {
let policy = WorkspacePolicy::builder()
.allow_read("/")
.allow_write("generated")
.deny_write("generated/locked")
.allow_hidden(".github")
.build()?;
Ok(Agent::builder()
.instructions("Work only inside the configured workspace.")
.provider(OpenAI::from_env()?)
.model("gpt-6.1-sol")
.workspace(root)
.workspace_policy(policy)
.build()?)
}
```
### Defaults
`WorkspacePolicy::default()` and `WorkspacePolicy::read_only()` use the same secure baseline:
| Operation | Default |
| ---------------------------------------- | ------- |
| Read ordinary workspace files | Allowed |
| Write, create, or delete | Denied |
| Read or write hidden paths | Denied |
| Read or write common credential paths | Denied |
| Read framework-managed `.agents` content | Allowed |
| Recursively delete a directory | Denied |
`WorkspacePolicy::read_write()` is an explicit opt-in to ordinary writes. It does not expose additional hidden or sensitive paths and does not enable recursive deletion. It also keeps common dependency and build directories such as `node_modules` and `target` non-writable at every depth. For narrower access, start with `WorkspacePolicy::builder()`, which has no readable or writable scopes until you add them.
A custom builder does not inherit the default `.agents` exception. If the agent needs workspace-provided instructions or skills, add both a readable scope and a narrow `allow_hidden(".agents")` opt-in.
Protected path names are defense in depth, not content-based secret scanning. Keep credentials outside the mounted workspace, add explicit deny scopes for application-specific secret locations, and never place credentials in `.agents` content.
### Matching and precedence
Scopes are literal path prefixes, not globs, and compare ASCII letters without case sensitivity so a deny cannot be bypassed on a case-insensitive backend. `generated` therefore includes `generated/report.md` but not `other/generated/report.md`.
* A deny scope always wins over an allow scope.
* `deny_write_component` rejects an exact directory or file name at every depth; `deny_write` rejects one rooted path subtree.
* Hidden paths need `allow_hidden` for the narrow path that should be visible.
* Common credential paths need the stronger `allow_sensitive` opt-in, which also permits hidden components inside that specific scope.
* `compose` is restrictive: every composed policy must allow the operation. A library can add constraints without accidentally broadening the application’s policy.
Trusted starter files are installed before model access is enforced. This lets an application seed a read-only file even under a non-writable policy. Later reads, writes, and deletes of that file still go through the policy, so seeding a hidden file does not automatically expose it.
### Paths and containment
Policy paths live in one portable workspace namespace. These spellings identify the same file:
```text
src/lib.rs
/src/lib.rs
/workspace/src/lib.rs
```
Traversal (`..`), NUL bytes, and backslash-separated paths fail closed. Host absolute paths are backend-specific and are not portable policy scopes; use `/workspace/...` in application configuration and model instructions.
The policy layer controls visibility and mutation. The selected filesystem backend remains responsible for mapping workspace paths to storage. The local host backend canonicalizes its root, keeps resolved paths contained, and rejects symlinks in existing path components before every operation. An absolute path outside the configured root cannot expose that host file.
The policy governs capabilities that use Everruns’ session filesystem. A custom tool that calls `std::fs`, launches a shell, or uses another storage API does not pass through this boundary. Apply equivalent restrictions to those tools or run them in a sandbox.
### Symlinks and races
The built-in local backend rejects a symlink introduced after the workspace was configured because it rechecks components on every operation. This blocks normal traversal and symlink-swap attempts between operations.
It is not an OS sandbox. A malicious process running as the same operating system user can race a final path check and filesystem syscall. If local processes are mutually untrusted, use an isolated sandbox/filesystem backend or operating-system isolation. Do not use `WorkspacePolicy` as a substitute for that process boundary.
### Backend extension
The in-process host applies the policy after resolving the platform’s filesystem factory. In-memory, local-disk, database, and custom backends all receive the same policy checks. Backend authors still own containment, symlink-safe I/O, quotas, durability, and atomic update guarantees for their storage system.
Directory listings and grep are enforced at the same boundary as direct reads. Denied files are not opened by policy grep, and denied names, match counts, and byte totals are not returned. Recursive deletes inspect descendants through the backend before deletion, so opting into recursion does not override a deny or protected descendant. Backends with mutable external state must still treat that preflight-to-delete window as a race boundary.
`WorkspaceRootSet` additional roots are named mounts inside one selected head; they are not independent heads and carry no fork/reopen lifecycle. Likewise, `WorkspacePolicy` is path authorization, not a compute sandbox. The sections above describe the identity and lifecycle model.
---
# Everruns Cloud quickstart
> Sign up for Everruns Cloud, use the built-in model provider with starter credit, create a token, and connect from the SDK, CLI, or an AI tool.
Source:
[Everruns Cloud](https://app.everruns.com) runs the Everruns Platform for you: the API, the workers, the database, and the UI. You create agents and sessions; Everruns operates the infrastructure. It includes a built-in model provider, so you can run an agent before you have any provider API keys.
This page takes you from sign-up to a first agent turn, then explains how credit works.
## 1. Sign up
Open [app.everruns.com](https://app.everruns.com) and sign in. Verify your email address when asked. When you create your first organization, it receives **$5 of starter credit**.
The starter credit is granted once per user account, on that user’s first organization, and needs a verified email. Creating more organizations does not grant more. Starter credit is spent before any credit you buy and expires after 90 days.
## 2. Use the built-in provider
New organizations come with the built-in model provider already set up. There are no keys to paste: create an agent in the UI and start a session, and its turns are charged against your organization’s credit.
## 3. Create a personal access token
The SDKs, the CLI, and the API authenticate with a personal access token. In the UI, open **Settings** > **Personal Access Tokens** and create one. Tokens start with `evr_pat_` and are shown once, so copy it before you close the dialog.
A token belongs to your user account, not to one organization, and can reach every organization you belong to. Choose **Read-only** for scripts that only read; it still reads every organization you belong to ([Token access](https://everruns.com/docs/features/ui/#token-access)). Revoke a token from the same page.
## 4. Connect
Pick the client you want. All three talk to `https://app.everruns.com`.
### SDK
Install an [SDK](https://everruns.com/docs/features/sdk/) and set two environment variables:
```bash
pip install everruns-sdk
export EVERRUNS_API_KEY=evr_pat_...
export EVERRUNS_API_URL=https://app.everruns.com/api
```
Newer SDK releases default to Everruns Cloud; setting `EVERRUNS_API_URL` keeps older ones pointed at it too. Then run one turn:
```python
import asyncio
from everruns_sdk import Everruns
async def main():
client = Everruns() # reads EVERRUNS_API_KEY and EVERRUNS_API_URL
agent = await client.agents.apply_by_name(
name="hello", system_prompt="You are a concise assistant."
)
session = await client.sessions.create(agent_id=agent.id)
await client.messages.create(session.id, "Say hello in one sentence.")
print(f"Session: {session.id}")
await client.close()
asyncio.run(main())
```
The message starts a turn; the answer arrives as events. The [SDK tutorial](https://everruns.com/docs/tutorials/building-agents-using-sdk/) shows how to stream them.
### CLI
The [CLI](https://everruns.com/docs/features/cli/) uses Everruns Cloud by default, so there is no URL to configure:
```bash
everruns login # opens the browser to sign in
SESSION=$(everruns sessions create -q)
everruns chat --session "$SESSION" "Say hello in one sentence."
```
`everruns login` signs you in through the browser, creates a personal access token for this machine, and asks which organization to use when you belong to more than one. On a machine without a browser, run `everruns login --token` and paste a token you created in step 3.
### AI tools
The `everruns` plugin connects Claude Code, Codex, and Cursor to Everruns Cloud over MCP, at `https://app.everruns.com/mcp`. It signs in with OAuth on first use, so it needs no token. See [Use in AI Tools](https://everruns.com/docs/getting-started/use-in-ai-tools/).
## Credit and the spend cap
Everruns Cloud is prepaid. There is no subscription.
* **Balance.** **Settings** > **Billing** shows the organization’s available credit, how much of it is starter credit and how much was purchased, and how much is held by work that is still running. Every member of the organization can see it.
* **Top up.** Organization owners and admins buy more credit from the same page. Payment goes through Stripe Checkout, and the credit appears once Stripe confirms the payment.
* **Spend cap.** Each model call reserves enough credit to cover its worst case before it runs, and is charged for actual usage when it finishes. When the balance reaches zero, the built-in provider refuses new calls, so turns that use it stop until someone tops up. A call that was already running when the credit ran out is still charged in full, so the balance can end slightly below zero; the next call is refused.
This credit is separate from [budgets](https://everruns.com/docs/advanced/budgets/). A budget is a limit you set on a session or an agent; credit is what pays for the built-in provider.
## Bring your own keys (optional)
You can add your own provider API keys under **Models** > **Providers**, and point agents at those models. Calls through your own keys are billed by that provider, not against your Everruns credit, and keep working when the credit balance is zero.
## Next steps
* [Concepts](https://everruns.com/docs/getting-started/concepts/): agents, sessions, harnesses, and capabilities.
* [Build your first agent](https://everruns.com/docs/tutorials/building-agents-using-sdk/): a guided SDK lesson that streams the agent’s answer.
* [Capabilities](https://everruns.com/docs/capabilities/): the tools you can give an agent.
* [Self-hosted with Docker Compose](https://everruns.com/docs/getting-started/docker-compose/): run the same Platform on your own infrastructure.
---
# Concepts
> A glossary of the Everruns execution model, harnesses, agents, sessions, turns, events, capabilities, tools, endpoints, and settings, with links to the design pages.
Source:
This page defines each entity you meet in Everruns and links to the page that explains it in depth. The entities fall into three layers:
| Layer | Entities | Lifetime |
| ----------------- | --------------------------- | ------------------------------------------------ |
| **Configuration** | Harness, Agent, Capability | Long-lived. You author these. |
| **Runtime** | Session, Turn, RuntimeAgent | Created per conversation. The server owns these. |
| **Data** | Event, Message | Append-only log produced during runtime. |
Your application creates configuration, starts runtime, and consumes data.

Solid arrows show configuration ownership: a Harness has Agents and Capabilities, and an Agent has Capabilities. Dashed arrows show runtime assembly: configuration merges into a RuntimeAgent, which executes in a Session.
## Configuration
### Harness
What an agent runs on: the execution environment, defaults, and constraints for sessions. A harness sets which capabilities are available by default, the default model, network access, and starter files.
* Each agent holds exactly one harness reference, and many agents can share one harness.
* Each session runs on exactly one harness, the agent’s unless the session names another.
* A harness can have capabilities attached to it.
A Harness is not the agent loop. Elsewhere “agent harness” often means that loop, and Everruns uses the word that way when it calls itself a durable agentic harness engine. The loop is the runtime; a Harness is configuration the runtime reads. See [Harnesses](https://everruns.com/docs/features/harnesses/).
### Agent
A domain-specific or task-specific configuration for the agentic loop: the system prompt, the default model, and the enabled capabilities.
* A session may or may not have an agent, and the agent can change during the session.
* Each agent has capabilities with position ordering and references a default model.
### Capability
A reusable unit that extends a harness, agent, or session. A capability can contribute system prompt additions, tools, and mount points (files and directories in the session filesystem).
* Session capabilities are additive to agent capabilities.
* Built-in capabilities use `snake_case` IDs, such as `current_time` and `web_fetch`.
* MCP servers appear as virtual capabilities with `mcp:{uuid}` IDs, and registry skills as `skill:{uuid}`.
* Capabilities can depend on other capabilities. Dependencies resolve in topological order, so enabling `bashkit_shell` pulls in `session_file_system`.
* Order matters: earlier capabilities’ prompt fragments appear first in the merged system prompt.
A capability exists because the tool definition, the prompt text that teaches the model when to use it, and the session state it needs travel together. See [Capabilities](https://everruns.com/docs/features/capabilities/).
### Channel
An Agent-owned way for an external caller to reach that Agent. Slack, AG-UI, A2A, FCP, and Public Chat each use a channel with transport-specific configuration.
* Each channel belongs to exactly one Agent and has its own publish state, credentials, identity, and session routing.
* An Agent can own several channels, each published or revoked independently.
Create and manage channels from the Agent’s **Integrations** tab. See [Channels](https://everruns.com/docs/features/channels/). For proactive scheduled work, use [Agent triggers](https://everruns.com/docs/features/agent-triggers/) instead.
### Everruns user and virtual user
An **Everruns user** signs in to manage organizations, agents, permissions, and personal access tokens. A **virtual user** is the organization-scoped account that uses agents. It owns an agent-facing profile, preferences, and provider connections.
Each organization membership has a default end-user virtual user. Chats and **Settings → Profile**, **Connections**, **MCP servers**, and **AI clients** use that account. Verified external callers, such as an authenticated public-chat visitor or Slack user, get virtual users without becoming Everruns organization members. Provider, issuer or workspace, and subject identify each external binding; matching email addresses do not merge accounts.
A service virtual user is an agent’s account for unattended execution. User connections resolve from the speaker of the current turn. Service connections resolve from the responding agent’s service account. Neither inherits credentials from a session owner’s management identity.
## Runtime
### RuntimeAgent
The merged configuration a session executes. When a session starts, harness, agent, and session settings fold into one `RuntimeAgent`: earlier layers form the base and later layers override or add. System prompts concatenate, capabilities are deduplicated by ID, and network policies can only narrow (allow lists intersect, blocklists union).
Each layer answers a different question. The harness answers “what environment am I running in?”, the agent answers “what role am I playing?”, and the session answers “what is true for this one conversation?”. Operators control harnesses, application authors control agents, and end users or the runtime control sessions.
### Session
A working instance of the agentic loop and the context where a conversation happens. A session owns an isolated filesystem, a key-value store, and the full event log.
* Each session has a harness; the agent is optional.
* Sessions can add their own capabilities and override the model.
* Status flow: `started` → `active` → `idle`, with `waiting_for_tool_results` while client-side tools run and `paused` when a budget pauses work. Sessions do not terminate; they wait in `idle` for the next input.

### Turn
The agent’s response to one input message: one or more iterations of the loop, each a reason step (call the model) followed by an act step (run the tools the model asked for, in parallel). The turn ends when the model produces a final answer.
* Lifecycle: `turn.started` → reason → act → `turn.completed` (or `turn.failed`).
* A turn runs at most **500 iterations** by default; set `max_iterations` on the agent or session to change it.
On the Platform each step is a separate durable task, so a turn survives a worker crash. See [The agentic loop](https://everruns.com/docs/explanation/agentic-loop/) and [Durable execution](https://everruns.com/docs/explanation/durable-execution/).
### Tool
A function the agent can invoke during the act step. Capabilities provide tools.
* Built-in tools have no name prefix.
* MCP tools are prefixed: `mcp_{server_name}__{tool_name}`.
### File system
Each session has an isolated virtual filesystem stored in PostgreSQL. Paths are relative to `/workspace`, capabilities can mount initial files, the File System and Bashkit Shell capabilities share it, and files can be marked read-only.
### Key-value store
Each session has scoped storage in two tiers: plain key/value entries for state and intermediate results, and secrets encrypted at rest with AES-256-GCM. Storage cannot be read across sessions.
## Data
### Event
An immutable, append-only record and the primary store for conversations and SSE notifications.
* Atomic per-session sequence numbering.
* Types cover input, output, turn, atom, tool, LLM, and session lifecycle.
* Events carry correlation context: turn ID, input message ID, execution ID.
See [Events as the primary store](https://everruns.com/docs/explanation/events/) for why the log comes first, and the [Event Reference](https://everruns.com/docs/event-reference/) for every type.
### Message
A conversation entry reconstructed from the event log. There is no separate messages table.
* Roles: `user`, `agent`, `tool_result`.
* Content is an array of parts: text, image, tool_call, tool_result.
* Agent messages may include extended thinking from reasoning models.
* Per-message controls include a model override and reasoning effort.
## Settings

### LLM provider
A configured API provider such as OpenAI or Anthropic. Providers store encrypted API keys and contain models. See [Providers](https://everruns.com/docs/providers/). On Everruns Cloud a built-in Everruns provider works without your own keys.
### LLM model
A specific model within a provider, such as `gpt-6.1-sol` or `claude-sonnet-5`. Models are predefined, discovered from the provider API, or added manually. Resolution order: message controls → session override → agent default → system default.
### MCP server
A remote server that exposes tools over the Model Context Protocol. Each server becomes a capability with ID `mcp:{server_uuid}`. Tools are discovered at runtime and cached for 24 hours, prefixed to avoid conflicts, and executed over HTTP JSON-RPC. See [MCP Servers](https://everruns.com/docs/features/mcp/).
## Further reading
* [Architecture](https://everruns.com/docs/explanation/architecture/): control plane, workers, and the API-first design.
* [The agentic loop](https://everruns.com/docs/explanation/agentic-loop/): the reason-act cycle and why turns are bounded.
* [Durable execution](https://everruns.com/docs/explanation/durable-execution/): why agents survive crashes.
* [Events as the primary store](https://everruns.com/docs/explanation/events/): why the event log is the source of truth.
---
# Docker Compose
> Deploy the Everruns platform with Docker Compose: control plane, workers, UI, and PostgreSQL.
Source:
Deploy the complete Everruns platform using Docker Compose. This guide sets up the control plane, workers, UI, and database in a single command.
On your own computer, [`everruns docker up`](https://everruns.com/docs/getting-started/run-locally/) does these steps for you. On a cluster, use the [Helm chart](https://everruns.com/docs/getting-started/kubernetes/).
## Prerequisites
* Docker Engine 20.10+
* Docker Compose v2.20+
* 4GB available RAM
## Quick Start
### 1. Download Docker Compose File
```bash
# Create directory and download docker-compose file
mkdir everruns && cd everruns
curl -o docker-compose.yaml https://raw.githubusercontent.com/everruns/everruns/main/examples/docker-compose-full.yaml
```
### 2. Generate Secrets
Everruns encrypts LLM API keys at rest, signs login sessions, and authenticates workers to the control plane. Generate one value for each:
```bash
python3 -c "import os, base64; print('kek-v1:' + base64.b64encode(os.urandom(32)).decode())" # encryption key
openssl rand -hex 32 # JWT secret
openssl rand -hex 32 # worker token
```
### 3. Create Environment File
Create a `.env` file next to `docker-compose.yaml`. The first five values are required: Compose refuses to start without `AUTH_JWT_SECRET`, and the server refuses to start without `WORKER_GRPC_AUTH_TOKEN`.
.env
```bash
SECRETS_ENCRYPTION_KEY=kek-v1:
AUTH_JWT_SECRET=
WORKER_GRPC_AUTH_TOKEN=
AUTH_ADMIN_EMAIL=admin@example.com
AUTH_ADMIN_PASSWORD=
# Optional: two extra workers and VictoriaMetrics (see Profiles below)
COMPOSE_PROFILES=scale,metrics
# Optional: Add API keys here to skip UI configuration
DEFAULT_OPENAI_API_KEY=sk-...
DEFAULT_ANTHROPIC_API_KEY=sk-ant-...
DEFAULT_GEMINI_API_KEY=AIza...
SEED_DEFAULT_PROVIDER_KEYS_FROM_ENV=true
```
`SEED_DEFAULT_PROVIDER_KEYS_FROM_ENV=true` copies the keys into the default organization’s providers at startup; without it they are ignored. The server refuses it when people can sign themselves up (`AUTH_MODE=full` with signup or Google/GitHub sign-in), because anyone who joins could spend the keys.
### 4. Start Services
```bash
docker compose pull # Fetch latest images
docker compose up -d
```
The published compose file defaults to app entry point on `9300`. If that port is busy, override before startup:
```bash
EXAMPLE_PROXY_PORT=10300 docker compose up -d
```
This starts:
* PostgreSQL database
* Valkey (rate limiting) and NATS (event delivery and task notifications)
* Control plane (`server`): HTTP API and worker gRPC
* 1 worker, or 3 with the `scale` profile
* Next.js UI
* Caddy reverse proxy
* VictoriaMetrics, scraping server metrics at (`metrics` profile)
Sign in to the UI with `AUTH_ADMIN_EMAIL` and `AUTH_ADMIN_PASSWORD`.
### 5. Access the Platform
| Service | URL |
| ------------------ | -------------------------------------------------------------- |
| **Web UI** | |
| **API** | … |
| **OAuth** | … |
| **MCP** | |
| **OAuth Metadata** | |
| **Health Check** | |
## Configuration
### Run Multiple Copies
If you want multiple Everruns compose stacks on the same machine, set both a Compose project name and host-port overrides:
```bash
COMPOSE_PROJECT_NAME=everruns-demo-2 \
EXAMPLE_PROXY_PORT=10300 \
docker compose up -d
```
### Configure LLM Provider
If you didn’t set LLM API keys (`DEFAULT_OPENAI_API_KEY`, `DEFAULT_ANTHROPIC_API_KEY`, or `DEFAULT_GEMINI_API_KEY`) in your `.env` file, configure via UI:
1. Open
2. Navigate to **Settings** > **Providers**
3. Add your OpenAI or Anthropic API key
4. Save and verify connection
### Create Your First Agent
1. Go to **Agents** in the UI
2. Click **Create Agent**
3. Set a name and system prompt
4. Select your configured LLM provider
5. Save the agent
### Start a Session
The API requires a token. Create a personal access token under **Settings** > **Personal Access Tokens** in the UI (it starts with `evr_pat_` and is shown once), or from the command line as described in [Authentication](https://everruns.com/docs/sre/runbooks/authentication/#create-personal-access-token).
```bash
export EVERRUNS_TOKEN=evr_pat_...
# Create a session (agent_id in request body)
curl -X POST http://localhost:9300/api/v1/sessions \
-H "Authorization: Bearer $EVERRUNS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"agent_id": "{agent_id}"}'
# Send a message
curl -X POST http://localhost:9300/api/v1/sessions/{session_id}/messages \
-H "Authorization: Bearer $EVERRUNS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": {"role": "user", "content": [{"type": "text", "text": "Hello!"}]}}'
```
## Profiles
Without profiles the stack is the lean set: PostgreSQL, Valkey, NATS, the server, one worker, the UI and Caddy. Two optional [Compose profiles](https://docs.docker.com/compose/how-tos/profiles/) add the rest:
| Profile | Adds |
| --------- | ----------------------------------------------- |
| `scale` | `worker-2` and `worker-3` |
| `metrics` | VictoriaMetrics at |
Set them in `.env` as `COMPOSE_PROFILES=scale,metrics` (as above), or per command: `docker compose --profile metrics up -d`. Profiles need Docker Compose 2.20 or newer.
## Scaling Workers
Add more workers by scaling the worker services (`worker-2` and `worker-3` need the `scale` profile):
```bash
# Scale to 5 workers
docker compose up -d --scale worker-1=1 --scale worker-2=1 --scale worker-3=3
```
Or modify `docker-compose.yaml` to add more worker services.
## Monitoring
### View Logs
```bash
# All services
docker compose logs -f
# Specific service
docker compose logs -f server
docker compose logs -f worker-1
```
### Distributed Tracing
Set `OTEL_EXPORTER_OTLP_ENDPOINT` to export traces to any OTLP-compatible backend (Grafana Tempo, Datadog, etc.).
## Stopping Services
```bash
# Stop all services
docker compose down
# Stop and remove volumes (deletes data)
docker compose down -v
```
## Troubleshooting
### Check Service Health
```bash
docker compose ps
```
`server` should report `healthy`. Its healthcheck runs `everruns-server --health-check`, which calls `/health` inside the container. Images released before that flag existed always show `unhealthy` even when the API works; check `curl http://localhost:9300/health` instead.
### Database Connection Issues
If services fail to connect to PostgreSQL:
```bash
# Check postgres health
docker compose ps postgres
# View postgres logs
docker compose logs postgres
```
### Migration Failures
Migrations are auto-applied when the server starts. If migrations fail:
```bash
# Check server logs for migration errors
docker compose logs server
# Restart the server to retry migrations
docker compose restart server
```
To inspect migration status, use the [admin container](https://everruns.com/docs/sre/admin-container/).
### Worker Not Processing
Verify workers can reach the control plane:
```bash
# Check worker logs
docker compose logs worker-1
```
A worker that logs authentication errors has a different `WORKER_GRPC_AUTH_TOKEN` from the server. Both read it from the same `.env`, so recreate them after changing it: `docker compose up -d --force-recreate`.
## Next Steps
* [API Reference](https://everruns.com/docs/api/) - Full API documentation
* [Capabilities](https://everruns.com/docs/features/capabilities/) - Extend agent functionality
* [Environment Variables](https://everruns.com/docs/sre/environment-variables/) - Advanced configuration
---
# Start here
> Choose how you run Everruns, the Framework, a self-hosted Platform, or Everruns Cloud, and go to the right first page.
Source:
Everruns is a durable agentic harness engine built on Rust. You can use it in three ways. They run the same agent model (agents, sessions, capabilities, events) and differ in what you operate.

| | Framework | Self-hosted | Everruns Cloud |
| -------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| **For** | Rust developers who want agents inside their own application | Teams that need the full Platform on their own infrastructure | Anyone who wants the Platform without operating it |
| **You operate** | Your application process. No server, database, or worker | The Platform: API server, workers, PostgreSQL, UI | Nothing. Everruns runs the Platform at [app.everruns.com](https://app.everruns.com) |
| **You talk to it through** | The `everruns` Rust crate, in process | The REST API, SDKs, CLI, UI, and MCP | The REST API, SDKs, CLI, UI, and MCP |
| **Cost** | Free and open source (MIT). You pay your model provider | Free and open source (MIT). You pay for your infrastructure and model providers | Prepaid credit for the built-in model provider, with $5 of starter credit. Your own provider keys are optional |
| **First page** | [Framework quickstart](https://everruns.com/docs/framework/quickstart/) | [On your machine](https://everruns.com/docs/getting-started/run-locally/), [Docker Compose](https://everruns.com/docs/getting-started/docker-compose/) or [Kubernetes](https://everruns.com/docs/getting-started/kubernetes/) | [Everruns Cloud quickstart](https://everruns.com/docs/getting-started/cloud/) |
Pages across these docs carry an **Applies to** row under the title that names which of the three the page covers.
## Framework
Embed agents in a Rust process with the `everruns` crate. The agent runs inside your application, so there is nothing else to deploy. Bring a model provider key.
```rust
let agent = Agent::builder().instructions("Be concise.")
.provider(OpenAI::from_env()?).model("gpt-6.1-sol").build()?;
let session = Engine::new().create(agent);
println!("{}", session.send_and_wait("Say hello.").await?.response);
```
Next: [Framework quickstart](https://everruns.com/docs/framework/quickstart/), which sets up the crate and runs this program end to end.
## Self-hosted
Run the full Platform on infrastructure you control: the API server, durable workers, PostgreSQL, and the management UI. Use it when you need remote clients, multi-tenant organizations, or durable execution that survives a worker restart, and want to keep the deployment and data in your own environment.
```bash
brew install everruns/tap/everruns
everruns docker up # UI and API on http://localhost:9300
```
Pick the page for where it runs:
* [On your machine](https://everruns.com/docs/getting-started/run-locally/): `everruns docker up` asks a few questions, starts the Platform in Docker and signs the CLI in.
* [Docker Compose](https://everruns.com/docs/getting-started/docker-compose/): the same stack set up by hand on a server.
* [Kubernetes](https://everruns.com/docs/getting-started/kubernetes/): the official Helm chart, with your own PostgreSQL and workers that can scale on queue depth.
Operators continue with [Environment variables](https://everruns.com/docs/sre/environment-variables/).
## Everruns Cloud
Use the Platform at [app.everruns.com](https://app.everruns.com) without running it. A built-in model provider is ready on sign-up, and your first organization gets $5 of starter credit, so you need no provider keys to start.
```bash
everruns login
SESSION=$(everruns sessions create -q)
everruns chat --session "$SESSION" "Say hello."
```
Next: [Everruns Cloud quickstart](https://everruns.com/docs/getting-started/cloud/), which covers sign-up, tokens, the SDK, CLI and AI-tool setup, and how credit works.
## After the first page
* [Concepts](https://everruns.com/docs/getting-started/concepts/): agents, sessions, harnesses, capabilities, and events.
* [Use in AI Tools](https://everruns.com/docs/getting-started/use-in-ai-tools/): drive Everruns from Claude Code, Codex, or Cursor.
* [Build your first agent](https://everruns.com/docs/tutorials/building-agents-using-sdk/): a guided SDK lesson for the self-hosted Platform or Everruns Cloud.
* [Capabilities](https://everruns.com/docs/capabilities/): the tools an agent can use, each marked with where it is available.
---
# Kubernetes
> Install the self-hosted Everruns Platform on Kubernetes with the official Helm chart, using your own PostgreSQL and secrets kept across upgrades.
Source:
The `everruns` Helm chart installs the self-hosted Platform: the API server, workers, the UI, a proxy in front of them, and NATS. Each release publishes the chart as an OCI chart that runs that release’s images.
## Prerequisites
* Kubernetes 1.27+ and Helm 3.8+
* PostgreSQL 15+ that the cluster can reach, such as a managed database or one run by an operator like CloudNativePG
* About 2 GB of memory for the Everruns pods
## Install
```bash
helm install everruns oci://ghcr.io/everruns/charts/everruns \
--namespace everruns --create-namespace \
--set database.url='postgres://everruns:PASSWORD@db.example.com:5432/everruns' \
--set auth.adminEmail=you@example.com \
--set publicUrl=https://everruns.example.com
```
The first start runs the database migrations. Then sign in as the admin with the generated password:
```bash
kubectl -n everruns get secret everruns-generated \
-o jsonpath='{.data.AUTH_ADMIN_PASSWORD}' | base64 -d
kubectl -n everruns port-forward svc/everruns 9300:9300 # then open http://localhost:9300
```
To try the chart without a database of your own, add `--set postgresql.enabled=true`. It starts a single PostgreSQL pod with no backups or failover, so use it only for experiments.
## What it runs
| Part | Notes |
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Server | Always one replica, replaced with `Recreate`: it keeps per-process session state and applies migrations when it starts. |
| Workers | `worker.replicas` (default 1), or scaled on queue depth by [KEDA](https://keda.sh) with `worker.autoscaling.enabled=true`. Scale workers, not the server. |
| UI | One replica. |
| Proxy | Caddy with the same routes as the [Docker Compose](https://everruns.com/docs/getting-started/docker-compose/) setup: `/api`, `/mcp`, OAuth and agent-readable documents go to the server, the rest to the UI. Service `everruns`, port 9300. |
| NATS | Bundled with JetStream for live event delivery, or `nats.url` for your own. |
| Valkey | Optional (`valkey.enabled` or `valkey.url`). The single server keeps rate limits in memory without it. |
## Expose it
Point an Ingress at the proxy service:
```yaml
publicUrl: https://everruns.example.com
ingress:
enabled: true
className: nginx
host: everruns.example.com
tls:
- secretName: everruns-tls
hosts: [everruns.example.com]
```
`publicUrl` is the address people use. Sign-in callbacks and links are built from it.
## Who signs in
`auth.mode` works like the server’s `AUTH_MODE`:
* `admin` (default): one admin account, `auth.adminEmail`, with a password generated on install or set with `auth.adminPassword`.
* `full`: people sign up and sign in. Each organization adds its own provider keys.
* `none`: no sign-in. Only with `deploymentGrade: dev`, and only on a network you trust.
## Secrets
On the first install the chart generates the encryption key, the session signing key, the worker token and the admin password, and stores them in the Secret `-generated`. Upgrades read them back instead of making new ones, and uninstalling the chart leaves that Secret in place.
**Back up that Secret with the database.** The encryption key protects the provider keys and secrets stored in the database; without it they cannot be read. To manage these values yourself, create a Secret with `SECRETS_ENCRYPTION_KEY`, `AUTH_JWT_SECRET`, `WORKER_GRPC_AUTH_TOKEN` and `AUTH_ADMIN_PASSWORD` and set `secrets.existingSecret`.
Provider keys for the whole server go in `providerKeys` or in a Secret named by `providerKeys.existingSecret`. With `seedDefaultProviderKeys: true` they become the default organization’s keys (not with `auth.mode: full`).
## Upgrade
```bash
helm upgrade everruns oci://ghcr.io/everruns/charts/everruns --version 0.49.0 \
--namespace everruns --reuse-values
```
The new server applies the database migrations when it starts. Migrations only go forward, so back up the database first.
## No operator
Everruns ships a chart, not an operator. PostgreSQL is the only stateful part, and managed databases or a PostgreSQL operator handle its lifecycle. The server migrates itself, and KEDA scales workers. Agents and sessions live in the database behind the API, so they are managed with the [CLI](https://everruns.com/docs/features/cli/), the UI or MCP, not as Kubernetes objects.
All chart values, with comments: [`charts/everruns/values.yaml`](https://github.com/everruns/everruns/blob/main/charts/everruns/values.yaml).
---
# Run Everruns on your machine
> Start the self-hosted Platform on your computer with one command, everruns docker up, which asks a few questions, starts it in Docker and signs the CLI in.
Source:
`everruns docker up` starts the self-hosted Platform on your computer: the API, a worker, PostgreSQL and the UI, in Docker. It asks a few questions the first time, generates the secrets, waits until the server is healthy and signs the CLI in to it. Running it again is safe: it starts what is stopped, repairs what is missing and leaves everything else alone.
## Prerequisites
* Docker Engine 20.10+ with Compose 2.20+ (Docker Desktop, OrbStack or Colima on macOS), with about 2 GB of memory for Docker
* The `everruns` CLI ([install](https://everruns.com/docs/features/cli/#install))
## Start it
```bash
everruns docker up
```
```text
✓ Docker: engine 27.3.1
✓ Docker Compose: 2.29.7
Who signs in?: Just me, with a password
Admin email: you@example.com
LLM provider: OpenAI (use OPENAI_API_KEY from your shell)
Everruns 0.48.0 is running at http://localhost:9300
Sign in: you@example.com / pNhN... (shown once; also in ~/.local/share/everruns/docker/default/.env)
CLI: signed in as you@example.com (profile 'local'), try `everruns agents list`
```
The first run pulls the images, so it takes a minute or two. Later runs take a second or two when nothing changed.
`everruns docker doctor` runs the same checks on their own. When Docker is missing, stopped, too old, or your user may not use it, each check says what to do about it.
## The questions
Every question has a flag, and `--yes` accepts the defaults, so scripts and CI run without prompts:
```bash
everruns docker up --yes --auth single --admin-email ci@example.com --provider openai
```
| Question | Flag | Default |
| ----------------------------- | ---------------------------------------------------- | --------------------------------------- |
| Who signs in | `--auth single`, `none` or `accounts` | `single` |
| Admin email | `--admin-email` | your `git config user.email` |
| LLM provider | `--provider openai`, `anthropic`, `gemini` or `skip` | the provider whose key is in your shell |
| Port, only when 9300 is taken | `--port` | the next free port |
**Who signs in:**
* `single`: one admin account with a generated password. Good for your own machine.
* `none`: no sign-in, every request is admin. The port then listens on 127.0.0.1 only, and the server runs as a dev deployment, which also turns on experimental features. Use it for throwaway experiments.
* `accounts`: people sign up and sign in with a password. The CLI creates your account first. Each organization adds its own provider keys in the UI, so `--provider` does not apply.
The provider key is read from `OPENAI_API_KEY`, `ANTHROPIC_API_KEY` or `GEMINI_API_KEY`, or typed at a hidden prompt, never from a command-line argument. It becomes the default organization’s provider key.
Change an answer by passing its flag again, for example `everruns docker up --auth none`. Only the containers whose settings changed restart. Moving away from `none` revokes the tokens made without sign-in, and `up` signs the CLI in again.
## What it keeps
Settings live in one folder per instance: `~/.local/share/everruns/docker/default/` on Linux, `~/Library/Application Support/everruns/docker/default/` on macOS.
* `.env` holds the answers, the admin password and the generated secrets, readable only by you. **Keep it**: it holds the encryption key for the provider keys and secrets stored in the database, and without it they cannot be read.
* `compose.yaml` is the [Docker Compose](https://everruns.com/docs/getting-started/docker-compose/) stack without its optional profiles.
* `backups/` holds the database backups `upgrade` takes.
The database lives in a Docker volume. `up` never regenerates a secret.
## Upgrade
The images match the CLI’s version when the instance is set up, and `up` never moves them. After you upgrade the CLI, `up` tells you when the instance runs an older version, and `upgrade` moves it:
```bash
everruns docker upgrade # to this CLI's version
everruns docker upgrade --image-tag v0.49.0 # or a tag you name
```
The new server runs the database migrations when it starts, and they only go forward, so `upgrade` first saves a database backup in the instance’s `backups/` folder (`--no-backup` skips it). Moving to an older tag asks first: it needs the backup taken before the newer version to work. To restore one, copy it into the `postgres` container and run `pg_restore --clean` against the `everruns` database there.
## Start over
```bash
everruns docker reset
```
`reset` deletes the instance: its containers, the database, the settings folder with the encryption key and backups, and the CLI profile. It asks you to type the instance name first; `--yes` skips that for scripts.
## Day to day
```bash
everruns docker status # URL, health, version, profile and containers
everruns docker logs server # follow one service's logs
everruns docker open # open the UI
everruns docker stop # stop it; data is kept
```
The CLI profile is `local`. It becomes the current profile, after asking when another one is in use. Use `everruns --profile local ...` to reach the local instance while another profile stays current.
## More than one instance
`--name` keeps a separate instance with its own settings, data, port and CLI profile:
```bash
everruns docker up --name demo # profile 'local-demo', next free port
```
## Use another container CLI
`EVERRUNS_DOCKER=podman everruns docker up` runs Podman’s Docker-compatible CLI instead of `docker`. Docker is the tested path.
---
# Use in AI Tools
> Install the Everruns plugin in Claude Code, Codex, Cursor or Gemini CLI so your coding agent can build, run, debug and ship Everruns agents.
Source:
The [Everruns plugin](https://github.com/everruns/plugins) lets a coding agent work with Everruns for you. It connects the Everruns MCP server at `https://app.everruns.com/mcp` (you sign in with OAuth in the browser) and adds skills that load only when a task needs them:
| Skill | Your agent can |
| -------------------------- | -------------------------------------------------------------------------------------------------- |
| `everruns` | Understand product concepts, find documentation and Cloud, and choose the right workflow skill |
| `everruns-cli` | Use the CLI and session shell, discover commands, script operations, and verify changes |
| `everruns-mcp` | Use an external MCP connection, run sessions, handle questions and connections, and read resources |
| `everruns-agent-authoring` | Design and revise agent behavior, harnesses, capabilities, identities, packages, and verification |
| `everruns-run-agent` | Hand a task to an existing agent, follow it, exchange files, and bring back the result |
| `everruns-build-agent` | Build or improve an agent from a plain-language brief, add knowledge and tools, and smoke-test it |
| `everruns-debug-session` | Diagnose failed, stalled, or poor-quality sessions and verify fixes |
| `everruns-evaluate-agent` | Build regression evals, compare models, and score live traffic |
| `everruns-ship-agent` | Expose agents through channels and triggers, with budgets and controls for stopping them |
| `everruns-operate` | Investigate organization spending, health, and change history |
| `everruns-agents-as-files` | Keep agent packages in a repository and validate, compare, import, or export them |
| `everruns-sdk` | Call hosted agents from Python, TypeScript, or Rust applications |
| `everruns-framework` | Develop Rust applications that run agents themselves and compose advanced execution hosts |
## Install
In the Everruns user menu, choose **Connect your coding agent**, copy the setup prompt and paste it into your coding agent. It reads the maintained plugin instructions and guides you through installation and browser sign-in. The same dialog is available after organisation setup and on an empty Agents page.
In **Settings → AI clients**, you can refresh approvals or revoke access. It records OAuth approval and last use, not local plugin installation.
For manual installation:
**Claude Code**:
```text
/plugin install everruns --marketplace everruns/plugins
```
On Claude Code older than 2.1.275, run `/plugin marketplace add everruns/plugins` first, then `/plugin install everruns@everruns`.
**Codex**:
```bash
codex plugin marketplace add everruns/plugins
codex plugin add everruns@everruns
```
Or open `/plugins` in Codex and pick **Everruns**. Start a new session afterwards.

**Cursor**: open **Settings > Plugins**, paste `https://github.com/everruns/plugins`, and install **Everruns**.
**Gemini CLI**:
```bash
gemini extensions install https://github.com/everruns/plugins
```
**Other agents**: hosts that read the [Agent Plugins](https://agent-plugins.org) format can load `plugins/everruns/` from the repository directly. Any MCP client can add `https://app.everruns.com/mcp` as a remote server named `everruns`.
## First run
1. Ask your agent “Who am I on Everruns?” and complete the sign-in in your browser.
2. Ask for real work, for example “create a Hacker News summarizer agent and run it” or “why did this session fail?” with a session link.
Every change the agent makes is recorded in the entity’s [change history](https://everruns.com/docs/features/change-history/) with the reason it gave.
Your coding-agent subscription does not configure model access inside Everruns. Running Everruns agents, including Platform Chat, requires a model provider in **Models → Providers**. An existing Cloud-managed provider can satisfy that need. If you skip provider setup, you can still connect your coding agent for setup and management; onboarding keeps an **Add a model provider** action visible.
## Use the CLI too
The plugin’s skills speak the same commands as the [`everruns` CLI](https://everruns.com/docs/features/cli/). When the CLI is installed and signed in, the agent runs commands there directly, which is faster than going through MCP.
## Use a self-hosted deployment
The plugin points at Everruns Cloud. For your own deployment, keep the plugin’s skills and register your server’s MCP endpoint under the same name, for example with Claude Code:
```bash
claude mcp add --transport http everruns http://localhost:9300/mcp
```
For the CLI, use `everruns --api-url http://localhost:9300/api login` or set `EVERRUNS_API_URL`. The [Docker Compose](https://everruns.com/docs/getting-started/docker-compose/) stack serves both on port 9300.
## Read the docs as text
The documentation site publishes itself as plain Markdown for agents and other tools that would rather read text than HTML.
* [`/llms.txt`](https://everruns.com/docs/llms.txt): the index: the three ways to run Everruns, every documentation set, and the machine-readable surfaces. Start here.
* [`/llms-full.txt`](https://everruns.com/docs/llms-full.txt): every prose page in one file (roughly 250k tokens).
* [`/llms-small.txt`](https://everruns.com/docs/llms-small.txt): the same corpus without the vendor- and operator-specific long tails.
* `/_llms-txt/.txt`: one topic at a time, mirroring the sidebar: [start-here](https://everruns.com/docs/_llms-txt/start-here.txt), [framework](https://everruns.com/docs/_llms-txt/framework.txt), [platform](https://everruns.com/docs/_llms-txt/platform.txt), [built-ins](https://everruns.com/docs/_llms-txt/built-ins.txt), [guides](https://everruns.com/docs/_llms-txt/guides.txt), [integrations](https://everruns.com/docs/_llms-txt/integrations.txt), [explanation](https://everruns.com/docs/_llms-txt/explanation.txt), [reference](https://everruns.com/docs/_llms-txt/reference.txt), [operations](https://everruns.com/docs/_llms-txt/operations.txt). Prefer a set over the complete text.
* [`/api/openapi.json`](https://everruns.com/docs/api/openapi.json): the REST API as OpenAPI 3.0. The text sets carry prose only, so take endpoint shapes from here.
Every page in those files begins with its title, its description, and a `Source:` line holding the canonical URL. Cite that URL, not the text file.
---
# How-to guides
> Task-oriented recipes for common Everruns workflows, equipping agents with tools, streaming events, deploying to channels, and operating production agents.
Source:
These guides target the Everruns Platform (Everruns Cloud or self-hosted). Framework guides live under [/framework/](https://everruns.com/docs/framework/).
Each how-to here solves one concrete problem. They assume you already understand the basics (read the [Tutorials](https://everruns.com/docs/tutorials/run-an-agent/) first) and they don’t try to teach concepts (see [Explanation](https://everruns.com/docs/explanation/) for that).
## Building agents
* [Equip an agent with tools](https://everruns.com/docs/how-to/equip-agents-with-tools/), pick capabilities and assign them.
* [Give an agent web access](https://everruns.com/docs/how-to/give-an-agent-web-access/), `web_fetch`, network policies, allowlists.
* [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/), version-controllable agent definitions in Markdown, TOML, or YAML.
* [Use AGENTS.md for project instructions](https://everruns.com/docs/how-to/use-agents-md/), inject project-level context into the system prompt.
* [Customize a harness](https://everruns.com/docs/how-to/customize-a-harness/), create your own harness as a starting point for many agents.
* [Share knowledge with OKF](https://everruns.com/docs/how-to/share-knowledge-with-okf/), import/export Knowledge Bases as Open Knowledge Format bundles, managed like code.
* [Migrate between LLM providers](https://everruns.com/docs/how-to/migrate-providers/), swap OpenAI ↔ Anthropic ↔ Gemini without rewriting agents.
## Running agents
* [Call your agent from code](https://everruns.com/docs/how-to/call-your-agent-from-code/), give an application its own key to one agent, act for your users, and reach it from a browser.
* [Stream events](https://everruns.com/docs/how-to/stream-events/), consume the SSE stream from the Python SDK, or from curl, EventSource, or any HTTP client, with reconnection and event filtering.
* [Complete a URL elicitation over the API](https://everruns.com/docs/how-to/complete-a-url-elicitation/), drive the pause-and-consent flow from your own client.
* [Handle errors and cancel turns](https://everruns.com/docs/how-to/handle-errors-and-cancellation/), graceful failure paths, turn cancellation, retries.
* [Orchestrate multi-agent pipelines](https://everruns.com/docs/how-to/orchestrate-multi-agent-pipelines/), chain sessions together.
* [Build a foreman agent](https://everruns.com/docs/how-to/build-a-foreman-agent/), put one agent in front of a team of specialists and let it triage and delegate.
## Packaging and distribution
* [Package and publish an agent skill](https://everruns.com/docs/how-to/package-a-skill/), author a SKILL.md, bundle scripts and references, and share it across agents through the registry.
* [Publish an agent as a Slack app](https://everruns.com/docs/how-to/publish-to-slack/), deploy an agent to a Slack workspace.
* [Summarize GitHub pull requests](https://everruns.com/docs/how-to/summarize-github-pull-requests/), connect GitHub and comment a summary on every pull request.
* [Set up review and security agents](https://everruns.com/docs/how-to/set-up-review-and-security-agents/), review every pull request and scan a repository for vulnerabilities on a schedule, on any model.
## Operating
* [Automate with the CLI](https://everruns.com/docs/how-to/automate-with-the-cli/), scripting against the CLI with `jq`.
* [Deploy with Docker Compose](https://everruns.com/docs/getting-started/docker-compose/), bring up the full platform.
* [Enforce a budget](https://everruns.com/docs/how-to/enforce-a-budget/), cap token spend per agent, session, or organization.
---
# Automate with the CLI
> Script the Everruns CLI with structured output, jq, quiet mode, and shell pipelines for CI, cron jobs, and integration with other tools.
Source:
The CLI emits structured output (JSON, YAML) for scripting. Combined with `jq` and `--quiet` mode, it composes naturally with shell pipelines.
For a command reference, see [CLI](https://everruns.com/docs/features/cli/).
## Capture IDs
```bash
AGENT_ID=$(everruns agents create \
--name "assistant" \
--system-prompt "You are a helpful assistant." \
-o json | jq -r '.id')
SESSION_ID=$(everruns sessions create --agent "$AGENT_ID" -o json | jq -r '.id')
everruns chat "What time is it?" --session "$SESSION_ID"
```
## Quiet mode
`--quiet` suppresses headers and tables, printing only the essential identifier:
```bash
everruns agents create -f agent.toml --quiet
# Output: agent_550e8400e29b41d4a716446655440000
```
Useful inside `$(...)` substitution when JSON parsing is overkill.
## Filter listings
```bash
# Active agents only
everruns agents list --output json | jq '.data[] | select(.status == "active")'
# Just the names of agents tagged "production"
everruns agents list -o json \
| jq -r '.data[] | select(.tags[]? == "production") | .name'
# Agents created in the last 24h
everruns agents list -o json \
| jq --arg cutoff "$(date -u -d '24 hours ago' +%FT%TZ)" \
'.data[] | select(.created_at > $cutoff)'
```
## Authenticate and configure the API URL
The CLI targets Everruns Cloud (`https://app.everruns.com/api`) by default. Sign in once:
```bash
everruns login # browser sign-in
everruns login --token # headless/SSH: paste an API key
```
For a self-hosted server, point the CLI at it:
```bash
# Per-command
everruns --api-url http://localhost:9300/api agents list
# For the whole shell
export EVERRUNS_API_URL=http://localhost:9300/api
```
In CI, skip `login`: set `EVERRUNS_API_KEY` (and `EVERRUNS_API_URL` for self-hosted) from secrets and the CLI picks them up. `EVERRUNS_API_KEY` always takes precedence over stored credentials.
## Drive sessions from a file-defined agent
```bash
cat > agent.md <<'EOF'
---
name: "code-reviewer"
capabilities:
- ref: current_time
- ref: session_file_system
tags: [development]
---
You are an expert code reviewer.
When reviewing code:
1. Check for bugs and edge cases
2. Suggest performance improvements
3. Ensure code follows best practices
EOF
AGENT_ID=$(everruns agents create -f agent.md -o json | jq -r '.id')
SESSION_ID=$(everruns sessions create --agent "$AGENT_ID" -o json | jq -r '.id')
everruns chat "Review the diff at HEAD~1..HEAD" --session "$SESSION_ID"
```
## Send-and-exit (no streaming)
`--no-stream` queues the message and returns immediately. Useful when a downstream system polls for results.
```bash
everruns chat "Process the queue" --session "$SESSION_ID" --no-stream
```
## See also
* [CLI reference](https://everruns.com/docs/features/cli/), full command list and flags.
* [Define agents as files](https://everruns.com/docs/how-to/define-agents-as-files/), the file formats accepted by `-f`.
---
# Build a foreman agent
> Put one agent in front of a team of specialists, so a Slack mention is triaged and delegated to the right worker instead of answered by a generalist.
Source:
A **foreman** is an agent whose job is routing, not answering. It receives an ambiguous request, decides which specialist should handle it, delegates, and reports back. The specialists are ordinary Agents with their own prompts, tools, and models.
This is server-side delegation. If you want the *application* to chain agents in sequence, see [Orchestrate multi-agent pipelines](https://everruns.com/docs/how-to/orchestrate-multi-agent-pipelines/) instead — that pattern keeps control in your code. A foreman keeps control in the agent, which is what you want when the trigger is a human asking for something in Slack.
## Prerequisites
* Two or more Agents to delegate to, each already working on its own.
* An agent to act as the foreman.
* For the Slack front door: a Slack workspace where you can create apps.
## Step 1 — Get the workers right first
Build and test each specialist on its own before wiring any delegation. A foreman that routes correctly to a broken worker looks like a broken foreman, and you will debug the wrong layer.
Give each one a narrow prompt and only the capabilities it needs. “Reviews Rust diffs for correctness bugs” routes better than “helps with code”, because the foreman picks targets from their descriptions.
Note each worker’s agent id (`agent_...`) and harness id (`harness_...`). Both are required for a handoff target: the harness determines the capabilities and mounts available to the worker’s child session.
## Step 2 — Give the foreman the `agent_handoff` capability
Delegation targets are an explicit allowlist on the foreman. Configure the `agent_handoff` capability with one entry per worker:
```json
{
"targets": [
{
"id": "code_reviewer",
"name": "Code Reviewer",
"description": "Reviews diffs and pull requests for correctness bugs",
"agent_id": "agent_...",
"harness_id": "harness_...",
"required_connections": [],
"required_scopes": []
},
{
"id": "incident_responder",
"name": "Incident Responder",
"description": "Investigates alerts and production incidents",
"agent_id": "agent_...",
"harness_id": "harness_..."
}
]
}
```
`description` is the field the model reads when choosing a target, so write it for that purpose. `id` is the stable key the foreman passes back in tool calls; keep it short.
Two properties worth knowing, because they shape what the foreman can do:
* The foreman **does not inherit the target’s tools**, and never receives the target’s provider credentials. It can ask a worker to act; it cannot act as the worker.
* `required_connections` gates a handoff on a provider connection existing before the run starts, which turns a mid-run credential failure into an up-front refusal.
`required_scopes` are audit labels in the current implementation, not enforced grants — a tool that needs hard authorization checks its own scopes before acting.
## Step 3 — Write the foreman’s prompt
This is the part that decides whether the thing works, and the part no configuration can do for you. A foreman’s prompt needs four things:
1. **When to delegate and when to answer.** Without this, a foreman either delegates trivia or answers things it should have routed. Be concrete: “If the request names a file, a diff, or a PR, hand off to `code_reviewer`.”
2. **How to choose between overlapping targets**, and what to do when none fits — usually ask a clarifying question rather than guessing.
3. **What to do while work is running.** Background handoffs return immediately; the foreman must know to report that it has dispatched, not to invent a result.
4. **How to report back.** A foreman that delegates silently is worse than no foreman, because the requester cannot tell whether anything is happening.
Keep it short. A long routing prompt tends to produce a foreman that reasons about routing out loud in your Slack channel.
## Step 4 — Understand what `spawn_agent` gives you
The foreman delegates with `spawn_agent`, using `target.type = "agent"` and the `target.id` from your config. The parameters that matter for a foreman:
| Parameter | Why a foreman cares |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode` | `background` (default) returns a `task_id` immediately so the foreman can dispatch several workers and stay responsive. `foreground` blocks until the child finishes. `invite` joins the target into the *current* session instead of a child one. |
| `result_schema` | A JSON Schema the child must satisfy. Turns “whatever the worker said” into a structured result the foreman can act on rather than re-parse. |
| `public_context` | Non-secret context appended to the child task. Use it for the Slack thread reference so the worker knows where the request came from. |
| `instructions` | The work request. **Must not contain credentials** — the child has its own. |
Background handoffs create a task with `wake_policy = on_terminal`, so the foreman is woken when a worker finishes. It does not poll, and you should not prompt it to.
While work is in flight the foreman manages it with the generic task tools: `list_tasks`, `get_task`, `message_task` to steer a worker mid-run, and `cancel_task`. These work identically for subagents, so a foreman can mix both kinds of delegation.
### Subagent or handoff?
Both go through `spawn_agent`; the difference is what the child *is*.
| | `target.type = "subagent"` | `target.type = "agent"` |
| ------------ | ------------------------------------------------------ | ------------------------------------------------------------------------- |
| Child config | Inherits the foreman’s harness and agent configuration | The target Agent’s own prompt, capabilities, MCP servers, model |
| Set-up cost | None — spawn by name | Build and allowlist the Agent first |
| Use when | The work is the same kind of work, just parallel | The work needs different tools, a different model, or different authority |
A foreman that fans out “review these six files” wants subagents. A foreman that routes “is this a code question or an incident?” wants handoff. Most real ones use both.
Subagent fan-out needs the separate `subagents` capability on the foreman — see [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/). Nesting is depth-governed (`max_subagent_depth`) with root-tree caps on live and total descendant tasks, so a foreman cannot fork-bomb your org by accident.
## Step 5 — Put it in front of Slack
Expose the **foreman** — and only the foreman — on Slack. The workers stay internal; they are reached through delegation, not by being mentioned.
Follow [Publish an agent as a Slack app](https://everruns.com/docs/how-to/publish-to-slack/) for the mechanics. Two choices matter for a foreman:
* **`session_strategy: per_thread`** (the default). Each Slack thread becomes one foreman session, which is what you want: the thread is the unit of work, and the foreman keeps its delegation state for the life of that thread.
* **Enable the agent surface** (`agent_surface_enabled`) if you want the foreman available in Slack’s assistant pane as well as in channels. The pane streams replies token-by-token and shows a status line while tools run.
## Step 6 — Let the foreman act on Slack (optional)
Out of the box an agent can reply in its own thread and nothing else. It cannot add a reaction, send a DM, look someone up, or post to another channel.
If your foreman needs those — acknowledging a request with an emoji while work runs is the common one — attach a Slack MCP server as a capability on the foreman. MCP servers appear as virtual capabilities alongside built-in ones.
Be aware this means a second Slack token, separate from the channel’s bot token, with its own scopes to manage and rotate.
## Step 7 — Test the routing, not the workers
Send the foreman requests that are deliberately near the boundary between two targets, and requests that match none. Those are where routing fails. A request that obviously belongs to one worker will pass whether or not your prompt is any good.
Check that a dispatched request reports back into the thread that asked. A foreman that accepts work and reports somewhere else trains people to stop using it.
## Limits worth knowing before you commit
* **No approval buttons.** Slack interactivity is not wired up, so a foreman cannot ask “approve this?” with a button and act on the click. It can only ask in prose and read the reply. For a foreman that dispatches consequential work, this is the real constraint.
* **Progress is per-turn, not per-task.** The Slack status line reflects the foreman’s current turn. “3 of 5 workers finished” is available to the foreman via `list_tasks` but is not rendered into Slack for you; the foreman has to say it.
* **Two identities** if you use a Slack MCP server, as above.
## See also
* [Sub Agents](https://everruns.com/docs/capabilities/sub-agents/) — the `subagents` capability and the shared `spawn_agent` dispatcher
* [Publish an agent as a Slack app](https://everruns.com/docs/how-to/publish-to-slack/)
* [Orchestrate multi-agent pipelines](https://everruns.com/docs/how-to/orchestrate-multi-agent-pipelines/) — the client-side alternative
---
# Call your agent from code
> Give an application its own key to one agent, call it from Python, TypeScript, Rust, or curl, act for your own users, and reach it safely from a browser.
Source:
An application that only needs to talk to one agent should not hold a personal access token, which reaches everything you can manage. Give it an **agent key** instead: it works on one agent’s session routes and nothing else. This guide uses the Agent API channel; the [Agent API](https://everruns.com/docs/features/agent-api/) reference covers every route and setting.
## 1. Publish the agent with an Agent API channel
In the agent’s **Integrations** tab, add an **Agent API** channel, then create a key in its **Agent keys** card. The secret (`evr_ak_…`) is shown once. The channel shows its **agent URL**, for example `https://app.everruns.com/api/v1/channels/appchan_…`.
Name each key after the application that holds it, so you know which one to revoke. Rotating gives a new secret and keeps the old one working for an overlap you choose, so you can deploy the new secret first.
## 2. Call it
Every SDK has an `AgentClient` for this. It reads `EVERRUNS_AGENT_URL` and `EVERRUNS_AGENT_KEY` from the environment.
```python
from everruns_sdk import AgentClient
async with AgentClient() as agent:
reply = await agent.run("Where is order 42?")
print(reply)
```
```typescript
import { AgentClient } from "@everruns/sdk";
const agent = new AgentClient();
console.log(await agent.run("Where is order 42?"));
```
```rust
use everruns_sdk::AgentClient;
let agent = AgentClient::from_env()?;
println!("{}", agent.run("Where is order 42?", None).await?);
```
`run` starts a session, sends the message, follows the event stream until the turn ends, and returns the agent’s reply. Pass a session id to continue a conversation. For full control use the session calls directly: `create_session`, `send_message`, `stream_events`, `list_events`, `cancel`, and the answers to questions and tool approvals.
Without an SDK, the same calls are plain HTTP:
```bash
curl -X POST "$EVERRUNS_AGENT_URL/sessions" \
-H "Authorization: Bearer $EVERRUNS_AGENT_KEY"
curl -X POST "$EVERRUNS_AGENT_URL/sessions/$SESSION_ID/messages" \
-H "Authorization: Bearer $EVERRUNS_AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{"message":{"role":"user","content":[{"type":"text","text":"Where is order 42?"}]}}'
curl -N "$EVERRUNS_AGENT_URL/sessions/$SESSION_ID/sse?after_sequence=0" \
-H "Authorization: Bearer $EVERRUNS_AGENT_KEY"
```
A key sees only the sessions it started. What the stream shows is the channel’s **visibility**: by default messages plus “a tool ran”, never tool names, arguments, or internal errors.
### Send files
A message can carry files in `files`, each as base64 `data` or a public `https` `url` that the server downloads. The Agent finds them in the session’s `/attachments` folder. Message `content` holds text and image parts; for anything else use `files`.
```json
{
"message": {
"role": "user",
"content": [
{
"type": "text",
"text": "Check this invoice against order 42."
}
]
},
"files": [
{
"name": "invoice.pdf",
"media_type": "application/pdf",
"url": "https://example.com/invoices/42.pdf"
}
]
}
```
A request body is limited in size, so send large files by `url`. A file that cannot be taken becomes a note the Agent reads instead ([Files people send](https://everruns.com/docs/features/channels/#files-people-send)).
## 3. Retry safely
Send an `Idempotency-Key` header (the SDKs take an `idempotency_key` argument) when you create a session or send a message. A retry with the same key and the same request within 24 hours returns the first response instead of starting a second session or sending the message twice.
## 4. Act for your own users
If your application has users, let the agent keep their conversations apart and use their own connections. Create the key with the **end user** permission and name the user on each request:
```python
alice = agent.for_end_user("customer-42")
await alice.run("Where is my order?")
```
The SDKs send this as an `End-User` header. Sessions then belong to that user: the key acting as itself, or for another user, does not see them.
Instead of a key, your users can also bring a token from your own identity provider: list it in the channel’s `auth_methods` (OpenID Connect or OAuth 2.0 introspection). Everruns only validates those tokens. Members of your organization can use their own personal access token when you turn on **Organization members**.
## 5. Call it from a browser
Never put an agent key in a browser or mobile app. Your backend exchanges it for a short-lived **runtime token** for one user, and the page uses that:
```python
token = await agent.for_end_user("customer-42").runtime_token()
# hand token["access_token"] to the page; it lasts 15 minutes and works on this agent only
```
List the page’s origin, such as `https://app.example.com`, under **Browser origins** on the channel so the browser allows the calls.
## 6. Cap what one caller can spend
Set **Daily spending limit per caller** on the channel. When one key or one end user reaches it, starting a session or sending a message answers `429` until the next UTC day. Reading sessions still works, and a turn already running finishes.
## Related
* [Tutorial: Serve an Agent and Call It from Code](https://everruns.com/docs/framework/serve-tutorial/): the same `AgentClient` against an agent you run yourself with serve.
* [Agent API](https://everruns.com/docs/features/agent-api/): every route, credential, channel setting and status code.
* [Channels](https://everruns.com/docs/features/channels/): every channel type and the shared lifecycle.
* [Stream events](https://everruns.com/docs/how-to/stream-events/): the event stream and reconnection.
* [Handle errors and cancel turns](https://everruns.com/docs/how-to/handle-errors-and-cancellation/).
---
# Complete a URL elicitation over the API
> Drive the pause-and-consent flow from your own client — declare the hint, read the confirm_url_elicitation event, and post the user's decision.
Source:
When an MCP server asks that a person finish something in their browser, Everruns pauses the turn and waits for a decision (see [URL mode elicitation](https://everruns.com/docs/features/mcp-url-elicitation/)). The Chat UI renders a card for this. Any client can do the same over the REST API and the SSE stream.
## 1. Declare that you can ask
The pause only happens for clients that say they can answer it:
```bash
curl -X POST "$EVERRUNS/api/v1/sessions" \
-H 'content-type: application/json' \
-d '{
"agent_id": "agent_01a063c3b55f79f2b5de55fb002e0ae3",
"hints": { "url_elicitation": true }
}'
```
Without the hint the turn never pauses and the flow cannot be completed — see [Clients that cannot pause](#clients-that-cannot-pause).
## 2. Watch for the pause
Send a message as usual. When a tool call hits an elicitation, the session moves to `waiting_for_tool_results` and a `tool.call_requested` event arrives on `GET /api/v1/sessions/{session_id}/sse`:
```json
{
"type": "tool.call_requested",
"data": {
"tool_calls": [
{
"id": "url_elicitation_01a063cf-c0bb-7891-926e-fd83aeb24d88",
"name": "confirm_url_elicitation",
"arguments": {
"server": "acme_analytics",
"tool": "run_revenue_report",
"retry_tool": "mcp_acme_analytics__run_revenue_report",
"message": "Acme Analytics needs your API key before it can run this report.",
"url": "https://acme-analytics.example/connect?ref=rev-2026-08",
"url_host": "acme-analytics.example",
"url_is_punycode": false
}
}
]
}
}
```
Everything needed to render your own surface is in `arguments`: the full URL, the host to emphasise, whether that host is Punycode, and which server is asking.
Show the URL in full and open it only on an explicit action. Never fetch it on the user’s behalf.
## 3. Post the decision
Post **when the user says they have finished**, not when they open the link. The server checks whether the out-of-band interaction completed, so consenting at open time just makes it ask again.
```bash
curl -X POST "$EVERRUNS/api/v1/sessions/$SESSION_ID/mcp-elicitation-consent" \
-H 'content-type: application/json' \
-d '{
"tool_call_id": "url_elicitation_01a063cf-c0bb-7891-926e-fd83aeb24d88",
"action": "accept"
}'
```
```json
{ "host": "acme-analytics.example", "status": "active" }
```
`"action": "decline"` records nothing and lets the agent continue without the tool. Errors worth handling: `404` when the tool call is not a pending elicitation, `409` when the session is not paused (already answered, or timed out).
The request body carries only the decision. The server, tool and domain the consent applies to are read from the event Everruns emitted, so a client cannot record consent for something the user was never shown.
## 4. Nothing else to do
Everruns records the consent, adds the decision to the conversation as a user turn, and resumes. Your stream then shows the tool being called again and the result arriving — the retry answers the MCP server `accept` on your behalf.
## Timing and reuse
* **You have about five minutes.** A session left in `waiting_for_tool_results` is swept (`TOOL_RESULT_TIMEOUT_SECS`, default `300`), the pending call is completed as a timeout, and the turn resumes without consent.
* **One consent authorises one retry.** It is deleted when used, so a second elicitation asks again.
* **Consent is bound to the domain the user saw.** If the server elicits a different host on the retry, the consent is not reused and a new `confirm_url_elicitation` event arrives.
## Clients that cannot pause
Without the `url_elicitation` hint the turn continues and the model relays the link, carrying this payload as the tool result:
```json
{
"code": "url_elicitation_required",
"url": "https://acme-analytics.example/connect?ref=rev-2026-08",
"url_host": "acme-analytics.example",
"url_is_punycode": false,
"server": "acme_analytics",
"tool": "run_revenue_report",
"retry_tool": "mcp_acme_analytics__run_revenue_report",
"message": "Acme Analytics needs your API key before it can run this report.",
"declined": false
}
```
That is informational only: with no pause there is no pending call to answer, the consent endpoint returns `409`, and the tool elicits again on every retry. Declare the hint for any client that needs these tools to complete.
## Calling Everruns as an MCP server
The reverse direction is plain MCP. Declare the capability in `_meta`:
```json
{
"_meta": {
"io.modelcontextprotocol/clientCapabilities": { "elicitation": { "url": {} } }
}
}
```
`session_set_secret` then answers with an `input_required` result instead of taking a value:
```json
{
"resultType": "input_required",
"requestState": "eyJ1c2VyX2lkIjoi…",
"inputRequests": {
"secret": {
"method": "elicitation/create",
"params": {
"mode": "url",
"url": "https://app.example.com/api/mcp/elicitations/secret?token=eyJ1c2Vy…",
"message": "Everruns needs the value of 'STRIPE_API_KEY' for session session_…"
}
}
}
}
```
Send the user to that URL, then retry the same call with the state echoed and the answer under the server’s own key:
```json
{
"name": "session_set_secret",
"arguments": { "session_id": "session_…", "name": "STRIPE_API_KEY" },
"requestState": "eyJ1c2VyX2lkIjoi…",
"inputResponses": { "secret": { "action": "accept" } }
}
```
```json
{ "resultType": "complete", "structuredContent": { "name": "STRIPE_API_KEY", "stored": true } }
```
A client that never declared `elicitation.url` gets `-32021` with the missing capability named, rather than being asked for the value.
## A runnable version
The [URL elicitation example](https://github.com/everruns/everruns/tree/main/examples/mcp-url-elicitation) has both halves: a dependency-free MCP server that elicits, and a script that walks the flow above end to end against your deployment.
## Related
* [URL mode elicitation](https://everruns.com/docs/features/mcp-url-elicitation/)
* [Consume events via SSE](https://everruns.com/docs/how-to/stream-events/#with-raw-sse)
---
# Customize a harness
> Create a custom harness that bundles your preferred capabilities, system prompt baseline, and default model, then use it as the starting point for many agents.
Source:
A harness is the base environment for sessions: a system prompt baseline, a default model, and pre-bundled capabilities. Create a custom one when you have a set of defaults you want to share across many agents.
For the design rationale, see [Why three configuration layers](https://everruns.com/docs/getting-started/concepts/#runtimeagent).
## Create a harness via API
```bash
curl -X POST http://localhost:9300/api/v1/harnesses \
-H "Content-Type: application/json" \
-d '{
"name": "research-assistant",
"display_name": "Research Assistant",
"description": "Harness with research capabilities",
"system_prompt": "You are a research assistant. Cite primary sources.",
"capabilities": [
{"ref": "session_file_system"},
{"ref": "web_fetch"},
{"ref": "stateless_todo_list"}
]
}'
```
Names follow `[a-z0-9]+(-[a-z0-9]+)*` (up to 64 characters, no consecutive hyphens) and are unique per organization. Use `name` in API calls; `display_name` is for the UI only.
`system_prompt` is optional. Omit it when a harness exists only to bundle capabilities or MCP servers on top of a parent, the effective prompt is then composed from the parent harness, agent, session, and capabilities. For example, a capability-only harness that inherits its prompt from `worker`:
```bash
curl -X POST http://localhost:9300/api/v1/harnesses \
-H "Content-Type: application/json" \
-d '{
"name": "research-tools",
"display_name": "Research Tools",
"parent_harness_id": "harness_...",
"capabilities": [
{"ref": "web_fetch"},
{"ref": "stateless_todo_list"}
]
}'
```
## Preview before creating
```bash
curl -X POST http://localhost:9300/api/v1/harnesses/preview \
-H "Content-Type: application/json" \
-d '{
"system_prompt": "You are a helpful assistant.",
"capabilities": [
{"ref": "session_file_system"},
{"ref": "bashkit_shell"}
]
}'
```
Preview returns the merged system prompt and the tool list. Useful when capability ordering matters or when you’re not sure which capability adds which prompt fragment.
## Use the harness for sessions
```bash
# By name
curl -X POST http://localhost:9300/api/v1/sessions \
-H "Content-Type: application/json" \
-d '{
"harness_name": "research-assistant",
"agent_id": "agent_..."
}'
# Or via the CLI
everruns sessions create --harness research-assistant --agent agent_...
```
## Inheritance
Harnesses support single-parent inheritance, a child harness can extend another, layering on extra capabilities or a longer system prompt. The merge is associative: a chain of N harnesses produces the same `RuntimeAgent` as a single pre-merged harness.
This is useful when one team owns a base harness and other teams add their own specialisation on top.
## When to use a harness vs. an agent
* **Harness**: defaults shared across *many agents* (e.g., “all our internal agents have file access, web fetch, and the company AGENTS.md”).
* **Agent**: per-role configuration (system prompt, voice, role-specific tools).
* **Session**: per-conversation tweaks (extra capability for this user, narrower network policy).
Don’t pack agent-specific behaviour into a harness, it makes the harness a god-object and erodes the layering benefit.
## See also
* [Harnesses feature page](https://everruns.com/docs/features/harnesses/)
* [Built-in harnesses](https://everruns.com/docs/built-ins/harnesses/base/), the shipped baselines you can extend.
* [Equip an agent with tools](https://everruns.com/docs/how-to/equip-agents-with-tools/), at the agent layer.
---
# Define agents as files
> Load, validate, compare, import, export and serve portable agents with instructions, files, skills, MCP servers and channel descriptions.
Source:
An agent package describes authored behavior and assets. The same definition works in the UI, API, CLI, Platform Chat, MCP, Framework and serve. Use an `agent.toml` folder with instructions and selected assets. ZIP is the transport format for complete folders.
## A folder with good defaults
```text
triage/
├── agent.toml
├── instructions.md
├── runbook.md
├── data/
│ └── example.csv
└── .agents/
└── skills/
└── investigate/
├── SKILL.md
└── scripts/check.py
```
```toml
schema_version = 1
name = "triage"
display_name = "Triage assistant"
capabilities = ["session_file_system", "current_time"]
max_iterations = 20
parallel_tool_calls = false
files = ["runbook.md", "data/**"]
[channels.chat]
type = "ag_ui"
[channels.chat.config]
tool_visibility = "generic"
generic_tool_text = "Investigating…"
rate_limit_per_minute = 30
```
A folder contains exactly one `agent.toml`, `agent.md`, `agent.yaml`, `agent.yml` or `agent.json`. The defaults are:
* `instructions.md` supplies instructions when none are inline.
* `files` explicitly selects ordinary files or globs. Paths stay relative to the directory root: `data/example.csv` becomes `data/example.csv` in Agent Files and Session Files. Files are read-only unless `is_readonly = false` is declared. Unselected project files are never automatically included.
* `.agents/skills/` supplies direct skill subfolders at the same relative paths, including scripts, references and binary assets. Each `SKILL.md` must have valid front matter and a name matching its directory. The `skills` capability is added automatically.
* `agent.toml` and `instructions.md` configure behavior; they are runtime files only when explicitly selected in `files`. Exports preserve runtime files that collide with authoring filenames as inline entries in the manifest.
* The Platform resolves its default model and harness when omitted. Framework applications bind a model explicitly. Serve uses its simulator when omitted.
* Channels default to disabled. `enabled = true` requests a draft channel; publication and credentials are destination bindings.
TOML, YAML and JSON share one schema. Capability strings select default config; `{ ref = "current_time", config = {} }` supplies explicit config. See the [format reference](https://everruns.com/docs/reference/agent-package/) below for optional fields.
## Paths, environments and session files
An Environment describes how execution is provisioned; a Sandbox is the live execution resource. Both use the same relative file tree. The physical working directory is host-specific (for example `/workspace` in a managed sandbox). Use relative paths in packages and tools; `/workspace/` remains a supported runtime alias. The UI shows **Files**, without adding a package directory wrapper.
Agent Files are the versioned starting files. New sessions with a new file tree receive that starting snapshot. Attaching to an existing file tree preserves its current files; `initial_files` on such a session request is rejected. Change existing files through the Files API after reviewing the intended updates. Editing or importing an agent does not rewrite existing sessions. A fork copies current session files, and sandbox recovery uses committed session files rather than reapplying the agent template. Durable files and live processes have separate lifecycles; installed software and background processes depend on the Environment.
Older folders with `files/` and `skills/` still import. When `files`/`initial_files` is omitted, legacy `files/` contents map to the working-directory root. An explicit empty list disables that discovery. Legacy `skills/` maps to `.agents/skills/`. If both skill roots exist, select one explicitly with `skills = [".agents/skills"]`. New folder and ZIP exports use root-relative files and `.agents/skills/`.
## Describe MCP dependencies
Reference an MCP server already configured in your organization when the platform owns its authentication and installation:
```toml
[mcpServers.issues]
use = "catalog:linear"
actsAs = "user"
```
An inline server can declare explicit credential requirements:
```toml
[mcpServers.docs]
type = "http"
url = "https://example.com/mcp"
[mcpServers.docs.headers]
Authorization = "${DOCS_AUTHORIZATION}"
```
The Framework resolves that requirement only through `package.bind_mcp(...)`. Bind this named server requirement with a same-named `AgentBuilder::mcp_server` before applying the package. Serve explicitly resolves named environment requirements at startup. Platform imports resolve named servers and credentials from your organization’s MCP settings.
## Validate, compare and import
```bash
# Offline; no platform credentials required.
everruns agents validate ./triage
everruns agents diff ./triage --against ./triage-before
# Check destination models, harnesses, capabilities, configured MCP servers and channels.
everruns agents validate ./triage --remote
everruns agents diff ./triage --target triage
# Create, or explicitly update by name.
everruns agents import ./triage
everruns agents import ./triage --target triage
# Full package export; does not overwrite existing output files.
everruns agents export triage --format folder --out ./triage-export
everruns agents export triage --format zip --out ./triage.zip
everruns agents export triage --format markdown --out ./triage.md
```
`agents create --file agent.toml` remains supported, including the existing `--initial-files-dir` and `--writable` options. New file workflows use `agents import`. Inline creation accepts `--instructions`; `--system-prompt` remains an alias.
Diffs compare normalized authored values: capability and tag ordering is ignored; file changes show SHA-256, size and read-only status instead of dumping contents. Remote diffs preserve omitted channel bindings, matching import’s channel-upsert behavior. Diff and validate never apply changes. Import replaces the agent’s configuration and upserts declared channel configuration. New channels default to disabled; `enabled = true` creates a draft; existing enablement and credential bindings remain at the destination, and omitted channels survive.
## UI and HTTP API
In **Agents → Import**, select a ZIP for a complete folder, or a self-contained TOML, YAML, JSON or Markdown file. The preview follows the Agent view: rendered instructions with a **Source** toggle, plus model/harness defaults and capabilities in the settings column. Open **Files** for starting files, permissions and skills, **Settings** for MCP servers and runtime settings, and **Integrations** for channels. For an existing destination, open **Changes** to review current/imported values and added, removed or updated files before applying. Validation failures block importing. Export offers a complete ZIP or Markdown.
The HTTP import endpoint accepts raw text or ZIP bytes. Folder references must be materialized by a local loader or supplied in ZIP; the server never reads paths from its own disk.
```bash
curl -X POST "$EVERRUNS_API_URL/v1/agents/import?format=zip" \
-H "Authorization: Bearer $EVERRUNS_API_KEY" \
-H "Content-Type: application/zip" --data-binary @triage.zip
curl "$EVERRUNS_API_URL/v1/agents/triage/export?format=zip" \
-H "Authorization: Bearer $EVERRUNS_API_KEY" -o triage.zip
```
`POST /v1/agents/validate` and `POST /v1/agents/diff` accept the same bytes or an envelope `{ "content": "…", "format": "toml", "target": "triage" }`. Validation returns `valid`, field-addressed `diagnostics`, and an authored `preview` when parsing succeeds, even if a destination dependency is missing. The preview contains file sizes, permissions and digests, without asset bodies or resolved credentials. Diff requires `target` and returns `changed` and `changes`. Export formats are `markdown` (default), `toml`, `yaml`, `json` and `zip`.
## Sync an Agent continuously from GitHub
Open an existing Agent and select **More actions → Sync with GitHub**. Connect the Agent’s GitHub App in **Integrations** first, or choose an organization-linked Everruns App installation there. Select an accessible repository, its branch, and a package folder or standalone manifest. Leave the path empty for a package at the repository root. Use **Browse folder** to navigate, then **Preview changes** to review the same validation and changes as an import. Select **Enable continuous sync** to apply the reviewed commit and follow subsequent changes. Built-in Agents must be copied before connecting a source.
A repository can contain several Agents, each in its own folder:
```text
agents/
support/
agent.toml
instructions.md
.agents/skills/triage/SKILL.md
reviewer/
agent.toml
instructions.md
```
Changes apply automatically after GitHub pushes. Periodic checks approximately every minute recover missed deliveries and work without a public webhook URL. New per-agent GitHub Apps subscribe to **Push** events. For an older App, enable that subscription in its GitHub settings for faster updates; periodic sync still works. The shared Everruns App needs the same subscription on its webhook.
The **Source** panel shows the applied commit, last check, last successful sync, and errors. **Sync now** queues another check. A missing file, invalid package, missing destination dependency or lost GitHub access retains the last successful Agent. Reconnecting a changed GitHub connection requires selecting the source again. If a human configured the source, that person must retain Agent management permission in the organization.
Choose **Sync from GitHub** to keep authored Agent configuration and package-owned channel settings read-only in Everruns, or **Sync with GitHub** to edit on either side. Two-way sync requires **Contents: Read and write** in the GitHub App settings; the installation must accept the updated permissions. Existing read-only bindings stay read-only until you change the sync direction and review the source again.
In two-way mode, saved edits are queued and committed directly to the selected branch. Branch protection still applies: a branch that requires pull requests will reject direct updates, retain local edits and show an error. Use a branch that permits the Agent’s App to commit. The Source panel shows queued edits and conflicts. If both sides change the package, neither version is overwritten automatically. **Review GitHub version** previews the incoming version; enabling sync after review replaces local authored changes. Export first if you want to save both versions, or disconnect to keep local edits without publishing them.
Write-back preserves the selected manifest format, instruction-file location and existing asset paths. New assets can be inline. Editing a manifest may normalize its formatting and comments. Unrelated repository files remain untouched. Source sync cannot change GitHub workflows. Credentials, unnamed destination channels and channel publication remain local. Sync follows the existing Agent Files lifecycle and never rewrites existing session files. Export and copy remain available. **Disconnect** retains your current definition and stops new sync attempts; a GitHub write already in flight may still complete.
The API exposes `GET`, `PUT` and `DELETE /v1/agents/{agent_id}/source`, plus `POST .../source/options`, `POST .../source/preview` and `POST .../source/sync`. Preview takes `repository`, `branch` and `path`; connecting supplies the returned `commit` and `agent_digest` alongside that selection. Set `mode` to `two_way` for write-back; omitted `mode` defaults to `pull`. Changing an existing source also supplies its current `expected_generation`. If the reviewed source or Agent changed, preview again before connecting.
The same operations are available under `everruns agents source --help`. Use `preview` to review a package, `connect` with the returned commit and digest to enable sync, `get` to inspect status, `sync` to request a check, and `disconnect` to restore local editing. Each command includes a worked example in its help.
## Platform Chat and MCP
In Platform Chat, ask Everruns to validate, compare, import or export an agent from the chat’s files. The assistant can run the same CLI commands:
```bash
everruns agents validate /agents/triage
everruns agents diff /agents/triage --target triage
everruns agents import /agents/triage --target triage
everruns agents export triage --format zip --out /exports/triage.zip
```
`file` and `out` refer only to the current session workspace. Reads and writes use its existing tenant, permission and private-memory checks. Export creates a new artifact and fails if that path already exists.
When using an MCP client connected to Everruns, these operations are available as the tools `import_agent`, `export_agent`, `validate_agent_package` and `diff_agent_package`. A client without a current session sends materialized `content`; export without `out` returns the portable manifest. `--content` also accepts inline text in Platform Chat.
## Framework: load and run
```rust
use everruns::{AgentPackage, Engine, Model, PackageFormat};
let package = AgentPackage::load("./triage")?;
let agent = package.builder()?
.model(Model::simulated("Ready to triage."))
.build()?;
let session = package.create(&Engine::new(), agent)?;
let result = session.send_and_wait("Help with an issue.").await?;
assert!(result.success);
package.write_folder("./triage-export")?;
let markdown = package.to_string(PackageFormat::Markdown)?;
```
Disk loading and folder export are enabled by the Framework’s default `agent-package-fs` feature. Applications using `default-features = false` must select it explicitly. In-memory parsing, ZIP and diffs remain available without disk access. The shared codec lives in `everruns-core::agent_package`, behind the core `agent-package` feature; core’s default build is unchanged.
For live inference, bind the provider in application code. MCP requires the `mcp` feature; stdio requires `mcp-stdio`. Resolve `${ENV_NAME}` values explicitly with `package.bind_mcp(|name| ...)` before applying the package. Custom tools must be bound through `.tool(...)` before `package.apply_to(builder)`; names and schemas must match. Unknown capabilities fail with a dependency diagnostic.
`Agent::to_package()` exports configuration, files and custom function-tool schemas. Closures, lifecycle hooks, approval handlers and code-defined capabilities require host code; they cannot be transferred as executable code. Packages preserve channel descriptions; Framework applications bind transports separately. Platform environment declarations need explicit session bindings and are rejected by the generic package builder.
`package.create` resolves the standard `base`, `conversation`, `worker` and `bashkit-worker` harnesses. For custom harnesses, use `Engine::create(agent).harness(...)`.
## Serve a file or folder
```rust
serve::start(
serve::App::builder().agent_package("./triage").build()
).await
```
Combine `.discover()` with `.agent_package(...)` to bind registered custom tools. Assets are loaded and frozen at app construction; restart to apply edits. Alternatively, `AppBuilder::package(AgentPackage::from_zip(include_bytes!(...))?)` embeds a package in the binary. The serve build manifest includes the declaration and asset contents so edits change its build ID. Channel descriptions do not register arbitrary network handlers; configure serve channel transports separately.
## Format reference and editor schema
The [agent folder and TOML reference](https://everruns.com/docs/reference/agent-package/) covers every manifest field, defaults, file mappings, skill layout, MCP settings, channels, validation limits and compatibility rules. The [public v1 JSON Schema](https://everruns.com/docs/schemas/agent/v1.json) applies to the parsed TOML document. Add its editor directive at the top of `agent.toml`:
```toml
#:schema https://everruns.com/docs/schemas/agent/v1.json
schema_version = 1
name = "my-agent"
```
Editor validation checks the document shape. Run `everruns agents validate` for asset and skill validation, and add `--remote` to check destination bindings.
## Examples and references
* [TOML agents, root files, skills and legacy Markdown examples](https://github.com/everruns/everruns/tree/main/examples/agents)
* [Runnable Framework example](https://github.com/everruns/everruns/blob/main/crates/everruns/examples/file_agent.rs)
* [Shared package API](https://docs.rs/everruns-core/latest/everruns_core/agent_package/)
* [REST API reference](https://everruns.com/docs/api/)
* [CLI](https://everruns.com/docs/features/cli/)
* [Framework agents](https://everruns.com/docs/framework/agents/) and [serve](https://everruns.com/docs/framework/serve/)
* [MCP configuration](https://everruns.com/docs/features/mcp/)
## Keep simple Markdown agents
Existing Markdown with YAML front matter remains supported. Its body becomes **instructions**. The old `system_prompt` field is accepted as an input alias; new exports use `instructions`. Conflicting values fail validation.
```markdown
---
name: dad-jokes
capabilities:
- current_time
---
Tell one short, family-friendly dad joke when asked.
```
Unversioned legacy imports can still use `id`, `default_model_id` and `harness_id`. New packages and every export omit these resource IDs. Use `--target dad-jokes` to update an existing agent explicitly.
---
# Enforce a budget
> Cap LLM spend per session or agent with USD, token, or credit-denominated budgets, soft pause thresholds, and stacked limits.
Source:
Budgets cap how much a session can spend on LLM calls. After every generation, Everruns debits the cost from any active budgets; when the balance reaches zero, the session stops. This guide creates and applies a budget.
For the design and enforcement semantics, see [Budgets](https://everruns.com/docs/advanced/budgets/).
## Create a USD budget for a session
```bash
curl -X POST http://localhost:9300/api/v1/budgets \
-H "Content-Type: application/json" \
-d '{
"scope": "session",
"scope_id": "session_...",
"currency": "usd",
"limit": 10.00,
"soft_limit": 8.00
}'
```
When session spend exceeds `soft_limit`, the session pauses (status becomes `paused`) so a human can decide to top up or stop. When it hits `limit`, the session terminates.
## Token-denominated budget
```bash
curl -X POST http://localhost:9300/api/v1/budgets \
-H "Content-Type: application/json" \
-d '{
"scope": "agent",
"scope_id": "agent_...",
"currency": "tokens",
"limit": 2000000
}'
```
Token budgets are model-agnostic, they cap raw token usage regardless of which provider the session uses.
## Currencies at a glance
| Currency | Unit | Cost basis |
| --------- | ----------------------- | -------------------------------------------------------- |
| `usd` | US dollars | Per-model pricing (input/output cost per million tokens) |
| `tokens` | Raw tokens | Direct count of input + output tokens |
| `credits` | 1 credit = 1,000 tokens | Token count ÷ 1,000 |
| Custom | Any string | Falls back to raw token count |
USD budgets reflect real costs, so the same amount lasts longer on a cheaper model than on a more expensive one.
## Stack budgets for layered limits
You can apply multiple budgets to a session at once. The **most restrictive** wins. A common pattern:
* `$10 USD` session budget, caps dollar cost.
* `2,000,000 tokens` agent budget, caps total tokens regardless of pricing.
Both apply; whichever runs out first stops the session.
## Listen for budget events
Budget thresholds emit events you can subscribe to:
```python
async for event in client.events.stream(session.id):
if event.type == "budget.warning":
print(f"Budget warning: {event.data}")
elif event.type == "budget.paused":
print(f"Session paused at soft limit")
elif event.type == "budget.exhausted":
print(f"Session stopped — budget exhausted")
```
A warning fires at 20% remaining; pause fires when crossing the soft limit; exhaustion fires at zero balance.
## Resume a paused session
After a `budget.paused` event, you can:
* Increase `limit` to give the session more headroom:
```bash
curl -X PATCH http://localhost:9300/api/v1/budgets/$BUDGET_ID \
-H "Content-Type: application/json" \
-d '{ "limit": 20.00, "soft_limit": 16.00 }'
```
* Or call the resume endpoint to continue against the existing limit (the next LLM call may push the budget over).
## A note on enforcement
Budget checks run **after** each LLM call, not before, to avoid latency on the hot path. The last generation can slightly overshoot the limit, this is expected and by design. Treat budgets as cost caps, not hard cutoffs measured in single tokens.
## See also
* [Budgets](https://everruns.com/docs/advanced/budgets/), full design, ledger semantics, custom currencies.
* [Self-Budget capability](https://everruns.com/docs/capabilities/self-budget/), let agents inspect their own budget at runtime.
---
# Equip an agent with tools
> Assign capabilities to an agent so it can read files, run shell commands, fetch URLs, and track tasks.
Source:
This guide assigns common capabilities to an agent so it can interact with files, run commands, and fetch URLs. For the full catalog see the [Capabilities reference](https://everruns.com/docs/capabilities/).
## Common capabilities
| Capability ID | Tools provided | What it’s for |
| --------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `web_fetch` | `web_fetch` | Fetch URLs, convert HTML to markdown |
| `session_file_system` | `read_file`, `write_file`, `edit_file`, `list_directory`, `grep_files`, `delete_file`, `stat_file` | Per-session virtual filesystem |
| `bashkit_shell` | `bash` | Sandboxed bash shell |
| `stateless_todo_list` | `write_todos` | Structured task tracking |
| `current_time` | `get_current_time` | Current date/time awareness |
| `session_storage` | `kv_store`, `secret_store` | Key/value and encrypted secrets |
## Assign capabilities at creation
```python
from everruns_sdk import AgentCapabilityConfig
agent = await client.agents.create(
name="researcher",
display_name="Researcher",
system_prompt="You research topics and save notes to /workspace.",
capabilities=[
AgentCapabilityConfig(ref="web_fetch"),
AgentCapabilityConfig(ref="session_file_system"),
AgentCapabilityConfig(ref="stateless_todo_list"),
],
)
```
## Update an existing agent
The SDK has no partial agent update. Re-apply the full definition with `apply_by_name`, which updates the agent when the name exists:
```python
agent = await client.agents.apply_by_name(
"researcher",
"You research topics and save notes to /workspace.",
capabilities=[
AgentCapabilityConfig(ref="web_fetch"),
AgentCapabilityConfig(ref="session_file_system"),
AgentCapabilityConfig(ref="bashkit_shell"),
],
)
```
## Configure a capability
Some capabilities accept per-agent configuration. Pass it in `config`:
```python
agent = await client.agents.apply_by_name(
"researcher",
"You research topics and save notes to /workspace.",
capabilities=[
AgentCapabilityConfig(ref="web_fetch", config={"enable_file_download": True}),
AgentCapabilityConfig(ref="session_file_system"),
],
)
```
## Verify the agent has the tools
```python
agent = await client.agents.get(agent.id)
for cap in agent.capabilities:
print(cap.ref, cap.config or "")
```
## Notes on ordering
Capability order matters: capabilities earlier in the list contribute their system prompt fragments first. Put high-priority context (project conventions, AGENTS.md) before tool-specific guidance.
## See also
* [Capabilities reference](https://everruns.com/docs/capabilities/), all available capabilities.
* [Why capabilities are first-class](https://everruns.com/docs/getting-started/concepts/#capability), the design rationale.
* [Give an agent web access](https://everruns.com/docs/how-to/give-an-agent-web-access/), narrower task with network policies.
---
# Give an agent web access
> Enable the web_fetch capability, restrict outbound network access with allow/block lists, and verify the agent reaches only intended hosts.
Source:
`web_fetch` gives an agent the `web_fetch` tool: fetch any URL, optionally convert HTML to markdown. By default it can reach any public host, with built-in SSRF protection blocking private IPs. To restrict it further, layer **network access lists** on the harness, agent, or session.
## Enable the capability
```bash
curl -X PATCH http://localhost:9300/api/v1/agents/$AGENT_ID \
-H "Content-Type: application/json" \
-d '{
"capabilities": [
{ "ref": "web_fetch" }
]
}'
```
Or in an agent definition file:
```yaml
capabilities:
- ref: web_fetch
- ref: session_file_system # so the agent can save fetched content
```
## Restrict to specific hosts
Pass `network_access` when creating or updating the agent. Patterns can be exact domains, wildcard domains, or URL prefixes:
```bash
curl -X POST http://localhost:9300/api/v1/agents \
-H "Content-Type: application/json" \
-d '{
"name": "Research Agent",
"system_prompt": "You are a research assistant.",
"capabilities": [{ "ref": "web_fetch" }],
"network_access": {
"allowed": ["*.github.com", "api.openai.com", "https://docs.python.org/3/"],
"blocked": ["evil.example.com"]
}
}'
```
| Pattern | Matches |
| ----------------------------- | ----------------------- |
| `api.example.com` | Exact domain |
| `*.example.com` | Domain + all subdomains |
| `https://api.example.com/v1/` | URL prefix |
Domain matching is case-insensitive. **Blocked patterns always win** over allowed patterns.
## Layered policies
The three layers (harness → agent → session) can only narrow access, never expand it:
* `allowed` lists intersect across layers.
* `blocked` lists union across layers.
So a session can tighten what its agent allows but cannot punch a hole through the agent’s `blocked` list.
## Tighten further per-session
When you don’t trust a particular session’s input, restrict more:
```bash
curl -X POST http://localhost:9300/api/v1/sessions \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent_...",
"network_access": {
"blocked": ["internal.corp", "*.staging.example.com"]
}
}'
```
## SSRF protection
Even without an explicit policy, `web_fetch` blocks private IP ranges by default, loopback, RFC1918, link-local, and CGNAT, with DNS pinning to prevent rebinding attacks. To explicitly allow a private host, set it in `allowed` and disable SSRF protection at the harness level (see [Network access control](https://everruns.com/docs/advanced/network-access/)).
## Verify
Try a URL the agent should reach, then one it shouldn’t, and check the tool result events:
```python
await client.messages.create(session.id, "Fetch https://api.github.com/zen")
# Should succeed.
await client.messages.create(session.id, "Fetch https://evil.example.com/")
# Should fail with a network policy error in the tool result.
```
## See also
* [Network access control](https://everruns.com/docs/advanced/network-access/), full pattern semantics and layering rules.
* [Web Fetch capability](https://everruns.com/docs/capabilities/web-fetch/), tool reference.
* [Equip an agent with tools](https://everruns.com/docs/how-to/equip-agents-with-tools/)
---
# Handle errors and cancel turns
> Detect and recover from failed turns, cancel a long-running turn, and react to common error events from the SSE stream.
Source:
Turns can fail (the LLM rejected the request, a tool errored repeatedly) or be cancelled by the user. The event stream tells you which. This guide covers both paths.
## Detect a failed turn
Failures surface as `turn.failed` events:
```python
async for event in client.events.stream(session.id):
if event.type == "turn.completed":
break
if event.type == "turn.failed":
message = event.data.get("error", "unknown")
code = event.data.get("error_code")
print(f"Turn failed [{code}]: {message}")
break
```
`error` is a human-readable string. `error_code` is a stable, machine-readable string; branch on it, not on the message text. Use it to decide whether to retry, surface to the user, or escalate:
```python
RETRYABLE = {"provider_rate_limited", "provider_unavailable", "dependency_unavailable"}
if event.type == "turn.failed":
code = event.data.get("error_code")
if code in RETRYABLE:
... # wait, then retry with backoff
elif code in {"budget_exhausted", "budget_paused"}:
... # raise or resume the budget
else:
... # surface the message to the user
```
With the `generic` error disclosure mode, the displayed `error_code` collapses to `processing_error` to hide provider detail.
## Cancel a long-running turn
To cancel a turn that’s already running:
```python
import asyncio
await client.messages.create(session.id, "Analyse every Python package on PyPI")
await asyncio.sleep(2)
await client.sessions.cancel(session.id)
```
Cancellation emits a `turn.cancelled` event, appends a user message noting the cancellation, and the worker emits a final agent message confirming the work was stopped. The session itself stays open and accepts new messages.
## React to all three terminal states
```python
TERMINAL = {"turn.completed", "turn.failed", "turn.cancelled"}
async for event in client.events.stream(session.id):
if event.type in TERMINAL:
print(f"[{event.type}]")
if event.type == "turn.failed":
print(event.data.get("error"))
break
```
## Retry a failed turn
Failed turns don’t auto-retry from the application’s perspective (durable execution retries individual steps inside a turn, not the whole turn). To retry, send the message again:
```python
async def send_with_retry(client, session_id, content, attempts=2):
for attempt in range(attempts):
await client.messages.create(session_id, content)
async for event in client.events.stream(session_id):
if event.type == "turn.completed":
return True
if event.type == "turn.failed":
if attempt == attempts - 1:
return False
break
return False
```
Don’t retry indefinitely, a turn that fails twice usually fails for a reason (rate limit, malformed prompt, missing capability). Surface to the user.
## Common error codes
| `error_code` | Cause | Action |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `provider_rate_limited` | LLM provider throttled the request. `reason: "in_flight_budget_exhausted"` means the provider’s in-flight spending limit is reached; `retry_after` is the wait in seconds | Wait, retry with backoff. When `retry_after` is at most 600 s, the session retries on its own (`auto_retry: true`), up to 3 times an hour |
| `provider_usage_limit_reached` | Provider subscription or plan limit hit; resets at a later time | Wait for the reset time, or switch model |
| `provider_unavailable` | Provider is down or unreachable | Retry later, or switch provider |
| `provider_quota_exhausted` | Provider account is out of credits. `requested_max_tokens` and `affordable_tokens` are set when the balance covers fewer output tokens than requested | Top up the provider account, or lower max output tokens |
| `provider_misconfigured` | Missing or invalid provider API key | Fix the provider key |
| `provider_attestation_required` | Provider needs an account confirmation (for example OpenRouter age verification) | Complete it in the provider settings |
| `model_unavailable` / `model_not_configured` | Model missing, or none set for the agent or session | Pick a configured model |
| `request_too_large` | Context overflowed even after compaction | Trim the conversation, start a fresh session |
| `invalid_tool_schema` | A tool definition was rejected by the provider | Fix the tool’s schema |
| `budget_exhausted` / `budget_paused` | The budget has no room left, or is paused | Raise or resume the budget, see [Enforce a budget](https://everruns.com/docs/how-to/enforce-a-budget/) |
| `blocked_by_hook` | A `user_prompt_submit` hook rejected the message | Change the message or the hook |
| `processing_error` | Unclassified failure, or details hidden by disclosure mode | Check server logs |
Cancellation is not a failure: `sessions.cancel` produces `turn.cancelled`, which needs no retry.
## See also
* [Stream events](https://everruns.com/docs/how-to/stream-events/)
* [Event Reference](https://everruns.com/docs/event-reference/), all event types and payloads.
* [The agentic loop](https://everruns.com/docs/explanation/agentic-loop/#what-happens-when-the-loop-gets-stuck), failure modes.
---
# Migrate between LLM providers
> Switch agents from OpenAI to Anthropic to Gemini (or any OpenAI-compatible provider) without rewriting prompts or capabilities.
Source:
Everruns abstracts the LLM behind a uniform interface, so the same agent can run on OpenAI, Anthropic, Gemini, or any OpenAI-compatible provider. This guide swaps providers cleanly without losing sessions or rewriting agents.
## Concepts
Two pieces decide which model runs:
* **LLM Provider**: a configured API provider with encrypted credentials (e.g., `openai`, `openrouter`, `anthropic`, `gemini`, `openai_completions`).
* **LLM Model**: a specific model on a provider (e.g., `gpt-6.1-sol`, `claude-sonnet-5-5`, `gemini-3.8-flash`).
Model resolution priority on each turn:
1. Message-level `controls.model_id` (if present on the incoming message).
2. Session override (`session.default_model_id`).
3. Agent default (`agent.default_model_id`).
4. System default.
## Add a new provider
```bash
curl -X POST http://localhost:9300/api/v1/providers \
-H "Content-Type: application/json" \
-d '{
"name": "anthropic",
"provider_type": "anthropic",
"api_key": "sk-ant-..."
}'
```
The API key is encrypted at rest. The provider’s models are discovered on creation; manually-added models are also supported.
## Switch an agent’s default model
```bash
curl -X PATCH http://localhost:9300/api/v1/agents/$AGENT_ID \
-H "Content-Type: application/json" \
-d '{ "default_model_id": "model_..." }'
```
Model IDs have the form `model_` followed by 32 hex characters. Find the ID of the model you want with `GET /api/v1/models` (or the model list in the UI), and substitute it for `model_...` throughout this guide.
New sessions inherit the new default. **Existing sessions keep running on the model they started with** unless you override per-session or per-message.
## Override per session
For an A/B comparison, override at the session level:
```bash
curl -X POST http://localhost:9300/api/v1/sessions \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent_...",
"default_model_id": "model_..."
}'
```
## Override per message
The most targeted form, run a single turn on a different model:
```python
from everruns_sdk import Controls
await client.messages.create(
session.id,
"Re-analyse the above with extra rigour.",
controls=Controls(model_id="model_..."),
)
```
## Compatibility caveats
Most behaviour ports across providers, but a few things differ:
* **Extended thinking / reasoning effort.** Supported on Anthropic Claude, OpenAI GPT-5.x, and o-series. Other models silently ignore the `reasoning_effort` control.
* **Execution phases on the wire.** OpenAI Responses API on the GPT-5.4 and GPT-5.5 families accepts `phase` annotations on replayed messages; other providers (and earlier OpenAI models) ignore them. Internal tracking continues regardless. See [Execution phases](https://everruns.com/docs/explanation/agentic-loop/#execution-phases).
* **Tool call format differences.** The platform handles translation, but very large tool schemas may compress better on one provider than another.
* **Per-token cost.** USD budgets adjust automatically since they use per-model pricing. Token budgets are model-agnostic.
## Verify before flipping production
A safe migration sequence:
1. Add the new provider and verify discovered models.
2. Create a test agent that mirrors production, with `default_model_id` set to a model on the new provider.
3. Run an evaluation suite against the test agent.
4. Once happy, update the production agent’s `default_model_id`, new sessions migrate over.
5. Leave old sessions on the old model; they age out naturally.
## See also
* [Concepts: LLM Provider and Model](https://everruns.com/docs/getting-started/concepts/), entity model.
* [Observability with Braintrust](https://everruns.com/docs/observability/braintrust/), evaluate cross-provider quality.
---
# Orchestrate multi-agent pipelines
> Chain multiple Everruns agents together by passing output from one session into another.
Source:
A common pattern is splitting work across specialised agents, a researcher gathers facts, a writer turns them into prose, an editor polishes the result. Each is a separate agent and session; the application chains them.
This is the simplest orchestration pattern: no shared state, no subagent spawning, just sequential calls.
## Pipeline skeleton
```python
import asyncio
from everruns_sdk import AgentCapabilityConfig, Everruns
async def run_pipeline(client: Everruns, topic: str) -> str:
researcher = await client.agents.create(
name="researcher",
system_prompt="Research the given topic thoroughly. Write detailed notes.",
capabilities=[
AgentCapabilityConfig(ref="web_fetch"),
AgentCapabilityConfig(ref="session_file_system"),
],
)
research_session = await client.sessions.create(agent_id=researcher.id)
await client.messages.create(research_session.id, f"Research {topic}")
research_output = await collect_final_text(client, research_session.id)
writer = await client.agents.create(
name="writer",
system_prompt="Write clear, well-structured technical articles.",
)
writer_session = await client.sessions.create(agent_id=writer.id)
await client.messages.create(
writer_session.id,
f"Write a blog post based on this research:\n\n{research_output}",
)
return await collect_final_text(client, writer_session.id)
async def collect_final_text(client: Everruns, session_id: str) -> str:
final_text: str | None = None
async for event in client.events.stream(session_id):
if event.type == "output.message.completed":
message = event.data.get("message", {})
final_text = "\n".join(
p["text"] for p in message.get("content", []) if p.get("type") == "text"
)
elif event.type == "turn.failed":
raise RuntimeError(event.data.get("error", "turn failed"))
elif event.type == "turn.cancelled":
raise RuntimeError("turn cancelled")
elif event.type == "turn.completed":
break
if not final_text:
raise RuntimeError("turn completed without producing a final message")
return final_text
```
## Cleanup
Each session and agent persists until you explicitly delete it. For ephemeral pipelines, clean up at the end:
```python
for sid in (research_session.id, writer_session.id):
await client.sessions.delete(sid)
for aid in (researcher.id, writer.id):
await client.agents.delete(aid)
```
For pipelines you’ll re-run, *don’t* recreate the agents, create them once, store the IDs, and reuse them.
## When to use subagents instead
If one agent needs to delegate to another *during a turn*, use the [Sub Agents capability](https://everruns.com/docs/capabilities/sub-agents/) instead of an application-level pipeline. Subagents run inside the parent session and are tracked as session tasks that emit `task.*` events (`task.created`, `task.updated`, `task.message.sent`, `task.message.received`) on the parent session.
Pick application-level pipelines when:
* The stages are clearly separated and you want independent observability per stage.
* Stages run at different cadences (e.g., scheduled research → on-demand writeup).
* You want to reuse intermediate output across multiple downstream agents.
Pick subagents when:
* The parent agent decides at runtime which subagent to call.
* The work feels like a single user request, not a pipeline.
## See also
* [Sub Agents capability](https://everruns.com/docs/capabilities/sub-agents/)
* [Stream events](https://everruns.com/docs/how-to/stream-events/)
---
# Package and publish an agent skill
> Author a SKILL.md, bundle scripts and references, use it from the session workspace, and publish it to the organization-wide Skills Registry so any agent can use it.
Source:
Skills are portable instruction packages following the [Agent Skills](https://agentskills.io/) open spec. They use progressive disclosure: the agent sees only names and descriptions until it activates a skill, at which point the full instructions load.
This guide first packages a skill and uses it from one session’s workspace, then publishes it to the Skills Registry so any agent in the organization can use it.
## SKILL.md format
Every skill is a directory containing a `SKILL.md` with YAML front matter:
```yaml
---
name: csv-analyzer
description: Analyze CSV files and generate summary reports.
metadata:
category: data-processing
version: "1.0"
---
# CSV Analyzer
## When to Use
Activate this skill when a user provides a CSV file and wants summary statistics.
## Instructions
1. Read the CSV file using the `read_file` tool
2. Run `scripts/analyze.py` via `bash`
3. Present findings to the user
```
Required front-matter fields:
| Field | Constraint |
| ------------- | -------------------------------------------- |
| `name` | 1–64 chars, lowercase alphanumeric + hyphens |
| `description` | 1–1024 chars, describes when to activate |
Optional fields: `metadata`, `license`, `compatibility`.
## Bundle scripts and references
Skills can include arbitrary files. The agent accesses them via the session filesystem after activation:
```plaintext
/.agents/skills/csv-analyzer/
├── SKILL.md
├── scripts/
│ └── analyze.py
└── references/
└── REFERENCE.md
```
The skill lives at `/workspace/.agents/skills/csv-analyzer/` in the session VFS. After activation, the agent reads its bundled files there with the regular file tools such as `read_file` and `list_directory`.
## Enable the skills capability
Add the built-in `skills` capability to the agent so it can discover and activate skills from the workspace:
```bash
curl -X POST http://localhost:9300/api/v1/agents \
-H "Content-Type: application/json" \
-d '{
"name": "Data Analyst",
"capabilities": [
{ "ref": "skills" },
{ "ref": "session_file_system" }
]
}'
```
`skills` depends on `session_file_system`; the platform pulls it in automatically.
## How activation looks to the agent
With the capability enabled, the system prompt includes an `` block (\~100 tokens per skill):
```xml
csv-analyzer
Analyze CSV files and generate summary reports.
```
When the user’s task matches, the agent calls `activate_skill`:
```json
{ "name": "activate_skill", "arguments": { "name": "csv-analyzer" } }
```
The tool returns the full SKILL.md instructions wrapped in `` tags. The agent now has the detailed instructions in context and can run the bundled scripts.
## Test the skill
1. Start a session with an agent that has the `skills` capability enabled.
2. Write the skill files to `/.agents/skills//` in the session.
3. Send a message that matches the skill’s “When to Use” criteria.
4. Watch the event stream for an `activate_skill` tool call.
## Publish to the Skills Registry
The Skills Registry stores skills at the organization level. Registry skills persist across sessions and can be assigned to any agent as a capability with ID `skill:{uuid}`. When a session starts, the skill is mounted into `/.agents/skills/{name}/`, so the `skills` capability discovers it the same way as a workspace skill.
### From SKILL.md
If your skill is a single Markdown file, post it directly:
```bash
curl -X POST http://localhost:9300/api/v1/skills \
-H "Content-Type: application/json" \
-d '{
"skill_md": "---\nname: hello-world\ndescription: A simple greeting skill.\n---\n\n# Hello World\n\nGreet the user warmly."
}'
```
The response includes the skill ID:
```json
{ "id": "skill_550e8400-e29b-41d4-a716-446655440000", "name": "hello-world", ... }
```
### From a ZIP archive
For skills with bundled scripts, references, or assets:
```bash
curl -X POST http://localhost:9300/api/v1/skills/upload \
-F "file=@csv-analyzer.zip"
```
Archive layout:
```plaintext
csv-analyzer/
├── SKILL.md
├── scripts/analyze.py
└── references/REFERENCE.md
```
The top-level directory name is informational; the skill `name` comes from the SKILL.md front matter and must be unique per organization.
### Validate first
Validate without creating:
```bash
curl -X POST http://localhost:9300/api/v1/skills/validate \
-H "Content-Type: application/json" \
-d '{"skill_md": "---\nname: my-skill\ndescription: Does things.\n---\n\n# Instructions"}'
```
Response:
```json
{ "valid": true, "name": "my-skill", "description": "Does things.", "warnings": [] }
```
### Assign to an agent
Registry skills appear in the capability system as virtual capabilities with ID `skill:{uuid}`:
```bash
curl -X POST http://localhost:9300/api/v1/agents \
-H "Content-Type: application/json" \
-d '{
"name": "Analyst Agent",
"capabilities": [
{ "ref": "skill:550e8400-e29b-41d4-a716-446655440000" },
{ "ref": "session_file_system" }
]
}'
```
`session_file_system` is pulled in automatically as a dependency.
### Update or delete
```bash
# Update
curl -X PATCH http://localhost:9300/api/v1/skills/$SKILL_ID \
-H "Content-Type: application/json" \
-d '{"skill_md": "..."}'
# Delete
curl -X DELETE http://localhost:9300/api/v1/skills/$SKILL_ID
```
Deleting a skill hides it from capability listings. Agents that reference it via `ref: "skill:..."` will still resolve until you update them.
## See also
* [Agent Skills](https://everruns.com/docs/features/skills/): the concept, the registry API, and its security rules.
* [Agent Skills capability reference](https://everruns.com/docs/capabilities/agent-skills/): `list_skills` and `activate_skill`.
---
# Publish an Agent to Slack
> Add a Slack channel to an Agent, publish it, connect a Slack workspace, and verify the first message.
Source:
This guide deploys an Agent as a Slack bot through an Agent-owned channel. For Slack scopes, manual setup, and troubleshooting, see [Slack Integration](https://everruns.com/docs/capabilities/slack/).
## Prerequisites
* An active Agent.
* A public HTTPS Everruns origin configured through `PUBLIC_APP_URL`.
* Permission to install an app in a Slack workspace.
## Add the Channel
1. Open the Agent and select **Integrations**.
2. Select **Add channel**, then select **Slack**.
3. Choose a session strategy.
4. Leave the Slack credentials empty and select **Save channel**.
## Choose a session strategy
`session_strategy` controls how incoming Slack messages map to Everruns sessions:
| Strategy | Behaviour | Use when |
| ---------------------- | ------------------------------------ | --------------------------------------------------------------- |
| `per_thread` (default) | Each Slack thread is its own session | Support bots, Q\&A, each thread is a separate conversation |
| `per_channel` | One session per channel | Persistent channel assistant, context shared across the channel |
| `per_user` | One session per user | Personal assistant, each user has their own ongoing chat |
## Choose when the agent responds
The Slack endpoint editor shows a **Response policy** selector:
* **All messages** preserves the existing behavior and is the default.
* **Mentions only** responds to direct messages and explicit `@mentions`.
* **Relevant messages** also responds to clear requests within the agent’s purpose, including contextual thread follow-ups. Unrelated and uncertain messages stay silent.
Relevant messages requires a decision model. By default that is the deployment’s Decisions service: for Jev, set `UTILITY_TYPESAFE_API_KEY` and select `UTILITY_DECISION_DRIVER=typesafe`. An organization can use its own model instead: pick a **Default decision model** on the Models page and set **System decisions** to **This organization’s model**. The check then runs on your provider account. In a thread that already has a session it counts against that session’s budget and usage. A message that would start a new session is checked before any session exists, so it is not budget-checked.
Mentions and direct messages work without a classifier. A missing classifier or a failed decision leaves unmentioned messages silent, without posting an acknowledgement or running the agent. An organization that chose its own model stays silent when that model is missing or failing; it never falls back to the deployment’s model.
The Agent’s **Communication** setting controls what an accepted turn posts to Slack: a Direct Agent posts its replies, and an Explicit Agent posts only what it sends with `send_message`. See [Explicit communication](https://everruns.com/docs/features/explicit-communication/). Response policy controls whether that turn starts. Select **All messages** to restore the previous behavior.
## Publish and Connect
Your organization must have a Slack workspace connected in **Settings** > **Slack workspaces** first; an administrator does this once. See [Connect a Slack Workspace](https://everruns.com/docs/capabilities/slack/#connect-a-slack-workspace).
If your deployment connects workspaces through a Slack manager app, complete its consent in the same browser that started the connection. Each attempt expires after thirty minutes and can finish once. Separate tabs keep independent attempts. After cancellation, expiry, or an exchange failure, start a new connection from **Settings** > **Slack workspaces**.
1. Select **Publish** in the channel editor.
2. If more than one workspace is connected, choose which one.
3. Select **Add to Slack**.
4. Approve Slack’s consent screen. The workspace is already selected.
5. If one-click setup is unavailable, return to **Integrations**, expand the channel, and select **Create Slack app**. Copy the resulting signing secret and bot token back through **Configure**.
Publish first because Slack verifies the manifest’s channel Request URL when it creates the Slack app. New installs use `/v1/channels/{channel_id}/slack/events`.
The Slack app uses the agent’s avatar as its icon. With one-click setup, Everruns sets the icon when it creates the app, again once the app is installed, and whenever you change the avatar in **Branding**. When this deployment’s API is reachable on the public internet, Slack fetches that image; otherwise Everruns uploads the 512 px PNG. A Slack app created from the manifest by hand keeps Slack’s default icon: upload the avatar’s 512 px PNG (`/v1/avatars/{avatar_id}/square-512.png`) under **Basic Information** in Slack.
With one-click setup, later changes to the agent’s display name and description also update its Slack app automatically. If no display name is set, Slack uses the agent’s name. Updates preserve the app’s permissions and connection settings. A manually created Slack app needs its name and description updated in Slack.
## Verify
1. In Slack, enter `/invite @botname` in a channel.
2. Mention the bot.
3. Return to the Agent’s **Integrations** tab and expand the Slack channel.
4. Confirm that the checklist records the first message.
To stop new Slack messages without deleting the configuration, select **Unpublish** on this channel. Existing sessions remain available.
## Remove an Agent from Slack
Select **Delete** in the Slack channel editor to remove that connection. For an app created through **Add to Slack**, this also deletes its Slack app, bot, and workspace installation. The confirmation explains this before you continue. Removing managed Slack apps requires permission to delete Agent integrations, including when archiving an Agent.
Archiving or deleting the Agent removes all of its Slack apps created by Everruns and stops new Slack messages. Archiving keeps the Agent definition, but its Slack connections become disabled and managed installation credentials are cleared. Restoring the Agent does not restore its Slack apps; install and publish its Slack channels again.
Manually configured Slack apps must be removed manually in Slack. The confirmation identifies these connections; Everruns stops receiving new messages but cannot remove their installations automatically.
If managed app removal fails, the action reports an error instead of completing. Check the connected workspace in **Settings** > **Slack workspaces** and retry. If the Agent has multiple Slack apps, some may already have been removed before another fails; retrying completes the remaining cleanup.
## Resolve installation issues
Open **Settings** > **Health** to review pending Slack permission or credential issues. The same warning appears beside the endpoint and under **Action required** in notifications. Reading or snoozing an announcement leaves the issue open.
For an app Everruns created, select **Reconnect Slack** and approve the additional permissions. For a manually configured app, add the listed bot scopes in Slack, reinstall the existing app, and save the refreshed bot token in the endpoint editor. Your Slack administrator may need to approve the change. Use **Check again** to verify the installation; unavailable or stale permission evidence keeps the issue open until a current check succeeds.
## See also
* [Slack Integration](https://everruns.com/docs/capabilities/slack/), including scopes, manual setup, and troubleshooting.
* [Change History](https://everruns.com/docs/features/change-history/), to see and restore earlier agent configurations.
---
# Set up review and security agents
> Adopt the PR Reviewer and Security Scanner templates to review every pull request with inline comments and scan a repository for vulnerabilities on a schedule.
Source:
Two agent templates turn the [GitHub](https://everruns.com/docs/capabilities/github/) capability into working automation in a few clicks:
* **PR Reviewer** reviews every pull request on a repository with inline comments and keeps one summary comment current. After a new push it adds only what is new.
* **Security Scanner** clones a repository into a cloud sandbox every week, looks for vulnerabilities, and files each confirmed finding as a GitHub issue, updating the same issue on later scans instead of opening duplicates. If you allow it, it also opens draft pull requests for small, high-confidence fixes.
Both run on whichever model the agent uses. Each gets its own GitHub App, so it reads code and posts as its own bot, with no token to create or paste.
## Prerequisites
* A public HTTPS Everruns origin for the PR Reviewer. GitHub only delivers webhooks to a public URL. The Security Scanner runs on a schedule and does not need one.
* Permission to create a GitHub App on your account or organization, and to install it on the repository.
* For the Security Scanner, a [Daytona](https://everruns.com/docs/capabilities/daytona/) API key.
## Adopt a template
1. Open **Agents** and find **PR Reviewer** or **Security Scanner** in the examples. Templates carry a **guided setup** badge.
2. Select **Import**. The agent is created and the setup page opens.
## Connect GitHub
1. On the setup page, select **Connect GitHub**. For a repository owned by a GitHub organization, first enter the organization under **GitHub organization**: the App is private, so it can only be installed on the account that owns it.
2. GitHub shows the App it is about to create for this agent. Select **Create GitHub App**, then choose where to install it and which repositories it can reach, and select **Install**.
You land back on the setup page with GitHub marked as connected.
## Pick a repository and settings
1. Pick the repository. When the App is installed on exactly one repository it is picked for you.
2. Security Scanner only:
* **File findings on private repositories only** is on by default. On a public repository an issue would disclose the vulnerability, so with this on the scanner reports the finding only in its run instead.
* **Open fix pull requests** is off by default. Turn it on to let the scanner open draft pull requests. Pushing a branch also needs **Contents: write** on the agent’s GitHub App, which agent Apps do not request: grant it under the App’s **Permissions & events** on GitHub and accept the change on the installation. Without it the scanner says the push was refused and carries on.
* Add the Daytona API key to the agent’s service account (the setup page links to it), since scheduled runs act as the agent.
3. Select **Create trigger**.
The PR Reviewer gets a GitHub trigger for pull requests opened, reopened, pushed to, or marked ready for review, skipping drafts, with one session per pull request. The Security Scanner gets a schedule trigger every Monday at 06:00 UTC. Both triggers can be edited afterwards from the agent’s **Integrations** tab, like any other trigger.
## Verify
PR Reviewer:
1. Open a pull request in the repository.
2. The trigger’s history shows a `dispatched` delivery and a session starts.
3. The agent submits a review with inline comments, or none when it finds nothing, and posts a summary comment. Push another commit: the summary is edited, and only new findings are added as comments.
Security Scanner:
1. On the agent’s **Integrations** tab, select **Run now** on the trigger instead of waiting for Monday.
2. The session clones the repository, scans it, and ends with a report. Findings appear as issues labelled `security`. Run it again: existing issues are updated, not duplicated, and an issue you closed stays closed.
## What the agents cannot do
* The reviewer cannot approve a pull request. It comments or requests changes.
* Pull request text, diffs, code and issues are written by others and treated as untrusted. The agents are told to ignore instructions in them, and the settings above are enforced by the tools rather than by the prompt.
* Without **Open fix pull requests** the scanner has no tool to open a pull request at all.
---
# Share knowledge with Open Knowledge Format (OKF)
> Import and export Knowledge Bases as Open Knowledge Format bundles, portable markdown-with-frontmatter that any OKF consumer or agent can read, managed like code.
Source:
[Open Knowledge Format (OKF)](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf) is a vendor-neutral interchange format for the metadata and curated context around your data: a bundle is just a directory of markdown files with YAML frontmatter, shippable as a tarball or git repo. everruns Knowledge Bases speak OKF on both ends, so you can keep curated knowledge as code, ingest bundles produced elsewhere, and hand your agents’ working set to any OKF consumer.
This guide covers importing a bundle into a Knowledge Base, the OKF↔entry mapping, and exporting a bundle back out.
## What a bundle looks like
```plaintext
sales/
├── index.md # optional navigation (reserved file)
├── tables/
│ └── orders.md # one concept document per file
└── metrics/
└── revenue.md
```
A concept document is frontmatter plus markdown body:
```markdown
---
type: BigQuery Table
title: Orders
description: One row per completed customer order.
resource: https://console.cloud.google.com/bigquery?p=acme&d=sales&t=orders
tags: [sales, revenue]
---
# Schema
| Column | Type | Description |
|--------|------|-------------|
| `order_id` | STRING | Globally unique order identifier. |
```
Only `type` is required. `index.md` and `log.md` are reserved navigation/history files and never become entries.
## Import a bundle
`POST /v1/knowledge-bases/{kb_id}/okf_import` accepts either inline files or a base64-encoded `.tar.gz` bundle. It is **idempotent**: re-importing an updated bundle converges the Knowledge Base to the bundle’s state without creating duplicates.
Create a Knowledge Base, then import inline files:
```bash
KB_ID=$(curl -s -X POST "http://localhost:9300/api/v1/knowledge-bases" \
-H "Content-Type: application/json" \
-d '{"name": "Sales Knowledge"}' | jq -r .id)
curl -s -X POST "http://localhost:9300/api/v1/knowledge-bases/$KB_ID/okf_import" \
-H "Content-Type: application/json" \
-d '{
"files": [
{
"path": "tables/orders.md",
"content": "---\ntype: BigQuery Table\ntitle: Orders\nresource: https://example.com/orders\ntags: [sales]\n---\n# Schema\nOne row per order.\n"
}
]
}'
```
Or import a tarball you already have:
```bash
curl -s -X POST "http://localhost:9300/api/v1/knowledge-bases/$KB_ID/okf_import" \
-H "Content-Type: application/json" \
-d "{\"bundle_base64\": \"$(base64 -w0 sales-bundle.tar.gz)\"}"
```
The response summarizes the run:
```json
{ "created": 1, "updated": 0, "skipped": 0, "pruned": 0, "warnings": [] }
```
### Keeping a Knowledge Base in sync
Re-run the same import whenever the bundle changes, matched entries update in place (keyed on `resource`, or the bundle path when there is no `resource`). To make the Knowledge Base a strict mirror of the bundle, pass `"prune": true`; entries that came from a previous import and are absent from the new bundle are removed.
```bash
curl -s -X POST "http://localhost:9300/api/v1/knowledge-bases/$KB_ID/okf_import" \
-H "Content-Type: application/json" \
-d "{\"prune\": true, \"bundle_base64\": \"$(base64 -w0 sales-bundle.tar.gz)\"}"
```
Import is tolerant by design (per OKF conformance): reserved files are skipped, and malformed documents are reported in `warnings` rather than failing the whole bundle.
## How OKF maps onto entries
| OKF frontmatter | Knowledge entry |
| --------------- | ----------------------------------------------------------------------------------------------------- |
| `type` | `kind` (`note`/`table`/`business`/`query`/`runbook`); the raw `type` is preserved for faithful export |
| `title` | `title` (falls back to the filename) |
| `description` | folded into the body lead |
| `resource` | `resource` |
| `tags` | `tags` (lowercased) |
| markdown body | `body` |
`type` is matched case-insensitively: anything containing *table/dataset/view* → `table`, *metric/business/kpi/definition* → `business`, *query/sql* → `query`, *playbook/runbook/procedure* → `runbook`, otherwise `note`.
## Export a bundle
`GET /v1/knowledge-bases/{kb_id}/okf_export` streams a conformant OKF bundle as a gzipped tarball, including a root `index.md` that declares the format version.
```bash
curl -s "http://localhost:9300/api/v1/knowledge-bases/$KB_ID/okf_export" \
-o sales-bundle.tar.gz
tar tzf sales-bundle.tar.gz
```
Export reconstructs each entry’s frontmatter from the preserved raw `type` (or a default for its `kind`), its `resource`, tags, and timestamp, under a `kind`-derived directory. Export → import into a fresh Knowledge Base reproduces the same entries.
## Agents read OKF too
The `data_knowledge` capability mounts a readonly `/knowledge/` scaffold shaped as an OKF bundle (frontmatter + `index.md` navigation). Agents on the **Data Analyst** harness read it as ground truth before writing SQL. Import an OKF bundle into a Knowledge Base, and that curated context is available to your agents and portable to any other OKF consumer.
See also: [Data Analyst harness](https://everruns.com/docs/built-ins/harnesses/data-analyst/).
---
# Stream events
> Consume a session's SSE event stream from the Python SDK or any HTTP client, with reconnection via since_id, heartbeats, and event filtering.
Source:
Every session publishes its events as a Server-Sent Events (SSE) stream. Use the [Python SDK](#with-the-python-sdk) when you can: it handles reconnection, heartbeat-based stale detection, backoff, and resumption with `since_id`. Use the [raw SSE protocol](#with-raw-sse) from any other client, such as a non-Python service, a browser, or `curl`.
## With the Python SDK
`client.events.stream(session_id)` returns an async iterator over typed events.
### Basic stream
```python
async for event in client.events.stream(session.id):
if event.type == "output.message.delta":
print(event.data.get("delta", ""), end="", flush=True)
elif event.type == "turn.completed":
print()
break
elif event.type == "turn.failed":
print(f"\n[failed: {event.data.get('error')}]")
break
```
### Tool visibility
To show what the agent is doing while it works, listen for `tool.started` and `tool.completed`:
```python
async for event in client.events.stream(session.id):
if event.type == "tool.started":
tool_call = event.data.get("tool_call", {})
print(f" [tool] {tool_call.get('name')}")
elif event.type == "tool.completed":
status = "ok" if event.data.get("success") else "error"
print(f" [tool] {event.data.get('tool_name')}: {status}")
elif event.type == "turn.completed":
break
```
### Get the full final message
`output.message.completed` carries the complete final message after streaming finishes:
```python
async for event in client.events.stream(session.id):
if event.type == "output.message.completed":
message = event.data.get("message", {})
for part in message.get("content", []):
if part.get("type") == "text":
print(part["text"])
elif event.type == "turn.completed":
break
```
### What the SDK handles for you
* **Reconnection.** The control plane cycles SSE connections every 5 minutes; the SDK reconnects transparently using `since_id`.
* **Stale detection.** The server sends a heartbeat every 30s; the SDK treats >45s of silence as a dead connection and reconnects.
* **Backoff.** Network errors trigger exponential backoff with jitter.
* **Typing.** Each event has `.type` and `.data` attributes parsed from SSE.
### Resuming with `since_id`
While a stream is open the SDK manages reconnection internally. You only need `since_id` when restarting your application and resuming from a previously recorded event ID:
```python
# Persisted somewhere — file, DB, etc.
last_seen_id = load_cursor()
async for event in client.events.stream(session.id, since_id=last_seen_id):
handle(event)
save_cursor(event.id) # so the next restart can resume from here
```
Inside the loop the SDK already remembers the last ID it yielded and reconnects with it on transient failures, `save_cursor` here is for *application restart* recovery, not per-iteration SDK state.
## With raw SSE
### Subscribe
```bash
curl -N "https://your-host/api/v1/sessions/$SESSION_ID/sse" \
-H "Authorization: Bearer $EVERRUNS_API_KEY"
```
Each event arrives as:
```plaintext
event: turn.completed
id: event_01933b5a00007000800000000000001
data: {"id":"event_...","type":"turn.completed","data":{...}}
```
(Blank line terminates each event, per the SSE spec.)
### Resume after disconnect
Pass `since_id` to pick up where you left off:

```bash
curl -N "https://your-host/api/v1/sessions/$SESSION_ID/sse?since_id=event_..." \
-H "Authorization: Bearer $EVERRUNS_API_KEY"
```
Event IDs are UUIDv7 and the server orders them by an atomic per-session sequence number. Resumption is gap-free and duplicate-free.
### Heartbeats
The server sends a heartbeat every 30 seconds as a comment line:
```plaintext
: heartbeat
```
Comments are invisible to SSE event parsers, they don’t appear as events. Their only purpose is to keep the TCP connection alive and let your client distinguish “idle” from “dead.”
**Client requirement:** treat the connection as stale if no data (event or heartbeat) arrives within 45 seconds. Reconnect with the last received event ID.
### Connection cycling
To avoid stale connections through proxies, the server gracefully cycles SSE connections every 5 minutes. Before closing, it sends:
```plaintext
event: disconnecting
data: {"reason":"connection_cycle","retry_ms":100}
```
Clients should reconnect immediately using `since_id` of the last event received. No events are dropped during the transition.
### Browser EventSource
```javascript
function connect(sessionId, lastEventId) {
const url = new URL(`/api/v1/sessions/${sessionId}/sse`, API_BASE);
if (lastEventId) url.searchParams.set("since_id", lastEventId);
const es = new EventSource(url, { withCredentials: true });
es.addEventListener("connected", () => console.log("SSE connected"));
es.addEventListener("disconnecting", (e) => {
const { retry_ms } = JSON.parse(e.data);
es.close();
setTimeout(() => connect(sessionId, lastEventId), retry_ms);
});
["input.message", "output.message.delta", "turn.completed"].forEach((t) => {
es.addEventListener(t, (e) => {
const data = JSON.parse(e.data);
lastEventId = data.id;
// handle event...
});
});
es.onerror = () => {
es.close();
setTimeout(() => connect(sessionId, lastEventId), 2000);
};
}
```
The native `EventSource` API uses the `retry:` field that every event includes (100ms during active streaming, up to 500ms while idle). You don’t need to set retry yourself.
### Poll as a fallback
If your environment can’t hold long-lived connections (some serverless runtimes), poll instead:
```bash
curl "https://your-host/api/v1/sessions/$SESSION_ID/events?since_id=$LAST_ID" \
-H "Authorization: Bearer $EVERRUNS_API_KEY"
```
The same `since_id` resumption works; latency increases by the polling interval.
## See also
* [Event Reference](https://everruns.com/docs/event-reference/): every event type and payload.
* [Events as the primary store](https://everruns.com/docs/explanation/events/): why the protocol is shaped this way.
---
# Summarize GitHub Pull Requests
> Connect GitHub to an Agent, subscribe it to pull request events, and have it keep one summary comment on every pull request up to date.
Source:
This guide builds an Agent that comments a summary on every pull request in the repositories you choose, and updates that comment when new commits are pushed. Setup is all clicks: the Agent gets its own GitHub App, so there is no token to create or paste.
## Prerequisites
* An active Agent.
* A public HTTPS Everruns origin. GitHub only delivers webhooks to a public URL; on a local origin the App is still created, and its tools work, but no events arrive.
* Permission to create a GitHub App on your account or organization, and to install it on the repositories you want summarized.
## Give the Agent the GitHub tools
1. Open the Agent and select **Edit**.
2. Add the [GitHub](https://everruns.com/docs/capabilities/github/) capability. It gives the Agent `get_github_pull_request`, `get_github_pull_request_diff` and `upsert_github_comment`.
3. Save.
## Connect GitHub
1. Open the Agent and select **Integrations**.
2. In the **GitHub** section, between **Channels** and **Triggers**, open **Create this agent’s own GitHub App** and choose where to create the App: **Personal account**, an organization your Everruns organization already connected to GitHub, or **Another organization…** to type one. The App is private, so it can only be installed on the account that owns it.
3. GitHub shows the App it is about to create for this Agent, named after the Agent (with a short random suffix when another App already has that name). Select **Create GitHub App**.
4. Choose where to install it and which repositories it can reach, then select **Install**.
You land back on **Integrations** with the **GitHub** section showing the Agent’s bot login (for example `pr-summarizer[bot]`), the account and the repositories. The App is this Agent’s alone: its comments appear as the App’s bot. Its GitHub tools, its trigger, and any MCP server set to use the Agent’s `github` connection all use this one installation.
### Set the avatar
GitHub offers no way to set an App’s logo for it, so the **GitHub** section shows a **Set the avatar** step. Select **Download avatar** to save the Agent’s avatar (shown when the Agent has one), then **Open App settings** and upload it as the logo on GitHub.
### Use the Everruns app instead
When an org admin has linked a GitHub account to the shared Everruns GitHub App, the **GitHub** section also offers **Use the Everruns app**. Pick a linked account and the Agent acts as the shared App’s bot on that installation, with no App of its own to create. Its comments then appear as the shared bot, not as the Agent, and there is no avatar step.
To link an account, an org admin opens **Settings > Organization**, selects **Link** in the **Everruns GitHub App** group, and installs the App on GitHub. **Unlink** there removes the account from every Agent that chose it; the App stays installed on GitHub.
## Add a pull request trigger
1. In the **GitHub** section, select **Add pull request trigger**.
2. Keep the default events (opened, reopened, new commits pushed, ready for review), or change them.
3. Leave repositories empty to cover every repository the App is installed on, or pick some.
4. Keep **One session per pull request**, so every push to a pull request continues the same conversation.
5. Review the message. The default asks the Agent to read the pull request and its diff and to post or update its summary. Values such as `{{github.repository}}`, `{{github.number}}`, `{{github.title}}` and `{{github.url}}` are filled in from the event.
6. Select **Save**.
## Verify
1. Open a pull request in one of the repositories.
2. Within a few seconds the trigger’s history on the Agent shows a `dispatched` delivery, and a session starts.
3. The Agent posts a comment on the pull request. Push another commit: the same comment is edited instead of a new one being added.
Deliveries that were filtered out (for example, a repository outside the trigger’s list) or deduplicated (a GitHub redelivery) are listed with the reason, so you can see why an event did not start a run.
## Disconnect
Select **Disconnect** in the **GitHub** section. This uninstalls the App from GitHub and removes the Agent’s GitHub connection. For an Agent using the Everruns app, it only removes the Agent’s choice; the account stays linked to the organization. The App itself stays on your GitHub account, so connecting again reuses it; delete it from GitHub’s **Developer settings** if you no longer want it.
If the App was created but never installed, for example on your personal account when the repositories belong to an organization, select **Start over** in the **GitHub** section’s actions menu instead. Everruns forgets that App, so the next **Connect GitHub** creates a new one.
## From the CLI
The same connection is available from the `everruns` CLI, and to agents through the Everruns MCP server:
```bash
# Is the App created, installed, and on which account?
everruns agents github get --agent-id agent_01h9
# The install link, once the App exists. Creating the App itself needs the
# web UI, because GitHub confirms it in the browser.
everruns agents github connect --agent-id agent_01h9 --reason 'Add the API repo'
# Uninstall and remove the connection, or start over with an App that was
# never installed.
everruns agents github disconnect --agent-id agent_01h9 --reason 'Reviewer retired'
```
---
# Use AGENTS.md for project instructions
> Inject project-level context, coding style, build commands, architecture notes, into an agent's leading user message by enabling the AGENTS.md capability.
Source:
`AGENTS.md` is an emerging open standard for providing project-level instructions to AI agents, backed by OpenAI, Google, Cursor, Sourcegraph, and others. Everruns ships it as the default file for its built-in agent instructions capability, which re-reads configured files on every turn.
## Enable the capability
```bash
curl -X PATCH http://localhost:9300/api/v1/agents/$AGENT_ID \
-H "Content-Type: application/json" \
-d '{
"capabilities": [
{ "ref": "agent_instructions" },
{ "ref": "session_file_system" }
]
}'
```
`session_file_system` isn’t required, but pairing it with `agent_instructions` lets the agent edit `AGENTS.md` itself.
To also read another instruction file, configure `files` on the capability:
```bash
curl -X PATCH http://localhost:9300/api/v1/agents/$AGENT_ID \
-H "Content-Type: application/json" \
-d '{
"capabilities": [
{
"ref": "agent_instructions",
"config": {
"files": ["AGENTS.md", "CLAUDE.md"]
}
},
{ "ref": "session_file_system" }
]
}'
```
## Write the file
Drop a plain Markdown file at `AGENTS.md` in the session workspace root for repo-wide rules. Add nested files (for example `docs/AGENTS.md`) for subdirectory-scoped rules — deeper files override shallower ones on conflict, and sibling subtrees never see each other’s files:
```markdown
## Project: Acme API
REST API built with Rust + Axum. PostgreSQL for storage.
## Style
- snake_case for variables and functions
- PascalCase for types
- Keep functions under 50 lines
## Build & Test
cargo build
cargo test --all-features
cargo clippy -- -D warnings
## Architecture
- `src/api/` — HTTP handlers
- `src/domain/` — Business logic
- `src/db/` — Database queries
## Commits
Use conventional commits: `feat(scope): description`
```
There are no required sections. Write whatever a new contributor would need to know.
## How it lands in the prompt
Every turn the model sees, top-to-bottom:
1. **System prompt**: harness safety instructions, tool guidance, role.
2. **Conversation context**: your resolved `AGENTS.md` hierarchy, broadest scope first.
3. **Conversation history and your message**.
Project files never enter the system prompt — system instructions always win on conflict, and your explicit message wins over project files.
## Limits and dynamics
* Content is capped at **32 KiB** (32,768 bytes) per file (excess truncated with a warning), plus a **128 KiB total budget** per turn across the hierarchy.
* Configured files are resolved from the filesystem root down to the working directory on every turn. Edits during a session apply on the next turn, no restart needed.
* If a configured file doesn’t exist, the agent operates normally without it.
## Other tools’ instruction files
Everruns reads `AGENTS.md` by default at every hierarchy level. Add other file names to `files` when an agent should also resolve `CLAUDE.md`, `.cursorrules`, or `.github/copilot-instructions.md` per level.
## See also
* [AGENTS.md capability reference](https://everruns.com/docs/capabilities/agent-instructions/)
* [Equip an agent with tools](https://everruns.com/docs/how-to/equip-agents-with-tools/), adding capabilities in general.
---
# Integrations
> An index of the external services Everruns agents connect to: cloud sandboxes, browsers, search, messaging and discovery, with setup and tool references.
Source:
This page lists the external vendors Everruns agents connect to. Tool integrations surface as [capabilities](https://everruns.com/docs/features/capabilities/), so a vendor that has a capability page (Daytona, E2B, Browserless, Slack) is documented there in full: setup, tools, and security. The remaining vendors have their own page in this section. Messaging integrations use [endpoints](https://everruns.com/docs/features/channels/) owned by the Agent.
## Sandboxes & execution
Give agents an isolated environment to run code, edit files, and persist state.
| Integration | What it provides |
| ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| [Daytona](https://everruns.com/docs/capabilities/daytona/) | Cloud sandbox environments via the Daytona REST API |
| [E2B](https://everruns.com/docs/capabilities/e2b/) | Cloud sandboxes via the E2B management + runtime APIs (bring your own key) |
| [Container Sandbox](https://everruns.com/docs/capabilities/container-sandbox/) | Self-hosted container sandboxes via Docker Engine, no external SaaS |
| [Modal](https://everruns.com/docs/integrations/modal/) | Full Linux VM or gVisor sandboxes with filesystem snapshots and public tunnels. Experimental, organisation opt-in |
| [Sprites](https://everruns.com/docs/integrations/sprites/) | Persistent Firecracker microVMs with checkpoints and HTTP services. Experimental, off by default |
| [Cursor](https://everruns.com/docs/integrations/cursor/) | Launch and manage asynchronous Cursor Cloud coding agents |
## Browser & web
| Integration | What it provides |
| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [Browserless](https://everruns.com/docs/capabilities/browserless/) | Cloud browser automation, screenshots, DOM, scraping, multi-step flows |
| [Brave Search](https://everruns.com/docs/integrations/brave-search/) | Web search via the Brave Search API |
| [Cloudflare Web Search](https://everruns.com/docs/integrations/cloudflare-web-search/) | Web search through Ceramic, Exa or Linkup via the Cloudflare Web Search API |
| [DuckDuckGo](https://everruns.com/docs/integrations/duckduckgo/) | Instant answers via the DuckDuckGo API |
| [Parallel](https://everruns.com/docs/integrations/parallel/) | Web search, extract, and task APIs (free and paid tiers) |
## Reasoning & judgment
| Integration | What it provides |
| ------------------------------------------------------------ | -------------------------------------------------------------------- |
| [TypeSafe](https://everruns.com/docs/integrations/typesafe/) | Typed judgments: probabilities, single-choice routing, graded scores |
Agents on the [OpenRouter provider](https://everruns.com/docs/providers/openrouter/) can also get web reach without a separate integration via the [OpenRouter Server Tools capability](https://everruns.com/docs/capabilities/openrouter-server-tools/) (`web_search`, `web_fetch`), which OpenRouter executes server-side.
## Messaging channels
| Integration | What it provides |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| [Slack](https://everruns.com/docs/integrations/slack/) | Build and publish an Agent for Slack, with conversations, tools, approvals, and a dedicated agent pane |
## Agent protocols
| Integration | What it provides |
| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| [A2A](https://everruns.com/docs/features/a2a/) | Let other agents call an Agent over A2A 1.0, and delegate work to external A2A agents |
| [AgentID Sign-In](https://everruns.com/docs/integrations/agentid/) | Let an Agent sign in to apps that support AgentID with its own AgentMail inbox. Experimental |
## Credentials
| Guide | What it provides |
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| [Secure MCP Credentials](https://everruns.com/docs/integrations/mcp-credentials/) | Write-only Agent credential bindings for secret MCP tool parameters |
## Discovery
| Integration | What it provides |
| -------------------------------------------------- | ----------------------------------------------------------------------- |
| [ARD](https://everruns.com/docs/integrations/ard/) | Client-side discovery of external MCP servers and A2A agents at runtime |
## Model providers
Integrations connect agents to tools and services. **[Providers](https://everruns.com/docs/providers/)** connect Everruns to the AI model vendors that run your agents, OpenAI, Anthropic, Google Gemini, AWS Bedrock, OpenRouter, and more. See the [Providers overview](https://everruns.com/docs/providers/) to configure one.
---
# AgentID Sign-In
> Let an agent sign in to apps that support AgentID as itself, using an AgentMail inbox connected to the agent's service account.
Source:
> **Status:** Experimental. No feature flag: adding the capability and an AgentMail connection is the opt-in.
[AgentID](https://www.agentid.com) is a sign-in for AI agents, run by AgentMail. An agent’s AgentID is an AgentMail inbox. When an app that supports AgentID asks an agent to sign in, it shows a waiting page with a short-lived auth token; the agent’s inbox approves it, and the app sees the agent signed in.
The **AgentID sign-in** capability gives an Everruns agent the `agentid_authorize` tool, which does that approval with the agent’s own inbox.
This page covers agents signing in to other apps. To let agents sign in to your own channels, see [Channels: Sign in with AgentID](https://everruns.com/docs/features/channels/#sign-in-with-agentid).
## Setup
1. Create an inbox for the agent in AgentMail, register it with AgentID, and create an AgentMail API key.
2. Open the agent, then its **service account** connections, and connect **AgentMail** with the API key and the inbox address (for example `research@acme.agentmail.to`).
3. Add the **\[Experimental] AgentID sign-in** capability to the agent.
The connection belongs to the agent. People chatting with the agent never get it, and the tool never uses their own connections or an administrator’s.
## Tool
`agentid_authorize` takes one argument:
| Parameter | Description |
| ------------ | -------------------------------------------------- |
| `auth_token` | The auth token from the app’s AgentID waiting page |
It asks AgentMail to approve that sign-in for the agent’s inbox, accepting AgentID’s disclosure of the agent’s identity to the app. On success it returns `authorized: true`, the inbox, and AgentMail’s `api_key_id` and `instructions`. If AgentMail refuses (for example the agent has reached its app limit, or the token has expired), the tool returns AgentMail’s reason.
The tool sends nothing else to AgentMail: it does not send or read mail.
## Security
* The AgentMail key is read only from the agent’s service account. With no AgentMail connection there, the tool refuses.
* Requests go only to `https://api.agentmail.to`, through the agent’s network access policy.
* Approving a sign-in shares the agent’s AgentID with the app. Only pass a token that the app showed the agent.
---
# Agentic Resource Discovery (ARD)
> Discover and attach external MCP servers and A2A agents at runtime via the ARD protocol, with registry configuration and trust gating.
Source:
Everruns integrates with [Agentic Resource Discovery (ARD)](https://agenticresourcediscovery.org/spec/) as a **client**: a running agent can search ARD registries for capabilities it was not pre-provisioned with, MCP servers and A2A agents, and attach them to its session on the fly. Newly attached MCP tools appear on the next turn; attached A2A agents become `spawn_agent` targets.
ARD is the discovery layer *above* `tool_search`. `tool_search` defers schemas for tools already attached to a session; ARD decides **which** MCP server / A2A agent to attach in the first place.
> **Status:** Experimental. No feature flag: adding the capability and an ARD registry connection is the opt-in.
## What You Get
* **Runtime discovery**: `discover_resources` runs a semantic search against a configured registry, outside the model context (like `tool_search`).
* **Dynamic attachment**: `attach_resource` materializes a result as a session-scoped MCP server or external A2A agent, reusing existing Everruns config-overlay machinery. The agent loop is unchanged.
* **Safety by construction**: registry allowlist, `trustManifest` verification, SSRF-safe URL validation, and a per-session attachment cap.
## Quick Start
### 1. Enable the capability on an agent
Add the `resource_discovery` capability and point it at one or more registries. The model selects a registry by `id`, it can never supply a raw URL.
```json
{
"registries": [
{ "id": "public", "url": "https://agenticresourcediscovery.org/api/v1", "federation": "none" }
],
"require_trust": [],
"allow_attach_types": ["application/mcp-server+json", "application/a2a-agent-card+json"],
"max_attachments": 5,
"allow_local_urls": false
}
```
The ready-made **Capability Scout** seed agent (Dev) ships with this wired to the public reference registry plus `tool_search`.
### 2. (Optional) Connect a registry token
For registries that require authentication, connect **Agentic Resource Discovery** under **Settings → Connections** (provider `ard`) and paste a bearer token, or set the `ARD_REGISTRY_TOKEN` session secret. Public anonymous-read registries need no token.
### 3. Discover and attach
From a session, ask for something the agent can’t yet do. It will:
1. `discover_resources({ text: "..." })`, search the registry and get ranked candidates, each with a `urn`.
2. `attach_resource({ urn })`, verify trust, validate the URL, and attach.
3. Use the new capability on the next turn, MCP tools appear prefixed `mcp___*` (surfaced through `tool_search`); A2A agents are reachable via `spawn_agent`.
4. `list_attached_resources()`, see what’s attached this session.
## Tools
| Tool | Description |
| ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `discover_resources({ text, filter?, registry_id? })` | Search a configured registry (`POST /search`). Returns ranked `{ urn, displayName, type, score, source, description, attachable }`. `registry_id` is optional when one registry is configured. |
| `attach_resource({ urn })` | Resolve a discovered entry, verify `trustManifest` + `require_trust`, SSRF-validate the URL, and attach it. Idempotent per URN. |
| `list_attached_resources()` | List attachments for the session (visibility / audit). |
## Attachment Lifecycle
* Attachments are **session-scoped** and torn down when the session ends.
* MCP entries become a session MCP server, the same session-scoped `mcpServers` record a server added for [this conversation only](https://everruns.com/docs/capabilities/user-mcp-servers/#this-conversation-only) uses. Their tools are then subject to `tool_search` deferral, in both the hosted and the in-process worker.
* A2A entries merge into the session’s A2A delegation config and are driven through the existing `spawn_agent` / `wait_task` / `message_task` tools.
* Re-attaching the same URN is a no-op (reports `already_attached`).
## Security
* **Registry allowlist**: only configured registries are queryable; the model picks a `registry_id`, never a URL.
* **Trust gate**: an entry’s `trustManifest` identity domain must match its URN publisher, and any `require_trust` attestations (e.g. `["soc2"]`) must be present, before it can be attached.
* **SSRF protection**: every resolved artifact and endpoint URL is validated (DNS-pinned; loopback, private, link-local, and cloud-metadata addresses are blocked). `allow_local_urls` relaxes this for local testing only.
* **Attachment cap**: `max_attachments` bounds how many capabilities a single session can attach, limiting prompt-injection-driven attach storms.
* **Untrusted data**: all registry-returned text is treated as untrusted external input.
See the co-located [`SPEC.md`](https://github.com/everruns/everruns/blob/main/crates/ard/SPEC.md) for architecture and the full security review.
---
# Brave Search
> Web search through Brave: ranked results, freshness filters, pagination, source attribution, and safe search. Requires a Brave API key.
Source:
Everruns integrates with [Brave Search](https://brave.com/search/api/) to give agents full web search capabilities. Agents can search the web and get relevant results including titles, URLs, and descriptions, perfect for research, fact-checking, and finding current information.
Provider-neutral search
For most agents, use [Web Search](https://everruns.com/docs/capabilities/web-search/) instead: one `web_search` tool that can search with Brave, the model’s own search or free search. An organization admin connects a Brave Search account once under **Registers > Web search**, and every agent can use it. This capability stays for agents built around one Brave key.
## What You Get
* **Full Web Search**: Query the web and get ranked results with titles, URLs, and descriptions
* **Freshness Filters**: Filter results by time (past day, week, month, year)
* **Pagination**: Navigate through large result sets
* **Source Attribution**: Every result includes a URL for citation and verification
* **Free Tier**: Brave Search offers a free plan with 2,000 queries/month
## Quick Start
### 1. Get Your API Key
1. Go to [Brave Search API](https://brave.com/search/api/)
2. Sign up for a **free** plan (2,000 queries/month)
3. Copy your **API Key** from the dashboard
### 2. Connect in Everruns
1. Go to **Settings** > **Connections**
2. Find **Brave Search** in the available providers
3. Click **Connect** and paste your API key
Once connected, the Brave Search capability is automatically available in agent sessions.
### 3. Use in Sessions
Agents with the Brave Search capability can use this tool:
| Tool | Description |
| ------------------ | ------------------------------------------ |
| `brave_web_search` | Search the web and return relevant results |
### Parameters
| Parameter | Type | Required | Description |
| ----------- | ------- | -------- | ----------------------------------------------------------------------------------- |
| `query` | string | Yes | Search query |
| `count` | integer | No | Number of results (1-20, default: 10) |
| `offset` | integer | No | Pagination offset |
| `freshness` | string | No | Time filter: `pd` (past day), `pw` (past week), `pm` (past month), `py` (past year) |
### Response Fields
The tool returns a JSON object with:
| Field | Description |
| --------- | -------------------------- |
| `query` | The original query |
| `results` | Array of result objects |
| `count` | Number of results returned |
Each result object contains:
| Field | Description |
| ------------- | ----------------------------------------------------- |
| `title` | Page title |
| `url` | Page URL (use for citations) |
| `description` | Snippet/description of the page |
| `age` | How old the result is (e.g., “2 hours ago”), optional |
## When to Use Brave Search vs DuckDuckGo
| Use Case | Brave Search | DuckDuckGo |
| ---------------------------- | ------------------------- | -------------- |
| Full web search results | Best choice | Not available |
| Current news and articles | Best choice | Limited |
| Quick facts and definitions | Works but slower | Best choice |
| Wikipedia-style summaries | Not available | Best choice |
| Calculations and conversions | Not available | Direct answers |
| API key required | Yes (free tier available) | No |
Both capabilities can be enabled simultaneously, the agent will choose the right tool based on the task.
## Security
* API keys are encrypted at rest (AES-256-GCM envelope encryption)
* Keys are validated on connection (test query to Brave Search API)
* API key never appears in tool results or message history
* Rate limiting deferred to Brave Search API (returns 429 on limit)
## Status
Experimental, available without a feature flag. This capability may change in future releases. For provider-neutral search, use [Web Search](https://everruns.com/docs/capabilities/web-search/), which can also use an organization Brave Search account.
## Links
* [Brave Search API](https://brave.com/search/api/)
* [Brave Search API Documentation](https://api.search.brave.com/app/#/documentation/web-search)
---
# Cloudflare Web Search
> Search Ceramic, Exa or Linkup through the Cloudflare Web Search API, billed to AI Gateway credits or your own provider key.
Source:
The [Cloudflare Web Search API](https://developers.cloudflare.com/web-search/) searches the web through one of three indexes and runs every search through your AI Gateway, so searches show up in its logs and analytics. A Cloudflare account connected as an organization search account answers the provider-neutral [Web Search](https://everruns.com/docs/capabilities/web-search/) capability: agents keep their one `web_search` tool.
## Indexes
| Index | Price per 1,000 searches | Zero data retention | Results |
| ------------------- | ------------------------ | ------------------- | --------------------------------------------------- |
| `ceramic` (default) | $0.25 | Yes | Long page descriptions from Ceramic’s own index |
| `exa` | $7.00 | No | Highlights: the passages most relevant to the query |
| `linkup` | $5.00 | Yes | Sourced results with text snippets |
Prices are Cloudflare’s list prices. Searches bill to the account’s AI Gateway credits, or to a provider key stored on the gateway when you give its alias. Each search account uses one index; connect a second account to offer another.
## Setup
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/profile/api-tokens), create an API token with **Account > Workers AI > Read** and **Account > AI Gateway > Read**.
2. Copy the account ID from the dashboard.
3. In **Registers > Web search**, connect **Cloudflare Web Search** and fill in:
| Field | Required | Description |
| ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Account ID | Yes | 32 hexadecimal characters |
| API Token | Yes | The token from step 1, stored encrypted |
| Search index | No | `ceramic` (default), `exa` or `linkup` |
| AI Gateway | No | The gateway searches run through; `default` when empty |
| Provider key alias | No | Alias of an Exa or Linkup key stored under AI Gateway > Provider Keys. When set, a missing key fails the search instead of falling back to credits |
Connecting runs one test search, so a wrong token, a missing permission, an unknown gateway or a missing key alias is reported before the account is saved. Only the token is a secret; the account ID, gateway, index and alias are stored as the connection’s settings.
The API connection is `POST /v1/orgs/{org}/organization-connections/providers/cloudflare_web_search` with `api_key` (the token), `account_id`, and optionally `index`, `gateway_id` and `byok_alias`.
## How it searches
Pick the account in an agent’s Web search settings, or put it in the organization’s Automatic order. A search returns at most 10 results, the API’s maximum. The API has no site or date filters, so allowed and blocked sites are applied to the results it returns, and an agent’s freshness setting is not applied (the search details say so). Like every search account, a failure (rate limit, no credit, rejected token) moves Automatic to the next provider.
---
# Cursor
> Launch and manage Cursor Cloud Agents from Everruns agents.
Source:
Everruns integrates with [Cursor Cloud Agents](https://docs.cursor.com/en/background-agents) so agents can delegate asynchronous coding work to Cursor. A triage agent can inspect a request, split it into focused tasks, launch Cursor agents against GitHub repositories, send follow-ups, and summarize status or results.
## What You Get
* **Launch Cloud Agents**: Start Cursor agents with repository, base ref, task prompt, optional branch name, and PR behavior.
* **Track Progress**: Read status, target branch, PR URL, summary, and conversation history.
* **Send Follow-ups**: Add more instructions to running Cursor agents.
* **Connection Prompt**: Configure a Cursor Cloud Agents API key in Settings → Connections.
* **Seed Agent**: Use the built-in **Cursor Agent Manager** example to triage and delegate work.
## Quick Start
### 1. Get a Cursor API Key
1. Open [Cursor Dashboard](https://cursor.com/dashboard?tab=cloud-agents)
2. Go to **Cloud Agents** > **My Settings** > **API Keys**
3. Create a Cloud Agents API key
4. Make sure Cursor’s GitHub app can access the repositories agents should work on
Use a Cloud Agents API key. A general Cursor dashboard API key may not be enough to create agents.
### 2. Connect in Everruns
1. Go to **Settings** > **Connections**
2. Find **Cursor**
3. Click **Connect**
4. Paste the Cloud Agents API key
### 3. Use in Sessions
Agents with the Cursor capability can use these tools:
| Tool | Description |
| -------------------------- | -------------------------------------------- |
| `cursor_launch_agent` | Start a Cursor Cloud Agent |
| `cursor_get_agent` | Get one agent’s status and result metadata |
| `cursor_list_agents` | List agents for the connected Cursor account |
| `cursor_add_followup` | Send extra instructions to a running agent |
| `cursor_get_conversation` | Read an agent’s conversation transcript |
| `cursor_delete_agent` | Delete an agent record/resources |
| `cursor_list_models` | List recommended Cursor model ids |
| `cursor_list_repositories` | List GitHub repositories Cursor can access |
| `cursor_key_info` | Check the active Cursor connection |
## Delegation Pattern
Use **Cursor Agent Manager** when you want Everruns to triage first and then delegate:
1. Provide the repository URL and base branch
2. Ask the agent to break the work into scoped tasks
3. Let it launch Cursor agents with clear acceptance criteria
4. Review returned Cursor links, branches, PR URLs, summaries, and transcripts
## Lifecycle
Cursor owns Cloud Agent lifecycle state. Everruns stores no per-agent state beyond normal session messages and tool results. Keep the returned `agent_id` if you want to poll, send follow-ups, read the conversation, or delete the agent later.
`cursor_list_repositories` is heavily rate-limited by Cursor and can be slow for large accounts. Prefer passing the repository URL directly.
## Security
* Cursor API keys are encrypted at rest when stored as Everruns user connections
* Everruns prompts for missing Cursor credentials through the inline connection dialog, not chat text
* Cursor agents run in Cursor-managed remote environments with internet access and command execution
* Repository access is controlled by the Cursor GitHub app and Cursor account settings
* Webhook secrets and image prompt payloads are intentionally not exposed in the first tool surface
## Links
* [Cursor Cloud Agents](https://docs.cursor.com/en/background-agents)
* [Cursor Background Agents API](https://docs.cursor.com/en/background-agent/api/overview)
* [Launch Agent API](https://docs.cursor.com/en/background-agent/api/launch-an-agent)
* [Cursor GitHub app](https://docs.cursor.com/en/github)
---
# DuckDuckGo
> Instant answers, definitions, and topic summaries via DuckDuckGo. No API key required.
Source:
Everruns integrates with [DuckDuckGo](https://duckduckgo.com/) to provide instant answers via the [DuckDuckGo Instant Answer API](https://api.duckduckgo.com/api). Agents can look up facts, definitions, topic summaries (from Wikipedia and other sources), and related topics, all without an API key.
## What You Get
* **Instant Answers**: Direct answers for calculations, IP lookups, conversions, and more
* **Topic Abstracts**: Wikipedia-style summaries for well-known topics
* **Definitions**: Dictionary definitions from Wiktionary and other sources
* **Related Topics**: Links to related topics for deeper exploration
* **No API Key Required**: The DuckDuckGo Instant Answer API is completely free
## Quick Start
### 1. No Setup Needed
Unlike other integrations, DuckDuckGo requires no API key or configuration. The DuckDuckGo Instant Answer API is free and public.
### 2. Enable the Capability
Add the `duckduckgo` capability to your agent or harness configuration.
### 3. Use in Sessions
Agents with the DuckDuckGo capability can use this tool:
| Tool | Description |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `duckduckgo_instant_answer` | Look up instant answers, abstracts, definitions, and related topics. Instant-answer lookup only, not a full web/SERP search |
### Parameters
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | --------------------------------------------- |
| `query` | string | Yes | Search query |
| `no_html` | boolean | No | Strip HTML from result text (default: `true`) |
### Response Fields
The tool returns a JSON object with available fields:
| Field | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `query` | The original query |
| `type` | Response type: `article`, `disambiguation`, `category`, `name`, `exclusive`, or `nothing` |
| `heading` | Topic heading |
| `abstract` | `{ text, source, url }`, topic summary |
| `answer` | `{ text, type }`, direct answer (calculations, etc.) |
| `definition` | `{ text, source, url }`, dictionary definition |
| `related_topics` | Array of `{ text, url }`, related topics (max 10) |
| `results` | Array of `{ text, url }`, official/direct results |
| `note` | Present only when no instant answer was found, a caveat that this is not a definitive web-search result and matching web pages may still exist |
Only non-empty fields are included in the response.
Note
This tool queries the DuckDuckGo **Instant Answer** API, not full web search. A `nothing` result (or a `note` field) means DuckDuckGo has no curated instant answer for the query, it does **not** mean no web pages match. Prefer a web-search or web-fetch tool (or Brave Search) for general web discovery; when none is available, this tool can still serve as a lightweight search for quick facts, definitions, and related topics.
## When to Use DuckDuckGo vs Brave Search
| Use Case | DuckDuckGo | Brave Search |
| ---------------------------- | ---------------- | ---------------- |
| Quick facts and definitions | ✅ Best choice | Works but slower |
| Wikipedia-style summaries | ✅ Best choice | Not available |
| Calculations and conversions | ✅ Direct answers | Not available |
| Full web search results | ❌ Not available | ✅ Best choice |
| Current news and articles | ❌ Limited | ✅ Best choice |
| API key required | No | Yes |
Both capabilities can be enabled simultaneously, the agent will choose the right tool based on the task.
## Security
* **No secrets**: No API key or credentials to manage
* **Read-only**: The API only returns information, no write operations
* **Privacy**: DuckDuckGo does not track searches
## Status
Experimental, available without a feature flag. This capability may change in future releases.
## Links
* [DuckDuckGo](https://duckduckgo.com/)
* [DuckDuckGo Instant Answer API](https://api.duckduckgo.com/api)
---
# Secure MCP Credentials
> Configure write-only credentials with scoped ownership and explicit access for MCP tools that require a secret parameter.
Source:
Use an Agent credential binding when an MCP tool requires a secret in its input, such as Visti’s `visti_send.channel_key`. Do not paste the value into chat, Agent instructions, memory, or session storage.
1. Attach the MCP server capability to the Agent.
2. Open the Agent and select **Credentials**.
3. Add or open the exact server, tool, and parameter binding.
4. Enter the value in the masked form and save it.
The value is encrypted and is never shown again. Everruns removes the bound parameter from the model-visible tool schema and injects the value only when it sends the MCP request. The same Agent binding works for a shared session and for triggers that create a new session per invocation.
Use **Replace** to replace a value. Use the revoke action to delete the binding; future calls then return a setup-required result with a link back to the agent’s Credentials settings.
Session Storage has a separate encrypted secret lifecycle for session-local workflows. Those secrets do not follow per-invocation sessions and are not injected into MCP tool calls, so they are not a substitute for an MCP credential binding.
## OAuth connections
For an MCP attachment that acts as a user, connect the provider from **Settings → Connections**. The grant belongs to your current organization’s virtual user and is used only for turns you initiate. Scheduled execution does not borrow the last speaker’s grant.
For an attachment that acts as a service, select the responding agent’s service virtual user and connect the provider on that account’s **Connections** tab. Agent behavior and service credentials remain separate.
External integrations can exchange verified channel authentication at `POST /v1/channels/{channel_id}/runtime-auth` for a short-lived runtime bearer token. The token allows virtual-user self-service and authenticated ingress for that channel. It cannot administer organizations, agents, or other users. Use `GET /v1/virtual-users/me` to discover the account, and the canonical `/v1/virtual-users/{id}/connections` routes for its grants. To start browser OAuth with bearer authentication, POST to the provider’s `authorize` subresource and navigate to the returned `authorization_url` while retaining its setup cookie. Setup captures the account and revalidates authority at callback.
Credential bindings also support session, virtual-user, and organization owners. Select personal credentials explicitly through an MCP attachment acting as the verified user; service attachments use their service identity. Session ownership and organization membership do not grant personal access. Organization bindings require explicit access grants. Two matching authorized bindings fail as ambiguous rather than selecting one silently.
Secure entry uses the revision of the target you reviewed, so a stale form cannot provision a changed target. See [write-only HTTP credentials](https://everruns.com/docs/capabilities/web-fetch/#write-only-credentials) for HTTP form/header bindings and response capture.
---
# Modal
> Modal sandboxes for code execution, full Linux VMs with their own kernel or lighter gVisor containers, with filesystem snapshots and public tunnels.
Source:
> **Status:** Experimental, behind the `modal` feature flag at the `adoption` rollout grade: an organisation owner or admin enables it in Settings → Features. `FEATURE_MODAL` can override the grade. Modal is not part of the supported sandbox set and may change or be removed.
Everruns integrates with [Modal](https://modal.com/) sandboxes. By default an agent gets a [VM sandbox](https://modal.com/blog/vm-sandboxes-agent-computers): a full Linux virtual machine with its own kernel, so workloads that need real kernel features (Docker, FUSE, databases, system services) run as they would on a server. The lighter gVisor container runtime is available too.
## What You Get
* **Full VMs or containers**: `runtime: "vm"` (default) boots a real kernel; `runtime: "gvisor"` starts a lighter container
* **Any registry image**: start from a public image (default `python:3.13-slim` with git and curl added), optionally with Dockerfile setup commands
* **Filesystem snapshots**: save a sandbox’s filesystem as an image and boot copies of it later
* **Public tunnels**: expose ports and get HTTPS URLs for them
* **Multiple sandboxes per session**, each cleaned up automatically when the session lease expires
## Quick Start
### 1. Create a Modal token
1. Sign in at [modal.com](https://modal.com/)
2. Open [Settings > API Tokens](https://modal.com/settings/tokens) and create a token
3. Copy the token ID (`ak-...`) and token secret (`as-...`)
### 2. Connect in Everruns
1. Go to **Settings** > **Connections**
2. Find **Modal** in the available providers
3. Click **Connect** and paste the token as `ak-...:as-...`
The form also accepts the `modal token set --token-id ak-... --token-secret as-...` command Modal shows when the token is created. Everruns checks the token with Modal before saving it.
### 3. Use in Sessions
Add the **Modal** capability to an agent. Agents with it can use these tools:
| Tool | Description |
| ------------------------ | -------------------------------------------------------------------------- |
| `modal_create_sandbox` | Create a VM or gVisor sandbox from an image, setup commands, or a snapshot |
| `modal_exec` | Run a shell command (`sh -c`) and return stdout, stderr and the exit code |
| `modal_read_file` | Read a text file from the sandbox |
| `modal_write_file` | Write a text file into the sandbox, creating parent directories |
| `modal_list_sandboxes` | List sandboxes created in this session |
| `modal_manage_sandbox` | Check a sandbox’s status or terminate it |
| `modal_snapshot_sandbox` | Snapshot the filesystem into an image (`im-...`) |
| `modal_tunnel_urls` | Get the public HTTPS URLs for exposed ports |
Commands and relative file paths start in `/workspace` inside the sandbox.
## Creating Sandboxes
`modal_create_sandbox` accepts:
| Parameter | Meaning |
| ---------------------- | ----------------------------------------------------------------- |
| `runtime` | `vm` (default) or `gvisor` |
| `image` | Registry image, for example `node:22` |
| `setup_commands` | Up to 20 Dockerfile lines (`RUN ...`) applied on top of the image |
| `snapshot_image_id` | Boot from a snapshot instead of an image |
| `timeout_seconds` | Maximum lifetime, 60 to 86400 (default 3600) |
| `idle_timeout_seconds` | Terminate after this long without activity |
| `cpu`, `memory_mb` | Resources to request |
| `expose_ports` | Up to 10 ports to publish through tunnels |
Modal caches built images per workspace, so the first sandbox from a new image takes longer than the ones after it.
## Network and GitHub access
Pass `network` to `modal_create_sandbox` to control outbound traffic. Modal enforces it outside the sandbox:
* `{"mode": "blocked"}`: no outbound network
* `{"mode": "allowlist", "domains": ["pypi.org", "*.pythonhosted.org"], "cidrs": ["10.0.0.0/8"]}`: only those destinations
* `{"mode": "open"}`: anything (the default)
Pass `"inject_connections": ["github"]` to let the sandbox use your GitHub connection without exposing the token (the agent’s connection when no person is in the run or the person has not connected GitHub). Modal adds it to requests for `api.github.com` and to git over `https://github.com`, so `curl https://api.github.com/user` and `git clone` of private repositories work, but the token never appears in the sandbox environment or files. Injection needs open network or a CIDR-only allowlist.
In Sandbox Templates, the template’s network setting (Open, Only listed domains, Blocked) is enforced the same way, and the **Use my GitHub connection** switch turns on injection.
## Snapshots
1. Call `modal_snapshot_sandbox` to save the current filesystem. The sandbox keeps running.
2. Pass the returned `im-...` ID as `snapshot_image_id` to `modal_create_sandbox` to boot a copy with that state.
## Tunnels
1. Create the sandbox with `expose_ports`, for example `[8080]`
2. Start a server listening on `0.0.0.0` at that port
3. Call `modal_tunnel_urls` to get the public HTTPS URL
## Sandbox Templates
On deployments that offer it, Modal is also a **managed** target in Sandbox Templates. Pick **Modal** under **Runs in** and choose the runtime (VM or gVisor), an image, CPU cores, memory and a workspace path (default `/workspace`). The agent then uses the standard sandbox tools instead of the `modal_*` tools, and Everruns manages the sandbox for the session.
When the session goes idle, Everruns snapshots the sandbox filesystem and terminates the sandbox. The next command boots a new sandbox from that snapshot, so files survive but running processes do not. Recovery for Modal templates is `provider_snapshot`.
## Lifecycle
Sandboxes run until the agent terminates them, their `timeout_seconds` or idle timeout passes, or the session’s lease on them expires. Everruns terminates leased sandboxes when the lease expires, so an abandoned session does not keep one running. Modal bills running sandboxes per second; see [Modal pricing](https://modal.com/pricing).
## Security
* **Isolation**: VM sandboxes run their own kernel; gVisor sandboxes run behind a user-space kernel
* **Encrypted credentials**: the token is stored in user connections, encrypted at rest
* **Session ownership**: tools only act on sandboxes this session created
* **Network access**: sandboxes have open outbound access unless you block or allowlist it, and exposed ports are public. Treat anything served on a tunnel as public
* **Injected credentials**: injected tokens go only to the service’s own domains, and the sandbox never holds them
---
# Parallel
> Use Parallel's hosted MCP server for free web search and URL fetching, with optional API-key authentication and OAuth-compatible endpoint selection.
Source:
> **Status:** Experimental, available without a feature flag.
Parallel provides hosted MCP tools for web search and URL fetching.
## Setup
Add the `parallel_search` capability to an agent or harness. It works for free without any connection.
To use a Parallel API key, add a `Parallel` connection in Settings → Connections, then configure the capability with `auth: "connection"`.
To use Parallel’s OAuth-compatible MCP endpoint, configure the capability with `endpoint: "oauth"`. This mode requires the `Parallel` connection because the endpoint rejects anonymous requests.
## As a Web search provider
A Parallel API key can also answer the provider-neutral [Web Search](https://everruns.com/docs/capabilities/web-search/) capability. Connect it as an organization search account in **Registers > Web search** (`parallel` connection), then pick it in an agent’s Web search settings or put it in the Automatic order. Searches go to Parallel’s Search API and are billed on that key. Use this instead of `parallel_search` when you want one `web_search` tool that can switch providers.
## Tools
| Tool | Purpose |
| -------------------------- | ---------------------------------------------------- |
| `mcp_parallel__web_search` | Search the web and return ranked URLs with excerpts. |
| `mcp_parallel__web_fetch` | Fetch and extract focused content from known URLs. |
Agents should reuse one stable `session_id` across Parallel tool calls in the same conversation.
## Paid machine payments
Operators can separately enable Parallel’s paid search, extraction, and task tools with `FEATURE_MACHINE_PAYMENTS=prod`, which enables it by default for organisations. The feature defaults to `off`; use `preview` for platform-managed enrolment or `adoption` for an organisation opt-in. See [feature rollout grades](https://everruns.com/docs/sre/environment-variables/#feature-rollout-grades). When it is off, Everruns does not expose Settings > Payments or the payment account, policy, and attempt APIs, so the deployment does not ask organization owners to entrust wallet keys for a capability that cannot spend.
---
# Build Agents for Slack
> Give your team an Agent with its own tools, instructions, and identity. Work with it in channels, direct messages, or Slack’s dedicated agent pane.
Source:
[Build a Slack Agent →](#build-your-first-slack-agent)
[Publish an existing Agent](https://everruns.com/docs/how-to/publish-to-slack/)
A conversationA daily briefingBased on real conversations

mikeSunday at 22:12
And whose turn is it on December 1?
4 replies

Kids DutiesAPPSunday at 22:12
December 1 (Tuesday) — **Darik 🐻** is on dishes (#2)! 😊
⚠️ A small hiccup: the briefing (07:00) and review (18:00) schedules haven’t been created yet — the scheduling tool returned an error a few times. I logged it and will try again later. The routine is unaffected 😊

mikeToday at 00:08
What can they do today?
New

Kids DutiesAPPToday at 00:09
Today, **Tuesday, October 13:**
* 🍽️ Dishes: **Daniil 🦊** (#1), tomorrow — Darik 🐻
* 🎮 Screen time: **up to 2 hours**, screens off by **20:30** 🌙
Have a lovely day! 😊

Kids DutiesAPP07:00
🦊 Dishes today: Daniil · 🎮 up to 2 hours\
🍽️ Dishes: Daniil (#1), tomorrow — Darik 🐻\
🎮 Screen time: up to 2 hours\
⏰ Deadline: screens off by 20:30\
🚫 Reminder: screen-free week, Oct 26 – Nov 1, is just 2 weeks away!
🔗 stend.sh/s/…
stend
Daily schedule 🏡
🏡 Daily schedule · Tuesday, October 13 · school day 📚 🚫 Screen-free week: October 26 – November 1. Get your board games and bikes ready! 🦊 Dishes today: Daniil #1. Tomorrow: Darik 🐻 🎮…
stend
stend.
### Daily schedule
Daily schedule · Tuesday, October 13 · school day. Screen-free week: October 26 – November 1. Get your board games and bikes ready…
stend.sh
Kids Duties answers questions, tracks the family routine, and shares a daily briefing with a linked schedule.Recreated from real Slack screenshots. Messages translated into English.
## More than a Slack bot
Build the Agent in Everruns. Give your team a familiar place to use it.
### Conversations with context
Talk in channels, threads, and DMs. Choose a session per thread, channel, or user.
### Slack’s dedicated agent pane
Enable the Agent surface for streaming replies, working status, and suggested prompts.
### Tools that do the work
Equip Agents with capabilities, skills, and MCP tools for knowledge, research, or operations.
### Actions inside Slack
React, update messages, share files in the thread, and look up users with the Slack capability.
### Human approval in the thread
The requester can approve or decline a proposed action through Slack approval cards.
### Visible task progress
A Tasks message updates as delegated work finishes. Open the session in Everruns for the full record.
## Build your first Slack Agent
Start with a job: answering team questions, researching a topic, or preparing an incident handoff.
01 / DEFINE
### Create the Agent
Set its name, avatar, instructions, and model. Add capabilities or MCP tools for its job.
02 / CONNECT
### Add Slack
In Integrations, add a Slack channel. Choose a session per thread, channel, or user.
03 / PUBLISH
### Invite your team
Publish the channel, select a connected workspace, choose Add to Slack, and approve the installation. Invite the bot and send a message.
An administrator connects the workspace once. Installation may need Slack administrator approval. Self-hosted deployments need public HTTPS. [Check setup requirements →](https://everruns.com/docs/capabilities/slack/#before-you-start)
[Follow the setup guide →](https://everruns.com/docs/how-to/publish-to-slack/)
[Learn about Agent definitions](https://everruns.com/docs/how-to/define-agents-as-files/)
## Make it a Slack Agent
Enable the Agent surface on your Slack channel to give it a dedicated pane alongside channel conversations. Replies stream in the pane; channel threads receive completed messages.
Existing Slack apps need an updated manifest and reinstall to grant the Agent surface permissions.
[Configure Slack’s agent pane →](https://everruns.com/docs/capabilities/slack/#agent-surface)
## Guides and reference
[**Publish an Agent →**Connect, install, and verify the first message.](https://everruns.com/docs/how-to/publish-to-slack/)[**Slack reference →**Scopes, session routing, actions, and troubleshooting.](https://everruns.com/docs/capabilities/slack/)
## Watch Kids Duties in Slack
A real Agent setup, its execution trace, and the morning briefing delivered in Slack. English narration and captions; the original Slack messages are Ukrainian.
[Slack integration: Kids Duties agent in Everruns](https://www.youtube-nocookie.com/embed/mbbYFif9juY?rel=0)
[Watch on YouTube](https://www.youtube.com/watch?v=mbbYFif9juY)
---
# Sprites
> Sprites persistent Firecracker microVMs for code execution, with filesystem persistence, checkpoints, and HTTP services.
Source:
> **Status:** Experimental and off by default: the `sprites` feature flag is at the `off` grade, so `FEATURE_SPRITES` must set a grade before any deployment registers it. Sprites is not part of the supported sandbox set and may change or be removed.
Everruns integrates with [Sprites](https://sprites.dev/) to provide persistent, hardware-isolated Linux microVMs powered by Firecracker. Unlike ephemeral sandboxes, Sprites maintain their filesystem across idle periods, support instant checkpoint/restore, and expose public HTTP endpoints.
## What You Get
* **Persistent Filesystem**: Full ext4 filesystem survives between sessions, backed to durable object storage
* **Hardware Isolation**: Firecracker VM-level isolation (stronger than containers)
* **Checkpoints**: Snapshot filesystem state in \~300ms for safe rollback before risky operations
* **HTTP Services**: Each sprite gets a unique public URL for exposing web services
* **Instant Wake**: Sprites wake from hibernation in <1 second
* **Multi-Sprite Sessions**: Create and manage multiple sprites per session
## Quick Start
### 1. Get Your API Token
1. Install the Sprites CLI: `curl https://sprites.dev/install.sh | bash`
2. Run `sprite login` to authenticate
3. Copy your token from the CLI output or dashboard
### 2. Connect in Everruns
1. Go to **Settings** > **Connections**
2. Find **Sprites** in the available providers
3. Click **Connect** and paste your API token
Once connected, the Sprites capability is automatically available in agent sessions.
Sprites default to `/home/sprite` as the working directory for commands and file paths.
### 3. Use in Sessions
Agents with the Sprites capability can use these tools:
| Tool | Description |
| ---------------------------- | ---------------------------------------------------- |
| `sprites_create_sprite` | Create a new Firecracker microVM |
| `sprites_exec` | Execute shell commands (wakes sprite if hibernating) |
| `sprites_read_file` | Read files from sprite filesystem |
| `sprites_write_file` | Write files to sprite filesystem |
| `sprites_list_sprites` | List sprites in this session |
| `sprites_manage_sprite` | Delete sprites |
| `sprites_checkpoint` | Create a filesystem checkpoint |
| `sprites_restore_checkpoint` | Restore to a previous checkpoint |
| `sprites_service_url` | Get the public HTTP URL for a sprite |
## Checkpoints
Sprites support instant filesystem checkpointing, a unique capability not found in other sandbox providers:
1. **Before risky operations**: Call `sprites_checkpoint` to snapshot the current state
2. **If something goes wrong**: Call `sprites_restore_checkpoint` to roll back
3. **Checkpoints are fast**: \~300ms without interrupting the running sprite
This makes Sprites ideal for iterative development where agents need to experiment safely.
## HTTP Services
Each sprite gets a unique public URL. To expose a web service:
1. Start a web server inside the sprite listening on **port 8080**
2. Call `sprites_service_url` to get the public URL
3. Share the URL for testing or preview
## Sprite Lifecycle

* **Running**: Active, consuming compute resources
* **Hibernating**: Idle, no compute charges, filesystem preserved on durable storage
* **Deleted**: Permanently removed, all data lost
Sprites persist indefinitely until explicitly deleted. They hibernate automatically when idle (no compute charges while idle). Always delete sprites when done to avoid storage charges.
## Pricing
Sprites bill per-second for compute and per-GB-hour for storage:
* **CPU**: $0.07/CPU-hour
* **Memory**: $0.04375/GB-hour
* **Storage**: $0.000027/GB-hour (persistent), $0.000683/GB-hour (hot NVMe cache)
* **Idle**: No compute charges (filesystem still persisted)
New users receive $30 trial credits (\~500 sprite sessions).
## Security
* **Firecracker VMs**: Hardware-level isolation between sprites
* **L3 Network Policies**: Domain whitelisting for outbound connections
* **Encrypted Credentials**: API token stored in user connections (encrypted at rest)
* **Leased Resources**: Sprites registered for automatic cleanup on session end
---
# TypeSafe
> Typed decisions from TypeSafe's System One: calibrated probabilities, routing and graded scores, via TypeSafe, OpenRouter, OpenAI or Microsoft Foundry.
Source:
> **Status:** Experimental. Available to every organisation; adding the capability and a TypeSafe connection is the opt-in.
Everruns integrates with [TypeSafe](https://typesafe.ai) so agents can ask for a **judgment** rather than an opinion. TypeSafe’s System One model answers typed questions about content and returns numbers your agent — and your code — can act on directly: the probability that something is true, which option out of a set applies, or where something falls on a scale you define.
It does not write prose. That is the point: there is no answer to interpret, and no JSON to parse out of a paragraph.
## What You Get
* **Yes/no with a probability**: “Is a refund being requested?” → `0.97`, not “Yes, it appears so.”
* **Single choice with a distribution**: pick one option and see how close the runners-up were
* **Graded scores**: rate against ordered levels you write, with the probability of each level
* **Confidence**: how concentrated the answer is, so the agent can escalate instead of guessing
* **One call, many questions**: every question in a call is answered together over the same content
## Use an existing OpenRouter account
In **Models**, add or enable a **Decisions** model under your OpenRouter provider. Choose the **Jev 1.13** profile and the provider’s model ID `typesafe/jev-1.13`. The same saved OpenRouter credential serves both chat and decisions. Direct TypeSafe uses `jev-1.13.0`.
## Use an existing OpenAI account
OpenAI’s [Decisions API](https://developers.openai.com/api/docs/guides/decisions) answers the same calibrated questions on GPT-6 Luna. In **Models**, enable the **GPT-6 Luna Decisions** model under your OpenAI provider (model ID `gpt-6-luna-decisions`, sent to OpenAI as `gpt-6-luna`). It sits next to the `gpt-6-luna` chat model and uses the same saved OpenAI key. OpenAI bills input tokens only, at $0.10 per million.
## Use an existing Microsoft Foundry account
[Microsoft-Decision-1](https://ai.azure.com/catalog/models/microsoft-decision-1) answers the same questions on Microsoft Foundry. Deploy it in the Foundry portal, then, in **Models**, add a **Decisions** model under your Microsoft MAI provider whose model ID is the deployment name. Foundry routes calls by deployment, so a deployment named `Decision-1` is model ID `Decision-1`. The names `Microsoft-Decision-1` and `Decision-1` pick the **Microsoft Decision 1** profile on their own; another name needs that profile chosen by hand. The provider’s saved Foundry API key and resource endpoint serve both chat and decisions. Microsoft bills input tokens only, at $0.042 per million, and a request (content plus questions) fits in 32,768 tokens.
Set the organization decision default in Models, or select an exact model in the **Jev Decisions** capability settings. The tool uses that model’s account. A disabled model or missing credential requires repairing the selection; it does not switch to another account. Decision calls honor session network restrictions and budgets, and appear in session usage.
The model service filter keeps decisions out of chat selectors. Model profile details show calibrated primitives and serving limits. Existing TypeSafe connections and session secrets continue to work when no catalog selection or decision default is configured.
## Quick Start
### 1. Get Your API Key
1. Sign in at [typesafe.ai](https://typesafe.ai)
2. Create an **API key** in the dashboard
3. Copy it
### 2. Connect in Everruns
1. Go to **Settings** > **Connections**
2. Find **TypeSafe** in the available providers
3. Click **Connect** and paste your API key
Once connected, the TypeSafe capability is available in agent sessions.
### 3. Use in Sessions
Agents with the TypeSafe capability get one tool:
| Tool | Description |
| -------------- | ------------------------------------------------------------ |
| `jev_decision` | Ask typed questions about content and get calibrated answers |
A call gives it the content plus the questions to ask about it:
```json
{
"state": "Why did the chicken cross the road? To get to the other side.",
"questions": [
{
"id": "is_funny",
"type": "noul",
"instructions": "Would a general audience laugh at this?"
},
{
"id": "humor",
"type": "score",
"instructions": "How funny is this joke?",
"levels": ["Not funny at all", "Mildly amusing", "Genuinely funny", "Hilarious"]
}
]
}
```
And gets back numbers, not a review:
```json
{
"model": "jev-1.13.0",
"answers": {
"is_funny": { "type": "noul", "probability_yes": 0.43 },
"humor": {
"type": "score",
"score": 0.58,
"normalized": 0.19,
"level": 1,
"label": "Mildly amusing",
"probabilities": { "0": 0.44, "1": 0.55, "2": 0.01, "3": 0.0 },
"confidence": 0.57
}
}
}
```
## Question Types
| Type | Ask it when | You get |
| ------------------------------------------------------ | --------------------------------------- | ---------------------------------------------------------------------------------- |
| [`noul`](https://docs.typesafe.ai/primitives/noul) | A condition either holds or it doesn’t | The probability of yes, from 0 to 1 |
| [`choice`](https://docs.typesafe.ai/primitives/choice) | Exactly one option out of a set applies | The selected option, every option’s probability, and a confidence |
| [`score`](https://docs.typesafe.ai/primitives/score) | Something falls somewhere on a scale | A weighted position across your levels, each level’s probability, and a confidence |
These are System One’s own primitives, kept under the same names here; TypeSafe documents them in full under [Primitives](https://docs.typesafe.ai/primitives).
Two things worth knowing when you write the questions:
* A `noul` near **0.5** means yes and no are roughly equally likely. It does not mean “somewhat” — for degree, use a `score`.
* `choice` options and `score` levels must each describe a concrete situation and stand on their own. The question id is never shown to the model, so the instructions have to carry the whole meaning.
The `confidence` on a `choice` or `score` is a second axis, not a restatement of the probability: the answer tells you *what*, confidence tells you *whether to act*. See [Confidence](https://docs.typesafe.ai/confidence), and [confidence-gated routing](https://docs.typesafe.ai/patterns/confidence-routing) for the pattern it enables.
## Good Fits
* **Verification**: does this answer actually follow from the source it cites?
* **Rating**: how severe is this report, how good is this draft, how funny is this joke
* **Routing**: which handler, team, or tool should take this — with a confidence to gate on
* **Screening**: does this content match a policy, and how clearly
## Embedding the Framework
Running the Everruns Framework in your own application rather than on the platform? The same capability attaches to an agent you build yourself, and the decision tool is also callable directly with no agent at all. See [Direct decision](https://everruns.com/docs/framework/direct-model-calls/#decisions).
## Guardrails
The same model backs Everruns [guardrails](https://everruns.com/docs/capabilities/guardrails/) when a `llm_judge` or `moderation` check sets `"engine": "jev"`. Instead of asking the utility model to write a verdict, the check gets a calibrated probability and your configured `threshold` decides — and every check on a stage is answered in a single call. That path uses a deployment-owned key (`UTILITY_TYPESAFE_API_KEY`), not your personal connection.
## Security
* The API key is stored as a user connection and never exposed to the agent or written into session transcripts.
* Content passed to `jev_decision` leaves the platform for TypeSafe, like any other integration that inspects content. Calls are capped at 20 questions and 32 KiB of content.
* The content being judged is sent as **data**, and every question states so — a document that tries to instruct the model is being rated, not obeyed.
## Learn more
The model and its concepts are TypeSafe’s, and their documentation is the reference for both:
* [System One](https://docs.typesafe.ai/concepts/system-one) — the class of model, and how it differs from an LLM
* [Primitives](https://docs.typesafe.ai/primitives) — [Noul](https://docs.typesafe.ai/primitives/noul), [Choice](https://docs.typesafe.ai/primitives/choice), [Score](https://docs.typesafe.ai/primitives/score), and [structured criteria](https://docs.typesafe.ai/primitives/advanced)
* [State](https://docs.typesafe.ai/concepts/state) — what to send as the thing being judged
* [Confidence](https://docs.typesafe.ai/confidence) — certainty as a second axis, distinct from the probability
* [Patterns](https://docs.typesafe.ai/patterns) — including [speculative fan-out](https://docs.typesafe.ai/patterns/fan-out) and [confidence-gated routing](https://docs.typesafe.ai/patterns/confidence-routing)
---
# Observability
> Send Everruns session traces, token usage, and tool-call timings to your observability platform of choice.
Source:
Everruns emits structured events for every agent turn, model calls, tool invocations, retries, token usage, latency. The integrations in this section forward those signals to observability platforms so you can monitor agents in production, evaluate prompt changes, and debug failures with full trace context.
## Investigate failed turns
In the platform UI, each agent’s activity chart covers the last 24 hours, with one bar per hour. Hover or keyboard-focus the chart to confirm the time window. Click **1 failed** (or the corresponding failure count) to open the agent’s **Sessions** tab filtered to failures in that chart’s window.
The filter uses the time a turn failed, so it includes sessions created earlier, archived sessions, and sessions that later recovered. Counts distinguish failed turns from affected sessions. Each card shows its current outcome and the latest matching failure. **View failure** opens that turn in **Trace**, selecting the error step when one was recorded. A failure can exist without a model or tool step; the Trace banner still identifies the failed turn.
Use **With failed turns** in an agent’s Sessions tab to investigate failures over the last 24 hours, 7 days, or 30 days. Choose **All sessions** to clear the failure filter.
For scripted investigation, run `everruns agents failures list `. Use `--after` and `--before` to choose a failure-time window, and `--limit` and `--offset` to page through matching sessions.
## Available Integrations
For live execution inside the platform, use [Monitoring](https://everruns.com/docs/observability/monitoring/). It provides organization-scoped and platform-wide views of execution phases, worker ownership, and actor leases.
* [OpenTelemetry](https://everruns.com/docs/observability/opentelemetry/), export traces over OTLP to any tracing backend. Spans follow the Gen-AI semantic conventions and the OpenInference conventions at once, so Grafana Tempo, Jaeger, Datadog, Langfuse, and Arize Phoenix all read them.
* [Braintrust](https://everruns.com/docs/observability/braintrust/), LLM observability, evaluation, and trace visualization. Turn traces are grouped by session, with token usage, time-to-first-token, and tool execution times.
## Framework Applications
Framework apps export the same traces from the `everruns` crate, behind the `otel` and `braintrust` Cargo features. See [Framework observability](https://everruns.com/docs/framework/observability/) for registering exporters and your own listeners on the Engine, and for flushing them on shutdown.
## Related
* [Events](https://everruns.com/docs/features/events/), the streaming event protocol that backs every observability export.
* [Environment Variables](https://everruns.com/docs/sre/environment-variables/), configure exporters, sampling, and OTLP endpoints.
* [Runnable Framework examples](https://everruns.com/docs/framework/examples/), complete programs built on the `everruns` crate.
---
# Braintrust
> Send Everruns traces to Braintrust for evaluation and trace visualization.
Source:

Preview
This integration is in preview. APIs and behavior may change.
Everruns integrates with [Braintrust](https://www.braintrust.dev/) to provide LLM observability, evaluation, and trace visualization for your agentic workflows.
## What You Get
* **Turn Traces Grouped by Session**: Keep one trace per turn while grouping the conversation by `metadata.session_id`
* **Token Usage Tracking**: Monitor input/output tokens and prompt cache efficiency
* **Performance Metrics**: Time-to-first-token, LLM call duration, tool execution times
* **Durable-ish Delivery**: Buffered batch delivery with retries for rate limits, `5xx`, and timeout/connect failures
* **Privacy Controls**: Raw content, thinking, tool args, and tool results are independently configurable
## Quick Start
### 1. Get Your API Key
1. Sign up at [braintrust.dev](https://www.braintrust.dev/)
2. Go to **Settings** → **API Keys**
3. Create a new API key
### 2. Configure Everruns
Set environment variables:
```bash
# Optional explicit switch
export BRAINTRUST_ENABLED=true
# Required
export BRAINTRUST_API_KEY=sk-bt-your-api-key
# Recommended: specify your project name
export BRAINTRUST_PROJECT_NAME="My Project"
# Conservative defaults
export BRAINTRUST_RECORD_CONTENT=false
export BRAINTRUST_RECORD_THINKING=none
export BRAINTRUST_TOOL_ARGS_MODE=redacted
export BRAINTRUST_TOOL_RESULTS_MODE=summary
```
| Variable | Required | Default | Description |
| -------------------------------- | -------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BRAINTRUST_ENABLED` | No | enabled when API key is present | Explicit Braintrust on/off switch |
| `BRAINTRUST_API_KEY` | Yes | - | API key from Braintrust settings |
| `BRAINTRUST_PROJECT_NAME` | No | `My Project` | Project name for organizing traces |
| `BRAINTRUST_PROJECT_ID` | No | - | Direct project UUID (skips name lookup) |
| `BRAINTRUST_API_URL` | No | `https://api.braintrust.dev` | API base URL |
| `BRAINTRUST_QUEUE_CAPACITY` | No | `1024` | Buffered event capacity before new exports are dropped |
| `BRAINTRUST_MAX_BATCH_SIZE` | No | `50` | Max events per Braintrust insert call |
| `BRAINTRUST_FLUSH_INTERVAL_MS` | No | `500` | Max delay before a partial batch flushes |
| `BRAINTRUST_REQUEST_TIMEOUT_MS` | No | `10000` | Per-request timeout |
| `BRAINTRUST_MAX_RETRIES` | No | `3` | Retries for `429`, `5xx`, and timeout/connect failures |
| `BRAINTRUST_RETRY_BASE_DELAY_MS` | No | `250` | Initial retry backoff |
| `BRAINTRUST_RETRY_MAX_DELAY_MS` | No | `5000` | Retry backoff cap |
| `BRAINTRUST_RECORD_CONTENT` | No | `false` | Export raw turn and LLM text content |
| `BRAINTRUST_RECORD_THINKING` | No | `none` | Export thinking as `none`, `summary`, or `full` |
| `BRAINTRUST_TOOL_ARGS_MODE` | No | `redacted` | Export tool args as `full`, `redacted`, or `none`; also governs `executed_arguments`, the hook-rewritten arguments a tool span carries when a pre-tool hook changed the call |
| `BRAINTRUST_TOOL_RESULTS_MODE` | No | `summary` | Export tool results as `full`, `summary`, `redacted`, or `none` |
| `BRAINTRUST_DEBUG_PAYLOADS` | No | `false` | Print full outbound Braintrust payload JSON to local debug logs |
### 3. View Traces
1. Open the Braintrust dashboard
2. Navigate to your project
3. Go to **Logs**
4. Group or filter by `metadata.session_id` to reconstruct the full session timeline across turn traces
## Trace Hierarchy
Each Everruns turn creates its own trace with the following structure:
```plaintext
agent turn (root span)
├── reason (iteration 1)
│ └── llm.generation (gpt-6.1-sol)
├── act (iteration 1)
│ ├── tool.call (search)
│ └── tool.call (fetch)
├── reason (iteration 2)
│ └── llm.generation (gpt-6.1-sol)
└── (no more tool calls - turn complete)
```
### Span Types
| Span | Type | Description |
| -------------- | ------ | ------------------------------------- |
| Agent Turn | `task` | Root span for the entire user request |
| Reason | `task` | LLM reasoning phase (may iterate) |
| Act | `task` | Tool execution phase |
| LLM Generation | `llm` | Individual LLM API call |
| Tool Call | `tool` | Individual tool execution |
## Session Grouping
Everruns does not export one giant trace for the whole conversation.
* Each turn remains its own Braintrust trace.
* Every root turn span carries `metadata.session_id`.
* Session lifecycle events (`session.started`, `session.activated`, `session.idled`) are exported as lightweight logs with the same `session_id`.
* Root turn metadata also carries stable filtering fields when available, such as `input_message_id`, monotonic event ordering, deployment grade, session status, model/provider summary, retry info, and compaction info.
Use Braintrust grouping, timeline, or thread views on `metadata.session_id` to analyze the session as a whole while keeping per-turn debugging sharp.
## Metrics Captured
### LLM Generations
* `prompt_tokens` - Input token count
* `completion_tokens` - Output token count
* `cache_read_tokens` - Tokens read from prompt cache (Claude)
* `cache_creation_tokens` - Tokens written to prompt cache (Claude)
* `time_to_first_token` - Time until first token received
* `duration_ms` - Total LLM call duration
### Tool Calls
* `status` - Success/failure
* `duration_ms` - Execution time
* `error` - Error message (on failure)
## Delivery Behavior
* Exports enqueue into a bounded in-memory buffer.
* The exporter flushes batches to `POST /v1/project_logs/{project_id}/insert`.
* `429`, `5xx`, timeout, and connect failures are retried with jittered backoff.
* If the queue fills, new events are dropped and the exporter logs the drop counter.
* Framework applications that register `Braintrust::from_env()` and call `Engine::shutdown` wait for the final HTTP batch.
This is best-effort durability, not a disk-backed queue.
## Privacy Controls
The Braintrust exporter defaults to conservative content handling:
* raw turn and LLM text are off unless `BRAINTRUST_RECORD_CONTENT=true`
* when raw content is off, the exporter emits structural metadata only; it does not emit truncated prompt/completion previews
* extended thinking is off unless `BRAINTRUST_RECORD_THINKING` says otherwise
* tool arguments default to `redacted`
* tool results default to `summary`
* tool arg/result modes still apply inside recorded LLM input/output payloads
* full outbound payload logging is off unless `BRAINTRUST_DEBUG_PAYLOADS=true`
## Troubleshooting
### Traces Not Appearing
1. **Check API key**: Verify `BRAINTRUST_API_KEY` is set correctly
2. **Check project resolution**: If `BRAINTRUST_PROJECT_NAME` does not match an existing project, startup logs will show a project resolution failure
3. **Check exporter logs**: Look for rate-limit retries, timeout retries, queue drops, or permanent insert failures
### Session Views Are Fragmented
1. Confirm root turn spans include `metadata.session_id`
2. Group Braintrust logs by `metadata.session_id`
3. Check whether privacy controls removed content you expected; the default is conservative
## Links
* [Braintrust Documentation](https://www.braintrust.dev/docs)
* [API Reference](https://www.braintrust.dev/docs/api-reference/introduction)
* [Insert Logs API](https://www.braintrust.dev/docs/api-reference/logs/insert-project-logs-events)
---
# Monitoring
> Watch execution across thousands of sessions, map actor ownership to workers, and inspect durable leases as the fleet runs.
Source:
Monitoring shows how the execution fleet is working, with aggregate population counts and progressively narrower worker and actor detail. The overview keeps agent names, prompts, and tool arguments out of the display.
## Organization and platform views
An organization administrator enables **Monitoring** in **Settings → Features**. The feature uses the `monitoring` adoption flag and starts disabled. The **Operational → Monitoring** destination then shows only the selected organization’s execution and actor ownership. Reading it requires session-read permission; opting in does not grant additional permissions.
Platform operators open **Durable Execution → Monitoring** for the cross-org view. This view also exposes worker capacity, reported load, and heartbeat freshness. Organization owners and admins cannot access this view unless they also have platform access.
## Execution
**Flow** shows current sessions in runnable, model, tool, waiting, and setup phases. Measured connections show committed event rates over the last minute. Dashed connections illustrate the execution lifecycle. A turn can iterate between model and tools many times before completing.
**Density** shows how that population changes over time. Each execution state has its own intensity scale. Select a time bucket to inspect an earlier population summary.
**Pause** freezes the displayed snapshot while observations continue to collect. **Replay** retains up to fifteen minutes of population summaries collected while this view is open. History starts at the first successful snapshot; it does not invent earlier data. Historical worker maps are not inferred from today’s ownership. Return to **Live** for worker and actor inspection.
## Workers and actors
**Workers** groups execution by worker, with organization-to-worker ownership mapping in the detail rail. Change group size to inspect individual workers or larger cohorts. Worker load includes background activities and differs from actor leases: a model phase may retain its lease while releasing a work slot. Parked, unleased sessions remain visible in Execution.
**Actors** lists current session actors, with expired leases and the oldest runnable work first. Select one to inspect its holder, fence, lease deadline, last renewal, lease duration, inbox cursors, cancellation, ownership history, and recent trace-step timing. A sequence difference is not a pending-message count. Expiry is a lease observation and does not establish worker death. Ownership history is retained for one day.
**Metrics** provides conventional event counts, rates, population trends, and lease health. Overview summaries refresh every second. Failed refreshes are marked stale rather than appearing healthy.
## API
Use `GET /api/v1/monitoring` for an enrolled organization’s summary and `GET /api/v1/durable/monitoring` for platform-wide monitoring. Both are read-only. Use browser credentials or a full-access personal token; scoped personal tokens remain denied. Organization adoption and read permissions still apply, and fleet access still requires platform authority. The adjacent `/actors` endpoints provide bounded, filtered pages and actor detail. The [API reference](https://everruns.com/docs/api/) owns the request and response contracts.
Monitoring complements [OpenTelemetry](https://everruns.com/docs/observability/opentelemetry/) and [Braintrust](https://everruns.com/docs/observability/braintrust/), which provide trace exports and provider-level diagnostics.
---
# OpenTelemetry
> Export Everruns agent traces over OTLP. Spans follow the OpenTelemetry Gen-AI and OpenInference conventions, so any tracing backend reads them.
Source:
Everruns turns every agent run into an OpenTelemetry trace and exports it over OTLP. Spans carry two attribute vocabularies at once, the [OpenTelemetry Gen-AI semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai/tree/main/docs/gen-ai) and the [OpenInference conventions](https://arize-ai.github.io/openinference/spec/semantic_conventions.html), so one endpoint feeds general-purpose backends such as Grafana Tempo, Jaeger, and Datadog as well as LLM-native ones such as Arize Phoenix and Langfuse.
## What You Get
* **A trace per turn**: an `invoke_agent` root span named after your agent, with model calls, tool runs, and reasoning phases nested underneath
* **Real timings**: spans start and end at the moment each event happened, so waterfalls show true model latency and tool duration
* **Token and cost detail**: input, output, and prompt-cache tokens per call and per turn, plus cost where the provider reports it
* **Tool visibility**: every tool call is its own span with name, description, call id, and outcome
* **Failure detail**: a low-cardinality `error.type`, an error span status carrying a safe classification (the full message requires content capture), and an `exception` event
* **Privacy by default**: prompts, completions, reasoning, and tool payloads are never exported unless you turn them on
## Quick Start
### 1. Point Everruns at a collector
Set one variable. Any OTLP/HTTP endpoint works.
```bash
# Local collector, Grafana Tempo, Datadog agent, ...
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# Name the service in your traces
export OTEL_SERVICE_NAME=everruns-server
```
Traces are exported over OTLP HTTP/protobuf. Point the variable at the base endpoint, usually port `4318`, and Everruns appends the `/v1/traces` path; a full signal URL is used as given.
### 2. Configure
| Variable | Required | Default | Description |
| ---------------------------------------------------- | -------- | ------------------------------------ | --------------------------------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Yes | - | OTLP/HTTP endpoint. Tracing stays off while unset |
| `OTEL_SERVICE_NAME` | No | `everruns-server`, `everruns-worker` | Service name on the spans |
| `OTEL_SERVICE_VERSION` | No | - | Service version on the spans |
| `OTEL_ENVIRONMENT` | No | - | Deployment environment label |
| `OTEL_SDK_DISABLED` | No | `false` | Disable tracing without unsetting the endpoint |
| `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` | No | `false` | Export instructions, messages, tool arguments and results |
| `EVERRUNS_TRACE_CONVENTIONS` | No | `gen_ai,openinference` | Which attribute vocabularies to write |
### 3. View traces
Open your backend and look for the `invoke_agent` spans. In Arize Phoenix, point the same variable at Phoenix and its spans appear as AGENT, LLM, and TOOL rows with no extra configuration:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:6006
```
## Trace Structure
Each turn is one trace. Reasoning and acting phases give the trace its shape, and extended thinking sits inside the model call it belongs to:
```plaintext
invoke_agent {agent name} (root, INTERNAL)
├── reason reasoning phase
│ └── chat {model} model call (CLIENT)
│ └── thinking extended thinking, when enabled
├── act tool execution phase
│ ├── execute_tool {name}
│ └── execute_tool {name}
├── reason
│ └── chat {model}
└── (no further tool calls, turn complete)
```
| Span | Kind | `gen_ai.operation.name` | `openinference.span.kind` |
| --------------------------- | ---------- | ----------------------- | ------------------------- |
| `invoke_agent {agent name}` | `INTERNAL` | `invoke_agent` | `AGENT` |
| `chat {model}` | `CLIENT` | `chat` | `LLM` |
| `execute_tool {name}` | `INTERNAL` | `execute_tool` | `TOOL` |
| `reason`, `act`, `thinking` | `INTERNAL` | none | `CHAIN` |
The reason, act, and thinking spans are Everruns phases rather than Gen-AI operations, so they carry no `gen_ai.operation.name` and backends do not count them as model calls.
## OpenTelemetry Gen-AI Support
| | Supported | Attributes |
| - | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ✅ | Agent span per turn | `gen_ai.agent.id`, `gen_ai.agent.name`, `gen_ai.agent.description` |
| ✅ | Model call spans | `chat {model}`, CLIENT kind, real call duration |
| ✅ | Provider and model identity | `gen_ai.provider.name`, `gen_ai.request.model`, `gen_ai.response.model`, `gen_ai.response.id` |
| ✅ | Finish reasons | `gen_ai.response.finish_reasons` as a string array |
| ✅ | Token usage with cache split | `gen_ai.usage.input_tokens`, `output_tokens`, `cache_read.input_tokens`, `cache_write.input_tokens` |
| ✅ | Request parameters | `gen_ai.request.temperature`, `max_tokens`, `reasoning.level`, `stream` |
| ✅ | Streaming latency and compaction | `gen_ai.response.time_to_first_chunk`, `gen_ai.conversation.compacted` |
| ✅ | Tool execution spans | `gen_ai.tool.name`, `gen_ai.tool.type`, `gen_ai.tool.call.id`, `gen_ai.tool.description` |
| ✅ | Extended thinking | Nested inside the model call it belongs to |
| ✅ | Errors | `error.type`, error span status, `exception` events |
| ✅ | Conversation correlation | `gen_ai.conversation.id` on every span |
| ✅ | Content capture (opt-in) | `gen_ai.system_instructions`, `gen_ai.input.messages`, `gen_ai.output.messages`, `gen_ai.tool.definitions`, `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result` |
| ✅ | Accurate timestamps | Spans start and end at the times the events record |
Input totals include uncached tokens plus cache reads and cache writes; cache attributes are subsets of that total. Sum model spans (`gen_ai.operation.name=chat`) for model usage or turn roots for turn totals. Summing both counts the same usage and cost twice.
Not emitted yet: `server.address` and `server.port` on model calls, and parameter schemas inside `gen_ai.tool.definitions`.
## OpenInference Support
| | Supported | Attributes |
| - | -------------------------------- | ---------------------------------------------------------------------------------------- |
| ✅ | Span kinds | `openinference.span.kind`: `AGENT`, `CHAIN`, `LLM`, `TOOL` |
| ✅ | Session and virtual user | `session.id`, `agent.name`, `metadata` |
| ✅ | Model identity | `llm.model_name`, `llm.provider`, `llm.system` |
| ✅ | Token counts | `llm.token_count.prompt`, `.completion`, `.total` |
| ✅ | Prompt cache detail | `llm.token_count.prompt_details.cache_read`, `.cache_write` |
| ✅ | Cost | `llm.cost.total` in USD, when the provider reports it |
| ✅ | Invocation parameters and tools | `llm.invocation_parameters`, `llm.tools.N.tool.json_schema` |
| ✅ | Input and output values (opt-in) | `input.value`, `output.value`, with `input.mime_type` and `output.mime_type` |
| ✅ | Flattened messages (opt-in) | `llm.input_messages.N.message.*`, `llm.output_messages.N.message.*`, tool calls included |
| ✅ | Tool spans | `tool.name`, `tool.description`, arguments and results as input and output values |
| ✅ | Errors | Error span status with `exception` events |
| ✅ | Phoenix out of the box | Point the OTLP endpoint at Phoenix, nothing else to configure |
## Everruns Attributes
Spans also carry a few Everruns-specific attributes under their own namespace, so they never collide with either convention:
| Attribute | Spans | Description |
| ------------------------------------------------------------------------------------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `everruns.turn.id`, `everruns.exec.id`, `everruns.input_message.id` | All | Correlation ids that match the [event stream](https://everruns.com/docs/features/events/) |
| `everruns.phase` | `reason`, `act`, `thinking` | Which phase the span represents |
| `everruns.turn.iterations`, `everruns.turn.tool_call_count`, `everruns.turn.llm_call_count`, `everruns.turn.status` | `invoke_agent` | Turn counters and outcome |
| `everruns.turn.stop_reason` | `invoke_agent` | How the turn’s final model call ended (`stop`, `length`, …) |
| `everruns.tool.status`, `everruns.tool.capability.id` | `execute_tool` | Tool outcome and the capability that provided it |
| `everruns.tool.executed_arguments`, `everruns.tool.executed_arguments_truncated` (opt-in) | `execute_tool` | The arguments the tool actually ran with, present only when a pre-tool hook rewrote the call. Bounded, with credential values redacted; emitted only with content capture |
| `everruns.usage.cost_usd` | `chat`, `invoke_agent` | Cost when known |
| `everruns.llm.retry.attempts`, `everruns.llm.retry.total_wait_ms` | `chat` | Provider retries behind a single call |
| `everruns.llm.finish_reason`, `everruns.llm.provider_finish_reason` | `chat` | Normalized finish reason as a scalar, and the provider’s own stop reason verbatim (`max_tokens`, `MAX_TOKENS`, …) |
| `everruns.llm.tool_calls_dropped`, `everruns.llm.tool_calls_truncated_executed` | `chat` | Tool calls discarded because the response was cut off (or their arguments were not valid JSON), and complete calls run from a truncated response; present only when non-zero |
| `everruns.llm.truncation_gate` | `chat` | `retried` or `failed` when the [output truncation](https://everruns.com/docs/capabilities/output-truncation/) policy acted on dropped calls |
| `everruns.span.orphaned`, `everruns.span.unterminated` | Any | Diagnostics: a span rebuilt from a terminal event, or closed because its turn ended first |
## Choosing Conventions
Both vocabularies are written by default. Narrow to one when a backend only reads one and you want smaller spans:
```bash
# OpenTelemetry Gen-AI only (Tempo, Jaeger, Datadog, Langfuse)
export EVERRUNS_TRACE_CONVENTIONS=gen_ai
# OpenInference only (Arize Phoenix)
export EVERRUNS_TRACE_CONVENTIONS=openinference
```
Span names, kinds, and hierarchy are the same either way; only the attributes differ. An unrecognized value falls back to writing both.
## Content Capture
Prompts, completions, reasoning, and tool payloads are **not** exported by default. Turn them on with the standard OpenTelemetry variable:
```bash
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
```
With capture on:
* model call spans carry `gen_ai.system_instructions`, `gen_ai.input.messages`, `gen_ai.output.messages`, and `gen_ai.tool.definitions` in the conventions’ `{role, parts}` JSON, plus the OpenInference `input.value` and `output.value` and the flattened `llm.input_messages.N.message.*` keys
* tool spans carry their arguments and results
* the turn root carries the user’s message and the final answer
* the thinking span carries the model’s reasoning text
With capture off, error status descriptions and exception messages contain only the error classification. With capture on, detailed errors may include provider response bodies or tool output; treat them as sensitive content.
Two details worth knowing. Everruns keeps the agent’s instructions separate from the conversation, so system messages are exported as `gen_ai.system_instructions` and left out of `gen_ai.input.messages`. Image bytes are never copied into a span; an image becomes a reference that names its media type only.
Treat this as a data-retention decision. Everything captured leaves your deployment for whatever backend the OTLP endpoint points at.
## Troubleshooting
### No traces appear
1. Confirm `OTEL_EXPORTER_OTLP_ENDPOINT` is set and reachable from the server and worker processes; without it, tracing is off and startup logs say so
2. Confirm the endpoint speaks OTLP over **HTTP**, typically port `4318`, not the gRPC port `4317`
3. Check startup logs for `OpenTelemetry tracing enabled` with your endpoint, or a warning that the exporter failed to initialize
4. Confirm `OTEL_SDK_DISABLED` is not set to `true`
### Traces appear but spans look empty
1. Prompts and completions require `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`; without it, spans carry structure and metrics only, which is the default
2. If your backend reads only one vocabulary, check `EVERRUNS_TRACE_CONVENTIONS` has not been narrowed to the other one
### Turns are split across traces
Each turn is deliberately its own trace. Group by `gen_ai.conversation.id`, or `session.id` in Phoenix, to follow a whole session.
## Related
* [Braintrust](https://everruns.com/docs/observability/braintrust/), LLM observability and evaluation with a dedicated exporter
* [Events](https://everruns.com/docs/features/events/), the event stream every exporter is built on
* [Environment Variables](https://everruns.com/docs/sre/environment-variables/), the full operator reference
---
# Model Providers
> Connect Everruns to OpenAI, Anthropic, Google Gemini, Meta, Mistral AI, AWS Bedrock, OpenRouter, Fireworks AI, Cloudflare and Vercel AI Gateway, and more.
Source:
A **provider** is an organization-scoped account on an AI model vendor, OpenAI, Anthropic, AWS Bedrock, OpenRouter, and others. You configure a provider once with credentials and connection settings, and it powers the models your agents run on. Everruns abstracts every vendor behind one uniform driver interface, so the same agent, prompt, and capabilities run unchanged whether the model is served by OpenAI, Claude, Gemini, or any OpenAI-compatible endpoint.
## Supported providers
| Provider | Driver | Notes |
| ------------------------------------------------------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| [OpenAI](https://everruns.com/docs/providers/openai/) | `openai`, `openai_completions` | Responses API (recommended) and Chat Completions. Also drives OpenAI-compatible endpoints via a base URL. |
| [Azure OpenAI](https://everruns.com/docs/providers/azure-openai/) | `azure_openai` | OpenAI models deployed in your Azure OpenAI resource. A dedicated provider type. |
| [Anthropic](https://everruns.com/docs/providers/anthropic/) | `anthropic` | Claude models via the Messages API, with extended thinking. |
| [Google Gemini](https://everruns.com/docs/providers/gemini/) | `gemini` | Gemini 4 Argon with long decode, reasoning, and implicit caching; explicit caching on supported Gemini models. |
| [AWS Bedrock](https://everruns.com/docs/providers/bedrock/) | `bedrock` | Models hosted on Amazon Bedrock via the `ConverseStream` API. |
| [OpenRouter](https://everruns.com/docs/providers/openrouter/) | `openrouter` | One key for a large multi-vendor catalog, with provider routing controls. |
| [Microsoft MAI](https://everruns.com/docs/providers/mai/) | `mai` | Microsoft MAI models via Azure AI Foundry, with API-key or Entra ID (OAuth) auth. |
| [Fireworks AI](https://everruns.com/docs/providers/fireworks/) | `fireworks` | Fast, low-cost inference for open models (Llama, Qwen, DeepSeek, Kimi, GLM, gpt-oss, …), with automatic model discovery. |
| [Mistral AI](https://everruns.com/docs/providers/mistral/) | `mistral` | Mistral Large, Medium, Small and Codestral from La Plateforme, with reasoning and automatic model discovery. |
| [Meta](https://everruns.com/docs/providers/meta/) | `meta` | Muse Wonder and Muse Spark through Meta’s stateful, OpenAI-compatible Responses API. |
| [Cloudflare AI Gateway](https://everruns.com/docs/providers/cloudflare/) | `cloudflare` | Many upstream vendors plus Cloudflare’s own Workers AI models behind one account token. |
| [Vercel AI Gateway](https://everruns.com/docs/providers/vercel/) | `vercel` | Many upstream vendors behind one Vercel endpoint, over the Open Responses API. |
Need a vendor that isn’t listed? Any OpenAI-compatible endpoint works through the [OpenAI](https://everruns.com/docs/providers/openai/) provider with a custom base URL (use the dedicated [Azure OpenAI](https://everruns.com/docs/providers/azure-openai/) provider for Azure deployments), and embedders can register additional drivers through the platform definition.
## Configure a provider
Providers are managed by organization admins:
1. Go to **Settings** → **Providers**.
2. Click **Add provider** and choose the provider type.
3. Enter the credentials (API key, or the provider-specific fields described on each provider’s page). Credentials are validated before they are saved.
4. Save. Everruns discovers the provider’s available models and makes them selectable for agents and sessions.
You can configure more than one provider for the same vendor, for example two Azure OpenAI regions, or a direct OpenAI key alongside an OpenRouter key.
## Providers vs. connections
Providers and [connections](https://everruns.com/docs/integrations/) look similar but are deliberately separate:
| | **Provider** | **Connection** |
| ------------- | ------------------------------- | ------------------------------------------------------- |
| Scope | Organization | User |
| Purpose | Infrastructure that runs agents | A user’s identity on an external service, used by tools |
| Configured in | Models → Providers | Settings → Connections |
| Examples | OpenAI, Anthropic, Bedrock | Daytona, GitHub, Slack |
Use a provider to decide **which model runs your agents**. Use a connection to give an agent access to an **external tool or service**.
## Models and switching providers
Agents and sessions bind to a specific model on a specific provider. Because the driver interface is uniform, you can move an agent from one provider to another without rewriting prompts or capabilities. See [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/) for the model-resolution rules and the API calls to add a provider, switch an agent’s default model, or run an A/B comparison per session.
## Credential security
* Credentials are encrypted at rest with AES-256-GCM envelope encryption.
* Credential values are never returned by the API, only a “configured” flag is exposed.
* Resolution is **fail-closed**: each organization resolves its own configured credentials. A turn with no configured provider fails with a clear error rather than running on another organization’s credentials.
## Organization small-model default
Under **Models → Defaults**, admins can select a **Small model** for lightweight work. This is a shared chat model on a specific provider, independent of the main chat and Decisions defaults. Personal providers and Decisions models cannot be selected here.
[Automatic session titles](https://everruns.com/docs/capabilities/automatic-session-titles/) use this setting by default. Individual capabilities can select another model or always inherit the session model. Leaving or clearing Small model means title generation inherits the session model. Setting a default does not enable any capability.
Use `PATCH /v1/orgs/{org_id}` to set `small_model_id` to a model’s public ID, or `null` to clear it. Omitted fields remain unchanged. A disabled or unavailable selection stays selected and skips generation without electing a replacement; deleting its model clears the selection. Requests use the selected provider’s organization credentials and count toward session usage and budgets.
---
# Anthropic
> Run Everruns agents on Anthropic Claude models via the Messages API, with streaming, tool use, and extended thinking.
Source:
Everruns runs agents on [Anthropic](https://www.anthropic.com/) Claude models through the Claude Messages API, mapping its provider-neutral messages, tools, and reasoning onto the Anthropic wire format.
## What you get
* **Claude Messages API** streaming.
* **Tool use** mapped to provider-neutral Everruns tools.
* **Extended thinking**: adaptive thinking on recent Claude families and budget-based thinking on older ones, with the chain-of-thought signature preserved across multi-turn conversations.
* **Prompt caching** via bounded `cache_control` breakpoints on stable, high-value sections of the request.
## Configure in Everruns
1. Go to **Settings** → **Providers** and click **Add provider**.
2. Choose **Anthropic**.
3. Paste your Anthropic API key. Get one from the [Anthropic Console](https://console.anthropic.com/).
4. Save. Everruns discovers available Claude models automatically.
### Claude Max and Team API credits
Claude Max and Team plans include monthly Claude API credits. They land in the Anthropic Console organization you link to your plan, so any API key from that organization spends them first, before purchased credits. To run Everruns agents on those credits, create the key in the linked organization and add it as described above. Nothing else in Everruns changes.
Everruns does not offer signing in with a Claude.ai account: Anthropic does not allow third-party products to use Claude.ai login or plan usage limits. See [API credits for Max and Team plans](https://platform.claude.com/docs/en/about-claude/api-credits-for-subscribers) for eligibility, amounts, and how to link the organization.
## Models
Anthropic’s `/v1/models` endpoint returns capability metadata, which Everruns merges with its built-in model profiles. Hardcoded profiles take precedence for cost data; discovered data fills gaps for newer models.
`max_tokens` is required on every Anthropic request, so Everruns always resolves a value from the model profile (falling back to a safe default) and will retry once with a lower limit if a stale profile causes the provider to reject it.
Thinking counts toward `max_tokens`. When you set `max_tokens` yourself, Everruns preserves it as a hard limit on all generated tokens, including thinking. When the limit cannot accommodate thinking, the driver omits thinking rather than exceed the limit. On models that always think (Opus 5.5, Sonnet 5.5, Haiku 5.5, Fable 5.x), omitting it would run thinking at the provider’s default effort, so the driver keeps it and lowers the effort to `low` instead. A very small limit on those models can still be spent on thinking; give them room for the answer.
Claude models that always think (Opus 5.5, Sonnet 5.5, Haiku 5.5, Fable 5.x) always get an explicit effort: the model’s default when you choose none, and `low` when you choose `none`. These and the other adaptive-thinking models reject assistant prefill, so a conversation must end with a user or tool message.
## Links
* [Anthropic](https://www.anthropic.com/)
* [Anthropic Console](https://console.anthropic.com/)
* [API credits for Max and Team plans](https://platform.claude.com/docs/en/about-claude/api-credits-for-subscribers)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers), feature `anthropic`
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Azure OpenAI
> Run Everruns agents on OpenAI models deployed in Azure OpenAI, using a dedicated provider type with your resource endpoint and key.
Source:
[Azure OpenAI](https://azure.microsoft.com/products/ai-services/openai-service) serves OpenAI models from your own Azure resource. Everruns ships a dedicated `azure_openai` provider type, distinct from the [OpenAI](https://everruns.com/docs/providers/openai/) provider, so Azure deployments resolve with the right endpoint and model behavior rather than being configured as a generic OpenAI base-URL override.
## What you get
* **Azure OpenAI Responses API** through your Azure resource endpoint.
* **Stateful continuation and context compaction**: Azure OpenAI hosts are recognized as stateful, like `api.openai.com`.
* **Streaming, tool calls, and reasoning** mapped to provider-neutral Everruns types.
## Configure in Everruns
1. Go to **Settings** → **Providers** and click **Add provider**.
2. Choose **Azure OpenAI** (not plain OpenAI).
3. Set the **base URL** to your Azure OpenAI resource endpoint.
4. Paste the API key for your Azure OpenAI resource.
5. Save. Note that Azure model availability depends on the deployments you have created in your resource.
## Models
Azure deployment names are operator-chosen, so a deployment whose name does not match a known model profile falls back to a minimal profile. Capability and cost metadata for recognized models come from Everruns’ built-in model profiles.
## Links
* [Azure OpenAI Service](https://azure.microsoft.com/products/ai-services/openai-service)
* [Azure AI Foundry](https://ai.azure.com/)
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# AWS Bedrock
> Run Everruns agents on models hosted in Amazon Bedrock via the ConverseStream API, with AWS credential and region resolution.
Source:
Everruns runs agents on models hosted in [Amazon Bedrock](https://aws.amazon.com/bedrock/) through the Bedrock Runtime `ConverseStream` API, mapping its provider-neutral messages, tools, and reasoning onto the Bedrock wire format.
## What you get
* **Bedrock Runtime `ConverseStream`** streaming.
* **Tool calls and reasoning** mapped to provider-neutral Everruns types.
* **AWS credential and region** resolution from explicit fields.
## Configure in Everruns
1. Go to **Settings** → **Providers** and click **Add provider**.
2. Choose **AWS Bedrock**.
3. Enter your AWS credentials as separate fields:
* **Access key ID**
* **Secret access key**
* **Region** (e.g. `us-east-1`)
* **Session token** (optional, for temporary credentials)
4. Save. Make sure the models you want are enabled in your Bedrock account.
Unlike most providers, Bedrock uses a multi-field credential rather than a single API key, so each field has its own input.
## Models
Enable the model access you need in the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/) first. Everruns resolves Bedrock model ids to its built-in model profiles for capability and cost metadata.
## Links
* [Amazon Bedrock](https://aws.amazon.com/bedrock/)
* [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers), feature `bedrock`
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Cloudflare AI Gateway
> Run Everruns agents through Cloudflare's AI REST API, reaching many upstream providers and Cloudflare's own Workers AI models with one account token.
Source:
Everruns runs agents through [Cloudflare’s AI REST API](https://developers.cloudflare.com/ai-gateway/usage/rest-api/), which fronts many upstream model providers and Cloudflare’s own Workers AI models behind one account-scoped, OpenAI-compatible endpoint. Every call routes through an AI Gateway for caching, rate limiting, and logging.
## What you get
* **Many vendors, one token**: OpenAI, Anthropic, Google, xAI and the rest of the catalog, plus Workers AI models, from a single Everruns provider.
* **No upstream keys**: Cloudflare authenticates and bills the whole call against your account, so there are no per-vendor API keys to manage.
* **Gateway controls**: response caching, rate limiting, retries, and per-request logging applied by the gateway the call routes through.
* **Full chat capabilities**: streaming, tool/function calling, and structured output, through the same uniform driver as every other provider.
* **Workers AI model discovery**: the `@cf/` catalog is synced with its advertised context windows and tool-calling support, so those ids appear in the model pickers without being typed by hand.
## Configure in Everruns
1. Create an API token with the **Account** → **Workers AI** → **Read** permission in the [Cloudflare dashboard](https://dash.cloudflare.com/profile/api-tokens).
2. Go to **Settings** → **Providers** and click **Add provider**.
3. Choose **Cloudflare AI Gateway**.
4. Enter:
* **API Token** — the token from step 1.
* **Account ID** — the account it belongs to.
* **Gateway name** — optional. Calls route through the account’s default gateway when it is unset.
5. Save.
Everruns derives the endpoint from the account id:
```plaintext
https://api.cloudflare.com/client/v4/accounts//ai/v1
```
Set a base URL only to route through a proxy in front of Cloudflare; it replaces the derived one.
## Models
Model ids are namespaced and passed through unchanged:
* Third-party: `openai/gpt-6-luna`, `anthropic/claude-opus-5` — see the [upstream providers AI Gateway supports](https://developers.cloudflare.com/ai-gateway/usage/providers/).
* Workers AI: `@cf/meta/llama-3.3-70b-instruct-fp8-fast` — see the [Workers AI model catalog](https://developers.cloudflare.com/workers-ai/models/).
Which third-party models a gateway can reach depends on the upstreams Cloudflare has enabled for your account, so verify an id with a real request rather than assuming the catalog.
Third-party models are billed to your Cloudflare account and return `402` (“Insufficient balance”) until it is funded or you configure BYOK. Workers AI models draw on the account’s own allocation instead.
Model sync covers the Workers AI half of the catalog: Everruns reads Cloudflare’s model search endpoint and keeps its text-generation models, which arrive with the context window and tool-calling support Cloudflare advertises. Third-party models are never listed — which ones an account can reach depends on what it can bill — so add those by id. Everruns matches a namespaced id against its model profile registry, so a model it recognizes still gets its capability and cost metadata.
## Why Chat Completions
Cloudflare’s REST API offers four formats. Everruns uses `/ai/v1/chat/completions` because it is the only one that serves the whole catalog and streams:
| Endpoint | Why not |
| ------------------ | ----------------------------------------------------------------------------------- |
| `/ai/v1/responses` | Rejects Workers AI models — they are not translated into the Responses shape |
| `/ai/run` | Cannot stream: with `stream: true` it returns an empty JSON body rather than events |
| `/ai/v1/messages` | Excludes Workers AI models |
The older `gateway.ai.cloudflare.com/.../compat/chat/completions` endpoint is deprecated by Cloudflare for single-model calls and kept only for dynamic routes (`dynamic/{route}`), which this provider does not target.
## Links
* [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/)
* [AI Gateway REST API](https://developers.cloudflare.com/ai-gateway/usage/rest-api/)
* [Workers AI models](https://developers.cloudflare.com/workers-ai/models/)
* [AI Gateway providers](https://developers.cloudflare.com/ai-gateway/usage/providers/)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers)
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Fireworks AI
> Run Everruns agents on open models hosted by Fireworks AI (Llama, Qwen, DeepSeek, Kimi, GLM, gpt-oss), with automatic model discovery.
Source:
Everruns runs agents on [Fireworks AI](https://fireworks.ai/) through its OpenAI-compatible Chat Completions API. Fireworks serves frontier **open models**: Llama, Qwen, DeepSeek, Kimi, GLM, gpt-oss, and more, on a fast, cost-efficient inference platform, so the same Everruns agent, prompt, and capabilities run unchanged on open weights.
## What you get
* **One key, many open models**: a single provider exposing Fireworks’ serverless model catalog.
* **Automatic model discovery**: Fireworks’ `/models` endpoint advertises rich metadata (chat, tool calling, image input, context window), which Everruns parses into capability profiles on sync, so tool and vision support surface correctly per model.
* **Full chat capabilities**: streaming, tool/function calling, vision, and structured output, through the same uniform driver as every other provider.
* **Host-gated discovery**: model sync runs only against Fireworks’ own host, so a custom proxy base URL is never probed.
## Configure in Everruns
1. Go to **Settings** → **Providers** and click **Add provider**.
2. Choose **Fireworks AI**.
3. Paste your Fireworks API key. Create one from the [Fireworks API keys page](https://fireworks.ai/account/api-keys).
4. Save. Everruns discovers available models and their capability profiles automatically.
You can optionally set a base URL to route through a proxy; leave it blank to use Fireworks’ hosted API (`https://api.fireworks.ai/inference/v1`).
## Models
Fireworks model ids are namespaced, for example `accounts/fireworks/models/llama-v3p1-70b-instruct`. After a sync, models appear in the agent and session model pickers with their discovered capabilities. Only chat models are imported, image and other non-chat endpoints are filtered out.
## Links
* [Fireworks AI](https://fireworks.ai/)
* [Fireworks docs](https://docs.fireworks.ai/)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers), feature `fireworks`
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Google Gemini
> Run Everruns agents on Google Gemini models, with streaming, tool calls, reasoning, context caching, and long decode on Gemini 4 Argon.
Source:
Everruns runs agents on [Google Gemini](https://ai.google.dev/) models, implementing the provider-neutral driver contract over the Gemini API.
## What you get
* **Gemini API** streaming.
* **Gemini 4 Argon long decode**: one response can continue across requests with a shared output budget.
* **Tool calls and reasoning** mapped to provider-neutral Everruns types.
* **Context caching**: explicit caching via `cachedContent` when a cached-content resource is supplied, otherwise Gemini’s default implicit caching on supported models.
## Configure in Everruns
1. Go to **Settings** → **Providers** and click **Add provider**.
2. Choose **Google Gemini**.
3. Paste your Gemini API key. Get one from [Google AI Studio](https://aistudio.google.com/apikey).
4. Save. Everruns discovers available Gemini models automatically.
## Models
Gemini 4 Argon is seeded as an enabled favourite. New Gemini setups prefer it when choosing an organisation default; existing defaults stay selected.
Gemini models resolve to Everruns’ built-in model profiles for capability and cost metadata. Argon defaults to 65,536 output tokens. Other Gemini models resolve their default from the model profile, falling back to a safe value.
## Long decode
For `gemini-4-argon`, set `max_tokens` above 65,536 to let a single response continue across multiple Gemini API requests. In an agent package, use the top-level `max_output_tokens`; the built-in Argon profile permits up to 1,048,576 tokens. Input, thinking, and output share that window, so leave room for the prompt. The default remains 65,536.
```rust
use everruns_contracts::{LlmCallConfig, ReasoningEffort};
let mut config = LlmCallConfig::new("gemini-4-argon");
config.max_tokens = Some(100_000);
config.reasoning_effort = Some(ReasoningEffort::High);
```
Everruns follows continuation tokens automatically, streams text and reasoning across the hops, and returns one completion. Usage sums the final counts from each hop, including billed thinking and repeated input. Context-window pressure is estimated from the assembled messages, rather than those summed billing counts.
With native Google Search enabled, citations retain the sources and supported answer spans from every hop, including when the final hop returns no grounding. Repeated search metadata within a hop is counted once; the same query in another hop counts as a separate search. Gemini 3 and later count queries for search usage, while Gemini 2.x counts grounded requests.
Argon supports low, medium, and high thinking levels; minimal maps to low and higher efforts map to high. Sampling temperature is omitted. Argon supports implicit caching only; do not supply an explicit cached-content resource.
Long-decode calls default to a 20-minute HTTP read and agent-loop stall timeout. Explicit host-level turn and stall limits still apply. A failed hop fails the call. A dropped stream resumes only when no output was exposed after its last checkpoint; otherwise it fails to prevent duplicate output. Cancelling or dropping the stream stops local continuation requests.
See Google’s [long decode guide](https://ai.google.dev/gemini-api/docs/generate-content/long-decode) for provider limits and billing.
## Links
* [Google AI for Developers](https://ai.google.dev/)
* [Google AI Studio](https://aistudio.google.com/)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers), feature `gemini`
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Microsoft MAI
> Run Everruns agents on Microsoft MAI models served via Azure AI Foundry, authenticated with an API key or Microsoft Entra ID (OAuth).
Source:
Everruns runs agents on Microsoft MAI models (for example `MAI-Code-1-Flash`), which are served via [Azure AI Foundry](https://ai.azure.com) behind an OpenAI-compatible Chat Completions API. The MAI provider exists as its own driver mainly because of its authentication options.
## What you get
* **OpenAI-compatible Chat Completions** streaming through Azure AI Foundry.
* **Two authentication schemes**: an Azure AI Foundry API key, or Microsoft Entra ID (OAuth) service-principal credentials with bearer tokens minted and refreshed automatically.
* **Model discovery** against Foundry’s `/models` endpoint where available, with capabilities supplied by Everruns’ built-in Microsoft model profiles.
## Configure in Everruns
1. Go to **Settings** → **Providers** and click **Add provider**.
2. Choose **Microsoft MAI**.
3. Set the **base URL** to your Azure AI Foundry resource (e.g. `https://.services.ai.azure.com`).
4. Provide credentials for one of the two methods, entered as discrete fields:
* **API key**: your Azure AI Foundry resource key.
* **Entra ID (OAuth)**: a client-credentials service principal: `tenant_id`, `client_id`, and `client_secret` (with optional `scope` and `authority`, which default to the Azure Cognitive Services scope and public Microsoft Entra authority).
5. Save.
Authentication is fail-closed: a stored credential is always required, and OAuth tokens are refreshed transparently for both chat execution and model sync.
## Models
Foundry’s `/models` listing is bare (ids only), so capabilities come from Everruns’ built-in Microsoft MAI model profiles, matched by id. Because Azure deployment names are operator-chosen, a deployment whose name does not match a known profile falls back to a minimal profile.
## Links
* [Azure AI Foundry](https://ai.azure.com/)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers), feature `mai`
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Meta
> Run Everruns agents on Muse Wonder and Muse Spark 1.3 through Meta Model API, including the discounted Contributor tier.
Source:
Everruns runs Muse models through [Meta Model API](https://dev.meta.ai/). The dedicated `meta` driver uses Meta’s OpenAI-compatible Responses API at `https://api.meta.ai/v1`, including streaming, parallel tool calls, reasoning replay, message phases, hosted tool search, and server-managed response history.
## Configure in Everruns
1. Create an API key in the [Meta Model API dashboard](https://dev.meta.ai/).
2. Go to **Settings** → **Providers** and click **Add provider**.
3. Choose **Meta**, paste the key, and save.
4. Sync models to import the Muse models available to your team.
The hosted endpoint is used by default. An optional base URL can point the driver at a compatible proxy; model discovery is disabled for non-Meta hosts.
## Muse Wonder
Muse Wonder (`muse-wonder-1.0`, preview) is Meta’s most capable Muse model, for complex software engineering, multi-hour agent runs and sub-agent orchestration. It has the same 1,048,576-token context window and inputs as Muse Spark 1.3, and takes the same requests, so switching is a model ID change.
| Model | Data use | Input / cached input / output per million tokens |
| ----------------- | --------------------------------------------------------- | ------------------------------------------------ |
| `muse-wonder-1.0` | Prompts and completions are not used to train Meta models | $4.00 / $0.20 / $20.00 |
Muse Wonder has no Contributor tier on pay-as-you-go. Muse Wonder Full is only available inside Meta’s Muse Code, not through the API.
## Muse Spark 1.3 tiers
Muse Spark 1.3 is built for long-horizon coding and multi-step agentic work, with native tool calling and MCP support. Both 1.3 model IDs have a 1,048,576-token context window and accept text, images, audio, video, and PDFs while producing text.
| Model | Data use | Input / cached input / output per million tokens |
| ---------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------ |
| `muse-spark-1.3` | Prompts and completions are not used to train Meta models | $1.25 / $0.15 / $4.25 |
| `muse-spark-1.3-contributor` | Prompts and completions are used to train and improve Meta models; rate-limited by tokens | $0.10 / $0.002 / $0.20 |
Choose the Contributor model only when the organization accepts its data-use terms. The distinction is part of the model ID, so changing tiers is an explicit model selection rather than a hidden provider setting.
The previous `muse-spark-1.2` and `muse-spark-1.2-contributor` IDs remain available with the same tiers and pricing.
## Reasoning effort
Every Muse model lets you pick a reasoning effort per agent or per message: Minimal, Low, Medium, High, Extra High and, on Muse Wonder and Muse Spark 1.3 (both tiers), Max. Muse Spark 1.2 stops at Extra High. No Muse model accepts `none`. Leave effort unset and Meta picks a level for the model.
## Links
* [Muse Wonder model card](https://research.meta.ai/static/muse-wonder-model-card)
* [Meta Model API documentation](https://dev.meta.ai/docs/overview/)
* [Muse Spark model page](https://developer.meta.com/ai/models/muse-spark/)
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Mistral AI
> Run Everruns agents on Mistral models (Mistral Large 4, Medium, Small, Codestral) through La Plateforme, with reasoning and automatic model discovery.
Source:
Everruns runs agents on [Mistral AI](https://mistral.ai/) models through La Plateforme’s Chat Completions API: Mistral Large 4, Mistral Medium, Mistral Small, Ministral and Codestral, with the same agent, prompt, and capabilities you run on any other provider.
## What you get
* **Mistral Large 4**: Mistral’s 1T-parameter multimodal model with a 512K context window, tool calling, image input, structured output, and reasoning.
* **Reasoning on demand**: Mistral Large 4’s thinking is on or off. Set the agent’s reasoning effort to **On** (`high`) to stream its reasoning separately from the answer; the default, **Off**, answers directly.
* **Automatic model discovery**: Mistral’s `/models` endpoint advertises chat, tool calling, vision and reasoning support per model, which Everruns turns into capability profiles on sync. Aliases are folded into one entry per model, and models Mistral has scheduled for retirement are skipped.
* **Host-gated discovery**: model sync runs only against `api.mistral.ai`, so a custom proxy base URL is never probed.
## Configure in Everruns
The Mistral AI provider is behind the `mistral` feature flag, which is off by default. A self-hosted deployment turns it on by setting `FEATURE_MISTRAL=prod` (on for every organization) or `adoption` (organizations opt in). Until then, Mistral AI does not appear in the provider picker and the API refuses to create a `mistral` provider. Mistral models remain reachable through OpenRouter either way.
1. Go to **Settings** → **Providers** and click **Add provider**.
2. Choose **Mistral AI**.
3. Paste your API key. Create one in [Mistral Studio](https://console.mistral.ai/api-keys).
4. Save. Everruns discovers the available models and their capabilities.
You can optionally set a base URL to route through a proxy; leave it blank to use `https://api.mistral.ai/v1`.
## Models
Use Mistral’s model ids, for example `mistral-large-4` (alias `mistral-large-4-0`), `mistral-medium-latest`, or `codestral-latest`. Mistral Large 4 is also reachable through [OpenRouter](https://everruns.com/docs/providers/openrouter/) as `mistralai/mistral-large-4-0`, with the same profile.
Everruns ships profiles (context, pricing, reasoning toggle) for Mistral Large 4, Mistral Medium 3.5 (`mistral-medium-2604`, also `mistral-medium-latest`), and Mistral Small 4 (`mistral-small-2603`, also `mistral-small-latest`).
## Framework
```rust
// Reads MISTRAL_API_KEY, and MISTRAL_BASE_URL when set.
let provider = everruns_drivers::mistral::from_env("mistral")?;
```
Enable the `mistral` feature of `everruns-drivers`.
## Links
* [Mistral Large 4 announcement](https://mistral.ai/news/mistral-large-4/)
* [Mistral API docs](https://docs.mistral.ai/)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers), feature `mistral`
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# OpenAI
> Run Everruns agents on OpenAI models via the Responses API or Chat Completions, including Azure OpenAI and any OpenAI-compatible endpoint.
Source:
Everruns runs agents on [OpenAI](https://openai.com/) models through a uniform driver, mapping its provider-neutral messages, tools, and reasoning onto the OpenAI wire format. The same driver also powers any **OpenAI-compatible endpoint** through a custom base URL. For Azure deployments, use the dedicated [Azure OpenAI](https://everruns.com/docs/providers/azure-openai/) provider instead.
## What you get
* **Responses API** (recommended), the default driver, with stateful continuation and server-side context compaction on `api.openai.com` and Azure OpenAI hosts.
* **Chat Completions**: a compatibility driver (`openai_completions`) for legacy integrations and OpenAI-compatible gateways.
* **Streaming, tool calls, and reasoning** mapped to provider-neutral Everruns types, including extended thinking on reasoning models.
* **Prompt caching** via a deterministic cache key derived from stable request inputs.
* **Structured output**: a direct model call can require a reply that matches a JSON Schema, enforced by OpenAI (`text.format` on the Responses API, `response_format` on Chat Completions).
* **Background mode** for `xhigh` and `max` reasoning on OpenAI and Azure: the response keeps running at OpenAI if the connection drops or the worker running the turn restarts, and Everruns picks the stream back up instead of paying for the call twice. A cancelled turn cancels the response. Background mode stores the response, so organizations with zero data retention fall back to a normal streaming call.
* **WebSocket transport (opt-in)**: on `api.openai.com`, a call can stream over OpenAI’s Responses WebSocket mode instead of HTTP streaming, keeping one connection open across the turns of a tool loop. OpenAI recommends it for the `ultrafast` speed tier. It is off by default; see [WebSocket transport](#websocket-transport).
## Configure in Everruns
1. Go to **Settings** → **Providers** and click **Add provider**.
2. Choose **OpenAI** (Responses API) for new setups, or **OpenAI Completions** for Chat Completions compatibility. For Azure, choose the dedicated [Azure OpenAI](https://everruns.com/docs/providers/azure-openai/) provider instead.
3. Paste your OpenAI API key. Get one from the [OpenAI API keys page](https://platform.openai.com/api-keys).
4. (Optional) Set a **base URL** to target another OpenAI-compatible endpoint.
5. Save. Everruns discovers available models automatically.
## OpenAI-compatible endpoints
The OpenAI driver works with any OpenAI-compatible API. Set the **base URL** to your endpoint. Self-hosted and proxy gateways are treated as stateless, so the driver replays the full transcript each turn instead of relying on server-side continuation. For Microsoft Azure deployments, use the dedicated [Azure OpenAI](https://everruns.com/docs/providers/azure-openai/) provider, which is recognized as a stateful host.
## WebSocket transport
HTTP streaming (SSE) is the default. To use OpenAI’s [WebSocket mode](https://developers.openai.com/api/docs/guides/websocket-mode) instead, opt in per call with the `openai/websocket` driver option set to `true` (`false` opts a call out), or for a whole provider when you build it in code:
```rust
use everruns_drivers::openai::OpenAIChatDriver;
let driver = OpenAIChatDriver::new().with_websocket_transport(true);
```
Each turn of a tool loop continues on the connection the previous turn used, sending only the new tool results. The transport applies to `api.openai.com` only; Azure OpenAI and custom endpoints keep using HTTP streaming. If the socket cannot connect, or drops or reports an error before the response starts, the call is sent over HTTP streaming instead, so opting in never fails a call that HTTP would have served. Background-mode calls always use HTTP.
## Models
Models are discovered when the provider is created and on each sync. OpenAI’s `/models` listing carries only identifiers, so capability and cost metadata come from Everruns’ built-in model profiles, matched by model id.
## Links
* [OpenAI Platform](https://platform.openai.com/)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers), feature `openai`
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# OpenRouter
> Run Everruns agents across OpenRouter's multi-vendor model catalog with one key, plus provider routing, fallbacks, and capacity controls.
Source:
Everruns runs agents on [OpenRouter](https://openrouter.ai/)’s model catalog through its OpenAI-compatible Responses API. One OpenRouter key gives you access to a large multi-vendor catalog, plus routing controls that decide which upstream provider actually serves each request.
## What you get
* **One key, many models**: a single provider exposing OpenRouter’s full catalog.
* **OAuth one-click connect**: authorize in the browser instead of copy-pasting an API key. See [Connecting](#connecting) below.
* **Actual-cost reporting**: OpenRouter returns the real money spent per generation, which Everruns records as authoritative cost. See [Actual cost](#actual-cost) below.
* **Built-in server tools**: let the model use OpenRouter-executed tools such as `web_search` and `web_fetch` without wiring up a separate integration. See [Server tools](#server-tools) below.
* **Provider routing**: order, allow/deny lists, data-retention and zero-data-retention policies, and price/throughput sorting.
* **Capacity strategy**: use OpenRouter’s shared capacity, prefer your own bring-your-own-key (BYOK) providers first, or require BYOK-only routing.
* **Routing presets**: high-level intents such as cheapest-with-tools, lowest-latency, or reasoning-required that compile into the underlying routing flags.
* **Capability profiling**: OpenRouter’s richer `/models` metadata is parsed into capability profiles, so reasoning support surfaces correctly even for models without a built-in profile.
* **Session grouping & logs**: Everruns forwards its session id so all generations from one session group together in OpenRouter’s dashboard, where you can inspect traces and forward them to observability tools. See [Logs, traces, and observability](#logs-traces-and-observability) below.
## Connecting
Providers are configured by organization admins under **Settings** → **Providers** → **Add provider** → **OpenRouter**. You can connect two ways:
### OAuth (recommended)
Click **Connect with OpenRouter** and authorize in the browser. OpenRouter’s one-click [PKCE](https://openrouter.ai/docs/use-cases/oauth-pkce) flow returns a user-controlled API key that Everruns stores org-wide, no key copy-pasting, and no app registration. The consent screen’s key label is preset to `Everruns `, which is also the name of the key OpenRouter creates. An admin authorizes once; everyone in the org then uses the models the provider serves against the single stored credential. The key is encrypted at rest and never returned by the API.
> OpenRouter’s callback URL must be HTTPS on port 443 or 3000 for non-localhost deployments.
### API key
Alternatively, paste an OpenRouter API key from the [OpenRouter keys page](https://openrouter.ai/keys).
Either way, save and Everruns discovers available models and their capability profiles automatically.
## Routing controls
OpenRouter-specific routing (model fallbacks, provider ordering, capacity strategy, and presets) is configured per agent and applied only to OpenRouter requests, direct OpenAI or other providers ignore these extensions. BYOK-only routing requires you to list at least one upstream provider, and fails closed if none is configured.
## Server tools
OpenRouter can run **provider-executed server tools**: `web_search`, `web_fetch`, `datetime`, `image_generation`, and more, during a generation. OpenRouter executes them server-side and folds the results into the same answer, so your agent gets built-in web reach without a separate search [integration](https://everruns.com/docs/integrations/) or an extra round-trip through Everruns.
Enable them per agent with the [OpenRouter Server Tools capability](https://everruns.com/docs/capabilities/openrouter-server-tools/). The capability is a harmless no-op on non-OpenRouter providers, so it is safe to leave on for agents that may switch providers. Because the model gains provider-executed web access, the capability is rated High risk, grant it only to agents you trust with outbound web access.
## Actual cost
OpenRouter’s API reports the **real money spent** per generation, not just an estimate. Everruns records this as the generation’s authoritative `actual_cost_usd` (sourced from OpenRouter’s `usage.cost`, reconciled against the [generation endpoint](https://openrouter.ai/docs/api-reference/get-a-generation) when needed) alongside its own estimated cost. Budgets debit the actual cost when present and fall back to the estimate otherwise, so spend tracking and [budgeting](https://everruns.com/docs/capabilities/budgeting/) reflect what OpenRouter actually charged.
## Low balance
When a request does not set a token limit, OpenRouter reserves the model’s full output limit against your credits and rejects the call with HTTP 402 (“can only afford N tokens”) if the balance cannot cover it. Set **Max output tokens** on the agent (`max_output_tokens` in the API, `--max-output-tokens` in the CLI) to a value below that number so each call fits. Everruns sends it as `max_tokens` and never asks for more than the model’s own output limit.
## Logs, traces, and observability
Everruns forwards its session id to OpenRouter, so every generation from one Everruns session groups together in OpenRouter’s dashboard. OpenRouter’s own activity logs show each request’s trace, often enough to debug a run without leaving OpenRouter. When you need richer tracing, OpenRouter can forward your generations to external observability tools such as Braintrust or LangSmith; configure that on the OpenRouter side. This complements Everruns’ own [Framework context inspection](https://everruns.com/docs/framework/agents/#inspect-effective-context) rather than replacing it.
## Links
* [OpenRouter](https://openrouter.ai/)
* [OpenRouter docs](https://openrouter.ai/docs)
* [OpenRouter Server Tools capability](https://everruns.com/docs/capabilities/openrouter-server-tools/)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers), feature `openrouter`
* [Integrations overview](https://everruns.com/docs/integrations/)
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Vercel AI Gateway
> Run Everruns agents through Vercel AI Gateway, reaching many upstream model providers over the Open Responses API with one key and automatic model discovery.
Source:
Everruns runs agents through [Vercel AI Gateway](https://vercel.com/docs/ai-gateway), which fronts many upstream model providers behind one endpoint. The gateway serves the [Open Responses](https://openresponses.org) specification, the same vendor-neutral standard Everruns prefers for new drivers, so this provider gets the richer surface rather than Chat Completions.
## What you get
* **Many vendors, one key**: OpenAI, Anthropic, Google, and the rest of the gateway’s catalog from a single Everruns provider.
* **Open Responses**: semantic streaming events, tool calling, structured output, and reasoning control over a spec Everruns implements natively.
* **Automatic model discovery**: the gateway’s `/models` endpoint is synced, so the catalog appears in the agent and session model pickers.
* **Host-gated discovery**: model sync runs only against Vercel’s own host, so a custom proxy base URL is never probed.
## Configure in Everruns
1. Create an AI Gateway API key in the [Vercel dashboard](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys).
2. Go to **Settings** → **Providers** and click **Add provider**.
3. Choose **Vercel AI Gateway**.
4. Paste the key. A Vercel OIDC token works in the same field — the gateway accepts either as a bearer token.
5. Save. Everruns discovers the available models automatically.
You can optionally set a base URL to route through a proxy; leave it blank to use the hosted gateway (`https://ai-gateway.vercel.sh/v1`).
## Models
Model ids are namespaced by upstream provider — `anthropic/claude-opus-5`, `openai/gpt-6-astra` — and are passed through to the gateway unchanged. The [AI Gateway model catalog](https://vercel.com/ai-gateway/models) lists what is available. After a sync they appear in the model pickers; Everruns matches a namespaced id against its model profile registry for capability and cost metadata.
## Links
* [Vercel AI Gateway](https://vercel.com/docs/ai-gateway)
* [Open Responses on AI Gateway](https://vercel.com/docs/ai-gateway/sdks-and-apis/openresponses)
* [AI Gateway models](https://vercel.com/ai-gateway/models)
* [`everruns-drivers` on crates.io](https://crates.io/crates/everruns-drivers)
* [Migrate between providers](https://everruns.com/docs/how-to/migrate-providers/)
---
# Agent folders and agent.toml
> Complete reference for portable agent directories, TOML fields, defaults, asset mappings and validation.
Source:
Use an `agent.toml` directory as the primary portable agent format. It holds instructions, configuration and explicitly selected runtime files. The same package loads in the Framework or serve and imports into the Platform through the UI, HTTP API, CLI, Platform Chat and MCP. ZIP carries the complete directory. See the [how-to guide](https://everruns.com/docs/how-to/define-agents-as-files/) for commands and workflows.
## Directory contract
```text
my-agent/
├── agent.toml # Agent configuration
├── instructions.md # Default instructions source
├── runbook.md # Selected runtime file
├── data/example.csv # Selected runtime file
├── src/sample.py # Only included when selected
└── .agents/skills/investigate/
├── SKILL.md # Skill instructions and metadata
├── references/checklist.md # Optional supporting reference
└── scripts/check.py # Optional supporting script
```
| Path | Role | Default behavior |
| ------------------------- | -------------------------------------- | ------------------------------------------------------------------ |
| `agent.toml` | Manifest | Exactly one manifest at the directory root |
| `instructions.md` | Agent instructions | Read when inline `instructions` and `instructions_file` are absent |
| Paths selected by `files` | Starting session files | Keep root-relative paths; read-only by default |
| `.agents/skills//` | Bundled skill | Automatically included, with supporting assets |
| Other project files | Authoring files or unrelated code/data | Omitted unless selected |
`agent.yaml`, `agent.yml`, `agent.json` and `agent.md` are alternative manifest encodings. Do not put multiple manifest filenames at the same root. ZIP contents must start at the agent root, with no enclosing `my-agent/` directory.
Instructions and metadata do not become session files automatically. To make `instructions.md` readable by a tool, explicitly select it in `files`. Runtime files named `agent.toml`, `instructions.md` or another reserved manifest filename are stored inline on export so authoring metadata cannot overwrite them.
Paths are relative to the working-directory root, independent of the execution provider. There is no `workspace/` wrapper. Agent Files are a starting snapshot; new file trees receive that snapshot once. Existing sessions and attachments to existing trees keep their current files. A provider may use a physical directory such as `/workspace`; that prefix is a runtime alias, not a package layout.
## Minimal manifest
```toml
#:schema https://everruns.com/docs/schemas/agent/v1.json
schema_version = 1
name = "my-agent"
```
Add `instructions.md` beside this file. Or keep a self-contained manifest:
```toml
schema_version = 1
name = "my-agent"
instructions = "Help the user with clear, accurate answers."
```
Use `instructions_file = "prompts/main.md"` for a different source path. Nonempty inline instructions and `instructions_file` cannot be combined.
## Manifest fields
Unknown fields fail validation. TOML keys are case-sensitive. All top-level keys must appear before the first `[table]`: a key after `[model]` belongs to the model table, rather than to the agent.
| Field | Type | Default and rules |
| --------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schema_version` | Integer | `1`; other versions fail. Include it in new definitions. |
| `name` | String | Required stable address: 1–255 lowercase letters, digits or hyphens, starting and ending with a letter or digit. No resource ID. |
| `display_name` | String | Optional human-readable title; host falls back to `name`. |
| `description` | String | Optional purpose or scope description. |
| `instructions` | String | Inline instructions; folders otherwise read `instructions_file` or `instructions.md`. |
| `instructions_file` | String | Safe package-relative instructions path; mutually exclusive with nonempty `instructions`. |
| `tags` | Array of strings | Empty; order does not affect diffs. |
| `model` | Table | Optional provider/model name pair; destination resolves the pair. |
| `harness` | String | Optional named harness; destination default when omitted. |
| `capabilities` | Array | Empty; stable names or `{ ref, config }` objects. Duplicate names fail. |
| `files` | Array | Explicit relative sources, globs, mappings or inline files; see below. |
| `skills` | Array of strings | Folder auto-discovers `.agents/skills/`; explicit values select directories containing direct skill subfolders. |
| `mcpServers` | Table of named servers | Empty; catalog references or inline MCP declarations. |
| `channels` | Table of named channels | Empty; transport descriptions without credentials or destination IDs. |
| `network_access` | Table | Optional `allowed`/`blocked` lists; host enforces the policy. |
| `max_iterations` | Integer | Runtime default; explicit values must be 1–1000. |
| `parallel_tool_calls` | Boolean | Runtime default; explicitly allow or disable concurrent independent calls. |
| `max_output_tokens` | Integer | Provider default; caps output tokens per model response (at least 1, clamped to the model’s limit). |
| `communication` | String | `direct`; `explicit` makes assistant text private notes and the agent talks through `send_message`. See [Explicit communication](https://everruns.com/docs/features/explicit-communication/). |
| `tools` | Array of tool tables | Empty; only client-side tool schemas, requiring executable host bindings. |
| `intro_markdown` | String | Optional Platform introduction displayed before a conversation. |
| `short_description` | String | Optional short Platform discovery text. |
| `starters` | Array of tables | Empty; Platform conversation starters with required `text` and optional `icon`. |
| `environments` | Table | Optional Platform environment declarations; explicit binding required in other hosts. |
Platform resolves its default model and harness when omitted. Framework code binds a model explicitly. Serve uses its simulator when a model is omitted. Framework file creation recognizes `base`, `conversation`, `worker` and `bashkit-worker` (the deprecated `worker-base` binds Bashkit Worker); custom harnesses require a host binding. Defaults that belong to the host are not frozen into portable exports.
## Select and map files
### Preserve paths
```toml
files = ["runbook.md", "data/**"]
```
Each string selects an exact file, directory or glob relative to the package root. `data/**` includes nested files. `data` also selects the directory’s files. Selected files keep their paths: `data/example.csv` becomes `data/example.csv` in the session. Every declared source must match at least one file.
`files = []` deliberately includes no ordinary files. It does not disable skill discovery. A README, source tree or neighboring agent definition is not included unless selected. Avoid `files = ["."]` in a repository unless every allowed file under that root should be packaged.
### Map a source to another destination
```toml
files = [
{ source = "fixtures", path = "data" },
{ source = "templates/report.md", path = "reports/current.md", is_readonly = false },
]
```
| File mapping key | Type | Default |
| ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `source` | String | Required safe relative file, directory or glob |
| `path` | String | Omitted: preserve source paths. For a directory, prepend the destination to its contents. For a single file, use the exact destination. |
| `is_readonly` | Boolean | `true`; false lets the session edit or delete the seeded file |
A mapping of `fixtures` to `data` makes `fixtures/a.csv` land at `data/a.csv`. A custom destination on a glob is an exact destination for every match; use a directory mapping when preserving multiple relative child paths.
### Embed content
```toml
[[files]]
path = "notes.md"
content = "Review notes go here."
is_readonly = false
[[files]]
path = "data/sample.bin"
content = "AP8K"
encoding = "base64"
```
| Inline file key | Type | Default |
| --------------- | ------- | -------------------------------------------------------------- |
| `path` | String | Required destination; prefer relative paths |
| `content` | String | Required text or Base64-encoded bytes |
| `encoding` | String | `text`; only `text` and `base64` are accepted |
| `is_readonly` | Boolean | `false` for inline files; set `true` to protect seeded content |
Source-selected files default to read-only; inline files default to writable. Set `is_readonly` explicitly when permissions matter.
Use either the `files = [...]` form or `[[files]]` tables in one TOML document, not both. Strings and inline mapping objects can be mixed in the array form. Leading `/` and `/workspace/` destination aliases are accepted; exports use relative paths. Destinations cannot traverse outside the file tree, duplicate another destination, or collide with a parent file.
## Bundle skills
Default skill discovery reads `.agents/skills//SKILL.md` and every allowed supporting asset under that skill directory. A skill must be a direct child folder, with valid YAML front matter and a `name` matching its directory:
```markdown
---
name: investigate
description: Investigate an issue using the bundled runbook and checklist.
---
Read runbook.md, then references/checklist.md. Report evidence and uncertainties.
```
Scripts, reference documents and binary assets retain their relative paths. Discovery adds the `skills` capability automatically. Including a script does not execute it; execution requires the corresponding host tool and policy.
For another source location, declare `skills = ["team-skills"]`; each child folder lands at `.agents/skills//`. When complete skill trees are explicitly included through `files`, they retain their declared permissions. Incomplete skill trees and invalid or mismatched `SKILL.md` fail validation.
## Model and capabilities
```toml
capabilities = [
"current_time",
{ ref = "session_file_system", config = {} },
]
[model]
provider = "openai"
model = "your-enabled-model"
```
`provider` and `model` must be nonempty names. Platform validation requires one enabled matching destination model. Account availability may vary. Omit this table to use the destination default rather than pinning a model.
A configured capability has `ref` (required string) and `config` (optional object, default `{}`). Configuration is validated by its host capability schema. Installed skill, MCP and plugin resource IDs are not portable capability declarations; use bundled skills or named MCP dependencies instead.
## MCP server settings
### Use an organization MCP server
```toml
[mcpServers.issues]
use = "catalog:linear"
actsAs = "user"
```
Here **catalog** means the organization’s configured MCP servers. `linear` is the server’s name at the destination; it supplies transport and authentication settings. It is a dependency, not an exported server installation or credential.
### Declare a remote or local server
```toml
[mcpServers.docs]
type = "http"
url = "https://example.com/mcp"
[mcpServers.local]
type = "stdio"
command = "python3"
args = ["-m", "my_mcp_server"]
```
HTTP uses a safe public HTTP(S) URL without embedded credentials. Stdio requires a nonempty command; hosted Platform imports reject local process execution. Framework and serve can bind stdio when their MCP stdio support is enabled.
| Server key | Type | Default and rules |
| -------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `use` | String | Optional `catalog:` reference; cannot be combined with inline `type`. |
| `type` | String | `http`; `http` or `stdio`. |
| `url` | String | Required for inline HTTP; ignored for stdio. |
| `command` | String | Required for stdio. |
| `args` | Array of strings | Empty; arguments for stdio. |
| `headers` | Table of strings | Empty; HTTP request headers. |
| `env` | Table of strings | Empty; stdio environment values. |
| `auth_mode` | String | `none`; `none`, `api_key` or `oauth`. Hosted credentials require destination bindings. |
| `actsAs` | String | `none`; `none`, `user`, `service` or `user_or_service` (the person’s grant, else the agent’s), identifying whose grant to use. |
| `deferred` | Boolean | `false`; `true` lists the server’s tools only when the agent loads them through tool search. |
| `connectInChat` | String | `ask`; `ask` pauses the chat on a Connect card when a sign-in is missing, `never` fails the call with a settings link instead. |
| `oauth_provider_id` | String | Optional named provider requirement; installed MCP resource IDs are rejected. |
| `tool_discovery` | Boolean | `true`; discover server tools. |
| `tool_labels` | Table of strings | Empty; risk label per tool name, `read_only` (never asks for approval) or `changes` (always asks). Wins over the tool’s own hints and a catalog server’s labels. |
| `protocol_mode` | String | `auto`; or pin `2025-03-26`, `2025-06-18`, `2026-07-28`. |
| `elicitation_policy` | String | `url`; `url`, `url_and_form` or `none`. |
Use `${ENV_NAME}` for credential-bearing headers/environment values:
```toml
[mcpServers.docs.headers]
Authorization = "${DOCS_AUTHORIZATION}"
```
Framework resolves placeholders through `package.bind_mcp(...)`; serve resolves explicit named requirements at startup. Platform requires catalog bindings for credentials and never reads arbitrary worker environment variables on import.
## Channel descriptions
```toml
[channels.chat]
type = "ag_ui"
enabled = false
[channels.chat.config]
tool_visibility = "generic"
generic_tool_text = "Working…"
rate_limit_per_minute = 30
```
| Channel key | Type | Default and rules |
| ----------- | ----------- | ------------------------------------------------------------------------------------------------------ |
| Table name | Stable name | Lookup/upsert name at the destination; no channel ID. |
| `type` | String | Required known transport: `ag_ui`, `public_chat`, `slack`, `fcp`, `a2a`, `api`, `schedule`, `webhook`. |
| `enabled` | Boolean | `false`; true requests an enabled draft, not automatic publication. |
| `config` | Table | `{}`; transport-specific declarative settings without credentials or resource IDs. |
Platform imports create `ag_ui`, `public_chat`, `fcp` and `slack` channels. Public Chat also requires its feature flag. Other known types require their host/dedicated APIs; Platform validation rejects unsupported bindings. Schedules remain trigger resources. Framework and serve bind transports separately. See [Channels](https://everruns.com/docs/features/channels/) for transport-specific settings.
Updates upsert declared channels, preserve omitted channels and existing activation/credentials, and require permission to modify live configuration. Packages do not transfer publication, grants, sessions, history or deployment state.
## Optional policies and Platform presentation
```toml
intro_markdown = "Welcome to the incident desk."
short_description = "Investigate incidents with evidence."
starters = [{ text = "Investigate the current incident.", icon = "search" }]
[network_access]
allowed = ["https://everruns.com/docs/**"]
blocked = ["https://example.com/private/**"]
```
`blocked` takes precedence. An empty `allowed` list imposes no allowlist restriction. Host policy can further restrict access. Network configuration is descriptive until an enforcing host binds it.
`sandbox_policy` retains the Platform’s Sandbox policy; see [Sandbox Templates](https://everruns.com/docs/features/sandbox-templates/) for the host configuration. The generic Framework package builder rejects this policy until explicitly bound.
A `tools` declaration contains a `client_side` type, `name`, `description` and object JSON Schema `parameters`. Optional display metadata and execution hints follow the [tool schema in the public agent schema](https://everruns.com/docs/schemas/agent/v1.json). The Framework requires a matching host handler and schema; declarations do not transfer executable closures. Built-in tools are selected through capabilities.
## Validation and limits
| Limit | Value |
| --------------------------------------- | ------------------------- |
| Package bytes | 10 MiB |
| Runtime files | 100 |
| Each asset / instructions | 1 MiB |
| Total decoded runtime file content | 5 MiB |
| Channel descriptions | 32 |
| Native folder nesting / visited entries | 32 levels / 1,024 entries |
Validation rejects unknown fields, unsupported versions, unsafe source paths, traversal, symlinks, duplicate or parent-conflicting destinations, malformed Base64, incomplete skills, literal credentials and nonportable resource IDs. Folder/glob collection skips disallowed hidden paths, including `.env`, `.ssh` and `.git`; explicit credential-file sources fail. Explicit inline content is already authored data and is not a host-file read. Review it before sharing.
Editor schema validation cannot inspect file bytes, symlinks, decoded sizes, credential policy or destination dependencies. Run:
```bash
everruns agents validate ./my-agent
everruns agents validate ./my-agent --remote
```
## Public schema and editor integration
Download the [v1 agent schema](https://everruns.com/docs/schemas/agent/v1.json). It is a standalone JSON Schema for the parsed TOML document, with named definitions for file forms, capabilities, MCP settings and channels. YAML and JSON use the same shape. It is generated from the public manifest types, with portable validation bounds.
Use this header for completion and diagnostics in editors supporting the [Taplo schema directive](https://taplo.tamasfe.dev/configuration/directives.html):
```toml
#:schema https://everruns.com/docs/schemas/agent/v1.json
```
This is a comment, not an agent field. Do not add a `$schema` field to the manifest: unknown keys are rejected. Offline editors can download the schema and use `#:schema ./agent-v1.schema.json` instead.
## Compatibility
Legacy Markdown with YAML front matter still imports; its body becomes instructions. `system_prompt`, `initial_files`, `harness_name` and `mcp_servers` remain input aliases. Unversioned legacy files may contain platform IDs; new versioned definitions and exports omit them. The public schema describes canonical authoring, rather than every legacy input alias.
Old `files/` contents map to the runtime root when `files`/`initial_files` is omitted. Explicit `files = []` disables that discovery. Old `skills/` maps to `.agents/skills/`. If both skill roots exist, explicitly choose one using `skills = [".agents/skills"]`. New exports use root-relative files.
---
# CLI command reference
> Every everruns CLI command, grouped by what it manages, with flags and a worked example.
Source:
Every command below is also in `everruns --help`, and an agent running on Everruns types the same words in its shell. Install the CLI and sign in first; see [CLI](https://everruns.com/docs/features/cli/).
## Flags every command takes
| Flag | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `--reason ` | Why you are making this change. Recorded in the changed entity’s history; give one on every command that changes something. |
| `--context-revision ` | The revision of the entity’s manager notes you read (`everruns context get`). The change is refused if the notes changed since. |
| `-o`, `--output ` | `text` (default), `json` or `yaml`. |
| `-q`, `--quiet` | Print only the essential identifier, for capturing ids in scripts. |
## Session and account
| Command | What it does |
| ----------------- | --------------------------------------------------------- |
| `everruns chat` | Send a message and stream the response |
| `everruns login` | Interactive login (localhost OAuth callback) |
| `everruns logout` | Remove stored credentials |
| `everruns status` | Show current user and org |
| `everruns whoami` | Show who the server says you are, and your token’s access |
## Command groups
| Group | What it manages |
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| [`agents`](https://everruns.com/docs/reference/cli/agents/) | Agent definitions and their configuration |
| [`budgets`](https://everruns.com/docs/reference/cli/budgets/) | Spending limits for sessions, agents, users and organizations. |
| [`capabilities`](https://everruns.com/docs/reference/cli/capabilities/) | Capabilities agents can use, including declarative ones you define. |
| [`connections`](https://everruns.com/docs/reference/cli/connections/) | Your LLM provider API keys |
| [`context`](https://everruns.com/docs/reference/cli/context/) | Notes managers keep about an entity, read by agents that manage it. |
| [`credential-bindings`](https://everruns.com/docs/reference/cli/credential-bindings/) | Declare, list and revoke write-only credential bindings. |
| [`docker`](https://everruns.com/docs/reference/cli/docker/) | Run Everruns on this machine with Docker |
| [`durable`](https://everruns.com/docs/reference/cli/durable/) | Durable scheduled tasks and their executions. |
| [`evals`](https://everruns.com/docs/reference/cli/evals/) | Evaluation cases, runs, results and scores. |
| [`files`](https://everruns.com/docs/reference/cli/files/) | Sync files between a local folder and a session |
| [`harnesses`](https://everruns.com/docs/reference/cli/harnesses/) | Reusable base setups (prompt plus capabilities) agents build on. |
| [`health-issues`](https://everruns.com/docs/reference/cli/health-issues/) | Operational problems detected in this installation. |
| [`history`](https://everruns.com/docs/reference/cli/history/) | Recorded changes to entities: list, compare, restore. |
| [`images`](https://everruns.com/docs/reference/cli/images/) | Uploaded images. |
| [`knowledge`](https://everruns.com/docs/reference/cli/knowledge/) | File collections with optional indexed retrieval. |
| [`mcp-servers`](https://everruns.com/docs/reference/cli/mcp-servers/) | Registered MCP servers available to agents and sessions. |
| [`memories`](https://everruns.com/docs/reference/cli/memories/) | Workspace memories in the organization. |
| [`models`](https://everruns.com/docs/reference/cli/models/) | LLM models available to agents, and organization defaults. |
| [`notifications`](https://everruns.com/docs/reference/cli/notifications/) | Your in-app notifications. |
| [`observers`](https://everruns.com/docs/reference/cli/observers/) | Online scoring of live traces. |
| [`orgs`](https://everruns.com/docs/reference/cli/orgs/) | Organizations you belong to, and which one is active |
| [`payments`](https://everruns.com/docs/reference/cli/payments/) | Machine payments: wallets, spend policies and attempts. |
| [`plugin-marketplaces`](https://everruns.com/docs/reference/cli/plugin-marketplaces/) | Sources of installable plugins. |
| [`plugins`](https://everruns.com/docs/reference/cli/plugins/) | Plugins installed in this organization. |
| [`providers`](https://everruns.com/docs/reference/cli/providers/) | LLM providers and their credentials. |
| [`reports`](https://everruns.com/docs/reference/cli/reports/) | Usage and cost reporting: queries, saved reports, catalog. |
| [`resources`](https://everruns.com/docs/reference/cli/resources/) | Access to credentials and connections. |
| [`sandbox-targets`](https://everruns.com/docs/reference/cli/sandbox-targets/) | Sandbox providers this deployment offers. |
| [`sandboxes`](https://everruns.com/docs/reference/cli/sandboxes/) | Sandboxes across providers: state, history, usage. |
| [`sessions`](https://everruns.com/docs/reference/cli/sessions/) | Running and archived sessions, their state and participants |
| [`skills`](https://everruns.com/docs/reference/cli/skills/) | Skill packages and their content. |
| [`system`](https://everruns.com/docs/reference/cli/system/) | Server status. |
| [`tasks`](https://everruns.com/docs/reference/cli/tasks/) | Background tasks across every session in the organization. |
| [`user`](https://everruns.com/docs/reference/cli/user/) | Your own account settings. |
| [`users`](https://everruns.com/docs/reference/cli/users/) | People in the current organization. |
| [`virtual-users`](https://everruns.com/docs/reference/cli/virtual-users/) | Runtime accounts for consumers or services. |
| [`workspaces`](https://everruns.com/docs/reference/cli/workspaces/) | Durable working areas holding the files agents work on. |
---
# everruns agents
> Agent definitions and their configuration. CLI reference for everruns agents: usage, flags and examples.
Source:
Agent definitions and their configuration.
| Command | What it does |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`agents import`](#agents-import) | Import a portable agent file, folder or ZIP. |
| [`agents export`](#agents-export) | Export an agent by name without IDs or credentials. |
| [`agents validate`](#agents-validate) | Validate a package locally, or also check destination dependencies. |
| [`agents diff`](#agents-diff) | Generate a semantic diff against a local package or a remote agent. |
| [`agents create`](#agents-create) | Create a new agent (upserts if id: is present in frontmatter) |
| [`agents update`](#agents-update) | Update an existing agent from a file definition. |
| [`agents analyze`](#agents-analyze) | Run advisory checks (built-in rules plus LLM analysis) against an agent configuration. |
| [`agents check-name`](#agents-check-name) | Check whether an agent name is available. |
| [`agents copy`](#agents-copy) | Copy an agent. |
| [`agents delete`](#agents-delete) | Archive an agent (soft delete). |
| [`agents destroy`](#agents-destroy) | Permanently delete an archived agent. |
| [`agents draft`](#agents-draft) | Revise an agent draft from a conversation with the agent builder. |
| [`agents get`](#agents-get) | Get a single agent by ID or name. |
| [`agents list`](#agents-list) | List all active agents. |
| [`agents preview`](#agents-preview) | Preview the final agent shape with capabilities applied. |
| [`agents upsert`](#agents-upsert) | Upsert agent — create (201) or update (200) by ID. |
| [`agents avatar presets`](#agents-avatar-presets) | List the curated agent avatars: stable id, name, family, suggested role, description and search keywords. |
| [`agents avatar set`](#agents-avatar-set) | Set an agent’s avatar from a curated preset (preset_id, see list_avatar_presets) or an image (image_base64: PNG, JPEG, GIF or WebP, at least 64x64, at most 1 MB). |
| [`agents channels create`](#agents-channels-create) | Create an ingress channel for an agent. |
| [`agents channels delete`](#agents-channels-delete) | Delete an agent ingress channel. |
| [`agents channels get`](#agents-channels-get) | Get an agent ingress channel. |
| [`agents channels list`](#agents-channels-list) | List an agent’s ingress channels. |
| [`agents channels publish`](#agents-channels-publish) | Publish an agent ingress channel. |
| [`agents channels trigger`](#agents-channels-trigger) | Run an agent schedule channel now. |
| [`agents channels unpublish`](#agents-channels-unpublish) | Unpublish an agent ingress channel. |
| [`agents channels update`](#agents-channels-update) | Update an agent ingress channel. |
| [`agents channels keys create`](#agents-channels-keys-create) | Create an agent key for an agent’s API channel. |
| [`agents channels keys list`](#agents-channels-keys-list) | List the agent keys of an agent’s API channel. |
| [`agents channels keys revoke`](#agents-channels-keys-revoke) | Revoke an agent key. |
| [`agents channels keys rotate`](#agents-channels-keys-rotate) | Replace an agent key’s secret, keeping its id. |
| [`agents check-rules delete`](#agents-check-rules-delete) | Delete an agent check rule (built-in override or custom rule). |
| [`agents check-rules list`](#agents-check-rules-list) | List the org’s agent check rules: built-in rules with their effective enabled/severity, plus custom rules. |
| [`agents check-rules upsert`](#agents-check-rules-upsert) | Create or update an agent check rule (built-in override or custom rule). |
| [`agents credentials create`](#agents-credentials-create) | Create a write-only Agent credential setup requirement: one attached MCP tool parameter, or an HTTP API the agent’s shell calls with $ENV_NAME (the value is injected only into requests to the bound URL). |
| [`agents examples adopt`](#agents-examples-adopt) | Create an agent from an agent example by name (see list_agent_examples). |
| [`agents examples list`](#agents-examples-list) | List adoptable agent examples (blueprints such as pr-reviewer): name, description, harness, capabilities, and the guided setup a template needs after adoption (GitHub App, connections, trigger). |
| [`agents exposures resume`](#agents-exposures-resume) | Let an agent’s live channels accept traffic again. |
| [`agents exposures suspend`](#agents-exposures-suspend) | Stop every channel on an agent from accepting traffic. |
| [`agents failures list`](#agents-failures-list) | List sessions with failed turns. |
| [`agents github connect`](#agents-github-connect) | Start connecting an agent to GitHub through its own GitHub App. |
| [`agents github disconnect`](#agents-github-disconnect) | Disconnect an agent from GitHub: uninstall its GitHub App and remove the connection, keeping the App for a later reconnect. |
| [`agents github get`](#agents-github-get) | Show an agent’s own GitHub App: whether it exists, whether it is installed, on which account, and which permissions it lacks. |
| [`agents github use-everruns-app`](#agents-github-use-everruns-app) | Make an agent act on GitHub as the shared Everruns App, through one installation an org admin linked in Settings > Organization. |
| [`agents health-checks get`](#agents-health-checks-get) | Get a single agent health check run with its results. |
| [`agents health-checks list`](#agents-health-checks-list) | List recent health check runs for an agent. |
| [`agents health-checks trigger`](#agents-health-checks-trigger) | Run a behavioral health check (generated smoke tests) against an agent. |
| [`agents health-checks latest get`](#agents-health-checks-latest-get) | Get the latest health check run for an agent, with a stale-config flag. |
| [`agents scripts create`](#agents-scripts-create) | Create a saved shell script an agent owns. |
| [`agents scripts delete`](#agents-scripts-delete) | Archive a saved script and free its name. |
| [`agents scripts get`](#agents-scripts-get) | Get a single saved script by id. |
| [`agents scripts list`](#agents-scripts-list) | List an agent’s saved scripts with their bodies. |
| [`agents scripts update`](#agents-scripts-update) | Update a saved script’s description, input schema or body. |
| [`agents source connect`](#agents-source-connect) | Apply a reviewed GitHub package and enable continuous sync. |
| [`agents source disconnect`](#agents-source-disconnect) | Disconnect GitHub source sync and restore local editing. |
| [`agents source get`](#agents-source-get) | Show the Agent’s continuous GitHub source binding and sync status. |
| [`agents source preview`](#agents-source-preview) | Validate and compare a GitHub Agent package without changing the Agent. |
| [`agents source sync`](#agents-source-sync) | Queue an immediate check of this Agent’s GitHub source. |
| [`agents source options get`](#agents-source-options-get) | Browse GitHub branches and Agent package paths. |
| [`agents triggers create`](#agents-triggers-create) | Create a schedule or webhook trigger for an agent. |
| [`agents triggers delete`](#agents-triggers-delete) | Archive an agent trigger and remove its durable schedule. |
| [`agents triggers get`](#agents-triggers-get) | Get a single agent trigger by id. |
| [`agents triggers list`](#agents-triggers-list) | List an agent’s triggers. |
| [`agents triggers trigger`](#agents-triggers-trigger) | Manually fire an agent trigger once. |
| [`agents triggers update`](#agents-triggers-update) | Update an agent trigger. |
| [`agents triggers deliveries list`](#agents-triggers-deliveries-list) | List recent events delivered to an agent trigger: dispatched, filtered, duplicate or failed. |
| [`agents triggers runs list`](#agents-triggers-runs-list) | List recent execution outcomes for an agent trigger. |
## agents import
Import a portable agent file, folder or ZIP.
```bash
everruns agents import [OPTIONS] [PATH]
```
| Flag | Description |
| --------------------- | ----------------------------------------------------------- |
| `` | |
| `--content ` | Inline definition (also supported by Platform Chat and MCP) |
| `--target ` | Explicit existing agent name to update. |
| `--format ` | |
## agents export
Export an agent by name without IDs or credentials.
```bash
everruns agents export [OPTIONS]
```
| Flag | Description |
| ------------------- | ----------------------------------------------------------- |
| `` | Required. |
| `--format ` | One of `markdown`, `toml`, `yaml`, `json`, `zip`, `folder`. |
| `--out ` | |
## agents validate
Validate a package locally, or also check destination dependencies.
```bash
everruns agents validate [OPTIONS]
```
| Flag | Description |
| ---------- | ----------- |
| `` | Required. |
| `--remote` | |
## agents diff
Generate a semantic diff against a local package or a remote agent.
```bash
everruns agents diff [OPTIONS]
```
| Flag | Description |
| --------------------- | ----------- |
| `` | Required. |
| `--against ` | |
| `--target ` | |
## agents create
Create a new agent (upserts if id: is present in frontmatter)
```bash
everruns agents create [OPTIONS]
```
| Flag | Description |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `-f`, `--file ` | TOML/YAML/JSON/Markdown file with agent definition. |
| `--initial-files-dir ` | Directory of files to upload as initial_files (read-only by default) |
| `--writable` | Make initial files writable (default: read-only) |
| `--name ` | Agent name (required if no —file) |
| `--instructions ` | System prompt (required if no —file) |
| `--description ` | Agent description. |
| `--model ` | Default model ID (e.g. mod_xxx) |
| `-H`, `--harness ` | Harness ID or name (e.g. harness_xxx or “generic”). Omit to default to the org’s generic harness. |
| `-t`, `--tag ` | Tag. Repeat for each, or pass one JSON array of strings. Repeatable. |
## agents update
Update an existing agent from a file definition.
```bash
everruns agents update [OPTIONS] [AGENT_ID]
```
| Flag | Description |
| ----------------------------------------- | --------------------------------------------------------------------- |
| `` | Agent ID (e.g. agent_xxx). If omitted, uses id from file frontmatter. |
| `-f`, `--file ` | TOML/YAML/JSON/Markdown file with agent definition. |
| `--initial-files-dir ` | Directory of files to upload as initial_files (read-only by default) |
| `--writable` | Make initial files writable (default: read-only) |
| `--name ` | Agent name. |
| `--instructions ` | System prompt. |
| `--description ` | Agent description. |
| `--model ` | Default model ID (e.g. mod_xxx) |
| `-H`, `--harness ` | Harness ID or name (e.g. harness_xxx or “generic”) |
| `-t`, `--tag ` | Tag. Repeat for each, or pass one JSON array of strings. Repeatable. |
## agents analyze
Run advisory checks (built-in rules plus LLM analysis) against an agent configuration.
```bash
everruns agents analyze [OPTIONS]
```
| Flag | Description |
| --------------------------------- | ----------- |
| `--capabilities ` | |
| `--harness-id ` | |
| `--initial-files ` | |
| `--mcp-servers ` | |
| `--system-prompt ` | |
| `--tools ` | |
Example:
```bash
# Check a draft configuration for problems before creating the agent
everruns agents analyze --system-prompt 'Triage incoming issues' --tools '["bash"]'
```
## agents check-name
Check whether an agent name is available.
```bash
everruns agents check-name [OPTIONS] --name
```
| Flag | Description |
| --------------------------- | ------------------------------ |
| `--exclude-id ` | |
| `--name ` | Required. Human-readable name. |
Example:
```bash
# See whether a name is free before creating an agent
everruns agents check-name --name triage
```
## agents copy
Copy an agent. Generates a unique name.
```bash
everruns agents copy [OPTIONS] [ID]
```
| Flag | Description |
| ----------- | --------------------------- |
| `--id ` | Prefixed public identifier. |
Example:
```bash
# Duplicate an agent to try a change without touching the original
everruns agents copy agent_01h9 --reason 'Trial a stricter prompt'
```
## agents delete
Archive an agent (soft delete). Can be restored.
```bash
everruns agents delete [OPTIONS] [ID]
```
| Flag | Description |
| ----------- | --------------------------- |
| `--id ` | Prefixed public identifier. |
Example:
```bash
# Archive an agent, keeping it restorable
everruns agents delete agent_01h9 --reason 'Replaced by triage-v2'
```
## agents destroy
Permanently delete an archived agent.
```bash
everruns agents destroy [OPTIONS] [ID]
```
| Flag | Description |
| ----------- | --------------------------- |
| `--id ` | Prefixed public identifier. |
Example:
```bash
# Permanently remove an already-archived agent
everruns agents destroy agent_01h9 --reason 'Retired after the archive window'
```
## agents draft
Revise an agent draft from a conversation with the agent builder. Returns the draft only; nothing is created.
```bash
everruns agents draft [OPTIONS] --messages
```
| Flag | Description |
| ----------------------- | -------------------------------------------- |
| `--draft ` | |
| `--messages ` | Required. Conversation so far, oldest first. |
Example:
```bash
# Ask the builder for a first draft
everruns agents draft --messages '[{"role":"user","content":"Triage new GitHub issues every morning"}]'
```
## agents get
Get a single agent by ID or name.
```bash
everruns agents get [OPTIONS] [ID]
```
| Flag | Description |
| ----------- | --------------------------- |
| `--id ` | Prefixed public identifier. |
Example:
```bash
# Show one agent's full configuration
everruns agents get agent_01h9
```
## agents list
List all active agents. Use search for name search, include_archived=true to include archived. Supports pagination (limit/offset).
```bash
everruns agents list [OPTIONS]
```
| Flag | Description |
| -------------------- | ---------------------------------------------- |
| `--include-archived` | |
| `--limit ` | Maximum number of items returned in this page. |
| `--offset ` | Zero-based offset into the result set. |
| `--search ` | |
Example:
```bash
# Find agents by name when you do not know the id
everruns agents list --search triage --limit 20
```
## agents preview
Preview the final agent shape with capabilities applied.
```bash
everruns agents preview [OPTIONS]
```
| Flag | Description |
| --------------------------------- | ----------- |
| `--capabilities ` | |
| `--harness-id ` | |
| `--initial-files ` | |
| `--mcp-servers ` | |
| `--system-prompt ` | |
| `--tools ` | |
Example:
```bash
# See the prompt a draft configuration would produce, without creating it
everruns agents preview --system-prompt 'Triage incoming issues'
```
## agents upsert
Upsert agent — create (201) or update (200) by ID.
```bash
everruns agents upsert [OPTIONS] --name --system-prompt [ID]
```
| Flag | Description |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `--id ` | Client-supplied agent ID (format: agent\_{32-hex}). If not provided, one is auto-generated. |
| `--capabilities ` | Capabilities to enable for this agent with per-agent configuration. Each capability has a \`re… |
| `--communication ` | How an agent talks to the people in its conversations. One of `direct`, `explicit`. |
| `--default-model-id ` | The ID of the default LLM model to use for this agent. If not specified, the system default m… |
| `--description ` | A human-readable description of what the agent does. |
| `--display-name ` | Human-readable display name shown in UI. Falls back to `name` when absent. |
| `-H`, `--harness ` | Addressable harness name. |
| `--harness-id ` | Harness ID used as this agent’s base execution environment. |
| `--initial-files ` | Starter files copied into each new session for this agent. |
| `--intro-markdown ` | Markdown intro shown as an intro box on a fresh Platform Chat thread. Images are allowed. |
| `--max-iterations ` | Maximum number of LLM iterations per turn for this agent. |
| `--max-output-tokens ` | Ceiling on output tokens per model response (sent as `max_tokens`). Omit to use the provider … |
| `--mcpServers ` | |
| `--name ` | Required. Name, unique per org. |
| `--network-access ` | |
| `--parallel-tool-calls` | Request-level parallel tool calling preference (EVE-598). |
| `--sandbox-policy ` | |
| `--service-virtual-user-id ` | |
| `--short-description ` | One-line description in simplified Markdown, shown below the chat title once the intro is hid… |
| `--starters ` | Conversation starters for a fresh Platform Chat thread. |
| `--system-prompt ` | Required. The system prompt that defines the agent’s behavior and capabilities. This is sent as the fir… |
| `--tags ` | Tags for organizing and filtering agents. Comma-separated or a JSON array of strings. Repeatable. |
| `--tools ` | Client-side tools for this agent. These tools are sent to the LLM but executed by the client,… |
Example:
```bash
# Create or replace an agent at a known id, for a scripted deploy
everruns agents upsert agent_01h9 --name triage --system-prompt 'Triage incoming issues' --reason 'Deploy release 42'
```
## agents avatar presets
List the curated agent avatars: stable id, name, family, suggested role, description and search keywords. Pass an id to set_agent_avatar as preset_id. Preview one at /v1/avatar-presets/{id}/square-256.png.
```bash
everruns agents avatar presets [OPTIONS]
```
Example:
```bash
# Find a curated avatar to give an agent
everruns agents avatar presets
```
## agents avatar set
Set an agent’s avatar from a curated preset (preset_id, see list_avatar_presets) or an image (image_base64: PNG, JPEG, GIF or WebP, at least 64x64, at most 1 MB). Replaces the previous avatar and updates the icon of the agent’s one-click Slack apps.
```bash
everruns agents avatar set [OPTIONS] [AGENT]
```
| Flag | Description |
| ------------------------------- | ---------------------------------------------------------------------------------------------- |
| `--agent