From f1787c4e9261cc1c2bd7331f535221f9aa5e801e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Daba=C5=A1inskas?= Date: Sat, 21 Mar 2026 19:51:12 +0200 Subject: [PATCH] feat(runtime): add auto-stop for max iterations in non-interactive mode When max iterations are reached in non-interactive mode (e.g., MCP server), the runtime now automatically stops execution instead of blocking indefinitely waiting for user input. This prevents the system from hanging when `ToolsApproved` is true and provides a clear assistant message explaining why execution was stopped. --- pkg/runtime/loop.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/runtime/loop.go b/pkg/runtime/loop.go index ae161a815..2e296af8d 100644 --- a/pkg/runtime/loop.go +++ b/pkg/runtime/loop.go @@ -175,6 +175,24 @@ func (r *LocalRuntime) RunStream(ctx context.Context, sess *session.Session) <-c r.executeNotificationHooks(ctx, a, sess.ID, "warning", maxIterMsg) r.executeOnUserInputHooks(ctx, sess.ID, "max iterations reached") + // In non-interactive mode (e.g. MCP server), auto-stop instead of + // blocking forever waiting for user input. + if sess.ToolsApproved { + slog.Debug("Auto-stopping after max iterations (non-interactive)", "agent", a.Name()) + + assistantMessage := chat.Message{ + Role: chat.MessageRoleAssistant, + Content: fmt.Sprintf( + "Execution stopped after reaching the configured max_iterations limit (%d).", + runtimeMaxIterations, + ), + CreatedAt: time.Now().Format(time.RFC3339), + } + + addAgentMessage(sess, a, &assistantMessage, events) + return + } + // Wait for user decision (resume / reject) select { case req := <-r.resumeChan: