diff --git a/platform/internal/handlers/a2a_proxy.go b/platform/internal/handlers/a2a_proxy.go index f2d20717..ead4239c 100644 --- a/platform/internal/handlers/a2a_proxy.go +++ b/platform/internal/handlers/a2a_proxy.go @@ -274,12 +274,28 @@ func (h *WorkspaceHandler) proxyA2ARequest(ctx context.Context, workspaceID stri } defer resp.Body.Close() - // Read agent response (capped at 10MB) - respBody, err := io.ReadAll(io.LimitReader(resp.Body, maxProxyResponseBody)) - if err != nil { + // Read agent response (capped at 10MB). + // #689: Do() succeeded, which means the target received the request and sent + // back response headers — delivery is confirmed. The body couldn't be + // fully read (connection drop, timeout mid-stream). Surface + // delivery_confirmed so callers can distinguish "not delivered" from + // "delivered, but response body lost". When delivery is confirmed, + // log the activity as successful (delivery happened) rather than leaving + // a false "failed" entry in the audit trail. + respBody, readErr := io.ReadAll(io.LimitReader(resp.Body, maxProxyResponseBody)) + if readErr != nil { + deliveryConfirmed := resp.StatusCode >= 200 && resp.StatusCode < 400 + log.Printf("ProxyA2A: body read failed for %s (status=%d delivery_confirmed=%v bytes_read=%d): %v", + workspaceID, resp.StatusCode, deliveryConfirmed, len(respBody), readErr) + if logActivity && deliveryConfirmed { + h.logA2ASuccess(ctx, workspaceID, callerID, body, respBody, a2aMethod, resp.StatusCode, durationMs) + } return 0, nil, &proxyA2AError{ - Status: http.StatusBadGateway, - Response: gin.H{"error": "failed to read agent response"}, + Status: http.StatusBadGateway, + Response: gin.H{ + "error": "failed to read agent response", + "delivery_confirmed": deliveryConfirmed, + }, } }