@opencode/ai 0.0.0-dev-19629 → 0.0.0-dev-19630

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.
@@ -52,9 +52,26 @@ const SERVER_CODES = new Set([
52
52
  "serviceunavailableexception",
53
53
  ]);
54
54
  const INVALID_REQUEST_CODES = new Set(["invalid_prompt", "invalid_request_error", "validationexception"]);
55
+ // Azure OpenAI reports `content_filter` with `innererror.code` ResponsibleAIPolicyViolation.
56
+ // OpenRouter tags provider failures with a typed `error_type`; its Responses skin also
57
+ // emits `image_content_policy_violation` as the native code.
58
+ const CONTENT_POLICY_CODES = new Set([
59
+ "content_filter",
60
+ "responsibleaipolicyviolation",
61
+ "content_policy_violation",
62
+ "image_content_policy_violation",
63
+ "refusal",
64
+ ]);
65
+ // OpenCode Zen replaces upstream codes outside its allow-list but keeps the original
66
+ // as a `[code]` label at the start of the rewritten message.
67
+ const GATEWAY_CODE_LABEL = /^[^:\n]+: \[([A-Za-z0-9_.-]+)\]/;
55
68
  const RATE_LIMIT_TEXT = /rate increased too quickly|rate[-_\s]?limit|too[_\s]?many[_\s]?requests/i;
56
69
  const QUOTA_TEXT = /insufficient[-_\s]?quota|quota[-_\s]?exceeded/i;
57
- const CONTENT_POLICY_TEXT = /content[-_\s]?policy|content_filter|safety/i;
70
+ // Policy rejections without a dedicated code, matched against the provider's own
71
+ // explanation only. OpenAI reuses `invalid_prompt` for usage-policy rejections while
72
+ // Bedrock Mantle reuses it for schema validation; Anthropic reports blocked output
73
+ // under `invalid_request_error`.
74
+ const CONTENT_POLICY_TEXT = /violating our usage policy|blocked by content filtering policy|content[-_\s]?policy|rejected as a result of our safety system/i;
58
75
  const SERVER_ERROR_TEXT = /\b(?:try again|(?:please |you can )?retry (?:the |this |your )?request|try (?:the |this |your )?request again|(?:currently |temporarily )?at capacity|overloaded|temporarily unavailable|service[-_\s]?unavailable|(?:server|internal)[-_\s]?error|server (?:is )?busy|provider returned (?:an )?error|resource[-_\s]?exhausted|upstream (?:connect|connection|request)|request buffer limit while retrying upstream)\b/i;
59
76
  // Classification records affirmative evidence about a failure. Deterministic
60
77
  // failures need positive identification (a 4xx status, quota/auth/policy
@@ -65,7 +82,12 @@ const SERVER_ERROR_TEXT = /\b(?:try again|(?:please |you can )?retry (?:the |thi
65
82
  export function classifyProviderFailure(input) {
66
83
  const details = { message: input.message, body: input.rawBody, http: input.http, cause: input.cause };
67
84
  const body = input.rawBody ?? "";
68
- const codes = [...providerCodes(input.data), ...providerCodes(body), ...providerCodes(input.message)].map((code) => code.toLowerCase());
85
+ const codes = [
86
+ ...providerCodes(input.data),
87
+ ...providerCodes(body),
88
+ ...providerCodes(input.message),
89
+ ...(GATEWAY_CODE_LABEL.exec(input.message)?.slice(1) ?? []),
90
+ ].map((code) => code.toLowerCase());
69
91
  // Scan the raw payload too so signals missing from the summary message
70
92
  // (e.g. overflow phrases nested in a JSON error body) still classify.
71
93
  const text = [input.message, body].filter((value) => value.length > 0).join("\n");
@@ -78,7 +100,7 @@ export function classifyProviderFailure(input) {
78
100
  return new InvalidRequestError({ ...details, classification: "context-overflow" });
79
101
  if (input.status === 413 || isPayloadTooLarge(text))
80
102
  return new InvalidRequestError({ ...details, classification: "payload-too-large" });
81
- if (CONTENT_POLICY_TEXT.test(text))
103
+ if (codes.some((code) => CONTENT_POLICY_CODES.has(code)) || (clientScoped && CONTENT_POLICY_TEXT.test(input.message)))
82
104
  return new ContentPolicyError(details);
83
105
  if (codes.some((code) => QUOTA_CODES.has(code)) || (input.status === 429 && QUOTA_TEXT.test(text)))
84
106
  return new QuotaExceededError(details);
@@ -115,10 +137,24 @@ function providerCodes(value) {
115
137
  if (!isRecord(decoded))
116
138
  return [];
117
139
  const error = isRecord(decoded.error) ? decoded.error : undefined;
140
+ const inner = error && isRecord(error.innererror) ? error.innererror : undefined;
141
+ const metadata = error && isRecord(error.metadata) ? error.metadata : undefined;
118
142
  const response = isRecord(decoded.response) ? decoded.response : undefined;
119
143
  const responseError = response && isRecord(response.error) ? response.error : undefined;
120
144
  const exception = isRecord(decoded.exception) ? decoded.exception : undefined;
121
- return [decoded.code, error?.code, error?.type, error?.status, responseError?.code, exception?.type].filter((value) => typeof value === "string");
145
+ return [
146
+ decoded.code,
147
+ decoded.error_type,
148
+ error?.code,
149
+ error?.type,
150
+ error?.status,
151
+ error?.error_type,
152
+ inner?.code,
153
+ metadata?.error_type,
154
+ responseError?.code,
155
+ response?.error_type,
156
+ exception?.type,
157
+ ].filter((value) => typeof value === "string");
122
158
  }
123
159
  function isRecord(value) {
124
160
  return typeof value === "object" && value !== null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-19629",
3
+ "version": "0.0.0-dev-19630",
4
4
  "name": "@opencode/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.0.0-alpha.1",
32
32
  "@effect/platform-node": "4.0.0-rc.112",
33
- "@opencode/http-recorder": "0.0.0-dev-19629",
33
+ "@opencode/http-recorder": "0.0.0-dev-19630",
34
34
  "@tsconfig/bun": "1.0.9",
35
35
  "@types/bun": "1.4.0",
36
36
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -40,7 +40,7 @@
40
40
  "@aws-sdk/credential-providers": "3.1057.0",
41
41
  "@smithy/eventstream-codec": "4.2.14",
42
42
  "@smithy/util-utf8": "4.2.2",
43
- "@opencode/schema": "0.0.0-dev-19629",
43
+ "@opencode/schema": "0.0.0-dev-19630",
44
44
  "aws4fetch": "1.0.20",
45
45
  "effect": "4.0.0-rc.112",
46
46
  "google-auth-library": "10.5.0"