-
Notifications
You must be signed in to change notification settings - Fork 5
fix: Agents REST list endpoint — merge owned+granted agents, fix N+1 query #994
Description
Summary
GET /datamachine/v1/agents has two bugs in the non-admin code path that block multi-agent frontend chat.
Related: #993 (DB primitives), #972 (per-user toolsets)
Bug 1: Non-admin path omits agents the user OWNS
File: inc/Api/Agents.php lines 312-358
The non-admin branch only queries AgentAccess::get_agent_ids_for_user():
} else {
$access_repo = new AgentAccess();
$accessible_ids = $access_repo->get_agent_ids_for_user($user_id);
// ...loops through IDs...
}If an owner's access grant is missing from agent_access (race condition, migration gap, manual DB edit), they won't see their own agent. The owner's relationship lives on datamachine_agents.owner_id, not in agent_access.
Fix
Merge owned agents with access-granted agents:
} else {
$access_repo = new AgentAccess();
$accessible_ids = $access_repo->get_agent_ids_for_user($user_id);
$owned_agents = $agents_repo->get_all_by_owner_id($user_id); // from #993
$owned_ids = array_column($owned_agents, 'agent_id');
$all_ids = array_unique(array_merge(array_map('intval', $owned_ids), $accessible_ids));
$all_agents = $agents_repo->get_agents_by_ids($all_ids); // from #993
// ...site_scope filter...
}Bug 2: N+1 query pattern
The non-admin path calls get_agent_ids_for_user() (1 query), then get_agent() in a loop (N queries). For a user with access to 20 agents, that's 21 queries.
Fix
Use get_agents_by_ids() (batch method from #993) for a single query.
Enhancement: Add user_role to list response
shape_list_item() returns agent metadata but not the requesting user's role (viewer/operator/admin). The frontend needs this for UI decisions (show edit button, show config, etc.).
Needed
Enrich the response with the user's role from agent_access:
private static function shape_list_item(array $agent, ?string $user_role = null): array {
$item = [
'agent_id' => (int) $agent['agent_id'],
'agent_slug' => (string) $agent['agent_slug'],
'agent_name' => (string) $agent['agent_name'],
'owner_id' => (int) $agent['owner_id'],
'site_scope' => isset($agent['site_scope']) ? (int) $agent['site_scope'] : null,
'status' => (string) $agent['status'],
'created_at' => $agent['created_at'] ?? '',
'updated_at' => $agent['updated_at'] ?? '',
];
if ($user_role !== null) {
$item['user_role'] = $user_role;
}
return $item;
}Checklist
- Merge owned + access-granted agents in non-admin
handle_list()path - Replace N+1
get_agent()loop with batchget_agents_by_ids() - Add
user_roleto list response shape