Skip to content

fix(agent-harness): structured policy-block messages — rework of #4390 onto post-#4399#4443

Merged
senamakel merged 1 commit into
tinyhumansai:mainfrom
M3gA-Mind:fix/GH-4094-policy-block-messages-rework
Jul 3, 2026
Merged

fix(agent-harness): structured policy-block messages — rework of #4390 onto post-#4399#4443
senamakel merged 1 commit into
tinyhumansai:mainfrom
M3gA-Mind:fix/GH-4094-policy-block-messages-rework

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Rework of #4390 onto post-#4399 main. When a tool call is blocked at a policy/permission boundary, the agent now gets a structured Blocked / Reason / Workaround / relay-to-user message instead of a bare denial line — so it relays the block and offers a concrete next step rather than dead-ending.

Why a rework (not a rebase)

#4399 (the TinyAgents harness migration) deleted session/agent_tool_exec.rs and session/policy_denial.rs where the original #4390 lived, and relocated the tool policy gate onto the tinyagents adapter middleware (ToolPolicyMiddleware in src/openhuman/tinyagents/middleware.rs), which still emitted the bare denial strings. So #4390 can't rebase — it's re-expressed against the current seam.

Changes

  • New src/openhuman/tinyagents/policy_denial.rs — ports the deleted renderer: a PolicyDenial enum (SessionForbidden / PermissionTooLow / PolicyDenied / ApprovalRequired) with render() producing Blocked: … Reason: … Workaround: … Relay this to the user: …. Permission-tier denials point at the real knob ([autonomy] / config.update_autonomy_settings, channel agent-access tier), not a vague "increase permissions".
  • ToolPolicyMiddleware — the three bare format! denials (session-forbidden + per-call permission-too-low in channel_permission_block, and the pluggable-policy Deny/RequireApproval branch in wrap_tool) now call PolicyDenial::render().

Tests

5 unit tests for the renderer (all four variants + the with/without-required session-forbidden paths). cargo test --lib policy_denial green.

Related

Summary by CodeRabbit

  • New Features

    • Tool-blocking messages are now clearer and more consistent, with actionable guidance on what happened, why it was blocked, and what to do next.
    • Approval-required cases now give a more helpful prompt for retrying after approval.
  • Bug Fixes

    • Improved the wording and structure of permission-denied responses so they’re easier to understand and relay.
    • Added coverage to ensure denial messages include the expected guidance.

@M3gA-Mind M3gA-Mind requested a review from a team July 3, 2026 14:18
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new PolicyDenial enum with a render() method in a new policy_denial module to produce structured tool/permission denial messages (blocked reason, workaround, relay instruction). ToolPolicyMiddleware in middleware.rs is updated to construct and render PolicyDenial variants instead of ad-hoc formatted strings.

Changes

PolicyDenial structured messaging

Layer / File(s) Summary
PolicyDenial enum and render logic
src/openhuman/tinyagents/policy_denial.rs
New PolicyDenial<'a> enum with variants for session-forbidden, permission-too-low, policy-denied, and approval-required cases; render() formats blocked/reason/workaround plus a relay instruction; raise_tier_workaround helper and unit tests included.
Module registration
src/openhuman/tinyagents/mod.rs
Declares the new policy_denial submodule.
Middleware integration
src/openhuman/tinyagents/middleware.rs
Imports PolicyDenial and updates channel_permission_block and wrap_tool to build and render PolicyDenial variants instead of inline formatted strings.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant ToolPolicyMiddleware
  participant PolicyDenial

  Caller->>ToolPolicyMiddleware: invoke tool call
  ToolPolicyMiddleware->>ToolPolicyMiddleware: evaluate policy/permission
  alt session forbidden or permission too low
    ToolPolicyMiddleware->>PolicyDenial: construct SessionForbidden/PermissionTooLow
  else require approval or policy denied
    ToolPolicyMiddleware->>PolicyDenial: construct ApprovalRequired/PolicyDenied
  end
  PolicyDenial->>PolicyDenial: render()
  PolicyDenial-->>ToolPolicyMiddleware: formatted denial string
  ToolPolicyMiddleware-->>Caller: denial content
Loading

Possibly related PRs

  • tinyhumansai/openhuman#4372: Both PRs modify ToolPolicyMiddleware's policy-blocking/denial handling in middleware.rs, with this PR building its PolicyDenial rendering on that enforcement logic.

Suggested labels: agent, bug

Poem

A denial once dry, now speaks with care,
"Blocked, but here's a workaround" — fair!
With reasons and tiers all lined in a row,
PolicyDenial helps the agent to know.
🐇 Hop along, message relayed with grace!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR does not address #4442's build break fixes for SqlRunLedgerCheckpointer, graph_fn, or the stale doc link. Implement the orchestration build fix: swap to SqliteCheckpointer, wrap the two graph_fn call sites in Some(...), and fix the stale intra-doc link.
Out of Scope Changes check ⚠️ Warning All changes add policy-denial rendering and middleware wiring, which are unrelated to the #4442 build-fix scope. Remove the policy-denial refactor from this PR or retarget it to the issue that requires structured policy-block messages.
✅ Passed checks (3 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 policy-block message refactor shown in the diff.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e6ba30d5d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +137 to +139
"Raise the '{channel}' channel's agent-access tier to at least {required} \
(Settings → Agent access, or the `config.update_autonomy_settings` RPC / \
`[autonomy]` config), or accomplish the goal with a tool that needs only \

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 Badge Point permission denials at the channel permission knob

When a tool is blocked by the channel permission ceiling, this workaround sends the user to config.update_autonomy_settings / [autonomy], but the ceiling being enforced here comes from AgentConfig.channel_permissions via ToolPolicyEngine::build_session, not the autonomy access-mode config. In a read-only channel, a user can follow this instruction and switch readonly/supervised/full without changing the denial; they need the channel permission entry raised instead, so the new actionable message is misleading for exactly the permission-tier blocks it is meant to recover from.

Useful? React with 👍 / 👎.

Rework of tinyhumansai#4390 onto post-tinyhumansai#4399 code. tinyhumansai#4399 relocated the tool policy gate
from the deleted session/agent_tool_exec.rs to the tinyagents adapter
middleware (ToolPolicyMiddleware), which still emitted bare denial lines.

- Ports the deleted policy_denial.rs into the adapter seam as
  src/openhuman/tinyagents/policy_denial.rs: a `PolicyDenial` renderer that
  turns each denial into a structured `Blocked / Reason / Workaround / relay`
  message (with the concrete "raise the channel's agent-access tier" or
  "permitted alternative / ask for approval" workaround) so the agent relays
  the block to the user instead of dead-ending.
- Swaps the three bare `format!` denials in ToolPolicyMiddleware
  (channel_permission_block session-forbidden + per-call permission-too-low,
  and the pluggable-policy Deny/RequireApproval branch in wrap_tool) for
  PolicyDenial::render().

5 unit tests for the renderer (session-forbidden with/without required,
permission-too-low, policy-denied, approval-required).
@M3gA-Mind M3gA-Mind force-pushed the fix/GH-4094-policy-block-messages-rework branch from 3e6ba30 to 65cbd12 Compare July 3, 2026 15:15
@coderabbitai coderabbitai Bot added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug labels Jul 3, 2026

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
src/openhuman/tinyagents/middleware.rs (1)

1344-1356: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider matching Deny explicitly instead of _.

The blocked_action match just above (Line 1326) explicitly lists RequireApproval, Deny, and Allow. The content match here falls back to _ => PolicyDenial::PolicyDenied for anything that isn't RequireApproval. Functionally correct today (only Deny can reach this branch), but if ToolPolicyDecision ever gains a new blocking variant, it would silently render as PolicyDenied without an explicit design decision.

♻️ Optional: explicit match arm
             let content = match &decision {
                 ToolPolicyDecision::RequireApproval { .. } => PolicyDenial::ApprovalRequired {
                     tool: &call.name,
                     policy: self.policy.name(),
                     reason,
                 },
-                _ => PolicyDenial::PolicyDenied {
+                ToolPolicyDecision::Deny { .. } | ToolPolicyDecision::Allow => PolicyDenial::PolicyDenied {
                     tool: &call.name,
                     policy: self.policy.name(),
                     reason,
                 },
             }
             .render();
🤖 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/tinyagents/middleware.rs` around lines 1344 - 1356, The
`content` match in `middleware.rs` is too broad by using `_` for all
non-`RequireApproval` cases, which hides the intended handling for blocking
decisions. Update the match on `decision` to explicitly handle
`ToolPolicyDecision::Deny` alongside `ToolPolicyDecision::RequireApproval`, and
keep the `PolicyDenial::PolicyDenied` rendering only for that explicit deny
case. Use the nearby `blocked_action` match and the `PolicyDenial` construction
in this branch as the reference points while preserving the existing behavior
for `RequireApproval`.
🤖 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 `@src/openhuman/tinyagents/middleware.rs`:
- Around line 1344-1356: The `content` match in `middleware.rs` is too broad by
using `_` for all non-`RequireApproval` cases, which hides the intended handling
for blocking decisions. Update the match on `decision` to explicitly handle
`ToolPolicyDecision::Deny` alongside `ToolPolicyDecision::RequireApproval`, and
keep the `PolicyDenial::PolicyDenied` rendering only for that explicit deny
case. Use the nearby `blocked_action` match and the `PolicyDenial` construction
in this branch as the reference points while preserving the existing behavior
for `RequireApproval`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 76cebb7b-2a60-4721-95ae-f2970e116956

📥 Commits

Reviewing files that changed from the base of the PR and between 8aab529 and 65cbd12.

📒 Files selected for processing (3)
  • src/openhuman/tinyagents/middleware.rs
  • src/openhuman/tinyagents/mod.rs
  • src/openhuman/tinyagents/policy_denial.rs

@senamakel senamakel merged commit 1fd9093 into tinyhumansai:main Jul 3, 2026
16 of 23 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Team Openhuman Jul 3, 2026
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/. bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants