Loop-break signals
MaxIters and token budget are hard stops. Clean stops matter too. The model can emit finish_reason "stop" or simply return content. The user can hit Ctrl-C. Both paths need to leave the process in a good state.
// Accept a context so the REPL can cancel on Ctrl-C.
func (a *Agent) RunCtx(ctx context.Context, userMsg string) (string, error) {
for iter := 0; iter < a.MaxIters; iter++ {
select {
case <-ctx.Done():
return "", ctx.Err()
default:
}
// ... existing loop body
}
return "", fmt.Errorf("agent: exceeded max iterations (%d)", a.MaxIters)
}A tiny context check at the top of each iteration. The REPL wires its signal handler to cancel the context, and a stuck tool call never blocks forever.
All the ways the loop ends
Clean stops, hard caps, and user cancellation.
Validation checklist: Loop safety checklist
Loading practice…
Checkpoint: Loop safety checkpoint
Loading practice…