@ljoukov/llm 3.0.0 → 3.0.2

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/dist/index.js CHANGED
@@ -136,7 +136,7 @@ function getGeminiProPricing(modelId) {
136
136
  if (modelId.includes("gemini-2.5-pro")) {
137
137
  return GEMINI_2_5_PRO_PRICING;
138
138
  }
139
- if (modelId.includes("gemini-3-pro")) {
139
+ if (modelId.includes("gemini-3-pro") || modelId.includes("gemini-3.1-pro")) {
140
140
  return GEMINI_3_PRO_PREVIEW_PRICING;
141
141
  }
142
142
  return void 0;
@@ -336,8 +336,6 @@ function parseEnvLine(line) {
336
336
  // src/openai/chatgpt-auth.ts
337
337
  var CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_URL";
338
338
  var CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_STORE";
339
- var CHATGPT_AUTH_SERVER_URL_ENV = "CHATGPT_AUTH_SERVER_URL";
340
- var CHATGPT_AUTH_SERVER_STORE_ENV = "CHATGPT_AUTH_SERVER_STORE";
341
339
  var CHATGPT_AUTH_API_KEY_ENV = "CHATGPT_AUTH_API_KEY";
342
340
  var CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY";
343
341
  var CHATGPT_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
@@ -469,7 +467,7 @@ async function refreshChatGptOauthToken(refreshToken, fallback) {
469
467
  }
470
468
  async function getChatGptAuthProfile() {
471
469
  loadLocalEnv();
472
- const tokenProviderUrl = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV] ?? process.env[CHATGPT_AUTH_SERVER_URL_ENV];
470
+ const tokenProviderUrl = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV];
473
471
  const tokenProviderKey = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV] ?? process.env[CHATGPT_AUTH_API_KEY_ENV];
474
472
  if (tokenProviderUrl && tokenProviderUrl.trim().length > 0 && tokenProviderKey && tokenProviderKey.trim().length > 0) {
475
473
  if (cachedProfile && !isExpired(cachedProfile)) {
@@ -480,7 +478,7 @@ async function getChatGptAuthProfile() {
480
478
  }
481
479
  refreshPromise = (async () => {
482
480
  try {
483
- const store = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV] ?? process.env[CHATGPT_AUTH_SERVER_STORE_ENV];
481
+ const store = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV];
484
482
  const profile = await fetchChatGptAuthProfileFromTokenProvider({
485
483
  baseUrl: tokenProviderUrl,
486
484
  apiKey: tokenProviderKey,
@@ -683,22 +681,588 @@ function extractChatGptAccountId(token) {
683
681
  return typeof namespaced === "string" && namespaced.length > 0 ? namespaced : void 0;
684
682
  }
685
683
 
684
+ // src/openai/responses-websocket.ts
685
+ import WebSocket from "ws";
686
+ var OPENAI_BETA_RESPONSES_WEBSOCKETS_V2 = "responses_websockets=2026-02-06";
687
+ var ResponsesWebSocketHttpError = class extends Error {
688
+ status;
689
+ body;
690
+ headers;
691
+ constructor(options) {
692
+ super(options.message);
693
+ this.name = "ResponsesWebSocketHttpError";
694
+ this.status = options.status;
695
+ this.body = options.body;
696
+ this.headers = options.headers;
697
+ }
698
+ };
699
+ function resolveResponsesWebSocketMode(raw, fallback = "auto") {
700
+ const value = raw?.trim().toLowerCase();
701
+ if (value === "auto" || value === "off" || value === "only") {
702
+ return value;
703
+ }
704
+ return fallback;
705
+ }
706
+ function mergeOpenAiBetaHeader(existing, required) {
707
+ const parts = /* @__PURE__ */ new Set();
708
+ for (const part of (existing ?? "").split(",")) {
709
+ const trimmed = part.trim();
710
+ if (trimmed.length > 0) {
711
+ parts.add(trimmed);
712
+ }
713
+ }
714
+ const normalizedRequired = required.trim();
715
+ if (normalizedRequired.length > 0) {
716
+ parts.add(normalizedRequired);
717
+ }
718
+ return Array.from(parts).join(", ");
719
+ }
720
+ function toWebSocketUrl(httpOrHttpsUrl) {
721
+ const parsed = new URL(httpOrHttpsUrl);
722
+ if (parsed.protocol === "https:") {
723
+ parsed.protocol = "wss:";
724
+ } else if (parsed.protocol === "http:") {
725
+ parsed.protocol = "ws:";
726
+ } else if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
727
+ throw new Error(`Unsupported websocket URL protocol: ${parsed.protocol}`);
728
+ }
729
+ return parsed.toString();
730
+ }
731
+ function isResponsesWebSocketUnsupportedError(error) {
732
+ if (error instanceof ResponsesWebSocketHttpError) {
733
+ return [400, 404, 405, 406, 426, 501].includes(error.status);
734
+ }
735
+ const message = error instanceof Error ? error.message.toLowerCase() : "";
736
+ return message.includes("unexpected server response: 426");
737
+ }
738
+ function createAdaptiveResponsesStream(options) {
739
+ let resolved = null;
740
+ let websocketSelected = false;
741
+ let fallbackSelected = false;
742
+ const activateFallback = (error) => {
743
+ options.onWebSocketFallback?.(error);
744
+ fallbackSelected = true;
745
+ websocketSelected = false;
746
+ const fallback = options.createFallbackStream();
747
+ resolved = Promise.resolve(fallback);
748
+ return fallback;
749
+ };
750
+ const getStream = async () => {
751
+ if (resolved) {
752
+ return await resolved;
753
+ }
754
+ resolved = (async () => {
755
+ if (options.mode === "off") {
756
+ fallbackSelected = true;
757
+ return options.createFallbackStream();
758
+ }
759
+ try {
760
+ const stream = await options.createWebSocketStream();
761
+ websocketSelected = true;
762
+ return stream;
763
+ } catch (error) {
764
+ if (options.mode === "only") {
765
+ throw error;
766
+ }
767
+ return activateFallback(error);
768
+ }
769
+ })();
770
+ return await resolved;
771
+ };
772
+ return {
773
+ async *[Symbol.asyncIterator]() {
774
+ const stream = await getStream();
775
+ let yielded = 0;
776
+ try {
777
+ for await (const event of stream) {
778
+ yielded += 1;
779
+ yield event;
780
+ }
781
+ } catch (error) {
782
+ if (options.mode !== "only" && websocketSelected && !fallbackSelected && yielded === 0) {
783
+ const fallback = activateFallback(error);
784
+ for await (const event of fallback) {
785
+ yield event;
786
+ }
787
+ return;
788
+ }
789
+ throw error;
790
+ }
791
+ },
792
+ async finalResponse() {
793
+ const stream = await getStream();
794
+ try {
795
+ return await stream.finalResponse();
796
+ } catch (error) {
797
+ if (options.mode === "only" || !websocketSelected || fallbackSelected) {
798
+ throw error;
799
+ }
800
+ const fallback = activateFallback(error);
801
+ return await fallback.finalResponse();
802
+ }
803
+ },
804
+ close() {
805
+ void getStream().then((stream) => stream.close()).catch(() => {
806
+ });
807
+ }
808
+ };
809
+ }
810
+ async function createResponsesWebSocketStream(options) {
811
+ const completionTypes = new Set(
812
+ options.completionEventTypes ?? ["response.completed", "response.failed", "response.done"]
813
+ );
814
+ const { socket, responseHeaders } = await connectWebSocket({
815
+ url: options.url,
816
+ headers: options.headers,
817
+ signal: options.signal
818
+ });
819
+ const queue = createAsyncQueue();
820
+ let settled = false;
821
+ let finalResponse = null;
822
+ let latestResponse = null;
823
+ let idleTimer = null;
824
+ let resolveFinal = null;
825
+ let rejectFinal = null;
826
+ const finalPromise = new Promise((resolve, reject) => {
827
+ resolveFinal = resolve;
828
+ rejectFinal = reject;
829
+ });
830
+ void finalPromise.catch(() => {
831
+ });
832
+ const clearIdleTimer = () => {
833
+ if (idleTimer) {
834
+ clearTimeout(idleTimer);
835
+ idleTimer = null;
836
+ }
837
+ };
838
+ const closeSocket = () => {
839
+ try {
840
+ if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
841
+ socket.close();
842
+ }
843
+ } catch {
844
+ }
845
+ };
846
+ const complete = (response) => {
847
+ if (settled) {
848
+ return;
849
+ }
850
+ settled = true;
851
+ clearIdleTimer();
852
+ finalResponse = response;
853
+ resolveFinal?.(response);
854
+ queue.close();
855
+ closeSocket();
856
+ };
857
+ const fail = (error) => {
858
+ if (settled) {
859
+ return;
860
+ }
861
+ settled = true;
862
+ clearIdleTimer();
863
+ rejectFinal?.(error);
864
+ queue.fail(error);
865
+ closeSocket();
866
+ };
867
+ const restartIdleTimer = () => {
868
+ clearIdleTimer();
869
+ const idleTimeoutMs = options.idleTimeoutMs;
870
+ if (!idleTimeoutMs || idleTimeoutMs <= 0 || settled) {
871
+ return;
872
+ }
873
+ idleTimer = setTimeout(() => {
874
+ fail(new Error(`Responses WebSocket idle timeout after ${idleTimeoutMs}ms.`));
875
+ }, idleTimeoutMs);
876
+ };
877
+ const onAbort = () => {
878
+ const error = createAbortError(options.signal?.reason);
879
+ fail(error);
880
+ };
881
+ if (options.signal) {
882
+ if (options.signal.aborted) {
883
+ socket.close();
884
+ throw createAbortError(options.signal.reason);
885
+ }
886
+ options.signal.addEventListener("abort", onAbort, { once: true });
887
+ }
888
+ const cleanup = () => {
889
+ clearIdleTimer();
890
+ socket.removeAllListeners();
891
+ if (options.signal) {
892
+ options.signal.removeEventListener("abort", onAbort);
893
+ }
894
+ };
895
+ socket.on("message", (raw) => {
896
+ restartIdleTimer();
897
+ const parsed = parseWebSocketPayload(raw);
898
+ if (!parsed) {
899
+ return;
900
+ }
901
+ const error = mapWebSocketErrorEvent(parsed);
902
+ if (error) {
903
+ fail(error);
904
+ return;
905
+ }
906
+ const event = parsed;
907
+ if (isObjectWithResponse(event)) {
908
+ latestResponse = event.response;
909
+ }
910
+ queue.push(event);
911
+ const type = typeof event.type === "string" ? event.type : "";
912
+ if (completionTypes.has(type)) {
913
+ const completedResponse = normalizeFinalResponse(
914
+ type,
915
+ event.response,
916
+ latestResponse,
917
+ responseHeaders
918
+ );
919
+ complete(completedResponse);
920
+ }
921
+ });
922
+ socket.on("error", (error) => {
923
+ fail(new Error(`Responses WebSocket error: ${error.message}`));
924
+ });
925
+ socket.on("close", (_code, _reason) => {
926
+ if (settled) {
927
+ cleanup();
928
+ return;
929
+ }
930
+ fail(new Error("Responses WebSocket closed before completion."));
931
+ cleanup();
932
+ });
933
+ restartIdleTimer();
934
+ const payload = serializeRequestPayload(options.request);
935
+ await new Promise((resolve, reject) => {
936
+ socket.send(payload, (error) => {
937
+ if (error) {
938
+ reject(error);
939
+ } else {
940
+ resolve();
941
+ }
942
+ });
943
+ }).catch((error) => {
944
+ fail(new Error(`Failed to send Responses WebSocket request: ${errorToMessage(error)}`));
945
+ throw error instanceof Error ? error : new Error(errorToMessage(error));
946
+ });
947
+ return {
948
+ async *[Symbol.asyncIterator]() {
949
+ try {
950
+ for await (const event of queue.iterable) {
951
+ yield event;
952
+ }
953
+ } finally {
954
+ if (!settled) {
955
+ closeSocket();
956
+ }
957
+ }
958
+ },
959
+ async finalResponse() {
960
+ return await finalPromise;
961
+ },
962
+ close() {
963
+ if (settled) {
964
+ return;
965
+ }
966
+ const response = finalResponse ?? latestResponse ?? { status: "cancelled" };
967
+ complete(response);
968
+ cleanup();
969
+ }
970
+ };
971
+ }
972
+ async function connectWebSocket(options) {
973
+ return await new Promise((resolve, reject) => {
974
+ const socket = new WebSocket(options.url, {
975
+ headers: options.headers,
976
+ handshakeTimeout: 3e4
977
+ });
978
+ let settled = false;
979
+ let responseBody = "";
980
+ const rejectOnce = (error) => {
981
+ if (settled) {
982
+ return;
983
+ }
984
+ settled = true;
985
+ cleanup();
986
+ try {
987
+ socket.terminate();
988
+ } catch {
989
+ }
990
+ reject(error);
991
+ };
992
+ const resolveOnce = (result) => {
993
+ if (settled) {
994
+ return;
995
+ }
996
+ settled = true;
997
+ cleanup(false);
998
+ resolve(result);
999
+ };
1000
+ const onAbort = () => {
1001
+ rejectOnce(createAbortError(options.signal?.reason));
1002
+ };
1003
+ const cleanup = (removeAbortListener = true) => {
1004
+ socket.removeListener("open", onOpen);
1005
+ socket.removeListener("error", onError);
1006
+ socket.removeListener("unexpected-response", onUnexpectedResponse);
1007
+ if (removeAbortListener && options.signal) {
1008
+ options.signal.removeEventListener("abort", onAbort);
1009
+ }
1010
+ };
1011
+ const onOpen = () => {
1012
+ const headers = normalizeUpgradeHeaders(socket);
1013
+ resolveOnce({ socket, responseHeaders: headers });
1014
+ };
1015
+ const onError = (error) => {
1016
+ rejectOnce(new Error(`Responses WebSocket connection failed: ${error.message}`));
1017
+ };
1018
+ const onUnexpectedResponse = (_request, response) => {
1019
+ if (typeof response.setEncoding === "function") {
1020
+ response.setEncoding("utf8");
1021
+ }
1022
+ response.on("data", (chunk) => {
1023
+ responseBody += typeof chunk === "string" ? chunk : chunk.toString("utf8");
1024
+ });
1025
+ response.on("end", () => {
1026
+ const status = Number(response.statusCode ?? 0);
1027
+ const headers = {};
1028
+ const rawHeaders = response.headers ?? {};
1029
+ for (const [key, value] of Object.entries(rawHeaders)) {
1030
+ if (typeof value === "string") {
1031
+ headers[key] = value;
1032
+ } else if (Array.isArray(value)) {
1033
+ headers[key] = value.join(", ");
1034
+ }
1035
+ }
1036
+ rejectOnce(
1037
+ new ResponsesWebSocketHttpError({
1038
+ status: Number.isFinite(status) && status > 0 ? status : 500,
1039
+ message: `Responses WebSocket upgrade failed${status ? ` (${status})` : ""}.`,
1040
+ body: responseBody || void 0,
1041
+ headers
1042
+ })
1043
+ );
1044
+ });
1045
+ response.on("error", (error) => {
1046
+ rejectOnce(
1047
+ new ResponsesWebSocketHttpError({
1048
+ status: Number(response.statusCode ?? 500),
1049
+ message: `Responses WebSocket upgrade failed: ${error.message}`,
1050
+ body: responseBody || void 0
1051
+ })
1052
+ );
1053
+ });
1054
+ };
1055
+ socket.once("open", onOpen);
1056
+ socket.once("error", onError);
1057
+ socket.once("unexpected-response", onUnexpectedResponse);
1058
+ if (options.signal) {
1059
+ if (options.signal.aborted) {
1060
+ onAbort();
1061
+ return;
1062
+ }
1063
+ options.signal.addEventListener("abort", onAbort, { once: true });
1064
+ }
1065
+ });
1066
+ }
1067
+ function normalizeUpgradeHeaders(socket) {
1068
+ const maybeUpgradeResponse = socket._req?.res;
1069
+ const raw = maybeUpgradeResponse?.headers ?? {};
1070
+ const normalized = {};
1071
+ for (const [key, value] of Object.entries(raw)) {
1072
+ if (typeof value === "string") {
1073
+ normalized[key.toLowerCase()] = value;
1074
+ } else if (Array.isArray(value)) {
1075
+ normalized[key.toLowerCase()] = value.join(", ");
1076
+ }
1077
+ }
1078
+ return normalized;
1079
+ }
1080
+ function parseWebSocketPayload(raw) {
1081
+ const text = toUtf8(raw);
1082
+ if (!text) {
1083
+ return null;
1084
+ }
1085
+ try {
1086
+ const parsed = JSON.parse(text);
1087
+ if (parsed && typeof parsed === "object") {
1088
+ return parsed;
1089
+ }
1090
+ } catch {
1091
+ return null;
1092
+ }
1093
+ return null;
1094
+ }
1095
+ function toUtf8(raw) {
1096
+ if (typeof raw === "string") {
1097
+ return raw;
1098
+ }
1099
+ if (raw instanceof ArrayBuffer) {
1100
+ return Buffer.from(raw).toString("utf8");
1101
+ }
1102
+ if (Array.isArray(raw)) {
1103
+ const chunks = raw.map((chunk) => {
1104
+ if (typeof chunk === "string") {
1105
+ return Buffer.from(chunk, "utf8");
1106
+ }
1107
+ return Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1108
+ });
1109
+ return Buffer.concat(chunks).toString("utf8");
1110
+ }
1111
+ return Buffer.isBuffer(raw) ? raw.toString("utf8") : Buffer.from(raw).toString("utf8");
1112
+ }
1113
+ function mapWebSocketErrorEvent(payload) {
1114
+ if (payload.type !== "error") {
1115
+ return null;
1116
+ }
1117
+ const status = resolveNumericStatus(payload.status) ?? resolveNumericStatus(payload.status_code);
1118
+ if (!status || status < 400) {
1119
+ const message = errorToMessage(payload.error) || "Responses WebSocket returned an error event.";
1120
+ return new Error(message);
1121
+ }
1122
+ const headers = mapErrorHeaders(payload.headers);
1123
+ const body = payload.error && typeof payload.error === "object" ? JSON.stringify({ error: payload.error }, null, 2) : void 0;
1124
+ return new ResponsesWebSocketHttpError({
1125
+ status,
1126
+ message: `Responses WebSocket returned status ${status}.`,
1127
+ body,
1128
+ headers
1129
+ });
1130
+ }
1131
+ function mapErrorHeaders(value) {
1132
+ if (!value || typeof value !== "object") {
1133
+ return void 0;
1134
+ }
1135
+ const headers = {};
1136
+ for (const [key, entry] of Object.entries(value)) {
1137
+ if (typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean") {
1138
+ headers[key.toLowerCase()] = String(entry);
1139
+ }
1140
+ }
1141
+ return Object.keys(headers).length > 0 ? headers : void 0;
1142
+ }
1143
+ function resolveNumericStatus(value) {
1144
+ if (typeof value === "number" && Number.isFinite(value)) {
1145
+ return Math.floor(value);
1146
+ }
1147
+ if (typeof value === "string" && value.trim().length > 0) {
1148
+ const parsed = Number(value);
1149
+ if (Number.isFinite(parsed)) {
1150
+ return Math.floor(parsed);
1151
+ }
1152
+ }
1153
+ return null;
1154
+ }
1155
+ function normalizeFinalResponse(eventType, eventResponse, latestResponse, responseHeaders) {
1156
+ const response = eventResponse && typeof eventResponse === "object" ? { ...eventResponse } : latestResponse ? { ...latestResponse } : {};
1157
+ if (typeof response.status !== "string") {
1158
+ if (eventType === "response.failed") {
1159
+ response.status = "failed";
1160
+ } else if (eventType === "response.done") {
1161
+ response.status = "completed";
1162
+ } else if (eventType === "response.completed") {
1163
+ response.status = "completed";
1164
+ }
1165
+ }
1166
+ const upgradeModel = responseHeaders["openai-model"];
1167
+ if (typeof response.model !== "string" && upgradeModel) {
1168
+ response.model = upgradeModel;
1169
+ }
1170
+ return response;
1171
+ }
1172
+ function serializeRequestPayload(request) {
1173
+ if (!request || typeof request !== "object" || Array.isArray(request)) {
1174
+ throw new Error("Responses WebSocket request must be a JSON object.");
1175
+ }
1176
+ const body = request;
1177
+ const payload = typeof body.type === "string" ? body : { type: "response.create", ...body };
1178
+ return JSON.stringify(payload);
1179
+ }
1180
+ function isObjectWithResponse(event) {
1181
+ return Boolean(event.response && typeof event.response === "object");
1182
+ }
1183
+ function errorToMessage(error) {
1184
+ if (error instanceof Error) {
1185
+ return error.message;
1186
+ }
1187
+ if (typeof error === "string") {
1188
+ return error;
1189
+ }
1190
+ try {
1191
+ return JSON.stringify(error);
1192
+ } catch {
1193
+ return String(error);
1194
+ }
1195
+ }
1196
+ function createAbortError(reason) {
1197
+ const error = new Error(
1198
+ reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "Request aborted."
1199
+ );
1200
+ error.name = "AbortError";
1201
+ return error;
1202
+ }
1203
+
686
1204
  // src/openai/chatgpt-codex.ts
687
1205
  var CHATGPT_CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
1206
+ var CHATGPT_RESPONSES_EXPERIMENTAL_HEADER = "responses=experimental";
1207
+ var cachedResponsesWebSocketMode = null;
1208
+ var chatGptResponsesWebSocketDisabled = false;
688
1209
  async function streamChatGptCodexResponse(options) {
689
1210
  const { access, accountId } = await getChatGptAuthProfile();
690
- const headers = {
691
- Authorization: `Bearer ${access}`,
692
- "chatgpt-account-id": accountId,
693
- "OpenAI-Beta": "responses=experimental",
694
- originator: "llm",
695
- "User-Agent": buildUserAgent(),
696
- Accept: "text/event-stream",
697
- "Content-Type": "application/json"
1211
+ const mode = resolveChatGptResponsesWebSocketMode();
1212
+ const fallbackStreamFactory = () => {
1213
+ const streamPromise = streamChatGptCodexResponseSse({
1214
+ request: options.request,
1215
+ access,
1216
+ accountId,
1217
+ sessionId: options.sessionId,
1218
+ signal: options.signal
1219
+ });
1220
+ return {
1221
+ async *[Symbol.asyncIterator]() {
1222
+ const stream = await streamPromise;
1223
+ for await (const event of stream) {
1224
+ yield event;
1225
+ }
1226
+ },
1227
+ async finalResponse() {
1228
+ return {};
1229
+ },
1230
+ close() {
1231
+ }
1232
+ };
698
1233
  };
699
- if (options.sessionId) {
700
- headers.session_id = options.sessionId;
1234
+ if (mode === "off" || chatGptResponsesWebSocketDisabled) {
1235
+ return fallbackStreamFactory();
701
1236
  }
1237
+ const websocketHeaders = buildChatGptCodexHeaders({
1238
+ access,
1239
+ accountId,
1240
+ sessionId: options.sessionId,
1241
+ useWebSocket: true
1242
+ });
1243
+ return createAdaptiveResponsesStream({
1244
+ mode,
1245
+ createWebSocketStream: async () => await createResponsesWebSocketStream({
1246
+ url: toWebSocketUrl(CHATGPT_CODEX_ENDPOINT),
1247
+ headers: websocketHeaders,
1248
+ request: options.request,
1249
+ signal: options.signal
1250
+ }),
1251
+ createFallbackStream: fallbackStreamFactory,
1252
+ onWebSocketFallback: () => {
1253
+ chatGptResponsesWebSocketDisabled = true;
1254
+ }
1255
+ });
1256
+ }
1257
+ async function streamChatGptCodexResponseSse(options) {
1258
+ const headers = buildChatGptCodexHeaders({
1259
+ access: options.access,
1260
+ accountId: options.accountId,
1261
+ sessionId: options.sessionId,
1262
+ useWebSocket: false
1263
+ });
1264
+ headers.Accept = "text/event-stream";
1265
+ headers["Content-Type"] = "application/json";
702
1266
  const response = await fetch(CHATGPT_CODEX_ENDPOINT, {
703
1267
  method: "POST",
704
1268
  headers,
@@ -715,20 +1279,67 @@ async function streamChatGptCodexResponse(options) {
715
1279
  }
716
1280
  return parseEventStream(body);
717
1281
  }
1282
+ function resolveChatGptResponsesWebSocketMode() {
1283
+ if (cachedResponsesWebSocketMode) {
1284
+ return cachedResponsesWebSocketMode;
1285
+ }
1286
+ cachedResponsesWebSocketMode = resolveResponsesWebSocketMode(
1287
+ process.env.CHATGPT_RESPONSES_WEBSOCKET_MODE ?? process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
1288
+ "auto"
1289
+ );
1290
+ return cachedResponsesWebSocketMode;
1291
+ }
1292
+ function buildChatGptCodexHeaders(options) {
1293
+ const openAiBeta = options.useWebSocket ? mergeOpenAiBetaHeader(
1294
+ CHATGPT_RESPONSES_EXPERIMENTAL_HEADER,
1295
+ OPENAI_BETA_RESPONSES_WEBSOCKETS_V2
1296
+ ) : CHATGPT_RESPONSES_EXPERIMENTAL_HEADER;
1297
+ const headers = {
1298
+ Authorization: `Bearer ${options.access}`,
1299
+ "chatgpt-account-id": options.accountId,
1300
+ "OpenAI-Beta": openAiBeta,
1301
+ originator: "llm",
1302
+ "User-Agent": buildUserAgent()
1303
+ };
1304
+ if (options.sessionId) {
1305
+ headers.session_id = options.sessionId;
1306
+ }
1307
+ return headers;
1308
+ }
718
1309
  async function collectChatGptCodexResponse(options) {
719
- let stream;
720
- try {
721
- stream = await streamChatGptCodexResponse(options);
722
- } catch (error) {
723
- if (shouldRetryWithoutReasoningSummary(options.request, error)) {
724
- stream = await streamChatGptCodexResponse({
1310
+ let requestForAttempt = options.request;
1311
+ let retriedWithoutReasoningSummary = false;
1312
+ let retriedViaSseFallback = false;
1313
+ while (true) {
1314
+ let sawAnyDelta = false;
1315
+ try {
1316
+ const stream = await streamChatGptCodexResponse({
725
1317
  ...options,
726
- request: removeReasoningSummary(options.request)
1318
+ request: requestForAttempt
727
1319
  });
728
- } else {
1320
+ return await collectChatGptCodexStream({
1321
+ stream,
1322
+ onDelta: (delta) => {
1323
+ sawAnyDelta = true;
1324
+ options.onDelta?.(delta);
1325
+ }
1326
+ });
1327
+ } catch (error) {
1328
+ if (!sawAnyDelta && !retriedViaSseFallback && shouldRetryViaSseFallback(error) && !chatGptResponsesWebSocketDisabled) {
1329
+ chatGptResponsesWebSocketDisabled = true;
1330
+ retriedViaSseFallback = true;
1331
+ continue;
1332
+ }
1333
+ if (!retriedWithoutReasoningSummary && shouldRetryWithoutReasoningSummary(requestForAttempt, error)) {
1334
+ requestForAttempt = removeReasoningSummary(requestForAttempt);
1335
+ retriedWithoutReasoningSummary = true;
1336
+ continue;
1337
+ }
729
1338
  throw error;
730
1339
  }
731
1340
  }
1341
+ }
1342
+ async function collectChatGptCodexStream(options) {
732
1343
  const toolCalls = /* @__PURE__ */ new Map();
733
1344
  const toolCallOrder = [];
734
1345
  const webSearchCalls = /* @__PURE__ */ new Map();
@@ -741,7 +1352,7 @@ async function collectChatGptCodexResponse(options) {
741
1352
  let model;
742
1353
  let status;
743
1354
  let blocked = false;
744
- for await (const event of stream) {
1355
+ for await (const event of options.stream) {
745
1356
  const type = typeof event.type === "string" ? event.type : void 0;
746
1357
  if (type === "response.output_text.delta") {
747
1358
  const delta = typeof event.delta === "string" ? event.delta : "";
@@ -865,6 +1476,16 @@ function shouldRetryWithoutReasoningSummary(request, error) {
865
1476
  const message = error.message.toLowerCase();
866
1477
  return message.includes("unsupported parameter") && message.includes("reasoning.summary");
867
1478
  }
1479
+ function shouldRetryViaSseFallback(error) {
1480
+ if (!(error instanceof Error)) {
1481
+ return false;
1482
+ }
1483
+ if (error.name === "AbortError") {
1484
+ return false;
1485
+ }
1486
+ const message = error.message.toLowerCase();
1487
+ return message.includes("responses websocket");
1488
+ }
868
1489
  function removeReasoningSummary(request) {
869
1490
  const reasoning = request.reasoning;
870
1491
  if (!reasoning?.summary) {
@@ -1112,14 +1733,16 @@ async function runFireworksCall(fn) {
1112
1733
  }
1113
1734
 
1114
1735
  // src/fireworks/models.ts
1115
- var FIREWORKS_MODEL_IDS = ["kimi-k2.5", "glm-5", "minimax-m2.1"];
1736
+ var FIREWORKS_MODEL_IDS = ["kimi-k2.5", "glm-5", "minimax-m2.1", "gpt-oss-120b"];
1116
1737
  var FIREWORKS_DEFAULT_KIMI_MODEL = "kimi-k2.5";
1117
1738
  var FIREWORKS_DEFAULT_GLM_MODEL = "glm-5";
1118
1739
  var FIREWORKS_DEFAULT_MINIMAX_MODEL = "minimax-m2.1";
1740
+ var FIREWORKS_DEFAULT_GPT_OSS_120B_MODEL = "gpt-oss-120b";
1119
1741
  var FIREWORKS_CANONICAL_MODEL_IDS = {
1120
1742
  "kimi-k2.5": "accounts/fireworks/models/kimi-k2p5",
1121
1743
  "glm-5": "accounts/fireworks/models/glm-5",
1122
- "minimax-m2.1": "accounts/fireworks/models/minimax-m2p1"
1744
+ "minimax-m2.1": "accounts/fireworks/models/minimax-m2p1",
1745
+ "gpt-oss-120b": "accounts/fireworks/models/gpt-oss-120b"
1123
1746
  };
1124
1747
  function isFireworksModelId(value) {
1125
1748
  return FIREWORKS_MODEL_IDS.includes(value.trim());
@@ -1200,6 +1823,7 @@ function getGoogleAuthOptions(scopes) {
1200
1823
  // src/google/client.ts
1201
1824
  var GEMINI_MODEL_IDS = [
1202
1825
  "gemini-3-pro-preview",
1826
+ "gemini-3.1-pro-preview",
1203
1827
  "gemini-3-flash-preview",
1204
1828
  "gemini-2.5-pro",
1205
1829
  "gemini-flash-latest",
@@ -1466,6 +2090,8 @@ var cachedApiKey2 = null;
1466
2090
  var cachedClient2 = null;
1467
2091
  var cachedFetch2 = null;
1468
2092
  var cachedTimeoutMs2 = null;
2093
+ var openAiResponsesWebSocketMode = null;
2094
+ var openAiResponsesWebSocketDisabled = false;
1469
2095
  var DEFAULT_OPENAI_TIMEOUT_MS = 15 * 6e4;
1470
2096
  function resolveOpenAiTimeoutMs() {
1471
2097
  if (cachedTimeoutMs2 !== null) {
@@ -1493,6 +2119,97 @@ function getOpenAiFetch() {
1493
2119
  });
1494
2120
  return cachedFetch2;
1495
2121
  }
2122
+ function resolveOpenAiBaseUrl() {
2123
+ loadLocalEnv();
2124
+ return process.env.OPENAI_BASE_URL?.trim() || "https://api.openai.com/v1";
2125
+ }
2126
+ function resolveOpenAiResponsesWebSocketMode() {
2127
+ if (openAiResponsesWebSocketMode) {
2128
+ return openAiResponsesWebSocketMode;
2129
+ }
2130
+ loadLocalEnv();
2131
+ openAiResponsesWebSocketMode = resolveResponsesWebSocketMode(
2132
+ process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
2133
+ "auto"
2134
+ );
2135
+ return openAiResponsesWebSocketMode;
2136
+ }
2137
+ function wrapFallbackStream(stream) {
2138
+ return {
2139
+ async *[Symbol.asyncIterator]() {
2140
+ for await (const event of stream) {
2141
+ yield event;
2142
+ }
2143
+ },
2144
+ async finalResponse() {
2145
+ return await stream.finalResponse();
2146
+ },
2147
+ close() {
2148
+ const maybeClose = stream;
2149
+ if (typeof maybeClose.close === "function") {
2150
+ maybeClose.close();
2151
+ }
2152
+ }
2153
+ };
2154
+ }
2155
+ function buildOpenAiResponsesEndpointUrl() {
2156
+ const base = resolveOpenAiBaseUrl();
2157
+ const normalized = base.endsWith("/") ? base : `${base}/`;
2158
+ return new URL("responses", normalized).toString();
2159
+ }
2160
+ function buildOpenAiResponsesWebSocketHeaders(apiKey) {
2161
+ const headers = {
2162
+ Authorization: `Bearer ${apiKey}`,
2163
+ "OpenAI-Beta": mergeOpenAiBetaHeader(
2164
+ process.env.OPENAI_BETA,
2165
+ OPENAI_BETA_RESPONSES_WEBSOCKETS_V2
2166
+ )
2167
+ };
2168
+ const organization = process.env.OPENAI_ORGANIZATION?.trim();
2169
+ if (organization) {
2170
+ headers["OpenAI-Organization"] = organization;
2171
+ }
2172
+ const project = process.env.OPENAI_PROJECT?.trim();
2173
+ if (project) {
2174
+ headers["OpenAI-Project"] = project;
2175
+ }
2176
+ return headers;
2177
+ }
2178
+ function installResponsesWebSocketTransport(client, apiKey) {
2179
+ const responsesApi = client.responses;
2180
+ const streamMethod = responsesApi?.stream;
2181
+ if (typeof streamMethod !== "function") {
2182
+ return;
2183
+ }
2184
+ const originalStream = streamMethod.bind(client.responses);
2185
+ responsesApi.stream = (request, options) => {
2186
+ const mode = resolveOpenAiResponsesWebSocketMode();
2187
+ const fallbackStreamFactory = () => wrapFallbackStream(originalStream(request, options));
2188
+ if (mode === "off" || openAiResponsesWebSocketDisabled) {
2189
+ return fallbackStreamFactory();
2190
+ }
2191
+ const signal = options && typeof options === "object" ? options.signal ?? void 0 : void 0;
2192
+ const websocketUrl = toWebSocketUrl(buildOpenAiResponsesEndpointUrl());
2193
+ const headers = buildOpenAiResponsesWebSocketHeaders(apiKey);
2194
+ const timeoutMs = resolveOpenAiTimeoutMs();
2195
+ return createAdaptiveResponsesStream({
2196
+ mode,
2197
+ createWebSocketStream: async () => await createResponsesWebSocketStream({
2198
+ url: websocketUrl,
2199
+ headers,
2200
+ request,
2201
+ signal,
2202
+ idleTimeoutMs: timeoutMs
2203
+ }),
2204
+ createFallbackStream: fallbackStreamFactory,
2205
+ onWebSocketFallback: (error) => {
2206
+ if (isResponsesWebSocketUnsupportedError(error)) {
2207
+ openAiResponsesWebSocketDisabled = true;
2208
+ }
2209
+ }
2210
+ });
2211
+ };
2212
+ }
1496
2213
  function getOpenAiApiKey() {
1497
2214
  if (cachedApiKey2 !== null) {
1498
2215
  return cachedApiKey2;
@@ -1510,6 +2227,7 @@ function getOpenAiClient() {
1510
2227
  if (cachedClient2) {
1511
2228
  return cachedClient2;
1512
2229
  }
2230
+ loadLocalEnv();
1513
2231
  const apiKey = getOpenAiApiKey();
1514
2232
  const timeoutMs = resolveOpenAiTimeoutMs();
1515
2233
  cachedClient2 = new OpenAI2({
@@ -1517,6 +2235,7 @@ function getOpenAiClient() {
1517
2235
  fetch: getOpenAiFetch(),
1518
2236
  timeout: timeoutMs
1519
2237
  });
2238
+ installResponsesWebSocketTransport(cachedClient2, apiKey);
1520
2239
  return cachedClient2;
1521
2240
  }
1522
2241
 
@@ -3090,6 +3809,7 @@ function extractFireworksToolCalls(message) {
3090
3809
  function resolveGeminiThinkingConfig(modelId) {
3091
3810
  switch (modelId) {
3092
3811
  case "gemini-3-pro-preview":
3812
+ case "gemini-3.1-pro-preview":
3093
3813
  return { includeThoughts: true };
3094
3814
  case "gemini-3-flash-preview":
3095
3815
  return { includeThoughts: true, thinkingBudget: 16384 };
@@ -6489,6 +7209,7 @@ export {
6489
7209
  CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
6490
7210
  CODEX_APPLY_PATCH_LARK_GRAMMAR,
6491
7211
  FIREWORKS_DEFAULT_GLM_MODEL,
7212
+ FIREWORKS_DEFAULT_GPT_OSS_120B_MODEL,
6492
7213
  FIREWORKS_DEFAULT_KIMI_MODEL,
6493
7214
  FIREWORKS_DEFAULT_MINIMAX_MODEL,
6494
7215
  FIREWORKS_MODEL_IDS,