diff --git a/README.md b/README.md index a6802fb8c..d6918ff68 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ Agent memory files (MEMORY.md, SOUL.md, USER.md, NETWORK.md, AGENTS.md, plus any add_filter( 'agents_api_memory_store', function ( $store, $scope ) { - // Return an AgentMemoryStoreInterface to replace the disk default + // Return an WP_Agent_Memory_Store to replace the disk default // for this scope, or null to let Data Machine read/write through // the filesystem. return new My_DB_Agent_Memory_Store(); diff --git a/composer.json b/composer.json index 969e8ae05..ec49f6db0 100644 --- a/composer.json +++ b/composer.json @@ -6,10 +6,10 @@ "require": { "php": ">=8.2", "woocommerce/action-scheduler": "^3.9", - "automattic/agents-api": "dev-main", "chubes4/block-format-bridge": "^0.6" }, "require-dev": { + "automattic/agents-api": "dev-main", "php-stubs/wordpress-stubs": "^6.9", "wp-coding-standards/wpcs": "^3.1", "phpcsstandards/phpcsutils": "^1.0", diff --git a/composer.lock b/composer.lock index 276985dd3..bb292ef53 100644 --- a/composer.lock +++ b/composer.lock @@ -4,75 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f74eed0e0e85754e1a534a824f6e862a", + "content-hash": "3b4f18f541cdb5ef9fd8ecf4674d82f6", "packages": [ - { - "name": "automattic/agents-api", - "version": "dev-main", - "source": { - "type": "git", - "url": "https://github.com/Automattic/agents-api.git", - "reference": "3697c0414950633fcb86a5654df07bc3de173b94" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Automattic/agents-api/zipball/3697c0414950633fcb86a5654df07bc3de173b94", - "reference": "3697c0414950633fcb86a5654df07bc3de173b94", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "default-branch": true, - "type": "wordpress-plugin", - "scripts": { - "test": [ - "php tests/bootstrap-smoke.php", - "php tests/message-envelope-smoke.php", - "php tests/registry-smoke.php", - "php tests/execution-principal-smoke.php", - "php tests/caller-context-smoke.php", - "php tests/authorization-smoke.php", - "php tests/action-policy-values-smoke.php", - "php tests/consent-policy-smoke.php", - "php tests/tool-policy-contracts-smoke.php", - "php tests/action-policy-resolver-smoke.php", - "php tests/tool-runtime-smoke.php", - "php tests/pending-action-store-contract-smoke.php", - "php tests/approval-resolver-contract-smoke.php", - "php tests/identity-smoke.php", - "php tests/memory-metadata-contract-smoke.php", - "php tests/approval-action-value-shape-smoke.php", - "php tests/workspace-scope-smoke.php", - "php tests/compaction-item-smoke.php", - "php tests/conversation-runner-contracts-smoke.php", - "php tests/conversation-transcript-lock-smoke.php", - "php tests/conversation-compaction-smoke.php", - "php tests/markdown-section-compaction-smoke.php", - "php tests/context-registry-smoke.php", - "php tests/conversation-loop-smoke.php", - "php tests/conversation-loop-tool-execution-smoke.php", - "php tests/conversation-loop-completion-policy-smoke.php", - "php tests/conversation-loop-transcript-persister-smoke.php", - "php tests/conversation-loop-events-smoke.php", - "php tests/iteration-budget-smoke.php", - "php tests/conversation-loop-budgets-smoke.php", - "php tests/context-authority-smoke.php", - "php tests/guidelines-substrate-smoke.php", - "php tests/no-product-imports-smoke.php" - ] - }, - "license": [ - "GPL-2.0-or-later" - ], - "description": "WordPress-shaped agent runtime substrate.", - "homepage": "https://github.com/Automattic/agents-api", - "support": { - "issues": "https://github.com/Automattic/agents-api/issues", - "source": "https://github.com/Automattic/agents-api" - }, - "time": "2026-05-04T19:27:48+00:00" - }, { "name": "chubes4/block-format-bridge", "version": "v0.6.6", @@ -881,6 +814,73 @@ } ], "packages-dev": [ + { + "name": "automattic/agents-api", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/Automattic/agents-api.git", + "reference": "ec60bd98ce25f4d4cc4481cc08e6377a48e741b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Automattic/agents-api/zipball/ec60bd98ce25f4d4cc4481cc08e6377a48e741b0", + "reference": "ec60bd98ce25f4d4cc4481cc08e6377a48e741b0", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "default-branch": true, + "type": "wordpress-plugin", + "scripts": { + "test": [ + "php tests/bootstrap-smoke.php", + "php tests/message-envelope-smoke.php", + "php tests/registry-smoke.php", + "php tests/execution-principal-smoke.php", + "php tests/caller-context-smoke.php", + "php tests/authorization-smoke.php", + "php tests/action-policy-values-smoke.php", + "php tests/consent-policy-smoke.php", + "php tests/tool-policy-contracts-smoke.php", + "php tests/action-policy-resolver-smoke.php", + "php tests/tool-runtime-smoke.php", + "php tests/pending-action-store-contract-smoke.php", + "php tests/approval-resolver-contract-smoke.php", + "php tests/identity-smoke.php", + "php tests/memory-metadata-contract-smoke.php", + "php tests/approval-action-value-shape-smoke.php", + "php tests/workspace-scope-smoke.php", + "php tests/compaction-item-smoke.php", + "php tests/conversation-runner-contracts-smoke.php", + "php tests/conversation-transcript-lock-smoke.php", + "php tests/conversation-compaction-smoke.php", + "php tests/markdown-section-compaction-smoke.php", + "php tests/context-registry-smoke.php", + "php tests/conversation-loop-smoke.php", + "php tests/conversation-loop-tool-execution-smoke.php", + "php tests/conversation-loop-completion-policy-smoke.php", + "php tests/conversation-loop-transcript-persister-smoke.php", + "php tests/conversation-loop-events-smoke.php", + "php tests/iteration-budget-smoke.php", + "php tests/conversation-loop-budgets-smoke.php", + "php tests/context-authority-smoke.php", + "php tests/guidelines-substrate-smoke.php", + "php tests/no-product-imports-smoke.php" + ] + }, + "license": [ + "GPL-2.0-or-later" + ], + "description": "WordPress-shaped agent runtime substrate.", + "homepage": "https://github.com/Automattic/agents-api", + "support": { + "issues": "https://github.com/Automattic/agents-api/issues", + "source": "https://github.com/Automattic/agents-api" + }, + "time": "2026-05-05T01:09:16+00:00" + }, { "name": "dealerdirect/phpcodesniffer-composer-installer", "version": "v1.2.0", diff --git a/data-machine.php b/data-machine.php index e1d661868..11b4c99b9 100644 --- a/data-machine.php +++ b/data-machine.php @@ -6,6 +6,7 @@ * Version: 0.103.14 * Requires at least: 6.9 * Requires PHP: 8.2 + * Requires Plugins: agents-api * Author: Chris Huber, extrachill * Author URI: https://chubes.net * License: GPL v2 or later @@ -28,13 +29,6 @@ require_once __DIR__ . '/vendor/autoload.php'; -if ( ! defined( 'AGENTS_API_LOADED' ) ) { - $datamachine_agents_api_bootstrap = __DIR__ . '/vendor/automattic/agents-api/agents-api.php'; - if ( file_exists( $datamachine_agents_api_bootstrap ) ) { - require_once $datamachine_agents_api_bootstrap; - } -} - // WP-CLI integration if ( defined( 'WP_CLI' ) && WP_CLI ) { require_once __DIR__ . '/inc/Cli/Bootstrap.php'; diff --git a/docs/ai-tools/tools-overview.md b/docs/ai-tools/tools-overview.md index e9833b0ea..8b3f693dd 100644 --- a/docs/ai-tools/tools-overview.md +++ b/docs/ai-tools/tools-overview.md @@ -519,11 +519,11 @@ add_filter('datamachine_tool_enabled', function($enabled, $tool_id, $agent_type) ### Parameter Building -`ToolParameters` (`/inc/Engine/AI/Tools/ToolParameters.php`) provides unified parameter construction: +`WP_Agent_Tool_Parameters` (`/inc/Engine/AI/Tools/ToolParameters.php`) provides unified parameter construction: **Standard Tools** (global tools): ```php -$parameters = \DataMachine\Engine\AI\ToolParameters::buildParameters( +$parameters = \DataMachine\Engine\AI\WP_Agent_Tool_Parameters::buildParameters( $data, $job_id, $flow_step_id @@ -533,7 +533,7 @@ $parameters = \DataMachine\Engine\AI\ToolParameters::buildParameters( **Handler Tools** (publish/upsert handlers): ```php -$parameters = \DataMachine\Engine\AI\ToolParameters::buildForHandlerTool( +$parameters = \DataMachine\Engine\AI\WP_Agent_Tool_Parameters::buildForHandlerTool( $data, $tool_def, $job_id, @@ -658,7 +658,7 @@ AI agents receive available tools based on: - Handler tool and global tool integration - Tool configuration validation -**ToolParameters** (`/inc/Engine/AI/Tools/ToolParameters.php`): +**WP_Agent_Tool_Parameters** (`/inc/Engine/AI/Tools/ToolParameters.php`): - Centralized parameter building for all AI tools - `buildParameters()` for standard AI tools with clean data extraction - `buildForHandlerTool()` for handler tools with engine parameters (source_url, image_url) diff --git a/docs/api/endpoints/chat.md b/docs/api/endpoints/chat.md index 6ee5bac70..1b81df93f 100644 --- a/docs/api/endpoints/chat.md +++ b/docs/api/endpoints/chat.md @@ -224,7 +224,7 @@ The Chat endpoint uses the Universal Engine architecture at `/inc/Engine/AI/` fo - Tool call/result message generation - Duplicate detection logic -**ToolParameters** +**WP_Agent_Tool_Parameters** - Unified parameter building for tools - Automatic content/title extraction - Session context integration @@ -290,7 +290,7 @@ $context = [ Used by: - RequestBuilder for directive application - ToolExecutor for tool execution -- ToolParameters for parameter building +- WP_Agent_Tool_Parameters for parameter building ### Tool Discovery diff --git a/docs/api/endpoints/parameter-systems.md b/docs/api/endpoints/parameter-systems.md index f95e37331..9df45717a 100644 --- a/docs/api/endpoints/parameter-systems.md +++ b/docs/api/endpoints/parameter-systems.md @@ -11,7 +11,7 @@ Data Machine uses an engine data filter architecture that provides clean data se 1. **Engine Data Propagation** - Fetch handlers store engine data via centralized filters; engine bundles the data into the payload passed to every step and tool 2. **Clean Data Separation** - AI receives clean data packets without URLs; handlers receive engine parameters via the payload-supplied engine data 3. **Unified Interface** - All steps, handlers, and tools use consistent parameter formats with required `job_id` for engine data access -4. **Tool-Based Parameter Building** - ToolParameters class (Universal Engine) provides standardized parameter construction +4. **Tool-Based Parameter Building** - WP_Agent_Tool_Parameters class (Universal Engine) provides standardized parameter construction 5. **Required Job ID** - All tools must include `job_id` parameter to enable engine data access and workflow continuity ## Core Payload Structure @@ -117,12 +117,12 @@ class MyFetchHandler { ``` ### Publish Handlers (Tool-Based) -Use `ToolParameters::buildForHandlerTool()` for parameter building with engine data access: +Use `WP_Agent_Tool_Parameters::buildForHandlerTool()` for parameter building with engine data access: ```php class MyPublishHandler { public function handle_tool_call(array $parameters, array $tool_def = []): array { - // Parameters built by ToolParameters::buildParameters() + // Parameters built by WP_Agent_Tool_Parameters::buildParameters() // Already contain: content, title, job_id, flow_step_id, handler_config // Engine data accessed via filter for handler tools @@ -165,7 +165,7 @@ class MyUpdateHandler { ## AI Tool Parameter Building -### ToolParameters Class (Universal Engine) +### WP_Agent_Tool_Parameters Class (Universal Engine) **File**: `/inc/Engine/AI/Tools/ToolParameters.php` **Since**: 0.2.0 @@ -175,7 +175,7 @@ Centralized parameter building for AI tool execution: use DataMachine\Engine\AI\Tools\ToolParameters; // Unified parameter building for all tool types -$parameters = ToolParameters::buildParameters( +$parameters = WP_Agent_Tool_Parameters::buildParameters( $ai_tool_parameters, // Parameters from AI tool call $payload, // Step payload (job_id, flow_step_id, data, flow_step_config, engine_data) $tool_definition // Tool definition array diff --git a/docs/architecture.md b/docs/architecture.md index 76fc72601..8f2cf1de2 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -278,7 +278,7 @@ Data Machine v0.2.0 introduced a universal Engine layer (`/inc/Engine/AI/`) that - **AIConversationLoop**: Multi-turn conversation execution with tool calling, completion detection, and state management - **ToolExecutor**: Universal tool discovery, enablement validation, and execution across agent types -- **ToolParameters**: Centralized parameter building for AI tools with data packet integration +- **WP_Agent_Tool_Parameters**: Centralized parameter building for AI tools with data packet integration - **ConversationManager**: Message formatting and conversation state management - **RequestBuilder**: AI request construction with directive application and tool restructuring - **ToolResultFinder**: Utility for finding tool execution results in data packets @@ -453,7 +453,7 @@ Complete extension system for custom handlers and tools: - **Universal Engine Architecture**: Shared AI infrastructure via `/inc/Engine/AI/` components: - AIConversationLoop for multi-turn conversation execution with automatic tool calling - ToolExecutor for universal tool discovery and execution - - ToolParameters for centralized parameter building (`buildParameters()` for standard tools, `buildForHandlerTool()` for handler tools with engine data) + - WP_Agent_Tool_Parameters for centralized parameter building (`buildParameters()` for standard tools, `buildForHandlerTool()` for handler tools with engine data) - ConversationManager for message formatting and conversation utilities - RequestBuilder for centralized AI request construction with directive application - ToolResultFinder for universal tool result search in data packets diff --git a/docs/architecture/agent-memory-backends.md b/docs/architecture/agent-memory-backends.md index 5c9289cff..e2f1ed00a 100644 --- a/docs/architecture/agent-memory-backends.md +++ b/docs/architecture/agent-memory-backends.md @@ -6,7 +6,7 @@ This lets self-hosted installs keep the current disk-backed workflow while manag ## Logical Identity -Every memory file is addressed by an `AgentMemoryScope` four-tuple: +Every memory file is addressed by an `WP_Agent_Memory_Scope` four-tuple: ```text (layer, user_id, agent_id, filename) @@ -43,7 +43,7 @@ AgentMemoryStoreFactory | +--> DiskAgentMemoryStore (default) | - +--> alternate AgentMemoryStoreInterface implementation + +--> alternate WP_Agent_Memory_Store implementation ``` `MemoryFileRegistry` is the source of truth for which core memory files exist, where they live logically, and which modes receive them. Registration metadata includes the layer, priority, editability, `modes`, whether a file is composable, and optional `convention_path` disk projection metadata. @@ -68,11 +68,11 @@ Data Machine resolves memory persistence through one current Agents API-shaped f apply_filters( 'agents_api_memory_store', null, - AgentMemoryScope $scope + WP_Agent_Memory_Scope $scope ); ``` -Return an `AgentMemoryStoreInterface` implementation to replace the disk default for the given scope. Return `null` to keep `DiskAgentMemoryStore`. +Return an `WP_Agent_Memory_Store` implementation to replace the disk default for the given scope. Return `null` to keep `DiskAgentMemoryStore`. `agents_api_memory_store` replaces the earlier `datamachine_memory_store` name in-place. Data Machine intentionally does not call both filters; consumers should migrate to the Agents API-shaped hook rather than relying on a permanent alias. @@ -92,7 +92,7 @@ DMC should be treated as a projection provider, not the memory model: | Concern | Owner | |---|---| -| Logical memory identity and access | Data Machine (`AgentMemoryScope`, `AgentMemory`) | +| Logical memory identity and access | Data Machine (`WP_Agent_Memory_Scope`, `AgentMemory`) | | Registered memory files and mode-aware injection | Data Machine (`MemoryFileRegistry`, directives) | | Disk file projection for local coding agents | DMC + `DiskAgentMemoryStore` environment capability | | Managed-host alternate backend | Consumer plugin via `agents_api_memory_store` | @@ -118,7 +118,7 @@ The memory-store seam is not a replacement for AI Framework. Data Machine still ## Extension Rules - Read and write memory through `AgentMemory`, not `AgentMemoryStoreFactory` directly. -- Implement `AgentMemoryStoreInterface` when replacing persistence. +- Implement `WP_Agent_Memory_Store` when replacing persistence. - Preserve the four-tuple identity model exactly, even if the physical backend has different keys. - Keep section parsing, scaffolding, editability gating, ability permissions, prompt-injection policy, and registry semantics above the store layer. - Gate guideline-backed stores on real substrate availability; do not assume `wp_guideline` exists. @@ -129,4 +129,4 @@ The memory-store seam is not a replacement for AI Framework. Data Machine still - [WordPress as Persistent Memory for AI Agents](../core-system/wordpress-as-agent-memory.md) - [Memory Policy](../core-system/memory-policy.md) - [Daily Memory System](../core-system/daily-memory-system.md) -- [Core Filters: AgentMemoryStoreInterface](../development/hooks/core-filters.md#agentmemorystoreinterface-inccorefilesrepositoryagentmemorystoreinterfacephp) +- [Core Filters: WP_Agent_Memory_Store](../development/hooks/core-filters.md#agentmemorystoreinterface-inccorefilesrepositoryagentmemorystoreinterfacephp) diff --git a/docs/architecture/iteration-budget.md b/docs/architecture/iteration-budget.md index 1a6c1613e..63f6383ee 100644 --- a/docs/architecture/iteration-budget.md +++ b/docs/architecture/iteration-budget.md @@ -2,14 +2,14 @@ Generic primitive for bounded iteration with a configurable ceiling. Counts a named dimension (conversation turns, A2A chain depth, retry attempts) and exposes a uniform API for checking exceedance, formatting warnings, and surfacing response flags. -**Source**: `inc/Engine/AI/IterationBudget.php`, `inc/Engine/AI/IterationBudgetRegistry.php` +**Source**: `inc/Engine/AI/WP_Agent_Iteration_Budget.php`, `inc/Engine/AI/IterationBudgetRegistry.php` **Since**: v0.71.0 ## Why a primitive Before this primitive, every bounded loop in the codebase invented its own counter and its own "did we hit the limit" check — turn limits in the conversation loop, retry caps in async tasks, chain-depth limits in cross-site A2A. The result was inconsistent thresholds, copy-pasted clamping logic, and four different ways to surface "you ran out of budget" to the AI. -`IterationBudget` is one value object covering all of them. Site config registers a named budget with a default, a site-setting override key, and clamp bounds; runtime code instantiates the budget for the current run and uses the same five methods on every consumer. +`WP_Agent_Iteration_Budget` is one value object covering all of them. Site config registers a named budget with a default, a site-setting override key, and clamp bounds; runtime code instantiates the budget for the current run and uses the same five methods on every consumer. ## Two-stage lifecycle @@ -56,7 +56,7 @@ while ( ! $budget->exceeded() ) { } ``` -`create()` reads the registered config, applies the ceiling-resolution chain, and returns a fresh `IterationBudget` value object. +`create()` reads the registered config, applies the ceiling-resolution chain, and returns a fresh `WP_Agent_Iteration_Budget` value object. ## Ceiling resolution @@ -70,7 +70,7 @@ Then clamped to `[config['min'], config['max']]`. The clamp is non-negotiable ## API surface -`IterationBudget` exposes: +`WP_Agent_Iteration_Budget` exposes: | Method | Purpose | |--------|---------| @@ -140,7 +140,7 @@ When exceeded, the request is refused with HTTP 429 and the error code `datamach Budgets don't share inheritance — they share *shape*. Different consumers count different things and integrate at different layers (an HTTP middleware, an AI loop, a retry helper). A trait or abstract class would force them to agree on lifecycle methods they don't actually share. -`IterationBudget` is a single value-object class with a registry. Every consumer constructs its own, calls the same five methods, and emits the same response-flag convention. That is the contract. Inheritance would add nothing. +`WP_Agent_Iteration_Budget` is a single value-object class with a registry. Every consumer constructs its own, calls the same five methods, and emits the same response-flag convention. That is the contract. Inheritance would add nothing. ## Related diff --git a/docs/core-system/ai-conversation-loop.md b/docs/core-system/ai-conversation-loop.md index 9f5896cc8..93993191a 100644 --- a/docs/core-system/ai-conversation-loop.md +++ b/docs/core-system/ai-conversation-loop.md @@ -7,7 +7,7 @@ Multi-turn conversation execution engine for AI agents. Handles automatic tool e ## Purpose -Data Machine's `datamachine_run_conversation()` adapter provides centralized multi-turn conversation management while delegating generic turn sequencing to `AgentsAPI\AI\AgentConversationLoop`. It orchestrates Data Machine request assembly, product-specific tool execution, turn limits, transcript persistence, and completion detection for Pipeline and Chat agents. +Data Machine's `datamachine_run_conversation()` adapter provides centralized multi-turn conversation management while delegating generic turn sequencing to `AgentsAPI\AI\WP_Agent_Conversation_Loop`. It orchestrates Data Machine request assembly, product-specific tool execution, turn limits, transcript persistence, and completion detection for Pipeline and Chat agents. ## Architecture @@ -109,7 +109,7 @@ if ($turn_count >= $max_turns && !$conversation_complete) { ### Canonical entry point -All callers should use `DataMachine\Engine\AI\datamachine_run_conversation()`. The function builds a Data Machine turn runner and passes it to `AgentsAPI\AI\AgentConversationLoop::run()` so generic lifecycle behavior stays in Agents API while product adapters own Data Machine request and tool semantics. +All callers should use `DataMachine\Engine\AI\datamachine_run_conversation()`. The function builds a Data Machine turn runner and passes it to `AgentsAPI\AI\WP_Agent_Conversation_Loop::run()` so generic lifecycle behavior stays in Agents API while product adapters own Data Machine request and tool semantics. ```php use function DataMachine\Engine\AI\datamachine_run_conversation; diff --git a/docs/core-system/ai-message-envelope.md b/docs/core-system/ai-message-envelope.md index 67416b474..bf1d7ad4d 100644 --- a/docs/core-system/ai-message-envelope.md +++ b/docs/core-system/ai-message-envelope.md @@ -1,6 +1,6 @@ # Agent Message Envelope -**File**: `/agents-api/inc/AI/AgentMessageEnvelope.php` +**File**: `/agents-api/inc/AI/WP_Agent_Message.php` The canonical agent message shape is a JSON-friendly typed envelope. Runtime code, chat storage, and transcript storage should store and return envelopes. @@ -45,13 +45,13 @@ part of the type contract. ## Compatibility -`AgentMessageEnvelope::normalize()` accepts existing `role/content/metadata` rows +`WP_Agent_Message::normalize()` accepts existing `role/content/metadata` rows and versioned envelopes. It also accepts the short-lived `data` envelope key from the initial envelope draft as a read-time compatibility input and rewrites it to `payload`. New writes should store canonical envelopes. Current provider requests use -`AgentMessageEnvelope::to_provider_message()` / `to_provider_messages()` to +`WP_Agent_Message::to_provider_message()` / `to_provider_messages()` to project envelopes into Data Machine's `role/content/metadata` provider-message shape. That projection folds the envelope `type` and `payload` into provider metadata. @@ -104,7 +104,7 @@ shape. ## Adapter Guidance Runtime adapters using `agents_api_conversation_runner` may return messages in -either legacy or envelope shape. `AgentConversationResult::normalize()` normalizes +either legacy or envelope shape. `WP_Agent_Conversation_Result::normalize()` normalizes every returned message to the canonical envelope before callers store or render the result. diff --git a/docs/core-system/files-repository.md b/docs/core-system/files-repository.md index e443244b3..671a2e0d6 100644 --- a/docs/core-system/files-repository.md +++ b/docs/core-system/files-repository.md @@ -13,7 +13,7 @@ The repository covers two responsibility groups: - **Flow file primitives** — `DirectoryManager`, `FileStorage`, `FileRetrieval`, `FileCleanup`, `RemoteFileDownloader`, `FilesystemHelper` - **Validation primitives** — `ImageValidator`, `MediaValidator`, `VideoValidator`, `VideoMetadata` -- **Agent memory primitives** — `AgentMemory`, `AgentMemoryStoreInterface`, `AgentMemoryStoreFactory`, `DiskAgentMemoryStore`, `AgentMemoryScope`, `AgentMemoryReadResult`, `AgentMemoryWriteResult`, `AgentMemoryListEntry`, `DailyMemory`, `DailyMemoryStorage` +- **Agent memory primitives** — `AgentMemory`, `WP_Agent_Memory_Store`, `AgentMemoryStoreFactory`, `DiskAgentMemoryStore`, `WP_Agent_Memory_Scope`, `WP_Agent_Memory_Read_Result`, `WP_Agent_Memory_Write_Result`, `WP_Agent_Memory_List_Entry`, `DailyMemory`, `DailyMemoryStorage` ## Components diff --git a/docs/core-system/pending-actions.md b/docs/core-system/pending-actions.md index 9305ff19b..fe5267925 100644 --- a/docs/core-system/pending-actions.md +++ b/docs/core-system/pending-actions.md @@ -12,14 +12,14 @@ Data Machine implements the Agents API pending-action approval storage contract Data Machine directly adapts to these merged contracts from `automattic/agents-api`: -- `AgentsAPI\AI\Approvals\PendingActionStoreInterface` -- `AgentsAPI\AI\Approvals\PendingActionResolverInterface` -- `AgentsAPI\AI\Approvals\PendingActionHandlerInterface` -- `AgentsAPI\AI\Approvals\ApprovalDecision` +- `AgentsAPI\AI\Approvals\PendingAction_Store` +- `AgentsAPI\AI\Approvals\PendingAction_Resolver` +- `AgentsAPI\AI\Approvals\PendingAction_Handler` +- `AgentsAPI\AI\Approvals\WP_Agent_Approval_Decision` - `AgentsAPI\AI\Approvals\PendingAction` - `AgentsAPI\AI\Tools\ActionPolicy` -The compatibility seam remains the existing `datamachine_pending_action_handlers` map. Handler objects that implement `AgentsAPI\AI\Approvals\PendingActionHandlerInterface` can be placed under the same `apply` key and Data Machine will dispatch to the Agents API handler method. Legacy callable handlers continue to receive the stored `apply_input` and full Data Machine payload. +The compatibility seam remains the existing `datamachine_pending_action_handlers` map. Handler objects that implement `AgentsAPI\AI\Approvals\PendingAction_Handler` can be placed under the same `apply` key and Data Machine will dispatch to the Agents API handler method. Legacy callable handlers continue to receive the stored `apply_input` and full Data Machine payload. ## Surfaces diff --git a/docs/core-system/tool-execution.md b/docs/core-system/tool-execution.md index 08aeb4b72..bd2306756 100644 --- a/docs/core-system/tool-execution.md +++ b/docs/core-system/tool-execution.md @@ -13,7 +13,7 @@ Universal tool discovery, enablement, and execution infrastructure shared by Pip The tool execution architecture consists of two core components: 1. **ToolExecutor** - Tool discovery, validation, and execution -2. **ToolParameters** - Centralized parameter building and merging +2. **WP_Agent_Tool_Parameters** - Centralized parameter building and merging Together, these components ensure consistent tool behavior across all AI agents while supporting flexible, filter-based tool registration. @@ -298,7 +298,7 @@ $result = ToolExecutor::executeTool( **Execution Process**: 1. Validate tool exists in available tools -2. Build complete parameters via `ToolParameters::buildParameters()` +2. Build complete parameters via `WP_Agent_Tool_Parameters::buildParameters()` 3. Instantiate tool class 4. Call `handle_tool_call()` method with complete parameters 5. Return standardized result array @@ -361,7 +361,7 @@ try { } ``` -## ToolParameters +## WP_Agent_Tool_Parameters **File**: `/inc/Engine/AI/Tools/ToolParameters.php` @@ -381,7 +381,7 @@ public static function buildParameters( **Usage**: ```php -$complete_parameters = ToolParameters::buildParameters( +$complete_parameters = WP_Agent_Tool_Parameters::buildParameters( ['query' => 'WordPress SEO tips'], // AI parameters ['session_id' => $session_id], // Unified context $tool_definition // Tool definition array @@ -461,7 +461,7 @@ public static function buildForHandlerTool( **Usage**: ```php -$complete_parameters = ToolParameters::buildForHandlerTool( +$complete_parameters = WP_Agent_Tool_Parameters::buildForHandlerTool( ['content' => 'Tweet text'], // AI parameters $data, // Data packets $tool_definition, // Tool definition diff --git a/docs/core-system/universal-engine.md b/docs/core-system/universal-engine.md index ce31956b5..4e0bdf778 100644 --- a/docs/core-system/universal-engine.md +++ b/docs/core-system/universal-engine.md @@ -29,7 +29,7 @@ The Universal Engine consolidates this shared functionality into a centralized l │ └──────────────────┘ └──────────────────┘ │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ -│ │ ToolParameters │ │ ToolResultFinder │ │ +│ │ WP_Agent_Tool_Parameters │ │ ToolResultFinder │ │ │ │ Parameter │ │ Result search │ │ │ │ building │ │ utility │ │ │ └──────────────────┘ └──────────────────┘ │ @@ -67,7 +67,7 @@ The Universal Engine consists of eight core components that provide shared AI in - **RequestBuilder** - Centralized AI request construction with directive application - **ToolExecutor** - Universal tool discovery, validation, and execution - **ToolManager** - Centralized tool management and validation (@since v0.2.1) -- **ToolParameters** - Standardized parameter building for tool handlers +- **WP_Agent_Tool_Parameters** - Standardized parameter building for tool handlers - **ConversationManager** - Message formatting, validation, and conversation utilities - **ToolResultFinder** - Universal tool result search and interpretation - **BaseTool** - Unified base class for all AI tools with registration and error handling (@since v0.14.10) diff --git a/docs/development/agents-api-duplicated-substrate-inventory.md b/docs/development/agents-api-duplicated-substrate-inventory.md index 584c7bafb..1f779f1a1 100644 --- a/docs/development/agents-api-duplicated-substrate-inventory.md +++ b/docs/development/agents-api-duplicated-substrate-inventory.md @@ -12,12 +12,12 @@ Data Machine now consumes `automattic/agents-api` at `109576b004ef61783456e27ad8 |---|---|---|---| | Guideline capability metadata from Automattic/agents-api#65 | `MemoryFileRegistry` editability/layer metadata, `MemoryPolicyResolver`, `SelfMemoryWritePolicy`, and guideline-backed memory store capability checks | [#1763](https://github.com/Extra-Chill/data-machine/issues/1763) | Adopt capability vocabulary where it gates memory/guideline access; keep Data Machine's concrete file registry, memory commands, and store selection product-owned. | | Consent operations and decisions from Automattic/agents-api#66 | Memory/transcript writes in `AgentMemory`, `DailyMemory`, `ConversationManager`, `DataMachinePipelineTranscriptPersister`, and memory tools/directives currently rely on Data Machine mode/config checks rather than a generic consent policy | [#1760](https://github.com/Extra-Chill/data-machine/issues/1760) | Wire consent checks at Data Machine adapter boundaries only. Do not add compatibility shims for old consent vocabulary because there is no persisted consent contract yet. | -| Workspace scope contracts from Automattic/agents-api#67 | `DirectoryManager`, `DiskAgentMemoryStore`, `GuidelineAgentMemoryStore`, `AgentMemoryScope` usage, and `ConversationStoreFactory::get_transcript_store()` encode user/agent/site identity directly | [#1756](https://github.com/Extra-Chill/data-machine/issues/1756) | Use `AgentWorkspaceScope` to stamp memory, transcripts, and audit records while keeping Data Machine paths, tables, and migration behavior in Data Machine. | +| Workspace scope contracts from Automattic/agents-api#67 | `DirectoryManager`, `DiskAgentMemoryStore`, `GuidelineAgentMemoryStore`, `WP_Agent_Memory_Scope` usage, and `ConversationStoreFactory::get_transcript_store()` encode user/agent/site identity directly | [#1756](https://github.com/Extra-Chill/data-machine/issues/1756) | Use `WP_Agent_Workspace_Scope` to stamp memory, transcripts, and audit records while keeping Data Machine paths, tables, and migration behavior in Data Machine. | | Memory context registries from Automattic/agents-api#68 | `MemoryFileRegistry`, `SectionRegistry`, `ComposableFileGenerator`, `ComposableFileInvalidation`, and prompt directives implement Data Machine's memory/context registry and composition model | [#1761](https://github.com/Extra-Chill/data-machine/issues/1761) | Adopt generic registry/projection vocabulary for context authority. Keep generated files, convention paths, invalidation hooks, and operator CLI as Data Machine product behavior. | | Memory provenance metadata from Automattic/agents-api#69 | `DiskAgentMemoryStore`, `GuidelineAgentMemoryStore`, `AgentMemoryStoreFactory`, `DailyMemoryStorage`, and memory list/read/write results currently expose Data Machine hash/path metadata but not generic provenance/query/capability metadata | [#1761](https://github.com/Extra-Chill/data-machine/issues/1761) | Extend adapters to return provenance metadata where backed by real storage data. Do not invent compatibility rows for unsupported metadata. | | Retrieved context authority cascade from Automattic/agents-api#70 | `DirectivePolicyResolver`, `CoreMemoryFilesDirective`, `MemoryFilesReader`, `ClientContextDirective`, `CallerContextDirective`, and `PromptBuilder` order and merge context without the upstream authority/conflict vocabulary | [#1761](https://github.com/Extra-Chill/data-machine/issues/1761) | Adopt authority/conflict contracts when composing retrieved context. Keep Data Machine directive registration and product modes outside Agents API. | | Durable pending-action approval contracts from Automattic/agents-api#71 | `PendingActionStore`, `PendingActionStoreAdapter`, `PendingActionResolverAdapter`, `PendingActionHelper`, `ResolvePendingActionAbility`, and pending-action REST/handler plumbing | [#1759](https://github.com/Extra-Chill/data-machine/issues/1759) | Data Machine remains the concrete store/resolver/product route. Expand adapters to the durable audit contract; do not duplicate generic lifecycle/status vocabulary locally. | -| Generic tool and action policy contracts from Automattic/agents-api#72 | `ToolPolicyFilter`, `ToolPolicyResolver`, `ActionPolicyResolver`, `DataMachineToolAccessPolicy`, `DataMachineAgentToolPolicyProvider`, `DataMachineMandatoryToolPolicy`, `ToolSourceRegistry`, and `ToolExecutionCore` | [#1758](https://github.com/Extra-Chill/data-machine/issues/1758) | Move generic allow/deny/action-policy evaluation to Agents API contracts. Keep Data Machine source providers, adjacent-handler preservation, persisted agent config, and legacy tool registry adapters local. | +| Generic tool and action policy contracts from Automattic/agents-api#72 | `ToolPolicyFilter`, `ToolPolicyResolver`, `ActionPolicyResolver`, `DataMachineToolAccessPolicy`, `DataMachineAgentToolPolicyProvider`, `DataMachineMandatoryToolPolicy`, `WP_Agent_Tool_Source_Registry`, and `WP_Agent_Tool_Execution_Core` | [#1758](https://github.com/Extra-Chill/data-machine/issues/1758) | Move generic allow/deny/action-policy evaluation to Agents API contracts. Keep Data Machine source providers, adjacent-handler preservation, persisted agent config, and legacy tool registry adapters local. | | Authorization grants, tokens, and capability ceilings from Automattic/agents-api#73 | `AgentAccess`, `AgentTokens`, `AgentTokenAbilities`, agent-call abilities, materializer owner grants, and runtime caller/agent permission checks | [#1757](https://github.com/Extra-Chill/data-machine/issues/1757) | Adapt Data Machine repositories to Agents API authorization contracts. Persisted Data Machine tables remain product-owned until an explicit storage migration exists. | ## Non-Goals diff --git a/docs/development/agents-api-extraction-map.md b/docs/development/agents-api-extraction-map.md index efdb0f74e..22d4eca8e 100644 --- a/docs/development/agents-api-extraction-map.md +++ b/docs/development/agents-api-extraction-map.md @@ -1,6 +1,6 @@ # Agents API Extraction Map -This map classifies Data Machine's current agent/runtime surface for the Agents API extraction. The in-repo module phase is complete: Data Machine now consumes the standalone `extra-chill/agents-api` package/plugin for public contracts while keeping Data Machine product adapters in this repository. +This map classifies Data Machine's current agent/runtime surface for the Agents API extraction. The in-repo module phase is complete: Data Machine now consumes the standalone `automattic/agents-api` package/plugin for public contracts while keeping Data Machine product adapters in this repository. Parent issue: [Explore splitting Agents API out of Data Machine](https://github.com/Extra-Chill/data-machine/issues/1561) @@ -14,15 +14,15 @@ Public contracts inside `agents-api/` use neutral `AgentsAPI\...` namespaces. Da | Former Data Machine namespace | Agents API namespace | |---|---| -| `DataMachine\Engine\AI\AgentMessageEnvelope` | `AgentsAPI\AI\AgentMessageEnvelope` | -| `DataMachine\Engine\AI\AgentConversationResult` | `AgentsAPI\AI\AgentConversationResult` | -| `DataMachine\Engine\AI\Tools\RuntimeToolDeclaration` | `AgentsAPI\AI\Tools\RuntimeToolDeclaration` | -| `DataMachine\Core\Database\Chat\ConversationTranscriptStoreInterface` | `AgentsAPI\Core\Database\Chat\ConversationTranscriptStoreInterface` | -| `DataMachine\Core\FilesRepository\AgentMemoryStoreInterface` | `AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface` | -| `DataMachine\Core\FilesRepository\AgentMemoryScope` | `AgentsAPI\Core\FilesRepository\AgentMemoryScope` | -| `DataMachine\Core\FilesRepository\AgentMemoryReadResult` | `AgentsAPI\Core\FilesRepository\AgentMemoryReadResult` | -| `DataMachine\Core\FilesRepository\AgentMemoryWriteResult` | `AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult` | -| `DataMachine\Core\FilesRepository\AgentMemoryListEntry` | `AgentsAPI\Core\FilesRepository\AgentMemoryListEntry` | +| `DataMachine\Engine\AI\WP_Agent_Message` | `AgentsAPI\AI\WP_Agent_Message` | +| `DataMachine\Engine\AI\WP_Agent_Conversation_Result` | `AgentsAPI\AI\WP_Agent_Conversation_Result` | +| `DataMachine\Engine\AI\Tools\WP_Agent_Tool_Declaration` | `AgentsAPI\AI\Tools\WP_Agent_Tool_Declaration` | +| `DataMachine\Core\Database\Chat\WP_Agent_Conversation_Store` | `AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Store` | +| `DataMachine\Core\FilesRepository\WP_Agent_Memory_Store` | `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store` | +| `DataMachine\Core\FilesRepository\WP_Agent_Memory_Scope` | `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope` | +| `DataMachine\Core\FilesRepository\WP_Agent_Memory_Read_Result` | `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result` | +| `DataMachine\Core\FilesRepository\WP_Agent_Memory_Write_Result` | `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result` | +| `DataMachine\Core\FilesRepository\WP_Agent_Memory_List_Entry` | `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry` | ## Current Strategy @@ -38,7 +38,7 @@ data-machine/ ...Data Machine pipelines/product code... ``` -Data Machine now treats the standalone `extra-chill/agents-api` package/plugin as that substrate: +Data Machine now treats the standalone `automattic/agents-api` package/plugin as that substrate: - `agents-api` must not import Data Machine product namespaces. - Data Machine may import and consume `agents-api` as product code. @@ -69,10 +69,10 @@ Mirror the WordPress Abilities API shape instead of importing Data Machine produ | `WP_Agent` / `WP_Agents_Registry` | `WP_Agent` / `WP_Agents_Registry` | Registry collects definitions. Persistence/adoption remains Data Machine adapter territory. | | `wp_agents_api_init` | `wp_agents_api_init` | Core-shaped init hook mirrored in-place while Data Machine hosts the substrate. | | Agent categories | No v1 registry | [#1669](https://github.com/Extra-Chill/data-machine/issues/1669) decides against `WP_Agent_Category` parity for v1. Future metadata/annotations may describe agents but must not grant permission or visibility. | -| `AgentMessageEnvelope` | `WP_Agent_Message` or same class name | Contract is generic and now uses Agents API-shaped vocabulary in place. | -| `ConversationTranscriptStoreInterface` | `WP_Agent_Conversation_Transcript_Store_Interface` | Transcript CRUD is the first extractable storage contract. Keep chat-product listing/read-state/reporting separate. | -| `AgentMemoryStoreInterface` | `WP_Agent_Memory_Store_Interface` | Generic identity tuple needs naming review; Data Machine scaffolding/abilities stay outside the store contract. | -| `RuntimeToolDeclaration` | `WP_Agent_Tool_Declaration` | Should stay ability-native and run-scoped. | +| `WP_Agent_Message` | `WP_Agent_Message` or same class name | Contract is generic and now uses Agents API-shaped vocabulary in place. | +| `WP_Agent_Conversation_Store` | `WP_Agent_Conversation_Transcript_Store_Interface` | Transcript CRUD is the first extractable storage contract. Keep chat-product listing/read-state/reporting separate. | +| `WP_Agent_Memory_Store` | `WP_Agent_Memory_Store_Interface` | Generic identity tuple needs naming review; Data Machine scaffolding/abilities stay outside the store contract. | +| `WP_Agent_Tool_Declaration` | `WP_Agent_Tool_Declaration` | Should stay ability-native and run-scoped. | | `LoopEventSinkInterface` | `WP_Agent_Run_Event_Sink_Interface` | Useful for logs, streaming, chat UIs, and async workers. | | Data Machine pending actions / diff approvals | Agents API approval primitives consumed through Data Machine adapters | Agents API owns generic approval contracts and policy vocabulary. Data Machine keeps concrete pending-action persistence, resolver ability, REST route, chat wrapper, product handlers, and permission checks while adapting to those primitives. | | REST `datamachine/v1` agent routes | REST `wp-agents/v1` deferred | [#1670](https://github.com/Extra-Chill/data-machine/issues/1670) reserves the namespace but defers public REST controllers from the first standalone extraction. Data Machine product routes stay under `datamachine/v1`. | @@ -110,9 +110,9 @@ Use these checks before moving anything: | Slice | Data Machine surface | Agents API primitive to consume | Boundary rule | |---|---|---|---| -| [#1742](https://github.com/Extra-Chill/data-machine/issues/1742) | `PendingActionStore` / store adapter | `AgentsAPI\AI\Approvals\PendingActionStoreInterface` | Keep Data Machine's concrete storage, table/transient compatibility, TTL, audit rows, and lookup behavior in Data Machine. | +| [#1742](https://github.com/Extra-Chill/data-machine/issues/1742) | `PendingActionStore` / store adapter | `AgentsAPI\AI\Approvals\PendingAction_Store` | Keep Data Machine's concrete storage, table/transient compatibility, TTL, audit rows, and lookup behavior in Data Machine. | | [#1743](https://github.com/Extra-Chill/data-machine/issues/1743) | `PendingActionHelper` staging result | Agents API `approval_required` envelope vocabulary | Preserve compatibility fields such as `staged`, `action_id`, `resolve_with`, and `resolve_params`; keep Data Machine route/tool names as adapter metadata. | -| [#1744](https://github.com/Extra-Chill/data-machine/issues/1744) | `ResolvePendingActionAbility` / resolver adapter | `AgentsAPI\AI\Approvals\ApprovalDecision`, `PendingActionResolverInterface`, and `PendingActionHandlerInterface` | Keep `datamachine/resolve-pending-action`, `datamachine/v1/actions/resolve`, permission checks, and `datamachine_pending_action_handlers` compatibility. | +| [#1744](https://github.com/Extra-Chill/data-machine/issues/1744) | `ResolvePendingActionAbility` / resolver adapter | `AgentsAPI\AI\Approvals\WP_Agent_Approval_Decision`, `PendingAction_Resolver`, and `PendingAction_Handler` | Keep `datamachine/resolve-pending-action`, `datamachine/v1/actions/resolve`, permission checks, and `datamachine_pending_action_handlers` compatibility. | | [#1745](https://github.com/Extra-Chill/data-machine/issues/1745) | `ActionPolicyResolver` / `ToolExecutor` policy checks | `AgentsAPI\AI\Tools\ActionPolicy` | Preserve Data Machine precedence for deny lists, agent overrides, category overrides, tool defaults, mode presets, and `datamachine_tool_action_policy`. | Acceptance guard: `tests/pending-actions-agents-api-contract-smoke.php` should stay a static boundary smoke. It pins the expected Agents API classes Data Machine consumes and verifies that Data Machine remains the adapter for storage, routes, abilities, handlers, CLI, and upgrade paths. It must not grow into a behavioral clone of the child-issue tests. @@ -190,10 +190,10 @@ The current namespace is intentionally mixed while extraction stays in place. Tr | Current namespace/surface | Bucket | Boundary decision | |---|---|---| -| `AgentsAPI\AI\AgentMessageEnvelope`, `AgentsAPI\AI\AgentConversationResult`, plus `DataMachine\Engine\AI\AgentConversationRequest`, `AgentConversationRunnerInterface`, `AgentConversationCompletionPolicyInterface`, `AgentConversationTranscriptPersisterInterface`, `LoopEventSinkInterface` | Agents API public candidate | Generic contracts/value objects. `AgentMessageEnvelope` and `AgentConversationResult` now live in the in-repo `agents-api/` module under neutral namespaces. `AgentConversationRequest` keeps Data Machine job/flow/pipeline/handler/transcript fields in adapter context rather than the generic runtime payload, so it remains outside until that compatibility shape is gone. | +| `AgentsAPI\AI\WP_Agent_Message`, `AgentsAPI\AI\WP_Agent_Conversation_Result`, plus `DataMachine\Engine\AI\WP_Agent_Conversation_Request`, `AgentConversationRunnerInterface`, `WP_Agent_Conversation_Completion_Policy`, `WP_Agent_Transcript_Persister`, `LoopEventSinkInterface` | Agents API public candidate | Generic contracts/value objects. `WP_Agent_Message` and `WP_Agent_Conversation_Result` now live in the in-repo `agents-api/` module under neutral namespaces. `WP_Agent_Conversation_Request` keeps Data Machine job/flow/pipeline/handler/transcript fields in adapter context rather than the generic runtime payload, so it remains outside until that compatibility shape is gone. | | `DataMachine\Engine\AI\BuiltInAgentConversationRunner`, `AIConversationLoop`, `RequestBuilder`, `RequestInspector`, `RequestMetadata`, `ConversationManager` | Agents API implementation candidate | Runtime implementation candidates, but still hosted by Data Machine and still carrying compatibility/provider/logging assumptions. Future provider primitive is direct `wp-ai-client`; `ai-http-client` is removal work, not an Agents API runtime layer. | -| `AgentsAPI\AI\Tools\RuntimeToolDeclaration`, plus `DataMachine\Engine\AI\Tools\Execution\ToolExecutionCore`, `Tools\ToolSourceRegistry`, `Tools\Policy\ToolPolicyFilter`, `Tools\ToolResultFinder` | Mixed runtime candidate | `RuntimeToolDeclaration` now lives in the in-repo `agents-api/` module under a neutral namespace. The remaining generic-looking pieces still sit next to Data Machine adapters and should move only after their source-provider, policy, and execution boundaries are proven generic. | -| `DataMachine\Engine\AI\Tools\Sources\DataMachineToolRegistrySource`, `Tools\Sources\AdjacentHandlerToolSource`, `Tools\Policy\DataMachineAgentToolPolicyProvider`, `Tools\Policy\DataMachineMandatoryToolPolicy`, `Tools\Policy\DataMachineToolAccessPolicy`, `Tools\ToolManager`, `Tools\ToolPolicyResolver`, `Tools\ToolParameters` payload merging | Data Machine adapter/product | These translate Data Machine handler, pipeline, queue, permission, persisted-agent, and legacy tool registry concepts into runtime inputs. They stay Data Machine. | +| `AgentsAPI\AI\Tools\WP_Agent_Tool_Declaration`, plus `DataMachine\Engine\AI\Tools\Execution\ToolExecutionCore`, `Tools\WP_Agent_Tool_Source_Registry`, `Tools\Policy\ToolPolicyFilter`, `Tools\ToolResultFinder` | Mixed runtime candidate | `WP_Agent_Tool_Declaration` now lives in the in-repo `agents-api/` module under a neutral namespace. The remaining generic-looking pieces still sit next to Data Machine adapters and should move only after their source-provider, policy, and execution boundaries are proven generic. | +| `DataMachine\Engine\AI\Tools\Sources\DataMachineToolRegistrySource`, `Tools\Sources\AdjacentHandlerToolSource`, `Tools\Policy\DataMachineAgentToolPolicyProvider`, `Tools\Policy\DataMachineMandatoryToolPolicy`, `Tools\Policy\DataMachineToolAccessPolicy`, `Tools\ToolManager`, `Tools\ToolPolicyResolver`, `Tools\WP_Agent_Tool_Parameters` payload merging | Data Machine adapter/product | These translate Data Machine handler, pipeline, queue, permission, persisted-agent, and legacy tool registry concepts into runtime inputs. They stay Data Machine. | | `DataMachine\Engine\AI\Tools\Global\*` | Data Machine product | Curated product/site-ops tools. Individual capabilities may move to abilities later, but the bundle is not the Agents API registry. | | `DataMachine\Engine\AI\System\*` and `System\Tasks\*` | Data Machine product | System tasks, task prompts, retention cleanup, and scheduled maintenance stay in Data Machine. A future Agents API may provide a task contract, not these tasks. | | `DataMachine\Engine\AI\Actions\*` | Mixed source material / Data Machine product | Generic pending-action and diff-approval vocabulary should be promoted to Agents API in a later extraction stage, but the current transient/database store, approval resolver implementation, `datamachine/resolve-pending-action`, `datamachine/v1/actions/resolve`, chat wrapper, product handlers, and permission checks stay Data Machine. Do not import non-existent upstream approval classes yet. | @@ -222,19 +222,19 @@ These are closest to generic public contracts. Most should be extracted as contr | Surface | Current location | Why it fits | Target notes | |---|---|---|---| -| `AgentMessageEnvelope` | `agents-api/inc/AI/AgentMessageEnvelope.php` | JSON-friendly canonical message envelope independent of flows/jobs. | Lives at `AgentsAPI\AI\AgentMessageEnvelope`. Review whether a future standalone extraction keeps this class name or adopts `WP_Agent_Message`. | -| `AgentConversationResult` | `agents-api/inc/AI/AgentConversationResult.php` | Validates result arrays from any runtime runner. | Lives at `AgentsAPI\AI\AgentConversationResult`. Future standalone extraction can rename to `WP_Agent_Run_Result` or split into result value object plus validator. | -| `AgentConversationCompletionPolicyInterface` | `inc/Engine/AI/AgentConversationCompletionPolicyInterface.php` | Generic runtime collaborator for deciding whether a tool result completes a run. | Keep Data Machine handler semantics in adapter implementations, not in the loop contract. | -| `AgentConversationTranscriptPersisterInterface` | `inc/Engine/AI/AgentConversationTranscriptPersisterInterface.php` | Generic runtime collaborator for optional transcript persistence. | Future extraction should pair this with the transcript store contract and keep job/flow metadata in Data Machine adapters. | +| `WP_Agent_Message` | `agents-api/inc/AI/WP_Agent_Message.php` | JSON-friendly canonical message envelope independent of flows/jobs. | Lives at `AgentsAPI\AI\WP_Agent_Message`. Review whether a future standalone extraction keeps this class name or adopts `WP_Agent_Message`. | +| `WP_Agent_Conversation_Result` | `agents-api/inc/AI/WP_Agent_Conversation_Result.php` | Validates result arrays from any runtime runner. | Lives at `AgentsAPI\AI\WP_Agent_Conversation_Result`. Future standalone extraction can rename to `WP_Agent_Run_Result` or split into result value object plus validator. | +| `WP_Agent_Conversation_Completion_Policy` | `inc/Engine/AI/WP_Agent_Conversation_Completion_Policy.php` | Generic runtime collaborator for deciding whether a tool result completes a run. | Keep Data Machine handler semantics in adapter implementations, not in the loop contract. | +| `WP_Agent_Transcript_Persister` | `inc/Engine/AI/WP_Agent_Transcript_Persister.php` | Generic runtime collaborator for optional transcript persistence. | Future extraction should pair this with the transcript store contract and keep job/flow metadata in Data Machine adapters. | | `LoopEventSinkInterface` | `inc/Engine/AI/LoopEventSinkInterface.php` | Transport-neutral event sink for logs, streaming, CLI, REST, or chat UIs. | Make event vocabulary public and provider-neutral before extraction. | | `NullLoopEventSink` | `inc/Engine/AI/NullLoopEventSink.php` | Generic no-op implementation for optional event sinks. | Implementation can move with the interface. | -| `RuntimeToolDeclaration` | `agents-api/inc/AI/Tools/RuntimeToolDeclaration.php` | Validates run-scoped client/runtime tool declarations without Data Machine state. | Lives at `AgentsAPI\AI\Tools\RuntimeToolDeclaration`. Future standalone extraction can rename around `WP_Agent_Tool_Declaration`; keep executor/source/scope vocabulary generic. | -| `AgentMemoryStoreInterface` | `agents-api/inc/Core/FilesRepository/AgentMemoryStoreInterface.php` | Generic memory persistence seam consumed by the Data Machine `agents_api_memory_store` resolver hook. | Lives at `AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface`. Keep CAS/hash behavior. | -| `AgentMemoryScope` | `agents-api/inc/Core/FilesRepository/AgentMemoryScope.php` | Encodes memory identity independently of disk/database implementations. | Review `layer`, `user_id`, `agent_id`, `filename` as the public model before standalone extraction. | -| `AgentMemoryReadResult` | `agents-api/inc/Core/FilesRepository/AgentMemoryReadResult.php` | Store-neutral read result. | Lives at `AgentsAPI\Core\FilesRepository\AgentMemoryReadResult`. | -| `AgentMemoryWriteResult` | `agents-api/inc/Core/FilesRepository/AgentMemoryWriteResult.php` | Store-neutral write result with hash/bytes/error shape. | Lives at `AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult`. | -| `AgentMemoryListEntry` | `agents-api/inc/Core/FilesRepository/AgentMemoryListEntry.php` | Store-neutral list entry. | Lives at `AgentsAPI\Core\FilesRepository\AgentMemoryListEntry`. | -| `ConversationTranscriptStoreInterface` | `agents-api/inc/Core/Database/Chat/ConversationTranscriptStoreInterface.php` | Transcript CRUD is generic conversation persistence. | Lives at `AgentsAPI\Core\Database\Chat\ConversationTranscriptStoreInterface`; do not require chat UI listing/read-state/reporting for transcript-only backends. | +| `WP_Agent_Tool_Declaration` | `agents-api/inc/AI/Tools/WP_Agent_Tool_Declaration.php` | Validates run-scoped client/runtime tool declarations without Data Machine state. | Lives at `AgentsAPI\AI\Tools\WP_Agent_Tool_Declaration`. Future standalone extraction can rename around `WP_Agent_Tool_Declaration`; keep executor/source/scope vocabulary generic. | +| `WP_Agent_Memory_Store` | `agents-api/inc/Core/FilesRepository/WP_Agent_Memory_Store.php` | Generic memory persistence seam consumed by the Data Machine `agents_api_memory_store` resolver hook. | Lives at `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store`. Keep CAS/hash behavior. | +| `WP_Agent_Memory_Scope` | `agents-api/inc/Core/FilesRepository/WP_Agent_Memory_Scope.php` | Encodes memory identity independently of disk/database implementations. | Review `layer`, `user_id`, `agent_id`, `filename` as the public model before standalone extraction. | +| `WP_Agent_Memory_Read_Result` | `agents-api/inc/Core/FilesRepository/WP_Agent_Memory_Read_Result.php` | Store-neutral read result. | Lives at `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result`. | +| `WP_Agent_Memory_Write_Result` | `agents-api/inc/Core/FilesRepository/WP_Agent_Memory_Write_Result.php` | Store-neutral write result with hash/bytes/error shape. | Lives at `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result`. | +| `WP_Agent_Memory_List_Entry` | `agents-api/inc/Core/FilesRepository/WP_Agent_Memory_List_Entry.php` | Store-neutral list entry. | Lives at `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry`. | +| `WP_Agent_Conversation_Store` | `agents-api/inc/Core/Database/Chat/WP_Agent_Conversation_Store.php` | Transcript CRUD is generic conversation persistence. | Lives at `AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Store`; do not require chat UI listing/read-state/reporting for transcript-only backends. | | `ConversationSessionIndexInterface` | `inc/Core/Database/Chat/ConversationSessionIndexInterface.php` | Session listing can be generic for UIs, but it is not required for transcript persistence. | Treat as optional until Agents API adopts an identity/listing model. Data Machine chat switcher uses it today. | | `ConversationReadStateInterface` | `inc/Core/Database/Chat/ConversationReadStateInterface.php` | Read-state is generic UI behavior, not transcript CRUD. | Optional interface at most. Data Machine chat unread state keeps consuming it. | | `ConversationRetentionInterface` | `inc/Core/Database/Chat/ConversationRetentionInterface.php` | Cleanup methods can be backend-generic, but retention policy/scheduling is product behavior. | Data Machine retention tasks stay product; future Agents API may expose only optional backend cleanup. | @@ -271,12 +271,12 @@ These are plausibly generic implementations, but should not move until naming an | `CallerContextDirective` | `inc/Engine/AI/Directives/CallerContextDirective.php` | Generic caller metadata injection. | Candidate if caller context becomes part of Agents API run input. | | `ConversationManager` | `inc/Engine/AI/ConversationManager.php` | Formats tool call/result messages and conversation artifacts. | Split message formatting helpers from Data Machine transcript details. | | `ToolExecutor` | `inc/Engine/AI/Tools/ToolExecutor.php` | Executes ability-native and legacy tools with policy staging. | Extract only the ability-native execution path; leave Data Machine post tracking and concrete pending-action glue behind. A future Agents API approval contract can inform this split after its vocabulary exists. | -| `RuntimeToolDeclaration` validators in tests | `tests/runtime-tool-declaration-smoke.php` | Tests generic declaration shape. | Move with the declaration contract. | +| `WP_Agent_Tool_Declaration` validators in tests | `tests/runtime-tool-declaration-smoke.php` | Tests generic declaration shape. | Move with the declaration contract. | | `ToolPolicyFilter` | `inc/Engine/AI/Tools/Policy/ToolPolicyFilter.php` | Generic allow/deny/category/capability filter that takes adapter callbacks for access and mandatory-tool preservation. | Move only with a generic access callback contract; Data Machine permission and handler preservation stay in adapters. | -| `ToolSourceRegistry` | `inc/Engine/AI/Tools/ToolSourceRegistry.php` | Source-provider composition is generic, but the default providers are Data Machine adapters. | Extract the source registry contract separately from `DataMachineToolRegistrySource` and `AdjacentHandlerToolSource`. | +| `WP_Agent_Tool_Source_Registry` | `inc/Engine/AI/Tools/ToolSourceRegistry.php` | Source-provider composition is generic, but the default providers are Data Machine adapters. | Extract the source registry contract separately from `DataMachineToolRegistrySource` and `AdjacentHandlerToolSource`. | | `DataMachineToolRegistrySource` | `inc/Engine/AI/Tools/Sources/DataMachineToolRegistrySource.php` | Adapts Data Machine's legacy/product `datamachine_tools` registry into source-provider composition. | Keep in Data Machine; Ability-native tool declarations should inform Agents API instead. | | `ToolManager` | `inc/Engine/AI/Tools/ToolManager.php` | Data Machine registry/normalization is based on `datamachine_tools`, legacy class/method tools, handler wrappers, configuration, and UI status. | Keep as Data Machine adapter/product layer; do not make it the public Agents API registry. | -| `ToolParameters` | `inc/Engine/AI/Tools/ToolParameters.php` | Parameter merge helper is useful, but payload includes job/flow/packet fields. | Keep generic parameter validation; move Data Machine payload merge rules to adapter. | +| `WP_Agent_Tool_Parameters` | `inc/Engine/AI/Tools/ToolParameters.php` | Parameter merge helper is useful, but payload includes job/flow/packet fields. | Keep generic parameter validation; move Data Machine payload merge rules to adapter. | | `ToolResultFinder` | `inc/Engine/AI/Tools/ToolResultFinder.php` | Generic enough if it only finds tool result envelopes. | Verify it does not rely on handler result naming before moving. | | `BaseTool` | `inc/Engine/AI/Tools/BaseTool.php` | Useful base class for built-in tools, but public API should favor abilities. | Do not make base-tool inheritance the primary Agents API extension point. | | `GuidelineAgentMemoryStore` | `inc/Core/FilesRepository/GuidelineAgentMemoryStore.php` | Generic implementation for `wp_guideline`, but Data Machine does not own that substrate. | Agents API can ship it as optional implementation guarded by `post_type_exists()`. | @@ -296,8 +296,8 @@ These should stay in Data Machine as compatibility glue if a generic runtime plu | `PipelineToolPolicyArgs` | `inc/Core/Steps/AI/ToolPolicy/PipelineToolPolicyArgs.php` | Translates `FlowStepConfig` enabled/disabled tool fields into generic resolver args. This is a Data Machine pipeline adapter. | | `ToolPolicyResolver` | `inc/Engine/AI/Tools/ToolPolicyResolver.php` | Orchestrates Data Machine source gathering, persisted agent policy lookup, mandatory adjacent-handler preservation, and permission adapters around the generic policy filter. | | `PipelineTranscriptPolicy` | `inc/Engine/AI/PipelineTranscriptPolicy.php` | Reads flow/pipeline config and site option to decide transcript persistence. Generic runtime receives the normalized decision through `DataMachinePipelineTranscriptPersister`. | -| `ToolSourceRegistry::SOURCE_ADJACENT_HANDLERS` | `inc/Engine/AI/Tools/ToolSourceRegistry.php` | Data Machine-specific source that exposes publish/upsert handler tools next to AI steps. | -| `ToolSourceRegistry::SOURCE_STATIC_REGISTRY` / `DataMachineToolRegistrySource` | `inc/Engine/AI/Tools/Sources/DataMachineToolRegistrySource.php` | Data Machine-specific source that adapts the curated `datamachine_tools` registry into runtime tool resolution. | +| `WP_Agent_Tool_Source_Registry::SOURCE_ADJACENT_HANDLERS` | `inc/Engine/AI/Tools/ToolSourceRegistry.php` | Data Machine-specific source that exposes publish/upsert handler tools next to AI steps. | +| `WP_Agent_Tool_Source_Registry::SOURCE_STATIC_REGISTRY` / `DataMachineToolRegistrySource` | `inc/Engine/AI/Tools/Sources/DataMachineToolRegistrySource.php` | Data Machine-specific source that adapts the curated `datamachine_tools` registry into runtime tool resolution. | | `FlowStepConfig::getAdjacentRequiredHandlerSlugsForAi()` consumers | `AIStep` and tool policy code | Converts pipeline topology into handler completion requirements. | | `QueueableTrait` prompt consumption in `AIStep` | `inc/Core/Steps/AI/AIStep.php` | Data Machine flow queue semantics (`static`, `drain`, `loop`) feeding a runtime user-message slot. | | `DataMachinePipelineTranscriptPersister` | `inc/Engine/AI/DataMachinePipelineTranscriptPersister.php` | Adapts a pipeline job run to the transcript store. Generic runtime calls the transcript persister contract and does not own job metadata. | @@ -315,7 +315,7 @@ Conversation storage is split in place, but only the narrow transcript surface i | Layer | Current surface | Boundary decision | |---|---|---| -| Generic transcript CRUD | `ConversationTranscriptStoreInterface`, `ConversationStoreFactory::get_transcript_store()` | The interface now lives in `agents-api/`; Data Machine's factory remains the product adapter. Runtime persistence should depend on this surface when it only needs complete transcript sessions. | +| Generic transcript CRUD | `WP_Agent_Conversation_Store`, `ConversationStoreFactory::get_transcript_store()` | The interface now lives in `agents-api/`; Data Machine's factory remains the product adapter. Runtime persistence should depend on this surface when it only needs complete transcript sessions. | | Data Machine compatibility aggregate | `ConversationStoreInterface`, `ConversationStoreFactory::get()`, `datamachine_conversation_store` | Stays in Data Machine for now so chat UI, REST, CLI, retention, and reporting keep one behavior-preserving resolver. | | Chat UI/session switcher | `ConversationSessionIndexInterface`, chat REST/abilities/UI callers | Product behavior today. It may become an optional Agents API UI contract later, but transcript-only backends should not implement it by default. | | Read state | `ConversationReadStateInterface` | Optional UI behavior. Not part of transcript persistence. | @@ -434,7 +434,7 @@ These tests currently pin the substrate most relevant to extraction. ## First Seams To Make Boring -1. Consume `extra-chill/agents-api` as the standalone package/plugin boundary before moving broad code into that repository. +1. Consume `automattic/agents-api` as the standalone package/plugin boundary before moving broad code into that repository. 2. Split pipeline policy translation out of `ToolPolicyResolver` so the resolver no longer imports or reads `FlowStepConfig`. 3. Split `AgentRegistry` into a pure registry and a Data Machine reconciler that creates database rows, access rows, directories, and scaffold files. 4. Rename and stabilize message/result/store interfaces in place before moving namespaces. diff --git a/docs/development/agents-api-pre-extraction-audit.md b/docs/development/agents-api-pre-extraction-audit.md index 54f753d3d..82fab64e9 100644 --- a/docs/development/agents-api-pre-extraction-audit.md +++ b/docs/development/agents-api-pre-extraction-audit.md @@ -6,7 +6,7 @@ Strategy issue: [Agents API blocker: update extraction docs around in-repo modul Related blockers: [standalone extraction umbrella](https://github.com/Extra-Chill/data-machine/issues/1596), [standalone skeleton plan](https://github.com/Extra-Chill/data-machine/issues/1618) ([docs](agents-api-standalone-skeleton-plan.md)), [in-repo module boundary](https://github.com/Extra-Chill/data-machine/issues/1631), [candidate relocation](https://github.com/Extra-Chill/data-machine/issues/1632), [wp-ai-client dependency contract](https://github.com/Extra-Chill/data-machine/issues/1633), [built-in loop ownership](https://github.com/Extra-Chill/data-machine/issues/1634), [backend-only boundary](https://github.com/Extra-Chill/data-machine/issues/1651), [agent category/capability metadata](https://github.com/Extra-Chill/data-machine/issues/1669), [REST surface decision](https://github.com/Extra-Chill/data-machine/issues/1670), [registration lifecycle](https://github.com/Extra-Chill/data-machine/issues/1671), [core-shape readiness checklist](https://github.com/Extra-Chill/data-machine/issues/1672), [one-shot AI boundary](https://github.com/Extra-Chill/data-machine/issues/1693), [approval primitive migration umbrella](https://github.com/Extra-Chill/data-machine/issues/1741), [pending-action store adapter](https://github.com/Extra-Chill/data-machine/issues/1742), [approval-required envelope adapter](https://github.com/Extra-Chill/data-machine/issues/1743), [approval resolver adapter](https://github.com/Extra-Chill/data-machine/issues/1744), [action-policy vocabulary adapter](https://github.com/Extra-Chill/data-machine/issues/1745), and [ai-http-client removal](https://github.com/Extra-Chill/data-machine/issues/1027). -This audit records the work that made the Agents API boundary extractable. Data Machine owns pipelines and automation; Agents API owns generic agent runtime primitives. The in-repo module phase is complete, and Data Machine now consumes the standalone `extra-chill/agents-api` package/plugin instead of carrying a second authoritative copy. +This audit records the work that made the Agents API boundary extractable. Data Machine owns pipelines and automation; Agents API owns generic agent runtime primitives. The in-repo module phase is complete, and Data Machine now consumes the standalone `automattic/agents-api` package/plugin instead of carrying a second authoritative copy. ## Strategy Update @@ -60,16 +60,16 @@ The initial untangling wave is complete: - Conversation transcript storage is narrowed behind a transcript facade, and the aggregate chat-product store is documented as a Data Machine compatibility layer rather than the default extraction target. - Memory store contracts/value objects live in the in-repo `agents-api/` module; Data Machine still owns the behavior-preserving factory/default store adapter. - The runner request boundary exists. -- `AgentConversationRequest::payload()` now exposes the generic runtime payload with Data Machine job/flow/pipeline/handler/transcript fields removed. Data Machine keeps those fields in `adapterContext()` and reconstructs the historical flat payload through `adapterPayload()` until the loop, prompt builder, and tool executor stop consuming the compatibility shape. +- `WP_Agent_Conversation_Request::payload()` now exposes the generic runtime payload with Data Machine job/flow/pipeline/handler/transcript fields removed. Data Machine keeps those fields in `adapterContext()` and reconstructs the historical flat payload through `adapterPayload()` until the loop, prompt builder, and tool executor stop consuming the compatibility shape. - The built-in loop now receives runtime completion and transcript collaborators. Data Machine's handler-completion and pipeline-transcript behavior lives behind adapter classes instead of being hardcoded as generic loop state. -The naming phase renamed the neutral runner result/request seam from `AIConversation*` to `AgentConversation*` while leaving `AIConversationLoop` as the temporary compatibility facade. The first generic runtime contracts now live in the standalone `extra-chill/agents-api` package with their `AgentsAPI\...` namespaces preserved. +The naming phase renamed the neutral runner result/request seam from `AIConversation*` to `AgentConversation*` while leaving `AIConversationLoop` as the temporary compatibility facade. The first generic runtime contracts now live in the standalone `automattic/agents-api` package with their `AgentsAPI\...` namespaces preserved. ## In-Repo Module Gate Before standalone extraction, the in-repo module should satisfy these gates: -- `extra-chill/agents-api` loads before Data Machine product runtime bootstraps. +- `automattic/agents-api` loads before Data Machine product runtime bootstraps. - A bootstrap smoke can load `agents-api` without Data Machine product code. - No `agents-api` file imports `DataMachine\Core\Steps`, `DataMachine\Core\Database\Jobs`, handler, queue, retention, Data Machine pending-action implementation, admin UI, or content-operation namespaces. - No `agents-api` file registers admin menus, admin screens, settings forms, or admin-only UI hooks. @@ -118,7 +118,7 @@ The deferred shape is reserved as a backend substrate namespace, not a Data Mach - `GET /wp-agents/v1/agents` may list registered agent definitions that explicitly opt into REST discovery. - `GET /wp-agents/v1/agents/{slug}` may read one registered definition. -- `POST /wp-agents/v1/agents/{slug}/runs` may eventually execute a generic `AgentConversationRequest` through `AgentConversationRunnerInterface`. +- `POST /wp-agents/v1/agents/{slug}/runs` may eventually execute a generic `WP_Agent_Conversation_Request` through `AgentConversationRunnerInterface`. - Any future transcript, memory, run-state, approval, or session routes require separate decisions. They must not inherit Data Machine chat-session switcher, jobs, flows, pipelines, queues, or concrete pending-action route/storage vocabulary. The v1 standalone skeleton should therefore document that `wp-agents/v1` is intentionally absent. Data Machine's existing `datamachine/v1` routes remain product REST for flows, pipelines, jobs, chat UI, agent files, and automation surfaces. They may adapt to Agents API contracts later, but they do not define the public substrate. @@ -161,10 +161,10 @@ REST acceptance gates before any future controller lands: Target shape: - `AgentConversationRunnerInterface` is the public runtime boundary. -- `AgentConversationRequest` and `AgentConversationResult` are neutral value contracts. +- `WP_Agent_Conversation_Request` and `WP_Agent_Conversation_Result` are neutral value contracts. - `AIConversationLoop` remains a Data Machine compatibility facade until callers are moved to the new name. -- `AgentConversationCompletionPolicyInterface` and `AgentConversationTranscriptPersisterInterface` are in-place runtime collaborator seams; Data Machine provides the current handler-completion and transcript adapters. -- A future `AgentConversationLoop` or `WP_Agent_Runner` should not know about `job_id`, `flow_step_id`, `pipeline_id`, or handler completion policy. +- `WP_Agent_Conversation_Completion_Policy` and `WP_Agent_Transcript_Persister` are in-place runtime collaborator seams; Data Machine provides the current handler-completion and transcript adapters. +- A future `WP_Agent_Conversation_Loop` or `WP_Agent_Runner` should not know about `job_id`, `flow_step_id`, `pipeline_id`, or handler completion policy. - The built-in compatibility loop must not move into `agents-api` while it preserves historical `AIConversationLoop::execute()` result normalization, Data Machine logging/transcript metadata, adjacent-handler completion, or `ai-http-client` / `chubes_ai_*` provider compatibility. ### 2. Runtime Hooks And Filters @@ -188,19 +188,19 @@ Target shape: ### 3. Message Envelope Vocabulary -`AgentMessageEnvelope` is the in-place Agents API-shaped name for the generic +`WP_Agent_Message` is the in-place Agents API-shaped name for the generic message contract. Its schema is `agents-api.message` while the class still lives inside Data Machine. Target shape: -- Review whether the physically extracted public class should stay `AgentMessageEnvelope` or become `WP_Agent_Message`. +- Review whether the physically extracted public class should stay `WP_Agent_Message` or become `WP_Agent_Message`. - Keep schema metadata generic; do not reintroduce Data Machine-owned schema names for the shared contract. - Keep host-specific message DTOs as adapters/source material, not public dependency vocabulary. ### 4. Conversation Storage Boundary -The transcript interface is now separated from the aggregate chat-product store. The interface lives in the in-repo `agents-api/` module while preserving its current namespace for behavior compatibility, and the dependency direction is explicit: runtime transcript persistence depends on `ConversationTranscriptStoreInterface`, while Data Machine chat UI/REST/CLI/retention/reporting depend on the broader `ConversationStoreInterface` aggregate. +The transcript interface is now separated from the aggregate chat-product store. The interface lives in the in-repo `agents-api/` module while preserving its current namespace for behavior compatibility, and the dependency direction is explicit: runtime transcript persistence depends on `WP_Agent_Conversation_Store`, while Data Machine chat UI/REST/CLI/retention/reporting depend on the broader `ConversationStoreInterface` aggregate. Target shape: @@ -215,7 +215,7 @@ The memory store contract is close to extractable. Target shape: -- `AgentMemoryStoreInterface`, `AgentMemoryScope`, `AgentMemoryReadResult`, `AgentMemoryWriteResult`, and `AgentMemoryListEntry` are WordPress-shaped Agents API contracts/value objects hosted in `agents-api/` with current namespaces preserved for compatibility. +- `WP_Agent_Memory_Store`, `WP_Agent_Memory_Scope`, `WP_Agent_Memory_Read_Result`, `WP_Agent_Memory_Write_Result`, and `WP_Agent_Memory_List_Entry` are WordPress-shaped Agents API contracts/value objects hosted in `agents-api/` with current namespaces preserved for compatibility. - `AgentMemoryStoreFactory` stays Data Machine-owned for now because its behavior-preserving fallback constructs `DiskAgentMemoryStore`. - `GuidelineAgentMemoryStore` may become an optional Agents API implementation later, guarded by `post_type_exists( 'wp_guideline' )` and taxonomy availability checks. - `DiskAgentMemoryStore` stays Data Machine product/adapter behavior with file scaffolding, SOUL/MEMORY/USER composition, section editing, and operator CLI surfaces. @@ -230,7 +230,7 @@ Current in-place migration: The execution core is split, but `ToolManager` still centers on `datamachine_tools` and legacy handler/class tool declarations. -The source-composition seam is now clearer: `ToolSourceRegistry` composes named providers, while `DataMachineToolRegistrySource` adapts the legacy/product `datamachine_tools` registry and `AdjacentHandlerToolSource` adapts pipeline-neighbor handler tools. Those providers are Data Machine consumers of the generic source idea, not the future Agents API tool registry. +The source-composition seam is now clearer: `WP_Agent_Tool_Source_Registry` composes named providers, while `DataMachineToolRegistrySource` adapts the legacy/product `datamachine_tools` registry and `AdjacentHandlerToolSource` adapts pipeline-neighbor handler tools. Those providers are Data Machine consumers of the generic source idea, not the future Agents API tool registry. The policy-filtering seam is also split in place: `ToolPolicyFilter` owns reusable allow/deny/category/capability filtering, while `ToolPolicyResolver` remains the Data Machine adapter that gathers Data Machine sources, reads persisted agent policy, preserves adjacent handler tools, and delegates permission checks to `DataMachineToolAccessPolicy`. @@ -257,9 +257,9 @@ Data Machine pending actions are now an adapter over Agents API approval primiti Adapter checklist: -- [#1742](https://github.com/Extra-Chill/data-machine/issues/1742): adapt `PendingActionStore` to `AgentsAPI\AI\Approvals\PendingActionStoreInterface` while preserving Data Machine storage, IDs, TTL/table compatibility, audit rows, and legacy lookup defaults. +- [#1742](https://github.com/Extra-Chill/data-machine/issues/1742): adapt `PendingActionStore` to `AgentsAPI\AI\Approvals\PendingAction_Store` while preserving Data Machine storage, IDs, TTL/table compatibility, audit rows, and legacy lookup defaults. - [#1743](https://github.com/Extra-Chill/data-machine/issues/1743): adapt `PendingActionHelper` staging to Agents API `approval_required` envelope semantics while preserving downstream compatibility fields such as `staged`, `action_id`, `resolve_with`, and `resolve_params`. -- [#1744](https://github.com/Extra-Chill/data-machine/issues/1744): adapt `ResolvePendingActionAbility` to `AgentsAPI\AI\Approvals\ApprovalDecision`, `PendingActionResolverInterface`, and `PendingActionHandlerInterface` while keeping `datamachine/resolve-pending-action`, `datamachine/v1/actions/resolve`, permission checks, and `datamachine_pending_action_handlers` compatibility. +- [#1744](https://github.com/Extra-Chill/data-machine/issues/1744): adapt `ResolvePendingActionAbility` to `AgentsAPI\AI\Approvals\WP_Agent_Approval_Decision`, `PendingAction_Resolver`, and `PendingAction_Handler` while keeping `datamachine/resolve-pending-action`, `datamachine/v1/actions/resolve`, permission checks, and `datamachine_pending_action_handlers` compatibility. - [#1745](https://github.com/Extra-Chill/data-machine/issues/1745): adapt Data Machine action policy checks to `AgentsAPI\AI\Tools\ActionPolicy` while preserving existing Data Machine precedence and `direct` / `preview` / `forbidden` behavior. - Data Machine owns the concrete storage/resolution implementation, resolver ability, REST route, chat wrapper, product handlers, CLI/operator surfaces, and Data Machine permission checks. - Keep Data Machine route names and storage keys out of future Agents API public contracts. diff --git a/docs/development/agents-api-standalone-skeleton-plan.md b/docs/development/agents-api-standalone-skeleton-plan.md index e577a7d6c..4366632e3 100644 --- a/docs/development/agents-api-standalone-skeleton-plan.md +++ b/docs/development/agents-api-standalone-skeleton-plan.md @@ -6,7 +6,7 @@ Refs: [standalone extraction umbrella](https://github.com/Extra-Chill/data-machi This plan turned the bounded in-repo `agents-api/` module into the first reviewable standalone-plugin extraction step. The standalone repository now provides the public WordPress-shaped surface and dependency direction before behavior-heavy runtime services move. -Status update for [#1596](https://github.com/Extra-Chill/data-machine/issues/1596): Data Machine consumes `extra-chill/agents-api` as a Composer/plugin dependency and no longer carries an authoritative in-repo copy of the module. +Status update for [#1596](https://github.com/Extra-Chill/data-machine/issues/1596): Data Machine consumes `automattic/agents-api` as a Composer/plugin dependency and no longer carries an authoritative in-repo copy of the module. ## Goal @@ -35,23 +35,23 @@ agents-api/ class-wp-agent-package-artifacts-registry.php class-wp-agent-package-adoption-diff.php class-wp-agent-package-adoption-result.php - class-wp-agent-package-adopter-interface.php + class-wp-agent-package-adopter.php register-agent-package-artifacts.php AI/ - AgentMessageEnvelope.php - AgentConversationResult.php + WP_Agent_Message.php + WP_Agent_Conversation_Result.php Tools/ - RuntimeToolDeclaration.php + WP_Agent_Tool_Declaration.php Core/ Database/ Chat/ - ConversationTranscriptStoreInterface.php + WP_Agent_Conversation_Store.php FilesRepository/ - AgentMemoryScope.php - AgentMemoryListEntry.php - AgentMemoryReadResult.php - AgentMemoryWriteResult.php - AgentMemoryStoreInterface.php + WP_Agent_Memory_Scope.php + WP_Agent_Memory_List_Entry.php + WP_Agent_Memory_Read_Result.php + WP_Agent_Memory_Write_Result.php + WP_Agent_Memory_Store.php tests/ bootstrap-smoke.php no-product-imports-smoke.php @@ -86,10 +86,10 @@ The first skeleton freezes only the names already used by the in-repo module: | Agent value object | `WP_Agent` | | Agent registry | `WP_Agents_Registry` | | Package artifacts | `WP_Agent_Package*` classes and `wp_register_agent_package_artifact_type()` helpers | -| Message/result contracts | `AgentsAPI\AI\AgentMessageEnvelope`, `AgentsAPI\AI\AgentConversationResult` | -| Tool declaration | `AgentsAPI\AI\Tools\RuntimeToolDeclaration` | -| Transcript contract | `AgentsAPI\Core\Database\Chat\ConversationTranscriptStoreInterface` | -| Memory contracts | `AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface`, `AgentMemoryScope`, `AgentMemoryListEntry`, `AgentMemoryReadResult`, and `AgentMemoryWriteResult` | +| Message/result contracts | `AgentsAPI\AI\WP_Agent_Message`, `AgentsAPI\AI\WP_Agent_Conversation_Result` | +| Tool declaration | `AgentsAPI\AI\Tools\WP_Agent_Tool_Declaration` | +| Transcript contract | `AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Store` | +| Memory contracts | `AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store`, `WP_Agent_Memory_Scope`, `WP_Agent_Memory_List_Entry`, `WP_Agent_Memory_Read_Result`, and `WP_Agent_Memory_Write_Result` | Do not add aliases back to old `DataMachine\...` class names. Data Machine is pre-1.0 and should hard-cut to the standalone package when the extraction PR lands. @@ -124,9 +124,9 @@ Move contracts/value objects before services. The first extraction PR should be |---|---|---| | `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. | -| `AgentMessageEnvelope` and `AgentConversationResult` | `agents-api/inc/AI/` | Generic run/message value contracts. | -| `RuntimeToolDeclaration` | `agents-api/inc/AI/Tools/` | Generic run-scoped tool declaration validation. | -| `ConversationTranscriptStoreInterface` | `agents-api/inc/Core/Database/Chat/` | Narrow transcript CRUD contract; does not require Data Machine chat UI. | +| `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. | Current in-repo source checklist for the first move: @@ -141,17 +141,17 @@ Current in-repo source checklist for the first move: - `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-interface.php` +- `inc/class-wp-agent-package-adopter.php` - `inc/register-agent-package-artifacts.php` -- `inc/AI/AgentMessageEnvelope.php` -- `inc/AI/AgentConversationResult.php` -- `inc/AI/Tools/RuntimeToolDeclaration.php` -- `inc/Core/Database/Chat/ConversationTranscriptStoreInterface.php` -- `inc/Core/FilesRepository/AgentMemoryScope.php` -- `inc/Core/FilesRepository/AgentMemoryListEntry.php` -- `inc/Core/FilesRepository/AgentMemoryReadResult.php` -- `inc/Core/FilesRepository/AgentMemoryWriteResult.php` -- `inc/Core/FilesRepository/AgentMemoryStoreInterface.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` ## Keep In Data Machine For Now diff --git a/docs/development/hooks/core-filters.md b/docs/development/hooks/core-filters.md index bb1c87f88..efac46c90 100644 --- a/docs/development/hooks/core-filters.md +++ b/docs/development/hooks/core-filters.md @@ -1225,7 +1225,7 @@ add_filter('datamachine_directives', function($directives) { Data Machine's Universal Engine provides shared AI infrastructure serving both Pipeline and Chat agents. See `/docs/core-system/universal-engine.md` for complete architecture documentation. -### ToolParameters (`/inc/Engine/AI/Tools/ToolParameters.php`) +### WP_Agent_Tool_Parameters (`/inc/Engine/AI/Tools/ToolParameters.php`) **Purpose**: Centralized parameter building for all AI tools with unified flat structure. @@ -1234,7 +1234,7 @@ Data Machine's Universal Engine provides shared AI infrastructure serving both P #### `buildParameters()` ```php -\DataMachine\Engine\AI\ToolParameters::buildParameters(array $data, ?string $job_id, ?string $flow_step_id): array +\DataMachine\Engine\AI\WP_Agent_Tool_Parameters::buildParameters(array $data, ?string $job_id, ?string $flow_step_id): array ``` Builds flat parameter structure for standard AI tools with content extraction and job context. @@ -1253,7 +1253,7 @@ Builds flat parameter structure for standard AI tools with content extraction an #### `buildForHandlerTool()` ```php -\DataMachine\Engine\AI\ToolParameters::buildForHandlerTool(array $data, array $tool_def, ?string $job_id, ?string $flow_step_id): array +\DataMachine\Engine\AI\WP_Agent_Tool_Parameters::buildForHandlerTool(array $data, array $tool_def, ?string $job_id, ?string $flow_step_id): array ``` Builds parameters for handler-specific tools with engine data merging (source_url, image_url). @@ -1459,7 +1459,7 @@ arrays are projection shapes at provider boundaries, not the store contract. - `list_sessions_for_day` — day-scoped summary rows for the Daily Memory Task - `get_storage_metrics` — row count + on-disk size for the `wp datamachine retention status` CLI; return `null` to opt out -### AgentMemoryStoreInterface (`/agents-api/inc/Core/FilesRepository/AgentMemoryStoreInterface.php`) +### WP_Agent_Memory_Store (`/agents-api/inc/Core/FilesRepository/WP_Agent_Memory_Store.php`) **Purpose**: Single seam between agent memory operations and the underlying persistence backend. The contract is generic agent-memory persistence: it does @@ -1474,12 +1474,12 @@ seam was introduced. ```php apply_filters( 'agents_api_memory_store', - null, // Return AgentMemoryStoreInterface to short-circuit - AgentMemoryScope $scope // Identifies (layer, user_id, agent_id, filename) + null, // Return WP_Agent_Memory_Store to short-circuit + WP_Agent_Memory_Scope $scope // Identifies (layer, user_id, agent_id, filename) ); ``` -Return an `AgentMemoryStoreInterface` +Return an `WP_Agent_Memory_Store` implementation to replace the disk default for this scope. Return `null` (the default) to let Data Machine read and write through the filesystem. @@ -1495,7 +1495,7 @@ ships a DB-backed implementation and registers it conditionally: ```php add_filter( 'agents_api_memory_store', function ( $store, $scope ) { - if ( $store instanceof AgentMemoryStoreInterface ) { + if ( $store instanceof WP_Agent_Memory_Store ) { return $store; // someone else already swapped } if ( filesystem_is_writable_here() ) { @@ -1507,13 +1507,13 @@ add_filter( 'agents_api_memory_store', function ( $store, $scope ) { **Contract**: -- `read( $scope )` → `AgentMemoryReadResult { exists, content, hash, bytes, updated_at }` -- `write( $scope, $content, $if_match = null )` → `AgentMemoryWriteResult` +- `read( $scope )` → `WP_Agent_Memory_Read_Result { exists, content, hash, bytes, updated_at }` +- `write( $scope, $content, $if_match = null )` → `WP_Agent_Memory_Write_Result` (implementations supporting concurrency MUST honor `$if_match` and return `error = 'conflict'` on hash mismatch) - `exists( $scope )` → `bool` -- `delete( $scope )` → `AgentMemoryWriteResult` (idempotent) -- `list_layer( $scope_query )` → `AgentMemoryListEntry[]` (enumerates one layer) +- `delete( $scope )` → `WP_Agent_Memory_Write_Result` (idempotent) +- `list_layer( $scope_query )` → `WP_Agent_Memory_List_Entry[]` (enumerates one layer) Section parsing, scaffolding, editability gating, ability permissions, prompt-injection policy, and registry-driven convention-path semantics stay in @@ -1525,8 +1525,8 @@ underneath. `AgentMemory` is the only class in core that talks to `AgentMemoryStoreFactory`. It exposes: - Section-level ops: `get_section()`, `set_section()`, `append_to_section()`, `get_sections()`, `search()` -- Whole-file ops: `read()` (returns `AgentMemoryReadResult`), `get_all()`, `replace_all()`, `exists()`, `delete()` -- Static layer enumerator: `AgentMemory::list_layer( $layer, $user_id, $agent_id )` → `AgentMemoryListEntry[]` +- Whole-file ops: `read()` (returns `WP_Agent_Memory_Read_Result`), `get_all()`, `replace_all()`, `exists()`, `delete()` +- Static layer enumerator: `AgentMemory::list_layer( $layer, $user_id, $agent_id )` → `WP_Agent_Memory_List_Entry[]` Higher-level consumers all go through this facade rather than instantiating store types directly: diff --git a/docs/handlers/fetch/handlers-overview.md b/docs/handlers/fetch/handlers-overview.md index 9ab41ec9b..ba2b51d41 100644 --- a/docs/handlers/fetch/handlers-overview.md +++ b/docs/handlers/fetch/handlers-overview.md @@ -254,7 +254,7 @@ Fetch handlers provide essential metadata that AI steps use for content processi **Content Structure**: All handlers structure content in consistent format that AI steps process through the modular AI directive system. -**Metadata Preservation**: Handler metadata (original titles, dates) flows through pipeline to AI tools via ToolParameters while URLs are accessed via engine data filter. +**Metadata Preservation**: Handler metadata (original titles, dates) flows through pipeline to AI tools via WP_Agent_Tool_Parameters while URLs are accessed via engine data filter. ### Tool-First Architecture Support @@ -271,7 +271,7 @@ if ($job_id) { // AI step processes clean content without URL pollution // Update tools access source_url via centralized datamachine_engine_data filter -// Publishing tools receive clean content via ToolParameters +// Publishing tools receive clean content via WP_Agent_Tool_Parameters ``` ## Integration Examples diff --git a/docs/overview.md b/docs/overview.md index 00b65aedf..d9091248c 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -146,7 +146,7 @@ wp datamachine jobs undo --dry-run --allow-root - **PromptBuilder + RequestBuilder** apply layered directives via the `datamachine_directives` filter so every request includes identity, context, and site-specific instructions. - **Global tools** (Google Search, Local Search, Web Fetch, WordPress Post Reader) are registered under `/inc/Engine/AI/Tools/` and available to all agents. - **Chat-specific tools** (AddPipelineStep, ApiQuery, AuthenticateHandler, ConfigureFlowSteps, ConfigurePipelineStep, CopyFlow, CreateFlow, CreatePipeline, CreateTaxonomyTerm, ExecuteWorkflowTool, GetHandlerDefaults, ManageLogs, ReadLogs, RunFlow, SearchTaxonomyTerms, SetHandlerDefaults, UpdateFlow) orchestrate pipeline and flow management within conversations. -- **ToolParameters + ToolResultFinder** gather parameter metadata for tools and interpret results inside data packets to keep conversations consistent. +- **WP_Agent_Tool_Parameters + ToolResultFinder** gather parameter metadata for tools and interpret results inside data packets to keep conversations consistent. ## Authentication & Security diff --git a/homeboy.json b/homeboy.json index 545480c19..e92a2e5e6 100644 --- a/homeboy.json +++ b/homeboy.json @@ -21,7 +21,10 @@ "mysql_host": "localhost", "mysql_database": "wptests", "mysql_user": "root", - "mysql_password": "" + "mysql_password": "", + "settings": { + "validation_dependencies": "agents-api" + } } }, "id": "data-machine", diff --git a/inc/Abilities/DailyMemoryAbilities.php b/inc/Abilities/DailyMemoryAbilities.php index 5c3ef96bf..04aef33e3 100644 --- a/inc/Abilities/DailyMemoryAbilities.php +++ b/inc/Abilities/DailyMemoryAbilities.php @@ -14,7 +14,7 @@ * Precedence: a valid DailyMemoryStorage returned by * `datamachine_daily_memory_storage` replaces the backend for these * abilities. If that filter is absent or returns an invalid value, - * DailyMemory remains active and the active AgentMemoryStoreInterface + * DailyMemory remains active and the active WP_Agent_Memory_Store * selected by `agents_api_memory_store` handles persistence. * * @package DataMachine\Abilities diff --git a/inc/Abilities/File/AgentFileAbilities.php b/inc/Abilities/File/AgentFileAbilities.php index c1871a9e7..b2cb8025c 100644 --- a/inc/Abilities/File/AgentFileAbilities.php +++ b/inc/Abilities/File/AgentFileAbilities.php @@ -632,7 +632,7 @@ public function executeUploadAgentFile( array $input ): array { * @param string $filename Filename to resolve. * @param int $user_id Effective user ID. * @param int $agent_id Agent ID for direct resolution. 0 = resolve from user_id. - * @return array{0: AgentMemory, 1: \DataMachine\Core\FilesRepository\AgentMemoryReadResult}|null + * @return array{0: AgentMemory, 1: \DataMachine\Core\FilesRepository\WP_Agent_Memory_Read_Result}|null */ private function locateMemory( string $filename, int $user_id, int $agent_id ): ?array { $layer_order = array(); @@ -685,5 +685,4 @@ private function sanitizeFileEntry( array $file ): array { return $sanitized; } - } diff --git a/inc/Abilities/PermissionHelper.php b/inc/Abilities/PermissionHelper.php index 2ff512d4e..4a8c9a4b0 100644 --- a/inc/Abilities/PermissionHelper.php +++ b/inc/Abilities/PermissionHelper.php @@ -8,7 +8,7 @@ namespace DataMachine\Abilities; -use AgentsAPI\AI\AgentExecutionPrincipal; +use AgentsAPI\AI\WP_Agent_Execution_Principal; /** * Helper class for ability permission checks. @@ -104,9 +104,9 @@ class PermissionHelper { * Agents API execution principal for the current request. * * @since 0.103.15 - * @var AgentExecutionPrincipal|null + * @var WP_Agent_Execution_Principal|null */ - private static ?AgentExecutionPrincipal $execution_principal = null; + private static ?WP_Agent_Execution_Principal $execution_principal = null; /** * Cross-site caller context for the current request. @@ -276,20 +276,20 @@ public static function set_agent_context( int $agent_id, int $owner_id, ?array $ ) ); self::$execution_principal = null !== $token_id - ? AgentExecutionPrincipal::agent_token( + ? WP_Agent_Execution_Principal::agent_token( $owner_id, (string) $agent_id, $token_id, - AgentExecutionPrincipal::REQUEST_CONTEXT_REST, + WP_Agent_Execution_Principal::REQUEST_CONTEXT_REST, array(), null, null, self::$capability_ceiling ) - : AgentExecutionPrincipal::user_session( + : WP_Agent_Execution_Principal::user_session( $owner_id, (string) $agent_id, - AgentExecutionPrincipal::REQUEST_CONTEXT_REST, + WP_Agent_Execution_Principal::REQUEST_CONTEXT_REST, array(), null, null, @@ -316,9 +316,9 @@ public static function clear_agent_context(): void { * * @since 0.103.15 * - * @param AgentExecutionPrincipal $principal Execution principal. + * @param WP_Agent_Execution_Principal $principal Execution principal. */ - public static function set_execution_principal( AgentExecutionPrincipal $principal ): void { + public static function set_execution_principal( WP_Agent_Execution_Principal $principal ): void { self::$execution_principal = $principal; if ( property_exists( $principal, 'capability_ceiling' ) && $principal->capability_ceiling instanceof \WP_Agent_Capability_Ceiling ) { self::$capability_ceiling = $principal->capability_ceiling; @@ -330,9 +330,9 @@ public static function set_execution_principal( AgentExecutionPrincipal $princip * * @since 0.103.15 * - * @return AgentExecutionPrincipal|null + * @return WP_Agent_Execution_Principal|null */ - public static function get_execution_principal(): ?AgentExecutionPrincipal { + public static function get_execution_principal(): ?WP_Agent_Execution_Principal { return self::$execution_principal; } @@ -429,10 +429,10 @@ private static function agent_can( string $action ): bool { return false; } - $principal = self::$execution_principal ?? AgentExecutionPrincipal::user_session( + $principal = self::$execution_principal ?? WP_Agent_Execution_Principal::user_session( self::$agent_owner_id, (string) self::$acting_agent_id, - AgentExecutionPrincipal::REQUEST_CONTEXT_REST, + WP_Agent_Execution_Principal::REQUEST_CONTEXT_REST, array(), null, null, diff --git a/inc/Abilities/SystemAbilities.php b/inc/Abilities/SystemAbilities.php index a9fbe389a..4a0522953 100644 --- a/inc/Abilities/SystemAbilities.php +++ b/inc/Abilities/SystemAbilities.php @@ -14,7 +14,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Engine\AI\RequestBuilder; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; use DataMachine\Core\Database\Chat\ConversationStoreFactory; use DataMachine\Core\PluginSettings; use DataMachine\Engine\Tasks\TaskScheduler; @@ -547,7 +547,7 @@ public static function generateSessionTitle( array $input ): array { $first_assistant_response = null; foreach ( $messages as $msg ) { - $msg = AgentMessageEnvelope::normalize( $msg ); + $msg = WP_Agent_Message::normalize( $msg ); $role = $msg['role'] ?? ''; $content = $msg['content'] ?? ''; $content = is_string( $content ) ? $content : wp_json_encode( $content, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); diff --git a/inc/Cli/Commands/JobsCommand.php b/inc/Cli/Commands/JobsCommand.php index e2aeab6b5..5e221f666 100644 --- a/inc/Cli/Commands/JobsCommand.php +++ b/inc/Cli/Commands/JobsCommand.php @@ -24,7 +24,7 @@ use DataMachine\Abilities\Job\RunMetricsAbility; use DataMachine\Core\Database\Chat\ConversationStoreFactory; use DataMachine\Core\Database\Jobs\Jobs; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; use DataMachine\Engine\AI\System\Tasks\SystemTask; use DataMachine\Engine\Tasks\TaskRegistry; @@ -569,9 +569,9 @@ private function renderTranscriptText( int $job_id, string $session_id, array $s WP_CLI::log( '' ); foreach ( $messages as $idx => $message ) { - $message = AgentMessageEnvelope::normalize( $message ); + $message = WP_Agent_Message::normalize( $message ); $role = $message['role'] ?? 'unknown'; - $type = $message['type'] ?? AgentMessageEnvelope::TYPE_TEXT; + $type = $message['type'] ?? WP_Agent_Message::TYPE_TEXT; $content = $message['content'] ?? ''; $header = sprintf( '[%d] %s (%s)', $idx, $role, $type ); diff --git a/inc/Core/Auth/AgentAuthMiddleware.php b/inc/Core/Auth/AgentAuthMiddleware.php index 0ecfe48fe..715c3bddc 100644 --- a/inc/Core/Auth/AgentAuthMiddleware.php +++ b/inc/Core/Auth/AgentAuthMiddleware.php @@ -27,7 +27,7 @@ use DataMachine\Core\Database\Agents\Agents; use DataMachine\Core\Database\Agents\AgentTokens; use DataMachine\Engine\AI\IterationBudgetRegistry; -use AgentsAPI\AI\AgentExecutionPrincipal; +use AgentsAPI\AI\WP_Agent_Execution_Principal; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -81,7 +81,7 @@ public function authenticate( $result ) { // Resolve token through the generic Agents API token contract. $tokens_repo = new AgentTokens(); $authenticator = new \WP_Agent_Token_Authenticator( $tokens_repo, self::TOKEN_PREFIX ); - $principal = $authenticator->authenticate_bearer_token( $raw_token, AgentExecutionPrincipal::REQUEST_CONTEXT_REST, array(), $request ); + $principal = $authenticator->authenticate_bearer_token( $raw_token, WP_Agent_Execution_Principal::REQUEST_CONTEXT_REST, array(), $request ); if ( ! $principal ) { do_action( diff --git a/inc/Core/Database/Agents/AgentAccess.php b/inc/Core/Database/Agents/AgentAccess.php index 94c450028..a7c89a3b3 100644 --- a/inc/Core/Database/Agents/AgentAccess.php +++ b/inc/Core/Database/Agents/AgentAccess.php @@ -17,7 +17,7 @@ exit; } -class AgentAccess extends BaseRepository implements \WP_Agent_Access_Store_Interface { +class AgentAccess extends BaseRepository implements \WP_Agent_Access_Store { /** * Table name (without prefix). diff --git a/inc/Core/Database/Agents/AgentTokens.php b/inc/Core/Database/Agents/AgentTokens.php index 1e1e644d7..eac2f8352 100644 --- a/inc/Core/Database/Agents/AgentTokens.php +++ b/inc/Core/Database/Agents/AgentTokens.php @@ -21,7 +21,7 @@ exit; } -class AgentTokens extends BaseRepository implements \WP_Agent_Token_Store_Interface { +class AgentTokens extends BaseRepository implements \WP_Agent_Token_Store { /** * Table name (without prefix). diff --git a/inc/Core/Database/Chat/Chat.php b/inc/Core/Database/Chat/Chat.php index 9be615727..0bd12f84f 100644 --- a/inc/Core/Database/Chat/Chat.php +++ b/inc/Core/Database/Chat/Chat.php @@ -13,8 +13,8 @@ use DataMachine\Core\Admin\DateFormatter; use DataMachine\Core\Database\BaseRepository; -use AgentsAPI\AI\AgentMessageEnvelope; -use AgentsAPI\Core\Workspace\AgentWorkspaceScope; +use AgentsAPI\AI\WP_Agent_Message; +use AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope; use DataMachine\Core\Workspace\WordPressWorkspaceScope; if ( ! defined( 'ABSPATH' ) ) { @@ -60,6 +60,7 @@ public static function create_table(): void { metadata LONGTEXT NULL, provider VARCHAR(50) NULL, model VARCHAR(100) NULL, + provider_response_id VARCHAR(191) NULL, mode VARCHAR(20) NOT NULL DEFAULT 'chat', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -284,7 +285,7 @@ private static function get_escaped_table_name(): string { /** * Create new chat session * - * @param AgentWorkspaceScope $workspace Workspace owning the session. + * @param WP_Agent_Workspace_Scope $workspace Workspace owning the session. * @param int $user_id WordPress user ID. * @param int $agent_id Agent ID. * @param array $metadata Optional session metadata. @@ -358,10 +359,10 @@ public function create_session( ...$args ): string { * Normalize create-session arguments across current and workspace-aware contracts. * * @param array $args Raw method arguments. - * @return array{0:AgentWorkspaceScope,1:int,2:int,3:array,4:string} + * @return array{0:WP_Agent_Workspace_Scope,1:int,2:int,3:array,4:string} */ private static function normalize_create_session_args( array $args ): array { - if ( isset( $args[0] ) && $args[0] instanceof AgentWorkspaceScope ) { + if ( isset( $args[0] ) && $args[0] instanceof WP_Agent_Workspace_Scope ) { return array( $args[0], (int) ( $args[1] ?? 0 ), @@ -384,10 +385,10 @@ private static function normalize_create_session_args( array $args ): array { * Normalize pending-session arguments across current and workspace-aware contracts. * * @param array $args Raw method arguments. - * @return array{0:AgentWorkspaceScope,1:int,2:int,3:string,4:int|null} + * @return array{0:WP_Agent_Workspace_Scope,1:int,2:int,3:string,4:int|null} */ private static function normalize_recent_pending_session_args( array $args ): array { - if ( isset( $args[0] ) && $args[0] instanceof AgentWorkspaceScope ) { + if ( isset( $args[0] ) && $args[0] instanceof WP_Agent_Workspace_Scope ) { return array( $args[0], (int) ( $args[1] ?? 0 ), @@ -445,6 +446,7 @@ public function get_session( string $session_id ): ?array { * @param array $metadata Updated metadata * @param string $provider AI provider * @param string $model AI model + * @param string|null $provider_response_id Provider-side response/state ID. * @return bool Success */ public function update_session( @@ -452,14 +454,15 @@ public function update_session( array $messages, array $metadata = array(), string $provider = '', - string $model = '' + string $model = '', + ?string $provider_response_id = null ): bool { global $wpdb; $table_name = self::get_prefixed_table_name(); try { - $normalized_messages = AgentMessageEnvelope::normalize_many( $messages ); + $normalized_messages = WP_Agent_Message::normalize_many( $messages ); } catch ( \InvalidArgumentException $e ) { do_action( 'datamachine_log', @@ -491,6 +494,11 @@ public function update_session( $update_format[] = '%s'; } + if ( null !== $provider_response_id ) { + $update_data['provider_response_id'] = $provider_response_id; + $update_format[] = '%s'; + } + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $result = $wpdb->update( $table_name, @@ -886,7 +894,7 @@ public function get_user_session_count( * instead of creating a new one. * * @since 0.9.8 - * @param AgentWorkspaceScope $workspace Workspace owning the session. + * @param WP_Agent_Workspace_Scope $workspace Workspace owning the session. * @param int $user_id WordPress user ID. * @param int $seconds Lookback window in seconds (default 600 = 10 minutes). * @param string $context Context filter. @@ -1005,14 +1013,14 @@ public function count_unread( array $messages, ?string $last_read_at ): int { $count = 0; foreach ( $messages as $msg ) { - $msg = AgentMessageEnvelope::normalize( $msg ); + $msg = WP_Agent_Message::normalize( $msg ); if ( ( $msg['role'] ?? '' ) !== 'assistant' ) { continue; } // Skip tool call/result messages — only count visible assistant responses. - $type = $msg['type'] ?? AgentMessageEnvelope::TYPE_TEXT; - if ( AgentMessageEnvelope::TYPE_TOOL_CALL === $type || AgentMessageEnvelope::TYPE_TOOL_RESULT === $type ) { + $type = $msg['type'] ?? WP_Agent_Message::TYPE_TEXT; + if ( WP_Agent_Message::TYPE_TOOL_CALL === $type || WP_Agent_Message::TYPE_TOOL_RESULT === $type ) { continue; } @@ -1038,7 +1046,7 @@ public function count_unread( array $messages, ?string $last_read_at ): int { */ private static function normalize_messages( array $messages ): array { try { - return AgentMessageEnvelope::normalize_many( $messages ); + return WP_Agent_Message::normalize_many( $messages ); } catch ( \InvalidArgumentException $e ) { do_action( 'datamachine_log', diff --git a/inc/Core/Database/Chat/ConversationStoreFactory.php b/inc/Core/Database/Chat/ConversationStoreFactory.php index 807b685f0..da93f752c 100644 --- a/inc/Core/Database/Chat/ConversationStoreFactory.php +++ b/inc/Core/Database/Chat/ConversationStoreFactory.php @@ -22,8 +22,8 @@ namespace DataMachine\Core\Database\Chat; -use AgentsAPI\Core\Database\Chat\ConversationTranscriptStoreInterface; -use AgentsAPI\Core\Workspace\AgentWorkspaceScope; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Store; +use AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope; defined( 'ABSPATH' ) || exit; @@ -76,7 +76,7 @@ public static function get(): ConversationStoreInterface { * * The store MUST normalize messages on read to Data Machine message * shape. Implementations that only need transcript CRUD should target - * {@see ConversationTranscriptStoreInterface}; this legacy Data Machine + * {@see WP_Agent_Conversation_Store}; this legacy Data Machine * filter still expects the full aggregate so chat UI, REST, CLI, retention, * and reporting callers keep their existing behavior. A future Agents API * resolver can expose a narrower transcript-store filter without carrying @@ -115,21 +115,21 @@ public static function get(): ConversationStoreInterface { * runtime transcript persistence depends only on CRUD, not Data Machine's * chat session index, read-state, retention, or reporting product surface. * - * @return ConversationTranscriptStoreInterface + * @return WP_Agent_Conversation_Store */ - public static function get_transcript_store(): ConversationTranscriptStoreInterface { + public static function get_transcript_store(): WP_Agent_Conversation_Store { return self::get(); } /** * Resolve Data Machine's default workspace scope for local chat/transcripts. * - * @return AgentWorkspaceScope + * @return WP_Agent_Workspace_Scope */ - public static function default_workspace(): AgentWorkspaceScope { + public static function default_workspace(): WP_Agent_Workspace_Scope { $blog_id = function_exists( 'get_current_blog_id' ) ? (int) get_current_blog_id() : 1; - return AgentWorkspaceScope::from_parts( 'site', (string) max( 1, $blog_id ) ); + return WP_Agent_Workspace_Scope::from_parts( 'site', (string) max( 1, $blog_id ) ); } /** diff --git a/inc/Core/Database/Chat/ConversationStoreInterface.php b/inc/Core/Database/Chat/ConversationStoreInterface.php index 6111fa691..d6a202f67 100644 --- a/inc/Core/Database/Chat/ConversationStoreInterface.php +++ b/inc/Core/Database/Chat/ConversationStoreInterface.php @@ -8,7 +8,7 @@ * aggregate store via the `datamachine_conversation_store` filter. * * This aggregate is intentionally broader than the generic Agents API storage - * candidate. {@see ConversationTranscriptStoreInterface} is the narrow generic + * candidate. {@see WP_Agent_Conversation_Store} is the narrow generic * transcript CRUD seam. The other composed interfaces are Data Machine chat * product surfaces today: session switcher indexes, read state, retention * cleanup, and reporting/metrics. They may become optional Agents API @@ -36,14 +36,14 @@ namespace DataMachine\Core\Database\Chat; -use AgentsAPI\Core\Database\Chat\ConversationTranscriptStoreInterface; -use AgentsAPI\Core\Database\Chat\ConversationTranscriptLockInterface; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Store; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Lock; defined( 'ABSPATH' ) || exit; interface ConversationStoreInterface extends - ConversationTranscriptStoreInterface, - ConversationTranscriptLockInterface, + WP_Agent_Conversation_Store, + WP_Agent_Conversation_Lock, ConversationSessionIndexInterface, ConversationReadStateInterface, ConversationRetentionInterface, diff --git a/inc/Core/FilesRepository/AgentMemory.php b/inc/Core/FilesRepository/AgentMemory.php index 0d6653de6..640f2288c 100644 --- a/inc/Core/FilesRepository/AgentMemory.php +++ b/inc/Core/FilesRepository/AgentMemory.php @@ -6,7 +6,7 @@ * Parses markdown sections and supports section-level operations * on any agent file (MEMORY.md, SOUL.md, USER.md, etc.). * - * Persistence is delegated to an {@see AgentMemoryStoreInterface} resolved + * Persistence is delegated to an {@see WP_Agent_Memory_Store} resolved * via the `agents_api_memory_store` filter. The default store * ({@see DiskAgentMemoryStore}) preserves the byte-for-byte filesystem * behavior the codebase used before the store seam was introduced. @@ -14,17 +14,17 @@ * @package DataMachine\Core\FilesRepository * @since 0.30.0 * @since 0.45.0 Generalized to support any agent file via $filename parameter. - * @since next Whole-file IO delegated to AgentMemoryStoreInterface. + * @since next Whole-file IO delegated to WP_Agent_Memory_Store. */ namespace DataMachine\Core\FilesRepository; -use AgentsAPI\Core\FilesRepository\AgentMemoryListEntry; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; -use AgentsAPI\Core\FilesRepository\AgentMemoryReadResult; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; -use AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result; use DataMachine\Core\Workspace\WordPressWorkspaceScope; use DataMachine\Engine\AI\MemoryFileRegistry; @@ -48,20 +48,20 @@ class AgentMemory { private DirectoryManager $directory_manager; /** - * @var AgentMemoryScope + * @var WP_Agent_Memory_Scope */ - private AgentMemoryScope $scope; + private WP_Agent_Memory_Scope $scope; /** - * @var AgentMemoryStoreInterface + * @var WP_Agent_Memory_Store */ - private AgentMemoryStoreInterface $store; + private WP_Agent_Memory_Store $store; /** * @since 0.37.0 Added $user_id parameter for multi-agent partitioning. * @since 0.41.0 Added $agent_id parameter for agent-first resolution. * @since 0.45.0 Added $filename parameter for any-file support. - * @since next Switched whole-file IO to AgentMemoryStoreInterface. + * @since next Switched whole-file IO to WP_Agent_Memory_Store. * @since next Optional $layer override for explicit-layer addressing. * * @param int $user_id WordPress user ID. 0 = legacy shared directory. @@ -76,7 +76,7 @@ public function __construct( int $user_id = 0, int $agent_id = 0, string $filena $safe_filename = $this->sanitize_filename( $filename ); $workspace = WordPressWorkspaceScope::current(); - $this->scope = new AgentMemoryScope( + $this->scope = new WP_Agent_Memory_Scope( $layer ?? self::resolve_layer_for( $safe_filename ), $workspace->workspace_type, $workspace->workspace_id, @@ -105,9 +105,9 @@ public static function resolve_layer_for( string $filename ): string { * Get the resolved scope for this memory file. * * @since next - * @return AgentMemoryScope + * @return WP_Agent_Memory_Scope */ - public function get_scope(): AgentMemoryScope { + public function get_scope(): WP_Agent_Memory_Scope { return $this->scope; } @@ -164,7 +164,7 @@ public function get_all(): array { /** * Low-level read returning the raw store result. * - * Exposes the underlying {@see AgentMemoryReadResult} (content, hash, + * Exposes the underlying {@see WP_Agent_Memory_Read_Result} (content, hash, * bytes, updated_at, exists) for consumers that need richer metadata * than {@see self::get_all()}'s human-shaped response — e.g. directives * that want byte counts for size budgeting, the React UI's whole-file @@ -172,9 +172,9 @@ public function get_all(): array { * for compare-and-swap upstream. * * @since next - * @return AgentMemoryReadResult + * @return WP_Agent_Memory_Read_Result */ - public function read(): AgentMemoryReadResult { + public function read(): WP_Agent_Memory_Read_Result { return $this->store->read( $this->scope ); } @@ -216,9 +216,9 @@ public function delete(): array { /** * List all files in a single layer for the given identity. * - * Static facade over {@see AgentMemoryStoreInterface::list_layer()} + * Static facade over {@see WP_Agent_Memory_Store::list_layer()} * so directory enumeration goes through the same swap point as - * single-file IO. Callers receive a list of {@see AgentMemoryListEntry} + * single-file IO. Callers receive a list of {@see WP_Agent_Memory_List_Entry} * value objects. * * @since next @@ -226,13 +226,13 @@ public function delete(): array { * @param string $layer Layer identifier (shared|agent|user|network). * @param int $user_id WordPress user ID. 0 = default agent. * @param int $agent_id Agent ID for direct resolution. 0 = resolve from user_id. - * @return AgentMemoryListEntry[] + * @return WP_Agent_Memory_List_Entry[] */ public static function list_layer( string $layer, int $user_id = 0, int $agent_id = 0 ): array { $dm = new DirectoryManager(); $effective_user_id = $dm->get_effective_user_id( $user_id ); $workspace = WordPressWorkspaceScope::current(); - $scope_query = new AgentMemoryScope( $layer, $workspace->workspace_type, $workspace->workspace_id, $effective_user_id, $agent_id, '' ); + $scope_query = new WP_Agent_Memory_Scope( $layer, $workspace->workspace_type, $workspace->workspace_id, $effective_user_id, $agent_id, '' ); $store = AgentMemoryStoreFactory::for_scope( $scope_query ); return $store->list_layer( $scope_query ); @@ -241,7 +241,7 @@ public static function list_layer( string $layer, int $user_id = 0, int $agent_i /** * List all files under a path prefix within a layer. * - * Static facade over {@see AgentMemoryStoreInterface::list_subtree()}. + * Static facade over {@see WP_Agent_Memory_Store::list_subtree()}. * Recursive — entries' filenames are full relative paths from the * layer root (e.g. `daily/2026/04/17.md`, `contexts/chat.md`). * @@ -255,13 +255,13 @@ public static function list_layer( string $layer, int $user_id = 0, int $agent_i * @param int $user_id WordPress user ID. 0 = default agent. * @param int $agent_id Agent ID for direct resolution. 0 = resolve from user_id. * @param string $prefix Path prefix without trailing slash (e.g. 'daily', 'contexts'). - * @return AgentMemoryListEntry[] + * @return WP_Agent_Memory_List_Entry[] */ public static function list_subtree( string $layer, int $user_id, int $agent_id, string $prefix ): array { $dm = new DirectoryManager(); $effective_user_id = $dm->get_effective_user_id( $user_id ); $workspace = WordPressWorkspaceScope::current(); - $scope_query = new AgentMemoryScope( $layer, $workspace->workspace_type, $workspace->workspace_id, $effective_user_id, $agent_id, '' ); + $scope_query = new WP_Agent_Memory_Scope( $layer, $workspace->workspace_type, $workspace->workspace_id, $effective_user_id, $agent_id, '' ); $store = AgentMemoryStoreFactory::for_scope( $scope_query ); return $store->list_subtree( $scope_query, $prefix ); @@ -333,10 +333,10 @@ public function get_section( string $section_name ): array { * * @since next * @param string $content New full file content. - * @param AgentMemoryMetadata|null $metadata Optional Agents API provenance/trust metadata. + * @param WP_Agent_Memory_Metadata|null $metadata Optional Agents API provenance/trust metadata. * @return array{success: bool, message: string, file_size?: int, warning?: string} */ - public function replace_all( string $content, ?AgentMemoryMetadata $metadata = null ): array { + public function replace_all( string $content, ?WP_Agent_Memory_Metadata $metadata = null ): array { $write = $this->store->write( $this->scope, $content, null, $metadata ); if ( ! $write->success ) { @@ -624,9 +624,9 @@ private function replace_section_content( string $file_content, array $position, * @since next * * @param string $content Persisted full-file content. - * @param AgentMemoryWriteResult $write Successful store write result. + * @param WP_Agent_Memory_Write_Result $write Successful store write result. */ - private function emit_updated_event( string $content, AgentMemoryWriteResult $write ): void { + private function emit_updated_event( string $content, WP_Agent_Memory_Write_Result $write ): void { do_action( 'datamachine_agent_memory_updated', $this->scope, @@ -647,16 +647,16 @@ private function emit_deleted_event(): void { /** * Build JSON-friendly metadata for memory change events. * - * The AgentMemoryScope object remains the first event argument for typed PHP + * The WP_Agent_Memory_Scope object remains the first event argument for typed PHP * consumers; duplicated scalar identity fields let queue/log/projector code * persist the event without inspecting the value object. * * @since next * - * @param AgentMemoryWriteResult $write Successful store write result. + * @param WP_Agent_Memory_Write_Result $write Successful store write result. * @return array{layer: string, user_id: int, agent_id: int, filename: string, key: string, hash: string, bytes: int, metadata?: array, unsupported_metadata_fields?: string[]} */ - private function event_metadata( AgentMemoryWriteResult $write ): array { + private function event_metadata( WP_Agent_Memory_Write_Result $write ): array { $metadata = array( 'layer' => $this->scope->layer, 'user_id' => $this->scope->user_id, diff --git a/inc/Core/FilesRepository/AgentMemoryStoreFactory.php b/inc/Core/FilesRepository/AgentMemoryStoreFactory.php index ec6e0db88..bddf08a2d 100644 --- a/inc/Core/FilesRepository/AgentMemoryStoreFactory.php +++ b/inc/Core/FilesRepository/AgentMemoryStoreFactory.php @@ -2,7 +2,7 @@ /** * Agent Memory Store Factory * - * Resolves the active {@see AgentMemoryStoreInterface} implementation. + * Resolves the active {@see WP_Agent_Memory_Store} implementation. * * This is Data Machine's in-place resolver for a generic agent-memory * persistence contract. It intentionally keeps one public swap point using @@ -17,7 +17,7 @@ * A guideline-backed store can register here when a site provides * `wp_guideline` (for example via Gutenberg or a plugin), but Data Machine * does not require that post type. The built-in disk store remains the - * default and any implementation of AgentMemoryStoreInterface is valid. + * default and any implementation of WP_Agent_Memory_Store is valid. * * @package DataMachine\Core\FilesRepository * @since next @@ -25,8 +25,8 @@ namespace DataMachine\Core\FilesRepository; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; defined( 'ABSPATH' ) || exit; @@ -40,14 +40,14 @@ class AgentMemoryStoreFactory { * they ever want to. Most consumers will return a single store * instance regardless of scope. * - * @param AgentMemoryScope $scope Scope the caller is about to operate on. - * @return AgentMemoryStoreInterface + * @param WP_Agent_Memory_Scope $scope Scope the caller is about to operate on. + * @return WP_Agent_Memory_Store */ - public static function for_scope( AgentMemoryScope $scope ): AgentMemoryStoreInterface { + public static function for_scope( WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Store { /** * Filter: swap the agent memory persistence backend. * - * Return an {@see AgentMemoryStoreInterface} instance to short-circuit + * Return an {@see WP_Agent_Memory_Store} instance to short-circuit * the default disk-backed store. Return null (the default) to use the * built-in {@see DiskAgentMemoryStore}. * @@ -63,14 +63,14 @@ public static function for_scope( AgentMemoryScope $scope ): AgentMemoryStoreInt * * @since next * - * @param AgentMemoryStoreInterface|null $store Null to use the disk default, + * @param WP_Agent_Memory_Store|null $store Null to use the disk default, * or a swap implementation. - * @param AgentMemoryScope $scope The scope being acted on. + * @param WP_Agent_Memory_Scope $scope The scope being acted on. */ // @phpstan-ignore-next-line WordPress apply_filters accepts additional hook arguments. $store = apply_filters( 'agents_api_memory_store', null, $scope ); - if ( $store instanceof AgentMemoryStoreInterface ) { + if ( $store instanceof WP_Agent_Memory_Store ) { return $store; } diff --git a/inc/Core/FilesRepository/DailyMemory.php b/inc/Core/FilesRepository/DailyMemory.php index e3b894e9f..7fdeb594b 100644 --- a/inc/Core/FilesRepository/DailyMemory.php +++ b/inc/Core/FilesRepository/DailyMemory.php @@ -10,7 +10,7 @@ * It is NOT logs (operational telemetry). It persists agent knowledge and * is never auto-cleared. * - * Persistence is delegated to the {@see AgentMemoryStoreInterface} + * Persistence is delegated to the {@see WP_Agent_Memory_Store} * registered for the agent layer (resolved through the * `agents_api_memory_store` filter). Daily files are addressed as * relative paths within the agent layer (`daily/YYYY/MM/DD.md`), so a diff --git a/inc/Core/FilesRepository/DailyMemoryStorage.php b/inc/Core/FilesRepository/DailyMemoryStorage.php index e810fd938..a8072c488 100644 --- a/inc/Core/FilesRepository/DailyMemoryStorage.php +++ b/inc/Core/FilesRepository/DailyMemoryStorage.php @@ -4,7 +4,7 @@ * * Contract for daily memory storage backends used by the Daily Memory * abilities. The default implementation is {@see DailyMemory}, which - * delegates persistence to the active {@see AgentMemoryStoreInterface} + * delegates persistence to the active {@see WP_Agent_Memory_Store} * resolved through `agents_api_memory_store`. * * `datamachine_daily_memory_storage` is a narrower escape hatch for diff --git a/inc/Core/FilesRepository/DiskAgentMemoryStore.php b/inc/Core/FilesRepository/DiskAgentMemoryStore.php index 04be3c0d4..7c61278b5 100644 --- a/inc/Core/FilesRepository/DiskAgentMemoryStore.php +++ b/inc/Core/FilesRepository/DiskAgentMemoryStore.php @@ -2,7 +2,7 @@ /** * Disk Agent Memory Store * - * Default implementation of {@see AgentMemoryStoreInterface} that persists + * Default implementation of {@see WP_Agent_Memory_Store} that persists * agent memory records as markdown files on the local filesystem under * wp-uploads. Preserves the byte-for-byte behavior the codebase used before * the store seam was introduced. @@ -22,19 +22,19 @@ namespace DataMachine\Core\FilesRepository; -use AgentsAPI\Core\FilesRepository\AgentMemoryListEntry; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; -use AgentsAPI\Core\FilesRepository\AgentMemoryQuery; -use AgentsAPI\Core\FilesRepository\AgentMemoryReadResult; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreCapabilities; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; -use AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Query; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store_Capabilities; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result; use DataMachine\Engine\AI\MemoryFileRegistry; defined( 'ABSPATH' ) || exit; -class DiskAgentMemoryStore implements AgentMemoryStoreInterface { +class DiskAgentMemoryStore implements WP_Agent_Memory_Store { /** * @var DirectoryManager @@ -48,30 +48,30 @@ public function __construct( ?DirectoryManager $directory_manager = null ) { /** * @inheritDoc */ - public function capabilities(): AgentMemoryStoreCapabilities { - return AgentMemoryStoreCapabilities::none(); + public function capabilities(): WP_Agent_Memory_Store_Capabilities { + return WP_Agent_Memory_Store_Capabilities::none(); } /** * @inheritDoc */ - public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryReadResult { + public function read( WP_Agent_Memory_Scope $scope, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_Read_Result { $filepath = $this->resolve_filepath( $scope ); if ( ! file_exists( $filepath ) ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } $fs = FilesystemHelper::get(); if ( ! $fs ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } $content = (string) $fs->get_contents( $filepath ); $bytes = strlen( $content ); $updated_at = filemtime( $filepath ); - return new AgentMemoryReadResult( + return new WP_Agent_Memory_Read_Result( true, $content, sha1( $content ), @@ -87,29 +87,29 @@ public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMem * * `$if_match` is intentionally ignored — see class docblock. */ - public function write( AgentMemoryScope $scope, string $content, ?string $if_match = null, ?AgentMemoryMetadata $metadata = null ): AgentMemoryWriteResult { + public function write( WP_Agent_Memory_Scope $scope, string $content, ?string $if_match = null, ?WP_Agent_Memory_Metadata $metadata = null ): WP_Agent_Memory_Write_Result { $filepath = $this->resolve_filepath( $scope ); $dir = dirname( $filepath ); if ( ! $this->directory_manager->ensure_directory_exists( $dir ) ) { - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } $fs = FilesystemHelper::get(); if ( ! $fs ) { - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } $ok = $fs->put_contents( $filepath, $content, FS_CHMOD_FILE ); if ( false === $ok ) { - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } FilesystemHelper::make_group_writable( $filepath ); $bytes = strlen( $content ); - return AgentMemoryWriteResult::ok( + return WP_Agent_Memory_Write_Result::ok( sha1( $content ), $bytes, null, @@ -120,34 +120,34 @@ public function write( AgentMemoryScope $scope, string $content, ?string $if_mat /** * @inheritDoc */ - public function exists( AgentMemoryScope $scope ): bool { + public function exists( WP_Agent_Memory_Scope $scope ): bool { return file_exists( $this->resolve_filepath( $scope ) ); } /** * @inheritDoc */ - public function delete( AgentMemoryScope $scope ): AgentMemoryWriteResult { + public function delete( WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Write_Result { $filepath = $this->resolve_filepath( $scope ); if ( ! file_exists( $filepath ) ) { // Idempotent: deleting a missing file is success. - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } $deleted = wp_delete_file( $filepath ); if ( ! $deleted ) { - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } /** * @inheritDoc */ - public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $query = null ): array { + public function list_layer( WP_Agent_Memory_Scope $scope_query, ?WP_Agent_Memory_Query $query = null ): array { $layer_dir = $this->resolve_layer_directory( $scope_query ); if ( ! is_dir( $layer_dir ) ) { @@ -168,7 +168,7 @@ public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $qu } $mtime = filemtime( $path ); - $entries[] = new AgentMemoryListEntry( + $entries[] = new WP_Agent_Memory_List_Entry( $entry, $scope_query->layer, (int) filesize( $path ), @@ -188,7 +188,7 @@ public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $qu * Returned filenames are relative paths from the layer root, * including the prefix (e.g. `daily/2026/04/17.md`). */ - public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?AgentMemoryQuery $query = null ): array { + public function list_subtree( WP_Agent_Memory_Scope $scope_query, string $prefix, ?WP_Agent_Memory_Query $query = null ): array { $prefix = trim( $prefix, '/' ); if ( '' === $prefix ) { return array(); @@ -220,7 +220,7 @@ public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?Ag $relative = ltrim( substr( $path, strlen( $layer_dir ) ), '/' ); $mtime = $file_info->getMTime(); - $entries[] = new AgentMemoryListEntry( + $entries[] = new WP_Agent_Memory_List_Entry( $relative, $scope_query->layer, (int) $file_info->getSize(), @@ -242,7 +242,7 @@ public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?Ag /** * @return string[] */ - private function unsupported_query_fields( ?AgentMemoryQuery $query ): array { + private function unsupported_query_fields( ?WP_Agent_Memory_Query $query ): array { if ( null === $query ) { return array(); } @@ -263,7 +263,7 @@ private function unsupported_query_fields( ?AgentMemoryQuery $query ): array { * Resolve a scope to its absolute filesystem path, honoring registered * convention paths (e.g. AGENTS.md at ABSPATH). */ - private function resolve_filepath( AgentMemoryScope $scope ): string { + private function resolve_filepath( WP_Agent_Memory_Scope $scope ): string { $layer_dir = $this->resolve_layer_directory( $scope ); // Convention-path files (e.g. AGENTS.md) override the layer directory. @@ -278,7 +278,7 @@ private function resolve_filepath( AgentMemoryScope $scope ): string { /** * Resolve the on-disk directory for a given (layer, user_id, agent_id). */ - private function resolve_layer_directory( AgentMemoryScope $scope ): string { + private function resolve_layer_directory( WP_Agent_Memory_Scope $scope ): string { switch ( $scope->layer ) { case MemoryFileRegistry::LAYER_SHARED: return $this->directory_manager->get_shared_directory(); diff --git a/inc/Core/FilesRepository/GuidelineAgentMemoryStore.php b/inc/Core/FilesRepository/GuidelineAgentMemoryStore.php index 6e27b6ee9..7980179a2 100644 --- a/inc/Core/FilesRepository/GuidelineAgentMemoryStore.php +++ b/inc/Core/FilesRepository/GuidelineAgentMemoryStore.php @@ -2,7 +2,7 @@ /** * Guideline Agent Memory Store * - * Optional {@see AgentMemoryStoreInterface} implementation that persists + * Optional {@see WP_Agent_Memory_Store} implementation that persists * agent memory records as `wp_guideline` posts tagged with * `wp_guideline_type=memory`. * @@ -22,18 +22,18 @@ namespace DataMachine\Core\FilesRepository; -use AgentsAPI\Core\FilesRepository\AgentMemoryListEntry; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; -use AgentsAPI\Core\FilesRepository\AgentMemoryQuery; -use AgentsAPI\Core\FilesRepository\AgentMemoryReadResult; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreCapabilities; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; -use AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Query; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store_Capabilities; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result; defined( 'ABSPATH' ) || exit; -class GuidelineAgentMemoryStore implements AgentMemoryStoreInterface { +class GuidelineAgentMemoryStore implements WP_Agent_Memory_Store { const POST_TYPE = 'wp_guideline'; const TAXONOMY = 'wp_guideline_type'; @@ -75,31 +75,31 @@ public static function is_available(): bool { * sha1 is sufficient here: this is a stable key inside the `wp_guideline` * CPT, not a security primitive. * - * @param AgentMemoryScope $scope Scope to encode. + * @param WP_Agent_Memory_Scope $scope Scope to encode. * @return string */ - public static function post_name_for_scope( AgentMemoryScope $scope ): string { + public static function post_name_for_scope( WP_Agent_Memory_Scope $scope ): string { return 'memory-' . sha1( $scope->key() ); } /** * @inheritDoc */ - public function capabilities(): AgentMemoryStoreCapabilities { - return AgentMemoryStoreCapabilities::all(); + public function capabilities(): WP_Agent_Memory_Store_Capabilities { + return WP_Agent_Memory_Store_Capabilities::all(); } /** * @inheritDoc */ - public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryReadResult { + public function read( WP_Agent_Memory_Scope $scope, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_Read_Result { $post = $this->find_post( $scope ); if ( ! $post instanceof \WP_Post ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } if ( ! $this->can_read_post( $post ) ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } $content = (string) $post->post_content; @@ -113,15 +113,15 @@ public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMem $updated = strtotime( (string) $post->post_modified_gmt ); $updated = false === $updated ? null : (int) $updated; - return new AgentMemoryReadResult( true, $content, $hash, $bytes, $updated, $this->metadata_for_post( $post, $metadata_fields ) ); + return new WP_Agent_Memory_Read_Result( true, $content, $hash, $bytes, $updated, $this->metadata_for_post( $post, $metadata_fields ) ); } /** * @inheritDoc */ - public function write( AgentMemoryScope $scope, string $content, ?string $if_match = null, ?AgentMemoryMetadata $metadata = null ): AgentMemoryWriteResult { + public function write( WP_Agent_Memory_Scope $scope, string $content, ?string $if_match = null, ?WP_Agent_Memory_Metadata $metadata = null ): WP_Agent_Memory_Write_Result { if ( ! self::is_available() ) { - return AgentMemoryWriteResult::failure( 'capability' ); + return WP_Agent_Memory_Write_Result::failure( 'capability' ); } $existing = $this->find_post( $scope ); @@ -133,22 +133,22 @@ public function write( AgentMemoryScope $scope, string $content, ?string $if_mat } if ( $stored !== $if_match ) { - return AgentMemoryWriteResult::failure( 'conflict' ); + return WP_Agent_Memory_Write_Result::failure( 'conflict' ); } } if ( $existing instanceof \WP_Post && ! $this->can_write_post( $existing ) ) { - return AgentMemoryWriteResult::failure( 'capability' ); + return WP_Agent_Memory_Write_Result::failure( 'capability' ); } if ( ! $existing instanceof \WP_Post && ! $this->can_create_scope( $scope ) ) { - return AgentMemoryWriteResult::failure( 'capability' ); + return WP_Agent_Memory_Write_Result::failure( 'capability' ); } $hash = sha1( $content ); $bytes = strlen( $content ); $author = $scope->user_id > 0 ? $scope->user_id : 0; - $metadata = ( $metadata ?? new AgentMemoryMetadata() )->with_defaults(); + $metadata = ( $metadata ?? new WP_Agent_Memory_Metadata() )->with_defaults(); $guideline_metadata = $this->guideline_metadata_for_scope( $scope ); if ( $existing instanceof \WP_Post ) { @@ -163,7 +163,7 @@ public function write( AgentMemoryScope $scope, string $content, ?string $if_mat ); if ( is_wp_error( $updated ) ) { - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } update_post_meta( $existing->ID, self::META_HASH, $hash ); @@ -177,7 +177,7 @@ public function write( AgentMemoryScope $scope, string $content, ?string $if_mat $this->emit_guideline_updated_event( $existing->ID ); - return AgentMemoryWriteResult::ok( $hash, $bytes, $metadata ); + return WP_Agent_Memory_Write_Result::ok( $hash, $bytes, $metadata ); } $meta_input = array_merge( @@ -211,13 +211,13 @@ public function write( AgentMemoryScope $scope, string $content, ?string $if_mat ); if ( is_wp_error( $post_id ) || 0 === $post_id ) { - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } wp_set_object_terms( $post_id, array( self::TERM_MEMORY ), self::TAXONOMY, false ); $this->emit_guideline_updated_event( (int) $post_id ); - return AgentMemoryWriteResult::ok( $hash, $bytes, $metadata ); + return WP_Agent_Memory_Write_Result::ok( $hash, $bytes, $metadata ); } /** @@ -234,7 +234,7 @@ private function emit_guideline_updated_event( int $post_id ): void { /** * @inheritDoc */ - public function exists( AgentMemoryScope $scope ): bool { + public function exists( WP_Agent_Memory_Scope $scope ): bool { $post = $this->find_post( $scope ); return $post instanceof \WP_Post && $this->can_read_post( $post ); } @@ -242,28 +242,28 @@ public function exists( AgentMemoryScope $scope ): bool { /** * @inheritDoc */ - public function delete( AgentMemoryScope $scope ): AgentMemoryWriteResult { + public function delete( WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Write_Result { $post = $this->find_post( $scope ); if ( ! $post instanceof \WP_Post ) { - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } if ( ! $this->can_write_post( $post ) ) { - return AgentMemoryWriteResult::failure( 'capability' ); + return WP_Agent_Memory_Write_Result::failure( 'capability' ); } $deleted = wp_delete_post( $post->ID, true ); if ( ! $deleted ) { - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } /** * @inheritDoc */ - public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $query = null ): array { + public function list_layer( WP_Agent_Memory_Scope $scope_query, ?WP_Agent_Memory_Query $query = null ): array { $posts = $this->query_scope_posts( $scope_query, null ); $entries = array(); @@ -277,7 +277,7 @@ public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $qu continue; } - $entry = $this->entry_for( $post, $filename, $scope_query->layer, $query?->metadata_fields ?? AgentMemoryMetadata::FIELDS ); + $entry = $this->entry_for( $post, $filename, $scope_query->layer, $query?->metadata_fields ?? WP_Agent_Memory_Metadata::FIELDS ); if ( $this->entry_matches_query( $entry, $query ) ) { $entries[] = $entry; } @@ -294,7 +294,7 @@ public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $qu /** * @inheritDoc */ - public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?AgentMemoryQuery $query = null ): array { + public function list_subtree( WP_Agent_Memory_Scope $scope_query, string $prefix, ?WP_Agent_Memory_Query $query = null ): array { $prefix = trim( $prefix, '/' ); if ( '' === $prefix ) { return array(); @@ -313,7 +313,7 @@ public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?Ag continue; } - $entry = $this->entry_for( $post, $filename, $scope_query->layer, $query?->metadata_fields ?? AgentMemoryMetadata::FIELDS ); + $entry = $this->entry_for( $post, $filename, $scope_query->layer, $query?->metadata_fields ?? WP_Agent_Memory_Metadata::FIELDS ); if ( $this->entry_matches_query( $entry, $query ) ) { $entries[] = $entry; } @@ -330,10 +330,10 @@ public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?Ag /** * Locate the post matching a full memory scope. * - * @param AgentMemoryScope $scope Scope to locate. + * @param WP_Agent_Memory_Scope $scope Scope to locate. * @return \WP_Post|null */ - private function find_post( AgentMemoryScope $scope ): ?\WP_Post { + private function find_post( WP_Agent_Memory_Scope $scope ): ?\WP_Post { if ( ! self::is_available() ) { return null; } @@ -357,11 +357,11 @@ private function find_post( AgentMemoryScope $scope ): ?\WP_Post { /** * Query memory posts for a layer/user/agent triple, optionally scoped to a subtree. * - * @param AgentMemoryScope $scope_query Scope query. Filename is ignored. + * @param WP_Agent_Memory_Scope $scope_query Scope query. Filename is ignored. * @param string|null $prefix Optional subtree prefix. * @return \WP_Post[] */ - private function query_scope_posts( AgentMemoryScope $scope_query, ?string $prefix ): array { + private function query_scope_posts( WP_Agent_Memory_Scope $scope_query, ?string $prefix ): array { if ( ! self::is_available() ) { return array(); } @@ -437,28 +437,28 @@ private function query_scope_posts( AgentMemoryScope $scope_query, ?string $pref * @param \WP_Post $post Memory post. * @param string $filename Scope filename. * @param string $layer Scope layer. - * @return AgentMemoryListEntry + * @return WP_Agent_Memory_List_Entry */ - private function entry_for( \WP_Post $post, string $filename, string $layer, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryListEntry { + private function entry_for( \WP_Post $post, string $filename, string $layer, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_List_Entry { $bytes_meta = get_post_meta( $post->ID, self::META_BYTES, true ); $bytes = is_numeric( $bytes_meta ) ? (int) $bytes_meta : strlen( (string) $post->post_content ); $updated = strtotime( (string) $post->post_modified_gmt ); $updated = false === $updated ? null : (int) $updated; - return new AgentMemoryListEntry( $filename, $layer, $bytes, $updated, $this->metadata_for_post( $post, $metadata_fields ) ); + return new WP_Agent_Memory_List_Entry( $filename, $layer, $bytes, $updated, $this->metadata_for_post( $post, $metadata_fields ) ); } - private function metadata_for_post( \WP_Post $post, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryMetadata { + private function metadata_for_post( \WP_Post $post, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_Metadata { $raw = get_post_meta( $post->ID, self::META_METADATA, true ); if ( ! is_array( $raw ) ) { $raw = array(); } - return AgentMemoryMetadata::from_array( $raw )->with_defaults()->only_fields( $metadata_fields ); + return WP_Agent_Memory_Metadata::from_array( $raw )->with_defaults()->only_fields( $metadata_fields ); } - private function entry_matches_query( AgentMemoryListEntry $entry, ?AgentMemoryQuery $query ): bool { + private function entry_matches_query( WP_Agent_Memory_List_Entry $entry, ?WP_Agent_Memory_Query $query ): bool { if ( null === $query || null === $entry->metadata ) { return true; } @@ -480,10 +480,10 @@ private function entry_matches_query( AgentMemoryListEntry $entry, ?AgentMemoryQ } /** - * @param AgentMemoryListEntry[] $entries Entries to sort. - * @return AgentMemoryListEntry[] + * @param WP_Agent_Memory_List_Entry[] $entries Entries to sort. + * @return WP_Agent_Memory_List_Entry[] */ - private function sort_entries( array $entries, ?AgentMemoryQuery $query ): array { + private function sort_entries( array $entries, ?WP_Agent_Memory_Query $query ): array { if ( null === $query || null === $query->order_by ) { return $entries; } @@ -492,7 +492,7 @@ private function sort_entries( array $entries, ?AgentMemoryQuery $query ): array $order = 'asc' === strtolower( $query->order ) ? 'asc' : 'desc'; usort( $entries, - static function ( AgentMemoryListEntry $left, AgentMemoryListEntry $right ) use ( $order_by, $order ): int { + static function ( WP_Agent_Memory_List_Entry $left, WP_Agent_Memory_List_Entry $right ) use ( $order_by, $order ): int { $left_value = self::entry_sort_value( $left, $order_by ); $right_value = self::entry_sort_value( $right, $order_by ); $delta = $left_value <=> $right_value; @@ -503,7 +503,7 @@ static function ( AgentMemoryListEntry $left, AgentMemoryListEntry $right ) use return $entries; } - private static function entry_sort_value( AgentMemoryListEntry $entry, string $field ) { + private static function entry_sort_value( WP_Agent_Memory_List_Entry $entry, string $field ) { if ( 'updated_at' === $field ) { return $entry->updated_at ?? 0; } @@ -523,10 +523,10 @@ private static function entry_sort_value( AgentMemoryListEntry $entry, string $f /** * Build the shared wp_guideline metadata required by Agents API. * - * @param AgentMemoryScope $scope Memory scope. + * @param WP_Agent_Memory_Scope $scope Memory scope. * @return array */ - private function guideline_metadata_for_scope( AgentMemoryScope $scope ): array { + private function guideline_metadata_for_scope( WP_Agent_Memory_Scope $scope ): array { return array( self::GUIDELINE_META_SCOPE => $this->guideline_scope_for_layer( $scope->layer ), self::GUIDELINE_META_USER_ID => $scope->user_id, @@ -587,10 +587,10 @@ private function can_write_post( \WP_Post $post ): bool { /** * Check whether the current context may create memory for a scope. * - * @param AgentMemoryScope $scope Target scope. + * @param WP_Agent_Memory_Scope $scope Target scope. * @return bool Whether create is allowed. */ - private function can_create_scope( AgentMemoryScope $scope ): bool { + private function can_create_scope( WP_Agent_Memory_Scope $scope ): bool { if ( $this->should_bypass_capability_checks() ) { return true; } @@ -626,10 +626,10 @@ private function is_private_memory_post( \WP_Post $post ): bool { /** * Check if the current user owns a private memory scope. * - * @param AgentMemoryScope $scope Memory scope. + * @param WP_Agent_Memory_Scope $scope Memory scope. * @return bool Whether the current user owns the scope. */ - private function current_user_owns_scope( AgentMemoryScope $scope ): bool { + private function current_user_owns_scope( WP_Agent_Memory_Scope $scope ): bool { return function_exists( 'get_current_user_id' ) && $scope->user_id > 0 && get_current_user_id() === $scope->user_id; } } diff --git a/inc/Core/Workspace/WordPressWorkspaceScope.php b/inc/Core/Workspace/WordPressWorkspaceScope.php index 3b24d9ca5..23247f125 100644 --- a/inc/Core/Workspace/WordPressWorkspaceScope.php +++ b/inc/Core/Workspace/WordPressWorkspaceScope.php @@ -7,7 +7,7 @@ namespace DataMachine\Core\Workspace; -use AgentsAPI\Core\Workspace\AgentWorkspaceScope; +use AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope; defined( 'ABSPATH' ) || exit; @@ -22,10 +22,10 @@ class WordPressWorkspaceScope { * WordPress-specific details such as blog ID and network ID stay in adapter * metadata; the generic boundary is the Agents API workspace value object. * - * @return AgentWorkspaceScope + * @return WP_Agent_Workspace_Scope */ - public static function current(): AgentWorkspaceScope { - return AgentWorkspaceScope::from_parts( 'site', self::current_site_id() ); + public static function current(): WP_Agent_Workspace_Scope { + return WP_Agent_Workspace_Scope::from_parts( 'site', self::current_site_id() ); } /** diff --git a/inc/Engine/AI/Actions/ActionPolicyResolver.php b/inc/Engine/AI/Actions/ActionPolicyResolver.php index 2ba737d90..a00b4b90a 100644 --- a/inc/Engine/AI/Actions/ActionPolicyResolver.php +++ b/inc/Engine/AI/Actions/ActionPolicyResolver.php @@ -5,7 +5,7 @@ * Determines HOW a tool invocation is allowed to execute. Sibling to * ToolPolicyResolver (which decides IF a tool is visible) and * MemoryPolicyResolver (which decides WHICH memory files inject). Where - * ToolPolicy answers "can the agent see this tool?", ActionPolicy answers + * ToolPolicy answers "can the agent see this tool?", WP_Agent_Action_Policy answers * "having called it, does it execute directly, stage for user approval, * or get refused?" * @@ -34,18 +34,10 @@ namespace DataMachine\Engine\AI\Actions; -use AgentsAPI\AI\Tools\ActionPolicy; +use AgentsAPI\AI\Tools\WP_Agent_Action_Policy; defined( 'ABSPATH' ) || exit; -if ( ! class_exists( '\WP_Agent_Action_Policy_Resolver' ) ) { - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy-interface.php'; - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy-filter.php'; - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy.php'; - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-action-policy-provider-interface.php'; - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-action-policy-resolver.php'; -} - class ActionPolicyResolver { /** @@ -62,9 +54,9 @@ class ActionPolicyResolver { * Keep the legacy Data Machine constant names as public aliases while the * generic vocabulary lives in Agents API. */ - public const POLICY_DIRECT = ActionPolicy::DIRECT; - public const POLICY_PREVIEW = ActionPolicy::PREVIEW; - public const POLICY_FORBIDDEN = ActionPolicy::FORBIDDEN; + public const POLICY_DIRECT = WP_Agent_Action_Policy::DIRECT; + public const POLICY_PREVIEW = WP_Agent_Action_Policy::PREVIEW; + public const POLICY_FORBIDDEN = WP_Agent_Action_Policy::FORBIDDEN; private \WP_Agent_Action_Policy_Resolver $resolver; @@ -178,7 +170,7 @@ private function getAgentConfig( int $agent_id ): array { private function applyDataMachineFilter( string $policy, string $tool_name, string $mode, array $context ): string { $filtered = apply_filters( 'datamachine_tool_action_policy', $policy, $tool_name, $mode, $context ); - return ActionPolicy::normalize( $filtered ) ?? $policy; + return WP_Agent_Action_Policy::normalize( $filtered ) ?? $policy; } /** diff --git a/inc/Engine/AI/Actions/DataMachineModeActionPolicyProvider.php b/inc/Engine/AI/Actions/DataMachineModeActionPolicyProvider.php index 0b60648b8..6f01ad9a7 100644 --- a/inc/Engine/AI/Actions/DataMachineModeActionPolicyProvider.php +++ b/inc/Engine/AI/Actions/DataMachineModeActionPolicyProvider.php @@ -7,18 +7,14 @@ namespace DataMachine\Engine\AI\Actions; -use AgentsAPI\AI\Tools\ActionPolicy; +use AgentsAPI\AI\Tools\WP_Agent_Action_Policy; defined( 'ABSPATH' ) || exit; -if ( ! interface_exists( '\WP_Agent_Action_Policy_Provider_Interface' ) ) { - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-action-policy-provider-interface.php'; -} - /** * Preserves Data Machine's mode-specific tool defaults through Agents API. */ -final class DataMachineModeActionPolicyProvider implements \WP_Agent_Action_Policy_Provider_Interface { +final class DataMachineModeActionPolicyProvider implements \WP_Agent_Action_Policy_Provider { /** * Return a mode-specific tool action policy when declared. @@ -31,6 +27,6 @@ public function get_action_policy( array $context ): ?string { $tool_def = is_array( $context['tool_def'] ?? null ) ? $context['tool_def'] : array(); $key = 'action_policy_' . $mode; - return ActionPolicy::normalize( $tool_def[ $key ] ?? null ); + return WP_Agent_Action_Policy::normalize( $tool_def[ $key ] ?? null ); } } diff --git a/inc/Engine/AI/Actions/PendingActionHelper.php b/inc/Engine/AI/Actions/PendingActionHelper.php index a174b059f..d5284c7dd 100644 --- a/inc/Engine/AI/Actions/PendingActionHelper.php +++ b/inc/Engine/AI/Actions/PendingActionHelper.php @@ -3,7 +3,7 @@ * PendingActionHelper — convenience wrapper for tool handlers that need to * stage an invocation for user approval. * - * Tools that opt into ActionPolicy should never hand-roll the store/envelope + * Tools that opt into WP_Agent_Action_Policy should never hand-roll the store/envelope * shape. Call PendingActionHelper::stage() from the 'preview' branch of the * tool handler and return its result directly. The AI sees a standardized * envelope that tells it to show the preview to the user and wait for @@ -29,8 +29,8 @@ namespace DataMachine\Engine\AI\Actions; -use AgentsAPI\AI\AgentMessageEnvelope; -use AgentsAPI\AI\Approvals\PendingAction; +use AgentsAPI\AI\WP_Agent_Message; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action; defined( 'ABSPATH' ) || exit; @@ -177,8 +177,8 @@ public static function stage( array $args ): array { 'expires_at' => null !== $expires_at ? gmdate( 'c', $expires_at ) : null, ); - if ( class_exists( PendingAction::class ) ) { - $pending_action = PendingAction::from_array( $pending_action )->to_array(); + if ( class_exists( WP_Agent_Pending_Action::class ) ) { + $pending_action = WP_Agent_Pending_Action::from_array( $pending_action )->to_array(); } $envelope_payload = array_merge( @@ -195,12 +195,12 @@ public static function stage( array $args ): array { ), ); - $envelope = method_exists( AgentMessageEnvelope::class, 'approvalRequired' ) - ? AgentMessageEnvelope::approvalRequired( $payload['summary'], $envelope_payload, $envelope_metadata ) + $envelope = method_exists( WP_Agent_Message::class, 'approvalRequired' ) + ? WP_Agent_Message::approvalRequired( $payload['summary'], $envelope_payload, $envelope_metadata ) : array( - 'schema' => AgentMessageEnvelope::SCHEMA, - 'version' => AgentMessageEnvelope::VERSION, - 'type' => AgentMessageEnvelope::TYPE_APPROVAL_REQUIRED, + 'schema' => WP_Agent_Message::SCHEMA, + 'version' => WP_Agent_Message::VERSION, + 'type' => WP_Agent_Message::TYPE_APPROVAL_REQUIRED, 'role' => 'tool', 'content' => $payload['summary'], 'payload' => $envelope_payload, diff --git a/inc/Engine/AI/Actions/PendingActionResolverAdapter.php b/inc/Engine/AI/Actions/PendingActionResolverAdapter.php index e9f1cd53f..8ddbde8fb 100644 --- a/inc/Engine/AI/Actions/PendingActionResolverAdapter.php +++ b/inc/Engine/AI/Actions/PendingActionResolverAdapter.php @@ -7,8 +7,8 @@ namespace DataMachine\Engine\AI\Actions; -use AgentsAPI\AI\Approvals\ApprovalDecision; -use AgentsAPI\AI\Approvals\PendingActionResolverInterface; +use AgentsAPI\AI\Approvals\WP_Agent_Approval_Decision; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Resolver; defined( 'ABSPATH' ) || exit; @@ -16,19 +16,19 @@ * Implements the generic Agents API resolver contract while preserving * `datamachine/resolve-pending-action` as the single Data Machine resolver. */ -final class PendingActionResolverAdapter implements PendingActionResolverInterface { +final class PendingActionResolverAdapter implements WP_Agent_Pending_Action_Resolver { /** * Resolve a pending action by identifier. * * @param string $pending_action_id Stable pending-action identifier. - * @param ApprovalDecision $decision Accepted/rejected decision. + * @param WP_Agent_Approval_Decision $decision Accepted/rejected decision. * @param string $resolver Resolver audit identifier. * @param array $payload Fresh resolver payload. * @param array $context Optional caller context. * @return mixed */ - public function resolve_pending_action( string $pending_action_id, ApprovalDecision $decision, string $resolver, array $payload = array(), array $context = array() ): mixed { + public function resolve_pending_action( string $pending_action_id, WP_Agent_Approval_Decision $decision, string $resolver, array $payload = array(), array $context = array() ): mixed { return ResolvePendingActionAbility::execute( array( 'action_id' => $pending_action_id, diff --git a/inc/Engine/AI/Actions/PendingActionStore.php b/inc/Engine/AI/Actions/PendingActionStore.php index 23a040fe8..047deafc6 100644 --- a/inc/Engine/AI/Actions/PendingActionStore.php +++ b/inc/Engine/AI/Actions/PendingActionStore.php @@ -34,10 +34,10 @@ namespace DataMachine\Engine\AI\Actions; -use AgentsAPI\AI\Approvals\ApprovalDecision; -use AgentsAPI\AI\Approvals\PendingAction; -use AgentsAPI\AI\Approvals\PendingActionStatus; -use AgentsAPI\AI\Approvals\PendingActionStoreInterface; +use AgentsAPI\AI\Approvals\WP_Agent_Approval_Decision; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Status; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Store; use DataMachine\Core\Workspace\WordPressWorkspaceScope; defined( 'ABSPATH' ) || exit; @@ -67,9 +67,9 @@ class PendingActionStore { /** * Agents API store contract singleton. * - * @var PendingActionStoreInterface|null + * @var WP_Agent_Pending_Action_Store|null */ - private static ?PendingActionStoreInterface $adapter = null; + private static ?WP_Agent_Pending_Action_Store $adapter = null; /** * Create or update the durable pending-actions table. @@ -171,7 +171,7 @@ public static function get_table_name(): string { /** * Return the Agents API store adapter when the contract is available. */ - public static function adapter(): PendingActionStoreInterface { + public static function adapter(): WP_Agent_Pending_Action_Store { if ( null === self::$adapter ) { self::$adapter = new PendingActionStoreAdapter(); } @@ -193,7 +193,7 @@ public static function store( string $action_id, array $payload ): bool { global $wpdb; $workspace = isset( $payload['workspace'] ) && is_array( $payload['workspace'] ) - ? \AgentsAPI\Core\Workspace\AgentWorkspaceScope::from_array( $payload['workspace'] ) + ? \AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope::from_array( $payload['workspace'] ) : WordPressWorkspaceScope::current(); $payload['workspace'] = $workspace->to_array(); $context = is_array( $payload['context'] ?? null ) ? $payload['context'] : array(); @@ -204,7 +204,7 @@ public static function store( string $action_id, array $payload ): bool { $payload['created_at'] = time(); $payload['expires_at'] = time() + self::resolve_ttl( $payload ); $payload['action_id'] = $action_id; - $payload['status'] = PendingActionStatus::PENDING; + $payload['status'] = WP_Agent_Pending_Action_Status::PENDING; return set_transient( self::TRANSIENT_PREFIX . $action_id, $payload, self::resolve_ttl( $payload ) ); } @@ -217,7 +217,7 @@ public static function store( string $action_id, array $payload ): bool { $payload['created_at'] = $created_at; $payload['expires_at'] = $expires_at; $payload['action_id'] = $action_id; - $payload['status'] = PendingActionStatus::PENDING; + $payload['status'] = WP_Agent_Pending_Action_Status::PENDING; $row = array( 'action_id' => $action_id, @@ -233,7 +233,7 @@ public static function store( string $action_id, array $payload ): bool { 'creator' => self::nullable_string( $payload['creator'] ?? ( isset( $payload['created_by'] ) && (int) $payload['created_by'] > 0 ? 'user:' . (int) $payload['created_by'] : null ) ), 'context' => self::encode_json( $payload['context'] ?? array() ), 'metadata' => self::encode_json( $payload['metadata'] ?? array() ), - 'status' => PendingActionStatus::PENDING, + 'status' => WP_Agent_Pending_Action_Status::PENDING, 'created_at' => gmdate( 'Y-m-d H:i:s', $created_at ), 'expires_at' => $expires_at > 0 ? gmdate( 'Y-m-d H:i:s', $expires_at ) : null, 'resolved_at' => null, @@ -270,12 +270,12 @@ public static function get( string $action_id, bool $include_resolved = false ): return null; } - if ( ! $include_resolved && PendingActionStatus::PENDING !== $row['status'] ) { + if ( ! $include_resolved && WP_Agent_Pending_Action_Status::PENDING !== $row['status'] ) { return null; } - if ( PendingActionStatus::PENDING === $row['status'] && self::is_expired_row( $row ) ) { - self::record_resolution( $action_id, PendingActionStatus::EXPIRED, null, 'Pending action expired.', 'system:expiration' ); + if ( WP_Agent_Pending_Action_Status::PENDING === $row['status'] && self::is_expired_row( $row ) ) { + self::record_resolution( $action_id, WP_Agent_Pending_Action_Status::EXPIRED, null, 'Pending action expired.', 'system:expiration' ); if ( ! $include_resolved ) { return null; } @@ -305,7 +305,7 @@ public static function delete( string $action_id ): bool { return delete_transient( self::TRANSIENT_PREFIX . $action_id ); } - return self::record_resolution( $action_id, PendingActionStatus::DELETED, null, 'Pending action deleted.', self::current_resolver() ); + return self::record_resolution( $action_id, WP_Agent_Pending_Action_Status::DELETED, null, 'Pending action deleted.', self::current_resolver() ); } /** @@ -469,10 +469,10 @@ public static function expire_due_actions( ?string $before = null ): int { $wpdb->prepare( 'UPDATE %i SET status = %s, resolved_at = %s, resolution_error = %s WHERE status = %s AND expires_at IS NOT NULL AND expires_at <= %s', self::get_table_name(), - PendingActionStatus::EXPIRED, + WP_Agent_Pending_Action_Status::EXPIRED, $boundary, 'Pending action expired.', - PendingActionStatus::PENDING, + WP_Agent_Pending_Action_Status::PENDING, $boundary ) ); @@ -492,21 +492,21 @@ public static function generate_id(): string { /** * Store an Agents API pending-action value object. */ - public static function store_action( PendingAction $action ): bool { + public static function store_action( WP_Agent_Pending_Action $action ): bool { return self::store( $action->get_action_id(), self::action_to_payload( $action ) ); } /** * Fetch a pending action as an Agents API value object. */ - public static function get_action( string $action_id, bool $include_resolved = false ): ?PendingAction { + public static function get_action( string $action_id, bool $include_resolved = false ): ?WP_Agent_Pending_Action { $payload = self::get( $action_id, $include_resolved ); if ( null === $payload ) { return null; } try { - return PendingAction::from_array( self::payload_to_action_array( $payload ) ); + return WP_Agent_Pending_Action::from_array( self::payload_to_action_array( $payload ) ); } catch ( \InvalidArgumentException $error ) { return null; } @@ -515,13 +515,13 @@ public static function get_action( string $action_id, bool $include_resolved = f /** * List pending actions as Agents API value objects. * - * @return array + * @return array */ public static function list_actions( array $filters = array() ): array { $actions = array(); foreach ( self::list( $filters ) as $payload ) { try { - $actions[] = PendingAction::from_array( self::payload_to_action_array( $payload ) ); + $actions[] = WP_Agent_Pending_Action::from_array( self::payload_to_action_array( $payload ) ); } catch ( \InvalidArgumentException $error ) { continue; } @@ -533,7 +533,7 @@ public static function list_actions( array $filters = array() ): array { /** * Record a resolution from the Agents API store contract. */ - public static function record_action_resolution( string $action_id, ApprovalDecision $decision, string $resolver, $result = null, ?string $error = null, array $metadata = array() ): bool { + public static function record_action_resolution( string $action_id, WP_Agent_Approval_Decision $decision, string $resolver, $result = null, ?string $error = null, array $metadata = array() ): bool { return self::record_resolution( $action_id, $decision->value(), $result, $error, $resolver, $metadata ); } @@ -577,7 +577,7 @@ private static function row_to_payload( array $row ): array { 'creator' => isset( $row['creator'] ) ? (string) $row['creator'] : null, 'context' => self::decode_json( $row['context'] ?? null ), 'metadata' => self::decode_json( $row['metadata'] ?? null ), - 'status' => (string) ( $row['status'] ?? PendingActionStatus::PENDING ), + 'status' => (string) ( $row['status'] ?? WP_Agent_Pending_Action_Status::PENDING ), 'created_at' => $created_at, 'created_at_iso' => $created_at > 0 ? gmdate( 'c', $created_at ) : null, 'expires_at' => $expires_at, @@ -595,7 +595,7 @@ private static function row_to_payload( array $row ): array { /** * Convert an Agents API pending action to Data Machine's persisted payload. */ - private static function action_to_payload( PendingAction $action ): array { + private static function action_to_payload( WP_Agent_Pending_Action $action ): array { $data = $action->to_array(); $metadata = isset( $data['metadata'] ) && is_array( $data['metadata'] ) ? $data['metadata'] : array(); @@ -649,7 +649,7 @@ private static function payload_to_action_array( array $payload ): array { 'workspace' => $payload['workspace'] ?? null, 'agent' => $payload['agent'] ?? ( ! empty( $payload['agent_id'] ) ? 'agent:' . (int) $payload['agent_id'] : null ), 'creator' => $payload['creator'] ?? ( ! empty( $payload['created_by'] ) ? 'user:' . (int) $payload['created_by'] : null ), - 'status' => (string) ( $payload['status'] ?? PendingActionStatus::PENDING ), + 'status' => (string) ( $payload['status'] ?? WP_Agent_Pending_Action_Status::PENDING ), 'created_at' => $created_at, 'expires_at' => $expires_at, 'resolved_at' => $resolved_at, @@ -775,7 +775,7 @@ private static function nullable_positive_int( $value ): ?int { */ private static function normalize_status( string $status ): string { try { - return PendingActionStatus::normalize( $status ); + return WP_Agent_Pending_Action_Status::normalize( $status ); } catch ( \InvalidArgumentException $error ) { return ''; } diff --git a/inc/Engine/AI/Actions/PendingActionStoreAdapter.php b/inc/Engine/AI/Actions/PendingActionStoreAdapter.php index 6fd672e2c..28fdf706b 100644 --- a/inc/Engine/AI/Actions/PendingActionStoreAdapter.php +++ b/inc/Engine/AI/Actions/PendingActionStoreAdapter.php @@ -7,9 +7,9 @@ namespace DataMachine\Engine\AI\Actions; -use AgentsAPI\AI\Approvals\ApprovalDecision; -use AgentsAPI\AI\Approvals\PendingAction; -use AgentsAPI\AI\Approvals\PendingActionStoreInterface; +use AgentsAPI\AI\Approvals\WP_Agent_Approval_Decision; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Store; defined( 'ABSPATH' ) || exit; @@ -17,15 +17,15 @@ * Implements the generic Agents API store contract without changing the * existing static Data Machine PendingActionStore facade. */ -final class PendingActionStoreAdapter implements PendingActionStoreInterface { +final class PendingActionStoreAdapter implements WP_Agent_Pending_Action_Store { /** * Persist a pending action payload. * - * @param PendingAction $action Durable pending action record. + * @param WP_Agent_Pending_Action $action Durable pending action record. * @return bool */ - public function store( PendingAction $action ): bool { + public function store( WP_Agent_Pending_Action $action ): bool { return PendingActionStore::store_action( $action ); } @@ -34,9 +34,9 @@ public function store( PendingAction $action ): bool { * * @param string $action_id Durable action identifier. * @param bool $include_resolved Whether terminal audit rows may be returned. - * @return PendingAction|null + * @return WP_Agent_Pending_Action|null */ - public function get( string $action_id, bool $include_resolved = false ): ?PendingAction { + public function get( string $action_id, bool $include_resolved = false ): ?WP_Agent_Pending_Action { return PendingActionStore::get_action( $action_id, $include_resolved ); } @@ -44,7 +44,7 @@ public function get( string $action_id, bool $include_resolved = false ): ?Pendi * List durable pending action records. * * @param array $filters Query filters. - * @return array + * @return array */ public function list( array $filters = array() ): array { return PendingActionStore::list_actions( $filters ); @@ -64,14 +64,14 @@ public function summary( array $filters = array() ): array { * Record a durable terminal resolution. * * @param string $action_id Durable action identifier. - * @param ApprovalDecision $decision Resolution decision. + * @param WP_Agent_Approval_Decision $decision Resolution decision. * @param string $resolver Resolver audit identifier. * @param mixed|null $result Resolution result. * @param string|null $error Resolution error. * @param array $metadata Resolution metadata. * @return bool */ - public function record_resolution( string $action_id, ApprovalDecision $decision, string $resolver, $result = null, ?string $error = null, array $metadata = array() ): bool { + public function record_resolution( string $action_id, WP_Agent_Approval_Decision $decision, string $resolver, $result = null, ?string $error = null, array $metadata = array() ): bool { return PendingActionStore::record_action_resolution( $action_id, $decision, $resolver, $result, $error, $metadata ); } diff --git a/inc/Engine/AI/Actions/ResolvePendingActionAbility.php b/inc/Engine/AI/Actions/ResolvePendingActionAbility.php index 4f966c5c7..814e7c221 100644 --- a/inc/Engine/AI/Actions/ResolvePendingActionAbility.php +++ b/inc/Engine/AI/Actions/ResolvePendingActionAbility.php @@ -2,7 +2,7 @@ /** * ResolvePendingActionAbility — accept or reject a pending tool invocation. * - * When a tool runs under ActionPolicy::POLICY_PREVIEW, it stages an invocation + * When a tool runs under WP_Agent_Action_Policy::POLICY_PREVIEW, it stages an invocation * via PendingActionHelper::stage() instead of executing directly. This ability * is the generic resolver that replays (accept) or discards (reject) the * stored payload, dispatching to the correct handler by `kind`. @@ -38,10 +38,10 @@ namespace DataMachine\Engine\AI\Actions; -use AgentsAPI\AI\Approvals\ApprovalDecision; -use AgentsAPI\AI\Approvals\PendingAction; -use AgentsAPI\AI\Approvals\PendingActionHandlerInterface; -use AgentsAPI\AI\Approvals\PendingActionStatus; +use AgentsAPI\AI\Approvals\WP_Agent_Approval_Decision; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Handler; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Status; use DataMachine\Abilities\PermissionHelper; defined( 'ABSPATH' ) || exit; @@ -67,7 +67,6 @@ class ResolvePendingActionAbility { */ public static function adapter(): PendingActionResolverAdapter { if ( null === self::$adapter ) { - self::loadApprovalContracts(); self::$adapter = new PendingActionResolverAdapter(); } @@ -93,7 +92,7 @@ private function register_ability(): void { 'datamachine/resolve-pending-action', array( 'label' => __( 'Resolve Pending Action', 'data-machine' ), - 'description' => __( 'Accept or reject a pending tool invocation staged by ActionPolicy.', 'data-machine' ), + 'description' => __( 'Accept or reject a pending tool invocation staged by WP_Agent_Action_Policy.', 'data-machine' ), 'category' => 'datamachine-actions', 'input_schema' => array( 'type' => 'object', @@ -244,7 +243,7 @@ public static function execute( array $input ): array { if ( ! is_array( $handler ) || empty( $handler['apply'] ) || ! self::isApplyHandler( $handler['apply'] ) ) { // No handler registered — can't apply, but reject is still safe. if ( $decision->is_rejected() ) { - PendingActionStore::record_resolution( $action_id, PendingActionStatus::REJECTED, null, null, $resolver, array( 'reason' => 'no_handler_rejected' ) ); + PendingActionStore::record_resolution( $action_id, WP_Agent_Pending_Action_Status::REJECTED, null, null, $resolver, array( 'reason' => 'no_handler_rejected' ) ); self::fireResolvedAction( $decision_value, $action_id, $kind, $payload, null ); return array( 'success' => true, @@ -302,7 +301,7 @@ public static function execute( array $input ): array { } if ( $decision->is_rejected() ) { - PendingActionStore::record_resolution( $action_id, PendingActionStatus::REJECTED, null, null, $resolver ); + PendingActionStore::record_resolution( $action_id, WP_Agent_Pending_Action_Status::REJECTED, null, null, $resolver ); self::fireResolvedAction( $decision_value, $action_id, $kind, $payload, null ); return array( 'success' => true, @@ -316,7 +315,7 @@ public static function execute( array $input ): array { $result = self::applyHandler( $handler, $decision, $apply_input, $payload, $resolver_payload, $resolver_context, $pending_action ); if ( is_wp_error( $result ) ) { - PendingActionStore::record_resolution( $action_id, PendingActionStatus::ACCEPTED, null, $result->get_error_message(), $resolver ); + PendingActionStore::record_resolution( $action_id, WP_Agent_Pending_Action_Status::ACCEPTED, null, $result->get_error_message(), $resolver ); self::fireResolvedAction( $decision_value, $action_id, $kind, $payload, $result ); return array( 'success' => false, @@ -328,7 +327,7 @@ public static function execute( array $input ): array { } if ( is_array( $result ) && array_key_exists( 'success', $result ) && false === $result['success'] ) { - PendingActionStore::record_resolution( $action_id, PendingActionStatus::ACCEPTED, $result, $result['error'] ?? 'Apply handler reported failure.', $resolver ); + PendingActionStore::record_resolution( $action_id, WP_Agent_Pending_Action_Status::ACCEPTED, $result, $result['error'] ?? 'Apply handler reported failure.', $resolver ); self::fireResolvedAction( $decision_value, $action_id, $kind, $payload, $result ); return array( 'success' => false, @@ -340,7 +339,7 @@ public static function execute( array $input ): array { ); } - PendingActionStore::record_resolution( $action_id, PendingActionStatus::ACCEPTED, $result, null, $resolver ); + PendingActionStore::record_resolution( $action_id, WP_Agent_Pending_Action_Status::ACCEPTED, $result, null, $resolver ); self::fireResolvedAction( $decision_value, $action_id, $kind, $payload, $result ); return array( @@ -385,18 +384,16 @@ private static function getKindHandlers(): array { * parallel Data Machine primitive. * * @param array $handler Handler configuration. - * @param ApprovalDecision $decision Accepted/rejected decision. + * @param WP_Agent_Approval_Decision $decision Accepted/rejected decision. * @param array $apply_input Stored apply input. * @param array $payload Stored pending action payload. * @param array $resolver_payload Fresh resolver payload. * @param array $resolver_context Optional resolver context. * @return mixed */ - private static function applyHandler( array $handler, ApprovalDecision $decision, array $apply_input, array $payload, array $resolver_payload = array(), array $resolver_context = array(), ?PendingAction $pending_action = null ) { - self::loadApprovalContracts(); - + private static function applyHandler( array $handler, WP_Agent_Approval_Decision $decision, array $apply_input, array $payload, array $resolver_payload = array(), array $resolver_context = array(), ?WP_Agent_Pending_Action $pending_action = null ) { $apply = $handler['apply']; - if ( $apply instanceof PendingActionHandlerInterface ) { + if ( $apply instanceof WP_Agent_Pending_Action_Handler ) { if ( null === $pending_action ) { return new \WP_Error( 'invalid_pending_action', 'Stored pending action could not be normalized.' ); } @@ -418,9 +415,7 @@ private static function isApplyHandler( $apply ): bool { return true; } - self::loadApprovalContracts(); - - return $apply instanceof PendingActionHandlerInterface; + return $apply instanceof WP_Agent_Pending_Action_Handler; } /** @@ -428,11 +423,9 @@ private static function isApplyHandler( $apply ): bool { * * @return bool|\WP_Error|null Null means no contract handler was provided. */ - private static function canResolveWithHandlerContract( array $handler, ?PendingAction $pending_action, ApprovalDecision $decision, array $resolver_payload, array $resolver_context ) { - self::loadApprovalContracts(); - + private static function canResolveWithHandlerContract( array $handler, ?WP_Agent_Pending_Action $pending_action, WP_Agent_Approval_Decision $decision, array $resolver_payload, array $resolver_context ) { $apply = $handler['apply'] ?? null; - if ( ! $apply instanceof PendingActionHandlerInterface ) { + if ( ! $apply instanceof WP_Agent_Pending_Action_Handler ) { return null; } @@ -447,35 +440,16 @@ private static function canResolveWithHandlerContract( array $handler, ?PendingA * Normalize an external decision value to the Agents API approval contract. * * @param string $value Request decision value. - * @return ApprovalDecision|null + * @return WP_Agent_Approval_Decision|null */ - private static function approvalDecisionFromValue( string $value ): ?ApprovalDecision { - self::loadApprovalContracts(); - + private static function approvalDecisionFromValue( string $value ): ?WP_Agent_Approval_Decision { try { - return ApprovalDecision::from_string( $value ); + return WP_Agent_Approval_Decision::from_string( $value ); } catch ( \InvalidArgumentException $e ) { return null; } } - /** - * Load Composer-installed Agents API approval contracts when the plugin is not active. - */ - private static function loadApprovalContracts(): void { - if ( class_exists( ApprovalDecision::class ) && class_exists( PendingAction::class ) && class_exists( PendingActionStatus::class ) && interface_exists( PendingActionHandlerInterface::class ) ) { - return; - } - - $approvals_path = dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Approvals/'; - foreach ( array( 'ApprovalDecision.php', 'PendingActionStatus.php', 'PendingAction.php', 'PendingActionHandlerInterface.php', 'PendingActionResolverInterface.php' ) as $file ) { - $path = $approvals_path . $file; - if ( file_exists( $path ) ) { - require_once $path; - } - } - } - /** * Build a resolver audit identifier for the active user. */ diff --git a/inc/Engine/AI/ConversationManager.php b/inc/Engine/AI/ConversationManager.php index b077eb90f..8e6418315 100644 --- a/inc/Engine/AI/ConversationManager.php +++ b/inc/Engine/AI/ConversationManager.php @@ -11,7 +11,7 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; defined( 'ABSPATH' ) || exit; @@ -41,7 +41,7 @@ class ConversationManager { * @return array Message envelope. */ public static function buildConversationMessage( string $role, $content, array $metadata = array() ): array { - return AgentMessageEnvelope::text( $role, $content, array_merge( array( 'timestamp' => gmdate( 'c' ) ), $metadata ) ); + return WP_Agent_Message::text( $role, $content, array_merge( array( 'timestamp' => gmdate( 'c' ) ), $metadata ) ); } /** @@ -127,7 +127,7 @@ public static function formatToolCallMessage( string $tool_name, array $tool_par $message .= ' with parameters: ' . implode( ', ', $params_str ); } - return AgentMessageEnvelope::toolCall( + return WP_Agent_Message::toolCall( $message, $tool_name, $tool_parameters, @@ -180,7 +180,7 @@ public static function formatToolResultMessage( string $tool_name, array $tool_r $payload['error'] = $tool_result['error']; } - return AgentMessageEnvelope::toolResult( $content, $tool_name, $payload, array( 'timestamp' => gmdate( 'c' ) ) ); + return WP_Agent_Message::toolResult( $content, $tool_name, $payload, array( 'timestamp' => gmdate( 'c' ) ) ); } /** @@ -300,13 +300,13 @@ public static function validateToolCall( string $tool_name, array $tool_paramete // Scan ALL previous tool_call messages, not just the most recent one. for ( $i = count( $conversation_messages ) - 1; $i >= 0; $i-- ) { - $message = AgentMessageEnvelope::normalize( $conversation_messages[ $i ] ); + $message = WP_Agent_Message::normalize( $conversation_messages[ $i ] ); if ( 'assistant' !== $message['role'] ) { continue; } - if ( AgentMessageEnvelope::TYPE_TOOL_CALL !== $message['type'] ) { + if ( WP_Agent_Message::TYPE_TOOL_CALL !== $message['type'] ) { continue; } @@ -341,9 +341,9 @@ public static function validateToolCall( string $tool_name, array $tool_paramete * @return array|null Tool call details or null if not a tool call message */ public static function extractToolCallFromMessage( array $message ): ?array { - $envelope = AgentMessageEnvelope::normalize( $message ); + $envelope = WP_Agent_Message::normalize( $message ); - if ( AgentMessageEnvelope::TYPE_TOOL_CALL === $envelope['type'] ) { + if ( WP_Agent_Message::TYPE_TOOL_CALL === $envelope['type'] ) { $tool_name = $envelope['payload']['tool_name'] ?? null; $parameters = $envelope['payload']['parameters'] ?? null; diff --git a/inc/Engine/AI/DataMachineAgentConsentPolicy.php b/inc/Engine/AI/DataMachineAgentConsentPolicy.php index 00bd1265c..292d88d52 100644 --- a/inc/Engine/AI/DataMachineAgentConsentPolicy.php +++ b/inc/Engine/AI/DataMachineAgentConsentPolicy.php @@ -7,86 +7,86 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\Consent\AgentConsentDecision; -use AgentsAPI\AI\Consent\AgentConsentOperation; +use AgentsAPI\AI\Consent\WP_Agent_Consent_Decision; +use AgentsAPI\AI\Consent\WP_Agent_Consent_Operation; defined( 'ABSPATH' ) || exit; /** * Implements Agents API consent decisions using Data Machine product policy. */ -class DataMachineAgentConsentPolicy implements \WP_Agent_Consent_Policy_Interface { +class DataMachineAgentConsentPolicy implements \WP_Agent_Consent_Policy { /** * Resolve the active product policy. * - * @return \WP_Agent_Consent_Policy_Interface + * @return \WP_Agent_Consent_Policy */ - public static function get(): \WP_Agent_Consent_Policy_Interface { + public static function get(): \WP_Agent_Consent_Policy { $policy = apply_filters( 'datamachine_agent_consent_policy', new self() ); - return $policy instanceof \WP_Agent_Consent_Policy_Interface ? $policy : new self(); + return $policy instanceof \WP_Agent_Consent_Policy ? $policy : new self(); } /** * @inheritDoc */ - public function can_store_memory( array $context = array() ): AgentConsentDecision { - if ( true === ( $context['permission_granted'] ?? null ) || true === ( $context[ AgentConsentOperation::STORE_MEMORY ] ?? null ) ) { - return $this->allowed( AgentConsentOperation::STORE_MEMORY, 'datamachine_memory_permission', $context ); + public function can_store_memory( array $context = array() ): WP_Agent_Consent_Decision { + if ( true === ( $context['permission_granted'] ?? null ) || true === ( $context[ WP_Agent_Consent_Operation::STORE_MEMORY ] ?? null ) ) { + return $this->allowed( WP_Agent_Consent_Operation::STORE_MEMORY, 'datamachine_memory_permission', $context ); } - return $this->denied( AgentConsentOperation::STORE_MEMORY, 'datamachine_memory_permission_missing', $context ); + return $this->denied( WP_Agent_Consent_Operation::STORE_MEMORY, 'datamachine_memory_permission_missing', $context ); } /** * @inheritDoc */ - public function can_use_memory( array $context = array() ): AgentConsentDecision { - if ( false === ( $context[ AgentConsentOperation::USE_MEMORY ] ?? null ) ) { - return $this->denied( AgentConsentOperation::USE_MEMORY, 'datamachine_memory_use_denied', $context ); + public function can_use_memory( array $context = array() ): WP_Agent_Consent_Decision { + if ( false === ( $context[ WP_Agent_Consent_Operation::USE_MEMORY ] ?? null ) ) { + return $this->denied( WP_Agent_Consent_Operation::USE_MEMORY, 'datamachine_memory_use_denied', $context ); } - return $this->allowed( AgentConsentOperation::USE_MEMORY, 'datamachine_memory_policy', $context ); + return $this->allowed( WP_Agent_Consent_Operation::USE_MEMORY, 'datamachine_memory_policy', $context ); } /** * @inheritDoc */ - public function can_store_transcript( array $context = array() ): AgentConsentDecision { + public function can_store_transcript( array $context = array() ): WP_Agent_Consent_Decision { $mode = strtolower( (string) ( $context['mode'] ?? '' ) ); if ( 'chat' === $mode && true === $this->is_interactive( $context ) ) { - return $this->allowed( AgentConsentOperation::STORE_TRANSCRIPT, 'datamachine_chat_session', $context ); + return $this->allowed( WP_Agent_Consent_Operation::STORE_TRANSCRIPT, 'datamachine_chat_session', $context ); } - if ( true === ( $context['persist_transcript'] ?? null ) || true === ( $context[ AgentConsentOperation::STORE_TRANSCRIPT ] ?? null ) ) { - return $this->allowed( AgentConsentOperation::STORE_TRANSCRIPT, 'datamachine_transcript_opt_in', $context ); + if ( true === ( $context['persist_transcript'] ?? null ) || true === ( $context[ WP_Agent_Consent_Operation::STORE_TRANSCRIPT ] ?? null ) ) { + return $this->allowed( WP_Agent_Consent_Operation::STORE_TRANSCRIPT, 'datamachine_transcript_opt_in', $context ); } - return $this->denied( AgentConsentOperation::STORE_TRANSCRIPT, 'datamachine_transcript_not_configured', $context ); + return $this->denied( WP_Agent_Consent_Operation::STORE_TRANSCRIPT, 'datamachine_transcript_not_configured', $context ); } /** * @inheritDoc */ - public function can_share_transcript( array $context = array() ): AgentConsentDecision { - if ( true === ( $context[ AgentConsentOperation::SHARE_TRANSCRIPT ] ?? null ) ) { - return $this->allowed( AgentConsentOperation::SHARE_TRANSCRIPT, 'explicit_share_transcript_consent', $context ); + public function can_share_transcript( array $context = array() ): WP_Agent_Consent_Decision { + if ( true === ( $context[ WP_Agent_Consent_Operation::SHARE_TRANSCRIPT ] ?? null ) ) { + return $this->allowed( WP_Agent_Consent_Operation::SHARE_TRANSCRIPT, 'explicit_share_transcript_consent', $context ); } - return $this->denied( AgentConsentOperation::SHARE_TRANSCRIPT, 'share_transcript_consent_missing', $context ); + return $this->denied( WP_Agent_Consent_Operation::SHARE_TRANSCRIPT, 'share_transcript_consent_missing', $context ); } /** * @inheritDoc */ - public function can_escalate_to_human( array $context = array() ): AgentConsentDecision { - if ( true === ( $context[ AgentConsentOperation::ESCALATE_TO_HUMAN ] ?? null ) ) { - return $this->allowed( AgentConsentOperation::ESCALATE_TO_HUMAN, 'explicit_escalation_consent', $context ); + public function can_escalate_to_human( array $context = array() ): WP_Agent_Consent_Decision { + if ( true === ( $context[ WP_Agent_Consent_Operation::ESCALATE_TO_HUMAN ] ?? null ) ) { + return $this->allowed( WP_Agent_Consent_Operation::ESCALATE_TO_HUMAN, 'explicit_escalation_consent', $context ); } - return $this->denied( AgentConsentOperation::ESCALATE_TO_HUMAN, 'escalation_consent_missing', $context ); + return $this->denied( WP_Agent_Consent_Operation::ESCALATE_TO_HUMAN, 'escalation_consent_missing', $context ); } /** @@ -95,10 +95,10 @@ public function can_escalate_to_human( array $context = array() ): AgentConsentD * @param string $operation Consent operation. * @param string $reason Reason code. * @param array $context Policy context. - * @return AgentConsentDecision + * @return WP_Agent_Consent_Decision */ - private function allowed( string $operation, string $reason, array $context ): AgentConsentDecision { - return AgentConsentDecision::allowed( $operation, $reason, $this->audit_metadata( $operation, $context ) ); + private function allowed( string $operation, string $reason, array $context ): WP_Agent_Consent_Decision { + return WP_Agent_Consent_Decision::allowed( $operation, $reason, $this->audit_metadata( $operation, $context ) ); } /** @@ -107,10 +107,10 @@ private function allowed( string $operation, string $reason, array $context ): A * @param string $operation Consent operation. * @param string $reason Reason code. * @param array $context Policy context. - * @return AgentConsentDecision + * @return WP_Agent_Consent_Decision */ - private function denied( string $operation, string $reason, array $context ): AgentConsentDecision { - return AgentConsentDecision::denied( $operation, $reason, $this->audit_metadata( $operation, $context ) ); + private function denied( string $operation, string $reason, array $context ): WP_Agent_Consent_Decision { + return WP_Agent_Consent_Decision::denied( $operation, $reason, $this->audit_metadata( $operation, $context ) ); } /** diff --git a/inc/Engine/AI/DataMachineHandlerCompletionPolicy.php b/inc/Engine/AI/DataMachineHandlerCompletionPolicy.php index 7a6715481..07ebfc53c 100644 --- a/inc/Engine/AI/DataMachineHandlerCompletionPolicy.php +++ b/inc/Engine/AI/DataMachineHandlerCompletionPolicy.php @@ -7,15 +7,15 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\AgentConversationCompletionDecision; -use AgentsAPI\AI\AgentConversationCompletionPolicyInterface; +use AgentsAPI\AI\WP_Agent_Conversation_Completion_Decision; +use AgentsAPI\AI\WP_Agent_Conversation_Completion_Policy; defined( 'ABSPATH' ) || exit; /** * Preserves pipeline handler completion behavior outside the generic turn loop. */ -class DataMachineHandlerCompletionPolicy implements AgentConversationCompletionPolicyInterface { +class DataMachineHandlerCompletionPolicy implements WP_Agent_Conversation_Completion_Policy { /** @var array Required handler slugs configured by the adjacent pipeline step. */ private array $configured_handlers; @@ -33,12 +33,12 @@ public function __construct( array $configured_handlers = array() ) { /** * @inheritDoc */ - public function recordToolResult( string $tool_name, ?array $tool_def, array $tool_result, array $runtime_context, int $turn_count ): AgentConversationCompletionDecision { + public function recordToolResult( string $tool_name, ?array $tool_def, array $tool_result, array $runtime_context, int $turn_count ): WP_Agent_Conversation_Completion_Decision { $is_handler_tool = is_array( $tool_def ) && isset( $tool_def['handler'] ); $mode = (string) ( $runtime_context['mode'] ?? '' ); if ( 'pipeline' !== $mode || ! $is_handler_tool || ! ( $tool_result['success'] ?? false ) ) { - return AgentConversationCompletionDecision::incomplete(); + return WP_Agent_Conversation_Completion_Decision::incomplete(); } $handler_slug = $tool_def['handler'] ?? null; @@ -47,7 +47,7 @@ public function recordToolResult( string $tool_name, ?array $tool_def, array $to } if ( empty( $this->configured_handlers ) ) { - return AgentConversationCompletionDecision::complete( + return WP_Agent_Conversation_Completion_Decision::complete( 'AIConversationLoop: Handler tool executed (legacy mode), ending conversation', array( 'tool_name' => $tool_name, @@ -58,7 +58,7 @@ public function recordToolResult( string $tool_name, ?array $tool_def, array $to $remaining = array_diff( $this->configured_handlers, array_unique( $this->executed_handler_slugs ) ); if ( empty( $remaining ) ) { - return AgentConversationCompletionDecision::complete( + return WP_Agent_Conversation_Completion_Decision::complete( 'AIConversationLoop: All configured handlers executed, ending conversation', array( 'tool_name' => $tool_name, @@ -69,7 +69,7 @@ public function recordToolResult( string $tool_name, ?array $tool_def, array $to ); } - return AgentConversationCompletionDecision::incomplete( + return WP_Agent_Conversation_Completion_Decision::incomplete( 'AIConversationLoop: Handler executed, waiting for remaining handlers', array( 'tool_name' => $tool_name, diff --git a/inc/Engine/AI/DataMachinePipelineTranscriptPersister.php b/inc/Engine/AI/DataMachinePipelineTranscriptPersister.php index 14f0c400e..35dabc566 100644 --- a/inc/Engine/AI/DataMachinePipelineTranscriptPersister.php +++ b/inc/Engine/AI/DataMachinePipelineTranscriptPersister.php @@ -7,8 +7,8 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\AgentConversationRequest; -use AgentsAPI\AI\AgentConversationTranscriptPersisterInterface; +use AgentsAPI\AI\WP_Agent_Conversation_Request; +use AgentsAPI\AI\WP_Agent_Transcript_Persister; use DataMachine\Core\Database\Chat\ConversationStoreFactory; use DataMachine\Core\Workspace\WordPressWorkspaceScope; @@ -17,12 +17,12 @@ /** * Persists opted-in pipeline transcripts through Data Machine's transcript store. */ -class DataMachinePipelineTranscriptPersister implements AgentConversationTranscriptPersisterInterface { +class DataMachinePipelineTranscriptPersister implements WP_Agent_Transcript_Persister { /** * @inheritDoc */ - public function persist( array $messages, AgentConversationRequest $request, array $result ): string { + public function persist( array $messages, WP_Agent_Conversation_Request $request, array $result ): string { $runtime_context = $request->runtimeContext(); $metadata = $request->metadata(); $provider = (string) ( $metadata['provider'] ?? '' ); diff --git a/inc/Engine/AI/DefaultAgentConversationCompletionPolicy.php b/inc/Engine/AI/DefaultAgentConversationCompletionPolicy.php index 8f0efb34c..729fcee1a 100644 --- a/inc/Engine/AI/DefaultAgentConversationCompletionPolicy.php +++ b/inc/Engine/AI/DefaultAgentConversationCompletionPolicy.php @@ -7,22 +7,22 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\AgentConversationCompletionDecision; -use AgentsAPI\AI\AgentConversationCompletionPolicyInterface; +use AgentsAPI\AI\WP_Agent_Conversation_Completion_Decision; +use AgentsAPI\AI\WP_Agent_Conversation_Completion_Policy; defined( 'ABSPATH' ) || exit; /** * Generic policy: tool calls alone do not complete the loop. */ -class DefaultAgentConversationCompletionPolicy implements AgentConversationCompletionPolicyInterface { +class DefaultAgentConversationCompletionPolicy implements WP_Agent_Conversation_Completion_Policy { /** * @inheritDoc */ - public function recordToolResult( string $tool_name, ?array $tool_def, array $tool_result, array $runtime_context, int $turn_count ): AgentConversationCompletionDecision { + public function recordToolResult( string $tool_name, ?array $tool_def, array $tool_result, array $runtime_context, int $turn_count ): WP_Agent_Conversation_Completion_Decision { unset( $tool_name, $tool_def, $tool_result, $runtime_context, $turn_count ); - return AgentConversationCompletionDecision::incomplete(); + return WP_Agent_Conversation_Completion_Decision::incomplete(); } } diff --git a/inc/Engine/AI/Directives/CoreMemoryFilesDirective.php b/inc/Engine/AI/Directives/CoreMemoryFilesDirective.php index 9e664251d..f53880674 100644 --- a/inc/Engine/AI/Directives/CoreMemoryFilesDirective.php +++ b/inc/Engine/AI/Directives/CoreMemoryFilesDirective.php @@ -25,9 +25,9 @@ namespace DataMachine\Engine\AI\Directives; -use AgentsAPI\AI\Context\ContextConflictKind; -use AgentsAPI\AI\Context\DefaultContextConflictResolver; -use AgentsAPI\AI\Context\RetrievedContextItem; +use AgentsAPI\AI\Context\WP_Agent_Context_Conflict_Kind; +use AgentsAPI\AI\Context\WP_Agent_Default_Context_Conflict_Resolver; +use AgentsAPI\AI\Context\WP_Agent_Context_Item; use DataMachine\Core\FilesRepository\AgentMemory; use DataMachine\Core\FilesRepository\DirectoryManager; use DataMachine\Engine\AI\Memory\MemoryPolicyResolver; @@ -92,15 +92,15 @@ public static function get_outputs( string $provider_name, array $tools, ?string continue; } - $items[] = new RetrievedContextItem( + $items[] = new WP_Agent_Context_Item( $content, array( 'filename' => $filename, 'layer' => $layer, ), - $meta['authority_tier'] ?? MemoryFileRegistry::get( $filename )['authority_tier'] ?? \AgentsAPI\AI\Context\ContextAuthorityTier::AGENT_MEMORY, + $meta['authority_tier'] ?? MemoryFileRegistry::get( $filename )['authority_tier'] ?? \AgentsAPI\AI\Context\WP_Agent_Context_Authority_Tier::AGENT_MEMORY, $meta['provenance'] ?? array( 'source_ref' => $filename ), - $meta['conflict_kind'] ?? ContextConflictKind::AUTHORITATIVE_FACT, + $meta['conflict_kind'] ?? WP_Agent_Context_Conflict_Kind::AUTHORITATIVE_FACT, is_string( $meta['conflict_key'] ?? null ) ? $meta['conflict_key'] : null, array( 'priority' => (int) ( $meta['priority'] ?? 50 ), @@ -118,16 +118,16 @@ public static function get_outputs( string $provider_name, array $tools, ?string * resolved by the generic conflict resolver, so product/support/workspace * authority can beat lower-scope agent memory when both assert the same fact. * - * @param RetrievedContextItem[] $items Retrieved memory context items. + * @param WP_Agent_Context_Item[] $items Retrieved memory context items. * @param array $payload Runtime payload. - * @return RetrievedContextItem[] Items that should be injected, preserving original order. + * @return WP_Agent_Context_Item[] Items that should be injected, preserving original order. */ private static function resolve_context_conflicts( array $items, array $payload ): array { $items = apply_filters( 'datamachine_retrieved_memory_context_items', $items, $payload ); - $items = array_values( array_filter( $items, static fn( $item ): bool => $item instanceof RetrievedContextItem ) ); + $items = array_values( array_filter( $items, static fn( $item ): bool => $item instanceof WP_Agent_Context_Item ) ); - $resolver = apply_filters( 'datamachine_context_conflict_resolver', new DefaultContextConflictResolver(), $payload ); - $resolutions = $resolver instanceof \AgentsAPI\AI\Context\ContextConflictResolverInterface + $resolver = apply_filters( 'datamachine_context_conflict_resolver', new WP_Agent_Default_Context_Conflict_Resolver(), $payload ); + $resolutions = $resolver instanceof \AgentsAPI\AI\Context\WP_Agent_Context_Conflict_Resolver ? $resolver->resolve( $items, $payload ) : array(); @@ -141,18 +141,18 @@ private static function resolve_context_conflicts( array $items, array $payload return array_values( array_filter( $items, - static fn( RetrievedContextItem $item ): bool => ! in_array( spl_object_id( $item ), $rejected, true ) + static fn( WP_Agent_Context_Item $item ): bool => ! in_array( spl_object_id( $item ), $rejected, true ) ) ); } /** - * @param RetrievedContextItem[] $items Context items to inject. + * @param WP_Agent_Context_Item[] $items Context items to inject. * @return array */ private static function items_to_outputs( array $items ): array { return array_map( - static fn( RetrievedContextItem $item ): array => array( + static fn( WP_Agent_Context_Item $item ): array => array( 'type' => 'system_text', 'content' => $item->content, ), diff --git a/inc/Engine/AI/Directives/DirectiveRenderer.php b/inc/Engine/AI/Directives/DirectiveRenderer.php index d5ea98468..ace844c9b 100644 --- a/inc/Engine/AI/Directives/DirectiveRenderer.php +++ b/inc/Engine/AI/Directives/DirectiveRenderer.php @@ -9,7 +9,7 @@ namespace DataMachine\Engine\AI\Directives; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; defined( 'ABSPATH' ) || exit; @@ -22,7 +22,7 @@ public static function renderMessages( array $validated_outputs ): array { $type = $output['type'] ?? ''; if ( 'system_text' === $type ) { - $messages[] = AgentMessageEnvelope::text( 'system', $output['content'] ); + $messages[] = WP_Agent_Message::text( 'system', $output['content'] ); continue; } @@ -30,12 +30,12 @@ public static function renderMessages( array $validated_outputs ): array { $label = $output['label']; $data = $output['data']; - $messages[] = AgentMessageEnvelope::text( 'system', $label . ":\n\n" . wp_json_encode( $data, JSON_PRETTY_PRINT ) ); + $messages[] = WP_Agent_Message::text( 'system', $label . ":\n\n" . wp_json_encode( $data, JSON_PRETTY_PRINT ) ); continue; } if ( 'system_file' === $type ) { - $messages[] = AgentMessageEnvelope::text( + $messages[] = WP_Agent_Message::text( 'system', array( array( diff --git a/inc/Engine/AI/IterationBudgetRegistry.php b/inc/Engine/AI/IterationBudgetRegistry.php index c09cd0c7e..a8e06ef8e 100644 --- a/inc/Engine/AI/IterationBudgetRegistry.php +++ b/inc/Engine/AI/IterationBudgetRegistry.php @@ -5,7 +5,7 @@ * Registry for named bounded-iteration budgets. Each registration * declares the budget's ceiling resolution rules (default, site-setting * key, clamp bounds) so consumers can instantiate a fresh - * {@see IterationBudget} at runtime without duplicating config-lookup + * {@see WP_Agent_Iteration_Budget} at runtime without duplicating config-lookup * boilerplate. * * Ceiling resolution order (per {@see create()}): @@ -24,7 +24,7 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\IterationBudget; +use AgentsAPI\AI\WP_Agent_Iteration_Budget; use DataMachine\Core\PluginSettings; defined( 'ABSPATH' ) || exit; @@ -96,7 +96,7 @@ public static function registered_names(): array { * Create a fresh budget for a named registration. * * Resolves the ceiling through the documented fallback chain and - * returns a new {@see IterationBudget} seeded at $current. + * returns a new {@see WP_Agent_Iteration_Budget} seeded at $current. * * Unregistered names return a permissive budget so callers using a * name-that-might-be-registered don't need conditional logic — the @@ -108,13 +108,13 @@ public static function registered_names(): array { * @param int|null $ceiling_override Optional caller-provided ceiling that * bypasses the setting/default lookup but * is still clamped to registered bounds. - * @return IterationBudget + * @return WP_Agent_Iteration_Budget */ public static function create( string $name, int $current = 0, ?int $ceiling_override = null - ): IterationBudget { + ): WP_Agent_Iteration_Budget { $config = self::$registered[ $name ] ?? array( 'default' => 10, 'min' => 1, @@ -132,6 +132,6 @@ public static function create( $ceiling = max( (int) $config['min'], min( (int) $config['max'], $ceiling ) ); - return new IterationBudget( $name, $ceiling, $current ); + return new WP_Agent_Iteration_Budget( $name, $ceiling, $current ); } } diff --git a/inc/Engine/AI/MemoryFileRegistry.php b/inc/Engine/AI/MemoryFileRegistry.php index be5311719..f6622bc20 100644 --- a/inc/Engine/AI/MemoryFileRegistry.php +++ b/inc/Engine/AI/MemoryFileRegistry.php @@ -571,18 +571,18 @@ private static function normalize_layer( string $layer ): string { private static function default_authority_tier( string $layer, string $filename ): string { if ( self::LAYER_SHARED === $layer || self::LAYER_NETWORK === $layer ) { - return \AgentsAPI\AI\Context\ContextAuthorityTier::WORKSPACE_SHARED; + return \AgentsAPI\AI\Context\WP_Agent_Context_Authority_Tier::WORKSPACE_SHARED; } if ( self::LAYER_USER === $layer ) { - return \AgentsAPI\AI\Context\ContextAuthorityTier::USER_GLOBAL; + return \AgentsAPI\AI\Context\WP_Agent_Context_Authority_Tier::USER_GLOBAL; } if ( 'SOUL.md' === $filename ) { - return \AgentsAPI\AI\Context\ContextAuthorityTier::AGENT_IDENTITY; + return \AgentsAPI\AI\Context\WP_Agent_Context_Authority_Tier::AGENT_IDENTITY; } - return \AgentsAPI\AI\Context\ContextAuthorityTier::AGENT_MEMORY; + return \AgentsAPI\AI\Context\WP_Agent_Context_Authority_Tier::AGENT_MEMORY; } private static function default_provenance( string $filename ): array { diff --git a/inc/Engine/AI/PipelineTranscriptPolicy.php b/inc/Engine/AI/PipelineTranscriptPolicy.php index f6739ee29..89dc9fd7b 100644 --- a/inc/Engine/AI/PipelineTranscriptPolicy.php +++ b/inc/Engine/AI/PipelineTranscriptPolicy.php @@ -25,7 +25,7 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\Consent\AgentConsentDecision; +use AgentsAPI\AI\Consent\WP_Agent_Consent_Decision; use DataMachine\Core\EngineData; defined( 'ABSPATH' ) || exit; @@ -59,9 +59,9 @@ public static function shouldPersist( EngineData $engine ): bool { * Resolve the Agents API consent decision for pipeline transcript storage. * * @param EngineData $engine Engine data snapshot for the running job. - * @return AgentConsentDecision Consent decision with audit metadata. + * @return WP_Agent_Consent_Decision Consent decision with audit metadata. */ - public static function decision( EngineData $engine ): AgentConsentDecision { + public static function decision( EngineData $engine ): WP_Agent_Consent_Decision { $flow_config = $engine->getFlowConfig(); $pipeline_config = $engine->getPipelineConfig(); $job_snapshot = $engine->get( 'job' ); diff --git a/inc/Engine/AI/PromptBuilder.php b/inc/Engine/AI/PromptBuilder.php index df8a8ba39..8db2821e3 100644 --- a/inc/Engine/AI/PromptBuilder.php +++ b/inc/Engine/AI/PromptBuilder.php @@ -11,7 +11,7 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; use DataMachine\Engine\AI\Directives\DirectiveInterface; use DataMachine\Engine\AI\Directives\DirectiveOutputValidator; use DataMachine\Engine\AI\Directives\DirectiveRenderer; @@ -57,7 +57,7 @@ class PromptBuilder { * @return self */ public function setMessages( array $messages ): self { - $this->messages = AgentMessageEnvelope::normalize_many( $messages ); + $this->messages = WP_Agent_Message::normalize_many( $messages ); return $this; } @@ -191,7 +191,7 @@ function ( $a, $b ) { $directive_messages = DirectiveRenderer::renderMessages( $validated_outputs ); return array( - 'messages' => AgentMessageEnvelope::normalize_many( array_merge( $directive_messages, $conversation_messages ) ), + 'messages' => WP_Agent_Message::normalize_many( array_merge( $directive_messages, $conversation_messages ) ), 'tools' => $this->tools, 'applied_directives' => $applied_directives, 'directive_metadata' => $directive_metadata, diff --git a/inc/Engine/AI/ProviderRequestAssembler.php b/inc/Engine/AI/ProviderRequestAssembler.php index 1589f17c5..49144acf9 100644 --- a/inc/Engine/AI/ProviderRequestAssembler.php +++ b/inc/Engine/AI/ProviderRequestAssembler.php @@ -10,7 +10,7 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; defined( 'ABSPATH' ) || exit; @@ -51,7 +51,7 @@ public function assemble( } $request = $prompt_builder->buildDetailed( $mode, $provider, $payload ); - $request['messages'] = AgentMessageEnvelope::normalize_many( $request['messages'] ?? array() ); + $request['messages'] = WP_Agent_Message::normalize_many( $request['messages'] ?? array() ); $applied_directives = $request['applied_directives'] ?? array(); $directive_metadata = $request['directive_metadata'] ?? array(); $directive_breakdown = $request['directive_breakdown'] ?? array(); @@ -75,7 +75,7 @@ public function assemble( */ public static function toProviderRequest( array $request ): array { $provider_request = $request; - $provider_request['messages'] = AgentMessageEnvelope::to_provider_messages( $request['messages'] ?? array() ); + $provider_request['messages'] = WP_Agent_Message::to_provider_messages( $request['messages'] ?? array() ); return $provider_request; } diff --git a/inc/Engine/AI/README.md b/inc/Engine/AI/README.md index fec76fbfd..67c69cd1b 100644 --- a/inc/Engine/AI/README.md +++ b/inc/Engine/AI/README.md @@ -17,7 +17,7 @@ Use this quick map before moving or renaming files: | Area | Boundary | |---|---| -| `AgentMessageEnvelope`, `AgentConversation*`, `LoopEventSinkInterface`, `RuntimeToolDeclaration` | Generic runtime candidates. | +| `WP_Agent_Message`, `AgentConversation*`, `LoopEventSinkInterface`, `WP_Agent_Tool_Declaration` | Generic runtime candidates. | | `BuiltInAgentConversationRunner`, `AIConversationLoop`, request builders, prompt/directive helpers | Implementation candidates, still carrying Data Machine compatibility seams. | | `System\*`, `System\Tasks\*`, `System\Tasks\Retention\*` | Data Machine product automation. Do not move into Agents API. | | `Actions\*` | Data Machine pending-action product surface. Do not move into Agents API. | diff --git a/inc/Engine/AI/System/Tasks/DailyMemoryTask.php b/inc/Engine/AI/System/Tasks/DailyMemoryTask.php index 90a3a2a3e..4bd42b4f0 100644 --- a/inc/Engine/AI/System/Tasks/DailyMemoryTask.php +++ b/inc/Engine/AI/System/Tasks/DailyMemoryTask.php @@ -27,9 +27,9 @@ use DataMachine\Core\FilesRepository\AgentMemory; use DataMachine\Core\FilesRepository\DailyMemory; use DataMachine\Engine\AI\RequestBuilder; -use AgentsAPI\AI\AgentConversationCompaction; -use AgentsAPI\AI\AgentMarkdownSectionCompactionAdapter; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Conversation_Compaction; +use AgentsAPI\AI\WP_Agent_Markdown_Section_Compaction_Adapter; +use AgentsAPI\AI\WP_Agent_Message; class DailyMemoryTask extends SystemTask { @@ -486,10 +486,10 @@ private function maybeHandleDeterministicOverflow( int $jobId, AgentMemory $memo * @return array{persistent: string, archived: string, persistent_blocks: int, archived_blocks: int} */ private static function planMemoryOverflowArchive( string $content, int $target_size, string $date ): array { - $items = AgentMarkdownSectionCompactionAdapter::parse( $content ); + $items = WP_Agent_Markdown_Section_Compaction_Adapter::parse( $content ); $section_count = 0; foreach ( $items as $item ) { - if ( AgentMarkdownSectionCompactionAdapter::TYPE_SECTION === ( $item['type'] ?? '' ) ) { + if ( WP_Agent_Markdown_Section_Compaction_Adapter::TYPE_SECTION === ( $item['type'] ?? '' ) ) { ++$section_count; } } @@ -511,7 +511,7 @@ private static function planMemoryOverflowArchive( string $content, int $target_ // Daily Memory overflow needs tail-section archival, so project sections in // reverse order and restore markdown order after Agents API chooses the cut. foreach ( array_reverse( $items ) as $item ) { - $messages[] = AgentMessageEnvelope::text( + $messages[] = WP_Agent_Message::text( 'system', (string) ( $item['content'] ?? '' ), array( @@ -520,7 +520,7 @@ private static function planMemoryOverflowArchive( string $content, int $target_ ); } - $result = AgentConversationCompaction::compact( + $result = WP_Agent_Conversation_Compaction::compact( $messages, array( 'overflow_archive_enabled' => true, @@ -536,7 +536,7 @@ static function (): string { ); $status = (string) ( $result['metadata']['compaction']['status'] ?? '' ); - if ( AgentConversationCompaction::STATUS_ARCHIVED !== $status || empty( $result['archive_items'] ) ) { + if ( WP_Agent_Conversation_Compaction::STATUS_ARCHIVED !== $status || empty( $result['archive_items'] ) ) { return array( 'persistent' => $content, 'archived' => '', @@ -549,8 +549,8 @@ static function (): string { $persistent_items = self::markdownItemsFromCompactionMessages( $result['messages'] ); return array( - 'persistent' => rtrim( AgentMarkdownSectionCompactionAdapter::reconstruct( $persistent_items ) . $pointer ) . "\n", - 'archived' => AgentMarkdownSectionCompactionAdapter::reconstruct( $archived_items ), + 'persistent' => rtrim( WP_Agent_Markdown_Section_Compaction_Adapter::reconstruct( $persistent_items ) . $pointer ) . "\n", + 'archived' => WP_Agent_Markdown_Section_Compaction_Adapter::reconstruct( $archived_items ), 'persistent_blocks' => count( $persistent_items ), 'archived_blocks' => count( $archived_items ), ); diff --git a/inc/Engine/AI/Tools/Policy/DataMachineAgentToolPolicyProvider.php b/inc/Engine/AI/Tools/Policy/DataMachineAgentToolPolicyProvider.php index 3f8a4acd4..d18d6141d 100644 --- a/inc/Engine/AI/Tools/Policy/DataMachineAgentToolPolicyProvider.php +++ b/inc/Engine/AI/Tools/Policy/DataMachineAgentToolPolicyProvider.php @@ -14,11 +14,7 @@ defined( 'ABSPATH' ) || exit; -if ( ! interface_exists( '\WP_Agent_Tool_Access_Policy_Interface' ) ) { - require_once dirname( __DIR__, 5 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy-interface.php'; -} - -final class DataMachineAgentToolPolicyProvider implements \WP_Agent_Tool_Access_Policy_Interface { +final class DataMachineAgentToolPolicyProvider implements \WP_Agent_Tool_Access_Policy { /** * Provide persisted Data Machine agent policy to Agents API. diff --git a/inc/Engine/AI/Tools/Policy/DataMachineMandatoryToolPolicy.php b/inc/Engine/AI/Tools/Policy/DataMachineMandatoryToolPolicy.php index 0e88a6947..fccd2c240 100644 --- a/inc/Engine/AI/Tools/Policy/DataMachineMandatoryToolPolicy.php +++ b/inc/Engine/AI/Tools/Policy/DataMachineMandatoryToolPolicy.php @@ -13,11 +13,7 @@ defined( 'ABSPATH' ) || exit; -if ( ! interface_exists( '\WP_Agent_Tool_Access_Policy_Interface' ) ) { - require_once dirname( __DIR__, 5 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy-interface.php'; -} - -final class DataMachineMandatoryToolPolicy implements \WP_Agent_Tool_Access_Policy_Interface { +final class DataMachineMandatoryToolPolicy implements \WP_Agent_Tool_Access_Policy { /** * Provide mandatory Data Machine plumbing tools to Agents API. diff --git a/inc/Engine/AI/Tools/ToolExecutor.php b/inc/Engine/AI/Tools/ToolExecutor.php index 9a126ca16..773718761 100644 --- a/inc/Engine/AI/Tools/ToolExecutor.php +++ b/inc/Engine/AI/Tools/ToolExecutor.php @@ -11,7 +11,7 @@ namespace DataMachine\Engine\AI\Tools; -use AgentsAPI\AI\Tools\ActionPolicy; +use AgentsAPI\AI\Tools\WP_Agent_Action_Policy; use DataMachine\Core\WordPress\PostTracking; use DataMachine\Engine\AI\Actions\ActionPolicyResolver; use DataMachine\Engine\AI\Actions\PendingActionHelper; @@ -30,7 +30,7 @@ class ToolExecutor { * decide whether the invocation should execute directly, be staged for * user approval (preview), or be refused (forbidden). Tools opt into * preview/forbidden via metadata; unopted tools resolve to 'direct' and - * behave identically to pre-ActionPolicy releases. + * behave identically to pre-WP_Agent_Action_Policy releases. * * The `$mode` / `$agent_id` / `$client_context` parameters were added in * 0.72.0 so the resolver has enough context to apply per-agent and @@ -81,7 +81,7 @@ public static function executeTool( ) ); - if ( ActionPolicy::refusesExecution( $policy ) ) { + if ( WP_Agent_Action_Policy::refusesExecution( $policy ) ) { return array( 'success' => false, 'error' => sprintf( 'Tool "%s" is not permitted in the current context (action_policy=forbidden).', $tool_name ), @@ -90,7 +90,7 @@ public static function executeTool( ); } - if ( ActionPolicy::stagesApproval( $policy ) ) { + if ( WP_Agent_Action_Policy::stagesApproval( $policy ) ) { // Tool must declare how to build an action kind and summary. // If it hasn't opted into the preview pipeline properly, fall // back to 'direct' and log a warning — preview is a contract, @@ -99,7 +99,7 @@ public static function executeTool( do_action( 'datamachine_log', 'warning', - 'ActionPolicy: tool resolved to preview but is missing action_kind metadata; falling back to direct.', + 'WP_Agent_Action_Policy: tool resolved to preview but is missing action_kind metadata; falling back to direct.', array( 'tool_name' => $tool_name, 'mode' => $mode, diff --git a/inc/Engine/AI/Tools/ToolPolicyResolver.php b/inc/Engine/AI/Tools/ToolPolicyResolver.php index f47320103..ad5b8fc83 100644 --- a/inc/Engine/AI/Tools/ToolPolicyResolver.php +++ b/inc/Engine/AI/Tools/ToolPolicyResolver.php @@ -27,12 +27,6 @@ defined( 'ABSPATH' ) || exit; -if ( ! class_exists( '\WP_Agent_Tool_Policy' ) ) { - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy-interface.php'; - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy-filter.php'; - require_once dirname( __DIR__, 4 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy.php'; -} - class ToolPolicyResolver { /** diff --git a/inc/Engine/AI/conversation-loop.php b/inc/Engine/AI/conversation-loop.php index 400fc1327..4aa98eec1 100644 --- a/inc/Engine/AI/conversation-loop.php +++ b/inc/Engine/AI/conversation-loop.php @@ -3,7 +3,7 @@ * Data Machine conversation loop — direct substrate consumer. * * Builds a DM-specific turn runner and passes it to the upstream - * AgentConversationLoop::run() with DM completion policy, transcript + * WP_Agent_Conversation_Loop::run() with DM completion policy, transcript * persistence, and event emission wired as options. * * This file replaces the former 748-LOC AIConversationLoop class. Both @@ -14,12 +14,12 @@ namespace DataMachine\Engine\AI; -use AgentsAPI\AI\AgentConversationCompletionPolicyInterface; -use AgentsAPI\AI\AgentConversationLoop; -use AgentsAPI\AI\AgentConversationResult; -use AgentsAPI\AI\AgentConversationTranscriptPersisterInterface; -use AgentsAPI\AI\AgentMessageEnvelope; -use AgentsAPI\AI\NullAgentConversationTranscriptPersister; +use AgentsAPI\AI\WP_Agent_Conversation_Completion_Policy; +use AgentsAPI\AI\WP_Agent_Conversation_Loop; +use AgentsAPI\AI\WP_Agent_Conversation_Result; +use AgentsAPI\AI\WP_Agent_Transcript_Persister; +use AgentsAPI\AI\WP_Agent_Message; +use AgentsAPI\AI\WP_Agent_Null_Transcript_Persister; use DataMachine\Core\PluginSettings; use DataMachine\Core\Workspace\WordPressWorkspaceScope; use DataMachine\Engine\AI\Tools\ToolExecutor; @@ -30,7 +30,7 @@ * Run a multi-turn AI conversation through the agents-api substrate. * * Builds a DM-specific turn runner (request building + wp-ai-client dispatch + - * tool execution) and delegates orchestration to AgentConversationLoop::run(). + * tool execution) and delegates orchestration to WP_Agent_Conversation_Loop::run(). * * @param array $messages Initial conversation messages. * @param array $tools Available tools keyed by tool name. @@ -52,7 +52,7 @@ function datamachine_run_conversation( int $max_turns = PluginSettings::DEFAULT_MAX_TURNS, bool $single_turn = false ): array { - $messages = AgentMessageEnvelope::normalize_many( $messages ); + $messages = WP_Agent_Message::normalize_many( $messages ); // Resolve DM runtime collaborators from the payload. $event_sink = datamachine_resolve_event_sink( $payload ); @@ -131,14 +131,14 @@ function datamachine_run_conversation( // Run through the upstream substrate loop. try { - $result = AgentConversationLoop::run( + $result = WP_Agent_Conversation_Loop::run( $messages, $turn_runner, array( 'max_turns' => $max_turns, 'budgets' => array( $turn_budget ), 'context' => array_merge( $loop_payload, array( 'mode' => $mode ) ), - 'request' => new \AgentsAPI\AI\AgentConversationRequest( + 'request' => new \AgentsAPI\AI\WP_Agent_Conversation_Request( $messages, array(), null, @@ -178,7 +178,7 @@ function datamachine_run_conversation( // Normalize the substrate result and augment with DM-specific fields. try { - $result = AgentConversationResult::normalize( $result ); + $result = WP_Agent_Conversation_Result::normalize( $result ); } catch ( \InvalidArgumentException $e ) { return array( 'messages' => $messages, @@ -228,7 +228,7 @@ function datamachine_run_conversation( * @param array $loop_payload Cleaned payload. * @param LoopEventSinkInterface $event_sink DM event sink. * @param array $base_log_context Base log context. - * @param AgentConversationCompletionPolicyInterface $completion_policy Completion policy. + * @param WP_Agent_Conversation_Completion_Policy $completion_policy Completion policy. * @param array &$last_request_metadata Mutable request metadata. * @param array &$last_tool_calls Mutable last tool calls. * @param string &$final_content Mutable final assistant content. @@ -244,7 +244,7 @@ function datamachine_build_turn_runner( array $loop_payload, LoopEventSinkInterface $event_sink, array $base_log_context, - AgentConversationCompletionPolicyInterface $completion_policy, + WP_Agent_Conversation_Completion_Policy $completion_policy, array &$last_request_metadata, array &$last_tool_calls, string &$final_content, @@ -548,11 +548,11 @@ function datamachine_resolve_event_sink( array $payload ): LoopEventSinkInterfac * * @param string $mode Execution mode. * @param array $payload Loop payload. - * @return AgentConversationCompletionPolicyInterface + * @return WP_Agent_Conversation_Completion_Policy */ -function datamachine_resolve_completion_policy( string $mode, array $payload ): AgentConversationCompletionPolicyInterface { +function datamachine_resolve_completion_policy( string $mode, array $payload ): WP_Agent_Conversation_Completion_Policy { $policy = $payload['completion_policy'] ?? null; - if ( $policy instanceof AgentConversationCompletionPolicyInterface ) { + if ( $policy instanceof WP_Agent_Conversation_Completion_Policy ) { return $policy; } @@ -570,11 +570,11 @@ function datamachine_resolve_completion_policy( string $mode, array $payload ): * Resolve the runtime transcript persister from the payload. * * @param array $payload Loop payload. - * @return AgentConversationTranscriptPersisterInterface + * @return WP_Agent_Transcript_Persister */ -function datamachine_resolve_transcript_persister( array $payload ): AgentConversationTranscriptPersisterInterface { +function datamachine_resolve_transcript_persister( array $payload ): WP_Agent_Transcript_Persister { $persister = $payload['transcript_persister'] ?? null; - if ( $persister instanceof AgentConversationTranscriptPersisterInterface ) { + if ( $persister instanceof WP_Agent_Transcript_Persister ) { return $persister; } @@ -582,7 +582,7 @@ function datamachine_resolve_transcript_persister( array $payload ): AgentConver return new DataMachinePipelineTranscriptPersister(); } - return new NullAgentConversationTranscriptPersister(); + return new WP_Agent_Null_Transcript_Persister(); } /** diff --git a/inc/bootstrap.php b/inc/bootstrap.php index 1b89b0196..f0d5f6a09 100644 --- a/inc/bootstrap.php +++ b/inc/bootstrap.php @@ -78,7 +78,7 @@ |-------------------------------------------------------------------------- | Named bounded-iteration budgets shared across the engine. Each budget | declares its ceiling-resolution rules (default, site-setting key, -| clamp bounds). Consumers instantiate a fresh IterationBudget per run +| clamp bounds). Consumers instantiate a fresh WP_Agent_Iteration_Budget per run | via IterationBudgetRegistry::create(). | | Registration is side-effect free (static map mutation) and safe to diff --git a/tests/Unit/AI/IterationBudgetTest.php b/tests/Unit/AI/IterationBudgetTest.php index 1f0b7290d..f13e94ca1 100644 --- a/tests/Unit/AI/IterationBudgetTest.php +++ b/tests/Unit/AI/IterationBudgetTest.php @@ -1,13 +1,13 @@ assertSame( 0, $budget->current() ); $this->assertSame( 5, $budget->ceiling() ); $this->assertSame( 5, $budget->remaining() ); @@ -32,7 +32,7 @@ public function test_budget_starts_below_ceiling(): void { } public function test_increment_advances_counter(): void { - $budget = new IterationBudget( 'x', 3 ); + $budget = new WP_Agent_Iteration_Budget( 'x', 3 ); $budget->increment(); $this->assertSame( 1, $budget->current() ); $this->assertSame( 2, $budget->remaining() ); @@ -40,7 +40,7 @@ public function test_increment_advances_counter(): void { } public function test_exceeded_at_ceiling(): void { - $budget = new IterationBudget( 'x', 2 ); + $budget = new WP_Agent_Iteration_Budget( 'x', 2 ); $budget->increment(); $budget->increment(); $this->assertSame( 2, $budget->current() ); @@ -49,7 +49,7 @@ public function test_exceeded_at_ceiling(): void { } public function test_exceeded_above_ceiling(): void { - $budget = new IterationBudget( 'x', 2 ); + $budget = new WP_Agent_Iteration_Budget( 'x', 2 ); $budget->increment(); $budget->increment(); $budget->increment(); @@ -59,17 +59,17 @@ public function test_exceeded_above_ceiling(): void { } public function test_ceiling_minimum_enforced(): void { - $budget = new IterationBudget( 'x', 0 ); + $budget = new WP_Agent_Iteration_Budget( 'x', 0 ); $this->assertSame( 1, $budget->ceiling(), 'Ceiling floor of 1 enforced' ); } public function test_negative_starting_current_clamped_to_zero(): void { - $budget = new IterationBudget( 'x', 5, -3 ); + $budget = new WP_Agent_Iteration_Budget( 'x', 5, -3 ); $this->assertSame( 0, $budget->current() ); } public function test_already_exceeded_start_preserved(): void { - $budget = new IterationBudget( 'x', 3, 7 ); + $budget = new WP_Agent_Iteration_Budget( 'x', 3, 7 ); $this->assertSame( 7, $budget->current() ); $this->assertTrue( $budget->exceeded() ); } diff --git a/tests/Unit/Abilities/ImageGenerationPromptRefinementTest.php b/tests/Unit/Abilities/ImageGenerationPromptRefinementTest.php index b5ca6c8ad..3a332d491 100644 --- a/tests/Unit/Abilities/ImageGenerationPromptRefinementTest.php +++ b/tests/Unit/Abilities/ImageGenerationPromptRefinementTest.php @@ -151,11 +151,12 @@ public function test_refine_prompt_includes_post_context_when_provided(): void { ImageGenerationAbilities::refine_prompt( 'Crane meaning', 'This article explores the spiritual symbolism of cranes in various cultures.' ); $this->assertNotNull( $captured_request ); - // Directives prepend messages, so find the last user message (our refinement request). - $user_message = ''; + // wp-ai-client receives the current user turn as `prompt`; older dispatch + // shapes may still carry it as the last user message. + $user_message = $captured_request['prompt'] ?? ''; foreach ( array_reverse( $captured_request['messages'] ) as $msg ) { if ( ( $msg['role'] ?? '' ) === 'user' ) { - $user_message = $msg['content'] ?? ''; + $user_message = $user_message ?: ( $msg['content'] ?? '' ); break; } } diff --git a/tests/Unit/Abilities/PermissionHelperTest.php b/tests/Unit/Abilities/PermissionHelperTest.php index 5773d59d5..0ed24a20f 100644 --- a/tests/Unit/Abilities/PermissionHelperTest.php +++ b/tests/Unit/Abilities/PermissionHelperTest.php @@ -197,8 +197,8 @@ public function test_agent_token_context_uses_agents_api_capability_ceiling(): v $principal = PermissionHelper::get_execution_principal(); - $this->assertInstanceOf( \AgentsAPI\AI\AgentExecutionPrincipal::class, $principal ); - $this->assertSame( \AgentsAPI\AI\AgentExecutionPrincipal::AUTH_SOURCE_AGENT_TOKEN, $principal->auth_source ); + $this->assertInstanceOf( \AgentsAPI\AI\WP_Agent_Execution_Principal::class, $principal ); + $this->assertSame( \AgentsAPI\AI\WP_Agent_Execution_Principal::AUTH_SOURCE_AGENT_TOKEN, $principal->auth_source ); $this->assertTrue( PermissionHelper::can( 'chat' ) ); $this->assertFalse( PermissionHelper::can( 'manage_agents' ) ); } @@ -210,8 +210,8 @@ public function test_user_session_agent_context_uses_owner_ceiling_without_token $principal = PermissionHelper::get_execution_principal(); - $this->assertInstanceOf( \AgentsAPI\AI\AgentExecutionPrincipal::class, $principal ); - $this->assertSame( \AgentsAPI\AI\AgentExecutionPrincipal::AUTH_SOURCE_USER, $principal->auth_source ); + $this->assertInstanceOf( \AgentsAPI\AI\WP_Agent_Execution_Principal::class, $principal ); + $this->assertSame( \AgentsAPI\AI\WP_Agent_Execution_Principal::AUTH_SOURCE_USER, $principal->auth_source ); $this->assertNull( $principal->token_id ); $this->assertTrue( PermissionHelper::can( 'manage_agents' ) ); } diff --git a/tests/Unit/Core/Database/Chat/ConversationStoreFactoryTest.php b/tests/Unit/Core/Database/Chat/ConversationStoreFactoryTest.php index c6a5d209d..68d377e71 100644 --- a/tests/Unit/Core/Database/Chat/ConversationStoreFactoryTest.php +++ b/tests/Unit/Core/Database/Chat/ConversationStoreFactoryTest.php @@ -22,9 +22,9 @@ use DataMachine\Core\Database\Chat\ConversationSessionIndexInterface; use DataMachine\Core\Database\Chat\ConversationStoreFactory; use DataMachine\Core\Database\Chat\ConversationStoreInterface; -use AgentsAPI\Core\Database\Chat\ConversationTranscriptLockInterface; -use AgentsAPI\Core\Database\Chat\ConversationTranscriptStoreInterface; -use AgentsAPI\Core\Workspace\AgentWorkspaceScope; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Lock; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Store; +use AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope; use WP_UnitTestCase; class ConversationStoreFactoryTest extends WP_UnitTestCase { @@ -48,8 +48,8 @@ public function test_default_resolution_returns_builtin_chat_store(): void { $store = ConversationStoreFactory::get(); $this->assertInstanceOf( ConversationStoreInterface::class, $store ); - $this->assertInstanceOf( ConversationTranscriptStoreInterface::class, $store ); - $this->assertInstanceOf( ConversationTranscriptLockInterface::class, $store ); + $this->assertInstanceOf( WP_Agent_Conversation_Store::class, $store ); + $this->assertInstanceOf( WP_Agent_Conversation_Lock::class, $store ); $this->assertInstanceOf( ConversationSessionIndexInterface::class, $store ); $this->assertInstanceOf( ConversationReadStateInterface::class, $store ); $this->assertInstanceOf( ConversationRetentionInterface::class, $store ); @@ -60,7 +60,7 @@ public function test_default_resolution_returns_builtin_chat_store(): void { public function test_transcript_resolution_returns_narrow_contract(): void { $store = ConversationStoreFactory::get_transcript_store(); - $this->assertInstanceOf( ConversationTranscriptStoreInterface::class, $store ); + $this->assertInstanceOf( WP_Agent_Conversation_Store::class, $store ); $this->assertInstanceOf( ConversationStoreInterface::class, ConversationStoreFactory::get() ); $this->assertSame( ConversationStoreFactory::get(), $store ); } @@ -110,8 +110,8 @@ public function test_builtin_chat_store_honors_transcript_lock_contract(): void public function test_conversation_store_interface_is_composed_from_narrow_contracts(): void { $reflection = new \ReflectionClass( ConversationStoreInterface::class ); $expected = array( - ConversationTranscriptLockInterface::class, - ConversationTranscriptStoreInterface::class, + WP_Agent_Conversation_Lock::class, + WP_Agent_Conversation_Store::class, ConversationSessionIndexInterface::class, ConversationReadStateInterface::class, ConversationRetentionInterface::class, @@ -138,8 +138,8 @@ public function test_filter_swaps_the_store(): void { $resolved = ConversationStoreFactory::get(); $this->assertSame( $memory_store, $resolved ); - $this->assertInstanceOf( ConversationTranscriptStoreInterface::class, $resolved ); - $this->assertInstanceOf( ConversationTranscriptLockInterface::class, $resolved ); + $this->assertInstanceOf( WP_Agent_Conversation_Store::class, $resolved ); + $this->assertInstanceOf( WP_Agent_Conversation_Lock::class, $resolved ); $this->assertInstanceOf( ConversationSessionIndexInterface::class, $resolved ); $this->assertInstanceOf( ConversationReadStateInterface::class, $resolved ); $this->assertInstanceOf( ConversationRetentionInterface::class, $resolved ); @@ -160,8 +160,8 @@ public function test_transcript_resolution_uses_conversation_store_filter(): voi $resolved = ConversationStoreFactory::get_transcript_store(); $this->assertSame( $memory_store, $resolved ); - $this->assertInstanceOf( ConversationTranscriptStoreInterface::class, $resolved ); - $this->assertInstanceOf( ConversationTranscriptLockInterface::class, $resolved ); + $this->assertInstanceOf( WP_Agent_Conversation_Store::class, $resolved ); + $this->assertInstanceOf( WP_Agent_Conversation_Lock::class, $resolved ); } public function test_misbehaving_filter_falls_back_to_default(): void { @@ -299,10 +299,10 @@ public function test_transcript_only_contract_can_persist_messages(): void { /** * Persist a transcript through only the narrow runtime contract. * - * @param ConversationTranscriptStoreInterface $store Transcript store. + * @param WP_Agent_Conversation_Store $store Transcript store. * @return array */ - private function persist_fixture_transcript( ConversationTranscriptStoreInterface $store ): array { + private function persist_fixture_transcript( WP_Agent_Conversation_Store $store ): array { $session_id = $store->create_session( $this->workspace(), 5, @@ -411,7 +411,7 @@ public function test_list_chat_sessions_ability_routes_through_swapped_store(): $this->assertSame( 'hello', $result['sessions'][0]['first_message'] ); } - private function workspace(): AgentWorkspaceScope { - return AgentWorkspaceScope::from_parts( 'site', 'https://example.test' ); + private function workspace(): WP_Agent_Workspace_Scope { + return WP_Agent_Workspace_Scope::from_parts( 'site', 'https://example.test' ); } } diff --git a/tests/Unit/Core/Database/Chat/InMemoryConversationStore.php b/tests/Unit/Core/Database/Chat/InMemoryConversationStore.php index 2c3af2913..8ad0b8abf 100644 --- a/tests/Unit/Core/Database/Chat/InMemoryConversationStore.php +++ b/tests/Unit/Core/Database/Chat/InMemoryConversationStore.php @@ -13,8 +13,8 @@ namespace DataMachine\Tests\Unit\Core\Database\Chat; use DataMachine\Core\Database\Chat\ConversationStoreInterface; -use AgentsAPI\AI\AgentMessageEnvelope; -use AgentsAPI\Core\Workspace\AgentWorkspaceScope; +use AgentsAPI\AI\WP_Agent_Message; +use AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope; class InMemoryConversationStore implements ConversationStoreInterface { @@ -68,10 +68,10 @@ public function create_session( ...$args ): string { /** * @param array $args Raw create-session arguments. - * @return array{0:AgentWorkspaceScope,1:int,2:int,3:array,4:string} + * @return array{0:WP_Agent_Workspace_Scope,1:int,2:int,3:array,4:string} */ private function normalize_create_session_args( array $args ): array { - if ( isset( $args[0] ) && $args[0] instanceof AgentWorkspaceScope ) { + if ( isset( $args[0] ) && $args[0] instanceof WP_Agent_Workspace_Scope ) { return array( $args[0], (int) ( $args[1] ?? 0 ), @@ -82,7 +82,7 @@ private function normalize_create_session_args( array $args ): array { } return array( - AgentWorkspaceScope::from_parts( 'site', '1' ), + WP_Agent_Workspace_Scope::from_parts( 'site', '1' ), (int) ( $args[0] ?? 0 ), (int) ( $args[1] ?? 0 ), is_array( $args[2] ?? null ) ? $args[2] : array(), @@ -92,10 +92,10 @@ private function normalize_create_session_args( array $args ): array { /** * @param array $args Raw pending-session arguments. - * @return array{0:AgentWorkspaceScope,1:int,2:int,3:string,4:int|null} + * @return array{0:WP_Agent_Workspace_Scope,1:int,2:int,3:string,4:int|null} */ private function normalize_recent_pending_session_args( array $args ): array { - if ( isset( $args[0] ) && $args[0] instanceof AgentWorkspaceScope ) { + if ( isset( $args[0] ) && $args[0] instanceof WP_Agent_Workspace_Scope ) { return array( $args[0], (int) ( $args[1] ?? 0 ), @@ -106,7 +106,7 @@ private function normalize_recent_pending_session_args( array $args ): array { } return array( - AgentWorkspaceScope::from_parts( 'site', '1' ), + WP_Agent_Workspace_Scope::from_parts( 'site', '1' ), (int) ( $args[0] ?? 0 ), (int) ( $args[1] ?? 600 ), (string) ( $args[2] ?? 'chat' ), @@ -118,7 +118,7 @@ public function get_session( string $session_id ): ?array { return $this->sessions[ $session_id ] ?? null; } - public function update_session( string $session_id, array $messages, array $metadata = array(), string $provider = '', string $model = '' ): bool { + public function update_session( string $session_id, array $messages, array $metadata = array(), string $provider = '', string $model = '', ?string $provider_response_id = null ): bool { if ( ! isset( $this->sessions[ $session_id ] ) ) { return false; } @@ -133,6 +133,9 @@ public function update_session( string $session_id, array $messages, array $meta if ( '' !== $model ) { $this->sessions[ $session_id ]['model'] = $model; } + if ( null !== $provider_response_id ) { + $this->sessions[ $session_id ]['provider_response_id'] = $provider_response_id; + } return true; } @@ -273,12 +276,12 @@ public function update_title( string $session_id, string $title ): bool { public function count_unread( array $messages, ?string $last_read_at ): int { $count = 0; foreach ( $messages as $msg ) { - $msg = AgentMessageEnvelope::normalize( $msg ); + $msg = WP_Agent_Message::normalize( $msg ); if ( ( $msg['role'] ?? '' ) !== 'assistant' ) { continue; } - $type = $msg['type'] ?? AgentMessageEnvelope::TYPE_TEXT; - if ( AgentMessageEnvelope::TYPE_TOOL_CALL === $type || AgentMessageEnvelope::TYPE_TOOL_RESULT === $type ) { + $type = $msg['type'] ?? WP_Agent_Message::TYPE_TEXT; + if ( WP_Agent_Message::TYPE_TOOL_CALL === $type || WP_Agent_Message::TYPE_TOOL_RESULT === $type ) { continue; } if ( null === $last_read_at ) { diff --git a/tests/Unit/Engine/AI/Actions/ActionPolicyResolverTest.php b/tests/Unit/Engine/AI/Actions/ActionPolicyResolverTest.php index 657e4b272..0b2d74dbc 100644 --- a/tests/Unit/Engine/AI/Actions/ActionPolicyResolverTest.php +++ b/tests/Unit/Engine/AI/Actions/ActionPolicyResolverTest.php @@ -16,13 +16,13 @@ namespace DataMachine\Tests\Unit\Engine\AI\Actions; -use AgentsAPI\AI\Tools\ActionPolicy; +use AgentsAPI\AI\Tools\WP_Agent_Action_Policy; use DataMachine\Core\Database\Agents\Agents; use DataMachine\Engine\AI\Actions\ActionPolicyResolver; use WP_UnitTestCase; -if ( ! class_exists( ActionPolicy::class ) ) { - require_once dirname( __DIR__, 5 ) . '/vendor/automattic/agents-api/src/Tools/ActionPolicy.php'; +if ( ! class_exists( WP_Agent_Action_Policy::class ) ) { + require_once dirname( __DIR__, 5 ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-action-policy.php'; } class ActionPolicyResolverTest extends WP_UnitTestCase { @@ -54,9 +54,9 @@ public function test_default_policy_is_direct(): void { } public function test_legacy_policy_constants_alias_agents_api_vocabulary(): void { - $this->assertSame( ActionPolicy::DIRECT, ActionPolicyResolver::POLICY_DIRECT ); - $this->assertSame( ActionPolicy::PREVIEW, ActionPolicyResolver::POLICY_PREVIEW ); - $this->assertSame( ActionPolicy::FORBIDDEN, ActionPolicyResolver::POLICY_FORBIDDEN ); + $this->assertSame( WP_Agent_Action_Policy::DIRECT, ActionPolicyResolver::POLICY_DIRECT ); + $this->assertSame( WP_Agent_Action_Policy::PREVIEW, ActionPolicyResolver::POLICY_PREVIEW ); + $this->assertSame( WP_Agent_Action_Policy::FORBIDDEN, ActionPolicyResolver::POLICY_FORBIDDEN ); } public function test_tool_declared_default_overrides_global_default(): void { diff --git a/tests/adjacent-handler-tool-policy-smoke.php b/tests/adjacent-handler-tool-policy-smoke.php index d1cc1f995..df2e6a360 100644 --- a/tests/adjacent-handler-tool-policy-smoke.php +++ b/tests/adjacent-handler-tool-policy-smoke.php @@ -36,7 +36,7 @@ function apply_filters( string $hook, $value ) { } require_once __DIR__ . '/../inc/Core/Steps/FlowStepConfig.php'; - require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy-interface.php'; + require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy.php'; require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy-filter.php'; require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy.php'; require_once __DIR__ . '/../inc/Engine/AI/Tools/Policy/DataMachineAgentToolPolicyProvider.php'; diff --git a/tests/agent-conversation-result-smoke.php b/tests/agent-conversation-result-smoke.php index 44766cb45..cc1e63fbc 100644 --- a/tests/agent-conversation-result-smoke.php +++ b/tests/agent-conversation-result-smoke.php @@ -7,8 +7,8 @@ require_once __DIR__ . '/bootstrap-unit.php'; -use AgentsAPI\AI\AgentConversationResult; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Conversation_Result; +use AgentsAPI\AI\WP_Agent_Message; function datamachine_agent_conversation_result_assert( bool $condition, string $message ): void { if ( ! $condition ) { @@ -38,9 +38,9 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ 'usage' => array( 'total_tokens' => 10 ), ); -$normalized = AgentConversationResult::normalize( $valid_result ); +$normalized = WP_Agent_Conversation_Result::normalize( $valid_result ); datamachine_agent_conversation_result_assert( - AgentMessageEnvelope::TYPE_TEXT === $normalized['messages'][0]['type'], + WP_Agent_Message::TYPE_TEXT === $normalized['messages'][0]['type'], 'Valid built-in-shaped result should normalize messages to canonical envelopes.' ); ++$assertions; @@ -54,7 +54,7 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ unset( $generic_tool_result['tool_execution_results'][0]['is_handler_tool'] ); $generic_tool_result['tool_execution_results'][0]['tool_name'] = 'search_knowledge_base'; $generic_tool_result['tool_execution_results'][0]['result'] = array( 'success' => true, 'items' => array() ); -$normalized_generic_tool_result = AgentConversationResult::normalize( $generic_tool_result ); +$normalized_generic_tool_result = WP_Agent_Conversation_Result::normalize( $generic_tool_result ); datamachine_agent_conversation_result_assert( ! array_key_exists( 'is_handler_tool', $normalized_generic_tool_result['tool_execution_results'][0] ), 'Generic tool execution result should not require Data Machine handler metadata.' @@ -65,7 +65,7 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ $malformed_handler_metadata['tool_execution_results'][0]['is_handler_tool'] = 'yes'; try { - AgentConversationResult::normalize( $malformed_handler_metadata ); + WP_Agent_Conversation_Result::normalize( $malformed_handler_metadata ); throw new RuntimeException( 'Malformed handler metadata should throw.' ); } catch ( InvalidArgumentException $e ) { datamachine_agent_conversation_result_assert( @@ -77,7 +77,7 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ $without_tool_results = $valid_result; unset( $without_tool_results['tool_execution_results'] ); -$normalized_without_tools = AgentConversationResult::normalize( $without_tool_results ); +$normalized_without_tools = WP_Agent_Conversation_Result::normalize( $without_tool_results ); datamachine_agent_conversation_result_assert( array_key_exists( 'tool_execution_results', $normalized_without_tools ), 'Missing tool_execution_results should normalize to an explicit empty list.' @@ -93,7 +93,7 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ unset( $malformed_tool_result['tool_execution_results'][0]['parameters'] ); try { - AgentConversationResult::normalize( $malformed_tool_result ); + WP_Agent_Conversation_Result::normalize( $malformed_tool_result ); throw new RuntimeException( 'Malformed tool result should throw.' ); } catch ( InvalidArgumentException $e ) { datamachine_agent_conversation_result_assert( @@ -107,7 +107,7 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ unset( $missing_messages['messages'] ); try { - AgentConversationResult::normalize( $missing_messages ); + WP_Agent_Conversation_Result::normalize( $missing_messages ); throw new RuntimeException( 'Missing messages should throw.' ); } catch ( InvalidArgumentException $e ) { datamachine_agent_conversation_result_assert( @@ -121,7 +121,7 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ $malformed_message['messages'][0] = 'not a message array'; try { - AgentConversationResult::normalize( $malformed_message ); + WP_Agent_Conversation_Result::normalize( $malformed_message ); throw new RuntimeException( 'Malformed message should throw.' ); } catch ( InvalidArgumentException $e ) { datamachine_agent_conversation_result_assert( @@ -133,7 +133,7 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ // Verify that malformed tool results produce a machine-readable validation error. try { - AgentConversationResult::normalize( $malformed_tool_result ); + WP_Agent_Conversation_Result::normalize( $malformed_tool_result ); throw new RuntimeException( 'Malformed tool result should throw.' ); } catch ( InvalidArgumentException $e ) { datamachine_agent_conversation_result_assert( @@ -144,7 +144,7 @@ function datamachine_agent_conversation_result_assert( bool $condition, string $ } // Verify that valid results normalize without error. -$runner_valid_result = AgentConversationResult::normalize( $valid_result ); +$runner_valid_result = WP_Agent_Conversation_Result::normalize( $valid_result ); datamachine_agent_conversation_result_assert( ! isset( $runner_valid_result['error'] ), diff --git a/tests/agent-conversation-runner-request-smoke.php b/tests/agent-conversation-runner-request-smoke.php index 59d3e2f8e..4fae10d15 100644 --- a/tests/agent-conversation-runner-request-smoke.php +++ b/tests/agent-conversation-runner-request-smoke.php @@ -3,7 +3,7 @@ * Smoke test for datamachine_run_conversation() substrate integration. * * Verifies that DM's conversation entry point correctly delegates to the - * upstream AgentConversationLoop::run() and returns a normalized result. + * upstream WP_Agent_Conversation_Loop::run() and returns a normalized result. * * Run with: php tests/agent-conversation-runner-request-smoke.php * @@ -56,7 +56,7 @@ function get_option( string $name, $default = false ) { use DataMachine\Engine\AI\LoopEventSinkInterface; use DataMachine\Tests\Unit\Support\WpAiClientTestDouble; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; use function DataMachine\Engine\AI\datamachine_run_conversation; diff --git a/tests/agent-conversation-runtime-policy-smoke.php b/tests/agent-conversation-runtime-policy-smoke.php index 0f02aa83d..628d85650 100644 --- a/tests/agent-conversation-runtime-policy-smoke.php +++ b/tests/agent-conversation-runtime-policy-smoke.php @@ -62,33 +62,33 @@ function get_option( string $_option, $default = false ) { require_once __DIR__ . '/bootstrap-unit.php'; require_once __DIR__ . '/Unit/Support/WpAiClientTestDoubles.php'; -use AgentsAPI\AI\AgentConversationCompletionDecision; -use AgentsAPI\AI\AgentConversationCompletionPolicyInterface; -use AgentsAPI\AI\AgentConversationRequest; -use AgentsAPI\AI\AgentConversationTranscriptPersisterInterface; +use AgentsAPI\AI\WP_Agent_Conversation_Completion_Decision; +use AgentsAPI\AI\WP_Agent_Conversation_Completion_Policy; +use AgentsAPI\AI\WP_Agent_Conversation_Request; +use AgentsAPI\AI\WP_Agent_Transcript_Persister; use DataMachine\Engine\AI\DataMachineHandlerCompletionPolicy; use DataMachine\Tests\Unit\Support\WpAiClientTestDouble; use function DataMachine\Engine\AI\datamachine_run_conversation; -class RuntimePolicySmokeCompletionPolicy implements AgentConversationCompletionPolicyInterface { +class RuntimePolicySmokeCompletionPolicy implements WP_Agent_Conversation_Completion_Policy { public array $calls = array(); - public function recordToolResult( string $tool_name, ?array $tool_def, array $tool_result, array $runtime_context, int $turn_count ): AgentConversationCompletionDecision { + public function recordToolResult( string $tool_name, ?array $tool_def, array $tool_result, array $runtime_context, int $turn_count ): WP_Agent_Conversation_Completion_Decision { $mode = (string) ( $runtime_context['mode'] ?? '' ); $this->calls[] = compact( 'tool_name', 'tool_def', 'tool_result', 'mode', 'turn_count' ); - return AgentConversationCompletionDecision::complete( + return WP_Agent_Conversation_Completion_Decision::complete( 'RuntimePolicySmoke: custom policy completed', array( 'tool_name' => $tool_name ) ); } } -class RuntimePolicySmokeTranscriptPersister implements AgentConversationTranscriptPersisterInterface { +class RuntimePolicySmokeTranscriptPersister implements WP_Agent_Transcript_Persister { public array $calls = array(); - public function persist( array $messages, AgentConversationRequest $request, array $result ): string { + public function persist( array $messages, WP_Agent_Conversation_Request $request, array $result ): string { $metadata = $request->metadata(); $provider = (string) ( $metadata['provider'] ?? '' ); $model = (string) ( $metadata['model'] ?? '' ); diff --git a/tests/agent-memory-events-smoke.php b/tests/agent-memory-events-smoke.php index 4e30f0384..efc5a7ea6 100644 --- a/tests/agent-memory-events-smoke.php +++ b/tests/agent-memory-events-smoke.php @@ -206,17 +206,17 @@ function is_wp_error( $_thing ): bool { require_once __DIR__ . '/../inc/Core/FilesRepository/GuidelineAgentMemoryStore.php'; use DataMachine\Core\FilesRepository\AgentMemory; -use AgentsAPI\Core\FilesRepository\AgentMemoryListEntry; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; -use AgentsAPI\Core\FilesRepository\AgentMemoryQuery; -use AgentsAPI\Core\FilesRepository\AgentMemoryReadResult; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreCapabilities; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; -use AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Query; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store_Capabilities; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result; use DataMachine\Core\FilesRepository\GuidelineAgentMemoryStore; -class AgentMemoryEventsFakeStore implements AgentMemoryStoreInterface { +class AgentMemoryEventsFakeStore implements WP_Agent_Memory_Store { /** * @var array @@ -226,50 +226,50 @@ class AgentMemoryEventsFakeStore implements AgentMemoryStoreInterface { public bool $fail_next_write = false; public bool $fail_next_delete = false; - public function capabilities(): AgentMemoryStoreCapabilities { - return AgentMemoryStoreCapabilities::none(); + public function capabilities(): WP_Agent_Memory_Store_Capabilities { + return WP_Agent_Memory_Store_Capabilities::none(); } - public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryReadResult { + public function read( WP_Agent_Memory_Scope $scope, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_Read_Result { if ( ! array_key_exists( $scope->key(), $this->files ) ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } $content = $this->files[ $scope->key() ]; - return new AgentMemoryReadResult( true, $content, sha1( $content ), strlen( $content ), 123, null, $metadata_fields ); + return new WP_Agent_Memory_Read_Result( true, $content, sha1( $content ), strlen( $content ), 123, null, $metadata_fields ); } - public function write( AgentMemoryScope $scope, string $content, ?string $_if_match = null, ?AgentMemoryMetadata $metadata = null ): AgentMemoryWriteResult { + public function write( WP_Agent_Memory_Scope $scope, string $content, ?string $_if_match = null, ?WP_Agent_Memory_Metadata $metadata = null ): WP_Agent_Memory_Write_Result { unset( $_if_match, $metadata ); if ( $this->fail_next_write ) { $this->fail_next_write = false; - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } $this->files[ $scope->key() ] = $content; - return AgentMemoryWriteResult::ok( sha1( $content ), strlen( $content ) ); + return WP_Agent_Memory_Write_Result::ok( sha1( $content ), strlen( $content ) ); } - public function exists( AgentMemoryScope $scope ): bool { + public function exists( WP_Agent_Memory_Scope $scope ): bool { return array_key_exists( $scope->key(), $this->files ); } - public function delete( AgentMemoryScope $scope ): AgentMemoryWriteResult { + public function delete( WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Write_Result { if ( $this->fail_next_delete ) { $this->fail_next_delete = false; - return AgentMemoryWriteResult::failure( 'io' ); + return WP_Agent_Memory_Write_Result::failure( 'io' ); } unset( $this->files[ $scope->key() ] ); - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } - public function list_layer( AgentMemoryScope $_scope_query, ?AgentMemoryQuery $query = null ): array { + public function list_layer( WP_Agent_Memory_Scope $_scope_query, ?WP_Agent_Memory_Query $query = null ): array { unset( $_scope_query, $query ); return array(); } - public function list_subtree( AgentMemoryScope $_scope_query, string $_prefix, ?AgentMemoryQuery $query = null ): array { + public function list_subtree( WP_Agent_Memory_Scope $_scope_query, string $_prefix, ?WP_Agent_Memory_Query $query = null ): array { unset( $_scope_query, $_prefix, $query ); return array(); } @@ -299,7 +299,7 @@ function datamachine_agent_memory_events_matching( string $hook ): array { $store = new AgentMemoryEventsFakeStore(); add_filter( 'agents_api_memory_store', - function ( $_default, AgentMemoryScope $_scope ) use ( $store ) { + function ( $_default, WP_Agent_Memory_Scope $_scope ) use ( $store ) { unset( $_default, $_scope ); return $store; }, @@ -315,7 +315,7 @@ function ( $_default, AgentMemoryScope $_scope ) use ( $store ) { $updates = datamachine_agent_memory_events_matching( 'datamachine_agent_memory_updated' ); datamachine_agent_memory_events_assert( 1 === count( $updates ), 'successful write emits one memory update event' ); -datamachine_agent_memory_events_assert( $updates[0]['args'][0] instanceof AgentMemoryScope, 'update event includes AgentMemoryScope argument' ); +datamachine_agent_memory_events_assert( $updates[0]['args'][0] instanceof WP_Agent_Memory_Scope, 'update event includes WP_Agent_Memory_Scope argument' ); datamachine_agent_memory_events_assert( "# Memory\n" === $updates[0]['args'][1], 'update event includes persisted full content' ); $metadata = $updates[0]['args'][2]; @@ -338,7 +338,7 @@ function ( $_default, AgentMemoryScope $_scope ) use ( $store ) { datamachine_agent_memory_events_assert( true === $delete['success'], 'delete succeeds through the fake store' ); $deletes = datamachine_agent_memory_events_matching( 'datamachine_agent_memory_deleted' ); datamachine_agent_memory_events_assert( 1 === count( $deletes ), 'successful delete emits one memory delete event' ); -datamachine_agent_memory_events_assert( $deletes[0]['args'][0] instanceof AgentMemoryScope, 'delete event includes AgentMemoryScope argument' ); +datamachine_agent_memory_events_assert( $deletes[0]['args'][0] instanceof WP_Agent_Memory_Scope, 'delete event includes WP_Agent_Memory_Scope argument' ); datamachine_agent_memory_events_assert( 'agent:site:1:7:42:MEMORY.md' === $deletes[0]['args'][0]->key(), 'delete event scope identifies deleted memory' ); $GLOBALS['datamachine_agent_memory_events_actions'] = array(); @@ -352,7 +352,7 @@ function ( $_default, AgentMemoryScope $_scope ) use ( $store ) { $GLOBALS['datamachine_agent_memory_events_actions'] = array(); $guideline_store = new GuidelineAgentMemoryStore(); -$guideline_scope = new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, 'GUIDELINE.md' ); +$guideline_scope = new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, 'GUIDELINE.md' ); $guideline_write = $guideline_store->write( $guideline_scope, 'Guideline content' ); datamachine_agent_memory_events_assert( true === $guideline_write->success, 'guideline-backed write succeeds when substrate exists' ); @@ -364,7 +364,7 @@ function ( $_default, AgentMemoryScope $_scope ) use ( $store ) { $GLOBALS['datamachine_agent_memory_events_post_types'] = array(); $GLOBALS['datamachine_agent_memory_events_taxonomies'] = array(); $GLOBALS['datamachine_agent_memory_events_actions'] = array(); -$capability_write = $guideline_store->write( new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, 'UNAVAILABLE.md' ), 'No substrate' ); +$capability_write = $guideline_store->write( new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, 'UNAVAILABLE.md' ), 'No substrate' ); datamachine_agent_memory_events_assert( false === $capability_write->success, 'guideline-backed write fails cleanly without substrate' ); datamachine_agent_memory_events_assert( array() === datamachine_agent_memory_events_matching( 'datamachine_guideline_updated' ), 'unavailable guideline substrate does not emit guideline event' ); diff --git a/tests/agent-memory-store-factory-contract-smoke.php b/tests/agent-memory-store-factory-contract-smoke.php index 1488dd1fa..467ad1674 100644 --- a/tests/agent-memory-store-factory-contract-smoke.php +++ b/tests/agent-memory-store-factory-contract-smoke.php @@ -43,59 +43,59 @@ function apply_filters( string $hook, $value, ...$args ) { require_once __DIR__ . '/../inc/Core/FilesRepository/DiskAgentMemoryStore.php'; require_once __DIR__ . '/../inc/Core/FilesRepository/AgentMemoryStoreFactory.php'; -use AgentsAPI\Core\FilesRepository\AgentMemoryListEntry; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; -use AgentsAPI\Core\FilesRepository\AgentMemoryQuery; -use AgentsAPI\Core\FilesRepository\AgentMemoryReadResult; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreCapabilities; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Query; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store_Capabilities; use DataMachine\Core\FilesRepository\AgentMemoryStoreFactory; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; -use AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result; use DataMachine\Core\FilesRepository\DiskAgentMemoryStore; -class AgentMemoryStoreContractFakeStore implements AgentMemoryStoreInterface { +class AgentMemoryStoreContractFakeStore implements WP_Agent_Memory_Store { /** @var array */ public array $files = array(); - /** @var AgentMemoryScope[] */ + /** @var WP_Agent_Memory_Scope[] */ public array $scopes = array(); - public function capabilities(): AgentMemoryStoreCapabilities { - return AgentMemoryStoreCapabilities::none(); + public function capabilities(): WP_Agent_Memory_Store_Capabilities { + return WP_Agent_Memory_Store_Capabilities::none(); } - public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryReadResult { + public function read( WP_Agent_Memory_Scope $scope, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_Read_Result { $this->scopes[] = $scope; if ( ! array_key_exists( $scope->key(), $this->files ) ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } $content = $this->files[ $scope->key() ]; - return new AgentMemoryReadResult( true, $content, sha1( $content ), strlen( $content ), 123, null, $metadata_fields ); + return new WP_Agent_Memory_Read_Result( true, $content, sha1( $content ), strlen( $content ), 123, null, $metadata_fields ); } - public function write( AgentMemoryScope $scope, string $content, ?string $_if_match = null, ?AgentMemoryMetadata $metadata = null ): AgentMemoryWriteResult { + public function write( WP_Agent_Memory_Scope $scope, string $content, ?string $_if_match = null, ?WP_Agent_Memory_Metadata $metadata = null ): WP_Agent_Memory_Write_Result { unset( $_if_match, $metadata ); $this->scopes[] = $scope; $this->files[ $scope->key() ] = $content; - return AgentMemoryWriteResult::ok( sha1( $content ), strlen( $content ) ); + return WP_Agent_Memory_Write_Result::ok( sha1( $content ), strlen( $content ) ); } - public function exists( AgentMemoryScope $scope ): bool { + public function exists( WP_Agent_Memory_Scope $scope ): bool { $this->scopes[] = $scope; return array_key_exists( $scope->key(), $this->files ); } - public function delete( AgentMemoryScope $scope ): AgentMemoryWriteResult { + public function delete( WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Write_Result { $this->scopes[] = $scope; unset( $this->files[ $scope->key() ] ); - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } - public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $query = null ): array { + public function list_layer( WP_Agent_Memory_Scope $scope_query, ?WP_Agent_Memory_Query $query = null ): array { unset( $query ); $this->scopes[] = $scope_query; $entries = array(); @@ -120,13 +120,13 @@ public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $qu continue; } - $entries[] = new AgentMemoryListEntry( $filename, $layer, strlen( $content ), 123 ); + $entries[] = new WP_Agent_Memory_List_Entry( $filename, $layer, strlen( $content ), 123 ); } return $entries; } - public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?AgentMemoryQuery $query = null ): array { + public function list_subtree( WP_Agent_Memory_Scope $scope_query, string $prefix, ?WP_Agent_Memory_Query $query = null ): array { unset( $query ); $this->scopes[] = $scope_query; unset( $prefix, $query ); @@ -150,14 +150,14 @@ function datamachine_agent_memory_store_contract_reset_filters(): void { $GLOBALS['datamachine_agent_memory_store_contract_filters'] = array(); } -function datamachine_agent_memory_store_contract_round_trip( AgentMemoryStoreInterface $store, AgentMemoryScope $scope ): AgentMemoryReadResult { +function datamachine_agent_memory_store_contract_round_trip( WP_Agent_Memory_Store $store, WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Read_Result { $write = $store->write( $scope, "# Memory\n" ); datamachine_agent_memory_store_contract_assert( true === $write->success, 'interface write succeeds without concrete-store branching' ); return $store->read( $scope ); } -$scope = new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, 'MEMORY.md' ); +$scope = new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, 'MEMORY.md' ); datamachine_agent_memory_store_contract_reset_filters(); $default_store = AgentMemoryStoreFactory::for_scope( $scope ); @@ -168,7 +168,7 @@ function datamachine_agent_memory_store_contract_round_trip( AgentMemoryStoreInt $filter_arguments = array(); add_filter( 'agents_api_memory_store', - static function ( $store, AgentMemoryScope $filter_scope ) use ( $fake_store, &$filter_arguments ) { + static function ( $store, WP_Agent_Memory_Scope $filter_scope ) use ( $fake_store, &$filter_arguments ) { $filter_arguments[] = array( $store, $filter_scope ); return $fake_store; }, @@ -194,7 +194,7 @@ static function ( $store, AgentMemoryScope $filter_scope ) use ( $fake_store, &$ 2 ); $invalid_store = AgentMemoryStoreFactory::for_scope( $scope ); -datamachine_agent_memory_store_contract_assert( $invalid_store instanceof DiskAgentMemoryStore, 'factory ignores non-AgentMemoryStoreInterface filter returns' ); +datamachine_agent_memory_store_contract_assert( $invalid_store instanceof DiskAgentMemoryStore, 'factory ignores non-WP_Agent_Memory_Store filter returns' ); datamachine_agent_memory_store_contract_reset_filters(); add_filter( diff --git a/tests/agents-api-bootstrap-smoke.php b/tests/agents-api-bootstrap-smoke.php index 10600eec1..6008b6d45 100644 --- a/tests/agents-api-bootstrap-smoke.php +++ b/tests/agents-api-bootstrap-smoke.php @@ -20,13 +20,13 @@ agents_api_smoke_require_module(); $namespace_map = array( - 'DataMachine\\Engine\\AI\\AgentMessageEnvelope' => 'AgentsAPI\\AI\\AgentMessageEnvelope', - 'DataMachine\\Engine\\AI\\AgentConversationResult' => 'AgentsAPI\\AI\\AgentConversationResult', - 'DataMachine\\Engine\\AI\\Tools\\RuntimeToolDeclaration' => 'AgentsAPI\\AI\\Tools\\RuntimeToolDeclaration', - 'DataMachine\\Core\\Database\\Chat\\ConversationTranscriptStoreInterface' => 'AgentsAPI\\Core\\Database\\Chat\\ConversationTranscriptStoreInterface', - 'DataMachine\\Core\\Database\\Chat\\ConversationTranscriptLockInterface' => 'AgentsAPI\\Core\\Database\\Chat\\ConversationTranscriptLockInterface', - 'DataMachine\\Core\\FilesRepository\\AgentMemoryStoreInterface' => 'AgentsAPI\\Core\\FilesRepository\\AgentMemoryStoreInterface', - 'DataMachine\\Core\\FilesRepository\\AgentMemoryScope' => 'AgentsAPI\\Core\\FilesRepository\\AgentMemoryScope', + 'DataMachine\\Engine\\AI\\WP_Agent_Message' => 'AgentsAPI\\AI\\WP_Agent_Message', + 'DataMachine\\Engine\\AI\\WP_Agent_Conversation_Result' => 'AgentsAPI\\AI\\WP_Agent_Conversation_Result', + 'DataMachine\\Engine\\AI\\Tools\\WP_Agent_Tool_Declaration' => 'AgentsAPI\\AI\\Tools\\WP_Agent_Tool_Declaration', + 'DataMachine\\Core\\Database\\Chat\\WP_Agent_Conversation_Store' => 'AgentsAPI\\Core\\Database\\Chat\\WP_Agent_Conversation_Store', + 'DataMachine\\Core\\Database\\Chat\\WP_Agent_Conversation_Lock' => 'AgentsAPI\\Core\\Database\\Chat\\WP_Agent_Conversation_Lock', + 'DataMachine\\Core\\FilesRepository\\WP_Agent_Memory_Store' => 'AgentsAPI\\Core\\FilesRepository\\WP_Agent_Memory_Store', + 'DataMachine\\Core\\FilesRepository\\WP_Agent_Memory_Scope' => 'AgentsAPI\\Core\\FilesRepository\\WP_Agent_Memory_Scope', ); echo "\n[1] Module bootstrap exposes registration facade without Data Machine product code:\n"; diff --git a/tests/agents-api-consent-policy-smoke.php b/tests/agents-api-consent-policy-smoke.php index a2dbe74cb..afed8b371 100644 --- a/tests/agents-api-consent-policy-smoke.php +++ b/tests/agents-api-consent-policy-smoke.php @@ -16,7 +16,7 @@ require_once dirname( __DIR__ ) . '/inc/Engine/AI/DataMachineAgentConsentPolicy.php'; -use AgentsAPI\AI\Consent\AgentConsentOperation; +use AgentsAPI\AI\Consent\WP_Agent_Consent_Operation; use DataMachine\Engine\AI\DataMachineAgentConsentPolicy; $failures = array(); @@ -43,9 +43,9 @@ function apply_filters( string $_hook_name, $value ) { $policy = DataMachineAgentConsentPolicy::get(); -$assert_true( $policy instanceof WP_Agent_Consent_Policy_Interface, 'Data Machine exposes the Agents API consent policy interface' ); -$assert_true( AgentConsentOperation::STORE_MEMORY === 'store_memory', 'Agents API memory operation vocabulary is installed' ); -$assert_true( AgentConsentOperation::STORE_TRANSCRIPT === 'store_transcript', 'Agents API transcript operation vocabulary is installed' ); +$assert_true( $policy instanceof WP_Agent_Consent_Policy, 'Data Machine exposes the Agents API consent policy interface' ); +$assert_true( WP_Agent_Consent_Operation::STORE_MEMORY === 'store_memory', 'Agents API memory operation vocabulary is installed' ); +$assert_true( WP_Agent_Consent_Operation::STORE_TRANSCRIPT === 'store_transcript', 'Agents API transcript operation vocabulary is installed' ); $memory_decision = $policy->can_store_memory( array( @@ -57,7 +57,7 @@ function apply_filters( string $_hook_name, $value ) { ) ); $assert_true( $memory_decision->is_allowed(), 'memory storage consent follows Data Machine memory permissions' ); -$assert_true( AgentConsentOperation::STORE_MEMORY === $memory_decision->operation(), 'memory decision uses Agents API store_memory operation' ); +$assert_true( WP_Agent_Consent_Operation::STORE_MEMORY === $memory_decision->operation(), 'memory decision uses Agents API store_memory operation' ); $pipeline_default = $policy->can_store_transcript( array( @@ -68,7 +68,7 @@ function apply_filters( string $_hook_name, $value ) { ) ); $assert_true( ! $pipeline_default->is_allowed(), 'non-interactive transcript storage remains denied by default' ); -$assert_true( AgentConsentOperation::STORE_TRANSCRIPT === $pipeline_default->operation(), 'pipeline decision uses Agents API store_transcript operation' ); +$assert_true( WP_Agent_Consent_Operation::STORE_TRANSCRIPT === $pipeline_default->operation(), 'pipeline decision uses Agents API store_transcript operation' ); $pipeline_opt_in = $policy->can_store_transcript( array( @@ -95,11 +95,11 @@ function apply_filters( string $_hook_name, $value ) { $share_default = $policy->can_share_transcript( array( 'mode' => 'chat', 'interactive' => true ) ); $assert_true( ! $share_default->is_allowed(), 'transcript sharing is independently denied without explicit consent' ); -$assert_true( AgentConsentOperation::SHARE_TRANSCRIPT === $share_default->operation(), 'sharing decision uses Agents API share_transcript operation' ); +$assert_true( WP_Agent_Consent_Operation::SHARE_TRANSCRIPT === $share_default->operation(), 'sharing decision uses Agents API share_transcript operation' ); $escalation_default = $policy->can_escalate_to_human( array( 'mode' => 'chat', 'interactive' => true ) ); $assert_true( ! $escalation_default->is_allowed(), 'human escalation is independently denied without explicit consent' ); -$assert_true( AgentConsentOperation::ESCALATE_TO_HUMAN === $escalation_default->operation(), 'escalation decision uses Agents API escalate_to_human operation' ); +$assert_true( WP_Agent_Consent_Operation::ESCALATE_TO_HUMAN === $escalation_default->operation(), 'escalation decision uses Agents API escalate_to_human operation' ); if ( $failures ) { echo "\nFAILED: " . count( $failures ) . " Data Machine consent policy assertions failed.\n"; diff --git a/tests/agents-api-memory-contract-adoption-smoke.php b/tests/agents-api-memory-contract-adoption-smoke.php index 5a46f5161..40d18a05e 100644 --- a/tests/agents-api-memory-contract-adoption-smoke.php +++ b/tests/agents-api-memory-contract-adoption-smoke.php @@ -69,11 +69,11 @@ function apply_filters( string $hook, $value, ...$args ) { require_once __DIR__ . '/../inc/Core/FilesRepository/DiskAgentMemoryStore.php'; require_once __DIR__ . '/../inc/Core/FilesRepository/GuidelineAgentMemoryStore.php'; -use AgentsAPI\AI\Context\ContextAuthorityTier; -use AgentsAPI\AI\Context\ContextConflictKind; -use AgentsAPI\AI\Context\DefaultContextConflictResolver; -use AgentsAPI\AI\Context\RetrievedContextItem; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; +use AgentsAPI\AI\Context\WP_Agent_Context_Authority_Tier; +use AgentsAPI\AI\Context\WP_Agent_Context_Conflict_Kind; +use AgentsAPI\AI\Context\WP_Agent_Default_Context_Conflict_Resolver; +use AgentsAPI\AI\Context\WP_Agent_Context_Item; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; use DataMachine\Core\FilesRepository\DiskAgentMemoryStore; use DataMachine\Core\FilesRepository\GuidelineAgentMemoryStore; use DataMachine\Engine\AI\MemoryFileRegistry; @@ -107,7 +107,7 @@ function datamachine_contract_adoption_assert( bool $condition, string $message $agents_source = WP_Agent_Memory_Registry::get( 'datamachine/site.md' ); datamachine_contract_adoption_assert( null !== $agents_source, 'Data Machine registration creates an Agents API memory source' ); datamachine_contract_adoption_assert( 'SITE.md' === ( $agents_source['meta']['filename'] ?? null ), 'Agents API source retains Data Machine filename adapter metadata' ); -datamachine_contract_adoption_assert( ContextAuthorityTier::WORKSPACE_SHARED === $datamachine_file['authority_tier'], 'shared memory file receives workspace authority tier' ); +datamachine_contract_adoption_assert( WP_Agent_Context_Authority_Tier::WORKSPACE_SHARED === $datamachine_file['authority_tier'], 'shared memory file receives workspace authority tier' ); datamachine_contract_adoption_assert( false === $datamachine_file['editable'], 'composable files remain non-editable through the adapter' ); SectionRegistry::reset(); @@ -122,12 +122,12 @@ function datamachine_contract_adoption_assert( bool $condition, string $message datamachine_contract_adoption_assert( array( 'source_type', 'authority_tier' ) === $unsupported, 'disk store declares unsupported metadata instead of dropping silently' ); $guideline_caps = ( new GuidelineAgentMemoryStore() )->capabilities(); -datamachine_contract_adoption_assert( array() === $guideline_caps->unsupported_metadata_fields( AgentMemoryMetadata::FIELDS, 'persist' ), 'guideline store declares full metadata persistence support' ); +datamachine_contract_adoption_assert( array() === $guideline_caps->unsupported_metadata_fields( WP_Agent_Memory_Metadata::FIELDS, 'persist' ), 'guideline store declares full metadata persistence support' ); -$resolver = new DefaultContextConflictResolver(); +$resolver = new WP_Agent_Default_Context_Conflict_Resolver(); $items = array( - new RetrievedContextItem( 'agent memory says no', array( 'filename' => 'MEMORY.md' ), ContextAuthorityTier::AGENT_MEMORY, array(), ContextConflictKind::AUTHORITATIVE_FACT, 'policy:x' ), - new RetrievedContextItem( 'workspace says yes', array( 'filename' => 'SITE.md' ), ContextAuthorityTier::WORKSPACE_SHARED, array(), ContextConflictKind::AUTHORITATIVE_FACT, 'policy:x' ), + new WP_Agent_Context_Item( 'agent memory says no', array( 'filename' => 'MEMORY.md' ), WP_Agent_Context_Authority_Tier::AGENT_MEMORY, array(), WP_Agent_Context_Conflict_Kind::AUTHORITATIVE_FACT, 'policy:x' ), + new WP_Agent_Context_Item( 'workspace says yes', array( 'filename' => 'SITE.md' ), WP_Agent_Context_Authority_Tier::WORKSPACE_SHARED, array(), WP_Agent_Context_Conflict_Kind::AUTHORITATIVE_FACT, 'policy:x' ), ); $resolution = $resolver->resolve( $items )['policy:x'] ?? null; datamachine_contract_adoption_assert( null !== $resolution && 'workspace says yes' === $resolution->winner->content, 'authority cascade rejects lower-scope memory for authoritative fact conflicts' ); diff --git a/tests/agents-api-memory-store-smoke.php b/tests/agents-api-memory-store-smoke.php index a89b3c9ba..71685fb3a 100644 --- a/tests/agents-api-memory-store-smoke.php +++ b/tests/agents-api-memory-store-smoke.php @@ -36,14 +36,14 @@ function do_action( string $_hook, ...$_args ): void {} require_once __DIR__ . '/agents-api-loader.php'; datamachine_tests_require_agents_api(); -use AgentsAPI\Core\FilesRepository\AgentMemoryListEntry; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; -use AgentsAPI\Core\FilesRepository\AgentMemoryQuery; -use AgentsAPI\Core\FilesRepository\AgentMemoryReadResult; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreCapabilities; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; -use AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Query; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store_Capabilities; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result; $failures = array(); $passes = 0; @@ -87,52 +87,52 @@ function agents_api_memory_has_failures( array $failures ): bool { return count( $failures ) > 0; } -class AgentsApiMemoryFakeStore implements AgentMemoryStoreInterface { +class AgentsApiMemoryFakeStore implements WP_Agent_Memory_Store { /** @var array */ private array $records = array(); - public function capabilities(): AgentMemoryStoreCapabilities { - return AgentMemoryStoreCapabilities::none(); + public function capabilities(): WP_Agent_Memory_Store_Capabilities { + return WP_Agent_Memory_Store_Capabilities::none(); } - public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryReadResult { + public function read( WP_Agent_Memory_Scope $scope, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_Read_Result { $key = $scope->key(); if ( ! array_key_exists( $key, $this->records ) ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } $content = $this->records[ $key ]; - return new AgentMemoryReadResult( true, $content, sha1( $content ), strlen( $content ), 123, null, $metadata_fields ); + return new WP_Agent_Memory_Read_Result( true, $content, sha1( $content ), strlen( $content ), 123, null, $metadata_fields ); } - public function write( AgentMemoryScope $scope, string $content, ?string $if_match = null, ?AgentMemoryMetadata $metadata = null ): AgentMemoryWriteResult { + public function write( WP_Agent_Memory_Scope $scope, string $content, ?string $if_match = null, ?WP_Agent_Memory_Metadata $metadata = null ): WP_Agent_Memory_Write_Result { unset( $metadata ); $current = $this->read( $scope ); if ( null !== $if_match && $current->hash !== $if_match ) { - return AgentMemoryWriteResult::failure( 'conflict' ); + return WP_Agent_Memory_Write_Result::failure( 'conflict' ); } $this->records[ $scope->key() ] = $content; - return AgentMemoryWriteResult::ok( sha1( $content ), strlen( $content ) ); + return WP_Agent_Memory_Write_Result::ok( sha1( $content ), strlen( $content ) ); } - public function exists( AgentMemoryScope $scope ): bool { + public function exists( WP_Agent_Memory_Scope $scope ): bool { return array_key_exists( $scope->key(), $this->records ); } - public function delete( AgentMemoryScope $scope ): AgentMemoryWriteResult { + public function delete( WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Write_Result { unset( $this->records[ $scope->key() ] ); - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } - public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $query = null ): array { + public function list_layer( WP_Agent_Memory_Scope $scope_query, ?WP_Agent_Memory_Query $query = null ): array { unset( $query ); return $this->list_subtree( $scope_query, '' ); } - public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?AgentMemoryQuery $query = null ): array { + public function list_subtree( WP_Agent_Memory_Scope $scope_query, string $prefix, ?WP_Agent_Memory_Query $query = null ): array { unset( $query ); $entries = array(); $prefix = trim( $prefix, '/' ); @@ -157,7 +157,7 @@ public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?Ag continue; } - $entries[] = new AgentMemoryListEntry( $filename, $layer, strlen( $content ), 123 ); + $entries[] = new WP_Agent_Memory_List_Entry( $filename, $layer, strlen( $content ), 123 ); } return $entries; @@ -167,17 +167,17 @@ public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?Ag echo "agents-api-memory-store-smoke\n"; echo "\n[1] Module bootstrap exposes memory contracts without Data Machine product runtime:\n"; -agents_api_memory_assert( class_exists( AgentMemoryScope::class ), 'AgentMemoryScope is available' ); -agents_api_memory_assert( class_exists( AgentMemoryReadResult::class ), 'AgentMemoryReadResult is available' ); -agents_api_memory_assert( class_exists( AgentMemoryWriteResult::class ), 'AgentMemoryWriteResult is available' ); -agents_api_memory_assert( class_exists( AgentMemoryListEntry::class ), 'AgentMemoryListEntry is available' ); -agents_api_memory_assert( interface_exists( AgentMemoryStoreInterface::class ), 'AgentMemoryStoreInterface is available' ); +agents_api_memory_assert( class_exists( WP_Agent_Memory_Scope::class ), 'WP_Agent_Memory_Scope is available' ); +agents_api_memory_assert( class_exists( WP_Agent_Memory_Read_Result::class ), 'WP_Agent_Memory_Read_Result is available' ); +agents_api_memory_assert( class_exists( WP_Agent_Memory_Write_Result::class ), 'WP_Agent_Memory_Write_Result is available' ); +agents_api_memory_assert( class_exists( WP_Agent_Memory_List_Entry::class ), 'WP_Agent_Memory_List_Entry is available' ); +agents_api_memory_assert( interface_exists( WP_Agent_Memory_Store::class ), 'WP_Agent_Memory_Store is available' ); agents_api_memory_assert( ! class_exists( 'DataMachine\Core\FilesRepository\DiskAgentMemoryStore', false ), 'DiskAgentMemoryStore is not loaded by agents-api bootstrap' ); agents_api_memory_assert( ! class_exists( 'DataMachine\Core\FilesRepository\AgentMemoryStoreFactory', false ), 'Data Machine memory factory is not loaded by agents-api bootstrap' ); echo "\n[2] Fake store satisfies the contract shape in isolation:\n"; $store = new AgentsApiMemoryFakeStore(); -$scope = new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, 'MEMORY.md' ); +$scope = new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, 'MEMORY.md' ); $missing = $store->read( $scope ); agents_api_memory_assert_same( false, $missing->exists, 'missing read returns not-found sentinel' ); @@ -200,14 +200,14 @@ public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?Ag $cas_write = $store->write( $scope, "Second memory\n", $read->hash ); agents_api_memory_assert_same( true, $cas_write->success, 'compare-and-swap write succeeds with matching hash' ); -$daily_scope = new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, 'daily/2026/04/17.md' ); +$daily_scope = new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, 'daily/2026/04/17.md' ); $store->write( $daily_scope, "Daily memory\n" ); -$layer_entries = $store->list_layer( new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, '' ) ); -agents_api_memory_assert_same( array( 'MEMORY.md', 'daily/2026/04/17.md' ), array_map( static fn( AgentMemoryListEntry $entry ): string => $entry->filename, $layer_entries ), 'layer list returns scoped entries' ); +$layer_entries = $store->list_layer( new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, '' ) ); +agents_api_memory_assert_same( array( 'MEMORY.md', 'daily/2026/04/17.md' ), array_map( static fn( WP_Agent_Memory_List_Entry $entry ): string => $entry->filename, $layer_entries ), 'layer list returns scoped entries' ); -$subtree_entries = $store->list_subtree( new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, '' ), 'daily' ); -agents_api_memory_assert_same( array( 'daily/2026/04/17.md' ), array_map( static fn( AgentMemoryListEntry $entry ): string => $entry->filename, $subtree_entries ), 'subtree list filters by prefix' ); +$subtree_entries = $store->list_subtree( new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, '' ), 'daily' ); +agents_api_memory_assert_same( array( 'daily/2026/04/17.md' ), array_map( static fn( WP_Agent_Memory_List_Entry $entry ): string => $entry->filename, $subtree_entries ), 'subtree list filters by prefix' ); $delete = $store->delete( $scope ); agents_api_memory_assert_same( true, $delete->success, 'delete succeeds' ); diff --git a/tests/agents-api-package-contract-smoke.php b/tests/agents-api-package-contract-smoke.php index 428e893bc..d2140de4b 100644 --- a/tests/agents-api-package-contract-smoke.php +++ b/tests/agents-api-package-contract-smoke.php @@ -194,7 +194,7 @@ static function () use ( $validate_callback, $diff_callback ): void { agents_api_smoke_assert_equals( 1, count( $GLOBALS['__agents_api_smoke_wrong'] ), 'outside-hook direct artifact type registration is rejected', $failures, $passes ); echo "\n[4] Adopter contracts stay runtime-neutral:\n"; -class Agents_API_Package_Smoke_Adopter implements WP_Agent_Package_Adopter_Interface { +class Agents_API_Package_Smoke_Adopter implements WP_Agent_Package_Adopter { public function diff( WP_Agent_Package $package ): WP_Agent_Package_Adoption_Diff { return new WP_Agent_Package_Adoption_Diff( 'needs-adoption', @@ -228,7 +228,7 @@ public function adopt( WP_Agent_Package $package, array $options = array() ): WP '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-interface.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', diff --git a/tests/agents-api-standalone-skeleton-plan-smoke.php b/tests/agents-api-standalone-skeleton-plan-smoke.php index 5943bce89..40bb8cd2f 100644 --- a/tests/agents-api-standalone-skeleton-plan-smoke.php +++ b/tests/agents-api-standalone-skeleton-plan-smoke.php @@ -44,11 +44,11 @@ 'wp_register_agent()', 'WP_Agent', 'WP_Agents_Registry', - 'AgentsAPI\\AI\\AgentMessageEnvelope', - 'AgentsAPI\\AI\\AgentConversationResult', - 'AgentsAPI\\AI\\Tools\\RuntimeToolDeclaration', - 'AgentsAPI\\Core\\Database\\Chat\\ConversationTranscriptStoreInterface', - 'AgentsAPI\\Core\\FilesRepository\\AgentMemoryStoreInterface', + 'AgentsAPI\\AI\\WP_Agent_Message', + 'AgentsAPI\\AI\\WP_Agent_Conversation_Result', + 'AgentsAPI\\AI\\Tools\\WP_Agent_Tool_Declaration', + 'AgentsAPI\\Core\\Database\\Chat\\WP_Agent_Conversation_Store', + 'AgentsAPI\\Core\\FilesRepository\\WP_Agent_Memory_Store', 'No `wp-agents/v1` REST routes.', 'No admin UI, React app, settings screen, list table, or agent CRUD screen.', 'Data Machine may depend on `agents-api`; `agents-api` must not depend on Data Machine.', @@ -71,17 +71,17 @@ '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-interface.php', + 'inc/class-wp-agent-package-adopter.php', 'inc/register-agent-package-artifacts.php', - 'inc/AI/AgentMessageEnvelope.php', - 'inc/AI/AgentConversationResult.php', - 'inc/AI/Tools/RuntimeToolDeclaration.php', - 'inc/Core/Database/Chat/ConversationTranscriptStoreInterface.php', - 'inc/Core/FilesRepository/AgentMemoryScope.php', - 'inc/Core/FilesRepository/AgentMemoryListEntry.php', - 'inc/Core/FilesRepository/AgentMemoryReadResult.php', - 'inc/Core/FilesRepository/AgentMemoryWriteResult.php', - 'inc/Core/FilesRepository/AgentMemoryStoreInterface.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', ); diff --git a/tests/agents-api-transcript-store-smoke.php b/tests/agents-api-transcript-store-smoke.php index bfb7a5d1c..22acd3ad6 100644 --- a/tests/agents-api-transcript-store-smoke.php +++ b/tests/agents-api-transcript-store-smoke.php @@ -31,8 +31,8 @@ function add_action( string $hook, callable $callback, int $priority = 10, int $ require_once __DIR__ . '/agents-api-loader.php'; datamachine_tests_require_agents_api(); -use AgentsAPI\Core\Database\Chat\ConversationTranscriptStoreInterface; -use AgentsAPI\Core\Workspace\AgentWorkspaceScope; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Store; +use AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope; $failures = array(); $passes = 0; @@ -48,14 +48,14 @@ function add_action( string $hook, callable $callback, int $priority = 10, int $ echo "FAIL: {$label}\n"; }; -class AgentsApiFakeTranscriptStore implements ConversationTranscriptStoreInterface { +class AgentsApiFakeTranscriptStore implements WP_Agent_Conversation_Store { /** * @var array> */ private array $sessions = array(); - public function create_session( \AgentsAPI\Core\Workspace\AgentWorkspaceScope $workspace, int $user_id, int $agent_id = 0, array $metadata = array(), string $context = 'chat' ): string { + public function create_session( \AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope $workspace, int $user_id, int $agent_id = 0, array $metadata = array(), string $context = 'chat' ): string { $session_id = 'session-' . ( count( $this->sessions ) + 1 ); $this->sessions[ $session_id ] = array( @@ -84,7 +84,8 @@ public function get_session( string $session_id ): ?array { return $this->sessions[ $session_id ] ?? null; } - public function update_session( string $session_id, array $messages, array $metadata = array(), string $provider = '', string $model = '' ): bool { + public function update_session( string $session_id, array $messages, array $metadata = array(), string $provider = '', string $model = '', ?string $provider_response_id = null ): bool { + unset( $provider_response_id ); if ( ! isset( $this->sessions[ $session_id ] ) ) { return false; } @@ -103,7 +104,7 @@ public function delete_session( string $session_id ): bool { return true; } - public function get_recent_pending_session( \AgentsAPI\Core\Workspace\AgentWorkspaceScope $workspace, int $user_id, int $seconds = 600, string $context = 'chat', ?int $token_id = null ): ?array { + public function get_recent_pending_session( \AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope $workspace, int $user_id, int $seconds = 600, string $context = 'chat', ?int $token_id = null ): ?array { unset( $workspace, $seconds, $token_id ); foreach ( array_reverse( $this->sessions ) as $session ) { @@ -128,14 +129,14 @@ public function update_title( string $session_id, string $title ): bool { echo "agents-api-transcript-store-smoke\n"; $assert_true( defined( 'AGENTS_API_LOADED' ), 'agents-api bootstrap loads without Data Machine product runtime' ); -$assert_true( interface_exists( ConversationTranscriptStoreInterface::class ), 'transcript contract is loaded by agents-api bootstrap' ); +$assert_true( interface_exists( WP_Agent_Conversation_Store::class ), 'transcript contract is loaded by agents-api bootstrap' ); $assert_true( false === class_exists( 'DataMachine\\Core\\Database\\Chat\\Chat', false ), 'Data Machine chat table implementation is not loaded' ); $assert_true( false === interface_exists( 'DataMachine\\Core\\Database\\Chat\\ConversationStoreInterface', false ), 'Data Machine aggregate chat contract is not loaded' ); $store = new AgentsApiFakeTranscriptStore(); -$assert_true( in_array( ConversationTranscriptStoreInterface::class, class_implements( $store ), true ), 'fake store can implement transcript contract without chat product interfaces' ); +$assert_true( in_array( WP_Agent_Conversation_Store::class, class_implements( $store ), true ), 'fake store can implement transcript contract without chat product interfaces' ); -$workspace = AgentWorkspaceScope::from_parts( 'site', 'https://example.test' ); +$workspace = WP_Agent_Workspace_Scope::from_parts( 'site', 'https://example.test' ); $session_id = $store->create_session( $workspace, 7, 3, array( 'source' => 'smoke' ), 'pipeline' ); $assert_true( 'session-1' === $session_id, 'fake store creates transcript session IDs' ); $assert_true( null !== $store->get_recent_pending_session( $workspace, 7, 600, 'pipeline' ), 'fake store can query pending transcript sessions' ); diff --git a/tests/ai-message-envelope-smoke.php b/tests/ai-message-envelope-smoke.php index 364fb971f..90c5fbe9f 100644 --- a/tests/ai-message-envelope-smoke.php +++ b/tests/ai-message-envelope-smoke.php @@ -9,9 +9,9 @@ require_once __DIR__ . '/bootstrap-unit.php'; -use AgentsAPI\AI\AgentConversationResult; +use AgentsAPI\AI\WP_Agent_Conversation_Result; use DataMachine\Engine\AI\ConversationManager; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; function datamachine_message_envelope_assert( bool $condition, string $message ): void { if ( ! $condition ) { @@ -30,16 +30,16 @@ function datamachine_message_envelope_count(): void { 'content' => 'Hello world.', ); -$text_envelope = AgentMessageEnvelope::normalize( $legacy_text ); -datamachine_message_envelope_assert( AgentMessageEnvelope::SCHEMA === $text_envelope['schema'], 'Legacy text normalizes to the Agents API schema.' ); +$text_envelope = WP_Agent_Message::normalize( $legacy_text ); +datamachine_message_envelope_assert( WP_Agent_Message::SCHEMA === $text_envelope['schema'], 'Legacy text normalizes to the Agents API schema.' ); datamachine_message_envelope_count(); -datamachine_message_envelope_assert( AgentMessageEnvelope::VERSION === $text_envelope['version'], 'Legacy text normalizes to the current envelope version.' ); +datamachine_message_envelope_assert( WP_Agent_Message::VERSION === $text_envelope['version'], 'Legacy text normalizes to the current envelope version.' ); datamachine_message_envelope_count(); -datamachine_message_envelope_assert( AgentMessageEnvelope::TYPE_TEXT === $text_envelope['type'], 'Legacy text infers text type.' ); +datamachine_message_envelope_assert( WP_Agent_Message::TYPE_TEXT === $text_envelope['type'], 'Legacy text infers text type.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( array() === $text_envelope['payload'], 'Plain legacy text normalizes with an empty payload.' ); datamachine_message_envelope_count(); -datamachine_message_envelope_assert( $legacy_text === AgentMessageEnvelope::to_provider_message( $legacy_text ), 'Plain legacy text projects without provider metadata churn.' ); +datamachine_message_envelope_assert( $legacy_text === WP_Agent_Message::to_provider_message( $legacy_text ), 'Plain legacy text projects without provider metadata churn.' ); datamachine_message_envelope_count(); $legacy_tool_call = array( @@ -53,18 +53,18 @@ function datamachine_message_envelope_count(): void { ), ); -$tool_call_envelope = AgentMessageEnvelope::normalize( $legacy_tool_call ); -datamachine_message_envelope_assert( AgentMessageEnvelope::TYPE_TOOL_CALL === $tool_call_envelope['type'], 'Legacy tool call keeps explicit tool_call type.' ); +$tool_call_envelope = WP_Agent_Message::normalize( $legacy_tool_call ); +datamachine_message_envelope_assert( WP_Agent_Message::TYPE_TOOL_CALL === $tool_call_envelope['type'], 'Legacy tool call keeps explicit tool_call type.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( 'wiki_upsert' === $tool_call_envelope['payload']['tool_name'], 'Tool call tool_name is promoted to envelope payload.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( array( 'title' => 'Demo' ) === $tool_call_envelope['payload']['parameters'], 'Tool call parameters are promoted to envelope payload.' ); datamachine_message_envelope_count(); -datamachine_message_envelope_assert( $legacy_tool_call === AgentMessageEnvelope::to_provider_message( $tool_call_envelope ), 'Tool call envelope projects back to provider message shape.' ); +datamachine_message_envelope_assert( $legacy_tool_call === WP_Agent_Message::to_provider_message( $tool_call_envelope ), 'Tool call envelope projects back to provider message shape.' ); datamachine_message_envelope_count(); $built_tool_call = ConversationManager::formatToolCallMessage( 'wiki_upsert', array( 'title' => 'Demo' ), 3 ); -datamachine_message_envelope_assert( AgentMessageEnvelope::TYPE_TOOL_CALL === $built_tool_call['type'], 'ConversationManager emits tool_call envelopes.' ); +datamachine_message_envelope_assert( WP_Agent_Message::TYPE_TOOL_CALL === $built_tool_call['type'], 'ConversationManager emits tool_call envelopes.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( 'wiki_upsert' === $built_tool_call['payload']['tool_name'], 'ConversationManager stores tool call details in payload.' ); datamachine_message_envelope_count(); @@ -81,60 +81,60 @@ function datamachine_message_envelope_count(): void { ), ); -$tool_result_envelope = AgentMessageEnvelope::normalize( $legacy_tool_result ); -datamachine_message_envelope_assert( AgentMessageEnvelope::TYPE_TOOL_RESULT === $tool_result_envelope['type'], 'Legacy tool result keeps explicit tool_result type.' ); +$tool_result_envelope = WP_Agent_Message::normalize( $legacy_tool_result ); +datamachine_message_envelope_assert( WP_Agent_Message::TYPE_TOOL_RESULT === $tool_result_envelope['type'], 'Legacy tool result keeps explicit tool_result type.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( true === $tool_result_envelope['payload']['success'], 'Tool result success is promoted to envelope payload.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( array( 'post_id' => 123 ) === $tool_result_envelope['payload']['tool_data'], 'Tool result data is promoted to envelope payload.' ); datamachine_message_envelope_count(); -datamachine_message_envelope_assert( $legacy_tool_result === AgentMessageEnvelope::to_provider_message( $tool_result_envelope ), 'Tool result envelope projects back to provider message shape.' ); +datamachine_message_envelope_assert( $legacy_tool_result === WP_Agent_Message::to_provider_message( $tool_result_envelope ), 'Tool result envelope projects back to provider message shape.' ); datamachine_message_envelope_count(); $typed_final_result = array( - 'schema' => AgentMessageEnvelope::SCHEMA, - 'version' => AgentMessageEnvelope::VERSION, - 'type' => AgentMessageEnvelope::TYPE_FINAL_RESULT, + 'schema' => WP_Agent_Message::SCHEMA, + 'version' => WP_Agent_Message::VERSION, + 'type' => WP_Agent_Message::TYPE_FINAL_RESULT, 'role' => 'assistant', 'content' => 'Finished.', 'payload' => array( 'status' => 'complete' ), 'metadata' => array( 'provider_message_id' => 'msg_123' ), ); -$typed_envelope = AgentMessageEnvelope::normalize( $typed_final_result ); +$typed_envelope = WP_Agent_Message::normalize( $typed_final_result ); datamachine_message_envelope_assert( 'assistant' === $typed_envelope['role'], 'Future typed envelope keeps role in canonical output.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( 'Finished.' === $typed_envelope['content'], 'Future typed envelope keeps content in canonical output.' ); datamachine_message_envelope_count(); -datamachine_message_envelope_assert( AgentMessageEnvelope::TYPE_FINAL_RESULT === $typed_envelope['type'], 'Future typed envelope keeps type as a top-level field.' ); +datamachine_message_envelope_assert( WP_Agent_Message::TYPE_FINAL_RESULT === $typed_envelope['type'], 'Future typed envelope keeps type as a top-level field.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( 'complete' === $typed_envelope['payload']['status'], 'Future typed envelope keeps type-specific data in payload.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( 'msg_123' === $typed_envelope['metadata']['provider_message_id'], 'Future typed envelope preserves extension metadata.' ); datamachine_message_envelope_count(); -$typed_provider = AgentMessageEnvelope::to_provider_message( $typed_envelope ); -datamachine_message_envelope_assert( AgentMessageEnvelope::TYPE_FINAL_RESULT === $typed_provider['metadata']['type'], 'Provider projection folds type into metadata.' ); +$typed_provider = WP_Agent_Message::to_provider_message( $typed_envelope ); +datamachine_message_envelope_assert( WP_Agent_Message::TYPE_FINAL_RESULT === $typed_provider['metadata']['type'], 'Provider projection folds type into metadata.' ); datamachine_message_envelope_count(); datamachine_message_envelope_assert( 'complete' === $typed_provider['metadata']['status'], 'Provider projection folds payload into metadata.' ); datamachine_message_envelope_count(); $typed_delta = array( - 'schema' => AgentMessageEnvelope::SCHEMA, - 'version' => AgentMessageEnvelope::VERSION, - 'type' => AgentMessageEnvelope::TYPE_DELTA, + 'schema' => WP_Agent_Message::SCHEMA, + 'version' => WP_Agent_Message::VERSION, + 'type' => WP_Agent_Message::TYPE_DELTA, 'content' => 'partial token', 'payload' => array( 'index' => 0 ), ); -$delta_envelope = AgentMessageEnvelope::normalize( $typed_delta ); +$delta_envelope = WP_Agent_Message::normalize( $typed_delta ); datamachine_message_envelope_assert( 'assistant' === $delta_envelope['role'], 'Typed delta envelope gets assistant default role.' ); datamachine_message_envelope_count(); $old_data_envelope = $typed_delta; $old_data_envelope['data'] = $old_data_envelope['payload']; unset( $old_data_envelope['payload'] ); -$old_data_normalized = AgentMessageEnvelope::normalize( $old_data_envelope ); +$old_data_normalized = WP_Agent_Message::normalize( $old_data_envelope ); datamachine_message_envelope_assert( array( 'index' => 0 ) === $old_data_normalized['payload'], 'Old data envelope key is accepted as a read-time compatibility input.' ); datamachine_message_envelope_count(); @@ -146,11 +146,11 @@ function datamachine_message_envelope_count(): void { ), ); -$multimodal_envelope = AgentMessageEnvelope::normalize( $multimodal_legacy ); -datamachine_message_envelope_assert( AgentMessageEnvelope::TYPE_MULTIMODAL_PART === $multimodal_envelope['type'], 'Array content infers multimodal_part type.' ); +$multimodal_envelope = WP_Agent_Message::normalize( $multimodal_legacy ); +datamachine_message_envelope_assert( WP_Agent_Message::TYPE_MULTIMODAL_PART === $multimodal_envelope['type'], 'Array content infers multimodal_part type.' ); datamachine_message_envelope_count(); -$result = AgentConversationResult::normalize( +$result = WP_Agent_Conversation_Result::normalize( array( 'messages' => array( $typed_final_result ), 'final_content' => 'Finished.', @@ -162,14 +162,14 @@ function datamachine_message_envelope_count(): void { ) ); -datamachine_message_envelope_assert( AgentMessageEnvelope::TYPE_FINAL_RESULT === $result['messages'][0]['type'], 'AgentConversationResult accepts typed envelopes and returns canonical envelopes.' ); +datamachine_message_envelope_assert( WP_Agent_Message::TYPE_FINAL_RESULT === $result['messages'][0]['type'], 'WP_Agent_Conversation_Result accepts typed envelopes and returns canonical envelopes.' ); datamachine_message_envelope_count(); try { - AgentMessageEnvelope::normalize( + WP_Agent_Message::normalize( array( - 'schema' => AgentMessageEnvelope::SCHEMA, - 'version' => AgentMessageEnvelope::VERSION, + 'schema' => WP_Agent_Message::SCHEMA, + 'version' => WP_Agent_Message::VERSION, 'type' => 'unknown_type', 'content' => 'bad', ) diff --git a/tests/ai-request-metadata-guardrails-smoke.php b/tests/ai-request-metadata-guardrails-smoke.php index 75004098e..594800c5e 100644 --- a/tests/ai-request-metadata-guardrails-smoke.php +++ b/tests/ai-request-metadata-guardrails-smoke.php @@ -103,8 +103,8 @@ function size_format( $bytes ): string { use DataMachine\Engine\AI\DataMachinePipelineTranscriptPersister; use DataMachine\Engine\AI\Directives\DirectiveInterface; use DataMachine\Engine\AI\RequestBuilder; -use AgentsAPI\AI\AgentConversationRequest; -use AgentsAPI\Core\Workspace\AgentWorkspaceScope; +use AgentsAPI\AI\WP_Agent_Conversation_Request; +use AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope; class RequestMetadataSmokeDirective implements DirectiveInterface { public static function get_outputs( string $provider_name, array $tools, ?string $step_id = null, array $payload = array() ): array { @@ -121,21 +121,22 @@ class RequestMetadataSmokeStore implements ConversationStoreInterface { public array $sessions = array(); public array $updated = array(); - public function create_session( AgentWorkspaceScope $workspace, int $user_id, int $agent_id = 0, array $metadata = array(), string $context = 'chat' ): string { + public function create_session( WP_Agent_Workspace_Scope $workspace, int $user_id, int $agent_id = 0, array $metadata = array(), string $context = 'chat' ): string { $metadata['workspace'] = $workspace->to_array(); $this->sessions['smoke-session'] = compact( 'user_id', 'agent_id', 'metadata', 'context' ); return 'smoke-session'; } public function get_session( string $session_id ): ?array { return null; } - public function update_session( string $session_id, array $messages, array $metadata = array(), string $provider = '', string $model = '' ): bool { + public function update_session( string $session_id, array $messages, array $metadata = array(), string $provider = '', string $model = '', ?string $provider_response_id = null ): bool { + unset( $provider_response_id ); $this->updated[ $session_id ] = compact( 'messages', 'metadata', 'provider', 'model' ); return true; } public function delete_session( string $session_id ): bool { return true; } public function get_user_sessions( int $user_id, int $limit = 20, int $offset = 0, ?string $context = null, ?int $agent_id = null ): array { return array(); } public function get_user_session_count( int $user_id, ?string $context = null, ?int $agent_id = null ): int { return 0; } - public function get_recent_pending_session( AgentWorkspaceScope $workspace, int $user_id, int $seconds = 600, string $context = 'chat', ?int $token_id = null ): ?array { return null; } + public function get_recent_pending_session( WP_Agent_Workspace_Scope $workspace, int $user_id, int $seconds = 600, string $context = 'chat', ?int $token_id = null ): ?array { return null; } public function update_title( string $session_id, string $title ): bool { return true; } public function count_unread( array $messages, ?string $last_read_at ): int { return 0; } public function mark_session_read( string $session_id, int $user_id ) { return gmdate( 'Y-m-d H:i:s' ); } @@ -286,7 +287,7 @@ function ( array $request ) { $property = new ReflectionProperty( ConversationStoreFactory::class, 'instance' ); $property->setValue( null, $store ); -$transcript_request = new AgentConversationRequest( +$transcript_request = new WP_Agent_Conversation_Request( array( array( 'role' => 'user', 'content' => 'hello' ) ), array(), null, @@ -294,7 +295,7 @@ function ( array $request ) { array( 'provider' => 'openai', 'model' => 'gpt-smoke' ), 10, false, - AgentWorkspaceScope::from_parts( 'wordpress_site', 'default' ) + WP_Agent_Workspace_Scope::from_parts( 'wordpress_site', 'default' ) ); $session_id = ( new DataMachinePipelineTranscriptPersister() )->persist( diff --git a/tests/conversation-store-contracts-smoke.php b/tests/conversation-store-contracts-smoke.php index 8c8e90147..0c2d8d07b 100644 --- a/tests/conversation-store-contracts-smoke.php +++ b/tests/conversation-store-contracts-smoke.php @@ -37,8 +37,8 @@ function add_filter( string $tag, callable $callback, int $priority = 10, int $a use DataMachine\Core\Database\Chat\ConversationSessionIndexInterface; use DataMachine\Core\Database\Chat\ConversationStoreFactory; use DataMachine\Core\Database\Chat\ConversationStoreInterface; -use AgentsAPI\Core\Database\Chat\ConversationTranscriptLockInterface; -use AgentsAPI\Core\Database\Chat\ConversationTranscriptStoreInterface; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Lock; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Store; use DataMachine\Tests\Unit\Core\Database\Chat\InMemoryConversationStore; $failures = array(); @@ -52,8 +52,8 @@ function add_filter( string $tag, callable $callback, int $priority = 10, int $a }; $narrow_contracts = array( - ConversationTranscriptStoreInterface::class, - ConversationTranscriptLockInterface::class, + WP_Agent_Conversation_Store::class, + WP_Agent_Conversation_Lock::class, ConversationSessionIndexInterface::class, ConversationReadStateInterface::class, ConversationRetentionInterface::class, @@ -65,8 +65,8 @@ function add_filter( string $tag, callable $callback, int $priority = 10, int $a $assert_true( $aggregate->implementsInterface( $contract ), "aggregate composes {$contract}" ); } -$transcript_ref = new ReflectionClass( ConversationTranscriptStoreInterface::class ); -$lock_ref = new ReflectionClass( ConversationTranscriptLockInterface::class ); +$transcript_ref = new ReflectionClass( WP_Agent_Conversation_Store::class ); +$lock_ref = new ReflectionClass( WP_Agent_Conversation_Lock::class ); $assert_true( ! $transcript_ref->implementsInterface( ConversationSessionIndexInterface::class ), 'transcript contract does not include session index surface' ); $assert_true( ! $transcript_ref->implementsInterface( ConversationReadStateInterface::class ), 'transcript contract does not include read-state surface' ); $assert_true( ! $transcript_ref->implementsInterface( ConversationRetentionInterface::class ), 'transcript contract does not include retention surface' ); @@ -116,7 +116,7 @@ function add_filter( string $tag, callable $callback, int $priority = 10, int $a $assert_true( ConversationStoreInterface::class === (string) $factory_get->getReturnType(), 'factory still returns the aggregate contract' ); $factory_transcript_get = new ReflectionMethod( ConversationStoreFactory::class, 'get_transcript_store' ); -$assert_true( ConversationTranscriptStoreInterface::class === (string) $factory_transcript_get->getReturnType(), 'factory exposes the narrow transcript contract' ); +$assert_true( WP_Agent_Conversation_Store::class === (string) $factory_transcript_get->getReturnType(), 'factory exposes the narrow transcript contract' ); echo "\n"; if ( empty( $failures ) ) { diff --git a/tests/conversation-transcript-lock-smoke.php b/tests/conversation-transcript-lock-smoke.php index 2dce6b315..11433fa1a 100644 --- a/tests/conversation-transcript-lock-smoke.php +++ b/tests/conversation-transcript-lock-smoke.php @@ -34,7 +34,7 @@ function add_filter( string $tag, callable $callback, int $priority = 10, int $a datamachine_tests_require_agents_api(); require_once dirname( __DIR__ ) . '/vendor/autoload.php'; -use AgentsAPI\Core\Database\Chat\ConversationTranscriptLockInterface; +use AgentsAPI\Core\Database\Chat\WP_Agent_Conversation_Lock; use DataMachine\Tests\Unit\Core\Database\Chat\InMemoryConversationStore; $failures = array(); @@ -57,7 +57,7 @@ function add_filter( string $tag, callable $callback, int $priority = 10, int $a $store->set_clock( 1000 ); $session_id = $store->create_session( 1, 0, array(), 'chat' ); -$assert_equals( true, $store instanceof ConversationTranscriptLockInterface, 'Data Machine aggregate store implements Agents API lock contract' ); +$assert_equals( true, $store instanceof WP_Agent_Conversation_Lock, 'Data Machine aggregate store implements Agents API lock contract' ); echo "\n[1] Acquire then release succeeds:\n"; $token = $store->acquire_session_lock( $session_id, 30 ); diff --git a/tests/daily-memory-overflow-split-smoke.php b/tests/daily-memory-overflow-split-smoke.php index 3ed8383c4..169fd593a 100644 --- a/tests/daily-memory-overflow-split-smoke.php +++ b/tests/daily-memory-overflow-split-smoke.php @@ -38,8 +38,8 @@ function add_action( string $_hook, callable $_callback, int $_priority = 10, in $source = (string) file_get_contents( __DIR__ . '/../inc/Engine/AI/System/Tasks/DailyMemoryTask.php' ); dm_overflow_assert( str_contains( $source, 'maybeHandleDeterministicOverflow' ), 'production task has deterministic overflow hook', $failures, $passes ); dm_overflow_assert( ! str_contains( $source, 'splitMemorySectionsForOverflow' ), 'local section-split helper is removed', $failures, $passes ); -dm_overflow_assert( str_contains( $source, 'AgentConversationCompaction::compact' ), 'overflow decision is delegated to Agents API compaction', $failures, $passes ); -dm_overflow_assert( str_contains( $source, 'AgentMarkdownSectionCompactionAdapter::parse' ), 'markdown projection uses Agents API adapter', $failures, $passes ); +dm_overflow_assert( str_contains( $source, 'WP_Agent_Conversation_Compaction::compact' ), 'overflow decision is delegated to Agents API compaction', $failures, $passes ); +dm_overflow_assert( str_contains( $source, 'WP_Agent_Markdown_Section_Compaction_Adapter::parse' ), 'markdown projection uses Agents API adapter', $failures, $passes ); dm_overflow_assert( str_contains( $source, 'datamachine_daily_memory_overflow_threshold' ), 'overflow threshold is filterable', $failures, $passes ); dm_overflow_assert( str_contains( $source, 'datamachine_daily_memory_overflow_target_size' ), 'overflow target size is filterable', $failures, $passes ); diff --git a/tests/daily-memory-store-seam-smoke.php b/tests/daily-memory-store-seam-smoke.php index 1adbd152d..f2c47de5d 100644 --- a/tests/daily-memory-store-seam-smoke.php +++ b/tests/daily-memory-store-seam-smoke.php @@ -4,7 +4,7 @@ * * Verifies that the default DailyMemory implementation addresses daily * entries as agent-layer files under daily/YYYY/MM/DD.md through the active - * AgentMemoryStoreInterface. The older datamachine_daily_memory_storage seam + * WP_Agent_Memory_Store. The older datamachine_daily_memory_storage seam * is ability-level only and is intentionally not needed for this default path. */ @@ -60,73 +60,73 @@ function size_format( int $bytes ): string { require_once __DIR__ . '/../inc/Core/FilesRepository/DailyMemoryStorage.php'; require_once __DIR__ . '/../inc/Core/FilesRepository/DailyMemory.php'; -use AgentsAPI\Core\FilesRepository\AgentMemoryListEntry; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; -use AgentsAPI\Core\FilesRepository\AgentMemoryQuery; -use AgentsAPI\Core\FilesRepository\AgentMemoryReadResult; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreCapabilities; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; -use AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Query; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store_Capabilities; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result; use DataMachine\Core\FilesRepository\DailyMemory; use DataMachine\Engine\AI\MemoryFileRegistry; -class DailyMemorySeamFakeStore implements AgentMemoryStoreInterface { +class DailyMemorySeamFakeStore implements WP_Agent_Memory_Store { /** @var array */ public array $files = array(); /** @var string[] */ public array $operations = array(); - /** @var AgentMemoryScope[] */ + /** @var WP_Agent_Memory_Scope[] */ public array $scopes = array(); /** @var string[] */ public array $list_prefixes = array(); - public function capabilities(): AgentMemoryStoreCapabilities { - return AgentMemoryStoreCapabilities::none(); + public function capabilities(): WP_Agent_Memory_Store_Capabilities { + return WP_Agent_Memory_Store_Capabilities::none(); } - public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryReadResult { + public function read( WP_Agent_Memory_Scope $scope, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_Read_Result { unset( $metadata_fields ); $this->record( 'read', $scope ); if ( ! array_key_exists( $scope->key(), $this->files ) ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } $content = $this->files[ $scope->key() ]; - return new AgentMemoryReadResult( true, $content, sha1( $content ), strlen( $content ), 123 ); + return new WP_Agent_Memory_Read_Result( true, $content, sha1( $content ), strlen( $content ), 123 ); } - public function write( AgentMemoryScope $scope, string $content, ?string $if_match = null, ?AgentMemoryMetadata $metadata = null ): AgentMemoryWriteResult { + public function write( WP_Agent_Memory_Scope $scope, string $content, ?string $if_match = null, ?WP_Agent_Memory_Metadata $metadata = null ): WP_Agent_Memory_Write_Result { unset( $metadata ); $this->record( 'write', $scope ); $this->files[ $scope->key() ] = $content; - return AgentMemoryWriteResult::ok( sha1( $content ), strlen( $content ) ); + return WP_Agent_Memory_Write_Result::ok( sha1( $content ), strlen( $content ) ); } - public function exists( AgentMemoryScope $scope ): bool { + public function exists( WP_Agent_Memory_Scope $scope ): bool { $this->record( 'exists', $scope ); return array_key_exists( $scope->key(), $this->files ); } - public function delete( AgentMemoryScope $scope ): AgentMemoryWriteResult { + public function delete( WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Write_Result { $this->record( 'delete', $scope ); unset( $this->files[ $scope->key() ] ); - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } - public function list_layer( AgentMemoryScope $scope_query, ?AgentMemoryQuery $query = null ): array { + public function list_layer( WP_Agent_Memory_Scope $scope_query, ?WP_Agent_Memory_Query $query = null ): array { unset( $query ); $this->record( 'list_layer', $scope_query ); return array(); } - public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?AgentMemoryQuery $query = null ): array { + public function list_subtree( WP_Agent_Memory_Scope $scope_query, string $prefix, ?WP_Agent_Memory_Query $query = null ): array { unset( $query ); $this->record( 'list_subtree', $scope_query ); $this->list_prefixes[] = $prefix; @@ -142,13 +142,13 @@ public function list_subtree( AgentMemoryScope $scope_query, string $prefix, ?Ag continue; } - $entries[] = new AgentMemoryListEntry( $filename, $layer, strlen( $content ), 123 ); + $entries[] = new WP_Agent_Memory_List_Entry( $filename, $layer, strlen( $content ), 123 ); } return $entries; } - private function record( string $operation, AgentMemoryScope $scope ): void { + private function record( string $operation, WP_Agent_Memory_Scope $scope ): void { $this->operations[] = $operation; $this->scopes[] = $scope; } @@ -169,7 +169,7 @@ function datamachine_daily_memory_store_seam_assert( bool $condition, string $me $store = new DailyMemorySeamFakeStore(); add_filter( 'agents_api_memory_store', - function ( $default, AgentMemoryScope $scope ) use ( $store ) { + function ( $default, WP_Agent_Memory_Scope $scope ) use ( $store ) { return $store; }, 10, @@ -192,7 +192,7 @@ function ( $default, AgentMemoryScope $scope ) use ( $store ) { datamachine_daily_memory_store_seam_assert( array( '28', '29' ) === $list['months']['2026/04'], 'list_all groups daily store filenames by month' ); $filenames = array_map( - static fn( AgentMemoryScope $scope ): string => $scope->filename, + static fn( WP_Agent_Memory_Scope $scope ): string => $scope->filename, $store->scopes ); diff --git a/tests/duplicate-tool-call-mode-aware-smoke.php b/tests/duplicate-tool-call-mode-aware-smoke.php index 2b3c2b159..3667fb4a7 100644 --- a/tests/duplicate-tool-call-mode-aware-smoke.php +++ b/tests/duplicate-tool-call-mode-aware-smoke.php @@ -15,7 +15,7 @@ require_once __DIR__ . '/bootstrap-unit.php'; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; use DataMachine\Engine\AI\ConversationManager; function datamachine_dup_msg_assert( bool $condition, string $message ): void { @@ -46,7 +46,7 @@ function datamachine_dup_msg_assert( bool $condition, string $message ): void { 'Default arg renders the duplicated tool name into the error.' ); datamachine_dup_msg_assert( - AgentMessageEnvelope::TYPE_TOOL_RESULT === $default_envelope['type'], + WP_Agent_Message::TYPE_TOOL_RESULT === $default_envelope['type'], 'Duplicate-correction message is emitted as a tool_result envelope.' ); datamachine_dup_msg_assert( diff --git a/tests/guideline-agent-memory-store-smoke.php b/tests/guideline-agent-memory-store-smoke.php index 8fc754070..dfb8ebf10 100644 --- a/tests/guideline-agent-memory-store-smoke.php +++ b/tests/guideline-agent-memory-store-smoke.php @@ -121,7 +121,7 @@ function get_post_meta( int $post_id, string $key, bool $_single = false ) { datamachine_tests_require_agents_api(); require_once __DIR__ . '/../inc/Core/FilesRepository/GuidelineAgentMemoryStore.php'; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; use DataMachine\Core\FilesRepository\GuidelineAgentMemoryStore; function datamachine_guideline_memory_assert( bool $condition, string $message ): void { @@ -163,9 +163,9 @@ function datamachine_guideline_memory_assert( bool $condition, string $message ) echo "\nTest: deterministic scope key encoding\n"; -$scope = new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, 'MEMORY.md' ); -$same = new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, 'MEMORY.md' ); -$daily = new AgentMemoryScope( 'agent', 'site', 'https://example.test', 7, 42, 'daily/2026/04/17.md' ); +$scope = new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, 'MEMORY.md' ); +$same = new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, 'MEMORY.md' ); +$daily = new WP_Agent_Memory_Scope( 'agent', 'site', 'https://example.test', 7, 42, 'daily/2026/04/17.md' ); $post_name = GuidelineAgentMemoryStore::post_name_for_scope( $scope ); datamachine_guideline_memory_assert( @@ -175,7 +175,7 @@ function datamachine_guideline_memory_assert( bool $condition, string $message ) datamachine_guideline_memory_assert( 'memory-' . sha1( $scope->key() ) === $post_name, - 'post_name is memory- prefixed sha1 of AgentMemoryScope::key()' + 'post_name is memory- prefixed sha1 of WP_Agent_Memory_Scope::key()' ); datamachine_guideline_memory_assert( @@ -248,7 +248,7 @@ function datamachine_guideline_memory_assert( bool $condition, string $message ) echo "\nTest: private memory requires explicit owner capability metadata\n"; $store = new GuidelineAgentMemoryStore(); -$private_scope = new AgentMemoryScope( 'user', 'site', '1', 7, 42, 'USER.md' ); +$private_scope = new WP_Agent_Memory_Scope( 'user', 'site', '1', 7, 42, 'USER.md' ); $post_id = 200; $GLOBALS['datamachine_guideline_posts'][ $post_id ] = new WP_Post( @@ -294,7 +294,7 @@ function datamachine_guideline_memory_assert( bool $condition, string $message ) echo "\nTest: workspace-shared guidance uses workspace guideline capabilities\n"; -$shared_scope = new AgentMemoryScope( 'shared', 'site', '1', 7, 42, 'RULES.md' ); +$shared_scope = new WP_Agent_Memory_Scope( 'shared', 'site', '1', 7, 42, 'RULES.md' ); $post_id = 201; $GLOBALS['datamachine_guideline_posts'][ $post_id ] = new WP_Post( diff --git a/tests/memory-bundle-policy-smoke.php b/tests/memory-bundle-policy-smoke.php index 6184b0e8b..8dbd5fa25 100644 --- a/tests/memory-bundle-policy-smoke.php +++ b/tests/memory-bundle-policy-smoke.php @@ -193,14 +193,14 @@ function is_wp_error( $_thing ): bool { require_once __DIR__ . '/../vendor/autoload.php'; use DataMachine\Abilities\PermissionHelper; -use AgentsAPI\Core\FilesRepository\AgentMemoryListEntry; -use AgentsAPI\Core\FilesRepository\AgentMemoryMetadata; -use AgentsAPI\Core\FilesRepository\AgentMemoryQuery; -use AgentsAPI\Core\FilesRepository\AgentMemoryReadResult; -use AgentsAPI\Core\FilesRepository\AgentMemoryScope; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreCapabilities; -use AgentsAPI\Core\FilesRepository\AgentMemoryStoreInterface; -use AgentsAPI\Core\FilesRepository\AgentMemoryWriteResult; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_List_Entry; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Metadata; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Query; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Read_Result; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Scope; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store_Capabilities; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Store; +use AgentsAPI\Core\FilesRepository\WP_Agent_Memory_Write_Result; use DataMachine\Engine\AI\Actions\PendingActionStore; use DataMachine\Engine\AI\Actions\ResolvePendingActionAbility; use DataMachine\Engine\AI\Memory\MemorySectionArtifact; @@ -209,47 +209,47 @@ function is_wp_error( $_thing ): bool { use DataMachine\Engine\Bundle\AgentBundleArtifactStatus; use DataMachine\Engine\Bundle\AgentBundleManifest; -class MemoryPolicyFakeStore implements AgentMemoryStoreInterface { +class MemoryPolicyFakeStore implements WP_Agent_Memory_Store { /** @var array */ public array $files = array(); - public function capabilities(): AgentMemoryStoreCapabilities { - return AgentMemoryStoreCapabilities::none(); + public function capabilities(): WP_Agent_Memory_Store_Capabilities { + return WP_Agent_Memory_Store_Capabilities::none(); } - public function read( AgentMemoryScope $scope, array $metadata_fields = AgentMemoryMetadata::FIELDS ): AgentMemoryReadResult { + public function read( WP_Agent_Memory_Scope $scope, array $metadata_fields = WP_Agent_Memory_Metadata::FIELDS ): WP_Agent_Memory_Read_Result { unset( $metadata_fields ); if ( ! array_key_exists( $scope->key(), $this->files ) ) { - return AgentMemoryReadResult::not_found(); + return WP_Agent_Memory_Read_Result::not_found(); } $content = $this->files[ $scope->key() ]; - return new AgentMemoryReadResult( true, $content, sha1( $content ), strlen( $content ), 123 ); + return new WP_Agent_Memory_Read_Result( true, $content, sha1( $content ), strlen( $content ), 123 ); } - public function write( AgentMemoryScope $scope, string $content, ?string $_if_match = null, ?AgentMemoryMetadata $metadata = null ): AgentMemoryWriteResult { + public function write( WP_Agent_Memory_Scope $scope, string $content, ?string $_if_match = null, ?WP_Agent_Memory_Metadata $metadata = null ): WP_Agent_Memory_Write_Result { unset( $_if_match, $metadata ); $this->files[ $scope->key() ] = $content; - return AgentMemoryWriteResult::ok( sha1( $content ), strlen( $content ) ); + return WP_Agent_Memory_Write_Result::ok( sha1( $content ), strlen( $content ) ); } - public function exists( AgentMemoryScope $scope ): bool { + public function exists( WP_Agent_Memory_Scope $scope ): bool { return array_key_exists( $scope->key(), $this->files ); } - public function delete( AgentMemoryScope $scope ): AgentMemoryWriteResult { + public function delete( WP_Agent_Memory_Scope $scope ): WP_Agent_Memory_Write_Result { unset( $this->files[ $scope->key() ] ); - return AgentMemoryWriteResult::ok( '', 0 ); + return WP_Agent_Memory_Write_Result::ok( '', 0 ); } - public function list_layer( AgentMemoryScope $_scope_query, ?AgentMemoryQuery $query = null ): array { + public function list_layer( WP_Agent_Memory_Scope $_scope_query, ?WP_Agent_Memory_Query $query = null ): array { unset( $query ); unset( $_scope_query ); return array(); } - public function list_subtree( AgentMemoryScope $_scope_query, string $_prefix, ?AgentMemoryQuery $query = null ): array { + public function list_subtree( WP_Agent_Memory_Scope $_scope_query, string $_prefix, ?WP_Agent_Memory_Query $query = null ): array { unset( $query ); unset( $_scope_query, $_prefix ); return array(); @@ -269,7 +269,7 @@ function memory_policy_assert( bool $condition, string $message ): void { $store = new MemoryPolicyFakeStore(); add_filter( 'agents_api_memory_store', - static function ( $_default, AgentMemoryScope $_scope ) use ( $store ) { + static function ( $_default, WP_Agent_Memory_Scope $_scope ) use ( $store ) { unset( $_default, $_scope ); return $store; }, diff --git a/tests/pending-action-helper-approval-envelope-smoke.php b/tests/pending-action-helper-approval-envelope-smoke.php index dc5b28646..6f550370d 100644 --- a/tests/pending-action-helper-approval-envelope-smoke.php +++ b/tests/pending-action-helper-approval-envelope-smoke.php @@ -9,7 +9,7 @@ require_once __DIR__ . '/bootstrap-unit.php'; -use AgentsAPI\AI\AgentMessageEnvelope; +use AgentsAPI\AI\WP_Agent_Message; use DataMachine\Engine\AI\Actions\PendingActionHelper; use DataMachine\Engine\AI\Actions\PendingActionStore; @@ -94,8 +94,8 @@ function datamachine_pending_action_helper_assert( bool $condition, string $mess ) ); -datamachine_pending_action_helper_assert( AgentMessageEnvelope::SCHEMA === $result['schema'], 'Result uses the Agents API envelope schema.' ); -datamachine_pending_action_helper_assert( AgentMessageEnvelope::TYPE_APPROVAL_REQUIRED === $result['type'], 'Result is an approval_required envelope.' ); +datamachine_pending_action_helper_assert( WP_Agent_Message::SCHEMA === $result['schema'], 'Result uses the Agents API envelope schema.' ); +datamachine_pending_action_helper_assert( WP_Agent_Message::TYPE_APPROVAL_REQUIRED === $result['type'], 'Result is an approval_required envelope.' ); datamachine_pending_action_helper_assert( 'tool' === $result['role'], 'Approval-required envelope uses the tool role.' ); datamachine_pending_action_helper_assert( true === $result['staged'], 'Legacy top-level staged flag is preserved.' ); @@ -128,8 +128,8 @@ function datamachine_pending_action_helper_assert( bool $condition, string $mess datamachine_pending_action_helper_assert( 'wiki_upsert' === $stored['kind'], 'Stored Data Machine resolver kind is unchanged.' ); datamachine_pending_action_helper_assert( array( 'title' => 'Demo', 'content' => 'Updated content.' ) === $stored['apply_input'], 'Stored apply input is unchanged.' ); -$normalized = AgentMessageEnvelope::normalize( $result ); -datamachine_pending_action_helper_assert( AgentMessageEnvelope::TYPE_APPROVAL_REQUIRED === $normalized['type'], 'Result normalizes as an approval_required envelope.' ); +$normalized = WP_Agent_Message::normalize( $result ); +datamachine_pending_action_helper_assert( WP_Agent_Message::TYPE_APPROVAL_REQUIRED === $normalized['type'], 'Result normalizes as an approval_required envelope.' ); datamachine_pending_action_helper_assert( $result['payload'] === $normalized['payload'], 'Envelope payload survives normalization.' ); echo "PendingActionHelper approval envelope smoke passed.\n"; diff --git a/tests/pending-action-resolver-contract-smoke.php b/tests/pending-action-resolver-contract-smoke.php index 2158afb43..ed29847a9 100644 --- a/tests/pending-action-resolver-contract-smoke.php +++ b/tests/pending-action-resolver-contract-smoke.php @@ -126,10 +126,10 @@ public function get_error_message() { require_once dirname( __DIR__ ) . '/inc/Engine/AI/Actions/PendingActionResolverAdapter.php'; require_once dirname( __DIR__ ) . '/inc/Engine/AI/Actions/ResolvePendingActionAbility.php'; -use AgentsAPI\AI\Approvals\ApprovalDecision; -use AgentsAPI\AI\Approvals\PendingAction; -use AgentsAPI\AI\Approvals\PendingActionHandlerInterface; -use AgentsAPI\AI\Approvals\PendingActionResolverInterface; +use AgentsAPI\AI\Approvals\WP_Agent_Approval_Decision; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Handler; +use AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Resolver; use DataMachine\Engine\AI\Actions\PendingActionStore; use DataMachine\Engine\AI\Actions\ResolvePendingActionAbility; @@ -150,14 +150,14 @@ function resolver_smoke_assert( bool $condition, string $message, array &$failur echo "pending-action-resolver-contract-smoke\n"; $adapter = ResolvePendingActionAbility::adapter(); -resolver_smoke_assert( $adapter instanceof PendingActionResolverInterface, 'resolver adapter implements Agents API resolver contract', $failures, $passes ); +resolver_smoke_assert( $adapter instanceof WP_Agent_Pending_Action_Resolver, 'resolver adapter implements Agents API resolver contract', $failures, $passes ); $handler_calls = array(); $permission_seen = array(); -$handler = new class( $handler_calls ) implements PendingActionHandlerInterface { +$handler = new class( $handler_calls ) implements WP_Agent_Pending_Action_Handler { public function __construct( private array &$handler_calls ) {} - public function can_resolve_pending_action( PendingAction $action, ApprovalDecision $decision, array $payload = array(), array $context = array() ): bool { + public function can_resolve_pending_action( WP_Agent_Pending_Action $action, WP_Agent_Approval_Decision $decision, array $payload = array(), array $context = array() ): bool { $this->handler_calls[] = array( 'permission_action_id' => $action->get_action_id(), 'permission_decision' => $decision->value(), @@ -168,7 +168,7 @@ public function can_resolve_pending_action( PendingAction $action, ApprovalDecis return true; } - public function handle_pending_action( PendingAction $action, ApprovalDecision $decision, array $payload = array(), array $context = array() ): mixed { + public function handle_pending_action( WP_Agent_Pending_Action $action, WP_Agent_Approval_Decision $decision, array $payload = array(), array $context = array() ): mixed { $this->handler_calls[] = array( 'action_id' => $action->get_action_id(), 'decision' => $decision->value(), @@ -219,7 +219,7 @@ static function ( array $handlers ) use ( $handler, &$permission_seen ) { $accepted = $adapter->resolve_pending_action( 'act_contract_accept', - ApprovalDecision::accepted(), + WP_Agent_Approval_Decision::accepted(), 'user:123', array( 'reason' => 'looks-good' ), array( 'actor' => 'reviewer' ) @@ -227,9 +227,9 @@ static function ( array $handlers ) use ( $handler, &$permission_seen ) { resolver_smoke_assert( true === ( $accepted['success'] ?? false ), 'accepted contract resolution succeeds', $failures, $passes ); resolver_smoke_assert( 'accepted' === ( $accepted['decision'] ?? null ), 'accepted response keeps Data Machine decision string', $failures, $passes ); -resolver_smoke_assert( 'accepted' === ( $handler_calls[0]['permission_decision'] ?? null ), 'contract handler permission receives ApprovalDecision object value', $failures, $passes ); -resolver_smoke_assert( 'act_contract_accept' === ( $handler_calls[1]['action_id'] ?? null ), 'contract handler receives PendingAction value object', $failures, $passes ); -resolver_smoke_assert( 'accepted' === ( $handler_calls[1]['decision'] ?? null ), 'contract handler receives ApprovalDecision object value', $failures, $passes ); +resolver_smoke_assert( 'accepted' === ( $handler_calls[0]['permission_decision'] ?? null ), 'contract handler permission receives WP_Agent_Approval_Decision object value', $failures, $passes ); +resolver_smoke_assert( 'act_contract_accept' === ( $handler_calls[1]['action_id'] ?? null ), 'contract handler receives WP_Agent_Pending_Action value object', $failures, $passes ); +resolver_smoke_assert( 'accepted' === ( $handler_calls[1]['decision'] ?? null ), 'contract handler receives WP_Agent_Approval_Decision object value', $failures, $passes ); resolver_smoke_assert( 'looks-good' === ( $handler_calls[1]['payload']['reason'] ?? null ), 'contract handler receives resolver payload', $failures, $passes ); resolver_smoke_assert( 'reviewer' === ( $handler_calls[1]['context']['actor'] ?? null ), 'contract handler receives resolver context', $failures, $passes ); resolver_smoke_assert( empty( $permission_seen ), 'legacy can_resolve is not duplicated for Agents API handler objects', $failures, $passes ); @@ -273,15 +273,15 @@ static function ( array $handlers ) use ( &$legacy_apply_calls ) { resolver_smoke_assert( 0 === $legacy_apply_calls, 'rejected resolution does not invoke apply handler', $failures, $passes ); $denied_apply_calls = 0; -$denying_handler = new class( $denied_apply_calls ) implements PendingActionHandlerInterface { +$denying_handler = new class( $denied_apply_calls ) implements WP_Agent_Pending_Action_Handler { public function __construct( private int &$denied_apply_calls ) {} - public function can_resolve_pending_action( PendingAction $action, ApprovalDecision $decision, array $payload = array(), array $context = array() ): bool { + public function can_resolve_pending_action( WP_Agent_Pending_Action $action, WP_Agent_Approval_Decision $decision, array $payload = array(), array $context = array() ): bool { unset( $action, $decision, $payload, $context ); return false; } - public function handle_pending_action( PendingAction $action, ApprovalDecision $decision, array $payload = array(), array $context = array() ): mixed { + public function handle_pending_action( WP_Agent_Pending_Action $action, WP_Agent_Approval_Decision $decision, array $payload = array(), array $context = array() ): mixed { unset( $action, $decision, $payload, $context ); ++$this->denied_apply_calls; return array( 'success' => true ); diff --git a/tests/pending-actions-agents-api-contract-smoke.php b/tests/pending-actions-agents-api-contract-smoke.php index a0934b9ce..0059110b7 100644 --- a/tests/pending-actions-agents-api-contract-smoke.php +++ b/tests/pending-actions-agents-api-contract-smoke.php @@ -48,33 +48,33 @@ function datamachine_pending_actions_source( string $path ): string { $action_policy = datamachine_pending_actions_source( 'inc/Engine/AI/Actions/ActionPolicyResolver.php' ); $expected_agents_api_approval_primitives = array( - 'AgentsAPI\\AI\\Approvals\\PendingActionStoreInterface' => array( - 'path' => 'vendor/automattic/agents-api/src/Approvals/PendingActionStoreInterface.php', - 'type' => 'interface PendingActionStoreInterface', + 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action_Store' => array( + 'path' => 'vendor/automattic/agents-api/src/Approvals/class-wp-agent-pending-action-store.php', + 'type' => 'interface WP_Agent_Pending_Action_Store', ), - 'AgentsAPI\\AI\\Approvals\\PendingActionResolverInterface' => array( - 'path' => 'vendor/automattic/agents-api/src/Approvals/PendingActionResolverInterface.php', - 'type' => 'interface PendingActionResolverInterface', + 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action_Resolver' => array( + 'path' => 'vendor/automattic/agents-api/src/Approvals/class-wp-agent-pending-action-resolver.php', + 'type' => 'interface WP_Agent_Pending_Action_Resolver', ), - 'AgentsAPI\\AI\\Approvals\\PendingActionHandlerInterface' => array( - 'path' => 'vendor/automattic/agents-api/src/Approvals/PendingActionHandlerInterface.php', - 'type' => 'interface PendingActionHandlerInterface', + 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action_Handler' => array( + 'path' => 'vendor/automattic/agents-api/src/Approvals/class-wp-agent-pending-action-handler.php', + 'type' => 'interface WP_Agent_Pending_Action_Handler', ), - 'AgentsAPI\\AI\\Approvals\\PendingAction' => array( - 'path' => 'vendor/automattic/agents-api/src/Approvals/PendingAction.php', - 'type' => 'class PendingAction', + 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action' => array( + 'path' => 'vendor/automattic/agents-api/src/Approvals/class-wp-agent-pending-action.php', + 'type' => 'class WP_Agent_Pending_Action', ), - 'AgentsAPI\\AI\\Approvals\\PendingActionStatus' => array( - 'path' => 'vendor/automattic/agents-api/src/Approvals/PendingActionStatus.php', - 'type' => 'final class PendingActionStatus', + 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action_Status' => array( + 'path' => 'vendor/automattic/agents-api/src/Approvals/class-wp-agent-pending-action-status.php', + 'type' => 'final class WP_Agent_Pending_Action_Status', ), - 'AgentsAPI\\AI\\Approvals\\ApprovalDecision' => array( - 'path' => 'vendor/automattic/agents-api/src/Approvals/ApprovalDecision.php', - 'type' => 'final class ApprovalDecision', + 'AgentsAPI\\AI\\Approvals\\WP_Agent_Approval_Decision' => array( + 'path' => 'vendor/automattic/agents-api/src/Approvals/class-wp-agent-approval-decision.php', + 'type' => 'final class WP_Agent_Approval_Decision', ), - 'AgentsAPI\\AI\\Tools\\ActionPolicy' => array( - 'path' => 'vendor/automattic/agents-api/src/Tools/ActionPolicy.php', - 'type' => 'final class ActionPolicy', + 'AgentsAPI\\AI\\Tools\\WP_Agent_Action_Policy' => array( + 'path' => 'vendor/automattic/agents-api/src/Tools/class-wp-agent-action-policy.php', + 'type' => 'final class WP_Agent_Action_Policy', ), ); @@ -83,23 +83,23 @@ function datamachine_pending_actions_source( string $path ): string { $primitive_source = datamachine_pending_actions_source( $primitive['path'] ); datamachine_pending_actions_assert( str_contains( $primitive_source, $primitive['type'] ), $class_name . ' is available from the installed Agents API dependency', $failures, $passes ); } -datamachine_pending_actions_assert( str_contains( $store_source, 'use AgentsAPI\\AI\\Approvals\\PendingActionStoreInterface;' ), 'concrete store consumes Agents API PendingActionStoreInterface', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $store_source, 'public static function adapter(): PendingActionStoreInterface' ), 'concrete store exposes the Agents API store contract', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $adapter_source, 'implements PendingActionStoreInterface' ), 'store adapter implements Agents API PendingActionStoreInterface', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $adapter_source, 'store( PendingAction $action )' ), 'store adapter accepts Agents API PendingAction records', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $adapter_source, 'record_resolution( string $action_id, ApprovalDecision $decision, string $resolver' ), 'store adapter records Agents API resolution audit metadata', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $resolver_adapter, 'implements PendingActionResolverInterface' ), 'resolver adapter implements Agents API PendingActionResolverInterface', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $action_policy, 'use AgentsAPI\\AI\\Tools\\ActionPolicy;' ) && str_contains( $action_policy, 'ActionPolicy::normalize' ), 'ActionPolicyResolver consumes Agents API ActionPolicy vocabulary', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $resolver_adapter, 'ApprovalDecision' ), 'resolver adapter consumes Agents API ApprovalDecision vocabulary', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $store_source, 'use AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action_Store;' ), 'concrete store consumes Agents API WP_Agent_Pending_Action_Store', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $store_source, 'public static function adapter(): WP_Agent_Pending_Action_Store' ), 'concrete store exposes the Agents API store contract', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $adapter_source, 'implements WP_Agent_Pending_Action_Store' ), 'store adapter implements Agents API WP_Agent_Pending_Action_Store', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $adapter_source, 'store( WP_Agent_Pending_Action $action )' ), 'store adapter accepts Agents API WP_Agent_Pending_Action records', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $adapter_source, 'record_resolution( string $action_id, WP_Agent_Approval_Decision $decision, string $resolver' ), 'store adapter records Agents API resolution audit metadata', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $resolver_adapter, 'implements WP_Agent_Pending_Action_Resolver' ), 'resolver adapter implements Agents API WP_Agent_Pending_Action_Resolver', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $action_policy, 'use AgentsAPI\\AI\\Tools\\WP_Agent_Action_Policy;' ) && str_contains( $action_policy, 'WP_Agent_Action_Policy::normalize' ), 'ActionPolicyResolver consumes Agents API WP_Agent_Action_Policy vocabulary', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $resolver_adapter, 'WP_Agent_Approval_Decision' ), 'resolver adapter consumes Agents API WP_Agent_Approval_Decision vocabulary', $failures, $passes ); echo "\n[2] Durable storage preserves legacy lookup while retaining audit rows:\n"; datamachine_pending_actions_assert( str_contains( $store_source, 'CREATE TABLE {$table_name}' ), 'PendingActionStore creates a durable table', $failures, $passes ); datamachine_pending_actions_assert( str_contains( $store_source, 'datamachine_pending_actions' ), 'durable table uses Data Machine pending-actions table name', $failures, $passes ); datamachine_pending_actions_assert( str_contains( $store_source, 'public static function get( string $action_id, bool $include_resolved = false )' ), 'legacy get defaults to live-pending rows only', $failures, $passes ); datamachine_pending_actions_assert( str_contains( $store_source, 'public static function inspect( string $action_id ): ?array' ), 'inspect surface can fetch resolved audit rows', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $store_source, 'PendingActionStatus::normalize' ), 'lifecycle vocabulary is delegated to Agents API PendingActionStatus', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $resolver_source, 'record_resolution( $action_id, PendingActionStatus::ACCEPTED' ), 'accepted resolutions are retained instead of transient-deleted', $failures, $passes ); -datamachine_pending_actions_assert( str_contains( $resolver_source, 'record_resolution( $action_id, PendingActionStatus::REJECTED' ), 'rejected resolutions are retained instead of transient-deleted', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $store_source, 'WP_Agent_Pending_Action_Status::normalize' ), 'lifecycle vocabulary is delegated to Agents API WP_Agent_Pending_Action_Status', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $resolver_source, 'record_resolution( $action_id, WP_Agent_Pending_Action_Status::ACCEPTED' ), 'accepted resolutions are retained instead of transient-deleted', $failures, $passes ); +datamachine_pending_actions_assert( str_contains( $resolver_source, 'record_resolution( $action_id, WP_Agent_Pending_Action_Status::REJECTED' ), 'rejected resolutions are retained instead of transient-deleted', $failures, $passes ); echo "\n[3] List/get/summary surfaces are registered for agents and operators:\n"; foreach ( array( 'datamachine/list-pending-actions', 'datamachine/get-pending-action', 'datamachine/summarize-pending-actions' ) as $ability ) { @@ -181,12 +181,13 @@ function wp_generate_uuid4(): string { } require_once dirname( __DIR__ ) . '/vendor/automattic/agents-api/agents-api.php'; +require_once dirname( __DIR__ ) . '/inc/Core/Workspace/WordPressWorkspaceScope.php'; require_once dirname( __DIR__ ) . '/inc/Engine/AI/Actions/PendingActionStoreAdapter.php'; require_once dirname( __DIR__ ) . '/inc/Engine/AI/Actions/PendingActionStore.php'; $store = \DataMachine\Engine\AI\Actions\PendingActionStore::adapter(); $action_id = \DataMachine\Engine\AI\Actions\PendingActionStore::generate_id(); -$action = \AgentsAPI\AI\Approvals\PendingAction::from_array( +$action = \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action::from_array( array( 'action_id' => $action_id, 'kind' => 'contract_smoke', @@ -206,14 +207,14 @@ function wp_generate_uuid4(): string { ) ); -datamachine_pending_actions_assert( $store instanceof \AgentsAPI\AI\Approvals\PendingActionStoreInterface, 'PendingActionStore adapter is an Agents API store', $failures, $passes ); +datamachine_pending_actions_assert( $store instanceof \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Store, 'PendingActionStore adapter is an Agents API store', $failures, $passes ); datamachine_pending_actions_assert( str_starts_with( $action_id, 'act_' ), 'legacy generate_id returns namespaced action IDs', $failures, $passes ); -datamachine_pending_actions_assert( $store->store( $action ), 'contract store writes PendingAction through transient fallback', $failures, $passes ); +datamachine_pending_actions_assert( $store->store( $action ), 'contract store writes WP_Agent_Pending_Action through transient fallback', $failures, $passes ); $stored = $store->get( $action_id ); -datamachine_pending_actions_assert( $stored instanceof \AgentsAPI\AI\Approvals\PendingAction && 'contract_smoke' === $stored->get_kind(), 'contract get reads the stored PendingAction', $failures, $passes ); -datamachine_pending_actions_assert( $stored instanceof \AgentsAPI\AI\Approvals\PendingAction && $action_id === $stored->get_action_id(), 'contract store preserves action ID in payload', $failures, $passes ); -datamachine_pending_actions_assert( $stored instanceof \AgentsAPI\AI\Approvals\PendingAction && null !== $stored->get_expires_at(), 'transient fallback preserves expiration audit data', $failures, $passes ); +datamachine_pending_actions_assert( $stored instanceof \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action && 'contract_smoke' === $stored->get_kind(), 'contract get reads the stored WP_Agent_Pending_Action', $failures, $passes ); +datamachine_pending_actions_assert( $stored instanceof \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action && $action_id === $stored->get_action_id(), 'contract store preserves action ID in payload', $failures, $passes ); +datamachine_pending_actions_assert( $stored instanceof \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action && null !== $stored->get_expires_at(), 'transient fallback preserves expiration audit data', $failures, $passes ); $legacy_action_id = \DataMachine\Engine\AI\Actions\PendingActionStore::generate_id(); $legacy_payload = array( @@ -229,10 +230,10 @@ function wp_generate_uuid4(): string { datamachine_pending_actions_assert( \DataMachine\Engine\AI\Actions\PendingActionStore::store( $legacy_action_id, $legacy_payload ), 'legacy Data Machine payload arrays still read through the product payload API', $failures, $passes ); $legacy_stored = $store->get( $legacy_action_id ); -datamachine_pending_actions_assert( $legacy_stored instanceof \AgentsAPI\AI\Approvals\PendingAction, 'legacy Data Machine payload arrays normalize to PendingAction value objects', $failures, $passes ); -datamachine_pending_actions_assert( $legacy_stored instanceof \AgentsAPI\AI\Approvals\PendingAction && 'legacy_smoke' === $legacy_stored->get_kind(), 'value-object conversion preserves Data Machine handler kind', $failures, $passes ); -datamachine_pending_actions_assert( $legacy_stored instanceof \AgentsAPI\AI\Approvals\PendingAction && array( 'legacy' => true ) === $legacy_stored->get_preview(), 'value-object conversion preserves preview payload', $failures, $passes ); -datamachine_pending_actions_assert( $legacy_stored instanceof \AgentsAPI\AI\Approvals\PendingAction && array( 'legacy_value' => 2 ) === $legacy_stored->get_apply_input(), 'value-object conversion preserves apply input', $failures, $passes ); +datamachine_pending_actions_assert( $legacy_stored instanceof \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action, 'legacy Data Machine payload arrays normalize to WP_Agent_Pending_Action value objects', $failures, $passes ); +datamachine_pending_actions_assert( $legacy_stored instanceof \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action && 'legacy_smoke' === $legacy_stored->get_kind(), 'value-object conversion preserves Data Machine handler kind', $failures, $passes ); +datamachine_pending_actions_assert( $legacy_stored instanceof \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action && array( 'legacy' => true ) === $legacy_stored->get_preview(), 'value-object conversion preserves preview payload', $failures, $passes ); +datamachine_pending_actions_assert( $legacy_stored instanceof \AgentsAPI\AI\Approvals\WP_Agent_Pending_Action && array( 'legacy_value' => 2 ) === $legacy_stored->get_apply_input(), 'value-object conversion preserves apply input', $failures, $passes ); datamachine_pending_actions_assert( $store->delete( $legacy_action_id ), 'contract delete removes legacy transient fallback payload', $failures, $passes ); datamachine_pending_actions_assert( $store->delete( $action_id ), 'contract delete removes transient fallback payload', $failures, $passes ); diff --git a/tests/pipeline-tool-policy-snapshot-smoke.php b/tests/pipeline-tool-policy-snapshot-smoke.php index 7a8c19ec1..b965cb36f 100644 --- a/tests/pipeline-tool-policy-snapshot-smoke.php +++ b/tests/pipeline-tool-policy-snapshot-smoke.php @@ -61,7 +61,7 @@ function wp_generate_uuid4(): string { require_once __DIR__ . '/../inc/Core/Steps/FlowStepConfigFactory.php'; require_once __DIR__ . '/../inc/Core/Steps/WorkflowConfigFactory.php'; require_once __DIR__ . '/../inc/Core/Steps/AI/ToolPolicy/PipelineToolPolicyArgs.php'; -require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy-interface.php'; +require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy.php'; require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy-filter.php'; require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy.php'; require_once __DIR__ . '/../inc/Engine/AI/Tools/ToolManager.php'; diff --git a/tests/runtime-tool-declaration-smoke.php b/tests/runtime-tool-declaration-smoke.php index 7e2247086..9a81f28fb 100644 --- a/tests/runtime-tool-declaration-smoke.php +++ b/tests/runtime-tool-declaration-smoke.php @@ -9,7 +9,7 @@ require_once __DIR__ . '/bootstrap-unit.php'; -use AgentsAPI\AI\Tools\RuntimeToolDeclaration; +use AgentsAPI\AI\Tools\WP_Agent_Tool_Declaration; function datamachine_runtime_tool_assert( bool $condition, string $message ): void { if ( ! $condition ) { @@ -19,17 +19,17 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $assertions = 0; -$runtime_tool_file = AGENTS_API_PATH . 'inc/AI/Tools/RuntimeToolDeclaration.php'; -$legacy_tool_file = dirname( __DIR__ ) . '/inc/Engine/AI/Tools/RuntimeToolDeclaration.php'; +$runtime_tool_file = AGENTS_API_PATH . 'src/Tools/class-wp-agent-tool-declaration.php'; +$legacy_tool_file = dirname( __DIR__ ) . '/inc/Engine/AI/Tools/WP_Agent_Tool_Declaration.php'; datamachine_runtime_tool_assert( is_file( $runtime_tool_file ), - 'RuntimeToolDeclaration should live in the standalone Agents API dependency.' + 'WP_Agent_Tool_Declaration should live in the standalone Agents API dependency.' ); ++$assertions; datamachine_runtime_tool_assert( ! file_exists( $legacy_tool_file ), - 'RuntimeToolDeclaration should not exist in the Data Machine product tool tree.' + 'WP_Agent_Tool_Declaration should not exist in the Data Machine product tool tree.' ); ++$assertions; @@ -47,7 +47,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo foreach ( $forbidden_tokens as $token ) { datamachine_runtime_tool_assert( ! str_contains( $runtime_tool_source, $token ), - 'RuntimeToolDeclaration should not mention Data Machine product token: ' . $token + 'WP_Agent_Tool_Declaration should not mention Data Machine product token: ' . $token ); ++$assertions; } @@ -65,7 +65,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo 'scope' => 'run', ); -$normalized = RuntimeToolDeclaration::normalize( $valid ); +$normalized = WP_Agent_Tool_Declaration::normalize( $valid ); datamachine_runtime_tool_assert( 'client' === $normalized['source'], 'Valid client source should be derived from namespaced name.' @@ -90,7 +90,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $with_source = $valid; $with_source['source'] = 'client'; datamachine_runtime_tool_assert( - array() === RuntimeToolDeclaration::validate( $with_source ), + array() === WP_Agent_Tool_Declaration::validate( $with_source ), 'Explicit matching source should pass validation.' ); ++$assertions; @@ -98,7 +98,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $forbidden_server_source = $valid; $forbidden_server_source['name'] = 'server/select_block'; datamachine_runtime_tool_assert( - array( 'source' ) === RuntimeToolDeclaration::validate( $forbidden_server_source ), + array( 'source' ) === WP_Agent_Tool_Declaration::validate( $forbidden_server_source ), 'Unknown runtime tool source should be forbidden by default.' ); ++$assertions; @@ -106,7 +106,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $forbidden_transport_source = $valid; $forbidden_transport_source['name'] = 'mcp/select_block'; datamachine_runtime_tool_assert( - array( 'source' ) === RuntimeToolDeclaration::validate( $forbidden_transport_source ), + array( 'source' ) === WP_Agent_Tool_Declaration::validate( $forbidden_transport_source ), 'Transport-specific runtime tool source should be forbidden by default.' ); ++$assertions; @@ -114,7 +114,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $mismatched_source = $valid; $mismatched_source['source'] = 'browser'; datamachine_runtime_tool_assert( - array( 'source' ) === RuntimeToolDeclaration::validate( $mismatched_source ), + array( 'source' ) === WP_Agent_Tool_Declaration::validate( $mismatched_source ), 'Explicit source must match the name prefix.' ); ++$assertions; @@ -122,7 +122,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $missing_executor = $valid; unset( $missing_executor['executor'] ); datamachine_runtime_tool_assert( - array( 'executor' ) === RuntimeToolDeclaration::validate( $missing_executor ), + array( 'executor' ) === WP_Agent_Tool_Declaration::validate( $missing_executor ), 'Runtime tool executor is required and must be client.' ); ++$assertions; @@ -130,7 +130,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $server_executor = $valid; $server_executor['executor'] = 'server'; datamachine_runtime_tool_assert( - array( 'executor' ) === RuntimeToolDeclaration::validate( $server_executor ), + array( 'executor' ) === WP_Agent_Tool_Declaration::validate( $server_executor ), 'Server executor is forbidden until Ability-native execution lands.' ); ++$assertions; @@ -138,7 +138,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $job_scope = $valid; $job_scope['scope'] = 'job'; datamachine_runtime_tool_assert( - array( 'scope' ) === RuntimeToolDeclaration::validate( $job_scope ), + array( 'scope' ) === WP_Agent_Tool_Declaration::validate( $job_scope ), 'Only run-scoped declarations are accepted.' ); ++$assertions; @@ -146,7 +146,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $bad_parameters = $valid; $bad_parameters['parameters'] = 'not a schema'; datamachine_runtime_tool_assert( - array( 'parameters' ) === RuntimeToolDeclaration::validate( $bad_parameters ), + array( 'parameters' ) === WP_Agent_Tool_Declaration::validate( $bad_parameters ), 'Parameters must be an array schema.' ); ++$assertions; @@ -154,13 +154,13 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo $bad_name = $valid; $bad_name['name'] = 'select_block'; datamachine_runtime_tool_assert( - array( 'name', 'source' ) === RuntimeToolDeclaration::validate( $bad_name ), + array( 'name', 'source' ) === WP_Agent_Tool_Declaration::validate( $bad_name ), 'Runtime tool names must be source/tool namespaced.' ); ++$assertions; try { - RuntimeToolDeclaration::normalize( $server_executor ); + WP_Agent_Tool_Declaration::normalize( $server_executor ); throw new RuntimeException( 'normalize() should reject forbidden executors.' ); } catch ( InvalidArgumentException $e ) { datamachine_runtime_tool_assert( @@ -171,7 +171,7 @@ function datamachine_runtime_tool_assert( bool $condition, string $message ): vo } datamachine_runtime_tool_assert( - 'client/select_block' === RuntimeToolDeclaration::namespacedName( 'client', 'select_block' ), + 'client/select_block' === WP_Agent_Tool_Declaration::namespacedName( 'client', 'select_block' ), 'namespacedName() should build source/tool names.' ); ++$assertions; diff --git a/tests/tool-executor-ability-native-smoke.php b/tests/tool-executor-ability-native-smoke.php index 4baba87dd..f954af85d 100644 --- a/tests/tool-executor-ability-native-smoke.php +++ b/tests/tool-executor-ability-native-smoke.php @@ -156,7 +156,7 @@ public static function store( int $post_id, array $tool_def, int $job_id ): void use DataMachine\Engine\AI\Tools\Execution\ToolExecutionCore; use DataMachine\Engine\AI\Tools\ToolExecutor; - require_once dirname( __DIR__ ) . '/vendor/automattic/agents-api/src/Tools/ActionPolicy.php'; + require_once dirname( __DIR__ ) . '/vendor/automattic/agents-api/src/Tools/class-wp-agent-action-policy.php'; require_once dirname( __DIR__ ) . '/inc/Engine/AI/Tools/ToolParameters.php'; require_once dirname( __DIR__ ) . '/inc/Engine/AI/Tools/Execution/ToolExecutionCore.php'; require_once dirname( __DIR__ ) . '/inc/Engine/AI/Tools/ToolExecutor.php'; diff --git a/tests/tool-source-registry-smoke.php b/tests/tool-source-registry-smoke.php index 096b02dc2..bbf7e476c 100644 --- a/tests/tool-source-registry-smoke.php +++ b/tests/tool-source-registry-smoke.php @@ -74,7 +74,7 @@ public function get_registered( string $slug ) { require_once __DIR__ . '/../inc/Core/PluginSettings.php'; require_once __DIR__ . '/../inc/Core/Steps/FlowStepConfig.php'; -require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy-interface.php'; +require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy.php'; require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy-filter.php'; require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy.php'; require_once __DIR__ . '/../inc/Engine/AI/Tools/ToolManager.php'; diff --git a/tests/upsert-handler-result-handoff-smoke.php b/tests/upsert-handler-result-handoff-smoke.php index 9652118a6..a3b2dcc23 100644 --- a/tests/upsert-handler-result-handoff-smoke.php +++ b/tests/upsert-handler-result-handoff-smoke.php @@ -57,7 +57,7 @@ function get_option( string $key, $default = false ) { require_once __DIR__ . '/../inc/Core/Steps/StepTypeRegistrationTrait.php'; require_once __DIR__ . '/../inc/Core/Steps/QueueableTrait.php'; require_once __DIR__ . '/../inc/Core/Steps/FlowStepConfig.php'; -require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy-interface.php'; +require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-access-policy.php'; require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy-filter.php'; require_once __DIR__ . '/../vendor/automattic/agents-api/src/Tools/class-wp-agent-tool-policy.php'; require_once __DIR__ . '/../inc/Engine/AI/ConversationManager.php';