@gajae-code/ai 0.16.1 → 0.16.3

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.16.3] - 2026-09-04
6
+
7
+ ## [0.16.2] - 2026-09-04
8
+
9
+ - OpenAI Codex GPT-5.6 Sol selections now reject a known non-Pro ChatGPT OAuth account before dispatch, instead of allowing a binding that fails later with the provider's raw entitlement error. The same rejection is normalized for HTTP and streaming provider responses with guidance to choose a callable model or use an API-key credential.
10
+
5
11
  ## [0.16.1] - 2026-09-03
6
12
 
7
13
  ### Added
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Model entitlement facts shared by Codex credential selection and provider
3
+ * error presentation.
4
+ *
5
+ * GPT-5.6 Sol is a Pro-tier ChatGPT Codex model. The usage endpoint is the
6
+ * authority for the account tier; this module only names the model policy and
7
+ * keeps the provider's deterministic rejection wording in one place.
8
+ */
9
+ export declare function requiresOpenAICodexProModel(provider: string, modelId: string | undefined): boolean;
10
+ export declare function requiresStrictOpenAICodexProModel(provider: string, modelId: string | undefined): boolean;
11
+ export declare function isOpenAICodexChatGPTEntitlementError(message: string | undefined, code?: string): boolean;
12
+ export declare function formatOpenAICodexChatGPTEntitlementError(modelId: string | undefined): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/ai",
4
- "version": "0.16.1",
4
+ "version": "0.16.3",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -40,8 +40,8 @@
40
40
  "dependencies": {
41
41
  "@anthropic-ai/sdk": "^0.94.0",
42
42
  "@bufbuild/protobuf": "^2.12.0",
43
- "@gajae-code/natives": "0.16.1",
44
- "@gajae-code/utils": "0.16.1",
43
+ "@gajae-code/natives": "0.16.3",
44
+ "@gajae-code/utils": "0.16.3",
45
45
  "openai": "^6.36.0",
46
46
  "partial-json": "^0.1.7",
47
47
  "zod": "4.4.3"
@@ -26,6 +26,11 @@ import type {
26
26
  UsageReport,
27
27
  } from "./usage";
28
28
 
29
+ import {
30
+ formatOpenAICodexChatGPTEntitlementError,
31
+ requiresOpenAICodexProModel,
32
+ requiresStrictOpenAICodexProModel,
33
+ } from "./utils/codex-entitlement";
29
34
  import { getOAuthApiKey, getOAuthProvider, refreshOAuthToken, resolveOAuthStorageProvider } from "./utils/oauth";
30
35
  import { loginDeepInfra } from "./utils/oauth/deepinfra";
31
36
  import { loginDeepSeek } from "./utils/oauth/deepseek";
@@ -1140,10 +1145,6 @@ export function readBrokerErrorBody(error: unknown): string | undefined {
1140
1145
  }
1141
1146
  }
1142
1147
 
1143
- function requiresOpenAICodexProModel(provider: string, modelId: string | undefined): boolean {
1144
- return provider === "openai-codex" && typeof modelId === "string" && modelId.includes("-spark");
1145
- }
1146
-
1147
1148
  function getUsagePlanType(report: UsageReport | null): string | undefined {
1148
1149
  const metadata = report?.metadata;
1149
1150
  if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return undefined;
@@ -4955,6 +4956,19 @@ export class AuthStorage {
4955
4956
  // non-Pro accounts can still attempt Spark requests (e.g. trial/grandfathered access).
4956
4957
  const enforceProRequirement =
4957
4958
  requiresProModel && candidates.some(candidate => hasOpenAICodexProPlan(candidate.usage));
4959
+ // Spark retains its historical Plus fallback for grandfathered accounts.
4960
+ // Sol is different: a confirmed non-Pro plan cannot call it, so reject the
4961
+ // model before returning an OAuth bearer and letting the turn fail remotely.
4962
+ const strictProRequirement = requiresStrictOpenAICodexProModel(provider, options?.modelId);
4963
+ if (
4964
+ strictProRequirement &&
4965
+ candidates.length > 0 &&
4966
+ candidates.every(
4967
+ candidate => getUsagePlanType(candidate.usage) !== undefined && !hasOpenAICodexProPlan(candidate.usage),
4968
+ )
4969
+ ) {
4970
+ throw new Error(formatOpenAICodexChatGPTEntitlementError(options?.modelId));
4971
+ }
4958
4972
 
4959
4973
  const fallback = candidates[0];
4960
4974
 
@@ -51,6 +51,10 @@ import {
51
51
  normalizeSystemPrompts,
52
52
  sanitizeOpenAIResponsesHistoryItemsForReplay,
53
53
  } from "../utils";
54
+ import {
55
+ formatOpenAICodexChatGPTEntitlementError,
56
+ isOpenAICodexChatGPTEntitlementError,
57
+ } from "../utils/codex-entitlement";
54
58
  import { AssistantMessageEventStream } from "../utils/event-stream";
55
59
  import { STREAM_FIRST_EVENT_TIMEOUT_PROVIDER_CODE, transportFailureFacts } from "../utils/fallback-transport";
56
60
  import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
@@ -1234,7 +1238,7 @@ function handleCodexStreamEvent(args: {
1234
1238
  }
1235
1239
 
1236
1240
  if (eventType === "error" || eventType === "response.failed") {
1237
- throw createCodexProviderStreamError(rawEvent);
1241
+ throw createCodexProviderStreamError(rawEvent, model.id);
1238
1242
  }
1239
1243
 
1240
1244
  return firstTokenTime;
@@ -2853,7 +2857,11 @@ async function openCodexSseEventStream(
2853
2857
  updateCodexSessionMetadataFromHeaders(state, response.headers);
2854
2858
  if (!response.ok) {
2855
2859
  const info = await parseCodexError(response);
2856
- const error = new Error(info.friendlyMessage || info.message);
2860
+ const error = new Error(
2861
+ isOpenAICodexChatGPTEntitlementError(info.message, info.code)
2862
+ ? formatOpenAICodexChatGPTEntitlementError(body.model)
2863
+ : info.friendlyMessage || info.message,
2864
+ );
2857
2865
  (error as { headers?: Headers; status?: number }).headers = response.headers;
2858
2866
  (error as { headers?: Headers; status?: number }).status = response.status;
2859
2867
  (error as { code?: string }).code = info.code;
@@ -3235,11 +3243,12 @@ function isRetryableCodexFailureEvent(rawEvent: Record<string, unknown>): boolea
3235
3243
  return !!message && CODEX_RETRYABLE_EVENT_MESSAGE.test(message);
3236
3244
  }
3237
3245
 
3238
- function createCodexProviderStreamError(rawEvent: Record<string, unknown>): CodexProviderStreamError {
3246
+ function createCodexProviderStreamError(rawEvent: Record<string, unknown>, modelId: string): CodexProviderStreamError {
3239
3247
  const code = getCodexEventErrorCode(rawEvent);
3240
3248
  const message = getCodexEventErrorMessage(rawEvent);
3241
- const formattedMessage =
3242
- typeof rawEvent.type === "string" && rawEvent.type === "error"
3249
+ const formattedMessage = isOpenAICodexChatGPTEntitlementError(message, code)
3250
+ ? formatOpenAICodexChatGPTEntitlementError(modelId)
3251
+ : typeof rawEvent.type === "string" && rawEvent.type === "error"
3243
3252
  ? formatCodexErrorEvent(rawEvent, code, message)
3244
3253
  : (formatCodexFailure(rawEvent) ?? "Codex response failed");
3245
3254
  return new CodexProviderStreamError(
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Model entitlement facts shared by Codex credential selection and provider
3
+ * error presentation.
4
+ *
5
+ * GPT-5.6 Sol is a Pro-tier ChatGPT Codex model. The usage endpoint is the
6
+ * authority for the account tier; this module only names the model policy and
7
+ * keeps the provider's deterministic rejection wording in one place.
8
+ */
9
+ export declare function requiresOpenAICodexProModel(provider: string, modelId: string | undefined): boolean;
10
+ export declare function requiresStrictOpenAICodexProModel(provider: string, modelId: string | undefined): boolean;
11
+ export declare function isOpenAICodexChatGPTEntitlementError(message: string | undefined, code?: string): boolean;
12
+ export declare function formatOpenAICodexChatGPTEntitlementError(modelId: string | undefined): string;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Model entitlement facts shared by Codex credential selection and provider
3
+ * error presentation.
4
+ *
5
+ * GPT-5.6 Sol is a Pro-tier ChatGPT Codex model. The usage endpoint is the
6
+ * authority for the account tier; this module only names the model policy and
7
+ * keeps the provider's deterministic rejection wording in one place.
8
+ */
9
+
10
+ export function requiresOpenAICodexProModel(provider: string, modelId: string | undefined): boolean {
11
+ return (
12
+ provider === "openai-codex" &&
13
+ typeof modelId === "string" &&
14
+ (modelId.toLowerCase().includes("-spark") || modelId.toLowerCase() === "gpt-5.6-sol")
15
+ );
16
+ }
17
+
18
+ export function requiresStrictOpenAICodexProModel(provider: string, modelId: string | undefined): boolean {
19
+ return provider === "openai-codex" && modelId?.toLowerCase() === "gpt-5.6-sol";
20
+ }
21
+
22
+ export function isOpenAICodexChatGPTEntitlementError(message: string | undefined, code?: string): boolean {
23
+ return (
24
+ /\bnot supported when using codex with a chatgpt account\b/i.test(message ?? "") &&
25
+ (code === undefined || code.toLowerCase() === "invalid_request_error")
26
+ );
27
+ }
28
+
29
+ export function formatOpenAICodexChatGPTEntitlementError(modelId: string | undefined): string {
30
+ const safeModelId = modelId
31
+ ?.replace(/[\x00-\x1f\x7f-\x9f]+/gu, " ")
32
+ .trim()
33
+ .slice(0, 128);
34
+ const model = safeModelId ? ` model "${safeModelId}"` : " model";
35
+ return `This ChatGPT Codex account cannot use${model}. Select a model available to this ChatGPT account, such as "gpt-5.5", or use an API-key credential that supports the model.`;
36
+ }