jinzd-ai-cli 0.4.60 → 0.4.61

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.
@@ -8,7 +8,7 @@ import {
8
8
  RateLimitError,
9
9
  schemaToJsonSchema,
10
10
  truncateForPersist
11
- } from "./chunk-C32FFHMY.js";
11
+ } from "./chunk-2ZCD5F4X.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-2DWWB4KD.js";
24
+ } from "./chunk-GA74LZ62.js";
25
25
 
26
26
  // src/config/config-manager.ts
27
27
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -3758,6 +3758,17 @@ function rebuildExtraMessages(provider, toolHistory) {
3758
3758
  return result;
3759
3759
  }
3760
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;
3770
+ }
3771
+
3761
3772
  // src/repl/dev-state.ts
3762
3773
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "fs";
3763
3774
  import { join as join5 } from "path";
@@ -3871,6 +3882,7 @@ export {
3871
3882
  persistToolRound,
3872
3883
  extractToolHistory,
3873
3884
  rebuildExtraMessages,
3885
+ estimateTokens,
3874
3886
  SNAPSHOT_PROMPT,
3875
3887
  sessionHasMeaningfulContent,
3876
3888
  saveDevState,
@@ -10,7 +10,7 @@ import {
10
10
  SUBAGENT_DEFAULT_MAX_ROUNDS,
11
11
  SUBAGENT_MAX_ROUNDS_LIMIT,
12
12
  runTestsTool
13
- } from "./chunk-2DWWB4KD.js";
13
+ } from "./chunk-GA74LZ62.js";
14
14
 
15
15
  // src/tools/builtin/bash.ts
16
16
  import { execSync } from "child_process";
@@ -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.60";
11
+ var VERSION = "0.4.61";
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.60";
9
+ var VERSION = "0.4.61";
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-Z4IK3UEA.js");
388
+ const { TaskOrchestrator } = await import("./task-orchestrator-W66JWWKF.js");
389
389
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
390
390
  let interrupted = false;
391
391
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  clearDevState,
14
14
  computeCost,
15
15
  detectsHallucinatedFileOp,
16
+ estimateTokens,
16
17
  extractToolHistory,
17
18
  extractWrittenFilePaths,
18
19
  findPhantomClaims,
@@ -30,7 +31,7 @@ import {
30
31
  saveDevState,
31
32
  sessionHasMeaningfulContent,
32
33
  setupProxy
33
- } from "./chunk-3YVHYAXK.js";
34
+ } from "./chunk-2WGVM2J2.js";
34
35
  import {
35
36
  ToolExecutor,
36
37
  ToolRegistry,
@@ -44,7 +45,7 @@ import {
44
45
  spawnAgentContext,
45
46
  theme,
46
47
  undoStack
47
- } from "./chunk-C32FFHMY.js";
48
+ } from "./chunk-2ZCD5F4X.js";
48
49
  import {
49
50
  fileCheckpoints
50
51
  } from "./chunk-4BKXL7SM.js";
@@ -69,7 +70,7 @@ import {
69
70
  SKILLS_DIR_NAME,
70
71
  VERSION,
71
72
  buildUserIdentityPrompt
72
- } from "./chunk-2DWWB4KD.js";
73
+ } from "./chunk-GA74LZ62.js";
73
74
 
74
75
  // src/index.ts
75
76
  import { program } from "commander";
@@ -2164,7 +2165,7 @@ ${hint}` : "")
2164
2165
  usage: "/test [command|filter]",
2165
2166
  async execute(args, ctx) {
2166
2167
  try {
2167
- const { executeTests } = await import("./run-tests-QGJHXL5Z.js");
2168
+ const { executeTests } = await import("./run-tests-CT4PRGGP.js");
2168
2169
  const argStr = args.join(" ").trim();
2169
2170
  let testArgs = {};
2170
2171
  if (argStr) {
@@ -4399,7 +4400,7 @@ Session '${this.resumeSessionId}' not found.
4399
4400
  * 混合 CJK / ASCII 文本平均约 2.5 字符 = 1 token(与 renderer.ts 中的估算公式一致)。
4400
4401
  */
4401
4402
  estimateTokens(text) {
4402
- return Math.ceil(text.length / 2.5);
4403
+ return estimateTokens(text);
4403
4404
  }
4404
4405
  /**
4405
4406
  * 估算当前对话的总 token 消耗(system prompt + 所有 session messages)。
@@ -4418,6 +4419,11 @@ Session '${this.resumeSessionId}' not found.
4418
4419
  if (part.type === "text" && part.text) total += this.estimateTokens(part.text);
4419
4420
  }
4420
4421
  }
4422
+ if (msg.toolCalls) {
4423
+ for (const tc of msg.toolCalls) {
4424
+ total += this.estimateTokens(JSON.stringify(tc.arguments));
4425
+ }
4426
+ }
4421
4427
  }
4422
4428
  }
4423
4429
  return total;
@@ -5704,7 +5710,7 @@ program.command("web").description("Start Web UI server with browser-based chat
5704
5710
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5705
5711
  process.exit(1);
5706
5712
  }
5707
- const { startWebServer } = await import("./server-L2XJYXMB.js");
5713
+ const { startWebServer } = await import("./server-YJWIPDF2.js");
5708
5714
  await startWebServer({ port, host: options.host });
5709
5715
  });
5710
5716
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -5937,7 +5943,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
5937
5943
  }),
5938
5944
  config.get("customProviders")
5939
5945
  );
5940
- const { startHub } = await import("./hub-JTMNY7JR.js");
5946
+ const { startHub } = await import("./hub-WA2DZCSQ.js");
5941
5947
  await startHub(
5942
5948
  {
5943
5949
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-X4GL6D5L.js";
4
+ } from "./chunk-QUD2AVHH.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-2DWWB4KD.js";
5
+ } from "./chunk-GA74LZ62.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -9,6 +9,7 @@ import {
9
9
  TOOL_CALL_REMINDER,
10
10
  computeCost,
11
11
  detectsHallucinatedFileOp,
12
+ estimateTokens,
12
13
  extractToolHistory,
13
14
  formatCost,
14
15
  formatGitContextForPrompt,
@@ -20,7 +21,7 @@ import {
20
21
  persistToolRound,
21
22
  rebuildExtraMessages,
22
23
  setupProxy
23
- } from "./chunk-3YVHYAXK.js";
24
+ } from "./chunk-2WGVM2J2.js";
24
25
  import {
25
26
  AuthManager
26
27
  } from "./chunk-BYNY5JPB.js";
@@ -39,7 +40,7 @@ import {
39
40
  spawnAgentContext,
40
41
  truncateOutput,
41
42
  undoStack
42
- } from "./chunk-C32FFHMY.js";
43
+ } from "./chunk-2ZCD5F4X.js";
43
44
  import "./chunk-4BKXL7SM.js";
44
45
  import {
45
46
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -59,7 +60,7 @@ import {
59
60
  SKILLS_DIR_NAME,
60
61
  VERSION,
61
62
  buildUserIdentityPrompt
62
- } from "./chunk-2DWWB4KD.js";
63
+ } from "./chunk-GA74LZ62.js";
63
64
 
64
65
  // src/web/server.ts
65
66
  import express from "express";
@@ -657,7 +658,7 @@ var SessionHandler = class _SessionHandler {
657
658
  }
658
659
  /** 粗略估算文本 token 数(2.5 chars/token)*/
659
660
  estTokens(text) {
660
- return Math.ceil(text.length / 2.5);
661
+ return estimateTokens(text);
661
662
  }
662
663
  /**
663
664
  * 估算当前 agentic 请求总 token 数(session messages + extraMessages + system prompt)。
@@ -678,6 +679,11 @@ var SessionHandler = class _SessionHandler {
678
679
  }
679
680
  }
680
681
  }
682
+ if (msg.toolCalls) {
683
+ for (const tc of msg.toolCalls) {
684
+ total += this.estTokens(JSON.stringify(tc.arguments));
685
+ }
686
+ }
681
687
  }
682
688
  }
683
689
  if (extraMessages.length > 0) {
@@ -1923,7 +1929,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1923
1929
  case "test": {
1924
1930
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1925
1931
  try {
1926
- const { executeTests } = await import("./run-tests-QGJHXL5Z.js");
1932
+ const { executeTests } = await import("./run-tests-CT4PRGGP.js");
1927
1933
  const argStr = args.join(" ").trim();
1928
1934
  let testArgs = {};
1929
1935
  if (argStr) {
@@ -2440,11 +2446,18 @@ Add .md files to create commands.` });
2440
2446
  sendSessionMessages() {
2441
2447
  const session = this.sessions.current;
2442
2448
  if (!session) return;
2443
- const messages = session.messages.map((m) => ({
2444
- role: m.role,
2445
- content: getContentText(m.content),
2446
- timestamp: m.timestamp?.toISOString()
2447
- }));
2449
+ const messages = session.messages.map((m) => {
2450
+ const out = {
2451
+ role: m.role,
2452
+ content: getContentText(m.content),
2453
+ timestamp: m.timestamp?.toISOString()
2454
+ };
2455
+ if (m.toolCalls) out.toolCalls = m.toolCalls;
2456
+ if (m.toolCallId) out.toolCallId = m.toolCallId;
2457
+ if (m.toolName) out.toolName = m.toolName;
2458
+ if (m.isError !== void 0) out.isError = m.isError;
2459
+ return out;
2460
+ });
2448
2461
  this.send({
2449
2462
  type: "session_messages",
2450
2463
  sessionId: session.id,
@@ -4,11 +4,11 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-C32FFHMY.js";
7
+ } from "./chunk-2ZCD5F4X.js";
8
8
  import "./chunk-4BKXL7SM.js";
9
9
  import {
10
10
  SUBAGENT_ALLOWED_TOOLS
11
- } from "./chunk-2DWWB4KD.js";
11
+ } from "./chunk-GA74LZ62.js";
12
12
 
13
13
  // src/hub/task-orchestrator.ts
14
14
  import { createInterface } from "readline";
@@ -717,6 +717,49 @@ function escapeHtml(str) {
717
717
  return div.innerHTML;
718
718
  }
719
719
 
720
+ /**
721
+ * Create a static tool-call card for session history view.
722
+ * Groups an assistant tool-call message with its subsequent tool results.
723
+ * Unlike live tool cards, these have no timer — just the final state.
724
+ *
725
+ * @param {Object} assistantMsg - The assistant message with toolCalls array
726
+ * @param {Object[]} resultMsgs - Subsequent tool-result messages
727
+ */
728
+ function createHistoryToolCards(assistantMsg, resultMsgs) {
729
+ const toolCalls = assistantMsg.toolCalls || [];
730
+ for (let i = 0; i < toolCalls.length; i++) {
731
+ const tc = toolCalls[i];
732
+ const rm = resultMsgs[i]; // may be undefined if results are missing
733
+ const isError = rm ? rm.isError : false;
734
+ const resultContent = rm ? rm.content : '';
735
+
736
+ const el = document.createElement('details');
737
+ el.className = 'tool-card tool-border-safe my-1';
738
+
739
+ const statusIcon = rm ? (isError ? '✗' : '✓') : '?';
740
+ const statusClass = rm ? (isError ? 'text-error' : 'text-success') : 'opacity-50';
741
+ const badgeClass = isError ? 'badge-error' : 'badge-info';
742
+ const levelIcon = isError ? '⚠' : '⚙';
743
+
744
+ const argsHtml = tc.arguments ? formatToolArgs(tc.arguments) : '';
745
+ const truncResult = resultContent.length > 500
746
+ ? resultContent.slice(0, 500) + '...'
747
+ : resultContent;
748
+
749
+ el.innerHTML = `
750
+ <summary class="flex items-center gap-2 w-full cursor-pointer select-none py-1">
751
+ <span class="badge ${badgeClass} badge-sm gap-1">${levelIcon} ${escapeHtml(tc.name)}</span>
752
+ <span class="tool-result-badge text-xs ml-auto ${statusClass}">${statusIcon}</span>
753
+ </summary>
754
+ <div class="tool-details-body pt-1">
755
+ ${argsHtml ? `<div class="tool-args w-full">${argsHtml}</div>` : ''}
756
+ ${rm ? `<div class="tool-result-content mt-2 pt-2 border-t border-base-content/10 w-full ${isError ? 'text-error' : 'text-success'}">${statusIcon} ${escapeHtml(truncResult)}</div>` : ''}
757
+ </div>
758
+ `;
759
+ messagesEl.appendChild(el);
760
+ }
761
+ }
762
+
720
763
  function scrollToBottom() {
721
764
  requestAnimationFrame(() => {
722
765
  chatArea.scrollTop = chatArea.scrollHeight;
@@ -1074,6 +1117,56 @@ function updateBatchBar() {
1074
1117
  * write it into the tab's `messagesHtml` cache; the live DOM is left
1075
1118
  * untouched so the active tab's content never flashes wrong data.
1076
1119
  */
1120
+
1121
+ /**
1122
+ * Render a messages array into the live DOM (messagesEl).
1123
+ * Handles user, assistant (text), assistant (toolCalls), and tool result messages.
1124
+ * Groups consecutive assistant+toolCalls with subsequent tool results into cards.
1125
+ */
1126
+ function renderMessagesArray(messages) {
1127
+ let i = 0;
1128
+ while (i < messages.length) {
1129
+ const m = messages[i];
1130
+ if (m.role === 'user') {
1131
+ addUserMessage(m.content);
1132
+ i++;
1133
+ } else if (m.role === 'assistant' && m.toolCalls && m.toolCalls.length > 0) {
1134
+ // Assistant message with tool calls — collect subsequent tool results
1135
+ const resultMsgs = [];
1136
+ let j = i + 1;
1137
+ while (j < messages.length && messages[j].role === 'tool') {
1138
+ resultMsgs.push(messages[j]);
1139
+ j++;
1140
+ }
1141
+ // If the assistant also had text content, render it first
1142
+ if (m.content && m.content.trim()) {
1143
+ const el = createAssistantMessage();
1144
+ renderMarkdown(el, m.content);
1145
+ }
1146
+ createHistoryToolCards(m, resultMsgs);
1147
+ i = j;
1148
+ } else if (m.role === 'tool') {
1149
+ // Orphan tool result (no preceding assistant+toolCalls) — render as info card
1150
+ const statusIcon = m.isError ? '✗' : '✓';
1151
+ const statusClass = m.isError ? 'text-error' : 'text-success';
1152
+ const el = document.createElement('div');
1153
+ el.className = `tool-card tool-border-safe my-1 p-2 ${statusClass}`;
1154
+ const toolLabel = m.toolName ? escapeHtml(m.toolName) : 'tool';
1155
+ const truncContent = m.content && m.content.length > 300
1156
+ ? m.content.slice(0, 300) + '...' : (m.content || '');
1157
+ el.innerHTML = `<span class="badge badge-info badge-sm">${toolLabel}</span> ${statusIcon} ${escapeHtml(truncContent)}`;
1158
+ messagesEl.appendChild(el);
1159
+ i++;
1160
+ } else if (m.role === 'assistant') {
1161
+ const el = createAssistantMessage();
1162
+ renderMarkdown(el, m.content);
1163
+ i++;
1164
+ } else {
1165
+ // system or unknown — skip
1166
+ i++;
1167
+ }
1168
+ }
1169
+ }
1077
1170
  function renderSessionMessages(msg) {
1078
1171
  // Back-compat: if called with a bare array (legacy), treat as active-tab apply
1079
1172
  const messages = Array.isArray(msg) ? msg : msg.messages;
@@ -1096,14 +1189,7 @@ function renderSessionMessages(msg) {
1096
1189
  // Active tab: paint directly into the live DOM (preserves any in-flight
1097
1190
  // streaming helpers that rely on messagesEl)
1098
1191
  messagesEl.innerHTML = '';
1099
- for (const m of messages) {
1100
- if (m.role === 'user') {
1101
- addUserMessage(m.content);
1102
- } else if (m.role === 'assistant') {
1103
- const el = createAssistantMessage();
1104
- renderMarkdown(el, m.content);
1105
- }
1106
- }
1192
+ renderMessagesArray(messages);
1107
1193
  scrollToBottom();
1108
1194
  // Snapshot into cache so subsequent tab-snapshots see the latest content
1109
1195
  sessionTabs[targetIdx].messagesHtml = messagesEl.innerHTML;
@@ -1136,14 +1222,7 @@ function buildMessagesHtmlOffDom(messages) {
1136
1222
  currentAssistantContent = '';
1137
1223
  currentThinkingEl = null;
1138
1224
  currentThinkingContent = '';
1139
- for (const m of messages) {
1140
- if (m.role === 'user') {
1141
- addUserMessage(m.content);
1142
- } else if (m.role === 'assistant') {
1143
- const el = createAssistantMessage();
1144
- renderMarkdown(el, m.content);
1145
- }
1146
- }
1225
+ renderMessagesArray(messages);
1147
1226
  return messagesEl.innerHTML;
1148
1227
  } finally {
1149
1228
  messagesEl.innerHTML = savedHtml;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.60",
3
+ "version": "0.4.61",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",