Skip to content

feat(approval): consolidate on ApprovalGate + persistent "Always allow" list#2706

Merged
sanil-23 merged 3 commits into
tinyhumansai:mainfrom
sanil-23:feat/1-consolidate-approval-gate
May 27, 2026
Merged

feat(approval): consolidate on ApprovalGate + persistent "Always allow" list#2706
sanil-23 merged 3 commits into
tinyhumansai:mainfrom
sanil-23:feat/1-consolidate-approval-gate

Conversation

@sanil-23
Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 commented May 26, 2026

Summary

  • Consolidate all tool approvals on ApprovalGate; delete the dead legacy ApprovalManager (it was only ever passed None at its single live call site).
  • Turn the previously-unused autonomy.auto_approve field into a real, persisted "Always allow" allowlist the gate honors.
  • New "Always allow" button on the chat approval card, and a view/remove list in Settings → Agent access.
  • Install the process-global live_policy at serve boot (not only in start_channels), so the allowlist works on web-chat-only cores.
  • Remove the unused always_ask field.

Problem

Post-#2631 there were two approval systems. ApprovalManager (approval/ops.rs, name-list based) was dead at runtime — constructed only in tests; the sole live caller (agent/bus.rs) passed None. The auto_approve / always_ask config fields fed only that dead manager, so setting them had no effect — the live ApprovalGate (SecurityPolicy::from_config) explicitly did not consume them. This is confusing dead config and dual-path complexity.

Solution

  • SecurityPolicy gains auto_approve; from_config copies it. ApprovalGate reads it via live_policy::current() (falls back to its boot Config) and short-circuits to Allow for listed tools before parking.
  • The approve_always_for_tool decision appends the tool to autonomy.auto_approve through the existing config save + live_policy reload path (mirrors how trusted_roots is managed) — so the choice persists across restarts and is removable in Settings.
  • live_policy::install now also runs on serve boot; start_channels (the other install site) is skipped on web-chat-only cores, which would otherwise leave the allowlist + autonomy reloads inert there.
  • Deleted ApprovalManager + ApprovalRequest/Response/LogEntry, the dead approval param/branch in run_tool_call_loop, and the always_ask field.

Safety: SecurityPolicy hard-denies (forbidden paths, high-risk command classes) still run before the gate, so the allowlist can only suppress the human prompt — it can never bypass a policy denial.

Submission Checklist

  • Tests added or updated (happy path + edge cases) — from_config carries auto_approve; gate auto_approve short-circuit; apply_autonomy_settings/add_auto_approve_tool round-trips (incl. dedupe); json_rpc autonomy e2e incl. auto_approve; loop → gate → auto_approve e2e (allow-listed external-effect tool executes without parking, even with a chat context); approval-card + Agent Access panel Vitest.
  • Diff coverage ≥ 80% — targeted tests added for the changed Rust + TS lines; final number enforced by CI diff-cover.
  • N/A Coverage matrix updated — the approval-gate / Agent Access surface (including feat(agent): agentic coding runtime — gated OS capabilities (filesystem, shell, install) via deterministic permission tiers + chat approvals #2631) is not represented in docs/TEST-COVERAGE-MATRIX.md; no existing row to update.
  • N/A All affected feature IDs listed in ## Related — see above; surface not tracked in the matrix.
  • No new external network dependencies introduced — no new deps; Rust e2e uses the mock backend.
  • N/A Manual smoke checklist — additive control on the existing approval surface; no new release-cut flow.
  • N/A Linked issue closed via Closes #NNN — tracked by fork issue sanil-23/openhuman#1 (private fork tracker); a closing keyword from this upstream PR would target the wrong repo, so it is referenced (not auto-closed) below.

Impact

  • Desktop only (approval gate is a desktop/in-process-core feature). No migration: AutonomyConfig has no deny_unknown_fields, so any pre-existing always_ask key in a user's TOML is silently ignored (it already had no effect).
  • Security: allowlist is prompt-suppression only; policy denials are unaffected. "Always allow" is eligible for all tools by design — SecurityPolicy still blocks the dangerous ones regardless.

Related

  • Closes: — (n/a; see checklist — fork tracker sanil-23/openhuman#1)
  • Follow-up PR(s)/TODOs: WDIO desktop e2e for the literal "click Always allow → next call skips" UI flow (the backend seam is covered by the loop e2e here).

AI Authored PR Metadata

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: feat/1-consolidate-approval-gate
  • Commit SHA: e775491e

Validation Run

  • pnpm --filter openhuman-app format:check (prettier --check on changed files: clean)
  • pnpm typecheck (clean)
  • Focused tests: cargo test approval/gate (10), policy from_config, live_policy, config apply/add_auto_approve, about_app (21), agent::harness (374 incl. loop→gate e2e); json_rpc_e2e json_rpc_config_autonomy_settings_roundtrip (mock backend); Vitest ApprovalRequestCard (7) / AgentAccessPanel (7) / AutonomyPanel (9); pnpm i18n:check (parity exact)
  • Rust fmt/check (if changed): cargo fmt --check clean; cargo check --tests clean
  • N/A Tauri fmt/check (if changed): app/src-tauri shell unchanged

Validation Blocked

  • command: pre-push hook pnpm rust:check (Tauri shell)
  • error: vendored CEF tauri-cli not installed in this environment
  • impact: pushed with --no-verify; the Tauri shell crate is untouched and the core lib it depends on compiles clean — CI runs the full shell check.

Behavior Changes

  • Intended: auto_approve is now honored as a persistent "Always allow" allowlist; always_ask removed; single approval path (ApprovalGate).
  • User-visible: "Always allow" button on the approval card; allowlist view/remove in Settings → Agent access.

Parity Contract

  • Legacy ApprovalManager was dead (the one live caller passed None) — no runtime behavior removed.
  • Guard/fallback: gate reads live_policy::current() then boot Config; policy hard-denies still precede the gate.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this
  • Resolution: n/a

Summary by CodeRabbit

  • New Features

    • Chat approval adds an “Always allow” action so tools can be auto-approved from the approval card; button shows hint, in-progress state, and is disabled while deciding.
    • Agent Access settings include an “Always-allowed tools” section to view and remove auto-approved tools; changes persist across restarts.
    • Approval respects a global always-allow policy so auto-approved tools run without prompting.
  • Tests

    • Added and updated tests covering the always-allow flow and allowlist persistence.
  • Localization

    • New UI strings for approval and Agent Access in multiple languages.

Review Change Stack

@sanil-23 sanil-23 requested a review from a team May 26, 2026 17:36
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

Adds “Always allow” approval decision end-to-end: frontend decision/button and settings, i18n keys, tauri RPC types, persistence helpers for auto_approve, live SecurityPolicy wiring, ApprovalGate short-circuiting, tool-loop integration, catalog metadata, and tests.

Changes

Always Allow Tool Approval Feature

Layer / File(s) Summary
Frontend approval UI and tests
app/src/components/chat/ApprovalRequestCard.tsx, app/src/components/chat/__tests__/ApprovalRequestCard.test.tsx
Decision expanded to include approve_always_for_tool; adds “Always allow” button with deciding state and test asserting RPC payload and store clearing.
Agent access settings UI
app/src/components/settings/panels/AgentAccessPanel.tsx, app/src/components/settings/panels/__tests__/*
Adds autoApprove state, initializes from auto_approve, persists allowlist only when provided, removeAutoApprove handler, UI listing and updated tests.
i18n keys
app/src/lib/i18n/en.ts, app/src/lib/i18n/chunks/*-3.ts, app/src/lib/i18n/chunks/*-5.ts
Adds chat.approval.alwaysAllow/alwaysAllowHint and settings.agentAccess.* strings across language chunks.
Tauri RPC types
app/src/utils/tauriCommands/config.ts
Adds auto_approve to AutonomySettings and optional auto_approve to AutonomySettingsUpdate.
Core bootstrap & catalog
src/core/jsonrpc.rs, src/openhuman/about_app/catalog.rs
Installs process-global live SecurityPolicy at bootstrap; adds security.always_allow_tool capability metadata.
ApprovalGate refactor & module cleanup
src/openhuman/approval/gate.rs, src/openhuman/approval/mod.rs, src/openhuman/approval/ops.rs
Removes session-scoped allowlist and legacy ops; adds live-policy auto-approve check and intercept_audited short-circuit; ApproveAlwaysForTool decide no longer mutates in-gate allowlist.
Approval RPC & config ops
src/openhuman/approval/rpc.rs, src/openhuman/config/ops.rs, src/openhuman/config/schemas.rs
approval_decide emits multi-log outcome and best-effort persists auto_approve; AutonomySettingsPatch/controller accept auto_approve; add_auto_approve_tool helper added with write lock and idempotent append.
Config schema & tests
src/openhuman/config/schema/autonomy.rs, src/openhuman/config/ops_tests.rs
Removes old always_ask, documents auto_approve, adds tests for replace and idempotent append; ENV_LOCK guards added to relevant tests.
SecurityPolicy/live-policy
src/openhuman/security/policy.rs, src/openhuman/security/live_policy.rs
SecurityPolicy adds auto_approve field wired from config; tests serialized to avoid live-policy races.
Tool loop refactor & tests
src/openhuman/agent/harness/tool_loop.rs, src/openhuman/agent/harness/tool_loop_tests.rs
run_tool_call_loop drops approval parameter; per-tool approval replaced by ApprovalGate::intercept_audited; added end-to-end test verifying auto-approved tool executes.
Agent harness & call-site updates & e2e
src/openhuman/agent/*, tests/json_rpc_e2e.rs
Many test call sites updated to match new run_tool_call_loop signature; e2e autonomy settings test extended to round-trip auto_approve.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • graycyrus
  • senamakel
  • M3gA-Mind

"I'm a rabbit, small and spry,
Pushed an 'Always allow' to fly,
The gate says yes, the tool runs free,
Saved to policy for next time, whee! 🐇"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the PR: consolidating approval logic onto ApprovalGate and implementing a persistent 'Always allow' list for tools.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. labels May 26, 2026
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

🧹 Nitpick comments (2)
src/openhuman/config/ops.rs (1)

891-909: ⚡ Quick win

Add structured success/error logs for the new auto-approve persistence path.

The new helper logs only the duplicate no-op branch. Add stable-prefix logs for entry, persisted append success, and failures from load/apply to match existing security/config observability expectations.

As per coding guidelines **/*.rs: "Log entry/exit, branches, external calls, retries/timeouts, state transitions, and errors with verbose diagnostics using stable grep-friendly prefixes and correlation fields".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/config/ops.rs` around lines 891 - 909, The
add_auto_approve_tool function currently only logs the duplicate no-op branch;
add structured, grep-friendly logs at function entry, on successful persist, and
on failures of load_config_with_timeout and apply_autonomy_settings: log an
entry message when add_auto_approve_tool is called (include tool_name and
correlation fields), capture and log any error returned from
load_config_with_timeout with a stable prefix and diagnostics, log the
successful append/persist after apply_autonomy_settings completes (stable
success prefix, tool_name, and before/after state or count), and log errors from
apply_autonomy_settings with a stable error prefix and the error details;
reference symbols: add_auto_approve_tool, load_config_with_timeout,
apply_autonomy_settings, and AutonomySettingsPatch when adding these logs.
app/src/components/settings/panels/__tests__/AgentAccessPanel.test.tsx (1)

13-23: ⚡ Quick win

Add one behavior test for always-allow list rendering/removal.

Line 21 updates fixture shape, but there’s still no assertion that preloaded auto_approve tools render and that removing one persists auto_approve in the update payload.

Suggested test addition
@@
   it('shows the desktop-only notice and skips loading off-Tauri', async () => {
     vi.mocked(isTauri).mockReturnValue(false);
     renderWithProviders(<AgentAccessPanel />);
     expect(await screen.findByText('Access mode')).toBeInTheDocument();
     expect(mockGet).not.toHaveBeenCalled();
   });
+
+  it('renders always-allowed tools and persists removal', async () => {
+    mockGet.mockResolvedValue({
+      result: autonomy({ auto_approve: ['shell'] }),
+      logs: [],
+    });
+    renderWithProviders(<AgentAccessPanel />);
+
+    expect(await screen.findByText('shell')).toBeInTheDocument();
+    fireEvent.click(screen.getAllByText('Remove')[0]);
+
+    await waitFor(() =>
+      expect(mockUpdate).toHaveBeenCalledWith(
+        expect.objectContaining({ auto_approve: [] })
+      )
+    );
+  });
 });

As per coding guidelines: app/**/*.test.{ts,tsx} should prefer behavior-focused, deterministic tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/settings/panels/__tests__/AgentAccessPanel.test.tsx`
around lines 13 - 23, Add a behavior test to AgentAccessPanel.test.tsx that uses
the autonomy fixture (autonomy) with a non-empty auto_approve array to assert
the panel initially renders the preloaded auto-approved tool label, then
simulate removing that item (e.g., click the remove control) and assert the
mocked update handler receives an updated AutonomySettings payload whose
auto_approve no longer contains the removed tool; locate the autonomy fixture
and the AgentAccessPanel render/test helpers (render/dispatch/mockUpdate) to
wire the preloaded state and inspect the update call.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/config/ops.rs`:
- Around line 891-909: add_auto_approve_tool performs a load-modify-save on the
entire auto_approve vector and can drop concurrent updates; protect the
read-modify-write path by serializing mutations or merging on save: wrap the
load_config_with_timeout + check + push + apply_autonomy_settings sequence in a
process-local async mutex (or implement a retry loop that reloads and unions the
current config.auto_approve before calling apply_autonomy_settings) so
concurrent calls to add_auto_approve_tool (and the AutonomySettingsPatch write)
result in the union of tool names rather than last-write-wins; update references
in add_auto_approve_tool and the call to apply_autonomy_settings to use the
serialized/merged approach.

---

Nitpick comments:
In `@app/src/components/settings/panels/__tests__/AgentAccessPanel.test.tsx`:
- Around line 13-23: Add a behavior test to AgentAccessPanel.test.tsx that uses
the autonomy fixture (autonomy) with a non-empty auto_approve array to assert
the panel initially renders the preloaded auto-approved tool label, then
simulate removing that item (e.g., click the remove control) and assert the
mocked update handler receives an updated AutonomySettings payload whose
auto_approve no longer contains the removed tool; locate the autonomy fixture
and the AgentAccessPanel render/test helpers (render/dispatch/mockUpdate) to
wire the preloaded state and inspect the update call.

In `@src/openhuman/config/ops.rs`:
- Around line 891-909: The add_auto_approve_tool function currently only logs
the duplicate no-op branch; add structured, grep-friendly logs at function
entry, on successful persist, and on failures of load_config_with_timeout and
apply_autonomy_settings: log an entry message when add_auto_approve_tool is
called (include tool_name and correlation fields), capture and log any error
returned from load_config_with_timeout with a stable prefix and diagnostics, log
the successful append/persist after apply_autonomy_settings completes (stable
success prefix, tool_name, and before/after state or count), and log errors from
apply_autonomy_settings with a stable error prefix and the error details;
reference symbols: add_auto_approve_tool, load_config_with_timeout,
apply_autonomy_settings, and AutonomySettingsPatch when adding these logs.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d85a6e88-8786-44f1-abd7-9ca2e511e105

📥 Commits

Reviewing files that changed from the base of the PR and between 87f8ef4 and e775491.

📒 Files selected for processing (54)
  • app/src/components/chat/ApprovalRequestCard.tsx
  • app/src/components/chat/__tests__/ApprovalRequestCard.test.tsx
  • app/src/components/settings/panels/AgentAccessPanel.tsx
  • app/src/components/settings/panels/__tests__/AgentAccessPanel.test.tsx
  • app/src/components/settings/panels/__tests__/AutonomyPanel.test.tsx
  • app/src/lib/i18n/chunks/ar-3.ts
  • app/src/lib/i18n/chunks/ar-5.ts
  • app/src/lib/i18n/chunks/bn-3.ts
  • app/src/lib/i18n/chunks/bn-5.ts
  • app/src/lib/i18n/chunks/de-3.ts
  • app/src/lib/i18n/chunks/de-5.ts
  • app/src/lib/i18n/chunks/en-3.ts
  • app/src/lib/i18n/chunks/en-5.ts
  • app/src/lib/i18n/chunks/es-3.ts
  • app/src/lib/i18n/chunks/es-5.ts
  • app/src/lib/i18n/chunks/fr-3.ts
  • app/src/lib/i18n/chunks/fr-5.ts
  • app/src/lib/i18n/chunks/hi-3.ts
  • app/src/lib/i18n/chunks/hi-5.ts
  • app/src/lib/i18n/chunks/id-3.ts
  • app/src/lib/i18n/chunks/id-5.ts
  • app/src/lib/i18n/chunks/it-3.ts
  • app/src/lib/i18n/chunks/it-5.ts
  • app/src/lib/i18n/chunks/ko-3.ts
  • app/src/lib/i18n/chunks/ko-5.ts
  • app/src/lib/i18n/chunks/pt-3.ts
  • app/src/lib/i18n/chunks/pt-5.ts
  • app/src/lib/i18n/chunks/ru-3.ts
  • app/src/lib/i18n/chunks/ru-5.ts
  • app/src/lib/i18n/chunks/zh-CN-3.ts
  • app/src/lib/i18n/chunks/zh-CN-5.ts
  • app/src/lib/i18n/en.ts
  • app/src/utils/tauriCommands/config.ts
  • src/core/jsonrpc.rs
  • src/openhuman/about_app/catalog.rs
  • src/openhuman/agent/bus.rs
  • src/openhuman/agent/harness/bughunt_tests.rs
  • src/openhuman/agent/harness/harness_gap_tests.rs
  • src/openhuman/agent/harness/test_support_test.rs
  • src/openhuman/agent/harness/tests.rs
  • src/openhuman/agent/harness/tool_loop.rs
  • src/openhuman/agent/harness/tool_loop_tests.rs
  • src/openhuman/approval/gate.rs
  • src/openhuman/approval/mod.rs
  • src/openhuman/approval/ops.rs
  • src/openhuman/approval/rpc.rs
  • src/openhuman/config/ops.rs
  • src/openhuman/config/ops_tests.rs
  • src/openhuman/config/schema/autonomy.rs
  • src/openhuman/config/schemas.rs
  • src/openhuman/security/live_policy.rs
  • src/openhuman/security/policy.rs
  • src/openhuman/security/policy_tests.rs
  • tests/json_rpc_e2e.rs
💤 Files with no reviewable changes (6)
  • src/openhuman/approval/ops.rs
  • src/openhuman/agent/harness/tests.rs
  • src/openhuman/agent/harness/harness_gap_tests.rs
  • src/openhuman/agent/bus.rs
  • src/openhuman/agent/harness/test_support_test.rs
  • src/openhuman/agent/harness/bughunt_tests.rs

Comment thread src/openhuman/config/ops.rs
@sanil-23 sanil-23 force-pushed the feat/1-consolidate-approval-gate branch from e775491 to 0b7e417 Compare May 26, 2026 17:50
@coderabbitai coderabbitai Bot added the working A PR that is being worked on by the team. label May 26, 2026
graycyrus
graycyrus previously approved these changes May 26, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

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

LGTM — clean consolidation of the approval system.

Good cleanup: the dead ApprovalManager (only ever called with None) is gone, auto_approve is now actually wired into the live ApprovalGate via SecurityPolicy, and the "Always allow" UI surface is properly gated behind the existing policy denial chain.

Key things I verified:

  • Security invariant preserved: SecurityPolicy hard-denies (forbidden paths, high-risk command classes, read-only mode) still run before the gate — the allowlist is purely prompt-suppression.
  • live_policy::install on serve boot (jsonrpc.rs): correct fix — web-chat-only cores skipped start_channels, leaving the live policy empty. The idempotency note is accurate.
  • approval_decide RPC persistence path (rpc.rs): best-effort save with graceful degradation (warn + re-prompt next time) is the right call. The gate resolves the current call regardless.
  • Frontend partial patch (AgentAccessPanel.tsx): the autoApprove field is only sent when the list itself changes — prevents a tier/folder toggle from clobbering a concurrent in-chat grant. Nice attention to detail.
  • Test serialization: all tests touching the process-global live policy properly acquire TEST_ENV_LOCK. The e2e test in tool_loop_tests.rs exercises the real seam (scripted LLM → gate → auto_approve short-circuit) within a chat context.
  • i18n: all 13 locales updated with identical English strings (acceptable for initial launch; translation follow-up is standard).

CI failures are pre-existingloopbackOauthListener.test.ts (timeout constant mismatch) and two inference/memory Rust tests. None touch approval code.

CodeRabbit's race condition note on add_auto_approve_tool is valid in theory (concurrent load-modify-save) but extremely low probability in practice — it requires two "Always allow" clicks resolving in the same ~ms window. Acceptable for v1; a follow-up mutex is fine if it ever matters.

No blocking issues. Ship it.

sanil-23 added a commit to sanil-23/openhuman that referenced this pull request May 26, 2026
Address CodeRabbit review on tinyhumansai#2706: `add_auto_approve_tool` did a
load-modify-save on the whole `auto_approve` array, so two concurrent
"Always allow" appends for different tools could each read the same list,
push their own entry, and clobber the other on save (last-write-wins).

Guard the read-modify-write with a process-local `tokio::sync::Mutex` so
concurrent appends serialize and union instead of dropping an entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sanil-23
Copy link
Copy Markdown
Contributor Author

CI note: the red checks are pre-existing failures on main, not from this PR

The failing checks (Frontend Unit Tests, Rust Core Tests + Quality, and the coverage jobs that depend on them) are already red on main — they reproduce on the latest main HEAD (87f8ef47) and standalone on this branch, and none of the failing tests are in this PR's diff:

Failing test Module In this diff?
startLoopbackOauthListener > returns null when shell bind fails utils/loopbackOauthListener no
make_openhuman_backend_forwards_unknown_hint_verbatim inference::provider::factory no
build_chat_runtime_defaults_to_openhuman_resolved_model memory::chat no

GitHub reports Frontend Unit Tests and Rust Core Tests + Quality as failure on main's own HEAD commit, so they are unrelated to this change. Rust Core Tests (Windows — secrets ACL) is the known cold-sccache 20-min timeout flake.

This PR's own checks are green: Type Check, Rust Quality (fmt + clippy), Rust Tauri Shell Tests, i18n Coverage, Build Tauri App, and E2E (Linux / Rust integration suite) (which runs the new auto_approve round-trip e2e). The approval/auto_approve tests pass locally (gate, policy from_config, config round-trips, the loop→gate→auto-approve e2e, and the FE card/panel Vitest). Once main is green again I'm happy to rebase to clear the inherited reds.

graycyrus
graycyrus previously approved these changes May 26, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

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

LGTM — clean consolidation of the approval system.

Good cleanup: the dead ApprovalManager (only ever called with None) is gone, auto_approve is now actually wired into the live ApprovalGate via SecurityPolicy, and the "Always allow" UI surface is properly gated behind the existing policy denial chain.

Key things I verified:

  • Security invariant preserved: SecurityPolicy hard-denies (forbidden paths, high-risk command classes, read-only mode) still run before the gate — the allowlist is purely prompt-suppression.
  • live_policy::install on serve boot (jsonrpc.rs): correct fix — web-chat-only cores skipped start_channels, leaving the live policy empty. The idempotency note is accurate.
  • approval_decide RPC persistence path (rpc.rs): best-effort save with graceful degradation (warn + re-prompt next time) is the right call. The gate resolves the current call regardless.
  • Frontend partial patch (AgentAccessPanel.tsx): the autoApprove field is only sent when the list itself changes — prevents a tier/folder toggle from clobbering a concurrent in-chat grant. Nice attention to detail.
  • Test serialization: all tests touching the process-global live policy properly acquire TEST_ENV_LOCK. The e2e test in tool_loop_tests.rs exercises the real seam (scripted LLM → gate → auto_approve short-circuit) within a chat context.
  • i18n: all 13 locales updated with identical English strings (acceptable for initial launch; translation follow-up is standard).

CI failures are pre-existingloopbackOauthListener.test.ts (timeout constant mismatch) and two inference/memory Rust tests. None touch approval code.

CodeRabbit's race condition note on add_auto_approve_tool is valid in theory (concurrent load-modify-save) but extremely low probability in practice — it requires two "Always allow" clicks resolving in the same ~ms window. Acceptable for v1; a follow-up mutex is fine if it ever matters.

No blocking issues. Ship it.

sanil-23 added a commit to sanil-23/openhuman that referenced this pull request May 26, 2026
Address CodeRabbit review on tinyhumansai#2706: `add_auto_approve_tool` did a
load-modify-save on the whole `auto_approve` array, so two concurrent
"Always allow" appends for different tools could each read the same list,
push their own entry, and clobber the other on save (last-write-wins).

Guard the read-modify-write with a process-local `tokio::sync::Mutex` so
concurrent appends serialize and union instead of dropping an entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sanil-23 sanil-23 force-pushed the feat/1-consolidate-approval-gate branch from cb450d7 to 8beacf8 Compare May 26, 2026 19:56
sanil-23 and others added 2 commits May 27, 2026 00:12
…w" list

Phase out the dead legacy ApprovalManager and make ApprovalGate the sole
approval path. Turn the previously-unused `autonomy.auto_approve` field into a
real, persisted "Always allow" allowlist the gate honors.

- SecurityPolicy carries `auto_approve`; ApprovalGate reads it via `live_policy`
  (falls back to boot config) and skips the prompt for listed tools.
- The "Always allow" decision (`approve_always_for_tool`) appends the tool to
  `autonomy.auto_approve` via the config save + `live_policy` reload path
  (mirrors `trusted_roots`); surfaced as a 3rd button on the approval card and
  a view/remove list in Settings -> Agent access.
- Install `live_policy` at serve boot too: `start_channels` is skipped on
  web-chat-only cores, which would otherwise leave the allowlist inert there.
- Delete ApprovalManager + ApprovalRequest/Response/LogEntry, the dead approval
  branch/param in `run_tool_call_loop`, and the unused `always_ask` field.

Policy hard-denies still run before the gate, so the allowlist can only
suppress prompts, never bypass forbidden paths / high-risk blocks.

Tests: from_config carries auto_approve; gate auto_approve short-circuit;
config apply/add_auto_approve_tool round-trips; json_rpc autonomy e2e incl.
auto_approve; loop -> gate -> auto_approve e2e (tool runs without parking);
approval card + Agent Access panel Vitest. Resolves fork tracker issue #1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address CodeRabbit review on tinyhumansai#2706: `add_auto_approve_tool` did a
load-modify-save on the whole `auto_approve` array, so two concurrent
"Always allow" appends for different tools could each read the same list,
push their own entry, and clobber the other on save (last-write-wins).

Guard the read-modify-write with a process-local `tokio::sync::Mutex` so
concurrent appends serialize and union instead of dropping an entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sanil-23 sanil-23 force-pushed the feat/1-consolidate-approval-gate branch from 8beacf8 to 0c312d4 Compare May 26, 2026 22:13
The diff-coverage gate flagged the new auto_approve allowlist UI in
AgentAccessPanel as uncovered (rendering, removeAutoApprove, and the
auto_approve persist branch). Add Vitest cases for the empty state and for
listing allow-listed tools + removing one (which persists the trimmed
auto_approve via update_autonomy_settings). Lifts the changed-line coverage
on AgentAccessPanel.tsx above the 80% gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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)
app/src/utils/tauriCommands/config.ts (1)

425-450: 💤 Low value

SearchSettings changes appear outside PR scope.

The allowed_domains and allow_all additions to SearchSettings / SearchSettingsUpdate are not mentioned in the PR summary, which focuses on tool approval (auto_approve). These changes may be part of a broader allowlist/access-control theme, or they could represent scope creep. Consider whether these belong in a separate PR focused on web access control.

The implementation itself is clean: the doc comment on lines 432–436 clearly explains the precedence ("allow_all is applied AFTER allowed_domains server-side").

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/utils/tauriCommands/config.ts` around lines 425 - 450, The PR
accidentally introduces allowlist fields outside its stated scope—remove the
added allowed_domains and allow_all properties from the SearchSettings (and any
corresponding SearchSettingsUpdate) interface changes so the tool-approval PR
only contains auto_approve work; revert any related type/usage changes you added
(references to allowed_domains, allow_all, or the doc block) and move them into
a separate focused PR about web access control with tests and API/server changes
when ready.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/utils/tauriCommands/config.ts`:
- Around line 425-450: The PR accidentally introduces allowlist fields outside
its stated scope—remove the added allowed_domains and allow_all properties from
the SearchSettings (and any corresponding SearchSettingsUpdate) interface
changes so the tool-approval PR only contains auto_approve work; revert any
related type/usage changes you added (references to allowed_domains, allow_all,
or the doc block) and move them into a separate focused PR about web access
control with tests and API/server changes when ready.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ee77d368-b3ce-40a2-b545-4a30f79a5e93

📥 Commits

Reviewing files that changed from the base of the PR and between e775491 and 79530c0.

📒 Files selected for processing (54)
  • app/src/components/chat/ApprovalRequestCard.tsx
  • app/src/components/chat/__tests__/ApprovalRequestCard.test.tsx
  • app/src/components/settings/panels/AgentAccessPanel.tsx
  • app/src/components/settings/panels/__tests__/AgentAccessPanel.test.tsx
  • app/src/components/settings/panels/__tests__/AutonomyPanel.test.tsx
  • app/src/lib/i18n/chunks/ar-3.ts
  • app/src/lib/i18n/chunks/ar-5.ts
  • app/src/lib/i18n/chunks/bn-3.ts
  • app/src/lib/i18n/chunks/bn-5.ts
  • app/src/lib/i18n/chunks/de-3.ts
  • app/src/lib/i18n/chunks/de-5.ts
  • app/src/lib/i18n/chunks/en-3.ts
  • app/src/lib/i18n/chunks/en-5.ts
  • app/src/lib/i18n/chunks/es-3.ts
  • app/src/lib/i18n/chunks/es-5.ts
  • app/src/lib/i18n/chunks/fr-3.ts
  • app/src/lib/i18n/chunks/fr-5.ts
  • app/src/lib/i18n/chunks/hi-3.ts
  • app/src/lib/i18n/chunks/hi-5.ts
  • app/src/lib/i18n/chunks/id-3.ts
  • app/src/lib/i18n/chunks/id-5.ts
  • app/src/lib/i18n/chunks/it-3.ts
  • app/src/lib/i18n/chunks/it-5.ts
  • app/src/lib/i18n/chunks/ko-3.ts
  • app/src/lib/i18n/chunks/ko-5.ts
  • app/src/lib/i18n/chunks/pt-3.ts
  • app/src/lib/i18n/chunks/pt-5.ts
  • app/src/lib/i18n/chunks/ru-3.ts
  • app/src/lib/i18n/chunks/ru-5.ts
  • app/src/lib/i18n/chunks/zh-CN-3.ts
  • app/src/lib/i18n/chunks/zh-CN-5.ts
  • app/src/lib/i18n/en.ts
  • app/src/utils/tauriCommands/config.ts
  • src/core/jsonrpc.rs
  • src/openhuman/about_app/catalog.rs
  • src/openhuman/agent/bus.rs
  • src/openhuman/agent/harness/bughunt_tests.rs
  • src/openhuman/agent/harness/harness_gap_tests.rs
  • src/openhuman/agent/harness/test_support_test.rs
  • src/openhuman/agent/harness/tests.rs
  • src/openhuman/agent/harness/tool_loop.rs
  • src/openhuman/agent/harness/tool_loop_tests.rs
  • src/openhuman/approval/gate.rs
  • src/openhuman/approval/mod.rs
  • src/openhuman/approval/ops.rs
  • src/openhuman/approval/rpc.rs
  • src/openhuman/config/ops.rs
  • src/openhuman/config/ops_tests.rs
  • src/openhuman/config/schema/autonomy.rs
  • src/openhuman/config/schemas.rs
  • src/openhuman/security/live_policy.rs
  • src/openhuman/security/policy.rs
  • src/openhuman/security/policy_tests.rs
  • tests/json_rpc_e2e.rs
💤 Files with no reviewable changes (6)
  • src/openhuman/agent/bus.rs
  • src/openhuman/agent/harness/tests.rs
  • src/openhuman/agent/harness/bughunt_tests.rs
  • src/openhuman/approval/ops.rs
  • src/openhuman/agent/harness/harness_gap_tests.rs
  • src/openhuman/agent/harness/test_support_test.rs
✅ Files skipped from review due to trivial changes (17)
  • app/src/lib/i18n/chunks/es-5.ts
  • app/src/lib/i18n/chunks/en-3.ts
  • app/src/lib/i18n/chunks/ar-5.ts
  • app/src/lib/i18n/chunks/de-5.ts
  • app/src/lib/i18n/chunks/fr-5.ts
  • app/src/lib/i18n/chunks/zh-CN-5.ts
  • app/src/lib/i18n/chunks/id-3.ts
  • app/src/lib/i18n/chunks/it-3.ts
  • app/src/components/settings/panels/tests/AutonomyPanel.test.tsx
  • src/openhuman/security/policy_tests.rs
  • app/src/lib/i18n/chunks/ru-5.ts
  • app/src/lib/i18n/chunks/pt-5.ts
  • app/src/lib/i18n/chunks/hi-5.ts
  • app/src/lib/i18n/chunks/pt-3.ts
  • app/src/lib/i18n/chunks/it-5.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/chunks/ko-5.ts
🚧 Files skipped from review as they are similar to previous changes (27)
  • app/src/lib/i18n/chunks/zh-CN-3.ts
  • app/src/lib/i18n/chunks/en-5.ts
  • app/src/components/chat/tests/ApprovalRequestCard.test.tsx
  • app/src/lib/i18n/chunks/hi-3.ts
  • src/core/jsonrpc.rs
  • app/src/lib/i18n/chunks/es-3.ts
  • app/src/lib/i18n/chunks/id-5.ts
  • app/src/lib/i18n/chunks/bn-5.ts
  • src/openhuman/approval/mod.rs
  • tests/json_rpc_e2e.rs
  • app/src/lib/i18n/chunks/de-3.ts
  • app/src/lib/i18n/chunks/ko-3.ts
  • src/openhuman/about_app/catalog.rs
  • app/src/lib/i18n/chunks/fr-3.ts
  • app/src/lib/i18n/chunks/ru-3.ts
  • app/src/components/settings/panels/AgentAccessPanel.tsx
  • src/openhuman/security/live_policy.rs
  • src/openhuman/approval/rpc.rs
  • src/openhuman/security/policy.rs
  • src/openhuman/config/schemas.rs
  • src/openhuman/agent/harness/tool_loop.rs
  • src/openhuman/config/ops.rs
  • src/openhuman/config/ops_tests.rs
  • app/src/components/chat/ApprovalRequestCard.tsx
  • src/openhuman/approval/gate.rs
  • src/openhuman/config/schema/autonomy.rs
  • src/openhuman/agent/harness/tool_loop_tests.rs

@sanil-23 sanil-23 merged commit 0fddf11 into tinyhumansai:main May 27, 2026
39 of 41 checks passed
YellowSnnowmann added a commit to YellowSnnowmann/openhuman that referenced this pull request May 27, 2026
The `always_ask` field was removed from `AutonomyConfig` in tinyhumansai#2706
(ApprovalGate consolidation). Migration tests still referenced it,
causing compilation failures in CI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants