zelari-code 2.35.0 → 2.36.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.
@@ -1473,7 +1473,7 @@ var init_anthropicOAuth = __esm({
1473
1473
  init_grokOAuth();
1474
1474
  DEFAULT_ANTHROPIC_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
1475
1475
  ANTHROPIC_AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
1476
- ANTHROPIC_TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
1476
+ ANTHROPIC_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
1477
1477
  ANTHROPIC_REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
1478
1478
  ANTHROPIC_SCOPE = "org:create_api_key user:profile user:inference";
1479
1479
  AnthropicOAuthError = class extends Error {
@@ -1495,7 +1495,35 @@ function registerDefaultRefreshImpls() {
1495
1495
  if (!registry.has("chatgpt")) registry.set("chatgpt", chatgptRefreshAdapter);
1496
1496
  if (!registry.has("anthropic")) registry.set("anthropic", anthropicRefreshAdapter);
1497
1497
  }
1498
- var registry, grokRefreshAdapter, chatgptRefreshAdapter, anthropicRefreshAdapter;
1498
+ function normalizeRefreshError(providerId, err) {
1499
+ const code = err?.code;
1500
+ const message = err instanceof Error ? err.message : String(err);
1501
+ if (code === "invalid_grant" || message.includes("invalid_grant")) {
1502
+ return new RefreshRejectedError(
1503
+ `${providerId}: refresh token rejected (invalid_grant) \u2014 run /login ${providerId} to re-authenticate`,
1504
+ providerId,
1505
+ err
1506
+ );
1507
+ }
1508
+ return err;
1509
+ }
1510
+ async function runRefreshImpl(id3, refreshToken) {
1511
+ const existing = inflightRefresh.get(id3);
1512
+ if (existing) return existing;
1513
+ const impl = getRefreshImpl(id3);
1514
+ if (!impl) {
1515
+ throw new Error(`No refresh impl registered for provider "${id3}"`);
1516
+ }
1517
+ const run = Promise.resolve().then(() => impl(id3, refreshToken)).catch((err) => {
1518
+ throw normalizeRefreshError(id3, err);
1519
+ });
1520
+ inflightRefresh.set(id3, run);
1521
+ void run.finally(() => {
1522
+ if (inflightRefresh.get(id3) === run) inflightRefresh.delete(id3);
1523
+ }).catch(() => void 0);
1524
+ return run;
1525
+ }
1526
+ var registry, grokRefreshAdapter, chatgptRefreshAdapter, anthropicRefreshAdapter, RefreshRejectedError, inflightRefresh;
1499
1527
  var init_refreshRegistry = __esm({
1500
1528
  "src/cli/refreshRegistry.ts"() {
1501
1529
  "use strict";
@@ -1514,6 +1542,17 @@ var init_refreshRegistry = __esm({
1514
1542
  anthropicRefreshAdapter = async (_providerId, refreshToken) => {
1515
1543
  return refreshAnthropicToken({ refreshToken });
1516
1544
  };
1545
+ RefreshRejectedError = class extends Error {
1546
+ constructor(message, providerId, cause) {
1547
+ super(message);
1548
+ this.providerId = providerId;
1549
+ this.cause = cause;
1550
+ this.name = "RefreshRejectedError";
1551
+ }
1552
+ /** Marker callers can check without importing the class. */
1553
+ reloginRequired = true;
1554
+ };
1555
+ inflightRefresh = /* @__PURE__ */ new Map();
1517
1556
  }
1518
1557
  });
1519
1558
 
@@ -1742,11 +1781,7 @@ var init_keyStore = __esm({
1742
1781
  "anthropic"
1743
1782
  ];
1744
1783
  defaultRefreshImpl = async (providerId, refreshToken) => {
1745
- const impl = getRefreshImpl(providerId);
1746
- if (!impl) {
1747
- throw new Error(`No refresh impl registered for provider "${providerId}"`);
1748
- }
1749
- return impl(providerId, refreshToken);
1784
+ return runRefreshImpl(providerId, refreshToken);
1750
1785
  };
1751
1786
  }
1752
1787
  });
@@ -1767,10 +1802,11 @@ function effortLevelsFor(id3, model) {
1767
1802
  const m = (model ?? "").trim();
1768
1803
  switch (id3) {
1769
1804
  case "grok":
1770
- case "openai-compatible":
1771
- case "custom":
1772
1805
  if (grokHasXhigh(m)) return [...BASE_EFFORTS, "xhigh"];
1773
1806
  return [...BASE_EFFORTS];
1807
+ case "openai-compatible":
1808
+ case "custom":
1809
+ return [...BASE_EFFORTS, "xhigh", "max"];
1774
1810
  case "chatgpt":
1775
1811
  if (gptHasMax(m)) return [...BASE_EFFORTS, "xhigh", "max"];
1776
1812
  if (gptHasXhigh(m)) return [...BASE_EFFORTS, "xhigh"];
@@ -1983,13 +2019,14 @@ function translateOpenAiCompatibleThinking(providerId, spec, model) {
1983
2019
  };
1984
2020
  }
1985
2021
  }
1986
- function translateResponsesThinking(spec, model) {
2022
+ function translateResponsesThinking(spec, model, providerId) {
1987
2023
  if (spec === "auto") return { patch: {}, degraded: false };
2024
+ const id3 = providerId ?? "chatgpt";
1988
2025
  switch (spec.kind) {
1989
2026
  case "off":
1990
2027
  return { patch: { reasoning: { effort: "minimal" } }, degraded: false };
1991
2028
  case "effort": {
1992
- const resolved = clampEffort("chatgpt", model, spec.effort);
2029
+ const resolved = clampEffort(id3, model, spec.effort);
1993
2030
  return withClampNote(
1994
2031
  { reasoning: { effort: resolved.effort } },
1995
2032
  resolved.clamped,
@@ -1997,7 +2034,7 @@ function translateResponsesThinking(spec, model) {
1997
2034
  );
1998
2035
  }
1999
2036
  case "budget":
2000
- return degrade('thinking "budget" is not supported for chatgpt \u2014 use low/medium/high/xhigh/max');
2037
+ return degrade(`thinking "budget" is not supported on the Responses API for "${id3}" \u2014 use low/medium/high/xhigh/max`);
2001
2038
  }
2002
2039
  }
2003
2040
  function translateAnthropicThinking(spec, model) {
@@ -2046,6 +2083,7 @@ __export(providerConfig_exports, {
2046
2083
  clearKrakenVerifier: () => clearKrakenVerifier,
2047
2084
  getActiveModel: () => getActiveModel,
2048
2085
  getActiveProvider: () => getActiveProvider,
2086
+ getApiStyleFor: () => getApiStyleFor,
2049
2087
  getCustomEndpoint: () => getCustomEndpoint,
2050
2088
  getKrakenVerifierOverride: () => getKrakenVerifierOverride,
2051
2089
  getModelForProvider: () => getModelForProvider,
@@ -2054,6 +2092,7 @@ __export(providerConfig_exports, {
2054
2092
  getThinkingForProvider: () => getThinkingForProvider,
2055
2093
  loadProviderConfig: () => loadProviderConfig,
2056
2094
  setActiveProviderId: () => setActiveProviderId,
2095
+ setApiStyleFor: () => setApiStyleFor,
2057
2096
  setCustomEndpoint: () => setCustomEndpoint,
2058
2097
  setKrakenVerifier: () => setKrakenVerifier,
2059
2098
  setModelForProvider: () => setModelForProvider,
@@ -2071,6 +2110,7 @@ function mergeStoredProviderConfig(parsed) {
2071
2110
  modelByProvider: { ...DEFAULTS.modelByProvider, ...parsed.modelByProvider },
2072
2111
  thinkingByProvider: { ...DEFAULTS.thinkingByProvider, ...parsed.thinkingByProvider },
2073
2112
  customEndpoints: mergeCustomEndpoints(parsed.customEndpoints),
2113
+ apiStyleByProvider: mergeApiStyles(parsed.apiStyleByProvider),
2074
2114
  krakenVerifier: mergeKrakenVerifier(parsed.krakenVerifier)
2075
2115
  };
2076
2116
  }
@@ -2084,6 +2124,17 @@ function cloneDefaults() {
2084
2124
  customEndpoints: { ...DEFAULTS.customEndpoints }
2085
2125
  };
2086
2126
  }
2127
+ function mergeApiStyles(raw) {
2128
+ if (!raw || typeof raw !== "object") return {};
2129
+ const result = {};
2130
+ const validIds = new Set(PROVIDERS.map((p3) => p3.id));
2131
+ for (const [key, value] of Object.entries(raw)) {
2132
+ if (!validIds.has(key)) continue;
2133
+ if (value !== "responses") continue;
2134
+ result[key] = "responses";
2135
+ }
2136
+ return result;
2137
+ }
2087
2138
  function applyEnvOverrides(config2) {
2088
2139
  const envActive = process.env.ANATHEMA_ACTIVE_PROVIDER;
2089
2140
  const envModel = process.env.OPENAI_MODEL;
@@ -2155,6 +2206,23 @@ function clearCustomEndpoint(id3) {
2155
2206
  delete config2.customEndpoints[id3];
2156
2207
  writeProviderConfig(config2);
2157
2208
  }
2209
+ function getApiStyleFor(id3) {
2210
+ return getProviderConfig().apiStyleByProvider?.[id3] === "responses" ? "responses" : "chat";
2211
+ }
2212
+ function setApiStyleFor(id3, style) {
2213
+ const spec = PROVIDERS.find((p3) => p3.id === id3);
2214
+ if (!spec) {
2215
+ throw new Error(`Unknown provider id: "${id3}". Available: ${PROVIDERS.map((p3) => p3.id).join(", ")}`);
2216
+ }
2217
+ if (style !== "chat" && style !== "responses") {
2218
+ throw new Error(`Invalid api style: "${style}". Use 'chat' or 'responses'.`);
2219
+ }
2220
+ const config2 = getProviderConfig();
2221
+ if (!config2.apiStyleByProvider) config2.apiStyleByProvider = {};
2222
+ if (style === "chat") delete config2.apiStyleByProvider[id3];
2223
+ else config2.apiStyleByProvider[id3] = "responses";
2224
+ writeProviderConfig(config2);
2225
+ }
2158
2226
  function mergeKrakenVerifier(raw) {
2159
2227
  if (!raw || typeof raw !== "object") return void 0;
2160
2228
  const provider = typeof raw.provider === "string" ? raw.provider.trim() : "";
@@ -2362,23 +2430,23 @@ async function resolveAuthToken(provider, options) {
2362
2430
  return resolved?.apiKey;
2363
2431
  }
2364
2432
  async function resolveDiscoveryHeaders(provider, authToken) {
2365
- const headers2 = { Accept: "application/json" };
2366
- if (!authToken) return headers2;
2433
+ const headers3 = { Accept: "application/json" };
2434
+ if (!authToken) return headers3;
2367
2435
  if (provider === "anthropic") {
2368
- headers2.Authorization = `Bearer ${authToken}`;
2369
- headers2["x-api-key"] = authToken;
2370
- headers2["anthropic-version"] = "2023-06-01";
2371
- headers2["anthropic-beta"] = "oauth-2025-04-20";
2372
- return headers2;
2436
+ headers3.Authorization = `Bearer ${authToken}`;
2437
+ headers3["x-api-key"] = authToken;
2438
+ headers3["anthropic-version"] = "2023-06-01";
2439
+ headers3["anthropic-beta"] = "oauth-2025-04-20";
2440
+ return headers3;
2373
2441
  }
2374
- headers2.Authorization = `Bearer ${authToken}`;
2442
+ headers3.Authorization = `Bearer ${authToken}`;
2375
2443
  if (provider === "chatgpt") {
2376
2444
  const { getOAuthToken: getOAuthToken2 } = await Promise.resolve().then(() => (init_keyStore(), keyStore_exports));
2377
2445
  const accountId = getOAuthToken2("chatgpt")?.accountId;
2378
- if (accountId) headers2["ChatGPT-Account-Id"] = accountId;
2379
- headers2["OpenAI-Beta"] = "responses=experimental";
2446
+ if (accountId) headers3["ChatGPT-Account-Id"] = accountId;
2447
+ headers3["OpenAI-Beta"] = "responses=experimental";
2380
2448
  }
2381
- return headers2;
2449
+ return headers3;
2382
2450
  }
2383
2451
  function parseAnthropicModelsResponse(json3) {
2384
2452
  if (!json3 || typeof json3 !== "object") return [];
@@ -2430,8 +2498,8 @@ async function discoverModelsForProvider(provider, options = {}) {
2430
2498
  const fetchImpl = options.fetchImpl ?? fetch;
2431
2499
  let response;
2432
2500
  try {
2433
- const headers2 = await resolveDiscoveryHeaders(provider, authToken);
2434
- response = await fetchImpl(url2, { method: "GET", headers: headers2 });
2501
+ const headers3 = await resolveDiscoveryHeaders(provider, authToken);
2502
+ response = await fetchImpl(url2, { method: "GET", headers: headers3 });
2435
2503
  } catch (err) {
2436
2504
  throw new ModelDiscoveryError(
2437
2505
  `Network error contacting ${url2}: ${err instanceof Error ? err.message : String(err)}`,
@@ -38221,7 +38289,7 @@ var CORE_VERSION;
38221
38289
  var init_version = __esm({
38222
38290
  "packages/core/dist/version.js"() {
38223
38291
  "use strict";
38224
- CORE_VERSION = "2.35.0";
38292
+ CORE_VERSION = "2.36.0";
38225
38293
  }
38226
38294
  });
38227
38295
 
@@ -51016,7 +51084,7 @@ function openaiCompatibleProvider(config2) {
51016
51084
  const forceRecoveryTool = generation?.toolChoice === "required" && capabilities.buildRecovery.forceToolChoice && recoveryAttempt <= capabilities.buildRecovery.maxForcedTurns;
51017
51085
  body.tool_choice = forceRecoveryTool ? "required" : "auto";
51018
51086
  }
51019
- const headers2 = {
51087
+ const headers3 = {
51020
51088
  "Content-Type": "application/json",
51021
51089
  Authorization: `Bearer ${config2.apiKey}`,
51022
51090
  ...config2.extraHeaders ?? {}
@@ -51024,7 +51092,7 @@ function openaiCompatibleProvider(config2) {
51024
51092
  const affinityHeader = capabilities.promptCache.conversationAffinityHeader;
51025
51093
  const conversationId = params.conversationId?.trim();
51026
51094
  if (affinityHeader && conversationId && conversationId.length <= 256 && !/[\u0000-\u001f\u007f]/.test(conversationId)) {
51027
- headers2[affinityHeader] = conversationId;
51095
+ headers3[affinityHeader] = conversationId;
51028
51096
  }
51029
51097
  let response;
51030
51098
  let lastErrText = "";
@@ -51049,7 +51117,7 @@ function openaiCompatibleProvider(config2) {
51049
51117
  try {
51050
51118
  response = await fetch(`${config2.baseUrl}/chat/completions`, {
51051
51119
  method: "POST",
51052
- headers: headers2,
51120
+ headers: headers3,
51053
51121
  body: JSON.stringify(body),
51054
51122
  // Cancel aborts the HTTP request; stream idle is enforced below
51055
51123
  // per-chunk so active multi-minute streams are not killed.
@@ -52958,6 +53026,244 @@ var init_chatgpt = __esm({
52958
53026
  }
52959
53027
  });
52960
53028
 
53029
+ // src/cli/provider/responsesApi.ts
53030
+ function backoffDelay2(attempt, retryAfterHeader) {
53031
+ if (retryAfterHeader) {
53032
+ const seconds = Number.parseFloat(retryAfterHeader);
53033
+ if (Number.isFinite(seconds) && seconds >= 0) {
53034
+ return Math.min(seconds * 1e3, BACKOFF_CAP_MS2);
53035
+ }
53036
+ }
53037
+ return Math.min(BACKOFF_BASE_MS2 * 2 ** attempt, BACKOFF_CAP_MS2);
53038
+ }
53039
+ function abortableSleep2(ms, signal) {
53040
+ return new Promise((resolve9) => {
53041
+ if (signal?.aborted) return resolve9();
53042
+ const t = setTimeout(resolve9, ms);
53043
+ signal?.addEventListener(
53044
+ "abort",
53045
+ () => {
53046
+ clearTimeout(t);
53047
+ resolve9();
53048
+ },
53049
+ { once: true }
53050
+ );
53051
+ });
53052
+ }
53053
+ function headers2(config2) {
53054
+ const h = {
53055
+ "Content-Type": "application/json",
53056
+ Accept: "text/event-stream",
53057
+ Authorization: `Bearer ${config2.apiKey}`
53058
+ };
53059
+ if (config2.extraHeaders) Object.assign(h, config2.extraHeaders);
53060
+ return h;
53061
+ }
53062
+ function responsesApiProvider(config2) {
53063
+ return async function* (params) {
53064
+ const capabilities = capabilitiesFor(params.model, config2.providerId);
53065
+ const { instructions, input } = toInput(params.messages);
53066
+ const body = {
53067
+ model: params.model,
53068
+ stream: true,
53069
+ input
53070
+ };
53071
+ if (instructions) body.instructions = instructions;
53072
+ if (params.tools && params.tools.length > 0) {
53073
+ body.tools = params.tools.map((t) => ({
53074
+ type: "function",
53075
+ name: t.name,
53076
+ description: t.description,
53077
+ parameters: t.parameters
53078
+ }));
53079
+ }
53080
+ const generation = params.generation;
53081
+ body.temperature = generation?.temperature ?? capabilities.sampling.temperature;
53082
+ const maxTokens = generation?.maxTokens ?? capabilities.maxOutputTokens;
53083
+ if (typeof maxTokens === "number" && maxTokens > 0) {
53084
+ body.max_output_tokens = maxTokens;
53085
+ }
53086
+ const thinkingSpec = config2.thinking ?? "auto";
53087
+ if (thinkingSpec !== "auto") {
53088
+ const t = translateResponsesThinking(thinkingSpec, config2.model, config2.providerId);
53089
+ if (t.degraded) {
53090
+ console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
53091
+ } else {
53092
+ Object.assign(body, t.patch);
53093
+ }
53094
+ }
53095
+ const base2 = config2.baseUrl.replace(/\/$/, "");
53096
+ const url2 = `${base2}/responses`;
53097
+ let response;
53098
+ let lastStatus = 0;
53099
+ let lastErrText = "";
53100
+ for (let attempt = 0; ; attempt++) {
53101
+ const connectController = new AbortController();
53102
+ const connectTimer = setTimeout(
53103
+ () => connectController.abort(
53104
+ new Error(
53105
+ `Provider connect timeout after ${Math.round(PROVIDER_CONNECT_TIMEOUT_MS / 1e3)}s (no response headers). Override ZELARI_PROVIDER_CONNECT_TIMEOUT_MS.`
53106
+ )
53107
+ ),
53108
+ PROVIDER_CONNECT_TIMEOUT_MS
53109
+ );
53110
+ const signals = [connectController.signal];
53111
+ if (params.signal) signals.push(params.signal);
53112
+ try {
53113
+ response = await fetch(url2, {
53114
+ method: "POST",
53115
+ headers: headers2(config2),
53116
+ body: JSON.stringify(body),
53117
+ signal: signals.length === 1 ? signals[0] : AbortSignal.any(signals)
53118
+ });
53119
+ } catch (err) {
53120
+ lastStatus = 0;
53121
+ lastErrText = err instanceof Error ? err.message : String(err);
53122
+ if (params.signal?.aborted) {
53123
+ yield { kind: "error", message: "aborted" };
53124
+ return;
53125
+ }
53126
+ if (attempt < MAX_RETRIES2) {
53127
+ await abortableSleep2(backoffDelay2(attempt, null), params.signal);
53128
+ continue;
53129
+ }
53130
+ yield { kind: "error", message: `Network error: ${lastErrText}` };
53131
+ return;
53132
+ } finally {
53133
+ clearTimeout(connectTimer);
53134
+ }
53135
+ if (response.ok && response.body) break;
53136
+ lastStatus = response.status;
53137
+ lastErrText = await response.text().catch(() => "");
53138
+ if (!RETRYABLE_STATUSES2.has(response.status) || attempt >= MAX_RETRIES2) break;
53139
+ await abortableSleep2(backoffDelay2(attempt, response.headers.get("retry-after")), params.signal);
53140
+ if (params.signal?.aborted) {
53141
+ yield { kind: "error", message: "aborted" };
53142
+ return;
53143
+ }
53144
+ }
53145
+ if (!response || !response.ok || !response.body) {
53146
+ const msg = lastStatus === 0 ? `Network error: ${lastErrText}` : `HTTP ${lastStatus}: ${lastErrText.slice(0, 240)}`;
53147
+ yield { kind: "error", message: msg };
53148
+ return;
53149
+ }
53150
+ const reader = response.body.getReader();
53151
+ const decoder = new TextDecoder();
53152
+ let buffer = "";
53153
+ const tools = /* @__PURE__ */ new Map();
53154
+ let emittedTool = false;
53155
+ const flush = function* (id3) {
53156
+ const t = tools.get(id3);
53157
+ if (!t?.name) return;
53158
+ let args = {};
53159
+ try {
53160
+ args = JSON.parse(t.argsJson || "{}");
53161
+ } catch {
53162
+ args = {};
53163
+ }
53164
+ tools.delete(id3);
53165
+ emittedTool = true;
53166
+ yield { kind: "tool_call", toolCallId: t.id, toolName: t.name, args };
53167
+ };
53168
+ const streamStartedAt = Date.now();
53169
+ let lastUsefulAt = streamStartedAt;
53170
+ const streamDeadline = streamStartedAt + PROVIDER_STREAM_MAX_MS;
53171
+ try {
53172
+ while (true) {
53173
+ const { value, done } = await readChunkWithTimeout(reader, {
53174
+ idleMs: PROVIDER_STREAM_IDLE_MS,
53175
+ deadlineMs: streamDeadline,
53176
+ signal: params.signal,
53177
+ lastUsefulAt: () => lastUsefulAt
53178
+ });
53179
+ if (done) break;
53180
+ buffer += decoder.decode(value, { stream: true });
53181
+ const lines = buffer.split("\n");
53182
+ buffer = lines.pop() ?? "";
53183
+ for (const line of lines) {
53184
+ const trimmed = line.trim();
53185
+ if (!trimmed.startsWith("data:")) continue;
53186
+ const data = trimmed.slice(5).trim();
53187
+ if (!data || data === "[DONE]") continue;
53188
+ let ev;
53189
+ try {
53190
+ ev = JSON.parse(data);
53191
+ } catch {
53192
+ continue;
53193
+ }
53194
+ const type = typeof ev.type === "string" ? ev.type : "";
53195
+ if (type) lastUsefulAt = Date.now();
53196
+ if (type === "response.output_text.delta" && typeof ev.delta === "string") {
53197
+ yield { kind: "text", delta: ev.delta };
53198
+ } else if (type === "response.reasoning_text.delta" && typeof ev.delta === "string") {
53199
+ yield { kind: "thinking", delta: ev.delta };
53200
+ } else if (type === "response.output_item.added") {
53201
+ const item = ev.item;
53202
+ if (item?.type === "function_call") {
53203
+ const id3 = String(item.call_id ?? item.id ?? `fc-${tools.size}`);
53204
+ tools.set(id3, {
53205
+ id: id3,
53206
+ name: typeof item.name === "string" ? item.name : "",
53207
+ argsJson: typeof item.arguments === "string" ? item.arguments : ""
53208
+ });
53209
+ }
53210
+ } else if (type === "response.function_call_arguments.delta") {
53211
+ const itemId = String(ev.item_id ?? ev.call_id ?? "");
53212
+ const existing = itemId ? tools.get(itemId) : [...tools.values()].at(-1);
53213
+ if (existing && typeof ev.delta === "string") existing.argsJson += ev.delta;
53214
+ } else if (type === "response.output_item.done") {
53215
+ const item = ev.item;
53216
+ if (item?.type === "function_call") {
53217
+ const id3 = String(item.call_id ?? item.id ?? "");
53218
+ if (id3) yield* flush(id3);
53219
+ }
53220
+ } else if (type === "response.completed") {
53221
+ const usage = ev.response?.usage;
53222
+ if (usage) {
53223
+ yield {
53224
+ kind: "usage",
53225
+ usage: {
53226
+ promptTokens: usage.input_tokens ?? usage.prompt_tokens ?? 0,
53227
+ completionTokens: usage.output_tokens ?? usage.completion_tokens ?? 0,
53228
+ totalTokens: usage.total_tokens ?? (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0)
53229
+ }
53230
+ };
53231
+ }
53232
+ yield { kind: "finish", reason: emittedTool ? "tool_calls" : "stop" };
53233
+ return;
53234
+ } else if (type === "response.failed" || type === "error") {
53235
+ const msg = typeof ev.message === "string" ? ev.message : JSON.stringify(ev.error ?? ev).slice(0, 200);
53236
+ yield { kind: "error", message: msg };
53237
+ return;
53238
+ }
53239
+ }
53240
+ }
53241
+ for (const id3 of [...tools.keys()]) yield* flush(id3);
53242
+ yield { kind: "finish", reason: emittedTool ? "tool_calls" : "stop" };
53243
+ } finally {
53244
+ reader.releaseLock();
53245
+ }
53246
+ };
53247
+ }
53248
+ var RETRYABLE_STATUSES2, MAX_RETRIES2, BACKOFF_BASE_MS2, BACKOFF_CAP_MS2;
53249
+ var init_responsesApi = __esm({
53250
+ "src/cli/provider/responsesApi.ts"() {
53251
+ "use strict";
53252
+ init_openai_compatible();
53253
+ init_chatgpt();
53254
+ init_thinking();
53255
+ init_capabilities();
53256
+ RETRYABLE_STATUSES2 = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
53257
+ MAX_RETRIES2 = (() => {
53258
+ const raw = process.env.ZELARI_PROVIDER_MAX_RETRIES;
53259
+ const n = raw ? Number.parseInt(raw, 10) : 3;
53260
+ return Number.isFinite(n) && n >= 0 ? n : 3;
53261
+ })();
53262
+ BACKOFF_BASE_MS2 = 500;
53263
+ BACKOFF_CAP_MS2 = 8e3;
53264
+ }
53265
+ });
53266
+
52961
53267
  // src/cli/provider/resolveStream.ts
52962
53268
  var resolveStream_exports = {};
52963
53269
  __export(resolveStream_exports, {
@@ -52966,6 +53272,9 @@ __export(resolveStream_exports, {
52966
53272
  function buildProviderStream(config2) {
52967
53273
  if (config2.providerId === "anthropic") return anthropicMessagesProvider(config2);
52968
53274
  if (config2.providerId === "chatgpt") return chatgptResponsesProvider(config2);
53275
+ if (getApiStyleFor(config2.providerId) === "responses") {
53276
+ return responsesApiProvider(config2);
53277
+ }
52969
53278
  return openaiCompatibleProvider(config2);
52970
53279
  }
52971
53280
  var init_resolveStream = __esm({
@@ -52974,6 +53283,8 @@ var init_resolveStream = __esm({
52974
53283
  init_openai_compatible();
52975
53284
  init_anthropic();
52976
53285
  init_chatgpt();
53286
+ init_responsesApi();
53287
+ init_providerConfig();
52977
53288
  }
52978
53289
  });
52979
53290
 
@@ -60440,11 +60751,11 @@ var init_httpTransport = __esm({
60440
60751
  const sid = this.sessionId;
60441
60752
  this.sessionId = null;
60442
60753
  if (!sid) return;
60443
- const headers2 = {
60754
+ const headers3 = {
60444
60755
  ...this.opts.headers ?? {},
60445
60756
  "mcp-session-id": sid
60446
60757
  };
60447
- void fetch(this.opts.url, { method: "DELETE", headers: headers2 }).catch(() => {
60758
+ void fetch(this.opts.url, { method: "DELETE", headers: headers3 }).catch(() => {
60448
60759
  });
60449
60760
  }
60450
60761
  // ── internals ────────────────────────────────────────────────────────
@@ -60454,15 +60765,15 @@ var init_httpTransport = __esm({
60454
60765
  const timer = setTimeout(() => ac.abort(), timeoutMs2 + ABORT_GRACE_MS);
60455
60766
  const hadSession = this.sessionId !== null;
60456
60767
  try {
60457
- const headers2 = {
60768
+ const headers3 = {
60458
60769
  "content-type": "application/json",
60459
60770
  accept: "application/json, text/event-stream",
60460
60771
  ...this.opts.headers ?? {}
60461
60772
  };
60462
- if (this.sessionId) headers2["mcp-session-id"] = this.sessionId;
60773
+ if (this.sessionId) headers3["mcp-session-id"] = this.sessionId;
60463
60774
  const res = await fetch(this.opts.url, {
60464
60775
  method: "POST",
60465
- headers: headers2,
60776
+ headers: headers3,
60466
60777
  signal: ac.signal,
60467
60778
  body: JSON.stringify({ jsonrpc: "2.0", ...msg })
60468
60779
  });
@@ -65494,6 +65805,16 @@ function handleProviderPicker(ctx, openPicker) {
65494
65805
  function handleProviderCustom(ctx, opts) {
65495
65806
  const id3 = ctx.activeProviderSpec.id;
65496
65807
  try {
65808
+ if (opts.apiStyle) {
65809
+ if (id3 === "chatgpt" || id3 === "anthropic") {
65810
+ appendSystem(ctx.setMessages, `[provider] ${id3} has a fixed transport \u2014 nothing to select.`);
65811
+ return;
65812
+ }
65813
+ setApiStyleFor(id3, opts.apiStyle);
65814
+ const target = opts.apiStyle === "responses" ? "POST /responses" : "POST /chat/completions";
65815
+ appendSystem(ctx.setMessages, `[provider] ${id3} endpoint style set to ${opts.apiStyle} (${target})`);
65816
+ return;
65817
+ }
65497
65818
  if (opts.clear) {
65498
65819
  clearCustomEndpoint(id3);
65499
65820
  appendSystem(ctx.setMessages, `[provider] cleared custom endpoint for ${id3} \u2014 falling back to default`);
@@ -70095,6 +70416,7 @@ function parseSetConfigFlags(argv) {
70095
70416
  let model;
70096
70417
  let endpoint;
70097
70418
  let thinking;
70419
+ let apiStyle;
70098
70420
  let endpointClear = false;
70099
70421
  let verifierProvider;
70100
70422
  let verifierModel;
@@ -70113,6 +70435,9 @@ function parseSetConfigFlags(argv) {
70113
70435
  } else if (arg === "--thinking") {
70114
70436
  thinking = argv[i + 1];
70115
70437
  i++;
70438
+ } else if (arg === "--api-style") {
70439
+ apiStyle = argv[i + 1];
70440
+ i++;
70116
70441
  } else if (arg === "--endpoint-clear") {
70117
70442
  endpointClear = true;
70118
70443
  } else if (arg === "--verifier-provider") {
@@ -70125,7 +70450,7 @@ function parseSetConfigFlags(argv) {
70125
70450
  verifierClear = true;
70126
70451
  }
70127
70452
  }
70128
- if (!provider && !model && !endpoint && !endpointClear && !thinking && !verifierProvider && !verifierModel && !verifierClear) {
70453
+ if (!provider && !model && !endpoint && !endpointClear && !thinking && !apiStyle && !verifierProvider && !verifierModel && !verifierClear) {
70129
70454
  return {
70130
70455
  request: null,
70131
70456
  error: "--set-config: nothing to update \u2014 provide at least one of --provider, --model, --endpoint, --thinking, --verifier-provider + --verifier-model, --verifier-clear, or --endpoint-clear"
@@ -70140,6 +70465,9 @@ function parseSetConfigFlags(argv) {
70140
70465
  if (endpoint !== void 0 && endpoint.trim().length === 0) {
70141
70466
  return { request: null, error: "--endpoint cannot be empty" };
70142
70467
  }
70468
+ if (apiStyle !== void 0 && apiStyle !== "chat" && apiStyle !== "responses") {
70469
+ return { request: null, error: "invalid --api-style " + apiStyle + " (use chat or responses)" };
70470
+ }
70143
70471
  if (verifierClear && (verifierProvider || verifierModel)) {
70144
70472
  return { request: null, error: "--verifier-clear conflicts with --verifier-provider/--verifier-model" };
70145
70473
  }
@@ -70164,6 +70492,7 @@ function parseSetConfigFlags(argv) {
70164
70492
  model: model?.trim(),
70165
70493
  endpoint: endpoint?.trim(),
70166
70494
  endpointClear: endpointClear || void 0,
70495
+ apiStyle,
70167
70496
  thinking: thinking?.trim().toLowerCase(),
70168
70497
  verifierProvider: verifierProvider?.trim(),
70169
70498
  verifierModel: verifierModel?.trim(),
@@ -70243,6 +70572,7 @@ function buildDesktopConfigSnapshot() {
70243
70572
  models,
70244
70573
  defaultModel,
70245
70574
  endpoint: custom2 ?? null,
70575
+ apiStyle: p3.id === "anthropic" || p3.id === "chatgpt" ? void 0 : getApiStyleFor(p3.id),
70246
70576
  baseUrl: custom2 ?? builtin,
70247
70577
  authKind: !hasKey ? "none" : oauth ? "oauth" : "api_key",
70248
70578
  expiresAt: stored?.expiresAt ?? null,
@@ -70289,6 +70619,9 @@ function applySetConfig(req) {
70289
70619
  if (req.endpoint) {
70290
70620
  setCustomEndpoint(targetProvider, req.endpoint);
70291
70621
  }
70622
+ if (req.apiStyle) {
70623
+ setApiStyleFor(targetProvider, req.apiStyle);
70624
+ }
70292
70625
  if (req.model) {
70293
70626
  setModelForProvider(targetProvider, req.model);
70294
70627
  }
@@ -79657,6 +79990,24 @@ ${formatSkillList(availableSkills)}`
79657
79990
  customEndpoint: url2
79658
79991
  };
79659
79992
  }
79993
+ if (subcommand === "api") {
79994
+ const target = args[1];
79995
+ if (!target || target === "show") {
79996
+ return {
79997
+ handled: true,
79998
+ kind: "provider_custom",
79999
+ message: "Usage: /provider api chat \u2014 POST /chat/completions (default)\n /provider api responses \u2014 POST /responses (OpenAI Responses API)\nApplies to the active provider; chatgpt/anthropic have a fixed transport."
80000
+ };
80001
+ }
80002
+ if (target !== "chat" && target !== "responses") {
80003
+ return {
80004
+ handled: true,
80005
+ kind: "provider_custom",
80006
+ message: `[provider] unknown api style: ${target}. Use: chat | responses`
80007
+ };
80008
+ }
80009
+ return { handled: true, kind: "provider_custom", apiStyle: target };
80010
+ }
79660
80011
  const providerId = subcommand;
79661
80012
  const sub = args[1];
79662
80013
  if (sub === "refresh") {
@@ -81013,27 +81364,27 @@ function parseCsv(text) {
81013
81364
  records.pop();
81014
81365
  }
81015
81366
  if (records.length === 0) return { headers: [], rows: [] };
81016
- const headers2 = records[0];
81367
+ const headers3 = records[0];
81017
81368
  const rows = records.slice(1).map((r) => {
81018
81369
  const obj = {};
81019
- for (let i = 0; i < headers2.length; i++) obj[headers2[i]] = r[i] ?? "";
81370
+ for (let i = 0; i < headers3.length; i++) obj[headers3[i]] = r[i] ?? "";
81020
81371
  return obj;
81021
81372
  });
81022
- return { headers: headers2, rows };
81373
+ return { headers: headers3, rows };
81023
81374
  }
81024
81375
  function applyTemplate(template, row) {
81025
81376
  return template.replace(/\{([a-zA-Z_][\w-]*)\}/g, (_, k) => row[k] ?? "");
81026
81377
  }
81027
- function serializeCsv(headers2, rows) {
81378
+ function serializeCsv(headers3, rows) {
81028
81379
  const escape = (v) => {
81029
81380
  if (v.includes(",") || v.includes("\n") || v.includes('"')) {
81030
81381
  return `"${v.replace(/"/g, '""')}"`;
81031
81382
  }
81032
81383
  return v;
81033
81384
  };
81034
- const out = [headers2.map(escape).join(",")];
81385
+ const out = [headers3.map(escape).join(",")];
81035
81386
  for (const row of rows) {
81036
- out.push(headers2.map((h) => escape(row[h] ?? "")).join(","));
81387
+ out.push(headers3.map((h) => escape(row[h] ?? "")).join(","));
81037
81388
  }
81038
81389
  return out.join("\n") + "\n";
81039
81390
  }
@@ -81047,21 +81398,21 @@ async function runCsvFanout(args, deps, opts) {
81047
81398
  const start = Date.now();
81048
81399
  const absCsv = path80.isAbsolute(args.csv_path) ? args.csv_path : path80.join(opts.parentCwd, args.csv_path);
81049
81400
  const absOut = path80.isAbsolute(args.output_csv_path) ? args.output_csv_path : path80.join(opts.parentCwd, args.output_csv_path);
81050
- const { headers: headers2, rows } = await readCsv(absCsv);
81051
- if (headers2.length === 0) {
81401
+ const { headers: headers3, rows } = await readCsv(absCsv);
81402
+ if (headers3.length === 0) {
81052
81403
  throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
81053
81404
  }
81054
81405
  if (!args.id_column) {
81055
81406
  throw new Error("kraken_csv_fanout: id_column is required");
81056
81407
  }
81057
- if (!headers2.includes(args.id_column)) {
81058
- throw new Error(`kraken_csv_fanout: id_column "${args.id_column}" not in CSV header [${headers2.join(", ")}]`);
81408
+ if (!headers3.includes(args.id_column)) {
81409
+ throw new Error(`kraken_csv_fanout: id_column "${args.id_column}" not in CSV header [${headers3.join(", ")}]`);
81059
81410
  }
81060
81411
  const concurrency = args.max_concurrency ?? resolveMaxConcurrency();
81061
81412
  opts.onLog?.(`csv fanout: ${rows.length} rows \xD7 ${args.agent_kind} @ concurrency=${concurrency}`);
81062
81413
  const perRowMs = args.max_runtime_seconds !== void 0 ? args.max_runtime_seconds * 1e3 : args.agent_kind === "general" ? TASK_TOOL_TIMEOUT_MS : 3e5;
81063
81414
  const outputRecords = rows.map((r) => ({ ...r, status: "pending", result: "", error: "" }));
81064
- const outHeaders = [...headers2, "status", "result", "error"];
81415
+ const outHeaders = [...headers3, "status", "result", "error"];
81065
81416
  let writeChain2 = Promise.resolve();
81066
81417
  function queueWrite(contents) {
81067
81418
  const next = writeChain2.then(() => atomicWrite(absOut, contents));
@@ -82422,6 +82773,7 @@ function useSlashDispatch(params) {
82422
82773
  handleProviderCustom(providerCtx, {
82423
82774
  endpoint: result.customEndpoint,
82424
82775
  clear: result.customClear,
82776
+ apiStyle: result.apiStyle,
82425
82777
  message: result.message
82426
82778
  });
82427
82779
  setInput("");