@skydiveai/pi-server 0.1.0-beta.2864 → 0.1.0-beta.2978

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +43 -8
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -919,6 +919,18 @@ function providerId(err, message) {
919
919
  */
920
920
  const CONTEXT_OVERFLOW_MESSAGE = /prompt is too long|context[_ ]length[_ ]exceeded|maximum context length|exceeds maximum token limit|input token count|too many tokens|exceeds the maximum/i;
921
921
  /**
922
+ * A content-filter / model-safety termination message from any provider.
923
+ * Used by BOTH the classifyCode message fallback and isModelProviderError's
924
+ * gate (a shape recognized there must pass this gate or it never gets
925
+ * classified), so it lives in one place rather than two copies kept "in
926
+ * sync" by comment. Verified shapes (see classifyCode):
927
+ * - "Provider finish_reason: content_filter" (Anthropic/gateway 200 stream)
928
+ * - "This request triggered restrictions on violative cyber content ..."
929
+ * - "This request triggered cyber-related safeguards. ..."
930
+ * - "This content was flagged for possible cybersecurity risk. ..."
931
+ */
932
+ const CONTENT_FILTER_MESSAGE = /finish_reason:\s*content_filter|content[_ ]filter|content[_ ]management[_ ]policy|violative cyber content|cyber[- ]related safeguards|cybersecurity risk|cyber verification program/i;
933
+ /**
922
934
  * Map a provider error to a stable code. Prefers the provider's machine code
923
935
  * (OpenAI `error.code`, OpenRouter `error.metadata.error_type`) over message
924
936
  * text, then falls back to status + message regex for providers that don't
@@ -931,7 +943,7 @@ function classifyCode(status, message, code) {
931
943
  if (status === 429 || code === "rate_limit_exceeded" || /rate[_ ]limit|too many requests|resource[_ ]exhausted/i.test(message)) return "rate_limited";
932
944
  if (status === 529 || code === "provider_overloaded" || /overloaded/i.test(message)) return "provider_overloaded";
933
945
  if (status === 503 || status === 504 || code === "provider_unavailable" || /no healthy upstream|upstream request timeout|stream timeout|service unavailable|unavailable|gateway/i.test(message)) return "provider_unavailable";
934
- if (code === "content_filter" || /finish_reason:\s*content_filter|content[_ ]filter|content[_ ]management[_ ]policy/i.test(message)) return "content_filter";
946
+ if (code === "content_filter" || CONTENT_FILTER_MESSAGE.test(message)) return "content_filter";
935
947
  return "provider_error";
936
948
  }
937
949
  /**
@@ -946,7 +958,7 @@ function isModelProviderError(err) {
946
958
  if (numericStatus(e) !== null) return true;
947
959
  if (e.error && typeof e.error === "object") return true;
948
960
  const msg = messageText(err);
949
- return /prompt is too long|context[_ ]length[_ ]exceeded|exceeds maximum token limit|input token count|rate[_ ]limit|overloaded|no healthy upstream|upstream request timeout|insufficient credits|requires more credits|credit balance is too low|payment required|purchase more credits|finish_reason:\s*content_filter|content[_ ]filter|content[_ ]management[_ ]policy/i.test(msg);
961
+ return /prompt is too long|context[_ ]length[_ ]exceeded|exceeds maximum token limit|input token count|rate[_ ]limit|overloaded|no healthy upstream|upstream request timeout|insufficient credits|requires more credits|credit balance is too low|payment required|purchase more credits/i.test(msg) || CONTENT_FILTER_MESSAGE.test(msg);
950
962
  }
951
963
  /** Build the structured, forwardable error from a thrown model-provider error. */
952
964
  function toModelProviderError(err) {
@@ -1509,6 +1521,21 @@ function parsePrefixedSignal(message) {
1509
1521
  }
1510
1522
  }
1511
1523
  /**
1524
+ * The sandbox proxy's other 402 envelope (verified prod 2026-09-20): the
1525
+ * billing gate's body surfaces with the type/code at TOP level and the
1526
+ * versioned signal nested inside `message` —
1527
+ * {"type":"billing_blocked","code":"billing_blocked","message":
1528
+ * "ANYONE_BILLING_BLOCKED_V1:{\"blockReason\":\"insufficient_balance\",…}"}
1529
+ * — with no blockReason/error wrapper for nestedProxyEnvelopeSchema, so it
1530
+ * used to fall through and degrade to a generic model-provider error that
1531
+ * read as "The model stopped responding" on an out-of-credit org. The outer
1532
+ * shape is validated with a schema instead of ad-hoc casting, and only the
1533
+ * top-level `message` is peeled; a signal nested inside `error.message` is
1534
+ * handled below through nestedProxyEnvelopeSchema's full integrity checks
1535
+ * (validating type, code, blockReason, and message agreement).
1536
+ */
1537
+ const topLevelMessageEnvelopeSchema = z.object({ message: z.string().optional() }).passthrough();
1538
+ /**
1512
1539
  * Decode only the versioned platform envelope. Anthropic and OpenAI prefix the
1513
1540
  * nested error message with HTTP 402. Google's SDK instead preserves the full
1514
1541
  * proxy response as JSON, so that outer shape is validated separately.
@@ -1517,16 +1544,24 @@ function parseBillingBlockedProviderSignal(message) {
1517
1544
  const unwrapped = unwrapOpenAICompatStatusPrefix(message);
1518
1545
  const direct = parsePrefixedSignal(unwrapped);
1519
1546
  if (direct) return direct;
1547
+ let body;
1520
1548
  try {
1521
- const envelope = nestedProxyEnvelopeSchema.safeParse(JSON.parse(unwrapped));
1522
- if (!envelope.success) return null;
1523
- const nested = parsePrefixedSignal(envelope.data.error.message);
1524
- const topLevel = parsePrefixedSignal(envelope.data.message);
1525
- if (!nested || nested.blockReason !== envelope.data.blockReason || envelope.data.message !== nested.message && (!topLevel || topLevel.blockReason !== nested.blockReason || topLevel.message !== nested.message)) return null;
1526
- return nested;
1549
+ body = JSON.parse(unwrapped);
1527
1550
  } catch (_err) {
1528
1551
  return null;
1529
1552
  }
1553
+ if (body === null || typeof body !== "object") return null;
1554
+ const nested = topLevelMessageEnvelopeSchema.safeParse(body);
1555
+ if (nested.success && nested.data.message) {
1556
+ const signal = parsePrefixedSignal(nested.data.message);
1557
+ if (signal) return signal;
1558
+ }
1559
+ const envelope = nestedProxyEnvelopeSchema.safeParse(body);
1560
+ if (!envelope.success) return null;
1561
+ const nestedError = parsePrefixedSignal(envelope.data.error.message);
1562
+ const topLevel = parsePrefixedSignal(envelope.data.message);
1563
+ if (!nestedError || nestedError.blockReason !== envelope.data.blockReason || envelope.data.message !== nestedError.message && (!topLevel || topLevel.blockReason !== nestedError.blockReason || topLevel.message !== nestedError.message)) return null;
1564
+ return nestedError;
1530
1565
  }
1531
1566
  //#endregion
1532
1567
  //#region src/protocols/chat-completions.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skydiveai/pi-server",
3
- "version": "0.1.0-beta.2864",
3
+ "version": "0.1.0-beta.2978",
4
4
  "homepage": "https://skydive.com",
5
5
  "license": "MIT",
6
6
  "author": "Create, Inc.",