Skip to content

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

Merged
senamakel merged 4 commits intotinyhumansai:mainfrom
senamakel:feat/monday-4
Apr 14, 2026
Merged

feat: disable permission auto-prompts + typing indicator on webhook channels#552
senamakel merged 4 commits intotinyhumansai:mainfrom
senamakel:feat/monday-4

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented Apr 14, 2026

Summary

Merged upstream/main (picking up #549 progressive-edit streaming + typing indicator) into this branch. After the merge, only the privacy-defaults change remains unique to this PR — the typing-indicator work I originally included in rest.rs and channels/bus.rs was already landed upstream in #549 with a stronger implementation (progressive edits, latched fallback). I took upstream's version of both files during conflict resolution.

What's left in this PR

Flip three config defaults to false so a fresh install never triggers a macOS TCC permission dialog before the user opts in:

  • dictation.enabled: truefalse (was auto-starting rdev::listen, which requests Accessibility / Input Monitoring on macOS)
  • screen_intelligence.use_vision_model: truefalse (Pass 1 Apple Vision OCR still runs; skips the surprise vision-model call)

screen_intelligence.enabled, autocomplete.enabled, and voice_server.auto_start already defaulted to false — this aligns dictation with the same opt-in posture.

Companion backend PR

The upstream client (post-#549) already calls POST /channels/:id/typing, but the backend route isn't deployed yet. https://github.com/tinyhumansai/backend/pull/636 adds it (Telegram sendChatAction + Discord Trigger Typing Indicator).

Test plan

  • cargo check --bin openhuman-core clean.
  • cargo test --lib --bin openhuman-core — 2655 passed / 0 failed.
  • Pre-push gate (format, lint, TS compile, rust check) clean.
  • Fresh macOS install: confirm no Screen Recording, Accessibility, Input Monitoring, or Microphone dialog appears until an RPC explicitly opts in.
  • dictation.enabled = true in config.toml, restart — confirm the Accessibility / Input Monitoring prompt now fires again.

Summary by CodeRabbit

  • Refactor
    • Vision model is now disabled by default
    • Dictation is now disabled by default

…l 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.
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.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

📝 Walkthrough

Walkthrough

Three configuration and test changes: two schema defaults flipped from true to false (vision model and dictation enablement), and test-only synchronization guard added to prevent environment variable race conditions across concurrent tests.

Changes

Cohort / File(s) Summary
Configuration Schema Defaults
src/openhuman/config/schema/accessibility.rs, src/openhuman/config/schema/dictation.rs
Flipped default values from true to false: ScreenIntelligenceConfig.use_vision_model and DictationConfig.enabled now disable these features by default instead of enabling them.
Test Synchronization
src/openhuman/composio/bus.rs
Added process-wide Mutex-based synchronization guard (TRIAGE_ENV_GUARD) in test module to serialize environment variable mutations and prevent race conditions across concurrent tests.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 Whiskers twitch at configs three,
Vision dims from yes to nay,
Dictation sleeps while tests all breathe,
Mutex guards the lock at play,
Rabbit paths now synchronized today!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title focuses on disabling permission auto-prompts and typing indicators on webhook channels, but the actual changes disable dictation and vision model features to avoid macOS TCC permission dialogs—a different concern than the title suggests. Revise the title to accurately reflect the main changes, such as: 'feat: disable dictation and vision model defaults for privacy' or 'feat: change dictation and screen intelligence defaults to false'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/openhuman/config/schema/accessibility.rs (1)

22-26: ⚠️ Potential issue | 🟡 Minor

Update stale rustdoc default for use_vision_model.

Line 25 still says Default: true., but default_use_vision_model() now returns false. Please update the doc comment to prevent config confusion.

Suggested patch
-    /// feeds into the text synthesis LLM (Pass 3) — no vision model required.
-    /// Default: `true`.
+    /// feeds into the text synthesis LLM (Pass 3) — no vision model required.
+    /// Default: `false`.

As per coding guidelines, {src/**/*.rs,.github/**/*.md,docs/**/*.md}: Add concise rustdoc / code comments where the flow is not obvious; update ... docs when user-visible behavior change.

Also applies to: 70-72

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/config/schema/accessibility.rs` around lines 22 - 26, Update
the stale rustdoc for the accessibility flag so it matches the actual default
returned by default_use_vision_model(): change the comment text that reads
"Default: `true`." to "Default: `false`." for the use_vision_model field and any
other duplicate occurrences (e.g., the similar comment around lines 70-72) so
the documentation matches the behavior of default_use_vision_model().
🧹 Nitpick comments (1)
src/openhuman/channels/bus.rs (1)

97-105: Capture request context before spawning the typing loop.

Lines 97-105, 175-187, and 193-228 only carry channel into the background task, so the typing path loses request_id and repeats config/session/client setup every 4 seconds. Passing an owned context into spawn_typing_task would keep these logs correlatable and avoid repeating most of that setup work on every refresh.

As per coding guidelines, "include correlation fields such as request IDs, method names, and entity IDs".

Also applies to: 175-187, 193-228

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/channels/bus.rs` around lines 97 - 105, The typing background
task currently only receives channel, losing request context (request_id) and
forcing repeated setup; capture and clone the minimal owned context (at least
request_id plus any correlation/config needed) before you call spawn_typing_task
and pass that owned context into spawn_typing_task instead of just channel so
the task can log correlated fields and reuse precomputed config/session/client;
update the spawn_typing_task signature and its callers (where typing_cancel,
typing_handle, spawn_typing_task, and run_agent_loop are used) to accept the
owned context and remove duplicate setup inside the task; apply the same change
to the other spawn_typing_task usages referenced in this review.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/openhuman/channels/bus.rs`:
- Around line 153-155: The RecvError::Lagged branch currently only warns and
continues, which can miss the matching chat_done/chat_error for request_id and
cause bogus timeouts; update the handler in the loop handling inbound channel
events (the branch matching tokio::sync::broadcast::error::RecvError::Lagged
under the "[channel-inbound]" logic) to treat lag as terminal for this request:
after logging, break out of the processing loop or return an error so the caller
can perform cleanup and emit a chat_error (or otherwise mark the request
finished) instead of continuing to wait for events (preventing the 180s bogus
timeout).

---

Outside diff comments:
In `@src/openhuman/config/schema/accessibility.rs`:
- Around line 22-26: Update the stale rustdoc for the accessibility flag so it
matches the actual default returned by default_use_vision_model(): change the
comment text that reads "Default: `true`." to "Default: `false`." for the
use_vision_model field and any other duplicate occurrences (e.g., the similar
comment around lines 70-72) so the documentation matches the behavior of
default_use_vision_model().

---

Nitpick comments:
In `@src/openhuman/channels/bus.rs`:
- Around line 97-105: The typing background task currently only receives
channel, losing request context (request_id) and forcing repeated setup; capture
and clone the minimal owned context (at least request_id plus any
correlation/config needed) before you call spawn_typing_task and pass that owned
context into spawn_typing_task instead of just channel so the task can log
correlated fields and reuse precomputed config/session/client; update the
spawn_typing_task signature and its callers (where typing_cancel, typing_handle,
spawn_typing_task, and run_agent_loop are used) to accept the owned context and
remove duplicate setup inside the task; apply the same change to the other
spawn_typing_task usages referenced in this review.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f7318a24-77a8-48e5-9f14-8dc09bc5be15

📥 Commits

Reviewing files that changed from the base of the PR and between 3db06e8 and 8aad251.

📒 Files selected for processing (4)
  • src/api/rest.rs
  • src/openhuman/channels/bus.rs
  • src/openhuman/config/schema/accessibility.rs
  • src/openhuman/config/schema/dictation.rs

Comment thread src/openhuman/channels/bus.rs Outdated
Comment on lines +153 to +155
Err(tokio::sync::broadcast::error::RecvError::Lagged(n)) => {
tracing::warn!("[channel-inbound] event bus lagged, skipped {} events", n);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Treat Lagged as terminal for this request.

Lines 153-155 only log and continue. After RecvError::Lagged, this receiver may already have skipped the matching chat_done / chat_error event for request_id, so the loop can keep typing until the 180s timeout and then emit a bogus timeout reply.

Minimal fail-closed fix
-                    Err(tokio::sync::broadcast::error::RecvError::Lagged(n)) => {
-                        tracing::warn!("[channel-inbound] event bus lagged, skipped {} events", n);
-                    }
+                    Err(tokio::sync::broadcast::error::RecvError::Lagged(n)) => {
+                        tracing::error!("[channel-inbound] event bus lagged, skipped {} events", n);
+                        send_channel_reply(
+                            channel,
+                            "Sorry, I lost track of that request. Please try again.",
+                        )
+                        .await;
+                        return;
+                    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/channels/bus.rs` around lines 153 - 155, The RecvError::Lagged
branch currently only warns and continues, which can miss the matching
chat_done/chat_error for request_id and cause bogus timeouts; update the handler
in the loop handling inbound channel events (the branch matching
tokio::sync::broadcast::error::RecvError::Lagged under the "[channel-inbound]"
logic) to treat lag as terminal for this request: after logging, break out of
the processing loop or return an error so the caller can perform cleanup and
emit a chat_error (or otherwise mark the request finished) instead of continuing
to wait for events (preventing the 180s bogus timeout).

# Conflicts:
#	src/api/rest.rs
#	src/openhuman/channels/bus.rs
…ED_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.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/openhuman/composio/bus.rs (1)

515-527: Make env-var cleanup panic-safe to avoid cascading test pollution.

Cleanup is currently manual; if an assertion/panic happens before remove_var, later tests may inherit stale state. Prefer an RAII restore helper.

♻️ Proposed refactor (panic-safe env restoration)
 #[cfg(test)]
 mod tests {
     use super::*;
     use serde_json::json;
     use std::sync::Mutex;
@@
     static TRIAGE_ENV_GUARD: Mutex<()> = Mutex::new(());
+
+    struct EnvVarRestore {
+        key: &'static str,
+        prev: Option<String>,
+    }
+
+    impl EnvVarRestore {
+        fn set(key: &'static str, value: &str) -> Self {
+            let prev = std::env::var(key).ok();
+            std::env::set_var(key, value);
+            Self { key, prev }
+        }
+    }
+
+    impl Drop for EnvVarRestore {
+        fn drop(&mut self) {
+            if let Some(prev) = &self.prev {
+                std::env::set_var(self.key, prev);
+            } else {
+                std::env::remove_var(self.key);
+            }
+        }
+    }
@@
     async fn handles_trigger_event_without_panic() {
         let _guard = TRIAGE_ENV_GUARD.lock().unwrap_or_else(|e| e.into_inner());
-        std::env::set_var(TRIAGE_DISABLED_ENV, "1");
+        let _env_restore = EnvVarRestore::set(TRIAGE_DISABLED_ENV, "1");
@@
-        std::env::remove_var(TRIAGE_DISABLED_ENV);
     }
@@
     fn triage_disabled_flag_parser() {
         let _guard = TRIAGE_ENV_GUARD.lock().unwrap_or_else(|e| e.into_inner());
+        let _env_restore = EnvVarRestore {
+            key: TRIAGE_DISABLED_ENV,
+            prev: std::env::var(TRIAGE_DISABLED_ENV).ok(),
+        };
@@
-        std::env::remove_var(TRIAGE_DISABLED_ENV);
+        std::env::remove_var(TRIAGE_DISABLED_ENV);
         assert!(!triage_disabled(), "unset must default to triage enabled");
     }
 }

Also applies to: 531-545

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/composio/bus.rs` around lines 515 - 527, The test sets
TRIAGE_DISABLED_ENV manually and removes it later which is not panic-safe;
replace the manual set/remove with an RAII helper that saves the previous state
and restores it on drop (e.g., an EnvRestore or TriageEnvGuard) and use it
around the ComposioTriggerSubscriber::new() / sub.handle(...) sequence; ensure
the helper interacts with TRIAGE_DISABLED_ENV and acquires/reuses
TRIAGE_ENV_GUARD as needed so removal always happens even if assertions panic,
and update both occurrences (the block around lines shown and the similar block
at 531-545) to construct this guard instead of calling
std::env::set_var/remove_var directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/openhuman/composio/bus.rs`:
- Around line 515-527: The test sets TRIAGE_DISABLED_ENV manually and removes it
later which is not panic-safe; replace the manual set/remove with an RAII helper
that saves the previous state and restores it on drop (e.g., an EnvRestore or
TriageEnvGuard) and use it around the ComposioTriggerSubscriber::new() /
sub.handle(...) sequence; ensure the helper interacts with TRIAGE_DISABLED_ENV
and acquires/reuses TRIAGE_ENV_GUARD as needed so removal always happens even if
assertions panic, and update both occurrences (the block around lines shown and
the similar block at 531-545) to construct this guard instead of calling
std::env::set_var/remove_var directly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c81c3526-bbb8-4e20-828c-334a5e6f55ed

📥 Commits

Reviewing files that changed from the base of the PR and between 8aad251 and d9e9f41.

📒 Files selected for processing (1)
  • src/openhuman/composio/bus.rs

@senamakel senamakel merged commit 7ae2bf8 into tinyhumansai:main Apr 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant