@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.
Files changed (2) hide show
  1. package/dist/main.js +2405 -669
  2. 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 React16 from "react";
1182
+ import React19 from "react";
501
1183
  import { render } from "ink";
502
1184
  import { EventEmitter as EventEmitter3 } from "events";
503
- import fs27 from "fs";
504
- import path32 from "path";
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 uuidv47 } from "uuid";
1189
+ import { v4 as uuidv48 } from "uuid";
508
1190
 
509
1191
  // src/app/ui/App.tsx
510
- import { useState as useState8, useEffect as useEffect8, useRef as useRef6, useCallback as useCallback3, memo as memo15 } from "react";
511
- import { Box as Box23, Text as Text22, Static } from "ink";
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 path33 = filePath.trim() || "unknown file";
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: path33 })
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 path33 = parsed.path || ".";
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
- path33
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: "baseline", children: [
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 path30 from "path";
3652
- import os19 from "os";
4333
+ import path32 from "path";
4334
+ import os21 from "os";
3653
4335
 
3654
4336
  // src/app/agent/tool_invoker.ts
3655
- import { promises as fs17 } from "fs";
3656
- import path19 from "path";
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 homeDir = os7.homedir();
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 os8 from "os";
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(os8.homedir(), ".bluma", "coding_memory.json");
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(os8.homedir(), ".bluma", "coding_memory.json");
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
- async function waitAgent(args) {
7109
- const sessionId = args?.session_id;
7110
- if (!sessionId || typeof sessionId !== "string") {
7111
- return { success: false, error: "session_id is required" };
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
- const timeoutMs = Math.max(1e3, Number(args.timeout_ms || 3e4));
7114
- const pollIntervalMs = Math.max(200, Number(args.poll_interval_ms || 1e3));
7115
- const deadline = Date.now() + timeoutMs;
7116
- while (Date.now() < deadline) {
7117
- const session2 = getSession(sessionId);
7118
- if (!session2) {
7119
- return { success: false, error: `Unknown session: ${sessionId}` };
7120
- }
7121
- if (session2.status !== "running") {
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: true,
7124
- completed: true,
7125
- session: toAgentSummary(session2),
7126
- result: extractLatestResult(sessionId)
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
- completed: false,
7138
- session: toAgentSummary(session),
7139
- result: extractLatestResult(sessionId),
7140
- message: `Timed out after ${timeoutMs}ms while waiting for agent ${sessionId}.`
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 listAgents(args = {}) {
7144
- const entries = listSessions().filter((entry) => entry.kind === "agent").filter((entry) => {
7145
- if (args.parent_session_id) {
7146
- return entry.metadata?.parent_session_id === args.parent_session_id;
7147
- }
7148
- return true;
7149
- }).filter((entry) => {
7150
- if (args.status) {
7151
- return entry.status === args.status;
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
- return true;
7154
- }).map(toAgentSummary);
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
- count: entries.length,
7158
- agents: entries
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 = path19.dirname(__filename);
7633
- const configPath = path19.resolve(__dirname, "config", "native_tools.json");
7634
- const fileContent = await fs17.readFile(configPath, "utf-8");
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 fs18 } from "fs";
7677
- import path20 from "path";
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 = path20.dirname(__filename);
7706
- const defaultConfigPath = path20.resolve(__dirname, "config", "bluma-mcp.json");
7707
- const userConfigPath = path20.join(os11.homedir(), ".bluma", "bluma-mcp.json");
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 fs18.readFile(configPath, "utf-8");
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 path28 from "path";
7913
- import { v4 as uuidv44 } from "uuid";
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 path21 from "path";
8541
+ import path22 from "path";
7917
8542
  import os12 from "os";
7918
- import { promises as fs19 } from "fs";
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 path21.join(os12.homedir(), p.slice(1));
8579
+ return path22.join(os12.homedir(), p.slice(1));
7955
8580
  }
7956
8581
  return p;
7957
8582
  }
7958
8583
  function getPreferredAppDir() {
7959
- const fixed = path21.join(os12.homedir(), ".bluma");
7960
- return path21.resolve(expandHome(fixed));
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 = path21.dirname(dest);
7969
- await fs19.mkdir(dir, { recursive: true }).catch(() => {
8593
+ const dir = path22.dirname(dest);
8594
+ await fs20.mkdir(dir, { recursive: true }).catch(() => {
7970
8595
  });
7971
- await fs19.rename(src, dest);
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 fs19.access(src);
7985
- const data = await fs19.readFile(src);
7986
- const dir = path21.dirname(dest);
7987
- await fs19.mkdir(dir, { recursive: true }).catch(() => {
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 fs19.writeFile(dest, data);
7990
- await fs19.unlink(src).catch(() => {
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 = path21.join(appDir, "sessions");
8004
- await fs19.mkdir(sessionDir, { recursive: true });
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 = path21.join(sessionDir, `${sessionId}.json`);
8634
+ const sessionFile = path22.join(sessionDir, `${sessionId}.json`);
8010
8635
  try {
8011
- await fs19.access(sessionFile);
8012
- const fileContent = await fs19.readFile(sessionFile, "utf-8");
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 fs19.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
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 = path21.dirname(sessionFile);
8038
- await fs19.mkdir(dir, { recursive: true });
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 fs19.readFile(sessionFile, "utf-8");
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 = path21.basename(sessionFile, ".json");
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 fs19.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
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 fs19.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
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 fs19.unlink(tempSessionFile);
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 os15 from "os";
8103
- import fs23 from "fs";
8104
- import path25 from "path";
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 fs20 from "fs";
8109
- import path22 from "path";
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 = path22.join(projectRoot, ".bluma", "skills");
8120
- this.globalSkillsDir = path22.join(os13.homedir(), ".bluma", "skills");
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 path22.join(process.cwd(), "dist", "config", "skills");
8754
+ return path23.join(process.cwd(), "dist", "config", "skills");
8130
8755
  }
8131
8756
  const candidates = [];
8132
8757
  const push = (p) => {
8133
- const abs = path22.resolve(p);
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 = path22.dirname(fileURLToPath4(import.meta.url));
8141
- push(path22.join(bundleDir, "config", "skills"));
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 (path22.isAbsolute(argv1) && fs20.existsSync(argv1)) {
8149
- resolved = fs20.realpathSync(argv1);
8150
- } else if (!path22.isAbsolute(argv1)) {
8151
- resolved = path22.resolve(process.cwd(), argv1);
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 = path22.dirname(resolved);
8154
- argvBundled = path22.join(scriptDir, "config", "skills");
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 (fs20.existsSync(abs)) {
8785
+ if (fs21.existsSync(abs)) {
8161
8786
  return abs;
8162
8787
  }
8163
8788
  }
8164
8789
  try {
8165
- return path22.join(path22.dirname(fileURLToPath4(import.meta.url)), "config", "skills");
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 path22.join(os13.homedir(), ".bluma", "__bundled_skills_unresolved__");
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: path22.join(dir, skill.name, "SKILL.md"),
8200
- bundledPath: path22.join(this.bundledSkillsDir, skill.name, "SKILL.md")
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 (!fs20.existsSync(dir)) return [];
8833
+ if (!fs21.existsSync(dir)) return [];
8209
8834
  try {
8210
- return fs20.readdirSync(dir).filter((d) => {
8211
- const fullPath = path22.join(dir, d);
8212
- return fs20.statSync(fullPath).isDirectory() && fs20.existsSync(path22.join(fullPath, "SKILL.md"));
8213
- }).map((d) => this.loadMetadataFromPath(path22.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
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 (!fs20.existsSync(skillPath)) return null;
8844
+ if (!fs21.existsSync(skillPath)) return null;
8220
8845
  try {
8221
- const raw = fs20.readFileSync(skillPath, "utf-8");
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 = path22.join(this.bundledSkillsDir, name, "SKILL.md");
8244
- const projectPath = path22.join(this.projectSkillsDir, name, "SKILL.md");
8245
- const globalPath = path22.join(this.globalSkillsDir, name, "SKILL.md");
8246
- const existsBundled = fs20.existsSync(bundledPath);
8247
- const existsProject = fs20.existsSync(projectPath);
8248
- const existsGlobal = fs20.existsSync(globalPath);
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 = fs20.readFileSync(skillPath, "utf-8");
8912
+ const raw = fs21.readFileSync(skillPath, "utf-8");
8288
8913
  const parsed = this.parseFrontmatter(raw);
8289
- const skillDir = path22.dirname(skillPath);
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(path22.join(skillDir, "references")),
8299
- scripts: this.scanAssets(path22.join(skillDir, "scripts"))
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 (!fs20.existsSync(dir)) return [];
8931
+ if (!fs21.existsSync(dir)) return [];
8307
8932
  try {
8308
- return fs20.readdirSync(dir).filter((f) => {
8309
- const fp = path22.join(dir, f);
8310
- return fs20.statSync(fp).isFile();
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: path22.resolve(dir, f)
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 = path22.join(this.bundledSkillsDir, name, "SKILL.md");
8371
- const projectPath = path22.join(this.projectSkillsDir, name, "SKILL.md");
8372
- const globalPath = path22.join(this.globalSkillsDir, name, "SKILL.md");
8373
- return fs20.existsSync(bundledPath) || fs20.existsSync(projectPath) || fs20.existsSync(globalPath);
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 fs21 from "fs";
8406
- import path23 from "path";
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 (!fs21.existsSync(filePath)) return null;
8422
- const st = fs21.statSync(filePath);
9046
+ if (!fs22.existsSync(filePath)) return null;
9047
+ const st = fs22.statSync(filePath);
8423
9048
  if (!st.isFile()) return null;
8424
- const raw = fs21.readFileSync(filePath, "utf8");
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(path23.join(cwd, name), LIMITS.readme);
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
- path23.join(cwd, "BluMa.md"),
8444
- path23.join(cwd, "BLUMA.md"),
8445
- path23.join(cwd, ".bluma", "BluMa.md")
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 = path23.relative(cwd, p) || p;
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 = path23.join(cwd, "package.json");
9083
+ const p = path24.join(cwd, "package.json");
8459
9084
  try {
8460
- if (!fs21.existsSync(p)) return null;
8461
- const pkg = JSON.parse(fs21.readFileSync(p, "utf8"));
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 = fs21.readdirSync(cwd, { withFileTypes: true });
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(path23.join(cwd, "pyproject.toml"), LIMITS.pyproject);
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(path23.join(cwd, "CONTRIBUTING.md"), LIMITS.contributing);
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(path23.join(cwd, "CHANGELOG.md"), LIMITS.changelog);
9201
+ const chlog = safeReadFile(path24.join(cwd, "CHANGELOG.md"), LIMITS.changelog);
8577
9202
  if (!chlog) {
8578
- const alt = safeReadFile(path23.join(cwd, "CHANGES.md"), LIMITS.changelog);
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 fs22 from "fs";
9243
+ import fs23 from "fs";
8619
9244
  import os14 from "os";
8620
- import path24 from "path";
9245
+ import path25 from "path";
8621
9246
  function getProjectPluginsDir() {
8622
9247
  const policy = getSandboxPolicy();
8623
- return path24.join(policy.workspaceRoot, ".bluma", "plugins");
9248
+ return path25.join(policy.workspaceRoot, ".bluma", "plugins");
8624
9249
  }
8625
9250
  function getGlobalPluginsDir() {
8626
- return path24.join(process.env.HOME || os14.homedir(), ".bluma", "plugins");
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 (!fs22.existsSync(manifestPath)) {
9260
+ if (!fs23.existsSync(manifestPath)) {
8636
9261
  return null;
8637
9262
  }
8638
9263
  try {
8639
- const parsed = JSON.parse(fs22.readFileSync(manifestPath, "utf-8"));
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
- path24.join(pluginDir, ".codex-plugin", "plugin.json"),
8653
- path24.join(pluginDir, "plugin.json")
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 (fs22.existsSync(candidate)) {
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 (!fs22.existsSync(baseDir)) {
9288
+ if (!fs23.existsSync(baseDir)) {
8664
9289
  return [];
8665
9290
  }
8666
- return fs22.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
8667
- const pluginDir = path24.join(baseDir, entry.name);
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
- - You do **not** receive XML or push notifications from workers \u2014 completion and payload come from \`wait_agent\` (and \`result\` inside that JSON when present).
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 enableCoordinatorMode() {
8904
- process.env.BLUMA_COORDINATOR_MODE = "true";
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 disableCoordinatorMode() {
8907
- delete process.env.BLUMA_COORDINATOR_MODE;
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 (fs23.existsSync(path25.join(dir, "pnpm-lock.yaml"))) return "pnpm";
8944
- if (fs23.existsSync(path25.join(dir, "yarn.lock"))) return "yarn";
8945
- if (fs23.existsSync(path25.join(dir, "bun.lockb"))) return "bun";
8946
- if (fs23.existsSync(path25.join(dir, "package-lock.json"))) return "npm";
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 = fs23.readdirSync(dir);
9891
+ const files = fs25.readdirSync(dir);
8955
9892
  if (files.includes("package.json")) {
8956
- const pkg = JSON.parse(fs23.readFileSync(path25.join(dir, "package.json"), "utf-8"));
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 = path25.join(dir, "package.json");
8976
- if (fs23.existsSync(pkgPath)) {
8977
- const pkg = JSON.parse(fs23.readFileSync(pkgPath, "utf-8"));
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 (fs23.existsSync(path25.join(dir, "pytest.ini")) || fs23.existsSync(path25.join(dir, "conftest.py"))) return "pytest";
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 = path25.join(dir, "package.json");
8995
- if (fs23.existsSync(pkgPath)) {
8996
- const pkg = JSON.parse(fs23.readFileSync(pkgPath, "utf-8"));
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 \`./artifacts/\`; in the last \`message\` (\`result\`) list **absolute** paths in \`attachments[]\`. Remove temp files; do not attach generator scripts or junk.
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: os15.type(),
9155
- os_version: os15.release(),
9156
- architecture: os15.arch(),
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: os15.userInfo().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 = path25.join(dir, ".git");
9218
- return fs23.existsSync(gitPath) && fs23.lstatSync(gitPath).isDirectory();
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
- anchor = await compressToAnchor(
9436
- pendingFlat,
9437
- anchor,
9438
- llmService,
9439
- userContext,
9440
- pendingSlices.length
9441
- );
9442
- sliceCount = recentStart;
9443
- pendingSlices = [];
9444
- pendingFlat = [];
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 os16 from "os";
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 = os16.networkInterfaces();
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:${os16.hostname()}`;
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 = os16.userInfo().username || null;
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 fs24 from "fs";
9947
- import path26 from "path";
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 path26.join(policy.workspaceRoot, ".bluma", "hooks.json");
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 (fs24.existsSync(statePath)) {
9978
- const parsed = JSON.parse(fs24.readFileSync(statePath, "utf-8"));
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
- fs24.mkdirSync(path26.dirname(statePath), { recursive: true });
10954
+ fs26.mkdirSync(path28.dirname(statePath), { recursive: true });
10005
10955
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
10006
- fs24.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8");
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 fs25 from "fs";
10104
- import * as path27 from "path";
10105
- import os17 from "os";
11053
+ import * as fs27 from "fs";
11054
+ import * as path29 from "path";
11055
+ import os18 from "os";
10106
11056
  function memoryPath() {
10107
- return path27.join(process.env.HOME || os17.homedir(), ".bluma", "coding_memory.json");
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 (!fs25.existsSync(p)) {
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
- fs25.copyFileSync(p, bak);
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(fs25.readFileSync(p, "utf-8"));
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
- fs25.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
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 = uuidv44();
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 = path28.basename(toolArgs.file_path);
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/init/init_subagent.ts
11065
- import { v4 as uuidv46 } from "uuid";
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 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
11075
- 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.
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 InitSubAgent. Maintain professionalism and technical language.
11083
-
11084
- - Communication:
11085
- ALL messages must be sent via 'message'.
11086
- No direct text replies to the user.
11087
-
11088
- - Task Completion:
11089
- When the init task is completed, immediately invoke 'agent_end_turn' without user permissions.
11090
-
11091
- - Tool Rules:
11092
- Never make parallel tool calls.
11093
- Only use the defined tools with their exact names.
11094
-
11095
- - Autonomy:
11096
- Act 100% autonomously.
11097
- Do not ask for formatting preferences.
11098
- Use the notebook for internal reasoning.
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's via message tools instead of direct text responses
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 without specific solutions
11121
- - Notify user's with brief explanation when changing methods or strategies
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's with results and deliverables before upon task completion 'agent_end_turn'
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 laptop (reasoning_nootebook) is your ORGANIZED MIND
12333
+ CRITICAL: Your notebook (reasoning_notebook) is your ORGANIZED MIND
12334
+
11130
12335
  ## IMPORTANT
11131
- ## NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
11132
- ## ALWAYS USE A NOTEBOOK (Always for):
11133
- - ANY task
11134
- - Before starting userelopment (plan first!)
11135
- - Projects with multiple files (organize the structure)
11136
- - Debugging sessions (monitor discoveries)
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 reasoning_nootebook
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
- - "[ ]" \u2192 for tasks already completed
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 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.
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
- ## INIT SUBAGENT OBJECTIVE
11197
- - Map repository structure and significant files.
11198
- - Infer tech stack (frameworks, package managers, languages, build/test tools).
11199
- - Identify entry points, configuration files, and scripts.
11200
- - Produce BluMa.md with:
11201
- - Project overview and goals inferred from code/docs
11202
- - Tech stack summary
11203
- - Directory map (high-level)
11204
- - Key configs and scripts
11205
- - Known tasks or next steps for agents
11206
- - Always use tools (ls, readLines, count_lines, shell_command, edit_tool) to gather evidence before writing.
11207
- - Never invent file content. Read files via tools to confirm.
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
- - Emit 'backend_message' events through tools only (message) for progress updates.
11211
- - Before writing BluMa.md, propose structure via message and proceed using edit_tool.
11212
- - If an irreversible operation is needed (e.g., overwriting an existing BluMa.md), issue 'confirmation_request' unless user policy indicates auto-approval.
11213
- - 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.
11214
- - On successful generation of BluMa.md, emit 'done' with status 'completed' and call agent_end_turn.
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
- - Be conservative with edits; generate previews (diff) for edit_tool where applicable.
11218
- - Keep file system operations idempotent and explicit.
11219
- - Prefer performance-efficient scans (avoid reading entire large binaries).
11220
- - Respect test environment constraints.
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 getInitPrompt() {
12504
+ function getWorkerPrompt() {
11232
12505
  const now2 = /* @__PURE__ */ new Date();
11233
12506
  const collectedData = {
11234
- os_type: os18.type(),
11235
- os_version: os18.release(),
11236
- architecture: os18.arch(),
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: os18.userInfo().username || "Unknown",
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 = SYSTEM_PROMPT2;
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 = uuidv45();
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: uuidv46(),
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(path33, handler) {
11547
- this.routeHandlers.set(path33, handler);
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 [path33, handler] of this.routeHandlers) {
11553
- if (inputText === path33 || inputText.startsWith(`${path33} `)) {
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 path29 from "path";
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 = path29.resolve(p.root, entry);
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 = path30.join(os19.homedir(), ".bluma", ".env");
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 = ({ eventBus, taskName, taskStatus, startedAtMs }) => {
11841
- const [currentAction, setCurrentAction] = useState4("Thinking");
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() || "Thinking";
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", maxWidth: "100%", children: styled }, idx);
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", maxWidth: "100%", children: styled }, cellIdx);
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: "baseline", children: [
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 path31 from "path";
14196
- import fs26 from "fs";
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 = path31.join(dir, "package.json");
14202
- if (fs26.existsSync(candidate)) {
15639
+ const candidate = path33.join(dir, "package.json");
15640
+ if (fs28.existsSync(candidate)) {
14203
15641
  try {
14204
- const raw = fs26.readFileSync(candidate, "utf8");
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 = path31.dirname(dir);
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 = path31.resolve(dir);
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(path31.dirname(fileURLToPath5(import.meta.url)));
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 (path31.isAbsolute(argv1) && fs26.existsSync(argv1)) {
14236
- resolved = fs26.realpathSync(argv1);
15673
+ if (path33.isAbsolute(argv1) && fs28.existsSync(argv1)) {
15674
+ resolved = fs28.realpathSync(argv1);
14237
15675
  } else {
14238
- resolved = path31.resolve(process.cwd(), argv1);
15676
+ resolved = path33.resolve(process.cwd(), argv1);
14239
15677
  }
14240
- const fromArgv = tryFrom(path31.dirname(resolved));
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/App.tsx
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__ */ jsx24(ChatUserMessage, { children: pathStrs.length > 0 ? /* @__PURE__ */ jsx24(
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__ */ jsx24(Text22, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, wrap: "wrap", children: fallbackDisp }) });
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__ */ jsx24(ChatUserMessage, { children: paths.length > 0 ? /* @__PURE__ */ jsx24(
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__ */ jsx24(Text22, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, wrap: "wrap", children: displayRaw }) });
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] = useState8([]);
14667
- const [statusMessage, setStatusMessage] = useState8(
16336
+ const [history, setHistory] = useState11([]);
16337
+ const [statusMessage, setStatusMessage] = useState11(
14668
16338
  "Initializing agent..."
14669
16339
  );
14670
- const [toolsCount, setToolsCount] = useState8(null);
14671
- const [mcpStatus, setMcpStatus] = useState8(
16340
+ const [toolsCount, setToolsCount] = useState11(null);
16341
+ const [mcpStatus, setMcpStatus] = useState11(
14672
16342
  "connecting"
14673
16343
  );
14674
- const [isProcessing, setIsProcessing] = useState8(true);
14675
- const [pendingConfirmation, setPendingConfirmation] = useState8(
16344
+ const [isProcessing, setIsProcessing] = useState11(true);
16345
+ const [pendingConfirmation, setPendingConfirmation] = useState11(
14676
16346
  null
14677
16347
  );
14678
- const [confirmationPreview, setConfirmationPreview] = useState8(
16348
+ const [confirmationPreview, setConfirmationPreview] = useState11(
14679
16349
  null
14680
16350
  );
14681
- const [pendingAskUserQuestions, setPendingAskUserQuestions] = useState8(null);
14682
- const [isInitAgentActive, setIsInitAgentActive] = useState8(false);
14683
- const [liveToolName, setLiveToolName] = useState8(null);
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] = useState8(null);
14688
- const markTurnStarted = useCallback3(() => {
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 = useCallback3(() => {
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__ */ jsx24(ChatMeta, { children: "Ctrl+O: no truncated preview to expand" }, id)
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__ */ jsx24(ExpandedPreviewBlock, { data: p }, id)
16399
+ component: /* @__PURE__ */ jsx27(ExpandedPreviewBlock, { data: p }, id)
14714
16400
  }
14715
16401
  ];
14716
16402
  });
14717
16403
  }, []);
14718
- useEffect8(() => {
16404
+ useEffect11(() => {
14719
16405
  expandPreviewHotkeyBus.on("expand", appendExpandPreviewToHistory);
14720
16406
  return () => {
14721
16407
  expandPreviewHotkeyBus.off("expand", appendExpandPreviewToHistory);
14722
16408
  };
14723
16409
  }, [appendExpandPreviewToHistory]);
14724
- useEffect8(() => {
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__ */ jsx24(UpdateNotice_default, { message: msg })
16421
+ component: /* @__PURE__ */ jsx27(UpdateNotice_default, { message: msg })
14736
16422
  }
14737
16423
  ];
14738
16424
  });
14739
16425
  });
14740
16426
  }, []);
14741
- useEffect8(() => {
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__ */ jsx24(Header, { sessionId, workdir, cliVersion })
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 = useCallback3(() => {
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__ */ jsx24(ChatMeta, { children: "cancelled (Esc)" })
16451
+ component: /* @__PURE__ */ jsx27(ChatMeta, { children: "cancelled (Esc)" })
14766
16452
  }
14767
16453
  ];
14768
16454
  });
14769
16455
  }, [isProcessing, eventBus]);
14770
- const handleSubmit = useCallback3(
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__ */ jsx24(ChatMeta, { children: "Slash command not recognized or incomplete. Type /help for the list." })
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__ */ jsx24(ChatMeta, { children: "Usage: /img ./screenshot.png \u2014 optional text after the path is sent too" })
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__ */ jsx24(UserMessageWithOptionalImages, { raw: payload, variant: "slash-img" })
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__ */ jsx24(ChatUserMessage, { children: /* @__PURE__ */ jsx24(Text22, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, wrap: "wrap", children: text }) })
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__ */ jsx24(
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__ */ jsx24(ChatUserMessage, { children: /* @__PURE__ */ jsxs22(Text22, { bold: true, color: "white", children: [
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__ */ jsxs22(Text22, { color: "red", children: [
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__ */ jsxs22(Text22, { color: "red", children: [
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__ */ jsx24(UserMessageWithOptionalImages, { raw: text, variant: "plain" })
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 = useCallback3(
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 = useCallback3((reasoning) => {
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__ */ jsx24(ReasoningDisplay, { reasoning })
16679
+ component: /* @__PURE__ */ jsx27(ReasoningDisplay, { reasoning })
14994
16680
  }
14995
16681
  ];
14996
16682
  });
14997
16683
  }, []);
14998
- const appendStreamedAssistant = useCallback3((content) => {
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__ */ jsx24(AssistantMessageDisplay, { content })
16695
+ component: /* @__PURE__ */ jsx27(AssistantMessageDisplay, { content })
15010
16696
  }
15011
16697
  ];
15012
16698
  });
15013
16699
  }, []);
15014
- useEffect8(() => {
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
- useEffect8(() => {
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__ */ jsx24(ChatTurnDuration, { durationMs: ms })
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__ */ jsx24(ChatMeta, { children: parsed.message });
16857
+ newComponent = /* @__PURE__ */ jsx27(ChatMeta, { children: parsed.message });
15147
16858
  } else if (parsed.type === "protocol_violation") {
15148
- newComponent = /* @__PURE__ */ jsx24(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", paddingLeft: 2, children: [
15149
- /* @__PURE__ */ jsx24(Text22, { dimColor: true, children: parsed.content }),
15150
- /* @__PURE__ */ jsx24(Text22, { dimColor: true, children: parsed.message })
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__ */ jsx24(
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__ */ jsx24(
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__ */ jsx24(
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__ */ jsx24(ChatUserMessage, { children: /* @__PURE__ */ jsx24(Text22, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, wrap: "wrap", children: parsed.payload }) });
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__ */ jsx24(ReasoningDisplay, { reasoning: String(parsed.content ?? "") });
16931
+ newComponent = /* @__PURE__ */ jsx27(ReasoningDisplay, { reasoning: String(parsed.content ?? "") });
15220
16932
  }
15221
16933
  } else if (parsed.type === "log") {
15222
- newComponent = /* @__PURE__ */ jsxs22(ChatMeta, { children: [
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__ */ jsx24(AssistantMessageDisplay, { content: body });
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__ */ jsx24(ChatTurnDuration, { durationMs: ms })
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__ */ jsx24(ChatMeta, { children: msg }) }];
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__ */ jsx24(
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__ */ jsxs22(Box23, { flexDirection: "column", children: [
15293
- pendingAskUserQuestions && pendingAskUserQuestions.length > 0 ? /* @__PURE__ */ jsx24(
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__ */ jsx24(
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__ */ jsx24(WorkingTimer, { eventBus, startedAtMs: processingStartMs }),
15323
- /* @__PURE__ */ jsx24(
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__ */ jsxs22(Box23, { flexDirection: "column", children: [
15336
- /* @__PURE__ */ jsx24(Static, { items: history, children: (item) => /* @__PURE__ */ jsx24(Box23, { children: item.component }, item.id) }),
15337
- liveToolName ? /* @__PURE__ */ jsx24(Box23, { paddingLeft: 2, marginBottom: 0, children: /* @__PURE__ */ jsxs22(Text22, { dimColor: true, children: [
15338
- /* @__PURE__ */ jsxs22(Text22, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: [
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__ */ jsx24(
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 = fs27.readFileSync(filePath, "utf-8");
17214
+ rawPayload = fs29.readFileSync(filePath, "utf-8");
15481
17215
  } else {
15482
- rawPayload = fs27.readFileSync(0, "utf-8");
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 || uuidv47();
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 userContent = JSON.stringify({
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 = path32.dirname(fileURLToPath6(import.meta.url));
15679
- const pkgPath = path32.join(base, "..", "package.json");
15680
- const j = JSON.parse(fs27.readFileSync(pkgPath, "utf8"));
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 || uuidv47();
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(React16.createElement(App_default, props));
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 = fs27.readFileSync(filePath, "utf-8");
17539
+ const rawPayload = fs29.readFileSync(filePath, "utf-8");
15804
17540
  const envelope = JSON.parse(rawPayload);
15805
- const sessionId = envelope.session_id || envelope.message_id || uuidv47();
17541
+ const sessionId = envelope.session_id || envelope.message_id || uuidv48();
15806
17542
  registerSession({
15807
17543
  sessionId,
15808
17544
  kind: "agent",