@nomad-e/bluma-cli 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +322 -116
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -3204,7 +3204,7 @@ __export(exportConversation_exports, {
3204
3204
  formatConversationMarkdown: () => formatConversationMarkdown,
3205
3205
  resolveExportOutputPath: () => resolveExportOutputPath
3206
3206
  });
3207
- import { promises as fs48 } from "fs";
3207
+ import { promises as fs49 } from "fs";
3208
3208
  import path54 from "path";
3209
3209
  function extractTextParts2(content) {
3210
3210
  if (typeof content === "string") {
@@ -3390,8 +3390,8 @@ async function exportConversationToFile(options) {
3390
3390
  }
3391
3391
  const markdown = formatConversationMarkdown(sessionId, history);
3392
3392
  const filePath = resolveExportOutputPath(sessionId, { outputPath, defaultExportDir });
3393
- await fs48.mkdir(path54.dirname(filePath), { recursive: true });
3394
- await fs48.writeFile(filePath, markdown, "utf-8");
3393
+ await fs49.mkdir(path54.dirname(filePath), { recursive: true });
3394
+ await fs49.writeFile(filePath, markdown, "utf-8");
3395
3395
  return {
3396
3396
  success: true,
3397
3397
  filePath,
@@ -14299,7 +14299,7 @@ var getInstance = (stdout, createInstance) => {
14299
14299
 
14300
14300
  // src/main.ts
14301
14301
  import { EventEmitter as EventEmitter7 } from "events";
14302
- import fs52 from "fs";
14302
+ import fs53 from "fs";
14303
14303
  import path58 from "path";
14304
14304
  import { fileURLToPath as fileURLToPath8 } from "url";
14305
14305
  import { spawn as spawn6 } from "child_process";
@@ -22029,12 +22029,12 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
22029
22029
  // src/app/agent/bluma/core/bluma.ts
22030
22030
  init_session_manager();
22031
22031
  import path45 from "path";
22032
- import fs40 from "fs";
22032
+ import fs41 from "fs";
22033
22033
  import { v4 as uuidv48 } from "uuid";
22034
22034
 
22035
22035
  // src/app/agent/core/prompt/prompt_builder.ts
22036
22036
  import os23 from "os";
22037
- import fs36 from "fs";
22037
+ import fs37 from "fs";
22038
22038
  import path39 from "path";
22039
22039
  import { execSync as execSync3 } from "child_process";
22040
22040
 
@@ -23923,14 +23923,14 @@ var FACTORAI_SH_SECTION = `
23923
23923
 
23924
23924
  **When to load full checklist:** \`load_skill("factorai-sh")\` before any create/deploy/edit/redeploy task.
23925
23925
 
23926
- ### Backend URL (already in your environment)
23926
+ ### Backend URL (see also \`<sandbox_runtime>\` table)
23927
23927
  | Variable | Used by |
23928
23928
  |----------|---------|
23929
23929
  | \`FACTORAI_BASE_URL\` | \`factorai.sh.get_app_status\`, \`factorai.sh.apply_app_changes\`, \`factorai.sh.redeploy_app\` |
23930
23930
  | \`SEVERINO_URL\` | \`factorai.sh.deploy_app\` (ZIP upload to \`/api/v1/deploy\`) |
23931
23931
  | \`FACTORAI_API_KEY\` / \`SEVERINO_API_KEY\` | Optional Bearer on write endpoints |
23932
23932
 
23933
- The orchestrator configures these \u2014 **use the values already set**. Do not invent hosts or assume prod vs dev; tools call whatever base URL the environment provides.
23933
+ The orchestrator configures these \u2014 **use the values in \`<sandbox_runtime>\`**, not invented hosts. Autonomous loop: \`tsc --noEmit\` locally \u2192 \`deploy_app\` \u2192 \`get_app_status({ wait: true })\` \u2192 fix on \`failed\` \u2192 only then \`message(result)\`.
23934
23934
 
23935
23935
  ### Source of truth: \`factorai.sh.json\`
23936
23936
  - Written/updated by \`factorai.sh.deploy_app\` and FactorAI tools \u2014 **read with normal file tools**, never a separate manifest tool.
@@ -24013,7 +24013,196 @@ function isFactorAiShPromptEnabled() {
24013
24013
  return isSandbox && Boolean(baseUrl);
24014
24014
  }
24015
24015
 
24016
+ // src/app/agent/core/prompt/sandbox_autonomy_prompt.ts
24017
+ var SANDBOX_AUTONOMY_SECTION = `
24018
+ <sandbox_autonomy>
24019
+ ## Modo aut\xF3nomo (como Manus / agente de engenharia)
24020
+
24021
+ Tu **n\xE3o** est\xE1s num chat interativo com humano ao teclado. Est\xE1s num **worker** com um objetivo, ferramentas, e um prazo. Comportamento esperado:
24022
+
24023
+ ### 1. Orientar-te (sempre primeiro)
24024
+ - L\xEA \`<sandbox_runtime>\` \u2014 session, cwd, URLs, pedido, \`factorai.sh.json\` se existir.
24025
+ - Se o pedido envolve Next.js / site / deploy: \`load_skill("factorai-sh")\`.
24026
+ - Lista o diret\xF3rio relevante (\`ls_tool\`) antes de assumir estrutura de ficheiros.
24027
+
24028
+ ### 2. Planear em sil\xEAncio \xFAtil
24029
+ - Usa \`todo\` ou \`task_create\` para 3\u20138 passos concretos (n\xE3o gen\xE9ricos).
24030
+ - \`message({ message_type: "info" })\` a cada fase importante \u2014 o orquestrador v\xEA progresso.
24031
+
24032
+ ### 3. Implementar como humano s\xE9nior
24033
+ - Edita com \`edit_tool\` / \`file_write\`; l\xEA ficheiros completos antes de patch grande.
24034
+ - UI Next: \`@/components/ui/*\` do scaffold; n\xE3o reinventar componentes.
24035
+ - Subtarefas paralelas: \`spawn_agent\` + \`wait_agent\` quando fizer sentido.
24036
+
24037
+ ### 4. Verificar **antes** de deploy (obrigat\xF3rio para apps Next)
24038
+ No diret\xF3rio do projeto (ex. \`./my-app\`):
24039
+ 1. \`npx tsc --noEmit\` \u2014 corrige todos os erros TypeScript.
24040
+ 2. Opcional: \`npm run lint\` se existir script.
24041
+ 3. **Evita** \`npm run build\` local **antes do primeiro** \`deploy_app\` (cria \`.next/\` e incha o ZIP). O build de produ\xE7\xE3o corre no FactorAI.sh.
24042
+
24043
+ ### 5. Deploy e valida\xE7\xE3o remota
24044
+ 1. \`factorai.sh.deploy_app({ projectDir, name })\` \u2014 primeiro deploy.
24045
+ 2. \`factorai.sh.get_app_status({ appId, wait: true })\` \u2014 espera \`ready\` ou \`failed\`.
24046
+ 3. Se \`failed\`: l\xEA \`typescriptErrors\`, \`buildLogTail\`, \`summary\` no resultado; corrige c\xF3digo; \`apply_app_changes\` ou novo deploy; **nunca** digas que est\xE1 online.
24047
+ 4. S\xF3 depois de \`ready\`: confirma URL (curl ou l\xF3gica) e \`message(result)\` com \`factor-sh-url-app\`.
24048
+
24049
+ ### 6. Edi\xE7\xF5es em app j\xE1 online (esta sess\xE3o)
24050
+ - \`factorai.sh.apply_app_changes\` com ficheiro **completo** em cada \`files[].content\`.
24051
+ - Volta a \`get_app_status({ wait: true })\` ap\xF3s rebuild.
24052
+
24053
+ ### 7. Entregar como produto acabado
24054
+ - Ficheiros: \`message(result)\` + \`attachments\` em \`.bluma/artifacts/\`.
24055
+ - App live: \`factor-sh-url-app\` com URL absoluta.
24056
+ - Resumo em portugu\xEAs claro: o que fizeste, URL, pr\xF3ximos passos se falhou.
24057
+
24058
+ ### Proibido no sandbox worker
24059
+ - Pedir confirma\xE7\xE3o ao utilizador (\`ask_user_question\`) salvo bloqueio real de credencial em falta no env.
24060
+ - Inventar URLs, appIds, ou hosts \u2014 usa s\xF3 \`<sandbox_runtime>\` e \`factorai.sh.json\`.
24061
+ - \`message(result)\` com sucesso se deploy/build falhou.
24062
+ - Ignorar erros de compila\xE7\xE3o (\u201Cvou deploy na mesma\u201D).
24063
+ </sandbox_autonomy>
24064
+ `;
24065
+
24066
+ // src/app/agent/runtime/sandbox_runtime_context.ts
24067
+ import fs36 from "fs";
24068
+ function env2(key) {
24069
+ return String(process.env[key] ?? "").trim();
24070
+ }
24071
+ function pickRuntime(cwd2, injected) {
24072
+ const severinoUrl = injected?.severinoUrl || env2("SEVERINO_URL");
24073
+ const factoraiBaseUrl = injected?.factoraiBaseUrl || env2("FACTORAI_BASE_URL") || env2("FACTORAI_URL") || severinoUrl;
24074
+ return {
24075
+ sandboxName: injected?.sandboxName || env2("BLUMA_SANDBOX_NAME") || "sandbox-api",
24076
+ sessionId: injected?.sessionId || env2("BLUMA_SESSION_ID") || "unknown",
24077
+ workspaceRoot: injected?.workspaceRoot || cwd2,
24078
+ fromAgent: injected?.fromAgent || env2("BLUMA_FROM_AGENT") || "orchestrator",
24079
+ action: injected?.action || env2("BLUMA_ACTION") || "unknown",
24080
+ severinoUrl: severinoUrl || void 0,
24081
+ factoraiBaseUrl: factoraiBaseUrl || void 0,
24082
+ severinoApiKeyConfigured: injected?.severinoApiKeyConfigured ?? Boolean(env2("SEVERINO_API_KEY") || env2("SEVERINO_TOKEN")),
24083
+ factoraiApiKeyConfigured: injected?.factoraiApiKeyConfigured ?? Boolean(env2("FACTORAI_API_KEY") || env2("FACTORAI_TOKEN")),
24084
+ timeoutSeconds: injected?.timeoutSeconds,
24085
+ userRequest: injected?.userRequest || env2("BLUMA_USER_REQUEST") || void 0,
24086
+ operatorMode: injected?.operatorMode || "autonomous_worker",
24087
+ injectedAt: injected?.injectedAt
24088
+ };
24089
+ }
24090
+ function applySandboxRuntimeFromEnvelope(envelope) {
24091
+ const rt = envelope.sandbox_runtime;
24092
+ if (!rt || typeof rt !== "object") {
24093
+ return null;
24094
+ }
24095
+ process.env.BLUMA_SANDBOX = "true";
24096
+ if (rt.sandboxName) process.env.BLUMA_SANDBOX_NAME = rt.sandboxName;
24097
+ if (rt.sessionId) process.env.BLUMA_SESSION_ID = rt.sessionId;
24098
+ if (rt.fromAgent) process.env.BLUMA_FROM_AGENT = rt.fromAgent;
24099
+ if (rt.action) process.env.BLUMA_ACTION = rt.action;
24100
+ if (rt.severinoUrl) process.env.SEVERINO_URL = rt.severinoUrl.replace(/\/$/, "");
24101
+ if (rt.factoraiBaseUrl) {
24102
+ process.env.FACTORAI_BASE_URL = rt.factoraiBaseUrl.replace(/\/$/, "");
24103
+ } else if (rt.severinoUrl && !env2("FACTORAI_BASE_URL")) {
24104
+ process.env.FACTORAI_BASE_URL = rt.severinoUrl.replace(/\/$/, "");
24105
+ }
24106
+ if (rt.userRequest?.trim()) {
24107
+ process.env.BLUMA_USER_REQUEST = rt.userRequest.trim();
24108
+ }
24109
+ if (typeof rt.timeoutSeconds === "number" && rt.timeoutSeconds > 0) {
24110
+ process.env.BLUMA_JOB_TIMEOUT_SECONDS = String(rt.timeoutSeconds);
24111
+ }
24112
+ if (rt.workspaceRoot) {
24113
+ process.env.BLUMA_SANDBOX_WORKSPACE = rt.workspaceRoot;
24114
+ }
24115
+ if (envelope.metadata?.sandbox === true && !process.env.BLUMA_SANDBOX_NAME?.trim()) {
24116
+ process.env.BLUMA_SANDBOX_NAME = String(envelope.metadata.sandbox_name || "sandbox-api");
24117
+ }
24118
+ return rt;
24119
+ }
24120
+ function listWorkspaceTopLevel(cwd2, max = 24) {
24121
+ try {
24122
+ return fs36.readdirSync(cwd2, { withFileTypes: true }).filter((e) => !e.name.startsWith(".")).slice(0, max).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
24123
+ } catch {
24124
+ return [];
24125
+ }
24126
+ }
24127
+ function buildSandboxRuntimeContextBlock(cwd2, injected) {
24128
+ const rt = pickRuntime(cwd2, injected);
24129
+ const factoraiEnabled = isFactorAiShPromptEnabled();
24130
+ const top = listWorkspaceTopLevel(cwd2);
24131
+ const lines = [
24132
+ "<sandbox_runtime>",
24133
+ "## Ambiente atual (injetado pelo orquestrador \u2014 factos autoritativos)",
24134
+ "",
24135
+ rt.injectedAt ? `_Atualizado: ${rt.injectedAt}_` : "_Dados do envelope stdin + env da sess\xE3o_",
24136
+ "",
24137
+ `Modo: **${rt.operatorMode || "autonomous_worker"}** (sem humano no terminal; progresso via \`message(info)\`, fim via \`message(result)\`).`,
24138
+ "",
24139
+ "| Campo | Valor |",
24140
+ "|-------|-------|",
24141
+ `| sandbox | ${rt.sandboxName} |`,
24142
+ `| session_id | ${rt.sessionId} |`,
24143
+ `| workspace (cwd) | ${rt.workspaceRoot || cwd2} |`,
24144
+ `| delegado por | ${rt.fromAgent} |`,
24145
+ `| action | ${rt.action} |`,
24146
+ `| node | ${process.version} |`,
24147
+ `| job_timeout_seconds | ${rt.timeoutSeconds ?? (env2("BLUMA_JOB_TIMEOUT_SECONDS") || "\u2014")} |`,
24148
+ `| auto_approve_tools | sim |`
24149
+ ];
24150
+ if (rt.severinoUrl) {
24151
+ lines.push(`| SEVERINO_URL (deploy ZIP) | ${rt.severinoUrl} |`);
24152
+ lines.push(`| SEVERINO_API_KEY | ${rt.severinoApiKeyConfigured ? "configurada" : "ausente"} |`);
24153
+ }
24154
+ if (rt.factoraiBaseUrl) {
24155
+ lines.push(`| FACTORAI_BASE_URL | ${rt.factoraiBaseUrl} |`);
24156
+ lines.push(`| FACTORAI_API_KEY | ${rt.factoraiApiKeyConfigured ? "configurada" : "ausente"} |`);
24157
+ }
24158
+ if (rt.severinoUrl && rt.factoraiBaseUrl && rt.severinoUrl !== rt.factoraiBaseUrl) {
24159
+ lines.push("");
24160
+ lines.push("\u26A0\uFE0F SEVERINO_URL \u2260 FACTORAI_BASE_URL \u2014 usa o host certo por tool.");
24161
+ }
24162
+ lines.push("");
24163
+ lines.push(
24164
+ factoraiEnabled ? "**FactorAI.sh:** ativo (`factorai.sh.*` dispon\xEDveis)." : "**FactorAI.sh:** inativo \u2014 orquestrador deve definir FACTORAI_BASE_URL + BLUMA_SANDBOX."
24165
+ );
24166
+ if (top.length > 0) {
24167
+ lines.push("");
24168
+ lines.push(`**Raiz do workspace:** ${top.join(", ")}`);
24169
+ }
24170
+ const manifest = readFactorAiWorkspaceManifest(cwd2);
24171
+ if (manifest) {
24172
+ const ctx = manifest.appContext;
24173
+ const liveUrl = resolveFactorShAppLiveUrl(cwd2);
24174
+ lines.push("");
24175
+ lines.push("**App FactorAI (factorai.sh.json no workspace):**");
24176
+ if (ctx?.appId) lines.push(`- appId: ${ctx.appId}`);
24177
+ if (liveUrl) lines.push(`- appUrl: ${liveUrl}`);
24178
+ }
24179
+ if (rt.userRequest) {
24180
+ lines.push("");
24181
+ lines.push("**Pedido deste job (user_request):**");
24182
+ lines.push(rt.userRequest.length > 4e3 ? `${rt.userRequest.slice(0, 4e3)}\u2026` : rt.userRequest);
24183
+ }
24184
+ lines.push("</sandbox_runtime>");
24185
+ return lines.join("\n");
24186
+ }
24187
+ function formatSandboxRuntimeUserTurnPrefix(rt) {
24188
+ const parts = [
24189
+ "<sandbox_runtime_injected>",
24190
+ `session=${rt.sessionId ?? "?"}`,
24191
+ `action=${rt.action ?? "?"}`,
24192
+ `from=${rt.fromAgent ?? "?"}`,
24193
+ rt.severinoUrl ? `SEVERINO_URL=${rt.severinoUrl}` : null,
24194
+ rt.factoraiBaseUrl ? `FACTORAI_BASE_URL=${rt.factoraiBaseUrl}` : null,
24195
+ typeof rt.timeoutSeconds === "number" ? `timeout_seconds=${rt.timeoutSeconds}` : null,
24196
+ "</sandbox_runtime_injected>"
24197
+ ].filter(Boolean);
24198
+ return parts.join("\n");
24199
+ }
24200
+
24016
24201
  // src/app/agent/core/prompt/prompt_builder.ts
24202
+ var _injectedSandboxRuntime = null;
24203
+ function setInjectedSandboxRuntimeForPrompt(rt) {
24204
+ _injectedSandboxRuntime = rt;
24205
+ }
24017
24206
  function getNodeVersion() {
24018
24207
  return process.version;
24019
24208
  }
@@ -24044,17 +24233,17 @@ function getGitBranch(dir) {
24044
24233
  }
24045
24234
  }
24046
24235
  function getPackageManager(dir) {
24047
- if (fs36.existsSync(path39.join(dir, "pnpm-lock.yaml"))) return "pnpm";
24048
- if (fs36.existsSync(path39.join(dir, "yarn.lock"))) return "yarn";
24049
- if (fs36.existsSync(path39.join(dir, "bun.lockb"))) return "bun";
24050
- if (fs36.existsSync(path39.join(dir, "package-lock.json"))) return "npm";
24236
+ if (fs37.existsSync(path39.join(dir, "pnpm-lock.yaml"))) return "pnpm";
24237
+ if (fs37.existsSync(path39.join(dir, "yarn.lock"))) return "yarn";
24238
+ if (fs37.existsSync(path39.join(dir, "bun.lockb"))) return "bun";
24239
+ if (fs37.existsSync(path39.join(dir, "package-lock.json"))) return "npm";
24051
24240
  return "unknown";
24052
24241
  }
24053
24242
  function getProjectType(dir) {
24054
24243
  try {
24055
- const files = fs36.readdirSync(dir);
24244
+ const files = fs37.readdirSync(dir);
24056
24245
  if (files.includes("package.json")) {
24057
- const pkg = JSON.parse(fs36.readFileSync(path39.join(dir, "package.json"), "utf-8"));
24246
+ const pkg = JSON.parse(fs37.readFileSync(path39.join(dir, "package.json"), "utf-8"));
24058
24247
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
24059
24248
  if (deps.next) return "Next.js";
24060
24249
  if (deps.react) return "React";
@@ -24075,8 +24264,8 @@ function getProjectType(dir) {
24075
24264
  function getTestFramework(dir) {
24076
24265
  try {
24077
24266
  const pkgPath = path39.join(dir, "package.json");
24078
- if (fs36.existsSync(pkgPath)) {
24079
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
24267
+ if (fs37.existsSync(pkgPath)) {
24268
+ const pkg = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
24080
24269
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
24081
24270
  if (deps.jest) return "jest";
24082
24271
  if (deps.vitest) return "vitest";
@@ -24085,7 +24274,7 @@ function getTestFramework(dir) {
24085
24274
  if (deps["@playwright/test"]) return "playwright";
24086
24275
  if (deps.cypress) return "cypress";
24087
24276
  }
24088
- if (fs36.existsSync(path39.join(dir, "pytest.ini")) || fs36.existsSync(path39.join(dir, "conftest.py"))) return "pytest";
24277
+ if (fs37.existsSync(path39.join(dir, "pytest.ini")) || fs37.existsSync(path39.join(dir, "conftest.py"))) return "pytest";
24089
24278
  return "unknown";
24090
24279
  } catch {
24091
24280
  return "unknown";
@@ -24094,8 +24283,8 @@ function getTestFramework(dir) {
24094
24283
  function getTestCommand(dir) {
24095
24284
  try {
24096
24285
  const pkgPath = path39.join(dir, "package.json");
24097
- if (fs36.existsSync(pkgPath)) {
24098
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
24286
+ if (fs37.existsSync(pkgPath)) {
24287
+ const pkg = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
24099
24288
  if (pkg.scripts?.test) return "npm test";
24100
24289
  if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
24101
24290
  }
@@ -24111,7 +24300,7 @@ function getTestCommand(dir) {
24111
24300
  function isGitRepo(dir) {
24112
24301
  try {
24113
24302
  const p = path39.join(dir, ".git");
24114
- return fs36.existsSync(p) && fs36.lstatSync(p).isDirectory();
24303
+ return fs37.existsSync(p) && fs37.lstatSync(p).isDirectory();
24115
24304
  } catch {
24116
24305
  return false;
24117
24306
  }
@@ -24322,7 +24511,7 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
24322
24511
  const runtimeConfig = getRuntimeConfig();
24323
24512
  const cwd2 = process.cwd();
24324
24513
  const isSandbox = process.env.BLUMA_SANDBOX === "true";
24325
- const env2 = {
24514
+ const env3 = {
24326
24515
  os_type: os23.type(),
24327
24516
  os_version: os23.release(),
24328
24517
  architecture: os23.arch(),
@@ -24347,7 +24536,7 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
24347
24536
  session_id: process.env.BLUMA_SESSION_ID || "unknown",
24348
24537
  workspace_root: cwd2
24349
24538
  };
24350
- const interpolate = (template) => Object.entries(env2).reduce((s, [k, v]) => s.replaceAll(`{${k}}`, v), template);
24539
+ const interpolate = (template) => Object.entries(env3).reduce((s, [k, v]) => s.replaceAll(`{${k}}`, v), template);
24351
24540
  let prompt = interpolate(SYSTEM_PROMPT).replace(
24352
24541
  "<<<BLUMA_WORKSPACE_SNAPSHOT_BODY>>>",
24353
24542
  buildWorkspaceSnapshot(cwd2)
@@ -24366,6 +24555,12 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
24366
24555
  prompt += `
24367
24556
 
24368
24557
  ${SUBJECT_MACHINE_MODEL_XML}`;
24558
+ prompt += `
24559
+
24560
+ ${buildSandboxRuntimeContextBlock(cwd2, _injectedSandboxRuntime ?? void 0)}`;
24561
+ prompt += `
24562
+
24563
+ ${SANDBOX_AUTONOMY_SECTION}`;
24369
24564
  prompt += interpolate(SANDBOX_SECTION);
24370
24565
  if (isFactorAiShPromptEnabled()) {
24371
24566
  prompt += `
@@ -25545,7 +25740,7 @@ var LLMService = class {
25545
25740
  };
25546
25741
 
25547
25742
  // src/app/agent/utils/user_message_images.ts
25548
- import fs37 from "fs";
25743
+ import fs38 from "fs";
25549
25744
  import os25 from "os";
25550
25745
  import path40 from "path";
25551
25746
  import { fileURLToPath as fileURLToPath5 } from "url";
@@ -25625,7 +25820,7 @@ function resolveImagePath(candidate, cwd2) {
25625
25820
  const abs = path40.isAbsolute(expanded) ? path40.normalize(expanded) : path40.normalize(path40.resolve(cwd2, expanded));
25626
25821
  if (!isPathAllowed(abs, cwd2)) return null;
25627
25822
  try {
25628
- if (!fs37.existsSync(abs) || !fs37.statSync(abs).isFile()) return null;
25823
+ if (!fs38.existsSync(abs) || !fs38.statSync(abs).isFile()) return null;
25629
25824
  } catch {
25630
25825
  return null;
25631
25826
  }
@@ -25649,7 +25844,7 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
25649
25844
  const abs = resolveImagePath(s, cwd2);
25650
25845
  if (!abs) return null;
25651
25846
  try {
25652
- const st = fs37.statSync(abs);
25847
+ const st = fs38.statSync(abs);
25653
25848
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
25654
25849
  return null;
25655
25850
  }
@@ -25684,7 +25879,7 @@ function tryPasteChunkAsSingleImagePath(chunk, cwd2) {
25684
25879
  return null;
25685
25880
  }
25686
25881
  try {
25687
- const st = fs37.statSync(abs);
25882
+ const st = fs38.statSync(abs);
25688
25883
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
25689
25884
  return null;
25690
25885
  }
@@ -25713,7 +25908,7 @@ function buildUserMessageContent(raw, cwd2) {
25713
25908
  const abs = resolveImagePath(c, cwd2);
25714
25909
  if (!abs) continue;
25715
25910
  try {
25716
- const st = fs37.statSync(abs);
25911
+ const st = fs38.statSync(abs);
25717
25912
  if (st.size > MAX_IMAGE_BYTES) continue;
25718
25913
  } catch {
25719
25914
  continue;
@@ -25730,7 +25925,7 @@ function buildUserMessageContent(raw, cwd2) {
25730
25925
  }
25731
25926
  const parts = [{ type: "text", text: textPart }];
25732
25927
  for (const abs of resolvedAbs) {
25733
- const buf = fs37.readFileSync(abs);
25928
+ const buf = fs38.readFileSync(abs);
25734
25929
  const b64 = buf.toString("base64");
25735
25930
  const mime = mimeFor(abs);
25736
25931
  parts.push({
@@ -25993,7 +26188,7 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
25993
26188
  }
25994
26189
 
25995
26190
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
25996
- import * as fs38 from "fs";
26191
+ import * as fs39 from "fs";
25997
26192
  import * as path42 from "path";
25998
26193
  import os26 from "os";
25999
26194
  function memoryPath2() {
@@ -26007,18 +26202,18 @@ function uniqTags(a, b) {
26007
26202
  }
26008
26203
  function consolidateCodingMemoryFile() {
26009
26204
  const p = memoryPath2();
26010
- if (!fs38.existsSync(p)) {
26205
+ if (!fs39.existsSync(p)) {
26011
26206
  return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
26012
26207
  }
26013
26208
  const bak = `${p}.bak`;
26014
26209
  try {
26015
- fs38.copyFileSync(p, bak);
26210
+ fs39.copyFileSync(p, bak);
26016
26211
  } catch (e) {
26017
26212
  return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
26018
26213
  }
26019
26214
  let data;
26020
26215
  try {
26021
- data = JSON.parse(fs38.readFileSync(p, "utf-8"));
26216
+ data = JSON.parse(fs39.readFileSync(p, "utf-8"));
26022
26217
  } catch (e) {
26023
26218
  return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
26024
26219
  }
@@ -26053,7 +26248,7 @@ function consolidateCodingMemoryFile() {
26053
26248
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
26054
26249
  };
26055
26250
  try {
26056
- fs38.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
26251
+ fs39.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
26057
26252
  } catch (e) {
26058
26253
  return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
26059
26254
  }
@@ -26560,17 +26755,17 @@ var BluMaToolRunner = class {
26560
26755
  // src/app/agent/session_manager/session_archive.ts
26561
26756
  init_bluma_app_dir();
26562
26757
  import path43 from "path";
26563
- import { promises as fs39 } from "fs";
26758
+ import { promises as fs40 } from "fs";
26564
26759
  async function archivePrunedConversationMessages(sessionId, messages) {
26565
26760
  if (!sessionId || messages.length === 0) {
26566
26761
  return null;
26567
26762
  }
26568
26763
  const appDir = getPreferredAppDir();
26569
26764
  const dir = path43.join(appDir, "sessions", "archive", sessionId);
26570
- await fs39.mkdir(dir, { recursive: true });
26765
+ await fs40.mkdir(dir, { recursive: true });
26571
26766
  const archiveFile = path43.join(dir, `${Date.now()}.jsonl`);
26572
26767
  const lines = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
26573
- await fs39.appendFile(archiveFile, lines, "utf-8");
26768
+ await fs40.appendFile(archiveFile, lines, "utf-8");
26574
26769
  return archiveFile;
26575
26770
  }
26576
26771
 
@@ -27992,7 +28187,7 @@ var BluMaAgent = class {
27992
28187
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
27993
28188
  ...this.compressor.getSnapshot()
27994
28189
  };
27995
- fs40.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
28190
+ fs41.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
27996
28191
  } catch (error) {
27997
28192
  console.error("[Bluma] Failed to persist session synchronously:", error);
27998
28193
  }
@@ -28933,7 +29128,7 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
28933
29128
  }
28934
29129
 
28935
29130
  // src/app/agent/core/memory/session_memory.ts
28936
- import fs41 from "fs";
29131
+ import fs42 from "fs";
28937
29132
  import os29 from "os";
28938
29133
  import path46 from "path";
28939
29134
  import { v4 as uuidv49 } from "uuid";
@@ -28999,15 +29194,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
28999
29194
  );
29000
29195
  unique.sort((a, b) => b.accessCount - a.accessCount);
29001
29196
  const trimmed = unique.slice(0, 200);
29002
- fs41.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
29197
+ fs42.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
29003
29198
  }
29004
29199
  /**
29005
29200
  * Load memories from disk
29006
29201
  */
29007
29202
  async loadMemories() {
29008
29203
  try {
29009
- if (!fs41.existsSync(this.memoryFile)) return [];
29010
- const data = fs41.readFileSync(this.memoryFile, "utf-8");
29204
+ if (!fs42.existsSync(this.memoryFile)) return [];
29205
+ const data = fs42.readFileSync(this.memoryFile, "utf-8");
29011
29206
  return JSON.parse(data);
29012
29207
  } catch {
29013
29208
  return [];
@@ -32102,16 +32297,16 @@ function supportsHyperlinks(options) {
32102
32297
  if (stdoutSupported) {
32103
32298
  return true;
32104
32299
  }
32105
- const env2 = options?.env ?? process.env;
32106
- const termProgram = env2["TERM_PROGRAM"];
32300
+ const env3 = options?.env ?? process.env;
32301
+ const termProgram = env3["TERM_PROGRAM"];
32107
32302
  if (termProgram && ADDITIONAL_HYPERLINK_TERMINALS.includes(termProgram)) {
32108
32303
  return true;
32109
32304
  }
32110
- const lcTerminal = env2["LC_TERMINAL"];
32305
+ const lcTerminal = env3["LC_TERMINAL"];
32111
32306
  if (lcTerminal && ADDITIONAL_HYPERLINK_TERMINALS.includes(lcTerminal)) {
32112
32307
  return true;
32113
32308
  }
32114
- const term = env2["TERM"];
32309
+ const term = env3["TERM"];
32115
32310
  if (term?.includes("kitty")) {
32116
32311
  return true;
32117
32312
  }
@@ -33433,12 +33628,12 @@ function patchToUnifiedDiffText(filePath, patch) {
33433
33628
  }
33434
33629
 
33435
33630
  // src/app/ui/utils/readEditContext.ts
33436
- import { promises as fs42 } from "fs";
33631
+ import { promises as fs43 } from "fs";
33437
33632
  var CHUNK_SIZE = 64 * 1024;
33438
33633
  var CONTEXT_LINES2 = 3;
33439
33634
  async function openForScan(filePath) {
33440
33635
  try {
33441
- return await fs42.open(filePath, "r");
33636
+ return await fs43.open(filePath, "r");
33442
33637
  } catch (e) {
33443
33638
  if (e.code === "ENOENT") return null;
33444
33639
  throw e;
@@ -35088,7 +35283,7 @@ var loadSkillTool = createTool({
35088
35283
  });
35089
35284
 
35090
35285
  // src/app/agent/tools/FileWriteTool/UI.tsx
35091
- import fs43 from "fs";
35286
+ import fs44 from "fs";
35092
35287
  import { diffLines as diffLines3 } from "diff";
35093
35288
  import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
35094
35289
  function getFilePath(args) {
@@ -35103,7 +35298,7 @@ function getFilePath(args) {
35103
35298
  function readExistingFileText(filePath) {
35104
35299
  if (!filePath) return "";
35105
35300
  try {
35106
- return fs43.readFileSync(filePath, "utf-8");
35301
+ return fs44.readFileSync(filePath, "utf-8");
35107
35302
  } catch {
35108
35303
  return "";
35109
35304
  }
@@ -38045,7 +38240,7 @@ import {
38045
38240
 
38046
38241
  // src/app/ui/hooks/useAtCompletion.ts
38047
38242
  import { useEffect as useEffect11, useRef as useRef4, useState as useState13 } from "react";
38048
- import fs44 from "fs";
38243
+ import fs45 from "fs";
38049
38244
  import path51 from "path";
38050
38245
  var MAX_RESULTS3 = 50;
38051
38246
  var DEFAULT_RECURSIVE_DEPTH = 2;
@@ -38080,7 +38275,7 @@ function listPathSuggestions(baseDir, pattern) {
38080
38275
  while (queue.length && results.length < MAX_RESULTS3) {
38081
38276
  const node = queue.shift();
38082
38277
  try {
38083
- const entries = fs44.readdirSync(node.dir, { withFileTypes: true });
38278
+ const entries = fs45.readdirSync(node.dir, { withFileTypes: true });
38084
38279
  for (const entry of entries) {
38085
38280
  if (isIgnoredName(entry.name)) continue;
38086
38281
  const entryAbs = path51.join(node.dir, entry.name);
@@ -38097,7 +38292,7 @@ function listPathSuggestions(baseDir, pattern) {
38097
38292
  }
38098
38293
  }
38099
38294
  } else {
38100
- const entries = fs44.readdirSync(listDir, { withFileTypes: true });
38295
+ const entries = fs45.readdirSync(listDir, { withFileTypes: true });
38101
38296
  for (const entry of entries) {
38102
38297
  if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
38103
38298
  if (isIgnoredName(entry.name)) continue;
@@ -38304,7 +38499,7 @@ var SlashSubmenuInlineComponent = ({ menu }) => {
38304
38499
  var SlashSubmenuInline = memo15(SlashSubmenuInlineComponent);
38305
38500
 
38306
38501
  // src/app/ui/utils/clipboardImage.ts
38307
- import fs45 from "fs";
38502
+ import fs46 from "fs";
38308
38503
  import os32 from "os";
38309
38504
  import path52 from "path";
38310
38505
  import { spawn as spawn5, execFile as execFileCb, execSync as execSync4 } from "child_process";
@@ -38456,14 +38651,14 @@ function resolveHelperBinary(cmd) {
38456
38651
  for (const dir of unixClipboardHelperDirs()) {
38457
38652
  const full = path52.join(dir, cmd);
38458
38653
  try {
38459
- fs45.accessSync(full, fs45.constants.X_OK);
38654
+ fs46.accessSync(full, fs46.constants.X_OK);
38460
38655
  return full;
38461
38656
  } catch {
38462
38657
  }
38463
38658
  }
38464
38659
  for (const dir of unixClipboardHelperDirs()) {
38465
38660
  const full = path52.join(dir, cmd);
38466
- if (fs45.existsSync(full)) {
38661
+ if (fs46.existsSync(full)) {
38467
38662
  return full;
38468
38663
  }
38469
38664
  }
@@ -38508,13 +38703,13 @@ function writeBufferIfImage(baseDir, buf) {
38508
38703
  baseDir,
38509
38704
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
38510
38705
  );
38511
- fs45.writeFileSync(out, buf);
38706
+ fs46.writeFileSync(out, buf);
38512
38707
  return out;
38513
38708
  }
38514
38709
  function unlinkQuiet(p) {
38515
38710
  try {
38516
- if (fs45.existsSync(p)) {
38517
- fs45.unlinkSync(p);
38711
+ if (fs46.existsSync(p)) {
38712
+ fs46.unlinkSync(p);
38518
38713
  }
38519
38714
  } catch {
38520
38715
  }
@@ -38531,12 +38726,12 @@ async function tryDarwinClipboardy(baseDir) {
38531
38726
  return null;
38532
38727
  }
38533
38728
  for (const src of tmpPaths) {
38534
- if (!src || !fs45.existsSync(src)) {
38729
+ if (!src || !fs46.existsSync(src)) {
38535
38730
  continue;
38536
38731
  }
38537
38732
  let st;
38538
38733
  try {
38539
- st = fs45.statSync(src);
38734
+ st = fs46.statSync(src);
38540
38735
  } catch {
38541
38736
  continue;
38542
38737
  }
@@ -38549,7 +38744,7 @@ async function tryDarwinClipboardy(baseDir) {
38549
38744
  baseDir,
38550
38745
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
38551
38746
  );
38552
- fs45.copyFileSync(src, out);
38747
+ fs46.copyFileSync(src, out);
38553
38748
  for (const p of tmpPaths) {
38554
38749
  unlinkQuiet(p);
38555
38750
  }
@@ -38570,7 +38765,7 @@ async function tryWindowsPowerShell(outFile) {
38570
38765
  "v1.0",
38571
38766
  "powershell.exe"
38572
38767
  ) : "powershell.exe";
38573
- if (!fs45.existsSync(ps)) {
38768
+ if (!fs46.existsSync(ps)) {
38574
38769
  return false;
38575
38770
  }
38576
38771
  const script = "$ErrorActionPreference='Stop';Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$img=[System.Windows.Forms.Clipboard]::GetImage();if($null -eq $img){exit 2};$img.Save($env:BLUMA_CLIP_OUT,[System.Drawing.Imaging.ImageFormat]::Png);exit 0";
@@ -38592,7 +38787,7 @@ async function tryWindowsPowerShell(outFile) {
38592
38787
  return false;
38593
38788
  }
38594
38789
  try {
38595
- const st = fs45.statSync(outFile);
38790
+ const st = fs46.statSync(outFile);
38596
38791
  return st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES;
38597
38792
  } catch {
38598
38793
  return false;
@@ -38679,8 +38874,8 @@ async function tryClipboardTextAsImageFile(baseDir) {
38679
38874
  const abs = parseClipboardTextAsImagePath(t);
38680
38875
  if (!abs) return null;
38681
38876
  try {
38682
- if (!fs45.existsSync(abs)) return null;
38683
- const st = fs45.statSync(abs);
38877
+ if (!fs46.existsSync(abs)) return null;
38878
+ const st = fs46.statSync(abs);
38684
38879
  if (!st.isFile() || st.size > CLIPBOARD_MAX_BYTES || st.size < 20) return null;
38685
38880
  } catch {
38686
38881
  return null;
@@ -38691,7 +38886,7 @@ async function tryClipboardTextAsImageFile(baseDir) {
38691
38886
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
38692
38887
  );
38693
38888
  try {
38694
- fs45.copyFileSync(abs, out);
38889
+ fs46.copyFileSync(abs, out);
38695
38890
  return out;
38696
38891
  } catch {
38697
38892
  return null;
@@ -38723,11 +38918,11 @@ printf '%s' "$OUT"
38723
38918
  maxBuffer: 4096
38724
38919
  });
38725
38920
  const written = String(stdout ?? "").trim();
38726
- if (written !== outPath || !fs45.existsSync(outPath)) {
38921
+ if (written !== outPath || !fs46.existsSync(outPath)) {
38727
38922
  unlinkQuiet(outPath);
38728
38923
  return null;
38729
38924
  }
38730
- const buf = fs45.readFileSync(outPath);
38925
+ const buf = fs46.readFileSync(outPath);
38731
38926
  unlinkQuiet(outPath);
38732
38927
  return writeBufferIfImage(baseDir, buf);
38733
38928
  } catch {
@@ -38748,8 +38943,8 @@ async function tryNativeClipboardImage() {
38748
38943
  }
38749
38944
  try {
38750
38945
  const result = readClipboardImageNative();
38751
- if (fs45.existsSync(result.path)) {
38752
- const st = fs45.statSync(result.path);
38946
+ if (fs46.existsSync(result.path)) {
38947
+ const st = fs46.statSync(result.path);
38753
38948
  if (st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES) {
38754
38949
  return result.path;
38755
38950
  }
@@ -38760,7 +38955,7 @@ async function tryNativeClipboardImage() {
38760
38955
  }
38761
38956
  async function readClipboardImageToTempFile() {
38762
38957
  const baseDir = path52.join(os32.homedir(), ".cache", "bluma", "clipboard");
38763
- fs45.mkdirSync(baseDir, { recursive: true });
38958
+ fs46.mkdirSync(baseDir, { recursive: true });
38764
38959
  const nativeResult = await tryNativeClipboardImage();
38765
38960
  if (nativeResult) {
38766
38961
  return nativeResult;
@@ -38819,7 +39014,7 @@ function expandLargePastePlaceholder(value, pending) {
38819
39014
  }
38820
39015
 
38821
39016
  // src/app/ui/components/InputPrompt.tsx
38822
- import fs46 from "fs";
39017
+ import fs47 from "fs";
38823
39018
  import { Fragment as Fragment7, jsx as jsx79, jsxs as jsxs62 } from "react/jsx-runtime";
38824
39019
  var persistedInputState = { text: "", cursorPosition: 0 };
38825
39020
  var StaticCursor = () => /* @__PURE__ */ jsx79(Box_default, { flexDirection: "row", flexWrap: "nowrap", children: /* @__PURE__ */ jsx79(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "\u2588 " }) });
@@ -38986,7 +39181,7 @@ var InputPrompt = memo16(({
38986
39181
  return;
38987
39182
  }
38988
39183
  if (routeText.startsWith("/")) {
38989
- const isFilePath = map.size > 0 && fs46.existsSync(routeText.split(/\s+/)[0]);
39184
+ const isFilePath = map.size > 0 && fs47.existsSync(routeText.split(/\s+/)[0]);
38990
39185
  if (isFilePath) {
38991
39186
  uiEventBus.emit("user_overlay", {
38992
39187
  kind: "message",
@@ -41155,7 +41350,7 @@ var renderSettingsEditUsage = () => {
41155
41350
  // src/app/agent/core/thread/thread_store.ts
41156
41351
  init_bluma_app_dir();
41157
41352
  import path53 from "path";
41158
- import { promises as fs47 } from "fs";
41353
+ import { promises as fs48 } from "fs";
41159
41354
  import { randomUUID as randomUUID2 } from "crypto";
41160
41355
  var INDEX_VERSION = 1;
41161
41356
  var fileLocks2 = /* @__PURE__ */ new Map();
@@ -41190,10 +41385,10 @@ var ThreadStore = class {
41190
41385
  * Inicializa o diretório de threads
41191
41386
  */
41192
41387
  async initialize() {
41193
- await fs47.mkdir(this.threadsDir, { recursive: true });
41194
- await fs47.mkdir(this.archiveDir, { recursive: true });
41388
+ await fs48.mkdir(this.threadsDir, { recursive: true });
41389
+ await fs48.mkdir(this.archiveDir, { recursive: true });
41195
41390
  try {
41196
- await fs47.access(this.indexPath);
41391
+ await fs48.access(this.indexPath);
41197
41392
  } catch {
41198
41393
  await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
41199
41394
  }
@@ -41222,7 +41417,7 @@ var ThreadStore = class {
41222
41417
  async loadIndex() {
41223
41418
  return withFileLock2(this.indexPath, async () => {
41224
41419
  try {
41225
- const content = await fs47.readFile(this.indexPath, "utf-8");
41420
+ const content = await fs48.readFile(this.indexPath, "utf-8");
41226
41421
  return JSON.parse(content);
41227
41422
  } catch {
41228
41423
  return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
@@ -41233,8 +41428,8 @@ var ThreadStore = class {
41233
41428
  return withFileLock2(this.indexPath, async () => {
41234
41429
  index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
41235
41430
  const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
41236
- await fs47.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
41237
- await fs47.rename(tempPath, this.indexPath);
41431
+ await fs48.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
41432
+ await fs48.rename(tempPath, this.indexPath);
41238
41433
  });
41239
41434
  }
41240
41435
  // ==================== Git Info ====================
@@ -41304,7 +41499,7 @@ var ThreadStore = class {
41304
41499
  messages: params.initialMessages || []
41305
41500
  };
41306
41501
  const historyPath = this.buildDatedThreadHistoryPath(threadId);
41307
- await fs47.mkdir(path53.dirname(historyPath), { recursive: true });
41502
+ await fs48.mkdir(path53.dirname(historyPath), { recursive: true });
41308
41503
  await this.saveHistoryAtPath(historyPath, history);
41309
41504
  const index = await this.loadIndex();
41310
41505
  index.threads.unshift({
@@ -41359,7 +41554,7 @@ var ThreadStore = class {
41359
41554
  compressedSliceCount: source.history.compressedSliceCount
41360
41555
  };
41361
41556
  const historyPath = this.buildDatedThreadHistoryPath(newThreadId);
41362
- await fs47.mkdir(path53.dirname(historyPath), { recursive: true });
41557
+ await fs48.mkdir(path53.dirname(historyPath), { recursive: true });
41363
41558
  await this.saveHistoryAtPath(historyPath, history);
41364
41559
  const index = await this.loadIndex();
41365
41560
  index.threads.unshift({
@@ -41434,7 +41629,7 @@ var ThreadStore = class {
41434
41629
  const oldPath = entry.historyPath || this.getLegacyHistoryPath(threadId);
41435
41630
  const newPath = path53.join(this.archiveDir, `${threadId}.jsonl`);
41436
41631
  try {
41437
- await fs47.rename(oldPath, newPath);
41632
+ await fs48.rename(oldPath, newPath);
41438
41633
  } catch (e) {
41439
41634
  if (e.code !== "ENOENT") throw e;
41440
41635
  }
@@ -41457,9 +41652,9 @@ var ThreadStore = class {
41457
41652
  if (entry.status === "active") return true;
41458
41653
  const oldPath = path53.join(this.archiveDir, `${threadId}.jsonl`);
41459
41654
  const newPath = this.buildDatedThreadHistoryPath(threadId);
41460
- await fs47.mkdir(path53.dirname(newPath), { recursive: true });
41655
+ await fs48.mkdir(path53.dirname(newPath), { recursive: true });
41461
41656
  try {
41462
- await fs47.rename(oldPath, newPath);
41657
+ await fs48.rename(oldPath, newPath);
41463
41658
  } catch (e) {
41464
41659
  if (e.code !== "ENOENT") throw e;
41465
41660
  }
@@ -41480,7 +41675,7 @@ var ThreadStore = class {
41480
41675
  if (entryIndex === -1) return false;
41481
41676
  const entry = index.threads[entryIndex];
41482
41677
  try {
41483
- await fs47.unlink(entry.historyPath);
41678
+ await fs48.unlink(entry.historyPath);
41484
41679
  } catch {
41485
41680
  }
41486
41681
  index.threads.splice(entryIndex, 1);
@@ -41504,14 +41699,14 @@ var ThreadStore = class {
41504
41699
  const entry = index.threads.find((t) => t.threadId === threadId);
41505
41700
  if (entry?.historyPath) {
41506
41701
  try {
41507
- await fs47.access(entry.historyPath);
41702
+ await fs48.access(entry.historyPath);
41508
41703
  return entry.historyPath;
41509
41704
  } catch {
41510
41705
  }
41511
41706
  }
41512
41707
  const legacy = this.getLegacyHistoryPath(threadId);
41513
41708
  try {
41514
- await fs47.access(legacy);
41709
+ await fs48.access(legacy);
41515
41710
  return legacy;
41516
41711
  } catch {
41517
41712
  return entry?.historyPath ?? legacy;
@@ -41528,9 +41723,9 @@ var ThreadStore = class {
41528
41723
  for (const msg of history.messages) {
41529
41724
  lines.push(JSON.stringify({ type: "message", ...msg }));
41530
41725
  }
41531
- await fs47.mkdir(path53.dirname(historyPath), { recursive: true }).catch(() => {
41726
+ await fs48.mkdir(path53.dirname(historyPath), { recursive: true }).catch(() => {
41532
41727
  });
41533
- await fs47.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
41728
+ await fs48.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
41534
41729
  }
41535
41730
  /**
41536
41731
  * Guarda o histórico de uma thread
@@ -41552,7 +41747,7 @@ var ThreadStore = class {
41552
41747
  ].filter((p, i, arr) => Boolean(p) && arr.indexOf(p) === i);
41553
41748
  for (const historyPath of pathsToTry) {
41554
41749
  try {
41555
- const content = await fs47.readFile(historyPath, "utf-8");
41750
+ const content = await fs48.readFile(historyPath, "utf-8");
41556
41751
  const lines = content.split("\n").filter(Boolean);
41557
41752
  const history = {
41558
41753
  threadId,
@@ -41587,7 +41782,7 @@ var ThreadStore = class {
41587
41782
  const entry = index.threads.find((t) => t.threadId === threadId);
41588
41783
  if (!entry) throw new Error(`Thread not found: ${threadId}`);
41589
41784
  const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
41590
- await fs47.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
41785
+ await fs48.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
41591
41786
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
41592
41787
  await this.saveIndex(index);
41593
41788
  }
@@ -44025,15 +44220,15 @@ import semverGt from "semver/functions/gt.js";
44025
44220
  import semverValid from "semver/functions/valid.js";
44026
44221
  import { fileURLToPath as fileURLToPath7 } from "url";
44027
44222
  import path55 from "path";
44028
- import fs49 from "fs";
44223
+ import fs50 from "fs";
44029
44224
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
44030
44225
  function findBlumaPackageJson(startDir) {
44031
44226
  let dir = startDir;
44032
44227
  for (let i = 0; i < 12; i++) {
44033
44228
  const candidate = path55.join(dir, "package.json");
44034
- if (fs49.existsSync(candidate)) {
44229
+ if (fs50.existsSync(candidate)) {
44035
44230
  try {
44036
- const raw = fs49.readFileSync(candidate, "utf8");
44231
+ const raw = fs50.readFileSync(candidate, "utf8");
44037
44232
  const parsed = JSON.parse(raw);
44038
44233
  if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
44039
44234
  return { name: parsed.name, version: String(parsed.version) };
@@ -44064,8 +44259,8 @@ function resolveInstalledBlumaPackage() {
44064
44259
  if (argv1 && !argv1.startsWith("-")) {
44065
44260
  try {
44066
44261
  let resolved = argv1;
44067
- if (path55.isAbsolute(argv1) && fs49.existsSync(argv1)) {
44068
- resolved = fs49.realpathSync(argv1);
44262
+ if (path55.isAbsolute(argv1) && fs50.existsSync(argv1)) {
44263
+ resolved = fs50.realpathSync(argv1);
44069
44264
  } else {
44070
44265
  resolved = path55.resolve(process.cwd(), argv1);
44071
44266
  }
@@ -44888,16 +45083,16 @@ function usePlanMode() {
44888
45083
 
44889
45084
  // src/app/hooks/useAgentMode.ts
44890
45085
  import { useState as useState22, useEffect as useEffect21, useCallback as useCallback9 } from "react";
44891
- import * as fs50 from "fs";
45086
+ import * as fs51 from "fs";
44892
45087
  import * as path56 from "path";
44893
45088
  import { homedir as homedir4 } from "os";
44894
45089
  var SETTINGS_PATH = path56.join(homedir4(), ".bluma", "settings.json");
44895
45090
  function readAgentModeFromFile() {
44896
45091
  try {
44897
- if (!fs50.existsSync(SETTINGS_PATH)) {
45092
+ if (!fs51.existsSync(SETTINGS_PATH)) {
44898
45093
  return "default";
44899
45094
  }
44900
- const content = fs50.readFileSync(SETTINGS_PATH, "utf-8");
45095
+ const content = fs51.readFileSync(SETTINGS_PATH, "utf-8");
44901
45096
  const settings = JSON.parse(content);
44902
45097
  return settings.agentMode || "default";
44903
45098
  } catch (error) {
@@ -44916,16 +45111,16 @@ function useAgentMode() {
44916
45111
  }, []);
44917
45112
  const updateAgentMode = useCallback9((mode) => {
44918
45113
  try {
44919
- if (!fs50.existsSync(SETTINGS_PATH)) {
44920
- fs50.mkdirSync(path56.dirname(SETTINGS_PATH), { recursive: true });
45114
+ if (!fs51.existsSync(SETTINGS_PATH)) {
45115
+ fs51.mkdirSync(path56.dirname(SETTINGS_PATH), { recursive: true });
44921
45116
  }
44922
45117
  let settings = {};
44923
- if (fs50.existsSync(SETTINGS_PATH)) {
44924
- const content = fs50.readFileSync(SETTINGS_PATH, "utf-8");
45118
+ if (fs51.existsSync(SETTINGS_PATH)) {
45119
+ const content = fs51.readFileSync(SETTINGS_PATH, "utf-8");
44925
45120
  settings = JSON.parse(content);
44926
45121
  }
44927
45122
  settings.agentMode = mode;
44928
- fs50.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
45123
+ fs51.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
44929
45124
  setAgentMode(mode);
44930
45125
  } catch (error) {
44931
45126
  console.error("Failed to update agent mode:", error);
@@ -46142,7 +46337,7 @@ Dados do utilizador final / pedido \u2014 n\xE3o confundir com o sandbox onde o
46142
46337
  ${JSON.stringify(extra, null, 2)}
46143
46338
  </${label}>`;
46144
46339
  }
46145
- function buildAgentTurnUserContent(context) {
46340
+ function buildAgentTurnUserContent(context, sandboxRuntime) {
46146
46341
  if (context == null) return "";
46147
46342
  if (typeof context === "string") {
46148
46343
  return enrichSandboxUserTurn(context.trim());
@@ -46161,6 +46356,9 @@ function buildAgentTurnUserContent(context) {
46161
46356
  const extra = extractExtraContextFields(ctx);
46162
46357
  const hasMachineSpecs = looksLikeMachineSpecs(extra.machine_specs) || looksLikeMachineSpecs(extra.machineSpecs) || looksLikeMachineSpecs(extra.client_machine) || looksLikeMachineSpecs(extra.target_machine) || looksLikeMachineSpecs(extra.hardware) || Object.values(extra).some(looksLikeMachineSpecs);
46163
46358
  const parts = [];
46359
+ if (sandboxRuntime && typeof sandboxRuntime === "object") {
46360
+ parts.push(formatSandboxRuntimeUserTurnPrefix(sandboxRuntime));
46361
+ }
46164
46362
  if (userRequest) parts.push(userRequest);
46165
46363
  if (coordinatorText) {
46166
46364
  parts.push(`<contexto_adicional>
@@ -46190,7 +46388,7 @@ import { memo as memo26, useCallback as useCallback11, useEffect as useEffect23,
46190
46388
  // src/app/agent/session_manager/session_resume_browser.ts
46191
46389
  init_bluma_app_dir();
46192
46390
  import path57 from "path";
46193
- import { promises as fs51 } from "fs";
46391
+ import { promises as fs52 } from "fs";
46194
46392
  function getSessionsRoot() {
46195
46393
  return path57.join(getPreferredAppDir(), "sessions");
46196
46394
  }
@@ -46213,9 +46411,9 @@ async function sessionEntryFromFile(absPath, sessionId) {
46213
46411
  let preview = "(no messages)";
46214
46412
  let lastActivityMs = Date.now();
46215
46413
  try {
46216
- const st = await fs51.stat(absPath);
46414
+ const st = await fs52.stat(absPath);
46217
46415
  lastActivityMs = st.mtimeMs;
46218
- const raw = await fs51.readFile(absPath, "utf-8");
46416
+ const raw = await fs52.readFile(absPath, "utf-8");
46219
46417
  const data = JSON.parse(raw);
46220
46418
  preview = previewFromHistory(data.conversation_history);
46221
46419
  const iso = data.last_updated || data.created_at;
@@ -46250,8 +46448,8 @@ async function listSessionBrowserEntries(cwdRel) {
46250
46448
  }
46251
46449
  let dirents;
46252
46450
  try {
46253
- await fs51.mkdir(absDir, { recursive: true });
46254
- dirents = await fs51.readdir(absDir, { withFileTypes: true });
46451
+ await fs52.mkdir(absDir, { recursive: true });
46452
+ dirents = await fs52.readdir(absDir, { withFileTypes: true });
46255
46453
  } catch {
46256
46454
  return out;
46257
46455
  }
@@ -46635,9 +46833,9 @@ async function runAgentMode() {
46635
46833
  try {
46636
46834
  if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
46637
46835
  const filePath = args[inputFileIndex + 1];
46638
- rawPayload = fs52.readFileSync(filePath, "utf-8");
46836
+ rawPayload = fs53.readFileSync(filePath, "utf-8");
46639
46837
  } else {
46640
- rawPayload = fs52.readFileSync(0, "utf-8");
46838
+ rawPayload = fs53.readFileSync(0, "utf-8");
46641
46839
  }
46642
46840
  } catch (err) {
46643
46841
  writeAgentEvent(registrySessionId, {
@@ -46689,6 +46887,14 @@ async function runAgentMode() {
46689
46887
  if (envelopeUserRequest.trim()) {
46690
46888
  process.env.BLUMA_USER_REQUEST = envelopeUserRequest.trim();
46691
46889
  }
46890
+ if (envelope.from_agent?.trim()) {
46891
+ process.env.BLUMA_FROM_AGENT = envelope.from_agent.trim();
46892
+ }
46893
+ if (envelope.action?.trim()) {
46894
+ process.env.BLUMA_ACTION = envelope.action.trim();
46895
+ }
46896
+ const injectedRuntime = applySandboxRuntimeFromEnvelope(envelope);
46897
+ setInjectedSandboxRuntimeForPrompt(injectedRuntime);
46692
46898
  const eventBus = new EventEmitter7();
46693
46899
  const sessionId = registrySessionId || envelope.session_id || envelope.message_id || uuidv412();
46694
46900
  process.env.BLUMA_SESSION_ID = sessionId;
@@ -46831,7 +47037,7 @@ async function runAgentMode() {
46831
47037
  const agent = new Agent(sessionId, eventBus);
46832
47038
  agentRef = agent;
46833
47039
  await agent.initialize();
46834
- const userContent = buildAgentTurnUserContent(envelope.context) || JSON.stringify({
47040
+ const userContent = buildAgentTurnUserContent(envelope.context, injectedRuntime) || JSON.stringify({
46835
47041
  message_id: envelope.message_id || sessionId,
46836
47042
  from_agent: envelope.from_agent || "unknown",
46837
47043
  to_agent: envelope.to_agent || "bluma",
@@ -46880,7 +47086,7 @@ function readCliPackageVersion() {
46880
47086
  try {
46881
47087
  const base = path58.dirname(fileURLToPath8(import.meta.url));
46882
47088
  const pkgPath = path58.join(base, "..", "package.json");
46883
- const j = JSON.parse(fs52.readFileSync(pkgPath, "utf8"));
47089
+ const j = JSON.parse(fs53.readFileSync(pkgPath, "utf8"));
46884
47090
  return String(j.version || "0.0.0");
46885
47091
  } catch {
46886
47092
  return "0.0.0";
@@ -47006,7 +47212,7 @@ function startBackgroundAgent() {
47006
47212
  process.exit(1);
47007
47213
  }
47008
47214
  const filePath = args[inputFileIndex + 1];
47009
- const rawPayload = fs52.readFileSync(filePath, "utf-8");
47215
+ const rawPayload = fs53.readFileSync(filePath, "utf-8");
47010
47216
  const envelope = JSON.parse(rawPayload);
47011
47217
  const sessionId = envelope.session_id || envelope.message_id || uuidv412();
47012
47218
  registerSession({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomad-e/bluma-cli",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "BluMa independent agent for automation and advanced software engineering.",
5
5
  "author": "Alex Fonseca",
6
6
  "license": "Apache-2.0",