zidane 1.7.0 → 1.8.1
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/{agent-B__ISsOE.d.ts → agent-8QBOx4_0.d.ts} +405 -15
- package/dist/{chunk-T6MC3WP5.js → chunk-3RJOWJOJ.js} +1 -1
- package/dist/chunk-3S377TS4.js +662 -0
- package/dist/{chunk-O4QCBJQF.js → chunk-4N5ADW7A.js} +382 -73
- package/dist/{chunk-QCJKUQTQ.js → chunk-EC7IRWVS.js} +135 -4
- package/dist/chunk-FFFDQHMA.js +68 -0
- package/dist/{chunk-WDBO3JCO.js → chunk-TBC6MSVK.js} +118 -7
- package/dist/{chunk-V4PBD7LE.js → chunk-VG2E6YK3.js} +162 -97
- package/dist/glob-Bo0GAiKk.d.ts +5 -0
- package/dist/harnesses.d.ts +1 -1
- package/dist/harnesses.js +4 -3
- package/dist/index.d.ts +68 -4
- package/dist/index.js +152 -8
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +3 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +16 -615
- package/dist/session/sqlite.d.ts +17 -0
- package/dist/session/sqlite.js +98 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +5 -4
- package/dist/{spawn-DRamirTu.d.ts → spawn-jjxPcNcH.d.ts} +1 -1
- package/dist/tools.d.ts +3 -2
- package/dist/tools.js +5 -2
- package/dist/types.d.ts +2 -2
- package/dist/types.js +14 -0
- package/package.json +12 -3
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assistantMessage,
|
|
3
|
+
fromAnthropic,
|
|
4
|
+
openaiCompat,
|
|
5
|
+
toAnthropic,
|
|
6
|
+
toolResultsMessage,
|
|
7
|
+
userMessage
|
|
8
|
+
} from "./chunk-EC7IRWVS.js";
|
|
9
|
+
import {
|
|
10
|
+
matchesContextExceeded
|
|
11
|
+
} from "./chunk-FFFDQHMA.js";
|
|
12
|
+
|
|
13
|
+
// src/providers/anthropic.ts
|
|
14
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
15
|
+
import { resolve as resolve2 } from "path";
|
|
16
|
+
|
|
17
|
+
// src/providers/oauth.ts
|
|
18
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
19
|
+
import { resolve } from "path";
|
|
20
|
+
import { getOAuthApiKey } from "@yaelg/pi-ai/oauth";
|
|
21
|
+
var CREDENTIALS_FILE = resolve(process.cwd(), ".credentials.json");
|
|
22
|
+
function readOAuthCredentials() {
|
|
23
|
+
if (!existsSync(CREDENTIALS_FILE))
|
|
24
|
+
return {};
|
|
25
|
+
return JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
26
|
+
}
|
|
27
|
+
function writeOAuthCredentials(credentials) {
|
|
28
|
+
writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
|
|
29
|
+
}
|
|
30
|
+
function credentialsFromParams(params, extraKeys = []) {
|
|
31
|
+
if (typeof params?.access !== "string" || typeof params.refresh !== "string" || typeof params.expires !== "number")
|
|
32
|
+
return void 0;
|
|
33
|
+
const extras = Object.fromEntries(
|
|
34
|
+
extraKeys.map((key) => [key, params[key]]).filter(([, value]) => value !== void 0)
|
|
35
|
+
);
|
|
36
|
+
return {
|
|
37
|
+
access: params.access,
|
|
38
|
+
refresh: params.refresh,
|
|
39
|
+
expires: params.expires,
|
|
40
|
+
...extras
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function resolveOAuthApiKey(options, callbacks) {
|
|
44
|
+
if (typeof options.params?.apiKey === "string")
|
|
45
|
+
return options.params.apiKey;
|
|
46
|
+
const paramsCredentials = credentialsFromParams(options.params, options.extraCredentialKeys);
|
|
47
|
+
if (paramsCredentials) {
|
|
48
|
+
return await resolveCredentialSource("params", paramsCredentials);
|
|
49
|
+
}
|
|
50
|
+
if (typeof options.params?.access === "string")
|
|
51
|
+
return options.params.access;
|
|
52
|
+
if (options.envKey && process.env[options.envKey])
|
|
53
|
+
return process.env[options.envKey];
|
|
54
|
+
const readCredentials = options.readCredentials ?? readOAuthCredentials;
|
|
55
|
+
const writeCredentials = options.writeCredentials ?? writeOAuthCredentials;
|
|
56
|
+
const allCredentials = readCredentials();
|
|
57
|
+
const storedCredentials = allCredentials[options.providerId];
|
|
58
|
+
if (!storedCredentials)
|
|
59
|
+
throw new Error(options.missingError);
|
|
60
|
+
return await resolveCredentialSource("file", storedCredentials, allCredentials, writeCredentials);
|
|
61
|
+
async function resolveCredentialSource(source, current, allCredentials2, persistCredentials) {
|
|
62
|
+
try {
|
|
63
|
+
const refreshOAuthApiKey = options.getOAuthApiKey ?? getOAuthApiKey;
|
|
64
|
+
const result = await refreshOAuthApiKey(options.providerId, { [options.providerId]: current });
|
|
65
|
+
if (!result)
|
|
66
|
+
throw new Error(options.missingError);
|
|
67
|
+
if (result.newCredentials !== current) {
|
|
68
|
+
if (source === "file" && allCredentials2 && persistCredentials) {
|
|
69
|
+
allCredentials2[options.providerId] = result.newCredentials;
|
|
70
|
+
persistCredentials(allCredentials2);
|
|
71
|
+
}
|
|
72
|
+
await callbacks?.onOAuthRefresh?.({
|
|
73
|
+
provider: options.provider,
|
|
74
|
+
providerId: options.providerId,
|
|
75
|
+
source,
|
|
76
|
+
previousCredentials: { ...current },
|
|
77
|
+
credentials: { ...result.newCredentials }
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return result.apiKey;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
83
|
+
throw new Error(options.refreshError(reason));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/providers/anthropic.ts
|
|
89
|
+
var CREDENTIALS_FILE2 = resolve2(process.cwd(), ".credentials.json");
|
|
90
|
+
var _sdkCtor = null;
|
|
91
|
+
async function loadAnthropicSdk() {
|
|
92
|
+
if (_sdkCtor)
|
|
93
|
+
return _sdkCtor;
|
|
94
|
+
try {
|
|
95
|
+
const mod = await import("@anthropic-ai/sdk");
|
|
96
|
+
_sdkCtor = mod.default;
|
|
97
|
+
return _sdkCtor;
|
|
98
|
+
} catch (err) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
"The `anthropic` provider requires the `@anthropic-ai/sdk` package, which is an optional peer dependency. Install it with your package manager (e.g. `bun add @anthropic-ai/sdk`).",
|
|
101
|
+
err instanceof Error ? { cause: err } : void 0
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function getConfiguredApiKey(anthropicParams) {
|
|
106
|
+
if (anthropicParams?.apiKey)
|
|
107
|
+
return anthropicParams.apiKey;
|
|
108
|
+
if (anthropicParams?.access)
|
|
109
|
+
return anthropicParams.access;
|
|
110
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
111
|
+
return process.env.ANTHROPIC_API_KEY;
|
|
112
|
+
if (existsSync2(CREDENTIALS_FILE2)) {
|
|
113
|
+
const creds = JSON.parse(readFileSync2(CREDENTIALS_FILE2, "utf-8"));
|
|
114
|
+
if (creds.anthropic?.access)
|
|
115
|
+
return creds.anthropic.access;
|
|
116
|
+
}
|
|
117
|
+
throw new Error("No API key found. Run `bun run auth` first.");
|
|
118
|
+
}
|
|
119
|
+
function createClient(SDK, apiKey, isOAuth, baseURL) {
|
|
120
|
+
const base = baseURL ? { baseURL } : {};
|
|
121
|
+
return new SDK(
|
|
122
|
+
isOAuth ? {
|
|
123
|
+
apiKey: null,
|
|
124
|
+
authToken: apiKey,
|
|
125
|
+
dangerouslyAllowBrowser: true,
|
|
126
|
+
defaultHeaders: {
|
|
127
|
+
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
|
|
128
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
129
|
+
"user-agent": "zidane/2.0.0",
|
|
130
|
+
"x-app": "cli"
|
|
131
|
+
},
|
|
132
|
+
...base
|
|
133
|
+
} : { apiKey, ...base }
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
var THINKING_BUDGETS = {
|
|
137
|
+
minimal: 1024,
|
|
138
|
+
low: 4096,
|
|
139
|
+
medium: 10240,
|
|
140
|
+
high: 32768
|
|
141
|
+
};
|
|
142
|
+
function mapStopReason(stopReason) {
|
|
143
|
+
if (!stopReason)
|
|
144
|
+
return void 0;
|
|
145
|
+
switch (stopReason) {
|
|
146
|
+
case "end_turn":
|
|
147
|
+
case "stop_sequence":
|
|
148
|
+
return "stop";
|
|
149
|
+
case "tool_use":
|
|
150
|
+
return "tool-calls";
|
|
151
|
+
case "max_tokens":
|
|
152
|
+
return "length";
|
|
153
|
+
case "refusal":
|
|
154
|
+
return "content-filter";
|
|
155
|
+
case "pause_turn":
|
|
156
|
+
return "other";
|
|
157
|
+
default:
|
|
158
|
+
return "other";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function looksLikeAnthropicApiError(err) {
|
|
162
|
+
if (!err || typeof err !== "object")
|
|
163
|
+
return false;
|
|
164
|
+
const e = err;
|
|
165
|
+
return typeof e.status === "number" && "error" in e;
|
|
166
|
+
}
|
|
167
|
+
function classifyAnthropicError(err) {
|
|
168
|
+
if (!err || typeof err !== "object")
|
|
169
|
+
return null;
|
|
170
|
+
const anyErr = err;
|
|
171
|
+
if (anyErr.name === "AbortError")
|
|
172
|
+
return { kind: "aborted" };
|
|
173
|
+
if (!looksLikeAnthropicApiError(err))
|
|
174
|
+
return null;
|
|
175
|
+
const innerType = anyErr.error?.error?.type;
|
|
176
|
+
const outerType = anyErr.error?.type;
|
|
177
|
+
const nativeType = innerType && innerType !== "error" ? innerType : outerType;
|
|
178
|
+
const message = anyErr.error?.error?.message ?? anyErr.error?.message ?? anyErr.message ?? "";
|
|
179
|
+
if (matchesContextExceeded(message)) {
|
|
180
|
+
return {
|
|
181
|
+
kind: "context_exceeded",
|
|
182
|
+
providerCode: nativeType ?? "invalid_request_error",
|
|
183
|
+
message
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
kind: "provider_error",
|
|
188
|
+
providerCode: nativeType ?? (anyErr.status ? String(anyErr.status) : void 0),
|
|
189
|
+
message
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function anthropicPromptMessage(parts) {
|
|
193
|
+
const content = [];
|
|
194
|
+
for (const part of parts) {
|
|
195
|
+
if (part.type === "text") {
|
|
196
|
+
if (part.text.length > 0)
|
|
197
|
+
content.push({ type: "text", text: part.text });
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (part.type === "image") {
|
|
201
|
+
content.push({ type: "image", mediaType: part.mediaType, data: part.data });
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (part.encoding === "text") {
|
|
205
|
+
const header2 = part.name ? `<attachment name="${part.name}" media_type="${part.mediaType}">` : `<attachment media_type="${part.mediaType}">`;
|
|
206
|
+
content.push({ type: "text", text: `${header2}
|
|
207
|
+
${part.data}
|
|
208
|
+
</attachment>` });
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const header = part.name ? `<attachment name="${part.name}" media_type="${part.mediaType}" encoding="base64">` : `<attachment media_type="${part.mediaType}" encoding="base64">`;
|
|
212
|
+
content.push({ type: "text", text: `${header}
|
|
213
|
+
${part.data}
|
|
214
|
+
</attachment>` });
|
|
215
|
+
}
|
|
216
|
+
return { role: "user", content };
|
|
217
|
+
}
|
|
218
|
+
function anthropic(anthropicParams) {
|
|
219
|
+
const configuredApiKey = getConfiguredApiKey(anthropicParams);
|
|
220
|
+
const isOAuth = configuredApiKey.includes("sk-ant-oat");
|
|
221
|
+
const defaultModel = anthropicParams?.defaultModel || "claude-opus-4-6";
|
|
222
|
+
let runtimeCredentials = typeof anthropicParams?.access === "string" && typeof anthropicParams.refresh === "string" && typeof anthropicParams.expires === "number" ? {
|
|
223
|
+
access: anthropicParams.access,
|
|
224
|
+
refresh: anthropicParams.refresh,
|
|
225
|
+
expires: anthropicParams.expires
|
|
226
|
+
} : void 0;
|
|
227
|
+
return {
|
|
228
|
+
name: "anthropic",
|
|
229
|
+
meta: { defaultModel, isOAuth },
|
|
230
|
+
formatTools(tools) {
|
|
231
|
+
return tools.map((t) => ({
|
|
232
|
+
name: t.name,
|
|
233
|
+
description: t.description,
|
|
234
|
+
input_schema: t.inputSchema
|
|
235
|
+
}));
|
|
236
|
+
},
|
|
237
|
+
userMessage(content, images) {
|
|
238
|
+
if (images && images.length > 0) {
|
|
239
|
+
return {
|
|
240
|
+
role: "user",
|
|
241
|
+
content: [
|
|
242
|
+
...images.map((img) => ({
|
|
243
|
+
type: "image",
|
|
244
|
+
mediaType: img.source.media_type,
|
|
245
|
+
data: img.source.data
|
|
246
|
+
})),
|
|
247
|
+
{ type: "text", text: content }
|
|
248
|
+
]
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return { role: "user", content: [{ type: "text", text: content }] };
|
|
252
|
+
},
|
|
253
|
+
assistantMessage(content) {
|
|
254
|
+
return { role: "assistant", content: [{ type: "text", text: content }] };
|
|
255
|
+
},
|
|
256
|
+
toolResultsMessage(results) {
|
|
257
|
+
return {
|
|
258
|
+
role: "user",
|
|
259
|
+
content: results.map((r) => ({
|
|
260
|
+
type: "tool_result",
|
|
261
|
+
callId: r.id,
|
|
262
|
+
output: r.content
|
|
263
|
+
}))
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
promptMessage: anthropicPromptMessage,
|
|
267
|
+
classifyError: classifyAnthropicError,
|
|
268
|
+
async stream(options, callbacks) {
|
|
269
|
+
const SDK = await loadAnthropicSdk();
|
|
270
|
+
const apiKey = await resolveOAuthApiKey(
|
|
271
|
+
{
|
|
272
|
+
provider: "anthropic",
|
|
273
|
+
providerId: "anthropic",
|
|
274
|
+
params: runtimeCredentials ? { ...anthropicParams, ...runtimeCredentials } : anthropicParams,
|
|
275
|
+
envKey: "ANTHROPIC_API_KEY",
|
|
276
|
+
missingError: "No API key found. Run `bun run auth` first.",
|
|
277
|
+
refreshError: (reason) => `Anthropic OAuth token refresh failed. Run \`bun run auth --anthropic\` again. ${reason}`
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
...callbacks,
|
|
281
|
+
async onOAuthRefresh(ctx) {
|
|
282
|
+
if (ctx.source === "params") {
|
|
283
|
+
runtimeCredentials = {
|
|
284
|
+
access: ctx.credentials.access,
|
|
285
|
+
refresh: ctx.credentials.refresh,
|
|
286
|
+
expires: ctx.credentials.expires
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
await callbacks.onOAuthRefresh?.(ctx);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
const client = createClient(SDK, apiKey, apiKey.includes("sk-ant-oat"), anthropicParams?.baseURL);
|
|
294
|
+
const system = isOAuth ? `You are Claude Code, Anthropic's official CLI for Claude.` : options.system;
|
|
295
|
+
const messages = isOAuth && options.system ? [
|
|
296
|
+
{ role: "user", content: [{ type: "text", text: options.system }] },
|
|
297
|
+
{ role: "assistant", content: [{ type: "text", text: "Understood. I will proceed with these instructions above the rest of my system prompt." }] },
|
|
298
|
+
...options.messages
|
|
299
|
+
] : [...options.messages];
|
|
300
|
+
const thinking = options.thinking ?? "off";
|
|
301
|
+
const modelId = options.model;
|
|
302
|
+
const params = {
|
|
303
|
+
model: modelId,
|
|
304
|
+
max_tokens: options.maxTokens,
|
|
305
|
+
system,
|
|
306
|
+
tools: options.tools,
|
|
307
|
+
messages: messages.map((m) => toAnthropic(m)),
|
|
308
|
+
stream: true
|
|
309
|
+
};
|
|
310
|
+
if (thinking !== "off") {
|
|
311
|
+
const budgetTokens = options.thinkingBudget ?? THINKING_BUDGETS[thinking];
|
|
312
|
+
params.thinking = {
|
|
313
|
+
type: "enabled",
|
|
314
|
+
budget_tokens: budgetTokens
|
|
315
|
+
};
|
|
316
|
+
params.max_tokens = budgetTokens + params.max_tokens;
|
|
317
|
+
params.temperature = 1;
|
|
318
|
+
}
|
|
319
|
+
if (options.toolChoice) {
|
|
320
|
+
if (options.toolChoice.type === "tool" && options.toolChoice.name)
|
|
321
|
+
params.tool_choice = { type: "tool", name: options.toolChoice.name };
|
|
322
|
+
else if (options.toolChoice.type === "required")
|
|
323
|
+
params.tool_choice = { type: "any" };
|
|
324
|
+
else
|
|
325
|
+
params.tool_choice = { type: "auto" };
|
|
326
|
+
}
|
|
327
|
+
const s = client.messages.stream(params, {
|
|
328
|
+
signal: options.signal
|
|
329
|
+
});
|
|
330
|
+
let text = "";
|
|
331
|
+
s.on("text", (delta) => {
|
|
332
|
+
text += delta;
|
|
333
|
+
callbacks.onText(delta);
|
|
334
|
+
});
|
|
335
|
+
if (callbacks.onThinking) {
|
|
336
|
+
s.on("thinking", (delta) => {
|
|
337
|
+
callbacks.onThinking(delta);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const response = await s.finalMessage();
|
|
341
|
+
const toolCalls = response.content.filter((b) => b.type === "tool_use").map((b) => ({ id: b.id, name: b.name, input: b.input }));
|
|
342
|
+
const finishReason = mapStopReason(response.stop_reason);
|
|
343
|
+
return {
|
|
344
|
+
assistantMessage: fromAnthropic({ role: "assistant", content: response.content }),
|
|
345
|
+
text,
|
|
346
|
+
toolCalls,
|
|
347
|
+
done: response.stop_reason === "end_turn" || toolCalls.length === 0,
|
|
348
|
+
usage: {
|
|
349
|
+
input: response.usage.input_tokens,
|
|
350
|
+
output: response.usage.output_tokens,
|
|
351
|
+
cacheCreation: response.usage.cache_creation_input_tokens ?? void 0,
|
|
352
|
+
cacheRead: response.usage.cache_read_input_tokens ?? void 0,
|
|
353
|
+
...finishReason ? { finishReason } : {},
|
|
354
|
+
modelId: response.model ?? options.model
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/providers/cerebras.ts
|
|
362
|
+
var BASE_URL = "https://api.cerebras.ai/v1";
|
|
363
|
+
function getApiKey(params) {
|
|
364
|
+
if (params?.apiKey)
|
|
365
|
+
return params.apiKey;
|
|
366
|
+
if (process.env.CEREBRAS_API_KEY)
|
|
367
|
+
return process.env.CEREBRAS_API_KEY;
|
|
368
|
+
throw new Error("No Cerebras API key found. Set CEREBRAS_API_KEY in your environment.");
|
|
369
|
+
}
|
|
370
|
+
function cerebras(params) {
|
|
371
|
+
const apiKey = getApiKey(params);
|
|
372
|
+
return openaiCompat({
|
|
373
|
+
name: "cerebras",
|
|
374
|
+
apiKey,
|
|
375
|
+
baseURL: BASE_URL,
|
|
376
|
+
defaultModel: params?.defaultModel || "zai-glm-4.7"
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/providers/openai.ts
|
|
381
|
+
import { getModel } from "@yaelg/pi-ai";
|
|
382
|
+
import { streamOpenAICodexResponses } from "@yaelg/pi-ai/openai-codex-responses";
|
|
383
|
+
var PROVIDER_ID = "openai-codex";
|
|
384
|
+
var DEFAULT_MODEL = "gpt-5.4";
|
|
385
|
+
function resolveModel(modelId) {
|
|
386
|
+
const model = getModel(PROVIDER_ID, modelId);
|
|
387
|
+
if (model)
|
|
388
|
+
return model;
|
|
389
|
+
const fallback = getModel(PROVIDER_ID, DEFAULT_MODEL);
|
|
390
|
+
if (!fallback)
|
|
391
|
+
throw new Error(`OpenAI Codex model registry is missing the default model: ${DEFAULT_MODEL}`);
|
|
392
|
+
return { ...fallback, id: modelId, name: modelId };
|
|
393
|
+
}
|
|
394
|
+
function emptyUsage() {
|
|
395
|
+
return {
|
|
396
|
+
input: 0,
|
|
397
|
+
output: 0,
|
|
398
|
+
cacheRead: 0,
|
|
399
|
+
cacheWrite: 0,
|
|
400
|
+
totalTokens: 0,
|
|
401
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function formatTools(tools) {
|
|
405
|
+
return tools.map((t) => ({
|
|
406
|
+
name: t.name,
|
|
407
|
+
description: t.description,
|
|
408
|
+
parameters: t.inputSchema
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
411
|
+
function toPiMessages(messages, modelId) {
|
|
412
|
+
const out = [];
|
|
413
|
+
for (const msg of messages) {
|
|
414
|
+
const toolResults = msg.content.filter((b) => b.type === "tool_result");
|
|
415
|
+
if (toolResults.length > 0) {
|
|
416
|
+
for (const result of toolResults) {
|
|
417
|
+
out.push({
|
|
418
|
+
role: "toolResult",
|
|
419
|
+
toolCallId: result.callId,
|
|
420
|
+
toolName: "",
|
|
421
|
+
content: [{ type: "text", text: result.output }],
|
|
422
|
+
isError: result.isError ?? false,
|
|
423
|
+
timestamp: Date.now()
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
429
|
+
const imageBlocks = msg.content.filter((b) => b.type === "image");
|
|
430
|
+
if (msg.role === "user") {
|
|
431
|
+
if (imageBlocks.length === 0 && textBlocks.length === 1) {
|
|
432
|
+
out.push({ role: "user", content: textBlocks[0].text, timestamp: Date.now() });
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
out.push({
|
|
436
|
+
role: "user",
|
|
437
|
+
content: [
|
|
438
|
+
...imageBlocks.map((img) => ({ type: "image", data: img.data, mimeType: img.mediaType })),
|
|
439
|
+
...textBlocks.map((block) => ({ type: "text", text: block.text }))
|
|
440
|
+
],
|
|
441
|
+
timestamp: Date.now()
|
|
442
|
+
});
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const content = [];
|
|
446
|
+
for (const block of msg.content) {
|
|
447
|
+
if (block.type === "text") {
|
|
448
|
+
content.push({ type: "text", text: block.text });
|
|
449
|
+
} else if (block.type === "thinking") {
|
|
450
|
+
content.push({ type: "thinking", thinking: block.text, thinkingSignature: block.signature });
|
|
451
|
+
} else if (block.type === "tool_call") {
|
|
452
|
+
content.push({ type: "toolCall", id: block.id, name: block.name, arguments: block.input });
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
out.push({
|
|
456
|
+
role: "assistant",
|
|
457
|
+
content,
|
|
458
|
+
api: "openai-codex-responses",
|
|
459
|
+
provider: PROVIDER_ID,
|
|
460
|
+
model: modelId,
|
|
461
|
+
usage: emptyUsage(),
|
|
462
|
+
stopReason: "stop",
|
|
463
|
+
timestamp: Date.now()
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
return out;
|
|
467
|
+
}
|
|
468
|
+
function fromPiAssistantMessage(message) {
|
|
469
|
+
const content = [];
|
|
470
|
+
for (const block of message.content) {
|
|
471
|
+
if (block.type === "text") {
|
|
472
|
+
content.push({ type: "text", text: block.text });
|
|
473
|
+
} else if (block.type === "thinking") {
|
|
474
|
+
content.push({ type: "thinking", text: block.thinking, signature: block.thinkingSignature });
|
|
475
|
+
} else if (block.type === "toolCall") {
|
|
476
|
+
content.push({ type: "tool_call", id: block.id, name: block.name, input: block.arguments });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return { role: "assistant", content };
|
|
480
|
+
}
|
|
481
|
+
function extractToolCalls(message) {
|
|
482
|
+
return message.content.filter((block) => block.type === "toolCall").map((block) => ({
|
|
483
|
+
id: block.id,
|
|
484
|
+
name: block.name,
|
|
485
|
+
input: block.arguments
|
|
486
|
+
}));
|
|
487
|
+
}
|
|
488
|
+
function extractText(message) {
|
|
489
|
+
return message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
490
|
+
}
|
|
491
|
+
function toTurnUsage(usage, finishReason, modelId) {
|
|
492
|
+
return {
|
|
493
|
+
input: usage.input,
|
|
494
|
+
output: usage.output,
|
|
495
|
+
cacheRead: usage.cacheRead || void 0,
|
|
496
|
+
cacheCreation: usage.cacheWrite || void 0,
|
|
497
|
+
cost: usage.cost.total || void 0,
|
|
498
|
+
...finishReason ? { finishReason } : {},
|
|
499
|
+
modelId
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function classifyOpenAIError(err) {
|
|
503
|
+
if (!err || typeof err !== "object")
|
|
504
|
+
return null;
|
|
505
|
+
const anyErr = err;
|
|
506
|
+
if (anyErr.name === "AbortError")
|
|
507
|
+
return { kind: "aborted" };
|
|
508
|
+
const message = anyErr.message ?? "";
|
|
509
|
+
const code = anyErr.code ?? anyErr.type;
|
|
510
|
+
if (code === "context_length_exceeded" || matchesContextExceeded(message)) {
|
|
511
|
+
return {
|
|
512
|
+
kind: "context_exceeded",
|
|
513
|
+
providerCode: code ?? "context_length_exceeded",
|
|
514
|
+
message
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
if (message.length > 0) {
|
|
518
|
+
return {
|
|
519
|
+
kind: "provider_error",
|
|
520
|
+
providerCode: code,
|
|
521
|
+
message
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
function applyPayloadOverrides(payload, options) {
|
|
527
|
+
const body = payload;
|
|
528
|
+
if (options.toolChoice) {
|
|
529
|
+
if (options.toolChoice.type === "tool" && options.toolChoice.name)
|
|
530
|
+
body.tool_choice = { type: "function", name: options.toolChoice.name };
|
|
531
|
+
else if (options.toolChoice.type === "required")
|
|
532
|
+
body.tool_choice = "required";
|
|
533
|
+
else
|
|
534
|
+
body.tool_choice = "auto";
|
|
535
|
+
}
|
|
536
|
+
return body;
|
|
537
|
+
}
|
|
538
|
+
function openai(params) {
|
|
539
|
+
const defaultModel = params?.defaultModel || DEFAULT_MODEL;
|
|
540
|
+
let runtimeCredentials = typeof params?.access === "string" && typeof params.refresh === "string" && typeof params.expires === "number" ? {
|
|
541
|
+
access: params.access,
|
|
542
|
+
refresh: params.refresh,
|
|
543
|
+
expires: params.expires,
|
|
544
|
+
...params.accountId ? { accountId: params.accountId } : {}
|
|
545
|
+
} : void 0;
|
|
546
|
+
return {
|
|
547
|
+
name: "openai",
|
|
548
|
+
meta: { defaultModel, isOAuth: true },
|
|
549
|
+
formatTools,
|
|
550
|
+
userMessage,
|
|
551
|
+
assistantMessage,
|
|
552
|
+
toolResultsMessage,
|
|
553
|
+
classifyError: classifyOpenAIError,
|
|
554
|
+
async stream(options, callbacks) {
|
|
555
|
+
const modelId = options.model || defaultModel;
|
|
556
|
+
const model = resolveModel(modelId);
|
|
557
|
+
const apiKey = await resolveOAuthApiKey(
|
|
558
|
+
{
|
|
559
|
+
provider: "openai",
|
|
560
|
+
providerId: PROVIDER_ID,
|
|
561
|
+
params: runtimeCredentials ? { ...params, ...runtimeCredentials } : params,
|
|
562
|
+
envKey: "OPENAI_CODEX_API_KEY",
|
|
563
|
+
extraCredentialKeys: ["accountId"],
|
|
564
|
+
missingError: "No OpenAI Codex OAuth token found. Run `bun run auth --openai` first.",
|
|
565
|
+
refreshError: (reason) => `OpenAI Codex OAuth token refresh failed. Run \`bun run auth --openai\` again. ${reason}`
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
...callbacks,
|
|
569
|
+
async onOAuthRefresh(ctx) {
|
|
570
|
+
if (ctx.source === "params") {
|
|
571
|
+
runtimeCredentials = {
|
|
572
|
+
access: ctx.credentials.access,
|
|
573
|
+
refresh: ctx.credentials.refresh,
|
|
574
|
+
expires: ctx.credentials.expires,
|
|
575
|
+
...typeof ctx.credentials.accountId === "string" ? { accountId: ctx.credentials.accountId } : {}
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
await callbacks.onOAuthRefresh?.(ctx);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
);
|
|
582
|
+
const context = {
|
|
583
|
+
systemPrompt: options.system,
|
|
584
|
+
messages: toPiMessages(options.messages, modelId),
|
|
585
|
+
tools: options.tools
|
|
586
|
+
};
|
|
587
|
+
const stream = streamOpenAICodexResponses(model, context, {
|
|
588
|
+
apiKey,
|
|
589
|
+
maxTokens: options.maxTokens,
|
|
590
|
+
signal: options.signal,
|
|
591
|
+
transport: params?.transport,
|
|
592
|
+
reasoningEffort: options.thinking && options.thinking !== "off" ? options.thinking : void 0,
|
|
593
|
+
reasoningSummary: options.thinking && options.thinking !== "off" ? "auto" : void 0,
|
|
594
|
+
onPayload: (payload) => applyPayloadOverrides(payload, options)
|
|
595
|
+
});
|
|
596
|
+
let finalMessage;
|
|
597
|
+
let text = "";
|
|
598
|
+
let thinking = "";
|
|
599
|
+
for await (const event of stream) {
|
|
600
|
+
if (event.type === "text_delta") {
|
|
601
|
+
text += event.delta;
|
|
602
|
+
callbacks.onText(event.delta);
|
|
603
|
+
} else if (event.type === "thinking_delta") {
|
|
604
|
+
thinking += event.delta;
|
|
605
|
+
callbacks.onThinking?.(event.delta);
|
|
606
|
+
} else if (event.type === "thinking_end") {
|
|
607
|
+
const delta = event.content.startsWith(thinking) ? event.content.slice(thinking.length) : thinking ? "" : event.content;
|
|
608
|
+
if (delta) {
|
|
609
|
+
thinking += delta;
|
|
610
|
+
callbacks.onThinking?.(delta);
|
|
611
|
+
}
|
|
612
|
+
} else if (event.type === "done") {
|
|
613
|
+
finalMessage = event.message;
|
|
614
|
+
} else if (event.type === "error") {
|
|
615
|
+
throw new Error(event.error.errorMessage || "OpenAI Codex API error");
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
finalMessage ??= await stream.result();
|
|
619
|
+
text ||= extractText(finalMessage);
|
|
620
|
+
const toolCalls = extractToolCalls(finalMessage);
|
|
621
|
+
const assistantTurn = fromPiAssistantMessage(finalMessage);
|
|
622
|
+
const finishReason = toolCalls.length > 0 ? "tool-calls" : "stop";
|
|
623
|
+
return {
|
|
624
|
+
assistantMessage: assistantTurn,
|
|
625
|
+
text,
|
|
626
|
+
toolCalls,
|
|
627
|
+
done: toolCalls.length === 0,
|
|
628
|
+
usage: toTurnUsage(finalMessage.usage, finishReason, modelId)
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// src/providers/openrouter.ts
|
|
635
|
+
var BASE_URL2 = "https://openrouter.ai/api/v1";
|
|
636
|
+
function getApiKey2(params) {
|
|
637
|
+
if (params?.apiKey)
|
|
638
|
+
return params.apiKey;
|
|
639
|
+
if (process.env.OPENROUTER_API_KEY)
|
|
640
|
+
return process.env.OPENROUTER_API_KEY;
|
|
641
|
+
throw new Error("No OpenRouter API key found. Set OPENROUTER_API_KEY in your environment.");
|
|
642
|
+
}
|
|
643
|
+
function openrouter(params) {
|
|
644
|
+
const apiKey = getApiKey2(params);
|
|
645
|
+
return openaiCompat({
|
|
646
|
+
name: "openrouter",
|
|
647
|
+
apiKey,
|
|
648
|
+
baseURL: BASE_URL2,
|
|
649
|
+
defaultModel: params?.defaultModel || "anthropic/claude-sonnet-4-6",
|
|
650
|
+
extraHeaders: {
|
|
651
|
+
"HTTP-Referer": "https://github.com/Tahul/zidane",
|
|
652
|
+
"X-Title": "zidane"
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
export {
|
|
658
|
+
anthropic,
|
|
659
|
+
cerebras,
|
|
660
|
+
openai,
|
|
661
|
+
openrouter
|
|
662
|
+
};
|