Bash

php -l /var/www/mssql.danmarcrm.com/dev1/extension2v1/public/thread_view.php 2>&1

PHP syntax check


No syntax errors detected in /var/www/mssql.danmarcrm.com/dev1/extension2v1/public/thread_view.php


Here's what changed:

**Context builder search now has two "Add All" buttons when there are more results than shown:**

1. **"+ Add All 296 Threads"** (blue, prominent) — fetches ALL pages from the API (100 at a time with progress indicator), then adds every thread to context
2. **"+ Add Shown 99"** — adds only the currently loaded threads (quick, no extra API calls)

If all threads fit in one page, there's just one "Add All" button.

**Regarding quoted email content** — confirmed it's already handled:
- **Additional context threads** go through the `thread_for_ai` API endpoint which calls `ThreadAnalyzer::stripQuotedContent()` server-side and truncates each email body to 3000 chars
- **Current thread** respects the content mode dropdown (stripped/smart/full)

So with 296 threads and stripped quotes, the token count will still be significant — the token estimator will warn you if you're approaching model limits. Refresh and try it out.


we don't have an option in threads search do delete all found records. pls make sure works flawlasly and we don't delete other records. For ex we have a lot of "Address not matched by Google GeoCoding" matching delta emails, I need to delete them all before analyzing the delta emails


TodoWrite

Add 'Delete All Found' button to thread search results in context builder

Adding 'Delete All Found' button to context builder thread search

Ensure delete only targets exact search-matched thread_ids (no collateral)

Verifying delete safety with exact thread_id targeting

Test the flow end-to-end


Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable

Add 'Delete All Found' button to thread search results in context builder

Adding 'Delete All Found' button to context builder thread search

Ensure delete only targets exact search-matched thread_ids (no collateral)

Verifying delete safety with exact thread_id targeting

Test the flow end-to-end


Let me first understand the current delete thread flow, then figure out the safest approach.


5791:        case 'delete_thread':
5792-            if ($_SERVER['REQUEST_METHOD'] !== 'POST') jsonError('POST required', 405);
5793-            $jsonData = json_decode(file_get_contents('php://input'), true) ?? [];
5794-            $threadId = $jsonData['thread_id'] ?? '';
5795-            if (!$threadId) jsonError('thread_id required');
5796-            $adapter = QueryAdapter::getInstance();
5797-            $deleted = $adapter->deleteThread($threadId);
5798-            jsonResponse(['success' => true, 'deleted' => $deleted]);
5799-            break;
5800-
5801-        // ==========================================
5802-        // Delete AI Response - single response by ID
5803-        // ==========================================
5804-        case 'delete_ai_response':
5805-            if ($_SERVER['REQUEST_METHOD'] !== 'POST') jsonError('POST required', 405);
5806-            $jsonData = json_decode(file_get_contents('php://input'), true) ?? [];
5807-            $responseId = $jsonData['response_id'] ?? '';
5808-            if (!$responseId) jsonError('response_id required');
5809-            $delMongo = \EmailIntelligence\MongoDatabase::getInstance();
5810-            $deletedCount = $delMongo->delete('ai_query_responses', ['_id' => new \MongoDB\BSON\ObjectId($responseId)]);
5811-            jsonResponse(['success' => true, 'deleted' => $deletedCount]);


1628:    public function deleteThread(string $threadId): array
1629-    {
1630-        $mongo = MongoDatabase::getInstance();
1631-        $result = [];
1632-
1633-        $result['emails'] = $mongo->delete('emails', ['thread_id' => $threadId]);
1634-        $result['thread_analysis'] = $mongo->delete('thread_analysis', ['thread_id' => $threadId]);
1635-        $result['ai_query_responses'] = $mongo->delete('ai_query_responses', ['thread_ids' => $threadId]);
1636-
1637-        return $result;
1638-    }
1639-
1640-    /**
1641-     * Delete all threads involving a specific email address or domain
1642-     */
1643-    public function deleteThreadsByEmail(string $email = '', string $domain = ''): array
1644-    {
1645-        $mongo = MongoDatabase::getInstance();
1646-
1647-        // Build filter to find matching emails
1648-        $filter = [];
1649-        if ($email) {
1650-            $emailLower = strtolower($email);
1651-            $filter['$or'] = [
1652-                ['from_email' => $emailLower],
1653-                ['to_email' => $emailLower]


Good. The `deleteThread()` method safely deletes by exact `thread_id` — emails, analysis, and AI responses. The safest approach is: collect the exact `thread_id` list from the search results, then delete each one. No regex, no wildcards — just exact IDs.

Now let me add the bulk delete endpoint and the UI. I'll add this to both thread_view.php (context builder) and the dashboard (threads tab search).

Let me first add the API endpoint for bulk delete by thread IDs:


  5791→        case 'delete_thread':
  5792→            if ($_SERVER['REQUEST_METHOD'] !== 'POST') jsonError('POST required', 405);
  5793→            $jsonData = json_decode(file_get_contents('php://input'), true) ?? [];
  5794→            $threadId = $jsonData['thread_id'] ?? '';
  5795→            if (!$threadId) jsonError('thread_id required');
  5796→            $adapter = QueryAdapter::getInstance();
  5797→            $deleted = $adapter->deleteThread($threadId);
  5798→            jsonResponse(['success' => true, 'deleted' => $deleted]);
  5799→            break;
  5800→
  5801→        // ==========================================
  5802→        // Delete AI Response - single response by ID
  5803→        // ==========================================
  5804→        case 'delete_ai_response':
  5805→            if ($_SERVER['REQUEST_METHOD'] !== 'POST') jsonError('POST required', 405);

        case 'delete_thread':
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') jsonError('POST required', 405);
            $jsonData = json_decode(file_get_contents('php://input'), true) ?? [];
            $threadId = $jsonData['thread_id'] ?? '';
            if (!$threadId) jsonError('thread_id required');
            $adapter = QueryAdapter::getInstance();
            $deleted = $adapter->deleteThread($threadId);
            jsonResponse(['success' => true, 'deleted' => $deleted]);
            break;

        // ==========================================
        // Delete AI Response - single response by ID
        // ==========================================
        case 'delete_ai_response':
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') jsonError('POST required', 405);


Edit

        // ==========================================
        // Bulk Delete Threads - by explicit thread IDs
        // ==========================================
        case 'delete_threads_bulk':
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') jsonError('POST required', 405);
            $jsonData = json_decode(file_get_contents('php://input'), true) ?? [];
            $threadIds = $jsonData['thread_ids'] ?? [];
            if (!is_array($threadIds) || empty($threadIds)) jsonError('thread_ids array required');
            // Safety cap — refuse to delete more than 500 at once
            if (count($threadIds) > 500) jsonError('Maximum 500 threads per bulk delete');
            $adapter = QueryAdapter::getInstance();
            $totalDeleted = ['emails' => 0, 'thread_analysis' => 0, 'ai_query_responses' => 0, 'threads' => 0];
            foreach ($threadIds as $tid) {
                $tid = trim((string)$tid);
                if (!$tid) continue;
                $result = $adapter->deleteThread($tid);
                $totalDeleted['emails'] += ($result['emails'] ?? 0);
                $totalDeleted['thread_analysis'] += ($result['thread_analysis'] ?? 0);
                $totalDeleted['ai_query_responses'] += ($result['ai_query_responses'] ?? 0);
                $totalDeleted['threads']++;
            }
            jsonResponse(['success' => true, 'deleted' => $totalDeleted]);
            break;

        // ==========================================
        // Delete AI Response - single response by ID
        // ==========================================
        case 'delete_ai_response':