diff options
Diffstat (limited to 'internal/executor/executor.go')
| -rw-r--r-- | internal/executor/executor.go | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/internal/executor/executor.go b/internal/executor/executor.go index d1c8e72..df222f8 100644 --- a/internal/executor/executor.go +++ b/internal/executor/executor.go @@ -6,6 +6,7 @@ import ( "fmt" "log/slog" "path/filepath" + "strings" "sync" "time" @@ -416,6 +417,10 @@ func (p *Pool) execute(ctx context.Context, t *task.Task) { p.mu.Unlock() }() + // Inject prior failure history so the agent knows what went wrong before. + priorExecs, priorErr := p.store.ListExecutions(t.ID) + t = withFailureHistory(t, priorExecs, priorErr) + // Run the task. err = runner.Run(ctx, t, exec) exec.EndTime = time.Now().UTC() @@ -478,6 +483,43 @@ var terminalFailureStates = map[task.State]bool{ task.StateBudgetExceeded: true, } +// withFailureHistory returns a shallow copy of t with prior failed execution +// error messages prepended to SystemPromptAppend so the agent knows what went +// wrong in previous attempts. +func withFailureHistory(t *task.Task, execs []*storage.Execution, err error) *task.Task { + if err != nil || len(execs) == 0 { + return t + } + + var failures []storage.Execution + for _, e := range execs { + if (e.Status == "FAILED" || e.Status == "TIMED_OUT") && e.ErrorMsg != "" { + failures = append(failures, *e) + } + } + if len(failures) == 0 { + return t + } + + var sb strings.Builder + sb.WriteString("## Prior Attempt History\n\n") + sb.WriteString("This task has failed before. Do not repeat the same mistakes.\n\n") + for i, f := range failures { + fmt.Fprintf(&sb, "**Attempt %d** (%s) — %s:\n%s\n\n", + i+1, f.StartTime.Format("2006-01-02 15:04 UTC"), f.Status, f.ErrorMsg) + } + sb.WriteString("---\n\n") + + copy := *t + copy.Agent = t.Agent + if copy.Agent.SystemPromptAppend != "" { + copy.Agent.SystemPromptAppend = sb.String() + copy.Agent.SystemPromptAppend + } else { + copy.Agent.SystemPromptAppend = sb.String() + } + return © +} + // waitForDependencies polls storage until all tasks in t.DependsOn reach COMPLETED, // or until a dependency enters a terminal failure state or the context is cancelled. func (p *Pool) waitForDependencies(ctx context.Context, t *task.Task) error { |
