fix: settle busy sessions after managed OpenCode restart (#3002)

* fix: reconcile busy sessions after managed OpenCode restart

Forced health-check restarts previously rebound the event stream without
settling in-flight turns, so sessions stayed busy with no terminal state.
Interrupt those sessions, classify health failures, and retain bounded
process diagnostics for post-restart diagnosis.

Fixes #2943

Co-authored-by: serkraser <serkraser@gmail.com>

* fix: surface interrupted chats after OpenCode restart

Complete unfinished assistant turns as aborted once the session is
authoritatively idle, and show a persistent toast so users can continue
instead of remaining silently stranded.

Fixes #2943

Co-authored-by: serkraser <serkraser@gmail.com>

* fix: redact Basic auth credentials in restart diagnostics

The key/value sanitizer stopped at whitespace, so Authorization: Basic
credentials survived in stderr tails and health snapshots. Redact the
scheme token before that rule runs.

Co-authored-by: serkraser <serkraser@gmail.com>
This commit is contained in:
Serhii Dziupin
2026-08-19 11:53:30 +03:00
committed by GitHub
parent 13a45aa5a5
commit 14d7a0ca9b
24 changed files with 806 additions and 80 deletions
+3
View File
@@ -1901,6 +1901,9 @@ export const dict = {
'chat.revert.toast.undo': 'Zurückgesetzt auf {preview}',
'chat.revert.toast.redo': 'Wiederholt',
'chat.revert.toast.restored': 'Alle Nachrichten wiederhergestellt',
'chat.toast.opencodeRestartInterrupted.title': 'Chat unterbrochen',
'chat.toast.opencodeRestartInterrupted.description': 'OpenCode wurde neu gestartet, während noch eine Antwort lief. Senden Sie eine Nachricht, um fortzufahren.',
'chat.toast.opencodeRestartInterrupted.openSession': 'Sitzung öffnen',
'chat.errorBoundary.title': 'Chat-Fehler',
'chat.errorBoundary.description': 'Die Chat-Oberfläche hat einen Fehler festgestellt. Dies könnte auf ein vorübergehendes Netzwerkproblem oder beschädigte Nachrichtendaten zurückzuführen sein.',
'chat.errorBoundary.sessionLabel': 'Sitzung',
+3
View File
@@ -2060,6 +2060,9 @@ export const dict = {
'chat.revert.toast.undo': 'Reverted to {preview}',
'chat.revert.toast.redo': 'Redone',
'chat.revert.toast.restored': 'Restored all messages',
'chat.toast.opencodeRestartInterrupted.title': 'Chat interrupted',
'chat.toast.opencodeRestartInterrupted.description': 'OpenCode restarted while a response was still running. Send a message to continue.',
'chat.toast.opencodeRestartInterrupted.openSession': 'Open session',
'chat.errorBoundary.title': 'Chat Error',
'chat.errorBoundary.description': 'The chat interface encountered an error. This might be due to a temporary network issue or corrupted message data.',
'chat.errorBoundary.sessionLabel': 'Session',
+3
View File
@@ -2038,6 +2038,9 @@ export const dict: Record<I18nKey, string> = {
"chat.revert.toast.undo": "Revertido a {preview}",
"chat.revert.toast.redo": "Rehecho",
"chat.revert.toast.restored": "Todos los mensajes restaurados",
"chat.toast.opencodeRestartInterrupted.title": "Conversación interrumpida",
"chat.toast.opencodeRestartInterrupted.description": "OpenCode se reinició mientras aún se estaba generando una respuesta. Envía un mensaje para continuar.",
"chat.toast.opencodeRestartInterrupted.openSession": "Abrir sesión",
"chat.errorBoundary.title": "Error en la conversación",
"chat.errorBoundary.description": "La interfaz de la conversación encontró un error. Esto podría deberse a un problema de red temporal o a datos de mensaje corruptos.",
"chat.errorBoundary.sessionLabel": "Sesión",
+3
View File
@@ -1802,6 +1802,9 @@ export const dict = {
'chat.revert.toast.undo': 'Revenu à {preview}',
'chat.revert.toast.redo': 'Refait',
'chat.revert.toast.restored': 'Restauré tous les messages',
'chat.toast.opencodeRestartInterrupted.title': 'Discussion interrompue',
'chat.toast.opencodeRestartInterrupted.description': 'OpenCode a redémarré alors quune réponse était encore en cours. Envoyez un message pour continuer.',
'chat.toast.opencodeRestartInterrupted.openSession': 'Ouvrir la session',
'chat.errorBoundary.title': 'Erreur de discussion',
'chat.errorBoundary.description': 'L\'interface de discussion a rencontré une erreur. Cela peut être dû à un problème de réseau temporaire ou à des données de message corrompues.',
'chat.errorBoundary.sessionLabel': 'Session',
+3
View File
@@ -2056,6 +2056,9 @@ export const dict: Record<I18nKey, string> = {
'chat.revert.toast.undo': '{preview}に元に戻しました',
'chat.revert.toast.redo': 'やり直しました',
'chat.revert.toast.restored': 'すべてのメッセージを復元しました',
'chat.toast.opencodeRestartInterrupted.title': 'チャットが中断されました',
'chat.toast.opencodeRestartInterrupted.description': '応答の生成中に OpenCode が再起動しました。続行するにはメッセージを送信してください。',
'chat.toast.opencodeRestartInterrupted.openSession': 'セッションを開く',
'chat.errorBoundary.title': 'チャットエラー',
'chat.errorBoundary.description': 'チャットインターフェースでエラーが発生しました。一時的なネットワーク問題または破損したメッセージデータが原因の可能性があります。',
'chat.errorBoundary.sessionLabel': 'セッション',
+3
View File
@@ -2062,6 +2062,9 @@ export const dict: Record<I18nKey, string> = {
'chat.revert.toast.undo': '{preview}(으)로 되돌림',
'chat.revert.toast.redo': '다시 실행',
'chat.revert.toast.restored': '모든 메시지 복원됨',
'chat.toast.opencodeRestartInterrupted.title': '채팅이 중단되었습니다',
'chat.toast.opencodeRestartInterrupted.description': '응답이 진행 중인 동안 OpenCode가 다시 시작되었습니다. 계속하려면 메시지를 보내세요.',
'chat.toast.opencodeRestartInterrupted.openSession': '세션 열기',
'chat.errorBoundary.title': '채팅 오류',
'chat.errorBoundary.description': '채팅 인터페이스에서 오류가 발생했습니다. 일시적인 네트워크 이슈 또는 손상된 메시지 데이터 때문일 수 있습니다.',
'chat.errorBoundary.sessionLabel': '세션',
+3
View File
@@ -766,6 +766,9 @@ export const dict: Record<I18nKey, string> = {
'chat.revert.toast.undo': 'Cofnięte do {preview}',
'chat.revert.toast.redo': 'Ponowione',
'chat.revert.toast.restored': 'Przywrócono wszystkie wiadomości',
'chat.toast.opencodeRestartInterrupted.title': 'Czat został przerwany',
'chat.toast.opencodeRestartInterrupted.description': 'OpenCode uruchomił się ponownie podczas generowania odpowiedzi. Wyślij wiadomość, aby kontynuować.',
'chat.toast.opencodeRestartInterrupted.openSession': 'Otwórz sesję',
'chat.errorBoundary.title': 'Błąd Czatu',
'chat.errorBoundary.description': 'Interfejs czatu napotkał błąd. Może to być spowodowane tymczasowym problemem sieciowym lub uszkodzonymi danymi wiadomości.',
'chat.errorBoundary.sessionLabel': 'Sesja',
@@ -2038,6 +2038,9 @@ export const dict: Record<I18nKey, string> = {
"chat.revert.toast.undo": "Revertido para {preview}",
"chat.revert.toast.redo": "Refeito",
"chat.revert.toast.restored": "Todas as mensagens restauradas",
"chat.toast.opencodeRestartInterrupted.title": "Conversa interrompida",
"chat.toast.opencodeRestartInterrupted.description": "O OpenCode foi reiniciado enquanto uma resposta ainda estava em andamento. Envie uma mensagem para continuar.",
"chat.toast.opencodeRestartInterrupted.openSession": "Abrir sessão",
"chat.errorBoundary.title": "Erro na conversa",
"chat.errorBoundary.description": "A interface da conversa encontrou um erro. Isso pode ter sido causado por um problema temporário de rede ou por dados de mensagem corrompidos.",
"chat.errorBoundary.sessionLabel": "Sessão",
+3
View File
@@ -2038,6 +2038,9 @@ export const dict: Record<I18nKey, string> = {
"chat.revert.toast.undo": "Відкочено до {preview}",
"chat.revert.toast.redo": "Повторено",
"chat.revert.toast.restored": "Всі повідомлення відновлено",
"chat.toast.opencodeRestartInterrupted.title": "Чат перервано",
"chat.toast.opencodeRestartInterrupted.description": "OpenCode перезапустився, поки відповідь ще формувалася. Надішліть повідомлення, щоб продовжити.",
"chat.toast.opencodeRestartInterrupted.openSession": "Відкрити сесію",
"chat.errorBoundary.title": "Помилка чату",
"chat.errorBoundary.description": "В інтерфейсі чату сталася помилка. Причиною може бути тимчасова проблема з мережею або пошкоджені дані повідомлення.",
"chat.errorBoundary.sessionLabel": "Сесія",
@@ -2026,6 +2026,9 @@ export const dict: Record<I18nKey, string> = {
'chat.revert.toast.undo': '已撤回至 {preview}',
'chat.revert.toast.redo': '已重做',
'chat.revert.toast.restored': '已恢复全部消息',
'chat.toast.opencodeRestartInterrupted.title': '聊天已中断',
'chat.toast.opencodeRestartInterrupted.description': 'OpenCode 在回复仍在生成时重启了。发送一条消息以继续。',
'chat.toast.opencodeRestartInterrupted.openSession': '打开会话',
'chat.errorBoundary.title': '聊天错误',
'chat.errorBoundary.description': '聊天界面发生错误,可能是临时网络问题或消息数据损坏导致。',
'chat.errorBoundary.sessionLabel': '会话',
@@ -2030,6 +2030,9 @@ export const dict: Record<I18nKey, string> = {
'chat.revert.toast.undo': '已收回至 {preview}',
'chat.revert.toast.redo': '已重做',
'chat.revert.toast.restored': '已恢復全部訊息',
'chat.toast.opencodeRestartInterrupted.title': '聊天已中斷',
'chat.toast.opencodeRestartInterrupted.description': 'OpenCode 在回覆仍在產生時重新啟動。傳送訊息以繼續。',
'chat.toast.opencodeRestartInterrupted.openSession': '開啟會話',
'chat.errorBoundary.title': '聊天錯誤',
'chat.errorBoundary.description': '聊天介面發生錯誤,可能是暫時網路問題或訊息資料損毀導致。',
'chat.errorBoundary.sessionLabel': '會話',