@@ -177,7 +177,22 @@ export async function runInteractive({
177177 let cursor = 0 ;
178178 let monitor = null ;
179179 let threadConnection = null ;
180- const agentPane = createAgentPane ( stdout ) ;
180+ let promptActive = false ;
181+ let promptLabel = "multiagent> " ;
182+ const refreshPrompt = ( ) => {
183+ if ( ! promptActive ) return ;
184+ if ( typeof terminal . setPrompt !== "function" || typeof terminal . prompt !== "function" ) return ;
185+ terminal . setPrompt ( promptLabel ) ;
186+ terminal . prompt ( true ) ;
187+ } ;
188+ const interactiveOutput = {
189+ write ( value ) {
190+ if ( promptActive && stdout . isTTY ) stdout . write ( "\r\u001b[2K" ) ;
191+ stdout . write ( value ) ;
192+ refreshPrompt ( ) ;
193+ } ,
194+ } ;
195+ const agentPane = createAgentPane ( stdout , { onDraw : refreshPrompt } ) ;
181196
182197 const listThreads = async ( ) => {
183198 threads = ( await client . request ( "/api/threads" ) ) . value . threads || [ ] ;
@@ -202,7 +217,7 @@ export async function runInteractive({
202217 const sequence = Number ( event . sequence ) || 0 ;
203218 if ( sequence <= cursor ) continue ;
204219 cursor = sequence ;
205- renderInteractiveEvent ( stdout , event ) ;
220+ renderInteractiveEvent ( interactiveOutput , event ) ;
206221 }
207222 return events ;
208223 } ;
@@ -238,15 +253,18 @@ export async function runInteractive({
238253 createWebSocketImpl,
239254 onState : ( state ) => agentPane . setConnectionState ( state ) ,
240255 onThread : ( thread ) => {
241- if ( current ?. id === threadId ) current = thread ;
256+ if ( current ?. id === threadId ) {
257+ current = thread ;
258+ agentPane . setThread ( thread ) ;
259+ }
242260 } ,
243261 onAgents : ( agents ) => agentPane . render ( agents ) ,
244262 onEvent : ( event ) => {
245263 if ( current ?. id !== threadId ) return ;
246264 const sequence = Number ( event . sequence ) || 0 ;
247265 if ( sequence <= cursor ) return ;
248266 cursor = sequence ;
249- renderInteractiveEvent ( stdout , event ) ;
267+ renderInteractiveEvent ( interactiveOutput , event ) ;
250268 if ( new Set ( [ "assistant_message" , "question" , "session_interrupted" ] ) . has ( event . type ) ) {
251269 monitor ?. controller . abort ( ) ;
252270 }
@@ -263,7 +281,7 @@ export async function runInteractive({
263281 const active = { sessionId, controller, promise : null } ;
264282 monitor = active ;
265283 active . promise = ( async ( ) => {
266- const stream = streamSessionTerminal ( { client, sessionId, stdout, sleep, signal : controller . signal , createWebSocketImpl } ) ;
284+ const stream = streamSessionTerminal ( { client, sessionId, stdout : interactiveOutput , sleep, signal : controller . signal , createWebSocketImpl } ) ;
267285 try {
268286 while ( ! controller . signal . aborted ) {
269287 const events = await replay ( ) ;
@@ -286,6 +304,7 @@ export async function runInteractive({
286304 await stopMonitor ( ) ;
287305 await stopThreadConnection ( ) ;
288306 current = response . value . thread ;
307+ agentPane . setThread ( current ) ;
289308 cursor = 0 ;
290309 stdout . write ( `\nOpened ${ current . id } [${ current . state } ] — ${ current . repository } \n` ) ;
291310 await replay ( { all : true } ) ;
@@ -302,7 +321,15 @@ export async function runInteractive({
302321 await listThreads ( ) ;
303322 if ( initialThreadId ) await openThread ( initialThreadId ) ;
304323 while ( true ) {
305- const line = String ( await terminal . question ( current ? `${ current . id } > ` : "multiagent> " ) ) . trim ( ) ;
324+ promptLabel = current ? `${ current . id } > ` : "multiagent> " ;
325+ promptActive = true ;
326+ let answer ;
327+ try {
328+ answer = await terminal . question ( promptLabel ) ;
329+ } finally {
330+ promptActive = false ;
331+ }
332+ const line = String ( answer ) . trim ( ) ;
306333 if ( ! line ) continue ;
307334 try {
308335 if ( line === "/quit" || line === "/exit" ) return 0 ;
@@ -332,6 +359,7 @@ export async function runInteractive({
332359 await stopMonitor ( ) ;
333360 await stopThreadConnection ( ) ;
334361 current = created . value . thread ;
362+ agentPane . setThread ( current ) ;
335363 cursor = 0 ;
336364 startThreadConnection ( current . id ) ;
337365 await listThreads ( ) ;
@@ -349,11 +377,14 @@ export async function runInteractive({
349377 const sequence = Number ( routed . event . sequence ) || 0 ;
350378 if ( sequence > cursor ) {
351379 cursor = sequence ;
352- renderInteractiveEvent ( stdout , routed . event ) ;
380+ renderInteractiveEvent ( interactiveOutput , routed . event ) ;
353381 }
354382 }
355383 const session = routed . session ;
356- if ( session ) stdout . write ( `[execution ${ session . status } ] ${ session . id } \n` ) ;
384+ if ( session ) {
385+ agentPane . setThread ( current , session . status ) ;
386+ stdout . write ( `[execution ${ session . status } ] ${ session . id } \n` ) ;
387+ }
357388 await startMonitor ( session ?. id || "" ) ;
358389 } catch ( error ) {
359390 if ( ! ( error instanceof ClientError ) ) throw error ;
@@ -634,19 +665,27 @@ function claudeStreamProgress(lines) {
634665
635666const inactiveAgentStatuses = new Set ( [ "done" , "completed" , "closed" , "cancelled" , "canceled" , "failed" , "released" , "skipped" , "finalized" , "killed" , "missing" ] ) ;
636667
637- export function renderAgentPane ( agents , { columns = 80 , maxRows = 6 , connectionState = "connected" } = { } ) {
668+ export function renderAgentPane ( agents , {
669+ columns = 80 ,
670+ maxRows = 6 ,
671+ connectionState = "connected" ,
672+ thread = null ,
673+ executionStatus = "" ,
674+ } = { } ) {
638675 const values = Array . isArray ( agents ) ? agents : [ ] ;
639676 const active = values . filter ( ( agent ) => ! inactiveAgentStatuses . has ( String ( agent . status || "" ) . toLowerCase ( ) ) ) . length ;
640- const header = `Subagents | ${ connectionState } | ${ active } active, ${ values . length } total` ;
641- const rows = values . slice ( 0 , Math . max ( 0 , maxRows - 1 ) ) . map ( ( agent ) => {
677+ const headers = [ ] ;
678+ if ( thread ?. id ) headers . push ( `Thread ${ thread . id } | orchestrator ${ executionStatus || thread . state || "idle" } ` ) ;
679+ headers . push ( `Subagents | ${ connectionState } | ${ active } active, ${ values . length } total` ) ;
680+ const rows = values . slice ( 0 , Math . max ( 0 , maxRows - headers . length ) ) . map ( ( agent ) => {
642681 const status = String ( agent . status || "unknown" ) ;
643682 const role = agent . role ? ` (${ agent . role } )` : "" ;
644683 const work = String ( agent . workingOn || agent . assignment || "waiting" ) ;
645684 return `${ inactiveAgentStatuses . has ( status . toLowerCase ( ) ) ? "-" : ">" } ${ agent . name || "agent" } [${ status } ]${ role } : ${ work } ` ;
646685 } ) ;
647- if ( values . length > rows . length ) rows . push ( `... ${ values . length - rows . length } more` ) ;
648- if ( ! rows . length && maxRows > 1 ) rows . push ( " No subagents reported yet" ) ;
649- return [ header , ...rows ] . slice ( 0 , maxRows ) . map ( ( line ) => truncateTerminalLine ( line , columns ) ) ;
686+ if ( values . length > rows . length && headers . length + rows . length < maxRows ) rows . push ( `... ${ values . length - rows . length } more` ) ;
687+ if ( ! rows . length && maxRows > headers . length ) rows . push ( " No subagents reported yet" ) ;
688+ return [ ... headers , ...rows ] . slice ( 0 , maxRows ) . map ( ( line ) => truncateTerminalLine ( line , columns ) ) ;
650689}
651690
652691function truncateTerminalLine ( value , columns ) {
@@ -656,10 +695,12 @@ function truncateTerminalLine(value, columns) {
656695 return width <= 3 ? line . slice ( 0 , width ) : `${ line . slice ( 0 , width - 3 ) } ...` ;
657696}
658697
659- function createAgentPane ( stdout ) {
698+ function createAgentPane ( stdout , { onDraw = ( ) => { } } = { } ) {
660699 const enabled = Boolean ( stdout ?. isTTY && Number ( stdout . rows ) >= 8 ) ;
661700 let agents = [ ] ;
662701 let connectionState = "disconnected" ;
702+ let thread = null ;
703+ let executionStatus = "" ;
663704 let panel = null ;
664705 let lastFrame = "" ;
665706
@@ -682,7 +723,13 @@ function createAgentPane(stdout) {
682723 const mainBottom = start - 1 ;
683724 if ( panel && ( panel . rows !== rows || panel . start !== start ) ) clear ( ) ;
684725 panel = { rows, start } ;
685- const lines = renderAgentPane ( agents , { columns, maxRows : height , connectionState } ) ;
726+ const lines = renderAgentPane ( agents , {
727+ columns,
728+ maxRows : height ,
729+ connectionState,
730+ thread,
731+ executionStatus,
732+ } ) ;
686733 const frame = JSON . stringify ( { rows, columns, height, lines } ) ;
687734 if ( frame === lastFrame ) return ;
688735 lastFrame = frame ;
@@ -692,6 +739,7 @@ function createAgentPane(stdout) {
692739 }
693740 output += "\u001b8" ;
694741 stdout . write ( output ) ;
742+ onDraw ( ) ;
695743 } ;
696744
697745 return {
@@ -704,6 +752,11 @@ function createAgentPane(stdout) {
704752 connectionState = next ;
705753 draw ( ) ;
706754 } ,
755+ setThread ( nextThread , nextExecutionStatus = "" ) {
756+ thread = nextThread || null ;
757+ executionStatus = nextExecutionStatus || "" ;
758+ draw ( ) ;
759+ } ,
707760 close : clear ,
708761 } ;
709762}
0 commit comments