@wrongstack/providers 0.264.0 → 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 +1010 -255
- 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,17 +574,20 @@ function normalizeGemini(stop) {
|
|
|
535
574
|
return "end_turn";
|
|
536
575
|
}
|
|
537
576
|
}
|
|
538
|
-
|
|
539
|
-
// src/tool-format/to-anthropic.ts
|
|
577
|
+
var _cache = /* @__PURE__ */ new WeakMap();
|
|
540
578
|
function toolsToAnthropic(tools) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
579
|
+
const hit = _cache.get(tools);
|
|
580
|
+
if (hit) return hit;
|
|
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
|
+
});
|
|
589
|
+
_cache.set(tools, result);
|
|
590
|
+
return result;
|
|
549
591
|
}
|
|
550
592
|
|
|
551
593
|
// src/stream-debug-state.ts
|
|
@@ -864,12 +906,34 @@ var AnthropicProvider = class extends WireAdapter {
|
|
|
864
906
|
return parseProviderHttpError(this.id, status, text);
|
|
865
907
|
}
|
|
866
908
|
normalizeMessage(m) {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
};
|
|
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)) };
|
|
871
912
|
}
|
|
872
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
|
+
}
|
|
873
937
|
async function* parseAnthropicStream(body, fallbackModel) {
|
|
874
938
|
const blocks = /* @__PURE__ */ new Map();
|
|
875
939
|
let model = fallbackModel;
|
|
@@ -972,12 +1036,575 @@ async function* parseAnthropicStream(body, fallbackModel) {
|
|
|
972
1036
|
yield { type: "message_stop", stopReason, usage };
|
|
973
1037
|
}
|
|
974
1038
|
}
|
|
975
|
-
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";
|
|
976
1603
|
var GoogleProvider = class extends WireAdapter {
|
|
977
1604
|
id;
|
|
978
1605
|
capabilities;
|
|
979
1606
|
constructor(opts) {
|
|
980
|
-
super(opts.apiKey, opts.baseUrl ??
|
|
1607
|
+
super(opts.apiKey, opts.baseUrl ?? DEFAULT_BASE4, opts.fetchImpl, opts.streamOpts);
|
|
981
1608
|
this.id = opts.id ?? "google";
|
|
982
1609
|
this.capabilities = capabilitiesForFamily("google", {
|
|
983
1610
|
...opts.capabilities
|
|
@@ -1022,14 +1649,17 @@ var GoogleProvider = class extends WireAdapter {
|
|
|
1022
1649
|
}
|
|
1023
1650
|
};
|
|
1024
1651
|
function toolsToGemini(tools) {
|
|
1025
|
-
return tools.map((t) =>
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
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
|
+
});
|
|
1033
1663
|
}
|
|
1034
1664
|
var GEMINI_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
1035
1665
|
"type",
|
|
@@ -1192,296 +1822,356 @@ async function* parseGoogleStream(body, fallbackModel) {
|
|
|
1192
1822
|
}
|
|
1193
1823
|
}
|
|
1194
1824
|
|
|
1195
|
-
// src/openai.ts
|
|
1825
|
+
// src/openai-codex.ts
|
|
1196
1826
|
init_tool_input();
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
}
|
|
1210
|
-
})
|
|
1827
|
+
var _toolCache = /* @__PURE__ */ new WeakMap();
|
|
1828
|
+
function toolsToResponses(tools) {
|
|
1829
|
+
const hit = _toolCache.get(tools);
|
|
1830
|
+
if (hit) return hit;
|
|
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);
|
|
1842
|
+
return result;
|
|
1211
1843
|
}
|
|
1212
|
-
function
|
|
1213
|
-
|
|
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) {
|
|
1214
1851
|
const out = [];
|
|
1215
|
-
if (system && system.length > 0) {
|
|
1216
|
-
const sysText = system.map((b) => b.text).join("\n\n");
|
|
1217
|
-
if (opts.systemAsMessage) {
|
|
1218
|
-
out.push({ role: "user", content: sysText });
|
|
1219
|
-
} else {
|
|
1220
|
-
out.push({ role: "system", content: sysText });
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
1852
|
for (const msg of messages) {
|
|
1853
|
+
const blocks = normalizeContent2(msg.content);
|
|
1224
1854
|
if (msg.role === "user") {
|
|
1225
|
-
const blocks = normalizeContent(msg.content);
|
|
1226
1855
|
const toolResults = blocks.filter((b) => b.type === "tool_result");
|
|
1227
|
-
const others = blocks.filter((b) => b.type !== "tool_result");
|
|
1228
1856
|
for (const r of toolResults) {
|
|
1229
|
-
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
1230
1857
|
out.push({
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
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)
|
|
1234
1861
|
});
|
|
1235
1862
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
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 });
|
|
1241
1873
|
}
|
|
1242
1874
|
} else if (msg.role === "assistant") {
|
|
1243
|
-
const blocks = normalizeContent(msg.content);
|
|
1244
1875
|
const textBlocks = blocks.filter((b) => b.type === "text");
|
|
1245
1876
|
const toolUses = blocks.filter((b) => b.type === "tool_use");
|
|
1246
|
-
const thinkingBlocks = blocks.filter((b) => b.type === "thinking");
|
|
1247
1877
|
const text = textBlocks.map((b) => b.text).join("");
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
if (toolCalls.length > 0) {
|
|
1256
|
-
message.tool_calls = toolCalls;
|
|
1257
|
-
if (text) {
|
|
1258
|
-
message.content = text;
|
|
1259
|
-
} else {
|
|
1260
|
-
message.content = emptyContentMode === "null" ? null : "";
|
|
1261
|
-
}
|
|
1262
|
-
} else {
|
|
1263
|
-
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
|
+
});
|
|
1264
1885
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
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
|
+
});
|
|
1267
1893
|
}
|
|
1268
|
-
out.push(message);
|
|
1269
1894
|
}
|
|
1270
1895
|
}
|
|
1271
1896
|
return out;
|
|
1272
1897
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
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}`);
|
|
1287
1919
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
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 {
|
|
1294
1939
|
return null;
|
|
1295
|
-
}
|
|
1940
|
+
}
|
|
1296
1941
|
}
|
|
1297
|
-
|
|
1298
|
-
// src/openai.ts
|
|
1299
|
-
var DEFAULT_BASE3 = "https://api.openai.com/v1";
|
|
1300
|
-
var OpenAIProvider = class extends WireAdapter {
|
|
1942
|
+
var OpenAICodexProvider = class extends WireAdapter {
|
|
1301
1943
|
id;
|
|
1302
1944
|
capabilities;
|
|
1303
|
-
|
|
1945
|
+
access;
|
|
1946
|
+
refresh;
|
|
1947
|
+
expiresAt;
|
|
1948
|
+
accountId;
|
|
1949
|
+
onRefresh;
|
|
1950
|
+
refreshFn;
|
|
1951
|
+
reasoningEffort;
|
|
1304
1952
|
constructor(opts) {
|
|
1305
|
-
super(
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
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
|
|
1312
1999
|
});
|
|
1313
2000
|
}
|
|
1314
2001
|
buildUrl(_req) {
|
|
1315
|
-
|
|
1316
|
-
if (/\/chat\/completions$/.test(base)) return base;
|
|
1317
|
-
if (/\/v\d+(\/[a-z0-9_-]+)*$/i.test(base)) return `${base}/chat/completions`;
|
|
1318
|
-
return `${base}/v1/chat/completions`;
|
|
2002
|
+
return resolveCodexUrl(this.baseUrl);
|
|
1319
2003
|
}
|
|
1320
|
-
buildHeaders(
|
|
2004
|
+
buildHeaders(_req) {
|
|
1321
2005
|
const headers = {
|
|
1322
|
-
...super.buildHeaders(
|
|
1323
|
-
authorization: `Bearer ${this.
|
|
2006
|
+
...super.buildHeaders(_req),
|
|
2007
|
+
authorization: `Bearer ${this.access}`,
|
|
2008
|
+
originator: "wrongstack",
|
|
2009
|
+
"OpenAI-Beta": "responses=experimental"
|
|
1324
2010
|
};
|
|
1325
|
-
if (this.
|
|
1326
|
-
headers["openai-organization"] = this.opts.organization;
|
|
1327
|
-
}
|
|
2011
|
+
if (this.accountId) headers["chatgpt-account-id"] = this.accountId;
|
|
1328
2012
|
return headers;
|
|
1329
2013
|
}
|
|
1330
|
-
/**
|
|
1331
|
-
* The request field used to cap output length. Real OpenAI deprecated
|
|
1332
|
-
* `max_tokens` and the newer model families (gpt-4o, o1/o3/o4) 400 on it —
|
|
1333
|
-
* they require `max_completion_tokens`. OpenAI-compatible endpoints that
|
|
1334
|
-
* still only accept `max_tokens` override this. See issue #10.
|
|
1335
|
-
*/
|
|
1336
|
-
tokenLimitParam() {
|
|
1337
|
-
return "max_completion_tokens";
|
|
1338
|
-
}
|
|
1339
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.";
|
|
1340
2016
|
const body = {
|
|
1341
2017
|
model: req.model,
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
[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,
|
|
1346
2021
|
stream: true,
|
|
1347
|
-
|
|
2022
|
+
instructions,
|
|
2023
|
+
input: messagesToResponsesInput(req.messages),
|
|
2024
|
+
include: ["reasoning.encrypted_content"],
|
|
2025
|
+
parallel_tool_calls: true
|
|
1348
2026
|
};
|
|
1349
2027
|
if (req.tools && req.tools.length > 0) {
|
|
1350
|
-
body["tools"] =
|
|
1351
|
-
|
|
1352
|
-
if (typeof req.toolChoice === "string") {
|
|
1353
|
-
body["tool_choice"] = req.toolChoice === "required" ? "required" : req.toolChoice;
|
|
1354
|
-
} else {
|
|
1355
|
-
body["tool_choice"] = {
|
|
1356
|
-
type: "function",
|
|
1357
|
-
function: { name: req.toolChoice.name }
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
2028
|
+
body["tools"] = toolsToResponses(req.tools);
|
|
2029
|
+
body["tool_choice"] = mapToolChoice(req.toolChoice);
|
|
1361
2030
|
}
|
|
1362
2031
|
if (req.temperature !== void 0) body["temperature"] = req.temperature;
|
|
1363
2032
|
if (req.topP !== void 0) body["top_p"] = req.topP;
|
|
1364
|
-
if (
|
|
2033
|
+
if (this.reasoningEffort !== "none") {
|
|
2034
|
+
body["reasoning"] = { effort: this.reasoningEffort, summary: "auto" };
|
|
2035
|
+
}
|
|
1365
2036
|
return body;
|
|
1366
2037
|
}
|
|
1367
2038
|
parseStream(body, fallbackModel) {
|
|
1368
|
-
return
|
|
2039
|
+
return parseCodexResponsesStream(body, fallbackModel);
|
|
1369
2040
|
}
|
|
1370
2041
|
translateError(status, text) {
|
|
1371
2042
|
return parseProviderHttpError(this.id, status, text);
|
|
1372
2043
|
}
|
|
1373
|
-
stripCacheControl(req) {
|
|
1374
|
-
if (!req.system) return void 0;
|
|
1375
|
-
return req.system.map((b) => {
|
|
1376
|
-
const { cache_control: _cc, ...rest } = b;
|
|
1377
|
-
return rest;
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
2044
|
};
|
|
1381
|
-
|
|
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) {
|
|
1382
2058
|
let model = fallbackModel;
|
|
2059
|
+
let started = false;
|
|
1383
2060
|
let usage = { input: 0, output: 0 };
|
|
1384
2061
|
let stopReason = "end_turn";
|
|
1385
|
-
let
|
|
1386
|
-
let
|
|
1387
|
-
|
|
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
|
+
};
|
|
1388
2070
|
for await (const msg of parseSSE(body)) {
|
|
1389
2071
|
if (!msg.data || msg.data === "[DONE]") continue;
|
|
1390
2072
|
const parsed = safeParse(msg.data);
|
|
1391
2073
|
if (!parsed.ok || !parsed.value) continue;
|
|
1392
|
-
const
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
if (!thinkingOpen) {
|
|
1403
|
-
thinkingOpen = true;
|
|
1404
|
-
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;
|
|
1405
2084
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
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;
|
|
1412
2102
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
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;
|
|
1419
2108
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
if (
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
};
|
|
1431
|
-
toolByIndex.set(idx, entry);
|
|
1432
|
-
} else {
|
|
1433
|
-
if (tc.id && !entry.id) entry.id = tc.id;
|
|
1434
|
-
if (tc.function?.name && !entry.name) entry.name = tc.function.name;
|
|
1435
|
-
}
|
|
1436
|
-
if (tc.function?.arguments) {
|
|
1437
|
-
entry.argBuf += tc.function.arguments;
|
|
1438
|
-
}
|
|
1439
|
-
if (!entry.emittedStart && entry.id && entry.name) {
|
|
1440
|
-
entry.emittedStart = true;
|
|
1441
|
-
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 };
|
|
1442
2120
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
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 = "";
|
|
1451
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
|
+
});
|
|
1452
2155
|
}
|
|
1453
2156
|
}
|
|
1454
|
-
if (choice?.finish_reason) {
|
|
1455
|
-
stopReason = normalizeOpenAI(choice.finish_reason);
|
|
1456
|
-
}
|
|
1457
|
-
const u = obj["usage"];
|
|
1458
|
-
if (u) {
|
|
1459
|
-
const hasDeepSeekCacheFields = u.prompt_cache_hit_tokens !== void 0 || u.prompt_cache_miss_tokens !== void 0;
|
|
1460
|
-
const cached = u.prompt_tokens_details?.cached_tokens ?? u.prompt_cache_hit_tokens ?? 0;
|
|
1461
|
-
const promptTotal = u.prompt_tokens ?? u.input_tokens ?? (hasDeepSeekCacheFields ? (u.prompt_cache_hit_tokens ?? 0) + (u.prompt_cache_miss_tokens ?? 0) : usage.input + cached);
|
|
1462
|
-
usage = {
|
|
1463
|
-
input: u.prompt_cache_miss_tokens ?? Math.max(0, promptTotal - cached),
|
|
1464
|
-
output: u.completion_tokens ?? usage.output,
|
|
1465
|
-
cacheRead: cached || usage.cacheRead
|
|
1466
|
-
};
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
if (thinkingOpen) {
|
|
1470
|
-
yield { type: "thinking_stop" };
|
|
1471
|
-
}
|
|
1472
|
-
for (const entry of toolByIndex.values()) {
|
|
1473
|
-
if (!entry.name) continue;
|
|
1474
|
-
if (!entry.id) entry.id = `call_${randomUUID()}`;
|
|
1475
|
-
if (!entry.emittedStart) {
|
|
1476
|
-
yield { type: "tool_use_start", id: entry.id, name: entry.name };
|
|
1477
|
-
}
|
|
1478
|
-
const input = parseToolInput(entry.argBuf);
|
|
1479
|
-
yield { type: "tool_use_stop", id: entry.id, input };
|
|
1480
2157
|
}
|
|
1481
2158
|
if (started) {
|
|
1482
2159
|
yield { type: "message_stop", stopReason, usage };
|
|
1483
2160
|
}
|
|
1484
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
|
+
}
|
|
1485
2175
|
|
|
1486
2176
|
// src/openai-compatible.ts
|
|
1487
2177
|
var VALID_QUIRK_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -2148,14 +2838,17 @@ function buildGenConfig(req) {
|
|
|
2148
2838
|
return cfg;
|
|
2149
2839
|
}
|
|
2150
2840
|
function toolsToGemini2(tools) {
|
|
2151
|
-
return tools.map((t) =>
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
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
|
+
});
|
|
2159
2852
|
}
|
|
2160
2853
|
var GEMINI_ALLOWED_KEYS2 = /* @__PURE__ */ new Set([
|
|
2161
2854
|
"type",
|
|
@@ -2409,6 +3102,11 @@ function parseToolArguments(raw, toolName, toolCallId, opts) {
|
|
|
2409
3102
|
}
|
|
2410
3103
|
|
|
2411
3104
|
// src/index.ts
|
|
3105
|
+
var _oauthPersist;
|
|
3106
|
+
function setOAuthTokenPersister(fn) {
|
|
3107
|
+
_oauthPersist = fn;
|
|
3108
|
+
}
|
|
3109
|
+
var setCodexTokenPersister = setOAuthTokenPersister;
|
|
2412
3110
|
async function buildProviderFactoriesFromRegistry(opts) {
|
|
2413
3111
|
const providers = await opts.registry.listProviders();
|
|
2414
3112
|
const factories = [];
|
|
@@ -2442,10 +3140,24 @@ async function buildProviderFactoriesFromRegistry(opts) {
|
|
|
2442
3140
|
}
|
|
2443
3141
|
return factories;
|
|
2444
3142
|
}
|
|
3143
|
+
function resolveActiveKey(cfg) {
|
|
3144
|
+
if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
|
|
3145
|
+
const active = cfg.activeKey ? cfg.apiKeys.find((k) => k.label === cfg.activeKey) : void 0;
|
|
3146
|
+
return (active ?? cfg.apiKeys[0])?.apiKey;
|
|
3147
|
+
}
|
|
3148
|
+
return cfg.apiKey && cfg.apiKey.length > 0 ? cfg.apiKey : void 0;
|
|
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
|
+
}
|
|
2445
3157
|
function makeProvider(p, cfg) {
|
|
2446
3158
|
const family = cfg.family ?? p.family;
|
|
2447
3159
|
const envVars = cfg.envVars && cfg.envVars.length > 0 ? cfg.envVars : p.envVars;
|
|
2448
|
-
const apiKey = cfg
|
|
3160
|
+
const apiKey = resolveActiveKey(cfg) ?? readFromEnv(envVars);
|
|
2449
3161
|
if (!apiKey && family !== "unsupported") {
|
|
2450
3162
|
throw new Error(
|
|
2451
3163
|
`Provider "${p.id}" requires an API key. Set ${envVars.join(" or ") || "apiKey in config"} or run \`wstack auth ${p.id}\`.`
|
|
@@ -2480,6 +3192,48 @@ function makeProvider(p, cfg) {
|
|
|
2480
3192
|
headers: cfg.headers,
|
|
2481
3193
|
quirks: validateQuirks(p.id, cfg.quirks)
|
|
2482
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
|
+
}
|
|
2483
3237
|
case "google":
|
|
2484
3238
|
return new GoogleProvider({ id: p.id, apiKey: expectDefined(apiKey), baseUrl });
|
|
2485
3239
|
default:
|
|
@@ -2510,7 +3264,8 @@ function readFromEnv(vars) {
|
|
|
2510
3264
|
return void 0;
|
|
2511
3265
|
}
|
|
2512
3266
|
function requireKey(cfg) {
|
|
2513
|
-
|
|
3267
|
+
const key = resolveActiveKey(cfg);
|
|
3268
|
+
if (key) return key;
|
|
2514
3269
|
throw new Error("Provider config requires apiKey (or set the corresponding env var).");
|
|
2515
3270
|
}
|
|
2516
3271
|
function validateQuirks(providerId, quirks) {
|
|
@@ -2523,6 +3278,6 @@ function validateQuirks(providerId, quirks) {
|
|
|
2523
3278
|
});
|
|
2524
3279
|
}
|
|
2525
3280
|
|
|
2526
|
-
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 };
|
|
2527
3282
|
//# sourceMappingURL=index.js.map
|
|
2528
3283
|
//# sourceMappingURL=index.js.map
|