manager mcp: expose 'remind' tool sharing storage helper with agent surface

This commit is contained in:
damocles 2026-05-17 11:43:14 +02:00
parent 0e6bac8388
commit 1770b51845
6 changed files with 120 additions and 24 deletions

View file

@ -165,6 +165,20 @@ pub fn write_payload(agent: &str, host_path: &Path, message: &str) -> Result<(),
Ok(())
}
/// Container-visible state prefix the caller's `file_path` must live
/// under. Sub-agents see their state at `/agents/<name>/state/`;
/// the manager keeps the legacy `/state/` mount (see
/// `lifecycle::set_nspawn_flags`). Auto-file paths use the same
/// prefix so the round-trip is symmetric.
#[must_use]
pub fn container_state_prefix(agent: &str) -> String {
if agent == hive_sh4re::MANAGER_AGENT {
"/state/".to_owned()
} else {
format!("/agents/{agent}/state/")
}
}
/// Map an agent-visible container path to the matching host path,
/// validating that it lives under the agent's own state subtree, has
/// a non-empty relative tail, and doesn't try to traverse out via
@ -172,7 +186,7 @@ pub fn write_payload(agent: &str, host_path: &Path, message: &str) -> Result<(),
/// reason string on rejection. `pub` so `agent_server::handle_remind`
/// can reuse it for the at-remind-time auto-file path.
pub fn resolve_host_path(agent: &str, req_path: &str) -> Result<PathBuf, String> {
let prefix = format!("/agents/{agent}/state/");
let prefix = container_state_prefix(agent);
let Some(rel) = req_path.strip_prefix(&prefix) else {
return Err(format!(
"must be absolute and under `{prefix}` (got `{req_path}`)"
@ -230,6 +244,21 @@ mod tests {
);
}
#[test]
fn manager_uses_legacy_state_prefix() {
// The manager container mounts its state at `/state/` (legacy),
// not `/agents/manager/state/`. Same host path; different
// container-visible path. resolve_host_path needs to know.
assert_eq!(container_state_prefix("manager"), "/state/");
let p = resolve_host_path("manager", "/state/reminders/x.md").unwrap();
assert_eq!(
p,
PathBuf::from("/var/lib/hyperhive/agents/manager/state/reminders/x.md")
);
// And the sub-agent prefix must NOT be accepted for the manager.
assert!(resolve_host_path("manager", "/agents/manager/state/x.md").is_err());
}
#[test]
fn prepare_body_passthrough_when_no_file_path() {
let s = prepare_body("foo", "hello world", None);