@corbat-tech/coco 2.27.3 → 2.27.5

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
@@ -13,10 +13,10 @@ import JSON5 from 'json5';
13
13
  import * as crypto from 'crypto';
14
14
  import { randomUUID, randomBytes, createHash } from 'crypto';
15
15
  import * as http from 'http';
16
- import * as p26 from '@clack/prompts';
17
- import chalk from 'chalk';
18
16
  import { execFile, execSync, spawn, execFileSync, exec } from 'child_process';
19
17
  import { promisify } from 'util';
18
+ import * as p26 from '@clack/prompts';
19
+ import chalk from 'chalk';
20
20
  import { Logger } from 'tslog';
21
21
  import Anthropic from '@anthropic-ai/sdk';
22
22
  import { jsonrepair } from 'jsonrepair';
@@ -1477,6 +1477,32 @@ async function exchangeForCopilotToken(githubToken) {
1477
1477
  }
1478
1478
  return await response.json();
1479
1479
  }
1480
+ async function getGitHubLogin(githubToken) {
1481
+ try {
1482
+ const response = await fetch("https://api.github.com/user", {
1483
+ method: "GET",
1484
+ headers: {
1485
+ Authorization: `token ${githubToken}`,
1486
+ Accept: "application/json",
1487
+ "User-Agent": "Corbat-Coco/1.0"
1488
+ }
1489
+ });
1490
+ if (!response.ok) return null;
1491
+ const data = await response.json();
1492
+ return data.login ?? null;
1493
+ } catch {
1494
+ return null;
1495
+ }
1496
+ }
1497
+ async function getGitHubCliToken() {
1498
+ try {
1499
+ const { stdout } = await execFileAsync("gh", ["auth", "token"], { timeout: 5e3 });
1500
+ const token = stdout.trim();
1501
+ return token.length > 0 ? token : null;
1502
+ } catch {
1503
+ return null;
1504
+ }
1505
+ }
1480
1506
  function getCopilotBaseUrl(accountType) {
1481
1507
  if (accountType && accountType in COPILOT_BASE_URLS) {
1482
1508
  return COPILOT_BASE_URLS[accountType];
@@ -1514,10 +1540,11 @@ function isCopilotTokenExpired(creds) {
1514
1540
  }
1515
1541
  async function getValidCopilotToken() {
1516
1542
  const creds = await loadCopilotCredentials();
1517
- if (!creds) return null;
1518
- const envToken = process.env["GITHUB_TOKEN"] || process.env["GH_TOKEN"];
1519
- const githubToken = envToken || creds.githubToken;
1520
- if (!isCopilotTokenExpired(creds) && creds.copilotToken) {
1543
+ const envToken = process.env["COPILOT_GITHUB_TOKEN"] || process.env["GH_TOKEN"] || process.env["GITHUB_TOKEN"];
1544
+ const fallbackGhToken = await getGitHubCliToken();
1545
+ const githubToken = envToken || creds?.githubToken || fallbackGhToken;
1546
+ if (!githubToken) return null;
1547
+ if (creds && !isCopilotTokenExpired(creds) && creds.copilotToken) {
1521
1548
  return {
1522
1549
  token: creds.copilotToken,
1523
1550
  baseUrl: getCopilotBaseUrl(creds.accountType),
@@ -1527,11 +1554,11 @@ async function getValidCopilotToken() {
1527
1554
  try {
1528
1555
  const copilotToken = await exchangeForCopilotToken(githubToken);
1529
1556
  const updatedCreds = {
1530
- ...creds,
1531
- githubToken: creds.githubToken,
1557
+ ...creds ?? { githubToken },
1558
+ githubToken: creds?.githubToken ?? githubToken,
1532
1559
  copilotToken: copilotToken.token,
1533
1560
  copilotTokenExpiresAt: copilotToken.expires_at * 1e3,
1534
- accountType: copilotToken.annotations?.copilot_plan ?? creds.accountType
1561
+ accountType: copilotToken.annotations?.copilot_plan ?? creds?.accountType
1535
1562
  };
1536
1563
  await saveCopilotCredentials(updatedCreds);
1537
1564
  return {
@@ -1547,7 +1574,7 @@ async function getValidCopilotToken() {
1547
1574
  throw error;
1548
1575
  }
1549
1576
  }
1550
- var COPILOT_CLIENT_ID, GITHUB_DEVICE_CODE_URL, GITHUB_TOKEN_URL, COPILOT_TOKEN_URL, COPILOT_BASE_URLS, DEFAULT_COPILOT_BASE_URL, REFRESH_BUFFER_MS, CopilotAuthError, CopilotCredentialsSchema;
1577
+ var COPILOT_CLIENT_ID, GITHUB_DEVICE_CODE_URL, GITHUB_TOKEN_URL, COPILOT_TOKEN_URL, COPILOT_BASE_URLS, DEFAULT_COPILOT_BASE_URL, REFRESH_BUFFER_MS, execFileAsync, CopilotAuthError, CopilotCredentialsSchema;
1551
1578
  var init_copilot = __esm({
1552
1579
  "src/auth/copilot.ts"() {
1553
1580
  COPILOT_CLIENT_ID = "Iv1.b507a08c87ecfe98";
@@ -1561,6 +1588,7 @@ var init_copilot = __esm({
1561
1588
  };
1562
1589
  DEFAULT_COPILOT_BASE_URL = "https://api.githubcopilot.com";
1563
1590
  REFRESH_BUFFER_MS = 6e4;
1591
+ execFileAsync = promisify(execFile);
1564
1592
  CopilotAuthError = class extends Error {
1565
1593
  constructor(message, permanent) {
1566
1594
  super(message);
@@ -1648,13 +1676,13 @@ async function openBrowser(url) {
1648
1676
  const platform = process.platform;
1649
1677
  try {
1650
1678
  if (platform === "darwin") {
1651
- await execFileAsync("open", [sanitizedUrl]);
1679
+ await execFileAsync2("open", [sanitizedUrl]);
1652
1680
  } else if (platform === "win32") {
1653
- await execFileAsync("rundll32", ["url.dll,FileProtocolHandler", sanitizedUrl]);
1681
+ await execFileAsync2("rundll32", ["url.dll,FileProtocolHandler", sanitizedUrl]);
1654
1682
  } else if (isWSL) {
1655
- await execFileAsync("cmd.exe", ["/c", "start", "", sanitizedUrl]);
1683
+ await execFileAsync2("cmd.exe", ["/c", "start", "", sanitizedUrl]);
1656
1684
  } else {
1657
- await execFileAsync("xdg-open", [sanitizedUrl]);
1685
+ await execFileAsync2("xdg-open", [sanitizedUrl]);
1658
1686
  }
1659
1687
  return true;
1660
1688
  } catch {
@@ -1704,7 +1732,7 @@ async function openBrowserFallback(url) {
1704
1732
  }
1705
1733
  for (const { cmd, args } of commands2) {
1706
1734
  try {
1707
- await execFileAsync(cmd, args);
1735
+ await execFileAsync2(cmd, args);
1708
1736
  return true;
1709
1737
  } catch {
1710
1738
  continue;
@@ -2168,6 +2196,10 @@ async function runCopilotDeviceFlow() {
2168
2196
  }
2169
2197
  );
2170
2198
  spinner18.stop(chalk.green("\u2713 GitHub authentication successful!"));
2199
+ const githubLogin = await getGitHubLogin(githubToken);
2200
+ if (githubLogin) {
2201
+ console.log(chalk.dim(` Authenticated as: @${githubLogin}`));
2202
+ }
2171
2203
  console.log(chalk.dim(" Exchanging token for Copilot access..."));
2172
2204
  const copilotToken = await exchangeForCopilotToken(githubToken);
2173
2205
  const creds = {
@@ -2194,6 +2226,11 @@ async function runCopilotDeviceFlow() {
2194
2226
  console.log(chalk.red(" \u2717 GitHub Copilot is not enabled for this account."));
2195
2227
  console.log(chalk.dim(" Please ensure you have an active Copilot subscription:"));
2196
2228
  console.log(chalk.cyan(" \u2192 https://github.com/settings/copilot"));
2229
+ console.log(
2230
+ chalk.dim(
2231
+ " If this account is wrong, sign out of github.com in your browser and run /provider again."
2232
+ )
2233
+ );
2197
2234
  } else if (errorMsg.includes("expired") || errorMsg.includes("timed out")) {
2198
2235
  console.log(chalk.yellow(" \u26A0 Authentication timed out. Please try again."));
2199
2236
  } else if (errorMsg.includes("denied")) {
@@ -2229,7 +2266,7 @@ async function getOrRefreshOAuthToken(provider) {
2229
2266
  }
2230
2267
  return null;
2231
2268
  }
2232
- var execFileAsync;
2269
+ var execFileAsync2;
2233
2270
  var init_flow = __esm({
2234
2271
  "src/auth/flow.ts"() {
2235
2272
  init_oauth();
@@ -2237,7 +2274,7 @@ var init_flow = __esm({
2237
2274
  init_callback_server();
2238
2275
  init_platform();
2239
2276
  init_copilot();
2240
- execFileAsync = promisify(execFile);
2277
+ execFileAsync2 = promisify(execFile);
2241
2278
  }
2242
2279
  });
2243
2280
  function getADCPath() {
@@ -2585,7 +2622,7 @@ function getDefaultModel(provider) {
2585
2622
  case "anthropic":
2586
2623
  return process.env["ANTHROPIC_MODEL"] ?? "claude-opus-4-6";
2587
2624
  case "openai":
2588
- return process.env["OPENAI_MODEL"] ?? "gpt-5.4-codex";
2625
+ return process.env["OPENAI_MODEL"] ?? "gpt-5.3-codex";
2589
2626
  case "gemini":
2590
2627
  return process.env["GEMINI_MODEL"] ?? "gemini-3.1-pro-preview";
2591
2628
  case "vertex":
@@ -3773,7 +3810,7 @@ var init_openai = __esm({
3773
3810
  init_errors();
3774
3811
  init_retry();
3775
3812
  init_tool_call_normalizer();
3776
- DEFAULT_MODEL2 = "gpt-5.4-codex";
3813
+ DEFAULT_MODEL2 = "gpt-5.3-codex";
3777
3814
  CONTEXT_WINDOWS2 = {
3778
3815
  // OpenAI models
3779
3816
  "gpt-4o": 128e3,
@@ -4983,7 +5020,7 @@ var init_codex = __esm({
4983
5020
  init_retry();
4984
5021
  init_tool_call_normalizer();
4985
5022
  CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
4986
- DEFAULT_MODEL3 = "gpt-5.4-codex";
5023
+ DEFAULT_MODEL3 = "gpt-5.3-codex";
4987
5024
  CONTEXT_WINDOWS3 = {
4988
5025
  "gpt-5.4-codex": 2e5,
4989
5026
  "gpt-5.3-codex": 2e5,
@@ -5597,23 +5634,35 @@ var init_copilot2 = __esm({
5597
5634
  CONTEXT_WINDOWS4 = {
5598
5635
  // Claude models — Copilot API caps these at 168 000 (not 200 000 like Anthropic direct)
5599
5636
  "claude-sonnet-4.6": 168e3,
5637
+ "claude-sonnet-4": 168e3,
5600
5638
  "claude-opus-4.6": 168e3,
5639
+ "claude-opus-4.6-fast": 168e3,
5601
5640
  "claude-sonnet-4.5": 168e3,
5602
5641
  "claude-opus-4.5": 168e3,
5603
5642
  "claude-haiku-4.5": 168e3,
5604
5643
  // OpenAI models — chat/completions
5605
5644
  "gpt-4.1": 1048576,
5645
+ "gpt-4o": 128e3,
5606
5646
  // OpenAI models — /responses API (Codex/GPT-5+)
5607
5647
  "gpt-5.4-codex": 4e5,
5648
+ "gpt-5.4": 4e5,
5649
+ "gpt-5.4-mini": 4e5,
5608
5650
  "gpt-5.3-codex": 4e5,
5609
5651
  "gpt-5.2-codex": 4e5,
5610
5652
  "gpt-5.1-codex-max": 4e5,
5653
+ "gpt-5-mini": 4e5,
5611
5654
  "gpt-5.2": 4e5,
5612
5655
  "gpt-5.1": 4e5,
5613
5656
  // Google models
5657
+ "gemini-3.1-pro": 1e6,
5614
5658
  "gemini-3.1-pro-preview": 1e6,
5659
+ "gemini-3-flash": 1e6,
5615
5660
  "gemini-3-flash-preview": 1e6,
5616
- "gemini-2.5-pro": 1048576
5661
+ "gemini-2.5-pro": 1048576,
5662
+ // Evaluation models
5663
+ "grok-code-fast-1": 4e5,
5664
+ "raptor-mini": 4e5,
5665
+ goldeneye: 4e5
5617
5666
  };
5618
5667
  DEFAULT_MODEL4 = "claude-sonnet-4.6";
5619
5668
  COPILOT_HEADERS = {
@@ -5745,11 +5794,12 @@ function createGeminiProvider(config) {
5745
5794
  }
5746
5795
  return provider;
5747
5796
  }
5748
- var DEFAULT_MODEL5, CONTEXT_WINDOWS5, GeminiProvider;
5797
+ var DEFAULT_MODEL5, SKIP_THOUGHT_SIGNATURE_VALIDATOR, CONTEXT_WINDOWS5, GeminiProvider;
5749
5798
  var init_gemini = __esm({
5750
5799
  "src/providers/gemini.ts"() {
5751
5800
  init_errors();
5752
5801
  DEFAULT_MODEL5 = "gemini-3.1-pro-preview";
5802
+ SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator";
5753
5803
  CONTEXT_WINDOWS5 = {
5754
5804
  "gemini-3.1-pro-preview": 1e6,
5755
5805
  "gemini-3.1-flash-lite-preview": 1e6,
@@ -5842,30 +5892,29 @@ var init_gemini = __esm({
5842
5892
  if (text15) {
5843
5893
  yield { type: "text", text: text15 };
5844
5894
  }
5845
- const functionCalls = this.extractFunctionCalls(chunk);
5846
- for (const functionCall of functionCalls) {
5847
- const toolCallId = functionCall.id ?? `gemini_call_${++fallbackToolCounter}`;
5895
+ const toolCalls = this.extractToolCalls(chunk, { includeLegacyFunctionCalls: true });
5896
+ for (const toolCall of toolCalls) {
5897
+ const toolCallId = toolCall.id ?? `gemini_call_${++fallbackToolCounter}`;
5848
5898
  if (emittedToolIds.has(toolCallId)) continue;
5849
5899
  emittedToolIds.add(toolCallId);
5850
- const toolCall = {
5851
- id: toolCallId,
5852
- name: functionCall.name ?? "unknown_function",
5853
- input: functionCall.args ?? {}
5900
+ const normalizedToolCall = {
5901
+ ...toolCall,
5902
+ id: toolCallId
5854
5903
  };
5855
5904
  yield {
5856
5905
  type: "tool_use_start",
5857
5906
  toolCall: {
5858
- id: toolCall.id,
5859
- name: toolCall.name
5907
+ id: normalizedToolCall.id,
5908
+ name: normalizedToolCall.name
5860
5909
  }
5861
5910
  };
5862
5911
  yield {
5863
5912
  type: "tool_use_end",
5864
- toolCall
5913
+ toolCall: normalizedToolCall
5865
5914
  };
5866
5915
  }
5867
5916
  const finishReason = chunk.candidates?.[0]?.finishReason;
5868
- if (functionCalls.length > 0) {
5917
+ if (toolCalls.length > 0) {
5869
5918
  streamStopReason = "tool_use";
5870
5919
  } else if (finishReason) {
5871
5920
  streamStopReason = this.mapFinishReason(finishReason);
@@ -5989,13 +6038,18 @@ var init_gemini = __esm({
5989
6038
  });
5990
6039
  } else if (block.type === "tool_use") {
5991
6040
  const toolUse = block;
5992
- parts.push({
5993
- functionCall: {
5994
- id: toolUse.id,
5995
- name: toolUse.name,
5996
- args: toolUse.input
5997
- }
5998
- });
6041
+ const thoughtSignature = toolUse.geminiThoughtSignature ?? SKIP_THOUGHT_SIGNATURE_VALIDATOR;
6042
+ const functionCall = {
6043
+ id: toolUse.id,
6044
+ name: toolUse.name,
6045
+ args: toolUse.input
6046
+ };
6047
+ const part = {
6048
+ functionCall,
6049
+ thoughtSignature,
6050
+ thought_signature: thoughtSignature
6051
+ };
6052
+ parts.push(part);
5999
6053
  }
6000
6054
  }
6001
6055
  return parts.length > 0 ? parts : [{ text: "" }];
@@ -6019,13 +6073,31 @@ var init_gemini = __esm({
6019
6073
  allowedFunctionNames: [choice.name]
6020
6074
  };
6021
6075
  }
6022
- extractFunctionCalls(response) {
6023
- if (response.functionCalls && response.functionCalls.length > 0) {
6024
- return response.functionCalls;
6076
+ extractThoughtSignatureFromPart(part) {
6077
+ const withSignature = part;
6078
+ return withSignature.thoughtSignature ?? withSignature.thought_signature ?? withSignature.functionCall?.thoughtSignature ?? withSignature.functionCall?.thought_signature;
6079
+ }
6080
+ extractToolCalls(response, options) {
6081
+ const toolCallsFromParts = (response.candidates?.[0]?.content?.parts ?? []).filter((part) => !!part.functionCall).map((part, index) => ({
6082
+ id: part.functionCall.id ?? `gemini_call_${index + 1}`,
6083
+ name: part.functionCall.name ?? "unknown_function",
6084
+ input: part.functionCall.args ?? {},
6085
+ geminiThoughtSignature: this.extractThoughtSignatureFromPart(part)
6086
+ }));
6087
+ if (toolCallsFromParts.length > 0) {
6088
+ return toolCallsFromParts;
6025
6089
  }
6026
- const candidate = response.candidates?.[0];
6027
- const parts = candidate?.content?.parts ?? [];
6028
- return parts.filter((part) => !!part.functionCall).map((part) => part.functionCall).filter(Boolean);
6090
+ if (!options?.includeLegacyFunctionCalls || !response.functionCalls?.length) {
6091
+ return [];
6092
+ }
6093
+ return response.functionCalls.map((functionCall, index) => ({
6094
+ id: functionCall.id ?? `gemini_call_${index + 1}`,
6095
+ name: functionCall.name ?? "unknown_function",
6096
+ input: functionCall.args ?? {},
6097
+ geminiThoughtSignature: this.extractThoughtSignatureFromPart({
6098
+ functionCall
6099
+ })
6100
+ }));
6029
6101
  }
6030
6102
  parseResponse(response, model) {
6031
6103
  const usage = response.usageMetadata;
@@ -6042,11 +6114,7 @@ var init_gemini = __esm({
6042
6114
  }
6043
6115
  parseResponseWithTools(response, model) {
6044
6116
  const usage = response.usageMetadata;
6045
- const toolCalls = this.extractFunctionCalls(response).map((functionCall, index) => ({
6046
- id: functionCall.id ?? `gemini_call_${index + 1}`,
6047
- name: functionCall.name ?? "unknown_function",
6048
- input: functionCall.args ?? {}
6049
- }));
6117
+ const toolCalls = this.extractToolCalls(response, { includeLegacyFunctionCalls: true });
6050
6118
  return {
6051
6119
  id: `gemini-${Date.now()}`,
6052
6120
  content: response.text ?? "",
@@ -6094,6 +6162,13 @@ var init_gemini = __esm({
6094
6162
  });
6095
6163
 
6096
6164
  // src/providers/vertex.ts
6165
+ function extractSseEventData(rawEvent) {
6166
+ const dataLines = rawEvent.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter(Boolean);
6167
+ if (dataLines.length === 0) {
6168
+ return null;
6169
+ }
6170
+ return dataLines.join("\n");
6171
+ }
6097
6172
  function createVertexProvider(config) {
6098
6173
  const provider = new VertexProvider();
6099
6174
  if (config) {
@@ -6102,7 +6177,7 @@ function createVertexProvider(config) {
6102
6177
  }
6103
6178
  return provider;
6104
6179
  }
6105
- var DEFAULT_MODEL6, DEFAULT_BASE_URL, DEFAULT_LOCATION, CONTEXT_WINDOWS6, VertexProvider;
6180
+ var DEFAULT_MODEL6, DEFAULT_BASE_URL, DEFAULT_LOCATION, CONTEXT_WINDOWS6, SKIP_THOUGHT_SIGNATURE_VALIDATOR2, VertexProvider;
6106
6181
  var init_vertex = __esm({
6107
6182
  "src/providers/vertex.ts"() {
6108
6183
  init_errors();
@@ -6112,12 +6187,15 @@ var init_vertex = __esm({
6112
6187
  DEFAULT_BASE_URL = "https://aiplatform.googleapis.com/v1";
6113
6188
  DEFAULT_LOCATION = "global";
6114
6189
  CONTEXT_WINDOWS6 = {
6190
+ "gemini-3-pro-preview": 1048576,
6191
+ "gemini-3-flash-preview": 1048576,
6115
6192
  "gemini-2.5-pro": 1048576,
6116
6193
  "gemini-2.5-flash": 1048576,
6117
6194
  "gemini-2.5-flash-lite": 1048576,
6118
6195
  "gemini-2.0-flash-001": 1048576,
6119
6196
  "gemini-2.0-flash-lite-001": 1048576
6120
6197
  };
6198
+ SKIP_THOUGHT_SIGNATURE_VALIDATOR2 = "skip_thought_signature_validator";
6121
6199
  VertexProvider = class {
6122
6200
  id = "vertex";
6123
6201
  name = "Google Vertex AI Gemini";
@@ -6202,12 +6280,14 @@ var init_vertex = __esm({
6202
6280
  }
6203
6281
  if (part.functionCall) {
6204
6282
  streamToolCallCounter++;
6283
+ const geminiThoughtSignature = part.thoughtSignature ?? part.thought_signature ?? part.functionCall.thoughtSignature ?? part.functionCall.thought_signature;
6205
6284
  yield {
6206
6285
  type: "tool_use_start",
6207
6286
  toolCall: {
6208
6287
  id: `vertex_call_${streamToolCallCounter}`,
6209
6288
  name: part.functionCall.name,
6210
- input: part.functionCall.args ?? {}
6289
+ input: part.functionCall.args ?? {},
6290
+ geminiThoughtSignature
6211
6291
  }
6212
6292
  };
6213
6293
  yield {
@@ -6215,7 +6295,8 @@ var init_vertex = __esm({
6215
6295
  toolCall: {
6216
6296
  id: `vertex_call_${streamToolCallCounter}`,
6217
6297
  name: part.functionCall.name,
6218
- input: part.functionCall.args ?? {}
6298
+ input: part.functionCall.args ?? {},
6299
+ geminiThoughtSignature
6219
6300
  }
6220
6301
  };
6221
6302
  }
@@ -6348,11 +6429,14 @@ var init_vertex = __esm({
6348
6429
  });
6349
6430
  } else if (block.type === "tool_use") {
6350
6431
  const toolUse = block;
6432
+ const thoughtSignature = toolUse.geminiThoughtSignature ?? SKIP_THOUGHT_SIGNATURE_VALIDATOR2;
6351
6433
  parts.push({
6352
6434
  functionCall: {
6353
6435
  name: toolUse.name,
6354
6436
  args: toolUse.input
6355
- }
6437
+ },
6438
+ thoughtSignature,
6439
+ thought_signature: thoughtSignature
6356
6440
  });
6357
6441
  }
6358
6442
  }
@@ -6444,22 +6528,27 @@ var init_vertex = __esm({
6444
6528
  if (done) break;
6445
6529
  buffer += decoder.decode(value, { stream: true });
6446
6530
  while (true) {
6447
- const eventBoundary = buffer.indexOf("\n\n");
6448
- if (eventBoundary === -1) break;
6449
- const rawEvent = buffer.slice(0, eventBoundary);
6450
- buffer = buffer.slice(eventBoundary + 2);
6451
- const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter(Boolean);
6452
- for (const line of dataLines) {
6453
- if (line === "[DONE]") return;
6454
- yield JSON.parse(line);
6531
+ const boundaryMatch = /\r?\n\r?\n/.exec(buffer);
6532
+ if (!boundaryMatch || boundaryMatch.index === void 0) break;
6533
+ const rawEvent = buffer.slice(0, boundaryMatch.index);
6534
+ buffer = buffer.slice(boundaryMatch.index + boundaryMatch[0].length);
6535
+ const data = extractSseEventData(rawEvent);
6536
+ if (!data || data === "[DONE]") {
6537
+ if (data === "[DONE]") return;
6538
+ continue;
6539
+ }
6540
+ try {
6541
+ yield JSON.parse(data);
6542
+ } catch {
6543
+ continue;
6455
6544
  }
6456
6545
  }
6457
6546
  }
6458
- const trailing = buffer.trim();
6459
- if (trailing.startsWith("data:")) {
6460
- const line = trailing.slice(5).trim();
6461
- if (line && line !== "[DONE]") {
6462
- yield JSON.parse(line);
6547
+ const trailingData = extractSseEventData(buffer.trim());
6548
+ if (trailingData && trailingData !== "[DONE]") {
6549
+ try {
6550
+ yield JSON.parse(trailingData);
6551
+ } catch {
6463
6552
  }
6464
6553
  }
6465
6554
  }
@@ -6492,7 +6581,8 @@ var init_vertex = __esm({
6492
6581
  toolCalls.push({
6493
6582
  id: `vertex_call_${toolIndex}`,
6494
6583
  name: part.functionCall.name,
6495
- input: part.functionCall.args ?? {}
6584
+ input: part.functionCall.args ?? {},
6585
+ geminiThoughtSignature: part.thoughtSignature ?? part.thought_signature ?? part.functionCall.thoughtSignature ?? part.functionCall.thought_signature
6496
6586
  });
6497
6587
  }
6498
6588
  }
@@ -6597,6 +6687,7 @@ var init_pricing = __esm({
6597
6687
  "gpt-4o": { inputPerMillion: 2.5, outputPerMillion: 10, contextWindow: 128e3 },
6598
6688
  "gpt-4o-mini": { inputPerMillion: 0.15, outputPerMillion: 0.6, contextWindow: 128e3 },
6599
6689
  // Google Gemini models
6690
+ "gemini-3-pro-preview": { inputPerMillion: 1.25, outputPerMillion: 5, contextWindow: 1e6 },
6600
6691
  "gemini-3.1-pro-preview": { inputPerMillion: 1.25, outputPerMillion: 5, contextWindow: 1e6 },
6601
6692
  "gemini-3-flash-preview": {
6602
6693
  inputPerMillion: 0.15,
@@ -10434,17 +10525,55 @@ async function saveTrustSettings(settings) {
10434
10525
  console.warn(`[Trust] Failed to save trust settings: ${msg}`);
10435
10526
  }
10436
10527
  }
10437
- async function loadTrustedTools(projectPath) {
10528
+ function getProjectTrustSettingsFile(projectPath) {
10529
+ return path39__default.join(projectPath, PROJECT_TRUST_FILE_RELATIVE_PATH);
10530
+ }
10531
+ async function loadProjectTrustSettings(projectPath) {
10438
10532
  const settings = await loadTrustSettings();
10533
+ const legacyTrusted = settings.projectTrusted[projectPath] ?? [];
10534
+ const legacyDenied = settings.projectDenied[projectPath] ?? [];
10535
+ const defaultState = {
10536
+ trusted: legacyTrusted,
10537
+ denied: legacyDenied,
10538
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
10539
+ };
10540
+ try {
10541
+ const content = await fs35__default.readFile(getProjectTrustSettingsFile(projectPath), "utf-8");
10542
+ const raw = JSON.parse(content);
10543
+ return {
10544
+ trusted: raw.trusted ?? defaultState.trusted,
10545
+ denied: raw.denied ?? defaultState.denied,
10546
+ updatedAt: raw.updatedAt ?? defaultState.updatedAt
10547
+ };
10548
+ } catch {
10549
+ return defaultState;
10550
+ }
10551
+ }
10552
+ async function saveProjectTrustSettings(projectPath, settings) {
10553
+ try {
10554
+ const filePath = getProjectTrustSettingsFile(projectPath);
10555
+ await fs35__default.mkdir(path39__default.dirname(filePath), { recursive: true });
10556
+ settings.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
10557
+ await fs35__default.writeFile(filePath, JSON.stringify(settings, null, 2), "utf-8");
10558
+ } catch (error) {
10559
+ const msg = error instanceof Error ? error.message : String(error);
10560
+ console.warn(`[Trust] Failed to save project trust settings: ${msg}`);
10561
+ }
10562
+ }
10563
+ async function loadTrustedTools(projectPath) {
10564
+ const [settings, projectSettings] = await Promise.all([
10565
+ loadTrustSettings(),
10566
+ loadProjectTrustSettings(projectPath)
10567
+ ]);
10439
10568
  const trusted = /* @__PURE__ */ new Set();
10440
10569
  for (const tool of settings.globalTrusted) {
10441
10570
  trusted.add(tool);
10442
10571
  }
10443
- const projectTrusted = settings.projectTrusted[projectPath] ?? [];
10572
+ const projectTrusted = projectSettings.trusted;
10444
10573
  for (const tool of projectTrusted) {
10445
10574
  trusted.add(tool);
10446
10575
  }
10447
- const projectDenied = settings.projectDenied[projectPath] ?? [];
10576
+ const projectDenied = projectSettings.denied;
10448
10577
  for (const tool of projectDenied) {
10449
10578
  trusted.delete(tool);
10450
10579
  }
@@ -10457,61 +10586,54 @@ async function saveTrustedTool(toolName, projectPath, global = false) {
10457
10586
  settings.globalTrusted.push(toolName);
10458
10587
  }
10459
10588
  } else if (projectPath) {
10460
- if (!settings.projectTrusted[projectPath]) {
10461
- settings.projectTrusted[projectPath] = [];
10462
- }
10463
- const projectTrusted = settings.projectTrusted[projectPath];
10464
- if (projectTrusted && !projectTrusted.includes(toolName)) {
10465
- projectTrusted.push(toolName);
10589
+ const projectSettings = await loadProjectTrustSettings(projectPath);
10590
+ if (!projectSettings.trusted.includes(toolName)) {
10591
+ projectSettings.trusted.push(toolName);
10466
10592
  }
10593
+ projectSettings.denied = projectSettings.denied.filter((t) => t !== toolName);
10594
+ await saveProjectTrustSettings(projectPath, projectSettings);
10595
+ }
10596
+ if (global) {
10597
+ await saveTrustSettings(settings);
10467
10598
  }
10468
- await saveTrustSettings(settings);
10469
10599
  }
10470
10600
  async function removeTrustedTool(toolName, projectPath, global = false) {
10471
10601
  const settings = await loadTrustSettings();
10472
10602
  if (global) {
10473
10603
  settings.globalTrusted = settings.globalTrusted.filter((t) => t !== toolName);
10604
+ await saveTrustSettings(settings);
10474
10605
  } else {
10475
- const projectTrusted = settings.projectTrusted[projectPath];
10476
- if (projectTrusted) {
10477
- settings.projectTrusted[projectPath] = projectTrusted.filter((t) => t !== toolName);
10478
- }
10606
+ const projectSettings = await loadProjectTrustSettings(projectPath);
10607
+ projectSettings.trusted = projectSettings.trusted.filter((t) => t !== toolName);
10608
+ await saveProjectTrustSettings(projectPath, projectSettings);
10479
10609
  }
10480
- await saveTrustSettings(settings);
10481
10610
  }
10482
10611
  async function saveDeniedTool(toolName, projectPath) {
10483
- const settings = await loadTrustSettings();
10484
- if (!settings.projectDenied[projectPath]) {
10485
- settings.projectDenied[projectPath] = [];
10486
- }
10487
- const denied = settings.projectDenied[projectPath];
10488
- if (denied && !denied.includes(toolName)) {
10489
- denied.push(toolName);
10490
- }
10491
- const projectTrusted = settings.projectTrusted[projectPath];
10492
- if (projectTrusted) {
10493
- settings.projectTrusted[projectPath] = projectTrusted.filter((t) => t !== toolName);
10612
+ const projectSettings = await loadProjectTrustSettings(projectPath);
10613
+ if (!projectSettings.denied.includes(toolName)) {
10614
+ projectSettings.denied.push(toolName);
10494
10615
  }
10495
- await saveTrustSettings(settings);
10616
+ projectSettings.trusted = projectSettings.trusted.filter((t) => t !== toolName);
10617
+ await saveProjectTrustSettings(projectPath, projectSettings);
10496
10618
  }
10497
10619
  async function removeDeniedTool(toolName, projectPath) {
10498
- const settings = await loadTrustSettings();
10499
- const denied = settings.projectDenied[projectPath];
10500
- if (denied) {
10501
- settings.projectDenied[projectPath] = denied.filter((t) => t !== toolName);
10502
- }
10503
- await saveTrustSettings(settings);
10620
+ const projectSettings = await loadProjectTrustSettings(projectPath);
10621
+ projectSettings.denied = projectSettings.denied.filter((t) => t !== toolName);
10622
+ await saveProjectTrustSettings(projectPath, projectSettings);
10504
10623
  }
10505
10624
  async function getDeniedTools(projectPath) {
10506
- const settings = await loadTrustSettings();
10507
- return settings.projectDenied[projectPath] ?? [];
10625
+ const settings = await loadProjectTrustSettings(projectPath);
10626
+ return settings.denied;
10508
10627
  }
10509
10628
  async function getAllTrustedTools(projectPath) {
10510
- const settings = await loadTrustSettings();
10629
+ const [settings, projectSettings] = await Promise.all([
10630
+ loadTrustSettings(),
10631
+ loadProjectTrustSettings(projectPath)
10632
+ ]);
10511
10633
  return {
10512
10634
  global: settings.globalTrusted,
10513
- project: settings.projectTrusted[projectPath] ?? [],
10514
- denied: settings.projectDenied[projectPath] ?? []
10635
+ project: projectSettings.trusted,
10636
+ denied: projectSettings.denied
10515
10637
  };
10516
10638
  }
10517
10639
  async function initializeSessionTrust(session) {
@@ -10602,7 +10724,7 @@ function getSessionMemory(session) {
10602
10724
  async function reloadSessionMemory(session) {
10603
10725
  await initializeSessionMemory(session);
10604
10726
  }
10605
- var MAX_SKILL_INSTRUCTIONS_CHARS, TRUST_SETTINGS_DIR, TRUST_SETTINGS_FILE, CATEGORY_LABELS, COCO_SYSTEM_PROMPT, SHELL_METACHARACTERS, SAFE_COMMAND_VALIDATORS;
10727
+ var MAX_SKILL_INSTRUCTIONS_CHARS, TRUST_SETTINGS_DIR, TRUST_SETTINGS_FILE, PROJECT_TRUST_FILE_RELATIVE_PATH, CATEGORY_LABELS, COCO_SYSTEM_PROMPT, SHELL_METACHARACTERS, SAFE_COMMAND_VALIDATORS;
10606
10728
  var init_session = __esm({
10607
10729
  "src/cli/repl/session.ts"() {
10608
10730
  init_env();
@@ -10617,6 +10739,7 @@ var init_session = __esm({
10617
10739
  MAX_SKILL_INSTRUCTIONS_CHARS = 16e3;
10618
10740
  TRUST_SETTINGS_DIR = path39__default.dirname(CONFIG_PATHS.trustedTools);
10619
10741
  TRUST_SETTINGS_FILE = CONFIG_PATHS.trustedTools;
10742
+ PROJECT_TRUST_FILE_RELATIVE_PATH = path39__default.join(".coco", "trusted-tools.json");
10620
10743
  CATEGORY_LABELS = {
10621
10744
  mcp: "MCP Connected Services",
10622
10745
  file: "File Operations",
@@ -21608,7 +21731,7 @@ async function openBrowser2(url) {
21608
21731
  }
21609
21732
  for (const { cmd, args } of commands2) {
21610
21733
  try {
21611
- await execFileAsync2(cmd, args);
21734
+ await execFileAsync3(cmd, args);
21612
21735
  return true;
21613
21736
  } catch {
21614
21737
  continue;
@@ -21884,13 +22007,13 @@ async function authenticateMcpOAuth(params) {
21884
22007
  await persistToken(resource, token, { authorizationServer, clientId });
21885
22008
  return token.access_token;
21886
22009
  }
21887
- var execFileAsync2, TOKEN_STORE_PATH, OAUTH_TIMEOUT_MS, logger;
22010
+ var execFileAsync3, TOKEN_STORE_PATH, OAUTH_TIMEOUT_MS, logger;
21888
22011
  var init_oauth2 = __esm({
21889
22012
  "src/mcp/oauth.ts"() {
21890
22013
  init_callback_server();
21891
22014
  init_paths();
21892
22015
  init_logger();
21893
- execFileAsync2 = promisify(execFile);
22016
+ execFileAsync3 = promisify(execFile);
21894
22017
  TOKEN_STORE_PATH = path39__default.join(CONFIG_PATHS.tokens, "mcp-oauth.json");
21895
22018
  OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
21896
22019
  logger = getLogger();
@@ -29111,20 +29234,13 @@ var PROVIDER_DEFINITIONS = {
29111
29234
  },
29112
29235
  // Updated: March 2026 — from platform.openai.com/docs/models
29113
29236
  models: [
29114
- {
29115
- id: "gpt-5.4-codex",
29116
- name: "GPT-5.4 Codex",
29117
- description: "Latest agentic coding model (Mar 2026)",
29118
- contextWindow: 4e5,
29119
- maxOutputTokens: 128e3,
29120
- recommended: true
29121
- },
29122
29237
  {
29123
29238
  id: "gpt-5.3-codex",
29124
29239
  name: "GPT-5.3 Codex",
29125
- description: "Previous agentic coding model (Feb 2026)",
29240
+ description: "Latest available agentic coding model",
29126
29241
  contextWindow: 4e5,
29127
- maxOutputTokens: 128e3
29242
+ maxOutputTokens: 128e3,
29243
+ recommended: true
29128
29244
  },
29129
29245
  {
29130
29246
  id: "gpt-5.2-codex",
@@ -29254,6 +29370,20 @@ var PROVIDER_DEFINITIONS = {
29254
29370
  contextWindow: 2e5,
29255
29371
  maxOutputTokens: 64e3
29256
29372
  },
29373
+ {
29374
+ id: "claude-sonnet-4",
29375
+ name: "Claude Sonnet 4",
29376
+ description: "Previous balanced Claude model via Copilot \u2014 Premium x1",
29377
+ contextWindow: 2e5,
29378
+ maxOutputTokens: 64e3
29379
+ },
29380
+ {
29381
+ id: "claude-opus-4.6-fast",
29382
+ name: "Claude Opus 4.6 (Fast)",
29383
+ description: "Public preview fast Opus mode via Copilot \u2014 Premium x30",
29384
+ contextWindow: 2e5,
29385
+ maxOutputTokens: 128e3
29386
+ },
29257
29387
  // OpenAI models (Codex/GPT-5+ use /responses API, others use /chat/completions)
29258
29388
  {
29259
29389
  id: "gpt-5.4-codex",
@@ -29278,40 +29408,111 @@ var PROVIDER_DEFINITIONS = {
29278
29408
  maxOutputTokens: 128e3
29279
29409
  },
29280
29410
  {
29281
- id: "gpt-5.1-codex-max",
29282
- name: "GPT-5.1 Codex Max",
29283
- description: "Frontier agentic coding model via Copilot \u2014 Premium x1",
29411
+ id: "gpt-5.2",
29412
+ name: "GPT-5.2",
29413
+ description: "General GPT-5 model via Copilot \u2014 Premium x1",
29414
+ contextWindow: 4e5,
29415
+ maxOutputTokens: 128e3
29416
+ },
29417
+ {
29418
+ id: "gpt-5.4",
29419
+ name: "GPT-5.4",
29420
+ description: "Latest general GPT-5 model via Copilot \u2014 Premium x1",
29421
+ contextWindow: 4e5,
29422
+ maxOutputTokens: 128e3
29423
+ },
29424
+ {
29425
+ id: "gpt-5.4-mini",
29426
+ name: "GPT-5.4 mini",
29427
+ description: "Fast GPT-5.4 variant via Copilot \u2014 Premium x0.33",
29428
+ contextWindow: 4e5,
29429
+ maxOutputTokens: 128e3
29430
+ },
29431
+ {
29432
+ id: "gpt-5-mini",
29433
+ name: "GPT-5 mini",
29434
+ description: "Included model via Copilot paid plans \u2014 Premium x0",
29435
+ contextWindow: 4e5,
29436
+ maxOutputTokens: 128e3
29437
+ },
29438
+ {
29439
+ id: "gpt-5.1",
29440
+ name: "GPT-5.1",
29441
+ description: "Legacy GPT-5 model via Copilot \u2014 Premium x1",
29284
29442
  contextWindow: 4e5,
29285
29443
  maxOutputTokens: 128e3
29286
29444
  },
29287
29445
  {
29288
29446
  id: "gpt-4.1",
29289
29447
  name: "GPT-4.1",
29290
- description: "OpenAI long-context model via Copilot (1M) \u2014 Premium x0",
29448
+ description: "Included OpenAI model via Copilot paid plans \u2014 Premium x0",
29291
29449
  contextWindow: 1048576,
29292
29450
  maxOutputTokens: 32768
29293
29451
  },
29452
+ {
29453
+ id: "gpt-4o",
29454
+ name: "GPT-4o",
29455
+ description: "Included fallback/LTS OpenAI model via Copilot paid plans \u2014 Premium x0",
29456
+ contextWindow: 128e3,
29457
+ maxOutputTokens: 16384
29458
+ },
29294
29459
  // Google models
29295
29460
  {
29296
- id: "gemini-3.1-pro-preview",
29461
+ id: "gemini-3.1-pro",
29297
29462
  name: "Gemini 3.1 Pro",
29298
29463
  description: "Google's latest model via Copilot (1M) \u2014 Premium x1",
29299
29464
  contextWindow: 1e6,
29300
29465
  maxOutputTokens: 64e3
29301
29466
  },
29302
29467
  {
29303
- id: "gemini-3-flash-preview",
29468
+ id: "gemini-3.1-pro-preview",
29469
+ name: "Gemini 3.1 Pro (Preview ID)",
29470
+ description: "Compatibility alias for Gemini 3.1 Pro via Copilot \u2014 Premium x1",
29471
+ contextWindow: 1e6,
29472
+ maxOutputTokens: 64e3
29473
+ },
29474
+ {
29475
+ id: "gemini-3-flash",
29304
29476
  name: "Gemini 3 Flash",
29305
29477
  description: "Google's fast model via Copilot (1M) \u2014 Premium x0.33",
29306
29478
  contextWindow: 1e6,
29307
29479
  maxOutputTokens: 64e3
29308
29480
  },
29481
+ {
29482
+ id: "gemini-3-flash-preview",
29483
+ name: "Gemini 3 Flash (Preview ID)",
29484
+ description: "Compatibility alias for Gemini 3 Flash via Copilot \u2014 Premium x0.33",
29485
+ contextWindow: 1e6,
29486
+ maxOutputTokens: 64e3
29487
+ },
29309
29488
  {
29310
29489
  id: "gemini-2.5-pro",
29311
29490
  name: "Gemini 2.5 Pro",
29312
29491
  description: "Google stable model via Copilot (1M) \u2014 Premium x1",
29313
29492
  contextWindow: 1048576,
29314
29493
  maxOutputTokens: 65536
29494
+ },
29495
+ // Evaluation models
29496
+ {
29497
+ id: "grok-code-fast-1",
29498
+ name: "Grok Code Fast 1",
29499
+ description: "xAI coding model via Copilot \u2014 Premium x0.25",
29500
+ contextWindow: 4e5,
29501
+ maxOutputTokens: 128e3
29502
+ },
29503
+ {
29504
+ id: "raptor-mini",
29505
+ name: "Raptor mini",
29506
+ description: "Fine-tuned GPT-5 mini via Copilot \u2014 Premium x0",
29507
+ contextWindow: 4e5,
29508
+ maxOutputTokens: 128e3
29509
+ },
29510
+ {
29511
+ id: "goldeneye",
29512
+ name: "Goldeneye",
29513
+ description: "Fine-tuned GPT-5.1-Codex via Copilot (Free x1)",
29514
+ contextWindow: 4e5,
29515
+ maxOutputTokens: 128e3
29315
29516
  }
29316
29517
  ]
29317
29518
  },
@@ -29340,20 +29541,13 @@ var PROVIDER_DEFINITIONS = {
29340
29541
  vision: true
29341
29542
  },
29342
29543
  models: [
29343
- {
29344
- id: "gpt-5.4-codex",
29345
- name: "GPT-5.4 Codex",
29346
- description: "Latest coding model via ChatGPT subscription (Mar 2026)",
29347
- contextWindow: 2e5,
29348
- maxOutputTokens: 128e3,
29349
- recommended: true
29350
- },
29351
29544
  {
29352
29545
  id: "gpt-5.3-codex",
29353
29546
  name: "GPT-5.3 Codex",
29354
- description: "Previous coding model via ChatGPT subscription",
29547
+ description: "Latest available coding model via ChatGPT subscription",
29355
29548
  contextWindow: 2e5,
29356
- maxOutputTokens: 128e3
29549
+ maxOutputTokens: 128e3,
29550
+ recommended: true
29357
29551
  },
29358
29552
  {
29359
29553
  id: "gpt-5.2-codex",
@@ -29463,13 +29657,27 @@ var PROVIDER_DEFINITIONS = {
29463
29657
  },
29464
29658
  models: [
29465
29659
  {
29466
- id: "gemini-2.5-pro",
29467
- name: "Gemini 2.5 Pro",
29468
- description: "Recommended Vertex model for coding and complex reasoning",
29660
+ id: "gemini-3-pro-preview",
29661
+ name: "Gemini 3 Pro (Preview)",
29662
+ description: "Most capable Vertex Gemini 3 model (preview)",
29469
29663
  contextWindow: 1048576,
29470
29664
  maxOutputTokens: 65536,
29471
29665
  recommended: true
29472
29666
  },
29667
+ {
29668
+ id: "gemini-3-flash-preview",
29669
+ name: "Gemini 3 Flash (Preview)",
29670
+ description: "Fast Gemini 3 model on Vertex (preview)",
29671
+ contextWindow: 1048576,
29672
+ maxOutputTokens: 65536
29673
+ },
29674
+ {
29675
+ id: "gemini-2.5-pro",
29676
+ name: "Gemini 2.5 Pro",
29677
+ description: "Stable high-quality Vertex model for coding and complex reasoning",
29678
+ contextWindow: 1048576,
29679
+ maxOutputTokens: 65536
29680
+ },
29473
29681
  {
29474
29682
  id: "gemini-2.5-flash",
29475
29683
  name: "Gemini 2.5 Flash",
@@ -33750,10 +33958,13 @@ async function selectModelInteractively(models, currentModelId) {
33750
33958
  totalLines++;
33751
33959
  }
33752
33960
  const star = model.recommended ? " \u2B50" : "";
33753
- const ctx = model.contextWindow ? ` ${Math.round(model.contextWindow / 1e3)}K` : "";
33961
+ const multiplierMatch = model.description?.match(
33962
+ /\b(?:Premium|Free)\s*x([0-9]+(?:\.[0-9]+)?)\b/i
33963
+ );
33964
+ const metric = multiplierMatch ? ` x${multiplierMatch[1]}` : model.contextWindow ? ` ${Math.round(model.contextWindow / 1e3)}K` : "";
33754
33965
  const desc = model.description ? ` ${model.description}` : "";
33755
33966
  const hint = model.hint ? ` \u2192 ${model.hint}` : "";
33756
- const suffix = truncate2(`${star}${ctx}${desc}${hint}`, descWidth, "\u2026");
33967
+ const suffix = truncate2(`${star}${metric}${desc}${hint}`, descWidth, "\u2026");
33757
33968
  if (model.disabled) {
33758
33969
  let line2 = chalk.dim(" \u25CB ");
33759
33970
  line2 += chalk.dim(model.id.padEnd(30));
@@ -34076,7 +34287,10 @@ async function setupProviderWithAuth(provider) {
34076
34287
  authMethod = choice;
34077
34288
  }
34078
34289
  if (authMethod === "oauth") {
34290
+ const oauthSpinner = p26.spinner();
34291
+ oauthSpinner.start("Starting OAuth sign-in flow...");
34079
34292
  const result = await runOAuthFlow(provider.id);
34293
+ oauthSpinner.stop(result ? "OAuth sign-in completed" : "OAuth sign-in cancelled");
34080
34294
  if (!result) return null;
34081
34295
  if (provider.id === "copilot") {
34082
34296
  const model3 = await selectModel(provider);
@@ -34160,7 +34374,10 @@ async function setupGcloudADC(provider) {
34160
34374
  );
34161
34375
  console.log(chalk.magenta(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
34162
34376
  console.log();
34377
+ const gcloudCheckSpinner = p26.spinner();
34378
+ gcloudCheckSpinner.start("Checking gcloud CLI...");
34163
34379
  const gcloudInstalled = await isGcloudInstalled();
34380
+ gcloudCheckSpinner.stop(gcloudInstalled ? "gcloud CLI detected" : "gcloud CLI not detected");
34164
34381
  if (!gcloudInstalled) {
34165
34382
  p26.log.error("gcloud CLI is not installed");
34166
34383
  console.log(chalk.dim(" Install it from: https://cloud.google.com/sdk/docs/install"));
@@ -34191,7 +34408,12 @@ async function setupGcloudADC(provider) {
34191
34408
  location: vertexSettings2?.location
34192
34409
  };
34193
34410
  }
34411
+ const adcInspectSpinner = p26.spinner();
34412
+ adcInspectSpinner.start("Checking existing ADC credentials...");
34194
34413
  let adc = await inspectADC();
34414
+ adcInspectSpinner.stop(
34415
+ adc.status === "ok" && adc.token ? "ADC credentials found" : "No reusable ADC credentials found"
34416
+ );
34195
34417
  if (adc.status === "ok" && adc.token) {
34196
34418
  console.log(chalk.green(" \u2713 gcloud ADC is already configured!"));
34197
34419
  console.log();
@@ -34221,9 +34443,17 @@ async function setupGcloudADC(provider) {
34221
34443
  if (p26.isCancel(runLoginNow)) return null;
34222
34444
  if (runLoginNow) {
34223
34445
  p26.log.step("Running `gcloud auth application-default login`...");
34446
+ const loginSpinner = p26.spinner();
34447
+ loginSpinner.start("Launching gcloud login flow (browser may open)...");
34224
34448
  const loginOk = await runGcloudADCLogin();
34449
+ loginSpinner.stop(loginOk ? "gcloud login flow completed" : "gcloud login flow failed");
34225
34450
  if (loginOk) {
34451
+ const recheckSpinner = p26.spinner();
34452
+ recheckSpinner.start("Verifying ADC credentials after login...");
34226
34453
  adc = await inspectADC();
34454
+ recheckSpinner.stop(
34455
+ adc.status === "ok" && adc.token ? "ADC credentials verified" : "ADC verification failed after login"
34456
+ );
34227
34457
  if (adc.status === "ok" && adc.token) {
34228
34458
  console.log(chalk.green(" \u2713 gcloud ADC is now configured."));
34229
34459
  console.log();
@@ -35351,9 +35581,15 @@ async function switchProvider(initialProvider, session) {
35351
35581
  const oauthProviderName = newProvider.id === "copilot" ? "copilot" : newProvider.id === "gemini" ? "gemini" : "openai";
35352
35582
  let oauthConnected = false;
35353
35583
  if (hasOAuth) {
35584
+ const oauthCheckSpinner = p26.spinner();
35585
+ oauthCheckSpinner.start("Checking existing OAuth session...");
35354
35586
  try {
35355
35587
  oauthConnected = await isOAuthConfigured(oauthProviderName);
35588
+ oauthCheckSpinner.stop(
35589
+ oauthConnected ? "Existing OAuth session found" : "No existing OAuth session"
35590
+ );
35356
35591
  } catch {
35592
+ oauthCheckSpinner.stop("Could not verify OAuth session");
35357
35593
  }
35358
35594
  }
35359
35595
  {
@@ -35420,159 +35656,103 @@ async function switchProvider(initialProvider, session) {
35420
35656
  label: "\u274C Cancel",
35421
35657
  hint: ""
35422
35658
  });
35659
+ let authChoice;
35423
35660
  if (authOptions.length > 2) {
35424
- const authChoice = await p26.select({
35661
+ const selected = await p26.select({
35425
35662
  message: `How would you like to authenticate with ${newProvider.name}?`,
35426
35663
  options: authOptions
35427
35664
  });
35428
- if (p26.isCancel(authChoice) || authChoice === "cancel") {
35665
+ if (p26.isCancel(selected) || selected === "cancel") {
35429
35666
  return false;
35430
35667
  }
35431
- if (authChoice === "oauth") {
35432
- const isCopilot = newProvider.id === "copilot";
35433
- const isGemini = newProvider.id === "gemini";
35434
- if (isCopilot) {
35435
- if (oauthConnected) {
35436
- console.log(chalk.dim(`
35668
+ authChoice = selected;
35669
+ } else {
35670
+ authChoice = authOptions.find((option) => option.value !== "cancel")?.value ?? "cancel";
35671
+ if (authChoice === "cancel") return false;
35672
+ }
35673
+ if (authChoice === "oauth") {
35674
+ const isCopilot = newProvider.id === "copilot";
35675
+ const isGemini = newProvider.id === "gemini";
35676
+ if (isCopilot) {
35677
+ if (oauthConnected) {
35678
+ console.log(chalk.dim(`
35437
35679
  Using existing GitHub session...`));
35438
- } else {
35439
- const result = await runOAuthFlow("copilot");
35440
- if (!result) return false;
35441
- }
35442
- selectedAuthMethod = "oauth";
35443
35680
  } else {
35444
- const tokenEnvVar = isGemini ? "GEMINI_OAUTH_TOKEN" : "OPENAI_CODEX_TOKEN";
35445
- if (oauthConnected) {
35446
- try {
35447
- const tokenResult = await getOrRefreshOAuthToken(oauthProviderName);
35448
- if (tokenResult) {
35449
- process.env[tokenEnvVar] = tokenResult.accessToken;
35450
- selectedAuthMethod = "oauth";
35451
- if (!isGemini) internalProviderId = "codex";
35452
- console.log(chalk.dim(`
35681
+ const oauthStartSpinner = p26.spinner();
35682
+ oauthStartSpinner.start("Starting GitHub OAuth sign-in...");
35683
+ const result = await runOAuthFlow("copilot");
35684
+ oauthStartSpinner.stop(
35685
+ result ? "GitHub OAuth sign-in completed" : "GitHub OAuth sign-in cancelled"
35686
+ );
35687
+ if (!result) return false;
35688
+ }
35689
+ selectedAuthMethod = "oauth";
35690
+ } else {
35691
+ const tokenEnvVar = isGemini ? "GEMINI_OAUTH_TOKEN" : "OPENAI_CODEX_TOKEN";
35692
+ if (oauthConnected) {
35693
+ const refreshSpinner = p26.spinner();
35694
+ refreshSpinner.start("Refreshing existing OAuth session...");
35695
+ try {
35696
+ const tokenResult = await getOrRefreshOAuthToken(oauthProviderName);
35697
+ if (tokenResult) {
35698
+ process.env[tokenEnvVar] = tokenResult.accessToken;
35699
+ selectedAuthMethod = "oauth";
35700
+ if (!isGemini) internalProviderId = "codex";
35701
+ refreshSpinner.stop("OAuth session ready");
35702
+ console.log(chalk.dim(`
35453
35703
  Using existing OAuth session...`));
35454
- } else {
35455
- const result = await runOAuthFlow(newProvider.id);
35456
- if (!result) return false;
35457
- process.env[tokenEnvVar] = result.accessToken;
35458
- selectedAuthMethod = "oauth";
35459
- if (!isGemini) internalProviderId = "codex";
35460
- }
35461
- } catch {
35704
+ } else {
35705
+ refreshSpinner.stop("OAuth refresh failed, re-authentication required");
35706
+ const oauthStartSpinner = p26.spinner();
35707
+ oauthStartSpinner.start("Starting OAuth sign-in...");
35462
35708
  const result = await runOAuthFlow(newProvider.id);
35709
+ oauthStartSpinner.stop(
35710
+ result ? "OAuth sign-in completed" : "OAuth sign-in cancelled"
35711
+ );
35463
35712
  if (!result) return false;
35464
35713
  process.env[tokenEnvVar] = result.accessToken;
35465
35714
  selectedAuthMethod = "oauth";
35466
35715
  if (!isGemini) internalProviderId = "codex";
35467
35716
  }
35468
- } else {
35717
+ } catch {
35718
+ refreshSpinner.stop("OAuth refresh failed, re-authentication required");
35719
+ const oauthStartSpinner = p26.spinner();
35720
+ oauthStartSpinner.start("Starting OAuth sign-in...");
35469
35721
  const result = await runOAuthFlow(newProvider.id);
35722
+ oauthStartSpinner.stop(result ? "OAuth sign-in completed" : "OAuth sign-in cancelled");
35470
35723
  if (!result) return false;
35471
35724
  process.env[tokenEnvVar] = result.accessToken;
35472
35725
  selectedAuthMethod = "oauth";
35473
35726
  if (!isGemini) internalProviderId = "codex";
35474
35727
  }
35475
- }
35476
- } else if (authChoice === "gcloud") {
35477
- const adcResult = await setupGcloudADCForProvider();
35478
- if (!adcResult) return false;
35479
- selectedAuthMethod = "gcloud";
35480
- if (newProvider.id === "vertex") {
35481
- const settings = await promptVertexSettings2({
35482
- project: session.config.provider.project,
35483
- location: session.config.provider.location
35484
- });
35485
- if (!settings) return false;
35486
- vertexSettings = settings;
35487
- }
35488
- } else if (authChoice === "apikey") {
35489
- if (apiKey) {
35490
- selectedAuthMethod = "apikey";
35491
- console.log(chalk.dim(`
35492
- Using existing API key...`));
35493
35728
  } else {
35494
- const key = await p26.password({
35495
- message: `Enter your ${newProvider.name} API key:`,
35496
- validate: (v) => !v || v.length < 10 ? "API key too short" : void 0
35497
- });
35498
- if (p26.isCancel(key)) {
35499
- return false;
35500
- }
35501
- process.env[newProvider.envVar] = key;
35502
- selectedAuthMethod = "apikey";
35503
- newApiKeyForSaving = key;
35504
- }
35505
- if (newProvider.id === "vertex") {
35506
- const settings = await promptVertexSettings2({
35507
- project: session.config.provider.project,
35508
- location: session.config.provider.location
35509
- });
35510
- if (!settings) return false;
35511
- vertexSettings = settings;
35512
- }
35513
- } else if (authChoice === "remove") {
35514
- const removeOptions = [];
35515
- if (oauthConnected) {
35516
- removeOptions.push({
35517
- value: "oauth",
35518
- label: "\u{1F510} Remove OAuth session"
35519
- });
35520
- }
35521
- if (apiKey) {
35522
- removeOptions.push({
35523
- value: "apikey",
35524
- label: "\u{1F511} Remove API key"
35525
- });
35526
- }
35527
- if (oauthConnected && apiKey) {
35528
- removeOptions.push({
35529
- value: "all",
35530
- label: "\u{1F5D1}\uFE0F Remove all credentials"
35531
- });
35729
+ const oauthStartSpinner = p26.spinner();
35730
+ oauthStartSpinner.start("Starting OAuth sign-in...");
35731
+ const result = await runOAuthFlow(newProvider.id);
35732
+ oauthStartSpinner.stop(result ? "OAuth sign-in completed" : "OAuth sign-in cancelled");
35733
+ if (!result) return false;
35734
+ process.env[tokenEnvVar] = result.accessToken;
35735
+ selectedAuthMethod = "oauth";
35736
+ if (!isGemini) internalProviderId = "codex";
35532
35737
  }
35533
- removeOptions.push({
35534
- value: "cancel",
35535
- label: "\u274C Cancel"
35536
- });
35537
- const removeChoice = await p26.select({
35538
- message: "What would you like to remove?",
35539
- options: removeOptions
35738
+ }
35739
+ } else if (authChoice === "gcloud") {
35740
+ const adcResult = await setupGcloudADCForProvider();
35741
+ if (!adcResult) return false;
35742
+ selectedAuthMethod = "gcloud";
35743
+ if (newProvider.id === "vertex") {
35744
+ const settings = await promptVertexSettings2({
35745
+ project: session.config.provider.project,
35746
+ location: session.config.provider.location
35540
35747
  });
35541
- if (p26.isCancel(removeChoice) || removeChoice === "cancel") {
35542
- return false;
35543
- }
35544
- if (removeChoice === "oauth" || removeChoice === "all") {
35545
- if (oauthProviderName === "copilot") {
35546
- await deleteCopilotCredentials();
35547
- } else {
35548
- await deleteTokens(oauthProviderName);
35549
- }
35550
- await clearAuthMethod(newProvider.id);
35551
- console.log(chalk.green("\u2713 OAuth session removed"));
35552
- }
35553
- if (removeChoice === "apikey" || removeChoice === "all") {
35554
- delete process.env[newProvider.envVar];
35555
- console.log(chalk.green("\u2713 API key removed from session"));
35556
- console.log(chalk.dim(` Note: If key is in ~/.coco/.env, remove it there too`));
35557
- }
35558
- console.log("");
35559
- return false;
35748
+ if (!settings) return false;
35749
+ vertexSettings = settings;
35560
35750
  }
35561
- } else {
35562
- console.log(chalk.yellow(`
35563
- ${newProvider.emoji} ${newProvider.name} is not configured.`));
35564
- if (hasGcloudADC && !supportsApiKey) {
35565
- const adcResult = await setupGcloudADCForProvider();
35566
- if (!adcResult) return false;
35567
- selectedAuthMethod = "gcloud";
35568
- if (newProvider.id === "vertex") {
35569
- const settings = await promptVertexSettings2({
35570
- project: session.config.provider.project,
35571
- location: session.config.provider.location
35572
- });
35573
- if (!settings) return false;
35574
- vertexSettings = settings;
35575
- }
35751
+ } else if (authChoice === "apikey") {
35752
+ if (apiKey) {
35753
+ selectedAuthMethod = "apikey";
35754
+ console.log(chalk.dim(`
35755
+ Using existing API key...`));
35576
35756
  } else {
35577
35757
  const key = await p26.password({
35578
35758
  message: `Enter your ${newProvider.name} API key:`,
@@ -35585,18 +35765,75 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
35585
35765
  selectedAuthMethod = "apikey";
35586
35766
  newApiKeyForSaving = key;
35587
35767
  }
35768
+ if (newProvider.id === "vertex") {
35769
+ const settings = await promptVertexSettings2({
35770
+ project: session.config.provider.project,
35771
+ location: session.config.provider.location
35772
+ });
35773
+ if (!settings) return false;
35774
+ vertexSettings = settings;
35775
+ }
35776
+ } else if (authChoice === "remove") {
35777
+ const removeOptions = [];
35778
+ if (oauthConnected) {
35779
+ removeOptions.push({
35780
+ value: "oauth",
35781
+ label: "\u{1F510} Remove OAuth session"
35782
+ });
35783
+ }
35784
+ if (apiKey) {
35785
+ removeOptions.push({
35786
+ value: "apikey",
35787
+ label: "\u{1F511} Remove API key"
35788
+ });
35789
+ }
35790
+ if (oauthConnected && apiKey) {
35791
+ removeOptions.push({
35792
+ value: "all",
35793
+ label: "\u{1F5D1}\uFE0F Remove all credentials"
35794
+ });
35795
+ }
35796
+ removeOptions.push({
35797
+ value: "cancel",
35798
+ label: "\u274C Cancel"
35799
+ });
35800
+ const removeChoice = await p26.select({
35801
+ message: "What would you like to remove?",
35802
+ options: removeOptions
35803
+ });
35804
+ if (p26.isCancel(removeChoice) || removeChoice === "cancel") {
35805
+ return false;
35806
+ }
35807
+ if (removeChoice === "oauth" || removeChoice === "all") {
35808
+ if (oauthProviderName === "copilot") {
35809
+ await deleteCopilotCredentials();
35810
+ } else {
35811
+ await deleteTokens(oauthProviderName);
35812
+ }
35813
+ await clearAuthMethod(newProvider.id);
35814
+ console.log(chalk.green("\u2713 OAuth session removed"));
35815
+ }
35816
+ if (removeChoice === "apikey" || removeChoice === "all") {
35817
+ delete process.env[newProvider.envVar];
35818
+ console.log(chalk.green("\u2713 API key removed from session"));
35819
+ console.log(chalk.dim(` Note: If key is in ~/.coco/.env, remove it there too`));
35820
+ }
35821
+ console.log("");
35822
+ return false;
35588
35823
  }
35589
35824
  }
35590
35825
  const rememberedModel = await getLastUsedModel(newProvider.id);
35591
35826
  const recommendedModel = getRecommendedModel(newProvider.id);
35592
35827
  const newModel = rememberedModel || recommendedModel?.id || newProvider.models[0]?.id || "";
35828
+ const resolvedVertexProject = newProvider.id === "vertex" ? (vertexSettings?.project ?? session.config.provider.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "").trim() : void 0;
35829
+ const resolvedVertexLocation = newProvider.id === "vertex" ? (vertexSettings?.location ?? session.config.provider.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? "global").trim() : void 0;
35593
35830
  const spinner18 = p26.spinner();
35594
35831
  spinner18.start(`Connecting to ${newProvider.name}...`);
35595
35832
  try {
35596
35833
  const testProvider = await createProvider(internalProviderId, {
35597
35834
  model: newModel,
35598
- project: vertexSettings?.project,
35599
- location: vertexSettings?.location
35835
+ project: resolvedVertexProject,
35836
+ location: resolvedVertexLocation
35600
35837
  });
35601
35838
  const available = await testProvider.isAvailable();
35602
35839
  if (!available) {
@@ -35610,8 +35847,14 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
35610
35847
  session.config.provider.type = userFacingProviderId;
35611
35848
  session.config.provider.model = newModel;
35612
35849
  if (userFacingProviderId === "vertex") {
35613
- session.config.provider.project = vertexSettings?.project;
35614
- session.config.provider.location = vertexSettings?.location;
35850
+ session.config.provider.project = resolvedVertexProject;
35851
+ session.config.provider.location = resolvedVertexLocation;
35852
+ if (resolvedVertexProject) {
35853
+ process.env["VERTEX_PROJECT"] = resolvedVertexProject;
35854
+ }
35855
+ if (resolvedVertexLocation) {
35856
+ process.env["VERTEX_LOCATION"] = resolvedVertexLocation;
35857
+ }
35615
35858
  } else {
35616
35859
  delete session.config.provider.project;
35617
35860
  delete session.config.provider.location;
@@ -35621,13 +35864,13 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
35621
35864
  type: userFacingProviderId,
35622
35865
  model: newModel,
35623
35866
  apiKey: newApiKeyForSaving,
35624
- project: vertexSettings?.project,
35625
- location: vertexSettings?.location
35867
+ project: resolvedVertexProject,
35868
+ location: resolvedVertexLocation
35626
35869
  });
35627
35870
  } else {
35628
35871
  await saveProviderPreference(userFacingProviderId, newModel, {
35629
- project: vertexSettings?.project,
35630
- location: vertexSettings?.location
35872
+ project: resolvedVertexProject,
35873
+ location: resolvedVertexLocation
35631
35874
  });
35632
35875
  }
35633
35876
  console.log(chalk.green(`
@@ -35650,13 +35893,21 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
35650
35893
  return false;
35651
35894
  }
35652
35895
  async function setupGcloudADCForProvider(_provider) {
35896
+ const gcloudCheckSpinner = p26.spinner();
35897
+ gcloudCheckSpinner.start("Checking gcloud CLI...");
35653
35898
  const gcloudInstalled = await isGcloudInstalled();
35899
+ gcloudCheckSpinner.stop(gcloudInstalled ? "gcloud CLI detected" : "gcloud CLI not detected");
35654
35900
  if (!gcloudInstalled) {
35655
35901
  p26.log.error("gcloud CLI is not installed");
35656
35902
  console.log(chalk.dim(" Install it from: https://cloud.google.com/sdk/docs/install\n"));
35657
35903
  return false;
35658
35904
  }
35905
+ const adcInspectSpinner = p26.spinner();
35906
+ adcInspectSpinner.start("Checking existing ADC credentials...");
35659
35907
  const adc = await inspectADC();
35908
+ adcInspectSpinner.stop(
35909
+ adc.status === "ok" && adc.token ? "ADC credentials found" : "No reusable ADC credentials found"
35910
+ );
35660
35911
  if (adc.status === "ok" && adc.token) {
35661
35912
  console.log(chalk.green(" \u2713 gcloud ADC is already configured!\n"));
35662
35913
  return true;
@@ -35672,9 +35923,17 @@ async function setupGcloudADCForProvider(_provider) {
35672
35923
  if (p26.isCancel(runLoginNow)) return false;
35673
35924
  if (runLoginNow) {
35674
35925
  p26.log.step("Running `gcloud auth application-default login`...");
35926
+ const loginSpinner = p26.spinner();
35927
+ loginSpinner.start("Launching gcloud login flow (browser may open)...");
35675
35928
  const loginOk = await runGcloudADCLogin();
35929
+ loginSpinner.stop(loginOk ? "gcloud login flow completed" : "gcloud login flow failed");
35676
35930
  if (loginOk) {
35931
+ const recheckSpinner = p26.spinner();
35932
+ recheckSpinner.start("Verifying ADC credentials after login...");
35677
35933
  const refreshed = await inspectADC();
35934
+ recheckSpinner.stop(
35935
+ refreshed.status === "ok" && refreshed.token ? "ADC credentials verified" : "ADC verification failed after login"
35936
+ );
35678
35937
  if (refreshed.status === "ok" && refreshed.token) {
35679
35938
  console.log(chalk.green(" \u2713 gcloud ADC is now configured.\n"));
35680
35939
  return true;
@@ -38947,10 +39206,8 @@ async function revokePath(dirPath, _session) {
38947
39206
 
38948
39207
  // src/cli/repl/commands/permissions.ts
38949
39208
  init_session();
38950
- init_paths();
38951
39209
 
38952
39210
  // src/cli/repl/recommended-permissions.ts
38953
- init_paths();
38954
39211
  init_session();
38955
39212
  var RECOMMENDED_GLOBAL = [
38956
39213
  // ── Coco native tools (read-only) ──
@@ -39286,100 +39543,94 @@ var RECOMMENDED_DENY = [
39286
39543
  "bash:eval",
39287
39544
  "bash:source"
39288
39545
  ];
39289
- function getProjectPreferenceKey(projectPath) {
39290
- return path39__default.resolve(projectPath);
39546
+ function getProjectPermissionStatePath(projectPath) {
39547
+ return path39__default.join(projectPath, ".coco", "recommended-permissions.json");
39291
39548
  }
39292
- async function loadPermissionPreferences() {
39549
+ async function resolvePermissionScopePath(projectPath) {
39550
+ let resolved = path39__default.resolve(projectPath);
39293
39551
  try {
39294
- const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
39295
- const config = JSON.parse(content);
39296
- return {
39297
- recommendedAllowlistApplied: config.recommendedAllowlistApplied,
39298
- recommendedAllowlistDismissed: config.recommendedAllowlistDismissed,
39299
- recommendedAllowlistPrompted: config.recommendedAllowlistPrompted,
39300
- recommendedAllowlistPromptedProjects: config.recommendedAllowlistPromptedProjects,
39301
- recommendedAllowlistAppliedProjects: config.recommendedAllowlistAppliedProjects,
39302
- recommendedAllowlistDismissedProjects: config.recommendedAllowlistDismissedProjects
39303
- };
39552
+ resolved = await fs35__default.realpath(resolved);
39304
39553
  } catch {
39305
- return {};
39306
39554
  }
39307
- }
39308
- async function savePermissionPreference(key, value) {
39309
- try {
39310
- let config = {};
39555
+ let current = resolved;
39556
+ while (true) {
39311
39557
  try {
39312
- const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
39313
- config = JSON.parse(content);
39558
+ await fs35__default.access(path39__default.join(current, ".git"));
39559
+ return current;
39314
39560
  } catch {
39315
39561
  }
39316
- config[key] = value;
39317
- await fs35__default.mkdir(path39__default.dirname(CONFIG_PATHS.config), { recursive: true });
39318
- await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
39319
- } catch {
39562
+ const parent = path39__default.dirname(current);
39563
+ if (parent === current) break;
39564
+ current = parent;
39320
39565
  }
39566
+ return resolved;
39321
39567
  }
39322
- function isRecommendedAllowlistAppliedForProject(prefs, projectPath) {
39323
- const projectKey = getProjectPreferenceKey(projectPath);
39324
- if (prefs.recommendedAllowlistAppliedProjects?.[projectKey] === true) {
39325
- return true;
39326
- }
39327
- if (prefs.recommendedAllowlistApplied === true && !prefs.recommendedAllowlistAppliedProjects) {
39328
- return true;
39329
- }
39330
- return false;
39331
- }
39332
- function isRecommendedAllowlistDismissedForProject(prefs, projectPath) {
39333
- const projectKey = getProjectPreferenceKey(projectPath);
39334
- if (prefs.recommendedAllowlistDismissedProjects?.[projectKey] === true) {
39335
- return true;
39336
- }
39337
- if (prefs.recommendedAllowlistDismissed === true && !prefs.recommendedAllowlistDismissedProjects) {
39338
- return true;
39568
+ async function loadProjectPermissionState(projectPath) {
39569
+ const defaultState = {
39570
+ applied: false,
39571
+ dismissed: false,
39572
+ prompted: false,
39573
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
39574
+ };
39575
+ try {
39576
+ const content = await fs35__default.readFile(getProjectPermissionStatePath(projectPath), "utf-8");
39577
+ const parsed = JSON.parse(content);
39578
+ return {
39579
+ applied: parsed.applied ?? defaultState.applied,
39580
+ dismissed: parsed.dismissed ?? defaultState.dismissed,
39581
+ prompted: parsed.prompted ?? defaultState.prompted,
39582
+ updatedAt: parsed.updatedAt ?? defaultState.updatedAt
39583
+ };
39584
+ } catch {
39585
+ return defaultState;
39339
39586
  }
39340
- return false;
39341
39587
  }
39342
- async function saveProjectPermissionPreference(key, projectPath, value) {
39588
+ async function saveProjectPermissionState(projectPath, update) {
39343
39589
  try {
39344
- let config = {};
39345
- try {
39346
- const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
39347
- config = JSON.parse(content);
39348
- } catch {
39349
- }
39350
- const projectKey = getProjectPreferenceKey(projectPath);
39351
- const currentMap = config[key] ?? {};
39352
- config[key] = {
39353
- ...currentMap,
39354
- [projectKey]: value
39590
+ const current = await loadProjectPermissionState(projectPath);
39591
+ const next = {
39592
+ ...current,
39593
+ ...update,
39594
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
39355
39595
  };
39356
- await fs35__default.mkdir(path39__default.dirname(CONFIG_PATHS.config), { recursive: true });
39357
- await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
39596
+ const filePath = getProjectPermissionStatePath(projectPath);
39597
+ await fs35__default.mkdir(path39__default.dirname(filePath), { recursive: true });
39598
+ await fs35__default.writeFile(filePath, JSON.stringify(next, null, 2), "utf-8");
39358
39599
  } catch {
39359
39600
  }
39360
39601
  }
39602
+ async function getProjectPermissionState(projectPath) {
39603
+ const scopePath = await resolvePermissionScopePath(projectPath);
39604
+ return loadProjectPermissionState(scopePath);
39605
+ }
39606
+ async function saveProjectPermissionPreference(key, projectPath, value) {
39607
+ const scopePath = await resolvePermissionScopePath(projectPath);
39608
+ if (key === "recommendedAllowlistAppliedProjects") {
39609
+ await saveProjectPermissionState(scopePath, { applied: value });
39610
+ return;
39611
+ }
39612
+ await saveProjectPermissionState(scopePath, { dismissed: value });
39613
+ }
39361
39614
  async function shouldShowPermissionSuggestion(projectPath = process.cwd()) {
39362
- const prefs = await loadPermissionPreferences();
39363
- if (isRecommendedAllowlistDismissedForProject(prefs, projectPath)) {
39615
+ const state = await getProjectPermissionState(projectPath);
39616
+ if (state.dismissed) {
39364
39617
  return false;
39365
39618
  }
39366
- if (isRecommendedAllowlistAppliedForProject(prefs, projectPath)) {
39619
+ if (state.applied) {
39367
39620
  return false;
39368
39621
  }
39369
39622
  return true;
39370
39623
  }
39371
39624
  async function applyRecommendedPermissions(projectPath = process.cwd()) {
39625
+ const scopePath = await resolvePermissionScopePath(projectPath);
39372
39626
  for (const tool of [...RECOMMENDED_GLOBAL, ...RECOMMENDED_PROJECT]) {
39373
39627
  await saveTrustedTool(tool, projectPath, false);
39374
39628
  }
39375
- await saveProjectPermissionPreference("recommendedAllowlistAppliedProjects", projectPath, true);
39376
- await saveProjectPermissionPreference(
39377
- "recommendedAllowlistDismissedProjects",
39378
- projectPath,
39379
- false
39380
- );
39629
+ await saveProjectPermissionPreference("recommendedAllowlistAppliedProjects", scopePath, true);
39630
+ await saveProjectPermissionPreference("recommendedAllowlistDismissedProjects", scopePath, false);
39381
39631
  }
39382
39632
  async function showPermissionSuggestion(projectPath = process.cwd()) {
39633
+ const scopePath = await resolvePermissionScopePath(projectPath);
39383
39634
  console.log();
39384
39635
  console.log(chalk.magenta.bold(" \u{1F4CB} Recommended Permissions"));
39385
39636
  console.log();
@@ -39390,7 +39641,7 @@ async function showPermissionSuggestion(projectPath = process.cwd()) {
39390
39641
  );
39391
39642
  console.log(chalk.dim(" \u2022 Deny: sudo, git push, docker push, inline code exec, DNS exfil..."));
39392
39643
  console.log();
39393
- console.log(chalk.dim(" Stored in ~/.coco/trusted-tools.json \u2014 edit manually or let"));
39644
+ console.log(chalk.dim(" Stored in .coco/trusted-tools.json \u2014 edit manually or let"));
39394
39645
  console.log(chalk.dim(" Coco manage it when you approve actions from the prompt."));
39395
39646
  console.log(chalk.dim(" Note: applying here affects only the current project."));
39396
39647
  console.log();
@@ -39411,11 +39662,7 @@ async function showPermissionSuggestion(projectPath = process.cwd()) {
39411
39662
  return;
39412
39663
  }
39413
39664
  if (action === "dismiss") {
39414
- await saveProjectPermissionPreference(
39415
- "recommendedAllowlistDismissedProjects",
39416
- projectPath,
39417
- true
39418
- );
39665
+ await saveProjectPermissionPreference("recommendedAllowlistDismissedProjects", scopePath, true);
39419
39666
  console.log(chalk.dim(" Won't show again. Use /permissions to apply later."));
39420
39667
  return;
39421
39668
  }
@@ -39515,12 +39762,12 @@ var permissionsCommand = {
39515
39762
  };
39516
39763
  async function showStatus(session) {
39517
39764
  const tools = await getAllTrustedTools(session.projectPath);
39518
- const prefs = await loadPermissionPreferences();
39765
+ const permissionState = await getProjectPermissionState(session.projectPath);
39519
39766
  console.log();
39520
39767
  console.log(chalk.magenta.bold(" \u{1F510} Tool Permissions"));
39521
39768
  console.log();
39522
39769
  const allowCount = RECOMMENDED_GLOBAL.length + RECOMMENDED_PROJECT.length;
39523
- if (isRecommendedAllowlistAppliedForProject(prefs, session.projectPath)) {
39770
+ if (permissionState.applied) {
39524
39771
  console.log(
39525
39772
  chalk.green(" \u2713 Recommended allowlist applied") + chalk.dim(` (${allowCount} allow, ${RECOMMENDED_DENY.length} deny)`)
39526
39773
  );
@@ -39601,25 +39848,26 @@ async function resetPermissions(session) {
39601
39848
  return;
39602
39849
  }
39603
39850
  session.trustedTools.clear();
39604
- const emptySettings = {
39605
- globalTrusted: [],
39606
- projectTrusted: {},
39607
- projectDenied: {},
39851
+ const emptyProjectSettings = {
39852
+ trusted: [],
39853
+ denied: [],
39608
39854
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
39609
39855
  };
39610
39856
  try {
39611
- await fs35__default.writeFile(CONFIG_PATHS.trustedTools, JSON.stringify(emptySettings, null, 2), "utf-8");
39857
+ const projectTrustPath = `${session.projectPath}/.coco/trusted-tools.json`;
39858
+ await fs35__default.mkdir(`${session.projectPath}/.coco`, { recursive: true });
39859
+ await fs35__default.writeFile(projectTrustPath, JSON.stringify(emptyProjectSettings, null, 2), "utf-8");
39612
39860
  } catch {
39613
39861
  }
39614
- await savePermissionPreference("recommendedAllowlistApplied", false);
39862
+ const permissionScopePath = session.projectPath;
39615
39863
  await saveProjectPermissionPreference(
39616
39864
  "recommendedAllowlistAppliedProjects",
39617
- session.projectPath,
39865
+ permissionScopePath,
39618
39866
  false
39619
39867
  );
39620
39868
  await saveProjectPermissionPreference(
39621
39869
  "recommendedAllowlistDismissedProjects",
39622
- session.projectPath,
39870
+ permissionScopePath,
39623
39871
  false
39624
39872
  );
39625
39873
  console.log(chalk.green(" \u2713 All tool permissions reset."));
@@ -43161,7 +43409,8 @@ var AgentManager = class extends EventEmitter {
43161
43409
  type: "tool_use",
43162
43410
  id: tc.id,
43163
43411
  name: tc.name,
43164
- input: tc.input
43412
+ input: tc.input,
43413
+ geminiThoughtSignature: tc.geminiThoughtSignature
43165
43414
  }));
43166
43415
  const assistantContent = response.content ? [{ type: "text", text: response.content }, ...toolUses] : toolUses;
43167
43416
  messages.push({ role: "assistant", content: assistantContent });
@@ -47712,7 +47961,13 @@ var SuggestImprovementsSchema = z.object({
47712
47961
  context: z.string().optional().describe("Additional context about the code")
47713
47962
  });
47714
47963
  async function analyzeAndSuggest(filePath, _context) {
47715
- const rawContent = await fs46.readFile(filePath, "utf-8");
47964
+ let rawContent = await fs46.readFile(filePath, "utf-8");
47965
+ if (typeof rawContent !== "string") {
47966
+ const defaultReadFile = fs46.default?.readFile;
47967
+ if (typeof defaultReadFile === "function") {
47968
+ rawContent = await defaultReadFile(filePath, "utf-8");
47969
+ }
47970
+ }
47716
47971
  const content = typeof rawContent === "string" ? rawContent : String(rawContent ?? "");
47717
47972
  const lines = content.split("\n");
47718
47973
  const suggestions = [];
@@ -49068,7 +49323,7 @@ function buildProgressBar(pct) {
49068
49323
 
49069
49324
  // src/cli/repl/worktree/manager.ts
49070
49325
  init_logger();
49071
- var execFileAsync3 = promisify(execFile);
49326
+ var execFileAsync4 = promisify(execFile);
49072
49327
  var WORKTREES_DIR = ".worktrees";
49073
49328
  var WorktreeManager = class {
49074
49329
  worktrees = /* @__PURE__ */ new Map();
@@ -49292,7 +49547,7 @@ var WorktreeManager = class {
49292
49547
  try {
49293
49548
  await this.git(["push", "-u", "origin", worktree.branch]);
49294
49549
  const title = options.message ?? `Agent: ${worktree.name}`;
49295
- const { stdout } = await execFileAsync3(
49550
+ const { stdout } = await execFileAsync4(
49296
49551
  "gh",
49297
49552
  [
49298
49553
  "pr",
@@ -49318,10 +49573,10 @@ var WorktreeManager = class {
49318
49573
  }
49319
49574
  // ── Helpers ──────────────────────────────────────────────────────
49320
49575
  async git(args) {
49321
- return execFileAsync3("git", args, { cwd: this.projectRoot });
49576
+ return execFileAsync4("git", args, { cwd: this.projectRoot });
49322
49577
  }
49323
49578
  async gitIn(cwd, args) {
49324
- return execFileAsync3("git", args, { cwd });
49579
+ return execFileAsync4("git", args, { cwd });
49325
49580
  }
49326
49581
  async countChangedFiles(branch) {
49327
49582
  try {
@@ -52951,7 +53206,8 @@ ${tail}`;
52951
53206
  toolCallBuilders.set(id, {
52952
53207
  id,
52953
53208
  name: toolName,
52954
- input: {}
53209
+ input: {},
53210
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
52955
53211
  });
52956
53212
  if (toolName) {
52957
53213
  options.onToolPreparing?.(toolName);
@@ -52964,14 +53220,16 @@ ${tail}`;
52964
53220
  const finalToolCall = {
52965
53221
  id: builder.id,
52966
53222
  name: chunk.toolCall.name ?? builder.name,
52967
- input: chunk.toolCall.input ?? builder.input
53223
+ input: chunk.toolCall.input ?? builder.input,
53224
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature ?? builder.geminiThoughtSignature
52968
53225
  };
52969
53226
  collectedToolCalls.push(finalToolCall);
52970
53227
  } else if (chunk.toolCall.id && chunk.toolCall.name) {
52971
53228
  collectedToolCalls.push({
52972
53229
  id: chunk.toolCall.id,
52973
53230
  name: chunk.toolCall.name,
52974
- input: chunk.toolCall.input ?? {}
53231
+ input: chunk.toolCall.input ?? {},
53232
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
52975
53233
  });
52976
53234
  }
52977
53235
  }
@@ -53228,7 +53486,8 @@ ${tail}`;
53228
53486
  type: "tool_use",
53229
53487
  id: toolCall.id,
53230
53488
  name: toolCall.name,
53231
- input: toolCall.input
53489
+ input: toolCall.input,
53490
+ geminiThoughtSignature: toolCall.geminiThoughtSignature
53232
53491
  });
53233
53492
  const declineReason = declinedTools.get(toolCall.id);
53234
53493
  if (declineReason) {
@@ -54012,11 +54271,15 @@ async function startRepl(options = {}) {
54012
54271
  }
54013
54272
  session.config = configured;
54014
54273
  const internalProviderId = getInternalProviderId(session.config.provider.type);
54274
+ const initialVertexProject = session.config.provider.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"];
54275
+ const initialVertexLocation = session.config.provider.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"];
54015
54276
  let provider;
54016
54277
  try {
54017
54278
  provider = await createProvider(internalProviderId, {
54018
54279
  model: session.config.provider.model || void 0,
54019
- maxTokens: session.config.provider.maxTokens
54280
+ maxTokens: session.config.provider.maxTokens,
54281
+ project: initialVertexProject,
54282
+ location: initialVertexLocation
54020
54283
  });
54021
54284
  } catch (error) {
54022
54285
  p26.log.error(
@@ -54401,7 +54664,9 @@ async function startRepl(options = {}) {
54401
54664
  const newInternalId = getInternalProviderId(session.config.provider.type);
54402
54665
  provider = await createProvider(newInternalId, {
54403
54666
  model: session.config.provider.model || void 0,
54404
- maxTokens: session.config.provider.maxTokens
54667
+ maxTokens: session.config.provider.maxTokens,
54668
+ project: session.config.provider.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"],
54669
+ location: session.config.provider.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"]
54405
54670
  });
54406
54671
  setAgentProvider(provider);
54407
54672
  initializeContextManager(session, provider);