@corbat-tech/coco 2.25.14 → 2.27.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/cli/index.js CHANGED
@@ -21,7 +21,7 @@ import { Logger } from 'tslog';
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 matter from 'gray-matter';
26
26
  import { minimatch } from 'minimatch';
27
27
  import hljs from 'highlight.js/lib/core';
@@ -151,6 +151,7 @@ var init_schema = __esm({
151
151
  "codex",
152
152
  "copilot",
153
153
  "gemini",
154
+ "vertex",
154
155
  "kimi",
155
156
  "kimi-code",
156
157
  "lmstudio",
@@ -167,7 +168,9 @@ var init_schema = __esm({
167
168
  model: z.string().default("claude-sonnet-4-6"),
168
169
  maxTokens: z.number().min(1).max(2e5).default(8192),
169
170
  temperature: z.number().min(0).max(2).default(0),
170
- timeout: z.number().min(1e3).default(12e4)
171
+ timeout: z.number().min(1e3).default(12e4),
172
+ project: z.string().optional(),
173
+ location: z.string().optional()
171
174
  });
172
175
  QualityConfigSchema = z.object({
173
176
  minScore: z.number().min(0).max(100).default(85),
@@ -788,8 +791,8 @@ async function requestDeviceCode(provider) {
788
791
  }
789
792
  const contentType = response.headers.get("content-type") || "";
790
793
  if (!contentType.includes("application/json")) {
791
- const text13 = await response.text();
792
- if (text13.includes("<!DOCTYPE") || text13.includes("<html")) {
794
+ const text14 = await response.text();
795
+ if (text14.includes("<!DOCTYPE") || text14.includes("<html")) {
793
796
  throw new Error(
794
797
  "OAuth service returned HTML instead of JSON.\n The service may be temporarily unavailable.\n Please use an API key instead, or try again later."
795
798
  );
@@ -2261,31 +2264,63 @@ async function hasADCCredentials() {
2261
2264
  return false;
2262
2265
  }
2263
2266
  }
2264
- async function getADCAccessToken() {
2267
+ async function inspectADC() {
2265
2268
  try {
2266
- const { stdout } = await execAsync("gcloud auth application-default print-access-token", {
2269
+ const { stdout } = await execAsync(PRINT_ACCESS_TOKEN_COMMAND, {
2267
2270
  timeout: 1e4
2268
2271
  });
2269
2272
  const accessToken = stdout.trim();
2270
- if (!accessToken) return null;
2273
+ if (!accessToken) {
2274
+ return {
2275
+ status: "missing",
2276
+ token: null,
2277
+ message: "gcloud ADC is not configured.",
2278
+ suggestion: `Run \`${ADC_LOGIN_COMMAND}\` manually, then retry Coco.`
2279
+ };
2280
+ }
2271
2281
  const expiresAt = Date.now() + 55 * 60 * 1e3;
2272
2282
  return {
2273
- accessToken,
2274
- expiresAt
2283
+ status: "ok",
2284
+ token: {
2285
+ accessToken,
2286
+ expiresAt
2287
+ }
2275
2288
  };
2276
2289
  } catch (error) {
2277
2290
  const message = error instanceof Error ? error.message : String(error);
2291
+ if (message.includes("scope is required but not consented")) {
2292
+ return {
2293
+ status: "scope_not_consented",
2294
+ token: null,
2295
+ message: "gcloud ADC exists, but the required Google scope was not consented for this account.",
2296
+ 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.`
2297
+ };
2298
+ }
2278
2299
  if (message.includes("not logged in") || message.includes("no application default credentials")) {
2279
- return null;
2300
+ return {
2301
+ status: "missing",
2302
+ token: null,
2303
+ message: "No application default credentials were found for gcloud.",
2304
+ suggestion: `Run \`${ADC_LOGIN_COMMAND}\` manually, then retry Coco.`
2305
+ };
2280
2306
  }
2281
- return null;
2307
+ return {
2308
+ status: "error",
2309
+ token: null,
2310
+ message,
2311
+ suggestion: `Try \`${PRINT_ACCESS_TOKEN_COMMAND}\` in your terminal to inspect the local ADC state.`
2312
+ };
2282
2313
  }
2283
2314
  }
2315
+ async function getADCAccessToken() {
2316
+ const result = await inspectADC();
2317
+ return result.token;
2318
+ }
2284
2319
  async function isADCConfigured() {
2285
2320
  const hasCredentials = await hasADCCredentials();
2286
2321
  if (!hasCredentials) return false;
2287
- const token = await getADCAccessToken();
2288
- return token !== null;
2322
+ const result = await inspectADC();
2323
+ return result.status === "ok" && result.token !== null;
2289
2324
  }
2290
2325
  async function runGcloudADCLogin() {
2291
2326
  try {
@@ -2313,10 +2348,16 @@ async function getCachedADCToken() {
2313
2348
  function clearADCCache() {
2314
2349
  cachedToken = null;
2315
2350
  }
2316
- var execAsync, cachedToken;
2351
+ var execAsync, PRINT_ACCESS_TOKEN_COMMAND, ADC_LOGIN_COMMAND, GEMINI_OAUTH_SCOPES, cachedToken;
2317
2352
  var init_gcloud = __esm({
2318
2353
  "src/auth/gcloud.ts"() {
2319
2354
  execAsync = promisify(exec);
2355
+ PRINT_ACCESS_TOKEN_COMMAND = "gcloud auth application-default print-access-token";
2356
+ ADC_LOGIN_COMMAND = "gcloud auth application-default login";
2357
+ GEMINI_OAUTH_SCOPES = [
2358
+ "https://www.googleapis.com/auth/cloud-platform",
2359
+ "https://www.googleapis.com/auth/generative-language.retriever"
2360
+ ].join(",");
2320
2361
  cachedToken = null;
2321
2362
  }
2322
2363
  });
@@ -2347,6 +2388,7 @@ __export(auth_exports, {
2347
2388
  getValidAccessToken: () => getValidAccessToken,
2348
2389
  getValidCopilotToken: () => getValidCopilotToken,
2349
2390
  hasADCCredentials: () => hasADCCredentials,
2391
+ inspectADC: () => inspectADC,
2350
2392
  isADCConfigured: () => isADCConfigured,
2351
2393
  isGcloudInstalled: () => isGcloudInstalled,
2352
2394
  isOAuthConfigured: () => isOAuthConfigured,
@@ -2461,6 +2503,8 @@ function getApiKey(provider) {
2461
2503
  return process.env["OPENAI_API_KEY"];
2462
2504
  case "gemini":
2463
2505
  return process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
2506
+ case "vertex":
2507
+ return void 0;
2464
2508
  case "kimi":
2465
2509
  return process.env["KIMI_API_KEY"] ?? process.env["MOONSHOT_API_KEY"];
2466
2510
  case "kimi-code":
@@ -2509,6 +2553,8 @@ function getBaseUrl(provider) {
2509
2553
  return "https://chatgpt.com/backend-api/codex/responses";
2510
2554
  case "copilot":
2511
2555
  return process.env["COPILOT_BASE_URL"] ?? "https://api.githubcopilot.com";
2556
+ case "vertex":
2557
+ return process.env["VERTEX_BASE_URL"] ?? "https://aiplatform.googleapis.com/v1";
2512
2558
  case "groq":
2513
2559
  return process.env["GROQ_BASE_URL"] ?? "https://api.groq.com/openai/v1";
2514
2560
  case "openrouter":
@@ -2535,6 +2581,8 @@ function getDefaultModel(provider) {
2535
2581
  return process.env["OPENAI_MODEL"] ?? "gpt-5.4-codex";
2536
2582
  case "gemini":
2537
2583
  return process.env["GEMINI_MODEL"] ?? "gemini-3.1-pro-preview";
2584
+ case "vertex":
2585
+ return process.env["VERTEX_MODEL"] ?? "gemini-2.5-pro";
2538
2586
  case "kimi":
2539
2587
  return process.env["KIMI_MODEL"] ?? "kimi-k2.5";
2540
2588
  case "kimi-code":
@@ -2606,7 +2654,7 @@ async function getLastUsedModel(provider) {
2606
2654
  }
2607
2655
  return void 0;
2608
2656
  }
2609
- async function saveProviderPreference(provider, model) {
2657
+ async function saveProviderPreference(provider, model, options) {
2610
2658
  let config;
2611
2659
  try {
2612
2660
  config = await loadConfig(CONFIG_PATHS.config);
@@ -2648,6 +2696,16 @@ async function saveProviderPreference(provider, model) {
2648
2696
  } else {
2649
2697
  config.provider.model = getDefaultModel(provider);
2650
2698
  }
2699
+ if (options?.project !== void 0) {
2700
+ config.provider.project = options.project;
2701
+ } else if (provider !== "vertex") {
2702
+ delete config.provider.project;
2703
+ }
2704
+ if (options?.location !== void 0) {
2705
+ config.provider.location = options.location;
2706
+ } else if (provider !== "vertex") {
2707
+ delete config.provider.location;
2708
+ }
2651
2709
  await saveConfig(config, void 0, true);
2652
2710
  await updateEnvProvider(provider);
2653
2711
  }
@@ -2782,6 +2840,7 @@ var init_env = __esm({
2782
2840
  "codex",
2783
2841
  "copilot",
2784
2842
  "gemini",
2843
+ "vertex",
2785
2844
  "kimi",
2786
2845
  "kimi-code",
2787
2846
  "lmstudio",
@@ -3311,25 +3370,25 @@ var init_anthropic = __esm({
3311
3370
  *
3312
3371
  * This heuristic analyzes the text to provide a better estimate.
3313
3372
  */
3314
- countTokens(text13) {
3315
- if (!text13) return 0;
3373
+ countTokens(text14) {
3374
+ if (!text14) return 0;
3316
3375
  const codePatterns = /[{}[\]();=<>!&|+\-*/]/g;
3317
3376
  const whitespacePattern = /\s/g;
3318
3377
  const wordPattern = /\b\w+\b/g;
3319
- const codeChars = (text13.match(codePatterns) || []).length;
3320
- const whitespace = (text13.match(whitespacePattern) || []).length;
3321
- const words = (text13.match(wordPattern) || []).length;
3322
- const isCodeLike = codeChars > text13.length * 0.05;
3378
+ const codeChars = (text14.match(codePatterns) || []).length;
3379
+ const whitespace = (text14.match(whitespacePattern) || []).length;
3380
+ const words = (text14.match(wordPattern) || []).length;
3381
+ const isCodeLike = codeChars > text14.length * 0.05;
3323
3382
  let charsPerToken;
3324
3383
  if (isCodeLike) {
3325
3384
  charsPerToken = 3.5;
3326
- } else if (whitespace > text13.length * 0.3) {
3385
+ } else if (whitespace > text14.length * 0.3) {
3327
3386
  charsPerToken = 5;
3328
3387
  } else {
3329
3388
  charsPerToken = 4.5;
3330
3389
  }
3331
3390
  const wordBasedEstimate = words * 1.3;
3332
- const charBasedEstimate = text13.length / charsPerToken;
3391
+ const charBasedEstimate = text14.length / charsPerToken;
3333
3392
  return Math.ceil((wordBasedEstimate + charBasedEstimate) / 2);
3334
3393
  }
3335
3394
  /**
@@ -3378,8 +3437,8 @@ var init_anthropic = __esm({
3378
3437
  const systemMsg = messages.find((m) => m.role === "system");
3379
3438
  if (!systemMsg) return void 0;
3380
3439
  if (typeof systemMsg.content === "string") return systemMsg.content;
3381
- const text13 = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
3382
- return text13 || void 0;
3440
+ const text14 = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
3441
+ return text14 || void 0;
3383
3442
  }
3384
3443
  /**
3385
3444
  * Convert messages to Anthropic format
@@ -3567,15 +3626,15 @@ var init_tool_call_normalizer = __esm({
3567
3626
  if (delta.function?.name) {
3568
3627
  builder.name = delta.function.name;
3569
3628
  }
3570
- const text13 = delta.function?.arguments ?? "";
3571
- if (!text13) return { started };
3572
- builder.arguments += text13;
3629
+ const text14 = delta.function?.arguments ?? "";
3630
+ if (!text14) return { started };
3631
+ builder.arguments += text14;
3573
3632
  return {
3574
3633
  started,
3575
3634
  argumentDelta: {
3576
3635
  id: builder.id,
3577
3636
  name: builder.name,
3578
- text: text13
3637
+ text: text14
3579
3638
  }
3580
3639
  };
3581
3640
  }
@@ -4164,23 +4223,23 @@ var init_openai = __esm({
4164
4223
  * For accurate counting, use the model's native tokenizer.
4165
4224
  * This heuristic provides a reasonable estimate without dependencies.
4166
4225
  */
4167
- countTokens(text13) {
4168
- if (!text13) return 0;
4226
+ countTokens(text14) {
4227
+ if (!text14) return 0;
4169
4228
  const codePatterns = /[{}[\]();=<>!&|+\-*/]/g;
4170
4229
  const whitespacePattern = /\s/g;
4171
4230
  const wordPattern = /\b\w+\b/g;
4172
4231
  const nonAsciiPattern = /[^\x00-\x7F]/g;
4173
- const codeChars = (text13.match(codePatterns) || []).length;
4174
- const whitespace = (text13.match(whitespacePattern) || []).length;
4175
- const words = (text13.match(wordPattern) || []).length;
4176
- const nonAscii = (text13.match(nonAsciiPattern) || []).length;
4177
- const isCodeLike = codeChars > text13.length * 0.05;
4232
+ const codeChars = (text14.match(codePatterns) || []).length;
4233
+ const whitespace = (text14.match(whitespacePattern) || []).length;
4234
+ const words = (text14.match(wordPattern) || []).length;
4235
+ const nonAscii = (text14.match(nonAsciiPattern) || []).length;
4236
+ const isCodeLike = codeChars > text14.length * 0.05;
4178
4237
  const isLocal = this.isLocalModel();
4179
4238
  let charsPerToken;
4180
4239
  if (isLocal) {
4181
4240
  if (isCodeLike) {
4182
4241
  charsPerToken = 3.2;
4183
- } else if (nonAscii > text13.length * 0.1) {
4242
+ } else if (nonAscii > text14.length * 0.1) {
4184
4243
  charsPerToken = 2;
4185
4244
  } else {
4186
4245
  charsPerToken = 3.5;
@@ -4188,7 +4247,7 @@ var init_openai = __esm({
4188
4247
  } else {
4189
4248
  if (isCodeLike) {
4190
4249
  charsPerToken = 3.3;
4191
- } else if (whitespace > text13.length * 0.3) {
4250
+ } else if (whitespace > text14.length * 0.3) {
4192
4251
  charsPerToken = 4.5;
4193
4252
  } else {
4194
4253
  charsPerToken = 4;
@@ -4196,7 +4255,7 @@ var init_openai = __esm({
4196
4255
  }
4197
4256
  const tokensPerWord = isLocal ? 1.4 : 1.3;
4198
4257
  const wordBasedEstimate = words * tokensPerWord;
4199
- const charBasedEstimate = text13.length / charsPerToken;
4258
+ const charBasedEstimate = text14.length / charsPerToken;
4200
4259
  const weight = isCodeLike ? 0.7 : 0.5;
4201
4260
  return Math.ceil(charBasedEstimate * weight + wordBasedEstimate * (1 - weight));
4202
4261
  }
@@ -4977,8 +5036,8 @@ var init_codex = __esm({
4977
5036
  * Count tokens in text (approximate)
4978
5037
  * Uses GPT-4 approximation: ~4 chars per token
4979
5038
  */
4980
- countTokens(text13) {
4981
- return Math.ceil(text13.length / 4);
5039
+ countTokens(text14) {
5040
+ return Math.ceil(text14.length / 4);
4982
5041
  }
4983
5042
  /**
4984
5043
  * Check if provider is available (has valid OAuth tokens)
@@ -5646,9 +5705,9 @@ var init_copilot2 = __esm({
5646
5705
  /**
5647
5706
  * Count tokens (approximate — Copilot models vary in tokenizer)
5648
5707
  */
5649
- countTokens(text13) {
5650
- if (!text13) return 0;
5651
- return Math.ceil(text13.length / 3.5);
5708
+ countTokens(text14) {
5709
+ if (!text14) return 0;
5710
+ return Math.ceil(text14.length / 3.5);
5652
5711
  }
5653
5712
  /**
5654
5713
  * Get context window for the current model
@@ -5683,19 +5742,14 @@ var DEFAULT_MODEL5, CONTEXT_WINDOWS5, GeminiProvider;
5683
5742
  var init_gemini = __esm({
5684
5743
  "src/providers/gemini.ts"() {
5685
5744
  init_errors();
5686
- init_gcloud();
5687
5745
  DEFAULT_MODEL5 = "gemini-3.1-pro-preview";
5688
5746
  CONTEXT_WINDOWS5 = {
5689
- // Gemini 3.1 series (latest)
5690
5747
  "gemini-3.1-pro-preview": 1e6,
5691
5748
  "gemini-3.1-flash-lite-preview": 1e6,
5692
- // Gemini 3 series
5693
5749
  "gemini-3-flash-preview": 1e6,
5694
- // Gemini 2.5 series (production stable)
5695
5750
  "gemini-2.5-pro": 1048576,
5696
5751
  "gemini-2.5-flash": 1048576,
5697
5752
  "gemini-2.5-flash-lite": 1048576,
5698
- // Legacy
5699
5753
  "gemini-1.5-flash": 1e6,
5700
5754
  "gemini-1.5-pro": 2e6
5701
5755
  };
@@ -5704,131 +5758,56 @@ var init_gemini = __esm({
5704
5758
  name = "Google Gemini";
5705
5759
  client = null;
5706
5760
  config = {};
5707
- /**
5708
- * Initialize the provider
5709
- *
5710
- * Authentication priority:
5711
- * 1. API key passed in config (unless it's the ADC marker)
5712
- * 2. GEMINI_API_KEY environment variable
5713
- * 3. GOOGLE_API_KEY environment variable
5714
- * 4. Google Cloud ADC (gcloud auth application-default login)
5715
- */
5716
5761
  async initialize(config) {
5717
5762
  this.config = config;
5718
- const isADCMarker = config.apiKey === "__gcloud_adc__";
5719
- let apiKey = !isADCMarker && config.apiKey ? config.apiKey : process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
5720
- if (!apiKey || isADCMarker) {
5721
- try {
5722
- const adcToken = await getCachedADCToken();
5723
- if (adcToken) {
5724
- apiKey = adcToken.accessToken;
5725
- this.config.useADC = true;
5726
- }
5727
- } catch {
5728
- }
5729
- }
5763
+ const apiKey = config.apiKey ?? process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
5730
5764
  if (!apiKey) {
5731
5765
  throw new ProviderError(
5732
- "Gemini API key not provided. Set GEMINI_API_KEY or run: gcloud auth application-default login",
5766
+ "Gemini Developer API key not provided. Set GEMINI_API_KEY or GOOGLE_API_KEY.",
5733
5767
  { provider: this.id }
5734
5768
  );
5735
5769
  }
5736
- this.client = new GoogleGenerativeAI(apiKey);
5770
+ this.client = new GoogleGenAI({ apiKey });
5737
5771
  }
5738
- /**
5739
- * Refresh ADC token if needed and reinitialize client
5740
- */
5741
- async refreshADCIfNeeded() {
5742
- if (!this.config.useADC) return;
5743
- try {
5744
- const adcToken = await getCachedADCToken();
5745
- if (adcToken) {
5746
- this.client = new GoogleGenerativeAI(adcToken.accessToken);
5747
- }
5748
- } catch {
5749
- }
5750
- }
5751
- /**
5752
- * Send a chat message
5753
- */
5754
5772
  async chat(messages, options) {
5755
5773
  this.ensureInitialized();
5756
- await this.refreshADCIfNeeded();
5757
5774
  try {
5758
- const model = this.client.getGenerativeModel({
5759
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
5760
- generationConfig: {
5761
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
5762
- temperature: options?.temperature ?? this.config.temperature ?? 0,
5763
- stopSequences: options?.stopSequences
5764
- },
5765
- systemInstruction: this.extractSystem(messages, options?.system)
5775
+ const response = await this.client.models.generateContent({
5776
+ model: this.getModel(options?.model),
5777
+ contents: this.convertContents(messages),
5778
+ config: this.buildConfig(messages, options)
5766
5779
  });
5767
- const { history, lastMessage } = this.convertMessages(messages);
5768
- const chat = model.startChat({ history });
5769
- const result = await chat.sendMessage(lastMessage);
5770
- return this.parseResponse(result);
5780
+ return this.parseResponse(response, options?.model);
5771
5781
  } catch (error) {
5772
5782
  throw this.handleError(error);
5773
5783
  }
5774
5784
  }
5775
- /**
5776
- * Send a chat message with tool use
5777
- */
5778
5785
  async chatWithTools(messages, options) {
5779
5786
  this.ensureInitialized();
5780
- await this.refreshADCIfNeeded();
5781
5787
  try {
5782
- const tools = [
5783
- {
5784
- functionDeclarations: this.convertTools(options.tools)
5785
- }
5786
- ];
5787
- const model = this.client.getGenerativeModel({
5788
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
5789
- generationConfig: {
5790
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
5791
- temperature: options?.temperature ?? this.config.temperature ?? 0
5792
- },
5793
- systemInstruction: this.extractSystem(messages, options?.system),
5794
- tools,
5795
- toolConfig: {
5796
- functionCallingConfig: {
5797
- mode: this.convertToolChoice(options.toolChoice)
5798
- }
5799
- }
5788
+ const response = await this.client.models.generateContent({
5789
+ model: this.getModel(options.model),
5790
+ contents: this.convertContents(messages),
5791
+ config: this.buildConfig(messages, options, options.tools, options.toolChoice)
5800
5792
  });
5801
- const { history, lastMessage } = this.convertMessages(messages);
5802
- const chat = model.startChat({ history });
5803
- const result = await chat.sendMessage(lastMessage);
5804
- return this.parseResponseWithTools(result);
5793
+ return this.parseResponseWithTools(response, options.model);
5805
5794
  } catch (error) {
5806
5795
  throw this.handleError(error);
5807
5796
  }
5808
5797
  }
5809
- /**
5810
- * Stream a chat response
5811
- */
5812
5798
  async *stream(messages, options) {
5813
5799
  this.ensureInitialized();
5814
- await this.refreshADCIfNeeded();
5815
5800
  try {
5816
- const model = this.client.getGenerativeModel({
5817
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
5818
- generationConfig: {
5819
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
5820
- temperature: options?.temperature ?? this.config.temperature ?? 0
5821
- },
5822
- systemInstruction: this.extractSystem(messages, options?.system)
5801
+ const stream = await this.client.models.generateContentStream({
5802
+ model: this.getModel(options?.model),
5803
+ contents: this.convertContents(messages),
5804
+ config: this.buildConfig(messages, options)
5823
5805
  });
5824
- const { history, lastMessage } = this.convertMessages(messages);
5825
- const chat = model.startChat({ history });
5826
- const result = await chat.sendMessageStream(lastMessage);
5827
- let streamStopReason;
5828
- for await (const chunk of result.stream) {
5829
- const text13 = chunk.text();
5830
- if (text13) {
5831
- yield { type: "text", text: text13 };
5806
+ let streamStopReason = "end_turn";
5807
+ for await (const chunk of stream) {
5808
+ const text14 = chunk.text;
5809
+ if (text14) {
5810
+ yield { type: "text", text: text14 };
5832
5811
  }
5833
5812
  const finishReason = chunk.candidates?.[0]?.finishReason;
5834
5813
  if (finishReason) {
@@ -5840,116 +5819,76 @@ var init_gemini = __esm({
5840
5819
  throw this.handleError(error);
5841
5820
  }
5842
5821
  }
5843
- /**
5844
- * Stream a chat response with tool use
5845
- */
5846
5822
  async *streamWithTools(messages, options) {
5847
5823
  this.ensureInitialized();
5848
- await this.refreshADCIfNeeded();
5849
5824
  try {
5850
- const tools = [
5851
- {
5852
- functionDeclarations: this.convertTools(options.tools)
5853
- }
5854
- ];
5855
- const model = this.client.getGenerativeModel({
5856
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
5857
- generationConfig: {
5858
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
5859
- temperature: options?.temperature ?? this.config.temperature ?? 0
5860
- },
5861
- systemInstruction: this.extractSystem(messages, options?.system),
5862
- tools,
5863
- toolConfig: {
5864
- functionCallingConfig: {
5865
- mode: this.convertToolChoice(options.toolChoice)
5866
- }
5867
- }
5825
+ const stream = await this.client.models.generateContentStream({
5826
+ model: this.getModel(options.model),
5827
+ contents: this.convertContents(messages),
5828
+ config: this.buildConfig(messages, options, options.tools, options.toolChoice)
5868
5829
  });
5869
- const { history, lastMessage } = this.convertMessages(messages);
5870
- const chat = model.startChat({ history });
5871
- const result = await chat.sendMessageStream(lastMessage);
5872
- let streamStopReason;
5873
- let streamToolCallCounter = 0;
5874
- for await (const chunk of result.stream) {
5875
- const text13 = chunk.text();
5876
- if (text13) {
5877
- yield { type: "text", text: text13 };
5830
+ let streamStopReason = "end_turn";
5831
+ let fallbackToolCounter = 0;
5832
+ const emittedToolIds = /* @__PURE__ */ new Set();
5833
+ for await (const chunk of stream) {
5834
+ const text14 = chunk.text;
5835
+ if (text14) {
5836
+ yield { type: "text", text: text14 };
5837
+ }
5838
+ const functionCalls = this.extractFunctionCalls(chunk);
5839
+ for (const functionCall of functionCalls) {
5840
+ const toolCallId = functionCall.id ?? `gemini_call_${++fallbackToolCounter}`;
5841
+ if (emittedToolIds.has(toolCallId)) continue;
5842
+ emittedToolIds.add(toolCallId);
5843
+ const toolCall = {
5844
+ id: toolCallId,
5845
+ name: functionCall.name ?? "unknown_function",
5846
+ input: functionCall.args ?? {}
5847
+ };
5848
+ yield {
5849
+ type: "tool_use_start",
5850
+ toolCall: {
5851
+ id: toolCall.id,
5852
+ name: toolCall.name
5853
+ }
5854
+ };
5855
+ yield {
5856
+ type: "tool_use_end",
5857
+ toolCall
5858
+ };
5878
5859
  }
5879
5860
  const finishReason = chunk.candidates?.[0]?.finishReason;
5880
- if (finishReason) {
5861
+ if (functionCalls.length > 0) {
5862
+ streamStopReason = "tool_use";
5863
+ } else if (finishReason) {
5881
5864
  streamStopReason = this.mapFinishReason(finishReason);
5882
5865
  }
5883
- const candidate = chunk.candidates?.[0];
5884
- if (candidate?.content?.parts) {
5885
- for (const part of candidate.content.parts) {
5886
- if ("functionCall" in part && part.functionCall) {
5887
- const funcCall = part.functionCall;
5888
- streamToolCallCounter++;
5889
- const toolCall = {
5890
- id: `gemini_call_${streamToolCallCounter}`,
5891
- name: funcCall.name,
5892
- input: funcCall.args ?? {}
5893
- };
5894
- yield {
5895
- type: "tool_use_start",
5896
- toolCall: {
5897
- id: toolCall.id,
5898
- name: toolCall.name
5899
- }
5900
- };
5901
- yield {
5902
- type: "tool_use_end",
5903
- toolCall
5904
- };
5905
- }
5906
- }
5907
- }
5908
5866
  }
5909
5867
  yield { type: "done", stopReason: streamStopReason };
5910
5868
  } catch (error) {
5911
5869
  throw this.handleError(error);
5912
5870
  }
5913
5871
  }
5914
- /**
5915
- * Count tokens (approximate)
5916
- *
5917
- * Gemini uses a SentencePiece tokenizer. The average ratio varies:
5918
- * - English text: ~4 characters per token
5919
- * - Code: ~3.2 characters per token
5920
- * - Mixed content: ~3.5 characters per token
5921
- *
5922
- * Using 3.5 as the default provides a better estimate for typical
5923
- * coding agent workloads which mix code and natural language.
5924
- */
5925
- countTokens(text13) {
5926
- if (!text13) return 0;
5927
- return Math.ceil(text13.length / 3.5);
5872
+ countTokens(text14) {
5873
+ if (!text14) return 0;
5874
+ return Math.ceil(text14.length / 3.5);
5928
5875
  }
5929
- /**
5930
- * Get context window size
5931
- */
5932
5876
  getContextWindow() {
5933
5877
  const model = this.config.model ?? DEFAULT_MODEL5;
5934
5878
  return CONTEXT_WINDOWS5[model] ?? 1e6;
5935
5879
  }
5936
- /**
5937
- * Check if provider is available
5938
- */
5939
5880
  async isAvailable() {
5940
5881
  if (!this.client) return false;
5941
5882
  try {
5942
- const modelName = this.config.model ?? DEFAULT_MODEL5;
5943
- const model = this.client.getGenerativeModel({ model: modelName });
5944
- await model.generateContent("hi");
5883
+ await this.client.models.generateContent({
5884
+ model: this.getModel(),
5885
+ contents: "hi"
5886
+ });
5945
5887
  return true;
5946
5888
  } catch {
5947
5889
  return false;
5948
5890
  }
5949
5891
  }
5950
- /**
5951
- * Ensure client is initialized
5952
- */
5953
5892
  ensureInitialized() {
5954
5893
  if (!this.client) {
5955
5894
  throw new ProviderError("Provider not initialized. Call initialize() first.", {
@@ -5957,70 +5896,62 @@ var init_gemini = __esm({
5957
5896
  });
5958
5897
  }
5959
5898
  }
5960
- /**
5961
- * Extract system prompt from messages array or options.
5962
- *
5963
- * convertMessages() skips system-role messages ("handled via systemInstruction"),
5964
- * but all callers forgot to also pass it via options.system. This helper bridges
5965
- * that gap mirrors the same fix applied to AnthropicProvider.
5966
- */
5899
+ getModel(model) {
5900
+ return model ?? this.config.model ?? DEFAULT_MODEL5;
5901
+ }
5902
+ buildConfig(messages, options, tools, toolChoice) {
5903
+ const config = {
5904
+ maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
5905
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
5906
+ stopSequences: options?.stopSequences,
5907
+ systemInstruction: this.extractSystem(messages, options?.system)
5908
+ };
5909
+ if (tools && tools.length > 0) {
5910
+ config.tools = [{ functionDeclarations: this.convertTools(tools) }];
5911
+ config.toolConfig = {
5912
+ functionCallingConfig: this.convertToolChoice(toolChoice)
5913
+ };
5914
+ }
5915
+ return config;
5916
+ }
5967
5917
  extractSystem(messages, optionsSystem) {
5968
5918
  if (optionsSystem !== void 0) return optionsSystem;
5969
5919
  const systemMsg = messages.find((m) => m.role === "system");
5970
5920
  if (!systemMsg) return void 0;
5971
5921
  if (typeof systemMsg.content === "string") return systemMsg.content;
5972
- const text13 = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
5973
- return text13 || void 0;
5922
+ const text14 = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
5923
+ return text14 || void 0;
5974
5924
  }
5975
- /**
5976
- * Convert messages to Gemini format
5977
- */
5978
- convertMessages(messages) {
5925
+ convertContents(messages) {
5979
5926
  const toolNameByUseId = this.buildToolUseNameMap(messages);
5980
5927
  const conversation = messages.filter((m) => m.role !== "system");
5981
- const history = [];
5982
- let lastUserMessage = "";
5983
- for (let i = 0; i < conversation.length; i++) {
5984
- const msg = conversation[i];
5985
- const isLastMessage = i === conversation.length - 1;
5928
+ const contents = [];
5929
+ for (const msg of conversation) {
5986
5930
  if (msg.role === "user") {
5987
5931
  if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
5988
- const functionResponses = [];
5932
+ const parts = [];
5989
5933
  for (const block of msg.content) {
5990
5934
  if (block.type === "tool_result") {
5991
5935
  const toolResult = block;
5992
- functionResponses.push({
5936
+ parts.push({
5993
5937
  functionResponse: {
5994
- // Gemini expects the function name in functionResponse.name.
5995
- // Recover it from prior assistant tool_use blocks when possible.
5938
+ id: toolResult.tool_use_id,
5996
5939
  name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
5997
5940
  response: { result: toolResult.content }
5998
5941
  }
5999
5942
  });
6000
5943
  }
6001
5944
  }
6002
- history.push({ role: "function", parts: functionResponses });
6003
- if (isLastMessage) {
6004
- lastUserMessage = "";
6005
- }
5945
+ contents.push({ role: "user", parts });
6006
5946
  } else {
6007
- const parts = this.convertContent(msg.content);
6008
- if (isLastMessage) {
6009
- lastUserMessage = parts;
6010
- } else {
6011
- history.push({ role: "user", parts });
6012
- }
5947
+ contents.push({ role: "user", parts: this.convertContent(msg.content) });
6013
5948
  }
6014
5949
  } else if (msg.role === "assistant") {
6015
- const parts = this.convertContent(msg.content);
6016
- history.push({ role: "model", parts });
5950
+ contents.push({ role: "model", parts: this.convertContent(msg.content) });
6017
5951
  }
6018
5952
  }
6019
- return { history, lastMessage: lastUserMessage };
5953
+ return contents.length > 0 ? contents : [{ role: "user", parts: [{ text: "" }] }];
6020
5954
  }
6021
- /**
6022
- * Build a map from tool_use IDs to function names from assistant history.
6023
- */
6024
5955
  buildToolUseNameMap(messages) {
6025
5956
  const map = /* @__PURE__ */ new Map();
6026
5957
  for (const msg of messages) {
@@ -6033,9 +5964,6 @@ var init_gemini = __esm({
6033
5964
  }
6034
5965
  return map;
6035
5966
  }
6036
- /**
6037
- * Convert content to Gemini parts
6038
- */
6039
5967
  convertContent(content) {
6040
5968
  if (typeof content === "string") {
6041
5969
  return [{ text: content }];
@@ -6045,27 +5973,26 @@ var init_gemini = __esm({
6045
5973
  if (block.type === "text") {
6046
5974
  parts.push({ text: block.text });
6047
5975
  } else if (block.type === "image") {
6048
- const imgBlock = block;
5976
+ const image = block;
6049
5977
  parts.push({
6050
5978
  inlineData: {
6051
- data: imgBlock.source.data,
6052
- mimeType: imgBlock.source.media_type
5979
+ data: image.source.data,
5980
+ mimeType: image.source.media_type
6053
5981
  }
6054
5982
  });
6055
5983
  } else if (block.type === "tool_use") {
5984
+ const toolUse = block;
6056
5985
  parts.push({
6057
5986
  functionCall: {
6058
- name: block.name,
6059
- args: block.input
5987
+ id: toolUse.id,
5988
+ name: toolUse.name,
5989
+ args: toolUse.input
6060
5990
  }
6061
5991
  });
6062
5992
  }
6063
5993
  }
6064
5994
  return parts.length > 0 ? parts : [{ text: "" }];
6065
5995
  }
6066
- /**
6067
- * Convert tools to Gemini format
6068
- */
6069
5996
  convertTools(tools) {
6070
5997
  return tools.map((tool) => ({
6071
5998
  name: tool.name,
@@ -6073,72 +6000,58 @@ var init_gemini = __esm({
6073
6000
  parameters: tool.input_schema
6074
6001
  }));
6075
6002
  }
6076
- /**
6077
- * Convert tool choice to Gemini format
6078
- */
6079
6003
  convertToolChoice(choice) {
6080
- if (!choice || choice === "auto") return FunctionCallingMode.AUTO;
6081
- if (choice === "any") return FunctionCallingMode.ANY;
6082
- return FunctionCallingMode.AUTO;
6004
+ if (!choice || choice === "auto") {
6005
+ return { mode: FunctionCallingConfigMode.AUTO };
6006
+ }
6007
+ if (choice === "any") {
6008
+ return { mode: FunctionCallingConfigMode.ANY };
6009
+ }
6010
+ return {
6011
+ mode: FunctionCallingConfigMode.ANY,
6012
+ allowedFunctionNames: [choice.name]
6013
+ };
6083
6014
  }
6084
- /**
6085
- * Parse response from Gemini
6086
- */
6087
- parseResponse(result) {
6088
- const response = result.response;
6089
- const text13 = response.text();
6015
+ extractFunctionCalls(response) {
6016
+ if (response.functionCalls && response.functionCalls.length > 0) {
6017
+ return response.functionCalls;
6018
+ }
6019
+ const candidate = response.candidates?.[0];
6020
+ const parts = candidate?.content?.parts ?? [];
6021
+ return parts.filter((part) => !!part.functionCall).map((part) => part.functionCall).filter(Boolean);
6022
+ }
6023
+ parseResponse(response, model) {
6090
6024
  const usage = response.usageMetadata;
6091
6025
  return {
6092
6026
  id: `gemini-${Date.now()}`,
6093
- content: text13,
6027
+ content: response.text ?? "",
6094
6028
  stopReason: this.mapFinishReason(response.candidates?.[0]?.finishReason),
6095
6029
  usage: {
6096
6030
  inputTokens: usage?.promptTokenCount ?? 0,
6097
6031
  outputTokens: usage?.candidatesTokenCount ?? 0
6098
6032
  },
6099
- model: this.config.model ?? DEFAULT_MODEL5
6033
+ model: this.getModel(model)
6100
6034
  };
6101
6035
  }
6102
- /**
6103
- * Parse response with tool calls from Gemini
6104
- */
6105
- parseResponseWithTools(result) {
6106
- const response = result.response;
6107
- const candidate = response.candidates?.[0];
6036
+ parseResponseWithTools(response, model) {
6108
6037
  const usage = response.usageMetadata;
6109
- let textContent = "";
6110
- const toolCalls = [];
6111
- if (candidate?.content?.parts) {
6112
- let toolIndex = 0;
6113
- for (const part of candidate.content.parts) {
6114
- if ("text" in part && part.text) {
6115
- textContent += part.text;
6116
- }
6117
- if ("functionCall" in part && part.functionCall) {
6118
- toolIndex++;
6119
- toolCalls.push({
6120
- id: `gemini_call_${toolIndex}`,
6121
- name: part.functionCall.name,
6122
- input: part.functionCall.args ?? {}
6123
- });
6124
- }
6125
- }
6126
- }
6038
+ const toolCalls = this.extractFunctionCalls(response).map((functionCall, index) => ({
6039
+ id: functionCall.id ?? `gemini_call_${index + 1}`,
6040
+ name: functionCall.name ?? "unknown_function",
6041
+ input: functionCall.args ?? {}
6042
+ }));
6127
6043
  return {
6128
6044
  id: `gemini-${Date.now()}`,
6129
- content: textContent,
6130
- stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(candidate?.finishReason),
6045
+ content: response.text ?? "",
6046
+ stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(response.candidates?.[0]?.finishReason),
6131
6047
  usage: {
6132
6048
  inputTokens: usage?.promptTokenCount ?? 0,
6133
6049
  outputTokens: usage?.candidatesTokenCount ?? 0
6134
6050
  },
6135
- model: this.config.model ?? DEFAULT_MODEL5,
6051
+ model: this.getModel(model),
6136
6052
  toolCalls
6137
6053
  };
6138
6054
  }
6139
- /**
6140
- * Map finish reason to our format
6141
- */
6142
6055
  mapFinishReason(reason) {
6143
6056
  switch (reason) {
6144
6057
  case "STOP":
@@ -6153,9 +6066,6 @@ var init_gemini = __esm({
6153
6066
  return "end_turn";
6154
6067
  }
6155
6068
  }
6156
- /**
6157
- * Handle API errors
6158
- */
6159
6069
  handleError(error) {
6160
6070
  const message = error instanceof Error ? error.message : String(error);
6161
6071
  const msg = message.toLowerCase();
@@ -6176,6 +6086,436 @@ var init_gemini = __esm({
6176
6086
  }
6177
6087
  });
6178
6088
 
6089
+ // src/providers/vertex.ts
6090
+ function createVertexProvider(config) {
6091
+ const provider = new VertexProvider();
6092
+ if (config) {
6093
+ provider.initialize(config).catch(() => {
6094
+ });
6095
+ }
6096
+ return provider;
6097
+ }
6098
+ var DEFAULT_MODEL6, DEFAULT_BASE_URL, DEFAULT_LOCATION, CONTEXT_WINDOWS6, VertexProvider;
6099
+ var init_vertex = __esm({
6100
+ "src/providers/vertex.ts"() {
6101
+ init_errors();
6102
+ init_gcloud();
6103
+ init_retry();
6104
+ DEFAULT_MODEL6 = "gemini-2.5-pro";
6105
+ DEFAULT_BASE_URL = "https://aiplatform.googleapis.com/v1";
6106
+ DEFAULT_LOCATION = "global";
6107
+ CONTEXT_WINDOWS6 = {
6108
+ "gemini-2.5-pro": 1048576,
6109
+ "gemini-2.5-flash": 1048576,
6110
+ "gemini-2.5-flash-lite": 1048576,
6111
+ "gemini-2.0-flash-001": 1048576,
6112
+ "gemini-2.0-flash-lite-001": 1048576
6113
+ };
6114
+ VertexProvider = class {
6115
+ id = "vertex";
6116
+ name = "Google Vertex AI Gemini";
6117
+ config = {};
6118
+ project = "";
6119
+ location = DEFAULT_LOCATION;
6120
+ retryConfig = DEFAULT_RETRY_CONFIG;
6121
+ async initialize(config) {
6122
+ this.config = config;
6123
+ this.project = config.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
6124
+ this.location = config.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? DEFAULT_LOCATION;
6125
+ if (!this.project.trim()) {
6126
+ throw new ProviderError(
6127
+ "Vertex AI project not configured. Set provider.project, VERTEX_PROJECT, or GOOGLE_CLOUD_PROJECT.",
6128
+ { provider: this.id }
6129
+ );
6130
+ }
6131
+ const token = await getCachedADCToken();
6132
+ if (!token) {
6133
+ throw new ProviderError(
6134
+ "Vertex AI ADC is not configured. Run `gcloud auth application-default login` manually, then retry.",
6135
+ { provider: this.id }
6136
+ );
6137
+ }
6138
+ }
6139
+ async chat(messages, options) {
6140
+ this.ensureInitialized();
6141
+ return withRetry(async () => {
6142
+ const response = await this.generateContent(messages, options);
6143
+ return this.parseResponse(response, options?.model);
6144
+ }, this.retryConfig);
6145
+ }
6146
+ async chatWithTools(messages, options) {
6147
+ this.ensureInitialized();
6148
+ return withRetry(async () => {
6149
+ const response = await this.generateContent(
6150
+ messages,
6151
+ options,
6152
+ options.tools,
6153
+ options.toolChoice
6154
+ );
6155
+ return this.parseResponseWithTools(response, options.model);
6156
+ }, this.retryConfig);
6157
+ }
6158
+ async *stream(messages, options) {
6159
+ this.ensureInitialized();
6160
+ const stream = await this.streamGenerateContent(messages, options);
6161
+ let stopReason = "end_turn";
6162
+ for await (const chunk of stream) {
6163
+ const candidate = chunk.candidates?.[0];
6164
+ const parts = candidate?.content?.parts ?? [];
6165
+ for (const part of parts) {
6166
+ if (part.text) {
6167
+ yield { type: "text", text: part.text };
6168
+ }
6169
+ }
6170
+ stopReason = this.mapFinishReason(candidate?.finishReason);
6171
+ }
6172
+ yield { type: "done", stopReason };
6173
+ }
6174
+ async *streamWithTools(messages, options) {
6175
+ this.ensureInitialized();
6176
+ const stream = await this.streamGenerateContent(
6177
+ messages,
6178
+ options,
6179
+ options.tools,
6180
+ options.toolChoice
6181
+ );
6182
+ let stopReason = "end_turn";
6183
+ let streamToolCallCounter = 0;
6184
+ for await (const chunk of stream) {
6185
+ const candidate = chunk.candidates?.[0];
6186
+ const parts = candidate?.content?.parts ?? [];
6187
+ for (const part of parts) {
6188
+ if (part.text) {
6189
+ yield { type: "text", text: part.text };
6190
+ }
6191
+ if (part.functionCall) {
6192
+ streamToolCallCounter++;
6193
+ yield {
6194
+ type: "tool_use_start",
6195
+ toolCall: {
6196
+ id: `vertex_call_${streamToolCallCounter}`,
6197
+ name: part.functionCall.name,
6198
+ input: part.functionCall.args ?? {}
6199
+ }
6200
+ };
6201
+ yield {
6202
+ type: "tool_use_end",
6203
+ toolCall: {
6204
+ id: `vertex_call_${streamToolCallCounter}`,
6205
+ name: part.functionCall.name,
6206
+ input: part.functionCall.args ?? {}
6207
+ }
6208
+ };
6209
+ }
6210
+ }
6211
+ stopReason = parts.some((part) => part.functionCall) ? "tool_use" : this.mapFinishReason(candidate?.finishReason);
6212
+ }
6213
+ yield { type: "done", stopReason };
6214
+ }
6215
+ countTokens(text14) {
6216
+ return Math.ceil(text14.length / 4);
6217
+ }
6218
+ getContextWindow() {
6219
+ return CONTEXT_WINDOWS6[this.config.model ?? DEFAULT_MODEL6] ?? 1048576;
6220
+ }
6221
+ async isAvailable() {
6222
+ try {
6223
+ await this.generateContent([{ role: "user", content: "hi" }], { maxTokens: 8 });
6224
+ return true;
6225
+ } catch {
6226
+ return false;
6227
+ }
6228
+ }
6229
+ ensureInitialized() {
6230
+ if (!this.project) {
6231
+ throw new ProviderError("Provider not initialized. Call initialize() first.", {
6232
+ provider: this.id
6233
+ });
6234
+ }
6235
+ }
6236
+ getModel(model) {
6237
+ return model ?? this.config.model ?? DEFAULT_MODEL6;
6238
+ }
6239
+ getResolvedBaseUrl() {
6240
+ if (this.config.baseUrl && this.config.baseUrl.trim()) {
6241
+ return this.config.baseUrl;
6242
+ }
6243
+ if (this.location === DEFAULT_LOCATION) {
6244
+ return DEFAULT_BASE_URL;
6245
+ }
6246
+ return `https://${encodeURIComponent(this.location)}-aiplatform.googleapis.com/v1`;
6247
+ }
6248
+ buildEndpoint(model, stream = false) {
6249
+ const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
6250
+ return `${this.getResolvedBaseUrl()}/projects/${encodeURIComponent(this.project)}/locations/${encodeURIComponent(this.location)}/publishers/google/models/${encodeURIComponent(this.getModel(model))}:${action}`;
6251
+ }
6252
+ async getHeaders() {
6253
+ const token = await getCachedADCToken();
6254
+ if (!token) {
6255
+ throw new ProviderError(
6256
+ "Vertex AI ADC token is unavailable. Re-authenticate with gcloud and retry.",
6257
+ { provider: this.id }
6258
+ );
6259
+ }
6260
+ return {
6261
+ "Content-Type": "application/json",
6262
+ Authorization: `Bearer ${token.accessToken}`,
6263
+ "x-goog-user-project": this.project
6264
+ };
6265
+ }
6266
+ extractSystem(messages, optionsSystem) {
6267
+ if (optionsSystem !== void 0) return optionsSystem;
6268
+ const systemMsg = messages.find((m) => m.role === "system");
6269
+ if (!systemMsg) return void 0;
6270
+ if (typeof systemMsg.content === "string") return systemMsg.content;
6271
+ const text14 = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
6272
+ return text14 || void 0;
6273
+ }
6274
+ buildToolUseNameMap(messages) {
6275
+ const map = /* @__PURE__ */ new Map();
6276
+ for (const msg of messages) {
6277
+ if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
6278
+ for (const block of msg.content) {
6279
+ if (block.type === "tool_use") {
6280
+ map.set(block.id, block.name);
6281
+ }
6282
+ }
6283
+ }
6284
+ return map;
6285
+ }
6286
+ convertContents(messages) {
6287
+ const toolNameByUseId = this.buildToolUseNameMap(messages);
6288
+ const conversation = messages.filter((m) => m.role !== "system");
6289
+ const contents = [];
6290
+ for (let i = 0; i < conversation.length; i++) {
6291
+ const msg = conversation[i];
6292
+ if (msg.role === "user") {
6293
+ if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
6294
+ const functionResponses = [];
6295
+ for (const block of msg.content) {
6296
+ if (block.type === "tool_result") {
6297
+ const toolResult = block;
6298
+ functionResponses.push({
6299
+ functionResponse: {
6300
+ name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
6301
+ response: { result: toolResult.content }
6302
+ }
6303
+ });
6304
+ }
6305
+ }
6306
+ contents.push({ role: "user", parts: functionResponses });
6307
+ } else {
6308
+ contents.push({ role: "user", parts: this.convertContent(msg.content) });
6309
+ }
6310
+ } else if (msg.role === "assistant") {
6311
+ contents.push({ role: "model", parts: this.convertContent(msg.content) });
6312
+ }
6313
+ }
6314
+ return contents.length > 0 ? contents : [{ role: "user", parts: [{ text: "" }] }];
6315
+ }
6316
+ convertContent(content) {
6317
+ if (typeof content === "string") return [{ text: content }];
6318
+ const parts = [];
6319
+ for (const block of content) {
6320
+ if (block.type === "text") {
6321
+ parts.push({ text: block.text });
6322
+ } else if (block.type === "image") {
6323
+ const image = block;
6324
+ parts.push({
6325
+ inlineData: {
6326
+ data: image.source.data,
6327
+ mimeType: image.source.media_type
6328
+ }
6329
+ });
6330
+ } else if (block.type === "tool_use") {
6331
+ const toolUse = block;
6332
+ parts.push({
6333
+ functionCall: {
6334
+ name: toolUse.name,
6335
+ args: toolUse.input
6336
+ }
6337
+ });
6338
+ }
6339
+ }
6340
+ return parts.length > 0 ? parts : [{ text: "" }];
6341
+ }
6342
+ convertTools(tools) {
6343
+ return [
6344
+ {
6345
+ functionDeclarations: tools.map((tool) => ({
6346
+ name: tool.name,
6347
+ description: tool.description,
6348
+ parameters: tool.input_schema
6349
+ }))
6350
+ }
6351
+ ];
6352
+ }
6353
+ convertToolChoice(choice) {
6354
+ if (!choice || choice === "auto") {
6355
+ return { functionCallingConfig: { mode: "AUTO" } };
6356
+ }
6357
+ if (choice === "any") {
6358
+ return { functionCallingConfig: { mode: "ANY" } };
6359
+ }
6360
+ return { functionCallingConfig: { mode: "ANY", allowedFunctionNames: [choice.name] } };
6361
+ }
6362
+ buildRequestBody(messages, options, tools, toolChoice) {
6363
+ const body = {
6364
+ contents: this.convertContents(messages),
6365
+ generationConfig: {
6366
+ maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
6367
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
6368
+ stopSequences: options?.stopSequences
6369
+ }
6370
+ };
6371
+ const systemInstruction = this.extractSystem(messages, options?.system);
6372
+ if (systemInstruction) {
6373
+ body["systemInstruction"] = {
6374
+ parts: [{ text: systemInstruction }]
6375
+ };
6376
+ }
6377
+ if (tools && tools.length > 0) {
6378
+ body["tools"] = this.convertTools(tools);
6379
+ const convertedChoice = this.convertToolChoice(toolChoice);
6380
+ if (convertedChoice) {
6381
+ body["toolConfig"] = convertedChoice;
6382
+ }
6383
+ }
6384
+ return body;
6385
+ }
6386
+ async generateContent(messages, options, tools, toolChoice) {
6387
+ const response = await fetch(this.buildEndpoint(options?.model), {
6388
+ method: "POST",
6389
+ headers: await this.getHeaders(),
6390
+ body: JSON.stringify(this.buildRequestBody(messages, options, tools, toolChoice)),
6391
+ signal: options?.signal
6392
+ });
6393
+ if (!response.ok) {
6394
+ throw await this.buildHttpError(response);
6395
+ }
6396
+ const data = await response.json();
6397
+ if (data.error?.message) {
6398
+ throw new ProviderError(data.error.message, {
6399
+ provider: this.id,
6400
+ statusCode: data.error.code
6401
+ });
6402
+ }
6403
+ return data;
6404
+ }
6405
+ async *streamGenerateContent(messages, options, tools, toolChoice) {
6406
+ const response = await fetch(this.buildEndpoint(options?.model, true), {
6407
+ method: "POST",
6408
+ headers: await this.getHeaders(),
6409
+ body: JSON.stringify(this.buildRequestBody(messages, options, tools, toolChoice)),
6410
+ signal: options?.signal
6411
+ });
6412
+ if (!response.ok) {
6413
+ throw await this.buildHttpError(response);
6414
+ }
6415
+ if (!response.body) {
6416
+ throw new ProviderError("Vertex AI streaming response body is empty.", {
6417
+ provider: this.id
6418
+ });
6419
+ }
6420
+ const reader = response.body.getReader();
6421
+ const decoder = new TextDecoder();
6422
+ let buffer = "";
6423
+ while (true) {
6424
+ const { value, done } = await reader.read();
6425
+ if (done) break;
6426
+ buffer += decoder.decode(value, { stream: true });
6427
+ while (true) {
6428
+ const eventBoundary = buffer.indexOf("\n\n");
6429
+ if (eventBoundary === -1) break;
6430
+ const rawEvent = buffer.slice(0, eventBoundary);
6431
+ buffer = buffer.slice(eventBoundary + 2);
6432
+ const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter(Boolean);
6433
+ for (const line of dataLines) {
6434
+ if (line === "[DONE]") return;
6435
+ yield JSON.parse(line);
6436
+ }
6437
+ }
6438
+ }
6439
+ const trailing = buffer.trim();
6440
+ if (trailing.startsWith("data:")) {
6441
+ const line = trailing.slice(5).trim();
6442
+ if (line && line !== "[DONE]") {
6443
+ yield JSON.parse(line);
6444
+ }
6445
+ }
6446
+ }
6447
+ parseResponse(response, model) {
6448
+ const candidate = response.candidates?.[0];
6449
+ const text14 = (candidate?.content?.parts ?? []).filter((part) => part.text).map((part) => part.text).join("");
6450
+ return {
6451
+ id: `vertex-${Date.now()}`,
6452
+ content: text14,
6453
+ stopReason: this.mapFinishReason(candidate?.finishReason),
6454
+ usage: {
6455
+ inputTokens: response.usageMetadata?.promptTokenCount ?? 0,
6456
+ outputTokens: response.usageMetadata?.candidatesTokenCount ?? 0
6457
+ },
6458
+ model: this.getModel(model)
6459
+ };
6460
+ }
6461
+ parseResponseWithTools(response, model) {
6462
+ const candidate = response.candidates?.[0];
6463
+ const parts = candidate?.content?.parts ?? [];
6464
+ const toolCalls = [];
6465
+ let textContent = "";
6466
+ let toolIndex = 0;
6467
+ for (const part of parts) {
6468
+ if (part.text) {
6469
+ textContent += part.text;
6470
+ }
6471
+ if (part.functionCall) {
6472
+ toolIndex++;
6473
+ toolCalls.push({
6474
+ id: `vertex_call_${toolIndex}`,
6475
+ name: part.functionCall.name,
6476
+ input: part.functionCall.args ?? {}
6477
+ });
6478
+ }
6479
+ }
6480
+ return {
6481
+ id: `vertex-${Date.now()}`,
6482
+ content: textContent,
6483
+ stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(candidate?.finishReason),
6484
+ usage: {
6485
+ inputTokens: response.usageMetadata?.promptTokenCount ?? 0,
6486
+ outputTokens: response.usageMetadata?.candidatesTokenCount ?? 0
6487
+ },
6488
+ model: this.getModel(model),
6489
+ toolCalls
6490
+ };
6491
+ }
6492
+ mapFinishReason(reason) {
6493
+ switch (reason) {
6494
+ case "STOP":
6495
+ return "end_turn";
6496
+ case "MAX_TOKENS":
6497
+ return "max_tokens";
6498
+ case "SAFETY":
6499
+ case "RECITATION":
6500
+ case "OTHER":
6501
+ return "stop_sequence";
6502
+ default:
6503
+ return "end_turn";
6504
+ }
6505
+ }
6506
+ async buildHttpError(response) {
6507
+ const body = await response.text();
6508
+ const retryable = response.status === 429 || response.status >= 500;
6509
+ return new ProviderError(`Vertex AI error: ${response.status} - ${body}`, {
6510
+ provider: this.id,
6511
+ statusCode: response.status,
6512
+ retryable
6513
+ });
6514
+ }
6515
+ };
6516
+ }
6517
+ });
6518
+
6179
6519
  // src/providers/pricing.ts
6180
6520
  function estimateCost(model, inputTokens, outputTokens, provider) {
6181
6521
  const pricing = MODEL_PRICING[model] ?? (provider ? DEFAULT_PRICING[provider] : DEFAULT_PRICING.anthropic);
@@ -6269,6 +6609,7 @@ var init_pricing = __esm({
6269
6609
  codex: { inputPerMillion: 0, outputPerMillion: 0, contextWindow: 128e3 },
6270
6610
  // ChatGPT Plus/Pro subscription
6271
6611
  gemini: { inputPerMillion: 0.1, outputPerMillion: 0.4, contextWindow: 1e6 },
6612
+ vertex: { inputPerMillion: 0.1, outputPerMillion: 0.4, contextWindow: 1048576 },
6272
6613
  kimi: { inputPerMillion: 1.2, outputPerMillion: 1.2, contextWindow: 8192 },
6273
6614
  "kimi-code": { inputPerMillion: 0, outputPerMillion: 0, contextWindow: 131072 },
6274
6615
  // Included in subscription
@@ -6591,9 +6932,9 @@ var init_fallback = __esm({
6591
6932
  * @param text - Text to count tokens for
6592
6933
  * @returns Estimated token count
6593
6934
  */
6594
- countTokens(text13) {
6935
+ countTokens(text14) {
6595
6936
  const provider = this.getCurrentProvider();
6596
- return provider.provider.countTokens(text13);
6937
+ return provider.provider.countTokens(text14);
6597
6938
  }
6598
6939
  /**
6599
6940
  * Get context window from current provider
@@ -6809,8 +7150,8 @@ var init_resilient = __esm({
6809
7150
  async *streamWithTools(messages, options) {
6810
7151
  yield* this.streamWithPolicy(() => this.provider.streamWithTools(messages, options));
6811
7152
  }
6812
- countTokens(text13) {
6813
- return this.provider.countTokens(text13);
7153
+ countTokens(text14) {
7154
+ return this.provider.countTokens(text14);
6814
7155
  }
6815
7156
  getContextWindow() {
6816
7157
  return this.provider.getContextWindow();
@@ -6877,6 +7218,7 @@ __export(providers_exports, {
6877
7218
  OpenAIProvider: () => OpenAIProvider,
6878
7219
  ProviderFallback: () => ProviderFallback,
6879
7220
  ResilientProvider: () => ResilientProvider,
7221
+ VertexProvider: () => VertexProvider,
6880
7222
  createAnthropicProvider: () => createAnthropicProvider,
6881
7223
  createCircuitBreaker: () => createCircuitBreaker,
6882
7224
  createCodexProvider: () => createCodexProvider,
@@ -6889,6 +7231,7 @@ __export(providers_exports, {
6889
7231
  createProviderFallback: () => createProviderFallback,
6890
7232
  createResilientProvider: () => createResilientProvider,
6891
7233
  createRetryableMethod: () => createRetryableMethod,
7234
+ createVertexProvider: () => createVertexProvider,
6892
7235
  estimateCost: () => estimateCost,
6893
7236
  formatCost: () => formatCost,
6894
7237
  getDefaultProvider: () => getDefaultProvider2,
@@ -6905,6 +7248,11 @@ function normalizeProviderModel(model) {
6905
7248
  const trimmed = model.trim();
6906
7249
  return trimmed.length > 0 ? trimmed : void 0;
6907
7250
  }
7251
+ function normalizeOptional(value) {
7252
+ if (typeof value !== "string") return void 0;
7253
+ const trimmed = value.trim();
7254
+ return trimmed.length > 0 ? trimmed : void 0;
7255
+ }
6908
7256
  async function createProvider(type, config = {}) {
6909
7257
  let provider;
6910
7258
  const mergedConfig = {
@@ -6913,7 +7261,11 @@ async function createProvider(type, config = {}) {
6913
7261
  model: normalizeProviderModel(config.model) ?? getDefaultModel(type),
6914
7262
  maxTokens: config.maxTokens,
6915
7263
  temperature: config.temperature,
6916
- timeout: config.timeout
7264
+ timeout: config.timeout,
7265
+ project: normalizeOptional(config.project) ?? (type === "vertex" ? normalizeOptional(
7266
+ process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"]
7267
+ ) : void 0),
7268
+ location: normalizeOptional(config.location) ?? (type === "vertex" ? normalizeOptional(process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"]) : void 0)
6917
7269
  };
6918
7270
  switch (type) {
6919
7271
  case "anthropic":
@@ -6931,6 +7283,9 @@ async function createProvider(type, config = {}) {
6931
7283
  case "gemini":
6932
7284
  provider = new GeminiProvider();
6933
7285
  break;
7286
+ case "vertex":
7287
+ provider = new VertexProvider();
7288
+ break;
6934
7289
  case "kimi":
6935
7290
  provider = createKimiProvider(mergedConfig);
6936
7291
  break;
@@ -7014,6 +7369,11 @@ function listProviders() {
7014
7369
  })()
7015
7370
  },
7016
7371
  { id: "gemini", name: "Google Gemini", configured: !!getApiKey("gemini") },
7372
+ {
7373
+ id: "vertex",
7374
+ name: "Google Vertex AI",
7375
+ configured: !!(process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"])
7376
+ },
7017
7377
  { id: "kimi", name: "Kimi (Moonshot API)", configured: !!getApiKey("kimi") },
7018
7378
  { id: "kimi-code", name: "Kimi Code (Subscription)", configured: !!getApiKey("kimi-code") },
7019
7379
  { id: "groq", name: "Groq", configured: !!getApiKey("groq") },
@@ -7034,6 +7394,7 @@ var init_providers = __esm({
7034
7394
  init_codex();
7035
7395
  init_copilot2();
7036
7396
  init_gemini();
7397
+ init_vertex();
7037
7398
  init_retry();
7038
7399
  init_pricing();
7039
7400
  init_circuit_breaker();
@@ -7043,6 +7404,7 @@ var init_providers = __esm({
7043
7404
  init_anthropic();
7044
7405
  init_openai();
7045
7406
  init_gemini();
7407
+ init_vertex();
7046
7408
  init_codex();
7047
7409
  init_copilot2();
7048
7410
  init_errors();
@@ -8102,8 +8464,8 @@ function tokenOverlap(queryTokens, targetTokens) {
8102
8464
  }
8103
8465
  return hits / queryTokens.length;
8104
8466
  }
8105
- function tokenize(text13) {
8106
- return text13.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/[\s-]+/).filter((word) => word.length > 1 && !STOP_WORDS.has(word)).map(stem);
8467
+ function tokenize(text14) {
8468
+ return text14.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/[\s-]+/).filter((word) => word.length > 1 && !STOP_WORDS.has(word)).map(stem);
8107
8469
  }
8108
8470
  function stem(word) {
8109
8471
  if (word.length < 4) return word;
@@ -9200,8 +9562,8 @@ ${tail}`
9200
9562
  estimateTokens(messages, provider) {
9201
9563
  let total = 0;
9202
9564
  for (const message of messages) {
9203
- const text13 = this.extractTextContent(message.content);
9204
- total += provider.countTokens(text13);
9565
+ const text14 = this.extractTextContent(message.content);
9566
+ total += provider.countTokens(text14);
9205
9567
  }
9206
9568
  return total;
9207
9569
  }
@@ -18291,15 +18653,15 @@ ${message}
18291
18653
  let stdoutBuffer = "";
18292
18654
  let stderrBuffer = "";
18293
18655
  subprocess.stdout?.on("data", (chunk) => {
18294
- const text13 = chunk.toString();
18295
- stdoutBuffer += text13;
18296
- process.stdout.write(text13);
18656
+ const text14 = chunk.toString();
18657
+ stdoutBuffer += text14;
18658
+ process.stdout.write(text14);
18297
18659
  heartbeat.activity();
18298
18660
  });
18299
18661
  subprocess.stderr?.on("data", (chunk) => {
18300
- const text13 = chunk.toString();
18301
- stderrBuffer += text13;
18302
- process.stderr.write(text13);
18662
+ const text14 = chunk.toString();
18663
+ stderrBuffer += text14;
18664
+ process.stderr.write(text14);
18303
18665
  heartbeat.activity();
18304
18666
  });
18305
18667
  const result = await subprocess;
@@ -19967,11 +20329,11 @@ async function runMergeRelease(ctx) {
19967
20329
  }
19968
20330
  const tagName = ctx.newVersion ? `v${ctx.newVersion}` : void 0;
19969
20331
  const mergeMsg = tagName ? `Merge PR #${ctx.prNumber} to ${ctx.profile.defaultBranch} and create release ${tagName}?` : `Merge PR #${ctx.prNumber} to ${ctx.profile.defaultBranch}?`;
19970
- const confirm23 = await p26.confirm({
20332
+ const confirm22 = await p26.confirm({
19971
20333
  message: mergeMsg,
19972
20334
  initialValue: true
19973
20335
  });
19974
- if (p26.isCancel(confirm23) || !confirm23) {
20336
+ if (p26.isCancel(confirm22) || !confirm22) {
19975
20337
  return {
19976
20338
  step: "merge-release",
19977
20339
  status: "skipped",
@@ -22756,22 +23118,22 @@ var init_types7 = __esm({
22756
23118
  });
22757
23119
 
22758
23120
  // src/cli/repl/interruptions/classifier.ts
22759
- function matchPatterns(text13, patterns) {
23121
+ function matchPatterns(text14, patterns) {
22760
23122
  for (let i = 0; i < patterns.length; i++) {
22761
- if (patterns[i].test(text13)) {
23123
+ if (patterns[i].test(text14)) {
22762
23124
  return 1 - i * 0.1;
22763
23125
  }
22764
23126
  }
22765
23127
  return 0;
22766
23128
  }
22767
23129
  function classifyInterruption(message) {
22768
- const text13 = message.text;
22769
- const abortConf = matchPatterns(text13, ABORT_PATTERNS);
22770
- const modifyConf = matchPatterns(text13, MODIFY_PATTERNS);
22771
- const correctConf = matchPatterns(text13, CORRECT_PATTERNS);
23130
+ const text14 = message.text;
23131
+ const abortConf = matchPatterns(text14, ABORT_PATTERNS);
23132
+ const modifyConf = matchPatterns(text14, MODIFY_PATTERNS);
23133
+ const correctConf = matchPatterns(text14, CORRECT_PATTERNS);
22772
23134
  if (abortConf > 0 && abortConf >= modifyConf && abortConf >= correctConf) {
22773
23135
  return {
22774
- text: text13,
23136
+ text: text14,
22775
23137
  type: "abort" /* Abort */,
22776
23138
  confidence: Math.min(1, abortConf),
22777
23139
  timestamp: message.timestamp
@@ -22779,7 +23141,7 @@ function classifyInterruption(message) {
22779
23141
  }
22780
23142
  if (modifyConf > 0 && modifyConf >= correctConf) {
22781
23143
  return {
22782
- text: text13,
23144
+ text: text14,
22783
23145
  type: "modify" /* Modify */,
22784
23146
  confidence: Math.min(1, modifyConf),
22785
23147
  timestamp: message.timestamp
@@ -22787,14 +23149,14 @@ function classifyInterruption(message) {
22787
23149
  }
22788
23150
  if (correctConf > 0) {
22789
23151
  return {
22790
- text: text13,
23152
+ text: text14,
22791
23153
  type: "correct" /* Correct */,
22792
23154
  confidence: Math.min(1, correctConf),
22793
23155
  timestamp: message.timestamp
22794
23156
  };
22795
23157
  }
22796
23158
  return {
22797
- text: text13,
23159
+ text: text14,
22798
23160
  type: "info" /* Info */,
22799
23161
  confidence: 0.5,
22800
23162
  timestamp: message.timestamp
@@ -25302,10 +25664,10 @@ function inferTargetUsers(session) {
25302
25664
  /(?:for|by)\s+(developers?|users?|administrators?|customers?)/gi,
25303
25665
  /(developers?|users?|administrators?|customers?)\s+(?:can|will|should)/gi
25304
25666
  ];
25305
- const text13 = session.requirements.map((r) => r.description).join(" ");
25667
+ const text14 = session.requirements.map((r) => r.description).join(" ");
25306
25668
  for (const pattern of userPatterns) {
25307
25669
  let match;
25308
- while ((match = pattern.exec(text13)) !== null) {
25670
+ while ((match = pattern.exec(text14)) !== null) {
25309
25671
  const user = match[1]?.toLowerCase();
25310
25672
  if (user && !users.includes(user)) {
25311
25673
  users.push(user);
@@ -25318,13 +25680,13 @@ function inferTargetUsers(session) {
25318
25680
  return users;
25319
25681
  }
25320
25682
  function inferProjectType(session) {
25321
- const text13 = session.initialInput.toLowerCase();
25322
- if (text13.includes("cli") || text13.includes("command line")) return "cli";
25323
- if (text13.includes("api") || text13.includes("rest") || text13.includes("graphql")) return "api";
25324
- if (text13.includes("web app") || text13.includes("frontend")) return "web_app";
25325
- if (text13.includes("library") || text13.includes("package")) return "library";
25326
- if (text13.includes("service") || text13.includes("daemon")) return "service";
25327
- if (text13.includes("full stack") || text13.includes("fullstack")) return "full_stack";
25683
+ const text14 = session.initialInput.toLowerCase();
25684
+ if (text14.includes("cli") || text14.includes("command line")) return "cli";
25685
+ if (text14.includes("api") || text14.includes("rest") || text14.includes("graphql")) return "api";
25686
+ if (text14.includes("web app") || text14.includes("frontend")) return "web_app";
25687
+ if (text14.includes("library") || text14.includes("package")) return "library";
25688
+ if (text14.includes("service") || text14.includes("daemon")) return "service";
25689
+ if (text14.includes("full stack") || text14.includes("fullstack")) return "full_stack";
25328
25690
  return "unknown";
25329
25691
  }
25330
25692
  function assessComplexity(session) {
@@ -28991,16 +29353,13 @@ var PROVIDER_DEFINITIONS = {
28991
29353
  id: "gemini",
28992
29354
  name: "Google Gemini",
28993
29355
  emoji: "\u{1F535}",
28994
- description: "Gemini 3 and 2.5 models",
29356
+ description: "Gemini Developer API via AI Studio API key",
28995
29357
  envVar: "GEMINI_API_KEY",
28996
29358
  apiKeyUrl: "https://aistudio.google.com/apikey",
28997
29359
  docsUrl: "https://ai.google.dev/gemini-api/docs",
28998
29360
  baseUrl: "https://generativelanguage.googleapis.com/v1beta",
28999
29361
  supportsCustomModels: true,
29000
29362
  openaiCompatible: false,
29001
- supportsGcloudADC: true,
29002
- // Supports gcloud auth application-default login
29003
- // NOTE: OAuth removed - Google's client ID is restricted to official apps only
29004
29363
  paymentType: "freemium",
29005
29364
  features: {
29006
29365
  streaming: true,
@@ -29055,6 +29414,63 @@ var PROVIDER_DEFINITIONS = {
29055
29414
  }
29056
29415
  ]
29057
29416
  },
29417
+ vertex: {
29418
+ id: "vertex",
29419
+ name: "Google Vertex AI Gemini",
29420
+ emoji: "\u2601\uFE0F",
29421
+ description: "Gemini on Vertex AI with GCP project, IAM and ADC",
29422
+ envVar: "VERTEX_PROJECT",
29423
+ apiKeyUrl: "https://console.cloud.google.com/vertex-ai",
29424
+ docsUrl: "https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstart",
29425
+ baseUrl: "https://aiplatform.googleapis.com/v1",
29426
+ supportsCustomModels: true,
29427
+ openaiCompatible: false,
29428
+ supportsGcloudADC: true,
29429
+ paymentType: "api",
29430
+ features: {
29431
+ streaming: true,
29432
+ functionCalling: true,
29433
+ vision: true
29434
+ },
29435
+ models: [
29436
+ {
29437
+ id: "gemini-2.5-pro",
29438
+ name: "Gemini 2.5 Pro",
29439
+ description: "Recommended Vertex model for coding and complex reasoning",
29440
+ contextWindow: 1048576,
29441
+ maxOutputTokens: 65536,
29442
+ recommended: true
29443
+ },
29444
+ {
29445
+ id: "gemini-2.5-flash",
29446
+ name: "Gemini 2.5 Flash",
29447
+ description: "Faster Vertex model with 1M context",
29448
+ contextWindow: 1048576,
29449
+ maxOutputTokens: 65536
29450
+ },
29451
+ {
29452
+ id: "gemini-2.5-flash-lite",
29453
+ name: "Gemini 2.5 Flash-Lite",
29454
+ description: "Lowest-cost Vertex option",
29455
+ contextWindow: 1048576,
29456
+ maxOutputTokens: 65536
29457
+ },
29458
+ {
29459
+ id: "gemini-2.0-flash-001",
29460
+ name: "Gemini 2.0 Flash 001",
29461
+ description: "Broadly available Vertex model",
29462
+ contextWindow: 1048576,
29463
+ maxOutputTokens: 8192
29464
+ },
29465
+ {
29466
+ id: "gemini-2.0-flash-lite-001",
29467
+ name: "Gemini 2.0 Flash-Lite 001",
29468
+ description: "Lightweight Vertex fallback",
29469
+ contextWindow: 1048576,
29470
+ maxOutputTokens: 8192
29471
+ }
29472
+ ]
29473
+ },
29058
29474
  // Kimi/Moonshot - OpenAI compatible
29059
29475
  kimi: {
29060
29476
  id: "kimi",
@@ -29681,6 +30097,9 @@ function getConfiguredProviders() {
29681
30097
  if (p45.id === "lmstudio" || p45.id === "ollama") {
29682
30098
  return hasLocalProviderConfig(p45.id);
29683
30099
  }
30100
+ if (p45.id === "vertex") {
30101
+ return !!(process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"]);
30102
+ }
29684
30103
  return !!process.env[p45.envVar];
29685
30104
  });
29686
30105
  }
@@ -30032,11 +30451,11 @@ async function runRemoveServer(name, options) {
30032
30451
  process.exit(1);
30033
30452
  }
30034
30453
  if (!options.yes) {
30035
- const confirm23 = await p26.confirm({
30454
+ const confirm22 = await p26.confirm({
30036
30455
  message: `Remove server '${name}'?`,
30037
30456
  initialValue: false
30038
30457
  });
30039
- if (p26.isCancel(confirm23) || !confirm23) {
30458
+ if (p26.isCancel(confirm22) || !confirm22) {
30040
30459
  p26.outro("Cancelled");
30041
30460
  return;
30042
30461
  }
@@ -30308,10 +30727,10 @@ async function runRemove(name, options) {
30308
30727
  return;
30309
30728
  }
30310
30729
  if (!options.yes) {
30311
- const confirm23 = await p26.confirm({
30730
+ const confirm22 = await p26.confirm({
30312
30731
  message: `Remove skill "${name}" from ${targetDir}?`
30313
30732
  });
30314
- if (p26.isCancel(confirm23) || !confirm23) {
30733
+ if (p26.isCancel(confirm22) || !confirm22) {
30315
30734
  p26.log.info("Cancelled.");
30316
30735
  p26.outro("");
30317
30736
  return;
@@ -30816,8 +31235,8 @@ ${suggestionsHtml}
30816
31235
  return filePath;
30817
31236
  }
30818
31237
  // ── Private helpers ───────────────────────────────────────────────────────
30819
- htmlEscape(text13) {
30820
- return text13.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
31238
+ htmlEscape(text14) {
31239
+ return text14.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
30821
31240
  }
30822
31241
  markdownIssue(issue) {
30823
31242
  const severity = issue.severity === "critical" ? "\u{1F534}" : issue.severity === "major" ? "\u{1F7E1}" : "\u{1F535}";
@@ -31339,12 +31758,12 @@ Return only JSON: { "score": <number 1-10>, "reasoning": "<brief explanation>" }
31339
31758
  }
31340
31759
  return classifyFeatureHeuristic(feature);
31341
31760
  }
31342
- function extractJsonScore(text13) {
31761
+ function extractJsonScore(text14) {
31343
31762
  try {
31344
- const stripped = text13.replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "").trim();
31763
+ const stripped = text14.replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "").trim();
31345
31764
  return JSON.parse(stripped);
31346
31765
  } catch {
31347
- const match = text13.match(/\{[\s\S]*?\}/);
31766
+ const match = text14.match(/\{[\s\S]*?\}/);
31348
31767
  if (match) {
31349
31768
  try {
31350
31769
  return JSON.parse(match[0]);
@@ -32557,12 +32976,12 @@ function computeGlobalScore(results) {
32557
32976
  const total = results.reduce((sum, r) => sum + r.reviewScore, 0);
32558
32977
  return Math.round(total / results.length);
32559
32978
  }
32560
- function extractJson(text13) {
32979
+ function extractJson(text14) {
32561
32980
  try {
32562
- const stripped = text13.replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "").trim();
32981
+ const stripped = text14.replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "").trim();
32563
32982
  return JSON.parse(stripped);
32564
32983
  } catch {
32565
- const match = text13.match(/\{[\s\S]*\}/);
32984
+ const match = text14.match(/\{[\s\S]*\}/);
32566
32985
  if (match) {
32567
32986
  try {
32568
32987
  return JSON.parse(match[0]);
@@ -33398,111 +33817,102 @@ async function setupGcloudADC(provider) {
33398
33817
  p26.log.error("gcloud CLI is not installed");
33399
33818
  console.log(chalk.dim(" Install it from: https://cloud.google.com/sdk/docs/install"));
33400
33819
  console.log();
33401
- const useFallback = await p26.confirm({
33820
+ if (provider.id === "vertex") {
33821
+ console.log(chalk.dim(" Vertex AI requires gcloud ADC plus a Google Cloud project."));
33822
+ console.log();
33823
+ return null;
33824
+ }
33825
+ const useFallback2 = await p26.confirm({
33402
33826
  message: "Use API key instead?",
33403
33827
  initialValue: true
33404
33828
  });
33405
- if (p26.isCancel(useFallback) || !useFallback) return null;
33829
+ if (p26.isCancel(useFallback2) || !useFallback2) return null;
33406
33830
  showProviderInfo(provider);
33407
- const apiKey = await requestApiKey(provider);
33408
- if (!apiKey) return null;
33409
- const model = await selectModel(provider);
33410
- if (!model) return null;
33411
- const valid = await testConnection(provider, apiKey, model);
33412
- if (!valid) return null;
33413
- return { type: provider.id, model, apiKey };
33414
- }
33415
- const adcConfigured = await isADCConfigured();
33416
- if (adcConfigured) {
33831
+ const apiKey2 = await requestApiKey(provider);
33832
+ if (!apiKey2) return null;
33833
+ const model2 = await selectModel(provider);
33834
+ if (!model2) return null;
33835
+ const valid2 = await testConnection(provider, apiKey2, model2);
33836
+ if (!valid2) return null;
33837
+ return { type: provider.id, model: model2, apiKey: apiKey2 };
33838
+ }
33839
+ const adc = await inspectADC();
33840
+ if (adc.status === "ok" && adc.token) {
33417
33841
  console.log(chalk.green(" \u2713 gcloud ADC is already configured!"));
33418
33842
  console.log();
33419
- const token = await getADCAccessToken();
33420
- if (token) {
33421
- p26.log.success("Authentication verified");
33422
- const model = await selectModel(provider);
33423
- if (!model) return null;
33424
- return {
33425
- type: provider.id,
33426
- model,
33427
- apiKey: "__gcloud_adc__"
33428
- // Special marker for ADC
33429
- };
33430
- }
33843
+ p26.log.success("Authentication verified");
33844
+ const vertexSettings = provider.id === "vertex" ? await promptVertexSettings() : void 0;
33845
+ if (provider.id === "vertex" && !vertexSettings) return null;
33846
+ const model2 = await selectModel(provider);
33847
+ if (!model2) return null;
33848
+ return {
33849
+ type: provider.id,
33850
+ model: model2,
33851
+ apiKey: "__gcloud_adc__",
33852
+ project: vertexSettings?.project,
33853
+ location: vertexSettings?.location
33854
+ };
33431
33855
  }
33432
- console.log(chalk.dim(" To authenticate with Google Cloud, you'll need to run:"));
33856
+ console.log(chalk.yellow(" No reusable gcloud ADC session was found for Coco."));
33433
33857
  console.log();
33434
- console.log(chalk.cyan(" $ gcloud auth application-default login"));
33858
+ if (adc.message) {
33859
+ console.log(chalk.dim(` ${adc.message}`));
33860
+ console.log();
33861
+ }
33862
+ console.log(chalk.dim(" Check the current machine-wide ADC state with:"));
33863
+ console.log(chalk.cyan(" $ gcloud auth application-default print-access-token"));
33435
33864
  console.log();
33436
- console.log(chalk.dim(" This will open a browser for Google sign-in."));
33437
- console.log(chalk.dim(" After signing in, the credentials will be stored locally."));
33865
+ console.log(chalk.dim(" If you want to authenticate manually, run in your terminal:"));
33866
+ console.log(chalk.cyan(" $ gcloud auth application-default login"));
33438
33867
  console.log();
33439
- const runNow = await p26.confirm({
33440
- message: "Run gcloud auth now?",
33441
- initialValue: true
33442
- });
33443
- if (p26.isCancel(runNow)) return null;
33444
- if (runNow) {
33868
+ if (adc.suggestion) {
33869
+ console.log(chalk.dim(` ${adc.suggestion}`));
33445
33870
  console.log();
33446
- console.log(chalk.dim(" Opening browser for Google sign-in..."));
33447
- console.log(chalk.dim(" (Complete the sign-in in your browser, then return here)"));
33871
+ }
33872
+ console.log(chalk.dim(" Coco will reuse the login on the next attempt if ADC is valid."));
33873
+ console.log();
33874
+ if (provider.id === "vertex") {
33875
+ console.log(
33876
+ chalk.dim(" Vertex AI does not use API keys in Coco. Configure ADC, then retry.")
33877
+ );
33448
33878
  console.log();
33449
- const { exec: exec3 } = await import('child_process');
33450
- const { promisify: promisify6 } = await import('util');
33451
- const execAsync3 = promisify6(exec3);
33452
- try {
33453
- await execAsync3("gcloud auth application-default login", {
33454
- timeout: 12e4
33455
- // 2 minute timeout
33456
- });
33457
- const token = await getADCAccessToken();
33458
- if (token) {
33459
- console.log(chalk.green("\n \u2713 Authentication successful!"));
33460
- const model = await selectModel(provider);
33461
- if (!model) return null;
33462
- return {
33463
- type: provider.id,
33464
- model,
33465
- apiKey: "__gcloud_adc__"
33466
- // Special marker for ADC
33467
- };
33468
- } else {
33469
- p26.log.error("Failed to verify authentication");
33470
- return null;
33471
- }
33472
- } catch (error) {
33473
- const errorMsg = error instanceof Error ? error.message : String(error);
33474
- p26.log.error(`Authentication failed: ${errorMsg}`);
33475
- const useFallback = await p26.confirm({
33476
- message: "Use API key instead?",
33477
- initialValue: true
33478
- });
33479
- if (p26.isCancel(useFallback) || !useFallback) return null;
33480
- showProviderInfo(provider);
33481
- const apiKey = await requestApiKey(provider);
33482
- if (!apiKey) return null;
33483
- const model = await selectModel(provider);
33484
- if (!model) return null;
33485
- const valid = await testConnection(provider, apiKey, model);
33486
- if (!valid) return null;
33487
- return { type: provider.id, model, apiKey };
33488
- }
33489
- } else {
33490
- console.log(chalk.dim("\n Run this command when ready:"));
33491
- console.log(chalk.cyan(" $ gcloud auth application-default login\n"));
33492
- const useFallback = await p26.confirm({
33493
- message: "Use API key for now?",
33494
- initialValue: true
33495
- });
33496
- if (p26.isCancel(useFallback) || !useFallback) return null;
33497
- showProviderInfo(provider);
33498
- const apiKey = await requestApiKey(provider);
33499
- if (!apiKey) return null;
33500
- const model = await selectModel(provider);
33501
- if (!model) return null;
33502
- const valid = await testConnection(provider, apiKey, model);
33503
- if (!valid) return null;
33504
- return { type: provider.id, model, apiKey };
33879
+ return null;
33505
33880
  }
33881
+ const useFallback = await p26.confirm({
33882
+ message: "Use API key for now?",
33883
+ initialValue: true
33884
+ });
33885
+ if (p26.isCancel(useFallback) || !useFallback) return null;
33886
+ showProviderInfo(provider);
33887
+ const apiKey = await requestApiKey(provider);
33888
+ if (!apiKey) return null;
33889
+ const model = await selectModel(provider);
33890
+ if (!model) return null;
33891
+ const valid = await testConnection(provider, apiKey, model);
33892
+ if (!valid) return null;
33893
+ return { type: provider.id, model, apiKey };
33894
+ }
33895
+ async function promptVertexSettings() {
33896
+ const projectDefault = process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
33897
+ const locationDefault = process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? "global";
33898
+ const project = await p26.text({
33899
+ message: "Google Cloud project ID:",
33900
+ placeholder: projectDefault || "my-gcp-project",
33901
+ initialValue: projectDefault,
33902
+ validate: (v) => !v?.trim() ? "Project ID is required for Vertex AI" : void 0
33903
+ });
33904
+ if (p26.isCancel(project)) return null;
33905
+ const location = await p26.text({
33906
+ message: "Vertex AI location:",
33907
+ placeholder: locationDefault,
33908
+ initialValue: locationDefault,
33909
+ validate: (v) => !v?.trim() ? "Location is required for Vertex AI" : void 0
33910
+ });
33911
+ if (p26.isCancel(location)) return null;
33912
+ return {
33913
+ project: project.trim(),
33914
+ location: location.trim()
33915
+ };
33506
33916
  }
33507
33917
  var LOCAL_PROVIDER_CONFIG = {
33508
33918
  lmstudio: {
@@ -33996,7 +34406,7 @@ async function testConnectionQuiet(provider, apiKey, model, baseUrl) {
33996
34406
  return false;
33997
34407
  }
33998
34408
  }
33999
- async function testConnection(provider, apiKey, model, baseUrl) {
34409
+ async function testConnection(provider, apiKey, model, baseUrl, vertexSettings) {
34000
34410
  p26.log.message("");
34001
34411
  const spinner18 = p26.spinner();
34002
34412
  spinner18.start(`Testing connection to ${provider.name}...`);
@@ -34013,7 +34423,15 @@ async function testConnection(provider, apiKey, model, baseUrl) {
34013
34423
  if (baseUrl) {
34014
34424
  process.env[`${provider.id.toUpperCase()}_BASE_URL`] = baseUrl;
34015
34425
  }
34016
- const testProvider = await createProvider(provider.id, { model });
34426
+ if (provider.id === "vertex") {
34427
+ if (vertexSettings?.project) ;
34428
+ if (vertexSettings?.location) ;
34429
+ }
34430
+ const testProvider = await createProvider(provider.id, {
34431
+ model,
34432
+ project: vertexSettings?.project,
34433
+ location: vertexSettings?.location
34434
+ });
34017
34435
  if (debug) {
34018
34436
  p26.log.message(chalk.dim(`[Debug] Provider created: ${testProvider.id}`));
34019
34437
  }
@@ -34073,7 +34491,20 @@ async function saveConfiguration(result) {
34073
34491
  p26.log.message(
34074
34492
  chalk.dim(" Run `gcloud auth application-default login` to refresh credentials")
34075
34493
  );
34076
- await saveProviderPreference(result.type, result.model);
34494
+ if (result.type === "vertex" && result.project) {
34495
+ await saveEnvVars(
34496
+ CONFIG_PATHS.env,
34497
+ {
34498
+ VERTEX_PROJECT: result.project,
34499
+ VERTEX_LOCATION: result.location ?? "global"
34500
+ },
34501
+ true
34502
+ );
34503
+ }
34504
+ await saveProviderPreference(result.type, result.model, {
34505
+ project: result.project,
34506
+ location: result.location
34507
+ });
34077
34508
  return;
34078
34509
  }
34079
34510
  if (result.type === "copilot") {
@@ -34116,6 +34547,10 @@ async function saveConfiguration(result) {
34116
34547
  if (result.baseUrl) {
34117
34548
  envVarsToSave[`${provider.envVar.replace("_API_KEY", "_BASE_URL")}`] = result.baseUrl;
34118
34549
  }
34550
+ if (result.type === "vertex" && result.project) {
34551
+ envVarsToSave["VERTEX_PROJECT"] = result.project;
34552
+ envVarsToSave["VERTEX_LOCATION"] = result.location ?? "global";
34553
+ }
34119
34554
  }
34120
34555
  switch (saveOptions) {
34121
34556
  case "global":
@@ -34129,7 +34564,10 @@ async function saveConfiguration(result) {
34129
34564
  p26.log.message(chalk.dim("\n\u{1F4A8} Configuration active for this session only."));
34130
34565
  break;
34131
34566
  }
34132
- await saveProviderPreference(result.type, result.model);
34567
+ await saveProviderPreference(result.type, result.model, {
34568
+ project: result.project,
34569
+ location: result.location
34570
+ });
34133
34571
  }
34134
34572
  async function saveEnvVars(filePath, vars, createDir = false) {
34135
34573
  if (createDir) {
@@ -34498,6 +34936,7 @@ async function switchProvider(initialProvider, session) {
34498
34936
  let internalProviderId = initialProvider.id;
34499
34937
  let selectedAuthMethod = "apikey";
34500
34938
  let newApiKeyForSaving = null;
34939
+ let vertexSettings;
34501
34940
  if (newProvider.id === "lmstudio" || newProvider.id === "ollama") {
34502
34941
  const result = newProvider.id === "ollama" ? await setupOllamaProvider() : await setupLMStudioProvider();
34503
34942
  if (!result) {
@@ -34514,7 +34953,8 @@ async function switchProvider(initialProvider, session) {
34514
34953
  `));
34515
34954
  return false;
34516
34955
  }
34517
- const apiKey = process.env[newProvider.envVar];
34956
+ const supportsApiKey = newProvider.id !== "vertex" && newProvider.requiresApiKey !== false;
34957
+ const apiKey = supportsApiKey ? process.env[newProvider.envVar] : void 0;
34518
34958
  const hasOAuth = supportsOAuth(newProvider.id) || newProvider.supportsOAuth;
34519
34959
  const hasGcloudADC = newProvider.supportsGcloudADC;
34520
34960
  const oauthProviderName = newProvider.id === "copilot" ? "copilot" : newProvider.id === "gemini" ? "gemini" : "openai";
@@ -34562,18 +35002,20 @@ async function switchProvider(initialProvider, session) {
34562
35002
  hint: "Authenticate via gcloud CLI"
34563
35003
  });
34564
35004
  }
34565
- if (apiKey) {
34566
- authOptions.push({
34567
- value: "apikey",
34568
- label: "\u{1F511} API key (configured \u2713)",
34569
- hint: "Use your existing API key"
34570
- });
34571
- } else {
34572
- authOptions.push({
34573
- value: "apikey",
34574
- label: "\u{1F511} Enter API key",
34575
- hint: `Get from ${newProvider.apiKeyUrl}`
34576
- });
35005
+ if (supportsApiKey) {
35006
+ if (apiKey) {
35007
+ authOptions.push({
35008
+ value: "apikey",
35009
+ label: "\u{1F511} API key (configured \u2713)",
35010
+ hint: "Use your existing API key"
35011
+ });
35012
+ } else {
35013
+ authOptions.push({
35014
+ value: "apikey",
35015
+ label: "\u{1F511} Enter API key",
35016
+ hint: `Get from ${newProvider.apiKeyUrl}`
35017
+ });
35018
+ }
34577
35019
  }
34578
35020
  if (oauthConnected || apiKey) {
34579
35021
  authOptions.push({
@@ -34644,6 +35086,11 @@ Using existing OAuth session...`));
34644
35086
  const adcResult = await setupGcloudADCForProvider();
34645
35087
  if (!adcResult) return false;
34646
35088
  selectedAuthMethod = "gcloud";
35089
+ if (newProvider.id === "vertex") {
35090
+ const settings = await promptVertexSettings2();
35091
+ if (!settings) return false;
35092
+ vertexSettings = settings;
35093
+ }
34647
35094
  } else if (authChoice === "apikey") {
34648
35095
  if (apiKey) {
34649
35096
  selectedAuthMethod = "apikey";
@@ -34712,16 +35159,27 @@ Using existing API key...`));
34712
35159
  } else {
34713
35160
  console.log(chalk.yellow(`
34714
35161
  ${newProvider.emoji} ${newProvider.name} is not configured.`));
34715
- const key = await p26.password({
34716
- message: `Enter your ${newProvider.name} API key:`,
34717
- validate: (v) => !v || v.length < 10 ? "API key too short" : void 0
34718
- });
34719
- if (p26.isCancel(key)) {
34720
- return false;
35162
+ if (hasGcloudADC && !supportsApiKey) {
35163
+ const adcResult = await setupGcloudADCForProvider();
35164
+ if (!adcResult) return false;
35165
+ selectedAuthMethod = "gcloud";
35166
+ if (newProvider.id === "vertex") {
35167
+ const settings = await promptVertexSettings2();
35168
+ if (!settings) return false;
35169
+ vertexSettings = settings;
35170
+ }
35171
+ } else {
35172
+ const key = await p26.password({
35173
+ message: `Enter your ${newProvider.name} API key:`,
35174
+ validate: (v) => !v || v.length < 10 ? "API key too short" : void 0
35175
+ });
35176
+ if (p26.isCancel(key)) {
35177
+ return false;
35178
+ }
35179
+ process.env[newProvider.envVar] = key;
35180
+ selectedAuthMethod = "apikey";
35181
+ newApiKeyForSaving = key;
34721
35182
  }
34722
- process.env[newProvider.envVar] = key;
34723
- selectedAuthMethod = "apikey";
34724
- newApiKeyForSaving = key;
34725
35183
  }
34726
35184
  }
34727
35185
  const rememberedModel = await getLastUsedModel(newProvider.id);
@@ -34731,7 +35189,9 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
34731
35189
  spinner18.start(`Connecting to ${newProvider.name}...`);
34732
35190
  try {
34733
35191
  const testProvider = await createProvider(internalProviderId, {
34734
- model: newModel
35192
+ model: newModel,
35193
+ project: vertexSettings?.project,
35194
+ location: vertexSettings?.location
34735
35195
  });
34736
35196
  const available = await testProvider.isAvailable();
34737
35197
  if (!available) {
@@ -34744,6 +35204,13 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
34744
35204
  spinner18.stop(chalk.green("Connected!"));
34745
35205
  session.config.provider.type = userFacingProviderId;
34746
35206
  session.config.provider.model = newModel;
35207
+ if (userFacingProviderId === "vertex") {
35208
+ session.config.provider.project = vertexSettings?.project;
35209
+ session.config.provider.location = vertexSettings?.location;
35210
+ } else {
35211
+ delete session.config.provider.project;
35212
+ delete session.config.provider.location;
35213
+ }
34747
35214
  if (newApiKeyForSaving) {
34748
35215
  await saveConfiguration({
34749
35216
  type: userFacingProviderId,
@@ -34751,7 +35218,10 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
34751
35218
  apiKey: newApiKeyForSaving
34752
35219
  });
34753
35220
  } else {
34754
- await saveProviderPreference(userFacingProviderId, newModel);
35221
+ await saveProviderPreference(userFacingProviderId, newModel, {
35222
+ project: vertexSettings?.project,
35223
+ location: vertexSettings?.location
35224
+ });
34755
35225
  }
34756
35226
  console.log(chalk.green(`
34757
35227
  \u2713 Switched to ${newProvider.emoji} ${newProvider.name}`));
@@ -34779,38 +35249,48 @@ async function setupGcloudADCForProvider(_provider) {
34779
35249
  console.log(chalk.dim(" Install it from: https://cloud.google.com/sdk/docs/install\n"));
34780
35250
  return false;
34781
35251
  }
34782
- const adcConfigured = await isADCConfigured();
34783
- if (adcConfigured) {
34784
- const token = await getADCAccessToken();
34785
- if (token) {
34786
- console.log(chalk.green(" \u2713 gcloud ADC is already configured!\n"));
34787
- return true;
34788
- }
35252
+ const adc = await inspectADC();
35253
+ if (adc.status === "ok" && adc.token) {
35254
+ console.log(chalk.green(" \u2713 gcloud ADC is already configured!\n"));
35255
+ return true;
34789
35256
  }
34790
- console.log(chalk.dim("\n To authenticate, run:"));
34791
- console.log(chalk.cyan(" $ gcloud auth application-default login\n"));
34792
- const runNow = await p26.confirm({
34793
- message: "Run gcloud auth now?",
34794
- initialValue: true
34795
- });
34796
- if (p26.isCancel(runNow) || !runNow) {
34797
- return false;
35257
+ console.log(chalk.yellow("\n No reusable gcloud ADC session was found for Coco."));
35258
+ if (adc.message) {
35259
+ console.log(chalk.dim(` ${adc.message}`));
34798
35260
  }
34799
- const { exec: exec3 } = await import('child_process');
34800
- const { promisify: promisify6 } = await import('util');
34801
- const execAsync3 = promisify6(exec3);
34802
- try {
34803
- await execAsync3("gcloud auth application-default login", { timeout: 12e4 });
34804
- const token = await getADCAccessToken();
34805
- if (token) {
34806
- console.log(chalk.green("\n \u2713 Authentication successful!\n"));
34807
- return true;
34808
- }
34809
- } catch (error) {
34810
- p26.log.error(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`);
35261
+ console.log(chalk.dim("\n Check the current ADC state with:"));
35262
+ console.log(chalk.cyan(" $ gcloud auth application-default print-access-token"));
35263
+ console.log(chalk.dim("\n Authenticate manually in your terminal with:"));
35264
+ console.log(chalk.cyan(" $ gcloud auth application-default login"));
35265
+ if (adc.suggestion) {
35266
+ console.log(chalk.dim(`
35267
+ ${adc.suggestion}`));
34811
35268
  }
35269
+ console.log();
34812
35270
  return false;
34813
35271
  }
35272
+ async function promptVertexSettings2() {
35273
+ const projectDefault = process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
35274
+ const locationDefault = process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? "global";
35275
+ const project = await p26.text({
35276
+ message: "Google Cloud project ID:",
35277
+ placeholder: projectDefault || "my-gcp-project",
35278
+ initialValue: projectDefault,
35279
+ validate: (value) => !value?.trim() ? "Project ID is required for Vertex AI" : void 0
35280
+ });
35281
+ if (p26.isCancel(project)) return null;
35282
+ const location = await p26.text({
35283
+ message: "Vertex AI location:",
35284
+ placeholder: locationDefault,
35285
+ initialValue: locationDefault,
35286
+ validate: (value) => !value?.trim() ? "Location is required for Vertex AI" : void 0
35287
+ });
35288
+ if (p26.isCancel(location)) return null;
35289
+ return {
35290
+ project: project.trim(),
35291
+ location: location.trim()
35292
+ };
35293
+ }
34814
35294
 
34815
35295
  // src/cli/repl/commands/status.ts
34816
35296
  init_state();
@@ -35500,11 +35980,11 @@ async function revokeTrust(session, trustStore) {
35500
35980
  p26.log.info("This project is not currently trusted");
35501
35981
  return;
35502
35982
  }
35503
- const confirm23 = await p26.confirm({
35983
+ const confirm22 = await p26.confirm({
35504
35984
  message: "Revoke all access to this project?",
35505
35985
  initialValue: false
35506
35986
  });
35507
- if (p26.isCancel(confirm23) || !confirm23) {
35987
+ if (p26.isCancel(confirm22) || !confirm22) {
35508
35988
  p26.outro("Cancelled");
35509
35989
  return;
35510
35990
  }
@@ -35621,11 +36101,11 @@ var initCommand = {
35621
36101
  p26.log.message(` Description: ${description}`);
35622
36102
  }
35623
36103
  p26.log.message("");
35624
- const confirm23 = await p26.confirm({
36104
+ const confirm22 = await p26.confirm({
35625
36105
  message: "Create project?",
35626
36106
  initialValue: true
35627
36107
  });
35628
- if (p26.isCancel(confirm23) || !confirm23) {
36108
+ if (p26.isCancel(confirm22) || !confirm22) {
35629
36109
  p26.outro("Cancelled");
35630
36110
  return false;
35631
36111
  }
@@ -36851,11 +37331,11 @@ async function runInteractiveMode(session) {
36851
37331
  );
36852
37332
  }
36853
37333
  console.log();
36854
- const confirm23 = await p26.confirm({
37334
+ const confirm22 = await p26.confirm({
36855
37335
  message: "Proceed with restoration?",
36856
37336
  initialValue: true
36857
37337
  });
36858
- if (p26.isCancel(confirm23) || !confirm23) {
37338
+ if (p26.isCancel(confirm22) || !confirm22) {
36859
37339
  p26.outro("Cancelled");
36860
37340
  return false;
36861
37341
  }
@@ -36898,11 +37378,11 @@ async function runDirectMode(session, checkpointId) {
36898
37378
  console.log(`${chalk.dim("Conversation:")} ${checkpoint.conversation?.messageCount} messages`);
36899
37379
  }
36900
37380
  console.log();
36901
- const confirm23 = await p26.confirm({
37381
+ const confirm22 = await p26.confirm({
36902
37382
  message: "Restore this checkpoint?",
36903
37383
  initialValue: true
36904
37384
  });
36905
- if (p26.isCancel(confirm23) || !confirm23) {
37385
+ if (p26.isCancel(confirm22) || !confirm22) {
36906
37386
  p26.outro("Cancelled");
36907
37387
  return false;
36908
37388
  }
@@ -37353,11 +37833,11 @@ async function runInteractiveMode2(session) {
37353
37833
  return false;
37354
37834
  }
37355
37835
  displaySessionDetails(selectedSession);
37356
- const confirm23 = await p26.confirm({
37836
+ const confirm22 = await p26.confirm({
37357
37837
  message: "Resume this session?",
37358
37838
  initialValue: true
37359
37839
  });
37360
- if (p26.isCancel(confirm23) || !confirm23) {
37840
+ if (p26.isCancel(confirm22) || !confirm22) {
37361
37841
  p26.outro("Cancelled");
37362
37842
  return false;
37363
37843
  }
@@ -37375,11 +37855,11 @@ async function runDirectMode2(session, sessionId) {
37375
37855
  return false;
37376
37856
  }
37377
37857
  displaySessionDetails(targetSession);
37378
- const confirm23 = await p26.confirm({
37858
+ const confirm22 = await p26.confirm({
37379
37859
  message: "Resume this session?",
37380
37860
  initialValue: true
37381
37861
  });
37382
- if (p26.isCancel(confirm23) || !confirm23) {
37862
+ if (p26.isCancel(confirm22) || !confirm22) {
37383
37863
  p26.outro("Cancelled");
37384
37864
  return false;
37385
37865
  }
@@ -37668,7 +38148,7 @@ function getClipboardCommand() {
37668
38148
  }
37669
38149
  return null;
37670
38150
  }
37671
- async function copyToClipboard(text13) {
38151
+ async function copyToClipboard(text14) {
37672
38152
  const clipboardCmd = getClipboardCommand();
37673
38153
  if (!clipboardCmd) {
37674
38154
  return false;
@@ -37689,7 +38169,7 @@ async function copyToClipboard(text13) {
37689
38169
  });
37690
38170
  xselProc.on("error", () => resolve4(false));
37691
38171
  xselProc.on("close", (code) => resolve4(code === 0));
37692
- xselProc.stdin.write(text13);
38172
+ xselProc.stdin.write(text14);
37693
38173
  xselProc.stdin.end();
37694
38174
  } catch {
37695
38175
  resolve4(false);
@@ -37705,7 +38185,7 @@ async function copyToClipboard(text13) {
37705
38185
  });
37706
38186
  proc.stdin.on("error", () => {
37707
38187
  });
37708
- proc.stdin.write(text13);
38188
+ proc.stdin.write(text14);
37709
38189
  proc.stdin.end();
37710
38190
  } catch {
37711
38191
  resolve4(false);
@@ -39439,9 +39919,9 @@ var UserCancelledError = class extends Error {
39439
39919
  var SPEC_AGENT_SYSTEM = `You are a senior technical product manager specialising in rapid MVP delivery.
39440
39920
  Your job is to help a developer plan a software project efficiently and honestly.
39441
39921
  Always respond with valid JSON only \u2014 no markdown fences, no prose outside JSON.`;
39442
- function extractJson2(text13) {
39443
- const match = text13.match(/```(?:json)?\s*([\s\S]*?)```/) ?? text13.match(/(\{[\s\S]*\})/);
39444
- return match ? (match[1] ?? text13).trim() : text13.trim();
39922
+ function extractJson2(text14) {
39923
+ const match = text14.match(/```(?:json)?\s*([\s\S]*?)```/) ?? text14.match(/(\{[\s\S]*\})/);
39924
+ return match ? (match[1] ?? text14).trim() : text14.trim();
39445
39925
  }
39446
39926
  function validateBacklogSpec(raw) {
39447
39927
  if (!raw.sprints || raw.sprints.length === 0) {
@@ -39619,11 +40099,11 @@ Response format (JSON only, no prose):
39619
40099
  }
39620
40100
  }
39621
40101
  if (!options?.skipConfirmation) {
39622
- const confirm23 = await p26.confirm({
40102
+ const confirm22 = await p26.confirm({
39623
40103
  message: "Start building with this plan?",
39624
40104
  initialValue: true
39625
40105
  });
39626
- if (p26.isCancel(confirm23) || !confirm23) {
40106
+ if (p26.isCancel(confirm22) || !confirm22) {
39627
40107
  cancel5("Build cancelled.");
39628
40108
  }
39629
40109
  }
@@ -41181,8 +41661,8 @@ Examples:
41181
41661
  recursive: z.boolean().optional().default(false).describe("Delete directories recursively"),
41182
41662
  confirm: z.boolean().optional().describe("Must be true to confirm deletion")
41183
41663
  }),
41184
- async execute({ path: filePath, recursive, confirm: confirm23 }) {
41185
- if (confirm23 !== true) {
41664
+ async execute({ path: filePath, recursive, confirm: confirm22 }) {
41665
+ if (confirm22 !== true) {
41186
41666
  throw new ToolError(
41187
41667
  "Deletion requires explicit confirmation. Set confirm: true to proceed.",
41188
41668
  { tool: "delete_file" }
@@ -43082,15 +43562,15 @@ ${message}
43082
43562
  let stdoutBuffer = "";
43083
43563
  let stderrBuffer = "";
43084
43564
  subprocess.stdout?.on("data", (chunk) => {
43085
- const text13 = chunk.toString();
43086
- stdoutBuffer += text13;
43087
- process.stdout.write(text13);
43565
+ const text14 = chunk.toString();
43566
+ stdoutBuffer += text14;
43567
+ process.stdout.write(text14);
43088
43568
  heartbeat.activity();
43089
43569
  });
43090
43570
  subprocess.stderr?.on("data", (chunk) => {
43091
- const text13 = chunk.toString();
43092
- stderrBuffer += text13;
43093
- process.stderr.write(text13);
43571
+ const text14 = chunk.toString();
43572
+ stderrBuffer += text14;
43573
+ process.stderr.write(text14);
43094
43574
  heartbeat.activity();
43095
43575
  });
43096
43576
  const result = await subprocess;
@@ -43207,15 +43687,15 @@ ${message}
43207
43687
  let stdoutBuffer = "";
43208
43688
  let stderrBuffer = "";
43209
43689
  subprocess.stdout?.on("data", (chunk) => {
43210
- const text13 = chunk.toString();
43211
- stdoutBuffer += text13;
43212
- process.stdout.write(text13);
43690
+ const text14 = chunk.toString();
43691
+ stdoutBuffer += text14;
43692
+ process.stdout.write(text14);
43213
43693
  heartbeat.activity();
43214
43694
  });
43215
43695
  subprocess.stderr?.on("data", (chunk) => {
43216
- const text13 = chunk.toString();
43217
- stderrBuffer += text13;
43218
- process.stderr.write(text13);
43696
+ const text14 = chunk.toString();
43697
+ stderrBuffer += text14;
43698
+ process.stderr.write(text14);
43219
43699
  heartbeat.activity();
43220
43700
  });
43221
43701
  const result = await subprocess;
@@ -43309,15 +43789,15 @@ ${message}
43309
43789
  let stdoutBuffer = "";
43310
43790
  let stderrBuffer = "";
43311
43791
  subprocess.stdout?.on("data", (chunk) => {
43312
- const text13 = chunk.toString();
43313
- stdoutBuffer += text13;
43314
- process.stdout.write(text13);
43792
+ const text14 = chunk.toString();
43793
+ stdoutBuffer += text14;
43794
+ process.stdout.write(text14);
43315
43795
  heartbeat.activity();
43316
43796
  });
43317
43797
  subprocess.stderr?.on("data", (chunk) => {
43318
- const text13 = chunk.toString();
43319
- stderrBuffer += text13;
43320
- process.stderr.write(text13);
43798
+ const text14 = chunk.toString();
43799
+ stderrBuffer += text14;
43800
+ process.stderr.write(text14);
43321
43801
  heartbeat.activity();
43322
43802
  });
43323
43803
  const result = await subprocess;
@@ -43412,15 +43892,15 @@ ${message}
43412
43892
  let stdoutBuffer = "";
43413
43893
  let stderrBuffer = "";
43414
43894
  subprocess.stdout?.on("data", (chunk) => {
43415
- const text13 = chunk.toString();
43416
- stdoutBuffer += text13;
43417
- process.stdout.write(text13);
43895
+ const text14 = chunk.toString();
43896
+ stdoutBuffer += text14;
43897
+ process.stdout.write(text14);
43418
43898
  heartbeat.activity();
43419
43899
  });
43420
43900
  subprocess.stderr?.on("data", (chunk) => {
43421
- const text13 = chunk.toString();
43422
- stderrBuffer += text13;
43423
- process.stderr.write(text13);
43901
+ const text14 = chunk.toString();
43902
+ stderrBuffer += text14;
43903
+ process.stderr.write(text14);
43424
43904
  heartbeat.activity();
43425
43905
  });
43426
43906
  const result = await subprocess;
@@ -43516,15 +43996,15 @@ ${message}
43516
43996
  let stdoutBuffer = "";
43517
43997
  let stderrBuffer = "";
43518
43998
  subprocess.stdout?.on("data", (chunk) => {
43519
- const text13 = chunk.toString();
43520
- stdoutBuffer += text13;
43521
- process.stdout.write(text13);
43999
+ const text14 = chunk.toString();
44000
+ stdoutBuffer += text14;
44001
+ process.stdout.write(text14);
43522
44002
  heartbeat.activity();
43523
44003
  });
43524
44004
  subprocess.stderr?.on("data", (chunk) => {
43525
- const text13 = chunk.toString();
43526
- stderrBuffer += text13;
43527
- process.stderr.write(text13);
44005
+ const text14 = chunk.toString();
44006
+ stderrBuffer += text14;
44007
+ process.stderr.write(text14);
43528
44008
  heartbeat.activity();
43529
44009
  });
43530
44010
  const result = await subprocess;
@@ -43603,15 +44083,15 @@ ${message}
43603
44083
  let stdoutBuffer = "";
43604
44084
  let stderrBuffer = "";
43605
44085
  subprocess.stdout?.on("data", (chunk) => {
43606
- const text13 = chunk.toString();
43607
- stdoutBuffer += text13;
43608
- process.stdout.write(text13);
44086
+ const text14 = chunk.toString();
44087
+ stdoutBuffer += text14;
44088
+ process.stdout.write(text14);
43609
44089
  heartbeat.activity();
43610
44090
  });
43611
44091
  subprocess.stderr?.on("data", (chunk) => {
43612
- const text13 = chunk.toString();
43613
- stderrBuffer += text13;
43614
- process.stderr.write(text13);
44092
+ const text14 = chunk.toString();
44093
+ stderrBuffer += text14;
44094
+ process.stderr.write(text14);
43615
44095
  heartbeat.activity();
43616
44096
  });
43617
44097
  const result = await subprocess;
@@ -44102,16 +44582,16 @@ function htmlToMarkdown(html) {
44102
44582
  const prefix = "#".repeat(i);
44103
44583
  const regex = new RegExp(`<h${i}[^>]*>([\\s\\S]*?)<\\/h${i}>`, "gi");
44104
44584
  md = md.replace(regex, (_, content) => {
44105
- const text13 = content.replace(/<[^>]*>/g, "").trim();
44106
- return text13 ? `
44585
+ const text14 = content.replace(/<[^>]*>/g, "").trim();
44586
+ return text14 ? `
44107
44587
 
44108
- ${prefix} ${text13}
44588
+ ${prefix} ${text14}
44109
44589
 
44110
44590
  ` : "";
44111
44591
  });
44112
44592
  }
44113
- md = md.replace(/<a\s+[^>]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, href, text13) => {
44114
- const cleanText = text13.replace(/<[^>]*>/g, "").trim();
44593
+ md = md.replace(/<a\s+[^>]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, href, text14) => {
44594
+ const cleanText = text14.replace(/<[^>]*>/g, "").trim();
44115
44595
  if (!cleanText) return "";
44116
44596
  if (href.startsWith("#") || href.startsWith("javascript:")) return cleanText;
44117
44597
  return `[${cleanText}](${href})`;
@@ -44138,8 +44618,8 @@ ${decoded.trim()}
44138
44618
  });
44139
44619
  md = md.replace(/<ul[^>]*>([\s\S]*?)<\/ul>/gi, (_, items) => {
44140
44620
  return "\n" + items.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_2, item) => {
44141
- const text13 = item.replace(/<[^>]*>/g, "").trim();
44142
- return text13 ? `- ${text13}
44621
+ const text14 = item.replace(/<[^>]*>/g, "").trim();
44622
+ return text14 ? `- ${text14}
44143
44623
  ` : "";
44144
44624
  }) + "\n";
44145
44625
  });
@@ -44147,29 +44627,29 @@ ${decoded.trim()}
44147
44627
  let counter = 0;
44148
44628
  return "\n" + items.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_2, item) => {
44149
44629
  counter++;
44150
- const text13 = item.replace(/<[^>]*>/g, "").trim();
44151
- return text13 ? `${counter}. ${text13}
44630
+ const text14 = item.replace(/<[^>]*>/g, "").trim();
44631
+ return text14 ? `${counter}. ${text14}
44152
44632
  ` : "";
44153
44633
  }) + "\n";
44154
44634
  });
44155
44635
  md = md.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, (_, content) => {
44156
- const text13 = content.replace(/<[^>]*>/g, "").trim();
44157
- return text13 ? "\n" + text13.split("\n").map((line) => `> ${line.trim()}`).join("\n") + "\n" : "";
44636
+ const text14 = content.replace(/<[^>]*>/g, "").trim();
44637
+ return text14 ? "\n" + text14.split("\n").map((line) => `> ${line.trim()}`).join("\n") + "\n" : "";
44158
44638
  });
44159
44639
  md = md.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, (_, content) => {
44160
- const text13 = content.replace(/<[^>]*>/g, "").trim();
44161
- return text13 ? `
44640
+ const text14 = content.replace(/<[^>]*>/g, "").trim();
44641
+ return text14 ? `
44162
44642
 
44163
- ${text13}
44643
+ ${text14}
44164
44644
 
44165
44645
  ` : "";
44166
44646
  });
44167
44647
  md = md.replace(/<br\s*\/?>/gi, "\n");
44168
44648
  md = md.replace(
44169
44649
  /<(?:strong|b)[^>]*>([\s\S]*?)<\/(?:strong|b)>/gi,
44170
- (_, text13) => `**${text13.trim()}**`
44650
+ (_, text14) => `**${text14.trim()}**`
44171
44651
  );
44172
- md = md.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, (_, text13) => `*${text13.trim()}*`);
44652
+ md = md.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, (_, text14) => `*${text14.trim()}*`);
44173
44653
  md = md.replace(/<hr\s*\/?>/gi, "\n---\n");
44174
44654
  md = md.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, tableContent) => {
44175
44655
  const rows = [];
@@ -45299,10 +45779,10 @@ function chunkContent(content, chunkSize) {
45299
45779
  const chunks = [];
45300
45780
  for (let i = 0; i < lines.length; i += chunkSize) {
45301
45781
  const chunkLines = lines.slice(i, Math.min(i + chunkSize, lines.length));
45302
- const text13 = chunkLines.join("\n").trim();
45303
- if (text13.length > 10) {
45782
+ const text14 = chunkLines.join("\n").trim();
45783
+ if (text14.length > 10) {
45304
45784
  chunks.push({
45305
- text: text13,
45785
+ text: text14,
45306
45786
  startLine: i + 1,
45307
45787
  endLine: Math.min(i + chunkSize, lines.length)
45308
45788
  });
@@ -45310,8 +45790,8 @@ function chunkContent(content, chunkSize) {
45310
45790
  }
45311
45791
  return chunks;
45312
45792
  }
45313
- function simpleEmbedding(text13) {
45314
- const words = text13.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 1);
45793
+ function simpleEmbedding(text14) {
45794
+ const words = text14.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 1);
45315
45795
  const freq = /* @__PURE__ */ new Map();
45316
45796
  for (const word of words) {
45317
45797
  freq.set(word, (freq.get(word) ?? 0) + 1);
@@ -45337,7 +45817,7 @@ function simpleEmbedding(text13) {
45337
45817
  }
45338
45818
  var embedFn = null;
45339
45819
  var usingFallbackEmbedding = false;
45340
- async function getEmbedding(text13) {
45820
+ async function getEmbedding(text14) {
45341
45821
  if (!embedFn) {
45342
45822
  try {
45343
45823
  const transformers = await import('@xenova/transformers');
@@ -45354,7 +45834,7 @@ async function getEmbedding(text13) {
45354
45834
  usingFallbackEmbedding = true;
45355
45835
  }
45356
45836
  }
45357
- return embedFn(text13);
45837
+ return embedFn(text14);
45358
45838
  }
45359
45839
  async function loadIndex2(indexDir) {
45360
45840
  try {
@@ -45907,23 +46387,23 @@ Examples:
45907
46387
  const pdfData = await pdfParse.default(dataBuffer, {
45908
46388
  max: maxPages
45909
46389
  });
45910
- let text13 = pdfData.text;
46390
+ let text14 = pdfData.text;
45911
46391
  let truncated = false;
45912
46392
  const totalPages = pdfData.numpages;
45913
46393
  if (pages) {
45914
46394
  const range = parsePageRange(pages, totalPages);
45915
- const pageTexts = text13.split(/\f/);
46395
+ const pageTexts = text14.split(/\f/);
45916
46396
  if (pageTexts.length > 1) {
45917
46397
  const selectedPages = pageTexts.slice(range.start - 1, range.end);
45918
- text13 = selectedPages.join("\n\n--- Page Break ---\n\n");
46398
+ text14 = selectedPages.join("\n\n--- Page Break ---\n\n");
45919
46399
  }
45920
46400
  }
45921
- if (text13.length > 5e5) {
45922
- text13 = text13.slice(0, 5e5);
46401
+ if (text14.length > 5e5) {
46402
+ text14 = text14.slice(0, 5e5);
45923
46403
  truncated = true;
45924
46404
  }
45925
46405
  return {
45926
- text: text13,
46406
+ text: text14,
45927
46407
  pages: totalPages,
45928
46408
  metadata: {
45929
46409
  title: pdfData.info?.Title,
@@ -46084,7 +46564,7 @@ Examples:
46084
46564
  description = response.choices[0]?.message?.content ?? "No description generated";
46085
46565
  } else if (selectedProvider === "gemini") {
46086
46566
  model = "gemini-2.0-flash";
46087
- const { GoogleGenerativeAI: GoogleGenerativeAI2 } = await import('@google/generative-ai');
46567
+ const { GoogleGenAI: GoogleGenAI2 } = await import('@google/genai');
46088
46568
  const apiKey = process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
46089
46569
  if (!apiKey) {
46090
46570
  throw new ToolError(
@@ -46092,18 +46572,25 @@ Examples:
46092
46572
  { tool: "read_image" }
46093
46573
  );
46094
46574
  }
46095
- const genAI = new GoogleGenerativeAI2(apiKey);
46096
- const genModel = genAI.getGenerativeModel({ model });
46097
- const result = await genModel.generateContent([
46098
- effectivePrompt,
46099
- {
46100
- inlineData: {
46101
- data: base64,
46102
- mimeType
46575
+ const genAI = new GoogleGenAI2({ apiKey });
46576
+ const result = await genAI.models.generateContent({
46577
+ model,
46578
+ contents: [
46579
+ {
46580
+ role: "user",
46581
+ parts: [
46582
+ { text: effectivePrompt },
46583
+ {
46584
+ inlineData: {
46585
+ data: base64,
46586
+ mimeType
46587
+ }
46588
+ }
46589
+ ]
46103
46590
  }
46104
- }
46105
- ]);
46106
- description = result.response.text() ?? "No description generated";
46591
+ ]
46592
+ });
46593
+ description = result.text ?? "No description generated";
46107
46594
  } else {
46108
46595
  throw new ToolError(`Unsupported provider: ${selectedProvider}`, {
46109
46596
  tool: "read_image"
@@ -46115,7 +46602,7 @@ Examples:
46115
46602
  const pkgMap = {
46116
46603
  anthropic: "@anthropic-ai/sdk",
46117
46604
  openai: "openai",
46118
- gemini: "@google/generative-ai"
46605
+ gemini: "@google/genai"
46119
46606
  };
46120
46607
  const pkg = pkgMap[selectedProvider] ?? selectedProvider;
46121
46608
  throw new ToolError(`Provider SDK not installed. Run: pnpm add ${pkg}`, {
@@ -48158,11 +48645,11 @@ var buildAppCommand = {
48158
48645
  return false;
48159
48646
  }
48160
48647
  if (!isAutonomous && !parsed.skipConfirmation) {
48161
- const confirm23 = await p26.confirm({
48648
+ const confirm22 = await p26.confirm({
48162
48649
  message: `Build "${spec.projectName}" with ${spec.sprints.length} sprints?`,
48163
48650
  initialValue: true
48164
48651
  });
48165
- if (p26.isCancel(confirm23) || !confirm23) {
48652
+ if (p26.isCancel(confirm22) || !confirm22) {
48166
48653
  p26.cancel("Build cancelled.");
48167
48654
  return false;
48168
48655
  }
@@ -49343,24 +49830,24 @@ function formatHtmlLine(line) {
49343
49830
  }
49344
49831
  return null;
49345
49832
  }
49346
- function formatInlineMarkdown(text13) {
49347
- text13 = text13.replace(/\*\*\*(.+?)\*\*\*/g, (_, content) => chalk.bold.italic(content));
49348
- text13 = text13.replace(/\*\*(.+?)\*\*/g, (_, content) => chalk.bold(content));
49349
- text13 = text13.replace(/\*([^*]+)\*/g, (_, content) => chalk.italic(content));
49350
- text13 = text13.replace(/_([^_]+)_/g, (_, content) => chalk.italic(content));
49351
- text13 = text13.replace(/`([^`]+)`/g, (_, content) => chalk.cyan(content));
49352
- text13 = text13.replace(/~~(.+?)~~/g, (_, content) => chalk.strikethrough(content));
49353
- text13 = text13.replace(/\[([^\]]+)\]\([^)]+\)/g, (_, linkText) => chalk.blue.underline(linkText));
49354
- return text13;
49355
- }
49356
- function wrapText(text13, maxWidth) {
49357
- if (maxWidth <= 0) return [text13];
49358
- const plainText = stripAnsi(text13);
49833
+ function formatInlineMarkdown(text14) {
49834
+ text14 = text14.replace(/\*\*\*(.+?)\*\*\*/g, (_, content) => chalk.bold.italic(content));
49835
+ text14 = text14.replace(/\*\*(.+?)\*\*/g, (_, content) => chalk.bold(content));
49836
+ text14 = text14.replace(/\*([^*]+)\*/g, (_, content) => chalk.italic(content));
49837
+ text14 = text14.replace(/_([^_]+)_/g, (_, content) => chalk.italic(content));
49838
+ text14 = text14.replace(/`([^`]+)`/g, (_, content) => chalk.cyan(content));
49839
+ text14 = text14.replace(/~~(.+?)~~/g, (_, content) => chalk.strikethrough(content));
49840
+ text14 = text14.replace(/\[([^\]]+)\]\([^)]+\)/g, (_, linkText) => chalk.blue.underline(linkText));
49841
+ return text14;
49842
+ }
49843
+ function wrapText(text14, maxWidth) {
49844
+ if (maxWidth <= 0) return [text14];
49845
+ const plainText = stripAnsi(text14);
49359
49846
  if (plainText.length <= maxWidth) {
49360
- return [text13];
49847
+ return [text14];
49361
49848
  }
49362
49849
  const lines = [];
49363
- let remaining = text13;
49850
+ let remaining = text14;
49364
49851
  while (true) {
49365
49852
  const plain = stripAnsi(remaining);
49366
49853
  if (plain.length <= maxWidth) break;
@@ -49398,7 +49885,7 @@ function wrapText(text13, maxWidth) {
49398
49885
  if (remaining) {
49399
49886
  lines.push(remaining);
49400
49887
  }
49401
- return lines.length > 0 ? lines : [text13];
49888
+ return lines.length > 0 ? lines : [text14];
49402
49889
  }
49403
49890
  function stripAnsi(str) {
49404
49891
  return str.replace(/\x1b\[[0-9;]*m/g, "");
@@ -49550,9 +50037,9 @@ function printEditDiff(oldStr, newStr) {
49550
50037
  if (lines.length === 0) return;
49551
50038
  const truncate4 = (s) => s.length > termWidth - 2 ? s.slice(0, termWidth - 3) + "\u2026" : s;
49552
50039
  for (const l of lines) {
49553
- const text13 = `+ ${truncate4(l)}`;
49554
- const pad = Math.max(0, termWidth - stripAnsi(text13).length + 2);
49555
- console.log(" " + diffBgAdd(text13 + " ".repeat(pad)));
50040
+ const text14 = `+ ${truncate4(l)}`;
50041
+ const pad = Math.max(0, termWidth - stripAnsi(text14).length + 2);
50042
+ console.log(" " + diffBgAdd(text14 + " ".repeat(pad)));
49556
50043
  }
49557
50044
  return;
49558
50045
  }
@@ -50008,10 +50495,10 @@ function findNextWordBoundary(line, pos) {
50008
50495
  while (i < line.length && line[i] === " ") i++;
50009
50496
  return i;
50010
50497
  }
50011
- function countVisualRows(text13, startCol, termCols) {
50498
+ function countVisualRows(text14, startCol, termCols) {
50012
50499
  let rows = 1;
50013
50500
  let col = startCol;
50014
- for (const char of text13) {
50501
+ for (const char of text14) {
50015
50502
  if (char === "\n") {
50016
50503
  if (col > 0) rows++;
50017
50504
  col = 0;
@@ -50025,11 +50512,11 @@ function countVisualRows(text13, startCol, termCols) {
50025
50512
  }
50026
50513
  return rows;
50027
50514
  }
50028
- function getCursorVisualPos(text13, cursorPos, promptLen, termCols) {
50515
+ function getCursorVisualPos(text14, cursorPos, promptLen, termCols) {
50029
50516
  let row = 0;
50030
50517
  let col = promptLen;
50031
50518
  for (let i = 0; i < cursorPos; i++) {
50032
- if (text13[i] === "\n") {
50519
+ if (text14[i] === "\n") {
50033
50520
  if (col > 0) row++;
50034
50521
  col = 0;
50035
50522
  } else {
@@ -50042,14 +50529,14 @@ function getCursorVisualPos(text13, cursorPos, promptLen, termCols) {
50042
50529
  }
50043
50530
  return { row, col };
50044
50531
  }
50045
- function computeWordWrap(text13, startCol, termCols) {
50532
+ function computeWordWrap(text14, startCol, termCols) {
50046
50533
  const passthrough = {
50047
- display: text13,
50534
+ display: text14,
50048
50535
  toDisplayPos: (p45) => p45,
50049
50536
  toOrigPos: (p45) => p45
50050
50537
  };
50051
- if (!text13 || termCols <= 1) return passthrough;
50052
- const origToDisp = new Int32Array(text13.length + 1);
50538
+ if (!text14 || termCols <= 1) return passthrough;
50539
+ const origToDisp = new Int32Array(text14.length + 1);
50053
50540
  const dispToOrig = [];
50054
50541
  let display = "";
50055
50542
  let col = startCol;
@@ -50065,15 +50552,15 @@ function computeWordWrap(text13, startCol, termCols) {
50065
50552
  col = 0;
50066
50553
  }
50067
50554
  let i = 0;
50068
- while (i < text13.length) {
50069
- const ch = text13[i];
50555
+ while (i < text14.length) {
50556
+ const ch = text14[i];
50070
50557
  if (ch === "\n") {
50071
50558
  emitChar("\n", i++);
50072
50559
  continue;
50073
50560
  }
50074
50561
  if (ch !== " ") {
50075
50562
  let wordEnd = i;
50076
- while (wordEnd < text13.length && text13[wordEnd] !== " " && text13[wordEnd] !== "\n") {
50563
+ while (wordEnd < text14.length && text14[wordEnd] !== " " && text14[wordEnd] !== "\n") {
50077
50564
  wordEnd++;
50078
50565
  }
50079
50566
  const wordLen = wordEnd - i;
@@ -50081,7 +50568,7 @@ function computeWordWrap(text13, startCol, termCols) {
50081
50568
  injectNewline();
50082
50569
  }
50083
50570
  for (let k = i; k < wordEnd; k++) {
50084
- emitChar(text13[k], k);
50571
+ emitChar(text14[k], k);
50085
50572
  if (col >= termCols && k + 1 < wordEnd) {
50086
50573
  injectNewline();
50087
50574
  }
@@ -50093,7 +50580,7 @@ function computeWordWrap(text13, startCol, termCols) {
50093
50580
  col = 0;
50094
50581
  } else {
50095
50582
  let nextWordEnd = i;
50096
- while (nextWordEnd < text13.length && text13[nextWordEnd] !== " " && text13[nextWordEnd] !== "\n") {
50583
+ while (nextWordEnd < text14.length && text14[nextWordEnd] !== " " && text14[nextWordEnd] !== "\n") {
50097
50584
  nextWordEnd++;
50098
50585
  }
50099
50586
  const nextWordLen = nextWordEnd - i;
@@ -50103,10 +50590,10 @@ function computeWordWrap(text13, startCol, termCols) {
50103
50590
  }
50104
50591
  }
50105
50592
  }
50106
- origToDisp[text13.length] = display.length;
50593
+ origToDisp[text14.length] = display.length;
50107
50594
  return {
50108
50595
  display,
50109
- toDisplayPos: (origPos) => origToDisp[Math.min(origPos, text13.length)] ?? display.length,
50596
+ toDisplayPos: (origPos) => origToDisp[Math.min(origPos, text14.length)] ?? display.length,
50110
50597
  toOrigPos: (displayPos) => {
50111
50598
  const dp = Math.max(0, Math.min(displayPos, dispToOrig.length - 1));
50112
50599
  for (let d = dp; d >= 0; d--) {
@@ -50239,11 +50726,11 @@ function createInputHandler(_session) {
50239
50726
  const item = visibleItems[itemIndex];
50240
50727
  const actualIndex = startIndex + itemIndex;
50241
50728
  const isSelected = actualIndex === selectedCompletion;
50242
- const text13 = ` ${item.cmd}`.padEnd(ITEM_WIDTH);
50729
+ const text14 = ` ${item.cmd}`.padEnd(ITEM_WIDTH);
50243
50730
  if (isSelected) {
50244
- output += chalk.bgBlue.white(text13);
50731
+ output += chalk.bgBlue.white(text14);
50245
50732
  } else {
50246
- output += chalk.cyan(text13);
50733
+ output += chalk.cyan(text14);
50247
50734
  }
50248
50735
  }
50249
50736
  }
@@ -50295,8 +50782,8 @@ function createInputHandler(_session) {
50295
50782
  process.stdout.write("\r" + ansiEscapes.eraseDown);
50296
50783
  lastMenuLines = 0;
50297
50784
  }
50298
- function insertTextAtCursor(text13) {
50299
- const cleaned = text13.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
50785
+ function insertTextAtCursor(text14) {
50786
+ const cleaned = text14.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
50300
50787
  const printable = cleaned.replace(/[^\n\x20-\x7E\u00A0-\uFFFF]/g, "");
50301
50788
  if (printable.length === 0) return;
50302
50789
  currentLine = currentLine.slice(0, cursorPos) + printable + currentLine.slice(cursorPos);
@@ -51199,10 +51686,10 @@ function formatWriteFilePreview(toolCall, maxLines = 10) {
51199
51686
  const footer = truncated ? chalk.dim(` \u2514\u2500 ... ${lines.length - maxLines} more lines`) : "";
51200
51687
  return formatted + (footer ? "\n" + footer : "");
51201
51688
  }
51202
- function wrapCommandText(text13, maxWidth = 70, indent = " ") {
51203
- if (text13.length <= maxWidth) return text13;
51689
+ function wrapCommandText(text14, maxWidth = 70, indent = " ") {
51690
+ if (text14.length <= maxWidth) return text14;
51204
51691
  const lines = [];
51205
- let remaining = text13;
51692
+ let remaining = text14;
51206
51693
  while (remaining.length > maxWidth) {
51207
51694
  let breakAt = maxWidth;
51208
51695
  const spaceIdx = remaining.lastIndexOf(" ", maxWidth);
@@ -53469,14 +53956,14 @@ async function startRepl(options = {}) {
53469
53956
  imageCount++;
53470
53957
  }
53471
53958
  }
53472
- const text13 = textParts.join("\n\n").trim();
53473
- if (text13.length > 0) {
53959
+ const text14 = textParts.join("\n\n").trim();
53960
+ if (text14.length > 0) {
53474
53961
  if (imageCount > 0) {
53475
- return `${text13}
53962
+ return `${text14}
53476
53963
 
53477
53964
  [System: The original request included ${imageCount} image(s). Use the image context already provided in this conversation.]`;
53478
53965
  }
53479
- return text13;
53966
+ return text14;
53480
53967
  }
53481
53968
  if (imageCount > 0) {
53482
53969
  return `[System: Retry the previous image-based user request (${imageCount} image(s)). Use the existing image context in the conversation and do not repeat the same failed action.]`;