feat(routines): ability wake targets alongside agents/chat - #547
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #546
Target model
A routine's wake target is now a first-class part of the value object.
WP_Agent_Routineaccepts exactly one of:agent(+ optionalprompt,session_id) — the existing chat target, unchanged;ability(slug) + optionalinput(string-keyed array) — a generic ability target.Providing both or neither throws
InvalidArgumentException. New surface:TARGET_CHAT/TARGET_ABILITYconstants,get_target_type(): 'chat'|'ability',get_ability(): string,get_input(): array(get_agent_slug()returns''for ability targets).to_array()always emitstargetand addsability+inputfor ability targets, so existing consumers keep their shape.Dispatch
dispatch_scheduled_routine_run()branches onget_target_type()after resolving the routine. The chat path (fromwp_get_ability( 'agents/chat' )through the completion action) is byte-for-byte unchanged; the ability path sits beside it:wp_get_ability( $slug )→ null ⇒agents_run_routine_dispatch_failed( 'ability_missing', … )(bool) apply_filters( 'wp_agent_routine_ability_permission', false, $routine, $ability )→ false ⇒dispatch_failed( 'permission_denied', … )$ability->execute( $routine->get_input() )→WP_Error⇒dispatch_failed( $error_code, … )wp_agent_routine_run_completedaction with the ability result.Permission filter — no implicit elevation
The scheduled invocation runs as the cron/loopback principal. The chat path already lifts the
agents/chatgate for that one invocation; for the ability path this PR deliberately does not add any per-ability bypass. Instead, the genericwp_agent_routine_ability_permissionfilter defaults to deny, and consumers that register ability-targeted routines opt in by filtering — typically scoped to the abilities they own. Same opt-in shape as the chat gate, without granting the scheduler blanket rights to execute arbitrary abilities.Reconcile surface
agents/reconcile-routinesreports routine ids only (no per-routine shapes), so nothing to extend there;to_array()now carriestarget/ability/inputfor any operator surface that serializes routines.Tests
New
tests/routine-ability-target-smoke.php(registered inscripts.smokeafterroutines-durability-smoke.php), 37 assertions:agent/ability;to_array()full-shape equality for both target types;wp_agent_routine_run_completedfires with the result;ability_missing; permission filter absent ⇒permission_deniedand the ability is not executed; abilityWP_Error⇒ its error code is reported;Existing
routine-smoke.php(36) androutines-durability-smoke.php(56) stay green; fullcomposer smokeandcomposer phpstanpass.Docs: "Wake targets" subsection added under Routines and scheduling in
docs/channels-workflows-operations.md.AI-generated with Claude Code via Homeboy worktree