todo: harness ergonomics wishlist (auto-attach oversize / inbox batching / self-cancel / whoami / reply-to)
This commit is contained in:
parent
e3b5837378
commit
0adce04a04
1 changed files with 49 additions and 0 deletions
49
TODO.md
49
TODO.md
|
|
@ -31,6 +31,55 @@
|
||||||
- **Privsep the dashboard from the privileged daemon**: hive-c0re runs as root (it has to — `nixos-container` create / start / destroy, the meta git repo, every per-agent bind mount). The HTTP server lives in the same process, so every read-endpoint (`/api/state-file`, `/api/journal/{name}`, `/api/agent-config/{name}`) is one allow-list bug away from serving arbitrary host files. Split the architecture: keep the privileged daemon doing lifecycle + git + ipc, run the web UI as an unprivileged user that talks to the daemon over a unix socket with a narrow request surface (`ReadAgentStateFile { agent, rel_path }` etc.). The unprivileged process can't read `/etc/shadow` even if every check in `get_state_file` is bypassed — it doesn't have the bits. Container-lifecycle POSTs (`/restart`, `/destroy`, etc.) become forwarded RPCs the privileged side authorises on its terms.
|
- **Privsep the dashboard from the privileged daemon**: hive-c0re runs as root (it has to — `nixos-container` create / start / destroy, the meta git repo, every per-agent bind mount). The HTTP server lives in the same process, so every read-endpoint (`/api/state-file`, `/api/journal/{name}`, `/api/agent-config/{name}`) is one allow-list bug away from serving arbitrary host files. Split the architecture: keep the privileged daemon doing lifecycle + git + ipc, run the web UI as an unprivileged user that talks to the daemon over a unix socket with a narrow request surface (`ReadAgentStateFile { agent, rel_path }` etc.). The unprivileged process can't read `/etc/shadow` even if every check in `get_state_file` is bypassed — it doesn't have the bits. Container-lifecycle POSTs (`/restart`, `/destroy`, etc.) become forwarded RPCs the privileged side authorises on its terms.
|
||||||
- **Defense in depth on `get_state_file`**: until privsep lands, the allow-list is load-bearing. Worth adding: refuse files whose mode is not world-readable (so an agent writing a 0600 file inside `state/` can't have its contents proxied through the endpoint to a different operator), and refuse symlinks at any path component (`O_NOFOLLOW`-style — `canonicalize` resolves them, but we currently don't reject if the original path had symlinks).
|
- **Defense in depth on `get_state_file`**: until privsep lands, the allow-list is load-bearing. Worth adding: refuse files whose mode is not world-readable (so an agent writing a 0600 file inside `state/` can't have its contents proxied through the endpoint to a different operator), and refuse symlinks at any path component (`O_NOFOLLOW`-style — `canonicalize` resolves them, but we currently don't reject if the original path had symlinks).
|
||||||
|
|
||||||
|
## Harness Ergonomics (agent-side wishlist)
|
||||||
|
|
||||||
|
Filed by damocles, who actually lives in this thing. Loosely ranked by
|
||||||
|
how often the friction bites in normal use.
|
||||||
|
|
||||||
|
- **Auto-attach oversize message bodies** — `send` / `ask` / `answer`
|
||||||
|
currently error at the 1 KiB cap and force the caller to manually
|
||||||
|
write a file and re-send a pointer string. Reminders already solve
|
||||||
|
this transparently (`/state/reminders/auto-<ts>.md` + pointer body).
|
||||||
|
Symmetric fix: when an oversize body lands, auto-persist to
|
||||||
|
`/agents/<asker>/state/auto-msg/<ts>-<n>.md` and rewrite the body to
|
||||||
|
`"<short excerpt> — full body at <path>"`. Caller never has to think
|
||||||
|
about it. Reuses the existing path-validation + container→host
|
||||||
|
mapping from `reminder_scheduler.rs`. Same writeup for `send(*)`
|
||||||
|
broadcasts — the cap currently nukes every recipient at once if even
|
||||||
|
one would overflow.
|
||||||
|
- **Inbox batching hint in the wake prompt** — when the harness pops a
|
||||||
|
message and there are N more waiting, the wake prompt should say so
|
||||||
|
(e.g. `"(+3 more queued; consider draining before acting)"`) so claude
|
||||||
|
knows to call `recv()` again in the same turn instead of doing the
|
||||||
|
expensive Read/Edit dance once per message over N turns. The data's
|
||||||
|
already in the broker (`Broker::pending_count(agent)`); just thread it
|
||||||
|
into the prompt builder in `hive-ag3nt::turn.rs`. Even better: add a
|
||||||
|
one-shot `recv_batch(max: u32)` MCP tool that returns up to `max`
|
||||||
|
pending messages in a single round-trip.
|
||||||
|
- **Self-management of own asks + reminders** — once I fire `ask` or
|
||||||
|
`remind` I have no way to inspect or cancel them from the agent side.
|
||||||
|
Operator can cancel asks via dashboard; nothing for reminders at all
|
||||||
|
(TODO above). Want `list_my_asks() -> [{id, target, question, asked_at}]`
|
||||||
|
and `cancel_ask(id)` on the agent surface, plus `list_my_reminders()`
|
||||||
|
/ `cancel_reminder(id)`. Bounded by `asker == self` and `reminder.owner
|
||||||
|
== self` so no cross-agent meddling.
|
||||||
|
- **`whoami` introspection tool** — agents currently rely on the system
|
||||||
|
prompt remembering their name + role. After a rename or model swap
|
||||||
|
there's no trustworthy source-of-truth from inside the harness.
|
||||||
|
Cheap: a `whoami() -> { name, role: "agent" | "manager", model, port,
|
||||||
|
hyperhive_rev, started_at }` tool reading from the harness's own env
|
||||||
|
+ `TurnState`. Useful for self-documenting state files ("this dropped
|
||||||
|
by damocles@gpt-5-codex on rev abc1234") and for the future
|
||||||
|
`get_open_threads` to know whose threads to query without
|
||||||
|
trusting prompt-substituted strings.
|
||||||
|
- **Optional `in_reply_to: <msg_id>` on send** — pure wire addition; no
|
||||||
|
behavioural change. The dashboard could render conversation threads
|
||||||
|
(already wants this for the agent-to-agent question UI in the
|
||||||
|
Dashboard section). Today every reply is a fresh root in the message
|
||||||
|
flow which obscures cause-and-effect when two agents are mid-debate.
|
||||||
|
Field is optional, ignored if the referenced id is unknown / cross-
|
||||||
|
agent / out of retention.
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
- **Post-rebuild system-message missed wake**: at 09:13:14 the dashboard showed `system → damocles container rebuilt` as ✓ delivered, but the agent harness never ran a turn for it (no claude invocation, no operator-visible activity). A subsequent `recv()` from inside the agent returned `(empty)`, confirming the message was popped + marked delivered server-side — yet drove no turn. Most likely cause: the agent_server `serve_agent_stdio` task is up and answering MCP/socket calls, but the `hive-ag3nt::serve` long-poll loop that drives `drive_turn` either died silently during rebuild or never restarted. Investigate: (a) does hive-ag3nt's serve loop survive `nixos-container update` cleanly, or does its tokio runtime get torn down mid-loop? (b) is there an early-exit path on a transient socket error during rebuild that drops the serve task without notifying the manager? (c) compare timeline with manager's own post-rebuild wake to see if this is rebuilt-agents-only or universal. Could be related to the `recv_blocking` fix in `e423d57` if the rebuild restarts the broker mid-subscribe.
|
- **Post-rebuild system-message missed wake**: at 09:13:14 the dashboard showed `system → damocles container rebuilt` as ✓ delivered, but the agent harness never ran a turn for it (no claude invocation, no operator-visible activity). A subsequent `recv()` from inside the agent returned `(empty)`, confirming the message was popped + marked delivered server-side — yet drove no turn. Most likely cause: the agent_server `serve_agent_stdio` task is up and answering MCP/socket calls, but the `hive-ag3nt::serve` long-poll loop that drives `drive_turn` either died silently during rebuild or never restarted. Investigate: (a) does hive-ag3nt's serve loop survive `nixos-container update` cleanly, or does its tokio runtime get torn down mid-loop? (b) is there an early-exit path on a transient socket error during rebuild that drops the serve task without notifying the manager? (c) compare timeline with manager's own post-rebuild wake to see if this is rebuilt-agents-only or universal. Could be related to the `recv_blocking` fix in `e423d57` if the rebuild restarts the broker mid-subscribe.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue