@@ -114,6 +114,13 @@ local function help_lines()
114114 }
115115end
116116
117+ local function is_session_not_attached_error (err )
118+ if type (err ) ~= ' string' then
119+ return false
120+ end
121+ return err :lower ():find (' session is not attached to this service' , 1 , true ) ~= nil
122+ end
123+
117124local function show_help_popup ()
118125 local lines = help_lines ()
119126 local max_w = 0
@@ -1256,6 +1263,60 @@ function M.ask(prompt, opts)
12561263 local function dispatch_prompt ()
12571264 local prompt_body = text
12581265 local included_restore_context = false
1266+ local body = { prompt = prompt_body }
1267+
1268+ local function cleanup_temp_files ()
1269+ for _ , p in ipairs (temp_files ) do
1270+ pcall (os.remove , p )
1271+ end
1272+ end
1273+
1274+ local function finalize_failure (request_err )
1275+ state .pending_checkpoint_turn = nil
1276+ state .active_turn_assistant_index = nil
1277+ state .live_assistant_entry_index = nil
1278+ state .active_turn_assistant_message_id = nil
1279+ state .active_assistant_merge_group = nil
1280+ state .active_tool = nil
1281+ state .active_tool_run_id = nil
1282+ state .active_tool_detail = nil
1283+ state .pending_tool_detail = nil
1284+ state .overlay_tool_display = nil
1285+ state .overlay_tool_queue = {}
1286+ state .overlay_tool_schedule_token = (tonumber (state .overlay_tool_schedule_token ) or 0 ) + 1
1287+ state .post_tool_use_hooks = {}
1288+ state .recent_activity_lines = {}
1289+ state .recent_activity_items = {}
1290+ state .recent_activity_tool_calls = {}
1291+ state .current_intent = nil
1292+ state .chat_busy = false
1293+ refresh_statuslines ()
1294+ refresh_reasoning_overlay (true )
1295+ append_entry (' error' , ' Failed to send prompt: ' .. request_err )
1296+ end
1297+
1298+ local function send_once ()
1299+ request (' POST' , string.format (' /sessions/%s/messages' , session_id ), body , function (_ , request_err )
1300+ if request_err and is_session_not_attached_error (request_err ) then
1301+ -- Recover the session so the next user prompt will succeed, but do
1302+ -- NOT retry automatically — retrying risks sending the prompt twice.
1303+ require (' copilot_agent.session' ).recover_after_service_restart (session_id , function () end )
1304+ cleanup_temp_files ()
1305+ finalize_failure (request_err .. ' (session recovered — please resend)' )
1306+ return
1307+ end
1308+
1309+ cleanup_temp_files ()
1310+ if request_err then
1311+ finalize_failure (request_err )
1312+ return
1313+ end
1314+ if included_restore_context then
1315+ state .pending_session_context = nil
1316+ end
1317+ end )
1318+ end
1319+
12591320 local pending_context = state .pending_session_context
12601321 if type (pending_context ) == ' table' and type (pending_context .text ) == ' string' and pending_context .text ~= ' ' then
12611322 if pending_context .session_id == nil or pending_context .session_id == session_id then
@@ -1271,6 +1332,8 @@ function M.ask(prompt, opts)
12711332 end
12721333 end
12731334
1335+ body .prompt = prompt_body
1336+
12741337 require (' copilot_agent' ).open_chat ({
12751338 activate_input_on_session_ready = false ,
12761339 replace_current = opts .replace_current ,
@@ -1286,44 +1349,11 @@ function M.ask(prompt, opts)
12861349 refresh_statuslines ()
12871350 schedule_render ()
12881351
1289- local body = { prompt = prompt_body }
12901352 body .clientId = service .client_id ()
12911353 if # api_attachments > 0 then
12921354 body .attachments = api_attachments
12931355 end
1294- request (' POST' , string.format (' /sessions/%s/messages' , session_id ), body , function (_ , request_err )
1295- -- Clean up any clipboard temp PNGs — the HTTP request has been delivered.
1296- for _ , p in ipairs (temp_files ) do
1297- pcall (os.remove , p )
1298- end
1299- if request_err then
1300- state .pending_checkpoint_turn = nil
1301- state .active_turn_assistant_index = nil
1302- state .live_assistant_entry_index = nil
1303- state .active_turn_assistant_message_id = nil
1304- state .active_assistant_merge_group = nil
1305- state .active_tool = nil
1306- state .active_tool_run_id = nil
1307- state .active_tool_detail = nil
1308- state .pending_tool_detail = nil
1309- state .overlay_tool_display = nil
1310- state .overlay_tool_queue = {}
1311- state .overlay_tool_schedule_token = (tonumber (state .overlay_tool_schedule_token ) or 0 ) + 1
1312- state .post_tool_use_hooks = {}
1313- state .recent_activity_lines = {}
1314- state .recent_activity_items = {}
1315- state .recent_activity_tool_calls = {}
1316- state .current_intent = nil
1317- state .chat_busy = false
1318- refresh_statuslines ()
1319- refresh_reasoning_overlay (true )
1320- append_entry (' error' , ' Failed to send prompt: ' .. request_err )
1321- return
1322- end
1323- if included_restore_context then
1324- state .pending_session_context = nil
1325- end
1326- end )
1356+ send_once ()
13271357 end
13281358
13291359 dispatch_prompt ()
0 commit comments