atlarix 15.8.2 → 15.9.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.
@@ -9587,6 +9587,10 @@ var init_schema = __esm({
9587
9587
  name: text("name").notNull(),
9588
9588
  api_base_url: text("api_base_url").notNull(),
9589
9589
  env_var_name: text("env_var_name"),
9590
+ /** Wire format: null / "openai" = OpenAI-compatible, "anthropic" = Messages API. */
9591
+ api_format: text("api_format"),
9592
+ /** How the key is sent: null = the format's usual header, or "x-api-key" / "bearer". */
9593
+ auth_header: text("auth_header"),
9590
9594
  createdAt: integer("created_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`),
9591
9595
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`)
9592
9596
  }
@@ -10911,6 +10915,14 @@ var init_usage_stats = __esm({
10911
10915
  }
10912
10916
  });
10913
10917
 
10918
+ // raw-file:/home/runner/work/Atlarix/Atlarix/drizzle/0080_custom_provider_api_format.sql
10919
+ var custom_provider_api_format_default;
10920
+ var init_custom_provider_api_format = __esm({
10921
+ "raw-file:/home/runner/work/Atlarix/Atlarix/drizzle/0080_custom_provider_api_format.sql"() {
10922
+ custom_provider_api_format_default = "-- The wire format a provider the user added speaks, and how it takes the key.\n--\n-- A custom provider could only be OpenAI-compatible. Many endpoints serve the\n-- Anthropic Messages API instead (Bedrock's /anthropic, gateways, proxies), and\n-- those take the key either as `x-api-key` or as `Authorization: Bearer`.\n--\n-- Additive only (two nullable columns, null = OpenAI-compatible with its usual\n-- Bearer key): an older reader ignores them, so min_reader stays where it is.\nALTER TABLE `language_model_providers` ADD `api_format` text;--> statement-breakpoint\nALTER TABLE `language_model_providers` ADD `auth_header` text;\n";
10923
+ }
10924
+ });
10925
+
10914
10926
  // src/db/migrations/bundled.generated.ts
10915
10927
  var BUNDLED_MIGRATIONS, DRIZZLE_HASH_TO_NAME;
10916
10928
  var init_bundled_generated = __esm({
@@ -10995,6 +11007,7 @@ var init_bundled_generated = __esm({
10995
11007
  init_automation_leases();
10996
11008
  init_mcp_headers();
10997
11009
  init_usage_stats();
11010
+ init_custom_provider_api_format();
10998
11011
  BUNDLED_MIGRATIONS = [
10999
11012
  { name: "0000_nebulous_proemial_gods", sql: nebulous_proemial_gods_default, drizzleHash: "2fd0348e72ac1429b9246311e8db17a34078771470c622a13ab09afdee0a0d31", when: 1744692127560 },
11000
11013
  { name: "0001_hesitant_roland_deschain", sql: hesitant_roland_deschain_default, drizzleHash: "b9b37284db688cf3aaaa2719dda788238fd216933d3756c0482f26b5343d0059", when: 1744999922420 },
@@ -11075,7 +11088,8 @@ var init_bundled_generated = __esm({
11075
11088
  { name: "0076_drop_installed_skills", sql: drop_installed_skills_default, drizzleHash: "b361a2b664ce74a3efeaa2f73cdf6de582509c6569a1aab54febbef507b98b3c", when: 17877024e5 },
11076
11089
  { name: "0077_automation_leases", sql: automation_leases_default, drizzleHash: "22f09427b49eb0fcdd2b62cd472735a88e5dd60974af0d0c008f6e6fddfdf545", when: 17877888e5 },
11077
11090
  { name: "0078_mcp_headers", sql: mcp_headers_default, drizzleHash: "2e2b040eac31e336e8e7662555fd8526e8cdfd59929671e9ca6e063a3f2d4004", when: 17878752e5 },
11078
- { name: "0079_usage_stats", sql: usage_stats_default, drizzleHash: "8ea9653e8339a4f4c4f0fb140766daace073443ed9e6efae3d4597b2fc6bd60d", when: 17879616e5 }
11091
+ { name: "0079_usage_stats", sql: usage_stats_default, drizzleHash: "8ea9653e8339a4f4c4f0fb140766daace073443ed9e6efae3d4597b2fc6bd60d", when: 17879616e5 },
11092
+ { name: "0080_custom_provider_api_format", sql: custom_provider_api_format_default, drizzleHash: "0c66aed1a57dea6a1e9925a9a74dc1ca4f181db3de0c8e446a860a9f3ef7b950", when: 1788048e6 }
11079
11093
  ];
11080
11094
  DRIZZLE_HASH_TO_NAME = {
11081
11095
  "2fd0348e72ac1429b9246311e8db17a34078771470c622a13ab09afdee0a0d31": "0000_nebulous_proemial_gods",
@@ -11157,7 +11171,8 @@ var init_bundled_generated = __esm({
11157
11171
  "b361a2b664ce74a3efeaa2f73cdf6de582509c6569a1aab54febbef507b98b3c": "0076_drop_installed_skills",
11158
11172
  "22f09427b49eb0fcdd2b62cd472735a88e5dd60974af0d0c008f6e6fddfdf545": "0077_automation_leases",
11159
11173
  "2e2b040eac31e336e8e7662555fd8526e8cdfd59929671e9ca6e063a3f2d4004": "0078_mcp_headers",
11160
- "8ea9653e8339a4f4c4f0fb140766daace073443ed9e6efae3d4597b2fc6bd60d": "0079_usage_stats"
11174
+ "8ea9653e8339a4f4c4f0fb140766daace073443ed9e6efae3d4597b2fc6bd60d": "0079_usage_stats",
11175
+ "0c66aed1a57dea6a1e9925a9a74dc1ca4f181db3de0c8e446a860a9f3ef7b950": "0080_custom_provider_api_format"
11161
11176
  };
11162
11177
  }
11163
11178
  });
@@ -31811,7 +31826,26 @@ function renderModeSystemBlock(activeMode) {
31811
31826
  lines.push("</mode_system>");
31812
31827
  return lines.join("\n");
31813
31828
  }
31814
- async function buildSystemPromptWithProjectInstructions(params) {
31829
+ function joinSystemPromptParts({ stableHeader, dynamicTail }) {
31830
+ return `${stableHeader}
31831
+
31832
+ ${dynamicTail}
31833
+ `;
31834
+ }
31835
+ function splitDynamicTail(systemPrompt, dynamicTail) {
31836
+ if (!dynamicTail?.trim()) return systemPrompt;
31837
+ const joined = `
31838
+
31839
+ ${dynamicTail}
31840
+ `;
31841
+ const at2 = systemPrompt.indexOf(joined);
31842
+ if (at2 < 0) return systemPrompt;
31843
+ return {
31844
+ systemPromptOverride: systemPrompt.slice(0, at2) + systemPrompt.slice(at2 + joined.length),
31845
+ dynamicSystemTail: dynamicTail
31846
+ };
31847
+ }
31848
+ async function buildSystemPromptPartsWithProjectInstructions(params) {
31815
31849
  const {
31816
31850
  workspacePath,
31817
31851
  workspacePromptContent,
@@ -31840,7 +31874,7 @@ async function buildSystemPromptWithProjectInstructions(params) {
31840
31874
  buildInstructionsFromLayers(layers),
31841
31875
  projectDocsHint
31842
31876
  ].filter((s3) => s3.trim().length > 0).join("\n\n");
31843
- return constructSystemPrompt({
31877
+ return constructSystemPromptParts({
31844
31878
  contextSize,
31845
31879
  chatMode,
31846
31880
  effectiveWorkMode,
@@ -31866,7 +31900,7 @@ async function buildSystemPromptWithProjectInstructions(params) {
31866
31900
  registeredToolNames
31867
31901
  });
31868
31902
  }
31869
- var import_electron_log10, logger8, cachedPromptShell, AGENTS_MD, loadAgentsFile, FALLBACK_AGENTS, getAgentsMdForContextSize, getAgentsMdForWorkMode, MODE_SYSTEM_DESCRIPTIONS, constructSystemPromptParts, constructSystemPrompt;
31903
+ var import_electron_log10, logger8, cachedPromptShell, AGENTS_MD, loadAgentsFile, FALLBACK_AGENTS, getAgentsMdForContextSize, getAgentsMdForWorkMode, MODE_SYSTEM_DESCRIPTIONS, constructSystemPromptParts;
31870
31904
  var init_system_prompt = __esm({
31871
31905
  "src/prompts/system_prompt.ts"() {
31872
31906
  import_electron_log10 = __toESM(require_src());
@@ -32047,13 +32081,6 @@ When you need clarification or a decision from the user (architecture choices, d
32047
32081
  }
32048
32082
  return { stableHeader, dynamicTail: tail };
32049
32083
  };
32050
- constructSystemPrompt = (params) => {
32051
- const { stableHeader, dynamicTail } = constructSystemPromptParts(params);
32052
- return `${stableHeader}
32053
-
32054
- ${dynamicTail}
32055
- `;
32056
- };
32057
32084
  }
32058
32085
  });
32059
32086
 
@@ -67768,7 +67795,11 @@ var init_provider_sdk_factories = __esm({
67768
67795
  const provider = createOpenAI({ apiKey, ...baseURL ? { baseURL } : {} });
67769
67796
  return baseURL ? provider(modelId) : provider.responses(modelId);
67770
67797
  },
67771
- "@ai-sdk/anthropic": ({ modelId, apiKey, baseURL }) => createAnthropic({ apiKey, ...baseURL ? { baseURL } : {} })(modelId),
67798
+ // Exactly one of apiKey / authToken: the SDK refuses both.
67799
+ "@ai-sdk/anthropic": ({ modelId, apiKey, baseURL, authHeader }) => createAnthropic({
67800
+ ...authHeader === "bearer" ? { authToken: apiKey } : { apiKey },
67801
+ ...baseURL ? { baseURL } : {}
67802
+ })(modelId),
67772
67803
  "@ai-sdk/google": ({ modelId, apiKey, baseURL }) => createGoogle({ apiKey, ...baseURL ? { baseURL } : {} })(modelId),
67773
67804
  "@ai-sdk/xai": ({ modelId, apiKey, baseURL }) => createXai({ apiKey, ...baseURL ? { baseURL } : {} })(modelId)
67774
67805
  };
@@ -68427,7 +68458,7 @@ var init_model_reasoning_profile = __esm({
68427
68458
  temperatureById = /* @__PURE__ */ new Map();
68428
68459
  effortOverrides = /* @__PURE__ */ new Map();
68429
68460
  BUDGET_LEVELS = ["low", "medium", "high"];
68430
- BUDGET_HINT = { low: "medium", medium: "high", high: "max" };
68461
+ BUDGET_HINT = { low: "low", medium: "medium", high: "max" };
68431
68462
  NO_REASONING = /* @__PURE__ */ new Set(["none", "minimal"]);
68432
68463
  AUTO_EFFORT_LEVELS = ["low", "medium", "max"];
68433
68464
  AUTO_EFFORT_CHOICES = { kind: "effort", levels: [...AUTO_EFFORT_LEVELS], defaultLevel: "medium" };
@@ -68963,6 +68994,18 @@ var init_models_dev = __esm({
68963
68994
  }
68964
68995
  });
68965
68996
 
68997
+ // src/lib/provider-key-check.ts
68998
+ function anthropicHeaders(apiKey, authHeader) {
68999
+ return {
69000
+ ...authHeader === "bearer" ? { Authorization: `Bearer ${apiKey}` } : { "x-api-key": apiKey },
69001
+ "anthropic-version": "2023-06-01"
69002
+ };
69003
+ }
69004
+ var init_provider_key_check = __esm({
69005
+ "src/lib/provider-key-check.ts"() {
69006
+ }
69007
+ });
69008
+
68966
69009
  // node_modules/isexe/windows.js
68967
69010
  var require_windows = __commonJS({
68968
69011
  "node_modules/isexe/windows.js"(exports2, module2) {
@@ -71123,21 +71166,22 @@ async function fetchModelsFromOpenAI(apiKey) {
71123
71166
  return [];
71124
71167
  }
71125
71168
  }
71126
- async function fetchModelsFromAnthropic(apiKey) {
71127
- const cached4 = fromCache("anthropic");
71169
+ async function fetchModelsFromAnthropic(apiKey, endpoint2) {
71170
+ const cacheId = endpoint2?.providerId ?? "anthropic";
71171
+ const label = endpoint2 ? endpoint2.providerId : "Anthropic";
71172
+ const cached4 = fromCache(cacheId);
71128
71173
  if (cached4) return cached4;
71129
71174
  try {
71130
71175
  const items = [];
71131
71176
  let after2;
71132
71177
  for (let page = 0; page < MAX_PAGES; page++) {
71133
- const url2 = new URL("https://api.anthropic.com/v1/models");
71178
+ const url2 = new URL(
71179
+ endpoint2 ? `${endpoint2.baseUrl.replace(/\/+$/, "")}/models` : "https://api.anthropic.com/v1/models"
71180
+ );
71134
71181
  url2.searchParams.set("limit", "1000");
71135
71182
  if (after2) url2.searchParams.set("after_id", after2);
71136
71183
  const res = await fetch(url2, {
71137
- headers: {
71138
- "x-api-key": apiKey,
71139
- "anthropic-version": "2023-06-01"
71140
- },
71184
+ headers: anthropicHeaders(apiKey, endpoint2?.authHeader),
71141
71185
  signal: AbortSignal.timeout(15e3)
71142
71186
  });
71143
71187
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
@@ -71149,18 +71193,18 @@ async function fetchModelsFromAnthropic(apiKey) {
71149
71193
  const models = items.map((m3) => ({
71150
71194
  apiName: m3.id,
71151
71195
  displayName: m3.display_name ?? m3.id,
71152
- description: "Claude model with tool support",
71196
+ description: endpoint2 ? "Chat model with tool support" : "Claude model with tool support",
71153
71197
  ...m3.max_input_tokens ? { contextWindow: m3.max_input_tokens } : {},
71154
71198
  ...m3.max_tokens ? { maxOutputTokens: m3.max_tokens } : {},
71155
71199
  type: "cloud",
71156
71200
  supportsToolChoice: true
71157
71201
  }));
71158
71202
  sortModels(models);
71159
- toCache("anthropic", models);
71160
- logger10.info(`[Anthropic] Fetched ${models.length} models`);
71203
+ toCache(cacheId, models);
71204
+ logger10.info(`[${label}] Fetched ${models.length} models`);
71161
71205
  return models;
71162
71206
  } catch (e3) {
71163
- logger10.warn("[Anthropic] Fetch failed", e3);
71207
+ logger10.warn(`[${label}] Fetch failed`, e3);
71164
71208
  return [];
71165
71209
  }
71166
71210
  }
@@ -71363,7 +71407,14 @@ async function fetchModelsFromOpenRouter(apiKey) {
71363
71407
  return [];
71364
71408
  }
71365
71409
  }
71366
- async function fetchCustomProviderModels(providerId, baseUrl, apiKey) {
71410
+ async function fetchCustomProviderModels(providerId, baseUrl, apiKey, wire = {}) {
71411
+ if (wire.apiFormat === "anthropic") {
71412
+ return fetchModelsFromAnthropic(apiKey?.trim() || "none", {
71413
+ providerId,
71414
+ baseUrl,
71415
+ authHeader: wire.authHeader
71416
+ });
71417
+ }
71367
71418
  return fetchOpenAICompatibleModels({
71368
71419
  providerId,
71369
71420
  url: `${baseUrl.replace(/\/+$/, "")}/models`,
@@ -71434,6 +71485,7 @@ var init_provider_model_fetchers = __esm({
71434
71485
  import_electron_log12 = __toESM(require_src());
71435
71486
  init_openrouter_app_headers();
71436
71487
  init_language_model_constants();
71488
+ init_provider_key_check();
71437
71489
  logger10 = import_electron_log12.default.scope("provider_fetchers");
71438
71490
  CACHE_MS = 5 * 60 * 1e3;
71439
71491
  cache = /* @__PURE__ */ new Map();
@@ -71560,6 +71612,29 @@ var init_provider_api_keys = __esm({
71560
71612
  }
71561
71613
  });
71562
71614
 
71615
+ // src/lib/routing/custom-provider-format.ts
71616
+ function rememberCustomProviderFormats(providers) {
71617
+ anthropicFormat.clear();
71618
+ for (const p2 of providers) {
71619
+ if (p2.apiFormat === "anthropic") anthropicFormat.add(p2.id);
71620
+ }
71621
+ }
71622
+ function isCustomAnthropicFormat(providerId) {
71623
+ return Boolean(providerId && anthropicFormat.has(providerId));
71624
+ }
71625
+ function anthropicFormatCapabilityId(providerId) {
71626
+ return isCustomAnthropicFormat(providerId) ? "anthropic" : providerId;
71627
+ }
71628
+ function anthropicCatalogModelId(modelId) {
71629
+ return modelId.trim().replace(/^(?:[a-z]{2,6}\.)?anthropic\./i, "").replace(/-v\d+(?::\d+)?$/i, "");
71630
+ }
71631
+ var anthropicFormat;
71632
+ var init_custom_provider_format = __esm({
71633
+ "src/lib/routing/custom-provider-format.ts"() {
71634
+ anthropicFormat = /* @__PURE__ */ new Set();
71635
+ }
71636
+ });
71637
+
71563
71638
  // src/ipc/utils/thinking_utils.ts
71564
71639
  function reasoningCeiling(providerId) {
71565
71640
  return providerId && PROVIDERS_THAT_SUPPORT_THINKING.includes(providerId) ? "max" : "high";
@@ -71583,7 +71658,7 @@ function reasoningBudgetTokens(effort, maxOutputTokens) {
71583
71658
  const max4 = Math.max(1, Math.floor(maxOutputTokens));
71584
71659
  const reserve = Math.max(Math.round(max4 * 0.15), 2048);
71585
71660
  const ceil = Math.max(1024, max4 - reserve);
71586
- const frac = effort === "max" ? 0.85 : effort === "xhigh" ? 0.7 : effort === "high" ? 0.5 : 0.33;
71661
+ const frac = effort === "max" ? 0.85 : effort === "xhigh" ? 0.7 : effort === "high" ? 0.5 : effort === "low" ? 0.15 : 0.33;
71587
71662
  return Math.min(ceil, Math.max(1024, Math.round(max4 * frac)));
71588
71663
  }
71589
71664
  function getExtraProviderOptions(providerId, settings, includeExtendedThinking = false, maxOutputTokens, modelId) {
@@ -71667,6 +71742,9 @@ function resolveReasoningCatalogModelId(effectiveModel) {
71667
71742
  if (effectiveModel.provider === "compass") {
71668
71743
  return getCoreModelsDevTargetForTier(name29)?.modelId ?? getCoreModelIdForCoreTier(name29) ?? name29;
71669
71744
  }
71745
+ if (isCustomAnthropicFormat(effectiveModel.provider)) {
71746
+ return anthropicCatalogModelId(name29);
71747
+ }
71670
71748
  return name29;
71671
71749
  }
71672
71750
  function resolveMediaModelId(effectiveModel) {
@@ -71677,13 +71755,13 @@ function resolveMediaModelId(effectiveModel) {
71677
71755
  }
71678
71756
  function resolveCapabilityProviderId(effectiveModel) {
71679
71757
  const provider = (effectiveModel.provider ?? "").trim();
71680
- if (provider !== "compass") return provider;
71758
+ if (provider !== "compass") return anthropicFormatCapabilityId(provider);
71681
71759
  return getCoreModelsDevTargetForTier(effectiveModel.name ?? "")?.providerId ?? provider;
71682
71760
  }
71683
71761
  function detectReasoningCapability(modelApiId, builtinProviderId) {
71684
71762
  const id = modelApiId.trim();
71685
71763
  const lower2 = id.toLowerCase();
71686
- const pid = builtinProviderId ?? "";
71764
+ const pid = anthropicFormatCapabilityId(builtinProviderId) ?? "";
71687
71765
  let useOpenRouterReasoning = false;
71688
71766
  if (pid === "openrouter" || pid === "compass") {
71689
71767
  const row = id.includes("/") ? getCachedOpenRouterModelRow(id) : void 0;
@@ -71737,7 +71815,7 @@ function isReasoningCapableModel(model) {
71737
71815
  return offersAChannel(capabilityProvider) || offersAChannel(model.provider ?? "");
71738
71816
  }
71739
71817
  function resolveDispatchCapabilityProviderId(effectiveModel, builtinProviderId) {
71740
- if (builtinProviderId !== "compass") return builtinProviderId;
71818
+ if (builtinProviderId !== "compass") return anthropicFormatCapabilityId(builtinProviderId);
71741
71819
  if (effectiveModel.provider !== "compass") return builtinProviderId;
71742
71820
  return resolveCapabilityProviderId(effectiveModel);
71743
71821
  }
@@ -71833,6 +71911,7 @@ var init_reasoning_detector = __esm({
71833
71911
  init_model_capability_profile();
71834
71912
  init_language_model_constants();
71835
71913
  init_model_reasoning_profile();
71914
+ init_custom_provider_format();
71836
71915
  init_thinking_utils();
71837
71916
  GEMINI_LEVELS = /* @__PURE__ */ new Set(["minimal", "low", "medium", "high"]);
71838
71917
  }
@@ -72009,11 +72088,16 @@ async function getLanguageModelProviders() {
72009
72088
  name: cp.name,
72010
72089
  apiBaseUrl: cp.api_base_url,
72011
72090
  envVarName: cp.env_var_name ?? void 0,
72091
+ // The Anthropic format dispatches through the catalog's own Anthropic SDK
72092
+ // factory (provider-sdk-factories), like any models.dev provider on it.
72093
+ ...cp.api_format === "anthropic" ? { apiFormat: "anthropic", npm: ANTHROPIC_NPM } : {},
72094
+ ...cp.auth_header === "x-api-key" || cp.auth_header === "bearer" ? { authHeader: cp.auth_header } : {},
72012
72095
  type: "custom"
72013
72096
  // hasFreeTier, websiteUrl, gatewayPrefix are not in the custom DB schema
72014
72097
  // They will be undefined unless overridden by hardcoded values if IDs match
72015
72098
  });
72016
72099
  }
72100
+ rememberCustomProviderFormats([...customProvidersMap.values()]);
72017
72101
  const catalogById = /* @__PURE__ */ new Map();
72018
72102
  try {
72019
72103
  for (const c2 of await getModelsDevProviderCatalog())
@@ -72100,15 +72184,15 @@ function providerDisplayName(providerId) {
72100
72184
  if (!providerId) return void 0;
72101
72185
  return providerNames.get(providerId) ?? providerId.replace(/^custom::/, "");
72102
72186
  }
72103
- async function liveCustomModels(providerId, baseUrl, apiKey, now2 = Date.now()) {
72187
+ async function liveCustomModels(providerId, baseUrl, apiKey, now2 = Date.now(), wire = {}) {
72104
72188
  const { createHash: createHash10 } = await import("node:crypto");
72105
72189
  const keyTag = apiKey?.trim() ? createHash10("sha256").update(apiKey.trim()).digest("hex").slice(0, 12) : "none";
72106
- const cacheKey = `${providerId}|${baseUrl}|${keyTag}`;
72190
+ const cacheKey = `${providerId}|${baseUrl}|${keyTag}|${wire.apiFormat ?? "openai"}|${wire.authHeader ?? ""}`;
72107
72191
  const hit = liveCache.get(cacheKey);
72108
72192
  if (hit && now2 - hit.at < LIVE_TTL_MS) return hit.models;
72109
72193
  const { fetchCustomProviderModels: fetchCustomProviderModels2 } = await Promise.resolve().then(() => (init_provider_model_fetchers(), provider_model_fetchers_exports));
72110
72194
  const models = Promise.race([
72111
- fetchCustomProviderModels2(providerId, baseUrl, apiKey).catch(
72195
+ fetchCustomProviderModels2(providerId, baseUrl, apiKey, wire).catch(
72112
72196
  () => []
72113
72197
  ),
72114
72198
  new Promise(
@@ -72141,7 +72225,10 @@ async function getLanguageModels({
72141
72225
  maxOutputTokens: language_models.max_output_tokens,
72142
72226
  contextWindow: language_models.context_window
72143
72227
  }).from(language_models).where(
72144
- isCustomProvider({ providerId }) ? eq(language_models.customProviderId, providerId) : eq(language_models.builtinProviderId, providerId)
72228
+ // A provider from the providers table, whatever its id: the CLI's dev
72229
+ // endpoint is `atlarix-cli-dev`, with no `custom::`, and its model row
72230
+ // was looked up as a built-in's and never found (QA B3).
72231
+ provider.type === "custom" || isCustomProvider({ providerId }) ? eq(language_models.customProviderId, providerId) : eq(language_models.builtinProviderId, providerId)
72145
72232
  );
72146
72233
  customModels = customModelsDb.map((model) => ({
72147
72234
  ...model,
@@ -72202,7 +72289,10 @@ async function getLanguageModels({
72202
72289
  key = readSettings2().providerSettings?.[providerId]?.apiKey?.value;
72203
72290
  } catch {
72204
72291
  }
72205
- const listed = await liveCustomModels(providerId, provider.apiBaseUrl, key);
72292
+ const listed = await liveCustomModels(providerId, provider.apiBaseUrl, key, Date.now(), {
72293
+ apiFormat: provider.apiFormat,
72294
+ authHeader: provider.authHeader
72295
+ });
72206
72296
  const typed = new Set(customModels.map((m3) => m3.apiName));
72207
72297
  return [...customModels, ...listed.filter((m3) => !typed.has(m3.apiName))];
72208
72298
  }
@@ -72313,7 +72403,7 @@ function stampDeepThinkingSupport(modelsByProvider) {
72313
72403
  function isCustomProvider({ providerId }) {
72314
72404
  return providerId.startsWith(CUSTOM_PROVIDER_PREFIX);
72315
72405
  }
72316
- var providerNames, LIVE_TTL_MS, liveCache, CUSTOM_PROVIDER_PREFIX;
72406
+ var ANTHROPIC_NPM, providerNames, LIVE_TTL_MS, liveCache, CUSTOM_PROVIDER_PREFIX;
72317
72407
  var init_language_model_helpers = __esm({
72318
72408
  "src/ipc/shared/language_model_helpers.ts"() {
72319
72409
  init_db2();
@@ -72328,6 +72418,8 @@ var init_language_model_helpers = __esm({
72328
72418
  init_compass_constants();
72329
72419
  init_reasoning_detector();
72330
72420
  init_model_capability_profile();
72421
+ init_custom_provider_format();
72422
+ ANTHROPIC_NPM = "@ai-sdk/anthropic";
72331
72423
  providerNames = /* @__PURE__ */ new Map();
72332
72424
  LIVE_TTL_MS = 6e4;
72333
72425
  liveCache = /* @__PURE__ */ new Map();
@@ -95745,7 +95837,8 @@ async function resolveModelContextLimit(model, settings) {
95745
95837
  if (pid.startsWith("custom::")) {
95746
95838
  try {
95747
95839
  const { getLanguageModelProviders: getLanguageModelProviders2 } = await Promise.resolve().then(() => (init_language_model_helpers(), language_model_helpers_exports));
95748
- const base = (await getLanguageModelProviders2()).find((p2) => p2.id.toLowerCase() === pid)?.apiBaseUrl;
95840
+ const custom2 = (await getLanguageModelProviders2()).find((p2) => p2.id.toLowerCase() === pid);
95841
+ const base = custom2?.apiFormat === "anthropic" ? void 0 : custom2?.apiBaseUrl;
95749
95842
  if (base) {
95750
95843
  const { isOllamaEndpoint: isOllamaEndpoint2, ollamaOrigin: ollamaOrigin2, ollamaNumCtx: ollamaNumCtx2 } = await Promise.resolve().then(() => (init_ollama_native_fetch(), ollama_native_fetch_exports));
95751
95844
  if (await isOllamaEndpoint2(base)) {
@@ -95768,10 +95861,7 @@ async function resolveModelContextLimit(model, settings) {
95768
95861
  }
95769
95862
  try {
95770
95863
  const { getModelsDevLimits: getModelsDevLimits2 } = await Promise.resolve().then(() => (init_models_dev(), models_dev_exports));
95771
- const limits = await getModelsDevLimits2(
95772
- model.provider ?? "",
95773
- model.name ?? ""
95774
- );
95864
+ const limits = isCustomAnthropicFormat(model.provider) ? await getModelsDevLimits2("anthropic", anthropicCatalogModelId(model.name ?? "")) : await getModelsDevLimits2(model.provider ?? "", model.name ?? "");
95775
95865
  if (limits?.context && limits.context > 0) {
95776
95866
  return {
95777
95867
  tierLimit: floorSafe(limits.context * clamp),
@@ -95793,6 +95883,7 @@ var init_resolve_model_context_limit = __esm({
95793
95883
  init_compass_constants();
95794
95884
  init_findLanguageModel();
95795
95885
  import_electron_log16 = __toESM(require_src());
95886
+ init_custom_provider_format();
95796
95887
  logger14 = import_electron_log16.default.scope("context-limit");
95797
95888
  SAFETY_FACTOR = 0.8;
95798
95889
  localContextCache = /* @__PURE__ */ new Map();
@@ -215041,7 +215132,7 @@ async function getRegularModelClient(model, settings, providerConfig, options) {
215041
215132
  };
215042
215133
  }
215043
215134
  default: {
215044
- if (providerConfig.type === "custom" && providerConfig.apiBaseUrl) {
215135
+ if (providerConfig.type === "custom" && providerConfig.apiBaseUrl && providerConfig.apiFormat !== "anthropic") {
215045
215136
  const { isOllamaEndpoint: isOllamaEndpoint2, ollamaOrigin: ollamaOrigin2 } = await Promise.resolve().then(() => (init_ollama_native_fetch(), ollama_native_fetch_exports));
215046
215137
  if (await isOllamaEndpoint2(providerConfig.apiBaseUrl)) {
215047
215138
  return {
@@ -215061,7 +215152,8 @@ async function getRegularModelClient(model, settings, providerConfig, options) {
215061
215152
  modelId: model.name,
215062
215153
  apiKey,
215063
215154
  baseURL: providerConfig.apiBaseUrl,
215064
- npm: providerConfig.npm
215155
+ npm: providerConfig.npm,
215156
+ authHeader: providerConfig.authHeader
215065
215157
  }),
215066
215158
  builtinProviderId: providerConfig.id
215067
215159
  },
@@ -241873,6 +241965,7 @@ var init_dist21 = __esm({
241873
241965
  function resolveCacheMarkerPlacement(builtinProviderId, modelName) {
241874
241966
  if (builtinProviderId === "anthropic") return "message";
241875
241967
  if (!/claude|anthropic/i.test(modelName ?? "")) return "none";
241968
+ if (isCustomAnthropicFormat(builtinProviderId)) return "message";
241876
241969
  if (builtinProviderId === "bedrock" || builtinProviderId === "vertex") {
241877
241970
  return "message";
241878
241971
  }
@@ -241984,6 +242077,7 @@ function advanceCacheBreakpointsForStep(messages, placement) {
241984
242077
  var CACHE_MARKER, CACHE_MARKER_HEADER_1H, CACHE_KEY_BY_PROVIDER;
241985
242078
  var init_anthropic_cache_breakpoints = __esm({
241986
242079
  "src/lib/ai/anthropic-cache-breakpoints.ts"() {
242080
+ init_custom_provider_format();
241987
242081
  CACHE_MARKER = {
241988
242082
  anthropic: { cacheControl: { type: "ephemeral" } },
241989
242083
  openrouter: { cacheControl: { type: "ephemeral" } },
@@ -338396,7 +338490,8 @@ async function simpleStreamText(ctx, deps, {
338396
338490
  ...reasoningPatch.anthropic
338397
338491
  };
338398
338492
  }
338399
- const handledNatively = offByBudget || Boolean(reasoningPatch.openrouter) || Boolean(reasoningPatch.google) || Boolean(reasoningPatch.openai) || Boolean(reasoningPatch.anthropic);
338493
+ const handledNatively = offByBudget || // The Anthropic SDK reads only its own bucket, in its own shape.
338494
+ isCustomAnthropicFormat(modelClient.builtinProviderId) || Boolean(reasoningPatch.openrouter) || Boolean(reasoningPatch.google) || Boolean(reasoningPatch.openai) || Boolean(reasoningPatch.anthropic);
338400
338495
  if (modelClient.builtinProviderId && !handledNatively) {
338401
338496
  const compatibleReasoning = getExtraProviderOptions(
338402
338497
  capabilityProviderId,
@@ -338415,6 +338510,7 @@ async function simpleStreamText(ctx, deps, {
338415
338510
  }
338416
338511
  }
338417
338512
  const isAnthropic = modelClient.builtinProviderId === "anthropic";
338513
+ const speaksAnthropic = isAnthropic || isCustomAnthropicFormat(modelClient.builtinProviderId);
338418
338514
  const cacheMarkerPlacement = resolveCacheMarkerPlacement(
338419
338515
  modelClient.builtinProviderId,
338420
338516
  effectiveModel?.name
@@ -338486,7 +338582,7 @@ async function simpleStreamText(ctx, deps, {
338486
338582
  history,
338487
338583
  placement: cacheMarkerPlacement,
338488
338584
  // v13.3: header prefix survives >5min between-turn gaps.
338489
- headerTtl1h: isAnthropic
338585
+ headerTtl1h: speaksAnthropic
338490
338586
  });
338491
338587
  const completedStepMessages = [];
338492
338588
  const recordStepMessages = (msgs) => {
@@ -338935,6 +339031,7 @@ var init_simple_stream_text = __esm({
338935
339031
  init_perf_marks();
338936
339032
  init_byok_pricing();
338937
339033
  init_reasoning_detector();
339034
+ init_custom_provider_format();
338938
339035
  init_system_reminder();
338939
339036
  init_step_messages();
338940
339037
  init_texts();
@@ -343033,6 +343130,7 @@ async function runBuildBranch(ctx, deps) {
343033
343130
  modelContextResolved,
343034
343131
  placeholderAssistantMessage,
343035
343132
  systemPrompt,
343133
+ dynamicTail,
343036
343134
  taskLedgerBlock,
343037
343135
  simpleStreamTextDeps,
343038
343136
  streamTurnId,
@@ -344280,7 +344378,9 @@ async function runBuildBranch(ctx, deps) {
344280
344378
  escalationMode: effectiveWorkMode === "plan" ? "plan" : effectiveWorkMode === "build" ? "build" : "agent",
344281
344379
  tools,
344282
344380
  repetition: agenticRepetition,
344283
- buildRoundSystemPrompt: () => systemPrompt,
344381
+ // The per-turn tail in its own system message, after the cached
344382
+ // header, so the next turn reads the cache instead of rewriting it.
344383
+ buildRoundSystemPrompt: () => splitDynamicTail(systemPrompt, dynamicTail),
344284
344384
  detectTextToolCalls: (fr2) => useTextToolFallback && containsTextToolCall2(fr2) ? parseTextToolCalls2(fr2) : null,
344285
344385
  executeTextToolCalls: (calls) => execBuildTextToolCalls(
344286
344386
  calls
@@ -344423,6 +344523,7 @@ var init_build_branch = __esm({
344423
344523
  init_dist21();
344424
344524
  import_electron_log102 = __toESM(require_src());
344425
344525
  init_edit_stats2();
344526
+ init_system_prompt();
344426
344527
  logger99 = import_electron_log102.default.scope("chat_stream_handlers");
344427
344528
  }
344428
344529
  });
@@ -346470,11 +346571,11 @@ function registerChatStreamHandlers() {
346470
346571
  providerBrand: modelIdentity.providerBrand,
346471
346572
  labelWithProvider: modelIdentity.labelWithProvider
346472
346573
  };
346473
- let baseSystemPrompt;
346574
+ let baseParts;
346474
346575
  if (hasWorkspace && appPath && effectiveAppId != null) {
346475
- baseSystemPrompt = await preflight.time(
346576
+ baseParts = await preflight.time(
346476
346577
  "system-prompt",
346477
- () => buildSystemPromptWithProjectInstructions({
346578
+ () => buildSystemPromptPartsWithProjectInstructions({
346478
346579
  workspacePath: appPath,
346479
346580
  workspacePromptContent: null,
346480
346581
  contextSize,
@@ -346487,7 +346588,7 @@ function registerChatStreamHandlers() {
346487
346588
  })
346488
346589
  );
346489
346590
  } else {
346490
- baseSystemPrompt = constructSystemPrompt({
346591
+ baseParts = constructSystemPromptParts({
346491
346592
  chatMode,
346492
346593
  effectiveWorkMode: modeBlockKey,
346493
346594
  contextSize,
@@ -346497,6 +346598,8 @@ function registerChatStreamHandlers() {
346497
346598
  ...promptIdentityOpts
346498
346599
  });
346499
346600
  }
346601
+ let baseSystemPrompt = joinSystemPromptParts(baseParts);
346602
+ const baseDynamicTail = baseParts.dynamicTail;
346500
346603
  if (activeUserAgent) {
346501
346604
  const { renderUserAgentSystemBlock: renderUserAgentSystemBlock2 } = await Promise.resolve().then(() => (init_user_agents(), user_agents_exports));
346502
346605
  baseSystemPrompt = `${renderUserAgentSystemBlock2(activeUserAgent)}
@@ -347006,6 +347109,7 @@ ${baseSystemPrompt}`;
347006
347109
  modelContextResolved,
347007
347110
  placeholderAssistantMessage,
347008
347111
  systemPrompt,
347112
+ dynamicTail: baseDynamicTail,
347009
347113
  taskLedgerBlock,
347010
347114
  simpleStreamTextDeps,
347011
347115
  streamTurnId,
@@ -347939,7 +348043,7 @@ ${err2.stack}` : "";
347939
348043
  }
347940
348044
 
347941
348045
  // src/headless/atlarix-headless.mts
347942
- var VERSION15 = false ? "dev" : "15.8.2";
348046
+ var VERSION15 = false ? "dev" : "15.9.0";
347943
348047
  if (process.argv[2] === "run") process.argv.splice(2, 1);
347944
348048
  var RUN_HELP = `atlarix run: one turn without the UI, for scripts and CI
347945
348049
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atlarix",
3
- "version": "15.8.2",
3
+ "version": "15.9.0",
4
4
  "description": "Atlarix CLI: an AI coding agent in your terminal. Works with any model (Atlarix Auto, your own API key, or a local model) and runs unattended with `atlarix run` for CI.",
5
5
  "license": "PROPRIETARY",
6
6
  "author": {
@@ -65,12 +65,12 @@
65
65
  "provenance": false
66
66
  },
67
67
  "optionalDependencies": {
68
- "atlarix-darwin-arm64": "15.8.2",
69
- "atlarix-darwin-x64": "15.8.2",
70
- "atlarix-linux-x64": "15.8.2",
71
- "atlarix-linux-arm64": "15.8.2",
72
- "atlarix-linux-x64-musl": "15.8.2",
73
- "atlarix-linux-arm64-musl": "15.8.2",
74
- "atlarix-windows-x64": "15.8.2"
68
+ "atlarix-darwin-arm64": "15.9.0",
69
+ "atlarix-darwin-x64": "15.9.0",
70
+ "atlarix-linux-x64": "15.9.0",
71
+ "atlarix-linux-arm64": "15.9.0",
72
+ "atlarix-linux-x64-musl": "15.9.0",
73
+ "atlarix-linux-arm64-musl": "15.9.0",
74
+ "atlarix-windows-x64": "15.9.0"
75
75
  }
76
76
  }