Skip to content

feat(routines): ability wake targets alongside agents/chat - #547

Merged
chubes4 merged 1 commit into
mainfrom
feat/routine-ability-target
Sep 9, 2026
Merged

feat(routines): ability wake targets alongside agents/chat#547
chubes4 merged 1 commit into
mainfrom
feat/routine-ability-target

Conversation

@chubes4

@chubes4 chubes4 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Closes #546

Target model

A routine's wake target is now a first-class part of the value object. WP_Agent_Routine accepts exactly one of:

  • agent (+ optional prompt, session_id) — the existing chat target, unchanged;
  • ability (slug) + optional input (string-keyed array) — a generic ability target.

Providing both or neither throws InvalidArgumentException. New surface: TARGET_CHAT / TARGET_ABILITY constants, get_target_type(): 'chat'|'ability', get_ability(): string, get_input(): array (get_agent_slug() returns '' for ability targets). to_array() always emits target and adds ability + input for ability targets, so existing consumers keep their shape.

Dispatch

dispatch_scheduled_routine_run() branches on get_target_type() after resolving the routine. The chat path (from wp_get_ability( 'agents/chat' ) through the completion action) is byte-for-byte unchanged; the ability path sits beside it:

  1. wp_get_ability( $slug ) → null ⇒ agents_run_routine_dispatch_failed( 'ability_missing', … )
  2. (bool) apply_filters( 'wp_agent_routine_ability_permission', false, $routine, $ability ) → false ⇒ dispatch_failed( 'permission_denied', … )
  3. $ability->execute( $routine->get_input() )WP_Errordispatch_failed( $error_code, … )
  4. Success ⇒ the existing wp_agent_routine_run_completed action 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/chat gate for that one invocation; for the ability path this PR deliberately does not add any per-ability bypass. Instead, the generic wp_agent_routine_ability_permission filter 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-routines reports routine ids only (no per-routine shapes), so nothing to extend there; to_array() now carries target/ability/input for any operator surface that serializes routines.

Tests

New tests/routine-ability-target-smoke.php (registered in scripts.smoke after routines-durability-smoke.php), 37 assertions:

  • ability-target construction, getters, non-string input keys dropped;
  • constructor rejects both / neither of agent / ability;
  • to_array() full-shape equality for both target types;
  • end-to-end dispatch through the scheduled hook against a fake Abilities API: ability receives the configured input and wp_agent_routine_run_completed fires with the result;
  • missing ability ⇒ ability_missing; permission filter absent ⇒ permission_denied and the ability is not executed; ability WP_Error ⇒ its error code is reported;
  • chat path through the same listener verified unchanged.

Existing routine-smoke.php (36) and routines-durability-smoke.php (56) stay green; full composer smoke and composer phpstan pass.

Docs: "Wake targets" subsection added under Routines and scheduling in docs/channels-workflows-operations.md.

AI-generated with Claude Code via Homeboy worktree

@chubes4
chubes4 merged commit 9470a68 into main Sep 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

routines: ability wake targets — a routine should be able to run any ability, not only agents/chat

1 participant