diff --git a/CLAUDE.md b/CLAUDE.md index f3ddc381..8d01ce66 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -116,8 +116,10 @@ hand-maintained per-file tree drifts out of sync with the code. speaks to `hive-c0re`. Same split rationale as the two above; the shared payload types it references stay in `hive-sh4re`. - **`hive-agent-sock/`** — wire types for the *in-agent* socket, served by - the harness to in-container producers (matrix / bash MCP daemons, - `forge_notify`). Carries the loose-ends-v2 **todo** ops plus + the harness to in-container producers — matrix / bash MCP daemons and + `forge_notify` are the built-in ones, but any user-configured MCP server + can push todos here too, nothing restricts the `subsystem` set. Carries + the loose-ends-v2 **todo** ops plus harness-local reminders + questions. ⚠️ Distinct from `hive-core-agent-sock` above: **this socket never leaves the container** and `hive-c0re` is not in the path at all — no broker round-trip, no diff --git a/docs/turn-loop/README.md b/docs/turn-loop/README.md index 08e145f0..6c8f3aa7 100644 --- a/docs/turn-loop/README.md +++ b/docs/turn-loop/README.md @@ -101,11 +101,13 @@ the login flow first (`NeedsLogin`). (The forge notification poller used to be spawned here too; it is its own process now — `hive-forge-notify`, see [`forge.md`](../forge.md).) -`spawn_todo_socket` opens the todos store, the socket the in-container -producers dial (matrix / bash / forge-notify daemons), and an -in-process `disk_watch` todo producer for low state-disk space. See +`spawn_todo_socket` opens the todos store and the socket the in-container +producers dial. Matrix / bash / forge-notify daemons and the in-process +`disk_watch` todo producer (low state-disk space) are the built-in +producers, but the socket accepts any `subsystem` marker — a +user-configured MCP server can push its own todos the same way. See [`docs/persistence.md`](../persistence.md#state-dirs-per-agent) for -what each todo producer watches and how the store + `get_loose_ends` +what each built-in todo producer watches and how the store + `get_loose_ends` merge work. Plugin install failures are not fatal: each entry comes back as a diff --git a/docs/web-ui/agent.md b/docs/web-ui/agent.md index 33c3aeb4..e69c76a1 100644 --- a/docs/web-ui/agent.md +++ b/docs/web-ui/agent.md @@ -162,8 +162,10 @@ are acked, the harness won't receive wake-prompts for them. A `{ marked: N }` pill surfaces the count. After the drain the inbox list empties on reload (the filter is `acked_at IS NULL`, so drained messages disappear). Todos flyout ("loose-ends v2"): the harness-local -todos other subsystems (matrix, forge, bash) push at this agent -(`GET /api/todos`) — each row shows the producing subsystem, an +todos other subsystems push at this agent (`GET /api/todos`) — matrix, +forge, and bash are the built-in producers, but any user-configured MCP +server can push its own via the same in-agent socket. Each row shows the +producing subsystem, an optional source label, a summary, and age. A checkbox per row plus a select-all / select-none / `✓ mark done` bulk row above the list POSTs the checked ids to `POST /api/todos/mark-done`, which dismisses them diff --git a/frontend/packages/agent/src/app.js b/frontend/packages/agent/src/app.js index d5862ee5..6a66e6bf 100644 --- a/frontend/packages/agent/src/app.js +++ b/frontend/packages/agent/src/app.js @@ -768,8 +768,9 @@ window.marked = marked; } renderStateBadge(); } - // Todos section: in-agent todos (loose-ends v2) pushed by subsystems - // (matrix, forge, bash). Best-effort fetch on cold load + after every + // Todos section: in-agent todos (loose-ends v2) pushed by subsystems — + // matrix, forge, bash are the built-in ones, but any user-configured MCP + // server can push its own. Best-effort fetch on cold load + after every // turn_end. Silent failure keeps the pill at zero. async function refreshTodos() { try { diff --git a/hive-agent-mcp/src/mcp/render.rs b/hive-agent-mcp/src/mcp/render.rs index 085c54a2..c093e569 100644 --- a/hive-agent-mcp/src/mcp/render.rs +++ b/hive-agent-mcp/src/mcp/render.rs @@ -139,7 +139,8 @@ fn msg_id_tag(id: i64) -> String { /// Hard cap on how many individual `Todo` lines `render_loose_ends` will /// emit. Approvals/questions/reminders stay naturally bounded (they're /// triaged interactively and don't self-multiply), but todos are pushed by -/// unattended producers (matrix/bash/forge) — an agent that goes a long +/// unattended producers — matrix/bash/forge are the built-in ones, but any +/// user-configured MCP server can push its own too — an agent that goes a long /// stretch without calling `get_loose_ends`, or whose producers outpace its /// triage, can accumulate hundreds of them. Rendering all of them /// unconditionally risks producing a tool result too large for the MCP diff --git a/hive-agent-sock/README.md b/hive-agent-sock/README.md index 6a772dd3..d1ff9956 100644 --- a/hive-agent-sock/README.md +++ b/hive-agent-sock/README.md @@ -1,9 +1,12 @@ # hive-agent-sock Wire types for the **in-agent socket** — the one the `hive-agent` harness serves -*inside* the container to its local producers (the matrix / bash MCP daemons and -`forge_notify`). Unlike the host-served sockets, this one **never leaves the -container**. +*inside* the container to its local producers. The matrix / bash MCP daemons +and `forge_notify` are the *built-in* producers that ship today, but any +user-configured MCP server declared in an agent's `agent.nix` can dial this +socket and push its own todos with an arbitrary `subsystem` marker — the +wire format doesn't restrict the set. Unlike the host-served sockets, this +one **never leaves the container**. ## What it carries diff --git a/hive-agent-sock/src/lib.rs b/hive-agent-sock/src/lib.rs index 294a4b43..76d8fb5b 100644 --- a/hive-agent-sock/src/lib.rs +++ b/hive-agent-sock/src/lib.rs @@ -1,9 +1,12 @@ //! Wire types for the *in-agent* socket, served by the hive-agent harness -//! to the in-container producers (matrix / bash MCP daemons) and -//! `forge_notify`. Carries the loose-ends-v2 *todo* op family plus the -//! harness-local *reminder* and *question* op families; more in-agent -//! request families may be added over time (the socket is deliberately -//! named for the agent, not the todos). +//! to the in-container producers. Matrix / bash MCP daemons and +//! `forge_notify` are the built-in ones today, but `subsystem` on the todo +//! ops below is a plain string, not a closed set: any user-configured MCP +//! server declared in an agent's `agent.nix` can dial this socket and push +//! its own todos the same way. Carries the loose-ends-v2 *todo* op family +//! plus the harness-local *reminder* and *question* op families; more +//! in-agent request families may be added over time (the socket is +//! deliberately named for the agent, not the todos). //! //! Distinct from `hive-core-agent-sock`, the *host*-served core↔agent //! protocol on `/run/hive/mcp.sock`: this socket never leaves the @@ -23,12 +26,16 @@ pub const DEFAULT_AGENT_SOCKET: &str = "/run/hive/agent.sock"; /// A request on the in-agent socket. Serialised with a `cmd` tag so the /// in-container producers can emit a plain JSON line without linking a -/// typed client (matrix/bash build the JSON by hand). +/// typed client (matrix/bash build the JSON by hand; a user-configured +/// MCP server can do the same with its own `subsystem` marker). #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "cmd", rename_all = "snake_case")] pub enum Request { - /// Upsert a todo from an in-container subsystem (matrix / bash / - /// forge). `subsystem` is the producer marker; `key` the optional + /// Upsert a todo from an in-container subsystem — matrix / bash / + /// forge are the built-in producers, but any subsystem name is + /// accepted; nothing restricts `subsystem` to a fixed set, so a + /// user-configured MCP server can push its own todos here too. + /// `subsystem` is the producer marker; `key` the optional /// subsystem-specific dedup key (a matrix room id, a bash task id). /// A new-or-changed row signals the turn loop; an identical keyed /// re-push is a silent no-op. Keyless todos always insert as one-offs. diff --git a/hive-agent/src/todo_server.rs b/hive-agent/src/todo_server.rs index 39ff323c..3b628a76 100644 --- a/hive-agent/src/todo_server.rs +++ b/hive-agent/src/todo_server.rs @@ -1,7 +1,9 @@ //! In-agent socket server (loose-ends v2 + harness-local reminders + //! questions mirror + self-service compact). Binds the harness-owned //! `HIVE_AGENT_SOCKET` and serves the `hive-agent-sock` protocol to the -//! in-container producers (matrix / bash daemons, forge-notify) and to +//! in-container producers — matrix / bash daemons and forge-notify are the +//! built-in ones, but any user-configured MCP server can dial the same +//! socket and push its own todos — and to //! `hive-agent-mcp`'s `ask`/`answer`/`remind`/`get_loose_ends`/ //! `cancel_loose_end`/`compact` tool impls. Todo ops hit the harness-local //! [`Todos`] store; a new-or-changed upsert fires an in-process [`Notify`] diff --git a/hive-c0re/src/coordinator.rs b/hive-c0re/src/coordinator.rs index 3e210bcc..46b01658 100644 --- a/hive-c0re/src/coordinator.rs +++ b/hive-c0re/src/coordinator.rs @@ -1387,9 +1387,10 @@ impl Coordinator { /// Push a todo directly into `agent`'s in-container todo store — a /// best-effort *live* dial of its `hive-agent-sock` socket /// (`hive_host_sock::agent_todo_socket`), same `UpsertTodo` request - /// shape the in-container producers (matrix/bash/forge-notify) - /// already send. This is the migration target for `HelperEvent` - /// variants that are pure "FYI, check when convenient" notices: the + /// shape the built-in in-container producers (matrix/bash/forge-notify) + /// already send — `subsystem` isn't restricted to that set, any + /// producer can use the same shape. This is the migration target for + /// `HelperEvent` variants that are pure "FYI, check when convenient" notices: the /// event stops being a broker `Message` (which always drives an /// immediate turn) and becomes a todo instead, with the same /// dedup-by-key semantics as any other producer. diff --git a/hive-sh4re/src/lib.rs b/hive-sh4re/src/lib.rs index a9c9cef0..a61ad7a1 100644 --- a/hive-sh4re/src/lib.rs +++ b/hive-sh4re/src/lib.rs @@ -278,13 +278,18 @@ pub enum LooseEnd { #[serde(default)] summary: String, }, - /// A dynamic, subsystem-pushed todo (loose-ends v2). Produced - /// by an in-container subsystem (matrix / forge / bash) via - /// `UpsertTodo`. Cleared by that subsystem (`ClearTodo`) or by the - /// agent itself (`MarkTodoDone`, by `id`). + /// A dynamic, subsystem-pushed todo (loose-ends v2). Produced by an + /// in-container subsystem via `UpsertTodo` — matrix/bash/forge are the + /// *built-in* producers that ship today, but `subsystem` is a plain + /// string, not a closed set: any user-configured MCP server declared + /// in an agent's `agent.nix` can dial the in-agent socket and push its + /// own todos the same way. Cleared by that subsystem (`ClearTodo`) or + /// by the agent itself (`MarkTodoDone`, by `id`). Todo { id: i64, - /// Producing subsystem marker (`"matrix"`, `"forge"`, `"bash"`, …). + /// Producing subsystem marker (`"matrix"`, `"forge"`, `"bash"`, … + /// — built-in producers; a user-configured MCP server can push + /// its own arbitrary marker here too, nothing enforces the set). subsystem: String, /// Optional subsystem-specific key (matrix room id, bash task id). #[serde(default, skip_serializing_if = "Option::is_none")]