jinzd-ai-cli 0.4.179 → 0.4.181
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/dist/{batch-OEAVIMNC.js → batch-DG5STMLZ.js} +2 -2
- package/dist/{chunk-M4BEF3S5.js → chunk-4VB6UP4W.js} +1 -1
- package/dist/{chunk-2TWARH5X.js → chunk-5LK7H45B.js} +331 -22
- package/dist/{chunk-FLJ5RSDY.js → chunk-6SY45V62.js} +2 -2
- package/dist/{chunk-GDZ7ITJM.js → chunk-CPZ7KG3Y.js} +1 -1
- package/dist/{chunk-IWBPFOYL.js → chunk-D4ZTYYBU.js} +1 -1
- package/dist/{chunk-NLOL7ZNV.js → chunk-D774AWKW.js} +1 -1
- package/dist/{chunk-VQQPCJ7I.js → chunk-ORXPLVM7.js} +3 -3
- package/dist/{chunk-7ZJKEL2S.js → chunk-P2VFMUR5.js} +1 -1
- package/dist/{chunk-A4VW4QJ6.js → chunk-ZWQ36YFI.js} +1 -1
- package/dist/{ci-2EUCJ23L.js → ci-UXVUG3LC.js} +3 -3
- package/dist/{constants-2XKWY6LU.js → constants-5DFFSPWI.js} +1 -1
- package/dist/{doctor-cli-E6FTBUEK.js → doctor-cli-GQBR6RI6.js} +5 -5
- package/dist/electron-server.js +380 -182
- package/dist/{hub-5UDU2CL6.js → hub-N75OQVNY.js} +1 -1
- package/dist/index.js +100 -317
- package/dist/{run-tests-SU6NWCF6.js → run-tests-673ABQQE.js} +2 -2
- package/dist/{run-tests-OB7X333R.js → run-tests-HZVKHQ33.js} +1 -1
- package/dist/{server-HHZHSKCO.js → server-RCUIX4R5.js} +4 -4
- package/dist/{server-LOSA6ROG.js → server-SLDE3AML.js} +77 -167
- package/dist/{task-orchestrator-SGBOJ7DD.js → task-orchestrator-42D5LBAX.js} +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
saveDevState,
|
|
19
19
|
sessionHasMeaningfulContent,
|
|
20
20
|
setupProxy
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-6SY45V62.js";
|
|
22
22
|
import {
|
|
23
23
|
ToolExecutor,
|
|
24
24
|
ToolRegistry,
|
|
@@ -37,10 +37,10 @@ import {
|
|
|
37
37
|
spawnAgentContext,
|
|
38
38
|
theme,
|
|
39
39
|
undoStack
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-ORXPLVM7.js";
|
|
41
41
|
import "./chunk-HDSKW7Q3.js";
|
|
42
42
|
import "./chunk-ZWVIDFGY.js";
|
|
43
|
-
import "./chunk-
|
|
43
|
+
import "./chunk-D4ZTYYBU.js";
|
|
44
44
|
import {
|
|
45
45
|
SessionManager,
|
|
46
46
|
getContentText
|
|
@@ -49,15 +49,25 @@ import {
|
|
|
49
49
|
getConfigDirUsage,
|
|
50
50
|
listRecentCrashes,
|
|
51
51
|
writeCrashLog
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-ZWQ36YFI.js";
|
|
53
53
|
import {
|
|
54
|
+
BudgetWarner,
|
|
54
55
|
CONTENT_ONLY_STREAM_REMINDER,
|
|
56
|
+
ContextPressureMonitor,
|
|
57
|
+
EmptyResponseGuard,
|
|
58
|
+
FreeRoundTracker,
|
|
55
59
|
HALLUCINATION_CORRECTION_MESSAGE,
|
|
56
60
|
ProviderRegistry,
|
|
57
61
|
TEE_FINAL_USER_NUDGE,
|
|
58
62
|
TOOL_CALL_REMINDER,
|
|
63
|
+
ThinkTagFilter,
|
|
64
|
+
accumulateUsage,
|
|
59
65
|
buildPhantomCorrectionMessage,
|
|
66
|
+
buildRoundBudgetHint,
|
|
67
|
+
buildRoundsExhaustedPrompt,
|
|
68
|
+
buildUserStopMessage,
|
|
60
69
|
buildWriteRoundReminder,
|
|
70
|
+
consumeToolCallStream,
|
|
61
71
|
detectMetaNarration,
|
|
62
72
|
detectPseudoToolCalls,
|
|
63
73
|
detectsHallucinatedFileOp,
|
|
@@ -67,18 +77,19 @@ import {
|
|
|
67
77
|
hadPreviousWriteToolCalls,
|
|
68
78
|
looksLikeDocumentBody,
|
|
69
79
|
stripPseudoToolCalls,
|
|
70
|
-
stripToolCallReminder
|
|
71
|
-
|
|
80
|
+
stripToolCallReminder,
|
|
81
|
+
summarizeRecentTools
|
|
82
|
+
} from "./chunk-5LK7H45B.js";
|
|
72
83
|
import {
|
|
73
84
|
getStatsSnapshot,
|
|
74
85
|
getTopFailingTools,
|
|
75
86
|
getTopUsedTools,
|
|
76
87
|
installFlushOnExit
|
|
77
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-4VB6UP4W.js";
|
|
78
89
|
import "./chunk-HIU2SH4V.js";
|
|
79
90
|
import {
|
|
80
91
|
ConfigManager
|
|
81
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-P2VFMUR5.js";
|
|
82
93
|
import {
|
|
83
94
|
AuthError,
|
|
84
95
|
ProviderError,
|
|
@@ -105,7 +116,7 @@ import {
|
|
|
105
116
|
SKILLS_DIR_NAME,
|
|
106
117
|
VERSION,
|
|
107
118
|
buildUserIdentityPrompt
|
|
108
|
-
} from "./chunk-
|
|
119
|
+
} from "./chunk-CPZ7KG3Y.js";
|
|
109
120
|
import {
|
|
110
121
|
formatGitContextForPrompt,
|
|
111
122
|
getGitContext,
|
|
@@ -208,13 +219,6 @@ function isInterruptedSession(messages) {
|
|
|
208
219
|
import chalk from "chalk";
|
|
209
220
|
import { createWriteStream, mkdirSync } from "fs";
|
|
210
221
|
import { dirname } from "path";
|
|
211
|
-
function partialTagTail(s, tag) {
|
|
212
|
-
const max = Math.min(s.length, tag.length - 1);
|
|
213
|
-
for (let k = max; k > 0; k--) {
|
|
214
|
-
if (s.endsWith(tag.slice(0, k))) return k;
|
|
215
|
-
}
|
|
216
|
-
return 0;
|
|
217
|
-
}
|
|
218
222
|
function fmtContextWindow(tokens) {
|
|
219
223
|
if (tokens >= 1e6) return `${Math.round(tokens / 1e5) / 10}M`;
|
|
220
224
|
if (tokens >= 1e3) return `${Math.round(tokens / 1024)}K`;
|
|
@@ -495,48 +499,15 @@ var Renderer = class {
|
|
|
495
499
|
}
|
|
496
500
|
let fullContent = "";
|
|
497
501
|
let usage;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if (!inThinking) {
|
|
505
|
-
const open = thinkBuf.indexOf("<think>");
|
|
506
|
-
if (open === -1) {
|
|
507
|
-
const keep = partialTagTail(thinkBuf, "<think>");
|
|
508
|
-
out += thinkBuf.slice(0, thinkBuf.length - keep);
|
|
509
|
-
thinkBuf = thinkBuf.slice(thinkBuf.length - keep);
|
|
510
|
-
break;
|
|
511
|
-
}
|
|
512
|
-
out += thinkBuf.slice(0, open);
|
|
513
|
-
thinkBuf = thinkBuf.slice(open + "<think>".length);
|
|
514
|
-
inThinking = true;
|
|
515
|
-
} else {
|
|
516
|
-
const close = thinkBuf.indexOf("</think>");
|
|
517
|
-
if (close === -1) {
|
|
518
|
-
const keep = partialTagTail(thinkBuf, "</think>");
|
|
519
|
-
thinkBuf = thinkBuf.slice(thinkBuf.length - keep);
|
|
520
|
-
break;
|
|
521
|
-
}
|
|
522
|
-
thinkBuf = thinkBuf.slice(close + "</think>".length);
|
|
523
|
-
inThinking = false;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
if (out) {
|
|
527
|
-
process.stdout.write(out);
|
|
528
|
-
if (fileStream) fileStream.write(out);
|
|
529
|
-
fullContent += out;
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
const flushTail = () => {
|
|
533
|
-
if (!inThinking && thinkBuf) {
|
|
534
|
-
process.stdout.write(thinkBuf);
|
|
535
|
-
if (fileStream) fileStream.write(thinkBuf);
|
|
536
|
-
fullContent += thinkBuf;
|
|
537
|
-
thinkBuf = "";
|
|
538
|
-
}
|
|
502
|
+
const thinkFilter = new ThinkTagFilter();
|
|
503
|
+
const writeVisible = (visible) => {
|
|
504
|
+
if (!visible) return;
|
|
505
|
+
process.stdout.write(visible);
|
|
506
|
+
if (fileStream) fileStream.write(visible);
|
|
507
|
+
fullContent += visible;
|
|
539
508
|
};
|
|
509
|
+
const emitText = (raw) => writeVisible(thinkFilter.push(raw));
|
|
510
|
+
const flushTail = () => writeVisible(thinkFilter.flush());
|
|
540
511
|
let interrupted = false;
|
|
541
512
|
let streamErr = null;
|
|
542
513
|
try {
|
|
@@ -1786,7 +1757,7 @@ No tools match "${filter}".
|
|
|
1786
1757
|
const { join: join6 } = await import("path");
|
|
1787
1758
|
const { existsSync: existsSync6 } = await import("fs");
|
|
1788
1759
|
const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
|
|
1789
|
-
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-
|
|
1760
|
+
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-5DFFSPWI.js");
|
|
1790
1761
|
const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
|
|
1791
1762
|
const cwd = process.cwd();
|
|
1792
1763
|
const projectRoot = getGitRoot2(cwd) ?? cwd;
|
|
@@ -2847,7 +2818,7 @@ ${hint}` : "")
|
|
|
2847
2818
|
usage: "/test [command|filter]",
|
|
2848
2819
|
async execute(args, ctx) {
|
|
2849
2820
|
try {
|
|
2850
|
-
const { executeTests } = await import("./run-tests-
|
|
2821
|
+
const { executeTests } = await import("./run-tests-673ABQQE.js");
|
|
2851
2822
|
const argStr = args.join(" ").trim();
|
|
2852
2823
|
let testArgs = {};
|
|
2853
2824
|
if (argStr) {
|
|
@@ -4694,17 +4665,8 @@ ${content}
|
|
|
4694
4665
|
parts.push(...imageParts);
|
|
4695
4666
|
return { parts, hasImage: imageParts.length > 0, refs };
|
|
4696
4667
|
}
|
|
4697
|
-
var FREE_ROUND_TOOLS = /* @__PURE__ */ new Set(["write_todos"]);
|
|
4698
|
-
var MAX_CONSECUTIVE_FREE_ROUNDS = 3;
|
|
4699
4668
|
var MAX_REPEATED_TOOL_CALLS = 2;
|
|
4700
4669
|
var DEFAULT_AUTO_PAUSE_INTERVAL = 50;
|
|
4701
|
-
function partialTagTail2(s, tag) {
|
|
4702
|
-
const max = Math.min(s.length, tag.length - 1);
|
|
4703
|
-
for (let k = max; k > 0; k--) {
|
|
4704
|
-
if (s.endsWith(tag.slice(0, k))) return k;
|
|
4705
|
-
}
|
|
4706
|
-
return 0;
|
|
4707
|
-
}
|
|
4708
4670
|
function fmtTokens(n) {
|
|
4709
4671
|
if (n >= 1e6) return `${Math.round(n / 1e5) / 10}M`;
|
|
4710
4672
|
if (n >= 1e3) return `${Math.round(n / 1024)}K`;
|
|
@@ -6407,14 +6369,12 @@ Session '${this.resumeSessionId}' not found.
|
|
|
6407
6369
|
/**
|
|
6408
6370
|
* 消费流式工具调用事件生成器,实时渲染文本内容和工具名称,
|
|
6409
6371
|
* 累积完整工具调用参数后返回结构化结果。
|
|
6372
|
+
*
|
|
6373
|
+
* v0.4.181: 累积与决策(<think> 折叠 / 截断 JSON 修复 / done 守卫 /
|
|
6374
|
+
* index 键累积)委托给 core/agent-loop 的统一消费器(与 Web 端同一实现),
|
|
6375
|
+
* 此处只保留终端呈现(spinner / theme 渲染)。
|
|
6410
6376
|
*/
|
|
6411
6377
|
async consumeToolStream(stream, spinner) {
|
|
6412
|
-
const textParts = [];
|
|
6413
|
-
const toolCallAccumulators = /* @__PURE__ */ new Map();
|
|
6414
|
-
let usage;
|
|
6415
|
-
let rawContent;
|
|
6416
|
-
let reasoningContent;
|
|
6417
|
-
let finishReason;
|
|
6418
6378
|
let spinnerStopped = false;
|
|
6419
6379
|
const stopSpinner = () => {
|
|
6420
6380
|
if (!spinnerStopped) {
|
|
@@ -6422,135 +6382,36 @@ Session '${this.resumeSessionId}' not found.
|
|
|
6422
6382
|
spinnerStopped = true;
|
|
6423
6383
|
}
|
|
6424
6384
|
};
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
let thinkBuf = "";
|
|
6428
|
-
const emitText = (raw) => {
|
|
6429
|
-
thinkBuf += raw;
|
|
6430
|
-
let out = "";
|
|
6431
|
-
while (thinkBuf.length > 0) {
|
|
6432
|
-
if (!inThink) {
|
|
6433
|
-
const open = thinkBuf.indexOf("<think>");
|
|
6434
|
-
if (open === -1) {
|
|
6435
|
-
const keep = partialTagTail2(thinkBuf, "<think>");
|
|
6436
|
-
out += thinkBuf.slice(0, thinkBuf.length - keep);
|
|
6437
|
-
thinkBuf = thinkBuf.slice(thinkBuf.length - keep);
|
|
6438
|
-
break;
|
|
6439
|
-
}
|
|
6440
|
-
out += thinkBuf.slice(0, open);
|
|
6441
|
-
thinkBuf = thinkBuf.slice(open + "<think>".length);
|
|
6442
|
-
inThink = true;
|
|
6443
|
-
thinkShown = true;
|
|
6444
|
-
} else {
|
|
6445
|
-
const close = thinkBuf.indexOf("</think>");
|
|
6446
|
-
if (close === -1) {
|
|
6447
|
-
const keep = partialTagTail2(thinkBuf, "</think>");
|
|
6448
|
-
thinkBuf = thinkBuf.slice(thinkBuf.length - keep);
|
|
6449
|
-
break;
|
|
6450
|
-
}
|
|
6451
|
-
thinkBuf = thinkBuf.slice(close + "</think>".length);
|
|
6452
|
-
inThink = false;
|
|
6453
|
-
}
|
|
6454
|
-
}
|
|
6455
|
-
if (out) {
|
|
6456
|
-
process.stdout.write(out);
|
|
6457
|
-
textParts.push(out);
|
|
6458
|
-
}
|
|
6459
|
-
};
|
|
6460
|
-
try {
|
|
6461
|
-
for await (const event of stream) {
|
|
6462
|
-
switch (event.type) {
|
|
6463
|
-
case "text_delta":
|
|
6464
|
-
stopSpinner();
|
|
6465
|
-
emitText(event.delta);
|
|
6466
|
-
break;
|
|
6467
|
-
case "thinking_start":
|
|
6468
|
-
stopSpinner();
|
|
6469
|
-
process.stdout.write(theme.dim("<think>"));
|
|
6470
|
-
break;
|
|
6471
|
-
case "thinking_delta":
|
|
6472
|
-
break;
|
|
6473
|
-
case "thinking_end":
|
|
6474
|
-
process.stdout.write(theme.dim("</think>"));
|
|
6475
|
-
break;
|
|
6476
|
-
case "tool_call_start":
|
|
6477
|
-
stopSpinner();
|
|
6478
|
-
process.stdout.write(
|
|
6479
|
-
theme.dim(`
|
|
6480
|
-
\u2699 Streaming: `) + theme.toolCall(event.name) + theme.dim("...\n")
|
|
6481
|
-
);
|
|
6482
|
-
toolCallAccumulators.set(event.index, {
|
|
6483
|
-
id: event.id,
|
|
6484
|
-
name: event.name,
|
|
6485
|
-
arguments: ""
|
|
6486
|
-
});
|
|
6487
|
-
break;
|
|
6488
|
-
case "tool_call_delta": {
|
|
6489
|
-
const acc = toolCallAccumulators.get(event.index);
|
|
6490
|
-
if (acc) {
|
|
6491
|
-
acc.arguments += event.argumentsDelta;
|
|
6492
|
-
}
|
|
6493
|
-
break;
|
|
6494
|
-
}
|
|
6495
|
-
case "tool_call_end":
|
|
6496
|
-
break;
|
|
6497
|
-
case "done":
|
|
6498
|
-
if (event.usage) usage = event.usage;
|
|
6499
|
-
if (event.rawContent) rawContent = event.rawContent;
|
|
6500
|
-
if (event.reasoningContent) reasoningContent = event.reasoningContent;
|
|
6501
|
-
if (event.finishReason) finishReason = event.finishReason;
|
|
6502
|
-
break;
|
|
6503
|
-
}
|
|
6504
|
-
}
|
|
6505
|
-
} catch (err) {
|
|
6506
|
-
if (err instanceof Error && (err.name === "AbortError" || err.message.includes("aborted"))) {
|
|
6385
|
+
const result = await consumeToolCallStream(stream, {
|
|
6386
|
+
onText: (visible) => {
|
|
6507
6387
|
stopSpinner();
|
|
6508
|
-
process.stdout.write(
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
}
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
try {
|
|
6529
|
-
parsedArgs = JSON.parse(acc.arguments || "{}");
|
|
6530
|
-
} catch {
|
|
6531
|
-
const truncated = acc.arguments.trimEnd();
|
|
6532
|
-
const lastComma = truncated.lastIndexOf(",");
|
|
6533
|
-
const fixed = lastComma > 0 ? truncated.slice(0, lastComma) + "}" : truncated.slice(0, truncated.indexOf("{") + 1) + "}";
|
|
6534
|
-
try {
|
|
6535
|
-
parsedArgs = JSON.parse(fixed);
|
|
6536
|
-
} catch {
|
|
6537
|
-
parsedArgs = {};
|
|
6538
|
-
}
|
|
6388
|
+
process.stdout.write(visible);
|
|
6389
|
+
},
|
|
6390
|
+
onThinkingStart: () => {
|
|
6391
|
+
stopSpinner();
|
|
6392
|
+
process.stdout.write(theme.dim("<think>"));
|
|
6393
|
+
},
|
|
6394
|
+
// thinking_delta 内容折叠,不显示详情(与现有行为一致)
|
|
6395
|
+
onThinkingEnd: () => {
|
|
6396
|
+
process.stdout.write(theme.dim("</think>"));
|
|
6397
|
+
},
|
|
6398
|
+
onToolCallStart: (_index, _id, name) => {
|
|
6399
|
+
stopSpinner();
|
|
6400
|
+
process.stdout.write(
|
|
6401
|
+
theme.dim(`
|
|
6402
|
+
\u2699 Streaming: `) + theme.toolCall(name) + theme.dim("...\n")
|
|
6403
|
+
);
|
|
6404
|
+
},
|
|
6405
|
+
onWarn: (message) => {
|
|
6406
|
+
process.stderr.write(`[warn] ${message}
|
|
6407
|
+
`);
|
|
6539
6408
|
}
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
});
|
|
6409
|
+
});
|
|
6410
|
+
if (result.aborted) {
|
|
6411
|
+
stopSpinner();
|
|
6412
|
+
process.stdout.write(theme.dim("\n[interrupted]\n"));
|
|
6545
6413
|
}
|
|
6546
|
-
return
|
|
6547
|
-
textContent: textParts.join(""),
|
|
6548
|
-
toolCalls,
|
|
6549
|
-
usage,
|
|
6550
|
-
rawContent,
|
|
6551
|
-
reasoningContent,
|
|
6552
|
-
finishReason
|
|
6553
|
-
};
|
|
6414
|
+
return result;
|
|
6554
6415
|
}
|
|
6555
6416
|
async handleChatWithTools(provider, messages, modelOverride) {
|
|
6556
6417
|
const session = this.sessions.current;
|
|
@@ -6592,25 +6453,7 @@ Session '${this.resumeSessionId}' not found.
|
|
|
6592
6453
|
const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
|
|
6593
6454
|
const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : DEFAULT_AUTO_PAUSE_INTERVAL;
|
|
6594
6455
|
const { stable: toolStable, volatile: toolVolatile } = this.buildCurrentSystemPrompt();
|
|
6595
|
-
const
|
|
6596
|
-
- Every ${autoPauseInterval} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.` : "";
|
|
6597
|
-
const roundBudgetHint = this.planMode ? `
|
|
6598
|
-
|
|
6599
|
-
[Tool Round Budget \u2014 Plan Mode]
|
|
6600
|
-
You have a maximum of ${maxToolRounds} tool call rounds. You are in READ-ONLY Plan Mode:
|
|
6601
|
-
- Only use: read_file, list_dir, grep_files, glob_files, ask_user, write_todos
|
|
6602
|
-
- Do NOT attempt to call bash, write_file, edit_file \u2014 they are disabled
|
|
6603
|
-
- Do NOT write shell commands or code blocks as a substitute for tool calls
|
|
6604
|
-
- Do NOT read the same file more than once
|
|
6605
|
-
- Call write_todos ONCE to present your plan, then give a text summary
|
|
6606
|
-
- If the user asks you to execute anything, respond: "Please type /plan execute to switch to execute mode."${pauseHint}` : `
|
|
6607
|
-
|
|
6608
|
-
[Tool Round Budget]
|
|
6609
|
-
You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan efficiently:
|
|
6610
|
-
- Prefer batch operations (e.g. global find-and-replace) over repetitive single edits.
|
|
6611
|
-
- Do NOT read the same file more than once \u2014 use the content from previous reads.
|
|
6612
|
-
- Prioritize the most critical tasks first in case rounds run out.
|
|
6613
|
-
- When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
|
|
6456
|
+
const roundBudgetHint = buildRoundBudgetHint({ maxToolRounds, autoPauseInterval, planMode: this.planMode });
|
|
6614
6457
|
const systemPrompt = toolStable + TOOL_CALL_REMINDER + roundBudgetHint + (mcpBudgetNote ? `
|
|
6615
6458
|
|
|
6616
6459
|
${mcpBudgetNote}` : "");
|
|
@@ -6620,22 +6463,15 @@ ${mcpBudgetNote}` : "");
|
|
|
6620
6463
|
const spinner = this.renderer.showSpinner("Thinking...");
|
|
6621
6464
|
const roundUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
6622
6465
|
const supportsStreamingTools = useStreaming && typeof provider.chatWithToolsStream === "function";
|
|
6623
|
-
let consecutiveFreeRounds = 0;
|
|
6624
6466
|
let lastToolCallSignature = "";
|
|
6625
6467
|
let repeatedToolCallCount = 0;
|
|
6626
|
-
let emptyResponseRetries = 0;
|
|
6627
|
-
let warnedCtx80 = false;
|
|
6628
6468
|
const roundToolHistory = [];
|
|
6469
|
+
const budgetWarner = new BudgetWarner(maxToolRounds);
|
|
6470
|
+
const emptyGuard = new EmptyResponseGuard();
|
|
6471
|
+
const ctxMonitor = new ContextPressureMonitor();
|
|
6472
|
+
const freeRounds = new FreeRoundTracker();
|
|
6629
6473
|
this.setupInterjectionListener();
|
|
6630
6474
|
try {
|
|
6631
|
-
const warnNoteAt = Math.max(10, Math.floor(maxToolRounds * 0.2));
|
|
6632
|
-
const warnLowAt = Math.max(5, Math.floor(maxToolRounds * 0.1));
|
|
6633
|
-
const warnCriticalAt = Math.max(3, Math.floor(maxToolRounds * 0.05));
|
|
6634
|
-
const warnLowEff = Math.min(warnLowAt, warnNoteAt - 1);
|
|
6635
|
-
const warnCriticalEff = Math.min(warnCriticalAt, warnLowEff - 1);
|
|
6636
|
-
let warnedNote = false;
|
|
6637
|
-
let warnedLow = false;
|
|
6638
|
-
let warnedCritical = false;
|
|
6639
6475
|
for (let round = 0; round < maxToolRounds; round++) {
|
|
6640
6476
|
this.toolExecutor.setRoundInfo(round + 1, maxToolRounds);
|
|
6641
6477
|
if (this.toolExecutor.pendingSlashCommand) {
|
|
@@ -6652,29 +6488,14 @@ ${mcpBudgetNote}` : "");
|
|
|
6652
6488
|
`));
|
|
6653
6489
|
extraMessages.push({ role: "user", content: cmd });
|
|
6654
6490
|
}
|
|
6655
|
-
const
|
|
6656
|
-
if (
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
});
|
|
6662
|
-
process.stdout.write(theme.error(` \u{1F6A8} Critical: ${roundsLeft} rounds remaining
|
|
6491
|
+
const budgetWarning = budgetWarner.check(maxToolRounds - round);
|
|
6492
|
+
if (budgetWarning) {
|
|
6493
|
+
extraMessages.push({ role: "user", content: budgetWarning.injectMessage });
|
|
6494
|
+
if (budgetWarning.displayMessage) {
|
|
6495
|
+
const paint = budgetWarning.level === "critical" ? theme.error : theme.warning;
|
|
6496
|
+
process.stdout.write(paint(` ${budgetWarning.displayMessage}
|
|
6663
6497
|
`));
|
|
6664
|
-
|
|
6665
|
-
warnedLow = true;
|
|
6666
|
-
extraMessages.push({
|
|
6667
|
-
role: "user",
|
|
6668
|
-
content: `\u26A0\uFE0F Budget warning: Only ${roundsLeft} tool rounds remaining. Prioritize completing the most critical task. Use efficient approaches (batch edits, fewer reads). If you cannot finish everything, summarize what's done and what remains.`
|
|
6669
|
-
});
|
|
6670
|
-
process.stdout.write(theme.warning(` \u26A0\uFE0F Low budget: ${roundsLeft} rounds remaining
|
|
6671
|
-
`));
|
|
6672
|
-
} else if (!warnedNote && roundsLeft <= warnNoteAt) {
|
|
6673
|
-
warnedNote = true;
|
|
6674
|
-
extraMessages.push({
|
|
6675
|
-
role: "user",
|
|
6676
|
-
content: `\u{1F4CA} Budget note: ${roundsLeft} tool rounds remaining out of ${maxToolRounds}. Plan your remaining work efficiently \u2014 use batch operations (e.g., replaceAll) when possible.`
|
|
6677
|
-
});
|
|
6498
|
+
}
|
|
6678
6499
|
}
|
|
6679
6500
|
if (this._userInterjection) {
|
|
6680
6501
|
const msg = this._userInterjection;
|
|
@@ -6686,13 +6507,13 @@ ${mcpBudgetNote}` : "");
|
|
|
6686
6507
|
const ctxWindow = this.getContextWindowSize();
|
|
6687
6508
|
if (ctxWindow > 0) {
|
|
6688
6509
|
const reqTokens = this.estimateRequestTokens(systemPrompt, extraMessages);
|
|
6689
|
-
const
|
|
6690
|
-
if (
|
|
6510
|
+
const pressure = ctxMonitor.check(reqTokens, ctxWindow);
|
|
6511
|
+
if (pressure.action === "abort") {
|
|
6691
6512
|
spinner.stop();
|
|
6692
6513
|
process.stderr.write(
|
|
6693
6514
|
theme.error(
|
|
6694
6515
|
`
|
|
6695
|
-
\u26A0 Context at ${Math.round(
|
|
6516
|
+
\u26A0 Context at ${Math.round(pressure.ratio * 100)}% of ${fmtTokens(ctxWindow)} \u2014 aborting agentic loop before API rejection.
|
|
6696
6517
|
`
|
|
6697
6518
|
)
|
|
6698
6519
|
);
|
|
@@ -6712,20 +6533,16 @@ ${mcpBudgetNote}` : "");
|
|
|
6712
6533
|
}
|
|
6713
6534
|
}
|
|
6714
6535
|
return;
|
|
6715
|
-
} else if (
|
|
6716
|
-
warnedCtx80 = true;
|
|
6536
|
+
} else if (pressure.action === "warn") {
|
|
6717
6537
|
spinner.stop();
|
|
6718
6538
|
process.stdout.write(
|
|
6719
6539
|
theme.warning(
|
|
6720
6540
|
`
|
|
6721
|
-
\u26A0 Context at ${Math.round(
|
|
6541
|
+
\u26A0 Context at ${Math.round(pressure.ratio * 100)}% of ${fmtTokens(ctxWindow)} \u2014 asking AI to wrap up.
|
|
6722
6542
|
`
|
|
6723
6543
|
)
|
|
6724
6544
|
);
|
|
6725
|
-
extraMessages.push({
|
|
6726
|
-
role: "user",
|
|
6727
|
-
content: `\u26A0\uFE0F Context pressure: ~${Math.round(reqRatio * 100)}% of the ${fmtTokens(ctxWindow)} context window is used. Avoid reading more files or running broad scans. Finish the current critical step, then produce a final summary. Every unnecessary tool call now risks breaking the conversation.`
|
|
6728
|
-
});
|
|
6545
|
+
extraMessages.push({ role: "user", content: pressure.injectMessage });
|
|
6729
6546
|
spinner.start(`Thinking... (round ${round + 1}/${maxToolRounds})`);
|
|
6730
6547
|
}
|
|
6731
6548
|
}
|
|
@@ -6797,12 +6614,7 @@ ${mcpBudgetNote}` : "");
|
|
|
6797
6614
|
(p, m) => p.chatWithTools({ ...chatRequest, model: m }, toolDefs)
|
|
6798
6615
|
);
|
|
6799
6616
|
}
|
|
6800
|
-
|
|
6801
|
-
roundUsage.inputTokens += result.usage.inputTokens;
|
|
6802
|
-
roundUsage.outputTokens += result.usage.outputTokens;
|
|
6803
|
-
roundUsage.cacheCreationTokens += result.usage.cacheCreationTokens ?? 0;
|
|
6804
|
-
roundUsage.cacheReadTokens += result.usage.cacheReadTokens ?? 0;
|
|
6805
|
-
}
|
|
6617
|
+
accumulateUsage(roundUsage, result.usage);
|
|
6806
6618
|
if ("content" in result) {
|
|
6807
6619
|
const hasWriteTools = toolDefs.some((t) => t.name === "write_file" || t.name === "edit_file");
|
|
6808
6620
|
const alreadyWrote = hadPreviousWriteToolCalls(extraMessages);
|
|
@@ -6831,32 +6643,25 @@ ${mcpBudgetNote}` : "");
|
|
|
6831
6643
|
}
|
|
6832
6644
|
if (!result.content || result.content.trim() === "") {
|
|
6833
6645
|
const fr = "finishReason" in result ? result.finishReason : void 0;
|
|
6834
|
-
const
|
|
6835
|
-
if (
|
|
6836
|
-
emptyResponseRetries++;
|
|
6646
|
+
const decision = emptyGuard.onEmpty(round < maxToolRounds - 1, fr);
|
|
6647
|
+
if (decision.action === "nudge") {
|
|
6837
6648
|
spinner.stop();
|
|
6838
6649
|
if (alreadyRendered) process.stdout.write("\n");
|
|
6839
|
-
process.stderr.write(
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
);
|
|
6843
|
-
extraMessages.push({
|
|
6844
|
-
role: "user",
|
|
6845
|
-
content: "Your previous response was empty \u2014 no text and no tool calls. This usually means the context window is nearly full. Please either: (1) continue the task by calling the next tool you need, or (2) give a concise final text summary of what has been accomplished so far and what remains. Do NOT repeat earlier long outputs."
|
|
6846
|
-
});
|
|
6650
|
+
process.stderr.write(theme.warning(`${decision.displayMessage}
|
|
6651
|
+
`));
|
|
6652
|
+
extraMessages.push({ role: "user", content: decision.injectMessage });
|
|
6847
6653
|
spinner.start(`Retrying... (round ${round + 2}/${maxToolRounds})`);
|
|
6848
6654
|
continue;
|
|
6849
6655
|
}
|
|
6850
6656
|
spinner.stop();
|
|
6851
6657
|
if (alreadyRendered) process.stdout.write("\n");
|
|
6852
|
-
const frHint = fr === "length" ? "Output token limit hit \u2014 try /compact to reduce context, raise maxTokens, or /model to switch." : fr === "content_filter" ? "Content was blocked by the provider filter." : "Context window may be exhausted or max_tokens too low.";
|
|
6853
6658
|
process.stderr.write(
|
|
6854
6659
|
theme.error(`
|
|
6855
|
-
|
|
6660
|
+
${decision.displayMessage}
|
|
6856
6661
|
`)
|
|
6857
6662
|
);
|
|
6858
6663
|
process.stderr.write(
|
|
6859
|
-
theme.dim(` ${
|
|
6664
|
+
theme.dim(` ${decision.hint}
|
|
6860
6665
|
Try: /compact, /clear, or /model to switch.
|
|
6861
6666
|
|
|
6862
6667
|
`)
|
|
@@ -6870,7 +6675,7 @@ ${mcpBudgetNote}` : "");
|
|
|
6870
6675
|
}
|
|
6871
6676
|
return;
|
|
6872
6677
|
}
|
|
6873
|
-
|
|
6678
|
+
emptyGuard.onNonEmpty();
|
|
6874
6679
|
spinner.stop();
|
|
6875
6680
|
const finalContent = result.content;
|
|
6876
6681
|
if (!alreadyRendered) {
|
|
@@ -7181,14 +6986,8 @@ ${systemPromptVolatile}` : systemPrompt;
|
|
|
7181
6986
|
});
|
|
7182
6987
|
}
|
|
7183
6988
|
}
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
consecutiveFreeRounds++;
|
|
7187
|
-
if (consecutiveFreeRounds <= MAX_CONSECUTIVE_FREE_ROUNDS) {
|
|
7188
|
-
round--;
|
|
7189
|
-
}
|
|
7190
|
-
} else {
|
|
7191
|
-
consecutiveFreeRounds = 0;
|
|
6989
|
+
if (freeRounds.apply(result.toolCalls.map((tc) => tc.name))) {
|
|
6990
|
+
round--;
|
|
7192
6991
|
}
|
|
7193
6992
|
const currentSignature = result.toolCalls.map((tc) => `${tc.name}:${JSON.stringify(tc.arguments)}`).join("|");
|
|
7194
6993
|
if (currentSignature === lastToolCallSignature) {
|
|
@@ -7226,15 +7025,8 @@ ${systemPromptVolatile}` : systemPrompt;
|
|
|
7226
7025
|
process.stdout.write("\n");
|
|
7227
7026
|
process.stdout.write(theme.warning(`\u23F8 Auto-pause: ${effectiveRound}/${maxToolRounds} rounds used, ${remaining} remaining
|
|
7228
7027
|
`));
|
|
7229
|
-
const
|
|
7230
|
-
if (
|
|
7231
|
-
const toolCounts = /* @__PURE__ */ new Map();
|
|
7232
|
-
for (const rh of recentHistory) {
|
|
7233
|
-
for (const t of rh.tools) {
|
|
7234
|
-
toolCounts.set(t, (toolCounts.get(t) || 0) + 1);
|
|
7235
|
-
}
|
|
7236
|
-
}
|
|
7237
|
-
const summary = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).map(([name, count]) => count > 1 ? `${name}\xD7${count}` : name).join(", ");
|
|
7028
|
+
const summary = summarizeRecentTools(roundToolHistory, autoPauseInterval);
|
|
7029
|
+
if (summary) {
|
|
7238
7030
|
process.stdout.write(theme.dim(` Tools used: ${summary}
|
|
7239
7031
|
`));
|
|
7240
7032
|
}
|
|
@@ -7253,10 +7045,7 @@ ${systemPromptVolatile}` : systemPrompt;
|
|
|
7253
7045
|
this.setupInterjectionListener();
|
|
7254
7046
|
if (pauseResponse === "n" || pauseResponse === "N" || pauseResponse === "\x1B") {
|
|
7255
7047
|
process.stdout.write(theme.warning("\u26A1 Stopped by user at auto-pause checkpoint\n"));
|
|
7256
|
-
extraMessages.push({
|
|
7257
|
-
role: "user",
|
|
7258
|
-
content: `The user has stopped the task at round ${effectiveRound}/${maxToolRounds}. Do not call any more tools. Summarize what has been completed and what remains.`
|
|
7259
|
-
});
|
|
7048
|
+
extraMessages.push({ role: "user", content: buildUserStopMessage(effectiveRound, maxToolRounds) });
|
|
7260
7049
|
break;
|
|
7261
7050
|
} else if (pauseResponse && pauseResponse !== "y" && pauseResponse !== "Y" && pauseResponse !== "") {
|
|
7262
7051
|
process.stdout.write(theme.warning(`\u26A1 Redirect: "${pauseResponse}"
|
|
@@ -7276,13 +7065,7 @@ ${systemPromptVolatile}` : systemPrompt;
|
|
|
7276
7065
|
spinner.start("Generating summary...");
|
|
7277
7066
|
const summaryExtra = [
|
|
7278
7067
|
...extraMessages,
|
|
7279
|
-
{
|
|
7280
|
-
role: "user",
|
|
7281
|
-
content: `You have used all ${maxToolRounds} tool call rounds. Do not call any more tools. Summarize in text:
|
|
7282
|
-
1. What work has been completed so far
|
|
7283
|
-
2. What tasks remain unfinished
|
|
7284
|
-
3. What the user can do next (e.g. send another request to continue)`
|
|
7285
|
-
}
|
|
7068
|
+
{ role: "user", content: buildRoundsExhaustedPrompt(maxToolRounds) }
|
|
7286
7069
|
];
|
|
7287
7070
|
const summaryResult = await provider.chatWithTools(
|
|
7288
7071
|
{
|
|
@@ -7633,7 +7416,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
7633
7416
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
7634
7417
|
process.exit(1);
|
|
7635
7418
|
}
|
|
7636
|
-
const { startWebServer } = await import("./server-
|
|
7419
|
+
const { startWebServer } = await import("./server-SLDE3AML.js");
|
|
7637
7420
|
await startWebServer({ port, host: options.host });
|
|
7638
7421
|
});
|
|
7639
7422
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -7800,12 +7583,12 @@ program.command("sessions").description("List recent conversation sessions").opt
|
|
|
7800
7583
|
console.log(footer + "\n");
|
|
7801
7584
|
});
|
|
7802
7585
|
program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
|
|
7803
|
-
const { runDoctorCli } = await import("./doctor-cli-
|
|
7586
|
+
const { runDoctorCli } = await import("./doctor-cli-GQBR6RI6.js");
|
|
7804
7587
|
await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
|
|
7805
7588
|
});
|
|
7806
7589
|
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) => {
|
|
7807
7590
|
try {
|
|
7808
|
-
const batch = await import("./batch-
|
|
7591
|
+
const batch = await import("./batch-DG5STMLZ.js");
|
|
7809
7592
|
switch (action) {
|
|
7810
7593
|
case "submit":
|
|
7811
7594
|
if (!arg) {
|
|
@@ -7848,7 +7631,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
|
|
|
7848
7631
|
}
|
|
7849
7632
|
});
|
|
7850
7633
|
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) => {
|
|
7851
|
-
const { startMcpServer } = await import("./server-
|
|
7634
|
+
const { startMcpServer } = await import("./server-RCUIX4R5.js");
|
|
7852
7635
|
await startMcpServer({
|
|
7853
7636
|
allowDestructive: !!options.allowDestructive,
|
|
7854
7637
|
allowOutsideCwd: !!options.allowOutsideCwd,
|
|
@@ -7857,7 +7640,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
|
|
|
7857
7640
|
});
|
|
7858
7641
|
});
|
|
7859
7642
|
program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
|
|
7860
|
-
const { runCi } = await import("./ci-
|
|
7643
|
+
const { runCi } = await import("./ci-UXVUG3LC.js");
|
|
7861
7644
|
const result = await runCi({
|
|
7862
7645
|
pr: options.pr,
|
|
7863
7646
|
base: options.base,
|
|
@@ -8002,7 +7785,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
8002
7785
|
}),
|
|
8003
7786
|
config.get("customProviders")
|
|
8004
7787
|
);
|
|
8005
|
-
const { startHub } = await import("./hub-
|
|
7788
|
+
const { startHub } = await import("./hub-N75OQVNY.js");
|
|
8006
7789
|
await startHub(
|
|
8007
7790
|
{
|
|
8008
7791
|
topic: topic ?? "",
|