fix: guard against nil EncryptedContent dereference in reasoning details

Andrey Nering created

Reasoning metadata can exist without encrypted content, causing a nil
pointer panic when building prompt messages for openrouter and vercel.

💘 Generated with Crush

Assisted-by: Claude Opus 4.6 via Crush <crush@charm.land>

Change summary

providers/openrouter/language_model_hooks.go | 28 ++++++++++++---------
providers/vercel/language_model_hooks.go     | 14 ++++++----
2 files changed, 24 insertions(+), 18 deletions(-)

Detailed changes

providers/openrouter/language_model_hooks.go 🔗

@@ -841,12 +841,14 @@ func languageModelToPrompt(prompt fantasy.Prompt, _, model string) ([]openaisdk.
 								Index:   inx,
 							})
 						}
-						reasoningDetails = append(reasoningDetails, ReasoningDetail{
-							Type:   "reasoning.encrypted",
-							Format: "openai-responses-v1",
-							Data:   *metadata.EncryptedContent,
-							ID:     metadata.ItemID,
-						})
+						if metadata.EncryptedContent != nil {
+							reasoningDetails = append(reasoningDetails, ReasoningDetail{
+								Type:   "reasoning.encrypted",
+								Format: "openai-responses-v1",
+								Data:   *metadata.EncryptedContent,
+								ID:     metadata.ItemID,
+							})
+						}
 						data, _ := json.Marshal(reasoningDetails)
 						reasoningDetailsMap := []map[string]any{}
 						_ = json.Unmarshal(data, &reasoningDetailsMap)
@@ -877,12 +879,14 @@ func languageModelToPrompt(prompt fantasy.Prompt, _, model string) ([]openaisdk.
 								Index:   inx,
 							})
 						}
-						reasoningDetails = append(reasoningDetails, ReasoningDetail{
-							Type:   "reasoning.encrypted",
-							Format: "xai-responses-v1",
-							Data:   *metadata.EncryptedContent,
-							ID:     metadata.ItemID,
-						})
+						if metadata.EncryptedContent != nil {
+							reasoningDetails = append(reasoningDetails, ReasoningDetail{
+								Type:   "reasoning.encrypted",
+								Format: "xai-responses-v1",
+								Data:   *metadata.EncryptedContent,
+								ID:     metadata.ItemID,
+							})
+						}
 						data, _ := json.Marshal(reasoningDetails)
 						reasoningDetailsMap := []map[string]any{}
 						_ = json.Unmarshal(data, &reasoningDetailsMap)

providers/vercel/language_model_hooks.go 🔗

@@ -874,12 +874,14 @@ func languageModelToPrompt(prompt fantasy.Prompt, _, model string) ([]openaisdk.
 								Index:   inx,
 							})
 						}
-						reasoningDetails = append(reasoningDetails, ReasoningDetail{
-							Type:   "reasoning.encrypted",
-							Format: "openai-responses-v1",
-							Data:   *metadata.EncryptedContent,
-							ID:     metadata.ItemID,
-						})
+						if metadata.EncryptedContent != nil {
+							reasoningDetails = append(reasoningDetails, ReasoningDetail{
+								Type:   "reasoning.encrypted",
+								Format: "openai-responses-v1",
+								Data:   *metadata.EncryptedContent,
+								ID:     metadata.ItemID,
+							})
+						}
 						data, _ := json.Marshal(reasoningDetails)
 						reasoningDetailsMap := []map[string]any{}
 						_ = json.Unmarshal(data, &reasoningDetailsMap)