From 7ae2bf83b2395a5339255f79dbe357f951c7cd3c Mon Sep 17 00:00:00 2001 From: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Date: Tue, 14 Apr 2026 08:19:57 -0700 Subject: [PATCH] feat: disable permission auto-prompts + typing indicator on webhook channels (#552) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(config): default screen intelligence, dictation, and vision model off Flip defaults so no macOS TCC permission prompt fires on first run: - `dictation.enabled`: `true` → `false` (was auto-starting rdev::listen, which requests Accessibility/Input Monitoring on macOS) - `screen_intelligence.use_vision_model`: `true` → `false` (fewer surprise vision-model calls; Pass 1 Apple Vision OCR still runs) Aligns all permission-gated auto-starts on a consistent opt-in posture: `screen_intelligence.enabled`, `autocomplete.enabled`, and `voice_server.auto_start` already default to `false`. Users must now explicitly flip each toggle (config or JSON-RPC) before the core triggers any OS permission dialog. * feat(channels): fire typing indicator on webhook-inbound path Two inbound flows exist today and only one fires typing: - Local bot (`channels_config.telegram.bot_token` set) → dispatch.rs already calls `channel.start_typing` + `spawn_scoped_typing_task` - Backend webhook (Telegram → backend → socket.io → core) → `ChannelInboundSubscriber` had **no typing call** — replies route via backend REST, so the local `Channel` trait isn't reachable. Close the gap by going through the backend: - `api/rest.rs`: add `send_channel_typing(channel, jwt, body)` hitting the new `POST /channels/:id/typing` backend route. - `channels/bus.rs`: extract the agent-wait loop into `run_agent_loop` and wrap it with a typing task that fires immediately on `start_chat` success, refreshes every 4s (beats Telegram's ~5s and Discord's ~10s typing TTLs), and cancels on every exit path (done / error / empty / bus-closed / lagged / timeout). Backend failures log at debug — a flaky typing call must never block the reply flow. Generalises to every channel with a backend adapter; adapters without a native typing API no-op gracefully. * Enhance test stability by introducing a Mutex guard for TRIAGE_DISABLED_ENV in tests - Added a static Mutex guard to ensure safe concurrent access to the `TRIAGE_DISABLED_ENV` variable during tests, preventing interleaved set_var/remove_var calls that could lead to spurious failures. - Updated relevant test cases to acquire the Mutex lock when accessing the environment variable, ensuring consistent behavior across concurrent test executions. --- src/openhuman/composio/bus.rs | 9 +++++++++ src/openhuman/config/schema/accessibility.rs | 2 +- src/openhuman/config/schema/dictation.rs | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/openhuman/composio/bus.rs b/src/openhuman/composio/bus.rs index 193a39c43..736753478 100644 --- a/src/openhuman/composio/bus.rs +++ b/src/openhuman/composio/bus.rs @@ -489,6 +489,13 @@ async fn wait_for_connection_active( mod tests { use super::*; use serde_json::json; + use std::sync::Mutex; + + /// Cargo runs tests concurrently by default, and `TRIAGE_DISABLED_ENV` + /// is process-global. Every test that reads or writes it must hold this + /// guard for the duration of its env-var usage, otherwise interleaved + /// `set_var` / `remove_var` calls cause spurious failures. + static TRIAGE_ENV_GUARD: Mutex<()> = Mutex::new(()); #[tokio::test] async fn ignores_non_composio_events() { @@ -505,6 +512,7 @@ mod tests { async fn handles_trigger_event_without_panic() { // Disable triage so this test takes the log-only path and // doesn't spawn a real LLM turn. + let _guard = TRIAGE_ENV_GUARD.lock().unwrap_or_else(|e| e.into_inner()); std::env::set_var(TRIAGE_DISABLED_ENV, "1"); let sub = ComposioTriggerSubscriber::new(); sub.handle(&DomainEvent::ComposioTriggerReceived { @@ -520,6 +528,7 @@ mod tests { #[test] fn triage_disabled_flag_parser() { + let _guard = TRIAGE_ENV_GUARD.lock().unwrap_or_else(|e| e.into_inner()); // Truthy values disable triage. for val in ["1", "true", "TRUE", "yes", "YES"] { std::env::set_var(TRIAGE_DISABLED_ENV, val); diff --git a/src/openhuman/config/schema/accessibility.rs b/src/openhuman/config/schema/accessibility.rs index 7ed04092b..669e44f91 100644 --- a/src/openhuman/config/schema/accessibility.rs +++ b/src/openhuman/config/schema/accessibility.rs @@ -68,7 +68,7 @@ fn default_autocomplete_enabled() -> bool { } fn default_use_vision_model() -> bool { - true + false } impl Default for ScreenIntelligenceConfig { diff --git a/src/openhuman/config/schema/dictation.rs b/src/openhuman/config/schema/dictation.rs index fbc3d6c7e..b5e976964 100644 --- a/src/openhuman/config/schema/dictation.rs +++ b/src/openhuman/config/schema/dictation.rs @@ -44,7 +44,7 @@ pub struct DictationConfig { } fn default_enabled() -> bool { - true + false } fn default_hotkey() -> String {