# Default harness

> Built-in agent loop, shipped tools (bash, file ops, web_fetch, todo, ask_question, agent, load_skill, connection_search), compaction defaults, and override/disable patterns.

- Repository: vercel/eve
- GitHub: https://github.com/vercel/eve
- Human docs: https://www.grok-wiki.com/public/docs/vercel-eve-759e1d74a10f
- Complete Markdown: https://www.grok-wiki.com/public/docs/vercel-eve-759e1d74a10f/llms-full.txt

## Source Files

- `docs/concepts/default-harness.md`
- `packages/eve/src/public/tools/index.ts`
- `packages/eve/src/compiler/normalize-agent-config.ts`
- `packages/eve/src/execution/sandbox/bash-tool.ts`
- `packages/eve/src/execution/sandbox/read-file-tool.ts`
- `docs/agent-config.md`

---

---
title: "Default harness"
description: "Built-in agent loop, shipped tools (bash, file ops, web_fetch, todo, ask_question, agent, load_skill, connection_search), compaction defaults, and override/disable patterns."
---

Every Eve agent runs on the framework-owned default harness: a durable tool-loop step (`createToolLoopHarness` in `packages/eve/src/harness/tool-loop.ts`) backed by the AI SDK `ToolLoopAgent`, wired per runtime node in `createExecutionNodeStep`. The harness owns turn execution—model calls, tool dispatch, compaction, HITL parking, and stream emission—while authored files under `agent/` supply instructions, tools, connections, and configuration that merge into the resolved toolset at graph build time.

```mermaid
flowchart TB
  subgraph authored ["Authored agent/"]
    instructions["instructions.md/ts"]
    tools["tools/*.ts"]
    connections["connections/*.ts"]
    skills["skills/"]
    agentTs["agent.ts"]
  end

  subgraph compile ["Compile + resolve"]
    graph["resolve-agent-graph"]
    registry["toolRegistry + dynamicToolResolvers"]
  end

  subgraph harness ["Default harness"]
    step["createToolLoopHarness"]
    loop["ToolLoopAgent loop"]
    compact["compaction + state preservation"]
  end

  subgraph surfaces ["Execution surfaces"]
    sandbox["Sandbox proxy: bash, read/write, glob, grep"]
    runtime["App runtime: web_fetch, todo, load_skill, agent"]
    provider["Provider: web_search"]
    dynamic["Dynamic: connection__search + discovered tools"]
  end

  authored --> graph --> registry --> step
  step --> loop
  loop --> compact
  loop --> surfaces
  agentTs --> graph
```

<Info>
The harness is separate from channels (ingress) and the durable workflow runtime (checkpoint/resume). Turn nesting, parked work, and continuation semantics live on the execution-model page; this page covers what the loop ships and how to reshape it.
</Info>

## Agent loop

Each resolved runtime node gets one harness step function. `createExecutionNodeStep` builds a unified `HarnessToolMap` from the node's tool registry and subagent registry, injects the built-in `agent` delegation tool when no tool with that name already exists, and passes the map into `createToolLoopHarness` along with compaction hooks, model resolution, and optional code-mode / Workflow surfaces.

Per step, the harness:

1. Assembles the effective toolset (`buildToolSetWithProviderTools`), including dynamic tools resolved at `step.started` and provider-managed replacements for `web_search`.
2. Runs the model through `ToolLoopAgent`, executing tool calls until the turn completes, parks for HITL/OAuth/subagent input, or hits a terminal error.
3. Evaluates compaction when estimated input tokens exceed the session threshold; on compaction, calls `preserveFrameworkStateOnCompaction` to reset read-before-write stamps and re-inject the active todo list.
4. Emits NDJSON stream events (`step.started`, tool results, `compaction.requested`, `compaction.completed`, `input.requested`, and terminal events).

Tools without a local `execute` function (notably `ask_question`) are client-side: the model can call them, but the runtime parks until the channel delivers user input.

## Compaction defaults

Compaction is on by default for every session. Configuration is authored in `agent/agent.ts` via `defineAgent({ compaction: { ... } })` and compiled into the agent manifest.

| Setting | Default | Behavior |
| --- | --- | --- |
| `compaction.thresholdPercent` | `0.9` | Fraction of the primary model context window that triggers summarization |
| Recent window | `10` messages | Harness keeps the tail of history verbatim; older messages are summarized |
| Compaction summary model | Active turn model | Override with `compaction.model` to use a different model for summaries only |
| Fallback threshold | `100_000` tokens | Used when context-window metadata is unavailable at session creation |

When compaction runs, the harness calls a dedicated summarizer (`compactMessages`) with a fixed system prompt, then appends the summary as a compact checkpoint message. Framework-owned state is preserved automatically—there is no per-tool compaction hook:

- **Read-before-write tracking** is cleared so a subsequent `write_file` must re-read any file whose read evidence was summarized away.
- **Todo list** is re-injected as a user message when non-empty items exist in durable `TodoStateKey`.

Tune compaction in `agent.ts`:

```ts title="agent/agent.ts"
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-opus-4.8",
  compaction: {
    thresholdPercent: 0.75,
    // model: "anthropic/claude-sonnet-4.6", // optional summary-only model
  },
});
```

Stream consumers see `compaction.requested` before the summary call and `compaction.completed` after the checkpoint message is appended.

## Built-in tools

These tools ship with every agent without imports. At graph resolution, framework defaults merge with authored tools: a file at `agent/tools/<slug>.ts` with the same slug replaces the built-in; a `disableTool()` sentinel removes it. Discovery (`eve info`, `eve build`) surfaces descriptors but never executes tools.

The harness shows descriptors to the model first and executes only what the model calls.

| Tool | Purpose | Where it runs |
| --- | --- | --- |
| `bash` | Run a shell command in the workspace | Sandbox (proxied via `executeBashOnSandbox`) |
| `read_file` | Read a text file with line-numbered output; records read stamps | Sandbox FS |
| `write_file` | Write a complete file; enforces read-before-write and stale-read detection | Sandbox FS |
| `glob` | Find files by glob pattern | Sandbox FS |
| `grep` | Search file contents by regex | Sandbox FS |
| `web_fetch` | Fetch a URL (markdown/text/html) | App runtime |
| `web_search` | Search the web via the model provider | Provider (no local `execute`; injected per step) |
| `todo` | Maintain a durable per-session todo list | App runtime (`TodoStateKey`) |
| `ask_question` | Ask the user a clarifying question mid-turn; parks until answered | App runtime (client-side, no `execute`) |
| `agent` | Delegate a subtask to a copy of the current agent | App runtime (subagent-call runtime action) |
| `load_skill` | Pull on-demand skill instructions into the current turn | App runtime (reads `SKILL.md` from sandbox) |
| `connection__search` | Discover tools across declared connections | App runtime (dynamic resolver, connections only) |

### Sandbox and file tools

Shell and filesystem tools (`bash`, `read_file`, `write_file`, `glob`, `grep`) are defined in the app runtime but proxy work into the agent's single sandbox session. `bash` tail-truncates stdout/stderr to shared output limits. `read_file` defaults to `offset: 1`, `limit: 2000` lines, rejects binary files (NUL bytes), and persists a full-file stamp for `write_file` stale-read detection.

### `web_search`

The framework registers `web_search` with a throwing local stub. At step time, `buildToolSetWithProviderTools` replaces it with the real provider tool when the active model supports a known backend (Anthropic, OpenAI, Google, or AI Gateway/Perplexity). Override with `defineTool()` in `agent/tools/web_search.ts` to supply your own executor—the override's `execute` prevents provider injection.

### `ask_question`

`ask_question` has no `execute` method. The harness exposes it only when `SessionCapabilities.requestInput` is `true` (interactive sessions with HITL input). Scheduled task roots and subagent chains without that capability never see the tool. Input schema: `{ prompt, options?, allowFreeform? }`; the turn parks until the channel delivers an answer.

### `agent` (self-delegation)

Unless a subagent tool already occupies the name, the harness injects an `agent` tool that launches a copy of the current agent on a focused subtask. The child inherits the same tools, connections, and instructions but starts with fresh conversation history and fresh state. For self-delegation (`subagentName: "agent"`), the child shares the parent's sandbox filesystem—writes remain visible to the parent.

Input schema (all subagent tools):

```json
{
  "type": "object",
  "properties": {
    "message": { "type": "string" },
    "outputSchema": { "type": "object" }
  },
  "required": ["message"]
}
```

### `load_skill`

`load_skill` is always registered as a framework tool. The **Available skills** system-prompt block—which lists skill names, descriptions, and tells the model when to call `load_skill`—is injected only when the agent declares skills under `agent/skills/`. Without declared skills, the tool has no listed targets in context. Loading adds instructions to the turn; it does not add new execution surfaces.

`load_skill` is never sandboxed, even when `experimental.codeMode` or Workflow is enabled.

### `connection__search`

When the agent declares connections, graph resolution appends a framework dynamic tool resolver (`slug: "connection"`). At each `step.started`, the resolver exposes:

- `connection__search` — keyword search across connections (`keywords`, optional `connection`, optional `limit`)
- `connection__<connection>__<tool>` — directly callable tools discovered from search results

Discovered tools persist in durable context (`ConnectionSearchResultsKey`) so they remain available in code-mode runs where results are wrapped inside the `code_mode` tool. The connections system-prompt section instructs the model to search before calling qualified tools.

<Warning>
The model-facing search tool is `connection__search` (double-underscore namespace), not `connection_search`. Qualified connection tools use the pattern `connection__<connection>__<tool>`.
</Warning>

## Override a default

Author a tool at the same slug under `agent/tools/` and it replaces the built-in of that name. Spread defaults from `eve/tools/defaults` to keep description, schema, and framework state wiring:

```ts title="agent/tools/write_file.ts"
import { defineTool } from "eve/tools";
import { writeFile } from "eve/tools/defaults";

export default defineTool({
  ...writeFile,
  async execute(input, ctx) {
    console.log("[write_file]", input.path);
    return writeFile.execute(input, ctx);
  },
});
```

Exportable defaults: `bash`, `readFile`, `writeFile`, `glob`, `grep`, `webFetch`, `webSearch`, `todo`, `loadSkill`.

Specialized factories (`defineBashTool`, `defineReadFileTool`, `defineWriteFileTool`, `defineGlobTool`, `defineGrepTool`) share the same schemas and executors as the framework definitions.

<Note>
Spreading `todo` keeps the framework's durable `TodoStateKey` behavior. A fresh `defineTool({ name: "todo", ... })` without the spread owns its own state semantics and loses compaction re-injection wiring.
</Note>

## Disable a default

Export `disableTool()` as the default export from `agent/tools/<slug>.ts`. The filename slug selects which framework tool to remove:

```ts title="agent/tools/bash.ts"
import { disableTool } from "eve/tools";

export default disableTool();
```

If the slug matches no known framework tool, graph resolution fails at build time with an explicit error listing valid framework tool names—typos surface early instead of silently doing nothing.

## When to override, disable, or author

| Goal | Action |
| --- | --- |
| Same capability, different behavior (logging, guards, backend) | **Override** — same slug, spread default from `eve/tools/defaults` |
| Remove a capability entirely (lock down shell or web fetch) | **Disable** — `disableTool()` sentinel |
| New capability the harness does not ship | **Author** — new slug under `agent/tools/` |

## Experimental Workflow tool

The `Workflow` tool is shipped but off by default. Enable it by re-exporting the opt-in marker from `agent/tools/workflow.ts`:

```ts title="agent/tools/workflow.ts"
export { ExperimentalWorkflow as default } from "eve/tools";
```

With Workflow enabled, the model gets a QuickJS sandbox (`Workflow` surface) whose callable operations are the agent's subagents and remote agents—model-authored JavaScript orchestrating delegation as one durable step. This composes with `experimental.codeMode` (`code_mode` surface), which sandboxes executable host tools plus subagents.

## Code mode interaction

When `experimental.codeMode` is `true` in `agent.ts`, executable tools route through the `code_mode` sandbox wrapper unless listed in `NEVER_SANDBOXED_TOOL_NAMES` (`load_skill`). Provider-managed tools without local executors are never sandboxed. See agent-configuration for the `codeMode` flag.

## Verification

After changing tools or compaction settings:

<Steps>
<Step title="Rebuild and inspect discovery">

```sh
pnpm exec eve build
pnpm exec eve info
```

Confirm the tool list reflects overrides/disables and that connection or skill surfaces match your authored files.

</Step>
<Step title="Exercise a dev session">

```sh
pnpm exec eve dev
```

Create or continue a session against `/eve/v1/session` and confirm built-in tools execute in the expected surface (sandbox vs runtime vs provider).

</Step>
</Steps>

If graph resolution fails on `disableTool()`, check the error for the list of valid framework tool slugs and rename the file to match.

## Related pages

<CardGroup>
<Card title="Execution model and durability" href="/execution-model">
Session/turn/step nesting, durable checkpoints, crash resume, and parked work semantics.
</Card>
<Card title="Tools" href="/tools">
`defineTool`, HITL approval, `toModelOutput`, and dynamic tool resolution beyond built-ins.
</Card>
<Card title="Sandbox" href="/sandbox">
Sandbox backends, workspace seeding, and how shell/file tools proxy execution.
</Card>
<Card title="Configure agent.ts" href="/agent-configuration">
`defineAgent` fields including `compaction`, `modelOptions`, and `experimental.codeMode`.
</Card>
<Card title="Context control" href="/context-control">
Instructions vs skills, workspace visibility, and subagent context isolation.
</Card>
</CardGroup>
