@hiper2d/ai-agents 0.1.5 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -44,10 +44,12 @@ __export(index_exports, {
44
44
  DeepSeekV2Agent: () => DeepSeekV2Agent,
45
45
  FUGU_REASONING_EFFORTS: () => FUGU_REASONING_EFFORTS,
46
46
  FuguAgent: () => FuguAgent,
47
+ GEMINI_AUDIO_TOKENS_PER_SECOND: () => GEMINI_AUDIO_TOKENS_PER_SECOND,
47
48
  GEMINI_REASONING_EFFORTS: () => GEMINI_REASONING_EFFORTS,
48
49
  GLM_REASONING_EFFORTS: () => GLM_REASONING_EFFORTS,
49
50
  GlmAgent: () => GlmAgent,
50
51
  GoogleAgent: () => GoogleAgent,
52
+ GoogleVoiceAgent: () => GoogleVoiceAgent,
51
53
  Gpt5Agent: () => Gpt5Agent,
52
54
  GrokAgent: () => GrokAgent,
53
55
  KimiAgent: () => KimiAgent,
@@ -65,23 +67,35 @@ __export(index_exports, {
65
67
  ModelRefusalError: () => ModelRefusalError,
66
68
  ModelUnavailableError: () => ModelUnavailableError,
67
69
  OPENAI_REASONING_EFFORTS: () => OPENAI_REASONING_EFFORTS,
70
+ OpenAiVoiceAgent: () => OpenAiVoiceAgent,
68
71
  QwenAgent: () => QwenAgent,
69
72
  REASONING_EFFORT_SCALE: () => REASONING_EFFORT_SCALE,
73
+ SUPPORTED_VOICE_PROVIDERS: () => SUPPORTED_VOICE_PROVIDERS,
70
74
  SupportedAiKeyNames: () => SupportedAiKeyNames,
71
75
  SupportedAiModels: () => SupportedAiModels,
76
+ VOICE_MODEL_CONSTANTS: () => VOICE_MODEL_CONSTANTS,
77
+ VOICE_MODEL_PRICING: () => VOICE_MODEL_PRICING,
78
+ VOICE_PROVIDER_API_KEY: () => VOICE_PROVIDER_API_KEY,
79
+ VoiceAgentFactory: () => VoiceAgentFactory,
72
80
  ZodSchemaConverter: () => ZodSchemaConverter,
81
+ buildGoogleTtsPrompt: () => buildGoogleTtsPrompt,
73
82
  calculateAnthropicCost: () => calculateAnthropicCost,
74
83
  calculateCost: () => calculateCost,
75
84
  calculateDeepSeekCost: () => calculateDeepSeekCost,
85
+ calculateGeminiSttCost: () => calculateGeminiSttCost,
86
+ calculateGeminiTtsCost: () => calculateGeminiTtsCost,
76
87
  calculateGoogleCost: () => calculateGoogleCost,
77
88
  calculateGrokCost: () => calculateGrokCost,
78
89
  calculateKimiCost: () => calculateKimiCost,
79
90
  calculateMistralCost: () => calculateMistralCost,
80
91
  calculateModelCost: () => calculateModelCost,
81
92
  calculateOpenAICost: () => calculateOpenAICost,
93
+ calculateOpenAiSttCost: () => calculateOpenAiSttCost,
94
+ calculateOpenAiTtsCost: () => calculateOpenAiTtsCost,
82
95
  clampReasoningEffort: () => clampReasoningEffort,
83
96
  cleanResponse: () => cleanResponse,
84
97
  createCatalog: () => createCatalog,
98
+ createVoiceAgent: () => createVoiceAgent,
85
99
  extractAnthropicTokenUsage: () => extractAnthropicTokenUsage,
86
100
  extractAnthropicTokenUsageFromResponse: () => extractTokenUsageFromResponse5,
87
101
  extractDeepSeekTokenUsage: () => extractDeepSeekTokenUsage,
@@ -99,6 +113,8 @@ __export(index_exports, {
99
113
  extractOpenAITokenUsageFromResponse: () => extractTokenUsageFromResponse,
100
114
  extractTokenUsage: () => extractTokenUsage,
101
115
  extractUsageAndCalculateCost: () => extractUsageAndCalculateCost,
116
+ generateGoogleTtsAudio: () => generateGoogleTtsAudio,
117
+ generateOpenAiTtsAudio: () => generateOpenAiTtsAudio,
102
118
  generateSchemaInstructions: () => generateSchemaInstructions,
103
119
  getModelConfigByApiName: () => getModelConfigByApiName,
104
120
  getModelDisplayName: () => getModelDisplayName,
@@ -115,6 +131,7 @@ __export(index_exports, {
115
131
  modelIsFast: () => modelIsFast,
116
132
  needsPromptBasedSchema: () => needsPromptBasedSchema,
117
133
  parseAndValidateLlmJson: () => parseAndValidateLlmJson,
134
+ pcmToWav: () => pcmToWav,
118
135
  safeValidateResponse: () => safeValidateResponse,
119
136
  setLlmLogger: () => setLlmLogger,
120
137
  stableHashHex: () => stableHashHex,
@@ -126,6 +143,8 @@ __export(index_exports, {
126
143
  toGeminiEffort: () => toGeminiEffort,
127
144
  toGlmEffort: () => toGlmEffort,
128
145
  toOpenAIEffort: () => toOpenAIEffort,
146
+ transcribeWithGemini: () => transcribeWithGemini,
147
+ transcribeWithOpenAi: () => transcribeWithOpenAi,
129
148
  validateResponse: () => validateResponse
130
149
  });
131
150
  module.exports = __toCommonJS(index_exports);
@@ -1750,6 +1769,263 @@ function extractTokenUsageFromResponse7(response) {
1750
1769
  return extractMistralTokenUsage(response);
1751
1770
  }
1752
1771
 
1772
+ // src/voice/voice-catalog.ts
1773
+ var VOICE_MODEL_CONSTANTS = {
1774
+ OPENAI_TTS: "gpt-4o-mini-tts",
1775
+ OPENAI_STT: "whisper-1",
1776
+ // ai.google.dev/gemini-api/docs/speech-generation
1777
+ GOOGLE_TTS: "gemini-3.1-flash-tts-preview",
1778
+ // ai.google.dev/gemini-api/docs/transcribe — Interactions API only (see google-stt.ts)
1779
+ GOOGLE_STT: "gemini-3.5-transcribe"
1780
+ };
1781
+ var SUPPORTED_VOICE_PROVIDERS = ["openai", "google"];
1782
+ var VOICE_PROVIDER_API_KEY = {
1783
+ openai: API_KEY_CONSTANTS.OPENAI,
1784
+ google: API_KEY_CONSTANTS.GOOGLE
1785
+ };
1786
+ var VOICE_MODEL_PRICING = {
1787
+ [VOICE_MODEL_CONSTANTS.OPENAI_TTS]: { pricePerMillionCharacters: 15 },
1788
+ [VOICE_MODEL_CONSTANTS.OPENAI_STT]: { pricePerMinute: 6e-3 },
1789
+ // Measured 2026-09-05: ~32 audio tokens per second of speech, so a 15-second
1790
+ // line is ~$0.01 — about 3-5x an OpenAI line of the same length.
1791
+ [VOICE_MODEL_CONSTANTS.GOOGLE_TTS]: { textInputPricePerM: 1, audioOutputPricePerM: 20 },
1792
+ // ~25 audio tokens per second in, ~175 text tokens per minute out: ≈ $0.005/min.
1793
+ [VOICE_MODEL_CONSTANTS.GOOGLE_STT]: { audioInputPricePerM: 2, textOutputPricePerM: 12 }
1794
+ };
1795
+
1796
+ // src/voice/voice-pricing.ts
1797
+ function roundUSD(value) {
1798
+ return parseFloat((value || 0).toFixed(6));
1799
+ }
1800
+ function calculateOpenAiTtsCost(characterCount) {
1801
+ const rate = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.OPENAI_TTS]?.pricePerMillionCharacters ?? 0;
1802
+ if (!characterCount || characterCount <= 0 || rate <= 0) return 0;
1803
+ return roundUSD(characterCount / 1e6 * rate);
1804
+ }
1805
+ function calculateOpenAiSttCost(durationSeconds) {
1806
+ const rate = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.OPENAI_STT]?.pricePerMinute ?? 0;
1807
+ if (!durationSeconds || durationSeconds <= 0 || rate <= 0) return 0;
1808
+ return roundUSD(durationSeconds / 60 * rate);
1809
+ }
1810
+ function calculateGeminiTtsCost(usage) {
1811
+ const pricing = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.GOOGLE_TTS];
1812
+ return tokenPairCost(usage, pricing?.textInputPricePerM ?? 0, pricing?.audioOutputPricePerM ?? 0);
1813
+ }
1814
+ function calculateGeminiSttCost(usage) {
1815
+ const pricing = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.GOOGLE_STT];
1816
+ return tokenPairCost(usage, pricing?.audioInputPricePerM ?? 0, pricing?.textOutputPricePerM ?? 0);
1817
+ }
1818
+ function tokenPairCost(usage, inputRate, outputRate) {
1819
+ const inputTokens = Math.max(0, usage.inputTokens || 0);
1820
+ const outputTokens = Math.max(0, usage.outputTokens || 0);
1821
+ return inputTokens / 1e6 * inputRate + outputTokens / 1e6 * outputRate;
1822
+ }
1823
+
1824
+ // src/voice/openai-tts.ts
1825
+ var import_openai = require("openai");
1826
+ async function generateOpenAiTtsAudio(text, apiKey, options = {}) {
1827
+ const client = new import_openai.OpenAI({ apiKey });
1828
+ const speechOptions = {
1829
+ model: VOICE_MODEL_CONSTANTS.OPENAI_TTS,
1830
+ voice: options.voice || "alloy",
1831
+ input: text,
1832
+ speed: options.speed || 1,
1833
+ response_format: options.format || "wav"
1834
+ };
1835
+ if (options.instructions) {
1836
+ speechOptions.instructions = options.instructions;
1837
+ }
1838
+ const response = await client.audio.speech.create(speechOptions);
1839
+ return await response.arrayBuffer();
1840
+ }
1841
+
1842
+ // src/voice/openai-stt.ts
1843
+ var import_openai2 = require("openai");
1844
+ async function transcribeWithOpenAi(audioBuffer, apiKey, options = {}) {
1845
+ const client = new import_openai2.OpenAI({ apiKey });
1846
+ const audioFile = new File([new Uint8Array(audioBuffer)], options.fileName || "audio.webm", { type: options.mimeType || "audio/webm" });
1847
+ const transcription = await client.audio.transcriptions.create({
1848
+ file: audioFile,
1849
+ model: VOICE_MODEL_CONSTANTS.OPENAI_STT,
1850
+ language: options.language || "en",
1851
+ prompt: options.prompt,
1852
+ temperature: options.temperature || 0,
1853
+ response_format: "verbose_json"
1854
+ });
1855
+ const explicitDuration = Number(transcription?.duration) || 0;
1856
+ const segments = Array.isArray(transcription?.segments) ? transcription.segments : [];
1857
+ const segmentsDuration = segments.reduce((max, segment) => {
1858
+ const end = Number(segment?.end);
1859
+ return end > max ? end : max;
1860
+ }, 0);
1861
+ const text = typeof transcription?.text === "string" ? transcription.text : segments.map((segment) => segment?.text || "").join(" ");
1862
+ return { text: text.trim(), durationSeconds: explicitDuration || segmentsDuration };
1863
+ }
1864
+
1865
+ // src/voice/openai-voice-agent.ts
1866
+ var OpenAiVoiceAgent = class {
1867
+ constructor(apiKey) {
1868
+ this.apiKey = apiKey;
1869
+ }
1870
+ apiKey;
1871
+ provider = "openai";
1872
+ ttsModel = VOICE_MODEL_CONSTANTS.OPENAI_TTS;
1873
+ sttModel = VOICE_MODEL_CONSTANTS.OPENAI_STT;
1874
+ async speak(request) {
1875
+ const audio = await generateOpenAiTtsAudio(request.text, this.apiKey, {
1876
+ voice: request.voice,
1877
+ instructions: request.voiceStyle || void 0
1878
+ });
1879
+ const characters = request.text.length;
1880
+ return { audio, costUSD: calculateOpenAiTtsCost(characters), usage: { characters } };
1881
+ }
1882
+ async transcribe(request) {
1883
+ const { text, durationSeconds } = await transcribeWithOpenAi(request.audio, this.apiKey, {
1884
+ language: request.language,
1885
+ prompt: request.prompt,
1886
+ fileName: request.fileName,
1887
+ mimeType: request.mimeType
1888
+ });
1889
+ return { text, durationSeconds, costUSD: calculateOpenAiSttCost(durationSeconds), usage: { audioSeconds: durationSeconds } };
1890
+ }
1891
+ };
1892
+
1893
+ // src/voice/google-tts.ts
1894
+ var import_genai = require("@google/genai");
1895
+ var AUDIO_TOKENS_PER_SECOND = 32;
1896
+ var SAMPLE_RATE = 24e3;
1897
+ var PCM_BYTES_PER_SECOND = SAMPLE_RATE * 2;
1898
+ function buildGoogleTtsPrompt(text, voiceStyle) {
1899
+ const style = voiceStyle?.trim().replace(/[:.!,;\s]+$/, "");
1900
+ if (!style) return text;
1901
+ const isShort = style.split(/\s+/).length <= 3 && !/[.!?,;]/.test(style);
1902
+ return isShort ? `Say ${style}: ${text}` : `${style}:
1903
+ ${text}`;
1904
+ }
1905
+ function pcmToWav(pcmData, sampleRate = SAMPLE_RATE) {
1906
+ const numChannels = 1;
1907
+ const bitsPerSample = 16;
1908
+ const blockAlign = numChannels * (bitsPerSample / 8);
1909
+ const byteRate = sampleRate * blockAlign;
1910
+ const headerSize = 44;
1911
+ const buffer = new ArrayBuffer(headerSize + pcmData.length);
1912
+ const view = new DataView(buffer);
1913
+ const writeString = (offset, str) => {
1914
+ for (let i = 0; i < str.length; i++) view.setUint8(offset + i, str.charCodeAt(i));
1915
+ };
1916
+ writeString(0, "RIFF");
1917
+ view.setUint32(4, 36 + pcmData.length, true);
1918
+ writeString(8, "WAVE");
1919
+ writeString(12, "fmt ");
1920
+ view.setUint32(16, 16, true);
1921
+ view.setUint16(20, 1, true);
1922
+ view.setUint16(22, numChannels, true);
1923
+ view.setUint32(24, sampleRate, true);
1924
+ view.setUint32(28, byteRate, true);
1925
+ view.setUint16(32, blockAlign, true);
1926
+ view.setUint16(34, bitsPerSample, true);
1927
+ writeString(36, "data");
1928
+ view.setUint32(40, pcmData.length, true);
1929
+ new Uint8Array(buffer, headerSize).set(pcmData);
1930
+ return buffer;
1931
+ }
1932
+ async function generateGoogleTtsAudio(text, apiKey, options) {
1933
+ const client = new import_genai.GoogleGenAI({ apiKey });
1934
+ const response = await client.models.generateContent({
1935
+ model: VOICE_MODEL_CONSTANTS.GOOGLE_TTS,
1936
+ contents: [{ parts: [{ text: buildGoogleTtsPrompt(text, options.voiceStyle) }] }],
1937
+ config: {
1938
+ responseModalities: ["AUDIO"],
1939
+ speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: options.voiceName } } }
1940
+ }
1941
+ });
1942
+ const parts = response.candidates?.[0]?.content?.parts ?? [];
1943
+ const audioPart = parts.find((part) => part.inlineData?.mimeType?.startsWith("audio/"));
1944
+ if (!audioPart?.inlineData?.data) {
1945
+ throw new Error("No audio data in Google TTS response");
1946
+ }
1947
+ const pcmData = new Uint8Array(Buffer.from(audioPart.inlineData.data, "base64"));
1948
+ const usageMetadata = response.usageMetadata ?? {};
1949
+ const inputTokens = usageMetadata.promptTokenCount ?? 0;
1950
+ const reportedOutput = usageMetadata.candidatesTokenCount;
1951
+ const outputTokens = reportedOutput && reportedOutput > 0 ? reportedOutput : Math.ceil(pcmData.length / PCM_BYTES_PER_SECOND * AUDIO_TOKENS_PER_SECOND);
1952
+ return { audio: pcmToWav(pcmData), usage: { inputTokens, outputTokens } };
1953
+ }
1954
+
1955
+ // src/voice/google-stt.ts
1956
+ var import_genai2 = require("@google/genai");
1957
+ var GEMINI_AUDIO_TOKENS_PER_SECOND = 25;
1958
+ async function transcribeWithGemini(audioBuffer, apiKey, options = {}) {
1959
+ const client = new import_genai2.GoogleGenAI({ apiKey });
1960
+ const interaction = await client.interactions.create({
1961
+ model: VOICE_MODEL_CONSTANTS.GOOGLE_STT,
1962
+ input: [{ type: "audio", data: Buffer.from(audioBuffer).toString("base64"), mime_type: options.mimeType || "audio/webm" }]
1963
+ });
1964
+ const text = typeof interaction?.output_text === "string" ? interaction.output_text.trim() : "";
1965
+ const usage = interaction?.usage ?? {};
1966
+ const byModality = (rows, modality) => (rows ?? []).filter((r) => r?.modality === modality).reduce((sum, r) => sum + (Number(r?.tokens) || 0), 0);
1967
+ const inputTokens = byModality(usage.input_tokens_by_modality, "audio") || usage.total_input_tokens || 0;
1968
+ const invocationOutput = (usage.model_invocation_token_counts ?? []).reduce((sum, inv) => sum + (inv?.candidates_tokens_details ?? []).reduce((s, d) => s + (Number(d?.tokens) || 0), 0), 0);
1969
+ const outputTokens = usage.total_output_tokens || invocationOutput || Math.ceil(text.length / 4);
1970
+ return { text, durationSeconds: inputTokens / GEMINI_AUDIO_TOKENS_PER_SECOND, usage: { inputTokens, outputTokens } };
1971
+ }
1972
+
1973
+ // src/voice/google-voice-agent.ts
1974
+ var GoogleVoiceAgent = class {
1975
+ constructor(apiKey) {
1976
+ this.apiKey = apiKey;
1977
+ }
1978
+ apiKey;
1979
+ provider = "google";
1980
+ ttsModel = VOICE_MODEL_CONSTANTS.GOOGLE_TTS;
1981
+ sttModel = VOICE_MODEL_CONSTANTS.GOOGLE_STT;
1982
+ async speak(request) {
1983
+ const { audio, usage } = await generateGoogleTtsAudio(request.text, this.apiKey, {
1984
+ voiceName: request.voice,
1985
+ voiceStyle: request.voiceStyle
1986
+ });
1987
+ return { audio, costUSD: calculateGeminiTtsCost(usage), usage };
1988
+ }
1989
+ async transcribe(request) {
1990
+ const { text, durationSeconds, usage } = await transcribeWithGemini(request.audio, this.apiKey, { mimeType: request.mimeType });
1991
+ return { text, durationSeconds, costUSD: calculateGeminiSttCost(usage), usage };
1992
+ }
1993
+ };
1994
+
1995
+ // src/voice/voice-agent-factory.ts
1996
+ var VoiceAgentFactory = class _VoiceAgentFactory {
1997
+ /** Build an agent from a key already resolved by the caller. */
1998
+ static createAgent(provider, apiKey) {
1999
+ switch (provider) {
2000
+ case "openai":
2001
+ return new OpenAiVoiceAgent(apiKey);
2002
+ case "google":
2003
+ return new GoogleVoiceAgent(apiKey);
2004
+ default:
2005
+ throw new Error(`Unknown voice provider: ${provider}`);
2006
+ }
2007
+ }
2008
+ /**
2009
+ * Build an agent from a key map (the shape AgentFactory takes), reading the
2010
+ * provider's key by its API_KEY_CONSTANTS name. Throws when the key is missing
2011
+ * so the host can report a misconfiguration before any SDK is touched.
2012
+ */
2013
+ static createAgentFromKeys(provider, apiKeys) {
2014
+ if (!SUPPORTED_VOICE_PROVIDERS.includes(provider)) {
2015
+ throw new Error(`Unknown voice provider: ${provider}`);
2016
+ }
2017
+ const keyName = VOICE_PROVIDER_API_KEY[provider];
2018
+ const apiKey = apiKeys[keyName];
2019
+ if (!apiKey) {
2020
+ throw new Error(`Missing API key ${keyName} for voice provider ${provider}`);
2021
+ }
2022
+ return _VoiceAgentFactory.createAgent(provider, apiKey);
2023
+ }
2024
+ };
2025
+ function createVoiceAgent(provider, apiKey) {
2026
+ return VoiceAgentFactory.createAgent(provider, apiKey);
2027
+ }
2028
+
1753
2029
  // src/agents/abstract-agent.ts
1754
2030
  var AbstractAgent = class {
1755
2031
  name;
@@ -1896,10 +2172,15 @@ ${msg.content}` };
1896
2172
  };
1897
2173
 
1898
2174
  // src/agents/gpt-5-agent.ts
1899
- var import_openai = __toESM(require("openai"));
2175
+ var import_openai3 = __toESM(require("openai"));
1900
2176
  var import_zod2 = require("openai/helpers/zod");
1901
2177
  var Gpt5Agent = class extends AbstractAgent {
1902
2178
  client;
2179
+ // Routing hint for OpenAI's prefix cache (same scheme as the Mistral/Grok agents): one
2180
+ // key per agent+instruction, so an agent's own calls group together instead of every
2181
+ // agent that shares a static prefix hashing to the same route. Keys influence routing
2182
+ // only; they do not guarantee a hit.
2183
+ promptCacheKey;
1903
2184
  // Log message templates
1904
2185
  logTemplates = {
1905
2186
  error: (name, error) => `Error in ${name} agent: ${error}`
@@ -1912,7 +2193,9 @@ var Gpt5Agent = class extends AbstractAgent {
1912
2193
  };
1913
2194
  constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
1914
2195
  super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
1915
- this.client = new import_openai.default({
2196
+ this.promptCacheKey = stableHashHex(`${name}
2197
+ ${instruction}`);
2198
+ this.client = new import_openai3.default({
1916
2199
  apiKey
1917
2200
  });
1918
2201
  }
@@ -1926,10 +2209,7 @@ var Gpt5Agent = class extends AbstractAgent {
1926
2209
  try {
1927
2210
  this.logAsking(messages);
1928
2211
  this.logMessages(messages);
1929
- const input = [
1930
- `System: ${this.instruction}`,
1931
- ...this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`)
1932
- ].join("\n\n");
2212
+ const input = this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
1933
2213
  const schemaToSend = zodSchema;
1934
2214
  let response;
1935
2215
  try {
@@ -1938,6 +2218,7 @@ var Gpt5Agent = class extends AbstractAgent {
1938
2218
  instructions: this.instruction,
1939
2219
  input,
1940
2220
  max_output_tokens: this.maxOutputTokens,
2221
+ prompt_cache_key: this.promptCacheKey,
1941
2222
  text: {
1942
2223
  format: (0, import_zod2.zodTextFormat)(schemaToSend, "response_schema")
1943
2224
  }
@@ -2016,15 +2297,13 @@ var Gpt5Agent = class extends AbstractAgent {
2016
2297
  try {
2017
2298
  this.logAsking(messages);
2018
2299
  this.logMessages(messages);
2019
- const input = [
2020
- `System: ${this.instruction}`,
2021
- ...this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`)
2022
- ].join("\n\n");
2300
+ const input = this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
2023
2301
  const response = await this.client.responses.create({
2024
2302
  model: this.model,
2025
2303
  instructions: this.instruction,
2026
2304
  input,
2027
- max_output_tokens: this.maxOutputTokens
2305
+ max_output_tokens: this.maxOutputTokens,
2306
+ prompt_cache_key: this.promptCacheKey
2028
2307
  });
2029
2308
  const content = response.output_text;
2030
2309
  if (!content) {
@@ -2080,13 +2359,24 @@ var Gpt5Agent = class extends AbstractAgent {
2080
2359
  var import_sdk = require("@anthropic-ai/sdk");
2081
2360
  var ClaudeAgent = class extends AbstractAgent {
2082
2361
  client;
2362
+ /**
2363
+ * TTL for every breakpoint this agent places (system tiers and the message anchor).
2364
+ * Anthropic bills a 5m write at 1.25x input, a 1h write at 2x, reads at 0.1x, and a read
2365
+ * refreshes the timer on either TTL. Default '1h' because the main consumer runs at human
2366
+ * pace: consecutive calls for one agent measured 12-78 minutes apart, so 5m entries
2367
+ * expired before they were ever read (0-16% hit rate over 30 days, hits only on gaps
2368
+ * under five minutes). Set '5m' for continuous traffic where every call lands inside the
2369
+ * window; there the cheaper write wins. One knob for all breakpoints on purpose: Anthropic
2370
+ * requires 1h entries to precede 5m ones, and a single TTL keeps that trivially true.
2371
+ */
2372
+ cacheTtl = "1h";
2083
2373
  // System-prompt breakpoints, one per cache tier (see CACHE_TIER_MARKER):
2084
- // block 1 shared static rules, byte-identical across all bots and games with the
2085
- // same rule set, so one org-level entry serves everyone and ANY bot's call
2086
- // refreshes its TTL;
2087
- // block 2 per-bot identity + game state + summaries, byte-stable from the start of
2088
- // a game day through the end of its night (deaths/role knowledge/summaries
2089
- // only change in startNewDay), so every call within a day reads it.
2374
+ // block 1 - shared static rules, byte-identical across all bots and games with the
2375
+ // same rule set. Caches are scoped per model, so one entry serves every bot
2376
+ // ON THAT MODEL (not the whole lobby), and any of their calls refreshes it;
2377
+ // block 2 - per-bot identity + game state + summaries, byte-stable between the game's
2378
+ // state writes (a lynch, the night resolution, the summary rewrite, the new
2379
+ // day), so every call inside one of those windows reads it.
2090
2380
  // GM prompts have no marker → single block, same behavior as before. Haiku 4.5 needs a
2091
2381
  // 4096-token cacheable prefix, so tiers below that silently no-op on Haiku — expected.
2092
2382
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
@@ -2094,7 +2384,7 @@ var ClaudeAgent = class extends AbstractAgent {
2094
2384
  get defaultParams() {
2095
2385
  return {
2096
2386
  max_tokens: this.maxOutputTokens,
2097
- system: this.instructionParts.map((part) => ({ type: "text", text: part, cache_control: { type: "ephemeral" } })),
2387
+ system: this.instructionParts.map((part) => ({ type: "text", text: part, cache_control: { type: "ephemeral", ttl: this.cacheTtl } })),
2098
2388
  model: this.model
2099
2389
  };
2100
2390
  }
@@ -2199,14 +2489,14 @@ var ClaudeAgent = class extends AbstractAgent {
2199
2489
  const anchor = messages[messages.length - 2];
2200
2490
  if (typeof anchor.content === "string") {
2201
2491
  if (anchor.content.length > 0) {
2202
- anchor.content = [{ type: "text", text: anchor.content, cache_control: { type: "ephemeral" } }];
2492
+ anchor.content = [{ type: "text", text: anchor.content, cache_control: { type: "ephemeral", ttl: this.cacheTtl } }];
2203
2493
  }
2204
2494
  return;
2205
2495
  }
2206
2496
  for (let i = anchor.content.length - 1; i >= 0; i--) {
2207
2497
  const block = anchor.content[i];
2208
2498
  if (block.type === "text" && block.text.length > 0) {
2209
- block.cache_control = { type: "ephemeral" };
2499
+ block.cache_control = { type: "ephemeral", ttl: this.cacheTtl };
2210
2500
  return;
2211
2501
  }
2212
2502
  }
@@ -2444,7 +2734,7 @@ ${schemaDescription}`;
2444
2734
  };
2445
2735
 
2446
2736
  // src/agents/google-agent.ts
2447
- var import_genai = require("@google/genai");
2737
+ var import_genai3 = require("@google/genai");
2448
2738
  var GoogleAgent = class extends AbstractAgent {
2449
2739
  client;
2450
2740
  defaultConfig = {
@@ -2463,7 +2753,7 @@ var GoogleAgent = class extends AbstractAgent {
2463
2753
  };
2464
2754
  constructor(name, instruction, model, apiKey, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
2465
2755
  super(name, instruction, model, 0.2, enableThinking, agentLoggingConfig);
2466
- this.client = new import_genai.GoogleGenAI({
2756
+ this.client = new import_genai3.GoogleGenAI({
2467
2757
  apiKey
2468
2758
  });
2469
2759
  }
@@ -3024,7 +3314,7 @@ ${schemaDescription}`
3024
3314
  };
3025
3315
 
3026
3316
  // src/agents/deepseek-v2-agent.ts
3027
- var import_openai2 = __toESM(require("openai"));
3317
+ var import_openai4 = __toESM(require("openai"));
3028
3318
  var DeepSeekV2Agent = class extends AbstractAgent {
3029
3319
  client;
3030
3320
  // Log message templates
@@ -3040,7 +3330,7 @@ var DeepSeekV2Agent = class extends AbstractAgent {
3040
3330
  };
3041
3331
  constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
3042
3332
  super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
3043
- this.client = new import_openai2.default({
3333
+ this.client = new import_openai4.default({
3044
3334
  baseURL: "https://api.deepseek.com",
3045
3335
  apiKey
3046
3336
  });
@@ -3232,7 +3522,7 @@ ${schemaDescription}`
3232
3522
  };
3233
3523
 
3234
3524
  // src/agents/grok-agent.ts
3235
- var import_openai3 = require("openai");
3525
+ var import_openai5 = require("openai");
3236
3526
  var GrokAgent = class extends AbstractAgent {
3237
3527
  client;
3238
3528
  // Log message templates
@@ -3249,7 +3539,7 @@ var GrokAgent = class extends AbstractAgent {
3249
3539
  super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
3250
3540
  const convId = stableHashHex(`${name}
3251
3541
  ${instruction}`);
3252
- this.client = new import_openai3.OpenAI({
3542
+ this.client = new import_openai5.OpenAI({
3253
3543
  apiKey,
3254
3544
  baseURL: "https://api.x.ai/v1",
3255
3545
  timeout: 12e5,
@@ -3422,7 +3712,7 @@ ${input[0].content}`;
3422
3712
  };
3423
3713
 
3424
3714
  // src/agents/kimi-agent.ts
3425
- var import_openai4 = require("openai");
3715
+ var import_openai6 = require("openai");
3426
3716
  var KimiAgent = class extends AbstractAgent {
3427
3717
  client;
3428
3718
  // kimi-k3 rejects any temperature other than 1, so we never send the field.
@@ -3449,7 +3739,7 @@ var KimiAgent = class extends AbstractAgent {
3449
3739
  };
3450
3740
  constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
3451
3741
  super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
3452
- this.client = new import_openai4.OpenAI({
3742
+ this.client = new import_openai6.OpenAI({
3453
3743
  apiKey,
3454
3744
  baseURL: "https://api.moonshot.ai/v1"
3455
3745
  });
@@ -3640,7 +3930,7 @@ ${openAIMessages[0].content}`;
3640
3930
  };
3641
3931
 
3642
3932
  // src/agents/glm-agent.ts
3643
- var import_openai5 = require("openai");
3933
+ var import_openai7 = require("openai");
3644
3934
  var GlmAgent = class extends AbstractAgent {
3645
3935
  client;
3646
3936
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
@@ -3666,7 +3956,7 @@ var GlmAgent = class extends AbstractAgent {
3666
3956
  };
3667
3957
  constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
3668
3958
  super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
3669
- this.client = new import_openai5.OpenAI({
3959
+ this.client = new import_openai7.OpenAI({
3670
3960
  apiKey,
3671
3961
  baseURL: "https://api.z.ai/api/paas/v4/"
3672
3962
  });
@@ -3820,7 +4110,7 @@ ${openAIMessages[0].content}`;
3820
4110
  };
3821
4111
 
3822
4112
  // src/agents/fugu-agent.ts
3823
- var import_openai6 = require("openai");
4113
+ var import_openai8 = require("openai");
3824
4114
  var FuguAgent = class extends AbstractAgent {
3825
4115
  client;
3826
4116
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
@@ -3844,7 +4134,7 @@ var FuguAgent = class extends AbstractAgent {
3844
4134
  };
3845
4135
  constructor(name, instruction, model, apiKey, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
3846
4136
  super(name, instruction, model, 1, enableThinking, agentLoggingConfig);
3847
- this.client = new import_openai6.OpenAI({
4137
+ this.client = new import_openai8.OpenAI({
3848
4138
  apiKey,
3849
4139
  baseURL: "https://api.sakana.ai/v1",
3850
4140
  timeout: 12e5
@@ -4011,7 +4301,7 @@ ${schemaDescription}`;
4011
4301
  };
4012
4302
 
4013
4303
  // src/agents/qwen-agent.ts
4014
- var import_openai7 = require("openai");
4304
+ var import_openai9 = require("openai");
4015
4305
  var QwenAgent = class extends AbstractAgent {
4016
4306
  client;
4017
4307
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
@@ -4036,7 +4326,7 @@ var QwenAgent = class extends AbstractAgent {
4036
4326
  };
4037
4327
  constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
4038
4328
  super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
4039
- this.client = new import_openai7.OpenAI({
4329
+ this.client = new import_openai9.OpenAI({
4040
4330
  apiKey,
4041
4331
  baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
4042
4332
  });
@@ -4211,7 +4501,7 @@ ${openAIMessages[0].content}`;
4211
4501
  };
4212
4502
 
4213
4503
  // src/agents/minimax-agent.ts
4214
- var import_openai8 = require("openai");
4504
+ var import_openai10 = require("openai");
4215
4505
  var MiniMaxAgent = class extends AbstractAgent {
4216
4506
  client;
4217
4507
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
@@ -4236,7 +4526,7 @@ var MiniMaxAgent = class extends AbstractAgent {
4236
4526
  };
4237
4527
  constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
4238
4528
  super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
4239
- this.client = new import_openai8.OpenAI({
4529
+ this.client = new import_openai10.OpenAI({
4240
4530
  apiKey,
4241
4531
  baseURL: "https://api.minimax.io/v1"
4242
4532
  });
@@ -4474,10 +4764,12 @@ var AgentFactory = class {
4474
4764
  DeepSeekV2Agent,
4475
4765
  FUGU_REASONING_EFFORTS,
4476
4766
  FuguAgent,
4767
+ GEMINI_AUDIO_TOKENS_PER_SECOND,
4477
4768
  GEMINI_REASONING_EFFORTS,
4478
4769
  GLM_REASONING_EFFORTS,
4479
4770
  GlmAgent,
4480
4771
  GoogleAgent,
4772
+ GoogleVoiceAgent,
4481
4773
  Gpt5Agent,
4482
4774
  GrokAgent,
4483
4775
  KimiAgent,
@@ -4495,23 +4787,35 @@ var AgentFactory = class {
4495
4787
  ModelRefusalError,
4496
4788
  ModelUnavailableError,
4497
4789
  OPENAI_REASONING_EFFORTS,
4790
+ OpenAiVoiceAgent,
4498
4791
  QwenAgent,
4499
4792
  REASONING_EFFORT_SCALE,
4793
+ SUPPORTED_VOICE_PROVIDERS,
4500
4794
  SupportedAiKeyNames,
4501
4795
  SupportedAiModels,
4796
+ VOICE_MODEL_CONSTANTS,
4797
+ VOICE_MODEL_PRICING,
4798
+ VOICE_PROVIDER_API_KEY,
4799
+ VoiceAgentFactory,
4502
4800
  ZodSchemaConverter,
4801
+ buildGoogleTtsPrompt,
4503
4802
  calculateAnthropicCost,
4504
4803
  calculateCost,
4505
4804
  calculateDeepSeekCost,
4805
+ calculateGeminiSttCost,
4806
+ calculateGeminiTtsCost,
4506
4807
  calculateGoogleCost,
4507
4808
  calculateGrokCost,
4508
4809
  calculateKimiCost,
4509
4810
  calculateMistralCost,
4510
4811
  calculateModelCost,
4511
4812
  calculateOpenAICost,
4813
+ calculateOpenAiSttCost,
4814
+ calculateOpenAiTtsCost,
4512
4815
  clampReasoningEffort,
4513
4816
  cleanResponse,
4514
4817
  createCatalog,
4818
+ createVoiceAgent,
4515
4819
  extractAnthropicTokenUsage,
4516
4820
  extractAnthropicTokenUsageFromResponse,
4517
4821
  extractDeepSeekTokenUsage,
@@ -4529,6 +4833,8 @@ var AgentFactory = class {
4529
4833
  extractOpenAITokenUsageFromResponse,
4530
4834
  extractTokenUsage,
4531
4835
  extractUsageAndCalculateCost,
4836
+ generateGoogleTtsAudio,
4837
+ generateOpenAiTtsAudio,
4532
4838
  generateSchemaInstructions,
4533
4839
  getModelConfigByApiName,
4534
4840
  getModelDisplayName,
@@ -4545,6 +4851,7 @@ var AgentFactory = class {
4545
4851
  modelIsFast,
4546
4852
  needsPromptBasedSchema,
4547
4853
  parseAndValidateLlmJson,
4854
+ pcmToWav,
4548
4855
  safeValidateResponse,
4549
4856
  setLlmLogger,
4550
4857
  stableHashHex,
@@ -4556,6 +4863,8 @@ var AgentFactory = class {
4556
4863
  toGeminiEffort,
4557
4864
  toGlmEffort,
4558
4865
  toOpenAIEffort,
4866
+ transcribeWithGemini,
4867
+ transcribeWithOpenAi,
4559
4868
  validateResponse
4560
4869
  });
4561
4870
  //# sourceMappingURL=index.js.map