@nomad-e/bluma-cli 0.6.9 → 0.8.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
@@ -291,6 +291,15 @@ function getSandboxPolicy() {
291
291
  workspaceRoot
292
292
  };
293
293
  }
294
+ function getArtifactsDirectory(policy = getSandboxPolicy()) {
295
+ return path6.join(policy.workspaceRoot, ".bluma", "artifacts");
296
+ }
297
+ function isPathInsideArtifactsDirectory(resolvedAbsolutePath, policy = getSandboxPolicy()) {
298
+ const artifactsRoot = getArtifactsDirectory(policy);
299
+ const resolved = path6.resolve(resolvedAbsolutePath);
300
+ const relative = path6.relative(artifactsRoot, resolved);
301
+ return relative === "" || !relative.startsWith("..") && !path6.isAbsolute(relative);
302
+ }
294
303
  function isPathInsideWorkspace(targetPath, policy = getSandboxPolicy()) {
295
304
  const resolved = path6.resolve(targetPath);
296
305
  const relative = path6.relative(policy.workspaceRoot, resolved);
@@ -397,11 +406,11 @@ var init_sandbox_policy = __esm({
397
406
  });
398
407
 
399
408
  // src/app/agent/runtime/task_store.ts
400
- import fs12 from "fs";
401
- import path10 from "path";
409
+ import fs13 from "fs";
410
+ import path11 from "path";
402
411
  function getStorePath() {
403
412
  const policy = getSandboxPolicy();
404
- return path10.join(policy.workspaceRoot, ".bluma", "task_state.json");
413
+ return path11.join(policy.workspaceRoot, ".bluma", "task_state.json");
405
414
  }
406
415
  function getDefaultState() {
407
416
  return {
@@ -417,8 +426,8 @@ function ensureLoaded() {
417
426
  return cache2;
418
427
  }
419
428
  try {
420
- if (fs12.existsSync(storePath)) {
421
- const raw = fs12.readFileSync(storePath, "utf-8");
429
+ if (fs13.existsSync(storePath)) {
430
+ const raw = fs13.readFileSync(storePath, "utf-8");
422
431
  const parsed = JSON.parse(raw);
423
432
  cache2 = {
424
433
  tasks: Array.isArray(parsed.tasks) ? parsed.tasks : [],
@@ -437,9 +446,9 @@ function ensureLoaded() {
437
446
  }
438
447
  function persist(state2) {
439
448
  const storePath = getStorePath();
440
- fs12.mkdirSync(path10.dirname(storePath), { recursive: true });
449
+ fs13.mkdirSync(path11.dirname(storePath), { recursive: true });
441
450
  state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
442
- fs12.writeFileSync(storePath, JSON.stringify(state2, null, 2), "utf-8");
451
+ fs13.writeFileSync(storePath, JSON.stringify(state2, null, 2), "utf-8");
443
452
  cache2 = state2;
444
453
  cachePath = storePath;
445
454
  }
@@ -826,37 +835,37 @@ __export(session_registry_exports, {
826
835
  removeSession: () => removeSession,
827
836
  updateSession: () => updateSession
828
837
  });
829
- import fs18 from "fs";
838
+ import fs19 from "fs";
830
839
  import os13 from "os";
831
- import path18 from "path";
840
+ import path19 from "path";
832
841
  function getRegistryDir() {
833
- return path18.join(process.env.HOME || os13.homedir(), ".bluma", "registry");
842
+ return path19.join(process.env.HOME || os13.homedir(), ".bluma", "registry");
834
843
  }
835
844
  function getRegistryFile() {
836
- return path18.join(getRegistryDir(), "sessions.json");
845
+ return path19.join(getRegistryDir(), "sessions.json");
837
846
  }
838
847
  function ensureRegistryDir() {
839
- fs18.mkdirSync(getRegistryDir(), { recursive: true });
848
+ fs19.mkdirSync(getRegistryDir(), { recursive: true });
840
849
  }
841
850
  function readRegistry() {
842
851
  ensureRegistryDir();
843
852
  const file = getRegistryFile();
844
- if (!fs18.existsSync(file)) {
853
+ if (!fs19.existsSync(file)) {
845
854
  return { entries: [] };
846
855
  }
847
856
  try {
848
- return JSON.parse(fs18.readFileSync(file, "utf-8"));
857
+ return JSON.parse(fs19.readFileSync(file, "utf-8"));
849
858
  } catch {
850
859
  return { entries: [] };
851
860
  }
852
861
  }
853
862
  function writeRegistry(state2) {
854
863
  ensureRegistryDir();
855
- fs18.writeFileSync(getRegistryFile(), JSON.stringify(state2, null, 2), "utf-8");
864
+ fs19.writeFileSync(getRegistryFile(), JSON.stringify(state2, null, 2), "utf-8");
856
865
  }
857
866
  function getSessionLogPath(sessionId) {
858
867
  ensureRegistryDir();
859
- return path18.join(getRegistryDir(), `${sessionId}.jsonl`);
868
+ return path19.join(getRegistryDir(), `${sessionId}.jsonl`);
860
869
  }
861
870
  function registerSession(entry) {
862
871
  const state2 = readRegistry();
@@ -901,13 +910,13 @@ function removeSession(sessionId) {
901
910
  }
902
911
  function appendSessionLog(sessionId, payload) {
903
912
  const logFile = getSessionLogPath(sessionId);
904
- fs18.appendFileSync(logFile, `${JSON.stringify(payload)}
913
+ fs19.appendFileSync(logFile, `${JSON.stringify(payload)}
905
914
  `, "utf-8");
906
915
  }
907
916
  function readSessionLog(sessionId) {
908
917
  const logFile = getSessionLogPath(sessionId);
909
- if (!fs18.existsSync(logFile)) return [];
910
- return fs18.readFileSync(logFile, "utf-8").split("\n").filter(Boolean);
918
+ if (!fs19.existsSync(logFile)) return [];
919
+ return fs19.readFileSync(logFile, "utf-8").split("\n").filter(Boolean);
911
920
  }
912
921
  var init_session_registry = __esm({
913
922
  "src/app/agent/runtime/session_registry.ts"() {
@@ -916,17 +925,17 @@ var init_session_registry = __esm({
916
925
  });
917
926
 
918
927
  // src/app/agent/utils/logger.ts
919
- import fs19 from "fs";
928
+ import fs20 from "fs";
920
929
  import os14 from "os";
921
- import path19 from "path";
930
+ import path20 from "path";
922
931
  function getLogDir() {
923
- const dir = path19.join(process.env.HOME || os14.homedir(), ".bluma", "logs");
924
- fs19.mkdirSync(dir, { recursive: true });
932
+ const dir = path20.join(process.env.HOME || os14.homedir(), ".bluma", "logs");
933
+ fs20.mkdirSync(dir, { recursive: true });
925
934
  return dir;
926
935
  }
927
936
  function getLogFilePath() {
928
937
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
929
- return path19.join(getLogDir(), `bluma-${today}.log`);
938
+ return path20.join(getLogDir(), `bluma-${today}.log`);
930
939
  }
931
940
  var BluMaLogger, logger;
932
941
  var init_logger = __esm({
@@ -956,7 +965,7 @@ var init_logger = __esm({
956
965
  turnId: this.turnId
957
966
  };
958
967
  try {
959
- fs19.appendFileSync(this.logFile, JSON.stringify(logEntry) + "\n");
968
+ fs20.appendFileSync(this.logFile, JSON.stringify(logEntry) + "\n");
960
969
  } catch {
961
970
  }
962
971
  if (this.consoleEnabled) {
@@ -1057,19 +1066,19 @@ __export(mailbox_registry_exports, {
1057
1066
  sendSignal: () => sendSignal,
1058
1067
  sendToMailbox: () => sendToMailbox
1059
1068
  });
1060
- import fs20 from "fs";
1069
+ import fs21 from "fs";
1061
1070
  import os15 from "os";
1062
- import path20 from "path";
1071
+ import path21 from "path";
1063
1072
  import { EventEmitter as EventEmitter4 } from "events";
1064
1073
  import { v4 as uuidv45 } from "uuid";
1065
1074
  function getMailboxesDir() {
1066
1075
  if (mailboxesDir) return mailboxesDir;
1067
- mailboxesDir = path20.join(process.env.HOME || os15.homedir(), ".bluma", "mailboxes");
1068
- fs20.mkdirSync(mailboxesDir, { recursive: true });
1076
+ mailboxesDir = path21.join(process.env.HOME || os15.homedir(), ".bluma", "mailboxes");
1077
+ fs21.mkdirSync(mailboxesDir, { recursive: true });
1069
1078
  return mailboxesDir;
1070
1079
  }
1071
1080
  function getMailboxPath(sessionId, type) {
1072
- return path20.join(getMailboxesDir(), `${sessionId}.${type}`);
1081
+ return path21.join(getMailboxesDir(), `${sessionId}.${type}`);
1073
1082
  }
1074
1083
  function sendToMailbox(sessionId, type, message2) {
1075
1084
  return mailbox.sendToMailbox(sessionId, type, message2);
@@ -1095,7 +1104,7 @@ function pruneMailbox(sessionId, type, keepLast = 100) {
1095
1104
  const pruned = queue.slice(-keepLast);
1096
1105
  mailbox.queues.set(sessionId, pruned);
1097
1106
  const filePath = getMailboxPath(sessionId, type);
1098
- fs20.writeFileSync(filePath, pruned.map((m) => JSON.stringify(m)).join("\n") + "\n", "utf-8");
1107
+ fs21.writeFileSync(filePath, pruned.map((m) => JSON.stringify(m)).join("\n") + "\n", "utf-8");
1099
1108
  }
1100
1109
  function listActiveMailboxes() {
1101
1110
  const result = [];
@@ -1163,16 +1172,16 @@ var init_mailbox_registry = __esm({
1163
1172
  loadExistingMailboxes() {
1164
1173
  try {
1165
1174
  const dir = getMailboxesDir();
1166
- const files = fs20.readdirSync(dir);
1175
+ const files = fs21.readdirSync(dir);
1167
1176
  for (const file of files) {
1168
1177
  if (!file.endsWith(".in") && !file.endsWith(".out") && !file.endsWith(".sig")) {
1169
1178
  continue;
1170
1179
  }
1171
1180
  const type = file.split(".").pop();
1172
1181
  const sessionId = file.slice(0, -(type.length + 1));
1173
- const filePath = path20.join(dir, file);
1182
+ const filePath = path21.join(dir, file);
1174
1183
  try {
1175
- const content = fs20.readFileSync(filePath, "utf-8");
1184
+ const content = fs21.readFileSync(filePath, "utf-8");
1176
1185
  const lines = content.trim().split("\n").filter(Boolean);
1177
1186
  const messages = [];
1178
1187
  for (const line of lines) {
@@ -1209,7 +1218,7 @@ var init_mailbox_registry = __esm({
1209
1218
  this.queues.set(sessionId, queue);
1210
1219
  try {
1211
1220
  const filePath = getMailboxPath(sessionId, type);
1212
- fs20.appendFileSync(filePath, JSON.stringify(fullMessage) + "\n", "utf-8");
1221
+ fs21.appendFileSync(filePath, JSON.stringify(fullMessage) + "\n", "utf-8");
1213
1222
  } catch {
1214
1223
  }
1215
1224
  this.emit(`message:${sessionId}`, fullMessage);
@@ -1258,7 +1267,7 @@ var init_mailbox_registry = __esm({
1258
1267
  this.emit(`signal:${sessionId}`, signal);
1259
1268
  try {
1260
1269
  const filePath = getMailboxPath(sessionId, "sig");
1261
- fs20.appendFileSync(filePath, JSON.stringify(signal) + "\n", "utf-8");
1270
+ fs21.appendFileSync(filePath, JSON.stringify(signal) + "\n", "utf-8");
1262
1271
  } catch {
1263
1272
  }
1264
1273
  return signal.id;
@@ -1272,9 +1281,9 @@ var init_mailbox_registry = __esm({
1272
1281
  ensureMailbox(sessionId) {
1273
1282
  const dir = getMailboxesDir();
1274
1283
  for (const type of ["in", "out", "sig"]) {
1275
- const filePath = path20.join(dir, `${sessionId}.${type}`);
1276
- if (!fs20.existsSync(filePath)) {
1277
- fs20.writeFileSync(filePath, "", "utf-8");
1284
+ const filePath = path21.join(dir, `${sessionId}.${type}`);
1285
+ if (!fs21.existsSync(filePath)) {
1286
+ fs21.writeFileSync(filePath, "", "utf-8");
1278
1287
  }
1279
1288
  }
1280
1289
  }
@@ -1284,8 +1293,8 @@ var init_mailbox_registry = __esm({
1284
1293
  removeMailbox(sessionId) {
1285
1294
  const dir = getMailboxesDir();
1286
1295
  for (const type of ["in", "out", "sig"]) {
1287
- const filePath = path20.join(dir, `${sessionId}.${type}`);
1288
- if (fs20.existsSync(filePath)) fs20.unlinkSync(filePath);
1296
+ const filePath = path21.join(dir, `${sessionId}.${type}`);
1297
+ if (fs21.existsSync(filePath)) fs21.unlinkSync(filePath);
1289
1298
  }
1290
1299
  this.queues.delete(sessionId);
1291
1300
  this.signalQueues.delete(sessionId);
@@ -1304,9 +1313,9 @@ __export(AgentCoordinationTool_exports, {
1304
1313
  spawnAgent: () => spawnAgent,
1305
1314
  waitAgent: () => waitAgent
1306
1315
  });
1307
- import fs21 from "fs";
1316
+ import fs22 from "fs";
1308
1317
  import os16 from "os";
1309
- import path21 from "path";
1318
+ import path22 from "path";
1310
1319
  import { spawn as spawn4 } from "child_process";
1311
1320
  import { v4 as uuidv46 } from "uuid";
1312
1321
  function readUserContextFromEnv() {
@@ -1420,10 +1429,10 @@ async function spawnAgent(args) {
1420
1429
  spawnLog.error("Payload NOT serializable", { error: e.message });
1421
1430
  throw new BluMaError("WORKER_CONTEXT_NOT_SERIALIZABLE" /* WORKER_CONTEXT_NOT_SERIALIZABLE */, `Worker context is not JSON-serializable: ${e.message}`);
1422
1431
  }
1423
- const payloadDir = fs21.mkdtempSync(path21.join(os16.tmpdir(), "bluma-worker-"));
1424
- const payloadPath = path21.join(payloadDir, `${sessionId}.json`);
1432
+ const payloadDir = fs22.mkdtempSync(path22.join(os16.tmpdir(), "bluma-worker-"));
1433
+ const payloadPath = path22.join(payloadDir, `${sessionId}.json`);
1425
1434
  try {
1426
- fs21.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
1435
+ fs22.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
1427
1436
  spawnLog.debug("Payload written", { payloadPath });
1428
1437
  } catch (e) {
1429
1438
  spawnLog.error("Failed to write payload", { error: e.message, payloadPath });
@@ -2192,11 +2201,11 @@ var NodeFsOperations = {
2192
2201
  if (fd) fs.closeSync(fd);
2193
2202
  }
2194
2203
  },
2195
- appendFileSync(path50, data, options) {
2196
- const _ = slowLogging`fs.appendFileSync(${path50}, ${data.length} chars)`;
2204
+ appendFileSync(path51, data, options) {
2205
+ const _ = slowLogging`fs.appendFileSync(${path51}, ${data.length} chars)`;
2197
2206
  if (options?.mode !== void 0) {
2198
2207
  try {
2199
- const fd = fs.openSync(path50, "ax", options.mode);
2208
+ const fd = fs.openSync(path51, "ax", options.mode);
2200
2209
  try {
2201
2210
  fs.appendFileSync(fd, data);
2202
2211
  } finally {
@@ -2207,35 +2216,35 @@ var NodeFsOperations = {
2207
2216
  if (getErrnoCode(e) !== "EEXIST") throw e;
2208
2217
  }
2209
2218
  }
2210
- fs.appendFileSync(path50, data);
2219
+ fs.appendFileSync(path51, data);
2211
2220
  },
2212
2221
  copyFileSync(src, dest) {
2213
2222
  const _ = slowLogging`fs.copyFileSync(${src} → ${dest})`;
2214
2223
  fs.copyFileSync(src, dest);
2215
2224
  },
2216
- unlinkSync(path50) {
2217
- const _ = slowLogging`fs.unlinkSync(${path50})`;
2218
- fs.unlinkSync(path50);
2225
+ unlinkSync(path51) {
2226
+ const _ = slowLogging`fs.unlinkSync(${path51})`;
2227
+ fs.unlinkSync(path51);
2219
2228
  },
2220
2229
  renameSync(oldPath, newPath) {
2221
2230
  const _ = slowLogging`fs.renameSync(${oldPath} → ${newPath})`;
2222
2231
  fs.renameSync(oldPath, newPath);
2223
2232
  },
2224
- linkSync(target, path50) {
2225
- const _ = slowLogging`fs.linkSync(${target} → ${path50})`;
2226
- fs.linkSync(target, path50);
2233
+ linkSync(target, path51) {
2234
+ const _ = slowLogging`fs.linkSync(${target} → ${path51})`;
2235
+ fs.linkSync(target, path51);
2227
2236
  },
2228
- symlinkSync(target, path50, type) {
2229
- const _ = slowLogging`fs.symlinkSync(${target} → ${path50})`;
2230
- fs.symlinkSync(target, path50, type);
2237
+ symlinkSync(target, path51, type) {
2238
+ const _ = slowLogging`fs.symlinkSync(${target} → ${path51})`;
2239
+ fs.symlinkSync(target, path51, type);
2231
2240
  },
2232
- readlinkSync(path50) {
2233
- const _ = slowLogging`fs.readlinkSync(${path50})`;
2234
- return fs.readlinkSync(path50);
2241
+ readlinkSync(path51) {
2242
+ const _ = slowLogging`fs.readlinkSync(${path51})`;
2243
+ return fs.readlinkSync(path51);
2235
2244
  },
2236
- realpathSync(path50) {
2237
- const _ = slowLogging`fs.realpathSync(${path50})`;
2238
- return fs.realpathSync(path50).normalize("NFC");
2245
+ realpathSync(path51) {
2246
+ const _ = slowLogging`fs.realpathSync(${path51})`;
2247
+ return fs.realpathSync(path51).normalize("NFC");
2239
2248
  },
2240
2249
  mkdirSync(dirPath, options) {
2241
2250
  const _ = slowLogging`fs.mkdirSync(${dirPath})`;
@@ -2268,12 +2277,12 @@ var NodeFsOperations = {
2268
2277
  const _ = slowLogging`fs.rmdirSync(${dirPath})`;
2269
2278
  fs.rmdirSync(dirPath);
2270
2279
  },
2271
- rmSync(path50, options) {
2272
- const _ = slowLogging`fs.rmSync(${path50})`;
2273
- fs.rmSync(path50, options);
2280
+ rmSync(path51, options) {
2281
+ const _ = slowLogging`fs.rmSync(${path51})`;
2282
+ fs.rmSync(path51, options);
2274
2283
  },
2275
- createWriteStream(path50) {
2276
- return fs.createWriteStream(path50);
2284
+ createWriteStream(path51) {
2285
+ return fs.createWriteStream(path51);
2277
2286
  },
2278
2287
  async readFileBytes(fsPath, maxBytes) {
2279
2288
  if (maxBytes === void 0) {
@@ -2380,12 +2389,12 @@ function shouldLogDebugMessage(message2) {
2380
2389
  var hasFormattedOutput = false;
2381
2390
  var debugWriter = null;
2382
2391
  var pendingWrite = Promise.resolve();
2383
- async function appendAsync(needMkdir, dir, path50, content) {
2392
+ async function appendAsync(needMkdir, dir, path51, content) {
2384
2393
  if (needMkdir) {
2385
2394
  await mkdir(dir, { recursive: true }).catch(() => {
2386
2395
  });
2387
2396
  }
2388
- await appendFile(path50, content);
2397
+ await appendFile(path51, content);
2389
2398
  void updateLatestDebugLogSymlink();
2390
2399
  }
2391
2400
  function noop() {
@@ -2395,8 +2404,8 @@ function getDebugWriter() {
2395
2404
  let ensuredDir = null;
2396
2405
  debugWriter = createBufferedWriter({
2397
2406
  writeFn: (content) => {
2398
- const path50 = getDebugLogPath();
2399
- const dir = dirname(path50);
2407
+ const path51 = getDebugLogPath();
2408
+ const dir = dirname(path51);
2400
2409
  const needMkdir = ensuredDir !== dir;
2401
2410
  ensuredDir = dir;
2402
2411
  if (isDebugMode()) {
@@ -2406,11 +2415,11 @@ function getDebugWriter() {
2406
2415
  } catch {
2407
2416
  }
2408
2417
  }
2409
- getFsImplementation().appendFileSync(path50, content);
2418
+ getFsImplementation().appendFileSync(path51, content);
2410
2419
  void updateLatestDebugLogSymlink();
2411
2420
  return;
2412
2421
  }
2413
- pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path50, content)).catch(noop);
2422
+ pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path51, content)).catch(noop);
2414
2423
  },
2415
2424
  flushIntervalMs: 1e3,
2416
2425
  maxBufferSize: 100,
@@ -8597,8 +8606,8 @@ import codeExcerpt from "code-excerpt";
8597
8606
  import { readFileSync as readFileSync2 } from "fs";
8598
8607
  import StackUtils from "stack-utils";
8599
8608
  import { jsx as jsx6, jsxs } from "react/jsx-runtime";
8600
- var cleanupPath = (path50) => {
8601
- return path50?.replace(`file://${process.cwd()}/`, "");
8609
+ var cleanupPath = (path51) => {
8610
+ return path51?.replace(`file://${process.cwd()}/`, "");
8602
8611
  };
8603
8612
  var stackUtils;
8604
8613
  function getStackUtils() {
@@ -12575,8 +12584,8 @@ var getInstance = (stdout, createInstance) => {
12575
12584
 
12576
12585
  // src/main.ts
12577
12586
  import { EventEmitter as EventEmitter7 } from "events";
12578
- import fs46 from "fs";
12579
- import path49 from "path";
12587
+ import fs47 from "fs";
12588
+ import path50 from "path";
12580
12589
  import { fileURLToPath as fileURLToPath7 } from "url";
12581
12590
  import { spawn as spawn6 } from "child_process";
12582
12591
  import { v4 as uuidv412 } from "uuid";
@@ -15120,12 +15129,12 @@ InputPrompt.displayName = "InputPrompt";
15120
15129
 
15121
15130
  // src/app/agent/agent.ts
15122
15131
  import * as dotenv from "dotenv";
15123
- import path43 from "path";
15132
+ import path44 from "path";
15124
15133
  import os32 from "os";
15125
15134
 
15126
15135
  // src/app/agent/tool_invoker.ts
15127
- import { promises as fs28 } from "fs";
15128
- import path28 from "path";
15136
+ import { promises as fs29 } from "fs";
15137
+ import path29 from "path";
15129
15138
  import { fileURLToPath as fileURLToPath3 } from "url";
15130
15139
 
15131
15140
  // src/app/agent/tools/EditTool/EditTool.ts
@@ -15366,7 +15375,7 @@ function createDiff(filename, oldContent, newContent) {
15366
15375
  const prefix = part.added ? "+" : part.removed ? "-" : " ";
15367
15376
  const lines = part.value.split("\n").slice(0, -1);
15368
15377
  for (const line of lines) {
15369
- if (line === "\") continue;
15378
+ if (line === "") continue;
15370
15379
  diffString += `${prefix}${line}
15371
15380
  `;
15372
15381
  lineCount++;
@@ -15668,8 +15677,168 @@ ${finalDiff}`,
15668
15677
 
15669
15678
  // src/app/agent/tools/MessageTool/MessageTool.ts
15670
15679
  import { v4 as uuidv42 } from "uuid";
15680
+
15681
+ // src/app/agent/runtime/sandbox_message_validation.ts
15682
+ init_sandbox_policy();
15683
+ import fs9 from "fs";
15684
+ import path8 from "path";
15685
+ var HTTP_URL_PATTERN = /^https?:\/\//i;
15686
+ function normalizeAttachmentPath(entry) {
15687
+ if (typeof entry === "string") {
15688
+ const trimmed = entry.trim();
15689
+ return trimmed.length > 0 ? trimmed : null;
15690
+ }
15691
+ if (entry && typeof entry === "object" && "path" in entry) {
15692
+ const pathValue = entry.path;
15693
+ if (typeof pathValue === "string") {
15694
+ const trimmed = pathValue.trim();
15695
+ return trimmed.length > 0 ? trimmed : null;
15696
+ }
15697
+ }
15698
+ return null;
15699
+ }
15700
+ function looksLikeHttpUrl(value) {
15701
+ return HTTP_URL_PATTERN.test(value.trim());
15702
+ }
15703
+ function taskExpectsFileDeliverable(userRequest) {
15704
+ if (!userRequest || !userRequest.trim()) {
15705
+ return false;
15706
+ }
15707
+ const text = userRequest.toLowerCase();
15708
+ return /\b(pdf|excel|xlsx|csv|docx|documento|relat[oó]rio|ficheiro|arquivo|export|download|artifact|artefacto|anexo)\b/i.test(
15709
+ text
15710
+ );
15711
+ }
15712
+ function validateSandboxMessageDeliverables(args, policy = getSandboxPolicy(), options) {
15713
+ if (!policy.isSandbox) {
15714
+ return { ok: true };
15715
+ }
15716
+ if (args.message_type !== "result") {
15717
+ return { ok: true };
15718
+ }
15719
+ const attachments = args.attachments;
15720
+ const hasAttachments = Array.isArray(attachments) && attachments.length > 0;
15721
+ if (!hasAttachments && taskExpectsFileDeliverable(options?.userRequest)) {
15722
+ return {
15723
+ ok: false,
15724
+ error: "File deliverables must use attachments[]",
15725
+ details: 'Write the file under .bluma/artifacts/ with file_write, then call message(result) with attachments: [".bluma/artifacts/<filename>"]. Paths in content alone are not delivered to the orchestrator.'
15726
+ };
15727
+ }
15728
+ if (!hasAttachments) {
15729
+ return { ok: true };
15730
+ }
15731
+ for (const entry of attachments) {
15732
+ const raw = normalizeAttachmentPath(entry);
15733
+ if (!raw) {
15734
+ return {
15735
+ ok: false,
15736
+ error: "Invalid attachment entry",
15737
+ details: 'Each attachment must be a workspace file path (string) or { path: ".bluma/artifacts/..." }. URLs are not allowed.'
15738
+ };
15739
+ }
15740
+ if (looksLikeHttpUrl(raw)) {
15741
+ return {
15742
+ ok: false,
15743
+ error: "Attachments cannot be URLs in sandbox",
15744
+ details: `Rejected "${raw}". Use file_write to create a file under .bluma/artifacts/ and pass that path in attachments[].`
15745
+ };
15746
+ }
15747
+ let resolved;
15748
+ try {
15749
+ resolved = resolveWorkspacePath(raw, policy);
15750
+ } catch (err) {
15751
+ return {
15752
+ ok: false,
15753
+ error: "Attachment path is outside the sandbox workspace",
15754
+ details: err instanceof Error ? err.message : String(err)
15755
+ };
15756
+ }
15757
+ if (!fs9.existsSync(resolved)) {
15758
+ return {
15759
+ ok: false,
15760
+ error: "Attachment file does not exist",
15761
+ details: `Create the deliverable first (file_write \u2192 .bluma/artifacts/...). Missing: ${resolved}`
15762
+ };
15763
+ }
15764
+ const stat = fs9.statSync(resolved);
15765
+ if (!stat.isFile()) {
15766
+ return {
15767
+ ok: false,
15768
+ error: "Attachment must be a file",
15769
+ details: resolved
15770
+ };
15771
+ }
15772
+ if (!isPathInsideArtifactsDirectory(resolved, policy)) {
15773
+ const artifactsDir2 = getArtifactsDirectory(policy);
15774
+ return {
15775
+ ok: false,
15776
+ error: "Files are delivered only via .bluma/artifacts/",
15777
+ details: `Move or copy the deliverable to ${path8.relative(policy.workspaceRoot, artifactsDir2)}/ and pass that path in attachments[]. Rejected: ${raw}`
15778
+ };
15779
+ }
15780
+ }
15781
+ return { ok: true };
15782
+ }
15783
+
15784
+ // src/app/agent/tools/MessageTool/normalize_args.ts
15785
+ function normalizeMessageToolArgs(raw) {
15786
+ const content = typeof raw.content === "string" ? raw.content : typeof raw.body === "string" ? raw.body : typeof raw.text === "string" ? raw.text : "";
15787
+ const messageTypeRaw = raw.message_type ?? raw.messageType ?? raw.type ?? raw.kind ?? "info";
15788
+ const message_type = String(messageTypeRaw).toLowerCase() === "result" ? "result" : "info";
15789
+ let attachments;
15790
+ const rawAttachments = raw.attachments;
15791
+ if (Array.isArray(rawAttachments)) {
15792
+ attachments = rawAttachments.map((entry) => {
15793
+ if (typeof entry === "string") {
15794
+ return entry.trim();
15795
+ }
15796
+ if (entry && typeof entry === "object" && "path" in entry) {
15797
+ const pathValue = entry.path;
15798
+ return typeof pathValue === "string" ? pathValue.trim() : "";
15799
+ }
15800
+ return "";
15801
+ }).filter((value) => value.length > 0);
15802
+ } else if (typeof rawAttachments === "string" && rawAttachments.trim()) {
15803
+ try {
15804
+ const parsed = JSON.parse(rawAttachments);
15805
+ if (Array.isArray(parsed)) {
15806
+ attachments = parsed.filter((value) => typeof value === "string");
15807
+ }
15808
+ } catch {
15809
+ attachments = [rawAttachments.trim()];
15810
+ }
15811
+ }
15812
+ return {
15813
+ content,
15814
+ message_type,
15815
+ attachments: attachments?.length ? attachments : void 0
15816
+ };
15817
+ }
15818
+
15819
+ // src/app/agent/tools/MessageTool/MessageTool.ts
15671
15820
  function message(args) {
15672
- const { content, message_type, attachments } = args;
15821
+ const { content, message_type, attachments } = normalizeMessageToolArgs(
15822
+ args
15823
+ );
15824
+ const deliverableCheck = validateSandboxMessageDeliverables(
15825
+ {
15826
+ message_type,
15827
+ attachments,
15828
+ content
15829
+ },
15830
+ void 0,
15831
+ { userRequest: process.env.BLUMA_USER_REQUEST }
15832
+ );
15833
+ if (!deliverableCheck.ok) {
15834
+ return Promise.resolve({
15835
+ status: "error",
15836
+ success: false,
15837
+ error: deliverableCheck.error,
15838
+ details: deliverableCheck.details,
15839
+ message_type
15840
+ });
15841
+ }
15673
15842
  const result = {
15674
15843
  type: "message",
15675
15844
  message_type,
@@ -15686,8 +15855,8 @@ function message(args) {
15686
15855
  }
15687
15856
 
15688
15857
  // src/app/agent/tools/LsTool/LsTool.ts
15689
- import { promises as fs9 } from "fs";
15690
- import path8 from "path";
15858
+ import { promises as fs10 } from "fs";
15859
+ import path9 from "path";
15691
15860
  import os7 from "os";
15692
15861
  import { minimatch } from "minimatch";
15693
15862
  var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
@@ -15707,7 +15876,7 @@ var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
15707
15876
  ]);
15708
15877
  function expandPath(p) {
15709
15878
  if (p === "~" || p.startsWith("~/")) {
15710
- return path8.join(os7.homedir(), p.slice(2));
15879
+ return path9.join(os7.homedir(), p.slice(2));
15711
15880
  }
15712
15881
  return p;
15713
15882
  }
@@ -15733,8 +15902,8 @@ async function ls(args) {
15733
15902
  max_depth
15734
15903
  } = args;
15735
15904
  try {
15736
- const basePath = path8.resolve(expandPath(directory_path));
15737
- const stat = await fs9.stat(basePath).catch(() => null);
15905
+ const basePath = path9.resolve(expandPath(directory_path));
15906
+ const stat = await fs10.stat(basePath).catch(() => null);
15738
15907
  if (!stat || !stat.isDirectory()) {
15739
15908
  throw new Error(`Directory '${directory_path}' not found.`);
15740
15909
  }
@@ -15746,11 +15915,11 @@ async function ls(args) {
15746
15915
  const allDirs = [];
15747
15916
  const walk = async (currentDir, currentDepth) => {
15748
15917
  if (max_depth !== void 0 && currentDepth >= max_depth) return;
15749
- const entries = await fs9.readdir(currentDir, { withFileTypes: true });
15918
+ const entries = await fs10.readdir(currentDir, { withFileTypes: true });
15750
15919
  for (const entry of entries) {
15751
15920
  const entryName = entry.name;
15752
- const fullPath = path8.join(currentDir, entryName);
15753
- const relativePath = path8.relative(basePath, fullPath).split(path8.sep).join("/");
15921
+ const fullPath = path9.join(currentDir, entryName);
15922
+ const relativePath = path9.relative(basePath, fullPath).split(path9.sep).join("/");
15754
15923
  const isHidden = entryName.startsWith(".");
15755
15924
  if (shouldIgnore(entryName, allIgnorePatterns) || isHidden && !show_hidden) {
15756
15925
  continue;
@@ -15759,7 +15928,7 @@ async function ls(args) {
15759
15928
  allDirs.push(relativePath);
15760
15929
  if (recursive) await walk(fullPath, currentDepth + 1);
15761
15930
  } else if (entry.isFile()) {
15762
- if (!normalizedExtensions || normalizedExtensions.includes(path8.extname(entryName).toLowerCase())) {
15931
+ if (!normalizedExtensions || normalizedExtensions.includes(path9.extname(entryName).toLowerCase())) {
15763
15932
  allFiles.push(relativePath);
15764
15933
  }
15765
15934
  }
@@ -15772,7 +15941,7 @@ async function ls(args) {
15772
15941
  const dirEnd = end_index ?? allDirs.length;
15773
15942
  return {
15774
15943
  success: true,
15775
- path: basePath.split(path8.sep).join("/"),
15944
+ path: basePath.split(path9.sep).join("/"),
15776
15945
  recursive,
15777
15946
  total_files: allFiles.length,
15778
15947
  total_directories: allDirs.length,
@@ -15794,13 +15963,13 @@ async function ls(args) {
15794
15963
 
15795
15964
  // src/app/agent/tools/ReadLinesTool/ReadLinesTool.ts
15796
15965
  init_sandbox_policy();
15797
- import { promises as fs10 } from "fs";
15798
- import path9 from "path";
15966
+ import { promises as fs11 } from "fs";
15967
+ import path10 from "path";
15799
15968
  import os8 from "os";
15800
15969
  var DEFAULT_LINE_WINDOW = 2e3;
15801
15970
  function expandPath2(p) {
15802
15971
  if (p === "~" || p.startsWith("~/")) {
15803
- return path9.join(os8.homedir(), p.slice(2));
15972
+ return path10.join(os8.homedir(), p.slice(2));
15804
15973
  }
15805
15974
  return p;
15806
15975
  }
@@ -15818,7 +15987,7 @@ async function readLines(args) {
15818
15987
  }
15819
15988
  try {
15820
15989
  const resolvedPath = resolveWorkspacePath(expandPath2(filepath));
15821
- const stat = await fs10.stat(resolvedPath).catch(() => null);
15990
+ const stat = await fs11.stat(resolvedPath).catch(() => null);
15822
15991
  if (!stat || !stat.isFile()) {
15823
15992
  const workspaceRoot = resolveWorkspacePath(".");
15824
15993
  const isInsideWorkspace = resolvedPath.startsWith(workspaceRoot);
@@ -15837,7 +16006,7 @@ async function readLines(args) {
15837
16006
  if (endLine < startLine) {
15838
16007
  throw new Error("Invalid line range: end_line must be >= start_line.");
15839
16008
  }
15840
- const fileContent = await fs10.readFile(resolvedPath, "utf-8");
16009
+ const fileContent = await fs11.readFile(resolvedPath, "utf-8");
15841
16010
  const lines = fileContent.split("\n");
15842
16011
  const total_lines = lines.length;
15843
16012
  const startIndex = startLine - 1;
@@ -15868,7 +16037,7 @@ async function readLines(args) {
15868
16037
 
15869
16038
  // src/app/agent/tools/CountLinesTool/CountLinesTool.ts
15870
16039
  import { createReadStream } from "fs";
15871
- import { promises as fs11 } from "fs";
16040
+ import { promises as fs12 } from "fs";
15872
16041
  import readline from "readline";
15873
16042
  async function countLines(args) {
15874
16043
  const filepathInput = typeof args?.filepath === "string" && args.filepath.trim().length > 0 ? args.filepath : typeof args?.file_path === "string" ? args.file_path : "";
@@ -15895,7 +16064,7 @@ async function countLines(args) {
15895
16064
  };
15896
16065
  }
15897
16066
  try {
15898
- if (!(await fs11.stat(filepath)).isFile()) {
16067
+ if (!(await fs12.stat(filepath)).isFile()) {
15899
16068
  throw new Error(`File '${filepath}' not found or is not a file.`);
15900
16069
  }
15901
16070
  const fileStream = createReadStream(filepath);
@@ -16085,7 +16254,7 @@ function formatTodoResult(result) {
16085
16254
 
16086
16255
  // src/app/agent/tools/FindByNameTool/FindByNameTool.ts
16087
16256
  init_sandbox_policy();
16088
- import path11 from "path";
16257
+ import path12 from "path";
16089
16258
  import { promises as fsPromises } from "fs";
16090
16259
  import os9 from "os";
16091
16260
  var MAX_RESULTS2 = 100;
@@ -16109,7 +16278,7 @@ var DEFAULT_IGNORE2 = /* @__PURE__ */ new Set([
16109
16278
  ]);
16110
16279
  function expandTilde2(p) {
16111
16280
  if (p === "~") return os9.homedir();
16112
- if (p.startsWith("~/")) return path11.join(os9.homedir(), p.slice(2));
16281
+ if (p.startsWith("~/")) return path12.join(os9.homedir(), p.slice(2));
16113
16282
  return p;
16114
16283
  }
16115
16284
  function globToRegex(glob) {
@@ -16153,7 +16322,7 @@ function shouldIgnore2(name, extraPatterns, includeHidden) {
16153
16322
  }
16154
16323
  function matchesExtensions(filename, extensions) {
16155
16324
  if (!extensions || extensions.length === 0) return true;
16156
- const ext = path11.extname(filename).toLowerCase();
16325
+ const ext = path12.extname(filename).toLowerCase();
16157
16326
  return extensions.some((e) => {
16158
16327
  const norm = e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`;
16159
16328
  return ext === norm;
@@ -16171,8 +16340,8 @@ async function searchDirectory(dir, pattern, baseDir, options, results) {
16171
16340
  if (results.length >= MAX_RESULTS2) break;
16172
16341
  const { name } = entry;
16173
16342
  if (shouldIgnore2(name, options.excludePatterns, options.includeHidden)) continue;
16174
- const fullPath = path11.join(dir, name);
16175
- const relativePath = path11.relative(baseDir, fullPath).split(path11.sep).join("/");
16343
+ const fullPath = path12.join(dir, name);
16344
+ const relativePath = path12.relative(baseDir, fullPath).split(path12.sep).join("/");
16176
16345
  if (entry.isDirectory()) {
16177
16346
  if (pattern.test(name)) {
16178
16347
  results.push({ path: relativePath, type: "directory" });
@@ -16254,7 +16423,7 @@ async function findByName(args) {
16254
16423
  }
16255
16424
 
16256
16425
  // src/app/agent/tools/GrepSearchTool/GrepSearchTool.ts
16257
- import path12 from "path";
16426
+ import path13 from "path";
16258
16427
  import { promises as fsPromises2 } from "fs";
16259
16428
  import os10 from "os";
16260
16429
  var MAX_RESULTS3 = 200;
@@ -16344,12 +16513,12 @@ var TEXT_BASENAMES = /* @__PURE__ */ new Set([
16344
16513
  ]);
16345
16514
  function expandTilde3(p) {
16346
16515
  if (p === "~") return os10.homedir();
16347
- if (p.startsWith("~/")) return path12.join(os10.homedir(), p.slice(2));
16516
+ if (p.startsWith("~/")) return path13.join(os10.homedir(), p.slice(2));
16348
16517
  return p;
16349
16518
  }
16350
16519
  function isTextFile(filepath) {
16351
- const ext = path12.extname(filepath).toLowerCase();
16352
- const base = path12.basename(filepath);
16520
+ const ext = path13.extname(filepath).toLowerCase();
16521
+ const base = path13.basename(filepath);
16353
16522
  return TEXT_EXTENSIONS.has(ext) || TEXT_BASENAMES.has(base) || base.startsWith(".") && !ext;
16354
16523
  }
16355
16524
  function shouldIgnore3(name) {
@@ -16386,7 +16555,7 @@ async function searchFile(filepath, baseDir, pattern, contextLines, maxMatchesPe
16386
16555
  if (stat.size > MAX_FILE_SIZE) return null;
16387
16556
  const content = await fsPromises2.readFile(filepath, "utf-8");
16388
16557
  const lines = content.split("\n");
16389
- const relativePath = path12.relative(baseDir, filepath).split(path12.sep).join("/");
16558
+ const relativePath = path13.relative(baseDir, filepath).split(path13.sep).join("/");
16390
16559
  const matches = [];
16391
16560
  for (let i = 0; i < lines.length && matches.length < maxMatchesPerFile; i++) {
16392
16561
  const line = lines[i];
@@ -16420,7 +16589,7 @@ async function searchDirectory2(dir, baseDir, pattern, includePatterns, contextL
16420
16589
  for (const entry of entries) {
16421
16590
  if (stats.totalMatches >= maxResults) break;
16422
16591
  if (shouldIgnore3(entry.name)) continue;
16423
- const fullPath = path12.join(dir, entry.name);
16592
+ const fullPath = path13.join(dir, entry.name);
16424
16593
  if (entry.isDirectory()) {
16425
16594
  await searchDirectory2(fullPath, baseDir, pattern, includePatterns, contextLines, maxResults, maxMatchesPerFile, results, stats);
16426
16595
  } else if (entry.isFile()) {
@@ -16461,7 +16630,7 @@ async function grepSearch(args) {
16461
16630
  });
16462
16631
  if (!query || typeof query !== "string") return empty("query is required");
16463
16632
  if (!searchPath) return empty("path is required");
16464
- const resolvedPath = path12.resolve(expandTilde3(searchPath));
16633
+ const resolvedPath = path13.resolve(expandTilde3(searchPath));
16465
16634
  const stat = await fsPromises2.stat(resolvedPath).catch(() => null);
16466
16635
  if (!stat) return empty(`Path not found: ${resolvedPath}`);
16467
16636
  let pattern;
@@ -16477,7 +16646,7 @@ async function grepSearch(args) {
16477
16646
  await searchDirectory2(resolvedPath, resolvedPath, pattern, include_patterns, context_lines, max_results, max_matches_per_file, results, stats);
16478
16647
  } else if (stat.isFile()) {
16479
16648
  stats.filesSearched = 1;
16480
- const fileResult = await searchFile(resolvedPath, path12.dirname(resolvedPath), pattern, context_lines, max_matches_per_file);
16649
+ const fileResult = await searchFile(resolvedPath, path13.dirname(resolvedPath), pattern, context_lines, max_matches_per_file);
16481
16650
  if (fileResult) {
16482
16651
  results.push(fileResult);
16483
16652
  stats.totalMatches = fileResult.match_count;
@@ -16500,7 +16669,7 @@ async function grepSearch(args) {
16500
16669
  }
16501
16670
 
16502
16671
  // src/app/agent/tools/ViewFileOutlineTool/ViewFileOutlineTool.ts
16503
- import path13 from "path";
16672
+ import path14 from "path";
16504
16673
  import { promises as fsPromises3 } from "fs";
16505
16674
  var LANGUAGE_MAP = {
16506
16675
  ".ts": "typescript",
@@ -16614,7 +16783,7 @@ var PATTERNS = {
16614
16783
  ]
16615
16784
  };
16616
16785
  function detectLanguage(filepath) {
16617
- const ext = path13.extname(filepath).toLowerCase();
16786
+ const ext = path14.extname(filepath).toLowerCase();
16618
16787
  return LANGUAGE_MAP[ext] || "unknown";
16619
16788
  }
16620
16789
  function determineItemType(line, language) {
@@ -16710,7 +16879,7 @@ async function viewFileOutline(args) {
16710
16879
  error: "file_path is required and must be a string"
16711
16880
  };
16712
16881
  }
16713
- const resolvedPath = path13.resolve(file_path);
16882
+ const resolvedPath = path14.resolve(file_path);
16714
16883
  let content;
16715
16884
  try {
16716
16885
  content = await fsPromises3.readFile(resolvedPath, "utf-8");
@@ -16756,21 +16925,21 @@ init_CommandStatusTool();
16756
16925
  // src/app/agent/tools/TaskBoundaryTool/TaskBoundaryTool.ts
16757
16926
  init_sandbox_policy();
16758
16927
  init_task_store();
16759
- import path14 from "path";
16760
- import { promises as fs13 } from "fs";
16928
+ import path15 from "path";
16929
+ import { promises as fs14 } from "fs";
16761
16930
  var artifactsDir = null;
16762
16931
  async function getArtifactsDir() {
16763
16932
  if (artifactsDir) return artifactsDir;
16764
16933
  const policy = getSandboxPolicy();
16765
- const baseDir = path14.join(policy.workspaceRoot, ".bluma", "artifacts");
16934
+ const baseDir = path15.join(policy.workspaceRoot, ".bluma", "artifacts");
16766
16935
  const sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
16767
- artifactsDir = path14.join(baseDir, sessionId);
16768
- await fs13.mkdir(artifactsDir, { recursive: true });
16936
+ artifactsDir = path15.join(baseDir, sessionId);
16937
+ await fs14.mkdir(artifactsDir, { recursive: true });
16769
16938
  return artifactsDir;
16770
16939
  }
16771
16940
  async function updateTaskFile(task) {
16772
16941
  const dir = await getArtifactsDir();
16773
- const taskFile = path14.join(dir, "task.md");
16942
+ const taskFile = path15.join(dir, "task.md");
16774
16943
  const content = `# ${task.taskName}
16775
16944
 
16776
16945
  **Mode:** ${task.mode}
@@ -16782,7 +16951,7 @@ async function updateTaskFile(task) {
16782
16951
  ## Summary
16783
16952
  ${task.summary}
16784
16953
  `;
16785
- await fs13.writeFile(taskFile, content, "utf-8");
16954
+ await fs14.writeFile(taskFile, content, "utf-8");
16786
16955
  }
16787
16956
  async function taskBoundary(args) {
16788
16957
  try {
@@ -16898,8 +17067,8 @@ async function readArtifact(args) {
16898
17067
  return { success: false, error: "filename is required" };
16899
17068
  }
16900
17069
  const dir = await getArtifactsDir();
16901
- const filepath = path14.join(dir, filename);
16902
- const content = await fs13.readFile(filepath, "utf-8");
17070
+ const filepath = path15.join(dir, filename);
17071
+ const content = await fs14.readFile(filepath, "utf-8");
16903
17072
  return {
16904
17073
  success: true,
16905
17074
  content
@@ -17443,8 +17612,8 @@ ${skill.content}`;
17443
17612
  }
17444
17613
 
17445
17614
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryTool.ts
17446
- import * as fs14 from "fs";
17447
- import * as path15 from "path";
17615
+ import * as fs15 from "fs";
17616
+ import * as path16 from "path";
17448
17617
  import os12 from "os";
17449
17618
  var PROMPT_DEFAULT_MAX_TOTAL = 1e4;
17450
17619
  var PROMPT_DEFAULT_MAX_NOTES = 25;
@@ -17453,14 +17622,14 @@ function readCodingMemoryForPrompt(options) {
17453
17622
  const maxTotal = options?.maxTotalChars ?? PROMPT_DEFAULT_MAX_TOTAL;
17454
17623
  const maxNotes = options?.maxNotes ?? PROMPT_DEFAULT_MAX_NOTES;
17455
17624
  const preview = options?.previewCharsPerNote ?? PROMPT_DEFAULT_PREVIEW;
17456
- const globalPath = path15.join(os12.homedir(), ".bluma", "coding_memory.json");
17457
- const legacyPath = path15.join(process.cwd(), ".bluma", "coding_memory.json");
17625
+ const globalPath = path16.join(os12.homedir(), ".bluma", "coding_memory.json");
17626
+ const legacyPath = path16.join(process.cwd(), ".bluma", "coding_memory.json");
17458
17627
  let raw = null;
17459
17628
  try {
17460
- if (fs14.existsSync(globalPath)) {
17461
- raw = fs14.readFileSync(globalPath, "utf-8");
17462
- } else if (path15.resolve(globalPath) !== path15.resolve(legacyPath) && fs14.existsSync(legacyPath)) {
17463
- raw = fs14.readFileSync(legacyPath, "utf-8");
17629
+ if (fs15.existsSync(globalPath)) {
17630
+ raw = fs15.readFileSync(globalPath, "utf-8");
17631
+ } else if (path16.resolve(globalPath) !== path16.resolve(legacyPath) && fs15.existsSync(legacyPath)) {
17632
+ raw = fs15.readFileSync(legacyPath, "utf-8");
17464
17633
  }
17465
17634
  } catch {
17466
17635
  return "";
@@ -17498,10 +17667,10 @@ var memoryStore = [];
17498
17667
  var nextId = 1;
17499
17668
  var loaded = false;
17500
17669
  function getMemoryFilePath() {
17501
- return path15.join(os12.homedir(), ".bluma", "coding_memory.json");
17670
+ return path16.join(os12.homedir(), ".bluma", "coding_memory.json");
17502
17671
  }
17503
17672
  function getLegacyMemoryFilePath() {
17504
- return path15.join(process.cwd(), ".bluma", "coding_memory.json");
17673
+ return path16.join(process.cwd(), ".bluma", "coding_memory.json");
17505
17674
  }
17506
17675
  function loadMemoryFromFile() {
17507
17676
  if (loaded) return;
@@ -17511,19 +17680,19 @@ function loadMemoryFromFile() {
17511
17680
  try {
17512
17681
  const filePath = getMemoryFilePath();
17513
17682
  const legacy = getLegacyMemoryFilePath();
17514
- const legacyDistinct = path15.resolve(legacy) !== path15.resolve(filePath);
17683
+ const legacyDistinct = path16.resolve(legacy) !== path16.resolve(filePath);
17515
17684
  const readIntoStore = (p) => {
17516
- const raw = fs14.readFileSync(p, "utf-8");
17685
+ const raw = fs15.readFileSync(p, "utf-8");
17517
17686
  const parsed = JSON.parse(raw);
17518
17687
  if (Array.isArray(parsed.entries)) {
17519
17688
  memoryStore = parsed.entries;
17520
17689
  nextId = typeof parsed.nextId === "number" ? parsed.nextId : memoryStore.length + 1;
17521
17690
  }
17522
17691
  };
17523
- if (fs14.existsSync(filePath)) {
17692
+ if (fs15.existsSync(filePath)) {
17524
17693
  readIntoStore(filePath);
17525
17694
  }
17526
- if (memoryStore.length === 0 && legacyDistinct && fs14.existsSync(legacy)) {
17695
+ if (memoryStore.length === 0 && legacyDistinct && fs15.existsSync(legacy)) {
17527
17696
  readIntoStore(legacy);
17528
17697
  if (memoryStore.length > 0) {
17529
17698
  saveMemoryToFile();
@@ -17537,16 +17706,16 @@ function loadMemoryFromFile() {
17537
17706
  function saveMemoryToFile() {
17538
17707
  try {
17539
17708
  const filePath = getMemoryFilePath();
17540
- const dir = path15.dirname(filePath);
17541
- if (!fs14.existsSync(dir)) {
17542
- fs14.mkdirSync(dir, { recursive: true });
17709
+ const dir = path16.dirname(filePath);
17710
+ if (!fs15.existsSync(dir)) {
17711
+ fs15.mkdirSync(dir, { recursive: true });
17543
17712
  }
17544
17713
  const payload = {
17545
17714
  entries: memoryStore,
17546
17715
  nextId,
17547
17716
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
17548
17717
  };
17549
- fs14.writeFileSync(filePath, JSON.stringify(payload, null, 2));
17718
+ fs15.writeFileSync(filePath, JSON.stringify(payload, null, 2));
17550
17719
  } catch {
17551
17720
  }
17552
17721
  }
@@ -18048,7 +18217,7 @@ async function cronDelete(args) {
18048
18217
 
18049
18218
  // src/app/agent/tools/NotebookEditTool/NotebookEditTool.ts
18050
18219
  init_sandbox_policy();
18051
- import { promises as fs15 } from "fs";
18220
+ import { promises as fs16 } from "fs";
18052
18221
  function sourceToString(s) {
18053
18222
  if (s == null) return "";
18054
18223
  return Array.isArray(s) ? s.join("") : s;
@@ -18060,7 +18229,7 @@ function stringToSource(s) {
18060
18229
  async function applyNotebookOperation(filepath, op) {
18061
18230
  try {
18062
18231
  const resolved = resolveWorkspacePath(filepath);
18063
- const raw = await fs15.readFile(resolved, "utf-8");
18232
+ const raw = await fs16.readFile(resolved, "utf-8");
18064
18233
  let doc;
18065
18234
  try {
18066
18235
  doc = JSON.parse(raw);
@@ -18101,7 +18270,7 @@ async function applyNotebookOperation(filepath, op) {
18101
18270
  return JSON.stringify({ success: false, error: `cell_index out of range: ${i}` });
18102
18271
  }
18103
18272
  doc.cells[i].source = stringToSource(String(op.source ?? ""));
18104
- await fs15.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
18273
+ await fs16.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
18105
18274
  return JSON.stringify({ success: true, message: `Updated cell ${i}` });
18106
18275
  }
18107
18276
  if (op.operation === "append_markdown_cell") {
@@ -18110,7 +18279,7 @@ async function applyNotebookOperation(filepath, op) {
18110
18279
  source: stringToSource(String(op.source ?? "")),
18111
18280
  metadata: {}
18112
18281
  });
18113
- await fs15.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
18282
+ await fs16.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
18114
18283
  return JSON.stringify({ success: true, message: `Appended markdown cell at index ${doc.cells.length - 1}` });
18115
18284
  }
18116
18285
  if (op.operation === "delete_cell") {
@@ -18119,7 +18288,7 @@ async function applyNotebookOperation(filepath, op) {
18119
18288
  return JSON.stringify({ success: false, error: `cell_index out of range: ${i}` });
18120
18289
  }
18121
18290
  doc.cells.splice(i, 1);
18122
- await fs15.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
18291
+ await fs16.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
18123
18292
  return JSON.stringify({ success: true, message: `Deleted cell ${i}` });
18124
18293
  }
18125
18294
  return JSON.stringify({ success: false, error: "unknown operation" });
@@ -18158,8 +18327,8 @@ async function notebook_edit(args) {
18158
18327
  // src/app/agent/tools/LspQueryTool/LspQueryTool.ts
18159
18328
  init_sandbox_policy();
18160
18329
  import { spawn as spawn3 } from "child_process";
18161
- import * as fs16 from "fs";
18162
- import * as path16 from "path";
18330
+ import * as fs17 from "fs";
18331
+ import * as path17 from "path";
18163
18332
  import { pathToFileURL } from "url";
18164
18333
  var RpcBuffer = class {
18165
18334
  buf = Buffer.alloc(0);
@@ -18209,12 +18378,12 @@ async function lsp_query(args) {
18209
18378
  } catch (e) {
18210
18379
  return JSON.stringify({ success: false, error: e.message || String(e) });
18211
18380
  }
18212
- if (!fs16.existsSync(resolved) || !fs16.statSync(resolved).isFile()) {
18381
+ if (!fs17.existsSync(resolved) || !fs17.statSync(resolved).isFile()) {
18213
18382
  return JSON.stringify({ success: false, error: "file not found" });
18214
18383
  }
18215
- const content = fs16.readFileSync(resolved, "utf-8");
18384
+ const content = fs17.readFileSync(resolved, "utf-8");
18216
18385
  const uri = pathToFileURL(resolved).href;
18217
- const root = path16.dirname(resolved);
18386
+ const root = path17.dirname(resolved);
18218
18387
  const line0 = Math.max(0, Math.floor(Number(args.line) || 1) - 1);
18219
18388
  const character = Math.max(0, Math.floor(Number(args.character ?? 0)));
18220
18389
  const operation = args.operation === "references" ? "references" : "definition";
@@ -18334,8 +18503,8 @@ async function lsp_query(args) {
18334
18503
 
18335
18504
  // src/app/agent/tools/FileWriteTool/FileWriteTool.ts
18336
18505
  init_sandbox_policy();
18337
- import { promises as fs17 } from "fs";
18338
- import path17 from "path";
18506
+ import { promises as fs18 } from "fs";
18507
+ import path18 from "path";
18339
18508
  async function fileWrite(args) {
18340
18509
  const {
18341
18510
  filepath,
@@ -18353,10 +18522,10 @@ async function fileWrite(args) {
18353
18522
  return { success: false, error: "content is required" };
18354
18523
  }
18355
18524
  const resolvedPath = resolveWorkspacePath(targetPath);
18356
- const dir = path17.dirname(resolvedPath);
18525
+ const dir = path18.dirname(resolvedPath);
18357
18526
  let existed = false;
18358
18527
  try {
18359
- const st = await fs17.stat(resolvedPath);
18528
+ const st = await fs18.stat(resolvedPath);
18360
18529
  existed = st.isFile();
18361
18530
  } catch {
18362
18531
  existed = false;
@@ -18370,15 +18539,15 @@ async function fileWrite(args) {
18370
18539
  let previousContent = "";
18371
18540
  if (existed) {
18372
18541
  try {
18373
- previousContent = await fs17.readFile(resolvedPath, "utf-8");
18542
+ previousContent = await fs18.readFile(resolvedPath, "utf-8");
18374
18543
  } catch {
18375
18544
  previousContent = "";
18376
18545
  }
18377
18546
  }
18378
18547
  if (create_directories) {
18379
- await fs17.mkdir(dir, { recursive: true });
18548
+ await fs18.mkdir(dir, { recursive: true });
18380
18549
  }
18381
- await fs17.writeFile(resolvedPath, String(content), "utf-8");
18550
+ await fs18.writeFile(resolvedPath, String(content), "utf-8");
18382
18551
  const bytes = Buffer.byteLength(String(content), "utf-8");
18383
18552
  return {
18384
18553
  success: true,
@@ -18687,9 +18856,9 @@ async function signalMailbox(args) {
18687
18856
  }
18688
18857
 
18689
18858
  // src/app/agent/tools/ReplTool/ReplTool.ts
18690
- import fs22 from "fs";
18859
+ import fs23 from "fs";
18691
18860
  import os17 from "os";
18692
- import path22 from "path";
18861
+ import path23 from "path";
18693
18862
  import { exec } from "child_process";
18694
18863
  import { v4 as uuidv47 } from "uuid";
18695
18864
  import { promisify as promisify2 } from "util";
@@ -18701,20 +18870,20 @@ async function repl(params) {
18701
18870
  let tempFile;
18702
18871
  switch (language) {
18703
18872
  case "python": {
18704
- tempFile = path22.join(os17.tmpdir(), `bluma_repl_${uuidv47()}.py`);
18705
- fs22.writeFileSync(tempFile, code, "utf-8");
18873
+ tempFile = path23.join(os17.tmpdir(), `bluma_repl_${uuidv47()}.py`);
18874
+ fs23.writeFileSync(tempFile, code, "utf-8");
18706
18875
  command = `python3 "${tempFile}"`;
18707
18876
  break;
18708
18877
  }
18709
18878
  case "node": {
18710
- tempFile = path22.join(os17.tmpdir(), `bluma_repl_${uuidv47()}.mjs`);
18711
- fs22.writeFileSync(tempFile, code, "utf-8");
18879
+ tempFile = path23.join(os17.tmpdir(), `bluma_repl_${uuidv47()}.mjs`);
18880
+ fs23.writeFileSync(tempFile, code, "utf-8");
18712
18881
  command = `node "${tempFile}"`;
18713
18882
  break;
18714
18883
  }
18715
18884
  case "bash": {
18716
- tempFile = path22.join(os17.tmpdir(), `bluma_repl_${uuidv47()}.sh`);
18717
- fs22.writeFileSync(tempFile, `#!/bin/bash
18885
+ tempFile = path23.join(os17.tmpdir(), `bluma_repl_${uuidv47()}.sh`);
18886
+ fs23.writeFileSync(tempFile, `#!/bin/bash
18718
18887
  set -e
18719
18888
  ${code}`, "utf-8");
18720
18889
  command = `bash "${tempFile}"`;
@@ -18728,7 +18897,7 @@ ${code}`, "utf-8");
18728
18897
  // 5MB
18729
18898
  });
18730
18899
  try {
18731
- fs22.unlinkSync(tempFile);
18900
+ fs23.unlinkSync(tempFile);
18732
18901
  } catch {
18733
18902
  }
18734
18903
  return {
@@ -18740,7 +18909,7 @@ ${code}`, "utf-8");
18740
18909
  };
18741
18910
  } catch (error) {
18742
18911
  try {
18743
- fs22.unlinkSync(tempFile);
18912
+ fs23.unlinkSync(tempFile);
18744
18913
  } catch {
18745
18914
  }
18746
18915
  const exitCode = error.code === "ERR_CHILD_PROCESS_TIMEOUT" ? 124 : error.status ?? 1;
@@ -19088,8 +19257,8 @@ ${preview}${toRemove.length > 5 ? `
19088
19257
 
19089
19258
  // src/app/agent/tools/BriefTool/BriefTool.ts
19090
19259
  init_sandbox_policy();
19091
- import * as fs23 from "fs";
19092
- import * as path23 from "path";
19260
+ import * as fs24 from "fs";
19261
+ import * as path24 from "path";
19093
19262
  async function brief(args) {
19094
19263
  const sentAt = (/* @__PURE__ */ new Date()).toISOString();
19095
19264
  if (!args.message || !args.message.trim()) {
@@ -19108,9 +19277,9 @@ async function brief(args) {
19108
19277
  const resolved = [];
19109
19278
  for (const filePath of args.attachments) {
19110
19279
  try {
19111
- const absolutePath = path23.isAbsolute(filePath) ? filePath : path23.join(resolveWorkspacePath("."), filePath);
19112
- if (fs23.existsSync(absolutePath)) {
19113
- const stat = fs23.statSync(absolutePath);
19280
+ const absolutePath = path24.isAbsolute(filePath) ? filePath : path24.join(resolveWorkspacePath("."), filePath);
19281
+ if (fs24.existsSync(absolutePath)) {
19282
+ const stat = fs24.statSync(absolutePath);
19114
19283
  resolved.push({
19115
19284
  path: absolutePath,
19116
19285
  size: stat.size,
@@ -19149,14 +19318,14 @@ async function brief(args) {
19149
19318
  }
19150
19319
 
19151
19320
  // src/app/agent/tools/DreamEngineTool/DreamEngineTool.ts
19152
- import * as fs24 from "fs";
19153
- import * as path24 from "path";
19321
+ import * as fs25 from "fs";
19322
+ import * as path25 from "path";
19154
19323
  import os18 from "os";
19155
19324
  function memoryPath() {
19156
- return path24.join(process.env.HOME || os18.homedir(), ".bluma", "coding_memory.json");
19325
+ return path25.join(process.env.HOME || os18.homedir(), ".bluma", "coding_memory.json");
19157
19326
  }
19158
19327
  function sessionsDir() {
19159
- return path24.join(process.env.HOME || os18.homedir(), ".bluma", "sessions");
19328
+ return path25.join(process.env.HOME || os18.homedir(), ".bluma", "sessions");
19160
19329
  }
19161
19330
  function normalizeNote(note) {
19162
19331
  return note.trim().toLowerCase().replace(/\s+/g, " ");
@@ -19181,9 +19350,9 @@ async function dream(args = {}) {
19181
19350
  const mergeSimilar = args.mergeSimilar !== false;
19182
19351
  const memPath = memoryPath();
19183
19352
  let memData = null;
19184
- if (fs24.existsSync(memPath)) {
19353
+ if (fs25.existsSync(memPath)) {
19185
19354
  try {
19186
- memData = JSON.parse(fs24.readFileSync(memPath, "utf-8"));
19355
+ memData = JSON.parse(fs25.readFileSync(memPath, "utf-8"));
19187
19356
  } catch {
19188
19357
  }
19189
19358
  }
@@ -19260,12 +19429,12 @@ async function dream(args = {}) {
19260
19429
  pruned += removed.length;
19261
19430
  }
19262
19431
  const sessDir = sessionsDir();
19263
- if (fs24.existsSync(sessDir)) {
19432
+ if (fs25.existsSync(sessDir)) {
19264
19433
  try {
19265
- const sessionFiles = fs24.readdirSync(sessDir).filter((f) => f.endsWith(".json")).slice(-5);
19434
+ const sessionFiles = fs25.readdirSync(sessDir).filter((f) => f.endsWith(".json")).slice(-5);
19266
19435
  for (const sf of sessionFiles) {
19267
19436
  try {
19268
- const sessionData = JSON.parse(fs24.readFileSync(path24.join(sessDir, sf), "utf-8"));
19437
+ const sessionData = JSON.parse(fs25.readFileSync(path25.join(sessDir, sf), "utf-8"));
19269
19438
  if (sessionData?.summary) {
19270
19439
  consolidatedMemories.push(`Session ${sf}: ${sessionData.summary}`);
19271
19440
  }
@@ -19280,7 +19449,7 @@ async function dream(args = {}) {
19280
19449
  memData.nextId = entries.reduce((max, e) => Math.max(max, e.id || 0), 0) + 1;
19281
19450
  memData.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
19282
19451
  try {
19283
- fs24.writeFileSync(memPath, JSON.stringify(memData, null, 2), "utf-8");
19452
+ fs25.writeFileSync(memPath, JSON.stringify(memData, null, 2), "utf-8");
19284
19453
  } catch (e) {
19285
19454
  return {
19286
19455
  success: false,
@@ -19473,8 +19642,8 @@ async function context_collapse(args = {}) {
19473
19642
 
19474
19643
  // src/app/agent/tools/CreateNextAppTool/CreateNextAppTool.ts
19475
19644
  init_sandbox_policy();
19476
- import { promises as fs25 } from "fs";
19477
- import path25 from "path";
19645
+ import { promises as fs26 } from "fs";
19646
+ import path26 from "path";
19478
19647
 
19479
19648
  // src/app/agent/tools/ShellCommandTool/ShellCommandTool.ts
19480
19649
  init_sandbox_policy();
@@ -20248,10 +20417,10 @@ export function cn(...inputs: ClassValue[]) {
20248
20417
  `
20249
20418
  };
20250
20419
  async function createFile(filePath, content, projectName) {
20251
- const dir = path25.dirname(filePath);
20252
- await fs25.mkdir(dir, { recursive: true });
20420
+ const dir = path26.dirname(filePath);
20421
+ await fs26.mkdir(dir, { recursive: true });
20253
20422
  const replacedContent = content.replace(/{{NAME}}/g, projectName);
20254
- await fs25.writeFile(filePath, replacedContent, "utf-8");
20423
+ await fs26.writeFile(filePath, replacedContent, "utf-8");
20255
20424
  }
20256
20425
  async function createNextApp(args) {
20257
20426
  const {
@@ -20270,26 +20439,26 @@ async function createNextApp(args) {
20270
20439
  };
20271
20440
  }
20272
20441
  const baseDir = directory ? resolveWorkspacePath(directory) : resolveWorkspacePath(".");
20273
- const projectPath = path25.join(baseDir, name);
20442
+ const projectPath = path26.join(baseDir, name);
20274
20443
  try {
20275
- await fs25.access(projectPath);
20444
+ await fs26.access(projectPath);
20276
20445
  return {
20277
20446
  success: false,
20278
20447
  error: `Directory already exists: ${projectPath}. Remove it first or choose a different name.`
20279
20448
  };
20280
20449
  } catch {
20281
20450
  }
20282
- await fs25.mkdir(projectPath, { recursive: true });
20451
+ await fs26.mkdir(projectPath, { recursive: true });
20283
20452
  const filesToCreate = template === "minimal" ? MINIMAL_FILES : FULL_FILES;
20284
20453
  const filesCreated = [];
20285
20454
  for (const [relativePath, content] of Object.entries(filesToCreate)) {
20286
20455
  if (relativePath === "package.json.full") continue;
20287
- const fullPath = path25.join(projectPath, relativePath);
20456
+ const fullPath = path26.join(projectPath, relativePath);
20288
20457
  await createFile(fullPath, content, name);
20289
20458
  filesCreated.push(relativePath);
20290
20459
  }
20291
20460
  if (template === "full") {
20292
- const packageJsonPath = path25.join(projectPath, "package.json");
20461
+ const packageJsonPath = path26.join(projectPath, "package.json");
20293
20462
  const fullPackageJson = FULL_FILES["package.json.full"];
20294
20463
  if (fullPackageJson) {
20295
20464
  await createFile(packageJsonPath, fullPackageJson, name);
@@ -20331,8 +20500,8 @@ async function createNextApp(args) {
20331
20500
 
20332
20501
  // src/app/agent/tools/DeployAppTool/DeployAppTool.ts
20333
20502
  init_sandbox_policy();
20334
- import { promises as fs26 } from "fs";
20335
- import path26 from "path";
20503
+ import { promises as fs27 } from "fs";
20504
+ import path27 from "path";
20336
20505
  var EXCLUDE_PATTERNS = [
20337
20506
  "node_modules",
20338
20507
  ".next",
@@ -20482,8 +20651,8 @@ function buildFactorAiManifest(appContext, deployResult, appName) {
20482
20651
  }
20483
20652
  async function writeFactorAiManifest(projectDir, appContext, deployResult, appName) {
20484
20653
  const manifest = buildFactorAiManifest(appContext, deployResult, appName);
20485
- const manifestPath = path26.join(projectDir, "factorai.sh.json");
20486
- await fs26.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
20654
+ const manifestPath = path27.join(projectDir, "factorai.sh.json");
20655
+ await fs27.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
20487
20656
  `, "utf-8");
20488
20657
  return manifest;
20489
20658
  }
@@ -20503,23 +20672,23 @@ async function deployApp(args) {
20503
20672
  }
20504
20673
  const resolvedProjectDir = resolveWorkspacePath(projectDir);
20505
20674
  try {
20506
- await fs26.access(resolvedProjectDir);
20675
+ await fs27.access(resolvedProjectDir);
20507
20676
  } catch {
20508
20677
  return {
20509
20678
  success: false,
20510
20679
  error: `Project directory not found: ${resolvedProjectDir}`
20511
20680
  };
20512
20681
  }
20513
- const packageJsonPath = path26.join(resolvedProjectDir, "package.json");
20682
+ const packageJsonPath = path27.join(resolvedProjectDir, "package.json");
20514
20683
  try {
20515
- await fs26.access(packageJsonPath);
20684
+ await fs27.access(packageJsonPath);
20516
20685
  } catch {
20517
20686
  return {
20518
20687
  success: false,
20519
20688
  error: "Not a Next.js project: package.json not found"
20520
20689
  };
20521
20690
  }
20522
- const packageJsonContent = await fs26.readFile(packageJsonPath, "utf-8");
20691
+ const packageJsonContent = await fs27.readFile(packageJsonPath, "utf-8");
20523
20692
  const packageJson = JSON.parse(packageJsonContent);
20524
20693
  const hasNext = packageJson.dependencies?.next || packageJson.devDependencies?.next;
20525
20694
  if (!hasNext) {
@@ -20528,18 +20697,18 @@ async function deployApp(args) {
20528
20697
  error: "Not a Next.js project: next not found in dependencies"
20529
20698
  };
20530
20699
  }
20531
- const appName = name || packageJson.name || path26.basename(resolvedProjectDir);
20532
- const tempDir = path26.join(resolvedProjectDir, ".tmp");
20533
- await fs26.mkdir(tempDir, { recursive: true });
20534
- const zipPath = path26.join(tempDir, `${appName}-${Date.now()}.zip`);
20700
+ const appName = name || packageJson.name || path27.basename(resolvedProjectDir);
20701
+ const tempDir = path27.join(resolvedProjectDir, ".tmp");
20702
+ await fs27.mkdir(tempDir, { recursive: true });
20703
+ const zipPath = path27.join(tempDir, `${appName}-${Date.now()}.zip`);
20535
20704
  console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
20536
20705
  await createProjectZip(resolvedProjectDir, zipPath);
20537
- const zipStats = await fs26.stat(zipPath);
20706
+ const zipStats = await fs27.stat(zipPath);
20538
20707
  const zipSizeMB = zipStats.size / 1024 / 1024;
20539
20708
  if (zipSizeMB > 50) {
20540
- await fs26.unlink(zipPath).catch(() => {
20709
+ await fs27.unlink(zipPath).catch(() => {
20541
20710
  });
20542
- await fs26.rmdir(tempDir).catch(() => {
20711
+ await fs27.rmdir(tempDir).catch(() => {
20543
20712
  });
20544
20713
  return {
20545
20714
  success: false,
@@ -20550,8 +20719,8 @@ async function deployApp(args) {
20550
20719
  console.log(`[deploy-app] Uploading to ${severinoUrl}...`);
20551
20720
  const deployResult = await uploadToSeverino(zipPath, severinoUrl, appName, apiKey, appId);
20552
20721
  try {
20553
- await fs26.unlink(zipPath);
20554
- await fs26.rmdir(tempDir);
20722
+ await fs27.unlink(zipPath);
20723
+ await fs27.rmdir(tempDir);
20555
20724
  } catch (e) {
20556
20725
  console.warn("[deploy-app] Cleanup warning:", e);
20557
20726
  }
@@ -20564,7 +20733,7 @@ async function deployApp(args) {
20564
20733
  appName
20565
20734
  );
20566
20735
  deployResult.factoraiManifest = manifest;
20567
- deployResult.factoraiManifestPath = path26.join(resolvedProjectDir, "factorai.sh.json");
20736
+ deployResult.factoraiManifestPath = path27.join(resolvedProjectDir, "factorai.sh.json");
20568
20737
  }
20569
20738
  console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
20570
20739
  }
@@ -20579,8 +20748,8 @@ async function deployApp(args) {
20579
20748
  }
20580
20749
 
20581
20750
  // src/app/agent/runtime/factorai_context.ts
20582
- import fs27 from "fs";
20583
- import path27 from "path";
20751
+ import fs28 from "fs";
20752
+ import path28 from "path";
20584
20753
  function normalizeContext(raw) {
20585
20754
  if (!raw || typeof raw.appId !== "string" || !raw.appId.trim()) {
20586
20755
  return null;
@@ -20601,10 +20770,10 @@ function normalizeContext(raw) {
20601
20770
  }
20602
20771
  function readJsonFile(filePath) {
20603
20772
  try {
20604
- if (!fs27.existsSync(filePath)) {
20773
+ if (!fs28.existsSync(filePath)) {
20605
20774
  return null;
20606
20775
  }
20607
- const parsed = JSON.parse(fs27.readFileSync(filePath, "utf8"));
20776
+ const parsed = JSON.parse(fs28.readFileSync(filePath, "utf8"));
20608
20777
  if (parsed && typeof parsed === "object") {
20609
20778
  return parsed.appContext && typeof parsed.appContext === "object" ? parsed.appContext : parsed;
20610
20779
  }
@@ -20614,19 +20783,19 @@ function readJsonFile(filePath) {
20614
20783
  return null;
20615
20784
  }
20616
20785
  function readFactorAiWorkspaceManifest(projectDir = process.cwd()) {
20617
- const manifestPath = path27.join(projectDir, "factorai.sh.json");
20786
+ const manifestPath = path28.join(projectDir, "factorai.sh.json");
20618
20787
  try {
20619
- if (!fs27.existsSync(manifestPath)) {
20788
+ if (!fs28.existsSync(manifestPath)) {
20620
20789
  return null;
20621
20790
  }
20622
- const parsed = JSON.parse(fs27.readFileSync(manifestPath, "utf8"));
20791
+ const parsed = JSON.parse(fs28.readFileSync(manifestPath, "utf8"));
20623
20792
  return parsed && typeof parsed === "object" ? parsed : null;
20624
20793
  } catch {
20625
20794
  return null;
20626
20795
  }
20627
20796
  }
20628
20797
  function readContextFromWorkspace() {
20629
- const candidate = path27.join(process.cwd(), "factorai.sh.json");
20798
+ const candidate = path28.join(process.cwd(), "factorai.sh.json");
20630
20799
  const parsed = readJsonFile(candidate);
20631
20800
  if (!parsed) {
20632
20801
  return null;
@@ -20713,8 +20882,8 @@ function buildFactorAiWorkspaceManifest(input) {
20713
20882
  async function writeFactorAiWorkspaceManifest(input) {
20714
20883
  const projectDir = input.projectDir || process.cwd();
20715
20884
  const manifest = buildFactorAiWorkspaceManifest({ ...input, projectDir });
20716
- const manifestPath = path27.join(projectDir, "factorai.sh.json");
20717
- fs27.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
20885
+ const manifestPath = path28.join(projectDir, "factorai.sh.json");
20886
+ fs28.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
20718
20887
  `, "utf8");
20719
20888
  return { manifestPath, manifest };
20720
20889
  }
@@ -20970,7 +21139,7 @@ var NATIVE_TOOL_ENTRIES = [
20970
21139
  autoApproveInLocal: true,
20971
21140
  autoApproveInSandbox: true,
20972
21141
  sandboxOnly: true,
20973
- description: 'Primary channel \u2014 use generously. message_type "info": call frequently (good UX expects multiple per turn; does not stop the turn). message_type "result": once to end the turn.'
21142
+ description: 'Primary channel \u2014 use generously. message_type "info": progress only (does not stop the turn). message_type "result": ends the turn; in sandbox, file deliverables MUST use attachments[] with paths under .bluma/artifacts/ (content alone does not deliver files).'
20974
21143
  },
20975
21144
  implementation: message
20976
21145
  },
@@ -21596,9 +21765,18 @@ function getNativeToolMetadata(toolName) {
21596
21765
  function getNativeToolImplementation(toolName) {
21597
21766
  return TOOL_IMPLEMENTATION_MAP.get(toolName);
21598
21767
  }
21768
+ function isFactorAiToolName(toolName) {
21769
+ return toolName.startsWith("factorai.sh.");
21770
+ }
21599
21771
  function shouldExposeToolEntry(entry, policy = getSandboxPolicy()) {
21600
- if (entry.metadata.sandboxOnly && !isFactorAiSandboxEnabled()) {
21601
- return false;
21772
+ if (entry.metadata.sandboxOnly) {
21773
+ if (isFactorAiToolName(entry.metadata.name)) {
21774
+ if (!isFactorAiSandboxEnabled()) {
21775
+ return false;
21776
+ }
21777
+ } else if (!policy.isSandbox) {
21778
+ return false;
21779
+ }
21602
21780
  }
21603
21781
  if (entry.metadata.hiddenInSandbox && policy.isSandbox) {
21604
21782
  return false;
@@ -21612,8 +21790,41 @@ function getAllNativeToolMetadata() {
21612
21790
  const policy = getSandboxPolicy();
21613
21791
  return NATIVE_TOOL_ENTRIES.filter((entry) => shouldExposeToolEntry(entry, policy)).map((entry) => entry.metadata);
21614
21792
  }
21793
+ var SANDBOX_COMMUNICATION_TOOL_DEFINITIONS = [
21794
+ {
21795
+ type: "function",
21796
+ function: {
21797
+ name: "message",
21798
+ description: 'Deliver user-visible output. message_type "info": progress (does not end turn). message_type "result": ends turn; file deliverables MUST use attachments[] with paths under .bluma/artifacts/.',
21799
+ parameters: {
21800
+ type: "object",
21801
+ properties: {
21802
+ content: { type: "string", description: "Markdown body shown to the user." },
21803
+ message_type: {
21804
+ type: "string",
21805
+ enum: ["info", "result"],
21806
+ description: "info = progress; result = final delivery (ends turn in sandbox)."
21807
+ },
21808
+ attachments: {
21809
+ type: "array",
21810
+ items: { type: "string" },
21811
+ description: "Workspace file paths under .bluma/artifacts/ (required for file deliverables on result)."
21812
+ }
21813
+ },
21814
+ required: ["content", "message_type"],
21815
+ additionalProperties: false
21816
+ }
21817
+ }
21818
+ }
21819
+ ];
21820
+ function getSandboxCommunicationToolDefinitions() {
21821
+ if (!getSandboxPolicy().isSandbox) {
21822
+ return [];
21823
+ }
21824
+ return SANDBOX_COMMUNICATION_TOOL_DEFINITIONS;
21825
+ }
21615
21826
  function getSandboxOnlyNativeToolDefinitions() {
21616
- return getFactorAiSandboxToolDefinitions();
21827
+ return [...getSandboxCommunicationToolDefinitions(), ...getFactorAiSandboxToolDefinitions()];
21617
21828
  }
21618
21829
  function applyMetadataToToolDefinitions(toolDefinitions) {
21619
21830
  return toolDefinitions.map((definition) => {
@@ -21709,6 +21920,24 @@ function toolResultToString(value) {
21709
21920
  const result = normalizeToolResult(value);
21710
21921
  return JSON.stringify(result, null, 2);
21711
21922
  }
21923
+ function extractMessageToolPayload(parsed) {
21924
+ if (!parsed || typeof parsed !== "object") {
21925
+ return null;
21926
+ }
21927
+ const root = parsed;
21928
+ if (root.status === "error") {
21929
+ return { status: "error", message_type: void 0 };
21930
+ }
21931
+ const data = root.data && typeof root.data === "object" && !Array.isArray(root.data) ? root.data : root;
21932
+ if (typeof data.message_type === "string") {
21933
+ return { message_type: data.message_type, status: typeof root.status === "string" ? root.status : void 0 };
21934
+ }
21935
+ return null;
21936
+ }
21937
+ function isMessageToolResultTerminal(parsed) {
21938
+ const payload = extractMessageToolPayload(parsed);
21939
+ return payload?.message_type === "result" && payload.status !== "error";
21940
+ }
21712
21941
 
21713
21942
  // src/app/agent/tool_invoker.ts
21714
21943
  var ToolInvoker = class {
@@ -21723,9 +21952,9 @@ var ToolInvoker = class {
21723
21952
  async initialize() {
21724
21953
  try {
21725
21954
  const currentFilePath = fileURLToPath3(import.meta.url);
21726
- const currentDirPath = path28.dirname(currentFilePath);
21727
- const configPath = path28.resolve(currentDirPath, "config", "native_tools.json");
21728
- const fileContent = await fs28.readFile(configPath, "utf-8");
21955
+ const currentDirPath = path29.dirname(currentFilePath);
21956
+ const configPath = path29.resolve(currentDirPath, "config", "native_tools.json");
21957
+ const fileContent = await fs29.readFile(configPath, "utf-8");
21729
21958
  const config2 = JSON.parse(fileContent);
21730
21959
  this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
21731
21960
  const sandboxOnlyTools = applyMetadataToToolDefinitions(getSandboxOnlyNativeToolDefinitions());
@@ -21772,8 +22001,8 @@ var ToolInvoker = class {
21772
22001
  };
21773
22002
 
21774
22003
  // src/app/agent/tools/mcp/mcp_client.ts
21775
- import { promises as fs29 } from "fs";
21776
- import path29 from "path";
22004
+ import { promises as fs30 } from "fs";
22005
+ import path30 from "path";
21777
22006
  import os20 from "os";
21778
22007
  import { fileURLToPath as fileURLToPath4 } from "url";
21779
22008
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -21801,9 +22030,9 @@ var MCPClient = class {
21801
22030
  });
21802
22031
  }
21803
22032
  const __filename = fileURLToPath4(import.meta.url);
21804
- const __dirname2 = path29.dirname(__filename);
21805
- const defaultConfigPath = path29.resolve(__dirname2, "config", "bluma-mcp.json");
21806
- const userConfigPath = path29.join(os20.homedir(), ".bluma", "bluma-mcp.json");
22033
+ const __dirname2 = path30.dirname(__filename);
22034
+ const defaultConfigPath = path30.resolve(__dirname2, "config", "bluma-mcp.json");
22035
+ const userConfigPath = path30.join(os20.homedir(), ".bluma", "bluma-mcp.json");
21807
22036
  const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
21808
22037
  const userConfig = await this.loadMcpConfig(userConfigPath, "User");
21809
22038
  const mergedConfig = {
@@ -21837,7 +22066,7 @@ var MCPClient = class {
21837
22066
  }
21838
22067
  async loadMcpConfig(configPath, configType) {
21839
22068
  try {
21840
- const fileContent = await fs29.readFile(configPath, "utf-8");
22069
+ const fileContent = await fs30.readFile(configPath, "utf-8");
21841
22070
  const processedContent = this.replaceEnvPlaceholders(fileContent);
21842
22071
  return JSON.parse(processedContent);
21843
22072
  } catch (error) {
@@ -21894,11 +22123,14 @@ var MCPClient = class {
21894
22123
  }
21895
22124
  }
21896
22125
  async invoke(toolName, args) {
21897
- const route = this.toolToServerMap.get(toolName);
22126
+ let route = this.toolToServerMap.get(toolName);
22127
+ if (!route && getNativeToolImplementation(toolName)) {
22128
+ route = { server: "native", originalName: toolName };
22129
+ }
21898
22130
  if (!route) {
21899
22131
  return {
21900
22132
  status: "error",
21901
- error: `This tool '${toolName}'is not found or registered you must use the correct name of this tool.`
22133
+ error: `This tool '${toolName}' is not found or registered. You must use the correct name from the available tools list.`
21902
22134
  };
21903
22135
  }
21904
22136
  if (route.server === "native") {
@@ -22031,7 +22263,7 @@ You streamed or returned **user-visible markdown as assistant content** instead
22031
22263
 
22032
22264
  Do this **immediately** in your next step (single tool call, no prose outside tools):
22033
22265
 
22034
- - Call **\`message\`** with **\`message_type\`: \`"result"\`**, put the user-facing summary in **\`content\`**, and put deliverable paths in **\`attachments\`** (absolute paths).
22266
+ - Call **\`message\`** with **\`message_type\`: \`"result"\`**, summary in **\`content\`**, and every file path in **\`attachments\`** under **\`.bluma/artifacts/\`** (this is the only delivery channel for files).
22035
22267
 
22036
22268
  Do **not** repeat the same summary as plain assistant text again.
22037
22269
  PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
@@ -22046,14 +22278,14 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
22046
22278
  };
22047
22279
 
22048
22280
  // src/app/agent/bluma/core/bluma.ts
22049
- import path40 from "path";
22050
- import fs39 from "fs";
22281
+ import path41 from "path";
22282
+ import fs40 from "fs";
22051
22283
  import { v4 as uuidv48 } from "uuid";
22052
22284
 
22053
22285
  // src/app/agent/session_manager/session_manager.ts
22054
- import path30 from "path";
22286
+ import path31 from "path";
22055
22287
  import os21 from "os";
22056
- import { promises as fs30 } from "fs";
22288
+ import { promises as fs31 } from "fs";
22057
22289
  var fileLocks = /* @__PURE__ */ new Map();
22058
22290
  async function withFileLock(file, fn) {
22059
22291
  const prev = fileLocks.get(file) || Promise.resolve();
@@ -22089,13 +22321,13 @@ function debouncedSave(sessionFile, history, memory) {
22089
22321
  function expandHome(p) {
22090
22322
  if (!p) return p;
22091
22323
  if (p.startsWith("~")) {
22092
- return path30.join(os21.homedir(), p.slice(1));
22324
+ return path31.join(os21.homedir(), p.slice(1));
22093
22325
  }
22094
22326
  return p;
22095
22327
  }
22096
22328
  function getPreferredAppDir() {
22097
- const fixed = path30.join(os21.homedir(), ".bluma");
22098
- return path30.resolve(expandHome(fixed));
22329
+ const fixed = path31.join(os21.homedir(), ".bluma");
22330
+ return path31.resolve(expandHome(fixed));
22099
22331
  }
22100
22332
  async function safeRenameWithRetry(src, dest, maxRetries = 6) {
22101
22333
  let attempt = 0;
@@ -22103,10 +22335,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
22103
22335
  const isWin = process.platform === "win32";
22104
22336
  while (attempt <= maxRetries) {
22105
22337
  try {
22106
- const dir = path30.dirname(dest);
22107
- await fs30.mkdir(dir, { recursive: true }).catch(() => {
22338
+ const dir = path31.dirname(dest);
22339
+ await fs31.mkdir(dir, { recursive: true }).catch(() => {
22108
22340
  });
22109
- await fs30.rename(src, dest);
22341
+ await fs31.rename(src, dest);
22110
22342
  return;
22111
22343
  } catch (e) {
22112
22344
  lastErr = e;
@@ -22119,13 +22351,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
22119
22351
  }
22120
22352
  }
22121
22353
  try {
22122
- await fs30.access(src);
22123
- const data = await fs30.readFile(src);
22124
- const dir = path30.dirname(dest);
22125
- await fs30.mkdir(dir, { recursive: true }).catch(() => {
22354
+ await fs31.access(src);
22355
+ const data = await fs31.readFile(src);
22356
+ const dir = path31.dirname(dest);
22357
+ await fs31.mkdir(dir, { recursive: true }).catch(() => {
22126
22358
  });
22127
- await fs30.writeFile(dest, data);
22128
- await fs30.unlink(src).catch(() => {
22359
+ await fs31.writeFile(dest, data);
22360
+ await fs31.unlink(src).catch(() => {
22129
22361
  });
22130
22362
  return;
22131
22363
  } catch (fallbackErr) {
@@ -22138,16 +22370,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
22138
22370
  }
22139
22371
  async function ensureSessionDir() {
22140
22372
  const appDir = getPreferredAppDir();
22141
- const sessionDir = path30.join(appDir, "sessions");
22142
- await fs30.mkdir(sessionDir, { recursive: true });
22373
+ const sessionDir = path31.join(appDir, "sessions");
22374
+ await fs31.mkdir(sessionDir, { recursive: true });
22143
22375
  return sessionDir;
22144
22376
  }
22145
22377
  async function loadSession(sessionId) {
22146
22378
  const sessionDir = await ensureSessionDir();
22147
- const sessionFile = path30.join(sessionDir, `${sessionId}.json`);
22379
+ const sessionFile = path31.join(sessionDir, `${sessionId}.json`);
22148
22380
  try {
22149
- await fs30.access(sessionFile);
22150
- const fileContent = await fs30.readFile(sessionFile, "utf-8");
22381
+ await fs31.access(sessionFile);
22382
+ const fileContent = await fs31.readFile(sessionFile, "utf-8");
22151
22383
  const sessionData = JSON.parse(fileContent);
22152
22384
  const memory = {
22153
22385
  historyAnchor: sessionData.history_anchor ?? null,
@@ -22164,13 +22396,13 @@ async function loadOrcreateSession(sessionId) {
22164
22396
  return existing;
22165
22397
  }
22166
22398
  const sessionDir = await ensureSessionDir();
22167
- const sessionFile = path30.join(sessionDir, `${sessionId}.json`);
22399
+ const sessionFile = path31.join(sessionDir, `${sessionId}.json`);
22168
22400
  const newSessionData = {
22169
22401
  session_id: sessionId,
22170
22402
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
22171
22403
  conversation_history: []
22172
22404
  };
22173
- await fs30.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
22405
+ await fs31.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
22174
22406
  const emptyMemory = {
22175
22407
  historyAnchor: null,
22176
22408
  compressedTurnSliceCount: 0
@@ -22181,12 +22413,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
22181
22413
  await withFileLock(sessionFile, async () => {
22182
22414
  let sessionData;
22183
22415
  try {
22184
- const dir = path30.dirname(sessionFile);
22185
- await fs30.mkdir(dir, { recursive: true });
22416
+ const dir = path31.dirname(sessionFile);
22417
+ await fs31.mkdir(dir, { recursive: true });
22186
22418
  } catch {
22187
22419
  }
22188
22420
  try {
22189
- const fileContent = await fs30.readFile(sessionFile, "utf-8");
22421
+ const fileContent = await fs31.readFile(sessionFile, "utf-8");
22190
22422
  sessionData = JSON.parse(fileContent);
22191
22423
  } catch (error) {
22192
22424
  const code = error && error.code;
@@ -22197,14 +22429,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
22197
22429
  console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
22198
22430
  }
22199
22431
  }
22200
- const sessionId = path30.basename(sessionFile, ".json");
22432
+ const sessionId = path31.basename(sessionFile, ".json");
22201
22433
  sessionData = {
22202
22434
  session_id: sessionId,
22203
22435
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
22204
22436
  conversation_history: []
22205
22437
  };
22206
22438
  try {
22207
- await fs30.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
22439
+ await fs31.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
22208
22440
  } catch {
22209
22441
  }
22210
22442
  }
@@ -22220,7 +22452,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
22220
22452
  }
22221
22453
  const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
22222
22454
  try {
22223
- await fs30.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
22455
+ await fs31.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
22224
22456
  await safeRenameWithRetry(tempSessionFile, sessionFile);
22225
22457
  } catch (writeError) {
22226
22458
  if (writeError instanceof Error) {
@@ -22229,7 +22461,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
22229
22461
  console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
22230
22462
  }
22231
22463
  try {
22232
- await fs30.unlink(tempSessionFile);
22464
+ await fs31.unlink(tempSessionFile);
22233
22465
  } catch {
22234
22466
  }
22235
22467
  }
@@ -22247,13 +22479,13 @@ async function saveSessionHistory(sessionFile, history, memory) {
22247
22479
 
22248
22480
  // src/app/agent/core/prompt/prompt_builder.ts
22249
22481
  import os26 from "os";
22250
- import fs37 from "fs";
22251
- import path37 from "path";
22482
+ import fs38 from "fs";
22483
+ import path38 from "path";
22252
22484
  import { execSync as execSync4 } from "child_process";
22253
22485
 
22254
22486
  // src/app/agent/skills/skill_loader.ts
22255
- import fs31 from "fs";
22256
- import path31 from "path";
22487
+ import fs32 from "fs";
22488
+ import path32 from "path";
22257
22489
  import os22 from "os";
22258
22490
  import { fileURLToPath as fileURLToPath5 } from "node:url";
22259
22491
  var SkillLoader = class _SkillLoader {
@@ -22263,8 +22495,8 @@ var SkillLoader = class _SkillLoader {
22263
22495
  cache = /* @__PURE__ */ new Map();
22264
22496
  conflicts = [];
22265
22497
  constructor(projectRoot, bundledDir) {
22266
- this.projectSkillsDir = path31.join(projectRoot, ".bluma", "skills");
22267
- this.globalSkillsDir = path31.join(os22.homedir(), ".bluma", "skills");
22498
+ this.projectSkillsDir = path32.join(projectRoot, ".bluma", "skills");
22499
+ this.globalSkillsDir = path32.join(os22.homedir(), ".bluma", "skills");
22268
22500
  this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
22269
22501
  }
22270
22502
  /**
@@ -22273,48 +22505,48 @@ var SkillLoader = class _SkillLoader {
22273
22505
  */
22274
22506
  static resolveBundledDir() {
22275
22507
  if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
22276
- return path31.join(process.cwd(), "dist", "config", "skills");
22508
+ return path32.join(process.cwd(), "dist", "config", "skills");
22277
22509
  }
22278
22510
  const candidates = [];
22279
22511
  const push = (p) => {
22280
- const abs = path31.resolve(p);
22512
+ const abs = path32.resolve(p);
22281
22513
  if (!candidates.includes(abs)) {
22282
22514
  candidates.push(abs);
22283
22515
  }
22284
22516
  };
22285
22517
  let argvBundled = null;
22286
22518
  try {
22287
- const bundleDir = path31.dirname(fileURLToPath5(import.meta.url));
22288
- push(path31.join(bundleDir, "config", "skills"));
22519
+ const bundleDir = path32.dirname(fileURLToPath5(import.meta.url));
22520
+ push(path32.join(bundleDir, "config", "skills"));
22289
22521
  } catch {
22290
22522
  }
22291
22523
  const argv1 = process.argv[1];
22292
22524
  if (argv1 && !argv1.startsWith("-")) {
22293
22525
  try {
22294
22526
  let resolved = argv1;
22295
- if (path31.isAbsolute(argv1) && fs31.existsSync(argv1)) {
22296
- resolved = fs31.realpathSync(argv1);
22297
- } else if (!path31.isAbsolute(argv1)) {
22298
- resolved = path31.resolve(process.cwd(), argv1);
22527
+ if (path32.isAbsolute(argv1) && fs32.existsSync(argv1)) {
22528
+ resolved = fs32.realpathSync(argv1);
22529
+ } else if (!path32.isAbsolute(argv1)) {
22530
+ resolved = path32.resolve(process.cwd(), argv1);
22299
22531
  }
22300
- const scriptDir = path31.dirname(resolved);
22301
- argvBundled = path31.join(scriptDir, "config", "skills");
22532
+ const scriptDir = path32.dirname(resolved);
22533
+ argvBundled = path32.join(scriptDir, "config", "skills");
22302
22534
  push(argvBundled);
22303
22535
  } catch {
22304
22536
  }
22305
22537
  }
22306
22538
  for (const abs of candidates) {
22307
- if (fs31.existsSync(abs)) {
22539
+ if (fs32.existsSync(abs)) {
22308
22540
  return abs;
22309
22541
  }
22310
22542
  }
22311
22543
  try {
22312
- return path31.join(path31.dirname(fileURLToPath5(import.meta.url)), "config", "skills");
22544
+ return path32.join(path32.dirname(fileURLToPath5(import.meta.url)), "config", "skills");
22313
22545
  } catch {
22314
22546
  if (argvBundled) {
22315
22547
  return argvBundled;
22316
22548
  }
22317
- return path31.join(os22.homedir(), ".bluma", "__bundled_skills_unresolved__");
22549
+ return path32.join(os22.homedir(), ".bluma", "__bundled_skills_unresolved__");
22318
22550
  }
22319
22551
  }
22320
22552
  /**
@@ -22343,8 +22575,8 @@ var SkillLoader = class _SkillLoader {
22343
22575
  this.conflicts.push({
22344
22576
  name: skill.name,
22345
22577
  userSource: source,
22346
- userPath: path31.join(dir, skill.name, "SKILL.md"),
22347
- bundledPath: path31.join(this.bundledSkillsDir, skill.name, "SKILL.md")
22578
+ userPath: path32.join(dir, skill.name, "SKILL.md"),
22579
+ bundledPath: path32.join(this.bundledSkillsDir, skill.name, "SKILL.md")
22348
22580
  });
22349
22581
  continue;
22350
22582
  }
@@ -22352,20 +22584,20 @@ var SkillLoader = class _SkillLoader {
22352
22584
  }
22353
22585
  }
22354
22586
  listFromDir(dir, source) {
22355
- if (!fs31.existsSync(dir)) return [];
22587
+ if (!fs32.existsSync(dir)) return [];
22356
22588
  try {
22357
- return fs31.readdirSync(dir).filter((d) => {
22358
- const fullPath = path31.join(dir, d);
22359
- return fs31.statSync(fullPath).isDirectory() && fs31.existsSync(path31.join(fullPath, "SKILL.md"));
22360
- }).map((d) => this.loadMetadataFromPath(path31.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
22589
+ return fs32.readdirSync(dir).filter((d) => {
22590
+ const fullPath = path32.join(dir, d);
22591
+ return fs32.statSync(fullPath).isDirectory() && fs32.existsSync(path32.join(fullPath, "SKILL.md"));
22592
+ }).map((d) => this.loadMetadataFromPath(path32.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
22361
22593
  } catch {
22362
22594
  return [];
22363
22595
  }
22364
22596
  }
22365
22597
  loadMetadataFromPath(skillPath, skillName, source) {
22366
- if (!fs31.existsSync(skillPath)) return null;
22598
+ if (!fs32.existsSync(skillPath)) return null;
22367
22599
  try {
22368
- const raw = fs31.readFileSync(skillPath, "utf-8");
22600
+ const raw = fs32.readFileSync(skillPath, "utf-8");
22369
22601
  const parsed = this.parseFrontmatter(raw);
22370
22602
  return {
22371
22603
  name: parsed.name || skillName,
@@ -22387,12 +22619,12 @@ var SkillLoader = class _SkillLoader {
22387
22619
  */
22388
22620
  load(name) {
22389
22621
  if (this.cache.has(name)) return this.cache.get(name);
22390
- const bundledPath = path31.join(this.bundledSkillsDir, name, "SKILL.md");
22391
- const projectPath = path31.join(this.projectSkillsDir, name, "SKILL.md");
22392
- const globalPath = path31.join(this.globalSkillsDir, name, "SKILL.md");
22393
- const existsBundled = fs31.existsSync(bundledPath);
22394
- const existsProject = fs31.existsSync(projectPath);
22395
- const existsGlobal = fs31.existsSync(globalPath);
22622
+ const bundledPath = path32.join(this.bundledSkillsDir, name, "SKILL.md");
22623
+ const projectPath = path32.join(this.projectSkillsDir, name, "SKILL.md");
22624
+ const globalPath = path32.join(this.globalSkillsDir, name, "SKILL.md");
22625
+ const existsBundled = fs32.existsSync(bundledPath);
22626
+ const existsProject = fs32.existsSync(projectPath);
22627
+ const existsGlobal = fs32.existsSync(globalPath);
22396
22628
  if (existsBundled && (existsProject || existsGlobal)) {
22397
22629
  const conflictSource = existsProject ? "project" : "global";
22398
22630
  const conflictPath = existsProject ? projectPath : globalPath;
@@ -22431,9 +22663,9 @@ var SkillLoader = class _SkillLoader {
22431
22663
  }
22432
22664
  loadFromPath(skillPath, name, source) {
22433
22665
  try {
22434
- const raw = fs31.readFileSync(skillPath, "utf-8");
22666
+ const raw = fs32.readFileSync(skillPath, "utf-8");
22435
22667
  const parsed = this.parseFrontmatter(raw);
22436
- const skillDir = path31.dirname(skillPath);
22668
+ const skillDir = path32.dirname(skillPath);
22437
22669
  return {
22438
22670
  name: parsed.name || name,
22439
22671
  description: parsed.description || "",
@@ -22442,22 +22674,22 @@ var SkillLoader = class _SkillLoader {
22442
22674
  version: parsed.version,
22443
22675
  author: parsed.author,
22444
22676
  license: parsed.license,
22445
- references: this.scanAssets(path31.join(skillDir, "references")),
22446
- scripts: this.scanAssets(path31.join(skillDir, "scripts"))
22677
+ references: this.scanAssets(path32.join(skillDir, "references")),
22678
+ scripts: this.scanAssets(path32.join(skillDir, "scripts"))
22447
22679
  };
22448
22680
  } catch {
22449
22681
  return null;
22450
22682
  }
22451
22683
  }
22452
22684
  scanAssets(dir) {
22453
- if (!fs31.existsSync(dir)) return [];
22685
+ if (!fs32.existsSync(dir)) return [];
22454
22686
  try {
22455
- return fs31.readdirSync(dir).filter((f) => {
22456
- const fp = path31.join(dir, f);
22457
- return fs31.statSync(fp).isFile();
22687
+ return fs32.readdirSync(dir).filter((f) => {
22688
+ const fp = path32.join(dir, f);
22689
+ return fs32.statSync(fp).isFile();
22458
22690
  }).map((f) => ({
22459
22691
  name: f,
22460
- path: path31.resolve(dir, f)
22692
+ path: path32.resolve(dir, f)
22461
22693
  }));
22462
22694
  } catch {
22463
22695
  return [];
@@ -22514,10 +22746,10 @@ var SkillLoader = class _SkillLoader {
22514
22746
  this.cache.clear();
22515
22747
  }
22516
22748
  exists(name) {
22517
- const bundledPath = path31.join(this.bundledSkillsDir, name, "SKILL.md");
22518
- const projectPath = path31.join(this.projectSkillsDir, name, "SKILL.md");
22519
- const globalPath = path31.join(this.globalSkillsDir, name, "SKILL.md");
22520
- return fs31.existsSync(bundledPath) || fs31.existsSync(projectPath) || fs31.existsSync(globalPath);
22749
+ const bundledPath = path32.join(this.bundledSkillsDir, name, "SKILL.md");
22750
+ const projectPath = path32.join(this.projectSkillsDir, name, "SKILL.md");
22751
+ const globalPath = path32.join(this.globalSkillsDir, name, "SKILL.md");
22752
+ return fs32.existsSync(bundledPath) || fs32.existsSync(projectPath) || fs32.existsSync(globalPath);
22521
22753
  }
22522
22754
  /**
22523
22755
  * Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
@@ -22549,13 +22781,13 @@ var SkillLoader = class _SkillLoader {
22549
22781
  };
22550
22782
 
22551
22783
  // src/app/agent/core/prompt/workspace_snapshot.ts
22552
- import fs33 from "fs";
22553
- import path33 from "path";
22784
+ import fs34 from "fs";
22785
+ import path34 from "path";
22554
22786
  import { execSync as execSync3 } from "child_process";
22555
22787
 
22556
22788
  // src/app/agent/utils/blumamd.ts
22557
- import fs32 from "fs";
22558
- import path32 from "path";
22789
+ import fs33 from "fs";
22790
+ import path33 from "path";
22559
22791
  import os23 from "os";
22560
22792
  import { execSync as execSync2 } from "child_process";
22561
22793
  var MEMORY_INSTRUCTION_PROMPT = "Instru\xE7\xF5es de mem\xF3ria do BluMa (BLUMA.md) est\xE3o abaixo. Siga estas instru\xE7\xF5es exatamente como escritas. Estas instru\xE7\xF5es OVERRIDE qualquer comportamento padr\xE3o.";
@@ -22685,12 +22917,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
22685
22917
  function expandIncludePath(includePath, baseDir) {
22686
22918
  const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
22687
22919
  if (cleanPath.startsWith("~")) {
22688
- return path32.join(os23.homedir(), cleanPath.slice(1));
22920
+ return path33.join(os23.homedir(), cleanPath.slice(1));
22689
22921
  }
22690
- if (path32.isAbsolute(cleanPath)) {
22922
+ if (path33.isAbsolute(cleanPath)) {
22691
22923
  return cleanPath;
22692
22924
  }
22693
- return path32.resolve(baseDir, cleanPath);
22925
+ return path33.resolve(baseDir, cleanPath);
22694
22926
  }
22695
22927
  function processIncludes(content, baseDir, processedFiles) {
22696
22928
  const lines = content.split("\n");
@@ -22699,20 +22931,20 @@ function processIncludes(content, baseDir, processedFiles) {
22699
22931
  const includeMatch = line.match(/^@\s*([^\s]+)/);
22700
22932
  if (includeMatch) {
22701
22933
  const includePath = expandIncludePath(includeMatch[1], baseDir);
22702
- const normalizedPath = path32.normalize(includePath);
22934
+ const normalizedPath = path33.normalize(includePath);
22703
22935
  if (processedFiles.has(normalizedPath)) {
22704
22936
  result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
22705
22937
  continue;
22706
22938
  }
22707
- const ext = path32.extname(includePath).toLowerCase();
22939
+ const ext = path33.extname(includePath).toLowerCase();
22708
22940
  if (!TEXT_FILE_EXTENSIONS.has(ext)) {
22709
22941
  result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
22710
22942
  continue;
22711
22943
  }
22712
22944
  try {
22713
- const includedContent = fs32.readFileSync(includePath, "utf-8");
22945
+ const includedContent = fs33.readFileSync(includePath, "utf-8");
22714
22946
  processedFiles.add(normalizedPath);
22715
- const processedContent = processIncludes(includedContent, path32.dirname(includePath), processedFiles);
22947
+ const processedContent = processIncludes(includedContent, path33.dirname(includePath), processedFiles);
22716
22948
  result.push(`
22717
22949
  <!-- BEGIN INCLUDE ${includeMatch[1]} -->
22718
22950
  `);
@@ -22758,9 +22990,9 @@ function parseFrontmatterPaths(paths2) {
22758
22990
  }
22759
22991
  function readMemoryFile(filePath, type, includeBasePath) {
22760
22992
  try {
22761
- const rawContent = fs32.readFileSync(filePath, "utf-8");
22762
- const baseDir = includeBasePath || path32.dirname(filePath);
22763
- const processedFiles = /* @__PURE__ */ new Set([path32.normalize(filePath)]);
22993
+ const rawContent = fs33.readFileSync(filePath, "utf-8");
22994
+ const baseDir = includeBasePath || path33.dirname(filePath);
22995
+ const processedFiles = /* @__PURE__ */ new Set([path33.normalize(filePath)]);
22764
22996
  const { frontmatter, content: withoutFrontmatter } = parseFrontmatter(rawContent);
22765
22997
  const globs = parseFrontmatterPaths(frontmatter.paths);
22766
22998
  const processedContent = processIncludes(withoutFrontmatter, baseDir, processedFiles);
@@ -22782,15 +23014,15 @@ function readMemoryFile(filePath, type, includeBasePath) {
22782
23014
  }
22783
23015
  function findGitRoot(startDir) {
22784
23016
  let current = startDir;
22785
- while (current !== path32.dirname(current)) {
22786
- const gitPath = path32.join(current, ".git");
23017
+ while (current !== path33.dirname(current)) {
23018
+ const gitPath = path33.join(current, ".git");
22787
23019
  try {
22788
- if (fs32.existsSync(gitPath)) {
23020
+ if (fs33.existsSync(gitPath)) {
22789
23021
  return current;
22790
23022
  }
22791
23023
  } catch {
22792
23024
  }
22793
- current = path32.dirname(current);
23025
+ current = path33.dirname(current);
22794
23026
  }
22795
23027
  return null;
22796
23028
  }
@@ -22815,17 +23047,17 @@ function getGitUserInfo(cwd2) {
22815
23047
  }
22816
23048
  function processRulesDirectory(rulesDir, type, processedPaths, conditionalRule = false) {
22817
23049
  const result = [];
22818
- if (!fs32.existsSync(rulesDir)) {
23050
+ if (!fs33.existsSync(rulesDir)) {
22819
23051
  return result;
22820
23052
  }
22821
23053
  try {
22822
- const entries = fs32.readdirSync(rulesDir, { withFileTypes: true });
23054
+ const entries = fs33.readdirSync(rulesDir, { withFileTypes: true });
22823
23055
  for (const entry of entries) {
22824
- const entryPath = path32.join(rulesDir, entry.name);
23056
+ const entryPath = path33.join(rulesDir, entry.name);
22825
23057
  if (entry.isDirectory()) {
22826
23058
  result.push(...processRulesDirectory(entryPath, type, processedPaths, conditionalRule));
22827
23059
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
22828
- const normalizedPath = path32.normalize(entryPath);
23060
+ const normalizedPath = path33.normalize(entryPath);
22829
23061
  if (processedPaths.has(normalizedPath)) {
22830
23062
  continue;
22831
23063
  }
@@ -22861,13 +23093,13 @@ function loadManagedMemory() {
22861
23093
  function loadUserMemory() {
22862
23094
  const files = [];
22863
23095
  const homeDir = os23.homedir();
22864
- const userBlumaDir = path32.join(homeDir, ".bluma");
22865
- const userBlumaMd = path32.join(userBlumaDir, "BLUMA.md");
23096
+ const userBlumaDir = path33.join(homeDir, ".bluma");
23097
+ const userBlumaMd = path33.join(userBlumaDir, "BLUMA.md");
22866
23098
  const userFile = readMemoryFile(userBlumaMd, "User");
22867
23099
  if (userFile && userFile.content.trim()) {
22868
23100
  files.push(userFile);
22869
23101
  }
22870
- const userRulesDir = path32.join(userBlumaDir, "rules");
23102
+ const userRulesDir = path33.join(userBlumaDir, "rules");
22871
23103
  const processedPaths = /* @__PURE__ */ new Set();
22872
23104
  files.push(...processRulesDirectory(userRulesDir, "User", processedPaths, false));
22873
23105
  return files;
@@ -22879,10 +23111,10 @@ function loadProjectMemory(cwd2) {
22879
23111
  let currentDir = cwd2;
22880
23112
  const MAX_TRAVERSAL_DEPTH = 20;
22881
23113
  let depth = 0;
22882
- const normalizedGitRoot = path32.resolve(gitRoot);
22883
- while (currentDir !== path32.dirname(currentDir) && path32.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
23114
+ const normalizedGitRoot = path33.resolve(gitRoot);
23115
+ while (currentDir !== path33.dirname(currentDir) && path33.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
22884
23116
  dirs.push(currentDir);
22885
- currentDir = path32.dirname(currentDir);
23117
+ currentDir = path33.dirname(currentDir);
22886
23118
  depth++;
22887
23119
  }
22888
23120
  if (!dirs.includes(gitRoot)) {
@@ -22890,7 +23122,7 @@ function loadProjectMemory(cwd2) {
22890
23122
  }
22891
23123
  const processedPaths = /* @__PURE__ */ new Set();
22892
23124
  for (const dir of dirs.reverse()) {
22893
- const projectBlumaMd = path32.join(dir, "BLUMA.md");
23125
+ const projectBlumaMd = path33.join(dir, "BLUMA.md");
22894
23126
  if (!processedPaths.has(projectBlumaMd)) {
22895
23127
  processedPaths.add(projectBlumaMd);
22896
23128
  const projectFile = readMemoryFile(projectBlumaMd, "Project");
@@ -22898,7 +23130,7 @@ function loadProjectMemory(cwd2) {
22898
23130
  files.push(projectFile);
22899
23131
  }
22900
23132
  }
22901
- const blumaDirBlumaMd = path32.join(dir, ".bluma", "BLUMA.md");
23133
+ const blumaDirBlumaMd = path33.join(dir, ".bluma", "BLUMA.md");
22902
23134
  if (!processedPaths.has(blumaDirBlumaMd)) {
22903
23135
  processedPaths.add(blumaDirBlumaMd);
22904
23136
  const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "Project");
@@ -22906,10 +23138,10 @@ function loadProjectMemory(cwd2) {
22906
23138
  files.push(blumaDirFile);
22907
23139
  }
22908
23140
  }
22909
- const rulesDir = path32.join(dir, ".bluma", "rules");
23141
+ const rulesDir = path33.join(dir, ".bluma", "rules");
22910
23142
  files.push(...processRulesDirectory(rulesDir, "Project", processedPaths, false));
22911
23143
  }
22912
- const localBlumaMd = path32.join(cwd2, "BLUMA.local.md");
23144
+ const localBlumaMd = path33.join(cwd2, "BLUMA.local.md");
22913
23145
  if (!processedPaths.has(localBlumaMd)) {
22914
23146
  processedPaths.add(localBlumaMd);
22915
23147
  const localFile = readMemoryFile(localBlumaMd, "Local");
@@ -22995,10 +23227,10 @@ var LIMITS = {
22995
23227
  };
22996
23228
  function safeReadFile(filePath, maxChars) {
22997
23229
  try {
22998
- if (!fs33.existsSync(filePath)) return null;
22999
- const st = fs33.statSync(filePath);
23230
+ if (!fs34.existsSync(filePath)) return null;
23231
+ const st = fs34.statSync(filePath);
23000
23232
  if (!st.isFile()) return null;
23001
- const raw = fs33.readFileSync(filePath, "utf8");
23233
+ const raw = fs34.readFileSync(filePath, "utf8");
23002
23234
  if (raw.includes("\0")) return null;
23003
23235
  if (raw.length <= maxChars) return raw;
23004
23236
  return `${raw.slice(0, maxChars)}
@@ -23010,7 +23242,7 @@ function safeReadFile(filePath, maxChars) {
23010
23242
  }
23011
23243
  function tryReadReadme(cwd2) {
23012
23244
  for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
23013
- const c = safeReadFile(path33.join(cwd2, name), LIMITS.readme);
23245
+ const c = safeReadFile(path34.join(cwd2, name), LIMITS.readme);
23014
23246
  if (c) return `(${name})
23015
23247
  ${c}`;
23016
23248
  }
@@ -23018,14 +23250,14 @@ ${c}`;
23018
23250
  }
23019
23251
  function tryReadBluMaMd(cwd2) {
23020
23252
  const paths2 = [
23021
- path33.join(cwd2, "BluMa.md"),
23022
- path33.join(cwd2, "BLUMA.md"),
23023
- path33.join(cwd2, ".bluma", "BluMa.md")
23253
+ path34.join(cwd2, "BluMa.md"),
23254
+ path34.join(cwd2, "BLUMA.md"),
23255
+ path34.join(cwd2, ".bluma", "BluMa.md")
23024
23256
  ];
23025
23257
  for (const p of paths2) {
23026
23258
  const c = safeReadFile(p, LIMITS.blumaMd);
23027
23259
  if (c) {
23028
- const rel = path33.relative(cwd2, p) || p;
23260
+ const rel = path34.relative(cwd2, p) || p;
23029
23261
  return `(${rel})
23030
23262
  ${c}`;
23031
23263
  }
@@ -23033,10 +23265,10 @@ ${c}`;
23033
23265
  return null;
23034
23266
  }
23035
23267
  function summarizePackageJson(cwd2) {
23036
- const p = path33.join(cwd2, "package.json");
23268
+ const p = path34.join(cwd2, "package.json");
23037
23269
  try {
23038
- if (!fs33.existsSync(p)) return null;
23039
- const pkg = JSON.parse(fs33.readFileSync(p, "utf8"));
23270
+ if (!fs34.existsSync(p)) return null;
23271
+ const pkg = JSON.parse(fs34.readFileSync(p, "utf8"));
23040
23272
  const scripts = pkg.scripts;
23041
23273
  let scriptKeys = "";
23042
23274
  if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
@@ -23069,7 +23301,7 @@ function summarizePackageJson(cwd2) {
23069
23301
  }
23070
23302
  function topLevelListing(cwd2) {
23071
23303
  try {
23072
- const names = fs33.readdirSync(cwd2, { withFileTypes: true });
23304
+ const names = fs34.readdirSync(cwd2, { withFileTypes: true });
23073
23305
  const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
23074
23306
  const limited = sorted.slice(0, LIMITS.topDirEntries);
23075
23307
  const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
@@ -23127,7 +23359,7 @@ function buildWorkspaceSnapshot(cwd2) {
23127
23359
  parts.push(pkg);
23128
23360
  parts.push("```\n");
23129
23361
  }
23130
- const py = safeReadFile(path33.join(cwd2, "pyproject.toml"), LIMITS.pyproject);
23362
+ const py = safeReadFile(path34.join(cwd2, "pyproject.toml"), LIMITS.pyproject);
23131
23363
  if (py) {
23132
23364
  parts.push("### pyproject.toml (excerpt)\n```toml");
23133
23365
  parts.push(py);
@@ -23145,15 +23377,15 @@ function buildWorkspaceSnapshot(cwd2) {
23145
23377
  parts.push(bluma);
23146
23378
  parts.push("```\n");
23147
23379
  }
23148
- const contrib = safeReadFile(path33.join(cwd2, "CONTRIBUTING.md"), LIMITS.contributing);
23380
+ const contrib = safeReadFile(path34.join(cwd2, "CONTRIBUTING.md"), LIMITS.contributing);
23149
23381
  if (contrib) {
23150
23382
  parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
23151
23383
  parts.push(contrib);
23152
23384
  parts.push("```\n");
23153
23385
  }
23154
- const chlog = safeReadFile(path33.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
23386
+ const chlog = safeReadFile(path34.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
23155
23387
  if (!chlog) {
23156
- const alt = safeReadFile(path33.join(cwd2, "CHANGES.md"), LIMITS.changelog);
23388
+ const alt = safeReadFile(path34.join(cwd2, "CHANGES.md"), LIMITS.changelog);
23157
23389
  if (alt) {
23158
23390
  parts.push("### CHANGES.md (excerpt)\n```markdown");
23159
23391
  parts.push(alt);
@@ -23189,14 +23421,14 @@ function buildWorkspaceSnapshot(cwd2) {
23189
23421
  } else {
23190
23422
  parts.push("### Git\n(not a git work tree, or `git` unavailable)\n");
23191
23423
  }
23192
- const tsconfig = safeReadFile(path33.join(cwd2, "tsconfig.json"), LIMITS.tsconfig);
23424
+ const tsconfig = safeReadFile(path34.join(cwd2, "tsconfig.json"), LIMITS.tsconfig);
23193
23425
  if (tsconfig) {
23194
23426
  parts.push("### tsconfig.json (excerpt)\n```json");
23195
23427
  parts.push(tsconfig);
23196
23428
  parts.push("```\n");
23197
23429
  }
23198
23430
  for (const name of ["Dockerfile", "dockerfile", "Dockerfile.prod", "Dockerfile.dev"]) {
23199
- const df = safeReadFile(path33.join(cwd2, name), LIMITS.dockerfile);
23431
+ const df = safeReadFile(path34.join(cwd2, name), LIMITS.dockerfile);
23200
23432
  if (df) {
23201
23433
  parts.push(`### ${name} (excerpt)
23202
23434
  `);
@@ -23206,7 +23438,7 @@ function buildWorkspaceSnapshot(cwd2) {
23206
23438
  }
23207
23439
  }
23208
23440
  for (const name of ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"]) {
23209
- const dc = safeReadFile(path33.join(cwd2, name), LIMITS.dockerfile);
23441
+ const dc = safeReadFile(path34.join(cwd2, name), LIMITS.dockerfile);
23210
23442
  if (dc) {
23211
23443
  parts.push(`### ${name} (excerpt)
23212
23444
  `);
@@ -23221,12 +23453,12 @@ function buildWorkspaceSnapshot(cwd2) {
23221
23453
  ".circleci/config.yml",
23222
23454
  "Jenkinsfile"
23223
23455
  ]) {
23224
- const ciFile = path33.join(cwd2, ciPath);
23225
- if (fs33.existsSync(ciFile)) {
23226
- const st = fs33.statSync(ciFile);
23456
+ const ciFile = path34.join(cwd2, ciPath);
23457
+ if (fs34.existsSync(ciFile)) {
23458
+ const st = fs34.statSync(ciFile);
23227
23459
  if (st.isDirectory()) {
23228
23460
  try {
23229
- const wfFiles = fs33.readdirSync(ciFile).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
23461
+ const wfFiles = fs34.readdirSync(ciFile).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
23230
23462
  if (wfFiles.length > 0) {
23231
23463
  parts.push(`### GitHub Actions workflows
23232
23464
  \`${wfFiles.join("`, `")}\`
@@ -23237,7 +23469,7 @@ function buildWorkspaceSnapshot(cwd2) {
23237
23469
  } else {
23238
23470
  const ci = safeReadFile(ciFile, LIMITS.ciConfig);
23239
23471
  if (ci) {
23240
- parts.push(`### CI config (${path33.basename(ciPath)})
23472
+ parts.push(`### CI config (${path34.basename(ciPath)})
23241
23473
  `);
23242
23474
  parts.push(ci);
23243
23475
  parts.push("\n");
@@ -23246,8 +23478,8 @@ function buildWorkspaceSnapshot(cwd2) {
23246
23478
  }
23247
23479
  }
23248
23480
  for (const depFile of ["requirements.txt", "Pipfile", "poetry.lock", "Gemfile", "go.sum", "Cargo.lock"]) {
23249
- const depPath = path33.join(cwd2, depFile);
23250
- if (fs33.existsSync(depPath)) {
23481
+ const depPath = path34.join(cwd2, depFile);
23482
+ if (fs34.existsSync(depPath)) {
23251
23483
  const depContent = safeReadFile(depPath, 1500);
23252
23484
  if (depContent) {
23253
23485
  parts.push(`### ${depFile} (top entries)
@@ -23260,10 +23492,10 @@ function buildWorkspaceSnapshot(cwd2) {
23260
23492
  }
23261
23493
  }
23262
23494
  for (const envFile of [".env.example", ".env.sample", ".env.template"]) {
23263
- const envPath = path33.join(cwd2, envFile);
23264
- if (fs33.existsSync(envPath)) {
23495
+ const envPath = path34.join(cwd2, envFile);
23496
+ if (fs34.existsSync(envPath)) {
23265
23497
  try {
23266
- const raw = fs33.readFileSync(envPath, "utf-8");
23498
+ const raw = fs34.readFileSync(envPath, "utf-8");
23267
23499
  const keys = raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).map((l) => {
23268
23500
  const eqIndex = l.indexOf("=");
23269
23501
  return eqIndex >= 0 ? l.slice(0, eqIndex).trim() : l.trim();
@@ -23286,15 +23518,15 @@ init_runtime_config();
23286
23518
 
23287
23519
  // src/app/agent/runtime/plugin_registry.ts
23288
23520
  init_sandbox_policy();
23289
- import fs34 from "fs";
23521
+ import fs35 from "fs";
23290
23522
  import os24 from "os";
23291
- import path34 from "path";
23523
+ import path35 from "path";
23292
23524
  function getProjectPluginsDir() {
23293
23525
  const policy = getSandboxPolicy();
23294
- return path34.join(policy.workspaceRoot, ".bluma", "plugins");
23526
+ return path35.join(policy.workspaceRoot, ".bluma", "plugins");
23295
23527
  }
23296
23528
  function getGlobalPluginsDir() {
23297
- return path34.join(process.env.HOME || os24.homedir(), ".bluma", "plugins");
23529
+ return path35.join(process.env.HOME || os24.homedir(), ".bluma", "plugins");
23298
23530
  }
23299
23531
  function getPluginDirs() {
23300
23532
  return {
@@ -23303,11 +23535,11 @@ function getPluginDirs() {
23303
23535
  };
23304
23536
  }
23305
23537
  function readManifest(manifestPath, fallbackName) {
23306
- if (!fs34.existsSync(manifestPath)) {
23538
+ if (!fs35.existsSync(manifestPath)) {
23307
23539
  return null;
23308
23540
  }
23309
23541
  try {
23310
- const parsed = JSON.parse(fs34.readFileSync(manifestPath, "utf-8"));
23542
+ const parsed = JSON.parse(fs35.readFileSync(manifestPath, "utf-8"));
23311
23543
  return {
23312
23544
  name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
23313
23545
  description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
@@ -23320,22 +23552,22 @@ function readManifest(manifestPath, fallbackName) {
23320
23552
  }
23321
23553
  function findManifestPath(pluginDir) {
23322
23554
  const candidates = [
23323
- path34.join(pluginDir, ".codex-plugin", "plugin.json"),
23324
- path34.join(pluginDir, "plugin.json")
23555
+ path35.join(pluginDir, ".codex-plugin", "plugin.json"),
23556
+ path35.join(pluginDir, "plugin.json")
23325
23557
  ];
23326
23558
  for (const candidate of candidates) {
23327
- if (fs34.existsSync(candidate)) {
23559
+ if (fs35.existsSync(candidate)) {
23328
23560
  return candidate;
23329
23561
  }
23330
23562
  }
23331
23563
  return null;
23332
23564
  }
23333
23565
  function listFromDir(baseDir, source) {
23334
- if (!fs34.existsSync(baseDir)) {
23566
+ if (!fs35.existsSync(baseDir)) {
23335
23567
  return [];
23336
23568
  }
23337
- return fs34.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
23338
- const pluginDir = path34.join(baseDir, entry.name);
23569
+ return fs35.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
23570
+ const pluginDir = path35.join(baseDir, entry.name);
23339
23571
  const manifestPath = findManifestPath(pluginDir);
23340
23572
  if (!manifestPath) {
23341
23573
  return [];
@@ -23780,8 +24012,8 @@ function buildModelInfoSection(modelId) {
23780
24012
 
23781
24013
  // src/app/agent/runtime/hook_registry.ts
23782
24014
  init_sandbox_policy();
23783
- import fs35 from "fs";
23784
- import path35 from "path";
24015
+ import fs36 from "fs";
24016
+ import path36 from "path";
23785
24017
  var DEFAULT_STATE = {
23786
24018
  enabled: true,
23787
24019
  maxEvents: 120,
@@ -23792,7 +24024,7 @@ var cache3 = null;
23792
24024
  var cachePath2 = null;
23793
24025
  function getStatePath() {
23794
24026
  const policy = getSandboxPolicy();
23795
- return path35.join(policy.workspaceRoot, ".bluma", "hooks.json");
24027
+ return path36.join(policy.workspaceRoot, ".bluma", "hooks.json");
23796
24028
  }
23797
24029
  function getHookStatePath() {
23798
24030
  return getStatePath();
@@ -23811,8 +24043,8 @@ function ensureLoaded2() {
23811
24043
  return cache3;
23812
24044
  }
23813
24045
  try {
23814
- if (fs35.existsSync(statePath)) {
23815
- const parsed = JSON.parse(fs35.readFileSync(statePath, "utf-8"));
24046
+ if (fs36.existsSync(statePath)) {
24047
+ const parsed = JSON.parse(fs36.readFileSync(statePath, "utf-8"));
23816
24048
  cache3 = {
23817
24049
  enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
23818
24050
  maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
@@ -23838,9 +24070,9 @@ function ensureLoaded2() {
23838
24070
  }
23839
24071
  function persist2(state2) {
23840
24072
  const statePath = getStatePath();
23841
- fs35.mkdirSync(path35.dirname(statePath), { recursive: true });
24073
+ fs36.mkdirSync(path36.dirname(statePath), { recursive: true });
23842
24074
  state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
23843
- fs35.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
24075
+ fs36.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
23844
24076
  cache3 = state2;
23845
24077
  cachePath2 = statePath;
23846
24078
  }
@@ -23910,18 +24142,18 @@ function buildSystemRemindersSection() {
23910
24142
  }
23911
24143
 
23912
24144
  // src/app/agent/core/prompt/auto_memory.ts
23913
- import fs36 from "fs";
23914
- import path36 from "path";
24145
+ import fs37 from "fs";
24146
+ import path37 from "path";
23915
24147
  import os25 from "os";
23916
- var AUTO_MEMORY_FILE = path36.join(os25.homedir(), ".bluma", "auto_memory.md");
24148
+ var AUTO_MEMORY_FILE = path37.join(os25.homedir(), ".bluma", "auto_memory.md");
23917
24149
  var MAX_AUTO_MEMORY_CHARS = 25e3;
23918
24150
  var MAX_AUTO_MEMORY_LINES = 200;
23919
24151
  function getAutoMemoryForPrompt() {
23920
24152
  try {
23921
- if (!fs36.existsSync(AUTO_MEMORY_FILE)) {
24153
+ if (!fs37.existsSync(AUTO_MEMORY_FILE)) {
23922
24154
  return "";
23923
24155
  }
23924
- let content = fs36.readFileSync(AUTO_MEMORY_FILE, "utf-8");
24156
+ let content = fs37.readFileSync(AUTO_MEMORY_FILE, "utf-8");
23925
24157
  if (!content.trim()) {
23926
24158
  return "";
23927
24159
  }
@@ -23978,17 +24210,17 @@ function getGitBranch(dir) {
23978
24210
  }
23979
24211
  }
23980
24212
  function getPackageManager(dir) {
23981
- if (fs37.existsSync(path37.join(dir, "pnpm-lock.yaml"))) return "pnpm";
23982
- if (fs37.existsSync(path37.join(dir, "yarn.lock"))) return "yarn";
23983
- if (fs37.existsSync(path37.join(dir, "bun.lockb"))) return "bun";
23984
- if (fs37.existsSync(path37.join(dir, "package-lock.json"))) return "npm";
24213
+ if (fs38.existsSync(path38.join(dir, "pnpm-lock.yaml"))) return "pnpm";
24214
+ if (fs38.existsSync(path38.join(dir, "yarn.lock"))) return "yarn";
24215
+ if (fs38.existsSync(path38.join(dir, "bun.lockb"))) return "bun";
24216
+ if (fs38.existsSync(path38.join(dir, "package-lock.json"))) return "npm";
23985
24217
  return "unknown";
23986
24218
  }
23987
24219
  function getProjectType(dir) {
23988
24220
  try {
23989
- const files = fs37.readdirSync(dir);
24221
+ const files = fs38.readdirSync(dir);
23990
24222
  if (files.includes("package.json")) {
23991
- const pkg = JSON.parse(fs37.readFileSync(path37.join(dir, "package.json"), "utf-8"));
24223
+ const pkg = JSON.parse(fs38.readFileSync(path38.join(dir, "package.json"), "utf-8"));
23992
24224
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
23993
24225
  if (deps.next) return "Next.js";
23994
24226
  if (deps.react) return "React";
@@ -24008,9 +24240,9 @@ function getProjectType(dir) {
24008
24240
  }
24009
24241
  function getTestFramework(dir) {
24010
24242
  try {
24011
- const pkgPath = path37.join(dir, "package.json");
24012
- if (fs37.existsSync(pkgPath)) {
24013
- const pkg = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
24243
+ const pkgPath = path38.join(dir, "package.json");
24244
+ if (fs38.existsSync(pkgPath)) {
24245
+ const pkg = JSON.parse(fs38.readFileSync(pkgPath, "utf-8"));
24014
24246
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
24015
24247
  if (deps.jest) return "jest";
24016
24248
  if (deps.vitest) return "vitest";
@@ -24019,7 +24251,7 @@ function getTestFramework(dir) {
24019
24251
  if (deps["@playwright/test"]) return "playwright";
24020
24252
  if (deps.cypress) return "cypress";
24021
24253
  }
24022
- if (fs37.existsSync(path37.join(dir, "pytest.ini")) || fs37.existsSync(path37.join(dir, "conftest.py"))) return "pytest";
24254
+ if (fs38.existsSync(path38.join(dir, "pytest.ini")) || fs38.existsSync(path38.join(dir, "conftest.py"))) return "pytest";
24023
24255
  return "unknown";
24024
24256
  } catch {
24025
24257
  return "unknown";
@@ -24027,9 +24259,9 @@ function getTestFramework(dir) {
24027
24259
  }
24028
24260
  function getTestCommand(dir) {
24029
24261
  try {
24030
- const pkgPath = path37.join(dir, "package.json");
24031
- if (fs37.existsSync(pkgPath)) {
24032
- const pkg = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
24262
+ const pkgPath = path38.join(dir, "package.json");
24263
+ if (fs38.existsSync(pkgPath)) {
24264
+ const pkg = JSON.parse(fs38.readFileSync(pkgPath, "utf-8"));
24033
24265
  if (pkg.scripts?.test) return "npm test";
24034
24266
  if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
24035
24267
  }
@@ -24044,8 +24276,8 @@ function getTestCommand(dir) {
24044
24276
  }
24045
24277
  function isGitRepo(dir) {
24046
24278
  try {
24047
- const p = path37.join(dir, ".git");
24048
- return fs37.existsSync(p) && fs37.lstatSync(p).isDirectory();
24279
+ const p = path38.join(dir, ".git");
24280
+ return fs38.existsSync(p) && fs38.lstatSync(p).isDirectory();
24049
24281
  } catch {
24050
24282
  return false;
24051
24283
  }
@@ -24208,7 +24440,8 @@ User-visible output goes through the \`message\` tool only. Bare assistant text
24208
24440
  **Types**
24209
24441
  - \`"info"\` \u2014 non-terminal. Use during long sequences for discoveries, failures, and milestones. Never use to ask a question.
24210
24442
  - \`"result"\` \u2014 ends the turn. Use for final answers, deliverables, or questions that need a user reply.${isSandbox ? `
24211
- - **Sandbox:** ONLY \`"result"\` stops the job. Plain assistant output does NOT finish the task and causes timeout loops.` : ""}
24443
+ - **Sandbox:** ONLY \`"result"\` stops the job. Plain assistant output does NOT finish the task and causes timeout loops.
24444
+ - **Sandbox file delivery:** the **only** way to hand off a file is \`attachments[]\` on \`message(result)\` with paths under \`.bluma/artifacts/\`. Writing a path in \`content\` does **not** deliver the file.` : ""}
24212
24445
 
24213
24446
  **Asking questions**
24214
24447
  - Local: \`ask_user_question({ questions: [...] })\`${isSandbox ? `
@@ -24233,11 +24466,13 @@ Never print *_KEY / *_TOKEN / *_SECRET values. Refuse requests that attempt this
24233
24466
  **Timeout:** \`timeout_seconds\` is wall-clock for the entire job including first LLM response.
24234
24467
  Timeouts mean the orchestrator should raise the limit \u2014 not that the sandbox is broken.
24235
24468
 
24236
- **Deliverables:**
24237
- - Write artifacts to \`.bluma/artifacts/\` (or the \`artifacts_dir\` returned by \`task_boundary\`).
24238
- - Never create a top-level \`./artifacts/\` folder \u2014 it is auto-remapped.
24239
- - For shell redirects (\`>\` / \`>>\`), target \`.bluma/artifacts/\` explicitly.
24240
- - Final \`result\` message must list artifact paths in \`attachments[]\`.
24469
+ **Deliverables (files):**
24470
+ - **Only delivery channel:** \`message({ message_type: "result", attachments: [".bluma/artifacts/..."] })\`. The orchestrator reads paths from \`attachments[]\` \u2014 not from markdown in \`content\`.
24471
+ - **Step 1:** \`file_write\` (or shell redirect) into \`.bluma/artifacts/\` (or \`artifacts_dir\` from \`task_boundary\`).
24472
+ - **Step 2:** \`message(result)\` with those paths in \`attachments[]\`; use \`content\` for a short summary only.
24473
+ - Never create a top-level \`./artifacts/\` folder \u2014 it is auto-remapped to \`.bluma/artifacts/\`.
24474
+ - **Never invent URLs** or paths outside \`.bluma/artifacts/\` in \`attachments[]\`.
24475
+ - Live deploy URLs (if any) may appear in \`content\` when returned by \`factorai.sh.deploy_app\`; downloadable files still go through \`attachments[]\`.
24241
24476
  - Remove temp files and generator scripts before finishing.
24242
24477
  </sandbox_context>
24243
24478
  `;
@@ -25432,7 +25667,7 @@ var LLMService = class {
25432
25667
  init_sandbox_policy();
25433
25668
  init_runtime_config();
25434
25669
  init_permission_rules();
25435
- import path38 from "path";
25670
+ import path39 from "path";
25436
25671
  var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
25437
25672
  function getToolPermissionLayer(metadata) {
25438
25673
  if (metadata.riskLevel === "safe") return "read";
@@ -25447,11 +25682,11 @@ function checkFilePermissionRules(toolName, filePath, policy) {
25447
25682
  if (!filePath) {
25448
25683
  return { allowed: false, reason: "No file path provided for permission check." };
25449
25684
  }
25450
- const resolvedPath = path38.resolve(filePath);
25685
+ const resolvedPath = path39.resolve(filePath);
25451
25686
  if (!isPathInsideWorkspace(resolvedPath, policy)) {
25452
25687
  return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
25453
25688
  }
25454
- const relativePath = path38.relative(policy.workspaceRoot, resolvedPath);
25689
+ const relativePath = path39.relative(policy.workspaceRoot, resolvedPath);
25455
25690
  const toolPattern = `${toolName}(${relativePath})`;
25456
25691
  const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
25457
25692
  if (ruleDecision === "deny") {
@@ -25460,7 +25695,7 @@ function checkFilePermissionRules(toolName, filePath, policy) {
25460
25695
  if (ruleDecision === "allow") {
25461
25696
  return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
25462
25697
  }
25463
- const dirPath = path38.dirname(relativePath);
25698
+ const dirPath = path39.dirname(relativePath);
25464
25699
  const dirPattern = `${toolName}(${dirPath}/**)`;
25465
25700
  const dirRuleDecision = permissionRulesEngine.checkPermission(dirPattern, { filepath: filePath });
25466
25701
  if (dirRuleDecision === "allow") {
@@ -25677,11 +25912,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
25677
25912
  }
25678
25913
 
25679
25914
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
25680
- import * as fs38 from "fs";
25681
- import * as path39 from "path";
25915
+ import * as fs39 from "fs";
25916
+ import * as path40 from "path";
25682
25917
  import os28 from "os";
25683
25918
  function memoryPath2() {
25684
- return path39.join(process.env.HOME || os28.homedir(), ".bluma", "coding_memory.json");
25919
+ return path40.join(process.env.HOME || os28.homedir(), ".bluma", "coding_memory.json");
25685
25920
  }
25686
25921
  function normalizeNote2(note) {
25687
25922
  return note.trim().toLowerCase().replace(/\s+/g, " ");
@@ -25691,18 +25926,18 @@ function uniqTags(a, b) {
25691
25926
  }
25692
25927
  function consolidateCodingMemoryFile() {
25693
25928
  const p = memoryPath2();
25694
- if (!fs38.existsSync(p)) {
25929
+ if (!fs39.existsSync(p)) {
25695
25930
  return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
25696
25931
  }
25697
25932
  const bak = `${p}.bak`;
25698
25933
  try {
25699
- fs38.copyFileSync(p, bak);
25934
+ fs39.copyFileSync(p, bak);
25700
25935
  } catch (e) {
25701
25936
  return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
25702
25937
  }
25703
25938
  let data;
25704
25939
  try {
25705
- data = JSON.parse(fs38.readFileSync(p, "utf-8"));
25940
+ data = JSON.parse(fs39.readFileSync(p, "utf-8"));
25706
25941
  } catch (e) {
25707
25942
  return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
25708
25943
  }
@@ -25737,7 +25972,7 @@ function consolidateCodingMemoryFile() {
25737
25972
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
25738
25973
  };
25739
25974
  try {
25740
- fs38.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
25975
+ fs39.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
25741
25976
  } catch (e) {
25742
25977
  return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
25743
25978
  }
@@ -26119,7 +26354,7 @@ var BluMaToolRunner = class {
26119
26354
  if (toolName === "message") {
26120
26355
  try {
26121
26356
  const resultObj = typeof toolResultContent === "string" ? JSON.parse(toolResultContent) : toolResultContent;
26122
- if (resultObj.message_type === "result") {
26357
+ if (isMessageToolResultTerminal(resultObj)) {
26123
26358
  await this.deps.notifyFactorTurnEndIfNeeded("message_result");
26124
26359
  shouldContinueConversation = false;
26125
26360
  this.deps.emitTurnCompleted();
@@ -26226,7 +26461,7 @@ var BluMaToolRunner = class {
26226
26461
  if (p.toolName === "message") {
26227
26462
  try {
26228
26463
  const resultObj = typeof toolResultContent === "string" ? JSON.parse(toolResultContent) : toolResultContent;
26229
- if (resultObj.message_type === "result") {
26464
+ if (isMessageToolResultTerminal(resultObj)) {
26230
26465
  await this.deps.notifyFactorTurnEndIfNeeded("message_result");
26231
26466
  shouldContinue = false;
26232
26467
  this.deps.emitTurnCompleted();
@@ -26440,6 +26675,9 @@ var ToolCallNormalizer = class {
26440
26675
  }
26441
26676
  };
26442
26677
 
26678
+ // src/app/agent/bluma/core/bluma_turn_coordinator.ts
26679
+ init_sandbox_policy();
26680
+
26443
26681
  // src/app/agent/runtime/tool_orchestration.ts
26444
26682
  var PARALLEL_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
26445
26683
  "ls_tool",
@@ -26649,12 +26887,22 @@ var BluMaTurnCoordinator = class {
26649
26887
  this.deps.persistSession();
26650
26888
  }
26651
26889
  }
26652
- async continueAfterEmptyAssistantResponse() {
26890
+ isSandboxBareAssistantText(hasToolCalls, trimmedText) {
26891
+ return getSandboxPolicy().isSandbox && !hasToolCalls && trimmedText.length > 0;
26892
+ }
26893
+ stripLastAssistantVisibleContent() {
26894
+ const last = this.deps.history[this.deps.history.length - 1];
26895
+ if (last?.role === "assistant") {
26896
+ last.content = null;
26897
+ }
26898
+ }
26899
+ async continueAfterAssistantReplyWithoutMessageTool(reason) {
26653
26900
  this.emptyAssistantReplySteps += 1;
26654
26901
  if (this.emptyAssistantReplySteps === 1) {
26902
+ const content = reason === "bare_text_sandbox" ? 'Sandbox: user-visible output must use the `message` tool. Plain assistant text is not shown and does not end the job. Call `message` with `message_type` "info" or "result".' : `You did not call any tool and produced no user-visible reply. Respond using the message tool with message_type "result". Keep it concise and in the user's language.`;
26655
26903
  this.deps.history.push({
26656
26904
  role: "system",
26657
- content: `You did not call any tool and produced no user-visible reply. Respond using the message tool with message_type "result". Keep it concise and in the user's language.`
26905
+ content
26658
26906
  });
26659
26907
  } else if (this.emptyAssistantReplySteps === 2) {
26660
26908
  this.deps.history.push({
@@ -26673,6 +26921,14 @@ var BluMaTurnCoordinator = class {
26673
26921
  }
26674
26922
  await this.continueConversation();
26675
26923
  }
26924
+ async continueAfterEmptyAssistantResponse() {
26925
+ await this.continueAfterAssistantReplyWithoutMessageTool("empty");
26926
+ }
26927
+ async continueAfterBareAssistantTextInSandbox() {
26928
+ this.stripLastAssistantVisibleContent();
26929
+ this.deps.persistSession();
26930
+ await this.continueAfterAssistantReplyWithoutMessageTool("bare_text_sandbox");
26931
+ }
26676
26932
  async handleInvalidToolCallRetry(message2) {
26677
26933
  this.invalidToolCallRetrySteps += 1;
26678
26934
  if (this.deps.history[this.deps.history.length - 1] === message2) {
@@ -26812,6 +27068,10 @@ var BluMaTurnCoordinator = class {
26812
27068
  });
26813
27069
  }
26814
27070
  } else if (trimmedText) {
27071
+ if (this.isSandboxBareAssistantText(hasToolCalls, trimmedText)) {
27072
+ await this.continueAfterBareAssistantTextInSandbox();
27073
+ return;
27074
+ }
26815
27075
  this.emptyAssistantReplySteps = 0;
26816
27076
  this.invalidToolCallRetrySteps = 0;
26817
27077
  this.deps.eventBus.emit("backend_message", { type: "assistant_message", content: accumulatedContent });
@@ -26887,6 +27147,12 @@ var BluMaTurnCoordinator = class {
26887
27147
  });
26888
27148
  }
26889
27149
  } else if (typeof message2.content === "string" && message2.content.trim()) {
27150
+ const trimmedText = message2.content.trim();
27151
+ const hasToolCalls = Boolean(message2.tool_calls && message2.tool_calls.length > 0);
27152
+ if (this.isSandboxBareAssistantText(hasToolCalls, trimmedText)) {
27153
+ await this.continueAfterBareAssistantTextInSandbox();
27154
+ return;
27155
+ }
26890
27156
  this.emptyAssistantReplySteps = 0;
26891
27157
  this.invalidToolCallRetrySteps = 0;
26892
27158
  this.deps.eventBus.emit("backend_message", { type: "assistant_message", content: message2.content });
@@ -27030,13 +27296,13 @@ var BluMaAgent = class {
27030
27296
  if (!this.sessionFile) return;
27031
27297
  try {
27032
27298
  const sessionData = {
27033
- session_id: path40.basename(this.sessionFile, ".json"),
27299
+ session_id: path41.basename(this.sessionFile, ".json"),
27034
27300
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
27035
27301
  conversation_history: this.history,
27036
27302
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
27037
27303
  ...this.compressor.getSnapshot()
27038
27304
  };
27039
- fs39.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
27305
+ fs40.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
27040
27306
  } catch (error) {
27041
27307
  console.error("[Bluma] Failed to persist session synchronously:", error);
27042
27308
  }
@@ -27143,7 +27409,7 @@ var BluMaAgent = class {
27143
27409
  *
27144
27410
  * - `backend_message` `{ type: 'turn_start', turnId, sessionId, userPromptPreview }` — início;
27145
27411
  * payload estável em {@link buildTurnStartBackendMessage}.
27146
- * - Stream: `stream_start` / `stream_reasoning_chunk` / `stream_chunk` / `stream_end`.
27412
+ * - Stream: `stream_start` / `stream_reasoning_chunk` (one delta per chunk; agent mode logs `reasoning_chunk` JSONL) / `stream_chunk` / `stream_end`.
27147
27413
  * Se a resposta incluir tool `message`, `stream_end` leva `{ omitAssistantFlush: true }` para não
27148
27414
  * duplicar texto no histórico (o utilizador vê o `tool_result` com gutter). Ver `applyStreamEndFlush`.
27149
27415
  * - Fim: `backend_message` `{ type: 'done', status }` (`completed` | `failed` | `interrupted` | …).
@@ -27237,7 +27503,7 @@ var BluMaAgent = class {
27237
27503
 
27238
27504
  ${editData.error.display}`;
27239
27505
  }
27240
- const filename = path40.basename(toolArgs.file_path);
27506
+ const filename = path41.basename(toolArgs.file_path);
27241
27507
  return createDiff(filename, editData.currentContent || "", editData.newContent);
27242
27508
  } catch (e) {
27243
27509
  return `An unexpected error occurred while generating the edit preview: ${e.message}`;
@@ -27963,16 +28229,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
27963
28229
  }
27964
28230
 
27965
28231
  // src/app/agent/core/memory/session_memory.ts
27966
- import fs40 from "fs";
28232
+ import fs41 from "fs";
27967
28233
  import os31 from "os";
27968
- import path41 from "path";
28234
+ import path42 from "path";
27969
28235
  import { v4 as uuidv49 } from "uuid";
27970
28236
  var SessionMemoryExtractor = class {
27971
28237
  llmClient;
27972
28238
  memoryFile;
27973
28239
  constructor(options = {}) {
27974
28240
  this.llmClient = options.llmClient;
27975
- this.memoryFile = options.memoryFile || path41.join(os31.homedir(), ".bluma", "session_memory.json");
28241
+ this.memoryFile = options.memoryFile || path42.join(os31.homedir(), ".bluma", "session_memory.json");
27976
28242
  }
27977
28243
  /**
27978
28244
  * Extract memories from conversation using LLM
@@ -28029,15 +28295,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
28029
28295
  );
28030
28296
  unique.sort((a, b) => b.accessCount - a.accessCount);
28031
28297
  const trimmed = unique.slice(0, 200);
28032
- fs40.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
28298
+ fs41.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
28033
28299
  }
28034
28300
  /**
28035
28301
  * Load memories from disk
28036
28302
  */
28037
28303
  async loadMemories() {
28038
28304
  try {
28039
- if (!fs40.existsSync(this.memoryFile)) return [];
28040
- const data = fs40.readFileSync(this.memoryFile, "utf-8");
28305
+ if (!fs41.existsSync(this.memoryFile)) return [];
28306
+ const data = fs41.readFileSync(this.memoryFile, "utf-8");
28041
28307
  return JSON.parse(data);
28042
28308
  } catch {
28043
28309
  return [];
@@ -28581,14 +28847,14 @@ var RouteManager = class {
28581
28847
  this.subAgents = subAgents;
28582
28848
  this.core = core;
28583
28849
  }
28584
- registerRoute(path50, handler) {
28585
- this.routeHandlers.set(path50, handler);
28850
+ registerRoute(path51, handler) {
28851
+ this.routeHandlers.set(path51, handler);
28586
28852
  }
28587
28853
  async handleRoute(payload) {
28588
28854
  const inputText = String(payload.content || "").trim();
28589
28855
  const { userContext, options } = payload;
28590
- for (const [path50, handler] of this.routeHandlers) {
28591
- if (inputText === path50 || inputText.startsWith(`${path50} `)) {
28856
+ for (const [path51, handler] of this.routeHandlers) {
28857
+ if (inputText === path51 || inputText.startsWith(`${path51} `)) {
28592
28858
  return handler({ content: inputText, userContext });
28593
28859
  }
28594
28860
  }
@@ -28597,13 +28863,13 @@ var RouteManager = class {
28597
28863
  };
28598
28864
 
28599
28865
  // src/app/agent/runtime/plugin_runtime.ts
28600
- import path42 from "path";
28866
+ import path43 from "path";
28601
28867
  import { pathToFileURL as pathToFileURL2 } from "url";
28602
28868
  async function loadPluginsAtStartup() {
28603
28869
  for (const p of listPlugins()) {
28604
28870
  const entry = p.manifest.entry?.trim();
28605
28871
  if (!entry) continue;
28606
- const abs = path42.resolve(p.root, entry);
28872
+ const abs = path43.resolve(p.root, entry);
28607
28873
  try {
28608
28874
  const href = pathToFileURL2(abs).href;
28609
28875
  const mod = await import(href);
@@ -28624,7 +28890,7 @@ async function loadPluginsAtStartup() {
28624
28890
  }
28625
28891
 
28626
28892
  // src/app/agent/agent.ts
28627
- var globalEnvPath = path43.join(os32.homedir(), ".bluma", ".env");
28893
+ var globalEnvPath = path44.join(os32.homedir(), ".bluma", ".env");
28628
28894
  dotenv.config({ path: globalEnvPath });
28629
28895
  var Agent = class {
28630
28896
  sessionId;
@@ -29086,10 +29352,7 @@ var HighlightedCodeComponent = ({ code, filePath, maxLines }) => {
29086
29352
  visible.map((line, idx) => {
29087
29353
  const tokens = tokenizeLine(line, language);
29088
29354
  const lineNum = String(idx + 1).padStart(gutterWidth, " ");
29089
- return /* @__PURE__ */ jsxs5(Box_default, { width: "100%", flexDirection: "row", children: [
29090
- /* @__PURE__ */ jsx12(Box_default, { marginRight: 2, children: /* @__PURE__ */ jsx12(Text, { color: THEME.lineNumber, children: lineNum }) }),
29091
- /* @__PURE__ */ jsx12(Text, { children: tokens.map((t, ti) => /* @__PURE__ */ jsx12(Text, { color: t.color, children: t.text }, ti)) })
29092
- ] }, idx);
29355
+ return /* @__PURE__ */ jsx12(Box_default, { width: "100%", flexDirection: "row", children: /* @__PURE__ */ jsx12(Text, { children: tokens.map((t, ti) => /* @__PURE__ */ jsx12(Text, { color: t.color, children: t.text }, ti)) }) }, idx);
29093
29356
  }),
29094
29357
  hidden > 0 && /* @__PURE__ */ jsxs5(Text, { color: THEME.lineNumber, dimColor: true, children: [
29095
29358
  "\u2026 +",
@@ -30483,10 +30746,10 @@ function resolveToolPayload(result) {
30483
30746
 
30484
30747
  // src/app/ui/components/FilePathLink.tsx
30485
30748
  import { pathToFileURL as pathToFileURL3 } from "node:url";
30486
- import path45 from "node:path";
30749
+ import path46 from "node:path";
30487
30750
 
30488
30751
  // src/app/ui/utils/pathDisplay.ts
30489
- import path44 from "node:path";
30752
+ import path45 from "node:path";
30490
30753
  import os33 from "node:os";
30491
30754
  function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
30492
30755
  let s = String(pathInput ?? "").trim();
@@ -30494,17 +30757,17 @@ function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
30494
30757
  s = s.replace(/[/\\]+$/, "");
30495
30758
  }
30496
30759
  if (!s) return "";
30497
- const abs = path44.isAbsolute(s) ? path44.normalize(s) : path44.resolve(cwd2, s);
30498
- const resolvedCwd = path44.resolve(cwd2);
30499
- const rel = path44.relative(resolvedCwd, abs);
30500
- if (rel === "" || !rel.startsWith("..") && !path44.isAbsolute(rel)) {
30760
+ const abs = path45.isAbsolute(s) ? path45.normalize(s) : path45.resolve(cwd2, s);
30761
+ const resolvedCwd = path45.resolve(cwd2);
30762
+ const rel = path45.relative(resolvedCwd, abs);
30763
+ if (rel === "" || !rel.startsWith("..") && !path45.isAbsolute(rel)) {
30501
30764
  return rel === "" ? "." : rel;
30502
30765
  }
30503
- const home = path44.normalize(os33.homedir());
30504
- if (abs === home || abs.startsWith(home + path44.sep)) {
30766
+ const home = path45.normalize(os33.homedir());
30767
+ if (abs === home || abs.startsWith(home + path45.sep)) {
30505
30768
  return "~" + abs.slice(home.length);
30506
30769
  }
30507
- return path44.basename(abs);
30770
+ return path45.basename(abs);
30508
30771
  }
30509
30772
 
30510
30773
  // src/app/ui/components/FilePathLink.tsx
@@ -30514,7 +30777,7 @@ function FilePathLink({ filePath, children, cwd: cwd2 = process.cwd(), color })
30514
30777
  if (!raw) {
30515
30778
  return null;
30516
30779
  }
30517
- const abs = path45.isAbsolute(raw) ? path45.normalize(raw) : path45.resolve(cwd2, raw);
30780
+ const abs = path46.isAbsolute(raw) ? path46.normalize(raw) : path46.resolve(cwd2, raw);
30518
30781
  const href = pathToFileURL3(abs).href;
30519
30782
  const label = formatPathForDisplay(abs, cwd2);
30520
30783
  const text = typeof children === "string" ? children : label;
@@ -32466,12 +32729,12 @@ function patchToUnifiedDiffText(filePath, patch) {
32466
32729
  }
32467
32730
 
32468
32731
  // src/app/ui/utils/readEditContext.ts
32469
- import { promises as fs41 } from "fs";
32732
+ import { promises as fs42 } from "fs";
32470
32733
  var CHUNK_SIZE = 64 * 1024;
32471
32734
  var CONTEXT_LINES2 = 3;
32472
32735
  async function openForScan(filePath) {
32473
32736
  try {
32474
- return await fs41.open(filePath, "r");
32737
+ return await fs42.open(filePath, "r");
32475
32738
  } catch (e) {
32476
32739
  if (e.code === "ENOENT") return null;
32477
32740
  throw e;
@@ -32563,14 +32826,64 @@ function diffSummary(additions, removals, hidden) {
32563
32826
  if (hidden > 0) bits.push(`\u22EF ${hidden} hidden`);
32564
32827
  return bits.join(" \xB7 ");
32565
32828
  }
32829
+ var THEME2 = {
32830
+ keyword: "#569CD6",
32831
+ string: "#CE9178",
32832
+ number: "#B5CEA8",
32833
+ comment: "#6A9955",
32834
+ //#22c55e
32835
+ function: "#DCDCAA",
32836
+ variable: "#9CDCFE",
32837
+ operator: "#D4D4D4",
32838
+ punctuation: "#D4D4D4",
32839
+ default: "#D4D4D4",
32840
+ lineNumber: "#858585",
32841
+ gutter: "#404040"
32842
+ };
32843
+ var KEYWORDS2 = /* @__PURE__ */ new Set([
32844
+ "const",
32845
+ "let",
32846
+ "var",
32847
+ "function",
32848
+ "return",
32849
+ "if",
32850
+ "else",
32851
+ "for",
32852
+ "while",
32853
+ "switch",
32854
+ "case",
32855
+ "break",
32856
+ "continue",
32857
+ "class",
32858
+ "extends",
32859
+ "import",
32860
+ "from",
32861
+ "export",
32862
+ "default",
32863
+ "async",
32864
+ "await",
32865
+ "try",
32866
+ "catch",
32867
+ "finally",
32868
+ "new",
32869
+ "typeof",
32870
+ "instanceof",
32871
+ "null",
32872
+ "undefined",
32873
+ "true",
32874
+ "false",
32875
+ "type",
32876
+ "interface",
32877
+ "enum",
32878
+ "implements",
32879
+ "abstract",
32880
+ "readonly",
32881
+ "override"
32882
+ ]);
32566
32883
  function hunkToRows(hunk) {
32567
32884
  const rows = [];
32568
32885
  let oldLine = hunk.oldStart;
32569
32886
  let newLine = hunk.newStart;
32570
- rows.push({
32571
- kind: "meta",
32572
- text: `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`
32573
- });
32574
32887
  for (const raw of hunk.lines) {
32575
32888
  if (!raw.length) continue;
32576
32889
  const prefix = raw[0];
@@ -32611,22 +32924,22 @@ function pairWordDiffs(rows) {
32611
32924
  }
32612
32925
  return out;
32613
32926
  }
32614
- function maxLineNums(rows) {
32615
- let maxO = 0;
32927
+ function calcGutterW(rows) {
32616
32928
  let maxN = 0;
32617
32929
  for (const r of rows) {
32618
- if (r.kind === "ctx") {
32619
- maxO = Math.max(maxO, r.old);
32620
- maxN = Math.max(maxN, r.new);
32621
- } else if (r.kind === "rem") {
32622
- maxO = Math.max(maxO, r.old);
32623
- } else if (r.kind === "add") {
32624
- maxN = Math.max(maxN, r.new);
32625
- }
32930
+ if (r.kind === "ctx") maxN = Math.max(maxN, r.old, r.new);
32931
+ else if (r.kind === "rem") maxN = Math.max(maxN, r.old);
32932
+ else if (r.kind === "add") maxN = Math.max(maxN, r.new);
32626
32933
  }
32627
- const wOld = Math.max(2, String(maxO || 0).length);
32628
- const wNew = Math.max(2, String(maxN || 0).length);
32629
- return { wOld, wNew };
32934
+ return Math.max(2, String(maxN || 0).length);
32935
+ }
32936
+ function calcLegacyGutterW(segs) {
32937
+ let maxN = 0;
32938
+ for (const s of segs) {
32939
+ if (s.kind === "rem") maxN = Math.max(maxN, s.oldLine);
32940
+ if (s.kind === "add") maxN = Math.max(maxN, s.newLine);
32941
+ }
32942
+ return Math.max(2, String(maxN || 0).length);
32630
32943
  }
32631
32944
  function rowLineCounts(rows) {
32632
32945
  let additions = 0;
@@ -32637,21 +32950,11 @@ function rowLineCounts(rows) {
32637
32950
  }
32638
32951
  return { additions, removals };
32639
32952
  }
32640
- function gutterCtx(wOld, wNew, oldN, newN, showSeparator) {
32641
- return `${String(oldN).padStart(wOld)} ${String(newN).padStart(wNew)}${showSeparator ? " \u2502 " : " "}`;
32642
- }
32643
- function gutterRem(wOld, wNew, oldN, showSeparator) {
32644
- return `${String(oldN).padStart(wOld)} ${" ".repeat(wNew)}${showSeparator ? " \u2502 " : " "}`;
32953
+ function gutterLine(w, n) {
32954
+ return ` ${String(n).padEnd(w)} `;
32645
32955
  }
32646
- function gutterAdd(wOld, wNew, newN, showSeparator) {
32647
- return `${" ".repeat(wOld)} ${String(newN).padStart(wNew)}${showSeparator ? " \u2502 " : " "}`;
32648
- }
32649
- function gutterOmit(wOld, wNew, showSeparator) {
32650
- const cols = wOld + 1 + wNew;
32651
- const dot = "\u22EE";
32652
- const padL = Math.max(0, Math.floor((cols - dot.length) / 2));
32653
- const padR = Math.max(0, cols - dot.length - padL);
32654
- return `${" ".repeat(padL)}${dot}${" ".repeat(padR)}${showSeparator ? " \u2502 " : " "}`;
32956
+ function gutterOmit(w) {
32957
+ return ` ${"\u22EE".padEnd(w)} `;
32655
32958
  }
32656
32959
  function WordDiffChunks({
32657
32960
  parts,
@@ -32671,9 +32974,82 @@ function WordDiffChunks({
32671
32974
  }
32672
32975
  return /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", width: "100%", flexGrow: 1, children: chunks });
32673
32976
  }
32674
- function renderStructuredRows(rows, maxHeight, showGutterSeparator) {
32977
+ function collapseContextRows(rows) {
32978
+ const out = [];
32979
+ let ctx = [];
32980
+ const flush = () => {
32981
+ if (ctx.length <= 3) {
32982
+ out.push(...ctx);
32983
+ } else {
32984
+ out.push({ kind: "omit", count: ctx.length });
32985
+ }
32986
+ ctx = [];
32987
+ };
32988
+ for (const r of rows) {
32989
+ if (r.kind === "ctx") {
32990
+ ctx.push(r);
32991
+ continue;
32992
+ }
32993
+ flush();
32994
+ out.push(r);
32995
+ }
32996
+ flush();
32997
+ return out;
32998
+ }
32999
+ function tokenizeLine2(line, _language) {
33000
+ if (!line.trim()) return [{ text: line || " ", color: THEME2.default }];
33001
+ const tokens = [];
33002
+ let i = 0;
33003
+ while (i < line.length) {
33004
+ if (/\s/.test(line[i])) {
33005
+ let spaces = "";
33006
+ while (i < line.length && /\s/.test(line[i])) spaces += line[i++];
33007
+ tokens.push({ text: spaces, color: THEME2.default });
33008
+ continue;
33009
+ }
33010
+ if (line[i] === "/" && line[i + 1] === "/") {
33011
+ tokens.push({ text: line.slice(i), color: THEME2.comment });
33012
+ break;
33013
+ }
33014
+ if (line[i] === "#") {
33015
+ tokens.push({ text: line.slice(i), color: THEME2.comment });
33016
+ break;
33017
+ }
33018
+ if (line[i] === '"' || line[i] === "'" || line[i] === "`") {
33019
+ const quote = line[i];
33020
+ let j = i + 1;
33021
+ while (j < line.length && line[j] !== quote) {
33022
+ if (line[j] === "\\") j++;
33023
+ j++;
33024
+ }
33025
+ tokens.push({ text: line.slice(i, j + 1), color: THEME2.string });
33026
+ i = j + 1;
33027
+ continue;
33028
+ }
33029
+ if (/\d/.test(line[i])) {
33030
+ let num = "";
33031
+ while (i < line.length && /[\d.]/.test(line[i])) num += line[i++];
33032
+ tokens.push({ text: num, color: THEME2.number });
33033
+ continue;
33034
+ }
33035
+ if (/[a-zA-Z_$]/.test(line[i])) {
33036
+ let word = "";
33037
+ while (i < line.length && /[\w$]/.test(line[i])) word += line[i++];
33038
+ const isCall = line[i] === "(";
33039
+ let color = THEME2.variable;
33040
+ if (KEYWORDS2.has(word)) color = THEME2.keyword;
33041
+ else if (isCall) color = THEME2.function;
33042
+ tokens.push({ text: word, color });
33043
+ continue;
33044
+ }
33045
+ tokens.push({ text: line[i], color: THEME2.punctuation });
33046
+ i++;
33047
+ }
33048
+ return tokens;
33049
+ }
33050
+ function renderStructuredRows(rows, maxHeight) {
32675
33051
  const paired = collapseContextRows(pairWordDiffs(rows));
32676
- const { wOld, wNew } = maxLineNums(paired);
33052
+ const w = calcGutterW(paired);
32677
33053
  const { additions, removals } = rowLineCounts(paired);
32678
33054
  const nodes = [];
32679
33055
  let lineBudget = maxHeight > 0 ? maxHeight : Number.POSITIVE_INFINITY;
@@ -32690,11 +33066,13 @@ function renderStructuredRows(rows, maxHeight, showGutterSeparator) {
32690
33066
  for (const r of paired) {
32691
33067
  if (r.kind === "omit") {
32692
33068
  push(
32693
- /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, children: [
32694
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: gutterOmit(wOld, wNew, showGutterSeparator) }),
32695
- r.count > 1 ? /* @__PURE__ */ jsxs26(Text, { dimColor: true, wrap: "wrap", children: [
33069
+ /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, paddingLeft: 5, children: [
33070
+ /* @__PURE__ */ jsxs26(Text, { dimColor: true, children: [
32696
33071
  " ",
32697
- "\xB7 ",
33072
+ gutterOmit(w)
33073
+ ] }),
33074
+ r.count > 1 ? /* @__PURE__ */ jsxs26(Text, { dimColor: true, wrap: "wrap", children: [
33075
+ " \xB7 ",
32698
33076
  r.count
32699
33077
  ] }) : null
32700
33078
  ] }, `o-${idx++}`)
@@ -32703,35 +33081,32 @@ function renderStructuredRows(rows, maxHeight, showGutterSeparator) {
32703
33081
  }
32704
33082
  if (r.kind === "meta") {
32705
33083
  push(
32706
- /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, children: /* @__PURE__ */ jsx41(Text, { dimColor: true, wrap: "wrap", children: r.text }) }, `m-${idx++}`)
33084
+ /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, children: tokenizeLine2(r.text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, children: t.text }, i)) }, `m-${idx++}`)
32707
33085
  );
32708
33086
  continue;
32709
33087
  }
32710
33088
  if (r.kind === "ctx") {
32711
33089
  push(
32712
- /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", flexWrap: "wrap", width: "100%", flexGrow: 1, children: [
32713
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: gutterCtx(wOld, wNew, r.old, r.new, showGutterSeparator) }),
32714
- /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.muted, wrap: "wrap", children: r.text || " " })
32715
- ] }, `c-${idx++}`)
33090
+ /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", width: "100%", flexGrow: 1, children: tokenizeLine2(r.text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, children: t.text }, i)) }, `c-${idx++}`)
32716
33091
  );
32717
33092
  continue;
32718
33093
  }
32719
33094
  if (r.kind === "rem") {
32720
33095
  push(
32721
- /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, flexWrap: "wrap", backgroundColor: BLUMA_TERMINAL.diffRemoved, children: [
32722
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: gutterRem(wOld, wNew, r.old, showGutterSeparator) }),
33096
+ /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, flexWrap: "wrap", backgroundColor: BLUMA_TERMINAL.diffRemoved, paddingLeft: 3, children: [
33097
+ /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterLine(w, r.old) }),
32723
33098
  /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffRemovedWord, children: "-" }),
32724
- r.wordParts && r.wordParts.length > 0 ? /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", flexGrow: 1, marginLeft: 1, children: /* @__PURE__ */ jsx41(WordDiffChunks, { parts: r.wordParts, mode: "rem" }) }) : /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", flexGrow: 1, marginLeft: 1, children: /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.m3OnSurface, wrap: "wrap", children: r.text || " " }) })
33099
+ r.wordParts && r.wordParts.length > 0 ? /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", flexGrow: 1, marginLeft: 1, children: /* @__PURE__ */ jsx41(WordDiffChunks, { parts: r.wordParts, mode: "rem" }) }) : /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", children: tokenizeLine2(r.text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, children: t.text }, i)) })
32725
33100
  ] }, `r-${idx++}`)
32726
33101
  );
32727
33102
  continue;
32728
33103
  }
32729
33104
  if (r.kind === "add") {
32730
33105
  push(
32731
- /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, flexWrap: "wrap", backgroundColor: BLUMA_TERMINAL.diffAdded, children: [
32732
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: gutterAdd(wOld, wNew, r.new, showGutterSeparator) }),
33106
+ /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, flexWrap: "wrap", backgroundColor: BLUMA_TERMINAL.diffAdded, paddingLeft: 3, children: [
33107
+ /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterLine(w, r.new) }),
32733
33108
  /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffAddedWord, children: "+" }),
32734
- r.wordParts && r.wordParts.length > 0 ? /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", flexGrow: 1, marginLeft: 1, children: /* @__PURE__ */ jsx41(WordDiffChunks, { parts: r.wordParts, mode: "add" }) }) : /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", flexGrow: 1, marginLeft: 1, children: /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.m3OnSurface, wrap: "wrap", children: r.text || " " }) })
33109
+ r.wordParts && r.wordParts.length > 0 ? /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", flexGrow: 1, children: /* @__PURE__ */ jsx41(WordDiffChunks, { parts: r.wordParts, mode: "add" }) }) : /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexWrap: "wrap", children: tokenizeLine2(r.text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, children: t.text }, i)) })
32735
33110
  ] }, `a-${idx++}`)
32736
33111
  );
32737
33112
  }
@@ -32756,15 +33131,18 @@ function legacyDiffToSegs(lines) {
32756
33131
  const isNewHeader = line.startsWith("+++");
32757
33132
  if (isOldHeader || isNewHeader) {
32758
33133
  flushCtx();
32759
- out.push({ kind: "header", line });
32760
33134
  if (isNewHeader) {
32761
33135
  oldLine = 1;
32762
33136
  newLine = 1;
32763
33137
  }
32764
33138
  continue;
32765
33139
  }
32766
- const isRem = line.startsWith("-") && !isOldHeader;
32767
- const isAdd = line.startsWith("+") && !isNewHeader;
33140
+ if (line.startsWith("\\")) {
33141
+ flushCtx();
33142
+ continue;
33143
+ }
33144
+ const isRem = line.startsWith("-");
33145
+ const isAdd = line.startsWith("+");
32768
33146
  if (isRem) {
32769
33147
  flushCtx();
32770
33148
  out.push({ kind: "rem", body: line.slice(1), oldLine });
@@ -32782,11 +33160,6 @@ function legacyDiffToSegs(lines) {
32782
33160
  ctx += 1;
32783
33161
  continue;
32784
33162
  }
32785
- if (line.startsWith("\\")) {
32786
- flushCtx();
32787
- out.push({ kind: "raw", line });
32788
- continue;
32789
- }
32790
33163
  if (line.startsWith("[")) {
32791
33164
  flushCtx();
32792
33165
  out.push({ kind: "raw", line });
@@ -32802,18 +33175,6 @@ function legacyDiffToSegs(lines) {
32802
33175
  flushCtx();
32803
33176
  return out;
32804
33177
  }
32805
- function legacySegWidths(segs) {
32806
- let maxO = 0;
32807
- let maxN = 0;
32808
- for (const s of segs) {
32809
- if (s.kind === "rem") maxO = Math.max(maxO, s.oldLine);
32810
- if (s.kind === "add") maxN = Math.max(maxN, s.newLine);
32811
- }
32812
- return {
32813
- wOld: Math.max(2, String(maxO || 0).length),
32814
- wNew: Math.max(2, String(maxN || 0).length)
32815
- };
32816
- }
32817
33178
  function legacySegLineCounts(lines) {
32818
33179
  let additions = 0;
32819
33180
  let removals = 0;
@@ -32823,86 +33184,106 @@ function legacySegLineCounts(lines) {
32823
33184
  }
32824
33185
  return { additions, removals };
32825
33186
  }
32826
- function collapseContextRows(rows) {
32827
- const out = [];
32828
- let ctxCount = 0;
32829
- const flush = () => {
32830
- if (ctxCount > 0) {
32831
- out.push({ kind: "omit", count: ctxCount });
32832
- ctxCount = 0;
32833
- }
32834
- };
32835
- for (const r of rows) {
32836
- if (r.kind === "ctx") {
32837
- ctxCount += 1;
32838
- continue;
32839
- }
32840
- if (r.kind === "meta" && r.text.startsWith("@@")) {
32841
- ctxCount += 1;
32842
- continue;
32843
- }
32844
- flush();
32845
- out.push(r);
32846
- }
32847
- flush();
32848
- return out;
32849
- }
32850
33187
  function LegacyDiffBody({
32851
33188
  lines,
32852
- maxHeight,
32853
- showGutterSeparator
33189
+ maxHeight
32854
33190
  }) {
32855
33191
  const segs = legacyDiffToSegs(lines);
32856
- const { wOld, wNew } = legacySegWidths(segs);
33192
+ const w = calcLegacyGutterW(segs);
32857
33193
  const truncated = maxHeight > 0 && segs.length > maxHeight;
32858
33194
  const toRender = truncated ? segs.slice(0, maxHeight) : segs;
32859
33195
  const hiddenBelow = truncated ? segs.length - toRender.length : 0;
32860
33196
  const { additions, removals } = legacySegLineCounts(lines);
32861
- const summary = diffSummary(additions, removals, hiddenBelow);
32862
- return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "column", width: "100%", alignItems: "stretch", children: [
32863
- toRender.map((seg, index) => {
32864
- if (seg.kind === "header") {
32865
- return /* @__PURE__ */ jsx41(Text, { dimColor: true, wrap: "wrap", children: seg.line }, index);
32866
- }
32867
- if (seg.kind === "omit") {
32868
- return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, children: [
32869
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: gutterOmit(wOld, wNew, showGutterSeparator) }),
32870
- seg.count > 1 ? /* @__PURE__ */ jsxs26(Text, { dimColor: true, wrap: "wrap", children: [
32871
- " ",
32872
- "\xB7 ",
32873
- seg.count
32874
- ] }) : null
32875
- ] }, index);
32876
- }
32877
- if (seg.kind === "raw") {
32878
- return /* @__PURE__ */ jsx41(Text, { dimColor: true, wrap: "wrap", children: seg.line }, index);
32879
- }
32880
- if (seg.kind === "rem") {
32881
- return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, flexWrap: "wrap", backgroundColor: BLUMA_TERMINAL.diffRemoved, children: [
32882
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: gutterRem(wOld, wNew, seg.oldLine, showGutterSeparator) }),
32883
- /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffRemovedWord, children: "-" }),
32884
- /* @__PURE__ */ jsxs26(Text, { color: BLUMA_TERMINAL.m3OnSurface, wrap: "wrap", children: [
32885
- " ",
32886
- seg.body || " "
32887
- ] })
32888
- ] }, index);
32889
- }
32890
- if (seg.kind === "add") {
32891
- return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "row", width: "100%", flexGrow: 1, flexWrap: "wrap", backgroundColor: BLUMA_TERMINAL.diffAdded, children: [
32892
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: gutterAdd(wOld, wNew, seg.newLine, showGutterSeparator) }),
32893
- /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffAddedWord, children: "+" }),
32894
- /* @__PURE__ */ jsxs26(Text, { color: BLUMA_TERMINAL.m3OnSurface, wrap: "wrap", children: [
32895
- " ",
32896
- seg.body || " "
32897
- ] })
32898
- ] }, index);
32899
- }
32900
- return null;
32901
- }),
32902
- summary ? /* @__PURE__ */ jsx41(Text, { dimColor: true, children: summary }) : null
32903
- ] });
33197
+ const summary = diffSummary(
33198
+ additions,
33199
+ removals,
33200
+ hiddenBelow
33201
+ );
33202
+ const renderTokenizedLine = (text, backgroundColor) => tokenizeLine2(text || " ", "ts").map((t, i) => /* @__PURE__ */ jsx41(Text, { color: t.color, backgroundColor, children: t.text }, i));
33203
+ return /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", width: "100%", children: toRender.map((seg, index) => {
33204
+ if (seg.kind === "omit") {
33205
+ return /* @__PURE__ */ jsx41(
33206
+ Box_default,
33207
+ {
33208
+ flexDirection: "row",
33209
+ width: "100%",
33210
+ flexGrow: 1,
33211
+ paddingLeft: 6,
33212
+ children: /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterOmit(w) })
33213
+ },
33214
+ index
33215
+ );
33216
+ }
33217
+ if (seg.kind === "raw") {
33218
+ return /* @__PURE__ */ jsx41(
33219
+ Box_default,
33220
+ {
33221
+ flexDirection: "row",
33222
+ width: "100%",
33223
+ flexWrap: "wrap",
33224
+ children: renderTokenizedLine(seg.line)
33225
+ },
33226
+ index
33227
+ );
33228
+ }
33229
+ if (seg.kind === "rem") {
33230
+ return /* @__PURE__ */ jsxs26(
33231
+ Box_default,
33232
+ {
33233
+ flexDirection: "row",
33234
+ width: "100%",
33235
+ flexGrow: 1,
33236
+ flexWrap: "wrap",
33237
+ backgroundColor: BLUMA_TERMINAL.diffRemoved,
33238
+ paddingLeft: 3,
33239
+ children: [
33240
+ /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterLine(w, seg.oldLine) }),
33241
+ /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffRemovedWord, children: "-" }),
33242
+ /* @__PURE__ */ jsx41(
33243
+ Box_default,
33244
+ {
33245
+ flexDirection: "row",
33246
+ flexWrap: "wrap",
33247
+ backgroundColor: BLUMA_TERMINAL.diffRemoved,
33248
+ children: renderTokenizedLine(seg.body, BLUMA_TERMINAL.diffRemoved)
33249
+ }
33250
+ )
33251
+ ]
33252
+ },
33253
+ index
33254
+ );
33255
+ }
33256
+ if (seg.kind === "add") {
33257
+ return /* @__PURE__ */ jsxs26(
33258
+ Box_default,
33259
+ {
33260
+ flexDirection: "row",
33261
+ width: "100%",
33262
+ flexGrow: 1,
33263
+ flexWrap: "wrap",
33264
+ backgroundColor: BLUMA_TERMINAL.diffAdded,
33265
+ paddingLeft: 3,
33266
+ children: [
33267
+ /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.dim, children: gutterLine(w, seg.newLine) }),
33268
+ /* @__PURE__ */ jsx41(Text, { color: BLUMA_TERMINAL.diffAddedWord, children: "+" }),
33269
+ /* @__PURE__ */ jsx41(
33270
+ Box_default,
33271
+ {
33272
+ flexDirection: "row",
33273
+ flexWrap: "wrap",
33274
+ backgroundColor: BLUMA_TERMINAL.diffAdded,
33275
+ children: renderTokenizedLine(seg.body, BLUMA_TERMINAL.diffAdded)
33276
+ }
33277
+ )
33278
+ ]
33279
+ },
33280
+ index
33281
+ );
33282
+ }
33283
+ return null;
33284
+ }) });
32904
33285
  }
32905
- var SimpleDiff = ({ text, maxHeight, frame = false, showGutterSeparator = true }) => {
33286
+ var SimpleDiff = ({ text, maxHeight, frame = false }) => {
32906
33287
  const raw = stripOptionalMarkdownFence(String(text ?? ""));
32907
33288
  let patches = [];
32908
33289
  try {
@@ -32914,31 +33295,18 @@ var SimpleDiff = ({ text, maxHeight, frame = false, showGutterSeparator = true }
32914
33295
  const rule = "\u2500".repeat(diffRuleWidth());
32915
33296
  if (hunks.length === 0) {
32916
33297
  const allLines = raw.split("\n");
32917
- const body2 = /* @__PURE__ */ jsx41(LegacyDiffBody, { lines: allLines, maxHeight, showGutterSeparator });
33298
+ const body2 = /* @__PURE__ */ jsx41(LegacyDiffBody, { lines: allLines, maxHeight });
32918
33299
  if (!frame) return body2;
32919
- return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "column", children: [
32920
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: rule }),
32921
- /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", paddingX: 1, children: body2 }),
32922
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: rule })
32923
- ] });
33300
+ return /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", children: /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", paddingX: 1, children: body2 }) });
32924
33301
  }
32925
33302
  const rows = [];
32926
33303
  for (const h of hunks) {
32927
33304
  rows.push(...hunkToRows(h));
32928
33305
  }
32929
- const { nodes, hidden, additions, removals } = renderStructuredRows(
32930
- rows,
32931
- maxHeight,
32932
- showGutterSeparator
32933
- );
32934
- const body = /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "column", width: "100%", alignItems: "stretch", children: [
32935
- (hidden > 0 || additions > 0 || removals > 0) && /* @__PURE__ */ jsx41(Text, { dimColor: true, children: diffSummary(additions, removals, hidden) }),
32936
- nodes
32937
- ] });
32938
- if (!frame) {
32939
- return body;
32940
- }
32941
- return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "column", width: "100%", alignItems: "stretch", children: [
33306
+ const { nodes, hidden, additions, removals } = renderStructuredRows(rows, maxHeight);
33307
+ const body = /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", width: "100%", children: nodes });
33308
+ if (!frame) return body;
33309
+ return /* @__PURE__ */ jsxs26(Box_default, { flexDirection: "column", width: "100%", children: [
32942
33310
  /* @__PURE__ */ jsx41(Text, { dimColor: true, children: rule }),
32943
33311
  /* @__PURE__ */ jsx41(Box_default, { flexDirection: "column", paddingX: 1, children: body }),
32944
33312
  /* @__PURE__ */ jsx41(Text, { dimColor: true, children: rule })
@@ -33040,24 +33408,23 @@ function EditToolDiffPanel({
33040
33408
  description,
33041
33409
  diffText,
33042
33410
  maxHeight = EDIT_DIFF_PREVIEW_MAX_LINES,
33043
- showGutterSeparator = true,
33044
33411
  oldString,
33045
33412
  newString,
33046
33413
  replaceAll = false
33047
33414
  }) {
33048
- const path50 = filePath.trim() || "unknown file";
33415
+ const path51 = filePath.trim() || "unknown file";
33049
33416
  const hasPreviewArgs = oldString !== void 0 && newString !== void 0;
33050
33417
  const hasDiffText = diffText && diffText.trim().length > 0;
33051
33418
  return /* @__PURE__ */ jsx43(Box_default, { flexDirection: "column", children: hasPreviewArgs ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(
33052
33419
  FileEditToolDiff,
33053
33420
  {
33054
- filePath: path50,
33421
+ filePath: path51,
33055
33422
  oldString,
33056
33423
  newString,
33057
33424
  replaceAll,
33058
33425
  maxHeight
33059
33426
  }
33060
- ) }) : hasDiffText ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(SimpleDiff, { text: diffText, maxHeight, showGutterSeparator }) }) : /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(Text, { dimColor: true, wrap: "wrap", children: "Diff preview unavailable" }) }) });
33427
+ ) }) : hasDiffText ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(SimpleDiff, { text: diffText, maxHeight }) }) : /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(Text, { dimColor: true, wrap: "wrap", children: "Diff preview unavailable" }) }) });
33061
33428
  }
33062
33429
 
33063
33430
  // src/app/agent/tools/EditTool/UI.tsx
@@ -33075,9 +33442,6 @@ function countLineDiff(oldText, newText) {
33075
33442
  }
33076
33443
  return { added, removed };
33077
33444
  }
33078
- function stripUnifiedDiffFileHeaders(diffText) {
33079
- return diffText.replace(/^--- .*\n/m, "").replace(/^\+\+\+ .*\n/m, "");
33080
- }
33081
33445
  function userFacingName11(args) {
33082
33446
  if (!args) return "Updated";
33083
33447
  if (args.edits && Array.isArray(args.edits) && args.edits.length > 0) {
@@ -33092,17 +33456,18 @@ function renderToolUseMessage12({ args }) {
33092
33456
  return /* @__PURE__ */ jsx44(Text, { color: BLUMA_TERMINAL.blue, children: p });
33093
33457
  }
33094
33458
  function renderToolHeader12({ args }) {
33095
- const path50 = args?.file_path ?? ".";
33459
+ const path51 = args?.file_path ?? ".";
33096
33460
  const oldText = typeof args?.old_string === "string" ? args.old_string : "";
33097
33461
  const newText = typeof args?.new_string === "string" ? args.new_string : "";
33098
33462
  const counts = countLineDiff(oldText, newText);
33099
- const fileName = String(path50).split("/").pop() || String(path50);
33100
33463
  const action = oldText === "" ? "Created" : "Update";
33101
33464
  return /* @__PURE__ */ jsxs27(Box_default, { flexDirection: "row", gap: 1, alignItems: "flex-end", children: [
33102
33465
  /* @__PURE__ */ jsxs27(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: [
33103
33466
  action,
33104
- " ",
33105
- /* @__PURE__ */ jsx44(FilePathLink, { filePath: path50, color: BLUMA_TERMINAL.dim })
33467
+ /* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
33468
+ " ",
33469
+ /* @__PURE__ */ jsx44(FilePathLink, { filePath: path51, color: BLUMA_TERMINAL.dim })
33470
+ ] })
33106
33471
  ] }),
33107
33472
  /* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
33108
33473
  "(",
@@ -33214,8 +33579,7 @@ function renderToolResultMessage12(result) {
33214
33579
  {
33215
33580
  filePath: edit.file_path,
33216
33581
  isNewFile: edit.is_new_file ?? false,
33217
- diffText: typeof edit.diff === "string" ? stripUnifiedDiffFileHeaders(edit.diff) : edit.diff,
33218
- showGutterSeparator: false
33582
+ diffText: typeof edit.diff === "string" ? edit.diff : null
33219
33583
  }
33220
33584
  ) }, `result-edit-${idx}`)) });
33221
33585
  }
@@ -33224,8 +33588,7 @@ function renderToolResultMessage12(result) {
33224
33588
  {
33225
33589
  filePath: result.file_path,
33226
33590
  isNewFile: result.is_new_file ?? false,
33227
- diffText: stripUnifiedDiffFileHeaders(result.diff),
33228
- showGutterSeparator: false
33591
+ diffText: result.diff
33229
33592
  }
33230
33593
  ) }) });
33231
33594
  }
@@ -33495,11 +33858,11 @@ function userFacingName13() {
33495
33858
  }
33496
33859
  function renderToolUseMessage14({ args }) {
33497
33860
  const q = args?.query ? `"${args.query}"` : "...";
33498
- const path50 = args?.path || ".";
33861
+ const path51 = args?.path || ".";
33499
33862
  return /* @__PURE__ */ jsxs30(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
33500
33863
  /* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.blue, children: q }),
33501
33864
  /* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.dim, children: " " }),
33502
- /* @__PURE__ */ jsx47(FilePathLink, { filePath: path50, color: BLUMA_TERMINAL.dim })
33865
+ /* @__PURE__ */ jsx47(FilePathLink, { filePath: path51, color: BLUMA_TERMINAL.dim })
33503
33866
  ] });
33504
33867
  }
33505
33868
  function renderToolHeader14({ args }) {
@@ -34021,7 +34384,7 @@ var loadSkillTool = createTool({
34021
34384
  });
34022
34385
 
34023
34386
  // src/app/agent/tools/FileWriteTool/UI.tsx
34024
- import fs42 from "fs";
34387
+ import fs43 from "fs";
34025
34388
  import { diffLines as diffLines3 } from "diff";
34026
34389
  import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
34027
34390
  function getFilePath(args) {
@@ -34036,7 +34399,7 @@ function getFilePath(args) {
34036
34399
  function readExistingFileText(filePath) {
34037
34400
  if (!filePath) return "";
34038
34401
  try {
34039
- return fs42.readFileSync(filePath, "utf-8");
34402
+ return fs43.readFileSync(filePath, "utf-8");
34040
34403
  } catch {
34041
34404
  return "";
34042
34405
  }
@@ -34113,7 +34476,7 @@ function renderToolMessage20({
34113
34476
  /* @__PURE__ */ jsx54(ToolUseLoader, { state: state2 ?? "pending" }),
34114
34477
  renderToolHeader21({ args: p })
34115
34478
  ] }),
34116
- /* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", paddingLeft: 1, children: payload != null ? renderToolResultMessage21(payload, { filePath, content }) : /* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", children: /* @__PURE__ */ jsx54(Box_default, { marginTop: 0, width: "100%", children: content ? /* @__PURE__ */ jsx54(Box_default, { width: "100%", flexGrow: 1, flexDirection: "column", alignItems: "stretch", children: /* @__PURE__ */ jsx54(SimpleDiff, { text: diffText, maxHeight: 4 }) }) : /* @__PURE__ */ jsxs37(Text, { dimColor: true, children: [
34479
+ /* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", paddingLeft: 1, children: payload != null ? renderToolResultMessage21(payload, { filePath, content }) : /* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", children: /* @__PURE__ */ jsx54(Box_default, { marginTop: 0, width: "100%", children: content ? /* @__PURE__ */ jsx54(Box_default, { width: "100%", flexGrow: 1, flexDirection: "column", children: /* @__PURE__ */ jsx54(SimpleDiff, { text: diffText, maxHeight: 2e3 }) }) : /* @__PURE__ */ jsxs37(Text, { dimColor: true, children: [
34117
34480
  "Writing to ",
34118
34481
  filePath ?? "...",
34119
34482
  " (",
@@ -34138,18 +34501,17 @@ function renderToolResultMessage21(result, context) {
34138
34501
  const previousContent = typeof payload.previous_content === "string" ? payload.previous_content : void 0;
34139
34502
  const currentContent = typeof payload.content === "string" ? payload.content : typeof context?.args?.content === "string" ? (context?.args).content : typeof context?.content === "string" ? context.content : void 0;
34140
34503
  const canRenderDiff = typeof currentContent === "string" && currentContent.trim() && typeof previousContent === "string";
34141
- return /* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", children: canRenderDiff ? /* @__PURE__ */ jsx54(Box_default, { marginTop: 0, width: "100%", flexDirection: "column", alignItems: "stretch", flexGrow: 1, children: /* @__PURE__ */ jsx54(
34504
+ return /* @__PURE__ */ jsx54(Box_default, { flexDirection: "column", children: canRenderDiff ? /* @__PURE__ */ jsx54(Box_default, { width: "100%", flexGrow: 1, children: /* @__PURE__ */ jsx54(
34142
34505
  SimpleDiff,
34143
34506
  {
34144
34507
  text: buildUnifiedDiffText(resultFilePath, previousContent ?? "", currentContent ?? ""),
34145
- maxHeight: 200
34508
+ maxHeight: 2e3
34146
34509
  }
34147
34510
  ) }) : typeof currentContent === "string" && currentContent.trim() ? /* @__PURE__ */ jsx54(
34148
34511
  Box_default,
34149
34512
  {
34150
34513
  width: "100%",
34151
34514
  backgroundColor: BLUMA_TERMINAL.diffAdded,
34152
- marginTop: 1,
34153
34515
  children: /* @__PURE__ */ jsx54(
34154
34516
  HighlightedCode,
34155
34517
  {
@@ -38751,9 +39113,9 @@ var renderCode = () => {
38751
39113
  };
38752
39114
 
38753
39115
  // src/app/agent/core/thread/thread_store.ts
38754
- import path46 from "path";
39116
+ import path47 from "path";
38755
39117
  import os34 from "os";
38756
- import { promises as fs43 } from "fs";
39118
+ import { promises as fs44 } from "fs";
38757
39119
  import { randomUUID as randomUUID2 } from "crypto";
38758
39120
  var INDEX_VERSION = 1;
38759
39121
  var fileLocks2 = /* @__PURE__ */ new Map();
@@ -38778,9 +39140,9 @@ var ThreadStore = class {
38778
39140
  packageVersion;
38779
39141
  constructor() {
38780
39142
  const homeDir = os34.homedir();
38781
- this.threadsDir = path46.join(homeDir, ".bluma", "threads");
38782
- this.archiveDir = path46.join(this.threadsDir, "archive");
38783
- this.indexPath = path46.join(this.threadsDir, "index.json");
39143
+ this.threadsDir = path47.join(homeDir, ".bluma", "threads");
39144
+ this.archiveDir = path47.join(this.threadsDir, "archive");
39145
+ this.indexPath = path47.join(this.threadsDir, "index.json");
38784
39146
  this.packageVersion = process.env.npm_package_version || "0.0.0";
38785
39147
  }
38786
39148
  // ==================== Inicialização ====================
@@ -38788,10 +39150,10 @@ var ThreadStore = class {
38788
39150
  * Inicializa o diretório de threads
38789
39151
  */
38790
39152
  async initialize() {
38791
- await fs43.mkdir(this.threadsDir, { recursive: true });
38792
- await fs43.mkdir(this.archiveDir, { recursive: true });
39153
+ await fs44.mkdir(this.threadsDir, { recursive: true });
39154
+ await fs44.mkdir(this.archiveDir, { recursive: true });
38793
39155
  try {
38794
- await fs43.access(this.indexPath);
39156
+ await fs44.access(this.indexPath);
38795
39157
  } catch {
38796
39158
  await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
38797
39159
  }
@@ -38820,7 +39182,7 @@ var ThreadStore = class {
38820
39182
  async loadIndex() {
38821
39183
  return withFileLock2(this.indexPath, async () => {
38822
39184
  try {
38823
- const content = await fs43.readFile(this.indexPath, "utf-8");
39185
+ const content = await fs44.readFile(this.indexPath, "utf-8");
38824
39186
  return JSON.parse(content);
38825
39187
  } catch {
38826
39188
  return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
@@ -38831,8 +39193,8 @@ var ThreadStore = class {
38831
39193
  return withFileLock2(this.indexPath, async () => {
38832
39194
  index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
38833
39195
  const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
38834
- await fs43.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
38835
- await fs43.rename(tempPath, this.indexPath);
39196
+ await fs44.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
39197
+ await fs44.rename(tempPath, this.indexPath);
38836
39198
  });
38837
39199
  }
38838
39200
  // ==================== Git Info ====================
@@ -39026,9 +39388,9 @@ var ThreadStore = class {
39026
39388
  const entry = index.threads[entryIndex];
39027
39389
  if (entry.status === "archived") return true;
39028
39390
  const oldPath = this.getHistoryPath(threadId);
39029
- const newPath = path46.join(this.archiveDir, `${threadId}.jsonl`);
39391
+ const newPath = path47.join(this.archiveDir, `${threadId}.jsonl`);
39030
39392
  try {
39031
- await fs43.rename(oldPath, newPath);
39393
+ await fs44.rename(oldPath, newPath);
39032
39394
  } catch (e) {
39033
39395
  if (e.code !== "ENOENT") throw e;
39034
39396
  }
@@ -39049,10 +39411,10 @@ var ThreadStore = class {
39049
39411
  if (entryIndex === -1) return false;
39050
39412
  const entry = index.threads[entryIndex];
39051
39413
  if (entry.status === "active") return true;
39052
- const oldPath = path46.join(this.archiveDir, `${threadId}.jsonl`);
39414
+ const oldPath = path47.join(this.archiveDir, `${threadId}.jsonl`);
39053
39415
  const newPath = this.getHistoryPath(threadId);
39054
39416
  try {
39055
- await fs43.rename(oldPath, newPath);
39417
+ await fs44.rename(oldPath, newPath);
39056
39418
  } catch (e) {
39057
39419
  if (e.code !== "ENOENT") throw e;
39058
39420
  }
@@ -39073,7 +39435,7 @@ var ThreadStore = class {
39073
39435
  if (entryIndex === -1) return false;
39074
39436
  const entry = index.threads[entryIndex];
39075
39437
  try {
39076
- await fs43.unlink(entry.historyPath);
39438
+ await fs44.unlink(entry.historyPath);
39077
39439
  } catch {
39078
39440
  }
39079
39441
  index.threads.splice(entryIndex, 1);
@@ -39083,7 +39445,7 @@ var ThreadStore = class {
39083
39445
  }
39084
39446
  // ==================== Histórico ====================
39085
39447
  getHistoryPath(threadId) {
39086
- return path46.join(this.threadsDir, `${threadId}.jsonl`);
39448
+ return path47.join(this.threadsDir, `${threadId}.jsonl`);
39087
39449
  }
39088
39450
  /**
39089
39451
  * Guarda o histórico de uma thread
@@ -39100,7 +39462,7 @@ var ThreadStore = class {
39100
39462
  for (const msg of history.messages) {
39101
39463
  lines.push(JSON.stringify({ type: "message", ...msg }));
39102
39464
  }
39103
- await fs43.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
39465
+ await fs44.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
39104
39466
  }
39105
39467
  /**
39106
39468
  * Carrega o histórico de uma thread
@@ -39110,7 +39472,7 @@ var ThreadStore = class {
39110
39472
  const entry = index.threads.find((t) => t.threadId === threadId);
39111
39473
  if (!entry) return null;
39112
39474
  try {
39113
- const content = await fs43.readFile(entry.historyPath, "utf-8");
39475
+ const content = await fs44.readFile(entry.historyPath, "utf-8");
39114
39476
  const lines = content.split("\n").filter(Boolean);
39115
39477
  const history = {
39116
39478
  threadId,
@@ -39143,7 +39505,7 @@ var ThreadStore = class {
39143
39505
  const entry = index.threads.find((t) => t.threadId === threadId);
39144
39506
  if (!entry) throw new Error(`Thread not found: ${threadId}`);
39145
39507
  const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
39146
- await fs43.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
39508
+ await fs44.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
39147
39509
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
39148
39510
  await this.saveIndex(index);
39149
39511
  }
@@ -42008,16 +42370,16 @@ import latestVersion from "latest-version";
42008
42370
  import semverGt from "semver/functions/gt.js";
42009
42371
  import semverValid from "semver/functions/valid.js";
42010
42372
  import { fileURLToPath as fileURLToPath6 } from "url";
42011
- import path47 from "path";
42012
- import fs44 from "fs";
42373
+ import path48 from "path";
42374
+ import fs45 from "fs";
42013
42375
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
42014
42376
  function findBlumaPackageJson(startDir) {
42015
42377
  let dir = startDir;
42016
42378
  for (let i = 0; i < 12; i++) {
42017
- const candidate = path47.join(dir, "package.json");
42018
- if (fs44.existsSync(candidate)) {
42379
+ const candidate = path48.join(dir, "package.json");
42380
+ if (fs45.existsSync(candidate)) {
42019
42381
  try {
42020
- const raw = fs44.readFileSync(candidate, "utf8");
42382
+ const raw = fs45.readFileSync(candidate, "utf8");
42021
42383
  const parsed = JSON.parse(raw);
42022
42384
  if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
42023
42385
  return { name: parsed.name, version: String(parsed.version) };
@@ -42025,7 +42387,7 @@ function findBlumaPackageJson(startDir) {
42025
42387
  } catch {
42026
42388
  }
42027
42389
  }
42028
- const parent = path47.dirname(dir);
42390
+ const parent = path48.dirname(dir);
42029
42391
  if (parent === dir) break;
42030
42392
  dir = parent;
42031
42393
  }
@@ -42034,13 +42396,13 @@ function findBlumaPackageJson(startDir) {
42034
42396
  function resolveInstalledBlumaPackage() {
42035
42397
  const tried = /* @__PURE__ */ new Set();
42036
42398
  const tryFrom = (dir) => {
42037
- const abs = path47.resolve(dir);
42399
+ const abs = path48.resolve(dir);
42038
42400
  if (tried.has(abs)) return null;
42039
42401
  tried.add(abs);
42040
42402
  return findBlumaPackageJson(abs);
42041
42403
  };
42042
42404
  try {
42043
- const fromBundle = tryFrom(path47.dirname(fileURLToPath6(import.meta.url)));
42405
+ const fromBundle = tryFrom(path48.dirname(fileURLToPath6(import.meta.url)));
42044
42406
  if (fromBundle) return fromBundle;
42045
42407
  } catch {
42046
42408
  }
@@ -42048,12 +42410,12 @@ function resolveInstalledBlumaPackage() {
42048
42410
  if (argv1 && !argv1.startsWith("-")) {
42049
42411
  try {
42050
42412
  let resolved = argv1;
42051
- if (path47.isAbsolute(argv1) && fs44.existsSync(argv1)) {
42052
- resolved = fs44.realpathSync(argv1);
42413
+ if (path48.isAbsolute(argv1) && fs45.existsSync(argv1)) {
42414
+ resolved = fs45.realpathSync(argv1);
42053
42415
  } else {
42054
- resolved = path47.resolve(process.cwd(), argv1);
42416
+ resolved = path48.resolve(process.cwd(), argv1);
42055
42417
  }
42056
- const fromArgv = tryFrom(path47.dirname(resolved));
42418
+ const fromArgv = tryFrom(path48.dirname(resolved));
42057
42419
  if (fromArgv) return fromArgv;
42058
42420
  } catch {
42059
42421
  }
@@ -42872,16 +43234,16 @@ function usePlanMode() {
42872
43234
 
42873
43235
  // src/app/hooks/useAgentMode.ts
42874
43236
  import { useState as useState22, useEffect as useEffect20, useCallback as useCallback9 } from "react";
42875
- import * as fs45 from "fs";
42876
- import * as path48 from "path";
43237
+ import * as fs46 from "fs";
43238
+ import * as path49 from "path";
42877
43239
  import { homedir as homedir3 } from "os";
42878
- var SETTINGS_PATH = path48.join(homedir3(), ".bluma", "settings.json");
43240
+ var SETTINGS_PATH = path49.join(homedir3(), ".bluma", "settings.json");
42879
43241
  function readAgentModeFromFile() {
42880
43242
  try {
42881
- if (!fs45.existsSync(SETTINGS_PATH)) {
43243
+ if (!fs46.existsSync(SETTINGS_PATH)) {
42882
43244
  return "default";
42883
43245
  }
42884
- const content = fs45.readFileSync(SETTINGS_PATH, "utf-8");
43246
+ const content = fs46.readFileSync(SETTINGS_PATH, "utf-8");
42885
43247
  const settings = JSON.parse(content);
42886
43248
  return settings.agentMode || "default";
42887
43249
  } catch (error) {
@@ -42900,16 +43262,16 @@ function useAgentMode() {
42900
43262
  }, []);
42901
43263
  const updateAgentMode = useCallback9((mode) => {
42902
43264
  try {
42903
- if (!fs45.existsSync(SETTINGS_PATH)) {
42904
- fs45.mkdirSync(path48.dirname(SETTINGS_PATH), { recursive: true });
43265
+ if (!fs46.existsSync(SETTINGS_PATH)) {
43266
+ fs46.mkdirSync(path49.dirname(SETTINGS_PATH), { recursive: true });
42905
43267
  }
42906
43268
  let settings = {};
42907
- if (fs45.existsSync(SETTINGS_PATH)) {
42908
- const content = fs45.readFileSync(SETTINGS_PATH, "utf-8");
43269
+ if (fs46.existsSync(SETTINGS_PATH)) {
43270
+ const content = fs46.readFileSync(SETTINGS_PATH, "utf-8");
42909
43271
  settings = JSON.parse(content);
42910
43272
  }
42911
43273
  settings.agentMode = mode;
42912
- fs45.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
43274
+ fs46.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
42913
43275
  setAgentMode(mode);
42914
43276
  } catch (error) {
42915
43277
  console.error("Failed to update agent mode:", error);
@@ -44165,9 +44527,9 @@ async function runAgentMode() {
44165
44527
  try {
44166
44528
  if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
44167
44529
  const filePath = args[inputFileIndex + 1];
44168
- rawPayload = fs46.readFileSync(filePath, "utf-8");
44530
+ rawPayload = fs47.readFileSync(filePath, "utf-8");
44169
44531
  } else {
44170
- rawPayload = fs46.readFileSync(0, "utf-8");
44532
+ rawPayload = fs47.readFileSync(0, "utf-8");
44171
44533
  }
44172
44534
  } catch (err) {
44173
44535
  writeAgentEvent(registrySessionId, {
@@ -44204,6 +44566,12 @@ async function runAgentMode() {
44204
44566
  process.env.BLUMA_SANDBOX_NAME = String(envelope.metadata.sandbox_name);
44205
44567
  }
44206
44568
  }
44569
+ const envelopeUserRequest = typeof envelope.context === "object" && envelope.context !== null ? String(
44570
+ envelope.context.user_request ?? envelope.context.userRequest ?? ""
44571
+ ) : "";
44572
+ if (envelopeUserRequest.trim()) {
44573
+ process.env.BLUMA_USER_REQUEST = envelopeUserRequest.trim();
44574
+ }
44207
44575
  const eventBus = new EventEmitter7();
44208
44576
  const sessionId = registrySessionId || envelope.session_id || envelope.message_id || uuidv412();
44209
44577
  process.env.BLUMA_SESSION_ID = sessionId;
@@ -44234,35 +44602,54 @@ async function runAgentMode() {
44234
44602
  }
44235
44603
  });
44236
44604
  let lastAssistantMessage = null;
44237
- let reasoningBuffer = null;
44238
44605
  let lastAttachments = null;
44239
44606
  let resultEmitted = false;
44240
44607
  let agentRef = null;
44241
- eventBus.on("backend_message", async (payload) => {
44608
+ let reasoningSequence = 0;
44609
+ const emitReasoningChunk = (delta, source) => {
44610
+ const text = String(delta ?? "");
44611
+ if (!text) return;
44612
+ reasoningSequence += 1;
44242
44613
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
44614
+ writeAgentEvent(sessionId, {
44615
+ event_type: "reasoning_chunk",
44616
+ timestamp,
44617
+ sequence: reasoningSequence,
44618
+ delta: text,
44619
+ source
44620
+ });
44243
44621
  writeAgentEvent(sessionId, {
44244
44622
  event_type: "backend_message",
44245
- backend_type: String(payload?.type || "unknown"),
44623
+ backend_type: "reasoning_chunk",
44246
44624
  timestamp,
44247
- payload
44625
+ payload: { type: "reasoning_chunk", sequence: reasoningSequence, delta: text, source }
44248
44626
  });
44627
+ };
44628
+ eventBus.on("backend_message", async (payload) => {
44629
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
44630
+ if (payload?.type === "reasoning" && typeof payload.content === "string") {
44631
+ emitReasoningChunk(payload.content, "message");
44632
+ } else {
44633
+ writeAgentEvent(sessionId, {
44634
+ event_type: "backend_message",
44635
+ backend_type: String(payload?.type || "unknown"),
44636
+ timestamp,
44637
+ payload
44638
+ });
44639
+ }
44249
44640
  if (payload?.type === "assistant_message" && typeof payload.content === "string") {
44250
44641
  lastAssistantMessage = payload.content;
44251
44642
  }
44252
- if (payload?.type === "reasoning" && typeof payload.content === "string") {
44253
- if (!reasoningBuffer) {
44254
- reasoningBuffer = payload.content;
44255
- }
44256
- }
44257
44643
  if (payload?.type === "tool_result" && payload.tool_name === "message") {
44258
44644
  try {
44259
44645
  const rawResult = payload.result;
44260
44646
  const parsed = typeof rawResult === "string" ? JSON.parse(rawResult) : rawResult;
44261
- const body = parsed?.content?.body;
44647
+ const messagePayload = parsed?.data && typeof parsed.data === "object" ? parsed.data : parsed;
44648
+ const body = messagePayload?.content?.body;
44262
44649
  if (typeof body === "string") {
44263
44650
  lastAssistantMessage = body;
44264
44651
  }
44265
- const attachments = parsed?.attachments;
44652
+ const attachments = messagePayload?.attachments;
44266
44653
  if (Array.isArray(attachments)) {
44267
44654
  lastAttachments = attachments.filter((p) => typeof p === "string");
44268
44655
  }
@@ -44282,7 +44669,6 @@ async function runAgentMode() {
44282
44669
  message_id: envelope.message_id || sessionId,
44283
44670
  action: envelope.action || "unknown",
44284
44671
  last_assistant_message: lastAssistantMessage,
44285
- reasoning: reasoningBuffer,
44286
44672
  attachments: lastAttachments
44287
44673
  }
44288
44674
  });
@@ -44298,7 +44684,7 @@ async function runAgentMode() {
44298
44684
  });
44299
44685
  });
44300
44686
  eventBus.on("stream_reasoning_chunk", (payload) => {
44301
- reasoningBuffer = (reasoningBuffer || "") + (payload?.delta || "");
44687
+ emitReasoningChunk(payload?.delta || "", "stream");
44302
44688
  });
44303
44689
  writeAgentEvent(sessionId, {
44304
44690
  event_type: "log",
@@ -44337,7 +44723,6 @@ async function runAgentMode() {
44337
44723
  message_id: envelope.message_id || sessionId,
44338
44724
  action: envelope.action || "unknown",
44339
44725
  last_assistant_message: lastAssistantMessage,
44340
- reasoning: reasoningBuffer,
44341
44726
  attachments: lastAttachments
44342
44727
  }
44343
44728
  });
@@ -44365,9 +44750,9 @@ async function runAgentMode() {
44365
44750
  }
44366
44751
  function readCliPackageVersion() {
44367
44752
  try {
44368
- const base = path49.dirname(fileURLToPath7(import.meta.url));
44369
- const pkgPath = path49.join(base, "..", "package.json");
44370
- const j = JSON.parse(fs46.readFileSync(pkgPath, "utf8"));
44753
+ const base = path50.dirname(fileURLToPath7(import.meta.url));
44754
+ const pkgPath = path50.join(base, "..", "package.json");
44755
+ const j = JSON.parse(fs47.readFileSync(pkgPath, "utf8"));
44371
44756
  return String(j.version || "0.0.0");
44372
44757
  } catch {
44373
44758
  return "0.0.0";
@@ -44493,7 +44878,7 @@ function startBackgroundAgent() {
44493
44878
  process.exit(1);
44494
44879
  }
44495
44880
  const filePath = args[inputFileIndex + 1];
44496
- const rawPayload = fs46.readFileSync(filePath, "utf-8");
44881
+ const rawPayload = fs47.readFileSync(filePath, "utf-8");
44497
44882
  const envelope = JSON.parse(rawPayload);
44498
44883
  const sessionId = envelope.session_id || envelope.message_id || uuidv412();
44499
44884
  registerSession({