feat(agent): auto-complete tasks when all plan steps are terminal
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
Desktop App / Build Linux (amd64) (push) Has been cancelled
Desktop App / Attach to Release (push) Has been cancelled

The #1 remaining model reliability gap: the agent does the work (proposes
plan, executes all steps, writes back) but forgets to call complete_task,
leaving the session stuck in 'executing'. The eval showed 3/8 failures
with this pattern.

Fix: autoCompleteIfPlanDone — a structural safety net that fires at both
chat exit paths (normal completion + maxIterations). If the session has a
goal, the agent didn't call complete_task, and ALL plan steps are in a
terminal state (done/failed/replaced/skipped/blocked), auto-complete with
the agent's final text as the summary. Mirrors autoCompleteTrivialTask
but for structured tasks where the work is provably done.

Also: bump maxLLMRetries from 2 to 3 (complex multi-turn flows benefit
from one more retry on empty responses).
This commit is contained in:
2026-07-15 13:06:47 +02:00
parent 3c3b12df5e
commit e3b5fdc358
3 changed files with 66 additions and 0 deletions

View File

@@ -415,6 +415,14 @@ func (a *agent) chatWith(ctx context.Context, sessionID, message, systemInject s
if !sawSetGoal && !sawCompleteTask {
a.autoCompleteTrivialTask(ctx, sessionID, msg.Content)
}
// Safety net: if the agent called set_goal (structured task)
// but didn't call complete_task, and all plan steps are
// terminal, auto-complete. The model often does the work but
// forgets to close the loop (confirmed live: the #1 remaining
// model reliability gap after D.1).
if !sawCompleteTask {
a.autoCompleteIfPlanDone(ctx, sessionID, msg.Content)
}
emit(agentEvent{Type: "done", Data: map[string]any{
"session_id": sessionID,
"usage": acc.Usage,
@@ -566,6 +574,9 @@ func (a *agent) chatWith(ctx context.Context, sessionID, message, systemInject s
summary = "I hit this turn's step limit while working. I've done a lot but couldn't wrap up cleanly — ask me for a status update and I'll summarize the current state."
}
emit(agentEvent{Type: "text", Data: summary, SessionID: sessionID})
if !sawCompleteTask {
a.autoCompleteIfPlanDone(ctx, sessionID, summary)
}
emit(agentEvent{Type: "done", Data: map[string]any{
"session_id": sessionID,
"correlation_id": correlationID,