feat: disable permission auto-prompts + typing indicator on webhook channels (#552)

* 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.
This commit is contained in:
Steven Enamakel
2026-04-14 08:19:57 -07:00
committed by GitHub
parent 933c233704
commit 7ae2bf83b2
3 changed files with 11 additions and 2 deletions
+9
View File
@@ -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);
+1 -1
View File
@@ -68,7 +68,7 @@ fn default_autocomplete_enabled() -> bool {
}
fn default_use_vision_model() -> bool {
true
false
}
impl Default for ScreenIntelligenceConfig {
+1 -1
View File
@@ -44,7 +44,7 @@ pub struct DictationConfig {
}
fn default_enabled() -> bool {
true
false
}
fn default_hotkey() -> String {