@@ -49,6 +49,7 @@ define(function (require, exports, module) {
4949 let _currentEdits = [ ] ; // edits in current response, for summary card
5050 let _firstEditInResponse = true ; // tracks first edit per response for initial PUC
5151 let _undoApplied = false ; // whether undo/restore has been clicked on any card
52+ let _sessionError = false ; // set when aiError fires; cleared on new send or new session
5253 // --- AI event trace logging (compact, non-flooding) ---
5354 let _traceTextChunks = 0 ;
5455 let _traceToolStreamCounts = { } ; // toolId → count
@@ -767,6 +768,7 @@ define(function (require, exports, module) {
767768 $textarea . css ( "height" , "auto" ) ;
768769
769770 // Set streaming state
771+ _sessionError = false ;
770772 _setStreaming ( true ) ;
771773
772774 // Reset segment tracking and show thinking indicator
@@ -870,6 +872,7 @@ define(function (require, exports, module) {
870872 _segmentText = "" ;
871873 _hasReceivedContent = false ;
872874 _isStreaming = false ;
875+ _sessionError = false ;
873876 _queuedMessage = null ;
874877 _removeQueueBubble ( ) ;
875878 _firstEditInResponse = true ;
@@ -1260,6 +1263,7 @@ define(function (require, exports, module) {
12601263
12611264 function _onError ( _event , data ) {
12621265 console . log ( "[AI UI]" , "Error:" , ( data . error || "" ) . slice ( 0 , 200 ) ) ;
1266+ _sessionError = true ;
12631267 _appendErrorMessage ( data . error ) ;
12641268 // Don't stop streaming — the node side may continue (partial results)
12651269 }
@@ -1279,6 +1283,46 @@ define(function (require, exports, module) {
12791283 SnapshotStore . stopTracking ( ) ;
12801284 _setStreaming ( false ) ;
12811285
1286+ // Fatal error (e.g. process exit code 1) — disable inputs, show "New Chat"
1287+ if ( _sessionError && ! data . sessionId ) {
1288+ $textarea . prop ( "disabled" , true ) ;
1289+ $textarea . closest ( ".ai-chat-input-wrap" ) . addClass ( "disabled" ) ;
1290+ $sendBtn . prop ( "disabled" , true ) ;
1291+ // Move queued text back to textarea for user to reuse after new session
1292+ if ( _queuedMessage ) {
1293+ $textarea . val ( _queuedMessage . text ) ;
1294+ _attachedImages = _queuedMessage . images ;
1295+ _renderImagePreview ( ) ;
1296+ _queuedMessage = null ;
1297+ _removeQueueBubble ( ) ;
1298+ }
1299+ // Append inline "New Chat" button below the error
1300+ const $newChat = $ (
1301+ '<div class="ai-msg ai-msg-new-chat">' +
1302+ '<button class="ai-error-new-chat-btn">' +
1303+ '<i class="fa-solid fa-plus"></i> ' + Strings . AI_CHAT_NEW_BTN +
1304+ '</button>' +
1305+ '</div>'
1306+ ) ;
1307+ $newChat . find ( ".ai-error-new-chat-btn" ) . on ( "click" , function ( ) {
1308+ // Preserve textarea content and images across the new session
1309+ const savedText = $textarea . val ( ) ;
1310+ const savedImages = _attachedImages . slice ( ) ;
1311+ $newChat . remove ( ) ;
1312+ _newSession ( ) ;
1313+ $textarea . prop ( "disabled" , false ) ;
1314+ $textarea . closest ( ".ai-chat-input-wrap" ) . removeClass ( "disabled" ) ;
1315+ $sendBtn . prop ( "disabled" , false ) ;
1316+ $textarea . val ( savedText ) ;
1317+ _attachedImages = savedImages ;
1318+ _renderImagePreview ( ) ;
1319+ $textarea [ 0 ] . focus ( { preventScroll : true } ) ;
1320+ } ) ;
1321+ $messages . append ( $newChat ) ;
1322+ _scrollToBottom ( ) ;
1323+ return ;
1324+ }
1325+
12821326 // If user had a queued message, auto-send it as the next turn
12831327 if ( _queuedMessage ) {
12841328 const pending = _queuedMessage ;
0 commit comments