From 703d12863b0ab3cf770ebb1b8dd4f33d157f6fe1 Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Sun, 5 Jul 2026 13:25:56 +0600 Subject: [PATCH] feat(optimization-detective): expose URL Metric to Chrome DevTools for agents - Add devtools-discovery.js extension module registering two read-only tools (get_od_url_metric, get_od_element_data) via the devtoolstooldiscovery event - Register it through the existing od_extension_module_urls filter, gated on WP_DEBUG - Add tests and a short docs note Lets an AI coding agent inspect the URL Metric being collected for the current page load while debugging performance, via Chrome DevTools for agents third-party tools API. Fixes #2553 --- plugins/optimization-detective/detection.php | 23 ++++++ .../devtools-discovery.js | 82 +++++++++++++++++++ .../docs/introduction.md | 2 + plugins/optimization-detective/hooks.php | 3 + .../tests/test-detection.php | 16 ++++ .../tests/test-hooks.php | 6 ++ webpack.config.js | 4 + 7 files changed, 136 insertions(+) create mode 100644 plugins/optimization-detective/devtools-discovery.js diff --git a/plugins/optimization-detective/detection.php b/plugins/optimization-detective/detection.php index 6566bf2c4f..36459ab06b 100644 --- a/plugins/optimization-detective/detection.php +++ b/plugins/optimization-detective/detection.php @@ -187,6 +187,29 @@ function od_get_detection_scripts( string $slug, OD_URL_Metric_Group_Collection return $json_script . $module_script; } +/** + * Filters the list of Optimization Detective extension module URLs to include the DevTools discovery module. + * + * This exposes the current URL Metric to Chrome DevTools for agents so that an AI agent can inspect it while + * debugging page performance. It is only added when WP_DEBUG is enabled, since this is a development tool. + * + * @since n.e.x.t + * @access private + * + * @param string[]|mixed $extension_module_urls Extension module URLs. + * @return string[] Extension module URLs. + */ +function od_filter_extension_module_urls_for_devtools_discovery( $extension_module_urls ): array { + if ( ! is_array( $extension_module_urls ) ) { + $extension_module_urls = array(); + } + $extension_module_urls[] = add_query_arg( + array( 'ver' => OPTIMIZATION_DETECTIVE_VERSION ), + plugins_url( od_get_asset_path( 'devtools-discovery.js' ), __FILE__ ) + ); + return $extension_module_urls; +} + /** * Registers the REST API endpoint for storing URL Metrics. * diff --git a/plugins/optimization-detective/devtools-discovery.js b/plugins/optimization-detective/devtools-discovery.js new file mode 100644 index 0000000000..0a907a60e9 --- /dev/null +++ b/plugins/optimization-detective/devtools-discovery.js @@ -0,0 +1,82 @@ +/** + * DevTools Discovery module for Optimization Detective. + * + * Exposes the URL Metric being collected for the current page load to Chrome DevTools for + * agents, via its third-party tools API, so that an AI agent can inspect it while debugging + * page performance. + * + * @since n.e.x.t + * + * @see https://developer.chrome.com/docs/devtools/agents/use-cases/third-party-tools + */ + +/** + * Extension name. + * + * @since n.e.x.t + * + * @type {string} + */ +export const name = 'DevTools Discovery'; + +/** + * @typedef {import("./types.ts").InitializeCallback} InitializeCallback + * @typedef {import("./types.ts").InitializeArgs} InitializeArgs + */ + +/** + * Event dispatched by Chrome DevTools for agents to discover third-party tools. + * + * This is a new, experimental, Chrome-only browser API not yet reflected in TypeScript's DOM lib. + * + * @typedef {Event & { respondWith: (toolGroup: Object) => void }} DevtoolsToolDiscoveryEvent + */ + +/** + * Initializes extension. + * + * @since n.e.x.t + * + * @type {InitializeCallback} + * @param {InitializeArgs} args Args. + */ +export async function initialize( { getRootData, getElementData } ) { + window.addEventListener( 'devtoolstooldiscovery', ( event ) => { + const discoveryEvent = /** @type {DevtoolsToolDiscoveryEvent} */ ( + event + ); + discoveryEvent.respondWith( { + name: 'Optimization Detective', + description: + 'Inspect the URL Metric being collected by Optimization Detective for the current page load.', + tools: [ + { + name: 'get_od_url_metric', + description: + 'Returns the URL Metric (viewport dimensions and tracked element LCP/intersection data) collected so far for the current page load, or null if none has been collected yet.', + inputSchema: { type: 'object', properties: {} }, + execute: async () => getRootData(), + }, + { + name: 'get_od_element_data', + description: + 'Returns the tracked LCP/intersection data for a single element, identified by its XPath as found in its data-od-xpath attribute, or null if the element is not tracked.', + inputSchema: { + type: 'object', + properties: { + xpath: { + type: 'string', + description: + 'XPath of the element, as found in its data-od-xpath attribute.', + }, + }, + required: [ 'xpath' ], + }, + execute: async ( + /** @type {{ xpath: string }} */ { xpath } + ) => getElementData( xpath ), + }, + ], + } ); + } ); +} diff --git a/plugins/optimization-detective/docs/introduction.md b/plugins/optimization-detective/docs/introduction.md index d53aa7cfa3..b905d9056a 100644 --- a/plugins/optimization-detective/docs/introduction.md +++ b/plugins/optimization-detective/docs/introduction.md @@ -448,6 +448,8 @@ During the development of Optimization Detective extensions, it’s recommended Then, to actually inspect the contents of the URL Metrics which are collected, it’s recommended you install the [Admin UI](https://github.com/westonruter/od-admin-ui) plugin. +When `WP_DEBUG` is enabled, Optimization Detective also registers a [Chrome DevTools for agents](https://developer.chrome.com/docs/devtools/agents/use-cases/third-party-tools) third-party tools integration, exposing `get_od_url_metric` and `get_od_element_data` tools so an AI coding agent can inspect the URL Metric being collected for the current page load while debugging performance. + # Highlighted Extensions There is a [reference](https://github.com/WordPress/performance/blob/trunk/plugins/optimization-detective/docs/extensions.md) for the full list of known Optimization Detective extensions, including links to the code on GitHub for how they were implemented. But here are highlights from Image Prioritizer and Embed Optimizer: diff --git a/plugins/optimization-detective/hooks.php b/plugins/optimization-detective/hooks.php index 18b0ad1f05..60bc154b7f 100644 --- a/plugins/optimization-detective/hooks.php +++ b/plugins/optimization-detective/hooks.php @@ -31,4 +31,7 @@ add_action( 'rest_api_init', 'od_register_rest_url_metric_store_endpoint' ); add_filter( 'rest_pre_dispatch', 'od_decompress_rest_request_body', 10, 3 ); add_action( 'od_trigger_page_cache_invalidation', 'od_trigger_post_update_actions' ); +if ( WP_DEBUG ) { + add_filter( 'od_extension_module_urls', 'od_filter_extension_module_urls_for_devtools_discovery' ); +} // @codeCoverageIgnoreEnd diff --git a/plugins/optimization-detective/tests/test-detection.php b/plugins/optimization-detective/tests/test-detection.php index 10df8b6ea2..233566c41d 100644 --- a/plugins/optimization-detective/tests/test-detection.php +++ b/plugins/optimization-detective/tests/test-detection.php @@ -326,4 +326,20 @@ public function test_od_trigger_post_update_actions(): void { $this->assertSame( $before_transition_post_status_count, did_action( 'transition_post_status' ) ); $this->assertSame( $before_save_post_count, did_action( 'save_post' ) ); } + + /** + * Test od_filter_extension_module_urls_for_devtools_discovery(). + * + * @covers ::od_filter_extension_module_urls_for_devtools_discovery + */ + public function test_od_filter_extension_module_urls_for_devtools_discovery(): void { + $urls = od_filter_extension_module_urls_for_devtools_discovery( null ); + $this->assertCount( 1, $urls ); + $this->assertStringContainsString( 'devtools-discovery', $urls[0] ); + + $urls = od_filter_extension_module_urls_for_devtools_discovery( array( 'foo.js' ) ); + $this->assertCount( 2, $urls ); + $this->assertStringContainsString( 'foo.js', $urls[0] ); + $this->assertStringContainsString( 'devtools-discovery', $urls[1] ); + } } diff --git a/plugins/optimization-detective/tests/test-hooks.php b/plugins/optimization-detective/tests/test-hooks.php index 6e1fa19efe..06dacb5807 100644 --- a/plugins/optimization-detective/tests/test-hooks.php +++ b/plugins/optimization-detective/tests/test-hooks.php @@ -26,5 +26,11 @@ public function test_hooks_added(): void { $this->assertEquals( 10, has_action( 'rest_api_init', 'od_register_rest_url_metric_store_endpoint' ) ); $this->assertEquals( 10, has_action( 'rest_pre_dispatch', 'od_decompress_rest_request_body' ) ); $this->assertEquals( 10, has_action( 'od_trigger_page_cache_invalidation', 'od_trigger_post_update_actions' ) ); + + if ( WP_DEBUG ) { + $this->assertEquals( 10, has_filter( 'od_extension_module_urls', 'od_filter_extension_module_urls_for_devtools_discovery' ) ); + } else { + $this->assertFalse( has_filter( 'od_extension_module_urls', 'od_filter_extension_module_urls_for_devtools_discovery' ) ); + } } } diff --git a/webpack.config.js b/webpack.config.js index 407a250f1c..9b1887c195 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -216,6 +216,10 @@ const optimizationDetective = ( env ) => { from: `${ destination }/detect.js`, to: `${ destination }/detect.min.js`, }, + { + from: `${ destination }/devtools-discovery.js`, + to: `${ destination }/devtools-discovery.min.js`, + }, ], } ), // @ts-expect-error TS2351: WebpackBar is constructable when using require(), type definitions might be geared towards ESM.