From 6943eea9ac356327ca7dbe2f36846335a363acb3 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 14 Apr 2026 15:03:24 -0400 Subject: [PATCH] feat: ability categories and tool-ability linkage for pipeline tool scoping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the flat 'datamachine' ability category into 18 semantic subcategories (content, media, analytics, seo, memory, taxonomy, publishing, fetch, email, pipeline, flow, jobs, agent, settings, auth, logging, system, chat). Add ability metadata to all 56 tool registerTool() calls so each tool declares which ability (or abilities) it wraps. Tools that bypass the Abilities API get explicit access_level metadata instead. Wire category-based filtering into ToolPolicyResolver — pipelines can now declare tool_categories to scope which tools the AI step receives, eliminating irrelevant tools from the context. Agent tool policies also support categories alongside individual tool names. This reduces pipeline token bloat (no more google_analytics in RSS→AI→publish pipelines) and aligns the tool surface with the Abilities API for the eventual wp-ai-client migration (#1027). Resolves phases 1-2 of #924. --- data-machine.php | 4 + inc/Abilities/AbilityCategories.php | 159 +++++++++++++++ inc/Abilities/AgentAbilities.php | 12 +- inc/Abilities/AgentMemoryAbilities.php | 8 +- inc/Abilities/AgentPing/SendPingAbility.php | 2 +- inc/Abilities/AgentTokenAbilities.php | 6 +- .../Analytics/BingWebmasterAbilities.php | 2 +- .../Analytics/GoogleAnalyticsAbilities.php | 2 +- .../GoogleSearchConsoleAbilities.php | 2 +- .../Analytics/PageSpeedAbilities.php | 2 +- inc/Abilities/AuthAbilities.php | 12 +- .../Chat/CreateChatSessionAbility.php | 2 +- .../Chat/DeleteChatSessionAbility.php | 2 +- inc/Abilities/Chat/GetChatSessionAbility.php | 2 +- .../Chat/ListChatSessionsAbility.php | 2 +- inc/Abilities/Chat/MarkSessionReadAbility.php | 2 +- inc/Abilities/Chat/SendMessageAbility.php | 2 +- .../Content/EditPostBlocksAbility.php | 3 +- .../Content/GetPostBlocksAbility.php | 3 +- .../Content/InsertContentAbility.php | 3 +- .../Content/ReplacePostBlocksAbility.php | 3 +- inc/Abilities/Content/ResolveDiffAbility.php | 2 +- inc/Abilities/DailyMemoryAbilities.php | 10 +- .../DuplicateCheck/DuplicateCheckAbility.php | 4 +- inc/Abilities/Email/EmailAbilities.php | 20 +- inc/Abilities/Engine/ExecuteStepAbility.php | 2 +- inc/Abilities/Engine/RunFlowAbility.php | 2 +- inc/Abilities/Engine/ScheduleFlowAbility.php | 2 +- .../Engine/ScheduleNextStepAbility.php | 2 +- inc/Abilities/Fetch/FetchEmailAbility.php | 2 +- inc/Abilities/Fetch/FetchFilesAbility.php | 2 +- inc/Abilities/Fetch/FetchRssAbility.php | 2 +- .../Fetch/FetchWordPressApiAbility.php | 2 +- .../Fetch/FetchWordPressMediaAbility.php | 2 +- .../Fetch/GetWordPressPostAbility.php | 2 +- .../Fetch/QueryWordPressPostsAbility.php | 2 +- inc/Abilities/File/AgentFileAbilities.php | 10 +- inc/Abilities/File/FlowFileAbilities.php | 10 +- inc/Abilities/File/ScaffoldAbilities.php | 2 +- inc/Abilities/Flow/CreateFlowAbility.php | 2 +- inc/Abilities/Flow/DeleteFlowAbility.php | 2 +- inc/Abilities/Flow/DuplicateFlowAbility.php | 2 +- inc/Abilities/Flow/GetFlowsAbility.php | 2 +- inc/Abilities/Flow/PauseFlowAbility.php | 2 +- inc/Abilities/Flow/QueueAbility.php | 14 +- inc/Abilities/Flow/ResumeFlowAbility.php | 2 +- inc/Abilities/Flow/UpdateFlowAbility.php | 2 +- inc/Abilities/Flow/WebhookTriggerAbility.php | 10 +- inc/Abilities/FlowAbilities.php | 23 --- .../FlowStep/ConfigureFlowStepsAbility.php | 2 +- .../FlowStep/GetFlowStepsAbility.php | 2 +- .../FlowStep/UpdateFlowStepAbility.php | 2 +- .../ValidateFlowStepsConfigAbility.php | 2 +- inc/Abilities/Handler/TestHandlerAbility.php | 2 +- inc/Abilities/HandlerAbilities.php | 10 +- inc/Abilities/InternalLinkingAbilities.php | 12 +- inc/Abilities/Job/DeleteJobsAbility.php | 2 +- inc/Abilities/Job/ExecuteWorkflowAbility.php | 2 +- inc/Abilities/Job/FailJobAbility.php | 2 +- inc/Abilities/Job/FlowHealthAbility.php | 2 +- inc/Abilities/Job/GetJobsAbility.php | 2 +- inc/Abilities/Job/JobsSummaryAbility.php | 2 +- inc/Abilities/Job/ProblemFlowsAbility.php | 2 +- inc/Abilities/Job/RecoverStuckJobsAbility.php | 2 +- inc/Abilities/Job/RetryJobAbility.php | 2 +- inc/Abilities/LocalSearchAbilities.php | 2 +- inc/Abilities/LogAbilities.php | 10 +- inc/Abilities/Media/AltTextAbilities.php | 4 +- .../Media/ImageGenerationAbilities.php | 2 +- .../Media/ImageOptimizationAbilities.php | 4 +- .../Media/ImageTemplateAbilities.php | 4 +- inc/Abilities/Media/MediaAbilities.php | 6 +- .../Pipeline/CreatePipelineAbility.php | 2 +- .../Pipeline/DeletePipelineAbility.php | 2 +- .../Pipeline/DuplicatePipelineAbility.php | 2 +- .../Pipeline/GetPipelinesAbility.php | 2 +- .../Pipeline/ImportExportAbility.php | 4 +- .../Pipeline/UpdatePipelineAbility.php | 2 +- inc/Abilities/PipelineStepAbilities.php | 10 +- inc/Abilities/PostQueryAbilities.php | 5 +- inc/Abilities/ProcessedItemsAbilities.php | 6 +- .../Publish/PublishWordPressAbility.php | 2 +- inc/Abilities/Publish/SendEmailAbility.php | 2 +- inc/Abilities/SEO/IndexNowAbilities.php | 8 +- .../SEO/MetaDescriptionAbilities.php | 4 +- inc/Abilities/SettingsAbilities.php | 14 +- inc/Abilities/StepTypeAbilities.php | 4 +- inc/Abilities/SystemAbilities.php | 6 +- .../Taxonomy/CreateTaxonomyTermAbility.php | 2 +- .../Taxonomy/DeleteTaxonomyTermAbility.php | 2 +- .../Taxonomy/GetTaxonomyTermsAbility.php | 2 +- inc/Abilities/Taxonomy/ResolveTermAbility.php | 2 +- .../Taxonomy/UpdateTaxonomyTermAbility.php | 2 +- .../Update/UpdateWordPressAbility.php | 2 +- inc/Api/Chat/Tools/AddPipelineStep.php | 2 +- inc/Api/Chat/Tools/ApiQuery.php | 2 +- inc/Api/Chat/Tools/AssignTaxonomyTerm.php | 2 +- inc/Api/Chat/Tools/AuthenticateHandler.php | 2 +- inc/Api/Chat/Tools/ConfigureFlowSteps.php | 2 +- inc/Api/Chat/Tools/ConfigurePipelineStep.php | 2 +- inc/Api/Chat/Tools/CopyFlow.php | 2 +- inc/Api/Chat/Tools/CreateFlow.php | 2 +- inc/Api/Chat/Tools/CreatePipeline.php | 2 +- inc/Api/Chat/Tools/CreateTaxonomyTerm.php | 2 +- inc/Api/Chat/Tools/DeleteFile.php | 2 +- inc/Api/Chat/Tools/DeleteFlow.php | 2 +- inc/Api/Chat/Tools/DeletePipeline.php | 2 +- inc/Api/Chat/Tools/DeletePipelineStep.php | 2 +- inc/Api/Chat/Tools/ExecuteWorkflowTool.php | 2 +- inc/Api/Chat/Tools/GetHandlerDefaults.php | 2 +- inc/Api/Chat/Tools/GetProblemFlows.php | 2 +- inc/Api/Chat/Tools/ListFlows.php | 2 +- inc/Api/Chat/Tools/ManageJobs.php | 2 +- inc/Api/Chat/Tools/ManageLogs.php | 2 +- inc/Api/Chat/Tools/ManageQueue.php | 2 +- inc/Api/Chat/Tools/MergeTaxonomyTerms.php | 2 +- inc/Api/Chat/Tools/ReadLogs.php | 2 +- inc/Api/Chat/Tools/ReorderPipelineSteps.php | 2 +- inc/Api/Chat/Tools/RunFlow.php | 2 +- inc/Api/Chat/Tools/SearchTaxonomyTerms.php | 2 +- inc/Api/Chat/Tools/SendPing.php | 2 +- inc/Api/Chat/Tools/SetHandlerDefaults.php | 2 +- inc/Api/Chat/Tools/SystemHealthCheck.php | 2 +- inc/Api/Chat/Tools/UpdateFlow.php | 2 +- inc/Api/Chat/Tools/UpdateTaxonomyTerm.php | 2 +- inc/Core/Steps/AI/AIStep.php | 9 + .../AI/Tools/Global/AgentDailyMemory.php | 2 +- inc/Engine/AI/Tools/Global/AgentMemory.php | 2 +- .../AI/Tools/Global/AmazonAffiliateLink.php | 2 +- inc/Engine/AI/Tools/Global/BingWebmaster.php | 2 +- .../AI/Tools/Global/GoogleAnalytics.php | 2 +- inc/Engine/AI/Tools/Global/GoogleSearch.php | 2 +- .../AI/Tools/Global/GoogleSearchConsole.php | 2 +- .../AI/Tools/Global/ImageGeneration.php | 2 +- .../AI/Tools/Global/InternalLinkAudit.php | 2 +- inc/Engine/AI/Tools/Global/LocalSearch.php | 2 +- inc/Engine/AI/Tools/Global/PageSpeed.php | 2 +- inc/Engine/AI/Tools/Global/QueueValidator.php | 2 +- inc/Engine/AI/Tools/Global/WebFetch.php | 2 +- .../AI/Tools/Global/WordPressPostReader.php | 2 +- inc/Engine/AI/Tools/ToolPolicyResolver.php | 187 ++++++++++++++++-- package-lock.json | 4 +- .../Abilities/AllAbilitiesRegisteredTest.php | 36 +++- 143 files changed, 598 insertions(+), 273 deletions(-) create mode 100644 inc/Abilities/AbilityCategories.php diff --git a/data-machine.php b/data-machine.php index 397ee12cd..2428f2d07 100644 --- a/data-machine.php +++ b/data-machine.php @@ -123,6 +123,10 @@ function () { // Agent auth callback handler (receives tokens from external DM instances). new \DataMachine\Core\Auth\AgentAuthCallback(); + // Register ability categories first — must happen before any ability registration. + require_once __DIR__ . '/inc/Abilities/AbilityCategories.php'; + \DataMachine\Abilities\AbilityCategories::ensure_registered(); + // Load abilities require_once __DIR__ . '/inc/Abilities/AuthAbilities.php'; require_once __DIR__ . '/inc/Abilities/File/FileConstants.php'; diff --git a/inc/Abilities/AbilityCategories.php b/inc/Abilities/AbilityCategories.php new file mode 100644 index 000000000..bb040db4c --- /dev/null +++ b/inc/Abilities/AbilityCategories.php @@ -0,0 +1,159 @@ + array( + 'label' => __( 'Content', 'data-machine' ), + 'description' => __( 'Content querying, editing, searching, and block operations.', 'data-machine' ), + ), + self::MEDIA => array( + 'label' => __( 'Media', 'data-machine' ), + 'description' => __( 'Image generation, alt text, media upload, validation, and optimization.', 'data-machine' ), + ), + self::ANALYTICS => array( + 'label' => __( 'Analytics', 'data-machine' ), + 'description' => __( 'Google Analytics, Search Console, Bing Webmaster, and PageSpeed.', 'data-machine' ), + ), + self::SEO => array( + 'label' => __( 'SEO', 'data-machine' ), + 'description' => __( 'Internal linking, meta descriptions, and IndexNow.', 'data-machine' ), + ), + self::MEMORY => array( + 'label' => __( 'Memory', 'data-machine' ), + 'description' => __( 'Agent memory, daily journals, and agent file management.', 'data-machine' ), + ), + self::TAXONOMY => array( + 'label' => __( 'Taxonomy', 'data-machine' ), + 'description' => __( 'Taxonomy term CRUD and resolution.', 'data-machine' ), + ), + self::PUBLISHING => array( + 'label' => __( 'Publishing', 'data-machine' ), + 'description' => __( 'WordPress publishing, email sending, and post updates.', 'data-machine' ), + ), + self::FETCH => array( + 'label' => __( 'Fetch', 'data-machine' ), + 'description' => __( 'RSS, WordPress API, media, files, and email fetching.', 'data-machine' ), + ), + self::EMAIL => array( + 'label' => __( 'Email', 'data-machine' ), + 'description' => __( 'Email management — reply, delete, move, flag, unsubscribe.', 'data-machine' ), + ), + self::PIPELINE => array( + 'label' => __( 'Pipeline', 'data-machine' ), + 'description' => __( 'Pipeline CRUD, step management, handler and step-type discovery.', 'data-machine' ), + ), + self::FLOW => array( + 'label' => __( 'Flow', 'data-machine' ), + 'description' => __( 'Flow CRUD, scheduling, queue management, and webhook triggers.', 'data-machine' ), + ), + self::JOBS => array( + 'label' => __( 'Jobs', 'data-machine' ), + 'description' => __( 'Job management, health monitoring, recovery, and workflow execution.', 'data-machine' ), + ), + self::AGENT => array( + 'label' => __( 'Agent', 'data-machine' ), + 'description' => __( 'Agent CRUD, tokens, pings, duplicate checking, and processed items.', 'data-machine' ), + ), + self::SETTINGS => array( + 'label' => __( 'Settings', 'data-machine' ), + 'description' => __( 'Plugin settings, tool configuration, and handler defaults.', 'data-machine' ), + ), + self::AUTH => array( + 'label' => __( 'Auth', 'data-machine' ), + 'description' => __( 'OAuth provider management and authentication.', 'data-machine' ), + ), + self::LOGGING => array( + 'label' => __( 'Logging', 'data-machine' ), + 'description' => __( 'Log reading, writing, clearing, and metadata.', 'data-machine' ), + ), + self::SYSTEM => array( + 'label' => __( 'System', 'data-machine' ), + 'description' => __( 'System health, session titles, and background task execution.', 'data-machine' ), + ), + self::CHAT => array( + 'label' => __( 'Chat', 'data-machine' ), + 'description' => __( 'Chat session management and messaging.', 'data-machine' ), + ), + ); + + foreach ( $categories as $slug => $args ) { + wp_register_ability_category( $slug, $args ); + } + + self::$registered = true; + } + + /** + * Ensure categories are registered. + * + * Handles timing: if the categories_init hook already fired, registers + * immediately. Otherwise hooks into it. This mirrors the pattern used + * by individual ability classes. + */ + public static function ensure_registered(): void { + if ( self::$registered ) { + return; + } + + if ( did_action( 'wp_abilities_api_categories_init' ) ) { + self::register(); + } else { + add_action( 'wp_abilities_api_categories_init', array( self::class, 'register' ) ); + } + } +} diff --git a/inc/Abilities/AgentAbilities.php b/inc/Abilities/AgentAbilities.php index ab8db38c2..9519ba8f9 100644 --- a/inc/Abilities/AgentAbilities.php +++ b/inc/Abilities/AgentAbilities.php @@ -41,7 +41,7 @@ private function registerAbilities(): void { array( 'label' => 'Rename Agent', 'description' => 'Rename an agent slug — updates database and moves filesystem directory', - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'old_slug', 'new_slug' ), @@ -78,7 +78,7 @@ private function registerAbilities(): void { array( 'label' => 'List Agents', 'description' => 'List all registered agent identities', - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'properties' => new \stdClass(), @@ -113,7 +113,7 @@ private function registerAbilities(): void { array( 'label' => 'Create Agent', 'description' => 'Create a new agent identity with filesystem directory and owner access. Admins can create agents for any user. Non-admins with create_own_agent can create one agent for themselves.', - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'agent_slug' ), @@ -160,7 +160,7 @@ private function registerAbilities(): void { array( 'label' => 'Get Agent', 'description' => 'Retrieve a single agent by slug or ID with access grants and directory info', - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -199,7 +199,7 @@ private function registerAbilities(): void { array( 'label' => 'Update Agent', 'description' => 'Update an agent\'s mutable fields (name, config, status)', - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'agent_id' ), @@ -241,7 +241,7 @@ private function registerAbilities(): void { array( 'label' => 'Delete Agent', 'description' => 'Delete an agent record and access grants, optionally removing filesystem directory', - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/AgentMemoryAbilities.php b/inc/Abilities/AgentMemoryAbilities.php index be6644cab..5ca9291f3 100644 --- a/inc/Abilities/AgentMemoryAbilities.php +++ b/inc/Abilities/AgentMemoryAbilities.php @@ -41,7 +41,7 @@ private function registerAbilities(): void { array( 'label' => 'Get Agent Memory', 'description' => 'Read agent file content — full file or a specific section. Supports any agent file (MEMORY.md, SOUL.md, USER.md, etc.).', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -89,7 +89,7 @@ private function registerAbilities(): void { array( 'label' => 'Update Agent Memory', 'description' => 'Write to a specific section of an agent file — set (replace) or append. Supports any agent file.', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -141,7 +141,7 @@ private function registerAbilities(): void { array( 'label' => 'Search Agent Memory', 'description' => 'Search across agent file content. Returns matching lines with context, grouped by section. Supports any agent file.', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'query' ), @@ -201,7 +201,7 @@ private function registerAbilities(): void { array( 'label' => 'List Agent Memory Sections', 'description' => 'List all section headers in an agent file. Supports any agent file.', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/AgentPing/SendPingAbility.php b/inc/Abilities/AgentPing/SendPingAbility.php index d6c44df17..57c76d81e 100644 --- a/inc/Abilities/AgentPing/SendPingAbility.php +++ b/inc/Abilities/AgentPing/SendPingAbility.php @@ -32,7 +32,7 @@ private function registerAbility(): void { array( 'label' => __( 'Send Ping', 'data-machine' ), 'description' => __( 'Send pipeline context to webhook endpoints. Supports multiple URLs (one per line).', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'webhook_url' ), diff --git a/inc/Abilities/AgentTokenAbilities.php b/inc/Abilities/AgentTokenAbilities.php index de36844d9..fa36aeb81 100644 --- a/inc/Abilities/AgentTokenAbilities.php +++ b/inc/Abilities/AgentTokenAbilities.php @@ -49,7 +49,7 @@ private function registerCreateToken(): void { array( 'label' => __( 'Create Agent Token', 'data-machine' ), 'description' => __( 'Create a bearer token for agent runtime authentication. The raw token is only returned once.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'agent_id' ), @@ -96,7 +96,7 @@ private function registerRevokeToken(): void { array( 'label' => __( 'Revoke Agent Token', 'data-machine' ), 'description' => __( 'Revoke (delete) an agent bearer token. The token will immediately stop working.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'agent_id', 'token_id' ), @@ -132,7 +132,7 @@ private function registerListTokens(): void { array( 'label' => __( 'List Agent Tokens', 'data-machine' ), 'description' => __( 'List all tokens for an agent. Returns metadata only (never the token value).', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'agent_id' ), diff --git a/inc/Abilities/Analytics/BingWebmasterAbilities.php b/inc/Abilities/Analytics/BingWebmasterAbilities.php index c211fcf28..c12467226 100644 --- a/inc/Abilities/Analytics/BingWebmasterAbilities.php +++ b/inc/Abilities/Analytics/BingWebmasterAbilities.php @@ -77,7 +77,7 @@ private function registerAbilities(): void { array( 'label' => 'Bing Webmaster Tools', 'description' => 'Fetch search analytics data from Bing Webmaster Tools API', - 'category' => 'datamachine', + 'category' => 'datamachine/analytics', 'input_schema' => array( 'type' => 'object', 'required' => array( 'action' ), diff --git a/inc/Abilities/Analytics/GoogleAnalyticsAbilities.php b/inc/Abilities/Analytics/GoogleAnalyticsAbilities.php index a1244eb90..0116893c4 100644 --- a/inc/Abilities/Analytics/GoogleAnalyticsAbilities.php +++ b/inc/Abilities/Analytics/GoogleAnalyticsAbilities.php @@ -123,7 +123,7 @@ private function registerAbilities(): void { array( 'label' => 'Google Analytics', 'description' => 'Fetch visitor analytics data from Google Analytics (GA4) Data API', - 'category' => 'datamachine', + 'category' => 'datamachine/analytics', 'input_schema' => array( 'type' => 'object', 'required' => array( 'action' ), diff --git a/inc/Abilities/Analytics/GoogleSearchConsoleAbilities.php b/inc/Abilities/Analytics/GoogleSearchConsoleAbilities.php index 58372c9e7..d8a36a986 100644 --- a/inc/Abilities/Analytics/GoogleSearchConsoleAbilities.php +++ b/inc/Abilities/Analytics/GoogleSearchConsoleAbilities.php @@ -80,7 +80,7 @@ private function registerAbilities(): void { array( 'label' => 'Google Search Console', 'description' => 'Fetch search analytics data from Google Search Console API', - 'category' => 'datamachine', + 'category' => 'datamachine/analytics', 'input_schema' => array( 'type' => 'object', 'required' => array( 'action' ), diff --git a/inc/Abilities/Analytics/PageSpeedAbilities.php b/inc/Abilities/Analytics/PageSpeedAbilities.php index 92f1419ca..9a1391ae9 100644 --- a/inc/Abilities/Analytics/PageSpeedAbilities.php +++ b/inc/Abilities/Analytics/PageSpeedAbilities.php @@ -87,7 +87,7 @@ private function registerAbilities(): void { array( 'label' => 'PageSpeed Insights', 'description' => 'Run Lighthouse audits via PageSpeed Insights API for performance, accessibility, SEO, and best practices scores', - 'category' => 'datamachine', + 'category' => 'datamachine/analytics', 'input_schema' => array( 'type' => 'object', 'required' => array( 'action' ), diff --git a/inc/Abilities/AuthAbilities.php b/inc/Abilities/AuthAbilities.php index 516d85a2a..7caa54aa9 100644 --- a/inc/Abilities/AuthAbilities.php +++ b/inc/Abilities/AuthAbilities.php @@ -197,7 +197,7 @@ private function registerGetAuthStatus(): void { array( 'label' => __( 'Get Auth Status', 'data-machine' ), 'description' => __( 'Get OAuth/authentication status for a handler including authorization URL if applicable.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/auth', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug' ), @@ -234,7 +234,7 @@ private function registerDisconnectAuth(): void { array( 'label' => __( 'Disconnect Auth', 'data-machine' ), 'description' => __( 'Disconnect/revoke authentication for a handler.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/auth', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug' ), @@ -266,7 +266,7 @@ private function registerSaveAuthConfig(): void { array( 'label' => __( 'Save Auth Config', 'data-machine' ), 'description' => __( 'Save authentication configuration for a handler.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/auth', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug' ), @@ -302,7 +302,7 @@ private function registerSetAuthToken(): void { array( 'label' => __( 'Set Auth Token', 'data-machine' ), 'description' => __( 'Manually set authentication token and account data for a handler. Used for migration, CI, and headless auth setup.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/auth', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug', 'account_data' ), @@ -338,7 +338,7 @@ private function registerRefreshAuth(): void { array( 'label' => __( 'Refresh Auth Token', 'data-machine' ), 'description' => __( 'Force a token refresh for an OAuth2 handler. Only works for providers that support token refresh.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/auth', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug' ), @@ -371,7 +371,7 @@ private function registerListProviders(): void { array( 'label' => __( 'List Auth Providers', 'data-machine' ), 'description' => __( 'List all registered authentication providers with status, config fields, and account details.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/auth', 'input_schema' => array( 'type' => 'object', 'properties' => array(), diff --git a/inc/Abilities/Chat/CreateChatSessionAbility.php b/inc/Abilities/Chat/CreateChatSessionAbility.php index 9ec15b74f..a5a983537 100644 --- a/inc/Abilities/Chat/CreateChatSessionAbility.php +++ b/inc/Abilities/Chat/CreateChatSessionAbility.php @@ -37,7 +37,7 @@ private function registerAbility(): void { array( 'label' => __( 'Create Chat Session', 'data-machine' ), 'description' => __( 'Create a new chat session for a user.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/chat', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Chat/DeleteChatSessionAbility.php b/inc/Abilities/Chat/DeleteChatSessionAbility.php index 82883c563..08eb6ef6a 100644 --- a/inc/Abilities/Chat/DeleteChatSessionAbility.php +++ b/inc/Abilities/Chat/DeleteChatSessionAbility.php @@ -36,7 +36,7 @@ private function registerAbility(): void { array( 'label' => __( 'Delete Chat Session', 'data-machine' ), 'description' => __( 'Delete a chat session after verifying ownership.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/chat', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Chat/GetChatSessionAbility.php b/inc/Abilities/Chat/GetChatSessionAbility.php index d1476b168..515094f69 100644 --- a/inc/Abilities/Chat/GetChatSessionAbility.php +++ b/inc/Abilities/Chat/GetChatSessionAbility.php @@ -37,7 +37,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Chat Session', 'data-machine' ), 'description' => __( 'Retrieve a chat session with conversation and metadata.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/chat', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Chat/ListChatSessionsAbility.php b/inc/Abilities/Chat/ListChatSessionsAbility.php index 25c6e962a..3ceea8371 100644 --- a/inc/Abilities/Chat/ListChatSessionsAbility.php +++ b/inc/Abilities/Chat/ListChatSessionsAbility.php @@ -36,7 +36,7 @@ private function registerAbility(): void { array( 'label' => __( 'List Chat Sessions', 'data-machine' ), 'description' => __( 'List chat sessions for a user with pagination and context filtering.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/chat', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Chat/MarkSessionReadAbility.php b/inc/Abilities/Chat/MarkSessionReadAbility.php index 81beb28f0..eaa0178f2 100644 --- a/inc/Abilities/Chat/MarkSessionReadAbility.php +++ b/inc/Abilities/Chat/MarkSessionReadAbility.php @@ -38,7 +38,7 @@ private function registerAbility(): void { array( 'label' => __( 'Mark Session Read', 'data-machine' ), 'description' => __( 'Mark a chat session as read up to the current timestamp.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/chat', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Chat/SendMessageAbility.php b/inc/Abilities/Chat/SendMessageAbility.php index 0a1d5e893..2f583aa8d 100644 --- a/inc/Abilities/Chat/SendMessageAbility.php +++ b/inc/Abilities/Chat/SendMessageAbility.php @@ -42,7 +42,7 @@ private function registerAbility(): void { array( 'label' => __( 'Send Message', 'data-machine' ), 'description' => __( 'Send a user message to an AI agent and get a response. Creates a session if needed.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/chat', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Content/EditPostBlocksAbility.php b/inc/Abilities/Content/EditPostBlocksAbility.php index 377dace87..029816626 100644 --- a/inc/Abilities/Content/EditPostBlocksAbility.php +++ b/inc/Abilities/Content/EditPostBlocksAbility.php @@ -37,7 +37,7 @@ private function registerAbility(): void { array( 'label' => __( 'Edit Post Blocks', 'data-machine' ), 'description' => __( 'Surgical find/replace within specific Gutenberg blocks by index', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/content', 'input_schema' => array( 'type' => 'object', 'required' => array( 'post_id', 'edits' ), @@ -108,6 +108,7 @@ function ( $tools ) { $tools['edit_post_blocks'] = array( '_callable' => array( self::class, 'getChatTool' ), 'contexts' => array( 'chat' ), + 'ability' => 'datamachine/edit-post-blocks', ); return $tools; } diff --git a/inc/Abilities/Content/GetPostBlocksAbility.php b/inc/Abilities/Content/GetPostBlocksAbility.php index 981e81017..a63a53a0c 100644 --- a/inc/Abilities/Content/GetPostBlocksAbility.php +++ b/inc/Abilities/Content/GetPostBlocksAbility.php @@ -37,7 +37,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Post Blocks', 'data-machine' ), 'description' => __( 'Parse a post into Gutenberg blocks with optional filtering by type or content', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/content', 'input_schema' => array( 'type' => 'object', 'required' => array( 'post_id' ), @@ -98,6 +98,7 @@ function ( $tools ) { $tools['get_post_blocks'] = array( '_callable' => array( self::class, 'getChatTool' ), 'contexts' => array( 'chat' ), + 'ability' => 'datamachine/get-post-blocks', ); return $tools; } diff --git a/inc/Abilities/Content/InsertContentAbility.php b/inc/Abilities/Content/InsertContentAbility.php index 89a0695f4..725df3e37 100644 --- a/inc/Abilities/Content/InsertContentAbility.php +++ b/inc/Abilities/Content/InsertContentAbility.php @@ -39,7 +39,7 @@ private function register_ability(): void { wp_register_ability( 'datamachine/insert-content', array( 'label' => 'Insert Content', 'description' => 'Insert new content at a specific position in a post (beginning, end, or after a paragraph).', - 'category' => 'datamachine', + 'category' => 'datamachine/content', 'input_schema' => array( 'type' => 'object', 'required' => array( 'post_id', 'content', 'position' ), @@ -94,6 +94,7 @@ function ( $tools ) { $tools['insert_content'] = array( '_callable' => array( self::class, 'getChatTool' ), 'contexts' => array( 'chat', 'pipeline', 'system', 'editor' ), + 'ability' => 'datamachine/insert-content', ); return $tools; } diff --git a/inc/Abilities/Content/ReplacePostBlocksAbility.php b/inc/Abilities/Content/ReplacePostBlocksAbility.php index 1e0fb75cd..9b56be161 100644 --- a/inc/Abilities/Content/ReplacePostBlocksAbility.php +++ b/inc/Abilities/Content/ReplacePostBlocksAbility.php @@ -36,7 +36,7 @@ private function registerAbility(): void { array( 'label' => __( 'Replace Post Blocks', 'data-machine' ), 'description' => __( 'Replace entire block content by index. Use for AI-rewritten paragraphs.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/content', 'input_schema' => array( 'type' => 'object', 'required' => array( 'post_id', 'replacements' ), @@ -103,6 +103,7 @@ function ( $tools ) { $tools['replace_post_blocks'] = array( '_callable' => array( self::class, 'getChatTool' ), 'contexts' => array( 'chat' ), + 'ability' => 'datamachine/replace-post-blocks', ); return $tools; } diff --git a/inc/Abilities/Content/ResolveDiffAbility.php b/inc/Abilities/Content/ResolveDiffAbility.php index cb8078c41..4a4b50d00 100644 --- a/inc/Abilities/Content/ResolveDiffAbility.php +++ b/inc/Abilities/Content/ResolveDiffAbility.php @@ -41,7 +41,7 @@ private function register_ability(): void { wp_register_ability( 'datamachine/resolve-diff', array( 'label' => __( 'Resolve Diff', 'data-machine' ), 'description' => __( 'Accept or reject a pending content diff.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/content', 'input_schema' => array( 'type' => 'object', 'required' => array( 'diff_id', 'decision' ), diff --git a/inc/Abilities/DailyMemoryAbilities.php b/inc/Abilities/DailyMemoryAbilities.php index 75cf3474f..68c903f0e 100644 --- a/inc/Abilities/DailyMemoryAbilities.php +++ b/inc/Abilities/DailyMemoryAbilities.php @@ -87,7 +87,7 @@ private function registerAbilities(): void { array( 'label' => 'Read Daily Memory', 'description' => 'Read a daily memory file by date. Defaults to today if no date provided.', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -122,7 +122,7 @@ private function registerAbilities(): void { array( 'label' => 'Write Daily Memory', 'description' => 'Write or append to a daily memory file. Use mode "append" to add without replacing.', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -165,7 +165,7 @@ private function registerAbilities(): void { array( 'label' => 'List Daily Memory Files', 'description' => 'List all daily memory files grouped by month', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -197,7 +197,7 @@ private function registerAbilities(): void { array( 'label' => 'Search Daily Memory', 'description' => 'Search across daily memory files with optional date range. Returns matching lines with context.', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'query' ), @@ -252,7 +252,7 @@ private function registerAbilities(): void { array( 'label' => 'Delete Daily Memory', 'description' => 'Delete a daily memory file by date.', - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/DuplicateCheck/DuplicateCheckAbility.php b/inc/Abilities/DuplicateCheck/DuplicateCheckAbility.php index 891623445..e36f0ef4d 100644 --- a/inc/Abilities/DuplicateCheck/DuplicateCheckAbility.php +++ b/inc/Abilities/DuplicateCheck/DuplicateCheckAbility.php @@ -66,7 +66,7 @@ private function registerCheckDuplicate(): void { array( 'label' => __( 'Check Duplicate', 'data-machine' ), 'description' => __( 'Check if similar content already exists as a published post, in a queue, or via extension-registered strategies. Returns match details or clear verdict.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'title' ), @@ -236,7 +236,7 @@ private function registerTitlesMatch(): void { array( 'label' => __( 'Titles Match', 'data-machine' ), 'description' => __( 'Compare two titles for semantic equivalence using the unified similarity engine. Returns match result with score and strategy.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'title1', 'title2' ), diff --git a/inc/Abilities/Email/EmailAbilities.php b/inc/Abilities/Email/EmailAbilities.php index 2802ae05c..ce04ff0bb 100644 --- a/inc/Abilities/Email/EmailAbilities.php +++ b/inc/Abilities/Email/EmailAbilities.php @@ -42,7 +42,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Reply to Email', 'data-machine' ), 'description' => __( 'Send a reply to an email, maintaining thread headers', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'to', 'subject', 'body', 'in_reply_to' ), @@ -99,7 +99,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Delete Email', 'data-machine' ), 'description' => __( 'Delete (expunge) an email by UID from the IMAP server', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'uid' ), @@ -134,7 +134,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Move Email', 'data-machine' ), 'description' => __( 'Move an email to a different IMAP folder', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'uid', 'destination' ), @@ -173,7 +173,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Flag Email', 'data-machine' ), 'description' => __( 'Set or clear IMAP flags on an email (Seen, Flagged, etc.)', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'uid', 'flag' ), @@ -217,7 +217,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Batch Move Emails', 'data-machine' ), 'description' => __( 'Move all emails matching a search to a destination folder', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'search', 'destination' ), @@ -263,7 +263,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Batch Flag Emails', 'data-machine' ), 'description' => __( 'Set or clear a flag on all emails matching a search', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'search', 'flag' ), @@ -313,7 +313,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Batch Delete Emails', 'data-machine' ), 'description' => __( 'Delete all emails matching a search', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'search' ), @@ -355,7 +355,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Unsubscribe from Email', 'data-machine' ), 'description' => __( 'Unsubscribe from a mailing list using List-Unsubscribe headers', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'uid' ), @@ -391,7 +391,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Batch Unsubscribe', 'data-machine' ), 'description' => __( 'Unsubscribe from all mailing lists matching a search', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'required' => array( 'search' ), @@ -434,7 +434,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Test Email Connection', 'data-machine' ), 'description' => __( 'Test IMAP connection with stored credentials', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/email', 'input_schema' => array( 'type' => 'object', 'properties' => new \stdClass(), diff --git a/inc/Abilities/Engine/ExecuteStepAbility.php b/inc/Abilities/Engine/ExecuteStepAbility.php index 37767fd0d..ab0fc6ab0 100644 --- a/inc/Abilities/Engine/ExecuteStepAbility.php +++ b/inc/Abilities/Engine/ExecuteStepAbility.php @@ -47,7 +47,7 @@ private function registerAbility(): void { array( 'label' => __( 'Execute Step', 'data-machine' ), 'description' => __( 'Execute a single pipeline step. Resolves config, runs the step, routes to next step or completion.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'required' => array( 'job_id', 'flow_step_id' ), diff --git a/inc/Abilities/Engine/RunFlowAbility.php b/inc/Abilities/Engine/RunFlowAbility.php index 5c39a1640..154ac3c95 100644 --- a/inc/Abilities/Engine/RunFlowAbility.php +++ b/inc/Abilities/Engine/RunFlowAbility.php @@ -40,7 +40,7 @@ private function registerAbility(): void { array( 'label' => __( 'Run Flow', 'data-machine' ), 'description' => __( 'Execute a flow immediately. Loads configs, creates job if needed, schedules first step.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), diff --git a/inc/Abilities/Engine/ScheduleFlowAbility.php b/inc/Abilities/Engine/ScheduleFlowAbility.php index 57e5b3268..114eb8be5 100644 --- a/inc/Abilities/Engine/ScheduleFlowAbility.php +++ b/inc/Abilities/Engine/ScheduleFlowAbility.php @@ -40,7 +40,7 @@ private function registerAbility(): void { array( 'label' => __( 'Schedule Flow', 'data-machine' ), 'description' => __( 'Schedule flow execution: manual (clear), one-time timestamp, or recurring interval.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id', 'interval_or_timestamp' ), diff --git a/inc/Abilities/Engine/ScheduleNextStepAbility.php b/inc/Abilities/Engine/ScheduleNextStepAbility.php index ad57516f0..3219c782f 100644 --- a/inc/Abilities/Engine/ScheduleNextStepAbility.php +++ b/inc/Abilities/Engine/ScheduleNextStepAbility.php @@ -42,7 +42,7 @@ private function registerAbility(): void { array( 'label' => __( 'Schedule Next Step', 'data-machine' ), 'description' => __( 'Store data packets and schedule the next pipeline step via Action Scheduler.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'required' => array( 'job_id', 'flow_step_id' ), diff --git a/inc/Abilities/Fetch/FetchEmailAbility.php b/inc/Abilities/Fetch/FetchEmailAbility.php index 800e90b23..9d03025fb 100644 --- a/inc/Abilities/Fetch/FetchEmailAbility.php +++ b/inc/Abilities/Fetch/FetchEmailAbility.php @@ -46,7 +46,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Fetch Emails', 'data-machine' ), 'description' => __( 'Retrieve emails from an IMAP inbox', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/fetch', 'input_schema' => array( 'type' => 'object', 'required' => array( 'imap_host', 'imap_user', 'imap_password' ), diff --git a/inc/Abilities/Fetch/FetchFilesAbility.php b/inc/Abilities/Fetch/FetchFilesAbility.php index d947f4cd5..83b4d7fe1 100644 --- a/inc/Abilities/Fetch/FetchFilesAbility.php +++ b/inc/Abilities/Fetch/FetchFilesAbility.php @@ -39,7 +39,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Fetch Files', 'data-machine' ), 'description' => __( 'Fetch files from the file repository with deduplication support', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/fetch', 'input_schema' => array( 'type' => 'object', 'required' => array( 'file_context' ), diff --git a/inc/Abilities/Fetch/FetchRssAbility.php b/inc/Abilities/Fetch/FetchRssAbility.php index 13c51f895..1d36ea7b3 100644 --- a/inc/Abilities/Fetch/FetchRssAbility.php +++ b/inc/Abilities/Fetch/FetchRssAbility.php @@ -38,7 +38,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Fetch RSS Feed', 'data-machine' ), 'description' => __( 'Fetch and parse RSS/Atom feeds with filtering', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/fetch', 'input_schema' => array( 'type' => 'object', 'required' => array( 'feed_url' ), diff --git a/inc/Abilities/Fetch/FetchWordPressApiAbility.php b/inc/Abilities/Fetch/FetchWordPressApiAbility.php index 4feae750f..5919fa032 100644 --- a/inc/Abilities/Fetch/FetchWordPressApiAbility.php +++ b/inc/Abilities/Fetch/FetchWordPressApiAbility.php @@ -39,7 +39,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Fetch WordPress REST API', 'data-machine' ), 'description' => __( 'Fetch posts from external WordPress sites via REST API', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/fetch', 'input_schema' => array( 'type' => 'object', 'required' => array( 'endpoint_url' ), diff --git a/inc/Abilities/Fetch/FetchWordPressMediaAbility.php b/inc/Abilities/Fetch/FetchWordPressMediaAbility.php index bc430fddd..1a7de2c72 100644 --- a/inc/Abilities/Fetch/FetchWordPressMediaAbility.php +++ b/inc/Abilities/Fetch/FetchWordPressMediaAbility.php @@ -38,7 +38,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Fetch WordPress Media', 'data-machine' ), 'description' => __( 'Fetch media attachments from WordPress media library with filtering', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/fetch', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Fetch/GetWordPressPostAbility.php b/inc/Abilities/Fetch/GetWordPressPostAbility.php index 85f4474a9..df9d72604 100644 --- a/inc/Abilities/Fetch/GetWordPressPostAbility.php +++ b/inc/Abilities/Fetch/GetWordPressPostAbility.php @@ -38,7 +38,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Get WordPress Post', 'data-machine' ), 'description' => __( 'Retrieve a single WordPress post by ID or URL with optional metadata', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/fetch', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Fetch/QueryWordPressPostsAbility.php b/inc/Abilities/Fetch/QueryWordPressPostsAbility.php index 5e2cb6deb..e148f6e22 100644 --- a/inc/Abilities/Fetch/QueryWordPressPostsAbility.php +++ b/inc/Abilities/Fetch/QueryWordPressPostsAbility.php @@ -38,7 +38,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Query WordPress Posts', 'data-machine' ), 'description' => __( 'Query WordPress posts with filtering for pipeline data fetching', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/fetch', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/File/AgentFileAbilities.php b/inc/Abilities/File/AgentFileAbilities.php index cd7ab6298..26785d7af 100644 --- a/inc/Abilities/File/AgentFileAbilities.php +++ b/inc/Abilities/File/AgentFileAbilities.php @@ -67,7 +67,7 @@ private function registerListAgentFiles(): void { array( 'label' => __( 'List Agent Files', 'data-machine' ), 'description' => __( 'List memory files from all layers (shared, agent, user).', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -99,7 +99,7 @@ private function registerGetAgentFile(): void { array( 'label' => __( 'Get Agent File', 'data-machine' ), 'description' => __( 'Get a single agent memory file with content.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'filename' ), @@ -136,7 +136,7 @@ private function registerWriteAgentFile(): void { array( 'label' => __( 'Write Agent File', 'data-machine' ), 'description' => __( 'Write or update content for a memory file. Layer is resolved from the registry, or can be explicitly specified.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'filename', 'content' ), @@ -183,7 +183,7 @@ private function registerDeleteAgentFile(): void { array( 'label' => __( 'Delete Agent File', 'data-machine' ), 'description' => __( 'Delete a memory file. Protected files cannot be deleted.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'filename' ), @@ -220,7 +220,7 @@ private function registerUploadAgentFile(): void { array( 'label' => __( 'Upload Agent File', 'data-machine' ), 'description' => __( 'Upload a file to a memory layer directory.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'file_data' ), diff --git a/inc/Abilities/File/FlowFileAbilities.php b/inc/Abilities/File/FlowFileAbilities.php index 9f48c4082..ee4428b50 100644 --- a/inc/Abilities/File/FlowFileAbilities.php +++ b/inc/Abilities/File/FlowFileAbilities.php @@ -69,7 +69,7 @@ private function registerListFlowFiles(): void { array( 'label' => __( 'List Flow Files', 'data-machine' ), 'description' => __( 'List uploaded files for a flow step.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_step_id' ), @@ -101,7 +101,7 @@ private function registerGetFlowFile(): void { array( 'label' => __( 'Get Flow File', 'data-machine' ), 'description' => __( 'Get metadata for a single flow file.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'filename', 'flow_step_id' ), @@ -137,7 +137,7 @@ private function registerDeleteFlowFile(): void { array( 'label' => __( 'Delete Flow File', 'data-machine' ), 'description' => __( 'Delete an uploaded file from a flow step.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'filename', 'flow_step_id' ), @@ -173,7 +173,7 @@ private function registerUploadFlowFile(): void { array( 'label' => __( 'Upload Flow File', 'data-machine' ), 'description' => __( 'Upload a file to a flow step.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'required' => array( 'file_data', 'flow_step_id' ), @@ -216,7 +216,7 @@ private function registerCleanupFlowFiles(): void { array( 'label' => __( 'Cleanup Flow Files', 'data-machine' ), 'description' => __( 'Cleanup data packets and temporary files for a job or flow.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/File/ScaffoldAbilities.php b/inc/Abilities/File/ScaffoldAbilities.php index 6f2e4988f..eeef180af 100644 --- a/inc/Abilities/File/ScaffoldAbilities.php +++ b/inc/Abilities/File/ScaffoldAbilities.php @@ -61,7 +61,7 @@ private function registerScaffoldMemoryFile(): void { array( 'label' => __( 'Scaffold Memory File', 'data-machine' ), 'description' => __( 'Create a missing memory file with default content generated from context. Never overwrites existing files. Supports registered files (USER.md, SOUL.md, etc.) and dynamic files (daily memory).', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/memory', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Flow/CreateFlowAbility.php b/inc/Abilities/Flow/CreateFlowAbility.php index ad5b0037e..433a87f05 100644 --- a/inc/Abilities/Flow/CreateFlowAbility.php +++ b/inc/Abilities/Flow/CreateFlowAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Create Flow', 'data-machine' ), 'description' => __( 'Create a new flow for a pipeline. Supports bulk mode via flows array.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Flow/DeleteFlowAbility.php b/inc/Abilities/Flow/DeleteFlowAbility.php index 4a9712744..559a220e1 100644 --- a/inc/Abilities/Flow/DeleteFlowAbility.php +++ b/inc/Abilities/Flow/DeleteFlowAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Delete Flow', 'data-machine' ), 'description' => __( 'Delete a flow and unschedule its actions.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), diff --git a/inc/Abilities/Flow/DuplicateFlowAbility.php b/inc/Abilities/Flow/DuplicateFlowAbility.php index 577ec566c..2deddfd0f 100644 --- a/inc/Abilities/Flow/DuplicateFlowAbility.php +++ b/inc/Abilities/Flow/DuplicateFlowAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Duplicate Flow', 'data-machine' ), 'description' => __( 'Duplicate a flow, optionally to a different pipeline.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'source_flow_id' ), diff --git a/inc/Abilities/Flow/GetFlowsAbility.php b/inc/Abilities/Flow/GetFlowsAbility.php index 307332873..0466a428b 100644 --- a/inc/Abilities/Flow/GetFlowsAbility.php +++ b/inc/Abilities/Flow/GetFlowsAbility.php @@ -38,7 +38,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Flows', 'data-machine' ), 'description' => __( 'Get flows with optional filtering by pipeline ID or handler slug. Supports single flow retrieval and flexible output modes.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Flow/PauseFlowAbility.php b/inc/Abilities/Flow/PauseFlowAbility.php index 577ad7149..2d3fe7319 100644 --- a/inc/Abilities/Flow/PauseFlowAbility.php +++ b/inc/Abilities/Flow/PauseFlowAbility.php @@ -42,7 +42,7 @@ private function registerAbility(): void { array( 'label' => __( 'Pause Flow', 'data-machine' ), 'description' => __( 'Pause one or more flows. Preserves schedule for later resume.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Flow/QueueAbility.php b/inc/Abilities/Flow/QueueAbility.php index bab09fee6..d35a702b6 100644 --- a/inc/Abilities/Flow/QueueAbility.php +++ b/inc/Abilities/Flow/QueueAbility.php @@ -60,7 +60,7 @@ private function registerQueueAdd(): void { array( 'label' => __( 'Add to Queue', 'data-machine' ), 'description' => __( 'Add a prompt to the flow queue.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id', 'flow_step_id', 'prompt' ), @@ -118,7 +118,7 @@ private function registerQueueList(): void { array( 'label' => __( 'List Queue', 'data-machine' ), 'description' => __( 'List all prompts in the flow queue.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id', 'flow_step_id' ), @@ -161,7 +161,7 @@ private function registerQueueClear(): void { array( 'label' => __( 'Clear Queue', 'data-machine' ), 'description' => __( 'Clear all prompts from the flow queue.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id', 'flow_step_id' ), @@ -203,7 +203,7 @@ private function registerQueueRemove(): void { array( 'label' => __( 'Remove from Queue', 'data-machine' ), 'description' => __( 'Remove a specific prompt from the queue by index.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id', 'flow_step_id', 'index' ), @@ -249,7 +249,7 @@ private function registerQueueUpdate(): void { array( 'label' => __( 'Update Queue Item', 'data-machine' ), 'description' => __( 'Update a prompt at a specific index in the flow queue.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id', 'flow_step_id', 'index', 'prompt' ), @@ -300,7 +300,7 @@ private function registerQueueMove(): void { array( 'label' => __( 'Move Queue Item', 'data-machine' ), 'description' => __( 'Move a prompt from one position to another in the queue.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id', 'flow_step_id', 'from_index', 'to_index' ), @@ -352,7 +352,7 @@ private function registerQueueSettings(): void { array( 'label' => __( 'Update Queue Settings', 'data-machine' ), 'description' => __( 'Update queue settings for a flow step.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id', 'flow_step_id', 'queue_enabled' ), diff --git a/inc/Abilities/Flow/ResumeFlowAbility.php b/inc/Abilities/Flow/ResumeFlowAbility.php index b04cfd72a..207ec11e2 100644 --- a/inc/Abilities/Flow/ResumeFlowAbility.php +++ b/inc/Abilities/Flow/ResumeFlowAbility.php @@ -41,7 +41,7 @@ private function registerAbility(): void { array( 'label' => __( 'Resume Flow', 'data-machine' ), 'description' => __( 'Resume one or more paused flows. Re-registers schedules.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Flow/UpdateFlowAbility.php b/inc/Abilities/Flow/UpdateFlowAbility.php index d62ced806..ccba4ab64 100644 --- a/inc/Abilities/Flow/UpdateFlowAbility.php +++ b/inc/Abilities/Flow/UpdateFlowAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Update Flow', 'data-machine' ), 'description' => __( 'Update flow name or scheduling.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), diff --git a/inc/Abilities/Flow/WebhookTriggerAbility.php b/inc/Abilities/Flow/WebhookTriggerAbility.php index 1d5d25dfc..82ce933f7 100644 --- a/inc/Abilities/Flow/WebhookTriggerAbility.php +++ b/inc/Abilities/Flow/WebhookTriggerAbility.php @@ -47,7 +47,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Enable Webhook Trigger', 'data-machine' ), 'description' => __( 'Enable webhook trigger for a flow and generate a Bearer token. External services can POST to the trigger URL to start flow executions.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), @@ -80,7 +80,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Disable Webhook Trigger', 'data-machine' ), 'description' => __( 'Disable webhook trigger for a flow. Revokes the token and stops accepting inbound webhooks.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), @@ -111,7 +111,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Regenerate Webhook Token', 'data-machine' ), 'description' => __( 'Regenerate the webhook trigger token for a flow. The old token is immediately invalidated.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), @@ -144,7 +144,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Configure Webhook Rate Limit', 'data-machine' ), 'description' => __( 'Set rate limiting for a flow webhook trigger. Limits the number of requests per time window.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), @@ -190,7 +190,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Webhook Trigger Status', 'data-machine' ), 'description' => __( 'Get the webhook trigger status for a flow, including URL and whether it is enabled.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), diff --git a/inc/Abilities/FlowAbilities.php b/inc/Abilities/FlowAbilities.php index 87fc7df4e..1919ff05d 100644 --- a/inc/Abilities/FlowAbilities.php +++ b/inc/Abilities/FlowAbilities.php @@ -44,8 +44,6 @@ public function __construct() { return; } - $this->registerCategory(); - $this->queue = new QueueAbility(); $this->get_flows = new GetFlowsAbility(); $this->create_flow = new CreateFlowAbility(); @@ -59,27 +57,6 @@ public function __construct() { self::$registered = true; } - /** - * Register the datamachine ability category. - */ - private function registerCategory(): void { - $category_callback = function () { - wp_register_ability_category( - 'datamachine', - array( - 'label' => __( 'Data Machine', 'data-machine' ), - 'description' => __( 'Data Machine flow and pipeline operations', 'data-machine' ), - ) - ); - }; - - if ( did_action( 'wp_abilities_api_categories_init' ) ) { - $category_callback(); - } else { - add_action( 'wp_abilities_api_categories_init', $category_callback ); - } - } - /** * Permission callback for abilities. * diff --git a/inc/Abilities/FlowStep/ConfigureFlowStepsAbility.php b/inc/Abilities/FlowStep/ConfigureFlowStepsAbility.php index 7908a3933..8a0549b34 100644 --- a/inc/Abilities/FlowStep/ConfigureFlowStepsAbility.php +++ b/inc/Abilities/FlowStep/ConfigureFlowStepsAbility.php @@ -34,7 +34,7 @@ private function registerAbility(): void { array( 'label' => __( 'Configure Flow Steps', 'data-machine' ), 'description' => __( 'Bulk configure flow steps across a pipeline or globally. Supports handler switching with field mapping.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/FlowStep/GetFlowStepsAbility.php b/inc/Abilities/FlowStep/GetFlowStepsAbility.php index 2cf2100ce..07059c778 100644 --- a/inc/Abilities/FlowStep/GetFlowStepsAbility.php +++ b/inc/Abilities/FlowStep/GetFlowStepsAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Flow Steps', 'data-machine' ), 'description' => __( 'Get all step configurations for a flow, or a single step by ID.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/FlowStep/UpdateFlowStepAbility.php b/inc/Abilities/FlowStep/UpdateFlowStepAbility.php index d8ce47841..0070bd963 100644 --- a/inc/Abilities/FlowStep/UpdateFlowStepAbility.php +++ b/inc/Abilities/FlowStep/UpdateFlowStepAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Update Flow Step', 'data-machine' ), 'description' => __( 'Update a single flow step handler configuration or user message.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_step_id' ), diff --git a/inc/Abilities/FlowStep/ValidateFlowStepsConfigAbility.php b/inc/Abilities/FlowStep/ValidateFlowStepsConfigAbility.php index d7022c6d1..1c7828e6e 100644 --- a/inc/Abilities/FlowStep/ValidateFlowStepsConfigAbility.php +++ b/inc/Abilities/FlowStep/ValidateFlowStepsConfigAbility.php @@ -34,7 +34,7 @@ private function registerAbility(): void { array( 'label' => __( 'Validate Flow Steps Config', 'data-machine' ), 'description' => __( 'Dry-run validation for configure_flow_steps operations. Validates without executing.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/flow', 'input_schema' => array( 'type' => 'object', 'required' => array( 'pipeline_id' ), diff --git a/inc/Abilities/Handler/TestHandlerAbility.php b/inc/Abilities/Handler/TestHandlerAbility.php index 97b9a588f..cfd81032d 100644 --- a/inc/Abilities/Handler/TestHandlerAbility.php +++ b/inc/Abilities/Handler/TestHandlerAbility.php @@ -42,7 +42,7 @@ private function registerAbility(): void { array( 'label' => __( 'Test Handler', 'data-machine' ), 'description' => __( 'Dry-run any fetch handler with a config and return packet summaries.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/HandlerAbilities.php b/inc/Abilities/HandlerAbilities.php index 14e895b97..daf4d437f 100644 --- a/inc/Abilities/HandlerAbilities.php +++ b/inc/Abilities/HandlerAbilities.php @@ -98,7 +98,7 @@ private function registerGetHandlersAbility(): void { array( 'label' => __( 'Get Handlers', 'data-machine' ), 'description' => __( 'Get all registered handlers, optionally filtered by step type, or a single handler by slug.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -135,7 +135,7 @@ private function registerValidateHandlerAbility(): void { array( 'label' => __( 'Validate Handler', 'data-machine' ), 'description' => __( 'Validate that a handler slug exists.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug' ), @@ -171,7 +171,7 @@ private function registerGetHandlerConfigFieldsAbility(): void { array( 'label' => __( 'Get Handler Config Fields', 'data-machine' ), 'description' => __( 'Get configuration field definitions for a handler.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug' ), @@ -204,7 +204,7 @@ private function registerApplyHandlerDefaultsAbility(): void { array( 'label' => __( 'Apply Handler Defaults', 'data-machine' ), 'description' => __( 'Apply default values to handler configuration.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug', 'config' ), @@ -240,7 +240,7 @@ private function registerGetHandlerSiteDefaultsAbility(): void { array( 'label' => __( 'Get Handler Site Defaults', 'data-machine' ), 'description' => __( 'Get site-wide handler default configurations.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/InternalLinkingAbilities.php b/inc/Abilities/InternalLinkingAbilities.php index 15eb6fcf1..9b57dd9e1 100644 --- a/inc/Abilities/InternalLinkingAbilities.php +++ b/inc/Abilities/InternalLinkingAbilities.php @@ -59,7 +59,7 @@ private function registerAbilities(): void { array( 'label' => 'Internal Linking', 'description' => 'Queue system agent insertion of semantic internal links into posts', - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -113,7 +113,7 @@ private function registerAbilities(): void { array( 'label' => 'Diagnose Internal Links', 'description' => 'Report internal link coverage across published posts', - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array(), @@ -143,7 +143,7 @@ private function registerAbilities(): void { array( 'label' => 'Audit Internal Links', 'description' => 'Scan post content for internal links, build a link graph, and cache results. Does NOT check for broken links — use datamachine/check-broken-links for that.', - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -199,7 +199,7 @@ private function registerAbilities(): void { array( 'label' => 'Get Orphaned Posts', 'description' => 'Return posts with zero inbound internal links from the cached link graph. Runs audit automatically if no cache exists.', - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -239,7 +239,7 @@ private function registerAbilities(): void { array( 'label' => 'Check Broken Links', 'description' => 'HTTP HEAD check links from the cached link graph to find broken URLs. Supports internal, external, or all links via scope. External checks include per-domain rate limiting and HEAD→GET fallback.', - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -291,7 +291,7 @@ private function registerAbilities(): void { array( 'label' => 'Link Opportunities', 'description' => 'Rank internal linking opportunities by combining GSC traffic data with the link graph. High-traffic pages with few inbound links score highest.', - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Job/DeleteJobsAbility.php b/inc/Abilities/Job/DeleteJobsAbility.php index 4c317f840..8c13e3624 100644 --- a/inc/Abilities/Job/DeleteJobsAbility.php +++ b/inc/Abilities/Job/DeleteJobsAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Delete Jobs', 'data-machine' ), 'description' => __( 'Delete jobs by type (all or failed). Optionally cleanup processed items tracking.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'required' => array( 'type' ), diff --git a/inc/Abilities/Job/ExecuteWorkflowAbility.php b/inc/Abilities/Job/ExecuteWorkflowAbility.php index a61080b38..905c0f509 100644 --- a/inc/Abilities/Job/ExecuteWorkflowAbility.php +++ b/inc/Abilities/Job/ExecuteWorkflowAbility.php @@ -36,7 +36,7 @@ private function registerAbility(): void { array( 'label' => __( 'Execute Workflow', 'data-machine' ), 'description' => __( 'Execute an ephemeral workflow from raw JSON steps. For database flow execution, use datamachine/run-flow.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'required' => array( 'workflow' ), diff --git a/inc/Abilities/Job/FailJobAbility.php b/inc/Abilities/Job/FailJobAbility.php index 51e69b1a2..7e081ca93 100644 --- a/inc/Abilities/Job/FailJobAbility.php +++ b/inc/Abilities/Job/FailJobAbility.php @@ -36,7 +36,7 @@ private function registerAbility(): void { array( 'label' => __( 'Fail Job', 'data-machine' ), 'description' => __( 'Manually fail a processing job with an optional reason.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Job/FlowHealthAbility.php b/inc/Abilities/Job/FlowHealthAbility.php index f7619ef05..c048f0517 100644 --- a/inc/Abilities/Job/FlowHealthAbility.php +++ b/inc/Abilities/Job/FlowHealthAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Flow Health', 'data-machine' ), 'description' => __( 'Get health metrics for a flow including consecutive failures and no-items counts.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_id' ), diff --git a/inc/Abilities/Job/GetJobsAbility.php b/inc/Abilities/Job/GetJobsAbility.php index 08f53d794..b8610759d 100644 --- a/inc/Abilities/Job/GetJobsAbility.php +++ b/inc/Abilities/Job/GetJobsAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Jobs', 'data-machine' ), 'description' => __( 'List jobs with optional filtering by flow_id, pipeline_id, or status. Supports pagination, sorting, and single job lookup via job_id.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Job/JobsSummaryAbility.php b/inc/Abilities/Job/JobsSummaryAbility.php index a4c9e89cd..3f154a6ac 100644 --- a/inc/Abilities/Job/JobsSummaryAbility.php +++ b/inc/Abilities/Job/JobsSummaryAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Jobs Summary', 'data-machine' ), 'description' => __( 'Get job counts grouped by base status. Compound statuses are normalized to their base status.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'properties' => array(), diff --git a/inc/Abilities/Job/ProblemFlowsAbility.php b/inc/Abilities/Job/ProblemFlowsAbility.php index b70259b08..47a9283d8 100644 --- a/inc/Abilities/Job/ProblemFlowsAbility.php +++ b/inc/Abilities/Job/ProblemFlowsAbility.php @@ -41,7 +41,7 @@ private function registerAbility(): void { __( 'Identify flows with issues: consecutive failures (broken) or consecutive no-items runs (source exhausted). Default threshold: %d.', 'data-machine' ), $default_threshold ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Job/RecoverStuckJobsAbility.php b/inc/Abilities/Job/RecoverStuckJobsAbility.php index 9ed4195b7..95c5e6aff 100644 --- a/inc/Abilities/Job/RecoverStuckJobsAbility.php +++ b/inc/Abilities/Job/RecoverStuckJobsAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Recover Stuck Jobs', 'data-machine' ), 'description' => __( 'Recover jobs stuck in processing state: jobs with status override in engine_data, and jobs exceeding timeout threshold.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Job/RetryJobAbility.php b/inc/Abilities/Job/RetryJobAbility.php index f1eca1bb1..3a449791e 100644 --- a/inc/Abilities/Job/RetryJobAbility.php +++ b/inc/Abilities/Job/RetryJobAbility.php @@ -36,7 +36,7 @@ private function registerAbility(): void { array( 'label' => __( 'Retry Job', 'data-machine' ), 'description' => __( 'Retry a failed or stuck job by marking it failed and optionally requeuing its prompt.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/jobs', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/LocalSearchAbilities.php b/inc/Abilities/LocalSearchAbilities.php index cb8236526..46af3e8a5 100644 --- a/inc/Abilities/LocalSearchAbilities.php +++ b/inc/Abilities/LocalSearchAbilities.php @@ -42,7 +42,7 @@ private function registerAbility(): void { array( 'label' => __( 'Local Search', 'data-machine' ), 'description' => __( 'Search WordPress site for posts by title or content', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/content', 'input_schema' => array( 'type' => 'object', 'required' => array( 'query' ), diff --git a/inc/Abilities/LogAbilities.php b/inc/Abilities/LogAbilities.php index 0a3905cc6..09694f7f5 100644 --- a/inc/Abilities/LogAbilities.php +++ b/inc/Abilities/LogAbilities.php @@ -39,7 +39,7 @@ private function registerAbilities(): void { array( 'label' => 'Write to Data Machine Logs', 'description' => 'Write log entries to the database', - 'category' => 'datamachine', + 'category' => 'datamachine/logging', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -77,7 +77,7 @@ private function registerAbilities(): void { array( 'label' => 'Clear Data Machine Logs', 'description' => 'Clear log entries for a specific agent or all logs', - 'category' => 'datamachine', + 'category' => 'datamachine/logging', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -106,7 +106,7 @@ private function registerAbilities(): void { array( 'label' => 'Read Data Machine Logs', 'description' => 'Read log entries with filtering and pagination', - 'category' => 'datamachine', + 'category' => 'datamachine/logging', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -174,7 +174,7 @@ private function registerAbilities(): void { array( 'label' => 'Get Log Metadata', 'description' => 'Get log entry counts and time range', - 'category' => 'datamachine', + 'category' => 'datamachine/logging', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -205,7 +205,7 @@ private function registerAbilities(): void { array( 'label' => 'Read WordPress Debug Log', 'description' => 'Read PHP debug.log entries from wp-content/debug.log', - 'category' => 'datamachine', + 'category' => 'datamachine/logging', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Media/AltTextAbilities.php b/inc/Abilities/Media/AltTextAbilities.php index 4dd06e2af..a6350e82b 100644 --- a/inc/Abilities/Media/AltTextAbilities.php +++ b/inc/Abilities/Media/AltTextAbilities.php @@ -44,7 +44,7 @@ private function registerAbilities(): void { array( 'label' => 'Generate Alt Text', 'description' => 'Queue system agent generation of alt text for images', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -87,7 +87,7 @@ private function registerAbilities(): void { array( 'label' => 'Diagnose Alt Text', 'description' => 'Report alt text coverage for image attachments', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'properties' => array(), diff --git a/inc/Abilities/Media/ImageGenerationAbilities.php b/inc/Abilities/Media/ImageGenerationAbilities.php index e372a9a45..39e36b1f8 100644 --- a/inc/Abilities/Media/ImageGenerationAbilities.php +++ b/inc/Abilities/Media/ImageGenerationAbilities.php @@ -75,7 +75,7 @@ private function registerAbilities(): void { array( 'label' => 'Generate Image', 'description' => 'Generate an image using AI models via Replicate API', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'required' => array( 'prompt' ), diff --git a/inc/Abilities/Media/ImageOptimizationAbilities.php b/inc/Abilities/Media/ImageOptimizationAbilities.php index 6e534d2a9..16c6d71d1 100644 --- a/inc/Abilities/Media/ImageOptimizationAbilities.php +++ b/inc/Abilities/Media/ImageOptimizationAbilities.php @@ -51,7 +51,7 @@ private function registerAbilities(): void { array( 'label' => 'Diagnose Images', 'description' => 'Scan the media library for oversized images, missing WebP variants, and missing thumbnail sizes.', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -93,7 +93,7 @@ private function registerAbilities(): void { array( 'label' => 'Optimize Images', 'description' => 'Compress oversized images and generate WebP variants. Uses WordPress image editor (Imagick/GD). Batch-aware.', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Media/ImageTemplateAbilities.php b/inc/Abilities/Media/ImageTemplateAbilities.php index 581d69b7d..5f4937f81 100644 --- a/inc/Abilities/Media/ImageTemplateAbilities.php +++ b/inc/Abilities/Media/ImageTemplateAbilities.php @@ -38,7 +38,7 @@ private function registerAbilities(): void { array( 'label' => 'Render Image Template', 'description' => 'Generate branded graphics from registered GD templates', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'required' => array( 'template_id', 'data' ), @@ -90,7 +90,7 @@ private function registerAbilities(): void { array( 'label' => 'List Image Templates', 'description' => 'List all registered image generation templates', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'properties' => array(), diff --git a/inc/Abilities/Media/MediaAbilities.php b/inc/Abilities/Media/MediaAbilities.php index e11c511ed..d818cfc34 100644 --- a/inc/Abilities/Media/MediaAbilities.php +++ b/inc/Abilities/Media/MediaAbilities.php @@ -67,7 +67,7 @@ private function registerUploadMedia(): void { array( 'label' => 'Upload Media', 'description' => 'Upload or fetch a media file (image or video), store it in the repository, and return a reference (path, URL, or media ID). Automatically detects media type from MIME.', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -127,7 +127,7 @@ private function registerValidateMedia(): void { array( 'label' => 'Validate Media', 'description' => 'Validate a media file (image or video) against platform-specific constraints (duration, size, codec, aspect ratio, resolution). Auto-detects media type.', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'required' => array( 'path' ), @@ -175,7 +175,7 @@ private function registerVideoMetadata(): void { array( 'label' => 'Video Metadata', 'description' => 'Extract video metadata (duration, resolution, codec, bitrate, framerate) using ffprobe with graceful degradation', - 'category' => 'datamachine', + 'category' => 'datamachine/media', 'input_schema' => array( 'type' => 'object', 'required' => array( 'path' ), diff --git a/inc/Abilities/Pipeline/CreatePipelineAbility.php b/inc/Abilities/Pipeline/CreatePipelineAbility.php index 0ccab015c..1e61c9792 100644 --- a/inc/Abilities/Pipeline/CreatePipelineAbility.php +++ b/inc/Abilities/Pipeline/CreatePipelineAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Create Pipeline', 'data-machine' ), 'description' => __( 'Create a new pipeline with optional steps. Pass flow_config to also create a flow; omit it for pipeline-only creation. Supports bulk mode via pipelines array.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Pipeline/DeletePipelineAbility.php b/inc/Abilities/Pipeline/DeletePipelineAbility.php index a8331e0f3..796ba2be5 100644 --- a/inc/Abilities/Pipeline/DeletePipelineAbility.php +++ b/inc/Abilities/Pipeline/DeletePipelineAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Delete Pipeline', 'data-machine' ), 'description' => __( 'Delete a pipeline and all associated flows.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'pipeline_id' ), diff --git a/inc/Abilities/Pipeline/DuplicatePipelineAbility.php b/inc/Abilities/Pipeline/DuplicatePipelineAbility.php index 874f285dd..20df9e6df 100644 --- a/inc/Abilities/Pipeline/DuplicatePipelineAbility.php +++ b/inc/Abilities/Pipeline/DuplicatePipelineAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Duplicate Pipeline', 'data-machine' ), 'description' => __( 'Duplicate a pipeline with all its flows.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'pipeline_id' ), diff --git a/inc/Abilities/Pipeline/GetPipelinesAbility.php b/inc/Abilities/Pipeline/GetPipelinesAbility.php index 26b62496a..334eff386 100644 --- a/inc/Abilities/Pipeline/GetPipelinesAbility.php +++ b/inc/Abilities/Pipeline/GetPipelinesAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Pipelines', 'data-machine' ), 'description' => __( 'Get pipelines with optional pagination and filtering, or a single pipeline by ID.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Pipeline/ImportExportAbility.php b/inc/Abilities/Pipeline/ImportExportAbility.php index b66030103..08128c2bc 100644 --- a/inc/Abilities/Pipeline/ImportExportAbility.php +++ b/inc/Abilities/Pipeline/ImportExportAbility.php @@ -47,7 +47,7 @@ private function registerImportAbility(): void { array( 'label' => __( 'Import Pipelines', 'data-machine' ), 'description' => __( 'Import pipelines from CSV data.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'data' ), @@ -87,7 +87,7 @@ private function registerExportAbility(): void { array( 'label' => __( 'Export Pipelines', 'data-machine' ), 'description' => __( 'Export pipelines to CSV format.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Pipeline/UpdatePipelineAbility.php b/inc/Abilities/Pipeline/UpdatePipelineAbility.php index 1b268d097..115577a57 100644 --- a/inc/Abilities/Pipeline/UpdatePipelineAbility.php +++ b/inc/Abilities/Pipeline/UpdatePipelineAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Update Pipeline', 'data-machine' ), 'description' => __( 'Update pipeline name or configuration.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'pipeline_id' ), diff --git a/inc/Abilities/PipelineStepAbilities.php b/inc/Abilities/PipelineStepAbilities.php index da83a9f2a..15310d43b 100644 --- a/inc/Abilities/PipelineStepAbilities.php +++ b/inc/Abilities/PipelineStepAbilities.php @@ -62,7 +62,7 @@ private function registerGetPipelineStepsAbility(): void { array( 'label' => __( 'Get Pipeline Steps', 'data-machine' ), 'description' => __( 'Get all steps for a pipeline, or a single step by ID.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -103,7 +103,7 @@ private function registerAddPipelineStepAbility(): void { array( 'label' => __( 'Add Pipeline Step', 'data-machine' ), 'description' => __( 'Add a step to a pipeline. Automatically syncs to all flows on that pipeline.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'pipeline_id', 'step_type' ), @@ -149,7 +149,7 @@ private function registerUpdatePipelineStepAbility(): void { array( 'label' => __( 'Update Pipeline Step', 'data-machine' ), 'description' => __( 'Update pipeline step configuration (system prompt, disabled tools). Model/provider are configured via context_models setting.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'pipeline_step_id' ), @@ -195,7 +195,7 @@ private function registerDeletePipelineStepAbility(): void { array( 'label' => __( 'Delete Pipeline Step', 'data-machine' ), 'description' => __( 'Remove a step from a pipeline. Removes step from all flows on the pipeline.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'pipeline_id', 'pipeline_step_id' ), @@ -234,7 +234,7 @@ private function registerReorderPipelineStepsAbility(): void { array( 'label' => __( 'Reorder Pipeline Steps', 'data-machine' ), 'description' => __( 'Reorder steps within a pipeline.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'pipeline_id', 'step_order' ), diff --git a/inc/Abilities/PostQueryAbilities.php b/inc/Abilities/PostQueryAbilities.php index 4c404a71a..bc96b678a 100644 --- a/inc/Abilities/PostQueryAbilities.php +++ b/inc/Abilities/PostQueryAbilities.php @@ -60,7 +60,7 @@ private function registerAbility(): void { array( 'label' => __( 'Query Posts', 'data-machine' ), 'description' => __( 'Find posts created by Data Machine, filtered by handler, flow, or pipeline', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/content', 'input_schema' => array( 'type' => 'object', 'required' => array( 'filter_by', 'filter_value' ), @@ -116,7 +116,7 @@ private function registerAbility(): void { array( 'label' => __( 'List Posts', 'data-machine' ), 'description' => __( 'List Data Machine posts with combinable filters (handler, flow, pipeline)', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/content', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -193,6 +193,7 @@ function ( $tools ) { $tools['query_posts'] = array( '_callable' => array( $this, 'getQueryPostsTool' ), 'contexts' => array( 'chat' ), + 'ability' => 'datamachine/query-posts', ); return $tools; } diff --git a/inc/Abilities/ProcessedItemsAbilities.php b/inc/Abilities/ProcessedItemsAbilities.php index 01c90c9dd..a6696f065 100644 --- a/inc/Abilities/ProcessedItemsAbilities.php +++ b/inc/Abilities/ProcessedItemsAbilities.php @@ -62,7 +62,7 @@ private function registerClearProcessedItems(): void { array( 'label' => __( 'Clear Processed Items', 'data-machine' ), 'description' => __( 'Clear processed items tracking by pipeline or flow scope. Used to reset deduplication so items can be re-processed.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'clear_type', 'target_id' ), @@ -103,7 +103,7 @@ private function registerCheckProcessedItem(): void { array( 'label' => __( 'Check Processed Item', 'data-machine' ), 'description' => __( 'Check if a specific item has already been processed for a given flow step and source type.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_step_id', 'source_type', 'item_identifier' ), @@ -146,7 +146,7 @@ private function registerHasProcessedHistory(): void { array( 'label' => __( 'Has Processed History', 'data-machine' ), 'description' => __( 'Check if a flow step has any processed items history. Useful for distinguishing "no new items" from "first run with nothing".', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/agent', 'input_schema' => array( 'type' => 'object', 'required' => array( 'flow_step_id' ), diff --git a/inc/Abilities/Publish/PublishWordPressAbility.php b/inc/Abilities/Publish/PublishWordPressAbility.php index 7c5fe8c6f..1a8cad3e1 100644 --- a/inc/Abilities/Publish/PublishWordPressAbility.php +++ b/inc/Abilities/Publish/PublishWordPressAbility.php @@ -40,7 +40,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Publish WordPress Post', 'data-machine' ), 'description' => __( 'Create WordPress posts with taxonomy assignment and featured images', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/publishing', 'input_schema' => array( 'type' => 'object', 'required' => array( 'title', 'content', 'post_type' ), diff --git a/inc/Abilities/Publish/SendEmailAbility.php b/inc/Abilities/Publish/SendEmailAbility.php index 86998bfc9..210e59f3b 100644 --- a/inc/Abilities/Publish/SendEmailAbility.php +++ b/inc/Abilities/Publish/SendEmailAbility.php @@ -43,7 +43,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Send Email', 'data-machine' ), 'description' => __( 'Send an email with optional attachments via wp_mail()', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/publishing', 'input_schema' => array( 'type' => 'object', 'required' => array( 'to', 'subject', 'body' ), diff --git a/inc/Abilities/SEO/IndexNowAbilities.php b/inc/Abilities/SEO/IndexNowAbilities.php index 5adfb7034..2b045be9e 100644 --- a/inc/Abilities/SEO/IndexNowAbilities.php +++ b/inc/Abilities/SEO/IndexNowAbilities.php @@ -418,7 +418,7 @@ private function registerSubmitAbility(): void { array( 'label' => __( 'IndexNow Submit', 'data-machine' ), 'description' => __( 'Submit one or more URLs to IndexNow for instant search engine indexing.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'required' => array( 'urls' ), @@ -458,7 +458,7 @@ private function registerStatusAbility(): void { array( 'label' => __( 'IndexNow Status', 'data-machine' ), 'description' => __( 'Get IndexNow integration status including enabled state and API key.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array(), @@ -492,7 +492,7 @@ private function registerGenerateKeyAbility(): void { array( 'label' => __( 'IndexNow Generate Key', 'data-machine' ), 'description' => __( 'Generate a new IndexNow API key and save it to settings.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array(), @@ -524,7 +524,7 @@ private function registerVerifyKeyAbility(): void { array( 'label' => __( 'IndexNow Verify Key', 'data-machine' ), 'description' => __( 'Verify that the IndexNow key file is accessible and correct.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array(), diff --git a/inc/Abilities/SEO/MetaDescriptionAbilities.php b/inc/Abilities/SEO/MetaDescriptionAbilities.php index 8abe0dec0..3dfc536d2 100644 --- a/inc/Abilities/SEO/MetaDescriptionAbilities.php +++ b/inc/Abilities/SEO/MetaDescriptionAbilities.php @@ -44,7 +44,7 @@ private function registerAbilities(): void { array( 'label' => 'Generate Meta Description', 'description' => 'Queue system agent generation of meta descriptions (saved to post excerpt)', - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -93,7 +93,7 @@ private function registerAbilities(): void { array( 'label' => 'Diagnose Meta Descriptions', 'description' => 'Report post excerpt (meta description) coverage for posts', - 'category' => 'datamachine', + 'category' => 'datamachine/seo', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/SettingsAbilities.php b/inc/Abilities/SettingsAbilities.php index 06e43e515..c3ba3e6c9 100644 --- a/inc/Abilities/SettingsAbilities.php +++ b/inc/Abilities/SettingsAbilities.php @@ -63,7 +63,7 @@ private function registerGetSettings(): void { array( 'label' => __( 'Get Settings', 'data-machine' ), 'description' => __( 'Get all plugin settings including AI settings, enabled tools, and masked API keys.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/settings', 'input_schema' => array( 'type' => 'object', 'properties' => array(), @@ -92,7 +92,7 @@ private function registerUpdateSettings(): void { array( 'label' => __( 'Update Settings', 'data-machine' ), 'description' => __( 'Partial update of plugin settings. Only provided fields are updated.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/settings', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -156,7 +156,7 @@ private function registerGetSchedulingIntervals(): void { array( 'label' => __( 'Get Scheduling Intervals', 'data-machine' ), 'description' => __( 'Get available scheduling intervals for flow scheduling.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/settings', 'input_schema' => array( 'type' => 'object', 'properties' => array(), @@ -182,7 +182,7 @@ private function registerGetToolConfig(): void { array( 'label' => __( 'Get Tool Config', 'data-machine' ), 'description' => __( 'Get configuration for a specific tool including fields and current config values.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/settings', 'input_schema' => array( 'type' => 'object', 'required' => array( 'tool_id' ), @@ -220,7 +220,7 @@ private function registerSaveToolConfig(): void { array( 'label' => __( 'Save Tool Config', 'data-machine' ), 'description' => __( 'Save configuration for a specific tool. Fires tool-specific handler via action hook.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/settings', 'input_schema' => array( 'type' => 'object', 'required' => array( 'tool_id', 'config_data' ), @@ -257,7 +257,7 @@ private function registerGetHandlerDefaults(): void { array( 'label' => __( 'Get Handler Defaults', 'data-machine' ), 'description' => __( 'Get all handler defaults grouped by step type.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/settings', 'input_schema' => array( 'type' => 'object', 'properties' => array(), @@ -283,7 +283,7 @@ private function registerUpdateHandlerDefaults(): void { array( 'label' => __( 'Update Handler Defaults', 'data-machine' ), 'description' => __( 'Update defaults for a specific handler.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/settings', 'input_schema' => array( 'type' => 'object', 'required' => array( 'handler_slug', 'defaults' ), diff --git a/inc/Abilities/StepTypeAbilities.php b/inc/Abilities/StepTypeAbilities.php index a0778ef55..a4000c5a5 100644 --- a/inc/Abilities/StepTypeAbilities.php +++ b/inc/Abilities/StepTypeAbilities.php @@ -66,7 +66,7 @@ private function registerGetStepTypesAbility(): void { array( 'label' => __( 'Get Step Types', 'data-machine' ), 'description' => __( 'Get all registered step types, or a single step type by slug.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -98,7 +98,7 @@ private function registerValidateStepTypeAbility(): void { array( 'label' => __( 'Validate Step Type', 'data-machine' ), 'description' => __( 'Validate that a step type slug exists.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/pipeline', 'input_schema' => array( 'type' => 'object', 'required' => array( 'step_type' ), diff --git a/inc/Abilities/SystemAbilities.php b/inc/Abilities/SystemAbilities.php index 5938dddc6..351646df6 100644 --- a/inc/Abilities/SystemAbilities.php +++ b/inc/Abilities/SystemAbilities.php @@ -60,7 +60,7 @@ private function registerSessionTitleAbility(): void { array( 'label' => 'Generate Session Title', 'description' => 'Generate an AI-powered title for a chat session based on conversation content', - 'category' => 'datamachine', + 'category' => 'datamachine/system', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -102,7 +102,7 @@ private function registerHealthCheckAbility(): void { array( 'label' => __( 'System Health Check', 'data-machine' ), 'description' => __( 'Unified health diagnostics for Data Machine and extensions', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/system', 'input_schema' => array( 'type' => 'object', 'properties' => array( @@ -299,7 +299,7 @@ private function registerRunTaskAbility(): void { array( 'label' => __( 'Run System Task', 'data-machine' ), 'description' => __( 'Manually trigger a registered system task for immediate execution.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/system', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Taxonomy/CreateTaxonomyTermAbility.php b/inc/Abilities/Taxonomy/CreateTaxonomyTermAbility.php index 1c43ed666..65f280386 100644 --- a/inc/Abilities/Taxonomy/CreateTaxonomyTermAbility.php +++ b/inc/Abilities/Taxonomy/CreateTaxonomyTermAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Create Taxonomy Term', 'data-machine' ), 'description' => __( 'Create a new taxonomy term. The term will be created if it does not already exist.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/taxonomy', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Taxonomy/DeleteTaxonomyTermAbility.php b/inc/Abilities/Taxonomy/DeleteTaxonomyTermAbility.php index ec352f480..e357aa659 100644 --- a/inc/Abilities/Taxonomy/DeleteTaxonomyTermAbility.php +++ b/inc/Abilities/Taxonomy/DeleteTaxonomyTermAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Delete Taxonomy Term', 'data-machine' ), 'description' => __( 'Delete an existing taxonomy term. Optionally reassign posts to another term.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/taxonomy', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Taxonomy/GetTaxonomyTermsAbility.php b/inc/Abilities/Taxonomy/GetTaxonomyTermsAbility.php index b928f391e..be98943f8 100644 --- a/inc/Abilities/Taxonomy/GetTaxonomyTermsAbility.php +++ b/inc/Abilities/Taxonomy/GetTaxonomyTermsAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Get Taxonomy Terms', 'data-machine' ), 'description' => __( 'Retrieve taxonomy terms with optional filtering by taxonomy, search, and pagination.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/taxonomy', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Taxonomy/ResolveTermAbility.php b/inc/Abilities/Taxonomy/ResolveTermAbility.php index 841a5aab1..db08c4f46 100644 --- a/inc/Abilities/Taxonomy/ResolveTermAbility.php +++ b/inc/Abilities/Taxonomy/ResolveTermAbility.php @@ -35,7 +35,7 @@ private function registerAbility(): void { array( 'label' => __( 'Resolve Term', 'data-machine' ), 'description' => __( 'Find or create a taxonomy term by ID, name, or slug. Single source of truth for term resolution.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/taxonomy', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Taxonomy/UpdateTaxonomyTermAbility.php b/inc/Abilities/Taxonomy/UpdateTaxonomyTermAbility.php index c872cab4d..5807ff7b6 100644 --- a/inc/Abilities/Taxonomy/UpdateTaxonomyTermAbility.php +++ b/inc/Abilities/Taxonomy/UpdateTaxonomyTermAbility.php @@ -33,7 +33,7 @@ private function registerAbility(): void { array( 'label' => __( 'Update Taxonomy Term', 'data-machine' ), 'description' => __( 'Update an existing taxonomy term. Supports updating name, slug, description, and parent.', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/taxonomy', 'input_schema' => array( 'type' => 'object', 'properties' => array( diff --git a/inc/Abilities/Update/UpdateWordPressAbility.php b/inc/Abilities/Update/UpdateWordPressAbility.php index 0337fb4ac..84348f72e 100644 --- a/inc/Abilities/Update/UpdateWordPressAbility.php +++ b/inc/Abilities/Update/UpdateWordPressAbility.php @@ -44,7 +44,7 @@ private function registerAbilities(): void { array( 'label' => __( 'Update WordPress Post', 'data-machine' ), 'description' => __( 'Update WordPress posts with surgical text edits, block-level edits, or full content replacement', 'data-machine' ), - 'category' => 'datamachine', + 'category' => 'datamachine/publishing', 'input_schema' => array( 'type' => 'object', 'required' => array( 'source_url' ), diff --git a/inc/Api/Chat/Tools/AddPipelineStep.php b/inc/Api/Chat/Tools/AddPipelineStep.php index 150ed6490..bb40b22e3 100644 --- a/inc/Api/Chat/Tools/AddPipelineStep.php +++ b/inc/Api/Chat/Tools/AddPipelineStep.php @@ -21,7 +21,7 @@ class AddPipelineStep extends BaseTool { public function __construct() { - $this->registerTool( 'add_pipeline_step', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'add_pipeline_step', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/add-pipeline-step' ) ); } private static function getValidStepTypes(): array { diff --git a/inc/Api/Chat/Tools/ApiQuery.php b/inc/Api/Chat/Tools/ApiQuery.php index b2508053d..2415c2a22 100644 --- a/inc/Api/Chat/Tools/ApiQuery.php +++ b/inc/Api/Chat/Tools/ApiQuery.php @@ -24,7 +24,7 @@ class ApiQuery extends BaseTool { public function __construct() { - $this->registerTool( 'api_query', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'api_query', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'access_level' => 'editor' ) ); } /** diff --git a/inc/Api/Chat/Tools/AssignTaxonomyTerm.php b/inc/Api/Chat/Tools/AssignTaxonomyTerm.php index b1e84ee55..6d9c395f5 100644 --- a/inc/Api/Chat/Tools/AssignTaxonomyTerm.php +++ b/inc/Api/Chat/Tools/AssignTaxonomyTerm.php @@ -21,7 +21,7 @@ class AssignTaxonomyTerm extends BaseTool { public function __construct() { - $this->registerTool( 'assign_taxonomy_term', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'assign_taxonomy_term', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'access_level' => 'editor' ) ); } public function getToolDefinition(): array { diff --git a/inc/Api/Chat/Tools/AuthenticateHandler.php b/inc/Api/Chat/Tools/AuthenticateHandler.php index 956312176..adbfe361c 100644 --- a/inc/Api/Chat/Tools/AuthenticateHandler.php +++ b/inc/Api/Chat/Tools/AuthenticateHandler.php @@ -23,7 +23,7 @@ class AuthenticateHandler extends BaseTool { public function __construct() { - $this->registerTool( 'authenticate_handler', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'authenticate_handler', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'abilities' => array( 'datamachine/get-handlers', 'datamachine/get-auth-status', 'datamachine/save-auth-config', 'datamachine/disconnect-auth' ) ) ); } /** diff --git a/inc/Api/Chat/Tools/ConfigureFlowSteps.php b/inc/Api/Chat/Tools/ConfigureFlowSteps.php index 05372415d..f3e3c58ae 100644 --- a/inc/Api/Chat/Tools/ConfigureFlowSteps.php +++ b/inc/Api/Chat/Tools/ConfigureFlowSteps.php @@ -21,7 +21,7 @@ class ConfigureFlowSteps extends BaseTool { public function __construct() { - $this->registerTool( 'configure_flow_steps', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'configure_flow_steps', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'abilities' => array( 'datamachine/update-flow-step', 'datamachine/validate-handler', 'datamachine/configure-flow-steps', 'datamachine/validate-flow-steps-config' ) ) ); } /** diff --git a/inc/Api/Chat/Tools/ConfigurePipelineStep.php b/inc/Api/Chat/Tools/ConfigurePipelineStep.php index 7a2a4f08d..64bc4a6de 100644 --- a/inc/Api/Chat/Tools/ConfigurePipelineStep.php +++ b/inc/Api/Chat/Tools/ConfigurePipelineStep.php @@ -20,7 +20,7 @@ class ConfigurePipelineStep extends BaseTool { public function __construct() { - $this->registerTool( 'configure_pipeline_step', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'configure_pipeline_step', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/update-pipeline-step' ) ); } /** diff --git a/inc/Api/Chat/Tools/CopyFlow.php b/inc/Api/Chat/Tools/CopyFlow.php index ecd31de9c..4fd99e457 100644 --- a/inc/Api/Chat/Tools/CopyFlow.php +++ b/inc/Api/Chat/Tools/CopyFlow.php @@ -21,7 +21,7 @@ class CopyFlow extends BaseTool { public function __construct() { - $this->registerTool( 'copy_flow', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'copy_flow', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/duplicate-flow' ) ); } /** diff --git a/inc/Api/Chat/Tools/CreateFlow.php b/inc/Api/Chat/Tools/CreateFlow.php index 0f6f59d98..8b3cad00c 100644 --- a/inc/Api/Chat/Tools/CreateFlow.php +++ b/inc/Api/Chat/Tools/CreateFlow.php @@ -21,7 +21,7 @@ class CreateFlow extends BaseTool { public function __construct() { - $this->registerTool( 'create_flow', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'create_flow', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'abilities' => array( 'datamachine/create-flow', 'datamachine/update-flow-step' ) ) ); } /** diff --git a/inc/Api/Chat/Tools/CreatePipeline.php b/inc/Api/Chat/Tools/CreatePipeline.php index ec4ca2b54..77262fb76 100644 --- a/inc/Api/Chat/Tools/CreatePipeline.php +++ b/inc/Api/Chat/Tools/CreatePipeline.php @@ -21,7 +21,7 @@ class CreatePipeline extends BaseTool { public function __construct() { - $this->registerTool( 'create_pipeline', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'create_pipeline', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/create-pipeline' ) ); } private static function getValidStepTypes(): array { diff --git a/inc/Api/Chat/Tools/CreateTaxonomyTerm.php b/inc/Api/Chat/Tools/CreateTaxonomyTerm.php index 029949297..1fd709537 100644 --- a/inc/Api/Chat/Tools/CreateTaxonomyTerm.php +++ b/inc/Api/Chat/Tools/CreateTaxonomyTerm.php @@ -21,7 +21,7 @@ class CreateTaxonomyTerm extends BaseTool { public function __construct() { - $this->registerTool( 'create_taxonomy_term', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'create_taxonomy_term', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'access_level' => 'editor' ) ); } public function getToolDefinition(): array { diff --git a/inc/Api/Chat/Tools/DeleteFile.php b/inc/Api/Chat/Tools/DeleteFile.php index 951a409d4..5260f6afe 100644 --- a/inc/Api/Chat/Tools/DeleteFile.php +++ b/inc/Api/Chat/Tools/DeleteFile.php @@ -19,7 +19,7 @@ class DeleteFile extends BaseTool { public function __construct() { - $this->registerTool( 'delete_file', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'delete_file', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/delete-flow-file' ) ); } /** diff --git a/inc/Api/Chat/Tools/DeleteFlow.php b/inc/Api/Chat/Tools/DeleteFlow.php index 0470cfec2..48b7a0075 100644 --- a/inc/Api/Chat/Tools/DeleteFlow.php +++ b/inc/Api/Chat/Tools/DeleteFlow.php @@ -18,7 +18,7 @@ class DeleteFlow extends BaseTool { public function __construct() { - $this->registerTool( 'delete_flow', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'delete_flow', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/delete-flow' ) ); } /** diff --git a/inc/Api/Chat/Tools/DeletePipeline.php b/inc/Api/Chat/Tools/DeletePipeline.php index 0e6602317..35dc8c09c 100644 --- a/inc/Api/Chat/Tools/DeletePipeline.php +++ b/inc/Api/Chat/Tools/DeletePipeline.php @@ -19,7 +19,7 @@ class DeletePipeline extends BaseTool { public function __construct() { - $this->registerTool( 'delete_pipeline', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'delete_pipeline', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/delete-pipeline' ) ); } /** diff --git a/inc/Api/Chat/Tools/DeletePipelineStep.php b/inc/Api/Chat/Tools/DeletePipelineStep.php index 453b425fa..f614b0d59 100644 --- a/inc/Api/Chat/Tools/DeletePipelineStep.php +++ b/inc/Api/Chat/Tools/DeletePipelineStep.php @@ -19,7 +19,7 @@ class DeletePipelineStep extends BaseTool { public function __construct() { - $this->registerTool( 'delete_pipeline_step', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'delete_pipeline_step', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/delete-pipeline-step' ) ); } /** diff --git a/inc/Api/Chat/Tools/ExecuteWorkflowTool.php b/inc/Api/Chat/Tools/ExecuteWorkflowTool.php index d0b4f1a5e..388dfc2d1 100644 --- a/inc/Api/Chat/Tools/ExecuteWorkflowTool.php +++ b/inc/Api/Chat/Tools/ExecuteWorkflowTool.php @@ -20,7 +20,7 @@ class ExecuteWorkflowTool extends BaseTool { public function __construct() { - $this->registerTool( 'execute_workflow', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'execute_workflow', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/execute-workflow' ) ); } /** diff --git a/inc/Api/Chat/Tools/GetHandlerDefaults.php b/inc/Api/Chat/Tools/GetHandlerDefaults.php index 6fe246328..d0adf6818 100644 --- a/inc/Api/Chat/Tools/GetHandlerDefaults.php +++ b/inc/Api/Chat/Tools/GetHandlerDefaults.php @@ -19,7 +19,7 @@ class GetHandlerDefaults extends BaseTool { public function __construct() { - $this->registerTool( 'get_handler_defaults', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'get_handler_defaults', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'abilities' => array( 'datamachine/get-handler-site-defaults', 'datamachine/get-handlers', 'datamachine/get-handler-config-fields' ) ) ); } public function getToolDefinition(): array { diff --git a/inc/Api/Chat/Tools/GetProblemFlows.php b/inc/Api/Chat/Tools/GetProblemFlows.php index 2f6b950c7..280902edd 100644 --- a/inc/Api/Chat/Tools/GetProblemFlows.php +++ b/inc/Api/Chat/Tools/GetProblemFlows.php @@ -24,7 +24,7 @@ class GetProblemFlows extends BaseTool { public function __construct() { - $this->registerTool( 'get_problem_flows', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'get_problem_flows', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/get-problem-flows' ) ); } /** diff --git a/inc/Api/Chat/Tools/ListFlows.php b/inc/Api/Chat/Tools/ListFlows.php index d5ef44bc8..76f253c7b 100644 --- a/inc/Api/Chat/Tools/ListFlows.php +++ b/inc/Api/Chat/Tools/ListFlows.php @@ -17,7 +17,7 @@ class ListFlows extends BaseTool { public function __construct() { - $this->registerTool( 'list_flows', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'list_flows', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/get-flows' ) ); } public function getToolDefinition(): array { diff --git a/inc/Api/Chat/Tools/ManageJobs.php b/inc/Api/Chat/Tools/ManageJobs.php index 44f7212c0..30377ba1d 100644 --- a/inc/Api/Chat/Tools/ManageJobs.php +++ b/inc/Api/Chat/Tools/ManageJobs.php @@ -19,7 +19,7 @@ class ManageJobs extends BaseTool { public function __construct() { - $this->registerTool( 'manage_jobs', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'manage_jobs', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'abilities' => array( 'datamachine/get-jobs', 'datamachine/get-jobs-summary', 'datamachine/delete-jobs', 'datamachine/fail-job', 'datamachine/retry-job', 'datamachine/recover-stuck-jobs' ) ) ); } /** diff --git a/inc/Api/Chat/Tools/ManageLogs.php b/inc/Api/Chat/Tools/ManageLogs.php index a14afc116..a5301ee80 100644 --- a/inc/Api/Chat/Tools/ManageLogs.php +++ b/inc/Api/Chat/Tools/ManageLogs.php @@ -20,7 +20,7 @@ class ManageLogs extends BaseTool { public function __construct() { - $this->registerTool( 'manage_logs', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'manage_logs', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'abilities' => array( 'datamachine/clear-logs', 'datamachine/get-log-metadata' ) ) ); } /** diff --git a/inc/Api/Chat/Tools/ManageQueue.php b/inc/Api/Chat/Tools/ManageQueue.php index 1a05dd898..fa7752529 100644 --- a/inc/Api/Chat/Tools/ManageQueue.php +++ b/inc/Api/Chat/Tools/ManageQueue.php @@ -20,7 +20,7 @@ class ManageQueue extends BaseTool { public function __construct() { - $this->registerTool( 'manage_queue', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'manage_queue', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'abilities' => array( 'datamachine/queue-add', 'datamachine/queue-list', 'datamachine/queue-clear', 'datamachine/queue-remove', 'datamachine/queue-update', 'datamachine/queue-move', 'datamachine/queue-settings' ) ) ); } /** diff --git a/inc/Api/Chat/Tools/MergeTaxonomyTerms.php b/inc/Api/Chat/Tools/MergeTaxonomyTerms.php index eda57ae89..735d9c093 100644 --- a/inc/Api/Chat/Tools/MergeTaxonomyTerms.php +++ b/inc/Api/Chat/Tools/MergeTaxonomyTerms.php @@ -20,7 +20,7 @@ class MergeTaxonomyTerms extends BaseTool { public function __construct() { - $this->registerTool( 'merge_taxonomy_terms', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'merge_taxonomy_terms', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'access_level' => 'editor' ) ); } public function getToolDefinition(): array { diff --git a/inc/Api/Chat/Tools/ReadLogs.php b/inc/Api/Chat/Tools/ReadLogs.php index a0857e479..2e5538252 100644 --- a/inc/Api/Chat/Tools/ReadLogs.php +++ b/inc/Api/Chat/Tools/ReadLogs.php @@ -20,7 +20,7 @@ class ReadLogs extends BaseTool { public function __construct() { - $this->registerTool( 'read_logs', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'read_logs', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/read-logs' ) ); } /** diff --git a/inc/Api/Chat/Tools/ReorderPipelineSteps.php b/inc/Api/Chat/Tools/ReorderPipelineSteps.php index 9c6eabbf6..d9d223b6f 100644 --- a/inc/Api/Chat/Tools/ReorderPipelineSteps.php +++ b/inc/Api/Chat/Tools/ReorderPipelineSteps.php @@ -19,7 +19,7 @@ class ReorderPipelineSteps extends BaseTool { public function __construct() { - $this->registerTool( 'reorder_pipeline_steps', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'reorder_pipeline_steps', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/reorder-pipeline-steps' ) ); } /** diff --git a/inc/Api/Chat/Tools/RunFlow.php b/inc/Api/Chat/Tools/RunFlow.php index 93bcaa241..c8b7f74f6 100644 --- a/inc/Api/Chat/Tools/RunFlow.php +++ b/inc/Api/Chat/Tools/RunFlow.php @@ -20,7 +20,7 @@ class RunFlow extends BaseTool { public function __construct() { - $this->registerTool( 'run_flow', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'run_flow', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'abilities' => array( 'datamachine/run-flow', 'datamachine/schedule-flow' ) ) ); } /** diff --git a/inc/Api/Chat/Tools/SearchTaxonomyTerms.php b/inc/Api/Chat/Tools/SearchTaxonomyTerms.php index 7e4858783..6aafb9f5c 100644 --- a/inc/Api/Chat/Tools/SearchTaxonomyTerms.php +++ b/inc/Api/Chat/Tools/SearchTaxonomyTerms.php @@ -20,7 +20,7 @@ class SearchTaxonomyTerms extends BaseTool { public function __construct() { - $this->registerTool( 'search_taxonomy_terms', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'search_taxonomy_terms', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'access_level' => 'editor' ) ); } public function getToolDefinition(): array { diff --git a/inc/Api/Chat/Tools/SendPing.php b/inc/Api/Chat/Tools/SendPing.php index 281f53984..1ed7b126d 100644 --- a/inc/Api/Chat/Tools/SendPing.php +++ b/inc/Api/Chat/Tools/SendPing.php @@ -19,7 +19,7 @@ class SendPing extends BaseTool { public function __construct() { - $this->registerTool( 'send_ping', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'send_ping', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/send-ping' ) ); } /** diff --git a/inc/Api/Chat/Tools/SetHandlerDefaults.php b/inc/Api/Chat/Tools/SetHandlerDefaults.php index c784bb25f..9a0ef6e4d 100644 --- a/inc/Api/Chat/Tools/SetHandlerDefaults.php +++ b/inc/Api/Chat/Tools/SetHandlerDefaults.php @@ -20,7 +20,7 @@ class SetHandlerDefaults extends BaseTool { public function __construct() { - $this->registerTool( 'set_handler_defaults', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'set_handler_defaults', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/update-handler-defaults' ) ); } public function getToolDefinition(): array { diff --git a/inc/Api/Chat/Tools/SystemHealthCheck.php b/inc/Api/Chat/Tools/SystemHealthCheck.php index e3a7e5fe9..99971653d 100644 --- a/inc/Api/Chat/Tools/SystemHealthCheck.php +++ b/inc/Api/Chat/Tools/SystemHealthCheck.php @@ -19,7 +19,7 @@ class SystemHealthCheck extends BaseTool { public function __construct() { - $this->registerTool( 'system_health_check', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'system_health_check', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/system-health-check' ) ); } /** diff --git a/inc/Api/Chat/Tools/UpdateFlow.php b/inc/Api/Chat/Tools/UpdateFlow.php index 10bae89c2..73bb0060a 100644 --- a/inc/Api/Chat/Tools/UpdateFlow.php +++ b/inc/Api/Chat/Tools/UpdateFlow.php @@ -18,7 +18,7 @@ class UpdateFlow extends BaseTool { public function __construct() { - $this->registerTool( 'update_flow', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'update_flow', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'ability' => 'datamachine/update-flow' ) ); } /** diff --git a/inc/Api/Chat/Tools/UpdateTaxonomyTerm.php b/inc/Api/Chat/Tools/UpdateTaxonomyTerm.php index 9ebd2820a..4454ec6fc 100644 --- a/inc/Api/Chat/Tools/UpdateTaxonomyTerm.php +++ b/inc/Api/Chat/Tools/UpdateTaxonomyTerm.php @@ -20,7 +20,7 @@ class UpdateTaxonomyTerm extends BaseTool { public function __construct() { - $this->registerTool( 'update_taxonomy_term', array( $this, 'getToolDefinition' ), array( 'chat' ) ); + $this->registerTool( 'update_taxonomy_term', array( $this, 'getToolDefinition' ), array( 'chat' ), array( 'access_level' => 'editor' ) ); } public function getToolDefinition(): array { diff --git a/inc/Core/Steps/AI/AIStep.php b/inc/Core/Steps/AI/AIStep.php index afa689013..b8d4076e1 100644 --- a/inc/Core/Steps/AI/AIStep.php +++ b/inc/Core/Steps/AI/AIStep.php @@ -252,6 +252,14 @@ protected function executeStep(): array { } $engine_data = $this->engine->all(); + + // Tool categories can be specified at the pipeline step level or pipeline level. + // This allows pipelines to declare which ability categories are relevant, + // reducing tool bloat by excluding irrelevant tools from the AI context. + $tool_categories = $pipeline_step_config['tool_categories'] + ?? $this->engine->get( 'pipeline_tool_categories' ) + ?? array(); + $resolver = new ToolPolicyResolver(); $available_tools = $resolver->resolve( array( 'context' => ToolPolicyResolver::CONTEXT_PIPELINE, @@ -260,6 +268,7 @@ protected function executeStep(): array { 'next_step_config' => $next_step_config, 'pipeline_step_id' => $pipeline_step_id, 'engine_data' => $engine_data, + 'categories' => $tool_categories, ) ); // Model/provider resolved exclusively via context system — pipeline config is ignored. diff --git a/inc/Engine/AI/Tools/Global/AgentDailyMemory.php b/inc/Engine/AI/Tools/Global/AgentDailyMemory.php index 21d9d9265..4dec8acaf 100644 --- a/inc/Engine/AI/Tools/Global/AgentDailyMemory.php +++ b/inc/Engine/AI/Tools/Global/AgentDailyMemory.php @@ -21,7 +21,7 @@ class AgentDailyMemory extends BaseTool { public function __construct() { - $this->registerTool( 'agent_daily_memory', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'agent_daily_memory', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'abilities' => array( 'datamachine/daily-memory-read', 'datamachine/daily-memory-write', 'datamachine/daily-memory-list', 'datamachine/search-daily-memory' ) ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/AgentMemory.php b/inc/Engine/AI/Tools/Global/AgentMemory.php index 43312d349..c312fc980 100644 --- a/inc/Engine/AI/Tools/Global/AgentMemory.php +++ b/inc/Engine/AI/Tools/Global/AgentMemory.php @@ -21,7 +21,7 @@ class AgentMemory extends BaseTool { public function __construct() { - $this->registerTool( 'agent_memory', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'agent_memory', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'abilities' => array( 'datamachine/get-agent-memory', 'datamachine/update-agent-memory', 'datamachine/list-agent-memory-sections' ) ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/AmazonAffiliateLink.php b/inc/Engine/AI/Tools/Global/AmazonAffiliateLink.php index 138fb84c4..ac19182ac 100644 --- a/inc/Engine/AI/Tools/Global/AmazonAffiliateLink.php +++ b/inc/Engine/AI/Tools/Global/AmazonAffiliateLink.php @@ -84,7 +84,7 @@ class AmazonAffiliateLink extends BaseTool { */ public function __construct() { $this->registerConfigurationHandlers( 'amazon_affiliate_link' ); - $this->registerTool( 'amazon_affiliate_link', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'amazon_affiliate_link', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'access_level' => 'admin' ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/BingWebmaster.php b/inc/Engine/AI/Tools/Global/BingWebmaster.php index 2eddeadcb..ccac73d96 100644 --- a/inc/Engine/AI/Tools/Global/BingWebmaster.php +++ b/inc/Engine/AI/Tools/Global/BingWebmaster.php @@ -19,7 +19,7 @@ class BingWebmaster extends BaseTool { public function __construct() { $this->registerConfigurationHandlers( 'bing_webmaster' ); - $this->registerTool( 'bing_webmaster', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'bing_webmaster', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'ability' => 'datamachine/bing-webmaster' ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/GoogleAnalytics.php b/inc/Engine/AI/Tools/Global/GoogleAnalytics.php index 6928472cc..5fb9a5307 100644 --- a/inc/Engine/AI/Tools/Global/GoogleAnalytics.php +++ b/inc/Engine/AI/Tools/Global/GoogleAnalytics.php @@ -20,7 +20,7 @@ class GoogleAnalytics extends BaseTool { public function __construct() { $this->registerConfigurationHandlers( 'google_analytics' ); - $this->registerTool( 'google_analytics', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'google_analytics', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'ability' => 'datamachine/google-analytics' ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/GoogleSearch.php b/inc/Engine/AI/Tools/Global/GoogleSearch.php index 9e10c71bc..0cd7cfeaf 100644 --- a/inc/Engine/AI/Tools/Global/GoogleSearch.php +++ b/inc/Engine/AI/Tools/Global/GoogleSearch.php @@ -16,7 +16,7 @@ class GoogleSearch extends BaseTool { public function __construct() { $this->registerConfigurationHandlers( 'google_search' ); - $this->registerTool( 'google_search', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'google_search', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'access_level' => 'admin' ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/GoogleSearchConsole.php b/inc/Engine/AI/Tools/Global/GoogleSearchConsole.php index 836328c06..48b94c0e4 100644 --- a/inc/Engine/AI/Tools/Global/GoogleSearchConsole.php +++ b/inc/Engine/AI/Tools/Global/GoogleSearchConsole.php @@ -19,7 +19,7 @@ class GoogleSearchConsole extends BaseTool { public function __construct() { $this->registerConfigurationHandlers( 'google_search_console' ); - $this->registerTool( 'google_search_console', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'google_search_console', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'ability' => 'datamachine/google-search-console' ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/ImageGeneration.php b/inc/Engine/AI/Tools/Global/ImageGeneration.php index 2ceb8b4f9..c088e7fa6 100644 --- a/inc/Engine/AI/Tools/Global/ImageGeneration.php +++ b/inc/Engine/AI/Tools/Global/ImageGeneration.php @@ -26,7 +26,7 @@ class ImageGeneration extends BaseTool { public function __construct() { $this->registerConfigurationHandlers( 'image_generation' ); - $this->registerTool( 'image_generation', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'image_generation', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'ability' => 'datamachine/generate-image' ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/InternalLinkAudit.php b/inc/Engine/AI/Tools/Global/InternalLinkAudit.php index 5392443d0..e78270ab6 100644 --- a/inc/Engine/AI/Tools/Global/InternalLinkAudit.php +++ b/inc/Engine/AI/Tools/Global/InternalLinkAudit.php @@ -23,7 +23,7 @@ class InternalLinkAudit extends BaseTool { public function __construct() { - $this->registerTool( 'internal_link_audit', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'internal_link_audit', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'abilities' => array( 'datamachine/audit-internal-links', 'datamachine/get-orphaned-posts', 'datamachine/check-broken-links' ) ) ); } public function handle_tool_call( array $parameters, array $tool_def = array() ): array { diff --git a/inc/Engine/AI/Tools/Global/LocalSearch.php b/inc/Engine/AI/Tools/Global/LocalSearch.php index 6241d1faa..c0bc9a131 100644 --- a/inc/Engine/AI/Tools/Global/LocalSearch.php +++ b/inc/Engine/AI/Tools/Global/LocalSearch.php @@ -17,7 +17,7 @@ class LocalSearch extends BaseTool { public function __construct() { - $this->registerTool( 'local_search', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'local_search', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'ability' => 'datamachine/local-search' ) ); } public function handle_tool_call( array $parameters, array $tool_def = array() ): array { diff --git a/inc/Engine/AI/Tools/Global/PageSpeed.php b/inc/Engine/AI/Tools/Global/PageSpeed.php index 3514378da..3b9058dea 100644 --- a/inc/Engine/AI/Tools/Global/PageSpeed.php +++ b/inc/Engine/AI/Tools/Global/PageSpeed.php @@ -20,7 +20,7 @@ class PageSpeed extends BaseTool { public function __construct() { $this->registerConfigurationHandlers( 'pagespeed' ); - $this->registerTool( 'pagespeed', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'pagespeed', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'ability' => 'datamachine/pagespeed' ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/QueueValidator.php b/inc/Engine/AI/Tools/Global/QueueValidator.php index a1caf8afe..6afa2effc 100644 --- a/inc/Engine/AI/Tools/Global/QueueValidator.php +++ b/inc/Engine/AI/Tools/Global/QueueValidator.php @@ -23,7 +23,7 @@ class QueueValidator extends BaseTool { public function __construct() { - $this->registerTool( 'queue_validator', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'queue_validator', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'access_level' => 'admin' ) ); } /** diff --git a/inc/Engine/AI/Tools/Global/WebFetch.php b/inc/Engine/AI/Tools/Global/WebFetch.php index e008665ad..fec20e3fc 100644 --- a/inc/Engine/AI/Tools/Global/WebFetch.php +++ b/inc/Engine/AI/Tools/Global/WebFetch.php @@ -14,7 +14,7 @@ class WebFetch extends BaseTool { public function __construct() { - $this->registerTool( 'web_fetch', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'web_fetch', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'access_level' => 'author' ) ); } public function handle_tool_call( array $parameters, array $tool_def = array() ): array { diff --git a/inc/Engine/AI/Tools/Global/WordPressPostReader.php b/inc/Engine/AI/Tools/Global/WordPressPostReader.php index 33d05345e..9b54d8676 100644 --- a/inc/Engine/AI/Tools/Global/WordPressPostReader.php +++ b/inc/Engine/AI/Tools/Global/WordPressPostReader.php @@ -17,7 +17,7 @@ class WordPressPostReader extends BaseTool { public function __construct() { - $this->registerTool( 'wordpress_post_reader', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ) ); + $this->registerTool( 'wordpress_post_reader', array( $this, 'getToolDefinition' ), array( 'chat', 'pipeline' ), array( 'ability' => 'datamachine/get-wordpress-post' ) ); } public function handle_tool_call( array $parameters, array $tool_def = array() ): array { diff --git a/inc/Engine/AI/Tools/ToolPolicyResolver.php b/inc/Engine/AI/Tools/ToolPolicyResolver.php index 744a97fe4..605f822db 100644 --- a/inc/Engine/AI/Tools/ToolPolicyResolver.php +++ b/inc/Engine/AI/Tools/ToolPolicyResolver.php @@ -9,11 +9,12 @@ * * Resolution precedence (highest to lowest): * 1. Explicit deny list (always wins) - * 2. Per-agent tool policy (deny/allow mode from agent_config) - * 3. Context-level allow_only (narrows to explicit subset) - * 4. Context preset (pipeline/chat/system) - * 5. Global enablement settings - * 6. Tool configuration requirements + * 2. Per-agent tool policy (deny/allow mode from agent_config, supports categories) + * 3. Ability category filter (narrows tools by their linked ability's category) + * 4. Context-level allow_only (narrows to explicit subset) + * 5. Context preset (pipeline/chat/system) + * 6. Global enablement settings + * 7. Tool configuration requirements * * @package DataMachine\Engine\AI\Tools * @since 0.39.0 @@ -65,6 +66,8 @@ public function __construct( ?ToolManager $tool_manager = null ) { * @type array $engine_data Engine data snapshot for dynamic tool generation. * @type array $deny Tool names to explicitly deny (highest precedence). * @type array $allow_only If set, only these tools are allowed (allowlist mode). + * @type array $categories If set, only tools whose linked ability belongs to one + * of these categories are included. Empty = no filtering. * @type string|null $cache_scope Scope key for tool cache (e.g. flow_step_id). * } * @return array Resolved tools array keyed by tool name. @@ -124,18 +127,25 @@ public function resolve( array $context ): array { $tools = $this->applyAgentPolicy( $tools, $agent_policy ); } - // 4. Apply allowlist if specified (narrows to explicit subset). + // 4. Filter by ability categories (narrows to tools whose linked ability + // belongs to one of the specified categories). + $categories = $context['categories'] ?? array(); + if ( ! empty( $categories ) ) { + $tools = $this->filterByAbilityCategories( $tools, $categories ); + } + + // 5. Apply allowlist if specified (narrows to explicit subset). $allow_only = $context['allow_only'] ?? array(); if ( ! empty( $allow_only ) ) { $tools = array_intersect_key( $tools, array_flip( $allow_only ) ); } - // 5. Apply deny list (always wins). + // 6. Apply deny list (always wins). if ( ! empty( $deny ) ) { $tools = array_diff_key( $tools, array_flip( $deny ) ); } - // 6. Allow external filtering of resolved tools. + // 7. Allow external filtering of resolved tools. $tools = apply_filters( 'datamachine_resolved_tools', $tools, $context_type, $context ); return $tools; @@ -364,6 +374,79 @@ private function gatherToolsForContext( string $context_type ): array { return $available_tools; } + /** + * Filter tools by their linked ability's category. + * + * For each tool, resolves its ability category via the Abilities API registry. + * Only tools whose ability belongs to one of the allowed categories pass through. + * + * Handler tools (those with a 'handler' key but no 'ability' key) are always + * included — they are dynamically scoped by the pipeline engine and should not + * be filtered by category. + * + * Tools without any ability linkage are excluded when category filtering is + * active, since they cannot be categorized. To include them, add their names + * to the context's `allow_only` list as an escape hatch. + * + * @since 0.55.0 + * + * @param array $tools Resolved tools array keyed by tool name. + * @param string[] $categories Allowed category slugs (e.g. 'datamachine/content'). + * @return array Filtered tools. + */ + private function filterByAbilityCategories( array $tools, array $categories ): array { + if ( empty( $categories ) ) { + return $tools; + } + + $registry = class_exists( 'WP_Abilities_Registry' ) ? \WP_Abilities_Registry::get_instance() : null; + $categories_flip = array_flip( $categories ); + $filtered = array(); + + foreach ( $tools as $name => $tool ) { + if ( ! is_array( $tool ) ) { + continue; + } + + // Handler tools bypass category filtering — they're already scoped + // by the pipeline engine to adjacent step handlers. + if ( isset( $tool['handler'] ) && ! isset( $tool['ability'] ) && ! isset( $tool['abilities'] ) ) { + $filtered[ $name ] = $tool; + continue; + } + + // Collect ability slugs from tool metadata. + $ability_slugs = array(); + + if ( ! empty( $tool['ability'] ) ) { + $ability_slugs[] = $tool['ability']; + } + + if ( ! empty( $tool['abilities'] ) && is_array( $tool['abilities'] ) ) { + $ability_slugs = array_merge( $ability_slugs, $tool['abilities'] ); + } + + // No ability linkage — cannot determine category, excluded. + if ( empty( $ability_slugs ) ) { + continue; + } + + // Check if ANY linked ability belongs to an allowed category. + if ( $registry ) { + foreach ( $ability_slugs as $slug ) { + $ability = $registry->get_registered( $slug ); + + if ( $ability && isset( $categories_flip[ $ability->get_category() ] ) ) { + $filtered[ $name ] = $tool; + break; + } + } + } + } + + return $filtered; + } + /** * Get tool policy from an agent's config. * @@ -394,8 +477,23 @@ public function getAgentToolPolicy( int $agent_id ): ?array { $policy = $config['tool_policy']; - // Validate structure: must have 'mode' and 'tools'. - if ( ! isset( $policy['mode'] ) || ! isset( $policy['tools'] ) || ! is_array( $policy['tools'] ) ) { + // Validate structure: must have 'mode' and at least 'tools' or 'categories'. + if ( ! isset( $policy['mode'] ) ) { + return null; + } + + // Ensure tools is present and an array (may be empty if only categories are used). + if ( ! isset( $policy['tools'] ) || ! is_array( $policy['tools'] ) ) { + $policy['tools'] = array(); + } + + // Normalize categories to an array. + if ( isset( $policy['categories'] ) && ! is_array( $policy['categories'] ) ) { + return null; + } + + // Must have at least tools or categories to be a valid policy. + if ( empty( $policy['tools'] ) && empty( $policy['categories'] ?? array() ) ) { return null; } @@ -410,11 +508,18 @@ public function getAgentToolPolicy( int $agent_id ): ?array { /** * Apply an agent's tool policy to a set of resolved tools. * - * - `deny` mode: agent can use everything EXCEPT listed tools. - * - `allow` mode: agent can ONLY use listed tools. + * - `deny` mode: agent can use everything EXCEPT listed tools/categories. + * - `allow` mode: agent can ONLY use listed tools/categories. * - No policy (null): no restrictions (backward compatible). * + * The policy supports both individual tool names (`tools` key) and ability + * categories (`categories` key). When both are present, they compose: + * - allow mode: tool passes if it matches a tool name OR a category. + * - deny mode: tool is excluded if it matches a tool name OR a category. + * * @since 0.42.0 + * @since 0.55.0 Added category support in tool policies. + * * @param array $tools Resolved tools array keyed by tool name. * @param array|null $policy Tool policy from getAgentToolPolicy(), or null for no restrictions. * @return array Filtered tools array. @@ -424,20 +529,62 @@ public function applyAgentPolicy( array $tools, ?array $policy ): array { return $tools; } - $mode = $policy['mode']; - $tool_names = $policy['tools']; + $mode = $policy['mode']; + $tool_names = $policy['tools'] ?? array(); + $policy_categories = $policy['categories'] ?? array(); - if ( empty( $tool_names ) ) { - // deny with empty list = no restrictions; allow with empty list = no tools. + // No tool names and no categories = no restrictions (deny) or no tools (allow). + if ( empty( $tool_names ) && empty( $policy_categories ) ) { return 'allow' === $mode ? array() : $tools; } - if ( 'deny' === $mode ) { - return array_diff_key( $tools, array_flip( $tool_names ) ); + // Simple case: no categories, just tool names (original behavior). + if ( empty( $policy_categories ) ) { + if ( 'deny' === $mode ) { + return array_diff_key( $tools, array_flip( $tool_names ) ); + } + return array_intersect_key( $tools, array_flip( $tool_names ) ); + } + + // Category-aware filtering: check both tool names and categories. + $registry = class_exists( 'WP_Abilities_Registry' ) ? \WP_Abilities_Registry::get_instance() : null; + $tool_names_flip = ! empty( $tool_names ) ? array_flip( $tool_names ) : array(); + $categories_flip = array_flip( $policy_categories ); + $filtered = array(); + + foreach ( $tools as $name => $tool ) { + $matches_tool = isset( $tool_names_flip[ $name ] ); + $matches_cat = false; + + if ( ! $matches_tool && is_array( $tool ) && $registry ) { + $ability_slugs = array(); + + if ( ! empty( $tool['ability'] ) ) { + $ability_slugs[] = $tool['ability']; + } + if ( ! empty( $tool['abilities'] ) && is_array( $tool['abilities'] ) ) { + $ability_slugs = array_merge( $ability_slugs, $tool['abilities'] ); + } + + foreach ( $ability_slugs as $slug ) { + $ability = $registry->get_registered( $slug ); + if ( $ability && isset( $categories_flip[ $ability->get_category() ] ) ) { + $matches_cat = true; + break; + } + } + } + + $matches = $matches_tool || $matches_cat; + + if ( 'allow' === $mode && $matches ) { + $filtered[ $name ] = $tool; + } elseif ( 'deny' === $mode && ! $matches ) { + $filtered[ $name ] = $tool; + } } - // 'allow' mode: only keep tools in the list. - return array_intersect_key( $tools, array_flip( $tool_names ) ); + return $filtered; } /** diff --git a/package-lock.json b/package-lock.json index 0a1d88481..52fdda20d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "datamachine", - "version": "0.65.0", + "version": "0.66.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "datamachine", - "version": "0.65.0", + "version": "0.66.0", "dependencies": { "@extrachill/chat": "github:Extra-Chill/chat", "@tanstack/react-query": "^5.90.12", diff --git a/tests/Unit/Abilities/AllAbilitiesRegisteredTest.php b/tests/Unit/Abilities/AllAbilitiesRegisteredTest.php index 50a14d008..30a716276 100644 --- a/tests/Unit/Abilities/AllAbilitiesRegisteredTest.php +++ b/tests/Unit/Abilities/AllAbilitiesRegisteredTest.php @@ -119,14 +119,38 @@ public function test_all_data_machine_abilities_registered(): void { } /** - * Test datamachine category is registered at boot. + * Test datamachine ability categories are registered at boot. */ - public function test_datamachine_category_registered(): void { + public function test_datamachine_categories_registered(): void { $categories = wp_get_ability_categories(); - $this->assertArrayHasKey( - 'datamachine', - $categories, - 'datamachine category should be registered during plugin boot' + + $expected_categories = array( + 'datamachine/content', + 'datamachine/media', + 'datamachine/analytics', + 'datamachine/seo', + 'datamachine/memory', + 'datamachine/taxonomy', + 'datamachine/publishing', + 'datamachine/fetch', + 'datamachine/email', + 'datamachine/pipeline', + 'datamachine/flow', + 'datamachine/jobs', + 'datamachine/agent', + 'datamachine/settings', + 'datamachine/auth', + 'datamachine/logging', + 'datamachine/system', + 'datamachine/chat', ); + + foreach ( $expected_categories as $slug ) { + $this->assertArrayHasKey( + $slug, + $categories, + "Ability category '{$slug}' should be registered during plugin boot" + ); + } } }