* Persist the user message and assistant response to the copilot chat. * This is necessary because the orchestrator doesn't persist messages — * in the interactive UI flow, the client store handles persistence. * For background execution, we write directly to the DB.
( chatId: string, userMessageId: string, userContent: string, result: OrchestratorResult, storedAttachments: StoredAttachment[] = [] )
| 354 | * For background execution, we write directly to the DB. |
| 355 | */ |
| 356 | async function persistChatMessages( |
| 357 | chatId: string, |
| 358 | userMessageId: string, |
| 359 | userContent: string, |
| 360 | result: OrchestratorResult, |
| 361 | storedAttachments: StoredAttachment[] = [] |
| 362 | ): Promise<void> { |
| 363 | try { |
| 364 | const userMessage = buildPersistedUserMessage({ |
| 365 | id: userMessageId, |
| 366 | content: userContent, |
| 367 | fileAttachments: storedAttachments.length > 0 ? storedAttachments : undefined, |
| 368 | }) |
| 369 | |
| 370 | const assistantMessage = buildPersistedAssistantMessage(result) |
| 371 | |
| 372 | // Best-effort: the email response is the primary deliverable, so a failure |
| 373 | // here is logged (in the catch below) rather than failing the task. |
| 374 | await db.transaction(async (tx) => { |
| 375 | const [updated] = await tx |
| 376 | .update(copilotChats) |
| 377 | .set({ updatedAt: new Date() }) |
| 378 | .where(eq(copilotChats.id, chatId)) |
| 379 | .returning({ model: copilotChats.model }) |
| 380 | if (!updated) return |
| 381 | await appendCopilotChatMessages( |
| 382 | chatId, |
| 383 | [userMessage, assistantMessage], |
| 384 | { chatModel: updated.model ?? null }, |
| 385 | tx |
| 386 | ) |
| 387 | }) |
| 388 | } catch (err) { |
| 389 | logger.error('Failed to persist chat messages', { |
| 390 | chatId, |
| 391 | error: getErrorMessage(err, 'Unknown error'), |
| 392 | }) |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | function stripThinkingTags(text: string): string { |
| 397 | return text |
no test coverage detected