ScuttleBot
fix: mirror loop retries indefinitely instead of failing after timeout The mirror now loops on session discovery instead of giving up after 60 seconds. If the session file doesn't exist yet (idle agent) or the tail is lost, it waits and retries. No more "mirror failed" messages for agents that just haven't started their first task yet. Closes #59
Commit
321167eca720bcc2d2e589ca80c2d657bedc974df1a097f68ec5b50652ad199d
Parent
64167c9d7219405…
1 file changed
+24
-14
+24
-14
| --- cmd/claude-relay/main.go | ||
| +++ cmd/claude-relay/main.go | ||
| @@ -266,27 +266,37 @@ | ||
| 266 | 266 | } |
| 267 | 267 | |
| 268 | 268 | // --- Session mirroring --- |
| 269 | 269 | |
| 270 | 270 | func mirrorSessionLoop(ctx context.Context, relay sessionrelay.Connector, cfg config, startedAt time.Time) { |
| 271 | - sessionPath, err := discoverSessionPath(ctx, cfg, startedAt) | |
| 272 | - if err != nil { | |
| 273 | - if ctx.Err() == nil { | |
| 274 | - _ = relay.Post(context.Background(), fmt.Sprintf("mirror failed: %v — session activity not visible in IRC", err)) | |
| 271 | + for { | |
| 272 | + if ctx.Err() != nil { | |
| 273 | + return | |
| 274 | + } | |
| 275 | + sessionPath, err := discoverSessionPath(ctx, cfg, startedAt) | |
| 276 | + if err != nil { | |
| 277 | + if ctx.Err() != nil { | |
| 278 | + return | |
| 279 | + } | |
| 280 | + // Session not found yet — wait and retry instead of giving up. | |
| 281 | + time.Sleep(10 * time.Second) | |
| 282 | + continue | |
| 283 | + } | |
| 284 | + if err := tailSessionFile(ctx, sessionPath, cfg.MirrorReasoning, func(text string) { | |
| 285 | + for _, line := range splitMirrorText(text) { | |
| 286 | + if line == "" { | |
| 287 | + continue | |
| 288 | + } | |
| 289 | + _ = relay.Post(ctx, line) | |
| 290 | + } | |
| 291 | + }); err != nil && ctx.Err() == nil { | |
| 292 | + // Tail lost — retry discovery. | |
| 293 | + time.Sleep(5 * time.Second) | |
| 294 | + continue | |
| 275 | 295 | } |
| 276 | 296 | return |
| 277 | 297 | } |
| 278 | - if err := tailSessionFile(ctx, sessionPath, cfg.MirrorReasoning, func(text string) { | |
| 279 | - for _, line := range splitMirrorText(text) { | |
| 280 | - if line == "" { | |
| 281 | - continue | |
| 282 | - } | |
| 283 | - _ = relay.Post(ctx, line) | |
| 284 | - } | |
| 285 | - }); err != nil && ctx.Err() == nil { | |
| 286 | - _ = relay.Post(context.Background(), fmt.Sprintf("mirror lost: %v — session activity no longer visible in IRC", err)) | |
| 287 | - } | |
| 288 | 298 | } |
| 289 | 299 | |
| 290 | 300 | func discoverSessionPath(ctx context.Context, cfg config, startedAt time.Time) (string, error) { |
| 291 | 301 | root, err := claudeSessionsRoot(cfg.TargetCWD) |
| 292 | 302 | if err != nil { |
| 293 | 303 |
| --- cmd/claude-relay/main.go | |
| +++ cmd/claude-relay/main.go | |
| @@ -266,27 +266,37 @@ | |
| 266 | } |
| 267 | |
| 268 | // --- Session mirroring --- |
| 269 | |
| 270 | func mirrorSessionLoop(ctx context.Context, relay sessionrelay.Connector, cfg config, startedAt time.Time) { |
| 271 | sessionPath, err := discoverSessionPath(ctx, cfg, startedAt) |
| 272 | if err != nil { |
| 273 | if ctx.Err() == nil { |
| 274 | _ = relay.Post(context.Background(), fmt.Sprintf("mirror failed: %v — session activity not visible in IRC", err)) |
| 275 | } |
| 276 | return |
| 277 | } |
| 278 | if err := tailSessionFile(ctx, sessionPath, cfg.MirrorReasoning, func(text string) { |
| 279 | for _, line := range splitMirrorText(text) { |
| 280 | if line == "" { |
| 281 | continue |
| 282 | } |
| 283 | _ = relay.Post(ctx, line) |
| 284 | } |
| 285 | }); err != nil && ctx.Err() == nil { |
| 286 | _ = relay.Post(context.Background(), fmt.Sprintf("mirror lost: %v — session activity no longer visible in IRC", err)) |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | func discoverSessionPath(ctx context.Context, cfg config, startedAt time.Time) (string, error) { |
| 291 | root, err := claudeSessionsRoot(cfg.TargetCWD) |
| 292 | if err != nil { |
| 293 |
| --- cmd/claude-relay/main.go | |
| +++ cmd/claude-relay/main.go | |
| @@ -266,27 +266,37 @@ | |
| 266 | } |
| 267 | |
| 268 | // --- Session mirroring --- |
| 269 | |
| 270 | func mirrorSessionLoop(ctx context.Context, relay sessionrelay.Connector, cfg config, startedAt time.Time) { |
| 271 | for { |
| 272 | if ctx.Err() != nil { |
| 273 | return |
| 274 | } |
| 275 | sessionPath, err := discoverSessionPath(ctx, cfg, startedAt) |
| 276 | if err != nil { |
| 277 | if ctx.Err() != nil { |
| 278 | return |
| 279 | } |
| 280 | // Session not found yet — wait and retry instead of giving up. |
| 281 | time.Sleep(10 * time.Second) |
| 282 | continue |
| 283 | } |
| 284 | if err := tailSessionFile(ctx, sessionPath, cfg.MirrorReasoning, func(text string) { |
| 285 | for _, line := range splitMirrorText(text) { |
| 286 | if line == "" { |
| 287 | continue |
| 288 | } |
| 289 | _ = relay.Post(ctx, line) |
| 290 | } |
| 291 | }); err != nil && ctx.Err() == nil { |
| 292 | // Tail lost — retry discovery. |
| 293 | time.Sleep(5 * time.Second) |
| 294 | continue |
| 295 | } |
| 296 | return |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | func discoverSessionPath(ctx context.Context, cfg config, startedAt time.Time) (string, error) { |
| 301 | root, err := claudeSessionsRoot(cfg.TargetCWD) |
| 302 | if err != nil { |
| 303 |