jinzd-ai-cli 0.4.61 → 0.4.62

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.
@@ -10,7 +10,7 @@ import {
10
10
  SUBAGENT_DEFAULT_MAX_ROUNDS,
11
11
  SUBAGENT_MAX_ROUNDS_LIMIT,
12
12
  runTestsTool
13
- } from "./chunk-GA74LZ62.js";
13
+ } from "./chunk-LLI6COMK.js";
14
14
 
15
15
  // src/tools/builtin/bash.ts
16
16
  import { execSync } from "child_process";
@@ -4062,6 +4062,28 @@ var notebookEditTool = {
4062
4062
  }
4063
4063
  };
4064
4064
 
4065
+ // src/core/token-estimator.ts
4066
+ var CJK_REGEX = /[\u2E80-\u9FFF\uA000-\uA4FF\uAC00-\uD7FF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF]/g;
4067
+ function estimateTokens(text) {
4068
+ if (!text) return 0;
4069
+ const cjkMatches = text.match(CJK_REGEX);
4070
+ const cjkCount = cjkMatches ? cjkMatches.length : 0;
4071
+ const nonCjkCount = text.length - cjkCount;
4072
+ const tokens = cjkCount * 1.5 + nonCjkCount * 0.25;
4073
+ return Math.ceil(tokens) + 4;
4074
+ }
4075
+ function estimateToolDefinitionTokens(def) {
4076
+ let charCount = def.name.length + def.description.length;
4077
+ for (const [key, param] of Object.entries(def.parameters)) {
4078
+ charCount += key.length + param.type.length + param.description.length;
4079
+ if (param.enum) {
4080
+ charCount += param.enum.join(" ").length;
4081
+ }
4082
+ }
4083
+ const structuralOverhead = 40 + Object.keys(def.parameters).length * 30;
4084
+ return Math.ceil((charCount + structuralOverhead) * 0.25) + 10;
4085
+ }
4086
+
4065
4087
  // src/tools/registry.ts
4066
4088
  import { pathToFileURL } from "url";
4067
4089
  import { existsSync as existsSync12, mkdirSync as mkdirSync4, readdirSync as readdirSync6 } from "fs";
@@ -4110,6 +4132,74 @@ var ToolRegistry = class {
4110
4132
  getDefinitions() {
4111
4133
  return [...this.tools.values()].map((t) => t.definition);
4112
4134
  }
4135
+ /**
4136
+ * Return tool definitions within a token budget, trimming MCP tools if needed.
4137
+ *
4138
+ * Strategy: always include all builtin + plugin tools. If total tokens exceed
4139
+ * the budget, keep only MCP tools that were used in this session (by name),
4140
+ * trimming the rest. Returns { definitions, trimmedCount, systemNote }.
4141
+ *
4142
+ * @param tokenBudget Max tokens for tool definitions (typically 20% of context window)
4143
+ * @param usedToolNames Names of MCP tools already called this session (always kept)
4144
+ */
4145
+ getDefinitionsWithBudget(tokenBudget, usedToolNames) {
4146
+ const allDefs = this.getDefinitions();
4147
+ let totalTokens = 0;
4148
+ for (const def of allDefs) {
4149
+ totalTokens += estimateToolDefinitionTokens(def);
4150
+ }
4151
+ if (totalTokens <= tokenBudget) {
4152
+ return { definitions: allDefs, trimmedCount: 0, systemNote: null };
4153
+ }
4154
+ const builtinDefs = [];
4155
+ const mcpDefs = [];
4156
+ for (const def of allDefs) {
4157
+ if (this.mcpToolNames.has(def.name)) {
4158
+ mcpDefs.push(def);
4159
+ } else {
4160
+ builtinDefs.push(def);
4161
+ }
4162
+ }
4163
+ let budget = tokenBudget;
4164
+ for (const def of builtinDefs) {
4165
+ budget -= estimateToolDefinitionTokens(def);
4166
+ }
4167
+ const kept = [...builtinDefs];
4168
+ const used = usedToolNames ?? /* @__PURE__ */ new Set();
4169
+ const remaining = [];
4170
+ for (const def of mcpDefs) {
4171
+ if (used.has(def.name)) {
4172
+ const cost = estimateToolDefinitionTokens(def);
4173
+ budget -= cost;
4174
+ kept.push(def);
4175
+ } else {
4176
+ remaining.push(def);
4177
+ }
4178
+ }
4179
+ let trimmed = 0;
4180
+ for (const def of remaining) {
4181
+ const cost = estimateToolDefinitionTokens(def);
4182
+ if (budget >= cost) {
4183
+ budget -= cost;
4184
+ kept.push(def);
4185
+ } else {
4186
+ trimmed++;
4187
+ }
4188
+ }
4189
+ let systemNote = null;
4190
+ if (trimmed > 0) {
4191
+ const trimmedServers = /* @__PURE__ */ new Set();
4192
+ const keptNames = new Set(kept.map((d) => d.name));
4193
+ for (const def of mcpDefs) {
4194
+ if (!keptNames.has(def.name)) {
4195
+ const parts = def.name.split("__");
4196
+ if (parts.length >= 2) trimmedServers.add(parts[1]);
4197
+ }
4198
+ }
4199
+ systemNote = `[MCP Tool Budget] ${trimmed} MCP tool(s) from server(s) [${[...trimmedServers].join(", ")}] were excluded to fit the context window. If you need a specific MCP tool that isn't listed, tell the user to ask for it explicitly.`;
4200
+ }
4201
+ return { definitions: kept, trimmedCount: trimmed, systemNote };
4202
+ }
4113
4203
  listAll() {
4114
4204
  return [...this.tools.values()];
4115
4205
  }
@@ -4229,5 +4319,6 @@ export {
4229
4319
  askUserContext,
4230
4320
  googleSearchContext,
4231
4321
  spawnAgentContext,
4322
+ estimateTokens,
4232
4323
  ToolRegistry
4233
4324
  };
@@ -8,7 +8,7 @@ import {
8
8
  RateLimitError,
9
9
  schemaToJsonSchema,
10
10
  truncateForPersist
11
- } from "./chunk-2ZCD5F4X.js";
11
+ } from "./chunk-672OV76Z.js";
12
12
  import {
13
13
  APP_NAME,
14
14
  CONFIG_DIR_NAME,
@@ -21,7 +21,7 @@ import {
21
21
  MCP_TOOL_PREFIX,
22
22
  PLUGINS_DIR_NAME,
23
23
  VERSION
24
- } from "./chunk-GA74LZ62.js";
24
+ } from "./chunk-LLI6COMK.js";
25
25
 
26
26
  // src/config/config-manager.ts
27
27
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -2462,14 +2462,25 @@ var Session = class _Session {
2462
2462
  /**
2463
2463
  * 上下文压缩:用摘要消息替换旧消息,保留最近 keepLast 条。
2464
2464
  *
2465
+ * Tool-history-aware: if the cut point lands inside a tool round
2466
+ * (assistant+toolCalls followed by tool results), expand to keep the
2467
+ * entire round intact. This prevents orphaned tool results.
2468
+ *
2465
2469
  * 压缩后消息结构:
2466
- * [summaryMsg(user), ackMsg(assistant), ...最近 keepLast 条原始消息]
2470
+ * [summaryMsg(user), ackMsg(assistant), ...最近 N 条原始消息]
2467
2471
  *
2468
2472
  * @returns 被删除的消息条数
2469
2473
  */
2470
2474
  compact(summaryMsg, ackMsg, keepLast) {
2471
- const preserved = this.messages.slice(-keepLast);
2472
- const removedCount = this.messages.length - preserved.length;
2475
+ let cutIndex = this.messages.length - keepLast;
2476
+ if (cutIndex <= 0) {
2477
+ return 0;
2478
+ }
2479
+ while (cutIndex > 0 && this.messages[cutIndex]?.role === "tool") {
2480
+ cutIndex--;
2481
+ }
2482
+ const preserved = this.messages.slice(cutIndex);
2483
+ const removedCount = cutIndex;
2473
2484
  this.messages = [summaryMsg, ackMsg, ...preserved];
2474
2485
  this.updated = /* @__PURE__ */ new Date();
2475
2486
  return removedCount;
@@ -3687,6 +3698,7 @@ function formatCost(amount) {
3687
3698
  }
3688
3699
 
3689
3700
  // src/session/tool-history.ts
3701
+ var SESSION_SIZE_LIMIT = 2 * 1024 * 1024;
3690
3702
  function persistToolRound(session, toolCalls, toolResults, opts) {
3691
3703
  session.addMessage({
3692
3704
  role: "assistant",
@@ -3757,16 +3769,51 @@ function rebuildExtraMessages(provider, toolHistory) {
3757
3769
  }
3758
3770
  return result;
3759
3771
  }
3760
-
3761
- // src/core/token-estimator.ts
3762
- var CJK_REGEX = /[\u2E80-\u9FFF\uA000-\uA4FF\uAC00-\uD7FF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF]/g;
3763
- function estimateTokens(text) {
3764
- if (!text) return 0;
3765
- const cjkMatches = text.match(CJK_REGEX);
3766
- const cjkCount = cjkMatches ? cjkMatches.length : 0;
3767
- const nonCjkCount = text.length - cjkCount;
3768
- const tokens = cjkCount * 1.5 + nonCjkCount * 0.25;
3769
- return Math.ceil(tokens) + 4;
3772
+ function trimOldToolOutput(messages, keepRecentRounds = 10) {
3773
+ const roundStarts = [];
3774
+ for (let i = 0; i < messages.length; i++) {
3775
+ if (messages[i].role === "assistant" && messages[i].toolCalls?.length) {
3776
+ roundStarts.push(i);
3777
+ }
3778
+ }
3779
+ if (roundStarts.length <= keepRecentRounds) return 0;
3780
+ const cutoffRoundIdx = roundStarts.length - keepRecentRounds;
3781
+ let trimmed = 0;
3782
+ for (let r = 0; r < cutoffRoundIdx; r++) {
3783
+ const start = roundStarts[r];
3784
+ const end = r + 1 < roundStarts.length ? roundStarts[r + 1] : messages.length;
3785
+ const assistantMsg = messages[start];
3786
+ if (typeof assistantMsg.content === "string" && assistantMsg.content.length > 200) {
3787
+ assistantMsg.content = assistantMsg.content.slice(0, 100) + "\u2026 [trimmed for size]";
3788
+ trimmed++;
3789
+ }
3790
+ for (let j = start + 1; j < end; j++) {
3791
+ const msg = messages[j];
3792
+ if (msg.role === "tool") {
3793
+ const status = msg.isError ? "\u2717 error" : "\u2713 ok";
3794
+ const name = msg.toolName ?? "unknown";
3795
+ const currentContent = typeof msg.content === "string" ? msg.content : "";
3796
+ if (currentContent.length > 200) {
3797
+ msg.content = `[${name}: ${status}] (output trimmed for size \u2014 ${currentContent.length} chars)`;
3798
+ trimmed++;
3799
+ }
3800
+ }
3801
+ }
3802
+ }
3803
+ return trimmed;
3804
+ }
3805
+ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
3806
+ const json = JSON.stringify(session.toJSON());
3807
+ if (json.length <= sizeLimit) return false;
3808
+ let keepRecent = 10;
3809
+ while (keepRecent >= 2) {
3810
+ const trimmed = trimOldToolOutput(session.messages, keepRecent);
3811
+ if (trimmed === 0) break;
3812
+ const newSize = JSON.stringify(session.toJSON()).length;
3813
+ if (newSize <= sizeLimit) return true;
3814
+ keepRecent = Math.max(2, Math.floor(keepRecent / 2));
3815
+ }
3816
+ return true;
3770
3817
  }
3771
3818
 
3772
3819
  // src/repl/dev-state.ts
@@ -3882,7 +3929,7 @@ export {
3882
3929
  persistToolRound,
3883
3930
  extractToolHistory,
3884
3931
  rebuildExtraMessages,
3885
- estimateTokens,
3932
+ autoTrimSessionIfNeeded,
3886
3933
  SNAPSHOT_PROMPT,
3887
3934
  sessionHasMeaningfulContent,
3888
3935
  saveDevState,
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.4.61";
11
+ var VERSION = "0.4.62";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
@@ -6,7 +6,7 @@ import { platform } from "os";
6
6
  import chalk from "chalk";
7
7
 
8
8
  // src/core/constants.ts
9
- var VERSION = "0.4.61";
9
+ var VERSION = "0.4.62";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -385,7 +385,7 @@ ${content}`);
385
385
  }
386
386
  }
387
387
  async function runTaskMode(config, providers, configManager, topic) {
388
- const { TaskOrchestrator } = await import("./task-orchestrator-W66JWWKF.js");
388
+ const { TaskOrchestrator } = await import("./task-orchestrator-2ATTBG2S.js");
389
389
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
390
390
  let interrupted = false;
391
391
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -8,12 +8,12 @@ import {
8
8
  SessionManager,
9
9
  SkillManager,
10
10
  TOOL_CALL_REMINDER,
11
+ autoTrimSessionIfNeeded,
11
12
  buildPhantomCorrectionMessage,
12
13
  buildWriteRoundReminder,
13
14
  clearDevState,
14
15
  computeCost,
15
16
  detectsHallucinatedFileOp,
16
- estimateTokens,
17
17
  extractToolHistory,
18
18
  extractWrittenFilePaths,
19
19
  findPhantomClaims,
@@ -31,11 +31,12 @@ import {
31
31
  saveDevState,
32
32
  sessionHasMeaningfulContent,
33
33
  setupProxy
34
- } from "./chunk-2WGVM2J2.js";
34
+ } from "./chunk-6OTF2ILP.js";
35
35
  import {
36
36
  ToolExecutor,
37
37
  ToolRegistry,
38
38
  askUserContext,
39
+ estimateTokens,
39
40
  googleSearchContext,
40
41
  initTheme,
41
42
  lastResponseStore,
@@ -45,7 +46,7 @@ import {
45
46
  spawnAgentContext,
46
47
  theme,
47
48
  undoStack
48
- } from "./chunk-2ZCD5F4X.js";
49
+ } from "./chunk-672OV76Z.js";
49
50
  import {
50
51
  fileCheckpoints
51
52
  } from "./chunk-4BKXL7SM.js";
@@ -70,7 +71,7 @@ import {
70
71
  SKILLS_DIR_NAME,
71
72
  VERSION,
72
73
  buildUserIdentityPrompt
73
- } from "./chunk-GA74LZ62.js";
74
+ } from "./chunk-LLI6COMK.js";
74
75
 
75
76
  // src/index.ts
76
77
  import { program } from "commander";
@@ -2165,7 +2166,7 @@ ${hint}` : "")
2165
2166
  usage: "/test [command|filter]",
2166
2167
  async execute(args, ctx) {
2167
2168
  try {
2168
- const { executeTests } = await import("./run-tests-CT4PRGGP.js");
2169
+ const { executeTests } = await import("./run-tests-ORVJAUJG.js");
2169
2170
  const argStr = args.join(" ").trim();
2170
2171
  let testArgs = {};
2171
2172
  if (argStr) {
@@ -3911,6 +3912,11 @@ ${skillContent}`);
3911
3912
  "4. Current status and tasks not yet completed",
3912
3913
  "5. Key details to remember (file paths, config values, error messages, variable names, etc.)",
3913
3914
  "",
3915
+ "If the conversation contains tool call rounds (assistant calling tools like read_file, write_file, bash, etc.), ",
3916
+ 'summarize each round as a single line: "- [tool_name] target \u2192 result/status". ',
3917
+ 'Do NOT include raw tool output. Group consecutive similar operations (e.g., "read 5 files in src/") ',
3918
+ "instead of listing each one individually.",
3919
+ "",
3914
3920
  "Requirements: Be well-organized so that subsequent conversation can seamlessly continue from this summary."
3915
3921
  ];
3916
3922
  if (instruction) summaryPromptLines.push("", `Additional requirement: ${instruction}`);
@@ -4318,6 +4324,9 @@ Session '${this.resumeSessionId}' not found.
4318
4324
  await this.handleChatSimple(provider, session.messages);
4319
4325
  }
4320
4326
  if (this.config.get("session").autoSave) {
4327
+ if (autoTrimSessionIfNeeded(session)) {
4328
+ process.stderr.write(theme.dim(" [session] Trimmed old tool output to reduce file size\n"));
4329
+ }
4321
4330
  await this.sessions.save();
4322
4331
  }
4323
4332
  const elapsed = Date.now() - t0;
@@ -4862,6 +4871,8 @@ Session '${this.resumeSessionId}' not found.
4862
4871
  async handleChatWithTools(provider, messages) {
4863
4872
  const session = this.sessions.current;
4864
4873
  let toolDefs;
4874
+ let mcpBudgetNote = null;
4875
+ const usedMcpToolNames = /* @__PURE__ */ new Set();
4865
4876
  if (this.planMode) {
4866
4877
  toolDefs = this.toolRegistry.getDefinitions().filter((t) => PLAN_MODE_READONLY_TOOLS.has(t.name));
4867
4878
  } else {
@@ -4869,7 +4880,19 @@ Session '${this.resumeSessionId}' not found.
4869
4880
  if (skillFilter) {
4870
4881
  toolDefs = this.toolRegistry.getDefinitions().filter((t) => skillFilter.has(t.name));
4871
4882
  } else {
4872
- toolDefs = this.toolRegistry.getDefinitions();
4883
+ const contextWindow = this.getContextWindowSize();
4884
+ if (contextWindow > 0) {
4885
+ const toolBudget = Math.floor(contextWindow * 0.2);
4886
+ const { definitions, trimmedCount, systemNote } = this.toolRegistry.getDefinitionsWithBudget(toolBudget, usedMcpToolNames);
4887
+ toolDefs = definitions;
4888
+ mcpBudgetNote = systemNote;
4889
+ if (trimmedCount > 0) {
4890
+ process.stderr.write(theme.dim(` [MCP budget] ${trimmedCount} MCP tool(s) trimmed to fit ${toolBudget.toLocaleString()} token budget
4891
+ `));
4892
+ }
4893
+ } else {
4894
+ toolDefs = this.toolRegistry.getDefinitions();
4895
+ }
4873
4896
  }
4874
4897
  }
4875
4898
  if (this.allowedTools) {
@@ -4904,7 +4927,9 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
4904
4927
  - Do NOT read the same file more than once \u2014 use the content from previous reads.
4905
4928
  - Prioritize the most critical tasks first in case rounds run out.
4906
4929
  - When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
4907
- const systemPrompt = baseSystemPrompt + roundBudgetHint;
4930
+ const systemPrompt = baseSystemPrompt + roundBudgetHint + (mcpBudgetNote ? `
4931
+
4932
+ ${mcpBudgetNote}` : "");
4908
4933
  const modelParams = this.getModelParams();
4909
4934
  const useStreaming = this.config.get("ui").streaming;
4910
4935
  const spinner = this.renderer.showSpinner("Thinking...");
@@ -5270,6 +5295,9 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
5270
5295
  const reasoningContent = "reasoningContent" in result ? result.reasoningContent : void 0;
5271
5296
  const newMsgs = provider.buildToolResultMessages(result.toolCalls, toolResults, reasoningContent);
5272
5297
  extraMessages.push(...newMsgs);
5298
+ for (const tc of result.toolCalls) {
5299
+ if (tc.name.startsWith("mcp__")) usedMcpToolNames.add(tc.name);
5300
+ }
5273
5301
  const streamedContent = "content" in result ? result.content : void 0;
5274
5302
  persistToolRound(session, result.toolCalls, toolResults, {
5275
5303
  assistantContent: streamedContent,
@@ -5710,7 +5738,7 @@ program.command("web").description("Start Web UI server with browser-based chat
5710
5738
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5711
5739
  process.exit(1);
5712
5740
  }
5713
- const { startWebServer } = await import("./server-YJWIPDF2.js");
5741
+ const { startWebServer } = await import("./server-HBAOUOIC.js");
5714
5742
  await startWebServer({ port, host: options.host });
5715
5743
  });
5716
5744
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -5943,7 +5971,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
5943
5971
  }),
5944
5972
  config.get("customProviders")
5945
5973
  );
5946
- const { startHub } = await import("./hub-WA2DZCSQ.js");
5974
+ const { startHub } = await import("./hub-2XLQSZY2.js");
5947
5975
  await startHub(
5948
5976
  {
5949
5977
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-QUD2AVHH.js";
4
+ } from "./chunk-YUUCUJHU.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-GA74LZ62.js";
5
+ } from "./chunk-LLI6COMK.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -7,9 +7,9 @@ import {
7
7
  SessionManager,
8
8
  SkillManager,
9
9
  TOOL_CALL_REMINDER,
10
+ autoTrimSessionIfNeeded,
10
11
  computeCost,
11
12
  detectsHallucinatedFileOp,
12
- estimateTokens,
13
13
  extractToolHistory,
14
14
  formatCost,
15
15
  formatGitContextForPrompt,
@@ -21,7 +21,7 @@ import {
21
21
  persistToolRound,
22
22
  rebuildExtraMessages,
23
23
  setupProxy
24
- } from "./chunk-2WGVM2J2.js";
24
+ } from "./chunk-6OTF2ILP.js";
25
25
  import {
26
26
  AuthManager
27
27
  } from "./chunk-BYNY5JPB.js";
@@ -30,6 +30,7 @@ import {
30
30
  ToolRegistry,
31
31
  askUserContext,
32
32
  checkPermission,
33
+ estimateTokens,
33
34
  getDangerLevel,
34
35
  googleSearchContext,
35
36
  isFileWriteTool,
@@ -40,7 +41,7 @@ import {
40
41
  spawnAgentContext,
41
42
  truncateOutput,
42
43
  undoStack
43
- } from "./chunk-2ZCD5F4X.js";
44
+ } from "./chunk-672OV76Z.js";
44
45
  import "./chunk-4BKXL7SM.js";
45
46
  import {
46
47
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -60,7 +61,7 @@ import {
60
61
  SKILLS_DIR_NAME,
61
62
  VERSION,
62
63
  buildUserIdentityPrompt
63
- } from "./chunk-GA74LZ62.js";
64
+ } from "./chunk-LLI6COMK.js";
64
65
 
65
66
  // src/web/server.ts
66
67
  import express from "express";
@@ -697,6 +698,7 @@ var SessionHandler = class _SessionHandler {
697
698
  /** Save session only if it exists and has messages (never persist empty "Untitled" sessions). */
698
699
  saveIfNeeded() {
699
700
  if (this.sessions.current && this.sessions.current.messages.length > 0) {
701
+ autoTrimSessionIfNeeded(this.sessions.current);
700
702
  const id = this.sessions.current.id;
701
703
  this.sessions.save();
702
704
  this.unsavedSessions.delete(id);
@@ -754,9 +756,9 @@ var SessionHandler = class _SessionHandler {
754
756
  return;
755
757
  }
756
758
  const hasToolSupport = typeof provider.chatWithTools === "function";
757
- const toolDefs = hasToolSupport ? this.getFilteredToolDefs() : [];
759
+ const { toolDefs, mcpBudgetNote } = hasToolSupport ? this.getFilteredToolDefs() : { toolDefs: [], mcpBudgetNote: null };
758
760
  if (hasToolSupport && toolDefs.length > 0) {
759
- await this.handleChatWithTools(provider, session.messages, toolDefs);
761
+ await this.handleChatWithTools(provider, session.messages, toolDefs, mcpBudgetNote);
760
762
  } else {
761
763
  await this.handleChatSimple(provider, session.messages);
762
764
  }
@@ -821,7 +823,7 @@ var SessionHandler = class _SessionHandler {
821
823
  this.abortController = null;
822
824
  }
823
825
  }
824
- async handleChatWithTools(provider, messages, toolDefs) {
826
+ async handleChatWithTools(provider, messages, toolDefs, mcpBudgetNote) {
825
827
  const session = this.sessions.current;
826
828
  const { baseMessages: cleanMessages, toolHistory } = extractToolHistory(messages);
827
829
  const apiMessages = [...cleanMessages];
@@ -839,7 +841,9 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
839
841
  - Prefer batch operations (e.g. global find-and-replace) over repetitive single edits.
840
842
  - Prioritize the most critical tasks first in case rounds run out.
841
843
  - When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
842
- const systemPrompt = baseSystemPrompt + roundBudgetHint;
844
+ const systemPrompt = baseSystemPrompt + roundBudgetHint + (mcpBudgetNote ? `
845
+
846
+ ${mcpBudgetNote}` : "");
843
847
  const modelParams = this.getModelParams();
844
848
  const roundUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
845
849
  const supportsStreamingTools = typeof provider.chatWithToolsStream === "function";
@@ -1929,7 +1933,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1929
1933
  case "test": {
1930
1934
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1931
1935
  try {
1932
- const { executeTests } = await import("./run-tests-CT4PRGGP.js");
1936
+ const { executeTests } = await import("./run-tests-ORVJAUJG.js");
1933
1937
  const argStr = args.join(" ").trim();
1934
1938
  let testArgs = {};
1935
1939
  if (argStr) {
@@ -2517,16 +2521,26 @@ Add .md files to create commands.` });
2517
2521
  };
2518
2522
  }
2519
2523
  getFilteredToolDefs() {
2520
- let defs = this.toolRegistry.getDefinitions();
2521
2524
  if (this.planMode) {
2522
- defs = defs.filter((t) => PLAN_MODE_READONLY_TOOLS.has(t.name));
2523
- } else {
2524
- const skillFilter = this.skillManager?.getActiveToolFilter();
2525
- if (skillFilter) {
2526
- defs = defs.filter((t) => skillFilter.has(t.name));
2527
- }
2525
+ return {
2526
+ toolDefs: this.toolRegistry.getDefinitions().filter((t) => PLAN_MODE_READONLY_TOOLS.has(t.name)),
2527
+ mcpBudgetNote: null
2528
+ };
2529
+ }
2530
+ const skillFilter = this.skillManager?.getActiveToolFilter();
2531
+ if (skillFilter) {
2532
+ return {
2533
+ toolDefs: this.toolRegistry.getDefinitions().filter((t) => skillFilter.has(t.name)),
2534
+ mcpBudgetNote: null
2535
+ };
2536
+ }
2537
+ const contextWindow = this.getContextWindowSize();
2538
+ if (contextWindow > 0) {
2539
+ const toolBudget = Math.floor(contextWindow * 0.2);
2540
+ const { definitions, systemNote } = this.toolRegistry.getDefinitionsWithBudget(toolBudget);
2541
+ return { toolDefs: definitions, mcpBudgetNote: systemNote };
2528
2542
  }
2529
- return defs;
2543
+ return { toolDefs: this.toolRegistry.getDefinitions(), mcpBudgetNote: null };
2530
2544
  }
2531
2545
  /**
2532
2546
  * Find first matching context file in a directory.
@@ -4,11 +4,11 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-2ZCD5F4X.js";
7
+ } from "./chunk-672OV76Z.js";
8
8
  import "./chunk-4BKXL7SM.js";
9
9
  import {
10
10
  SUBAGENT_ALLOWED_TOOLS
11
- } from "./chunk-GA74LZ62.js";
11
+ } from "./chunk-LLI6COMK.js";
12
12
 
13
13
  // src/hub/task-orchestrator.ts
14
14
  import { createInterface } from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.61",
3
+ "version": "0.4.62",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",