document reading and editing an agent's agents.md through the swarm
docs/swarm/README.md gets the routes, the revision, the apply semantics and the stopped-agent behaviour; the persistence doc points there from the AGENTS.md entry. The swarm-controller, swarm-controller-api and swarmctl READMEs and the repo map in AGENTS.md list the new bodies and routes. docs/tools/swarmctl-cli.md is regenerated for `swarmctl agent agents-md`, whose help text loses two passive phrasings vale flags.
This commit is contained in:
parent
88674eb63a
commit
9add99838c
8 changed files with 124 additions and 13 deletions
|
|
@ -118,11 +118,11 @@ Swarm-level:
|
||||||
**the socket's directory is its access control**; the crate README has
|
**the socket's directory is its access control**; the crate README has
|
||||||
the constraint and a unit test pins the path.
|
the constraint and a unit test pins the path.
|
||||||
- **`swarm-controller-api/`** — serde-only request and response bodies of
|
- **`swarm-controller-api/`** — serde-only request and response bodies of
|
||||||
the controller's HTTP API, shared with `swarmctl` (forge, schedules and
|
the controller's HTTP API, shared with `swarmctl` (forge, schedules,
|
||||||
the agent-create and forge-token responses).
|
`AGENTS.md` edits, and the agent-create and forge-token responses).
|
||||||
- **`swarmctl/`** — swarm operator CLI; runs as **root and acts directly**
|
- **`swarmctl/`** — swarm operator CLI; runs as **root and acts directly**
|
||||||
(README says why). Doesn't link `swarm-controller`: the forge and
|
(README says why). Doesn't link `swarm-controller`: the forge, schedule
|
||||||
schedule bodies and the agent responses come from
|
and `AGENTS.md` bodies and the agent responses come from
|
||||||
`swarm-controller-api`; the agent-create request, inference and
|
`swarm-controller-api`; the agent-create request, inference and
|
||||||
permissions ones are a local mirror. ⚠️ Edits authelia's
|
permissions ones are a local mirror. ⚠️ Edits authelia's
|
||||||
**`users.yml` in place**, shared with `swarm-authelia-bridge`; that
|
**`users.yml` in place**, shared with `swarm-authelia-bridge`; that
|
||||||
|
|
|
||||||
|
|
@ -276,7 +276,10 @@ Under `/var/lib/hyperhive/agents/<name>/`:
|
||||||
`agents_md_migrate::run` renames a `state/CLAUDE.md` to `AGENTS.md`
|
`agents_md_migrate::run` renames a `state/CLAUDE.md` to `AGENTS.md`
|
||||||
when no `AGENTS.md` exists. When both exist it renames neither and
|
when no `AGENTS.md` exists. When both exist it renames neither and
|
||||||
logs an error naming both paths: claude loads only `CLAUDE.md` while
|
logs an error naming both paths: claude loads only `CLAUDE.md` while
|
||||||
one exists in its cwd or above.
|
one exists in its cwd or above. The harness also publishes the file
|
||||||
|
to the swarm, which reads that copy, and applies the git diffs the
|
||||||
|
swarm sends to edit it. This file stays the only copy that counts:
|
||||||
|
[`docs/swarm/README.md`](../swarm/README.md#an-agents-agentsmd).
|
||||||
- `harness/` — harness-internal ephemeral state; not intended for
|
- `harness/` — harness-internal ephemeral state; not intended for
|
||||||
agent consumption. Bind-mounted to `/agents/<name>/harness`
|
agent consumption. Bind-mounted to `/agents/<name>/harness`
|
||||||
inside the container (`$HYPERHIVE_HARNESS_DIR`). Contents:
|
inside the container (`$HYPERHIVE_HARNESS_DIR`). Contents:
|
||||||
|
|
|
||||||
|
|
@ -278,13 +278,19 @@ The third thing an agent publishes is its **icon**, the same SVG its own
|
||||||
`GET /icon` serves. It goes into the `agent-icons` KV bucket under the key
|
`GET /icon` serves. It goes into the `agent-icons` KV bucket under the key
|
||||||
`<agent>`, with no hive in it, so the swarm can show the icon of an agent that's
|
`<agent>`, with no hive in it, so the swarm can show the icon of an agent that's
|
||||||
stopped or has moved hives. The queue grants the agent's own credential
|
stopped or has moved hives. The queue grants the agent's own credential
|
||||||
`$KV.agent-icons.<agent>` and no other key. The harness writes once per start,
|
`$KV.agent-icons.<agent>` and no other key of that bucket. The harness writes
|
||||||
because a config change reaches an agent by restarting its container. An agent
|
once per start, because a config change reaches an agent by restarting its
|
||||||
with no icon deletes its key. Swarm-side, `GET /api/agents/<name>/icon` serves
|
container. An agent with no icon deletes its key. Swarm-side,
|
||||||
|
`GET /api/agents/<name>/icon` serves
|
||||||
the stored bytes, and 404 means the agent has no icon. swarm-ui's agent cards
|
the stored bytes, and 404 means the agent has no icon. swarm-ui's agent cards
|
||||||
load it as an `<img>` and show the dimmed hyperhive mark for an agent without
|
load it as an `<img>` and show the dimmed hyperhive mark for an agent without
|
||||||
one, as the hive dashboard does.
|
one, as the hive dashboard does.
|
||||||
|
|
||||||
|
The fourth is its **`AGENTS.md`**, which goes into the `agents-md` bucket under
|
||||||
|
the key `<agent>`, writable by the agent's own credential alone
|
||||||
|
(`$KV.agents-md.<agent>`). The swarm reads that copy and edits the file through
|
||||||
|
the agent; [An agent's AGENTS.md](#an-agents-agentsmd) has the details.
|
||||||
|
|
||||||
The queue also limits what an agent may **subscribe** to, because agents are the
|
The queue also limits what an agent may **subscribe** to, because agents are the
|
||||||
least trusted clients on the queue. An agent's own queue credential, which its
|
least trusted clients on the queue. An agent's own queue credential, which its
|
||||||
subagent daemon also presents, may subscribe to its reply inbox
|
subagent daemon also presents, may subscribe to its reply inbox
|
||||||
|
|
@ -309,6 +315,41 @@ no replies, so every request it makes times out.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### An agent's AGENTS.md
|
||||||
|
|
||||||
|
The file in the agent's state dir, `state/AGENTS.md`, is the only copy that
|
||||||
|
counts, and only the agent writes it: itself, or its harness applying an edit
|
||||||
|
from the swarm. No hive is in either path.
|
||||||
|
|
||||||
|
**Reading.** The harness publishes the file's text and its revision, the git
|
||||||
|
blob id that `git hash-object` prints for it, at start, within ten seconds of
|
||||||
|
any change, and straight after an edit it applied. An agent with no `AGENTS.md`
|
||||||
|
publishes `""`. `GET /api/agents/<name>/agents-md` answers `{content, revision}`
|
||||||
|
from that copy, so it works while the agent isn't running. A 404 means the agent
|
||||||
|
hasn't published one: its harness hasn't run since it began to.
|
||||||
|
|
||||||
|
**Editing.** `PUT /api/agents/<name>/agents-md` takes `{base_revision, diff}`.
|
||||||
|
The diff is a unified diff that names `a/AGENTS.md` and `b/AGENTS.md`, made
|
||||||
|
against the text at `base_revision`. swarm-controller sends it as a request on
|
||||||
|
the agent's command subject `$SWARM.control.<name>`, as it sends a cancel. The
|
||||||
|
harness refuses it when the file isn't at `base_revision`. Otherwise it runs
|
||||||
|
`git apply --check` and then `git apply` on a copy of the file, and writes the
|
||||||
|
result over the file only when both pass:
|
||||||
|
|
||||||
|
| Status | Meaning |
|
||||||
|
| ------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 200 | applied; the body is `{revision}`, the file's new revision |
|
||||||
|
| 409 | the file isn't at `base_revision` any more; nothing applied, and `revision` in the body is the one it's at |
|
||||||
|
| 422 | `git apply` refused the diff, or it touches a file other than `AGENTS.md`; `detail` carries git's message, and the file stays as it was |
|
||||||
|
| 503 | the agent isn't running or isn't connected to the queue |
|
||||||
|
| 504 | the harness didn't answer within ten seconds, so whether the edit applied is unknown |
|
||||||
|
| 500 | the harness couldn't read or write the file or run git, or the queue failed the request |
|
||||||
|
|
||||||
|
An edit needs the agent running, and a read of a stopped agent shows the text
|
||||||
|
it last published. A harness older than this route doesn't know the command and
|
||||||
|
drops it unanswered, which reads as a 504. `swarmctl agent agents-md get` and
|
||||||
|
`apply` call the two routes.
|
||||||
|
|
||||||
### Swarm-wide forge objects
|
### Swarm-wide forge objects
|
||||||
|
|
||||||
The controller also keeps the forge objects that are one per swarm, not
|
The controller also keeps the forge objects that are one per swarm, not
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ This document contains the help content for the `swarmctl` command-line program.
|
||||||
* [`swarmctl agent permissions get`↴](#swarmctl-agent-permissions-get)
|
* [`swarmctl agent permissions get`↴](#swarmctl-agent-permissions-get)
|
||||||
* [`swarmctl agent permissions set`↴](#swarmctl-agent-permissions-set)
|
* [`swarmctl agent permissions set`↴](#swarmctl-agent-permissions-set)
|
||||||
* [`swarmctl agent permissions clear`↴](#swarmctl-agent-permissions-clear)
|
* [`swarmctl agent permissions clear`↴](#swarmctl-agent-permissions-clear)
|
||||||
|
* [`swarmctl agent agents-md`↴](#swarmctl-agent-agents-md)
|
||||||
|
* [`swarmctl agent agents-md get`↴](#swarmctl-agent-agents-md-get)
|
||||||
|
* [`swarmctl agent agents-md apply`↴](#swarmctl-agent-agents-md-apply)
|
||||||
* [`swarmctl schedule`↴](#swarmctl-schedule)
|
* [`swarmctl schedule`↴](#swarmctl-schedule)
|
||||||
* [`swarmctl schedule add`↴](#swarmctl-schedule-add)
|
* [`swarmctl schedule add`↴](#swarmctl-schedule-add)
|
||||||
* [`swarmctl schedule list`↴](#swarmctl-schedule-list)
|
* [`swarmctl schedule list`↴](#swarmctl-schedule-list)
|
||||||
|
|
@ -71,6 +74,7 @@ Manage agents across the swarm
|
||||||
* `mint-forge-token` — Check one agent's forge token, and mint it if it's missing or stale
|
* `mint-forge-token` — Check one agent's forge token, and mint it if it's missing or stale
|
||||||
* `inference` — Show the model, effort, subagent models and subagent cap the swarm sets for an agent, or change the model, effort and subagent models
|
* `inference` — Show the model, effort, subagent models and subagent cap the swarm sets for an agent, or change the model, effort and subagent models
|
||||||
* `permissions` — Show or change the permissions the swarm grants an agent
|
* `permissions` — Show or change the permissions the swarm grants an agent
|
||||||
|
* `agents-md` — Show an agent's AGENTS.md, or edit it with a diff
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -274,6 +278,61 @@ Clear the agent's permissions, so the default applies again
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `swarmctl agent agents-md`
|
||||||
|
|
||||||
|
Show an agent's AGENTS.md, or edit it with a diff.
|
||||||
|
|
||||||
|
`get` shows the copy the agent last published, so it works while the agent isn't running. `apply` hands the diff to the agent, which applies it to the file with `git apply`; it needs the agent running.
|
||||||
|
|
||||||
|
**Usage:** `swarmctl agent agents-md <COMMAND>`
|
||||||
|
|
||||||
|
###### **Subcommands:**
|
||||||
|
|
||||||
|
* `get` — Print an agent's AGENTS.md on stdout, and its revision on stderr
|
||||||
|
* `apply` — Apply a unified diff to an agent's AGENTS.md
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `swarmctl agent agents-md get`
|
||||||
|
|
||||||
|
Print an agent's AGENTS.md on stdout, and its revision on stderr
|
||||||
|
|
||||||
|
**Usage:** `swarmctl agent agents-md get [OPTIONS] <AGENT>`
|
||||||
|
|
||||||
|
###### **Arguments:**
|
||||||
|
|
||||||
|
* `<AGENT>` — Agent name
|
||||||
|
|
||||||
|
###### **Options:**
|
||||||
|
|
||||||
|
* `--controller-socket <PATH>` — swarm-controller's unix socket.
|
||||||
|
|
||||||
|
Supplied by the nix module that installs this binary, from the same `socketPath` option the daemon binds; falls back to `SWARM_CONTROLLER_SOCKET`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `swarmctl agent agents-md apply`
|
||||||
|
|
||||||
|
Apply a unified diff to an agent's AGENTS.md.
|
||||||
|
|
||||||
|
The diff names `a/AGENTS.md` and `b/AGENTS.md`, as `diff -u a/AGENTS.md b/AGENTS.md` writes it. The agent refuses it when the file is no longer at `--base`, or when it doesn't apply, and leaves the file as it was.
|
||||||
|
|
||||||
|
**Usage:** `swarmctl agent agents-md apply [OPTIONS] --base <REVISION> <AGENT> <DIFF_FILE>`
|
||||||
|
|
||||||
|
###### **Arguments:**
|
||||||
|
|
||||||
|
* `<AGENT>` — Agent name
|
||||||
|
* `<DIFF_FILE>` — The file holding the diff
|
||||||
|
|
||||||
|
###### **Options:**
|
||||||
|
|
||||||
|
* `--base <REVISION>` — The revision you made the diff against, as `get` printed it. It's the git blob id of that text, so `git hash-object` of the unedited file prints it too
|
||||||
|
* `--controller-socket <PATH>` — swarm-controller's unix socket.
|
||||||
|
|
||||||
|
Supplied by the nix module that installs this binary, from the same `socketPath` option the daemon binds; falls back to `SWARM_CONTROLLER_SOCKET`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `swarmctl schedule`
|
## `swarmctl schedule`
|
||||||
|
|
||||||
Manage scheduled prompts.
|
Manage scheduled prompts.
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ controller's `OpenAPI` spec. `swarmctl` leaves it off.
|
||||||
## What's here
|
## What's here
|
||||||
|
|
||||||
One module per endpoint family: `agents` (the responses of `POST /api/agents`
|
One module per endpoint family: `agents` (the responses of `POST /api/agents`
|
||||||
and `POST /api/agents/{name}/forge-token`), `forge`
|
and `POST /api/agents/{name}/forge-token`), `agents_md`
|
||||||
|
(`/api/agents/{name}/agents-md`), `forge`
|
||||||
(`POST /api/forge/users/{name}/admin`) and `schedules` (`/api/schedules`).
|
(`POST /api/forge/users/{name}/admin`) and `schedules` (`/api/schedules`).
|
||||||
The agent-create request, inference and permissions bodies are still mirrored
|
The agent-create request, inference and permissions bodies are still mirrored
|
||||||
by hand in `swarmctl`.
|
by hand in `swarmctl`.
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,12 @@ The swarm's control plane. The swarm UI and `swarmctl` are its clients.
|
||||||
(`GET`/`PUT`/`DELETE /api/agents/{name}/limits`); the hive running the
|
(`GET`/`PUT`/`DELETE /api/agents/{name}/limits`); the hive running the
|
||||||
agent reads it, and each cap replaces the hive's own answer for that field →
|
agent reads it, and each cap replaces the hive's own answer for that field →
|
||||||
[`docs/scheduler/coordinator.md`](../docs/scheduler/coordinator.md#container-resource-limits).
|
[`docs/scheduler/coordinator.md`](../docs/scheduler/coordinator.md#container-resource-limits).
|
||||||
|
- **Agent instructions** — each agent's `AGENTS.md` as its harness last
|
||||||
|
published it into the `agents-md` KV bucket
|
||||||
|
(`GET /api/agents/{name}/agents-md`), and edits to it as git diffs, which
|
||||||
|
this daemon hands to the agent's harness to apply
|
||||||
|
(`PUT /api/agents/{name}/agents-md`) →
|
||||||
|
[`docs/swarm/README.md`](../docs/swarm/README.md#an-agents-agentsmd).
|
||||||
- **Review classifier** — try-outs from the swarm UI and reviews on review
|
- **Review classifier** — try-outs from the swarm UI and reviews on review
|
||||||
requests, and the newest 100 finished runs, one document in the
|
requests, and the newest 100 finished runs, one document in the
|
||||||
`review-classifier-runs` KV bucket, which this daemon alone writes
|
`review-classifier-runs` KV bucket, which this daemon alone writes
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@ that talk to the controller, from anywhere as your own SSO account (see
|
||||||
|
|
||||||
Distinct from `hivectl`, which drives one hive's `hive-c0re` over its
|
Distinct from `hivectl`, which drives one hive's `hive-c0re` over its
|
||||||
admin socket. This crate doesn't link `swarm-controller`, for the same
|
admin socket. This crate doesn't link `swarm-controller`, for the same
|
||||||
reason `hivectl` doesn't link `hive-c0re`; the forge and schedule bodies
|
reason `hivectl` doesn't link `hive-c0re`; the forge, schedule and
|
||||||
and the agent responses both sides speak come from `swarm-controller-api`,
|
`AGENTS.md` bodies and the agent responses both sides speak come from
|
||||||
|
`swarm-controller-api`,
|
||||||
as `hivectl`'s come from `hive-host-sock`.
|
as `hivectl`'s come from `hive-host-sock`.
|
||||||
|
|
||||||
## Why root, and why no socket
|
## Why root, and why no socket
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,7 @@ enum AgentVerb {
|
||||||
/// Show an agent's AGENTS.md, or edit it with a diff.
|
/// Show an agent's AGENTS.md, or edit it with a diff.
|
||||||
///
|
///
|
||||||
/// `get` shows the copy the agent last published, so it works while the
|
/// `get` shows the copy the agent last published, so it works while the
|
||||||
/// agent is stopped. `apply` hands the diff to the agent, which applies
|
/// agent isn't running. `apply` hands the diff to the agent, which applies
|
||||||
/// it to the file with `git apply`; it needs the agent running.
|
/// it to the file with `git apply`; it needs the agent running.
|
||||||
AgentsMd {
|
AgentsMd {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
|
@ -333,7 +333,7 @@ struct AgentsMdApplyArgs {
|
||||||
agent: String,
|
agent: String,
|
||||||
/// The file holding the diff.
|
/// The file holding the diff.
|
||||||
diff_file: PathBuf,
|
diff_file: PathBuf,
|
||||||
/// The revision the diff was made against, as `get` printed it. It is
|
/// The revision you made the diff against, as `get` printed it. It's
|
||||||
/// the git blob id of that text, so `git hash-object` of the unedited
|
/// the git blob id of that text, so `git hash-object` of the unedited
|
||||||
/// file prints it too.
|
/// file prints it too.
|
||||||
#[arg(long, value_name = "REVISION")]
|
#[arg(long, value_name = "REVISION")]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue