( chatIds: string[], label: string )
| 124 | * Chunked at 1000 per request. |
| 125 | */ |
| 126 | export async function cleanupCopilotBackend( |
| 127 | chatIds: string[], |
| 128 | label: string |
| 129 | ): Promise<{ deleted: number; failed: number }> { |
| 130 | const stats = { deleted: 0, failed: 0 } |
| 131 | |
| 132 | if (chatIds.length === 0 || !env.COPILOT_API_KEY) { |
| 133 | if (!env.COPILOT_API_KEY) { |
| 134 | logger.warn(`[${label}] COPILOT_API_KEY not set, skipping copilot backend cleanup`) |
| 135 | } |
| 136 | return stats |
| 137 | } |
| 138 | |
| 139 | for (let i = 0; i < chatIds.length; i += COPILOT_CLEANUP_BATCH_SIZE) { |
| 140 | const chunk = chatIds.slice(i, i + COPILOT_CLEANUP_BATCH_SIZE) |
| 141 | try { |
| 142 | const response = await fetch(`${SIM_AGENT_API_URL}/api/tasks/cleanup`, { |
| 143 | method: 'POST', |
| 144 | headers: { |
| 145 | 'Content-Type': 'application/json', |
| 146 | 'x-api-key': env.COPILOT_API_KEY, |
| 147 | }, |
| 148 | body: JSON.stringify({ chatIds: chunk }), |
| 149 | }) |
| 150 | |
| 151 | if (!response.ok) { |
| 152 | const errorBody = await response.text().catch(() => '') |
| 153 | logger.error(`[${label}] Copilot backend cleanup failed: ${response.status}`, { |
| 154 | errorBody, |
| 155 | chatCount: chunk.length, |
| 156 | }) |
| 157 | stats.failed += chunk.length |
| 158 | continue |
| 159 | } |
| 160 | |
| 161 | const result = await response.json() |
| 162 | stats.deleted += result.deleted ?? 0 |
| 163 | logger.info( |
| 164 | `[${label}] Copilot backend cleanup: ${result.deleted} chats deleted (batch ${Math.floor(i / COPILOT_CLEANUP_BATCH_SIZE) + 1})` |
| 165 | ) |
| 166 | } catch (error) { |
| 167 | stats.failed += chunk.length |
| 168 | logger.error(`[${label}] Copilot backend cleanup request failed:`, { error }) |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | return stats |
| 173 | } |
| 174 | |
| 175 | /** |
| 176 | * Full chat cleanup: collect file refs, then (after DB deletion by caller) |
no test coverage detected