Skip to content

Commit 864845a

Browse files
zmanianclaude
andcommitted
Fix loop "(No response)": capture result events in flush path
The flush path after stream completion only handled assistant events, missing the result event where Claude's final response text lives. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d9b10fb commit 864845a

File tree

1 file changed

+22
-5
lines changed

1 file changed

+22
-5
lines changed

Wisp/Services/LoopManager.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,8 @@ final class LoopManager {
322322
let maxAttempts = 5
323323

324324
for attempt in 1...maxAttempts {
325-
try? await apiClient.deleteService(spriteName: spriteName, serviceName: serviceName)
326325
if attempt > 1 {
326+
try? await apiClient.deleteService(spriteName: spriteName, serviceName: serviceName)
327327
let backoff = attempt * 10 // 10s, 20s, 30s...
328328
logger.info("Retrying service stream for loop (attempt \(attempt)/\(maxAttempts), backoff \(backoff)s)")
329329
try? await Task.sleep(for: .seconds(backoff))
@@ -352,12 +352,19 @@ final class LoopManager {
352352
guard let rawData = Data(base64Encoded: base64Data) else { continue }
353353
let claudeEvents = await parser.parse(data: rawData)
354354
for claudeEvent in claudeEvents {
355-
if case .assistant(let assistantEvent) = claudeEvent {
355+
switch claudeEvent {
356+
case .assistant(let assistantEvent):
356357
for block in assistantEvent.message.content {
357358
if case .text(let text) = block {
358359
responseText += text
359360
}
360361
}
362+
case .result(let resultEvent):
363+
if let text = resultEvent.result, !text.isEmpty {
364+
responseText += text
365+
}
366+
default:
367+
break
361368
}
362369
}
363370
}
@@ -367,9 +374,11 @@ final class LoopManager {
367374
return .failure(CancellationError())
368375
}
369376
// Retry if we got no data (connection failed before streaming started)
377+
let urlError = error as? URLError
378+
let errorCode = urlError?.code.rawValue ?? -1
379+
logger.warning("Service stream error (attempt \(attempt)/\(maxAttempts), gotData=\(gotData), code=\(errorCode)): \(error.localizedDescription)")
370380
if !gotData && attempt < maxAttempts {
371381
lastError = error
372-
logger.warning("Service stream failed before receiving data: \(error.localizedDescription)")
373382
continue
374383
}
375384
if responseText.isEmpty {
@@ -380,12 +389,19 @@ final class LoopManager {
380389

381390
let remaining = await parser.flush()
382391
for claudeEvent in remaining {
383-
if case .assistant(let assistantEvent) = claudeEvent {
392+
switch claudeEvent {
393+
case .assistant(let assistantEvent):
384394
for block in assistantEvent.message.content {
385395
if case .text(let text) = block {
386396
responseText += text
387397
}
388398
}
399+
case .result(let resultEvent):
400+
if let text = resultEvent.result, !text.isEmpty {
401+
responseText += text
402+
}
403+
default:
404+
break
389405
}
390406
}
391407

@@ -400,7 +416,8 @@ final class LoopManager {
400416

401417
// All retries exhausted
402418
try? await apiClient.deleteService(spriteName: spriteName, serviceName: serviceName)
403-
return .failure(lastError ?? NSError(domain: "LoopManager", code: -4, userInfo: [NSLocalizedDescriptionKey: "Service stream failed after \(maxAttempts) attempts"]))
419+
let underlying = lastError?.localizedDescription ?? "unknown"
420+
return .failure(NSError(domain: "LoopManager", code: -4, userInfo: [NSLocalizedDescriptionKey: "Failed after \(maxAttempts) retries: \(underlying)"]))
404421
} onCancel: {
405422
Task {
406423
try? await apiClient.deleteService(spriteName: spriteName, serviceName: serviceName)

0 commit comments

Comments
 (0)