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
135 changes: 135 additions & 0 deletions harper-best-practices/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2526,3 +2526,138 @@ loadEnv:
- Because Harper is a single-process application, variables loaded onto `process.env` are shared across all components.
- Without `override: true`, variables already set in the shell or container environment will not be overwritten by values in `.env` files.
- `files` is the only required option; omitting it will produce an invalid configuration.

### 4.7 Delegating to the Built-in Agent

Harper 5.2+ ships with a **built-in agent** that runs _inside_ the server, on the main thread
adjacent to the operations API. Because it runs in-process, it can do things a remote client
cannot: call the operations API as RBAC-filtered tools, read and write component files under the
instance's components root, attach the V8 inspector to worker threads to debug and profile them,
schedule follow-up work, and consult the Harper best-practices skill. You send it a natural-language
task; it runs a tool-using loop under a super_user identity and reports back.

#### When to Use

Delegate to the built-in agent when the work is best done **on the instance itself** rather than
from your local client:

- Operating on a deployed instance in place — inspect the schema, build or adjust a component,
restart, run an operation.
- Debugging or profiling a running instance — attach to a worker thread, capture a CPU profile,
set a logpoint.
- Handing off a larger, multi-step task to an agent that already has the instance's tools,
filesystem, and credentials in context.

Do the work in your own client instead when it's purely local (editing source before deploy) or
when you don't want a server-side agent making changes.

**Prerequisites:** the target instance must have the agent enabled (an `agent:` config block with
`enabled: true`) and a configured generative model backend. All agent operations require
**super_user**.

#### How It Works

The lifecycle assumes you have already deployed to and authenticated with the target instance (see
[deploying-to-harper-fabric.md](deploying-to-harper-fabric.md) — `harper login` stores a token so
you don't repeat credentials). Delegation reuses that same target and credentials.

There are two equivalent ways to drive the agent.

##### Option A — the `harper agent` CLI (simplest)

A thin client over the agent operations API that reuses your stored `harper login` credentials, so
no connector setup is needed:

```bash
# One-shot: send a task, print the reply, exit
harper agent "Describe the schema, then add a price index to the Product table."

# Interactive session (REPL)
harper agent

# Against a specific remote instead of the logged-in default
harper agent --target <Application URL> "List the databases and tables."
```

The CLI polls the run to completion and renders the transcript (tool calls, results, and the
agent's reply). When a run needs approval for a destructive action, it prompts you inline.

##### Option B — the agent operations API (programmatic)

Call the operations API directly (HTTP POST to the ops endpoint, super_user auth). This is the path
to use from scripts and services.

1. **Start a task** with `agent_prompt`. Returns a `session_id` and a `status`.

```bash
curl -s -u <user>:<pass> <ops-endpoint> \
-H 'Content-Type: application/json' \
-d '{"operation":"agent_prompt","message":"Build a Customer table (id, email, name) exported over REST."}'
```

2. **Poll for progress** with `get_agent_session`, passing the `session_id`. The returned session
carries the `status`, the `messages` transcript, and any `pendingApprovals`.

```bash
curl -s -u <user>:<pass> <ops-endpoint> \
-H 'Content-Type: application/json' \
-d '{"operation":"get_agent_session","session_id":"<id>"}'
```

Poll until `status` leaves `running` — terminal states are `completed`, `aborted`, and `error`;
`awaiting_approval` means it is paused for an approval decision (see step 3).

3. **Approve or deny a paused action.** When the agent enabled configuration has `autoApprove:false`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In YAML, keys and values should be separated by a space after the colon (e.g., autoApprove: false instead of autoApprove:false). This ensures standard YAML formatting and prevents potential parsing issues if users copy-paste this configuration.

Suggested change
3. **Approve or deny a paused action.** When the agent enabled configuration has `autoApprove:false`,
3. **Approve or deny a paused action.** When the agent enabled configuration has `autoApprove: false`,

a destructive tool call pauses the run with a `pendingApprovals[]` entry. Resolve it with
`approve_agent_action`, then poll again — approval executes the saved call, denial hands the
rejection back to the agent so it can adjust.

```bash
curl -s -u <user>:<pass> <ops-endpoint> \
-H 'Content-Type: application/json' \
-d '{"operation":"approve_agent_action","session_id":"<id>","approval_id":"<approval-id>","approved":true}'
```

4. **Continue the conversation** by passing the same `session_id` back into `agent_prompt` with a
new `message`. Omit `session_id` to start a fresh session.

Supporting operations: `list_agent_sessions` (recent sessions), `cancel_agent_run` (terminate a
running or paused session), and `set_agent_config` (adjust `autoApprove`, `allowDestructive`,
`model`, and related settings on a running instance).

#### Examples

**Delegate a build to a deployed Fabric instance and wait for the result:**

```bash
harper login <Application URL>
harper agent --target <Application URL> \
"Create a Product table (id, name, price) exported over REST, then confirm the endpoint responds."
```

**Programmatic start-and-poll loop:**

```bash
SID=$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \
-d '{"operation":"agent_prompt","message":"Add a vector index to the Document.embedding field."}' \
| jq -r .session_id)

while [ "$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \
-d "{\"operation\":\"get_agent_session\",\"session_id\":\"$SID\"}" | jq -r .status)" = "running" ]; do
sleep 3
done
Comment on lines +2645 to +2648

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current programmatic polling loop exits as soon as the status is no longer "running". However, if the session transitions to awaiting_approval, the loop terminates silently without indicating the final status or prompting the user. Capturing the final status in a variable and printing it after the loop exits provides better visibility and debugging information.

Suggested change
while [ "$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \
-d "{\"operation\":\"get_agent_session\",\"session_id\":\"$SID\"}" | jq -r .status)" = "running" ]; do
sleep 3
done
while true; do
STATUS=$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \
-d "{\"operation\":\"get_agent_session\",\"session_id\":\"$SID\"}" | jq -r .status)
if [ "$STATUS" != "running" ]; then
break
fi
sleep 3
done
echo "Session ended with status: $STATUS"

```

#### Notes

- **All agent operations require super_user.** Authenticate with `harper login`, which stores a
short-lived JWT (operation token) plus a refresh token rather than your password — prefer that
over passing credentials inline, and never embed a raw password in scripts or client config.
- **Approvals are your safety gate.** With `autoApprove:false`, the agent pauses before destructive

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In YAML, keys and values should be separated by a space after the colon (e.g., autoApprove: false instead of autoApprove:false). This ensures standard YAML formatting and prevents potential parsing issues if users copy-paste this configuration.

Suggested change
- **Approvals are your safety gate.** With `autoApprove:false`, the agent pauses before destructive
- **Approvals are your safety gate.** With `autoApprove: false`, the agent pauses before destructive

tools (writing files, deploying, restarting) so an operator decides. Set `autoApprove:true` only
when you want unattended runs.
- **Sessions are single-active.** A session that is `running` or `awaiting_approval` rejects a new
`agent_prompt`; resolve the approval or cancel the run first.
- **MCP alternative.** For MCP-native clients, an instance with MCP enabled exposes the agent as
curated MCP tools (`agent_prompt`, `get_agent_session`, `list_agent_sessions`) at the ops API's
`/mcp` endpoint — the same delegation loop over the MCP transport instead of raw operations.
1 change: 1 addition & 0 deletions harper-best-practices/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ See the concrete examples embedded in each rule subsection below (GraphQL schema
- `serving-web-content` — How to serve static files and integrated Vite/React applications in Harper.
- `logging` — Best practices for logging in Harper, including console capture, the granular logger interface, and programmatic log retrieval.
- `load-env` — How to load environment variables from .env files into a Harper application using the loadEnv plugin.
- `delegating-to-the-built-in-agent` — How to delegate tasks to Harper's built-in agent via the CLI and the agent operations API.

<!-- END GENERATED INDEX -->

Expand Down
9 changes: 9 additions & 0 deletions harper-best-practices/rules.manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,12 @@ rules:
- 'process.env'
- 'override'
- 'config.yaml'

- rule: delegating-to-the-built-in-agent
description: How to delegate tasks to Harper's built-in agent via the CLI and the agent operations API.
category: ops
priority: 4
order: 7
mode: synthesized
cross_links:
- deploying-to-harper-fabric
141 changes: 141 additions & 0 deletions harper-best-practices/rules/delegating-to-the-built-in-agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
name: delegating-to-the-built-in-agent
description: How to delegate tasks to Harper's built-in agent via the CLI and the agent operations API.
metadata:
mode: synthesized
---

# Delegating to the Built-in Agent

Harper 5.2+ ships with a **built-in agent** that runs _inside_ the server, on the main thread
adjacent to the operations API. Because it runs in-process, it can do things a remote client
cannot: call the operations API as RBAC-filtered tools, read and write component files under the
instance's components root, attach the V8 inspector to worker threads to debug and profile them,
schedule follow-up work, and consult the Harper best-practices skill. You send it a natural-language
task; it runs a tool-using loop under a super_user identity and reports back.

## When to Use

Delegate to the built-in agent when the work is best done **on the instance itself** rather than
from your local client:

- Operating on a deployed instance in place — inspect the schema, build or adjust a component,
restart, run an operation.
- Debugging or profiling a running instance — attach to a worker thread, capture a CPU profile,
set a logpoint.
- Handing off a larger, multi-step task to an agent that already has the instance's tools,
filesystem, and credentials in context.

Do the work in your own client instead when it's purely local (editing source before deploy) or
when you don't want a server-side agent making changes.

**Prerequisites:** the target instance must have the agent enabled (an `agent:` config block with
`enabled: true`) and a configured generative model backend. All agent operations require
**super_user**.

## How It Works

The lifecycle assumes you have already deployed to and authenticated with the target instance (see
[deploying-to-harper-fabric.md](deploying-to-harper-fabric.md) — `harper login` stores a token so
you don't repeat credentials). Delegation reuses that same target and credentials.

There are two equivalent ways to drive the agent.

### Option A — the `harper agent` CLI (simplest)

A thin client over the agent operations API that reuses your stored `harper login` credentials, so
no connector setup is needed:

```bash
# One-shot: send a task, print the reply, exit
harper agent "Describe the schema, then add a price index to the Product table."

# Interactive session (REPL)
harper agent

# Against a specific remote instead of the logged-in default
harper agent --target <Application URL> "List the databases and tables."
```

The CLI polls the run to completion and renders the transcript (tool calls, results, and the
agent's reply). When a run needs approval for a destructive action, it prompts you inline.

### Option B — the agent operations API (programmatic)

Call the operations API directly (HTTP POST to the ops endpoint, super_user auth). This is the path
to use from scripts and services.

1. **Start a task** with `agent_prompt`. Returns a `session_id` and a `status`.

```bash
curl -s -u <user>:<pass> <ops-endpoint> \
-H 'Content-Type: application/json' \
-d '{"operation":"agent_prompt","message":"Build a Customer table (id, email, name) exported over REST."}'
```

2. **Poll for progress** with `get_agent_session`, passing the `session_id`. The returned session
carries the `status`, the `messages` transcript, and any `pendingApprovals`.

```bash
curl -s -u <user>:<pass> <ops-endpoint> \
-H 'Content-Type: application/json' \
-d '{"operation":"get_agent_session","session_id":"<id>"}'
```

Poll until `status` leaves `running` — terminal states are `completed`, `aborted`, and `error`;
`awaiting_approval` means it is paused for an approval decision (see step 3).

3. **Approve or deny a paused action.** When the agent enabled configuration has `autoApprove:false`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In YAML, keys and values should be separated by a space after the colon (e.g., autoApprove: false instead of autoApprove:false). This ensures standard YAML formatting and prevents potential parsing issues if users copy-paste this configuration.

Suggested change
3. **Approve or deny a paused action.** When the agent enabled configuration has `autoApprove:false`,
3. **Approve or deny a paused action.** When the agent enabled configuration has `autoApprove: false`,

a destructive tool call pauses the run with a `pendingApprovals[]` entry. Resolve it with
`approve_agent_action`, then poll again — approval executes the saved call, denial hands the
rejection back to the agent so it can adjust.

```bash
curl -s -u <user>:<pass> <ops-endpoint> \
-H 'Content-Type: application/json' \
-d '{"operation":"approve_agent_action","session_id":"<id>","approval_id":"<approval-id>","approved":true}'
```

4. **Continue the conversation** by passing the same `session_id` back into `agent_prompt` with a
new `message`. Omit `session_id` to start a fresh session.

Supporting operations: `list_agent_sessions` (recent sessions), `cancel_agent_run` (terminate a
running or paused session), and `set_agent_config` (adjust `autoApprove`, `allowDestructive`,
`model`, and related settings on a running instance).

## Examples

**Delegate a build to a deployed Fabric instance and wait for the result:**

```bash
harper login <Application URL>
harper agent --target <Application URL> \
"Create a Product table (id, name, price) exported over REST, then confirm the endpoint responds."
```

**Programmatic start-and-poll loop:**

```bash
SID=$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \
-d '{"operation":"agent_prompt","message":"Add a vector index to the Document.embedding field."}' \
| jq -r .session_id)

while [ "$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \
-d "{\"operation\":\"get_agent_session\",\"session_id\":\"$SID\"}" | jq -r .status)" = "running" ]; do
sleep 3
done
Comment on lines +123 to +126

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current programmatic polling loop exits as soon as the status is no longer "running". However, if the session transitions to awaiting_approval, the loop terminates silently without indicating the final status or prompting the user. Capturing the final status in a variable and printing it after the loop exits provides better visibility and debugging information.

Suggested change
while [ "$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \
-d "{\"operation\":\"get_agent_session\",\"session_id\":\"$SID\"}" | jq -r .status)" = "running" ]; do
sleep 3
done
while true; do
STATUS=$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \
-d "{\"operation\":\"get_agent_session\",\"session_id\":\"$SID\"}" | jq -r .status)
if [ "$STATUS" != "running" ]; then
break
fi
sleep 3
done
echo "Session ended with status: $STATUS"

```

## Notes

- **All agent operations require super_user.** Authenticate with `harper login`, which stores a
short-lived JWT (operation token) plus a refresh token rather than your password — prefer that
over passing credentials inline, and never embed a raw password in scripts or client config.
- **Approvals are your safety gate.** With `autoApprove:false`, the agent pauses before destructive

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In YAML, keys and values should be separated by a space after the colon (e.g., autoApprove: false instead of autoApprove:false). This ensures standard YAML formatting and prevents potential parsing issues if users copy-paste this configuration.

Suggested change
- **Approvals are your safety gate.** With `autoApprove:false`, the agent pauses before destructive
- **Approvals are your safety gate.** With `autoApprove: false`, the agent pauses before destructive

tools (writing files, deploying, restarting) so an operator decides. Set `autoApprove:true` only
when you want unattended runs.
- **Sessions are single-active.** A session that is `running` or `awaiting_approval` rejects a new
`agent_prompt`; resolve the approval or cancel the run first.
- **MCP alternative.** For MCP-native clients, an instance with MCP enabled exposes the agent as
curated MCP tools (`agent_prompt`, `get_agent_session`, `list_agent_sessions`) at the ops API's
`/mcp` endpoint — the same delegation loop over the MCP transport instead of raw operations.