///|
/// Stable provider-side `prompt_cache_key` for a posoco session id.
///
/// Normal session ids keep the historical `"posoco-" + session_id` shape so
/// existing provider affinity is not churned unnecessarily. Oversized ids are
/// replaced with a versioned SHA-256-derived key instead of being truncated:
/// `posoco-v1-<48 lowercase hex chars>`. The hashed form is 58 chars, stays
/// below the 64-char provider limit, and two ids that share a long prefix do
/// not collapse to the same cache key.
pub fn derive_prompt_cache_key(session_id : String) -> String {
  let full = "posoco-" + session_id
  if full.length() <= 64 {
    return full
  }
  let digest = @crypto.bytes_to_hex_string(
    @crypto.sha256(@utf8.encode(session_id)),
  )
  let chars : Array[Char] = []
  for ch in digest {
    if chars.length() >= 48 {
      break
    }
    chars.push(ch)
  }
  "posoco-v1-" + String::from_array(chars)
}

///|
/// Whether a provider error body explicitly rejects the `prompt_cache_key`
/// parameter: true only when the text contains BOTH "unsupported parameter"
/// AND "prompt_cache_key", case-insensitively. Empty bodies, bodies naming
/// only one phrase, and unrelated 400s never match.
pub fn is_prompt_cache_key_rejection(body_text : String) -> Bool {
  case_insensitive_contains(body_text, "unsupported parameter") &&
  case_insensitive_contains(body_text, "prompt_cache_key")
}