(ctx context.Context)
| 210 | } |
| 211 | |
| 212 | func (c *PTYConversation) Start(ctx context.Context) { |
| 213 | // Snapshot loop |
| 214 | c.cfg.Clock.TickerFunc(ctx, c.cfg.SnapshotInterval, func() error { |
| 215 | c.lock.Lock() |
| 216 | screen := c.cfg.AgentIO.ReadScreen() |
| 217 | c.snapshotLocked(screen) |
| 218 | status := c.statusLocked() |
| 219 | messages := c.messagesLocked() |
| 220 | |
| 221 | // Signal send loop if agent is ready and queue has items. |
| 222 | // We check readiness independently of statusLocked() because |
| 223 | // statusLocked() returns "changing" when queue has items. |
| 224 | if !c.initialPromptReady && c.cfg.ReadyForInitialPrompt(screen) { |
| 225 | c.initialPromptReady = true |
| 226 | } |
| 227 | |
| 228 | var loadErr string |
| 229 | if c.initialPromptReady && c.loadStateStatus == LoadStatePending && c.cfg.StatePersistenceConfig.LoadState { |
| 230 | if err, shouldEmit := c.loadStateLocked(); err != nil { |
| 231 | c.loadStateStatus = LoadStateFailed |
| 232 | if shouldEmit { |
| 233 | c.cfg.Logger.Error("Failed to load state", "error", err) |
| 234 | loadErr = fmt.Sprintf("Failed to restore previous session: %v", err) |
| 235 | } |
| 236 | } else { |
| 237 | c.loadStateStatus = LoadStateSucceeded |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | if c.initialPromptReady && len(c.cfg.InitialPrompt) > 0 && !c.initialPromptSent { |
| 242 | // Safe to send under lock: the queue is guaranteed empty here because |
| 243 | // statusLocked blocks Send until the snapshot buffer fills, which |
| 244 | // cannot happen before this first enqueue completes. |
| 245 | c.outboundQueue <- outboundMessage{parts: c.cfg.InitialPrompt, errCh: nil} |
| 246 | c.initialPromptSent = true |
| 247 | c.dirty = true |
| 248 | } |
| 249 | |
| 250 | if c.initialPromptReady && len(c.outboundQueue) > 0 && c.isScreenStableLocked() { |
| 251 | select { |
| 252 | case c.stableSignal <- struct{}{}: |
| 253 | c.sendingMessage = true |
| 254 | default: |
| 255 | // Signal already pending |
| 256 | } |
| 257 | } |
| 258 | c.lock.Unlock() |
| 259 | |
| 260 | if loadErr != "" { |
| 261 | c.emitter.EmitError(loadErr, ErrorLevelWarning) |
| 262 | } |
| 263 | c.emitter.EmitStatus(status) |
| 264 | c.emitter.EmitMessages(messages) |
| 265 | c.emitter.EmitScreen(screen) |
| 266 | return nil |
| 267 | }, "snapshot") |
| 268 | |
| 269 | // Send loop - primary call site for sendLocked() in production |
nothing calls this directly
no test coverage detected