Preserve upstream provider detail in OpenRouter errors

OpenRouter wraps the real upstream failure under `error.metadata`
(`provider_name`, `raw`). The previous `extractErrorMessage` only
took `error.message`, leaving users with the generic
`Provider returned error` whenever the upstream model returned a
non-2xx (rate limits, quota exhaustion, model-side failures).

Append the metadata when present so the surfaced message looks
like:

  Provider returned error: provider=Venice, qwen/qwen3-coder:free
  is temporarily rate-limited upstream. Please retry shortly, ...

Drop the `[${code}]` prefix from the formatted message: the HTTP
status is already logged by `AiHttpClientImpl` and exposed
separately on `AiCodeReviewOutput.error.status_code`.

Change-Id: I7e4075a209d1062bc2149b96f8c2c67e3c820738
diff --git a/ai/ai-review-agent-openrouter-1.0.groovy b/ai/ai-review-agent-openrouter-1.0.groovy
index 7cf579d..4b504ed 100644
--- a/ai/ai-review-agent-openrouter-1.0.groovy
+++ b/ai/ai-review-agent-openrouter-1.0.groovy
@@ -200,12 +200,26 @@
     private static String extractErrorMessage(String body) {
         try {
             def json = new JsonSlurper().parseText(body)
-            if (json?.error) return "[${json.error.code}] ${json.error.message}"
+            if (json?.error) {
+                def message = json.error.message as String
+                def detail = formatErrorMetadata(json.error.metadata)
+                return detail ? "${message}: ${detail}" : message
+            }
         } catch (Exception e) {
             logger.atWarning().withCause(e).log('Failed to parse error response')
         }
         return body.length() > MAX_ERROR_LEN ? "${body.take(MAX_ERROR_LEN)}..." : body
     }
+
+    // OpenRouter wraps the real upstream failure under `error.metadata`.
+    // Without this, the user only sees the generic "Provider returned error".
+    private static String formatErrorMetadata(metadata) {
+        if (!metadata) return null
+        def parts = []
+        if (metadata.provider_name) parts << "provider=${metadata.provider_name}"
+        if (metadata.raw) parts << (metadata.raw as String)
+        return parts ? parts.join(', ') : null
+    }
 }
 
 class AiOpenRouterModule extends AbstractModule {