@nomad-e/bluma-cli 0.6.8 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/config/skills/pdf/SKILL.md +7 -0
- package/dist/config/skills/pdf/scripts/create_report.py +18 -2
- package/dist/config/skills/pdf/scripts/merge_pdfs.py +9 -0
- package/dist/config/skills/xlsx/SKILL.md +8 -0
- package/dist/config/skills/xlsx/scripts/recalc.py +9 -0
- package/dist/main.js +1966 -1396
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -12575,7 +12575,7 @@ var getInstance = (stdout, createInstance) => {
|
|
|
12575
12575
|
|
|
12576
12576
|
// src/main.ts
|
|
12577
12577
|
import { EventEmitter as EventEmitter7 } from "events";
|
|
12578
|
-
import
|
|
12578
|
+
import fs46 from "fs";
|
|
12579
12579
|
import path49 from "path";
|
|
12580
12580
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
12581
12581
|
import { spawn as spawn6 } from "child_process";
|
|
@@ -12638,9 +12638,9 @@ var BLUMA_TERMINAL = {
|
|
|
12638
12638
|
// red-500
|
|
12639
12639
|
warn: "#eab308",
|
|
12640
12640
|
// yellow-500
|
|
12641
|
-
diffAdded: "#
|
|
12641
|
+
diffAdded: "#203a2b",
|
|
12642
12642
|
// green-900
|
|
12643
|
-
diffRemoved: "#
|
|
12643
|
+
diffRemoved: "#4a221d",
|
|
12644
12644
|
// red-900
|
|
12645
12645
|
diffAddedWord: "#22c55e",
|
|
12646
12646
|
diffRemovedWord: "#ef4444",
|
|
@@ -12686,14 +12686,18 @@ var HeaderComponent = ({ workdir, cliVersion = "?" }) => /* @__PURE__ */ jsxs2(
|
|
|
12686
12686
|
Box_default,
|
|
12687
12687
|
{
|
|
12688
12688
|
flexDirection: "column",
|
|
12689
|
-
|
|
12689
|
+
marginBottom: 1,
|
|
12690
|
+
width: "100%",
|
|
12690
12691
|
children: [
|
|
12691
12692
|
/* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", flexWrap: "wrap", children: [
|
|
12692
|
-
/* @__PURE__ */
|
|
12693
|
+
/* @__PURE__ */ jsxs2(Text, { color: BLUMA_TERMINAL.dim, children: [
|
|
12694
|
+
"$_",
|
|
12695
|
+
" "
|
|
12696
|
+
] }),
|
|
12693
12697
|
/* @__PURE__ */ jsx9(Text, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, children: "Blu" }),
|
|
12694
12698
|
/* @__PURE__ */ jsx9(Text, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, children: "Ma" })
|
|
12695
12699
|
] }),
|
|
12696
|
-
/* @__PURE__ */ jsx9(Box_default, { flexDirection: "row", flexWrap: "wrap",
|
|
12700
|
+
/* @__PURE__ */ jsx9(Box_default, { flexDirection: "row", flexWrap: "wrap", width: "100%", children: /* @__PURE__ */ jsx9(Text, { color: BLUMA_TERMINAL.dim, children: "Base Language Unit \xB7 Model Agent" }) })
|
|
12697
12701
|
]
|
|
12698
12702
|
}
|
|
12699
12703
|
);
|
|
@@ -12701,7 +12705,7 @@ var Header = memo(HeaderComponent);
|
|
|
12701
12705
|
var SessionInfoComponent = ({
|
|
12702
12706
|
toolsCount,
|
|
12703
12707
|
mcpStatus
|
|
12704
|
-
}) => /* @__PURE__ */ jsxs2(Box_default, {
|
|
12708
|
+
}) => /* @__PURE__ */ jsxs2(Box_default, { children: [
|
|
12705
12709
|
/* @__PURE__ */ jsx9(Text, { dimColor: true, children: "mcp " }),
|
|
12706
12710
|
/* @__PURE__ */ jsx9(Text, { color: mcpStatus === "connected" ? BLUMA_TERMINAL.success : BLUMA_TERMINAL.warn, children: mcpStatus === "connected" ? "on" : "\u2026" }),
|
|
12707
12711
|
toolsCount !== null ? /* @__PURE__ */ jsxs2(Text, { dimColor: true, children: [
|
|
@@ -12721,7 +12725,7 @@ var TaskStatusBarComponent = ({
|
|
|
12721
12725
|
EXECUTION: BLUMA_TERMINAL.magenta,
|
|
12722
12726
|
VERIFICATION: BLUMA_TERMINAL.orange
|
|
12723
12727
|
};
|
|
12724
|
-
return /* @__PURE__ */ jsxs2(Box_default, {
|
|
12728
|
+
return /* @__PURE__ */ jsxs2(Box_default, { children: [
|
|
12725
12729
|
/* @__PURE__ */ jsxs2(Text, { color: modeColors[mode], bold: true, children: [
|
|
12726
12730
|
"[",
|
|
12727
12731
|
mode.charAt(0),
|
|
@@ -15362,7 +15366,7 @@ function createDiff(filename, oldContent, newContent) {
|
|
|
15362
15366
|
const prefix = part.added ? "+" : part.removed ? "-" : " ";
|
|
15363
15367
|
const lines = part.value.split("\n").slice(0, -1);
|
|
15364
15368
|
for (const line of lines) {
|
|
15365
|
-
if (line === "
|
|
15369
|
+
if (line === "") continue;
|
|
15366
15370
|
diffString += `${prefix}${line}
|
|
15367
15371
|
`;
|
|
15368
15372
|
lineCount++;
|
|
@@ -17828,9 +17832,6 @@ async function exitPlanMode(args) {
|
|
|
17828
17832
|
// src/app/agent/tools/TaskToolsTool/TaskToolsTool.ts
|
|
17829
17833
|
var bySession = /* @__PURE__ */ new Map();
|
|
17830
17834
|
var currentSessionId = "";
|
|
17831
|
-
function setTaskToolSessionContext(sessionId) {
|
|
17832
|
-
currentSessionId = String(sessionId || "").trim() || "default";
|
|
17833
|
-
}
|
|
17834
17835
|
function sessionMap() {
|
|
17835
17836
|
const sid2 = currentSessionId || "default";
|
|
17836
17837
|
let m = bySession.get(sid2);
|
|
@@ -18366,6 +18367,14 @@ async function fileWrite(args) {
|
|
|
18366
18367
|
error: `File already exists and overwrite=false: ${resolvedPath}`
|
|
18367
18368
|
};
|
|
18368
18369
|
}
|
|
18370
|
+
let previousContent = "";
|
|
18371
|
+
if (existed) {
|
|
18372
|
+
try {
|
|
18373
|
+
previousContent = await fs17.readFile(resolvedPath, "utf-8");
|
|
18374
|
+
} catch {
|
|
18375
|
+
previousContent = "";
|
|
18376
|
+
}
|
|
18377
|
+
}
|
|
18369
18378
|
if (create_directories) {
|
|
18370
18379
|
await fs17.mkdir(dir, { recursive: true });
|
|
18371
18380
|
}
|
|
@@ -18374,7 +18383,9 @@ async function fileWrite(args) {
|
|
|
18374
18383
|
return {
|
|
18375
18384
|
success: true,
|
|
18376
18385
|
filepath: resolvedPath,
|
|
18386
|
+
content: String(content),
|
|
18377
18387
|
bytes_written: bytes,
|
|
18388
|
+
previous_content: existed ? previousContent : "",
|
|
18378
18389
|
created: !existed,
|
|
18379
18390
|
overwritten: existed
|
|
18380
18391
|
};
|
|
@@ -24540,8 +24551,7 @@ Rules:
|
|
|
24540
24551
|
],
|
|
24541
24552
|
temperature: 0,
|
|
24542
24553
|
max_tokens: 4096,
|
|
24543
|
-
userContext
|
|
24544
|
-
response_format: { type: "json_object" }
|
|
24554
|
+
userContext
|
|
24545
24555
|
};
|
|
24546
24556
|
const resp = await llmService.chatCompletion(requestBase);
|
|
24547
24557
|
const text = resp.choices[0]?.message?.content;
|
|
@@ -24836,6 +24846,39 @@ function sanitizeConversationForProvider(conversationHistory) {
|
|
|
24836
24846
|
droppingCorruptTurn = true;
|
|
24837
24847
|
continue;
|
|
24838
24848
|
}
|
|
24849
|
+
const expectedToolIds = new Set(
|
|
24850
|
+
toolCalls.map((call) => String(call?.id ?? "").trim()).filter(Boolean)
|
|
24851
|
+
);
|
|
24852
|
+
const turnBuffer = [conversationHistory[index]];
|
|
24853
|
+
const seenToolIds = /* @__PURE__ */ new Set();
|
|
24854
|
+
let j = index + 1;
|
|
24855
|
+
let turnValid = true;
|
|
24856
|
+
while (j < conversationHistory.length) {
|
|
24857
|
+
const next = conversationHistory[j];
|
|
24858
|
+
if (next?.role !== "tool") {
|
|
24859
|
+
break;
|
|
24860
|
+
}
|
|
24861
|
+
const toolCallId = String(next?.tool_call_id ?? "").trim();
|
|
24862
|
+
if (!toolCallId || !expectedToolIds.has(toolCallId) || seenToolIds.has(toolCallId)) {
|
|
24863
|
+
issues.push({
|
|
24864
|
+
index,
|
|
24865
|
+
reason: "assistant tool_calls were not followed by a valid tool turn",
|
|
24866
|
+
toolNames: toolCalls.map((call) => String(call?.function?.name ?? "unknown"))
|
|
24867
|
+
});
|
|
24868
|
+
droppingCorruptTurn = true;
|
|
24869
|
+
turnValid = false;
|
|
24870
|
+
break;
|
|
24871
|
+
}
|
|
24872
|
+
seenToolIds.add(toolCallId);
|
|
24873
|
+
turnBuffer.push(conversationHistory[j]);
|
|
24874
|
+
j += 1;
|
|
24875
|
+
}
|
|
24876
|
+
if (turnValid) {
|
|
24877
|
+
cleaned.push(...turnBuffer);
|
|
24878
|
+
index = j - 1;
|
|
24879
|
+
continue;
|
|
24880
|
+
}
|
|
24881
|
+
continue;
|
|
24839
24882
|
}
|
|
24840
24883
|
cleaned.push(conversationHistory[index]);
|
|
24841
24884
|
}
|
|
@@ -24858,50 +24901,9 @@ function partitionConversationIntoTurnSlices(conversationHistory) {
|
|
|
24858
24901
|
}
|
|
24859
24902
|
return turns;
|
|
24860
24903
|
}
|
|
24861
|
-
var
|
|
24862
|
-
|
|
24863
|
-
|
|
24864
|
-
"gpt-4o-2024-08-06": 128e3,
|
|
24865
|
-
"gpt-4-turbo": 128e3,
|
|
24866
|
-
"gpt-4-0125-preview": 128e3,
|
|
24867
|
-
"gpt-4-1106-preview": 128e3,
|
|
24868
|
-
"claude-3-5-sonnet": 2e5,
|
|
24869
|
-
"claude-3-opus": 2e5,
|
|
24870
|
-
"claude-3-sonnet": 2e5,
|
|
24871
|
-
"claude-3-haiku": 2e5,
|
|
24872
|
-
"claude-sonnet-4-6-1m": 1e6,
|
|
24873
|
-
"claude-opus-4-6-1m": 1e6
|
|
24874
|
-
};
|
|
24875
|
-
function getContextWindowForModel(modelName = "") {
|
|
24876
|
-
if (!modelName) {
|
|
24877
|
-
return MODEL_CONTEXT_WINDOW_DEFAULT;
|
|
24878
|
-
}
|
|
24879
|
-
const normalizedModel = modelName.toLowerCase();
|
|
24880
|
-
const envOverride = process.env.BLUMA_CONTEXT_WINDOW_OVERRIDE;
|
|
24881
|
-
if (envOverride) {
|
|
24882
|
-
const override = parseInt(envOverride, 10);
|
|
24883
|
-
if (!isNaN(override) && override > 0) {
|
|
24884
|
-
return override;
|
|
24885
|
-
}
|
|
24886
|
-
}
|
|
24887
|
-
for (const [modelPattern, contextWindow] of Object.entries(MODEL_CONTEXT_WINDOWS)) {
|
|
24888
|
-
if (normalizedModel.includes(modelPattern)) {
|
|
24889
|
-
return contextWindow;
|
|
24890
|
-
}
|
|
24891
|
-
}
|
|
24892
|
-
if (normalizedModel.includes("[1m]") || normalizedModel.includes("1m")) {
|
|
24893
|
-
return 1e6;
|
|
24894
|
-
}
|
|
24895
|
-
return MODEL_CONTEXT_WINDOW_DEFAULT;
|
|
24896
|
-
}
|
|
24897
|
-
function getContextInputBudgetForModel(modelName = "") {
|
|
24898
|
-
const contextWindow = getContextWindowForModel(modelName);
|
|
24899
|
-
const outputReserve = Math.max(8e3, Math.floor(contextWindow * 0.1));
|
|
24900
|
-
const effectiveContextPercent = 0.95;
|
|
24901
|
-
const inputBudget = Math.floor(
|
|
24902
|
-
(contextWindow - outputReserve) * effectiveContextPercent
|
|
24903
|
-
);
|
|
24904
|
-
return Math.max(inputBudget, 32e3);
|
|
24904
|
+
var STATIC_CONTEXT_INPUT_BUDGET = 96e3;
|
|
24905
|
+
function getContextInputBudgetForModel(_modelName = "") {
|
|
24906
|
+
return STATIC_CONTEXT_INPUT_BUDGET;
|
|
24905
24907
|
}
|
|
24906
24908
|
|
|
24907
24909
|
// src/app/agent/core/llm/llm.ts
|
|
@@ -24932,6 +24934,8 @@ function extractStreamingDelta(previous, next) {
|
|
|
24932
24934
|
}
|
|
24933
24935
|
|
|
24934
24936
|
// src/app/agent/core/llm/llm.ts
|
|
24937
|
+
init_logger();
|
|
24938
|
+
var llmLog = logger.child("llm");
|
|
24935
24939
|
function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
24936
24940
|
const msg = String(userMessage || "").slice(0, 300);
|
|
24937
24941
|
return {
|
|
@@ -25009,6 +25013,7 @@ var THINKING_TOKEN_BUDGET_BY_EFFORT = {
|
|
|
25009
25013
|
medium: 1024,
|
|
25010
25014
|
high: 2048
|
|
25011
25015
|
};
|
|
25016
|
+
var DEFAULT_REQUEST_MAX_TOKENS = 8192;
|
|
25012
25017
|
function getThinkingTokenBudgetForEffort(effort) {
|
|
25013
25018
|
if (!effort) return void 0;
|
|
25014
25019
|
return THINKING_TOKEN_BUDGET_BY_EFFORT[effort];
|
|
@@ -25022,6 +25027,13 @@ function buildVllmReasoningPayload(effort) {
|
|
|
25022
25027
|
}
|
|
25023
25028
|
};
|
|
25024
25029
|
}
|
|
25030
|
+
function resolveRequestMaxTokens(params) {
|
|
25031
|
+
const requested = params.max_tokens;
|
|
25032
|
+
if (typeof requested === "number" && Number.isFinite(requested) && requested > 0) {
|
|
25033
|
+
return Math.floor(requested);
|
|
25034
|
+
}
|
|
25035
|
+
return DEFAULT_REQUEST_MAX_TOKENS;
|
|
25036
|
+
}
|
|
25025
25037
|
function resolveReasoningEffortForRequest(params, runtimeConfig = getRuntimeConfig()) {
|
|
25026
25038
|
const policy = getSandboxPolicy();
|
|
25027
25039
|
if (policy.isSandbox) {
|
|
@@ -25029,24 +25041,188 @@ function resolveReasoningEffortForRequest(params, runtimeConfig = getRuntimeConf
|
|
|
25029
25041
|
}
|
|
25030
25042
|
return params.reasoning?.effort ?? runtimeConfig.reasoningEffort;
|
|
25031
25043
|
}
|
|
25032
|
-
function buildChatCompletionRequestBody(params, runtimeConfig = getRuntimeConfig(), stream = false) {
|
|
25044
|
+
function buildChatCompletionRequestBody(params, runtimeConfig = getRuntimeConfig(), stream = false, options) {
|
|
25033
25045
|
const tools = params.tools;
|
|
25034
25046
|
const hasTools = Array.isArray(tools) && tools.length > 0;
|
|
25035
25047
|
const effort = resolveReasoningEffortForRequest(params, runtimeConfig);
|
|
25036
|
-
const reasoningPayload = buildVllmReasoningPayload(effort);
|
|
25048
|
+
const reasoningPayload = options?.disableReasoning ? void 0 : buildVllmReasoningPayload(effort);
|
|
25049
|
+
const messages = sanitizeMessagesForRequest(params.messages, options);
|
|
25037
25050
|
return {
|
|
25038
25051
|
model: params.model || runtimeConfig.model || "auto",
|
|
25039
|
-
messages
|
|
25040
|
-
tools: hasTools ? tools : void 0,
|
|
25041
|
-
tool_choice: hasTools ? "auto" : void 0,
|
|
25042
|
-
parallel_tool_calls: params.parallel_tool_calls ?? false,
|
|
25052
|
+
messages,
|
|
25053
|
+
tools: hasTools && !options?.disableTools ? tools : void 0,
|
|
25054
|
+
tool_choice: hasTools && !options?.disableTools ? "auto" : void 0,
|
|
25055
|
+
parallel_tool_calls: options?.disableParallelToolCalls ? void 0 : params.parallel_tool_calls ?? false,
|
|
25043
25056
|
temperature: params.temperature ?? 0,
|
|
25044
25057
|
...reasoningPayload ?? {},
|
|
25045
|
-
max_tokens: params
|
|
25058
|
+
max_tokens: resolveRequestMaxTokens(params),
|
|
25046
25059
|
response_format: params.response_format,
|
|
25047
25060
|
...stream ? { stream: true } : {}
|
|
25048
25061
|
};
|
|
25049
25062
|
}
|
|
25063
|
+
function isBadRequestLike(error) {
|
|
25064
|
+
const anyError = error;
|
|
25065
|
+
if (anyError && typeof anyError.status === "number" && anyError.status === 400) {
|
|
25066
|
+
return true;
|
|
25067
|
+
}
|
|
25068
|
+
const message2 = String(anyError?.message ?? error ?? "").toLowerCase();
|
|
25069
|
+
return message2.includes("400 status code") || message2.includes("bad request");
|
|
25070
|
+
}
|
|
25071
|
+
function describeFallback(options) {
|
|
25072
|
+
const parts = [];
|
|
25073
|
+
if (options.disableReasoning) parts.push("no_reasoning");
|
|
25074
|
+
if (options.disableParallelToolCalls) parts.push("no_parallel");
|
|
25075
|
+
if (options.disableTools) parts.push("no_tools");
|
|
25076
|
+
if (options.stripToolTurns) parts.push("no_tool_turns");
|
|
25077
|
+
if (options.stripImages) parts.push("no_images");
|
|
25078
|
+
return parts.length > 0 ? parts.join("+") : "base";
|
|
25079
|
+
}
|
|
25080
|
+
function summarizeError(error) {
|
|
25081
|
+
if (!error || typeof error !== "object") {
|
|
25082
|
+
return { message: String(error) };
|
|
25083
|
+
}
|
|
25084
|
+
const anyError = error;
|
|
25085
|
+
return {
|
|
25086
|
+
name: typeof anyError.name === "string" ? anyError.name : void 0,
|
|
25087
|
+
message: typeof anyError.message === "string" ? anyError.message : String(error),
|
|
25088
|
+
status: typeof anyError.status === "number" ? anyError.status : void 0,
|
|
25089
|
+
code: typeof anyError.code === "string" || typeof anyError.code === "number" ? anyError.code : void 0,
|
|
25090
|
+
type: typeof anyError.type === "string" ? anyError.type : void 0
|
|
25091
|
+
};
|
|
25092
|
+
}
|
|
25093
|
+
function summarizeRequestForLog(params, runtimeConfig = getRuntimeConfig()) {
|
|
25094
|
+
const messageCounts = {
|
|
25095
|
+
total: params.messages.length,
|
|
25096
|
+
system: 0,
|
|
25097
|
+
user: 0,
|
|
25098
|
+
assistant: 0,
|
|
25099
|
+
tool: 0,
|
|
25100
|
+
function: 0
|
|
25101
|
+
};
|
|
25102
|
+
let toolCallMessages = 0;
|
|
25103
|
+
let imageMessages = 0;
|
|
25104
|
+
for (const message2 of params.messages) {
|
|
25105
|
+
if (message2.role in messageCounts) {
|
|
25106
|
+
messageCounts[message2.role] += 1;
|
|
25107
|
+
}
|
|
25108
|
+
if (message2.role === "assistant" && Array.isArray(message2.tool_calls) && message2.tool_calls.length > 0) {
|
|
25109
|
+
toolCallMessages += 1;
|
|
25110
|
+
}
|
|
25111
|
+
if (Array.isArray(message2.content)) {
|
|
25112
|
+
imageMessages += 1;
|
|
25113
|
+
}
|
|
25114
|
+
}
|
|
25115
|
+
const messagePreview = params.messages.slice(0, 8).map((message2) => {
|
|
25116
|
+
const content = message2.content;
|
|
25117
|
+
const toolCalls = Array.isArray(message2.tool_calls) ? message2.tool_calls : [];
|
|
25118
|
+
const preview = typeof content === "string" ? content.slice(0, 120) : Array.isArray(content) ? content.filter((part) => part && typeof part === "object" && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(" ").slice(0, 120) : null;
|
|
25119
|
+
return {
|
|
25120
|
+
role: message2.role,
|
|
25121
|
+
name: message2.name ?? null,
|
|
25122
|
+
content_type: Array.isArray(content) ? "array" : typeof content,
|
|
25123
|
+
preview: preview || null,
|
|
25124
|
+
tool_calls: toolCalls.length,
|
|
25125
|
+
tool_call_id: message2.tool_call_id ?? null
|
|
25126
|
+
};
|
|
25127
|
+
});
|
|
25128
|
+
return {
|
|
25129
|
+
model: params.model || runtimeConfig.model || "auto",
|
|
25130
|
+
temperature: params.temperature ?? 0,
|
|
25131
|
+
max_tokens: resolveRequestMaxTokens(params),
|
|
25132
|
+
parallel_tool_calls: params.parallel_tool_calls ?? false,
|
|
25133
|
+
has_tools: Boolean(params.tools?.length),
|
|
25134
|
+
tool_count: params.tools?.length ?? 0,
|
|
25135
|
+
message_counts: messageCounts,
|
|
25136
|
+
assistant_tool_call_messages: toolCallMessages,
|
|
25137
|
+
multimodal_messages: imageMessages,
|
|
25138
|
+
reasoning: params.reasoning?.effort ?? runtimeConfig.reasoningEffort,
|
|
25139
|
+
message_preview: messagePreview
|
|
25140
|
+
};
|
|
25141
|
+
}
|
|
25142
|
+
function logLLMRetry(stage, params, fallbackIndex, totalFallbacks, error, stream) {
|
|
25143
|
+
llmLog.warn("LLM request fallback failed", {
|
|
25144
|
+
stage,
|
|
25145
|
+
fallback: `${fallbackIndex + 1}/${totalFallbacks}`,
|
|
25146
|
+
stream,
|
|
25147
|
+
request: summarizeRequestForLog(params),
|
|
25148
|
+
error: summarizeError(error)
|
|
25149
|
+
});
|
|
25150
|
+
}
|
|
25151
|
+
function getRequestFallbacks() {
|
|
25152
|
+
return [
|
|
25153
|
+
{},
|
|
25154
|
+
{ disableReasoning: true },
|
|
25155
|
+
{ disableReasoning: true, disableParallelToolCalls: true },
|
|
25156
|
+
{ disableReasoning: true, disableParallelToolCalls: true, disableTools: true },
|
|
25157
|
+
{
|
|
25158
|
+
disableReasoning: true,
|
|
25159
|
+
disableParallelToolCalls: true,
|
|
25160
|
+
disableTools: true,
|
|
25161
|
+
stripToolTurns: true,
|
|
25162
|
+
stripImages: true
|
|
25163
|
+
}
|
|
25164
|
+
];
|
|
25165
|
+
}
|
|
25166
|
+
function sanitizeTextOnlyMessageContent(content) {
|
|
25167
|
+
if (typeof content === "string") {
|
|
25168
|
+
const trimmed = content.trim();
|
|
25169
|
+
return trimmed || void 0;
|
|
25170
|
+
}
|
|
25171
|
+
if (!Array.isArray(content)) {
|
|
25172
|
+
return void 0;
|
|
25173
|
+
}
|
|
25174
|
+
const parts = [];
|
|
25175
|
+
for (const part of content) {
|
|
25176
|
+
if (!part || typeof part !== "object") continue;
|
|
25177
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
25178
|
+
const text = part.text.trim();
|
|
25179
|
+
if (text) parts.push(text);
|
|
25180
|
+
}
|
|
25181
|
+
}
|
|
25182
|
+
const joined = parts.join("\n").trim();
|
|
25183
|
+
return joined || void 0;
|
|
25184
|
+
}
|
|
25185
|
+
function sanitizeMessagesForRequest(messages, options) {
|
|
25186
|
+
if (!options?.stripToolTurns && !options?.stripImages) {
|
|
25187
|
+
return messages;
|
|
25188
|
+
}
|
|
25189
|
+
const out = [];
|
|
25190
|
+
for (const msg of messages) {
|
|
25191
|
+
if (options.stripToolTurns && (msg.role === "tool" || msg.role === "function")) {
|
|
25192
|
+
continue;
|
|
25193
|
+
}
|
|
25194
|
+
if (options.stripToolTurns && msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
|
|
25195
|
+
const textOnly = sanitizeTextOnlyMessageContent(msg.content);
|
|
25196
|
+
if (!textOnly) {
|
|
25197
|
+
continue;
|
|
25198
|
+
}
|
|
25199
|
+
out.push({
|
|
25200
|
+
role: "assistant",
|
|
25201
|
+
content: textOnly
|
|
25202
|
+
});
|
|
25203
|
+
continue;
|
|
25204
|
+
}
|
|
25205
|
+
if (options.stripImages && msg.role === "user" && Array.isArray(msg.content)) {
|
|
25206
|
+
const textOnly = sanitizeTextOnlyMessageContent(msg.content);
|
|
25207
|
+
out.push({
|
|
25208
|
+
...msg,
|
|
25209
|
+
content: textOnly || "(image omitted)"
|
|
25210
|
+
});
|
|
25211
|
+
continue;
|
|
25212
|
+
}
|
|
25213
|
+
if (options.stripImages && msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
25214
|
+
const textOnly = sanitizeTextOnlyMessageContent(msg.content);
|
|
25215
|
+
if (!textOnly) continue;
|
|
25216
|
+
out.push({
|
|
25217
|
+
...msg,
|
|
25218
|
+
content: textOnly
|
|
25219
|
+
});
|
|
25220
|
+
continue;
|
|
25221
|
+
}
|
|
25222
|
+
out.push(msg);
|
|
25223
|
+
}
|
|
25224
|
+
return out;
|
|
25225
|
+
}
|
|
25050
25226
|
function applyDeltaToolCallsToAccumulator(toolCallsAccumulator, deltaToolCalls) {
|
|
25051
25227
|
if (!deltaToolCalls?.length) {
|
|
25052
25228
|
return false;
|
|
@@ -25128,11 +25304,43 @@ var LLMService = class {
|
|
|
25128
25304
|
throw new Error("LLMService.chatCompletion: userContext \xE9 obrigat\xF3rio");
|
|
25129
25305
|
}
|
|
25130
25306
|
const runtimeConfig = getRuntimeConfig();
|
|
25131
|
-
const
|
|
25132
|
-
|
|
25133
|
-
|
|
25307
|
+
const requestHeaders = this.requestHeaders(params.userContext);
|
|
25308
|
+
let lastError = null;
|
|
25309
|
+
const fallbacks = getRequestFallbacks();
|
|
25310
|
+
for (let i = 0; i < fallbacks.length; i += 1) {
|
|
25311
|
+
const fallback = fallbacks[i];
|
|
25312
|
+
const stage = describeFallback(fallback);
|
|
25313
|
+
try {
|
|
25314
|
+
llmLog.debug("LLM request attempt", {
|
|
25315
|
+
stream: false,
|
|
25316
|
+
stage,
|
|
25317
|
+
fallback: `${i + 1}/${fallbacks.length}`,
|
|
25318
|
+
request: summarizeRequestForLog(params, runtimeConfig)
|
|
25319
|
+
});
|
|
25320
|
+
const resp = await this.client.chat.completions.create(
|
|
25321
|
+
buildChatCompletionRequestBody(params, runtimeConfig, false, fallback),
|
|
25322
|
+
{ headers: requestHeaders }
|
|
25323
|
+
);
|
|
25324
|
+
return resp;
|
|
25325
|
+
} catch (error) {
|
|
25326
|
+
lastError = error;
|
|
25327
|
+
logLLMRetry(stage, params, i, fallbacks.length, error, false);
|
|
25328
|
+
if (!isBadRequestLike(error)) {
|
|
25329
|
+
throw error;
|
|
25330
|
+
}
|
|
25331
|
+
}
|
|
25332
|
+
}
|
|
25333
|
+
const lastStage = describeFallback(fallbacks[fallbacks.length - 1] ?? {});
|
|
25334
|
+
llmLog.error("LLM request failed after retries", {
|
|
25335
|
+
stream: false,
|
|
25336
|
+
last_stage: lastStage,
|
|
25337
|
+
attempts: fallbacks.length,
|
|
25338
|
+
request: summarizeRequestForLog(params, runtimeConfig),
|
|
25339
|
+
error: summarizeError(lastError)
|
|
25340
|
+
});
|
|
25341
|
+
throw new Error(
|
|
25342
|
+
`LLM request failed after ${fallbacks.length} attempts [last_stage=${lastStage}]: ${lastError instanceof Error ? lastError.message : String(lastError ?? "unknown error")}`
|
|
25134
25343
|
);
|
|
25135
|
-
return resp;
|
|
25136
25344
|
}
|
|
25137
25345
|
/**
|
|
25138
25346
|
* Streaming — mesmo turnId em todas as chamadas do loop de tools (via params.userContext).
|
|
@@ -25142,10 +25350,46 @@ var LLMService = class {
|
|
|
25142
25350
|
throw new Error("LLMService.chatCompletionStream: userContext \xE9 obrigat\xF3rio");
|
|
25143
25351
|
}
|
|
25144
25352
|
const runtimeConfig = getRuntimeConfig();
|
|
25145
|
-
const
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
);
|
|
25353
|
+
const requestHeaders = this.requestHeaders(params.userContext);
|
|
25354
|
+
let stream = null;
|
|
25355
|
+
let lastError = null;
|
|
25356
|
+
const fallbacks = getRequestFallbacks();
|
|
25357
|
+
for (let i = 0; i < fallbacks.length; i += 1) {
|
|
25358
|
+
const fallback = fallbacks[i];
|
|
25359
|
+
const stage = describeFallback(fallback);
|
|
25360
|
+
try {
|
|
25361
|
+
llmLog.debug("LLM stream request attempt", {
|
|
25362
|
+
stream: true,
|
|
25363
|
+
stage,
|
|
25364
|
+
fallback: `${i + 1}/${fallbacks.length}`,
|
|
25365
|
+
request: summarizeRequestForLog(params, runtimeConfig)
|
|
25366
|
+
});
|
|
25367
|
+
stream = await this.client.chat.completions.create(
|
|
25368
|
+
buildChatCompletionRequestBody(params, runtimeConfig, true, fallback),
|
|
25369
|
+
{ headers: requestHeaders }
|
|
25370
|
+
);
|
|
25371
|
+
break;
|
|
25372
|
+
} catch (error) {
|
|
25373
|
+
lastError = error;
|
|
25374
|
+
logLLMRetry(stage, params, i, fallbacks.length, error, true);
|
|
25375
|
+
if (!isBadRequestLike(error)) {
|
|
25376
|
+
throw error;
|
|
25377
|
+
}
|
|
25378
|
+
}
|
|
25379
|
+
}
|
|
25380
|
+
if (!stream) {
|
|
25381
|
+
const lastStage = describeFallback(fallbacks[fallbacks.length - 1] ?? {});
|
|
25382
|
+
llmLog.error("LLM stream request failed after retries", {
|
|
25383
|
+
stream: true,
|
|
25384
|
+
last_stage: lastStage,
|
|
25385
|
+
attempts: fallbacks.length,
|
|
25386
|
+
request: summarizeRequestForLog(params, runtimeConfig),
|
|
25387
|
+
error: summarizeError(lastError)
|
|
25388
|
+
});
|
|
25389
|
+
throw new Error(
|
|
25390
|
+
`LLM request failed after ${fallbacks.length} attempts [last_stage=${lastStage}]: ${lastError instanceof Error ? lastError.message : String(lastError ?? "unknown error")}`
|
|
25391
|
+
);
|
|
25392
|
+
}
|
|
25149
25393
|
const toolCallsAccumulator = /* @__PURE__ */ new Map();
|
|
25150
25394
|
let reasoningSnapshot = "";
|
|
25151
25395
|
for await (const chunk of stream) {
|
|
@@ -25184,231 +25428,37 @@ var LLMService = class {
|
|
|
25184
25428
|
}
|
|
25185
25429
|
};
|
|
25186
25430
|
|
|
25187
|
-
// src/app/agent/
|
|
25188
|
-
|
|
25189
|
-
|
|
25190
|
-
|
|
25191
|
-
|
|
25192
|
-
|
|
25193
|
-
|
|
25194
|
-
|
|
25195
|
-
|
|
25196
|
-
|
|
25197
|
-
|
|
25198
|
-
} else if (lower.includes("api")) {
|
|
25199
|
-
message2 = "Erro de comunica\xE7\xE3o com a API do modelo.";
|
|
25200
|
-
}
|
|
25201
|
-
return {
|
|
25202
|
-
message: message2,
|
|
25203
|
-
rawMessage
|
|
25204
|
-
};
|
|
25431
|
+
// src/app/agent/runtime/tool_execution_policy.ts
|
|
25432
|
+
init_sandbox_policy();
|
|
25433
|
+
init_runtime_config();
|
|
25434
|
+
init_permission_rules();
|
|
25435
|
+
import path38 from "path";
|
|
25436
|
+
var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
|
|
25437
|
+
function getToolPermissionLayer(metadata) {
|
|
25438
|
+
if (metadata.riskLevel === "safe") return "read";
|
|
25439
|
+
if (metadata.riskLevel === "write") return "write";
|
|
25440
|
+
if (metadata.riskLevel === "network") return "network";
|
|
25441
|
+
return "execute";
|
|
25205
25442
|
}
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
|
|
25212
|
-
*/
|
|
25213
|
-
static assistantContentWithToolCalls(content) {
|
|
25214
|
-
if (content === void 0 || content === null) return null;
|
|
25215
|
-
if (typeof content === "string") return content.trim() === "" ? null : content;
|
|
25216
|
-
return null;
|
|
25443
|
+
function isLocalEditTool(toolName) {
|
|
25444
|
+
return LOCAL_EDIT_TOOL_NAMES.has(toolName);
|
|
25445
|
+
}
|
|
25446
|
+
function checkFilePermissionRules(toolName, filePath, policy) {
|
|
25447
|
+
if (!filePath) {
|
|
25448
|
+
return { allowed: false, reason: "No file path provided for permission check." };
|
|
25217
25449
|
}
|
|
25218
|
-
|
|
25219
|
-
|
|
25220
|
-
|
|
25221
|
-
static normalizeAssistantMessage(message2) {
|
|
25222
|
-
if (message2.tool_calls && this.isOpenAIFormat(message2.tool_calls)) {
|
|
25223
|
-
return {
|
|
25224
|
-
...message2,
|
|
25225
|
-
content: this.assistantContentWithToolCalls(message2.content)
|
|
25226
|
-
};
|
|
25227
|
-
}
|
|
25228
|
-
const toolCalls = this.extractToolCalls(message2);
|
|
25229
|
-
if (toolCalls.length > 0) {
|
|
25230
|
-
return {
|
|
25231
|
-
role: message2.role || "assistant",
|
|
25232
|
-
content: this.assistantContentWithToolCalls(message2.content),
|
|
25233
|
-
tool_calls: toolCalls
|
|
25234
|
-
};
|
|
25235
|
-
}
|
|
25236
|
-
return message2;
|
|
25450
|
+
const resolvedPath = path38.resolve(filePath);
|
|
25451
|
+
if (!isPathInsideWorkspace(resolvedPath, policy)) {
|
|
25452
|
+
return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
|
|
25237
25453
|
}
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
const firstCall = toolCalls[0];
|
|
25244
|
-
return typeof firstCall.id === "string" && firstCall.type === "function" && typeof firstCall.function?.name === "string" && typeof firstCall.function?.arguments === "string";
|
|
25454
|
+
const relativePath = path38.relative(policy.workspaceRoot, resolvedPath);
|
|
25455
|
+
const toolPattern = `${toolName}(${relativePath})`;
|
|
25456
|
+
const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
|
|
25457
|
+
if (ruleDecision === "deny") {
|
|
25458
|
+
return { allowed: false, reason: `File "${filePath}" denied by permission rules.` };
|
|
25245
25459
|
}
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
*/
|
|
25249
|
-
static extractToolCalls(message2) {
|
|
25250
|
-
const results = [];
|
|
25251
|
-
if (message2.tool_calls && Array.isArray(message2.tool_calls)) {
|
|
25252
|
-
for (const call of message2.tool_calls) {
|
|
25253
|
-
const normalized = this.normalizeToolCall(call);
|
|
25254
|
-
if (normalized) results.push(normalized);
|
|
25255
|
-
}
|
|
25256
|
-
}
|
|
25257
|
-
if (typeof message2.content === "string" && message2.content.trim()) {
|
|
25258
|
-
const extracted = this.extractFromContent(message2.content);
|
|
25259
|
-
results.push(...extracted);
|
|
25260
|
-
}
|
|
25261
|
-
if (message2.function_call) {
|
|
25262
|
-
const normalized = this.normalizeToolCall(message2.function_call);
|
|
25263
|
-
if (normalized) results.push(normalized);
|
|
25264
|
-
}
|
|
25265
|
-
return results;
|
|
25266
|
-
}
|
|
25267
|
-
/**
|
|
25268
|
-
* Normaliza um único tool call para o formato OpenAI
|
|
25269
|
-
*/
|
|
25270
|
-
static normalizeToolCall(call) {
|
|
25271
|
-
try {
|
|
25272
|
-
if (call.id && call.function?.name) {
|
|
25273
|
-
return {
|
|
25274
|
-
id: call.id,
|
|
25275
|
-
type: "function",
|
|
25276
|
-
function: {
|
|
25277
|
-
name: call.function.name,
|
|
25278
|
-
arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments)
|
|
25279
|
-
}
|
|
25280
|
-
};
|
|
25281
|
-
}
|
|
25282
|
-
if (call.name) {
|
|
25283
|
-
return {
|
|
25284
|
-
id: call.id || randomUUID(),
|
|
25285
|
-
type: "function",
|
|
25286
|
-
function: {
|
|
25287
|
-
name: call.name,
|
|
25288
|
-
arguments: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments || {})
|
|
25289
|
-
}
|
|
25290
|
-
};
|
|
25291
|
-
}
|
|
25292
|
-
if (call.function && typeof call.function === "object") {
|
|
25293
|
-
return {
|
|
25294
|
-
id: call.id || randomUUID(),
|
|
25295
|
-
type: "function",
|
|
25296
|
-
function: {
|
|
25297
|
-
name: call.function.name,
|
|
25298
|
-
arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments || {})
|
|
25299
|
-
}
|
|
25300
|
-
};
|
|
25301
|
-
}
|
|
25302
|
-
return null;
|
|
25303
|
-
} catch (error) {
|
|
25304
|
-
console.error("Error normalizing tool call:", error, call);
|
|
25305
|
-
return null;
|
|
25306
|
-
}
|
|
25307
|
-
}
|
|
25308
|
-
/**
|
|
25309
|
-
* Extrai tool calls do content (pode estar em markdown, JSON, etc)
|
|
25310
|
-
*/
|
|
25311
|
-
static extractFromContent(content) {
|
|
25312
|
-
const results = [];
|
|
25313
|
-
const cleanContent2 = content.replace(/```(?:json)?\s*([\s\S]*?)```/g, "$1");
|
|
25314
|
-
const jsonMatches = this.extractJsonObjects(cleanContent2);
|
|
25315
|
-
for (const jsonStr of jsonMatches) {
|
|
25316
|
-
try {
|
|
25317
|
-
const parsed = JSON.parse(jsonStr);
|
|
25318
|
-
if (Array.isArray(parsed)) {
|
|
25319
|
-
for (const call of parsed) {
|
|
25320
|
-
const normalized = this.normalizeToolCall(call);
|
|
25321
|
-
if (normalized) results.push(normalized);
|
|
25322
|
-
}
|
|
25323
|
-
} else if (parsed.name || parsed.function) {
|
|
25324
|
-
const normalized = this.normalizeToolCall(parsed);
|
|
25325
|
-
if (normalized) results.push(normalized);
|
|
25326
|
-
} else if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) {
|
|
25327
|
-
for (const call of parsed.tool_calls) {
|
|
25328
|
-
const normalized = this.normalizeToolCall(call);
|
|
25329
|
-
if (normalized) results.push(normalized);
|
|
25330
|
-
}
|
|
25331
|
-
}
|
|
25332
|
-
} catch (e) {
|
|
25333
|
-
}
|
|
25334
|
-
}
|
|
25335
|
-
return results;
|
|
25336
|
-
}
|
|
25337
|
-
/**
|
|
25338
|
-
* Extrai objetos JSON de uma string (suporta múltiplos objetos)
|
|
25339
|
-
*/
|
|
25340
|
-
static extractJsonObjects(text) {
|
|
25341
|
-
const results = [];
|
|
25342
|
-
let depth = 0;
|
|
25343
|
-
let start = -1;
|
|
25344
|
-
for (let i = 0; i < text.length; i++) {
|
|
25345
|
-
if (text[i] === "{") {
|
|
25346
|
-
if (depth === 0) start = i;
|
|
25347
|
-
depth++;
|
|
25348
|
-
} else if (text[i] === "}") {
|
|
25349
|
-
depth--;
|
|
25350
|
-
if (depth === 0 && start !== -1) {
|
|
25351
|
-
results.push(text.substring(start, i + 1));
|
|
25352
|
-
start = -1;
|
|
25353
|
-
}
|
|
25354
|
-
}
|
|
25355
|
-
}
|
|
25356
|
-
return results;
|
|
25357
|
-
}
|
|
25358
|
-
/**
|
|
25359
|
-
* Valida se um tool call normalizado é válido
|
|
25360
|
-
* Validação dupla:
|
|
25361
|
-
* 1. JSON dos argumentos é válido
|
|
25362
|
-
* 2. A ferramenta existe no catálogo de ferramentas nativas
|
|
25363
|
-
*/
|
|
25364
|
-
static isValidToolCall(call) {
|
|
25365
|
-
if (!(call.id && call.type === "function" && call.function?.name && typeof call.function.arguments === "string")) {
|
|
25366
|
-
return false;
|
|
25367
|
-
}
|
|
25368
|
-
try {
|
|
25369
|
-
JSON.parse(call.function.arguments);
|
|
25370
|
-
} catch {
|
|
25371
|
-
return false;
|
|
25372
|
-
}
|
|
25373
|
-
const toolMetadata = getNativeToolMetadata(call.function.name);
|
|
25374
|
-
if (!toolMetadata) {
|
|
25375
|
-
return false;
|
|
25376
|
-
}
|
|
25377
|
-
return true;
|
|
25378
|
-
}
|
|
25379
|
-
};
|
|
25380
|
-
|
|
25381
|
-
// src/app/agent/runtime/tool_execution_policy.ts
|
|
25382
|
-
init_sandbox_policy();
|
|
25383
|
-
init_runtime_config();
|
|
25384
|
-
init_permission_rules();
|
|
25385
|
-
import path38 from "path";
|
|
25386
|
-
var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
|
|
25387
|
-
function getToolPermissionLayer(metadata) {
|
|
25388
|
-
if (metadata.riskLevel === "safe") return "read";
|
|
25389
|
-
if (metadata.riskLevel === "write") return "write";
|
|
25390
|
-
if (metadata.riskLevel === "network") return "network";
|
|
25391
|
-
return "execute";
|
|
25392
|
-
}
|
|
25393
|
-
function isLocalEditTool(toolName) {
|
|
25394
|
-
return LOCAL_EDIT_TOOL_NAMES.has(toolName);
|
|
25395
|
-
}
|
|
25396
|
-
function checkFilePermissionRules(toolName, filePath, policy) {
|
|
25397
|
-
if (!filePath) {
|
|
25398
|
-
return { allowed: false, reason: "No file path provided for permission check." };
|
|
25399
|
-
}
|
|
25400
|
-
const resolvedPath = path38.resolve(filePath);
|
|
25401
|
-
if (!isPathInsideWorkspace(resolvedPath, policy)) {
|
|
25402
|
-
return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
|
|
25403
|
-
}
|
|
25404
|
-
const relativePath = path38.relative(policy.workspaceRoot, resolvedPath);
|
|
25405
|
-
const toolPattern = `${toolName}(${relativePath})`;
|
|
25406
|
-
const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
|
|
25407
|
-
if (ruleDecision === "deny") {
|
|
25408
|
-
return { allowed: false, reason: `File "${filePath}" denied by permission rules.` };
|
|
25409
|
-
}
|
|
25410
|
-
if (ruleDecision === "allow") {
|
|
25411
|
-
return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
|
|
25460
|
+
if (ruleDecision === "allow") {
|
|
25461
|
+
return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
|
|
25412
25462
|
}
|
|
25413
25463
|
const dirPath = path38.dirname(relativePath);
|
|
25414
25464
|
const dirPattern = `${toolName}(${dirPath}/**)`;
|
|
@@ -25699,28 +25749,6 @@ function consolidateCodingMemoryFile() {
|
|
|
25699
25749
|
};
|
|
25700
25750
|
}
|
|
25701
25751
|
|
|
25702
|
-
// src/app/agent/runtime/tool_orchestration.ts
|
|
25703
|
-
var PARALLEL_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
25704
|
-
"ls_tool",
|
|
25705
|
-
"read_file_lines",
|
|
25706
|
-
"count_file_lines",
|
|
25707
|
-
"find_by_name",
|
|
25708
|
-
"grep_search",
|
|
25709
|
-
"view_file_outline",
|
|
25710
|
-
"read_artifact",
|
|
25711
|
-
"list_agents",
|
|
25712
|
-
"list_mcp_resources",
|
|
25713
|
-
"read_mcp_resource",
|
|
25714
|
-
"todo"
|
|
25715
|
-
]);
|
|
25716
|
-
function toolEligibleForParallelRead(toolName, sessionId) {
|
|
25717
|
-
const name = String(toolName || "").trim();
|
|
25718
|
-
if (!name || !PARALLEL_SAFE_TOOL_NAMES.has(name)) return false;
|
|
25719
|
-
if (!decideToolExecution(name, void 0, sessionId).autoApprove) return false;
|
|
25720
|
-
const meta = getNativeToolMetadata(name);
|
|
25721
|
-
return meta?.riskLevel === "safe";
|
|
25722
|
-
}
|
|
25723
|
-
|
|
25724
25752
|
// src/app/agent/bluma/turn_start_payload.ts
|
|
25725
25753
|
function buildUserPromptPreview(inputText) {
|
|
25726
25754
|
const t = String(inputText ?? "").trim();
|
|
@@ -25785,276 +25813,153 @@ function subscribeToolResultStatuses(listener) {
|
|
|
25785
25813
|
return changed.subscribe(listener);
|
|
25786
25814
|
}
|
|
25787
25815
|
|
|
25788
|
-
// src/
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25792
|
-
sessionId;
|
|
25793
|
-
sessionFile = "";
|
|
25794
|
-
history = [];
|
|
25795
|
-
eventBus;
|
|
25796
|
-
mcpClient;
|
|
25797
|
-
feedbackSystem;
|
|
25798
|
-
skillLoader;
|
|
25799
|
-
compressor;
|
|
25800
|
-
isInterrupted = false;
|
|
25801
|
-
/** Mesmo turnId durante processTurn + todo o loop de tool_calls (FactorRouter). */
|
|
25802
|
-
activeTurnContext = null;
|
|
25803
|
-
/** Evita POST /turns/.../end duplicado no mesmo turno (ex.: Esc após message_result). */
|
|
25804
|
-
factorRouterTurnClosed = false;
|
|
25805
|
-
/** Passos seguidos sem tool_calls nem texto visível (só raciocínio) — evita loop lento no mesmo turno. */
|
|
25806
|
-
emptyAssistantReplySteps = 0;
|
|
25807
|
-
/** Reintentos consecutivos por tool call inválido. */
|
|
25808
|
-
invalidToolCallRetrySteps = 0;
|
|
25809
|
-
constructor(sessionId, eventBus, llm, mcpClient, feedbackSystem) {
|
|
25810
|
-
this.sessionId = sessionId;
|
|
25811
|
-
this.eventBus = eventBus;
|
|
25812
|
-
this.llm = llm;
|
|
25813
|
-
this.mcpClient = mcpClient;
|
|
25814
|
-
this.feedbackSystem = feedbackSystem;
|
|
25815
|
-
this.skillLoader = new SkillLoader(process.cwd());
|
|
25816
|
-
this.compressor = new HistoryCompressor();
|
|
25817
|
-
this.eventBus.on("user_interrupt", async () => {
|
|
25818
|
-
this.isInterrupted = true;
|
|
25819
|
-
await this.notifyFactorTurnEndIfNeeded("user_interrupt");
|
|
25820
|
-
this.eventBus.emit("backend_message", { type: "done", status: "interrupted" });
|
|
25821
|
-
});
|
|
25822
|
-
this.eventBus.on("user_overlay", async (data) => {
|
|
25823
|
-
const clean = String(data.payload ?? "").trim();
|
|
25824
|
-
this.history.push({ role: "user", content: clean });
|
|
25825
|
-
this.eventBus.emit("backend_message", { type: "user_overlay", payload: clean, ts: data.ts || Date.now() });
|
|
25826
|
-
try {
|
|
25827
|
-
if (this.sessionFile) {
|
|
25828
|
-
this.persistSession();
|
|
25829
|
-
} else {
|
|
25830
|
-
const [sessionFile, , mem] = await loadOrcreateSession(this.sessionId);
|
|
25831
|
-
this.sessionFile = sessionFile;
|
|
25832
|
-
this.compressor.restoreSnapshot(mem);
|
|
25833
|
-
this.persistSession();
|
|
25834
|
-
}
|
|
25835
|
-
} catch (e) {
|
|
25836
|
-
this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s user_overlay: ${e.message}` });
|
|
25837
|
-
}
|
|
25838
|
-
});
|
|
25839
|
-
}
|
|
25840
|
-
getMemorySnapshot() {
|
|
25841
|
-
return this.compressor.getSnapshot();
|
|
25842
|
-
}
|
|
25843
|
-
/**
|
|
25844
|
-
* Salvar histórico de forma SÍNCRONA - usado em situações críticas (erros, shutdown)
|
|
25845
|
-
* para garantir que nada se perde. NÃO usa debounce.
|
|
25846
|
-
*/
|
|
25847
|
-
persistSessionSync() {
|
|
25848
|
-
if (!this.sessionFile) return;
|
|
25816
|
+
// src/utils/toolActionStatus.ts
|
|
25817
|
+
function parseArgsRecord(args) {
|
|
25818
|
+
if (args == null) return {};
|
|
25819
|
+
if (typeof args === "string") {
|
|
25849
25820
|
try {
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
conversation_history: this.history,
|
|
25854
|
-
last_updated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25855
|
-
...this.compressor.getSnapshot()
|
|
25856
|
-
};
|
|
25857
|
-
fs39.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
25858
|
-
} catch (error) {
|
|
25859
|
-
console.error("[Bluma] Failed to persist session synchronously:", error);
|
|
25821
|
+
return JSON.parse(args);
|
|
25822
|
+
} catch {
|
|
25823
|
+
return {};
|
|
25860
25824
|
}
|
|
25861
25825
|
}
|
|
25862
|
-
|
|
25863
|
-
|
|
25864
|
-
if (!this.sessionFile) return;
|
|
25865
|
-
void saveSessionHistory(this.sessionFile, this.history, this.getMemorySnapshot());
|
|
25866
|
-
}
|
|
25867
|
-
recordUiSlashCommand(command, mode = "visible_only") {
|
|
25868
|
-
const text = String(command ?? "").trim();
|
|
25869
|
-
if (!text) return;
|
|
25870
|
-
this.history.push({
|
|
25871
|
-
role: "user",
|
|
25872
|
-
name: mode === "with_internal_prompt" ? "ui_slash_command" : "ui_slash_command_local",
|
|
25873
|
-
content: text
|
|
25874
|
-
});
|
|
25875
|
-
this.persistSession();
|
|
25826
|
+
if (typeof args === "object") {
|
|
25827
|
+
return args;
|
|
25876
25828
|
}
|
|
25877
|
-
|
|
25878
|
-
|
|
25879
|
-
|
|
25880
|
-
|
|
25881
|
-
|
|
25882
|
-
|
|
25883
|
-
|
|
25884
|
-
|
|
25885
|
-
|
|
25886
|
-
|
|
25887
|
-
|
|
25888
|
-
|
|
25889
|
-
|
|
25890
|
-
|
|
25891
|
-
|
|
25892
|
-
|
|
25893
|
-
|
|
25894
|
-
|
|
25895
|
-
|
|
25829
|
+
return {};
|
|
25830
|
+
}
|
|
25831
|
+
function truncate2(value, max = 44) {
|
|
25832
|
+
const text = value.trim().replace(/\s+/g, " ");
|
|
25833
|
+
if (!text) return "";
|
|
25834
|
+
return text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`;
|
|
25835
|
+
}
|
|
25836
|
+
function shortFileName(pathLike) {
|
|
25837
|
+
const trimmed = pathLike.trim();
|
|
25838
|
+
if (!trimmed) return "";
|
|
25839
|
+
return trimmed.split("/").pop() || trimmed;
|
|
25840
|
+
}
|
|
25841
|
+
function formatCommand(command) {
|
|
25842
|
+
const compact = truncate2(command, 42);
|
|
25843
|
+
return compact ? `shell command: ${compact}` : "shell command";
|
|
25844
|
+
}
|
|
25845
|
+
function formatSearchQuery(prefix, query) {
|
|
25846
|
+
const compact = truncate2(query, 36);
|
|
25847
|
+
return compact ? `${prefix}: ${compact}` : prefix;
|
|
25848
|
+
}
|
|
25849
|
+
function formatResponseLabel(toolName, args) {
|
|
25850
|
+
switch (toolName) {
|
|
25851
|
+
case "shell_command":
|
|
25852
|
+
case "run_command": {
|
|
25853
|
+
const cmd = typeof args.command === "string" ? args.command : "";
|
|
25854
|
+
return cmd ? `Running ${formatCommand(cmd)}` : "Running shell command";
|
|
25896
25855
|
}
|
|
25897
|
-
|
|
25898
|
-
|
|
25899
|
-
|
|
25900
|
-
|
|
25901
|
-
|
|
25902
|
-
|
|
25903
|
-
|
|
25904
|
-
|
|
25905
|
-
|
|
25906
|
-
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
|
|
25917
|
-
|
|
25918
|
-
|
|
25919
|
-
|
|
25920
|
-
|
|
25921
|
-
|
|
25922
|
-
|
|
25923
|
-
|
|
25924
|
-
|
|
25925
|
-
|
|
25926
|
-
|
|
25927
|
-
|
|
25928
|
-
|
|
25929
|
-
|
|
25930
|
-
|
|
25931
|
-
|
|
25932
|
-
|
|
25933
|
-
|
|
25934
|
-
|
|
25935
|
-
|
|
25936
|
-
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
25940
|
-
|
|
25941
|
-
|
|
25942
|
-
|
|
25943
|
-
|
|
25944
|
-
|
|
25945
|
-
|
|
25946
|
-
|
|
25947
|
-
|
|
25948
|
-
|
|
25949
|
-
|
|
25950
|
-
|
|
25951
|
-
|
|
25952
|
-
|
|
25953
|
-
|
|
25954
|
-
|
|
25955
|
-
|
|
25956
|
-
|
|
25957
|
-
|
|
25958
|
-
|
|
25959
|
-
|
|
25960
|
-
|
|
25961
|
-
|
|
25962
|
-
|
|
25963
|
-
|
|
25964
|
-
|
|
25965
|
-
|
|
25966
|
-
|
|
25967
|
-
|
|
25968
|
-
|
|
25969
|
-
|
|
25970
|
-
|
|
25971
|
-
|
|
25972
|
-
|
|
25973
|
-
|
|
25974
|
-
listAvailableSkills() {
|
|
25975
|
-
return this.skillLoader.listAvailable();
|
|
25976
|
-
}
|
|
25977
|
-
getSkillsDirs() {
|
|
25978
|
-
return this.skillLoader.getSkillsDirs();
|
|
25979
|
-
}
|
|
25980
|
-
getSkillConflictWarnings() {
|
|
25981
|
-
return this.skillLoader.formatConflictWarnings();
|
|
25982
|
-
}
|
|
25983
|
-
getFeedbackScore() {
|
|
25984
|
-
return this.feedbackSystem.getCumulativeScore();
|
|
25985
|
-
}
|
|
25986
|
-
/**
|
|
25987
|
-
* Contrato de ciclo de turno (UI, hooks, Factor Router):
|
|
25988
|
-
*
|
|
25989
|
-
* - `backend_message` `{ type: 'turn_start', turnId, sessionId, userPromptPreview }` — início;
|
|
25990
|
-
* payload estável em {@link buildTurnStartBackendMessage}.
|
|
25991
|
-
* - Stream: `stream_start` / `stream_reasoning_chunk` / `stream_chunk` / `stream_end`.
|
|
25992
|
-
* Se a resposta incluir tool `message`, `stream_end` leva `{ omitAssistantFlush: true }` para não
|
|
25993
|
-
* duplicar texto no histórico (o utilizador vê o `tool_result` com gutter). Ver `applyStreamEndFlush`.
|
|
25994
|
-
* - Fim: `backend_message` `{ type: 'done', status }` (`completed` | `failed` | `interrupted` | …).
|
|
25995
|
-
* - Factor Router: um POST `/turns/{turnId}/end` por turno via `notifyFactorTurnEndIfNeeded` (razões abaixo).
|
|
25996
|
-
*
|
|
25997
|
-
* Smoke manual sugerido: mensagem normal com `message`+`result`; Ctrl+C; erro LLM; loop sem resposta útil.
|
|
25998
|
-
*/
|
|
25999
|
-
async processTurn(userInput, userContextInput, options) {
|
|
26000
|
-
this.isInterrupted = false;
|
|
26001
|
-
this.factorRouterTurnClosed = false;
|
|
26002
|
-
const inputText = String(userInput.content || "").trim();
|
|
26003
|
-
const turnId = uuidv48();
|
|
26004
|
-
this.activeTurnContext = {
|
|
26005
|
-
...userContextInput,
|
|
26006
|
-
turnId,
|
|
26007
|
-
sessionId: userContextInput.sessionId || this.sessionId
|
|
26008
|
-
};
|
|
26009
|
-
process.env.BLUMA_USER_CONTEXT_JSON = JSON.stringify({
|
|
26010
|
-
conversationId: this.activeTurnContext.conversationId ?? null,
|
|
26011
|
-
userId: this.activeTurnContext.userId ?? null,
|
|
26012
|
-
userName: this.activeTurnContext.userName ?? null,
|
|
26013
|
-
userEmail: this.activeTurnContext.userEmail ?? null,
|
|
26014
|
-
companyId: this.activeTurnContext.companyId ?? null,
|
|
26015
|
-
companyName: this.activeTurnContext.companyName ?? null
|
|
26016
|
-
});
|
|
26017
|
-
const userContent = buildUserMessageContent(inputText, process.cwd());
|
|
26018
|
-
this.history.push({
|
|
26019
|
-
role: "user",
|
|
26020
|
-
content: userContent,
|
|
26021
|
-
...options?.historyName ? { name: options.historyName } : {}
|
|
26022
|
-
});
|
|
26023
|
-
this.emptyAssistantReplySteps = 0;
|
|
26024
|
-
this.invalidToolCallRetrySteps = 0;
|
|
26025
|
-
this.eventBus.emit(
|
|
26026
|
-
"backend_message",
|
|
26027
|
-
buildTurnStartBackendMessage({
|
|
26028
|
-
turnId: this.activeTurnContext.turnId,
|
|
26029
|
-
sessionId: this.activeTurnContext.sessionId,
|
|
26030
|
-
inputText,
|
|
26031
|
-
appContext: this.activeTurnContext.appContext ?? null
|
|
26032
|
-
})
|
|
26033
|
-
);
|
|
26034
|
-
if (inputText === "/init") {
|
|
26035
|
-
this.eventBus.emit("dispatch", inputText);
|
|
26036
|
-
}
|
|
26037
|
-
await this._continueConversation();
|
|
25856
|
+
case "command_status":
|
|
25857
|
+
return "Checking command status";
|
|
25858
|
+
case "send_command_input":
|
|
25859
|
+
return "Sending command input";
|
|
25860
|
+
case "kill_command":
|
|
25861
|
+
return "Stopping command";
|
|
25862
|
+
case "read_file_lines":
|
|
25863
|
+
return `Reading ${shortFileName(String(args.filepath ?? args.file_path ?? "")) || "file"}`;
|
|
25864
|
+
case "count_file_lines":
|
|
25865
|
+
return `Counting lines in ${shortFileName(String(args.filepath ?? args.file_path ?? "")) || "file"}`;
|
|
25866
|
+
case "ls_tool":
|
|
25867
|
+
return "Listing files";
|
|
25868
|
+
case "edit_tool": {
|
|
25869
|
+
const edits = Array.isArray(args.edits) ? args.edits : [];
|
|
25870
|
+
const count = edits.length || 1;
|
|
25871
|
+
const filePath = typeof args.file_path === "string" ? args.file_path : typeof edits[0]?.file_path === "string" ? String(edits[0].file_path) : "";
|
|
25872
|
+
const file = shortFileName(filePath) || "file";
|
|
25873
|
+
return count === 1 ? `Editing ${file}` : `Editing ${count} files`;
|
|
25874
|
+
}
|
|
25875
|
+
case "file_write":
|
|
25876
|
+
return `Writing ${shortFileName(String(args.filepath ?? args.file_path ?? "")) || "file"}`;
|
|
25877
|
+
case "grep_search":
|
|
25878
|
+
return formatSearchQuery("Searching code", String(args.query ?? ""));
|
|
25879
|
+
case "find_by_name":
|
|
25880
|
+
return formatSearchQuery("Finding files", String(args.pattern ?? ""));
|
|
25881
|
+
case "view_file_outline":
|
|
25882
|
+
return `Inspecting ${shortFileName(String(args.file_path ?? "")) || "file outline"}`;
|
|
25883
|
+
case "web_fetch":
|
|
25884
|
+
return formatSearchQuery("Fetching URL", String(args.url ?? ""));
|
|
25885
|
+
case "search_web":
|
|
25886
|
+
return formatSearchQuery("Searching the web", String(args.query ?? ""));
|
|
25887
|
+
case "spawn_agent":
|
|
25888
|
+
return "Spawning a worker";
|
|
25889
|
+
case "wait_agent":
|
|
25890
|
+
return "Waiting for worker";
|
|
25891
|
+
case "list_agents":
|
|
25892
|
+
return "Reviewing workers";
|
|
25893
|
+
case "todo":
|
|
25894
|
+
return "Updating task list";
|
|
25895
|
+
case "task_boundary":
|
|
25896
|
+
return "Switching task boundary";
|
|
25897
|
+
case "task_create":
|
|
25898
|
+
case "task_list":
|
|
25899
|
+
case "task_get":
|
|
25900
|
+
case "task_update":
|
|
25901
|
+
case "task_stop":
|
|
25902
|
+
return "Managing task";
|
|
25903
|
+
case "load_skill":
|
|
25904
|
+
return "Loading skill context";
|
|
25905
|
+
case "coding_memory":
|
|
25906
|
+
return "Updating working memory";
|
|
25907
|
+
case "read_artifact":
|
|
25908
|
+
return `Reading ${String(args.filename ?? "artifact")}`;
|
|
25909
|
+
case "message":
|
|
25910
|
+
return "Preparing assistant reply";
|
|
25911
|
+
case "ask_user_question":
|
|
25912
|
+
return "Waiting for your answer";
|
|
25913
|
+
case "enter_plan_mode":
|
|
25914
|
+
return "Entering plan mode";
|
|
25915
|
+
case "exit_plan_mode":
|
|
25916
|
+
return "Leaving plan mode";
|
|
25917
|
+
case "list_mcp_resources":
|
|
25918
|
+
return "Browsing MCP resources";
|
|
25919
|
+
case "read_mcp_resource":
|
|
25920
|
+
return "Reading MCP resource";
|
|
25921
|
+
case "cron_create":
|
|
25922
|
+
return "Scheduling reminder";
|
|
25923
|
+
case "cron_list":
|
|
25924
|
+
return "Listing reminders";
|
|
25925
|
+
case "cron_delete":
|
|
25926
|
+
return "Cancelling reminder";
|
|
25927
|
+
case "notebook_edit":
|
|
25928
|
+
return `Editing ${shortFileName(String(args.filepath ?? "")) || "notebook"}`;
|
|
25929
|
+
case "lsp_query":
|
|
25930
|
+
return "Querying language server";
|
|
25931
|
+
default:
|
|
25932
|
+
return toolName.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()) || "Working";
|
|
26038
25933
|
}
|
|
26039
|
-
|
|
26040
|
-
|
|
26041
|
-
|
|
26042
|
-
|
|
26043
|
-
|
|
26044
|
-
|
|
26045
|
-
|
|
26046
|
-
|
|
26047
|
-
|
|
26048
|
-
|
|
26049
|
-
|
|
25934
|
+
}
|
|
25935
|
+
function getToolActionLabel(toolName, args) {
|
|
25936
|
+
return formatResponseLabel(toolName, parseArgsRecord(args));
|
|
25937
|
+
}
|
|
25938
|
+
function getToolActionStatus(toolName, args) {
|
|
25939
|
+
return {
|
|
25940
|
+
action: getToolActionLabel(toolName, args),
|
|
25941
|
+
phase: "tool"
|
|
25942
|
+
};
|
|
25943
|
+
}
|
|
25944
|
+
function getReasoningActionStatus() {
|
|
25945
|
+
return {
|
|
25946
|
+
action: "Reasoning",
|
|
25947
|
+
phase: "thinking"
|
|
25948
|
+
};
|
|
25949
|
+
}
|
|
25950
|
+
function getRespondingActionStatus() {
|
|
25951
|
+
return {
|
|
25952
|
+
action: "Writing",
|
|
25953
|
+
phase: "responding"
|
|
25954
|
+
};
|
|
25955
|
+
}
|
|
25956
|
+
|
|
25957
|
+
// src/app/agent/bluma/core/bluma_tool_runner.ts
|
|
25958
|
+
var BluMaToolRunner = class {
|
|
25959
|
+
constructor(deps) {
|
|
25960
|
+
this.deps = deps;
|
|
26050
25961
|
}
|
|
26051
|
-
|
|
26052
|
-
* Executa uma tool aprovada: emit tool_call → invoke → tool_result → histórico.
|
|
26053
|
-
* `backend_message` tool_call / tool_result incluem `tool_call_id` (id do tool call no modelo)
|
|
26054
|
-
* para a UI correlacionar a linha de invocação (•) com o resultado compacto.
|
|
26055
|
-
* @returns shouldContinue — false se `message` com message_type result terminou o turno.
|
|
26056
|
-
*/
|
|
26057
|
-
async executeApprovedToolInvocation(toolCall, decisionData) {
|
|
25962
|
+
async executeApprovedToolInvocation(toolCall, _decisionData) {
|
|
26058
25963
|
const toolName = toolCall.function.name;
|
|
26059
25964
|
let toolResultContent;
|
|
26060
25965
|
let shouldContinueConversation = true;
|
|
@@ -26068,8 +25973,8 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26068
25973
|
if (toolArgs === void 0 || toolArgs === null) {
|
|
26069
25974
|
toolArgs = {};
|
|
26070
25975
|
}
|
|
26071
|
-
} catch
|
|
26072
|
-
this.eventBus.emit("backend_message", {
|
|
25976
|
+
} catch {
|
|
25977
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26073
25978
|
type: "info",
|
|
26074
25979
|
message: "O BluMa encontrou um erro ao processar. A tentar recuperar a sess\xE3o..."
|
|
26075
25980
|
});
|
|
@@ -26078,12 +25983,12 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26078
25983
|
error: "Tool arguments could not be parsed",
|
|
26079
25984
|
recovery: "Session recovered automatically"
|
|
26080
25985
|
});
|
|
26081
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26082
|
-
this.history.push({
|
|
25986
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
25987
|
+
this.deps.history.push({
|
|
26083
25988
|
role: "system",
|
|
26084
25989
|
content: "The previous tool call had invalid JSON arguments. Please retry with properly formatted JSON arguments."
|
|
26085
25990
|
});
|
|
26086
|
-
this.persistSession();
|
|
25991
|
+
this.deps.persistSession();
|
|
26087
25992
|
return true;
|
|
26088
25993
|
}
|
|
26089
25994
|
const toolMetadata = getNativeToolMetadata(toolName);
|
|
@@ -26093,18 +25998,18 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26093
25998
|
error: `Tool "${toolName}" not found in agent catalog.`,
|
|
26094
25999
|
suggestion: "This tool does not exist. Please use a valid tool name from the available tools list."
|
|
26095
26000
|
});
|
|
26096
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26097
|
-
this.history.push({
|
|
26001
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26002
|
+
this.deps.history.push({
|
|
26098
26003
|
role: "system",
|
|
26099
26004
|
content: `[SYSTEM] Tool "${toolName}" is not found in the agent's available tools. Please use only tools that are listed in your tool catalog.`
|
|
26100
26005
|
});
|
|
26101
|
-
this.persistSession();
|
|
26006
|
+
this.deps.persistSession();
|
|
26102
26007
|
return true;
|
|
26103
26008
|
}
|
|
26104
26009
|
if (toolName === "edit_tool") {
|
|
26105
26010
|
const batch = toolArgs.edits;
|
|
26106
26011
|
if (Array.isArray(batch) && batch.length > 0) {
|
|
26107
|
-
for (let i = 0; i < batch.length; i
|
|
26012
|
+
for (let i = 0; i < batch.length; i += 1) {
|
|
26108
26013
|
const entry = batch[i];
|
|
26109
26014
|
if (!entry?.file_path || typeof entry.file_path !== "string") {
|
|
26110
26015
|
toolResultContent = JSON.stringify({
|
|
@@ -26113,8 +26018,8 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26113
26018
|
details: `edits[${i}].file_path is required`,
|
|
26114
26019
|
received: toolArgs
|
|
26115
26020
|
});
|
|
26116
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26117
|
-
this.persistSession();
|
|
26021
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26022
|
+
this.deps.persistSession();
|
|
26118
26023
|
return true;
|
|
26119
26024
|
}
|
|
26120
26025
|
if (entry.old_string === void 0 || entry.new_string === void 0) {
|
|
@@ -26124,8 +26029,8 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26124
26029
|
details: `edits[${i}]: old_string and new_string are required`,
|
|
26125
26030
|
received: toolArgs
|
|
26126
26031
|
});
|
|
26127
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26128
|
-
this.persistSession();
|
|
26032
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26033
|
+
this.deps.persistSession();
|
|
26129
26034
|
return true;
|
|
26130
26035
|
}
|
|
26131
26036
|
}
|
|
@@ -26137,8 +26042,8 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26137
26042
|
details: "file_path is required and must be a string (or provide non-empty edits[])",
|
|
26138
26043
|
received: toolArgs
|
|
26139
26044
|
});
|
|
26140
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26141
|
-
this.persistSession();
|
|
26045
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26046
|
+
this.deps.persistSession();
|
|
26142
26047
|
return true;
|
|
26143
26048
|
}
|
|
26144
26049
|
if (toolArgs.old_string === void 0 || toolArgs.new_string === void 0) {
|
|
@@ -26148,8 +26053,8 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26148
26053
|
details: "old_string and new_string are required (or provide edits[])",
|
|
26149
26054
|
received: toolArgs
|
|
26150
26055
|
});
|
|
26151
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26152
|
-
this.persistSession();
|
|
26056
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26057
|
+
this.deps.persistSession();
|
|
26153
26058
|
return true;
|
|
26154
26059
|
}
|
|
26155
26060
|
}
|
|
@@ -26157,61 +26062,34 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26157
26062
|
let previewContent;
|
|
26158
26063
|
if (toolName === "edit_tool") {
|
|
26159
26064
|
try {
|
|
26160
|
-
previewContent = await this.
|
|
26065
|
+
previewContent = await this.deps.generateEditPreview(toolArgs);
|
|
26161
26066
|
} catch (previewError) {
|
|
26162
26067
|
previewContent = `Failed to generate preview: ${previewError.message}`;
|
|
26163
26068
|
}
|
|
26164
26069
|
} else if (toolName === "file_write") {
|
|
26165
26070
|
try {
|
|
26166
|
-
previewContent = this.
|
|
26071
|
+
previewContent = this.deps.generateFileWritePreview(toolArgs);
|
|
26167
26072
|
} catch (previewError) {
|
|
26168
26073
|
previewContent = `Failed to generate preview: ${previewError.message}`;
|
|
26169
26074
|
}
|
|
26170
26075
|
}
|
|
26171
|
-
this.eventBus.emit("backend_message", {
|
|
26076
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26172
26077
|
type: "tool_call",
|
|
26173
26078
|
tool_call_id: String(toolCall.id),
|
|
26174
26079
|
tool_name: toolName,
|
|
26175
26080
|
arguments: toolArgs,
|
|
26176
26081
|
preview: previewContent,
|
|
26177
26082
|
suppress_edit_diff_preview: toolName === "edit_tool",
|
|
26178
|
-
tool_policy: this.buildToolPolicyForUi(toolName, toolArgs)
|
|
26083
|
+
tool_policy: this.deps.buildToolPolicyForUi(toolName, toolArgs)
|
|
26179
26084
|
});
|
|
26180
26085
|
setToolResultStatus(String(toolCall.id), "running");
|
|
26181
26086
|
try {
|
|
26182
|
-
if (this.isInterrupted) {
|
|
26183
|
-
this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled before tool execution." });
|
|
26087
|
+
if (this.deps.isInterrupted()) {
|
|
26088
|
+
this.deps.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled before tool execution." });
|
|
26184
26089
|
return false;
|
|
26185
26090
|
}
|
|
26186
|
-
|
|
26187
|
-
|
|
26188
|
-
shell_command: "Executing",
|
|
26189
|
-
command_status: "Waiting",
|
|
26190
|
-
ls_tool: "Reading",
|
|
26191
|
-
read_file_lines: "Reading",
|
|
26192
|
-
message: "Responding",
|
|
26193
|
-
load_skill: "Loading skill",
|
|
26194
|
-
search_web: "Searching",
|
|
26195
|
-
todo: "Planning",
|
|
26196
|
-
ask_user_question: "Question",
|
|
26197
|
-
enter_plan_mode: "Planning",
|
|
26198
|
-
exit_plan_mode: "Planning",
|
|
26199
|
-
task_create: "Planning",
|
|
26200
|
-
task_list: "Planning",
|
|
26201
|
-
task_get: "Planning",
|
|
26202
|
-
task_update: "Planning",
|
|
26203
|
-
task_stop: "Planning",
|
|
26204
|
-
list_mcp_resources: "Reading",
|
|
26205
|
-
read_mcp_resource: "Reading",
|
|
26206
|
-
cron_create: "Scheduling",
|
|
26207
|
-
cron_list: "Reading",
|
|
26208
|
-
cron_delete: "Scheduling",
|
|
26209
|
-
notebook_edit: "Editing",
|
|
26210
|
-
lsp_query: "Reading"
|
|
26211
|
-
};
|
|
26212
|
-
const action = actionMap[toolName] || "Processing";
|
|
26213
|
-
this.eventBus.emit("action_status", { action });
|
|
26214
|
-
const result = await this.mcpClient.invoke(toolName, toolArgs);
|
|
26091
|
+
this.deps.eventBus.emit("action_status", getToolActionStatus(toolName, toolArgs));
|
|
26092
|
+
const result = await this.deps.mcpClient.invoke(toolName, toolArgs);
|
|
26215
26093
|
const normalized = normalizeToolResult(result);
|
|
26216
26094
|
setToolResultStatus(String(toolCall.id), normalized.status);
|
|
26217
26095
|
toolResultContent = toolResultToString(normalized);
|
|
@@ -26225,12 +26103,12 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26225
26103
|
null,
|
|
26226
26104
|
2
|
|
26227
26105
|
);
|
|
26228
|
-
this.eventBus.emit("backend_message", {
|
|
26106
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26229
26107
|
type: "error",
|
|
26230
26108
|
message: `Tool "${toolName}" failed: ${error.message}`
|
|
26231
26109
|
});
|
|
26232
26110
|
}
|
|
26233
|
-
this.eventBus.emit("backend_message", {
|
|
26111
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26234
26112
|
type: "tool_result",
|
|
26235
26113
|
tool_call_id: String(toolCall.id),
|
|
26236
26114
|
tool_name: toolName,
|
|
@@ -26242,21 +26120,20 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26242
26120
|
try {
|
|
26243
26121
|
const resultObj = typeof toolResultContent === "string" ? JSON.parse(toolResultContent) : toolResultContent;
|
|
26244
26122
|
if (resultObj.message_type === "result") {
|
|
26245
|
-
await this.notifyFactorTurnEndIfNeeded("message_result");
|
|
26123
|
+
await this.deps.notifyFactorTurnEndIfNeeded("message_result");
|
|
26246
26124
|
shouldContinueConversation = false;
|
|
26247
|
-
this.emitTurnCompleted();
|
|
26125
|
+
this.deps.emitTurnCompleted();
|
|
26248
26126
|
}
|
|
26249
26127
|
} catch {
|
|
26250
26128
|
}
|
|
26251
26129
|
}
|
|
26252
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26253
|
-
this.persistSession();
|
|
26130
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26131
|
+
this.deps.persistSession();
|
|
26254
26132
|
return shouldContinueConversation;
|
|
26255
26133
|
}
|
|
26256
|
-
/** Várias leituras seguras em paralelo (grep, ls, read_file, …). */
|
|
26257
26134
|
async executeParallelReadBatch(batch, _decisionData) {
|
|
26258
|
-
if (this.isInterrupted) {
|
|
26259
|
-
this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled before tool execution." });
|
|
26135
|
+
if (this.deps.isInterrupted()) {
|
|
26136
|
+
this.deps.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled before tool execution." });
|
|
26260
26137
|
return false;
|
|
26261
26138
|
}
|
|
26262
26139
|
const parsed = [];
|
|
@@ -26279,59 +26156,46 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26279
26156
|
error: `Tool "${toolName}" not found in agent catalog.`,
|
|
26280
26157
|
suggestion: "This tool does not exist. Please use a valid tool name from the available tools list."
|
|
26281
26158
|
});
|
|
26282
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26283
|
-
this.history.push({
|
|
26159
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26160
|
+
this.deps.history.push({
|
|
26284
26161
|
role: "system",
|
|
26285
26162
|
content: `[SYSTEM] Tool "${toolName}" is not found in the agent's available tools. Please use only tools that are listed in your tool catalog.`
|
|
26286
26163
|
});
|
|
26287
|
-
this.persistSession();
|
|
26164
|
+
this.deps.persistSession();
|
|
26288
26165
|
return true;
|
|
26289
26166
|
}
|
|
26290
26167
|
parsed.push({ toolCall, toolName, toolArgs });
|
|
26291
|
-
} catch
|
|
26168
|
+
} catch {
|
|
26292
26169
|
const toolResultContent = JSON.stringify({
|
|
26293
26170
|
error: "Tool arguments could not be parsed",
|
|
26294
26171
|
recovery: "Session recovered automatically"
|
|
26295
26172
|
});
|
|
26296
|
-
this.eventBus.emit("backend_message", {
|
|
26173
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26297
26174
|
type: "info",
|
|
26298
26175
|
message: "O BluMa encontrou um erro ao processar. A tentar recuperar a sess\xE3o..."
|
|
26299
26176
|
});
|
|
26300
|
-
this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26301
|
-
this.persistSession();
|
|
26177
|
+
this.deps.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
|
|
26178
|
+
this.deps.persistSession();
|
|
26302
26179
|
return true;
|
|
26303
26180
|
}
|
|
26304
26181
|
}
|
|
26305
|
-
const actionMap = {
|
|
26306
|
-
ls_tool: "Reading",
|
|
26307
|
-
read_file_lines: "Reading",
|
|
26308
|
-
count_file_lines: "Reading",
|
|
26309
|
-
find_by_name: "Reading",
|
|
26310
|
-
grep_search: "Reading",
|
|
26311
|
-
view_file_outline: "Reading",
|
|
26312
|
-
read_artifact: "Reading",
|
|
26313
|
-
list_agents: "Reading",
|
|
26314
|
-
list_mcp_resources: "Reading",
|
|
26315
|
-
read_mcp_resource: "Reading"
|
|
26316
|
-
};
|
|
26317
26182
|
const settled = await Promise.allSettled(
|
|
26318
26183
|
parsed.map(async (p) => {
|
|
26319
|
-
this.eventBus.emit("backend_message", {
|
|
26184
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26320
26185
|
type: "tool_call",
|
|
26321
26186
|
tool_call_id: String(p.toolCall.id),
|
|
26322
26187
|
tool_name: p.toolName,
|
|
26323
26188
|
arguments: p.toolArgs,
|
|
26324
|
-
tool_policy: this.buildToolPolicyForUi(p.toolName, p.toolArgs)
|
|
26189
|
+
tool_policy: this.deps.buildToolPolicyForUi(p.toolName, p.toolArgs)
|
|
26325
26190
|
});
|
|
26326
26191
|
setToolResultStatus(String(p.toolCall.id), "pending");
|
|
26327
26192
|
setToolResultStatus(String(p.toolCall.id), "running");
|
|
26328
|
-
|
|
26329
|
-
this.
|
|
26330
|
-
return this.mcpClient.invoke(p.toolName, p.toolArgs);
|
|
26193
|
+
this.deps.eventBus.emit("action_status", getToolActionStatus(p.toolName, p.toolArgs));
|
|
26194
|
+
return this.deps.mcpClient.invoke(p.toolName, p.toolArgs);
|
|
26331
26195
|
})
|
|
26332
26196
|
);
|
|
26333
26197
|
let shouldContinue = true;
|
|
26334
|
-
for (let k = 0; k < parsed.length; k
|
|
26198
|
+
for (let k = 0; k < parsed.length; k += 1) {
|
|
26335
26199
|
const p = parsed[k];
|
|
26336
26200
|
const outcome = settled[k];
|
|
26337
26201
|
let toolResultContent;
|
|
@@ -26343,7 +26207,7 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26343
26207
|
2
|
|
26344
26208
|
);
|
|
26345
26209
|
setToolResultStatus(String(p.toolCall.id), "error");
|
|
26346
|
-
this.eventBus.emit("backend_message", {
|
|
26210
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26347
26211
|
type: "error",
|
|
26348
26212
|
message: `Tool "${p.toolName}" failed: ${errMsg}`
|
|
26349
26213
|
});
|
|
@@ -26352,7 +26216,7 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26352
26216
|
setToolResultStatus(String(p.toolCall.id), normalized.status);
|
|
26353
26217
|
toolResultContent = toolResultToString(normalized);
|
|
26354
26218
|
}
|
|
26355
|
-
this.eventBus.emit("backend_message", {
|
|
26219
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26356
26220
|
type: "tool_result",
|
|
26357
26221
|
tool_call_id: String(p.toolCall.id),
|
|
26358
26222
|
tool_name: p.toolName,
|
|
@@ -26363,35 +26227,263 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26363
26227
|
try {
|
|
26364
26228
|
const resultObj = typeof toolResultContent === "string" ? JSON.parse(toolResultContent) : toolResultContent;
|
|
26365
26229
|
if (resultObj.message_type === "result") {
|
|
26366
|
-
await this.notifyFactorTurnEndIfNeeded("message_result");
|
|
26230
|
+
await this.deps.notifyFactorTurnEndIfNeeded("message_result");
|
|
26367
26231
|
shouldContinue = false;
|
|
26368
|
-
this.emitTurnCompleted();
|
|
26232
|
+
this.deps.emitTurnCompleted();
|
|
26369
26233
|
}
|
|
26370
26234
|
} catch {
|
|
26371
26235
|
}
|
|
26372
26236
|
}
|
|
26373
|
-
this.history.push({ role: "tool", tool_call_id: p.toolCall.id, content: toolResultContent });
|
|
26237
|
+
this.deps.history.push({ role: "tool", tool_call_id: p.toolCall.id, content: toolResultContent });
|
|
26374
26238
|
}
|
|
26375
|
-
this.persistSession();
|
|
26239
|
+
this.deps.persistSession();
|
|
26376
26240
|
return shouldContinue;
|
|
26377
26241
|
}
|
|
26378
|
-
|
|
26379
|
-
|
|
26380
|
-
|
|
26381
|
-
|
|
26382
|
-
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26242
|
+
};
|
|
26243
|
+
|
|
26244
|
+
// src/app/agent/core/llm/llm_errors.ts
|
|
26245
|
+
function isContextWindowValidationError(error) {
|
|
26246
|
+
const rawMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
26247
|
+
const lower = rawMessage.toLowerCase();
|
|
26248
|
+
return lower.includes("vllmvalidationerror") || lower.includes("maximum context length") || lower.includes("input prompt contains at least") || lower.includes("requested 0 output tokens") || lower.includes("context length");
|
|
26249
|
+
}
|
|
26250
|
+
function formatLlmUiError(error) {
|
|
26251
|
+
const rawMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error during LLM request.";
|
|
26252
|
+
const lower = rawMessage.toLowerCase();
|
|
26253
|
+
let message2 = "Ocorreu um erro inesperado ao contactar o modelo.";
|
|
26254
|
+
if (lower.includes("timeout") || lower.includes("etimedout") || lower.includes("upstream_timeout")) {
|
|
26255
|
+
message2 = "O BluMa demorou demasiado a responder.";
|
|
26256
|
+
} else if (lower.includes("connection") || lower.includes("econnrefused") || lower.includes("ehostunreach") || lower.includes("enotfound")) {
|
|
26257
|
+
message2 = "N\xE3o foi poss\xEDvel conectar ao servi\xE7o do modelo.";
|
|
26258
|
+
} else if (lower.includes("401") || lower.includes("403") || lower.includes("unauthorized") || lower.includes("forbidden")) {
|
|
26259
|
+
message2 = "Falha de autentica\xE7\xE3o/autoriza\xE7\xE3o ao contactar o modelo.";
|
|
26260
|
+
} else if (lower.includes("api")) {
|
|
26261
|
+
message2 = "Erro de comunica\xE7\xE3o com a API do modelo.";
|
|
26262
|
+
}
|
|
26263
|
+
return {
|
|
26264
|
+
message: message2,
|
|
26265
|
+
rawMessage
|
|
26266
|
+
};
|
|
26267
|
+
}
|
|
26268
|
+
|
|
26269
|
+
// src/app/agent/core/llm/tool_call_normalizer.ts
|
|
26270
|
+
import { randomUUID } from "crypto";
|
|
26271
|
+
var ToolCallNormalizer = class {
|
|
26272
|
+
/**
|
|
26273
|
+
* Com tool_calls e sem texto visível: content deve ser null (API OpenAI-compatible), nunca "" nem undefined.
|
|
26274
|
+
*/
|
|
26275
|
+
static assistantContentWithToolCalls(content) {
|
|
26276
|
+
if (content === void 0 || content === null) return null;
|
|
26277
|
+
if (typeof content === "string") return content.trim() === "" ? null : content;
|
|
26278
|
+
return null;
|
|
26279
|
+
}
|
|
26280
|
+
/**
|
|
26281
|
+
* Normaliza a mensagem do assistant, convertendo diferentes formatos de tool calls
|
|
26282
|
+
*/
|
|
26283
|
+
static normalizeAssistantMessage(message2) {
|
|
26284
|
+
if (message2.tool_calls && this.isOpenAIFormat(message2.tool_calls)) {
|
|
26285
|
+
return {
|
|
26286
|
+
...message2,
|
|
26287
|
+
content: this.assistantContentWithToolCalls(message2.content)
|
|
26288
|
+
};
|
|
26389
26289
|
}
|
|
26390
|
-
|
|
26391
|
-
|
|
26392
|
-
|
|
26393
|
-
|
|
26394
|
-
|
|
26290
|
+
const toolCalls = this.extractToolCalls(message2);
|
|
26291
|
+
if (toolCalls.length > 0) {
|
|
26292
|
+
return {
|
|
26293
|
+
role: message2.role || "assistant",
|
|
26294
|
+
content: this.assistantContentWithToolCalls(message2.content),
|
|
26295
|
+
tool_calls: toolCalls
|
|
26296
|
+
};
|
|
26297
|
+
}
|
|
26298
|
+
return message2;
|
|
26299
|
+
}
|
|
26300
|
+
/**
|
|
26301
|
+
* Verifica se já está no formato OpenAI
|
|
26302
|
+
*/
|
|
26303
|
+
static isOpenAIFormat(toolCalls) {
|
|
26304
|
+
if (!Array.isArray(toolCalls) || toolCalls.length === 0) return false;
|
|
26305
|
+
const firstCall = toolCalls[0];
|
|
26306
|
+
return typeof firstCall.id === "string" && firstCall.type === "function" && typeof firstCall.function?.name === "string" && typeof firstCall.function?.arguments === "string";
|
|
26307
|
+
}
|
|
26308
|
+
/**
|
|
26309
|
+
* Extrai tool calls de diversos formatos possíveis
|
|
26310
|
+
*/
|
|
26311
|
+
static extractToolCalls(message2) {
|
|
26312
|
+
const results = [];
|
|
26313
|
+
if (message2.tool_calls && Array.isArray(message2.tool_calls)) {
|
|
26314
|
+
for (const call of message2.tool_calls) {
|
|
26315
|
+
const normalized = this.normalizeToolCall(call);
|
|
26316
|
+
if (normalized) results.push(normalized);
|
|
26317
|
+
}
|
|
26318
|
+
}
|
|
26319
|
+
if (typeof message2.content === "string" && message2.content.trim()) {
|
|
26320
|
+
const extracted = this.extractFromContent(message2.content);
|
|
26321
|
+
results.push(...extracted);
|
|
26322
|
+
}
|
|
26323
|
+
if (message2.function_call) {
|
|
26324
|
+
const normalized = this.normalizeToolCall(message2.function_call);
|
|
26325
|
+
if (normalized) results.push(normalized);
|
|
26326
|
+
}
|
|
26327
|
+
return results;
|
|
26328
|
+
}
|
|
26329
|
+
/**
|
|
26330
|
+
* Normaliza um único tool call para o formato OpenAI
|
|
26331
|
+
*/
|
|
26332
|
+
static normalizeToolCall(call) {
|
|
26333
|
+
try {
|
|
26334
|
+
if (call.id && call.function?.name) {
|
|
26335
|
+
return {
|
|
26336
|
+
id: call.id,
|
|
26337
|
+
type: "function",
|
|
26338
|
+
function: {
|
|
26339
|
+
name: call.function.name,
|
|
26340
|
+
arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments)
|
|
26341
|
+
}
|
|
26342
|
+
};
|
|
26343
|
+
}
|
|
26344
|
+
if (call.name) {
|
|
26345
|
+
return {
|
|
26346
|
+
id: call.id || randomUUID(),
|
|
26347
|
+
type: "function",
|
|
26348
|
+
function: {
|
|
26349
|
+
name: call.name,
|
|
26350
|
+
arguments: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments || {})
|
|
26351
|
+
}
|
|
26352
|
+
};
|
|
26353
|
+
}
|
|
26354
|
+
if (call.function && typeof call.function === "object") {
|
|
26355
|
+
return {
|
|
26356
|
+
id: call.id || randomUUID(),
|
|
26357
|
+
type: "function",
|
|
26358
|
+
function: {
|
|
26359
|
+
name: call.function.name,
|
|
26360
|
+
arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments || {})
|
|
26361
|
+
}
|
|
26362
|
+
};
|
|
26363
|
+
}
|
|
26364
|
+
return null;
|
|
26365
|
+
} catch (error) {
|
|
26366
|
+
console.error("Error normalizing tool call:", error, call);
|
|
26367
|
+
return null;
|
|
26368
|
+
}
|
|
26369
|
+
}
|
|
26370
|
+
/**
|
|
26371
|
+
* Extrai tool calls do content (pode estar em markdown, JSON, etc)
|
|
26372
|
+
*/
|
|
26373
|
+
static extractFromContent(content) {
|
|
26374
|
+
const results = [];
|
|
26375
|
+
const cleanContent2 = content.replace(/```(?:json)?\s*([\s\S]*?)```/g, "$1");
|
|
26376
|
+
const jsonMatches = this.extractJsonObjects(cleanContent2);
|
|
26377
|
+
for (const jsonStr of jsonMatches) {
|
|
26378
|
+
try {
|
|
26379
|
+
const parsed = JSON.parse(jsonStr);
|
|
26380
|
+
if (Array.isArray(parsed)) {
|
|
26381
|
+
for (const call of parsed) {
|
|
26382
|
+
const normalized = this.normalizeToolCall(call);
|
|
26383
|
+
if (normalized) results.push(normalized);
|
|
26384
|
+
}
|
|
26385
|
+
} else if (parsed.name || parsed.function) {
|
|
26386
|
+
const normalized = this.normalizeToolCall(parsed);
|
|
26387
|
+
if (normalized) results.push(normalized);
|
|
26388
|
+
} else if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) {
|
|
26389
|
+
for (const call of parsed.tool_calls) {
|
|
26390
|
+
const normalized = this.normalizeToolCall(call);
|
|
26391
|
+
if (normalized) results.push(normalized);
|
|
26392
|
+
}
|
|
26393
|
+
}
|
|
26394
|
+
} catch (e) {
|
|
26395
|
+
}
|
|
26396
|
+
}
|
|
26397
|
+
return results;
|
|
26398
|
+
}
|
|
26399
|
+
/**
|
|
26400
|
+
* Extrai objetos JSON de uma string (suporta múltiplos objetos)
|
|
26401
|
+
*/
|
|
26402
|
+
static extractJsonObjects(text) {
|
|
26403
|
+
const results = [];
|
|
26404
|
+
let depth = 0;
|
|
26405
|
+
let start = -1;
|
|
26406
|
+
for (let i = 0; i < text.length; i++) {
|
|
26407
|
+
if (text[i] === "{") {
|
|
26408
|
+
if (depth === 0) start = i;
|
|
26409
|
+
depth++;
|
|
26410
|
+
} else if (text[i] === "}") {
|
|
26411
|
+
depth--;
|
|
26412
|
+
if (depth === 0 && start !== -1) {
|
|
26413
|
+
results.push(text.substring(start, i + 1));
|
|
26414
|
+
start = -1;
|
|
26415
|
+
}
|
|
26416
|
+
}
|
|
26417
|
+
}
|
|
26418
|
+
return results;
|
|
26419
|
+
}
|
|
26420
|
+
/**
|
|
26421
|
+
* Valida se um tool call normalizado é válido
|
|
26422
|
+
* Validação dupla:
|
|
26423
|
+
* 1. JSON dos argumentos é válido
|
|
26424
|
+
* 2. A ferramenta existe no catálogo de ferramentas nativas
|
|
26425
|
+
*/
|
|
26426
|
+
static isValidToolCall(call) {
|
|
26427
|
+
if (!(call.id && call.type === "function" && call.function?.name && typeof call.function.arguments === "string")) {
|
|
26428
|
+
return false;
|
|
26429
|
+
}
|
|
26430
|
+
try {
|
|
26431
|
+
JSON.parse(call.function.arguments);
|
|
26432
|
+
} catch {
|
|
26433
|
+
return false;
|
|
26434
|
+
}
|
|
26435
|
+
const toolMetadata = getNativeToolMetadata(call.function.name);
|
|
26436
|
+
if (!toolMetadata) {
|
|
26437
|
+
return false;
|
|
26438
|
+
}
|
|
26439
|
+
return true;
|
|
26440
|
+
}
|
|
26441
|
+
};
|
|
26442
|
+
|
|
26443
|
+
// src/app/agent/runtime/tool_orchestration.ts
|
|
26444
|
+
var PARALLEL_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
26445
|
+
"ls_tool",
|
|
26446
|
+
"read_file_lines",
|
|
26447
|
+
"count_file_lines",
|
|
26448
|
+
"find_by_name",
|
|
26449
|
+
"grep_search",
|
|
26450
|
+
"view_file_outline",
|
|
26451
|
+
"read_artifact",
|
|
26452
|
+
"list_agents",
|
|
26453
|
+
"list_mcp_resources",
|
|
26454
|
+
"read_mcp_resource",
|
|
26455
|
+
"todo"
|
|
26456
|
+
]);
|
|
26457
|
+
function toolEligibleForParallelRead(toolName, sessionId) {
|
|
26458
|
+
const name = String(toolName || "").trim();
|
|
26459
|
+
if (!name || !PARALLEL_SAFE_TOOL_NAMES.has(name)) return false;
|
|
26460
|
+
if (!decideToolExecution(name, void 0, sessionId).autoApprove) return false;
|
|
26461
|
+
const meta = getNativeToolMetadata(name);
|
|
26462
|
+
return meta?.riskLevel === "safe";
|
|
26463
|
+
}
|
|
26464
|
+
|
|
26465
|
+
// src/app/agent/bluma/core/bluma_turn_coordinator.ts
|
|
26466
|
+
init_logger();
|
|
26467
|
+
var BluMaTurnCoordinator = class {
|
|
26468
|
+
constructor(deps) {
|
|
26469
|
+
this.deps = deps;
|
|
26470
|
+
}
|
|
26471
|
+
emptyAssistantReplySteps = 0;
|
|
26472
|
+
invalidToolCallRetrySteps = 0;
|
|
26473
|
+
turnLog = logger.child("turn_coordinator");
|
|
26474
|
+
resetTurnState() {
|
|
26475
|
+
this.emptyAssistantReplySteps = 0;
|
|
26476
|
+
this.invalidToolCallRetrySteps = 0;
|
|
26477
|
+
}
|
|
26478
|
+
async handleToolResponse(decisionData) {
|
|
26479
|
+
const calls = decisionData.tool_calls;
|
|
26480
|
+
if (!calls?.length) return;
|
|
26481
|
+
const shouldContinueConversation = decisionData.continueConversation !== false;
|
|
26482
|
+
if (decisionData.type === "user_decision_decline") {
|
|
26483
|
+
const declineMsg = "The system rejected this action. Verify that the command you are executing contributes to the tasks intent and try again.";
|
|
26484
|
+
for (const toolCall of calls) {
|
|
26485
|
+
const toolName = toolCall.function?.name || "tool";
|
|
26486
|
+
let args = {};
|
|
26395
26487
|
try {
|
|
26396
26488
|
if (typeof toolCall.function?.arguments === "string") {
|
|
26397
26489
|
args = JSON.parse(toolCall.function.arguments);
|
|
@@ -26401,276 +26493,254 @@ var BluMaAgent = class _BluMaAgent {
|
|
|
26401
26493
|
} catch {
|
|
26402
26494
|
args = {};
|
|
26403
26495
|
}
|
|
26404
|
-
|
|
26405
|
-
setToolResultStatus(String(toolCall.id), "error");
|
|
26406
|
-
}
|
|
26407
|
-
this.eventBus.emit("backend_message", {
|
|
26496
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26408
26497
|
type: "tool_result",
|
|
26409
26498
|
tool_call_id: String(toolCall.id),
|
|
26410
26499
|
tool_name: toolName,
|
|
26411
26500
|
arguments: args,
|
|
26412
26501
|
result: JSON.stringify({ status: "error", error: declineMsg }, null, 2)
|
|
26413
26502
|
});
|
|
26414
|
-
this.history.push({
|
|
26503
|
+
this.deps.history.push({
|
|
26415
26504
|
role: "tool",
|
|
26416
26505
|
tool_call_id: toolCall.id,
|
|
26417
26506
|
content: JSON.stringify({ status: "error", error: declineMsg }, null, 2)
|
|
26418
26507
|
});
|
|
26419
26508
|
}
|
|
26420
|
-
this.persistSession();
|
|
26421
|
-
if (shouldContinueConversation && !this.isInterrupted) {
|
|
26422
|
-
await this.
|
|
26509
|
+
this.deps.persistSession();
|
|
26510
|
+
if (shouldContinueConversation && !this.deps.isInterrupted()) {
|
|
26511
|
+
await this.continueConversation();
|
|
26423
26512
|
}
|
|
26424
26513
|
return;
|
|
26425
26514
|
}
|
|
26426
26515
|
if (decisionData.type !== "user_decision_execute") {
|
|
26427
26516
|
return;
|
|
26428
26517
|
}
|
|
26429
|
-
setTaskToolSessionContext(this.sessionId);
|
|
26430
26518
|
let shouldContinue = true;
|
|
26431
26519
|
let i = 0;
|
|
26432
|
-
while (i < calls.length && shouldContinue && !this.isInterrupted) {
|
|
26520
|
+
while (i < calls.length && shouldContinue && !this.deps.isInterrupted()) {
|
|
26433
26521
|
const name = calls[i].function.name;
|
|
26434
|
-
if (!toolEligibleForParallelRead(name, this.sessionId)) {
|
|
26435
|
-
shouldContinue = await this.executeApprovedToolInvocation(calls[i], decisionData);
|
|
26522
|
+
if (!toolEligibleForParallelRead(name, this.deps.sessionId)) {
|
|
26523
|
+
shouldContinue = await this.deps.toolRunner.executeApprovedToolInvocation(calls[i], decisionData);
|
|
26436
26524
|
i += 1;
|
|
26437
26525
|
continue;
|
|
26438
26526
|
}
|
|
26439
26527
|
let j = i;
|
|
26440
|
-
while (j < calls.length && toolEligibleForParallelRead(calls[j].function.name, this.sessionId)) {
|
|
26528
|
+
while (j < calls.length && toolEligibleForParallelRead(calls[j].function.name, this.deps.sessionId)) {
|
|
26441
26529
|
j += 1;
|
|
26442
26530
|
}
|
|
26443
26531
|
const slice = calls.slice(i, j);
|
|
26444
26532
|
if (slice.length === 1) {
|
|
26445
|
-
shouldContinue = await this.executeApprovedToolInvocation(slice[0], decisionData);
|
|
26533
|
+
shouldContinue = await this.deps.toolRunner.executeApprovedToolInvocation(slice[0], decisionData);
|
|
26446
26534
|
} else {
|
|
26447
|
-
shouldContinue = await this.executeParallelReadBatch(slice, decisionData);
|
|
26535
|
+
shouldContinue = await this.deps.toolRunner.executeParallelReadBatch(slice, decisionData);
|
|
26448
26536
|
}
|
|
26449
26537
|
i = j;
|
|
26450
26538
|
}
|
|
26451
|
-
if (shouldContinueConversation && shouldContinue && !this.isInterrupted) {
|
|
26452
|
-
await this.
|
|
26539
|
+
if (shouldContinueConversation && shouldContinue && !this.deps.isInterrupted()) {
|
|
26540
|
+
await this.continueConversation();
|
|
26453
26541
|
}
|
|
26454
26542
|
}
|
|
26455
|
-
async
|
|
26543
|
+
async continueConversation() {
|
|
26456
26544
|
try {
|
|
26457
|
-
if (
|
|
26458
|
-
|
|
26545
|
+
if (this.deps.isInterrupted()) {
|
|
26546
|
+
this.deps.eventBus.emit("backend_message", { type: "info", message: "Task Canceled." });
|
|
26547
|
+
return;
|
|
26459
26548
|
}
|
|
26460
|
-
|
|
26461
|
-
|
|
26549
|
+
const mailboxUpdate = await this.pollWorkerMailbox();
|
|
26550
|
+
if (mailboxUpdate && mailboxUpdate.followUp) {
|
|
26551
|
+
this.deps.history.push({
|
|
26552
|
+
role: "user",
|
|
26553
|
+
content: mailboxUpdate.followUp.message
|
|
26554
|
+
});
|
|
26555
|
+
this.deps.persistSession();
|
|
26556
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26557
|
+
type: "info",
|
|
26558
|
+
message: `Received follow-up from coordinator (priority: ${mailboxUpdate.followUp.priority})`
|
|
26559
|
+
});
|
|
26462
26560
|
}
|
|
26463
|
-
const
|
|
26464
|
-
|
|
26465
|
-
|
|
26466
|
-
|
|
26467
|
-
|
|
26468
|
-
|
|
26469
|
-
|
|
26470
|
-
|
|
26471
|
-
|
|
26472
|
-
|
|
26561
|
+
const sanitized = sanitizeConversationForProvider(this.deps.history);
|
|
26562
|
+
if (sanitized.issues.length > 0) {
|
|
26563
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26564
|
+
type: "info",
|
|
26565
|
+
message: `Sanitizing ${sanitized.issues.length} messages from history`
|
|
26566
|
+
});
|
|
26567
|
+
this.deps.history.splice(0, this.deps.history.length, ...sanitized.messages);
|
|
26568
|
+
this.deps.persistSession();
|
|
26569
|
+
}
|
|
26570
|
+
this.turnLog.debug("Conversation sanitized", {
|
|
26571
|
+
history_length: this.deps.history.length,
|
|
26572
|
+
sanitized_length: sanitized.messages.length,
|
|
26573
|
+
issue_count: sanitized.issues.length,
|
|
26574
|
+
issues: sanitized.issues.slice(0, 5),
|
|
26575
|
+
history_preview: summarizeHistoryForLog(this.deps.history, 6)
|
|
26576
|
+
});
|
|
26577
|
+
const modelName = this.deps.llm.getModelName ? this.deps.llm.getModelName() : "auto";
|
|
26578
|
+
const baseTokenBudget = getContextInputBudgetForModel(modelName);
|
|
26579
|
+
const retryTokenBudget = Math.max(48e3, Math.floor(baseTokenBudget * 0.75));
|
|
26580
|
+
const tokenBudgets = [baseTokenBudget, retryTokenBudget];
|
|
26581
|
+
const llmService = this.deps.llm;
|
|
26582
|
+
for (let attempt = 0; attempt < tokenBudgets.length; attempt += 1) {
|
|
26583
|
+
const tokenBudget = tokenBudgets[attempt];
|
|
26584
|
+
try {
|
|
26585
|
+
const { messages: contextWindow, prunedHistory } = await this.deps.compressor.buildContextWindow(
|
|
26586
|
+
this.deps.history,
|
|
26587
|
+
this.deps.llm,
|
|
26588
|
+
this.deps.getLlmUserContext(),
|
|
26589
|
+
{
|
|
26590
|
+
tokenBudget,
|
|
26591
|
+
toolDefinitions: this.deps.mcpClient.getAvailableTools()
|
|
26592
|
+
}
|
|
26593
|
+
);
|
|
26594
|
+
this.turnLog.debug("Context window prepared", {
|
|
26595
|
+
history_length: this.deps.history.length,
|
|
26596
|
+
context_window_length: contextWindow.length,
|
|
26597
|
+
pruned_history_length: prunedHistory?.length ?? null,
|
|
26598
|
+
token_budget: tokenBudget,
|
|
26599
|
+
context_preview: summarizeHistoryForLog(contextWindow, 8)
|
|
26600
|
+
});
|
|
26601
|
+
if (prunedHistory) {
|
|
26602
|
+
this.deps.history.splice(0, this.deps.history.length, ...prunedHistory);
|
|
26603
|
+
}
|
|
26604
|
+
this.deps.persistSession();
|
|
26605
|
+
if (typeof llmService.chatCompletionStream === "function") {
|
|
26606
|
+
await this.handleStreamingResponse(contextWindow);
|
|
26607
|
+
} else {
|
|
26608
|
+
await this.handleNonStreamingResponse(contextWindow);
|
|
26609
|
+
}
|
|
26610
|
+
return;
|
|
26611
|
+
} catch (error) {
|
|
26612
|
+
const retryable = isContextWindowValidationError(error);
|
|
26613
|
+
const hasRetryLeft = attempt < tokenBudgets.length - 1;
|
|
26614
|
+
if (retryable && hasRetryLeft) {
|
|
26615
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26616
|
+
type: "info",
|
|
26617
|
+
message: "The model rejected the prompt size. Retrying with a smaller context window."
|
|
26618
|
+
});
|
|
26619
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26620
|
+
type: "log",
|
|
26621
|
+
message: "Context validation retry",
|
|
26622
|
+
payload: `attempt=${attempt + 1} budget=${tokenBudget} nextBudget=${tokenBudgets[attempt + 1]}`
|
|
26623
|
+
});
|
|
26624
|
+
continue;
|
|
26625
|
+
}
|
|
26626
|
+
throw error;
|
|
26627
|
+
}
|
|
26473
26628
|
}
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26629
|
+
} catch (error) {
|
|
26630
|
+
this.deps.persistSessionSync();
|
|
26631
|
+
const uiError = formatLlmUiError(error);
|
|
26632
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26633
|
+
type: "error",
|
|
26634
|
+
message: "Communication error with the BluMa"
|
|
26635
|
+
});
|
|
26636
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26637
|
+
type: "log",
|
|
26638
|
+
message: "LLM request failed",
|
|
26639
|
+
payload: uiError.rawMessage
|
|
26640
|
+
});
|
|
26641
|
+
await this.deps.notifyFactorTurnEndIfNeeded("llm_error");
|
|
26642
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26643
|
+
type: "done",
|
|
26644
|
+
status: "error",
|
|
26645
|
+
canRecover: true,
|
|
26646
|
+
message: "Communication error with the BluMa"
|
|
26647
|
+
});
|
|
26648
|
+
} finally {
|
|
26649
|
+
this.deps.persistSession();
|
|
26478
26650
|
}
|
|
26479
26651
|
}
|
|
26480
|
-
_generateFileWritePreview(toolArgs) {
|
|
26481
|
-
try {
|
|
26482
|
-
const content = typeof toolArgs?.content === "string" ? toolArgs.content : String(toolArgs?.content ?? "");
|
|
26483
|
-
if (!content) {
|
|
26484
|
-
return "(No content)";
|
|
26485
|
-
}
|
|
26486
|
-
const normalized = content.replace(/\r\n/g, "\n");
|
|
26487
|
-
const lines = normalized.split("\n");
|
|
26488
|
-
const maxPreviewLines = 20;
|
|
26489
|
-
const visibleLines = lines.slice(0, maxPreviewLines);
|
|
26490
|
-
const hiddenLines = lines.length - visibleLines.length;
|
|
26491
|
-
return hiddenLines > 0 ? `${visibleLines.join("\n")}
|
|
26492
|
-
\u2026 +${hiddenLines} more lines` : normalized;
|
|
26493
|
-
} catch (e) {
|
|
26494
|
-
return `An unexpected error occurred while generating the file write preview: ${e.message}`;
|
|
26495
|
-
}
|
|
26496
|
-
}
|
|
26497
|
-
getLlmUserContext() {
|
|
26498
|
-
if (!this.activeTurnContext) {
|
|
26499
|
-
throw new Error("BluMaAgent: activeTurnContext ausente (processTurn n\xE3o iniciou o turno).");
|
|
26500
|
-
}
|
|
26501
|
-
return this.activeTurnContext;
|
|
26502
|
-
}
|
|
26503
|
-
/**
|
|
26504
|
-
* Um único POST ao Factor Router por turno (`notifyFactorRouterTurnEnd`).
|
|
26505
|
-
* `reason` típicos: `message_result`, `user_interrupt`, `empty_reply_exhausted`, `llm_error`.
|
|
26506
|
-
*/
|
|
26507
|
-
async notifyFactorTurnEndIfNeeded(reason) {
|
|
26508
|
-
if (this.factorRouterTurnClosed || !this.activeTurnContext) return;
|
|
26509
|
-
this.factorRouterTurnClosed = true;
|
|
26510
|
-
const ctx = this.activeTurnContext;
|
|
26511
|
-
await notifyFactorRouterTurnEnd({
|
|
26512
|
-
turnId: ctx.turnId,
|
|
26513
|
-
userContext: ctx,
|
|
26514
|
-
reason
|
|
26515
|
-
});
|
|
26516
|
-
}
|
|
26517
|
-
/** Fecho explícito de turno (útil para workers antes de process.exit). */
|
|
26518
|
-
async closeActiveTurn(reason = "worker_exit") {
|
|
26519
|
-
await this.notifyFactorTurnEndIfNeeded(reason);
|
|
26520
|
-
}
|
|
26521
|
-
async _continueConversation() {
|
|
26522
|
-
try {
|
|
26523
|
-
if (this.isInterrupted) {
|
|
26524
|
-
this.eventBus.emit("backend_message", { type: "info", message: "Task Canceled." });
|
|
26525
|
-
return;
|
|
26526
|
-
}
|
|
26527
|
-
const mailboxUpdate = await this._pollWorkerMailbox();
|
|
26528
|
-
if (mailboxUpdate && mailboxUpdate.followUp) {
|
|
26529
|
-
this.history.push({
|
|
26530
|
-
role: "user",
|
|
26531
|
-
content: mailboxUpdate.followUp.message
|
|
26532
|
-
});
|
|
26533
|
-
this.persistSession();
|
|
26534
|
-
this.eventBus.emit("backend_message", {
|
|
26535
|
-
type: "info",
|
|
26536
|
-
message: `Received follow-up from coordinator (priority: ${mailboxUpdate.followUp.priority})`
|
|
26537
|
-
});
|
|
26538
|
-
}
|
|
26539
|
-
const sanitized = sanitizeConversationForProvider(this.history);
|
|
26540
|
-
if (sanitized.issues.length > 0) {
|
|
26541
|
-
this.eventBus.emit("backend_message", {
|
|
26542
|
-
type: "info",
|
|
26543
|
-
message: `Sanitizing ${sanitized.issues.length} messages from history`
|
|
26544
|
-
});
|
|
26545
|
-
this.history = sanitized.messages;
|
|
26546
|
-
this.persistSession();
|
|
26547
|
-
}
|
|
26548
|
-
const modelName = this.llm.getModelName ? this.llm.getModelName() : "auto";
|
|
26549
|
-
const tokenBudget = getContextInputBudgetForModel(modelName);
|
|
26550
|
-
const { messages: contextWindow, prunedHistory } = await this.compressor.buildContextWindow(
|
|
26551
|
-
this.history,
|
|
26552
|
-
this.llm,
|
|
26553
|
-
this.getLlmUserContext(),
|
|
26554
|
-
{
|
|
26555
|
-
tokenBudget,
|
|
26556
|
-
toolDefinitions: this.mcpClient.getAvailableTools()
|
|
26557
|
-
}
|
|
26558
|
-
);
|
|
26559
|
-
if (prunedHistory) {
|
|
26560
|
-
this.history = prunedHistory;
|
|
26561
|
-
}
|
|
26562
|
-
this.persistSession();
|
|
26563
|
-
const llmService = this.llm;
|
|
26564
|
-
if (typeof llmService.chatCompletionStream === "function") {
|
|
26565
|
-
await this._handleStreamingResponse(contextWindow);
|
|
26566
|
-
} else {
|
|
26567
|
-
await this._handleNonStreamingResponse(contextWindow);
|
|
26568
|
-
}
|
|
26569
|
-
} catch (error) {
|
|
26570
|
-
this.persistSessionSync();
|
|
26571
|
-
this.history.push({
|
|
26572
|
-
role: "assistant",
|
|
26573
|
-
content: "[<=================== The conversation will continue from here ==================>]"
|
|
26574
|
-
});
|
|
26575
|
-
this.persistSessionSync();
|
|
26576
|
-
const uiError = formatLlmUiError(error);
|
|
26577
|
-
this.eventBus.emit("backend_message", {
|
|
26578
|
-
type: "error",
|
|
26579
|
-
message: "Communication error with the BluMa"
|
|
26580
|
-
//details: 'The connection was temporarily interrupted. Your history has been safely saved.',
|
|
26581
|
-
//hint: 'You can continue from where you left off. No information was lost.',
|
|
26582
|
-
});
|
|
26583
|
-
this.eventBus.emit("backend_message", {
|
|
26584
|
-
type: "log",
|
|
26585
|
-
message: "LLM request failed",
|
|
26586
|
-
payload: uiError.rawMessage
|
|
26587
|
-
});
|
|
26588
|
-
await this.notifyFactorTurnEndIfNeeded("llm_error");
|
|
26589
|
-
this.eventBus.emit("backend_message", {
|
|
26590
|
-
type: "done",
|
|
26591
|
-
status: "error",
|
|
26592
|
-
canRecover: true,
|
|
26593
|
-
message: "Communication error with the BluMa"
|
|
26594
|
-
});
|
|
26595
|
-
} finally {
|
|
26596
|
-
this.persistSession();
|
|
26597
|
-
}
|
|
26598
|
-
}
|
|
26599
|
-
/**
|
|
26600
|
-
* O modelo devolveu mensagem sem texto útil nem tool_calls (só raciocínio interno).
|
|
26601
|
-
* Nudge único + teto de tentativas para não ficar preso em "Thinking..." como no olá.
|
|
26602
|
-
*/
|
|
26603
26652
|
async continueAfterEmptyAssistantResponse() {
|
|
26604
26653
|
this.emptyAssistantReplySteps += 1;
|
|
26605
26654
|
if (this.emptyAssistantReplySteps === 1) {
|
|
26606
|
-
this.history.push({
|
|
26655
|
+
this.deps.history.push({
|
|
26607
26656
|
role: "system",
|
|
26608
26657
|
content: `You did not call any tool and produced no user-visible reply. Respond using the message tool with message_type "result". Keep it concise and in the user's language.`
|
|
26609
26658
|
});
|
|
26610
26659
|
} else if (this.emptyAssistantReplySteps === 2) {
|
|
26611
|
-
this.history.push({
|
|
26660
|
+
this.deps.history.push({
|
|
26612
26661
|
role: "system",
|
|
26613
26662
|
content: 'Retry: call message tool with message_type "result" or use a tool.'
|
|
26614
26663
|
});
|
|
26615
26664
|
} else if (this.emptyAssistantReplySteps >= 6) {
|
|
26616
|
-
this.eventBus.emit("backend_message", {
|
|
26665
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26617
26666
|
type: "info",
|
|
26618
26667
|
message: "The BluMa is having difficulty processing yor task."
|
|
26619
26668
|
});
|
|
26620
|
-
await this.notifyFactorTurnEndIfNeeded("empty_reply_exhausted");
|
|
26621
|
-
this.eventBus.emit("backend_message", { type: "done", status: "failed" });
|
|
26669
|
+
await this.deps.notifyFactorTurnEndIfNeeded("empty_reply_exhausted");
|
|
26670
|
+
this.deps.eventBus.emit("backend_message", { type: "done", status: "failed" });
|
|
26622
26671
|
this.emptyAssistantReplySteps = 0;
|
|
26623
26672
|
return;
|
|
26624
26673
|
}
|
|
26625
|
-
await this.
|
|
26674
|
+
await this.continueConversation();
|
|
26626
26675
|
}
|
|
26627
|
-
|
|
26628
|
-
|
|
26629
|
-
|
|
26630
|
-
|
|
26631
|
-
const hasContent = content.trim().length > 0;
|
|
26632
|
-
if (!hasContent && !hasTools) {
|
|
26633
|
-
return;
|
|
26676
|
+
async handleInvalidToolCallRetry(message2) {
|
|
26677
|
+
this.invalidToolCallRetrySteps += 1;
|
|
26678
|
+
if (this.deps.history[this.deps.history.length - 1] === message2) {
|
|
26679
|
+
this.deps.history.pop();
|
|
26634
26680
|
}
|
|
26635
|
-
this.
|
|
26636
|
-
|
|
26637
|
-
|
|
26681
|
+
if (this.invalidToolCallRetrySteps >= 3) {
|
|
26682
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26683
|
+
type: "error",
|
|
26684
|
+
message: "The model kept returning invalid tool calls. Closing the turn to avoid a retry loop."
|
|
26685
|
+
});
|
|
26686
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26687
|
+
type: "log",
|
|
26688
|
+
message: "Invalid tool call retry limit reached",
|
|
26689
|
+
payload: String(this.invalidToolCallRetrySteps)
|
|
26690
|
+
});
|
|
26691
|
+
await this.deps.notifyFactorTurnEndIfNeeded("invalid_tool_calls_exhausted");
|
|
26692
|
+
this.deps.eventBus.emit("backend_message", { type: "done", status: "failed" });
|
|
26693
|
+
this.invalidToolCallRetrySteps = 0;
|
|
26694
|
+
return;
|
|
26638
26695
|
}
|
|
26639
|
-
|
|
26640
|
-
|
|
26696
|
+
this.deps.history.push({
|
|
26697
|
+
role: "system",
|
|
26698
|
+
content: "Previous assistant tool_calls were invalid. Retry with valid JSON arguments only, or answer without tools."
|
|
26699
|
+
});
|
|
26700
|
+
this.deps.persistSession();
|
|
26701
|
+
await this.continueConversation();
|
|
26641
26702
|
}
|
|
26642
|
-
async
|
|
26643
|
-
const llmService = this.llm;
|
|
26703
|
+
async handleStreamingResponse(contextWindow) {
|
|
26704
|
+
const llmService = this.deps.llm;
|
|
26644
26705
|
let accumulatedContent = "";
|
|
26645
26706
|
let toolCalls;
|
|
26646
26707
|
let hasEmittedStart = false;
|
|
26708
|
+
let currentStreamPhase = "idle";
|
|
26647
26709
|
const stream = llmService.chatCompletionStream({
|
|
26648
26710
|
messages: contextWindow,
|
|
26649
26711
|
temperature: 0,
|
|
26650
|
-
tools: this.mcpClient.getAvailableTools(),
|
|
26712
|
+
tools: this.deps.mcpClient.getAvailableTools(),
|
|
26651
26713
|
parallel_tool_calls: true,
|
|
26652
|
-
userContext: this.getLlmUserContext()
|
|
26714
|
+
userContext: this.deps.getLlmUserContext()
|
|
26653
26715
|
});
|
|
26654
26716
|
for await (const chunk of stream) {
|
|
26655
|
-
if (this.isInterrupted) {
|
|
26656
|
-
this.eventBus.emit("stream_end", {});
|
|
26657
|
-
this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
|
|
26717
|
+
if (this.deps.isInterrupted()) {
|
|
26718
|
+
this.deps.eventBus.emit("stream_end", {});
|
|
26719
|
+
this.deps.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
|
|
26658
26720
|
return;
|
|
26659
26721
|
}
|
|
26660
26722
|
if (chunk.reasoning) {
|
|
26661
26723
|
if (!hasEmittedStart) {
|
|
26662
|
-
this.eventBus.emit("stream_start", {});
|
|
26724
|
+
this.deps.eventBus.emit("stream_start", {});
|
|
26663
26725
|
hasEmittedStart = true;
|
|
26664
26726
|
}
|
|
26665
|
-
|
|
26727
|
+
if (currentStreamPhase !== "reasoning") {
|
|
26728
|
+
this.deps.eventBus.emit("action_status", getReasoningActionStatus());
|
|
26729
|
+
currentStreamPhase = "reasoning";
|
|
26730
|
+
}
|
|
26731
|
+
this.deps.eventBus.emit("stream_reasoning_chunk", { delta: chunk.reasoning });
|
|
26666
26732
|
}
|
|
26667
26733
|
if (chunk.delta) {
|
|
26668
26734
|
if (!hasEmittedStart) {
|
|
26669
|
-
this.eventBus.emit("stream_start", {});
|
|
26735
|
+
this.deps.eventBus.emit("stream_start", {});
|
|
26670
26736
|
hasEmittedStart = true;
|
|
26671
26737
|
}
|
|
26738
|
+
if (currentStreamPhase !== "responding") {
|
|
26739
|
+
this.deps.eventBus.emit("action_status", getRespondingActionStatus());
|
|
26740
|
+
currentStreamPhase = "responding";
|
|
26741
|
+
}
|
|
26672
26742
|
accumulatedContent += chunk.delta;
|
|
26673
|
-
this.eventBus.emit("stream_chunk", { delta: chunk.delta });
|
|
26743
|
+
this.deps.eventBus.emit("stream_chunk", { delta: chunk.delta });
|
|
26674
26744
|
}
|
|
26675
26745
|
if (chunk.tool_calls && chunk.tool_calls.length > 0) {
|
|
26676
26746
|
toolCalls = chunk.tool_calls;
|
|
@@ -26678,14 +26748,14 @@ ${editData.error.display}`;
|
|
|
26678
26748
|
const toolName = tc?.function?.name ?? "";
|
|
26679
26749
|
const argsRaw = tc?.function?.arguments ?? "{}";
|
|
26680
26750
|
if (toolName) {
|
|
26681
|
-
this.eventBus.emit("tool_stream_start", { toolName, argsRaw });
|
|
26751
|
+
this.deps.eventBus.emit("tool_stream_start", { toolName, argsRaw });
|
|
26682
26752
|
}
|
|
26683
26753
|
}
|
|
26684
26754
|
}
|
|
26685
26755
|
}
|
|
26686
26756
|
const omitAssistantFlush = Array.isArray(toolCalls) && toolCalls.some((tc) => String(tc?.function?.name ?? "").includes("message"));
|
|
26687
26757
|
if (hasEmittedStart) {
|
|
26688
|
-
this.eventBus.emit("stream_end", { omitAssistantFlush });
|
|
26758
|
+
this.deps.eventBus.emit("stream_end", { omitAssistantFlush });
|
|
26689
26759
|
}
|
|
26690
26760
|
const trimmedText = accumulatedContent.trim();
|
|
26691
26761
|
const hasToolCalls = Boolean(toolCalls && toolCalls.length > 0);
|
|
@@ -26698,7 +26768,7 @@ ${editData.error.display}`;
|
|
|
26698
26768
|
message2.tool_calls = toolCalls;
|
|
26699
26769
|
}
|
|
26700
26770
|
const normalizedMessage = ToolCallNormalizer.normalizeAssistantMessage(message2);
|
|
26701
|
-
this.history.push(normalizedMessage);
|
|
26771
|
+
this.deps.history.push(normalizedMessage);
|
|
26702
26772
|
if (normalizedMessage.tool_calls && normalizedMessage.tool_calls.length > 0) {
|
|
26703
26773
|
this.emptyAssistantReplySteps = 0;
|
|
26704
26774
|
this.invalidToolCallRetrySteps = 0;
|
|
@@ -26710,10 +26780,10 @@ ${editData.error.display}`;
|
|
|
26710
26780
|
return;
|
|
26711
26781
|
}
|
|
26712
26782
|
const autoApprovedToolCalls = validToolCalls.filter(
|
|
26713
|
-
(tc) => effectiveToolAutoApprove(tc, this.sessionId)
|
|
26783
|
+
(tc) => effectiveToolAutoApprove(tc, this.deps.sessionId)
|
|
26714
26784
|
);
|
|
26715
26785
|
const confirmationToolCalls = validToolCalls.filter(
|
|
26716
|
-
(tc) => !effectiveToolAutoApprove(tc, this.sessionId)
|
|
26786
|
+
(tc) => !effectiveToolAutoApprove(tc, this.deps.sessionId)
|
|
26717
26787
|
);
|
|
26718
26788
|
if (autoApprovedToolCalls.length > 0) {
|
|
26719
26789
|
await this.handleToolResponse({
|
|
@@ -26724,17 +26794,17 @@ ${editData.error.display}`;
|
|
|
26724
26794
|
}
|
|
26725
26795
|
if (confirmationToolCalls.length > 0) {
|
|
26726
26796
|
const toolToCall = confirmationToolCalls[0];
|
|
26727
|
-
const decision = decideToolExecution(toolToCall.function.name, void 0, this.sessionId);
|
|
26797
|
+
const decision = decideToolExecution(toolToCall.function.name, void 0, this.deps.sessionId);
|
|
26728
26798
|
const toolName = toolToCall.function.name;
|
|
26729
26799
|
let previewContent;
|
|
26730
26800
|
if (toolName === "edit_tool") {
|
|
26731
26801
|
const args = JSON.parse(toolToCall.function.arguments);
|
|
26732
|
-
previewContent = await this.
|
|
26802
|
+
previewContent = await this.deps.generateEditPreview(args);
|
|
26733
26803
|
} else if (toolName === "file_write") {
|
|
26734
26804
|
const args = JSON.parse(toolToCall.function.arguments);
|
|
26735
|
-
previewContent = this.
|
|
26805
|
+
previewContent = this.deps.generateFileWritePreview(args);
|
|
26736
26806
|
}
|
|
26737
|
-
this.eventBus.emit("backend_message", {
|
|
26807
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26738
26808
|
type: "confirmation_request",
|
|
26739
26809
|
tool_calls: confirmationToolCalls,
|
|
26740
26810
|
preview: previewContent,
|
|
@@ -26743,90 +26813,477 @@ ${editData.error.display}`;
|
|
|
26743
26813
|
}
|
|
26744
26814
|
} else if (trimmedText) {
|
|
26745
26815
|
this.emptyAssistantReplySteps = 0;
|
|
26746
|
-
this.
|
|
26747
|
-
|
|
26748
|
-
this.
|
|
26816
|
+
this.invalidToolCallRetrySteps = 0;
|
|
26817
|
+
this.deps.eventBus.emit("backend_message", { type: "assistant_message", content: accumulatedContent });
|
|
26818
|
+
await this.deps.notifyFactorTurnEndIfNeeded("assistant_text_without_tool_call");
|
|
26819
|
+
this.deps.emitTurnCompleted();
|
|
26749
26820
|
return;
|
|
26750
26821
|
} else {
|
|
26751
26822
|
await this.continueAfterEmptyAssistantResponse();
|
|
26752
26823
|
}
|
|
26753
26824
|
}
|
|
26754
|
-
async
|
|
26755
|
-
const response = await this.llm.chatCompletion({
|
|
26756
|
-
messages: contextWindow,
|
|
26757
|
-
temperature: 0,
|
|
26758
|
-
tools: this.mcpClient.getAvailableTools(),
|
|
26759
|
-
parallel_tool_calls: true,
|
|
26760
|
-
userContext: this.getLlmUserContext()
|
|
26761
|
-
});
|
|
26762
|
-
if (this.isInterrupted) {
|
|
26763
|
-
this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
|
|
26764
|
-
return;
|
|
26765
|
-
}
|
|
26766
|
-
let message2 = response.choices[0].message;
|
|
26767
|
-
message2 = ToolCallNormalizer.normalizeAssistantMessage(message2);
|
|
26768
|
-
if (message2.reasoning_content || message2.reasoning) {
|
|
26769
|
-
const reasoningText = message2.reasoning_content || message2.reasoning;
|
|
26770
|
-
this.eventBus.emit("backend_message", {
|
|
26771
|
-
type: "reasoning",
|
|
26772
|
-
content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
|
|
26773
|
-
});
|
|
26774
|
-
}
|
|
26775
|
-
this.
|
|
26776
|
-
|
|
26777
|
-
|
|
26778
|
-
this.
|
|
26779
|
-
|
|
26780
|
-
|
|
26781
|
-
|
|
26782
|
-
)
|
|
26783
|
-
|
|
26784
|
-
|
|
26785
|
-
|
|
26825
|
+
async handleNonStreamingResponse(contextWindow) {
|
|
26826
|
+
const response = await this.deps.llm.chatCompletion({
|
|
26827
|
+
messages: contextWindow,
|
|
26828
|
+
temperature: 0,
|
|
26829
|
+
tools: this.deps.mcpClient.getAvailableTools(),
|
|
26830
|
+
parallel_tool_calls: true,
|
|
26831
|
+
userContext: this.deps.getLlmUserContext()
|
|
26832
|
+
});
|
|
26833
|
+
if (this.deps.isInterrupted()) {
|
|
26834
|
+
this.deps.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
|
|
26835
|
+
return;
|
|
26836
|
+
}
|
|
26837
|
+
let message2 = response.choices[0].message;
|
|
26838
|
+
message2 = ToolCallNormalizer.normalizeAssistantMessage(message2);
|
|
26839
|
+
if (message2.reasoning_content || message2.reasoning) {
|
|
26840
|
+
const reasoningText = message2.reasoning_content || message2.reasoning;
|
|
26841
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26842
|
+
type: "reasoning",
|
|
26843
|
+
content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
|
|
26844
|
+
});
|
|
26845
|
+
}
|
|
26846
|
+
this.deps.history.push(message2);
|
|
26847
|
+
if (message2.tool_calls && message2.tool_calls.length > 0) {
|
|
26848
|
+
this.emptyAssistantReplySteps = 0;
|
|
26849
|
+
this.invalidToolCallRetrySteps = 0;
|
|
26850
|
+
const validToolCalls = message2.tool_calls.filter(
|
|
26851
|
+
(call) => ToolCallNormalizer.isValidToolCall(call)
|
|
26852
|
+
);
|
|
26853
|
+
if (validToolCalls.length === 0) {
|
|
26854
|
+
await this.handleInvalidToolCallRetry(message2);
|
|
26855
|
+
return;
|
|
26856
|
+
}
|
|
26857
|
+
const autoApprovedToolCalls = validToolCalls.filter(
|
|
26858
|
+
(tc) => effectiveToolAutoApprove(tc, this.deps.sessionId)
|
|
26859
|
+
);
|
|
26860
|
+
const confirmationToolCalls = validToolCalls.filter(
|
|
26861
|
+
(tc) => !effectiveToolAutoApprove(tc, this.deps.sessionId)
|
|
26862
|
+
);
|
|
26863
|
+
if (autoApprovedToolCalls.length > 0) {
|
|
26864
|
+
await this.handleToolResponse({
|
|
26865
|
+
type: "user_decision_execute",
|
|
26866
|
+
tool_calls: autoApprovedToolCalls,
|
|
26867
|
+
continueConversation: confirmationToolCalls.length === 0
|
|
26868
|
+
});
|
|
26869
|
+
}
|
|
26870
|
+
if (confirmationToolCalls.length > 0) {
|
|
26871
|
+
const toolToCall = confirmationToolCalls[0];
|
|
26872
|
+
const decision = decideToolExecution(toolToCall.function.name, void 0, this.deps.sessionId);
|
|
26873
|
+
const toolName = toolToCall.function.name;
|
|
26874
|
+
let previewContent;
|
|
26875
|
+
if (toolName === "edit_tool") {
|
|
26876
|
+
const args = JSON.parse(toolToCall.function.arguments);
|
|
26877
|
+
previewContent = await this.deps.generateEditPreview(args);
|
|
26878
|
+
} else if (toolName === "file_write") {
|
|
26879
|
+
const args = JSON.parse(toolToCall.function.arguments);
|
|
26880
|
+
previewContent = this.deps.generateFileWritePreview(args);
|
|
26881
|
+
}
|
|
26882
|
+
this.deps.eventBus.emit("backend_message", {
|
|
26883
|
+
type: "confirmation_request",
|
|
26884
|
+
tool_calls: confirmationToolCalls,
|
|
26885
|
+
preview: previewContent,
|
|
26886
|
+
tool_policy: decision
|
|
26887
|
+
});
|
|
26888
|
+
}
|
|
26889
|
+
} else if (typeof message2.content === "string" && message2.content.trim()) {
|
|
26890
|
+
this.emptyAssistantReplySteps = 0;
|
|
26891
|
+
this.invalidToolCallRetrySteps = 0;
|
|
26892
|
+
this.deps.eventBus.emit("backend_message", { type: "assistant_message", content: message2.content });
|
|
26893
|
+
await this.deps.notifyFactorTurnEndIfNeeded("assistant_text_without_tool_call");
|
|
26894
|
+
this.deps.emitTurnCompleted();
|
|
26895
|
+
return;
|
|
26896
|
+
} else {
|
|
26897
|
+
await this.continueAfterEmptyAssistantResponse();
|
|
26898
|
+
}
|
|
26899
|
+
}
|
|
26900
|
+
async pollWorkerMailbox() {
|
|
26901
|
+
if (!process.env.BLUMA_PARENT_SESSION_ID) {
|
|
26902
|
+
return null;
|
|
26903
|
+
}
|
|
26904
|
+
try {
|
|
26905
|
+
const { pollMailbox: pollMailbox2 } = await import("../../tools/natives/poll_mailbox.js");
|
|
26906
|
+
const result = await pollMailbox2({
|
|
26907
|
+
timeout: 0,
|
|
26908
|
+
pollInterval: 500,
|
|
26909
|
+
types: ["follow_up", "cancel_request", "permission_response"],
|
|
26910
|
+
includeSignals: false
|
|
26911
|
+
});
|
|
26912
|
+
if (!result.success || !result.hasNewMessages) {
|
|
26913
|
+
return null;
|
|
26914
|
+
}
|
|
26915
|
+
return {
|
|
26916
|
+
followUp: result.followUp ? {
|
|
26917
|
+
message: result.followUp.message,
|
|
26918
|
+
priority: result.followUp.priority
|
|
26919
|
+
} : void 0,
|
|
26920
|
+
cancelRequested: result.cancelRequested
|
|
26921
|
+
};
|
|
26922
|
+
} catch {
|
|
26923
|
+
return null;
|
|
26924
|
+
}
|
|
26925
|
+
}
|
|
26926
|
+
};
|
|
26927
|
+
function summarizeHistoryForLog(messages, limit = 8) {
|
|
26928
|
+
return messages.slice(0, limit).map((message2) => {
|
|
26929
|
+
const content = message2?.content;
|
|
26930
|
+
const contentType = Array.isArray(content) ? "array" : typeof content;
|
|
26931
|
+
const preview = typeof content === "string" ? content.slice(0, 120) : Array.isArray(content) ? content.filter((part) => part && typeof part === "object" && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(" ").slice(0, 120) : void 0;
|
|
26932
|
+
return {
|
|
26933
|
+
role: message2?.role,
|
|
26934
|
+
name: message2?.name ?? null,
|
|
26935
|
+
content_type: contentType,
|
|
26936
|
+
preview: preview || null,
|
|
26937
|
+
has_tool_calls: Array.isArray(message2?.tool_calls) && message2.tool_calls.length > 0,
|
|
26938
|
+
tool_call_id: message2?.tool_call_id ?? null
|
|
26939
|
+
};
|
|
26940
|
+
});
|
|
26941
|
+
}
|
|
26942
|
+
|
|
26943
|
+
// src/app/agent/bluma/core/bluma.ts
|
|
26944
|
+
var BluMaAgent = class {
|
|
26945
|
+
llm;
|
|
26946
|
+
sessionId;
|
|
26947
|
+
sessionFile = "";
|
|
26948
|
+
history = [];
|
|
26949
|
+
eventBus;
|
|
26950
|
+
mcpClient;
|
|
26951
|
+
feedbackSystem;
|
|
26952
|
+
skillLoader;
|
|
26953
|
+
compressor;
|
|
26954
|
+
toolRunner;
|
|
26955
|
+
turnCoordinator;
|
|
26956
|
+
isInterrupted = false;
|
|
26957
|
+
/** Mesmo turnId durante processTurn + todo o loop de tool_calls (FactorRouter). */
|
|
26958
|
+
activeTurnContext = null;
|
|
26959
|
+
/** Evita POST /turns/.../end duplicado no mesmo turno (ex.: Esc após message_result). */
|
|
26960
|
+
factorRouterTurnClosed = false;
|
|
26961
|
+
constructor(sessionId, eventBus, llm, mcpClient, feedbackSystem) {
|
|
26962
|
+
this.sessionId = sessionId;
|
|
26963
|
+
this.eventBus = eventBus;
|
|
26964
|
+
this.llm = llm;
|
|
26965
|
+
this.mcpClient = mcpClient;
|
|
26966
|
+
this.feedbackSystem = feedbackSystem;
|
|
26967
|
+
this.skillLoader = new SkillLoader(process.cwd());
|
|
26968
|
+
this.compressor = new HistoryCompressor();
|
|
26969
|
+
this.toolRunner = new BluMaToolRunner({
|
|
26970
|
+
eventBus: this.eventBus,
|
|
26971
|
+
history: this.history,
|
|
26972
|
+
sessionId: this.sessionId,
|
|
26973
|
+
mcpClient: this.mcpClient,
|
|
26974
|
+
isInterrupted: () => this.isInterrupted,
|
|
26975
|
+
persistSession: () => this.persistSession(),
|
|
26976
|
+
notifyFactorTurnEndIfNeeded: (reason) => this.notifyFactorTurnEndIfNeeded(reason),
|
|
26977
|
+
emitTurnCompleted: () => this.emitTurnCompleted(),
|
|
26978
|
+
buildToolPolicyForUi: (toolName, toolArgs) => this.buildToolPolicyForUi(toolName, toolArgs),
|
|
26979
|
+
generateEditPreview: (toolArgs) => this._generateEditPreview(toolArgs),
|
|
26980
|
+
generateFileWritePreview: (toolArgs) => this._generateFileWritePreview(toolArgs)
|
|
26981
|
+
});
|
|
26982
|
+
this.turnCoordinator = new BluMaTurnCoordinator({
|
|
26983
|
+
eventBus: this.eventBus,
|
|
26984
|
+
history: this.history,
|
|
26985
|
+
sessionId: this.sessionId,
|
|
26986
|
+
llm: this.llm,
|
|
26987
|
+
mcpClient: this.mcpClient,
|
|
26988
|
+
compressor: this.compressor,
|
|
26989
|
+
toolRunner: this.toolRunner,
|
|
26990
|
+
isInterrupted: () => this.isInterrupted,
|
|
26991
|
+
getLlmUserContext: () => this.getLlmUserContext(),
|
|
26992
|
+
persistSession: () => this.persistSession(),
|
|
26993
|
+
persistSessionSync: () => this.persistSessionSync(),
|
|
26994
|
+
notifyFactorTurnEndIfNeeded: (reason) => this.notifyFactorTurnEndIfNeeded(reason),
|
|
26995
|
+
emitTurnCompleted: () => this.emitTurnCompleted(),
|
|
26996
|
+
generateEditPreview: (toolArgs) => this._generateEditPreview(toolArgs),
|
|
26997
|
+
generateFileWritePreview: (toolArgs) => this._generateFileWritePreview(toolArgs)
|
|
26998
|
+
});
|
|
26999
|
+
this.eventBus.on("user_interrupt", async () => {
|
|
27000
|
+
this.isInterrupted = true;
|
|
27001
|
+
await this.notifyFactorTurnEndIfNeeded("user_interrupt");
|
|
27002
|
+
this.eventBus.emit("backend_message", { type: "done", status: "interrupted" });
|
|
27003
|
+
});
|
|
27004
|
+
this.eventBus.on("user_overlay", async (data) => {
|
|
27005
|
+
const clean = String(data.payload ?? "").trim();
|
|
27006
|
+
this.history.push({ role: "user", content: clean });
|
|
27007
|
+
this.eventBus.emit("backend_message", { type: "user_overlay", payload: clean, ts: data.ts || Date.now() });
|
|
27008
|
+
try {
|
|
27009
|
+
if (this.sessionFile) {
|
|
27010
|
+
this.persistSession();
|
|
27011
|
+
} else {
|
|
27012
|
+
const [sessionFile, , mem] = await loadOrcreateSession(this.sessionId);
|
|
27013
|
+
this.sessionFile = sessionFile;
|
|
27014
|
+
this.compressor.restoreSnapshot(mem);
|
|
27015
|
+
this.persistSession();
|
|
27016
|
+
}
|
|
27017
|
+
} catch (e) {
|
|
27018
|
+
this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s user_overlay: ${e.message}` });
|
|
27019
|
+
}
|
|
27020
|
+
});
|
|
27021
|
+
}
|
|
27022
|
+
getMemorySnapshot() {
|
|
27023
|
+
return this.compressor.getSnapshot();
|
|
27024
|
+
}
|
|
27025
|
+
/**
|
|
27026
|
+
* Salvar histórico de forma SÍNCRONA - usado em situações críticas (erros, shutdown)
|
|
27027
|
+
* para garantir que nada se perde. NÃO usa debounce.
|
|
27028
|
+
*/
|
|
27029
|
+
persistSessionSync() {
|
|
27030
|
+
if (!this.sessionFile) return;
|
|
27031
|
+
try {
|
|
27032
|
+
const sessionData = {
|
|
27033
|
+
session_id: path40.basename(this.sessionFile, ".json"),
|
|
27034
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27035
|
+
conversation_history: this.history,
|
|
27036
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27037
|
+
...this.compressor.getSnapshot()
|
|
27038
|
+
};
|
|
27039
|
+
fs39.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
27040
|
+
} catch (error) {
|
|
27041
|
+
console.error("[Bluma] Failed to persist session synchronously:", error);
|
|
27042
|
+
}
|
|
27043
|
+
}
|
|
27044
|
+
/** Debounced: grava histórico + estado de compressão no mesmo ficheiro de sessão. */
|
|
27045
|
+
persistSession() {
|
|
27046
|
+
if (!this.sessionFile) return;
|
|
27047
|
+
void saveSessionHistory(this.sessionFile, this.history, this.getMemorySnapshot());
|
|
27048
|
+
}
|
|
27049
|
+
recordUiSlashCommand(command, mode = "visible_only") {
|
|
27050
|
+
const text = String(command ?? "").trim();
|
|
27051
|
+
if (!text) return;
|
|
27052
|
+
this.history.push({
|
|
27053
|
+
role: "user",
|
|
27054
|
+
name: mode === "with_internal_prompt" ? "ui_slash_command" : "ui_slash_command_local",
|
|
27055
|
+
content: text
|
|
27056
|
+
});
|
|
27057
|
+
this.persistSession();
|
|
27058
|
+
}
|
|
27059
|
+
async initialize() {
|
|
27060
|
+
await this.mcpClient.nativeToolInvoker.initialize();
|
|
27061
|
+
await this.mcpClient.initialize();
|
|
27062
|
+
const [sessionFile, history, mem] = await loadOrcreateSession(this.sessionId);
|
|
27063
|
+
this.sessionFile = sessionFile;
|
|
27064
|
+
this.history.splice(0, this.history.length, ...history);
|
|
27065
|
+
this.compressor.restoreSnapshot(mem);
|
|
27066
|
+
initializeSkillContext({
|
|
27067
|
+
history: this.history,
|
|
27068
|
+
skillLoader: this.skillLoader
|
|
27069
|
+
});
|
|
27070
|
+
registerSessionCronBridge({
|
|
27071
|
+
eventBus: this.eventBus,
|
|
27072
|
+
getSessionId: () => this.sessionId
|
|
27073
|
+
});
|
|
27074
|
+
const dirs = this.skillLoader.getSkillsDirs();
|
|
27075
|
+
this.eventBus.emit("backend_message", {
|
|
27076
|
+
type: "info",
|
|
27077
|
+
message: `Skills dirs \u2014 bundled: ${dirs.bundled} | project: ${dirs.project} | global: ${dirs.global}`
|
|
27078
|
+
});
|
|
27079
|
+
const availableSkills = this.skillLoader.listAvailable();
|
|
27080
|
+
this.eventBus.emit("backend_message", {
|
|
27081
|
+
type: "info",
|
|
27082
|
+
message: `Skills loaded: ${availableSkills.length} \u2014 ${availableSkills.map((s) => `${s.name} (${s.source})`).join(", ") || "none"}`
|
|
27083
|
+
});
|
|
27084
|
+
if (this.skillLoader.hasConflicts()) {
|
|
27085
|
+
for (const warning of this.skillLoader.formatConflictWarnings()) {
|
|
27086
|
+
this.eventBus.emit("backend_message", {
|
|
27087
|
+
type: "warning",
|
|
27088
|
+
message: warning
|
|
27089
|
+
});
|
|
27090
|
+
}
|
|
27091
|
+
}
|
|
27092
|
+
const toolsDetailed = this.mcpClient.getAvailableToolsDetailed();
|
|
27093
|
+
const availableToolNames = toolsDetailed.map((t) => t.function?.name).filter(Boolean);
|
|
27094
|
+
const systemPrompt = await getUnifiedSystemPrompt(availableSkills, {
|
|
27095
|
+
availableToolNames,
|
|
27096
|
+
toolsDetailed,
|
|
27097
|
+
mcpClient: this.mcpClient,
|
|
27098
|
+
sessionId: this.sessionId
|
|
27099
|
+
});
|
|
27100
|
+
if (this.history.length === 0) {
|
|
27101
|
+
this.history.push({ role: "system", content: systemPrompt });
|
|
27102
|
+
} else {
|
|
27103
|
+
const sysIdx = this.history.findIndex(
|
|
27104
|
+
(m) => m.role === "system" && typeof m.content === "string" && String(m.content).includes("<identity>")
|
|
27105
|
+
);
|
|
27106
|
+
if (sysIdx >= 0) {
|
|
27107
|
+
this.history[sysIdx] = { ...this.history[sysIdx], content: systemPrompt };
|
|
27108
|
+
} else {
|
|
27109
|
+
this.history.unshift({ role: "system", content: systemPrompt });
|
|
27110
|
+
}
|
|
27111
|
+
}
|
|
27112
|
+
this.persistSession();
|
|
27113
|
+
const uiHistory = this.history.filter(
|
|
27114
|
+
(m) => m.role !== "system"
|
|
27115
|
+
);
|
|
27116
|
+
if (uiHistory.length > 0) {
|
|
27117
|
+
this.eventBus.emit("backend_message", {
|
|
27118
|
+
type: "session_history",
|
|
27119
|
+
messages: uiHistory
|
|
27120
|
+
});
|
|
27121
|
+
}
|
|
27122
|
+
}
|
|
27123
|
+
getAvailableTools() {
|
|
27124
|
+
return this.mcpClient.getAvailableTools();
|
|
27125
|
+
}
|
|
27126
|
+
getUiToolsDetailed() {
|
|
27127
|
+
return this.mcpClient.getAvailableToolsDetailed();
|
|
27128
|
+
}
|
|
27129
|
+
listAvailableSkills() {
|
|
27130
|
+
return this.skillLoader.listAvailable();
|
|
27131
|
+
}
|
|
27132
|
+
getSkillsDirs() {
|
|
27133
|
+
return this.skillLoader.getSkillsDirs();
|
|
27134
|
+
}
|
|
27135
|
+
getSkillConflictWarnings() {
|
|
27136
|
+
return this.skillLoader.formatConflictWarnings();
|
|
27137
|
+
}
|
|
27138
|
+
getFeedbackScore() {
|
|
27139
|
+
return this.feedbackSystem.getCumulativeScore();
|
|
27140
|
+
}
|
|
27141
|
+
/**
|
|
27142
|
+
* Contrato de ciclo de turno (UI, hooks, Factor Router):
|
|
27143
|
+
*
|
|
27144
|
+
* - `backend_message` `{ type: 'turn_start', turnId, sessionId, userPromptPreview }` — início;
|
|
27145
|
+
* payload estável em {@link buildTurnStartBackendMessage}.
|
|
27146
|
+
* - Stream: `stream_start` / `stream_reasoning_chunk` / `stream_chunk` / `stream_end`.
|
|
27147
|
+
* Se a resposta incluir tool `message`, `stream_end` leva `{ omitAssistantFlush: true }` para não
|
|
27148
|
+
* duplicar texto no histórico (o utilizador vê o `tool_result` com gutter). Ver `applyStreamEndFlush`.
|
|
27149
|
+
* - Fim: `backend_message` `{ type: 'done', status }` (`completed` | `failed` | `interrupted` | …).
|
|
27150
|
+
* - Factor Router: um POST `/turns/{turnId}/end` por turno via `notifyFactorTurnEndIfNeeded` (razões abaixo).
|
|
27151
|
+
*
|
|
27152
|
+
* Smoke manual sugerido: mensagem normal com `message`+`result`; Ctrl+C; erro LLM; loop sem resposta útil.
|
|
27153
|
+
*/
|
|
27154
|
+
async processTurn(userInput, userContextInput, options) {
|
|
27155
|
+
this.isInterrupted = false;
|
|
27156
|
+
this.factorRouterTurnClosed = false;
|
|
27157
|
+
this.turnCoordinator.resetTurnState();
|
|
27158
|
+
const inputText = String(userInput.content || "").trim();
|
|
27159
|
+
const turnId = uuidv48();
|
|
27160
|
+
this.activeTurnContext = {
|
|
27161
|
+
...userContextInput,
|
|
27162
|
+
turnId,
|
|
27163
|
+
sessionId: userContextInput.sessionId || this.sessionId
|
|
27164
|
+
};
|
|
27165
|
+
process.env.BLUMA_USER_CONTEXT_JSON = JSON.stringify({
|
|
27166
|
+
conversationId: this.activeTurnContext.conversationId ?? null,
|
|
27167
|
+
userId: this.activeTurnContext.userId ?? null,
|
|
27168
|
+
userName: this.activeTurnContext.userName ?? null,
|
|
27169
|
+
userEmail: this.activeTurnContext.userEmail ?? null,
|
|
27170
|
+
companyId: this.activeTurnContext.companyId ?? null,
|
|
27171
|
+
companyName: this.activeTurnContext.companyName ?? null
|
|
27172
|
+
});
|
|
27173
|
+
const userContent = buildUserMessageContent(inputText, process.cwd());
|
|
27174
|
+
this.history.push({
|
|
27175
|
+
role: "user",
|
|
27176
|
+
content: userContent,
|
|
27177
|
+
...options?.historyName ? { name: options.historyName } : {}
|
|
27178
|
+
});
|
|
27179
|
+
this.eventBus.emit(
|
|
27180
|
+
"backend_message",
|
|
27181
|
+
buildTurnStartBackendMessage({
|
|
27182
|
+
turnId: this.activeTurnContext.turnId,
|
|
27183
|
+
sessionId: this.activeTurnContext.sessionId,
|
|
27184
|
+
inputText,
|
|
27185
|
+
appContext: this.activeTurnContext.appContext ?? null
|
|
27186
|
+
})
|
|
27187
|
+
);
|
|
27188
|
+
if (inputText === "/init") {
|
|
27189
|
+
this.eventBus.emit("dispatch", inputText);
|
|
27190
|
+
}
|
|
27191
|
+
await this.turnCoordinator.continueConversation();
|
|
27192
|
+
}
|
|
27193
|
+
/** Política mostrada na UI: combina metadata base com auto-approve efectivo (incl. permission_ml). */
|
|
27194
|
+
buildToolPolicyForUi(toolName, toolArgs) {
|
|
27195
|
+
const base = decideToolExecution(toolName, void 0, this.sessionId);
|
|
27196
|
+
const argsStr = typeof toolArgs === "string" ? toolArgs : JSON.stringify(toolArgs ?? {});
|
|
27197
|
+
const synthetic = { function: { name: toolName, arguments: argsStr } };
|
|
27198
|
+
const auto = effectiveToolAutoApprove(synthetic, this.sessionId, { logClassifier: false });
|
|
27199
|
+
return {
|
|
27200
|
+
...base,
|
|
27201
|
+
autoApprove: auto,
|
|
27202
|
+
requiresConfirmation: !auto
|
|
27203
|
+
};
|
|
27204
|
+
}
|
|
27205
|
+
/**
|
|
27206
|
+
* Executa uma tool aprovada: emit tool_call → invoke → tool_result → histórico.
|
|
27207
|
+
* `backend_message` tool_call / tool_result incluem `tool_call_id` (id do tool call no modelo)
|
|
27208
|
+
* para a UI correlacionar a linha de invocação (•) com o resultado compacto.
|
|
27209
|
+
* @returns shouldContinue — false se `message` com message_type result terminou o turno.
|
|
27210
|
+
*/
|
|
27211
|
+
async executeApprovedToolInvocation(toolCall, decisionData) {
|
|
27212
|
+
return this.toolRunner.executeApprovedToolInvocation(toolCall, decisionData);
|
|
27213
|
+
}
|
|
27214
|
+
/** Várias leituras seguras em paralelo (grep, ls, read_file, …). */
|
|
27215
|
+
async executeParallelReadBatch(batch, _decisionData) {
|
|
27216
|
+
return this.toolRunner.executeParallelReadBatch(batch, _decisionData);
|
|
27217
|
+
}
|
|
27218
|
+
async handleToolResponse(decisionData) {
|
|
27219
|
+
return this.turnCoordinator.handleToolResponse(decisionData);
|
|
27220
|
+
}
|
|
27221
|
+
async _generateEditPreview(toolArgs) {
|
|
27222
|
+
try {
|
|
27223
|
+
if (Array.isArray(toolArgs.edits) && toolArgs.edits.length > 0) {
|
|
27224
|
+
return previewEditBatch(toolArgs.edits);
|
|
26786
27225
|
}
|
|
26787
|
-
|
|
26788
|
-
|
|
26789
|
-
|
|
26790
|
-
const
|
|
26791
|
-
|
|
27226
|
+
if (!toolArgs.file_path) {
|
|
27227
|
+
return "Error: file_path is required";
|
|
27228
|
+
}
|
|
27229
|
+
const editData = await calculateEdit(
|
|
27230
|
+
toolArgs.file_path,
|
|
27231
|
+
toolArgs.old_string || "",
|
|
27232
|
+
toolArgs.new_string || "",
|
|
27233
|
+
toolArgs.expected_replacements || 1
|
|
26792
27234
|
);
|
|
26793
|
-
if (
|
|
26794
|
-
|
|
26795
|
-
|
|
26796
|
-
|
|
26797
|
-
continueConversation: confirmationToolCalls.length === 0
|
|
26798
|
-
});
|
|
27235
|
+
if (editData.error) {
|
|
27236
|
+
return `Failed to generate diff:
|
|
27237
|
+
|
|
27238
|
+
${editData.error.display}`;
|
|
26799
27239
|
}
|
|
26800
|
-
|
|
26801
|
-
|
|
26802
|
-
|
|
26803
|
-
|
|
26804
|
-
|
|
26805
|
-
|
|
26806
|
-
|
|
26807
|
-
|
|
26808
|
-
|
|
26809
|
-
|
|
26810
|
-
|
|
26811
|
-
}
|
|
26812
|
-
this.eventBus.emit("backend_message", {
|
|
26813
|
-
type: "confirmation_request",
|
|
26814
|
-
tool_calls: confirmationToolCalls,
|
|
26815
|
-
preview: previewContent,
|
|
26816
|
-
tool_policy: decision
|
|
26817
|
-
});
|
|
27240
|
+
const filename = path40.basename(toolArgs.file_path);
|
|
27241
|
+
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
27242
|
+
} catch (e) {
|
|
27243
|
+
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
27244
|
+
}
|
|
27245
|
+
}
|
|
27246
|
+
_generateFileWritePreview(toolArgs) {
|
|
27247
|
+
try {
|
|
27248
|
+
const content = typeof toolArgs?.content === "string" ? toolArgs.content : String(toolArgs?.content ?? "");
|
|
27249
|
+
if (!content) {
|
|
27250
|
+
return "(No content)";
|
|
26818
27251
|
}
|
|
26819
|
-
|
|
26820
|
-
|
|
26821
|
-
|
|
26822
|
-
|
|
26823
|
-
|
|
26824
|
-
|
|
26825
|
-
|
|
26826
|
-
}
|
|
26827
|
-
|
|
27252
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
27253
|
+
const lines = normalized.split("\n");
|
|
27254
|
+
const maxPreviewLines = 20;
|
|
27255
|
+
const visibleLines = lines.slice(0, maxPreviewLines);
|
|
27256
|
+
const hiddenLines = lines.length - visibleLines.length;
|
|
27257
|
+
return hiddenLines > 0 ? `${visibleLines.join("\n")}
|
|
27258
|
+
\u2026 +${hiddenLines} more lines` : normalized;
|
|
27259
|
+
} catch (e) {
|
|
27260
|
+
return `An unexpected error occurred while generating the file write preview: ${e.message}`;
|
|
26828
27261
|
}
|
|
26829
27262
|
}
|
|
27263
|
+
getLlmUserContext() {
|
|
27264
|
+
if (!this.activeTurnContext) {
|
|
27265
|
+
throw new Error("BluMaAgent: activeTurnContext ausente (processTurn n\xE3o iniciou o turno).");
|
|
27266
|
+
}
|
|
27267
|
+
return this.activeTurnContext;
|
|
27268
|
+
}
|
|
27269
|
+
/**
|
|
27270
|
+
* Um único POST ao Factor Router por turno (`notifyFactorRouterTurnEnd`).
|
|
27271
|
+
* `reason` típicos: `message_result`, `user_interrupt`, `empty_reply_exhausted`, `llm_error`.
|
|
27272
|
+
*/
|
|
27273
|
+
async notifyFactorTurnEndIfNeeded(reason) {
|
|
27274
|
+
if (this.factorRouterTurnClosed || !this.activeTurnContext) return;
|
|
27275
|
+
this.factorRouterTurnClosed = true;
|
|
27276
|
+
const ctx = this.activeTurnContext;
|
|
27277
|
+
await notifyFactorRouterTurnEnd({
|
|
27278
|
+
turnId: ctx.turnId,
|
|
27279
|
+
userContext: ctx,
|
|
27280
|
+
reason
|
|
27281
|
+
});
|
|
27282
|
+
}
|
|
27283
|
+
/** Fecho explícito de turno (útil para workers antes de process.exit). */
|
|
27284
|
+
async closeActiveTurn(reason = "worker_exit") {
|
|
27285
|
+
await this.notifyFactorTurnEndIfNeeded(reason);
|
|
27286
|
+
}
|
|
26830
27287
|
emitTurnCompleted() {
|
|
26831
27288
|
this.eventBus.emit("backend_message", { type: "done", status: "completed" });
|
|
26832
27289
|
void this.runAutoDreamIfEnabled();
|
|
@@ -26838,37 +27295,6 @@ ${editData.error.display}`;
|
|
|
26838
27295
|
} catch {
|
|
26839
27296
|
}
|
|
26840
27297
|
}
|
|
26841
|
-
/**
|
|
26842
|
-
* WORKER: Poll mailbox para checkar follow-ups do coordinator
|
|
26843
|
-
* Retorna null se não há novas mensagens ou se não é um worker
|
|
26844
|
-
*/
|
|
26845
|
-
async _pollWorkerMailbox() {
|
|
26846
|
-
if (!process.env.BLUMA_PARENT_SESSION_ID) {
|
|
26847
|
-
return null;
|
|
26848
|
-
}
|
|
26849
|
-
try {
|
|
26850
|
-
const { pollMailbox: pollMailbox2 } = await import("../../tools/natives/poll_mailbox.js");
|
|
26851
|
-
const result = await pollMailbox2({
|
|
26852
|
-
timeout: 0,
|
|
26853
|
-
// Non-blocking
|
|
26854
|
-
pollInterval: 500,
|
|
26855
|
-
types: ["follow_up", "cancel_request", "permission_response"],
|
|
26856
|
-
includeSignals: false
|
|
26857
|
-
});
|
|
26858
|
-
if (!result.success || !result.hasNewMessages) {
|
|
26859
|
-
return null;
|
|
26860
|
-
}
|
|
26861
|
-
return {
|
|
26862
|
-
followUp: result.followUp ? {
|
|
26863
|
-
message: result.followUp.message,
|
|
26864
|
-
priority: result.followUp.priority
|
|
26865
|
-
} : void 0,
|
|
26866
|
-
cancelRequested: result.cancelRequested
|
|
26867
|
-
};
|
|
26868
|
-
} catch (error) {
|
|
26869
|
-
return null;
|
|
26870
|
-
}
|
|
26871
|
-
}
|
|
26872
27298
|
};
|
|
26873
27299
|
|
|
26874
27300
|
// src/app/agent/subagents/registry.ts
|
|
@@ -28482,7 +28908,7 @@ function ChatUserImageBlock({
|
|
|
28482
28908
|
if (imageCount < 1) return null;
|
|
28483
28909
|
const cap = caption?.trim() ?? "";
|
|
28484
28910
|
const capLines = cap.length > 0 ? cap.split("\n") : [];
|
|
28485
|
-
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column",
|
|
28911
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
|
|
28486
28912
|
Array.from({ length: imageCount }, (_, i) => /* @__PURE__ */ jsx11(Text, { color: BLUMA_TERMINAL.blue, children: `[IMAGE #${i + 1}]` }, `img-${i}`)),
|
|
28487
28913
|
capLines.map((line, i) => /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "row", flexWrap: "wrap", children: [
|
|
28488
28914
|
/* @__PURE__ */ jsx11(Text, { color: BLUMA_TERMINAL.subtle, children: i === 0 ? " L " : " " }),
|
|
@@ -28656,15 +29082,11 @@ var HighlightedCodeComponent = ({ code, filePath, maxLines }) => {
|
|
|
28656
29082
|
const visible = maxLines ? lines.slice(0, maxLines) : lines;
|
|
28657
29083
|
const hidden = lines.length - visible.length;
|
|
28658
29084
|
const gutterWidth = String(lines.length).length;
|
|
28659
|
-
return /* @__PURE__ */ jsxs5(Box_default, { flexDirection: "column", children: [
|
|
29085
|
+
return /* @__PURE__ */ jsxs5(Box_default, { flexDirection: "column", width: "100%", alignItems: "stretch", children: [
|
|
28660
29086
|
visible.map((line, idx) => {
|
|
28661
29087
|
const tokens = tokenizeLine(line, language);
|
|
28662
29088
|
const lineNum = String(idx + 1).padStart(gutterWidth, " ");
|
|
28663
|
-
return /* @__PURE__ */ jsx12(Box_default, { children: /* @__PURE__ */
|
|
28664
|
-
/* @__PURE__ */ jsx12(Text, { color: THEME.lineNumber, children: lineNum }),
|
|
28665
|
-
/* @__PURE__ */ jsx12(Text, { color: THEME.gutter, children: " \u2502 " }),
|
|
28666
|
-
tokens.map((t, ti) => /* @__PURE__ */ jsx12(Text, { color: t.color, children: t.text }, ti))
|
|
28667
|
-
] }) }, idx);
|
|
29089
|
+
return /* @__PURE__ */ jsx12(Box_default, { width: "100%", flexDirection: "row", children: /* @__PURE__ */ jsx12(Text, { children: tokens.map((t, ti) => /* @__PURE__ */ jsx12(Text, { color: t.color, children: t.text }, ti)) }) }, idx);
|
|
28668
29090
|
}),
|
|
28669
29091
|
hidden > 0 && /* @__PURE__ */ jsxs5(Text, { color: THEME.lineNumber, dimColor: true, children: [
|
|
28670
29092
|
"\u2026 +",
|
|
@@ -28735,6 +29157,11 @@ function headingAccent(depth) {
|
|
|
28735
29157
|
if (depth === 3) return BLUMA_TERMINAL.magenta;
|
|
28736
29158
|
return BLUMA_TERMINAL.inactive;
|
|
28737
29159
|
}
|
|
29160
|
+
function normalizeMarkdownText(content) {
|
|
29161
|
+
const trimmed = content.trim();
|
|
29162
|
+
if (!trimmed) return "";
|
|
29163
|
+
return trimmed.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n");
|
|
29164
|
+
}
|
|
28738
29165
|
function listMarker(ordered, index, start, task, checked) {
|
|
28739
29166
|
if (task) return checked ? "[x]" : "[ ]";
|
|
28740
29167
|
if (ordered) {
|
|
@@ -28928,6 +29355,46 @@ function renderParagraph(p, key, compact) {
|
|
|
28928
29355
|
const nodes = walkInline(p.tokens, key);
|
|
28929
29356
|
return /* @__PURE__ */ jsx13(Box_default, { marginBottom: compact ? 0 : 1, flexDirection: "row", flexWrap: "wrap", children: nodes.length > 0 ? nodes : /* @__PURE__ */ jsx13(Text, { color: BLUMA_TERMINAL.muted, wrap: "wrap", children: p.text }) }, key);
|
|
28930
29357
|
}
|
|
29358
|
+
function renderCodeBlock(code, key) {
|
|
29359
|
+
const raw = code.text.replace(/\n$/, "");
|
|
29360
|
+
return /* @__PURE__ */ jsxs6(
|
|
29361
|
+
Box_default,
|
|
29362
|
+
{
|
|
29363
|
+
flexDirection: "column",
|
|
29364
|
+
marginBottom: 1,
|
|
29365
|
+
paddingX: 1,
|
|
29366
|
+
paddingY: 0,
|
|
29367
|
+
backgroundColor: BLUMA_TERMINAL.surfaceContainer,
|
|
29368
|
+
borderStyle: "round",
|
|
29369
|
+
borderColor: BLUMA_TERMINAL.outlineVariant,
|
|
29370
|
+
children: [
|
|
29371
|
+
code.lang ? /* @__PURE__ */ jsx13(Box_default, { marginBottom: 0, children: /* @__PURE__ */ jsx13(Text, { color: BLUMA_TERMINAL.blue, bold: true, children: code.lang }) }) : null,
|
|
29372
|
+
/* @__PURE__ */ jsx13(HighlightedCode, { code: raw, filePath: code.lang || "snippet" })
|
|
29373
|
+
]
|
|
29374
|
+
},
|
|
29375
|
+
key
|
|
29376
|
+
);
|
|
29377
|
+
}
|
|
29378
|
+
function renderBlockquote(q, key) {
|
|
29379
|
+
const inner = q.tokens && q.tokens.length > 0 ? renderBlockTokens(q.tokens, `${key}-inner`) : /* @__PURE__ */ jsx13(Text, { color: BLUMA_TERMINAL.blue, italic: true, wrap: "wrap", children: q.text });
|
|
29380
|
+
return /* @__PURE__ */ jsxs6(
|
|
29381
|
+
Box_default,
|
|
29382
|
+
{
|
|
29383
|
+
flexDirection: "row",
|
|
29384
|
+
marginBottom: 1,
|
|
29385
|
+
alignItems: "flex-start",
|
|
29386
|
+
paddingX: 1,
|
|
29387
|
+
borderStyle: "round",
|
|
29388
|
+
borderColor: BLUMA_TERMINAL.outlineVariant,
|
|
29389
|
+
backgroundColor: BLUMA_TERMINAL.surfaceContainer,
|
|
29390
|
+
children: [
|
|
29391
|
+
/* @__PURE__ */ jsx13(Text, { color: BLUMA_TERMINAL.blue, children: "\u2502 " }),
|
|
29392
|
+
/* @__PURE__ */ jsx13(Box_default, { flexDirection: "column", flexGrow: 1, children: inner })
|
|
29393
|
+
]
|
|
29394
|
+
},
|
|
29395
|
+
key
|
|
29396
|
+
);
|
|
29397
|
+
}
|
|
28931
29398
|
function renderListItemBlocks(item, depth, keyBase) {
|
|
28932
29399
|
if (!item.tokens?.length) {
|
|
28933
29400
|
const nodes = walkInline(
|
|
@@ -28963,13 +29430,6 @@ function renderListBlock(list, depth, keyBase) {
|
|
|
28963
29430
|
] }, `${keyBase}-row-${idx}`);
|
|
28964
29431
|
}) }, keyBase);
|
|
28965
29432
|
}
|
|
28966
|
-
function renderBlockquote(q, key) {
|
|
28967
|
-
const inner = q.tokens && q.tokens.length > 0 ? renderBlockTokens(q.tokens, `${key}-inner`) : /* @__PURE__ */ jsx13(Text, { color: BLUMA_TERMINAL.blue, italic: true, wrap: "wrap", children: q.text });
|
|
28968
|
-
return /* @__PURE__ */ jsxs6(Box_default, { flexDirection: "row", marginBottom: 1, alignItems: "flex-start", children: [
|
|
28969
|
-
/* @__PURE__ */ jsx13(Text, { color: BLUMA_TERMINAL.blue, children: "\u2502 " }),
|
|
28970
|
-
/* @__PURE__ */ jsx13(Box_default, { flexDirection: "column", flexGrow: 1, children: inner })
|
|
28971
|
-
] }, key);
|
|
28972
|
-
}
|
|
28973
29433
|
function renderBlockTokens(tokens, keyRoot) {
|
|
28974
29434
|
const elements = [];
|
|
28975
29435
|
for (let i = 0; i < tokens.length; i++) {
|
|
@@ -28992,17 +29452,9 @@ function renderBlockTokens(tokens, keyRoot) {
|
|
|
28992
29452
|
case "list":
|
|
28993
29453
|
elements.push(renderListBlock(token, 0, key));
|
|
28994
29454
|
break;
|
|
28995
|
-
case "code":
|
|
28996
|
-
|
|
28997
|
-
const raw = code.text.replace(/\n$/, "");
|
|
28998
|
-
elements.push(
|
|
28999
|
-
/* @__PURE__ */ jsxs6(Box_default, { flexDirection: "column", marginBottom: 1, children: [
|
|
29000
|
-
code.lang ? /* @__PURE__ */ jsx13(Text, { color: BLUMA_TERMINAL.blue, wrap: "wrap", children: code.lang }) : null,
|
|
29001
|
-
/* @__PURE__ */ jsx13(HighlightedCode, { code: raw, filePath: code.lang || "snippet" })
|
|
29002
|
-
] }, key)
|
|
29003
|
-
);
|
|
29455
|
+
case "code":
|
|
29456
|
+
elements.push(renderCodeBlock(token, key));
|
|
29004
29457
|
break;
|
|
29005
|
-
}
|
|
29006
29458
|
case "blockquote":
|
|
29007
29459
|
elements.push(renderBlockquote(token, key));
|
|
29008
29460
|
break;
|
|
@@ -29059,7 +29511,8 @@ var MarkdownRendererComponent = ({ markdown }) => {
|
|
|
29059
29511
|
if (!markdown || markdown.trim() === "") {
|
|
29060
29512
|
return null;
|
|
29061
29513
|
}
|
|
29062
|
-
const
|
|
29514
|
+
const normalized = normalizeMarkdownText(markdown);
|
|
29515
|
+
const tokens = cachedLexer(normalized);
|
|
29063
29516
|
return /* @__PURE__ */ jsx13(Box_default, { flexDirection: "column", paddingRight: 1, children: renderBlockTokens(tokens, "md") });
|
|
29064
29517
|
};
|
|
29065
29518
|
var MarkdownRenderer = memo4(MarkdownRendererComponent, (prevProps, nextProps) => {
|
|
@@ -30618,9 +31071,12 @@ import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
|
30618
31071
|
function renderToolHeader6({ args }) {
|
|
30619
31072
|
const cmd = args?.command ?? "";
|
|
30620
31073
|
const displayCmd = cmd.length > 50 ? cmd.substring(0, 50) + "\u2026" : cmd;
|
|
30621
|
-
return /* @__PURE__ */ jsxs20(Box_default, { flexDirection: "row",
|
|
31074
|
+
return /* @__PURE__ */ jsxs20(Box_default, { flexDirection: "row", alignItems: "flex-end", children: [
|
|
30622
31075
|
/* @__PURE__ */ jsx33(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "Ran" }),
|
|
30623
|
-
/* @__PURE__ */
|
|
31076
|
+
/* @__PURE__ */ jsxs20(Text, { color: BLUMA_TERMINAL.dim, children: [
|
|
31077
|
+
" ",
|
|
31078
|
+
displayCmd
|
|
31079
|
+
] })
|
|
30624
31080
|
] });
|
|
30625
31081
|
}
|
|
30626
31082
|
function renderToolUseMessage6({ args }) {
|
|
@@ -31965,6 +32421,46 @@ function getPatchForEdit({
|
|
|
31965
32421
|
}));
|
|
31966
32422
|
return { patch, updatedFile: unescapeFromDiff(updatedFile) };
|
|
31967
32423
|
}
|
|
32424
|
+
function getPatchFromContents({
|
|
32425
|
+
filePath,
|
|
32426
|
+
oldContent,
|
|
32427
|
+
newContent
|
|
32428
|
+
}) {
|
|
32429
|
+
const result = structuredPatch(
|
|
32430
|
+
filePath,
|
|
32431
|
+
filePath,
|
|
32432
|
+
escapeForDiff(oldContent),
|
|
32433
|
+
escapeForDiff(newContent),
|
|
32434
|
+
void 0,
|
|
32435
|
+
void 0,
|
|
32436
|
+
{
|
|
32437
|
+
context: CONTEXT_LINES,
|
|
32438
|
+
timeout: DIFF_TIMEOUT_MS
|
|
32439
|
+
}
|
|
32440
|
+
);
|
|
32441
|
+
if (!result) {
|
|
32442
|
+
return [];
|
|
32443
|
+
}
|
|
32444
|
+
return result.hunks.map((h) => ({
|
|
32445
|
+
...h,
|
|
32446
|
+
lines: h.lines.map(unescapeFromDiff)
|
|
32447
|
+
}));
|
|
32448
|
+
}
|
|
32449
|
+
function patchToUnifiedDiffText(filePath, patch) {
|
|
32450
|
+
if (patch.length === 0) return "";
|
|
32451
|
+
let text = `--- a/${filePath}
|
|
32452
|
+
+++ b/${filePath}
|
|
32453
|
+
`;
|
|
32454
|
+
for (const hunk of patch) {
|
|
32455
|
+
text += `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@
|
|
32456
|
+
`;
|
|
32457
|
+
for (const line of hunk.lines) {
|
|
32458
|
+
text += `${line}
|
|
32459
|
+
`;
|
|
32460
|
+
}
|
|
32461
|
+
}
|
|
32462
|
+
return text;
|
|
32463
|
+
}
|
|
31968
32464
|
|
|
31969
32465
|
// src/app/ui/utils/readEditContext.ts
|
|
31970
32466
|
import { promises as fs41 } from "fs";
|
|
@@ -32064,14 +32560,64 @@ function diffSummary(additions, removals, hidden) {
|
|
|
32064
32560
|
if (hidden > 0) bits.push(`\u22EF ${hidden} hidden`);
|
|
32065
32561
|
return bits.join(" \xB7 ");
|
|
32066
32562
|
}
|
|
32563
|
+
var THEME2 = {
|
|
32564
|
+
keyword: "#569CD6",
|
|
32565
|
+
string: "#CE9178",
|
|
32566
|
+
number: "#B5CEA8",
|
|
32567
|
+
comment: "#6A9955",
|
|
32568
|
+
//#22c55e
|
|
32569
|
+
function: "#DCDCAA",
|
|
32570
|
+
variable: "#9CDCFE",
|
|
32571
|
+
operator: "#D4D4D4",
|
|
32572
|
+
punctuation: "#D4D4D4",
|
|
32573
|
+
default: "#D4D4D4",
|
|
32574
|
+
lineNumber: "#858585",
|
|
32575
|
+
gutter: "#404040"
|
|
32576
|
+
};
|
|
32577
|
+
var KEYWORDS2 = /* @__PURE__ */ new Set([
|
|
32578
|
+
"const",
|
|
32579
|
+
"let",
|
|
32580
|
+
"var",
|
|
32581
|
+
"function",
|
|
32582
|
+
"return",
|
|
32583
|
+
"if",
|
|
32584
|
+
"else",
|
|
32585
|
+
"for",
|
|
32586
|
+
"while",
|
|
32587
|
+
"switch",
|
|
32588
|
+
"case",
|
|
32589
|
+
"break",
|
|
32590
|
+
"continue",
|
|
32591
|
+
"class",
|
|
32592
|
+
"extends",
|
|
32593
|
+
"import",
|
|
32594
|
+
"from",
|
|
32595
|
+
"export",
|
|
32596
|
+
"default",
|
|
32597
|
+
"async",
|
|
32598
|
+
"await",
|
|
32599
|
+
"try",
|
|
32600
|
+
"catch",
|
|
32601
|
+
"finally",
|
|
32602
|
+
"new",
|
|
32603
|
+
"typeof",
|
|
32604
|
+
"instanceof",
|
|
32605
|
+
"null",
|
|
32606
|
+
"undefined",
|
|
32607
|
+
"true",
|
|
32608
|
+
"false",
|
|
32609
|
+
"type",
|
|
32610
|
+
"interface",
|
|
32611
|
+
"enum",
|
|
32612
|
+
"implements",
|
|
32613
|
+
"abstract",
|
|
32614
|
+
"readonly",
|
|
32615
|
+
"override"
|
|
32616
|
+
]);
|
|
32067
32617
|
function hunkToRows(hunk) {
|
|
32068
32618
|
const rows = [];
|
|
32069
32619
|
let oldLine = hunk.oldStart;
|
|
32070
32620
|
let newLine = hunk.newStart;
|
|
32071
|
-
rows.push({
|
|
32072
|
-
kind: "meta",
|
|
32073
|
-
text: `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`
|
|
32074
|
-
});
|
|
32075
32621
|
for (const raw of hunk.lines) {
|
|
32076
32622
|
if (!raw.length) continue;
|
|
32077
32623
|
const prefix = raw[0];
|
|
@@ -32112,22 +32658,22 @@ function pairWordDiffs(rows) {
|
|
|
32112
32658
|
}
|
|
32113
32659
|
return out;
|
|
32114
32660
|
}
|
|
32115
|
-
function
|
|
32116
|
-
let maxO = 0;
|
|
32661
|
+
function calcGutterW(rows) {
|
|
32117
32662
|
let maxN = 0;
|
|
32118
32663
|
for (const r of rows) {
|
|
32119
|
-
if (r.kind === "ctx")
|
|
32120
|
-
|
|
32121
|
-
|
|
32122
|
-
} else if (r.kind === "rem") {
|
|
32123
|
-
maxO = Math.max(maxO, r.old);
|
|
32124
|
-
} else if (r.kind === "add") {
|
|
32125
|
-
maxN = Math.max(maxN, r.new);
|
|
32126
|
-
}
|
|
32664
|
+
if (r.kind === "ctx") maxN = Math.max(maxN, r.old, r.new);
|
|
32665
|
+
else if (r.kind === "rem") maxN = Math.max(maxN, r.old);
|
|
32666
|
+
else if (r.kind === "add") maxN = Math.max(maxN, r.new);
|
|
32127
32667
|
}
|
|
32128
|
-
|
|
32129
|
-
|
|
32130
|
-
|
|
32668
|
+
return Math.max(2, String(maxN || 0).length);
|
|
32669
|
+
}
|
|
32670
|
+
function calcLegacyGutterW(segs) {
|
|
32671
|
+
let maxN = 0;
|
|
32672
|
+
for (const s of segs) {
|
|
32673
|
+
if (s.kind === "rem") maxN = Math.max(maxN, s.oldLine);
|
|
32674
|
+
if (s.kind === "add") maxN = Math.max(maxN, s.newLine);
|
|
32675
|
+
}
|
|
32676
|
+
return Math.max(2, String(maxN || 0).length);
|
|
32131
32677
|
}
|
|
32132
32678
|
function rowLineCounts(rows) {
|
|
32133
32679
|
let additions = 0;
|
|
@@ -32138,21 +32684,11 @@ function rowLineCounts(rows) {
|
|
|
32138
32684
|
}
|
|
32139
32685
|
return { additions, removals };
|
|
32140
32686
|
}
|
|
32141
|
-
function
|
|
32142
|
-
return
|
|
32143
|
-
}
|
|
32144
|
-
function gutterRem(wOld, wNew, oldN) {
|
|
32145
|
-
return `${String(oldN).padStart(wOld)} ${" ".repeat(wNew)} \u2502\u2212`;
|
|
32687
|
+
function gutterLine(w, n) {
|
|
32688
|
+
return ` ${String(n).padEnd(w)} `;
|
|
32146
32689
|
}
|
|
32147
|
-
function
|
|
32148
|
-
return
|
|
32149
|
-
}
|
|
32150
|
-
function gutterOmit(wOld, wNew) {
|
|
32151
|
-
const cols = wOld + 1 + wNew;
|
|
32152
|
-
const dot = "\u22EE";
|
|
32153
|
-
const padL = Math.max(0, Math.floor((cols - dot.length) / 2));
|
|
32154
|
-
const padR = Math.max(0, cols - dot.length - padL);
|
|
32155
|
-
return `${" ".repeat(padL)}${dot}${" ".repeat(padR)} \u2502 `;
|
|
32690
|
+
function gutterOmit(w) {
|
|
32691
|
+
return ` ${"\u22EE".padEnd(w)} `;
|
|
32156
32692
|
}
|
|
32157
32693
|
function WordDiffChunks({
|
|
32158
32694
|
parts,
|
|
@@ -32170,11 +32706,84 @@ function WordDiffChunks({
|
|
|
32170
32706
|
/* @__PURE__ */ jsx41(Text, { backgroundColor: bg, color: BLUMA_TERMINAL.m3OnSurface, wrap: "wrap", children: p.value }, k++)
|
|
32171
32707
|
);
|
|
32172
32708
|
}
|
|
32173
|
-
return /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", children: chunks });
|
|
32709
|
+
return /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", width: "100%", flexGrow: 1, children: chunks });
|
|
32710
|
+
}
|
|
32711
|
+
function collapseContextRows(rows) {
|
|
32712
|
+
const out = [];
|
|
32713
|
+
let ctx = [];
|
|
32714
|
+
const flush = () => {
|
|
32715
|
+
if (ctx.length <= 3) {
|
|
32716
|
+
out.push(...ctx);
|
|
32717
|
+
} else {
|
|
32718
|
+
out.push({ kind: "omit", count: ctx.length });
|
|
32719
|
+
}
|
|
32720
|
+
ctx = [];
|
|
32721
|
+
};
|
|
32722
|
+
for (const r of rows) {
|
|
32723
|
+
if (r.kind === "ctx") {
|
|
32724
|
+
ctx.push(r);
|
|
32725
|
+
continue;
|
|
32726
|
+
}
|
|
32727
|
+
flush();
|
|
32728
|
+
out.push(r);
|
|
32729
|
+
}
|
|
32730
|
+
flush();
|
|
32731
|
+
return out;
|
|
32732
|
+
}
|
|
32733
|
+
function tokenizeLine2(line, _language) {
|
|
32734
|
+
if (!line.trim()) return [{ text: line || " ", color: THEME2.default }];
|
|
32735
|
+
const tokens = [];
|
|
32736
|
+
let i = 0;
|
|
32737
|
+
while (i < line.length) {
|
|
32738
|
+
if (/\s/.test(line[i])) {
|
|
32739
|
+
let spaces = "";
|
|
32740
|
+
while (i < line.length && /\s/.test(line[i])) spaces += line[i++];
|
|
32741
|
+
tokens.push({ text: spaces, color: THEME2.default });
|
|
32742
|
+
continue;
|
|
32743
|
+
}
|
|
32744
|
+
if (line[i] === "/" && line[i + 1] === "/") {
|
|
32745
|
+
tokens.push({ text: line.slice(i), color: THEME2.comment });
|
|
32746
|
+
break;
|
|
32747
|
+
}
|
|
32748
|
+
if (line[i] === "#") {
|
|
32749
|
+
tokens.push({ text: line.slice(i), color: THEME2.comment });
|
|
32750
|
+
break;
|
|
32751
|
+
}
|
|
32752
|
+
if (line[i] === '"' || line[i] === "'" || line[i] === "`") {
|
|
32753
|
+
const quote = line[i];
|
|
32754
|
+
let j = i + 1;
|
|
32755
|
+
while (j < line.length && line[j] !== quote) {
|
|
32756
|
+
if (line[j] === "\\") j++;
|
|
32757
|
+
j++;
|
|
32758
|
+
}
|
|
32759
|
+
tokens.push({ text: line.slice(i, j + 1), color: THEME2.string });
|
|
32760
|
+
i = j + 1;
|
|
32761
|
+
continue;
|
|
32762
|
+
}
|
|
32763
|
+
if (/\d/.test(line[i])) {
|
|
32764
|
+
let num = "";
|
|
32765
|
+
while (i < line.length && /[\d.]/.test(line[i])) num += line[i++];
|
|
32766
|
+
tokens.push({ text: num, color: THEME2.number });
|
|
32767
|
+
continue;
|
|
32768
|
+
}
|
|
32769
|
+
if (/[a-zA-Z_$]/.test(line[i])) {
|
|
32770
|
+
let word = "";
|
|
32771
|
+
while (i < line.length && /[\w$]/.test(line[i])) word += line[i++];
|
|
32772
|
+
const isCall = line[i] === "(";
|
|
32773
|
+
let color = THEME2.variable;
|
|
32774
|
+
if (KEYWORDS2.has(word)) color = THEME2.keyword;
|
|
32775
|
+
else if (isCall) color = THEME2.function;
|
|
32776
|
+
tokens.push({ text: word, color });
|
|
32777
|
+
continue;
|
|
32778
|
+
}
|
|
32779
|
+
tokens.push({ text: line[i], color: THEME2.punctuation });
|
|
32780
|
+
i++;
|
|
32781
|
+
}
|
|
32782
|
+
return tokens;
|
|
32174
32783
|
}
|
|
32175
32784
|
function renderStructuredRows(rows, maxHeight) {
|
|
32176
32785
|
const paired = collapseContextRows(pairWordDiffs(rows));
|
|
32177
|
-
const
|
|
32786
|
+
const w = calcGutterW(paired);
|
|
32178
32787
|
const { additions, removals } = rowLineCounts(paired);
|
|
32179
32788
|
const nodes = [];
|
|
32180
32789
|
let lineBudget = maxHeight > 0 ? maxHeight : Number.POSITIVE_INFINITY;
|
|
@@ -32191,11 +32800,13 @@ function renderStructuredRows(rows, maxHeight) {
|
|
|
32191
32800
|
for (const r of paired) {
|
|
32192
32801
|
if (r.kind === "omit") {
|
|
32193
32802
|
push(
|
|
32194
|
-
/* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", children: [
|
|
32195
|
-
/* @__PURE__ */
|
|
32196
|
-
r.count > 1 ? /* @__PURE__ */ jsxs26(Text, { dimColor: true, wrap: "wrap", children: [
|
|
32803
|
+
/* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, paddingLeft: 5, children: [
|
|
32804
|
+
/* @__PURE__ */ jsxs26(Text, { dimColor: true, children: [
|
|
32197
32805
|
" ",
|
|
32198
|
-
|
|
32806
|
+
gutterOmit(w)
|
|
32807
|
+
] }),
|
|
32808
|
+
r.count > 1 ? /* @__PURE__ */ jsxs26(Text, { dimColor: true, wrap: "wrap", children: [
|
|
32809
|
+
" \xB7 ",
|
|
32199
32810
|
r.count
|
|
32200
32811
|
] }) : null
|
|
32201
32812
|
] }, `o-${idx++}`)
|
|
@@ -32204,33 +32815,32 @@ function renderStructuredRows(rows, maxHeight) {
|
|
|
32204
32815
|
}
|
|
32205
32816
|
if (r.kind === "meta") {
|
|
32206
32817
|
push(
|
|
32207
|
-
/* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", width: "100%", children: /* @__PURE__ */ jsx41(Text, {
|
|
32818
|
+
/* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, children: tokenizeLine2(r.text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, children: t.text }, i)) }, `m-${idx++}`)
|
|
32208
32819
|
);
|
|
32209
32820
|
continue;
|
|
32210
32821
|
}
|
|
32211
32822
|
if (r.kind === "ctx") {
|
|
32212
32823
|
push(
|
|
32213
|
-
/* @__PURE__ */
|
|
32214
|
-
/* @__PURE__ */ jsx41(Text, { dimColor: true, children: gutterCtx(wOld, wNew, r.old, r.new) }),
|
|
32215
|
-
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.muted, wrap: "wrap", children: r.text || " " })
|
|
32216
|
-
] }, `c-${idx++}`)
|
|
32824
|
+
/* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", width: "100%", flexGrow: 1, children: tokenizeLine2(r.text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, children: t.text }, i)) }, `c-${idx++}`)
|
|
32217
32825
|
);
|
|
32218
32826
|
continue;
|
|
32219
32827
|
}
|
|
32220
32828
|
if (r.kind === "rem") {
|
|
32221
32829
|
push(
|
|
32222
|
-
/* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexWrap: "wrap", children: [
|
|
32223
|
-
/* @__PURE__ */ jsx41(Text, {
|
|
32224
|
-
|
|
32830
|
+
/* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, flexWrap: "wrap", backgroundColor: BLUMA_TERMINAL.diffRemoved, paddingLeft: 3, children: [
|
|
32831
|
+
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterLine(w, r.old) }),
|
|
32832
|
+
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffRemovedWord, children: "-" }),
|
|
32833
|
+
r.wordParts && r.wordParts.length > 0 ? /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", flexGrow: 1, marginLeft: 1, children: /* @__PURE__ */ jsx41(WordDiffChunks, { parts: r.wordParts, mode: "rem" }) }) : /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", children: tokenizeLine2(r.text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, children: t.text }, i)) })
|
|
32225
32834
|
] }, `r-${idx++}`)
|
|
32226
32835
|
);
|
|
32227
32836
|
continue;
|
|
32228
32837
|
}
|
|
32229
32838
|
if (r.kind === "add") {
|
|
32230
32839
|
push(
|
|
32231
|
-
/* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexWrap: "wrap", children: [
|
|
32232
|
-
/* @__PURE__ */ jsx41(Text, {
|
|
32233
|
-
|
|
32840
|
+
/* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, flexWrap: "wrap", backgroundColor: BLUMA_TERMINAL.diffAdded, paddingLeft: 3, children: [
|
|
32841
|
+
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterLine(w, r.new) }),
|
|
32842
|
+
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffAddedWord, children: "+" }),
|
|
32843
|
+
r.wordParts && r.wordParts.length > 0 ? /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", flexGrow: 1, children: /* @__PURE__ */ jsx41(WordDiffChunks, { parts: r.wordParts, mode: "add" }) }) : /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", children: tokenizeLine2(r.text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, children: t.text }, i)) })
|
|
32234
32844
|
] }, `a-${idx++}`)
|
|
32235
32845
|
);
|
|
32236
32846
|
}
|
|
@@ -32255,15 +32865,18 @@ function legacyDiffToSegs(lines) {
|
|
|
32255
32865
|
const isNewHeader = line.startsWith("+++");
|
|
32256
32866
|
if (isOldHeader || isNewHeader) {
|
|
32257
32867
|
flushCtx();
|
|
32258
|
-
out.push({ kind: "header", line });
|
|
32259
32868
|
if (isNewHeader) {
|
|
32260
32869
|
oldLine = 1;
|
|
32261
32870
|
newLine = 1;
|
|
32262
32871
|
}
|
|
32263
32872
|
continue;
|
|
32264
32873
|
}
|
|
32265
|
-
|
|
32266
|
-
|
|
32874
|
+
if (line.startsWith("\\")) {
|
|
32875
|
+
flushCtx();
|
|
32876
|
+
continue;
|
|
32877
|
+
}
|
|
32878
|
+
const isRem = line.startsWith("-");
|
|
32879
|
+
const isAdd = line.startsWith("+");
|
|
32267
32880
|
if (isRem) {
|
|
32268
32881
|
flushCtx();
|
|
32269
32882
|
out.push({ kind: "rem", body: line.slice(1), oldLine });
|
|
@@ -32281,11 +32894,6 @@ function legacyDiffToSegs(lines) {
|
|
|
32281
32894
|
ctx += 1;
|
|
32282
32895
|
continue;
|
|
32283
32896
|
}
|
|
32284
|
-
if (line.startsWith("\\")) {
|
|
32285
|
-
flushCtx();
|
|
32286
|
-
out.push({ kind: "raw", line });
|
|
32287
|
-
continue;
|
|
32288
|
-
}
|
|
32289
32897
|
if (line.startsWith("[")) {
|
|
32290
32898
|
flushCtx();
|
|
32291
32899
|
out.push({ kind: "raw", line });
|
|
@@ -32301,18 +32909,6 @@ function legacyDiffToSegs(lines) {
|
|
|
32301
32909
|
flushCtx();
|
|
32302
32910
|
return out;
|
|
32303
32911
|
}
|
|
32304
|
-
function legacySegWidths(segs) {
|
|
32305
|
-
let maxO = 0;
|
|
32306
|
-
let maxN = 0;
|
|
32307
|
-
for (const s of segs) {
|
|
32308
|
-
if (s.kind === "rem") maxO = Math.max(maxO, s.oldLine);
|
|
32309
|
-
if (s.kind === "add") maxN = Math.max(maxN, s.newLine);
|
|
32310
|
-
}
|
|
32311
|
-
return {
|
|
32312
|
-
wOld: Math.max(2, String(maxO || 0).length),
|
|
32313
|
-
wNew: Math.max(2, String(maxN || 0).length)
|
|
32314
|
-
};
|
|
32315
|
-
}
|
|
32316
32912
|
function legacySegLineCounts(lines) {
|
|
32317
32913
|
let additions = 0;
|
|
32318
32914
|
let removals = 0;
|
|
@@ -32322,75 +32918,104 @@ function legacySegLineCounts(lines) {
|
|
|
32322
32918
|
}
|
|
32323
32919
|
return { additions, removals };
|
|
32324
32920
|
}
|
|
32325
|
-
function collapseContextRows(rows) {
|
|
32326
|
-
const out = [];
|
|
32327
|
-
let ctxCount = 0;
|
|
32328
|
-
const flush = () => {
|
|
32329
|
-
if (ctxCount > 0) {
|
|
32330
|
-
out.push({ kind: "omit", count: ctxCount });
|
|
32331
|
-
ctxCount = 0;
|
|
32332
|
-
}
|
|
32333
|
-
};
|
|
32334
|
-
for (const r of rows) {
|
|
32335
|
-
if (r.kind === "ctx") {
|
|
32336
|
-
ctxCount += 1;
|
|
32337
|
-
continue;
|
|
32338
|
-
}
|
|
32339
|
-
if (r.kind === "meta" && r.text.startsWith("@@")) {
|
|
32340
|
-
ctxCount += 1;
|
|
32341
|
-
continue;
|
|
32342
|
-
}
|
|
32343
|
-
flush();
|
|
32344
|
-
out.push(r);
|
|
32345
|
-
}
|
|
32346
|
-
flush();
|
|
32347
|
-
return out;
|
|
32348
|
-
}
|
|
32349
32921
|
function LegacyDiffBody({
|
|
32350
32922
|
lines,
|
|
32351
32923
|
maxHeight
|
|
32352
32924
|
}) {
|
|
32353
32925
|
const segs = legacyDiffToSegs(lines);
|
|
32354
|
-
const
|
|
32926
|
+
const w = calcLegacyGutterW(segs);
|
|
32355
32927
|
const truncated = maxHeight > 0 && segs.length > maxHeight;
|
|
32356
32928
|
const toRender = truncated ? segs.slice(0, maxHeight) : segs;
|
|
32357
32929
|
const hiddenBelow = truncated ? segs.length - toRender.length : 0;
|
|
32358
32930
|
const { additions, removals } = legacySegLineCounts(lines);
|
|
32359
|
-
const summary = diffSummary(
|
|
32360
|
-
|
|
32361
|
-
|
|
32362
|
-
|
|
32363
|
-
|
|
32364
|
-
|
|
32365
|
-
|
|
32366
|
-
|
|
32367
|
-
|
|
32368
|
-
|
|
32369
|
-
|
|
32370
|
-
|
|
32371
|
-
|
|
32372
|
-
|
|
32373
|
-
|
|
32374
|
-
|
|
32375
|
-
|
|
32376
|
-
|
|
32377
|
-
|
|
32378
|
-
|
|
32379
|
-
|
|
32380
|
-
|
|
32381
|
-
|
|
32382
|
-
|
|
32383
|
-
|
|
32384
|
-
|
|
32385
|
-
|
|
32386
|
-
|
|
32387
|
-
|
|
32388
|
-
|
|
32389
|
-
|
|
32390
|
-
|
|
32391
|
-
|
|
32392
|
-
|
|
32393
|
-
|
|
32931
|
+
const summary = diffSummary(
|
|
32932
|
+
additions,
|
|
32933
|
+
removals,
|
|
32934
|
+
hiddenBelow
|
|
32935
|
+
);
|
|
32936
|
+
const renderTokenizedLine = (text, backgroundColor) => tokenizeLine2(text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, backgroundColor, children: t.text }, i));
|
|
32937
|
+
return /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", width: "100%", children: toRender.map((seg, index) => {
|
|
32938
|
+
if (seg.kind === "omit") {
|
|
32939
|
+
return /* @__PURE__ */ jsx41(
|
|
32940
|
+
Box_default,
|
|
32941
|
+
{
|
|
32942
|
+
flexDirection: "row",
|
|
32943
|
+
width: "100%",
|
|
32944
|
+
flexGrow: 1,
|
|
32945
|
+
paddingLeft: 6,
|
|
32946
|
+
children: /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterOmit(w) })
|
|
32947
|
+
},
|
|
32948
|
+
index
|
|
32949
|
+
);
|
|
32950
|
+
}
|
|
32951
|
+
if (seg.kind === "raw") {
|
|
32952
|
+
return /* @__PURE__ */ jsx41(
|
|
32953
|
+
Box_default,
|
|
32954
|
+
{
|
|
32955
|
+
flexDirection: "row",
|
|
32956
|
+
width: "100%",
|
|
32957
|
+
flexWrap: "wrap",
|
|
32958
|
+
children: renderTokenizedLine(seg.line)
|
|
32959
|
+
},
|
|
32960
|
+
index
|
|
32961
|
+
);
|
|
32962
|
+
}
|
|
32963
|
+
if (seg.kind === "rem") {
|
|
32964
|
+
return /* @__PURE__ */ jsxs26(
|
|
32965
|
+
Box_default,
|
|
32966
|
+
{
|
|
32967
|
+
flexDirection: "row",
|
|
32968
|
+
width: "100%",
|
|
32969
|
+
flexGrow: 1,
|
|
32970
|
+
flexWrap: "wrap",
|
|
32971
|
+
backgroundColor: BLUMA_TERMINAL.diffRemoved,
|
|
32972
|
+
paddingLeft: 3,
|
|
32973
|
+
children: [
|
|
32974
|
+
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterLine(w, seg.oldLine) }),
|
|
32975
|
+
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffRemovedWord, children: "-" }),
|
|
32976
|
+
/* @__PURE__ */ jsx41(
|
|
32977
|
+
Box_default,
|
|
32978
|
+
{
|
|
32979
|
+
flexDirection: "row",
|
|
32980
|
+
flexWrap: "wrap",
|
|
32981
|
+
backgroundColor: BLUMA_TERMINAL.diffRemoved,
|
|
32982
|
+
children: renderTokenizedLine(seg.body, BLUMA_TERMINAL.diffRemoved)
|
|
32983
|
+
}
|
|
32984
|
+
)
|
|
32985
|
+
]
|
|
32986
|
+
},
|
|
32987
|
+
index
|
|
32988
|
+
);
|
|
32989
|
+
}
|
|
32990
|
+
if (seg.kind === "add") {
|
|
32991
|
+
return /* @__PURE__ */ jsxs26(
|
|
32992
|
+
Box_default,
|
|
32993
|
+
{
|
|
32994
|
+
flexDirection: "row",
|
|
32995
|
+
width: "100%",
|
|
32996
|
+
flexGrow: 1,
|
|
32997
|
+
flexWrap: "wrap",
|
|
32998
|
+
backgroundColor: BLUMA_TERMINAL.diffAdded,
|
|
32999
|
+
paddingLeft: 3,
|
|
33000
|
+
children: [
|
|
33001
|
+
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterLine(w, seg.newLine) }),
|
|
33002
|
+
/* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffAddedWord, children: "+" }),
|
|
33003
|
+
/* @__PURE__ */ jsx41(
|
|
33004
|
+
Box_default,
|
|
33005
|
+
{
|
|
33006
|
+
flexDirection: "row",
|
|
33007
|
+
flexWrap: "wrap",
|
|
33008
|
+
backgroundColor: BLUMA_TERMINAL.diffAdded,
|
|
33009
|
+
children: renderTokenizedLine(seg.body, BLUMA_TERMINAL.diffAdded)
|
|
33010
|
+
}
|
|
33011
|
+
)
|
|
33012
|
+
]
|
|
33013
|
+
},
|
|
33014
|
+
index
|
|
33015
|
+
);
|
|
33016
|
+
}
|
|
33017
|
+
return null;
|
|
33018
|
+
}) });
|
|
32394
33019
|
}
|
|
32395
33020
|
var SimpleDiff = ({ text, maxHeight, frame = false }) => {
|
|
32396
33021
|
const raw = stripOptionalMarkdownFence(String(text ?? ""));
|
|
@@ -32406,28 +33031,16 @@ var SimpleDiff = ({ text, maxHeight, frame = false }) => {
|
|
|
32406
33031
|
const allLines = raw.split("\n");
|
|
32407
33032
|
const body2 = /* @__PURE__ */ jsx41(LegacyDiffBody, { lines: allLines, maxHeight });
|
|
32408
33033
|
if (!frame) return body2;
|
|
32409
|
-
return /* @__PURE__ */
|
|
32410
|
-
/* @__PURE__ */ jsx41(Text, { dimColor: true, children: rule }),
|
|
32411
|
-
/* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", paddingX: 1, children: body2 }),
|
|
32412
|
-
/* @__PURE__ */ jsx41(Text, { dimColor: true, children: rule })
|
|
32413
|
-
] });
|
|
33034
|
+
return /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", children: /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", paddingX: 1, children: body2 }) });
|
|
32414
33035
|
}
|
|
32415
33036
|
const rows = [];
|
|
32416
33037
|
for (const h of hunks) {
|
|
32417
33038
|
rows.push(...hunkToRows(h));
|
|
32418
33039
|
}
|
|
32419
|
-
const { nodes, hidden, additions, removals } = renderStructuredRows(
|
|
32420
|
-
|
|
32421
|
-
|
|
32422
|
-
|
|
32423
|
-
const body = /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "column", children: [
|
|
32424
|
-
(hidden > 0 || additions > 0 || removals > 0) && /* @__PURE__ */ jsx41(Text, { dimColor: true, children: diffSummary(additions, removals, hidden) }),
|
|
32425
|
-
nodes
|
|
32426
|
-
] });
|
|
32427
|
-
if (!frame) {
|
|
32428
|
-
return body;
|
|
32429
|
-
}
|
|
32430
|
-
return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "column", children: [
|
|
33040
|
+
const { nodes, hidden, additions, removals } = renderStructuredRows(rows, maxHeight);
|
|
33041
|
+
const body = /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", width: "100%", children: nodes });
|
|
33042
|
+
if (!frame) return body;
|
|
33043
|
+
return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "column", width: "100%", children: [
|
|
32431
33044
|
/* @__PURE__ */ jsx41(Text, { dimColor: true, children: rule }),
|
|
32432
33045
|
/* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", paddingX: 1, children: body }),
|
|
32433
33046
|
/* @__PURE__ */ jsx41(Text, { dimColor: true, children: rule })
|
|
@@ -32549,7 +33162,20 @@ function EditToolDiffPanel({
|
|
|
32549
33162
|
}
|
|
32550
33163
|
|
|
32551
33164
|
// src/app/agent/tools/EditTool/UI.tsx
|
|
33165
|
+
import { diffLines as diffLines2 } from "diff";
|
|
32552
33166
|
import { Fragment as Fragment5, jsx as jsx44, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
33167
|
+
function countLineDiff(oldText, newText) {
|
|
33168
|
+
const parts = diffLines2(oldText.replace(/\r\n/g, "\n"), newText.replace(/\r\n/g, "\n"));
|
|
33169
|
+
let added = 0;
|
|
33170
|
+
let removed = 0;
|
|
33171
|
+
for (const part of parts) {
|
|
33172
|
+
const lines = part.value.replace(/\n$/, "").split("\n");
|
|
33173
|
+
const lineCount = part.value === "" ? 0 : lines.length;
|
|
33174
|
+
if (part.added) added += lineCount;
|
|
33175
|
+
if (part.removed) removed += lineCount;
|
|
33176
|
+
}
|
|
33177
|
+
return { added, removed };
|
|
33178
|
+
}
|
|
32553
33179
|
function userFacingName11(args) {
|
|
32554
33180
|
if (!args) return "Updated";
|
|
32555
33181
|
if (args.edits && Array.isArray(args.edits) && args.edits.length > 0) {
|
|
@@ -32564,11 +33190,32 @@ function renderToolUseMessage12({ args }) {
|
|
|
32564
33190
|
return /* @__PURE__ */ jsx44(Text, { color: BLUMA_TERMINAL.blue, children: p });
|
|
32565
33191
|
}
|
|
32566
33192
|
function renderToolHeader12({ args }) {
|
|
32567
|
-
const label = userFacingName11(args);
|
|
32568
33193
|
const path50 = args?.file_path ?? ".";
|
|
33194
|
+
const oldText = typeof args?.old_string === "string" ? args.old_string : "";
|
|
33195
|
+
const newText = typeof args?.new_string === "string" ? args.new_string : "";
|
|
33196
|
+
const counts = countLineDiff(oldText, newText);
|
|
33197
|
+
const action = oldText === "" ? "Created" : "Update";
|
|
32569
33198
|
return /* @__PURE__ */ jsxs27(Box_default, { flexDirection: "row", gap: 1, alignItems: "flex-end", children: [
|
|
32570
|
-
/* @__PURE__ */
|
|
32571
|
-
|
|
33199
|
+
/* @__PURE__ */ jsxs27(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: [
|
|
33200
|
+
action,
|
|
33201
|
+
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
33202
|
+
" ",
|
|
33203
|
+
/* @__PURE__ */ jsx44(FilePathLink, { filePath: path50, color: BLUMA_TERMINAL.dim })
|
|
33204
|
+
] })
|
|
33205
|
+
] }),
|
|
33206
|
+
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
33207
|
+
"(",
|
|
33208
|
+
/* @__PURE__ */ jsxs27(Text, { color: BLUMA_TERMINAL.diffAddedWord, children: [
|
|
33209
|
+
"+",
|
|
33210
|
+
counts.added
|
|
33211
|
+
] }),
|
|
33212
|
+
" ",
|
|
33213
|
+
/* @__PURE__ */ jsxs27(Text, { color: BLUMA_TERMINAL.diffRemovedWord, children: [
|
|
33214
|
+
"-",
|
|
33215
|
+
counts.removed
|
|
33216
|
+
] }),
|
|
33217
|
+
")"
|
|
33218
|
+
] })
|
|
32572
33219
|
] });
|
|
32573
33220
|
}
|
|
32574
33221
|
function renderSingleEditDiff(edit, index, total, state2) {
|
|
@@ -32584,7 +33231,20 @@ function renderSingleEditDiff(edit, index, total, state2) {
|
|
|
32584
33231
|
total
|
|
32585
33232
|
] }),
|
|
32586
33233
|
/* @__PURE__ */ jsx44(Text, { bold: true, color: isNewFile ? BLUMA_TERMINAL.success : BLUMA_TERMINAL.blue, children: isNewFile ? "Create" : "Update" }),
|
|
32587
|
-
/* @__PURE__ */ jsx44(FilePathLink, { filePath, color: BLUMA_TERMINAL.dim })
|
|
33234
|
+
/* @__PURE__ */ jsx44(FilePathLink, { filePath, color: BLUMA_TERMINAL.dim }),
|
|
33235
|
+
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
33236
|
+
"(",
|
|
33237
|
+
/* @__PURE__ */ jsxs27(Text, { color: BLUMA_TERMINAL.diffAddedWord, children: [
|
|
33238
|
+
"+",
|
|
33239
|
+
countLineDiff(oldString, newString).added
|
|
33240
|
+
] }),
|
|
33241
|
+
" ",
|
|
33242
|
+
/* @__PURE__ */ jsxs27(Text, { color: BLUMA_TERMINAL.diffRemovedWord, children: [
|
|
33243
|
+
"-",
|
|
33244
|
+
countLineDiff(oldString, newString).removed
|
|
33245
|
+
] }),
|
|
33246
|
+
")"
|
|
33247
|
+
] })
|
|
32588
33248
|
] }),
|
|
32589
33249
|
/* @__PURE__ */ jsx44(Box_default, { marginLeft: 2, marginTop: 0, children: /* @__PURE__ */ jsx44(
|
|
32590
33250
|
EditToolDiffPanel,
|
|
@@ -32653,7 +33313,7 @@ function renderToolResultMessage12(result) {
|
|
|
32653
33313
|
{
|
|
32654
33314
|
filePath: edit.file_path,
|
|
32655
33315
|
isNewFile: edit.is_new_file ?? false,
|
|
32656
|
-
diffText: edit.diff
|
|
33316
|
+
diffText: typeof edit.diff === "string" ? edit.diff : null
|
|
32657
33317
|
}
|
|
32658
33318
|
) }, `result-edit-${idx}`)) });
|
|
32659
33319
|
}
|
|
@@ -32691,7 +33351,7 @@ function prettifyToolToken(value) {
|
|
|
32691
33351
|
if (!s) return "";
|
|
32692
33352
|
return s.replace(/[_-]+/g, " ").replace(/\s+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
32693
33353
|
}
|
|
32694
|
-
function
|
|
33354
|
+
function parseArgsRecord2(args) {
|
|
32695
33355
|
if (args == null) return {};
|
|
32696
33356
|
if (typeof args === "string") {
|
|
32697
33357
|
try {
|
|
@@ -32748,7 +33408,7 @@ var TOOL_INVOCATION_TITLES = {
|
|
|
32748
33408
|
function getToolInvocationTitle(internalName, args) {
|
|
32749
33409
|
const key = internalName.trim();
|
|
32750
33410
|
if (key === "edit_tool") {
|
|
32751
|
-
const p =
|
|
33411
|
+
const p = parseArgsRecord2(args);
|
|
32752
33412
|
const edits = p.edits;
|
|
32753
33413
|
if (Array.isArray(edits) && edits.length > 0) {
|
|
32754
33414
|
const allCreate = edits.every(
|
|
@@ -33458,6 +34118,8 @@ var loadSkillTool = createTool({
|
|
|
33458
34118
|
});
|
|
33459
34119
|
|
|
33460
34120
|
// src/app/agent/tools/FileWriteTool/UI.tsx
|
|
34121
|
+
import fs42 from "fs";
|
|
34122
|
+
import { diffLines as diffLines3 } from "diff";
|
|
33461
34123
|
import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
33462
34124
|
function getFilePath(args) {
|
|
33463
34125
|
if (typeof args?.filepath === "string" && args.filepath.trim()) {
|
|
@@ -33468,6 +34130,49 @@ function getFilePath(args) {
|
|
|
33468
34130
|
}
|
|
33469
34131
|
return void 0;
|
|
33470
34132
|
}
|
|
34133
|
+
function readExistingFileText(filePath) {
|
|
34134
|
+
if (!filePath) return "";
|
|
34135
|
+
try {
|
|
34136
|
+
return fs42.readFileSync(filePath, "utf-8");
|
|
34137
|
+
} catch {
|
|
34138
|
+
return "";
|
|
34139
|
+
}
|
|
34140
|
+
}
|
|
34141
|
+
function countLineDiff2(oldText, newText) {
|
|
34142
|
+
const parts = diffLines3(oldText.replace(/\r\n/g, "\n"), newText.replace(/\r\n/g, "\n"));
|
|
34143
|
+
let added = 0;
|
|
34144
|
+
let removed = 0;
|
|
34145
|
+
for (const part of parts) {
|
|
34146
|
+
const lines = part.value.replace(/\n$/, "").split("\n");
|
|
34147
|
+
const lineCount = part.value === "" ? 0 : lines.length;
|
|
34148
|
+
if (part.added) added += lineCount;
|
|
34149
|
+
if (part.removed) removed += lineCount;
|
|
34150
|
+
}
|
|
34151
|
+
return { added, removed };
|
|
34152
|
+
}
|
|
34153
|
+
function renderChangeCount(added, removed) {
|
|
34154
|
+
return /* @__PURE__ */ jsxs37(Text, { dimColor: true, children: [
|
|
34155
|
+
"(",
|
|
34156
|
+
/* @__PURE__ */ jsxs37(Text, { color: BLUMA_TERMINAL.diffAddedWord, children: [
|
|
34157
|
+
"+",
|
|
34158
|
+
added
|
|
34159
|
+
] }),
|
|
34160
|
+
" ",
|
|
34161
|
+
/* @__PURE__ */ jsxs37(Text, { color: BLUMA_TERMINAL.diffRemovedWord, children: [
|
|
34162
|
+
"-",
|
|
34163
|
+
removed
|
|
34164
|
+
] }),
|
|
34165
|
+
")"
|
|
34166
|
+
] });
|
|
34167
|
+
}
|
|
34168
|
+
function buildUnifiedDiffText(filePath, oldContent, newContent) {
|
|
34169
|
+
const patch = getPatchFromContents({
|
|
34170
|
+
filePath,
|
|
34171
|
+
oldContent,
|
|
34172
|
+
newContent
|
|
34173
|
+
});
|
|
34174
|
+
return patchToUnifiedDiffText(filePath, patch);
|
|
34175
|
+
}
|
|
33471
34176
|
function userFacingName20(args) {
|
|
33472
34177
|
if (!args) return "Wrote";
|
|
33473
34178
|
const p = getFilePath(args)?.split("/").pop() ?? "...";
|
|
@@ -33480,9 +34185,12 @@ function renderToolUseMessage21({ args }) {
|
|
|
33480
34185
|
function renderToolHeader21({ args }) {
|
|
33481
34186
|
const filePath = getFilePath(args) ?? "";
|
|
33482
34187
|
const isNewFile = !args?.hasExistingContent;
|
|
34188
|
+
const content = typeof args?.content === "string" ? args.content : "";
|
|
34189
|
+
const counts = content ? countLineDiff2(isNewFile ? "" : readExistingFileText(filePath), content) : { added: 0, removed: 0 };
|
|
33483
34190
|
return /* @__PURE__ */ jsxs37(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", gap: 1, children: [
|
|
33484
|
-
/* @__PURE__ */ jsx54(Text, { bold: true, color: isNewFile ? BLUMA_TERMINAL.
|
|
33485
|
-
/* @__PURE__ */ jsx54(FilePathLink, { filePath, color: BLUMA_TERMINAL.dim })
|
|
34191
|
+
/* @__PURE__ */ jsx54(Text, { bold: true, color: isNewFile ? BLUMA_TERMINAL.m3OnSurface : BLUMA_TERMINAL.err, children: isNewFile ? "Created" : "Updated" }),
|
|
34192
|
+
/* @__PURE__ */ jsx54(FilePathLink, { filePath, color: isNewFile ? BLUMA_TERMINAL.dim : BLUMA_TERMINAL.err }),
|
|
34193
|
+
content ? renderChangeCount(counts.added, counts.removed) : null
|
|
33486
34194
|
] });
|
|
33487
34195
|
}
|
|
33488
34196
|
function renderToolMessage20({
|
|
@@ -33494,12 +34202,15 @@ function renderToolMessage20({
|
|
|
33494
34202
|
const filePath = getFilePath(p);
|
|
33495
34203
|
const payload = resolveToolPayload(result);
|
|
33496
34204
|
const content = typeof p?.content === "string" ? p.content : "";
|
|
34205
|
+
const isNewFile = !p?.hasExistingContent;
|
|
34206
|
+
const previousContent = isNewFile ? "" : readExistingFileText(filePath);
|
|
34207
|
+
const diffText = content && filePath ? buildUnifiedDiffText(filePath, previousContent, content) : "";
|
|
33497
34208
|
return /* @__PURE__ */ jsx54(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx54(MessageResponse, { children: /* @__PURE__ */ jsxs37(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
33498
34209
|
/* @__PURE__ */ jsxs37(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
|
|
33499
34210
|
/* @__PURE__ */ jsx54(ToolUseLoader, { state: state2 ?? "pending" }),
|
|
33500
34211
|
renderToolHeader21({ args: p })
|
|
33501
34212
|
] }),
|
|
33502
|
-
/* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", paddingLeft: 1,
|
|
34213
|
+
/* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", paddingLeft: 1, children: payload != null ? renderToolResultMessage21(payload, { filePath, content }) : /* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", children: /* @__PURE__ */ jsx54(Box_default, { marginTop: 0, width: "100%", children: content ? /* @__PURE__ */ jsx54(Box_default, { width: "100%", flexGrow: 1, flexDirection: "column", children: /* @__PURE__ */ jsx54(SimpleDiff, { text: diffText, maxHeight: 2e3 }) }) : /* @__PURE__ */ jsxs37(Text, { dimColor: true, children: [
|
|
33503
34214
|
"Writing to ",
|
|
33504
34215
|
filePath ?? "...",
|
|
33505
34216
|
" (",
|
|
@@ -33520,15 +34231,31 @@ function renderToolResultMessage21(result, context) {
|
|
|
33520
34231
|
}
|
|
33521
34232
|
const filePath = payload.filepath ?? payload.file_path ?? "";
|
|
33522
34233
|
const bytes = payload.bytes_written ?? 0;
|
|
33523
|
-
const
|
|
33524
|
-
|
|
33525
|
-
|
|
34234
|
+
const resultFilePath = filePath || context?.filePath || "file.txt";
|
|
34235
|
+
const previousContent = typeof payload.previous_content === "string" ? payload.previous_content : void 0;
|
|
34236
|
+
const currentContent = typeof payload.content === "string" ? payload.content : typeof context?.args?.content === "string" ? (context?.args).content : typeof context?.content === "string" ? context.content : void 0;
|
|
34237
|
+
const canRenderDiff = typeof currentContent === "string" && currentContent.trim() && typeof previousContent === "string";
|
|
34238
|
+
return /* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", children: canRenderDiff ? /* @__PURE__ */ jsx54(Box_default, { width: "100%", flexGrow: 1, children: /* @__PURE__ */ jsx54(
|
|
34239
|
+
SimpleDiff,
|
|
34240
|
+
{
|
|
34241
|
+
text: buildUnifiedDiffText(resultFilePath, previousContent ?? "", currentContent ?? ""),
|
|
34242
|
+
maxHeight: 2e3
|
|
34243
|
+
}
|
|
34244
|
+
) }) : typeof currentContent === "string" && currentContent.trim() ? /* @__PURE__ */ jsx54(
|
|
34245
|
+
Box_default,
|
|
33526
34246
|
{
|
|
33527
|
-
|
|
33528
|
-
|
|
33529
|
-
|
|
34247
|
+
width: "100%",
|
|
34248
|
+
backgroundColor: BLUMA_TERMINAL.diffAdded,
|
|
34249
|
+
children: /* @__PURE__ */ jsx54(
|
|
34250
|
+
HighlightedCode,
|
|
34251
|
+
{
|
|
34252
|
+
code: currentContent,
|
|
34253
|
+
filePath: resultFilePath,
|
|
34254
|
+
maxLines: 2e3
|
|
34255
|
+
}
|
|
34256
|
+
)
|
|
33530
34257
|
}
|
|
33531
|
-
)
|
|
34258
|
+
) : null });
|
|
33532
34259
|
}
|
|
33533
34260
|
|
|
33534
34261
|
// src/app/agent/tools/FileWriteTool/index.ts
|
|
@@ -35961,162 +36688,6 @@ var ConfirmationPrompt = memo13(ConfirmationPromptComponent);
|
|
|
35961
36688
|
// src/app/ui/WorkingTimer.tsx
|
|
35962
36689
|
import { useState as useState15, useEffect as useEffect13, memo as memo14, useRef as useRef6 } from "react";
|
|
35963
36690
|
import { EventEmitter as EventEmitter6 } from "events";
|
|
35964
|
-
|
|
35965
|
-
// src/app/ui/utils/toolActionLabels.ts
|
|
35966
|
-
function parseArgsRecord2(args) {
|
|
35967
|
-
if (args == null) return {};
|
|
35968
|
-
if (typeof args === "string") {
|
|
35969
|
-
try {
|
|
35970
|
-
return JSON.parse(args);
|
|
35971
|
-
} catch {
|
|
35972
|
-
return {};
|
|
35973
|
-
}
|
|
35974
|
-
}
|
|
35975
|
-
if (typeof args === "object") {
|
|
35976
|
-
return args;
|
|
35977
|
-
}
|
|
35978
|
-
return {};
|
|
35979
|
-
}
|
|
35980
|
-
function getToolActionLabel(toolName, args) {
|
|
35981
|
-
const p = parseArgsRecord2(args);
|
|
35982
|
-
switch (toolName) {
|
|
35983
|
-
case "shell_command":
|
|
35984
|
-
case "run_command": {
|
|
35985
|
-
const cmd = typeof p.command === "string" ? p.command : "";
|
|
35986
|
-
const truncated = cmd.length > 40 ? `${cmd.slice(0, 40)}\u2026` : cmd;
|
|
35987
|
-
return truncated ? `Bash ${truncated}` : "Bash";
|
|
35988
|
-
}
|
|
35989
|
-
case "command_status":
|
|
35990
|
-
return "Status";
|
|
35991
|
-
case "send_command_input":
|
|
35992
|
-
return "Input";
|
|
35993
|
-
case "kill_command":
|
|
35994
|
-
return "Kill";
|
|
35995
|
-
case "read_file_lines": {
|
|
35996
|
-
const filepath = typeof p.filepath === "string" ? p.filepath : "";
|
|
35997
|
-
const file = filepath.split("/").pop() || filepath;
|
|
35998
|
-
return file ? `Read ${file}` : "Read";
|
|
35999
|
-
}
|
|
36000
|
-
case "ls_tool":
|
|
36001
|
-
return "List";
|
|
36002
|
-
case "count_file_lines": {
|
|
36003
|
-
const filepath = typeof p.filepath === "string" ? p.filepath : "";
|
|
36004
|
-
const file = filepath.split("/").pop() || filepath;
|
|
36005
|
-
return file ? `Count ${file}` : "Count";
|
|
36006
|
-
}
|
|
36007
|
-
case "edit_tool": {
|
|
36008
|
-
const edits = p.edits;
|
|
36009
|
-
const count = Array.isArray(edits) ? edits.length : 1;
|
|
36010
|
-
const filepath = typeof p.file_path === "string" ? p.file_path : Array.isArray(edits) && edits[0]?.file_path ? edits[0].file_path : "";
|
|
36011
|
-
const file = filepath ? filepath.split("/").pop() : "file";
|
|
36012
|
-
return count === 1 ? `Edit ${file}` : `Edit ${count} files`;
|
|
36013
|
-
}
|
|
36014
|
-
case "file_write": {
|
|
36015
|
-
const filepath = typeof p.filepath === "string" ? p.filepath : "";
|
|
36016
|
-
const file = filepath.split("/").pop() || filepath;
|
|
36017
|
-
return file ? `Write ${file}` : "Write";
|
|
36018
|
-
}
|
|
36019
|
-
case "grep_search": {
|
|
36020
|
-
const query = typeof p.query === "string" ? p.query : "";
|
|
36021
|
-
const truncated = query.length > 30 ? `${query.slice(0, 30)}\u2026` : query;
|
|
36022
|
-
return truncated ? `Grep ${truncated}` : "Grep";
|
|
36023
|
-
}
|
|
36024
|
-
case "find_by_name": {
|
|
36025
|
-
const pattern = typeof p.pattern === "string" ? p.pattern : "";
|
|
36026
|
-
return pattern ? `Find ${pattern}` : "Find files";
|
|
36027
|
-
}
|
|
36028
|
-
case "view_file_outline": {
|
|
36029
|
-
const filepath = typeof p.file_path === "string" ? p.file_path : "";
|
|
36030
|
-
const file = filepath.split("/").pop() || filepath;
|
|
36031
|
-
return file ? `Outline ${file}` : "Outline";
|
|
36032
|
-
}
|
|
36033
|
-
case "web_fetch": {
|
|
36034
|
-
const url = typeof p.url === "string" ? p.url : "";
|
|
36035
|
-
const truncated = url.length > 40 ? `${url.slice(0, 40)}\u2026` : url;
|
|
36036
|
-
return truncated ? `Fetch ${truncated}` : "Fetch URL";
|
|
36037
|
-
}
|
|
36038
|
-
case "search_web": {
|
|
36039
|
-
const query = typeof p.query === "string" ? p.query : "";
|
|
36040
|
-
const truncated = query.length > 30 ? `${query.slice(0, 30)}\u2026` : query;
|
|
36041
|
-
return truncated ? `Web ${truncated}` : "Web search";
|
|
36042
|
-
}
|
|
36043
|
-
case "spawn_agent": {
|
|
36044
|
-
const title = typeof p.title === "string" ? p.title : "";
|
|
36045
|
-
const task = typeof p.task === "string" ? p.task : "";
|
|
36046
|
-
const label = title ? prettifyToolToken(title) : task ? task.slice(0, 40) : "task";
|
|
36047
|
-
return `Spawn ${label}`;
|
|
36048
|
-
}
|
|
36049
|
-
case "wait_agent":
|
|
36050
|
-
return "Wait";
|
|
36051
|
-
case "list_agents":
|
|
36052
|
-
return "Agents";
|
|
36053
|
-
case "todo": {
|
|
36054
|
-
const action = typeof p.action === "string" ? p.action : "update";
|
|
36055
|
-
return `Todo ${action}`;
|
|
36056
|
-
}
|
|
36057
|
-
case "task_boundary": {
|
|
36058
|
-
const mode = typeof p.mode === "string" ? p.mode : "";
|
|
36059
|
-
const taskName = typeof p.task_name === "string" ? p.task_name : "";
|
|
36060
|
-
return taskName ? `${mode} ${taskName}` : `Task ${mode}`;
|
|
36061
|
-
}
|
|
36062
|
-
case "task_create":
|
|
36063
|
-
case "task_list":
|
|
36064
|
-
case "task_get":
|
|
36065
|
-
case "task_update":
|
|
36066
|
-
case "task_stop": {
|
|
36067
|
-
const title = typeof p.title === "string" ? p.title : "";
|
|
36068
|
-
return title ? `Task ${prettifyToolToken(title)}` : "Task";
|
|
36069
|
-
}
|
|
36070
|
-
case "load_skill": {
|
|
36071
|
-
const skill = typeof p.skill_name === "string" ? p.skill_name : "";
|
|
36072
|
-
return skill ? `Skill ${skill.replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())}` : "Skill";
|
|
36073
|
-
}
|
|
36074
|
-
case "coding_memory": {
|
|
36075
|
-
const action = typeof p.action === "string" ? p.action : "update";
|
|
36076
|
-
return `Memory ${action}`;
|
|
36077
|
-
}
|
|
36078
|
-
case "read_artifact": {
|
|
36079
|
-
const filename = typeof p.filename === "string" ? p.filename : "";
|
|
36080
|
-
return filename ? `Read ${filename}` : "Read artifact";
|
|
36081
|
-
}
|
|
36082
|
-
case "message":
|
|
36083
|
-
return "Message";
|
|
36084
|
-
case "ask_user_question":
|
|
36085
|
-
return "Question";
|
|
36086
|
-
case "enter_plan_mode":
|
|
36087
|
-
return "Plan mode";
|
|
36088
|
-
case "exit_plan_mode":
|
|
36089
|
-
return "Exit plan";
|
|
36090
|
-
case "list_mcp_resources":
|
|
36091
|
-
return "MCP resources";
|
|
36092
|
-
case "read_mcp_resource": {
|
|
36093
|
-
const server = typeof p.server === "string" ? p.server : "";
|
|
36094
|
-
const uri = typeof p.uri === "string" ? p.uri : "";
|
|
36095
|
-
return `MCP ${server || uri || "resource"}`;
|
|
36096
|
-
}
|
|
36097
|
-
case "cron_create":
|
|
36098
|
-
return "Schedule";
|
|
36099
|
-
case "cron_list":
|
|
36100
|
-
return "Reminders";
|
|
36101
|
-
case "cron_delete":
|
|
36102
|
-
return "Cancel reminder";
|
|
36103
|
-
case "notebook_edit": {
|
|
36104
|
-
const filepath = typeof p.filepath === "string" ? p.filepath : "";
|
|
36105
|
-
const file = filepath.split("/").pop() || filepath;
|
|
36106
|
-
return file ? `Notebook ${file}` : "Notebook";
|
|
36107
|
-
}
|
|
36108
|
-
case "lsp_query": {
|
|
36109
|
-
const operation = typeof p.operation === "string" ? p.operation : "";
|
|
36110
|
-
return `LSP ${operation || "query"}`;
|
|
36111
|
-
}
|
|
36112
|
-
default: {
|
|
36113
|
-
const pretty = toolName.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
36114
|
-
return pretty || "Working";
|
|
36115
|
-
}
|
|
36116
|
-
}
|
|
36117
|
-
}
|
|
36118
|
-
|
|
36119
|
-
// src/app/ui/WorkingTimer.tsx
|
|
36120
36691
|
import { Fragment as Fragment8, jsx as jsx77, jsxs as jsxs60 } from "react/jsx-runtime";
|
|
36121
36692
|
var SHIMMER_CYCLE_MS = 2e3;
|
|
36122
36693
|
var SHIMMER_TICK_MS = 80;
|
|
@@ -36163,9 +36734,6 @@ var WorkingTimerComponent = ({
|
|
|
36163
36734
|
finalElapsedMs: externalFinalElapsedMs
|
|
36164
36735
|
// External final elapsed time (from parent)
|
|
36165
36736
|
}) => {
|
|
36166
|
-
if (startedAtMs == null && externalFinalElapsedMs == null) {
|
|
36167
|
-
return null;
|
|
36168
|
-
}
|
|
36169
36737
|
const [currentAction, setCurrentAction] = useState15("working");
|
|
36170
36738
|
const [nowTick, setNowTick] = useState15(() => Date.now());
|
|
36171
36739
|
const [internalFinalElapsedMs, setInternalFinalElapsedMs] = useState15(null);
|
|
@@ -36206,11 +36774,10 @@ var WorkingTimerComponent = ({
|
|
|
36206
36774
|
}, [startedAtMs]);
|
|
36207
36775
|
useEffect13(() => {
|
|
36208
36776
|
const localBus = localEventBusRef.current;
|
|
36209
|
-
if (startedAtMs == null) return;
|
|
36210
36777
|
let isMounted = true;
|
|
36211
36778
|
completionHandledRef.current = false;
|
|
36212
36779
|
const handleTurnComplete = (data) => {
|
|
36213
|
-
if (!isMounted || completionHandledRef.current) return;
|
|
36780
|
+
if (!isMounted || completionHandledRef.current || startedAtMs == null) return;
|
|
36214
36781
|
const elapsedMs2 = typeof data?.elapsedMs === "number" ? data.elapsedMs : null;
|
|
36215
36782
|
if (elapsedMs2 == null) return;
|
|
36216
36783
|
completionHandledRef.current = true;
|
|
@@ -36235,7 +36802,10 @@ var WorkingTimerComponent = ({
|
|
|
36235
36802
|
localBus.removeAllListeners();
|
|
36236
36803
|
};
|
|
36237
36804
|
}, [eventBus, startedAtMs]);
|
|
36238
|
-
|
|
36805
|
+
if (startedAtMs == null && externalFinalElapsedMs == null) {
|
|
36806
|
+
return null;
|
|
36807
|
+
}
|
|
36808
|
+
const displayAction = dynamicActionLabel || (taskStatus || (currentAction !== "working" ? currentAction : isReasoning ? getReasoningActionStatus().action : currentAction)).trim() || "working";
|
|
36239
36809
|
const actionLine = `${displayAction}`;
|
|
36240
36810
|
const elapsedMs = finalElapsedMs != null ? finalElapsedMs : startedAtMs != null ? Math.max(0, nowTick - startedAtMs) : 0;
|
|
36241
36811
|
const elapsedLabel = formatTurnDurationMs(elapsedMs);
|
|
@@ -36379,13 +36949,13 @@ var StatusBar = memo16(() => {
|
|
|
36379
36949
|
} = snapshot;
|
|
36380
36950
|
void renderKey;
|
|
36381
36951
|
const colors = {
|
|
36382
|
-
primary: BLUMA_TERMINAL.
|
|
36383
|
-
secondary: BLUMA_TERMINAL.
|
|
36384
|
-
accent: BLUMA_TERMINAL.
|
|
36385
|
-
success: BLUMA_TERMINAL.
|
|
36386
|
-
warning: BLUMA_TERMINAL.
|
|
36387
|
-
error: BLUMA_TERMINAL.
|
|
36388
|
-
muted: BLUMA_TERMINAL.
|
|
36952
|
+
primary: BLUMA_TERMINAL.subtle,
|
|
36953
|
+
secondary: BLUMA_TERMINAL.subtle,
|
|
36954
|
+
accent: BLUMA_TERMINAL.subtle,
|
|
36955
|
+
success: BLUMA_TERMINAL.subtle,
|
|
36956
|
+
warning: BLUMA_TERMINAL.subtle,
|
|
36957
|
+
error: BLUMA_TERMINAL.subtle,
|
|
36958
|
+
muted: BLUMA_TERMINAL.subtle
|
|
36389
36959
|
};
|
|
36390
36960
|
const remainingPercent = tokenBudget > 0 ? Math.max(0, Math.round((tokenBudget - tokenCount) / tokenBudget * 100)) : 100;
|
|
36391
36961
|
let remainingColor = colors.muted;
|
|
@@ -36399,27 +36969,27 @@ var StatusBar = memo16(() => {
|
|
|
36399
36969
|
const shortWorkdir = workdir ? workdir.split("/").filter(Boolean).pop() || workdir : "";
|
|
36400
36970
|
const compressionText = isCompressing ? ` \xB7 compressing ${compressionProgress}%` : "";
|
|
36401
36971
|
const planModeText = isPlanMode ? /* @__PURE__ */ jsxs62(Fragment9, { children: [
|
|
36402
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36403
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36972
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "\xB7" }),
|
|
36973
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "Plan Mode" })
|
|
36404
36974
|
] }) : null;
|
|
36405
36975
|
const agentModeIndicator = (() => {
|
|
36406
36976
|
if (!agentMode || agentMode === "default") return null;
|
|
36407
36977
|
const mode = agentMode.toLowerCase();
|
|
36408
36978
|
if (mode === "coordinator") {
|
|
36409
36979
|
return /* @__PURE__ */ jsxs62(Fragment9, { children: [
|
|
36410
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36411
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36980
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "\xB7" }),
|
|
36981
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "Coordinator" })
|
|
36412
36982
|
] });
|
|
36413
36983
|
}
|
|
36414
36984
|
if (mode === "plan") {
|
|
36415
36985
|
return /* @__PURE__ */ jsxs62(Fragment9, { children: [
|
|
36416
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36417
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36986
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "\xB7" }),
|
|
36987
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "Plan" })
|
|
36418
36988
|
] });
|
|
36419
36989
|
}
|
|
36420
36990
|
return /* @__PURE__ */ jsxs62(Fragment9, { children: [
|
|
36421
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36422
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36991
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "\xB7" }),
|
|
36992
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: agentMode })
|
|
36423
36993
|
] });
|
|
36424
36994
|
})();
|
|
36425
36995
|
const modelText = modelName ? ` \xB7 model: ${modelName}` : "";
|
|
@@ -36435,25 +37005,25 @@ var StatusBar = memo16(() => {
|
|
|
36435
37005
|
flexShrink: 0,
|
|
36436
37006
|
children: [
|
|
36437
37007
|
shortWorkdir && /* @__PURE__ */ jsxs62(Fragment9, { children: [
|
|
36438
|
-
/* @__PURE__ */ jsxs62(Text, { color: BLUMA_TERMINAL.
|
|
37008
|
+
/* @__PURE__ */ jsxs62(Text, { color: BLUMA_TERMINAL.subtle, children: [
|
|
36439
37009
|
"~/",
|
|
36440
37010
|
shortWorkdir
|
|
36441
37011
|
] }),
|
|
36442
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
37012
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "\xB7" })
|
|
36443
37013
|
] }),
|
|
36444
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
37014
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: "Context" }),
|
|
36445
37015
|
/* @__PURE__ */ jsxs62(Text, { color: remainingColor, children: [
|
|
36446
37016
|
remainingPercent,
|
|
36447
37017
|
"% left"
|
|
36448
37018
|
] }),
|
|
36449
37019
|
modelText && /* @__PURE__ */ jsxs62(Fragment9, { children: [
|
|
36450
|
-
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
36451
|
-
/* @__PURE__ */ jsx79(Text, {
|
|
37020
|
+
/* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: modelText }),
|
|
37021
|
+
/* @__PURE__ */ jsx79(Text, { subtleColor: true, children: "\xB7" })
|
|
36452
37022
|
] }),
|
|
36453
|
-
compressionText && /* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
37023
|
+
compressionText && /* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: compressionText }),
|
|
36454
37024
|
agentModeIndicator,
|
|
36455
37025
|
planModeText,
|
|
36456
|
-
projectsText && /* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.
|
|
37026
|
+
projectsText && /* @__PURE__ */ jsx79(Text, { color: BLUMA_TERMINAL.subtle, children: projectsText })
|
|
36457
37027
|
]
|
|
36458
37028
|
}
|
|
36459
37029
|
);
|
|
@@ -36841,7 +37411,7 @@ function ToolMessageComponent({
|
|
|
36841
37411
|
if (!normalizedResult) return null;
|
|
36842
37412
|
if (entry?.renderToolResultMessage) {
|
|
36843
37413
|
const payload = normalizedResult.status === "success" ? normalizedResult.data : normalizedResult;
|
|
36844
|
-
return entry.renderToolResultMessage(payload, { verbose: false });
|
|
37414
|
+
return entry.renderToolResultMessage(payload, { verbose: false, args });
|
|
36845
37415
|
}
|
|
36846
37416
|
return renderRawJson(normalizedResult);
|
|
36847
37417
|
})();
|
|
@@ -38279,7 +38849,7 @@ var renderCode = () => {
|
|
|
38279
38849
|
// src/app/agent/core/thread/thread_store.ts
|
|
38280
38850
|
import path46 from "path";
|
|
38281
38851
|
import os34 from "os";
|
|
38282
|
-
import { promises as
|
|
38852
|
+
import { promises as fs43 } from "fs";
|
|
38283
38853
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
38284
38854
|
var INDEX_VERSION = 1;
|
|
38285
38855
|
var fileLocks2 = /* @__PURE__ */ new Map();
|
|
@@ -38314,10 +38884,10 @@ var ThreadStore = class {
|
|
|
38314
38884
|
* Inicializa o diretório de threads
|
|
38315
38885
|
*/
|
|
38316
38886
|
async initialize() {
|
|
38317
|
-
await
|
|
38318
|
-
await
|
|
38887
|
+
await fs43.mkdir(this.threadsDir, { recursive: true });
|
|
38888
|
+
await fs43.mkdir(this.archiveDir, { recursive: true });
|
|
38319
38889
|
try {
|
|
38320
|
-
await
|
|
38890
|
+
await fs43.access(this.indexPath);
|
|
38321
38891
|
} catch {
|
|
38322
38892
|
await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
|
|
38323
38893
|
}
|
|
@@ -38346,7 +38916,7 @@ var ThreadStore = class {
|
|
|
38346
38916
|
async loadIndex() {
|
|
38347
38917
|
return withFileLock2(this.indexPath, async () => {
|
|
38348
38918
|
try {
|
|
38349
|
-
const content = await
|
|
38919
|
+
const content = await fs43.readFile(this.indexPath, "utf-8");
|
|
38350
38920
|
return JSON.parse(content);
|
|
38351
38921
|
} catch {
|
|
38352
38922
|
return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -38357,8 +38927,8 @@ var ThreadStore = class {
|
|
|
38357
38927
|
return withFileLock2(this.indexPath, async () => {
|
|
38358
38928
|
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
38359
38929
|
const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
|
|
38360
|
-
await
|
|
38361
|
-
await
|
|
38930
|
+
await fs43.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
|
|
38931
|
+
await fs43.rename(tempPath, this.indexPath);
|
|
38362
38932
|
});
|
|
38363
38933
|
}
|
|
38364
38934
|
// ==================== Git Info ====================
|
|
@@ -38554,7 +39124,7 @@ var ThreadStore = class {
|
|
|
38554
39124
|
const oldPath = this.getHistoryPath(threadId);
|
|
38555
39125
|
const newPath = path46.join(this.archiveDir, `${threadId}.jsonl`);
|
|
38556
39126
|
try {
|
|
38557
|
-
await
|
|
39127
|
+
await fs43.rename(oldPath, newPath);
|
|
38558
39128
|
} catch (e) {
|
|
38559
39129
|
if (e.code !== "ENOENT") throw e;
|
|
38560
39130
|
}
|
|
@@ -38578,7 +39148,7 @@ var ThreadStore = class {
|
|
|
38578
39148
|
const oldPath = path46.join(this.archiveDir, `${threadId}.jsonl`);
|
|
38579
39149
|
const newPath = this.getHistoryPath(threadId);
|
|
38580
39150
|
try {
|
|
38581
|
-
await
|
|
39151
|
+
await fs43.rename(oldPath, newPath);
|
|
38582
39152
|
} catch (e) {
|
|
38583
39153
|
if (e.code !== "ENOENT") throw e;
|
|
38584
39154
|
}
|
|
@@ -38599,7 +39169,7 @@ var ThreadStore = class {
|
|
|
38599
39169
|
if (entryIndex === -1) return false;
|
|
38600
39170
|
const entry = index.threads[entryIndex];
|
|
38601
39171
|
try {
|
|
38602
|
-
await
|
|
39172
|
+
await fs43.unlink(entry.historyPath);
|
|
38603
39173
|
} catch {
|
|
38604
39174
|
}
|
|
38605
39175
|
index.threads.splice(entryIndex, 1);
|
|
@@ -38626,7 +39196,7 @@ var ThreadStore = class {
|
|
|
38626
39196
|
for (const msg of history.messages) {
|
|
38627
39197
|
lines.push(JSON.stringify({ type: "message", ...msg }));
|
|
38628
39198
|
}
|
|
38629
|
-
await
|
|
39199
|
+
await fs43.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
|
|
38630
39200
|
}
|
|
38631
39201
|
/**
|
|
38632
39202
|
* Carrega o histórico de uma thread
|
|
@@ -38636,7 +39206,7 @@ var ThreadStore = class {
|
|
|
38636
39206
|
const entry = index.threads.find((t) => t.threadId === threadId);
|
|
38637
39207
|
if (!entry) return null;
|
|
38638
39208
|
try {
|
|
38639
|
-
const content = await
|
|
39209
|
+
const content = await fs43.readFile(entry.historyPath, "utf-8");
|
|
38640
39210
|
const lines = content.split("\n").filter(Boolean);
|
|
38641
39211
|
const history = {
|
|
38642
39212
|
threadId,
|
|
@@ -38669,7 +39239,7 @@ var ThreadStore = class {
|
|
|
38669
39239
|
const entry = index.threads.find((t) => t.threadId === threadId);
|
|
38670
39240
|
if (!entry) throw new Error(`Thread not found: ${threadId}`);
|
|
38671
39241
|
const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
|
|
38672
|
-
await
|
|
39242
|
+
await fs43.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
|
|
38673
39243
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
38674
39244
|
await this.saveIndex(index);
|
|
38675
39245
|
}
|
|
@@ -38957,7 +39527,7 @@ function formatRelativeTime(iso) {
|
|
|
38957
39527
|
if (diffDays < 7) return `h\xE1 ${diffDays}d`;
|
|
38958
39528
|
return formatDate(iso);
|
|
38959
39529
|
}
|
|
38960
|
-
function
|
|
39530
|
+
function truncate4(str, max) {
|
|
38961
39531
|
if (str.length <= max) return str;
|
|
38962
39532
|
return str.slice(0, max - 3) + "...";
|
|
38963
39533
|
}
|
|
@@ -38983,7 +39553,7 @@ async function renderCurrentThread() {
|
|
|
38983
39553
|
] }),
|
|
38984
39554
|
/* @__PURE__ */ jsxs79(Text, { children: [
|
|
38985
39555
|
/* @__PURE__ */ jsx96(Text, { dimColor: true, children: "Preview: " }),
|
|
38986
|
-
/* @__PURE__ */ jsx96(Text, { dimColor: true, children:
|
|
39556
|
+
/* @__PURE__ */ jsx96(Text, { dimColor: true, children: truncate4(metadata.preview, 60) })
|
|
38987
39557
|
] }),
|
|
38988
39558
|
/* @__PURE__ */ jsxs79(Text, { children: [
|
|
38989
39559
|
/* @__PURE__ */ jsx96(Text, { dimColor: true, children: "Modelo: " }),
|
|
@@ -38998,7 +39568,7 @@ async function renderCurrentThread() {
|
|
|
38998
39568
|
/* @__PURE__ */ jsxs79(Text, { dimColor: true, children: [
|
|
38999
39569
|
metadata.gitInfo.branch || "?",
|
|
39000
39570
|
" @ ",
|
|
39001
|
-
|
|
39571
|
+
truncate4(metadata.gitInfo.sha || "?", 8)
|
|
39002
39572
|
] })
|
|
39003
39573
|
] }),
|
|
39004
39574
|
/* @__PURE__ */ jsxs79(Text, { children: [
|
|
@@ -39066,7 +39636,7 @@ async function renderThreadList(args) {
|
|
|
39066
39636
|
}
|
|
39067
39637
|
var ThreadListItem = ({ thread, isActive, index }) => {
|
|
39068
39638
|
const statusColor = thread.status === "archived" ? BLUMA_TERMINAL.warning : BLUMA_TERMINAL.success;
|
|
39069
|
-
const name = thread.name ||
|
|
39639
|
+
const name = thread.name || truncate4(thread.preview, 40);
|
|
39070
39640
|
return /* @__PURE__ */ jsxs79(Box_default, { flexDirection: "column", children: [
|
|
39071
39641
|
/* @__PURE__ */ jsxs79(Box_default, { gap: 1, children: [
|
|
39072
39642
|
/* @__PURE__ */ jsxs79(Text, { dimColor: true, children: [
|
|
@@ -41535,15 +42105,15 @@ import semverGt from "semver/functions/gt.js";
|
|
|
41535
42105
|
import semverValid from "semver/functions/valid.js";
|
|
41536
42106
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
41537
42107
|
import path47 from "path";
|
|
41538
|
-
import
|
|
42108
|
+
import fs44 from "fs";
|
|
41539
42109
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
41540
42110
|
function findBlumaPackageJson(startDir) {
|
|
41541
42111
|
let dir = startDir;
|
|
41542
42112
|
for (let i = 0; i < 12; i++) {
|
|
41543
42113
|
const candidate = path47.join(dir, "package.json");
|
|
41544
|
-
if (
|
|
42114
|
+
if (fs44.existsSync(candidate)) {
|
|
41545
42115
|
try {
|
|
41546
|
-
const raw =
|
|
42116
|
+
const raw = fs44.readFileSync(candidate, "utf8");
|
|
41547
42117
|
const parsed = JSON.parse(raw);
|
|
41548
42118
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
41549
42119
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -41574,8 +42144,8 @@ function resolveInstalledBlumaPackage() {
|
|
|
41574
42144
|
if (argv1 && !argv1.startsWith("-")) {
|
|
41575
42145
|
try {
|
|
41576
42146
|
let resolved = argv1;
|
|
41577
|
-
if (path47.isAbsolute(argv1) &&
|
|
41578
|
-
resolved =
|
|
42147
|
+
if (path47.isAbsolute(argv1) && fs44.existsSync(argv1)) {
|
|
42148
|
+
resolved = fs44.realpathSync(argv1);
|
|
41579
42149
|
} else {
|
|
41580
42150
|
resolved = path47.resolve(process.cwd(), argv1);
|
|
41581
42151
|
}
|
|
@@ -42398,16 +42968,16 @@ function usePlanMode() {
|
|
|
42398
42968
|
|
|
42399
42969
|
// src/app/hooks/useAgentMode.ts
|
|
42400
42970
|
import { useState as useState22, useEffect as useEffect20, useCallback as useCallback9 } from "react";
|
|
42401
|
-
import * as
|
|
42971
|
+
import * as fs45 from "fs";
|
|
42402
42972
|
import * as path48 from "path";
|
|
42403
42973
|
import { homedir as homedir3 } from "os";
|
|
42404
42974
|
var SETTINGS_PATH = path48.join(homedir3(), ".bluma", "settings.json");
|
|
42405
42975
|
function readAgentModeFromFile() {
|
|
42406
42976
|
try {
|
|
42407
|
-
if (!
|
|
42977
|
+
if (!fs45.existsSync(SETTINGS_PATH)) {
|
|
42408
42978
|
return "default";
|
|
42409
42979
|
}
|
|
42410
|
-
const content =
|
|
42980
|
+
const content = fs45.readFileSync(SETTINGS_PATH, "utf-8");
|
|
42411
42981
|
const settings = JSON.parse(content);
|
|
42412
42982
|
return settings.agentMode || "default";
|
|
42413
42983
|
} catch (error) {
|
|
@@ -42426,16 +42996,16 @@ function useAgentMode() {
|
|
|
42426
42996
|
}, []);
|
|
42427
42997
|
const updateAgentMode = useCallback9((mode) => {
|
|
42428
42998
|
try {
|
|
42429
|
-
if (!
|
|
42430
|
-
|
|
42999
|
+
if (!fs45.existsSync(SETTINGS_PATH)) {
|
|
43000
|
+
fs45.mkdirSync(path48.dirname(SETTINGS_PATH), { recursive: true });
|
|
42431
43001
|
}
|
|
42432
43002
|
let settings = {};
|
|
42433
|
-
if (
|
|
42434
|
-
const content =
|
|
43003
|
+
if (fs45.existsSync(SETTINGS_PATH)) {
|
|
43004
|
+
const content = fs45.readFileSync(SETTINGS_PATH, "utf-8");
|
|
42435
43005
|
settings = JSON.parse(content);
|
|
42436
43006
|
}
|
|
42437
43007
|
settings.agentMode = mode;
|
|
42438
|
-
|
|
43008
|
+
fs45.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
|
|
42439
43009
|
setAgentMode(mode);
|
|
42440
43010
|
} catch (error) {
|
|
42441
43011
|
console.error("Failed to update agent mode:", error);
|
|
@@ -43691,9 +44261,9 @@ async function runAgentMode() {
|
|
|
43691
44261
|
try {
|
|
43692
44262
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
43693
44263
|
const filePath = args[inputFileIndex + 1];
|
|
43694
|
-
rawPayload =
|
|
44264
|
+
rawPayload = fs46.readFileSync(filePath, "utf-8");
|
|
43695
44265
|
} else {
|
|
43696
|
-
rawPayload =
|
|
44266
|
+
rawPayload = fs46.readFileSync(0, "utf-8");
|
|
43697
44267
|
}
|
|
43698
44268
|
} catch (err) {
|
|
43699
44269
|
writeAgentEvent(registrySessionId, {
|
|
@@ -43893,7 +44463,7 @@ function readCliPackageVersion() {
|
|
|
43893
44463
|
try {
|
|
43894
44464
|
const base = path49.dirname(fileURLToPath7(import.meta.url));
|
|
43895
44465
|
const pkgPath = path49.join(base, "..", "package.json");
|
|
43896
|
-
const j = JSON.parse(
|
|
44466
|
+
const j = JSON.parse(fs46.readFileSync(pkgPath, "utf8"));
|
|
43897
44467
|
return String(j.version || "0.0.0");
|
|
43898
44468
|
} catch {
|
|
43899
44469
|
return "0.0.0";
|
|
@@ -44019,7 +44589,7 @@ function startBackgroundAgent() {
|
|
|
44019
44589
|
process.exit(1);
|
|
44020
44590
|
}
|
|
44021
44591
|
const filePath = args[inputFileIndex + 1];
|
|
44022
|
-
const rawPayload =
|
|
44592
|
+
const rawPayload = fs46.readFileSync(filePath, "utf-8");
|
|
44023
44593
|
const envelope = JSON.parse(rawPayload);
|
|
44024
44594
|
const sessionId = envelope.session_id || envelope.message_id || uuidv412();
|
|
44025
44595
|
registerSession({
|