# Context control

> Always-on instructions vs on-demand skills, workspace visibility through sandbox tools, dynamic capabilities (defineDynamic), and subagent context isolation.

- 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/context-control.md`
- `packages/eve/src/compiler/normalize-instructions.ts`
- `packages/eve/src/compiler/normalize-skill.ts`
- `packages/eve/src/execution/skills/instructions.ts`
- `docs/guides/dynamic-capabilities.md`
- `packages/eve/src/compiler/workspace-resources.ts`

---

---
title: "Context control"
description: "Always-on instructions vs on-demand skills, workspace visibility through sandbox tools, dynamic capabilities (defineDynamic), and subagent context isolation."
---

Eve assembles model context in layers: `composeRuntimeBasePrompt` builds the static system prompt from authored instructions, a shallow workspace overview, connection summaries, and skill routing hints; the harness tool-loop appends runtime-resolved dynamic instructions and skill announcements before each model call; `load_skill` injects full skill bodies as tool results rather than expanding the system prompt.

## Context layers

```text
System prompt (every model call)
├── Static instructions (build-time composed)
├── Workspace overview (root entries only)
├── Available skills (name + description, not body)
├── Connections section (when declared)
├── Tool execution guidance (when tools exist)
├── Dynamic instructions (session.started / turn.started)
└── Dynamic skill announcement (when manifest changes)

Turn-local context
├── load_skill tool results (full SKILL.md body)
├── Tool call / result history
└── Compaction summaries (when threshold crossed)

Sandbox inspection (/workspace)
├── bash, read_file, write_file, glob, grep
└── Packaged skill siblings (references/, scripts/, assets/)
```

<Note>
Active skill markdown never appears in the static system prompt. The model receives skill bodies from `load_skill` tool results, which keeps the system prompt stable across a session and preserves prompt-cache hits.
</Note>

## Always-on instructions

Instructions are the permanent system prompt. Eve prepends them to every model call in a session.

| Source | When resolved | Runtime behavior |
| --- | --- | --- |
| `agent/instructions.md` | Build time | Markdown captured into compiled manifest |
| `agent/instructions.ts` with `defineInstructions` | Build time (once) | Module runs once at compile; resulting markdown is frozen in manifest |
| `agent/instructions/` directory | Build time | Non-recursive; entries compose in filename order after any root `instructions.md` |
| `agent/instructions/*.ts` with `defineDynamic` | Runtime | Resolver runs on `session.started` and `turn.started` |

Static module-backed instructions execute once at build time. There is no per-session re-evaluation for `defineInstructions` exports.

```ts title="agent/instructions.ts"
import { defineInstructions } from "eve/instructions";
import { buildInstructionsPrompt } from "./lib/prompts.js";

export default defineInstructions({
  markdown: buildInstructionsPrompt(),
});
```

Keep instructions short and stable: identity, tone, standing rules. Long or situational procedures belong in skills.

Dynamic instruction resolvers return `defineInstructions({ markdown })` and store output in durable session or turn keys. The tool-loop calls `buildDynamicInstructionMessages` before each model call to flatten session-scoped entries first, then turn-scoped entries.

## On-demand skills

Skills stay out of the always-on prompt. Eve advertises each skill's name and description in an **Available skills** section and registers the framework-owned `load_skill` tool when the agent declares skills.

The available-skills section lists every skill regardless of activation state. It includes routing guidance and workspace paths but never inlines skill markdown:

```text
- research: Research unfamiliar topics before answering with confidence.
  (path: /workspace/skills/research/SKILL.md)
```

When a request matches a skill description or the user names a skill, the model calls `load_skill`. Eve reads `SKILL.md` from the active sandbox at `/workspace/skills/<id>/SKILL.md`, strips YAML frontmatter, and returns the body as the tool result.

<ParamField body="skill" type="string" required>
Skill name or id from the Available skills block.
</ParamField>

Loading a skill adds instructions only. It does not register new tools. Typed runtime behavior still comes from `agent/tools/`.

### Skill authoring shapes

| Shape | Path | Seeded to workspace |
| --- | --- | --- |
| Flat markdown | `agent/skills/<name>.md` | `/workspace/skills/<name>/SKILL.md` |
| Packaged directory | `agent/skills/<name>/SKILL.md` + siblings | Full directory under `/workspace/skills/<name>/` |
| Module-backed | `agent/skills/<name>.ts` with `defineSkill` | Generated `SKILL.md` and `files` entries |
| Dynamic | `agent/skills/<name>.ts` with `defineDynamic` | Resolved at runtime, materialized to sandbox |

Packaged sibling files (`references/`, `assets/`, `scripts/`) are not pasted into the prompt. The model inspects them with `bash`, `read_file`, `glob`, or `grep` after loading the skill.

At build time, `materializeWorkspaceResources` copies authored skills and sandbox workspace seeds into `.eve/compile/workspace-resources/<nodeId>/`. Eve seeds that tree into the runtime sandbox at session bootstrap. A sandbox workspace cannot define a `skills/` entry — Eve manages that path.

## Workspace visibility through sandbox tools

Eve does not inline the full authored tree into the prompt. Only two sources reach the runtime workspace:

- `agent/skills/` → `/workspace/skills/...`
- `agent/sandbox/workspace/**` → `/workspace/...`

Everything under `lib/` stays import-only and never mounts to the workspace.

The workspace section in the system prompt is a shallow overview: the live root (`/workspace`), sorted root entries, and guidance to verify paths with `bash` before claiming file availability. Deeper inspection is explicit tool work.

| Tool | Sandbox scope | Typical use |
| --- | --- | --- |
| `bash` | `/workspace` cwd | `ls`, `find`, `rg`, shell commands |
| `read_file` | Sandbox FS | Line-numbered text reads |
| `glob` | Sandbox FS | Pattern-based file discovery |
| `grep` | Sandbox FS | Regex search across files |
| `write_file` | Sandbox FS | File writes (read-before-write enforced) |

<Warning>
The workspace overview is not a file listing. If `bash` verification fails, the model should report the failure rather than answering from the overview alone.
</Warning>

Tools and `load_skill` proxy through the app runtime into the agent's single sandbox. `load_skill` itself runs in the app runtime but reads skill files from the sandbox.

## Dynamic capabilities with defineDynamic

When context depends on caller identity, tenant, channel metadata, or external data, use `defineDynamic` instead of static authoring. Import paths:

| Capability | Import | Authored under |
| --- | --- | --- |
| Instructions | `defineDynamic`, `defineInstructions` from `eve/instructions` | `agent/instructions/` |
| Skills | `defineDynamic`, `defineSkill` from `eve/skills` | `agent/skills/` |
| Tools | `defineDynamic`, `defineTool` from `eve/tools` | `agent/tools/` |

Resolvers receive a `DynamicResolveContext` with `ctx.session.id`, `ctx.session.auth`, `ctx.channel` metadata, and conversation `messages`.

### Resolver events

| Event | Dynamic instructions | Dynamic skills | Dynamic tools |
| --- | --- | --- | --- |
| `session.started` | Yes | Yes | Yes |
| `turn.started` | Yes | Yes | Yes |
| `step.started` | No | No | Yes |

Instructions and skills are restricted to session and turn boundaries because they feed the cache-sensitive system prompt. Dynamic tools can also resolve before each model call.

On a matching event, execution order is: channel adapter handler → stream-event hooks → dynamic resolvers. The tool loop reads the current tool set immediately before each model call.

### Dynamic instructions example

```ts title="agent/instructions/persona.ts"
import { defineDynamic, defineInstructions } from "eve/instructions";

export default defineDynamic({
  events: {
    "session.started": (_event, ctx) => {
      const plan = ctx.session.auth.current?.attributes.plan ?? "free";
      return defineInstructions({
        markdown: `The caller is on the ${plan} plan. Match the depth of your answers to it.`,
      });
    },
  },
});
```

Each resolver owns a slug-keyed slot. A later event for the same file replaces that slot. Session-scoped output persists for the session; turn-scoped output resets each turn.

### Dynamic skills example

```ts title="agent/skills/team_playbook.ts"
import { defineDynamic, defineSkill } from "eve/skills";
import { PLAYBOOKS } from "../lib/playbooks.js";

export default defineDynamic({
  events: {
    "session.started": (_event, ctx) => {
      const team = ctx.session.auth.current?.attributes.team;
      const markdown = team ? PLAYBOOKS[team] : undefined;
      return markdown ? defineSkill({ markdown }) : null;
    },
  },
});
```

Resolved skills are written to the sandbox and announced through the same **Available skills** formatter static skills use. A map return names skills `slug__key`; a single `defineSkill` return names the skill after the file slug.

Dynamic skill names cannot collide with authored skills or another resolver's output. The build/runtime rejects conflicts.

<Info>
Hooks observe stream events but cannot inject model context. Use `defineDynamic` in `agent/instructions/` or `agent/skills/` for runtime prompt contributions. Hooks can update channel state that resolvers read on the next event.
</Info>

## Subagent context isolation

Subagents are a context-control boundary when a task needs a different prompt, tool surface, or runtime environment.

### Built-in `agent` tool

Every agent ships an `agent` tool that delegates to a copy of itself:

- Shares the parent's sandbox, tools, connections, and instructions
- Starts with fresh conversation history and fresh `defineState` state
- Child file writes are immediately visible to the parent

The parent packs everything the child needs into `message`. The child never sees parent history.

### Declared subagents

A declared subagent under `agent/subagents/<id>/` is discovered as its own agent root. It inherits nothing from the root's authored slots.

| Slot | Built-in `agent` tool | Declared subagent |
| --- | --- | --- |
| Instructions | Copy of parent | Own `instructions.{md,ts}`, optional |
| Tools | Inherited | Own `tools/` |
| Connections | Inherited | Own `connections/` |
| Skills | Inherited | Own `skills/` (invisible to parent) |
| Sandbox | Shared with parent | Own `sandbox/` or framework default |
| Hooks | Inherited | Own `hooks/` (parent hooks do not fire) |
| State | Fresh | Fresh |
| History | Fresh child session | Fresh child session |

Each delegation spins up a child session and stream. The parent emits `subagent.called` with `childSessionId`; subscribe to `GET /eve/v1/session/:childSessionId/stream` to follow child progress.

When the child needs shared procedures, duplicate skill markdown under each subagent's `skills/` or share typed helpers through `lib/`. There is no cross-agent skill visibility.

Reach for a declared subagent when the task needs a different specialist surface. Reach for a skill when the root agent keeps its identity and only needs an optional procedure.

## Choosing a context lever

| Need | Use |
| --- | --- |
| Permanent identity and standing rules | `instructions.md` / `instructions.ts` |
| Build-time prompt composition from typed helpers | `instructions.ts` with `defineInstructions` |
| Optional procedures that should not bloat every turn | `agent/skills/` + `load_skill` |
| Per-caller instructions or skill sets | `defineDynamic` in `agent/instructions/` or `agent/skills/` |
| Per-session or per-step tool sets | `defineDynamic` in `agent/tools/` |
| File inspection or command execution | Sandbox tools against `/workspace` |
| Different prompt, tools, or sandbox for a subtask | Declared subagent under `agent/subagents/<id>/` |
| Parallel work on the same files with same tools | Built-in `agent` tool |
| Long sessions exceeding context window | `compaction.thresholdPercent` in `agent.ts` |

## Prompt assembly reference

`createResolvedRuntimeTurnAgent` calls `composeRuntimeBasePrompt` with the resolved agent's instructions, skills, connections, and workspace spec. Section order:

1. **Instructions** — `Instructions (<name>)\n<markdown>`
2. **Workspace** — shallow root-entry overview when `rootEntries` is non-empty
3. **Tool execution** — parallel batch guidance when tools are available
4. **Connections** — when connections are declared
5. **Available skills** — name, description, and `/workspace/skills/...` path per skill

Before each model call, the tool-loop appends dynamic instruction system messages and any pending dynamic skill announcement. Skill bodies activated via `load_skill` appear in tool-result history for that turn.

## Related pages

<CardGroup>
<Card title="Instructions and skills" href="/instructions-and-skills">
Author always-on instructions and on-demand skills, including `load_skill` activation and workspace seeding.
</Card>
<Card title="Default harness" href="/default-harness">
Built-in tools including `load_skill`, `bash`, and file ops that inspect the workspace.
</Card>
<Card title="Sandbox" href="/sandbox">
Workspace seeding, sandbox backends, and proxy execution for shell and file tools.
</Card>
<Card title="Subagents and schedules" href="/subagents-and-schedules">
Declared subagents, the built-in `agent` tool, and delegation boundaries.
</Card>
<Card title="State, hooks, and context" href="/state-hooks-and-context">
`ctx.session`, `ctx.getSkill`, and where managed-context APIs are valid.
</Card>
<Card title="Configure agent.ts" href="/agent-configuration">
Compaction `thresholdPercent` and other settings that affect long-session context.
</Card>
</CardGroup>
