jinzd-ai-cli 0.4.59 → 0.4.61
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-NXSYL5OP.js → chunk-2WGVM2J2.js} +120 -14
- package/dist/{chunk-7RX7675B.js → chunk-2ZCD5F4X.js} +16 -1
- package/dist/{chunk-YJCJBUOG.js → chunk-GA74LZ62.js} +1 -1
- package/dist/{chunk-F5WLEWN2.js → chunk-QUD2AVHH.js} +1 -1
- package/dist/{hub-3BY5W4VE.js → hub-WA2DZCSQ.js} +1 -1
- package/dist/index.js +24 -9
- package/dist/{run-tests-OT3MSOUF.js → run-tests-672N6ZKM.js} +1 -1
- package/dist/{run-tests-IW6GHAVV.js → run-tests-CT4PRGGP.js} +1 -1
- package/dist/{server-J7PNU32E.js → server-YJWIPDF2.js} +33 -12
- package/dist/{task-orchestrator-I5YCZ72U.js → task-orchestrator-W66JWWKF.js} +2 -2
- package/dist/web/client/app.js +95 -16
- package/package.json +1 -1
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
ProviderError,
|
|
7
7
|
ProviderNotFoundError,
|
|
8
8
|
RateLimitError,
|
|
9
|
-
schemaToJsonSchema
|
|
10
|
-
|
|
9
|
+
schemaToJsonSchema,
|
|
10
|
+
truncateForPersist
|
|
11
|
+
} from "./chunk-2ZCD5F4X.js";
|
|
11
12
|
import {
|
|
12
13
|
APP_NAME,
|
|
13
14
|
CONFIG_DIR_NAME,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
MCP_TOOL_PREFIX,
|
|
21
22
|
PLUGINS_DIR_NAME,
|
|
22
23
|
VERSION
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-GA74LZ62.js";
|
|
24
25
|
|
|
25
26
|
// src/config/config-manager.ts
|
|
26
27
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -309,9 +310,12 @@ var BaseProvider = class {
|
|
|
309
310
|
/**
|
|
310
311
|
* 将 Message[] 转换为 OpenAI API 格式的消息数组。
|
|
311
312
|
* content 为 string 时直接传递;为 MessageContentPart[] 时保留数组格式(vision 请求)。
|
|
313
|
+
*
|
|
314
|
+
* 自动跳过 role='tool' 和带 toolCalls 的 assistant 消息——
|
|
315
|
+
* 这些是 v0.4.60+ 持久化的工具历史,由 _extraMessages 机制单独注入。
|
|
312
316
|
*/
|
|
313
317
|
normalizeMessages(messages) {
|
|
314
|
-
return messages.map((m) => ({ role: m.role, content: m.content }));
|
|
318
|
+
return messages.filter((m) => m.role !== "tool" && !m.toolCalls).map((m) => ({ role: m.role, content: m.content }));
|
|
315
319
|
}
|
|
316
320
|
};
|
|
317
321
|
|
|
@@ -475,7 +479,7 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
475
479
|
}
|
|
476
480
|
async chat(request) {
|
|
477
481
|
try {
|
|
478
|
-
const messages = request.messages.filter((m) => m.role !== "system").map((m) => ({
|
|
482
|
+
const messages = request.messages.filter((m) => m.role !== "system" && m.role !== "tool" && !m.toolCalls).map((m) => ({
|
|
479
483
|
role: m.role,
|
|
480
484
|
content: this.contentToClaudeParts(m.content)
|
|
481
485
|
}));
|
|
@@ -500,7 +504,7 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
500
504
|
}
|
|
501
505
|
async *chatStream(request) {
|
|
502
506
|
try {
|
|
503
|
-
const messages = request.messages.filter((m) => m.role !== "system").map((m) => ({
|
|
507
|
+
const messages = request.messages.filter((m) => m.role !== "system" && m.role !== "tool" && !m.toolCalls).map((m) => ({
|
|
504
508
|
role: m.role,
|
|
505
509
|
content: this.contentToClaudeParts(m.content)
|
|
506
510
|
}));
|
|
@@ -557,7 +561,7 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
557
561
|
}
|
|
558
562
|
}))
|
|
559
563
|
);
|
|
560
|
-
const baseMessages = request.messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: this.contentToClaudeParts(m.content) }));
|
|
564
|
+
const baseMessages = request.messages.filter((m) => m.role !== "system" && m.role !== "tool" && !m.toolCalls).map((m) => ({ role: m.role, content: this.contentToClaudeParts(m.content) }));
|
|
561
565
|
const extraMessages = request._extraMessages ?? [];
|
|
562
566
|
const allMessages = [...baseMessages, ...extraMessages];
|
|
563
567
|
const { thinking, temperature } = this.buildThinkingParams(request);
|
|
@@ -869,7 +873,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
869
873
|
return parts.length > 0 ? parts : [{ text: "" }];
|
|
870
874
|
}
|
|
871
875
|
toGeminiHistory(messages) {
|
|
872
|
-
return messages.filter((m) => m.role !== "system").map((m) => ({
|
|
876
|
+
return messages.filter((m) => m.role !== "system" && m.role !== "tool" && !m.toolCalls).map((m) => ({
|
|
873
877
|
role: m.role === "assistant" ? "model" : "user",
|
|
874
878
|
parts: this.contentToGeminiParts(m.content)
|
|
875
879
|
}));
|
|
@@ -2521,10 +2525,19 @@ var Session = class _Session {
|
|
|
2521
2525
|
messageIndex: c.messageIndex,
|
|
2522
2526
|
timestamp: c.timestamp.toISOString()
|
|
2523
2527
|
})),
|
|
2524
|
-
messages: this.messages.map((m) =>
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
+
messages: this.messages.map((m) => {
|
|
2529
|
+
const out = {
|
|
2530
|
+
role: m.role,
|
|
2531
|
+
content: m.content,
|
|
2532
|
+
timestamp: m.timestamp.toISOString()
|
|
2533
|
+
};
|
|
2534
|
+
if (m.toolCalls) out.toolCalls = m.toolCalls;
|
|
2535
|
+
if (m.reasoningContent !== void 0) out.reasoningContent = m.reasoningContent;
|
|
2536
|
+
if (m.toolCallId) out.toolCallId = m.toolCallId;
|
|
2537
|
+
if (m.toolName) out.toolName = m.toolName;
|
|
2538
|
+
if (m.isError !== void 0) out.isError = m.isError;
|
|
2539
|
+
return out;
|
|
2540
|
+
})
|
|
2528
2541
|
};
|
|
2529
2542
|
}
|
|
2530
2543
|
/**
|
|
@@ -2594,11 +2607,17 @@ var Session = class _Session {
|
|
|
2594
2607
|
}
|
|
2595
2608
|
session.messages = d.messages.map((m) => {
|
|
2596
2609
|
const ts = new Date(m.timestamp);
|
|
2597
|
-
|
|
2610
|
+
const msg = {
|
|
2598
2611
|
role: m.role ?? "user",
|
|
2599
|
-
content: m.content,
|
|
2612
|
+
content: Array.isArray(m.content) ? m.content : String(m.content ?? ""),
|
|
2600
2613
|
timestamp: isNaN(ts.getTime()) ? /* @__PURE__ */ new Date() : ts
|
|
2601
2614
|
};
|
|
2615
|
+
if (Array.isArray(m.toolCalls)) msg.toolCalls = m.toolCalls;
|
|
2616
|
+
if (typeof m.reasoningContent === "string") msg.reasoningContent = m.reasoningContent;
|
|
2617
|
+
if (typeof m.toolCallId === "string") msg.toolCallId = m.toolCallId;
|
|
2618
|
+
if (typeof m.toolName === "string") msg.toolName = m.toolName;
|
|
2619
|
+
if (typeof m.isError === "boolean") msg.isError = m.isError;
|
|
2620
|
+
return msg;
|
|
2602
2621
|
});
|
|
2603
2622
|
return session;
|
|
2604
2623
|
}
|
|
@@ -3667,6 +3686,89 @@ function formatCost(amount) {
|
|
|
3667
3686
|
return `$${amount.toFixed(2)}`;
|
|
3668
3687
|
}
|
|
3669
3688
|
|
|
3689
|
+
// src/session/tool-history.ts
|
|
3690
|
+
function persistToolRound(session, toolCalls, toolResults, opts) {
|
|
3691
|
+
session.addMessage({
|
|
3692
|
+
role: "assistant",
|
|
3693
|
+
content: opts?.assistantContent ?? "",
|
|
3694
|
+
toolCalls,
|
|
3695
|
+
reasoningContent: opts?.reasoningContent,
|
|
3696
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3697
|
+
});
|
|
3698
|
+
for (let i = 0; i < toolCalls.length; i++) {
|
|
3699
|
+
const tc = toolCalls[i];
|
|
3700
|
+
const tr = toolResults[i];
|
|
3701
|
+
if (!tr) continue;
|
|
3702
|
+
session.addMessage({
|
|
3703
|
+
role: "tool",
|
|
3704
|
+
content: truncateForPersist(tr.content),
|
|
3705
|
+
toolCallId: tr.callId,
|
|
3706
|
+
toolName: tc.name,
|
|
3707
|
+
isError: tr.isError,
|
|
3708
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3709
|
+
});
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
function isToolMessage(m) {
|
|
3713
|
+
return m.role === "tool" || !!(m.toolCalls && m.toolCalls.length > 0);
|
|
3714
|
+
}
|
|
3715
|
+
function extractToolHistory(messages) {
|
|
3716
|
+
const baseMessages = [];
|
|
3717
|
+
const toolHistory = [];
|
|
3718
|
+
for (const m of messages) {
|
|
3719
|
+
if (isToolMessage(m)) {
|
|
3720
|
+
toolHistory.push(m);
|
|
3721
|
+
} else {
|
|
3722
|
+
baseMessages.push(m);
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
return { baseMessages, toolHistory };
|
|
3726
|
+
}
|
|
3727
|
+
function rebuildExtraMessages(provider, toolHistory) {
|
|
3728
|
+
if (toolHistory.length === 0) return [];
|
|
3729
|
+
const result = [];
|
|
3730
|
+
let i = 0;
|
|
3731
|
+
while (i < toolHistory.length) {
|
|
3732
|
+
const msg = toolHistory[i];
|
|
3733
|
+
if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) {
|
|
3734
|
+
const toolCalls = msg.toolCalls;
|
|
3735
|
+
const toolResults = [];
|
|
3736
|
+
let j = i + 1;
|
|
3737
|
+
while (j < toolHistory.length && toolHistory[j].role === "tool") {
|
|
3738
|
+
const tm = toolHistory[j];
|
|
3739
|
+
toolResults.push({
|
|
3740
|
+
callId: tm.toolCallId ?? "",
|
|
3741
|
+
content: typeof tm.content === "string" ? tm.content : getContentText(tm.content),
|
|
3742
|
+
isError: tm.isError ?? false
|
|
3743
|
+
});
|
|
3744
|
+
j++;
|
|
3745
|
+
}
|
|
3746
|
+
result.push(
|
|
3747
|
+
...provider.buildToolResultMessages(toolCalls, toolResults, msg.reasoningContent)
|
|
3748
|
+
);
|
|
3749
|
+
i = j;
|
|
3750
|
+
} else {
|
|
3751
|
+
result.push({
|
|
3752
|
+
role: msg.role,
|
|
3753
|
+
content: typeof msg.content === "string" ? msg.content : getContentText(msg.content)
|
|
3754
|
+
});
|
|
3755
|
+
i++;
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
return result;
|
|
3759
|
+
}
|
|
3760
|
+
|
|
3761
|
+
// src/core/token-estimator.ts
|
|
3762
|
+
var CJK_REGEX = /[\u2E80-\u9FFF\uA000-\uA4FF\uAC00-\uD7FF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF]/g;
|
|
3763
|
+
function estimateTokens(text) {
|
|
3764
|
+
if (!text) return 0;
|
|
3765
|
+
const cjkMatches = text.match(CJK_REGEX);
|
|
3766
|
+
const cjkCount = cjkMatches ? cjkMatches.length : 0;
|
|
3767
|
+
const nonCjkCount = text.length - cjkCount;
|
|
3768
|
+
const tokens = cjkCount * 1.5 + nonCjkCount * 0.25;
|
|
3769
|
+
return Math.ceil(tokens) + 4;
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3670
3772
|
// src/repl/dev-state.ts
|
|
3671
3773
|
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "fs";
|
|
3672
3774
|
import { join as join5 } from "path";
|
|
@@ -3777,6 +3879,10 @@ export {
|
|
|
3777
3879
|
computeCost,
|
|
3778
3880
|
formatCost,
|
|
3779
3881
|
parseSimpleYaml,
|
|
3882
|
+
persistToolRound,
|
|
3883
|
+
extractToolHistory,
|
|
3884
|
+
rebuildExtraMessages,
|
|
3885
|
+
estimateTokens,
|
|
3780
3886
|
SNAPSHOT_PROMPT,
|
|
3781
3887
|
sessionHasMeaningfulContent,
|
|
3782
3888
|
saveDevState,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
11
11
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
12
12
|
runTestsTool
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GA74LZ62.js";
|
|
14
14
|
|
|
15
15
|
// src/tools/builtin/bash.ts
|
|
16
16
|
import { execSync } from "child_process";
|
|
@@ -1098,6 +1098,20 @@ function snapToLineBoundary(content, target, direction) {
|
|
|
1098
1098
|
return target;
|
|
1099
1099
|
}
|
|
1100
1100
|
}
|
|
1101
|
+
var PERSIST_MAX_CHARS = 8192;
|
|
1102
|
+
function truncateForPersist(content, maxChars = PERSIST_MAX_CHARS) {
|
|
1103
|
+
if (content.length <= maxChars) return content;
|
|
1104
|
+
const headSize = Math.floor(maxChars * 0.7);
|
|
1105
|
+
const tailSize = Math.floor(maxChars * 0.2);
|
|
1106
|
+
const head = content.slice(0, headSize);
|
|
1107
|
+
const tail = content.slice(-tailSize);
|
|
1108
|
+
const omitted = content.length - headSize - tailSize;
|
|
1109
|
+
return `${head}
|
|
1110
|
+
|
|
1111
|
+
[... ${omitted} chars omitted for storage ...]
|
|
1112
|
+
|
|
1113
|
+
${tail}`;
|
|
1114
|
+
}
|
|
1101
1115
|
function truncateOutput(content, toolName, maxChars) {
|
|
1102
1116
|
const limit = maxChars ?? activeMaxChars;
|
|
1103
1117
|
if (content.length <= limit) return content;
|
|
@@ -4208,6 +4222,7 @@ export {
|
|
|
4208
4222
|
checkPermission,
|
|
4209
4223
|
setMaxOutputCap,
|
|
4210
4224
|
setContextWindow,
|
|
4225
|
+
truncateForPersist,
|
|
4211
4226
|
truncateOutput,
|
|
4212
4227
|
ToolExecutor,
|
|
4213
4228
|
lastResponseStore,
|
|
@@ -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-W66JWWKF.js");
|
|
389
389
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
390
390
|
let interrupted = false;
|
|
391
391
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
clearDevState,
|
|
14
14
|
computeCost,
|
|
15
15
|
detectsHallucinatedFileOp,
|
|
16
|
+
estimateTokens,
|
|
17
|
+
extractToolHistory,
|
|
16
18
|
extractWrittenFilePaths,
|
|
17
19
|
findPhantomClaims,
|
|
18
20
|
formatCost,
|
|
@@ -24,10 +26,12 @@ import {
|
|
|
24
26
|
hadPreviousWriteToolCalls,
|
|
25
27
|
loadDevState,
|
|
26
28
|
parseSimpleYaml,
|
|
29
|
+
persistToolRound,
|
|
30
|
+
rebuildExtraMessages,
|
|
27
31
|
saveDevState,
|
|
28
32
|
sessionHasMeaningfulContent,
|
|
29
33
|
setupProxy
|
|
30
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-2WGVM2J2.js";
|
|
31
35
|
import {
|
|
32
36
|
ToolExecutor,
|
|
33
37
|
ToolRegistry,
|
|
@@ -41,7 +45,7 @@ import {
|
|
|
41
45
|
spawnAgentContext,
|
|
42
46
|
theme,
|
|
43
47
|
undoStack
|
|
44
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-2ZCD5F4X.js";
|
|
45
49
|
import {
|
|
46
50
|
fileCheckpoints
|
|
47
51
|
} from "./chunk-4BKXL7SM.js";
|
|
@@ -66,7 +70,7 @@ import {
|
|
|
66
70
|
SKILLS_DIR_NAME,
|
|
67
71
|
VERSION,
|
|
68
72
|
buildUserIdentityPrompt
|
|
69
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-GA74LZ62.js";
|
|
70
74
|
|
|
71
75
|
// src/index.ts
|
|
72
76
|
import { program } from "commander";
|
|
@@ -2161,7 +2165,7 @@ ${hint}` : "")
|
|
|
2161
2165
|
usage: "/test [command|filter]",
|
|
2162
2166
|
async execute(args, ctx) {
|
|
2163
2167
|
try {
|
|
2164
|
-
const { executeTests } = await import("./run-tests-
|
|
2168
|
+
const { executeTests } = await import("./run-tests-CT4PRGGP.js");
|
|
2165
2169
|
const argStr = args.join(" ").trim();
|
|
2166
2170
|
let testArgs = {};
|
|
2167
2171
|
if (argStr) {
|
|
@@ -4396,7 +4400,7 @@ Session '${this.resumeSessionId}' not found.
|
|
|
4396
4400
|
* 混合 CJK / ASCII 文本平均约 2.5 字符 = 1 token(与 renderer.ts 中的估算公式一致)。
|
|
4397
4401
|
*/
|
|
4398
4402
|
estimateTokens(text) {
|
|
4399
|
-
return
|
|
4403
|
+
return estimateTokens(text);
|
|
4400
4404
|
}
|
|
4401
4405
|
/**
|
|
4402
4406
|
* 估算当前对话的总 token 消耗(system prompt + 所有 session messages)。
|
|
@@ -4415,6 +4419,11 @@ Session '${this.resumeSessionId}' not found.
|
|
|
4415
4419
|
if (part.type === "text" && part.text) total += this.estimateTokens(part.text);
|
|
4416
4420
|
}
|
|
4417
4421
|
}
|
|
4422
|
+
if (msg.toolCalls) {
|
|
4423
|
+
for (const tc of msg.toolCalls) {
|
|
4424
|
+
total += this.estimateTokens(JSON.stringify(tc.arguments));
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4418
4427
|
}
|
|
4419
4428
|
}
|
|
4420
4429
|
return total;
|
|
@@ -4869,8 +4878,9 @@ Session '${this.resumeSessionId}' not found.
|
|
|
4869
4878
|
if (this.blockedTools) {
|
|
4870
4879
|
toolDefs = toolDefs.filter((t) => !this.blockedTools.has(t.name));
|
|
4871
4880
|
}
|
|
4872
|
-
const
|
|
4873
|
-
const
|
|
4881
|
+
const { baseMessages: cleanMessages, toolHistory } = extractToolHistory(messages);
|
|
4882
|
+
const apiMessages = [...cleanMessages];
|
|
4883
|
+
const extraMessages = toolHistory.length > 0 ? rebuildExtraMessages(provider, toolHistory) : [];
|
|
4874
4884
|
const maxToolRounds = this.maxToolRoundsOverride ?? this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
4875
4885
|
const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
|
|
4876
4886
|
const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : DEFAULT_AUTO_PAUSE_INTERVAL;
|
|
@@ -5260,6 +5270,11 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
5260
5270
|
const reasoningContent = "reasoningContent" in result ? result.reasoningContent : void 0;
|
|
5261
5271
|
const newMsgs = provider.buildToolResultMessages(result.toolCalls, toolResults, reasoningContent);
|
|
5262
5272
|
extraMessages.push(...newMsgs);
|
|
5273
|
+
const streamedContent = "content" in result ? result.content : void 0;
|
|
5274
|
+
persistToolRound(session, result.toolCalls, toolResults, {
|
|
5275
|
+
assistantContent: streamedContent,
|
|
5276
|
+
reasoningContent
|
|
5277
|
+
});
|
|
5263
5278
|
const thisRoundHadWrite = result.toolCalls.some(
|
|
5264
5279
|
(tc) => tc.name === "write_file" || tc.name === "edit_file"
|
|
5265
5280
|
);
|
|
@@ -5695,7 +5710,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
5695
5710
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
5696
5711
|
process.exit(1);
|
|
5697
5712
|
}
|
|
5698
|
-
const { startWebServer } = await import("./server-
|
|
5713
|
+
const { startWebServer } = await import("./server-YJWIPDF2.js");
|
|
5699
5714
|
await startWebServer({ port, host: options.host });
|
|
5700
5715
|
});
|
|
5701
5716
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -5928,7 +5943,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
5928
5943
|
}),
|
|
5929
5944
|
config.get("customProviders")
|
|
5930
5945
|
);
|
|
5931
|
-
const { startHub } = await import("./hub-
|
|
5946
|
+
const { startHub } = await import("./hub-WA2DZCSQ.js");
|
|
5932
5947
|
await startHub(
|
|
5933
5948
|
{
|
|
5934
5949
|
topic: topic ?? "",
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
TOOL_CALL_REMINDER,
|
|
10
10
|
computeCost,
|
|
11
11
|
detectsHallucinatedFileOp,
|
|
12
|
+
estimateTokens,
|
|
13
|
+
extractToolHistory,
|
|
12
14
|
formatCost,
|
|
13
15
|
formatGitContextForPrompt,
|
|
14
16
|
getContentText,
|
|
@@ -16,8 +18,10 @@ import {
|
|
|
16
18
|
getGitRoot,
|
|
17
19
|
hadPreviousWriteToolCalls,
|
|
18
20
|
loadDevState,
|
|
21
|
+
persistToolRound,
|
|
22
|
+
rebuildExtraMessages,
|
|
19
23
|
setupProxy
|
|
20
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-2WGVM2J2.js";
|
|
21
25
|
import {
|
|
22
26
|
AuthManager
|
|
23
27
|
} from "./chunk-BYNY5JPB.js";
|
|
@@ -36,7 +40,7 @@ import {
|
|
|
36
40
|
spawnAgentContext,
|
|
37
41
|
truncateOutput,
|
|
38
42
|
undoStack
|
|
39
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-2ZCD5F4X.js";
|
|
40
44
|
import "./chunk-4BKXL7SM.js";
|
|
41
45
|
import {
|
|
42
46
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -56,7 +60,7 @@ import {
|
|
|
56
60
|
SKILLS_DIR_NAME,
|
|
57
61
|
VERSION,
|
|
58
62
|
buildUserIdentityPrompt
|
|
59
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-GA74LZ62.js";
|
|
60
64
|
|
|
61
65
|
// src/web/server.ts
|
|
62
66
|
import express from "express";
|
|
@@ -654,7 +658,7 @@ var SessionHandler = class _SessionHandler {
|
|
|
654
658
|
}
|
|
655
659
|
/** 粗略估算文本 token 数(2.5 chars/token)*/
|
|
656
660
|
estTokens(text) {
|
|
657
|
-
return
|
|
661
|
+
return estimateTokens(text);
|
|
658
662
|
}
|
|
659
663
|
/**
|
|
660
664
|
* 估算当前 agentic 请求总 token 数(session messages + extraMessages + system prompt)。
|
|
@@ -675,6 +679,11 @@ var SessionHandler = class _SessionHandler {
|
|
|
675
679
|
}
|
|
676
680
|
}
|
|
677
681
|
}
|
|
682
|
+
if (msg.toolCalls) {
|
|
683
|
+
for (const tc of msg.toolCalls) {
|
|
684
|
+
total += this.estTokens(JSON.stringify(tc.arguments));
|
|
685
|
+
}
|
|
686
|
+
}
|
|
678
687
|
}
|
|
679
688
|
}
|
|
680
689
|
if (extraMessages.length > 0) {
|
|
@@ -814,8 +823,9 @@ var SessionHandler = class _SessionHandler {
|
|
|
814
823
|
}
|
|
815
824
|
async handleChatWithTools(provider, messages, toolDefs) {
|
|
816
825
|
const session = this.sessions.current;
|
|
817
|
-
const
|
|
818
|
-
const
|
|
826
|
+
const { baseMessages: cleanMessages, toolHistory } = extractToolHistory(messages);
|
|
827
|
+
const apiMessages = [...cleanMessages];
|
|
828
|
+
const extraMessages = toolHistory.length > 0 ? rebuildExtraMessages(provider, toolHistory) : [];
|
|
819
829
|
const maxToolRounds = this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
820
830
|
const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
|
|
821
831
|
const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : 50;
|
|
@@ -1020,6 +1030,10 @@ Details: ${errMsg.split("\n")[0]}
|
|
|
1020
1030
|
const reasoningContent = result.reasoningContent;
|
|
1021
1031
|
const newMsgs = provider.buildToolResultMessages(result.toolCalls, toolResults, reasoningContent);
|
|
1022
1032
|
extraMessages.push(...newMsgs);
|
|
1033
|
+
persistToolRound(session, result.toolCalls, toolResults, {
|
|
1034
|
+
assistantContent: result.content,
|
|
1035
|
+
reasoningContent
|
|
1036
|
+
});
|
|
1023
1037
|
const allFree = result.toolCalls.every((tc) => FREE_ROUND_TOOLS.has(tc.name));
|
|
1024
1038
|
if (allFree) {
|
|
1025
1039
|
consecutiveFreeRounds++;
|
|
@@ -1915,7 +1929,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1915
1929
|
case "test": {
|
|
1916
1930
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
1917
1931
|
try {
|
|
1918
|
-
const { executeTests } = await import("./run-tests-
|
|
1932
|
+
const { executeTests } = await import("./run-tests-CT4PRGGP.js");
|
|
1919
1933
|
const argStr = args.join(" ").trim();
|
|
1920
1934
|
let testArgs = {};
|
|
1921
1935
|
if (argStr) {
|
|
@@ -2432,11 +2446,18 @@ Add .md files to create commands.` });
|
|
|
2432
2446
|
sendSessionMessages() {
|
|
2433
2447
|
const session = this.sessions.current;
|
|
2434
2448
|
if (!session) return;
|
|
2435
|
-
const messages = session.messages.map((m) =>
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2449
|
+
const messages = session.messages.map((m) => {
|
|
2450
|
+
const out = {
|
|
2451
|
+
role: m.role,
|
|
2452
|
+
content: getContentText(m.content),
|
|
2453
|
+
timestamp: m.timestamp?.toISOString()
|
|
2454
|
+
};
|
|
2455
|
+
if (m.toolCalls) out.toolCalls = m.toolCalls;
|
|
2456
|
+
if (m.toolCallId) out.toolCallId = m.toolCallId;
|
|
2457
|
+
if (m.toolName) out.toolName = m.toolName;
|
|
2458
|
+
if (m.isError !== void 0) out.isError = m.isError;
|
|
2459
|
+
return out;
|
|
2460
|
+
});
|
|
2440
2461
|
this.send({
|
|
2441
2462
|
type: "session_messages",
|
|
2442
2463
|
sessionId: session.id,
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2ZCD5F4X.js";
|
|
8
8
|
import "./chunk-4BKXL7SM.js";
|
|
9
9
|
import {
|
|
10
10
|
SUBAGENT_ALLOWED_TOOLS
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-GA74LZ62.js";
|
|
12
12
|
|
|
13
13
|
// src/hub/task-orchestrator.ts
|
|
14
14
|
import { createInterface } from "readline";
|
package/dist/web/client/app.js
CHANGED
|
@@ -717,6 +717,49 @@ function escapeHtml(str) {
|
|
|
717
717
|
return div.innerHTML;
|
|
718
718
|
}
|
|
719
719
|
|
|
720
|
+
/**
|
|
721
|
+
* Create a static tool-call card for session history view.
|
|
722
|
+
* Groups an assistant tool-call message with its subsequent tool results.
|
|
723
|
+
* Unlike live tool cards, these have no timer — just the final state.
|
|
724
|
+
*
|
|
725
|
+
* @param {Object} assistantMsg - The assistant message with toolCalls array
|
|
726
|
+
* @param {Object[]} resultMsgs - Subsequent tool-result messages
|
|
727
|
+
*/
|
|
728
|
+
function createHistoryToolCards(assistantMsg, resultMsgs) {
|
|
729
|
+
const toolCalls = assistantMsg.toolCalls || [];
|
|
730
|
+
for (let i = 0; i < toolCalls.length; i++) {
|
|
731
|
+
const tc = toolCalls[i];
|
|
732
|
+
const rm = resultMsgs[i]; // may be undefined if results are missing
|
|
733
|
+
const isError = rm ? rm.isError : false;
|
|
734
|
+
const resultContent = rm ? rm.content : '';
|
|
735
|
+
|
|
736
|
+
const el = document.createElement('details');
|
|
737
|
+
el.className = 'tool-card tool-border-safe my-1';
|
|
738
|
+
|
|
739
|
+
const statusIcon = rm ? (isError ? '✗' : '✓') : '?';
|
|
740
|
+
const statusClass = rm ? (isError ? 'text-error' : 'text-success') : 'opacity-50';
|
|
741
|
+
const badgeClass = isError ? 'badge-error' : 'badge-info';
|
|
742
|
+
const levelIcon = isError ? '⚠' : '⚙';
|
|
743
|
+
|
|
744
|
+
const argsHtml = tc.arguments ? formatToolArgs(tc.arguments) : '';
|
|
745
|
+
const truncResult = resultContent.length > 500
|
|
746
|
+
? resultContent.slice(0, 500) + '...'
|
|
747
|
+
: resultContent;
|
|
748
|
+
|
|
749
|
+
el.innerHTML = `
|
|
750
|
+
<summary class="flex items-center gap-2 w-full cursor-pointer select-none py-1">
|
|
751
|
+
<span class="badge ${badgeClass} badge-sm gap-1">${levelIcon} ${escapeHtml(tc.name)}</span>
|
|
752
|
+
<span class="tool-result-badge text-xs ml-auto ${statusClass}">${statusIcon}</span>
|
|
753
|
+
</summary>
|
|
754
|
+
<div class="tool-details-body pt-1">
|
|
755
|
+
${argsHtml ? `<div class="tool-args w-full">${argsHtml}</div>` : ''}
|
|
756
|
+
${rm ? `<div class="tool-result-content mt-2 pt-2 border-t border-base-content/10 w-full ${isError ? 'text-error' : 'text-success'}">${statusIcon} ${escapeHtml(truncResult)}</div>` : ''}
|
|
757
|
+
</div>
|
|
758
|
+
`;
|
|
759
|
+
messagesEl.appendChild(el);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
720
763
|
function scrollToBottom() {
|
|
721
764
|
requestAnimationFrame(() => {
|
|
722
765
|
chatArea.scrollTop = chatArea.scrollHeight;
|
|
@@ -1074,6 +1117,56 @@ function updateBatchBar() {
|
|
|
1074
1117
|
* write it into the tab's `messagesHtml` cache; the live DOM is left
|
|
1075
1118
|
* untouched so the active tab's content never flashes wrong data.
|
|
1076
1119
|
*/
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Render a messages array into the live DOM (messagesEl).
|
|
1123
|
+
* Handles user, assistant (text), assistant (toolCalls), and tool result messages.
|
|
1124
|
+
* Groups consecutive assistant+toolCalls with subsequent tool results into cards.
|
|
1125
|
+
*/
|
|
1126
|
+
function renderMessagesArray(messages) {
|
|
1127
|
+
let i = 0;
|
|
1128
|
+
while (i < messages.length) {
|
|
1129
|
+
const m = messages[i];
|
|
1130
|
+
if (m.role === 'user') {
|
|
1131
|
+
addUserMessage(m.content);
|
|
1132
|
+
i++;
|
|
1133
|
+
} else if (m.role === 'assistant' && m.toolCalls && m.toolCalls.length > 0) {
|
|
1134
|
+
// Assistant message with tool calls — collect subsequent tool results
|
|
1135
|
+
const resultMsgs = [];
|
|
1136
|
+
let j = i + 1;
|
|
1137
|
+
while (j < messages.length && messages[j].role === 'tool') {
|
|
1138
|
+
resultMsgs.push(messages[j]);
|
|
1139
|
+
j++;
|
|
1140
|
+
}
|
|
1141
|
+
// If the assistant also had text content, render it first
|
|
1142
|
+
if (m.content && m.content.trim()) {
|
|
1143
|
+
const el = createAssistantMessage();
|
|
1144
|
+
renderMarkdown(el, m.content);
|
|
1145
|
+
}
|
|
1146
|
+
createHistoryToolCards(m, resultMsgs);
|
|
1147
|
+
i = j;
|
|
1148
|
+
} else if (m.role === 'tool') {
|
|
1149
|
+
// Orphan tool result (no preceding assistant+toolCalls) — render as info card
|
|
1150
|
+
const statusIcon = m.isError ? '✗' : '✓';
|
|
1151
|
+
const statusClass = m.isError ? 'text-error' : 'text-success';
|
|
1152
|
+
const el = document.createElement('div');
|
|
1153
|
+
el.className = `tool-card tool-border-safe my-1 p-2 ${statusClass}`;
|
|
1154
|
+
const toolLabel = m.toolName ? escapeHtml(m.toolName) : 'tool';
|
|
1155
|
+
const truncContent = m.content && m.content.length > 300
|
|
1156
|
+
? m.content.slice(0, 300) + '...' : (m.content || '');
|
|
1157
|
+
el.innerHTML = `<span class="badge badge-info badge-sm">${toolLabel}</span> ${statusIcon} ${escapeHtml(truncContent)}`;
|
|
1158
|
+
messagesEl.appendChild(el);
|
|
1159
|
+
i++;
|
|
1160
|
+
} else if (m.role === 'assistant') {
|
|
1161
|
+
const el = createAssistantMessage();
|
|
1162
|
+
renderMarkdown(el, m.content);
|
|
1163
|
+
i++;
|
|
1164
|
+
} else {
|
|
1165
|
+
// system or unknown — skip
|
|
1166
|
+
i++;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1077
1170
|
function renderSessionMessages(msg) {
|
|
1078
1171
|
// Back-compat: if called with a bare array (legacy), treat as active-tab apply
|
|
1079
1172
|
const messages = Array.isArray(msg) ? msg : msg.messages;
|
|
@@ -1096,14 +1189,7 @@ function renderSessionMessages(msg) {
|
|
|
1096
1189
|
// Active tab: paint directly into the live DOM (preserves any in-flight
|
|
1097
1190
|
// streaming helpers that rely on messagesEl)
|
|
1098
1191
|
messagesEl.innerHTML = '';
|
|
1099
|
-
|
|
1100
|
-
if (m.role === 'user') {
|
|
1101
|
-
addUserMessage(m.content);
|
|
1102
|
-
} else if (m.role === 'assistant') {
|
|
1103
|
-
const el = createAssistantMessage();
|
|
1104
|
-
renderMarkdown(el, m.content);
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1192
|
+
renderMessagesArray(messages);
|
|
1107
1193
|
scrollToBottom();
|
|
1108
1194
|
// Snapshot into cache so subsequent tab-snapshots see the latest content
|
|
1109
1195
|
sessionTabs[targetIdx].messagesHtml = messagesEl.innerHTML;
|
|
@@ -1136,14 +1222,7 @@ function buildMessagesHtmlOffDom(messages) {
|
|
|
1136
1222
|
currentAssistantContent = '';
|
|
1137
1223
|
currentThinkingEl = null;
|
|
1138
1224
|
currentThinkingContent = '';
|
|
1139
|
-
|
|
1140
|
-
if (m.role === 'user') {
|
|
1141
|
-
addUserMessage(m.content);
|
|
1142
|
-
} else if (m.role === 'assistant') {
|
|
1143
|
-
const el = createAssistantMessage();
|
|
1144
|
-
renderMarkdown(el, m.content);
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1225
|
+
renderMessagesArray(messages);
|
|
1147
1226
|
return messagesEl.innerHTML;
|
|
1148
1227
|
} finally {
|
|
1149
1228
|
messagesEl.innerHTML = savedHtml;
|