Skip to content

refactor: datamachine/list-agents — scope-aware defaults, per-user access resolution #996

@chubes4

Description

@chubes4

Summary

Refactor the existing datamachine/list-agents ability from admin-only to scope-aware with smart defaults. One primitive that serves all callers — no need for a separate list-accessible-agents ability.

Replaces: #995 (closed)
Related: #993 (batch DB methods), #994 (REST endpoint fixes), #972 (per-user toolsets)
Blocked by: #993 (get_all_by_owner_id(), get_agents_by_ids())

Current Behavior

// datamachine/list-agents — admin-only, returns everything
// Permission: PermissionHelper::can_manage()
// Returns: all agents scoped to current site + network-wide

The REST endpoint GET /datamachine/v1/agents bypasses this ability entirely and does its own access-scoped DB queries for non-admin users. Business logic is split between two places.

Proposed Behavior

Default (no params) = list agents the calling user can access. Admin can escalate.

Parameters

Param Type Required Default Description
scope string no 'mine' 'mine' = user's accessible agents. 'all' = all agents on site (admin-only).
user_id int no acting user Whose accessible agents to list. Admin can query for any user. Non-admin forced to self.
site_id int no current blog Filter by site_scope (matches exact + NULL/network-wide)
status string no 'active' Filter by agent status. 'any' to skip.
include_role bool no false Include the user's access role per agent in response

Permission Model

Caller scope user_id Result
Any user with can('chat') mine (default) self (default) Agents they own + have grants to
Admin mine (default) self (default) Same — their own accessible agents
Admin all All agents on site (current behavior)
Admin mine other user's ID That user's accessible agents
Non-admin all Permission denied
Non-admin mine other user's ID Permission denied (forced to self)

Base permission: can('chat') (down from can_manage())
Escalation: scope=all or user_id != self requires can_manage()

Resolution Logic (scope=mine)

// 1. Agents the user OWNS
$owned = $agents_repo->get_all_by_owner_id($user_id);

// 2. Agents the user has ACCESS GRANTS to
$granted_ids = $access_repo->get_agent_ids_for_user($user_id);
$extra_ids   = array_diff($granted_ids, array_column($owned, 'agent_id'));
$granted     = $agents_repo->get_agents_by_ids($extra_ids);

// 3. Merge + dedupe
$all = array_merge($owned, $granted);

// 4. Filter by site_scope (match exact site OR network-wide NULL)
// 5. Filter by status
// 6. Final gate: PermissionHelper::can_access_agent() per agent
// 7. Shape response

Resolution Logic (scope=all)

// Current behavior, unchanged
$all = $agents_repo->get_all(['site_id' => $site_id]);
// Filter by status
// Shape response

Return Shape

[
    'success' => true,
    'agents'  => [
        [
            'agent_id'    => 5,
            'agent_slug'  => 'roadie',
            'agent_name'  => 'Roadie',
            'owner_id'    => 1,
            'site_scope'  => null,
            'status'      => 'active',
            'description' => 'Your AI assistant.',  // from agent_config.description
            'is_owner'    => true,                   // whether acting user owns this agent
            'user_role'   => 'admin',                // only when include_role=true
        ],
    ],
]

What Changes After This Ships

  1. REST GET /agents refactored to call this ability — no more inline DB queries in the controller
  2. Frontend chat plugin calls wp_get_ability('datamachine/list-agents')->execute() with no params — gets the user's accessible agents automatically
  3. CLI gets natural behavior: wp datamachine agents list = your agents, wp datamachine agents list --scope=all = all (admin)
  4. Chat tools can list a user's agents through the same primitive
  5. Agent-to-agent workflows can query accessible agents for any user (admin context)

CLI Mapping

# Default: my accessible agents
wp datamachine agents list

# Admin: all agents on site  
wp datamachine agents list --scope=all

# Admin: another user's agents
wp datamachine agents list --user_id=5

# With role info
wp datamachine agents list --include_role

Backward Compatibility

The ability currently returns all agents for admins. After this change:

  • Admin calling with no params gets their own accessible agents (behavior change)
  • Admin calling with --scope=all gets the old behavior
  • Any existing code that calls the ability is admin-only code (non-admins couldn't use it before), so the admin caller just needs to add scope=all if they want the old behavior
  • REST endpoint refactored at the same time, so the transition is atomic

Checklist

  • Lower base permission from can_manage() to can('chat')
  • Add scope parameter (default 'mine')
  • Add user_id parameter (default acting user, admin-only escalation)
  • Add site_id, status, include_role parameters
  • Implement scope=mine resolution: owned + access-granted, merged, deduped, filtered
  • Keep scope=all as current behavior (admin-only gate)
  • Extract description from agent_config JSON
  • Add is_owner boolean to response shape
  • Optional user_role enrichment from access table
  • Refactor Api/Agents.php::handle_list() to delegate to this ability
  • Update CLI agents list command for new params
  • Uses get_all_by_owner_id() + get_agents_by_ids() from feat: Multi-agent database primitives — batch queries + session agent info #993

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions