jinzd-ai-cli 0.4.108 → 0.4.110

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/README.md CHANGED
@@ -422,6 +422,12 @@ npm run test:watch # Watch mode
422
422
 
423
423
  ## Documentation
424
424
 
425
+ - [`docs/USAGE.md`](docs/USAGE.md) — Complete reference manual (commands, tools, config)
426
+ - [`docs/TUTORIAL.md`](docs/TUTORIAL.md) — Hands-on tutorial, zero to fluent in an hour
427
+ - [`docs/ADVANCED.md`](docs/ADVANCED.md) — Architecture and internals (for developers)
428
+ - [`docs/RECIPES.md`](docs/RECIPES.md) — Practical recipes by scenario
429
+ - [`docs/SECURITY.md`](docs/SECURITY.md) — **Security model, deployment checklist, audit history.** Read before exposing `aicli web` to a network.
430
+ - [`CHANGELOG.md`](CHANGELOG.md) — Per-version change log
425
431
  - [Chinese README](README.zh-CN.md) — 中文说明文档
426
432
 
427
433
  ## License
package/README.zh-CN.md CHANGED
@@ -184,6 +184,8 @@ AI 在对话中可自主调用 28 个工具:
184
184
  - [`docs/USAGE.zh-CN.md`](docs/USAGE.zh-CN.md) — 完整参考手册(所有命令、工具、配置)
185
185
  - [`docs/ADVANCED.zh-CN.md`](docs/ADVANCED.zh-CN.md) — 架构与内部原理(开发者向)
186
186
  - [`docs/RECIPES.zh-CN.md`](docs/RECIPES.zh-CN.md) — 实战配方(按场景查)
187
+ - [`docs/SECURITY.zh-CN.md`](docs/SECURITY.zh-CN.md) — **安全模型、部署清单、审计历史。** 把 `aicli web` 暴露到网络前请先读。
188
+ - [`CHANGELOG.md`](CHANGELOG.md) — 每个版本的更新记录
187
189
 
188
190
  ## 主要 REPL 命令
189
191
 
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-A26EOV2J.js";
4
+ } from "./chunk-BLJIPN2F.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-DNKI3JIL.js";
6
+ import "./chunk-SQPICAHN.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
 
9
9
  // src/cli/batch.ts
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-DNKI3JIL.js";
11
+ } from "./chunk-SQPICAHN.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -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.108";
9
+ var VERSION = "0.4.110";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-3BICTI5M.js";
6
6
  import {
7
7
  runTestsTool
8
- } from "./chunk-NN2B3UBA.js";
8
+ } from "./chunk-M77QS5QW.js";
9
9
  import {
10
10
  EnvLoader,
11
11
  NetworkError,
@@ -18,7 +18,7 @@ import {
18
18
  SUBAGENT_ALLOWED_TOOLS,
19
19
  SUBAGENT_DEFAULT_MAX_ROUNDS,
20
20
  SUBAGENT_MAX_ROUNDS_LIMIT
21
- } from "./chunk-DNKI3JIL.js";
21
+ } from "./chunk-SQPICAHN.js";
22
22
  import {
23
23
  fileCheckpoints
24
24
  } from "./chunk-4BKXL7SM.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-DNKI3JIL.js";
4
+ } from "./chunk-SQPICAHN.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.108";
4
+ var VERSION = "0.4.110";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  schemaToJsonSchema,
4
4
  truncateForPersist
5
- } from "./chunk-A3HZY7DA.js";
5
+ } from "./chunk-LKWFPJIP.js";
6
6
  import {
7
7
  AuthError,
8
8
  ProviderError,
@@ -18,7 +18,7 @@ import {
18
18
  MCP_PROTOCOL_VERSION,
19
19
  MCP_TOOL_PREFIX,
20
20
  VERSION
21
- } from "./chunk-DNKI3JIL.js";
21
+ } from "./chunk-SQPICAHN.js";
22
22
  import {
23
23
  redactJson
24
24
  } from "./chunk-7ZJN4KLV.js";
@@ -1772,6 +1772,39 @@ function buildPhantomCorrectionMessage(phantoms) {
1772
1772
  const list = phantoms.map((p) => ` - ${p}`).join("\n");
1773
1773
  return "You claimed to have written the following file(s), but no matching write_file tool call was actually made in this turn:\n" + list + '\n\nEach of these files does NOT exist on disk. You MUST now invoke write_file (via the function calling API) for every missing file listed above. Do NOT output another "completion summary" until the tool calls have actually been made.';
1774
1774
  }
1775
+ var PSEUDO_TOOL_CALL_PATTERNS = [
1776
+ // <tool_call name="..."> ... </tool_call> (DeepSeek V4 thinking, GLM)
1777
+ /<tool_call\s+name\s*=\s*["'][\w._-]+["']/,
1778
+ // <function_calls> ... </function_calls> (Anthropic-style as text)
1779
+ /<\/?function_calls\s*>/,
1780
+ // <invoke name="..." /> (Anthropic XML tool-call, which is real for
1781
+ // Claude API but is text/garbage for any other provider's plain stream)
1782
+ /<invoke\s+name\s*=\s*["'][\w._-]+["']/,
1783
+ // <tool_use> ... <tool_use_id> (Claude flavor leaked into text)
1784
+ /<tool_use(?:_id)?\b/,
1785
+ // ```tool_call\n...\n``` markdown fences (Kimi/Zhipu fallback)
1786
+ /```\s*tool_call\b/i,
1787
+ // Bare JSON tool-call block: lines starting with `{"name":"...","arguments":`
1788
+ /^\s*\{\s*"name"\s*:\s*"[\w._-]+"\s*,\s*"arguments"\s*:/m
1789
+ ];
1790
+ function detectPseudoToolCalls(content) {
1791
+ if (!content || content.length === 0) return null;
1792
+ for (const re of PSEUDO_TOOL_CALL_PATTERNS) {
1793
+ if (re.test(content)) return re.source;
1794
+ }
1795
+ return null;
1796
+ }
1797
+ var CONTENT_ONLY_STREAM_REMINDER = `
1798
+
1799
+ [\u26A0\uFE0F CONTENT GENERATION MODE]
1800
+ You are now in a CONTENT-ONLY streaming pass. The file at the configured path will receive every token of THIS response.
1801
+ - Do NOT emit <tool_call>, </tool_call>, <function_calls>, <invoke>, <tool_use>, or any tool-call XML/JSON markup.
1802
+ - Do NOT print "I will now call ...", "let me read ...", "<think>" reasoning blocks (the surrounding REPL handles those separately \u2014 they should not enter the saved file).
1803
+ - Do NOT pretend to call tools. There are NO tools available in this stream \u2014 only your text output is captured.
1804
+ - Produce ONLY the requested document body. Markdown is fine. Code blocks are fine. Tool-call markup is NOT.
1805
+ - If you accidentally start a <tool_call>, STOP and produce the document body instead.
1806
+
1807
+ The file is closed and named when this stream ends. If your output contains pseudo-tool-call markup, the save will be REJECTED and you will be asked to retry.`;
1775
1808
 
1776
1809
  // src/providers/kimi.ts
1777
1810
  var KIMI_XML_REMINDER = `
@@ -4047,6 +4080,8 @@ export {
4047
4080
  extractWrittenFilePaths,
4048
4081
  findPhantomClaims,
4049
4082
  buildPhantomCorrectionMessage,
4083
+ detectPseudoToolCalls,
4084
+ CONTENT_ONLY_STREAM_REMINDER,
4050
4085
  ProviderRegistry,
4051
4086
  getContentText,
4052
4087
  SessionManager,
@@ -36,7 +36,7 @@ import {
36
36
  TEST_TIMEOUT,
37
37
  VERSION,
38
38
  buildUserIdentityPrompt
39
- } from "./chunk-DNKI3JIL.js";
39
+ } from "./chunk-SQPICAHN.js";
40
40
  import "./chunk-PDX44BCA.js";
41
41
  export {
42
42
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-XWJTZWS7.js";
39
+ } from "./chunk-GFDEAYPM.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -2186,6 +2186,39 @@ CRITICAL \u2014 Batch file generation rules:
2186
2186
  4. Only produce a text summary AFTER all write_file calls have been made and returned success.
2187
2187
  5. The system compares every "file saved" claim against actual tool calls. Phantom claims trigger an automatic retry \u2014 do not waste rounds.`;
2188
2188
  var HALLUCINATION_CORRECTION_MESSAGE = "You did NOT actually call the write_file tool \u2014 the file was NOT created! Please immediately use the write_file tool via the function calling API to perform the actual file write. Do NOT describe file content in text \u2014 you MUST invoke write_file through the tool_calls mechanism.";
2189
+ var PSEUDO_TOOL_CALL_PATTERNS = [
2190
+ // <tool_call name="..."> ... </tool_call> (DeepSeek V4 thinking, GLM)
2191
+ /<tool_call\s+name\s*=\s*["'][\w._-]+["']/,
2192
+ // <function_calls> ... </function_calls> (Anthropic-style as text)
2193
+ /<\/?function_calls\s*>/,
2194
+ // <invoke name="..." /> (Anthropic XML tool-call, which is real for
2195
+ // Claude API but is text/garbage for any other provider's plain stream)
2196
+ /<invoke\s+name\s*=\s*["'][\w._-]+["']/,
2197
+ // <tool_use> ... <tool_use_id> (Claude flavor leaked into text)
2198
+ /<tool_use(?:_id)?\b/,
2199
+ // ```tool_call\n...\n``` markdown fences (Kimi/Zhipu fallback)
2200
+ /```\s*tool_call\b/i,
2201
+ // Bare JSON tool-call block: lines starting with `{"name":"...","arguments":`
2202
+ /^\s*\{\s*"name"\s*:\s*"[\w._-]+"\s*,\s*"arguments"\s*:/m
2203
+ ];
2204
+ function detectPseudoToolCalls(content) {
2205
+ if (!content || content.length === 0) return null;
2206
+ for (const re of PSEUDO_TOOL_CALL_PATTERNS) {
2207
+ if (re.test(content)) return re.source;
2208
+ }
2209
+ return null;
2210
+ }
2211
+ var CONTENT_ONLY_STREAM_REMINDER = `
2212
+
2213
+ [\u26A0\uFE0F CONTENT GENERATION MODE]
2214
+ You are now in a CONTENT-ONLY streaming pass. The file at the configured path will receive every token of THIS response.
2215
+ - Do NOT emit <tool_call>, </tool_call>, <function_calls>, <invoke>, <tool_use>, or any tool-call XML/JSON markup.
2216
+ - Do NOT print "I will now call ...", "let me read ...", "<think>" reasoning blocks (the surrounding REPL handles those separately \u2014 they should not enter the saved file).
2217
+ - Do NOT pretend to call tools. There are NO tools available in this stream \u2014 only your text output is captured.
2218
+ - Produce ONLY the requested document body. Markdown is fine. Code blocks are fine. Tool-call markup is NOT.
2219
+ - If you accidentally start a <tool_call>, STOP and produce the document body instead.
2220
+
2221
+ The file is closed and named when this stream ends. If your output contains pseudo-tool-call markup, the save will be REJECTED and you will be asked to retry.`;
2189
2222
 
2190
2223
  // src/providers/kimi.ts
2191
2224
  var KIMI_XML_REMINDER = `
@@ -9787,7 +9820,7 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
9787
9820
  }
9788
9821
 
9789
9822
  // src/web/session-handler.ts
9790
- import { existsSync as existsSync20, readFileSync as readFileSync13, appendFileSync as appendFileSync3, writeFileSync as writeFileSync8, mkdirSync as mkdirSync9, readdirSync as readdirSync9, statSync as statSync8, createWriteStream } from "fs";
9823
+ import { existsSync as existsSync20, readFileSync as readFileSync13, appendFileSync as appendFileSync3, writeFileSync as writeFileSync8, mkdirSync as mkdirSync9, readdirSync as readdirSync9, statSync as statSync8, createWriteStream, unlinkSync as unlinkSync4 } from "fs";
9791
9824
  import { join as join13, resolve as resolve5, dirname as dirname4 } from "path";
9792
9825
  import { execSync as execSync3 } from "child_process";
9793
9826
 
@@ -10663,10 +10696,11 @@ ${summaryResult.content}`,
10663
10696
  try {
10664
10697
  mkdirSync9(dirname4(saveToFile), { recursive: true });
10665
10698
  fileStream = createWriteStream(saveToFile, { encoding: "utf-8" });
10699
+ const teeSystemPrompt = (systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
10666
10700
  const chatRequest = {
10667
10701
  messages: apiMessages,
10668
10702
  model: this.currentModel,
10669
- systemPrompt,
10703
+ systemPrompt: teeSystemPrompt,
10670
10704
  systemPromptVolatile,
10671
10705
  stream: true,
10672
10706
  temperature: modelParams.temperature,
@@ -10691,10 +10725,20 @@ ${summaryResult.content}`,
10691
10725
  await new Promise((resolve7, reject) => {
10692
10726
  fileStream.end((err) => err ? reject(err) : resolve7());
10693
10727
  });
10694
- const lines = fullContent.split("\n").length;
10695
- const bytes = Buffer.byteLength(fullContent, "utf-8");
10696
- summary = `File saved: ${saveToFile} (${lines} lines, ${bytes} bytes)`;
10697
- undoStack.push(saveToFile, `save_last_response: ${saveToFile}`);
10728
+ const pseudoMatch = detectPseudoToolCalls(fullContent);
10729
+ if (pseudoMatch) {
10730
+ try {
10731
+ unlinkSync4(saveToFile);
10732
+ } catch {
10733
+ }
10734
+ isError = true;
10735
+ summary = `[save_last_response REJECTED] Your fresh streaming output contained pseudo-tool-call markup (matched: ${pseudoMatch}). The file at "${saveToFile}" was NOT saved (deleted). This fresh stream has NO tools attached \u2014 output is captured verbatim. Do NOT print <tool_call>, <function_calls>, <invoke>, or {"name":"...","arguments":...} JSON. Try again: produce ONLY the document body.`;
10736
+ } else {
10737
+ const lines = fullContent.split("\n").length;
10738
+ const bytes = Buffer.byteLength(fullContent, "utf-8");
10739
+ summary = `File saved: ${saveToFile} (${lines} lines, ${bytes} bytes)`;
10740
+ undoStack.push(saveToFile, `save_last_response: ${saveToFile}`);
10741
+ }
10698
10742
  if (teeUsage) {
10699
10743
  roundUsage.inputTokens += teeUsage.inputTokens;
10700
10744
  roundUsage.outputTokens += teeUsage.outputTokens;
@@ -11789,7 +11833,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11789
11833
  case "test": {
11790
11834
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
11791
11835
  try {
11792
- const { executeTests } = await import("./run-tests-VBJ5H3XR.js");
11836
+ const { executeTests } = await import("./run-tests-XVK66TGP.js");
11793
11837
  const argStr = args.join(" ").trim();
11794
11838
  let testArgs = {};
11795
11839
  if (argStr) {
@@ -386,7 +386,7 @@ ${content}`);
386
386
  }
387
387
  }
388
388
  async function runTaskMode(config, providers, configManager, topic) {
389
- const { TaskOrchestrator } = await import("./task-orchestrator-6WIYZPDB.js");
389
+ const { TaskOrchestrator } = await import("./task-orchestrator-2TGWMUUT.js");
390
390
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
391
391
  let interrupted = false;
392
392
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ CONTENT_ONLY_STREAM_REMINDER,
3
4
  HALLUCINATION_CORRECTION_MESSAGE,
4
5
  McpManager,
5
6
  ProviderRegistry,
@@ -12,6 +13,7 @@ import {
12
13
  buildWriteRoundReminder,
13
14
  clearDevState,
14
15
  computeCost,
16
+ detectPseudoToolCalls,
15
17
  detectsHallucinatedFileOp,
16
18
  extractWrittenFilePaths,
17
19
  findPhantomClaims,
@@ -25,10 +27,10 @@ import {
25
27
  saveDevState,
26
28
  sessionHasMeaningfulContent,
27
29
  setupProxy
28
- } from "./chunk-IKX4D6C7.js";
30
+ } from "./chunk-YIHYQVV7.js";
29
31
  import {
30
32
  ConfigManager
31
- } from "./chunk-A26EOV2J.js";
33
+ } from "./chunk-BLJIPN2F.js";
32
34
  import {
33
35
  ToolExecutor,
34
36
  ToolRegistry,
@@ -47,10 +49,10 @@ import {
47
49
  spawnAgentContext,
48
50
  theme,
49
51
  undoStack
50
- } from "./chunk-A3HZY7DA.js";
52
+ } from "./chunk-LKWFPJIP.js";
51
53
  import "./chunk-3BICTI5M.js";
52
54
  import "./chunk-2DXY7UGF.js";
53
- import "./chunk-NN2B3UBA.js";
55
+ import "./chunk-M77QS5QW.js";
54
56
  import "./chunk-2ZD3YTVM.js";
55
57
  import {
56
58
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -73,7 +75,7 @@ import {
73
75
  SKILLS_DIR_NAME,
74
76
  VERSION,
75
77
  buildUserIdentityPrompt
76
- } from "./chunk-DNKI3JIL.js";
78
+ } from "./chunk-SQPICAHN.js";
77
79
  import {
78
80
  formatGitContextForPrompt,
79
81
  getGitContext,
@@ -100,7 +102,7 @@ import { program } from "commander";
100
102
 
101
103
  // src/repl/repl.ts
102
104
  import * as readline from "readline";
103
- import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
105
+ import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
104
106
  import { join as join5, resolve as resolve2, extname as extname2, dirname as dirname3, basename as basename2 } from "path";
105
107
  import chalk4 from "chalk";
106
108
 
@@ -1594,7 +1596,7 @@ ${text}
1594
1596
  const { join: join6 } = await import("path");
1595
1597
  const { existsSync: existsSync6 } = await import("fs");
1596
1598
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1597
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-QZWMYEMB.js");
1599
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-EVSUKVTR.js");
1598
1600
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1599
1601
  const cwd = process.cwd();
1600
1602
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2644,7 +2646,7 @@ ${hint}` : "")
2644
2646
  usage: "/test [command|filter]",
2645
2647
  async execute(args, ctx) {
2646
2648
  try {
2647
- const { executeTests } = await import("./run-tests-LWSTPCZY.js");
2649
+ const { executeTests } = await import("./run-tests-M73756VV.js");
2648
2650
  const argStr = args.join(" ").trim();
2649
2651
  let testArgs = {};
2650
2652
  if (argStr) {
@@ -6237,10 +6239,11 @@ ${mcpBudgetNote}` : "");
6237
6239
  } else {
6238
6240
  const teeAc = this.setupStreamInterrupt();
6239
6241
  try {
6242
+ const teeSystemPrompt = (systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
6240
6243
  const genStream = provider.chatStream({
6241
6244
  messages: apiMessages,
6242
6245
  model: effectiveModel,
6243
- systemPrompt,
6246
+ systemPrompt: teeSystemPrompt,
6244
6247
  systemPromptVolatile,
6245
6248
  stream: true,
6246
6249
  temperature: modelParams.temperature,
@@ -6256,6 +6259,37 @@ ${mcpBudgetNote}` : "");
6256
6259
  genStream,
6257
6260
  { saveToFile, showTokens: teeShowTokens, sessionTotal: teeShowTokens ? { ...this.sessionTokenUsage } : void 0, signal: teeAc.signal }
6258
6261
  );
6262
+ const pseudoMatch = detectPseudoToolCalls(genContent);
6263
+ if (pseudoMatch) {
6264
+ try {
6265
+ unlinkSync2(saveToFile);
6266
+ } catch {
6267
+ }
6268
+ process.stdout.write(theme.error(
6269
+ `
6270
+ \u2717 Rejected save: response contained pseudo-tool-call markup (matched: ${pseudoMatch})
6271
+ ${saveToFile} was deleted; the model will be asked to regenerate without tool markup.
6272
+
6273
+ `
6274
+ ));
6275
+ const errorResults = result.toolCalls.map((tc) => ({
6276
+ callId: tc.id,
6277
+ content: tc.name === "save_last_response" ? `[save_last_response REJECTED] Your fresh streaming output contained pseudo-tool-call markup (e.g. <tool_call>, <function_calls>, or {"name":"...","arguments":...}). The file at "${saveToFile}" was NOT saved (it was deleted to prevent garbage on disk).
6278
+
6279
+ This fresh stream has NO tools attached \u2014 your output is captured verbatim. Do NOT print tool-call XML/JSON. Try again: produce ONLY the document body (markdown is fine; tool markup is NOT).` : `[skipped: save_last_response was rejected and other parallel calls are abandoned]`,
6280
+ isError: tc.name === "save_last_response"
6281
+ }));
6282
+ const reasoningContent3 = "reasoningContent" in result ? result.reasoningContent : void 0;
6283
+ const newMsgs3 = provider.buildToolResultMessages(result.toolCalls, errorResults, reasoningContent3);
6284
+ extraMessages.push(...newMsgs3);
6285
+ if (genUsage) {
6286
+ roundUsage.inputTokens += genUsage.inputTokens;
6287
+ roundUsage.outputTokens += genUsage.outputTokens;
6288
+ roundUsage.cacheCreationTokens += genUsage.cacheCreationTokens ?? 0;
6289
+ roundUsage.cacheReadTokens += genUsage.cacheReadTokens ?? 0;
6290
+ }
6291
+ continue;
6292
+ }
6259
6293
  lastResponseStore.content = genContent;
6260
6294
  if (genUsage) {
6261
6295
  roundUsage.inputTokens += genUsage.inputTokens;
@@ -6800,7 +6834,7 @@ program.command("web").description("Start Web UI server with browser-based chat
6800
6834
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6801
6835
  process.exit(1);
6802
6836
  }
6803
- const { startWebServer } = await import("./server-6F2JF2JK.js");
6837
+ const { startWebServer } = await import("./server-YRX5VO5N.js");
6804
6838
  await startWebServer({ port, host: options.host });
6805
6839
  });
6806
6840
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -6923,7 +6957,7 @@ program.command("sessions").description("List recent conversation sessions").act
6923
6957
  });
6924
6958
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
6925
6959
  try {
6926
- const batch = await import("./batch-A6F3UA3N.js");
6960
+ const batch = await import("./batch-V255CIMB.js");
6927
6961
  switch (action) {
6928
6962
  case "submit":
6929
6963
  if (!arg) {
@@ -6966,7 +7000,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
6966
7000
  }
6967
7001
  });
6968
7002
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
6969
- const { startMcpServer } = await import("./server-DSG3EYRA.js");
7003
+ const { startMcpServer } = await import("./server-54OXGXJ6.js");
6970
7004
  await startMcpServer({
6971
7005
  allowDestructive: !!options.allowDestructive,
6972
7006
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7093,7 +7127,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7093
7127
  }),
7094
7128
  config.get("customProviders")
7095
7129
  );
7096
- const { startHub } = await import("./hub-3HEMIJTM.js");
7130
+ const { startHub } = await import("./hub-O3EQ2LDY.js");
7097
7131
  await startHub(
7098
7132
  {
7099
7133
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-NN2B3UBA.js";
6
- import "./chunk-DNKI3JIL.js";
5
+ } from "./chunk-M77QS5QW.js";
6
+ import "./chunk-SQPICAHN.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-XWJTZWS7.js";
4
+ } from "./chunk-GFDEAYPM.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -3,14 +3,14 @@ import {
3
3
  ToolRegistry,
4
4
  getDangerLevel,
5
5
  schemaToJsonSchema
6
- } from "./chunk-A3HZY7DA.js";
6
+ } from "./chunk-LKWFPJIP.js";
7
7
  import "./chunk-3BICTI5M.js";
8
8
  import "./chunk-2DXY7UGF.js";
9
- import "./chunk-NN2B3UBA.js";
9
+ import "./chunk-M77QS5QW.js";
10
10
  import "./chunk-2ZD3YTVM.js";
11
11
  import {
12
12
  VERSION
13
- } from "./chunk-DNKI3JIL.js";
13
+ } from "./chunk-SQPICAHN.js";
14
14
  import "./chunk-4BKXL7SM.js";
15
15
  import "./chunk-7ZJN4KLV.js";
16
16
  import "./chunk-KHYD3WXE.js";
@@ -3,6 +3,7 @@ import {
3
3
  AuthManager
4
4
  } from "./chunk-BYNY5JPB.js";
5
5
  import {
6
+ CONTENT_ONLY_STREAM_REMINDER,
6
7
  HALLUCINATION_CORRECTION_MESSAGE,
7
8
  McpManager,
8
9
  ProviderRegistry,
@@ -11,6 +12,7 @@ import {
11
12
  TOOL_CALL_REMINDER,
12
13
  autoTrimSessionIfNeeded,
13
14
  computeCost,
15
+ detectPseudoToolCalls,
14
16
  detectsHallucinatedFileOp,
15
17
  formatCost,
16
18
  getContentText,
@@ -18,10 +20,10 @@ import {
18
20
  loadDevState,
19
21
  persistToolRound,
20
22
  setupProxy
21
- } from "./chunk-IKX4D6C7.js";
23
+ } from "./chunk-YIHYQVV7.js";
22
24
  import {
23
25
  ConfigManager
24
- } from "./chunk-A26EOV2J.js";
26
+ } from "./chunk-BLJIPN2F.js";
25
27
  import {
26
28
  ToolExecutor,
27
29
  ToolRegistry,
@@ -39,10 +41,10 @@ import {
39
41
  spawnAgentContext,
40
42
  truncateOutput,
41
43
  undoStack
42
- } from "./chunk-A3HZY7DA.js";
44
+ } from "./chunk-LKWFPJIP.js";
43
45
  import "./chunk-3BICTI5M.js";
44
46
  import "./chunk-2DXY7UGF.js";
45
- import "./chunk-NN2B3UBA.js";
47
+ import "./chunk-M77QS5QW.js";
46
48
  import "./chunk-2ZD3YTVM.js";
47
49
  import {
48
50
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -62,7 +64,7 @@ import {
62
64
  SKILLS_DIR_NAME,
63
65
  VERSION,
64
66
  buildUserIdentityPrompt
65
- } from "./chunk-DNKI3JIL.js";
67
+ } from "./chunk-SQPICAHN.js";
66
68
  import {
67
69
  formatGitContextForPrompt,
68
70
  getGitContext,
@@ -473,7 +475,7 @@ function loadMemoryContent(configDir) {
473
475
  }
474
476
 
475
477
  // src/web/session-handler.ts
476
- import { existsSync as existsSync3, readFileSync as readFileSync3, appendFileSync, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream } from "fs";
478
+ import { existsSync as existsSync3, readFileSync as readFileSync3, appendFileSync, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream, unlinkSync } from "fs";
477
479
  import { join as join2, resolve, dirname } from "path";
478
480
  import { execSync } from "child_process";
479
481
  var FREE_ROUND_TOOLS = /* @__PURE__ */ new Set(["write_todos"]);
@@ -1257,10 +1259,11 @@ ${summaryResult.content}`,
1257
1259
  try {
1258
1260
  mkdirSync(dirname(saveToFile), { recursive: true });
1259
1261
  fileStream = createWriteStream(saveToFile, { encoding: "utf-8" });
1262
+ const teeSystemPrompt = (systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
1260
1263
  const chatRequest = {
1261
1264
  messages: apiMessages,
1262
1265
  model: this.currentModel,
1263
- systemPrompt,
1266
+ systemPrompt: teeSystemPrompt,
1264
1267
  systemPromptVolatile,
1265
1268
  stream: true,
1266
1269
  temperature: modelParams.temperature,
@@ -1285,10 +1288,20 @@ ${summaryResult.content}`,
1285
1288
  await new Promise((resolve3, reject) => {
1286
1289
  fileStream.end((err) => err ? reject(err) : resolve3());
1287
1290
  });
1288
- const lines = fullContent.split("\n").length;
1289
- const bytes = Buffer.byteLength(fullContent, "utf-8");
1290
- summary = `File saved: ${saveToFile} (${lines} lines, ${bytes} bytes)`;
1291
- undoStack.push(saveToFile, `save_last_response: ${saveToFile}`);
1291
+ const pseudoMatch = detectPseudoToolCalls(fullContent);
1292
+ if (pseudoMatch) {
1293
+ try {
1294
+ unlinkSync(saveToFile);
1295
+ } catch {
1296
+ }
1297
+ isError = true;
1298
+ summary = `[save_last_response REJECTED] Your fresh streaming output contained pseudo-tool-call markup (matched: ${pseudoMatch}). The file at "${saveToFile}" was NOT saved (deleted). This fresh stream has NO tools attached \u2014 output is captured verbatim. Do NOT print <tool_call>, <function_calls>, <invoke>, or {"name":"...","arguments":...} JSON. Try again: produce ONLY the document body.`;
1299
+ } else {
1300
+ const lines = fullContent.split("\n").length;
1301
+ const bytes = Buffer.byteLength(fullContent, "utf-8");
1302
+ summary = `File saved: ${saveToFile} (${lines} lines, ${bytes} bytes)`;
1303
+ undoStack.push(saveToFile, `save_last_response: ${saveToFile}`);
1304
+ }
1292
1305
  if (teeUsage) {
1293
1306
  roundUsage.inputTokens += teeUsage.inputTokens;
1294
1307
  roundUsage.outputTokens += teeUsage.outputTokens;
@@ -2383,7 +2396,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2383
2396
  case "test": {
2384
2397
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2385
2398
  try {
2386
- const { executeTests } = await import("./run-tests-LWSTPCZY.js");
2399
+ const { executeTests } = await import("./run-tests-M73756VV.js");
2387
2400
  const argStr = args.join(" ").trim();
2388
2401
  let testArgs = {};
2389
2402
  if (argStr) {
@@ -4,14 +4,14 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-A3HZY7DA.js";
7
+ } from "./chunk-LKWFPJIP.js";
8
8
  import "./chunk-3BICTI5M.js";
9
9
  import "./chunk-2DXY7UGF.js";
10
- import "./chunk-NN2B3UBA.js";
10
+ import "./chunk-M77QS5QW.js";
11
11
  import "./chunk-2ZD3YTVM.js";
12
12
  import {
13
13
  SUBAGENT_ALLOWED_TOOLS
14
- } from "./chunk-DNKI3JIL.js";
14
+ } from "./chunk-SQPICAHN.js";
15
15
  import "./chunk-4BKXL7SM.js";
16
16
  import "./chunk-7ZJN4KLV.js";
17
17
  import "./chunk-KHYD3WXE.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.108",
3
+ "version": "0.4.110",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",