jinzd-ai-cli 0.4.55 → 0.4.57
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/{chunk-W7QVBFIJ.js → chunk-C2MNNHJ6.js} +1 -1
- package/dist/{chunk-JL5NK6AR.js → chunk-G5REL4FK.js} +28 -2
- package/dist/{chunk-YQEIQJ6K.js → chunk-G6K64M6X.js} +1 -1
- package/dist/{chunk-DJ342VFS.js → chunk-H7MNK3YO.js} +1 -1
- package/dist/{hub-AUWP4SWJ.js → hub-P3BR4JB5.js} +1 -1
- package/dist/index.js +61 -13
- package/dist/{run-tests-X4PCLXA2.js → run-tests-NVCAP42D.js} +1 -1
- package/dist/{run-tests-I6UDHVIS.js → run-tests-XAWG6R73.js} +1 -1
- package/dist/{server-YPAZWGUE.js → server-46J5MXHG.js} +85 -5
- package/dist/{task-orchestrator-MWO6A4KQ.js → task-orchestrator-FRF6LTWK.js} +2 -2
- package/dist/web/client/app.js +53 -0
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ProviderNotFoundError,
|
|
8
8
|
RateLimitError,
|
|
9
9
|
schemaToJsonSchema
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-G6K64M6X.js";
|
|
11
11
|
import {
|
|
12
12
|
APP_NAME,
|
|
13
13
|
CONFIG_DIR_NAME,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
MCP_TOOL_PREFIX,
|
|
21
21
|
PLUGINS_DIR_NAME,
|
|
22
22
|
VERSION
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-C2MNNHJ6.js";
|
|
24
24
|
|
|
25
25
|
// src/config/config-manager.ts
|
|
26
26
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -172,6 +172,10 @@ var ConfigSchema = z.object({
|
|
|
172
172
|
// 建议范围:25(保守)~ 1000(宽松,接近 Claude Code)。
|
|
173
173
|
// CLI `--max-tool-rounds <n>` 可覆盖此值。
|
|
174
174
|
maxToolRounds: z.number().int().min(1).max(1e4).default(200),
|
|
175
|
+
// Auto-pause checkpoint:Agentic 循环每隔多少轮暂停一次,让用户确认方向或中途介入。
|
|
176
|
+
// 默认 50;设为 0 禁用(完全自动执行到完成或 maxToolRounds 用尽)。
|
|
177
|
+
// CLI 与 Web UI 行为一致:CLI 用 readline question 提示,Web UI 弹出对话框。
|
|
178
|
+
autoPauseInterval: z.number().int().min(0).max(1e4).default(50),
|
|
175
179
|
// 单次工具输出(如 read_file、bash、grep_files)返回给 AI 的最大字符数上限。
|
|
176
180
|
// 默认 500_000 (~500K chars ≈ 6000-8000 行代码)。
|
|
177
181
|
// 实际上限还会受模型 contextWindow 动态约束(取 contextWindow/4 作为下限)。
|
|
@@ -1216,7 +1220,29 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
1216
1220
|
return { content: "", usage: void 0 };
|
|
1217
1221
|
}
|
|
1218
1222
|
const message = firstChoice.message;
|
|
1223
|
+
const finishReason = firstChoice.finish_reason;
|
|
1219
1224
|
const usage = toUsage(response.usage);
|
|
1225
|
+
const contentStr = typeof message.content === "string" ? message.content : "";
|
|
1226
|
+
const hasToolCalls = !!(message.tool_calls && message.tool_calls.length > 0);
|
|
1227
|
+
if (!hasToolCalls && contentStr.trim() === "") {
|
|
1228
|
+
const providerId = this.info.id;
|
|
1229
|
+
if (finishReason === "length") {
|
|
1230
|
+
process.stderr.write(
|
|
1231
|
+
`[${providerId}] \u26A0 Empty response with finish_reason='length' \u2014 output/context limit reached. Try /compact, raise maxTokens, or switch to a larger-context model.
|
|
1232
|
+
`
|
|
1233
|
+
);
|
|
1234
|
+
} else if (finishReason === "content_filter") {
|
|
1235
|
+
process.stderr.write(
|
|
1236
|
+
`[${providerId}] \u26A0 Empty response with finish_reason='content_filter' \u2014 content was blocked.
|
|
1237
|
+
`
|
|
1238
|
+
);
|
|
1239
|
+
} else {
|
|
1240
|
+
process.stderr.write(
|
|
1241
|
+
`[${providerId}] \u26A0 Empty response (finish_reason=${finishReason ?? "unknown"}). Model produced no text and no tool calls. Context window may be exhausted.
|
|
1242
|
+
`
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1220
1246
|
const reasoningContent = message.reasoning_content;
|
|
1221
1247
|
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
1222
1248
|
const toolCalls = message.tool_calls.map((tc) => {
|
|
@@ -385,7 +385,7 @@ ${content}`);
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
388
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
388
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-FRF6LTWK.js");
|
|
389
389
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
390
390
|
let interrupted = false;
|
|
391
391
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
saveDevState,
|
|
28
28
|
sessionHasMeaningfulContent,
|
|
29
29
|
setupProxy
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-G5REL4FK.js";
|
|
31
31
|
import {
|
|
32
32
|
ToolExecutor,
|
|
33
33
|
ToolRegistry,
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
spawnAgentContext,
|
|
42
42
|
theme,
|
|
43
43
|
undoStack
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-G6K64M6X.js";
|
|
45
45
|
import {
|
|
46
46
|
fileCheckpoints
|
|
47
47
|
} from "./chunk-4BKXL7SM.js";
|
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
SKILLS_DIR_NAME,
|
|
67
67
|
VERSION,
|
|
68
68
|
buildUserIdentityPrompt
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-C2MNNHJ6.js";
|
|
70
70
|
|
|
71
71
|
// src/index.ts
|
|
72
72
|
import { program } from "commander";
|
|
@@ -2161,7 +2161,7 @@ ${hint}` : "")
|
|
|
2161
2161
|
usage: "/test [command|filter]",
|
|
2162
2162
|
async execute(args, ctx) {
|
|
2163
2163
|
try {
|
|
2164
|
-
const { executeTests } = await import("./run-tests-
|
|
2164
|
+
const { executeTests } = await import("./run-tests-NVCAP42D.js");
|
|
2165
2165
|
const argStr = args.join(" ").trim();
|
|
2166
2166
|
let testArgs = {};
|
|
2167
2167
|
if (argStr) {
|
|
@@ -3337,7 +3337,7 @@ ${content}
|
|
|
3337
3337
|
var FREE_ROUND_TOOLS = /* @__PURE__ */ new Set(["write_todos"]);
|
|
3338
3338
|
var MAX_CONSECUTIVE_FREE_ROUNDS = 3;
|
|
3339
3339
|
var MAX_REPEATED_TOOL_CALLS = 2;
|
|
3340
|
-
var
|
|
3340
|
+
var DEFAULT_AUTO_PAUSE_INTERVAL = 50;
|
|
3341
3341
|
function fmtTokens(n) {
|
|
3342
3342
|
if (n >= 1e6) return `${Math.round(n / 1e5) / 10}M`;
|
|
3343
3343
|
if (n >= 1e3) return `${Math.round(n / 1024)}K`;
|
|
@@ -4833,7 +4833,11 @@ Session '${this.resumeSessionId}' not found.
|
|
|
4833
4833
|
const apiMessages = [...messages];
|
|
4834
4834
|
const extraMessages = [];
|
|
4835
4835
|
const maxToolRounds = this.maxToolRoundsOverride ?? this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
4836
|
+
const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
|
|
4837
|
+
const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : DEFAULT_AUTO_PAUSE_INTERVAL;
|
|
4836
4838
|
const baseSystemPrompt = (this.buildCurrentSystemPrompt() ?? "") + TOOL_CALL_REMINDER;
|
|
4839
|
+
const pauseHint = autoPauseInterval > 0 ? `
|
|
4840
|
+
- Every ${autoPauseInterval} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.` : "";
|
|
4837
4841
|
const roundBudgetHint = this.planMode ? `
|
|
4838
4842
|
|
|
4839
4843
|
[Tool Round Budget \u2014 Plan Mode]
|
|
@@ -4843,16 +4847,14 @@ You have a maximum of ${maxToolRounds} tool call rounds. You are in READ-ONLY Pl
|
|
|
4843
4847
|
- Do NOT write shell commands or code blocks as a substitute for tool calls
|
|
4844
4848
|
- Do NOT read the same file more than once
|
|
4845
4849
|
- Call write_todos ONCE to present your plan, then give a text summary
|
|
4846
|
-
- If the user asks you to execute anything, respond: "Please type /plan execute to switch to execute mode."
|
|
4847
|
-
- Every ${AUTO_PAUSE_INTERVAL} rounds the user will be asked whether to continue.` : `
|
|
4850
|
+
- If the user asks you to execute anything, respond: "Please type /plan execute to switch to execute mode."${pauseHint}` : `
|
|
4848
4851
|
|
|
4849
4852
|
[Tool Round Budget]
|
|
4850
4853
|
You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan efficiently:
|
|
4851
4854
|
- Prefer batch operations (e.g. global find-and-replace) over repetitive single edits.
|
|
4852
4855
|
- Do NOT read the same file more than once \u2014 use the content from previous reads.
|
|
4853
4856
|
- Prioritize the most critical tasks first in case rounds run out.
|
|
4854
|
-
- When remaining rounds are low, focus on completing the current task and summarizing
|
|
4855
|
-
- Every ${AUTO_PAUSE_INTERVAL} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.`;
|
|
4857
|
+
- When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
|
|
4856
4858
|
const systemPrompt = baseSystemPrompt + roundBudgetHint;
|
|
4857
4859
|
const modelParams = this.getModelParams();
|
|
4858
4860
|
const useStreaming = this.config.get("ui").streaming;
|
|
@@ -4862,6 +4864,7 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
4862
4864
|
let consecutiveFreeRounds = 0;
|
|
4863
4865
|
let lastToolCallSignature = "";
|
|
4864
4866
|
let repeatedToolCallCount = 0;
|
|
4867
|
+
let emptyResponseRetries = 0;
|
|
4865
4868
|
const roundToolHistory = [];
|
|
4866
4869
|
this.setupInterjectionListener();
|
|
4867
4870
|
try {
|
|
@@ -4990,6 +4993,51 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
4990
4993
|
spinner.start(`Retrying... (round ${round + 2}/${maxToolRounds})`);
|
|
4991
4994
|
continue;
|
|
4992
4995
|
}
|
|
4996
|
+
if (!result.content || result.content.trim() === "") {
|
|
4997
|
+
if (emptyResponseRetries === 0 && round < maxToolRounds - 1) {
|
|
4998
|
+
emptyResponseRetries++;
|
|
4999
|
+
spinner.stop();
|
|
5000
|
+
if (alreadyRendered) process.stdout.write("\n");
|
|
5001
|
+
process.stderr.write(
|
|
5002
|
+
theme.warning(
|
|
5003
|
+
`\u26A0 AI returned an empty response (no text, no tool calls). Nudging to continue...
|
|
5004
|
+
`
|
|
5005
|
+
)
|
|
5006
|
+
);
|
|
5007
|
+
extraMessages.push({
|
|
5008
|
+
role: "user",
|
|
5009
|
+
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."
|
|
5010
|
+
});
|
|
5011
|
+
spinner.start(`Retrying... (round ${round + 2}/${maxToolRounds})`);
|
|
5012
|
+
continue;
|
|
5013
|
+
}
|
|
5014
|
+
spinner.stop();
|
|
5015
|
+
if (alreadyRendered) process.stdout.write("\n");
|
|
5016
|
+
process.stderr.write(
|
|
5017
|
+
theme.error(
|
|
5018
|
+
`
|
|
5019
|
+
\u26A0 AI returned empty responses twice in a row. Stopping agentic loop.
|
|
5020
|
+
`
|
|
5021
|
+
)
|
|
5022
|
+
);
|
|
5023
|
+
process.stderr.write(
|
|
5024
|
+
theme.dim(
|
|
5025
|
+
` Likely causes: context window exhausted, max_tokens too low, or content filter.
|
|
5026
|
+
Try: /compact to reduce context, /clear to reset, or /model to switch.
|
|
5027
|
+
|
|
5028
|
+
`
|
|
5029
|
+
)
|
|
5030
|
+
);
|
|
5031
|
+
if (roundUsage.inputTokens > 0 || roundUsage.outputTokens > 0) {
|
|
5032
|
+
this.addSessionUsage(roundUsage);
|
|
5033
|
+
session.addTokenUsage(roundUsage);
|
|
5034
|
+
if (this.shouldShowTokens()) {
|
|
5035
|
+
this.renderer.renderUsage(roundUsage, this.sessionTokenUsage);
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
return;
|
|
5039
|
+
}
|
|
5040
|
+
emptyResponseRetries = 0;
|
|
4993
5041
|
spinner.stop();
|
|
4994
5042
|
const finalContent = result.content;
|
|
4995
5043
|
if (!alreadyRendered) {
|
|
@@ -5178,12 +5226,12 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
5178
5226
|
}
|
|
5179
5227
|
const effectiveRound = round + 1;
|
|
5180
5228
|
const remaining = maxToolRounds - effectiveRound;
|
|
5181
|
-
if (
|
|
5229
|
+
if (autoPauseInterval > 0 && effectiveRound > 0 && effectiveRound % autoPauseInterval === 0 && remaining > 0) {
|
|
5182
5230
|
spinner.stop();
|
|
5183
5231
|
process.stdout.write("\n");
|
|
5184
5232
|
process.stdout.write(theme.warning(`\u23F8 Auto-pause: ${effectiveRound}/${maxToolRounds} rounds used, ${remaining} remaining
|
|
5185
5233
|
`));
|
|
5186
|
-
const recentHistory = roundToolHistory.slice(-
|
|
5234
|
+
const recentHistory = roundToolHistory.slice(-autoPauseInterval);
|
|
5187
5235
|
if (recentHistory.length > 0) {
|
|
5188
5236
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
5189
5237
|
for (const rh of recentHistory) {
|
|
@@ -5561,7 +5609,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
5561
5609
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
5562
5610
|
process.exit(1);
|
|
5563
5611
|
}
|
|
5564
|
-
const { startWebServer } = await import("./server-
|
|
5612
|
+
const { startWebServer } = await import("./server-46J5MXHG.js");
|
|
5565
5613
|
await startWebServer({ port, host: options.host });
|
|
5566
5614
|
});
|
|
5567
5615
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -5794,7 +5842,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
5794
5842
|
}),
|
|
5795
5843
|
config.get("customProviders")
|
|
5796
5844
|
);
|
|
5797
|
-
const { startHub } = await import("./hub-
|
|
5845
|
+
const { startHub } = await import("./hub-P3BR4JB5.js");
|
|
5798
5846
|
await startHub(
|
|
5799
5847
|
{
|
|
5800
5848
|
topic: topic ?? "",
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
hadPreviousWriteToolCalls,
|
|
18
18
|
loadDevState,
|
|
19
19
|
setupProxy
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-G5REL4FK.js";
|
|
21
21
|
import {
|
|
22
22
|
AuthManager
|
|
23
23
|
} from "./chunk-BYNY5JPB.js";
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
spawnAgentContext,
|
|
37
37
|
truncateOutput,
|
|
38
38
|
undoStack
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-G6K64M6X.js";
|
|
40
40
|
import "./chunk-4BKXL7SM.js";
|
|
41
41
|
import {
|
|
42
42
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
SKILLS_DIR_NAME,
|
|
57
57
|
VERSION,
|
|
58
58
|
buildUserIdentityPrompt
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-C2MNNHJ6.js";
|
|
60
60
|
|
|
61
61
|
// src/web/server.ts
|
|
62
62
|
import express from "express";
|
|
@@ -501,6 +501,8 @@ var SessionHandler = class _SessionHandler {
|
|
|
501
501
|
processing = false;
|
|
502
502
|
/** Pending ask_user promises */
|
|
503
503
|
pendingAskUser = /* @__PURE__ */ new Map();
|
|
504
|
+
/** Pending auto-pause promises */
|
|
505
|
+
pendingAutoPause = /* @__PURE__ */ new Map();
|
|
504
506
|
/** Active system prompt from context files */
|
|
505
507
|
activeSystemPrompt;
|
|
506
508
|
/** Directories added via /add-dir */
|
|
@@ -602,6 +604,14 @@ var SessionHandler = class _SessionHandler {
|
|
|
602
604
|
}
|
|
603
605
|
return;
|
|
604
606
|
}
|
|
607
|
+
case "auto_pause_response": {
|
|
608
|
+
const resolve3 = this.pendingAutoPause.get(msg.requestId);
|
|
609
|
+
if (resolve3) {
|
|
610
|
+
this.pendingAutoPause.delete(msg.requestId);
|
|
611
|
+
resolve3({ action: msg.action, message: msg.message });
|
|
612
|
+
}
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
605
615
|
case "abort":
|
|
606
616
|
if (this.abortController) {
|
|
607
617
|
this.abortController.abort();
|
|
@@ -617,6 +627,8 @@ var SessionHandler = class _SessionHandler {
|
|
|
617
627
|
if (this.abortController) this.abortController.abort();
|
|
618
628
|
for (const resolve3 of this.pendingAskUser.values()) resolve3(null);
|
|
619
629
|
this.pendingAskUser.clear();
|
|
630
|
+
for (const resolve3 of this.pendingAutoPause.values()) resolve3({ action: "stop" });
|
|
631
|
+
this.pendingAutoPause.clear();
|
|
620
632
|
this.saveIfNeeded();
|
|
621
633
|
}
|
|
622
634
|
/** 根据当前模型 context window 更新工具输出截断上限 */
|
|
@@ -762,19 +774,24 @@ var SessionHandler = class _SessionHandler {
|
|
|
762
774
|
const apiMessages = [...messages];
|
|
763
775
|
const extraMessages = [];
|
|
764
776
|
const maxToolRounds = this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
777
|
+
const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
|
|
778
|
+
const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : 50;
|
|
765
779
|
const baseSystemPrompt = (this.buildSystemPrompt() ?? "") + TOOL_CALL_REMINDER;
|
|
780
|
+
const pauseHint = autoPauseInterval > 0 ? `
|
|
781
|
+
- Every ${autoPauseInterval} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.` : "";
|
|
766
782
|
const roundBudgetHint = `
|
|
767
783
|
|
|
768
784
|
[Tool Round Budget]
|
|
769
785
|
You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan efficiently:
|
|
770
786
|
- Prefer batch operations (e.g. global find-and-replace) over repetitive single edits.
|
|
771
787
|
- Prioritize the most critical tasks first in case rounds run out.
|
|
772
|
-
- When remaining rounds are low, focus on completing the current task and summarizing
|
|
788
|
+
- When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
|
|
773
789
|
const systemPrompt = baseSystemPrompt + roundBudgetHint;
|
|
774
790
|
const modelParams = this.getModelParams();
|
|
775
791
|
const roundUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
776
792
|
const supportsStreamingTools = typeof provider.chatWithToolsStream === "function";
|
|
777
793
|
let consecutiveFreeRounds = 0;
|
|
794
|
+
const roundToolHistory = [];
|
|
778
795
|
const warnNoteAt = Math.max(10, Math.floor(maxToolRounds * 0.2));
|
|
779
796
|
const warnLowAt = Math.max(5, Math.floor(maxToolRounds * 0.1));
|
|
780
797
|
const warnCriticalAt = Math.max(3, Math.floor(maxToolRounds * 0.05));
|
|
@@ -783,6 +800,7 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
783
800
|
let warnedNote = false;
|
|
784
801
|
let warnedLow = false;
|
|
785
802
|
let warnedCritical = false;
|
|
803
|
+
let emptyResponseRetries = 0;
|
|
786
804
|
const ac = new AbortController();
|
|
787
805
|
this.abortController = ac;
|
|
788
806
|
try {
|
|
@@ -845,6 +863,31 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
845
863
|
roundUsage.cacheCreationTokens += result.usage.cacheCreationTokens ?? 0;
|
|
846
864
|
roundUsage.cacheReadTokens += result.usage.cacheReadTokens ?? 0;
|
|
847
865
|
}
|
|
866
|
+
const hasToolCalls = !!(result.toolCalls && result.toolCalls.length > 0);
|
|
867
|
+
const contentBlank = !result.content || result.content.trim() === "";
|
|
868
|
+
if (!hasToolCalls && contentBlank) {
|
|
869
|
+
if (emptyResponseRetries === 0 && round < maxToolRounds - 1) {
|
|
870
|
+
emptyResponseRetries++;
|
|
871
|
+
this.send({
|
|
872
|
+
type: "info",
|
|
873
|
+
message: "\u26A0 AI returned an empty response. Nudging to continue..."
|
|
874
|
+
});
|
|
875
|
+
extraMessages.push({
|
|
876
|
+
role: "user",
|
|
877
|
+
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."
|
|
878
|
+
});
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
this.send({
|
|
882
|
+
type: "response_done",
|
|
883
|
+
content: "\u26A0 AI returned empty responses twice in a row. Stopping agentic loop.\n\nLikely causes: context window exhausted, max_tokens too low, or content filter.\nTry: /compact to reduce context, /clear to reset, or switch to a larger-context model.",
|
|
884
|
+
usage: roundUsage
|
|
885
|
+
});
|
|
886
|
+
this.addWebSessionUsage(roundUsage);
|
|
887
|
+
session.addTokenUsage(roundUsage);
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
emptyResponseRetries = 0;
|
|
848
891
|
if (result.content && !result.toolCalls) {
|
|
849
892
|
const hasWriteTools = toolDefs.some((t) => t.name === "write_file" || t.name === "edit_file");
|
|
850
893
|
const alreadyWrote = hadPreviousWriteToolCalls(extraMessages);
|
|
@@ -863,6 +906,10 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
863
906
|
return;
|
|
864
907
|
}
|
|
865
908
|
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
909
|
+
roundToolHistory.push({
|
|
910
|
+
round: round + 1,
|
|
911
|
+
tools: result.toolCalls.map((tc) => tc.name)
|
|
912
|
+
});
|
|
866
913
|
googleSearchContext.configManager = this.config;
|
|
867
914
|
spawnAgentContext.provider = provider;
|
|
868
915
|
spawnAgentContext.model = this.currentModel;
|
|
@@ -890,6 +937,39 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
890
937
|
extraMessages.push({ role: "user", content: msg });
|
|
891
938
|
}
|
|
892
939
|
}
|
|
940
|
+
const effectiveRound = round + 1;
|
|
941
|
+
const remaining = maxToolRounds - effectiveRound;
|
|
942
|
+
if (autoPauseInterval > 0 && effectiveRound > 0 && effectiveRound % autoPauseInterval === 0 && remaining > 0 && !ac.signal.aborted) {
|
|
943
|
+
const recentHistory = roundToolHistory.slice(-autoPauseInterval);
|
|
944
|
+
const toolCounts = /* @__PURE__ */ new Map();
|
|
945
|
+
for (const rh of recentHistory) {
|
|
946
|
+
for (const t of rh.tools) toolCounts.set(t, (toolCounts.get(t) || 0) + 1);
|
|
947
|
+
}
|
|
948
|
+
const toolSummary = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).map(([name, count]) => count > 1 ? `${name}\xD7${count}` : name).join(", ");
|
|
949
|
+
const requestId = `pause_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
950
|
+
const pauseResp = await new Promise((resolve3) => {
|
|
951
|
+
this.pendingAutoPause.set(requestId, resolve3);
|
|
952
|
+
this.send({
|
|
953
|
+
type: "auto_pause_request",
|
|
954
|
+
requestId,
|
|
955
|
+
currentRound: effectiveRound,
|
|
956
|
+
totalRounds: maxToolRounds,
|
|
957
|
+
toolSummary
|
|
958
|
+
});
|
|
959
|
+
});
|
|
960
|
+
if (ac.signal.aborted) break;
|
|
961
|
+
if (pauseResp.action === "stop") {
|
|
962
|
+
this.send({ type: "info", message: `\u23F8 Stopped by user at ${effectiveRound}/${maxToolRounds}` });
|
|
963
|
+
extraMessages.push({
|
|
964
|
+
role: "user",
|
|
965
|
+
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.`
|
|
966
|
+
});
|
|
967
|
+
break;
|
|
968
|
+
} else if (pauseResp.action === "redirect" && pauseResp.message) {
|
|
969
|
+
this.send({ type: "info", message: `\u26A1 Redirect: "${pauseResp.message}"` });
|
|
970
|
+
extraMessages.push({ role: "user", content: pauseResp.message });
|
|
971
|
+
}
|
|
972
|
+
}
|
|
893
973
|
}
|
|
894
974
|
try {
|
|
895
975
|
const summaryExtra = [
|
|
@@ -1736,7 +1816,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1736
1816
|
case "test": {
|
|
1737
1817
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
1738
1818
|
try {
|
|
1739
|
-
const { executeTests } = await import("./run-tests-
|
|
1819
|
+
const { executeTests } = await import("./run-tests-NVCAP42D.js");
|
|
1740
1820
|
const argStr = args.join(" ").trim();
|
|
1741
1821
|
let testArgs = {};
|
|
1742
1822
|
if (argStr) {
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-G6K64M6X.js";
|
|
8
8
|
import "./chunk-4BKXL7SM.js";
|
|
9
9
|
import {
|
|
10
10
|
SUBAGENT_ALLOWED_TOOLS
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-C2MNNHJ6.js";
|
|
12
12
|
|
|
13
13
|
// src/hub/task-orchestrator.ts
|
|
14
14
|
import { createInterface } from "readline";
|
package/dist/web/client/app.js
CHANGED
|
@@ -176,6 +176,7 @@ function handleServerMessage(msg) {
|
|
|
176
176
|
case 'confirm_request': handleConfirmRequest(msg); break;
|
|
177
177
|
case 'batch_confirm_request': handleBatchConfirmRequest(msg); break;
|
|
178
178
|
case 'ask_user_request':handleAskUserRequest(msg); break;
|
|
179
|
+
case 'auto_pause_request': handleAutoPauseRequest(msg); break;
|
|
179
180
|
case 'thinking_start': handleThinkingStart(); break;
|
|
180
181
|
case 'thinking_delta': handleThinkingDelta(msg.delta); break;
|
|
181
182
|
case 'thinking_end': handleThinkingEnd(); break;
|
|
@@ -377,6 +378,34 @@ function handleAskUserRequest(msg) {
|
|
|
377
378
|
setTimeout(() => document.getElementById(`ask-input-${msg.requestId}`)?.focus(), 100);
|
|
378
379
|
}
|
|
379
380
|
|
|
381
|
+
function handleAutoPauseRequest(msg) {
|
|
382
|
+
const el = document.createElement('div');
|
|
383
|
+
el.className = 'confirm-card tool-border-write my-1';
|
|
384
|
+
el.id = `pause-card-${msg.requestId}`;
|
|
385
|
+
const summary = msg.toolSummary ? escapeHtml(msg.toolSummary) : '(none)';
|
|
386
|
+
el.innerHTML = `
|
|
387
|
+
<div class="flex items-center gap-2 mb-2">
|
|
388
|
+
<span class="badge badge-warning badge-sm">⏸ Auto-pause</span>
|
|
389
|
+
<span class="text-xs opacity-70">Round ${msg.currentRound}/${msg.totalRounds} · ${msg.totalRounds - msg.currentRound} remaining</span>
|
|
390
|
+
</div>
|
|
391
|
+
<div class="text-xs opacity-70 mb-2">Recent tools: ${summary}</div>
|
|
392
|
+
<div class="flex gap-2 w-full mb-2">
|
|
393
|
+
<input type="text" id="pause-input-${msg.requestId}"
|
|
394
|
+
class="input input-sm input-bordered flex-1"
|
|
395
|
+
placeholder="Optional: redirect AI with a new instruction..."
|
|
396
|
+
onkeydown="if(event.key==='Enter')respondAutoPause('${msg.requestId}','redirect')">
|
|
397
|
+
</div>
|
|
398
|
+
<div class="flex gap-2">
|
|
399
|
+
<button class="btn btn-success btn-sm flex-1" onclick="respondAutoPause('${msg.requestId}','continue')">▶ Continue</button>
|
|
400
|
+
<button class="btn btn-primary btn-sm flex-1" onclick="respondAutoPause('${msg.requestId}','redirect')">↪ Redirect</button>
|
|
401
|
+
<button class="btn btn-error btn-sm flex-1" onclick="respondAutoPause('${msg.requestId}','stop')">⏹ Stop</button>
|
|
402
|
+
</div>
|
|
403
|
+
`;
|
|
404
|
+
messagesEl.appendChild(el);
|
|
405
|
+
scrollToBottom();
|
|
406
|
+
setTimeout(() => document.getElementById(`pause-input-${msg.requestId}`)?.focus(), 100);
|
|
407
|
+
}
|
|
408
|
+
|
|
380
409
|
function handleRoundProgress(msg) {
|
|
381
410
|
const progressBar = document.getElementById('round-progress');
|
|
382
411
|
const progressBarEl = document.getElementById('round-progress-bar');
|
|
@@ -568,6 +597,30 @@ window.respondAskUser = function(requestId) {
|
|
|
568
597
|
}
|
|
569
598
|
};
|
|
570
599
|
|
|
600
|
+
window.respondAutoPause = function(requestId, action) {
|
|
601
|
+
const card = document.getElementById(`pause-card-${requestId}`);
|
|
602
|
+
const input = document.getElementById(`pause-input-${requestId}`);
|
|
603
|
+
const typed = input ? input.value.trim() : '';
|
|
604
|
+
|
|
605
|
+
// 若用户在输入框敲回车,action 是 'redirect';若内容为空则视作 continue
|
|
606
|
+
let effective = action;
|
|
607
|
+
let message;
|
|
608
|
+
if (action === 'redirect') {
|
|
609
|
+
if (!typed) { effective = 'continue'; }
|
|
610
|
+
else { message = typed; }
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
send({ type: 'auto_pause_response', requestId, action: effective, message });
|
|
614
|
+
|
|
615
|
+
if (card) {
|
|
616
|
+
card.className = 'confirm-card tool-border-safe my-1 opacity-60';
|
|
617
|
+
const label = effective === 'continue' ? '▶ Continued'
|
|
618
|
+
: effective === 'stop' ? '⏹ Stopped'
|
|
619
|
+
: `↪ Redirected: ${escapeHtml(message || '')}`;
|
|
620
|
+
card.innerHTML = `<span class="text-sm">${label}</span>`;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
|
|
571
624
|
// DaisyUI light themes → highlight.js light stylesheet; others → dark
|
|
572
625
|
const LIGHT_DAISYUI_THEMES = new Set(['light', 'cupcake', 'bumblebee', 'emerald', 'corporate', 'garden', 'lofi', 'pastel', 'fantasy', 'wireframe', 'cmyk', 'autumn', 'acid', 'lemonade', 'winter', 'nord']);
|
|
573
626
|
const HLJS_CDN = 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles';
|