@nomad-e/bluma-cli 0.18.0 → 0.20.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.
package/dist/main.js CHANGED
@@ -3204,8 +3204,8 @@ __export(exportConversation_exports, {
3204
3204
  formatConversationMarkdown: () => formatConversationMarkdown,
3205
3205
  resolveExportOutputPath: () => resolveExportOutputPath
3206
3206
  });
3207
- import { promises as fs48 } from "fs";
3208
- import path54 from "path";
3207
+ import { promises as fs49 } from "fs";
3208
+ import path55 from "path";
3209
3209
  function extractTextParts2(content) {
3210
3210
  if (typeof content === "string") {
3211
3211
  const trimmed = content.trim();
@@ -3363,12 +3363,12 @@ function resolveExportOutputPath(sessionId, options) {
3363
3363
  const { outputPath, defaultExportDir } = options ?? {};
3364
3364
  if (outputPath?.trim()) {
3365
3365
  const raw = outputPath.trim();
3366
- return path54.isAbsolute(raw) ? raw : path54.resolve(process.cwd(), raw);
3366
+ return path55.isAbsolute(raw) ? raw : path55.resolve(process.cwd(), raw);
3367
3367
  }
3368
3368
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
3369
3369
  const safeId = sessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 48) || "session";
3370
3370
  const baseDir = defaultExportDir ?? getSandboxPolicy().workspaceRoot;
3371
- return path54.join(baseDir, `bluma-export-${safeId}-${stamp}.md`);
3371
+ return path55.join(baseDir, `bluma-export-${safeId}-${stamp}.md`);
3372
3372
  }
3373
3373
  async function resolveHistory(sessionId, fallbackHistory) {
3374
3374
  const loaded2 = await loadSession(sessionId);
@@ -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(path55.dirname(filePath), { recursive: true });
3394
+ await fs49.writeFile(filePath, markdown, "utf-8");
3395
3395
  return {
3396
3396
  success: true,
3397
3397
  filePath,
@@ -3916,11 +3916,11 @@ var NodeFsOperations = {
3916
3916
  if (fd) fs.closeSync(fd);
3917
3917
  }
3918
3918
  },
3919
- appendFileSync(path59, data, options) {
3920
- const _ = slowLogging`fs.appendFileSync(${path59}, ${data.length} chars)`;
3919
+ appendFileSync(path60, data, options) {
3920
+ const _ = slowLogging`fs.appendFileSync(${path60}, ${data.length} chars)`;
3921
3921
  if (options?.mode !== void 0) {
3922
3922
  try {
3923
- const fd = fs.openSync(path59, "ax", options.mode);
3923
+ const fd = fs.openSync(path60, "ax", options.mode);
3924
3924
  try {
3925
3925
  fs.appendFileSync(fd, data);
3926
3926
  } finally {
@@ -3931,35 +3931,35 @@ var NodeFsOperations = {
3931
3931
  if (getErrnoCode(e) !== "EEXIST") throw e;
3932
3932
  }
3933
3933
  }
3934
- fs.appendFileSync(path59, data);
3934
+ fs.appendFileSync(path60, data);
3935
3935
  },
3936
3936
  copyFileSync(src, dest) {
3937
3937
  const _ = slowLogging`fs.copyFileSync(${src} → ${dest})`;
3938
3938
  fs.copyFileSync(src, dest);
3939
3939
  },
3940
- unlinkSync(path59) {
3941
- const _ = slowLogging`fs.unlinkSync(${path59})`;
3942
- fs.unlinkSync(path59);
3940
+ unlinkSync(path60) {
3941
+ const _ = slowLogging`fs.unlinkSync(${path60})`;
3942
+ fs.unlinkSync(path60);
3943
3943
  },
3944
3944
  renameSync(oldPath, newPath) {
3945
3945
  const _ = slowLogging`fs.renameSync(${oldPath} → ${newPath})`;
3946
3946
  fs.renameSync(oldPath, newPath);
3947
3947
  },
3948
- linkSync(target, path59) {
3949
- const _ = slowLogging`fs.linkSync(${target} → ${path59})`;
3950
- fs.linkSync(target, path59);
3948
+ linkSync(target, path60) {
3949
+ const _ = slowLogging`fs.linkSync(${target} → ${path60})`;
3950
+ fs.linkSync(target, path60);
3951
3951
  },
3952
- symlinkSync(target, path59, type) {
3953
- const _ = slowLogging`fs.symlinkSync(${target} → ${path59})`;
3954
- fs.symlinkSync(target, path59, type);
3952
+ symlinkSync(target, path60, type) {
3953
+ const _ = slowLogging`fs.symlinkSync(${target} → ${path60})`;
3954
+ fs.symlinkSync(target, path60, type);
3955
3955
  },
3956
- readlinkSync(path59) {
3957
- const _ = slowLogging`fs.readlinkSync(${path59})`;
3958
- return fs.readlinkSync(path59);
3956
+ readlinkSync(path60) {
3957
+ const _ = slowLogging`fs.readlinkSync(${path60})`;
3958
+ return fs.readlinkSync(path60);
3959
3959
  },
3960
- realpathSync(path59) {
3961
- const _ = slowLogging`fs.realpathSync(${path59})`;
3962
- return fs.realpathSync(path59).normalize("NFC");
3960
+ realpathSync(path60) {
3961
+ const _ = slowLogging`fs.realpathSync(${path60})`;
3962
+ return fs.realpathSync(path60).normalize("NFC");
3963
3963
  },
3964
3964
  mkdirSync(dirPath, options) {
3965
3965
  const _ = slowLogging`fs.mkdirSync(${dirPath})`;
@@ -3992,12 +3992,12 @@ var NodeFsOperations = {
3992
3992
  const _ = slowLogging`fs.rmdirSync(${dirPath})`;
3993
3993
  fs.rmdirSync(dirPath);
3994
3994
  },
3995
- rmSync(path59, options) {
3996
- const _ = slowLogging`fs.rmSync(${path59})`;
3997
- fs.rmSync(path59, options);
3995
+ rmSync(path60, options) {
3996
+ const _ = slowLogging`fs.rmSync(${path60})`;
3997
+ fs.rmSync(path60, options);
3998
3998
  },
3999
- createWriteStream(path59) {
4000
- return fs.createWriteStream(path59);
3999
+ createWriteStream(path60) {
4000
+ return fs.createWriteStream(path60);
4001
4001
  },
4002
4002
  async readFileBytes(fsPath, maxBytes) {
4003
4003
  if (maxBytes === void 0) {
@@ -4104,12 +4104,12 @@ function shouldLogDebugMessage(message2) {
4104
4104
  var hasFormattedOutput = false;
4105
4105
  var debugWriter = null;
4106
4106
  var pendingWrite = Promise.resolve();
4107
- async function appendAsync(needMkdir, dir, path59, content) {
4107
+ async function appendAsync(needMkdir, dir, path60, content) {
4108
4108
  if (needMkdir) {
4109
4109
  await mkdir(dir, { recursive: true }).catch(() => {
4110
4110
  });
4111
4111
  }
4112
- await appendFile(path59, content);
4112
+ await appendFile(path60, content);
4113
4113
  void updateLatestDebugLogSymlink();
4114
4114
  }
4115
4115
  function noop() {
@@ -4119,8 +4119,8 @@ function getDebugWriter() {
4119
4119
  let ensuredDir = null;
4120
4120
  debugWriter = createBufferedWriter({
4121
4121
  writeFn: (content) => {
4122
- const path59 = getDebugLogPath();
4123
- const dir = dirname(path59);
4122
+ const path60 = getDebugLogPath();
4123
+ const dir = dirname(path60);
4124
4124
  const needMkdir = ensuredDir !== dir;
4125
4125
  ensuredDir = dir;
4126
4126
  if (isDebugMode()) {
@@ -4130,11 +4130,11 @@ function getDebugWriter() {
4130
4130
  } catch {
4131
4131
  }
4132
4132
  }
4133
- getFsImplementation().appendFileSync(path59, content);
4133
+ getFsImplementation().appendFileSync(path60, content);
4134
4134
  void updateLatestDebugLogSymlink();
4135
4135
  return;
4136
4136
  }
4137
- pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path59, content)).catch(noop);
4137
+ pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path60, content)).catch(noop);
4138
4138
  },
4139
4139
  flushIntervalMs: 1e3,
4140
4140
  maxBufferSize: 100,
@@ -10321,8 +10321,8 @@ import codeExcerpt from "code-excerpt";
10321
10321
  import { readFileSync as readFileSync2 } from "fs";
10322
10322
  import StackUtils from "stack-utils";
10323
10323
  import { jsx as jsx6, jsxs } from "react/jsx-runtime";
10324
- var cleanupPath = (path59) => {
10325
- return path59?.replace(`file://${process.cwd()}/`, "");
10324
+ var cleanupPath = (path60) => {
10325
+ return path60?.replace(`file://${process.cwd()}/`, "");
10326
10326
  };
10327
10327
  var stackUtils;
10328
10328
  function getStackUtils() {
@@ -14299,8 +14299,8 @@ var getInstance = (stdout, createInstance) => {
14299
14299
 
14300
14300
  // src/main.ts
14301
14301
  import { EventEmitter as EventEmitter7 } from "events";
14302
- import fs52 from "fs";
14303
- import path58 from "path";
14302
+ import fs53 from "fs";
14303
+ import path59 from "path";
14304
14304
  import { fileURLToPath as fileURLToPath8 } from "url";
14305
14305
  import { spawn as spawn6 } from "child_process";
14306
14306
  import { v4 as uuidv412 } from "uuid";
@@ -14979,7 +14979,7 @@ function cancelSlashSubmenu() {
14979
14979
 
14980
14980
  // src/app/agent/agent.ts
14981
14981
  import * as dotenv from "dotenv";
14982
- import path48 from "path";
14982
+ import path49 from "path";
14983
14983
  import os30 from "os";
14984
14984
 
14985
14985
  // src/app/agent/tool_invoker.ts
@@ -20269,7 +20269,7 @@ async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
20269
20269
  name: data.name,
20270
20270
  status: data.status || "building",
20271
20271
  url: appContext.appUrl || severinoUrl.replace(/\/$/, "") + `/app/${resolvedAppId}`,
20272
- message: data.message || "Deploy iniciado",
20272
+ message: data.message || "Deploy iniciado. Chama factorai.sh.get_app_status({ appId, wait: true }) at\xE9 status ready ou failed; se failed, l\xEA typescriptErrors/buildLogTail e corrige o c\xF3digo.",
20273
20273
  isRedeploy: !!appId,
20274
20274
  appContext
20275
20275
  };
@@ -20413,6 +20413,102 @@ async function deployApp(args) {
20413
20413
  }
20414
20414
  }
20415
20415
 
20416
+ // src/app/agent/runtime/factorai_deploy_feedback.ts
20417
+ var FAILED_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "build_failed"]);
20418
+ var READY_STATUSES = /* @__PURE__ */ new Set(["ready", "running", "live"]);
20419
+ var BUILDING_STATUSES = /* @__PURE__ */ new Set(["building", "deploying", "installing", "pending"]);
20420
+ function extractTypeScriptErrors(logText) {
20421
+ if (!logText.trim()) return [];
20422
+ const lines = logText.split("\n");
20423
+ const blocks = [];
20424
+ for (let i = 0; i < lines.length; i++) {
20425
+ const line = lines[i];
20426
+ if (line.includes("Type error:") || line.includes("Failed to compile") || /^\s*>\s*\d+\s*\|/.test(line)) {
20427
+ const start = Math.max(0, i - 3);
20428
+ const end = Math.min(lines.length, i + 8);
20429
+ blocks.push(lines.slice(start, end).join("\n").trim());
20430
+ }
20431
+ }
20432
+ return [...new Set(blocks)].slice(0, 6);
20433
+ }
20434
+ function normalizeFactorAiAppStatus(raw, appId) {
20435
+ const envelope = raw && typeof raw === "object" ? raw : {};
20436
+ const data = envelope.data && typeof envelope.data === "object" && !Array.isArray(envelope.data) ? envelope.data : envelope;
20437
+ const status = String(data.status ?? "unknown").toLowerCase();
20438
+ const logs = typeof data.logs === "string" ? data.logs : "";
20439
+ const logTail = logs.length > 12e3 ? logs.slice(-12e3) : logs;
20440
+ const lastError = typeof data.lastError === "string" && data.lastError || typeof data.error === "string" && data.error || void 0;
20441
+ const slug = typeof data.slug === "string" ? data.slug : void 0;
20442
+ const url = typeof data.url === "string" ? data.url : void 0;
20443
+ const tsErrors = extractTypeScriptErrors(logTail);
20444
+ if (FAILED_STATUSES.has(status) || lastError) {
20445
+ const error = lastError || tsErrors[0] || (logTail.includes("next build falhou") ? logTail.split("\n").slice(-5).join("\n") : "") || "Deploy/build failed on FactorAI.sh";
20446
+ return {
20447
+ success: false,
20448
+ terminal: true,
20449
+ status: "failed",
20450
+ appId,
20451
+ slug,
20452
+ url,
20453
+ error,
20454
+ buildLogTail: logTail || void 0,
20455
+ typescriptErrors: tsErrors.length > 0 ? tsErrors : void 0,
20456
+ agentGuidance: "O build falhou no servidor. Corrige os erros TypeScript/compila\xE7\xE3o no projeto local (ex.: components/ui/card.tsx), confirma com npx tsc --noEmit se poss\xEDvel, depois factorai.sh.apply_app_changes ou deploy_app. N\xE3o digas ao utilizador que a app est\xE1 online."
20457
+ };
20458
+ }
20459
+ if (READY_STATUSES.has(status)) {
20460
+ return {
20461
+ success: true,
20462
+ terminal: true,
20463
+ status: "ready",
20464
+ appId,
20465
+ slug,
20466
+ url,
20467
+ buildLogTail: logTail || void 0
20468
+ };
20469
+ }
20470
+ if (BUILDING_STATUSES.has(status)) {
20471
+ return {
20472
+ success: true,
20473
+ terminal: false,
20474
+ status,
20475
+ appId,
20476
+ slug,
20477
+ url,
20478
+ buildLogTail: logTail || void 0
20479
+ };
20480
+ }
20481
+ return {
20482
+ success: true,
20483
+ terminal: false,
20484
+ status,
20485
+ appId,
20486
+ slug,
20487
+ url,
20488
+ buildLogTail: logTail || void 0
20489
+ };
20490
+ }
20491
+ function formatFactorAiDeployStatusForAgent(result) {
20492
+ const lines = [`status=${result.status}`, `appId=${result.appId}`];
20493
+ if (result.slug) lines.push(`slug=${result.slug}`);
20494
+ if (result.url) lines.push(`url=${result.url}`);
20495
+ if (result.error) lines.push(`error=${result.error}`);
20496
+ if (result.typescriptErrors?.length) {
20497
+ lines.push("typescriptErrors:");
20498
+ for (const block of result.typescriptErrors) {
20499
+ lines.push(block);
20500
+ }
20501
+ } else if (result.buildLogTail) {
20502
+ lines.push("buildLogTail:");
20503
+ lines.push(result.buildLogTail);
20504
+ }
20505
+ if (result.agentGuidance) lines.push(`guidance=${result.agentGuidance}`);
20506
+ return lines.join("\n");
20507
+ }
20508
+ function sleep(ms) {
20509
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
20510
+ }
20511
+
20416
20512
  // src/app/agent/runtime/native_tool_catalog.ts
20417
20513
  init_sandbox_policy();
20418
20514
  function getFactorAiBaseUrl() {
@@ -20494,9 +20590,81 @@ async function resolveFactorAiAppId(explicitAppId) {
20494
20590
  "appId em falta: passa o UUID de factorai.sh.json (appContext.appId) ou faz deploy_app primeiro nesta sess\xE3o."
20495
20591
  );
20496
20592
  }
20593
+ function isFactorAiNotFoundError(err) {
20594
+ const msg = err instanceof Error ? err.message : String(err);
20595
+ return /not found/i.test(msg) || /404/.test(msg);
20596
+ }
20597
+ async function fetchAndNormalizeAppStatus(appId) {
20598
+ const payload = await requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}`);
20599
+ return normalizeFactorAiAppStatus(payload, appId);
20600
+ }
20497
20601
  async function factorAiGetAppStatus(args) {
20498
20602
  const appId = await resolveFactorAiAppId(args?.appId);
20499
- return requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}`);
20603
+ const wait = args?.wait === true;
20604
+ const timeoutMs = Math.max(5, args?.timeoutSeconds ?? 480) * 1e3;
20605
+ const pollMs = Math.max(2, args?.pollIntervalSeconds ?? 5) * 1e3;
20606
+ const deadline = Date.now() + timeoutMs;
20607
+ const finalize = (result) => ({
20608
+ success: true,
20609
+ data: {
20610
+ ...result,
20611
+ summary: formatFactorAiDeployStatusForAgent(result)
20612
+ }
20613
+ });
20614
+ if (!wait) {
20615
+ try {
20616
+ return finalize(await fetchAndNormalizeAppStatus(appId));
20617
+ } catch (err) {
20618
+ if (isFactorAiNotFoundError(err)) {
20619
+ return finalize({
20620
+ success: false,
20621
+ terminal: true,
20622
+ status: "failed",
20623
+ appId,
20624
+ error: String(err instanceof Error ? err.message : err),
20625
+ agentGuidance: "App n\xE3o encontrada no FactorAI.sh \u2014 o deploy provavelmente falhou e o registo foi limpo, ou o appId est\xE1 errado. L\xEA factorai.sh.json, confirma FACTORAI_BASE_URL=SEVERINO_URL, e faz deploy de novo ap\xF3s corrigir o c\xF3digo."
20626
+ });
20627
+ }
20628
+ throw err;
20629
+ }
20630
+ }
20631
+ let sawBuilding = false;
20632
+ while (Date.now() < deadline) {
20633
+ try {
20634
+ const result = await fetchAndNormalizeAppStatus(appId);
20635
+ if (!result.terminal && result.status !== "failed") {
20636
+ sawBuilding = true;
20637
+ }
20638
+ if (result.terminal) {
20639
+ if (result.status === "ready" && !sawBuilding) {
20640
+ await sleep(pollMs);
20641
+ continue;
20642
+ }
20643
+ return finalize(result);
20644
+ }
20645
+ } catch (err) {
20646
+ if (isFactorAiNotFoundError(err)) {
20647
+ return finalize({
20648
+ success: false,
20649
+ terminal: true,
20650
+ status: "failed",
20651
+ appId,
20652
+ error: String(err instanceof Error ? err.message : err),
20653
+ agentGuidance: "Deploy falhou: a app desapareceu do registry (build error no servidor). Corrige o c\xF3digo e volta a deploy_app."
20654
+ });
20655
+ }
20656
+ throw err;
20657
+ }
20658
+ await sleep(pollMs);
20659
+ }
20660
+ return finalize({
20661
+ success: true,
20662
+ terminal: false,
20663
+ status: "building",
20664
+ appId,
20665
+ error: `Timeout ap\xF3s ${Math.round(timeoutMs / 1e3)}s \xE0 espera de ready/failed`,
20666
+ agentGuidance: "Continua a pollar get_app_status ou verifica logs no FactorAI.sh."
20667
+ });
20500
20668
  }
20501
20669
  async function factorAiApplyAppChanges(args) {
20502
20670
  const appId = await resolveFactorAiAppId(args?.appId);
@@ -20512,7 +20680,13 @@ async function factorAiApplyAppChanges(args) {
20512
20680
  })
20513
20681
  });
20514
20682
  await persistFactorAiWorkspaceManifestFromResponse(response);
20515
- return response;
20683
+ const normalized = normalizeFactorAiAppStatus(response, appId);
20684
+ const summary = formatFactorAiDeployStatusForAgent(normalized);
20685
+ const message2 = args?.deploy !== false && !normalized.terminal ? "Changes accepted; build em curso. Chama factorai.sh.get_app_status({ wait: true }) at\xE9 ready ou failed." : normalized.success ? "Changes applied" : normalized.error;
20686
+ return {
20687
+ success: true,
20688
+ data: { ...normalized, summary, message: message2 }
20689
+ };
20516
20690
  }
20517
20691
  async function factorAiRedeployApp(args) {
20518
20692
  const appId = await resolveFactorAiAppId(args?.appId);
@@ -20520,7 +20694,13 @@ async function factorAiRedeployApp(args) {
20520
20694
  method: "POST"
20521
20695
  });
20522
20696
  await persistFactorAiWorkspaceManifestFromResponse(response);
20523
- return response;
20697
+ const normalized = normalizeFactorAiAppStatus(response, appId);
20698
+ const summary = formatFactorAiDeployStatusForAgent(normalized);
20699
+ const message2 = normalized.terminal ? normalized.success ? "Redeploy complete" : normalized.error : "Redeploy started; poll get_app_status({ wait: true }).";
20700
+ return {
20701
+ success: true,
20702
+ data: { ...normalized, summary, message: message2 }
20703
+ };
20524
20704
  }
20525
20705
  function extractFactorAiResponseData(response) {
20526
20706
  if (response && typeof response === "object" && response.data && typeof response.data === "object") {
@@ -20607,13 +20787,25 @@ function getFactorAiSandboxToolDefinitions() {
20607
20787
  type: "function",
20608
20788
  function: {
20609
20789
  name: "factorai.sh.get_app_status",
20610
- description: "Poll deploy/rebuild status (building|ready|failed). Call after deploy_app or apply_app_changes until ready before telling the user the app is live.",
20790
+ description: "Poll deploy/rebuild status. On failed returns buildLogTail + typescriptErrors so you can fix code. Use wait:true after deploy_app/apply_app_changes until ready or failed before message(result).",
20611
20791
  parameters: {
20612
20792
  type: "object",
20613
20793
  properties: {
20614
20794
  appId: {
20615
20795
  type: "string",
20616
20796
  description: "UUID from factorai.sh.json (appContext.appId). Optional if manifest exists in cwd. Do not use URL slug."
20797
+ },
20798
+ wait: {
20799
+ type: "boolean",
20800
+ description: "If true, poll until ready/failed/timeout (recommended after deploy)."
20801
+ },
20802
+ timeoutSeconds: {
20803
+ type: "number",
20804
+ description: "Max wait time when wait=true (default 480)."
20805
+ },
20806
+ pollIntervalSeconds: {
20807
+ type: "number",
20808
+ description: "Seconds between polls when wait=true (default 5)."
20617
20809
  }
20618
20810
  },
20619
20811
  required: [],
@@ -21836,14 +22028,14 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
21836
22028
 
21837
22029
  // src/app/agent/bluma/core/bluma.ts
21838
22030
  init_session_manager();
21839
- import path45 from "path";
21840
- import fs40 from "fs";
22031
+ import path46 from "path";
22032
+ import fs41 from "fs";
21841
22033
  import { v4 as uuidv48 } from "uuid";
21842
22034
 
21843
22035
  // src/app/agent/core/prompt/prompt_builder.ts
21844
22036
  import os23 from "os";
21845
- import fs36 from "fs";
21846
- import path39 from "path";
22037
+ import fs37 from "fs";
22038
+ import path40 from "path";
21847
22039
  import { execSync as execSync3 } from "child_process";
21848
22040
 
21849
22041
  // src/app/agent/skills/skill_loader.ts
@@ -23700,12 +23892,12 @@ init_session_memory_paths();
23700
23892
  import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
23701
23893
  async function loadSessionMemoryForPrompt(sessionId, cwd2 = process.cwd()) {
23702
23894
  if (!sessionId?.trim()) return "";
23703
- const path59 = getSessionMemoryPath(sessionId, cwd2);
23895
+ const path60 = getSessionMemoryPath(sessionId, cwd2);
23704
23896
  try {
23705
- const content = await readFile3(path59, "utf-8");
23897
+ const content = await readFile3(path60, "utf-8");
23706
23898
  if (!content.trim()) return "";
23707
23899
  return `<session_memory>
23708
- Notes for this conversation (${path59}):
23900
+ Notes for this conversation (${path60}):
23709
23901
 
23710
23902
  ${content.trim()}
23711
23903
  </session_memory>`;
@@ -23715,11 +23907,11 @@ ${content.trim()}
23715
23907
  }
23716
23908
  async function initSessionMemoryFile(sessionId, cwd2 = process.cwd()) {
23717
23909
  await ensureSessionMemoryDir(sessionId, cwd2);
23718
- const path59 = getSessionMemoryPath(sessionId, cwd2);
23910
+ const path60 = getSessionMemoryPath(sessionId, cwd2);
23719
23911
  try {
23720
- await readFile3(path59, "utf-8");
23912
+ await readFile3(path60, "utf-8");
23721
23913
  } catch {
23722
- await writeFile2(path59, `${DEFAULT_SESSION_MEMORY_TEMPLATE}
23914
+ await writeFile2(path60, `${DEFAULT_SESSION_MEMORY_TEMPLATE}
23723
23915
  `, "utf-8");
23724
23916
  }
23725
23917
  }
@@ -23731,14 +23923,14 @@ var FACTORAI_SH_SECTION = `
23731
23923
 
23732
23924
  **When to load full checklist:** \`load_skill("factorai-sh")\` before any create/deploy/edit/redeploy task.
23733
23925
 
23734
- ### Backend URL (already in your environment)
23926
+ ### Backend URL (see also \`<sandbox_runtime>\` table)
23735
23927
  | Variable | Used by |
23736
23928
  |----------|---------|
23737
23929
  | \`FACTORAI_BASE_URL\` | \`factorai.sh.get_app_status\`, \`factorai.sh.apply_app_changes\`, \`factorai.sh.redeploy_app\` |
23738
23930
  | \`SEVERINO_URL\` | \`factorai.sh.deploy_app\` (ZIP upload to \`/api/v1/deploy\`) |
23739
23931
  | \`FACTORAI_API_KEY\` / \`SEVERINO_API_KEY\` | Optional Bearer on write endpoints |
23740
23932
 
23741
- 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)\`.
23742
23934
 
23743
23935
  ### Source of truth: \`factorai.sh.json\`
23744
23936
  - Written/updated by \`factorai.sh.deploy_app\` and FactorAI tools \u2014 **read with normal file tools**, never a separate manifest tool.
@@ -23791,11 +23983,11 @@ Use \`factorai.sh.redeploy_app({ appId })\` only when sources on the server are
23791
23983
 
23792
23984
  ## Polling after deploy / apply_app_changes
23793
23985
 
23794
- 1. \`factorai.sh.get_app_status({ appId })\` or GET \`/api/v1/apps/{appId}\`.
23986
+ 1. **Always** \`factorai.sh.get_app_status({ appId, wait: true })\` after \`deploy_app\` or \`apply_app_changes\` (polls at\xE9 \`ready\` ou \`failed\`).
23795
23987
  2. Expect \`status: "building"\` during \`npm install\` / \`next build\` (~30\u201390s).
23796
- 3. **Do not** report "live" on the first \`ready\` if you have not seen \`building\` since this operation \u2014 stale \`ready\` can appear briefly.
23797
- 4. Wait until \`status\` is \`ready\` (or \`running\` / \`live\`) **after** a \`building\` phase, then verify the public route returns HTML (not \`Service Unavailable\`).
23798
- 5. On \`failed\` / \`error\`: read server/build logs if available; fix TS/scaffold issues; retry \`apply_app_changes\` or \`redeploy_app\`.
23988
+ 3. **Do not** report "live" until \`success: true\` and \`status: "ready"\` no resultado do \`get_app_status\`.
23989
+ 4. On \`success: false\` / \`status: "failed"\`: read \`typescriptErrors\`, \`buildLogTail\`, and \`summary\` from the tool result \u2014 fix the code like a human reading the Next build log, then \`apply_app_changes\` or \`deploy_app\` again.
23990
+ 5. Never ignore deploy failures; never tell the user the app is online when \`get_app_status\` returned \`failed\`.
23799
23991
 
23800
23992
  ---
23801
23993
 
@@ -23821,6 +24013,146 @@ function isFactorAiShPromptEnabled() {
23821
24013
  return isSandbox && Boolean(baseUrl);
23822
24014
  }
23823
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
+ import path39 from "path";
24069
+ function env2(key) {
24070
+ return String(process.env[key] ?? "").trim();
24071
+ }
24072
+ function listWorkspaceTopLevel(cwd2, max = 24) {
24073
+ try {
24074
+ return fs36.readdirSync(cwd2, { withFileTypes: true }).filter((e) => !e.name.startsWith(".")).slice(0, max).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
24075
+ } catch {
24076
+ return [];
24077
+ }
24078
+ }
24079
+ function buildSandboxRuntimeContextBlock(cwd2) {
24080
+ const sessionId = env2("BLUMA_SESSION_ID") || "unknown";
24081
+ const sandboxName = env2("BLUMA_SANDBOX_NAME") || "sandbox-api";
24082
+ const fromAgent = env2("BLUMA_FROM_AGENT") || "orchestrator";
24083
+ const action = env2("BLUMA_ACTION") || "unknown";
24084
+ const severinoUrl = env2("SEVERINO_URL");
24085
+ const factoraiUrl = env2("FACTORAI_BASE_URL") || env2("FACTORAI_URL") || severinoUrl;
24086
+ const userRequest = env2("BLUMA_USER_REQUEST");
24087
+ const factoraiEnabled = isFactorAiShPromptEnabled();
24088
+ const top = listWorkspaceTopLevel(cwd2);
24089
+ const lines = [
24090
+ "<sandbox_runtime>",
24091
+ "## Ambiente atual (factos reais \u2014 usa isto para te orientares)",
24092
+ "",
24093
+ "Operas como **agente aut\xF3nomo** (estilo Manus): n\xE3o h\xE1 utilizador no terminal a responder. O orquestrador (Severino / sandbox-api) envia o pedido, observa o teu progresso via `message(info)` e s\xF3 encerra o job com `message(result)`.",
24094
+ "",
24095
+ "| Campo | Valor |",
24096
+ "|-------|-------|",
24097
+ `| sandbox | ${sandboxName} |`,
24098
+ `| session_id | ${sessionId} |`,
24099
+ `| workspace (cwd) | ${cwd2} |`,
24100
+ `| delegado por | ${fromAgent} |`,
24101
+ `| action | ${action} |`,
24102
+ `| node | ${process.version} |`,
24103
+ `| auto_approve_tools | sim (sandbox isolado) |`
24104
+ ];
24105
+ if (severinoUrl) {
24106
+ lines.push(`| SEVERINO_URL (deploy ZIP) | ${severinoUrl} |`);
24107
+ }
24108
+ if (factoraiUrl) {
24109
+ lines.push(`| FACTORAI_BASE_URL (status/apply/redeploy) | ${factoraiUrl} |`);
24110
+ }
24111
+ if (severinoUrl && factoraiUrl && severinoUrl !== factoraiUrl) {
24112
+ lines.push("");
24113
+ lines.push(
24114
+ "\u26A0\uFE0F SEVERINO_URL e FACTORAI_BASE_URL diferem \u2014 confirma qual backend falhou se `get_app_status` der 404 ap\xF3s deploy."
24115
+ );
24116
+ }
24117
+ if (factoraiEnabled) {
24118
+ lines.push("");
24119
+ lines.push("**FactorAI.sh:** ativo nesta sess\xE3o (`factorai.sh.*` tools dispon\xEDveis).");
24120
+ } else {
24121
+ lines.push("");
24122
+ lines.push(
24123
+ "**FactorAI.sh:** n\xE3o configurado (falta `FACTORAI_BASE_URL` ou `BLUMA_SANDBOX`). Hosting Next.js via factorai.sh indispon\xEDvel at\xE9 o orquestrador definir env."
24124
+ );
24125
+ }
24126
+ if (top.length > 0) {
24127
+ lines.push("");
24128
+ lines.push(`**Raiz do workspace:** ${top.join(", ")}`);
24129
+ }
24130
+ const manifest = readFactorAiWorkspaceManifest(cwd2);
24131
+ if (manifest) {
24132
+ const ctx = manifest.appContext;
24133
+ const appId = ctx?.appId;
24134
+ const liveUrl = resolveFactorShAppLiveUrl(cwd2);
24135
+ lines.push("");
24136
+ lines.push("**App FactorAI nesta sess\xE3o (factorai.sh.json):**");
24137
+ if (appId) lines.push(`- appId: ${appId}`);
24138
+ if (liveUrl) lines.push(`- appUrl: ${liveUrl}`);
24139
+ const appDir = typeof manifest.app === "object" && manifest.app && typeof manifest.app.name === "string" ? manifest.app.name : null;
24140
+ if (appDir) {
24141
+ const projectPath = path39.join(cwd2, appDir);
24142
+ if (fs36.existsSync(projectPath)) {
24143
+ lines.push(`- project_dir: ${projectPath}`);
24144
+ }
24145
+ }
24146
+ }
24147
+ if (userRequest) {
24148
+ lines.push("");
24149
+ lines.push("**Pedido deste turno (resumo):**");
24150
+ lines.push(userRequest.length > 2500 ? `${userRequest.slice(0, 2500)}\u2026` : userRequest);
24151
+ }
24152
+ lines.push("</sandbox_runtime>");
24153
+ return lines.join("\n");
24154
+ }
24155
+
23824
24156
  // src/app/agent/core/prompt/prompt_builder.ts
23825
24157
  function getNodeVersion() {
23826
24158
  return process.version;
@@ -23852,17 +24184,17 @@ function getGitBranch(dir) {
23852
24184
  }
23853
24185
  }
23854
24186
  function getPackageManager(dir) {
23855
- if (fs36.existsSync(path39.join(dir, "pnpm-lock.yaml"))) return "pnpm";
23856
- if (fs36.existsSync(path39.join(dir, "yarn.lock"))) return "yarn";
23857
- if (fs36.existsSync(path39.join(dir, "bun.lockb"))) return "bun";
23858
- if (fs36.existsSync(path39.join(dir, "package-lock.json"))) return "npm";
24187
+ if (fs37.existsSync(path40.join(dir, "pnpm-lock.yaml"))) return "pnpm";
24188
+ if (fs37.existsSync(path40.join(dir, "yarn.lock"))) return "yarn";
24189
+ if (fs37.existsSync(path40.join(dir, "bun.lockb"))) return "bun";
24190
+ if (fs37.existsSync(path40.join(dir, "package-lock.json"))) return "npm";
23859
24191
  return "unknown";
23860
24192
  }
23861
24193
  function getProjectType(dir) {
23862
24194
  try {
23863
- const files = fs36.readdirSync(dir);
24195
+ const files = fs37.readdirSync(dir);
23864
24196
  if (files.includes("package.json")) {
23865
- const pkg = JSON.parse(fs36.readFileSync(path39.join(dir, "package.json"), "utf-8"));
24197
+ const pkg = JSON.parse(fs37.readFileSync(path40.join(dir, "package.json"), "utf-8"));
23866
24198
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
23867
24199
  if (deps.next) return "Next.js";
23868
24200
  if (deps.react) return "React";
@@ -23882,9 +24214,9 @@ function getProjectType(dir) {
23882
24214
  }
23883
24215
  function getTestFramework(dir) {
23884
24216
  try {
23885
- const pkgPath = path39.join(dir, "package.json");
23886
- if (fs36.existsSync(pkgPath)) {
23887
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
24217
+ const pkgPath = path40.join(dir, "package.json");
24218
+ if (fs37.existsSync(pkgPath)) {
24219
+ const pkg = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
23888
24220
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
23889
24221
  if (deps.jest) return "jest";
23890
24222
  if (deps.vitest) return "vitest";
@@ -23893,7 +24225,7 @@ function getTestFramework(dir) {
23893
24225
  if (deps["@playwright/test"]) return "playwright";
23894
24226
  if (deps.cypress) return "cypress";
23895
24227
  }
23896
- if (fs36.existsSync(path39.join(dir, "pytest.ini")) || fs36.existsSync(path39.join(dir, "conftest.py"))) return "pytest";
24228
+ if (fs37.existsSync(path40.join(dir, "pytest.ini")) || fs37.existsSync(path40.join(dir, "conftest.py"))) return "pytest";
23897
24229
  return "unknown";
23898
24230
  } catch {
23899
24231
  return "unknown";
@@ -23901,9 +24233,9 @@ function getTestFramework(dir) {
23901
24233
  }
23902
24234
  function getTestCommand(dir) {
23903
24235
  try {
23904
- const pkgPath = path39.join(dir, "package.json");
23905
- if (fs36.existsSync(pkgPath)) {
23906
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
24236
+ const pkgPath = path40.join(dir, "package.json");
24237
+ if (fs37.existsSync(pkgPath)) {
24238
+ const pkg = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
23907
24239
  if (pkg.scripts?.test) return "npm test";
23908
24240
  if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
23909
24241
  }
@@ -23918,8 +24250,8 @@ function getTestCommand(dir) {
23918
24250
  }
23919
24251
  function isGitRepo(dir) {
23920
24252
  try {
23921
- const p = path39.join(dir, ".git");
23922
- return fs36.existsSync(p) && fs36.lstatSync(p).isDirectory();
24253
+ const p = path40.join(dir, ".git");
24254
+ return fs37.existsSync(p) && fs37.lstatSync(p).isDirectory();
23923
24255
  } catch {
23924
24256
  return false;
23925
24257
  }
@@ -24130,7 +24462,7 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
24130
24462
  const runtimeConfig = getRuntimeConfig();
24131
24463
  const cwd2 = process.cwd();
24132
24464
  const isSandbox = process.env.BLUMA_SANDBOX === "true";
24133
- const env2 = {
24465
+ const env3 = {
24134
24466
  os_type: os23.type(),
24135
24467
  os_version: os23.release(),
24136
24468
  architecture: os23.arch(),
@@ -24155,7 +24487,7 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
24155
24487
  session_id: process.env.BLUMA_SESSION_ID || "unknown",
24156
24488
  workspace_root: cwd2
24157
24489
  };
24158
- const interpolate = (template) => Object.entries(env2).reduce((s, [k, v]) => s.replaceAll(`{${k}}`, v), template);
24490
+ const interpolate = (template) => Object.entries(env3).reduce((s, [k, v]) => s.replaceAll(`{${k}}`, v), template);
24159
24491
  let prompt = interpolate(SYSTEM_PROMPT).replace(
24160
24492
  "<<<BLUMA_WORKSPACE_SNAPSHOT_BODY>>>",
24161
24493
  buildWorkspaceSnapshot(cwd2)
@@ -24174,6 +24506,12 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
24174
24506
  prompt += `
24175
24507
 
24176
24508
  ${SUBJECT_MACHINE_MODEL_XML}`;
24509
+ prompt += `
24510
+
24511
+ ${buildSandboxRuntimeContextBlock(cwd2)}`;
24512
+ prompt += `
24513
+
24514
+ ${SANDBOX_AUTONOMY_SECTION}`;
24177
24515
  prompt += interpolate(SANDBOX_SECTION);
24178
24516
  if (isFactorAiShPromptEnabled()) {
24179
24517
  prompt += `
@@ -25353,9 +25691,9 @@ var LLMService = class {
25353
25691
  };
25354
25692
 
25355
25693
  // src/app/agent/utils/user_message_images.ts
25356
- import fs37 from "fs";
25694
+ import fs38 from "fs";
25357
25695
  import os25 from "os";
25358
- import path40 from "path";
25696
+ import path41 from "path";
25359
25697
  import { fileURLToPath as fileURLToPath5 } from "url";
25360
25698
  var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp)$/i;
25361
25699
  var MAX_IMAGE_BYTES = 4 * 1024 * 1024;
@@ -25371,22 +25709,22 @@ var MIME = {
25371
25709
  function expandUserPath(p) {
25372
25710
  const t = p.trim();
25373
25711
  if (t.startsWith("~")) {
25374
- return path40.join(os25.homedir(), t.slice(1).replace(/^\//, ""));
25712
+ return path41.join(os25.homedir(), t.slice(1).replace(/^\//, ""));
25375
25713
  }
25376
25714
  return t;
25377
25715
  }
25378
25716
  function isPathAllowed(absResolved, cwd2) {
25379
- const resolved = path40.normalize(path40.resolve(absResolved));
25380
- const cwdR = path40.normalize(path40.resolve(cwd2));
25381
- const homeR = path40.normalize(path40.resolve(os25.homedir()));
25382
- const tmpR = path40.normalize(path40.resolve(os25.tmpdir()));
25383
- const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path40.sep);
25384
- const underHome = resolved === homeR || resolved.startsWith(homeR + path40.sep);
25385
- const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path40.sep);
25717
+ const resolved = path41.normalize(path41.resolve(absResolved));
25718
+ const cwdR = path41.normalize(path41.resolve(cwd2));
25719
+ const homeR = path41.normalize(path41.resolve(os25.homedir()));
25720
+ const tmpR = path41.normalize(path41.resolve(os25.tmpdir()));
25721
+ const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path41.sep);
25722
+ const underHome = resolved === homeR || resolved.startsWith(homeR + path41.sep);
25723
+ const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path41.sep);
25386
25724
  return underCwd || underHome || underTmp;
25387
25725
  }
25388
25726
  function mimeFor(abs) {
25389
- const ext = path40.extname(abs).toLowerCase();
25727
+ const ext = path41.extname(abs).toLowerCase();
25390
25728
  return MIME[ext] || "application/octet-stream";
25391
25729
  }
25392
25730
  var IMAGE_EXT_SRC = String.raw`(?:png|jpe?g|gif|webp|bmp)`;
@@ -25430,10 +25768,10 @@ function collectImagePathStrings(raw) {
25430
25768
  }
25431
25769
  function resolveImagePath(candidate, cwd2) {
25432
25770
  const expanded = expandUserPath(candidate);
25433
- const abs = path40.isAbsolute(expanded) ? path40.normalize(expanded) : path40.normalize(path40.resolve(cwd2, expanded));
25771
+ const abs = path41.isAbsolute(expanded) ? path41.normalize(expanded) : path41.normalize(path41.resolve(cwd2, expanded));
25434
25772
  if (!isPathAllowed(abs, cwd2)) return null;
25435
25773
  try {
25436
- if (!fs37.existsSync(abs) || !fs37.statSync(abs).isFile()) return null;
25774
+ if (!fs38.existsSync(abs) || !fs38.statSync(abs).isFile()) return null;
25437
25775
  } catch {
25438
25776
  return null;
25439
25777
  }
@@ -25453,11 +25791,11 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
25453
25791
  if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
25454
25792
  s = s.slice(1, -1).trim();
25455
25793
  }
25456
- if (!IMAGE_EXT.test(path40.extname(s))) return null;
25794
+ if (!IMAGE_EXT.test(path41.extname(s))) return null;
25457
25795
  const abs = resolveImagePath(s, cwd2);
25458
25796
  if (!abs) return null;
25459
25797
  try {
25460
- const st = fs37.statSync(abs);
25798
+ const st = fs38.statSync(abs);
25461
25799
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
25462
25800
  return null;
25463
25801
  }
@@ -25492,7 +25830,7 @@ function tryPasteChunkAsSingleImagePath(chunk, cwd2) {
25492
25830
  return null;
25493
25831
  }
25494
25832
  try {
25495
- const st = fs37.statSync(abs);
25833
+ const st = fs38.statSync(abs);
25496
25834
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
25497
25835
  return null;
25498
25836
  }
@@ -25521,7 +25859,7 @@ function buildUserMessageContent(raw, cwd2) {
25521
25859
  const abs = resolveImagePath(c, cwd2);
25522
25860
  if (!abs) continue;
25523
25861
  try {
25524
- const st = fs37.statSync(abs);
25862
+ const st = fs38.statSync(abs);
25525
25863
  if (st.size > MAX_IMAGE_BYTES) continue;
25526
25864
  } catch {
25527
25865
  continue;
@@ -25538,7 +25876,7 @@ function buildUserMessageContent(raw, cwd2) {
25538
25876
  }
25539
25877
  const parts = [{ type: "text", text: textPart }];
25540
25878
  for (const abs of resolvedAbs) {
25541
- const buf = fs37.readFileSync(abs);
25879
+ const buf = fs38.readFileSync(abs);
25542
25880
  const b64 = buf.toString("base64");
25543
25881
  const mime = mimeFor(abs);
25544
25882
  parts.push({
@@ -25556,7 +25894,7 @@ function buildUserMessageContent(raw, cwd2) {
25556
25894
  init_sandbox_policy();
25557
25895
  init_runtime_config();
25558
25896
  init_permission_rules();
25559
- import path41 from "path";
25897
+ import path42 from "path";
25560
25898
  var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
25561
25899
  function getToolPermissionLayer(metadata) {
25562
25900
  if (metadata.riskLevel === "safe") return "read";
@@ -25571,11 +25909,11 @@ function checkFilePermissionRules(toolName, filePath, policy) {
25571
25909
  if (!filePath) {
25572
25910
  return { allowed: false, reason: "No file path provided for permission check." };
25573
25911
  }
25574
- const resolvedPath = path41.resolve(filePath);
25912
+ const resolvedPath = path42.resolve(filePath);
25575
25913
  if (!isPathInsideWorkspace(resolvedPath, policy)) {
25576
25914
  return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
25577
25915
  }
25578
- const relativePath = path41.relative(policy.workspaceRoot, resolvedPath);
25916
+ const relativePath = path42.relative(policy.workspaceRoot, resolvedPath);
25579
25917
  const toolPattern = `${toolName}(${relativePath})`;
25580
25918
  const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
25581
25919
  if (ruleDecision === "deny") {
@@ -25584,7 +25922,7 @@ function checkFilePermissionRules(toolName, filePath, policy) {
25584
25922
  if (ruleDecision === "allow") {
25585
25923
  return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
25586
25924
  }
25587
- const dirPath = path41.dirname(relativePath);
25925
+ const dirPath = path42.dirname(relativePath);
25588
25926
  const dirPattern = `${toolName}(${dirPath}/**)`;
25589
25927
  const dirRuleDecision = permissionRulesEngine.checkPermission(dirPattern, { filepath: filePath });
25590
25928
  if (dirRuleDecision === "allow") {
@@ -25801,11 +26139,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
25801
26139
  }
25802
26140
 
25803
26141
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
25804
- import * as fs38 from "fs";
25805
- import * as path42 from "path";
26142
+ import * as fs39 from "fs";
26143
+ import * as path43 from "path";
25806
26144
  import os26 from "os";
25807
26145
  function memoryPath2() {
25808
- return path42.join(process.env.HOME || os26.homedir(), ".bluma", "coding_memory.json");
26146
+ return path43.join(process.env.HOME || os26.homedir(), ".bluma", "coding_memory.json");
25809
26147
  }
25810
26148
  function normalizeNote2(note) {
25811
26149
  return note.trim().toLowerCase().replace(/\s+/g, " ");
@@ -25815,18 +26153,18 @@ function uniqTags(a, b) {
25815
26153
  }
25816
26154
  function consolidateCodingMemoryFile() {
25817
26155
  const p = memoryPath2();
25818
- if (!fs38.existsSync(p)) {
26156
+ if (!fs39.existsSync(p)) {
25819
26157
  return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
25820
26158
  }
25821
26159
  const bak = `${p}.bak`;
25822
26160
  try {
25823
- fs38.copyFileSync(p, bak);
26161
+ fs39.copyFileSync(p, bak);
25824
26162
  } catch (e) {
25825
26163
  return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
25826
26164
  }
25827
26165
  let data;
25828
26166
  try {
25829
- data = JSON.parse(fs38.readFileSync(p, "utf-8"));
26167
+ data = JSON.parse(fs39.readFileSync(p, "utf-8"));
25830
26168
  } catch (e) {
25831
26169
  return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
25832
26170
  }
@@ -25861,7 +26199,7 @@ function consolidateCodingMemoryFile() {
25861
26199
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
25862
26200
  };
25863
26201
  try {
25864
- fs38.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
26202
+ fs39.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
25865
26203
  } catch (e) {
25866
26204
  return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
25867
26205
  }
@@ -26367,18 +26705,18 @@ var BluMaToolRunner = class {
26367
26705
 
26368
26706
  // src/app/agent/session_manager/session_archive.ts
26369
26707
  init_bluma_app_dir();
26370
- import path43 from "path";
26371
- import { promises as fs39 } from "fs";
26708
+ import path44 from "path";
26709
+ import { promises as fs40 } from "fs";
26372
26710
  async function archivePrunedConversationMessages(sessionId, messages) {
26373
26711
  if (!sessionId || messages.length === 0) {
26374
26712
  return null;
26375
26713
  }
26376
26714
  const appDir = getPreferredAppDir();
26377
- const dir = path43.join(appDir, "sessions", "archive", sessionId);
26378
- await fs39.mkdir(dir, { recursive: true });
26379
- const archiveFile = path43.join(dir, `${Date.now()}.jsonl`);
26715
+ const dir = path44.join(appDir, "sessions", "archive", sessionId);
26716
+ await fs40.mkdir(dir, { recursive: true });
26717
+ const archiveFile = path44.join(dir, `${Date.now()}.jsonl`);
26380
26718
  const lines = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
26381
- await fs39.appendFile(archiveFile, lines, "utf-8");
26719
+ await fs40.appendFile(archiveFile, lines, "utf-8");
26382
26720
  return archiveFile;
26383
26721
  }
26384
26722
 
@@ -27239,7 +27577,7 @@ Update existing files instead of duplicating.` : "";
27239
27577
 
27240
27578
  // src/app/agent/memory/memory_tool_policy.ts
27241
27579
  init_paths();
27242
- import path44 from "path";
27580
+ import path45 from "path";
27243
27581
  var MEMORY_READ_TOOLS = /* @__PURE__ */ new Set([
27244
27582
  "read_file_lines",
27245
27583
  "grep_search",
@@ -27266,7 +27604,7 @@ function isReadOnlyShellCommand(command) {
27266
27604
  return READ_ONLY_SHELL.test(trimmed);
27267
27605
  }
27268
27606
  function createAutoMemToolGate(memoryDir) {
27269
- const memRoot = memoryDir.endsWith(path44.sep) ? memoryDir : memoryDir + path44.sep;
27607
+ const memRoot = memoryDir.endsWith(path45.sep) ? memoryDir : memoryDir + path45.sep;
27270
27608
  return (toolName, args) => {
27271
27609
  if (MEMORY_READ_TOOLS.has(toolName)) {
27272
27610
  return { allowed: true };
@@ -27279,7 +27617,7 @@ function createAutoMemToolGate(memoryDir) {
27279
27617
  }
27280
27618
  if (MEMORY_WRITE_TOOLS.has(toolName)) {
27281
27619
  const fp = extractFilePath(args);
27282
- if (fp && isAutoMemPath(path44.resolve(fp))) {
27620
+ if (fp && isAutoMemPath(path45.resolve(fp))) {
27283
27621
  return { allowed: true };
27284
27622
  }
27285
27623
  return { allowed: false, reason: `Writes must stay under ${memRoot}` };
@@ -27288,18 +27626,18 @@ function createAutoMemToolGate(memoryDir) {
27288
27626
  };
27289
27627
  }
27290
27628
  function createSessionMemoryToolGate(sessionMemoryPath) {
27291
- const resolvedTarget = path44.resolve(sessionMemoryPath);
27629
+ const resolvedTarget = path45.resolve(sessionMemoryPath);
27292
27630
  return (toolName, args) => {
27293
27631
  if (toolName === "read_file_lines") {
27294
27632
  const fp = extractFilePath(args);
27295
- if (fp && path44.resolve(fp) === resolvedTarget) {
27633
+ if (fp && path45.resolve(fp) === resolvedTarget) {
27296
27634
  return { allowed: true };
27297
27635
  }
27298
27636
  return { allowed: false, reason: "Session memory subagent may only read the session summary file" };
27299
27637
  }
27300
27638
  if (toolName === "edit_tool") {
27301
27639
  const fp = extractFilePath(args);
27302
- if (fp && path44.resolve(fp) === resolvedTarget) {
27640
+ if (fp && path45.resolve(fp) === resolvedTarget) {
27303
27641
  return { allowed: true };
27304
27642
  }
27305
27643
  return { allowed: false, reason: "Session memory subagent may only edit the session summary file" };
@@ -27326,7 +27664,7 @@ function hasAutoMemWritesSinceHistory(history, sinceIndex) {
27326
27664
  continue;
27327
27665
  }
27328
27666
  const fp = extractFilePath(args);
27329
- if (fp && isAutoMemPath(path44.resolve(fp))) {
27667
+ if (fp && isAutoMemPath(path45.resolve(fp))) {
27330
27668
  return true;
27331
27669
  }
27332
27670
  }
@@ -27794,13 +28132,13 @@ var BluMaAgent = class {
27794
28132
  if (!this.sessionFile) return;
27795
28133
  try {
27796
28134
  const sessionData = {
27797
- session_id: path45.basename(this.sessionFile, ".json"),
28135
+ session_id: path46.basename(this.sessionFile, ".json"),
27798
28136
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
27799
28137
  conversation_history: this.history,
27800
28138
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
27801
28139
  ...this.compressor.getSnapshot()
27802
28140
  };
27803
- fs40.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
28141
+ fs41.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
27804
28142
  } catch (error) {
27805
28143
  console.error("[Bluma] Failed to persist session synchronously:", error);
27806
28144
  }
@@ -28006,7 +28344,7 @@ var BluMaAgent = class {
28006
28344
 
28007
28345
  ${editData.error.display}`;
28008
28346
  }
28009
- const filename = path45.basename(toolArgs.file_path);
28347
+ const filename = path46.basename(toolArgs.file_path);
28010
28348
  return createDiff(filename, editData.currentContent || "", editData.newContent);
28011
28349
  } catch (e) {
28012
28350
  return `An unexpected error occurred while generating the edit preview: ${e.message}`;
@@ -28741,16 +29079,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
28741
29079
  }
28742
29080
 
28743
29081
  // src/app/agent/core/memory/session_memory.ts
28744
- import fs41 from "fs";
29082
+ import fs42 from "fs";
28745
29083
  import os29 from "os";
28746
- import path46 from "path";
29084
+ import path47 from "path";
28747
29085
  import { v4 as uuidv49 } from "uuid";
28748
29086
  var SessionMemoryExtractor = class {
28749
29087
  llmClient;
28750
29088
  memoryFile;
28751
29089
  constructor(options = {}) {
28752
29090
  this.llmClient = options.llmClient;
28753
- this.memoryFile = options.memoryFile || path46.join(os29.homedir(), ".bluma", "session_memory.json");
29091
+ this.memoryFile = options.memoryFile || path47.join(os29.homedir(), ".bluma", "session_memory.json");
28754
29092
  }
28755
29093
  /**
28756
29094
  * Extract memories from conversation using LLM
@@ -28807,15 +29145,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
28807
29145
  );
28808
29146
  unique.sort((a, b) => b.accessCount - a.accessCount);
28809
29147
  const trimmed = unique.slice(0, 200);
28810
- fs41.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
29148
+ fs42.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
28811
29149
  }
28812
29150
  /**
28813
29151
  * Load memories from disk
28814
29152
  */
28815
29153
  async loadMemories() {
28816
29154
  try {
28817
- if (!fs41.existsSync(this.memoryFile)) return [];
28818
- const data = fs41.readFileSync(this.memoryFile, "utf-8");
29155
+ if (!fs42.existsSync(this.memoryFile)) return [];
29156
+ const data = fs42.readFileSync(this.memoryFile, "utf-8");
28819
29157
  return JSON.parse(data);
28820
29158
  } catch {
28821
29159
  return [];
@@ -29359,14 +29697,14 @@ var RouteManager = class {
29359
29697
  this.subAgents = subAgents;
29360
29698
  this.core = core;
29361
29699
  }
29362
- registerRoute(path59, handler) {
29363
- this.routeHandlers.set(path59, handler);
29700
+ registerRoute(path60, handler) {
29701
+ this.routeHandlers.set(path60, handler);
29364
29702
  }
29365
29703
  async handleRoute(payload) {
29366
29704
  const inputText = String(payload.content || "").trim();
29367
29705
  const { userContext, options } = payload;
29368
- for (const [path59, handler] of this.routeHandlers) {
29369
- if (inputText === path59 || inputText.startsWith(`${path59} `)) {
29706
+ for (const [path60, handler] of this.routeHandlers) {
29707
+ if (inputText === path60 || inputText.startsWith(`${path60} `)) {
29370
29708
  return handler({ content: inputText, userContext });
29371
29709
  }
29372
29710
  }
@@ -29375,13 +29713,13 @@ var RouteManager = class {
29375
29713
  };
29376
29714
 
29377
29715
  // src/app/agent/runtime/plugin_runtime.ts
29378
- import path47 from "path";
29716
+ import path48 from "path";
29379
29717
  import { pathToFileURL as pathToFileURL2 } from "url";
29380
29718
  async function loadPluginsAtStartup() {
29381
29719
  for (const p of listPlugins()) {
29382
29720
  const entry = p.manifest.entry?.trim();
29383
29721
  if (!entry) continue;
29384
- const abs = path47.resolve(p.root, entry);
29722
+ const abs = path48.resolve(p.root, entry);
29385
29723
  try {
29386
29724
  const href = pathToFileURL2(abs).href;
29387
29725
  const mod = await import(href);
@@ -29402,7 +29740,7 @@ async function loadPluginsAtStartup() {
29402
29740
  }
29403
29741
 
29404
29742
  // src/app/agent/agent.ts
29405
- var globalEnvPath = path48.join(os30.homedir(), ".bluma", ".env");
29743
+ var globalEnvPath = path49.join(os30.homedir(), ".bluma", ".env");
29406
29744
  dotenv.config({ path: globalEnvPath });
29407
29745
  var Agent = class {
29408
29746
  sessionId;
@@ -31258,10 +31596,10 @@ function resolveToolPayload(result) {
31258
31596
 
31259
31597
  // src/app/ui/components/FilePathLink.tsx
31260
31598
  import { pathToFileURL as pathToFileURL3 } from "node:url";
31261
- import path50 from "node:path";
31599
+ import path51 from "node:path";
31262
31600
 
31263
31601
  // src/app/ui/utils/pathDisplay.ts
31264
- import path49 from "node:path";
31602
+ import path50 from "node:path";
31265
31603
  import os31 from "node:os";
31266
31604
  function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
31267
31605
  let s = String(pathInput ?? "").trim();
@@ -31269,17 +31607,17 @@ function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
31269
31607
  s = s.replace(/[/\\]+$/, "");
31270
31608
  }
31271
31609
  if (!s) return "";
31272
- const abs = path49.isAbsolute(s) ? path49.normalize(s) : path49.resolve(cwd2, s);
31273
- const resolvedCwd = path49.resolve(cwd2);
31274
- const rel = path49.relative(resolvedCwd, abs);
31275
- if (rel === "" || !rel.startsWith("..") && !path49.isAbsolute(rel)) {
31610
+ const abs = path50.isAbsolute(s) ? path50.normalize(s) : path50.resolve(cwd2, s);
31611
+ const resolvedCwd = path50.resolve(cwd2);
31612
+ const rel = path50.relative(resolvedCwd, abs);
31613
+ if (rel === "" || !rel.startsWith("..") && !path50.isAbsolute(rel)) {
31276
31614
  return rel === "" ? "." : rel;
31277
31615
  }
31278
- const home = path49.normalize(os31.homedir());
31279
- if (abs === home || abs.startsWith(home + path49.sep)) {
31616
+ const home = path50.normalize(os31.homedir());
31617
+ if (abs === home || abs.startsWith(home + path50.sep)) {
31280
31618
  return "~" + abs.slice(home.length);
31281
31619
  }
31282
- return path49.basename(abs);
31620
+ return path50.basename(abs);
31283
31621
  }
31284
31622
 
31285
31623
  // src/app/ui/components/FilePathLink.tsx
@@ -31289,7 +31627,7 @@ function FilePathLink({ filePath, children, cwd: cwd2 = process.cwd(), color })
31289
31627
  if (!raw) {
31290
31628
  return null;
31291
31629
  }
31292
- const abs = path50.isAbsolute(raw) ? path50.normalize(raw) : path50.resolve(cwd2, raw);
31630
+ const abs = path51.isAbsolute(raw) ? path51.normalize(raw) : path51.resolve(cwd2, raw);
31293
31631
  const href = pathToFileURL3(abs).href;
31294
31632
  const label = formatPathForDisplay(abs, cwd2);
31295
31633
  const text = typeof children === "string" ? children : label;
@@ -31910,16 +32248,16 @@ function supportsHyperlinks(options) {
31910
32248
  if (stdoutSupported) {
31911
32249
  return true;
31912
32250
  }
31913
- const env2 = options?.env ?? process.env;
31914
- const termProgram = env2["TERM_PROGRAM"];
32251
+ const env3 = options?.env ?? process.env;
32252
+ const termProgram = env3["TERM_PROGRAM"];
31915
32253
  if (termProgram && ADDITIONAL_HYPERLINK_TERMINALS.includes(termProgram)) {
31916
32254
  return true;
31917
32255
  }
31918
- const lcTerminal = env2["LC_TERMINAL"];
32256
+ const lcTerminal = env3["LC_TERMINAL"];
31919
32257
  if (lcTerminal && ADDITIONAL_HYPERLINK_TERMINALS.includes(lcTerminal)) {
31920
32258
  return true;
31921
32259
  }
31922
- const term = env2["TERM"];
32260
+ const term = env3["TERM"];
31923
32261
  if (term?.includes("kitty")) {
31924
32262
  return true;
31925
32263
  }
@@ -33241,12 +33579,12 @@ function patchToUnifiedDiffText(filePath, patch) {
33241
33579
  }
33242
33580
 
33243
33581
  // src/app/ui/utils/readEditContext.ts
33244
- import { promises as fs42 } from "fs";
33582
+ import { promises as fs43 } from "fs";
33245
33583
  var CHUNK_SIZE = 64 * 1024;
33246
33584
  var CONTEXT_LINES2 = 3;
33247
33585
  async function openForScan(filePath) {
33248
33586
  try {
33249
- return await fs42.open(filePath, "r");
33587
+ return await fs43.open(filePath, "r");
33250
33588
  } catch (e) {
33251
33589
  if (e.code === "ENOENT") return null;
33252
33590
  throw e;
@@ -33924,13 +34262,13 @@ function EditToolDiffPanel({
33924
34262
  newString,
33925
34263
  replaceAll = false
33926
34264
  }) {
33927
- const path59 = filePath.trim() || "unknown file";
34265
+ const path60 = filePath.trim() || "unknown file";
33928
34266
  const hasPreviewArgs = oldString !== void 0 && newString !== void 0;
33929
34267
  const hasDiffText = diffText && diffText.trim().length > 0;
33930
34268
  return /* @__PURE__ */ jsx43(Box_default, { flexDirection: "column", children: hasPreviewArgs ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(
33931
34269
  FileEditToolDiff,
33932
34270
  {
33933
- filePath: path59,
34271
+ filePath: path60,
33934
34272
  oldString,
33935
34273
  newString,
33936
34274
  replaceAll,
@@ -33968,7 +34306,7 @@ function renderToolUseMessage12({ args }) {
33968
34306
  return /* @__PURE__ */ jsx44(Text, { color: BLUMA_TERMINAL.blue, children: p });
33969
34307
  }
33970
34308
  function renderToolHeader12({ args }) {
33971
- const path59 = args?.file_path ?? ".";
34309
+ const path60 = args?.file_path ?? ".";
33972
34310
  const oldText = typeof args?.old_string === "string" ? args.old_string : "";
33973
34311
  const newText = typeof args?.new_string === "string" ? args.new_string : "";
33974
34312
  const counts = countLineDiff(oldText, newText);
@@ -33978,7 +34316,7 @@ function renderToolHeader12({ args }) {
33978
34316
  action,
33979
34317
  /* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
33980
34318
  " ",
33981
- /* @__PURE__ */ jsx44(FilePathLink, { filePath: path59, color: BLUMA_TERMINAL.dim })
34319
+ /* @__PURE__ */ jsx44(FilePathLink, { filePath: path60, color: BLUMA_TERMINAL.dim })
33982
34320
  ] })
33983
34321
  ] }),
33984
34322
  /* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
@@ -34370,11 +34708,11 @@ function userFacingName13() {
34370
34708
  }
34371
34709
  function renderToolUseMessage14({ args }) {
34372
34710
  const q = args?.query ? `"${args.query}"` : "...";
34373
- const path59 = args?.path || ".";
34711
+ const path60 = args?.path || ".";
34374
34712
  return /* @__PURE__ */ jsxs30(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
34375
34713
  /* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.blue, children: q }),
34376
34714
  /* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.dim, children: " " }),
34377
- /* @__PURE__ */ jsx47(FilePathLink, { filePath: path59, color: BLUMA_TERMINAL.dim })
34715
+ /* @__PURE__ */ jsx47(FilePathLink, { filePath: path60, color: BLUMA_TERMINAL.dim })
34378
34716
  ] });
34379
34717
  }
34380
34718
  function renderToolHeader14({ args }) {
@@ -34896,7 +35234,7 @@ var loadSkillTool = createTool({
34896
35234
  });
34897
35235
 
34898
35236
  // src/app/agent/tools/FileWriteTool/UI.tsx
34899
- import fs43 from "fs";
35237
+ import fs44 from "fs";
34900
35238
  import { diffLines as diffLines3 } from "diff";
34901
35239
  import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
34902
35240
  function getFilePath(args) {
@@ -34911,7 +35249,7 @@ function getFilePath(args) {
34911
35249
  function readExistingFileText(filePath) {
34912
35250
  if (!filePath) return "";
34913
35251
  try {
34914
- return fs43.readFileSync(filePath, "utf-8");
35252
+ return fs44.readFileSync(filePath, "utf-8");
34915
35253
  } catch {
34916
35254
  return "";
34917
35255
  }
@@ -37853,8 +38191,8 @@ import {
37853
38191
 
37854
38192
  // src/app/ui/hooks/useAtCompletion.ts
37855
38193
  import { useEffect as useEffect11, useRef as useRef4, useState as useState13 } from "react";
37856
- import fs44 from "fs";
37857
- import path51 from "path";
38194
+ import fs45 from "fs";
38195
+ import path52 from "path";
37858
38196
  var MAX_RESULTS3 = 50;
37859
38197
  var DEFAULT_RECURSIVE_DEPTH = 2;
37860
38198
  function listPathSuggestions(baseDir, pattern) {
@@ -37862,7 +38200,7 @@ function listPathSuggestions(baseDir, pattern) {
37862
38200
  const patternEndsWithSlash = raw.endsWith("/");
37863
38201
  const relDir = raw.replace(/^\/+|\/+$/g, "");
37864
38202
  const filterPrefix = patternEndsWithSlash ? "" : relDir.split("/").slice(-1)[0] || "";
37865
- const listDir = path51.resolve(baseDir, relDir || ".");
38203
+ const listDir = path52.resolve(baseDir, relDir || ".");
37866
38204
  const results = [];
37867
38205
  const IGNORED_DIRS = ["node_modules", ".git", ".venv", "dist", "build"];
37868
38206
  const IGNORED_EXTS = [".pyc", ".class", ".o", ".map", ".log", ".tmp"];
@@ -37879,7 +38217,7 @@ function listPathSuggestions(baseDir, pattern) {
37879
38217
  }
37880
38218
  function pushEntry(entryPath, label, isDir) {
37881
38219
  if (results.length >= MAX_RESULTS3) return;
37882
- const clean = label.split(path51.sep).join("/").replace(/[]+/g, "");
38220
+ const clean = label.split(path52.sep).join("/").replace(/[]+/g, "");
37883
38221
  results.push({ label: clean + (isDir ? "/" : ""), fullPath: entryPath, isDir });
37884
38222
  }
37885
38223
  try {
@@ -37888,11 +38226,11 @@ function listPathSuggestions(baseDir, pattern) {
37888
38226
  while (queue.length && results.length < MAX_RESULTS3) {
37889
38227
  const node = queue.shift();
37890
38228
  try {
37891
- const entries = fs44.readdirSync(node.dir, { withFileTypes: true });
38229
+ const entries = fs45.readdirSync(node.dir, { withFileTypes: true });
37892
38230
  for (const entry of entries) {
37893
38231
  if (isIgnoredName(entry.name)) continue;
37894
- const entryAbs = path51.join(node.dir, entry.name);
37895
- const entryRel = node.rel ? path51.posix.join(node.rel, entry.name) : entry.name;
38232
+ const entryAbs = path52.join(node.dir, entry.name);
38233
+ const entryRel = node.rel ? path52.posix.join(node.rel, entry.name) : entry.name;
37896
38234
  if (entryRel.split("/").includes("node_modules")) continue;
37897
38235
  if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
37898
38236
  pushEntry(entryAbs, entryRel, entry.isDirectory());
@@ -37905,13 +38243,13 @@ function listPathSuggestions(baseDir, pattern) {
37905
38243
  }
37906
38244
  }
37907
38245
  } else {
37908
- const entries = fs44.readdirSync(listDir, { withFileTypes: true });
38246
+ const entries = fs45.readdirSync(listDir, { withFileTypes: true });
37909
38247
  for (const entry of entries) {
37910
38248
  if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
37911
38249
  if (isIgnoredName(entry.name)) continue;
37912
38250
  if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
37913
- const entryAbs = path51.join(listDir, entry.name);
37914
- const label = relDir ? path51.posix.join(relDir, entry.name) : entry.name;
38251
+ const entryAbs = path52.join(listDir, entry.name);
38252
+ const label = relDir ? path52.posix.join(relDir, entry.name) : entry.name;
37915
38253
  pushEntry(entryAbs, label, entry.isDirectory());
37916
38254
  if (results.length >= MAX_RESULTS3) break;
37917
38255
  }
@@ -38112,9 +38450,9 @@ var SlashSubmenuInlineComponent = ({ menu }) => {
38112
38450
  var SlashSubmenuInline = memo15(SlashSubmenuInlineComponent);
38113
38451
 
38114
38452
  // src/app/ui/utils/clipboardImage.ts
38115
- import fs45 from "fs";
38453
+ import fs46 from "fs";
38116
38454
  import os32 from "os";
38117
- import path52 from "path";
38455
+ import path53 from "path";
38118
38456
  import { spawn as spawn5, execFile as execFileCb, execSync as execSync4 } from "child_process";
38119
38457
  import { promisify as promisify2 } from "util";
38120
38458
 
@@ -38243,8 +38581,8 @@ function commandOnPath(cmd) {
38243
38581
  function unixClipboardHelperDirs() {
38244
38582
  const h = os32.homedir();
38245
38583
  return [
38246
- path52.join(h, ".local", "bin"),
38247
- path52.join(h, "bin"),
38584
+ path53.join(h, ".local", "bin"),
38585
+ path53.join(h, "bin"),
38248
38586
  "/usr/bin",
38249
38587
  "/usr/local/bin",
38250
38588
  "/bin",
@@ -38262,16 +38600,16 @@ function resolveHelperBinary(cmd) {
38262
38600
  return cmd;
38263
38601
  }
38264
38602
  for (const dir of unixClipboardHelperDirs()) {
38265
- const full = path52.join(dir, cmd);
38603
+ const full = path53.join(dir, cmd);
38266
38604
  try {
38267
- fs45.accessSync(full, fs45.constants.X_OK);
38605
+ fs46.accessSync(full, fs46.constants.X_OK);
38268
38606
  return full;
38269
38607
  } catch {
38270
38608
  }
38271
38609
  }
38272
38610
  for (const dir of unixClipboardHelperDirs()) {
38273
- const full = path52.join(dir, cmd);
38274
- if (fs45.existsSync(full)) {
38611
+ const full = path53.join(dir, cmd);
38612
+ if (fs46.existsSync(full)) {
38275
38613
  return full;
38276
38614
  }
38277
38615
  }
@@ -38312,17 +38650,17 @@ function writeBufferIfImage(baseDir, buf) {
38312
38650
  if (!ext) {
38313
38651
  return null;
38314
38652
  }
38315
- const out = path52.join(
38653
+ const out = path53.join(
38316
38654
  baseDir,
38317
38655
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
38318
38656
  );
38319
- fs45.writeFileSync(out, buf);
38657
+ fs46.writeFileSync(out, buf);
38320
38658
  return out;
38321
38659
  }
38322
38660
  function unlinkQuiet(p) {
38323
38661
  try {
38324
- if (fs45.existsSync(p)) {
38325
- fs45.unlinkSync(p);
38662
+ if (fs46.existsSync(p)) {
38663
+ fs46.unlinkSync(p);
38326
38664
  }
38327
38665
  } catch {
38328
38666
  }
@@ -38339,25 +38677,25 @@ async function tryDarwinClipboardy(baseDir) {
38339
38677
  return null;
38340
38678
  }
38341
38679
  for (const src of tmpPaths) {
38342
- if (!src || !fs45.existsSync(src)) {
38680
+ if (!src || !fs46.existsSync(src)) {
38343
38681
  continue;
38344
38682
  }
38345
38683
  let st;
38346
38684
  try {
38347
- st = fs45.statSync(src);
38685
+ st = fs46.statSync(src);
38348
38686
  } catch {
38349
38687
  continue;
38350
38688
  }
38351
38689
  if (st.size < 80 || st.size > CLIPBOARD_MAX_BYTES) {
38352
38690
  continue;
38353
38691
  }
38354
- const ext = path52.extname(src).toLowerCase();
38692
+ const ext = path53.extname(src).toLowerCase();
38355
38693
  const safeExt = ext && /^\.(png|jpe?g|gif|webp)$/i.test(ext) ? ext : ".png";
38356
- const out = path52.join(
38694
+ const out = path53.join(
38357
38695
  baseDir,
38358
38696
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
38359
38697
  );
38360
- fs45.copyFileSync(src, out);
38698
+ fs46.copyFileSync(src, out);
38361
38699
  for (const p of tmpPaths) {
38362
38700
  unlinkQuiet(p);
38363
38701
  }
@@ -38371,14 +38709,14 @@ async function tryDarwinClipboardy(baseDir) {
38371
38709
  return null;
38372
38710
  }
38373
38711
  async function tryWindowsPowerShell(outFile) {
38374
- const ps = process.env.SystemRoot != null ? path52.join(
38712
+ const ps = process.env.SystemRoot != null ? path53.join(
38375
38713
  process.env.SystemRoot,
38376
38714
  "System32",
38377
38715
  "WindowsPowerShell",
38378
38716
  "v1.0",
38379
38717
  "powershell.exe"
38380
38718
  ) : "powershell.exe";
38381
- if (!fs45.existsSync(ps)) {
38719
+ if (!fs46.existsSync(ps)) {
38382
38720
  return false;
38383
38721
  }
38384
38722
  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";
@@ -38400,7 +38738,7 @@ async function tryWindowsPowerShell(outFile) {
38400
38738
  return false;
38401
38739
  }
38402
38740
  try {
38403
- const st = fs45.statSync(outFile);
38741
+ const st = fs46.statSync(outFile);
38404
38742
  return st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES;
38405
38743
  } catch {
38406
38744
  return false;
@@ -38472,8 +38810,8 @@ function parseClipboardTextAsImagePath(raw) {
38472
38810
  s = s.slice(1, -1);
38473
38811
  }
38474
38812
  s = s.trim();
38475
- if (!CLIPBOARD_PATH_IMAGE_EXT.test(path52.extname(s))) return null;
38476
- const abs = path52.isAbsolute(s) ? path52.normalize(s) : path52.resolve(process.cwd(), s);
38813
+ if (!CLIPBOARD_PATH_IMAGE_EXT.test(path53.extname(s))) return null;
38814
+ const abs = path53.isAbsolute(s) ? path53.normalize(s) : path53.resolve(process.cwd(), s);
38477
38815
  return abs;
38478
38816
  }
38479
38817
  async function tryClipboardTextAsImageFile(baseDir) {
@@ -38487,19 +38825,19 @@ async function tryClipboardTextAsImageFile(baseDir) {
38487
38825
  const abs = parseClipboardTextAsImagePath(t);
38488
38826
  if (!abs) return null;
38489
38827
  try {
38490
- if (!fs45.existsSync(abs)) return null;
38491
- const st = fs45.statSync(abs);
38828
+ if (!fs46.existsSync(abs)) return null;
38829
+ const st = fs46.statSync(abs);
38492
38830
  if (!st.isFile() || st.size > CLIPBOARD_MAX_BYTES || st.size < 20) return null;
38493
38831
  } catch {
38494
38832
  return null;
38495
38833
  }
38496
- const ext = path52.extname(abs).toLowerCase();
38497
- const out = path52.join(
38834
+ const ext = path53.extname(abs).toLowerCase();
38835
+ const out = path53.join(
38498
38836
  baseDir,
38499
38837
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
38500
38838
  );
38501
38839
  try {
38502
- fs45.copyFileSync(abs, out);
38840
+ fs46.copyFileSync(abs, out);
38503
38841
  return out;
38504
38842
  } catch {
38505
38843
  return null;
@@ -38509,7 +38847,7 @@ async function tryLinuxShellPipelineSave(baseDir) {
38509
38847
  if (process.platform !== "linux" && process.platform !== "freebsd") {
38510
38848
  return null;
38511
38849
  }
38512
- const outPath = path52.join(
38850
+ const outPath = path53.join(
38513
38851
  baseDir,
38514
38852
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.tmp`
38515
38853
  );
@@ -38531,11 +38869,11 @@ printf '%s' "$OUT"
38531
38869
  maxBuffer: 4096
38532
38870
  });
38533
38871
  const written = String(stdout ?? "").trim();
38534
- if (written !== outPath || !fs45.existsSync(outPath)) {
38872
+ if (written !== outPath || !fs46.existsSync(outPath)) {
38535
38873
  unlinkQuiet(outPath);
38536
38874
  return null;
38537
38875
  }
38538
- const buf = fs45.readFileSync(outPath);
38876
+ const buf = fs46.readFileSync(outPath);
38539
38877
  unlinkQuiet(outPath);
38540
38878
  return writeBufferIfImage(baseDir, buf);
38541
38879
  } catch {
@@ -38556,8 +38894,8 @@ async function tryNativeClipboardImage() {
38556
38894
  }
38557
38895
  try {
38558
38896
  const result = readClipboardImageNative();
38559
- if (fs45.existsSync(result.path)) {
38560
- const st = fs45.statSync(result.path);
38897
+ if (fs46.existsSync(result.path)) {
38898
+ const st = fs46.statSync(result.path);
38561
38899
  if (st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES) {
38562
38900
  return result.path;
38563
38901
  }
@@ -38567,8 +38905,8 @@ async function tryNativeClipboardImage() {
38567
38905
  return null;
38568
38906
  }
38569
38907
  async function readClipboardImageToTempFile() {
38570
- const baseDir = path52.join(os32.homedir(), ".cache", "bluma", "clipboard");
38571
- fs45.mkdirSync(baseDir, { recursive: true });
38908
+ const baseDir = path53.join(os32.homedir(), ".cache", "bluma", "clipboard");
38909
+ fs46.mkdirSync(baseDir, { recursive: true });
38572
38910
  const nativeResult = await tryNativeClipboardImage();
38573
38911
  if (nativeResult) {
38574
38912
  return nativeResult;
@@ -38586,7 +38924,7 @@ async function readClipboardImageToTempFile() {
38586
38924
  }
38587
38925
  }
38588
38926
  if (process.platform === "win32") {
38589
- const outFile = path52.join(
38927
+ const outFile = path53.join(
38590
38928
  baseDir,
38591
38929
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.png`
38592
38930
  );
@@ -38627,7 +38965,7 @@ function expandLargePastePlaceholder(value, pending) {
38627
38965
  }
38628
38966
 
38629
38967
  // src/app/ui/components/InputPrompt.tsx
38630
- import fs46 from "fs";
38968
+ import fs47 from "fs";
38631
38969
  import { Fragment as Fragment7, jsx as jsx79, jsxs as jsxs62 } from "react/jsx-runtime";
38632
38970
  var persistedInputState = { text: "", cursorPosition: 0 };
38633
38971
  var StaticCursor = () => /* @__PURE__ */ jsx79(Box_default, { flexDirection: "row", flexWrap: "nowrap", children: /* @__PURE__ */ jsx79(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "\u2588 " }) });
@@ -38794,7 +39132,7 @@ var InputPrompt = memo16(({
38794
39132
  return;
38795
39133
  }
38796
39134
  if (routeText.startsWith("/")) {
38797
- const isFilePath = map.size > 0 && fs46.existsSync(routeText.split(/\s+/)[0]);
39135
+ const isFilePath = map.size > 0 && fs47.existsSync(routeText.split(/\s+/)[0]);
38798
39136
  if (isFilePath) {
38799
39137
  uiEventBus.emit("user_overlay", {
38800
39138
  kind: "message",
@@ -40962,8 +41300,8 @@ var renderSettingsEditUsage = () => {
40962
41300
 
40963
41301
  // src/app/agent/core/thread/thread_store.ts
40964
41302
  init_bluma_app_dir();
40965
- import path53 from "path";
40966
- import { promises as fs47 } from "fs";
41303
+ import path54 from "path";
41304
+ import { promises as fs48 } from "fs";
40967
41305
  import { randomUUID as randomUUID2 } from "crypto";
40968
41306
  var INDEX_VERSION = 1;
40969
41307
  var fileLocks2 = /* @__PURE__ */ new Map();
@@ -40988,9 +41326,9 @@ var ThreadStore = class {
40988
41326
  packageVersion;
40989
41327
  constructor() {
40990
41328
  const appDir = getPreferredAppDir();
40991
- this.threadsDir = path53.join(appDir, "threads");
40992
- this.archiveDir = path53.join(this.threadsDir, "archive");
40993
- this.indexPath = path53.join(this.threadsDir, "index.json");
41329
+ this.threadsDir = path54.join(appDir, "threads");
41330
+ this.archiveDir = path54.join(this.threadsDir, "archive");
41331
+ this.indexPath = path54.join(this.threadsDir, "index.json");
40994
41332
  this.packageVersion = process.env.npm_package_version || "0.0.0";
40995
41333
  }
40996
41334
  // ==================== Inicialização ====================
@@ -40998,10 +41336,10 @@ var ThreadStore = class {
40998
41336
  * Inicializa o diretório de threads
40999
41337
  */
41000
41338
  async initialize() {
41001
- await fs47.mkdir(this.threadsDir, { recursive: true });
41002
- await fs47.mkdir(this.archiveDir, { recursive: true });
41339
+ await fs48.mkdir(this.threadsDir, { recursive: true });
41340
+ await fs48.mkdir(this.archiveDir, { recursive: true });
41003
41341
  try {
41004
- await fs47.access(this.indexPath);
41342
+ await fs48.access(this.indexPath);
41005
41343
  } catch {
41006
41344
  await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
41007
41345
  }
@@ -41030,7 +41368,7 @@ var ThreadStore = class {
41030
41368
  async loadIndex() {
41031
41369
  return withFileLock2(this.indexPath, async () => {
41032
41370
  try {
41033
- const content = await fs47.readFile(this.indexPath, "utf-8");
41371
+ const content = await fs48.readFile(this.indexPath, "utf-8");
41034
41372
  return JSON.parse(content);
41035
41373
  } catch {
41036
41374
  return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
@@ -41041,8 +41379,8 @@ var ThreadStore = class {
41041
41379
  return withFileLock2(this.indexPath, async () => {
41042
41380
  index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
41043
41381
  const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
41044
- await fs47.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
41045
- await fs47.rename(tempPath, this.indexPath);
41382
+ await fs48.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
41383
+ await fs48.rename(tempPath, this.indexPath);
41046
41384
  });
41047
41385
  }
41048
41386
  // ==================== Git Info ====================
@@ -41112,7 +41450,7 @@ var ThreadStore = class {
41112
41450
  messages: params.initialMessages || []
41113
41451
  };
41114
41452
  const historyPath = this.buildDatedThreadHistoryPath(threadId);
41115
- await fs47.mkdir(path53.dirname(historyPath), { recursive: true });
41453
+ await fs48.mkdir(path54.dirname(historyPath), { recursive: true });
41116
41454
  await this.saveHistoryAtPath(historyPath, history);
41117
41455
  const index = await this.loadIndex();
41118
41456
  index.threads.unshift({
@@ -41167,7 +41505,7 @@ var ThreadStore = class {
41167
41505
  compressedSliceCount: source.history.compressedSliceCount
41168
41506
  };
41169
41507
  const historyPath = this.buildDatedThreadHistoryPath(newThreadId);
41170
- await fs47.mkdir(path53.dirname(historyPath), { recursive: true });
41508
+ await fs48.mkdir(path54.dirname(historyPath), { recursive: true });
41171
41509
  await this.saveHistoryAtPath(historyPath, history);
41172
41510
  const index = await this.loadIndex();
41173
41511
  index.threads.unshift({
@@ -41240,9 +41578,9 @@ var ThreadStore = class {
41240
41578
  const entry = index.threads[entryIndex];
41241
41579
  if (entry.status === "archived") return true;
41242
41580
  const oldPath = entry.historyPath || this.getLegacyHistoryPath(threadId);
41243
- const newPath = path53.join(this.archiveDir, `${threadId}.jsonl`);
41581
+ const newPath = path54.join(this.archiveDir, `${threadId}.jsonl`);
41244
41582
  try {
41245
- await fs47.rename(oldPath, newPath);
41583
+ await fs48.rename(oldPath, newPath);
41246
41584
  } catch (e) {
41247
41585
  if (e.code !== "ENOENT") throw e;
41248
41586
  }
@@ -41263,11 +41601,11 @@ var ThreadStore = class {
41263
41601
  if (entryIndex === -1) return false;
41264
41602
  const entry = index.threads[entryIndex];
41265
41603
  if (entry.status === "active") return true;
41266
- const oldPath = path53.join(this.archiveDir, `${threadId}.jsonl`);
41604
+ const oldPath = path54.join(this.archiveDir, `${threadId}.jsonl`);
41267
41605
  const newPath = this.buildDatedThreadHistoryPath(threadId);
41268
- await fs47.mkdir(path53.dirname(newPath), { recursive: true });
41606
+ await fs48.mkdir(path54.dirname(newPath), { recursive: true });
41269
41607
  try {
41270
- await fs47.rename(oldPath, newPath);
41608
+ await fs48.rename(oldPath, newPath);
41271
41609
  } catch (e) {
41272
41610
  if (e.code !== "ENOENT") throw e;
41273
41611
  }
@@ -41288,7 +41626,7 @@ var ThreadStore = class {
41288
41626
  if (entryIndex === -1) return false;
41289
41627
  const entry = index.threads[entryIndex];
41290
41628
  try {
41291
- await fs47.unlink(entry.historyPath);
41629
+ await fs48.unlink(entry.historyPath);
41292
41630
  } catch {
41293
41631
  }
41294
41632
  index.threads.splice(entryIndex, 1);
@@ -41298,28 +41636,28 @@ var ThreadStore = class {
41298
41636
  }
41299
41637
  // ==================== Histórico ====================
41300
41638
  getLegacyHistoryPath(threadId) {
41301
- return path53.join(this.threadsDir, `${threadId}.jsonl`);
41639
+ return path54.join(this.threadsDir, `${threadId}.jsonl`);
41302
41640
  }
41303
41641
  /** ~/.bluma/threads/YYYY/MM/DD/<threadId>.jsonl (data local de criação). */
41304
41642
  buildDatedThreadHistoryPath(threadId, at = /* @__PURE__ */ new Date()) {
41305
41643
  const y = String(at.getFullYear());
41306
41644
  const mo = String(at.getMonth() + 1).padStart(2, "0");
41307
41645
  const d = String(at.getDate()).padStart(2, "0");
41308
- return path53.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
41646
+ return path54.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
41309
41647
  }
41310
41648
  async resolveHistoryPath(threadId) {
41311
41649
  const index = await this.loadIndex();
41312
41650
  const entry = index.threads.find((t) => t.threadId === threadId);
41313
41651
  if (entry?.historyPath) {
41314
41652
  try {
41315
- await fs47.access(entry.historyPath);
41653
+ await fs48.access(entry.historyPath);
41316
41654
  return entry.historyPath;
41317
41655
  } catch {
41318
41656
  }
41319
41657
  }
41320
41658
  const legacy = this.getLegacyHistoryPath(threadId);
41321
41659
  try {
41322
- await fs47.access(legacy);
41660
+ await fs48.access(legacy);
41323
41661
  return legacy;
41324
41662
  } catch {
41325
41663
  return entry?.historyPath ?? legacy;
@@ -41336,9 +41674,9 @@ var ThreadStore = class {
41336
41674
  for (const msg of history.messages) {
41337
41675
  lines.push(JSON.stringify({ type: "message", ...msg }));
41338
41676
  }
41339
- await fs47.mkdir(path53.dirname(historyPath), { recursive: true }).catch(() => {
41677
+ await fs48.mkdir(path54.dirname(historyPath), { recursive: true }).catch(() => {
41340
41678
  });
41341
- await fs47.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
41679
+ await fs48.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
41342
41680
  }
41343
41681
  /**
41344
41682
  * Guarda o histórico de uma thread
@@ -41360,7 +41698,7 @@ var ThreadStore = class {
41360
41698
  ].filter((p, i, arr) => Boolean(p) && arr.indexOf(p) === i);
41361
41699
  for (const historyPath of pathsToTry) {
41362
41700
  try {
41363
- const content = await fs47.readFile(historyPath, "utf-8");
41701
+ const content = await fs48.readFile(historyPath, "utf-8");
41364
41702
  const lines = content.split("\n").filter(Boolean);
41365
41703
  const history = {
41366
41704
  threadId,
@@ -41395,7 +41733,7 @@ var ThreadStore = class {
41395
41733
  const entry = index.threads.find((t) => t.threadId === threadId);
41396
41734
  if (!entry) throw new Error(`Thread not found: ${threadId}`);
41397
41735
  const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
41398
- await fs47.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
41736
+ await fs48.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
41399
41737
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
41400
41738
  await this.saveIndex(index);
41401
41739
  }
@@ -43832,16 +44170,16 @@ import latestVersion from "latest-version";
43832
44170
  import semverGt from "semver/functions/gt.js";
43833
44171
  import semverValid from "semver/functions/valid.js";
43834
44172
  import { fileURLToPath as fileURLToPath7 } from "url";
43835
- import path55 from "path";
43836
- import fs49 from "fs";
44173
+ import path56 from "path";
44174
+ import fs50 from "fs";
43837
44175
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
43838
44176
  function findBlumaPackageJson(startDir) {
43839
44177
  let dir = startDir;
43840
44178
  for (let i = 0; i < 12; i++) {
43841
- const candidate = path55.join(dir, "package.json");
43842
- if (fs49.existsSync(candidate)) {
44179
+ const candidate = path56.join(dir, "package.json");
44180
+ if (fs50.existsSync(candidate)) {
43843
44181
  try {
43844
- const raw = fs49.readFileSync(candidate, "utf8");
44182
+ const raw = fs50.readFileSync(candidate, "utf8");
43845
44183
  const parsed = JSON.parse(raw);
43846
44184
  if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
43847
44185
  return { name: parsed.name, version: String(parsed.version) };
@@ -43849,7 +44187,7 @@ function findBlumaPackageJson(startDir) {
43849
44187
  } catch {
43850
44188
  }
43851
44189
  }
43852
- const parent = path55.dirname(dir);
44190
+ const parent = path56.dirname(dir);
43853
44191
  if (parent === dir) break;
43854
44192
  dir = parent;
43855
44193
  }
@@ -43858,13 +44196,13 @@ function findBlumaPackageJson(startDir) {
43858
44196
  function resolveInstalledBlumaPackage() {
43859
44197
  const tried = /* @__PURE__ */ new Set();
43860
44198
  const tryFrom = (dir) => {
43861
- const abs = path55.resolve(dir);
44199
+ const abs = path56.resolve(dir);
43862
44200
  if (tried.has(abs)) return null;
43863
44201
  tried.add(abs);
43864
44202
  return findBlumaPackageJson(abs);
43865
44203
  };
43866
44204
  try {
43867
- const fromBundle = tryFrom(path55.dirname(fileURLToPath7(import.meta.url)));
44205
+ const fromBundle = tryFrom(path56.dirname(fileURLToPath7(import.meta.url)));
43868
44206
  if (fromBundle) return fromBundle;
43869
44207
  } catch {
43870
44208
  }
@@ -43872,12 +44210,12 @@ function resolveInstalledBlumaPackage() {
43872
44210
  if (argv1 && !argv1.startsWith("-")) {
43873
44211
  try {
43874
44212
  let resolved = argv1;
43875
- if (path55.isAbsolute(argv1) && fs49.existsSync(argv1)) {
43876
- resolved = fs49.realpathSync(argv1);
44213
+ if (path56.isAbsolute(argv1) && fs50.existsSync(argv1)) {
44214
+ resolved = fs50.realpathSync(argv1);
43877
44215
  } else {
43878
- resolved = path55.resolve(process.cwd(), argv1);
44216
+ resolved = path56.resolve(process.cwd(), argv1);
43879
44217
  }
43880
- const fromArgv = tryFrom(path55.dirname(resolved));
44218
+ const fromArgv = tryFrom(path56.dirname(resolved));
43881
44219
  if (fromArgv) return fromArgv;
43882
44220
  } catch {
43883
44221
  }
@@ -44696,16 +45034,16 @@ function usePlanMode() {
44696
45034
 
44697
45035
  // src/app/hooks/useAgentMode.ts
44698
45036
  import { useState as useState22, useEffect as useEffect21, useCallback as useCallback9 } from "react";
44699
- import * as fs50 from "fs";
44700
- import * as path56 from "path";
45037
+ import * as fs51 from "fs";
45038
+ import * as path57 from "path";
44701
45039
  import { homedir as homedir4 } from "os";
44702
- var SETTINGS_PATH = path56.join(homedir4(), ".bluma", "settings.json");
45040
+ var SETTINGS_PATH = path57.join(homedir4(), ".bluma", "settings.json");
44703
45041
  function readAgentModeFromFile() {
44704
45042
  try {
44705
- if (!fs50.existsSync(SETTINGS_PATH)) {
45043
+ if (!fs51.existsSync(SETTINGS_PATH)) {
44706
45044
  return "default";
44707
45045
  }
44708
- const content = fs50.readFileSync(SETTINGS_PATH, "utf-8");
45046
+ const content = fs51.readFileSync(SETTINGS_PATH, "utf-8");
44709
45047
  const settings = JSON.parse(content);
44710
45048
  return settings.agentMode || "default";
44711
45049
  } catch (error) {
@@ -44724,16 +45062,16 @@ function useAgentMode() {
44724
45062
  }, []);
44725
45063
  const updateAgentMode = useCallback9((mode) => {
44726
45064
  try {
44727
- if (!fs50.existsSync(SETTINGS_PATH)) {
44728
- fs50.mkdirSync(path56.dirname(SETTINGS_PATH), { recursive: true });
45065
+ if (!fs51.existsSync(SETTINGS_PATH)) {
45066
+ fs51.mkdirSync(path57.dirname(SETTINGS_PATH), { recursive: true });
44729
45067
  }
44730
45068
  let settings = {};
44731
- if (fs50.existsSync(SETTINGS_PATH)) {
44732
- const content = fs50.readFileSync(SETTINGS_PATH, "utf-8");
45069
+ if (fs51.existsSync(SETTINGS_PATH)) {
45070
+ const content = fs51.readFileSync(SETTINGS_PATH, "utf-8");
44733
45071
  settings = JSON.parse(content);
44734
45072
  }
44735
45073
  settings.agentMode = mode;
44736
- fs50.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
45074
+ fs51.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
44737
45075
  setAgentMode(mode);
44738
45076
  } catch (error) {
44739
45077
  console.error("Failed to update agent mode:", error);
@@ -45997,10 +46335,10 @@ import { memo as memo26, useCallback as useCallback11, useEffect as useEffect23,
45997
46335
 
45998
46336
  // src/app/agent/session_manager/session_resume_browser.ts
45999
46337
  init_bluma_app_dir();
46000
- import path57 from "path";
46001
- import { promises as fs51 } from "fs";
46338
+ import path58 from "path";
46339
+ import { promises as fs52 } from "fs";
46002
46340
  function getSessionsRoot() {
46003
- return path57.join(getPreferredAppDir(), "sessions");
46341
+ return path58.join(getPreferredAppDir(), "sessions");
46004
46342
  }
46005
46343
  function normalizeSessionsCwd(cwd2) {
46006
46344
  return cwd2.replace(/\\/g, "/").split("/").filter((p) => p && p !== "." && p !== "..").join("/");
@@ -46021,9 +46359,9 @@ async function sessionEntryFromFile(absPath, sessionId) {
46021
46359
  let preview = "(no messages)";
46022
46360
  let lastActivityMs = Date.now();
46023
46361
  try {
46024
- const st = await fs51.stat(absPath);
46362
+ const st = await fs52.stat(absPath);
46025
46363
  lastActivityMs = st.mtimeMs;
46026
- const raw = await fs51.readFile(absPath, "utf-8");
46364
+ const raw = await fs52.readFile(absPath, "utf-8");
46027
46365
  const data = JSON.parse(raw);
46028
46366
  preview = previewFromHistory(data.conversation_history);
46029
46367
  const iso = data.last_updated || data.created_at;
@@ -46051,15 +46389,15 @@ function compareDirNames(a, b) {
46051
46389
  async function listSessionBrowserEntries(cwdRel) {
46052
46390
  const cwd2 = normalizeSessionsCwd(cwdRel);
46053
46391
  const root = getSessionsRoot();
46054
- const absDir = cwd2 ? path57.join(root, ...cwd2.split("/")) : root;
46392
+ const absDir = cwd2 ? path58.join(root, ...cwd2.split("/")) : root;
46055
46393
  const out = [];
46056
46394
  if (cwd2) {
46057
46395
  out.push({ kind: "up", label: ".." });
46058
46396
  }
46059
46397
  let dirents;
46060
46398
  try {
46061
- await fs51.mkdir(absDir, { recursive: true });
46062
- dirents = await fs51.readdir(absDir, { withFileTypes: true });
46399
+ await fs52.mkdir(absDir, { recursive: true });
46400
+ dirents = await fs52.readdir(absDir, { withFileTypes: true });
46063
46401
  } catch {
46064
46402
  return out;
46065
46403
  }
@@ -46068,7 +46406,7 @@ async function listSessionBrowserEntries(cwdRel) {
46068
46406
  for (const e of dirents) {
46069
46407
  const name = String(e.name);
46070
46408
  if (name.startsWith(".")) continue;
46071
- const full = path57.join(absDir, name);
46409
+ const full = path58.join(absDir, name);
46072
46410
  if (e.isDirectory()) {
46073
46411
  dirNames.push(name);
46074
46412
  } else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
@@ -46078,7 +46416,7 @@ async function listSessionBrowserEntries(cwdRel) {
46078
46416
  dirNames.sort(compareDirNames);
46079
46417
  const sessions = [];
46080
46418
  for (const { name, full } of sessionFiles) {
46081
- const sessionId = path57.basename(name, ".json");
46419
+ const sessionId = path58.basename(name, ".json");
46082
46420
  sessions.push(await sessionEntryFromFile(full, sessionId));
46083
46421
  }
46084
46422
  sessions.sort((a, b) => {
@@ -46443,9 +46781,9 @@ async function runAgentMode() {
46443
46781
  try {
46444
46782
  if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
46445
46783
  const filePath = args[inputFileIndex + 1];
46446
- rawPayload = fs52.readFileSync(filePath, "utf-8");
46784
+ rawPayload = fs53.readFileSync(filePath, "utf-8");
46447
46785
  } else {
46448
- rawPayload = fs52.readFileSync(0, "utf-8");
46786
+ rawPayload = fs53.readFileSync(0, "utf-8");
46449
46787
  }
46450
46788
  } catch (err) {
46451
46789
  writeAgentEvent(registrySessionId, {
@@ -46497,6 +46835,12 @@ async function runAgentMode() {
46497
46835
  if (envelopeUserRequest.trim()) {
46498
46836
  process.env.BLUMA_USER_REQUEST = envelopeUserRequest.trim();
46499
46837
  }
46838
+ if (envelope.from_agent?.trim()) {
46839
+ process.env.BLUMA_FROM_AGENT = envelope.from_agent.trim();
46840
+ }
46841
+ if (envelope.action?.trim()) {
46842
+ process.env.BLUMA_ACTION = envelope.action.trim();
46843
+ }
46500
46844
  const eventBus = new EventEmitter7();
46501
46845
  const sessionId = registrySessionId || envelope.session_id || envelope.message_id || uuidv412();
46502
46846
  process.env.BLUMA_SESSION_ID = sessionId;
@@ -46686,9 +47030,9 @@ async function runAgentMode() {
46686
47030
  }
46687
47031
  function readCliPackageVersion() {
46688
47032
  try {
46689
- const base = path58.dirname(fileURLToPath8(import.meta.url));
46690
- const pkgPath = path58.join(base, "..", "package.json");
46691
- const j = JSON.parse(fs52.readFileSync(pkgPath, "utf8"));
47033
+ const base = path59.dirname(fileURLToPath8(import.meta.url));
47034
+ const pkgPath = path59.join(base, "..", "package.json");
47035
+ const j = JSON.parse(fs53.readFileSync(pkgPath, "utf8"));
46692
47036
  return String(j.version || "0.0.0");
46693
47037
  } catch {
46694
47038
  return "0.0.0";
@@ -46814,7 +47158,7 @@ function startBackgroundAgent() {
46814
47158
  process.exit(1);
46815
47159
  }
46816
47160
  const filePath = args[inputFileIndex + 1];
46817
- const rawPayload = fs52.readFileSync(filePath, "utf-8");
47161
+ const rawPayload = fs53.readFileSync(filePath, "utf-8");
46818
47162
  const envelope = JSON.parse(rawPayload);
46819
47163
  const sessionId = envelope.session_id || envelope.message_id || uuidv412();
46820
47164
  registerSession({