# Agent config

`bot/agent.ts` default-exports `defineAgent(config)`: which model runs
the agent and its local harness defaults.
Everything is optional on the root agent.

```ts
import { defineAgent } from "@grok/agent-sdk";

export default defineAgent({
  model: {
    id: "grok-4.5",
    params: [
      { id: "effort", value: "high" },
      { id: "fast", value: "true" },
    ],
  }, // optional; this is the default
});
```

## Agent fields

| Field | Type | Meaning |
| --- | --- | --- |
| `model` | string or `{ id, params }` | Cursor model for turns. Defaults to `grok-4.5` with `effort=high`, `fast=true` on the root agent. Subagents omit it to inherit. |
| `name` | string | Display name override. Defaults to the package name or directory name. |
| `description` | string | What the agent is for. Required on subagents; the parent model reads it to decide when to delegate. Documentation-only on the root. |
| `instructions` | string | Inline instructions. Prefer `instructions.md`; this exists for subagents and generated configs. |
| `local` | `{ cwd?, workspaceDir?, sandbox? }` | Local harness defaults. See [Local options](#local-options). |
| `persistent` | boolean | Keep one long-lived runtime for the hosted deployment. Defaults to `false`. See [Persistent hosting](#persistent-hosting). |
| `hosting` | `{ egressDomains?, secretNames?, image? }` | Managed-hosting declarations read by `agent-sdk deploy`: the deployment's egress allowlist, the secret names the agent expects, and a container image for a [persistent agent](#persistent-hosting). Mounted [extensions](/docs/reference/extensions.md#build-an-extension) add their own. Ignored by local serving. |
| `concurrency` | `{ maxRunningTurns? }` | Agent-wide concurrent turn limit. See [Concurrency](#concurrency). |
| `builtinTools` | `{ reminders? }` | Framework-provided model-facing tools, opted in per capability. See [Built-in tools](#built-in-tools). |
| `tools` | `ToolName[]` | Allowlist of built-in harness tools offered to the model. Unset = the model's full standard toolset. See [Harness tools](#harness-tools). |
| `fs` | `FileSystem` or `(ctx) => FileSystem` | Custom file tree for the session. `root` is the deprecated name. See [Custom filesystem](#custom-filesystem). |
| `skills` | `false` or `(ctx) => FileSystem` | Replaces the skills tree mounted at `/agent/skills`, or turns that mount off. |

## Model

`model` is a Cursor model id string, or `{ id, params }`. Effort and
speed are params, not id suffixes. Agent SDK rejects suffix-style ids
like `grok-4.5-fast`:

```ts
model: {
  id: "grok-4.5",
  params: [
    { id: "effort", value: "high" },
    { id: "fast", value: "true" },
  ],
}
```

A plain string works when you don't need params:

```ts
model: "composer-2.5",
```

## Local options

`local` sets local-harness defaults.

`local.workspaceDir` points every session at one shared harness cwd,
for agents that work inside an existing checkout. It takes precedence
over `cwd`, and a per-send `workspaceDir` still wins over both.
Sessions share a working tree, so a file one turn writes is visible to
the next.

`local.sandbox` runs the harness inside Cursor's local sandbox. It's
off by default: shell then auto-approves and inherits the serve process
environment, including any credentials the host holds. Turn it on for
agents whose turns read untrusted input (webhook payloads, PR diffs,
inbound chat). The boundary is in the tools, not in the prompt, so it
holds when an injected prompt tells the model to ignore its instructions.

Agent SDK runs the file tools (`read`, `edit`, `delete`, `grep`, `glob`, `ls`)
in the serve process, outside the sandbox. With `local.sandbox` on, these
tools refuse a path outside the session workspace, after symlinks, with
`Path is outside the workspace`. The read tools can also read the
large-output files that web search and web fetch write. With
`local.sandbox` off, the file tools can read and write any path the serve
process can.

### Local cwd

`local.cwd` sets the parent directory for local harness workspaces. Each
session uses `<cwd>/<sessionId>` unless a per-send `workspaceDir` overrides
it.

Session workspaces are real Cursor project directories. The harness loads
`AGENTS.md` and `.cursor` config from ancestor directories. An agent nested
in another git repo (a monorepo package) defaults to a per-project
cache directory under `~/.cache` when you omit `cwd`, so the enclosing checkout
does not leak rules, skills, or MCP servers into the turn. A standalone git
root keeps the in-project session workspace. Point `cwd` at a checkout only
when the agent should inherit that tree.

## Persistent hosting

By default, a hosted deployment starts a runtime when an event arrives
and stops it when the work ends. Set `persistent: true` to keep one
long-lived runtime for the deployment instead, with its state directory
on the runtime's own disk. On a redeploy, the new version starts beside
the old one on the same state, and the old version stops after the new
one takes over. Each team can have one persistent deployment. Local
serving ignores the flag.

```ts
export default defineAgent({
  persistent: true,
  hosting: { image: "incident-bot-base" },
});
```

`hosting.image` names a container image that your team pushed with
`agent-sdk image push`. The agent runs inside that image instead of the
default Node 22 image. Only persistent agents can use it, and `deploy`
rejects `hosting.image` on any other agent. A deploy pins the image it
finds, so after you push a new image under the same name, deploy again
to pick it up.

## Custom filesystem

`fs` is mounted at `/`. Agent SDK mounts `/tmp` and `/agent/skills` over it.

`root` is the deprecated name of `fs`. Set one of them. A tree that already
has `/agent/skills` fails session start unless `skills` is `false`.

Semantic search is removed. Use Grep. See
[Migrate off semantic search](#migrate-off-semantic-search).

## Harness tools

Use `tools` to limit which built-in Cursor harness tools the model can
call. Omit it to keep the standard toolset. When you set it, the model
gets only the tools you list. An empty list disables all native
built-in tools. New platform tools stay disabled until you add them.

```ts
export default defineAgent({
  model: "composer-2.5",
  // Read-only triage agent: search and read only.
  // No shell, no edits, no subagents.
  tools: ["read", "grep", "glob", "ls"],
});
```

Agent SDK always adds `"mcp"` to a configured allowlist, because
authored server tools in `bot/tools/` reach the model over MCP. MCP
can also expose declared connections and servers from the harness
directory's ambient `.cursor` config. To exclude a checkout's MCP
servers, point `local.cwd` outside the checkout. See
[Local cwd](#local-cwd). `local.sandbox` makes MCP tool calls fail
closed.

Use the Cursor SDK's public tool names, including `"shell"`, `"read"`,
`"edit"`, `"grep"`, `"glob"`, `"ls"`, and `"task"`. Unknown names
fail the turn with a `ConfigurationError`. `"SemanticSearch"` and
`"semSearch"` fail validation. Use `"grep"`. See
[Migrate off semantic search](#migrate-off-semantic-search).

Two names have broader effects:

- `"shell"` also grants shell input. Tools with
  `execution: "agent"` need it to run their scripts. Discovery warns
  when your allowlist would prevent those tools from running.
- `"task"` lets the root agent start subagents. Each subagent keeps
  its own curated toolset.

The allowlist controls which tools the model can call. It does not
isolate the serve host. For agents that process untrusted input, also
set `local: { sandbox: true }`.

## Migrate off semantic search

Semantic search is removed. Use Grep.

Delete `FileSystem.search` from a custom `fs`. Content search is `grep`. A
`GrepQuery` has a regular expression `pattern`, and can set `path`,
`glob`, and `maxResults`. A `GrepResult` has `path`, `lineNumber`, and
`line`.

Delete imports of `SearchQuery` and `SearchResult`. `SearchQuery`
carried a natural-language `query`, plus optional `maxResults` and
`targetDirectories`. `SearchResult` carried `path`, `score`, and
`snippet`. Import `GrepQuery` and `GrepResult` from `@grok/agent-sdk`.

List `"grep"` in `tools`. Remove `"SemanticSearch"` and `"semSearch"`.
Delete `bot/tools/SemanticSearch.ts` or `bot/tools/semSearch.ts`. In
`instructions.md`, tell the model to call `Grep` with `pattern`, and
with `path` or `glob` when the lookup should stay in one directory.

```ts
export default defineAgent({
  tools: ["read", "grep"],
});
```

`agent-sdk validate` reports `Semantic search has been removed. Use Grep.`
when `tools` lists either name, or a file in `bot/tools/` uses it.

## Concurrency

`concurrency.maxRunningTurns` caps how many model turns run at once
across all of the agent's sessions (positive integer, hard cap 200).
When every slot is busy, newly admitted turns queue FIFO instead of
failing: the stream records a durable `turn.queued` event with the
queue position, `GET /v1/sessions` reports `queued: true`, and each
queued turn starts as soon as a slot frees. A queued turn still counts
as running for busy semantics: follow-ups preempt it, and direct
write-effect tool calls get `409 session_busy`. Omit for unlimited.

```ts
export default defineAgent({
  concurrency: { maxRunningTurns: 3 },
});
```

## Built-in tools

`builtinTools` opts into framework-provided model-facing tools. Each
enabled capability shows up as ordinary server tools, so turns, direct
calls, `info`, and the playground treat them like authored tools. Authored tools with the same name win, with a
warning, and like all server tools they run on the local runtime.

`builtinTools: { reminders: true }` adds three tools bound to the
current conversation over `host.reminders`: `reminders_create`,
`reminders_list`, and `reminders_cancel`. Sessions without a
continuation key can't arm reminders. See
[Schedules and reminders](/docs/reference/schedules.md#reminders).

## Serve programmatically

`serve(dirOrProject, options)` embeds the server in your own process:

```ts
import { serve } from "@grok/agent-sdk";

const handle = await serve("./my-agent", {
  port: 3000,
});
console.log(`listening on ${handle.url}`);
// handle.callTool(...), handle.dispatchSchedule("heartbeat"),
// handle.createReminder(...), handle.agents, await handle.close()
```

`handle.agents` lists each mount as `slug`, `url`, and `info`. `handle.project` is the first agent's project. It is deprecated.

Host settings match the documented [CLI](/docs/reference/cli.md) `serve` flags.
`serve()` also accepts `discovery` (project-loading options) and
`mode: "single" | "multi"`. Pass `apiKey` or use the same Cursor
credential as the CLI: `CURSOR_API_KEY`, `CURSOR_API_KEY_FILE`,
`CURSOR_SERVICE_ACCOUNT_KEY`, or `agent-sdk login`.

## Inspect a project in tests

`loadProject(dir)` reads one project and does not serve it. The result is a `Project`. The only field is `info`. That object matches the `GET /v1/info` body for the project. `agent-sdk info --json` on a project directory prints `{ agents: [{ slug, ...info }] }`. It prints the bare object only when the single mount slug is empty.

```ts
import { loadProject } from "@grok/agent-sdk";

const project = await loadProject("./my-agent");
console.log(project.info.name);
console.log(project.info.tools.map((tool) => tool.name));
```

`loadProject` throws `ProjectLoadError` when discovery reports an error. Pass `{ strict: true }` to throw on a warning as well. `error.diagnostics` lists the findings. Each `path` is relative to the project directory. A path outside that directory starts with `../`. `loadProject` does not take `importModule`. `serve` still accepts `discovery.importModule` for that override.

`info.local` is `{ cwd }` when the project sets a cwd. The serve banner reads `agents[].info`.

`serve` accepts that `Project` or a directory path.

## Related

- [Instructions](/docs/reference/instructions.md): the required half of a minimal
  agent
- [CLI](/docs/reference/cli.md): the `serve` flags `serve()` accepts
- [Sessions](/docs/reference/sessions.md): workspaces, identifiers, and turn admission
- [Schedules](/docs/reference/schedules.md): reminder tools opted in here
