@wrongstack/providers 0.265.1 → 0.267.0
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.d.ts +222 -2
- package/dist/index.js +993 -256
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expectDefined, ProviderError, StreamHangError, safeParse, sanitizeJsonString, WrongStackError, ERROR_CODES } from '@wrongstack/core';
|
|
1
|
+
import { expectDefined, ProviderError, compactToolDefinitionForWire, StreamHangError, safeParse, sanitizeJsonString, WrongStackError, ERROR_CODES } from '@wrongstack/core';
|
|
2
2
|
import { Readable } from 'stream';
|
|
3
3
|
import { toErrorMessage } from '@wrongstack/core/utils';
|
|
4
4
|
import { randomUUID } from 'crypto';
|
|
@@ -362,6 +362,45 @@ var CAPABILITIES_BY_FAMILY = {
|
|
|
362
362
|
maxContext: 0,
|
|
363
363
|
cacheControl: "none"
|
|
364
364
|
},
|
|
365
|
+
// Claude Pro/Max via OAuth (Sign in with Claude). Same wire as anthropic.
|
|
366
|
+
"anthropic-oauth": {
|
|
367
|
+
tools: true,
|
|
368
|
+
parallelTools: true,
|
|
369
|
+
vision: true,
|
|
370
|
+
streaming: true,
|
|
371
|
+
promptCache: true,
|
|
372
|
+
systemPrompt: true,
|
|
373
|
+
jsonMode: false,
|
|
374
|
+
reasoning: false,
|
|
375
|
+
maxContext: 2e5,
|
|
376
|
+
cacheControl: "native"
|
|
377
|
+
},
|
|
378
|
+
// GitHub Copilot subscription (OpenAI chat/completions-compatible wire).
|
|
379
|
+
"github-copilot": {
|
|
380
|
+
tools: true,
|
|
381
|
+
parallelTools: true,
|
|
382
|
+
vision: true,
|
|
383
|
+
streaming: true,
|
|
384
|
+
promptCache: false,
|
|
385
|
+
systemPrompt: true,
|
|
386
|
+
jsonMode: true,
|
|
387
|
+
reasoning: false,
|
|
388
|
+
maxContext: 128e3,
|
|
389
|
+
cacheControl: "auto"
|
|
390
|
+
},
|
|
391
|
+
// ChatGPT-backend Responses API (Sign in with ChatGPT / Codex subscription).
|
|
392
|
+
"openai-codex": {
|
|
393
|
+
tools: true,
|
|
394
|
+
parallelTools: true,
|
|
395
|
+
vision: true,
|
|
396
|
+
streaming: true,
|
|
397
|
+
promptCache: true,
|
|
398
|
+
systemPrompt: true,
|
|
399
|
+
jsonMode: false,
|
|
400
|
+
reasoning: true,
|
|
401
|
+
maxContext: 272e3,
|
|
402
|
+
cacheControl: "auto"
|
|
403
|
+
},
|
|
365
404
|
google: {
|
|
366
405
|
tools: true,
|
|
367
406
|
parallelTools: true,
|
|
@@ -535,20 +574,18 @@ function normalizeGemini(stop) {
|
|
|
535
574
|
return "end_turn";
|
|
536
575
|
}
|
|
537
576
|
}
|
|
538
|
-
|
|
539
|
-
// src/tool-format/to-anthropic.ts
|
|
540
577
|
var _cache = /* @__PURE__ */ new WeakMap();
|
|
541
578
|
function toolsToAnthropic(tools) {
|
|
542
579
|
const hit = _cache.get(tools);
|
|
543
580
|
if (hit) return hit;
|
|
544
|
-
const result = tools.map((t) =>
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}
|
|
551
|
-
})
|
|
581
|
+
const result = tools.map((t) => {
|
|
582
|
+
const compact = compactToolDefinitionForWire(t);
|
|
583
|
+
return {
|
|
584
|
+
name: compact.name,
|
|
585
|
+
description: compact.description,
|
|
586
|
+
input_schema: compact.inputSchema
|
|
587
|
+
};
|
|
588
|
+
});
|
|
552
589
|
_cache.set(tools, result);
|
|
553
590
|
return result;
|
|
554
591
|
}
|
|
@@ -869,12 +906,34 @@ var AnthropicProvider = class extends WireAdapter {
|
|
|
869
906
|
return parseProviderHttpError(this.id, status, text);
|
|
870
907
|
}
|
|
871
908
|
normalizeMessage(m) {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
};
|
|
909
|
+
const role = m.role === "system" ? "user" : m.role;
|
|
910
|
+
if (typeof m.content === "string") return { role, content: m.content };
|
|
911
|
+
return { role, content: m.content.map((b) => sanitizeAnthropicBlock(b)) };
|
|
876
912
|
}
|
|
877
913
|
};
|
|
914
|
+
function sanitizeAnthropicBlock(b) {
|
|
915
|
+
switch (b.type) {
|
|
916
|
+
case "text":
|
|
917
|
+
return b.cache_control ? { type: "text", text: b.text, cache_control: b.cache_control } : { type: "text", text: b.text };
|
|
918
|
+
case "tool_use":
|
|
919
|
+
return { type: "tool_use", id: b.id, name: b.name, input: b.input };
|
|
920
|
+
case "tool_result": {
|
|
921
|
+
const out = {
|
|
922
|
+
type: "tool_result",
|
|
923
|
+
tool_use_id: b.tool_use_id,
|
|
924
|
+
content: b.content
|
|
925
|
+
};
|
|
926
|
+
if (b.is_error) out["is_error"] = true;
|
|
927
|
+
return out;
|
|
928
|
+
}
|
|
929
|
+
case "thinking":
|
|
930
|
+
return b.signature ? { type: "thinking", thinking: b.thinking, signature: b.signature } : { type: "thinking", thinking: b.thinking };
|
|
931
|
+
case "image":
|
|
932
|
+
return { type: "image", source: b.source };
|
|
933
|
+
default:
|
|
934
|
+
return b;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
878
937
|
async function* parseAnthropicStream(body, fallbackModel) {
|
|
879
938
|
const blocks = /* @__PURE__ */ new Map();
|
|
880
939
|
let model = fallbackModel;
|
|
@@ -977,12 +1036,575 @@ async function* parseAnthropicStream(body, fallbackModel) {
|
|
|
977
1036
|
yield { type: "message_stop", stopReason, usage };
|
|
978
1037
|
}
|
|
979
1038
|
}
|
|
980
|
-
var
|
|
1039
|
+
var CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
1040
|
+
var TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
|
|
1041
|
+
var DEFAULT_BASE2 = "https://api.anthropic.com";
|
|
1042
|
+
var ANTHROPIC_VERSION = "2023-06-01";
|
|
1043
|
+
var OAUTH_BETA = "claude-code-20250219,oauth-2025-04-20";
|
|
1044
|
+
var REFRESH_SKEW_MS = 6e4;
|
|
1045
|
+
var CLAUDE_CODE_VERSION = "2.1.75";
|
|
1046
|
+
var CLAUDE_CODE_SYSTEM_PROMPT = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
1047
|
+
var CLAUDE_CODE_TOOLS = [
|
|
1048
|
+
"Read",
|
|
1049
|
+
"Write",
|
|
1050
|
+
"Edit",
|
|
1051
|
+
"Bash",
|
|
1052
|
+
"Grep",
|
|
1053
|
+
"Glob",
|
|
1054
|
+
"AskUserQuestion",
|
|
1055
|
+
"EnterPlanMode",
|
|
1056
|
+
"ExitPlanMode",
|
|
1057
|
+
"KillShell",
|
|
1058
|
+
"NotebookEdit",
|
|
1059
|
+
"Skill",
|
|
1060
|
+
"Task",
|
|
1061
|
+
"TaskOutput",
|
|
1062
|
+
"TodoWrite",
|
|
1063
|
+
"WebFetch",
|
|
1064
|
+
"WebSearch"
|
|
1065
|
+
];
|
|
1066
|
+
var CC_TOOL_BY_LOWER = new Map(CLAUDE_CODE_TOOLS.map((t) => [t.toLowerCase(), t]));
|
|
1067
|
+
function toClaudeCodeName(name) {
|
|
1068
|
+
return CC_TOOL_BY_LOWER.get(name.toLowerCase()) ?? name;
|
|
1069
|
+
}
|
|
1070
|
+
function fromClaudeCodeName(name, tools) {
|
|
1071
|
+
const lower = name.toLowerCase();
|
|
1072
|
+
const match = tools?.find((t) => t.name.toLowerCase() === lower);
|
|
1073
|
+
return match?.name ?? name;
|
|
1074
|
+
}
|
|
1075
|
+
async function refreshAnthropicOAuthToken(refreshToken, signal) {
|
|
1076
|
+
const res = await fetch(TOKEN_URL, {
|
|
1077
|
+
method: "POST",
|
|
1078
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
1079
|
+
body: JSON.stringify({
|
|
1080
|
+
grant_type: "refresh_token",
|
|
1081
|
+
client_id: CLIENT_ID,
|
|
1082
|
+
refresh_token: refreshToken
|
|
1083
|
+
}),
|
|
1084
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(3e4)]) : AbortSignal.timeout(3e4)
|
|
1085
|
+
});
|
|
1086
|
+
if (!res.ok) {
|
|
1087
|
+
const text = await res.text().catch(() => "");
|
|
1088
|
+
throw new Error(`Claude token refresh failed (${res.status}): ${text || res.statusText}`);
|
|
1089
|
+
}
|
|
1090
|
+
const json = await res.json();
|
|
1091
|
+
if (!json?.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
|
|
1092
|
+
throw new Error("Claude token refresh response missing fields");
|
|
1093
|
+
}
|
|
1094
|
+
return {
|
|
1095
|
+
access: json.access_token,
|
|
1096
|
+
refresh: json.refresh_token,
|
|
1097
|
+
expires: Date.now() + json.expires_in * 1e3
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
var AnthropicOAuthProvider = class extends AnthropicProvider {
|
|
1101
|
+
id;
|
|
1102
|
+
capabilities;
|
|
1103
|
+
access;
|
|
1104
|
+
refresh;
|
|
1105
|
+
expiresAt;
|
|
1106
|
+
onRefresh;
|
|
1107
|
+
refreshFn;
|
|
1108
|
+
constructor(opts) {
|
|
1109
|
+
super({
|
|
1110
|
+
apiKey: opts.credentials.accessToken,
|
|
1111
|
+
baseUrl: opts.baseUrl ?? DEFAULT_BASE2,
|
|
1112
|
+
fetchImpl: opts.fetchImpl,
|
|
1113
|
+
streamOpts: opts.streamOpts
|
|
1114
|
+
});
|
|
1115
|
+
this.id = opts.id ?? "anthropic-oauth";
|
|
1116
|
+
this.capabilities = capabilitiesForFamily("anthropic-oauth");
|
|
1117
|
+
this.access = opts.credentials.accessToken;
|
|
1118
|
+
this.refresh = opts.credentials.refreshToken;
|
|
1119
|
+
this.expiresAt = opts.credentials.expiresAt;
|
|
1120
|
+
this.onRefresh = opts.onRefresh;
|
|
1121
|
+
this.refreshFn = opts.refreshFn ?? refreshAnthropicOAuthToken;
|
|
1122
|
+
}
|
|
1123
|
+
async *stream(req, opts) {
|
|
1124
|
+
await this.ensureFreshToken(opts.signal);
|
|
1125
|
+
try {
|
|
1126
|
+
yield* this.remapToolNames(super.stream(req, opts), req.tools);
|
|
1127
|
+
} catch (err) {
|
|
1128
|
+
if (err instanceof ProviderError && err.status === 401 && this.refresh) {
|
|
1129
|
+
await this.doRefresh(opts.signal);
|
|
1130
|
+
yield* this.remapToolNames(super.stream(req, opts), req.tools);
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
throw err;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
/** Map Claude-Code-cased tool_use names in the stream back to real names. */
|
|
1137
|
+
async *remapToolNames(events, tools) {
|
|
1138
|
+
for await (const ev of events) {
|
|
1139
|
+
if ((ev.type === "tool_use_start" || ev.type === "content_block_start") && typeof ev.name === "string") {
|
|
1140
|
+
yield { ...ev, name: fromClaudeCodeName(ev.name, tools) };
|
|
1141
|
+
} else {
|
|
1142
|
+
yield ev;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
async ensureFreshToken(signal) {
|
|
1147
|
+
if (!this.refresh) return;
|
|
1148
|
+
if (this.expiresAt !== void 0 && Date.now() < this.expiresAt - REFRESH_SKEW_MS) return;
|
|
1149
|
+
await this.doRefresh(signal);
|
|
1150
|
+
}
|
|
1151
|
+
async doRefresh(signal) {
|
|
1152
|
+
if (!this.refresh) return;
|
|
1153
|
+
const t = await this.refreshFn(this.refresh, signal);
|
|
1154
|
+
this.access = t.access;
|
|
1155
|
+
this.refresh = t.refresh;
|
|
1156
|
+
this.expiresAt = t.expires;
|
|
1157
|
+
this.onRefresh?.({ accessToken: t.access, refreshToken: t.refresh, expiresAt: t.expires });
|
|
1158
|
+
}
|
|
1159
|
+
buildHeaders(_req) {
|
|
1160
|
+
return {
|
|
1161
|
+
"content-type": "application/json",
|
|
1162
|
+
accept: "text/event-stream",
|
|
1163
|
+
"anthropic-version": ANTHROPIC_VERSION,
|
|
1164
|
+
authorization: `Bearer ${this.access}`,
|
|
1165
|
+
"anthropic-beta": OAUTH_BETA,
|
|
1166
|
+
// Present as the official Claude Code CLI so the subscription backend
|
|
1167
|
+
// accepts the request and the client isn't trivially fingerprinted.
|
|
1168
|
+
"user-agent": `claude-cli/${CLAUDE_CODE_VERSION}`,
|
|
1169
|
+
"x-app": "cli",
|
|
1170
|
+
"anthropic-dangerous-direct-browser-access": "true"
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
buildBody(req) {
|
|
1174
|
+
const body = super.buildBody(req);
|
|
1175
|
+
const existing = body["system"] ?? [];
|
|
1176
|
+
const alreadyLed = existing[0]?.text?.startsWith(CLAUDE_CODE_SYSTEM_PROMPT) === true;
|
|
1177
|
+
body["system"] = alreadyLed ? existing : [{ type: "text", text: CLAUDE_CODE_SYSTEM_PROMPT }, ...existing];
|
|
1178
|
+
const tools = body["tools"];
|
|
1179
|
+
if (Array.isArray(tools)) {
|
|
1180
|
+
for (const t of tools) {
|
|
1181
|
+
if (typeof t.name === "string") t.name = toClaudeCodeName(t.name);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
const messages = body["messages"];
|
|
1185
|
+
if (Array.isArray(messages)) {
|
|
1186
|
+
for (const m of messages) {
|
|
1187
|
+
if (!Array.isArray(m.content)) continue;
|
|
1188
|
+
for (const block of m.content) {
|
|
1189
|
+
if (block?.type === "tool_use" && typeof block.name === "string") {
|
|
1190
|
+
block.name = toClaudeCodeName(block.name);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
return body;
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
// src/openai.ts
|
|
1200
|
+
init_tool_input();
|
|
1201
|
+
var _cache2 = /* @__PURE__ */ new WeakMap();
|
|
1202
|
+
function toolsToOpenAI(tools) {
|
|
1203
|
+
const hit = _cache2.get(tools);
|
|
1204
|
+
if (hit) return hit;
|
|
1205
|
+
const result = tools.map((t) => {
|
|
1206
|
+
const compact = compactToolDefinitionForWire(t);
|
|
1207
|
+
return {
|
|
1208
|
+
type: "function",
|
|
1209
|
+
function: {
|
|
1210
|
+
name: compact.name,
|
|
1211
|
+
description: compact.description,
|
|
1212
|
+
parameters: compact.inputSchema
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
});
|
|
1216
|
+
_cache2.set(tools, result);
|
|
1217
|
+
return result;
|
|
1218
|
+
}
|
|
1219
|
+
function messagesToOpenAI(system, messages, opts = {}) {
|
|
1220
|
+
const emptyContentMode = opts.emptyToolCallContent ?? "empty_string";
|
|
1221
|
+
const out = [];
|
|
1222
|
+
if (system && system.length > 0) {
|
|
1223
|
+
const sysText = system.map((b) => b.text).join("\n\n");
|
|
1224
|
+
if (opts.systemAsMessage) {
|
|
1225
|
+
out.push({ role: "user", content: sysText });
|
|
1226
|
+
} else {
|
|
1227
|
+
out.push({ role: "system", content: sysText });
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
for (const msg of messages) {
|
|
1231
|
+
if (msg.role === "user") {
|
|
1232
|
+
const blocks = normalizeContent(msg.content);
|
|
1233
|
+
const toolResults = blocks.filter((b) => b.type === "tool_result");
|
|
1234
|
+
const others = blocks.filter((b) => b.type !== "tool_result");
|
|
1235
|
+
for (const r of toolResults) {
|
|
1236
|
+
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
1237
|
+
out.push({
|
|
1238
|
+
role: "tool",
|
|
1239
|
+
tool_call_id: r.tool_use_id,
|
|
1240
|
+
content
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
if (others.length > 0) {
|
|
1244
|
+
out.push({
|
|
1245
|
+
role: "user",
|
|
1246
|
+
content: opts.flattenContentToString ? blocksToString(others) : blocksToContentArray(others)
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
} else if (msg.role === "assistant") {
|
|
1250
|
+
const blocks = normalizeContent(msg.content);
|
|
1251
|
+
const textBlocks = blocks.filter((b) => b.type === "text");
|
|
1252
|
+
const toolUses = blocks.filter((b) => b.type === "tool_use");
|
|
1253
|
+
const thinkingBlocks = blocks.filter((b) => b.type === "thinking");
|
|
1254
|
+
const text = textBlocks.map((b) => b.text).join("");
|
|
1255
|
+
const reasoning = thinkingBlocks.map((b) => b.thinking).filter((t) => t && t.length > 0).join("");
|
|
1256
|
+
const toolCalls = toolUses.map((u) => ({
|
|
1257
|
+
id: u.id,
|
|
1258
|
+
type: "function",
|
|
1259
|
+
function: { name: u.name, arguments: JSON.stringify(u.input) }
|
|
1260
|
+
}));
|
|
1261
|
+
const message = { role: "assistant" };
|
|
1262
|
+
if (toolCalls.length > 0) {
|
|
1263
|
+
message.tool_calls = toolCalls;
|
|
1264
|
+
if (text) {
|
|
1265
|
+
message.content = text;
|
|
1266
|
+
} else {
|
|
1267
|
+
message.content = emptyContentMode === "null" ? null : "";
|
|
1268
|
+
}
|
|
1269
|
+
} else {
|
|
1270
|
+
message.content = text;
|
|
1271
|
+
}
|
|
1272
|
+
if (reasoning.length > 0) {
|
|
1273
|
+
message.reasoning_content = reasoning;
|
|
1274
|
+
}
|
|
1275
|
+
out.push(message);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return out;
|
|
1279
|
+
}
|
|
1280
|
+
function normalizeContent(content) {
|
|
1281
|
+
return typeof content === "string" ? [{ type: "text", text: content }] : content;
|
|
1282
|
+
}
|
|
1283
|
+
function blocksToString(blocks) {
|
|
1284
|
+
return blocks.map((b) => {
|
|
1285
|
+
if (b.type === "text") return b.text;
|
|
1286
|
+
if (b.type === "image") return "[image]";
|
|
1287
|
+
return "";
|
|
1288
|
+
}).join("");
|
|
1289
|
+
}
|
|
1290
|
+
function blocksToContentArray(blocks) {
|
|
1291
|
+
const hasImage = blocks.some((b) => b.type === "image");
|
|
1292
|
+
if (!hasImage) {
|
|
1293
|
+
return blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
1294
|
+
}
|
|
1295
|
+
return blocks.map((b) => {
|
|
1296
|
+
if (b.type === "text") return { type: "text", text: b.text };
|
|
1297
|
+
if (b.type === "image") {
|
|
1298
|
+
const url = b.source.type === "url" ? b.source.url ?? "" : `data:${b.source.media_type ?? "image/png"};base64,${b.source.data ?? ""}`;
|
|
1299
|
+
return { type: "image_url", image_url: { url } };
|
|
1300
|
+
}
|
|
1301
|
+
return null;
|
|
1302
|
+
}).filter((c) => c !== null);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// src/openai.ts
|
|
1306
|
+
var DEFAULT_BASE3 = "https://api.openai.com/v1";
|
|
1307
|
+
var OpenAIProvider = class extends WireAdapter {
|
|
1308
|
+
id;
|
|
1309
|
+
capabilities;
|
|
1310
|
+
opts;
|
|
1311
|
+
constructor(opts) {
|
|
1312
|
+
super(opts.apiKey, opts.baseUrl ?? DEFAULT_BASE3, opts.fetchImpl, opts.streamOpts);
|
|
1313
|
+
this.opts = opts;
|
|
1314
|
+
this.id = opts.id ?? "openai";
|
|
1315
|
+
this.capabilities = capabilitiesForFamily("openai", {
|
|
1316
|
+
parallelTools: !opts.quirks?.parallelToolsDisabled,
|
|
1317
|
+
systemPrompt: !opts.quirks?.systemAsMessage,
|
|
1318
|
+
...opts.capabilities
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
buildUrl(_req) {
|
|
1322
|
+
const base = this.baseUrl.replace(/\/+$/, "");
|
|
1323
|
+
if (/\/chat\/completions$/.test(base)) return base;
|
|
1324
|
+
if (/\/v\d+(\/[a-z0-9_-]+)*$/i.test(base)) return `${base}/chat/completions`;
|
|
1325
|
+
return `${base}/v1/chat/completions`;
|
|
1326
|
+
}
|
|
1327
|
+
buildHeaders(req) {
|
|
1328
|
+
const headers = {
|
|
1329
|
+
...super.buildHeaders(req),
|
|
1330
|
+
authorization: `Bearer ${this.apiKey}`
|
|
1331
|
+
};
|
|
1332
|
+
if (this.opts.organization) {
|
|
1333
|
+
headers["openai-organization"] = this.opts.organization;
|
|
1334
|
+
}
|
|
1335
|
+
return headers;
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* The request field used to cap output length. Real OpenAI deprecated
|
|
1339
|
+
* `max_tokens` and the newer model families (gpt-4o, o1/o3/o4) 400 on it —
|
|
1340
|
+
* they require `max_completion_tokens`. OpenAI-compatible endpoints that
|
|
1341
|
+
* still only accept `max_tokens` override this. See issue #10.
|
|
1342
|
+
*/
|
|
1343
|
+
tokenLimitParam() {
|
|
1344
|
+
return "max_completion_tokens";
|
|
1345
|
+
}
|
|
1346
|
+
buildBody(req) {
|
|
1347
|
+
const body = {
|
|
1348
|
+
model: req.model,
|
|
1349
|
+
messages: messagesToOpenAI(this.stripCacheControl(req), req.messages, {
|
|
1350
|
+
...this.opts.quirks
|
|
1351
|
+
}),
|
|
1352
|
+
[this.tokenLimitParam()]: req.maxTokens,
|
|
1353
|
+
stream: true,
|
|
1354
|
+
stream_options: { include_usage: true }
|
|
1355
|
+
};
|
|
1356
|
+
if (req.tools && req.tools.length > 0) {
|
|
1357
|
+
body["tools"] = toolsToOpenAI(req.tools);
|
|
1358
|
+
if (req.toolChoice) {
|
|
1359
|
+
if (typeof req.toolChoice === "string") {
|
|
1360
|
+
body["tool_choice"] = req.toolChoice === "required" ? "required" : req.toolChoice;
|
|
1361
|
+
} else {
|
|
1362
|
+
body["tool_choice"] = {
|
|
1363
|
+
type: "function",
|
|
1364
|
+
function: { name: req.toolChoice.name }
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
if (req.temperature !== void 0) body["temperature"] = req.temperature;
|
|
1370
|
+
if (req.topP !== void 0) body["top_p"] = req.topP;
|
|
1371
|
+
if (req.stopSequences) body["stop"] = req.stopSequences;
|
|
1372
|
+
return body;
|
|
1373
|
+
}
|
|
1374
|
+
parseStream(body, fallbackModel) {
|
|
1375
|
+
return parseOpenAIStream(body, fallbackModel);
|
|
1376
|
+
}
|
|
1377
|
+
translateError(status, text) {
|
|
1378
|
+
return parseProviderHttpError(this.id, status, text);
|
|
1379
|
+
}
|
|
1380
|
+
stripCacheControl(req) {
|
|
1381
|
+
if (!req.system) return void 0;
|
|
1382
|
+
return req.system.map((b) => {
|
|
1383
|
+
const { cache_control: _cc, ...rest } = b;
|
|
1384
|
+
return rest;
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
async function* parseOpenAIStream(body, fallbackModel) {
|
|
1389
|
+
let model = fallbackModel;
|
|
1390
|
+
let usage = { input: 0, output: 0 };
|
|
1391
|
+
let stopReason = "end_turn";
|
|
1392
|
+
let started = false;
|
|
1393
|
+
let thinkingOpen = false;
|
|
1394
|
+
const toolByIndex = /* @__PURE__ */ new Map();
|
|
1395
|
+
for await (const msg of parseSSE(body)) {
|
|
1396
|
+
if (!msg.data || msg.data === "[DONE]") continue;
|
|
1397
|
+
const parsed = safeParse(msg.data);
|
|
1398
|
+
if (!parsed.ok || !parsed.value) continue;
|
|
1399
|
+
const obj = parsed.value;
|
|
1400
|
+
if (typeof obj["model"] === "string") model = obj["model"];
|
|
1401
|
+
if (!started) {
|
|
1402
|
+
started = true;
|
|
1403
|
+
yield { type: "message_start", model };
|
|
1404
|
+
}
|
|
1405
|
+
const choices = obj["choices"];
|
|
1406
|
+
const choice = choices?.[0];
|
|
1407
|
+
const reasoningDelta = typeof choice?.delta?.reasoning_content === "string" ? choice.delta.reasoning_content : typeof choice?.delta?.reasoning === "string" ? choice.delta.reasoning : void 0;
|
|
1408
|
+
if (reasoningDelta && reasoningDelta.length > 0) {
|
|
1409
|
+
if (!thinkingOpen) {
|
|
1410
|
+
thinkingOpen = true;
|
|
1411
|
+
yield { type: "thinking_start" };
|
|
1412
|
+
}
|
|
1413
|
+
yield { type: "thinking_delta", text: reasoningDelta };
|
|
1414
|
+
}
|
|
1415
|
+
if (choice?.delta?.content) {
|
|
1416
|
+
if (thinkingOpen) {
|
|
1417
|
+
thinkingOpen = false;
|
|
1418
|
+
yield { type: "thinking_stop" };
|
|
1419
|
+
}
|
|
1420
|
+
yield { type: "text_delta", text: choice.delta.content };
|
|
1421
|
+
}
|
|
1422
|
+
if (choice?.delta?.tool_calls) {
|
|
1423
|
+
if (thinkingOpen) {
|
|
1424
|
+
thinkingOpen = false;
|
|
1425
|
+
yield { type: "thinking_stop" };
|
|
1426
|
+
}
|
|
1427
|
+
for (const tc of choice.delta.tool_calls) {
|
|
1428
|
+
const idx = tc.index ?? 0;
|
|
1429
|
+
let entry = toolByIndex.get(idx);
|
|
1430
|
+
if (!entry) {
|
|
1431
|
+
entry = {
|
|
1432
|
+
id: tc.id,
|
|
1433
|
+
name: tc.function?.name,
|
|
1434
|
+
argBuf: "",
|
|
1435
|
+
emittedStart: false,
|
|
1436
|
+
emittedArgLength: 0
|
|
1437
|
+
};
|
|
1438
|
+
toolByIndex.set(idx, entry);
|
|
1439
|
+
} else {
|
|
1440
|
+
if (tc.id && !entry.id) entry.id = tc.id;
|
|
1441
|
+
if (tc.function?.name && !entry.name) entry.name = tc.function.name;
|
|
1442
|
+
}
|
|
1443
|
+
if (tc.function?.arguments) {
|
|
1444
|
+
entry.argBuf += tc.function.arguments;
|
|
1445
|
+
}
|
|
1446
|
+
if (!entry.emittedStart && entry.id && entry.name) {
|
|
1447
|
+
entry.emittedStart = true;
|
|
1448
|
+
yield { type: "tool_use_start", id: entry.id, name: entry.name };
|
|
1449
|
+
}
|
|
1450
|
+
if (entry.emittedStart && entry.id && entry.emittedArgLength < entry.argBuf.length) {
|
|
1451
|
+
const partial = entry.argBuf.slice(entry.emittedArgLength);
|
|
1452
|
+
entry.emittedArgLength = entry.argBuf.length;
|
|
1453
|
+
yield {
|
|
1454
|
+
type: "tool_use_input_delta",
|
|
1455
|
+
id: entry.id,
|
|
1456
|
+
partial
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
if (choice?.finish_reason) {
|
|
1462
|
+
stopReason = normalizeOpenAI(choice.finish_reason);
|
|
1463
|
+
}
|
|
1464
|
+
const u = obj["usage"];
|
|
1465
|
+
if (u) {
|
|
1466
|
+
const hasDeepSeekCacheFields = u.prompt_cache_hit_tokens !== void 0 || u.prompt_cache_miss_tokens !== void 0;
|
|
1467
|
+
const cached = u.prompt_tokens_details?.cached_tokens ?? u.prompt_cache_hit_tokens ?? 0;
|
|
1468
|
+
const promptTotal = u.prompt_tokens ?? u.input_tokens ?? (hasDeepSeekCacheFields ? (u.prompt_cache_hit_tokens ?? 0) + (u.prompt_cache_miss_tokens ?? 0) : usage.input + cached);
|
|
1469
|
+
usage = {
|
|
1470
|
+
input: u.prompt_cache_miss_tokens ?? Math.max(0, promptTotal - cached),
|
|
1471
|
+
output: u.completion_tokens ?? usage.output,
|
|
1472
|
+
cacheRead: cached || usage.cacheRead
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
if (thinkingOpen) {
|
|
1477
|
+
yield { type: "thinking_stop" };
|
|
1478
|
+
}
|
|
1479
|
+
for (const entry of toolByIndex.values()) {
|
|
1480
|
+
if (!entry.name) continue;
|
|
1481
|
+
if (!entry.id) entry.id = `call_${randomUUID()}`;
|
|
1482
|
+
if (!entry.emittedStart) {
|
|
1483
|
+
yield { type: "tool_use_start", id: entry.id, name: entry.name };
|
|
1484
|
+
}
|
|
1485
|
+
const input = parseToolInput(entry.argBuf);
|
|
1486
|
+
yield { type: "tool_use_stop", id: entry.id, input };
|
|
1487
|
+
}
|
|
1488
|
+
if (started) {
|
|
1489
|
+
yield { type: "message_stop", stopReason, usage };
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// src/github-copilot.ts
|
|
1494
|
+
var COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
|
|
1495
|
+
var COPILOT_API_VERSION = "2026-06-01";
|
|
1496
|
+
var COPILOT_HEADERS = {
|
|
1497
|
+
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1498
|
+
"Editor-Version": "vscode/1.107.0",
|
|
1499
|
+
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1500
|
+
"Copilot-Integration-Id": "vscode-chat"
|
|
1501
|
+
};
|
|
1502
|
+
var DEFAULT_API_BASE = "https://api.individual.githubcopilot.com";
|
|
1503
|
+
var REFRESH_SKEW_MS2 = 6e4;
|
|
1504
|
+
function copilotBaseUrlFromToken(token) {
|
|
1505
|
+
if (token) {
|
|
1506
|
+
const m = token.match(/proxy-ep=([^;]+)/);
|
|
1507
|
+
if (m?.[1]) return `https://${m[1].replace(/^proxy\./, "api.")}`;
|
|
1508
|
+
}
|
|
1509
|
+
return DEFAULT_API_BASE;
|
|
1510
|
+
}
|
|
1511
|
+
async function refreshCopilotToken(githubToken, signal) {
|
|
1512
|
+
const res = await fetch(COPILOT_TOKEN_URL, {
|
|
1513
|
+
headers: {
|
|
1514
|
+
accept: "application/json",
|
|
1515
|
+
authorization: `Bearer ${githubToken}`,
|
|
1516
|
+
...COPILOT_HEADERS
|
|
1517
|
+
},
|
|
1518
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(15e3)]) : AbortSignal.timeout(15e3)
|
|
1519
|
+
});
|
|
1520
|
+
if (!res.ok) {
|
|
1521
|
+
const text = await res.text().catch(() => "");
|
|
1522
|
+
throw new Error(`Copilot token request failed (${res.status}): ${text || res.statusText}`);
|
|
1523
|
+
}
|
|
1524
|
+
const json = await res.json();
|
|
1525
|
+
if (!json?.token || typeof json.expires_at !== "number") {
|
|
1526
|
+
throw new Error("Copilot token response missing fields");
|
|
1527
|
+
}
|
|
1528
|
+
return { token: json.token, expires: json.expires_at * 1e3 };
|
|
1529
|
+
}
|
|
1530
|
+
var GitHubCopilotProvider = class extends OpenAIProvider {
|
|
1531
|
+
capabilities;
|
|
1532
|
+
copilotToken;
|
|
1533
|
+
githubToken;
|
|
1534
|
+
expiresAt;
|
|
1535
|
+
apiBase;
|
|
1536
|
+
onRefresh;
|
|
1537
|
+
refreshFn;
|
|
1538
|
+
constructor(opts) {
|
|
1539
|
+
const apiBase = copilotBaseUrlFromToken(opts.credentials.copilotToken);
|
|
1540
|
+
super({
|
|
1541
|
+
// OpenAIProvider requires a non-empty apiKey; use a placeholder when the
|
|
1542
|
+
// Copilot token is empty (it will be minted before the first request).
|
|
1543
|
+
apiKey: opts.credentials.copilotToken || "pending",
|
|
1544
|
+
baseUrl: apiBase,
|
|
1545
|
+
id: opts.id ?? "github-copilot",
|
|
1546
|
+
fetchImpl: opts.fetchImpl,
|
|
1547
|
+
streamOpts: opts.streamOpts,
|
|
1548
|
+
capabilities: opts.capabilities
|
|
1549
|
+
});
|
|
1550
|
+
this.copilotToken = opts.credentials.copilotToken;
|
|
1551
|
+
this.githubToken = opts.credentials.githubToken;
|
|
1552
|
+
this.expiresAt = opts.credentials.expiresAt;
|
|
1553
|
+
this.apiBase = apiBase;
|
|
1554
|
+
this.onRefresh = opts.onRefresh;
|
|
1555
|
+
this.refreshFn = opts.refreshFn ?? refreshCopilotToken;
|
|
1556
|
+
this.capabilities = capabilitiesForFamily("github-copilot", { ...opts.capabilities });
|
|
1557
|
+
}
|
|
1558
|
+
async *stream(req, opts) {
|
|
1559
|
+
await this.ensureFreshToken(opts.signal);
|
|
1560
|
+
try {
|
|
1561
|
+
yield* super.stream(req, opts);
|
|
1562
|
+
} catch (err) {
|
|
1563
|
+
if (err instanceof ProviderError && err.status === 401 && this.githubToken) {
|
|
1564
|
+
await this.doRefresh(opts.signal);
|
|
1565
|
+
yield* super.stream(req, opts);
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
throw err;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
async ensureFreshToken(signal) {
|
|
1572
|
+
const stale = this.expiresAt === void 0 || Date.now() >= this.expiresAt - REFRESH_SKEW_MS2;
|
|
1573
|
+
if (!this.copilotToken || stale && this.githubToken) {
|
|
1574
|
+
await this.doRefresh(signal);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
async doRefresh(signal) {
|
|
1578
|
+
if (!this.githubToken) return;
|
|
1579
|
+
const t = await this.refreshFn(this.githubToken, signal);
|
|
1580
|
+
this.copilotToken = t.token;
|
|
1581
|
+
this.expiresAt = t.expires;
|
|
1582
|
+
this.apiBase = copilotBaseUrlFromToken(t.token);
|
|
1583
|
+
this.onRefresh?.({
|
|
1584
|
+
accessToken: t.token,
|
|
1585
|
+
refreshToken: this.githubToken,
|
|
1586
|
+
expiresAt: t.expires
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
buildUrl(_req) {
|
|
1590
|
+
return `${this.apiBase.replace(/\/+$/, "")}/chat/completions`;
|
|
1591
|
+
}
|
|
1592
|
+
buildHeaders(_req) {
|
|
1593
|
+
return {
|
|
1594
|
+
"content-type": "application/json",
|
|
1595
|
+
accept: "text/event-stream",
|
|
1596
|
+
authorization: `Bearer ${this.copilotToken}`,
|
|
1597
|
+
"X-GitHub-Api-Version": COPILOT_API_VERSION,
|
|
1598
|
+
...COPILOT_HEADERS
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
var DEFAULT_BASE4 = "https://generativelanguage.googleapis.com/v1beta";
|
|
981
1603
|
var GoogleProvider = class extends WireAdapter {
|
|
982
1604
|
id;
|
|
983
1605
|
capabilities;
|
|
984
1606
|
constructor(opts) {
|
|
985
|
-
super(opts.apiKey, opts.baseUrl ??
|
|
1607
|
+
super(opts.apiKey, opts.baseUrl ?? DEFAULT_BASE4, opts.fetchImpl, opts.streamOpts);
|
|
986
1608
|
this.id = opts.id ?? "google";
|
|
987
1609
|
this.capabilities = capabilitiesForFamily("google", {
|
|
988
1610
|
...opts.capabilities
|
|
@@ -1027,14 +1649,17 @@ var GoogleProvider = class extends WireAdapter {
|
|
|
1027
1649
|
}
|
|
1028
1650
|
};
|
|
1029
1651
|
function toolsToGemini(tools) {
|
|
1030
|
-
return tools.map((t) =>
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1652
|
+
return tools.map((t) => {
|
|
1653
|
+
const compact = compactToolDefinitionForWire(t);
|
|
1654
|
+
return {
|
|
1655
|
+
name: compact.name,
|
|
1656
|
+
description: compact.description,
|
|
1657
|
+
parameters: sanitizeSchemaForGemini(compact.inputSchema) ?? {
|
|
1658
|
+
type: "object",
|
|
1659
|
+
properties: {}
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
});
|
|
1038
1663
|
}
|
|
1039
1664
|
var GEMINI_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
1040
1665
|
"type",
|
|
@@ -1197,301 +1822,356 @@ async function* parseGoogleStream(body, fallbackModel) {
|
|
|
1197
1822
|
}
|
|
1198
1823
|
}
|
|
1199
1824
|
|
|
1200
|
-
// src/openai.ts
|
|
1825
|
+
// src/openai-codex.ts
|
|
1201
1826
|
init_tool_input();
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
function toolsToOpenAI(tools) {
|
|
1206
|
-
const hit = _cache2.get(tools);
|
|
1827
|
+
var _toolCache = /* @__PURE__ */ new WeakMap();
|
|
1828
|
+
function toolsToResponses(tools) {
|
|
1829
|
+
const hit = _toolCache.get(tools);
|
|
1207
1830
|
if (hit) return hit;
|
|
1208
|
-
const result = tools.map((t) =>
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
_cache2.set(tools, result);
|
|
1831
|
+
const result = tools.map((t) => {
|
|
1832
|
+
const compact = compactToolDefinitionForWire(t);
|
|
1833
|
+
return {
|
|
1834
|
+
type: "function",
|
|
1835
|
+
name: compact.name,
|
|
1836
|
+
description: compact.description,
|
|
1837
|
+
parameters: compact.inputSchema,
|
|
1838
|
+
strict: false
|
|
1839
|
+
};
|
|
1840
|
+
});
|
|
1841
|
+
_toolCache.set(tools, result);
|
|
1220
1842
|
return result;
|
|
1221
1843
|
}
|
|
1222
|
-
function
|
|
1223
|
-
|
|
1844
|
+
function normalizeContent2(content) {
|
|
1845
|
+
return typeof content === "string" ? [{ type: "text", text: content }] : content;
|
|
1846
|
+
}
|
|
1847
|
+
function imageUrl(b) {
|
|
1848
|
+
return b.source.type === "url" ? b.source.url ?? "" : `data:${b.source.media_type ?? "image/png"};base64,${b.source.data ?? ""}`;
|
|
1849
|
+
}
|
|
1850
|
+
function messagesToResponsesInput(messages) {
|
|
1224
1851
|
const out = [];
|
|
1225
|
-
if (system && system.length > 0) {
|
|
1226
|
-
const sysText = system.map((b) => b.text).join("\n\n");
|
|
1227
|
-
if (opts.systemAsMessage) {
|
|
1228
|
-
out.push({ role: "user", content: sysText });
|
|
1229
|
-
} else {
|
|
1230
|
-
out.push({ role: "system", content: sysText });
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
1852
|
for (const msg of messages) {
|
|
1853
|
+
const blocks = normalizeContent2(msg.content);
|
|
1234
1854
|
if (msg.role === "user") {
|
|
1235
|
-
const blocks = normalizeContent(msg.content);
|
|
1236
1855
|
const toolResults = blocks.filter((b) => b.type === "tool_result");
|
|
1237
|
-
const others = blocks.filter((b) => b.type !== "tool_result");
|
|
1238
1856
|
for (const r of toolResults) {
|
|
1239
|
-
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
1240
1857
|
out.push({
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
content
|
|
1858
|
+
type: "function_call_output",
|
|
1859
|
+
call_id: r.tool_use_id,
|
|
1860
|
+
output: typeof r.content === "string" ? r.content : JSON.stringify(r.content)
|
|
1244
1861
|
});
|
|
1245
1862
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1863
|
+
const others = blocks.filter((b) => b.type !== "tool_result");
|
|
1864
|
+
if (others.length > 0) {
|
|
1865
|
+
const content = others.map((b) => {
|
|
1866
|
+
if (b.type === "text") return { type: "input_text", text: b.text };
|
|
1867
|
+
if (b.type === "image") {
|
|
1868
|
+
return { type: "input_image", detail: "auto", image_url: imageUrl(b) };
|
|
1869
|
+
}
|
|
1870
|
+
return null;
|
|
1871
|
+
}).filter((c) => c !== null);
|
|
1872
|
+
if (content.length > 0) out.push({ role: "user", content });
|
|
1251
1873
|
}
|
|
1252
1874
|
} else if (msg.role === "assistant") {
|
|
1253
|
-
const blocks = normalizeContent(msg.content);
|
|
1254
1875
|
const textBlocks = blocks.filter((b) => b.type === "text");
|
|
1255
1876
|
const toolUses = blocks.filter((b) => b.type === "tool_use");
|
|
1256
|
-
const thinkingBlocks = blocks.filter((b) => b.type === "thinking");
|
|
1257
1877
|
const text = textBlocks.map((b) => b.text).join("");
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
if (toolCalls.length > 0) {
|
|
1266
|
-
message.tool_calls = toolCalls;
|
|
1267
|
-
if (text) {
|
|
1268
|
-
message.content = text;
|
|
1269
|
-
} else {
|
|
1270
|
-
message.content = emptyContentMode === "null" ? null : "";
|
|
1271
|
-
}
|
|
1272
|
-
} else {
|
|
1273
|
-
message.content = text;
|
|
1878
|
+
if (text.length > 0) {
|
|
1879
|
+
out.push({
|
|
1880
|
+
type: "message",
|
|
1881
|
+
role: "assistant",
|
|
1882
|
+
content: [{ type: "output_text", text, annotations: [] }],
|
|
1883
|
+
status: "completed"
|
|
1884
|
+
});
|
|
1274
1885
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1886
|
+
for (const u of toolUses) {
|
|
1887
|
+
out.push({
|
|
1888
|
+
type: "function_call",
|
|
1889
|
+
call_id: u.id,
|
|
1890
|
+
name: u.name,
|
|
1891
|
+
arguments: JSON.stringify(u.input ?? {})
|
|
1892
|
+
});
|
|
1277
1893
|
}
|
|
1278
|
-
out.push(message);
|
|
1279
1894
|
}
|
|
1280
1895
|
}
|
|
1281
1896
|
return out;
|
|
1282
1897
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1898
|
+
|
|
1899
|
+
// src/openai-codex.ts
|
|
1900
|
+
var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
1901
|
+
var TOKEN_URL2 = "https://auth.openai.com/oauth/token";
|
|
1902
|
+
var JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
1903
|
+
var DEFAULT_CODEX_BASE = "https://chatgpt.com/backend-api";
|
|
1904
|
+
var REFRESH_SKEW_MS3 = 6e4;
|
|
1905
|
+
async function refreshCodexAccessToken(refreshToken, signal) {
|
|
1906
|
+
const res = await fetch(TOKEN_URL2, {
|
|
1907
|
+
method: "POST",
|
|
1908
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
1909
|
+
body: new URLSearchParams({
|
|
1910
|
+
grant_type: "refresh_token",
|
|
1911
|
+
client_id: CLIENT_ID2,
|
|
1912
|
+
refresh_token: refreshToken
|
|
1913
|
+
}).toString(),
|
|
1914
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(3e4)]) : AbortSignal.timeout(3e4)
|
|
1915
|
+
});
|
|
1916
|
+
if (!res.ok) {
|
|
1917
|
+
const text = await res.text().catch(() => "");
|
|
1918
|
+
throw new Error(`Codex token refresh failed (${res.status}): ${text || res.statusText}`);
|
|
1297
1919
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1920
|
+
const json = await res.json();
|
|
1921
|
+
if (!json?.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
|
|
1922
|
+
throw new Error("Codex token refresh response missing fields");
|
|
1923
|
+
}
|
|
1924
|
+
return {
|
|
1925
|
+
access: json.access_token,
|
|
1926
|
+
refresh: json.refresh_token,
|
|
1927
|
+
expires: Date.now() + json.expires_in * 1e3
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
function extractAccountId(token) {
|
|
1931
|
+
try {
|
|
1932
|
+
const parts = token.split(".");
|
|
1933
|
+
if (parts.length !== 3) return null;
|
|
1934
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
1935
|
+
const auth = payload[JWT_CLAIM_PATH];
|
|
1936
|
+
const id = auth?.chatgpt_account_id;
|
|
1937
|
+
return typeof id === "string" && id.length > 0 ? id : null;
|
|
1938
|
+
} catch {
|
|
1304
1939
|
return null;
|
|
1305
|
-
}
|
|
1940
|
+
}
|
|
1306
1941
|
}
|
|
1307
|
-
|
|
1308
|
-
// src/openai.ts
|
|
1309
|
-
var DEFAULT_BASE3 = "https://api.openai.com/v1";
|
|
1310
|
-
var OpenAIProvider = class extends WireAdapter {
|
|
1942
|
+
var OpenAICodexProvider = class extends WireAdapter {
|
|
1311
1943
|
id;
|
|
1312
1944
|
capabilities;
|
|
1313
|
-
|
|
1945
|
+
access;
|
|
1946
|
+
refresh;
|
|
1947
|
+
expiresAt;
|
|
1948
|
+
accountId;
|
|
1949
|
+
onRefresh;
|
|
1950
|
+
refreshFn;
|
|
1951
|
+
reasoningEffort;
|
|
1314
1952
|
constructor(opts) {
|
|
1315
|
-
super(
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1953
|
+
super(
|
|
1954
|
+
opts.credentials.accessToken,
|
|
1955
|
+
opts.baseUrl ?? DEFAULT_CODEX_BASE,
|
|
1956
|
+
opts.fetchImpl,
|
|
1957
|
+
opts.streamOpts
|
|
1958
|
+
);
|
|
1959
|
+
this.id = opts.id ?? "openai-codex";
|
|
1960
|
+
this.access = opts.credentials.accessToken;
|
|
1961
|
+
this.refresh = opts.credentials.refreshToken;
|
|
1962
|
+
this.expiresAt = opts.credentials.expiresAt;
|
|
1963
|
+
this.accountId = opts.credentials.accountId ?? extractAccountId(this.access) ?? void 0;
|
|
1964
|
+
this.onRefresh = opts.onRefresh;
|
|
1965
|
+
this.refreshFn = opts.refreshFn ?? refreshCodexAccessToken;
|
|
1966
|
+
this.reasoningEffort = opts.reasoningEffort ?? "medium";
|
|
1967
|
+
this.capabilities = capabilitiesForFamily("openai-codex", { ...opts.capabilities });
|
|
1968
|
+
}
|
|
1969
|
+
async *stream(req, opts) {
|
|
1970
|
+
await this.ensureFreshToken(opts.signal);
|
|
1971
|
+
try {
|
|
1972
|
+
yield* super.stream(req, opts);
|
|
1973
|
+
} catch (err) {
|
|
1974
|
+
if (err instanceof ProviderError && err.status === 401 && this.refresh) {
|
|
1975
|
+
await this.doRefresh(opts.signal);
|
|
1976
|
+
yield* super.stream(req, opts);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
throw err;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
async ensureFreshToken(signal) {
|
|
1983
|
+
if (!this.refresh) return;
|
|
1984
|
+
if (this.expiresAt !== void 0 && Date.now() < this.expiresAt - REFRESH_SKEW_MS3) return;
|
|
1985
|
+
await this.doRefresh(signal);
|
|
1986
|
+
}
|
|
1987
|
+
async doRefresh(signal) {
|
|
1988
|
+
if (!this.refresh) return;
|
|
1989
|
+
const t = await this.refreshFn(this.refresh, signal);
|
|
1990
|
+
this.access = t.access;
|
|
1991
|
+
this.refresh = t.refresh;
|
|
1992
|
+
this.expiresAt = t.expires;
|
|
1993
|
+
this.accountId = extractAccountId(t.access) ?? this.accountId;
|
|
1994
|
+
this.onRefresh?.({
|
|
1995
|
+
accessToken: t.access,
|
|
1996
|
+
refreshToken: t.refresh,
|
|
1997
|
+
expiresAt: t.expires,
|
|
1998
|
+
accountId: this.accountId
|
|
1322
1999
|
});
|
|
1323
2000
|
}
|
|
1324
2001
|
buildUrl(_req) {
|
|
1325
|
-
|
|
1326
|
-
if (/\/chat\/completions$/.test(base)) return base;
|
|
1327
|
-
if (/\/v\d+(\/[a-z0-9_-]+)*$/i.test(base)) return `${base}/chat/completions`;
|
|
1328
|
-
return `${base}/v1/chat/completions`;
|
|
2002
|
+
return resolveCodexUrl(this.baseUrl);
|
|
1329
2003
|
}
|
|
1330
|
-
buildHeaders(
|
|
2004
|
+
buildHeaders(_req) {
|
|
1331
2005
|
const headers = {
|
|
1332
|
-
...super.buildHeaders(
|
|
1333
|
-
authorization: `Bearer ${this.
|
|
2006
|
+
...super.buildHeaders(_req),
|
|
2007
|
+
authorization: `Bearer ${this.access}`,
|
|
2008
|
+
originator: "wrongstack",
|
|
2009
|
+
"OpenAI-Beta": "responses=experimental"
|
|
1334
2010
|
};
|
|
1335
|
-
if (this.
|
|
1336
|
-
headers["openai-organization"] = this.opts.organization;
|
|
1337
|
-
}
|
|
2011
|
+
if (this.accountId) headers["chatgpt-account-id"] = this.accountId;
|
|
1338
2012
|
return headers;
|
|
1339
2013
|
}
|
|
1340
|
-
/**
|
|
1341
|
-
* The request field used to cap output length. Real OpenAI deprecated
|
|
1342
|
-
* `max_tokens` and the newer model families (gpt-4o, o1/o3/o4) 400 on it —
|
|
1343
|
-
* they require `max_completion_tokens`. OpenAI-compatible endpoints that
|
|
1344
|
-
* still only accept `max_tokens` override this. See issue #10.
|
|
1345
|
-
*/
|
|
1346
|
-
tokenLimitParam() {
|
|
1347
|
-
return "max_completion_tokens";
|
|
1348
|
-
}
|
|
1349
2014
|
buildBody(req) {
|
|
2015
|
+
const instructions = req.system && req.system.length > 0 ? req.system.map((b) => b.text).join("\n\n") : "You are a helpful assistant.";
|
|
1350
2016
|
const body = {
|
|
1351
2017
|
model: req.model,
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
[this.tokenLimitParam()]: req.maxTokens,
|
|
2018
|
+
// The ChatGPT Codex backend rejects `store: true` ("Store must be set to
|
|
2019
|
+
// false"). We send the full conversation as `input` each turn.
|
|
2020
|
+
store: false,
|
|
1356
2021
|
stream: true,
|
|
1357
|
-
|
|
2022
|
+
instructions,
|
|
2023
|
+
input: messagesToResponsesInput(req.messages),
|
|
2024
|
+
include: ["reasoning.encrypted_content"],
|
|
2025
|
+
parallel_tool_calls: true
|
|
1358
2026
|
};
|
|
1359
2027
|
if (req.tools && req.tools.length > 0) {
|
|
1360
|
-
body["tools"] =
|
|
1361
|
-
|
|
1362
|
-
if (typeof req.toolChoice === "string") {
|
|
1363
|
-
body["tool_choice"] = req.toolChoice === "required" ? "required" : req.toolChoice;
|
|
1364
|
-
} else {
|
|
1365
|
-
body["tool_choice"] = {
|
|
1366
|
-
type: "function",
|
|
1367
|
-
function: { name: req.toolChoice.name }
|
|
1368
|
-
};
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
2028
|
+
body["tools"] = toolsToResponses(req.tools);
|
|
2029
|
+
body["tool_choice"] = mapToolChoice(req.toolChoice);
|
|
1371
2030
|
}
|
|
1372
2031
|
if (req.temperature !== void 0) body["temperature"] = req.temperature;
|
|
1373
2032
|
if (req.topP !== void 0) body["top_p"] = req.topP;
|
|
1374
|
-
if (
|
|
2033
|
+
if (this.reasoningEffort !== "none") {
|
|
2034
|
+
body["reasoning"] = { effort: this.reasoningEffort, summary: "auto" };
|
|
2035
|
+
}
|
|
1375
2036
|
return body;
|
|
1376
2037
|
}
|
|
1377
2038
|
parseStream(body, fallbackModel) {
|
|
1378
|
-
return
|
|
2039
|
+
return parseCodexResponsesStream(body, fallbackModel);
|
|
1379
2040
|
}
|
|
1380
2041
|
translateError(status, text) {
|
|
1381
2042
|
return parseProviderHttpError(this.id, status, text);
|
|
1382
2043
|
}
|
|
1383
|
-
stripCacheControl(req) {
|
|
1384
|
-
if (!req.system) return void 0;
|
|
1385
|
-
return req.system.map((b) => {
|
|
1386
|
-
const { cache_control: _cc, ...rest } = b;
|
|
1387
|
-
return rest;
|
|
1388
|
-
});
|
|
1389
|
-
}
|
|
1390
2044
|
};
|
|
1391
|
-
|
|
2045
|
+
function resolveCodexUrl(baseUrl) {
|
|
2046
|
+
const raw = baseUrl && baseUrl.trim().length > 0 ? baseUrl : DEFAULT_CODEX_BASE;
|
|
2047
|
+
const normalized = raw.replace(/\/+$/, "");
|
|
2048
|
+
if (normalized.endsWith("/codex/responses")) return normalized;
|
|
2049
|
+
if (normalized.endsWith("/codex")) return `${normalized}/responses`;
|
|
2050
|
+
return `${normalized}/codex/responses`;
|
|
2051
|
+
}
|
|
2052
|
+
function mapToolChoice(choice) {
|
|
2053
|
+
if (choice === void 0) return "auto";
|
|
2054
|
+
if (choice === "auto" || choice === "required" || choice === "none") return choice;
|
|
2055
|
+
return { type: "function", name: choice.name };
|
|
2056
|
+
}
|
|
2057
|
+
async function* parseCodexResponsesStream(body, fallbackModel) {
|
|
1392
2058
|
let model = fallbackModel;
|
|
2059
|
+
let started = false;
|
|
1393
2060
|
let usage = { input: 0, output: 0 };
|
|
1394
2061
|
let stopReason = "end_turn";
|
|
1395
|
-
let
|
|
1396
|
-
let
|
|
1397
|
-
|
|
2062
|
+
let sawToolUse = false;
|
|
2063
|
+
let toolCallId;
|
|
2064
|
+
let toolArgBuf = "";
|
|
2065
|
+
const ensureStart = () => {
|
|
2066
|
+
if (started) return void 0;
|
|
2067
|
+
started = true;
|
|
2068
|
+
return { type: "message_start", model };
|
|
2069
|
+
};
|
|
1398
2070
|
for await (const msg of parseSSE(body)) {
|
|
1399
2071
|
if (!msg.data || msg.data === "[DONE]") continue;
|
|
1400
2072
|
const parsed = safeParse(msg.data);
|
|
1401
2073
|
if (!parsed.ok || !parsed.value) continue;
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
if (!thinkingOpen) {
|
|
1413
|
-
thinkingOpen = true;
|
|
1414
|
-
yield { type: "thinking_start" };
|
|
2074
|
+
const evt = parsed.value;
|
|
2075
|
+
const type = typeof evt["type"] === "string" ? evt["type"] : "";
|
|
2076
|
+
switch (type) {
|
|
2077
|
+
case "response.created":
|
|
2078
|
+
case "response.in_progress": {
|
|
2079
|
+
const resp = evt["response"];
|
|
2080
|
+
if (typeof resp?.model === "string") model = resp.model;
|
|
2081
|
+
const s = ensureStart();
|
|
2082
|
+
if (s) yield s;
|
|
2083
|
+
break;
|
|
1415
2084
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
2085
|
+
case "response.output_item.added": {
|
|
2086
|
+
const s = ensureStart();
|
|
2087
|
+
if (s) yield s;
|
|
2088
|
+
const item = evt["item"];
|
|
2089
|
+
if (!item) break;
|
|
2090
|
+
if (item.type === "reasoning") {
|
|
2091
|
+
yield { type: "thinking_start" };
|
|
2092
|
+
} else if (item.type === "function_call") {
|
|
2093
|
+
toolCallId = item.call_id ?? item.id ?? `call_${Math.random().toString(36).slice(2)}`;
|
|
2094
|
+
toolArgBuf = item.arguments ?? "";
|
|
2095
|
+
sawToolUse = true;
|
|
2096
|
+
yield { type: "tool_use_start", id: toolCallId, name: item.name ?? "unknown" };
|
|
2097
|
+
if (toolArgBuf.length > 0) {
|
|
2098
|
+
yield { type: "tool_use_input_delta", id: toolCallId, partial: toolArgBuf };
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
break;
|
|
1422
2102
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
yield { type: "thinking_stop" };
|
|
2103
|
+
case "response.output_text.delta":
|
|
2104
|
+
case "response.refusal.delta": {
|
|
2105
|
+
const delta = typeof evt["delta"] === "string" ? evt["delta"] : "";
|
|
2106
|
+
if (delta) yield { type: "text_delta", text: delta };
|
|
2107
|
+
break;
|
|
1429
2108
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
if (
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
};
|
|
1441
|
-
toolByIndex.set(idx, entry);
|
|
1442
|
-
} else {
|
|
1443
|
-
if (tc.id && !entry.id) entry.id = tc.id;
|
|
1444
|
-
if (tc.function?.name && !entry.name) entry.name = tc.function.name;
|
|
1445
|
-
}
|
|
1446
|
-
if (tc.function?.arguments) {
|
|
1447
|
-
entry.argBuf += tc.function.arguments;
|
|
1448
|
-
}
|
|
1449
|
-
if (!entry.emittedStart && entry.id && entry.name) {
|
|
1450
|
-
entry.emittedStart = true;
|
|
1451
|
-
yield { type: "tool_use_start", id: entry.id, name: entry.name };
|
|
2109
|
+
case "response.reasoning_text.delta":
|
|
2110
|
+
case "response.reasoning_summary_text.delta": {
|
|
2111
|
+
const delta = typeof evt["delta"] === "string" ? evt["delta"] : "";
|
|
2112
|
+
if (delta) yield { type: "thinking_delta", text: delta };
|
|
2113
|
+
break;
|
|
2114
|
+
}
|
|
2115
|
+
case "response.function_call_arguments.delta": {
|
|
2116
|
+
const delta = typeof evt["delta"] === "string" ? evt["delta"] : "";
|
|
2117
|
+
if (toolCallId && delta) {
|
|
2118
|
+
toolArgBuf += delta;
|
|
2119
|
+
yield { type: "tool_use_input_delta", id: toolCallId, partial: delta };
|
|
1452
2120
|
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
2121
|
+
break;
|
|
2122
|
+
}
|
|
2123
|
+
case "response.function_call_arguments.done": {
|
|
2124
|
+
const args = typeof evt["arguments"] === "string" ? evt["arguments"] : void 0;
|
|
2125
|
+
if (args !== void 0) toolArgBuf = args;
|
|
2126
|
+
break;
|
|
2127
|
+
}
|
|
2128
|
+
case "response.output_item.done": {
|
|
2129
|
+
const item = evt["item"];
|
|
2130
|
+
if (!item) break;
|
|
2131
|
+
if (item.type === "reasoning") {
|
|
2132
|
+
yield { type: "thinking_stop" };
|
|
2133
|
+
} else if (item.type === "function_call") {
|
|
2134
|
+
const id = item.call_id ?? toolCallId ?? `call_${Math.random().toString(36).slice(2)}`;
|
|
2135
|
+
const raw = item.arguments && item.arguments.length > 0 ? item.arguments : toolArgBuf;
|
|
2136
|
+
yield { type: "tool_use_stop", id, input: parseToolInput(raw || "{}") };
|
|
2137
|
+
toolCallId = void 0;
|
|
2138
|
+
toolArgBuf = "";
|
|
1461
2139
|
}
|
|
2140
|
+
break;
|
|
2141
|
+
}
|
|
2142
|
+
case "response.completed":
|
|
2143
|
+
case "response.incomplete": {
|
|
2144
|
+
const resp = evt["response"];
|
|
2145
|
+
if (resp?.usage) usage = normalizeUsage(resp.usage);
|
|
2146
|
+
stopReason = mapResponsesStatus(resp?.status, sawToolUse);
|
|
2147
|
+
break;
|
|
2148
|
+
}
|
|
2149
|
+
case "error":
|
|
2150
|
+
case "response.failed": {
|
|
2151
|
+
const message = evt["message"] ?? evt["response"]?.error?.message ?? "Codex response failed";
|
|
2152
|
+
throw new ProviderError(message, 502, true, "openai-codex", {
|
|
2153
|
+
body: { message }
|
|
2154
|
+
});
|
|
1462
2155
|
}
|
|
1463
2156
|
}
|
|
1464
|
-
if (choice?.finish_reason) {
|
|
1465
|
-
stopReason = normalizeOpenAI(choice.finish_reason);
|
|
1466
|
-
}
|
|
1467
|
-
const u = obj["usage"];
|
|
1468
|
-
if (u) {
|
|
1469
|
-
const hasDeepSeekCacheFields = u.prompt_cache_hit_tokens !== void 0 || u.prompt_cache_miss_tokens !== void 0;
|
|
1470
|
-
const cached = u.prompt_tokens_details?.cached_tokens ?? u.prompt_cache_hit_tokens ?? 0;
|
|
1471
|
-
const promptTotal = u.prompt_tokens ?? u.input_tokens ?? (hasDeepSeekCacheFields ? (u.prompt_cache_hit_tokens ?? 0) + (u.prompt_cache_miss_tokens ?? 0) : usage.input + cached);
|
|
1472
|
-
usage = {
|
|
1473
|
-
input: u.prompt_cache_miss_tokens ?? Math.max(0, promptTotal - cached),
|
|
1474
|
-
output: u.completion_tokens ?? usage.output,
|
|
1475
|
-
cacheRead: cached || usage.cacheRead
|
|
1476
|
-
};
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
if (thinkingOpen) {
|
|
1480
|
-
yield { type: "thinking_stop" };
|
|
1481
|
-
}
|
|
1482
|
-
for (const entry of toolByIndex.values()) {
|
|
1483
|
-
if (!entry.name) continue;
|
|
1484
|
-
if (!entry.id) entry.id = `call_${randomUUID()}`;
|
|
1485
|
-
if (!entry.emittedStart) {
|
|
1486
|
-
yield { type: "tool_use_start", id: entry.id, name: entry.name };
|
|
1487
|
-
}
|
|
1488
|
-
const input = parseToolInput(entry.argBuf);
|
|
1489
|
-
yield { type: "tool_use_stop", id: entry.id, input };
|
|
1490
2157
|
}
|
|
1491
2158
|
if (started) {
|
|
1492
2159
|
yield { type: "message_stop", stopReason, usage };
|
|
1493
2160
|
}
|
|
1494
2161
|
}
|
|
2162
|
+
function normalizeUsage(u) {
|
|
2163
|
+
const cached = u.input_tokens_details?.cached_tokens ?? 0;
|
|
2164
|
+
const total = u.input_tokens ?? 0;
|
|
2165
|
+
return {
|
|
2166
|
+
input: Math.max(0, total - cached),
|
|
2167
|
+
output: u.output_tokens ?? 0,
|
|
2168
|
+
cacheRead: cached || void 0
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
function mapResponsesStatus(status, sawToolUse) {
|
|
2172
|
+
if (status === "incomplete") return "max_tokens";
|
|
2173
|
+
return sawToolUse ? "tool_use" : "end_turn";
|
|
2174
|
+
}
|
|
1495
2175
|
|
|
1496
2176
|
// src/openai-compatible.ts
|
|
1497
2177
|
var VALID_QUIRK_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -2158,14 +2838,17 @@ function buildGenConfig(req) {
|
|
|
2158
2838
|
return cfg;
|
|
2159
2839
|
}
|
|
2160
2840
|
function toolsToGemini2(tools) {
|
|
2161
|
-
return tools.map((t) =>
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2841
|
+
return tools.map((t) => {
|
|
2842
|
+
const compact = compactToolDefinitionForWire(t);
|
|
2843
|
+
return {
|
|
2844
|
+
name: compact.name,
|
|
2845
|
+
description: compact.description,
|
|
2846
|
+
parameters: sanitizeSchemaForGemini2(compact.inputSchema) ?? {
|
|
2847
|
+
type: "object",
|
|
2848
|
+
properties: {}
|
|
2849
|
+
}
|
|
2850
|
+
};
|
|
2851
|
+
});
|
|
2169
2852
|
}
|
|
2170
2853
|
var GEMINI_ALLOWED_KEYS2 = /* @__PURE__ */ new Set([
|
|
2171
2854
|
"type",
|
|
@@ -2419,6 +3102,11 @@ function parseToolArguments(raw, toolName, toolCallId, opts) {
|
|
|
2419
3102
|
}
|
|
2420
3103
|
|
|
2421
3104
|
// src/index.ts
|
|
3105
|
+
var _oauthPersist;
|
|
3106
|
+
function setOAuthTokenPersister(fn) {
|
|
3107
|
+
_oauthPersist = fn;
|
|
3108
|
+
}
|
|
3109
|
+
var setCodexTokenPersister = setOAuthTokenPersister;
|
|
2422
3110
|
async function buildProviderFactoriesFromRegistry(opts) {
|
|
2423
3111
|
const providers = await opts.registry.listProviders();
|
|
2424
3112
|
const factories = [];
|
|
@@ -2459,6 +3147,13 @@ function resolveActiveKey(cfg) {
|
|
|
2459
3147
|
}
|
|
2460
3148
|
return cfg.apiKey && cfg.apiKey.length > 0 ? cfg.apiKey : void 0;
|
|
2461
3149
|
}
|
|
3150
|
+
function resolveActiveKeyEntry(cfg) {
|
|
3151
|
+
if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
|
|
3152
|
+
const active = cfg.activeKey ? cfg.apiKeys.find((k) => k.label === cfg.activeKey) : void 0;
|
|
3153
|
+
return active ?? cfg.apiKeys[0];
|
|
3154
|
+
}
|
|
3155
|
+
return void 0;
|
|
3156
|
+
}
|
|
2462
3157
|
function makeProvider(p, cfg) {
|
|
2463
3158
|
const family = cfg.family ?? p.family;
|
|
2464
3159
|
const envVars = cfg.envVars && cfg.envVars.length > 0 ? cfg.envVars : p.envVars;
|
|
@@ -2497,6 +3192,48 @@ function makeProvider(p, cfg) {
|
|
|
2497
3192
|
headers: cfg.headers,
|
|
2498
3193
|
quirks: validateQuirks(p.id, cfg.quirks)
|
|
2499
3194
|
});
|
|
3195
|
+
case "openai-codex": {
|
|
3196
|
+
const entry = resolveActiveKeyEntry(cfg);
|
|
3197
|
+
const parsedExpiry = entry?.expiresAt ? Date.parse(entry.expiresAt) : Number.NaN;
|
|
3198
|
+
return new OpenAICodexProvider({
|
|
3199
|
+
id: p.id,
|
|
3200
|
+
baseUrl,
|
|
3201
|
+
credentials: {
|
|
3202
|
+
accessToken: expectDefined(apiKey),
|
|
3203
|
+
refreshToken: entry?.refreshToken,
|
|
3204
|
+
expiresAt: Number.isFinite(parsedExpiry) ? parsedExpiry : void 0,
|
|
3205
|
+
accountId: entry?.accountId
|
|
3206
|
+
},
|
|
3207
|
+
onRefresh: (creds) => _oauthPersist?.(p.id, creds)
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
3210
|
+
case "anthropic-oauth": {
|
|
3211
|
+
const entry = resolveActiveKeyEntry(cfg);
|
|
3212
|
+
const parsedExpiry = entry?.expiresAt ? Date.parse(entry.expiresAt) : Number.NaN;
|
|
3213
|
+
return new AnthropicOAuthProvider({
|
|
3214
|
+
id: p.id,
|
|
3215
|
+
baseUrl,
|
|
3216
|
+
credentials: {
|
|
3217
|
+
accessToken: expectDefined(apiKey),
|
|
3218
|
+
refreshToken: entry?.refreshToken,
|
|
3219
|
+
expiresAt: Number.isFinite(parsedExpiry) ? parsedExpiry : void 0
|
|
3220
|
+
},
|
|
3221
|
+
onRefresh: (creds) => _oauthPersist?.(p.id, creds)
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3224
|
+
case "github-copilot": {
|
|
3225
|
+
const entry = resolveActiveKeyEntry(cfg);
|
|
3226
|
+
const parsedExpiry = entry?.expiresAt ? Date.parse(entry.expiresAt) : Number.NaN;
|
|
3227
|
+
return new GitHubCopilotProvider({
|
|
3228
|
+
id: p.id,
|
|
3229
|
+
credentials: {
|
|
3230
|
+
copilotToken: resolveActiveKey(cfg) ?? "",
|
|
3231
|
+
githubToken: entry?.refreshToken,
|
|
3232
|
+
expiresAt: Number.isFinite(parsedExpiry) ? parsedExpiry : void 0
|
|
3233
|
+
},
|
|
3234
|
+
onRefresh: (creds) => _oauthPersist?.(p.id, creds)
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
2500
3237
|
case "google":
|
|
2501
3238
|
return new GoogleProvider({ id: p.id, apiKey: expectDefined(apiKey), baseUrl });
|
|
2502
3239
|
default:
|
|
@@ -2541,6 +3278,6 @@ function validateQuirks(providerId, quirks) {
|
|
|
2541
3278
|
});
|
|
2542
3279
|
}
|
|
2543
3280
|
|
|
2544
|
-
export { AnthropicProvider, CAPABILITIES_BY_FAMILY, GoogleProvider, OpenAICompatibleProvider, OpenAIProvider, WireAdapter, WireFormatProvider, anthropicWireFormat, buildProviderFactoriesFromRegistry, capabilitiesFor, capabilitiesForFamily, contentFromAnthropic, contentFromOpenAI, createWireFormatFactory, defaultDebugStreamCallback, defineWireFormat, googleWireFormat, isDebugStreamEnabled, makeProviderFromConfig, messagesToOpenAI, mistralWireFormat, normalizeAnthropic, normalizeOpenAI, openaiWireFormat, parseProviderHttpError, pushDebugChunkStats, setDebugStreamCallback, setDebugStreamEnabled, toolsToAnthropic, toolsToOpenAI };
|
|
3281
|
+
export { AnthropicOAuthProvider, AnthropicProvider, CAPABILITIES_BY_FAMILY, CLAUDE_CODE_SYSTEM_PROMPT, GitHubCopilotProvider, GoogleProvider, OpenAICodexProvider, OpenAICompatibleProvider, OpenAIProvider, WireAdapter, WireFormatProvider, anthropicWireFormat, buildProviderFactoriesFromRegistry, capabilitiesFor, capabilitiesForFamily, contentFromAnthropic, contentFromOpenAI, copilotBaseUrlFromToken, createWireFormatFactory, defaultDebugStreamCallback, defineWireFormat, extractAccountId, googleWireFormat, isDebugStreamEnabled, makeProviderFromConfig, messagesToOpenAI, mistralWireFormat, normalizeAnthropic, normalizeOpenAI, openaiWireFormat, parseProviderHttpError, pushDebugChunkStats, refreshAnthropicOAuthToken, refreshCodexAccessToken, refreshCopilotToken, resolveCodexUrl, setCodexTokenPersister, setDebugStreamCallback, setDebugStreamEnabled, setOAuthTokenPersister, toolsToAnthropic, toolsToOpenAI };
|
|
2545
3282
|
//# sourceMappingURL=index.js.map
|
|
2546
3283
|
//# sourceMappingURL=index.js.map
|