11package root
22
33import (
4- "bufio"
54 "bytes"
65 "context"
76 "encoding/base64"
87 "fmt"
98 "io"
109 "log/slog"
1110 "os"
12- "os/signal"
1311 "path/filepath"
1412 "sort"
1513 "strings"
@@ -189,7 +187,7 @@ func doRunCommand(ctx context.Context, args []string, exec bool) error {
189187 return err
190188 }
191189 defer func () {
192- if err := agents .StopToolSets (); err != nil {
190+ if err := agents .StopToolSets (ctx ); err != nil {
193191 slog .Error ("Failed to stop tool sets" , "error" , err )
194192 }
195193 }()
@@ -363,12 +361,21 @@ func doRunCommand(ctx context.Context, args []string, exec bool) error {
363361}
364362
365363func runWithoutTUI (ctx context.Context , agentFilename string , rt runtime.Runtime , sess * session.Session , args []string ) error {
364+ // Create a cancellable context for this agentic loop and wire Ctrl+C to cancel it
365+ ctx , cancel := context .WithCancel (ctx )
366+
367+ // Ensure telemetry is initialized and add to context so runtime can access it
368+ telemetry .EnsureGlobalTelemetryInitialized ()
369+ if telemetryClient := telemetry .GetGlobalTelemetryClient (); telemetryClient != nil {
370+ ctx = telemetry .WithClient (ctx , telemetryClient )
371+ }
372+
366373 sess .Title = "Running agent"
367374 // If the last received event was an error, return it. That way the exit code
368375 // will be non-zero if the agent failed.
369376 var lastErr error
370377
371- oneLoop := func (text string , scannerConfirmations * bufio. Scanner ) error {
378+ oneLoop := func (text string , rd io. Reader ) error {
372379 userInput := strings .TrimSpace (text )
373380 if userInput == "" {
374381 return nil
@@ -383,25 +390,6 @@ func runWithoutTUI(ctx context.Context, agentFilename string, rt runtime.Runtime
383390 return nil
384391 }
385392
386- // Create a cancellable context for this agentic loop and wire Ctrl+C to cancel it
387- loopCtx , loopCancel := context .WithCancel (ctx )
388-
389- // Ensure telemetry is initialized and add to context so runtime can access it
390- telemetry .EnsureGlobalTelemetryInitialized ()
391- if telemetryClient := telemetry .GetGlobalTelemetryClient (); telemetryClient != nil {
392- loopCtx = telemetry .WithClient (loopCtx , telemetryClient )
393- }
394-
395- sigCh := make (chan os.Signal , 1 )
396- signal .Notify (sigCh , os .Interrupt )
397- go func () {
398- <- sigCh
399- // Ensure we break any inline typing output nicely
400- fmt .Println ()
401- loopCancel ()
402- }()
403- defer signal .Stop (sigCh )
404-
405393 // Parse for /attach commands in the message
406394 messageText , attachPath := parseAttachCommand (userInput )
407395
@@ -417,7 +405,7 @@ func runWithoutTUI(ctx context.Context, agentFilename string, rt runtime.Runtime
417405 lastAgent := rt .CurrentAgent ().Name ()
418406 llmIsTyping := false
419407 var lastConfirmedToolCallID string
420- for event := range rt .RunStream (loopCtx , sess ) {
408+ for event := range rt .RunStream (ctx , sess ) {
421409 agentName := event .GetAgentName ()
422410 if agentName != "" && (firstLoop || lastAgent != agentName ) {
423411 if ! firstLoop {
@@ -449,9 +437,9 @@ func runWithoutTUI(ctx context.Context, agentFilename string, rt runtime.Runtime
449437 fmt .Println ()
450438 llmIsTyping = false
451439 }
452- result := printToolCallWithConfirmation (e .ToolCall , scannerConfirmations )
440+ result := printToolCallWithConfirmation (ctx , e .ToolCall , rd )
453441 // If interrupted, skip resuming; the runtime will notice context cancellation and stop
454- if loopCtx .Err () != nil {
442+ if ctx .Err () != nil {
455443 continue
456444 }
457445 lastConfirmedToolCallID = e .ToolCall .ID // Store the ID to avoid duplicate printing
@@ -466,7 +454,7 @@ func runWithoutTUI(ctx context.Context, agentFilename string, rt runtime.Runtime
466454 lastConfirmedToolCallID = "" // Clear on reject since tool won't execute
467455 case ConfirmationAbort :
468456 // Stop the agent loop immediately
469- loopCancel ()
457+ cancel ()
470458 continue
471459 }
472460 case * runtime.ToolCallEvent :
@@ -494,7 +482,7 @@ func runWithoutTUI(ctx context.Context, agentFilename string, rt runtime.Runtime
494482 llmIsTyping = false
495483 }
496484 lowerErr := strings .ToLower (e .Error )
497- if strings .Contains (lowerErr , "context cancel" ) && loopCtx .Err () != nil { // treat Ctrl+C cancellations as non-errors
485+ if strings .Contains (lowerErr , "context cancel" ) && ctx .Err () != nil { // treat Ctrl+C cancellations as non-errors
498486 lastErr = nil
499487 } else {
500488 lastErr = fmt .Errorf ("%s" , e .Error )
@@ -538,7 +526,7 @@ func runWithoutTUI(ctx context.Context, agentFilename string, rt runtime.Runtime
538526 }
539527
540528 // If the loop ended due to Ctrl+C, inform the user succinctly
541- if loopCtx .Err () != nil {
529+ if ctx .Err () != nil {
542530 fmt .Println (yellow ("\n ⚠️ agent stopped ⚠️" ))
543531 }
544532
@@ -556,17 +544,16 @@ func runWithoutTUI(ctx context.Context, agentFilename string, rt runtime.Runtime
556544 return fmt .Errorf ("failed to read from stdin: %w" , err )
557545 }
558546
559- if err := oneLoop (string (buf ), bufio . NewScanner ( os .Stdin ) ); err != nil {
547+ if err := oneLoop (string (buf ), os .Stdin ); err != nil {
560548 return err
561549 }
562550 } else {
563- if err := oneLoop (args [1 ], bufio . NewScanner ( os .Stdin ) ); err != nil {
551+ if err := oneLoop (args [1 ], os .Stdin ); err != nil {
564552 return err
565553 }
566554 }
567555 } else {
568556 printWelcomeMessage ()
569- scanner := bufio .NewScanner (os .Stdin )
570557 firstQuestion := true
571558 for {
572559 if ! firstQuestion {
@@ -575,18 +562,15 @@ func runWithoutTUI(ctx context.Context, agentFilename string, rt runtime.Runtime
575562 fmt .Print (blue ("> " ))
576563 firstQuestion = false
577564
578- if ! scanner .Scan () {
579- break
565+ line , err := readLine (ctx , os .Stdin )
566+ if err != nil {
567+ return err
580568 }
581569
582- if err := oneLoop (scanner . Text (), scanner ); err != nil {
570+ if err := oneLoop (line , os . Stdin ); err != nil {
583571 return err
584572 }
585573 }
586-
587- if err := scanner .Err (); err != nil {
588- return err
589- }
590574 }
591575
592576 // Wrap runtime errors to prevent duplicate error messages and usage display
0 commit comments