@hiper2d/ai-agents 0.5.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -59,6 +59,7 @@ __export(src_exports, {
59
59
  LLM_CONSTANTS: () => LLM_CONSTANTS,
60
60
  MESSAGE_ROLE: () => MESSAGE_ROLE,
61
61
  META_REASONING_EFFORTS: () => META_REASONING_EFFORTS,
62
+ MISTRAL_REASONING_EFFORTS: () => MISTRAL_REASONING_EFFORTS,
62
63
  MODEL_PRICING: () => MODEL_PRICING,
63
64
  MetaAgent: () => MetaAgent,
64
65
  MiniMaxAgent: () => MiniMaxAgent,
@@ -160,6 +161,7 @@ __export(src_exports, {
160
161
  toGeminiEffort: () => toGeminiEffort,
161
162
  toGlmEffort: () => toGlmEffort,
162
163
  toMetaEffort: () => toMetaEffort,
164
+ toMistralEffort: () => toMistralEffort,
163
165
  toOpenAIEffort: () => toOpenAIEffort,
164
166
  transcribeWithGemini: () => transcribeWithGemini,
165
167
  transcribeWithOpenAi: () => transcribeWithOpenAi,
@@ -967,15 +969,14 @@ var LLM_CONSTANTS = {
967
969
  GEMINI_PRO: "gemini-pro",
968
970
  GEMINI_FLASH: "gemini-flash",
969
971
  GEMINI_LITE: "gemini-lite",
970
- MISTRAL_LARGE: "mistral-large",
971
972
  MISTRAL_MEDIUM: "mistral-medium",
972
973
  MISTRAL_SMALL: "mistral-small",
973
- MISTRAL_MAGISTRAL: "mistral-magistral",
974
974
  GROK: "grok",
975
975
  KIMI: "kimi",
976
976
  GLM: "glm",
977
977
  GLM_FLASH: "glm-flash",
978
978
  FUGU_ULTRA: "fugu-ultra",
979
+ FUGU_MAX: "fugu-max",
979
980
  // Qwen (QwenCloud/DashScope). Stable picker ids without the version, matching the gpt/gemini
980
981
  // pattern, so future repoints don't orphan persisted ids.
981
982
  QWEN_MAX: "qwen-max",
@@ -1128,36 +1129,40 @@ var SupportedAiModels = {
1128
1129
  hasThinking: true,
1129
1130
  temperature: 0.7
1130
1131
  },
1131
- // Mistral models
1132
- [LLM_CONSTANTS.MISTRAL_LARGE]: {
1133
- displayName: "Mistral Large 3",
1134
- modelApiName: "mistral-large-latest",
1135
- apiKeyName: API_KEY_CONSTANTS.MISTRAL,
1136
- hasThinking: false,
1137
- tags: ["fast"]
1138
- },
1132
+ // Mistral models. Two hybrid entries since 2026-09-18: reasoning is off by default on the
1133
+ // API and switched on per request with `reasoning_effort` (see mistral-agent.ts); the trace
1134
+ // comes back as a `thinking` content chunk and works with json_schema structured output
1135
+ // (verified live 2026-09-18 on both). Pinned to 'high' — the only level Mistral's docs
1136
+ // describe ("full thinking chunk before the final answer"); Small is cheap enough that the
1137
+ // extra tokens don't matter, Medium's are priced into its free-tier band via the hybrid
1138
+ // multiplier. Live 2026-09-18 (one short schema ask each): Small 1.7s with a ~600-char
1139
+ // trace, Medium 3.8s with ~2000 chars — Medium's trace ran ~15x its answer length.
1140
+ //
1141
+ // Mistral Large 3 and Magistral Medium 1.2 were dropped 2026-09-18. Large 3 was retired by
1142
+ // Mistral on 2026-08-31 (the `-latest` alias still answered, but its capabilities say
1143
+ // reasoning: false and `reasoning_effort` is a 400 on it). Magistral was retired 2026-07-31
1144
+ // and `magistral-medium-latest` is now literally an alias of Medium 3.5 — the model list
1145
+ // returns `mistral-medium-3-5`, `mistral-medium-3`, `mistral-medium-latest` and
1146
+ // `magistral-medium-latest` as one entry — so Magistral seats had silently become Medium
1147
+ // seats already. Consumers map both retired ids onto the surviving pair.
1139
1148
  [LLM_CONSTANTS.MISTRAL_MEDIUM]: {
1140
1149
  displayName: "Mistral Medium 3.5",
1141
- modelApiName: "mistral-medium-3",
1150
+ // The docs' explicit 3.5 id. It was `mistral-medium-3` until 2026-09-18, which Mistral
1151
+ // now serves as an alias of the same model (Medium 3 itself retired 2026-08-31).
1152
+ modelApiName: "mistral-medium-3-5",
1142
1153
  apiKeyName: API_KEY_CONSTANTS.MISTRAL,
1143
- hasThinking: false,
1144
- tags: ["very-fast", "expensive"]
1154
+ hasThinking: true,
1155
+ reasoningEffort: "high",
1156
+ tags: ["fast", "expensive"]
1145
1157
  },
1146
1158
  [LLM_CONSTANTS.MISTRAL_SMALL]: {
1147
1159
  displayName: "Mistral 4 Small",
1160
+ // Resolves to mistral-small-2603 (Small 4), which also carries the magistral-small alias.
1148
1161
  modelApiName: "mistral-small-latest",
1149
1162
  apiKeyName: API_KEY_CONSTANTS.MISTRAL,
1150
- hasThinking: false,
1151
- tags: ["very-fast", "cheap"]
1152
- },
1153
- [LLM_CONSTANTS.MISTRAL_MAGISTRAL]: {
1154
- displayName: "Magistral Medium 1.2",
1155
- modelApiName: "magistral-medium-latest",
1156
- apiKeyName: API_KEY_CONSTANTS.MISTRAL,
1157
1163
  hasThinking: true,
1158
- // Measured very-fast (1.6s) because JSON response mode suppresses its thinking
1159
- // (see mistral-agent.ts) — it effectively runs as a non-reasoning model here.
1160
- tags: ["very-fast"]
1164
+ reasoningEffort: "high",
1165
+ tags: ["very-fast", "cheap"]
1161
1166
  },
1162
1167
  // Kimi models. Single always-reasoning entry: K3 reasons by default and the only way to stop
1163
1168
  // it is the undocumented K2-era `thinking: disabled` toggle, which we no longer rely on.
@@ -1217,13 +1222,37 @@ var SupportedAiModels = {
1217
1222
  // so the rate is not even guaranteed stable, and its cache hit rate was 9.3% — effectively
1218
1223
  // zero, since every hit came from a duplicate call seconds apart rather than turn-to-turn
1219
1224
  // prefix reuse. Ultra costs the same and is predictable.
1225
+ //
1226
+ // reasoningEffort is PINNED on both entries because the server default differs per model:
1227
+ // fugu-ultra defaults to `xhigh` (console.sakana.ai/models), fugu-max to `high`. Until
1228
+ // 2026-09-20 the agent sent no effort at all, so every Ultra turn ran at xhigh. Measured that
1229
+ // day on a 2k-token three-day game prompt (two samples each, chat completions, json mode):
1230
+ // ultra xhigh 148s / 265s $0.34 / $0.50 a turn (orchestration tokens included)
1231
+ // ultra high 65s / 78s $0.16 / $0.17
1232
+ // max high 6s / 10s $0.006 / $0.008
1233
+ // max xhigh 41s / 47s $0.02
1234
+ // `max_tokens` is not a latency lever for Ultra: Sakana applies it to the final response only,
1235
+ // the orchestrator "still uses maximum token limit". Effort is the only knob.
1220
1236
  [LLM_CONSTANTS.FUGU_ULTRA]: {
1221
1237
  displayName: "Sakana Fugu Ultra",
1222
1238
  modelApiName: "fugu-ultra",
1223
1239
  apiKeyName: API_KEY_CONSTANTS.FUGU,
1224
1240
  hasThinking: false,
1241
+ reasoningEffort: "high",
1225
1242
  tags: ["extremely-slow", "expensive"]
1226
1243
  },
1244
+ // Fugu Max (2026-09-20, fugu-max → fugu-max-v1.0): Sakana's "largest pool of models on the
1245
+ // cost–performance Pareto frontier" — the everyday Fugu at 5x less than Ultra's output rate
1246
+ // and a fraction of its latency. It reasons (reasoning_tokens 80-500 a turn) but, like
1247
+ // Ultra, never returns reasoning_content, hence hasThinking: false. No orchestration
1248
+ // tokens observed on any call.
1249
+ [LLM_CONSTANTS.FUGU_MAX]: {
1250
+ displayName: "Sakana Fugu Max",
1251
+ modelApiName: "fugu-max",
1252
+ apiKeyName: API_KEY_CONSTANTS.FUGU,
1253
+ hasThinking: false,
1254
+ reasoningEffort: "high"
1255
+ },
1227
1256
  // Qwen models (QwenCloud, OpenAI-compatible endpoint). Added 2026-08-05 straight into the
1228
1257
  // thinking-only catalog: their API has an `enable_thinking` toggle, we always send true, and
1229
1258
  // thinking arrives in `reasoning_content` (verified live against all three, non-streaming).
@@ -1485,13 +1514,10 @@ var MODEL_PRICING = {
1485
1514
  outputPrice: 1.5,
1486
1515
  cacheHitPrice: 0.025
1487
1516
  },
1488
- // Mistral models. Cached tokens bill at 10% of the input price (documented on the
1489
- // prompt_cache_key param in the API reference; no per-model cached prices published).
1490
- [SupportedAiModels[LLM_CONSTANTS.MISTRAL_LARGE].modelApiName]: {
1491
- inputPrice: 0.5,
1492
- outputPrice: 1.5,
1493
- cacheHitPrice: 0.05
1494
- },
1517
+ // Mistral models (mistral.ai/pricing/api, verified 2026-09-18). Cached tokens bill at 10% of
1518
+ // the input price (documented on the prompt_cache_key param; no per-model cached prices
1519
+ // published). Reasoning tokens are counted inside completion_tokens — no separate
1520
+ // reasoning_tokens field — so the output rate already covers the trace.
1495
1521
  [SupportedAiModels[LLM_CONSTANTS.MISTRAL_MEDIUM].modelApiName]: {
1496
1522
  inputPrice: 1.5,
1497
1523
  outputPrice: 7.5,
@@ -1502,11 +1528,6 @@ var MODEL_PRICING = {
1502
1528
  outputPrice: 0.6,
1503
1529
  cacheHitPrice: 0.015
1504
1530
  },
1505
- [SupportedAiModels[LLM_CONSTANTS.MISTRAL_MAGISTRAL].modelApiName]: {
1506
- inputPrice: 2,
1507
- outputPrice: 5,
1508
- cacheHitPrice: 0.2
1509
- },
1510
1531
  // Grok models. Cached price is per-model on xAI (not a uniform ratio):
1511
1532
  // grok-4.6 is $0.50/M cached vs $2.00/M input, and all rates double for prompts
1512
1533
  // >= 200K tokens, per docs.x.ai/developers/models (verified 2026-08-12).
@@ -1519,9 +1540,12 @@ var MODEL_PRICING = {
1519
1540
  extendedContextCacheHitPrice: 1,
1520
1541
  extendedContextThresholdTokens: 2e5
1521
1542
  },
1522
- // Sakana Fugu models. Base `fugu` was retired 2026-08-04 — it had no published price and
1523
- // measured out at these same ultra rates, so it has no pricing entry.
1524
- // fugu-ultra has published pricing. Above 272K context the rates roughly double.
1543
+ // Sakana Fugu models (console.sakana.ai/pricing, read 2026-09-20). Base `fugu` was retired
1544
+ // 2026-08-04 — it had no published price and measured out at these same ultra rates, so it
1545
+ // has no pricing entry. fugu-ultra: above 272K context the rates roughly double. Ultra also
1546
+ // reports "orchestration" tokens (its internal expert calls) in prompt_tokens_details /
1547
+ // completion_tokens_details, billed at these same input/output rates — FuguAgent folds them
1548
+ // into the token counts before pricing.
1525
1549
  [SupportedAiModels[LLM_CONSTANTS.FUGU_ULTRA].modelApiName]: {
1526
1550
  inputPrice: 5,
1527
1551
  outputPrice: 30,
@@ -1531,6 +1555,12 @@ var MODEL_PRICING = {
1531
1555
  extendedContextCacheHitPrice: 1,
1532
1556
  extendedContextThresholdTokens: 272e3
1533
1557
  },
1558
+ // fugu-max: flat rates, no context tiers.
1559
+ [SupportedAiModels[LLM_CONSTANTS.FUGU_MAX].modelApiName]: {
1560
+ inputPrice: 2,
1561
+ outputPrice: 6,
1562
+ cacheHitPrice: 0.25
1563
+ },
1534
1564
  // Qwen models. Rates from the official pricing page (qwencloud.com/pricing/api, read
1535
1565
  // 2026-08-30 — the page is client-rendered, so it was read by eye, not WebFetch):
1536
1566
  // qwen3.8-max $2/$6 with implicit-cache hits at $0.25; qwen3.8-flash $0.15/$0.47, hits
@@ -1581,7 +1611,11 @@ var HYBRID_THINKING_API_NAMES = /* @__PURE__ */ new Set([
1581
1611
  // hybrid by the same definition: we force reasoning on, so they carry the multiplier.
1582
1612
  SupportedAiModels[LLM_CONSTANTS.QWEN_MAX].modelApiName,
1583
1613
  SupportedAiModels[LLM_CONSTANTS.QWEN_FLASH].modelApiName,
1584
- SupportedAiModels[LLM_CONSTANTS.MINIMAX].modelApiName
1614
+ SupportedAiModels[LLM_CONSTANTS.MINIMAX].modelApiName,
1615
+ // Mistral reasons only when asked (`reasoning_effort`), and we always ask — hybrid by the
1616
+ // same definition as Qwen. Added 2026-09-18 when the two entries went thinking-on.
1617
+ SupportedAiModels[LLM_CONSTANTS.MISTRAL_MEDIUM].modelApiName,
1618
+ SupportedAiModels[LLM_CONSTANTS.MISTRAL_SMALL].modelApiName
1585
1619
  ]);
1586
1620
  function isHybridThinkingModel(modelApiName) {
1587
1621
  return HYBRID_THINKING_API_NAMES.has(modelApiName);
@@ -1643,8 +1677,9 @@ var ANTHROPIC_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
1643
1677
  var GEMINI_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
1644
1678
  var GLM_REASONING_EFFORTS = ["low", "high", "max"];
1645
1679
  var DEEPSEEK_REASONING_EFFORTS = ["low", "high", "max"];
1646
- var FUGU_REASONING_EFFORTS = ["high", "xhigh"];
1680
+ var FUGU_REASONING_EFFORTS = ["high", "xhigh", "max"];
1647
1681
  var META_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh", "max"];
1682
+ var MISTRAL_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
1648
1683
  function clampReasoningEffort(effort, allowed) {
1649
1684
  const rank = REASONING_EFFORT_SCALE.indexOf(effort);
1650
1685
  let best = allowed[0];
@@ -1665,6 +1700,7 @@ var toGlmEffort = (effort) => clampReasoningEffort(effort, GLM_REASONING_EFFORTS
1665
1700
  var toDeepSeekEffort = (effort) => clampReasoningEffort(effort, DEEPSEEK_REASONING_EFFORTS);
1666
1701
  var toMetaEffort = (effort) => clampReasoningEffort(effort, META_REASONING_EFFORTS);
1667
1702
  var toFuguEffort = (effort) => clampReasoningEffort(effort, FUGU_REASONING_EFFORTS);
1703
+ var toMistralEffort = (effort) => clampReasoningEffort(effort, MISTRAL_REASONING_EFFORTS);
1668
1704
 
1669
1705
  // src/pricing/token-usage-utils.ts
1670
1706
  function extractTokenUsage(response) {
@@ -3397,6 +3433,9 @@ ${instruction}`);
3397
3433
  const body = await request.clone().text();
3398
3434
  const json = JSON.parse(body);
3399
3435
  json.prompt_cache_key = promptCacheKey;
3436
+ if (this.enableThinking && this.reasoningEffort) {
3437
+ json.reasoning_effort = toMistralEffort(this.reasoningEffort);
3438
+ }
3400
3439
  return new Request(request.url, {
3401
3440
  method: request.method,
3402
3441
  headers: request.headers,
@@ -3409,11 +3448,25 @@ ${instruction}`);
3409
3448
  });
3410
3449
  this.client = new import_mistralai.Mistral({ apiKey, httpClient });
3411
3450
  }
3451
+ /**
3452
+ * History in Mistral's shape. A prior assistant turn that carries a stored trace is
3453
+ * replayed as [thinking, text] chunks, as Mistral's multi-turn guidance asks; every other
3454
+ * turn is a plain string.
3455
+ */
3412
3456
  convertToMistralMessages(messages) {
3413
- return this.prepareMessages(messages).map((msg) => ({
3414
- role: msg.role === "developer" ? "system" : msg.role,
3415
- content: msg.content
3416
- }));
3457
+ return this.prepareMessages(messages).map((msg) => {
3458
+ if (msg.role === "assistant" && msg.thinking) {
3459
+ const chunks = [
3460
+ { type: "thinking", thinking: [{ type: "text", text: msg.thinking }] },
3461
+ { type: "text", text: msg.content }
3462
+ ];
3463
+ return { role: "assistant", content: chunks };
3464
+ }
3465
+ return {
3466
+ role: msg.role === "developer" ? "system" : msg.role,
3467
+ content: msg.content
3468
+ };
3469
+ });
3417
3470
  }
3418
3471
  processReply(response) {
3419
3472
  const message = response?.choices?.[0]?.message;
@@ -3448,7 +3501,6 @@ ${instruction}`);
3448
3501
  extractTokenUsage(response) {
3449
3502
  const usage = extractMistralTokenUsage(response);
3450
3503
  if (!usage) return void 0;
3451
- this.logger(`MISTRAL_CACHE_CALIBRATION raw usage: ${JSON.stringify(response?.usage)}`);
3452
3504
  if (usage.reasoningTokens && usage.reasoningTokens > 0) {
3453
3505
  this.logger(`\u{1F9E0} Reasoning tokens used: ${usage.reasoningTokens}`);
3454
3506
  }
@@ -3484,7 +3536,7 @@ ${instruction}`);
3484
3536
  const convertedMessages = this.convertToMistralMessages(messages);
3485
3537
  if (convertedMessages.length > 0) {
3486
3538
  const lastMessage = convertedMessages[convertedMessages.length - 1];
3487
- if (lastMessage && lastMessage.content) {
3539
+ if (lastMessage && typeof lastMessage.content === "string" && lastMessage.content) {
3488
3540
  lastMessage.content += `
3489
3541
 
3490
3542
  Your response must be a valid JSON object matching this schema:
@@ -3555,9 +3607,8 @@ ${schemaDescription}`
3555
3607
  }
3556
3608
  }
3557
3609
  /**
3558
- * Plain-text ask: no schema appended, no responseFormat. Note that Magistral
3559
- * reasoning models only return thinking traces when responseFormat is NOT
3560
- * json_object, so unlike askWithZodSchema this path can surface thinking content.
3610
+ * Plain-text ask: no schema appended, no responseFormat. Reasoning (and the trace) works
3611
+ * the same way as on the schema path.
3561
3612
  */
3562
3613
  async doAskText(messages) {
3563
3614
  try {
@@ -4394,13 +4445,15 @@ var FuguAgent = class extends AbstractAgent {
4394
4445
  client;
4395
4446
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
4396
4447
  // initializer would snapshot the default and silently ignore the override.
4448
+ // `reasoning_effort` is re-declared as string: the OpenAI SDK's union lacks Sakana's 'max'.
4397
4449
  get defaultParams() {
4398
4450
  return {
4399
4451
  model: this.model,
4400
4452
  stream: false,
4401
- // Caps visible output only. Server-side orchestration/reasoning tokens are
4402
- // separate and unaffected by this.
4403
- max_tokens: this.maxOutputTokens
4453
+ // Caps the final reply only. Per Sakana's docs the orchestrator "still uses maximum
4454
+ // token limit", so this bounds neither Ultra's latency nor its orchestration spend.
4455
+ max_tokens: this.maxOutputTokens,
4456
+ reasoning_effort: toFuguEffort(this.reasoningEffort ?? "high")
4404
4457
  };
4405
4458
  }
4406
4459
  logTemplates = {
@@ -4425,33 +4478,6 @@ var FuguAgent = class extends AbstractAgent {
4425
4478
  content: msg.content
4426
4479
  }));
4427
4480
  }
4428
- // ───────────────────────────────────────────────────────────────────────────────────────
4429
- // TEMPORARY (cost calibration). Base `fugu` is a dynamic router with no published per-token
4430
- // price and Sakana returns NO cost field in the response — only token counts. Crucially the
4431
- // response also reports "orchestration tokens" (billed at input/output rates per Sakana's
4432
- // pricing page) that our standard TokenUsage drops. This logs the full raw breakdown to
4433
- // BetterStack under a distinctive tag so we can sum real tokens per game and, combined with
4434
- // the Sakana billing dashboard total, derive the true per-token rate. REMOVE AFTER CALIBRATION.
4435
- logRawUsageForCalibration(completion) {
4436
- const usage = completion?.usage;
4437
- if (!usage) return;
4438
- logger.info("FUGU_COST_CALIBRATION", {
4439
- tag: "FUGU_COST_CALIBRATION",
4440
- model: this.model,
4441
- agentName: this.name,
4442
- gameId: this.gameId,
4443
- userId: this.userId,
4444
- promptTokens: usage.prompt_tokens ?? 0,
4445
- completionTokens: usage.completion_tokens ?? 0,
4446
- totalTokens: usage.total_tokens ?? 0,
4447
- cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,
4448
- orchestrationInputTokens: usage.prompt_tokens_details?.orchestration_input_tokens ?? 0,
4449
- orchestrationInputCachedTokens: usage.prompt_tokens_details?.orchestration_input_cached_tokens ?? 0,
4450
- reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,
4451
- orchestrationOutputTokens: usage.completion_tokens_details?.orchestration_output_tokens ?? 0,
4452
- rawUsage: usage
4453
- });
4454
- }
4455
4481
  extractThinkingAndUsage(completion) {
4456
4482
  let thinkingContent = "";
4457
4483
  const message = completion.choices[0]?.message;
@@ -4459,21 +4485,14 @@ var FuguAgent = class extends AbstractAgent {
4459
4485
  thinkingContent = message.reasoning_content;
4460
4486
  this.logger(`Captured reasoning_content (${thinkingContent.length} characters)`);
4461
4487
  }
4462
- let tokenUsage;
4463
- const usageResult = extractUsageAndCalculateCost(this.model, completion);
4464
- if (usageResult) {
4465
- tokenUsage = {
4466
- inputTokens: usageResult.usage.promptTokens,
4467
- outputTokens: usageResult.usage.completionTokens,
4468
- totalTokens: usageResult.usage.totalTokens,
4469
- costUSD: usageResult.cost,
4470
- ...usageResult.usage.cacheHitTokens !== void 0 ? { cachedInputTokens: usageResult.usage.cacheHitTokens } : {}
4471
- };
4472
- if (usageResult.usage.reasoningTokens) {
4473
- const reasoningTokens = usageResult.usage.reasoningTokens;
4474
- const finalAnswerTokens = Math.max(0, tokenUsage.outputTokens - reasoningTokens);
4488
+ const tokenUsage = extractFuguTokenUsage(this.model, completion);
4489
+ if (tokenUsage) {
4490
+ const details = completion.usage ?? {};
4491
+ const orchestrationIn = details.prompt_tokens_details?.orchestration_input_tokens ?? 0;
4492
+ const orchestrationOut = details.completion_tokens_details?.orchestration_output_tokens ?? 0;
4493
+ if (tokenUsage.reasoningTokens || orchestrationIn || orchestrationOut) {
4475
4494
  this.logger(
4476
- `Output breakdown: ${reasoningTokens} reasoning tokens, ${finalAnswerTokens} final answer tokens`
4495
+ `Usage breakdown: ${tokenUsage.reasoningTokens ?? 0} reasoning tokens; orchestration ${orchestrationIn} in / ${orchestrationOut} out (billed, folded into the totals)`
4477
4496
  );
4478
4497
  }
4479
4498
  }
@@ -4515,7 +4534,6 @@ ${schemaDescription}`;
4515
4534
  this.logger(this.logTemplates.error(this.name, apiError));
4516
4535
  throw new Error(this.errorMessages.apiError(apiError));
4517
4536
  }
4518
- this.logRawUsageForCalibration(completion);
4519
4537
  const rawReply = completion.choices[0]?.message?.content;
4520
4538
  if (!rawReply) {
4521
4539
  throw new Error(this.errorMessages.emptyResponse);
@@ -4559,7 +4577,6 @@ ${schemaDescription}`;
4559
4577
  this.logger(this.logTemplates.error(this.name, apiError));
4560
4578
  throw new Error(this.errorMessages.apiError(apiError));
4561
4579
  }
4562
- this.logRawUsageForCalibration(completion);
4563
4580
  const rawReply = completion.choices[0]?.message?.content;
4564
4581
  if (!rawReply) {
4565
4582
  throw new Error(this.errorMessages.emptyResponse);
@@ -4578,6 +4595,31 @@ ${schemaDescription}`;
4578
4595
  }
4579
4596
  }
4580
4597
  };
4598
+ function extractFuguTokenUsage(modelApiName, completion) {
4599
+ const usage = completion?.usage;
4600
+ if (!usage) {
4601
+ return void 0;
4602
+ }
4603
+ const promptDetails = usage.prompt_tokens_details ?? {};
4604
+ const completionDetails = usage.completion_tokens_details ?? {};
4605
+ const inputTokens = (usage.prompt_tokens ?? 0) + (promptDetails.orchestration_input_tokens ?? 0);
4606
+ const outputTokens = (usage.completion_tokens ?? 0) + (completionDetails.orchestration_output_tokens ?? 0);
4607
+ const cachedInputTokens = (promptDetails.cached_tokens ?? 0) + (promptDetails.orchestration_input_cached_tokens ?? 0);
4608
+ const reasoningTokens = completionDetails.reasoning_tokens ?? 0;
4609
+ return {
4610
+ inputTokens,
4611
+ outputTokens,
4612
+ totalTokens: inputTokens + outputTokens,
4613
+ cachedInputTokens,
4614
+ // Omitted rather than 0: consumers persist the object as-is (see TokenUsage).
4615
+ ...reasoningTokens > 0 ? { reasoningTokens } : {},
4616
+ costUSD: calculateModelCost(modelApiName, inputTokens, outputTokens, {
4617
+ cacheHitTokens: cachedInputTokens,
4618
+ // Context-tier selection: the request's own size, not the orchestrator's traffic.
4619
+ contextTokens: usage.total_tokens ?? inputTokens
4620
+ })
4621
+ };
4622
+ }
4581
4623
 
4582
4624
  // src/agents/qwen-agent.ts
4583
4625
  var import_openai9 = require("openai");
@@ -5229,8 +5271,6 @@ var AgentFactory = class {
5229
5271
  // Mistral models
5230
5272
  case LLM_CONSTANTS.MISTRAL_MEDIUM:
5231
5273
  case LLM_CONSTANTS.MISTRAL_SMALL:
5232
- case LLM_CONSTANTS.MISTRAL_LARGE:
5233
- case LLM_CONSTANTS.MISTRAL_MAGISTRAL:
5234
5274
  return new MistralAgent(name, instruction, model.modelApiName, key, shouldEnableThinking);
5235
5275
  case LLM_CONSTANTS.KIMI:
5236
5276
  return new KimiAgent(name, instruction, model.modelApiName, key, 0, shouldEnableThinking);
@@ -5240,6 +5280,7 @@ var AgentFactory = class {
5240
5280
  return new GlmAgent(name, instruction, model.modelApiName, key, model.temperature, shouldEnableThinking);
5241
5281
  // Sakana Fugu models — always-on reasoning, no temperature (ignored by the model)
5242
5282
  case LLM_CONSTANTS.FUGU_ULTRA:
5283
+ case LLM_CONSTANTS.FUGU_MAX:
5243
5284
  return new FuguAgent(name, instruction, model.modelApiName, key, shouldEnableThinking);
5244
5285
  // Qwen models — thinking-only (enable_thinking always sent)
5245
5286
  case LLM_CONSTANTS.QWEN_MAX:
@@ -5294,6 +5335,7 @@ var AgentFactory = class {
5294
5335
  LLM_CONSTANTS,
5295
5336
  MESSAGE_ROLE,
5296
5337
  META_REASONING_EFFORTS,
5338
+ MISTRAL_REASONING_EFFORTS,
5297
5339
  MODEL_PRICING,
5298
5340
  MetaAgent,
5299
5341
  MiniMaxAgent,
@@ -5395,6 +5437,7 @@ var AgentFactory = class {
5395
5437
  toGeminiEffort,
5396
5438
  toGlmEffort,
5397
5439
  toMetaEffort,
5440
+ toMistralEffort,
5398
5441
  toOpenAIEffort,
5399
5442
  transcribeWithGemini,
5400
5443
  transcribeWithOpenAi,