@corbat-tech/coco 2.27.4 → 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,
@@ -5757,11 +5794,12 @@ function createGeminiProvider(config) {
5757
5794
  }
5758
5795
  return provider;
5759
5796
  }
5760
- var DEFAULT_MODEL5, CONTEXT_WINDOWS5, GeminiProvider;
5797
+ var DEFAULT_MODEL5, SKIP_THOUGHT_SIGNATURE_VALIDATOR, CONTEXT_WINDOWS5, GeminiProvider;
5761
5798
  var init_gemini = __esm({
5762
5799
  "src/providers/gemini.ts"() {
5763
5800
  init_errors();
5764
5801
  DEFAULT_MODEL5 = "gemini-3.1-pro-preview";
5802
+ SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator";
5765
5803
  CONTEXT_WINDOWS5 = {
5766
5804
  "gemini-3.1-pro-preview": 1e6,
5767
5805
  "gemini-3.1-flash-lite-preview": 1e6,
@@ -5854,30 +5892,29 @@ var init_gemini = __esm({
5854
5892
  if (text15) {
5855
5893
  yield { type: "text", text: text15 };
5856
5894
  }
5857
- const functionCalls = this.extractFunctionCalls(chunk);
5858
- for (const functionCall of functionCalls) {
5859
- 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}`;
5860
5898
  if (emittedToolIds.has(toolCallId)) continue;
5861
5899
  emittedToolIds.add(toolCallId);
5862
- const toolCall = {
5863
- id: toolCallId,
5864
- name: functionCall.name ?? "unknown_function",
5865
- input: functionCall.args ?? {}
5900
+ const normalizedToolCall = {
5901
+ ...toolCall,
5902
+ id: toolCallId
5866
5903
  };
5867
5904
  yield {
5868
5905
  type: "tool_use_start",
5869
5906
  toolCall: {
5870
- id: toolCall.id,
5871
- name: toolCall.name
5907
+ id: normalizedToolCall.id,
5908
+ name: normalizedToolCall.name
5872
5909
  }
5873
5910
  };
5874
5911
  yield {
5875
5912
  type: "tool_use_end",
5876
- toolCall
5913
+ toolCall: normalizedToolCall
5877
5914
  };
5878
5915
  }
5879
5916
  const finishReason = chunk.candidates?.[0]?.finishReason;
5880
- if (functionCalls.length > 0) {
5917
+ if (toolCalls.length > 0) {
5881
5918
  streamStopReason = "tool_use";
5882
5919
  } else if (finishReason) {
5883
5920
  streamStopReason = this.mapFinishReason(finishReason);
@@ -6001,13 +6038,18 @@ var init_gemini = __esm({
6001
6038
  });
6002
6039
  } else if (block.type === "tool_use") {
6003
6040
  const toolUse = block;
6004
- parts.push({
6005
- functionCall: {
6006
- id: toolUse.id,
6007
- name: toolUse.name,
6008
- args: toolUse.input
6009
- }
6010
- });
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);
6011
6053
  }
6012
6054
  }
6013
6055
  return parts.length > 0 ? parts : [{ text: "" }];
@@ -6031,13 +6073,31 @@ var init_gemini = __esm({
6031
6073
  allowedFunctionNames: [choice.name]
6032
6074
  };
6033
6075
  }
6034
- extractFunctionCalls(response) {
6035
- if (response.functionCalls && response.functionCalls.length > 0) {
6036
- 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;
6037
6089
  }
6038
- const candidate = response.candidates?.[0];
6039
- const parts = candidate?.content?.parts ?? [];
6040
- 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
+ }));
6041
6101
  }
6042
6102
  parseResponse(response, model) {
6043
6103
  const usage = response.usageMetadata;
@@ -6054,11 +6114,7 @@ var init_gemini = __esm({
6054
6114
  }
6055
6115
  parseResponseWithTools(response, model) {
6056
6116
  const usage = response.usageMetadata;
6057
- const toolCalls = this.extractFunctionCalls(response).map((functionCall, index) => ({
6058
- id: functionCall.id ?? `gemini_call_${index + 1}`,
6059
- name: functionCall.name ?? "unknown_function",
6060
- input: functionCall.args ?? {}
6061
- }));
6117
+ const toolCalls = this.extractToolCalls(response, { includeLegacyFunctionCalls: true });
6062
6118
  return {
6063
6119
  id: `gemini-${Date.now()}`,
6064
6120
  content: response.text ?? "",
@@ -6106,6 +6162,13 @@ var init_gemini = __esm({
6106
6162
  });
6107
6163
 
6108
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
+ }
6109
6172
  function createVertexProvider(config) {
6110
6173
  const provider = new VertexProvider();
6111
6174
  if (config) {
@@ -6114,7 +6177,7 @@ function createVertexProvider(config) {
6114
6177
  }
6115
6178
  return provider;
6116
6179
  }
6117
- 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;
6118
6181
  var init_vertex = __esm({
6119
6182
  "src/providers/vertex.ts"() {
6120
6183
  init_errors();
@@ -6124,12 +6187,15 @@ var init_vertex = __esm({
6124
6187
  DEFAULT_BASE_URL = "https://aiplatform.googleapis.com/v1";
6125
6188
  DEFAULT_LOCATION = "global";
6126
6189
  CONTEXT_WINDOWS6 = {
6190
+ "gemini-3-pro-preview": 1048576,
6191
+ "gemini-3-flash-preview": 1048576,
6127
6192
  "gemini-2.5-pro": 1048576,
6128
6193
  "gemini-2.5-flash": 1048576,
6129
6194
  "gemini-2.5-flash-lite": 1048576,
6130
6195
  "gemini-2.0-flash-001": 1048576,
6131
6196
  "gemini-2.0-flash-lite-001": 1048576
6132
6197
  };
6198
+ SKIP_THOUGHT_SIGNATURE_VALIDATOR2 = "skip_thought_signature_validator";
6133
6199
  VertexProvider = class {
6134
6200
  id = "vertex";
6135
6201
  name = "Google Vertex AI Gemini";
@@ -6214,12 +6280,14 @@ var init_vertex = __esm({
6214
6280
  }
6215
6281
  if (part.functionCall) {
6216
6282
  streamToolCallCounter++;
6283
+ const geminiThoughtSignature = part.thoughtSignature ?? part.thought_signature ?? part.functionCall.thoughtSignature ?? part.functionCall.thought_signature;
6217
6284
  yield {
6218
6285
  type: "tool_use_start",
6219
6286
  toolCall: {
6220
6287
  id: `vertex_call_${streamToolCallCounter}`,
6221
6288
  name: part.functionCall.name,
6222
- input: part.functionCall.args ?? {}
6289
+ input: part.functionCall.args ?? {},
6290
+ geminiThoughtSignature
6223
6291
  }
6224
6292
  };
6225
6293
  yield {
@@ -6227,7 +6295,8 @@ var init_vertex = __esm({
6227
6295
  toolCall: {
6228
6296
  id: `vertex_call_${streamToolCallCounter}`,
6229
6297
  name: part.functionCall.name,
6230
- input: part.functionCall.args ?? {}
6298
+ input: part.functionCall.args ?? {},
6299
+ geminiThoughtSignature
6231
6300
  }
6232
6301
  };
6233
6302
  }
@@ -6360,11 +6429,14 @@ var init_vertex = __esm({
6360
6429
  });
6361
6430
  } else if (block.type === "tool_use") {
6362
6431
  const toolUse = block;
6432
+ const thoughtSignature = toolUse.geminiThoughtSignature ?? SKIP_THOUGHT_SIGNATURE_VALIDATOR2;
6363
6433
  parts.push({
6364
6434
  functionCall: {
6365
6435
  name: toolUse.name,
6366
6436
  args: toolUse.input
6367
- }
6437
+ },
6438
+ thoughtSignature,
6439
+ thought_signature: thoughtSignature
6368
6440
  });
6369
6441
  }
6370
6442
  }
@@ -6456,22 +6528,27 @@ var init_vertex = __esm({
6456
6528
  if (done) break;
6457
6529
  buffer += decoder.decode(value, { stream: true });
6458
6530
  while (true) {
6459
- const eventBoundary = buffer.indexOf("\n\n");
6460
- if (eventBoundary === -1) break;
6461
- const rawEvent = buffer.slice(0, eventBoundary);
6462
- buffer = buffer.slice(eventBoundary + 2);
6463
- const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter(Boolean);
6464
- for (const line of dataLines) {
6465
- if (line === "[DONE]") return;
6466
- 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;
6467
6544
  }
6468
6545
  }
6469
6546
  }
6470
- const trailing = buffer.trim();
6471
- if (trailing.startsWith("data:")) {
6472
- const line = trailing.slice(5).trim();
6473
- if (line && line !== "[DONE]") {
6474
- yield JSON.parse(line);
6547
+ const trailingData = extractSseEventData(buffer.trim());
6548
+ if (trailingData && trailingData !== "[DONE]") {
6549
+ try {
6550
+ yield JSON.parse(trailingData);
6551
+ } catch {
6475
6552
  }
6476
6553
  }
6477
6554
  }
@@ -6504,7 +6581,8 @@ var init_vertex = __esm({
6504
6581
  toolCalls.push({
6505
6582
  id: `vertex_call_${toolIndex}`,
6506
6583
  name: part.functionCall.name,
6507
- input: part.functionCall.args ?? {}
6584
+ input: part.functionCall.args ?? {},
6585
+ geminiThoughtSignature: part.thoughtSignature ?? part.thought_signature ?? part.functionCall.thoughtSignature ?? part.functionCall.thought_signature
6508
6586
  });
6509
6587
  }
6510
6588
  }
@@ -6609,6 +6687,7 @@ var init_pricing = __esm({
6609
6687
  "gpt-4o": { inputPerMillion: 2.5, outputPerMillion: 10, contextWindow: 128e3 },
6610
6688
  "gpt-4o-mini": { inputPerMillion: 0.15, outputPerMillion: 0.6, contextWindow: 128e3 },
6611
6689
  // Google Gemini models
6690
+ "gemini-3-pro-preview": { inputPerMillion: 1.25, outputPerMillion: 5, contextWindow: 1e6 },
6612
6691
  "gemini-3.1-pro-preview": { inputPerMillion: 1.25, outputPerMillion: 5, contextWindow: 1e6 },
6613
6692
  "gemini-3-flash-preview": {
6614
6693
  inputPerMillion: 0.15,
@@ -10446,17 +10525,55 @@ async function saveTrustSettings(settings) {
10446
10525
  console.warn(`[Trust] Failed to save trust settings: ${msg}`);
10447
10526
  }
10448
10527
  }
10449
- 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) {
10450
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
+ ]);
10451
10568
  const trusted = /* @__PURE__ */ new Set();
10452
10569
  for (const tool of settings.globalTrusted) {
10453
10570
  trusted.add(tool);
10454
10571
  }
10455
- const projectTrusted = settings.projectTrusted[projectPath] ?? [];
10572
+ const projectTrusted = projectSettings.trusted;
10456
10573
  for (const tool of projectTrusted) {
10457
10574
  trusted.add(tool);
10458
10575
  }
10459
- const projectDenied = settings.projectDenied[projectPath] ?? [];
10576
+ const projectDenied = projectSettings.denied;
10460
10577
  for (const tool of projectDenied) {
10461
10578
  trusted.delete(tool);
10462
10579
  }
@@ -10469,61 +10586,54 @@ async function saveTrustedTool(toolName, projectPath, global = false) {
10469
10586
  settings.globalTrusted.push(toolName);
10470
10587
  }
10471
10588
  } else if (projectPath) {
10472
- if (!settings.projectTrusted[projectPath]) {
10473
- settings.projectTrusted[projectPath] = [];
10474
- }
10475
- const projectTrusted = settings.projectTrusted[projectPath];
10476
- if (projectTrusted && !projectTrusted.includes(toolName)) {
10477
- projectTrusted.push(toolName);
10589
+ const projectSettings = await loadProjectTrustSettings(projectPath);
10590
+ if (!projectSettings.trusted.includes(toolName)) {
10591
+ projectSettings.trusted.push(toolName);
10478
10592
  }
10593
+ projectSettings.denied = projectSettings.denied.filter((t) => t !== toolName);
10594
+ await saveProjectTrustSettings(projectPath, projectSettings);
10595
+ }
10596
+ if (global) {
10597
+ await saveTrustSettings(settings);
10479
10598
  }
10480
- await saveTrustSettings(settings);
10481
10599
  }
10482
10600
  async function removeTrustedTool(toolName, projectPath, global = false) {
10483
10601
  const settings = await loadTrustSettings();
10484
10602
  if (global) {
10485
10603
  settings.globalTrusted = settings.globalTrusted.filter((t) => t !== toolName);
10604
+ await saveTrustSettings(settings);
10486
10605
  } else {
10487
- const projectTrusted = settings.projectTrusted[projectPath];
10488
- if (projectTrusted) {
10489
- settings.projectTrusted[projectPath] = projectTrusted.filter((t) => t !== toolName);
10490
- }
10606
+ const projectSettings = await loadProjectTrustSettings(projectPath);
10607
+ projectSettings.trusted = projectSettings.trusted.filter((t) => t !== toolName);
10608
+ await saveProjectTrustSettings(projectPath, projectSettings);
10491
10609
  }
10492
- await saveTrustSettings(settings);
10493
10610
  }
10494
10611
  async function saveDeniedTool(toolName, projectPath) {
10495
- const settings = await loadTrustSettings();
10496
- if (!settings.projectDenied[projectPath]) {
10497
- settings.projectDenied[projectPath] = [];
10612
+ const projectSettings = await loadProjectTrustSettings(projectPath);
10613
+ if (!projectSettings.denied.includes(toolName)) {
10614
+ projectSettings.denied.push(toolName);
10498
10615
  }
10499
- const denied = settings.projectDenied[projectPath];
10500
- if (denied && !denied.includes(toolName)) {
10501
- denied.push(toolName);
10502
- }
10503
- const projectTrusted = settings.projectTrusted[projectPath];
10504
- if (projectTrusted) {
10505
- settings.projectTrusted[projectPath] = projectTrusted.filter((t) => t !== toolName);
10506
- }
10507
- await saveTrustSettings(settings);
10616
+ projectSettings.trusted = projectSettings.trusted.filter((t) => t !== toolName);
10617
+ await saveProjectTrustSettings(projectPath, projectSettings);
10508
10618
  }
10509
10619
  async function removeDeniedTool(toolName, projectPath) {
10510
- const settings = await loadTrustSettings();
10511
- const denied = settings.projectDenied[projectPath];
10512
- if (denied) {
10513
- settings.projectDenied[projectPath] = denied.filter((t) => t !== toolName);
10514
- }
10515
- await saveTrustSettings(settings);
10620
+ const projectSettings = await loadProjectTrustSettings(projectPath);
10621
+ projectSettings.denied = projectSettings.denied.filter((t) => t !== toolName);
10622
+ await saveProjectTrustSettings(projectPath, projectSettings);
10516
10623
  }
10517
10624
  async function getDeniedTools(projectPath) {
10518
- const settings = await loadTrustSettings();
10519
- return settings.projectDenied[projectPath] ?? [];
10625
+ const settings = await loadProjectTrustSettings(projectPath);
10626
+ return settings.denied;
10520
10627
  }
10521
10628
  async function getAllTrustedTools(projectPath) {
10522
- const settings = await loadTrustSettings();
10629
+ const [settings, projectSettings] = await Promise.all([
10630
+ loadTrustSettings(),
10631
+ loadProjectTrustSettings(projectPath)
10632
+ ]);
10523
10633
  return {
10524
10634
  global: settings.globalTrusted,
10525
- project: settings.projectTrusted[projectPath] ?? [],
10526
- denied: settings.projectDenied[projectPath] ?? []
10635
+ project: projectSettings.trusted,
10636
+ denied: projectSettings.denied
10527
10637
  };
10528
10638
  }
10529
10639
  async function initializeSessionTrust(session) {
@@ -10614,7 +10724,7 @@ function getSessionMemory(session) {
10614
10724
  async function reloadSessionMemory(session) {
10615
10725
  await initializeSessionMemory(session);
10616
10726
  }
10617
- 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;
10618
10728
  var init_session = __esm({
10619
10729
  "src/cli/repl/session.ts"() {
10620
10730
  init_env();
@@ -10629,6 +10739,7 @@ var init_session = __esm({
10629
10739
  MAX_SKILL_INSTRUCTIONS_CHARS = 16e3;
10630
10740
  TRUST_SETTINGS_DIR = path39__default.dirname(CONFIG_PATHS.trustedTools);
10631
10741
  TRUST_SETTINGS_FILE = CONFIG_PATHS.trustedTools;
10742
+ PROJECT_TRUST_FILE_RELATIVE_PATH = path39__default.join(".coco", "trusted-tools.json");
10632
10743
  CATEGORY_LABELS = {
10633
10744
  mcp: "MCP Connected Services",
10634
10745
  file: "File Operations",
@@ -21620,7 +21731,7 @@ async function openBrowser2(url) {
21620
21731
  }
21621
21732
  for (const { cmd, args } of commands2) {
21622
21733
  try {
21623
- await execFileAsync2(cmd, args);
21734
+ await execFileAsync3(cmd, args);
21624
21735
  return true;
21625
21736
  } catch {
21626
21737
  continue;
@@ -21896,13 +22007,13 @@ async function authenticateMcpOAuth(params) {
21896
22007
  await persistToken(resource, token, { authorizationServer, clientId });
21897
22008
  return token.access_token;
21898
22009
  }
21899
- var execFileAsync2, TOKEN_STORE_PATH, OAUTH_TIMEOUT_MS, logger;
22010
+ var execFileAsync3, TOKEN_STORE_PATH, OAUTH_TIMEOUT_MS, logger;
21900
22011
  var init_oauth2 = __esm({
21901
22012
  "src/mcp/oauth.ts"() {
21902
22013
  init_callback_server();
21903
22014
  init_paths();
21904
22015
  init_logger();
21905
- execFileAsync2 = promisify(execFile);
22016
+ execFileAsync3 = promisify(execFile);
21906
22017
  TOKEN_STORE_PATH = path39__default.join(CONFIG_PATHS.tokens, "mcp-oauth.json");
21907
22018
  OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
21908
22019
  logger = getLogger();
@@ -29123,20 +29234,13 @@ var PROVIDER_DEFINITIONS = {
29123
29234
  },
29124
29235
  // Updated: March 2026 — from platform.openai.com/docs/models
29125
29236
  models: [
29126
- {
29127
- id: "gpt-5.4-codex",
29128
- name: "GPT-5.4 Codex",
29129
- description: "Latest agentic coding model (Mar 2026)",
29130
- contextWindow: 4e5,
29131
- maxOutputTokens: 128e3,
29132
- recommended: true
29133
- },
29134
29237
  {
29135
29238
  id: "gpt-5.3-codex",
29136
29239
  name: "GPT-5.3 Codex",
29137
- description: "Previous agentic coding model (Feb 2026)",
29240
+ description: "Latest available agentic coding model",
29138
29241
  contextWindow: 4e5,
29139
- maxOutputTokens: 128e3
29242
+ maxOutputTokens: 128e3,
29243
+ recommended: true
29140
29244
  },
29141
29245
  {
29142
29246
  id: "gpt-5.2-codex",
@@ -29437,20 +29541,13 @@ var PROVIDER_DEFINITIONS = {
29437
29541
  vision: true
29438
29542
  },
29439
29543
  models: [
29440
- {
29441
- id: "gpt-5.4-codex",
29442
- name: "GPT-5.4 Codex",
29443
- description: "Latest coding model via ChatGPT subscription (Mar 2026)",
29444
- contextWindow: 2e5,
29445
- maxOutputTokens: 128e3,
29446
- recommended: true
29447
- },
29448
29544
  {
29449
29545
  id: "gpt-5.3-codex",
29450
29546
  name: "GPT-5.3 Codex",
29451
- description: "Previous coding model via ChatGPT subscription",
29547
+ description: "Latest available coding model via ChatGPT subscription",
29452
29548
  contextWindow: 2e5,
29453
- maxOutputTokens: 128e3
29549
+ maxOutputTokens: 128e3,
29550
+ recommended: true
29454
29551
  },
29455
29552
  {
29456
29553
  id: "gpt-5.2-codex",
@@ -29560,13 +29657,27 @@ var PROVIDER_DEFINITIONS = {
29560
29657
  },
29561
29658
  models: [
29562
29659
  {
29563
- id: "gemini-2.5-pro",
29564
- name: "Gemini 2.5 Pro",
29565
- 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)",
29566
29663
  contextWindow: 1048576,
29567
29664
  maxOutputTokens: 65536,
29568
29665
  recommended: true
29569
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
+ },
29570
29681
  {
29571
29682
  id: "gemini-2.5-flash",
29572
29683
  name: "Gemini 2.5 Flash",
@@ -33847,10 +33958,13 @@ async function selectModelInteractively(models, currentModelId) {
33847
33958
  totalLines++;
33848
33959
  }
33849
33960
  const star = model.recommended ? " \u2B50" : "";
33850
- 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` : "";
33851
33965
  const desc = model.description ? ` ${model.description}` : "";
33852
33966
  const hint = model.hint ? ` \u2192 ${model.hint}` : "";
33853
- const suffix = truncate2(`${star}${ctx}${desc}${hint}`, descWidth, "\u2026");
33967
+ const suffix = truncate2(`${star}${metric}${desc}${hint}`, descWidth, "\u2026");
33854
33968
  if (model.disabled) {
33855
33969
  let line2 = chalk.dim(" \u25CB ");
33856
33970
  line2 += chalk.dim(model.id.padEnd(30));
@@ -34173,7 +34287,10 @@ async function setupProviderWithAuth(provider) {
34173
34287
  authMethod = choice;
34174
34288
  }
34175
34289
  if (authMethod === "oauth") {
34290
+ const oauthSpinner = p26.spinner();
34291
+ oauthSpinner.start("Starting OAuth sign-in flow...");
34176
34292
  const result = await runOAuthFlow(provider.id);
34293
+ oauthSpinner.stop(result ? "OAuth sign-in completed" : "OAuth sign-in cancelled");
34177
34294
  if (!result) return null;
34178
34295
  if (provider.id === "copilot") {
34179
34296
  const model3 = await selectModel(provider);
@@ -34257,7 +34374,10 @@ async function setupGcloudADC(provider) {
34257
34374
  );
34258
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"));
34259
34376
  console.log();
34377
+ const gcloudCheckSpinner = p26.spinner();
34378
+ gcloudCheckSpinner.start("Checking gcloud CLI...");
34260
34379
  const gcloudInstalled = await isGcloudInstalled();
34380
+ gcloudCheckSpinner.stop(gcloudInstalled ? "gcloud CLI detected" : "gcloud CLI not detected");
34261
34381
  if (!gcloudInstalled) {
34262
34382
  p26.log.error("gcloud CLI is not installed");
34263
34383
  console.log(chalk.dim(" Install it from: https://cloud.google.com/sdk/docs/install"));
@@ -34288,7 +34408,12 @@ async function setupGcloudADC(provider) {
34288
34408
  location: vertexSettings2?.location
34289
34409
  };
34290
34410
  }
34411
+ const adcInspectSpinner = p26.spinner();
34412
+ adcInspectSpinner.start("Checking existing ADC credentials...");
34291
34413
  let adc = await inspectADC();
34414
+ adcInspectSpinner.stop(
34415
+ adc.status === "ok" && adc.token ? "ADC credentials found" : "No reusable ADC credentials found"
34416
+ );
34292
34417
  if (adc.status === "ok" && adc.token) {
34293
34418
  console.log(chalk.green(" \u2713 gcloud ADC is already configured!"));
34294
34419
  console.log();
@@ -34318,9 +34443,17 @@ async function setupGcloudADC(provider) {
34318
34443
  if (p26.isCancel(runLoginNow)) return null;
34319
34444
  if (runLoginNow) {
34320
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)...");
34321
34448
  const loginOk = await runGcloudADCLogin();
34449
+ loginSpinner.stop(loginOk ? "gcloud login flow completed" : "gcloud login flow failed");
34322
34450
  if (loginOk) {
34451
+ const recheckSpinner = p26.spinner();
34452
+ recheckSpinner.start("Verifying ADC credentials after login...");
34323
34453
  adc = await inspectADC();
34454
+ recheckSpinner.stop(
34455
+ adc.status === "ok" && adc.token ? "ADC credentials verified" : "ADC verification failed after login"
34456
+ );
34324
34457
  if (adc.status === "ok" && adc.token) {
34325
34458
  console.log(chalk.green(" \u2713 gcloud ADC is now configured."));
34326
34459
  console.log();
@@ -35448,9 +35581,15 @@ async function switchProvider(initialProvider, session) {
35448
35581
  const oauthProviderName = newProvider.id === "copilot" ? "copilot" : newProvider.id === "gemini" ? "gemini" : "openai";
35449
35582
  let oauthConnected = false;
35450
35583
  if (hasOAuth) {
35584
+ const oauthCheckSpinner = p26.spinner();
35585
+ oauthCheckSpinner.start("Checking existing OAuth session...");
35451
35586
  try {
35452
35587
  oauthConnected = await isOAuthConfigured(oauthProviderName);
35588
+ oauthCheckSpinner.stop(
35589
+ oauthConnected ? "Existing OAuth session found" : "No existing OAuth session"
35590
+ );
35453
35591
  } catch {
35592
+ oauthCheckSpinner.stop("Could not verify OAuth session");
35454
35593
  }
35455
35594
  }
35456
35595
  {
@@ -35517,159 +35656,103 @@ async function switchProvider(initialProvider, session) {
35517
35656
  label: "\u274C Cancel",
35518
35657
  hint: ""
35519
35658
  });
35659
+ let authChoice;
35520
35660
  if (authOptions.length > 2) {
35521
- const authChoice = await p26.select({
35661
+ const selected = await p26.select({
35522
35662
  message: `How would you like to authenticate with ${newProvider.name}?`,
35523
35663
  options: authOptions
35524
35664
  });
35525
- if (p26.isCancel(authChoice) || authChoice === "cancel") {
35665
+ if (p26.isCancel(selected) || selected === "cancel") {
35526
35666
  return false;
35527
35667
  }
35528
- if (authChoice === "oauth") {
35529
- const isCopilot = newProvider.id === "copilot";
35530
- const isGemini = newProvider.id === "gemini";
35531
- if (isCopilot) {
35532
- if (oauthConnected) {
35533
- 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(`
35534
35679
  Using existing GitHub session...`));
35535
- } else {
35536
- const result = await runOAuthFlow("copilot");
35537
- if (!result) return false;
35538
- }
35539
- selectedAuthMethod = "oauth";
35540
35680
  } else {
35541
- const tokenEnvVar = isGemini ? "GEMINI_OAUTH_TOKEN" : "OPENAI_CODEX_TOKEN";
35542
- if (oauthConnected) {
35543
- try {
35544
- const tokenResult = await getOrRefreshOAuthToken(oauthProviderName);
35545
- if (tokenResult) {
35546
- process.env[tokenEnvVar] = tokenResult.accessToken;
35547
- selectedAuthMethod = "oauth";
35548
- if (!isGemini) internalProviderId = "codex";
35549
- 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(`
35550
35703
  Using existing OAuth session...`));
35551
- } else {
35552
- const result = await runOAuthFlow(newProvider.id);
35553
- if (!result) return false;
35554
- process.env[tokenEnvVar] = result.accessToken;
35555
- selectedAuthMethod = "oauth";
35556
- if (!isGemini) internalProviderId = "codex";
35557
- }
35558
- } catch {
35704
+ } else {
35705
+ refreshSpinner.stop("OAuth refresh failed, re-authentication required");
35706
+ const oauthStartSpinner = p26.spinner();
35707
+ oauthStartSpinner.start("Starting OAuth sign-in...");
35559
35708
  const result = await runOAuthFlow(newProvider.id);
35709
+ oauthStartSpinner.stop(
35710
+ result ? "OAuth sign-in completed" : "OAuth sign-in cancelled"
35711
+ );
35560
35712
  if (!result) return false;
35561
35713
  process.env[tokenEnvVar] = result.accessToken;
35562
35714
  selectedAuthMethod = "oauth";
35563
35715
  if (!isGemini) internalProviderId = "codex";
35564
35716
  }
35565
- } else {
35717
+ } catch {
35718
+ refreshSpinner.stop("OAuth refresh failed, re-authentication required");
35719
+ const oauthStartSpinner = p26.spinner();
35720
+ oauthStartSpinner.start("Starting OAuth sign-in...");
35566
35721
  const result = await runOAuthFlow(newProvider.id);
35722
+ oauthStartSpinner.stop(result ? "OAuth sign-in completed" : "OAuth sign-in cancelled");
35567
35723
  if (!result) return false;
35568
35724
  process.env[tokenEnvVar] = result.accessToken;
35569
35725
  selectedAuthMethod = "oauth";
35570
35726
  if (!isGemini) internalProviderId = "codex";
35571
35727
  }
35572
- }
35573
- } else if (authChoice === "gcloud") {
35574
- const adcResult = await setupGcloudADCForProvider();
35575
- if (!adcResult) return false;
35576
- selectedAuthMethod = "gcloud";
35577
- if (newProvider.id === "vertex") {
35578
- const settings = await promptVertexSettings2({
35579
- project: session.config.provider.project,
35580
- location: session.config.provider.location
35581
- });
35582
- if (!settings) return false;
35583
- vertexSettings = settings;
35584
- }
35585
- } else if (authChoice === "apikey") {
35586
- if (apiKey) {
35587
- selectedAuthMethod = "apikey";
35588
- console.log(chalk.dim(`
35589
- Using existing API key...`));
35590
35728
  } else {
35591
- const key = await p26.password({
35592
- message: `Enter your ${newProvider.name} API key:`,
35593
- validate: (v) => !v || v.length < 10 ? "API key too short" : void 0
35594
- });
35595
- if (p26.isCancel(key)) {
35596
- return false;
35597
- }
35598
- process.env[newProvider.envVar] = key;
35599
- selectedAuthMethod = "apikey";
35600
- newApiKeyForSaving = key;
35601
- }
35602
- if (newProvider.id === "vertex") {
35603
- const settings = await promptVertexSettings2({
35604
- project: session.config.provider.project,
35605
- location: session.config.provider.location
35606
- });
35607
- if (!settings) return false;
35608
- vertexSettings = settings;
35609
- }
35610
- } else if (authChoice === "remove") {
35611
- const removeOptions = [];
35612
- if (oauthConnected) {
35613
- removeOptions.push({
35614
- value: "oauth",
35615
- label: "\u{1F510} Remove OAuth session"
35616
- });
35617
- }
35618
- if (apiKey) {
35619
- removeOptions.push({
35620
- value: "apikey",
35621
- label: "\u{1F511} Remove API key"
35622
- });
35623
- }
35624
- if (oauthConnected && apiKey) {
35625
- removeOptions.push({
35626
- value: "all",
35627
- label: "\u{1F5D1}\uFE0F Remove all credentials"
35628
- });
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";
35629
35737
  }
35630
- removeOptions.push({
35631
- value: "cancel",
35632
- label: "\u274C Cancel"
35633
- });
35634
- const removeChoice = await p26.select({
35635
- message: "What would you like to remove?",
35636
- 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
35637
35747
  });
35638
- if (p26.isCancel(removeChoice) || removeChoice === "cancel") {
35639
- return false;
35640
- }
35641
- if (removeChoice === "oauth" || removeChoice === "all") {
35642
- if (oauthProviderName === "copilot") {
35643
- await deleteCopilotCredentials();
35644
- } else {
35645
- await deleteTokens(oauthProviderName);
35646
- }
35647
- await clearAuthMethod(newProvider.id);
35648
- console.log(chalk.green("\u2713 OAuth session removed"));
35649
- }
35650
- if (removeChoice === "apikey" || removeChoice === "all") {
35651
- delete process.env[newProvider.envVar];
35652
- console.log(chalk.green("\u2713 API key removed from session"));
35653
- console.log(chalk.dim(` Note: If key is in ~/.coco/.env, remove it there too`));
35654
- }
35655
- console.log("");
35656
- return false;
35748
+ if (!settings) return false;
35749
+ vertexSettings = settings;
35657
35750
  }
35658
- } else {
35659
- console.log(chalk.yellow(`
35660
- ${newProvider.emoji} ${newProvider.name} is not configured.`));
35661
- if (hasGcloudADC && !supportsApiKey) {
35662
- const adcResult = await setupGcloudADCForProvider();
35663
- if (!adcResult) return false;
35664
- selectedAuthMethod = "gcloud";
35665
- if (newProvider.id === "vertex") {
35666
- const settings = await promptVertexSettings2({
35667
- project: session.config.provider.project,
35668
- location: session.config.provider.location
35669
- });
35670
- if (!settings) return false;
35671
- vertexSettings = settings;
35672
- }
35751
+ } else if (authChoice === "apikey") {
35752
+ if (apiKey) {
35753
+ selectedAuthMethod = "apikey";
35754
+ console.log(chalk.dim(`
35755
+ Using existing API key...`));
35673
35756
  } else {
35674
35757
  const key = await p26.password({
35675
35758
  message: `Enter your ${newProvider.name} API key:`,
@@ -35682,18 +35765,75 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
35682
35765
  selectedAuthMethod = "apikey";
35683
35766
  newApiKeyForSaving = key;
35684
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;
35685
35823
  }
35686
35824
  }
35687
35825
  const rememberedModel = await getLastUsedModel(newProvider.id);
35688
35826
  const recommendedModel = getRecommendedModel(newProvider.id);
35689
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;
35690
35830
  const spinner18 = p26.spinner();
35691
35831
  spinner18.start(`Connecting to ${newProvider.name}...`);
35692
35832
  try {
35693
35833
  const testProvider = await createProvider(internalProviderId, {
35694
35834
  model: newModel,
35695
- project: vertexSettings?.project,
35696
- location: vertexSettings?.location
35835
+ project: resolvedVertexProject,
35836
+ location: resolvedVertexLocation
35697
35837
  });
35698
35838
  const available = await testProvider.isAvailable();
35699
35839
  if (!available) {
@@ -35707,8 +35847,14 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
35707
35847
  session.config.provider.type = userFacingProviderId;
35708
35848
  session.config.provider.model = newModel;
35709
35849
  if (userFacingProviderId === "vertex") {
35710
- session.config.provider.project = vertexSettings?.project;
35711
- 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
+ }
35712
35858
  } else {
35713
35859
  delete session.config.provider.project;
35714
35860
  delete session.config.provider.location;
@@ -35718,13 +35864,13 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
35718
35864
  type: userFacingProviderId,
35719
35865
  model: newModel,
35720
35866
  apiKey: newApiKeyForSaving,
35721
- project: vertexSettings?.project,
35722
- location: vertexSettings?.location
35867
+ project: resolvedVertexProject,
35868
+ location: resolvedVertexLocation
35723
35869
  });
35724
35870
  } else {
35725
35871
  await saveProviderPreference(userFacingProviderId, newModel, {
35726
- project: vertexSettings?.project,
35727
- location: vertexSettings?.location
35872
+ project: resolvedVertexProject,
35873
+ location: resolvedVertexLocation
35728
35874
  });
35729
35875
  }
35730
35876
  console.log(chalk.green(`
@@ -35747,13 +35893,21 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
35747
35893
  return false;
35748
35894
  }
35749
35895
  async function setupGcloudADCForProvider(_provider) {
35896
+ const gcloudCheckSpinner = p26.spinner();
35897
+ gcloudCheckSpinner.start("Checking gcloud CLI...");
35750
35898
  const gcloudInstalled = await isGcloudInstalled();
35899
+ gcloudCheckSpinner.stop(gcloudInstalled ? "gcloud CLI detected" : "gcloud CLI not detected");
35751
35900
  if (!gcloudInstalled) {
35752
35901
  p26.log.error("gcloud CLI is not installed");
35753
35902
  console.log(chalk.dim(" Install it from: https://cloud.google.com/sdk/docs/install\n"));
35754
35903
  return false;
35755
35904
  }
35905
+ const adcInspectSpinner = p26.spinner();
35906
+ adcInspectSpinner.start("Checking existing ADC credentials...");
35756
35907
  const adc = await inspectADC();
35908
+ adcInspectSpinner.stop(
35909
+ adc.status === "ok" && adc.token ? "ADC credentials found" : "No reusable ADC credentials found"
35910
+ );
35757
35911
  if (adc.status === "ok" && adc.token) {
35758
35912
  console.log(chalk.green(" \u2713 gcloud ADC is already configured!\n"));
35759
35913
  return true;
@@ -35769,9 +35923,17 @@ async function setupGcloudADCForProvider(_provider) {
35769
35923
  if (p26.isCancel(runLoginNow)) return false;
35770
35924
  if (runLoginNow) {
35771
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)...");
35772
35928
  const loginOk = await runGcloudADCLogin();
35929
+ loginSpinner.stop(loginOk ? "gcloud login flow completed" : "gcloud login flow failed");
35773
35930
  if (loginOk) {
35931
+ const recheckSpinner = p26.spinner();
35932
+ recheckSpinner.start("Verifying ADC credentials after login...");
35774
35933
  const refreshed = await inspectADC();
35934
+ recheckSpinner.stop(
35935
+ refreshed.status === "ok" && refreshed.token ? "ADC credentials verified" : "ADC verification failed after login"
35936
+ );
35775
35937
  if (refreshed.status === "ok" && refreshed.token) {
35776
35938
  console.log(chalk.green(" \u2713 gcloud ADC is now configured.\n"));
35777
35939
  return true;
@@ -39044,10 +39206,8 @@ async function revokePath(dirPath, _session) {
39044
39206
 
39045
39207
  // src/cli/repl/commands/permissions.ts
39046
39208
  init_session();
39047
- init_paths();
39048
39209
 
39049
39210
  // src/cli/repl/recommended-permissions.ts
39050
- init_paths();
39051
39211
  init_session();
39052
39212
  var RECOMMENDED_GLOBAL = [
39053
39213
  // ── Coco native tools (read-only) ──
@@ -39383,100 +39543,94 @@ var RECOMMENDED_DENY = [
39383
39543
  "bash:eval",
39384
39544
  "bash:source"
39385
39545
  ];
39386
- function getProjectPreferenceKey(projectPath) {
39387
- return path39__default.resolve(projectPath);
39546
+ function getProjectPermissionStatePath(projectPath) {
39547
+ return path39__default.join(projectPath, ".coco", "recommended-permissions.json");
39388
39548
  }
39389
- async function loadPermissionPreferences() {
39549
+ async function resolvePermissionScopePath(projectPath) {
39550
+ let resolved = path39__default.resolve(projectPath);
39390
39551
  try {
39391
- const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
39392
- const config = JSON.parse(content);
39393
- return {
39394
- recommendedAllowlistApplied: config.recommendedAllowlistApplied,
39395
- recommendedAllowlistDismissed: config.recommendedAllowlistDismissed,
39396
- recommendedAllowlistPrompted: config.recommendedAllowlistPrompted,
39397
- recommendedAllowlistPromptedProjects: config.recommendedAllowlistPromptedProjects,
39398
- recommendedAllowlistAppliedProjects: config.recommendedAllowlistAppliedProjects,
39399
- recommendedAllowlistDismissedProjects: config.recommendedAllowlistDismissedProjects
39400
- };
39552
+ resolved = await fs35__default.realpath(resolved);
39401
39553
  } catch {
39402
- return {};
39403
39554
  }
39404
- }
39405
- async function savePermissionPreference(key, value) {
39406
- try {
39407
- let config = {};
39555
+ let current = resolved;
39556
+ while (true) {
39408
39557
  try {
39409
- const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
39410
- config = JSON.parse(content);
39558
+ await fs35__default.access(path39__default.join(current, ".git"));
39559
+ return current;
39411
39560
  } catch {
39412
39561
  }
39413
- config[key] = value;
39414
- await fs35__default.mkdir(path39__default.dirname(CONFIG_PATHS.config), { recursive: true });
39415
- await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
39416
- } catch {
39562
+ const parent = path39__default.dirname(current);
39563
+ if (parent === current) break;
39564
+ current = parent;
39417
39565
  }
39566
+ return resolved;
39418
39567
  }
39419
- function isRecommendedAllowlistAppliedForProject(prefs, projectPath) {
39420
- const projectKey = getProjectPreferenceKey(projectPath);
39421
- if (prefs.recommendedAllowlistAppliedProjects?.[projectKey] === true) {
39422
- return true;
39423
- }
39424
- if (prefs.recommendedAllowlistApplied === true && !prefs.recommendedAllowlistAppliedProjects) {
39425
- return true;
39426
- }
39427
- return false;
39428
- }
39429
- function isRecommendedAllowlistDismissedForProject(prefs, projectPath) {
39430
- const projectKey = getProjectPreferenceKey(projectPath);
39431
- if (prefs.recommendedAllowlistDismissedProjects?.[projectKey] === true) {
39432
- return true;
39433
- }
39434
- if (prefs.recommendedAllowlistDismissed === true && !prefs.recommendedAllowlistDismissedProjects) {
39435
- 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;
39436
39586
  }
39437
- return false;
39438
39587
  }
39439
- async function saveProjectPermissionPreference(key, projectPath, value) {
39588
+ async function saveProjectPermissionState(projectPath, update) {
39440
39589
  try {
39441
- let config = {};
39442
- try {
39443
- const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
39444
- config = JSON.parse(content);
39445
- } catch {
39446
- }
39447
- const projectKey = getProjectPreferenceKey(projectPath);
39448
- const currentMap = config[key] ?? {};
39449
- config[key] = {
39450
- ...currentMap,
39451
- [projectKey]: value
39590
+ const current = await loadProjectPermissionState(projectPath);
39591
+ const next = {
39592
+ ...current,
39593
+ ...update,
39594
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
39452
39595
  };
39453
- await fs35__default.mkdir(path39__default.dirname(CONFIG_PATHS.config), { recursive: true });
39454
- 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");
39455
39599
  } catch {
39456
39600
  }
39457
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
+ }
39458
39614
  async function shouldShowPermissionSuggestion(projectPath = process.cwd()) {
39459
- const prefs = await loadPermissionPreferences();
39460
- if (isRecommendedAllowlistDismissedForProject(prefs, projectPath)) {
39615
+ const state = await getProjectPermissionState(projectPath);
39616
+ if (state.dismissed) {
39461
39617
  return false;
39462
39618
  }
39463
- if (isRecommendedAllowlistAppliedForProject(prefs, projectPath)) {
39619
+ if (state.applied) {
39464
39620
  return false;
39465
39621
  }
39466
39622
  return true;
39467
39623
  }
39468
39624
  async function applyRecommendedPermissions(projectPath = process.cwd()) {
39625
+ const scopePath = await resolvePermissionScopePath(projectPath);
39469
39626
  for (const tool of [...RECOMMENDED_GLOBAL, ...RECOMMENDED_PROJECT]) {
39470
39627
  await saveTrustedTool(tool, projectPath, false);
39471
39628
  }
39472
- await saveProjectPermissionPreference("recommendedAllowlistAppliedProjects", projectPath, true);
39473
- await saveProjectPermissionPreference(
39474
- "recommendedAllowlistDismissedProjects",
39475
- projectPath,
39476
- false
39477
- );
39629
+ await saveProjectPermissionPreference("recommendedAllowlistAppliedProjects", scopePath, true);
39630
+ await saveProjectPermissionPreference("recommendedAllowlistDismissedProjects", scopePath, false);
39478
39631
  }
39479
39632
  async function showPermissionSuggestion(projectPath = process.cwd()) {
39633
+ const scopePath = await resolvePermissionScopePath(projectPath);
39480
39634
  console.log();
39481
39635
  console.log(chalk.magenta.bold(" \u{1F4CB} Recommended Permissions"));
39482
39636
  console.log();
@@ -39487,7 +39641,7 @@ async function showPermissionSuggestion(projectPath = process.cwd()) {
39487
39641
  );
39488
39642
  console.log(chalk.dim(" \u2022 Deny: sudo, git push, docker push, inline code exec, DNS exfil..."));
39489
39643
  console.log();
39490
- 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"));
39491
39645
  console.log(chalk.dim(" Coco manage it when you approve actions from the prompt."));
39492
39646
  console.log(chalk.dim(" Note: applying here affects only the current project."));
39493
39647
  console.log();
@@ -39508,11 +39662,7 @@ async function showPermissionSuggestion(projectPath = process.cwd()) {
39508
39662
  return;
39509
39663
  }
39510
39664
  if (action === "dismiss") {
39511
- await saveProjectPermissionPreference(
39512
- "recommendedAllowlistDismissedProjects",
39513
- projectPath,
39514
- true
39515
- );
39665
+ await saveProjectPermissionPreference("recommendedAllowlistDismissedProjects", scopePath, true);
39516
39666
  console.log(chalk.dim(" Won't show again. Use /permissions to apply later."));
39517
39667
  return;
39518
39668
  }
@@ -39612,12 +39762,12 @@ var permissionsCommand = {
39612
39762
  };
39613
39763
  async function showStatus(session) {
39614
39764
  const tools = await getAllTrustedTools(session.projectPath);
39615
- const prefs = await loadPermissionPreferences();
39765
+ const permissionState = await getProjectPermissionState(session.projectPath);
39616
39766
  console.log();
39617
39767
  console.log(chalk.magenta.bold(" \u{1F510} Tool Permissions"));
39618
39768
  console.log();
39619
39769
  const allowCount = RECOMMENDED_GLOBAL.length + RECOMMENDED_PROJECT.length;
39620
- if (isRecommendedAllowlistAppliedForProject(prefs, session.projectPath)) {
39770
+ if (permissionState.applied) {
39621
39771
  console.log(
39622
39772
  chalk.green(" \u2713 Recommended allowlist applied") + chalk.dim(` (${allowCount} allow, ${RECOMMENDED_DENY.length} deny)`)
39623
39773
  );
@@ -39698,25 +39848,26 @@ async function resetPermissions(session) {
39698
39848
  return;
39699
39849
  }
39700
39850
  session.trustedTools.clear();
39701
- const emptySettings = {
39702
- globalTrusted: [],
39703
- projectTrusted: {},
39704
- projectDenied: {},
39851
+ const emptyProjectSettings = {
39852
+ trusted: [],
39853
+ denied: [],
39705
39854
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
39706
39855
  };
39707
39856
  try {
39708
- 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");
39709
39860
  } catch {
39710
39861
  }
39711
- await savePermissionPreference("recommendedAllowlistApplied", false);
39862
+ const permissionScopePath = session.projectPath;
39712
39863
  await saveProjectPermissionPreference(
39713
39864
  "recommendedAllowlistAppliedProjects",
39714
- session.projectPath,
39865
+ permissionScopePath,
39715
39866
  false
39716
39867
  );
39717
39868
  await saveProjectPermissionPreference(
39718
39869
  "recommendedAllowlistDismissedProjects",
39719
- session.projectPath,
39870
+ permissionScopePath,
39720
39871
  false
39721
39872
  );
39722
39873
  console.log(chalk.green(" \u2713 All tool permissions reset."));
@@ -43258,7 +43409,8 @@ var AgentManager = class extends EventEmitter {
43258
43409
  type: "tool_use",
43259
43410
  id: tc.id,
43260
43411
  name: tc.name,
43261
- input: tc.input
43412
+ input: tc.input,
43413
+ geminiThoughtSignature: tc.geminiThoughtSignature
43262
43414
  }));
43263
43415
  const assistantContent = response.content ? [{ type: "text", text: response.content }, ...toolUses] : toolUses;
43264
43416
  messages.push({ role: "assistant", content: assistantContent });
@@ -49171,7 +49323,7 @@ function buildProgressBar(pct) {
49171
49323
 
49172
49324
  // src/cli/repl/worktree/manager.ts
49173
49325
  init_logger();
49174
- var execFileAsync3 = promisify(execFile);
49326
+ var execFileAsync4 = promisify(execFile);
49175
49327
  var WORKTREES_DIR = ".worktrees";
49176
49328
  var WorktreeManager = class {
49177
49329
  worktrees = /* @__PURE__ */ new Map();
@@ -49395,7 +49547,7 @@ var WorktreeManager = class {
49395
49547
  try {
49396
49548
  await this.git(["push", "-u", "origin", worktree.branch]);
49397
49549
  const title = options.message ?? `Agent: ${worktree.name}`;
49398
- const { stdout } = await execFileAsync3(
49550
+ const { stdout } = await execFileAsync4(
49399
49551
  "gh",
49400
49552
  [
49401
49553
  "pr",
@@ -49421,10 +49573,10 @@ var WorktreeManager = class {
49421
49573
  }
49422
49574
  // ── Helpers ──────────────────────────────────────────────────────
49423
49575
  async git(args) {
49424
- return execFileAsync3("git", args, { cwd: this.projectRoot });
49576
+ return execFileAsync4("git", args, { cwd: this.projectRoot });
49425
49577
  }
49426
49578
  async gitIn(cwd, args) {
49427
- return execFileAsync3("git", args, { cwd });
49579
+ return execFileAsync4("git", args, { cwd });
49428
49580
  }
49429
49581
  async countChangedFiles(branch) {
49430
49582
  try {
@@ -53054,7 +53206,8 @@ ${tail}`;
53054
53206
  toolCallBuilders.set(id, {
53055
53207
  id,
53056
53208
  name: toolName,
53057
- input: {}
53209
+ input: {},
53210
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
53058
53211
  });
53059
53212
  if (toolName) {
53060
53213
  options.onToolPreparing?.(toolName);
@@ -53067,14 +53220,16 @@ ${tail}`;
53067
53220
  const finalToolCall = {
53068
53221
  id: builder.id,
53069
53222
  name: chunk.toolCall.name ?? builder.name,
53070
- input: chunk.toolCall.input ?? builder.input
53223
+ input: chunk.toolCall.input ?? builder.input,
53224
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature ?? builder.geminiThoughtSignature
53071
53225
  };
53072
53226
  collectedToolCalls.push(finalToolCall);
53073
53227
  } else if (chunk.toolCall.id && chunk.toolCall.name) {
53074
53228
  collectedToolCalls.push({
53075
53229
  id: chunk.toolCall.id,
53076
53230
  name: chunk.toolCall.name,
53077
- input: chunk.toolCall.input ?? {}
53231
+ input: chunk.toolCall.input ?? {},
53232
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
53078
53233
  });
53079
53234
  }
53080
53235
  }
@@ -53331,7 +53486,8 @@ ${tail}`;
53331
53486
  type: "tool_use",
53332
53487
  id: toolCall.id,
53333
53488
  name: toolCall.name,
53334
- input: toolCall.input
53489
+ input: toolCall.input,
53490
+ geminiThoughtSignature: toolCall.geminiThoughtSignature
53335
53491
  });
53336
53492
  const declineReason = declinedTools.get(toolCall.id);
53337
53493
  if (declineReason) {
@@ -54115,11 +54271,15 @@ async function startRepl(options = {}) {
54115
54271
  }
54116
54272
  session.config = configured;
54117
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"];
54118
54276
  let provider;
54119
54277
  try {
54120
54278
  provider = await createProvider(internalProviderId, {
54121
54279
  model: session.config.provider.model || void 0,
54122
- maxTokens: session.config.provider.maxTokens
54280
+ maxTokens: session.config.provider.maxTokens,
54281
+ project: initialVertexProject,
54282
+ location: initialVertexLocation
54123
54283
  });
54124
54284
  } catch (error) {
54125
54285
  p26.log.error(
@@ -54504,7 +54664,9 @@ async function startRepl(options = {}) {
54504
54664
  const newInternalId = getInternalProviderId(session.config.provider.type);
54505
54665
  provider = await createProvider(newInternalId, {
54506
54666
  model: session.config.provider.model || void 0,
54507
- 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"]
54508
54670
  });
54509
54671
  setAgentProvider(provider);
54510
54672
  initializeContextManager(session, provider);