Skip to content

fix(agent): restart into an Input Monitoring grant this process cannot use - #1593

Closed
aniruddhaadak80 wants to merge 3 commits into
AprilNEA:masterfrom
aniruddhaadak80:fix/macos-input-monitoring-grant-relaunch
Closed

aniruddhaadak80 wants to merge 3 commits into
AprilNEA:masterfrom
aniruddhaadak80:fix/macos-input-monitoring-grant-relaunch

Conversation

@aniruddhaadak80

Copy link
Copy Markdown

Summary

On macOS the Input Monitoring decision is resolved for a process when it starts, so a grant made afterwards applies to the next launch of the identity and not to the running one. The agent already asks for the permission at arming and relaunches when the consent dialog is answered with a grant, but a grant that arrives later - in System Settings, with no dialog left to answer - had no path at all: the polling watcher republished the new state to clients and nothing restarted the agent. Every in-process retry therefore kept failing against a permission that could never take effect, which is the dead end in #1304 where both "Try again" and a GUI restart leave the KeepAlive launchd job untouched.

This records what arming learned about the permission and turns a grant the current process cannot use into a relaunch, reusing the successor the binary watcher already knows how to schedule.

Changes

  • crates/openlogi-agent-core
  • crates/openlogi-agent
    • lifecycle.rs: Running now carries the arming outcome; request_input_monitoring reports it as AccessAtArming (InEffect / GrantedToSuccessor / Denied) instead of only deciding whether to relaunch immediately. The Input Monitoring watcher event republishes the state as before and then, when the grant is one this process cannot use, schedules a relaunch and leaves through the normal shutdown path. schedule_input_monitoring_relaunch has a non-macOS body so the branch is a plain false there rather than dead code.

Behaviour deliberately unchanged: a grant answered in the consent dialog still relaunches at arming, and a refusal is still left alone - relaunching into a declined dialog would ask again on every launch. A revoke never restarts the agent either, since that process held the grant already.

No wire types, locales, packaging or CI inputs are touched, so PROTOCOL_VERSION and the locale catalogs are unchanged.

Testing

No command below was run. This branch was prepared through the GitHub API without a checkout, so nothing was compiled, formatted, linted or executed locally, and no result is being claimed. CI is the first compile of this diff. These are the commands the diff asks for, in the order I would run them:

Focused proof of the new contract:

cargo test -p openlogi-agent-core input_monitoring

Local gate. The changed packages are openlogi-agent-core and openlogi-agent, and the latter is a dependency of most of the application, so the reverse-dependency closure is the whole workspace - the affected-package tier is not the honest choice here and this is the full tier:

export RUSTFLAGS="-D warnings"
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps \
  --document-private-items --exclude openlogi-ui --exclude openlogi-desktop \
  --exclude openlogi-overlay --exclude openlogi-agent

This diff is almost entirely #[cfg(target_os = ...)], so macOS-green proves nothing about the other hosts. Those cross-lints are also not run:

cargo xtask ci clippy-windows          # or: devenv tasks run openlogi:check-windows
cargo clippy --target aarch64-unknown-linux-musl \
  -p openlogi-hook -p openlogi-inject -p openlogi-hid -p openlogi-hidpp \
  -p openlogi-core -p openlogi-agent -p openlogi-agent-core -p openlogi-ipc \
  -p openlogi-permissions --all-targets -- -D warnings

In their place the cfg-gated code was hand-audited line by line, and the audit is what shaped the diff: the relaunch condition is evaluated on every host (so the new field is read, and needs_successor is used, off macOS too), the only macOS-only piece is the free function that schedules the successor, and its non-macOS twin returns false - which keeps the if body non-empty everywhere and avoids dead_code, an empty if, and unused_async under the pedantic table. Access and needs_successor are pub in a library crate, so they cannot be reported as dead on any host.

Not required by this diff, and not run: typos, ast-grep, shell, cargo-deny, publish closure, wasm, MSRV, wire_format, locale parity.

Not runtime-tested on hardware. No Logitech device, no macOS host, and no TCC grant were involved on my side. macOS TCC and hardware limits worth knowing before verifying by hand:

  • The behaviour only exists on macOS. Off it, Access::Granted is the only state and nothing can call for a relaunch, so the four unit tests are the only executable coverage of the decision.
  • Reproducing the original report needs a fresh Input Monitoring denial: install, launch, decline or ignore the consent dialog, then grant in System Settings with the agent still running. A machine that already granted the permission cannot show it.
  • Verify with a signed bundle. An ad-hoc-signed dev build has a cdhash designated requirement, so the grant goes stale on every rebuild.
  • After the relaunch the agent must be the one launchd runs: sudo launchctl procinfo $(pgrep -x openlogi-agent) | grep -i responsible should name the agent, not OpenLogi.app or Terminal.app.
  • IOHIDRequestAccess blocks the calling thread, so the prompt still runs off the async runtime; that path is unchanged.

One question for the maintainer

The fix triggers on the watcher observing the grant, which is the right hook if IOHIDCheckAccess reports an out-of-band grant to the running process. If macOS instead keeps the pre-grant answer cached for the life of the process - which the report's 18 minutes of unchanged log lines are consistent with, and which no CI job here can settle - then this path stays quiet and the dead end survives. In that case the remaining fix is the other half of the suggestion in #1304: have the GUI's Input Monitoring row and "Try again" ask the agent to restart, which needs an append-only IPC method and a PROTOCOL_VERSION bump. I left that out deliberately rather than guess which half you want; tell me which and I will do it.

On the report's secondary point about the copy naming a row that does not exist: I did not touch it. The permissions skill is explicit that the identity to grant is OpenLogi Agent and that a grant to the GUI does nothing for the agent, so renaming the row in the message to match what one machine happened to list would contradict the documented identity map. Happy to revisit if the skill's guidance has moved.

Suggested guidance changes

Not applied here, per the guidance-maintenance rule - raised for maintainer review as a separate change:

  • .claude/skills/openlogi-macos-permissions/SKILL.md §5 could state the consequence more sharply: the existing relaunch covers a grant answered in the dialog, and a grant made in System Settings needs the watcher to hand it to a successor. Worth one sentence now that the second path exists.

Fixes #1304

…t use

macOS resolves the Input Monitoring decision for a process when it starts,
so a grant made afterwards is applied to the next launch of the identity and
not to the running one. The agent already asks for the permission at arming
and relaunches when the consent dialog is answered with a grant, but a grant
that arrives later -- in System Settings, with no dialog left to answer --
had no path at all: the watcher republished the new state to clients and
nothing restarted the agent, so every in-process retry kept failing against a
permission that could never take effect. That is the dead end in AprilNEA#1304, where
"Try again" and a GUI restart both leave the launchd job untouched.

Record what arming learned about the permission and let the watcher turn a
grant this process cannot use into a relaunch, reusing the successor the
binary watcher already knows how to schedule. A revoke still does not
restart the agent: it held the grant already, and relaunching on every
revoke would put the agent straight back into the dialog the user dismissed.

The decision is a pure function with unit tests, so the contract is checked
on every host CI builds rather than only on a machine that can reproduce a
TCC grant.
@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium impact] The code appears safe to merge, with the previous non-blocking concern about the uncompiled helper still open.

Fix All in CodexFindings

  1. P2 Hit test is never compiled ▶

Summary

The actual diff adds a rectangle-based hit_test helper and two tests in crates/openlogi-hook/src/pointer/hit_test.rs.

  • Pointer hit testing picks the first matching window target.

Reviews (3) · Last reviewed commit: "Merge branch 'master' into fix/macos-inp..." · Reviewed by Greptile

Comment thread crates/openlogi-agent/src/lifecycle.rs Outdated
@greptile-apps

This comment has been minimized.

Copilot AI balanced review requested due to automatic review settings October 3, 2026 22:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

}

/// A malformed row is not evidence that the space behind it is desktop.
pub(crate) fn hit_test(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Hit test is never compiled

pointer.rs does not declare this file as a module, and no production code calls hit_test. Rust therefore never compiles the helper or discovers its two tests. Pointer lookup continues to use the existing implementation, while the new tests provide no coverage.

Knowledge Base Used: Global input hooks

Fix in Codex Fix in Claude Code

@davidbudnick davidbudnick added platform: macos macOS-specific issue type: bug Something is broken or behaves incorrectly labels Oct 9, 2026
@davidbudnick
davidbudnick self-requested a review as a code owner October 9, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: macos macOS-specific issue type: bug Something is broken or behaves incorrectly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: macOS — granting Input Monitoring has no effect until the agent's launchd job is restarted, so "Try again" can never succeed

3 participants