@corbat-tech/coco 2.25.15 → 2.27.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
@@ -21,7 +21,7 @@ import { glob } from 'glob';
21
21
  import Anthropic from '@anthropic-ai/sdk';
22
22
  import { jsonrepair } from 'jsonrepair';
23
23
  import OpenAI from 'openai';
24
- import { GoogleGenerativeAI, FunctionCallingMode } from '@google/generative-ai';
24
+ import { GoogleGenAI, FunctionCallingConfigMode } from '@google/genai';
25
25
  import 'events';
26
26
  import 'minimatch';
27
27
  import { simpleGit } from 'simple-git';
@@ -782,26 +782,58 @@ var init_flow = __esm({
782
782
  promisify(execFile);
783
783
  }
784
784
  });
785
- async function getADCAccessToken() {
785
+ async function inspectADC() {
786
786
  try {
787
- const { stdout } = await execAsync2("gcloud auth application-default print-access-token", {
787
+ const { stdout } = await execAsync2(PRINT_ACCESS_TOKEN_COMMAND, {
788
788
  timeout: 1e4
789
789
  });
790
790
  const accessToken = stdout.trim();
791
- if (!accessToken) return null;
791
+ if (!accessToken) {
792
+ return {
793
+ status: "missing",
794
+ token: null,
795
+ message: "gcloud ADC is not configured.",
796
+ suggestion: `Run \`${ADC_LOGIN_COMMAND}\` manually, then retry Coco.`
797
+ };
798
+ }
792
799
  const expiresAt = Date.now() + 55 * 60 * 1e3;
793
800
  return {
794
- accessToken,
795
- expiresAt
801
+ status: "ok",
802
+ token: {
803
+ accessToken,
804
+ expiresAt
805
+ }
796
806
  };
797
807
  } catch (error) {
798
808
  const message = error instanceof Error ? error.message : String(error);
809
+ if (message.includes("scope is required but not consented")) {
810
+ return {
811
+ status: "scope_not_consented",
812
+ token: null,
813
+ message: "gcloud ADC exists, but the required Google scope was not consented for this account.",
814
+ suggestion: `For Vertex AI, rerun \`gcloud auth application-default login\` manually. For Gemini API OAuth, follow Google's OAuth guide with your own OAuth client and run \`gcloud auth application-default login --client-id-file=client_secret.json --scopes='${GEMINI_OAUTH_SCOPES}'\`. Otherwise use a Gemini API key in Coco.`
815
+ };
816
+ }
799
817
  if (message.includes("not logged in") || message.includes("no application default credentials")) {
800
- return null;
818
+ return {
819
+ status: "missing",
820
+ token: null,
821
+ message: "No application default credentials were found for gcloud.",
822
+ suggestion: `Run \`${ADC_LOGIN_COMMAND}\` manually, then retry Coco.`
823
+ };
801
824
  }
802
- return null;
825
+ return {
826
+ status: "error",
827
+ token: null,
828
+ message,
829
+ suggestion: `Try \`${PRINT_ACCESS_TOKEN_COMMAND}\` in your terminal to inspect the local ADC state.`
830
+ };
803
831
  }
804
832
  }
833
+ async function getADCAccessToken() {
834
+ const result = await inspectADC();
835
+ return result.token;
836
+ }
805
837
  async function getCachedADCToken() {
806
838
  if (cachedToken && cachedToken.expiresAt && Date.now() < cachedToken.expiresAt) {
807
839
  return cachedToken;
@@ -809,10 +841,16 @@ async function getCachedADCToken() {
809
841
  cachedToken = await getADCAccessToken();
810
842
  return cachedToken;
811
843
  }
812
- var execAsync2, cachedToken;
844
+ var execAsync2, PRINT_ACCESS_TOKEN_COMMAND, ADC_LOGIN_COMMAND, GEMINI_OAUTH_SCOPES, cachedToken;
813
845
  var init_gcloud = __esm({
814
846
  "src/auth/gcloud.ts"() {
815
847
  execAsync2 = promisify(exec);
848
+ PRINT_ACCESS_TOKEN_COMMAND = "gcloud auth application-default print-access-token";
849
+ ADC_LOGIN_COMMAND = "gcloud auth application-default login";
850
+ GEMINI_OAUTH_SCOPES = [
851
+ "https://www.googleapis.com/auth/cloud-platform",
852
+ "https://www.googleapis.com/auth/generative-language.retriever"
853
+ ].join(",");
816
854
  cachedToken = null;
817
855
  }
818
856
  });
@@ -898,6 +936,7 @@ var init_schema = __esm({
898
936
  "codex",
899
937
  "copilot",
900
938
  "gemini",
939
+ "vertex",
901
940
  "kimi",
902
941
  "kimi-code",
903
942
  "lmstudio",
@@ -914,7 +953,9 @@ var init_schema = __esm({
914
953
  model: z.string().default("claude-sonnet-4-6"),
915
954
  maxTokens: z.number().min(1).max(2e5).default(8192),
916
955
  temperature: z.number().min(0).max(2).default(0),
917
- timeout: z.number().min(1e3).default(12e4)
956
+ timeout: z.number().min(1e3).default(12e4),
957
+ project: z.string().optional(),
958
+ location: z.string().optional()
918
959
  });
919
960
  QualityConfigSchema = z.object({
920
961
  minScore: z.number().min(0).max(100).default(85),
@@ -1366,6 +1407,8 @@ function getApiKey(provider) {
1366
1407
  return process.env["OPENAI_API_KEY"];
1367
1408
  case "gemini":
1368
1409
  return process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
1410
+ case "vertex":
1411
+ return void 0;
1369
1412
  case "kimi":
1370
1413
  return process.env["KIMI_API_KEY"] ?? process.env["MOONSHOT_API_KEY"];
1371
1414
  case "kimi-code":
@@ -1414,6 +1457,8 @@ function getBaseUrl(provider) {
1414
1457
  return "https://chatgpt.com/backend-api/codex/responses";
1415
1458
  case "copilot":
1416
1459
  return process.env["COPILOT_BASE_URL"] ?? "https://api.githubcopilot.com";
1460
+ case "vertex":
1461
+ return process.env["VERTEX_BASE_URL"] ?? "https://aiplatform.googleapis.com/v1";
1417
1462
  case "groq":
1418
1463
  return process.env["GROQ_BASE_URL"] ?? "https://api.groq.com/openai/v1";
1419
1464
  case "openrouter":
@@ -1440,6 +1485,8 @@ function getDefaultModel(provider) {
1440
1485
  return process.env["OPENAI_MODEL"] ?? "gpt-5.4-codex";
1441
1486
  case "gemini":
1442
1487
  return process.env["GEMINI_MODEL"] ?? "gemini-3.1-pro-preview";
1488
+ case "vertex":
1489
+ return process.env["VERTEX_MODEL"] ?? "gemini-2.5-pro";
1443
1490
  case "kimi":
1444
1491
  return process.env["KIMI_MODEL"] ?? "kimi-k2.5";
1445
1492
  case "kimi-code":
@@ -1489,6 +1536,7 @@ var init_env = __esm({
1489
1536
  "codex",
1490
1537
  "copilot",
1491
1538
  "gemini",
1539
+ "vertex",
1492
1540
  "kimi",
1493
1541
  "kimi-code",
1494
1542
  "lmstudio",
@@ -16028,19 +16076,14 @@ var CopilotProvider = class extends OpenAIProvider {
16028
16076
 
16029
16077
  // src/providers/gemini.ts
16030
16078
  init_errors();
16031
- init_gcloud();
16032
16079
  var DEFAULT_MODEL5 = "gemini-3.1-pro-preview";
16033
16080
  var CONTEXT_WINDOWS5 = {
16034
- // Gemini 3.1 series (latest)
16035
16081
  "gemini-3.1-pro-preview": 1e6,
16036
16082
  "gemini-3.1-flash-lite-preview": 1e6,
16037
- // Gemini 3 series
16038
16083
  "gemini-3-flash-preview": 1e6,
16039
- // Gemini 2.5 series (production stable)
16040
16084
  "gemini-2.5-pro": 1048576,
16041
16085
  "gemini-2.5-flash": 1048576,
16042
16086
  "gemini-2.5-flash-lite": 1048576,
16043
- // Legacy
16044
16087
  "gemini-1.5-flash": 1e6,
16045
16088
  "gemini-1.5-pro": 2e6
16046
16089
  };
@@ -16049,129 +16092,54 @@ var GeminiProvider = class {
16049
16092
  name = "Google Gemini";
16050
16093
  client = null;
16051
16094
  config = {};
16052
- /**
16053
- * Initialize the provider
16054
- *
16055
- * Authentication priority:
16056
- * 1. API key passed in config (unless it's the ADC marker)
16057
- * 2. GEMINI_API_KEY environment variable
16058
- * 3. GOOGLE_API_KEY environment variable
16059
- * 4. Google Cloud ADC (gcloud auth application-default login)
16060
- */
16061
16095
  async initialize(config) {
16062
16096
  this.config = config;
16063
- const isADCMarker = config.apiKey === "__gcloud_adc__";
16064
- let apiKey = !isADCMarker && config.apiKey ? config.apiKey : process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
16065
- if (!apiKey || isADCMarker) {
16066
- try {
16067
- const adcToken = await getCachedADCToken();
16068
- if (adcToken) {
16069
- apiKey = adcToken.accessToken;
16070
- this.config.useADC = true;
16071
- }
16072
- } catch {
16073
- }
16074
- }
16097
+ const apiKey = config.apiKey ?? process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
16075
16098
  if (!apiKey) {
16076
16099
  throw new ProviderError(
16077
- "Gemini API key not provided. Set GEMINI_API_KEY or run: gcloud auth application-default login",
16100
+ "Gemini Developer API key not provided. Set GEMINI_API_KEY or GOOGLE_API_KEY.",
16078
16101
  { provider: this.id }
16079
16102
  );
16080
16103
  }
16081
- this.client = new GoogleGenerativeAI(apiKey);
16104
+ this.client = new GoogleGenAI({ apiKey });
16082
16105
  }
16083
- /**
16084
- * Refresh ADC token if needed and reinitialize client
16085
- */
16086
- async refreshADCIfNeeded() {
16087
- if (!this.config.useADC) return;
16088
- try {
16089
- const adcToken = await getCachedADCToken();
16090
- if (adcToken) {
16091
- this.client = new GoogleGenerativeAI(adcToken.accessToken);
16092
- }
16093
- } catch {
16094
- }
16095
- }
16096
- /**
16097
- * Send a chat message
16098
- */
16099
16106
  async chat(messages, options) {
16100
16107
  this.ensureInitialized();
16101
- await this.refreshADCIfNeeded();
16102
16108
  try {
16103
- const model = this.client.getGenerativeModel({
16104
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
16105
- generationConfig: {
16106
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
16107
- temperature: options?.temperature ?? this.config.temperature ?? 0,
16108
- stopSequences: options?.stopSequences
16109
- },
16110
- systemInstruction: this.extractSystem(messages, options?.system)
16109
+ const response = await this.client.models.generateContent({
16110
+ model: this.getModel(options?.model),
16111
+ contents: this.convertContents(messages),
16112
+ config: this.buildConfig(messages, options)
16111
16113
  });
16112
- const { history, lastMessage } = this.convertMessages(messages);
16113
- const chat = model.startChat({ history });
16114
- const result = await chat.sendMessage(lastMessage);
16115
- return this.parseResponse(result);
16114
+ return this.parseResponse(response, options?.model);
16116
16115
  } catch (error) {
16117
16116
  throw this.handleError(error);
16118
16117
  }
16119
16118
  }
16120
- /**
16121
- * Send a chat message with tool use
16122
- */
16123
16119
  async chatWithTools(messages, options) {
16124
16120
  this.ensureInitialized();
16125
- await this.refreshADCIfNeeded();
16126
16121
  try {
16127
- const tools = [
16128
- {
16129
- functionDeclarations: this.convertTools(options.tools)
16130
- }
16131
- ];
16132
- const model = this.client.getGenerativeModel({
16133
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
16134
- generationConfig: {
16135
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
16136
- temperature: options?.temperature ?? this.config.temperature ?? 0
16137
- },
16138
- systemInstruction: this.extractSystem(messages, options?.system),
16139
- tools,
16140
- toolConfig: {
16141
- functionCallingConfig: {
16142
- mode: this.convertToolChoice(options.toolChoice)
16143
- }
16144
- }
16122
+ const response = await this.client.models.generateContent({
16123
+ model: this.getModel(options.model),
16124
+ contents: this.convertContents(messages),
16125
+ config: this.buildConfig(messages, options, options.tools, options.toolChoice)
16145
16126
  });
16146
- const { history, lastMessage } = this.convertMessages(messages);
16147
- const chat = model.startChat({ history });
16148
- const result = await chat.sendMessage(lastMessage);
16149
- return this.parseResponseWithTools(result);
16127
+ return this.parseResponseWithTools(response, options.model);
16150
16128
  } catch (error) {
16151
16129
  throw this.handleError(error);
16152
16130
  }
16153
16131
  }
16154
- /**
16155
- * Stream a chat response
16156
- */
16157
16132
  async *stream(messages, options) {
16158
16133
  this.ensureInitialized();
16159
- await this.refreshADCIfNeeded();
16160
16134
  try {
16161
- const model = this.client.getGenerativeModel({
16162
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
16163
- generationConfig: {
16164
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
16165
- temperature: options?.temperature ?? this.config.temperature ?? 0
16166
- },
16167
- systemInstruction: this.extractSystem(messages, options?.system)
16135
+ const stream = await this.client.models.generateContentStream({
16136
+ model: this.getModel(options?.model),
16137
+ contents: this.convertContents(messages),
16138
+ config: this.buildConfig(messages, options)
16168
16139
  });
16169
- const { history, lastMessage } = this.convertMessages(messages);
16170
- const chat = model.startChat({ history });
16171
- const result = await chat.sendMessageStream(lastMessage);
16172
- let streamStopReason;
16173
- for await (const chunk of result.stream) {
16174
- const text = chunk.text();
16140
+ let streamStopReason = "end_turn";
16141
+ for await (const chunk of stream) {
16142
+ const text = chunk.text;
16175
16143
  if (text) {
16176
16144
  yield { type: "text", text };
16177
16145
  }
@@ -16185,116 +16153,76 @@ var GeminiProvider = class {
16185
16153
  throw this.handleError(error);
16186
16154
  }
16187
16155
  }
16188
- /**
16189
- * Stream a chat response with tool use
16190
- */
16191
16156
  async *streamWithTools(messages, options) {
16192
16157
  this.ensureInitialized();
16193
- await this.refreshADCIfNeeded();
16194
16158
  try {
16195
- const tools = [
16196
- {
16197
- functionDeclarations: this.convertTools(options.tools)
16198
- }
16199
- ];
16200
- const model = this.client.getGenerativeModel({
16201
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
16202
- generationConfig: {
16203
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
16204
- temperature: options?.temperature ?? this.config.temperature ?? 0
16205
- },
16206
- systemInstruction: this.extractSystem(messages, options?.system),
16207
- tools,
16208
- toolConfig: {
16209
- functionCallingConfig: {
16210
- mode: this.convertToolChoice(options.toolChoice)
16211
- }
16212
- }
16159
+ const stream = await this.client.models.generateContentStream({
16160
+ model: this.getModel(options.model),
16161
+ contents: this.convertContents(messages),
16162
+ config: this.buildConfig(messages, options, options.tools, options.toolChoice)
16213
16163
  });
16214
- const { history, lastMessage } = this.convertMessages(messages);
16215
- const chat = model.startChat({ history });
16216
- const result = await chat.sendMessageStream(lastMessage);
16217
- let streamStopReason;
16218
- let streamToolCallCounter = 0;
16219
- for await (const chunk of result.stream) {
16220
- const text = chunk.text();
16164
+ let streamStopReason = "end_turn";
16165
+ let fallbackToolCounter = 0;
16166
+ const emittedToolIds = /* @__PURE__ */ new Set();
16167
+ for await (const chunk of stream) {
16168
+ const text = chunk.text;
16221
16169
  if (text) {
16222
16170
  yield { type: "text", text };
16223
16171
  }
16172
+ const functionCalls = this.extractFunctionCalls(chunk);
16173
+ for (const functionCall of functionCalls) {
16174
+ const toolCallId = functionCall.id ?? `gemini_call_${++fallbackToolCounter}`;
16175
+ if (emittedToolIds.has(toolCallId)) continue;
16176
+ emittedToolIds.add(toolCallId);
16177
+ const toolCall = {
16178
+ id: toolCallId,
16179
+ name: functionCall.name ?? "unknown_function",
16180
+ input: functionCall.args ?? {}
16181
+ };
16182
+ yield {
16183
+ type: "tool_use_start",
16184
+ toolCall: {
16185
+ id: toolCall.id,
16186
+ name: toolCall.name
16187
+ }
16188
+ };
16189
+ yield {
16190
+ type: "tool_use_end",
16191
+ toolCall
16192
+ };
16193
+ }
16224
16194
  const finishReason = chunk.candidates?.[0]?.finishReason;
16225
- if (finishReason) {
16195
+ if (functionCalls.length > 0) {
16196
+ streamStopReason = "tool_use";
16197
+ } else if (finishReason) {
16226
16198
  streamStopReason = this.mapFinishReason(finishReason);
16227
16199
  }
16228
- const candidate = chunk.candidates?.[0];
16229
- if (candidate?.content?.parts) {
16230
- for (const part of candidate.content.parts) {
16231
- if ("functionCall" in part && part.functionCall) {
16232
- const funcCall = part.functionCall;
16233
- streamToolCallCounter++;
16234
- const toolCall = {
16235
- id: `gemini_call_${streamToolCallCounter}`,
16236
- name: funcCall.name,
16237
- input: funcCall.args ?? {}
16238
- };
16239
- yield {
16240
- type: "tool_use_start",
16241
- toolCall: {
16242
- id: toolCall.id,
16243
- name: toolCall.name
16244
- }
16245
- };
16246
- yield {
16247
- type: "tool_use_end",
16248
- toolCall
16249
- };
16250
- }
16251
- }
16252
- }
16253
16200
  }
16254
16201
  yield { type: "done", stopReason: streamStopReason };
16255
16202
  } catch (error) {
16256
16203
  throw this.handleError(error);
16257
16204
  }
16258
16205
  }
16259
- /**
16260
- * Count tokens (approximate)
16261
- *
16262
- * Gemini uses a SentencePiece tokenizer. The average ratio varies:
16263
- * - English text: ~4 characters per token
16264
- * - Code: ~3.2 characters per token
16265
- * - Mixed content: ~3.5 characters per token
16266
- *
16267
- * Using 3.5 as the default provides a better estimate for typical
16268
- * coding agent workloads which mix code and natural language.
16269
- */
16270
16206
  countTokens(text) {
16271
16207
  if (!text) return 0;
16272
16208
  return Math.ceil(text.length / 3.5);
16273
16209
  }
16274
- /**
16275
- * Get context window size
16276
- */
16277
16210
  getContextWindow() {
16278
16211
  const model = this.config.model ?? DEFAULT_MODEL5;
16279
16212
  return CONTEXT_WINDOWS5[model] ?? 1e6;
16280
16213
  }
16281
- /**
16282
- * Check if provider is available
16283
- */
16284
16214
  async isAvailable() {
16285
16215
  if (!this.client) return false;
16286
16216
  try {
16287
- const modelName = this.config.model ?? DEFAULT_MODEL5;
16288
- const model = this.client.getGenerativeModel({ model: modelName });
16289
- await model.generateContent("hi");
16217
+ await this.client.models.generateContent({
16218
+ model: this.getModel(),
16219
+ contents: "hi"
16220
+ });
16290
16221
  return true;
16291
16222
  } catch {
16292
16223
  return false;
16293
16224
  }
16294
16225
  }
16295
- /**
16296
- * Ensure client is initialized
16297
- */
16298
16226
  ensureInitialized() {
16299
16227
  if (!this.client) {
16300
16228
  throw new ProviderError("Provider not initialized. Call initialize() first.", {
@@ -16302,13 +16230,24 @@ var GeminiProvider = class {
16302
16230
  });
16303
16231
  }
16304
16232
  }
16305
- /**
16306
- * Extract system prompt from messages array or options.
16307
- *
16308
- * convertMessages() skips system-role messages ("handled via systemInstruction"),
16309
- * but all callers forgot to also pass it via options.system. This helper bridges
16310
- * that gap mirrors the same fix applied to AnthropicProvider.
16311
- */
16233
+ getModel(model) {
16234
+ return model ?? this.config.model ?? DEFAULT_MODEL5;
16235
+ }
16236
+ buildConfig(messages, options, tools, toolChoice) {
16237
+ const config = {
16238
+ maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
16239
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
16240
+ stopSequences: options?.stopSequences,
16241
+ systemInstruction: this.extractSystem(messages, options?.system)
16242
+ };
16243
+ if (tools && tools.length > 0) {
16244
+ config.tools = [{ functionDeclarations: this.convertTools(tools) }];
16245
+ config.toolConfig = {
16246
+ functionCallingConfig: this.convertToolChoice(toolChoice)
16247
+ };
16248
+ }
16249
+ return config;
16250
+ }
16312
16251
  extractSystem(messages, optionsSystem) {
16313
16252
  if (optionsSystem !== void 0) return optionsSystem;
16314
16253
  const systemMsg = messages.find((m) => m.role === "system");
@@ -16317,55 +16256,36 @@ var GeminiProvider = class {
16317
16256
  const text = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
16318
16257
  return text || void 0;
16319
16258
  }
16320
- /**
16321
- * Convert messages to Gemini format
16322
- */
16323
- convertMessages(messages) {
16259
+ convertContents(messages) {
16324
16260
  const toolNameByUseId = this.buildToolUseNameMap(messages);
16325
16261
  const conversation = messages.filter((m) => m.role !== "system");
16326
- const history = [];
16327
- let lastUserMessage = "";
16328
- for (let i = 0; i < conversation.length; i++) {
16329
- const msg = conversation[i];
16330
- const isLastMessage = i === conversation.length - 1;
16262
+ const contents = [];
16263
+ for (const msg of conversation) {
16331
16264
  if (msg.role === "user") {
16332
16265
  if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
16333
- const functionResponses = [];
16266
+ const parts = [];
16334
16267
  for (const block of msg.content) {
16335
16268
  if (block.type === "tool_result") {
16336
16269
  const toolResult = block;
16337
- functionResponses.push({
16270
+ parts.push({
16338
16271
  functionResponse: {
16339
- // Gemini expects the function name in functionResponse.name.
16340
- // Recover it from prior assistant tool_use blocks when possible.
16272
+ id: toolResult.tool_use_id,
16341
16273
  name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
16342
16274
  response: { result: toolResult.content }
16343
16275
  }
16344
16276
  });
16345
16277
  }
16346
16278
  }
16347
- history.push({ role: "function", parts: functionResponses });
16348
- if (isLastMessage) {
16349
- lastUserMessage = "";
16350
- }
16279
+ contents.push({ role: "user", parts });
16351
16280
  } else {
16352
- const parts = this.convertContent(msg.content);
16353
- if (isLastMessage) {
16354
- lastUserMessage = parts;
16355
- } else {
16356
- history.push({ role: "user", parts });
16357
- }
16281
+ contents.push({ role: "user", parts: this.convertContent(msg.content) });
16358
16282
  }
16359
16283
  } else if (msg.role === "assistant") {
16360
- const parts = this.convertContent(msg.content);
16361
- history.push({ role: "model", parts });
16284
+ contents.push({ role: "model", parts: this.convertContent(msg.content) });
16362
16285
  }
16363
16286
  }
16364
- return { history, lastMessage: lastUserMessage };
16287
+ return contents.length > 0 ? contents : [{ role: "user", parts: [{ text: "" }] }];
16365
16288
  }
16366
- /**
16367
- * Build a map from tool_use IDs to function names from assistant history.
16368
- */
16369
16289
  buildToolUseNameMap(messages) {
16370
16290
  const map = /* @__PURE__ */ new Map();
16371
16291
  for (const msg of messages) {
@@ -16378,9 +16298,6 @@ var GeminiProvider = class {
16378
16298
  }
16379
16299
  return map;
16380
16300
  }
16381
- /**
16382
- * Convert content to Gemini parts
16383
- */
16384
16301
  convertContent(content) {
16385
16302
  if (typeof content === "string") {
16386
16303
  return [{ text: content }];
@@ -16390,27 +16307,26 @@ var GeminiProvider = class {
16390
16307
  if (block.type === "text") {
16391
16308
  parts.push({ text: block.text });
16392
16309
  } else if (block.type === "image") {
16393
- const imgBlock = block;
16310
+ const image = block;
16394
16311
  parts.push({
16395
16312
  inlineData: {
16396
- data: imgBlock.source.data,
16397
- mimeType: imgBlock.source.media_type
16313
+ data: image.source.data,
16314
+ mimeType: image.source.media_type
16398
16315
  }
16399
16316
  });
16400
16317
  } else if (block.type === "tool_use") {
16318
+ const toolUse = block;
16401
16319
  parts.push({
16402
16320
  functionCall: {
16403
- name: block.name,
16404
- args: block.input
16321
+ id: toolUse.id,
16322
+ name: toolUse.name,
16323
+ args: toolUse.input
16405
16324
  }
16406
16325
  });
16407
16326
  }
16408
16327
  }
16409
16328
  return parts.length > 0 ? parts : [{ text: "" }];
16410
16329
  }
16411
- /**
16412
- * Convert tools to Gemini format
16413
- */
16414
16330
  convertTools(tools) {
16415
16331
  return tools.map((tool) => ({
16416
16332
  name: tool.name,
@@ -16418,72 +16334,58 @@ var GeminiProvider = class {
16418
16334
  parameters: tool.input_schema
16419
16335
  }));
16420
16336
  }
16421
- /**
16422
- * Convert tool choice to Gemini format
16423
- */
16424
16337
  convertToolChoice(choice) {
16425
- if (!choice || choice === "auto") return FunctionCallingMode.AUTO;
16426
- if (choice === "any") return FunctionCallingMode.ANY;
16427
- return FunctionCallingMode.AUTO;
16338
+ if (!choice || choice === "auto") {
16339
+ return { mode: FunctionCallingConfigMode.AUTO };
16340
+ }
16341
+ if (choice === "any") {
16342
+ return { mode: FunctionCallingConfigMode.ANY };
16343
+ }
16344
+ return {
16345
+ mode: FunctionCallingConfigMode.ANY,
16346
+ allowedFunctionNames: [choice.name]
16347
+ };
16428
16348
  }
16429
- /**
16430
- * Parse response from Gemini
16431
- */
16432
- parseResponse(result) {
16433
- const response = result.response;
16434
- const text = response.text();
16349
+ extractFunctionCalls(response) {
16350
+ if (response.functionCalls && response.functionCalls.length > 0) {
16351
+ return response.functionCalls;
16352
+ }
16353
+ const candidate = response.candidates?.[0];
16354
+ const parts = candidate?.content?.parts ?? [];
16355
+ return parts.filter((part) => !!part.functionCall).map((part) => part.functionCall).filter(Boolean);
16356
+ }
16357
+ parseResponse(response, model) {
16435
16358
  const usage = response.usageMetadata;
16436
16359
  return {
16437
16360
  id: `gemini-${Date.now()}`,
16438
- content: text,
16361
+ content: response.text ?? "",
16439
16362
  stopReason: this.mapFinishReason(response.candidates?.[0]?.finishReason),
16440
16363
  usage: {
16441
16364
  inputTokens: usage?.promptTokenCount ?? 0,
16442
16365
  outputTokens: usage?.candidatesTokenCount ?? 0
16443
16366
  },
16444
- model: this.config.model ?? DEFAULT_MODEL5
16367
+ model: this.getModel(model)
16445
16368
  };
16446
16369
  }
16447
- /**
16448
- * Parse response with tool calls from Gemini
16449
- */
16450
- parseResponseWithTools(result) {
16451
- const response = result.response;
16452
- const candidate = response.candidates?.[0];
16370
+ parseResponseWithTools(response, model) {
16453
16371
  const usage = response.usageMetadata;
16454
- let textContent = "";
16455
- const toolCalls = [];
16456
- if (candidate?.content?.parts) {
16457
- let toolIndex = 0;
16458
- for (const part of candidate.content.parts) {
16459
- if ("text" in part && part.text) {
16460
- textContent += part.text;
16461
- }
16462
- if ("functionCall" in part && part.functionCall) {
16463
- toolIndex++;
16464
- toolCalls.push({
16465
- id: `gemini_call_${toolIndex}`,
16466
- name: part.functionCall.name,
16467
- input: part.functionCall.args ?? {}
16468
- });
16469
- }
16470
- }
16471
- }
16372
+ const toolCalls = this.extractFunctionCalls(response).map((functionCall, index) => ({
16373
+ id: functionCall.id ?? `gemini_call_${index + 1}`,
16374
+ name: functionCall.name ?? "unknown_function",
16375
+ input: functionCall.args ?? {}
16376
+ }));
16472
16377
  return {
16473
16378
  id: `gemini-${Date.now()}`,
16474
- content: textContent,
16475
- stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(candidate?.finishReason),
16379
+ content: response.text ?? "",
16380
+ stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(response.candidates?.[0]?.finishReason),
16476
16381
  usage: {
16477
16382
  inputTokens: usage?.promptTokenCount ?? 0,
16478
16383
  outputTokens: usage?.candidatesTokenCount ?? 0
16479
16384
  },
16480
- model: this.config.model ?? DEFAULT_MODEL5,
16385
+ model: this.getModel(model),
16481
16386
  toolCalls
16482
16387
  };
16483
16388
  }
16484
- /**
16485
- * Map finish reason to our format
16486
- */
16487
16389
  mapFinishReason(reason) {
16488
16390
  switch (reason) {
16489
16391
  case "STOP":
@@ -16498,9 +16400,6 @@ var GeminiProvider = class {
16498
16400
  return "end_turn";
16499
16401
  }
16500
16402
  }
16501
- /**
16502
- * Handle API errors
16503
- */
16504
16403
  handleError(error) {
16505
16404
  const message = error instanceof Error ? error.message : String(error);
16506
16405
  const msg = message.toLowerCase();
@@ -16519,6 +16418,422 @@ var GeminiProvider = class {
16519
16418
  }
16520
16419
  };
16521
16420
 
16421
+ // src/providers/vertex.ts
16422
+ init_errors();
16423
+ init_gcloud();
16424
+ var DEFAULT_MODEL6 = "gemini-2.5-pro";
16425
+ var DEFAULT_BASE_URL = "https://aiplatform.googleapis.com/v1";
16426
+ var DEFAULT_LOCATION = "global";
16427
+ var CONTEXT_WINDOWS6 = {
16428
+ "gemini-2.5-pro": 1048576,
16429
+ "gemini-2.5-flash": 1048576,
16430
+ "gemini-2.5-flash-lite": 1048576,
16431
+ "gemini-2.0-flash-001": 1048576,
16432
+ "gemini-2.0-flash-lite-001": 1048576
16433
+ };
16434
+ var VertexProvider = class {
16435
+ id = "vertex";
16436
+ name = "Google Vertex AI Gemini";
16437
+ config = {};
16438
+ project = "";
16439
+ location = DEFAULT_LOCATION;
16440
+ retryConfig = DEFAULT_RETRY_CONFIG;
16441
+ async initialize(config) {
16442
+ this.config = config;
16443
+ this.project = config.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
16444
+ this.location = config.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? DEFAULT_LOCATION;
16445
+ if (!this.project.trim()) {
16446
+ throw new ProviderError(
16447
+ "Vertex AI project not configured. Set provider.project, VERTEX_PROJECT, or GOOGLE_CLOUD_PROJECT.",
16448
+ { provider: this.id }
16449
+ );
16450
+ }
16451
+ const token = await getCachedADCToken();
16452
+ if (!token) {
16453
+ throw new ProviderError(
16454
+ "Vertex AI ADC is not configured. Run `gcloud auth application-default login` manually, then retry.",
16455
+ { provider: this.id }
16456
+ );
16457
+ }
16458
+ }
16459
+ async chat(messages, options) {
16460
+ this.ensureInitialized();
16461
+ return withRetry(async () => {
16462
+ const response = await this.generateContent(messages, options);
16463
+ return this.parseResponse(response, options?.model);
16464
+ }, this.retryConfig);
16465
+ }
16466
+ async chatWithTools(messages, options) {
16467
+ this.ensureInitialized();
16468
+ return withRetry(async () => {
16469
+ const response = await this.generateContent(
16470
+ messages,
16471
+ options,
16472
+ options.tools,
16473
+ options.toolChoice
16474
+ );
16475
+ return this.parseResponseWithTools(response, options.model);
16476
+ }, this.retryConfig);
16477
+ }
16478
+ async *stream(messages, options) {
16479
+ this.ensureInitialized();
16480
+ const stream = await this.streamGenerateContent(messages, options);
16481
+ let stopReason = "end_turn";
16482
+ for await (const chunk of stream) {
16483
+ const candidate = chunk.candidates?.[0];
16484
+ const parts = candidate?.content?.parts ?? [];
16485
+ for (const part of parts) {
16486
+ if (part.text) {
16487
+ yield { type: "text", text: part.text };
16488
+ }
16489
+ }
16490
+ stopReason = this.mapFinishReason(candidate?.finishReason);
16491
+ }
16492
+ yield { type: "done", stopReason };
16493
+ }
16494
+ async *streamWithTools(messages, options) {
16495
+ this.ensureInitialized();
16496
+ const stream = await this.streamGenerateContent(
16497
+ messages,
16498
+ options,
16499
+ options.tools,
16500
+ options.toolChoice
16501
+ );
16502
+ let stopReason = "end_turn";
16503
+ let streamToolCallCounter = 0;
16504
+ for await (const chunk of stream) {
16505
+ const candidate = chunk.candidates?.[0];
16506
+ const parts = candidate?.content?.parts ?? [];
16507
+ for (const part of parts) {
16508
+ if (part.text) {
16509
+ yield { type: "text", text: part.text };
16510
+ }
16511
+ if (part.functionCall) {
16512
+ streamToolCallCounter++;
16513
+ yield {
16514
+ type: "tool_use_start",
16515
+ toolCall: {
16516
+ id: `vertex_call_${streamToolCallCounter}`,
16517
+ name: part.functionCall.name,
16518
+ input: part.functionCall.args ?? {}
16519
+ }
16520
+ };
16521
+ yield {
16522
+ type: "tool_use_end",
16523
+ toolCall: {
16524
+ id: `vertex_call_${streamToolCallCounter}`,
16525
+ name: part.functionCall.name,
16526
+ input: part.functionCall.args ?? {}
16527
+ }
16528
+ };
16529
+ }
16530
+ }
16531
+ stopReason = parts.some((part) => part.functionCall) ? "tool_use" : this.mapFinishReason(candidate?.finishReason);
16532
+ }
16533
+ yield { type: "done", stopReason };
16534
+ }
16535
+ countTokens(text) {
16536
+ return Math.ceil(text.length / 4);
16537
+ }
16538
+ getContextWindow() {
16539
+ return CONTEXT_WINDOWS6[this.config.model ?? DEFAULT_MODEL6] ?? 1048576;
16540
+ }
16541
+ async isAvailable() {
16542
+ try {
16543
+ await this.generateContent([{ role: "user", content: "hi" }], { maxTokens: 8 });
16544
+ return true;
16545
+ } catch {
16546
+ return false;
16547
+ }
16548
+ }
16549
+ ensureInitialized() {
16550
+ if (!this.project) {
16551
+ throw new ProviderError("Provider not initialized. Call initialize() first.", {
16552
+ provider: this.id
16553
+ });
16554
+ }
16555
+ }
16556
+ getModel(model) {
16557
+ return model ?? this.config.model ?? DEFAULT_MODEL6;
16558
+ }
16559
+ getResolvedBaseUrl() {
16560
+ if (this.config.baseUrl && this.config.baseUrl.trim()) {
16561
+ return this.config.baseUrl;
16562
+ }
16563
+ if (this.location === DEFAULT_LOCATION) {
16564
+ return DEFAULT_BASE_URL;
16565
+ }
16566
+ return `https://${encodeURIComponent(this.location)}-aiplatform.googleapis.com/v1`;
16567
+ }
16568
+ buildEndpoint(model, stream = false) {
16569
+ const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
16570
+ return `${this.getResolvedBaseUrl()}/projects/${encodeURIComponent(this.project)}/locations/${encodeURIComponent(this.location)}/publishers/google/models/${encodeURIComponent(this.getModel(model))}:${action}`;
16571
+ }
16572
+ async getHeaders() {
16573
+ const token = await getCachedADCToken();
16574
+ if (!token) {
16575
+ throw new ProviderError(
16576
+ "Vertex AI ADC token is unavailable. Re-authenticate with gcloud and retry.",
16577
+ { provider: this.id }
16578
+ );
16579
+ }
16580
+ return {
16581
+ "Content-Type": "application/json",
16582
+ Authorization: `Bearer ${token.accessToken}`,
16583
+ "x-goog-user-project": this.project
16584
+ };
16585
+ }
16586
+ extractSystem(messages, optionsSystem) {
16587
+ if (optionsSystem !== void 0) return optionsSystem;
16588
+ const systemMsg = messages.find((m) => m.role === "system");
16589
+ if (!systemMsg) return void 0;
16590
+ if (typeof systemMsg.content === "string") return systemMsg.content;
16591
+ const text = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
16592
+ return text || void 0;
16593
+ }
16594
+ buildToolUseNameMap(messages) {
16595
+ const map = /* @__PURE__ */ new Map();
16596
+ for (const msg of messages) {
16597
+ if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
16598
+ for (const block of msg.content) {
16599
+ if (block.type === "tool_use") {
16600
+ map.set(block.id, block.name);
16601
+ }
16602
+ }
16603
+ }
16604
+ return map;
16605
+ }
16606
+ convertContents(messages) {
16607
+ const toolNameByUseId = this.buildToolUseNameMap(messages);
16608
+ const conversation = messages.filter((m) => m.role !== "system");
16609
+ const contents = [];
16610
+ for (let i = 0; i < conversation.length; i++) {
16611
+ const msg = conversation[i];
16612
+ if (msg.role === "user") {
16613
+ if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
16614
+ const functionResponses = [];
16615
+ for (const block of msg.content) {
16616
+ if (block.type === "tool_result") {
16617
+ const toolResult = block;
16618
+ functionResponses.push({
16619
+ functionResponse: {
16620
+ name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
16621
+ response: { result: toolResult.content }
16622
+ }
16623
+ });
16624
+ }
16625
+ }
16626
+ contents.push({ role: "user", parts: functionResponses });
16627
+ } else {
16628
+ contents.push({ role: "user", parts: this.convertContent(msg.content) });
16629
+ }
16630
+ } else if (msg.role === "assistant") {
16631
+ contents.push({ role: "model", parts: this.convertContent(msg.content) });
16632
+ }
16633
+ }
16634
+ return contents.length > 0 ? contents : [{ role: "user", parts: [{ text: "" }] }];
16635
+ }
16636
+ convertContent(content) {
16637
+ if (typeof content === "string") return [{ text: content }];
16638
+ const parts = [];
16639
+ for (const block of content) {
16640
+ if (block.type === "text") {
16641
+ parts.push({ text: block.text });
16642
+ } else if (block.type === "image") {
16643
+ const image = block;
16644
+ parts.push({
16645
+ inlineData: {
16646
+ data: image.source.data,
16647
+ mimeType: image.source.media_type
16648
+ }
16649
+ });
16650
+ } else if (block.type === "tool_use") {
16651
+ const toolUse = block;
16652
+ parts.push({
16653
+ functionCall: {
16654
+ name: toolUse.name,
16655
+ args: toolUse.input
16656
+ }
16657
+ });
16658
+ }
16659
+ }
16660
+ return parts.length > 0 ? parts : [{ text: "" }];
16661
+ }
16662
+ convertTools(tools) {
16663
+ return [
16664
+ {
16665
+ functionDeclarations: tools.map((tool) => ({
16666
+ name: tool.name,
16667
+ description: tool.description,
16668
+ parameters: tool.input_schema
16669
+ }))
16670
+ }
16671
+ ];
16672
+ }
16673
+ convertToolChoice(choice) {
16674
+ if (!choice || choice === "auto") {
16675
+ return { functionCallingConfig: { mode: "AUTO" } };
16676
+ }
16677
+ if (choice === "any") {
16678
+ return { functionCallingConfig: { mode: "ANY" } };
16679
+ }
16680
+ return { functionCallingConfig: { mode: "ANY", allowedFunctionNames: [choice.name] } };
16681
+ }
16682
+ buildRequestBody(messages, options, tools, toolChoice) {
16683
+ const body = {
16684
+ contents: this.convertContents(messages),
16685
+ generationConfig: {
16686
+ maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
16687
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
16688
+ stopSequences: options?.stopSequences
16689
+ }
16690
+ };
16691
+ const systemInstruction = this.extractSystem(messages, options?.system);
16692
+ if (systemInstruction) {
16693
+ body["systemInstruction"] = {
16694
+ parts: [{ text: systemInstruction }]
16695
+ };
16696
+ }
16697
+ if (tools && tools.length > 0) {
16698
+ body["tools"] = this.convertTools(tools);
16699
+ const convertedChoice = this.convertToolChoice(toolChoice);
16700
+ if (convertedChoice) {
16701
+ body["toolConfig"] = convertedChoice;
16702
+ }
16703
+ }
16704
+ return body;
16705
+ }
16706
+ async generateContent(messages, options, tools, toolChoice) {
16707
+ const response = await fetch(this.buildEndpoint(options?.model), {
16708
+ method: "POST",
16709
+ headers: await this.getHeaders(),
16710
+ body: JSON.stringify(this.buildRequestBody(messages, options, tools, toolChoice)),
16711
+ signal: options?.signal
16712
+ });
16713
+ if (!response.ok) {
16714
+ throw await this.buildHttpError(response);
16715
+ }
16716
+ const data = await response.json();
16717
+ if (data.error?.message) {
16718
+ throw new ProviderError(data.error.message, {
16719
+ provider: this.id,
16720
+ statusCode: data.error.code
16721
+ });
16722
+ }
16723
+ return data;
16724
+ }
16725
+ async *streamGenerateContent(messages, options, tools, toolChoice) {
16726
+ const response = await fetch(this.buildEndpoint(options?.model, true), {
16727
+ method: "POST",
16728
+ headers: await this.getHeaders(),
16729
+ body: JSON.stringify(this.buildRequestBody(messages, options, tools, toolChoice)),
16730
+ signal: options?.signal
16731
+ });
16732
+ if (!response.ok) {
16733
+ throw await this.buildHttpError(response);
16734
+ }
16735
+ if (!response.body) {
16736
+ throw new ProviderError("Vertex AI streaming response body is empty.", {
16737
+ provider: this.id
16738
+ });
16739
+ }
16740
+ const reader = response.body.getReader();
16741
+ const decoder = new TextDecoder();
16742
+ let buffer = "";
16743
+ while (true) {
16744
+ const { value, done } = await reader.read();
16745
+ if (done) break;
16746
+ buffer += decoder.decode(value, { stream: true });
16747
+ while (true) {
16748
+ const eventBoundary = buffer.indexOf("\n\n");
16749
+ if (eventBoundary === -1) break;
16750
+ const rawEvent = buffer.slice(0, eventBoundary);
16751
+ buffer = buffer.slice(eventBoundary + 2);
16752
+ const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter(Boolean);
16753
+ for (const line of dataLines) {
16754
+ if (line === "[DONE]") return;
16755
+ yield JSON.parse(line);
16756
+ }
16757
+ }
16758
+ }
16759
+ const trailing = buffer.trim();
16760
+ if (trailing.startsWith("data:")) {
16761
+ const line = trailing.slice(5).trim();
16762
+ if (line && line !== "[DONE]") {
16763
+ yield JSON.parse(line);
16764
+ }
16765
+ }
16766
+ }
16767
+ parseResponse(response, model) {
16768
+ const candidate = response.candidates?.[0];
16769
+ const text = (candidate?.content?.parts ?? []).filter((part) => part.text).map((part) => part.text).join("");
16770
+ return {
16771
+ id: `vertex-${Date.now()}`,
16772
+ content: text,
16773
+ stopReason: this.mapFinishReason(candidate?.finishReason),
16774
+ usage: {
16775
+ inputTokens: response.usageMetadata?.promptTokenCount ?? 0,
16776
+ outputTokens: response.usageMetadata?.candidatesTokenCount ?? 0
16777
+ },
16778
+ model: this.getModel(model)
16779
+ };
16780
+ }
16781
+ parseResponseWithTools(response, model) {
16782
+ const candidate = response.candidates?.[0];
16783
+ const parts = candidate?.content?.parts ?? [];
16784
+ const toolCalls = [];
16785
+ let textContent = "";
16786
+ let toolIndex = 0;
16787
+ for (const part of parts) {
16788
+ if (part.text) {
16789
+ textContent += part.text;
16790
+ }
16791
+ if (part.functionCall) {
16792
+ toolIndex++;
16793
+ toolCalls.push({
16794
+ id: `vertex_call_${toolIndex}`,
16795
+ name: part.functionCall.name,
16796
+ input: part.functionCall.args ?? {}
16797
+ });
16798
+ }
16799
+ }
16800
+ return {
16801
+ id: `vertex-${Date.now()}`,
16802
+ content: textContent,
16803
+ stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(candidate?.finishReason),
16804
+ usage: {
16805
+ inputTokens: response.usageMetadata?.promptTokenCount ?? 0,
16806
+ outputTokens: response.usageMetadata?.candidatesTokenCount ?? 0
16807
+ },
16808
+ model: this.getModel(model),
16809
+ toolCalls
16810
+ };
16811
+ }
16812
+ mapFinishReason(reason) {
16813
+ switch (reason) {
16814
+ case "STOP":
16815
+ return "end_turn";
16816
+ case "MAX_TOKENS":
16817
+ return "max_tokens";
16818
+ case "SAFETY":
16819
+ case "RECITATION":
16820
+ case "OTHER":
16821
+ return "stop_sequence";
16822
+ default:
16823
+ return "end_turn";
16824
+ }
16825
+ }
16826
+ async buildHttpError(response) {
16827
+ const body = await response.text();
16828
+ const retryable = response.status === 429 || response.status >= 500;
16829
+ return new ProviderError(`Vertex AI error: ${response.status} - ${body}`, {
16830
+ provider: this.id,
16831
+ statusCode: response.status,
16832
+ retryable
16833
+ });
16834
+ }
16835
+ };
16836
+
16522
16837
  // src/providers/circuit-breaker.ts
16523
16838
  init_errors();
16524
16839
  var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
@@ -16820,6 +17135,11 @@ function normalizeProviderModel(model) {
16820
17135
  const trimmed = model.trim();
16821
17136
  return trimmed.length > 0 ? trimmed : void 0;
16822
17137
  }
17138
+ function normalizeOptional(value) {
17139
+ if (typeof value !== "string") return void 0;
17140
+ const trimmed = value.trim();
17141
+ return trimmed.length > 0 ? trimmed : void 0;
17142
+ }
16823
17143
  async function createProvider(type, config = {}) {
16824
17144
  let provider;
16825
17145
  const mergedConfig = {
@@ -16828,7 +17148,11 @@ async function createProvider(type, config = {}) {
16828
17148
  model: normalizeProviderModel(config.model) ?? getDefaultModel(type),
16829
17149
  maxTokens: config.maxTokens,
16830
17150
  temperature: config.temperature,
16831
- timeout: config.timeout
17151
+ timeout: config.timeout,
17152
+ project: normalizeOptional(config.project) ?? (type === "vertex" ? normalizeOptional(
17153
+ process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"]
17154
+ ) : void 0),
17155
+ location: normalizeOptional(config.location) ?? (type === "vertex" ? normalizeOptional(process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"]) : void 0)
16832
17156
  };
16833
17157
  switch (type) {
16834
17158
  case "anthropic":
@@ -16846,6 +17170,9 @@ async function createProvider(type, config = {}) {
16846
17170
  case "gemini":
16847
17171
  provider = new GeminiProvider();
16848
17172
  break;
17173
+ case "vertex":
17174
+ provider = new VertexProvider();
17175
+ break;
16849
17176
  case "kimi":
16850
17177
  provider = createKimiProvider(mergedConfig);
16851
17178
  break;
@@ -24902,7 +25229,7 @@ Examples:
24902
25229
  description = response.choices[0]?.message?.content ?? "No description generated";
24903
25230
  } else if (selectedProvider === "gemini") {
24904
25231
  model = "gemini-2.0-flash";
24905
- const { GoogleGenerativeAI: GoogleGenerativeAI2 } = await import('@google/generative-ai');
25232
+ const { GoogleGenAI: GoogleGenAI2 } = await import('@google/genai');
24906
25233
  const apiKey = process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
24907
25234
  if (!apiKey) {
24908
25235
  throw new ToolError(
@@ -24910,18 +25237,25 @@ Examples:
24910
25237
  { tool: "read_image" }
24911
25238
  );
24912
25239
  }
24913
- const genAI = new GoogleGenerativeAI2(apiKey);
24914
- const genModel = genAI.getGenerativeModel({ model });
24915
- const result = await genModel.generateContent([
24916
- effectivePrompt,
24917
- {
24918
- inlineData: {
24919
- data: base64,
24920
- mimeType
25240
+ const genAI = new GoogleGenAI2({ apiKey });
25241
+ const result = await genAI.models.generateContent({
25242
+ model,
25243
+ contents: [
25244
+ {
25245
+ role: "user",
25246
+ parts: [
25247
+ { text: effectivePrompt },
25248
+ {
25249
+ inlineData: {
25250
+ data: base64,
25251
+ mimeType
25252
+ }
25253
+ }
25254
+ ]
24921
25255
  }
24922
- }
24923
- ]);
24924
- description = result.response.text() ?? "No description generated";
25256
+ ]
25257
+ });
25258
+ description = result.text ?? "No description generated";
24925
25259
  } else {
24926
25260
  throw new ToolError(`Unsupported provider: ${selectedProvider}`, {
24927
25261
  tool: "read_image"
@@ -24933,7 +25267,7 @@ Examples:
24933
25267
  const pkgMap = {
24934
25268
  anthropic: "@anthropic-ai/sdk",
24935
25269
  openai: "openai",
24936
- gemini: "@google/generative-ai"
25270
+ gemini: "@google/genai"
24937
25271
  };
24938
25272
  const pkg = pkgMap[selectedProvider] ?? selectedProvider;
24939
25273
  throw new ToolError(`Provider SDK not installed. Run: pnpm add ${pkg}`, {