( threadID uuid.UUID, interceptions []database.AIBridgeInterception, tokensByInterception map[uuid.UUID][]database.AIBridgeTokenUsage, toolsByInterception map[uuid.UUID][]database.AIBridgeToolUsage, promptsByInterception map[uuid.UUID][]database.AIBridgeUserPrompt, thoughtsByInterception map[uuid.UUID][]database.AIBridgeModelThought, )
| 1318 | } |
| 1319 | |
| 1320 | func buildAIBridgeThread( |
| 1321 | threadID uuid.UUID, |
| 1322 | interceptions []database.AIBridgeInterception, |
| 1323 | tokensByInterception map[uuid.UUID][]database.AIBridgeTokenUsage, |
| 1324 | toolsByInterception map[uuid.UUID][]database.AIBridgeToolUsage, |
| 1325 | promptsByInterception map[uuid.UUID][]database.AIBridgeUserPrompt, |
| 1326 | thoughtsByInterception map[uuid.UUID][]database.AIBridgeModelThought, |
| 1327 | ) codersdk.AIBridgeThread { |
| 1328 | // Find the root interception (where id == threadID) to get the |
| 1329 | // thread prompt and model. |
| 1330 | var rootIntc *database.AIBridgeInterception |
| 1331 | for i := range interceptions { |
| 1332 | if interceptions[i].ID == threadID { |
| 1333 | rootIntc = &interceptions[i] |
| 1334 | break |
| 1335 | } |
| 1336 | } |
| 1337 | // Fallback to first interception if root not found. |
| 1338 | if rootIntc == nil && len(interceptions) > 0 { |
| 1339 | rootIntc = &interceptions[0] |
| 1340 | } |
| 1341 | |
| 1342 | thread := codersdk.AIBridgeThread{ |
| 1343 | ID: threadID, |
| 1344 | } |
| 1345 | if rootIntc != nil { |
| 1346 | thread.Model = rootIntc.Model |
| 1347 | thread.Provider = rootIntc.Provider |
| 1348 | thread.CredentialKind = string(rootIntc.CredentialKind) |
| 1349 | thread.CredentialHint = sanitizeCredentialHint(rootIntc.CredentialHint) |
| 1350 | // Get first user prompt from root interception. |
| 1351 | // A thread can only have one prompt, by definition, since we currently |
| 1352 | // only store the last prompt observed in an interception. |
| 1353 | if prompts := promptsByInterception[rootIntc.ID]; len(prompts) > 0 { |
| 1354 | thread.Prompt = &prompts[0].Prompt |
| 1355 | } |
| 1356 | } |
| 1357 | |
| 1358 | // Compute thread time bounds from interceptions. |
| 1359 | for _, intc := range interceptions { |
| 1360 | if thread.StartedAt.IsZero() || intc.StartedAt.Before(thread.StartedAt) { |
| 1361 | thread.StartedAt = intc.StartedAt |
| 1362 | } |
| 1363 | if intc.EndedAt.Valid { |
| 1364 | if thread.EndedAt == nil || intc.EndedAt.Time.After(*thread.EndedAt) { |
| 1365 | t := intc.EndedAt.Time |
| 1366 | thread.EndedAt = &t |
| 1367 | } |
| 1368 | } |
| 1369 | } |
| 1370 | |
| 1371 | // Build agentic actions grouped by interception. Each interception that |
| 1372 | // has tool calls produces one action with all its tool calls, thinking |
| 1373 | // blocks, and token usage. |
| 1374 | var actions []codersdk.AIBridgeAgenticAction |
| 1375 | for _, intc := range interceptions { |
| 1376 | tools := toolsByInterception[intc.ID] |
| 1377 | if len(tools) == 0 { |
no test coverage detected