@@ -40,7 +40,7 @@ type Runtime interface {
4040 // CurrentAgent returns the currently active agent
4141 CurrentAgent () * agent.Agent
4242 // RunStream starts the agent's interaction loop and returns a channel of events
43- RunStream (ctx context.Context , sess * session.Session ) <- chan Event
43+ RunStream (ctx context.Context , sess * session.Session , maxIterations int ) <- chan Event
4444 // Run starts the agent's interaction loop and returns the final messages
4545 Run (ctx context.Context , sess * session.Session ) ([]session.Message , error )
4646 // Resume allows resuming execution after user confirmation
@@ -168,8 +168,9 @@ func (r *runtime) finalizeEventChannel(ctx context.Context, sess *session.Sessio
168168 }
169169}
170170
171- // Run starts the agent's interaction loop
172- func (r * runtime ) RunStream (ctx context.Context , sess * session.Session ) <- chan Event {
171+ // RunStream starts the agent's interaction loop with an optional iteration limit
172+ // If maxIterations is 0, there is no limit
173+ func (r * runtime ) RunStream (ctx context.Context , sess * session.Session , maxIterations int ) <- chan Event {
173174 slog .Debug ("Starting runtime stream" , "agent" , r .currentAgent , "session_id" , sess .ID )
174175 events := make (chan Event , 128 )
175176
@@ -206,7 +207,29 @@ func (r *runtime) RunStream(ctx context.Context, sess *session.Session) <-chan E
206207 slog .Debug ("Failed to get model definition" , "error" , err )
207208 }
208209
210+ iteration := 0
209211 for {
212+ // Check iteration limit
213+ if maxIterations > 0 && iteration >= maxIterations {
214+ slog .Debug ("Maximum iterations reached" , "agent" , a .Name (), "iterations" , iteration , "max" , maxIterations )
215+ events <- MaxIterationsReached (maxIterations )
216+
217+ // Wait for user decision
218+ select {
219+ case resumeType := <- r .resumeChan :
220+ if resumeType == ResumeTypeApprove {
221+ slog .Debug ("User chose to continue after max iterations" , "agent" , a .Name ())
222+ maxIterations = iteration + 10
223+ } else {
224+ slog .Debug ("User chose to exit after max iterations" , "agent" , a .Name ())
225+ return
226+ }
227+ case <- ctx .Done ():
228+ slog .Debug ("Context cancelled while waiting for max iterations decision" , "agent" , a .Name ())
229+ return
230+ }
231+ }
232+ iteration ++
210233 // Exit immediately if the stream context has been cancelled (e.g., Ctrl+C)
211234 if err := ctx .Err (); err != nil {
212235 slog .Debug ("Runtime stream context cancelled, stopping loop" , "agent" , a .Name (), "session_id" , sess .ID )
@@ -402,7 +425,7 @@ func (r *runtime) ResumeCodeReceived(_ context.Context, code string) error {
402425
403426// Run starts the agent's interaction loop
404427func (r * runtime ) Run (ctx context.Context , sess * session.Session ) ([]session.Message , error ) {
405- eventsChan := r .RunStream (ctx , sess )
428+ eventsChan := r .RunStream (ctx , sess , 0 )
406429
407430 for event := range eventsChan {
408431 if errEvent , ok := event .(* ErrorEvent ); ok {
@@ -821,7 +844,7 @@ func (r *runtime) handleTaskTransfer(ctx context.Context, sess *session.Session,
821844 s .Title = "Transferred task"
822845 s .ToolsApproved = sess .ToolsApproved
823846
824- for event := range r .RunStream (ctx , s ) {
847+ for event := range r .RunStream (ctx , s , 0 ) {
825848 evts <- event
826849 if errEvent , ok := event .(* ErrorEvent ); ok {
827850 span .RecordError (fmt .Errorf ("%s" , errEvent .Error ))
0 commit comments