Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/openhuman/agent/harness/session/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ fn set_connected_integrations_marks_session_initialized_and_updates_hash() {

agent.set_connected_integrations(vec![
crate::openhuman::agent::context::prompt::ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email".into(),
tools: vec![],
Expand Down Expand Up @@ -306,6 +307,7 @@ fn refresh_delegation_tools_updates_schema_even_when_tool_arc_is_shared() {
let mut agent = build_minimal_agent_with_definition_name(Some("orchestrator"));
agent.set_connected_integrations(vec![
crate::openhuman::agent::context::prompt::ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email".into(),
tools: vec![],
Expand All @@ -326,6 +328,7 @@ fn refresh_delegation_tools_updates_schema_even_when_tool_arc_is_shared() {
let _shared_tools = agent.tools_arc();
agent.set_connected_integrations(vec![
crate::openhuman::agent::context::prompt::ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email".into(),
tools: vec![],
Expand All @@ -335,6 +338,7 @@ fn refresh_delegation_tools_updates_schema_even_when_tool_arc_is_shared() {
non_active_status: None,
},
crate::openhuman::agent::context::prompt::ConnectedIntegration {
result_notes: None,
toolkit: "notion".into(),
description: "Docs".into(),
tools: vec![],
Expand Down Expand Up @@ -370,6 +374,7 @@ fn refresh_delegation_tools_no_duplicate_specs_across_shared_arc_connects() {

let conn =
|slug: &str, desc: &str| crate::openhuman::agent::context::prompt::ConnectedIntegration {
result_notes: None,
toolkit: slug.into(),
description: desc.into(),
tools: vec![],
Expand Down
1 change: 1 addition & 0 deletions src/openhuman/agent/harness/subagent_runner/ops/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,7 @@ async fn run_typed_mode(
}
};
let integration = crate::openhuman::agent::context::prompt::ConnectedIntegration {
result_notes: None,
toolkit: cached_integration.toolkit.clone(),
description: cached_integration.description.clone(),
tools: fresh_actions,
Expand Down
1 change: 1 addition & 0 deletions src/openhuman/agent/orchestration/tools/tools_e2e_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ async fn skill_delegation_tool_runs_integrations_agent_e2e() {
workspace.path(),
provider.clone(),
vec![ConnectedIntegration {
result_notes: None,
toolkit: "gmail".to_string(),
description: "Email access.".to_string(),
tools: Vec::new(),
Expand Down
1 change: 1 addition & 0 deletions src/openhuman/agent/profiles/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ mod tests {
toolkit: &str,
) -> crate::openhuman::agent::prompts::ConnectedIntegration {
crate::openhuman::agent::prompts::ConnectedIntegration {
result_notes: None,
toolkit: toolkit.to_string(),
description: String::new(),
tools: Vec::new(),
Expand Down
11 changes: 11 additions & 0 deletions src/openhuman/agent/prompts/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,17 @@ pub struct ConnectedIntegration {
pub toolkit: String,
/// Human-readable one-line description of what this integration can do.
pub description: String,
/// What this toolkit's actions hand back, and which returned field feeds
/// which follow-up action. `None` for a toolkit we have not established
/// this for — see
/// [`toolkit_result_notes`](crate::openhuman::integrations::composio::providers::toolkit_result_notes).
///
/// Separate from [`Self::description`] because the two answer different
/// questions for different readers. The description is a routing signal and
/// belongs anywhere a service is named, including the delegator's guide;
/// this is only useful to whoever actually calls the actions and reads what
/// comes back, so only that agent renders it.
pub result_notes: Option<String>,
Comment thread
yh928 marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
/// Per-action catalogue (only populated when `connected == true`).
pub tools: Vec<ConnectedIntegrationTool>,
/// Per-action catalogue for actions that the toolkit **does** support but
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ mod tests {

fn integration(toolkit: &str, connected: bool) -> ConnectedIntegration {
ConnectedIntegration {
result_notes: None,
toolkit: toolkit.to_string(),
description: String::new(),
tools: vec![],
Expand Down
63 changes: 63 additions & 0 deletions src/openhuman/agent/registry/agents/integrations_agent/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ fn render_connected_integrations(integrations: &[ConnectedIntegration]) -> Strin
} else {
let _ = writeln!(out, "- **{}** — {}", ci.toolkit, ci.description);
}
// What the actions hand back. This agent is the one that calls them and
// reads the results, so it is the one that needs to know a returned id
// is the handle for the detail it wanted. Nothing else it reads says so:
// the catalogue, the parameter schemas, and the contract the gate
// delivers all describe arguments only. Omitted for a toolkit we have
// not established a result shape for.
if let Some(notes) = ci
.result_notes
.as_deref()
.map(str::trim)
.filter(|n| !n.is_empty())
{
let _ = writeln!(out, " Results: {notes}");
}
}

// Surface pref-gated tools so the agent can honestly say "I have this
Expand Down Expand Up @@ -215,6 +229,7 @@ mod tests {
#[test]
fn build_includes_connected_integrations_in_executor_voice() {
let integrations = vec![ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email access.".into(),
tools: Vec::new(),
Expand Down Expand Up @@ -247,6 +262,7 @@ mod tests {
#[test]
fn build_skips_unconnected_integrations() {
let integrations = vec![ConnectedIntegration {
result_notes: None,
toolkit: "notion".into(),
description: "Pages.".into(),
tools: Vec::new(),
Expand All @@ -258,4 +274,51 @@ mod tests {
let body = build(&ctx_with(&integrations)).unwrap();
assert!(!body.contains("## Connected Integrations"));
}

/// This agent is the one that calls the actions and reads what comes back,
/// so it is the one that has to know a returned id is the handle for the
/// detail it wanted. Everything else it reads describes arguments.
#[test]
fn build_states_what_the_actions_hand_back() {
let integrations = vec![ConnectedIntegration {
toolkit: "gmail".into(),
description: "Email access.".into(),
result_notes: Some(
"Read actions answer with one record per message carrying id and threadId. \
To read one message in full, pass its id to \
GMAIL_FETCH_MESSAGE_BY_MESSAGE_ID."
.into(),
),
tools: Vec::new(),
gated_tools: Vec::new(),
connected: true,
connections: Vec::new(),
non_active_status: None,
}];
let body = build(&ctx_with(&integrations)).unwrap();
assert!(
body.contains("Results: Read actions answer with one record per message"),
"result notes must reach the executor prompt: {body}"
);
assert!(body.contains("GMAIL_FETCH_MESSAGE_BY_MESSAGE_ID"));
}

/// A toolkit whose result shape this repository has not established renders
/// nothing rather than a guess.
#[test]
fn build_omits_result_notes_when_there_are_none() {
let integrations = vec![ConnectedIntegration {
toolkit: "notion".into(),
description: "Pages.".into(),
result_notes: None,
tools: Vec::new(),
gated_tools: Vec::new(),
connected: true,
connections: Vec::new(),
non_active_status: None,
}];
let body = build(&ctx_with(&integrations)).unwrap();
assert!(body.contains("- **notion** — Pages."));
assert!(!body.contains("Results:"), "no notes, no line: {body}");
}
}
7 changes: 7 additions & 0 deletions src/openhuman/agent/registry/agents/orchestrator/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ mod tests {
#[test]
fn build_emits_delegation_guide_with_collapsed_tool() {
let integrations = vec![ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email access.".into(),
tools: Vec::new(),
Expand Down Expand Up @@ -685,6 +686,7 @@ mod tests {
);

let gmail = vec![ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email access.".into(),
tools: Vec::new(),
Expand Down Expand Up @@ -718,6 +720,7 @@ mod tests {
#[test]
fn delegation_guide_uses_compact_collapsed_format() {
let integrations = vec![ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email access.".into(),
tools: Vec::new(),
Expand All @@ -736,6 +739,7 @@ mod tests {

fn gmail_only() -> Vec<ConnectedIntegration> {
vec![ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email access.".into(),
tools: Vec::new(),
Expand Down Expand Up @@ -820,6 +824,7 @@ mod tests {
// focused on what the orchestrator can actually delegate.
let integrations = vec![
ConnectedIntegration {
result_notes: None,
toolkit: "gmail".into(),
description: "Email.".into(),
tools: Vec::new(),
Expand All @@ -829,6 +834,7 @@ mod tests {
non_active_status: None,
},
ConnectedIntegration {
result_notes: None,
toolkit: "linear".into(),
description: "Tracker.".into(),
tools: Vec::new(),
Expand Down Expand Up @@ -876,6 +882,7 @@ mod tests {
#[test]
fn build_omits_guide_when_no_integrations_connected() {
let integrations = vec![ConnectedIntegration {
result_notes: None,
toolkit: "linear".into(),
description: "Tracker.".into(),
tools: Vec::new(),
Expand Down
1 change: 1 addition & 0 deletions src/openhuman/channels/runtime/dispatch/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ mod connected_fallback_tests {

fn integration(toolkit: &str) -> ConnectedIntegration {
ConnectedIntegration {
result_notes: None,
toolkit: toolkit.into(),
description: String::new(),
tools: vec![],
Expand Down
1 change: 1 addition & 0 deletions src/openhuman/flows/tinyflows/caps/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,7 @@ mod tests {
connections: Vec<IntegrationConnection>,
) -> ConnectedIntegration {
ConnectedIntegration {
result_notes: None,
toolkit: toolkit.to_string(),
description: String::new(),
tools: Vec::new(),
Expand Down
4 changes: 4 additions & 0 deletions src/openhuman/integrations/composio/connected_integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,10 @@ async fn fetch_connected_integrations_uncached(
integrations.push(ConnectedIntegration {
toolkit: slug.clone(),
description: resolve_toolkit_description(&catalog_descriptions, slug),
// Unlike the description, this has no catalog counterpart to prefer
// — the Composio catalog publishes what an action takes, never what
// it returns — so the local table is the only source.
result_notes: super::providers::toolkit_result_notes(slug).map(str::to_string),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
tools,
gated_tools,
connected,
Expand Down
1 change: 1 addition & 0 deletions src/openhuman/integrations/composio/ops_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,7 @@ fn seed_cache(key: &str, integrations: Vec<ConnectedIntegration>) {
/// Only `toolkit` + `connected` matter for diff-based invalidation.
fn integration(toolkit: &str, connected: bool) -> ConnectedIntegration {
ConnectedIntegration {
result_notes: None,
toolkit: toolkit.to_string(),
description: String::new(),
tools: Vec::new(),
Expand Down
Loading
Loading