@corbat-tech/coco 2.33.2 → 2.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -158,6 +158,19 @@ function getProxyFromSystem(platform = process.platform, run = defaultRunner) {
158
158
  }
159
159
  return null;
160
160
  }
161
+ function detectPacProxy(run = defaultRunner, platform = process.platform) {
162
+ if (platform !== "darwin") return null;
163
+ const out = run("scutil", ["--proxy"]);
164
+ if (!out) return null;
165
+ const getField = (name) => {
166
+ const re = new RegExp(`^\\s*${name}\\s*:\\s*(.+?)\\s*$`, "m");
167
+ return out.match(re)?.[1];
168
+ };
169
+ if (getField("ProxyAutoConfigEnable") === "1") {
170
+ return getField("ProxyAutoConfigURLString") ?? "PAC script";
171
+ }
172
+ return null;
173
+ }
161
174
  function installProxyDispatcher(resolveSystem = () => getProxyFromSystem()) {
162
175
  if (installed) {
163
176
  const existing = getProxyFromEnv();
@@ -1706,6 +1719,44 @@ function isCopilotTokenExpired(creds) {
1706
1719
  if (!creds.copilotToken || !creds.copilotTokenExpiresAt) return true;
1707
1720
  return Date.now() >= creds.copilotTokenExpiresAt - REFRESH_BUFFER_MS;
1708
1721
  }
1722
+ function exchangeForCopilotTokenViaGhCli() {
1723
+ return new Promise((resolve4) => {
1724
+ execFile("gh", ["api", "/copilot_internal/v2/token"], { timeout: 1e4 }, (err, stdout) => {
1725
+ if (err || !stdout) {
1726
+ resolve4(null);
1727
+ return;
1728
+ }
1729
+ try {
1730
+ const parsed = JSON.parse(stdout);
1731
+ resolve4(parsed.token && parsed.expires_at ? parsed : null);
1732
+ } catch {
1733
+ resolve4(null);
1734
+ }
1735
+ });
1736
+ });
1737
+ }
1738
+ function getGitHubCliAuthStatus() {
1739
+ return new Promise((resolve4) => {
1740
+ execFile(
1741
+ "gh",
1742
+ ["auth", "status", "--hostname", "github.com"],
1743
+ { timeout: 5e3 },
1744
+ (_err, stdout, stderr) => {
1745
+ const combined = (stdout ?? "") + (stderr ?? "");
1746
+ const match = combined.match(/Logged in to github\.com account (\S+)/);
1747
+ if (match) {
1748
+ resolve4(match[1]);
1749
+ return;
1750
+ }
1751
+ if (combined.includes("Logged in")) {
1752
+ resolve4("authenticated");
1753
+ return;
1754
+ }
1755
+ resolve4(null);
1756
+ }
1757
+ );
1758
+ });
1759
+ }
1709
1760
  async function getValidCopilotToken() {
1710
1761
  const creds = await loadCopilotCredentials();
1711
1762
  const envToken = process.env["COPILOT_GITHUB_TOKEN"] || process.env["GH_TOKEN"] || process.env["GITHUB_TOKEN"];
@@ -1719,8 +1770,7 @@ async function getValidCopilotToken() {
1719
1770
  isNew: false
1720
1771
  };
1721
1772
  }
1722
- try {
1723
- const copilotToken = await exchangeForCopilotToken(githubToken);
1773
+ const saveAndReturn = async (copilotToken) => {
1724
1774
  const updatedCreds = {
1725
1775
  ...creds ?? { githubToken },
1726
1776
  githubToken: creds?.githubToken ?? githubToken,
@@ -1734,11 +1784,23 @@ async function getValidCopilotToken() {
1734
1784
  baseUrl: getCopilotBaseUrl(updatedCreds.accountType),
1735
1785
  isNew: true
1736
1786
  };
1787
+ };
1788
+ try {
1789
+ const copilotToken = await exchangeForCopilotToken(githubToken);
1790
+ return saveAndReturn(copilotToken);
1737
1791
  } catch (error) {
1738
1792
  if (error instanceof CopilotAuthError && error.permanent) {
1793
+ const ghCliToken2 = await exchangeForCopilotTokenViaGhCli();
1794
+ if (ghCliToken2) {
1795
+ return saveAndReturn(ghCliToken2);
1796
+ }
1739
1797
  await deleteCopilotCredentials();
1740
1798
  return null;
1741
1799
  }
1800
+ const ghCliToken = await exchangeForCopilotTokenViaGhCli();
1801
+ if (ghCliToken) {
1802
+ return saveAndReturn(ghCliToken);
1803
+ }
1742
1804
  throw error;
1743
1805
  }
1744
1806
  }
@@ -2310,6 +2372,51 @@ async function runApiKeyFlow(provider) {
2310
2372
  console.log(chalk.green("\n \u2705 API key saved!\n"));
2311
2373
  return { tokens, accessToken: apiKey };
2312
2374
  }
2375
+ async function runCopilotAuthViaGhCli(ghCliUser) {
2376
+ const spinner18 = p26.spinner();
2377
+ spinner18.start("Exchanging GitHub CLI credentials for Copilot token...");
2378
+ try {
2379
+ const githubToken = await getGitHubCliToken();
2380
+ if (!githubToken) {
2381
+ spinner18.stop(chalk.red("\u2717 Could not read gh auth token"));
2382
+ return null;
2383
+ }
2384
+ let copilotToken;
2385
+ try {
2386
+ copilotToken = await exchangeForCopilotToken(githubToken);
2387
+ } catch {
2388
+ copilotToken = await exchangeForCopilotTokenViaGhCli();
2389
+ }
2390
+ if (!copilotToken) {
2391
+ spinner18.stop(chalk.red("\u2717 Could not obtain Copilot token via gh CLI"));
2392
+ console.log(chalk.dim(" Ensure your GitHub account has an active Copilot subscription:"));
2393
+ console.log(chalk.cyan(" \u2192 https://github.com/settings/copilot"));
2394
+ return null;
2395
+ }
2396
+ const creds = {
2397
+ githubToken,
2398
+ copilotToken: copilotToken.token,
2399
+ copilotTokenExpiresAt: copilotToken.expires_at * 1e3,
2400
+ accountType: copilotToken.annotations?.copilot_plan
2401
+ };
2402
+ await saveCopilotCredentials(creds);
2403
+ spinner18.stop(chalk.green("\u2713 GitHub Copilot authenticated via gh CLI!"));
2404
+ const userLabel = ghCliUser !== "authenticated" ? ` (@${ghCliUser})` : "";
2405
+ console.log(chalk.dim(` Account${userLabel} \xB7 Plan: ${creds.accountType ?? "individual"}`));
2406
+ console.log(chalk.dim(" Credentials stored in ~/.coco/tokens/copilot.json\n"));
2407
+ const tokens = {
2408
+ accessToken: copilotToken.token,
2409
+ tokenType: "Bearer",
2410
+ expiresAt: copilotToken.expires_at * 1e3
2411
+ };
2412
+ return { tokens, accessToken: copilotToken.token };
2413
+ } catch (error) {
2414
+ const { code } = describeFetchError(error);
2415
+ spinner18.stop(chalk.red("\u2717 Failed to authenticate via gh CLI"));
2416
+ printNetworkTroubleshooting(code);
2417
+ return null;
2418
+ }
2419
+ }
2313
2420
  async function runCopilotDeviceFlow() {
2314
2421
  console.log();
2315
2422
  console.log(chalk.magenta(" \u256D\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\u256E"));
@@ -2321,6 +2428,21 @@ async function runCopilotDeviceFlow() {
2321
2428
  console.log(chalk.dim(" Requires an active GitHub Copilot subscription."));
2322
2429
  console.log(chalk.dim(" https://github.com/settings/copilot"));
2323
2430
  console.log();
2431
+ const ghCliUser = await getGitHubCliAuthStatus();
2432
+ if (ghCliUser) {
2433
+ console.log(
2434
+ chalk.dim(` \u2139 GitHub CLI session detected`) + (ghCliUser !== "authenticated" ? chalk.dim(` (@${ghCliUser})`) : "") + chalk.dim(".")
2435
+ );
2436
+ const useGhSession = await p26.confirm({
2437
+ message: "Use your existing `gh` session? (recommended on corporate networks)",
2438
+ initialValue: true
2439
+ });
2440
+ if (p26.isCancel(useGhSession)) return null;
2441
+ if (useGhSession) {
2442
+ return runCopilotAuthViaGhCli(ghCliUser);
2443
+ }
2444
+ console.log();
2445
+ }
2324
2446
  try {
2325
2447
  console.log(chalk.dim(" Requesting device code from GitHub..."));
2326
2448
  const deviceCode = await requestGitHubDeviceCode();
@@ -2423,23 +2545,38 @@ async function runCopilotDeviceFlow() {
2423
2545
  }
2424
2546
  function printNetworkTroubleshooting(code) {
2425
2547
  const proxy = getProxyFromEnv();
2548
+ const pacUrl = detectPacProxy();
2426
2549
  if (proxy) {
2427
2550
  console.log(chalk.dim(` Proxy in use: ${maskProxyUrl(proxy)}`));
2428
2551
  console.log(chalk.dim(" \u2192 Verify the proxy allows github.com and api.github.com."));
2552
+ } else if (pacUrl) {
2553
+ console.log(
2554
+ chalk.dim(" Automatic proxy (PAC script) detected \u2014 Node.js cannot evaluate it.")
2555
+ );
2556
+ console.log(chalk.dim(" You have two options:"));
2557
+ console.log(chalk.dim(" 1. Run `gh auth login` first, then re-run /provider copilot."));
2558
+ console.log(
2559
+ chalk.dim(
2560
+ " Coco will reuse your `gh` session (Go HTTP client handles PAC automatically)."
2561
+ )
2562
+ );
2563
+ console.log(chalk.dim(" 2. Set HTTPS_PROXY=http://<your-proxy>:<port> manually and retry."));
2429
2564
  } else {
2430
2565
  console.log(chalk.dim(" No HTTPS_PROXY / HTTP_PROXY env vars detected."));
2431
2566
  console.log(chalk.dim(" \u2192 If you're behind a corporate proxy, set HTTPS_PROXY and retry."));
2567
+ console.log(chalk.dim(" \u2192 Or run `gh auth login` first \u2014 Coco will reuse the gh session."));
2432
2568
  }
2433
2569
  if (code === "SELF_SIGNED_CERT_IN_CHAIN" || code === "UNABLE_TO_VERIFY_LEAF_SIGNATURE") {
2434
2570
  console.log(
2435
2571
  chalk.dim(
2436
- " \u2192 A TLS interceptor is rewriting certificates. Add your corp root CA to NODE_EXTRA_CA_CERTS."
2572
+ " \u2192 TLS interceptor detected. Add your corporate root CA to NODE_EXTRA_CA_CERTS."
2437
2573
  )
2438
2574
  );
2575
+ console.log(chalk.dim(" Example: NODE_EXTRA_CA_CERTS=/path/to/corp-ca.crt coco"));
2439
2576
  } else if (code === "ENOTFOUND") {
2440
2577
  console.log(chalk.dim(" \u2192 Check DNS: `nslookup github.com`"));
2441
2578
  } else if (code === "ETIMEDOUT" || code === "UND_ERR_CONNECT_TIMEOUT") {
2442
- console.log(chalk.dim(" \u2192 A firewall may be dropping the connection silently."));
2579
+ console.log(chalk.dim(" \u2192 A firewall may be blocking the connection. Try `gh auth login`."));
2443
2580
  }
2444
2581
  }
2445
2582
  async function getOrRefreshOAuthToken(provider) {
@@ -2690,10 +2827,12 @@ __export(env_exports, {
2690
2827
  getBaseUrl: () => getBaseUrl,
2691
2828
  getDefaultModel: () => getDefaultModel,
2692
2829
  getDefaultProvider: () => getDefaultProvider,
2830
+ getEditorModel: () => getEditorModel,
2693
2831
  getInternalProviderId: () => getInternalProviderId,
2694
2832
  getLastUsedModel: () => getLastUsedModel,
2695
2833
  getLastUsedProvider: () => getLastUsedProvider,
2696
2834
  getLastUsedThinking: () => getLastUsedThinking,
2835
+ getWeakModel: () => getWeakModel,
2697
2836
  isOAuthProvider: () => isOAuthProvider,
2698
2837
  migrateOldPreferences: () => migrateOldPreferences,
2699
2838
  removeEnvProvider: () => removeEnvProvider,
@@ -3137,6 +3276,18 @@ async function migrateOldPreferences() {
3137
3276
  } catch {
3138
3277
  }
3139
3278
  }
3279
+ function getEditorModel() {
3280
+ const raw = process.env["COCO_EDITOR_MODEL"]?.trim();
3281
+ if (!raw || raw.length === 0) return void 0;
3282
+ if (["none", "default", "null", "undefined"].includes(raw.toLowerCase())) return void 0;
3283
+ return raw;
3284
+ }
3285
+ function getWeakModel() {
3286
+ const raw = process.env["COCO_WEAK_MODEL"]?.trim();
3287
+ if (!raw || raw.length === 0) return void 0;
3288
+ if (["none", "default", "null", "undefined"].includes(raw.toLowerCase())) return void 0;
3289
+ return raw;
3290
+ }
3140
3291
  var VALID_PROVIDERS, env;
3141
3292
  var init_env = __esm({
3142
3293
  "src/config/env.ts"() {
@@ -3166,7 +3317,9 @@ var init_env = __esm({
3166
3317
  provider: getDefaultProvider(),
3167
3318
  getApiKey,
3168
3319
  getBaseUrl,
3169
- getDefaultModel
3320
+ getDefaultModel,
3321
+ getWeakModel,
3322
+ getEditorModel
3170
3323
  };
3171
3324
  }
3172
3325
  });
@@ -4208,6 +4361,134 @@ var init_tool_call_normalizer = __esm({
4208
4361
  };
4209
4362
  }
4210
4363
  });
4364
+
4365
+ // src/providers/model-tier.ts
4366
+ function matchTier(model, table) {
4367
+ const lower = model.toLowerCase();
4368
+ const sorted = [...table].sort((a, b) => b.prefix.length - a.prefix.length);
4369
+ for (const { prefix, tier } of sorted) {
4370
+ if (lower.startsWith(prefix.toLowerCase())) return tier;
4371
+ }
4372
+ return null;
4373
+ }
4374
+ function getModelTier(provider, model) {
4375
+ if (!model) return "standard";
4376
+ const p47 = provider.toLowerCase();
4377
+ if (p47 === "anthropic") {
4378
+ return matchTier(model, ANTHROPIC_TIERS) ?? "standard";
4379
+ }
4380
+ if (p47 === "kimi-code") {
4381
+ return matchTier(model, KIMI_TIERS) ?? matchTier(model, ANTHROPIC_TIERS) ?? "standard";
4382
+ }
4383
+ if (p47 === "openai" || p47 === "copilot" || p47 === "codex") {
4384
+ if (model.startsWith("claude-")) {
4385
+ return matchTier(model, ANTHROPIC_TIERS) ?? "standard";
4386
+ }
4387
+ const evalMatch = matchTier(model, EVAL_TIERS);
4388
+ if (evalMatch) return evalMatch;
4389
+ return matchTier(model, OPENAI_TIERS) ?? "standard";
4390
+ }
4391
+ if (p47 === "gemini" || p47 === "vertex") {
4392
+ return matchTier(model, GEMINI_TIERS) ?? "standard";
4393
+ }
4394
+ if (p47 === "kimi" || p47 === "moonshot") {
4395
+ return matchTier(model, KIMI_TIERS) ?? "standard";
4396
+ }
4397
+ return "standard";
4398
+ }
4399
+ function getTierConfig(provider, model) {
4400
+ return TIER_CONFIGS[getModelTier(provider, model)];
4401
+ }
4402
+ var TIER_CONFIGS, ANTHROPIC_TIERS, OPENAI_TIERS, GEMINI_TIERS, KIMI_TIERS, EVAL_TIERS;
4403
+ var init_model_tier = __esm({
4404
+ "src/providers/model-tier.ts"() {
4405
+ TIER_CONFIGS = {
4406
+ mini: {
4407
+ maxTools: 12,
4408
+ parallelToolCalls: false,
4409
+ compactionThreshold: 0.5,
4410
+ supportsCoT: false
4411
+ },
4412
+ standard: {
4413
+ maxTools: 40,
4414
+ parallelToolCalls: true,
4415
+ compactionThreshold: 0.75,
4416
+ supportsCoT: true
4417
+ },
4418
+ advanced: {
4419
+ maxTools: 128,
4420
+ parallelToolCalls: true,
4421
+ compactionThreshold: 0.8,
4422
+ supportsCoT: true
4423
+ }
4424
+ };
4425
+ ANTHROPIC_TIERS = [
4426
+ // Haiku — mini tier
4427
+ { prefix: "claude-haiku", tier: "mini" },
4428
+ { prefix: "claude-3-haiku", tier: "mini" },
4429
+ // Sonnet / Claude 3.5 — standard tier
4430
+ { prefix: "claude-3-5-sonnet", tier: "standard" },
4431
+ { prefix: "claude-3-7-sonnet", tier: "standard" },
4432
+ { prefix: "claude-sonnet", tier: "standard" },
4433
+ // Opus — advanced tier
4434
+ { prefix: "claude-opus", tier: "advanced" },
4435
+ { prefix: "claude-3-opus", tier: "advanced" }
4436
+ // claude-4+ (future) — default to standard unless matched above
4437
+ ];
4438
+ OPENAI_TIERS = [
4439
+ // Mini models
4440
+ { prefix: "gpt-4o-mini", tier: "mini" },
4441
+ { prefix: "gpt-5-mini", tier: "mini" },
4442
+ { prefix: "gpt-5.4-mini", tier: "mini" },
4443
+ { prefix: "gpt-5.3-mini", tier: "mini" },
4444
+ { prefix: "o1-mini", tier: "mini" },
4445
+ { prefix: "o3-mini", tier: "mini" },
4446
+ // Advanced / reasoning models
4447
+ { prefix: "o1", tier: "advanced" },
4448
+ { prefix: "o3", tier: "advanced" },
4449
+ { prefix: "o4", tier: "advanced" },
4450
+ { prefix: "gpt-4.1", tier: "advanced" },
4451
+ { prefix: "gpt-5.4-codex", tier: "advanced" },
4452
+ { prefix: "gpt-5.3-codex", tier: "advanced" },
4453
+ { prefix: "gpt-5.2-codex", tier: "advanced" },
4454
+ { prefix: "gpt-5.1-codex", tier: "advanced" },
4455
+ { prefix: "gpt-5.4", tier: "advanced" },
4456
+ { prefix: "gpt-5.3", tier: "advanced" },
4457
+ { prefix: "gpt-5.2", tier: "advanced" },
4458
+ { prefix: "gpt-5.1", tier: "advanced" },
4459
+ // GPT-5 catch-all (non-mini/codex) — advanced
4460
+ { prefix: "gpt-5", tier: "advanced" },
4461
+ // GPT-4o — standard
4462
+ { prefix: "gpt-4o", tier: "standard" },
4463
+ // GPT-4 — standard
4464
+ { prefix: "gpt-4", tier: "standard" }
4465
+ ];
4466
+ GEMINI_TIERS = [
4467
+ // Flash — mini tier
4468
+ { prefix: "gemini-3-flash", tier: "mini" },
4469
+ { prefix: "gemini-2.5-flash", tier: "mini" },
4470
+ { prefix: "gemini-2.0-flash", tier: "mini" },
4471
+ { prefix: "gemini-1.5-flash", tier: "mini" },
4472
+ // Pro — standard/advanced
4473
+ { prefix: "gemini-3.1-pro", tier: "advanced" },
4474
+ { prefix: "gemini-3-pro", tier: "advanced" },
4475
+ { prefix: "gemini-2.5-pro", tier: "standard" },
4476
+ { prefix: "gemini-2.0-pro", tier: "standard" },
4477
+ { prefix: "gemini-1.5-pro", tier: "standard" }
4478
+ ];
4479
+ KIMI_TIERS = [
4480
+ { prefix: "kimi-for-coding", tier: "advanced" },
4481
+ { prefix: "kimi-k2", tier: "advanced" },
4482
+ { prefix: "kimi-latest", tier: "standard" },
4483
+ { prefix: "kimi", tier: "standard" }
4484
+ ];
4485
+ EVAL_TIERS = [
4486
+ { prefix: "grok-code", tier: "standard" },
4487
+ { prefix: "raptor", tier: "mini" },
4488
+ { prefix: "goldeneye", tier: "standard" }
4489
+ ];
4490
+ }
4491
+ });
4211
4492
  function needsResponsesApi(model) {
4212
4493
  return model.includes("codex") || model.startsWith("gpt-5") || model.startsWith("o4-") || model === "o3";
4213
4494
  }
@@ -4253,6 +4534,7 @@ var init_openai = __esm({
4253
4534
  init_retry();
4254
4535
  init_tool_call_normalizer();
4255
4536
  init_thinking();
4537
+ init_model_tier();
4256
4538
  DEFAULT_MODEL2 = "gpt-5.3-codex";
4257
4539
  CONTEXT_WINDOWS2 = {
4258
4540
  // OpenAI models
@@ -4474,18 +4756,21 @@ var init_openai = __esm({
4474
4756
  if (this.modelNeedsResponsesApi(model)) {
4475
4757
  return this.chatWithToolsViaResponses(messages, options);
4476
4758
  }
4759
+ const tierCfg = getTierConfig(this.id, model);
4477
4760
  return withRetry(async () => {
4478
4761
  try {
4479
4762
  const supportsTemp = this.supportsTemperature(model);
4480
4763
  const extraBody = this.getExtraBody(model, options?.thinking);
4481
4764
  const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4482
4765
  const maxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
4766
+ const tools = this.limitTools(options.tools, tierCfg.maxTools);
4483
4767
  const requestParams = {
4484
4768
  model,
4485
4769
  ...buildMaxTokensParam(model, maxTokens),
4486
4770
  messages: this.convertMessages(messages, options?.system),
4487
- tools: this.convertTools(options.tools),
4488
- tool_choice: this.convertToolChoice(options.toolChoice)
4771
+ tools: this.convertTools(tools),
4772
+ tool_choice: this.convertToolChoice(options.toolChoice),
4773
+ parallel_tool_calls: tierCfg.parallelToolCalls
4489
4774
  };
4490
4775
  if (supportsTemp) {
4491
4776
  requestParams.temperature = options?.temperature ?? this.config.temperature ?? 0;
@@ -4565,18 +4850,21 @@ var init_openai = __esm({
4565
4850
  yield* this.streamWithToolsViaResponses(messages, options);
4566
4851
  return;
4567
4852
  }
4853
+ const tierCfg = getTierConfig(this.id, model);
4568
4854
  let timeoutTriggered = false;
4569
4855
  try {
4570
4856
  const supportsTemp = this.supportsTemperature(model);
4571
4857
  const extraBody = this.getExtraBody(model, options?.thinking);
4572
4858
  const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4573
4859
  const maxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
4860
+ const tools = this.limitTools(options.tools, tierCfg.maxTools);
4574
4861
  const requestParams = {
4575
4862
  model,
4576
4863
  ...buildMaxTokensParam(model, maxTokens),
4577
4864
  messages: this.convertMessages(messages, options?.system),
4578
- tools: this.convertTools(options.tools),
4865
+ tools: this.convertTools(tools),
4579
4866
  tool_choice: this.convertToolChoice(options.toolChoice),
4867
+ parallel_tool_calls: tierCfg.parallelToolCalls,
4580
4868
  stream: true
4581
4869
  };
4582
4870
  if (supportsTemp) {
@@ -4946,6 +5234,17 @@ var init_openai = __esm({
4946
5234
  }
4947
5235
  return content.filter((block) => block.type === "text").map((block) => block.text).join("");
4948
5236
  }
5237
+ /**
5238
+ * Limit the tool list to at most `max` entries.
5239
+ * Built-in tools (those without an `mcp_server` tag) are prioritised over
5240
+ * MCP tools so core capabilities are never dropped.
5241
+ */
5242
+ limitTools(tools, max) {
5243
+ if (tools.length <= max) return tools;
5244
+ const builtin = tools.filter((t) => !("serverName" in t && t.serverName));
5245
+ const mcp = tools.filter((t) => "serverName" in t && t.serverName);
5246
+ return [...builtin, ...mcp].slice(0, max);
5247
+ }
4949
5248
  /**
4950
5249
  * Convert tools to OpenAI format
4951
5250
  */
@@ -4955,7 +5254,8 @@ var init_openai = __esm({
4955
5254
  function: {
4956
5255
  name: tool.name,
4957
5256
  description: truncateToolDescription(tool.description),
4958
- parameters: tool.input_schema
5257
+ parameters: tool.input_schema,
5258
+ strict: true
4959
5259
  }
4960
5260
  }));
4961
5261
  }
@@ -5088,8 +5388,11 @@ var init_openai = __esm({
5088
5388
  return withRetry(async () => {
5089
5389
  try {
5090
5390
  const model = options?.model ?? this.config.model ?? DEFAULT_MODEL2;
5391
+ const tierCfg = getTierConfig(this.id, model);
5091
5392
  const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
5092
- const tools = this.convertToolsForResponses(options.tools);
5393
+ const tools = this.convertToolsForResponses(
5394
+ this.limitTools(options.tools, tierCfg.maxTools)
5395
+ );
5093
5396
  const supportsTemp = this.supportsTemperature(model);
5094
5397
  const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
5095
5398
  const response = await this.client.responses.create({
@@ -5209,8 +5512,10 @@ var init_openai = __esm({
5209
5512
  let timeoutTriggered = false;
5210
5513
  try {
5211
5514
  const model = options?.model ?? this.config.model ?? DEFAULT_MODEL2;
5515
+ const tierCfg = getTierConfig(this.id, model);
5212
5516
  const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
5213
- const tools = options.tools.length > 0 ? this.convertToolsForResponses(options.tools) : void 0;
5517
+ const limitedTools = this.limitTools(options.tools, tierCfg.maxTools);
5518
+ const tools = limitedTools.length > 0 ? this.convertToolsForResponses(limitedTools) : void 0;
5214
5519
  const supportsTemp = this.supportsTemperature(model);
5215
5520
  const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
5216
5521
  const requestParams = {
@@ -5442,7 +5747,7 @@ var init_openai = __esm({
5442
5747
  name: tool.name,
5443
5748
  description: tool.description ? truncateToolDescription(tool.description) : void 0,
5444
5749
  parameters: tool.input_schema ?? null,
5445
- strict: false
5750
+ strict: true
5446
5751
  }));
5447
5752
  }
5448
5753
  };
@@ -10022,7 +10327,8 @@ var init_compactor = __esm({
10022
10327
  conversationText,
10023
10328
  provider,
10024
10329
  signal,
10025
- options.focusTopic
10330
+ options.focusTopic,
10331
+ options.summaryModel
10026
10332
  );
10027
10333
  const systemMessages = messages.filter((m) => m.role === "system");
10028
10334
  const summaryMessage = {
@@ -10077,14 +10383,15 @@ ${summary}
10077
10383
  /**
10078
10384
  * Generate a summary of the conversation using the LLM
10079
10385
  */
10080
- async generateSummary(conversationText, provider, signal, focusTopic) {
10386
+ async generateSummary(conversationText, provider, signal, focusTopic, summaryModel) {
10081
10387
  if (signal?.aborted) return "[Compaction cancelled]";
10082
10388
  const prompt = buildCompactionPrompt(focusTopic) + conversationText;
10083
10389
  try {
10084
10390
  const chatPromise = provider.chat([{ role: "user", content: prompt }], {
10085
10391
  maxTokens: this.config.summaryMaxTokens,
10086
- temperature: 0.3
10392
+ temperature: 0.3,
10087
10393
  // Lower temperature for more consistent summaries
10394
+ ...summaryModel ? { model: summaryModel } : {}
10088
10395
  });
10089
10396
  if (signal) {
10090
10397
  const abortPromise = new Promise((_, reject) => {
@@ -10980,6 +11287,14 @@ ${finalInstructions}`;
10980
11287
  systemPrompt = `${systemPrompt}
10981
11288
 
10982
11289
  ${PLAN_MODE_SYSTEM_PROMPT}`;
11290
+ }
11291
+ const providerType = session.config.provider.type;
11292
+ const currentModel = session.config.provider.model ?? "";
11293
+ const tierCfg = getTierConfig(providerType, currentModel);
11294
+ if (!tierCfg.supportsCoT) {
11295
+ systemPrompt = `${systemPrompt}
11296
+
11297
+ ${MINI_MODEL_ADDENDUM}`;
10983
11298
  }
10984
11299
  return [{ role: "system", content: systemPrompt }, ...session.messages];
10985
11300
  }
@@ -11164,8 +11479,11 @@ async function initializeSessionTrust(session) {
11164
11479
  }
11165
11480
  function initializeContextManager(session, provider) {
11166
11481
  const contextWindow = provider.getContextWindow();
11482
+ const providerType = session.config.provider.type;
11483
+ const model = session.config.provider.model ?? "";
11484
+ const tierCfg = getTierConfig(providerType, model);
11167
11485
  session.contextManager = createContextManager(contextWindow, {
11168
- compactionThreshold: 0.8,
11486
+ compactionThreshold: tierCfg.compactionThreshold,
11169
11487
  reservedTokens: 4096
11170
11488
  });
11171
11489
  }
@@ -11200,7 +11518,11 @@ async function checkAndCompactContext(session, provider, signal, toolRegistry) {
11200
11518
  summaryMaxTokens: 1e3
11201
11519
  });
11202
11520
  try {
11203
- const result = await compactor.compact(session.messages, provider, signal);
11521
+ const summaryModel = session.config.provider.weakModel ?? session.config.provider.editorModel ?? getWeakModel() ?? getEditorModel();
11522
+ const result = await compactor.compact(session.messages, provider, {
11523
+ signal,
11524
+ ...summaryModel ? { summaryModel } : {}
11525
+ });
11204
11526
  if (result.wasCompacted) {
11205
11527
  const compactedNonSystem = result.messages.filter((m) => m.role !== "system");
11206
11528
  session.messages = compactedNonSystem;
@@ -11244,11 +11566,12 @@ function getSessionMemory(session) {
11244
11566
  async function reloadSessionMemory(session) {
11245
11567
  await initializeSessionMemory(session);
11246
11568
  }
11247
- 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;
11569
+ var MAX_SKILL_INSTRUCTIONS_CHARS, TRUST_SETTINGS_DIR, TRUST_SETTINGS_FILE, PROJECT_TRUST_FILE_RELATIVE_PATH, CATEGORY_LABELS, COCO_SYSTEM_PROMPT, MINI_MODEL_ADDENDUM, SHELL_METACHARACTERS, SAFE_COMMAND_VALIDATORS;
11248
11570
  var init_session = __esm({
11249
11571
  "src/cli/repl/session.ts"() {
11250
11572
  init_env();
11251
11573
  init_thinking();
11574
+ init_model_tier();
11252
11575
  init_manager();
11253
11576
  init_compactor();
11254
11577
  init_memory();
@@ -11446,6 +11769,17 @@ Responses are short and direct by default. Lead with the answer or action, not r
11446
11769
  **During tool-calling iterations, keep text minimal.** A single short orienting line before tool calls is acceptable. Do NOT explain every step, narrate what you are about to do, or produce paragraphs between tool calls. Reserve explanatory text for your final response after all tools have completed.
11447
11770
 
11448
11771
  **Code blocks in responses are expensive.** Only include a code block when the user explicitly asks to see code, or when the code IS the deliverable (e.g., a script to paste in a terminal). Never include a code block to "show your work" when you can write the file directly instead.`;
11772
+ MINI_MODEL_ADDENDUM = `
11773
+ ## Mini-Model Mode
11774
+
11775
+ You are running on a fast, compact model. Keep outputs minimal and actions direct.
11776
+
11777
+ - Call ONE tool at a time. Do NOT combine multiple tool calls per turn.
11778
+ - Skip multi-step planning \u2014 just do the next concrete action.
11779
+ - Debugging: read the error, fix it, verify. No analysis phases.
11780
+ - Verification: run the command, report the result. No elaborate protocols.
11781
+ - Skip "Parallel Execution" \u2014 serialize all tool calls for reliability.
11782
+ - NEVER narrate your plan. NEVER explain what you are about to do. Just do it.`;
11449
11783
  SHELL_METACHARACTERS = /[;|&`$(){}<>!\n\\'"]/;
11450
11784
  SAFE_COMMAND_VALIDATORS = {
11451
11785
  git: (args) => {
@@ -12589,13 +12923,21 @@ var init_registry4 = __esm({
12589
12923
  const field = issue.path.join(".") || "input";
12590
12924
  return `${field} (${issue.message.toLowerCase()})`;
12591
12925
  });
12592
- errorMessage = `Invalid tool input \u2014 ${fields.join(", ")}`;
12926
+ errorMessage = `Invalid tool input for '${name}' \u2014 ${fields.join(", ")}`;
12593
12927
  const allUndefined = error.issues.every(
12594
12928
  (i) => i.message.toLowerCase().includes("received undefined")
12595
12929
  );
12596
12930
  if (allUndefined && error.issues.length > 1) {
12597
12931
  errorMessage += ". All parameters are missing \u2014 this is likely a JSON serialization error on our side. Please retry with the same arguments.";
12598
12932
  }
12933
+ try {
12934
+ const schema = zodToJsonSchema(tool.parameters);
12935
+ errorMessage += `
12936
+
12937
+ Expected schema for '${name}':
12938
+ ${JSON.stringify(schema, null, 2)}`;
12939
+ } catch {
12940
+ }
12599
12941
  } else if (isCocoError(error)) {
12600
12942
  const causeMsg = error.cause instanceof Error ? error.cause.message : "";
12601
12943
  const isRawEnoent = causeMsg.startsWith("ENOENT:");
@@ -18701,7 +19043,7 @@ var init_git = __esm({
18701
19043
  init_errors();
18702
19044
  gitStatusTool = defineTool({
18703
19045
  name: "git_status",
18704
- description: `Get the current git repository status including branch, staged, modified, and untracked files.
19046
+ description: `Return the current git repository state: branch name, staged files, modified files, and untracked files. Use this before committing to see what has changed, or to check which branch is active. Do NOT use this to see the content of changes (use git_diff), or to view history (use git_log). Returns isClean: true when the working tree is clean with nothing to commit.
18705
19047
 
18706
19048
  Examples:
18707
19049
  - Current dir: {} \u2192 { "branch": "main", "isClean": false, "modified": ["src/app.ts"] }
@@ -18735,7 +19077,7 @@ Examples:
18735
19077
  });
18736
19078
  gitDiffTool = defineTool({
18737
19079
  name: "git_diff",
18738
- description: `Get git diff showing file changes.
19080
+ description: `Show the unified diff of changes in the working tree or staging area. Use this to see exactly what lines changed in files before committing, or to review changes the user just made. Use staged: true to see only what has been staged (git add), or omit it to see all unstaged changes. Do NOT use this to see commit history (use git_log) or file status summary (use git_status).
18739
19081
 
18740
19082
  Examples:
18741
19083
  - All changes: {} \u2192 { "diff": "...", "filesChanged": 3, "insertions": 42, "deletions": 10 }
@@ -18772,7 +19114,7 @@ Examples:
18772
19114
  });
18773
19115
  gitAddTool = defineTool({
18774
19116
  name: "git_add",
18775
- description: `Stage files for commit.
19117
+ description: `Stage one or more files (or patterns) so they are included in the next git commit. Use this after making file edits to mark them ready for commit. Pass ["."] to stage all changes, or list specific file paths to stage selectively. Do NOT use this to commit (use git_commit after staging) or to view what is staged (use git_status or git_diff with staged: true).
18776
19118
 
18777
19119
  Examples:
18778
19120
  - Stage all: { "files": ["."] }
@@ -18798,7 +19140,7 @@ Examples:
18798
19140
  });
18799
19141
  gitCommitTool = defineTool({
18800
19142
  name: "git_commit",
18801
- description: `Create a git commit with the staged changes.
19143
+ description: `Create a git commit from whatever is currently staged (git add must run first). Use conventional commit format (feat/fix/docs/chore). Do NOT use this when nothing is staged \u2014 check git_status first; do NOT use this to stage files (use git_add) or to see what will be committed (use git_diff with staged: true).
18802
19144
 
18803
19145
  Examples:
18804
19146
  - Simple commit: { "message": "fix: resolve auth bug" }
@@ -18831,7 +19173,7 @@ Examples:
18831
19173
  });
18832
19174
  gitLogTool = defineTool({
18833
19175
  name: "git_log",
18834
- description: `Get git commit history.
19176
+ description: `Show git commit history with hashes, messages, authors, and dates. Use this to understand what changed recently, find the commit that introduced a bug, or check whether a feature was already merged. Do NOT use this to see the content of changes \u2014 use git_diff; do NOT use this to check current file state \u2014 use git_status.
18835
19177
 
18836
19178
  Examples:
18837
19179
  - Last 10 commits: {} (default)
@@ -18871,7 +19213,7 @@ Examples:
18871
19213
  });
18872
19214
  gitBranchTool = defineTool({
18873
19215
  name: "git_branch",
18874
- description: `Manage git branches (list, create, delete).
19216
+ description: `List all local branches, create a new branch from the current HEAD, or delete a branch. Use this to see available branches before checking out, or to create a feature branch. Do NOT use this to switch the active branch \u2014 use git_checkout; do NOT delete branches that have unmerged work.
18875
19217
 
18876
19218
  Examples:
18877
19219
  - List branches: {} \u2192 { "branches": ["main", "feature/x"], "current": "main" }
@@ -18915,7 +19257,7 @@ Examples:
18915
19257
  });
18916
19258
  gitCheckoutTool = defineTool({
18917
19259
  name: "git_checkout",
18918
- description: `Switch branches or create and switch to a new branch.
19260
+ description: `Switch the working directory to an existing branch, or create a new branch and switch to it in one step. Use this to move between branches or start a new feature branch. Do NOT use this with unsaved file edits (stage or stash first); do NOT use this to just list branches \u2014 use git_branch.
18919
19261
 
18920
19262
  Examples:
18921
19263
  - Switch branch: { "branch": "main" }
@@ -18945,7 +19287,7 @@ Examples:
18945
19287
  });
18946
19288
  gitPushTool = defineTool({
18947
19289
  name: "git_push",
18948
- description: `Push commits to remote repository.
19290
+ description: `Upload local commits to a remote repository. Use setUpstream: true the first time you push a new branch to create the tracking relationship. Do NOT use this on main/master without explicit user confirmation \u2014 it modifies shared history; do NOT push without first checking git_status to confirm all commits are clean.
18949
19291
 
18950
19292
  Examples:
18951
19293
  - Push current: {} \u2192 pushes to origin
@@ -18983,7 +19325,7 @@ Examples:
18983
19325
  });
18984
19326
  gitPullTool = defineTool({
18985
19327
  name: "git_pull",
18986
- description: `Pull changes from remote repository.
19328
+ description: `Fetch and integrate remote commits into the current branch. Use rebase: true to keep a linear history (preferred for feature branches). Do NOT use this when you have uncommitted local changes \u2014 stage or stash them first; if a merge conflict is reported, use read_file / edit_file to resolve then git_add and git_commit.
18987
19329
 
18988
19330
  Examples:
18989
19331
  - Pull current: {} \u2192 pulls from origin
@@ -19241,16 +19583,11 @@ var init_bash = __esm({
19241
19583
  ];
19242
19584
  bashExecTool = defineTool({
19243
19585
  name: "bash_exec",
19244
- description: `Execute a bash/shell command in the user's shell environment.
19586
+ description: `Execute a shell command and return its stdout, stderr, and exit code. Use this for running build scripts, test runners, linters, package managers, CLI tools, git commands, or any other shell operation. Runs in the user's shell environment with their full PATH and locally-configured credentials (kubeconfig, gcloud auth, AWS profiles, SSH keys) so never claim you cannot run a command due to missing credentials \u2014 always attempt and report the actual exit code. Do NOT use this to read or write files (use read_file / write_file / edit_file which are safer); prefer specific file tools for file operations.
19245
19587
 
19246
19588
  Runs with the user's full PATH and inherited environment \u2014 any tool installed
19247
19589
  on the user's machine is available: kubectl, gcloud, aws, docker, git, node,
19248
- pnpm, and others. Credentials configured locally are inherited automatically:
19249
- kubeconfig contexts, gcloud auth, AWS profiles, SSH keys, etc.
19250
-
19251
- IMPORTANT: never claim you cannot run a command because you lack credentials
19252
- or access \u2014 the environment is the user's own shell. Always attempt; report
19253
- failure only if the command actually returns a non-zero exit code.
19590
+ pnpm, and others.
19254
19591
 
19255
19592
  Examples:
19256
19593
  - List files: { "command": "ls -la" }
@@ -19495,7 +19832,7 @@ var init_github = __esm({
19495
19832
  init_errors();
19496
19833
  ghCheckAuthTool = defineTool({
19497
19834
  name: "gh_check_auth",
19498
- description: "Check if the GitHub CLI is installed and authenticated.",
19835
+ description: "Verify the gh CLI is installed and the user is logged into GitHub. Call this before any other gh_ tool to confirm authentication is working. Returns the logged-in username if authenticated.",
19499
19836
  category: "git",
19500
19837
  parameters: z.object({
19501
19838
  cwd: z.string().optional()
@@ -19515,7 +19852,7 @@ var init_github = __esm({
19515
19852
  });
19516
19853
  ghRepoInfoTool = defineTool({
19517
19854
  name: "gh_repo_info",
19518
- description: "Get GitHub repository information (name, default branch, URL).",
19855
+ description: "Get the remote GitHub repository's name, owner, default branch, and URL from within the current git working directory. Use before creating PRs to confirm the target repo. Requires gh_check_auth to pass first.",
19519
19856
  category: "git",
19520
19857
  parameters: z.object({
19521
19858
  cwd: z.string().optional()
@@ -19537,7 +19874,7 @@ var init_github = __esm({
19537
19874
  });
19538
19875
  ghPrCreateTool = defineTool({
19539
19876
  name: "gh_pr_create",
19540
- description: "Create a GitHub pull request.",
19877
+ description: "Open a pull request on GitHub from the current branch. Requires commits to be pushed first (use git_push with setUpstream: true). Do NOT use this if a PR for this branch already exists \u2014 use gh_pr_list to check first. Use draft: true for work in progress.",
19541
19878
  category: "git",
19542
19879
  parameters: z.object({
19543
19880
  title: z.string().describe("PR title"),
@@ -19561,7 +19898,7 @@ var init_github = __esm({
19561
19898
  });
19562
19899
  ghPrMergeTool = defineTool({
19563
19900
  name: "gh_pr_merge",
19564
- description: "Merge a GitHub pull request.",
19901
+ description: "Merge a pull request into its base branch. Use squash (default) for feature branches to keep history clean. Always confirm with gh_pr_checks first to ensure CI passed. Do NOT merge if anyFailed is true.",
19565
19902
  category: "git",
19566
19903
  parameters: z.object({
19567
19904
  number: z.number().describe("PR number"),
@@ -19582,7 +19919,7 @@ var init_github = __esm({
19582
19919
  });
19583
19920
  ghPrChecksTool = defineTool({
19584
19921
  name: "gh_pr_checks",
19585
- description: "Get CI check statuses for a pull request.",
19922
+ description: "Poll the CI check results for a pull request \u2014 returns pass/fail/pending per check and convenience flags (allPassed, anyFailed). Call this after pushing to confirm CI is green before merging. Check anyPending to know if results are still arriving.",
19586
19923
  category: "git",
19587
19924
  parameters: z.object({
19588
19925
  number: z.number().describe("PR number"),
@@ -19616,7 +19953,7 @@ var init_github = __esm({
19616
19953
  });
19617
19954
  ghPrListTool = defineTool({
19618
19955
  name: "gh_pr_list",
19619
- description: "List pull requests, optionally filtered by head branch.",
19956
+ description: "List open (or all) pull requests for this repository, optionally filtered to a specific branch. Use before gh_pr_create to ensure a PR for the current branch doesn't already exist. Returns PR number, title, URL, and state.",
19620
19957
  category: "git",
19621
19958
  parameters: z.object({
19622
19959
  head: z.string().optional().describe("Filter by head branch name"),
@@ -19633,7 +19970,7 @@ var init_github = __esm({
19633
19970
  });
19634
19971
  ghReleaseCreateTool = defineTool({
19635
19972
  name: "gh_release_create",
19636
- description: "Create a GitHub release with notes.",
19973
+ description: "Publish a versioned GitHub release attached to a tag. The tag must already exist in the repo (push it with git_push first). Provide markdown release notes. Use prerelease: true for release candidates or beta builds.",
19637
19974
  category: "git",
19638
19975
  parameters: z.object({
19639
19976
  tag: z.string().describe("Tag name (e.g., v1.2.3)"),
@@ -42613,7 +42950,7 @@ Use list_dir or glob to find the correct path.`;
42613
42950
  }
42614
42951
  var readFileTool = defineTool({
42615
42952
  name: "read_file",
42616
- description: `Read the contents of a file.
42953
+ description: `Read the full text content of a file at the given path and return it as a string. Use this when you need the actual source code, configuration values, or text content of a specific file you already know the path to. Do NOT use this to list files in a directory (use list_directory), to check if a file exists (use file_exists), or to search for files by name pattern (use find_files). Returns an error if the path does not exist or is not a readable text file.
42617
42954
 
42618
42955
  Examples:
42619
42956
  - Read config: { "path": "package.json" }
@@ -42671,7 +43008,7 @@ Examples:
42671
43008
  });
42672
43009
  var writeFileTool = defineTool({
42673
43010
  name: "write_file",
42674
- description: `Write content to a file, creating it if it doesn't exist.
43011
+ description: `Write text content to a file, replacing it entirely if it already exists or creating it if it does not. Use this when you want to create a new file or fully replace an existing file's content. Do NOT use this to make a small change to an existing file (use edit_file instead, which performs a targeted find-and-replace without rewriting the whole file). Set createDirs: true to automatically create missing parent directories; otherwise the parent directory must already exist.
42675
43012
 
42676
43013
  Examples:
42677
43014
  - Create file: { "path": "src/utils.ts", "content": "export const foo = 1;" }
@@ -42732,7 +43069,7 @@ Examples:
42732
43069
  });
42733
43070
  var editFileTool = defineTool({
42734
43071
  name: "edit_file",
42735
- description: `Edit a file by replacing text (find and replace).
43072
+ description: `Make a targeted text replacement inside an existing file by finding oldText and replacing it with newText. Use this for surgical edits to source code, configuration files, or documentation \u2014 it is much safer than rewriting the whole file with write_file because it only touches the exact bytes you specify. The oldText must match exactly (including whitespace and indentation); if it appears more than once in the file, use all: true to replace every occurrence or make oldText longer to be unique. Do NOT use this to create new files (use write_file) or to rename/move files (use move_path).
42736
43073
 
42737
43074
  Examples:
42738
43075
  - Single replace: { "path": "src/app.ts", "oldText": "TODO:", "newText": "DONE:" }
@@ -42816,7 +43153,7 @@ Hint: Use read_file first to verify the exact content.`
42816
43153
  });
42817
43154
  var globTool = defineTool({
42818
43155
  name: "glob",
42819
- description: `Find files matching a glob pattern.
43156
+ description: `Find files whose paths match a glob pattern and return their relative paths as a list. Use this when you know the file extension or naming convention but not the exact path (e.g. find all TypeScript test files, all JSON configs). Do NOT use this to search inside file contents \u2014 use grep or search for that. Returns an empty list when nothing matches; does not throw an error for zero results. node_modules, .git, and dist directories are excluded by default.
42820
43157
 
42821
43158
  Examples:
42822
43159
  - All TypeScript: { "pattern": "**/*.ts" }
@@ -42858,7 +43195,7 @@ Examples:
42858
43195
  });
42859
43196
  var fileExistsTool = defineTool({
42860
43197
  name: "file_exists",
42861
- description: `Check if a file or directory exists.
43198
+ description: `Check whether a path exists on disk and whether it is a file or directory. Use this before attempting to read or write a path when you are unsure it exists \u2014 it never throws, always returning { exists: false } for missing paths. Do NOT use this to read file contents (use read_file) or to list directory contents (use list_directory). Returns isFile and isDirectory flags so you can distinguish files from directories in a single call.
42862
43199
 
42863
43200
  Examples:
42864
43201
  - Check file: { "path": "package.json" } \u2192 { "exists": true, "isFile": true, "isDirectory": false }
@@ -42888,7 +43225,7 @@ Examples:
42888
43225
  });
42889
43226
  var listDirTool = defineTool({
42890
43227
  name: "list_dir",
42891
- description: `List contents of a directory.
43228
+ description: `List the immediate entries (files and subdirectories) inside a directory and return their names, types, and sizes. Use this to understand what's in a folder before deciding which files to read. Do NOT use this to find files matching a pattern across the whole project (use glob) or to read file contents (use read_file). Returns an error if the path does not exist or is not a directory.
42892
43229
 
42893
43230
  Examples:
42894
43231
  - List src: { "path": "src" }
@@ -44426,7 +44763,7 @@ init_registry4();
44426
44763
  init_errors();
44427
44764
  var grepTool = defineTool({
44428
44765
  name: "grep",
44429
- description: `Search for text patterns in files using regex.
44766
+ description: `Search for a regex pattern across all files in a directory and return matching lines with file paths and line numbers. Use this when you know a symbol name, string literal, or pattern and want to find where it appears in the codebase (function definitions, imports, error messages, config keys). Do NOT use this to find files by name \u2014 use glob for that. Do NOT use this when you already know the file path \u2014 use read_file directly. Searches are recursive by default; narrow with the include glob to restrict to specific file types.
44430
44767
 
44431
44768
  Examples:
44432
44769
  - Simple search: { "pattern": "TODO" }
@@ -44559,7 +44896,7 @@ Examples:
44559
44896
  });
44560
44897
  var findInFileTool = defineTool({
44561
44898
  name: "find_in_file",
44562
- description: `Search for a pattern in a single file.
44899
+ description: `Search for a text or regex pattern inside a single known file and return matching line numbers and content. Use this when you already have the file path and want to quickly find which lines match (e.g. locate a function signature, find an import, or confirm a value exists). Do NOT use this to search across the whole project \u2014 use grep for that. Returns line numbers so you can navigate directly to the match.
44563
44900
 
44564
44901
  Examples:
44565
44902
  - Find text: { "file": "src/app.ts", "pattern": "export" }
@@ -45700,7 +46037,7 @@ async function searchSerpApi(query, maxResults, timeout) {
45700
46037
  }
45701
46038
  var webSearchTool = defineTool({
45702
46039
  name: "web_search",
45703
- description: `Search the web for information, documentation, error solutions, and API references.
46040
+ description: `Search the web using a natural-language query and return a list of result titles, URLs, and short excerpts. Use this when you need to find documentation, research an error message, discover API references, or learn about a library before you have a specific URL. Do NOT use this when you already have the URL \u2014 use web_fetch to read a known page directly. Returns result titles and URLs you can then fetch with web_fetch for full content.
45704
46041
 
45705
46042
  Examples:
45706
46043
  - Basic search: { "query": "typescript zod validation examples" }
@@ -45985,7 +46322,7 @@ ${rows.join("\n")}
45985
46322
  }
45986
46323
  var webFetchTool = defineTool({
45987
46324
  name: "web_fetch",
45988
- description: `Fetch a URL and convert its content to clean markdown. Extracts main content, strips navigation/ads, and returns readable text.
46325
+ description: `Fetch the content of a specific URL and return it as clean, readable markdown text. Use this when you already have the exact URL \u2014 for example a documentation page, a GitHub file, or an API response \u2014 and want to read its content. Do NOT use this to find information without a URL; use web_search instead to discover relevant URLs first. By default strips navigation menus, headers, and ads to return only the main content; set extractContent: false to get the raw HTML/text.
45989
46326
 
45990
46327
  Examples:
45991
46328
  - Fetch documentation: { "url": "https://docs.example.com/api" }
@@ -46673,8 +47010,7 @@ async function saveMemory(scope, memory) {
46673
47010
  }
46674
47011
  var createMemoryTool = defineTool({
46675
47012
  name: "create_memory",
46676
- description: `Save a memory (key-value pair) that persists between sessions.
46677
- Use for storing project conventions, patterns, preferences, and learnings.
47013
+ description: `Persist a named key-value fact that survives across sessions \u2014 use this for project conventions, patterns, user preferences, or discovered constraints you'll need later. If the key already exists the value is overwritten. Do NOT use this for temporary scratch data within a single session; do NOT use this to store file contents \u2014 use write_file for that.
46678
47014
 
46679
47015
  Examples:
46680
47016
  - Save convention: { "key": "naming-convention", "value": "Use camelCase for variables", "tags": ["style"] }
@@ -46731,7 +47067,7 @@ Examples:
46731
47067
  });
46732
47068
  var recallMemoryTool = defineTool({
46733
47069
  name: "recall_memory",
46734
- description: `Search and recall stored memories by key, tags, or free text query.
47070
+ description: `Search previously saved memories by key substring, tags, or free-text value match. Use this at the start of a task to check if relevant conventions or patterns were already learned. Returns full memory content. Do NOT use this to list all memories (use list_memories) or to search file content (use grep).
46735
47071
 
46736
47072
  Examples:
46737
47073
  - By key substring: { "query": "naming" }
@@ -46786,7 +47122,7 @@ Examples:
46786
47122
  });
46787
47123
  var listMemoriesTool = defineTool({
46788
47124
  name: "list_memories",
46789
- description: `List all stored memories with optional filtering. Returns lightweight index entries.
47125
+ description: `List stored memory keys and tags without loading full values \u2014 useful for browsing what has been saved before recalling specific entries. Do NOT use this to get memory content (use recall_memory for that).
46790
47126
 
46791
47127
  Examples:
46792
47128
  - List all: { "scope": "all" }
@@ -57005,7 +57341,7 @@ program.command("setup").description("Configure AI provider and API key").action
57005
57341
  console.log("\n\u274C Setup cancelled.");
57006
57342
  }
57007
57343
  });
57008
- program.command("chat", { isDefault: true }).description("Start interactive chat session with the agent").option("-m, --model <model>", "LLM model to use").option("--provider <provider>", "LLM provider (anthropic, openai, codex, gemini, kimi)").option("-p, --path <path>", "Project path", process.cwd()).option("-P, --print [task]", "Headless mode: run task and print output (no interactive UI)").option("--output <format>", "Output format for headless mode (text or json)", "text").option("--setup", "Run setup wizard before starting").action(
57344
+ program.command("chat", { isDefault: true }).description("Start interactive chat session with the agent").option("-m, --model <model>", "LLM model to use").option("--provider <provider>", "LLM provider (anthropic, openai, codex, gemini, kimi)").option("--editor-model <model>", "Cheap model for file edits (architect/editor split)").option("--weak-model <model>", "Cheap model for background tasks (compaction, summaries)").option("-p, --path <path>", "Project path", process.cwd()).option("-P, --print [task]", "Headless mode: run task and print output (no interactive UI)").option("--output <format>", "Output format for headless mode (text or json)", "text").option("--setup", "Run setup wizard before starting").action(
57009
57345
  async (options) => {
57010
57346
  if (options.setup) {
57011
57347
  const result = await runOnboardingV2();
@@ -57040,7 +57376,9 @@ program.command("chat", { isDefault: true }).description("Start interactive chat
57040
57376
  provider: {
57041
57377
  type: providerType,
57042
57378
  model: options.model ?? "",
57043
- maxTokens: 8192
57379
+ maxTokens: 8192,
57380
+ editorModel: options.editorModel,
57381
+ weakModel: options.weakModel
57044
57382
  }
57045
57383
  }
57046
57384
  });