@nomad-e/bluma-cli 0.1.51 → 0.1.53
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/main.js +2405 -669
- package/package.json +3 -3
package/dist/main.js
CHANGED
|
@@ -496,19 +496,701 @@ var init_async_command = __esm({
|
|
|
496
496
|
}
|
|
497
497
|
});
|
|
498
498
|
|
|
499
|
+
// src/app/agent/runtime/session_registry.ts
|
|
500
|
+
var session_registry_exports = {};
|
|
501
|
+
__export(session_registry_exports, {
|
|
502
|
+
appendSessionLog: () => appendSessionLog,
|
|
503
|
+
getSession: () => getSession,
|
|
504
|
+
getSessionLogPath: () => getSessionLogPath,
|
|
505
|
+
listSessions: () => listSessions,
|
|
506
|
+
readSessionLog: () => readSessionLog,
|
|
507
|
+
registerSession: () => registerSession,
|
|
508
|
+
updateSession: () => updateSession
|
|
509
|
+
});
|
|
510
|
+
import fs15 from "fs";
|
|
511
|
+
import os8 from "os";
|
|
512
|
+
import path17 from "path";
|
|
513
|
+
function getRegistryDir() {
|
|
514
|
+
return path17.join(process.env.HOME || os8.homedir(), ".bluma", "registry");
|
|
515
|
+
}
|
|
516
|
+
function getRegistryFile() {
|
|
517
|
+
return path17.join(getRegistryDir(), "sessions.json");
|
|
518
|
+
}
|
|
519
|
+
function ensureRegistryDir() {
|
|
520
|
+
fs15.mkdirSync(getRegistryDir(), { recursive: true });
|
|
521
|
+
}
|
|
522
|
+
function readRegistry() {
|
|
523
|
+
ensureRegistryDir();
|
|
524
|
+
const file = getRegistryFile();
|
|
525
|
+
if (!fs15.existsSync(file)) {
|
|
526
|
+
return { entries: [] };
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
return JSON.parse(fs15.readFileSync(file, "utf-8"));
|
|
530
|
+
} catch {
|
|
531
|
+
return { entries: [] };
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function writeRegistry(state) {
|
|
535
|
+
ensureRegistryDir();
|
|
536
|
+
fs15.writeFileSync(getRegistryFile(), JSON.stringify(state, null, 2), "utf-8");
|
|
537
|
+
}
|
|
538
|
+
function getSessionLogPath(sessionId) {
|
|
539
|
+
ensureRegistryDir();
|
|
540
|
+
return path17.join(getRegistryDir(), `${sessionId}.jsonl`);
|
|
541
|
+
}
|
|
542
|
+
function registerSession(entry) {
|
|
543
|
+
const state = readRegistry();
|
|
544
|
+
const nextEntry = {
|
|
545
|
+
...entry,
|
|
546
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
547
|
+
logFile: getSessionLogPath(entry.sessionId)
|
|
548
|
+
};
|
|
549
|
+
state.entries = state.entries.filter((item) => item.sessionId !== entry.sessionId);
|
|
550
|
+
state.entries.unshift(nextEntry);
|
|
551
|
+
writeRegistry(state);
|
|
552
|
+
return nextEntry;
|
|
553
|
+
}
|
|
554
|
+
function updateSession(sessionId, patch) {
|
|
555
|
+
const state = readRegistry();
|
|
556
|
+
const index = state.entries.findIndex((entry) => entry.sessionId === sessionId);
|
|
557
|
+
if (index === -1) return null;
|
|
558
|
+
const nextEntry = {
|
|
559
|
+
...state.entries[index],
|
|
560
|
+
...patch,
|
|
561
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
562
|
+
};
|
|
563
|
+
state.entries[index] = nextEntry;
|
|
564
|
+
writeRegistry(state);
|
|
565
|
+
return nextEntry;
|
|
566
|
+
}
|
|
567
|
+
function listSessions() {
|
|
568
|
+
return readRegistry().entries;
|
|
569
|
+
}
|
|
570
|
+
function getSession(sessionId) {
|
|
571
|
+
return readRegistry().entries.find((entry) => entry.sessionId === sessionId) || null;
|
|
572
|
+
}
|
|
573
|
+
function appendSessionLog(sessionId, payload) {
|
|
574
|
+
const logFile = getSessionLogPath(sessionId);
|
|
575
|
+
fs15.appendFileSync(logFile, `${JSON.stringify(payload)}
|
|
576
|
+
`, "utf-8");
|
|
577
|
+
}
|
|
578
|
+
function readSessionLog(sessionId) {
|
|
579
|
+
const logFile = getSessionLogPath(sessionId);
|
|
580
|
+
if (!fs15.existsSync(logFile)) return [];
|
|
581
|
+
return fs15.readFileSync(logFile, "utf-8").split("\n").filter(Boolean);
|
|
582
|
+
}
|
|
583
|
+
var init_session_registry = __esm({
|
|
584
|
+
"src/app/agent/runtime/session_registry.ts"() {
|
|
585
|
+
"use strict";
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
// src/app/agent/tools/natives/agent_coordination.ts
|
|
590
|
+
var agent_coordination_exports = {};
|
|
591
|
+
__export(agent_coordination_exports, {
|
|
592
|
+
listAgents: () => listAgents,
|
|
593
|
+
spawnAgent: () => spawnAgent,
|
|
594
|
+
waitAgent: () => waitAgent
|
|
595
|
+
});
|
|
596
|
+
import fs16 from "fs";
|
|
597
|
+
import os9 from "os";
|
|
598
|
+
import path18 from "path";
|
|
599
|
+
import { spawn as spawn4 } from "child_process";
|
|
600
|
+
import { v4 as uuidv43 } from "uuid";
|
|
601
|
+
function readUserContextFromEnv() {
|
|
602
|
+
const raw = process.env.BLUMA_USER_CONTEXT_JSON;
|
|
603
|
+
if (!raw) return null;
|
|
604
|
+
try {
|
|
605
|
+
const parsed = JSON.parse(raw);
|
|
606
|
+
return {
|
|
607
|
+
conversationId: parsed.conversationId ?? null,
|
|
608
|
+
userId: parsed.userId ?? null,
|
|
609
|
+
userName: parsed.userName ?? null,
|
|
610
|
+
userEmail: parsed.userEmail ?? null,
|
|
611
|
+
companyId: parsed.companyId ?? null,
|
|
612
|
+
companyName: parsed.companyName ?? null
|
|
613
|
+
};
|
|
614
|
+
} catch {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function buildWorkerPayload(sessionId, args, parentSessionId, userContext) {
|
|
619
|
+
return {
|
|
620
|
+
message_id: sessionId,
|
|
621
|
+
session_id: sessionId,
|
|
622
|
+
from_agent: parentSessionId || "interactive",
|
|
623
|
+
to_agent: "bluma-worker",
|
|
624
|
+
action: "worker_task",
|
|
625
|
+
context: {
|
|
626
|
+
user_request: args.task,
|
|
627
|
+
coordinator_context: args.context || null,
|
|
628
|
+
worker_title: args.title || null,
|
|
629
|
+
worker_role: args.agent_type || "worker"
|
|
630
|
+
},
|
|
631
|
+
user_context: userContext || void 0,
|
|
632
|
+
metadata: {
|
|
633
|
+
sandbox: process.env.BLUMA_SANDBOX === "true",
|
|
634
|
+
sandbox_name: process.env.BLUMA_SANDBOX_NAME || void 0,
|
|
635
|
+
coordinator_worker: true,
|
|
636
|
+
parent_session_id: parentSessionId
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function extractLatestResult(sessionId) {
|
|
641
|
+
const lines = readSessionLog(sessionId);
|
|
642
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
643
|
+
try {
|
|
644
|
+
const parsed = JSON.parse(lines[index]);
|
|
645
|
+
if (parsed.event_type === "result") {
|
|
646
|
+
return parsed;
|
|
647
|
+
}
|
|
648
|
+
} catch {
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
function toAgentSummary(entry) {
|
|
654
|
+
return {
|
|
655
|
+
session_id: entry.sessionId,
|
|
656
|
+
title: entry.title,
|
|
657
|
+
kind: entry.kind,
|
|
658
|
+
status: entry.status,
|
|
659
|
+
started_at: entry.startedAt,
|
|
660
|
+
updated_at: entry.updatedAt,
|
|
661
|
+
pid: entry.pid || null,
|
|
662
|
+
metadata: entry.metadata || {}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
async function spawnAgent(args) {
|
|
666
|
+
if (!args?.task || typeof args.task !== "string") {
|
|
667
|
+
return { success: false, error: "task is required" };
|
|
668
|
+
}
|
|
669
|
+
const entrypoint = process.argv[1];
|
|
670
|
+
if (!entrypoint) {
|
|
671
|
+
return { success: false, error: "Unable to resolve CLI entrypoint for worker spawn" };
|
|
672
|
+
}
|
|
673
|
+
const sessionId = uuidv43();
|
|
674
|
+
const parentSessionId = process.env.BLUMA_SESSION_ID || null;
|
|
675
|
+
const userContext = readUserContextFromEnv();
|
|
676
|
+
const title = args.title || `worker:${args.agent_type || "worker"}`;
|
|
677
|
+
const payload = buildWorkerPayload(sessionId, args, parentSessionId, userContext);
|
|
678
|
+
const payloadDir = fs16.mkdtempSync(path18.join(os9.tmpdir(), "bluma-worker-"));
|
|
679
|
+
const payloadPath = path18.join(payloadDir, `${sessionId}.json`);
|
|
680
|
+
fs16.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
681
|
+
registerSession({
|
|
682
|
+
sessionId,
|
|
683
|
+
kind: "agent",
|
|
684
|
+
status: "running",
|
|
685
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
686
|
+
workdir: process.cwd(),
|
|
687
|
+
title,
|
|
688
|
+
metadata: {
|
|
689
|
+
action: "worker_task",
|
|
690
|
+
background: true,
|
|
691
|
+
coordinator_worker: true,
|
|
692
|
+
parent_session_id: parentSessionId,
|
|
693
|
+
agent_type: args.agent_type || "worker",
|
|
694
|
+
user_id: userContext?.userId ?? null,
|
|
695
|
+
company_id: userContext?.companyId ?? null
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
const child = spawn4(
|
|
699
|
+
process.execPath,
|
|
700
|
+
[entrypoint, "agent", "--input-file", payloadPath, "--background-worker", "--registry-session", sessionId],
|
|
701
|
+
{
|
|
702
|
+
detached: true,
|
|
703
|
+
stdio: "ignore",
|
|
704
|
+
cwd: process.cwd(),
|
|
705
|
+
env: {
|
|
706
|
+
...process.env,
|
|
707
|
+
BLUMA_PARENT_SESSION_ID: parentSessionId || ""
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
child.unref();
|
|
712
|
+
updateSession(sessionId, { pid: child.pid });
|
|
713
|
+
return {
|
|
714
|
+
success: true,
|
|
715
|
+
session_id: sessionId,
|
|
716
|
+
pid: child.pid || null,
|
|
717
|
+
parent_session_id: parentSessionId,
|
|
718
|
+
title,
|
|
719
|
+
status: "running"
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
async function waitAgent(args) {
|
|
723
|
+
const sessionId = args?.session_id;
|
|
724
|
+
if (!sessionId || typeof sessionId !== "string") {
|
|
725
|
+
return { success: false, error: "session_id is required" };
|
|
726
|
+
}
|
|
727
|
+
const timeoutMs = Math.max(1e3, Number(args.timeout_ms || 3e4));
|
|
728
|
+
const pollIntervalMs = Math.max(200, Number(args.poll_interval_ms || 1e3));
|
|
729
|
+
const deadline = Date.now() + timeoutMs;
|
|
730
|
+
while (Date.now() < deadline) {
|
|
731
|
+
const session2 = getSession(sessionId);
|
|
732
|
+
if (!session2) {
|
|
733
|
+
return { success: false, error: `Unknown session: ${sessionId}` };
|
|
734
|
+
}
|
|
735
|
+
if (session2.status !== "running") {
|
|
736
|
+
return {
|
|
737
|
+
success: true,
|
|
738
|
+
completed: true,
|
|
739
|
+
session: toAgentSummary(session2),
|
|
740
|
+
result: extractLatestResult(sessionId)
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollIntervalMs));
|
|
744
|
+
}
|
|
745
|
+
const session = getSession(sessionId);
|
|
746
|
+
if (!session) {
|
|
747
|
+
return { success: false, error: `Unknown session: ${sessionId}` };
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
success: true,
|
|
751
|
+
completed: false,
|
|
752
|
+
session: toAgentSummary(session),
|
|
753
|
+
result: extractLatestResult(sessionId),
|
|
754
|
+
message: `Timed out after ${timeoutMs}ms while waiting for agent ${sessionId}.`
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
async function listAgents(args = {}) {
|
|
758
|
+
const entries = listSessions().filter((entry) => entry.kind === "agent").filter((entry) => {
|
|
759
|
+
if (args.parent_session_id) {
|
|
760
|
+
return entry.metadata?.parent_session_id === args.parent_session_id;
|
|
761
|
+
}
|
|
762
|
+
return true;
|
|
763
|
+
}).filter((entry) => {
|
|
764
|
+
if (args.status) {
|
|
765
|
+
return entry.status === args.status;
|
|
766
|
+
}
|
|
767
|
+
return true;
|
|
768
|
+
}).map(toAgentSummary);
|
|
769
|
+
return {
|
|
770
|
+
success: true,
|
|
771
|
+
count: entries.length,
|
|
772
|
+
agents: entries
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
var init_agent_coordination = __esm({
|
|
776
|
+
"src/app/agent/tools/natives/agent_coordination.ts"() {
|
|
777
|
+
"use strict";
|
|
778
|
+
init_session_registry();
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
// src/app/agent/runtime/mailbox_registry.ts
|
|
783
|
+
var mailbox_registry_exports = {};
|
|
784
|
+
__export(mailbox_registry_exports, {
|
|
785
|
+
ensureMailbox: () => ensureMailbox,
|
|
786
|
+
listActiveMailboxes: () => listActiveMailboxes,
|
|
787
|
+
pruneMailbox: () => pruneMailbox,
|
|
788
|
+
readFromMailbox: () => readFromMailbox,
|
|
789
|
+
readSignals: () => readSignals,
|
|
790
|
+
removeMailbox: () => removeMailbox,
|
|
791
|
+
sendFollowUp: () => sendFollowUp,
|
|
792
|
+
sendPermissionRequest: () => sendPermissionRequest,
|
|
793
|
+
sendPermissionResponse: () => sendPermissionResponse,
|
|
794
|
+
sendProgressUpdate: () => sendProgressUpdate,
|
|
795
|
+
sendSignal: () => sendSignal,
|
|
796
|
+
sendToMailbox: () => sendToMailbox
|
|
797
|
+
});
|
|
798
|
+
import fs17 from "fs";
|
|
799
|
+
import os10 from "os";
|
|
800
|
+
import path19 from "path";
|
|
801
|
+
import { v4 as uuidv44 } from "uuid";
|
|
802
|
+
function getMailboxesDir() {
|
|
803
|
+
if (mailboxesDir) {
|
|
804
|
+
return mailboxesDir;
|
|
805
|
+
}
|
|
806
|
+
mailboxesDir = path19.join(process.env.HOME || os10.homedir(), ".bluma", "mailboxes");
|
|
807
|
+
fs17.mkdirSync(mailboxesDir, { recursive: true });
|
|
808
|
+
return mailboxesDir;
|
|
809
|
+
}
|
|
810
|
+
function getMailboxPath(sessionId, type) {
|
|
811
|
+
const dir = getMailboxesDir();
|
|
812
|
+
return path19.join(dir, `${sessionId}.${type}`);
|
|
813
|
+
}
|
|
814
|
+
function ensureMailbox(sessionId) {
|
|
815
|
+
const dir = getMailboxesDir();
|
|
816
|
+
const types = ["in", "out", "sig"];
|
|
817
|
+
for (const type of types) {
|
|
818
|
+
const filePath = path19.join(dir, `${sessionId}.${type}`);
|
|
819
|
+
if (!fs17.existsSync(filePath)) {
|
|
820
|
+
fs17.writeFileSync(filePath, "", "utf-8");
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
function sendToMailbox(sessionId, type, message2) {
|
|
825
|
+
const filePath = getMailboxPath(sessionId, type);
|
|
826
|
+
const fullMessage = {
|
|
827
|
+
...message2,
|
|
828
|
+
id: uuidv44(),
|
|
829
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
830
|
+
};
|
|
831
|
+
const line = JSON.stringify(fullMessage) + "\n";
|
|
832
|
+
fs17.appendFileSync(filePath, line, "utf-8");
|
|
833
|
+
return fullMessage.id;
|
|
834
|
+
}
|
|
835
|
+
function readFromMailbox(sessionId, type, lastReadId) {
|
|
836
|
+
const filePath = getMailboxPath(sessionId, type);
|
|
837
|
+
if (!fs17.existsSync(filePath)) {
|
|
838
|
+
return [];
|
|
839
|
+
}
|
|
840
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
841
|
+
if (!content.trim()) {
|
|
842
|
+
return [];
|
|
843
|
+
}
|
|
844
|
+
const lines = content.trim().split("\n");
|
|
845
|
+
const messages = [];
|
|
846
|
+
let shouldStartReading = !lastReadId;
|
|
847
|
+
for (const line of lines) {
|
|
848
|
+
if (!line.trim()) continue;
|
|
849
|
+
try {
|
|
850
|
+
const msg = JSON.parse(line);
|
|
851
|
+
if (lastReadId && !shouldStartReading) {
|
|
852
|
+
if (msg.id === lastReadId) {
|
|
853
|
+
shouldStartReading = true;
|
|
854
|
+
}
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
messages.push(msg);
|
|
858
|
+
} catch {
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return messages;
|
|
862
|
+
}
|
|
863
|
+
function pruneMailbox(sessionId, type, keepLast = 100) {
|
|
864
|
+
const filePath = getMailboxPath(sessionId, type);
|
|
865
|
+
if (!fs17.existsSync(filePath)) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
869
|
+
if (!content.trim()) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const lines = content.trim().split("\n").filter((l) => l.trim());
|
|
873
|
+
if (lines.length <= keepLast) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
const pruned = lines.slice(-keepLast);
|
|
877
|
+
fs17.writeFileSync(filePath, pruned.join("\n") + "\n", "utf-8");
|
|
878
|
+
}
|
|
879
|
+
function sendSignal(sessionId, type, data, messageId) {
|
|
880
|
+
const filePath = getMailboxPath(sessionId, "sig");
|
|
881
|
+
const signal = {
|
|
882
|
+
id: uuidv44(),
|
|
883
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
884
|
+
type,
|
|
885
|
+
messageId,
|
|
886
|
+
data
|
|
887
|
+
};
|
|
888
|
+
const line = JSON.stringify(signal) + "\n";
|
|
889
|
+
fs17.appendFileSync(filePath, line, "utf-8");
|
|
890
|
+
return signal.id;
|
|
891
|
+
}
|
|
892
|
+
function readSignals(sessionId, lastReadId) {
|
|
893
|
+
const filePath = getMailboxPath(sessionId, "sig");
|
|
894
|
+
if (!fs17.existsSync(filePath)) {
|
|
895
|
+
return [];
|
|
896
|
+
}
|
|
897
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
898
|
+
if (!content.trim()) {
|
|
899
|
+
return [];
|
|
900
|
+
}
|
|
901
|
+
const lines = content.trim().split("\n");
|
|
902
|
+
const signals = [];
|
|
903
|
+
let shouldStartReading = !lastReadId;
|
|
904
|
+
for (const line of lines) {
|
|
905
|
+
if (!line.trim()) continue;
|
|
906
|
+
try {
|
|
907
|
+
const signal = JSON.parse(line);
|
|
908
|
+
if (lastReadId && !shouldStartReading) {
|
|
909
|
+
if (signal.id === lastReadId) {
|
|
910
|
+
shouldStartReading = true;
|
|
911
|
+
}
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
signals.push(signal);
|
|
915
|
+
} catch {
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return signals;
|
|
919
|
+
}
|
|
920
|
+
function removeMailbox(sessionId) {
|
|
921
|
+
const dir = getMailboxesDir();
|
|
922
|
+
const types = ["in", "out", "sig"];
|
|
923
|
+
for (const type of types) {
|
|
924
|
+
const filePath = path19.join(dir, `${sessionId}.${type}`);
|
|
925
|
+
if (fs17.existsSync(filePath)) {
|
|
926
|
+
fs17.unlinkSync(filePath);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
function listActiveMailboxes() {
|
|
931
|
+
const dir = getMailboxesDir();
|
|
932
|
+
if (!fs17.existsSync(dir)) {
|
|
933
|
+
return [];
|
|
934
|
+
}
|
|
935
|
+
const files = fs17.readdirSync(dir);
|
|
936
|
+
const mailboxes = /* @__PURE__ */ new Map();
|
|
937
|
+
for (const file of files) {
|
|
938
|
+
const match = file.match(/^([a-f0-9-]+)\.(in|out|sig)$/);
|
|
939
|
+
if (!match) continue;
|
|
940
|
+
const sessionId = match[1];
|
|
941
|
+
const type = match[2];
|
|
942
|
+
if (!mailboxes.has(sessionId)) {
|
|
943
|
+
mailboxes.set(sessionId, { in: 0, out: 0, sig: 0 });
|
|
944
|
+
}
|
|
945
|
+
const filePath = path19.join(dir, file);
|
|
946
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
947
|
+
const lines = content.trim().split("\n").filter((l) => l.trim()).length;
|
|
948
|
+
mailboxes.get(sessionId)[type] = lines;
|
|
949
|
+
}
|
|
950
|
+
return Array.from(mailboxes.entries()).map(([sessionId, counts]) => ({
|
|
951
|
+
sessionId,
|
|
952
|
+
hasMessages: counts.in > 0 || counts.out > 0 || counts.sig > 0
|
|
953
|
+
}));
|
|
954
|
+
}
|
|
955
|
+
function sendFollowUp(workerSessionId, coordinatorSessionId, message2, requiresAck = true) {
|
|
956
|
+
return sendToMailbox(workerSessionId, "in", {
|
|
957
|
+
type: "follow_up",
|
|
958
|
+
from: coordinatorSessionId,
|
|
959
|
+
to: workerSessionId,
|
|
960
|
+
content: message2,
|
|
961
|
+
metadata: {
|
|
962
|
+
priority: "normal",
|
|
963
|
+
requiresAck
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
function sendProgressUpdate(coordinatorSessionId, workerSessionId, percent, currentTask, toolCallsCount, tokensUsed) {
|
|
968
|
+
return sendToMailbox(coordinatorSessionId, "out", {
|
|
969
|
+
type: "progress_update",
|
|
970
|
+
from: workerSessionId,
|
|
971
|
+
to: coordinatorSessionId,
|
|
972
|
+
content: currentTask || `Progress: ${percent}%`,
|
|
973
|
+
metadata: {
|
|
974
|
+
priority: "low",
|
|
975
|
+
requiresAck: false,
|
|
976
|
+
progress: {
|
|
977
|
+
percent,
|
|
978
|
+
currentTask,
|
|
979
|
+
toolCallsCount,
|
|
980
|
+
tokensUsed
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
function sendPermissionRequest(coordinatorSessionId, workerSessionId, toolName, toolArgs, reason) {
|
|
986
|
+
return sendToMailbox(coordinatorSessionId, "out", {
|
|
987
|
+
type: "permission_request",
|
|
988
|
+
from: workerSessionId,
|
|
989
|
+
to: coordinatorSessionId,
|
|
990
|
+
content: `Permission request: ${toolName}`,
|
|
991
|
+
metadata: {
|
|
992
|
+
priority: "high",
|
|
993
|
+
requiresAck: true,
|
|
994
|
+
permission: {
|
|
995
|
+
toolName,
|
|
996
|
+
toolArgs,
|
|
997
|
+
reason
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
function sendPermissionResponse(workerSessionId, coordinatorSessionId, approved, reason) {
|
|
1003
|
+
return sendToMailbox(workerSessionId, "in", {
|
|
1004
|
+
type: "permission_response",
|
|
1005
|
+
from: coordinatorSessionId,
|
|
1006
|
+
to: workerSessionId,
|
|
1007
|
+
content: approved ? "approved" : "denied",
|
|
1008
|
+
metadata: {
|
|
1009
|
+
priority: "urgent",
|
|
1010
|
+
requiresAck: true,
|
|
1011
|
+
permission: {
|
|
1012
|
+
toolName: "permission_response",
|
|
1013
|
+
toolArgs: { approved, reason },
|
|
1014
|
+
reason: reason || ""
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
var mailboxesDir;
|
|
1020
|
+
var init_mailbox_registry = __esm({
|
|
1021
|
+
"src/app/agent/runtime/mailbox_registry.ts"() {
|
|
1022
|
+
"use strict";
|
|
1023
|
+
mailboxesDir = null;
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
// src/app/agent/tools/natives/list_mailbox_messages.ts
|
|
1028
|
+
var list_mailbox_messages_exports = {};
|
|
1029
|
+
__export(list_mailbox_messages_exports, {
|
|
1030
|
+
listMailboxMessages: () => listMailboxMessages
|
|
1031
|
+
});
|
|
1032
|
+
async function listMailboxMessages(args = {}) {
|
|
1033
|
+
const {
|
|
1034
|
+
from,
|
|
1035
|
+
type,
|
|
1036
|
+
unreadOnly = true,
|
|
1037
|
+
lastReadId,
|
|
1038
|
+
includeSignals = false,
|
|
1039
|
+
prune = false
|
|
1040
|
+
} = args;
|
|
1041
|
+
const coordinatorSessionId = process.env.BLUMA_SESSION_ID || "unknown";
|
|
1042
|
+
const allMessages = [];
|
|
1043
|
+
if (from) {
|
|
1044
|
+
const messages = readFromMailbox(from, "out", lastReadId);
|
|
1045
|
+
for (const msg of messages) {
|
|
1046
|
+
if (!type || msg.type === type) {
|
|
1047
|
+
allMessages.push({ ...msg, direction: "out" });
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
} else {
|
|
1051
|
+
const { listActiveMailboxes: listActiveMailboxes2 } = await Promise.resolve().then(() => (init_mailbox_registry(), mailbox_registry_exports));
|
|
1052
|
+
const { getSession: getSession2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
1053
|
+
const activeMailboxes = listActiveMailboxes2();
|
|
1054
|
+
const workers = [];
|
|
1055
|
+
for (const { sessionId } of activeMailboxes) {
|
|
1056
|
+
const session = getSession2(sessionId);
|
|
1057
|
+
if (!session || session.kind !== "agent") continue;
|
|
1058
|
+
const messages = readFromMailbox(sessionId, "out", lastReadId);
|
|
1059
|
+
let messageCount = 0;
|
|
1060
|
+
for (const msg of messages) {
|
|
1061
|
+
if (!type || msg.type === type) {
|
|
1062
|
+
allMessages.push({ ...msg, direction: "out" });
|
|
1063
|
+
messageCount++;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
workers.push({
|
|
1067
|
+
sessionId,
|
|
1068
|
+
status: session.status,
|
|
1069
|
+
messageCount
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
if (workers.length > 0) {
|
|
1073
|
+
return {
|
|
1074
|
+
success: true,
|
|
1075
|
+
messages: allMessages.sort(
|
|
1076
|
+
(a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
1077
|
+
),
|
|
1078
|
+
count: allMessages.length,
|
|
1079
|
+
lastMessageId: allMessages[allMessages.length - 1]?.id,
|
|
1080
|
+
workers
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
allMessages.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
1085
|
+
let signals;
|
|
1086
|
+
if (includeSignals && from) {
|
|
1087
|
+
signals = readSignals(from, lastReadId);
|
|
1088
|
+
}
|
|
1089
|
+
if (prune && from) {
|
|
1090
|
+
pruneMailbox(from, "out", 50);
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
success: true,
|
|
1094
|
+
messages: allMessages,
|
|
1095
|
+
signals,
|
|
1096
|
+
count: allMessages.length,
|
|
1097
|
+
lastMessageId: allMessages[allMessages.length - 1]?.id
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
var init_list_mailbox_messages = __esm({
|
|
1101
|
+
"src/app/agent/tools/natives/list_mailbox_messages.ts"() {
|
|
1102
|
+
"use strict";
|
|
1103
|
+
init_mailbox_registry();
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
// src/app/agent/tools/natives/poll_mailbox.ts
|
|
1108
|
+
var poll_mailbox_exports = {};
|
|
1109
|
+
__export(poll_mailbox_exports, {
|
|
1110
|
+
pollMailbox: () => pollMailbox
|
|
1111
|
+
});
|
|
1112
|
+
async function pollMailbox(args = {}) {
|
|
1113
|
+
const {
|
|
1114
|
+
timeout = 0,
|
|
1115
|
+
pollInterval = 500,
|
|
1116
|
+
types,
|
|
1117
|
+
lastReadId,
|
|
1118
|
+
includeSignals = true
|
|
1119
|
+
} = args;
|
|
1120
|
+
const sessionId = process.env.BLUMA_SESSION_ID;
|
|
1121
|
+
if (!sessionId) {
|
|
1122
|
+
return {
|
|
1123
|
+
success: false,
|
|
1124
|
+
messages: [],
|
|
1125
|
+
count: 0,
|
|
1126
|
+
hasNewMessages: false,
|
|
1127
|
+
error: "BLUMA_SESSION_ID not set"
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
ensureMailbox(sessionId);
|
|
1131
|
+
const deadline = Date.now() + timeout;
|
|
1132
|
+
let messages = [];
|
|
1133
|
+
let signals = [];
|
|
1134
|
+
do {
|
|
1135
|
+
messages = readFromMailbox(sessionId, "in", lastReadId);
|
|
1136
|
+
if (types && types.length > 0) {
|
|
1137
|
+
messages = messages.filter((msg) => types.includes(msg.type));
|
|
1138
|
+
}
|
|
1139
|
+
if (messages.length > 0) {
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
if (timeout > 0 && Date.now() < deadline) {
|
|
1143
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
1144
|
+
}
|
|
1145
|
+
} while (timeout > 0 && Date.now() < deadline);
|
|
1146
|
+
if (includeSignals) {
|
|
1147
|
+
signals = readSignals(sessionId, lastReadId);
|
|
1148
|
+
}
|
|
1149
|
+
const followUpMessage = messages.find((msg) => msg.type === "follow_up");
|
|
1150
|
+
const followUp = followUpMessage ? {
|
|
1151
|
+
message: followUpMessage.content,
|
|
1152
|
+
priority: followUpMessage.metadata?.priority || "normal",
|
|
1153
|
+
messageId: followUpMessage.id
|
|
1154
|
+
} : void 0;
|
|
1155
|
+
const cancelRequested = messages.some((msg) => msg.type === "cancel_request") || signals.some((sig) => sig.type === "progress" && sig.data?.event === "cancel");
|
|
1156
|
+
const permissionResponseMsg = messages.find((msg) => msg.type === "permission_response");
|
|
1157
|
+
const permissionResponse = permissionResponseMsg ? {
|
|
1158
|
+
approved: permissionResponseMsg.content === "approved",
|
|
1159
|
+
reason: permissionResponseMsg.metadata?.permission?.reason,
|
|
1160
|
+
messageId: permissionResponseMsg.id
|
|
1161
|
+
} : void 0;
|
|
1162
|
+
return {
|
|
1163
|
+
success: true,
|
|
1164
|
+
messages,
|
|
1165
|
+
signals,
|
|
1166
|
+
count: messages.length,
|
|
1167
|
+
hasNewMessages: messages.length > 0,
|
|
1168
|
+
lastMessageId: messages[messages.length - 1]?.id,
|
|
1169
|
+
followUp,
|
|
1170
|
+
cancelRequested,
|
|
1171
|
+
permissionResponse
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
var init_poll_mailbox = __esm({
|
|
1175
|
+
"src/app/agent/tools/natives/poll_mailbox.ts"() {
|
|
1176
|
+
"use strict";
|
|
1177
|
+
init_mailbox_registry();
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
|
|
499
1181
|
// src/main.ts
|
|
500
|
-
import
|
|
1182
|
+
import React19 from "react";
|
|
501
1183
|
import { render } from "ink";
|
|
502
1184
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
503
|
-
import
|
|
504
|
-
import
|
|
1185
|
+
import fs29 from "fs";
|
|
1186
|
+
import path34 from "path";
|
|
505
1187
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
506
1188
|
import { spawn as spawn5 } from "child_process";
|
|
507
|
-
import { v4 as
|
|
1189
|
+
import { v4 as uuidv48 } from "uuid";
|
|
508
1190
|
|
|
509
1191
|
// src/app/ui/App.tsx
|
|
510
|
-
import { useState as
|
|
511
|
-
import { Box as
|
|
1192
|
+
import { useState as useState11, useEffect as useEffect11, useRef as useRef6, useCallback as useCallback4, memo as memo15 } from "react";
|
|
1193
|
+
import { Box as Box26, Text as Text25, Static, useInput as useInput6 } from "ink";
|
|
512
1194
|
|
|
513
1195
|
// src/app/ui/layout.tsx
|
|
514
1196
|
import { Box, Text } from "ink";
|
|
@@ -2970,12 +3652,12 @@ function EditToolDiffPanel({
|
|
|
2970
3652
|
maxHeight = EDIT_DIFF_PREVIEW_MAX_LINES,
|
|
2971
3653
|
fallbackSnippet
|
|
2972
3654
|
}) {
|
|
2973
|
-
const
|
|
3655
|
+
const path35 = filePath.trim() || "unknown file";
|
|
2974
3656
|
const diff = diffText?.trim() ?? "";
|
|
2975
3657
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
2976
3658
|
/* @__PURE__ */ jsx5(Box5, { flexDirection: "row", flexWrap: "wrap", children: /* @__PURE__ */ jsxs5(Text5, { color: isNewFile ? BLUMA_TERMINAL.success : void 0, children: [
|
|
2977
3659
|
isNewFile ? "Created " : "Wrote to ",
|
|
2978
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, children:
|
|
3660
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: path35 })
|
|
2979
3661
|
] }) }),
|
|
2980
3662
|
description ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "wrap", children: description }) : null,
|
|
2981
3663
|
diff.length > 0 ? /* @__PURE__ */ jsx5(Box5, { marginTop: 0, children: /* @__PURE__ */ jsx5(SimpleDiff, { text: diff, maxHeight, frame: true }) }) : fallbackSnippet ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 0, children: [
|
|
@@ -3209,7 +3891,7 @@ var renderFindByName = ({ args }) => {
|
|
|
3209
3891
|
var renderGrepSearch = ({ args }) => {
|
|
3210
3892
|
const parsed = parseArgs(args);
|
|
3211
3893
|
const query = parsed.query || "";
|
|
3212
|
-
const
|
|
3894
|
+
const path35 = parsed.path || ".";
|
|
3213
3895
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "row", flexWrap: "wrap", children: [
|
|
3214
3896
|
/* @__PURE__ */ jsxs7(Text7, { color: BLUMA_TERMINAL.muted, children: [
|
|
3215
3897
|
'"',
|
|
@@ -3218,7 +3900,7 @@ var renderGrepSearch = ({ args }) => {
|
|
|
3218
3900
|
] }),
|
|
3219
3901
|
/* @__PURE__ */ jsxs7(Text7, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, children: [
|
|
3220
3902
|
" ",
|
|
3221
|
-
|
|
3903
|
+
path35
|
|
3222
3904
|
] })
|
|
3223
3905
|
] });
|
|
3224
3906
|
};
|
|
@@ -3625,7 +4307,7 @@ var ConfirmationPromptComponent = ({ toolCalls, preview, onDecision }) => {
|
|
|
3625
4307
|
const rule = TERMINAL_RULE_CHAR.repeat(Math.min(cols - 2, 64));
|
|
3626
4308
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, paddingX: 0, children: [
|
|
3627
4309
|
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: rule }),
|
|
3628
|
-
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", marginTop: 0, marginBottom: 1, flexWrap: "wrap", alignItems: "
|
|
4310
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", marginTop: 0, marginBottom: 1, flexWrap: "wrap", alignItems: "flex-end", children: [
|
|
3629
4311
|
/* @__PURE__ */ jsx8(Text8, { color: shellLike ? BLUMA_TERMINAL.m3OnSurface : BLUMA_TERMINAL.inactive, children: TOOL_INVOCATION_MARK }),
|
|
3630
4312
|
/* @__PURE__ */ jsx8(Text8, { color: shellLike ? BLUMA_TERMINAL.m3OnSurface : BLUMA_TERMINAL.claude, bold: true, children: getToolInvocationTitle(toolName, toolCall.function.arguments) }),
|
|
3631
4313
|
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \xB7 permission required" }),
|
|
@@ -3648,12 +4330,12 @@ var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
|
3648
4330
|
|
|
3649
4331
|
// src/app/agent/agent.ts
|
|
3650
4332
|
import * as dotenv from "dotenv";
|
|
3651
|
-
import
|
|
3652
|
-
import
|
|
4333
|
+
import path32 from "path";
|
|
4334
|
+
import os21 from "os";
|
|
3653
4335
|
|
|
3654
4336
|
// src/app/agent/tool_invoker.ts
|
|
3655
|
-
import { promises as
|
|
3656
|
-
import
|
|
4337
|
+
import { promises as fs18 } from "fs";
|
|
4338
|
+
import path20 from "path";
|
|
3657
4339
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3658
4340
|
|
|
3659
4341
|
// src/app/agent/tools/natives/edit.ts
|
|
@@ -5345,13 +6027,11 @@ init_async_command();
|
|
|
5345
6027
|
init_sandbox_policy();
|
|
5346
6028
|
import path13 from "path";
|
|
5347
6029
|
import { promises as fs10 } from "fs";
|
|
5348
|
-
import os7 from "os";
|
|
5349
6030
|
var artifactsDir = null;
|
|
5350
6031
|
async function getArtifactsDir() {
|
|
5351
6032
|
if (artifactsDir) return artifactsDir;
|
|
5352
6033
|
const policy = getSandboxPolicy();
|
|
5353
|
-
const
|
|
5354
|
-
const baseDir = policy.isSandbox ? path13.join(policy.workspaceRoot, "artifacts") : path13.join(homeDir, ".bluma", "artifacts");
|
|
6034
|
+
const baseDir = path13.join(policy.workspaceRoot, ".bluma", "artifacts");
|
|
5355
6035
|
const sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
|
5356
6036
|
artifactsDir = path13.join(baseDir, sessionId);
|
|
5357
6037
|
await fs10.mkdir(artifactsDir, { recursive: true });
|
|
@@ -5887,7 +6567,7 @@ ${skill.content}`;
|
|
|
5887
6567
|
// src/app/agent/tools/natives/coding_memory.ts
|
|
5888
6568
|
import * as fs11 from "fs";
|
|
5889
6569
|
import * as path14 from "path";
|
|
5890
|
-
import
|
|
6570
|
+
import os7 from "os";
|
|
5891
6571
|
var PROMPT_DEFAULT_MAX_TOTAL = 1e4;
|
|
5892
6572
|
var PROMPT_DEFAULT_MAX_NOTES = 25;
|
|
5893
6573
|
var PROMPT_DEFAULT_PREVIEW = 500;
|
|
@@ -5895,7 +6575,7 @@ function readCodingMemoryForPrompt(options) {
|
|
|
5895
6575
|
const maxTotal = options?.maxTotalChars ?? PROMPT_DEFAULT_MAX_TOTAL;
|
|
5896
6576
|
const maxNotes = options?.maxNotes ?? PROMPT_DEFAULT_MAX_NOTES;
|
|
5897
6577
|
const preview = options?.previewCharsPerNote ?? PROMPT_DEFAULT_PREVIEW;
|
|
5898
|
-
const globalPath = path14.join(
|
|
6578
|
+
const globalPath = path14.join(os7.homedir(), ".bluma", "coding_memory.json");
|
|
5899
6579
|
const legacyPath = path14.join(process.cwd(), ".bluma", "coding_memory.json");
|
|
5900
6580
|
let raw = null;
|
|
5901
6581
|
try {
|
|
@@ -5940,7 +6620,7 @@ var memoryStore = [];
|
|
|
5940
6620
|
var nextId = 1;
|
|
5941
6621
|
var loaded = false;
|
|
5942
6622
|
function getMemoryFilePath() {
|
|
5943
|
-
return path14.join(
|
|
6623
|
+
return path14.join(os7.homedir(), ".bluma", "coding_memory.json");
|
|
5944
6624
|
}
|
|
5945
6625
|
function getLegacyMemoryFilePath() {
|
|
5946
6626
|
return path14.join(process.cwd(), ".bluma", "coding_memory.json");
|
|
@@ -6868,294 +7548,195 @@ function stripHtml(html) {
|
|
|
6868
7548
|
}
|
|
6869
7549
|
async function webFetch(args) {
|
|
6870
7550
|
const maxChars = Math.max(500, Math.min(args.max_chars ?? DEFAULT_MAX_CHARS, 5e4));
|
|
6871
|
-
const url = String(args.url || "").trim();
|
|
6872
|
-
if (!/^https?:\/\//i.test(url)) {
|
|
6873
|
-
return {
|
|
6874
|
-
success: false,
|
|
6875
|
-
url,
|
|
6876
|
-
error: "url must start with http:// or https://"
|
|
6877
|
-
};
|
|
6878
|
-
}
|
|
6879
|
-
try {
|
|
6880
|
-
const response = await fetchUrl(url);
|
|
6881
|
-
const contentType = response.contentType || "unknown";
|
|
6882
|
-
const raw = /html/i.test(contentType) ? stripHtml(response.body) : response.body.trim();
|
|
6883
|
-
const truncated = raw.length > maxChars;
|
|
6884
|
-
const content = truncated ? `${raw.slice(0, maxChars)}
|
|
6885
|
-
|
|
6886
|
-
[...truncated...]` : raw;
|
|
6887
|
-
return {
|
|
6888
|
-
success: true,
|
|
6889
|
-
url,
|
|
6890
|
-
status_code: response.statusCode,
|
|
6891
|
-
content_type: contentType,
|
|
6892
|
-
content,
|
|
6893
|
-
truncated
|
|
6894
|
-
};
|
|
6895
|
-
} catch (error) {
|
|
6896
|
-
return {
|
|
6897
|
-
success: false,
|
|
6898
|
-
url,
|
|
6899
|
-
error: error.message || String(error)
|
|
6900
|
-
};
|
|
6901
|
-
}
|
|
6902
|
-
}
|
|
6903
|
-
|
|
6904
|
-
// src/app/agent/tools/natives/agent_coordination.ts
|
|
6905
|
-
import fs16 from "fs";
|
|
6906
|
-
import os10 from "os";
|
|
6907
|
-
import path18 from "path";
|
|
6908
|
-
import { spawn as spawn4 } from "child_process";
|
|
6909
|
-
import { v4 as uuidv43 } from "uuid";
|
|
6910
|
-
|
|
6911
|
-
// src/app/agent/runtime/session_registry.ts
|
|
6912
|
-
import fs15 from "fs";
|
|
6913
|
-
import os9 from "os";
|
|
6914
|
-
import path17 from "path";
|
|
6915
|
-
function getRegistryDir() {
|
|
6916
|
-
return path17.join(process.env.HOME || os9.homedir(), ".bluma", "registry");
|
|
6917
|
-
}
|
|
6918
|
-
function getRegistryFile() {
|
|
6919
|
-
return path17.join(getRegistryDir(), "sessions.json");
|
|
6920
|
-
}
|
|
6921
|
-
function ensureRegistryDir() {
|
|
6922
|
-
fs15.mkdirSync(getRegistryDir(), { recursive: true });
|
|
6923
|
-
}
|
|
6924
|
-
function readRegistry() {
|
|
6925
|
-
ensureRegistryDir();
|
|
6926
|
-
const file = getRegistryFile();
|
|
6927
|
-
if (!fs15.existsSync(file)) {
|
|
6928
|
-
return { entries: [] };
|
|
6929
|
-
}
|
|
6930
|
-
try {
|
|
6931
|
-
return JSON.parse(fs15.readFileSync(file, "utf-8"));
|
|
6932
|
-
} catch {
|
|
6933
|
-
return { entries: [] };
|
|
6934
|
-
}
|
|
6935
|
-
}
|
|
6936
|
-
function writeRegistry(state) {
|
|
6937
|
-
ensureRegistryDir();
|
|
6938
|
-
fs15.writeFileSync(getRegistryFile(), JSON.stringify(state, null, 2), "utf-8");
|
|
6939
|
-
}
|
|
6940
|
-
function getSessionLogPath(sessionId) {
|
|
6941
|
-
ensureRegistryDir();
|
|
6942
|
-
return path17.join(getRegistryDir(), `${sessionId}.jsonl`);
|
|
6943
|
-
}
|
|
6944
|
-
function registerSession(entry) {
|
|
6945
|
-
const state = readRegistry();
|
|
6946
|
-
const nextEntry = {
|
|
6947
|
-
...entry,
|
|
6948
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6949
|
-
logFile: getSessionLogPath(entry.sessionId)
|
|
6950
|
-
};
|
|
6951
|
-
state.entries = state.entries.filter((item) => item.sessionId !== entry.sessionId);
|
|
6952
|
-
state.entries.unshift(nextEntry);
|
|
6953
|
-
writeRegistry(state);
|
|
6954
|
-
return nextEntry;
|
|
6955
|
-
}
|
|
6956
|
-
function updateSession(sessionId, patch) {
|
|
6957
|
-
const state = readRegistry();
|
|
6958
|
-
const index = state.entries.findIndex((entry) => entry.sessionId === sessionId);
|
|
6959
|
-
if (index === -1) return null;
|
|
6960
|
-
const nextEntry = {
|
|
6961
|
-
...state.entries[index],
|
|
6962
|
-
...patch,
|
|
6963
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6964
|
-
};
|
|
6965
|
-
state.entries[index] = nextEntry;
|
|
6966
|
-
writeRegistry(state);
|
|
6967
|
-
return nextEntry;
|
|
6968
|
-
}
|
|
6969
|
-
function listSessions() {
|
|
6970
|
-
return readRegistry().entries;
|
|
6971
|
-
}
|
|
6972
|
-
function getSession(sessionId) {
|
|
6973
|
-
return readRegistry().entries.find((entry) => entry.sessionId === sessionId) || null;
|
|
6974
|
-
}
|
|
6975
|
-
function appendSessionLog(sessionId, payload) {
|
|
6976
|
-
const logFile = getSessionLogPath(sessionId);
|
|
6977
|
-
fs15.appendFileSync(logFile, `${JSON.stringify(payload)}
|
|
6978
|
-
`, "utf-8");
|
|
6979
|
-
}
|
|
6980
|
-
function readSessionLog(sessionId) {
|
|
6981
|
-
const logFile = getSessionLogPath(sessionId);
|
|
6982
|
-
if (!fs15.existsSync(logFile)) return [];
|
|
6983
|
-
return fs15.readFileSync(logFile, "utf-8").split("\n").filter(Boolean);
|
|
6984
|
-
}
|
|
6985
|
-
|
|
6986
|
-
// src/app/agent/tools/natives/agent_coordination.ts
|
|
6987
|
-
function readUserContextFromEnv() {
|
|
6988
|
-
const raw = process.env.BLUMA_USER_CONTEXT_JSON;
|
|
6989
|
-
if (!raw) return null;
|
|
6990
|
-
try {
|
|
6991
|
-
const parsed = JSON.parse(raw);
|
|
6992
|
-
return {
|
|
6993
|
-
conversationId: parsed.conversationId ?? null,
|
|
6994
|
-
userId: parsed.userId ?? null,
|
|
6995
|
-
userName: parsed.userName ?? null,
|
|
6996
|
-
userEmail: parsed.userEmail ?? null,
|
|
6997
|
-
companyId: parsed.companyId ?? null,
|
|
6998
|
-
companyName: parsed.companyName ?? null
|
|
6999
|
-
};
|
|
7000
|
-
} catch {
|
|
7001
|
-
return null;
|
|
7002
|
-
}
|
|
7003
|
-
}
|
|
7004
|
-
function buildWorkerPayload(sessionId, args, parentSessionId, userContext) {
|
|
7005
|
-
return {
|
|
7006
|
-
message_id: sessionId,
|
|
7007
|
-
session_id: sessionId,
|
|
7008
|
-
from_agent: parentSessionId || "interactive",
|
|
7009
|
-
to_agent: "bluma-worker",
|
|
7010
|
-
action: "worker_task",
|
|
7011
|
-
context: {
|
|
7012
|
-
user_request: args.task,
|
|
7013
|
-
coordinator_context: args.context || null,
|
|
7014
|
-
worker_title: args.title || null,
|
|
7015
|
-
worker_role: args.agent_type || "worker"
|
|
7016
|
-
},
|
|
7017
|
-
user_context: userContext || void 0,
|
|
7018
|
-
metadata: {
|
|
7019
|
-
sandbox: process.env.BLUMA_SANDBOX === "true",
|
|
7020
|
-
sandbox_name: process.env.BLUMA_SANDBOX_NAME || void 0,
|
|
7021
|
-
coordinator_worker: true,
|
|
7022
|
-
parent_session_id: parentSessionId
|
|
7023
|
-
}
|
|
7024
|
-
};
|
|
7025
|
-
}
|
|
7026
|
-
function extractLatestResult(sessionId) {
|
|
7027
|
-
const lines = readSessionLog(sessionId);
|
|
7028
|
-
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
7029
|
-
try {
|
|
7030
|
-
const parsed = JSON.parse(lines[index]);
|
|
7031
|
-
if (parsed.event_type === "result") {
|
|
7032
|
-
return parsed;
|
|
7033
|
-
}
|
|
7034
|
-
} catch {
|
|
7035
|
-
}
|
|
7036
|
-
}
|
|
7037
|
-
return null;
|
|
7038
|
-
}
|
|
7039
|
-
function toAgentSummary(entry) {
|
|
7040
|
-
return {
|
|
7041
|
-
session_id: entry.sessionId,
|
|
7042
|
-
title: entry.title,
|
|
7043
|
-
kind: entry.kind,
|
|
7044
|
-
status: entry.status,
|
|
7045
|
-
started_at: entry.startedAt,
|
|
7046
|
-
updated_at: entry.updatedAt,
|
|
7047
|
-
pid: entry.pid || null,
|
|
7048
|
-
metadata: entry.metadata || {}
|
|
7049
|
-
};
|
|
7050
|
-
}
|
|
7051
|
-
async function spawnAgent(args) {
|
|
7052
|
-
if (!args?.task || typeof args.task !== "string") {
|
|
7053
|
-
return { success: false, error: "task is required" };
|
|
7054
|
-
}
|
|
7055
|
-
const entrypoint = process.argv[1];
|
|
7056
|
-
if (!entrypoint) {
|
|
7057
|
-
return { success: false, error: "Unable to resolve CLI entrypoint for worker spawn" };
|
|
7058
|
-
}
|
|
7059
|
-
const sessionId = uuidv43();
|
|
7060
|
-
const parentSessionId = process.env.BLUMA_SESSION_ID || null;
|
|
7061
|
-
const userContext = readUserContextFromEnv();
|
|
7062
|
-
const title = args.title || `worker:${args.agent_type || "worker"}`;
|
|
7063
|
-
const payload = buildWorkerPayload(sessionId, args, parentSessionId, userContext);
|
|
7064
|
-
const payloadDir = fs16.mkdtempSync(path18.join(os10.tmpdir(), "bluma-worker-"));
|
|
7065
|
-
const payloadPath = path18.join(payloadDir, `${sessionId}.json`);
|
|
7066
|
-
fs16.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
7067
|
-
registerSession({
|
|
7068
|
-
sessionId,
|
|
7069
|
-
kind: "agent",
|
|
7070
|
-
status: "running",
|
|
7071
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7072
|
-
workdir: process.cwd(),
|
|
7073
|
-
title,
|
|
7074
|
-
metadata: {
|
|
7075
|
-
action: "worker_task",
|
|
7076
|
-
background: true,
|
|
7077
|
-
coordinator_worker: true,
|
|
7078
|
-
parent_session_id: parentSessionId,
|
|
7079
|
-
agent_type: args.agent_type || "worker",
|
|
7080
|
-
user_id: userContext?.userId ?? null,
|
|
7081
|
-
company_id: userContext?.companyId ?? null
|
|
7082
|
-
}
|
|
7083
|
-
});
|
|
7084
|
-
const child = spawn4(
|
|
7085
|
-
process.execPath,
|
|
7086
|
-
[entrypoint, "agent", "--input-file", payloadPath, "--background-worker", "--registry-session", sessionId],
|
|
7087
|
-
{
|
|
7088
|
-
detached: true,
|
|
7089
|
-
stdio: "ignore",
|
|
7090
|
-
cwd: process.cwd(),
|
|
7091
|
-
env: {
|
|
7092
|
-
...process.env,
|
|
7093
|
-
BLUMA_PARENT_SESSION_ID: parentSessionId || ""
|
|
7094
|
-
}
|
|
7095
|
-
}
|
|
7096
|
-
);
|
|
7097
|
-
child.unref();
|
|
7098
|
-
updateSession(sessionId, { pid: child.pid });
|
|
7099
|
-
return {
|
|
7100
|
-
success: true,
|
|
7101
|
-
session_id: sessionId,
|
|
7102
|
-
pid: child.pid || null,
|
|
7103
|
-
parent_session_id: parentSessionId,
|
|
7104
|
-
title,
|
|
7105
|
-
status: "running"
|
|
7106
|
-
};
|
|
7551
|
+
const url = String(args.url || "").trim();
|
|
7552
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
7553
|
+
return {
|
|
7554
|
+
success: false,
|
|
7555
|
+
url,
|
|
7556
|
+
error: "url must start with http:// or https://"
|
|
7557
|
+
};
|
|
7558
|
+
}
|
|
7559
|
+
try {
|
|
7560
|
+
const response = await fetchUrl(url);
|
|
7561
|
+
const contentType = response.contentType || "unknown";
|
|
7562
|
+
const raw = /html/i.test(contentType) ? stripHtml(response.body) : response.body.trim();
|
|
7563
|
+
const truncated = raw.length > maxChars;
|
|
7564
|
+
const content = truncated ? `${raw.slice(0, maxChars)}
|
|
7565
|
+
|
|
7566
|
+
[...truncated...]` : raw;
|
|
7567
|
+
return {
|
|
7568
|
+
success: true,
|
|
7569
|
+
url,
|
|
7570
|
+
status_code: response.statusCode,
|
|
7571
|
+
content_type: contentType,
|
|
7572
|
+
content,
|
|
7573
|
+
truncated
|
|
7574
|
+
};
|
|
7575
|
+
} catch (error) {
|
|
7576
|
+
return {
|
|
7577
|
+
success: false,
|
|
7578
|
+
url,
|
|
7579
|
+
error: error.message || String(error)
|
|
7580
|
+
};
|
|
7581
|
+
}
|
|
7107
7582
|
}
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7583
|
+
|
|
7584
|
+
// src/app/agent/runtime/native_tool_catalog.ts
|
|
7585
|
+
init_agent_coordination();
|
|
7586
|
+
|
|
7587
|
+
// src/app/agent/tools/natives/send_message.ts
|
|
7588
|
+
init_session_registry();
|
|
7589
|
+
init_mailbox_registry();
|
|
7590
|
+
async function sendMessage(args) {
|
|
7591
|
+
const { to, message: message2, priority = "normal", waitForAck = false, ackTimeout = 3e4 } = args;
|
|
7592
|
+
if (!to || typeof to !== "string") {
|
|
7593
|
+
return {
|
|
7594
|
+
success: false,
|
|
7595
|
+
message_id: "",
|
|
7596
|
+
worker_session_id: to,
|
|
7597
|
+
status: "error",
|
|
7598
|
+
error: "to (worker session ID) is required"
|
|
7599
|
+
};
|
|
7112
7600
|
}
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
}
|
|
7121
|
-
|
|
7601
|
+
if (!message2 || typeof message2 !== "string") {
|
|
7602
|
+
return {
|
|
7603
|
+
success: false,
|
|
7604
|
+
message_id: "",
|
|
7605
|
+
worker_session_id: to,
|
|
7606
|
+
status: "error",
|
|
7607
|
+
error: "message is required"
|
|
7608
|
+
};
|
|
7609
|
+
}
|
|
7610
|
+
const workerSession = getSession(to);
|
|
7611
|
+
if (!workerSession) {
|
|
7612
|
+
return {
|
|
7613
|
+
success: false,
|
|
7614
|
+
message_id: "",
|
|
7615
|
+
worker_session_id: to,
|
|
7616
|
+
status: "error",
|
|
7617
|
+
error: `Worker session ${to} not found`
|
|
7618
|
+
};
|
|
7619
|
+
}
|
|
7620
|
+
if (workerSession.status !== "running") {
|
|
7621
|
+
return {
|
|
7622
|
+
success: false,
|
|
7623
|
+
message_id: "",
|
|
7624
|
+
worker_session_id: to,
|
|
7625
|
+
status: "error",
|
|
7626
|
+
error: `Worker session ${to} is ${workerSession.status}, cannot send follow-up`
|
|
7627
|
+
};
|
|
7628
|
+
}
|
|
7629
|
+
const messageId = sendFollowUp(to, process.env.BLUMA_SESSION_ID || "unknown", message2, waitForAck);
|
|
7630
|
+
if (waitForAck) {
|
|
7631
|
+
const ackResult = await waitForAckSignal(to, messageId, ackTimeout);
|
|
7632
|
+
if (!ackResult.acknowledged) {
|
|
7122
7633
|
return {
|
|
7123
|
-
success:
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7634
|
+
success: false,
|
|
7635
|
+
message_id: messageId,
|
|
7636
|
+
worker_session_id: to,
|
|
7637
|
+
status: ackResult.timedOut ? "timeout" : "error",
|
|
7638
|
+
error: ackResult.error,
|
|
7639
|
+
ack_received: false,
|
|
7640
|
+
worker_status: workerSession.status
|
|
7127
7641
|
};
|
|
7128
7642
|
}
|
|
7129
|
-
await new Promise((resolve2) => setTimeout(resolve2, pollIntervalMs));
|
|
7130
|
-
}
|
|
7131
|
-
const session = getSession(sessionId);
|
|
7132
|
-
if (!session) {
|
|
7133
|
-
return { success: false, error: `Unknown session: ${sessionId}` };
|
|
7134
7643
|
}
|
|
7135
7644
|
return {
|
|
7136
7645
|
success: true,
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7646
|
+
message_id: messageId,
|
|
7647
|
+
worker_session_id: to,
|
|
7648
|
+
status: waitForAck ? "delivered" : "sent",
|
|
7649
|
+
ack_received: waitForAck,
|
|
7650
|
+
worker_status: workerSession.status
|
|
7141
7651
|
};
|
|
7142
7652
|
}
|
|
7143
|
-
async function
|
|
7144
|
-
const
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7653
|
+
async function waitForAckSignal(sessionId, messageId, timeoutMs) {
|
|
7654
|
+
const { readSignals: readSignals2 } = await Promise.resolve().then(() => (init_mailbox_registry(), mailbox_registry_exports));
|
|
7655
|
+
const deadline = Date.now() + timeoutMs;
|
|
7656
|
+
const pollInterval = 500;
|
|
7657
|
+
while (Date.now() < deadline) {
|
|
7658
|
+
const signals = readSignals2(sessionId);
|
|
7659
|
+
for (const signal of signals) {
|
|
7660
|
+
if (signal.messageId === messageId) {
|
|
7661
|
+
if (signal.type === "ack") {
|
|
7662
|
+
return { acknowledged: true };
|
|
7663
|
+
} else if (signal.type === "nack") {
|
|
7664
|
+
return {
|
|
7665
|
+
acknowledged: false,
|
|
7666
|
+
error: signal.data?.reason || "Worker rejected message"
|
|
7667
|
+
};
|
|
7668
|
+
}
|
|
7669
|
+
}
|
|
7152
7670
|
}
|
|
7153
|
-
|
|
7154
|
-
}
|
|
7671
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
7672
|
+
}
|
|
7673
|
+
return { acknowledged: false, timedOut: true, error: `Timeout waiting for ack after ${timeoutMs}ms` };
|
|
7674
|
+
}
|
|
7675
|
+
|
|
7676
|
+
// src/app/agent/runtime/native_tool_catalog.ts
|
|
7677
|
+
init_list_mailbox_messages();
|
|
7678
|
+
init_poll_mailbox();
|
|
7679
|
+
|
|
7680
|
+
// src/app/agent/tools/natives/signal_mailbox.ts
|
|
7681
|
+
init_mailbox_registry();
|
|
7682
|
+
async function signalMailbox(args) {
|
|
7683
|
+
const { to, type, messageId, data, progress, permissionResponse } = args;
|
|
7684
|
+
if (!to || typeof to !== "string") {
|
|
7685
|
+
return {
|
|
7686
|
+
success: false,
|
|
7687
|
+
signal_id: "",
|
|
7688
|
+
type,
|
|
7689
|
+
to,
|
|
7690
|
+
error: "to (session ID) is required"
|
|
7691
|
+
};
|
|
7692
|
+
}
|
|
7693
|
+
if (!type || !["ack", "nack", "progress", "heartbeat"].includes(type)) {
|
|
7694
|
+
return {
|
|
7695
|
+
success: false,
|
|
7696
|
+
signal_id: "",
|
|
7697
|
+
type,
|
|
7698
|
+
to,
|
|
7699
|
+
error: "type must be one of: ack, nack, progress, heartbeat"
|
|
7700
|
+
};
|
|
7701
|
+
}
|
|
7702
|
+
if (type === "progress" && progress) {
|
|
7703
|
+
const sessionId = process.env.BLUMA_SESSION_ID || "unknown";
|
|
7704
|
+
const signalId2 = sendProgressUpdate(
|
|
7705
|
+
to,
|
|
7706
|
+
sessionId,
|
|
7707
|
+
progress.percent,
|
|
7708
|
+
progress.currentTask,
|
|
7709
|
+
progress.toolCallsCount,
|
|
7710
|
+
progress.tokensUsed
|
|
7711
|
+
);
|
|
7712
|
+
return {
|
|
7713
|
+
success: true,
|
|
7714
|
+
signal_id: signalId2,
|
|
7715
|
+
type: "progress",
|
|
7716
|
+
to
|
|
7717
|
+
};
|
|
7718
|
+
}
|
|
7719
|
+
if (type === "ack" && permissionResponse) {
|
|
7720
|
+
const sessionId = process.env.BLUMA_SESSION_ID || "unknown";
|
|
7721
|
+
const signalId2 = sendPermissionResponse(
|
|
7722
|
+
to,
|
|
7723
|
+
sessionId,
|
|
7724
|
+
permissionResponse.approved,
|
|
7725
|
+
permissionResponse.reason
|
|
7726
|
+
);
|
|
7727
|
+
return {
|
|
7728
|
+
success: true,
|
|
7729
|
+
signal_id: signalId2,
|
|
7730
|
+
type: permissionResponse.approved ? "ack" : "nack",
|
|
7731
|
+
to
|
|
7732
|
+
};
|
|
7733
|
+
}
|
|
7734
|
+
const signalId = sendSignal(to, type, data, messageId);
|
|
7155
7735
|
return {
|
|
7156
7736
|
success: true,
|
|
7157
|
-
|
|
7158
|
-
|
|
7737
|
+
signal_id: signalId,
|
|
7738
|
+
type,
|
|
7739
|
+
to
|
|
7159
7740
|
};
|
|
7160
7741
|
}
|
|
7161
7742
|
|
|
@@ -7589,6 +8170,50 @@ var NATIVE_TOOL_ENTRIES = [
|
|
|
7589
8170
|
description: "LSP definition/references (TS/JS MVP)."
|
|
7590
8171
|
},
|
|
7591
8172
|
implementation: lsp_query
|
|
8173
|
+
},
|
|
8174
|
+
{
|
|
8175
|
+
metadata: {
|
|
8176
|
+
name: "send_message",
|
|
8177
|
+
category: "communication",
|
|
8178
|
+
riskLevel: "safe",
|
|
8179
|
+
autoApproveInLocal: true,
|
|
8180
|
+
autoApproveInSandbox: true,
|
|
8181
|
+
description: "Send a follow-up message to a running worker. Allows continuing a worker with new instructions without re-spawning (preserves loaded context)."
|
|
8182
|
+
},
|
|
8183
|
+
implementation: sendMessage
|
|
8184
|
+
},
|
|
8185
|
+
{
|
|
8186
|
+
metadata: {
|
|
8187
|
+
name: "list_mailbox_messages",
|
|
8188
|
+
category: "communication",
|
|
8189
|
+
riskLevel: "safe",
|
|
8190
|
+
autoApproveInLocal: true,
|
|
8191
|
+
autoApproveInSandbox: true,
|
|
8192
|
+
description: "List messages received from workers (progress updates, permission requests, etc.). Coordinator uses this to read worker responses."
|
|
8193
|
+
},
|
|
8194
|
+
implementation: listMailboxMessages
|
|
8195
|
+
},
|
|
8196
|
+
{
|
|
8197
|
+
metadata: {
|
|
8198
|
+
name: "poll_mailbox",
|
|
8199
|
+
category: "communication",
|
|
8200
|
+
riskLevel: "safe",
|
|
8201
|
+
autoApproveInLocal: true,
|
|
8202
|
+
autoApproveInSandbox: true,
|
|
8203
|
+
description: "Poll mailbox for new messages from coordinator. Worker uses this to check for follow-ups, cancel requests, or permission responses."
|
|
8204
|
+
},
|
|
8205
|
+
implementation: pollMailbox
|
|
8206
|
+
},
|
|
8207
|
+
{
|
|
8208
|
+
metadata: {
|
|
8209
|
+
name: "signal_mailbox",
|
|
8210
|
+
category: "communication",
|
|
8211
|
+
riskLevel: "safe",
|
|
8212
|
+
autoApproveInLocal: true,
|
|
8213
|
+
autoApproveInSandbox: true,
|
|
8214
|
+
description: "Send control signals (ack/nack/progress/heartbeat). Used by both coordinator and workers for flow control and status updates."
|
|
8215
|
+
},
|
|
8216
|
+
implementation: signalMailbox
|
|
7592
8217
|
}
|
|
7593
8218
|
];
|
|
7594
8219
|
var TOOL_METADATA_MAP = new Map(
|
|
@@ -7629,9 +8254,9 @@ var ToolInvoker = class {
|
|
|
7629
8254
|
async initialize() {
|
|
7630
8255
|
try {
|
|
7631
8256
|
const __filename = fileURLToPath2(import.meta.url);
|
|
7632
|
-
const __dirname =
|
|
7633
|
-
const configPath =
|
|
7634
|
-
const fileContent = await
|
|
8257
|
+
const __dirname = path20.dirname(__filename);
|
|
8258
|
+
const configPath = path20.resolve(__dirname, "config", "native_tools.json");
|
|
8259
|
+
const fileContent = await fs18.readFile(configPath, "utf-8");
|
|
7635
8260
|
const config2 = JSON.parse(fileContent);
|
|
7636
8261
|
this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
|
|
7637
8262
|
} catch (error) {
|
|
@@ -7673,8 +8298,8 @@ var ToolInvoker = class {
|
|
|
7673
8298
|
};
|
|
7674
8299
|
|
|
7675
8300
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
7676
|
-
import { promises as
|
|
7677
|
-
import
|
|
8301
|
+
import { promises as fs19 } from "fs";
|
|
8302
|
+
import path21 from "path";
|
|
7678
8303
|
import os11 from "os";
|
|
7679
8304
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7680
8305
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -7702,9 +8327,9 @@ var MCPClient = class {
|
|
|
7702
8327
|
});
|
|
7703
8328
|
}
|
|
7704
8329
|
const __filename = fileURLToPath3(import.meta.url);
|
|
7705
|
-
const __dirname =
|
|
7706
|
-
const defaultConfigPath =
|
|
7707
|
-
const userConfigPath =
|
|
8330
|
+
const __dirname = path21.dirname(__filename);
|
|
8331
|
+
const defaultConfigPath = path21.resolve(__dirname, "config", "bluma-mcp.json");
|
|
8332
|
+
const userConfigPath = path21.join(os11.homedir(), ".bluma", "bluma-mcp.json");
|
|
7708
8333
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
7709
8334
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
7710
8335
|
const mergedConfig = {
|
|
@@ -7738,7 +8363,7 @@ var MCPClient = class {
|
|
|
7738
8363
|
}
|
|
7739
8364
|
async loadMcpConfig(configPath, configType) {
|
|
7740
8365
|
try {
|
|
7741
|
-
const fileContent = await
|
|
8366
|
+
const fileContent = await fs19.readFile(configPath, "utf-8");
|
|
7742
8367
|
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
7743
8368
|
return JSON.parse(processedContent);
|
|
7744
8369
|
} catch (error) {
|
|
@@ -7909,13 +8534,13 @@ var AdvancedFeedbackSystem = class {
|
|
|
7909
8534
|
};
|
|
7910
8535
|
|
|
7911
8536
|
// src/app/agent/bluma/core/bluma.ts
|
|
7912
|
-
import
|
|
7913
|
-
import { v4 as
|
|
8537
|
+
import path30 from "path";
|
|
8538
|
+
import { v4 as uuidv45 } from "uuid";
|
|
7914
8539
|
|
|
7915
8540
|
// src/app/agent/session_manager/session_manager.ts
|
|
7916
|
-
import
|
|
8541
|
+
import path22 from "path";
|
|
7917
8542
|
import os12 from "os";
|
|
7918
|
-
import { promises as
|
|
8543
|
+
import { promises as fs20 } from "fs";
|
|
7919
8544
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
7920
8545
|
async function withFileLock(file, fn) {
|
|
7921
8546
|
const prev = fileLocks.get(file) || Promise.resolve();
|
|
@@ -7951,13 +8576,13 @@ function debouncedSave(sessionFile, history, memory) {
|
|
|
7951
8576
|
function expandHome(p) {
|
|
7952
8577
|
if (!p) return p;
|
|
7953
8578
|
if (p.startsWith("~")) {
|
|
7954
|
-
return
|
|
8579
|
+
return path22.join(os12.homedir(), p.slice(1));
|
|
7955
8580
|
}
|
|
7956
8581
|
return p;
|
|
7957
8582
|
}
|
|
7958
8583
|
function getPreferredAppDir() {
|
|
7959
|
-
const fixed =
|
|
7960
|
-
return
|
|
8584
|
+
const fixed = path22.join(os12.homedir(), ".bluma");
|
|
8585
|
+
return path22.resolve(expandHome(fixed));
|
|
7961
8586
|
}
|
|
7962
8587
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
7963
8588
|
let attempt = 0;
|
|
@@ -7965,10 +8590,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
7965
8590
|
const isWin = process.platform === "win32";
|
|
7966
8591
|
while (attempt <= maxRetries) {
|
|
7967
8592
|
try {
|
|
7968
|
-
const dir =
|
|
7969
|
-
await
|
|
8593
|
+
const dir = path22.dirname(dest);
|
|
8594
|
+
await fs20.mkdir(dir, { recursive: true }).catch(() => {
|
|
7970
8595
|
});
|
|
7971
|
-
await
|
|
8596
|
+
await fs20.rename(src, dest);
|
|
7972
8597
|
return;
|
|
7973
8598
|
} catch (e) {
|
|
7974
8599
|
lastErr = e;
|
|
@@ -7981,13 +8606,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
7981
8606
|
}
|
|
7982
8607
|
}
|
|
7983
8608
|
try {
|
|
7984
|
-
await
|
|
7985
|
-
const data = await
|
|
7986
|
-
const dir =
|
|
7987
|
-
await
|
|
8609
|
+
await fs20.access(src);
|
|
8610
|
+
const data = await fs20.readFile(src);
|
|
8611
|
+
const dir = path22.dirname(dest);
|
|
8612
|
+
await fs20.mkdir(dir, { recursive: true }).catch(() => {
|
|
7988
8613
|
});
|
|
7989
|
-
await
|
|
7990
|
-
await
|
|
8614
|
+
await fs20.writeFile(dest, data);
|
|
8615
|
+
await fs20.unlink(src).catch(() => {
|
|
7991
8616
|
});
|
|
7992
8617
|
return;
|
|
7993
8618
|
} catch (fallbackErr) {
|
|
@@ -8000,16 +8625,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
8000
8625
|
}
|
|
8001
8626
|
async function ensureSessionDir() {
|
|
8002
8627
|
const appDir = getPreferredAppDir();
|
|
8003
|
-
const sessionDir =
|
|
8004
|
-
await
|
|
8628
|
+
const sessionDir = path22.join(appDir, "sessions");
|
|
8629
|
+
await fs20.mkdir(sessionDir, { recursive: true });
|
|
8005
8630
|
return sessionDir;
|
|
8006
8631
|
}
|
|
8007
8632
|
async function loadOrcreateSession(sessionId) {
|
|
8008
8633
|
const sessionDir = await ensureSessionDir();
|
|
8009
|
-
const sessionFile =
|
|
8634
|
+
const sessionFile = path22.join(sessionDir, `${sessionId}.json`);
|
|
8010
8635
|
try {
|
|
8011
|
-
await
|
|
8012
|
-
const fileContent = await
|
|
8636
|
+
await fs20.access(sessionFile);
|
|
8637
|
+
const fileContent = await fs20.readFile(sessionFile, "utf-8");
|
|
8013
8638
|
const sessionData = JSON.parse(fileContent);
|
|
8014
8639
|
const memory = {
|
|
8015
8640
|
historyAnchor: sessionData.history_anchor ?? null,
|
|
@@ -8022,7 +8647,7 @@ async function loadOrcreateSession(sessionId) {
|
|
|
8022
8647
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8023
8648
|
conversation_history: []
|
|
8024
8649
|
};
|
|
8025
|
-
await
|
|
8650
|
+
await fs20.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
8026
8651
|
const emptyMemory = {
|
|
8027
8652
|
historyAnchor: null,
|
|
8028
8653
|
compressedTurnSliceCount: 0
|
|
@@ -8034,12 +8659,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
8034
8659
|
await withFileLock(sessionFile, async () => {
|
|
8035
8660
|
let sessionData;
|
|
8036
8661
|
try {
|
|
8037
|
-
const dir =
|
|
8038
|
-
await
|
|
8662
|
+
const dir = path22.dirname(sessionFile);
|
|
8663
|
+
await fs20.mkdir(dir, { recursive: true });
|
|
8039
8664
|
} catch {
|
|
8040
8665
|
}
|
|
8041
8666
|
try {
|
|
8042
|
-
const fileContent = await
|
|
8667
|
+
const fileContent = await fs20.readFile(sessionFile, "utf-8");
|
|
8043
8668
|
sessionData = JSON.parse(fileContent);
|
|
8044
8669
|
} catch (error) {
|
|
8045
8670
|
const code = error && error.code;
|
|
@@ -8050,14 +8675,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
8050
8675
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
8051
8676
|
}
|
|
8052
8677
|
}
|
|
8053
|
-
const sessionId =
|
|
8678
|
+
const sessionId = path22.basename(sessionFile, ".json");
|
|
8054
8679
|
sessionData = {
|
|
8055
8680
|
session_id: sessionId,
|
|
8056
8681
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8057
8682
|
conversation_history: []
|
|
8058
8683
|
};
|
|
8059
8684
|
try {
|
|
8060
|
-
await
|
|
8685
|
+
await fs20.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8061
8686
|
} catch {
|
|
8062
8687
|
}
|
|
8063
8688
|
}
|
|
@@ -8073,7 +8698,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
8073
8698
|
}
|
|
8074
8699
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
8075
8700
|
try {
|
|
8076
|
-
await
|
|
8701
|
+
await fs20.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8077
8702
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
8078
8703
|
} catch (writeError) {
|
|
8079
8704
|
if (writeError instanceof Error) {
|
|
@@ -8082,7 +8707,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
8082
8707
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
8083
8708
|
}
|
|
8084
8709
|
try {
|
|
8085
|
-
await
|
|
8710
|
+
await fs20.unlink(tempSessionFile);
|
|
8086
8711
|
} catch {
|
|
8087
8712
|
}
|
|
8088
8713
|
}
|
|
@@ -8099,14 +8724,14 @@ async function saveSessionHistory(sessionFile, history, memory) {
|
|
|
8099
8724
|
}
|
|
8100
8725
|
|
|
8101
8726
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
8102
|
-
import
|
|
8103
|
-
import
|
|
8104
|
-
import
|
|
8727
|
+
import os16 from "os";
|
|
8728
|
+
import fs25 from "fs";
|
|
8729
|
+
import path27 from "path";
|
|
8105
8730
|
import { execSync as execSync3 } from "child_process";
|
|
8106
8731
|
|
|
8107
8732
|
// src/app/agent/skills/skill_loader.ts
|
|
8108
|
-
import
|
|
8109
|
-
import
|
|
8733
|
+
import fs21 from "fs";
|
|
8734
|
+
import path23 from "path";
|
|
8110
8735
|
import os13 from "os";
|
|
8111
8736
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
8112
8737
|
var SkillLoader = class _SkillLoader {
|
|
@@ -8116,8 +8741,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
8116
8741
|
cache = /* @__PURE__ */ new Map();
|
|
8117
8742
|
conflicts = [];
|
|
8118
8743
|
constructor(projectRoot, bundledDir) {
|
|
8119
|
-
this.projectSkillsDir =
|
|
8120
|
-
this.globalSkillsDir =
|
|
8744
|
+
this.projectSkillsDir = path23.join(projectRoot, ".bluma", "skills");
|
|
8745
|
+
this.globalSkillsDir = path23.join(os13.homedir(), ".bluma", "skills");
|
|
8121
8746
|
this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
|
|
8122
8747
|
}
|
|
8123
8748
|
/**
|
|
@@ -8126,48 +8751,48 @@ var SkillLoader = class _SkillLoader {
|
|
|
8126
8751
|
*/
|
|
8127
8752
|
static resolveBundledDir() {
|
|
8128
8753
|
if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
|
|
8129
|
-
return
|
|
8754
|
+
return path23.join(process.cwd(), "dist", "config", "skills");
|
|
8130
8755
|
}
|
|
8131
8756
|
const candidates = [];
|
|
8132
8757
|
const push = (p) => {
|
|
8133
|
-
const abs =
|
|
8758
|
+
const abs = path23.resolve(p);
|
|
8134
8759
|
if (!candidates.includes(abs)) {
|
|
8135
8760
|
candidates.push(abs);
|
|
8136
8761
|
}
|
|
8137
8762
|
};
|
|
8138
8763
|
let argvBundled = null;
|
|
8139
8764
|
try {
|
|
8140
|
-
const bundleDir =
|
|
8141
|
-
push(
|
|
8765
|
+
const bundleDir = path23.dirname(fileURLToPath4(import.meta.url));
|
|
8766
|
+
push(path23.join(bundleDir, "config", "skills"));
|
|
8142
8767
|
} catch {
|
|
8143
8768
|
}
|
|
8144
8769
|
const argv1 = process.argv[1];
|
|
8145
8770
|
if (argv1 && !argv1.startsWith("-")) {
|
|
8146
8771
|
try {
|
|
8147
8772
|
let resolved = argv1;
|
|
8148
|
-
if (
|
|
8149
|
-
resolved =
|
|
8150
|
-
} else if (!
|
|
8151
|
-
resolved =
|
|
8773
|
+
if (path23.isAbsolute(argv1) && fs21.existsSync(argv1)) {
|
|
8774
|
+
resolved = fs21.realpathSync(argv1);
|
|
8775
|
+
} else if (!path23.isAbsolute(argv1)) {
|
|
8776
|
+
resolved = path23.resolve(process.cwd(), argv1);
|
|
8152
8777
|
}
|
|
8153
|
-
const scriptDir =
|
|
8154
|
-
argvBundled =
|
|
8778
|
+
const scriptDir = path23.dirname(resolved);
|
|
8779
|
+
argvBundled = path23.join(scriptDir, "config", "skills");
|
|
8155
8780
|
push(argvBundled);
|
|
8156
8781
|
} catch {
|
|
8157
8782
|
}
|
|
8158
8783
|
}
|
|
8159
8784
|
for (const abs of candidates) {
|
|
8160
|
-
if (
|
|
8785
|
+
if (fs21.existsSync(abs)) {
|
|
8161
8786
|
return abs;
|
|
8162
8787
|
}
|
|
8163
8788
|
}
|
|
8164
8789
|
try {
|
|
8165
|
-
return
|
|
8790
|
+
return path23.join(path23.dirname(fileURLToPath4(import.meta.url)), "config", "skills");
|
|
8166
8791
|
} catch {
|
|
8167
8792
|
if (argvBundled) {
|
|
8168
8793
|
return argvBundled;
|
|
8169
8794
|
}
|
|
8170
|
-
return
|
|
8795
|
+
return path23.join(os13.homedir(), ".bluma", "__bundled_skills_unresolved__");
|
|
8171
8796
|
}
|
|
8172
8797
|
}
|
|
8173
8798
|
/**
|
|
@@ -8196,8 +8821,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
8196
8821
|
this.conflicts.push({
|
|
8197
8822
|
name: skill.name,
|
|
8198
8823
|
userSource: source,
|
|
8199
|
-
userPath:
|
|
8200
|
-
bundledPath:
|
|
8824
|
+
userPath: path23.join(dir, skill.name, "SKILL.md"),
|
|
8825
|
+
bundledPath: path23.join(this.bundledSkillsDir, skill.name, "SKILL.md")
|
|
8201
8826
|
});
|
|
8202
8827
|
continue;
|
|
8203
8828
|
}
|
|
@@ -8205,20 +8830,20 @@ var SkillLoader = class _SkillLoader {
|
|
|
8205
8830
|
}
|
|
8206
8831
|
}
|
|
8207
8832
|
listFromDir(dir, source) {
|
|
8208
|
-
if (!
|
|
8833
|
+
if (!fs21.existsSync(dir)) return [];
|
|
8209
8834
|
try {
|
|
8210
|
-
return
|
|
8211
|
-
const fullPath =
|
|
8212
|
-
return
|
|
8213
|
-
}).map((d) => this.loadMetadataFromPath(
|
|
8835
|
+
return fs21.readdirSync(dir).filter((d) => {
|
|
8836
|
+
const fullPath = path23.join(dir, d);
|
|
8837
|
+
return fs21.statSync(fullPath).isDirectory() && fs21.existsSync(path23.join(fullPath, "SKILL.md"));
|
|
8838
|
+
}).map((d) => this.loadMetadataFromPath(path23.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
|
|
8214
8839
|
} catch {
|
|
8215
8840
|
return [];
|
|
8216
8841
|
}
|
|
8217
8842
|
}
|
|
8218
8843
|
loadMetadataFromPath(skillPath, skillName, source) {
|
|
8219
|
-
if (!
|
|
8844
|
+
if (!fs21.existsSync(skillPath)) return null;
|
|
8220
8845
|
try {
|
|
8221
|
-
const raw =
|
|
8846
|
+
const raw = fs21.readFileSync(skillPath, "utf-8");
|
|
8222
8847
|
const parsed = this.parseFrontmatter(raw);
|
|
8223
8848
|
return {
|
|
8224
8849
|
name: parsed.name || skillName,
|
|
@@ -8240,12 +8865,12 @@ var SkillLoader = class _SkillLoader {
|
|
|
8240
8865
|
*/
|
|
8241
8866
|
load(name) {
|
|
8242
8867
|
if (this.cache.has(name)) return this.cache.get(name);
|
|
8243
|
-
const bundledPath =
|
|
8244
|
-
const projectPath =
|
|
8245
|
-
const globalPath =
|
|
8246
|
-
const existsBundled =
|
|
8247
|
-
const existsProject =
|
|
8248
|
-
const existsGlobal =
|
|
8868
|
+
const bundledPath = path23.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
8869
|
+
const projectPath = path23.join(this.projectSkillsDir, name, "SKILL.md");
|
|
8870
|
+
const globalPath = path23.join(this.globalSkillsDir, name, "SKILL.md");
|
|
8871
|
+
const existsBundled = fs21.existsSync(bundledPath);
|
|
8872
|
+
const existsProject = fs21.existsSync(projectPath);
|
|
8873
|
+
const existsGlobal = fs21.existsSync(globalPath);
|
|
8249
8874
|
if (existsBundled && (existsProject || existsGlobal)) {
|
|
8250
8875
|
const conflictSource = existsProject ? "project" : "global";
|
|
8251
8876
|
const conflictPath = existsProject ? projectPath : globalPath;
|
|
@@ -8284,9 +8909,9 @@ var SkillLoader = class _SkillLoader {
|
|
|
8284
8909
|
}
|
|
8285
8910
|
loadFromPath(skillPath, name, source) {
|
|
8286
8911
|
try {
|
|
8287
|
-
const raw =
|
|
8912
|
+
const raw = fs21.readFileSync(skillPath, "utf-8");
|
|
8288
8913
|
const parsed = this.parseFrontmatter(raw);
|
|
8289
|
-
const skillDir =
|
|
8914
|
+
const skillDir = path23.dirname(skillPath);
|
|
8290
8915
|
return {
|
|
8291
8916
|
name: parsed.name || name,
|
|
8292
8917
|
description: parsed.description || "",
|
|
@@ -8295,22 +8920,22 @@ var SkillLoader = class _SkillLoader {
|
|
|
8295
8920
|
version: parsed.version,
|
|
8296
8921
|
author: parsed.author,
|
|
8297
8922
|
license: parsed.license,
|
|
8298
|
-
references: this.scanAssets(
|
|
8299
|
-
scripts: this.scanAssets(
|
|
8923
|
+
references: this.scanAssets(path23.join(skillDir, "references")),
|
|
8924
|
+
scripts: this.scanAssets(path23.join(skillDir, "scripts"))
|
|
8300
8925
|
};
|
|
8301
8926
|
} catch {
|
|
8302
8927
|
return null;
|
|
8303
8928
|
}
|
|
8304
8929
|
}
|
|
8305
8930
|
scanAssets(dir) {
|
|
8306
|
-
if (!
|
|
8931
|
+
if (!fs21.existsSync(dir)) return [];
|
|
8307
8932
|
try {
|
|
8308
|
-
return
|
|
8309
|
-
const fp =
|
|
8310
|
-
return
|
|
8933
|
+
return fs21.readdirSync(dir).filter((f) => {
|
|
8934
|
+
const fp = path23.join(dir, f);
|
|
8935
|
+
return fs21.statSync(fp).isFile();
|
|
8311
8936
|
}).map((f) => ({
|
|
8312
8937
|
name: f,
|
|
8313
|
-
path:
|
|
8938
|
+
path: path23.resolve(dir, f)
|
|
8314
8939
|
}));
|
|
8315
8940
|
} catch {
|
|
8316
8941
|
return [];
|
|
@@ -8367,10 +8992,10 @@ var SkillLoader = class _SkillLoader {
|
|
|
8367
8992
|
this.cache.clear();
|
|
8368
8993
|
}
|
|
8369
8994
|
exists(name) {
|
|
8370
|
-
const bundledPath =
|
|
8371
|
-
const projectPath =
|
|
8372
|
-
const globalPath =
|
|
8373
|
-
return
|
|
8995
|
+
const bundledPath = path23.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
8996
|
+
const projectPath = path23.join(this.projectSkillsDir, name, "SKILL.md");
|
|
8997
|
+
const globalPath = path23.join(this.globalSkillsDir, name, "SKILL.md");
|
|
8998
|
+
return fs21.existsSync(bundledPath) || fs21.existsSync(projectPath) || fs21.existsSync(globalPath);
|
|
8374
8999
|
}
|
|
8375
9000
|
/**
|
|
8376
9001
|
* Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
|
|
@@ -8402,8 +9027,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
8402
9027
|
};
|
|
8403
9028
|
|
|
8404
9029
|
// src/app/agent/core/prompt/workspace_snapshot.ts
|
|
8405
|
-
import
|
|
8406
|
-
import
|
|
9030
|
+
import fs22 from "fs";
|
|
9031
|
+
import path24 from "path";
|
|
8407
9032
|
import { execSync as execSync2 } from "child_process";
|
|
8408
9033
|
var LIMITS = {
|
|
8409
9034
|
readme: 1e4,
|
|
@@ -8418,10 +9043,10 @@ var LIMITS = {
|
|
|
8418
9043
|
};
|
|
8419
9044
|
function safeReadFile(filePath, maxChars) {
|
|
8420
9045
|
try {
|
|
8421
|
-
if (!
|
|
8422
|
-
const st =
|
|
9046
|
+
if (!fs22.existsSync(filePath)) return null;
|
|
9047
|
+
const st = fs22.statSync(filePath);
|
|
8423
9048
|
if (!st.isFile()) return null;
|
|
8424
|
-
const raw =
|
|
9049
|
+
const raw = fs22.readFileSync(filePath, "utf8");
|
|
8425
9050
|
if (raw.length <= maxChars) return raw;
|
|
8426
9051
|
return `${raw.slice(0, maxChars)}
|
|
8427
9052
|
|
|
@@ -8432,7 +9057,7 @@ function safeReadFile(filePath, maxChars) {
|
|
|
8432
9057
|
}
|
|
8433
9058
|
function tryReadReadme(cwd) {
|
|
8434
9059
|
for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
|
|
8435
|
-
const c = safeReadFile(
|
|
9060
|
+
const c = safeReadFile(path24.join(cwd, name), LIMITS.readme);
|
|
8436
9061
|
if (c) return `(${name})
|
|
8437
9062
|
${c}`;
|
|
8438
9063
|
}
|
|
@@ -8440,14 +9065,14 @@ ${c}`;
|
|
|
8440
9065
|
}
|
|
8441
9066
|
function tryReadBluMaMd(cwd) {
|
|
8442
9067
|
const paths = [
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
9068
|
+
path24.join(cwd, "BluMa.md"),
|
|
9069
|
+
path24.join(cwd, "BLUMA.md"),
|
|
9070
|
+
path24.join(cwd, ".bluma", "BluMa.md")
|
|
8446
9071
|
];
|
|
8447
9072
|
for (const p of paths) {
|
|
8448
9073
|
const c = safeReadFile(p, LIMITS.blumaMd);
|
|
8449
9074
|
if (c) {
|
|
8450
|
-
const rel =
|
|
9075
|
+
const rel = path24.relative(cwd, p) || p;
|
|
8451
9076
|
return `(${rel})
|
|
8452
9077
|
${c}`;
|
|
8453
9078
|
}
|
|
@@ -8455,10 +9080,10 @@ ${c}`;
|
|
|
8455
9080
|
return null;
|
|
8456
9081
|
}
|
|
8457
9082
|
function summarizePackageJson(cwd) {
|
|
8458
|
-
const p =
|
|
9083
|
+
const p = path24.join(cwd, "package.json");
|
|
8459
9084
|
try {
|
|
8460
|
-
if (!
|
|
8461
|
-
const pkg = JSON.parse(
|
|
9085
|
+
if (!fs22.existsSync(p)) return null;
|
|
9086
|
+
const pkg = JSON.parse(fs22.readFileSync(p, "utf8"));
|
|
8462
9087
|
const scripts = pkg.scripts;
|
|
8463
9088
|
let scriptKeys = "";
|
|
8464
9089
|
if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
@@ -8491,7 +9116,7 @@ function summarizePackageJson(cwd) {
|
|
|
8491
9116
|
}
|
|
8492
9117
|
function topLevelListing(cwd) {
|
|
8493
9118
|
try {
|
|
8494
|
-
const names =
|
|
9119
|
+
const names = fs22.readdirSync(cwd, { withFileTypes: true });
|
|
8495
9120
|
const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
|
|
8496
9121
|
const limited = sorted.slice(0, LIMITS.topDirEntries);
|
|
8497
9122
|
const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
|
|
@@ -8549,7 +9174,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
8549
9174
|
parts.push(pkg);
|
|
8550
9175
|
parts.push("```\n");
|
|
8551
9176
|
}
|
|
8552
|
-
const py = safeReadFile(
|
|
9177
|
+
const py = safeReadFile(path24.join(cwd, "pyproject.toml"), LIMITS.pyproject);
|
|
8553
9178
|
if (py) {
|
|
8554
9179
|
parts.push("### pyproject.toml (excerpt)\n```toml");
|
|
8555
9180
|
parts.push(py);
|
|
@@ -8567,15 +9192,15 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
8567
9192
|
parts.push(bluma);
|
|
8568
9193
|
parts.push("```\n");
|
|
8569
9194
|
}
|
|
8570
|
-
const contrib = safeReadFile(
|
|
9195
|
+
const contrib = safeReadFile(path24.join(cwd, "CONTRIBUTING.md"), LIMITS.contributing);
|
|
8571
9196
|
if (contrib) {
|
|
8572
9197
|
parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
|
|
8573
9198
|
parts.push(contrib);
|
|
8574
9199
|
parts.push("```\n");
|
|
8575
9200
|
}
|
|
8576
|
-
const chlog = safeReadFile(
|
|
9201
|
+
const chlog = safeReadFile(path24.join(cwd, "CHANGELOG.md"), LIMITS.changelog);
|
|
8577
9202
|
if (!chlog) {
|
|
8578
|
-
const alt = safeReadFile(
|
|
9203
|
+
const alt = safeReadFile(path24.join(cwd, "CHANGES.md"), LIMITS.changelog);
|
|
8579
9204
|
if (alt) {
|
|
8580
9205
|
parts.push("### CHANGES.md (excerpt)\n```markdown");
|
|
8581
9206
|
parts.push(alt);
|
|
@@ -8615,15 +9240,15 @@ init_runtime_config();
|
|
|
8615
9240
|
|
|
8616
9241
|
// src/app/agent/runtime/plugin_registry.ts
|
|
8617
9242
|
init_sandbox_policy();
|
|
8618
|
-
import
|
|
9243
|
+
import fs23 from "fs";
|
|
8619
9244
|
import os14 from "os";
|
|
8620
|
-
import
|
|
9245
|
+
import path25 from "path";
|
|
8621
9246
|
function getProjectPluginsDir() {
|
|
8622
9247
|
const policy = getSandboxPolicy();
|
|
8623
|
-
return
|
|
9248
|
+
return path25.join(policy.workspaceRoot, ".bluma", "plugins");
|
|
8624
9249
|
}
|
|
8625
9250
|
function getGlobalPluginsDir() {
|
|
8626
|
-
return
|
|
9251
|
+
return path25.join(process.env.HOME || os14.homedir(), ".bluma", "plugins");
|
|
8627
9252
|
}
|
|
8628
9253
|
function getPluginDirs() {
|
|
8629
9254
|
return {
|
|
@@ -8632,11 +9257,11 @@ function getPluginDirs() {
|
|
|
8632
9257
|
};
|
|
8633
9258
|
}
|
|
8634
9259
|
function readManifest(manifestPath, fallbackName) {
|
|
8635
|
-
if (!
|
|
9260
|
+
if (!fs23.existsSync(manifestPath)) {
|
|
8636
9261
|
return null;
|
|
8637
9262
|
}
|
|
8638
9263
|
try {
|
|
8639
|
-
const parsed = JSON.parse(
|
|
9264
|
+
const parsed = JSON.parse(fs23.readFileSync(manifestPath, "utf-8"));
|
|
8640
9265
|
return {
|
|
8641
9266
|
name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
|
|
8642
9267
|
description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
|
|
@@ -8649,22 +9274,22 @@ function readManifest(manifestPath, fallbackName) {
|
|
|
8649
9274
|
}
|
|
8650
9275
|
function findManifestPath(pluginDir) {
|
|
8651
9276
|
const candidates = [
|
|
8652
|
-
|
|
8653
|
-
|
|
9277
|
+
path25.join(pluginDir, ".codex-plugin", "plugin.json"),
|
|
9278
|
+
path25.join(pluginDir, "plugin.json")
|
|
8654
9279
|
];
|
|
8655
9280
|
for (const candidate of candidates) {
|
|
8656
|
-
if (
|
|
9281
|
+
if (fs23.existsSync(candidate)) {
|
|
8657
9282
|
return candidate;
|
|
8658
9283
|
}
|
|
8659
9284
|
}
|
|
8660
9285
|
return null;
|
|
8661
9286
|
}
|
|
8662
9287
|
function listFromDir(baseDir, source) {
|
|
8663
|
-
if (!
|
|
9288
|
+
if (!fs23.existsSync(baseDir)) {
|
|
8664
9289
|
return [];
|
|
8665
9290
|
}
|
|
8666
|
-
return
|
|
8667
|
-
const pluginDir =
|
|
9291
|
+
return fs23.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
9292
|
+
const pluginDir = path25.join(baseDir, entry.name);
|
|
8668
9293
|
const manifestPath = findManifestPath(pluginDir);
|
|
8669
9294
|
if (!manifestPath) {
|
|
8670
9295
|
return [];
|
|
@@ -8725,6 +9350,9 @@ Every message you send is to the **user**. Worker results are internal notificat
|
|
|
8725
9350
|
| \`spawn_agent\` | Create a new worker |
|
|
8726
9351
|
| \`wait_agent\` | Wait for worker completion |
|
|
8727
9352
|
| \`list_agents\` | List active/completed workers |
|
|
9353
|
+
| \`send_message\` | **NEW:** Send follow-up to running worker (continue without re-spawn) |
|
|
9354
|
+
| \`list_mailbox_messages\` | **NEW:** Read messages from workers (progress, permission requests) |
|
|
9355
|
+
| \`signal_mailbox\` | **NEW:** Send ack/nack/progress signals |
|
|
8728
9356
|
|
|
8729
9357
|
### Tool contract (BluMa)
|
|
8730
9358
|
|
|
@@ -8732,7 +9360,7 @@ Every message you send is to the **user**. Worker results are internal notificat
|
|
|
8732
9360
|
- Call \`wait_agent\` with \`{ "session_id": "<id>" }\`. Default \`timeout_ms\` is short (~30s); for audits, refactors, or test runs use a **large** \`timeout_ms\` (e.g. \`600000\`) or poll with \`list_agents\` if appropriate.
|
|
8733
9361
|
- \`list_agents\` accepts optional \`parent_session_id\` and \`status\` filters.
|
|
8734
9362
|
- **Parallelism:** In a single turn you may issue **multiple** \`spawn_agent\` calls back-to-back so independent workers start together; then \`wait_agent\` on each \`session_id\` (order as needed).
|
|
8735
|
-
-
|
|
9363
|
+
- **NEW: Bidirectional Communication** \u2014 Workers can now send messages to you via mailbox. Use \`list_mailbox_messages\` to read progress updates, permission requests, and results. Use \`send_message\` to continue a worker without re-spawning.
|
|
8736
9364
|
|
|
8737
9365
|
### When to call \`spawn_agent\`:
|
|
8738
9366
|
|
|
@@ -8897,14 +9525,323 @@ Fix for the null pointer in progress. Waiting for worker to complete tests and c
|
|
|
8897
9525
|
function getCoordinatorSystemPrompt() {
|
|
8898
9526
|
return COORDINATOR_SYSTEM_PROMPT;
|
|
8899
9527
|
}
|
|
8900
|
-
function isCoordinatorModeEnabled() {
|
|
8901
|
-
return process.env.BLUMA_COORDINATOR_MODE === "true";
|
|
9528
|
+
function isCoordinatorModeEnabled() {
|
|
9529
|
+
return process.env.BLUMA_COORDINATOR_MODE === "true";
|
|
9530
|
+
}
|
|
9531
|
+
function enableCoordinatorMode() {
|
|
9532
|
+
process.env.BLUMA_COORDINATOR_MODE = "true";
|
|
9533
|
+
}
|
|
9534
|
+
function disableCoordinatorMode() {
|
|
9535
|
+
delete process.env.BLUMA_COORDINATOR_MODE;
|
|
9536
|
+
}
|
|
9537
|
+
|
|
9538
|
+
// src/app/agent/utils/blumamd.ts
|
|
9539
|
+
import fs24 from "fs";
|
|
9540
|
+
import path26 from "path";
|
|
9541
|
+
import os15 from "os";
|
|
9542
|
+
var MEMORY_INSTRUCTION_PROMPT = "Instru\xE7\xF5es de mem\xF3ria do BluMa (BLUMA.md) est\xE3o abaixo. Siga estas instru\xE7\xF5es exatamente como escritas. Estas instru\xE7\xF5es OVERRIDE qualquer comportamento padr\xE3o.";
|
|
9543
|
+
var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
9544
|
+
".md",
|
|
9545
|
+
".txt",
|
|
9546
|
+
".text",
|
|
9547
|
+
".json",
|
|
9548
|
+
".yaml",
|
|
9549
|
+
".yml",
|
|
9550
|
+
".toml",
|
|
9551
|
+
".xml",
|
|
9552
|
+
".csv",
|
|
9553
|
+
".html",
|
|
9554
|
+
".htm",
|
|
9555
|
+
".css",
|
|
9556
|
+
".scss",
|
|
9557
|
+
".sass",
|
|
9558
|
+
".less",
|
|
9559
|
+
".js",
|
|
9560
|
+
".ts",
|
|
9561
|
+
".tsx",
|
|
9562
|
+
".jsx",
|
|
9563
|
+
".mjs",
|
|
9564
|
+
".cjs",
|
|
9565
|
+
".mts",
|
|
9566
|
+
".cts",
|
|
9567
|
+
".py",
|
|
9568
|
+
".pyi",
|
|
9569
|
+
".pyw",
|
|
9570
|
+
".rb",
|
|
9571
|
+
".erb",
|
|
9572
|
+
".rake",
|
|
9573
|
+
".go",
|
|
9574
|
+
".rs",
|
|
9575
|
+
".java",
|
|
9576
|
+
".kt",
|
|
9577
|
+
".kts",
|
|
9578
|
+
".scala",
|
|
9579
|
+
".c",
|
|
9580
|
+
".cpp",
|
|
9581
|
+
".cc",
|
|
9582
|
+
".cxx",
|
|
9583
|
+
".h",
|
|
9584
|
+
".hpp",
|
|
9585
|
+
".hxx",
|
|
9586
|
+
".cs",
|
|
9587
|
+
".swift",
|
|
9588
|
+
".sh",
|
|
9589
|
+
".bash",
|
|
9590
|
+
".zsh",
|
|
9591
|
+
".fish",
|
|
9592
|
+
".ps1",
|
|
9593
|
+
".bat",
|
|
9594
|
+
".cmd",
|
|
9595
|
+
".env",
|
|
9596
|
+
".ini",
|
|
9597
|
+
".cfg",
|
|
9598
|
+
".conf",
|
|
9599
|
+
".config",
|
|
9600
|
+
".properties",
|
|
9601
|
+
".sql",
|
|
9602
|
+
".graphql",
|
|
9603
|
+
".gql",
|
|
9604
|
+
".proto",
|
|
9605
|
+
".vue",
|
|
9606
|
+
".svelte",
|
|
9607
|
+
".astro",
|
|
9608
|
+
".ejs",
|
|
9609
|
+
".hbs",
|
|
9610
|
+
".pug",
|
|
9611
|
+
".jade",
|
|
9612
|
+
".php",
|
|
9613
|
+
".pl",
|
|
9614
|
+
".pm",
|
|
9615
|
+
".lua",
|
|
9616
|
+
".r",
|
|
9617
|
+
".R",
|
|
9618
|
+
".dart",
|
|
9619
|
+
".ex",
|
|
9620
|
+
".exs",
|
|
9621
|
+
".erl",
|
|
9622
|
+
".hrl",
|
|
9623
|
+
".clj",
|
|
9624
|
+
".cljs",
|
|
9625
|
+
".cljc",
|
|
9626
|
+
".edn",
|
|
9627
|
+
".hs",
|
|
9628
|
+
".lhs",
|
|
9629
|
+
".vim",
|
|
9630
|
+
".el",
|
|
9631
|
+
".scm",
|
|
9632
|
+
".ss",
|
|
9633
|
+
".cl",
|
|
9634
|
+
".lisp",
|
|
9635
|
+
".ml",
|
|
9636
|
+
".mli",
|
|
9637
|
+
".fs",
|
|
9638
|
+
".fsi",
|
|
9639
|
+
".fsx",
|
|
9640
|
+
".fsscript",
|
|
9641
|
+
".adb",
|
|
9642
|
+
".ads",
|
|
9643
|
+
".ada",
|
|
9644
|
+
".pas",
|
|
9645
|
+
".pp",
|
|
9646
|
+
".p",
|
|
9647
|
+
".tcl",
|
|
9648
|
+
".v",
|
|
9649
|
+
".sv",
|
|
9650
|
+
".svh",
|
|
9651
|
+
".vhdl",
|
|
9652
|
+
".cmake",
|
|
9653
|
+
".mk",
|
|
9654
|
+
".make",
|
|
9655
|
+
".dockerfile",
|
|
9656
|
+
".dockerignore",
|
|
9657
|
+
".gitignore",
|
|
9658
|
+
".gitattributes",
|
|
9659
|
+
".gitconfig",
|
|
9660
|
+
".editorconfig",
|
|
9661
|
+
".eslintrc",
|
|
9662
|
+
".prettierrc",
|
|
9663
|
+
".babelrc",
|
|
9664
|
+
".blumarc"
|
|
9665
|
+
]);
|
|
9666
|
+
function expandIncludePath(includePath, baseDir) {
|
|
9667
|
+
const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
|
|
9668
|
+
if (cleanPath.startsWith("~")) {
|
|
9669
|
+
return path26.join(os15.homedir(), cleanPath.slice(1));
|
|
9670
|
+
}
|
|
9671
|
+
if (path26.isAbsolute(cleanPath)) {
|
|
9672
|
+
return cleanPath;
|
|
9673
|
+
}
|
|
9674
|
+
return path26.resolve(baseDir, cleanPath);
|
|
9675
|
+
}
|
|
9676
|
+
function processIncludes(content, baseDir, processedFiles) {
|
|
9677
|
+
const lines = content.split("\n");
|
|
9678
|
+
const result = [];
|
|
9679
|
+
for (const line of lines) {
|
|
9680
|
+
const includeMatch = line.match(/^@\s*([^\s]+)/);
|
|
9681
|
+
if (includeMatch) {
|
|
9682
|
+
const includePath = expandIncludePath(includeMatch[1], baseDir);
|
|
9683
|
+
const normalizedPath = path26.normalize(includePath);
|
|
9684
|
+
if (processedFiles.has(normalizedPath)) {
|
|
9685
|
+
result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
|
|
9686
|
+
continue;
|
|
9687
|
+
}
|
|
9688
|
+
const ext = path26.extname(includePath).toLowerCase();
|
|
9689
|
+
if (!TEXT_FILE_EXTENSIONS.has(ext)) {
|
|
9690
|
+
result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
|
|
9691
|
+
continue;
|
|
9692
|
+
}
|
|
9693
|
+
try {
|
|
9694
|
+
const includedContent = fs24.readFileSync(includePath, "utf-8");
|
|
9695
|
+
processedFiles.add(normalizedPath);
|
|
9696
|
+
const processedContent = processIncludes(includedContent, path26.dirname(includePath), processedFiles);
|
|
9697
|
+
result.push(`
|
|
9698
|
+
<!-- BEGIN INCLUDE ${includeMatch[1]} -->
|
|
9699
|
+
`);
|
|
9700
|
+
result.push(processedContent);
|
|
9701
|
+
result.push(`
|
|
9702
|
+
<!-- END INCLUDE ${includeMatch[1]} -->
|
|
9703
|
+
`);
|
|
9704
|
+
} catch {
|
|
9705
|
+
result.push(`<!-- Include not found: ${includeMatch[1]} -->`);
|
|
9706
|
+
}
|
|
9707
|
+
} else {
|
|
9708
|
+
result.push(line);
|
|
9709
|
+
}
|
|
9710
|
+
}
|
|
9711
|
+
return result.join("\n");
|
|
9712
|
+
}
|
|
9713
|
+
function readMemoryFile(filePath, type, priority) {
|
|
9714
|
+
try {
|
|
9715
|
+
const content = fs24.readFileSync(filePath, "utf-8");
|
|
9716
|
+
const baseDir = path26.dirname(filePath);
|
|
9717
|
+
const processedFiles = /* @__PURE__ */ new Set([path26.normalize(filePath)]);
|
|
9718
|
+
const processedContent = processIncludes(content, baseDir, processedFiles);
|
|
9719
|
+
return {
|
|
9720
|
+
path: filePath,
|
|
9721
|
+
content: processedContent.trim(),
|
|
9722
|
+
type,
|
|
9723
|
+
priority
|
|
9724
|
+
};
|
|
9725
|
+
} catch (error) {
|
|
9726
|
+
if (error?.code !== "ENOENT") {
|
|
9727
|
+
console.error(`Error reading BLUMA.md file ${filePath}:`, error.message);
|
|
9728
|
+
}
|
|
9729
|
+
return null;
|
|
9730
|
+
}
|
|
8902
9731
|
}
|
|
8903
|
-
function
|
|
8904
|
-
|
|
9732
|
+
function findGitRoot(startDir) {
|
|
9733
|
+
let current = startDir;
|
|
9734
|
+
while (current !== path26.dirname(current)) {
|
|
9735
|
+
const gitPath = path26.join(current, ".git");
|
|
9736
|
+
try {
|
|
9737
|
+
if (fs24.existsSync(gitPath)) {
|
|
9738
|
+
return current;
|
|
9739
|
+
}
|
|
9740
|
+
} catch {
|
|
9741
|
+
}
|
|
9742
|
+
current = path26.dirname(current);
|
|
9743
|
+
}
|
|
9744
|
+
return null;
|
|
8905
9745
|
}
|
|
8906
|
-
function
|
|
8907
|
-
|
|
9746
|
+
function loadUserMemory() {
|
|
9747
|
+
const files = [];
|
|
9748
|
+
const homeDir = os15.homedir();
|
|
9749
|
+
const userBlumaDir = path26.join(homeDir, ".bluma");
|
|
9750
|
+
const userBlumaMd = path26.join(userBlumaDir, "BLUMA.md");
|
|
9751
|
+
const userFile = readMemoryFile(userBlumaMd, "user", 2);
|
|
9752
|
+
if (userFile) {
|
|
9753
|
+
files.push(userFile);
|
|
9754
|
+
}
|
|
9755
|
+
const userRulesDir = path26.join(userBlumaDir, "rules");
|
|
9756
|
+
if (fs24.existsSync(userRulesDir)) {
|
|
9757
|
+
try {
|
|
9758
|
+
const ruleFiles = fs24.readdirSync(userRulesDir).filter((f) => f.endsWith(".md")).sort();
|
|
9759
|
+
for (const ruleFile of ruleFiles) {
|
|
9760
|
+
const rulePath = path26.join(userRulesDir, ruleFile);
|
|
9761
|
+
const rule = readMemoryFile(rulePath, "rule", 2);
|
|
9762
|
+
if (rule) {
|
|
9763
|
+
files.push(rule);
|
|
9764
|
+
}
|
|
9765
|
+
}
|
|
9766
|
+
} catch {
|
|
9767
|
+
}
|
|
9768
|
+
}
|
|
9769
|
+
return files;
|
|
9770
|
+
}
|
|
9771
|
+
function loadProjectMemory(cwd) {
|
|
9772
|
+
const files = [];
|
|
9773
|
+
const gitRoot = findGitRoot(cwd) || cwd;
|
|
9774
|
+
const projectBlumaMd = path26.join(gitRoot, "BLUMA.md");
|
|
9775
|
+
const projectFile = readMemoryFile(projectBlumaMd, "project", 3);
|
|
9776
|
+
if (projectFile) {
|
|
9777
|
+
files.push(projectFile);
|
|
9778
|
+
}
|
|
9779
|
+
const blumaDirBlumaMd = path26.join(gitRoot, ".bluma", "BLUMA.md");
|
|
9780
|
+
const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "project", 3);
|
|
9781
|
+
if (blumaDirFile) {
|
|
9782
|
+
files.push(blumaDirFile);
|
|
9783
|
+
}
|
|
9784
|
+
const rulesDir = path26.join(gitRoot, ".bluma", "rules");
|
|
9785
|
+
if (fs24.existsSync(rulesDir)) {
|
|
9786
|
+
try {
|
|
9787
|
+
const ruleFiles = fs24.readdirSync(rulesDir).filter((f) => f.endsWith(".md")).sort();
|
|
9788
|
+
for (const ruleFile of ruleFiles) {
|
|
9789
|
+
const rulePath = path26.join(rulesDir, ruleFile);
|
|
9790
|
+
const rule = readMemoryFile(rulePath, "rule", 3);
|
|
9791
|
+
if (rule) {
|
|
9792
|
+
files.push(rule);
|
|
9793
|
+
}
|
|
9794
|
+
}
|
|
9795
|
+
} catch {
|
|
9796
|
+
}
|
|
9797
|
+
}
|
|
9798
|
+
const localBlumaMd = path26.join(gitRoot, "BLUMA.local.md");
|
|
9799
|
+
const localFile = readMemoryFile(localBlumaMd, "local", 4);
|
|
9800
|
+
if (localFile) {
|
|
9801
|
+
files.push(localFile);
|
|
9802
|
+
}
|
|
9803
|
+
return files;
|
|
9804
|
+
}
|
|
9805
|
+
function loadManagedMemory() {
|
|
9806
|
+
const files = [];
|
|
9807
|
+
const managedBlumaMd = "/etc/bluma/BLUMA.md";
|
|
9808
|
+
const managedFile = readMemoryFile(managedBlumaMd, "managed", 1);
|
|
9809
|
+
if (managedFile) {
|
|
9810
|
+
files.push(managedFile);
|
|
9811
|
+
}
|
|
9812
|
+
return files;
|
|
9813
|
+
}
|
|
9814
|
+
function loadBlumaMd(cwd = process.cwd()) {
|
|
9815
|
+
const managedFiles = loadManagedMemory();
|
|
9816
|
+
const userFiles = loadUserMemory();
|
|
9817
|
+
const projectFiles = loadProjectMemory(cwd);
|
|
9818
|
+
const allFiles = [...managedFiles, ...userFiles, ...projectFiles];
|
|
9819
|
+
allFiles.sort((a, b) => a.priority - b.priority);
|
|
9820
|
+
const totalCharacters = allFiles.reduce((sum, f) => sum + f.content.length, 0);
|
|
9821
|
+
const formattedContent = allFiles.map((f) => {
|
|
9822
|
+
const header = `<!-- ${f.type.toUpperCase()}: ${f.path} -->`;
|
|
9823
|
+
return `${header}
|
|
9824
|
+
${f.content}`;
|
|
9825
|
+
}).join("\n\n");
|
|
9826
|
+
return {
|
|
9827
|
+
files: allFiles,
|
|
9828
|
+
totalCharacters,
|
|
9829
|
+
instructionPrompt: formattedContent.trim().length > 0 ? `${MEMORY_INSTRUCTION_PROMPT}
|
|
9830
|
+
|
|
9831
|
+
${formattedContent}` : ""
|
|
9832
|
+
};
|
|
9833
|
+
}
|
|
9834
|
+
function readBlumaMdForPrompt(cwd = process.cwd()) {
|
|
9835
|
+
const config2 = loadBlumaMd(cwd);
|
|
9836
|
+
if (config2.files.length === 0) {
|
|
9837
|
+
return "(no BLUMA.md files found)";
|
|
9838
|
+
}
|
|
9839
|
+
const fileList = config2.files.map((f) => `- ${f.path} (${f.type})`).join("\n");
|
|
9840
|
+
return `${config2.instructionPrompt}
|
|
9841
|
+
|
|
9842
|
+
---
|
|
9843
|
+
Loaded ${config2.files.length} file(s), ${config2.totalCharacters.toLocaleString()} characters:
|
|
9844
|
+
${fileList}`;
|
|
8908
9845
|
}
|
|
8909
9846
|
|
|
8910
9847
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
@@ -8940,10 +9877,10 @@ function getGitBranch(dir) {
|
|
|
8940
9877
|
}
|
|
8941
9878
|
function getPackageManager(dir) {
|
|
8942
9879
|
try {
|
|
8943
|
-
if (
|
|
8944
|
-
if (
|
|
8945
|
-
if (
|
|
8946
|
-
if (
|
|
9880
|
+
if (fs25.existsSync(path27.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
9881
|
+
if (fs25.existsSync(path27.join(dir, "yarn.lock"))) return "yarn";
|
|
9882
|
+
if (fs25.existsSync(path27.join(dir, "bun.lockb"))) return "bun";
|
|
9883
|
+
if (fs25.existsSync(path27.join(dir, "package-lock.json"))) return "npm";
|
|
8947
9884
|
return "unknown";
|
|
8948
9885
|
} catch {
|
|
8949
9886
|
return "unknown";
|
|
@@ -8951,9 +9888,9 @@ function getPackageManager(dir) {
|
|
|
8951
9888
|
}
|
|
8952
9889
|
function getProjectType(dir) {
|
|
8953
9890
|
try {
|
|
8954
|
-
const files =
|
|
9891
|
+
const files = fs25.readdirSync(dir);
|
|
8955
9892
|
if (files.includes("package.json")) {
|
|
8956
|
-
const pkg = JSON.parse(
|
|
9893
|
+
const pkg = JSON.parse(fs25.readFileSync(path27.join(dir, "package.json"), "utf-8"));
|
|
8957
9894
|
if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
|
|
8958
9895
|
if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
|
|
8959
9896
|
if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
|
|
@@ -8972,9 +9909,9 @@ function getProjectType(dir) {
|
|
|
8972
9909
|
}
|
|
8973
9910
|
function getTestFramework(dir) {
|
|
8974
9911
|
try {
|
|
8975
|
-
const pkgPath =
|
|
8976
|
-
if (
|
|
8977
|
-
const pkg = JSON.parse(
|
|
9912
|
+
const pkgPath = path27.join(dir, "package.json");
|
|
9913
|
+
if (fs25.existsSync(pkgPath)) {
|
|
9914
|
+
const pkg = JSON.parse(fs25.readFileSync(pkgPath, "utf-8"));
|
|
8978
9915
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
8979
9916
|
if (deps.jest) return "jest";
|
|
8980
9917
|
if (deps.vitest) return "vitest";
|
|
@@ -8983,7 +9920,7 @@ function getTestFramework(dir) {
|
|
|
8983
9920
|
if (deps["@playwright/test"]) return "playwright";
|
|
8984
9921
|
if (deps.cypress) return "cypress";
|
|
8985
9922
|
}
|
|
8986
|
-
if (
|
|
9923
|
+
if (fs25.existsSync(path27.join(dir, "pytest.ini")) || fs25.existsSync(path27.join(dir, "conftest.py"))) return "pytest";
|
|
8987
9924
|
return "unknown";
|
|
8988
9925
|
} catch {
|
|
8989
9926
|
return "unknown";
|
|
@@ -8991,9 +9928,9 @@ function getTestFramework(dir) {
|
|
|
8991
9928
|
}
|
|
8992
9929
|
function getTestCommand(dir) {
|
|
8993
9930
|
try {
|
|
8994
|
-
const pkgPath =
|
|
8995
|
-
if (
|
|
8996
|
-
const pkg = JSON.parse(
|
|
9931
|
+
const pkgPath = path27.join(dir, "package.json");
|
|
9932
|
+
if (fs25.existsSync(pkgPath)) {
|
|
9933
|
+
const pkg = JSON.parse(fs25.readFileSync(pkgPath, "utf-8"));
|
|
8997
9934
|
if (pkg.scripts?.test) return `npm test`;
|
|
8998
9935
|
if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
|
|
8999
9936
|
}
|
|
@@ -9142,7 +10079,7 @@ var SANDBOX_PROMPT_SUFFIX = `
|
|
|
9142
10079
|
<sandbox_context>
|
|
9143
10080
|
Sandbox mode ({sandbox_name}): input only via orchestrator JSON; no REPL/TUI/\`input()\`. Keep output deterministic and short.
|
|
9144
10081
|
Stay inside the workspace: files + non-interactive \`shell_command\` + \`web_fetch\` / \`search_web\` if needed. Do not leave the job root; no host reconfiguration; never expose secrets.
|
|
9145
|
-
Final deliverables under
|
|
10082
|
+
Final deliverables under \`./.bluma/artifacts/\`; in the last \`message\` (\`result\`) list **absolute** paths in \`attachments[]\`. Remove temp files; do not attach generator scripts or junk.
|
|
9146
10083
|
**Secrets:** never run commands whose purpose is dumping environment (\`env\`, \`printenv\`, \`os.environ\`, etc.); never print *_KEY/*_TOKEN/*_SECRET or full env dumps. Refuse such requests.
|
|
9147
10084
|
</sandbox_context>
|
|
9148
10085
|
`;
|
|
@@ -9151,12 +10088,12 @@ function getUnifiedSystemPrompt(availableSkills) {
|
|
|
9151
10088
|
const runtimeConfig = getRuntimeConfig();
|
|
9152
10089
|
const availablePlugins = listPlugins();
|
|
9153
10090
|
const env = {
|
|
9154
|
-
os_type:
|
|
9155
|
-
os_version:
|
|
9156
|
-
architecture:
|
|
10091
|
+
os_type: os16.type(),
|
|
10092
|
+
os_version: os16.release(),
|
|
10093
|
+
architecture: os16.arch(),
|
|
9157
10094
|
workdir: cwd,
|
|
9158
10095
|
shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
|
|
9159
|
-
username:
|
|
10096
|
+
username: os16.userInfo().username,
|
|
9160
10097
|
current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
9161
10098
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
9162
10099
|
is_git_repo: isGitRepo(cwd) ? "yes" : "no",
|
|
@@ -9209,13 +10146,20 @@ Runtime extensions (not skills); use when relevant; do not invent names.
|
|
|
9209
10146
|
<coding_memory_snapshot>
|
|
9210
10147
|
${memorySnapshot.trim().length > 0 ? memorySnapshot : "(empty \u2014 use coding_memory: add | list | search)"}
|
|
9211
10148
|
</coding_memory_snapshot>
|
|
10149
|
+
`;
|
|
10150
|
+
const blumaMdContent = readBlumaMdForPrompt(cwd);
|
|
10151
|
+
prompt += `
|
|
10152
|
+
|
|
10153
|
+
<bluma_md>
|
|
10154
|
+
${blumaMdContent}
|
|
10155
|
+
</bluma_md>
|
|
9212
10156
|
`;
|
|
9213
10157
|
return prompt;
|
|
9214
10158
|
}
|
|
9215
10159
|
function isGitRepo(dir) {
|
|
9216
10160
|
try {
|
|
9217
|
-
const gitPath =
|
|
9218
|
-
return
|
|
10161
|
+
const gitPath = path27.join(dir, ".git");
|
|
10162
|
+
return fs25.existsSync(gitPath) && fs25.lstatSync(gitPath).isDirectory();
|
|
9219
10163
|
} catch {
|
|
9220
10164
|
return false;
|
|
9221
10165
|
}
|
|
@@ -9432,16 +10376,22 @@ async function createApiContextWindow(fullHistory, currentAnchor, compressedTurn
|
|
|
9432
10376
|
let messages = buildContextMessages(systemMessages, anchor, pendingFlat, recentFlat);
|
|
9433
10377
|
let tokens = countTokens(messages);
|
|
9434
10378
|
while (tokens >= thresholdTokens && pendingSlices.length > 0) {
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
10379
|
+
try {
|
|
10380
|
+
anchor = await compressToAnchor(
|
|
10381
|
+
pendingFlat,
|
|
10382
|
+
anchor,
|
|
10383
|
+
llmService,
|
|
10384
|
+
userContext,
|
|
10385
|
+
pendingSlices.length
|
|
10386
|
+
);
|
|
10387
|
+
sliceCount = recentStart;
|
|
10388
|
+
pendingSlices = [];
|
|
10389
|
+
pendingFlat = [];
|
|
10390
|
+
} catch (compressError) {
|
|
10391
|
+
console.warn("[ContextManager] Compression failed, keeping uncompressed history:", compressError?.message || compressError);
|
|
10392
|
+
pendingSlices = [];
|
|
10393
|
+
pendingFlat = [];
|
|
10394
|
+
}
|
|
9445
10395
|
messages = buildContextMessages(systemMessages, anchor, pendingFlat, recentFlat);
|
|
9446
10396
|
tokens = countTokens(messages);
|
|
9447
10397
|
}
|
|
@@ -9454,7 +10404,7 @@ async function createApiContextWindow(fullHistory, currentAnchor, compressedTurn
|
|
|
9454
10404
|
|
|
9455
10405
|
// src/app/agent/core/llm/llm.ts
|
|
9456
10406
|
init_runtime_config();
|
|
9457
|
-
import
|
|
10407
|
+
import os17 from "os";
|
|
9458
10408
|
import OpenAI from "openai";
|
|
9459
10409
|
function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
9460
10410
|
const msg = String(userMessage || "").slice(0, 300);
|
|
@@ -9471,7 +10421,7 @@ function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
|
9471
10421
|
}
|
|
9472
10422
|
function getPreferredMacAddress() {
|
|
9473
10423
|
try {
|
|
9474
|
-
const ifaces =
|
|
10424
|
+
const ifaces = os17.networkInterfaces();
|
|
9475
10425
|
for (const name of Object.keys(ifaces)) {
|
|
9476
10426
|
const addrs = ifaces[name];
|
|
9477
10427
|
if (!addrs) continue;
|
|
@@ -9486,7 +10436,7 @@ function getPreferredMacAddress() {
|
|
|
9486
10436
|
} catch {
|
|
9487
10437
|
}
|
|
9488
10438
|
try {
|
|
9489
|
-
return `host:${
|
|
10439
|
+
return `host:${os17.hostname()}`;
|
|
9490
10440
|
} catch {
|
|
9491
10441
|
return "unknown";
|
|
9492
10442
|
}
|
|
@@ -9496,7 +10446,7 @@ function defaultInteractiveCliUserContextInput(sessionId, userMessage) {
|
|
|
9496
10446
|
const machineId = getPreferredMacAddress();
|
|
9497
10447
|
let userName = null;
|
|
9498
10448
|
try {
|
|
9499
|
-
userName =
|
|
10449
|
+
userName = os17.userInfo().username || null;
|
|
9500
10450
|
} catch {
|
|
9501
10451
|
userName = null;
|
|
9502
10452
|
}
|
|
@@ -9943,8 +10893,8 @@ function classifyToolInvocation(input) {
|
|
|
9943
10893
|
|
|
9944
10894
|
// src/app/agent/runtime/hook_registry.ts
|
|
9945
10895
|
init_sandbox_policy();
|
|
9946
|
-
import
|
|
9947
|
-
import
|
|
10896
|
+
import fs26 from "fs";
|
|
10897
|
+
import path28 from "path";
|
|
9948
10898
|
var DEFAULT_STATE = {
|
|
9949
10899
|
enabled: true,
|
|
9950
10900
|
maxEvents: 120,
|
|
@@ -9955,7 +10905,7 @@ var cache2 = null;
|
|
|
9955
10905
|
var cachePath2 = null;
|
|
9956
10906
|
function getStatePath() {
|
|
9957
10907
|
const policy = getSandboxPolicy();
|
|
9958
|
-
return
|
|
10908
|
+
return path28.join(policy.workspaceRoot, ".bluma", "hooks.json");
|
|
9959
10909
|
}
|
|
9960
10910
|
function getHookStatePath() {
|
|
9961
10911
|
return getStatePath();
|
|
@@ -9974,8 +10924,8 @@ function ensureLoaded2() {
|
|
|
9974
10924
|
return cache2;
|
|
9975
10925
|
}
|
|
9976
10926
|
try {
|
|
9977
|
-
if (
|
|
9978
|
-
const parsed = JSON.parse(
|
|
10927
|
+
if (fs26.existsSync(statePath)) {
|
|
10928
|
+
const parsed = JSON.parse(fs26.readFileSync(statePath, "utf-8"));
|
|
9979
10929
|
cache2 = {
|
|
9980
10930
|
enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
|
|
9981
10931
|
maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
|
|
@@ -10001,9 +10951,9 @@ function ensureLoaded2() {
|
|
|
10001
10951
|
}
|
|
10002
10952
|
function persist2(state) {
|
|
10003
10953
|
const statePath = getStatePath();
|
|
10004
|
-
|
|
10954
|
+
fs26.mkdirSync(path28.dirname(statePath), { recursive: true });
|
|
10005
10955
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10006
|
-
|
|
10956
|
+
fs26.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
10007
10957
|
cache2 = state;
|
|
10008
10958
|
cachePath2 = statePath;
|
|
10009
10959
|
}
|
|
@@ -10100,11 +11050,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
|
|
|
10100
11050
|
}
|
|
10101
11051
|
|
|
10102
11052
|
// src/app/agent/tools/natives/coding_memory_consolidate.ts
|
|
10103
|
-
import * as
|
|
10104
|
-
import * as
|
|
10105
|
-
import
|
|
11053
|
+
import * as fs27 from "fs";
|
|
11054
|
+
import * as path29 from "path";
|
|
11055
|
+
import os18 from "os";
|
|
10106
11056
|
function memoryPath() {
|
|
10107
|
-
return
|
|
11057
|
+
return path29.join(process.env.HOME || os18.homedir(), ".bluma", "coding_memory.json");
|
|
10108
11058
|
}
|
|
10109
11059
|
function normalizeNote(note) {
|
|
10110
11060
|
return note.trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -10114,18 +11064,18 @@ function uniqTags(a, b) {
|
|
|
10114
11064
|
}
|
|
10115
11065
|
function consolidateCodingMemoryFile() {
|
|
10116
11066
|
const p = memoryPath();
|
|
10117
|
-
if (!
|
|
11067
|
+
if (!fs27.existsSync(p)) {
|
|
10118
11068
|
return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
|
|
10119
11069
|
}
|
|
10120
11070
|
const bak = `${p}.bak`;
|
|
10121
11071
|
try {
|
|
10122
|
-
|
|
11072
|
+
fs27.copyFileSync(p, bak);
|
|
10123
11073
|
} catch (e) {
|
|
10124
11074
|
return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
|
|
10125
11075
|
}
|
|
10126
11076
|
let data;
|
|
10127
11077
|
try {
|
|
10128
|
-
data = JSON.parse(
|
|
11078
|
+
data = JSON.parse(fs27.readFileSync(p, "utf-8"));
|
|
10129
11079
|
} catch (e) {
|
|
10130
11080
|
return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
|
|
10131
11081
|
}
|
|
@@ -10160,7 +11110,7 @@ function consolidateCodingMemoryFile() {
|
|
|
10160
11110
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10161
11111
|
};
|
|
10162
11112
|
try {
|
|
10163
|
-
|
|
11113
|
+
fs27.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
|
|
10164
11114
|
} catch (e) {
|
|
10165
11115
|
return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
|
|
10166
11116
|
}
|
|
@@ -10353,7 +11303,7 @@ var BluMaAgent = class {
|
|
|
10353
11303
|
this.isInterrupted = false;
|
|
10354
11304
|
this.factorRouterTurnClosed = false;
|
|
10355
11305
|
const inputText = String(userInput.content || "").trim();
|
|
10356
|
-
const turnId =
|
|
11306
|
+
const turnId = uuidv45();
|
|
10357
11307
|
this.activeTurnContext = {
|
|
10358
11308
|
...userContextInput,
|
|
10359
11309
|
turnId,
|
|
@@ -10755,7 +11705,7 @@ var BluMaAgent = class {
|
|
|
10755
11705
|
|
|
10756
11706
|
${editData.error.display}`;
|
|
10757
11707
|
}
|
|
10758
|
-
const filename =
|
|
11708
|
+
const filename = path30.basename(toolArgs.file_path);
|
|
10759
11709
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
10760
11710
|
} catch (e) {
|
|
10761
11711
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -10791,6 +11741,18 @@ ${editData.error.display}`;
|
|
|
10791
11741
|
this.eventBus.emit("backend_message", { type: "info", message: "Task Canceled." });
|
|
10792
11742
|
return;
|
|
10793
11743
|
}
|
|
11744
|
+
const mailboxUpdate = await this._pollWorkerMailbox();
|
|
11745
|
+
if (mailboxUpdate && mailboxUpdate.followUp) {
|
|
11746
|
+
this.history.push({
|
|
11747
|
+
role: "user",
|
|
11748
|
+
content: mailboxUpdate.followUp.message
|
|
11749
|
+
});
|
|
11750
|
+
this.persistSession();
|
|
11751
|
+
this.eventBus.emit("backend_message", {
|
|
11752
|
+
type: "info",
|
|
11753
|
+
message: `Received follow-up from coordinator (priority: ${mailboxUpdate.followUp.priority})`
|
|
11754
|
+
});
|
|
11755
|
+
}
|
|
10794
11756
|
const { messages: contextWindow, newAnchor, newCompressedTurnSliceCount } = await createApiContextWindow(
|
|
10795
11757
|
this.history,
|
|
10796
11758
|
this.sessionAnchor,
|
|
@@ -10855,7 +11817,6 @@ ${editData.error.display}`;
|
|
|
10855
11817
|
}
|
|
10856
11818
|
async _handleStreamingResponse(contextWindow) {
|
|
10857
11819
|
const llmService = this.llm;
|
|
10858
|
-
this.eventBus.emit("action_status", { action: "Thinking" });
|
|
10859
11820
|
let accumulatedContent = "";
|
|
10860
11821
|
let toolCalls;
|
|
10861
11822
|
let hasEmittedStart = false;
|
|
@@ -10887,8 +11848,15 @@ ${editData.error.display}`;
|
|
|
10887
11848
|
accumulatedContent += chunk.delta;
|
|
10888
11849
|
this.eventBus.emit("stream_chunk", { delta: chunk.delta });
|
|
10889
11850
|
}
|
|
10890
|
-
if (chunk.tool_calls) {
|
|
11851
|
+
if (chunk.tool_calls && chunk.tool_calls.length > 0) {
|
|
10891
11852
|
toolCalls = chunk.tool_calls;
|
|
11853
|
+
for (const tc of toolCalls || []) {
|
|
11854
|
+
const toolName = tc?.function?.name ?? "";
|
|
11855
|
+
const argsRaw = tc?.function?.arguments ?? "{}";
|
|
11856
|
+
if (toolName) {
|
|
11857
|
+
this.eventBus.emit("tool_stream_start", { toolName, argsRaw });
|
|
11858
|
+
}
|
|
11859
|
+
}
|
|
10892
11860
|
}
|
|
10893
11861
|
}
|
|
10894
11862
|
const omitAssistantFlush = Array.isArray(toolCalls) && toolCalls.some((tc) => String(tc?.function?.name ?? "").includes("message"));
|
|
@@ -11048,55 +12016,291 @@ ${editData.error.display}`;
|
|
|
11048
12016
|
} catch {
|
|
11049
12017
|
}
|
|
11050
12018
|
}
|
|
12019
|
+
/**
|
|
12020
|
+
* WORKER: Poll mailbox para checkar follow-ups do coordinator
|
|
12021
|
+
* Retorna null se não há novas mensagens ou se não é um worker
|
|
12022
|
+
*/
|
|
12023
|
+
async _pollWorkerMailbox() {
|
|
12024
|
+
if (!process.env.BLUMA_PARENT_SESSION_ID) {
|
|
12025
|
+
return null;
|
|
12026
|
+
}
|
|
12027
|
+
try {
|
|
12028
|
+
const { pollMailbox: pollMailbox2 } = await Promise.resolve().then(() => (init_poll_mailbox(), poll_mailbox_exports));
|
|
12029
|
+
const result = await pollMailbox2({
|
|
12030
|
+
timeout: 0,
|
|
12031
|
+
// Non-blocking
|
|
12032
|
+
pollInterval: 500,
|
|
12033
|
+
types: ["follow_up", "cancel_request", "permission_response"],
|
|
12034
|
+
includeSignals: false
|
|
12035
|
+
});
|
|
12036
|
+
if (!result.success || !result.hasNewMessages) {
|
|
12037
|
+
return null;
|
|
12038
|
+
}
|
|
12039
|
+
return {
|
|
12040
|
+
followUp: result.followUp ? {
|
|
12041
|
+
message: result.followUp.message,
|
|
12042
|
+
priority: result.followUp.priority
|
|
12043
|
+
} : void 0,
|
|
12044
|
+
cancelRequested: result.cancelRequested
|
|
12045
|
+
};
|
|
12046
|
+
} catch (error) {
|
|
12047
|
+
return null;
|
|
12048
|
+
}
|
|
12049
|
+
}
|
|
11051
12050
|
};
|
|
11052
12051
|
|
|
11053
|
-
// src/app/agent/subagents/registry.ts
|
|
11054
|
-
var subAgentRegistry = {};
|
|
11055
|
-
function registerSubAgent(subAgent) {
|
|
11056
|
-
for (const cap of subAgent.capabilities) {
|
|
11057
|
-
subAgentRegistry[cap] = subAgent;
|
|
12052
|
+
// src/app/agent/subagents/registry.ts
|
|
12053
|
+
var subAgentRegistry = {};
|
|
12054
|
+
function registerSubAgent(subAgent) {
|
|
12055
|
+
for (const cap of subAgent.capabilities) {
|
|
12056
|
+
subAgentRegistry[cap] = subAgent;
|
|
12057
|
+
}
|
|
12058
|
+
}
|
|
12059
|
+
function getSubAgentByCommand(cmd) {
|
|
12060
|
+
return subAgentRegistry[cmd];
|
|
12061
|
+
}
|
|
12062
|
+
|
|
12063
|
+
// src/app/agent/subagents/init/init_subagent.ts
|
|
12064
|
+
import { v4 as uuidv47 } from "uuid";
|
|
12065
|
+
|
|
12066
|
+
// src/app/agent/subagents/base_llm_subagent.ts
|
|
12067
|
+
import { v4 as uuidv46 } from "uuid";
|
|
12068
|
+
|
|
12069
|
+
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
12070
|
+
import os19 from "os";
|
|
12071
|
+
var SYSTEM_PROMPT2 = `
|
|
12072
|
+
|
|
12073
|
+
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
12074
|
+
You extend the BluMa multi-agent architecture and handle the project bootstrapping/init workflow: scanning the repository, inferring stack, and generating a high-quality BluMa.md with actionable project context.
|
|
12075
|
+
|
|
12076
|
+
---
|
|
12077
|
+
|
|
12078
|
+
## BEHAVIORAL RULES
|
|
12079
|
+
|
|
12080
|
+
- Identity:
|
|
12081
|
+
You are BluMa InitSubAgent. Maintain professionalism and technical language.
|
|
12082
|
+
|
|
12083
|
+
- Communication:
|
|
12084
|
+
ALL messages must be sent via 'message'.
|
|
12085
|
+
No direct text replies to the user.
|
|
12086
|
+
|
|
12087
|
+
- Task Completion:
|
|
12088
|
+
When the init task is completed, immediately invoke 'agent_end_turn' without user permissions.
|
|
12089
|
+
|
|
12090
|
+
- Tool Rules:
|
|
12091
|
+
Never make parallel tool calls.
|
|
12092
|
+
Only use the defined tools with their exact names.
|
|
12093
|
+
|
|
12094
|
+
- Autonomy:
|
|
12095
|
+
Act 100% autonomously.
|
|
12096
|
+
Do not ask for formatting preferences.
|
|
12097
|
+
Use the notebook for internal reasoning.
|
|
12098
|
+
|
|
12099
|
+
|
|
12100
|
+
### CRITICAL COMMUNICATION PROTOCOL
|
|
12101
|
+
- Only tool_calls are allowed for assistant replies. Never include a "content" field.
|
|
12102
|
+
- Always use tools to respond, retrieve data, compute or transform. Await a valid tool response before any final message.
|
|
12103
|
+
- Zero tolerance for protocol violations.
|
|
12104
|
+
|
|
12105
|
+
<current_system_environment>
|
|
12106
|
+
- Operating System: {os_type} ({os_version})
|
|
12107
|
+
- Architecture: {architecture}
|
|
12108
|
+
- Current Working Directory: {workdir}
|
|
12109
|
+
- Shell: {shell_type}
|
|
12110
|
+
- User: {username}
|
|
12111
|
+
- Current Date: {current_date}
|
|
12112
|
+
- Timezone: {timezone}
|
|
12113
|
+
- Locale: {locale}
|
|
12114
|
+
</current_system_environment>
|
|
12115
|
+
|
|
12116
|
+
<message_rules>
|
|
12117
|
+
- Communicate with user's via message tools instead of direct text responses
|
|
12118
|
+
- Reply immediately to new user messages before other operations
|
|
12119
|
+
- First reply must be brief, only confirming receipt without specific solutions
|
|
12120
|
+
- Notify user's with brief explanation when changing methods or strategies
|
|
12121
|
+
- Message tools are divided into notify (non-blocking, no reply needed) and ask (blocking)
|
|
12122
|
+
- Actively use notify for progress updates, reserve ask for essential needs to avoid blocking
|
|
12123
|
+
- Must message user's with results and deliverables before upon task completion 'agent_end_turn'
|
|
12124
|
+
</message_rules>
|
|
12125
|
+
|
|
12126
|
+
<reasoning_rules>
|
|
12127
|
+
# YOUR THINKING ON A NOTEBOOK - MANDATORY USE
|
|
12128
|
+
CRITICAL: Your laptop (reasoning_nootebook) is your ORGANIZED MIND
|
|
12129
|
+
## IMPORTANT
|
|
12130
|
+
## NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
|
|
12131
|
+
## ALWAYS USE A NOTEBOOK (Always for):
|
|
12132
|
+
- ANY task
|
|
12133
|
+
- Before starting userelopment (plan first!)
|
|
12134
|
+
- Projects with multiple files (organize the structure)
|
|
12135
|
+
- Debugging sessions (monitor discoveries)
|
|
12136
|
+
- Extensive refactoring (map the changes)
|
|
12137
|
+
- Architectural decisions (think through the options)
|
|
12138
|
+
|
|
12139
|
+
## HOW TO USE A NOTEBOOK:
|
|
12140
|
+
1. Start with reasoning_nootebook
|
|
12141
|
+
2. Break the task down into logical steps
|
|
12142
|
+
3. Plan the approach \u2013 Which files? What changes? What order?
|
|
12143
|
+
4. Track progress \u2013 Check off completed steps
|
|
12144
|
+
5. Write down decisions \u2013 Why did you choose this approach?
|
|
12145
|
+
6. Update continuously \u2013 Keep the notebook up to date
|
|
12146
|
+
|
|
12147
|
+
## THE NOTEBOOK PREVENTS:
|
|
12148
|
+
- Acting "outside the box"
|
|
12149
|
+
- Forgetting task requirements
|
|
12150
|
+
- Losing control of complex workflows
|
|
12151
|
+
- Making unplanned changes
|
|
12152
|
+
- Ineffective approaches
|
|
12153
|
+
- Working without a clear roadmap
|
|
12154
|
+
- Jumping between unrelated subtasks
|
|
12155
|
+
|
|
12156
|
+
Important rule:
|
|
12157
|
+
Do not include future steps/to-dos in thought; put them strictly in to_do, using the mandated checklist markers.
|
|
12158
|
+
|
|
12159
|
+
- to_do: Checklist list of high-level upcoming tasks.
|
|
12160
|
+
Format is mandatory:
|
|
12161
|
+
- "\u{1F5F8}" \u2192 for tasks not yet done (pending)
|
|
12162
|
+
- "[ ]" \u2192 for tasks already completed
|
|
12163
|
+
</reasoning_rules>
|
|
12164
|
+
|
|
12165
|
+
<edit_tool_rules>
|
|
12166
|
+
- Use this tool to perform precise text replacements inside files based on exact literal matches.
|
|
12167
|
+
- Can be used to create new files or directories implicitly by targeting non-existing paths.
|
|
12168
|
+
- Suitable for inserting full content into a file even if the file does not yet exist.
|
|
12169
|
+
- Shell access is not required for file or directory creation when using this tool.
|
|
12170
|
+
- Always prefer this tool over shell_command when performing structured edits or creating files with specific content.
|
|
12171
|
+
- Ensure **old_string** includes 3+ lines of exact context before and after the target if replacing existing content.
|
|
12172
|
+
- 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.
|
|
12173
|
+
- When generating or modifying todo.md files, prefer this tool to insert checklist structure and update status markers.
|
|
12174
|
+
- After completing any task in the checklist, immediately update the corresponding section in todo.md using this tool.
|
|
12175
|
+
- Reconstruct the entire file from task planning context if todo.md becomes outdated or inconsistent.
|
|
12176
|
+
- Track all progress related to planning and execution inside todo.md using text replacement only.
|
|
12177
|
+
</edit_tool_rules>
|
|
12178
|
+
|
|
12179
|
+
|
|
12180
|
+
<agent_end_turn>
|
|
12181
|
+
This tool is mandatory.
|
|
12182
|
+
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.
|
|
12183
|
+
</agent_end_turn>
|
|
12184
|
+
|
|
12185
|
+
### Tool Naming Policy
|
|
12186
|
+
- Use plain, unmodified, lowercase tool names
|
|
12187
|
+
- No special characters, spaces, or version suffixes
|
|
12188
|
+
|
|
12189
|
+
Rule Summary:
|
|
12190
|
+
- Use only a\u2013z, 0\u20139, and underscores (_)
|
|
12191
|
+
- Do not append suffixes like :0, :v2, etc.
|
|
12192
|
+
- Tool names must be static and predictable
|
|
12193
|
+
|
|
12194
|
+
|
|
12195
|
+
## INIT SUBAGENT OBJECTIVE
|
|
12196
|
+
- Map repository structure and significant files.
|
|
12197
|
+
- Infer tech stack (frameworks, package managers, languages, build/test tools).
|
|
12198
|
+
- Identify entry points, configuration files, and scripts.
|
|
12199
|
+
- Produce BluMa.md with:
|
|
12200
|
+
- Project overview and goals inferred from code/docs
|
|
12201
|
+
- Tech stack summary
|
|
12202
|
+
- Directory map (high-level)
|
|
12203
|
+
- Key configs and scripts
|
|
12204
|
+
- Known tasks or next steps for agents
|
|
12205
|
+
- Always use tools (ls, readLines, count_lines, shell_command, edit_tool) to gather evidence before writing.
|
|
12206
|
+
- Never invent file content. Read files via tools to confirm.
|
|
12207
|
+
|
|
12208
|
+
## OUTPUT
|
|
12209
|
+
- Emit 'backend_message' events through tools only (message) for progress updates.
|
|
12210
|
+
- Before writing BluMa.md, propose structure via message and proceed using edit_tool.
|
|
12211
|
+
- If an irreversible operation is needed (e.g., overwriting an existing BluMa.md), issue 'confirmation_request' unless user policy indicates auto-approval.
|
|
12212
|
+
- 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.
|
|
12213
|
+
- On successful generation of BluMa.md, emit 'done' with status 'completed' and call agent_end_turn.
|
|
12214
|
+
|
|
12215
|
+
## SAFETY & QUALITY
|
|
12216
|
+
- Be conservative with edits; generate previews (diff) for edit_tool where applicable.
|
|
12217
|
+
- Keep file system operations idempotent and explicit.
|
|
12218
|
+
- Prefer performance-efficient scans (avoid reading entire large binaries).
|
|
12219
|
+
- Respect test environment constraints.
|
|
12220
|
+
|
|
12221
|
+
## EXEMPLAR FLOW (GUIDELINE)
|
|
12222
|
+
1) Explore repo: ls + targeted readLines for key files (package.json, tsconfig.json, README, etc.)
|
|
12223
|
+
2) Synthesize stack and structure with citations of evidence (file paths) in the notebook
|
|
12224
|
+
3) Draft BluMa.md structure (message)
|
|
12225
|
+
4) Write BluMa.md via edit_tool
|
|
12226
|
+
5) Announce completion and agent_end_turn
|
|
12227
|
+
|
|
12228
|
+
|
|
12229
|
+
`;
|
|
12230
|
+
function getInitPrompt() {
|
|
12231
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
12232
|
+
const collectedData = {
|
|
12233
|
+
os_type: os19.type(),
|
|
12234
|
+
os_version: os19.release(),
|
|
12235
|
+
architecture: os19.arch(),
|
|
12236
|
+
workdir: process.cwd(),
|
|
12237
|
+
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
12238
|
+
username: os19.userInfo().username || "Unknown",
|
|
12239
|
+
current_date: now2.toISOString().split("T")[0],
|
|
12240
|
+
// Formato YYYY-MM-DD
|
|
12241
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
12242
|
+
locale: process.env.LANG || process.env.LC_ALL || "Unknown"
|
|
12243
|
+
};
|
|
12244
|
+
const finalEnv = {
|
|
12245
|
+
os_type: "Unknown",
|
|
12246
|
+
os_version: "Unknown",
|
|
12247
|
+
workdir: "Unknown",
|
|
12248
|
+
shell_type: "Unknown",
|
|
12249
|
+
username: "Unknown",
|
|
12250
|
+
architecture: "Unknown",
|
|
12251
|
+
current_date: "Unknown",
|
|
12252
|
+
timezone: "Unknown",
|
|
12253
|
+
locale: "Unknown",
|
|
12254
|
+
...collectedData
|
|
12255
|
+
// Os dados coletados sobrescrevem os padrões
|
|
12256
|
+
};
|
|
12257
|
+
let formattedPrompt = SYSTEM_PROMPT2;
|
|
12258
|
+
for (const key in finalEnv) {
|
|
12259
|
+
const placeholder = `{${key}}`;
|
|
12260
|
+
formattedPrompt = formattedPrompt.replace(new RegExp(placeholder, "g"), finalEnv[key]);
|
|
11058
12261
|
}
|
|
11059
|
-
|
|
11060
|
-
function getSubAgentByCommand(cmd) {
|
|
11061
|
-
return subAgentRegistry[cmd];
|
|
12262
|
+
return formattedPrompt;
|
|
11062
12263
|
}
|
|
11063
12264
|
|
|
11064
|
-
// src/app/agent/subagents/
|
|
11065
|
-
import
|
|
11066
|
-
|
|
11067
|
-
// src/app/agent/subagents/base_llm_subagent.ts
|
|
11068
|
-
import { v4 as uuidv45 } from "uuid";
|
|
11069
|
-
|
|
11070
|
-
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
11071
|
-
import os18 from "os";
|
|
11072
|
-
var SYSTEM_PROMPT2 = `
|
|
12265
|
+
// src/app/agent/subagents/worker_system_prompt.ts
|
|
12266
|
+
import os20 from "os";
|
|
12267
|
+
var WORKER_SYSTEM_PROMPT = `
|
|
11073
12268
|
|
|
11074
|
-
### YOU ARE BluMa CLI \u2014
|
|
11075
|
-
You
|
|
12269
|
+
### YOU ARE BluMa CLI \u2014 WORKER AGENT \u2014 AUTONOMOUS SOFTWARE ENGINEERING SPECIALIST @ NOMADENGENUITY
|
|
12270
|
+
You are a worker agent spawned by the BluMa Coordinator to execute specific software engineering tasks.
|
|
11076
12271
|
|
|
11077
12272
|
---
|
|
11078
12273
|
|
|
11079
12274
|
## BEHAVIORAL RULES
|
|
11080
12275
|
|
|
11081
|
-
- Identity
|
|
11082
|
-
You are BluMa
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
12276
|
+
- **Identity:**
|
|
12277
|
+
You are a BluMa Worker Agent. You execute tasks delegated by the Coordinator.
|
|
12278
|
+
Maintain professionalism and technical excellence.
|
|
12279
|
+
|
|
12280
|
+
- **Communication:**
|
|
12281
|
+
- ALL messages must be sent via the \`message\` tool
|
|
12282
|
+
- No direct text replies to the user
|
|
12283
|
+
- Report progress frequently using \`message\` with \`message_type: "info"\`
|
|
12284
|
+
- Report final results using \`message\` with \`message_type: "result"\`
|
|
12285
|
+
|
|
12286
|
+
- **Task Completion:**
|
|
12287
|
+
- When your task is completed, immediately invoke \`agent_end_turn\` without user permissions
|
|
12288
|
+
- Before ending, ensure all work is committed and tested
|
|
12289
|
+
- Report the final state (e.g., commit hash, test results, file paths)
|
|
12290
|
+
|
|
12291
|
+
- **Tool Rules:**
|
|
12292
|
+
- Never make parallel tool calls
|
|
12293
|
+
- Only use the defined tools with their exact names
|
|
12294
|
+
- Read before editing (\`read_file_lines\`, \`grep_search\`, \`ls_tool\`)
|
|
12295
|
+
- Verify changes with tests or typechecks when applicable
|
|
12296
|
+
|
|
12297
|
+
- **Autonomy:**
|
|
12298
|
+
- Act 100% autonomously within your task scope
|
|
12299
|
+
- Do not ask for clarification unless the task is fundamentally blocked
|
|
12300
|
+
- Use the notebook for internal reasoning and planning
|
|
12301
|
+
- If you encounter errors, attempt to resolve them before reporting failure
|
|
11099
12302
|
|
|
12303
|
+
---
|
|
11100
12304
|
|
|
11101
12305
|
### CRITICAL COMMUNICATION PROTOCOL
|
|
11102
12306
|
- Only tool_calls are allowed for assistant replies. Never include a "content" field.
|
|
@@ -11115,30 +12319,31 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
|
|
|
11115
12319
|
</current_system_environment>
|
|
11116
12320
|
|
|
11117
12321
|
<message_rules>
|
|
11118
|
-
- Communicate with user
|
|
12322
|
+
- Communicate with the user via \`message\` tool instead of direct text responses
|
|
11119
12323
|
- Reply immediately to new user messages before other operations
|
|
11120
|
-
- First reply must be brief, only confirming receipt
|
|
11121
|
-
- Notify user
|
|
12324
|
+
- First reply must be brief, only confirming receipt of the task
|
|
12325
|
+
- Notify user with brief explanation when changing methods or strategies
|
|
11122
12326
|
- Message tools are divided into notify (non-blocking, no reply needed) and ask (blocking)
|
|
11123
12327
|
- Actively use notify for progress updates, reserve ask for essential needs to avoid blocking
|
|
11124
|
-
- Must message user
|
|
12328
|
+
- Must message user with results and deliverables before calling \`agent_end_turn\`
|
|
11125
12329
|
</message_rules>
|
|
11126
12330
|
|
|
11127
12331
|
<reasoning_rules>
|
|
11128
12332
|
# YOUR THINKING ON A NOTEBOOK - MANDATORY USE
|
|
11129
|
-
CRITICAL: Your
|
|
12333
|
+
CRITICAL: Your notebook (reasoning_notebook) is your ORGANIZED MIND
|
|
12334
|
+
|
|
11130
12335
|
## IMPORTANT
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
- ANY task
|
|
11134
|
-
- Before starting
|
|
11135
|
-
- Projects with multiple files (organize the structure)
|
|
11136
|
-
- Debugging sessions (
|
|
11137
|
-
- Extensive refactoring (map the changes)
|
|
11138
|
-
- Architectural decisions (think through the options)
|
|
12336
|
+
- NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
|
|
12337
|
+
- ALWAYS USE A NOTEBOOK (Always for):
|
|
12338
|
+
- ANY task
|
|
12339
|
+
- Before starting development (plan first!)
|
|
12340
|
+
- Projects with multiple files (organize the structure)
|
|
12341
|
+
- Debugging sessions (track discoveries)
|
|
12342
|
+
- Extensive refactoring (map the changes)
|
|
12343
|
+
- Architectural decisions (think through the options)
|
|
11139
12344
|
|
|
11140
12345
|
## HOW TO USE A NOTEBOOK:
|
|
11141
|
-
1. Start with
|
|
12346
|
+
1. Start with reasoning_notebook
|
|
11142
12347
|
2. Break the task down into logical steps
|
|
11143
12348
|
3. Plan the approach \u2013 Which files? What changes? What order?
|
|
11144
12349
|
4. Track progress \u2013 Check off completed steps
|
|
@@ -11160,7 +12365,7 @@ Do not include future steps/to-dos in thought; put them strictly in to_do, using
|
|
|
11160
12365
|
- to_do: Checklist list of high-level upcoming tasks.
|
|
11161
12366
|
Format is mandatory:
|
|
11162
12367
|
- "\u{1F5F8}" \u2192 for tasks not yet done (pending)
|
|
11163
|
-
- "[
|
|
12368
|
+
- "[\u2713]" \u2192 for tasks already completed
|
|
11164
12369
|
</reasoning_rules>
|
|
11165
12370
|
|
|
11166
12371
|
<edit_tool_rules>
|
|
@@ -11177,10 +12382,9 @@ Do not include future steps/to-dos in thought; put them strictly in to_do, using
|
|
|
11177
12382
|
- Track all progress related to planning and execution inside todo.md using text replacement only.
|
|
11178
12383
|
</edit_tool_rules>
|
|
11179
12384
|
|
|
11180
|
-
|
|
11181
12385
|
<agent_end_turn>
|
|
11182
12386
|
This tool is mandatory.
|
|
11183
|
-
You must use it to inform
|
|
12387
|
+
You must use it to inform the user that the task has been completed and that there are no further pending actions, in accordance with the objectives defined for the task.
|
|
11184
12388
|
</agent_end_turn>
|
|
11185
12389
|
|
|
11186
12390
|
### Tool Naming Policy
|
|
@@ -11192,53 +12396,121 @@ Rule Summary:
|
|
|
11192
12396
|
- Do not append suffixes like :0, :v2, etc.
|
|
11193
12397
|
- Tool names must be static and predictable
|
|
11194
12398
|
|
|
12399
|
+
---
|
|
11195
12400
|
|
|
11196
|
-
##
|
|
11197
|
-
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
12401
|
+
## WORKER AGENT OBJECTIVE
|
|
12402
|
+
|
|
12403
|
+
You receive a specific task from the Coordinator. Your job is to:
|
|
12404
|
+
|
|
12405
|
+
1. **Understand the task** - Read the task description carefully
|
|
12406
|
+
2. **Plan your approach** - Use the notebook to break down the work
|
|
12407
|
+
3. **Execute autonomously** - Use tools to gather evidence, make changes, and verify
|
|
12408
|
+
4. **Report progress** - Keep the Coordinator informed via \`message\` tool
|
|
12409
|
+
5. **Verify your work** - Run tests, typechecks, or other validation
|
|
12410
|
+
6. **Complete and report** - Call \`agent_end_turn\` with final results
|
|
12411
|
+
|
|
12412
|
+
### Task Types
|
|
12413
|
+
|
|
12414
|
+
You may be assigned different types of work:
|
|
12415
|
+
|
|
12416
|
+
**Research Tasks:**
|
|
12417
|
+
- Investigate codebase structure
|
|
12418
|
+
- Find specific files or patterns
|
|
12419
|
+
- Analyze architectural decisions
|
|
12420
|
+
- Report findings with file paths, line numbers, and evidence
|
|
12421
|
+
- Do NOT modify files unless explicitly instructed
|
|
12422
|
+
|
|
12423
|
+
**Implementation Tasks:**
|
|
12424
|
+
- Make targeted code changes
|
|
12425
|
+
- Follow best practices and existing patterns
|
|
12426
|
+
- Run relevant tests and typechecks
|
|
12427
|
+
- Commit changes and report the commit hash
|
|
12428
|
+
- Fix root causes, not symptoms
|
|
12429
|
+
|
|
12430
|
+
**Verification Tasks:**
|
|
12431
|
+
- Test changes made by other workers
|
|
12432
|
+
- Run tests with the feature enabled
|
|
12433
|
+
- Investigate failures - don't dismiss as unrelated
|
|
12434
|
+
- Prove the code works, don't just confirm it exists
|
|
12435
|
+
- Report specific evidence (test output, error messages)
|
|
12436
|
+
|
|
12437
|
+
**Refactoring Tasks:**
|
|
12438
|
+
- Improve code structure without changing behavior
|
|
12439
|
+
- Maintain existing functionality
|
|
12440
|
+
- Update tests if needed
|
|
12441
|
+
- Verify with tests and typechecks
|
|
12442
|
+
|
|
12443
|
+
---
|
|
11208
12444
|
|
|
11209
12445
|
## OUTPUT
|
|
11210
|
-
|
|
11211
|
-
-
|
|
11212
|
-
-
|
|
11213
|
-
- Never
|
|
11214
|
-
- On successful
|
|
12446
|
+
|
|
12447
|
+
- Emit \`backend_message\` events through tools only (\`message\`) for progress updates
|
|
12448
|
+
- Before making irreversible changes, ensure you have proper authorization or the runtime allows auto-approval
|
|
12449
|
+
- Never present draft versions - only produce and deliver final, validated results
|
|
12450
|
+
- On successful task completion, emit 'done' with status 'completed' and call \`agent_end_turn\`
|
|
12451
|
+
|
|
12452
|
+
---
|
|
11215
12453
|
|
|
11216
12454
|
## SAFETY & QUALITY
|
|
11217
|
-
|
|
11218
|
-
-
|
|
11219
|
-
-
|
|
11220
|
-
-
|
|
12455
|
+
|
|
12456
|
+
- Be conservative with edits; generate previews (diff) for \`edit_tool\` where applicable
|
|
12457
|
+
- Keep file system operations idempotent and explicit
|
|
12458
|
+
- Prefer performance-efficient scans (avoid reading entire large binaries)
|
|
12459
|
+
- Respect test environment constraints
|
|
12460
|
+
- Never expose secrets or run commands that dump environment variables
|
|
12461
|
+
|
|
12462
|
+
---
|
|
11221
12463
|
|
|
11222
12464
|
## EXEMPLAR FLOW (GUIDELINE)
|
|
11223
|
-
1) Explore repo: ls + targeted readLines for key files (package.json, tsconfig.json, README, etc.)
|
|
11224
|
-
2) Synthesize stack and structure with citations of evidence (file paths) in the notebook
|
|
11225
|
-
3) Draft BluMa.md structure (message)
|
|
11226
|
-
4) Write BluMa.md via edit_tool
|
|
11227
|
-
5) Announce completion and agent_end_turn
|
|
11228
12465
|
|
|
12466
|
+
### Research Task Example:
|
|
12467
|
+
1. Acknowledge task via \`message\` (info)
|
|
12468
|
+
2. Use \`ls_tool\`, \`read_file_lines\`, \`grep_search\` to gather evidence
|
|
12469
|
+
3. Track findings in notebook
|
|
12470
|
+
4. Report findings via \`message\` (result) with specific file paths and line numbers
|
|
12471
|
+
5. Call \`agent_end_turn\`
|
|
12472
|
+
|
|
12473
|
+
### Implementation Task Example:
|
|
12474
|
+
1. Acknowledge task via \`message\` (info)
|
|
12475
|
+
2. Read relevant files to understand current state
|
|
12476
|
+
3. Plan changes in notebook
|
|
12477
|
+
4. Use \`edit_tool\` to make changes
|
|
12478
|
+
5. Run tests via \`shell_command\`
|
|
12479
|
+
6. Fix any failures
|
|
12480
|
+
7. Commit changes via \`shell_command\` (git commit)
|
|
12481
|
+
8. Report commit hash via \`message\` (result)
|
|
12482
|
+
9. Call \`agent_end_turn\`
|
|
12483
|
+
|
|
12484
|
+
### Verification Task Example:
|
|
12485
|
+
1. Acknowledge task via \`message\` (info)
|
|
12486
|
+
2. Run tests via \`shell_command\`
|
|
12487
|
+
3. Investigate any failures deeply
|
|
12488
|
+
4. Report specific evidence (pass/fail, error messages)
|
|
12489
|
+
5. Call \`agent_end_turn\` with verdict
|
|
12490
|
+
|
|
12491
|
+
---
|
|
12492
|
+
|
|
12493
|
+
## CRITICAL REMINDERS
|
|
12494
|
+
|
|
12495
|
+
- **You cannot see the Coordinator's conversation** - Your task prompt must be self-contained
|
|
12496
|
+
- **Work autonomously** - Don't ask for clarification unless fundamentally blocked
|
|
12497
|
+
- **Verify before reporting done** - Run tests, check types, ensure code works
|
|
12498
|
+
- **Report specific evidence** - File paths, line numbers, commit hashes, test output
|
|
12499
|
+
- **Use the notebook** - Plan, track progress, record decisions
|
|
12500
|
+
- **Communicate progress** - Use \`message\` tool frequently with \`message_type: "info"\`
|
|
12501
|
+
- **End properly** - Call \`agent_end_turn\` when task is complete
|
|
11229
12502
|
|
|
11230
12503
|
`;
|
|
11231
|
-
function
|
|
12504
|
+
function getWorkerPrompt() {
|
|
11232
12505
|
const now2 = /* @__PURE__ */ new Date();
|
|
11233
12506
|
const collectedData = {
|
|
11234
|
-
os_type:
|
|
11235
|
-
os_version:
|
|
11236
|
-
architecture:
|
|
12507
|
+
os_type: os20.type(),
|
|
12508
|
+
os_version: os20.release(),
|
|
12509
|
+
architecture: os20.arch(),
|
|
11237
12510
|
workdir: process.cwd(),
|
|
11238
12511
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
11239
|
-
username:
|
|
12512
|
+
username: os20.userInfo().username || "Unknown",
|
|
11240
12513
|
current_date: now2.toISOString().split("T")[0],
|
|
11241
|
-
// Formato YYYY-MM-DD
|
|
11242
12514
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
11243
12515
|
locale: process.env.LANG || process.env.LC_ALL || "Unknown"
|
|
11244
12516
|
};
|
|
@@ -11253,9 +12525,8 @@ function getInitPrompt() {
|
|
|
11253
12525
|
timezone: "Unknown",
|
|
11254
12526
|
locale: "Unknown",
|
|
11255
12527
|
...collectedData
|
|
11256
|
-
// Os dados coletados sobrescrevem os padrões
|
|
11257
12528
|
};
|
|
11258
|
-
let formattedPrompt =
|
|
12529
|
+
let formattedPrompt = WORKER_SYSTEM_PROMPT;
|
|
11259
12530
|
for (const key in finalEnv) {
|
|
11260
12531
|
const placeholder = `{${key}}`;
|
|
11261
12532
|
formattedPrompt = formattedPrompt.replace(new RegExp(placeholder, "g"), finalEnv[key]);
|
|
@@ -11281,7 +12552,7 @@ var BaseLLMSubAgent = class {
|
|
|
11281
12552
|
await this.initializeHistory();
|
|
11282
12553
|
const rawUser = typeof input === "string" ? input : JSON.stringify(input);
|
|
11283
12554
|
const base = ctx.blumaUserContextInput ?? defaultBlumaUserContextInput(`subagent:${this.id}`, rawUser.slice(0, 300));
|
|
11284
|
-
const turnId =
|
|
12555
|
+
const turnId = uuidv46();
|
|
11285
12556
|
this.subagentTurnContext = {
|
|
11286
12557
|
...base,
|
|
11287
12558
|
turnId,
|
|
@@ -11296,7 +12567,7 @@ var BaseLLMSubAgent = class {
|
|
|
11296
12567
|
const [sessionFile, history] = await loadOrcreateSession(sessionId);
|
|
11297
12568
|
this.sessionFile = sessionFile;
|
|
11298
12569
|
this.history = history || [];
|
|
11299
|
-
const systemPromptContent = getInitPrompt();
|
|
12570
|
+
const systemPromptContent = this.id === "init" ? getInitPrompt() : getWorkerPrompt();
|
|
11300
12571
|
if (this.history.length === 0) {
|
|
11301
12572
|
this.history.push({
|
|
11302
12573
|
role: "system",
|
|
@@ -11449,7 +12720,7 @@ var InitAgentImpl = class extends BaseLLMSubAgent {
|
|
|
11449
12720
|
const base = ctx.blumaUserContextInput ?? defaultBlumaUserContextInput(`subagent:${this.id}`, preview);
|
|
11450
12721
|
this.subagentTurnContext = {
|
|
11451
12722
|
...base,
|
|
11452
|
-
turnId:
|
|
12723
|
+
turnId: uuidv47(),
|
|
11453
12724
|
sessionId: base.sessionId || `subagent:${this.id}`
|
|
11454
12725
|
};
|
|
11455
12726
|
const seed = `
|
|
@@ -11543,14 +12814,14 @@ var RouteManager = class {
|
|
|
11543
12814
|
this.subAgents = subAgents;
|
|
11544
12815
|
this.core = core;
|
|
11545
12816
|
}
|
|
11546
|
-
registerRoute(
|
|
11547
|
-
this.routeHandlers.set(
|
|
12817
|
+
registerRoute(path35, handler) {
|
|
12818
|
+
this.routeHandlers.set(path35, handler);
|
|
11548
12819
|
}
|
|
11549
12820
|
async handleRoute(payload) {
|
|
11550
12821
|
const inputText = String(payload.content || "").trim();
|
|
11551
12822
|
const { userContext } = payload;
|
|
11552
|
-
for (const [
|
|
11553
|
-
if (inputText ===
|
|
12823
|
+
for (const [path35, handler] of this.routeHandlers) {
|
|
12824
|
+
if (inputText === path35 || inputText.startsWith(`${path35} `)) {
|
|
11554
12825
|
return handler({ content: inputText, userContext });
|
|
11555
12826
|
}
|
|
11556
12827
|
}
|
|
@@ -11559,13 +12830,13 @@ var RouteManager = class {
|
|
|
11559
12830
|
};
|
|
11560
12831
|
|
|
11561
12832
|
// src/app/agent/runtime/plugin_runtime.ts
|
|
11562
|
-
import
|
|
12833
|
+
import path31 from "path";
|
|
11563
12834
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
11564
12835
|
async function loadPluginsAtStartup() {
|
|
11565
12836
|
for (const p of listPlugins()) {
|
|
11566
12837
|
const entry = p.manifest.entry?.trim();
|
|
11567
12838
|
if (!entry) continue;
|
|
11568
|
-
const abs =
|
|
12839
|
+
const abs = path31.resolve(p.root, entry);
|
|
11569
12840
|
try {
|
|
11570
12841
|
const href = pathToFileURL2(abs).href;
|
|
11571
12842
|
const mod = await import(href);
|
|
@@ -11586,7 +12857,7 @@ async function loadPluginsAtStartup() {
|
|
|
11586
12857
|
}
|
|
11587
12858
|
|
|
11588
12859
|
// src/app/agent/agent.ts
|
|
11589
|
-
var globalEnvPath =
|
|
12860
|
+
var globalEnvPath = path32.join(os21.homedir(), ".bluma", ".env");
|
|
11590
12861
|
dotenv.config({ path: globalEnvPath });
|
|
11591
12862
|
var Agent = class {
|
|
11592
12863
|
sessionId;
|
|
@@ -11820,6 +13091,162 @@ function formatTurnDurationMs(ms) {
|
|
|
11820
13091
|
return `${min}min \xB7 ${sec}s`;
|
|
11821
13092
|
}
|
|
11822
13093
|
|
|
13094
|
+
// src/app/ui/utils/toolActionLabels.ts
|
|
13095
|
+
function parseArgsRecord2(args) {
|
|
13096
|
+
if (args == null) return {};
|
|
13097
|
+
if (typeof args === "string") {
|
|
13098
|
+
try {
|
|
13099
|
+
return JSON.parse(args);
|
|
13100
|
+
} catch {
|
|
13101
|
+
return {};
|
|
13102
|
+
}
|
|
13103
|
+
}
|
|
13104
|
+
if (typeof args === "object") {
|
|
13105
|
+
return args;
|
|
13106
|
+
}
|
|
13107
|
+
return {};
|
|
13108
|
+
}
|
|
13109
|
+
function getToolActionLabel(toolName, args) {
|
|
13110
|
+
const p = parseArgsRecord2(args);
|
|
13111
|
+
switch (toolName) {
|
|
13112
|
+
case "shell_command":
|
|
13113
|
+
case "run_command": {
|
|
13114
|
+
const cmd = typeof p.command === "string" ? p.command : "";
|
|
13115
|
+
const truncated = cmd.length > 40 ? `${cmd.slice(0, 40)}\u2026` : cmd;
|
|
13116
|
+
return truncated ? `Executing: ${truncated}` : "Executing command";
|
|
13117
|
+
}
|
|
13118
|
+
case "command_status":
|
|
13119
|
+
return "Checking command status";
|
|
13120
|
+
case "send_command_input":
|
|
13121
|
+
return "Sending input to command";
|
|
13122
|
+
case "kill_command":
|
|
13123
|
+
return "Terminating command";
|
|
13124
|
+
case "read_file_lines": {
|
|
13125
|
+
const filepath = typeof p.filepath === "string" ? p.filepath : "";
|
|
13126
|
+
const file = filepath.split("/").pop() || filepath;
|
|
13127
|
+
return file ? `Reading: ${file}` : "Reading file";
|
|
13128
|
+
}
|
|
13129
|
+
case "count_file_lines": {
|
|
13130
|
+
const filepath = typeof p.filepath === "string" ? p.filepath : "";
|
|
13131
|
+
const file = filepath.split("/").pop() || filepath;
|
|
13132
|
+
return file ? `Counting: ${file}` : "Counting lines";
|
|
13133
|
+
}
|
|
13134
|
+
case "edit_tool": {
|
|
13135
|
+
const edits = p.edits;
|
|
13136
|
+
const count = Array.isArray(edits) ? edits.length : 1;
|
|
13137
|
+
const filepath = typeof p.file_path === "string" ? p.file_path : Array.isArray(edits) && edits[0]?.file_path ? edits[0].file_path : "";
|
|
13138
|
+
const file = filepath ? filepath.split("/").pop() : "file";
|
|
13139
|
+
return count === 1 ? `Editing: ${file}` : `Editing ${count} changes`;
|
|
13140
|
+
}
|
|
13141
|
+
case "file_write": {
|
|
13142
|
+
const filepath = typeof p.filepath === "string" ? p.filepath : "";
|
|
13143
|
+
const file = filepath.split("/").pop() || filepath;
|
|
13144
|
+
return file ? `Writing: ${file}` : "Writing file";
|
|
13145
|
+
}
|
|
13146
|
+
case "grep_search": {
|
|
13147
|
+
const query = typeof p.query === "string" ? p.query : "";
|
|
13148
|
+
const truncated = query.length > 30 ? `${query.slice(0, 30)}\u2026` : query;
|
|
13149
|
+
return truncated ? `Searching: "${truncated}"` : "Searching";
|
|
13150
|
+
}
|
|
13151
|
+
case "find_by_name": {
|
|
13152
|
+
const pattern = typeof p.pattern === "string" ? p.pattern : "";
|
|
13153
|
+
return pattern ? `Finding: ${pattern}` : "Finding files";
|
|
13154
|
+
}
|
|
13155
|
+
case "view_file_outline": {
|
|
13156
|
+
const filepath = typeof p.file_path === "string" ? p.file_path : "";
|
|
13157
|
+
const file = filepath.split("/").pop() || filepath;
|
|
13158
|
+
return file ? `Outline: ${file}` : "Reading outline";
|
|
13159
|
+
}
|
|
13160
|
+
case "web_fetch": {
|
|
13161
|
+
const url = typeof p.url === "string" ? p.url : "";
|
|
13162
|
+
const truncated = url.length > 40 ? `${url.slice(0, 40)}\u2026` : url;
|
|
13163
|
+
return truncated ? `Fetching: ${truncated}` : "Fetching URL";
|
|
13164
|
+
}
|
|
13165
|
+
case "search_web": {
|
|
13166
|
+
const query = typeof p.query === "string" ? p.query : "";
|
|
13167
|
+
const truncated = query.length > 30 ? `${query.slice(0, 30)}\u2026` : query;
|
|
13168
|
+
return truncated ? `Web search: "${truncated}"` : "Searching web";
|
|
13169
|
+
}
|
|
13170
|
+
case "spawn_agent": {
|
|
13171
|
+
const title = typeof p.title === "string" ? p.title : "";
|
|
13172
|
+
const task = typeof p.task === "string" ? p.task : "";
|
|
13173
|
+
const label = title || (task ? task.slice(0, 40) : "task");
|
|
13174
|
+
return `Spawning agent: ${label}`;
|
|
13175
|
+
}
|
|
13176
|
+
case "wait_agent":
|
|
13177
|
+
return "Waiting for agent";
|
|
13178
|
+
case "list_agents":
|
|
13179
|
+
return "Listing agents";
|
|
13180
|
+
case "todo": {
|
|
13181
|
+
const action = typeof p.action === "string" ? p.action : "update";
|
|
13182
|
+
return `Updating todo: ${action}`;
|
|
13183
|
+
}
|
|
13184
|
+
case "task_boundary": {
|
|
13185
|
+
const mode = typeof p.mode === "string" ? p.mode : "";
|
|
13186
|
+
const taskName = typeof p.task_name === "string" ? p.task_name : "";
|
|
13187
|
+
return taskName ? `${mode}: ${taskName}` : `Task ${mode}`;
|
|
13188
|
+
}
|
|
13189
|
+
case "task_create":
|
|
13190
|
+
case "task_list":
|
|
13191
|
+
case "task_get":
|
|
13192
|
+
case "task_update":
|
|
13193
|
+
case "task_stop": {
|
|
13194
|
+
const title = typeof p.title === "string" ? p.title : "";
|
|
13195
|
+
return title ? `Task: ${title}` : "Managing task";
|
|
13196
|
+
}
|
|
13197
|
+
case "load_skill": {
|
|
13198
|
+
const skill = typeof p.skill_name === "string" ? p.skill_name : "";
|
|
13199
|
+
return skill ? `Loading skill: ${skill}` : "Loading skill";
|
|
13200
|
+
}
|
|
13201
|
+
case "coding_memory": {
|
|
13202
|
+
const action = typeof p.action === "string" ? p.action : "update";
|
|
13203
|
+
return `Coding memory: ${action}`;
|
|
13204
|
+
}
|
|
13205
|
+
case "create_artifact": {
|
|
13206
|
+
const filename = typeof p.filename === "string" ? p.filename : "";
|
|
13207
|
+
return filename ? `Creating: ${filename}` : "Creating artifact";
|
|
13208
|
+
}
|
|
13209
|
+
case "read_artifact": {
|
|
13210
|
+
const filename = typeof p.filename === "string" ? p.filename : "";
|
|
13211
|
+
return filename ? `Reading: ${filename}` : "Reading artifact";
|
|
13212
|
+
}
|
|
13213
|
+
case "message":
|
|
13214
|
+
return "Writing message";
|
|
13215
|
+
case "ask_user_question":
|
|
13216
|
+
return "Asking question";
|
|
13217
|
+
case "enter_plan_mode":
|
|
13218
|
+
return "Entering plan mode";
|
|
13219
|
+
case "exit_plan_mode":
|
|
13220
|
+
return "Exiting plan mode";
|
|
13221
|
+
case "list_mcp_resources":
|
|
13222
|
+
return "Listing MCP resources";
|
|
13223
|
+
case "read_mcp_resource": {
|
|
13224
|
+
const server = typeof p.server === "string" ? p.server : "";
|
|
13225
|
+
const uri = typeof p.uri === "string" ? p.uri : "";
|
|
13226
|
+
return `Reading MCP: ${server || uri || "resource"}`;
|
|
13227
|
+
}
|
|
13228
|
+
case "cron_create":
|
|
13229
|
+
return "Scheduling reminder";
|
|
13230
|
+
case "cron_list":
|
|
13231
|
+
return "Listing reminders";
|
|
13232
|
+
case "cron_delete":
|
|
13233
|
+
return "Canceling reminder";
|
|
13234
|
+
case "notebook_edit": {
|
|
13235
|
+
const filepath = typeof p.filepath === "string" ? p.filepath : "";
|
|
13236
|
+
const file = filepath.split("/").pop() || filepath;
|
|
13237
|
+
return file ? `Editing notebook: ${file}` : "Editing notebook";
|
|
13238
|
+
}
|
|
13239
|
+
case "lsp_query": {
|
|
13240
|
+
const operation = typeof p.operation === "string" ? p.operation : "";
|
|
13241
|
+
return `LSP: ${operation || "query"}`;
|
|
13242
|
+
}
|
|
13243
|
+
default: {
|
|
13244
|
+
const pretty = toolName.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
13245
|
+
return pretty || "Working";
|
|
13246
|
+
}
|
|
13247
|
+
}
|
|
13248
|
+
}
|
|
13249
|
+
|
|
11823
13250
|
// src/app/ui/WorkingTimer.tsx
|
|
11824
13251
|
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
11825
13252
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -11837,11 +13264,20 @@ function shimmerWorkingLine(text, phase) {
|
|
|
11837
13264
|
}).join("");
|
|
11838
13265
|
}
|
|
11839
13266
|
var SHIMMER_RAMP_LEN = BLUMA_TERMINAL.workingShimmerRamp.length;
|
|
11840
|
-
var WorkingTimerComponent = ({
|
|
11841
|
-
|
|
13267
|
+
var WorkingTimerComponent = ({
|
|
13268
|
+
eventBus,
|
|
13269
|
+
taskName,
|
|
13270
|
+
taskStatus,
|
|
13271
|
+
liveToolName,
|
|
13272
|
+
liveToolArgs,
|
|
13273
|
+
isReasoning,
|
|
13274
|
+
startedAtMs
|
|
13275
|
+
}) => {
|
|
13276
|
+
const [currentAction, setCurrentAction] = useState4("working");
|
|
11842
13277
|
const [frame, setFrame] = useState4(0);
|
|
11843
13278
|
const [shimmerPhase, setShimmerPhase] = useState4(0);
|
|
11844
13279
|
const [nowTick, setNowTick] = useState4(() => Date.now());
|
|
13280
|
+
const dynamicActionLabel = liveToolName ? getToolActionLabel(liveToolName, liveToolArgs) : null;
|
|
11845
13281
|
useEffect4(() => {
|
|
11846
13282
|
if (!eventBus) return;
|
|
11847
13283
|
const handleActionStatus = (data) => {
|
|
@@ -11874,7 +13310,7 @@ var WorkingTimerComponent = ({ eventBus, taskName, taskStatus, startedAtMs }) =>
|
|
|
11874
13310
|
const id = setInterval(() => setNowTick(Date.now()), 500);
|
|
11875
13311
|
return () => clearInterval(id);
|
|
11876
13312
|
}, [startedAtMs]);
|
|
11877
|
-
const displayAction = (taskStatus || currentAction).trim() || "
|
|
13313
|
+
const displayAction = dynamicActionLabel || (taskStatus || (isReasoning ? "thinking" : currentAction)).trim() || "working";
|
|
11878
13314
|
const actionLine = `${displayAction}\u2026`;
|
|
11879
13315
|
const shimmerLine = shimmerWorkingLine(actionLine, shimmerPhase);
|
|
11880
13316
|
const elapsedMs = startedAtMs != null ? Math.max(0, nowTick - startedAtMs) : 0;
|
|
@@ -12127,12 +13563,12 @@ function renderBlockTokens(tokens, keyRoot) {
|
|
|
12127
13563
|
/* @__PURE__ */ jsx10(Box10, { flexDirection: "row", flexWrap: "wrap", children: table.header.map((cell, idx) => {
|
|
12128
13564
|
const headerNodes = walkInline(cell.tokens, `${key}-h${idx}`);
|
|
12129
13565
|
const styled = headerNodes.length > 0 ? styleInlineNodes(headerNodes, `${key}-h${idx}`, { bold: true, color: BLUMA_TERMINAL.inactive }) : [/* @__PURE__ */ jsx10(Text10, { bold: true, color: BLUMA_TERMINAL.inactive, wrap: "wrap", children: cell.text }, `${key}-h${idx}-t`)];
|
|
12130
|
-
return /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, flexDirection: "row", flexWrap: "wrap",
|
|
13566
|
+
return /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, flexDirection: "row", flexWrap: "wrap", children: styled }, idx);
|
|
12131
13567
|
}) }),
|
|
12132
13568
|
table.rows.map((row, rowIdx) => /* @__PURE__ */ jsx10(Box10, { flexDirection: "row", flexWrap: "wrap", children: row.map((cell, cellIdx) => {
|
|
12133
13569
|
const cellNodes = walkInline(cell.tokens, `${key}-c${rowIdx}-${cellIdx}`);
|
|
12134
13570
|
const styled = cellNodes.length > 0 ? styleInlineNodes(cellNodes, `${key}-c${rowIdx}-${cellIdx}`, { dimColor: true }) : [/* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "wrap", children: cell.text }, `${key}-c${rowIdx}-${cellIdx}-t`)];
|
|
12135
|
-
return /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, flexDirection: "row", flexWrap: "wrap",
|
|
13571
|
+
return /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, flexDirection: "row", flexWrap: "wrap", children: styled }, cellIdx);
|
|
12136
13572
|
}) }, `${key}-r${rowIdx}`))
|
|
12137
13573
|
] }, key)
|
|
12138
13574
|
);
|
|
@@ -12728,7 +14164,7 @@ var ToolCallDisplayComponent = ({
|
|
|
12728
14164
|
const Renderer = getToolRenderer(toolName);
|
|
12729
14165
|
const shellLike = isShellLikeToolName(toolName);
|
|
12730
14166
|
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 0, children: [
|
|
12731
|
-
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", flexWrap: "wrap", alignItems: "
|
|
14167
|
+
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
|
|
12732
14168
|
/* @__PURE__ */ jsx13(Text13, { color: shellLike ? BLUMA_TERMINAL.m3OnSurface : BLUMA_TERMINAL.inactive, children: TOOL_INVOCATION_MARK }),
|
|
12733
14169
|
/* @__PURE__ */ jsx13(Text13, { bold: true, color: shellLike ? BLUMA_TERMINAL.m3OnSurface : BLUMA_TERMINAL.claude, children: getToolInvocationTitle(toolName, args) })
|
|
12734
14170
|
] }),
|
|
@@ -12851,11 +14287,13 @@ function formatLogLine(line) {
|
|
|
12851
14287
|
}
|
|
12852
14288
|
|
|
12853
14289
|
// src/app/ui/components/SlashCommands.tsx
|
|
14290
|
+
init_session_registry();
|
|
12854
14291
|
init_sandbox_policy();
|
|
12855
14292
|
|
|
12856
14293
|
// src/app/agent/runtime/diagnostics.ts
|
|
12857
14294
|
init_runtime_config();
|
|
12858
14295
|
init_sandbox_policy();
|
|
14296
|
+
init_session_registry();
|
|
12859
14297
|
function buildDiagnosticsSnapshot(feedbackScore) {
|
|
12860
14298
|
const runtime = getRuntimeConfig();
|
|
12861
14299
|
const policy = getSandboxPolicy();
|
|
@@ -14192,16 +15630,16 @@ import latestVersion from "latest-version";
|
|
|
14192
15630
|
import semverGt from "semver/functions/gt.js";
|
|
14193
15631
|
import semverValid from "semver/functions/valid.js";
|
|
14194
15632
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
14195
|
-
import
|
|
14196
|
-
import
|
|
15633
|
+
import path33 from "path";
|
|
15634
|
+
import fs28 from "fs";
|
|
14197
15635
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
14198
15636
|
function findBlumaPackageJson(startDir) {
|
|
14199
15637
|
let dir = startDir;
|
|
14200
15638
|
for (let i = 0; i < 12; i++) {
|
|
14201
|
-
const candidate =
|
|
14202
|
-
if (
|
|
15639
|
+
const candidate = path33.join(dir, "package.json");
|
|
15640
|
+
if (fs28.existsSync(candidate)) {
|
|
14203
15641
|
try {
|
|
14204
|
-
const raw =
|
|
15642
|
+
const raw = fs28.readFileSync(candidate, "utf8");
|
|
14205
15643
|
const parsed = JSON.parse(raw);
|
|
14206
15644
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
14207
15645
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -14209,7 +15647,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
14209
15647
|
} catch {
|
|
14210
15648
|
}
|
|
14211
15649
|
}
|
|
14212
|
-
const parent =
|
|
15650
|
+
const parent = path33.dirname(dir);
|
|
14213
15651
|
if (parent === dir) break;
|
|
14214
15652
|
dir = parent;
|
|
14215
15653
|
}
|
|
@@ -14218,13 +15656,13 @@ function findBlumaPackageJson(startDir) {
|
|
|
14218
15656
|
function resolveInstalledBlumaPackage() {
|
|
14219
15657
|
const tried = /* @__PURE__ */ new Set();
|
|
14220
15658
|
const tryFrom = (dir) => {
|
|
14221
|
-
const abs =
|
|
15659
|
+
const abs = path33.resolve(dir);
|
|
14222
15660
|
if (tried.has(abs)) return null;
|
|
14223
15661
|
tried.add(abs);
|
|
14224
15662
|
return findBlumaPackageJson(abs);
|
|
14225
15663
|
};
|
|
14226
15664
|
try {
|
|
14227
|
-
const fromBundle = tryFrom(
|
|
15665
|
+
const fromBundle = tryFrom(path33.dirname(fileURLToPath5(import.meta.url)));
|
|
14228
15666
|
if (fromBundle) return fromBundle;
|
|
14229
15667
|
} catch {
|
|
14230
15668
|
}
|
|
@@ -14232,12 +15670,12 @@ function resolveInstalledBlumaPackage() {
|
|
|
14232
15670
|
if (argv1 && !argv1.startsWith("-")) {
|
|
14233
15671
|
try {
|
|
14234
15672
|
let resolved = argv1;
|
|
14235
|
-
if (
|
|
14236
|
-
resolved =
|
|
15673
|
+
if (path33.isAbsolute(argv1) && fs28.existsSync(argv1)) {
|
|
15674
|
+
resolved = fs28.realpathSync(argv1);
|
|
14237
15675
|
} else {
|
|
14238
|
-
resolved =
|
|
15676
|
+
resolved = path33.resolve(process.cwd(), argv1);
|
|
14239
15677
|
}
|
|
14240
|
-
const fromArgv = tryFrom(
|
|
15678
|
+
const fromArgv = tryFrom(path33.dirname(resolved));
|
|
14241
15679
|
if (fromArgv) return fromArgv;
|
|
14242
15680
|
} catch {
|
|
14243
15681
|
}
|
|
@@ -14610,8 +16048,240 @@ var AskUserQuestionPromptComponent = ({
|
|
|
14610
16048
|
};
|
|
14611
16049
|
var AskUserQuestionPrompt = memo14(AskUserQuestionPromptComponent);
|
|
14612
16050
|
|
|
14613
|
-
// src/app/ui/
|
|
16051
|
+
// src/app/ui/components/WorkerOverlay.tsx
|
|
16052
|
+
import { useState as useState10 } from "react";
|
|
16053
|
+
import { Box as Box25, useInput as useInput5 } from "ink";
|
|
16054
|
+
|
|
16055
|
+
// src/app/ui/components/WorkerStatusList.tsx
|
|
16056
|
+
import { useEffect as useEffect8, useState as useState8 } from "react";
|
|
16057
|
+
import { Box as Box23, Text as Text22 } from "ink";
|
|
16058
|
+
import Spinner2 from "ink-spinner";
|
|
14614
16059
|
import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
16060
|
+
var WorkerStatusList = ({
|
|
16061
|
+
parentSessionId,
|
|
16062
|
+
collapsed = false
|
|
16063
|
+
}) => {
|
|
16064
|
+
const [workers, setWorkers] = useState8([]);
|
|
16065
|
+
const [pollKey, setPollKey] = useState8(0);
|
|
16066
|
+
useEffect8(() => {
|
|
16067
|
+
const pollWorkers = async () => {
|
|
16068
|
+
try {
|
|
16069
|
+
const { listAgents: listAgents2 } = await Promise.resolve().then(() => (init_agent_coordination(), agent_coordination_exports));
|
|
16070
|
+
const { listMailboxMessages: listMailboxMessages2 } = await Promise.resolve().then(() => (init_list_mailbox_messages(), list_mailbox_messages_exports));
|
|
16071
|
+
const agentsResult = await listAgents2({ parent_session_id: parentSessionId });
|
|
16072
|
+
if (!agentsResult.success || !agentsResult.agents) {
|
|
16073
|
+
setWorkers([]);
|
|
16074
|
+
return;
|
|
16075
|
+
}
|
|
16076
|
+
const workersInfo = [];
|
|
16077
|
+
const agents = Array.isArray(agentsResult.agents) ? agentsResult.agents : [];
|
|
16078
|
+
for (const agent of agents) {
|
|
16079
|
+
const messagesResult = await listMailboxMessages2({
|
|
16080
|
+
from: agent.session_id,
|
|
16081
|
+
type: "progress_update",
|
|
16082
|
+
unreadOnly: false
|
|
16083
|
+
});
|
|
16084
|
+
let progress = void 0;
|
|
16085
|
+
if (messagesResult.messages && messagesResult.messages.length > 0) {
|
|
16086
|
+
const lastMsg = messagesResult.messages[0];
|
|
16087
|
+
if (lastMsg.metadata?.progress) {
|
|
16088
|
+
progress = {
|
|
16089
|
+
percent: lastMsg.metadata.progress.percent,
|
|
16090
|
+
currentTask: lastMsg.metadata.progress.currentTask,
|
|
16091
|
+
toolCallsCount: lastMsg.metadata.progress.toolCallsCount,
|
|
16092
|
+
tokensUsed: lastMsg.metadata.progress.tokensUsed
|
|
16093
|
+
};
|
|
16094
|
+
}
|
|
16095
|
+
}
|
|
16096
|
+
workersInfo.push({
|
|
16097
|
+
session_id: agent.session_id,
|
|
16098
|
+
title: agent.title || "worker",
|
|
16099
|
+
status: agent.status,
|
|
16100
|
+
started_at: agent.started_at,
|
|
16101
|
+
pid: agent.pid,
|
|
16102
|
+
metadata: agent.metadata,
|
|
16103
|
+
progress,
|
|
16104
|
+
isAlive: agent.status === "running"
|
|
16105
|
+
});
|
|
16106
|
+
}
|
|
16107
|
+
setWorkers(workersInfo);
|
|
16108
|
+
} catch (error) {
|
|
16109
|
+
}
|
|
16110
|
+
};
|
|
16111
|
+
pollWorkers();
|
|
16112
|
+
const interval = setInterval(() => setPollKey((k) => k + 1), 2e3);
|
|
16113
|
+
return () => clearInterval(interval);
|
|
16114
|
+
}, [pollKey, parentSessionId]);
|
|
16115
|
+
if (workers.length === 0) return null;
|
|
16116
|
+
if (collapsed) {
|
|
16117
|
+
return /* @__PURE__ */ jsx24(Box23, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", children: /* @__PURE__ */ jsxs22(Text22, { bold: true, color: "yellow", children: [
|
|
16118
|
+
"Workers: ",
|
|
16119
|
+
workers.length
|
|
16120
|
+
] }) });
|
|
16121
|
+
}
|
|
16122
|
+
return /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", children: [
|
|
16123
|
+
/* @__PURE__ */ jsxs22(Text22, { bold: true, color: "yellow", children: [
|
|
16124
|
+
"Workers (",
|
|
16125
|
+
workers.length,
|
|
16126
|
+
")"
|
|
16127
|
+
] }),
|
|
16128
|
+
workers.map((w, idx) => /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", borderBottom: true, borderBottomColor: "gray", children: [
|
|
16129
|
+
/* @__PURE__ */ jsxs22(Box23, { children: [
|
|
16130
|
+
/* @__PURE__ */ jsx24(Text22, { color: "cyan", children: w.metadata?.agent_type === "researcher" ? "[R]" : w.metadata?.agent_type === "implementer" ? "[I]" : "[W]" }),
|
|
16131
|
+
/* @__PURE__ */ jsxs22(Text22, { bold: true, color: "cyan", children: [
|
|
16132
|
+
" ",
|
|
16133
|
+
w.title
|
|
16134
|
+
] }),
|
|
16135
|
+
w.isAlive === true ? /* @__PURE__ */ jsxs22(Text22, { color: "green", children: [
|
|
16136
|
+
/* @__PURE__ */ jsx24(Spinner2, {}),
|
|
16137
|
+
" Running"
|
|
16138
|
+
] }) : /* @__PURE__ */ jsx24(Text22, { color: "gray", children: w.status })
|
|
16139
|
+
] }),
|
|
16140
|
+
w.progress && /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", children: [
|
|
16141
|
+
/* @__PURE__ */ jsxs22(Text22, { color: "magenta", children: [
|
|
16142
|
+
"Progress: ",
|
|
16143
|
+
w.progress.percent,
|
|
16144
|
+
"% ",
|
|
16145
|
+
w.progress.currentTask ? `- ${w.progress.currentTask}` : ""
|
|
16146
|
+
] }),
|
|
16147
|
+
/* @__PURE__ */ jsx24(Box23, { children: /* @__PURE__ */ jsx24(Text22, { color: "gray", children: Array.from({ length: 20 }).map((_, i) => i / 20 * 100 <= (w.progress.percent || 0) ? "#" : "-").join("") }) }),
|
|
16148
|
+
/* @__PURE__ */ jsxs22(Box23, { children: [
|
|
16149
|
+
w.progress.toolCallsCount !== void 0 && /* @__PURE__ */ jsxs22(Text22, { color: "blue", children: [
|
|
16150
|
+
" Tools: ",
|
|
16151
|
+
w.progress.toolCallsCount
|
|
16152
|
+
] }),
|
|
16153
|
+
w.progress.tokensUsed !== void 0 && /* @__PURE__ */ jsxs22(Text22, { color: "blue", children: [
|
|
16154
|
+
" Tokens: ",
|
|
16155
|
+
w.progress.tokensUsed.toLocaleString()
|
|
16156
|
+
] })
|
|
16157
|
+
] })
|
|
16158
|
+
] }),
|
|
16159
|
+
/* @__PURE__ */ jsxs22(Text22, { dimColor: true, color: "gray", children: [
|
|
16160
|
+
"ID: ",
|
|
16161
|
+
w.session_id.slice(0, 8),
|
|
16162
|
+
"... PID: ",
|
|
16163
|
+
w.pid || "N/A",
|
|
16164
|
+
" Started: ",
|
|
16165
|
+
new Date(w.started_at).toLocaleTimeString()
|
|
16166
|
+
] }),
|
|
16167
|
+
/* @__PURE__ */ jsxs22(Text22, { italic: true, dimColor: true, color: "gray", children: [
|
|
16168
|
+
"Ctrl+Shift+",
|
|
16169
|
+
idx + 1,
|
|
16170
|
+
" para transcript"
|
|
16171
|
+
] })
|
|
16172
|
+
] }, w.session_id))
|
|
16173
|
+
] });
|
|
16174
|
+
};
|
|
16175
|
+
|
|
16176
|
+
// src/app/ui/components/WorkerTranscript.tsx
|
|
16177
|
+
import { useEffect as useEffect9, useState as useState9 } from "react";
|
|
16178
|
+
import { Box as Box24, Text as Text23 } from "ink";
|
|
16179
|
+
import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
16180
|
+
var WorkerTranscript = ({ sessionId, title, onClose }) => {
|
|
16181
|
+
const [messages, setMessages] = useState9([]);
|
|
16182
|
+
useEffect9(() => {
|
|
16183
|
+
const loadTranscript = async () => {
|
|
16184
|
+
try {
|
|
16185
|
+
const { listMailboxMessages: listMailboxMessages2 } = await Promise.resolve().then(() => (init_list_mailbox_messages(), list_mailbox_messages_exports));
|
|
16186
|
+
const result = await listMailboxMessages2({ from: sessionId, unreadOnly: false, includeSignals: true });
|
|
16187
|
+
if (result.success && result.messages) {
|
|
16188
|
+
setMessages(result.messages);
|
|
16189
|
+
}
|
|
16190
|
+
} catch (error) {
|
|
16191
|
+
}
|
|
16192
|
+
};
|
|
16193
|
+
loadTranscript();
|
|
16194
|
+
const interval = setInterval(loadTranscript, 3e3);
|
|
16195
|
+
return () => clearInterval(interval);
|
|
16196
|
+
}, [sessionId]);
|
|
16197
|
+
return /* @__PURE__ */ jsxs23(Box24, { flexDirection: "column", borderStyle: "double", borderColor: "cyan", children: [
|
|
16198
|
+
/* @__PURE__ */ jsxs23(Box24, { children: [
|
|
16199
|
+
/* @__PURE__ */ jsxs23(Text23, { bold: true, color: "cyan", children: [
|
|
16200
|
+
"Transcript: ",
|
|
16201
|
+
title || sessionId.slice(0, 8),
|
|
16202
|
+
"..."
|
|
16203
|
+
] }),
|
|
16204
|
+
/* @__PURE__ */ jsx25(Text23, { color: "gray", dimColor: true, children: " ESC: fechar" })
|
|
16205
|
+
] }),
|
|
16206
|
+
/* @__PURE__ */ jsx25(Box24, { flexDirection: "column", height: 20, children: messages.length === 0 ? /* @__PURE__ */ jsx25(Text23, { dimColor: true, color: "gray", italic: true, children: "Nenhuma mensagem..." }) : messages.map((msg) => /* @__PURE__ */ jsxs23(Box24, { flexDirection: "column", children: [
|
|
16207
|
+
/* @__PURE__ */ jsxs23(Box24, { children: [
|
|
16208
|
+
/* @__PURE__ */ jsxs23(Text23, { dimColor: true, color: "gray", children: [
|
|
16209
|
+
"[",
|
|
16210
|
+
new Date(msg.timestamp).toLocaleTimeString(),
|
|
16211
|
+
"]"
|
|
16212
|
+
] }),
|
|
16213
|
+
/* @__PURE__ */ jsxs23(Text23, { color: msg.type === "progress_update" ? "green" : msg.type === "permission_request" ? "yellow" : "cyan", bold: true, children: [
|
|
16214
|
+
" ",
|
|
16215
|
+
msg.type
|
|
16216
|
+
] }),
|
|
16217
|
+
/* @__PURE__ */ jsxs23(Text23, { dimColor: true, color: "gray", children: [
|
|
16218
|
+
" ",
|
|
16219
|
+
msg.from.slice(0, 8),
|
|
16220
|
+
"... \u2192 ",
|
|
16221
|
+
msg.to.slice(0, 8),
|
|
16222
|
+
"..."
|
|
16223
|
+
] })
|
|
16224
|
+
] }),
|
|
16225
|
+
/* @__PURE__ */ jsxs23(Text23, { children: [
|
|
16226
|
+
" ",
|
|
16227
|
+
msg.content
|
|
16228
|
+
] }),
|
|
16229
|
+
msg.metadata?.progress && /* @__PURE__ */ jsxs23(Text23, { color: "magenta", children: [
|
|
16230
|
+
" Progress: ",
|
|
16231
|
+
msg.metadata.progress.percent,
|
|
16232
|
+
"% ",
|
|
16233
|
+
msg.metadata.progress.currentTask || ""
|
|
16234
|
+
] })
|
|
16235
|
+
] }, msg.id)) }),
|
|
16236
|
+
/* @__PURE__ */ jsx25(Box24, { borderTop: true, borderTopColor: "gray", children: /* @__PURE__ */ jsxs23(Text23, { dimColor: true, color: "gray", children: [
|
|
16237
|
+
"Total: ",
|
|
16238
|
+
messages.length,
|
|
16239
|
+
" messages | Session: ",
|
|
16240
|
+
sessionId.slice(0, 8),
|
|
16241
|
+
"..."
|
|
16242
|
+
] }) })
|
|
16243
|
+
] });
|
|
16244
|
+
};
|
|
16245
|
+
|
|
16246
|
+
// src/app/ui/components/WorkerOverlay.tsx
|
|
16247
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
16248
|
+
var WorkerOverlay = ({
|
|
16249
|
+
visible,
|
|
16250
|
+
sessionId,
|
|
16251
|
+
onClose,
|
|
16252
|
+
onZoomWorker
|
|
16253
|
+
}) => {
|
|
16254
|
+
const [zoomedWorker, setZoomedWorker] = useState10(null);
|
|
16255
|
+
useInput5((input, key) => {
|
|
16256
|
+
if (!visible) return;
|
|
16257
|
+
if (key.escape) {
|
|
16258
|
+
if (zoomedWorker) {
|
|
16259
|
+
setZoomedWorker(null);
|
|
16260
|
+
} else {
|
|
16261
|
+
onClose();
|
|
16262
|
+
}
|
|
16263
|
+
}
|
|
16264
|
+
});
|
|
16265
|
+
if (!visible) {
|
|
16266
|
+
return null;
|
|
16267
|
+
}
|
|
16268
|
+
return /* @__PURE__ */ jsx26(Box25, { flexDirection: "column", children: zoomedWorker ? /* @__PURE__ */ jsx26(
|
|
16269
|
+
WorkerTranscript,
|
|
16270
|
+
{
|
|
16271
|
+
sessionId: zoomedWorker,
|
|
16272
|
+
onClose: () => setZoomedWorker(null)
|
|
16273
|
+
}
|
|
16274
|
+
) : /* @__PURE__ */ jsx26(
|
|
16275
|
+
WorkerStatusList,
|
|
16276
|
+
{
|
|
16277
|
+
parentSessionId: sessionId,
|
|
16278
|
+
collapsed: false
|
|
16279
|
+
}
|
|
16280
|
+
) });
|
|
16281
|
+
};
|
|
16282
|
+
|
|
16283
|
+
// src/app/ui/App.tsx
|
|
16284
|
+
import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
14615
16285
|
var HISTORY_EMERGENCY_LIMIT = 3;
|
|
14616
16286
|
var HISTORY_KEEP_AFTER_CLEANUP = 3;
|
|
14617
16287
|
var blumaUpdateRegistryCheckStarted = false;
|
|
@@ -14641,51 +16311,67 @@ function UserMessageWithOptionalImages({
|
|
|
14641
16311
|
const cap = stripped2.trim();
|
|
14642
16312
|
const capDisp = cap.length > 800 ? `${cap.slice(0, 800)}\u2026` : cap;
|
|
14643
16313
|
const fallbackDisp = raw.length > 800 ? `${raw.slice(0, 800)}\u2026` : raw;
|
|
14644
|
-
return /* @__PURE__ */
|
|
16314
|
+
return /* @__PURE__ */ jsx27(ChatUserMessage, { children: pathStrs.length > 0 ? /* @__PURE__ */ jsx27(
|
|
14645
16315
|
ChatUserImageBlock,
|
|
14646
16316
|
{
|
|
14647
16317
|
imageCount: pathStrs.length,
|
|
14648
16318
|
caption: cap.length > 0 ? capDisp : null,
|
|
14649
16319
|
captionDim: true
|
|
14650
16320
|
}
|
|
14651
|
-
) : /* @__PURE__ */
|
|
16321
|
+
) : /* @__PURE__ */ jsx27(Text25, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, wrap: "wrap", children: fallbackDisp }) });
|
|
14652
16322
|
}
|
|
14653
16323
|
const displayRaw = raw.length > 1e4 ? `${raw.substring(0, 1e4)}...` : raw;
|
|
14654
16324
|
const paths = collectImagePathStrings(displayRaw);
|
|
14655
16325
|
const stripped = paths.length > 0 ? stripImagePathStrings(displayRaw, paths) : displayRaw;
|
|
14656
|
-
return /* @__PURE__ */
|
|
16326
|
+
return /* @__PURE__ */ jsx27(ChatUserMessage, { children: paths.length > 0 ? /* @__PURE__ */ jsx27(
|
|
14657
16327
|
ChatUserImageBlock,
|
|
14658
16328
|
{
|
|
14659
16329
|
imageCount: paths.length,
|
|
14660
16330
|
caption: stripped.trim().length > 0 ? stripped : null
|
|
14661
16331
|
}
|
|
14662
|
-
) : /* @__PURE__ */
|
|
16332
|
+
) : /* @__PURE__ */ jsx27(Text25, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, wrap: "wrap", children: displayRaw }) });
|
|
14663
16333
|
}
|
|
14664
16334
|
var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
14665
16335
|
const agentInstance = useRef6(null);
|
|
14666
|
-
const [history, setHistory] =
|
|
14667
|
-
const [statusMessage, setStatusMessage] =
|
|
16336
|
+
const [history, setHistory] = useState11([]);
|
|
16337
|
+
const [statusMessage, setStatusMessage] = useState11(
|
|
14668
16338
|
"Initializing agent..."
|
|
14669
16339
|
);
|
|
14670
|
-
const [toolsCount, setToolsCount] =
|
|
14671
|
-
const [mcpStatus, setMcpStatus] =
|
|
16340
|
+
const [toolsCount, setToolsCount] = useState11(null);
|
|
16341
|
+
const [mcpStatus, setMcpStatus] = useState11(
|
|
14672
16342
|
"connecting"
|
|
14673
16343
|
);
|
|
14674
|
-
const [isProcessing, setIsProcessing] =
|
|
14675
|
-
const [pendingConfirmation, setPendingConfirmation] =
|
|
16344
|
+
const [isProcessing, setIsProcessing] = useState11(true);
|
|
16345
|
+
const [pendingConfirmation, setPendingConfirmation] = useState11(
|
|
14676
16346
|
null
|
|
14677
16347
|
);
|
|
14678
|
-
const [confirmationPreview, setConfirmationPreview] =
|
|
16348
|
+
const [confirmationPreview, setConfirmationPreview] = useState11(
|
|
14679
16349
|
null
|
|
14680
16350
|
);
|
|
14681
|
-
const [pendingAskUserQuestions, setPendingAskUserQuestions] =
|
|
14682
|
-
const [
|
|
14683
|
-
const [
|
|
16351
|
+
const [pendingAskUserQuestions, setPendingAskUserQuestions] = useState11(null);
|
|
16352
|
+
const [showWorkers, setShowWorkers] = useState11(false);
|
|
16353
|
+
const [zoomedWorkerSession, setZoomedWorkerSession] = useState11(null);
|
|
16354
|
+
useInput6((input, key) => {
|
|
16355
|
+
if (key.ctrl && key.shift && input.toLowerCase() === "w") {
|
|
16356
|
+
setShowWorkers((prev) => !prev);
|
|
16357
|
+
}
|
|
16358
|
+
if (key.escape && showWorkers) {
|
|
16359
|
+
if (zoomedWorkerSession) {
|
|
16360
|
+
setZoomedWorkerSession(null);
|
|
16361
|
+
} else {
|
|
16362
|
+
setShowWorkers(false);
|
|
16363
|
+
}
|
|
16364
|
+
}
|
|
16365
|
+
});
|
|
16366
|
+
const [isInitAgentActive, setIsInitAgentActive] = useState11(false);
|
|
16367
|
+
const [liveToolName, setLiveToolName] = useState11(null);
|
|
16368
|
+
const [liveToolArgs, setLiveToolArgs] = useState11(void 0);
|
|
16369
|
+
const [isReasoning, setIsReasoning] = useState11(false);
|
|
14684
16370
|
const alwaysAcceptList = useRef6([]);
|
|
14685
16371
|
const workdir = process.cwd();
|
|
14686
16372
|
const turnStartedAtRef = useRef6(null);
|
|
14687
|
-
const [processingStartMs, setProcessingStartMs] =
|
|
14688
|
-
const markTurnStarted =
|
|
16373
|
+
const [processingStartMs, setProcessingStartMs] = useState11(null);
|
|
16374
|
+
const markTurnStarted = useCallback4(() => {
|
|
14689
16375
|
const t = Date.now();
|
|
14690
16376
|
turnStartedAtRef.current = t;
|
|
14691
16377
|
setProcessingStartMs(t);
|
|
@@ -14693,7 +16379,7 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14693
16379
|
const lastReasoningTextRef = useRef6(null);
|
|
14694
16380
|
const lastStreamAssistantKeyRef = useRef6(null);
|
|
14695
16381
|
const pendingToolInvocationIdsRef = useRef6(/* @__PURE__ */ new Set());
|
|
14696
|
-
const appendExpandPreviewToHistory =
|
|
16382
|
+
const appendExpandPreviewToHistory = useCallback4(() => {
|
|
14697
16383
|
const p = peekLatestExpandable();
|
|
14698
16384
|
setHistory((prev) => {
|
|
14699
16385
|
const id = prev.length;
|
|
@@ -14702,7 +16388,7 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14702
16388
|
...prev,
|
|
14703
16389
|
{
|
|
14704
16390
|
id,
|
|
14705
|
-
component: /* @__PURE__ */
|
|
16391
|
+
component: /* @__PURE__ */ jsx27(ChatMeta, { children: "Ctrl+O: no truncated preview to expand" }, id)
|
|
14706
16392
|
}
|
|
14707
16393
|
];
|
|
14708
16394
|
}
|
|
@@ -14710,18 +16396,18 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14710
16396
|
...prev,
|
|
14711
16397
|
{
|
|
14712
16398
|
id,
|
|
14713
|
-
component: /* @__PURE__ */
|
|
16399
|
+
component: /* @__PURE__ */ jsx27(ExpandedPreviewBlock, { data: p }, id)
|
|
14714
16400
|
}
|
|
14715
16401
|
];
|
|
14716
16402
|
});
|
|
14717
16403
|
}, []);
|
|
14718
|
-
|
|
16404
|
+
useEffect11(() => {
|
|
14719
16405
|
expandPreviewHotkeyBus.on("expand", appendExpandPreviewToHistory);
|
|
14720
16406
|
return () => {
|
|
14721
16407
|
expandPreviewHotkeyBus.off("expand", appendExpandPreviewToHistory);
|
|
14722
16408
|
};
|
|
14723
16409
|
}, [appendExpandPreviewToHistory]);
|
|
14724
|
-
|
|
16410
|
+
useEffect11(() => {
|
|
14725
16411
|
if (process.env.CI || blumaUpdateRegistryCheckStarted) return;
|
|
14726
16412
|
blumaUpdateRegistryCheckStarted = true;
|
|
14727
16413
|
void checkForUpdates().then((msg) => {
|
|
@@ -14732,25 +16418,25 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14732
16418
|
...prev,
|
|
14733
16419
|
{
|
|
14734
16420
|
id: nextId2,
|
|
14735
|
-
component: /* @__PURE__ */
|
|
16421
|
+
component: /* @__PURE__ */ jsx27(UpdateNotice_default, { message: msg })
|
|
14736
16422
|
}
|
|
14737
16423
|
];
|
|
14738
16424
|
});
|
|
14739
16425
|
});
|
|
14740
16426
|
}, []);
|
|
14741
|
-
|
|
16427
|
+
useEffect11(() => {
|
|
14742
16428
|
setHistory((prev) => {
|
|
14743
16429
|
const tail = prev.filter((h) => h.id !== HEADER_PANEL_HISTORY_ID);
|
|
14744
16430
|
return [
|
|
14745
16431
|
{
|
|
14746
16432
|
id: HEADER_PANEL_HISTORY_ID,
|
|
14747
|
-
component: /* @__PURE__ */
|
|
16433
|
+
component: /* @__PURE__ */ jsx27(Header, { sessionId, workdir, cliVersion })
|
|
14748
16434
|
},
|
|
14749
16435
|
...tail
|
|
14750
16436
|
];
|
|
14751
16437
|
});
|
|
14752
16438
|
}, [sessionId, workdir, cliVersion]);
|
|
14753
|
-
const handleInterrupt =
|
|
16439
|
+
const handleInterrupt = useCallback4(() => {
|
|
14754
16440
|
if (!isProcessing) return;
|
|
14755
16441
|
eventBus.emit("user_interrupt");
|
|
14756
16442
|
turnStartedAtRef.current = null;
|
|
@@ -14762,12 +16448,12 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14762
16448
|
...prev,
|
|
14763
16449
|
{
|
|
14764
16450
|
id,
|
|
14765
|
-
component: /* @__PURE__ */
|
|
16451
|
+
component: /* @__PURE__ */ jsx27(ChatMeta, { children: "cancelled (Esc)" })
|
|
14766
16452
|
}
|
|
14767
16453
|
];
|
|
14768
16454
|
});
|
|
14769
16455
|
}, [isProcessing, eventBus]);
|
|
14770
|
-
const handleSubmit =
|
|
16456
|
+
const handleSubmit = useCallback4(
|
|
14771
16457
|
(text) => {
|
|
14772
16458
|
if (!text || !agentInstance.current) return;
|
|
14773
16459
|
const trimmedForSlash = text.trim();
|
|
@@ -14779,7 +16465,7 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14779
16465
|
...prev,
|
|
14780
16466
|
{
|
|
14781
16467
|
id,
|
|
14782
|
-
component: /* @__PURE__ */
|
|
16468
|
+
component: /* @__PURE__ */ jsx27(ChatMeta, { children: "Slash command not recognized or incomplete. Type /help for the list." })
|
|
14783
16469
|
}
|
|
14784
16470
|
];
|
|
14785
16471
|
});
|
|
@@ -14797,7 +16483,7 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14797
16483
|
...prev,
|
|
14798
16484
|
{
|
|
14799
16485
|
id,
|
|
14800
|
-
component: /* @__PURE__ */
|
|
16486
|
+
component: /* @__PURE__ */ jsx27(ChatMeta, { children: "Usage: /img ./screenshot.png \u2014 optional text after the path is sent too" })
|
|
14801
16487
|
}
|
|
14802
16488
|
];
|
|
14803
16489
|
});
|
|
@@ -14816,7 +16502,7 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14816
16502
|
...prev,
|
|
14817
16503
|
{
|
|
14818
16504
|
id,
|
|
14819
|
-
component: /* @__PURE__ */
|
|
16505
|
+
component: /* @__PURE__ */ jsx27(UserMessageWithOptionalImages, { raw: payload, variant: "slash-img" })
|
|
14820
16506
|
}
|
|
14821
16507
|
];
|
|
14822
16508
|
});
|
|
@@ -14849,11 +16535,11 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14849
16535
|
...prev,
|
|
14850
16536
|
{
|
|
14851
16537
|
id: firstId,
|
|
14852
|
-
component: /* @__PURE__ */
|
|
16538
|
+
component: /* @__PURE__ */ jsx27(ChatUserMessage, { children: /* @__PURE__ */ jsx27(Text25, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, wrap: "wrap", children: text }) })
|
|
14853
16539
|
},
|
|
14854
16540
|
{
|
|
14855
16541
|
id: secondId,
|
|
14856
|
-
component: /* @__PURE__ */
|
|
16542
|
+
component: /* @__PURE__ */ jsx27(
|
|
14857
16543
|
SlashCommands_default,
|
|
14858
16544
|
{
|
|
14859
16545
|
input: text,
|
|
@@ -14885,7 +16571,7 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14885
16571
|
...prev,
|
|
14886
16572
|
{
|
|
14887
16573
|
id,
|
|
14888
|
-
component: /* @__PURE__ */
|
|
16574
|
+
component: /* @__PURE__ */ jsx27(ChatUserMessage, { children: /* @__PURE__ */ jsxs24(Text25, { bold: true, color: "white", children: [
|
|
14889
16575
|
"$ !",
|
|
14890
16576
|
command
|
|
14891
16577
|
] }) })
|
|
@@ -14911,7 +16597,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
14911
16597
|
...prev,
|
|
14912
16598
|
{
|
|
14913
16599
|
id,
|
|
14914
|
-
component: /* @__PURE__ */
|
|
16600
|
+
component: /* @__PURE__ */ jsxs24(Text25, { color: "red", children: [
|
|
14915
16601
|
"Failed to execute: ",
|
|
14916
16602
|
result.error || result.message
|
|
14917
16603
|
] })
|
|
@@ -14929,7 +16615,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
14929
16615
|
...prev,
|
|
14930
16616
|
{
|
|
14931
16617
|
id,
|
|
14932
|
-
component: /* @__PURE__ */
|
|
16618
|
+
component: /* @__PURE__ */ jsxs24(Text25, { color: "red", children: [
|
|
14933
16619
|
"Error: ",
|
|
14934
16620
|
err.message
|
|
14935
16621
|
] })
|
|
@@ -14949,7 +16635,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
14949
16635
|
...prev,
|
|
14950
16636
|
{
|
|
14951
16637
|
id,
|
|
14952
|
-
component: /* @__PURE__ */
|
|
16638
|
+
component: /* @__PURE__ */ jsx27(UserMessageWithOptionalImages, { raw: text, variant: "plain" })
|
|
14953
16639
|
}
|
|
14954
16640
|
];
|
|
14955
16641
|
});
|
|
@@ -14957,7 +16643,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
14957
16643
|
},
|
|
14958
16644
|
[isProcessing, markTurnStarted]
|
|
14959
16645
|
);
|
|
14960
|
-
const handleConfirmation =
|
|
16646
|
+
const handleConfirmation = useCallback4(
|
|
14961
16647
|
(decision, toolCalls, opts) => {
|
|
14962
16648
|
if (!agentInstance.current) return;
|
|
14963
16649
|
setPendingConfirmation(null);
|
|
@@ -14979,7 +16665,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
14979
16665
|
},
|
|
14980
16666
|
[]
|
|
14981
16667
|
);
|
|
14982
|
-
const appendStreamedReasoning =
|
|
16668
|
+
const appendStreamedReasoning = useCallback4((reasoning) => {
|
|
14983
16669
|
const r = String(reasoning ?? "").trim();
|
|
14984
16670
|
const key = reasoningDedupeKey(r);
|
|
14985
16671
|
if (!r || key === lastReasoningTextRef.current) return;
|
|
@@ -14990,12 +16676,12 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
14990
16676
|
...prev,
|
|
14991
16677
|
{
|
|
14992
16678
|
id,
|
|
14993
|
-
component: /* @__PURE__ */
|
|
16679
|
+
component: /* @__PURE__ */ jsx27(ReasoningDisplay, { reasoning })
|
|
14994
16680
|
}
|
|
14995
16681
|
];
|
|
14996
16682
|
});
|
|
14997
16683
|
}, []);
|
|
14998
|
-
const appendStreamedAssistant =
|
|
16684
|
+
const appendStreamedAssistant = useCallback4((content) => {
|
|
14999
16685
|
const t = String(content ?? "").trim();
|
|
15000
16686
|
if (!t) return;
|
|
15001
16687
|
const key = reasoningDedupeKey(t);
|
|
@@ -15006,12 +16692,12 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15006
16692
|
...prev,
|
|
15007
16693
|
{
|
|
15008
16694
|
id,
|
|
15009
|
-
component: /* @__PURE__ */
|
|
16695
|
+
component: /* @__PURE__ */ jsx27(AssistantMessageDisplay, { content })
|
|
15010
16696
|
}
|
|
15011
16697
|
];
|
|
15012
16698
|
});
|
|
15013
16699
|
}, []);
|
|
15014
|
-
|
|
16700
|
+
useEffect11(() => {
|
|
15015
16701
|
if (history.length >= HISTORY_EMERGENCY_LIMIT) {
|
|
15016
16702
|
setHistory((prev) => {
|
|
15017
16703
|
const header = prev.find((h) => h.id === HEADER_PANEL_HISTORY_ID);
|
|
@@ -15021,7 +16707,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15021
16707
|
});
|
|
15022
16708
|
}
|
|
15023
16709
|
}, [history.length]);
|
|
15024
|
-
|
|
16710
|
+
useEffect11(() => {
|
|
15025
16711
|
const initializeAgent = async () => {
|
|
15026
16712
|
try {
|
|
15027
16713
|
agentInstance.current = new Agent(sessionId, eventBus);
|
|
@@ -15058,7 +16744,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15058
16744
|
...prev,
|
|
15059
16745
|
{
|
|
15060
16746
|
id,
|
|
15061
|
-
component: /* @__PURE__ */
|
|
16747
|
+
component: /* @__PURE__ */ jsx27(ChatTurnDuration, { durationMs: ms })
|
|
15062
16748
|
}
|
|
15063
16749
|
];
|
|
15064
16750
|
});
|
|
@@ -15080,6 +16766,31 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15080
16766
|
});
|
|
15081
16767
|
return;
|
|
15082
16768
|
}
|
|
16769
|
+
if (parsed.type === "tool_stream_start") {
|
|
16770
|
+
const toolName = String(parsed.toolName || "");
|
|
16771
|
+
const argsRaw = parsed.argsRaw;
|
|
16772
|
+
let args = void 0;
|
|
16773
|
+
if (typeof argsRaw === "string") {
|
|
16774
|
+
try {
|
|
16775
|
+
args = JSON.parse(argsRaw);
|
|
16776
|
+
} catch {
|
|
16777
|
+
args = argsRaw;
|
|
16778
|
+
}
|
|
16779
|
+
} else {
|
|
16780
|
+
args = argsRaw;
|
|
16781
|
+
}
|
|
16782
|
+
if (toolName) {
|
|
16783
|
+
setLiveToolName(toolName);
|
|
16784
|
+
setLiveToolArgs(args);
|
|
16785
|
+
}
|
|
16786
|
+
return;
|
|
16787
|
+
}
|
|
16788
|
+
if (parsed.type === "reasoning") {
|
|
16789
|
+
setIsReasoning(true);
|
|
16790
|
+
}
|
|
16791
|
+
if (parsed.type === "stream_end") {
|
|
16792
|
+
setIsReasoning(false);
|
|
16793
|
+
}
|
|
15083
16794
|
if (parsed.type === "ask_user_question") {
|
|
15084
16795
|
const qs = parsed.questions;
|
|
15085
16796
|
if (Array.isArray(qs) && qs.length > 0) {
|
|
@@ -15143,14 +16854,14 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15143
16854
|
}
|
|
15144
16855
|
let newComponent = null;
|
|
15145
16856
|
if (parsed.type === "debug") {
|
|
15146
|
-
newComponent = /* @__PURE__ */
|
|
16857
|
+
newComponent = /* @__PURE__ */ jsx27(ChatMeta, { children: parsed.message });
|
|
15147
16858
|
} else if (parsed.type === "protocol_violation") {
|
|
15148
|
-
newComponent = /* @__PURE__ */
|
|
15149
|
-
/* @__PURE__ */
|
|
15150
|
-
/* @__PURE__ */
|
|
16859
|
+
newComponent = /* @__PURE__ */ jsx27(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs24(Box26, { flexDirection: "column", paddingLeft: 2, children: [
|
|
16860
|
+
/* @__PURE__ */ jsx27(Text25, { dimColor: true, children: parsed.content }),
|
|
16861
|
+
/* @__PURE__ */ jsx27(Text25, { dimColor: true, children: parsed.message })
|
|
15151
16862
|
] }) });
|
|
15152
16863
|
} else if (parsed.type === "error") {
|
|
15153
|
-
newComponent = /* @__PURE__ */
|
|
16864
|
+
newComponent = /* @__PURE__ */ jsx27(
|
|
15154
16865
|
ErrorMessage_default,
|
|
15155
16866
|
{
|
|
15156
16867
|
message: parsed.message,
|
|
@@ -15168,6 +16879,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15168
16879
|
});
|
|
15169
16880
|
if (parsed.tool_name) {
|
|
15170
16881
|
setLiveToolName(String(parsed.tool_name));
|
|
16882
|
+
setLiveToolArgs(parsed.arguments);
|
|
15171
16883
|
}
|
|
15172
16884
|
const tn = String(parsed.tool_name || "");
|
|
15173
16885
|
if (isToolInvocationRowVisible(tn)) {
|
|
@@ -15176,7 +16888,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15176
16888
|
parsed.tool_call_id
|
|
15177
16889
|
);
|
|
15178
16890
|
}
|
|
15179
|
-
newComponent = isToolInvocationRowVisible(tn) ? /* @__PURE__ */
|
|
16891
|
+
newComponent = isToolInvocationRowVisible(tn) ? /* @__PURE__ */ jsx27(
|
|
15180
16892
|
ToolInvocationBlock,
|
|
15181
16893
|
{
|
|
15182
16894
|
toolName: tn,
|
|
@@ -15197,7 +16909,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15197
16909
|
pendingToolInvocationIdsRef.current,
|
|
15198
16910
|
parsed.tool_call_id
|
|
15199
16911
|
);
|
|
15200
|
-
newComponent = /* @__PURE__ */
|
|
16912
|
+
newComponent = /* @__PURE__ */ jsx27(
|
|
15201
16913
|
ToolResultDisplay,
|
|
15202
16914
|
{
|
|
15203
16915
|
toolName: parsed.tool_name,
|
|
@@ -15208,7 +16920,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15208
16920
|
}
|
|
15209
16921
|
);
|
|
15210
16922
|
} else if (parsed.type === "user_overlay") {
|
|
15211
|
-
newComponent = /* @__PURE__ */
|
|
16923
|
+
newComponent = /* @__PURE__ */ jsx27(ChatUserMessage, { children: /* @__PURE__ */ jsx27(Text25, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, wrap: "wrap", children: parsed.payload }) });
|
|
15212
16924
|
} else if (parsed.type === "reasoning") {
|
|
15213
16925
|
const r = String(parsed.content ?? "").trim();
|
|
15214
16926
|
const key = reasoningDedupeKey(r);
|
|
@@ -15216,10 +16928,10 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15216
16928
|
newComponent = null;
|
|
15217
16929
|
} else {
|
|
15218
16930
|
if (r) lastReasoningTextRef.current = key;
|
|
15219
|
-
newComponent = /* @__PURE__ */
|
|
16931
|
+
newComponent = /* @__PURE__ */ jsx27(ReasoningDisplay, { reasoning: String(parsed.content ?? "") });
|
|
15220
16932
|
}
|
|
15221
16933
|
} else if (parsed.type === "log") {
|
|
15222
|
-
newComponent = /* @__PURE__ */
|
|
16934
|
+
newComponent = /* @__PURE__ */ jsxs24(ChatMeta, { children: [
|
|
15223
16935
|
parsed.message,
|
|
15224
16936
|
parsed.payload ? `: ${parsed.payload}` : ""
|
|
15225
16937
|
] });
|
|
@@ -15230,7 +16942,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15230
16942
|
newComponent = null;
|
|
15231
16943
|
} else {
|
|
15232
16944
|
lastStreamAssistantKeyRef.current = key || null;
|
|
15233
|
-
newComponent = /* @__PURE__ */
|
|
16945
|
+
newComponent = /* @__PURE__ */ jsx27(AssistantMessageDisplay, { content: body });
|
|
15234
16946
|
}
|
|
15235
16947
|
}
|
|
15236
16948
|
if (newComponent) {
|
|
@@ -15248,7 +16960,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15248
16960
|
const ms = Date.now() - t;
|
|
15249
16961
|
next.push({
|
|
15250
16962
|
id: nextHistoryId(next),
|
|
15251
|
-
component: /* @__PURE__ */
|
|
16963
|
+
component: /* @__PURE__ */ jsx27(ChatTurnDuration, { durationMs: ms })
|
|
15252
16964
|
});
|
|
15253
16965
|
}
|
|
15254
16966
|
}
|
|
@@ -15266,7 +16978,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15266
16978
|
if (!msg) return;
|
|
15267
16979
|
setHistory((prev) => {
|
|
15268
16980
|
const id = nextHistoryId(prev);
|
|
15269
|
-
return [...prev, { id, component: /* @__PURE__ */
|
|
16981
|
+
return [...prev, { id, component: /* @__PURE__ */ jsx27(ChatMeta, { children: msg }) }];
|
|
15270
16982
|
});
|
|
15271
16983
|
};
|
|
15272
16984
|
uiEventBus.on("user_overlay", handleUiOverlay);
|
|
@@ -15281,7 +16993,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15281
16993
|
}, [eventBus, sessionId, handleConfirmation]);
|
|
15282
16994
|
const renderInteractiveComponent = () => {
|
|
15283
16995
|
if (mcpStatus !== "connected") {
|
|
15284
|
-
return /* @__PURE__ */
|
|
16996
|
+
return /* @__PURE__ */ jsx27(
|
|
15285
16997
|
SessionInfoConnectingMCP_default,
|
|
15286
16998
|
{
|
|
15287
16999
|
workdir,
|
|
@@ -15289,8 +17001,8 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15289
17001
|
}
|
|
15290
17002
|
);
|
|
15291
17003
|
}
|
|
15292
|
-
return /* @__PURE__ */
|
|
15293
|
-
pendingAskUserQuestions && pendingAskUserQuestions.length > 0 ? /* @__PURE__ */
|
|
17004
|
+
return /* @__PURE__ */ jsxs24(Box26, { flexDirection: "column", children: [
|
|
17005
|
+
pendingAskUserQuestions && pendingAskUserQuestions.length > 0 ? /* @__PURE__ */ jsx27(
|
|
15294
17006
|
AskUserQuestionPrompt,
|
|
15295
17007
|
{
|
|
15296
17008
|
questions: pendingAskUserQuestions,
|
|
@@ -15306,7 +17018,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15306
17018
|
}
|
|
15307
17019
|
}
|
|
15308
17020
|
) : null,
|
|
15309
|
-
pendingConfirmation ? /* @__PURE__ */
|
|
17021
|
+
pendingConfirmation ? /* @__PURE__ */ jsx27(
|
|
15310
17022
|
ConfirmationPrompt,
|
|
15311
17023
|
{
|
|
15312
17024
|
toolCalls: pendingConfirmation,
|
|
@@ -15319,8 +17031,17 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15319
17031
|
}
|
|
15320
17032
|
}
|
|
15321
17033
|
) : null,
|
|
15322
|
-
isProcessing && !pendingConfirmation && !pendingAskUserQuestions && /* @__PURE__ */
|
|
15323
|
-
|
|
17034
|
+
isProcessing && !pendingConfirmation && !pendingAskUserQuestions && /* @__PURE__ */ jsx27(
|
|
17035
|
+
WorkingTimer,
|
|
17036
|
+
{
|
|
17037
|
+
eventBus,
|
|
17038
|
+
startedAtMs: processingStartMs,
|
|
17039
|
+
liveToolName,
|
|
17040
|
+
liveToolArgs,
|
|
17041
|
+
isReasoning
|
|
17042
|
+
}
|
|
17043
|
+
),
|
|
17044
|
+
/* @__PURE__ */ jsx27(
|
|
15324
17045
|
InputPrompt,
|
|
15325
17046
|
{
|
|
15326
17047
|
onSubmit: handleSubmit,
|
|
@@ -15332,10 +17053,10 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15332
17053
|
)
|
|
15333
17054
|
] });
|
|
15334
17055
|
};
|
|
15335
|
-
return /* @__PURE__ */
|
|
15336
|
-
/* @__PURE__ */
|
|
15337
|
-
liveToolName ? /* @__PURE__ */
|
|
15338
|
-
/* @__PURE__ */
|
|
17056
|
+
return /* @__PURE__ */ jsxs24(Box26, { flexDirection: "column", children: [
|
|
17057
|
+
/* @__PURE__ */ jsx27(Static, { items: history, children: (item) => /* @__PURE__ */ jsx27(Box26, { children: item.component }, item.id) }),
|
|
17058
|
+
liveToolName ? /* @__PURE__ */ jsx27(Box26, { paddingLeft: 2, marginBottom: 0, children: /* @__PURE__ */ jsxs24(Text25, { dimColor: true, children: [
|
|
17059
|
+
/* @__PURE__ */ jsxs24(Text25, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: [
|
|
15339
17060
|
"*",
|
|
15340
17061
|
" "
|
|
15341
17062
|
] }),
|
|
@@ -15343,7 +17064,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15343
17064
|
liveToolName,
|
|
15344
17065
|
"\u2026"
|
|
15345
17066
|
] }) }) : null,
|
|
15346
|
-
/* @__PURE__ */
|
|
17067
|
+
/* @__PURE__ */ jsx27(
|
|
15347
17068
|
StreamingText,
|
|
15348
17069
|
{
|
|
15349
17070
|
eventBus,
|
|
@@ -15351,7 +17072,19 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
15351
17072
|
onAssistantContentComplete: appendStreamedAssistant
|
|
15352
17073
|
}
|
|
15353
17074
|
),
|
|
15354
|
-
renderInteractiveComponent()
|
|
17075
|
+
renderInteractiveComponent(),
|
|
17076
|
+
showWorkers && /* @__PURE__ */ jsx27(
|
|
17077
|
+
WorkerOverlay,
|
|
17078
|
+
{
|
|
17079
|
+
visible: showWorkers,
|
|
17080
|
+
sessionId,
|
|
17081
|
+
onClose: () => {
|
|
17082
|
+
setShowWorkers(false);
|
|
17083
|
+
setZoomedWorkerSession(null);
|
|
17084
|
+
},
|
|
17085
|
+
onZoomWorker: setZoomedWorkerSession
|
|
17086
|
+
}
|
|
17087
|
+
)
|
|
15355
17088
|
] });
|
|
15356
17089
|
};
|
|
15357
17090
|
var App = memo15(AppComponent);
|
|
@@ -15413,6 +17146,7 @@ function stopTitleKeeper() {
|
|
|
15413
17146
|
}
|
|
15414
17147
|
|
|
15415
17148
|
// src/main.ts
|
|
17149
|
+
init_session_registry();
|
|
15416
17150
|
function extractUserMessage(envelope) {
|
|
15417
17151
|
const c = envelope.context;
|
|
15418
17152
|
if (c && typeof c === "object" && typeof c.user_request === "string") {
|
|
@@ -15477,9 +17211,9 @@ async function runAgentMode() {
|
|
|
15477
17211
|
try {
|
|
15478
17212
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
15479
17213
|
const filePath = args[inputFileIndex + 1];
|
|
15480
|
-
rawPayload =
|
|
17214
|
+
rawPayload = fs29.readFileSync(filePath, "utf-8");
|
|
15481
17215
|
} else {
|
|
15482
|
-
rawPayload =
|
|
17216
|
+
rawPayload = fs29.readFileSync(0, "utf-8");
|
|
15483
17217
|
}
|
|
15484
17218
|
} catch (err) {
|
|
15485
17219
|
writeAgentEvent(registrySessionId, {
|
|
@@ -15517,7 +17251,7 @@ async function runAgentMode() {
|
|
|
15517
17251
|
}
|
|
15518
17252
|
}
|
|
15519
17253
|
const eventBus = new EventEmitter3();
|
|
15520
|
-
const sessionId = registrySessionId || envelope.session_id || envelope.message_id ||
|
|
17254
|
+
const sessionId = registrySessionId || envelope.session_id || envelope.message_id || uuidv48();
|
|
15521
17255
|
process.env.BLUMA_SESSION_ID = sessionId;
|
|
15522
17256
|
installSessionLifecycle(sessionId);
|
|
15523
17257
|
const uc = envelope.user_context;
|
|
@@ -15628,7 +17362,9 @@ async function runAgentMode() {
|
|
|
15628
17362
|
const agent = new Agent(sessionId, eventBus);
|
|
15629
17363
|
agentRef = agent;
|
|
15630
17364
|
await agent.initialize();
|
|
15631
|
-
const
|
|
17365
|
+
const userRequest = envelope.context?.user_request || envelope.context?.userRequest || "";
|
|
17366
|
+
const coordinatorContext = envelope.context?.coordinator_context || envelope.context?.coordinatorContext || "";
|
|
17367
|
+
const userContent = userRequest ? `${userRequest}${coordinatorContext ? "\n\nContexto adicional:\n" + JSON.stringify(coordinatorContext, null, 2) : ""}` : JSON.stringify({
|
|
15632
17368
|
message_id: envelope.message_id || sessionId,
|
|
15633
17369
|
from_agent: envelope.from_agent || "unknown",
|
|
15634
17370
|
to_agent: envelope.to_agent || "bluma",
|
|
@@ -15675,9 +17411,9 @@ async function runAgentMode() {
|
|
|
15675
17411
|
}
|
|
15676
17412
|
function readCliPackageVersion() {
|
|
15677
17413
|
try {
|
|
15678
|
-
const base =
|
|
15679
|
-
const pkgPath =
|
|
15680
|
-
const j = JSON.parse(
|
|
17414
|
+
const base = path34.dirname(fileURLToPath6(import.meta.url));
|
|
17415
|
+
const pkgPath = path34.join(base, "..", "package.json");
|
|
17416
|
+
const j = JSON.parse(fs29.readFileSync(pkgPath, "utf8"));
|
|
15681
17417
|
return String(j.version || "0.0.0");
|
|
15682
17418
|
} catch {
|
|
15683
17419
|
return "0.0.0";
|
|
@@ -15687,7 +17423,7 @@ function runCliMode(sessionId) {
|
|
|
15687
17423
|
const BLUMA_TITLE = "bluma";
|
|
15688
17424
|
startTitleKeeper(BLUMA_TITLE);
|
|
15689
17425
|
const eventBus = new EventEmitter3();
|
|
15690
|
-
const resolvedSessionId = sessionId ||
|
|
17426
|
+
const resolvedSessionId = sessionId || uuidv48();
|
|
15691
17427
|
process.env.BLUMA_SESSION_ID = resolvedSessionId;
|
|
15692
17428
|
registerSession({
|
|
15693
17429
|
sessionId: resolvedSessionId,
|
|
@@ -15705,7 +17441,7 @@ function runCliMode(sessionId) {
|
|
|
15705
17441
|
sessionId: resolvedSessionId,
|
|
15706
17442
|
cliVersion: readCliPackageVersion()
|
|
15707
17443
|
};
|
|
15708
|
-
const instance = render(
|
|
17444
|
+
const instance = render(React19.createElement(App_default, props));
|
|
15709
17445
|
void instance.waitUntilExit().then(() => {
|
|
15710
17446
|
finalizeSession(resolvedSessionId, "completed", { finishedBy: "interactive-exit" });
|
|
15711
17447
|
});
|
|
@@ -15800,9 +17536,9 @@ function startBackgroundAgent() {
|
|
|
15800
17536
|
process.exit(1);
|
|
15801
17537
|
}
|
|
15802
17538
|
const filePath = args[inputFileIndex + 1];
|
|
15803
|
-
const rawPayload =
|
|
17539
|
+
const rawPayload = fs29.readFileSync(filePath, "utf-8");
|
|
15804
17540
|
const envelope = JSON.parse(rawPayload);
|
|
15805
|
-
const sessionId = envelope.session_id || envelope.message_id ||
|
|
17541
|
+
const sessionId = envelope.session_id || envelope.message_id || uuidv48();
|
|
15806
17542
|
registerSession({
|
|
15807
17543
|
sessionId,
|
|
15808
17544
|
kind: "agent",
|