diff --git a/inc/Engine/AI/Actions/ResolvePendingActionAbility.php b/inc/Engine/AI/Actions/ResolvePendingActionAbility.php index cb98e7e90..f0b8c3aa1 100644 --- a/inc/Engine/AI/Actions/ResolvePendingActionAbility.php +++ b/inc/Engine/AI/Actions/ResolvePendingActionAbility.php @@ -24,7 +24,7 @@ * array on 'accepted'. Return value is included in the * response. Return a WP_Error or an array with `success=>false` * to surface failure. - * - can_resolve (callable, optional): invoked with ($payload, $decision, $user_id) + * - can_resolve (callable, optional): invoked with ($payload, $decision, $user_id, $context) * before apply. Must return true. Return a WP_Error (or false) * to deny. Defaults to "anyone with access to the ability". * @@ -226,6 +226,7 @@ public static function execute( array $input ): array { $resolver_payload = isset( $input['payload'] ) && is_array( $input['payload'] ) ? $input['payload'] : array(); $resolver_context = isset( $input['context'] ) && is_array( $input['context'] ) ? $input['context'] : array(); $resolver = isset( $input['resolver'] ) ? sanitize_text_field( $input['resolver'] ) : self::resolverFromCurrentUser(); + $resolver_context = array_merge( $resolver_context, array( 'resolver' => $resolver ) ); $pending_action = PendingActionStore::get_action( $action_id ); if ( '' === $kind ) { @@ -280,7 +281,7 @@ public static function execute( array $input ): array { } if ( null === $contract_allowed && ! empty( $handler['can_resolve'] ) && is_callable( $handler['can_resolve'] ) ) { - $allowed = call_user_func( $handler['can_resolve'], $payload, $decision_value, $user_id ); + $allowed = call_user_func( $handler['can_resolve'], $payload, $decision_value, $user_id, $resolver_context ); if ( is_wp_error( $allowed ) ) { return array( 'success' => false, @@ -359,7 +360,7 @@ private static function getKindHandlers(): array { * * array( * 'apply' => callable ( array $apply_input, array $payload ): mixed, - * 'can_resolve' => callable ( array $payload, string $decision, int $user_id ): bool|WP_Error, + * 'can_resolve' => callable ( array $payload, string $decision, int $user_id, array $context ): bool|WP_Error, * ) * * @since 0.72.0 diff --git a/tests/pending-action-resolver-contract-smoke.php b/tests/pending-action-resolver-contract-smoke.php index f2d565913..586c69958 100644 --- a/tests/pending-action-resolver-contract-smoke.php +++ b/tests/pending-action-resolver-contract-smoke.php @@ -238,15 +238,20 @@ static function ( array $handlers ) use ( $handler, &$permission_seen ) { resolver_smoke_assert( empty( $permission_seen ), 'legacy can_resolve is not duplicated for Agents API handler objects', $failures, $passes ); $legacy_apply_calls = 0; +$legacy_permission_seen = array(); add_filter( 'datamachine_pending_action_handlers', - static function ( array $handlers ) use ( &$legacy_apply_calls ) { + static function ( array $handlers ) use ( &$legacy_apply_calls, &$legacy_permission_seen ) { $handlers['legacy_kind'] = array( - 'apply' => static function ( array $apply_input, array $payload ) use ( &$legacy_apply_calls ) { + 'apply' => static function ( array $apply_input, array $payload ) use ( &$legacy_apply_calls ) { unset( $apply_input, $payload ); ++$legacy_apply_calls; return array( 'success' => true ); }, + 'can_resolve' => static function ( array $payload, string $decision, int $user_id, array $context ) use ( &$legacy_permission_seen ) { + $legacy_permission_seen[] = compact( 'payload', 'decision', 'user_id', 'context' ); + return true; + }, ); return $handlers; @@ -268,11 +273,15 @@ static function ( array $handlers ) use ( &$legacy_apply_calls ) { array( 'action_id' => 'act_legacy_reject', 'decision' => 'rejected', + 'resolver' => 'system:test-resolver', + 'context' => array( 'reason' => 'safe-test' ), ) ); resolver_smoke_assert( true === ( $rejected['success'] ?? false ), 'rejected legacy resolution succeeds', $failures, $passes ); resolver_smoke_assert( 'rejected' === ( $rejected['decision'] ?? null ), 'rejected response keeps Data Machine decision string', $failures, $passes ); +resolver_smoke_assert( 'safe-test' === ( $legacy_permission_seen[0]['context']['reason'] ?? null ), 'legacy can_resolve receives resolver context', $failures, $passes ); +resolver_smoke_assert( 'system:test-resolver' === ( $legacy_permission_seen[0]['context']['resolver'] ?? null ), 'legacy can_resolve receives resolver identity', $failures, $passes ); resolver_smoke_assert( 0 === $legacy_apply_calls, 'rejected resolution does not invoke apply handler', $failures, $passes ); $denied_apply_calls = 0;