@nomad-e/bluma-cli 0.1.14 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -331,6 +331,7 @@ var init_async_command = __esm({
331
331
  import React11 from "react";
332
332
  import { render } from "ink";
333
333
  import { EventEmitter as EventEmitter2 } from "events";
334
+ import fs14 from "fs";
334
335
  import { v4 as uuidv43 } from "uuid";
335
336
 
336
337
  // src/app/ui/App.tsx
@@ -342,7 +343,7 @@ import { Box, Text } from "ink";
342
343
  import { memo } from "react";
343
344
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
344
345
  var HeaderComponent = ({
345
- sessionId: sessionId2,
346
+ sessionId,
346
347
  workdir
347
348
  }) => {
348
349
  const dirName = workdir.split("/").pop() || workdir;
@@ -353,13 +354,13 @@ var HeaderComponent = ({
353
354
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: dirName }),
354
355
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " | " }),
355
356
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "session " }),
356
- /* @__PURE__ */ jsx(Text, { color: "gray", children: sessionId2.slice(0, 8) })
357
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: sessionId.slice(0, 8) })
357
358
  ] })
358
359
  ] });
359
360
  };
360
361
  var Header = memo(HeaderComponent);
361
362
  var SessionInfoComponent = ({
362
- sessionId: sessionId2,
363
+ sessionId,
363
364
  workdir,
364
365
  toolsCount,
365
366
  mcpStatus
@@ -2934,8 +2935,8 @@ async function getArtifactsDir() {
2934
2935
  if (artifactsDir) return artifactsDir;
2935
2936
  const homeDir = os3.homedir();
2936
2937
  const baseDir = path9.join(homeDir, ".bluma", "artifacts");
2937
- const sessionId2 = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
2938
- artifactsDir = path9.join(baseDir, sessionId2);
2938
+ const sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
2939
+ artifactsDir = path9.join(baseDir, sessionId);
2939
2940
  await fs7.mkdir(artifactsDir, { recursive: true });
2940
2941
  return artifactsDir;
2941
2942
  }
@@ -3482,9 +3483,9 @@ var MCPClient = class {
3482
3483
  nativeToolInvoker;
3483
3484
  eventBus;
3484
3485
  // <<< ADICIONA A PROPRIEDADE
3485
- constructor(nativeToolInvoker, eventBus2) {
3486
+ constructor(nativeToolInvoker, eventBus) {
3486
3487
  this.nativeToolInvoker = nativeToolInvoker;
3487
- this.eventBus = eventBus2;
3488
+ this.eventBus = eventBus;
3488
3489
  }
3489
3490
  // ... (método initialize inalterado) ...
3490
3491
  async initialize() {
@@ -3762,9 +3763,9 @@ async function ensureSessionDir() {
3762
3763
  await fs10.mkdir(sessionDir, { recursive: true });
3763
3764
  return sessionDir;
3764
3765
  }
3765
- async function loadOrcreateSession(sessionId2) {
3766
+ async function loadOrcreateSession(sessionId) {
3766
3767
  const sessionDir = await ensureSessionDir();
3767
- const sessionFile = path12.join(sessionDir, `${sessionId2}.json`);
3768
+ const sessionFile = path12.join(sessionDir, `${sessionId}.json`);
3768
3769
  try {
3769
3770
  await fs10.access(sessionFile);
3770
3771
  const fileContent = await fs10.readFile(sessionFile, "utf-8");
@@ -3772,7 +3773,7 @@ async function loadOrcreateSession(sessionId2) {
3772
3773
  return [sessionFile, [], []];
3773
3774
  } catch (error) {
3774
3775
  const newSessionData = {
3775
- session_id: sessionId2,
3776
+ session_id: sessionId,
3776
3777
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
3777
3778
  conversation_history: []
3778
3779
  };
@@ -3800,9 +3801,9 @@ async function doSaveSessionHistory(sessionFile, history) {
3800
3801
  console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
3801
3802
  }
3802
3803
  }
3803
- const sessionId2 = path12.basename(sessionFile, ".json");
3804
+ const sessionId = path12.basename(sessionFile, ".json");
3804
3805
  sessionData = {
3805
- session_id: sessionId2,
3806
+ session_id: sessionId,
3806
3807
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
3807
3808
  conversation_history: []
3808
3809
  };
@@ -4414,6 +4415,59 @@ You communicate because you respect your teammate.
4414
4415
  Let's build something great, {username}.
4415
4416
  </personality>
4416
4417
  `;
4418
+ var SANDBOX_PROMPT_SUFFIX = `
4419
+
4420
+ ---
4421
+
4422
+ <sandbox_context>
4423
+ ## Sandbox Context
4424
+
4425
+ Sandbox Name: {sandbox_name}
4426
+
4427
+ You are running INSIDE an orchestrated sandbox / API container.
4428
+
4429
+ In this mode:
4430
+
4431
+ - You are NOT talking directly to a human; all inputs come from JSON payloads provided by an orchestrator (the Sandbox API).
4432
+ - You MUST avoid interactive flows (no REPL, no prompts that wait for human input, no TUI/CLI menus).
4433
+ - You MUST keep all outputs deterministic, concise and structured so that external systems can log and replay your reasoning.
4434
+
4435
+ ### Execution Capabilities (Python-only)
4436
+
4437
+ - You are allowed to:
4438
+ - Generate and modify **Python code** (modules, scripts, notebooks, tests).
4439
+ - Propose and run **Python commands only**, e.g.:
4440
+ - \`python main.py\`
4441
+ - \`python -m pytest\`
4442
+ - Use the existing Python environment and preinstalled libraries inside the sandbox (e.g. pandas and other whitelisted packages).
4443
+
4444
+ - You are NOT allowed to:
4445
+ - Execute arbitrary shell commands (\`bash\`, \`sh\`, \`zsh\`, \`fish\`, \`cmd\`, \`powershell\`).
4446
+ - Run system-level tools (\`docker\`, \`npm\`, \`node\`, \`git\`, \`curl\`, \`wget\`, package managers, etc.).
4447
+ - Change global system configuration, users, permissions, or network settings.
4448
+ - Depend on interactive stdin/stdout behavior (no \`input()\`, no click/typer prompts).
4449
+
4450
+ ### Filesystem & IO
4451
+
4452
+ - Assume you are working in a **project directory managed by the orchestrator**.
4453
+ - You MAY:
4454
+ - Read and write project files (source, tests, configs) as requested by the job.
4455
+ - Create temporary Python files or modules needed to execute the job.
4456
+ - You MUST NOT:
4457
+ - Access files outside the project directory tree.
4458
+ - Store secrets or credentials in code or logs.
4459
+ - Rely on long-lived state: each job is independent and may run in a fresh environment.
4460
+
4461
+ ### Logging & Observability
4462
+
4463
+ - Treat every step as being logged and parsed by the orchestrator.
4464
+ - Prefer **structured, step-wise logs** (JSON lines) over free-form prose when emitting tool logs:
4465
+ - Each log entry SHOULD include at least: \`event_type\`, \`level\`, \`message\`, \`timestamp\`, and optional \`data\`.
4466
+ - Final results MUST be clearly separated from intermediate logs, using a dedicated \`"result"\` event when appropriate.
4467
+
4468
+ In summary: in sandbox mode you are a Python-focused, non-interactive, deterministic agent. You generate and execute Python code inside a controlled environment, and all interactions are mediated by JSON payloads and structured logs.
4469
+ </sandbox_context>
4470
+ `;
4417
4471
  function getUnifiedSystemPrompt(availableSkills) {
4418
4472
  const cwd = process.cwd();
4419
4473
  const env = {
@@ -4432,11 +4486,14 @@ function getUnifiedSystemPrompt(availableSkills) {
4432
4486
  package_manager: getPackageManager(cwd),
4433
4487
  project_type: getProjectType(cwd),
4434
4488
  test_framework: getTestFramework(cwd),
4435
- test_command: getTestCommand(cwd)
4489
+ test_command: getTestCommand(cwd),
4490
+ sandbox_mode: process.env.BLUMA_SANDBOX === "true" ? "yes" : "no",
4491
+ sandbox_name: process.env.BLUMA_SANDBOX_NAME || "local"
4436
4492
  };
4493
+ const basePrompt = env.sandbox_mode === "yes" ? SYSTEM_PROMPT + SANDBOX_PROMPT_SUFFIX : SYSTEM_PROMPT;
4437
4494
  let prompt = Object.entries(env).reduce(
4438
4495
  (p, [key, value]) => p.replaceAll(`{${key}}`, value),
4439
- SYSTEM_PROMPT
4496
+ basePrompt
4440
4497
  );
4441
4498
  if (availableSkills && availableSkills.length > 0) {
4442
4499
  const skillsList = availableSkills.map((s) => `- ${s.name}: ${s.description}`).join("\n");
@@ -4825,9 +4882,9 @@ var BluMaAgent = class {
4825
4882
  skillLoader;
4826
4883
  maxContextTurns = 5;
4827
4884
  isInterrupted = false;
4828
- constructor(sessionId2, eventBus2, llm, model, mcpClient, feedbackSystem) {
4829
- this.sessionId = sessionId2;
4830
- this.eventBus = eventBus2;
4885
+ constructor(sessionId, eventBus, llm, model, mcpClient, feedbackSystem) {
4886
+ this.sessionId = sessionId;
4887
+ this.eventBus = eventBus;
4831
4888
  this.llm = llm;
4832
4889
  this.model = model;
4833
4890
  this.mcpClient = mcpClient;
@@ -5068,7 +5125,7 @@ ${editData.error.display}`;
5068
5125
  messages: contextWindow,
5069
5126
  temperature: 0,
5070
5127
  tools: this.mcpClient.getAvailableTools(),
5071
- tool_choice: "required",
5128
+ //tool_choice: 'required',
5072
5129
  parallel_tool_calls: false
5073
5130
  });
5074
5131
  for await (const chunk of stream) {
@@ -5130,6 +5187,7 @@ ${editData.error.display}`;
5130
5187
  await this._continueConversation();
5131
5188
  return;
5132
5189
  }
5190
+ const isSandbox = process.env.BLUMA_SANDBOX === "true";
5133
5191
  const autoApprovedTools = [
5134
5192
  "message",
5135
5193
  "ls_tool",
@@ -5137,10 +5195,11 @@ ${editData.error.display}`;
5137
5195
  "read_file_lines",
5138
5196
  "todo",
5139
5197
  "load_skill",
5140
- "search_web"
5198
+ "search_web",
5199
+ "coding_memory"
5141
5200
  ];
5142
5201
  const toolToCall = validToolCalls[0];
5143
- const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
5202
+ const isSafeTool = isSandbox || autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
5144
5203
  if (isSafeTool) {
5145
5204
  await this.handleToolResponse({ type: "user_decision_execute", tool_calls: validToolCalls });
5146
5205
  } else {
@@ -5159,7 +5218,6 @@ ${editData.error.display}`;
5159
5218
  event: "protocol_violation_direct_text",
5160
5219
  details: { violationContent: accumulatedContent }
5161
5220
  });
5162
- this.eventBus.emit("backend_message", { type: "protocol_violation", message: feedback.message, content: accumulatedContent });
5163
5221
  this.history.push({ role: "system", content: feedback.correction });
5164
5222
  await this._continueConversation();
5165
5223
  } else {
@@ -5173,7 +5231,7 @@ ${editData.error.display}`;
5173
5231
  messages: contextWindow,
5174
5232
  temperature: 0,
5175
5233
  tools: this.mcpClient.getAvailableTools(),
5176
- tool_choice: "required",
5234
+ //tool_choice: 'required',
5177
5235
  parallel_tool_calls: false
5178
5236
  });
5179
5237
  if (this.isInterrupted) {
@@ -5202,6 +5260,7 @@ ${editData.error.display}`;
5202
5260
  await this._continueConversation();
5203
5261
  return;
5204
5262
  }
5263
+ const isSandbox = process.env.BLUMA_SANDBOX === "true";
5205
5264
  const autoApprovedTools = [
5206
5265
  "message",
5207
5266
  "ls_tool",
@@ -5209,10 +5268,11 @@ ${editData.error.display}`;
5209
5268
  "read_file_lines",
5210
5269
  "todo",
5211
5270
  "load_skill",
5212
- "search_web"
5271
+ "search_web",
5272
+ "coding_memory"
5213
5273
  ];
5214
5274
  const toolToCall = validToolCalls[0];
5215
- const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
5275
+ const isSafeTool = isSandbox || autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
5216
5276
  if (isSafeTool) {
5217
5277
  await this.handleToolResponse({ type: "user_decision_execute", tool_calls: validToolCalls });
5218
5278
  } else {
@@ -5267,7 +5327,7 @@ var LLMService = class {
5267
5327
  model: params.model || this.defaultModel,
5268
5328
  messages: params.messages,
5269
5329
  tools: params.tools,
5270
- tool_choice: params.tool_choice,
5330
+ //tool_choice: params.tool_choice,
5271
5331
  parallel_tool_calls: params.parallel_tool_calls,
5272
5332
  temperature: params.temperature,
5273
5333
  max_tokens: params.max_tokens
@@ -5289,7 +5349,7 @@ var LLMService = class {
5289
5349
  model: params.model || this.defaultModel,
5290
5350
  messages: params.messages,
5291
5351
  tools: params.tools,
5292
- tool_choice: params.tool_choice,
5352
+ //tool_choice: params.tool_choice,
5293
5353
  parallel_tool_calls: params.parallel_tool_calls,
5294
5354
  temperature: params.temperature,
5295
5355
  max_tokens: params.max_tokens,
@@ -5566,8 +5626,8 @@ var BaseLLMSubAgent = class {
5566
5626
  return { history: this.history };
5567
5627
  }
5568
5628
  async initializeHistory() {
5569
- const sessionId2 = `${this.id}`;
5570
- const [sessionFile, history] = await loadOrcreateSession(sessionId2);
5629
+ const sessionId = `${this.id}`;
5630
+ const [sessionFile, history] = await loadOrcreateSession(sessionId);
5571
5631
  this.sessionFile = sessionFile;
5572
5632
  this.history = history || [];
5573
5633
  const systemPromptContent = getInitPrompt();
@@ -5604,7 +5664,7 @@ ${editData.error.display}`;
5604
5664
  model: this.ctx.policy?.llmDeployment || "default",
5605
5665
  messages: contextWindow,
5606
5666
  tools: this.ctx.mcpClient.getAvailableTools(),
5607
- tool_choice: "required",
5667
+ //tool_choice: 'required',
5608
5668
  parallel_tool_calls: false
5609
5669
  });
5610
5670
  if (this.isInterrupted) {
@@ -5810,12 +5870,12 @@ var Agent = class {
5810
5870
  core;
5811
5871
  subAgents;
5812
5872
  toolInvoker;
5813
- constructor(sessionId2, eventBus2) {
5814
- this.sessionId = sessionId2;
5815
- this.eventBus = eventBus2;
5873
+ constructor(sessionId, eventBus) {
5874
+ this.sessionId = sessionId;
5875
+ this.eventBus = eventBus;
5816
5876
  const nativeToolInvoker = new ToolInvoker();
5817
5877
  this.toolInvoker = nativeToolInvoker;
5818
- this.mcpClient = new MCPClient(nativeToolInvoker, eventBus2);
5878
+ this.mcpClient = new MCPClient(nativeToolInvoker, eventBus);
5819
5879
  this.feedbackSystem = new AdvancedFeedbackSystem();
5820
5880
  const apiKey = process.env.NOMAD_API_KEY;
5821
5881
  const baseUrl = process.env.NOMAD_BASE_URL;
@@ -5913,22 +5973,22 @@ var Agent = class {
5913
5973
  import { useState as useState4, useEffect as useEffect4, memo as memo5 } from "react";
5914
5974
  import { Box as Box7, Text as Text7 } from "ink";
5915
5975
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
5916
- var WorkingTimerComponent = ({ eventBus: eventBus2, taskName, taskStatus }) => {
5976
+ var WorkingTimerComponent = ({ eventBus, taskName, taskStatus }) => {
5917
5977
  const [currentAction, setCurrentAction] = useState4("Thinking...");
5918
5978
  const [shinePosition, setShinePosition] = useState4(0);
5919
5979
  const [dots, setDots] = useState4("");
5920
5980
  useEffect4(() => {
5921
- if (!eventBus2) return;
5981
+ if (!eventBus) return;
5922
5982
  const handleActionStatus = (data) => {
5923
5983
  if (data.action) {
5924
5984
  setCurrentAction(data.action);
5925
5985
  }
5926
5986
  };
5927
- eventBus2.on("action_status", handleActionStatus);
5987
+ eventBus.on("action_status", handleActionStatus);
5928
5988
  return () => {
5929
- eventBus2.off("action_status", handleActionStatus);
5989
+ eventBus.off("action_status", handleActionStatus);
5930
5990
  };
5931
- }, [eventBus2]);
5991
+ }, [eventBus]);
5932
5992
  useEffect4(() => {
5933
5993
  const shineTimer = setInterval(() => {
5934
5994
  setShinePosition((prev) => (prev + 1) % 30);
@@ -6262,7 +6322,7 @@ var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
6262
6322
  if (toolName.includes("message")) {
6263
6323
  return null;
6264
6324
  }
6265
- const Renderer = ToolRenderDisplay[toolName] || ((props2) => renderGeneric2({ ...props2, toolName }));
6325
+ const Renderer = ToolRenderDisplay[toolName] || ((props) => renderGeneric2({ ...props, toolName }));
6266
6326
  return /* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Renderer, { toolName, args, preview }) });
6267
6327
  };
6268
6328
  var ToolCallDisplay = memo6(ToolCallDisplayComponent);
@@ -6903,7 +6963,7 @@ var ReasoningDisplay = memo9(ReasoningDisplayComponent);
6903
6963
  import { useState as useState6, useEffect as useEffect5, useRef as useRef4, memo as memo10 } from "react";
6904
6964
  import { Box as Box16, Text as Text15 } from "ink";
6905
6965
  import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
6906
- var StreamingTextComponent = ({ eventBus: eventBus2, onReasoningComplete }) => {
6966
+ var StreamingTextComponent = ({ eventBus, onReasoningComplete }) => {
6907
6967
  const [reasoning, setReasoning] = useState6("");
6908
6968
  const [isStreaming, setIsStreaming] = useState6(false);
6909
6969
  const reasoningRef = useRef4("");
@@ -6945,15 +7005,15 @@ var StreamingTextComponent = ({ eventBus: eventBus2, onReasoningComplete }) => {
6945
7005
  setReasoning("");
6946
7006
  reasoningRef.current = "";
6947
7007
  };
6948
- eventBus2.on("stream_start", handleStart);
6949
- eventBus2.on("stream_reasoning_chunk", handleReasoningChunk);
6950
- eventBus2.on("stream_end", handleEnd);
7008
+ eventBus.on("stream_start", handleStart);
7009
+ eventBus.on("stream_reasoning_chunk", handleReasoningChunk);
7010
+ eventBus.on("stream_end", handleEnd);
6951
7011
  return () => {
6952
- eventBus2.off("stream_start", handleStart);
6953
- eventBus2.off("stream_reasoning_chunk", handleReasoningChunk);
6954
- eventBus2.off("stream_end", handleEnd);
7012
+ eventBus.off("stream_start", handleStart);
7013
+ eventBus.off("stream_reasoning_chunk", handleReasoningChunk);
7014
+ eventBus.off("stream_end", handleEnd);
6955
7015
  };
6956
- }, [eventBus2, onReasoningComplete]);
7016
+ }, [eventBus, onReasoningComplete]);
6957
7017
  if (!isStreaming || !reasoning) {
6958
7018
  return null;
6959
7019
  }
@@ -6993,7 +7053,7 @@ var SAFE_AUTO_APPROVE_TOOLS = [
6993
7053
  // Status de comandos (read-only)
6994
7054
  "command_status"
6995
7055
  ];
6996
- var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
7056
+ var AppComponent = ({ eventBus, sessionId }) => {
6997
7057
  const agentInstance = useRef5(null);
6998
7058
  const [history, setHistory] = useState7([]);
6999
7059
  const [statusMessage, setStatusMessage] = useState7(
@@ -7018,7 +7078,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
7018
7078
  const [toolCallCount, setToolCallCount] = useState7(0);
7019
7079
  const handleInterrupt = useCallback2(() => {
7020
7080
  if (!isProcessing) return;
7021
- eventBus2.emit("user_interrupt");
7081
+ eventBus.emit("user_interrupt");
7022
7082
  setIsProcessing(false);
7023
7083
  setHistory((prev) => [
7024
7084
  ...prev,
@@ -7027,7 +7087,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
7027
7087
  component: /* @__PURE__ */ jsx17(Text16, { color: "yellow", children: "-- Task cancelled by dev. --" })
7028
7088
  }
7029
7089
  ]);
7030
- }, [isProcessing, eventBus2]);
7090
+ }, [isProcessing, eventBus]);
7031
7091
  const handleSubmit = useCallback2(
7032
7092
  (text) => {
7033
7093
  if (!text || isProcessing || !agentInstance.current) return;
@@ -7167,19 +7227,19 @@ Please use command_status to check the result and report back to the user.`;
7167
7227
  []
7168
7228
  );
7169
7229
  useEffect6(() => {
7170
- setHistory([{ id: 0, component: /* @__PURE__ */ jsx17(Header, { sessionId: sessionId2, workdir }) }]);
7230
+ setHistory([{ id: 0, component: /* @__PURE__ */ jsx17(Header, { sessionId, workdir }) }]);
7171
7231
  const initializeAgent = async () => {
7172
7232
  try {
7173
- agentInstance.current = new Agent(sessionId2, eventBus2);
7233
+ agentInstance.current = new Agent(sessionId, eventBus);
7174
7234
  await agentInstance.current.initialize();
7175
- eventBus2.emit("backend_message", {
7235
+ eventBus.emit("backend_message", {
7176
7236
  type: "status",
7177
7237
  status: "mcp_connected",
7178
7238
  tools: agentInstance.current.getAvailableTools().length
7179
7239
  });
7180
7240
  } catch (error) {
7181
7241
  const errorMessage = error instanceof Error ? error.message : "Unknown error during Agent initialization.";
7182
- eventBus2.emit("backend_message", {
7242
+ eventBus.emit("backend_message", {
7183
7243
  type: "error",
7184
7244
  message: errorMessage
7185
7245
  });
@@ -7320,16 +7380,16 @@ Please use command_status to check the result and report back to the user.`;
7320
7380
  }
7321
7381
  };
7322
7382
  const handleUiOverlay = (data) => {
7323
- eventBus2.emit("user_overlay", data);
7383
+ eventBus.emit("user_overlay", data);
7324
7384
  };
7325
7385
  uiEventBus.on("user_overlay", handleUiOverlay);
7326
- eventBus2.on("backend_message", handleBackendMessage);
7386
+ eventBus.on("backend_message", handleBackendMessage);
7327
7387
  initializeAgent();
7328
7388
  return () => {
7329
7389
  uiEventBus.off("user_overlay", handleUiOverlay);
7330
- eventBus2.off("backend_message", handleBackendMessage);
7390
+ eventBus.off("backend_message", handleBackendMessage);
7331
7391
  };
7332
- }, [eventBus2, sessionId2, handleConfirmation]);
7392
+ }, [eventBus, sessionId, handleConfirmation]);
7333
7393
  const renderInteractiveComponent = () => {
7334
7394
  if (mcpStatus !== "connected") {
7335
7395
  return;
@@ -7348,7 +7408,7 @@ Please use command_status to check the result and report back to the user.`;
7348
7408
  );
7349
7409
  }
7350
7410
  return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
7351
- isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx17(WorkingTimer, { eventBus: eventBus2 }),
7411
+ isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx17(WorkingTimer, { eventBus }),
7352
7412
  /* @__PURE__ */ jsx17(
7353
7413
  InputPrompt,
7354
7414
  {
@@ -7365,7 +7425,7 @@ Please use command_status to check the result and report back to the user.`;
7365
7425
  /* @__PURE__ */ jsx17(
7366
7426
  StreamingText,
7367
7427
  {
7368
- eventBus: eventBus2,
7428
+ eventBus,
7369
7429
  onReasoningComplete: (reasoning) => {
7370
7430
  if (reasoning) {
7371
7431
  setHistory((prev) => [
@@ -7441,12 +7501,174 @@ function stopTitleKeeper() {
7441
7501
  }
7442
7502
 
7443
7503
  // src/main.ts
7444
- var BLUMA_TITLE = process.env.BLUMA_TITLE || "BluMa - NomadEngenuity";
7445
- startTitleKeeper(BLUMA_TITLE);
7446
- var eventBus = new EventEmitter2();
7447
- var sessionId = uuidv43();
7448
- var props = {
7449
- eventBus,
7450
- sessionId
7451
- };
7452
- render(React11.createElement(App_default, props));
7504
+ function writeJsonl(event) {
7505
+ try {
7506
+ process.stdout.write(JSON.stringify(event) + "\n");
7507
+ } catch {
7508
+ }
7509
+ }
7510
+ async function runAgentMode() {
7511
+ const args = process.argv.slice(2);
7512
+ const inputFileIndex = args.indexOf("--input-file");
7513
+ const inputIndex = args.indexOf("--input");
7514
+ let rawPayload = "";
7515
+ try {
7516
+ if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
7517
+ const filePath = args[inputFileIndex + 1];
7518
+ rawPayload = fs14.readFileSync(filePath, "utf-8");
7519
+ } else {
7520
+ rawPayload = fs14.readFileSync(0, "utf-8");
7521
+ }
7522
+ } catch (err) {
7523
+ writeJsonl({
7524
+ event_type: "result",
7525
+ status: "error",
7526
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7527
+ error: {
7528
+ message: "Failed to read agent payload",
7529
+ details: err?.message
7530
+ }
7531
+ });
7532
+ process.exit(1);
7533
+ return;
7534
+ }
7535
+ let envelope;
7536
+ try {
7537
+ envelope = JSON.parse(rawPayload);
7538
+ } catch (err) {
7539
+ writeJsonl({
7540
+ event_type: "result",
7541
+ status: "error",
7542
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7543
+ error: {
7544
+ message: "Invalid JSON payload for agent mode",
7545
+ details: err?.message
7546
+ }
7547
+ });
7548
+ process.exit(1);
7549
+ return;
7550
+ }
7551
+ const eventBus = new EventEmitter2();
7552
+ const sessionId = envelope.message_id || uuidv43();
7553
+ let lastAssistantMessage = null;
7554
+ let reasoningBuffer = null;
7555
+ let resultEmitted = false;
7556
+ eventBus.on("backend_message", (payload) => {
7557
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
7558
+ writeJsonl({
7559
+ event_type: "backend_message",
7560
+ backend_type: String(payload?.type || "unknown"),
7561
+ timestamp,
7562
+ payload
7563
+ });
7564
+ if (payload?.type === "assistant_message" && typeof payload.content === "string") {
7565
+ lastAssistantMessage = payload.content;
7566
+ }
7567
+ if (payload?.type === "reasoning" && typeof payload.content === "string") {
7568
+ reasoningBuffer = (reasoningBuffer || "") + payload.content;
7569
+ }
7570
+ if (payload?.type === "tool_result" && payload.tool_name === "message") {
7571
+ try {
7572
+ const rawResult = payload.result;
7573
+ const parsed = typeof rawResult === "string" ? JSON.parse(rawResult) : rawResult;
7574
+ const body = parsed?.content?.body;
7575
+ if (typeof body === "string") {
7576
+ lastAssistantMessage = body;
7577
+ }
7578
+ } catch {
7579
+ }
7580
+ }
7581
+ if (!resultEmitted && payload?.type === "done") {
7582
+ resultEmitted = true;
7583
+ writeJsonl({
7584
+ event_type: "result",
7585
+ status: "success",
7586
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7587
+ data: {
7588
+ message_id: envelope.message_id || sessionId,
7589
+ action: envelope.action || "unknown",
7590
+ last_assistant_message: lastAssistantMessage,
7591
+ reasoning: reasoningBuffer
7592
+ }
7593
+ });
7594
+ process.exit(0);
7595
+ }
7596
+ });
7597
+ eventBus.on("action_status", (payload) => {
7598
+ writeJsonl({
7599
+ event_type: "action_status",
7600
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7601
+ payload
7602
+ });
7603
+ });
7604
+ writeJsonl({
7605
+ event_type: "log",
7606
+ level: "info",
7607
+ message: "Starting agent mode execution",
7608
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7609
+ data: {
7610
+ message_id: envelope.message_id,
7611
+ action: envelope.action,
7612
+ from_agent: envelope.from_agent,
7613
+ to_agent: envelope.to_agent
7614
+ }
7615
+ });
7616
+ try {
7617
+ const agent = new Agent(sessionId, eventBus);
7618
+ await agent.initialize();
7619
+ const userContent = JSON.stringify({
7620
+ message_id: envelope.message_id || sessionId,
7621
+ from_agent: envelope.from_agent || "sandbox-api",
7622
+ to_agent: envelope.to_agent || "bluma",
7623
+ action: envelope.action || "unknown",
7624
+ context: envelope.context || {},
7625
+ metadata: envelope.metadata || {}
7626
+ });
7627
+ await agent.processTurn({ content: userContent });
7628
+ if (!resultEmitted) {
7629
+ resultEmitted = true;
7630
+ writeJsonl({
7631
+ event_type: "result",
7632
+ status: "success",
7633
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7634
+ data: {
7635
+ message_id: envelope.message_id || sessionId,
7636
+ action: envelope.action || "unknown",
7637
+ last_assistant_message: lastAssistantMessage,
7638
+ reasoning: reasoningBuffer
7639
+ }
7640
+ });
7641
+ process.exit(0);
7642
+ }
7643
+ } catch (err) {
7644
+ if (!resultEmitted) {
7645
+ writeJsonl({
7646
+ event_type: "result",
7647
+ status: "error",
7648
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7649
+ error: {
7650
+ message: "Agent mode execution failed",
7651
+ details: err?.message
7652
+ }
7653
+ });
7654
+ }
7655
+ process.exit(1);
7656
+ }
7657
+ }
7658
+ function runCliMode() {
7659
+ const BLUMA_TITLE = process.env.BLUMA_TITLE || "BluMa - NomadEngenuity";
7660
+ startTitleKeeper(BLUMA_TITLE);
7661
+ const eventBus = new EventEmitter2();
7662
+ const sessionId = uuidv43();
7663
+ const props = {
7664
+ eventBus,
7665
+ sessionId
7666
+ };
7667
+ render(React11.createElement(App_default, props));
7668
+ }
7669
+ var argv = process.argv.slice(2);
7670
+ if (argv[0] === "agent") {
7671
+ runAgentMode();
7672
+ } else {
7673
+ runCliMode();
7674
+ }
@@ -0,0 +1,3 @@
1
+ Copyright (c) NomadEngenuity. All rights reserved.
2
+
3
+ This skill is part of the Bluma CLI and is subject to the same license as the main package (Apache-2.0) unless otherwise stated in the SKILL.md frontmatter.