Skip to content

feat: Multi-agent database primitives — batch queries + session agent info #993

@chubes4

Description

@chubes4

Summary

The agent database layer assumes single-agent-per-user throughout. To support multi-agent frontend chat (agent switching, personal agents, multi-agent views), several DB-level primitives are missing.

Related: #972 (per-user dynamic toolsets), #919 (scoped agent creation)

Gap 1: No get_all_by_owner_id() on Agents repository

File: inc/Core/Database/Agents/Agents.php

get_by_owner_id() is hard-coded to LIMIT 1 (line 137). There is no way to list all agents a user owns without fetching the entire agents table:

// Current workaround in AgentAbilities::createAgent() line 503:
$all_agents = $agents_repo->get_all();
$owned = array_filter($all_agents, fn($a) => (int) $a['owner_id'] === $owner_id);

This fetches every agent in the system and filters in-memory. Completely wrong at scale.

Needed

public function get_all_by_owner_id(int $owner_id): array {
    $rows = $this->wpdb->get_results(
        $this->wpdb->prepare(
            'SELECT * FROM %i WHERE owner_id = %d ORDER BY agent_id ASC',
            $this->table_name,
            $owner_id
        ),
        ARRAY_A
    );
    // ...decode agent_config...
    return $rows ?: [];
}

Also fix AgentAbilities::createAgent() to use this instead of get_all() + array_filter.

Gap 2: No get_agents_by_ids() batch method

File: inc/Core/Database/Agents/Agents.php

AgentAccess::get_agent_ids_for_user() returns int[] — just IDs. To get full agent rows, callers must make N+1 queries via get_agent() in a loop. This pattern exists in Api/Agents.php::handle_list() for non-admin users.

Needed

public function get_agents_by_ids(array $agent_ids): array {
    if (empty($agent_ids)) return [];
    $placeholders = implode(',', array_fill(0, count($agent_ids), '%d'));
    $rows = $this->wpdb->get_results(
        $this->wpdb->prepare(
            "SELECT * FROM %i WHERE agent_id IN ({$placeholders}) ORDER BY agent_id ASC",
            array_merge([$this->table_name], $agent_ids)
        ),
        ARRAY_A
    );
    // ...decode agent_config...
    return $rows ?: [];
}

Gap 3: get_user_sessions() strips agent_id from response

File: inc/Core/Database/Chat/Chat.php lines 507-528

The SELECT * query fetches agent_id from the row, but the return array mapping discards it:

$result[] = [
    'session_id'    => $session['session_id'],
    'title'         => $session['title'] ?? null,
    'context'       => $session['context'] ?? 'chat',
    'first_message' => mb_substr($first_message, 0, 100),
    'message_count' => count($messages),
    'created_at'    => ...,
    'updated_at'    => ...,
    // agent_id is NOT included
];

The frontend cannot display which agent a session belongs to, making multi-agent session lists impossible.

Needed

  1. Add agent_id to the return array
  2. Enrich with agent_name/agent_slug via batch post-query lookup using get_agents_by_ids()

Gap 4: get_all() has no owner_id filter

File: inc/Core/Database/Agents/Agents.php lines 269-306

get_all() accepts $args['site_id'] but ignores any owner_id parameter. Should support $args['owner_id'] for filtered queries.

Gap 5: PermissionHelper::resolve_scoped_agent_id() returns only first agent

File: inc/Abilities/PermissionHelper.php lines 379-425

This method finds ONE agent via get_by_owner_id() (LIMIT 1) or takes the first from get_agent_ids_for_user(). For multi-agent, a companion resolve_all_accessible_agent_ids() is needed.

Checklist

  • Add Agents::get_all_by_owner_id(int $owner_id): array
  • Add Agents::get_agents_by_ids(array $agent_ids): array
  • Add owner_id support to Agents::get_all() args
  • Add agent_id (+ optionally agent_name, agent_slug) to Chat::get_user_sessions() return shape
  • Fix AgentAbilities::createAgent() to use get_all_by_owner_id() instead of get_all() + array_filter
  • Add PermissionHelper::resolve_all_accessible_agent_ids() or equivalent

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