Skip to content

feat(hooks): let hooks participate in the approval decision (blocking PermissionRequest, or an "allow" decision on PreToolUse) #1326

Description

@rullerzhou-afk

Context

I build Clawd, a desktop pet that visualizes coding-agent activity and surfaces permission prompts as desktop bubbles. I recently integrated Kimi Code's hooks (0.22.0) and want to say the permission events are genuinely well designed: PermissionRequest fires exactly when the approval panel blocks (no guessing from PreToolUse timing), the display block carries the real command and cwd for UI to show, and PermissionResult makes "the user answered" a clean, immediate signal. My integration uses all three and they work great for notifying.

The gap

External UIs can only observe an approval — they cannot answer it:

  • PermissionRequest is fired via fireAndForgetTrigger (packages/agent-core/src/agent/permission/index.ts), so nothing a hook returns is read; the decision lives exclusively inside the TUI reverse-RPC approval flow.
  • PreToolUse is blockable, but only with deny semantics (exit 2 / permissionDecision: "deny"). An allow simply means "this hook doesn't object" — the permission policy chain continues and the TUI panel still pops. So a hook-driven UI can implement "reject from the bubble", but never "approve from the bubble"; users would face a double prompt.

This blocks a whole class of integrations: one-click approval from a desktop companion, remote/mobile approval (approve from your phone while away from the keyboard), or an org-side policy gateway that auto-approves vetted commands.

I'm aware the daemon/server modes (kimi server / kimi web) expose an approval broker over WS/REST for Kimi's own web and desktop frontends — but that only covers sessions running under those modes. The interactive TUI is a single process (the SDK RPC pair is in-memory), so for people who run kimi in a terminal — which is who a companion tool sees — hooks are the only integration surface, and asking them to switch how they launch Kimi just so an external tool can answer approvals isn't realistic.

Proposal

Either of these would unlock it (option A feels cleanest):

A. A blocking variant of PermissionRequest. Run it through the blocking path (like Stop/PreToolUse) before falling back to the TUI panel. A hook that prints a structured decision, e.g.

{ "hookSpecificOutput": { "permissionDecision": "approve", "scope": "once" } }

resolves the approval; no output / exit 0 / timeout falls through to the normal TUI panel. That keeps it fully backward compatible — every existing PermissionRequest hook today exits without a decision and would behave exactly as before — and preserves the fail-open philosophy (hook crash → TUI panel, never a hang or an implicit approval).

B. An "allow" decision on PreToolUse. Today only "deny" is honored; honoring "allow" as "skip the ask, treat as approved" (short-circuiting the ask policy the way a session approval rule does) would achieve the same with the existing event, at the cost of deciding before the approval context (action/display) exists.

Prior art in the ecosystem: Claude Code's PreToolUse hooks accept allow/deny/ask decisions, and Codex's PermissionRequest hook is blocking with a decision output — third-party approval surfaces (remote approval bots, desktop companions) are built on exactly this. Kimi Code already has the cleanest event model of the three; this is the one missing piece.

I'd be happy to contribute a PR for option A if the direction sounds acceptable to you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions