jinzd-ai-cli 0.4.110 → 0.4.111

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.
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-BLJIPN2F.js";
4
+ } from "./chunk-VYDXL2PC.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-SQPICAHN.js";
6
+ import "./chunk-CPIQXP7Q.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
 
9
9
  // src/cli/batch.ts
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-3BICTI5M.js";
6
6
  import {
7
7
  runTestsTool
8
- } from "./chunk-M77QS5QW.js";
8
+ } from "./chunk-5LKW2GOF.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-SQPICAHN.js";
21
+ } from "./chunk-CPIQXP7Q.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-SQPICAHN.js";
4
+ } from "./chunk-CPIQXP7Q.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } from "child_process";
@@ -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.110";
9
+ var VERSION = "0.4.111";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.110";
4
+ var VERSION = "0.4.111";
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-LKWFPJIP.js";
5
+ } from "./chunk-5EVKTBUU.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-SQPICAHN.js";
21
+ } from "./chunk-CPIQXP7Q.js";
22
22
  import {
23
23
  redactJson
24
24
  } from "./chunk-7ZJN4KLV.js";
@@ -1794,6 +1794,35 @@ function detectPseudoToolCalls(content) {
1794
1794
  }
1795
1795
  return null;
1796
1796
  }
1797
+ function stripPseudoToolCalls(content) {
1798
+ if (!content) return content;
1799
+ let out = content;
1800
+ out = out.replace(/<tool_call\b[^>]*>[\s\S]*?<\/tool_call>/gi, "");
1801
+ out = out.replace(/<tool_call\b[^>]*\/>/gi, "");
1802
+ out = out.replace(/<function_calls\b[^>]*>[\s\S]*?<\/function_calls>/gi, "");
1803
+ out = out.replace(/<invoke\b[^>]*>[\s\S]*?<\/invoke>/gi, "");
1804
+ out = out.replace(/<invoke\b[^>]*\/>/gi, "");
1805
+ out = out.replace(/<tool_use(?:_id)?\b[^>]*>[\s\S]*?<\/tool_use(?:_id)?>/gi, "");
1806
+ out = out.replace(/```\s*tool_call\b[\s\S]*?```/gi, "");
1807
+ out = out.replace(/<think\b[^>]*>[\s\S]*?<\/think>/gi, "");
1808
+ out = out.replace(/^\s*\{\s*"name"\s*:\s*"[\w._-]+"\s*,\s*"arguments"\s*:[\s\S]*?\}\s*$/gm, "");
1809
+ out = out.replace(/\n{3,}/g, "\n\n").trim();
1810
+ return out;
1811
+ }
1812
+ function looksLikeDocumentBody(content) {
1813
+ if (!content || content.length < 200) return false;
1814
+ if (/^#{1,6}\s+\S/m.test(content)) return true;
1815
+ const paragraphs = content.split(/\n\s*\n/).filter((p) => p.trim().length > 30);
1816
+ if (paragraphs.length >= 3) return true;
1817
+ return false;
1818
+ }
1819
+ function stripToolCallReminder(systemPrompt) {
1820
+ if (!systemPrompt) return systemPrompt;
1821
+ const idx = systemPrompt.indexOf("[\u26A0\uFE0F Mandatory Tool Call Policy]");
1822
+ if (idx === -1) return systemPrompt;
1823
+ return systemPrompt.slice(0, idx).trimEnd();
1824
+ }
1825
+ var TEE_FINAL_USER_NUDGE = `\u26A0\uFE0F STOP using tools NOW. The save_last_response tee stream is open and capturing every token of THIS response. Output ONLY the requested document body, in markdown. The very first character of your response must be the document's top-level heading (e.g. "# \u5BA1\u8BA1\u62A5\u544A" / "# Audit Report"). Do NOT print <tool_call>, </tool_call>, <function_calls>, <invoke>, <tool_use>, <think>, or any other tool-call markup. Do NOT narrate that you will produce the document \u2014 just produce it. Do NOT pretend to call tools \u2014 there are none in this stream.`;
1797
1826
  var CONTENT_ONLY_STREAM_REMINDER = `
1798
1827
 
1799
1828
  [\u26A0\uFE0F CONTENT GENERATION MODE]
@@ -4081,6 +4110,10 @@ export {
4081
4110
  findPhantomClaims,
4082
4111
  buildPhantomCorrectionMessage,
4083
4112
  detectPseudoToolCalls,
4113
+ stripPseudoToolCalls,
4114
+ looksLikeDocumentBody,
4115
+ stripToolCallReminder,
4116
+ TEE_FINAL_USER_NUDGE,
4084
4117
  CONTENT_ONLY_STREAM_REMINDER,
4085
4118
  ProviderRegistry,
4086
4119
  getContentText,
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-SQPICAHN.js";
11
+ } from "./chunk-CPIQXP7Q.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -36,7 +36,7 @@ import {
36
36
  TEST_TIMEOUT,
37
37
  VERSION,
38
38
  buildUserIdentityPrompt
39
- } from "./chunk-SQPICAHN.js";
39
+ } from "./chunk-CPIQXP7Q.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-GFDEAYPM.js";
39
+ } from "./chunk-AVMGKU4E.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -2208,6 +2208,35 @@ function detectPseudoToolCalls(content) {
2208
2208
  }
2209
2209
  return null;
2210
2210
  }
2211
+ function stripPseudoToolCalls(content) {
2212
+ if (!content) return content;
2213
+ let out = content;
2214
+ out = out.replace(/<tool_call\b[^>]*>[\s\S]*?<\/tool_call>/gi, "");
2215
+ out = out.replace(/<tool_call\b[^>]*\/>/gi, "");
2216
+ out = out.replace(/<function_calls\b[^>]*>[\s\S]*?<\/function_calls>/gi, "");
2217
+ out = out.replace(/<invoke\b[^>]*>[\s\S]*?<\/invoke>/gi, "");
2218
+ out = out.replace(/<invoke\b[^>]*\/>/gi, "");
2219
+ out = out.replace(/<tool_use(?:_id)?\b[^>]*>[\s\S]*?<\/tool_use(?:_id)?>/gi, "");
2220
+ out = out.replace(/```\s*tool_call\b[\s\S]*?```/gi, "");
2221
+ out = out.replace(/<think\b[^>]*>[\s\S]*?<\/think>/gi, "");
2222
+ out = out.replace(/^\s*\{\s*"name"\s*:\s*"[\w._-]+"\s*,\s*"arguments"\s*:[\s\S]*?\}\s*$/gm, "");
2223
+ out = out.replace(/\n{3,}/g, "\n\n").trim();
2224
+ return out;
2225
+ }
2226
+ function looksLikeDocumentBody(content) {
2227
+ if (!content || content.length < 200) return false;
2228
+ if (/^#{1,6}\s+\S/m.test(content)) return true;
2229
+ const paragraphs = content.split(/\n\s*\n/).filter((p) => p.trim().length > 30);
2230
+ if (paragraphs.length >= 3) return true;
2231
+ return false;
2232
+ }
2233
+ function stripToolCallReminder(systemPrompt) {
2234
+ if (!systemPrompt) return systemPrompt;
2235
+ const idx = systemPrompt.indexOf("[\u26A0\uFE0F Mandatory Tool Call Policy]");
2236
+ if (idx === -1) return systemPrompt;
2237
+ return systemPrompt.slice(0, idx).trimEnd();
2238
+ }
2239
+ var TEE_FINAL_USER_NUDGE = `\u26A0\uFE0F STOP using tools NOW. The save_last_response tee stream is open and capturing every token of THIS response. Output ONLY the requested document body, in markdown. The very first character of your response must be the document's top-level heading (e.g. "# \u5BA1\u8BA1\u62A5\u544A" / "# Audit Report"). Do NOT print <tool_call>, </tool_call>, <function_calls>, <invoke>, <tool_use>, <think>, or any other tool-call markup. Do NOT narrate that you will produce the document \u2014 just produce it. Do NOT pretend to call tools \u2014 there are none in this stream.`;
2211
2240
  var CONTENT_ONLY_STREAM_REMINDER = `
2212
2241
 
2213
2242
  [\u26A0\uFE0F CONTENT GENERATION MODE]
@@ -10696,7 +10725,8 @@ ${summaryResult.content}`,
10696
10725
  try {
10697
10726
  mkdirSync9(dirname4(saveToFile), { recursive: true });
10698
10727
  fileStream = createWriteStream(saveToFile, { encoding: "utf-8" });
10699
- const teeSystemPrompt = (systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
10728
+ const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
10729
+ const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
10700
10730
  const chatRequest = {
10701
10731
  messages: apiMessages,
10702
10732
  model: this.currentModel,
@@ -10709,7 +10739,7 @@ ${summaryResult.content}`,
10709
10739
  thinking: modelParams.thinking,
10710
10740
  thinkingBudget: modelParams.thinkingBudget,
10711
10741
  signal: ac.signal,
10712
- ...extraMessages.length > 0 ? { _extraMessages: extraMessages } : {}
10742
+ _extraMessages: teeExtraMessages
10713
10743
  };
10714
10744
  const stream = provider.chatStream(chatRequest);
10715
10745
  for await (const chunk of stream) {
@@ -10727,12 +10757,22 @@ ${summaryResult.content}`,
10727
10757
  });
10728
10758
  const pseudoMatch = detectPseudoToolCalls(fullContent);
10729
10759
  if (pseudoMatch) {
10730
- try {
10731
- unlinkSync4(saveToFile);
10732
- } catch {
10760
+ const cleaned = stripPseudoToolCalls(fullContent);
10761
+ if (looksLikeDocumentBody(cleaned)) {
10762
+ writeFileSync8(saveToFile, cleaned, "utf-8");
10763
+ fullContent = cleaned;
10764
+ const lines = cleaned.split("\n").length;
10765
+ const bytes = Buffer.byteLength(cleaned, "utf-8");
10766
+ summary = `File saved (with cleanup): ${saveToFile} (${lines} lines, ${bytes} bytes; pseudo-tool-call markup matching ${pseudoMatch} was stripped before save)`;
10767
+ undoStack.push(saveToFile, `save_last_response: ${saveToFile}`);
10768
+ } else {
10769
+ try {
10770
+ unlinkSync4(saveToFile);
10771
+ } catch {
10772
+ }
10773
+ isError = true;
10774
+ summary = `[save_last_response REJECTED] Your output was tool-call markup with no usable document body (matched: ${pseudoMatch}). ${saveToFile} was NOT saved. This fresh stream has NO tools \u2014 output is captured verbatim. STOP emitting <tool_call>, <function_calls>, <invoke>, <think>, or JSON tool blocks. Produce the document body NOW: start with a markdown heading and write the full content.`;
10733
10775
  }
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
10776
  } else {
10737
10777
  const lines = fullContent.split("\n").length;
10738
10778
  const bytes = Buffer.byteLength(fullContent, "utf-8");
@@ -11833,7 +11873,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11833
11873
  case "test": {
11834
11874
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
11835
11875
  try {
11836
- const { executeTests } = await import("./run-tests-XVK66TGP.js");
11876
+ const { executeTests } = await import("./run-tests-TTU6DNLI.js");
11837
11877
  const argStr = args.join(" ").trim();
11838
11878
  let testArgs = {};
11839
11879
  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-2TGWMUUT.js");
389
+ const { TaskOrchestrator } = await import("./task-orchestrator-IJFF7XLQ.js");
390
390
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
391
391
  let interrupted = false;
392
392
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  SNAPSHOT_PROMPT,
8
8
  SessionManager,
9
9
  SkillManager,
10
+ TEE_FINAL_USER_NUDGE,
10
11
  TOOL_CALL_REMINDER,
11
12
  autoTrimSessionIfNeeded,
12
13
  buildPhantomCorrectionMessage,
@@ -22,15 +23,18 @@ import {
22
23
  getPricing,
23
24
  hadPreviousWriteToolCalls,
24
25
  loadDevState,
26
+ looksLikeDocumentBody,
25
27
  parseSimpleYaml,
26
28
  persistToolRound,
27
29
  saveDevState,
28
30
  sessionHasMeaningfulContent,
29
- setupProxy
30
- } from "./chunk-YIHYQVV7.js";
31
+ setupProxy,
32
+ stripPseudoToolCalls,
33
+ stripToolCallReminder
34
+ } from "./chunk-GRIEVPII.js";
31
35
  import {
32
36
  ConfigManager
33
- } from "./chunk-BLJIPN2F.js";
37
+ } from "./chunk-VYDXL2PC.js";
34
38
  import {
35
39
  ToolExecutor,
36
40
  ToolRegistry,
@@ -49,10 +53,10 @@ import {
49
53
  spawnAgentContext,
50
54
  theme,
51
55
  undoStack
52
- } from "./chunk-LKWFPJIP.js";
56
+ } from "./chunk-5EVKTBUU.js";
53
57
  import "./chunk-3BICTI5M.js";
54
58
  import "./chunk-2DXY7UGF.js";
55
- import "./chunk-M77QS5QW.js";
59
+ import "./chunk-5LKW2GOF.js";
56
60
  import "./chunk-2ZD3YTVM.js";
57
61
  import {
58
62
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -75,7 +79,7 @@ import {
75
79
  SKILLS_DIR_NAME,
76
80
  VERSION,
77
81
  buildUserIdentityPrompt
78
- } from "./chunk-SQPICAHN.js";
82
+ } from "./chunk-CPIQXP7Q.js";
79
83
  import {
80
84
  formatGitContextForPrompt,
81
85
  getGitContext,
@@ -102,7 +106,7 @@ import { program } from "commander";
102
106
 
103
107
  // src/repl/repl.ts
104
108
  import * as readline from "readline";
105
- import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
109
+ import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
106
110
  import { join as join5, resolve as resolve2, extname as extname2, dirname as dirname3, basename as basename2 } from "path";
107
111
  import chalk4 from "chalk";
108
112
 
@@ -1596,7 +1600,7 @@ ${text}
1596
1600
  const { join: join6 } = await import("path");
1597
1601
  const { existsSync: existsSync6 } = await import("fs");
1598
1602
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1599
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-EVSUKVTR.js");
1603
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-NPGSV4QY.js");
1600
1604
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1601
1605
  const cwd = process.cwd();
1602
1606
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2646,7 +2650,7 @@ ${hint}` : "")
2646
2650
  usage: "/test [command|filter]",
2647
2651
  async execute(args, ctx) {
2648
2652
  try {
2649
- const { executeTests } = await import("./run-tests-M73756VV.js");
2653
+ const { executeTests } = await import("./run-tests-QBNLSYCO.js");
2650
2654
  const argStr = args.join(" ").trim();
2651
2655
  let testArgs = {};
2652
2656
  if (argStr) {
@@ -6239,7 +6243,8 @@ ${mcpBudgetNote}` : "");
6239
6243
  } else {
6240
6244
  const teeAc = this.setupStreamInterrupt();
6241
6245
  try {
6242
- const teeSystemPrompt = (systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
6246
+ const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
6247
+ const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
6243
6248
  const genStream = provider.chatStream({
6244
6249
  messages: apiMessages,
6245
6250
  model: effectiveModel,
@@ -6252,7 +6257,7 @@ ${mcpBudgetNote}` : "");
6252
6257
  thinking: modelParams.thinking,
6253
6258
  thinkingBudget: modelParams.thinkingBudget,
6254
6259
  signal: teeAc.signal,
6255
- ...extraMessages.length > 0 ? { _extraMessages: extraMessages } : {}
6260
+ _extraMessages: teeExtraMessages
6256
6261
  });
6257
6262
  const teeShowTokens = this.shouldShowTokens();
6258
6263
  const { content: genContent, usage: genUsage, tokensShown: teeTokShown } = await this.renderer.renderStream(
@@ -6261,22 +6266,65 @@ ${mcpBudgetNote}` : "");
6261
6266
  );
6262
6267
  const pseudoMatch = detectPseudoToolCalls(genContent);
6263
6268
  if (pseudoMatch) {
6269
+ const cleaned = stripPseudoToolCalls(genContent);
6270
+ if (looksLikeDocumentBody(cleaned)) {
6271
+ try {
6272
+ writeFileSync3(saveToFile, cleaned, "utf-8");
6273
+ process.stdout.write(theme.warning(
6274
+ `
6275
+ \u26A0 Salvaged save: stripped pseudo-tool-call markup (matched: ${pseudoMatch})
6276
+ ${saveToFile} now contains the cleaned document (${cleaned.length} chars; was ${genContent.length}).
6277
+
6278
+ `
6279
+ ));
6280
+ lastResponseStore.content = cleaned;
6281
+ if (genUsage) {
6282
+ roundUsage.inputTokens += genUsage.inputTokens;
6283
+ roundUsage.outputTokens += genUsage.outputTokens;
6284
+ roundUsage.cacheCreationTokens += genUsage.cacheCreationTokens ?? 0;
6285
+ roundUsage.cacheReadTokens += genUsage.cacheReadTokens ?? 0;
6286
+ }
6287
+ session.addMessage({ role: "assistant", content: cleaned, timestamp: /* @__PURE__ */ new Date() });
6288
+ this.events.emit("message.after", { content: cleaned });
6289
+ const lines2 = cleaned.split("\n").length;
6290
+ const bytes2 = Buffer.byteLength(cleaned, "utf-8");
6291
+ const okResults = result.toolCalls.map((tc) => ({
6292
+ callId: tc.id,
6293
+ content: tc.name === "save_last_response" ? `File saved (with cleanup): ${saveToFile} (${lines2} lines, ${bytes2} bytes; pseudo-tool-call markup was stripped before save)` : `[skipped: file already saved by tee streaming]`,
6294
+ isError: false
6295
+ }));
6296
+ const reasoningContent4 = "reasoningContent" in result ? result.reasoningContent : void 0;
6297
+ const newMsgs4 = provider.buildToolResultMessages(result.toolCalls, okResults, reasoningContent4);
6298
+ extraMessages.push(...newMsgs4);
6299
+ if (roundUsage.inputTokens > 0 || roundUsage.outputTokens > 0) {
6300
+ this.addSessionUsage(roundUsage, effectiveModel);
6301
+ session.addTokenUsage(roundUsage);
6302
+ if (teeShowTokens && !teeTokShown) {
6303
+ this.renderer.renderUsage(roundUsage, this.sessionTokenUsage);
6304
+ }
6305
+ }
6306
+ return;
6307
+ } catch (writeErr) {
6308
+ process.stderr.write(`[tee] salvage write failed: ${writeErr.message ?? writeErr}
6309
+ `);
6310
+ }
6311
+ }
6264
6312
  try {
6265
6313
  unlinkSync2(saveToFile);
6266
6314
  } catch {
6267
6315
  }
6268
6316
  process.stdout.write(theme.error(
6269
6317
  `
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.
6318
+ \u2717 Rejected save: response was pseudo-tool-call markup with no usable document body (matched: ${pseudoMatch})
6319
+ ${saveToFile} was deleted; asking model to retry.
6272
6320
 
6273
6321
  `
6274
6322
  ));
6275
6323
  const errorResults = result.toolCalls.map((tc) => ({
6276
6324
  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).
6325
+ content: tc.name === "save_last_response" ? `[save_last_response REJECTED] Your output was tool-call XML/JSON with no document body. ${saveToFile} was NOT saved.
6278
6326
 
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]`,
6327
+ This fresh stream has NO tools \u2014 output is captured verbatim. STOP emitting <tool_call>, <function_calls>, <invoke>, <think>, or JSON tool blocks. Produce the document body NOW: start with a markdown heading like "# \u5BA1\u8BA1\u62A5\u544A" and write the full report.` : `[skipped: save_last_response was rejected and other parallel calls are abandoned]`,
6280
6328
  isError: tc.name === "save_last_response"
6281
6329
  }));
6282
6330
  const reasoningContent3 = "reasoningContent" in result ? result.reasoningContent : void 0;
@@ -6834,7 +6882,7 @@ program.command("web").description("Start Web UI server with browser-based chat
6834
6882
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6835
6883
  process.exit(1);
6836
6884
  }
6837
- const { startWebServer } = await import("./server-YRX5VO5N.js");
6885
+ const { startWebServer } = await import("./server-42ZKNS56.js");
6838
6886
  await startWebServer({ port, host: options.host });
6839
6887
  });
6840
6888
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -6957,7 +7005,7 @@ program.command("sessions").description("List recent conversation sessions").act
6957
7005
  });
6958
7006
  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) => {
6959
7007
  try {
6960
- const batch = await import("./batch-V255CIMB.js");
7008
+ const batch = await import("./batch-XIDQVPRL.js");
6961
7009
  switch (action) {
6962
7010
  case "submit":
6963
7011
  if (!arg) {
@@ -7000,7 +7048,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7000
7048
  }
7001
7049
  });
7002
7050
  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) => {
7003
- const { startMcpServer } = await import("./server-54OXGXJ6.js");
7051
+ const { startMcpServer } = await import("./server-5TGJOWML.js");
7004
7052
  await startMcpServer({
7005
7053
  allowDestructive: !!options.allowDestructive,
7006
7054
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7127,7 +7175,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7127
7175
  }),
7128
7176
  config.get("customProviders")
7129
7177
  );
7130
- const { startHub } = await import("./hub-O3EQ2LDY.js");
7178
+ const { startHub } = await import("./hub-T34FCE5J.js");
7131
7179
  await startHub(
7132
7180
  {
7133
7181
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-M77QS5QW.js";
6
- import "./chunk-SQPICAHN.js";
5
+ } from "./chunk-5LKW2GOF.js";
6
+ import "./chunk-CPIQXP7Q.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-GFDEAYPM.js";
4
+ } from "./chunk-AVMGKU4E.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -9,6 +9,7 @@ import {
9
9
  ProviderRegistry,
10
10
  SessionManager,
11
11
  SkillManager,
12
+ TEE_FINAL_USER_NUDGE,
12
13
  TOOL_CALL_REMINDER,
13
14
  autoTrimSessionIfNeeded,
14
15
  computeCost,
@@ -18,12 +19,15 @@ import {
18
19
  getContentText,
19
20
  hadPreviousWriteToolCalls,
20
21
  loadDevState,
22
+ looksLikeDocumentBody,
21
23
  persistToolRound,
22
- setupProxy
23
- } from "./chunk-YIHYQVV7.js";
24
+ setupProxy,
25
+ stripPseudoToolCalls,
26
+ stripToolCallReminder
27
+ } from "./chunk-GRIEVPII.js";
24
28
  import {
25
29
  ConfigManager
26
- } from "./chunk-BLJIPN2F.js";
30
+ } from "./chunk-VYDXL2PC.js";
27
31
  import {
28
32
  ToolExecutor,
29
33
  ToolRegistry,
@@ -41,10 +45,10 @@ import {
41
45
  spawnAgentContext,
42
46
  truncateOutput,
43
47
  undoStack
44
- } from "./chunk-LKWFPJIP.js";
48
+ } from "./chunk-5EVKTBUU.js";
45
49
  import "./chunk-3BICTI5M.js";
46
50
  import "./chunk-2DXY7UGF.js";
47
- import "./chunk-M77QS5QW.js";
51
+ import "./chunk-5LKW2GOF.js";
48
52
  import "./chunk-2ZD3YTVM.js";
49
53
  import {
50
54
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -64,7 +68,7 @@ import {
64
68
  SKILLS_DIR_NAME,
65
69
  VERSION,
66
70
  buildUserIdentityPrompt
67
- } from "./chunk-SQPICAHN.js";
71
+ } from "./chunk-CPIQXP7Q.js";
68
72
  import {
69
73
  formatGitContextForPrompt,
70
74
  getGitContext,
@@ -1259,7 +1263,8 @@ ${summaryResult.content}`,
1259
1263
  try {
1260
1264
  mkdirSync(dirname(saveToFile), { recursive: true });
1261
1265
  fileStream = createWriteStream(saveToFile, { encoding: "utf-8" });
1262
- const teeSystemPrompt = (systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
1266
+ const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
1267
+ const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
1263
1268
  const chatRequest = {
1264
1269
  messages: apiMessages,
1265
1270
  model: this.currentModel,
@@ -1272,7 +1277,7 @@ ${summaryResult.content}`,
1272
1277
  thinking: modelParams.thinking,
1273
1278
  thinkingBudget: modelParams.thinkingBudget,
1274
1279
  signal: ac.signal,
1275
- ...extraMessages.length > 0 ? { _extraMessages: extraMessages } : {}
1280
+ _extraMessages: teeExtraMessages
1276
1281
  };
1277
1282
  const stream = provider.chatStream(chatRequest);
1278
1283
  for await (const chunk of stream) {
@@ -1290,12 +1295,22 @@ ${summaryResult.content}`,
1290
1295
  });
1291
1296
  const pseudoMatch = detectPseudoToolCalls(fullContent);
1292
1297
  if (pseudoMatch) {
1293
- try {
1294
- unlinkSync(saveToFile);
1295
- } catch {
1298
+ const cleaned = stripPseudoToolCalls(fullContent);
1299
+ if (looksLikeDocumentBody(cleaned)) {
1300
+ writeFileSync(saveToFile, cleaned, "utf-8");
1301
+ fullContent = cleaned;
1302
+ const lines = cleaned.split("\n").length;
1303
+ const bytes = Buffer.byteLength(cleaned, "utf-8");
1304
+ summary = `File saved (with cleanup): ${saveToFile} (${lines} lines, ${bytes} bytes; pseudo-tool-call markup matching ${pseudoMatch} was stripped before save)`;
1305
+ undoStack.push(saveToFile, `save_last_response: ${saveToFile}`);
1306
+ } else {
1307
+ try {
1308
+ unlinkSync(saveToFile);
1309
+ } catch {
1310
+ }
1311
+ isError = true;
1312
+ summary = `[save_last_response REJECTED] Your output was tool-call markup with no usable document body (matched: ${pseudoMatch}). ${saveToFile} was NOT saved. This fresh stream has NO tools \u2014 output is captured verbatim. STOP emitting <tool_call>, <function_calls>, <invoke>, <think>, or JSON tool blocks. Produce the document body NOW: start with a markdown heading and write the full content.`;
1296
1313
  }
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
1314
  } else {
1300
1315
  const lines = fullContent.split("\n").length;
1301
1316
  const bytes = Buffer.byteLength(fullContent, "utf-8");
@@ -2396,7 +2411,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2396
2411
  case "test": {
2397
2412
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2398
2413
  try {
2399
- const { executeTests } = await import("./run-tests-M73756VV.js");
2414
+ const { executeTests } = await import("./run-tests-QBNLSYCO.js");
2400
2415
  const argStr = args.join(" ").trim();
2401
2416
  let testArgs = {};
2402
2417
  if (argStr) {
@@ -3,14 +3,14 @@ import {
3
3
  ToolRegistry,
4
4
  getDangerLevel,
5
5
  schemaToJsonSchema
6
- } from "./chunk-LKWFPJIP.js";
6
+ } from "./chunk-5EVKTBUU.js";
7
7
  import "./chunk-3BICTI5M.js";
8
8
  import "./chunk-2DXY7UGF.js";
9
- import "./chunk-M77QS5QW.js";
9
+ import "./chunk-5LKW2GOF.js";
10
10
  import "./chunk-2ZD3YTVM.js";
11
11
  import {
12
12
  VERSION
13
- } from "./chunk-SQPICAHN.js";
13
+ } from "./chunk-CPIQXP7Q.js";
14
14
  import "./chunk-4BKXL7SM.js";
15
15
  import "./chunk-7ZJN4KLV.js";
16
16
  import "./chunk-KHYD3WXE.js";
@@ -4,14 +4,14 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-LKWFPJIP.js";
7
+ } from "./chunk-5EVKTBUU.js";
8
8
  import "./chunk-3BICTI5M.js";
9
9
  import "./chunk-2DXY7UGF.js";
10
- import "./chunk-M77QS5QW.js";
10
+ import "./chunk-5LKW2GOF.js";
11
11
  import "./chunk-2ZD3YTVM.js";
12
12
  import {
13
13
  SUBAGENT_ALLOWED_TOOLS
14
- } from "./chunk-SQPICAHN.js";
14
+ } from "./chunk-CPIQXP7Q.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.110",
3
+ "version": "0.4.111",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",