@hiper2d/ai-agents 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -370,6 +370,7 @@ declare const LLM_CONSTANTS: {
370
370
  GLM: string;
371
371
  GLM_FLASH: string;
372
372
  FUGU_ULTRA: string;
373
+ FUGU_MAX: string;
373
374
  QWEN_MAX: string;
374
375
  QWEN_FLASH: string;
375
376
  MINIMAX: string;
@@ -527,7 +528,8 @@ declare function getProviderSignatureFields(aiType: string, signature?: string):
527
528
  * - Gemini 3.x thinkingLevel: minimal|low|medium|high (3.1 Pro and 3.7 Flash reject minimal)
528
529
  * - Z.AI GLM-5.3 / 5.3-Flash: low|high|max only
529
530
  * - DeepSeek V4: low|high|max (the API itself aliases medium → high)
530
- * - Sakana Fugu: high|xhigh
531
+ * - Sakana Fugu (verified live 2026-09-20): high|xhigh|max on every model ("low" → 400 naming
532
+ * exactly that enum). The server default is xhigh for fugu-ultra and high for the rest.
531
533
  * - Meta Muse Spark (verified 2026-09-12): minimal|low|medium|high|xhigh|max ("none" → 400; max is
532
534
  * Standard tier only)
533
535
  * - Mistral Small 4 / Medium 3.5 (verified 2026-09-18): the API enum is
@@ -543,7 +545,7 @@ type AnthropicReasoningEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
543
545
  type GeminiReasoningEffort = 'minimal' | 'low' | 'medium' | 'high';
544
546
  type GlmReasoningEffort = 'low' | 'high' | 'max';
545
547
  type DeepSeekReasoningEffort = 'low' | 'high' | 'max';
546
- type FuguReasoningEffort = 'high' | 'xhigh';
548
+ type FuguReasoningEffort = 'high' | 'xhigh' | 'max';
547
549
  type MetaReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
548
550
  type MistralReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
549
551
  /** The shared scale, lowest first. */
@@ -1651,7 +1653,6 @@ declare class FuguAgent extends AbstractAgent {
1651
1653
  private readonly errorMessages;
1652
1654
  constructor(name: string, instruction: string, model: string, apiKey: string, enableThinking?: boolean, agentLoggingConfig?: AgentLoggingConfig);
1653
1655
  private convertToOpenAIMessages;
1654
- private logRawUsageForCalibration;
1655
1656
  private extractThinkingAndUsage;
1656
1657
  private prependSystemInstruction;
1657
1658
  doAskWithZodSchema<T>(zodSchema: z.ZodSchema<T>, messages: AIMessage[]): Promise<[T, string, TokenUsage$1?, string?]>;
package/dist/index.d.ts CHANGED
@@ -370,6 +370,7 @@ declare const LLM_CONSTANTS: {
370
370
  GLM: string;
371
371
  GLM_FLASH: string;
372
372
  FUGU_ULTRA: string;
373
+ FUGU_MAX: string;
373
374
  QWEN_MAX: string;
374
375
  QWEN_FLASH: string;
375
376
  MINIMAX: string;
@@ -527,7 +528,8 @@ declare function getProviderSignatureFields(aiType: string, signature?: string):
527
528
  * - Gemini 3.x thinkingLevel: minimal|low|medium|high (3.1 Pro and 3.7 Flash reject minimal)
528
529
  * - Z.AI GLM-5.3 / 5.3-Flash: low|high|max only
529
530
  * - DeepSeek V4: low|high|max (the API itself aliases medium → high)
530
- * - Sakana Fugu: high|xhigh
531
+ * - Sakana Fugu (verified live 2026-09-20): high|xhigh|max on every model ("low" → 400 naming
532
+ * exactly that enum). The server default is xhigh for fugu-ultra and high for the rest.
531
533
  * - Meta Muse Spark (verified 2026-09-12): minimal|low|medium|high|xhigh|max ("none" → 400; max is
532
534
  * Standard tier only)
533
535
  * - Mistral Small 4 / Medium 3.5 (verified 2026-09-18): the API enum is
@@ -543,7 +545,7 @@ type AnthropicReasoningEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
543
545
  type GeminiReasoningEffort = 'minimal' | 'low' | 'medium' | 'high';
544
546
  type GlmReasoningEffort = 'low' | 'high' | 'max';
545
547
  type DeepSeekReasoningEffort = 'low' | 'high' | 'max';
546
- type FuguReasoningEffort = 'high' | 'xhigh';
548
+ type FuguReasoningEffort = 'high' | 'xhigh' | 'max';
547
549
  type MetaReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
548
550
  type MistralReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
549
551
  /** The shared scale, lowest first. */
@@ -1651,7 +1653,6 @@ declare class FuguAgent extends AbstractAgent {
1651
1653
  private readonly errorMessages;
1652
1654
  constructor(name: string, instruction: string, model: string, apiKey: string, enableThinking?: boolean, agentLoggingConfig?: AgentLoggingConfig);
1653
1655
  private convertToOpenAIMessages;
1654
- private logRawUsageForCalibration;
1655
1656
  private extractThinkingAndUsage;
1656
1657
  private prependSystemInstruction;
1657
1658
  doAskWithZodSchema<T>(zodSchema: z.ZodSchema<T>, messages: AIMessage[]): Promise<[T, string, TokenUsage$1?, string?]>;
package/dist/index.js CHANGED
@@ -976,6 +976,7 @@ var LLM_CONSTANTS = {
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",
@@ -997,9 +998,13 @@ var SupportedAiModels = {
997
998
  tags: ["expensive"]
998
999
  },
999
1000
  // Claude models — thinking-only entries (non-thinking variants retired 2026-08-05)
1001
+ // Opus moved 5 → 5.5 on 2026-09-22 (`claude-opus-5-5`, verified against GET /v1/models).
1002
+ // Cheaper than Opus 5 ($4/$20 vs $5/$25) and the same 1M context / 128K output. Thinking is
1003
+ // always on, as on Opus 5. Its API default effort is `medium`, but we keep the catalog-wide
1004
+ // `high` pin so bot answers don't get shallower than every other model in the lobby.
1000
1005
  [LLM_CONSTANTS.CLAUDE_OPUS]: {
1001
- displayName: "Claude 5 Opus",
1002
- modelApiName: "claude-opus-5",
1006
+ displayName: "Claude 5.5 Opus",
1007
+ modelApiName: "claude-opus-5-5",
1003
1008
  apiKeyName: API_KEY_CONSTANTS.ANTHROPIC,
1004
1009
  hasThinking: true,
1005
1010
  reasoningEffort: "high",
@@ -1221,13 +1226,37 @@ var SupportedAiModels = {
1221
1226
  // so the rate is not even guaranteed stable, and its cache hit rate was 9.3% — effectively
1222
1227
  // zero, since every hit came from a duplicate call seconds apart rather than turn-to-turn
1223
1228
  // prefix reuse. Ultra costs the same and is predictable.
1229
+ //
1230
+ // reasoningEffort is PINNED on both entries because the server default differs per model:
1231
+ // fugu-ultra defaults to `xhigh` (console.sakana.ai/models), fugu-max to `high`. Until
1232
+ // 2026-09-20 the agent sent no effort at all, so every Ultra turn ran at xhigh. Measured that
1233
+ // day on a 2k-token three-day game prompt (two samples each, chat completions, json mode):
1234
+ // ultra xhigh 148s / 265s $0.34 / $0.50 a turn (orchestration tokens included)
1235
+ // ultra high 65s / 78s $0.16 / $0.17
1236
+ // max high 6s / 10s $0.006 / $0.008
1237
+ // max xhigh 41s / 47s $0.02
1238
+ // `max_tokens` is not a latency lever for Ultra: Sakana applies it to the final response only,
1239
+ // the orchestrator "still uses maximum token limit". Effort is the only knob.
1224
1240
  [LLM_CONSTANTS.FUGU_ULTRA]: {
1225
1241
  displayName: "Sakana Fugu Ultra",
1226
1242
  modelApiName: "fugu-ultra",
1227
1243
  apiKeyName: API_KEY_CONSTANTS.FUGU,
1228
1244
  hasThinking: false,
1245
+ reasoningEffort: "high",
1229
1246
  tags: ["extremely-slow", "expensive"]
1230
1247
  },
1248
+ // Fugu Max (2026-09-20, fugu-max → fugu-max-v1.0): Sakana's "largest pool of models on the
1249
+ // cost–performance Pareto frontier" — the everyday Fugu at 5x less than Ultra's output rate
1250
+ // and a fraction of its latency. It reasons (reasoning_tokens 80-500 a turn) but, like
1251
+ // Ultra, never returns reasoning_content, hence hasThinking: false. No orchestration
1252
+ // tokens observed on any call.
1253
+ [LLM_CONSTANTS.FUGU_MAX]: {
1254
+ displayName: "Sakana Fugu Max",
1255
+ modelApiName: "fugu-max",
1256
+ apiKeyName: API_KEY_CONSTANTS.FUGU,
1257
+ hasThinking: false,
1258
+ reasoningEffort: "high"
1259
+ },
1231
1260
  // Qwen models (QwenCloud, OpenAI-compatible endpoint). Added 2026-08-05 straight into the
1232
1261
  // thinking-only catalog: their API has an `enable_thinking` toggle, we always send true, and
1233
1262
  // thinking arrives in `reasoning_content` (verified live against all three, non-streaming).
@@ -1448,9 +1477,13 @@ var MODEL_PRICING = {
1448
1477
  cacheHitPrice: 0.25
1449
1478
  },
1450
1479
  [SupportedAiModels[LLM_CONSTANTS.CLAUDE_OPUS].modelApiName]: {
1451
- inputPrice: 5,
1452
- outputPrice: 25,
1453
- cacheHitPrice: 0.5
1480
+ // Opus 5.5 (2026-09-22): $4/$20, down from Opus 5's $5/$25. Cache reads are 5% of input
1481
+ // here, not the usual 10% — $0.20, not $0.40. Cache WRITES ($5 at 5m, $8 at 1h) are not
1482
+ // modelled: calculateCost bills every uncached prompt token at inputPrice, so a write is
1483
+ // under-billed by the 1.25x/2x premium. Pre-existing for every Anthropic model.
1484
+ inputPrice: 4,
1485
+ outputPrice: 20,
1486
+ cacheHitPrice: 0.2
1454
1487
  },
1455
1488
  [SupportedAiModels[LLM_CONSTANTS.CLAUDE_SONNET].modelApiName]: {
1456
1489
  inputPrice: 2,
@@ -1515,9 +1548,12 @@ var MODEL_PRICING = {
1515
1548
  extendedContextCacheHitPrice: 1,
1516
1549
  extendedContextThresholdTokens: 2e5
1517
1550
  },
1518
- // Sakana Fugu models. Base `fugu` was retired 2026-08-04 — it had no published price and
1519
- // measured out at these same ultra rates, so it has no pricing entry.
1520
- // fugu-ultra has published pricing. Above 272K context the rates roughly double.
1551
+ // Sakana Fugu models (console.sakana.ai/pricing, read 2026-09-20). Base `fugu` was retired
1552
+ // 2026-08-04 — it had no published price and measured out at these same ultra rates, so it
1553
+ // has no pricing entry. fugu-ultra: above 272K context the rates roughly double. Ultra also
1554
+ // reports "orchestration" tokens (its internal expert calls) in prompt_tokens_details /
1555
+ // completion_tokens_details, billed at these same input/output rates — FuguAgent folds them
1556
+ // into the token counts before pricing.
1521
1557
  [SupportedAiModels[LLM_CONSTANTS.FUGU_ULTRA].modelApiName]: {
1522
1558
  inputPrice: 5,
1523
1559
  outputPrice: 30,
@@ -1527,6 +1563,12 @@ var MODEL_PRICING = {
1527
1563
  extendedContextCacheHitPrice: 1,
1528
1564
  extendedContextThresholdTokens: 272e3
1529
1565
  },
1566
+ // fugu-max: flat rates, no context tiers.
1567
+ [SupportedAiModels[LLM_CONSTANTS.FUGU_MAX].modelApiName]: {
1568
+ inputPrice: 2,
1569
+ outputPrice: 6,
1570
+ cacheHitPrice: 0.25
1571
+ },
1530
1572
  // Qwen models. Rates from the official pricing page (qwencloud.com/pricing/api, read
1531
1573
  // 2026-08-30 — the page is client-rendered, so it was read by eye, not WebFetch):
1532
1574
  // qwen3.8-max $2/$6 with implicit-cache hits at $0.25; qwen3.8-flash $0.15/$0.47, hits
@@ -1643,7 +1685,7 @@ var ANTHROPIC_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
1643
1685
  var GEMINI_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
1644
1686
  var GLM_REASONING_EFFORTS = ["low", "high", "max"];
1645
1687
  var DEEPSEEK_REASONING_EFFORTS = ["low", "high", "max"];
1646
- var FUGU_REASONING_EFFORTS = ["high", "xhigh"];
1688
+ var FUGU_REASONING_EFFORTS = ["high", "xhigh", "max"];
1647
1689
  var META_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh", "max"];
1648
1690
  var MISTRAL_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
1649
1691
  function clampReasoningEffort(effort, allowed) {
@@ -2599,6 +2641,7 @@ ${instruction}`);
2599
2641
 
2600
2642
  // src/agents/anthropic-agent.ts
2601
2643
  var import_sdk = require("@anthropic-ai/sdk");
2644
+ var rejectsDisabledThinking = (model) => model.includes("fable") || model.includes("opus-5-5");
2602
2645
  var ClaudeAgent = class extends AbstractAgent {
2603
2646
  client;
2604
2647
  /**
@@ -2815,7 +2858,9 @@ ${schemaDescription}`;
2815
2858
  params.temperature = 1;
2816
2859
  }
2817
2860
  } else if (usesAdaptiveThinking) {
2818
- params.thinking = { type: "disabled" };
2861
+ if (!rejectsDisabledThinking(this.model)) {
2862
+ params.thinking = { type: "disabled" };
2863
+ }
2819
2864
  } else {
2820
2865
  params.temperature = this.temperature;
2821
2866
  }
@@ -2909,7 +2954,9 @@ ${schemaDescription}`;
2909
2954
  params.temperature = 1;
2910
2955
  }
2911
2956
  } else if (usesAdaptiveThinking) {
2912
- params.thinking = { type: "disabled" };
2957
+ if (!rejectsDisabledThinking(this.model)) {
2958
+ params.thinking = { type: "disabled" };
2959
+ }
2913
2960
  } else {
2914
2961
  params.temperature = this.temperature;
2915
2962
  }
@@ -4411,13 +4458,15 @@ var FuguAgent = class extends AbstractAgent {
4411
4458
  client;
4412
4459
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
4413
4460
  // initializer would snapshot the default and silently ignore the override.
4461
+ // `reasoning_effort` is re-declared as string: the OpenAI SDK's union lacks Sakana's 'max'.
4414
4462
  get defaultParams() {
4415
4463
  return {
4416
4464
  model: this.model,
4417
4465
  stream: false,
4418
- // Caps visible output only. Server-side orchestration/reasoning tokens are
4419
- // separate and unaffected by this.
4420
- max_tokens: this.maxOutputTokens
4466
+ // Caps the final reply only. Per Sakana's docs the orchestrator "still uses maximum
4467
+ // token limit", so this bounds neither Ultra's latency nor its orchestration spend.
4468
+ max_tokens: this.maxOutputTokens,
4469
+ reasoning_effort: toFuguEffort(this.reasoningEffort ?? "high")
4421
4470
  };
4422
4471
  }
4423
4472
  logTemplates = {
@@ -4442,33 +4491,6 @@ var FuguAgent = class extends AbstractAgent {
4442
4491
  content: msg.content
4443
4492
  }));
4444
4493
  }
4445
- // ───────────────────────────────────────────────────────────────────────────────────────
4446
- // TEMPORARY (cost calibration). Base `fugu` is a dynamic router with no published per-token
4447
- // price and Sakana returns NO cost field in the response — only token counts. Crucially the
4448
- // response also reports "orchestration tokens" (billed at input/output rates per Sakana's
4449
- // pricing page) that our standard TokenUsage drops. This logs the full raw breakdown to
4450
- // BetterStack under a distinctive tag so we can sum real tokens per game and, combined with
4451
- // the Sakana billing dashboard total, derive the true per-token rate. REMOVE AFTER CALIBRATION.
4452
- logRawUsageForCalibration(completion) {
4453
- const usage = completion?.usage;
4454
- if (!usage) return;
4455
- logger.info("FUGU_COST_CALIBRATION", {
4456
- tag: "FUGU_COST_CALIBRATION",
4457
- model: this.model,
4458
- agentName: this.name,
4459
- gameId: this.gameId,
4460
- userId: this.userId,
4461
- promptTokens: usage.prompt_tokens ?? 0,
4462
- completionTokens: usage.completion_tokens ?? 0,
4463
- totalTokens: usage.total_tokens ?? 0,
4464
- cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,
4465
- orchestrationInputTokens: usage.prompt_tokens_details?.orchestration_input_tokens ?? 0,
4466
- orchestrationInputCachedTokens: usage.prompt_tokens_details?.orchestration_input_cached_tokens ?? 0,
4467
- reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,
4468
- orchestrationOutputTokens: usage.completion_tokens_details?.orchestration_output_tokens ?? 0,
4469
- rawUsage: usage
4470
- });
4471
- }
4472
4494
  extractThinkingAndUsage(completion) {
4473
4495
  let thinkingContent = "";
4474
4496
  const message = completion.choices[0]?.message;
@@ -4476,21 +4498,14 @@ var FuguAgent = class extends AbstractAgent {
4476
4498
  thinkingContent = message.reasoning_content;
4477
4499
  this.logger(`Captured reasoning_content (${thinkingContent.length} characters)`);
4478
4500
  }
4479
- let tokenUsage;
4480
- const usageResult = extractUsageAndCalculateCost(this.model, completion);
4481
- if (usageResult) {
4482
- tokenUsage = {
4483
- inputTokens: usageResult.usage.promptTokens,
4484
- outputTokens: usageResult.usage.completionTokens,
4485
- totalTokens: usageResult.usage.totalTokens,
4486
- costUSD: usageResult.cost,
4487
- ...usageResult.usage.cacheHitTokens !== void 0 ? { cachedInputTokens: usageResult.usage.cacheHitTokens } : {}
4488
- };
4489
- if (usageResult.usage.reasoningTokens) {
4490
- const reasoningTokens = usageResult.usage.reasoningTokens;
4491
- const finalAnswerTokens = Math.max(0, tokenUsage.outputTokens - reasoningTokens);
4501
+ const tokenUsage = extractFuguTokenUsage(this.model, completion);
4502
+ if (tokenUsage) {
4503
+ const details = completion.usage ?? {};
4504
+ const orchestrationIn = details.prompt_tokens_details?.orchestration_input_tokens ?? 0;
4505
+ const orchestrationOut = details.completion_tokens_details?.orchestration_output_tokens ?? 0;
4506
+ if (tokenUsage.reasoningTokens || orchestrationIn || orchestrationOut) {
4492
4507
  this.logger(
4493
- `Output breakdown: ${reasoningTokens} reasoning tokens, ${finalAnswerTokens} final answer tokens`
4508
+ `Usage breakdown: ${tokenUsage.reasoningTokens ?? 0} reasoning tokens; orchestration ${orchestrationIn} in / ${orchestrationOut} out (billed, folded into the totals)`
4494
4509
  );
4495
4510
  }
4496
4511
  }
@@ -4532,7 +4547,6 @@ ${schemaDescription}`;
4532
4547
  this.logger(this.logTemplates.error(this.name, apiError));
4533
4548
  throw new Error(this.errorMessages.apiError(apiError));
4534
4549
  }
4535
- this.logRawUsageForCalibration(completion);
4536
4550
  const rawReply = completion.choices[0]?.message?.content;
4537
4551
  if (!rawReply) {
4538
4552
  throw new Error(this.errorMessages.emptyResponse);
@@ -4576,7 +4590,6 @@ ${schemaDescription}`;
4576
4590
  this.logger(this.logTemplates.error(this.name, apiError));
4577
4591
  throw new Error(this.errorMessages.apiError(apiError));
4578
4592
  }
4579
- this.logRawUsageForCalibration(completion);
4580
4593
  const rawReply = completion.choices[0]?.message?.content;
4581
4594
  if (!rawReply) {
4582
4595
  throw new Error(this.errorMessages.emptyResponse);
@@ -4595,6 +4608,31 @@ ${schemaDescription}`;
4595
4608
  }
4596
4609
  }
4597
4610
  };
4611
+ function extractFuguTokenUsage(modelApiName, completion) {
4612
+ const usage = completion?.usage;
4613
+ if (!usage) {
4614
+ return void 0;
4615
+ }
4616
+ const promptDetails = usage.prompt_tokens_details ?? {};
4617
+ const completionDetails = usage.completion_tokens_details ?? {};
4618
+ const inputTokens = (usage.prompt_tokens ?? 0) + (promptDetails.orchestration_input_tokens ?? 0);
4619
+ const outputTokens = (usage.completion_tokens ?? 0) + (completionDetails.orchestration_output_tokens ?? 0);
4620
+ const cachedInputTokens = (promptDetails.cached_tokens ?? 0) + (promptDetails.orchestration_input_cached_tokens ?? 0);
4621
+ const reasoningTokens = completionDetails.reasoning_tokens ?? 0;
4622
+ return {
4623
+ inputTokens,
4624
+ outputTokens,
4625
+ totalTokens: inputTokens + outputTokens,
4626
+ cachedInputTokens,
4627
+ // Omitted rather than 0: consumers persist the object as-is (see TokenUsage).
4628
+ ...reasoningTokens > 0 ? { reasoningTokens } : {},
4629
+ costUSD: calculateModelCost(modelApiName, inputTokens, outputTokens, {
4630
+ cacheHitTokens: cachedInputTokens,
4631
+ // Context-tier selection: the request's own size, not the orchestrator's traffic.
4632
+ contextTokens: usage.total_tokens ?? inputTokens
4633
+ })
4634
+ };
4635
+ }
4598
4636
 
4599
4637
  // src/agents/qwen-agent.ts
4600
4638
  var import_openai9 = require("openai");
@@ -5255,6 +5293,7 @@ var AgentFactory = class {
5255
5293
  return new GlmAgent(name, instruction, model.modelApiName, key, model.temperature, shouldEnableThinking);
5256
5294
  // Sakana Fugu models — always-on reasoning, no temperature (ignored by the model)
5257
5295
  case LLM_CONSTANTS.FUGU_ULTRA:
5296
+ case LLM_CONSTANTS.FUGU_MAX:
5258
5297
  return new FuguAgent(name, instruction, model.modelApiName, key, shouldEnableThinking);
5259
5298
  // Qwen models — thinking-only (enable_thinking always sent)
5260
5299
  case LLM_CONSTANTS.QWEN_MAX: