diff --git a/src/Channels/register-agents-chat-ability.php b/src/Channels/register-agents-chat-ability.php index 65cf10f..551c4d3 100644 --- a/src/Channels/register-agents-chat-ability.php +++ b/src/Channels/register-agents-chat-ability.php @@ -383,52 +383,75 @@ function agents_chat_input_schema(): array { 'type' => 'object', 'description' => 'Optional transport-level context describing where this turn originated. Hosts may include opaque, product-owned metadata; Agents API preserves it but does not define product semantics.', 'properties' => array( - 'source' => array( + 'source' => array( 'type' => 'string', 'enum' => array( 'channel', 'bridge', 'rest', 'block', 'peer-agent', 'jsonrpc' ), 'description' => 'How the request reached this dispatcher.', ), - 'client_name' => array( + 'client_name' => array( 'type' => 'string', 'description' => 'Specific client identifier within the source (e.g. "cli-relay" or "messaging-bot").', ), - 'connector_id' => array( + 'connector_id' => array( 'type' => 'string', 'description' => 'Stable connector or channel instance id used for settings, attribution, and external conversation session mapping.', ), - 'external_provider' => array( + 'external_provider' => array( 'type' => array( 'string', 'null' ), 'description' => 'External network identifier (e.g. "whatsapp", "slack", "email"). Null if not applicable.', ), - 'external_conversation_id' => array( + 'external_conversation_id' => array( 'type' => array( 'string', 'null' ), 'description' => 'Opaque external conversation id (chat JID, channel id, thread root). Null if the source has no per-conversation isolation.', ), - 'external_message_id' => array( + 'external_message_id' => array( 'type' => array( 'string', 'null' ), 'description' => 'Stable transport-side message id, used for reply threading / dedup / audit.', ), - 'sender_id' => array( + 'sender_id' => array( 'type' => array( 'string', 'null' ), 'description' => 'Opaque external sender id. In group rooms this identifies the human sender inside the conversation.', ), - 'room_kind' => array( + 'room_kind' => array( 'type' => array( 'string', 'null' ), 'enum' => array( 'dm', 'group', 'channel' ), 'description' => 'Conversation kind: direct message, multi-participant group, broadcast channel. Null when the source has no notion of room kind.', ), - 'caller_agent' => array( + 'caller_agent' => array( 'type' => array( 'string', 'null' ), 'description' => 'Agent slug that initiated this turn when source is peer-agent. Null when the source is not another agent.', ), - 'caller_session_id' => array( + 'caller_session_id' => array( 'type' => array( 'string', 'null' ), 'description' => 'Originating agent session id when source is peer-agent. Null when unavailable or not applicable.', ), - 'peer_agent_call' => array( + 'peer_agent_call' => array( 'type' => 'boolean', 'description' => 'Whether this turn is an explicit agent-to-agent delegation call.', ), + 'runtime_tools' => array( + 'type' => 'object', + 'description' => 'Explicit runtime-local tool declarations supplied by a trusted caller for this turn.', + 'additionalProperties' => array( 'type' => 'object' ), + ), + 'runtime_tool_declarations' => array( + 'type' => 'object', + 'description' => 'Alias for explicit runtime-local tool declarations supplied by a trusted caller for this turn.', + 'additionalProperties' => array( 'type' => 'object' ), + ), + 'tool_declarations' => array( + 'type' => 'object', + 'description' => 'Transport-level tool declarations supplied by a trusted caller for this turn.', + 'additionalProperties' => array( 'type' => 'object' ), + ), + 'runtime_tool_callback' => array( + 'type' => 'string', + 'description' => 'Runtime-local callback identifier for executing runtime tool calls in trusted in-process callers.', + ), + 'runtime_tool_timeout' => array( + 'type' => 'integer', + 'description' => 'Runtime-local tool timeout in seconds.', + ), ), ), ), diff --git a/tests/ability-meta-abilities-smoke.php b/tests/ability-meta-abilities-smoke.php index dd1f395..7d55872 100644 --- a/tests/ability-meta-abilities-smoke.php +++ b/tests/ability-meta-abilities-smoke.php @@ -11,32 +11,38 @@ define( 'ABSPATH', __DIR__ . '/' ); } -class WP_Error { - public function __construct( private string $code = '', private string $message = '', private $data = null ) {} - public function get_error_code(): string { return $this->code; } - public function get_error_message(): string { return $this->message; } - public function get_error_data() { return $this->data; } +if ( ! class_exists( 'WP_Error' ) ) { + class WP_Error { + public function __construct( private string $code = '', private string $message = '', private $data = null ) {} + public function get_error_code(): string { return $this->code; } + public function get_error_message(): string { return $this->message; } + public function get_error_data() { return $this->data; } + } } -class WP_Ability_Category {} - -class WP_Ability { - public function __construct( private string $name, private array $args ) {} - public function get_name(): string { return $this->name; } - public function get_label(): string { return (string) ( $this->args['label'] ?? '' ); } - public function get_description(): string { return (string) ( $this->args['description'] ?? '' ); } - public function get_category(): string { return (string) ( $this->args['category'] ?? '' ); } - public function get_input_schema(): array { return isset( $this->args['input_schema'] ) && is_array( $this->args['input_schema'] ) ? $this->args['input_schema'] : array(); } - public function get_output_schema(): array { return isset( $this->args['output_schema'] ) && is_array( $this->args['output_schema'] ) ? $this->args['output_schema'] : array(); } - public function get_meta_item( string $key, $default = null ) { return $this->args['meta'][ $key ] ?? $default; } - public function execute( $input = null ) { - $permission = $this->args['permission_callback'] ?? null; - if ( is_callable( $permission ) && true !== call_user_func( $permission, is_array( $input ) ? $input : array() ) ) { - return new WP_Error( 'ability_invalid_permissions', 'Permission denied.' ); - } +if ( ! class_exists( 'WP_Ability_Category' ) ) { + class WP_Ability_Category {} +} - $callback = $this->args['execute_callback'] ?? null; - return is_callable( $callback ) ? call_user_func( $callback, is_array( $input ) ? $input : array() ) : null; +if ( ! class_exists( 'WP_Ability' ) ) { + class WP_Ability { + public function __construct( private string $name, private array $args ) {} + public function get_name(): string { return $this->name; } + public function get_label(): string { return (string) ( $this->args['label'] ?? '' ); } + public function get_description(): string { return (string) ( $this->args['description'] ?? '' ); } + public function get_category(): string { return (string) ( $this->args['category'] ?? '' ); } + public function get_input_schema(): array { return isset( $this->args['input_schema'] ) && is_array( $this->args['input_schema'] ) ? $this->args['input_schema'] : array(); } + public function get_output_schema(): array { return isset( $this->args['output_schema'] ) && is_array( $this->args['output_schema'] ) ? $this->args['output_schema'] : array(); } + public function get_meta_item( string $key, $default = null ) { return $this->args['meta'][ $key ] ?? $default; } + public function execute( $input = null ) { + $permission = $this->args['permission_callback'] ?? null; + if ( is_callable( $permission ) && true !== call_user_func( $permission, is_array( $input ) ? $input : array() ) ) { + return new WP_Error( 'ability_invalid_permissions', 'Permission denied.' ); + } + + $callback = $this->args['execute_callback'] ?? null; + return is_callable( $callback ) ? call_user_func( $callback, is_array( $input ) ? $input : array() ) : null; + } } } @@ -51,83 +57,139 @@ public function execute( $input = null ) { $GLOBALS['__agents_api_smoke_ability_categories'] = array(); $GLOBALS['__agents_api_smoke_can'] = true; -function current_user_can( string $capability ): bool { - unset( $capability ); - return (bool) $GLOBALS['__agents_api_smoke_can']; +if ( function_exists( 'current_user_can' ) ) { + add_filter( + 'user_has_cap', + static function ( array $allcaps ): array { + $allcaps['manage_options'] = (bool) $GLOBALS['__agents_api_smoke_can']; + return $allcaps; + } + ); +} else { + function current_user_can( string $capability ): bool { + unset( $capability ); + return (bool) $GLOBALS['__agents_api_smoke_can']; + } } -function wp_has_ability_category( string $slug ): bool { - return isset( $GLOBALS['__agents_api_smoke_ability_categories'][ $slug ] ); +if ( ! function_exists( 'wp_has_ability_category' ) ) { + function wp_has_ability_category( string $slug ): bool { + return isset( $GLOBALS['__agents_api_smoke_ability_categories'][ $slug ] ); + } } -function wp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category { - $GLOBALS['__agents_api_smoke_ability_categories'][ $slug ] = $args; - return null; +if ( ! function_exists( 'wp_register_ability_category' ) ) { + function wp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category { + $GLOBALS['__agents_api_smoke_ability_categories'][ $slug ] = $args; + return null; + } } -function wp_has_ability( string $name ): bool { - return isset( $GLOBALS['__agents_api_smoke_abilities'][ $name ] ); +if ( ! function_exists( 'wp_has_ability' ) ) { + function wp_has_ability( string $name ): bool { + return isset( $GLOBALS['__agents_api_smoke_abilities'][ $name ] ); + } } -function wp_register_ability( string $name, array $args ): ?WP_Ability { - $ability = new WP_Ability( $name, $args ); - $GLOBALS['__agents_api_smoke_abilities'][ $name ] = $ability; - return $ability; +if ( ! function_exists( 'wp_register_ability' ) ) { + function wp_register_ability( string $name, array $args ): ?WP_Ability { + $ability = new WP_Ability( $name, $args ); + $GLOBALS['__agents_api_smoke_abilities'][ $name ] = $ability; + return $ability; + } } -function wp_get_ability( string $name ): ?WP_Ability { - return $GLOBALS['__agents_api_smoke_abilities'][ $name ] ?? null; +if ( ! function_exists( 'wp_get_ability' ) ) { + function wp_get_ability( string $name ): ?WP_Ability { + return $GLOBALS['__agents_api_smoke_abilities'][ $name ] ?? null; + } } -function wp_get_abilities(): array { - return array_values( $GLOBALS['__agents_api_smoke_abilities'] ); +if ( ! function_exists( 'wp_get_abilities' ) ) { + function wp_get_abilities(): array { + return array_values( $GLOBALS['__agents_api_smoke_abilities'] ); + } } agents_api_smoke_require_module(); -do_action( 'wp_abilities_api_categories_init' ); -do_action( 'wp_abilities_api_init' ); +add_action( + 'wp_abilities_api_categories_init', + static function (): void { + if ( ! wp_has_ability_category( 'demo-tools' ) ) { + wp_register_ability_category( + 'demo-tools', + array( + 'label' => 'Demo Tools', + 'description' => 'Demo tool abilities for smoke coverage.', + ) + ); + } -wp_register_ability( - 'demo/weather-forecast', - array( - 'label' => 'Weather Forecast', - 'description' => 'Fetch a local weather forecast for a city.', - 'category' => 'demo-tools', - 'input_schema' => array( - 'type' => 'object', - 'required' => array( 'city' ), - 'properties' => array( - 'city' => array( 'type' => 'string' ), - ), - ), - 'execute_callback' => static function ( array $input ): array { - return array( 'forecast' => 'sunny in ' . ( $input['city'] ?? '' ) ); - }, - 'permission_callback' => static function (): bool { - return true; - }, - ) + if ( ! wp_has_ability_category( 'content' ) ) { + wp_register_ability_category( + 'content', + array( + 'label' => 'Content', + 'description' => 'Demo content abilities for smoke coverage.', + ) + ); + } + } ); -wp_register_ability( - 'demo/publish-post', - array( - 'label' => 'Publish Post', - 'description' => 'Publish a draft post by ID.', - 'category' => 'content', - 'input_schema' => array( - 'type' => 'object', - 'required' => array( 'post_id' ), - ), - 'execute_callback' => static function ( array $input ): array { - return array( 'published' => (int) ( $input['post_id'] ?? 0 ) ); - }, - 'permission_callback' => static function (): bool { - return true; - }, - ) +add_action( + 'wp_abilities_api_init', + static function (): void { + if ( ! wp_has_ability( 'demo/weather-forecast' ) ) { + wp_register_ability( + 'demo/weather-forecast', + array( + 'label' => 'Weather Forecast', + 'description' => 'Fetch a local weather forecast for a city.', + 'category' => 'demo-tools', + 'input_schema' => array( + 'type' => 'object', + 'required' => array( 'city' ), + 'properties' => array( + 'city' => array( 'type' => 'string' ), + ), + ), + 'execute_callback' => static function ( array $input ): array { + return array( 'forecast' => 'sunny in ' . ( $input['city'] ?? '' ) ); + }, + 'permission_callback' => static function (): bool { + return true; + }, + ) + ); + } + + if ( ! wp_has_ability( 'demo/publish-post' ) ) { + wp_register_ability( + 'demo/publish-post', + array( + 'label' => 'Publish Post', + 'description' => 'Publish a draft post by ID.', + 'category' => 'content', + 'input_schema' => array( + 'type' => 'object', + 'required' => array( 'post_id' ), + ), + 'execute_callback' => static function ( array $input ): array { + return array( 'published' => (int) ( $input['post_id'] ?? 0 ) ); + }, + 'permission_callback' => static function (): bool { + return true; + }, + ) + ); + } + } ); +do_action( 'wp_abilities_api_categories_init' ); +do_action( 'wp_abilities_api_init' ); + echo "\n[1] Meta-abilities register in canonical namespace:\n"; agents_api_smoke_assert_equals( true, wp_has_ability( 'agents/ability-search' ), 'ability-search is registered', $failures, $passes ); agents_api_smoke_assert_equals( true, wp_has_ability( 'agents/ability-call' ), 'ability-call is registered', $failures, $passes ); diff --git a/tests/agents-access-ability-smoke.php b/tests/agents-access-ability-smoke.php index bd13822..7c2cffa 100644 --- a/tests/agents-access-ability-smoke.php +++ b/tests/agents-access-ability-smoke.php @@ -22,32 +22,67 @@ $GLOBALS['__agents_api_smoke_abilities'] = array(); $GLOBALS['__agents_api_smoke_categories'] = array(); -function get_current_user_id(): int { - return (int) $GLOBALS['__agents_api_smoke_current_user_id']; +if ( function_exists( 'wp_set_current_user' ) ) { + wp_set_current_user( 7 ); } -function is_user_logged_in(): bool { - return get_current_user_id() > 0; +if ( ! function_exists( 'get_current_user_id' ) ) { + function get_current_user_id(): int { + return (int) $GLOBALS['__agents_api_smoke_current_user_id']; + } +} + +if ( ! function_exists( 'is_user_logged_in' ) ) { + function is_user_logged_in(): bool { + return get_current_user_id() > 0; + } } -function wp_has_ability_category( string $category ): bool { - return isset( $GLOBALS['__agents_api_smoke_categories'][ $category ] ); +if ( ! function_exists( 'wp_has_ability_category' ) ) { + function wp_has_ability_category( string $category ): bool { + return isset( $GLOBALS['__agents_api_smoke_categories'][ $category ] ); + } } -function wp_register_ability_category( string $category, array $args ): void { - $GLOBALS['__agents_api_smoke_categories'][ $category ] = $args; +if ( ! function_exists( 'wp_register_ability_category' ) ) { + function wp_register_ability_category( string $category, array $args ): void { + $GLOBALS['__agents_api_smoke_categories'][ $category ] = $args; + } } -function wp_has_ability( string $ability ): bool { - return isset( $GLOBALS['__agents_api_smoke_abilities'][ $ability ] ); +if ( ! function_exists( 'wp_has_ability' ) ) { + function wp_has_ability( string $ability ): bool { + return isset( $GLOBALS['__agents_api_smoke_abilities'][ $ability ] ); + } } -function wp_register_ability( string $ability, array $args ): void { - $GLOBALS['__agents_api_smoke_abilities'][ $ability ] = $args; +if ( ! function_exists( 'wp_register_ability' ) ) { + function wp_register_ability( string $ability, array $args ): void { + $GLOBALS['__agents_api_smoke_abilities'][ $ability ] = $args; + } } agents_api_smoke_require_module(); +add_filter( + 'agents_api_execution_principal', + static function ( $principal, array $context ) { + if ( (int) $GLOBALS['__agents_api_smoke_current_user_id'] <= 0 ) { + return $principal; + } + + return AgentsAPI\AI\WP_Agent_Execution_Principal::user_session( + (int) $GLOBALS['__agents_api_smoke_current_user_id'], + 'user-session', + $context['request_context'] ?? AgentsAPI\AI\WP_Agent_Execution_Principal::REQUEST_CONTEXT_REST, + array( 'source' => 'smoke-test' ), + $context['workspace_id'] ?? null + ); + }, + 10, + 2 +); + add_action( 'wp_agents_api_init', static function (): void { @@ -209,6 +244,9 @@ static function ( $store ) use ( $access_store ) { agents_api_smoke_assert_equals( 'editor-agent', $ability_list['agents'][0]['slug'] ?? null, 'list-accessible ability returns granted registered agent', $failures, $passes ); $GLOBALS['__agents_api_smoke_current_user_id'] = 0; +if ( function_exists( 'wp_set_current_user' ) ) { + wp_set_current_user( 0 ); +} add_filter( 'agents_api_execution_principal', static function ( $principal, array $context ) { diff --git a/tests/agents-api-smoke-helpers.php b/tests/agents-api-smoke-helpers.php index 306d984..806b2bd 100644 --- a/tests/agents-api-smoke-helpers.php +++ b/tests/agents-api-smoke-helpers.php @@ -19,168 +19,220 @@ $GLOBALS['__agents_api_smoke_posts'] = array(); $GLOBALS['__agents_api_smoke_post_meta'] = array(); -function __( string $text, string $domain = 'default' ): string { - unset( $domain ); - return $text; +if ( ! function_exists( '__' ) ) { + function __( string $text, string $domain = 'default' ): string { + unset( $domain ); + return $text; + } } -function _x( string $text, string $context, string $domain = 'default' ): string { - unset( $context, $domain ); - return $text; +if ( ! function_exists( '_x' ) ) { + function _x( string $text, string $context, string $domain = 'default' ): string { + unset( $context, $domain ); + return $text; + } } -function sanitize_title( string $value ): string { - $value = strtolower( $value ); - $value = preg_replace( '/[^a-z0-9]+/', '-', $value ); - return trim( (string) $value, '-' ); +if ( ! function_exists( 'sanitize_title' ) ) { + function sanitize_title( string $value ): string { + $value = strtolower( $value ); + $value = preg_replace( '/[^a-z0-9]+/', '-', $value ); + return trim( (string) $value, '-' ); + } } -function sanitize_file_name( string $value ): string { - return basename( $value ); +if ( ! function_exists( 'sanitize_file_name' ) ) { + function sanitize_file_name( string $value ): string { + return basename( $value ); + } } -function add_action( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void { - unset( $accepted_args ); - $GLOBALS['__agents_api_smoke_actions'][ $hook ][ $priority ][] = $callback; +if ( ! function_exists( 'add_action' ) ) { + function add_action( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void { + unset( $accepted_args ); + $GLOBALS['__agents_api_smoke_actions'][ $hook ][ $priority ][] = $callback; + } } -function do_action( string $hook, ...$args ): void { - $GLOBALS['__agents_api_smoke_current'][] = $hook; - $callbacks = $GLOBALS['__agents_api_smoke_actions'][ $hook ] ?? array(); - ksort( $callbacks ); +if ( ! function_exists( 'do_action' ) ) { + function do_action( string $hook, ...$args ): void { + $GLOBALS['__agents_api_smoke_current'][] = $hook; + $callbacks = $GLOBALS['__agents_api_smoke_actions'][ $hook ] ?? array(); + ksort( $callbacks ); - foreach ( $callbacks as $priority_callbacks ) { - foreach ( $priority_callbacks as $callback ) { - call_user_func_array( $callback, $args ); + foreach ( $callbacks as $priority_callbacks ) { + foreach ( $priority_callbacks as $callback ) { + call_user_func_array( $callback, $args ); + } } - } - array_pop( $GLOBALS['__agents_api_smoke_current'] ); - $GLOBALS['__agents_api_smoke_done'][ $hook ] = ( $GLOBALS['__agents_api_smoke_done'][ $hook ] ?? 0 ) + 1; + array_pop( $GLOBALS['__agents_api_smoke_current'] ); + $GLOBALS['__agents_api_smoke_done'][ $hook ] = ( $GLOBALS['__agents_api_smoke_done'][ $hook ] ?? 0 ) + 1; + } } -function add_filter( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void { - add_action( $hook, $callback, $priority, $accepted_args ); +if ( ! function_exists( 'add_filter' ) ) { + function add_filter( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void { + add_action( $hook, $callback, $priority, $accepted_args ); + } } -function apply_filters( string $hook, $value, ...$args ) { - $GLOBALS['__agents_api_smoke_current'][] = $hook; - $callbacks = $GLOBALS['__agents_api_smoke_actions'][ $hook ] ?? array(); - ksort( $callbacks ); +if ( ! function_exists( 'apply_filters' ) ) { + function apply_filters( string $hook, $value, ...$args ) { + $GLOBALS['__agents_api_smoke_current'][] = $hook; + $callbacks = $GLOBALS['__agents_api_smoke_actions'][ $hook ] ?? array(); + ksort( $callbacks ); - foreach ( $callbacks as $priority_callbacks ) { - foreach ( $priority_callbacks as $callback ) { - $value = call_user_func_array( $callback, array_merge( array( $value ), $args ) ); + foreach ( $callbacks as $priority_callbacks ) { + foreach ( $priority_callbacks as $callback ) { + $value = call_user_func_array( $callback, array_merge( array( $value ), $args ) ); + } } - } - array_pop( $GLOBALS['__agents_api_smoke_current'] ); - $GLOBALS['__agents_api_smoke_done'][ $hook ] = ( $GLOBALS['__agents_api_smoke_done'][ $hook ] ?? 0 ) + 1; + array_pop( $GLOBALS['__agents_api_smoke_current'] ); + $GLOBALS['__agents_api_smoke_done'][ $hook ] = ( $GLOBALS['__agents_api_smoke_done'][ $hook ] ?? 0 ) + 1; - return $value; + return $value; + } } -function doing_action( string $hook ): bool { - return in_array( $hook, $GLOBALS['__agents_api_smoke_current'], true ); +if ( ! function_exists( 'doing_action' ) ) { + function doing_action( string $hook ): bool { + return in_array( $hook, $GLOBALS['__agents_api_smoke_current'], true ); + } } -function did_action( string $hook ): int { - return (int) ( $GLOBALS['__agents_api_smoke_done'][ $hook ] ?? 0 ); +if ( ! function_exists( 'did_action' ) ) { + function did_action( string $hook ): int { + return (int) ( $GLOBALS['__agents_api_smoke_done'][ $hook ] ?? 0 ); + } } -function esc_html( string $value ): string { - return htmlspecialchars( $value, ENT_QUOTES, 'UTF-8' ); +if ( ! function_exists( 'esc_html' ) ) { + function esc_html( string $value ): string { + return htmlspecialchars( $value, ENT_QUOTES, 'UTF-8' ); + } } -function wp_json_encode( $value, int $flags = 0, int $depth = 512 ) { - return json_encode( $value, $flags, max( 1, $depth ) ); +if ( ! function_exists( 'wp_json_encode' ) ) { + function wp_json_encode( $value, int $flags = 0, int $depth = 512 ) { + return json_encode( $value, $flags, max( 1, $depth ) ); + } } -function wp_parse_url( string $url, int $component = -1 ) { - return parse_url( $url, $component ); +if ( ! function_exists( 'wp_parse_url' ) ) { + function wp_parse_url( string $url, int $component = -1 ) { + return parse_url( $url, $component ); + } } -function _doing_it_wrong( string $function_name, string $message, string $version ): void { - $GLOBALS['__agents_api_smoke_wrong'][] = array( - 'function' => $function_name, - 'message' => $message, - 'version' => $version, - ); +if ( ! function_exists( '_doing_it_wrong' ) ) { + function _doing_it_wrong( string $function_name, string $message, string $version ): void { + $GLOBALS['__agents_api_smoke_wrong'][] = array( + 'function' => $function_name, + 'message' => $message, + 'version' => $version, + ); + } } -function post_type_exists( string $post_type ): bool { - return isset( $GLOBALS['__agents_api_smoke_post_types'][ $post_type ] ); +if ( ! function_exists( 'post_type_exists' ) ) { + function post_type_exists( string $post_type ): bool { + return isset( $GLOBALS['__agents_api_smoke_post_types'][ $post_type ] ); + } } -function register_post_type( string $post_type, array $args = array() ) { - $GLOBALS['__agents_api_smoke_post_types'][ $post_type ] = $args; - return (object) array( - 'name' => $post_type, - 'args' => $args, - ); +if ( ! function_exists( 'register_post_type' ) ) { + function register_post_type( string $post_type, array $args = array() ) { + $GLOBALS['__agents_api_smoke_post_types'][ $post_type ] = $args; + return (object) array( + 'name' => $post_type, + 'args' => $args, + ); + } } -function get_post( int $post_id ) { - return $GLOBALS['__agents_api_smoke_posts'][ $post_id ] ?? null; +if ( ! function_exists( 'get_post' ) ) { + function get_post( int $post_id ) { + return $GLOBALS['__agents_api_smoke_posts'][ $post_id ] ?? null; + } } -function get_post_meta( int $post_id, string $key = '', bool $single = false ) { - $meta = $GLOBALS['__agents_api_smoke_post_meta'][ $post_id ] ?? array(); +if ( ! function_exists( 'get_post_meta' ) ) { + function get_post_meta( int $post_id, string $key = '', bool $single = false ) { + $meta = $GLOBALS['__agents_api_smoke_post_meta'][ $post_id ] ?? array(); - if ( '' === $key ) { - return $meta; - } + if ( '' === $key ) { + return $meta; + } - $value = $meta[ $key ] ?? ( $single ? '' : array() ); - return $single && is_array( $value ) ? reset( $value ) : $value; + $value = $meta[ $key ] ?? ( $single ? '' : array() ); + return $single && is_array( $value ) ? reset( $value ) : $value; + } } -function taxonomy_exists( string $taxonomy ): bool { - return isset( $GLOBALS['__agents_api_smoke_taxonomies'][ $taxonomy ] ); +if ( ! function_exists( 'taxonomy_exists' ) ) { + function taxonomy_exists( string $taxonomy ): bool { + return isset( $GLOBALS['__agents_api_smoke_taxonomies'][ $taxonomy ] ); + } } -function register_taxonomy( string $taxonomy, $object_type, array $args = array() ) { - $GLOBALS['__agents_api_smoke_taxonomies'][ $taxonomy ] = array( - 'object_type' => $object_type, - 'args' => $args, - ); - return (object) array( - 'name' => $taxonomy, - 'args' => $args, - ); +if ( ! function_exists( 'register_taxonomy' ) ) { + function register_taxonomy( string $taxonomy, $object_type, array $args = array() ) { + $GLOBALS['__agents_api_smoke_taxonomies'][ $taxonomy ] = array( + 'object_type' => $object_type, + 'args' => $args, + ); + return (object) array( + 'name' => $taxonomy, + 'args' => $args, + ); + } } -function wp_is_post_revision( int $post_id ) { - unset( $post_id ); - return false; +if ( ! function_exists( 'wp_is_post_revision' ) ) { + function wp_is_post_revision( int $post_id ) { + unset( $post_id ); + return false; + } } -function get_the_terms( int $post_id, string $taxonomy ) { - return $GLOBALS['__agents_api_smoke_object_terms'][ $post_id ][ $taxonomy ] ?? array(); +if ( ! function_exists( 'get_the_terms' ) ) { + function get_the_terms( int $post_id, string $taxonomy ) { + return $GLOBALS['__agents_api_smoke_object_terms'][ $post_id ][ $taxonomy ] ?? array(); + } } -function term_exists( string $term, string $taxonomy ) { - return $GLOBALS['__agents_api_smoke_terms'][ $taxonomy ][ $term ] ?? null; +if ( ! function_exists( 'term_exists' ) ) { + function term_exists( string $term, string $taxonomy ) { + return $GLOBALS['__agents_api_smoke_terms'][ $taxonomy ][ $term ] ?? null; + } } -function wp_insert_term( string $term, string $taxonomy, array $args = array() ) { - $slug = isset( $args['slug'] ) ? (string) $args['slug'] : sanitize_title( $term ); - $term_id = count( $GLOBALS['__agents_api_smoke_terms'][ $taxonomy ] ?? array() ) + 1; - $created = array( - 'term_id' => $term_id, - 'slug' => $slug, - 'name' => $term, - ); - $GLOBALS['__agents_api_smoke_terms'][ $taxonomy ][ $slug ] = $created; - return $created; +if ( ! function_exists( 'wp_insert_term' ) ) { + function wp_insert_term( string $term, string $taxonomy, array $args = array() ) { + $slug = isset( $args['slug'] ) ? (string) $args['slug'] : sanitize_title( $term ); + $term_id = count( $GLOBALS['__agents_api_smoke_terms'][ $taxonomy ] ?? array() ) + 1; + $created = array( + 'term_id' => $term_id, + 'slug' => $slug, + 'name' => $term, + ); + $GLOBALS['__agents_api_smoke_terms'][ $taxonomy ][ $slug ] = $created; + return $created; + } } -function wp_set_object_terms( int $post_id, $terms, string $taxonomy ): void { - $GLOBALS['__agents_api_smoke_object_terms'][ $post_id ][ $taxonomy ] = (array) $terms; +if ( ! function_exists( 'wp_set_object_terms' ) ) { + function wp_set_object_terms( int $post_id, $terms, string $taxonomy ): void { + $GLOBALS['__agents_api_smoke_object_terms'][ $post_id ][ $taxonomy ] = (array) $terms; + } } -function is_wp_error( $value ): bool { - return class_exists( 'WP_Error' ) && $value instanceof WP_Error; +if ( ! function_exists( 'is_wp_error' ) ) { + function is_wp_error( $value ): bool { + return class_exists( 'WP_Error' ) && $value instanceof WP_Error; + } } function agents_api_smoke_assert_equals( $expected, $actual, string $name, array &$failures, int &$passes ): void { diff --git a/tests/agents-chat-ability-smoke.php b/tests/agents-chat-ability-smoke.php index 72ca1a0..61cbf37 100644 --- a/tests/agents-chat-ability-smoke.php +++ b/tests/agents-chat-ability-smoke.php @@ -180,6 +180,9 @@ function smoke_assert( $expected, $actual, string $name, array &$failures, int & smoke_assert( true, isset( $in['properties']['client_context']['properties']['caller_agent'] ), 'client_context_schema_has_caller_agent', $failures, $passes ); smoke_assert( true, isset( $in['properties']['client_context']['properties']['caller_session_id'] ), 'client_context_schema_has_caller_session_id', $failures, $passes ); smoke_assert( true, isset( $in['properties']['client_context']['properties']['peer_agent_call'] ), 'client_context_schema_has_peer_agent_call', $failures, $passes ); +smoke_assert( true, isset( $in['properties']['client_context']['properties']['runtime_tools']['additionalProperties'] ), 'client_context_schema_has_runtime_tools', $failures, $passes ); +smoke_assert( true, isset( $in['properties']['client_context']['properties']['runtime_tool_declarations']['additionalProperties'] ), 'client_context_schema_has_runtime_tool_declarations', $failures, $passes ); +smoke_assert( true, isset( $in['properties']['client_context']['properties']['runtime_tool_callback'] ), 'client_context_schema_has_runtime_tool_callback', $failures, $passes ); smoke_assert( false, isset( $in['properties']['client_context']['properties']['context_binding'] ), 'client_context_schema_omits_context_binding_api', $failures, $passes ); smoke_assert( false, isset( $in['properties']['client_context']['properties']['agent_chat_depth'] ), 'client_context_schema_omits_tool_specific_depth', $failures, $passes ); smoke_assert( true, isset( $in['properties']['principal'] ), 'input_schema_has_principal', $failures, $passes );