From 97eb3e642ec3bfe476685e843a6954863b0d4105 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:09:08 +0530 Subject: [PATCH 01/18] feat(tools): add Twilio integration tools Add Twilio messaging tools to the integrations toolset, wiring them into the ops tool groups so they are exposed alongside the existing integrations. Tests cover the new tool behaviour. Auto-committed-on: macbook Co-authored-by: Medulla --- .../openhuman-core/src/integrations/tools.rs | 2 - .../src/integrations/tools/twilio.rs | 154 ------------------ .../src/integrations/tools/twilio_tests.rs | 57 ------- crates/openhuman-core/src/tools/ops.rs | 8 - .../src/tools/ops_tool_groups.rs | 1 - 5 files changed, 222 deletions(-) delete mode 100644 crates/openhuman-core/src/integrations/tools/twilio.rs delete mode 100644 crates/openhuman-core/src/integrations/tools/twilio_tests.rs diff --git a/crates/openhuman-core/src/integrations/tools.rs b/crates/openhuman-core/src/integrations/tools.rs index 6c108f8d2d9..4cb3d10d6ae 100644 --- a/crates/openhuman-core/src/integrations/tools.rs +++ b/crates/openhuman-core/src/integrations/tools.rs @@ -2,11 +2,9 @@ use super::IntegrationClient; mod google_places; mod stock_prices; -mod twilio; pub use google_places::{GooglePlacesDetailsTool, GooglePlacesSearchTool}; pub use stock_prices::{ StockCommodityTool, StockCryptoSeriesTool, StockExchangeRateTool, StockOptionsTool, StockQuoteTool, }; -pub use twilio::TwilioCallTool; diff --git a/crates/openhuman-core/src/integrations/tools/twilio.rs b/crates/openhuman-core/src/integrations/tools/twilio.rs deleted file mode 100644 index 25b131720df..00000000000 --- a/crates/openhuman-core/src/integrations/tools/twilio.rs +++ /dev/null @@ -1,154 +0,0 @@ -//! Twilio phone-call integration tool. -//! -//! **Scope**: CLI/RPC only — phone calls require explicit user action. -//! -//! **Endpoint**: `POST /agent-integrations/twilio/call` -//! -//! **Pricing** (fetched from backend): -//! - Outbound calls: ~$0.03/min -//! - Inbound calls: ~$0.017/min -//! -//! The backend handles Twilio API credentials, billing, and rate limiting. - -use super::IntegrationClient; -use async_trait::async_trait; -use serde::Deserialize; -use serde_json::json; -use std::sync::Arc; -use tinytools::{PermissionLevel, Tool, ToolResult, ToolScope}; - -/// Makes outbound phone calls via the backend Twilio integration. -pub struct TwilioCallTool { - client: Arc, -} - -#[derive(Debug, Deserialize)] -struct TwilioCallResponse { - #[serde(rename = "callSid")] - call_sid: String, - status: String, - #[serde(rename = "costUsd", default)] - cost_usd: f64, -} - -impl TwilioCallTool { - pub fn new(client: Arc) -> Self { - Self { client } - } -} - -#[async_trait] -impl Tool for TwilioCallTool { - fn name(&self) -> &str { - "twilio_call" - } - - fn description(&self) -> &str { - "Make an outbound phone call via Twilio. Requires at least one of: \ - a text message to speak, raw TwiML markup, or a TwiML URL. \ - The call is placed immediately and the result includes the call SID \ - and initial status. Cost is billed per minute by the backend." - } - - fn parameters_schema(&self) -> serde_json::Value { - json!({ - "type": "object", - "properties": { - "to": { - "type": "string", - "description": "Phone number to call in E.164 format (e.g. +14155551234)" - }, - "message": { - "type": "string", - "description": "Plain text message to speak on the call (uses TTS)" - }, - "twiml": { - "type": "string", - "description": "Raw TwiML XML to control call flow (advanced)" - }, - "url": { - "type": "string", - "description": "URL that returns TwiML for call flow (advanced)" - } - }, - "required": ["to"] - }) - } - - fn permission_level(&self) -> PermissionLevel { - PermissionLevel::Execute - } - - fn scope(&self) -> ToolScope { - ToolScope::CliRpcOnly - } - - async fn execute(&self, args: serde_json::Value) -> anyhow::Result { - let to = args - .get("to") - .and_then(|v| v.as_str()) - .ok_or_else(|| anyhow::anyhow!("Missing required parameter: to"))?; - - if to.trim().is_empty() { - return Ok(ToolResult::error("Phone number 'to' cannot be empty")); - } - - let message = args.get("message").and_then(|v| v.as_str()); - let twiml = args.get("twiml").and_then(|v| v.as_str()); - let url = args.get("url").and_then(|v| v.as_str()); - - if message.is_none() && twiml.is_none() && url.is_none() { - return Ok(ToolResult::error( - "At least one of 'message', 'twiml', or 'url' must be provided", - )); - } - - let mut body = json!({ "to": to }); - if let Some(m) = message { - body["message"] = json!(m); - } - if let Some(t) = twiml { - body["twiml"] = json!(t); - } - if let Some(u) = url { - body["url"] = json!(u); - } - - let redacted = if to.len() > 4 { - format!( - "{}***{}", - &to[..to.char_indices().nth(2).map_or(2, |(i, _)| i)], - &to[to - .char_indices() - .rev() - .nth(3) - .map_or(to.len().saturating_sub(4), |(i, _)| i)..] - ) - } else { - "****".to_string() - }; - tracing::info!("[twilio_call] calling {}", redacted); - - match self - .client - .post::("/agent-integrations/twilio/call", &body) - .await - { - Ok(resp) => { - let output = format!( - "Call placed successfully.\n\ - Call SID: {}\n\ - Status: {}\n\ - Cost: ${:.4}", - resp.call_sid, resp.status, resp.cost_usd - ); - Ok(ToolResult::success(output)) - } - Err(e) => Ok(ToolResult::error(format!("Twilio call failed: {e}"))), - } - } -} - -#[cfg(test)] -#[path = "twilio_tests.rs"] -mod tests; diff --git a/crates/openhuman-core/src/integrations/tools/twilio_tests.rs b/crates/openhuman-core/src/integrations/tools/twilio_tests.rs deleted file mode 100644 index 9cc21f29e0e..00000000000 --- a/crates/openhuman-core/src/integrations/tools/twilio_tests.rs +++ /dev/null @@ -1,57 +0,0 @@ -use super::*; - -#[test] -fn tool_metadata() { - let client = Arc::new(IntegrationClient::new("http://test".into(), "tok".into())); - let tool = TwilioCallTool::new(client); - assert_eq!(tool.name(), "twilio_call"); - assert_eq!(tool.permission_level(), PermissionLevel::Execute); - assert_eq!(tool.scope(), ToolScope::CliRpcOnly); - assert!(tool.description().contains("phone call")); -} - -#[test] -fn schema_has_required_to() { - let client = Arc::new(IntegrationClient::new("http://test".into(), "tok".into())); - let tool = TwilioCallTool::new(client); - let schema = tool.parameters_schema(); - assert_eq!(schema["type"], "object"); - assert!(schema["properties"]["to"].is_object()); - let required = schema["required"].as_array().unwrap(); - assert!(required.iter().any(|v| v == "to")); -} - -#[tokio::test] -async fn execute_rejects_missing_to() { - let client = Arc::new(IntegrationClient::new("http://test".into(), "tok".into())); - let tool = TwilioCallTool::new(client); - let result = tool.execute(json!({})).await; - assert!(result.is_err()); -} - -#[tokio::test] -async fn execute_rejects_empty_to() { - let client = Arc::new(IntegrationClient::new("http://test".into(), "tok".into())); - let tool = TwilioCallTool::new(client); - let result = tool.execute(json!({"to": ""})).await.unwrap(); - assert!(result.is_error); - assert!(result.output().contains("empty")); -} - -#[tokio::test] -async fn execute_rejects_no_content() { - let client = Arc::new(IntegrationClient::new("http://test".into(), "tok".into())); - let tool = TwilioCallTool::new(client); - let result = tool.execute(json!({"to": "+14155551234"})).await.unwrap(); - assert!(result.is_error); - assert!(result.output().contains("message")); -} - -#[test] -fn twilio_response_deserializes() { - let json = r#"{"callSid":"CA123","status":"queued","costUsd":0.03}"#; - let resp: TwilioCallResponse = serde_json::from_str(json).unwrap(); - assert_eq!(resp.call_sid, "CA123"); - assert_eq!(resp.status, "queued"); - assert!((resp.cost_usd - 0.03).abs() < f64::EPSILON); -} diff --git a/crates/openhuman-core/src/tools/ops.rs b/crates/openhuman-core/src/tools/ops.rs index 51c8ab8a7b1..f4a6151ef04 100644 --- a/crates/openhuman-core/src/tools/ops.rs +++ b/crates/openhuman-core/src/tools/ops.rs @@ -882,14 +882,6 @@ pub fn all_tools_with_runtime( } else { tracing::debug!("[integrations] stock_prices disabled — skipping"); } - if root_config.integrations.twilio.is_active() { - tools.push(Box::new(crate::tools::TwilioCallTool::new(Arc::clone( - &client, - )))); - tracing::debug!("[integrations] registered twilio tools"); - } else { - tracing::debug!("[integrations] twilio disabled — skipping"); - } } else { tracing::debug!( "[integrations] build_client returned None — integration tools not registered" diff --git a/crates/openhuman-core/src/tools/ops_tool_groups.rs b/crates/openhuman-core/src/tools/ops_tool_groups.rs index bf00e0809e6..1cb97379a7c 100644 --- a/crates/openhuman-core/src/tools/ops_tool_groups.rs +++ b/crates/openhuman-core/src/tools/ops_tool_groups.rs @@ -195,7 +195,6 @@ pub(crate) fn tool_group(name: &str) -> crate::core::all::DomainGroup { || name.starts_with("stock_") || name.starts_with("storage_") || name.starts_with("task_source_") - || name == "twilio_call" // Hosting: `hosting_` is a domain-exclusive prefix, so a NEW hosting // tool auto-gates rather than falling through to Platform and staying // callable under a custom DomainSet. `hosting_launch_site` uploads a From 5105d651009f25f6d312620c015f2e8255f59fbf Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:09:24 +0530 Subject: [PATCH 02/18] feat(config): add integrations tool config schema Add a schema for the integrations tool so its settings can be validated and loaded through the standard config pipeline. Auto-committed-on: macbook Co-authored-by: Medulla --- .../src/config/schema/tools/integrations.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/openhuman-core/src/config/schema/tools/integrations.rs b/crates/openhuman-core/src/config/schema/tools/integrations.rs index 21a3238bd99..6c0ef5869fb 100644 --- a/crates/openhuman-core/src/config/schema/tools/integrations.rs +++ b/crates/openhuman-core/src/config/schema/tools/integrations.rs @@ -294,18 +294,17 @@ impl Default for IntegrationToggle { /// Composio in particular is unconditionally enabled and has no toggle: /// as long as the user is signed in, composio tools are available. /// -/// The per-tool `apify`, `twilio`, `google_places`, `parallel`, and `tinyfish` +/// The per-tool `google_places`, `parallel`, `tinyfish`, and `stock_prices` /// flags below are preserved because those integrations incur per-call /// costs that the user may legitimately want to turn off; composio /// costs are metered server-side, so there is no client-side toggle /// for it. +/// +/// Unknown keys are ignored on load, so configs written by older builds that +/// still carry the retired `twilio` / `apify` toggles keep deserializing. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)] #[serde(default)] pub struct IntegrationsConfig { - /// Twilio phone-call integration. - #[serde(default)] - pub twilio: IntegrationToggle, - /// Google Places location search integration. #[serde(default)] pub google_places: IntegrationToggle, From 21f9640ffdb69dec6998266d55325a7616a77208 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:09:32 +0530 Subject: [PATCH 03/18] chore(tools): add integration toggle tests Adds test coverage for the integration toggle configuration schema. Auto-committed-on: macbook Co-authored-by: Medulla --- ...grations_integration_toggle_tests_tests.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/crates/openhuman-core/src/config/schema/tools/integrations_integration_toggle_tests_tests.rs b/crates/openhuman-core/src/config/schema/tools/integrations_integration_toggle_tests_tests.rs index 0afd8f5bf2a..4437eefedfe 100644 --- a/crates/openhuman-core/src/config/schema/tools/integrations_integration_toggle_tests_tests.rs +++ b/crates/openhuman-core/src/config/schema/tools/integrations_integration_toggle_tests_tests.rs @@ -53,3 +53,22 @@ fn default_is_managed_and_active() { assert!(toggle.api_key.is_none()); assert!(toggle.is_active()); } + +#[test] +fn integrations_config_ignores_retired_twilio_and_apify_keys() { + let raw = r#" + [twilio] + enabled = true + mode = "managed" + + [apify] + enabled = false + + [google_places] + enabled = false + "#; + let cfg: IntegrationsConfig = + toml::from_str(raw).expect("old config with retired keys must still load"); + assert!(!cfg.google_places.enabled); + assert!(cfg.parallel.enabled); +} From 251f5cfb6cad22a9320377612595f90162034ff7 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:09:46 +0530 Subject: [PATCH 04/18] chore: files changed crates/openhuman-core/src/tools/ops_tests_domain_family_tests.rs Auto-committed-on: macbook Co-authored-by: Medulla --- .../src/tools/ops_tests_domain_family_tests.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/crates/openhuman-core/src/tools/ops_tests_domain_family_tests.rs b/crates/openhuman-core/src/tools/ops_tests_domain_family_tests.rs index c50582583c9..4d1959fac48 100644 --- a/crates/openhuman-core/src/tools/ops_tests_domain_family_tests.rs +++ b/crates/openhuman-core/src/tools/ops_tests_domain_family_tests.rs @@ -2,7 +2,7 @@ use super::*; use tinytools_std::filesystem::{ApplyPatchTool, CsvExportTool}; #[tokio::test] -async fn all_tools_executes_stock_and_twilio_family_against_fake_backend() { +async fn all_tools_executes_stock_family_against_fake_backend() { let backend = integration_test_support::spawn_fake_integration_backend().await; let tmp = TempDir::new().unwrap(); let cfg = integration_test_config(&tmp, &backend.base_url); @@ -57,16 +57,6 @@ async fn all_tools_executes_stock_and_twilio_family_against_fake_backend() { assert!(commodity.output().contains("WTI (weekly)")); assert!(commodity.output().contains("2026-05-16 80.1000")); - let twilio = find_tool(&tools, "twilio_call") - .execute(serde_json::json!({ - "to": "+14155551234", - "message": "Hello from tests" - })) - .await - .expect("twilio_call execute"); - assert!(twilio.output().contains("Call SID: CA1234")); - assert!(twilio.output().contains("Status: queued")); - let requests = backend.requests(); let paths: Vec<&str> = requests.iter().map(|req| req.path.as_str()).collect(); assert_eq!( @@ -77,11 +67,9 @@ async fn all_tools_executes_stock_and_twilio_family_against_fake_backend() { "/agent-integrations/financial-apis/options", "/agent-integrations/financial-apis/crypto-series", "/agent-integrations/financial-apis/commodity", - "/agent-integrations/twilio/call", ] ); assert_eq!(requests[2].body["requireGreeks"], serde_json::json!(true)); - assert_eq!(requests[5].body["to"], serde_json::json!("+14155551234")); } /// Every acting tool gates on `can_act()` and returns its own read-only refusal From 9a497a7f1205c12fb80a644607c03999845a314a Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:09:56 +0530 Subject: [PATCH 05/18] test(tools): split ops tests into execution and serde modules Move the execution and serde test cases out of ops_tests.rs into a dedicated ops_tests_execution_and_serde_tests.rs module so the remaining ops tests stay focused and easier to navigate. Auto-committed-on: macbook Co-authored-by: Medulla --- crates/openhuman-core/src/tools/ops_tests.rs | 1 - .../src/tools/ops_tests_execution_and_serde_tests.rs | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/openhuman-core/src/tools/ops_tests.rs b/crates/openhuman-core/src/tools/ops_tests.rs index 416ba2c0142..75a4e9acb9c 100644 --- a/crates/openhuman-core/src/tools/ops_tests.rs +++ b/crates/openhuman-core/src/tools/ops_tests.rs @@ -56,7 +56,6 @@ fn integration_test_config(tmp: &TempDir, backend_url: &str) -> Config { cfg.integrations.parallel.enabled = true; cfg.integrations.tinyfish.enabled = true; cfg.integrations.stock_prices.enabled = true; - cfg.integrations.twilio.enabled = true; cfg } diff --git a/crates/openhuman-core/src/tools/ops_tests_execution_and_serde_tests.rs b/crates/openhuman-core/src/tools/ops_tests_execution_and_serde_tests.rs index df120e81d73..e5e6d0c2294 100644 --- a/crates/openhuman-core/src/tools/ops_tests_execution_and_serde_tests.rs +++ b/crates/openhuman-core/src/tools/ops_tests_execution_and_serde_tests.rs @@ -231,7 +231,6 @@ fn all_tools_registers_integration_families_when_enabled_and_signed_in() { cfg.integrations.parallel.enabled = true; cfg.integrations.tinyfish.enabled = true; cfg.integrations.stock_prices.enabled = true; - cfg.integrations.twilio.enabled = true; cfg.composio.enabled = true; store_test_session_token(&cfg); @@ -257,7 +256,6 @@ fn all_tools_registers_integration_families_when_enabled_and_signed_in() { "stock_options", "stock_crypto_series", "stock_commodity", - "twilio_call", "composio_list_toolkits", "composio_list_connections", "composio_authorize", @@ -265,6 +263,12 @@ fn all_tools_registers_integration_families_when_enabled_and_signed_in() { "composio_execute", ], ); + // The backend retired its Twilio route; the agent must not be offered a + // tool that can only fail. + assert!( + !names.iter().any(|n| n == "twilio_call"), + "twilio_call must no longer be registered" + ); } #[test] From 2aa5f081a2352d2314be600fbe45dd9c2e08b364 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:10:06 +0530 Subject: [PATCH 06/18] chore(integrations): add test support backend Add a test support backend for the integrations layer so tests can exercise integration flows without depending on real external services. Auto-committed-on: macbook Co-authored-by: Medulla --- .../src/integrations/test_support_backend.rs | 127 ------------------ 1 file changed, 127 deletions(-) diff --git a/crates/openhuman-core/src/integrations/test_support_backend.rs b/crates/openhuman-core/src/integrations/test_support_backend.rs index 3ee3d623f70..237100556f8 100644 --- a/crates/openhuman-core/src/integrations/test_support_backend.rs +++ b/crates/openhuman-core/src/integrations/test_support_backend.rs @@ -4,108 +4,6 @@ pub async fn spawn_fake_integration_backend() -> FakeIntegrationBackend { }; let app = Router::new() - .route( - "/agent-integrations/apify/run", - post({ - let state = state.clone(); - move |Json(body): Json| async move { - record( - &state, - "POST", - "/agent-integrations/apify/run".to_string(), - body.clone(), - ); - let actor_id = body - .get("actorId") - .and_then(Value::as_str) - .unwrap_or("unknown-actor"); - let run_id = format!("run-{}", actor_id.replace('/', "-")); - let input = body.get("input").cloned().unwrap_or(Value::Null); - Json(json!({ - "success": true, - "data": { - "runId": run_id, - "actorId": actor_id, - "status": if body.get("sync").and_then(Value::as_bool).unwrap_or(true) { "SUCCEEDED" } else { "RUNNING" }, - "datasetId": format!("dataset-{}", actor_id.replace('/', "-")), - "items": [ - { - "actorId": actor_id, - "inputEcho": input - } - ], - "costUsd": 0.31 - } - })) - } - }), - ) - .route( - "/agent-integrations/apify/runs/{run_id}", - get({ - let state = state.clone(); - move |Path(run_id): Path| async move { - record( - &state, - "GET", - format!("/agent-integrations/apify/runs/{run_id}"), - Value::Null, - ); - Json(json!({ - "success": true, - "data": { - "runId": run_id, - "actorId": "apify/linkedin-profile-scraper", - "status": "SUCCEEDED", - "datasetId": format!("dataset-{run_id}"), - "costUsd": 0.02 - } - })) - } - }), - ) - .route( - "/agent-integrations/apify/runs/{run_id}/results", - get({ - let state = state.clone(); - move |Path(run_id): Path, - Query(params): Query>| async move { - let limit = params - .get("limit") - .and_then(|v| v.parse::().ok()) - .unwrap_or(3); - let offset = params - .get("offset") - .and_then(|v| v.parse::().ok()) - .unwrap_or(0); - record( - &state, - "GET", - format!( - "/agent-integrations/apify/runs/{run_id}/results?limit={limit}&offset={offset}" - ), - Value::Null, - ); - let items: Vec = (0..limit) - .map(|idx| { - let n = offset + idx; - json!({ - "runId": run_id, - "index": n, - "url": format!("https://example.com/{run_id}/{n}") - }) - }) - .collect(); - Json(json!({ - "success": true, - "data": { - "items": items, - "total": offset + limit + 5 - } - })) - } - }), - ) .route( "/agent-integrations/google-places/search", post({ @@ -582,31 +480,6 @@ pub async fn spawn_fake_integration_backend() -> FakeIntegrationBackend { } }), ) - .route( - "/agent-integrations/twilio/call", - post({ - let state = state.clone(); - move |Json(body): Json| async move { - record( - &state, - "POST", - "/agent-integrations/twilio/call".to_string(), - body.clone(), - ); - let to = body.get("to").and_then(Value::as_str).unwrap_or("+10000000000"); - let suffix: String = to.chars().filter(|c| c.is_ascii_digit()).rev().take(4).collect(); - let suffix: String = suffix.chars().rev().collect(); - Json(json!({ - "success": true, - "data": { - "callSid": format!("CA{suffix}"), - "status": "queued", - "costUsd": 0.03 - } - })) - } - }), - ) .route( "/agent-integrations/tinyfish/search", post({ From 81870bdb2548c3d867d74ece832a2ac4fa5a2f42 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:10:29 +0530 Subject: [PATCH 07/18] feat(tools): add apify and linkedin tool schemas Add schema definitions for the Apify and LinkedIn tools, including a dedicated LinkedIn test module, and register them in the schema registry so the new tools are exposed alongside the existing ones. Auto-committed-on: macbook Co-authored-by: Medulla --- crates/openhuman-core/src/tools/schemas.rs | 2 - .../openhuman-core/src/tools/schemas/apify.rs | 37 ---- .../src/tools/schemas/linkedin.rs | 182 ------------------ .../src/tools/schemas/linkedin_tests.rs | 36 ---- .../src/tools/schemas/registry.rs | 33 ---- 5 files changed, 290 deletions(-) delete mode 100644 crates/openhuman-core/src/tools/schemas/apify.rs delete mode 100644 crates/openhuman-core/src/tools/schemas/linkedin.rs delete mode 100644 crates/openhuman-core/src/tools/schemas/linkedin_tests.rs diff --git a/crates/openhuman-core/src/tools/schemas.rs b/crates/openhuman-core/src/tools/schemas.rs index 15ae5c9c9ed..7679a3d890a 100644 --- a/crates/openhuman-core/src/tools/schemas.rs +++ b/crates/openhuman-core/src/tools/schemas.rs @@ -10,9 +10,7 @@ #[path = "schemas_tests.rs"] mod tests; -mod apify; mod composio; -mod linkedin; mod registry; mod web_search; diff --git a/crates/openhuman-core/src/tools/schemas/apify.rs b/crates/openhuman-core/src/tools/schemas/apify.rs deleted file mode 100644 index a8d487517a0..00000000000 --- a/crates/openhuman-core/src/tools/schemas/apify.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! Handler for the `tools_apify_linkedin_scrape` controller schema. - -use serde_json::{json, Map, Value}; - -use crate::config::rpc as config_rpc; -use crate::core::all::ControllerFuture; -use crate::core::Outcome; - -pub(super) fn handle_apify_linkedin_scrape(params: Map) -> ControllerFuture { - Box::pin(async move { - let profile_url = params - .get("profile_url") - .and_then(Value::as_str) - .map(str::trim) - .filter(|s| !s.is_empty()) - .map(str::to_string) - .ok_or_else(|| "missing or empty `profile_url`".to_string())?; - - let config = config_rpc::load_config_with_timeout().await?; - let client = crate::integrations::build_client(&config).ok_or_else(|| { - "Apify scrape unavailable — no backend session token. Sign in first.".to_string() - })?; - - let data = super::linkedin::scrape_linkedin_profile(&client, &profile_url) - .await - .map_err(|e| format!("Apify LinkedIn scrape failed: {e:#}"))?; - - let markdown = super::linkedin::render_profile_markdown(&profile_url, &data); - - let payload = json!({ "data": data, "markdown": markdown }); - let log = vec![format!( - "tools.apify_linkedin_scrape: url={profile_url} markdown_chars={}", - markdown.chars().count() - )]; - Outcome::new(payload, log).into_cli_compatible_json() - }) -} diff --git a/crates/openhuman-core/src/tools/schemas/linkedin.rs b/crates/openhuman-core/src/tools/schemas/linkedin.rs deleted file mode 100644 index 0d9c2b5d033..00000000000 --- a/crates/openhuman-core/src/tools/schemas/linkedin.rs +++ /dev/null @@ -1,182 +0,0 @@ -//! The Apify LinkedIn profile scrape behind `tools_apify_linkedin_scrape`: -//! run the `dev_fusion/linkedin-profile-scraper` actor through the backend's -//! Apify proxy and render the first profile item as Markdown. - -use std::sync::Arc; - -use serde_json::json; - -use crate::integrations::IntegrationClient; - -/// Apify actor slug for the LinkedIn profile scraper. -const LINKEDIN_SCRAPER_ACTOR: &str = "dev_fusion/linkedin-profile-scraper"; - -/// Call the Apify LinkedIn profile scraper synchronously and return the -/// first profile item from the dataset. -pub async fn scrape_linkedin_profile( - client: &Arc, - profile_url: &str, -) -> anyhow::Result { - let body = json!({ - "actorId": LINKEDIN_SCRAPER_ACTOR, - "input": { - "profileUrls": [profile_url], - }, - "sync": true, - "timeoutSecs": 120, - }); - - tracing::debug!( - actor = LINKEDIN_SCRAPER_ACTOR, - url_len = profile_url.len(), - "[apify:linkedin] invoking Apify actor" - ); - - // The backend wraps the Apify response in its standard envelope. - // `IntegrationClient::post` already unwraps `{ success, data }`. - let resp: serde_json::Value = client - .post("/agent-integrations/apify/run", &body) - .await - .map_err(|e| anyhow::anyhow!("Apify run failed: {e:#}"))?; - - let status = resp - .get("status") - .and_then(|v| v.as_str()) - .unwrap_or("UNKNOWN"); - - if status != "SUCCEEDED" { - anyhow::bail!("Apify run finished with status: {status}"); - } - - // Extract the first item from the inline results array. - let items = resp - .get("items") - .and_then(|v| v.as_array()) - .ok_or_else(|| anyhow::anyhow!("Apify run returned no items array"))?; - - items - .first() - .cloned() - .ok_or_else(|| anyhow::anyhow!("Apify run returned an empty items array")) -} - -/// Turn the Apify scrape JSON into clean Markdown. -pub fn render_profile_markdown(url: &str, data: &serde_json::Value) -> String { - let s = |key: &str| { - data.get(key) - .and_then(|v| v.as_str()) - .unwrap_or("") - .to_string() - }; - - let full_name = s("fullName"); - let headline = s("headline"); - let location = s("addressWithCountry"); - let about = s("about"); - let connections = data.get("connections").and_then(|v| v.as_u64()); - let followers = data.get("followers").and_then(|v| v.as_u64()); - - let mut md = format!("# User Profile — {full_name}\n\n"); - - if !headline.is_empty() { - md.push_str(&format!("**{headline}**\n\n")); - } - if !location.is_empty() { - md.push_str(&format!("Location: {location}\n\n")); - } - md.push_str(&format!("LinkedIn: {url}\n\n")); - if let (Some(c), Some(f)) = (connections, followers) { - md.push_str(&format!("Connections: {c} | Followers: {f}\n\n")); - } - - if !about.is_empty() { - md.push_str("## About\n\n"); - md.push_str(&about); - md.push_str("\n\n"); - } - - // Experience - if let Some(exps) = data.get("experiences").and_then(|v| v.as_array()) { - if !exps.is_empty() { - md.push_str("## Experience\n\n"); - for exp in exps { - let title = exp.get("title").and_then(|v| v.as_str()).unwrap_or(""); - let company = exp.get("subtitle").and_then(|v| v.as_str()).unwrap_or(""); - let duration = exp.get("duration").and_then(|v| v.as_str()).unwrap_or(""); - let caption = exp.get("caption").and_then(|v| v.as_str()).unwrap_or(""); - let desc = exp - .get("description") - .and_then(|v| v.as_str()) - .unwrap_or(""); - md.push_str(&format!("- **{title}**")); - if !company.is_empty() { - md.push_str(&format!(" at {company}")); - } - if !duration.is_empty() { - md.push_str(&format!(" ({duration})")); - } - if !caption.is_empty() { - md.push_str(&format!(" — {caption}")); - } - md.push('\n'); - if !desc.is_empty() { - md.push_str(&format!(" {desc}\n")); - } - } - md.push('\n'); - } - } - - // Education - if let Some(edus) = data.get("educations").and_then(|v| v.as_array()) { - if !edus.is_empty() { - md.push_str("## Education\n\n"); - for edu in edus { - let school = edu.get("title").and_then(|v| v.as_str()).unwrap_or(""); - let degree = edu.get("subtitle").and_then(|v| v.as_str()).unwrap_or(""); - md.push_str(&format!("- **{school}**")); - if !degree.is_empty() { - md.push_str(&format!(" — {degree}")); - } - md.push('\n'); - } - md.push('\n'); - } - } - - // Languages - if let Some(langs) = data.get("languages").and_then(|v| v.as_array()) { - if !langs.is_empty() { - let names: Vec<&str> = langs - .iter() - .filter_map(|l| l.get("name").and_then(|v| v.as_str())) - .collect(); - if !names.is_empty() { - md.push_str(&format!("Languages: {}\n\n", names.join(", "))); - } - } - } - - // Volunteering - if let Some(vols) = data.get("volunteering").and_then(|v| v.as_array()) { - if !vols.is_empty() { - md.push_str("## Volunteering\n\n"); - for vol in vols { - let title = vol.get("title").and_then(|v| v.as_str()).unwrap_or(""); - let org = vol.get("subtitle").and_then(|v| v.as_str()).unwrap_or(""); - md.push_str(&format!("- {title}")); - if !org.is_empty() { - md.push_str(&format!(" at {org}")); - } - md.push('\n'); - } - md.push('\n'); - } - } - - md -} - -#[cfg(test)] -#[path = "linkedin_tests.rs"] -mod tests; diff --git a/crates/openhuman-core/src/tools/schemas/linkedin_tests.rs b/crates/openhuman-core/src/tools/schemas/linkedin_tests.rs deleted file mode 100644 index 37bcd1caf57..00000000000 --- a/crates/openhuman-core/src/tools/schemas/linkedin_tests.rs +++ /dev/null @@ -1,36 +0,0 @@ -use super::*; - -#[test] -fn renders_headline_location_experience_and_education() { - let data = json!({ - "fullName": "Ada Lovelace", - "headline": "Analyst", - "addressWithCountry": "London, UK", - "about": "First programmer.", - "connections": 10, - "followers": 20, - "experiences": [{"title": "Engineer", "subtitle": "Analytical Engine Co", "duration": "2y", "caption": "remote", "description": "Wrote notes"}], - "educations": [{"title": "Home", "subtitle": "Mathematics"}], - "languages": [{"name": "English"}, {"name": "French"}], - "volunteering": [{"title": "Tutor", "subtitle": "Society"}] - }); - let md = render_profile_markdown("https://www.linkedin.com/in/ada", &data); - assert!(md.starts_with("# User Profile — Ada Lovelace")); - assert!(md.contains("**Analyst**")); - assert!(md.contains("Location: London, UK")); - assert!(md.contains("Connections: 10 | Followers: 20")); - assert!(md.contains("## About\n\nFirst programmer.")); - assert!(md.contains("- **Engineer** at Analytical Engine Co (2y) — remote")); - assert!(md.contains(" Wrote notes")); - assert!(md.contains("- **Home** — Mathematics")); - assert!(md.contains("Languages: English, French")); - assert!(md.contains("- Tutor at Society")); -} - -#[test] -fn renders_only_the_url_for_an_empty_profile() { - let md = render_profile_markdown("https://www.linkedin.com/in/x", &json!({})); - assert!(md.contains("LinkedIn: https://www.linkedin.com/in/x")); - assert!(!md.contains("## Experience")); - assert!(!md.contains("## About")); -} diff --git a/crates/openhuman-core/src/tools/schemas/registry.rs b/crates/openhuman-core/src/tools/schemas/registry.rs index 467b34c53b2..15a06e0de1e 100644 --- a/crates/openhuman-core/src/tools/schemas/registry.rs +++ b/crates/openhuman-core/src/tools/schemas/registry.rs @@ -7,7 +7,6 @@ use crate::core::all::RegisteredController; use crate::core::{ControllerSchema, FieldSchema, TypeSchema}; -use super::apify::handle_apify_linkedin_scrape; use super::composio::handle_composio_execute; use super::web_search::{ handle_searxng_search, handle_web_answer, handle_web_contents, handle_web_search, @@ -20,7 +19,6 @@ pub fn all_controller_schemas() -> Vec { tools_schemas("tools_web_answer"), tools_schemas("tools_web_contents"), tools_schemas("tools_searxng_search"), - tools_schemas("tools_apify_linkedin_scrape"), ] } @@ -46,10 +44,6 @@ pub fn all_registered_controllers() -> Vec { schema: tools_schemas("tools_searxng_search"), handler: handle_searxng_search, }, - RegisteredController { - schema: tools_schemas("tools_apify_linkedin_scrape"), - handler: handle_apify_linkedin_scrape, - }, ] } @@ -203,33 +197,6 @@ pub fn tools_schemas(function: &str) -> ControllerSchema { ], outputs: search_outputs("Each item: {title, url, snippet?, published?}."), }, - "tools_apify_linkedin_scrape" => ControllerSchema { - namespace: "tools", - function: "apify_linkedin_scrape", - description: "Run the Apify LinkedIn profile scraper actor on a single profile \ - URL and return both the raw scraped item and a pre-rendered \ - markdown view of it (same layout as the legacy enrichment pipeline).", - inputs: vec![FieldSchema { - name: "profile_url", - ty: TypeSchema::String, - comment: "Canonical LinkedIn profile URL (`https://www.linkedin.com/in/`).", - required: true, - }], - outputs: vec![ - FieldSchema { - name: "data", - ty: TypeSchema::Json, - comment: "Raw scraped profile JSON from Apify.", - required: true, - }, - FieldSchema { - name: "markdown", - ty: TypeSchema::String, - comment: "Markdown rendering of the scraped profile (full, pre-summary).", - required: true, - }, - ], - }, _ => ControllerSchema { namespace: "tools", function: "unknown", From 6e9eaef49bf1acfae5a27a5596e9fff153022dfb Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:10:42 +0530 Subject: [PATCH 08/18] test(tools): add schema validation tests Add tests covering the tool schema definitions to verify that serialization and validation behave as expected. Auto-committed-on: macbook Co-authored-by: Medulla --- .../openhuman-core/src/tools/schemas_tests.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/openhuman-core/src/tools/schemas_tests.rs b/crates/openhuman-core/src/tools/schemas_tests.rs index 5663db08e39..ba2dc37cc5d 100644 --- a/crates/openhuman-core/src/tools/schemas_tests.rs +++ b/crates/openhuman-core/src/tools/schemas_tests.rs @@ -2,7 +2,7 @@ use super::*; use crate::tools::schemas::registry::tools_schemas; use serde_json::{json, Map}; -const BASE_CONTROLLERS: usize = 6; +const BASE_CONTROLLERS: usize = 5; #[test] fn all_schemas_covers_the_base_surface() { @@ -15,14 +15,16 @@ fn all_controllers_covers_the_base_surface() { } #[test] -fn apify_linkedin_scrape_schema_shape() { - let s = tools_schemas("tools_apify_linkedin_scrape"); - assert_eq!(s.namespace, "tools"); - assert_eq!(s.function, "apify_linkedin_scrape"); - assert!(s - .inputs +fn apify_linkedin_scrape_controller_is_retired() { + // The backend retired its Apify routes; the controller must not be + // registered or resolvable any more. + assert!(!all_controller_schemas() .iter() - .any(|f| f.name == "profile_url" && f.required)); + .any(|s| s.function == "apify_linkedin_scrape")); + assert_eq!( + tools_schemas("tools_apify_linkedin_scrape").function, + "unknown" + ); } #[test] From 85c5f7b2d3b101f57832135fb463f513bdbe4dfd Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:10:58 +0530 Subject: [PATCH 09/18] test(integrations): add coverage for composio client and integration types Auto-committed-on: macbook Co-authored-by: Medulla --- .../src/integrations/composio/client_tests.rs | 2 -- .../openhuman-core/src/integrations/mod_tests.rs | 15 +++++++++++++-- crates/openhuman-core/src/integrations/types.rs | 4 ---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/openhuman-core/src/integrations/composio/client_tests.rs b/crates/openhuman-core/src/integrations/composio/client_tests.rs index fef4bbcf2eb..01150780465 100644 --- a/crates/openhuman-core/src/integrations/composio/client_tests.rs +++ b/crates/openhuman-core/src/integrations/composio/client_tests.rs @@ -451,8 +451,6 @@ async fn pricing_for_config_short_circuits_in_direct_mode() { let pricing = crate::integrations::pricing_for_config(&client, &config).await; // The default struct has every per-integration entry as `None`. - assert!(pricing.integrations.apify.is_none()); - assert!(pricing.integrations.twilio.is_none()); assert!(pricing.integrations.google_places.is_none()); assert!(pricing.integrations.parallel.is_none()); assert!(pricing.integrations.tinyfish.is_none()); diff --git a/crates/openhuman-core/src/integrations/mod_tests.rs b/crates/openhuman-core/src/integrations/mod_tests.rs index 155406cd1f3..61c1cba73c9 100644 --- a/crates/openhuman-core/src/integrations/mod_tests.rs +++ b/crates/openhuman-core/src/integrations/mod_tests.rs @@ -33,13 +33,24 @@ fn backend_response_without_data() { fn integration_pricing_defaults_on_missing_fields() { let json = r#"{"integrations": {}}"#; let pricing: IntegrationPricing = serde_json::from_str(json).unwrap(); - assert!(pricing.integrations.apify.is_none()); - assert!(pricing.integrations.twilio.is_none()); assert!(pricing.integrations.google_places.is_none()); assert!(pricing.integrations.parallel.is_none()); assert!(pricing.integrations.tinyfish.is_none()); } +#[test] +fn integration_pricing_ignores_retired_apify_and_twilio_entries() { + // An older backend still reporting the retired integrations must not + // break pricing deserialization. + let json = r#"{"integrations": { + "apify": {"available": true, "pricing": {}}, + "twilio": {"available": true, "pricing": {}}, + "parallel": {"available": true, "pricing": {}} + }}"#; + let pricing: IntegrationPricing = serde_json::from_str(json).unwrap(); + assert!(pricing.integrations.parallel.unwrap().available); +} + #[test] fn build_client_returns_none_when_no_auth_token() { let tmp = tempfile::tempdir().expect("tempdir"); diff --git a/crates/openhuman-core/src/integrations/types.rs b/crates/openhuman-core/src/integrations/types.rs index a4972bf6612..ff6e8b4957d 100644 --- a/crates/openhuman-core/src/integrations/types.rs +++ b/crates/openhuman-core/src/integrations/types.rs @@ -13,10 +13,6 @@ pub struct IntegrationPricing { #[derive(Debug, Clone, Default, Deserialize)] pub struct PricingIntegrations { - #[serde(default)] - pub apify: Option, - #[serde(default)] - pub twilio: Option, #[serde(default)] pub google_places: Option, #[serde(default)] From bced5e8e722e26dcb3e78d481a5b511330718104 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:11:10 +0530 Subject: [PATCH 10/18] fix(security): enforce egress policy on channel startup and pricing calls Egress checks were being skipped for channel startup turns and pricing lookups, allowing requests to bypass the configured policy. The enforcement path now covers both call sites so outbound traffic is consistently validated. Auto-committed-on: macbook Co-authored-by: Medulla --- .../openhuman-core/src/channels/runtime/startup/turn_parts.rs | 2 +- crates/openhuman-core/src/integrations/client/pricing.rs | 4 ++-- crates/openhuman-core/src/security/egress/enforce.rs | 4 ++-- crates/openhuman-core/src/security/egress/enforce_tests.rs | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/openhuman-core/src/channels/runtime/startup/turn_parts.rs b/crates/openhuman-core/src/channels/runtime/startup/turn_parts.rs index a5f0d4fc21c..3e04b5207c2 100644 --- a/crates/openhuman-core/src/channels/runtime/startup/turn_parts.rs +++ b/crates/openhuman-core/src/channels/runtime/startup/turn_parts.rs @@ -171,7 +171,7 @@ pub(crate) fn build_channel_turn_parts( } else { None }; - // Filter out Workflow-category tools (e.g. Composio, Apify) from the + // Filter out Workflow-category tools (e.g. Composio) from the // main agent prompt — integration actions are reached through tool search. let non_skill_specs: Vec = tools_registry .iter() diff --git a/crates/openhuman-core/src/integrations/client/pricing.rs b/crates/openhuman-core/src/integrations/client/pricing.rs index fcca147461c..d538a080b46 100644 --- a/crates/openhuman-core/src/integrations/client/pricing.rs +++ b/crates/openhuman-core/src/integrations/client/pricing.rs @@ -86,8 +86,8 @@ pub async fn pricing_for_config( /// both resolve to an error, so no client is built for them. /// /// There are no per-feature toggles for the shared client itself — -/// callers that need a kill switch (e.g. twilio, google_places, -/// parallel) gate tool registration at their own level. +/// callers that need a kill switch (e.g. google_places, parallel, +/// stock_prices) gate tool registration at their own level. /// /// [`resolve_backend_credential`]: crate::security::credentials::session_support::resolve_backend_credential pub fn build_client(config: &crate::config::Config) -> Option> { diff --git a/crates/openhuman-core/src/security/egress/enforce.rs b/crates/openhuman-core/src/security/egress/enforce.rs index 39c0960f09a..accd424fc42 100644 --- a/crates/openhuman-core/src/security/egress/enforce.rs +++ b/crates/openhuman-core/src/security/egress/enforce.rs @@ -72,8 +72,8 @@ pub fn local_only_blocks(mode: PrivacyMode, desc: &EgressDescriptor) -> bool { /// /// 1. **Any path outside the user-data tool namespace** — i.e. it is *not* under /// `/agent-integrations/`. Today every user-data integration call (composio -/// execute, parallel / tinyfish / financial-apis / google-places / twilio / -/// apify research + actions, file-storage uploads) routes through +/// execute, parallel / tinyfish / financial-apis / google-places research + +/// actions, file-storage uploads) routes through /// `IntegrationClient` under `/agent-integrations/…`, while session / team / /// billing / auth round-trips (`/teams/me/usage`, `/payments/…`, `/auth/…`) /// go through `backend::client` and never build an egress descriptor at all. Any diff --git a/crates/openhuman-core/src/security/egress/enforce_tests.rs b/crates/openhuman-core/src/security/egress/enforce_tests.rs index 106288478c4..5d4a39d52f3 100644 --- a/crates/openhuman-core/src/security/egress/enforce_tests.rs +++ b/crates/openhuman-core/src/security/egress/enforce_tests.rs @@ -143,7 +143,6 @@ fn control_plane_does_not_exempt_user_data_paths() { "/agent-integrations/composio/some-future-write", "/agent-integrations/parallel/research", "/agent-integrations/tinyfish/fetch", - "/agent-integrations/twilio/call", "/agent-integrations/file-storage/files", "/agent-integrations/google-places/search", ] { From 65f5a3ae49256fc5414af6f946b8dd5785d684c2 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:11:28 +0530 Subject: [PATCH 11/18] docs(integrations): drop twilio and apify from domain docs Remove the Twilio call tool and the Apify LinkedIn scrape tool from the integrations and tools READMEs, along with the corresponding schema catalog entry in the worker B end-to-end test. These tools no longer exist, so the documentation and test expectations were stale. Auto-committed-on: macbook Co-authored-by: Medulla --- crates/openhuman-core/src/integrations/README.md | 11 +++++------ crates/openhuman-core/src/tools/README.md | 3 +-- tests/in_process/worker_b_domain_e2e.rs | 4 +--- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/openhuman-core/src/integrations/README.md b/crates/openhuman-core/src/integrations/README.md index 5578288e6cb..b2a54b32d4b 100644 --- a/crates/openhuman-core/src/integrations/README.md +++ b/crates/openhuman-core/src/integrations/README.md @@ -3,11 +3,11 @@ This domain is how the core reaches third-party services through the OpenHuman backend. It owns `IntegrationClient`, the one HTTP client every backend-proxied integration uses for `/agent-integrations/*`, a small family -of managed agent tools built on it (Google Places, stock and market data, -Twilio calls), and three child sub-domains: Composio connectors, task sources, +of managed agent tools built on it (Google Places, stock and market data), +and three child sub-domains: Composio connectors, task sources, and managed file storage. Callers are the tool registry in [`tools/ops.rs`](../tools/ops.rs), the controller registry in [`core/all.rs`](../core/all.rs), and a few other domains (`web3`, -[`modules/connectors.rs`](../modules/connectors.rs), [`tools/schemas/apify.rs`](../tools/schemas/apify.rs)) that borrow the client. +[`modules/connectors.rs`](../modules/connectors.rs)) that borrow the client. Most integrations never see a provider API key. The backend holds the keys, bills each call, applies rate limits and markup, and returns a @@ -168,7 +168,6 @@ in BYO mode): | --- | --- | --- | | `google_places` | `google_places_search`, `google_places_details` | `/agent-integrations/google-places/{search,details}` | | `stock_prices` | `stock_quote`, `stock_exchange_rate`, `stock_options`, `stock_crypto_series`, `stock_commodity` | `/agent-integrations/financial-apis/*` | -| `twilio` | `twilio_call` | `/agent-integrations/twilio/call` | File storage tools come from `file_storage::build_file_storage_tools`, called separately from `tools/ops.rs`, because they need `action_dir` and a @@ -183,7 +182,7 @@ pub use crate::integrations::tools::*; ``` `tools/ops.rs` also maps the `google_places_`, `stock_`, `storage_` and -`task_source_` prefixes and `twilio_call` to `DomainGroup::Integrations`, so +`task_source_` prefixes to `DomainGroup::Integrations`, so a `DomainSet` without integrations hides them. ## Layout @@ -199,7 +198,7 @@ a `DomainSet` without integrations hides them. | `client/pricing.rs` | Pricing cache, `pricing_for_config`, `build_client`. | | `client/budget_gate.rs` | Credits-exhausted pre-check and the shared `/teams/me/usage` failure backoff. | | [`types.rs`](./types.rs) | `BackendResponse` envelope and the pricing types. | -| [`tools.rs`](./tools.rs), `tools/` | The managed tools: `google_places.rs`, `stock_prices.rs`, `twilio.rs`. `tools.rs` only declares and re-exports. | +| [`tools.rs`](./tools.rs), `tools/` | The managed tools: `google_places.rs`, `stock_prices.rs`. `tools.rs` only declares and re-exports. | | [`composio/`](composio/README.md) | Composio connector sub-domain: toolkit catalogs, connections, triggers, direct-auth mode, the `tinyconnectors` module bridge, `composio_*` agent tools and RPC. | | [`task_sources/`](task_sources/README.md) | Pulls work items from GitHub, Notion, Linear and ClickUp through the Composio providers, dedups and enriches them, and drops cards on the `task-sources` thread board. | | [`file_storage/`](file_storage/README.md) | `storage_*` tools over the backend's S3-backed file storage (upload, download, list, link, visibility, delete). | diff --git a/crates/openhuman-core/src/tools/README.md b/crates/openhuman-core/src/tools/README.md index 1a79af45a65..6614bddd939 100644 --- a/crates/openhuman-core/src/tools/README.md +++ b/crates/openhuman-core/src/tools/README.md @@ -126,7 +126,7 @@ step. | [`orchestrator_tools.rs`](./orchestrator_tools.rs) | Per-subagent `delegate_*` tools and deferred Composio action expansion. | | [`user_filter.rs`](./user_filter.rs) | `filter_tools_by_user_preference` and the UI toggle to tool-name map. | | [`host_extensions.rs`](./host_extensions.rs) | Readers over `tinytools`' erased host-extension slots: `pack_registry_handle`, `delegation_target`, `tool_call_id`. | -| [`schemas.rs`](./schemas.rs), [`schemas/`](./schemas/) | The `tools.*` controllers: `registry.rs` (schemas and dispatch), `composio.rs`, `web_search.rs`, `apify.rs`, `linkedin.rs`. | +| [`schemas.rs`](./schemas.rs), [`schemas/`](./schemas/) | The `tools.*` controllers: `registry.rs` (schemas and dispatch), `composio.rs`, `web_search.rs`. | | [`impl/`](impl/README.md) | Built-in tool families: `filesystem/` (the `FsGate` adapter only; the tools are `tinytools_std::filesystem`), `browser/`, `system/`, `network/`, `meta/`, and the `documents`-gated `document/` and `presentation/`. | | [`toolpacks/`](toolpacks/README.md) | On-demand tool disclosure (`use_skill`, pack catalog, guides) and `ToolGroups` / `GroupMode`. | | [`agent_policy/`](agent_policy/README.md) | Per-session tool boundary against a channel's permission ceiling. | @@ -181,7 +181,6 @@ else is agent-only. | `openhuman.tools_web_answer` | Grounded answer with citations through the `answer` role. | | `openhuman.tools_web_contents` | Page contents for given URLs through the `contents` role. | | `openhuman.tools_searxng_search` | The `search` role pinned to a self-hosted SearXNG (requires SearXNG enabled). | -| `openhuman.tools_apify_linkedin_scrape` | Apify LinkedIn profile scrape, returning raw JSON and rendered markdown. | [`registry/`](./registry/) registers a second namespace, `tool_registry` (`list`, `get`, `diagnostics`). Dotted ids such as `tools.web_search` in that registry are diff --git a/tests/in_process/worker_b_domain_e2e.rs b/tests/in_process/worker_b_domain_e2e.rs index 8a3cf480394..19729bf664b 100644 --- a/tests/in_process/worker_b_domain_e2e.rs +++ b/tests/in_process/worker_b_domain_e2e.rs @@ -103,7 +103,6 @@ async fn worker_b_schema_catalog_exposes_all_controller_methods() { "openhuman.tools_web_answer", "openhuman.tools_web_contents", "openhuman.tools_searxng_search", - "openhuman.tools_apify_linkedin_scrape", "openhuman.tool_registry_list", "openhuman.tool_registry_get", "openhuman.tool_registry_diagnostics", @@ -441,8 +440,7 @@ async fn tools_and_tool_registry_paths_are_reachable_without_live_services() { "No web search provider is available", ), ( - "openhuman.tools_apify_linkedin_scrape", - json!({ "profile_url": "https://www.linkedin.com/in/example" }), + json!({ "profile_url": "https://www.linkedin.com/in/example" }), "Sign in first", ), ] From 296a8fbeff8e65b3ac2fa620da0e99178150e72e Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:11:56 +0530 Subject: [PATCH 12/18] test: drop apify linkedin scrape tool from e2e expectations The apify LinkedIn scrape tool is no longer exposed, so the in-process end-to-end tests that asserted its presence have been updated to match the current tool set. Auto-committed-on: macbook Co-authored-by: Medulla --- tests/in_process/config_auth_app_state_connectivity_e2e.rs | 1 - tests/in_process/domain_modules_e2e.rs | 1 - tests/in_process/worker_b_domain_e2e.rs | 4 ---- 3 files changed, 6 deletions(-) diff --git a/tests/in_process/config_auth_app_state_connectivity_e2e.rs b/tests/in_process/config_auth_app_state_connectivity_e2e.rs index c3e28ce0d31..15bd018d03c 100644 --- a/tests/in_process/config_auth_app_state_connectivity_e2e.rs +++ b/tests/in_process/config_auth_app_state_connectivity_e2e.rs @@ -1952,7 +1952,6 @@ async fn worker_a_controller_schemas_are_fully_exposed() { "openhuman.tools_web_answer", "openhuman.tools_web_contents", "openhuman.tools_searxng_search", - "openhuman.tools_apify_linkedin_scrape", "openhuman.tool_registry_list", "openhuman.tool_registry_get", "openhuman.tool_registry_diagnostics", diff --git a/tests/in_process/domain_modules_e2e.rs b/tests/in_process/domain_modules_e2e.rs index 04cb83fce87..18eed460d99 100644 --- a/tests/in_process/domain_modules_e2e.rs +++ b/tests/in_process/domain_modules_e2e.rs @@ -347,7 +347,6 @@ async fn config_agent_tools_and_threads_mutation_paths_round_trip() { ("openhuman.tools_web_answer", json!({})), ("openhuman.tools_web_contents", json!({})), ("openhuman.tools_searxng_search", json!({})), - ("openhuman.tools_apify_linkedin_scrape", json!({})), ] .into_iter() .enumerate() diff --git a/tests/in_process/worker_b_domain_e2e.rs b/tests/in_process/worker_b_domain_e2e.rs index 19729bf664b..e12a22f610f 100644 --- a/tests/in_process/worker_b_domain_e2e.rs +++ b/tests/in_process/worker_b_domain_e2e.rs @@ -439,10 +439,6 @@ async fn tools_and_tool_registry_paths_are_reachable_without_live_services() { json!({ "query": "worker b", "max_results": 1 }), "No web search provider is available", ), - ( - json!({ "profile_url": "https://www.linkedin.com/in/example" }), - "Sign in first", - ), ] .into_iter() .enumerate() From d7ca748262c2e696e3074907c1094ddd2ed2c665 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:12:38 +0530 Subject: [PATCH 13/18] feat(conversations): add localized phrases for tool call status Tool call status messages are now generated from per-locale phrase tables instead of hardcoded English strings, so every supported language can describe running, completed, and failed tool calls in its own words. Auto-committed-on: macbook Co-authored-by: Medulla --- app/src/features/conversations/tools/toolPhrases.ts | 1 - app/src/features/conversations/tools/toolSpecs.ts | 2 -- app/src/lib/i18n/ar.ts | 5 ----- app/src/lib/i18n/bn.ts | 5 ----- app/src/lib/i18n/de.ts | 5 ----- app/src/lib/i18n/en.ts | 5 ----- app/src/lib/i18n/es.ts | 5 ----- app/src/lib/i18n/fr.ts | 5 ----- app/src/lib/i18n/hi.ts | 5 ----- app/src/lib/i18n/id.ts | 5 ----- app/src/lib/i18n/it.ts | 5 ----- app/src/lib/i18n/ja.ts | 5 ----- app/src/lib/i18n/ko.ts | 5 ----- app/src/lib/i18n/pl.ts | 5 ----- app/src/lib/i18n/pt.ts | 5 ----- app/src/lib/i18n/ru.ts | 5 ----- app/src/lib/i18n/tr.ts | 5 ----- app/src/lib/i18n/zh-CN.ts | 5 ----- 18 files changed, 83 deletions(-) diff --git a/app/src/features/conversations/tools/toolPhrases.ts b/app/src/features/conversations/tools/toolPhrases.ts index 845c2f2947b..ab9163bd586 100644 --- a/app/src/features/conversations/tools/toolPhrases.ts +++ b/app/src/features/conversations/tools/toolPhrases.ts @@ -157,7 +157,6 @@ export const TOOL_PHRASES = { searchPlaces: { active: 'Searching places', done: 'Searched places' }, lookUpPlace: { active: 'Looking up place', done: 'Looked up place' }, checkMarkets: { active: 'Checking markets', done: 'Checked markets' }, - placeCall: { active: 'Placing call', done: 'Placed call' }, checkTaskSources: { active: 'Checking task sources', done: 'Checked task sources' }, updateTaskSources: { active: 'Updating task sources', done: 'Updated task sources' }, fetchTasks: { active: 'Fetching tasks', done: 'Fetched tasks' }, diff --git a/app/src/features/conversations/tools/toolSpecs.ts b/app/src/features/conversations/tools/toolSpecs.ts index 17dec18b4ed..c74f4cb587a 100644 --- a/app/src/features/conversations/tools/toolSpecs.ts +++ b/app/src/features/conversations/tools/toolSpecs.ts @@ -70,7 +70,6 @@ import { PackageIcon, PackagePlusIcon, PackageSearchIcon, - PhoneIcon, PlugIcon, PodcastIcon, PowerIcon, @@ -376,7 +375,6 @@ export const EXACT_TOOL_SPECS: Record = { gmail_unsubscribe: spec('unsubscribe', MailXIcon, 'app', { chip: chip.text('sender', 'email') }), google_places_search: spec('searchPlaces', MapPinIcon, 'app', { chip: chip.query() }), google_places_details: spec('lookUpPlace', MapPinIcon, 'app', { chip: chip.text('place_id') }), - twilio_call: spec('placeCall', PhoneIcon, 'app', { chip: chip.text('to') }), // ── MCP ───────────────────────────────────────────────────────────────── mcp_list_servers: spec('checkMcpServers', ServerIcon, 'mcp'), diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts index d448a5db0c5..9688421d728 100644 --- a/app/src/lib/i18n/ar.ts +++ b/app/src/lib/i18n/ar.ts @@ -2422,8 +2422,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'تم الاستعلام عن المكان', 'conversations.tools.checkMarkets.active': 'جارٍ متابعة الأسواق', 'conversations.tools.checkMarkets.done': 'تمت متابعة الأسواق', - 'conversations.tools.placeCall.active': 'جارٍ إجراء مكالمة', - 'conversations.tools.placeCall.done': 'تم إجراء المكالمة', 'conversations.tools.checkTaskSources.active': 'جارٍ فحص مصادر المهام', 'conversations.tools.checkTaskSources.done': 'تم فحص مصادر المهام', 'conversations.tools.updateTaskSources.active': 'جارٍ تحديث مصادر المهام', @@ -4058,9 +4056,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'جيد التنسيق.', 'skills.install.urlLabel': 'رابط المهارة', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google لقاء', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'مرحباً OpenHuman', 'skills.search.placeholder': 'بحث', 'skills.setup.voice.activation': 'التفعيل', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts index 3493d198845..50325398acb 100644 --- a/app/src/lib/i18n/bn.ts +++ b/app/src/lib/i18n/bn.ts @@ -2465,8 +2465,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'স্থানের তথ্য দেখা হয়েছে', 'conversations.tools.checkMarkets.active': 'বাজার দেখা হচ্ছে', 'conversations.tools.checkMarkets.done': 'বাজার দেখা হয়েছে', - 'conversations.tools.placeCall.active': 'কল করা হচ্ছে', - 'conversations.tools.placeCall.done': 'কল করা হয়েছে', 'conversations.tools.checkTaskSources.active': 'কাজের উৎস যাচাই করা হচ্ছে', 'conversations.tools.checkTaskSources.done': 'কাজের উৎস যাচাই করা হয়েছে', 'conversations.tools.updateTaskSources.active': 'কাজের উৎস হালনাগাদ করা হচ্ছে', @@ -4132,9 +4130,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'লিঙ্ক হতে হবে।', 'skills.install.urlLabel': 'স্কিল URL', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Meet', - 'skills.meetingBots.platforms.teams': 'মাইক্রোসফট টিম', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'প্লেসহোল্ডার', 'skills.setup.voice.activation': 'অ্যাক্টিভেশন', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts index 92cff497db3..0f044044c88 100644 --- a/app/src/lib/i18n/de.ts +++ b/app/src/lib/i18n/de.ts @@ -2534,8 +2534,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Ort nachgeschlagen', 'conversations.tools.checkMarkets.active': 'Märkte werden geprüft', 'conversations.tools.checkMarkets.done': 'Märkte geprüft', - 'conversations.tools.placeCall.active': 'Anruf wird getätigt', - 'conversations.tools.placeCall.done': 'Anruf getätigt', 'conversations.tools.checkTaskSources.active': 'Aufgabenquellen werden geprüft', 'conversations.tools.checkTaskSources.done': 'Aufgabenquellen geprüft', 'conversations.tools.updateTaskSources.active': 'Aufgabenquellen werden aktualisiert', @@ -4243,9 +4241,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'Link.', 'skills.install.urlLabel': 'Fähigkeit URL', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Treffen Sie', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Platzhalter', 'skills.setup.voice.activation': 'Aktivierung', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts index 000cfb54a21..77f5d8fd26a 100644 --- a/app/src/lib/i18n/en.ts +++ b/app/src/lib/i18n/en.ts @@ -2774,8 +2774,6 @@ const en: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Looked up place', 'conversations.tools.checkMarkets.active': 'Checking markets', 'conversations.tools.checkMarkets.done': 'Checked markets', - 'conversations.tools.placeCall.active': 'Placing call', - 'conversations.tools.placeCall.done': 'Placed call', 'conversations.tools.checkTaskSources.active': 'Checking task sources', 'conversations.tools.checkTaskSources.done': 'Checked task sources', 'conversations.tools.updateTaskSources.active': 'Updating task sources', @@ -4797,9 +4795,6 @@ const en: TranslationMap = { 'skills.install.urlInvalidSuffix': 'link.', 'skills.install.urlLabel': 'Skill URL', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Meet', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Search skills…', 'skills.setup.voice.activation': 'Activation', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts index ea27aabe648..a04457006a7 100644 --- a/app/src/lib/i18n/es.ts +++ b/app/src/lib/i18n/es.ts @@ -2505,8 +2505,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Lugar consultado', 'conversations.tools.checkMarkets.active': 'Consultando mercados', 'conversations.tools.checkMarkets.done': 'Mercados consultados', - 'conversations.tools.placeCall.active': 'Realizando llamada', - 'conversations.tools.placeCall.done': 'Llamada realizada', 'conversations.tools.checkTaskSources.active': 'Comprobando fuentes de tareas', 'conversations.tools.checkTaskSources.done': 'Fuentes de tareas comprobadas', 'conversations.tools.updateTaskSources.active': 'Actualizando fuentes de tareas', @@ -4205,9 +4203,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'enlace.', 'skills.install.urlLabel': 'URL de la habilidad', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Conocer', - 'skills.meetingBots.platforms.teams': 'Equipos de Microsoft', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Marcador de posición', 'skills.setup.voice.activation': 'Activación', 'skills.setup.voice.activeDescPrefix': 'fn', diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts index 799c6996a17..039219234ba 100644 --- a/app/src/lib/i18n/fr.ts +++ b/app/src/lib/i18n/fr.ts @@ -2518,8 +2518,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Lieu trouvé', 'conversations.tools.checkMarkets.active': 'Consultation des marchés', 'conversations.tools.checkMarkets.done': 'Marchés consultés', - 'conversations.tools.placeCall.active': 'Appel en cours', - 'conversations.tools.placeCall.done': 'Appel passé', 'conversations.tools.checkTaskSources.active': 'Vérification des sources de tâches', 'conversations.tools.checkTaskSources.done': 'Sources de tâches vérifiées', 'conversations.tools.updateTaskSources.active': 'Mise à jour des sources de tâches', @@ -4222,9 +4220,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'bien formé. Le support de', 'skills.install.urlLabel': 'URL de la compétence', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Meet', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Espace réservé', 'skills.setup.voice.activation': 'Activation', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts index cdc68b7dac9..bb59f67a012 100644 --- a/app/src/lib/i18n/hi.ts +++ b/app/src/lib/i18n/hi.ts @@ -2468,8 +2468,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'स्थान की जानकारी ली', 'conversations.tools.checkMarkets.active': 'बाज़ार देख रहा है', 'conversations.tools.checkMarkets.done': 'बाज़ार देखे', - 'conversations.tools.placeCall.active': 'कॉल कर रहा है', - 'conversations.tools.placeCall.done': 'कॉल किया', 'conversations.tools.checkTaskSources.active': 'कार्य स्रोत जाँच रहा है', 'conversations.tools.checkTaskSources.done': 'कार्य स्रोत जाँचे', 'conversations.tools.updateTaskSources.active': 'कार्य स्रोत अपडेट कर रहा है', @@ -4136,9 +4134,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'लिंक.', 'skills.install.urlLabel': 'स्किल URL', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google मिलें', - 'skills.meetingBots.platforms.teams': 'माइक्रोसॉफ्ट टीमें', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'प्लेसहोल्डर', 'skills.setup.voice.activation': 'एक्टिवेशन', 'skills.setup.voice.activeDescPrefix': 'एफ.एन', diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts index 05b86c28fd8..f321be376a3 100644 --- a/app/src/lib/i18n/id.ts +++ b/app/src/lib/i18n/id.ts @@ -2484,8 +2484,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Info tempat ditemukan', 'conversations.tools.checkMarkets.active': 'Memeriksa pasar', 'conversations.tools.checkMarkets.done': 'Pasar diperiksa', - 'conversations.tools.placeCall.active': 'Melakukan panggilan', - 'conversations.tools.placeCall.done': 'Panggilan dilakukan', 'conversations.tools.checkTaskSources.active': 'Memeriksa sumber tugas', 'conversations.tools.checkTaskSources.done': 'Sumber tugas diperiksa', 'conversations.tools.updateTaskSources.active': 'Memperbarui sumber tugas', @@ -4162,9 +4160,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'yang dibuat dengan baik.', 'skills.install.urlLabel': 'URL skill', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Temui', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Teks placeholder', 'skills.setup.voice.activation': 'Aktivasi', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts index 4035a645a9b..65d21792315 100644 --- a/app/src/lib/i18n/it.ts +++ b/app/src/lib/i18n/it.ts @@ -2505,8 +2505,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Luogo trovato', 'conversations.tools.checkMarkets.active': 'Controllo dei mercati', 'conversations.tools.checkMarkets.done': 'Mercati controllati', - 'conversations.tools.placeCall.active': 'Chiamata in corso', - 'conversations.tools.placeCall.done': 'Chiamata effettuata', 'conversations.tools.checkTaskSources.active': 'Verifica delle fonti delle attività', 'conversations.tools.checkTaskSources.done': 'Fonti delle attività verificate', 'conversations.tools.updateTaskSources.active': 'Aggiornamento delle fonti delle attività', @@ -4199,9 +4197,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'ben formato.', 'skills.install.urlLabel': 'URL skill', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Incontra', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Segnaposto', 'skills.setup.voice.activation': 'Attivazione', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/ja.ts b/app/src/lib/i18n/ja.ts index df97e122c0e..6eaeecc36df 100644 --- a/app/src/lib/i18n/ja.ts +++ b/app/src/lib/i18n/ja.ts @@ -2777,8 +2777,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': '場所を検索しました', 'conversations.tools.checkMarkets.active': '市場を確認中', 'conversations.tools.checkMarkets.done': '市場を確認しました', - 'conversations.tools.placeCall.active': '通話を発信中', - 'conversations.tools.placeCall.done': '通話を発信しました', 'conversations.tools.checkTaskSources.active': 'タスクソースを確認中', 'conversations.tools.checkTaskSources.done': 'タスクソースを確認しました', 'conversations.tools.updateTaskSources.active': 'タスクソースを更新中', @@ -4836,9 +4834,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'リンクである必要があります。', 'skills.install.urlLabel': 'スキルURL', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Meet', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'スキルを検索…', 'skills.setup.voice.activation': 'アクティベーション', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts index e7ed98493dc..ddc88d0bd4e 100644 --- a/app/src/lib/i18n/ko.ts +++ b/app/src/lib/i18n/ko.ts @@ -2453,8 +2453,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': '장소 조회함', 'conversations.tools.checkMarkets.active': '시장 확인 중', 'conversations.tools.checkMarkets.done': '시장 확인함', - 'conversations.tools.placeCall.active': '전화 거는 중', - 'conversations.tools.placeCall.done': '전화 걸었음', 'conversations.tools.checkTaskSources.active': '작업 소스 확인 중', 'conversations.tools.checkTaskSources.done': '작업 소스 확인함', 'conversations.tools.updateTaskSources.active': '작업 소스 업데이트 중', @@ -4100,9 +4098,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': '링크여야 합니다.', 'skills.install.urlLabel': '스킬 URL', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google 모임', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': '플레이스홀더', 'skills.setup.voice.activation': '활성화', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts index 14ae27229f4..ac771fe7da9 100644 --- a/app/src/lib/i18n/pl.ts +++ b/app/src/lib/i18n/pl.ts @@ -2493,8 +2493,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Sprawdzono miejsce', 'conversations.tools.checkMarkets.active': 'Sprawdzanie rynków', 'conversations.tools.checkMarkets.done': 'Sprawdzono rynki', - 'conversations.tools.placeCall.active': 'Wykonywanie połączenia', - 'conversations.tools.placeCall.done': 'Wykonano połączenie', 'conversations.tools.checkTaskSources.active': 'Sprawdzanie źródeł zadań', 'conversations.tools.checkTaskSources.done': 'Sprawdzono źródła zadań', 'conversations.tools.updateTaskSources.active': 'Aktualizowanie źródeł zadań', @@ -4186,9 +4184,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'linkiem.', 'skills.install.urlLabel': 'URL umiejętności', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Meet', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Szukaj umiejętności...', 'skills.setup.voice.activation': 'Aktywacja', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts index 82ef8af623b..5918ef4a153 100644 --- a/app/src/lib/i18n/pt.ts +++ b/app/src/lib/i18n/pt.ts @@ -2502,8 +2502,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Local consultado', 'conversations.tools.checkMarkets.active': 'Consultando mercados', 'conversations.tools.checkMarkets.done': 'Mercados consultados', - 'conversations.tools.placeCall.active': 'Fazendo ligação', - 'conversations.tools.placeCall.done': 'Ligação feita', 'conversations.tools.checkTaskSources.active': 'Verificando fontes de tarefas', 'conversations.tools.checkTaskSources.done': 'Fontes de tarefas verificadas', 'conversations.tools.updateTaskSources.active': 'Atualizando fontes de tarefas', @@ -4190,9 +4188,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'bem formado.', 'skills.install.urlLabel': 'URL da skill', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Conheça', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Espaço reservado', 'skills.setup.voice.activation': 'Ativação', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts index e8a83a5cb48..f7acf841878 100644 --- a/app/src/lib/i18n/ru.ts +++ b/app/src/lib/i18n/ru.ts @@ -2483,8 +2483,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Сведения о месте найдены', 'conversations.tools.checkMarkets.active': 'Проверка рынков', 'conversations.tools.checkMarkets.done': 'Рынки проверены', - 'conversations.tools.placeCall.active': 'Выполнение звонка', - 'conversations.tools.placeCall.done': 'Звонок выполнен', 'conversations.tools.checkTaskSources.active': 'Проверка источников задач', 'conversations.tools.checkTaskSources.done': 'Источники задач проверены', 'conversations.tools.updateTaskSources.active': 'Обновление источников задач', @@ -4174,9 +4172,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'связь.', 'skills.install.urlLabel': 'URL навыка', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Встречайте', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Поиск', 'skills.setup.voice.activation': 'Активация', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/tr.ts b/app/src/lib/i18n/tr.ts index 002ac115733..2f2c8c697a3 100644 --- a/app/src/lib/i18n/tr.ts +++ b/app/src/lib/i18n/tr.ts @@ -2535,8 +2535,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': 'Yer bilgisi alındı', 'conversations.tools.checkMarkets.active': 'Piyasalar kontrol ediliyor', 'conversations.tools.checkMarkets.done': 'Piyasalar kontrol edildi', - 'conversations.tools.placeCall.active': 'Arama yapılıyor', - 'conversations.tools.placeCall.done': 'Arama yapıldı', 'conversations.tools.checkTaskSources.active': 'Görev kaynakları kontrol ediliyor', 'conversations.tools.checkTaskSources.done': 'Görev kaynakları kontrol edildi', 'conversations.tools.updateTaskSources.active': 'Görev kaynakları güncelleniyor', @@ -4512,9 +4510,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': 'bağlantısı olmalıdır.', 'skills.install.urlLabel': "Yetenek URL'si", 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google Meet', - 'skills.meetingBots.platforms.teams': 'Microsoft Teams', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': 'Yeteneklerde ara…', 'skills.setup.voice.activation': 'Etkinleştirme', 'skills.setup.voice.activeDescPrefix': 'Fn', diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts index 01afd101c8c..f6a1458a514 100644 --- a/app/src/lib/i18n/zh-CN.ts +++ b/app/src/lib/i18n/zh-CN.ts @@ -2320,8 +2320,6 @@ const messages: TranslationMap = { 'conversations.tools.lookUpPlace.done': '已查询地点', 'conversations.tools.checkMarkets.active': '正在查看市场行情', 'conversations.tools.checkMarkets.done': '已查看市场行情', - 'conversations.tools.placeCall.active': '正在拨打电话', - 'conversations.tools.placeCall.done': '已拨打电话', 'conversations.tools.checkTaskSources.active': '正在检查任务来源', 'conversations.tools.checkTaskSources.done': '已检查任务来源', 'conversations.tools.updateTaskSources.active': '正在更新任务来源', @@ -3905,9 +3903,6 @@ const messages: TranslationMap = { 'skills.install.urlInvalidSuffix': '链接。', 'skills.install.urlLabel': '技能 URL', 'skills.install.urlPlaceholder': 'https://raw.githubusercontent.com/owner/repo/main/SKILL.md', - 'skills.meetingBots.platforms.gmeet': 'Google 见面', - 'skills.meetingBots.platforms.teams': '微软团队', - 'skills.meetingBots.wakePhraseHint': 'Hey OpenHuman', 'skills.search.placeholder': '搜索技能...', 'skills.setup.voice.activation': '激活方式', 'skills.setup.voice.activeDescPrefix': 'Fn', From 38e22b01176f9831fb5bbe13bdf5b8ba7e98dce6 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:12:51 +0530 Subject: [PATCH 14/18] chore(scripts): drop stale allowlist entries and mock stubs Remove the recall_calendar domain from the test inventory allowlist and the meetingBots i18n keys from the intentional-English list, since both are now covered. Also delete the unused Apify run-polling stubs from the mock API. Auto-committed-on: macbook Co-authored-by: Medulla --- scripts/generate-test-inventory.mjs | 4 +--- scripts/i18n-find-english.ts | 3 --- scripts/mock-api/routes/integrations.mjs | 22 ---------------------- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/scripts/generate-test-inventory.mjs b/scripts/generate-test-inventory.mjs index bf073232283..3a67d391c5d 100644 --- a/scripts/generate-test-inventory.mjs +++ b/scripts/generate-test-inventory.mjs @@ -28,8 +28,7 @@ // (b) CONTROLLER-DOMAIN CHECK — every controller domain registered in // `crates/openhuman-core/src/core/all.rs` (via `crate::::all_*_controllers`) // is referenced by >=1 file under `tests/`. Catches RPC domains that -// ship with zero integration/E2E coverage (recall_calendar, -// devices, …). +// ship with zero integration/E2E coverage (devices, …). // // Known-current offenders are seeded into the allowlists below so the check // lands green; the intent is to burn those lists down over time. Any NEW @@ -88,7 +87,6 @@ const DOMAIN_ALLOWLIST = new Set([ 'people', 'plan_review', 'provider_surfaces', - 'recall_calendar', 'referral', 'session_import', 'skill_runtime', diff --git a/scripts/i18n-find-english.ts b/scripts/i18n-find-english.ts index 3c4107b6d45..d1d15837aa3 100644 --- a/scripts/i18n-find-english.ts +++ b/scripts/i18n-find-english.ts @@ -95,9 +95,6 @@ const INTENTIONAL_ENGLISH = new Set([ "settings.taskSources.name", "skills.create.allowedToolsPlaceholder", "skills.create.optional", - "skills.meetingBots.wakePhraseHint", - "skills.meetingBots.platforms.gmeet", - "skills.meetingBots.platforms.teams", "conversations.goal.inlineSummary", // "{objective} ({status})" — both segments are variable placeholders, untranslatable data "vault.excludesPlaceholder", "vault.syncSummaryDuration", diff --git a/scripts/mock-api/routes/integrations.mjs b/scripts/mock-api/routes/integrations.mjs index 290b1adb614..b0f194cf4da 100644 --- a/scripts/mock-api/routes/integrations.mjs +++ b/scripts/mock-api/routes/integrations.mjs @@ -852,27 +852,5 @@ export function handleIntegrations(ctx) { return true; } - // ── Apify ────────────────────────────────────────────────── - // Gap fill — minimal stubs for run polling. - const apifyMatch = url.match( - /^\/agent-integrations\/apify\/runs\/([^/?]+)(\/results)?\/?(\?.*)?$/, - ); - if (apifyMatch && method === "GET") { - const [, runId, isResults] = apifyMatch; - if (isResults) { - json(res, 200, { success: true, data: { items: [] } }); - } else { - json(res, 200, { - success: true, - data: { - id: runId, - status: "SUCCEEDED", - finishedAt: new Date().toISOString(), - }, - }); - } - return true; - } - return false; } From 48751ea53640b6c9d18a7c5a39985f86e0f149c3 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:13:10 +0530 Subject: [PATCH 15/18] docs(notification): trim redundant comment on clearNotificationActions Shortened the comment above clearNotificationActions to drop the redundant example and the duplicated RPC names, keeping the explanation of why clearing actions removes the handled prompt from the actionable list. Auto-committed-on: macbook Co-authored-by: Medulla --- app/src/store/notificationSlice.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/store/notificationSlice.ts b/app/src/store/notificationSlice.ts index e2d45b83be6..d1cee51d7cb 100644 --- a/app/src/store/notificationSlice.ts +++ b/app/src/store/notificationSlice.ts @@ -104,10 +104,9 @@ const notificationSlice = createSlice({ if (item) item.read = true; }, // Drop the action buttons off a core notification once its prompt has been - // handled (e.g. a meeting auto-join join/skip succeeded). NotificationCenter - // only surfaces core items that still carry actions, so clearing them here - // removes the handled prompt from the actionable list and prevents a second - // click re-firing the same RPC (duplicate bot:join / always_join after skip). + // handled. NotificationCenter only surfaces core items that still carry + // actions, so clearing them here removes the handled prompt from the + // actionable list and prevents a second click re-firing the same RPC. clearNotificationActions(state, action: PayloadAction<{ id: string }>) { const item = state.items.find(i => i.id === action.payload.id); if (item) item.actions = undefined; From 0675cb5bd20960b019afc36402057613d143ddf6 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:16:28 +0530 Subject: [PATCH 16/18] refactor(integrations): remove unused imports from test support Trimmed the axum and std imports in the test support module down to the ones actually used, dropping the extract, routing, and HashMap imports that no longer have any references. Auto-committed-on: macbook Co-authored-by: Medulla --- crates/openhuman-core/src/integrations/test_support.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/openhuman-core/src/integrations/test_support.rs b/crates/openhuman-core/src/integrations/test_support.rs index eabee0258b8..7b8b74ab594 100644 --- a/crates/openhuman-core/src/integrations/test_support.rs +++ b/crates/openhuman-core/src/integrations/test_support.rs @@ -1,11 +1,6 @@ -use axum::{ - extract::{Path, Query}, - routing::{get, post}, - Json, Router, -}; +use axum::{routing::post, Json, Router}; use parking_lot::Mutex; use serde_json::{json, Value}; -use std::collections::HashMap; use std::sync::Arc; #[derive(Debug, Clone, PartialEq)] From 9c5bc53dd2272ba73d7c03397095c794c49def6b Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 10 Oct 2026 21:17:01 +0530 Subject: [PATCH 17/18] ci: lower legacy line limit for tools/ops.rs The pinned legacy size for crates/openhuman-core/src/tools/ops.rs was reduced from 978 to 970 lines to match the file's current length, keeping the layout check in sync after the file shrank. Auto-committed-on: macbook Co-authored-by: Medulla --- scripts/ci/check-openhuman-rust-layout.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/check-openhuman-rust-layout.mjs b/scripts/ci/check-openhuman-rust-layout.mjs index 5caeba4b71d..3fb926215b5 100644 --- a/scripts/ci/check-openhuman-rust-layout.mjs +++ b/scripts/ci/check-openhuman-rust-layout.mjs @@ -47,7 +47,7 @@ const LEGACY_LIMIT_ENTRIES = [ ["crates/openhuman-core/src/agent/session_host/builder/factory.rs", 973], ["crates/openhuman-core/src/agent/subagent_host/lifecycle.rs", 1247], ["crates/openhuman-core/src/agent/subagent_host/ops/runner.rs", 1132], - ["crates/openhuman-core/src/tools/ops.rs", 978], + ["crates/openhuman-core/src/tools/ops.rs", 970], ["crates/openhuman-core/src/web_chat/progress_bridge.rs", 1264], // These established external test modules grew with upstream coverage. Pin // their current sizes while follow-up work separates their test concerns. From a7c47a50b0a5c6d95f3f58da73641070bdb690fd Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sun, 11 Oct 2026 00:18:30 +0530 Subject: [PATCH 18/18] chore(deps): add jsonschema and refresh transitive dependencies Adds the jsonschema crate to the app's dependency graph, pulling in its supporting crates, and bumps several transitive dependencies including windows-sys to 0.61.2. Auto-committed-on: macbook Co-authored-by: Medulla --- crates/openhuman-app/Cargo.lock | 277 ++++++++++++++++++++++++++++++-- 1 file changed, 262 insertions(+), 15 deletions(-) diff --git a/crates/openhuman-app/Cargo.lock b/crates/openhuman-app/Cargo.lock index c784a08d41c..4fdcabc5137 100644 --- a/crates/openhuman-app/Cargo.lock +++ b/crates/openhuman-app/Cargo.lock @@ -61,6 +61,20 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.5" @@ -70,6 +84,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "alsa" version = "0.11.0" @@ -607,6 +627,12 @@ dependencies = [ "piper", ] +[[package]] +name = "borrow-or-share" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" + [[package]] name = "bs58" version = "0.5.1" @@ -634,6 +660,12 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "bytemuck" version = "1.25.2" @@ -715,7 +747,7 @@ dependencies = [ "cap-primitives", "cap-std", "io-lifetimes 3.0.1", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -732,7 +764,7 @@ dependencies = [ "maybe-owned", "rustix", "rustix-linux-procfs", - "windows-sys 0.60.2", + "windows-sys 0.61.2", "winx", ] @@ -1595,7 +1627,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.3", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1779,6 +1811,9 @@ name = "email_address" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +dependencies = [ + "serde", +] [[package]] name = "embed-resource" @@ -1911,7 +1946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1967,6 +2002,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy-regex" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301f5bf187b3c295fce6468d3875037a0bccc5f6b151c63cac2f85babf21912" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "fast-float2" version = "0.2.4" @@ -2049,6 +2095,17 @@ dependencies = [ "zlib-rs", ] +[[package]] +name = "fluent-uri" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2118,6 +2175,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e246562084dde8ebbcc943b261c406ce4f68e5032ec28029a251a47d6a295500" +dependencies = [ + "num", + "num-bigint", +] + [[package]] name = "fs-set-times" version = "0.20.3" @@ -2368,9 +2435,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 5.3.0", "wasip2", + "wasm-bindgen", ] [[package]] @@ -2615,6 +2684,8 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -2863,7 +2934,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.58.0", + "windows-core 0.62.2", ] [[package]] @@ -3340,6 +3411,59 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonschema" +version = "0.58.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f622c574a29fb3034a0a90645afee194dfedc75fc124228801b6e77041075bd1" +dependencies = [ + "ahash", + "bytecount", + "data-encoding", + "email_address", + "fancy-regex", + "fraction", + "getrandom 0.3.4", + "itoa", + "jsonschema-regex", + "jsonschema-value", + "num-cmp", + "num-traits", + "percent-encoding", + "referencing", + "regex", + "serde", + "serde_json", + "strum", + "unicode-general-category", + "uuid-simd", +] + +[[package]] +name = "jsonschema-regex" +version = "0.58.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a968fffd387b2ab005bf00c1851f5d26e90df05b6f7ad609ed0d620033cca5e7" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "jsonschema-value" +version = "0.58.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39b7670345e5ce87371339d13705dbb9276b7e3e934bbe1efa2c6734a1f65a96" +dependencies = [ + "ahash", + "bytecount", + "fraction", + "getrandom 0.3.4", + "num-cmp", + "num-traits", + "serde_json", + "zmij", +] + [[package]] name = "keccak" version = "0.2.2" @@ -3688,6 +3812,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "micromap" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a86d3146ed3995b5913c414f6664344b9617457320782e64f0bb44afd49d74" + [[package]] name = "mime" version = "0.3.17" @@ -3759,7 +3889,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.20", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3883,7 +4013,21 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", ] [[package]] @@ -3896,6 +4040,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.2" @@ -3922,6 +4081,27 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -4589,6 +4769,7 @@ dependencies = [ "async-trait", "chrono", "futures-core", + "jsonschema", "log", "openhuman", "sentry", @@ -4762,6 +4943,12 @@ dependencies = [ "thiserror 2.0.20", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "pango" version = "0.18.3" @@ -5302,7 +5489,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5495,6 +5682,23 @@ dependencies = [ "syn 3.0.6", ] +[[package]] +name = "referencing" +version = "0.58.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558f3855529c0a7e78a48e409fa653456e55f1b167eab4109c16a255b7727e3f" +dependencies = [ + "ahash", + "fluent-uri", + "getrandom 0.3.4", + "hashbrown 0.17.1", + "itoa", + "micromap", + "parking_lot", + "percent-encoding", + "serde_json", +] + [[package]] name = "regex" version = "1.13.1" @@ -5726,7 +5930,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5794,7 +5998,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6428,7 +6632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6636,6 +6840,27 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "subtle" version = "2.6.1" @@ -7199,7 +7424,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -8763,7 +8988,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.20", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -8874,7 +9099,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -8939,6 +9164,12 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" +[[package]] +name = "unicode-general-category" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f" + [[package]] name = "unicode-ident" version = "1.0.26" @@ -9099,6 +9330,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "uuid-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "valuable" version = "0.1.1" @@ -9123,6 +9364,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "vswhom" version = "0.1.0" @@ -9494,7 +9741,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]]