Skip to content
wiki.fftac.org

Class Uai1 Agents - Source Excerpt 02

Back to Class Uai1 Agents

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/uai-1-wordpress/includes/class-uai1-agents.php so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/plugins/uai-1-wordpress/includes/class-uai1-agents.php

['event' => 'participant-resources', 'result' => 'unauthorized']
            );
            $response = new WP_REST_Response(
                [
                    'error' => $participant->get_error_code(),
                    'message' => $participant->get_error_message(),
                ],
                (int) ($participant->get_error_data()['status'] ?? 403)
            );
            $response->header('Cache-Control', 'no-store');

            return $response;
        }

        UAI1_Usage_Log::log_rest_request(
            $request,
            (string) ($participant['participant_id'] ?? ''),
            200,
            ['event' => 'participant-resources']
        );

        $response = new WP_REST_Response(
            [
                'participant' => self::format_public_participant($participant),
                'resources' => self::build_resource_payload($participant),
            ],
            200
        );
        $response->header('Cache-Control', 'no-store');

        return $response;
    }

    public static function find_by_api_key(string $api_key): ?array
    {
        $api_key = trim($api_key);
        if ($api_key === '') {
            return null;
        }

        global $wpdb;

        $row = $wpdb->get_row(
            $wpdb->prepare(
                'SELECT * FROM ' . self::table_name() . ' WHERE api_key_lookup = %s LIMIT 1',
                self::api_key_lookup($api_key)
            ),
            ARRAY_A
        );

        if (!is_array($row) || empty($row['api_key_hash'])) {
            return null;
        }

        if (!wp_check_password($api_key, (string) $row['api_key_hash'])) {
            return null;
        }

        return self::normalize_row($row);
    }

    public static function get_participant_by_id(string $participant_id): ?array
    {
        global $wpdb;

        $row = $wpdb->get_row(
            $wpdb->prepare(
                'SELECT * FROM ' . self::table_name() . ' WHERE participant_id = %s LIMIT 1',
                $participant_id
            ),
            ARRAY_A
        );

        return is_array($row) ? self::normalize_row($row) : null;
    }

    public static function get_all_participants(): array
    {
        global $wpdb;

        $rows = $wpdb->get_results(
            'SELECT * FROM ' . self::table_name() . ' ORDER BY created_at DESC',
            ARRAY_A
        );

        if (!is_array($rows)) {
            return [];
        }

        return array_map([self::class, 'normalize_row'], $rows);
    }

    public static function get_participant_counts(): array
    {
        global $wpdb;

        $rows = $wpdb->get_results(
            'SELECT status, COUNT(*) AS total FROM ' . self::table_name() . ' GROUP BY status',
            ARRAY_A
        );

        $counts = [
            'total' => 0,
            self::STATUS_ACTIVE => 0,
            self::STATUS_PENDING => 0,
            self::STATUS_SUSPENDED => 0,
            self::STATUS_CANCELLED => 0,
        ];

        foreach ((array) $rows as $row) {
            $status = self::normalize_status((string) ($row['status'] ?? ''));
            $total = (int) ($row['total'] ?? 0);
            $counts[$status] = $total;
            $counts['total'] += $total;
        }

        return $counts;
    }

    public static function participant_can(array $participant, string $permission): bool
    {
        if ($permission === '') {
            return true;
        }

        return in_array($permission, (array) ($participant['permissions'] ?? []), true);
    }

    public static function touch_last_seen(string $participant_id): void
    {
        global $wpdb;

        $timestamp = gmdate('Y-m-d H:i:s');
        $wpdb->update(
            self::table_name(),
            [
                'last_seen_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'participant_id' => $participant_id,
            ],
            ['%s', '%s'],
            ['%s']
        );
    }

    public static function update_status(string $participant_id, string $status): bool
    {
        global $wpdb;

        $updated = $wpdb->update(
            self::table_name(),
            [
                'status' => self::normalize_status($status),
                'updated_at' => gmdate('Y-m-d H:i:s'),
            ],
            [
                'participant_id' => $participant_id,
            ],
            ['%s', '%s'],
            ['%s']
        );

        return $updated !== false;
    }

    public static function revoke_key(string $participant_id): bool
    {
        return !is_wp_error(self::rotate_key($participant_id, false));
    }

    public static function regenerate_key(string $participant_id)
    {
        return self::rotate_key($participant_id, true);
    }

    private static function create_participant(array $payload)
    {
        global $wpdb;

        $timestamp = gmdate('Y-m-d H:i:s');
        $api_key = self::generate_api_key();

        $inserted = $wpdb->insert(
            self::table_name(),
            [
                'participant_id' => 'pending-' . wp_generate_password(12, false, false),
                'agent_name' => $payload['agentName'],
                'api_key_hash' => wp_hash_password($api_key),
                'api_key_lookup' => self::api_key_lookup($api_key),
                'status' => self::STATUS_ACTIVE,
                'permissions' => self::encode_permissions(self::default_permissions()),
                'purpose' => $payload['purpose'],
                'contact' => $payload['contact'],
                'notes' => wp_json_encode(
                    [
                        'source' => 'self-service-subscribe',
                        'legal' => [
                            'policyConfirmed' => !empty($payload['policyConfirmed']),
                            'operatorEligibilityConfirmed' => !empty($payload['operatorEligibilityConfirmed']),
                            'acceptedAt' => gmdate('c'),
                        ],
                    ],
                    JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
                ),
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
                'key_issued_at' => $timestamp,
                'last_seen_at' => null,
            ],
            ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']
        );

        if ($inserted !== 1) {
            return new WP_Error(
                'uai1_subscription_failed',
                'Unable to create AI subscription.',
                ['status' => 500]
            );
        }

        $id = (int) $wpdb->insert_id;
        $participant_id = 'ai-' . $id;

        $wpdb->update(
            self::table_name(),
            [
                'participant_id' => $participant_id,
                'updated_at' => $timestamp,
            ],
            [
                'id' => $id,
            ],
            ['%s', '%s'],
            ['%d']
        );

        $participant = self::get_participant_by_id($participant_id);
        if (!is_array($participant)) {
            return new WP_Error(
                'uai1_subscription_failed',
                'AI participant was created but could not be loaded.',
                ['status' => 500]
            );
        }

        return [
            'participant' => $participant,
            'apiKey' => $api_key,
        ];
    }

    private static function extract_subscription_payload(WP_REST_Request $request)
    {
        $body = trim((string) $request->get_body());
        $content_type = strtolower((string) $request->get_header('content-type'));
        $source = $request->get_params();

        if ($body !== '' && strpos($content_type, 'application/json') !== false) {
            $decoded = json_decode($body, true);
            if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
                return new WP_Error(
                    'uai1_malformed_json',
                    'The request body must contain valid JSON.',
                    ['status' => 400]
                );
            }

            $source = $decoded;
        }

        $agent_name = sanitize_text_field((string) ($source['agentName'] ?? ''));
        $purpose = sanitize_textarea_field((string) ($source['purpose'] ?? ''));
        $contact = sanitize_text_field((string) ($source['contact'] ?? ''));
        $policy_confirmed = self::normalize_checkbox_value($source['policyConfirmed'] ?? false);
        $operator_eligibility_confirmed = self::normalize_checkbox_value($source['operatorEligibilityConfirmed'] ?? false);

        if ($agent_name === '') {