Run executes the agent loop until completion or max iterations.
(ctx context.Context)
| 136 | |
| 137 | // Run executes the agent loop until completion or max iterations. |
| 138 | func (l *Loop) Run(ctx context.Context) error { |
| 139 | if l.provider == nil { |
| 140 | return fmt.Errorf("loop provider is not configured") |
| 141 | } |
| 142 | |
| 143 | // Open log file in PRD directory |
| 144 | prdDir := filepath.Dir(l.prdPath) |
| 145 | logPath := filepath.Join(prdDir, l.provider.LogFileName()) |
| 146 | var err error |
| 147 | l.logFile, err = os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) |
| 148 | if err != nil { |
| 149 | return fmt.Errorf("failed to open log file: %w", err) |
| 150 | } |
| 151 | defer l.logFile.Close() |
| 152 | defer close(l.events) |
| 153 | |
| 154 | for { |
| 155 | l.mu.Lock() |
| 156 | if l.stopped { |
| 157 | l.mu.Unlock() |
| 158 | return nil |
| 159 | } |
| 160 | if l.paused { |
| 161 | l.mu.Unlock() |
| 162 | return nil |
| 163 | } |
| 164 | l.iteration++ |
| 165 | currentIter := l.iteration |
| 166 | l.mu.Unlock() |
| 167 | |
| 168 | // Check if max iterations reached |
| 169 | if currentIter > l.maxIter { |
| 170 | l.events <- Event{ |
| 171 | Type: EventMaxIterationsReached, |
| 172 | Iteration: currentIter - 1, |
| 173 | } |
| 174 | return nil |
| 175 | } |
| 176 | |
| 177 | // Rebuild prompt if builder is set (inlines the current story each iteration) |
| 178 | if l.buildPrompt != nil { |
| 179 | prompt, storyID, err := l.buildPrompt() |
| 180 | if err != nil { |
| 181 | l.events <- Event{ |
| 182 | Type: EventComplete, |
| 183 | Iteration: currentIter, |
| 184 | } |
| 185 | return nil |
| 186 | } |
| 187 | l.mu.Lock() |
| 188 | l.prompt = prompt |
| 189 | l.currentStoryID = storyID |
| 190 | l.sawStoryDone = false |
| 191 | l.mu.Unlock() |
| 192 | } |
| 193 | |
| 194 | // Send iteration start event with current story ID |
| 195 | l.mu.Lock() |