@nomad-e/bluma-cli 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/bluma-mcp.json +0 -25
- package/dist/config/native_tools.json +3 -3
- package/dist/main.js +639 -531
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -6,8 +6,8 @@ import { EventEmitter as EventEmitter2 } from "events";
|
|
|
6
6
|
import { v4 as uuidv42 } from "uuid";
|
|
7
7
|
|
|
8
8
|
// src/app/ui/App.tsx
|
|
9
|
-
import { useState as
|
|
10
|
-
import { Box as
|
|
9
|
+
import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback, memo as memo4 } from "react";
|
|
10
|
+
import { Box as Box15, Text as Text14, Static } from "ink";
|
|
11
11
|
|
|
12
12
|
// src/app/ui/layout.tsx
|
|
13
13
|
import { Box, Text } from "ink";
|
|
@@ -115,7 +115,7 @@ function inputReducer(state, action, viewWidth) {
|
|
|
115
115
|
};
|
|
116
116
|
switch (action.type) {
|
|
117
117
|
case "INPUT": {
|
|
118
|
-
const cleanInput = action.payload.replace(/(
|
|
118
|
+
const cleanInput = action.payload.replace(/(||)/gm, "");
|
|
119
119
|
const newText = state.text.slice(0, state.cursorPosition) + cleanInput + state.text.slice(state.cursorPosition);
|
|
120
120
|
const newCursorPosition = state.cursorPosition + cleanInput.length;
|
|
121
121
|
const newViewStart = adjustView(newCursorPosition, state.viewStart);
|
|
@@ -140,6 +140,14 @@ function inputReducer(state, action, viewWidth) {
|
|
|
140
140
|
case "SUBMIT": {
|
|
141
141
|
return { text: "", cursorPosition: 0, viewStart: 0 };
|
|
142
142
|
}
|
|
143
|
+
case "SET": {
|
|
144
|
+
const t = action.payload.text.replace(/(||)/gm, "");
|
|
145
|
+
const moveToEnd = action.payload.moveCursorToEnd ?? true;
|
|
146
|
+
const newText = t;
|
|
147
|
+
const newCursorPosition = moveToEnd ? newText.length : Math.min(state.cursorPosition, newText.length);
|
|
148
|
+
const newViewStart = adjustView(newCursorPosition, 0);
|
|
149
|
+
return { text: newText, cursorPosition: newCursorPosition, viewStart: newViewStart };
|
|
150
|
+
}
|
|
143
151
|
default:
|
|
144
152
|
return state;
|
|
145
153
|
}
|
|
@@ -170,6 +178,13 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
|
|
|
170
178
|
return dispatch({ type: "INPUT", payload: input });
|
|
171
179
|
}
|
|
172
180
|
if (key.return) {
|
|
181
|
+
if (globalThis.__BLUMA_AT_OPEN__) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (globalThis.__BLUMA_SUPPRESS_SUBMIT__) {
|
|
185
|
+
globalThis.__BLUMA_SUPPRESS_SUBMIT__ = false;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
173
188
|
if (state.text.trim().length > 0) {
|
|
174
189
|
onSubmit(state.text);
|
|
175
190
|
dispatch({ type: "SUBMIT" });
|
|
@@ -188,12 +203,20 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
|
|
|
188
203
|
return {
|
|
189
204
|
text: state.text,
|
|
190
205
|
cursorPosition: state.cursorPosition,
|
|
191
|
-
viewStart: state.viewStart
|
|
206
|
+
viewStart: state.viewStart,
|
|
207
|
+
setText: (t, pos) => {
|
|
208
|
+
if (typeof pos === "number") {
|
|
209
|
+
dispatch({ type: "SET", payload: { text: t, moveCursorToEnd: false, cursorPosition: pos } });
|
|
210
|
+
} else {
|
|
211
|
+
dispatch({ type: "SET", payload: { text: t, moveCursorToEnd: true } });
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
setCursor: (pos) => dispatch({ type: "SET_CURSOR", payload: pos })
|
|
192
215
|
};
|
|
193
216
|
};
|
|
194
217
|
|
|
195
218
|
// src/app/ui/components/InputPrompt.tsx
|
|
196
|
-
import { useEffect, useMemo, useState } from "react";
|
|
219
|
+
import { useEffect as useEffect2, useMemo, useState as useState2 } from "react";
|
|
197
220
|
import { EventEmitter } from "events";
|
|
198
221
|
|
|
199
222
|
// src/app/ui/utils/slashRegistry.ts
|
|
@@ -242,14 +265,160 @@ var filterSlashCommands = (query) => {
|
|
|
242
265
|
return scored.map((s) => s.cmd);
|
|
243
266
|
};
|
|
244
267
|
|
|
268
|
+
// src/app/ui/hooks/useAtCompletion.ts
|
|
269
|
+
import { useEffect, useRef, useState } from "react";
|
|
270
|
+
import fs from "fs";
|
|
271
|
+
import path from "path";
|
|
272
|
+
var MAX_RESULTS = 50;
|
|
273
|
+
var DEFAULT_RECURSIVE_DEPTH = 2;
|
|
274
|
+
function listPathSuggestions(baseDir, pattern) {
|
|
275
|
+
const raw = pattern || "";
|
|
276
|
+
const patternEndsWithSlash = raw.endsWith("/");
|
|
277
|
+
const relDir = raw.replace(/^\/+|\/+$/g, "");
|
|
278
|
+
const filterPrefix = patternEndsWithSlash ? "" : relDir.split("/").slice(-1)[0] || "";
|
|
279
|
+
const listDir = path.resolve(baseDir, relDir || ".");
|
|
280
|
+
const results = [];
|
|
281
|
+
const IGNORED_DIRS = ["node_modules", ".git", ".venv", "dist", "build"];
|
|
282
|
+
const IGNORED_EXTS = [".pyc", ".class", ".o", ".map", ".log", ".tmp"];
|
|
283
|
+
function isIgnoredName(name) {
|
|
284
|
+
if (!name) return false;
|
|
285
|
+
if (IGNORED_DIRS.includes(name)) return true;
|
|
286
|
+
if (name.startsWith(".")) return true;
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
function isIgnoredFile(name) {
|
|
290
|
+
if (!name) return false;
|
|
291
|
+
for (const e of IGNORED_EXTS) if (name.endsWith(e)) return true;
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
function pushEntry(entryPath, label, isDir) {
|
|
295
|
+
if (results.length >= MAX_RESULTS) return;
|
|
296
|
+
const clean = label.split(path.sep).join("/").replace(/[]+/g, "");
|
|
297
|
+
results.push({ label: clean + (isDir ? "/" : ""), fullPath: entryPath, isDir });
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
if (raw.length === 0 || patternEndsWithSlash) {
|
|
301
|
+
const queue = [{ dir: listDir, depth: 0, rel: relDir }];
|
|
302
|
+
while (queue.length && results.length < MAX_RESULTS) {
|
|
303
|
+
const node = queue.shift();
|
|
304
|
+
try {
|
|
305
|
+
const entries = fs.readdirSync(node.dir, { withFileTypes: true });
|
|
306
|
+
for (const entry of entries) {
|
|
307
|
+
if (isIgnoredName(entry.name)) continue;
|
|
308
|
+
const entryAbs = path.join(node.dir, entry.name);
|
|
309
|
+
const entryRel = node.rel ? path.posix.join(node.rel, entry.name) : entry.name;
|
|
310
|
+
if (entryRel.split("/").includes("node_modules")) continue;
|
|
311
|
+
if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
|
|
312
|
+
pushEntry(entryAbs, entryRel, entry.isDirectory());
|
|
313
|
+
if (entry.isDirectory() && node.depth < DEFAULT_RECURSIVE_DEPTH) {
|
|
314
|
+
queue.push({ dir: entryAbs, depth: node.depth + 1, rel: node.rel ? node.rel + "/" + entry.name : entry.name + "/" });
|
|
315
|
+
}
|
|
316
|
+
if (results.length >= MAX_RESULTS) break;
|
|
317
|
+
}
|
|
318
|
+
} catch (e) {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
const entries = fs.readdirSync(listDir, { withFileTypes: true });
|
|
323
|
+
for (const entry of entries) {
|
|
324
|
+
if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
|
|
325
|
+
if (isIgnoredName(entry.name)) continue;
|
|
326
|
+
if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
|
|
327
|
+
const entryAbs = path.join(listDir, entry.name);
|
|
328
|
+
const label = relDir ? path.posix.join(relDir, entry.name) : entry.name;
|
|
329
|
+
pushEntry(entryAbs, label, entry.isDirectory());
|
|
330
|
+
if (results.length >= MAX_RESULTS) break;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} catch (e) {
|
|
334
|
+
}
|
|
335
|
+
return results.slice(0, MAX_RESULTS);
|
|
336
|
+
}
|
|
337
|
+
function useAtCompletion({
|
|
338
|
+
cwd,
|
|
339
|
+
text,
|
|
340
|
+
cursorPosition,
|
|
341
|
+
setText
|
|
342
|
+
}) {
|
|
343
|
+
const [open, setOpen] = useState(false);
|
|
344
|
+
const [selected, setSelected] = useState(0);
|
|
345
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
346
|
+
const lastQuery = useRef("");
|
|
347
|
+
function scanForAt(text2, pos) {
|
|
348
|
+
const before = text2.slice(0, pos);
|
|
349
|
+
const m = before.match(/@([\w\/.\-_]*)$/);
|
|
350
|
+
if (!m) return { pattern: null, insertStart: -1 };
|
|
351
|
+
return { pattern: m[1] || "", insertStart: m.index + 1 };
|
|
352
|
+
}
|
|
353
|
+
function update(newText, newCursor) {
|
|
354
|
+
const res = scanForAt(newText, newCursor);
|
|
355
|
+
if (res.pattern !== null && res.pattern.length >= 0) {
|
|
356
|
+
setOpen(true);
|
|
357
|
+
globalThis.__BLUMA_AT_OPEN__ = true;
|
|
358
|
+
const suggs = listPathSuggestions(cwd, res.pattern);
|
|
359
|
+
setSuggestions(suggs);
|
|
360
|
+
setSelected(0);
|
|
361
|
+
lastQuery.current = res.pattern;
|
|
362
|
+
} else {
|
|
363
|
+
setOpen(false);
|
|
364
|
+
globalThis.__BLUMA_AT_OPEN__ = false;
|
|
365
|
+
setSuggestions([]);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
useEffect(() => {
|
|
369
|
+
update(text, cursorPosition);
|
|
370
|
+
}, [text, cursorPosition, cwd]);
|
|
371
|
+
function insertAtSelection() {
|
|
372
|
+
if (!open || !suggestions[selected]) return;
|
|
373
|
+
const res = scanForAt(text, cursorPosition);
|
|
374
|
+
if (!res || res.insertStart < 0) return;
|
|
375
|
+
let chosen = suggestions[selected].label;
|
|
376
|
+
const isDir = suggestions[selected].isDir;
|
|
377
|
+
chosen = chosen.replace(/\\/g, "/").replace(/\|/g, "");
|
|
378
|
+
let insertVal = chosen;
|
|
379
|
+
if (insertVal.includes("/")) {
|
|
380
|
+
insertVal = insertVal.replace(/\/+$/g, "");
|
|
381
|
+
const parts = insertVal.split("/");
|
|
382
|
+
insertVal = parts[parts.length - 1];
|
|
383
|
+
}
|
|
384
|
+
if (isDir && !insertVal.endsWith("/")) insertVal = insertVal + "/";
|
|
385
|
+
const pattern = res.pattern || "";
|
|
386
|
+
const lastSlash = pattern.lastIndexOf("/");
|
|
387
|
+
const segmentOffset = lastSlash >= 0 ? lastSlash + 1 : 0;
|
|
388
|
+
const segmentStart = res.insertStart + segmentOffset;
|
|
389
|
+
const before = text.slice(0, segmentStart);
|
|
390
|
+
const after = text.slice(cursorPosition);
|
|
391
|
+
const newText = before + insertVal + after;
|
|
392
|
+
setText(newText + (isDir ? "" : " "), true);
|
|
393
|
+
if (isDir) {
|
|
394
|
+
setOpen(false);
|
|
395
|
+
setSuggestions([]);
|
|
396
|
+
setTimeout(() => {
|
|
397
|
+
setOpen(true);
|
|
398
|
+
update(newText, newText.length);
|
|
399
|
+
}, 0);
|
|
400
|
+
} else {
|
|
401
|
+
setOpen(false);
|
|
402
|
+
setSuggestions([]);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function close() {
|
|
406
|
+
setOpen(false);
|
|
407
|
+
setSuggestions([]);
|
|
408
|
+
}
|
|
409
|
+
return { open, suggestions, selected, setSelected, insertAtSelection, close, update };
|
|
410
|
+
}
|
|
411
|
+
|
|
245
412
|
// src/app/ui/components/InputPrompt.tsx
|
|
246
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
413
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
247
414
|
var uiEventBus = global.__bluma_ui_eventbus__ || new EventEmitter();
|
|
248
415
|
global.__bluma_ui_eventbus__ = uiEventBus;
|
|
249
416
|
var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing = false }) => {
|
|
250
417
|
const { stdout } = useStdout();
|
|
251
|
-
const [viewWidth, setViewWidth] =
|
|
252
|
-
|
|
418
|
+
const [viewWidth, setViewWidth] = useState2(() => stdout.columns - 6);
|
|
419
|
+
const [slashOpen, setSlashOpen] = useState2(false);
|
|
420
|
+
const [slashIndex, setSlashIndex] = useState2(0);
|
|
421
|
+
useEffect2(() => {
|
|
253
422
|
const onResize = () => setViewWidth(stdout.columns - 6);
|
|
254
423
|
stdout.on("resize", onResize);
|
|
255
424
|
return () => {
|
|
@@ -261,43 +430,40 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
|
|
|
261
430
|
if (isReadOnly) {
|
|
262
431
|
if (trimmed.length > 0) {
|
|
263
432
|
const payload = trimmed;
|
|
264
|
-
uiEventBus.emit("
|
|
433
|
+
uiEventBus.emit("user_overlay", { kind: "message", payload, ts: Date.now() });
|
|
265
434
|
return;
|
|
266
435
|
}
|
|
267
436
|
return;
|
|
268
437
|
}
|
|
269
438
|
onSubmit(value);
|
|
270
439
|
};
|
|
271
|
-
const effectiveReadOnly =
|
|
272
|
-
const { text, cursorPosition, viewStart } = useCustomInput({
|
|
440
|
+
const effectiveReadOnly = isReadOnly;
|
|
441
|
+
const { text, cursorPosition, viewStart, setText } = useCustomInput({
|
|
442
|
+
// Sobrepõe a lógica padrão: nunca submete se autocomplete aberto
|
|
273
443
|
onSubmit: (value) => {
|
|
274
444
|
if (disableWhileProcessing && isReadOnly) return;
|
|
445
|
+
if (pathAutocomplete.open) return;
|
|
275
446
|
permissiveOnSubmit(value);
|
|
276
447
|
},
|
|
277
448
|
viewWidth,
|
|
278
449
|
isReadOnly: effectiveReadOnly,
|
|
279
450
|
onInterrupt
|
|
280
451
|
});
|
|
281
|
-
const [slashOpen, setSlashOpen] = useState(false);
|
|
282
|
-
const [slashIndex, setSlashIndex] = useState(0);
|
|
283
452
|
const visibleText = text.slice(viewStart, viewStart + viewWidth);
|
|
284
453
|
const visibleCursorPosition = cursorPosition - viewStart;
|
|
285
454
|
const textBeforeCursor = visibleText.slice(0, visibleCursorPosition);
|
|
286
|
-
const charAtCursor = visibleText.slice(
|
|
287
|
-
visibleCursorPosition,
|
|
288
|
-
visibleCursorPosition + 1
|
|
289
|
-
);
|
|
455
|
+
const charAtCursor = visibleText.slice(visibleCursorPosition, visibleCursorPosition + 1);
|
|
290
456
|
const textAfterCursor = visibleText.slice(visibleCursorPosition + 1);
|
|
291
457
|
const cursorGlyph = charAtCursor && charAtCursor.length > 0 ? charAtCursor : " ";
|
|
292
458
|
const borderColor = isReadOnly ? "gray" : "gray";
|
|
293
|
-
const placeholder = isReadOnly ? "
|
|
459
|
+
const placeholder = isReadOnly ? " Press Esc to cancel | Enter message while agent runs" : "";
|
|
294
460
|
const showPlaceholder = text.length === 0 && isReadOnly;
|
|
295
461
|
const slashQuery = useMemo(() => text.startsWith("/") ? text : "", [text]);
|
|
296
462
|
const slashSuggestions = useMemo(() => {
|
|
297
463
|
if (!slashQuery) return [];
|
|
298
464
|
return filterSlashCommands(slashQuery);
|
|
299
465
|
}, [slashQuery]);
|
|
300
|
-
|
|
466
|
+
useEffect2(() => {
|
|
301
467
|
if (isReadOnly) {
|
|
302
468
|
setSlashOpen(false);
|
|
303
469
|
return;
|
|
@@ -320,36 +486,102 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
|
|
|
320
486
|
if (choice) {
|
|
321
487
|
const cmd = choice.name;
|
|
322
488
|
setSlashOpen(false);
|
|
323
|
-
|
|
489
|
+
try {
|
|
490
|
+
setText(`${cmd} `, true);
|
|
491
|
+
} catch (e) {
|
|
492
|
+
permissiveOnSubmit(`${cmd} `);
|
|
493
|
+
}
|
|
324
494
|
}
|
|
325
495
|
} else if (key.escape) {
|
|
326
496
|
setSlashOpen(false);
|
|
327
497
|
}
|
|
328
498
|
}, { isActive: slashOpen });
|
|
499
|
+
const cwd = process.cwd();
|
|
500
|
+
const pathAutocomplete = useAtCompletion({ cwd, text, cursorPosition, setText });
|
|
501
|
+
useInput2((input, key) => {
|
|
502
|
+
if (pathAutocomplete.open) {
|
|
503
|
+
if (key.downArrow) {
|
|
504
|
+
pathAutocomplete.setSelected((i) => Math.min(i + 1, Math.max(0, pathAutocomplete.suggestions.length - 1)));
|
|
505
|
+
return;
|
|
506
|
+
} else if (key.upArrow) {
|
|
507
|
+
pathAutocomplete.setSelected((i) => Math.max(i - 1, 0));
|
|
508
|
+
return;
|
|
509
|
+
} else if (key.return || key.tab) {
|
|
510
|
+
const selected = pathAutocomplete.suggestions[pathAutocomplete.selected];
|
|
511
|
+
if (selected) {
|
|
512
|
+
const before = text.slice(0, cursorPosition);
|
|
513
|
+
const m = before.match(/@([\w\/.\-_]*)$/);
|
|
514
|
+
if (m) {
|
|
515
|
+
globalThis.__BLUMA_SUPPRESS_SUBMIT__ = true;
|
|
516
|
+
pathAutocomplete.insertAtSelection();
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return;
|
|
520
|
+
} else if (key.escape) {
|
|
521
|
+
pathAutocomplete.close();
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
}, { isActive: true });
|
|
527
|
+
function canSubmitGivenCursor() {
|
|
528
|
+
if (visibleCursorPosition < visibleText.length) {
|
|
529
|
+
return visibleText[visibleCursorPosition] === " ";
|
|
530
|
+
} else {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
329
534
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
"
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
535
|
+
disableWhileProcessing ? (
|
|
536
|
+
// Modo bloqueado visualmente, mantendo hooks estáveis
|
|
537
|
+
/* @__PURE__ */ jsx2(Fragment, { children: /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor: "gray", borderDimColor: true, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
|
|
538
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "white", children: [
|
|
539
|
+
">",
|
|
540
|
+
" "
|
|
541
|
+
] }),
|
|
542
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "ctrl+c to exit" })
|
|
543
|
+
] }) }) })
|
|
544
|
+
) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
545
|
+
/* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor, borderDimColor: !isReadOnly, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
|
|
546
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "white", children: [
|
|
547
|
+
">",
|
|
548
|
+
" "
|
|
549
|
+
] }),
|
|
550
|
+
/* @__PURE__ */ jsx2(Text2, { children: textBeforeCursor }),
|
|
551
|
+
/* @__PURE__ */ jsx2(Text2, { inverse: true, children: cursorGlyph }),
|
|
552
|
+
showPlaceholder ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: placeholder }) : /* @__PURE__ */ jsx2(Text2, { children: textAfterCursor })
|
|
553
|
+
] }) }),
|
|
554
|
+
pathAutocomplete.open && pathAutocomplete.suggestions.length > 0 && (() => {
|
|
555
|
+
const VISIBLE = 7;
|
|
556
|
+
const total = pathAutocomplete.suggestions.length;
|
|
557
|
+
const sel = Math.max(0, Math.min(pathAutocomplete.selected, total - 1));
|
|
558
|
+
let start = Math.max(0, sel - Math.floor(VISIBLE / 2));
|
|
559
|
+
if (start + VISIBLE > total) start = Math.max(0, total - VISIBLE);
|
|
560
|
+
const windowItems = pathAutocomplete.suggestions.slice(start, start + VISIBLE);
|
|
561
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, height: Math.min(VISIBLE, total), overflowY: "auto", children: windowItems.map((s, idx) => {
|
|
562
|
+
const realIdx = start + idx;
|
|
563
|
+
const isSelected = realIdx === pathAutocomplete.selected;
|
|
564
|
+
return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
|
|
565
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "cyan" : "gray", children: isSelected ? "\u276F " : " " }),
|
|
566
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "cyan" : "white", bold: isSelected, dimColor: !isSelected, children: s.label })
|
|
567
|
+
] }, s.fullPath);
|
|
568
|
+
}) });
|
|
569
|
+
})(),
|
|
570
|
+
slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: slashSuggestions.map((s, idx) => {
|
|
571
|
+
const isSelected = idx === slashIndex;
|
|
572
|
+
return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
|
|
573
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "blue" : "gray", children: isSelected ? "\u276F " : " " }),
|
|
574
|
+
/* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "blue" : "white", bold: isSelected, dimColor: !isSelected, children: [
|
|
575
|
+
s.name,
|
|
576
|
+
" ",
|
|
577
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
578
|
+
"- ",
|
|
579
|
+
s.description
|
|
580
|
+
] })
|
|
349
581
|
] })
|
|
350
|
-
] })
|
|
351
|
-
|
|
352
|
-
|
|
582
|
+
] }, s.name);
|
|
583
|
+
}) })
|
|
584
|
+
] }),
|
|
353
585
|
/* @__PURE__ */ jsx2(Box2, { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", dimColor: true, children: [
|
|
354
586
|
"ctrl+c to exit | /help to explore commands | esc to interrupt | ",
|
|
355
587
|
isReadOnly ? "Read-only mode (message passthrough)" : "Editable mode"
|
|
@@ -361,7 +593,7 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
|
|
|
361
593
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
362
594
|
|
|
363
595
|
// src/app/ui/InteractiveMenu.tsx
|
|
364
|
-
import { useState as
|
|
596
|
+
import { useState as useState3, memo } from "react";
|
|
365
597
|
import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
|
|
366
598
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
367
599
|
var InteractiveMenuComponent = ({ onDecision }) => {
|
|
@@ -370,7 +602,7 @@ var InteractiveMenuComponent = ({ onDecision }) => {
|
|
|
370
602
|
{ label: "2. No, cancel this command", value: "decline" },
|
|
371
603
|
{ label: "3. Always allow this type of command", value: "accept_always" }
|
|
372
604
|
];
|
|
373
|
-
const [selectedOption, setSelectedOption] =
|
|
605
|
+
const [selectedOption, setSelectedOption] = useState3(0);
|
|
374
606
|
useInput3((input, key) => {
|
|
375
607
|
if (key.upArrow) {
|
|
376
608
|
setSelectedOption((prev) => prev > 0 ? prev - 1 : options.length - 1);
|
|
@@ -411,7 +643,7 @@ var InteractiveMenu = memo(InteractiveMenuComponent);
|
|
|
411
643
|
|
|
412
644
|
// src/app/ui/components/promptRenderers.tsx
|
|
413
645
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
414
|
-
import
|
|
646
|
+
import path2 from "path";
|
|
415
647
|
|
|
416
648
|
// src/app/ui/components/SimpleDiff.tsx
|
|
417
649
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
@@ -450,7 +682,7 @@ var SimpleDiff = ({ text, maxHeight }) => {
|
|
|
450
682
|
// src/app/ui/components/promptRenderers.tsx
|
|
451
683
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
452
684
|
var getBasePath = (filePath) => {
|
|
453
|
-
return
|
|
685
|
+
return path2.basename(filePath);
|
|
454
686
|
};
|
|
455
687
|
var renderShellCommand = ({
|
|
456
688
|
toolCall
|
|
@@ -618,12 +850,12 @@ var ConfirmationPrompt = ({ toolCalls, preview, onDecision }) => {
|
|
|
618
850
|
// src/app/agent/agent.ts
|
|
619
851
|
import OpenAI from "openai";
|
|
620
852
|
import * as dotenv from "dotenv";
|
|
621
|
-
import
|
|
853
|
+
import path9 from "path";
|
|
622
854
|
import os6 from "os";
|
|
623
855
|
|
|
624
856
|
// src/app/agent/tool_invoker.ts
|
|
625
|
-
import { promises as
|
|
626
|
-
import
|
|
857
|
+
import { promises as fs6 } from "fs";
|
|
858
|
+
import path5 from "path";
|
|
627
859
|
import { fileURLToPath } from "url";
|
|
628
860
|
|
|
629
861
|
// src/app/agent/tools/natives/shell_command.ts
|
|
@@ -691,8 +923,8 @@ ${stderr.trim()}`.trim();
|
|
|
691
923
|
}
|
|
692
924
|
|
|
693
925
|
// src/app/agent/tools/natives/edit.ts
|
|
694
|
-
import
|
|
695
|
-
import { promises as
|
|
926
|
+
import path3 from "path";
|
|
927
|
+
import { promises as fs2 } from "fs";
|
|
696
928
|
import { diffLines } from "diff";
|
|
697
929
|
function unescapeLlmString(inputString) {
|
|
698
930
|
return inputString.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r").replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, "\\");
|
|
@@ -728,7 +960,7 @@ async function calculateEdit(filePath, oldString, newString, expectedReplacement
|
|
|
728
960
|
let finalOldString = oldString.replace(/\r\n/g, "\n");
|
|
729
961
|
let occurrences = 0;
|
|
730
962
|
try {
|
|
731
|
-
currentContent = await
|
|
963
|
+
currentContent = await fs2.readFile(filePath, "utf-8");
|
|
732
964
|
currentContent = currentContent.replace(/\r\n/g, "\n");
|
|
733
965
|
} catch (e) {
|
|
734
966
|
if (e.code !== "ENOENT") {
|
|
@@ -784,7 +1016,7 @@ function createDiff(filename, oldContent, newContent) {
|
|
|
784
1016
|
}
|
|
785
1017
|
async function editTool(args) {
|
|
786
1018
|
const { file_path, old_string, new_string, expected_replacements = 1 } = args;
|
|
787
|
-
if (!
|
|
1019
|
+
if (!path3.isAbsolute(file_path)) {
|
|
788
1020
|
return { success: false, error: `Invalid parameters: file_path must be absolute.`, file_path };
|
|
789
1021
|
}
|
|
790
1022
|
if (file_path.includes("..")) {
|
|
@@ -800,10 +1032,10 @@ async function editTool(args) {
|
|
|
800
1032
|
file_path
|
|
801
1033
|
};
|
|
802
1034
|
}
|
|
803
|
-
await
|
|
804
|
-
await
|
|
805
|
-
const relativePath =
|
|
806
|
-
const filename =
|
|
1035
|
+
await fs2.mkdir(path3.dirname(file_path), { recursive: true });
|
|
1036
|
+
await fs2.writeFile(file_path, editData.newContent, "utf-8");
|
|
1037
|
+
const relativePath = path3.relative(process.cwd(), file_path);
|
|
1038
|
+
const filename = path3.basename(file_path);
|
|
807
1039
|
if (editData.isNewFile) {
|
|
808
1040
|
return {
|
|
809
1041
|
success: true,
|
|
@@ -838,10 +1070,10 @@ ${finalDiff}`,
|
|
|
838
1070
|
|
|
839
1071
|
// src/app/agent/tools/natives/message.ts
|
|
840
1072
|
import { v4 as uuidv4 } from "uuid";
|
|
841
|
-
function
|
|
1073
|
+
function messageNotifyuser(args) {
|
|
842
1074
|
const { text_markdown } = args;
|
|
843
1075
|
const notification = {
|
|
844
|
-
type: "
|
|
1076
|
+
type: "message_notify_user",
|
|
845
1077
|
id: `notify_${uuidv4()}`,
|
|
846
1078
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
847
1079
|
content: {
|
|
@@ -855,8 +1087,8 @@ function messageNotifyDev(args) {
|
|
|
855
1087
|
}
|
|
856
1088
|
|
|
857
1089
|
// src/app/agent/tools/natives/ls.ts
|
|
858
|
-
import { promises as
|
|
859
|
-
import
|
|
1090
|
+
import { promises as fs3 } from "fs";
|
|
1091
|
+
import path4 from "path";
|
|
860
1092
|
var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
|
|
861
1093
|
".git",
|
|
862
1094
|
".gitignore",
|
|
@@ -884,8 +1116,8 @@ async function ls(args) {
|
|
|
884
1116
|
max_depth
|
|
885
1117
|
} = args;
|
|
886
1118
|
try {
|
|
887
|
-
const basePath =
|
|
888
|
-
if (!(await
|
|
1119
|
+
const basePath = path4.resolve(directory_path);
|
|
1120
|
+
if (!(await fs3.stat(basePath)).isDirectory()) {
|
|
889
1121
|
throw new Error(`Directory '${directory_path}' not found.`);
|
|
890
1122
|
}
|
|
891
1123
|
const allIgnorePatterns = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore_patterns]);
|
|
@@ -894,11 +1126,11 @@ async function ls(args) {
|
|
|
894
1126
|
const allDirs = [];
|
|
895
1127
|
const walk = async (currentDir, currentDepth) => {
|
|
896
1128
|
if (max_depth !== void 0 && currentDepth > max_depth) return;
|
|
897
|
-
const entries = await
|
|
1129
|
+
const entries = await fs3.readdir(currentDir, { withFileTypes: true });
|
|
898
1130
|
for (const entry of entries) {
|
|
899
1131
|
const entryName = entry.name;
|
|
900
|
-
const fullPath =
|
|
901
|
-
const posixPath = fullPath.split(
|
|
1132
|
+
const fullPath = path4.join(currentDir, entryName);
|
|
1133
|
+
const posixPath = fullPath.split(path4.sep).join("/");
|
|
902
1134
|
const isHidden = entryName.startsWith(".");
|
|
903
1135
|
if (allIgnorePatterns.has(entryName) || isHidden && !show_hidden) {
|
|
904
1136
|
continue;
|
|
@@ -909,7 +1141,7 @@ async function ls(args) {
|
|
|
909
1141
|
await walk(fullPath, currentDepth + 1);
|
|
910
1142
|
}
|
|
911
1143
|
} else if (entry.isFile()) {
|
|
912
|
-
if (!normalizedExtensions || normalizedExtensions.includes(
|
|
1144
|
+
if (!normalizedExtensions || normalizedExtensions.includes(path4.extname(entryName).toLowerCase())) {
|
|
913
1145
|
allFiles.push(posixPath);
|
|
914
1146
|
}
|
|
915
1147
|
}
|
|
@@ -920,7 +1152,7 @@ async function ls(args) {
|
|
|
920
1152
|
allDirs.sort();
|
|
921
1153
|
return {
|
|
922
1154
|
success: true,
|
|
923
|
-
path: basePath.split(
|
|
1155
|
+
path: basePath.split(path4.sep).join("/"),
|
|
924
1156
|
recursive,
|
|
925
1157
|
total_files: allFiles.length,
|
|
926
1158
|
total_directories: allDirs.length,
|
|
@@ -936,17 +1168,17 @@ async function ls(args) {
|
|
|
936
1168
|
}
|
|
937
1169
|
|
|
938
1170
|
// src/app/agent/tools/natives/readLines.ts
|
|
939
|
-
import { promises as
|
|
1171
|
+
import { promises as fs4 } from "fs";
|
|
940
1172
|
async function readLines(args) {
|
|
941
1173
|
const { filepath, start_line, end_line } = args;
|
|
942
1174
|
try {
|
|
943
|
-
if (!(await
|
|
1175
|
+
if (!(await fs4.stat(filepath)).isFile()) {
|
|
944
1176
|
throw new Error(`File '${filepath}' not found or is not a file.`);
|
|
945
1177
|
}
|
|
946
1178
|
if (start_line < 1 || end_line < start_line) {
|
|
947
1179
|
throw new Error("Invalid line range. start_line must be >= 1 and end_line must be >= start_line.");
|
|
948
1180
|
}
|
|
949
|
-
const fileContent = await
|
|
1181
|
+
const fileContent = await fs4.readFile(filepath, "utf-8");
|
|
950
1182
|
const lines = fileContent.split("\n");
|
|
951
1183
|
const total_lines = lines.length;
|
|
952
1184
|
const startIndex = start_line - 1;
|
|
@@ -974,12 +1206,12 @@ async function readLines(args) {
|
|
|
974
1206
|
|
|
975
1207
|
// src/app/agent/tools/natives/count_lines.ts
|
|
976
1208
|
import { createReadStream } from "fs";
|
|
977
|
-
import { promises as
|
|
1209
|
+
import { promises as fs5 } from "fs";
|
|
978
1210
|
import readline from "readline";
|
|
979
1211
|
async function countLines(args) {
|
|
980
1212
|
const { filepath } = args;
|
|
981
1213
|
try {
|
|
982
|
-
if (!(await
|
|
1214
|
+
if (!(await fs5.stat(filepath)).isFile()) {
|
|
983
1215
|
throw new Error(`File '${filepath}' not found or is not a file.`);
|
|
984
1216
|
}
|
|
985
1217
|
const fileStream = createReadStream(filepath);
|
|
@@ -1014,9 +1246,9 @@ var ToolInvoker = class {
|
|
|
1014
1246
|
async initialize() {
|
|
1015
1247
|
try {
|
|
1016
1248
|
const __filename = fileURLToPath(import.meta.url);
|
|
1017
|
-
const __dirname =
|
|
1018
|
-
const configPath =
|
|
1019
|
-
const fileContent = await
|
|
1249
|
+
const __dirname = path5.dirname(__filename);
|
|
1250
|
+
const configPath = path5.resolve(__dirname, "config", "native_tools.json");
|
|
1251
|
+
const fileContent = await fs6.readFile(configPath, "utf-8");
|
|
1020
1252
|
const config2 = JSON.parse(fileContent);
|
|
1021
1253
|
this.toolDefinitions = config2.nativeTools;
|
|
1022
1254
|
} catch (error) {
|
|
@@ -1031,7 +1263,7 @@ var ToolInvoker = class {
|
|
|
1031
1263
|
registerTools() {
|
|
1032
1264
|
this.toolImplementations.set("shell_command", shellCommand);
|
|
1033
1265
|
this.toolImplementations.set("edit_tool", editTool);
|
|
1034
|
-
this.toolImplementations.set("
|
|
1266
|
+
this.toolImplementations.set("message_notify_user", messageNotifyuser);
|
|
1035
1267
|
this.toolImplementations.set("ls_tool", ls);
|
|
1036
1268
|
this.toolImplementations.set("count_file_lines", countLines);
|
|
1037
1269
|
this.toolImplementations.set("read_file_lines", readLines);
|
|
@@ -1065,8 +1297,8 @@ var ToolInvoker = class {
|
|
|
1065
1297
|
};
|
|
1066
1298
|
|
|
1067
1299
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
1068
|
-
import { promises as
|
|
1069
|
-
import
|
|
1300
|
+
import { promises as fs7 } from "fs";
|
|
1301
|
+
import path6 from "path";
|
|
1070
1302
|
import os2 from "os";
|
|
1071
1303
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1072
1304
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -1094,9 +1326,9 @@ var MCPClient = class {
|
|
|
1094
1326
|
});
|
|
1095
1327
|
}
|
|
1096
1328
|
const __filename = fileURLToPath2(import.meta.url);
|
|
1097
|
-
const __dirname =
|
|
1098
|
-
const defaultConfigPath =
|
|
1099
|
-
const userConfigPath =
|
|
1329
|
+
const __dirname = path6.dirname(__filename);
|
|
1330
|
+
const defaultConfigPath = path6.resolve(__dirname, "config", "bluma-mcp.json");
|
|
1331
|
+
const userConfigPath = path6.join(os2.homedir(), ".bluma-cli", "bluma-mcp.json");
|
|
1100
1332
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
1101
1333
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
1102
1334
|
const mergedConfig = {
|
|
@@ -1130,7 +1362,7 @@ var MCPClient = class {
|
|
|
1130
1362
|
}
|
|
1131
1363
|
async loadMcpConfig(configPath, configType) {
|
|
1132
1364
|
try {
|
|
1133
|
-
const fileContent = await
|
|
1365
|
+
const fileContent = await fs7.readFile(configPath, "utf-8");
|
|
1134
1366
|
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
1135
1367
|
return JSON.parse(processedContent);
|
|
1136
1368
|
} catch (error) {
|
|
@@ -1189,14 +1421,14 @@ var MCPClient = class {
|
|
|
1189
1421
|
async invoke(toolName, args) {
|
|
1190
1422
|
const route = this.toolToServerMap.get(toolName);
|
|
1191
1423
|
if (!route) {
|
|
1192
|
-
return { error: `
|
|
1424
|
+
return { error: `This tool '${toolName}'is not found or registered you must use the correct name of this tool.` };
|
|
1193
1425
|
}
|
|
1194
1426
|
if (route.server === "native") {
|
|
1195
1427
|
return this.nativeToolInvoker.invoke(route.originalName, args);
|
|
1196
1428
|
} else {
|
|
1197
1429
|
const session = this.sessions.get(route.server);
|
|
1198
1430
|
if (!session) {
|
|
1199
|
-
return { error: `
|
|
1431
|
+
return { error: `Session to the server '${route.server}' n\xE3o encontrada.` };
|
|
1200
1432
|
}
|
|
1201
1433
|
const result = await session.callTool({ name: route.originalName, arguments: args });
|
|
1202
1434
|
return result.content;
|
|
@@ -1223,7 +1455,7 @@ var MCPClient = class {
|
|
|
1223
1455
|
try {
|
|
1224
1456
|
await session.close();
|
|
1225
1457
|
} catch (error) {
|
|
1226
|
-
console.error(`[MCPClient]
|
|
1458
|
+
console.error(`[MCPClient]Error closing connection to'${name}':`, error);
|
|
1227
1459
|
}
|
|
1228
1460
|
}
|
|
1229
1461
|
}
|
|
@@ -1248,12 +1480,12 @@ var AdvancedFeedbackSystem = class {
|
|
|
1248
1480
|
this.cumulativeScore += penalty;
|
|
1249
1481
|
return {
|
|
1250
1482
|
score: penalty,
|
|
1251
|
-
message: "
|
|
1483
|
+
message: "You are attempting a direct message without a tool_call. All replies must contain tool_call.",
|
|
1252
1484
|
correction: `
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1485
|
+
## PROTOCOL VIOLATION \u2014 SERIOUS
|
|
1486
|
+
You are sending a direct response without tool_call, which is strictly prohibited.
|
|
1487
|
+
PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
|
|
1488
|
+
You MUST always use tool_call without exception.
|
|
1257
1489
|
`.trim()
|
|
1258
1490
|
};
|
|
1259
1491
|
}
|
|
@@ -1265,12 +1497,12 @@ var AdvancedFeedbackSystem = class {
|
|
|
1265
1497
|
};
|
|
1266
1498
|
|
|
1267
1499
|
// src/app/agent/bluma/core/bluma.ts
|
|
1268
|
-
import
|
|
1500
|
+
import path8 from "path";
|
|
1269
1501
|
|
|
1270
1502
|
// src/app/agent/session_manger/session_manager.ts
|
|
1271
|
-
import
|
|
1503
|
+
import path7 from "path";
|
|
1272
1504
|
import os3 from "os";
|
|
1273
|
-
import { promises as
|
|
1505
|
+
import { promises as fs8 } from "fs";
|
|
1274
1506
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
1275
1507
|
async function withFileLock(file, fn) {
|
|
1276
1508
|
const prev = fileLocks.get(file) || Promise.resolve();
|
|
@@ -1288,13 +1520,13 @@ async function withFileLock(file, fn) {
|
|
|
1288
1520
|
function expandHome(p) {
|
|
1289
1521
|
if (!p) return p;
|
|
1290
1522
|
if (p.startsWith("~")) {
|
|
1291
|
-
return
|
|
1523
|
+
return path7.join(os3.homedir(), p.slice(1));
|
|
1292
1524
|
}
|
|
1293
1525
|
return p;
|
|
1294
1526
|
}
|
|
1295
1527
|
function getPreferredAppDir() {
|
|
1296
|
-
const fixed =
|
|
1297
|
-
return
|
|
1528
|
+
const fixed = path7.join(os3.homedir(), ".bluma-cli");
|
|
1529
|
+
return path7.resolve(expandHome(fixed));
|
|
1298
1530
|
}
|
|
1299
1531
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
1300
1532
|
let attempt = 0;
|
|
@@ -1302,7 +1534,7 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
1302
1534
|
const isWin = process.platform === "win32";
|
|
1303
1535
|
while (attempt <= maxRetries) {
|
|
1304
1536
|
try {
|
|
1305
|
-
await
|
|
1537
|
+
await fs8.rename(src, dest);
|
|
1306
1538
|
return;
|
|
1307
1539
|
} catch (e) {
|
|
1308
1540
|
lastErr = e;
|
|
@@ -1315,9 +1547,9 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
1315
1547
|
}
|
|
1316
1548
|
}
|
|
1317
1549
|
try {
|
|
1318
|
-
const data = await
|
|
1319
|
-
await
|
|
1320
|
-
await
|
|
1550
|
+
const data = await fs8.readFile(src);
|
|
1551
|
+
await fs8.writeFile(dest, data);
|
|
1552
|
+
await fs8.unlink(src).catch(() => {
|
|
1321
1553
|
});
|
|
1322
1554
|
return;
|
|
1323
1555
|
} catch (fallbackErr) {
|
|
@@ -1326,16 +1558,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
1326
1558
|
}
|
|
1327
1559
|
async function ensureSessionDir() {
|
|
1328
1560
|
const appDir = getPreferredAppDir();
|
|
1329
|
-
const sessionDir =
|
|
1330
|
-
await
|
|
1561
|
+
const sessionDir = path7.join(appDir, "sessions");
|
|
1562
|
+
await fs8.mkdir(sessionDir, { recursive: true });
|
|
1331
1563
|
return sessionDir;
|
|
1332
1564
|
}
|
|
1333
1565
|
async function loadOrcreateSession(sessionId2) {
|
|
1334
1566
|
const sessionDir = await ensureSessionDir();
|
|
1335
|
-
const sessionFile =
|
|
1567
|
+
const sessionFile = path7.join(sessionDir, `${sessionId2}.json`);
|
|
1336
1568
|
try {
|
|
1337
|
-
await
|
|
1338
|
-
const fileContent = await
|
|
1569
|
+
await fs8.access(sessionFile);
|
|
1570
|
+
const fileContent = await fs8.readFile(sessionFile, "utf-8");
|
|
1339
1571
|
const sessionData = JSON.parse(fileContent);
|
|
1340
1572
|
return [sessionFile, sessionData.conversation_history || []];
|
|
1341
1573
|
} catch (error) {
|
|
@@ -1344,7 +1576,7 @@ async function loadOrcreateSession(sessionId2) {
|
|
|
1344
1576
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1345
1577
|
conversation_history: []
|
|
1346
1578
|
};
|
|
1347
|
-
await
|
|
1579
|
+
await fs8.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
1348
1580
|
return [sessionFile, []];
|
|
1349
1581
|
}
|
|
1350
1582
|
}
|
|
@@ -1352,12 +1584,12 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
1352
1584
|
await withFileLock(sessionFile, async () => {
|
|
1353
1585
|
let sessionData;
|
|
1354
1586
|
try {
|
|
1355
|
-
const dir =
|
|
1356
|
-
await
|
|
1587
|
+
const dir = path7.dirname(sessionFile);
|
|
1588
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
1357
1589
|
} catch {
|
|
1358
1590
|
}
|
|
1359
1591
|
try {
|
|
1360
|
-
const fileContent = await
|
|
1592
|
+
const fileContent = await fs8.readFile(sessionFile, "utf-8");
|
|
1361
1593
|
sessionData = JSON.parse(fileContent);
|
|
1362
1594
|
} catch (error) {
|
|
1363
1595
|
const code = error && error.code;
|
|
@@ -1368,14 +1600,14 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
1368
1600
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
1369
1601
|
}
|
|
1370
1602
|
}
|
|
1371
|
-
const sessionId2 =
|
|
1603
|
+
const sessionId2 = path7.basename(sessionFile, ".json");
|
|
1372
1604
|
sessionData = {
|
|
1373
1605
|
session_id: sessionId2,
|
|
1374
1606
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1375
1607
|
conversation_history: []
|
|
1376
1608
|
};
|
|
1377
1609
|
try {
|
|
1378
|
-
await
|
|
1610
|
+
await fs8.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
1379
1611
|
} catch {
|
|
1380
1612
|
}
|
|
1381
1613
|
}
|
|
@@ -1383,7 +1615,7 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
1383
1615
|
sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
1384
1616
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
1385
1617
|
try {
|
|
1386
|
-
await
|
|
1618
|
+
await fs8.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
1387
1619
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
1388
1620
|
} catch (writeError) {
|
|
1389
1621
|
if (writeError instanceof Error) {
|
|
@@ -1392,7 +1624,7 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
1392
1624
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
1393
1625
|
}
|
|
1394
1626
|
try {
|
|
1395
|
-
await
|
|
1627
|
+
await fs8.unlink(tempSessionFile);
|
|
1396
1628
|
} catch {
|
|
1397
1629
|
}
|
|
1398
1630
|
}
|
|
@@ -1403,320 +1635,73 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
1403
1635
|
import os4 from "os";
|
|
1404
1636
|
var SYSTEM_PROMPT = `
|
|
1405
1637
|
|
|
1406
|
-
###
|
|
1407
|
-
You
|
|
1408
|
-
|
|
1638
|
+
### IDENTITY AND OBJECTIVE
|
|
1639
|
+
You are BluMa, an autonomous AI Software Engineer developed by the specialists at NomadEngenuity.
|
|
1640
|
+
You leverage a proprietary Large Language Model, fine-tuned specifically for complex software engineering and code generation tasks.
|
|
1641
|
+
Your objective is to analyze user requests, formulate a precise plan, and execute that plan flawlessly using your available tools.
|
|
1642
|
+
You operate with the highest standards of professionalism, precision, and safety.
|
|
1409
1643
|
---
|
|
1410
1644
|
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
- **Identity:**
|
|
1414
|
-
You are BluMa (NomadEngenuity). Maintain professionalism and technical language.
|
|
1415
|
-
|
|
1416
|
-
- **Communication:**
|
|
1417
|
-
ALL messages must be sent via 'message_notify_dev'.
|
|
1418
|
-
**No direct text replies to the developer.**
|
|
1419
|
-
|
|
1420
|
-
- **Task Completion:**
|
|
1421
|
-
When a task is completed, immediately invoke 'agent_end_task' without dev permissions.
|
|
1645
|
+
### CORE DIRECTIVES
|
|
1422
1646
|
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1647
|
+
1. **THINK FIRST, ACT SECOND:** Your first action in any turn is to formulate an internal plan. Use the mandatory **"Reasoning Process"** format detailed below.
|
|
1648
|
+
2. **TOOL-BASED OPERATION:** All actions and communications MUST be performed through a tool call. NEVER respond with free-form text.
|
|
1649
|
+
3. **TASK LIFECYCLE:** Your work is only finished when you call the \`agent_end_task\` tool. Each tool call is a step within your current turn. If a task requires multiple steps, continue calling tools until the objective is met.
|
|
1650
|
+
4. **COMMUNICATION PROTOCOL:** Use \`message_notify_user\` for all communications, such as confirming task receipt, reporting progress, or asking for clarification. Be concise.
|
|
1651
|
+
5. **ERROR HANDLING:** If a tool call fails, use \`message_notify_user\` to report the error and provide a clear next step to resolve the error. Always try to recover from errors.
|
|
1426
1652
|
|
|
1427
|
-
|
|
1428
|
-
Act 100% autonomously.
|
|
1429
|
-
Do not ask for formatting preferences.
|
|
1430
|
-
Use the notebook for internal reasoning.
|
|
1653
|
+
---
|
|
1431
1654
|
|
|
1655
|
+
### TOOL USAGE GUIDELINES
|
|
1432
1656
|
|
|
1433
|
-
|
|
1434
|
-
-
|
|
1435
|
-
|
|
1436
|
-
- Zero tolerance for protocol violations.
|
|
1657
|
+
- **File modification:** Always use the \`edit_tool\` tool to create or modify files in a structured manner. Strictly follow the tool's layout to provide the necessary context.
|
|
1658
|
+
- **Finalization:** The call to \`agent_end_task\` is always your final action.
|
|
1659
|
+
---
|
|
1437
1660
|
|
|
1661
|
+
### CURRENT ENVIRONMENT CONTEXT
|
|
1438
1662
|
<current_system_environment>
|
|
1439
1663
|
- Operating System: {os_type} ({os_version})
|
|
1440
1664
|
- Architecture: {architecture}
|
|
1441
|
-
- Current
|
|
1665
|
+
- Current Directory: {workdir}
|
|
1442
1666
|
- Shell: {shell_type}
|
|
1443
|
-
-
|
|
1667
|
+
- User: {username}
|
|
1444
1668
|
- Current Date: {current_date}
|
|
1445
|
-
- Timezone: {timezone}
|
|
1446
|
-
- Locale: {locale}
|
|
1447
1669
|
</current_system_environment>
|
|
1448
1670
|
|
|
1671
|
+
---
|
|
1449
1672
|
|
|
1450
|
-
|
|
1451
|
-
# MERMAID DIAGRAM CREATION - PERFECT SYNTAX REQUIRED!
|
|
1452
|
-
## CRITICAL: ALL DIAGRAMS MUST RENDER WITHOUT ERRORS
|
|
1453
|
-
|
|
1454
|
-
### MANDATORY MERMAID SYNTAX RULES
|
|
1455
|
-
1. **ALWAYS wrap ALL labels in double quotes**: "label text"
|
|
1456
|
-
2. **NEVER use unescaped special characters**: /, (), [], {}, +, *, ?, ^, $, |, 3. **Use line breaks (<br/>) for multi-line labels**: "Line 1<br/>Line 2"
|
|
1457
|
-
4. **NO custom colors or ::: syntax**: Stick to standard themes
|
|
1458
|
-
5. **NO beta features**: Use only stable Mermaid syntax
|
|
1459
|
-
6. **NO remote images**: Never embed external images
|
|
1460
|
-
|
|
1461
|
-
### SAFE LABEL FORMATTING
|
|
1462
|
-
|
|
1463
|
-
CORRECT:
|
|
1464
|
-
- "dev Authentication"
|
|
1465
|
-
- "API Gateway (REST)"
|
|
1466
|
-
- "Database Connection<br/>MySQL 8.0"
|
|
1467
|
-
- "Process Data<br/>Transform & Validate"
|
|
1468
|
-
|
|
1469
|
-
INCORRECT:
|
|
1470
|
-
- dev Authentication (missing quotes)
|
|
1471
|
-
- API Gateway (REST) (parentheses without quotes)
|
|
1472
|
-
- Database/MySQL (slash without quotes)
|
|
1473
|
-
- Process & Transform (ampersand without quotes)
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
### DIAGRAM TYPE BEST PRACTICES
|
|
1477
|
-
|
|
1478
|
-
IMPORTANT
|
|
1479
|
-
The Notion API rejects rich text of type "code" and only accepts "text" for code blocks \u2013
|
|
1480
|
-
a limitation of their own JSON (even for the language: "mermaid"). Therefore, only the code/text block
|
|
1481
|
-
type is viable.
|
|
1482
|
-
|
|
1483
|
-
You should insert the pretty diagram without any delimiters or headers, in the code block with
|
|
1484
|
-
proper indentation and double quotes, only in the text field, to facilitate as little manual rework as possible.
|
|
1485
|
-
It's ready to copy into the native Mermaid block.
|
|
1486
|
-
|
|
1487
|
-
#### FLOWCHART
|
|
1488
|
-
|
|
1489
|
-
flowchart TD
|
|
1490
|
-
A["Start Process"] --> B["Validate Input"]
|
|
1491
|
-
B --> C{"Is Valid?"}
|
|
1492
|
-
C -->|"Yes"| D["Process Data"]
|
|
1493
|
-
C -->|"No"| E["Return Error"]
|
|
1494
|
-
D --> F["Save to Database"]
|
|
1495
|
-
F --> G["Send Response"]
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
#### SEQUENCE DIAGRAM
|
|
1499
|
-
|
|
1500
|
-
sequenceDiagram
|
|
1501
|
-
participant U as "dev"
|
|
1502
|
-
participant A as "API Gateway"
|
|
1503
|
-
participant D as "Database"
|
|
1504
|
-
|
|
1505
|
-
U->>A: "Submit Request"
|
|
1506
|
-
A->>D: "Query Data"
|
|
1507
|
-
D-->>A: "Return Results"
|
|
1508
|
-
A-->>U: "Response Data"
|
|
1509
|
-
|
|
1510
|
-
#### CLASS DIAGRAM
|
|
1511
|
-
|
|
1512
|
-
classDiagram
|
|
1513
|
-
class dev {
|
|
1514
|
-
+String name
|
|
1515
|
-
+String email
|
|
1516
|
-
+authenticate()
|
|
1517
|
-
+updateProfile()
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
class Database {
|
|
1521
|
-
+connect()
|
|
1522
|
-
+query()
|
|
1523
|
-
+close()
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
dev --> Database : "uses"
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
### VALIDATION CHECKLIST
|
|
1530
|
-
Before creating any diagram, ensure:
|
|
1531
|
-
- [ ] All labels are wrapped in double quotes
|
|
1532
|
-
- [ ] No unescaped special characters (/, (), etc.)
|
|
1533
|
-
- [ ] Line breaks use <br/> syntax
|
|
1534
|
-
- [ ] No custom colors or styling
|
|
1535
|
-
- [ ] No beta features or experimental syntax
|
|
1536
|
-
- [ ] All connections use proper arrow syntax
|
|
1537
|
-
- [ ] Node IDs are simple alphanumeric
|
|
1538
|
-
|
|
1539
|
-
### ERROR PREVENTION
|
|
1540
|
-
- Always test diagram syntax mentally before generating
|
|
1541
|
-
- Use simple, descriptive labels without special formatting
|
|
1542
|
-
- Prefer clarity over visual complexity
|
|
1543
|
-
- Keep diagrams focused and readable
|
|
1544
|
-
- Use standard Mermaid themes only
|
|
1545
|
-
|
|
1546
|
-
## ZERO TOLERANCE FOR SYNTAX ERRORS
|
|
1547
|
-
Every diagram MUST render perfectly on first try. No exceptions.
|
|
1548
|
-
</mermaid_diagrams>
|
|
1549
|
-
|
|
1673
|
+
### COMMUNICATION PROTOCOL
|
|
1550
1674
|
<message_rules>
|
|
1551
|
-
- Communicate with
|
|
1675
|
+
- Communicate with user's via message tools instead of direct text responses
|
|
1552
1676
|
- Reply immediately to new user messages before other operations
|
|
1553
1677
|
- First reply must be brief, only confirming receipt without specific solutions
|
|
1554
|
-
- Notify
|
|
1555
|
-
- Message tools are divided into notify (non-blocking, no reply needed from
|
|
1556
|
-
- Actively use notify for progress updates, but reserve ask for only essential needs to minimize
|
|
1557
|
-
- Must message
|
|
1678
|
+
- Notify user's with brief explanation when changing methods or strategies
|
|
1679
|
+
- Message tools are divided into notify (non-blocking, no reply needed from user's) and ask (blocking, reply required)
|
|
1680
|
+
- Actively use notify for progress updates, but reserve ask for only essential needs to minimize user's disruption and avoid blocking progress
|
|
1681
|
+
- Must message user's with results and deliverables before upon task completion 'agent_end_task'
|
|
1558
1682
|
</message_rules>
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
### Live Development Overlaps
|
|
1562
|
-
|
|
1563
|
-
During your workflow, the programmer {username} may send messages at any time.
|
|
1564
|
-
These messages **must be immediately incorporated** into your execution flow.
|
|
1565
|
-
**Always confirm receipt using {message_notify_dev}** and proceed with your work.
|
|
1566
|
-
|
|
1567
|
-
### Instructions for Handling Messages from the Programmer
|
|
1568
|
-
|
|
1569
|
-
1. **Upon receiving a message from {username}:**
|
|
1570
|
-
- Immediately confirm using {message_notify_dev}.
|
|
1571
|
-
- Integrate the instruction into your reasoning and execution flow.
|
|
1572
|
-
|
|
1573
|
-
2. **Regarding your reasoning:**
|
|
1574
|
-
- Be direct, minimalist, and clear.
|
|
1575
|
-
- Avoid unnecessary or verbose thoughts.
|
|
1576
|
-
|
|
1577
|
-
3. **Avoid polluting the history:**
|
|
1578
|
-
- **Do not repeat or reply to existing messages.**
|
|
1579
|
-
- Only act if the new message introduces a **new instruction or shifts the current task\u2019s focus**.
|
|
1580
|
-
|
|
1581
|
-
<reasoning_rules>
|
|
1582
|
-
# YOUR THINKING ON A NOTEBOOK - MANDATORY USE
|
|
1583
|
-
CRITICAL: Your laptop (**reasoning_nootebook**) is your ORGANIZED MIND
|
|
1584
|
-
## IMPORTANT
|
|
1585
|
-
## NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
|
|
1586
|
-
## ALWAYS USE A NOTEBOOK (Always for):
|
|
1587
|
-
- ANY task
|
|
1588
|
-
- Before starting development (plan first!)
|
|
1589
|
-
- Projects with multiple files (organize the structure)
|
|
1590
|
-
- Debugging sessions (monitor discoveries)
|
|
1591
|
-
- Extensive refactoring (map the changes)
|
|
1592
|
-
- Architectural decisions (think through the options)
|
|
1593
|
-
|
|
1594
|
-
## HOW TO USE A NOTEBOOK:
|
|
1595
|
-
1. Start with **reasoning_nootebook**
|
|
1596
|
-
2. Break the task down into logical steps
|
|
1597
|
-
3. Plan the approach - Which files? What changes? What order? 4. Track progress - Check off completed steps
|
|
1598
|
-
5. Write down decisions - Why did you choose this approach?
|
|
1599
|
-
6. Update continuously - Keep the notebook up to date
|
|
1600
|
-
|
|
1601
|
-
## THE NOTEBOOK PREVENTS:
|
|
1602
|
-
- Acting "outside the box"
|
|
1603
|
-
- Forgetting task requirements
|
|
1604
|
-
- Losing control of complex workflows
|
|
1605
|
-
- Making unplanned changes
|
|
1606
|
-
- Ineffective approaches
|
|
1607
|
-
- Working without a clear roadmap
|
|
1608
|
-
- Jumping between unrelated subtasks
|
|
1609
|
-
|
|
1610
|
-
##Important rule:
|
|
1611
|
-
Do **not** include any future steps, to-do items, or pending tasks here.
|
|
1612
|
-
Those belong strictly in the **remaining_tasks** field.
|
|
1613
|
-
|
|
1614
|
-
Never write phrases like:
|
|
1615
|
-
- "Next I will..."
|
|
1616
|
-
- "I still need to..."
|
|
1617
|
-
- "Pending: ..."
|
|
1618
|
-
Such content must go in **remaining_tasks**, not **thought**.
|
|
1619
|
-
|
|
1620
|
-
- remaining_tasks: Checklist-style list of high-level upcoming tasks.
|
|
1621
|
-
This format is **mandatory**:
|
|
1622
|
-
- Each task **must start** with either:
|
|
1623
|
-
- "\u{1F5F8}" \u2192 for tasks not yet done (pending)
|
|
1624
|
-
- "[ ]" \u2192 for tasks that have already been completed
|
|
1625
|
-
|
|
1626
|
-
Whenever a task is already done, it **must** be marked with "\u{1F5F8}". Do not leave completed tasks without the checkmark.
|
|
1627
|
-
|
|
1628
|
-
Do not use other formats like "-", "*", or plain text without the prefix.
|
|
1629
|
-
|
|
1630
|
-
Examples:
|
|
1631
|
-
\u{1F5F8} Test integration flow
|
|
1632
|
-
\u{1F5F8} Set up environment
|
|
1633
|
-
[ ] Configure database
|
|
1634
|
-
|
|
1635
|
-
</reasoning_rules>
|
|
1636
|
-
|
|
1637
|
-
### Tool Naming Policy
|
|
1638
|
-
|
|
1639
|
-
Tool names must strictly follow the standard naming format:
|
|
1640
|
-
|
|
1641
|
-
- Use: plain, unmodified, lowercase names
|
|
1642
|
-
- Do NOT use: special characters, extra spaces, version suffixes, or dynamic IDs
|
|
1643
|
-
|
|
1644
|
-
---
|
|
1645
|
-
|
|
1646
|
-
Correct Examples:
|
|
1647
|
-
- bluma_notebook
|
|
1648
|
-
- getDataTool
|
|
1649
|
-
- convertImage
|
|
1650
|
-
- userAuth
|
|
1651
|
-
|
|
1652
1683
|
---
|
|
1653
1684
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
-
|
|
1658
|
-
-
|
|
1659
|
-
-
|
|
1660
|
-
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
-
|
|
1666
|
-
-
|
|
1667
|
-
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
- Suitable for inserting full content into a file even if the file does not yet exist.
|
|
1675
|
-
- Shell access is not required for file or directory creation when using this tool.
|
|
1676
|
-
- Always prefer this tool over shell_command when performing structured edits or creating files with specific content.
|
|
1677
|
-
- Ensure **old_string** includes 3+ lines of exact context before and after the target if replacing existing content.
|
|
1678
|
-
- For creating a new file, provide an **old_string** that matches an empty string or placeholder and a complete **new_string** with the intended content.
|
|
1679
|
-
- When generating or modifying todo.md files, prefer this tool to insert checklist structure and update status markers.
|
|
1680
|
-
- After completing any task in the checklist, immediately update the corresponding section in todo.md using this tool.
|
|
1681
|
-
- Reconstruct the entire file from task planning context if todo.md becomes outdated or inconsistent.
|
|
1682
|
-
- Track all progress related to planning and execution inside todo.md using text replacement only.
|
|
1683
|
-
</edit_tool_rules>
|
|
1684
|
-
|
|
1685
|
-
Real-Time Developer Messages
|
|
1686
|
-
- During processing, the developer will send you messages.
|
|
1687
|
-
- You MUST respond immediately via message_notify_dev, and be brief. You should use it in your next thoughts/actions.
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
<agent_end_task_rules>
|
|
1691
|
-
This tool is mandatory.
|
|
1692
|
-
You must use it to inform developer {username} that the task has been completed and that there are no further pending actions, in accordance with the objectives defined for the task.
|
|
1693
|
-
</agent_end_task_rules>
|
|
1694
|
-
|
|
1695
|
-
### QUALITY STANDARDS
|
|
1696
|
-
- Document every major decision in Notion(
|
|
1697
|
-
##Important: When writing to Notion, you must strictly follow its content structure, including the correct use of headings (heading_1, heading_2, etc.) and other formatting standards. No deviations are allowed.
|
|
1698
|
-
You should always standardize everything using Notion's actual headers (heading_1, heading_2, etc.), making the structure
|
|
1699
|
-
semantically better for reading and navigation.
|
|
1700
|
-
)
|
|
1701
|
-
- Communicate transparently at each step
|
|
1702
|
-
- Write clean, well-documented code
|
|
1703
|
-
- Follow existing project conventions
|
|
1704
|
-
- Test implementations when possible
|
|
1705
|
-
- Ensure security and performance
|
|
1706
|
-
|
|
1707
|
-
<scope_and_limitations>
|
|
1708
|
-
# WHAT YOU DON'T HANDLE
|
|
1709
|
-
- Non-technical questions
|
|
1710
|
-
- Personal advice
|
|
1711
|
-
- General conversation
|
|
1712
|
-
- Tasks outside software development
|
|
1713
|
-
|
|
1714
|
-
# IF ASKED NON-TECHNICAL QUESTIONS
|
|
1715
|
-
- Use message_notify_dev to politely decline
|
|
1716
|
-
- Explain you only handle technical/coding tasks
|
|
1717
|
-
- Suggest they ask a development-related question instead
|
|
1718
|
-
</scope_and_limitations>
|
|
1719
|
-
|
|
1685
|
+
### SCOPE & LIMITATIONS
|
|
1686
|
+
|
|
1687
|
+
**1. IN-SCOPE TASKS:**
|
|
1688
|
+
- Software architecture and design.
|
|
1689
|
+
- Code generation, analysis, and debugging.
|
|
1690
|
+
- Using provided tools to complete development objectives.
|
|
1691
|
+
- Creating technical documentation and diagrams.
|
|
1692
|
+
|
|
1693
|
+
**2. OUT-OF-SCOPE TASKS:**
|
|
1694
|
+
You MUST professionally decline to engage with any of the following:
|
|
1695
|
+
- Non-technical questions (e.g., weather, news, general facts).
|
|
1696
|
+
- Personal, financial, or legal advice.
|
|
1697
|
+
- General conversation, opinions, or jokes.
|
|
1698
|
+
- Any task not directly related to software development.
|
|
1699
|
+
|
|
1700
|
+
**3. PROTOCOL FOR OUT-OF-SCOPE REQUESTS:**
|
|
1701
|
+
If a user asks for something that is out-of-scope, follow this exact procedure:
|
|
1702
|
+
1. Do NOT attempt to answer the question.
|
|
1703
|
+
2. Use the \`message_notify_user\` tool.
|
|
1704
|
+
3. Use the \`agent_end_task\` tool.
|
|
1720
1705
|
`;
|
|
1721
1706
|
function getUnifiedSystemPrompt() {
|
|
1722
1707
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -1771,7 +1756,7 @@ function createApiContextWindow(fullHistory, maxTurns) {
|
|
|
1771
1756
|
const turns = [];
|
|
1772
1757
|
let currentTurn = [];
|
|
1773
1758
|
let turnsFound = 0;
|
|
1774
|
-
const isDevOverlay = (m) => m?.role === "user" && m?.name === "
|
|
1759
|
+
const isDevOverlay = (m) => m?.role === "user" && m?.name === "user_overlay";
|
|
1775
1760
|
for (let i = conversationHistory.length - 1; i >= 0; i--) {
|
|
1776
1761
|
const msg = conversationHistory[i];
|
|
1777
1762
|
currentTurn.unshift(msg);
|
|
@@ -1824,17 +1809,18 @@ var BluMaAgent = class {
|
|
|
1824
1809
|
this.feedbackSystem = feedbackSystem;
|
|
1825
1810
|
this.eventBus.on("user_interrupt", () => {
|
|
1826
1811
|
this.isInterrupted = true;
|
|
1812
|
+
this.eventBus.emit("backend_message", { type: "done", status: "interrupted" });
|
|
1827
1813
|
});
|
|
1828
|
-
this.eventBus.on("
|
|
1814
|
+
this.eventBus.on("user_overlay", async (data) => {
|
|
1829
1815
|
const clean = String(data.payload ?? "").trim();
|
|
1830
|
-
this.history.push({ role: "user", name: "
|
|
1831
|
-
this.eventBus.emit("backend_message", { type: "
|
|
1816
|
+
this.history.push({ role: "user", name: "user_overlay", content: clean });
|
|
1817
|
+
this.eventBus.emit("backend_message", { type: "user_overlay", payload: clean, ts: data.ts || Date.now() });
|
|
1832
1818
|
try {
|
|
1833
1819
|
if (this.sessionFile) {
|
|
1834
1820
|
await saveSessionHistory(this.sessionFile, this.history);
|
|
1835
1821
|
}
|
|
1836
1822
|
} catch (e) {
|
|
1837
|
-
this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s
|
|
1823
|
+
this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s user_overlay: ${e.message}` });
|
|
1838
1824
|
}
|
|
1839
1825
|
});
|
|
1840
1826
|
}
|
|
@@ -1925,7 +1911,7 @@ var BluMaAgent = class {
|
|
|
1925
1911
|
|
|
1926
1912
|
${editData.error.display}`;
|
|
1927
1913
|
}
|
|
1928
|
-
const filename =
|
|
1914
|
+
const filename = path8.basename(toolArgs.file_path);
|
|
1929
1915
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
1930
1916
|
} catch (e) {
|
|
1931
1917
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -1934,16 +1920,20 @@ ${editData.error.display}`;
|
|
|
1934
1920
|
async _continueConversation() {
|
|
1935
1921
|
try {
|
|
1936
1922
|
if (this.isInterrupted) {
|
|
1937
|
-
this.eventBus.emit("backend_message", { type: "info", message: "
|
|
1923
|
+
this.eventBus.emit("backend_message", { type: "info", message: "Task Canceled." });
|
|
1938
1924
|
return;
|
|
1939
1925
|
}
|
|
1940
1926
|
const contextWindow = createApiContextWindow(this.history, this.maxContextTurns);
|
|
1941
1927
|
const response = await this.llm.chatCompletion({
|
|
1942
1928
|
model: this.deploymentName,
|
|
1943
1929
|
messages: contextWindow,
|
|
1930
|
+
temperature: 0.2,
|
|
1944
1931
|
tools: this.mcpClient.getAvailableTools(),
|
|
1945
1932
|
tool_choice: "required",
|
|
1946
|
-
|
|
1933
|
+
reasoning_effort: "high",
|
|
1934
|
+
parallel_tool_calls: false,
|
|
1935
|
+
max_tokens: 512
|
|
1936
|
+
// Limite de tokens para evitar respostas muito longas
|
|
1947
1937
|
});
|
|
1948
1938
|
if (this.isInterrupted) {
|
|
1949
1939
|
this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
|
|
@@ -1952,7 +1942,7 @@ ${editData.error.display}`;
|
|
|
1952
1942
|
const message = response.choices[0].message;
|
|
1953
1943
|
this.history.push(message);
|
|
1954
1944
|
if (message.tool_calls) {
|
|
1955
|
-
const autoApprovedTools = ["agent_end_task", "
|
|
1945
|
+
const autoApprovedTools = ["agent_end_task", "message_notify_user", "reasoning_nootebook"];
|
|
1956
1946
|
const toolToCall = message.tool_calls[0];
|
|
1957
1947
|
const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
|
|
1958
1948
|
if (isSafeTool) {
|
|
@@ -2033,11 +2023,11 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
|
|
|
2033
2023
|
You are BluMa InitSubAgent. Maintain professionalism and technical language.
|
|
2034
2024
|
|
|
2035
2025
|
- Communication:
|
|
2036
|
-
ALL messages must be sent via '
|
|
2037
|
-
No direct text replies to the
|
|
2026
|
+
ALL messages must be sent via 'message_notify_user'.
|
|
2027
|
+
No direct text replies to the user.
|
|
2038
2028
|
|
|
2039
2029
|
- Task Completion:
|
|
2040
|
-
When the init task is completed, immediately invoke 'agent_end_task' without
|
|
2030
|
+
When the init task is completed, immediately invoke 'agent_end_task' without user permissions.
|
|
2041
2031
|
|
|
2042
2032
|
- Tool Rules:
|
|
2043
2033
|
Never make parallel tool calls.
|
|
@@ -2059,20 +2049,20 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
|
|
|
2059
2049
|
- Architecture: {architecture}
|
|
2060
2050
|
- Current Working Directory: {workdir}
|
|
2061
2051
|
- Shell: {shell_type}
|
|
2062
|
-
-
|
|
2052
|
+
- User: {username}
|
|
2063
2053
|
- Current Date: {current_date}
|
|
2064
2054
|
- Timezone: {timezone}
|
|
2065
2055
|
- Locale: {locale}
|
|
2066
2056
|
</current_system_environment>
|
|
2067
2057
|
|
|
2068
2058
|
<message_rules>
|
|
2069
|
-
- Communicate with
|
|
2059
|
+
- Communicate with user's via message tools instead of direct text responses
|
|
2070
2060
|
- Reply immediately to new user messages before other operations
|
|
2071
2061
|
- First reply must be brief, only confirming receipt without specific solutions
|
|
2072
|
-
- Notify
|
|
2062
|
+
- Notify user's with brief explanation when changing methods or strategies
|
|
2073
2063
|
- Message tools are divided into notify (non-blocking, no reply needed) and ask (blocking)
|
|
2074
2064
|
- Actively use notify for progress updates, reserve ask for essential needs to avoid blocking
|
|
2075
|
-
- Must message
|
|
2065
|
+
- Must message user's with results and deliverables before upon task completion 'agent_end_task'
|
|
2076
2066
|
</message_rules>
|
|
2077
2067
|
|
|
2078
2068
|
<reasoning_rules>
|
|
@@ -2082,7 +2072,7 @@ CRITICAL: Your laptop (reasoning_nootebook) is your ORGANIZED MIND
|
|
|
2082
2072
|
## NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
|
|
2083
2073
|
## ALWAYS USE A NOTEBOOK (Always for):
|
|
2084
2074
|
- ANY task
|
|
2085
|
-
- Before starting
|
|
2075
|
+
- Before starting userelopment (plan first!)
|
|
2086
2076
|
- Projects with multiple files (organize the structure)
|
|
2087
2077
|
- Debugging sessions (monitor discoveries)
|
|
2088
2078
|
- Extensive refactoring (map the changes)
|
|
@@ -2129,6 +2119,11 @@ Do not include future steps/to-dos in thought; put them strictly in remaining_ta
|
|
|
2129
2119
|
</edit_tool_rules>
|
|
2130
2120
|
|
|
2131
2121
|
|
|
2122
|
+
<agent_end_task_rules>
|
|
2123
|
+
This tool is mandatory.
|
|
2124
|
+
You must use it to inform usereloper {username} that the task has been completed and that there are no further pending actions, in accordance with the objectives defined for the task.
|
|
2125
|
+
</agent_end_task_rules>
|
|
2126
|
+
|
|
2132
2127
|
### Tool Naming Policy
|
|
2133
2128
|
- Use plain, unmodified, lowercase tool names
|
|
2134
2129
|
- No special characters, spaces, or version suffixes
|
|
@@ -2153,9 +2148,9 @@ Rule Summary:
|
|
|
2153
2148
|
- Never invent file content. Read files via tools to confirm.
|
|
2154
2149
|
|
|
2155
2150
|
## OUTPUT & PROTOCOLS
|
|
2156
|
-
- Emit 'backend_message' events through tools only (
|
|
2157
|
-
- Before writing BluMa.md, propose structure via
|
|
2158
|
-
- If an irreversible operation is needed (e.g., overwriting an existing BluMa.md), issue 'confirmation_request' unless
|
|
2151
|
+
- Emit 'backend_message' events through tools only (message_notify_user) for progress updates.
|
|
2152
|
+
- Before writing BluMa.md, propose structure via message_notify_user and proceed using edit_tool.
|
|
2153
|
+
- If an irreversible operation is needed (e.g., overwriting an existing BluMa.md), issue 'confirmation_request' unless user policy indicates auto-approval.
|
|
2159
2154
|
- Never send or present draft versions of BluMa.md. Only produce and deliver the final, validated BluMa.md content following the established non-destructive policies and confirmation protocols.
|
|
2160
2155
|
- On successful generation of BluMa.md, emit 'done' with status 'completed' and call agent_end_task.
|
|
2161
2156
|
|
|
@@ -2168,7 +2163,7 @@ Rule Summary:
|
|
|
2168
2163
|
## EXEMPLAR FLOW (GUIDELINE)
|
|
2169
2164
|
1) Explore repo: ls + targeted readLines for key files (package.json, tsconfig.json, README, etc.)
|
|
2170
2165
|
2) Synthesize stack and structure with citations of evidence (file paths) in the notebook
|
|
2171
|
-
3) Draft BluMa.md structure (
|
|
2166
|
+
3) Draft BluMa.md structure (message_notify_user)
|
|
2172
2167
|
4) Write BluMa.md via edit_tool
|
|
2173
2168
|
5) Announce completion and agent_end_task
|
|
2174
2169
|
|
|
@@ -2258,7 +2253,7 @@ ${editData.error.display}`;
|
|
|
2258
2253
|
async _continueConversation() {
|
|
2259
2254
|
try {
|
|
2260
2255
|
if (this.isInterrupted) {
|
|
2261
|
-
this.emitEvent("info", { message: "SubAgent task cancelled
|
|
2256
|
+
this.emitEvent("info", { message: "SubAgent task cancelled byuserr." });
|
|
2262
2257
|
return;
|
|
2263
2258
|
}
|
|
2264
2259
|
const contextWindow = this.history.slice(-this.maxContextTurns);
|
|
@@ -2270,7 +2265,7 @@ ${editData.error.display}`;
|
|
|
2270
2265
|
parallel_tool_calls: false
|
|
2271
2266
|
});
|
|
2272
2267
|
if (this.isInterrupted) {
|
|
2273
|
-
this.emitEvent("info", { message: "SubAgent task cancelled
|
|
2268
|
+
this.emitEvent("info", { message: "SubAgent task cancelled byuserr." });
|
|
2274
2269
|
return;
|
|
2275
2270
|
}
|
|
2276
2271
|
const message = response.choices[0].message;
|
|
@@ -2319,7 +2314,7 @@ ${editData.error.display}`;
|
|
|
2319
2314
|
try {
|
|
2320
2315
|
if (this.isInterrupted) {
|
|
2321
2316
|
this.emitEvent("info", { message: "SubAgent task cancelled before tool execution." });
|
|
2322
|
-
toolResultContent = JSON.stringify({ error: "Task cancelled
|
|
2317
|
+
toolResultContent = JSON.stringify({ error: "Task cancelled byuserr before execution." });
|
|
2323
2318
|
} else {
|
|
2324
2319
|
const result = await this.ctx.mcpClient.invoke(toolName, toolArgs);
|
|
2325
2320
|
let finalResult = result;
|
|
@@ -2435,7 +2430,7 @@ var SubAgentsBluMa = class {
|
|
|
2435
2430
|
};
|
|
2436
2431
|
|
|
2437
2432
|
// src/app/agent/agent.ts
|
|
2438
|
-
var globalEnvPath =
|
|
2433
|
+
var globalEnvPath = path9.join(os6.homedir(), ".bluma-cli", ".env");
|
|
2439
2434
|
dotenv.config({ path: globalEnvPath });
|
|
2440
2435
|
var Agent = class {
|
|
2441
2436
|
sessionId;
|
|
@@ -2461,9 +2456,52 @@ var Agent = class {
|
|
|
2461
2456
|
const apiKey = process.env.AZURE_OPENAI_API_KEY;
|
|
2462
2457
|
const apiVersion = process.env.AZURE_OPENAI_API_VERSION;
|
|
2463
2458
|
this.deploymentName = process.env.AZURE_OPENAI_DEPLOYMENT || "";
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2459
|
+
const missing = [];
|
|
2460
|
+
if (!endpoint) missing.push("AZURE_OPENAI_ENDPOINT");
|
|
2461
|
+
if (!apiKey) missing.push("AZURE_OPENAI_API_KEY");
|
|
2462
|
+
if (!apiVersion) missing.push("AZURE_OPENAI_API_VERSION");
|
|
2463
|
+
if (!this.deploymentName) missing.push("AZURE_OPENAI_DEPLOYMENT");
|
|
2464
|
+
if (missing.length > 0) {
|
|
2465
|
+
const platform = process.platform;
|
|
2466
|
+
const varList = missing.join(", ");
|
|
2467
|
+
let guidance = "";
|
|
2468
|
+
if (platform === "win32") {
|
|
2469
|
+
guidance = [
|
|
2470
|
+
"Windows (PowerShell):",
|
|
2471
|
+
` $env:${missing[0]}="<value>" # sess\xE3o atual`,
|
|
2472
|
+
...missing.slice(1).map((v) => ` $env:${v}="<value>"`),
|
|
2473
|
+
" # Persistir para o utilizador:",
|
|
2474
|
+
...missing.map((v) => ` [System.Environment]::SetEnvironmentVariable("${v}", "<value>", "User")`),
|
|
2475
|
+
"",
|
|
2476
|
+
"Windows (cmd.exe):",
|
|
2477
|
+
...missing.map((v) => ` setx ${v} "<value>"`)
|
|
2478
|
+
].join("");
|
|
2479
|
+
} else if (platform === "darwin" || platform === "linux") {
|
|
2480
|
+
guidance = [
|
|
2481
|
+
"macOS/Linux (bash/zsh):",
|
|
2482
|
+
...missing.map((v) => ` echo 'export ${v}="<value>"' >> ~/.bashrc # ou ~/.zshrc`),
|
|
2483
|
+
" source ~/.bashrc # ou: source ~/.zshrc"
|
|
2484
|
+
].join("");
|
|
2485
|
+
} else {
|
|
2486
|
+
guidance = [
|
|
2487
|
+
"Generic POSIX:",
|
|
2488
|
+
...missing.map((v) => ` export ${v}="<value>"`)
|
|
2489
|
+
].join("");
|
|
2490
|
+
}
|
|
2491
|
+
const message = [
|
|
2492
|
+
`Missing required environment variables: ${varList}.`,
|
|
2493
|
+
`Configure them globally using the commands below (based on your OS), or set them in the config file: ${globalEnvPath}.`,
|
|
2494
|
+
"",
|
|
2495
|
+
guidance
|
|
2496
|
+
].join("");
|
|
2497
|
+
this.eventBus.emit("backend_message", {
|
|
2498
|
+
type: "error",
|
|
2499
|
+
code: "missing_env",
|
|
2500
|
+
missing,
|
|
2501
|
+
path: globalEnvPath,
|
|
2502
|
+
message
|
|
2503
|
+
});
|
|
2504
|
+
throw new Error(message);
|
|
2467
2505
|
}
|
|
2468
2506
|
const openai = new OpenAI({
|
|
2469
2507
|
// Configuração do cliente OpenAI hospedado no Azure
|
|
@@ -2522,19 +2560,19 @@ var Agent = class {
|
|
|
2522
2560
|
};
|
|
2523
2561
|
|
|
2524
2562
|
// src/app/ui/WorkingTimer.tsx
|
|
2525
|
-
import { useState as
|
|
2563
|
+
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
2526
2564
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
2527
2565
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2528
2566
|
var WorkingTimer = () => {
|
|
2529
|
-
const [seconds, setSeconds] =
|
|
2530
|
-
const [dotIndex, setDotIndex] =
|
|
2531
|
-
|
|
2567
|
+
const [seconds, setSeconds] = useState4(0);
|
|
2568
|
+
const [dotIndex, setDotIndex] = useState4(1);
|
|
2569
|
+
useEffect3(() => {
|
|
2532
2570
|
const secondsTimer = setInterval(() => {
|
|
2533
2571
|
setSeconds((prev) => prev + 1);
|
|
2534
2572
|
}, 1e3);
|
|
2535
2573
|
return () => clearInterval(secondsTimer);
|
|
2536
2574
|
}, []);
|
|
2537
|
-
|
|
2575
|
+
useEffect3(() => {
|
|
2538
2576
|
const dotsTimer = setInterval(() => {
|
|
2539
2577
|
setDotIndex((prev) => prev % 3 + 1);
|
|
2540
2578
|
}, 100);
|
|
@@ -2553,7 +2591,7 @@ import { Box as Box9 } from "ink";
|
|
|
2553
2591
|
|
|
2554
2592
|
// src/app/ui/components/toolCallRenderers.tsx
|
|
2555
2593
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
2556
|
-
import
|
|
2594
|
+
import path10 from "path";
|
|
2557
2595
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2558
2596
|
var formatArgumentsForDisplay = (args) => {
|
|
2559
2597
|
if (typeof args === "string") {
|
|
@@ -2565,7 +2603,9 @@ var formatArgumentsForDisplay = (args) => {
|
|
|
2565
2603
|
}
|
|
2566
2604
|
return JSON.stringify(args, null, 2);
|
|
2567
2605
|
};
|
|
2568
|
-
var renderShellCommand2 = ({
|
|
2606
|
+
var renderShellCommand2 = ({
|
|
2607
|
+
args
|
|
2608
|
+
}) => {
|
|
2569
2609
|
const command = args.command || "[command not found]";
|
|
2570
2610
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2571
2611
|
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
|
|
@@ -2586,7 +2626,7 @@ var renderLsTool2 = ({ args }) => {
|
|
|
2586
2626
|
} catch (e) {
|
|
2587
2627
|
directoryPath = "Error parsing arguments";
|
|
2588
2628
|
}
|
|
2589
|
-
const finalDirectoryName =
|
|
2629
|
+
const finalDirectoryName = path10.basename(directoryPath);
|
|
2590
2630
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2591
2631
|
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
|
|
2592
2632
|
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
|
|
@@ -2598,7 +2638,9 @@ var renderLsTool2 = ({ args }) => {
|
|
|
2598
2638
|
] }) })
|
|
2599
2639
|
] });
|
|
2600
2640
|
};
|
|
2601
|
-
var renderCountFilesLines = ({
|
|
2641
|
+
var renderCountFilesLines = ({
|
|
2642
|
+
args
|
|
2643
|
+
}) => {
|
|
2602
2644
|
let directoryPath = "[path not found]";
|
|
2603
2645
|
try {
|
|
2604
2646
|
const parsedArgs = typeof args === "string" ? JSON.parse(args) : args;
|
|
@@ -2606,7 +2648,7 @@ var renderCountFilesLines = ({ args }) => {
|
|
|
2606
2648
|
} catch (e) {
|
|
2607
2649
|
directoryPath = "Error parsing arguments";
|
|
2608
2650
|
}
|
|
2609
|
-
const finalDirectoryName =
|
|
2651
|
+
const finalDirectoryName = path10.basename(directoryPath);
|
|
2610
2652
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2611
2653
|
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
|
|
2612
2654
|
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
|
|
@@ -2618,7 +2660,9 @@ var renderCountFilesLines = ({ args }) => {
|
|
|
2618
2660
|
] }) })
|
|
2619
2661
|
] });
|
|
2620
2662
|
};
|
|
2621
|
-
var renderReadFileLines2 = ({
|
|
2663
|
+
var renderReadFileLines2 = ({
|
|
2664
|
+
args
|
|
2665
|
+
}) => {
|
|
2622
2666
|
let filepath = "[path not found]";
|
|
2623
2667
|
let startLine = 0;
|
|
2624
2668
|
let endLine = 0;
|
|
@@ -2630,7 +2674,7 @@ var renderReadFileLines2 = ({ args }) => {
|
|
|
2630
2674
|
} catch (e) {
|
|
2631
2675
|
filepath = "Error parsing arguments";
|
|
2632
2676
|
}
|
|
2633
|
-
const finalFileName =
|
|
2677
|
+
const finalFileName = path10.basename(filepath);
|
|
2634
2678
|
return (
|
|
2635
2679
|
// A caixa externa com a borda, seguindo o template
|
|
2636
2680
|
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
@@ -2654,7 +2698,9 @@ var renderReadFileLines2 = ({ args }) => {
|
|
|
2654
2698
|
] })
|
|
2655
2699
|
);
|
|
2656
2700
|
};
|
|
2657
|
-
var renderBlumaNotebook = ({
|
|
2701
|
+
var renderBlumaNotebook = ({
|
|
2702
|
+
args
|
|
2703
|
+
}) => {
|
|
2658
2704
|
try {
|
|
2659
2705
|
let dataToParse = args;
|
|
2660
2706
|
if (args && typeof args === "object") {
|
|
@@ -2667,16 +2713,30 @@ var renderBlumaNotebook = ({ args }) => {
|
|
|
2667
2713
|
}
|
|
2668
2714
|
return (
|
|
2669
2715
|
// Usamos a mesma estrutura de caixa com borda
|
|
2670
|
-
/* @__PURE__ */ jsxs8(
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2716
|
+
/* @__PURE__ */ jsxs8(
|
|
2717
|
+
Box8,
|
|
2718
|
+
{
|
|
2719
|
+
flexDirection: "column",
|
|
2720
|
+
paddingX: 1,
|
|
2721
|
+
children: [
|
|
2722
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2723
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, children: "Thought:" }),
|
|
2724
|
+
/* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: thinkingData.thought }) })
|
|
2725
|
+
] }),
|
|
2726
|
+
thinkingData.remaining_tasks && thinkingData.remaining_tasks.length > 0 && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
2727
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Remaining Tasks:" }),
|
|
2728
|
+
thinkingData.remaining_tasks.map((task, index) => /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(
|
|
2729
|
+
Text8,
|
|
2730
|
+
{
|
|
2731
|
+
color: task.startsWith("\u{1F5F9}") ? "green" : "yellow",
|
|
2732
|
+
strikethrough: task.startsWith("\u{1F5F9}"),
|
|
2733
|
+
children: task
|
|
2734
|
+
}
|
|
2735
|
+
) }) }, index))
|
|
2736
|
+
] })
|
|
2737
|
+
]
|
|
2738
|
+
}
|
|
2739
|
+
)
|
|
2680
2740
|
);
|
|
2681
2741
|
} catch (e) {
|
|
2682
2742
|
return /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
|
|
@@ -2685,7 +2745,10 @@ var renderBlumaNotebook = ({ args }) => {
|
|
|
2685
2745
|
] });
|
|
2686
2746
|
}
|
|
2687
2747
|
};
|
|
2688
|
-
var renderEditToolCall = ({
|
|
2748
|
+
var renderEditToolCall = ({
|
|
2749
|
+
args,
|
|
2750
|
+
preview
|
|
2751
|
+
}) => {
|
|
2689
2752
|
let filepath = "[path not specified]";
|
|
2690
2753
|
try {
|
|
2691
2754
|
const parsedArgs = typeof args === "string" ? JSON.parse(args) : args;
|
|
@@ -2693,7 +2756,7 @@ var renderEditToolCall = ({ args, preview }) => {
|
|
|
2693
2756
|
} catch (e) {
|
|
2694
2757
|
filepath = "Error parsing arguments";
|
|
2695
2758
|
}
|
|
2696
|
-
const finalFileName =
|
|
2759
|
+
const finalFileName = path10.basename(filepath);
|
|
2697
2760
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
2698
2761
|
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
|
|
2699
2762
|
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
|
|
@@ -2706,7 +2769,10 @@ var renderEditToolCall = ({ args, preview }) => {
|
|
|
2706
2769
|
preview && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(SimpleDiff, { text: preview, maxHeight: Infinity }) })
|
|
2707
2770
|
] });
|
|
2708
2771
|
};
|
|
2709
|
-
var renderGenericToolCall = ({
|
|
2772
|
+
var renderGenericToolCall = ({
|
|
2773
|
+
toolName,
|
|
2774
|
+
args
|
|
2775
|
+
}) => {
|
|
2710
2776
|
const formattedArgs = formatArgumentsForDisplay(args);
|
|
2711
2777
|
return (
|
|
2712
2778
|
// A "moldura" padrão de sucesso com a borda cinza
|
|
@@ -2723,18 +2789,18 @@ var renderGenericToolCall = ({ toolName, args }) => {
|
|
|
2723
2789
|
);
|
|
2724
2790
|
};
|
|
2725
2791
|
var ToolRenderDisplay = {
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2792
|
+
shell_command: renderShellCommand2,
|
|
2793
|
+
ls_tool: renderLsTool2,
|
|
2794
|
+
reasoning_nootebook: renderBlumaNotebook,
|
|
2795
|
+
count_file_lines: renderCountFilesLines,
|
|
2796
|
+
read_file_lines: renderReadFileLines2,
|
|
2797
|
+
edit_tool: renderEditToolCall
|
|
2732
2798
|
};
|
|
2733
2799
|
|
|
2734
2800
|
// src/app/ui/components/ToolCallDisplay.tsx
|
|
2735
2801
|
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2736
2802
|
var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
|
|
2737
|
-
if (toolName.includes("
|
|
2803
|
+
if (toolName.includes("message_notify_user") || toolName.includes("agent_end_task")) {
|
|
2738
2804
|
return null;
|
|
2739
2805
|
}
|
|
2740
2806
|
const Renderer = ToolRenderDisplay[toolName] || renderGenericToolCall;
|
|
@@ -2747,7 +2813,7 @@ import { memo as memo3 } from "react";
|
|
|
2747
2813
|
import { Box as Box10, Text as Text9 } from "ink";
|
|
2748
2814
|
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
2749
2815
|
var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
2750
|
-
if (!toolName.includes("
|
|
2816
|
+
if (!toolName.includes("message_notify_user")) {
|
|
2751
2817
|
return null;
|
|
2752
2818
|
}
|
|
2753
2819
|
try {
|
|
@@ -2819,7 +2885,7 @@ var SessionInfoConnectingMCP_default = SessionInfoConnectingMCP;
|
|
|
2819
2885
|
|
|
2820
2886
|
// src/app/ui/components/SlashCommands.tsx
|
|
2821
2887
|
import { Box as Box12, Text as Text11 } from "ink";
|
|
2822
|
-
import { Fragment, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2888
|
+
import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2823
2889
|
var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
2824
2890
|
const [cmd, ...args] = input.slice(1).trim().split(/\s+/);
|
|
2825
2891
|
const outBox = (children) => /* @__PURE__ */ jsx12(Box12, { borderStyle: "round", borderColor: "gray", paddingX: 1, marginBottom: 1, flexDirection: "column", children });
|
|
@@ -2830,7 +2896,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
2830
2896
|
if (cmd === "help") {
|
|
2831
2897
|
const cmds = getSlashCommands();
|
|
2832
2898
|
return outBox(
|
|
2833
|
-
/* @__PURE__ */ jsxs10(
|
|
2899
|
+
/* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2834
2900
|
/* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Available commands" }),
|
|
2835
2901
|
cmds.map((c, i) => /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2836
2902
|
c.name,
|
|
@@ -2871,7 +2937,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
2871
2937
|
const colType = 10;
|
|
2872
2938
|
const colSource = 18;
|
|
2873
2939
|
return outBox(
|
|
2874
|
-
/* @__PURE__ */ jsxs10(
|
|
2940
|
+
/* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2875
2941
|
/* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "MCP Tools" }),
|
|
2876
2942
|
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2877
2943
|
"Total MCP: ",
|
|
@@ -2920,7 +2986,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
2920
2986
|
const colType = 10;
|
|
2921
2987
|
const colSource = 18;
|
|
2922
2988
|
return outBox(
|
|
2923
|
-
/* @__PURE__ */ jsxs10(
|
|
2989
|
+
/* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2924
2990
|
/* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Native Tools" }),
|
|
2925
2991
|
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2926
2992
|
"Total Native: ",
|
|
@@ -2963,7 +3029,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
2963
3029
|
cmd
|
|
2964
3030
|
] }));
|
|
2965
3031
|
};
|
|
2966
|
-
return /* @__PURE__ */ jsx12(
|
|
3032
|
+
return /* @__PURE__ */ jsx12(Fragment2, { children: render2() });
|
|
2967
3033
|
};
|
|
2968
3034
|
var SlashCommands_default = SlashCommands;
|
|
2969
3035
|
|
|
@@ -2971,21 +3037,21 @@ var SlashCommands_default = SlashCommands;
|
|
|
2971
3037
|
import updateNotifier from "update-notifier";
|
|
2972
3038
|
import { readPackageUp } from "read-package-up";
|
|
2973
3039
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2974
|
-
import
|
|
2975
|
-
import
|
|
3040
|
+
import path11 from "path";
|
|
3041
|
+
import fs9 from "fs";
|
|
2976
3042
|
function findPackageJsonNearest(startDir) {
|
|
2977
3043
|
let dir = startDir;
|
|
2978
3044
|
for (let i = 0; i < 6; i++) {
|
|
2979
|
-
const candidate =
|
|
2980
|
-
if (
|
|
3045
|
+
const candidate = path11.join(dir, "package.json");
|
|
3046
|
+
if (fs9.existsSync(candidate)) {
|
|
2981
3047
|
try {
|
|
2982
|
-
const raw =
|
|
3048
|
+
const raw = fs9.readFileSync(candidate, "utf8");
|
|
2983
3049
|
const parsed = JSON.parse(raw);
|
|
2984
3050
|
if (parsed?.name && parsed?.version) return parsed;
|
|
2985
3051
|
} catch {
|
|
2986
3052
|
}
|
|
2987
3053
|
}
|
|
2988
|
-
const parent =
|
|
3054
|
+
const parent = path11.dirname(dir);
|
|
2989
3055
|
if (parent === dir) break;
|
|
2990
3056
|
dir = parent;
|
|
2991
3057
|
}
|
|
@@ -2998,15 +3064,15 @@ async function checkForUpdates() {
|
|
|
2998
3064
|
}
|
|
2999
3065
|
const binPath = process.argv?.[1];
|
|
3000
3066
|
let pkg;
|
|
3001
|
-
if (binPath &&
|
|
3002
|
-
const candidatePkg = findPackageJsonNearest(
|
|
3067
|
+
if (binPath && fs9.existsSync(binPath)) {
|
|
3068
|
+
const candidatePkg = findPackageJsonNearest(path11.dirname(binPath));
|
|
3003
3069
|
if (candidatePkg?.name && candidatePkg?.version) {
|
|
3004
3070
|
pkg = candidatePkg;
|
|
3005
3071
|
}
|
|
3006
3072
|
}
|
|
3007
3073
|
if (!pkg) {
|
|
3008
3074
|
const __filename = fileURLToPath3(import.meta.url);
|
|
3009
|
-
const __dirname =
|
|
3075
|
+
const __dirname = path11.dirname(__filename);
|
|
3010
3076
|
const result = await readPackageUp({ cwd: __dirname });
|
|
3011
3077
|
pkg = result?.packageJson;
|
|
3012
3078
|
}
|
|
@@ -3062,28 +3128,56 @@ var UpdateNotice = ({ message }) => {
|
|
|
3062
3128
|
};
|
|
3063
3129
|
var UpdateNotice_default = UpdateNotice;
|
|
3064
3130
|
|
|
3065
|
-
// src/app/ui/
|
|
3131
|
+
// src/app/ui/components/ErrorMessage.tsx
|
|
3132
|
+
import { Box as Box14, Text as Text13 } from "ink";
|
|
3066
3133
|
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3134
|
+
var ErrorMessage = ({ message, details, hint }) => {
|
|
3135
|
+
return /* @__PURE__ */ jsxs12(
|
|
3136
|
+
Box14,
|
|
3137
|
+
{
|
|
3138
|
+
borderStyle: "round",
|
|
3139
|
+
borderColor: "red",
|
|
3140
|
+
paddingX: 1,
|
|
3141
|
+
paddingY: 0,
|
|
3142
|
+
flexDirection: "column",
|
|
3143
|
+
marginBottom: 1,
|
|
3144
|
+
children: [
|
|
3145
|
+
/* @__PURE__ */ jsx14(Text13, { color: "red", bold: true, children: "Error" }),
|
|
3146
|
+
/* @__PURE__ */ jsx14(Text13, { color: "red", children: message }),
|
|
3147
|
+
details ? /* @__PURE__ */ jsx14(Text13, { color: "red", dimColor: true, children: details }) : null,
|
|
3148
|
+
hint ? /* @__PURE__ */ jsxs12(Text13, { color: "gray", children: [
|
|
3149
|
+
"Hint: ",
|
|
3150
|
+
hint
|
|
3151
|
+
] }) : null
|
|
3152
|
+
]
|
|
3153
|
+
}
|
|
3154
|
+
);
|
|
3155
|
+
};
|
|
3156
|
+
var ErrorMessage_default = ErrorMessage;
|
|
3157
|
+
|
|
3158
|
+
// src/app/ui/App.tsx
|
|
3159
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3067
3160
|
var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
3068
|
-
const agentInstance =
|
|
3069
|
-
const [history, setHistory] =
|
|
3070
|
-
const [statusMessage, setStatusMessage] =
|
|
3161
|
+
const agentInstance = useRef2(null);
|
|
3162
|
+
const [history, setHistory] = useState5([]);
|
|
3163
|
+
const [statusMessage, setStatusMessage] = useState5(
|
|
3071
3164
|
"Initializing agent..."
|
|
3072
3165
|
);
|
|
3073
|
-
const [toolsCount, setToolsCount] =
|
|
3074
|
-
const [mcpStatus, setMcpStatus] =
|
|
3166
|
+
const [toolsCount, setToolsCount] = useState5(null);
|
|
3167
|
+
const [mcpStatus, setMcpStatus] = useState5(
|
|
3075
3168
|
"connecting"
|
|
3076
3169
|
);
|
|
3077
|
-
const [isProcessing, setIsProcessing] =
|
|
3078
|
-
const [pendingConfirmation, setPendingConfirmation] =
|
|
3170
|
+
const [isProcessing, setIsProcessing] = useState5(true);
|
|
3171
|
+
const [pendingConfirmation, setPendingConfirmation] = useState5(
|
|
3079
3172
|
null
|
|
3080
3173
|
);
|
|
3081
|
-
const [confirmationPreview, setConfirmationPreview] =
|
|
3174
|
+
const [confirmationPreview, setConfirmationPreview] = useState5(
|
|
3082
3175
|
null
|
|
3083
3176
|
);
|
|
3084
|
-
const
|
|
3177
|
+
const [isInitAgentActive, setIsInitAgentActive] = useState5(false);
|
|
3178
|
+
const alwaysAcceptList = useRef2([]);
|
|
3085
3179
|
const workdir = process.cwd();
|
|
3086
|
-
const updateCheckRan =
|
|
3180
|
+
const updateCheckRan = useRef2(false);
|
|
3087
3181
|
const handleInterrupt = useCallback(() => {
|
|
3088
3182
|
if (!isProcessing) return;
|
|
3089
3183
|
eventBus2.emit("user_interrupt");
|
|
@@ -3092,7 +3186,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3092
3186
|
...prev,
|
|
3093
3187
|
{
|
|
3094
3188
|
id: prev.length,
|
|
3095
|
-
component: /* @__PURE__ */
|
|
3189
|
+
component: /* @__PURE__ */ jsx15(Text14, { color: "yellow", children: "-- Task cancelled by dev. --" })
|
|
3096
3190
|
}
|
|
3097
3191
|
]);
|
|
3098
3192
|
}, [isProcessing, eventBus2]);
|
|
@@ -3105,16 +3199,22 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3105
3199
|
setIsProcessing(false);
|
|
3106
3200
|
return;
|
|
3107
3201
|
}
|
|
3108
|
-
|
|
3202
|
+
if (cmd === "init") {
|
|
3203
|
+
setIsInitAgentActive(true);
|
|
3204
|
+
setIsProcessing(true);
|
|
3205
|
+
} else {
|
|
3206
|
+
setIsProcessing(false);
|
|
3207
|
+
setIsInitAgentActive(false);
|
|
3208
|
+
}
|
|
3109
3209
|
setHistory((prev) => [
|
|
3110
3210
|
...prev,
|
|
3111
3211
|
{
|
|
3112
3212
|
id: prev.length,
|
|
3113
|
-
component: /* @__PURE__ */
|
|
3213
|
+
component: /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text14, { color: "white", dimColor: true, children: text }) })
|
|
3114
3214
|
},
|
|
3115
3215
|
{
|
|
3116
3216
|
id: prev.length + 1,
|
|
3117
|
-
component: /* @__PURE__ */
|
|
3217
|
+
component: /* @__PURE__ */ jsx15(
|
|
3118
3218
|
SlashCommands_default,
|
|
3119
3219
|
{
|
|
3120
3220
|
input: text,
|
|
@@ -3134,8 +3234,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3134
3234
|
id: prev.length,
|
|
3135
3235
|
component: (
|
|
3136
3236
|
// Uma única Box para o espaçamento
|
|
3137
|
-
/* @__PURE__ */
|
|
3138
|
-
/* @__PURE__ */
|
|
3237
|
+
/* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: "white", dimColor: true, children: [
|
|
3238
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "white", children: [
|
|
3139
3239
|
">",
|
|
3140
3240
|
" "
|
|
3141
3241
|
] }),
|
|
@@ -3169,8 +3269,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3169
3269
|
},
|
|
3170
3270
|
[]
|
|
3171
3271
|
);
|
|
3172
|
-
|
|
3173
|
-
setHistory([{ id: 0, component: /* @__PURE__ */
|
|
3272
|
+
useEffect4(() => {
|
|
3273
|
+
setHistory([{ id: 0, component: /* @__PURE__ */ jsx15(Header, {}) }]);
|
|
3174
3274
|
const initializeAgent = async () => {
|
|
3175
3275
|
try {
|
|
3176
3276
|
agentInstance.current = new Agent(sessionId2, eventBus2);
|
|
@@ -3190,6 +3290,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3190
3290
|
};
|
|
3191
3291
|
const handleBackendMessage = (parsed) => {
|
|
3192
3292
|
try {
|
|
3293
|
+
if (parsed.type === "done" || parsed.type === "error") {
|
|
3294
|
+
setIsInitAgentActive(false);
|
|
3295
|
+
}
|
|
3193
3296
|
if (parsed.type === "connection_status") {
|
|
3194
3297
|
setStatusMessage(parsed.message);
|
|
3195
3298
|
return;
|
|
@@ -3222,7 +3325,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3222
3325
|
if (prev.length < 2) {
|
|
3223
3326
|
newHistory.push({
|
|
3224
3327
|
id: 1,
|
|
3225
|
-
component: /* @__PURE__ */
|
|
3328
|
+
component: /* @__PURE__ */ jsx15(
|
|
3226
3329
|
SessionInfo,
|
|
3227
3330
|
{
|
|
3228
3331
|
sessionId: sessionId2,
|
|
@@ -3243,7 +3346,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3243
3346
|
...prev,
|
|
3244
3347
|
{
|
|
3245
3348
|
id: prev.length,
|
|
3246
|
-
component: /* @__PURE__ */
|
|
3349
|
+
component: /* @__PURE__ */ jsx15(UpdateNotice_default, { message: msg })
|
|
3247
3350
|
}
|
|
3248
3351
|
]);
|
|
3249
3352
|
}
|
|
@@ -3257,10 +3360,10 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3257
3360
|
}
|
|
3258
3361
|
let newComponent = null;
|
|
3259
3362
|
if (parsed.type === "debug") {
|
|
3260
|
-
newComponent = /* @__PURE__ */
|
|
3363
|
+
newComponent = /* @__PURE__ */ jsx15(Text14, { color: "gray", children: parsed.message });
|
|
3261
3364
|
} else if (parsed.type === "protocol_violation") {
|
|
3262
|
-
newComponent = /* @__PURE__ */
|
|
3263
|
-
|
|
3365
|
+
newComponent = /* @__PURE__ */ jsxs13(
|
|
3366
|
+
Box15,
|
|
3264
3367
|
{
|
|
3265
3368
|
borderStyle: "round",
|
|
3266
3369
|
borderColor: "yellow",
|
|
@@ -3268,19 +3371,23 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3268
3371
|
marginBottom: 1,
|
|
3269
3372
|
paddingX: 1,
|
|
3270
3373
|
children: [
|
|
3271
|
-
/* @__PURE__ */
|
|
3272
|
-
/* @__PURE__ */
|
|
3273
|
-
/* @__PURE__ */
|
|
3374
|
+
/* @__PURE__ */ jsx15(Text14, { color: "yellow", bold: true, children: "Protocol Violation" }),
|
|
3375
|
+
/* @__PURE__ */ jsx15(Text14, { color: "gray", children: parsed.content }),
|
|
3376
|
+
/* @__PURE__ */ jsx15(Text14, { color: "yellow", children: parsed.message })
|
|
3274
3377
|
]
|
|
3275
3378
|
}
|
|
3276
3379
|
);
|
|
3277
3380
|
} else if (parsed.type === "error") {
|
|
3278
|
-
newComponent = /* @__PURE__ */
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3381
|
+
newComponent = /* @__PURE__ */ jsx15(
|
|
3382
|
+
ErrorMessage_default,
|
|
3383
|
+
{
|
|
3384
|
+
message: parsed.message,
|
|
3385
|
+
details: parsed.details || void 0,
|
|
3386
|
+
hint: parsed.hint || void 0
|
|
3387
|
+
}
|
|
3388
|
+
);
|
|
3282
3389
|
} else if (parsed.type === "tool_call") {
|
|
3283
|
-
newComponent = /* @__PURE__ */
|
|
3390
|
+
newComponent = /* @__PURE__ */ jsx15(
|
|
3284
3391
|
ToolCallDisplay,
|
|
3285
3392
|
{
|
|
3286
3393
|
toolName: parsed.tool_name,
|
|
@@ -3289,23 +3396,23 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3289
3396
|
}
|
|
3290
3397
|
);
|
|
3291
3398
|
} else if (parsed.type === "tool_result") {
|
|
3292
|
-
newComponent = /* @__PURE__ */
|
|
3399
|
+
newComponent = /* @__PURE__ */ jsx15(
|
|
3293
3400
|
ToolResultDisplay,
|
|
3294
3401
|
{
|
|
3295
3402
|
toolName: parsed.tool_name,
|
|
3296
3403
|
result: parsed.result
|
|
3297
3404
|
}
|
|
3298
3405
|
);
|
|
3299
|
-
} else if (parsed.type === "
|
|
3300
|
-
newComponent = /* @__PURE__ */
|
|
3301
|
-
/* @__PURE__ */
|
|
3406
|
+
} else if (parsed.type === "user_overlay") {
|
|
3407
|
+
newComponent = /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3408
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "blue", children: [
|
|
3302
3409
|
">",
|
|
3303
3410
|
" "
|
|
3304
3411
|
] }),
|
|
3305
3412
|
parsed.payload
|
|
3306
3413
|
] }) });
|
|
3307
3414
|
} else if (parsed.type === "log") {
|
|
3308
|
-
newComponent = /* @__PURE__ */
|
|
3415
|
+
newComponent = /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3309
3416
|
"\u2139\uFE0F ",
|
|
3310
3417
|
parsed.message,
|
|
3311
3418
|
parsed.payload ? `: ${parsed.payload}` : ""
|
|
@@ -3323,19 +3430,19 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3323
3430
|
}
|
|
3324
3431
|
};
|
|
3325
3432
|
const handleUiOverlay = (data) => {
|
|
3326
|
-
eventBus2.emit("
|
|
3433
|
+
eventBus2.emit("user_overlay", data);
|
|
3327
3434
|
};
|
|
3328
|
-
uiEventBus.on("
|
|
3435
|
+
uiEventBus.on("user_overlay", handleUiOverlay);
|
|
3329
3436
|
eventBus2.on("backend_message", handleBackendMessage);
|
|
3330
3437
|
initializeAgent();
|
|
3331
3438
|
return () => {
|
|
3332
|
-
uiEventBus.off("
|
|
3439
|
+
uiEventBus.off("user_overlay", handleUiOverlay);
|
|
3333
3440
|
eventBus2.off("backend_message", handleBackendMessage);
|
|
3334
3441
|
};
|
|
3335
3442
|
}, [eventBus2, sessionId2, handleConfirmation]);
|
|
3336
3443
|
const renderInteractiveComponent = () => {
|
|
3337
3444
|
if (mcpStatus !== "connected") {
|
|
3338
|
-
return /* @__PURE__ */
|
|
3445
|
+
return /* @__PURE__ */ jsx15(Box15, { borderStyle: "round", borderColor: "black", children: /* @__PURE__ */ jsx15(
|
|
3339
3446
|
SessionInfoConnectingMCP_default,
|
|
3340
3447
|
{
|
|
3341
3448
|
sessionId: sessionId2,
|
|
@@ -3345,7 +3452,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3345
3452
|
) });
|
|
3346
3453
|
}
|
|
3347
3454
|
if (pendingConfirmation) {
|
|
3348
|
-
return /* @__PURE__ */
|
|
3455
|
+
return /* @__PURE__ */ jsx15(
|
|
3349
3456
|
ConfirmationPrompt,
|
|
3350
3457
|
{
|
|
3351
3458
|
toolCalls: pendingConfirmation,
|
|
@@ -3357,20 +3464,21 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3357
3464
|
}
|
|
3358
3465
|
);
|
|
3359
3466
|
}
|
|
3360
|
-
return /* @__PURE__ */
|
|
3361
|
-
isProcessing && !pendingConfirmation && /* @__PURE__ */
|
|
3362
|
-
/* @__PURE__ */
|
|
3467
|
+
return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", children: [
|
|
3468
|
+
isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx15(WorkingTimer, {}),
|
|
3469
|
+
/* @__PURE__ */ jsx15(
|
|
3363
3470
|
InputPrompt,
|
|
3364
3471
|
{
|
|
3365
3472
|
onSubmit: handleSubmit,
|
|
3366
|
-
isReadOnly: isProcessing,
|
|
3473
|
+
isReadOnly: isProcessing || isInitAgentActive,
|
|
3474
|
+
disableWhileProcessing: isInitAgentActive,
|
|
3367
3475
|
onInterrupt: handleInterrupt
|
|
3368
3476
|
}
|
|
3369
3477
|
)
|
|
3370
3478
|
] });
|
|
3371
3479
|
};
|
|
3372
|
-
return /* @__PURE__ */
|
|
3373
|
-
/* @__PURE__ */
|
|
3480
|
+
return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", children: [
|
|
3481
|
+
/* @__PURE__ */ jsx15(Static, { items: history, children: (item) => /* @__PURE__ */ jsx15(Box15, { children: item.component }, item.id) }),
|
|
3374
3482
|
renderInteractiveComponent()
|
|
3375
3483
|
] });
|
|
3376
3484
|
};
|