feat: emit audit action hook on capability-denied tool calls (#412 Stage D)#419
Open
chubes4 wants to merge 1 commit into
Open
feat: emit audit action hook on capability-denied tool calls (#412 Stage D)#419chubes4 wants to merge 1 commit into
chubes4 wants to merge 1 commit into
Conversation
…tic#412 Stage D) Fire the `agents_api_tool_capability_denied` action at the moment an ability-backed tool call is denied on capability grounds, so hosts can log, telemeter, or alert on ceiling-denied tool calls. The substrate emits the event only and owns no storage, logger, or telemetry backend. The payload is JSON-friendly and redaction-safe: it reuses the safe principal metadata from `to_safe_metadata()` and the already-redacted ability parameters from `redacted_parameters()`, and never carries raw parameters, secrets, or unredacted principal fields. The hook is a pure notification that does not alter control flow or the returned tool result, and is safe when no listener is attached. Closes Automattic#412 (Stage d — audit/telemetry).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #412 (Stage d — audit/telemetry).
Summary
This is the last remaining part of #412. Stages (a)/(b)/(c) merged in #413 and #414, which added the capability ceiling and the
capability_deniedtool-error result. This PR adds the audit/telemetry surface: a pure notification action hook that fires at the moment a tool call is denied on capability grounds, so hosts can log, telemeter, or alert on ceiling-denied tool calls — without the substrate owning any storage, logger, or telemetry backend.What changed
In
WP_Agent_Ability_Tool_Executor::authorize_required_capability(), right before the existing denial error is returned (after$allowedis knownfalse), emit:The method then returns the same denial error as before. The hook does not alter control flow or the returned result.
Hook name
agents_api_tool_capability_denied— follows the substrate's existingagents_api_*action convention (cf.agents_api_runtime_tool_request_created,agents_api_ability_executed,agents_api_loop_event), matching the repo's past-tense / noun-event naming.Payload contract
The
$denial_event(first arg) is JSON-friendly and redaction-safe:schema_version1operationtool_executiontool_nameability_namerequired_capabilityreasoncapability_not_permitted(principal present but ceiling/user denied) orprincipal_unavailable(no principal threaded — fail closed)principalWP_Agent_Execution_Principal::to_safe_metadata(), ornullwhen no principal was threadedparametersWP_Agent_Ability_Dispatcher::redacted_parameters()parameters_redactedtrueThe second arg is the caller-owned
$contextarray, passed through unchanged (same convention as the runtime-tool lifecycle actions).Redaction guarantees
to_safe_metadata(), which intentionally omits token ids, owner keys, request metadata, audience claims, capability details, and cryptographic binding claims. Hosts needing richer audit trails should persist the fullto_array()shape in private storage they control.redacted_parameters(), which redacts via JSON-schema annotations, ability metadata, and a conservative key-name fallback (api_key,authorization,token,secret,password,cookie,credential,nonce, …). Raw parameter values never appear in the event.Layer purity
The substrate emits the event only. It does not add a table, a logger, a telemetry backend, or any storage. No consumer-specific concepts are referenced. This is a generic notification surface; hosts own what happens next.
Success-side symmetry — deliberately not added
A symmetric
agents_api_tool_capability_allowedevent was considered but intentionally omitted. Rationale:agents_api_ability_executedfires post-execute for every ability. Adding a redundant allowed-event at the capability-check layer would duplicate that signal.If a future need for per-capability-check success telemetry emerges, it can be added then without a parallel system. Shipping only the denial event keeps this change minimal and focused.
Refactor note
redacted_parameters()is now computed once into a local and reused in both the event payload and the returned denial error (previously it was called inline only for the error). This is behavior-preserving — the returned error is identical — and avoids double redaction work.Verification
tests/ability-tool-capability-denied-event-smoke.php(42 assertions), wired intocomposer smoke:tool_name/ability_name/required_capability/reason, principal safe-metadata present, sensitiveapi_keyparameter redacted to[redacted], non-sensitivedraftpreserved, raw secret never present in the JSON-encoded payload, context passed through).principal_unavailablepath fires the event withprincipalset tonull.do_action; result unchanged).ability-tool-ceiling-smoke.php, which runs the denial path without attaching a listener.composer smoke— green (exit 0).composer phpstan(level: max, memory-limit 2G) —[OK] No errors.docs/runtime-and-tools.mddocumenting the hook name, payload contract, and redaction guarantees, matching the existing hook-documentation style.Definition of done
A layer-pure, redaction-safe audit action hook fires on every capability-denied tool call; no storage added to the substrate; returned result unchanged; smoke test + docs cover it; phpstan clean.
Do not merge/release/deploy — PR only.