Watch
0
0
Fork
You've already forked hyperhive
0

read an agent's agents.md through the swarm and edit it with git diffs

The agent's state/AGENTS.md stays the truth and its harness the only
writer. The harness mirrors the file into a new `agents-md` KV bucket,
and swarm-controller serves that copy, so a read works while the agent is
stopped. An edit is a unified diff plus the revision it was made against,
sent over the agent's existing control subject; the harness refuses a
stale base, applies the diff with `git apply --check` then `git apply` to
a temp copy, and writes the file only when both pass. Fixes #5519.

Files:
- swarm-queue-client/src/agents_md.rs, lib.rs: the bucket name and
  subject, the mirror value, the `ApplyAgentsMd` control command and its
  reply. `AgentControl` loses `Copy` because the new variant carries text.
- hive-agent/src/swarm_agents_md.rs, main.rs: the mirror task (start,
  10 s re-read, immediate republish after an edit) and the apply.
- hive-agent/src/swarm_input.rs: the control loop answers both commands.
- hive-agent/src/claude_md_watch.rs: its AGENTS.md path is shared.
- hive-agent/Cargo.toml, Cargo.lock: sha1 for the git blob id (already in
  the lock), tempfile moves from dev to runtime for the apply directory.
- swarm-controller/src/agents_md.rs, main.rs: the bucket reader and
  GET/PUT /api/agents/{name}/agents-md; test AppState literals get the
  new field.
- swarm-controller/src/agent_control.rs: the sink hands back raw reply
  bytes, so each command decodes its own reply type.
- swarm-controller-api/src/agents_md.rs: the route bodies, shared with
  swarmctl.
- swarmctl/src/agents_md.rs, main.rs: `swarmctl agent agents-md get|apply`.
- nix/host-modules/swarm-nats.nix, nix/module-eval/nats-authelia.nix: the
  agent's publish grant on its own `$KV.agents-md.<agent>` key.

git needs no new package: agent containers enable programs.git, and the
harness unit's PATH includes /run/current-system/sw.
This commit is contained in:
atlas 2026-10-18 03:11:15 +02:00 • committed by mara
commit 88674eb63a
22 changed files with 1296 additions and 47 deletions

View file

@ -209,6 +209,11 @@ pub mod agent_token;
/// placement stays out of the key, and for who may write it.
pub mod agent_icon;
/// The bucket each agent mirrors its `AGENTS.md` into, and the edit command
/// for it. The names and types are unconditional; opening the bucket needs
/// `kv`.
pub mod agents_md;
/// The bucket swarm-controller declares each agent's scheduled prompts in —
/// one key per agent, read by that agent alone. The names are unconditional;
/// writing needs `kv` and reading needs `agent-schedules`.
@ -427,14 +432,18 @@ pub fn client_inbox_prefix(client_id: &str) -> String {
/// The harness acts on a command only because the controller is the one
/// client granted publish on the subject, so there is no sender field to
/// trust or forge here, unlike [`AgentInput::from`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AgentControl {
/// Interrupt the turn in flight, as the agent's own cancel button does.
/// Answered with an [`AgentControlReply`].
Cancel,
/// Apply a diff to the agent's `AGENTS.md`. Answered with an
/// [`agents_md::ApplyReply`].
ApplyAgentsMd(agents_md::Apply),
}
/// What an agent answers an [`AgentControl`] request with.
/// What an agent answers an [`AgentControl::Cancel`] request with.
///
/// The controller sends a command as a request, so a reply is the only
/// confirmation the operator gets that the harness took it. No reply within