mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix: allow Medulla to message accepted contacts (#4824)
This commit is contained in:
@@ -251,6 +251,17 @@ async fn contact_status(agent_id: &str) -> Result<String, String> {
|
|||||||
Ok(remote_status(&remote).unwrap_or_else(|| "none".to_string()))
|
Ok(remote_status(&remote).unwrap_or_else(|| "none".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the peer is an accepted tiny.place contact.
|
||||||
|
///
|
||||||
|
/// This is intentionally a live relay check rather than a read of the local
|
||||||
|
/// orchestration pairing store. An accepted contact is already a
|
||||||
|
/// user-consented recipient for an outbound message, even when it has not been
|
||||||
|
/// linked as an inbound orchestration session in the current workspace.
|
||||||
|
pub(crate) async fn is_accepted_contact(agent_id: &str) -> Result<bool, String> {
|
||||||
|
let agent_id = normalize_agent_id(agent_id)?;
|
||||||
|
Ok(contact_status(&agent_id).await? == "accepted")
|
||||||
|
}
|
||||||
|
|
||||||
fn remote_status(value: &Value) -> Option<String> {
|
fn remote_status(value: &Value) -> Option<String> {
|
||||||
value
|
value
|
||||||
.get("status")
|
.get("status")
|
||||||
|
|||||||
@@ -92,6 +92,14 @@ fn required_str(args: &Value, field: &str) -> Result<String, ToolResult> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recipient_may_receive_message(
|
||||||
|
locally_linked: bool,
|
||||||
|
has_known_session: bool,
|
||||||
|
accepted_contact: bool,
|
||||||
|
) -> bool {
|
||||||
|
locally_linked || has_known_session || accepted_contact
|
||||||
|
}
|
||||||
|
|
||||||
// ── Reasoning-core session-history read tools (Master chat) ──────────────────
|
// ── Reasoning-core session-history read tools (Master chat) ──────────────────
|
||||||
|
|
||||||
/// Default / cap on how many messages a `orchestration_read_session` call returns.
|
/// Default / cap on how many messages a `orchestration_read_session` call returns.
|
||||||
@@ -473,15 +481,39 @@ impl Tool for SendToAgentTool {
|
|||||||
|
|
||||||
let workspace = self.config.workspace_dir.clone();
|
let workspace = self.config.workspace_dir.clone();
|
||||||
|
|
||||||
// Guardrail: linked peer OR an existing session with them. Never cold-DM
|
// Guardrail: locally linked peer, accepted tiny.place contact, OR an
|
||||||
// an arbitrary new address from this un-gated background origin.
|
// existing session with them. Never cold-DM an arbitrary new address
|
||||||
|
// from this un-gated background origin. The live contact check matters
|
||||||
|
// after login changes the workspace: the relay contact remains accepted
|
||||||
|
// while the workspace-local orchestration pairing/session stores start
|
||||||
|
// empty.
|
||||||
let linked =
|
let linked =
|
||||||
crate::openhuman::agent_orchestration::pairing::linked_agent_ids(&workspace).await;
|
crate::openhuman::agent_orchestration::pairing::linked_agent_ids(&workspace).await;
|
||||||
let known_session = store::with_connection(&workspace, |conn| {
|
let known_session = store::with_connection(&workspace, |conn| {
|
||||||
store::latest_session_for_agent(conn, &recipient)
|
store::latest_session_for_agent(conn, &recipient)
|
||||||
})
|
})
|
||||||
.map_err(|e| anyhow::anyhow!("lookup session: {e}"))?;
|
.map_err(|e| anyhow::anyhow!("lookup session: {e}"))?;
|
||||||
if !linked.contains(&recipient) && known_session.is_none() {
|
let accepted_contact = if linked.contains(&recipient) || known_session.is_some() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
match crate::openhuman::agent_orchestration::pairing::is_accepted_contact(&recipient)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(accepted) => accepted,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!(
|
||||||
|
target: "orchestration",
|
||||||
|
"[orchestration] tool.send_to_agent contact check failed: {e}"
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !recipient_may_receive_message(
|
||||||
|
linked.contains(&recipient),
|
||||||
|
known_session.is_some(),
|
||||||
|
accepted_contact,
|
||||||
|
) {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
target: "orchestration",
|
target: "orchestration",
|
||||||
"[orchestration] tool.send_to_agent refused unlinked recipient",
|
"[orchestration] tool.send_to_agent refused unlinked recipient",
|
||||||
@@ -827,4 +859,12 @@ mod tests {
|
|||||||
assert!(out.is_error);
|
assert!(out.is_error);
|
||||||
assert!(out.text().contains("not a linked"));
|
assert!(out.text().contains("not a linked"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accepted_contact_can_receive_without_workspace_pairing_or_session() {
|
||||||
|
assert!(recipient_may_receive_message(false, false, true));
|
||||||
|
assert!(recipient_may_receive_message(true, false, false));
|
||||||
|
assert!(recipient_may_receive_message(false, true, false));
|
||||||
|
assert!(!recipient_may_receive_message(false, false, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user