* @brief Loads a captured snapshot into memory, resetting all in-flight turn state and * downgrading any stale Running/AwaitingConfirm tool cards to Done. */
| 2475 | * downgrading any stale Running/AwaitingConfirm tool cards to Done. |
| 2476 | */ |
| 2477 | void AI::Conversation::loadSnapshot(const QJsonObject& doc) |
| 2478 | { |
| 2479 | cancel(); |
| 2480 | |
| 2481 | m_history = doc.value(QStringLiteral("history")).toArray(); |
| 2482 | m_uiMessages = doc.value(QStringLiteral("messages")).toArray().toVariantList(); |
| 2483 | |
| 2484 | m_assistantIndex = -1; |
| 2485 | m_assistantText.clear(); |
| 2486 | m_assistantThinking.clear(); |
| 2487 | m_pendingThinkingBlocks = QJsonArray(); |
| 2488 | m_pendingToolUseBlocks = QJsonArray(); |
| 2489 | m_pendingToolResultBlocks = QJsonArray(); |
| 2490 | m_outstandingToolResults = 0; |
| 2491 | m_awaitingConfirm.clear(); |
| 2492 | setLastError(QString()); |
| 2493 | |
| 2494 | for (int i = 0; i < m_uiMessages.size(); ++i) { |
| 2495 | auto map = m_uiMessages.at(i).toMap(); |
| 2496 | auto calls = map.value(QStringLiteral("toolCalls")).toList(); |
| 2497 | bool changed = false; |
| 2498 | for (int j = 0; j < calls.size(); ++j) { |
| 2499 | auto card = calls.at(j).toMap(); |
| 2500 | const auto state = card.value(QStringLiteral("status")).toInt(); |
| 2501 | if (state == static_cast<int>(CallStatus::Running) |
| 2502 | || state == static_cast<int>(CallStatus::AwaitingConfirm)) { |
| 2503 | card[QStringLiteral("status")] = static_cast<int>(CallStatus::Done); |
| 2504 | calls[j] = card; |
| 2505 | changed = true; |
| 2506 | } |
| 2507 | } |
| 2508 | if (changed) { |
| 2509 | map.insert(QStringLiteral("toolCalls"), calls); |
| 2510 | m_uiMessages[i] = map; |
| 2511 | } |
| 2512 | } |
| 2513 | |
| 2514 | pruneHistory(); |
| 2515 | |
| 2516 | Q_EMIT messagesChanged(); |
| 2517 | } |
| 2518 | |
| 2519 | /** |
| 2520 | * @brief Returns the text of the first user row, used to title a chat. |
no test coverage detected