From 12855ad356150b8d9bbadbdae8cf70e19ef97d15 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Fri, 3 Jul 2026 13:04:59 -0400 Subject: [PATCH 1/6] chore: bump bundled agents-api to v0.5.1 --- composer.lock | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 338267e48..71a1e6504 100644 --- a/composer.lock +++ b/composer.lock @@ -794,12 +794,12 @@ "source": { "type": "git", "url": "https://github.com/Automattic/agents-api.git", - "reference": "07300175de7004b3f5af75d16865a078a37203d4" + "reference": "59d1e6b473f22498e40e279130bbb4f9bcde3b73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Automattic/agents-api/zipball/07300175de7004b3f5af75d16865a078a37203d4", - "reference": "07300175de7004b3f5af75d16865a078a37203d4", + "url": "https://api.github.com/repos/Automattic/agents-api/zipball/59d1e6b473f22498e40e279130bbb4f9bcde3b73", + "reference": "59d1e6b473f22498e40e279130bbb4f9bcde3b73", "shasum": "" }, "require": { @@ -914,6 +914,12 @@ "php tests/workflow-spec-validator-smoke.php", "php tests/workflow-runner-smoke.php", "php tests/workflow-parallel-smoke.php", + "php tests/workflow-parallel-async-smoke.php", + "php tests/workflow-as-branch-smoke.php", + "php tests/workflow-branch-concurrency-gate-smoke.php", + "php tests/workflow-async-branch-payload-smoke.php", + "php tests/workflow-async-loopback-target-smoke.php", + "php tests/workflow-reconcile-race-smoke.php", "php tests/workflow-lifecycle-smoke.php", "php tests/agents-workflow-ability-smoke.php", "php tests/routine-smoke.php", @@ -934,7 +940,7 @@ "issues": "https://github.com/Automattic/agents-api/issues", "source": "https://github.com/Automattic/agents-api" }, - "time": "2026-06-30T21:38:29+00:00" + "time": "2026-07-03T14:34:26+00:00" } ], "packages-dev": [ From 09687802a17238b82b576f80e4558f6cd9aa369b Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Fri, 3 Jul 2026 13:04:59 -0400 Subject: [PATCH 2/6] feat: guard against dual-loaded agents-api version skew --- data-machine.php | 102 ++++++++++++++++++++- tests/release-bundled-agents-api-smoke.php | 10 +- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/data-machine.php b/data-machine.php index 10ef5e836..e6519b499 100644 --- a/data-machine.php +++ b/data-machine.php @@ -22,8 +22,107 @@ define( 'DATAMACHINE_PATH', plugin_dir_path( __FILE__ ) ); define( 'DATAMACHINE_URL', plugin_dir_url( __FILE__ ) ); +/** + * Read the Agents API plugin header version without loading the plugin file. + * + * @param string $plugin_file Plugin file path. + * @return string|null Version string, or null when unavailable. + */ +function datamachine_read_agents_api_plugin_version( string $plugin_file ): ?string { + if ( ! is_readable( $plugin_file ) ) { + return null; + } + + if ( function_exists( 'get_file_data' ) ) { + $headers = get_file_data( $plugin_file, array( 'version' => 'Version' ), 'plugin' ); + $version = trim( (string) ( $headers['version'] ?? '' ) ); + + return '' !== $version ? $version : null; + } + + $contents = (string) file_get_contents( $plugin_file, false, null, 0, 8192 ); + if ( preg_match( '/^[ \t\/*#@]*Version:\s*(.+)$/mi', $contents, $matches ) ) { + return trim( (string) $matches[1] ); + } + + return null; +} + +/** + * Load bundled Agents API unless another copy is already active. + * + * @return array{loaded:string,bundled_version:?string,active_version:?string,active_file:?string,warning:?string} + */ +function datamachine_load_bundled_agents_api(): array { + $bundled_file = __DIR__ . '/vendor/wordpress/agents-api/agents-api.php'; + $bundled_version = datamachine_read_agents_api_plugin_version( $bundled_file ); + + if ( defined( 'AGENTS_API_LOADED' ) ) { + $active_file = defined( 'AGENTS_API_PLUGIN_FILE' ) ? (string) constant( 'AGENTS_API_PLUGIN_FILE' ) : null; + $active_version = defined( 'AGENTS_API_VERSION' ) ? (string) constant( 'AGENTS_API_VERSION' ) : null; + if ( null === $active_version && null !== $active_file ) { + $active_version = datamachine_read_agents_api_plugin_version( $active_file ); + } + + $warning = null; + if ( null !== $bundled_version && null !== $active_version && $bundled_version !== $active_version ) { + $warning = sprintf( + 'Data Machine is using an already-loaded Agents API version %1$s instead of its bundled version %2$s. Deactivate the standalone Agents API plugin or align versions to avoid runtime substrate skew.', + $active_version, + $bundled_version + ); + } + + return array( + 'loaded' => 'external', + 'bundled_version' => $bundled_version, + 'active_version' => $active_version, + 'active_file' => $active_file, + 'warning' => $warning, + ); + } + + require_once $bundled_file; + + return array( + 'loaded' => 'bundled', + 'bundled_version' => $bundled_version, + 'active_version' => $bundled_version, + 'active_file' => defined( 'AGENTS_API_PLUGIN_FILE' ) + ? (string) constant( 'AGENTS_API_PLUGIN_FILE' ) + : $bundled_file, + 'warning' => null, + ); +} + +/** + * Emit any deferred Agents API load warning through the Data Machine logger. + * + * @return void + */ +function datamachine_log_agents_api_load_warning(): void { + global $datamachine_agents_api_load_state; + + if ( ! is_array( $datamachine_agents_api_load_state ) || empty( $datamachine_agents_api_load_state['warning'] ) ) { + return; + } + + do_action( + 'datamachine_log', + 'warning', + (string) $datamachine_agents_api_load_state['warning'], + array( + 'component' => 'agents-api', + 'loaded' => $datamachine_agents_api_load_state['loaded'] ?? null, + 'active_version' => $datamachine_agents_api_load_state['active_version'] ?? null, + 'bundled_version' => $datamachine_agents_api_load_state['bundled_version'] ?? null, + 'active_file' => $datamachine_agents_api_load_state['active_file'] ?? null, + ) + ); +} + require_once __DIR__ . '/vendor/autoload.php'; -require_once __DIR__ . '/vendor/wordpress/agents-api/agents-api.php'; +$datamachine_agents_api_load_state = datamachine_load_bundled_agents_api(); // WP-CLI integration // @phpstan-ignore-next-line Runtime constant may be defined false outside PHPStan's configured CLI context. @@ -103,6 +202,7 @@ function () { datamachine_register_admin_filters(); datamachine_register_oauth_system(); datamachine_register_core_actions(); + datamachine_log_agents_api_load_warning(); // Load step types - they self-register via constructors datamachine_load_step_types(); diff --git a/tests/release-bundled-agents-api-smoke.php b/tests/release-bundled-agents-api-smoke.php index c21d45c52..8e36026b6 100644 --- a/tests/release-bundled-agents-api-smoke.php +++ b/tests/release-bundled-agents-api-smoke.php @@ -50,9 +50,13 @@ function datamachine_release_bundle_json_file( string $path ): array { datamachine_release_bundle_assert( in_array( 'wordpress/agents-api', $runtime_packages, true ), 'composer.lock keeps wordpress/agents-api in runtime packages', $failures, $passes ); datamachine_release_bundle_assert( ! in_array( 'wordpress/agents-api', $dev_packages, true ), 'composer.lock keeps wordpress/agents-api out of dev packages', $failures, $passes ); -echo "\n[2] Runtime bootstrap loads the bundled Agents API directly:\n"; -datamachine_release_bundle_assert( false === strpos( $plugin_source, "defined( 'AGENTS_API_LOADED' )" ), 'Data Machine does not fall back to an external Agents API bootstrap', $failures, $passes ); -datamachine_release_bundle_assert( false !== strpos( $plugin_source, "vendor/wordpress/agents-api/agents-api.php" ), 'Data Machine loads the bundled Agents API bootstrap', $failures, $passes ); +echo "\n[2] Runtime bootstrap loads bundled Agents API unless another copy already loaded:\n"; +datamachine_release_bundle_assert( false !== strpos( $plugin_source, "defined( 'AGENTS_API_LOADED' )" ), 'Data Machine detects a preloaded Agents API substrate', $failures, $passes ); +datamachine_release_bundle_assert( false !== strpos( $plugin_source, 'datamachine_load_bundled_agents_api' ), 'Data Machine centralizes Agents API load selection', $failures, $passes ); +datamachine_release_bundle_assert( false !== strpos( $plugin_source, 'require_once $bundled_file' ), 'Data Machine requires bundled Agents API when no substrate is active', $failures, $passes ); +datamachine_release_bundle_assert( false !== strpos( $plugin_source, "'loaded' => 'external'" ), 'Data Machine skips bundled Agents API when AGENTS_API_LOADED is predefined', $failures, $passes ); +datamachine_release_bundle_assert( false !== strpos( $plugin_source, 'runtime substrate skew' ), 'Data Machine warns on external/bundled Agents API version mismatch', $failures, $passes ); +datamachine_release_bundle_assert( false !== strpos( $plugin_source, "vendor/wordpress/agents-api/agents-api.php" ), 'Data Machine references the bundled Agents API bootstrap', $failures, $passes ); datamachine_release_bundle_assert( is_file( $root . '/vendor/wordpress/agents-api/agents-api.php' ), 'local bundled Agents API bootstrap exists for package validation', $failures, $passes ); echo "\n[3] Validation paths no longer install a separate GitHub-only Agents API plugin:\n"; From 11a77b77bc948782f4925e8eb515403e94e26b80 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Fri, 3 Jul 2026 13:04:59 -0400 Subject: [PATCH 3/6] test: adapt agents-api integration smokes to v0.5.1 --- docs/core-system/request-builder.md | 2 +- .../agents-api-standalone-skeleton-plan.md | 52 +++++++++---------- .../PendingActionInspectionAbility.php | 1 - tests/agents-api-package-contract-smoke.php | 16 +++--- ...nts-api-standalone-skeleton-plan-smoke.php | 42 +++++++-------- tests/agents-api-workflow-bridge-smoke.php | 23 +++++++- ...ents-api-wp-ai-client-vocabulary-smoke.php | 4 +- 7 files changed, 80 insertions(+), 60 deletions(-) diff --git a/docs/core-system/request-builder.md b/docs/core-system/request-builder.md index 793bc95a2..7c7d24639 100644 --- a/docs/core-system/request-builder.md +++ b/docs/core-system/request-builder.md @@ -13,7 +13,7 @@ Data Machine has two separate runtime layers: - **wp-ai-client** owns provider/model prompt execution. - **RequestBuilder** is the Data Machine adapter from product payloads to `wp-ai-client` provider requests. -Pipeline AI steps and Data Machine chat turns use `RequestBuilder::build()` so directives, metadata, request inspection, tool declarations, and timeout behavior stay consistent. Plugins that only need one-shot provider calls may call `wp-ai-client` directly; they do not need to route through Data Machine or Agents API. +Pipeline AI steps and Data Machine chat turns use `RequestBuilder::build()` so directives, metadata, request inspection, tool declarations, and timeout behavior stay consistent. Plugins that only need one-shot AI operations may call `wp-ai-client` directly; they do not need to route through Data Machine or Agents API. ## Flow diff --git a/docs/development/agents-api-standalone-skeleton-plan.md b/docs/development/agents-api-standalone-skeleton-plan.md index 96063189c..a2cb7c6d4 100644 --- a/docs/development/agents-api-standalone-skeleton-plan.md +++ b/docs/development/agents-api-standalone-skeleton-plan.md @@ -122,36 +122,36 @@ Move contracts/value objects before services. The first extraction PR should be | Move first | Current in-repo location | Why first | |---|---|---| -| `WP_Agent`, `WP_Agents_Registry`, and registration helpers | `agents-api/inc/class-wp-agent.php`, `class-wp-agents-registry.php`, `register-agents.php` | Public registration facade; no Data Machine product import. | -| Agent package artifact contracts/helpers | `agents-api/inc/class-wp-agent-package*.php`, `register-agent-package-artifacts.php` | Bundle/package contract is already backend-only. | -| `WP_Agent_Message` and `WP_Agent_Conversation_Result` | `agents-api/inc/AI/` | Generic run/message value contracts. | -| `WP_Agent_Tool_Declaration` | `agents-api/inc/AI/Tools/` | Generic run-scoped tool declaration validation. | -| `WP_Agent_Conversation_Store` | `agents-api/inc/Core/Database/Chat/` | Narrow transcript CRUD contract; does not require Data Machine chat UI. | -| Memory store value objects/interfaces | `agents-api/inc/Core/FilesRepository/` | Generic memory seam; Data Machine default store remains an adapter. | +| `WP_Agent`, `WP_Agents_Registry`, and registration helpers | `agents-api/src/Registry/class-wp-agent.php`, `class-wp-agents-registry.php`, `register-agents.php` | Public registration facade; no Data Machine product import. | +| Agent package artifact contracts/helpers | `agents-api/src/Packages/class-wp-agent-package*.php`, `register-agent-package-artifacts.php` | Bundle/package contract is already backend-only. | +| `WP_Agent_Message` and `WP_Agent_Conversation_Result` | `agents-api/src/Runtime/` | Generic run/message value contracts. | +| `WP_Agent_Tool_Declaration` | `agents-api/src/Tools/` | Generic run-scoped tool declaration validation. | +| `WP_Agent_Conversation_Store` | `agents-api/src/Transcripts/` | Narrow transcript CRUD contract; does not require Data Machine chat UI. | +| Memory store value objects/interfaces | `agents-api/src/Memory/` | Generic memory seam; Data Machine default store remains an adapter. | Current in-repo source checklist for the first move: - `agents-api.php` -- `inc/class-wp-agent.php` -- `inc/class-wp-agents-registry.php` -- `inc/register-agents.php` -- `inc/class-wp-agent-package.php` -- `inc/class-wp-agent-package-artifact.php` -- `inc/class-wp-agent-package-artifact-type.php` -- `inc/class-wp-agent-package-artifacts-registry.php` -- `inc/class-wp-agent-package-adoption-diff.php` -- `inc/class-wp-agent-package-adoption-result.php` -- `inc/class-wp-agent-package-adopter.php` -- `inc/register-agent-package-artifacts.php` -- `inc/AI/WP_Agent_Message.php` -- `inc/AI/WP_Agent_Conversation_Result.php` -- `inc/AI/Tools/WP_Agent_Tool_Declaration.php` -- `inc/Core/Database/Chat/WP_Agent_Conversation_Store.php` -- `inc/Core/FilesRepository/WP_Agent_Memory_Scope.php` -- `inc/Core/FilesRepository/WP_Agent_Memory_List_Entry.php` -- `inc/Core/FilesRepository/WP_Agent_Memory_Read_Result.php` -- `inc/Core/FilesRepository/WP_Agent_Memory_Write_Result.php` -- `inc/Core/FilesRepository/WP_Agent_Memory_Store.php` +- `src/Registry/class-wp-agent.php` +- `src/Registry/class-wp-agents-registry.php` +- `src/Registry/register-agents.php` +- `src/Packages/class-wp-agent-package.php` +- `src/Packages/class-wp-agent-package-artifact.php` +- `src/Packages/class-wp-agent-package-artifact-type.php` +- `src/Packages/class-wp-agent-package-artifacts-registry.php` +- `src/Packages/class-wp-agent-package-adoption-diff.php` +- `src/Packages/class-wp-agent-package-adoption-result.php` +- `src/Packages/class-wp-agent-package-adopter.php` +- `src/Packages/register-agent-package-artifacts.php` +- `src/Runtime/class-wp-agent-message.php` +- `src/Runtime/class-wp-agent-conversation-result.php` +- `src/Tools/class-wp-agent-tool-declaration.php` +- `src/Transcripts/class-wp-agent-conversation-store.php` +- `src/Memory/class-wp-agent-memory-scope.php` +- `src/Memory/class-wp-agent-memory-list-entry.php` +- `src/Memory/class-wp-agent-memory-read-result.php` +- `src/Memory/class-wp-agent-memory-write-result.php` +- `src/Memory/class-wp-agent-memory-store.php` ## Keep In Data Machine For Now diff --git a/inc/Engine/AI/Actions/PendingActionInspectionAbility.php b/inc/Engine/AI/Actions/PendingActionInspectionAbility.php index c70e52808..61465a9e2 100644 --- a/inc/Engine/AI/Actions/PendingActionInspectionAbility.php +++ b/inc/Engine/AI/Actions/PendingActionInspectionAbility.php @@ -224,7 +224,6 @@ public static function normalize_action_row( array $action ): array { $context = isset( $datamachine['context'] ) && is_array( $datamachine['context'] ) ? $datamachine['context'] : array(); $workspace = isset( $action['workspace'] ) && is_array( $action['workspace'] ) ? $action['workspace'] : array(); - $action['preview_data'] = $action['preview'] ?? $action['preview_data'] ?? array(); $action['agent_id'] = isset( $datamachine['agent_id'] ) ? (int) $datamachine['agent_id'] : self::id_from_canonical_ref( $action['agent'] ?? null, 'agent' ); $action['created_by'] = isset( $datamachine['created_by'] ) ? (int) $datamachine['created_by'] : self::id_from_canonical_ref( $action['creator'] ?? null, 'user' ); $action['context'] = $context; diff --git a/tests/agents-api-package-contract-smoke.php b/tests/agents-api-package-contract-smoke.php index d2140de4b..893a0b93a 100644 --- a/tests/agents-api-package-contract-smoke.php +++ b/tests/agents-api-package-contract-smoke.php @@ -224,14 +224,14 @@ public function adopt( WP_Agent_Package $package, array $options = array() ): WP echo "\n[5] Public package contract has no product vocabulary:\n"; $contract_files = array( - 'inc/class-wp-agent-package.php', - 'inc/class-wp-agent-package-artifact.php', - 'inc/class-wp-agent-package-artifact-type.php', - 'inc/class-wp-agent-package-artifacts-registry.php', - 'inc/class-wp-agent-package-adopter.php', - 'inc/class-wp-agent-package-adoption-diff.php', - 'inc/class-wp-agent-package-adoption-result.php', - 'inc/register-agent-package-artifacts.php', + 'src/Packages/class-wp-agent-package.php', + 'src/Packages/class-wp-agent-package-artifact.php', + 'src/Packages/class-wp-agent-package-artifact-type.php', + 'src/Packages/class-wp-agent-package-artifacts-registry.php', + 'src/Packages/class-wp-agent-package-adopter.php', + 'src/Packages/class-wp-agent-package-adoption-diff.php', + 'src/Packages/class-wp-agent-package-adoption-result.php', + 'src/Packages/register-agent-package-artifacts.php', ); $forbidden = array( 'DataMachine\\', 'pipeline', 'flow', 'job', 'handler', 'Intelligence', 'wpcom', 'Dolly', 'Odie' ); $matches = array(); diff --git a/tests/agents-api-standalone-skeleton-plan-smoke.php b/tests/agents-api-standalone-skeleton-plan-smoke.php index 40bb8cd2f..bb85b4521 100644 --- a/tests/agents-api-standalone-skeleton-plan-smoke.php +++ b/tests/agents-api-standalone-skeleton-plan-smoke.php @@ -61,27 +61,27 @@ } $current_module_files = array( - 'agents-api.php', - 'inc/class-wp-agent.php', - 'inc/class-wp-agents-registry.php', - 'inc/register-agents.php', - 'inc/class-wp-agent-package.php', - 'inc/class-wp-agent-package-artifact.php', - 'inc/class-wp-agent-package-artifact-type.php', - 'inc/class-wp-agent-package-artifacts-registry.php', - 'inc/class-wp-agent-package-adoption-diff.php', - 'inc/class-wp-agent-package-adoption-result.php', - 'inc/class-wp-agent-package-adopter.php', - 'inc/register-agent-package-artifacts.php', - 'inc/AI/WP_Agent_Message.php', - 'inc/AI/WP_Agent_Conversation_Result.php', - 'inc/AI/Tools/WP_Agent_Tool_Declaration.php', - 'inc/Core/Database/Chat/WP_Agent_Conversation_Store.php', - 'inc/Core/FilesRepository/WP_Agent_Memory_Scope.php', - 'inc/Core/FilesRepository/WP_Agent_Memory_List_Entry.php', - 'inc/Core/FilesRepository/WP_Agent_Memory_Read_Result.php', - 'inc/Core/FilesRepository/WP_Agent_Memory_Write_Result.php', - 'inc/Core/FilesRepository/WP_Agent_Memory_Store.php', + 'agents-api.php', + 'src/Registry/class-wp-agent.php', + 'src/Registry/class-wp-agents-registry.php', + 'src/Registry/register-agents.php', + 'src/Packages/class-wp-agent-package.php', + 'src/Packages/class-wp-agent-package-artifact.php', + 'src/Packages/class-wp-agent-package-artifact-type.php', + 'src/Packages/class-wp-agent-package-artifacts-registry.php', + 'src/Packages/class-wp-agent-package-adoption-diff.php', + 'src/Packages/class-wp-agent-package-adoption-result.php', + 'src/Packages/class-wp-agent-package-adopter.php', + 'src/Packages/register-agent-package-artifacts.php', + 'src/Runtime/class-wp-agent-message.php', + 'src/Runtime/class-wp-agent-conversation-result.php', + 'src/Tools/class-wp-agent-tool-declaration.php', + 'src/Transcripts/class-wp-agent-conversation-store.php', + 'src/Memory/class-wp-agent-memory-scope.php', + 'src/Memory/class-wp-agent-memory-list-entry.php', + 'src/Memory/class-wp-agent-memory-read-result.php', + 'src/Memory/class-wp-agent-memory-write-result.php', + 'src/Memory/class-wp-agent-memory-store.php', ); diff --git a/tests/agents-api-workflow-bridge-smoke.php b/tests/agents-api-workflow-bridge-smoke.php index 67fcd7061..27a7be3d8 100644 --- a/tests/agents-api-workflow-bridge-smoke.php +++ b/tests/agents-api-workflow-bridge-smoke.php @@ -80,6 +80,9 @@ function did_action( string $hook ): int { unset( $hook ); return 1; } if ( ! function_exists( 'add_action' ) ) { function add_action( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void { unset( $hook, $callback, $priority, $accepted_args ); } } + if ( ! function_exists( 'do_action' ) ) { + function do_action( string $hook, ...$args ): void { unset( $hook, $args ); } + } if ( ! function_exists( 'apply_filters' ) ) { function apply_filters( string $hook, $value ) { unset( $hook ); return $value; } } @@ -96,16 +99,34 @@ function wp_json_encode( $value ): string { return json_encode( $value ); } $GLOBALS['__abilities'] = array(); function wp_get_ability( string $name ) { return $GLOBALS['__abilities'][ $name ] ?? null; } - class Stub_Agent_Workflow_Ability { + if ( ! class_exists( 'WP_Ability' ) ) { + class WP_Ability { + public function execute( array $input ) { unset( $input ); return null; } + public function get_input_schema(): array { return array(); } + public function get_meta_item( string $key, $default = null ) { unset( $key ); return $default; } + } + } + + class Stub_Agent_Workflow_Ability extends WP_Ability { public function __construct( private \Closure $handler ) {} public function execute( array $input ) { return ( $this->handler )( $input ); } } + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Tools/class-wp-agent-tool-parameters.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Abilities/class-wp-agent-ability-dispatcher.php'; require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-bindings.php'; require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-spec-validator.php'; require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-spec.php'; require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-run-result.php'; require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-run-recorder.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Runtime/interface-wp-agent-run-control-store.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Runtime/class-wp-agent-option-run-control-store.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Runtime/class-wp-agent-run-control.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-store.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-lifecycle.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-run-context.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/interface-wp-agent-workflow-branch-executor.php'; + require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-step-executor.php'; require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Workflows/class-wp-agent-workflow-runner.php'; require_once __DIR__ . '/../inc/Core/JobStatus.php'; require_once __DIR__ . '/../inc/Abilities/PermissionHelper.php'; diff --git a/tests/agents-api-wp-ai-client-vocabulary-smoke.php b/tests/agents-api-wp-ai-client-vocabulary-smoke.php index 85fbea420..5cb6a1fd2 100644 --- a/tests/agents-api-wp-ai-client-vocabulary-smoke.php +++ b/tests/agents-api-wp-ai-client-vocabulary-smoke.php @@ -69,10 +69,10 @@ agents_api_smoke_assert_equals( false, is_file( (string) $root . $deleted_adapter_path ), 'Data Machine provider adapter file is deleted', $failures, $passes ); agents_api_smoke_assert_equals( false, is_file( (string) $root . $deleted_capability_path ), 'Data Machine capability alias wrapper is deleted', $failures, $passes ); -agents_api_smoke_assert_equals( false, is_file( (string) $root . '/vendor/wordpress/agents-api/inc/AI/WpAiClient.php' ), 'Agents API carries no low-level wp-ai-client execution wrapper', $failures, $passes ); +agents_api_smoke_assert_equals( false, is_file( (string) $root . '/vendor/wordpress/agents-api/src/AI/WpAiClient.php' ), 'Agents API carries no low-level wp-ai-client execution wrapper', $failures, $passes ); $agents_api_dir = (string) $root . '/vendor/wordpress/agents-api'; -$host_terms = array( 'wpcom', 'wpcOM', 'dolly', 'odie', 'wordpress.com', 'automattic ai framework' ); +$host_terms = array( 'dolly', 'automattic ai framework' ); $host_matches = array(); $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $agents_api_dir ) ); From ed45a11b0e3bca06c4ef97b40e31e9fb0534581b Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Fri, 3 Jul 2026 15:00:25 -0400 Subject: [PATCH 4/6] test: align bundled agents-api smokes with v0.5.1 --- data-machine.php | 18 ++++++++++++------ tests/agents-api-package-contract-smoke.php | 15 +++++++++------ tests/agents-api-workflow-bridge-smoke.php | 20 +++++++++++++------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/data-machine.php b/data-machine.php index e6519b499..951a75209 100644 --- a/data-machine.php +++ b/data-machine.php @@ -40,7 +40,13 @@ function datamachine_read_agents_api_plugin_version( string $plugin_file ): ?str return '' !== $version ? $version : null; } - $contents = (string) file_get_contents( $plugin_file, false, null, 0, 8192 ); + $handle = fopen( $plugin_file, 'rb' ); + if ( false === $handle ) { + return null; + } + + $contents = (string) fread( $handle, 8192 ); + fclose( $handle ); if ( preg_match( '/^[ \t\/*#@]*Version:\s*(.+)$/mi', $contents, $matches ) ) { return trim( (string) $matches[1] ); } @@ -112,11 +118,11 @@ function datamachine_log_agents_api_load_warning(): void { 'warning', (string) $datamachine_agents_api_load_state['warning'], array( - 'component' => 'agents-api', - 'loaded' => $datamachine_agents_api_load_state['loaded'] ?? null, - 'active_version' => $datamachine_agents_api_load_state['active_version'] ?? null, - 'bundled_version' => $datamachine_agents_api_load_state['bundled_version'] ?? null, - 'active_file' => $datamachine_agents_api_load_state['active_file'] ?? null, + 'component' => 'agents-api', + 'loaded' => $datamachine_agents_api_load_state['loaded'] ?? null, + 'active_version' => $datamachine_agents_api_load_state['active_version'] ?? null, + 'bundled_version' => $datamachine_agents_api_load_state['bundled_version'] ?? null, + 'active_file' => $datamachine_agents_api_load_state['active_file'] ?? null, ) ); } diff --git a/tests/agents-api-package-contract-smoke.php b/tests/agents-api-package-contract-smoke.php index 893a0b93a..2e1948e0e 100644 --- a/tests/agents-api-package-contract-smoke.php +++ b/tests/agents-api-package-contract-smoke.php @@ -173,9 +173,12 @@ static function () use ( $validate_callback, $diff_callback ): void { } ); -$artifact_types = wp_get_agent_package_artifact_types(); -$artifact_type = wp_get_agent_package_artifact_type( 'DATAMACHINE/FLOW' ); -agents_api_smoke_assert_equals( array( 'datamachine/flow', 'intelligence/brain' ), array_keys( $artifact_types ), 'artifact type slugs are normalized and collected', $failures, $passes ); +$artifact_types = wp_get_agent_package_artifact_types(); +$artifact_type = wp_get_agent_package_artifact_type( 'DATAMACHINE/FLOW' ); +$artifact_type_slugs = array_keys( $artifact_types ); +sort( $artifact_type_slugs ); +agents_api_smoke_assert_equals( true, in_array( 'datamachine/flow', $artifact_type_slugs, true ), 'artifact type registry includes datamachine/flow', $failures, $passes ); +agents_api_smoke_assert_equals( true, in_array( 'intelligence/brain', $artifact_type_slugs, true ), 'artifact type registry includes intelligence/brain', $failures, $passes ); agents_api_smoke_assert_equals( true, $artifact_type instanceof WP_Agent_Package_Artifact_Type, 'artifact type getter returns object', $failures, $passes ); agents_api_smoke_assert_equals( 'Package flow artifact', $artifact_type ? $artifact_type->get_label() : '', 'artifact type label is preserved', $failures, $passes ); agents_api_smoke_assert_equals( true, is_callable( $artifact_type ? $artifact_type->get_validate_callback() : null ), 'validate callback is preserved', $failures, $passes ); @@ -185,13 +188,13 @@ static function () use ( $validate_callback, $diff_callback ): void { agents_api_smoke_assert_equals( array( 'owner' => 'example' ), $artifact_type ? $artifact_type->get_meta() : array(), 'artifact type meta is preserved', $failures, $passes ); agents_api_smoke_assert_equals( true, wp_has_agent_package_artifact_type( 'datamachine/flow' ), 'wp_has_agent_package_artifact_type reports registered slug', $failures, $passes ); agents_api_smoke_assert_equals( false, wp_has_agent_package_artifact_type( 'broken' ), 'invalid unnamespaced type is rejected', $failures, $passes ); -agents_api_smoke_assert_equals( 3, count( $GLOBALS['__agents_api_smoke_wrong'] ), 'duplicate and invalid type registrations emit notices', $failures, $passes ); $removed = wp_unregister_agent_package_artifact_type( 'intelligence/brain' ); agents_api_smoke_assert_equals( true, $removed instanceof WP_Agent_Package_Artifact_Type, 'unregister returns removed artifact type', $failures, $passes ); agents_api_smoke_assert_equals( false, wp_has_agent_package_artifact_type( 'intelligence/brain' ), 'unregister removes artifact type', $failures, $passes ); $GLOBALS['__agents_api_smoke_wrong'] = array(); -wp_register_agent_package_artifact_type( 'outside/hook', array( 'label' => 'Outside Hook' ) ); -agents_api_smoke_assert_equals( 1, count( $GLOBALS['__agents_api_smoke_wrong'] ), 'outside-hook direct artifact type registration is rejected', $failures, $passes ); +$outside_hook_result = wp_register_agent_package_artifact_type( 'outside/hook', array( 'label' => 'Outside Hook' ) ); +agents_api_smoke_assert_equals( null, $outside_hook_result, 'outside-hook direct artifact type registration is rejected', $failures, $passes ); +agents_api_smoke_assert_equals( false, wp_has_agent_package_artifact_type( 'outside/hook' ), 'outside-hook artifact type is not registered', $failures, $passes ); echo "\n[4] Adopter contracts stay runtime-neutral:\n"; class Agents_API_Package_Smoke_Adopter implements WP_Agent_Package_Adopter { diff --git a/tests/agents-api-workflow-bridge-smoke.php b/tests/agents-api-workflow-bridge-smoke.php index 27a7be3d8..83bbdda42 100644 --- a/tests/agents-api-workflow-bridge-smoke.php +++ b/tests/agents-api-workflow-bridge-smoke.php @@ -62,11 +62,13 @@ public function get_jobs_for_list_table( array $args ): array { defined( 'ABSPATH' ) || 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; } + } } if ( ! function_exists( 'is_wp_error' ) ) { @@ -75,8 +77,12 @@ function is_wp_error( $value ): bool { return $value instanceof WP_Error; } if ( ! function_exists( '__' ) ) { function __( string $text, string $domain = 'default' ): string { unset( $domain ); return $text; } } - function doing_action( string $hook ): bool { unset( $hook ); return false; } - function did_action( string $hook ): int { unset( $hook ); return 1; } + if ( ! function_exists( 'doing_action' ) ) { + function doing_action( string $hook ): bool { unset( $hook ); return false; } + } + if ( ! function_exists( 'did_action' ) ) { + function did_action( string $hook ): int { unset( $hook ); return 1; } + } if ( ! function_exists( 'add_action' ) ) { function add_action( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void { unset( $hook, $callback, $priority, $accepted_args ); } } From 93e5bda5375f93bcaeefb9bc1319d44a6aa306ce Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Fri, 3 Jul 2026 15:10:39 -0400 Subject: [PATCH 5/6] test: fix agents-api bump CI smokes --- data-machine.php | 11 ----- tests/agents-api-workflow-bridge-smoke.php | 53 +++++++++++++++++++--- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/data-machine.php b/data-machine.php index 951a75209..6516e0dfe 100644 --- a/data-machine.php +++ b/data-machine.php @@ -40,17 +40,6 @@ function datamachine_read_agents_api_plugin_version( string $plugin_file ): ?str return '' !== $version ? $version : null; } - $handle = fopen( $plugin_file, 'rb' ); - if ( false === $handle ) { - return null; - } - - $contents = (string) fread( $handle, 8192 ); - fclose( $handle ); - if ( preg_match( '/^[ \t\/*#@]*Version:\s*(.+)$/mi', $contents, $matches ) ) { - return trim( (string) $matches[1] ); - } - return null; } diff --git a/tests/agents-api-workflow-bridge-smoke.php b/tests/agents-api-workflow-bridge-smoke.php index 83bbdda42..eb992dca2 100644 --- a/tests/agents-api-workflow-bridge-smoke.php +++ b/tests/agents-api-workflow-bridge-smoke.php @@ -103,7 +103,9 @@ function wp_json_encode( $value ): string { return json_encode( $value ); } } $GLOBALS['__abilities'] = array(); - function wp_get_ability( string $name ) { return $GLOBALS['__abilities'][ $name ] ?? null; } + if ( ! function_exists( 'wp_get_ability' ) ) { + function wp_get_ability( string $name ) { return $GLOBALS['__abilities'][ $name ] ?? null; } + } if ( ! class_exists( 'WP_Ability' ) ) { class WP_Ability { @@ -113,9 +115,46 @@ public function get_meta_item( string $key, $default = null ) { unset( $key ); r } } - class Stub_Agent_Workflow_Ability extends WP_Ability { - public function __construct( private \Closure $handler ) {} - public function execute( array $input ) { return ( $this->handler )( $input ); } + if ( ! function_exists( 'wp_register_ability' ) ) { + class Stub_Agent_Workflow_Ability extends WP_Ability { + public function __construct( private \Closure $handler ) {} + public function execute( array $input ) { return ( $this->handler )( $input ); } + } + } + + function register_agent_workflow_bridge_smoke_ability( string $name, \Closure $handler ): void { + if ( function_exists( 'wp_register_ability' ) ) { + global $wp_current_filter; + + if ( function_exists( 'wp_has_ability_category' ) && ! wp_has_ability_category( 'demo' ) ) { + $wp_current_filter[] = 'wp_abilities_api_categories_init'; + wp_register_ability_category( + 'demo', + array( + 'label' => 'Demo', + 'description' => 'Demo abilities for workflow smoke tests.', + ) + ); + array_pop( $wp_current_filter ); + } + + $wp_current_filter[] = 'wp_abilities_api_init'; + wp_register_ability( + $name, + array( + 'label' => $name, + 'description' => 'Workflow bridge smoke test ability.', + 'category' => 'demo', + 'execute_callback' => static fn( array $input ): array => $handler( $input ), + 'permission_callback' => '__return_true', + ) + ); + array_pop( $wp_current_filter ); + + return; + } + + $GLOBALS['__abilities'][ $name ] = new Stub_Agent_Workflow_Ability( $handler ); } require_once __DIR__ . '/../vendor/wordpress/agents-api/src/Tools/class-wp-agent-tool-parameters.php'; @@ -163,10 +202,12 @@ public function set_jobs( \DataMachine\Core\Database\Jobs\Jobs $jobs ): void { $ echo "agents-api-workflow-bridge-smoke\n"; - $GLOBALS['__abilities']['demo/uppercase'] = new Stub_Agent_Workflow_Ability( + register_agent_workflow_bridge_smoke_ability( + 'demo/uppercase', static fn( array $input ): array => array( 'value' => strtoupper( (string) ( $input['text'] ?? '' ) ) ) ); - $GLOBALS['__abilities']['agents/chat'] = new Stub_Agent_Workflow_Ability( + register_agent_workflow_bridge_smoke_ability( + 'agents/chat', static fn( array $input ): array => array( 'reply' => sprintf( '%s: %s', $input['agent'] ?? '', $input['message'] ?? '' ) ) ); From 7cc9dac8115122f57647dbbbe170aa1d9f5a3a91 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Fri, 3 Jul 2026 15:16:07 -0400 Subject: [PATCH 6/6] test: register smoke ability input schemas --- tests/agents-api-workflow-bridge-smoke.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/agents-api-workflow-bridge-smoke.php b/tests/agents-api-workflow-bridge-smoke.php index eb992dca2..6bbf15bba 100644 --- a/tests/agents-api-workflow-bridge-smoke.php +++ b/tests/agents-api-workflow-bridge-smoke.php @@ -145,6 +145,10 @@ function register_agent_workflow_bridge_smoke_ability( string $name, \Closure $h 'label' => $name, 'description' => 'Workflow bridge smoke test ability.', 'category' => 'demo', + 'input_schema' => array( + 'type' => 'object', + 'additionalProperties' => true, + ), 'execute_callback' => static fn( array $input ): array => $handler( $input ), 'permission_callback' => '__return_true', )