@@ -1025,7 +1025,7 @@ async fn submit_prompt_and_tail(
10251025 include_partial_messages : bool ,
10261026) -> Result < TranscriptState > {
10271027 tail. prepare_offset ( ) ?;
1028- submit_prompt_to_tty ( process, tail, prompt) . await ?;
1028+ submit_prompt_to_tty ( process, tail, prompt, true ) . await ?;
10291029 tail_until_complete ( process, tail, output_format, include_partial_messages) . await
10301030}
10311031
@@ -1040,7 +1040,13 @@ async fn submit_prompt_and_tail_stream(
10401040 include_partial_messages : bool ,
10411041) -> Result < TranscriptState > {
10421042 tail. prepare_offset ( ) ?;
1043- submit_prompt_to_tty ( process, tail, prompt) . await ?;
1043+ submit_prompt_to_tty (
1044+ process,
1045+ tail,
1046+ prompt,
1047+ sdk_state. sdk_mcp_server_names ( ) . is_empty ( ) ,
1048+ )
1049+ . await ?;
10441050 tail_until_complete_stream (
10451051 process,
10461052 tail,
@@ -1057,11 +1063,29 @@ async fn submit_prompt_to_tty(
10571063 process : & mut PtyProcess ,
10581064 tail : & mut TailCursor ,
10591065 prompt : & str ,
1066+ confirm_via_ack : bool ,
10601067) -> Result < ( ) > {
10611068 logging:: event ( format ! (
10621069 "prompt_submit start content_chars={}" ,
10631070 prompt. chars( ) . count( )
10641071 ) ) ;
1072+ if !confirm_via_ack {
1073+ // Plain submit: paste, and press Enter only if the prompt is still
1074+ // visibly being edited. Used when an SDK MCP runtime is active — its
1075+ // control-channel negotiation (initialize/tools-list/tools-call) would be
1076+ // disrupted by the ACK path's clear-and-re-paste.
1077+ process. write_all ( & bracketed_paste_input ( prompt) ) ?;
1078+ tokio:: time:: sleep ( Duration :: from_millis ( 120 ) ) . await ;
1079+ maybe_log_submit_tty_diagnostic ( process, "after_paste" ) ;
1080+ if tty_output_still_editing_prompt ( & process. recent_output ( ) , prompt) {
1081+ logging:: event ( "prompt_submit_retry reason=prompt_still_visible" ) ;
1082+ process. write_all ( b"\r " ) ?;
1083+ tokio:: time:: sleep ( Duration :: from_millis ( 120 ) ) . await ;
1084+ maybe_log_submit_tty_diagnostic ( process, "after_retry_enter" ) ;
1085+ }
1086+ logging:: event ( "prompt_submit done" ) ;
1087+ return Ok ( ( ) ) ;
1088+ }
10651089 // Confirm the submission via the transcript (an ACK that Claude actually
10661090 // accepted the message) instead of trusting the on-screen state. When Claude
10671091 // is slow to become input-ready — connecting MCP servers, or right after the
@@ -1096,7 +1120,7 @@ async fn submit_prompt_to_tty(
10961120 tokio:: time:: sleep ( Duration :: from_millis ( 120 ) ) . await ;
10971121 maybe_log_submit_tty_diagnostic ( process, "after_retry_enter" ) ;
10981122 }
1099- if wait_for_transcript_ack ( tail, ACK_TIMEOUT ) . await ? {
1123+ if wait_for_transcript_ack ( process , tail, ACK_TIMEOUT ) . await ? {
11001124 logging:: event ( format ! ( "prompt_submit done attempt={attempt} ack=true" ) ) ;
11011125 return Ok ( ( ) ) ;
11021126 }
@@ -1105,11 +1129,22 @@ async fn submit_prompt_to_tty(
11051129 Ok ( ( ) )
11061130}
11071131
1108- /// Wait until the target transcript shows new bytes — Claude's acknowledgement
1109- /// that it accepted the submitted prompt — or the timeout elapses.
1110- async fn wait_for_transcript_ack ( tail : & mut TailCursor , timeout : Duration ) -> Result < bool > {
1132+ /// Confirm Claude accepted the submitted prompt. Returns true as soon as the
1133+ /// transcript shows new bytes, or Claude is clearly producing (busy) / has
1134+ /// finished a turn that leaves no transcript (cctty synthesizes it downstream).
1135+ /// Returns false only while Claude sits at a clean, idle prompt with no
1136+ /// transcript — i.e. the paste didn't land and the caller should re-submit. This
1137+ /// reuses the same completion heuristics as the tail loop so fake-Claude /
1138+ /// no-transcript turns aren't mistaken for a dropped paste.
1139+ async fn wait_for_transcript_ack (
1140+ process : & mut PtyProcess ,
1141+ tail : & mut TailCursor ,
1142+ timeout : Duration ,
1143+ ) -> Result < bool > {
11111144 let deadline = Instant :: now ( ) + timeout;
1145+ let mut progress = TtyVisibleProgress :: new ( process) ;
11121146 loop {
1147+ progress. observe ( process) ;
11131148 if let Some ( path) = tail. resolve_path ( ) ? {
11141149 if std:: fs:: metadata ( & path)
11151150 . map ( |meta| meta. len ( ) > tail. offset )
@@ -1118,10 +1153,15 @@ async fn wait_for_transcript_ack(tail: &mut TailCursor, timeout: Duration) -> Re
11181153 return Ok ( true ) ;
11191154 }
11201155 }
1156+ if tty_wait_class ( & process. recent_output ( ) ) != "prompt_ready"
1157+ || progress. completed_without_transcript ( process)
1158+ {
1159+ return Ok ( true ) ;
1160+ }
11211161 if Instant :: now ( ) >= deadline {
11221162 return Ok ( false ) ;
11231163 }
1124- tokio:: time:: sleep ( Duration :: from_millis ( 250 ) ) . await ;
1164+ tokio:: time:: sleep ( Duration :: from_millis ( 200 ) ) . await ;
11251165 }
11261166}
11271167
0 commit comments