agent.go: don't panic on nil initialResp
a.processUserMessage(ctx) appears to return nil, nil under
certain circumstances, and this causes a panic in processTurn().
This change makes it log the error instead of panicing.
Co-Authored-By: sketch <hello@sketch.dev>
diff --git a/loop/agent.go b/loop/agent.go
index c407420..497f048 100644
--- a/loop/agent.go
+++ b/loop/agent.go
@@ -822,7 +822,10 @@
// hence the mutex.
a.cancelTurn = cancel
a.cancelTurnMu.Unlock()
- a.processTurn(ctxInner) // Renamed from InnerLoop to better reflect its purpose
+ err := a.processTurn(ctxInner) // Renamed from InnerLoop to better reflect its purpose
+ if err != nil {
+ slog.ErrorContext(ctxOuter, "Error in processing turn", "error", err)
+ }
cancel(nil)
}
}
@@ -876,14 +879,21 @@
}
// processTurn handles a single conversation turn with the user
-func (a *Agent) processTurn(ctx context.Context) {
+func (a *Agent) processTurn(ctx context.Context) error {
// Reset the start of turn time
a.startOfTurn = time.Now()
// Process initial user message
initialResp, err := a.processUserMessage(ctx)
if err != nil {
- return
+ return err
+ }
+
+ // Handle edge case where both initialResp and err are nil
+ if initialResp == nil {
+ err := fmt.Errorf("unexpected nil response from processUserMessage with no error")
+ a.pushToOutbox(ctx, errorMessage(err))
+ return err
}
// We do this as we go, but let's also do it at the end of the turn
@@ -899,7 +909,7 @@
for {
// Check if we are over budget
if err := a.overBudget(ctx); err != nil {
- return
+ return err
}
// If the model is not requesting to use a tool, we're done
@@ -910,12 +920,14 @@
// Handle tool execution
continueConversation, toolResp := a.handleToolExecution(ctx, resp)
if !continueConversation {
- return
+ return nil
}
// Set the response for the next iteration
resp = toolResp
}
+
+ return nil
}
// processUserMessage waits for user messages and sends them to the model