Skip to content

feat: human-in-the-loop tool approvals + network-capable shell - #52

Merged
jasonli0226 merged 10 commits into
mainfrom
feat/tool-approval-and-shell_net
Jul 20, 2026
Merged

feat: human-in-the-loop tool approvals + network-capable shell#52
jasonli0226 merged 10 commits into
mainfrom
feat/tool-approval-and-shell_net

Conversation

@jasonli0226

@jasonli0226 jasonli0226 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds two related capabilities to Clawix:

  1. Human-in-the-loop tool approval system — agents can be gated so specific tool calls require explicit user approval before executing. Approval requests are delivered over web dashboard and Telegram, held at a rendezvous gate until the user allows or denies.
  2. Network-capable shell tool (shell_net) — a shell runner with an outbound egress firewall, so agents can run network-dependent commands under a per-container allow/deny policy.

Details

Approvals

  • New ApprovalRequest / UserToolApproval models + repositories.
  • Rendezvous gate with domain types and frame delivery; engine guard enforced at the ToolRegistry chokepoint so nothing bypasses it.
  • Delivery channels: Telegram and web dashboard.
  • Web dashboard surfaces: approval card, notification bell, pending-approval banner, dedicated tool-approvals page.
  • Session approvals cleared on reset; seed reset gated.
  • Per-tool "ask before use" approval overrides in MCP tiers.

Network shell + egress

  • shell_net tool with policy caps and a dedicated runner image.
  • Per-container egress firewall for network runners.

Agent provider policy

  • Provider policy enforced on agent definitions and spawns.

…sitories

Persistence layer for tool-call approvals: the ApprovalRequest (one row per non-memory gate hit) and UserToolApproval (standing always-scope allow/deny) models, the TOOL_APPROVAL notification type, and the MCP annotation/approval-override columns. Adds repositories for both tables and registers them in DbModule.
Core of the tool-approval system:
- ToolApprovalService rendezvous gate with resolve/revoke/timeout/abort and a
  restart sweep; precedence lookup over standing, session, and per-run memory.
- Domain types (offered-scope/downgrade rules), params redaction, and metrics.
- REST controllers for resolve/revoke/pending and standing approvals.
- Pub/sub approval-frame delivery keyed off cache.constants.
- Tool interface gains requiresApproval/approvalScope/resourceKey fields; adds
  the run-activity module the gate reads.
…er image

Adds an ephemeral, network-capable shell tool gated by policy.allowShellNet:
- shell_net tool and a dedicated ShellNetConcurrencyLimiter, with per-policy
  memory/timeout/cpu/concurrency caps (maxShellNet* columns).
- shell tool sensitive-command classification so risky invocations can be gated.
- shell-net-runner image (curl/wget/git/ssh + writable HOME).
- Unit, concurrency, classification, and live integration coverage.
Boots the network-capable runners behind an L3 egress filter:
- egress-firewall.sh blocks private/metadata/gateway ranges (SSRF) and drops to
  an unprivileged user via gosu at container start; python-runner self-firewalls
  on boot with a dual-mode entrypoint.
- container-runner gains an egressFirewall start flag and exec-user option; the
  no-network path is unchanged. ExecOptions/egress types live in shared.
- python_run_net now runs behind the firewall; compose files, .env.example, and
  the image build scripts updated for the new build context. Integration test
  proves private/metadata/gateway blocked while public + DNS + allowlist work.
Enforces approvals where every tool call funnels through:
- ToolRegistry gates execute() behind ToolApprovalService with registration-time
  scope validation; fails closed when an approval-requiring tool runs without an
  approval context. Per-action predicates keep read-only actions exempt (cron
  gates only mutating actions).
- Privileged built-ins (cron, wiki share/unshare/delete, browser) annotated with
  approval scopes; MCP tools derive ask-by-default from captured annotations plus
  per-tool overrides.
- Approval context wired into agent runs. Full integration coverage.
Routes approval frames to the surface the user is on:
- Telegram inline-keyboard prompts with callback resolution; the tracked prompt
  is edited when tapped, and updates are processed concurrently (@grammyjs/runner)
  so approval taps arrive promptly.
- Web WS approval frames over the channel protocol.
- Channel-manager routing plus module wiring.
Delivers approvals across the dashboard UI:
- In-chat approval card with honest offered-scope buttons, wired through use-chat
  frame handling and the conversations page.
- Dashboard-wide pending-approval banner (counts only still-pending) and inline
  resolve from the notification bell.
- Standing-approvals management page.
- Approval frame parsing/labels helpers, reduced-motion support, and the
  notifications API client.
- Session reset revokes pending approvals and clears per-session approval memory;
  ApprovalsModule wired into the app module. Reset-command and e2e coverage.
- Make the destructive seed reset opt-in via SEED_RESET so a normal seed no
  longer wipes existing data.
Lets operators mark MCP tools as ask-before-use per connection:
- mcp.service/controller expose and persist sparse approvalOverrides
  ({ [toolName]: 'ask' | 'allow' }); tier-utils and mcp-client capture tool
  annotations at discovery. Schema and validation updated in shared.
- Tiers tab gains an ask-before-use column; web mcp client wired through.
Gate agent providers against the owner's Policy.allowedProviders:
- agents.service rejects creating/updating an agent that names an out-of-policy
  provider, and gates sub-agent spawns against the spawning user's policy.
- agent-definition.repository resolves a policy-permitted default worker
  (falling back to the first allowed, configured provider) so anonymous spawns
  keep working without forcing the org default into every policy.
- spawn tool honours the policy path. Unit coverage across all three.
@jasonli0226
jasonli0226 merged commit 86fafd7 into main Jul 20, 2026
1 check 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.

2 participants