jinzd-ai-cli 0.4.59 → 0.4.60

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 { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.4.59";
11
+ var VERSION = "0.4.60";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
@@ -6,8 +6,9 @@ import {
6
6
  ProviderError,
7
7
  ProviderNotFoundError,
8
8
  RateLimitError,
9
- schemaToJsonSchema
10
- } from "./chunk-7RX7675B.js";
9
+ schemaToJsonSchema,
10
+ truncateForPersist
11
+ } from "./chunk-C32FFHMY.js";
11
12
  import {
12
13
  APP_NAME,
13
14
  CONFIG_DIR_NAME,
@@ -20,7 +21,7 @@ import {
20
21
  MCP_TOOL_PREFIX,
21
22
  PLUGINS_DIR_NAME,
22
23
  VERSION
23
- } from "./chunk-YJCJBUOG.js";
24
+ } from "./chunk-2DWWB4KD.js";
24
25
 
25
26
  // src/config/config-manager.ts
26
27
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -309,9 +310,12 @@ var BaseProvider = class {
309
310
  /**
310
311
  * 将 Message[] 转换为 OpenAI API 格式的消息数组。
311
312
  * content 为 string 时直接传递;为 MessageContentPart[] 时保留数组格式(vision 请求)。
313
+ *
314
+ * 自动跳过 role='tool' 和带 toolCalls 的 assistant 消息——
315
+ * 这些是 v0.4.60+ 持久化的工具历史,由 _extraMessages 机制单独注入。
312
316
  */
313
317
  normalizeMessages(messages) {
314
- return messages.map((m) => ({ role: m.role, content: m.content }));
318
+ return messages.filter((m) => m.role !== "tool" && !m.toolCalls).map((m) => ({ role: m.role, content: m.content }));
315
319
  }
316
320
  };
317
321
 
@@ -475,7 +479,7 @@ var ClaudeProvider = class extends BaseProvider {
475
479
  }
476
480
  async chat(request) {
477
481
  try {
478
- const messages = request.messages.filter((m) => m.role !== "system").map((m) => ({
482
+ const messages = request.messages.filter((m) => m.role !== "system" && m.role !== "tool" && !m.toolCalls).map((m) => ({
479
483
  role: m.role,
480
484
  content: this.contentToClaudeParts(m.content)
481
485
  }));
@@ -500,7 +504,7 @@ var ClaudeProvider = class extends BaseProvider {
500
504
  }
501
505
  async *chatStream(request) {
502
506
  try {
503
- const messages = request.messages.filter((m) => m.role !== "system").map((m) => ({
507
+ const messages = request.messages.filter((m) => m.role !== "system" && m.role !== "tool" && !m.toolCalls).map((m) => ({
504
508
  role: m.role,
505
509
  content: this.contentToClaudeParts(m.content)
506
510
  }));
@@ -557,7 +561,7 @@ var ClaudeProvider = class extends BaseProvider {
557
561
  }
558
562
  }))
559
563
  );
560
- const baseMessages = request.messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: this.contentToClaudeParts(m.content) }));
564
+ const baseMessages = request.messages.filter((m) => m.role !== "system" && m.role !== "tool" && !m.toolCalls).map((m) => ({ role: m.role, content: this.contentToClaudeParts(m.content) }));
561
565
  const extraMessages = request._extraMessages ?? [];
562
566
  const allMessages = [...baseMessages, ...extraMessages];
563
567
  const { thinking, temperature } = this.buildThinkingParams(request);
@@ -869,7 +873,7 @@ var GeminiProvider = class extends BaseProvider {
869
873
  return parts.length > 0 ? parts : [{ text: "" }];
870
874
  }
871
875
  toGeminiHistory(messages) {
872
- return messages.filter((m) => m.role !== "system").map((m) => ({
876
+ return messages.filter((m) => m.role !== "system" && m.role !== "tool" && !m.toolCalls).map((m) => ({
873
877
  role: m.role === "assistant" ? "model" : "user",
874
878
  parts: this.contentToGeminiParts(m.content)
875
879
  }));
@@ -2521,10 +2525,19 @@ var Session = class _Session {
2521
2525
  messageIndex: c.messageIndex,
2522
2526
  timestamp: c.timestamp.toISOString()
2523
2527
  })),
2524
- messages: this.messages.map((m) => ({
2525
- ...m,
2526
- timestamp: m.timestamp.toISOString()
2527
- }))
2528
+ messages: this.messages.map((m) => {
2529
+ const out = {
2530
+ role: m.role,
2531
+ content: m.content,
2532
+ timestamp: m.timestamp.toISOString()
2533
+ };
2534
+ if (m.toolCalls) out.toolCalls = m.toolCalls;
2535
+ if (m.reasoningContent !== void 0) out.reasoningContent = m.reasoningContent;
2536
+ if (m.toolCallId) out.toolCallId = m.toolCallId;
2537
+ if (m.toolName) out.toolName = m.toolName;
2538
+ if (m.isError !== void 0) out.isError = m.isError;
2539
+ return out;
2540
+ })
2528
2541
  };
2529
2542
  }
2530
2543
  /**
@@ -2594,11 +2607,17 @@ var Session = class _Session {
2594
2607
  }
2595
2608
  session.messages = d.messages.map((m) => {
2596
2609
  const ts = new Date(m.timestamp);
2597
- return {
2610
+ const msg = {
2598
2611
  role: m.role ?? "user",
2599
- content: m.content,
2612
+ content: Array.isArray(m.content) ? m.content : String(m.content ?? ""),
2600
2613
  timestamp: isNaN(ts.getTime()) ? /* @__PURE__ */ new Date() : ts
2601
2614
  };
2615
+ if (Array.isArray(m.toolCalls)) msg.toolCalls = m.toolCalls;
2616
+ if (typeof m.reasoningContent === "string") msg.reasoningContent = m.reasoningContent;
2617
+ if (typeof m.toolCallId === "string") msg.toolCallId = m.toolCallId;
2618
+ if (typeof m.toolName === "string") msg.toolName = m.toolName;
2619
+ if (typeof m.isError === "boolean") msg.isError = m.isError;
2620
+ return msg;
2602
2621
  });
2603
2622
  return session;
2604
2623
  }
@@ -3667,6 +3686,78 @@ function formatCost(amount) {
3667
3686
  return `$${amount.toFixed(2)}`;
3668
3687
  }
3669
3688
 
3689
+ // src/session/tool-history.ts
3690
+ function persistToolRound(session, toolCalls, toolResults, opts) {
3691
+ session.addMessage({
3692
+ role: "assistant",
3693
+ content: opts?.assistantContent ?? "",
3694
+ toolCalls,
3695
+ reasoningContent: opts?.reasoningContent,
3696
+ timestamp: /* @__PURE__ */ new Date()
3697
+ });
3698
+ for (let i = 0; i < toolCalls.length; i++) {
3699
+ const tc = toolCalls[i];
3700
+ const tr = toolResults[i];
3701
+ if (!tr) continue;
3702
+ session.addMessage({
3703
+ role: "tool",
3704
+ content: truncateForPersist(tr.content),
3705
+ toolCallId: tr.callId,
3706
+ toolName: tc.name,
3707
+ isError: tr.isError,
3708
+ timestamp: /* @__PURE__ */ new Date()
3709
+ });
3710
+ }
3711
+ }
3712
+ function isToolMessage(m) {
3713
+ return m.role === "tool" || !!(m.toolCalls && m.toolCalls.length > 0);
3714
+ }
3715
+ function extractToolHistory(messages) {
3716
+ const baseMessages = [];
3717
+ const toolHistory = [];
3718
+ for (const m of messages) {
3719
+ if (isToolMessage(m)) {
3720
+ toolHistory.push(m);
3721
+ } else {
3722
+ baseMessages.push(m);
3723
+ }
3724
+ }
3725
+ return { baseMessages, toolHistory };
3726
+ }
3727
+ function rebuildExtraMessages(provider, toolHistory) {
3728
+ if (toolHistory.length === 0) return [];
3729
+ const result = [];
3730
+ let i = 0;
3731
+ while (i < toolHistory.length) {
3732
+ const msg = toolHistory[i];
3733
+ if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) {
3734
+ const toolCalls = msg.toolCalls;
3735
+ const toolResults = [];
3736
+ let j = i + 1;
3737
+ while (j < toolHistory.length && toolHistory[j].role === "tool") {
3738
+ const tm = toolHistory[j];
3739
+ toolResults.push({
3740
+ callId: tm.toolCallId ?? "",
3741
+ content: typeof tm.content === "string" ? tm.content : getContentText(tm.content),
3742
+ isError: tm.isError ?? false
3743
+ });
3744
+ j++;
3745
+ }
3746
+ result.push(
3747
+ ...provider.buildToolResultMessages(toolCalls, toolResults, msg.reasoningContent)
3748
+ );
3749
+ i = j;
3750
+ } else {
3751
+ result.push({
3752
+ role: msg.role,
3753
+ content: typeof msg.content === "string" ? msg.content : getContentText(msg.content)
3754
+ });
3755
+ i++;
3756
+ }
3757
+ }
3758
+ return result;
3759
+ }
3760
+
3670
3761
  // src/repl/dev-state.ts
3671
3762
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "fs";
3672
3763
  import { join as join5 } from "path";
@@ -3777,6 +3868,9 @@ export {
3777
3868
  computeCost,
3778
3869
  formatCost,
3779
3870
  parseSimpleYaml,
3871
+ persistToolRound,
3872
+ extractToolHistory,
3873
+ rebuildExtraMessages,
3780
3874
  SNAPSHOT_PROMPT,
3781
3875
  sessionHasMeaningfulContent,
3782
3876
  saveDevState,
@@ -10,7 +10,7 @@ import {
10
10
  SUBAGENT_DEFAULT_MAX_ROUNDS,
11
11
  SUBAGENT_MAX_ROUNDS_LIMIT,
12
12
  runTestsTool
13
- } from "./chunk-YJCJBUOG.js";
13
+ } from "./chunk-2DWWB4KD.js";
14
14
 
15
15
  // src/tools/builtin/bash.ts
16
16
  import { execSync } from "child_process";
@@ -1098,6 +1098,20 @@ function snapToLineBoundary(content, target, direction) {
1098
1098
  return target;
1099
1099
  }
1100
1100
  }
1101
+ var PERSIST_MAX_CHARS = 8192;
1102
+ function truncateForPersist(content, maxChars = PERSIST_MAX_CHARS) {
1103
+ if (content.length <= maxChars) return content;
1104
+ const headSize = Math.floor(maxChars * 0.7);
1105
+ const tailSize = Math.floor(maxChars * 0.2);
1106
+ const head = content.slice(0, headSize);
1107
+ const tail = content.slice(-tailSize);
1108
+ const omitted = content.length - headSize - tailSize;
1109
+ return `${head}
1110
+
1111
+ [... ${omitted} chars omitted for storage ...]
1112
+
1113
+ ${tail}`;
1114
+ }
1101
1115
  function truncateOutput(content, toolName, maxChars) {
1102
1116
  const limit = maxChars ?? activeMaxChars;
1103
1117
  if (content.length <= limit) return content;
@@ -4208,6 +4222,7 @@ export {
4208
4222
  checkPermission,
4209
4223
  setMaxOutputCap,
4210
4224
  setContextWindow,
4225
+ truncateForPersist,
4211
4226
  truncateOutput,
4212
4227
  ToolExecutor,
4213
4228
  lastResponseStore,
@@ -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.59";
9
+ var VERSION = "0.4.60";
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-I5YCZ72U.js");
388
+ const { TaskOrchestrator } = await import("./task-orchestrator-Z4IK3UEA.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
+ extractToolHistory,
16
17
  extractWrittenFilePaths,
17
18
  findPhantomClaims,
18
19
  formatCost,
@@ -24,10 +25,12 @@ import {
24
25
  hadPreviousWriteToolCalls,
25
26
  loadDevState,
26
27
  parseSimpleYaml,
28
+ persistToolRound,
29
+ rebuildExtraMessages,
27
30
  saveDevState,
28
31
  sessionHasMeaningfulContent,
29
32
  setupProxy
30
- } from "./chunk-NXSYL5OP.js";
33
+ } from "./chunk-3YVHYAXK.js";
31
34
  import {
32
35
  ToolExecutor,
33
36
  ToolRegistry,
@@ -41,7 +44,7 @@ import {
41
44
  spawnAgentContext,
42
45
  theme,
43
46
  undoStack
44
- } from "./chunk-7RX7675B.js";
47
+ } from "./chunk-C32FFHMY.js";
45
48
  import {
46
49
  fileCheckpoints
47
50
  } from "./chunk-4BKXL7SM.js";
@@ -66,7 +69,7 @@ import {
66
69
  SKILLS_DIR_NAME,
67
70
  VERSION,
68
71
  buildUserIdentityPrompt
69
- } from "./chunk-YJCJBUOG.js";
72
+ } from "./chunk-2DWWB4KD.js";
70
73
 
71
74
  // src/index.ts
72
75
  import { program } from "commander";
@@ -2161,7 +2164,7 @@ ${hint}` : "")
2161
2164
  usage: "/test [command|filter]",
2162
2165
  async execute(args, ctx) {
2163
2166
  try {
2164
- const { executeTests } = await import("./run-tests-IW6GHAVV.js");
2167
+ const { executeTests } = await import("./run-tests-QGJHXL5Z.js");
2165
2168
  const argStr = args.join(" ").trim();
2166
2169
  let testArgs = {};
2167
2170
  if (argStr) {
@@ -4869,8 +4872,9 @@ Session '${this.resumeSessionId}' not found.
4869
4872
  if (this.blockedTools) {
4870
4873
  toolDefs = toolDefs.filter((t) => !this.blockedTools.has(t.name));
4871
4874
  }
4872
- const apiMessages = [...messages];
4873
- const extraMessages = [];
4875
+ const { baseMessages: cleanMessages, toolHistory } = extractToolHistory(messages);
4876
+ const apiMessages = [...cleanMessages];
4877
+ const extraMessages = toolHistory.length > 0 ? rebuildExtraMessages(provider, toolHistory) : [];
4874
4878
  const maxToolRounds = this.maxToolRoundsOverride ?? this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
4875
4879
  const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
4876
4880
  const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : DEFAULT_AUTO_PAUSE_INTERVAL;
@@ -5260,6 +5264,11 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
5260
5264
  const reasoningContent = "reasoningContent" in result ? result.reasoningContent : void 0;
5261
5265
  const newMsgs = provider.buildToolResultMessages(result.toolCalls, toolResults, reasoningContent);
5262
5266
  extraMessages.push(...newMsgs);
5267
+ const streamedContent = "content" in result ? result.content : void 0;
5268
+ persistToolRound(session, result.toolCalls, toolResults, {
5269
+ assistantContent: streamedContent,
5270
+ reasoningContent
5271
+ });
5263
5272
  const thisRoundHadWrite = result.toolCalls.some(
5264
5273
  (tc) => tc.name === "write_file" || tc.name === "edit_file"
5265
5274
  );
@@ -5695,7 +5704,7 @@ program.command("web").description("Start Web UI server with browser-based chat
5695
5704
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5696
5705
  process.exit(1);
5697
5706
  }
5698
- const { startWebServer } = await import("./server-J7PNU32E.js");
5707
+ const { startWebServer } = await import("./server-L2XJYXMB.js");
5699
5708
  await startWebServer({ port, host: options.host });
5700
5709
  });
5701
5710
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -5928,7 +5937,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
5928
5937
  }),
5929
5938
  config.get("customProviders")
5930
5939
  );
5931
- const { startHub } = await import("./hub-3BY5W4VE.js");
5940
+ const { startHub } = await import("./hub-JTMNY7JR.js");
5932
5941
  await startHub(
5933
5942
  {
5934
5943
  topic: topic ?? "",
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-YJCJBUOG.js";
5
+ } from "./chunk-2DWWB4KD.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-F5WLEWN2.js";
4
+ } from "./chunk-X4GL6D5L.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -9,6 +9,7 @@ import {
9
9
  TOOL_CALL_REMINDER,
10
10
  computeCost,
11
11
  detectsHallucinatedFileOp,
12
+ extractToolHistory,
12
13
  formatCost,
13
14
  formatGitContextForPrompt,
14
15
  getContentText,
@@ -16,8 +17,10 @@ import {
16
17
  getGitRoot,
17
18
  hadPreviousWriteToolCalls,
18
19
  loadDevState,
20
+ persistToolRound,
21
+ rebuildExtraMessages,
19
22
  setupProxy
20
- } from "./chunk-NXSYL5OP.js";
23
+ } from "./chunk-3YVHYAXK.js";
21
24
  import {
22
25
  AuthManager
23
26
  } from "./chunk-BYNY5JPB.js";
@@ -36,7 +39,7 @@ import {
36
39
  spawnAgentContext,
37
40
  truncateOutput,
38
41
  undoStack
39
- } from "./chunk-7RX7675B.js";
42
+ } from "./chunk-C32FFHMY.js";
40
43
  import "./chunk-4BKXL7SM.js";
41
44
  import {
42
45
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -56,7 +59,7 @@ import {
56
59
  SKILLS_DIR_NAME,
57
60
  VERSION,
58
61
  buildUserIdentityPrompt
59
- } from "./chunk-YJCJBUOG.js";
62
+ } from "./chunk-2DWWB4KD.js";
60
63
 
61
64
  // src/web/server.ts
62
65
  import express from "express";
@@ -814,8 +817,9 @@ var SessionHandler = class _SessionHandler {
814
817
  }
815
818
  async handleChatWithTools(provider, messages, toolDefs) {
816
819
  const session = this.sessions.current;
817
- const apiMessages = [...messages];
818
- const extraMessages = [];
820
+ const { baseMessages: cleanMessages, toolHistory } = extractToolHistory(messages);
821
+ const apiMessages = [...cleanMessages];
822
+ const extraMessages = toolHistory.length > 0 ? rebuildExtraMessages(provider, toolHistory) : [];
819
823
  const maxToolRounds = this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
820
824
  const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
821
825
  const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : 50;
@@ -1020,6 +1024,10 @@ Details: ${errMsg.split("\n")[0]}
1020
1024
  const reasoningContent = result.reasoningContent;
1021
1025
  const newMsgs = provider.buildToolResultMessages(result.toolCalls, toolResults, reasoningContent);
1022
1026
  extraMessages.push(...newMsgs);
1027
+ persistToolRound(session, result.toolCalls, toolResults, {
1028
+ assistantContent: result.content,
1029
+ reasoningContent
1030
+ });
1023
1031
  const allFree = result.toolCalls.every((tc) => FREE_ROUND_TOOLS.has(tc.name));
1024
1032
  if (allFree) {
1025
1033
  consecutiveFreeRounds++;
@@ -1915,7 +1923,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1915
1923
  case "test": {
1916
1924
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1917
1925
  try {
1918
- const { executeTests } = await import("./run-tests-IW6GHAVV.js");
1926
+ const { executeTests } = await import("./run-tests-QGJHXL5Z.js");
1919
1927
  const argStr = args.join(" ").trim();
1920
1928
  let testArgs = {};
1921
1929
  if (argStr) {
@@ -4,11 +4,11 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-7RX7675B.js";
7
+ } from "./chunk-C32FFHMY.js";
8
8
  import "./chunk-4BKXL7SM.js";
9
9
  import {
10
10
  SUBAGENT_ALLOWED_TOOLS
11
- } from "./chunk-YJCJBUOG.js";
11
+ } from "./chunk-2DWWB4KD.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.59",
3
+ "version": "0.4.60",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",