Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions reference/configuration/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,37 @@ rootPath: /var/lib/harper

---

## `agent`

Added in: v5.2.0

Built-in Harper agent — an LLM loop that operates this instance through Harper's own operations, scoped filesystem access, followup scheduling, the V8 inspector, and outbound HTTP. Disabled by default, so it never incurs LLM cost unless you turn it on. Driven through the [Agent operations](../operations-api/operations.md#agent), which also describe the privilege boundary before you enable it.

```yaml
agent:
enabled: true
model: default
maxTurns: 50
autoApprove: false
allowDestructive: false
user: hdb_agent
```

- `enabled` - Enable the agent component; _Default_: `false`
- `provider` - Recorded on the session but **not yet used to route the model call** — only `model` reaches the provider. Set the provider through the [`models`](../models/overview.md#configuration) configuration instead
- `model` - Model id override, passed through to the model call; _Default_: the [`models`](../models/overview.md#configuration) generative default
- `maxTurns` - Maximum tool-call iterations in a single run; _Default_: `50`
- `maxCostUsd` - Intended per-run cost ceiling. **Not enforced** — it is a stored setting only, and nothing checks spend against it; _Default_: `5.00`
- `autoApprove` - Run without per-action approval gates; _Default_: `false`
- `allowDestructive` - Include destructive tools (`drop_component`, `restart`, `set_configuration`, ...) in the agent's toolset. When `false` they are removed entirely rather than gated; _Default_: `false`
- `user` - Harper user the agent's **operations** tools run as; the filesystem, HTTP, schedule, and inspector tools always run at process privilege regardless. If it cannot be resolved and it is not the default, the agent fails closed and runs with no operations tools; _Default_: `hdb_agent`, which falls back to a `super_user` bootstrap identity
- `componentsScope` - Filesystem write scope for component edits, relative to `rootPath`; _Default_: the full `componentsRoot`
- `systemPromptAppend` - Operator text appended to the agent's system prompt
Comment thread
kriszyp marked this conversation as resolved.

`enabled`, `provider`, `model`, `maxTurns`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend` can also be changed at runtime with [`set_agent_config`](../operations-api/operations.md#set_agent_config), which applies in memory only. `enabled` is the exception worth knowing: it cannot switch the agent on, because with the agent disabled at startup no agent operation is registered at all.

---

## `applications`

Added in: v5.0.0
Expand Down
163 changes: 163 additions & 0 deletions reference/operations-api/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,169 @@ Manage in-memory application status values. Status types: `primary`, `maintenanc

---

## Agent

<VersionBadge version="v5.2.0" />

Operations for driving Harper's built-in agent — an LLM loop that operates the instance through Harper's own operations, scoped filesystem access, followup scheduling, the V8 inspector, and outbound HTTP. The loop runs on the main thread, so an active run competes with Harper's other main-thread work; prefer running exploratory prompts against a node that is not serving production traffic.

The agent component is **disabled by default**. Enable it with `agent.enabled: true` in `harper-config.yaml` (see [`agent`](../configuration/options.md#agent)) and configure a generative model under [`models`](../models/overview.md#configuration). With the component disabled at startup none of these operations are registered, so calling one is an unknown-operation error rather than a permission or state error.

All six operations are `super_user` by default. They participate in the role [`operations` allowlist](../users-and-roles/overview.md#operation-permissions), so a non-`super_user` role can be granted a scoped subset (for example `operations: ['agent_prompt', 'get_agent_session']`) without granting full `super_user`. Note that the read operations are not caller-scoped: a role granted `get_agent_session` or `list_agent_sessions` reads every session on the instance, including transcripts of runs it did not start. Because a transcript records the _output_ of every tool call, and those calls ran as `agent.user`, delegating a read operation hands that role the results of work done at the agent's privilege — table contents, log excerpts, configuration — regardless of its own permissions. Delegate the read operations only to roles you would trust with the agent itself.

:::warning
Anyone who can call `agent_prompt` can direct whatever the agent does. Understand the boundary before enabling it:

- `agent.user` (default: a `super_user` bootstrap identity) governs only the **operations** tools. Setting it to a restricted user narrows those, and nothing else.
- The agent's other tools — scoped filesystem access, outbound `http_fetch`, followup scheduling, and the V8 inspector — run at the Harper process's own privilege, whatever `agent.user` is. (The inspector tools additionally need `threads.debug`, and fail with an explanatory error without it.)
- `http_fetch` blocks only the known cloud-metadata hostnames and the IPv4 link-local range `169.254.0.0/16`, and it checks the literal hostname you pass — a name that resolves to a blocked address is not caught, and redirects are followed without re-checking. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the log and configuration directories as well as the component tree, so an enabled agent puts a read path and an egress path in the same toolset. Treat it as an outbound network client and apply egress policy to the host.
- With the default `agent.allowDestructive: false`, destructive tools (including filesystem writes) are removed from the toolset entirely. Turning it on admits component writes, and component code is executed by the Harper process — a write is effectively code execution at process privilege.
- Leave `agent.autoApprove` off so any destructive call that is admitted still pauses for [approval](#approve_agent_action). The gate covers only the tools marked destructive — filesystem writes and the inspector's code-evaluation tools. `http_fetch` and followup scheduling are not gated, so an outbound POST and a self-rescheduling run proceed without an approval prompt.
:::

| Operation | Description | Role Required |
| ---------------------- | ------------------------------------------------------------- | ------------- |
| `agent_prompt` | Starts or continues an agent session and kicks off a run | super_user |
| `get_agent_session` | Returns a session: status, full transcript, pending approvals | super_user |
| `list_agent_sessions` | Lists agent sessions | super_user |
| `approve_agent_action` | Approves or denies a gated tool call and resumes the run | super_user |
| `cancel_agent_run` | Cancels a run and marks the session aborted | super_user |
| `set_agent_config` | Updates agent settings in memory for the life of the process | super_user |

### Sessions and run status

Each conversation is a session, persisted to `system.hdb_agent_session` so transcripts survive a restart. Runs are asynchronous: `agent_prompt` returns as soon as the run is started, and you poll `get_agent_session` for progress and results.

Transcripts are retained indefinitely — the table is audited and none of these operations delete a session — so treat a prompt as durably recorded and keep credentials out of them.

A session's `status` is one of:

| Status | Meaning |
| ------------------- | -------------------------------------------------------------------- |
| `idle` | Created, or resumable — no run in flight |
| `running` | A run is in progress |
| `awaiting_approval` | Paused on one or more destructive tool calls; see `pendingApprovals` |
| `completed` | The model produced a final answer with no further tool calls |
| `aborted` | Cancelled by an operator via `cancel_agent_run` |
| `error` | The run failed; `lastError` carries the message |

`completed` also covers hitting the `agent.maxTurns` ceiling — in that case `lastError` reads `Reached maxTurns=<n> without a final answer.`, so check it before treating a completed session as finished.

### `agent_prompt`

Sends a prompt to the agent. Omit `session_id` to start a new session; supply one to continue an existing conversation. Returns immediately with the session id and `"status": "running"`.

| Parameter | Type | Description |
| ------------ | ------ | --------------------------------------------------------------- |
| `message` | string | The instruction for the agent. **Required**, must be non-empty. |
| `session_id` | string | Existing session to continue. Omit to create a new session. |

```json
{
"operation": "agent_prompt",
"message": "Create a component called inventory with a Product table keyed by sku, then verify it responds over REST."
}
```

Response:

```json
{ "session_id": "3f7c...", "status": "running" }
```

A session that is `running` or `awaiting_approval` rejects a new prompt with a 409 — resolve the pending approval or cancel the run first.

### `get_agent_session`

Returns the full session record: `status`, `user` (the Operations API caller who created the session, falling back to `agent.user`), the `messages` transcript (user, assistant, and tool messages, including tool calls and their observations), `pendingApprovals`, `model`, `provider`, `createdAt`/`updatedAt`, and `lastError`. This is the polling endpoint for a run in flight.
Comment thread
kriszyp marked this conversation as resolved.

```json
{ "operation": "get_agent_session", "session_id": "3f7c..." }
```

Unknown `session_id` returns a 404.

### `list_agent_sessions`

Lists agent sessions.

| Parameter | Type | Description |
| --------- | ------- | ---------------------------------------- |
| `limit` | integer | Maximum sessions to return. Default 100. |

```json
{ "operation": "list_agent_sessions", "limit": 20 }
```

Response:

```json
{ "sessions": [{ "session_id": "3f7c...", "status": "completed", "...": "..." }] }
```

The result order is not chronological — session ids are UUIDs and the listing walks them in reverse key order. Sort on `updatedAt` or `createdAt` if you need recency.

### `approve_agent_action`

When `agent.autoApprove` is off (the default), any tool call the agent makes to a destructive operation pauses the run and lands in the session's `pendingApprovals`. This operation resolves one of them and resumes the run. Each entry carries its identifier in an `id` field — pass that as `approval_id` — alongside `toolName`, `arguments`, and `reason`.

| Parameter | Type | Description |
| ------------- | ------- | -------------------------------------------------------------------------------- |
| `session_id` | string | **Required.** |
| `approval_id` | string | The `id` of the entry in `get_agent_session`'s `pendingApprovals`. **Required.** |
| `approved` | boolean | `true` to approve (default). `false` denies the call. |

```json
{
"operation": "approve_agent_action",
"session_id": "3f7c...",
"approval_id": "9b21...",
"approved": true
}
```

Both decisions resume the loop: an approval executes the saved tool call, and a denial hands the refusal back to the model as an observation so it can adjust. Neither ends the run — use `cancel_agent_run` for that. If a single turn produced several gated calls, the session stays `awaiting_approval` until every one of them is resolved. Resolving an already-resolved approval is an error.

Whether a tool is treated as destructive at all is governed by `agent.allowDestructive`: when it is `false` (the default), destructive tools are removed from the agent's toolset entirely rather than gated.

### `cancel_agent_run`

Cancels a session's run, clears any followups it scheduled, and marks the session `aborted`. Works on a paused (`awaiting_approval`) or `idle` session as well as an actively running one.

```json
{ "operation": "cancel_agent_run", "session_id": "3f7c..." }
```

Response:

```json
{ "cancelled": true, "signalledLiveRun": true }
```

`cancelled` is `false` if the session had already reached a terminal state (`completed`, `aborted`, `error`). `signalledLiveRun` reports whether there was an in-flight run to abort — a paused session yields `false` while still being marked aborted.
Comment thread
kriszyp marked this conversation as resolved.

One gap is worth knowing: changing `allowDestructive` with [`set_agent_config`](#set_agent_config) rebuilds the toolset, and followups scheduled before that change are no longer tracked, so a later cancel does not clear them. A stray followup starts a fresh run even after a cancel and even with `enabled` set to `false`. If a run has scheduled followups, avoid toggling `allowDestructive` mid-session, and restart the node if one escapes.

### `set_agent_config`

Updates agent settings and returns the resulting configuration. Accepts any of `enabled`, `provider`, `model`, `maxTurns`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend`; keys not supplied are left unchanged. Each field is described under [`agent`](../configuration/options.md#agent).

```json
{ "operation": "set_agent_config", "autoApprove": false, "maxTurns": 20 }
```

Three limits are worth knowing:

- **The change is in-memory and not persisted.** It applies for the life of the process and is lost on restart; edit `harper-config.yaml` for a durable change.
- **A run already in flight keeps the settings it started with** — its toolset, `autoApprove`, `model`, and `systemPromptAppend` are all captured at start. Changes take effect on the next run. To stop a run immediately, use `cancel_agent_run`.
- **`enabled` is not a kill switch.** It cannot turn the agent on — if it was off at startup, this operation does not exist. Setting it to `false` only makes subsequent `agent_prompt` calls return 409; a run already in flight continues, and `approve_agent_action` still resumes a paused one. Use `cancel_agent_run` to stop a run.

### MCP access

When the [MCP server](../mcp/overview.md) is enabled with the operations profile, `agent_prompt`, `get_agent_session`, `list_agent_sessions`, `approve_agent_action`, and `cancel_agent_run` are also exposed as MCP tools, with no allow-list entry required. They dispatch through the same authorization path as the operations above, and are listed only for users whose role could call them. `set_agent_config` is deliberately not exposed over MCP — it is an operator action.

---

## Backup & Restore

Operations for backing up and restoring databases. Managed backups <VersionBadge version="v5.2.0" /> require the RocksDB storage engine; `get_backup` works with both RocksDB and LMDB.
Expand Down
1 change: 1 addition & 0 deletions reference/operations-api/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Operations are grouped by topic. See [Operations](./operations.md) for the compl
| [Configuration](./operations.md#configuration) | Read and update Harper configuration | — |
| [Web Application Firewall](./operations.md#web-application-firewall) | Manage WAF rules, mode, and score threshold | [WAF Operations](../web-application-firewall/operations.md) |
| [System](./operations.md#system) | Restart, system information, status management | — |
| [Agent](./operations.md#agent) | Drive the built-in Harper agent | [Models Overview](../models/overview.md) |
| [Jobs](./operations.md#jobs) | Query background job status | [Database Jobs](../database/jobs.md) |
| [Logs](./operations.md#logs) | Read standard, transaction, and audit logs | [Logging Operations](../logging/operations.md) |
| [Certificate Management](./operations.md#certificate-management) | Manage TLS certificates | [Certificate Management](../security/certificate-management.md) |
Expand Down