@nomad-e/bluma-cli 0.10.0 → 0.11.1

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
@@ -406,11 +406,11 @@ var init_sandbox_policy = __esm({
406
406
  });
407
407
 
408
408
  // src/app/agent/runtime/task_store.ts
409
- import fs9 from "fs";
410
- import path8 from "path";
409
+ import fs10 from "fs";
410
+ import path9 from "path";
411
411
  function getStorePath() {
412
412
  const policy = getSandboxPolicy();
413
- return path8.join(policy.workspaceRoot, ".bluma", "task_state.json");
413
+ return path9.join(policy.workspaceRoot, ".bluma", "task_state.json");
414
414
  }
415
415
  function getDefaultState() {
416
416
  return {
@@ -426,8 +426,8 @@ function ensureLoaded() {
426
426
  return cache2;
427
427
  }
428
428
  try {
429
- if (fs9.existsSync(storePath)) {
430
- const raw = fs9.readFileSync(storePath, "utf-8");
429
+ if (fs10.existsSync(storePath)) {
430
+ const raw = fs10.readFileSync(storePath, "utf-8");
431
431
  const parsed = JSON.parse(raw);
432
432
  cache2 = {
433
433
  tasks: Array.isArray(parsed.tasks) ? parsed.tasks : [],
@@ -446,9 +446,9 @@ function ensureLoaded() {
446
446
  }
447
447
  function persist(state2) {
448
448
  const storePath = getStorePath();
449
- fs9.mkdirSync(path8.dirname(storePath), { recursive: true });
449
+ fs10.mkdirSync(path9.dirname(storePath), { recursive: true });
450
450
  state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
451
- fs9.writeFileSync(storePath, JSON.stringify(state2, null, 2), "utf-8");
451
+ fs10.writeFileSync(storePath, JSON.stringify(state2, null, 2), "utf-8");
452
452
  cache2 = state2;
453
453
  cachePath = storePath;
454
454
  }
@@ -835,37 +835,37 @@ __export(session_registry_exports, {
835
835
  removeSession: () => removeSession,
836
836
  updateSession: () => updateSession
837
837
  });
838
- import fs15 from "fs";
838
+ import fs16 from "fs";
839
839
  import os11 from "os";
840
- import path16 from "path";
840
+ import path17 from "path";
841
841
  function getRegistryDir() {
842
- return path16.join(process.env.HOME || os11.homedir(), ".bluma", "registry");
842
+ return path17.join(process.env.HOME || os11.homedir(), ".bluma", "registry");
843
843
  }
844
844
  function getRegistryFile() {
845
- return path16.join(getRegistryDir(), "sessions.json");
845
+ return path17.join(getRegistryDir(), "sessions.json");
846
846
  }
847
847
  function ensureRegistryDir() {
848
- fs15.mkdirSync(getRegistryDir(), { recursive: true });
848
+ fs16.mkdirSync(getRegistryDir(), { recursive: true });
849
849
  }
850
850
  function readRegistry() {
851
851
  ensureRegistryDir();
852
852
  const file = getRegistryFile();
853
- if (!fs15.existsSync(file)) {
853
+ if (!fs16.existsSync(file)) {
854
854
  return { entries: [] };
855
855
  }
856
856
  try {
857
- return JSON.parse(fs15.readFileSync(file, "utf-8"));
857
+ return JSON.parse(fs16.readFileSync(file, "utf-8"));
858
858
  } catch {
859
859
  return { entries: [] };
860
860
  }
861
861
  }
862
862
  function writeRegistry(state2) {
863
863
  ensureRegistryDir();
864
- fs15.writeFileSync(getRegistryFile(), JSON.stringify(state2, null, 2), "utf-8");
864
+ fs16.writeFileSync(getRegistryFile(), JSON.stringify(state2, null, 2), "utf-8");
865
865
  }
866
866
  function getSessionLogPath(sessionId) {
867
867
  ensureRegistryDir();
868
- return path16.join(getRegistryDir(), `${sessionId}.jsonl`);
868
+ return path17.join(getRegistryDir(), `${sessionId}.jsonl`);
869
869
  }
870
870
  function registerSession(entry) {
871
871
  const state2 = readRegistry();
@@ -910,13 +910,13 @@ function removeSession(sessionId) {
910
910
  }
911
911
  function appendSessionLog(sessionId, payload) {
912
912
  const logFile = getSessionLogPath(sessionId);
913
- fs15.appendFileSync(logFile, `${JSON.stringify(payload)}
913
+ fs16.appendFileSync(logFile, `${JSON.stringify(payload)}
914
914
  `, "utf-8");
915
915
  }
916
916
  function readSessionLog(sessionId) {
917
917
  const logFile = getSessionLogPath(sessionId);
918
- if (!fs15.existsSync(logFile)) return [];
919
- return fs15.readFileSync(logFile, "utf-8").split("\n").filter(Boolean);
918
+ if (!fs16.existsSync(logFile)) return [];
919
+ return fs16.readFileSync(logFile, "utf-8").split("\n").filter(Boolean);
920
920
  }
921
921
  var init_session_registry = __esm({
922
922
  "src/app/agent/runtime/session_registry.ts"() {
@@ -925,17 +925,17 @@ var init_session_registry = __esm({
925
925
  });
926
926
 
927
927
  // src/app/agent/utils/logger.ts
928
- import fs16 from "fs";
928
+ import fs17 from "fs";
929
929
  import os12 from "os";
930
- import path17 from "path";
930
+ import path18 from "path";
931
931
  function getLogDir() {
932
- const dir = path17.join(process.env.HOME || os12.homedir(), ".bluma", "logs");
933
- fs16.mkdirSync(dir, { recursive: true });
932
+ const dir = path18.join(process.env.HOME || os12.homedir(), ".bluma", "logs");
933
+ fs17.mkdirSync(dir, { recursive: true });
934
934
  return dir;
935
935
  }
936
936
  function getLogFilePath() {
937
937
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
938
- return path17.join(getLogDir(), `bluma-${today}.log`);
938
+ return path18.join(getLogDir(), `bluma-${today}.log`);
939
939
  }
940
940
  var BluMaLogger, logger;
941
941
  var init_logger = __esm({
@@ -965,7 +965,7 @@ var init_logger = __esm({
965
965
  turnId: this.turnId
966
966
  };
967
967
  try {
968
- fs16.appendFileSync(this.logFile, JSON.stringify(logEntry) + "\n");
968
+ fs17.appendFileSync(this.logFile, JSON.stringify(logEntry) + "\n");
969
969
  } catch {
970
970
  }
971
971
  if (this.consoleEnabled) {
@@ -1066,19 +1066,19 @@ __export(mailbox_registry_exports, {
1066
1066
  sendSignal: () => sendSignal,
1067
1067
  sendToMailbox: () => sendToMailbox
1068
1068
  });
1069
- import fs17 from "fs";
1069
+ import fs18 from "fs";
1070
1070
  import os13 from "os";
1071
- import path18 from "path";
1071
+ import path19 from "path";
1072
1072
  import { EventEmitter as EventEmitter3 } from "events";
1073
1073
  import { v4 as uuidv45 } from "uuid";
1074
1074
  function getMailboxesDir() {
1075
1075
  if (mailboxesDir) return mailboxesDir;
1076
- mailboxesDir = path18.join(process.env.HOME || os13.homedir(), ".bluma", "mailboxes");
1077
- fs17.mkdirSync(mailboxesDir, { recursive: true });
1076
+ mailboxesDir = path19.join(process.env.HOME || os13.homedir(), ".bluma", "mailboxes");
1077
+ fs18.mkdirSync(mailboxesDir, { recursive: true });
1078
1078
  return mailboxesDir;
1079
1079
  }
1080
1080
  function getMailboxPath(sessionId, type) {
1081
- return path18.join(getMailboxesDir(), `${sessionId}.${type}`);
1081
+ return path19.join(getMailboxesDir(), `${sessionId}.${type}`);
1082
1082
  }
1083
1083
  function sendToMailbox(sessionId, type, message2) {
1084
1084
  return mailbox.sendToMailbox(sessionId, type, message2);
@@ -1104,7 +1104,7 @@ function pruneMailbox(sessionId, type, keepLast = 100) {
1104
1104
  const pruned = queue.slice(-keepLast);
1105
1105
  mailbox.queues.set(sessionId, pruned);
1106
1106
  const filePath = getMailboxPath(sessionId, type);
1107
- fs17.writeFileSync(filePath, pruned.map((m) => JSON.stringify(m)).join("\n") + "\n", "utf-8");
1107
+ fs18.writeFileSync(filePath, pruned.map((m) => JSON.stringify(m)).join("\n") + "\n", "utf-8");
1108
1108
  }
1109
1109
  function listActiveMailboxes() {
1110
1110
  const result = [];
@@ -1172,16 +1172,16 @@ var init_mailbox_registry = __esm({
1172
1172
  loadExistingMailboxes() {
1173
1173
  try {
1174
1174
  const dir = getMailboxesDir();
1175
- const files = fs17.readdirSync(dir);
1175
+ const files = fs18.readdirSync(dir);
1176
1176
  for (const file of files) {
1177
1177
  if (!file.endsWith(".in") && !file.endsWith(".out") && !file.endsWith(".sig")) {
1178
1178
  continue;
1179
1179
  }
1180
1180
  const type = file.split(".").pop();
1181
1181
  const sessionId = file.slice(0, -(type.length + 1));
1182
- const filePath = path18.join(dir, file);
1182
+ const filePath = path19.join(dir, file);
1183
1183
  try {
1184
- const content = fs17.readFileSync(filePath, "utf-8");
1184
+ const content = fs18.readFileSync(filePath, "utf-8");
1185
1185
  const lines = content.trim().split("\n").filter(Boolean);
1186
1186
  const messages = [];
1187
1187
  for (const line of lines) {
@@ -1218,7 +1218,7 @@ var init_mailbox_registry = __esm({
1218
1218
  this.queues.set(sessionId, queue);
1219
1219
  try {
1220
1220
  const filePath = getMailboxPath(sessionId, type);
1221
- fs17.appendFileSync(filePath, JSON.stringify(fullMessage) + "\n", "utf-8");
1221
+ fs18.appendFileSync(filePath, JSON.stringify(fullMessage) + "\n", "utf-8");
1222
1222
  } catch {
1223
1223
  }
1224
1224
  this.emit(`message:${sessionId}`, fullMessage);
@@ -1267,7 +1267,7 @@ var init_mailbox_registry = __esm({
1267
1267
  this.emit(`signal:${sessionId}`, signal);
1268
1268
  try {
1269
1269
  const filePath = getMailboxPath(sessionId, "sig");
1270
- fs17.appendFileSync(filePath, JSON.stringify(signal) + "\n", "utf-8");
1270
+ fs18.appendFileSync(filePath, JSON.stringify(signal) + "\n", "utf-8");
1271
1271
  } catch {
1272
1272
  }
1273
1273
  return signal.id;
@@ -1281,9 +1281,9 @@ var init_mailbox_registry = __esm({
1281
1281
  ensureMailbox(sessionId) {
1282
1282
  const dir = getMailboxesDir();
1283
1283
  for (const type of ["in", "out", "sig"]) {
1284
- const filePath = path18.join(dir, `${sessionId}.${type}`);
1285
- if (!fs17.existsSync(filePath)) {
1286
- fs17.writeFileSync(filePath, "", "utf-8");
1284
+ const filePath = path19.join(dir, `${sessionId}.${type}`);
1285
+ if (!fs18.existsSync(filePath)) {
1286
+ fs18.writeFileSync(filePath, "", "utf-8");
1287
1287
  }
1288
1288
  }
1289
1289
  }
@@ -1293,8 +1293,8 @@ var init_mailbox_registry = __esm({
1293
1293
  removeMailbox(sessionId) {
1294
1294
  const dir = getMailboxesDir();
1295
1295
  for (const type of ["in", "out", "sig"]) {
1296
- const filePath = path18.join(dir, `${sessionId}.${type}`);
1297
- if (fs17.existsSync(filePath)) fs17.unlinkSync(filePath);
1296
+ const filePath = path19.join(dir, `${sessionId}.${type}`);
1297
+ if (fs18.existsSync(filePath)) fs18.unlinkSync(filePath);
1298
1298
  }
1299
1299
  this.queues.delete(sessionId);
1300
1300
  this.signalQueues.delete(sessionId);
@@ -1313,9 +1313,9 @@ __export(AgentCoordinationTool_exports, {
1313
1313
  spawnAgent: () => spawnAgent,
1314
1314
  waitAgent: () => waitAgent
1315
1315
  });
1316
- import fs18 from "fs";
1316
+ import fs19 from "fs";
1317
1317
  import os14 from "os";
1318
- import path19 from "path";
1318
+ import path20 from "path";
1319
1319
  import { spawn as spawn3 } from "child_process";
1320
1320
  import { v4 as uuidv46 } from "uuid";
1321
1321
  function readUserContextFromEnv() {
@@ -1429,10 +1429,10 @@ async function spawnAgent(args) {
1429
1429
  spawnLog.error("Payload NOT serializable", { error: e.message });
1430
1430
  throw new BluMaError("WORKER_CONTEXT_NOT_SERIALIZABLE" /* WORKER_CONTEXT_NOT_SERIALIZABLE */, `Worker context is not JSON-serializable: ${e.message}`);
1431
1431
  }
1432
- const payloadDir = fs18.mkdtempSync(path19.join(os14.tmpdir(), "bluma-worker-"));
1433
- const payloadPath = path19.join(payloadDir, `${sessionId}.json`);
1432
+ const payloadDir = fs19.mkdtempSync(path20.join(os14.tmpdir(), "bluma-worker-"));
1433
+ const payloadPath = path20.join(payloadDir, `${sessionId}.json`);
1434
1434
  try {
1435
- fs18.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
1435
+ fs19.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
1436
1436
  spawnLog.debug("Payload written", { payloadPath });
1437
1437
  } catch (e) {
1438
1438
  spawnLog.error("Failed to write payload", { error: e.message, payloadPath });
@@ -2201,11 +2201,11 @@ var NodeFsOperations = {
2201
2201
  if (fd) fs.closeSync(fd);
2202
2202
  }
2203
2203
  },
2204
- appendFileSync(path54, data, options) {
2205
- const _ = slowLogging`fs.appendFileSync(${path54}, ${data.length} chars)`;
2204
+ appendFileSync(path57, data, options) {
2205
+ const _ = slowLogging`fs.appendFileSync(${path57}, ${data.length} chars)`;
2206
2206
  if (options?.mode !== void 0) {
2207
2207
  try {
2208
- const fd = fs.openSync(path54, "ax", options.mode);
2208
+ const fd = fs.openSync(path57, "ax", options.mode);
2209
2209
  try {
2210
2210
  fs.appendFileSync(fd, data);
2211
2211
  } finally {
@@ -2216,35 +2216,35 @@ var NodeFsOperations = {
2216
2216
  if (getErrnoCode(e) !== "EEXIST") throw e;
2217
2217
  }
2218
2218
  }
2219
- fs.appendFileSync(path54, data);
2219
+ fs.appendFileSync(path57, data);
2220
2220
  },
2221
2221
  copyFileSync(src, dest) {
2222
2222
  const _ = slowLogging`fs.copyFileSync(${src} → ${dest})`;
2223
2223
  fs.copyFileSync(src, dest);
2224
2224
  },
2225
- unlinkSync(path54) {
2226
- const _ = slowLogging`fs.unlinkSync(${path54})`;
2227
- fs.unlinkSync(path54);
2225
+ unlinkSync(path57) {
2226
+ const _ = slowLogging`fs.unlinkSync(${path57})`;
2227
+ fs.unlinkSync(path57);
2228
2228
  },
2229
2229
  renameSync(oldPath, newPath) {
2230
2230
  const _ = slowLogging`fs.renameSync(${oldPath} → ${newPath})`;
2231
2231
  fs.renameSync(oldPath, newPath);
2232
2232
  },
2233
- linkSync(target, path54) {
2234
- const _ = slowLogging`fs.linkSync(${target} → ${path54})`;
2235
- fs.linkSync(target, path54);
2233
+ linkSync(target, path57) {
2234
+ const _ = slowLogging`fs.linkSync(${target} → ${path57})`;
2235
+ fs.linkSync(target, path57);
2236
2236
  },
2237
- symlinkSync(target, path54, type) {
2238
- const _ = slowLogging`fs.symlinkSync(${target} → ${path54})`;
2239
- fs.symlinkSync(target, path54, type);
2237
+ symlinkSync(target, path57, type) {
2238
+ const _ = slowLogging`fs.symlinkSync(${target} → ${path57})`;
2239
+ fs.symlinkSync(target, path57, type);
2240
2240
  },
2241
- readlinkSync(path54) {
2242
- const _ = slowLogging`fs.readlinkSync(${path54})`;
2243
- return fs.readlinkSync(path54);
2241
+ readlinkSync(path57) {
2242
+ const _ = slowLogging`fs.readlinkSync(${path57})`;
2243
+ return fs.readlinkSync(path57);
2244
2244
  },
2245
- realpathSync(path54) {
2246
- const _ = slowLogging`fs.realpathSync(${path54})`;
2247
- return fs.realpathSync(path54).normalize("NFC");
2245
+ realpathSync(path57) {
2246
+ const _ = slowLogging`fs.realpathSync(${path57})`;
2247
+ return fs.realpathSync(path57).normalize("NFC");
2248
2248
  },
2249
2249
  mkdirSync(dirPath, options) {
2250
2250
  const _ = slowLogging`fs.mkdirSync(${dirPath})`;
@@ -2277,12 +2277,12 @@ var NodeFsOperations = {
2277
2277
  const _ = slowLogging`fs.rmdirSync(${dirPath})`;
2278
2278
  fs.rmdirSync(dirPath);
2279
2279
  },
2280
- rmSync(path54, options) {
2281
- const _ = slowLogging`fs.rmSync(${path54})`;
2282
- fs.rmSync(path54, options);
2280
+ rmSync(path57, options) {
2281
+ const _ = slowLogging`fs.rmSync(${path57})`;
2282
+ fs.rmSync(path57, options);
2283
2283
  },
2284
- createWriteStream(path54) {
2285
- return fs.createWriteStream(path54);
2284
+ createWriteStream(path57) {
2285
+ return fs.createWriteStream(path57);
2286
2286
  },
2287
2287
  async readFileBytes(fsPath, maxBytes) {
2288
2288
  if (maxBytes === void 0) {
@@ -2389,12 +2389,12 @@ function shouldLogDebugMessage(message2) {
2389
2389
  var hasFormattedOutput = false;
2390
2390
  var debugWriter = null;
2391
2391
  var pendingWrite = Promise.resolve();
2392
- async function appendAsync(needMkdir, dir, path54, content) {
2392
+ async function appendAsync(needMkdir, dir, path57, content) {
2393
2393
  if (needMkdir) {
2394
2394
  await mkdir(dir, { recursive: true }).catch(() => {
2395
2395
  });
2396
2396
  }
2397
- await appendFile(path54, content);
2397
+ await appendFile(path57, content);
2398
2398
  void updateLatestDebugLogSymlink();
2399
2399
  }
2400
2400
  function noop() {
@@ -2404,8 +2404,8 @@ function getDebugWriter() {
2404
2404
  let ensuredDir = null;
2405
2405
  debugWriter = createBufferedWriter({
2406
2406
  writeFn: (content) => {
2407
- const path54 = getDebugLogPath();
2408
- const dir = dirname(path54);
2407
+ const path57 = getDebugLogPath();
2408
+ const dir = dirname(path57);
2409
2409
  const needMkdir = ensuredDir !== dir;
2410
2410
  ensuredDir = dir;
2411
2411
  if (isDebugMode()) {
@@ -2415,11 +2415,11 @@ function getDebugWriter() {
2415
2415
  } catch {
2416
2416
  }
2417
2417
  }
2418
- getFsImplementation().appendFileSync(path54, content);
2418
+ getFsImplementation().appendFileSync(path57, content);
2419
2419
  void updateLatestDebugLogSymlink();
2420
2420
  return;
2421
2421
  }
2422
- pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path54, content)).catch(noop);
2422
+ pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path57, content)).catch(noop);
2423
2423
  },
2424
2424
  flushIntervalMs: 1e3,
2425
2425
  maxBufferSize: 100,
@@ -8606,8 +8606,8 @@ import codeExcerpt from "code-excerpt";
8606
8606
  import { readFileSync as readFileSync2 } from "fs";
8607
8607
  import StackUtils from "stack-utils";
8608
8608
  import { jsx as jsx6, jsxs } from "react/jsx-runtime";
8609
- var cleanupPath = (path54) => {
8610
- return path54?.replace(`file://${process.cwd()}/`, "");
8609
+ var cleanupPath = (path57) => {
8610
+ return path57?.replace(`file://${process.cwd()}/`, "");
8611
8611
  };
8612
8612
  var stackUtils;
8613
8613
  function getStackUtils() {
@@ -12584,8 +12584,8 @@ var getInstance = (stdout, createInstance) => {
12584
12584
 
12585
12585
  // src/main.ts
12586
12586
  import { EventEmitter as EventEmitter7 } from "events";
12587
- import fs49 from "fs";
12588
- import path53 from "path";
12587
+ import fs51 from "fs";
12588
+ import path56 from "path";
12589
12589
  import { fileURLToPath as fileURLToPath7 } from "url";
12590
12590
  import { spawn as spawn6 } from "child_process";
12591
12591
  import { v4 as uuidv412 } from "uuid";
@@ -13479,12 +13479,12 @@ function cancelSlashSubmenu() {
13479
13479
 
13480
13480
  // src/app/agent/agent.ts
13481
13481
  import * as dotenv from "dotenv";
13482
- import path44 from "path";
13482
+ import path47 from "path";
13483
13483
  import os31 from "os";
13484
13484
 
13485
13485
  // src/app/agent/tool_invoker.ts
13486
13486
  import { promises as fs25 } from "fs";
13487
- import path26 from "path";
13487
+ import path27 from "path";
13488
13488
  import { fileURLToPath } from "url";
13489
13489
 
13490
13490
  // src/app/agent/tools/EditTool/EditTool.ts
@@ -14028,10 +14028,173 @@ ${finalDiff}`,
14028
14028
  // src/app/agent/tools/MessageTool/MessageTool.ts
14029
14029
  import { v4 as uuidv42 } from "uuid";
14030
14030
 
14031
- // src/app/agent/runtime/sandbox_message_validation.ts
14032
- init_sandbox_policy();
14031
+ // src/app/agent/runtime/factorai_context.ts
14033
14032
  import fs5 from "fs";
14034
14033
  import path5 from "path";
14034
+ function normalizeContext(raw) {
14035
+ if (!raw || typeof raw.appId !== "string" || !raw.appId.trim()) {
14036
+ return null;
14037
+ }
14038
+ const appId = raw.appId.trim();
14039
+ return {
14040
+ appId,
14041
+ tenantId: typeof raw.tenantId === "string" ? raw.tenantId : null,
14042
+ projectId: typeof raw.projectId === "string" ? raw.projectId : null,
14043
+ workspaceId: typeof raw.workspaceId === "string" ? raw.workspaceId : null,
14044
+ revisionId: typeof raw.revisionId === "string" ? raw.revisionId : null,
14045
+ deploymentId: typeof raw.deploymentId === "string" ? raw.deploymentId : null,
14046
+ buildJobId: typeof raw.buildJobId === "string" ? raw.buildJobId : null,
14047
+ manifestPath: typeof raw.manifestPath === "string" ? raw.manifestPath : null,
14048
+ manifestFile: typeof raw.manifestFile === "string" ? raw.manifestFile : null,
14049
+ appUrl: typeof raw.appUrl === "string" ? raw.appUrl : null
14050
+ };
14051
+ }
14052
+ function readJsonFile(filePath) {
14053
+ try {
14054
+ if (!fs5.existsSync(filePath)) {
14055
+ return null;
14056
+ }
14057
+ const parsed = JSON.parse(fs5.readFileSync(filePath, "utf8"));
14058
+ if (parsed && typeof parsed === "object") {
14059
+ return parsed.appContext && typeof parsed.appContext === "object" ? parsed.appContext : parsed;
14060
+ }
14061
+ } catch {
14062
+ return null;
14063
+ }
14064
+ return null;
14065
+ }
14066
+ function readFactorAiWorkspaceManifest(projectDir = process.cwd()) {
14067
+ const manifestPath = path5.join(projectDir, "factorai.sh.json");
14068
+ try {
14069
+ if (!fs5.existsSync(manifestPath)) {
14070
+ return null;
14071
+ }
14072
+ const parsed = JSON.parse(fs5.readFileSync(manifestPath, "utf8"));
14073
+ return parsed && typeof parsed === "object" ? parsed : null;
14074
+ } catch {
14075
+ return null;
14076
+ }
14077
+ }
14078
+ function readContextFromWorkspace() {
14079
+ const candidate = path5.join(process.cwd(), "factorai.sh.json");
14080
+ const parsed = readJsonFile(candidate);
14081
+ if (!parsed) {
14082
+ return null;
14083
+ }
14084
+ const context = normalizeContext(parsed);
14085
+ return context;
14086
+ }
14087
+ function loadFactorAiAppContext() {
14088
+ return readContextFromWorkspace();
14089
+ }
14090
+ var FACTOR_SH_URL_APP_FIELD = "factor-sh-url-app";
14091
+ function resolveFactorShAppLiveUrl(projectDir = process.cwd()) {
14092
+ const manifest = readFactorAiWorkspaceManifest(projectDir);
14093
+ const raw = manifest?.appContext?.appUrl ?? (typeof manifest?.app?.url === "string" ? manifest.app.url : null);
14094
+ if (typeof raw !== "string" || !raw.trim()) {
14095
+ return null;
14096
+ }
14097
+ const url = raw.trim();
14098
+ if (/^https?:\/\//i.test(url)) {
14099
+ return url;
14100
+ }
14101
+ const base = (process.env.SEVERINO_URL || process.env.FACTORAI_BASE_URL || process.env.FACTORAI_URL || "").trim().replace(/\/$/, "");
14102
+ if (!base) {
14103
+ return url.startsWith("/") ? url : `/${url}`;
14104
+ }
14105
+ return `${base}${url.startsWith("/") ? url : `/${url}`}`;
14106
+ }
14107
+ function formatFactorAiAppContextSummary(context) {
14108
+ if (!context) {
14109
+ return "";
14110
+ }
14111
+ const parts = [
14112
+ `appId=${context.appId}`,
14113
+ context.tenantId ? `tenantId=${context.tenantId}` : null,
14114
+ context.projectId ? `projectId=${context.projectId}` : null,
14115
+ context.workspaceId ? `workspaceId=${context.workspaceId}` : null,
14116
+ context.revisionId ? `revisionId=${context.revisionId}` : null,
14117
+ context.deploymentId ? `deploymentId=${context.deploymentId}` : null,
14118
+ context.buildJobId ? `buildJobId=${context.buildJobId}` : null,
14119
+ context.appUrl ? `appUrl=${context.appUrl}` : null
14120
+ ].filter(Boolean);
14121
+ return parts.join(" | ");
14122
+ }
14123
+ function buildFactorAiWorkspaceManifest(input) {
14124
+ const projectDir = input.projectDir || process.cwd();
14125
+ const existing = readFactorAiWorkspaceManifest(projectDir) || {};
14126
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
14127
+ const nextAppContext = {
14128
+ ...typeof existing.appContext === "object" && existing.appContext ? existing.appContext : {},
14129
+ appId: input.appContext.appId,
14130
+ tenantId: input.appContext.tenantId ?? null,
14131
+ projectId: input.appContext.projectId ?? null,
14132
+ workspaceId: input.appContext.workspaceId ?? null,
14133
+ revisionId: input.appContext.revisionId ?? null,
14134
+ deploymentId: input.appContext.deploymentId ?? null,
14135
+ buildJobId: input.appContext.buildJobId ?? null,
14136
+ manifestPath: "factorai.sh.json",
14137
+ manifestFile: "factorai.sh.json",
14138
+ appUrl: input.appContext.appUrl ?? null
14139
+ };
14140
+ return {
14141
+ ...existing,
14142
+ version: 1,
14143
+ manifestFile: "factorai.sh.json",
14144
+ manifestPath: "factorai.sh.json",
14145
+ appContext: nextAppContext,
14146
+ app: {
14147
+ ...typeof existing.app === "object" && existing.app ? existing.app : {},
14148
+ name: input.name ?? (typeof existing.app === "object" && existing.app ? existing.app.name : void 0) ?? null,
14149
+ status: input.status ?? (typeof existing.app === "object" && existing.app ? existing.app.status : void 0) ?? "building",
14150
+ isRedeploy: input.isRedeploy ?? (typeof existing.app === "object" && existing.app ? existing.app.isRedeploy : false),
14151
+ message: input.message ?? (typeof existing.app === "object" && existing.app ? existing.app.message : null),
14152
+ ...input.app || {}
14153
+ },
14154
+ agent: {
14155
+ provider: "bluma",
14156
+ mode: "tool-first",
14157
+ instructions: [
14158
+ "Read factorai.sh.json as the source of truth before editing files.",
14159
+ "Load skill factorai-sh for the full FactorAI.sh workflow.",
14160
+ "For an app already online: use factorai.sh.apply_app_changes with deploy:true (not deploy_app again).",
14161
+ "Poll factorai.sh.get_app_status until building\u2192ready before reporting the live URL.",
14162
+ "Use factorai.sh.redeploy_app only when rebuilding without new file patches."
14163
+ ],
14164
+ ...typeof existing.agent === "object" && existing.agent ? existing.agent : {},
14165
+ ...input.agent || {}
14166
+ },
14167
+ sandbox: {
14168
+ ...typeof existing.sandbox === "object" && existing.sandbox ? existing.sandbox : {},
14169
+ ...input.sandbox || {}
14170
+ },
14171
+ source: {
14172
+ root: ".",
14173
+ publicDir: "./public",
14174
+ appDir: ".",
14175
+ ...typeof existing.source === "object" && existing.source ? existing.source : {},
14176
+ ...input.source || {}
14177
+ },
14178
+ updatedAt: now2,
14179
+ ...input.extra || {}
14180
+ };
14181
+ }
14182
+ async function writeFactorAiWorkspaceManifest(input) {
14183
+ const projectDir = input.projectDir || process.cwd();
14184
+ const manifest = buildFactorAiWorkspaceManifest({ ...input, projectDir });
14185
+ const manifestPath = path5.join(projectDir, "factorai.sh.json");
14186
+ fs5.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
14187
+ `, "utf8");
14188
+ return { manifestPath, manifest };
14189
+ }
14190
+
14191
+ // src/app/agent/tools/MessageTool/MessageTool.ts
14192
+ init_sandbox_policy();
14193
+
14194
+ // src/app/agent/runtime/sandbox_message_validation.ts
14195
+ init_sandbox_policy();
14196
+ import fs6 from "fs";
14197
+ import path6 from "path";
14035
14198
  var HTTP_URL_PATTERN = /^https?:\/\//i;
14036
14199
  function normalizeAttachmentPath(entry) {
14037
14200
  if (typeof entry === "string") {
@@ -14104,14 +14267,14 @@ function validateSandboxMessageDeliverables(args, policy = getSandboxPolicy(), o
14104
14267
  details: err instanceof Error ? err.message : String(err)
14105
14268
  };
14106
14269
  }
14107
- if (!fs5.existsSync(resolved)) {
14270
+ if (!fs6.existsSync(resolved)) {
14108
14271
  return {
14109
14272
  ok: false,
14110
14273
  error: "Attachment file does not exist",
14111
14274
  details: `Create the deliverable first (file_write \u2192 .bluma/artifacts/...). Missing: ${resolved}`
14112
14275
  };
14113
14276
  }
14114
- const stat = fs5.statSync(resolved);
14277
+ const stat = fs6.statSync(resolved);
14115
14278
  if (!stat.isFile()) {
14116
14279
  return {
14117
14280
  ok: false,
@@ -14124,7 +14287,7 @@ function validateSandboxMessageDeliverables(args, policy = getSandboxPolicy(), o
14124
14287
  return {
14125
14288
  ok: false,
14126
14289
  error: "Files are delivered only via .bluma/artifacts/",
14127
- details: `Move or copy the deliverable to ${path5.relative(policy.workspaceRoot, artifactsDir2)}/ and pass that path in attachments[]. Rejected: ${raw}`
14290
+ details: `Move or copy the deliverable to ${path6.relative(policy.workspaceRoot, artifactsDir2)}/ and pass that path in attachments[]. Rejected: ${raw}`
14128
14291
  };
14129
14292
  }
14130
14293
  }
@@ -14159,18 +14322,25 @@ function normalizeMessageToolArgs(raw) {
14159
14322
  attachments = [rawAttachments.trim()];
14160
14323
  }
14161
14324
  }
14325
+ const rawUrl = raw[FACTOR_SH_URL_APP_FIELD] ?? raw.factor_sh_url_app ?? raw.factoraiShUrlApp ?? raw.factorai_sh_url_app;
14326
+ const factorShUrlApp = typeof rawUrl === "string" && rawUrl.trim() ? rawUrl.trim() : void 0;
14162
14327
  return {
14163
14328
  content,
14164
14329
  message_type,
14165
- attachments: attachments?.length ? attachments : void 0
14330
+ attachments: attachments?.length ? attachments : void 0,
14331
+ factorShUrlApp
14166
14332
  };
14167
14333
  }
14168
14334
 
14169
14335
  // src/app/agent/tools/MessageTool/MessageTool.ts
14170
14336
  function message(args) {
14171
- const { content, message_type, attachments } = normalizeMessageToolArgs(
14337
+ const { content, message_type, attachments, factorShUrlApp } = normalizeMessageToolArgs(
14172
14338
  args
14173
14339
  );
14340
+ let resolvedFactorShUrl = factorShUrlApp;
14341
+ if (message_type === "result" && getSandboxPolicy().isSandbox && !resolvedFactorShUrl) {
14342
+ resolvedFactorShUrl = resolveFactorShAppLiveUrl() ?? void 0;
14343
+ }
14174
14344
  const deliverableCheck = validateSandboxMessageDeliverables(
14175
14345
  {
14176
14346
  message_type,
@@ -14198,6 +14368,7 @@ function message(args) {
14198
14368
  body: content
14199
14369
  },
14200
14370
  attachments: Array.isArray(attachments) ? attachments : void 0,
14371
+ ...resolvedFactorShUrl ? { [FACTOR_SH_URL_APP_FIELD]: resolvedFactorShUrl } : {},
14201
14372
  success: true,
14202
14373
  delivered: true
14203
14374
  };
@@ -14205,8 +14376,8 @@ function message(args) {
14205
14376
  }
14206
14377
 
14207
14378
  // src/app/agent/tools/LsTool/LsTool.ts
14208
- import { promises as fs6 } from "fs";
14209
- import path6 from "path";
14379
+ import { promises as fs7 } from "fs";
14380
+ import path7 from "path";
14210
14381
  import os5 from "os";
14211
14382
  import { minimatch } from "minimatch";
14212
14383
  var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
@@ -14226,7 +14397,7 @@ var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
14226
14397
  ]);
14227
14398
  function expandPath(p) {
14228
14399
  if (p === "~" || p.startsWith("~/")) {
14229
- return path6.join(os5.homedir(), p.slice(2));
14400
+ return path7.join(os5.homedir(), p.slice(2));
14230
14401
  }
14231
14402
  return p;
14232
14403
  }
@@ -14252,8 +14423,8 @@ async function ls(args) {
14252
14423
  max_depth
14253
14424
  } = args;
14254
14425
  try {
14255
- const basePath = path6.resolve(expandPath(directory_path));
14256
- const stat = await fs6.stat(basePath).catch(() => null);
14426
+ const basePath = path7.resolve(expandPath(directory_path));
14427
+ const stat = await fs7.stat(basePath).catch(() => null);
14257
14428
  if (!stat || !stat.isDirectory()) {
14258
14429
  throw new Error(`Directory '${directory_path}' not found.`);
14259
14430
  }
@@ -14265,11 +14436,11 @@ async function ls(args) {
14265
14436
  const allDirs = [];
14266
14437
  const walk = async (currentDir, currentDepth) => {
14267
14438
  if (max_depth !== void 0 && currentDepth >= max_depth) return;
14268
- const entries = await fs6.readdir(currentDir, { withFileTypes: true });
14439
+ const entries = await fs7.readdir(currentDir, { withFileTypes: true });
14269
14440
  for (const entry of entries) {
14270
14441
  const entryName = entry.name;
14271
- const fullPath = path6.join(currentDir, entryName);
14272
- const relativePath = path6.relative(basePath, fullPath).split(path6.sep).join("/");
14442
+ const fullPath = path7.join(currentDir, entryName);
14443
+ const relativePath = path7.relative(basePath, fullPath).split(path7.sep).join("/");
14273
14444
  const isHidden = entryName.startsWith(".");
14274
14445
  if (shouldIgnore(entryName, allIgnorePatterns) || isHidden && !show_hidden) {
14275
14446
  continue;
@@ -14278,7 +14449,7 @@ async function ls(args) {
14278
14449
  allDirs.push(relativePath);
14279
14450
  if (recursive) await walk(fullPath, currentDepth + 1);
14280
14451
  } else if (entry.isFile()) {
14281
- if (!normalizedExtensions || normalizedExtensions.includes(path6.extname(entryName).toLowerCase())) {
14452
+ if (!normalizedExtensions || normalizedExtensions.includes(path7.extname(entryName).toLowerCase())) {
14282
14453
  allFiles.push(relativePath);
14283
14454
  }
14284
14455
  }
@@ -14291,7 +14462,7 @@ async function ls(args) {
14291
14462
  const dirEnd = end_index ?? allDirs.length;
14292
14463
  return {
14293
14464
  success: true,
14294
- path: basePath.split(path6.sep).join("/"),
14465
+ path: basePath.split(path7.sep).join("/"),
14295
14466
  recursive,
14296
14467
  total_files: allFiles.length,
14297
14468
  total_directories: allDirs.length,
@@ -14313,13 +14484,13 @@ async function ls(args) {
14313
14484
 
14314
14485
  // src/app/agent/tools/ReadLinesTool/ReadLinesTool.ts
14315
14486
  init_sandbox_policy();
14316
- import { promises as fs7 } from "fs";
14317
- import path7 from "path";
14487
+ import { promises as fs8 } from "fs";
14488
+ import path8 from "path";
14318
14489
  import os6 from "os";
14319
14490
  var DEFAULT_LINE_WINDOW = 2e3;
14320
14491
  function expandPath2(p) {
14321
14492
  if (p === "~" || p.startsWith("~/")) {
14322
- return path7.join(os6.homedir(), p.slice(2));
14493
+ return path8.join(os6.homedir(), p.slice(2));
14323
14494
  }
14324
14495
  return p;
14325
14496
  }
@@ -14337,7 +14508,7 @@ async function readLines(args) {
14337
14508
  }
14338
14509
  try {
14339
14510
  const resolvedPath = resolveWorkspacePath(expandPath2(filepath));
14340
- const stat = await fs7.stat(resolvedPath).catch(() => null);
14511
+ const stat = await fs8.stat(resolvedPath).catch(() => null);
14341
14512
  if (!stat || !stat.isFile()) {
14342
14513
  const workspaceRoot = resolveWorkspacePath(".");
14343
14514
  const isInsideWorkspace = resolvedPath.startsWith(workspaceRoot);
@@ -14356,7 +14527,7 @@ async function readLines(args) {
14356
14527
  if (endLine < startLine) {
14357
14528
  throw new Error("Invalid line range: end_line must be >= start_line.");
14358
14529
  }
14359
- const fileContent = await fs7.readFile(resolvedPath, "utf-8");
14530
+ const fileContent = await fs8.readFile(resolvedPath, "utf-8");
14360
14531
  const lines = fileContent.split("\n");
14361
14532
  const total_lines = lines.length;
14362
14533
  const startIndex = startLine - 1;
@@ -14387,7 +14558,7 @@ async function readLines(args) {
14387
14558
 
14388
14559
  // src/app/agent/tools/CountLinesTool/CountLinesTool.ts
14389
14560
  import { createReadStream } from "fs";
14390
- import { promises as fs8 } from "fs";
14561
+ import { promises as fs9 } from "fs";
14391
14562
  import readline from "readline";
14392
14563
  async function countLines(args) {
14393
14564
  const filepathInput = typeof args?.filepath === "string" && args.filepath.trim().length > 0 ? args.filepath : typeof args?.file_path === "string" ? args.file_path : "";
@@ -14414,7 +14585,7 @@ async function countLines(args) {
14414
14585
  };
14415
14586
  }
14416
14587
  try {
14417
- if (!(await fs8.stat(filepath)).isFile()) {
14588
+ if (!(await fs9.stat(filepath)).isFile()) {
14418
14589
  throw new Error(`File '${filepath}' not found or is not a file.`);
14419
14590
  }
14420
14591
  const fileStream = createReadStream(filepath);
@@ -14604,7 +14775,7 @@ function formatTodoResult(result) {
14604
14775
 
14605
14776
  // src/app/agent/tools/FindByNameTool/FindByNameTool.ts
14606
14777
  init_sandbox_policy();
14607
- import path9 from "path";
14778
+ import path10 from "path";
14608
14779
  import { promises as fsPromises } from "fs";
14609
14780
  import os7 from "os";
14610
14781
  var MAX_RESULTS = 100;
@@ -14628,7 +14799,7 @@ var DEFAULT_IGNORE2 = /* @__PURE__ */ new Set([
14628
14799
  ]);
14629
14800
  function expandTilde2(p) {
14630
14801
  if (p === "~") return os7.homedir();
14631
- if (p.startsWith("~/")) return path9.join(os7.homedir(), p.slice(2));
14802
+ if (p.startsWith("~/")) return path10.join(os7.homedir(), p.slice(2));
14632
14803
  return p;
14633
14804
  }
14634
14805
  function globToRegex(glob) {
@@ -14672,7 +14843,7 @@ function shouldIgnore2(name, extraPatterns, includeHidden) {
14672
14843
  }
14673
14844
  function matchesExtensions(filename, extensions) {
14674
14845
  if (!extensions || extensions.length === 0) return true;
14675
- const ext = path9.extname(filename).toLowerCase();
14846
+ const ext = path10.extname(filename).toLowerCase();
14676
14847
  return extensions.some((e) => {
14677
14848
  const norm = e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`;
14678
14849
  return ext === norm;
@@ -14690,8 +14861,8 @@ async function searchDirectory(dir, pattern, baseDir, options, results) {
14690
14861
  if (results.length >= MAX_RESULTS) break;
14691
14862
  const { name } = entry;
14692
14863
  if (shouldIgnore2(name, options.excludePatterns, options.includeHidden)) continue;
14693
- const fullPath = path9.join(dir, name);
14694
- const relativePath = path9.relative(baseDir, fullPath).split(path9.sep).join("/");
14864
+ const fullPath = path10.join(dir, name);
14865
+ const relativePath = path10.relative(baseDir, fullPath).split(path10.sep).join("/");
14695
14866
  if (entry.isDirectory()) {
14696
14867
  if (pattern.test(name)) {
14697
14868
  results.push({ path: relativePath, type: "directory" });
@@ -14773,7 +14944,7 @@ async function findByName(args) {
14773
14944
  }
14774
14945
 
14775
14946
  // src/app/agent/tools/GrepSearchTool/GrepSearchTool.ts
14776
- import path10 from "path";
14947
+ import path11 from "path";
14777
14948
  import { promises as fsPromises2 } from "fs";
14778
14949
  import os8 from "os";
14779
14950
  var MAX_RESULTS2 = 200;
@@ -14863,12 +15034,12 @@ var TEXT_BASENAMES = /* @__PURE__ */ new Set([
14863
15034
  ]);
14864
15035
  function expandTilde3(p) {
14865
15036
  if (p === "~") return os8.homedir();
14866
- if (p.startsWith("~/")) return path10.join(os8.homedir(), p.slice(2));
15037
+ if (p.startsWith("~/")) return path11.join(os8.homedir(), p.slice(2));
14867
15038
  return p;
14868
15039
  }
14869
15040
  function isTextFile(filepath) {
14870
- const ext = path10.extname(filepath).toLowerCase();
14871
- const base = path10.basename(filepath);
15041
+ const ext = path11.extname(filepath).toLowerCase();
15042
+ const base = path11.basename(filepath);
14872
15043
  return TEXT_EXTENSIONS.has(ext) || TEXT_BASENAMES.has(base) || base.startsWith(".") && !ext;
14873
15044
  }
14874
15045
  function shouldIgnore3(name) {
@@ -14905,7 +15076,7 @@ async function searchFile(filepath, baseDir, pattern, contextLines, maxMatchesPe
14905
15076
  if (stat.size > MAX_FILE_SIZE) return null;
14906
15077
  const content = await fsPromises2.readFile(filepath, "utf-8");
14907
15078
  const lines = content.split("\n");
14908
- const relativePath = path10.relative(baseDir, filepath).split(path10.sep).join("/");
15079
+ const relativePath = path11.relative(baseDir, filepath).split(path11.sep).join("/");
14909
15080
  const matches = [];
14910
15081
  for (let i = 0; i < lines.length && matches.length < maxMatchesPerFile; i++) {
14911
15082
  const line = lines[i];
@@ -14939,7 +15110,7 @@ async function searchDirectory2(dir, baseDir, pattern, includePatterns, contextL
14939
15110
  for (const entry of entries) {
14940
15111
  if (stats.totalMatches >= maxResults) break;
14941
15112
  if (shouldIgnore3(entry.name)) continue;
14942
- const fullPath = path10.join(dir, entry.name);
15113
+ const fullPath = path11.join(dir, entry.name);
14943
15114
  if (entry.isDirectory()) {
14944
15115
  await searchDirectory2(fullPath, baseDir, pattern, includePatterns, contextLines, maxResults, maxMatchesPerFile, results, stats);
14945
15116
  } else if (entry.isFile()) {
@@ -14980,7 +15151,7 @@ async function grepSearch(args) {
14980
15151
  });
14981
15152
  if (!query || typeof query !== "string") return empty("query is required");
14982
15153
  if (!searchPath) return empty("path is required");
14983
- const resolvedPath = path10.resolve(expandTilde3(searchPath));
15154
+ const resolvedPath = path11.resolve(expandTilde3(searchPath));
14984
15155
  const stat = await fsPromises2.stat(resolvedPath).catch(() => null);
14985
15156
  if (!stat) return empty(`Path not found: ${resolvedPath}`);
14986
15157
  let pattern;
@@ -14996,7 +15167,7 @@ async function grepSearch(args) {
14996
15167
  await searchDirectory2(resolvedPath, resolvedPath, pattern, include_patterns, context_lines, max_results, max_matches_per_file, results, stats);
14997
15168
  } else if (stat.isFile()) {
14998
15169
  stats.filesSearched = 1;
14999
- const fileResult = await searchFile(resolvedPath, path10.dirname(resolvedPath), pattern, context_lines, max_matches_per_file);
15170
+ const fileResult = await searchFile(resolvedPath, path11.dirname(resolvedPath), pattern, context_lines, max_matches_per_file);
15000
15171
  if (fileResult) {
15001
15172
  results.push(fileResult);
15002
15173
  stats.totalMatches = fileResult.match_count;
@@ -15019,7 +15190,7 @@ async function grepSearch(args) {
15019
15190
  }
15020
15191
 
15021
15192
  // src/app/agent/tools/ViewFileOutlineTool/ViewFileOutlineTool.ts
15022
- import path11 from "path";
15193
+ import path12 from "path";
15023
15194
  import { promises as fsPromises3 } from "fs";
15024
15195
  var LANGUAGE_MAP = {
15025
15196
  ".ts": "typescript",
@@ -15133,7 +15304,7 @@ var PATTERNS = {
15133
15304
  ]
15134
15305
  };
15135
15306
  function detectLanguage(filepath) {
15136
- const ext = path11.extname(filepath).toLowerCase();
15307
+ const ext = path12.extname(filepath).toLowerCase();
15137
15308
  return LANGUAGE_MAP[ext] || "unknown";
15138
15309
  }
15139
15310
  function determineItemType(line, language) {
@@ -15229,7 +15400,7 @@ async function viewFileOutline(args) {
15229
15400
  error: "file_path is required and must be a string"
15230
15401
  };
15231
15402
  }
15232
- const resolvedPath = path11.resolve(file_path);
15403
+ const resolvedPath = path12.resolve(file_path);
15233
15404
  let content;
15234
15405
  try {
15235
15406
  content = await fsPromises3.readFile(resolvedPath, "utf-8");
@@ -15275,21 +15446,21 @@ init_CommandStatusTool();
15275
15446
  // src/app/agent/tools/TaskBoundaryTool/TaskBoundaryTool.ts
15276
15447
  init_sandbox_policy();
15277
15448
  init_task_store();
15278
- import path12 from "path";
15279
- import { promises as fs10 } from "fs";
15449
+ import path13 from "path";
15450
+ import { promises as fs11 } from "fs";
15280
15451
  var artifactsDir = null;
15281
15452
  async function getArtifactsDir() {
15282
15453
  if (artifactsDir) return artifactsDir;
15283
15454
  const policy = getSandboxPolicy();
15284
- const baseDir = path12.join(policy.workspaceRoot, ".bluma", "artifacts");
15455
+ const baseDir = path13.join(policy.workspaceRoot, ".bluma", "artifacts");
15285
15456
  const sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
15286
- artifactsDir = path12.join(baseDir, sessionId);
15287
- await fs10.mkdir(artifactsDir, { recursive: true });
15457
+ artifactsDir = path13.join(baseDir, sessionId);
15458
+ await fs11.mkdir(artifactsDir, { recursive: true });
15288
15459
  return artifactsDir;
15289
15460
  }
15290
15461
  async function updateTaskFile(task) {
15291
15462
  const dir = await getArtifactsDir();
15292
- const taskFile = path12.join(dir, "task.md");
15463
+ const taskFile = path13.join(dir, "task.md");
15293
15464
  const content = `# ${task.taskName}
15294
15465
 
15295
15466
  **Mode:** ${task.mode}
@@ -15301,7 +15472,7 @@ async function updateTaskFile(task) {
15301
15472
  ## Summary
15302
15473
  ${task.summary}
15303
15474
  `;
15304
- await fs10.writeFile(taskFile, content, "utf-8");
15475
+ await fs11.writeFile(taskFile, content, "utf-8");
15305
15476
  }
15306
15477
  async function taskBoundary(args) {
15307
15478
  try {
@@ -15417,8 +15588,8 @@ async function readArtifact(args) {
15417
15588
  return { success: false, error: "filename is required" };
15418
15589
  }
15419
15590
  const dir = await getArtifactsDir();
15420
- const filepath = path12.join(dir, filename);
15421
- const content = await fs10.readFile(filepath, "utf-8");
15591
+ const filepath = path13.join(dir, filename);
15592
+ const content = await fs11.readFile(filepath, "utf-8");
15422
15593
  return {
15423
15594
  success: true,
15424
15595
  content
@@ -15962,8 +16133,8 @@ ${skill.content}`;
15962
16133
  }
15963
16134
 
15964
16135
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryTool.ts
15965
- import * as fs11 from "fs";
15966
- import * as path13 from "path";
16136
+ import * as fs12 from "fs";
16137
+ import * as path14 from "path";
15967
16138
  import os10 from "os";
15968
16139
  var PROMPT_DEFAULT_MAX_TOTAL = 1e4;
15969
16140
  var PROMPT_DEFAULT_MAX_NOTES = 25;
@@ -15972,14 +16143,14 @@ function readCodingMemoryForPrompt(options) {
15972
16143
  const maxTotal = options?.maxTotalChars ?? PROMPT_DEFAULT_MAX_TOTAL;
15973
16144
  const maxNotes = options?.maxNotes ?? PROMPT_DEFAULT_MAX_NOTES;
15974
16145
  const preview = options?.previewCharsPerNote ?? PROMPT_DEFAULT_PREVIEW;
15975
- const globalPath = path13.join(os10.homedir(), ".bluma", "coding_memory.json");
15976
- const legacyPath = path13.join(process.cwd(), ".bluma", "coding_memory.json");
16146
+ const globalPath = path14.join(os10.homedir(), ".bluma", "coding_memory.json");
16147
+ const legacyPath = path14.join(process.cwd(), ".bluma", "coding_memory.json");
15977
16148
  let raw = null;
15978
16149
  try {
15979
- if (fs11.existsSync(globalPath)) {
15980
- raw = fs11.readFileSync(globalPath, "utf-8");
15981
- } else if (path13.resolve(globalPath) !== path13.resolve(legacyPath) && fs11.existsSync(legacyPath)) {
15982
- raw = fs11.readFileSync(legacyPath, "utf-8");
16150
+ if (fs12.existsSync(globalPath)) {
16151
+ raw = fs12.readFileSync(globalPath, "utf-8");
16152
+ } else if (path14.resolve(globalPath) !== path14.resolve(legacyPath) && fs12.existsSync(legacyPath)) {
16153
+ raw = fs12.readFileSync(legacyPath, "utf-8");
15983
16154
  }
15984
16155
  } catch {
15985
16156
  return "";
@@ -16017,10 +16188,10 @@ var memoryStore = [];
16017
16188
  var nextId = 1;
16018
16189
  var loaded = false;
16019
16190
  function getMemoryFilePath() {
16020
- return path13.join(os10.homedir(), ".bluma", "coding_memory.json");
16191
+ return path14.join(os10.homedir(), ".bluma", "coding_memory.json");
16021
16192
  }
16022
16193
  function getLegacyMemoryFilePath() {
16023
- return path13.join(process.cwd(), ".bluma", "coding_memory.json");
16194
+ return path14.join(process.cwd(), ".bluma", "coding_memory.json");
16024
16195
  }
16025
16196
  function loadMemoryFromFile() {
16026
16197
  if (loaded) return;
@@ -16030,19 +16201,19 @@ function loadMemoryFromFile() {
16030
16201
  try {
16031
16202
  const filePath = getMemoryFilePath();
16032
16203
  const legacy = getLegacyMemoryFilePath();
16033
- const legacyDistinct = path13.resolve(legacy) !== path13.resolve(filePath);
16204
+ const legacyDistinct = path14.resolve(legacy) !== path14.resolve(filePath);
16034
16205
  const readIntoStore = (p) => {
16035
- const raw = fs11.readFileSync(p, "utf-8");
16206
+ const raw = fs12.readFileSync(p, "utf-8");
16036
16207
  const parsed = JSON.parse(raw);
16037
16208
  if (Array.isArray(parsed.entries)) {
16038
16209
  memoryStore = parsed.entries;
16039
16210
  nextId = typeof parsed.nextId === "number" ? parsed.nextId : memoryStore.length + 1;
16040
16211
  }
16041
16212
  };
16042
- if (fs11.existsSync(filePath)) {
16213
+ if (fs12.existsSync(filePath)) {
16043
16214
  readIntoStore(filePath);
16044
16215
  }
16045
- if (memoryStore.length === 0 && legacyDistinct && fs11.existsSync(legacy)) {
16216
+ if (memoryStore.length === 0 && legacyDistinct && fs12.existsSync(legacy)) {
16046
16217
  readIntoStore(legacy);
16047
16218
  if (memoryStore.length > 0) {
16048
16219
  saveMemoryToFile();
@@ -16056,16 +16227,16 @@ function loadMemoryFromFile() {
16056
16227
  function saveMemoryToFile() {
16057
16228
  try {
16058
16229
  const filePath = getMemoryFilePath();
16059
- const dir = path13.dirname(filePath);
16060
- if (!fs11.existsSync(dir)) {
16061
- fs11.mkdirSync(dir, { recursive: true });
16230
+ const dir = path14.dirname(filePath);
16231
+ if (!fs12.existsSync(dir)) {
16232
+ fs12.mkdirSync(dir, { recursive: true });
16062
16233
  }
16063
16234
  const payload = {
16064
16235
  entries: memoryStore,
16065
16236
  nextId,
16066
16237
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
16067
16238
  };
16068
- fs11.writeFileSync(filePath, JSON.stringify(payload, null, 2));
16239
+ fs12.writeFileSync(filePath, JSON.stringify(payload, null, 2));
16069
16240
  } catch {
16070
16241
  }
16071
16242
  }
@@ -16567,7 +16738,7 @@ async function cronDelete(args) {
16567
16738
 
16568
16739
  // src/app/agent/tools/NotebookEditTool/NotebookEditTool.ts
16569
16740
  init_sandbox_policy();
16570
- import { promises as fs12 } from "fs";
16741
+ import { promises as fs13 } from "fs";
16571
16742
  function sourceToString(s) {
16572
16743
  if (s == null) return "";
16573
16744
  return Array.isArray(s) ? s.join("") : s;
@@ -16579,7 +16750,7 @@ function stringToSource(s) {
16579
16750
  async function applyNotebookOperation(filepath, op) {
16580
16751
  try {
16581
16752
  const resolved = resolveWorkspacePath(filepath);
16582
- const raw = await fs12.readFile(resolved, "utf-8");
16753
+ const raw = await fs13.readFile(resolved, "utf-8");
16583
16754
  let doc;
16584
16755
  try {
16585
16756
  doc = JSON.parse(raw);
@@ -16620,7 +16791,7 @@ async function applyNotebookOperation(filepath, op) {
16620
16791
  return JSON.stringify({ success: false, error: `cell_index out of range: ${i}` });
16621
16792
  }
16622
16793
  doc.cells[i].source = stringToSource(String(op.source ?? ""));
16623
- await fs12.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
16794
+ await fs13.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
16624
16795
  return JSON.stringify({ success: true, message: `Updated cell ${i}` });
16625
16796
  }
16626
16797
  if (op.operation === "append_markdown_cell") {
@@ -16629,7 +16800,7 @@ async function applyNotebookOperation(filepath, op) {
16629
16800
  source: stringToSource(String(op.source ?? "")),
16630
16801
  metadata: {}
16631
16802
  });
16632
- await fs12.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
16803
+ await fs13.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
16633
16804
  return JSON.stringify({ success: true, message: `Appended markdown cell at index ${doc.cells.length - 1}` });
16634
16805
  }
16635
16806
  if (op.operation === "delete_cell") {
@@ -16638,7 +16809,7 @@ async function applyNotebookOperation(filepath, op) {
16638
16809
  return JSON.stringify({ success: false, error: `cell_index out of range: ${i}` });
16639
16810
  }
16640
16811
  doc.cells.splice(i, 1);
16641
- await fs12.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
16812
+ await fs13.writeFile(resolved, JSON.stringify(doc, null, 2), "utf-8");
16642
16813
  return JSON.stringify({ success: true, message: `Deleted cell ${i}` });
16643
16814
  }
16644
16815
  return JSON.stringify({ success: false, error: "unknown operation" });
@@ -16677,8 +16848,8 @@ async function notebook_edit(args) {
16677
16848
  // src/app/agent/tools/LspQueryTool/LspQueryTool.ts
16678
16849
  init_sandbox_policy();
16679
16850
  import { spawn as spawn2 } from "child_process";
16680
- import * as fs13 from "fs";
16681
- import * as path14 from "path";
16851
+ import * as fs14 from "fs";
16852
+ import * as path15 from "path";
16682
16853
  import { pathToFileURL } from "url";
16683
16854
  var RpcBuffer = class {
16684
16855
  buf = Buffer.alloc(0);
@@ -16728,12 +16899,12 @@ async function lsp_query(args) {
16728
16899
  } catch (e) {
16729
16900
  return JSON.stringify({ success: false, error: e.message || String(e) });
16730
16901
  }
16731
- if (!fs13.existsSync(resolved) || !fs13.statSync(resolved).isFile()) {
16902
+ if (!fs14.existsSync(resolved) || !fs14.statSync(resolved).isFile()) {
16732
16903
  return JSON.stringify({ success: false, error: "file not found" });
16733
16904
  }
16734
- const content = fs13.readFileSync(resolved, "utf-8");
16905
+ const content = fs14.readFileSync(resolved, "utf-8");
16735
16906
  const uri = pathToFileURL(resolved).href;
16736
- const root = path14.dirname(resolved);
16907
+ const root = path15.dirname(resolved);
16737
16908
  const line0 = Math.max(0, Math.floor(Number(args.line) || 1) - 1);
16738
16909
  const character = Math.max(0, Math.floor(Number(args.character ?? 0)));
16739
16910
  const operation = args.operation === "references" ? "references" : "definition";
@@ -16853,8 +17024,8 @@ async function lsp_query(args) {
16853
17024
 
16854
17025
  // src/app/agent/tools/FileWriteTool/FileWriteTool.ts
16855
17026
  init_sandbox_policy();
16856
- import { promises as fs14 } from "fs";
16857
- import path15 from "path";
17027
+ import { promises as fs15 } from "fs";
17028
+ import path16 from "path";
16858
17029
  async function fileWrite(args) {
16859
17030
  const {
16860
17031
  filepath,
@@ -16872,10 +17043,10 @@ async function fileWrite(args) {
16872
17043
  return { success: false, error: "content is required" };
16873
17044
  }
16874
17045
  const resolvedPath = resolveWorkspacePath(targetPath);
16875
- const dir = path15.dirname(resolvedPath);
17046
+ const dir = path16.dirname(resolvedPath);
16876
17047
  let existed = false;
16877
17048
  try {
16878
- const st = await fs14.stat(resolvedPath);
17049
+ const st = await fs15.stat(resolvedPath);
16879
17050
  existed = st.isFile();
16880
17051
  } catch {
16881
17052
  existed = false;
@@ -16889,15 +17060,15 @@ async function fileWrite(args) {
16889
17060
  let previousContent = "";
16890
17061
  if (existed) {
16891
17062
  try {
16892
- previousContent = await fs14.readFile(resolvedPath, "utf-8");
17063
+ previousContent = await fs15.readFile(resolvedPath, "utf-8");
16893
17064
  } catch {
16894
17065
  previousContent = "";
16895
17066
  }
16896
17067
  }
16897
17068
  if (create_directories) {
16898
- await fs14.mkdir(dir, { recursive: true });
17069
+ await fs15.mkdir(dir, { recursive: true });
16899
17070
  }
16900
- await fs14.writeFile(resolvedPath, String(content), "utf-8");
17071
+ await fs15.writeFile(resolvedPath, String(content), "utf-8");
16901
17072
  const bytes = Buffer.byteLength(String(content), "utf-8");
16902
17073
  return {
16903
17074
  success: true,
@@ -17206,9 +17377,9 @@ async function signalMailbox(args) {
17206
17377
  }
17207
17378
 
17208
17379
  // src/app/agent/tools/ReplTool/ReplTool.ts
17209
- import fs19 from "fs";
17380
+ import fs20 from "fs";
17210
17381
  import os15 from "os";
17211
- import path20 from "path";
17382
+ import path21 from "path";
17212
17383
  import { exec } from "child_process";
17213
17384
  import { v4 as uuidv47 } from "uuid";
17214
17385
  import { promisify } from "util";
@@ -17220,20 +17391,20 @@ async function repl(params) {
17220
17391
  let tempFile;
17221
17392
  switch (language) {
17222
17393
  case "python": {
17223
- tempFile = path20.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.py`);
17224
- fs19.writeFileSync(tempFile, code, "utf-8");
17394
+ tempFile = path21.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.py`);
17395
+ fs20.writeFileSync(tempFile, code, "utf-8");
17225
17396
  command = `python3 "${tempFile}"`;
17226
17397
  break;
17227
17398
  }
17228
17399
  case "node": {
17229
- tempFile = path20.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.mjs`);
17230
- fs19.writeFileSync(tempFile, code, "utf-8");
17400
+ tempFile = path21.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.mjs`);
17401
+ fs20.writeFileSync(tempFile, code, "utf-8");
17231
17402
  command = `node "${tempFile}"`;
17232
17403
  break;
17233
17404
  }
17234
17405
  case "bash": {
17235
- tempFile = path20.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.sh`);
17236
- fs19.writeFileSync(tempFile, `#!/bin/bash
17406
+ tempFile = path21.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.sh`);
17407
+ fs20.writeFileSync(tempFile, `#!/bin/bash
17237
17408
  set -e
17238
17409
  ${code}`, "utf-8");
17239
17410
  command = `bash "${tempFile}"`;
@@ -17247,7 +17418,7 @@ ${code}`, "utf-8");
17247
17418
  // 5MB
17248
17419
  });
17249
17420
  try {
17250
- fs19.unlinkSync(tempFile);
17421
+ fs20.unlinkSync(tempFile);
17251
17422
  } catch {
17252
17423
  }
17253
17424
  return {
@@ -17259,7 +17430,7 @@ ${code}`, "utf-8");
17259
17430
  };
17260
17431
  } catch (error) {
17261
17432
  try {
17262
- fs19.unlinkSync(tempFile);
17433
+ fs20.unlinkSync(tempFile);
17263
17434
  } catch {
17264
17435
  }
17265
17436
  const exitCode = error.code === "ERR_CHILD_PROCESS_TIMEOUT" ? 124 : error.status ?? 1;
@@ -17381,6 +17552,24 @@ function countToolDefinitionsTokens(tools) {
17381
17552
  const json = JSON.stringify(tools);
17382
17553
  return enc.encode(json).length;
17383
17554
  }
17555
+ var DEFAULT_OUTPUT_TOKEN_RESERVE = 8192;
17556
+ var DEFAULT_PROTOCOL_OVERHEAD_TOKENS = 512;
17557
+ function computeEffectiveInputBudget(rawBudget, toolDefinitions = [], options) {
17558
+ const toolDefinitionsTokens = countToolDefinitionsTokens(toolDefinitions);
17559
+ const outputReserveTokens = options?.outputReserveTokens ?? DEFAULT_OUTPUT_TOKEN_RESERVE;
17560
+ const protocolOverheadTokens = options?.protocolOverheadTokens ?? DEFAULT_PROTOCOL_OVERHEAD_TOKENS;
17561
+ const effectiveBudget = Math.max(
17562
+ 0,
17563
+ rawBudget - toolDefinitionsTokens - outputReserveTokens - protocolOverheadTokens
17564
+ );
17565
+ return {
17566
+ rawBudget,
17567
+ toolDefinitionsTokens,
17568
+ outputReserveTokens,
17569
+ protocolOverheadTokens,
17570
+ effectiveBudget
17571
+ };
17572
+ }
17384
17573
 
17385
17574
  // src/app/agent/tools/CtxInspectTool/CtxInspectTool.ts
17386
17575
  async function ctx_inspect(args = {}) {
@@ -17607,8 +17796,8 @@ ${preview}${toRemove.length > 5 ? `
17607
17796
 
17608
17797
  // src/app/agent/tools/BriefTool/BriefTool.ts
17609
17798
  init_sandbox_policy();
17610
- import * as fs20 from "fs";
17611
- import * as path21 from "path";
17799
+ import * as fs21 from "fs";
17800
+ import * as path22 from "path";
17612
17801
  async function brief(args) {
17613
17802
  const sentAt = (/* @__PURE__ */ new Date()).toISOString();
17614
17803
  if (!args.message || !args.message.trim()) {
@@ -17627,9 +17816,9 @@ async function brief(args) {
17627
17816
  const resolved = [];
17628
17817
  for (const filePath of args.attachments) {
17629
17818
  try {
17630
- const absolutePath = path21.isAbsolute(filePath) ? filePath : path21.join(resolveWorkspacePath("."), filePath);
17631
- if (fs20.existsSync(absolutePath)) {
17632
- const stat = fs20.statSync(absolutePath);
17819
+ const absolutePath = path22.isAbsolute(filePath) ? filePath : path22.join(resolveWorkspacePath("."), filePath);
17820
+ if (fs21.existsSync(absolutePath)) {
17821
+ const stat = fs21.statSync(absolutePath);
17633
17822
  resolved.push({
17634
17823
  path: absolutePath,
17635
17824
  size: stat.size,
@@ -17668,14 +17857,14 @@ async function brief(args) {
17668
17857
  }
17669
17858
 
17670
17859
  // src/app/agent/tools/DreamEngineTool/DreamEngineTool.ts
17671
- import * as fs21 from "fs";
17672
- import * as path22 from "path";
17860
+ import * as fs22 from "fs";
17861
+ import * as path23 from "path";
17673
17862
  import os16 from "os";
17674
17863
  function memoryPath() {
17675
- return path22.join(process.env.HOME || os16.homedir(), ".bluma", "coding_memory.json");
17864
+ return path23.join(process.env.HOME || os16.homedir(), ".bluma", "coding_memory.json");
17676
17865
  }
17677
17866
  function sessionsDir() {
17678
- return path22.join(process.env.HOME || os16.homedir(), ".bluma", "sessions");
17867
+ return path23.join(process.env.HOME || os16.homedir(), ".bluma", "sessions");
17679
17868
  }
17680
17869
  function normalizeNote(note) {
17681
17870
  return note.trim().toLowerCase().replace(/\s+/g, " ");
@@ -17700,9 +17889,9 @@ async function dream(args = {}) {
17700
17889
  const mergeSimilar = args.mergeSimilar !== false;
17701
17890
  const memPath = memoryPath();
17702
17891
  let memData = null;
17703
- if (fs21.existsSync(memPath)) {
17892
+ if (fs22.existsSync(memPath)) {
17704
17893
  try {
17705
- memData = JSON.parse(fs21.readFileSync(memPath, "utf-8"));
17894
+ memData = JSON.parse(fs22.readFileSync(memPath, "utf-8"));
17706
17895
  } catch {
17707
17896
  }
17708
17897
  }
@@ -17779,12 +17968,12 @@ async function dream(args = {}) {
17779
17968
  pruned += removed.length;
17780
17969
  }
17781
17970
  const sessDir = sessionsDir();
17782
- if (fs21.existsSync(sessDir)) {
17971
+ if (fs22.existsSync(sessDir)) {
17783
17972
  try {
17784
- const sessionFiles = fs21.readdirSync(sessDir).filter((f) => f.endsWith(".json")).slice(-5);
17973
+ const sessionFiles = fs22.readdirSync(sessDir).filter((f) => f.endsWith(".json")).slice(-5);
17785
17974
  for (const sf of sessionFiles) {
17786
17975
  try {
17787
- const sessionData = JSON.parse(fs21.readFileSync(path22.join(sessDir, sf), "utf-8"));
17976
+ const sessionData = JSON.parse(fs22.readFileSync(path23.join(sessDir, sf), "utf-8"));
17788
17977
  if (sessionData?.summary) {
17789
17978
  consolidatedMemories.push(`Session ${sf}: ${sessionData.summary}`);
17790
17979
  }
@@ -17799,7 +17988,7 @@ async function dream(args = {}) {
17799
17988
  memData.nextId = entries.reduce((max, e) => Math.max(max, e.id || 0), 0) + 1;
17800
17989
  memData.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
17801
17990
  try {
17802
- fs21.writeFileSync(memPath, JSON.stringify(memData, null, 2), "utf-8");
17991
+ fs22.writeFileSync(memPath, JSON.stringify(memData, null, 2), "utf-8");
17803
17992
  } catch (e) {
17804
17993
  return {
17805
17994
  success: false,
@@ -17992,8 +18181,8 @@ async function context_collapse(args = {}) {
17992
18181
 
17993
18182
  // src/app/agent/tools/CreateNextAppTool/CreateNextAppTool.ts
17994
18183
  init_sandbox_policy();
17995
- import { promises as fs22 } from "fs";
17996
- import path23 from "path";
18184
+ import { promises as fs23 } from "fs";
18185
+ import path24 from "path";
17997
18186
 
17998
18187
  // src/app/agent/tools/ShellCommandTool/ShellCommandTool.ts
17999
18188
  init_sandbox_policy();
@@ -18422,6 +18611,40 @@ yarn-error.log*
18422
18611
  # typescript
18423
18612
  *.tsbuildinfo
18424
18613
  next-env.d.ts
18614
+ `,
18615
+ // App Router mínimo (obrigatório para next build / deploy no Severino)
18616
+ "app/layout.tsx": `import type { Metadata } from 'next';
18617
+ import './globals.css';
18618
+
18619
+ export const metadata: Metadata = {
18620
+ title: '{{NAME}}',
18621
+ description: 'Generated by create-next-app',
18622
+ };
18623
+
18624
+ export default function RootLayout({
18625
+ children,
18626
+ }: Readonly<{
18627
+ children: React.ReactNode;
18628
+ }>) {
18629
+ return (
18630
+ <html lang="en">
18631
+ <body>{children}</body>
18632
+ </html>
18633
+ );
18634
+ }
18635
+ `,
18636
+ "app/page.tsx": `export default function Home() {
18637
+ return (
18638
+ <main className="flex min-h-screen flex-col items-center justify-center p-8">
18639
+ <h1 className="text-2xl font-semibold">{{NAME}}</h1>
18640
+ <p className="mt-2 text-sm text-gray-600">Next.js app ready for deploy.</p>
18641
+ </main>
18642
+ );
18643
+ }
18644
+ `,
18645
+ "app/globals.css": `@tailwind base;
18646
+ @tailwind components;
18647
+ @tailwind utilities;
18425
18648
  `
18426
18649
  };
18427
18650
  var FULL_FILES = {
@@ -18566,25 +18789,22 @@ body {
18566
18789
  }
18567
18790
  }
18568
18791
  `,
18569
- // shadcn/ui base components
18792
+ // shadcn/ui base components (sem @radix-ui/react-slot — evita falha de build no deploy)
18570
18793
  "components/ui/button.tsx": `import * as React from "react"
18571
18794
  import { cva, type VariantProps } from "class-variance-authority"
18572
18795
  import { cn } from "@/lib/utils"
18573
18796
 
18574
18797
  const buttonVariants = cva(
18575
- "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
18798
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
18576
18799
  {
18577
18800
  variants: {
18578
18801
  variant: {
18579
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
18580
- destructive:
18581
- "bg-destructive text-destructive-foreground hover:bg-destructive/90",
18582
- outline:
18583
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
18584
- secondary:
18585
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
18586
- ghost: "hover:bg-accent hover:text-accent-foreground",
18587
- link: "text-primary underline-offset-4 hover:underline",
18802
+ default: "bg-zinc-900 text-white hover:bg-zinc-800",
18803
+ destructive: "bg-red-600 text-white hover:bg-red-700",
18804
+ outline: "border border-zinc-200 bg-white hover:bg-zinc-50",
18805
+ secondary: "bg-zinc-100 text-zinc-900 hover:bg-zinc-200",
18806
+ ghost: "hover:bg-zinc-100",
18807
+ link: "text-zinc-900 underline-offset-4 hover:underline",
18588
18808
  },
18589
18809
  size: {
18590
18810
  default: "h-10 px-4 py-2",
@@ -18602,21 +18822,16 @@ const buttonVariants = cva(
18602
18822
 
18603
18823
  export interface ButtonProps
18604
18824
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
18605
- VariantProps<typeof buttonVariants> {
18606
- asChild?: boolean
18607
- }
18825
+ VariantProps<typeof buttonVariants> {}
18608
18826
 
18609
18827
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
18610
- ({ className, variant, size, asChild = false, ...props }, ref) => {
18611
- const Comp = asChild ? Slot : "button"
18612
- return (
18613
- <Comp
18614
- className={cn(buttonVariants({ variant, size, className }))}
18615
- ref={ref}
18616
- {...props}
18617
- />
18618
- )
18619
- }
18828
+ ({ className, variant, size, ...props }, ref) => (
18829
+ <button
18830
+ className={cn(buttonVariants({ variant, size, className }))}
18831
+ ref={ref}
18832
+ {...props}
18833
+ />
18834
+ )
18620
18835
  )
18621
18836
  Button.displayName = "Button"
18622
18837
 
@@ -18632,7 +18847,7 @@ const Card = React.forwardRef<
18632
18847
  <div
18633
18848
  ref={ref}
18634
18849
  className={cn(
18635
- "rounded-lg border bg-card text-card-foreground shadow-sm",
18850
+ "rounded-lg border border-zinc-200 bg-white text-zinc-950 shadow-sm",
18636
18851
  className
18637
18852
  )}
18638
18853
  {...props}
@@ -18673,7 +18888,7 @@ const CardDescription = React.forwardRef<
18673
18888
  >(({ className, ...props }, ref) => (
18674
18889
  <p
18675
18890
  ref={ref}
18676
- className={cn("text-sm text-muted-foreground", className)}
18891
+ className={cn("text-sm text-zinc-500", className)}
18677
18892
  {...props}
18678
18893
  />
18679
18894
  ))
@@ -18713,7 +18928,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
18713
18928
  <input
18714
18929
  type={type}
18715
18930
  className={cn(
18716
- "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
18931
+ "flex h-10 w-full rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-zinc-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
18717
18932
  className
18718
18933
  )}
18719
18934
  ref={ref}
@@ -18767,10 +18982,10 @@ export function cn(...inputs: ClassValue[]) {
18767
18982
  `
18768
18983
  };
18769
18984
  async function createFile(filePath, content, projectName) {
18770
- const dir = path23.dirname(filePath);
18771
- await fs22.mkdir(dir, { recursive: true });
18985
+ const dir = path24.dirname(filePath);
18986
+ await fs23.mkdir(dir, { recursive: true });
18772
18987
  const replacedContent = content.replace(/{{NAME}}/g, projectName);
18773
- await fs22.writeFile(filePath, replacedContent, "utf-8");
18988
+ await fs23.writeFile(filePath, replacedContent, "utf-8");
18774
18989
  }
18775
18990
  async function createNextApp(args) {
18776
18991
  const {
@@ -18789,26 +19004,26 @@ async function createNextApp(args) {
18789
19004
  };
18790
19005
  }
18791
19006
  const baseDir = directory ? resolveWorkspacePath(directory) : resolveWorkspacePath(".");
18792
- const projectPath = path23.join(baseDir, name);
19007
+ const projectPath = path24.join(baseDir, name);
18793
19008
  try {
18794
- await fs22.access(projectPath);
19009
+ await fs23.access(projectPath);
18795
19010
  return {
18796
19011
  success: false,
18797
19012
  error: `Directory already exists: ${projectPath}. Remove it first or choose a different name.`
18798
19013
  };
18799
19014
  } catch {
18800
19015
  }
18801
- await fs22.mkdir(projectPath, { recursive: true });
19016
+ await fs23.mkdir(projectPath, { recursive: true });
18802
19017
  const filesToCreate = template === "minimal" ? MINIMAL_FILES : FULL_FILES;
18803
19018
  const filesCreated = [];
18804
19019
  for (const [relativePath, content] of Object.entries(filesToCreate)) {
18805
19020
  if (relativePath === "package.json.full") continue;
18806
- const fullPath = path23.join(projectPath, relativePath);
19021
+ const fullPath = path24.join(projectPath, relativePath);
18807
19022
  await createFile(fullPath, content, name);
18808
19023
  filesCreated.push(relativePath);
18809
19024
  }
18810
19025
  if (template === "full") {
18811
- const packageJsonPath = path23.join(projectPath, "package.json");
19026
+ const packageJsonPath = path24.join(projectPath, "package.json");
18812
19027
  const fullPackageJson = FULL_FILES["package.json.full"];
18813
19028
  if (fullPackageJson) {
18814
19029
  await createFile(packageJsonPath, fullPackageJson, name);
@@ -18850,9 +19065,14 @@ async function createNextApp(args) {
18850
19065
 
18851
19066
  // src/app/agent/tools/DeployAppTool/DeployAppTool.ts
18852
19067
  init_sandbox_policy();
18853
- import { promises as fs23 } from "fs";
18854
- import path24 from "path";
18855
- var EXCLUDE_PATTERNS = [
19068
+ import { promises as fs24 } from "fs";
19069
+ import path26 from "path";
19070
+
19071
+ // src/app/agent/tools/DeployAppTool/createDeployProjectZip.ts
19072
+ import { lstat, readdir, readFile, writeFile } from "fs/promises";
19073
+ import path25 from "path";
19074
+ import { minimatch as minimatch2 } from "minimatch";
19075
+ var DEPLOY_ZIP_EXCLUDE_PATTERNS = [
18856
19076
  "node_modules",
18857
19077
  ".next",
18858
19078
  ".git",
@@ -18865,58 +19085,116 @@ var EXCLUDE_PATTERNS = [
18865
19085
  ".env*.local",
18866
19086
  "coverage",
18867
19087
  ".vercel",
18868
- ".turbo"
19088
+ ".turbo",
19089
+ ".tmp"
18869
19090
  ];
18870
- async function createProjectZip(projectDir, zipPath) {
18871
- const excludes = [];
18872
- for (const pattern of EXCLUDE_PATTERNS) {
18873
- excludes.push("--exclude", pattern);
18874
- }
18875
- const zipCommand = `zip -r "${zipPath}" . ${excludes.join(" ")}`;
18876
- const result = await shellCommand({
18877
- command: zipCommand,
18878
- cwd: projectDir,
18879
- timeout: 120
18880
- // 2 minutos para zippar
18881
- });
18882
- const resultJson = JSON.parse(result);
18883
- if (resultJson.status !== "success") {
18884
- throw new Error(`Failed to create ZIP: ${resultJson.stderr}`);
19091
+ function pathSegments(relativePosix) {
19092
+ return relativePosix.split("/").filter(Boolean);
19093
+ }
19094
+ function shouldExcludeFromDeployZip(relativePosix, isDirectory) {
19095
+ const rel = relativePosix.replace(/\\/g, "/").replace(/^\.\//, "");
19096
+ if (!rel) return false;
19097
+ const segments = pathSegments(rel);
19098
+ const baseName = path25.posix.basename(rel);
19099
+ for (const pattern of DEPLOY_ZIP_EXCLUDE_PATTERNS) {
19100
+ if (pattern.includes("*")) {
19101
+ if (minimatch2(rel, pattern) || minimatch2(baseName, pattern)) {
19102
+ return true;
19103
+ }
19104
+ continue;
19105
+ }
19106
+ if (segments.includes(pattern)) {
19107
+ return true;
19108
+ }
19109
+ if (!isDirectory && baseName === pattern) {
19110
+ return true;
19111
+ }
19112
+ }
19113
+ return false;
19114
+ }
19115
+ async function collectDeployZipEntries(projectDir, files) {
19116
+ const base = path25.resolve(projectDir);
19117
+ async function walk(currentDir) {
19118
+ let entries;
19119
+ try {
19120
+ entries = await readdir(currentDir, { withFileTypes: true });
19121
+ } catch {
19122
+ return;
19123
+ }
19124
+ for (const entry of entries) {
19125
+ const fullPath = path25.join(currentDir, entry.name);
19126
+ const rel = path25.relative(base, fullPath).replace(/\\/g, "/");
19127
+ if (shouldExcludeFromDeployZip(rel, entry.isDirectory())) {
19128
+ continue;
19129
+ }
19130
+ if (entry.isSymbolicLink()) {
19131
+ try {
19132
+ const st = await lstat(fullPath);
19133
+ if (st.isDirectory()) continue;
19134
+ } catch {
19135
+ continue;
19136
+ }
19137
+ }
19138
+ if (entry.isDirectory()) {
19139
+ await walk(fullPath);
19140
+ continue;
19141
+ }
19142
+ if (!entry.isFile()) {
19143
+ continue;
19144
+ }
19145
+ try {
19146
+ const content = await readFile(fullPath);
19147
+ files[rel] = new Uint8Array(content);
19148
+ } catch {
19149
+ }
19150
+ }
19151
+ }
19152
+ await walk(base);
19153
+ }
19154
+ async function createDeployProjectZip(projectDir, zipPath) {
19155
+ const files = {};
19156
+ await collectDeployZipEntries(projectDir, files);
19157
+ const fileCount = Object.keys(files).length;
19158
+ if (fileCount === 0) {
19159
+ throw new Error("No files to include in deploy ZIP (check project directory and exclusions)");
18885
19160
  }
19161
+ const { zipSync } = await import("fflate");
19162
+ const zipData = zipSync(files, { level: 6 });
19163
+ await writeFile(zipPath, zipData);
18886
19164
  return zipPath;
18887
19165
  }
19166
+
19167
+ // src/app/agent/tools/DeployAppTool/DeployAppTool.ts
18888
19168
  async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
18889
19169
  const deployUrl = `${severinoUrl.replace(/\/$/, "")}/api/v1/deploy`;
18890
- const curlArgs = [
18891
- "-X",
18892
- "POST",
18893
- deployUrl,
18894
- "-F",
18895
- `file=@${zipPath}`
18896
- ];
18897
- if (name) {
18898
- curlArgs.push("-F", `name=${name}`);
18899
- }
18900
- if (appId) {
18901
- curlArgs.push("-F", `appId=${appId}`);
18902
- }
19170
+ const zipBytes = await fs24.readFile(zipPath);
19171
+ const form = new FormData();
19172
+ form.append(
19173
+ "file",
19174
+ new Blob([zipBytes], { type: "application/zip" }),
19175
+ path26.basename(zipPath)
19176
+ );
19177
+ if (name) form.append("name", name);
19178
+ if (appId) form.append("appId", appId);
19179
+ const headers = { Accept: "application/json" };
18903
19180
  if (apiKey) {
18904
- curlArgs.push("-H", `Authorization: Bearer ${apiKey}`);
18905
- curlArgs.push("-H", `X-API-Key: ${apiKey}`);
18906
- }
18907
- curlArgs.push("-H", "Accept: application/json");
18908
- const curlCommand = `curl ${curlArgs.join(" ")}`;
18909
- const result = await shellCommand({
18910
- command: curlCommand,
18911
- timeout: 60
18912
- // 1 minuto para upload
19181
+ headers.Authorization = `Bearer ${apiKey}`;
19182
+ headers["X-API-Key"] = apiKey;
19183
+ }
19184
+ const httpResponse = await fetch(deployUrl, {
19185
+ method: "POST",
19186
+ body: form,
19187
+ headers,
19188
+ signal: AbortSignal.timeout(12e4)
18913
19189
  });
18914
- const resultJson = JSON.parse(result);
18915
- if (resultJson.status !== "success") {
18916
- throw new Error(`Upload failed: ${resultJson.stderr}`);
19190
+ const responseText = await httpResponse.text();
19191
+ if (!httpResponse.ok) {
19192
+ throw new Error(
19193
+ `Upload failed (${httpResponse.status}): ${responseText.slice(0, 500)}`
19194
+ );
18917
19195
  }
18918
19196
  try {
18919
- const response = JSON.parse(resultJson.stdout);
19197
+ const response = JSON.parse(responseText);
18920
19198
  if (response.success === false || response.error) {
18921
19199
  const errorMsg = typeof response.error === "string" ? response.error : response.error?.message || "Deploy failed";
18922
19200
  return {
@@ -18992,8 +19270,10 @@ function buildFactorAiManifest(appContext, deployResult, appName) {
18992
19270
  mode: "tool-first",
18993
19271
  instructions: [
18994
19272
  "Read factorai.sh.json as the source of truth before editing files.",
18995
- "Prefer incremental changes and preserve the existing deployment context.",
18996
- "After edits, use the redeploy tool instead of rebuilding from scratch."
19273
+ "Load skill factorai-sh for the full FactorAI.sh workflow.",
19274
+ "For an app already online: use factorai.sh.apply_app_changes with deploy:true (not deploy_app again).",
19275
+ "Poll factorai.sh.get_app_status until building\u2192ready before reporting the live URL.",
19276
+ "Use factorai.sh.redeploy_app only when rebuilding without new file patches."
18997
19277
  ]
18998
19278
  },
18999
19279
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -19001,8 +19281,8 @@ function buildFactorAiManifest(appContext, deployResult, appName) {
19001
19281
  }
19002
19282
  async function writeFactorAiManifest(projectDir, appContext, deployResult, appName) {
19003
19283
  const manifest = buildFactorAiManifest(appContext, deployResult, appName);
19004
- const manifestPath = path24.join(projectDir, "factorai.sh.json");
19005
- await fs23.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
19284
+ const manifestPath = path26.join(projectDir, "factorai.sh.json");
19285
+ await fs24.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
19006
19286
  `, "utf-8");
19007
19287
  return manifest;
19008
19288
  }
@@ -19022,23 +19302,23 @@ async function deployApp(args) {
19022
19302
  }
19023
19303
  const resolvedProjectDir = resolveWorkspacePath(projectDir);
19024
19304
  try {
19025
- await fs23.access(resolvedProjectDir);
19305
+ await fs24.access(resolvedProjectDir);
19026
19306
  } catch {
19027
19307
  return {
19028
19308
  success: false,
19029
19309
  error: `Project directory not found: ${resolvedProjectDir}`
19030
19310
  };
19031
19311
  }
19032
- const packageJsonPath = path24.join(resolvedProjectDir, "package.json");
19312
+ const packageJsonPath = path26.join(resolvedProjectDir, "package.json");
19033
19313
  try {
19034
- await fs23.access(packageJsonPath);
19314
+ await fs24.access(packageJsonPath);
19035
19315
  } catch {
19036
19316
  return {
19037
19317
  success: false,
19038
19318
  error: "Not a Next.js project: package.json not found"
19039
19319
  };
19040
19320
  }
19041
- const packageJsonContent = await fs23.readFile(packageJsonPath, "utf-8");
19321
+ const packageJsonContent = await fs24.readFile(packageJsonPath, "utf-8");
19042
19322
  const packageJson = JSON.parse(packageJsonContent);
19043
19323
  const hasNext = packageJson.dependencies?.next || packageJson.devDependencies?.next;
19044
19324
  if (!hasNext) {
@@ -19047,18 +19327,18 @@ async function deployApp(args) {
19047
19327
  error: "Not a Next.js project: next not found in dependencies"
19048
19328
  };
19049
19329
  }
19050
- const appName = name || packageJson.name || path24.basename(resolvedProjectDir);
19051
- const tempDir = path24.join(resolvedProjectDir, ".tmp");
19052
- await fs23.mkdir(tempDir, { recursive: true });
19053
- const zipPath = path24.join(tempDir, `${appName}-${Date.now()}.zip`);
19330
+ const appName = name || packageJson.name || path26.basename(resolvedProjectDir);
19331
+ const tempDir = path26.join(resolvedProjectDir, ".tmp");
19332
+ await fs24.mkdir(tempDir, { recursive: true });
19333
+ const zipPath = path26.join(tempDir, `${appName}-${Date.now()}.zip`);
19054
19334
  console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
19055
- await createProjectZip(resolvedProjectDir, zipPath);
19056
- const zipStats = await fs23.stat(zipPath);
19335
+ await createDeployProjectZip(resolvedProjectDir, zipPath);
19336
+ const zipStats = await fs24.stat(zipPath);
19057
19337
  const zipSizeMB = zipStats.size / 1024 / 1024;
19058
19338
  if (zipSizeMB > 50) {
19059
- await fs23.unlink(zipPath).catch(() => {
19339
+ await fs24.unlink(zipPath).catch(() => {
19060
19340
  });
19061
- await fs23.rmdir(tempDir).catch(() => {
19341
+ await fs24.rmdir(tempDir).catch(() => {
19062
19342
  });
19063
19343
  return {
19064
19344
  success: false,
@@ -19069,8 +19349,8 @@ async function deployApp(args) {
19069
19349
  console.log(`[deploy-app] Uploading to ${severinoUrl}...`);
19070
19350
  const deployResult = await uploadToSeverino(zipPath, severinoUrl, appName, apiKey, appId);
19071
19351
  try {
19072
- await fs23.unlink(zipPath);
19073
- await fs23.rmdir(tempDir);
19352
+ await fs24.unlink(zipPath);
19353
+ await fs24.rmdir(tempDir);
19074
19354
  } catch (e) {
19075
19355
  console.warn("[deploy-app] Cleanup warning:", e);
19076
19356
  }
@@ -19083,7 +19363,7 @@ async function deployApp(args) {
19083
19363
  appName
19084
19364
  );
19085
19365
  deployResult.factoraiManifest = manifest;
19086
- deployResult.factoraiManifestPath = path24.join(resolvedProjectDir, "factorai.sh.json");
19366
+ deployResult.factoraiManifestPath = path26.join(resolvedProjectDir, "factorai.sh.json");
19087
19367
  }
19088
19368
  console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
19089
19369
  }
@@ -19097,147 +19377,6 @@ async function deployApp(args) {
19097
19377
  }
19098
19378
  }
19099
19379
 
19100
- // src/app/agent/runtime/factorai_context.ts
19101
- import fs24 from "fs";
19102
- import path25 from "path";
19103
- function normalizeContext(raw) {
19104
- if (!raw || typeof raw.appId !== "string" || !raw.appId.trim()) {
19105
- return null;
19106
- }
19107
- const appId = raw.appId.trim();
19108
- return {
19109
- appId,
19110
- tenantId: typeof raw.tenantId === "string" ? raw.tenantId : null,
19111
- projectId: typeof raw.projectId === "string" ? raw.projectId : null,
19112
- workspaceId: typeof raw.workspaceId === "string" ? raw.workspaceId : null,
19113
- revisionId: typeof raw.revisionId === "string" ? raw.revisionId : null,
19114
- deploymentId: typeof raw.deploymentId === "string" ? raw.deploymentId : null,
19115
- buildJobId: typeof raw.buildJobId === "string" ? raw.buildJobId : null,
19116
- manifestPath: typeof raw.manifestPath === "string" ? raw.manifestPath : null,
19117
- manifestFile: typeof raw.manifestFile === "string" ? raw.manifestFile : null,
19118
- appUrl: typeof raw.appUrl === "string" ? raw.appUrl : null
19119
- };
19120
- }
19121
- function readJsonFile(filePath) {
19122
- try {
19123
- if (!fs24.existsSync(filePath)) {
19124
- return null;
19125
- }
19126
- const parsed = JSON.parse(fs24.readFileSync(filePath, "utf8"));
19127
- if (parsed && typeof parsed === "object") {
19128
- return parsed.appContext && typeof parsed.appContext === "object" ? parsed.appContext : parsed;
19129
- }
19130
- } catch {
19131
- return null;
19132
- }
19133
- return null;
19134
- }
19135
- function readFactorAiWorkspaceManifest(projectDir = process.cwd()) {
19136
- const manifestPath = path25.join(projectDir, "factorai.sh.json");
19137
- try {
19138
- if (!fs24.existsSync(manifestPath)) {
19139
- return null;
19140
- }
19141
- const parsed = JSON.parse(fs24.readFileSync(manifestPath, "utf8"));
19142
- return parsed && typeof parsed === "object" ? parsed : null;
19143
- } catch {
19144
- return null;
19145
- }
19146
- }
19147
- function readContextFromWorkspace() {
19148
- const candidate = path25.join(process.cwd(), "factorai.sh.json");
19149
- const parsed = readJsonFile(candidate);
19150
- if (!parsed) {
19151
- return null;
19152
- }
19153
- const context = normalizeContext(parsed);
19154
- return context;
19155
- }
19156
- function loadFactorAiAppContext() {
19157
- return readContextFromWorkspace();
19158
- }
19159
- function formatFactorAiAppContextSummary(context) {
19160
- if (!context) {
19161
- return "";
19162
- }
19163
- const parts = [
19164
- `appId=${context.appId}`,
19165
- context.tenantId ? `tenantId=${context.tenantId}` : null,
19166
- context.projectId ? `projectId=${context.projectId}` : null,
19167
- context.workspaceId ? `workspaceId=${context.workspaceId}` : null,
19168
- context.revisionId ? `revisionId=${context.revisionId}` : null,
19169
- context.deploymentId ? `deploymentId=${context.deploymentId}` : null,
19170
- context.buildJobId ? `buildJobId=${context.buildJobId}` : null,
19171
- context.appUrl ? `appUrl=${context.appUrl}` : null
19172
- ].filter(Boolean);
19173
- return parts.join(" | ");
19174
- }
19175
- function buildFactorAiWorkspaceManifest(input) {
19176
- const projectDir = input.projectDir || process.cwd();
19177
- const existing = readFactorAiWorkspaceManifest(projectDir) || {};
19178
- const now2 = (/* @__PURE__ */ new Date()).toISOString();
19179
- const nextAppContext = {
19180
- ...typeof existing.appContext === "object" && existing.appContext ? existing.appContext : {},
19181
- appId: input.appContext.appId,
19182
- tenantId: input.appContext.tenantId ?? null,
19183
- projectId: input.appContext.projectId ?? null,
19184
- workspaceId: input.appContext.workspaceId ?? null,
19185
- revisionId: input.appContext.revisionId ?? null,
19186
- deploymentId: input.appContext.deploymentId ?? null,
19187
- buildJobId: input.appContext.buildJobId ?? null,
19188
- manifestPath: "factorai.sh.json",
19189
- manifestFile: "factorai.sh.json",
19190
- appUrl: input.appContext.appUrl ?? null
19191
- };
19192
- return {
19193
- ...existing,
19194
- version: 1,
19195
- manifestFile: "factorai.sh.json",
19196
- manifestPath: "factorai.sh.json",
19197
- appContext: nextAppContext,
19198
- app: {
19199
- ...typeof existing.app === "object" && existing.app ? existing.app : {},
19200
- name: input.name ?? (typeof existing.app === "object" && existing.app ? existing.app.name : void 0) ?? null,
19201
- status: input.status ?? (typeof existing.app === "object" && existing.app ? existing.app.status : void 0) ?? "building",
19202
- isRedeploy: input.isRedeploy ?? (typeof existing.app === "object" && existing.app ? existing.app.isRedeploy : false),
19203
- message: input.message ?? (typeof existing.app === "object" && existing.app ? existing.app.message : null),
19204
- ...input.app || {}
19205
- },
19206
- agent: {
19207
- provider: "bluma",
19208
- mode: "tool-first",
19209
- instructions: [
19210
- "Read factorai.sh.json as the source of truth before editing files.",
19211
- "Prefer incremental changes and preserve the existing deployment context.",
19212
- "After edits, use the redeploy tool instead of rebuilding from scratch."
19213
- ],
19214
- ...typeof existing.agent === "object" && existing.agent ? existing.agent : {},
19215
- ...input.agent || {}
19216
- },
19217
- sandbox: {
19218
- ...typeof existing.sandbox === "object" && existing.sandbox ? existing.sandbox : {},
19219
- ...input.sandbox || {}
19220
- },
19221
- source: {
19222
- root: ".",
19223
- publicDir: "./public",
19224
- appDir: ".",
19225
- ...typeof existing.source === "object" && existing.source ? existing.source : {},
19226
- ...input.source || {}
19227
- },
19228
- updatedAt: now2,
19229
- ...input.extra || {}
19230
- };
19231
- }
19232
- async function writeFactorAiWorkspaceManifest(input) {
19233
- const projectDir = input.projectDir || process.cwd();
19234
- const manifest = buildFactorAiWorkspaceManifest({ ...input, projectDir });
19235
- const manifestPath = path25.join(projectDir, "factorai.sh.json");
19236
- fs24.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
19237
- `, "utf8");
19238
- return { manifestPath, manifest };
19239
- }
19240
-
19241
19380
  // src/app/agent/runtime/native_tool_catalog.ts
19242
19381
  init_sandbox_policy();
19243
19382
  function getFactorAiBaseUrl() {
@@ -19389,7 +19528,7 @@ function getFactorAiSandboxToolDefinitions() {
19389
19528
  type: "function",
19390
19529
  function: {
19391
19530
  name: "factorai.sh.deploy_app",
19392
- description: "Deploy a Next.js project from the sandbox to the FactorAI hosting backend. The returned metadata must be written into factorai.sh.json in the project root for future incremental edits.",
19531
+ description: "First deploy only: ZIP project source (excludes node_modules/.next) to SEVERINO_URL /api/v1/deploy. Writes factorai.sh.json. For later edits use apply_app_changes instead.",
19393
19532
  parameters: {
19394
19533
  type: "object",
19395
19534
  properties: {
@@ -19407,7 +19546,7 @@ function getFactorAiSandboxToolDefinitions() {
19407
19546
  type: "function",
19408
19547
  function: {
19409
19548
  name: "factorai.sh.get_app_status",
19410
- description: "Get the current status and contract for a FactorAI deployment in the sandbox.",
19549
+ description: "Poll deploy/rebuild status (building|ready|failed). Call after deploy_app or apply_app_changes until ready before telling the user the app is live.",
19411
19550
  parameters: {
19412
19551
  type: "object",
19413
19552
  properties: {
@@ -19422,7 +19561,7 @@ function getFactorAiSandboxToolDefinitions() {
19422
19561
  type: "function",
19423
19562
  function: {
19424
19563
  name: "factorai.sh.apply_app_changes",
19425
- description: "Apply incremental file changes to a FactorAI workspace and optionally redeploy.",
19564
+ description: "Patch files on an already-deployed FactorAI app in THIS session only (read appId from factorai.sh.json in cwd). Each files[].content must be the COMPLETE file body (server replaces the whole file \u2014 never send a JSX/footer fragment). After edit_tool, read_file_lines the full file before calling. deploy:true (default) runs next build + restart. Prefer over deploy_app for live edits.",
19426
19565
  parameters: {
19427
19566
  type: "object",
19428
19567
  properties: {
@@ -20086,7 +20225,7 @@ var NATIVE_TOOL_ENTRIES = [
20086
20225
  autoApproveInLocal: false,
20087
20226
  autoApproveInSandbox: true,
20088
20227
  sandboxOnly: true,
20089
- description: "Sandbox-only FactorAI tool. Apply file changes to a deployed app workspace and redeploy it."
20228
+ description: "This session only: appId from factorai.sh.json in cwd. Each files[].content = COMPLETE file (server replaces whole file). Read full file after edit_tool. deploy defaults true. Poll get_app_status until ready."
20090
20229
  },
20091
20230
  implementation: factorAiApplyAppChanges
20092
20231
  },
@@ -20145,7 +20284,7 @@ var SANDBOX_COMMUNICATION_TOOL_DEFINITIONS = [
20145
20284
  type: "function",
20146
20285
  function: {
20147
20286
  name: "message",
20148
- 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/.',
20287
+ description: 'Deliver user-visible output. message_type "info": progress (does not end turn). message_type "result": ends turn; file deliverables use attachments[] under .bluma/artifacts/; live FactorAI.sh app URL MUST use factor-sh-url-app (auto-filled from factorai.sh.json when omitted).',
20149
20288
  parameters: {
20150
20289
  type: "object",
20151
20290
  properties: {
@@ -20159,6 +20298,10 @@ var SANDBOX_COMMUNICATION_TOOL_DEFINITIONS = [
20159
20298
  type: "array",
20160
20299
  items: { type: "string" },
20161
20300
  description: "Workspace file paths under .bluma/artifacts/ (required for file deliverables on result)."
20301
+ },
20302
+ "factor-sh-url-app": {
20303
+ type: "string",
20304
+ description: "Required on message_type result after FactorAI.sh deploy/redeploy: absolute live app URL for Severino (from factorai.sh.json appUrl). Auto-filled if omitted."
20162
20305
  }
20163
20306
  },
20164
20307
  required: ["content", "message_type"],
@@ -20302,8 +20445,8 @@ var ToolInvoker = class {
20302
20445
  async initialize() {
20303
20446
  try {
20304
20447
  const currentFilePath = fileURLToPath(import.meta.url);
20305
- const currentDirPath = path26.dirname(currentFilePath);
20306
- const configPath = path26.resolve(currentDirPath, "config", "native_tools.json");
20448
+ const currentDirPath = path27.dirname(currentFilePath);
20449
+ const configPath = path27.resolve(currentDirPath, "config", "native_tools.json");
20307
20450
  const fileContent = await fs25.readFile(configPath, "utf-8");
20308
20451
  const config2 = JSON.parse(fileContent);
20309
20452
  this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
@@ -20352,7 +20495,7 @@ var ToolInvoker = class {
20352
20495
 
20353
20496
  // src/app/agent/tools/mcp/mcp_client.ts
20354
20497
  import { promises as fs26 } from "fs";
20355
- import path27 from "path";
20498
+ import path28 from "path";
20356
20499
  import os18 from "os";
20357
20500
  import { fileURLToPath as fileURLToPath2 } from "url";
20358
20501
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -20380,9 +20523,9 @@ var MCPClient = class {
20380
20523
  });
20381
20524
  }
20382
20525
  const __filename = fileURLToPath2(import.meta.url);
20383
- const __dirname2 = path27.dirname(__filename);
20384
- const defaultConfigPath = path27.resolve(__dirname2, "config", "bluma-mcp.json");
20385
- const userConfigPath = path27.join(os18.homedir(), ".bluma", "bluma-mcp.json");
20526
+ const __dirname2 = path28.dirname(__filename);
20527
+ const defaultConfigPath = path28.resolve(__dirname2, "config", "bluma-mcp.json");
20528
+ const userConfigPath = path28.join(os18.homedir(), ".bluma", "bluma-mcp.json");
20386
20529
  const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
20387
20530
  const userConfig = await this.loadMcpConfig(userConfigPath, "User");
20388
20531
  const mergedConfig = {
@@ -20628,55 +20771,299 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
20628
20771
  };
20629
20772
 
20630
20773
  // src/app/agent/bluma/core/bluma.ts
20631
- import path41 from "path";
20632
- import fs38 from "fs";
20774
+ import path44 from "path";
20775
+ import fs40 from "fs";
20633
20776
  import { v4 as uuidv48 } from "uuid";
20634
20777
 
20635
20778
  // src/app/agent/session_manager/session_manager.ts
20636
- import path30 from "path";
20637
- import { promises as fs28 } from "fs";
20779
+ import path32 from "path";
20780
+ import { promises as fs29 } from "fs";
20638
20781
 
20639
20782
  // src/app/agent/session_manager/agent_session_paths.ts
20640
- import path29 from "path";
20641
- import { promises as fs27 } from "fs";
20783
+ import path31 from "path";
20784
+ import { promises as fs28 } from "fs";
20642
20785
 
20643
20786
  // src/app/agent/session_manager/bluma_app_dir.ts
20644
- import path28 from "path";
20787
+ import path29 from "path";
20645
20788
  import os19 from "os";
20646
20789
  function expandHome(p) {
20647
20790
  if (!p) return p;
20648
20791
  if (p.startsWith("~")) {
20649
- return path28.join(os19.homedir(), p.slice(1));
20792
+ return path29.join(os19.homedir(), p.slice(1));
20650
20793
  }
20651
20794
  return p;
20652
20795
  }
20653
20796
  function getPreferredAppDir() {
20654
20797
  const fromEnv = process.env.BLUMA_HOME?.trim();
20655
20798
  if (fromEnv) {
20656
- return path28.resolve(expandHome(fromEnv));
20799
+ return path29.resolve(expandHome(fromEnv));
20657
20800
  }
20658
- const fixed = path28.join(os19.homedir(), ".bluma");
20659
- return path28.resolve(expandHome(fixed));
20801
+ const fixed = path29.join(os19.homedir(), ".bluma");
20802
+ return path29.resolve(expandHome(fixed));
20660
20803
  }
20661
20804
 
20662
- // src/app/agent/session_manager/agent_session_paths.ts
20805
+ // src/app/agent/session_manager/session_index_db.ts
20806
+ import path30 from "path";
20807
+ import { mkdirSync as mkdirSync3 } from "fs";
20808
+ import { promises as fs27 } from "fs";
20809
+ import { createRequire } from "module";
20663
20810
  var AGENT_SESSION_PATHS_JSONL = "agent_session_paths.jsonl";
20811
+ var BLUMA_SESSION_DB_FILE = "bluma.sqlite";
20812
+ var SCHEMA_VERSION = 1;
20813
+ var dbInstance = null;
20814
+ var dbAppDir = null;
20815
+ var migratePromise = null;
20816
+ function loadSqlite() {
20817
+ const nodeRequire = createRequire(import.meta.url);
20818
+ return nodeRequire("better-sqlite3");
20819
+ }
20820
+ function getSessionDbPath(appDir = getPreferredAppDir()) {
20821
+ return path30.join(appDir, BLUMA_SESSION_DB_FILE);
20822
+ }
20823
+ function normalizeRelativePath(relativePath) {
20824
+ return relativePath.split(path30.sep).join("/");
20825
+ }
20826
+ async function walkSessionJsonFiles(dir, onFile) {
20827
+ let entries;
20828
+ try {
20829
+ entries = await fs27.readdir(dir, { withFileTypes: true });
20830
+ } catch {
20831
+ return;
20832
+ }
20833
+ for (const e of entries) {
20834
+ const name = String(e.name);
20835
+ const full = path30.join(dir, name);
20836
+ if (e.isDirectory()) {
20837
+ await walkSessionJsonFiles(full, onFile);
20838
+ } else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
20839
+ await onFile(full);
20840
+ }
20841
+ }
20842
+ }
20843
+ async function loadJsonlEntries(appDir) {
20844
+ const map = /* @__PURE__ */ new Map();
20845
+ const indexFile = path30.join(appDir, AGENT_SESSION_PATHS_JSONL);
20846
+ let raw;
20847
+ try {
20848
+ raw = await fs27.readFile(indexFile, "utf-8");
20849
+ } catch {
20850
+ return map;
20851
+ }
20852
+ for (const line of raw.split("\n")) {
20853
+ const trimmed = line.trim();
20854
+ if (!trimmed) continue;
20855
+ try {
20856
+ const row = JSON.parse(trimmed);
20857
+ if (row.sessionId && typeof row.relativePath === "string") {
20858
+ map.set(row.sessionId, {
20859
+ relativePath: normalizeRelativePath(row.relativePath),
20860
+ updatedAt: row.updatedAt ?? (/* @__PURE__ */ new Date(0)).toISOString()
20861
+ });
20862
+ }
20863
+ } catch {
20864
+ }
20865
+ }
20866
+ return map;
20867
+ }
20868
+ function previewFromConversationHistory(history) {
20869
+ for (const msg of history ?? []) {
20870
+ if (msg.role !== "user") continue;
20871
+ const c = msg.content;
20872
+ if (typeof c !== "string") continue;
20873
+ const t = c.trim();
20874
+ if (!t || t.startsWith("[SKILL:")) continue;
20875
+ const oneLine = t.replace(/\s+/g, " ");
20876
+ return oneLine.length > 72 ? `${oneLine.slice(0, 72)}\u2026` : oneLine;
20877
+ }
20878
+ return "(no messages)";
20879
+ }
20880
+ async function readSessionMetaFromJson(absPath) {
20881
+ const fallback = {
20882
+ preview: "(no messages)",
20883
+ createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
20884
+ updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
20885
+ };
20886
+ try {
20887
+ const raw = await fs27.readFile(absPath, "utf-8");
20888
+ const data = JSON.parse(raw);
20889
+ const createdAt = data.created_at ?? fallback.createdAt;
20890
+ const updatedAt = data.last_updated ?? data.created_at ?? fallback.updatedAt;
20891
+ return {
20892
+ preview: previewFromConversationHistory(data.conversation_history),
20893
+ createdAt,
20894
+ updatedAt
20895
+ };
20896
+ } catch {
20897
+ try {
20898
+ const st = await fs27.stat(absPath);
20899
+ const iso = new Date(st.mtimeMs).toISOString();
20900
+ return { preview: fallback.preview, createdAt: iso, updatedAt: iso };
20901
+ } catch {
20902
+ return fallback;
20903
+ }
20904
+ }
20905
+ }
20906
+ async function runMigrationV1(db, appDir) {
20907
+ const jsonl = await loadJsonlEntries(appDir);
20908
+ const upsert = db.prepare(`
20909
+ INSERT INTO agent_sessions (session_id, relative_path, created_at, updated_at, preview)
20910
+ VALUES (@sessionId, @relativePath, @createdAt, @updatedAt, @preview)
20911
+ ON CONFLICT(session_id) DO UPDATE SET
20912
+ relative_path = excluded.relative_path,
20913
+ updated_at = CASE
20914
+ WHEN excluded.updated_at > agent_sessions.updated_at THEN excluded.updated_at
20915
+ ELSE agent_sessions.updated_at
20916
+ END,
20917
+ preview = CASE
20918
+ WHEN excluded.updated_at > agent_sessions.updated_at THEN excluded.preview
20919
+ ELSE agent_sessions.preview
20920
+ END
20921
+ `);
20922
+ const insertFromPath = async (sessionId, absPath, jsonlUpdatedAt) => {
20923
+ let rel;
20924
+ try {
20925
+ rel = normalizeRelativePath(path30.relative(appDir, absPath));
20926
+ } catch {
20927
+ return;
20928
+ }
20929
+ const meta = await readSessionMetaFromJson(absPath);
20930
+ const updatedAt = jsonlUpdatedAt && Date.parse(jsonlUpdatedAt) > Date.parse(meta.updatedAt) ? jsonlUpdatedAt : meta.updatedAt;
20931
+ upsert.run({
20932
+ sessionId,
20933
+ relativePath: rel,
20934
+ createdAt: meta.createdAt,
20935
+ updatedAt,
20936
+ preview: meta.preview
20937
+ });
20938
+ };
20939
+ for (const [sessionId, entry] of jsonl) {
20940
+ const full = path30.join(appDir, entry.relativePath.replace(/\//g, path30.sep));
20941
+ try {
20942
+ await fs27.access(full);
20943
+ await insertFromPath(sessionId, full, entry.updatedAt);
20944
+ } catch {
20945
+ upsert.run({
20946
+ sessionId,
20947
+ relativePath: entry.relativePath,
20948
+ createdAt: entry.updatedAt,
20949
+ updatedAt: entry.updatedAt,
20950
+ preview: "(no messages)"
20951
+ });
20952
+ }
20953
+ }
20954
+ const sessionsRoot = path30.join(appDir, "sessions");
20955
+ const existing = db.prepare("SELECT session_id FROM agent_sessions").all();
20956
+ const seen = new Set(existing.map((r) => r.session_id));
20957
+ await walkSessionJsonFiles(sessionsRoot, async (absPath) => {
20958
+ const sessionId = path30.basename(absPath, ".json");
20959
+ if (!sessionId || seen.has(sessionId)) return;
20960
+ seen.add(sessionId);
20961
+ await insertFromPath(sessionId, absPath);
20962
+ });
20963
+ db.pragma(`user_version = ${SCHEMA_VERSION}`);
20964
+ }
20965
+ function applySchema(db) {
20966
+ db.exec(`
20967
+ CREATE TABLE IF NOT EXISTS agent_sessions (
20968
+ session_id TEXT PRIMARY KEY NOT NULL,
20969
+ relative_path TEXT NOT NULL,
20970
+ created_at TEXT NOT NULL,
20971
+ updated_at TEXT NOT NULL,
20972
+ preview TEXT NOT NULL DEFAULT '(no messages)'
20973
+ );
20974
+ CREATE INDEX IF NOT EXISTS idx_agent_sessions_updated
20975
+ ON agent_sessions(updated_at DESC);
20976
+ `);
20977
+ }
20978
+ async function ensureSessionIndexDb(appDir = getPreferredAppDir()) {
20979
+ if (dbInstance && dbAppDir === appDir) {
20980
+ return dbInstance;
20981
+ }
20982
+ if (!migratePromise || dbAppDir !== appDir) {
20983
+ migratePromise = (async () => {
20984
+ if (dbInstance) {
20985
+ try {
20986
+ dbInstance.close();
20987
+ } catch {
20988
+ }
20989
+ dbInstance = null;
20990
+ }
20991
+ const BetterSqlite3 = loadSqlite();
20992
+ const dbPath = getSessionDbPath(appDir);
20993
+ mkdirSync3(path30.dirname(dbPath), { recursive: true });
20994
+ const db = new BetterSqlite3(dbPath);
20995
+ db.pragma("journal_mode = WAL");
20996
+ applySchema(db);
20997
+ const version = db.pragma("user_version", { simple: true });
20998
+ if (version < SCHEMA_VERSION) {
20999
+ await runMigrationV1(db, appDir);
21000
+ }
21001
+ dbInstance = db;
21002
+ dbAppDir = appDir;
21003
+ })();
21004
+ }
21005
+ await migratePromise;
21006
+ return dbInstance;
21007
+ }
21008
+ async function upsertSessionIndexRow(row, appDir = getPreferredAppDir()) {
21009
+ const db = await ensureSessionIndexDb(appDir);
21010
+ const stmt = db.prepare(`
21011
+ INSERT INTO agent_sessions (session_id, relative_path, created_at, updated_at, preview)
21012
+ VALUES (@sessionId, @relativePath, @createdAt, @updatedAt, @preview)
21013
+ ON CONFLICT(session_id) DO UPDATE SET
21014
+ relative_path = excluded.relative_path,
21015
+ updated_at = excluded.updated_at,
21016
+ preview = excluded.preview
21017
+ `);
21018
+ stmt.run({
21019
+ sessionId: row.sessionId,
21020
+ relativePath: normalizeRelativePath(row.relativePath),
21021
+ createdAt: row.createdAt,
21022
+ updatedAt: row.updatedAt,
21023
+ preview: row.preview
21024
+ });
21025
+ }
21026
+ async function getSessionPathFromIndex(sessionId, appDir = getPreferredAppDir()) {
21027
+ const db = await ensureSessionIndexDb(appDir);
21028
+ const row = db.prepare("SELECT relative_path FROM agent_sessions WHERE session_id = ?").get(sessionId);
21029
+ return row?.relative_path?.replace(/\//g, path30.sep) ?? null;
21030
+ }
21031
+ async function listSessionsFromIndex(limit = 50, appDir = getPreferredAppDir()) {
21032
+ const db = await ensureSessionIndexDb(appDir);
21033
+ const rows = db.prepare(
21034
+ `SELECT session_id, relative_path, created_at, updated_at, preview
21035
+ FROM agent_sessions
21036
+ ORDER BY updated_at DESC
21037
+ LIMIT ?`
21038
+ ).all(limit);
21039
+ return rows.map((r) => ({
21040
+ sessionId: r.session_id,
21041
+ relativePath: r.relative_path.replace(/\//g, path30.sep),
21042
+ createdAt: r.created_at,
21043
+ updatedAt: r.updated_at,
21044
+ preview: r.preview
21045
+ }));
21046
+ }
21047
+
21048
+ // src/app/agent/session_manager/agent_session_paths.ts
21049
+ var AGENT_SESSION_PATHS_JSONL2 = "agent_session_paths.jsonl";
20664
21050
  async function appendAgentSessionPath(sessionId, relativePath) {
20665
21051
  const app = getPreferredAppDir();
20666
- const indexFile = path29.join(app, AGENT_SESSION_PATHS_JSONL);
20667
- await fs27.mkdir(app, { recursive: true });
20668
- const line = JSON.stringify({
21052
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
21053
+ const rel = relativePath.split(path31.sep).join("/");
21054
+ await upsertSessionIndexRow({
20669
21055
  sessionId,
20670
- relativePath: relativePath.split(path29.sep).join("/"),
20671
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
20672
- }) + "\n";
20673
- await fs27.appendFile(indexFile, line, "utf-8");
21056
+ relativePath: rel,
21057
+ createdAt: now2,
21058
+ updatedAt: now2,
21059
+ preview: "(no messages)"
21060
+ });
20674
21061
  }
20675
- async function resolveSessionRelativePathFromIndex(sessionId) {
20676
- const indexFile = path29.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL);
21062
+ async function resolveSessionRelativePathFromJsonl(sessionId) {
21063
+ const indexFile = path31.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
20677
21064
  let raw;
20678
21065
  try {
20679
- raw = await fs27.readFile(indexFile, "utf-8");
21066
+ raw = await fs28.readFile(indexFile, "utf-8");
20680
21067
  } catch {
20681
21068
  return null;
20682
21069
  }
@@ -20685,37 +21072,42 @@ async function resolveSessionRelativePathFromIndex(sessionId) {
20685
21072
  try {
20686
21073
  const row = JSON.parse(lines[i]);
20687
21074
  if (row.sessionId === sessionId && typeof row.relativePath === "string") {
20688
- return row.relativePath.replace(/\//g, path29.sep);
21075
+ return row.relativePath.replace(/\//g, path31.sep);
20689
21076
  }
20690
21077
  } catch {
20691
21078
  }
20692
21079
  }
20693
21080
  return null;
20694
21081
  }
20695
- async function walkSessionJsonFiles(dir, onFile) {
21082
+ async function resolveSessionRelativePathFromIndex(sessionId) {
21083
+ const fromDb = await getSessionPathFromIndex(sessionId);
21084
+ if (fromDb) return fromDb;
21085
+ return resolveSessionRelativePathFromJsonl(sessionId);
21086
+ }
21087
+ async function walkSessionJsonFiles2(dir, onFile) {
20696
21088
  let entries;
20697
21089
  try {
20698
- entries = await fs27.readdir(dir, { withFileTypes: true });
21090
+ entries = await fs28.readdir(dir, { withFileTypes: true });
20699
21091
  } catch {
20700
21092
  return;
20701
21093
  }
20702
21094
  for (const e of entries) {
20703
21095
  const name = String(e.name);
20704
- const full = path29.join(dir, name);
21096
+ const full = path31.join(dir, name);
20705
21097
  if (e.isDirectory()) {
20706
- await walkSessionJsonFiles(full, onFile);
21098
+ await walkSessionJsonFiles2(full, onFile);
20707
21099
  } else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
20708
21100
  await onFile(full);
20709
21101
  }
20710
21102
  }
20711
21103
  }
20712
21104
  async function findSessionFileGlobFallback(sessionId) {
20713
- const sessionsRoot = path29.join(getPreferredAppDir(), "sessions");
21105
+ const sessionsRoot = path31.join(getPreferredAppDir(), "sessions");
20714
21106
  const state2 = { best: null };
20715
- await walkSessionJsonFiles(sessionsRoot, async (abs) => {
20716
- if (path29.basename(abs, ".json") !== sessionId) return;
21107
+ await walkSessionJsonFiles2(sessionsRoot, async (abs) => {
21108
+ if (path31.basename(abs, ".json") !== sessionId) return;
20717
21109
  try {
20718
- const st = await fs27.stat(abs);
21110
+ const st = await fs28.stat(abs);
20719
21111
  if (!st.isFile()) return;
20720
21112
  if (!state2.best || st.mtimeMs > state2.best.m) {
20721
21113
  state2.best = { p: abs, m: st.mtimeMs };
@@ -20726,11 +21118,11 @@ async function findSessionFileGlobFallback(sessionId) {
20726
21118
  return state2.best !== null ? state2.best.p : null;
20727
21119
  }
20728
21120
  async function loadLatestIndexEntriesBySessionId() {
20729
- const indexFile = path29.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL);
21121
+ const indexFile = path31.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
20730
21122
  const map = /* @__PURE__ */ new Map();
20731
21123
  let raw;
20732
21124
  try {
20733
- raw = await fs27.readFile(indexFile, "utf-8");
21125
+ raw = await fs28.readFile(indexFile, "utf-8");
20734
21126
  } catch {
20735
21127
  return map;
20736
21128
  }
@@ -20763,40 +21155,36 @@ function dateFolderFromRelativePath(relativePath) {
20763
21155
  }
20764
21156
  return null;
20765
21157
  }
20766
- function previewFromConversationHistory(history) {
20767
- for (const msg of history ?? []) {
20768
- if (msg.role !== "user") continue;
20769
- const c = msg.content;
20770
- if (typeof c !== "string") continue;
20771
- const t = c.trim();
20772
- if (!t || t.startsWith("[SKILL:")) continue;
20773
- const oneLine = t.replace(/\s+/g, " ");
20774
- return oneLine.length > 72 ? `${oneLine.slice(0, 72)}\u2026` : oneLine;
20775
- }
20776
- return "(no messages)";
20777
- }
20778
- async function enrichCandidateFromFile(absPath, sessionId, lastMtimeMs) {
21158
+ async function enrichCandidateFromFile(absPath, sessionId, lastMtimeMs, opts) {
20779
21159
  let rel;
20780
21160
  try {
20781
- rel = path29.relative(getPreferredAppDir(), absPath);
21161
+ rel = path31.relative(getPreferredAppDir(), absPath);
20782
21162
  } catch {
20783
21163
  rel = absPath;
20784
21164
  }
20785
- const dateFolder = dateFolderFromRelativePath(rel.split(path29.sep).join("/"));
20786
- let preview = "(no messages)";
21165
+ const dateFolder = dateFolderFromRelativePath(rel.split(path31.sep).join("/"));
21166
+ let preview = opts?.preview ?? "(no messages)";
20787
21167
  let lastActivityMs = lastMtimeMs;
20788
- try {
20789
- const raw = await fs27.readFile(absPath, "utf-8");
20790
- const data = JSON.parse(raw);
20791
- preview = previewFromConversationHistory(data.conversation_history);
20792
- const iso = data.last_updated || data.created_at;
20793
- if (iso) {
20794
- const parsed = Date.parse(iso);
20795
- if (Number.isFinite(parsed)) {
20796
- lastActivityMs = Math.max(lastMtimeMs, parsed);
21168
+ if (opts?.updatedAtIso) {
21169
+ const parsed = Date.parse(opts.updatedAtIso);
21170
+ if (Number.isFinite(parsed)) {
21171
+ lastActivityMs = Math.max(lastMtimeMs, parsed);
21172
+ }
21173
+ }
21174
+ if (!opts?.preview || opts.preview === "(no messages)") {
21175
+ try {
21176
+ const raw = await fs28.readFile(absPath, "utf-8");
21177
+ const data = JSON.parse(raw);
21178
+ preview = previewFromConversationHistory(data.conversation_history);
21179
+ const iso = data.last_updated || data.created_at;
21180
+ if (iso) {
21181
+ const parsed = Date.parse(iso);
21182
+ if (Number.isFinite(parsed)) {
21183
+ lastActivityMs = Math.max(lastActivityMs, parsed);
21184
+ }
20797
21185
  }
21186
+ } catch {
20798
21187
  }
20799
- } catch {
20800
21188
  }
20801
21189
  return {
20802
21190
  sessionId,
@@ -20809,13 +21197,29 @@ async function enrichCandidateFromFile(absPath, sessionId, lastMtimeMs) {
20809
21197
  }
20810
21198
  async function listAgentSessionsForResume(limit = 50) {
20811
21199
  const appDir = getPreferredAppDir();
20812
- const sessionsRoot = path29.join(appDir, "sessions");
21200
+ await ensureSessionIndexDb(appDir);
21201
+ const fromIndex = await listSessionsFromIndex(limit * 2, appDir);
20813
21202
  const map = /* @__PURE__ */ new Map();
20814
- await walkSessionJsonFiles(sessionsRoot, async (absPath) => {
20815
- const id = path29.basename(absPath, ".json");
21203
+ for (const row of fromIndex) {
21204
+ const full = path31.join(appDir, row.relativePath);
21205
+ try {
21206
+ const st = await fs28.stat(full);
21207
+ if (!st.isFile()) continue;
21208
+ map.set(row.sessionId, {
21209
+ path: full,
21210
+ lastMtimeMs: st.mtimeMs,
21211
+ preview: row.preview,
21212
+ updatedAt: row.updatedAt
21213
+ });
21214
+ } catch {
21215
+ }
21216
+ }
21217
+ const sessionsRoot = path31.join(appDir, "sessions");
21218
+ await walkSessionJsonFiles2(sessionsRoot, async (absPath) => {
21219
+ const id = path31.basename(absPath, ".json");
20816
21220
  if (!id) return;
20817
21221
  try {
20818
- const st = await fs27.stat(absPath);
21222
+ const st = await fs28.stat(absPath);
20819
21223
  if (!st.isFile()) return;
20820
21224
  const cur = map.get(id);
20821
21225
  if (!cur || st.mtimeMs > cur.lastMtimeMs) {
@@ -20827,9 +21231,9 @@ async function listAgentSessionsForResume(limit = 50) {
20827
21231
  const index = await loadLatestIndexEntriesBySessionId();
20828
21232
  for (const [sessionId, entry] of index) {
20829
21233
  if (map.has(sessionId)) continue;
20830
- const full = path29.join(appDir, entry.relativePath.replace(/\//g, path29.sep));
21234
+ const full = path31.join(appDir, entry.relativePath.replace(/\//g, path31.sep));
20831
21235
  try {
20832
- const st = await fs27.stat(full);
21236
+ const st = await fs28.stat(full);
20833
21237
  if (st.isFile()) {
20834
21238
  map.set(sessionId, { path: full, lastMtimeMs: st.mtimeMs });
20835
21239
  }
@@ -20838,7 +21242,10 @@ async function listAgentSessionsForResume(limit = 50) {
20838
21242
  }
20839
21243
  const enriched = await Promise.all(
20840
21244
  [...map.entries()].map(
20841
- ([sessionId, v]) => enrichCandidateFromFile(v.path, sessionId, v.lastMtimeMs)
21245
+ ([sessionId, v]) => enrichCandidateFromFile(v.path, sessionId, v.lastMtimeMs, {
21246
+ preview: v.preview,
21247
+ updatedAtIso: v.updatedAt
21248
+ })
20842
21249
  )
20843
21250
  );
20844
21251
  return enriched.sort((a, b) => b.lastActivityMs - a.lastActivityMs).slice(0, limit);
@@ -20883,10 +21290,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
20883
21290
  const isWin = process.platform === "win32";
20884
21291
  while (attempt <= maxRetries) {
20885
21292
  try {
20886
- const dir = path30.dirname(dest);
20887
- await fs28.mkdir(dir, { recursive: true }).catch(() => {
21293
+ const dir = path32.dirname(dest);
21294
+ await fs29.mkdir(dir, { recursive: true }).catch(() => {
20888
21295
  });
20889
- await fs28.rename(src, dest);
21296
+ await fs29.rename(src, dest);
20890
21297
  return;
20891
21298
  } catch (e) {
20892
21299
  lastErr = e;
@@ -20899,13 +21306,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
20899
21306
  }
20900
21307
  }
20901
21308
  try {
20902
- await fs28.access(src);
20903
- const data = await fs28.readFile(src);
20904
- const dir = path30.dirname(dest);
20905
- await fs28.mkdir(dir, { recursive: true }).catch(() => {
21309
+ await fs29.access(src);
21310
+ const data = await fs29.readFile(src);
21311
+ const dir = path32.dirname(dest);
21312
+ await fs29.mkdir(dir, { recursive: true }).catch(() => {
20906
21313
  });
20907
- await fs28.writeFile(dest, data);
20908
- await fs28.unlink(src).catch(() => {
21314
+ await fs29.writeFile(dest, data);
21315
+ await fs29.unlink(src).catch(() => {
20909
21316
  });
20910
21317
  return;
20911
21318
  } catch (fallbackErr) {
@@ -20918,23 +21325,23 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
20918
21325
  }
20919
21326
  async function ensureSessionDir() {
20920
21327
  const appDir = getPreferredAppDir();
20921
- const sessionDir = path30.join(appDir, "sessions");
20922
- await fs28.mkdir(sessionDir, { recursive: true });
21328
+ const sessionDir = path32.join(appDir, "sessions");
21329
+ await fs29.mkdir(sessionDir, { recursive: true });
20923
21330
  return sessionDir;
20924
21331
  }
20925
21332
  async function resolveAgentSessionFilePath(sessionId) {
20926
21333
  const appDir = getPreferredAppDir();
20927
- const legacy = path30.join(appDir, "sessions", `${sessionId}.json`);
21334
+ const legacy = path32.join(appDir, "sessions", `${sessionId}.json`);
20928
21335
  try {
20929
- await fs28.access(legacy);
21336
+ await fs29.access(legacy);
20930
21337
  return legacy;
20931
21338
  } catch {
20932
21339
  }
20933
21340
  const rel = await resolveSessionRelativePathFromIndex(sessionId);
20934
21341
  if (rel) {
20935
- const full = path30.join(appDir, rel);
21342
+ const full = path32.join(appDir, rel);
20936
21343
  try {
20937
- await fs28.access(full);
21344
+ await fs29.access(full);
20938
21345
  return full;
20939
21346
  } catch {
20940
21347
  }
@@ -20947,7 +21354,7 @@ async function loadSession(sessionId) {
20947
21354
  return null;
20948
21355
  }
20949
21356
  try {
20950
- const fileContent = await fs28.readFile(sessionFile, "utf-8");
21357
+ const fileContent = await fs29.readFile(sessionFile, "utf-8");
20951
21358
  const sessionData = JSON.parse(fileContent);
20952
21359
  const memory = {
20953
21360
  historyAnchor: sessionData.history_anchor ?? null,
@@ -20968,16 +21375,16 @@ async function loadOrcreateSession(sessionId) {
20968
21375
  const y = String(now2.getFullYear());
20969
21376
  const mo = String(now2.getMonth() + 1).padStart(2, "0");
20970
21377
  const d = String(now2.getDate()).padStart(2, "0");
20971
- const datedDir = path30.join(sessionsRoot, y, mo, d);
20972
- await fs28.mkdir(datedDir, { recursive: true });
20973
- const sessionFile = path30.join(datedDir, `${sessionId}.json`);
21378
+ const datedDir = path32.join(sessionsRoot, y, mo, d);
21379
+ await fs29.mkdir(datedDir, { recursive: true });
21380
+ const sessionFile = path32.join(datedDir, `${sessionId}.json`);
20974
21381
  const newSessionData = {
20975
21382
  session_id: sessionId,
20976
21383
  created_at: now2.toISOString(),
20977
21384
  conversation_history: []
20978
21385
  };
20979
- await fs28.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
20980
- const relToApp = path30.relative(getPreferredAppDir(), sessionFile);
21386
+ await fs29.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
21387
+ const relToApp = path32.relative(getPreferredAppDir(), sessionFile);
20981
21388
  await appendAgentSessionPath(sessionId, relToApp);
20982
21389
  const emptyMemory = {
20983
21390
  historyAnchor: null,
@@ -20989,12 +21396,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
20989
21396
  await withFileLock(sessionFile, async () => {
20990
21397
  let sessionData;
20991
21398
  try {
20992
- const dir = path30.dirname(sessionFile);
20993
- await fs28.mkdir(dir, { recursive: true });
21399
+ const dir = path32.dirname(sessionFile);
21400
+ await fs29.mkdir(dir, { recursive: true });
20994
21401
  } catch {
20995
21402
  }
20996
21403
  try {
20997
- const fileContent = await fs28.readFile(sessionFile, "utf-8");
21404
+ const fileContent = await fs29.readFile(sessionFile, "utf-8");
20998
21405
  sessionData = JSON.parse(fileContent);
20999
21406
  } catch (error) {
21000
21407
  const code = error && error.code;
@@ -21005,14 +21412,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
21005
21412
  console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
21006
21413
  }
21007
21414
  }
21008
- const sessionId = path30.basename(sessionFile, ".json");
21415
+ const sessionId = path32.basename(sessionFile, ".json");
21009
21416
  sessionData = {
21010
21417
  session_id: sessionId,
21011
21418
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
21012
21419
  conversation_history: []
21013
21420
  };
21014
21421
  try {
21015
- await fs28.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
21422
+ await fs29.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
21016
21423
  } catch {
21017
21424
  }
21018
21425
  }
@@ -21028,8 +21435,26 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
21028
21435
  }
21029
21436
  const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
21030
21437
  try {
21031
- await fs28.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
21438
+ await fs29.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
21032
21439
  await safeRenameWithRetry(tempSessionFile, sessionFile);
21440
+ const sessionId = sessionData.session_id ?? path32.basename(sessionFile, ".json");
21441
+ const updatedAt = sessionData.last_updated ?? (/* @__PURE__ */ new Date()).toISOString();
21442
+ const createdAt = sessionData.created_at ?? updatedAt;
21443
+ let relToApp;
21444
+ try {
21445
+ relToApp = path32.relative(getPreferredAppDir(), sessionFile);
21446
+ } catch {
21447
+ relToApp = sessionFile;
21448
+ }
21449
+ await upsertSessionIndexRow({
21450
+ sessionId,
21451
+ relativePath: relToApp,
21452
+ createdAt,
21453
+ updatedAt,
21454
+ preview: previewFromConversationHistory(
21455
+ history
21456
+ )
21457
+ });
21033
21458
  } catch (writeError) {
21034
21459
  if (writeError instanceof Error) {
21035
21460
  console.error(`Fatal error saving session to ${sessionFile}: ${writeError.message}`);
@@ -21037,7 +21462,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
21037
21462
  console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
21038
21463
  }
21039
21464
  try {
21040
- await fs28.unlink(tempSessionFile);
21465
+ await fs29.unlink(tempSessionFile);
21041
21466
  } catch {
21042
21467
  }
21043
21468
  }
@@ -21052,16 +21477,30 @@ async function saveSessionHistory(sessionFile, history, memory) {
21052
21477
  });
21053
21478
  debouncedSave(sessionFile, cleanHistory, memory);
21054
21479
  }
21480
+ async function saveSessionHistoryNow(sessionFile, history, memory) {
21481
+ const pending = pendingSaves.get(sessionFile);
21482
+ if (pending) {
21483
+ clearTimeout(pending.timer);
21484
+ pendingSaves.delete(sessionFile);
21485
+ }
21486
+ const cleanHistory = history.filter((msg) => {
21487
+ if (msg.role === "user" && typeof msg.content === "string") {
21488
+ return !msg.content.startsWith("[SKILL:");
21489
+ }
21490
+ return true;
21491
+ });
21492
+ await doSaveSessionHistory(sessionFile, cleanHistory, memory);
21493
+ }
21055
21494
 
21056
21495
  // src/app/agent/core/prompt/prompt_builder.ts
21057
21496
  import os24 from "os";
21058
- import fs35 from "fs";
21059
- import path37 from "path";
21497
+ import fs36 from "fs";
21498
+ import path39 from "path";
21060
21499
  import { execSync as execSync3 } from "child_process";
21061
21500
 
21062
21501
  // src/app/agent/skills/skill_loader.ts
21063
- import fs29 from "fs";
21064
- import path31 from "path";
21502
+ import fs30 from "fs";
21503
+ import path33 from "path";
21065
21504
  import os20 from "os";
21066
21505
  import { fileURLToPath as fileURLToPath3 } from "node:url";
21067
21506
  var SkillLoader = class _SkillLoader {
@@ -21071,8 +21510,8 @@ var SkillLoader = class _SkillLoader {
21071
21510
  cache = /* @__PURE__ */ new Map();
21072
21511
  conflicts = [];
21073
21512
  constructor(projectRoot, bundledDir) {
21074
- this.projectSkillsDir = path31.join(projectRoot, ".bluma", "skills");
21075
- this.globalSkillsDir = path31.join(os20.homedir(), ".bluma", "skills");
21513
+ this.projectSkillsDir = path33.join(projectRoot, ".bluma", "skills");
21514
+ this.globalSkillsDir = path33.join(os20.homedir(), ".bluma", "skills");
21076
21515
  this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
21077
21516
  }
21078
21517
  /**
@@ -21081,48 +21520,48 @@ var SkillLoader = class _SkillLoader {
21081
21520
  */
21082
21521
  static resolveBundledDir() {
21083
21522
  if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
21084
- return path31.join(process.cwd(), "dist", "config", "skills");
21523
+ return path33.join(process.cwd(), "dist", "config", "skills");
21085
21524
  }
21086
21525
  const candidates = [];
21087
21526
  const push = (p) => {
21088
- const abs = path31.resolve(p);
21527
+ const abs = path33.resolve(p);
21089
21528
  if (!candidates.includes(abs)) {
21090
21529
  candidates.push(abs);
21091
21530
  }
21092
21531
  };
21093
21532
  let argvBundled = null;
21094
21533
  try {
21095
- const bundleDir = path31.dirname(fileURLToPath3(import.meta.url));
21096
- push(path31.join(bundleDir, "config", "skills"));
21534
+ const bundleDir = path33.dirname(fileURLToPath3(import.meta.url));
21535
+ push(path33.join(bundleDir, "config", "skills"));
21097
21536
  } catch {
21098
21537
  }
21099
21538
  const argv1 = process.argv[1];
21100
21539
  if (argv1 && !argv1.startsWith("-")) {
21101
21540
  try {
21102
21541
  let resolved = argv1;
21103
- if (path31.isAbsolute(argv1) && fs29.existsSync(argv1)) {
21104
- resolved = fs29.realpathSync(argv1);
21105
- } else if (!path31.isAbsolute(argv1)) {
21106
- resolved = path31.resolve(process.cwd(), argv1);
21542
+ if (path33.isAbsolute(argv1) && fs30.existsSync(argv1)) {
21543
+ resolved = fs30.realpathSync(argv1);
21544
+ } else if (!path33.isAbsolute(argv1)) {
21545
+ resolved = path33.resolve(process.cwd(), argv1);
21107
21546
  }
21108
- const scriptDir = path31.dirname(resolved);
21109
- argvBundled = path31.join(scriptDir, "config", "skills");
21547
+ const scriptDir = path33.dirname(resolved);
21548
+ argvBundled = path33.join(scriptDir, "config", "skills");
21110
21549
  push(argvBundled);
21111
21550
  } catch {
21112
21551
  }
21113
21552
  }
21114
21553
  for (const abs of candidates) {
21115
- if (fs29.existsSync(abs)) {
21554
+ if (fs30.existsSync(abs)) {
21116
21555
  return abs;
21117
21556
  }
21118
21557
  }
21119
21558
  try {
21120
- return path31.join(path31.dirname(fileURLToPath3(import.meta.url)), "config", "skills");
21559
+ return path33.join(path33.dirname(fileURLToPath3(import.meta.url)), "config", "skills");
21121
21560
  } catch {
21122
21561
  if (argvBundled) {
21123
21562
  return argvBundled;
21124
21563
  }
21125
- return path31.join(os20.homedir(), ".bluma", "__bundled_skills_unresolved__");
21564
+ return path33.join(os20.homedir(), ".bluma", "__bundled_skills_unresolved__");
21126
21565
  }
21127
21566
  }
21128
21567
  /**
@@ -21151,8 +21590,8 @@ var SkillLoader = class _SkillLoader {
21151
21590
  this.conflicts.push({
21152
21591
  name: skill.name,
21153
21592
  userSource: source,
21154
- userPath: path31.join(dir, skill.name, "SKILL.md"),
21155
- bundledPath: path31.join(this.bundledSkillsDir, skill.name, "SKILL.md")
21593
+ userPath: path33.join(dir, skill.name, "SKILL.md"),
21594
+ bundledPath: path33.join(this.bundledSkillsDir, skill.name, "SKILL.md")
21156
21595
  });
21157
21596
  continue;
21158
21597
  }
@@ -21160,20 +21599,20 @@ var SkillLoader = class _SkillLoader {
21160
21599
  }
21161
21600
  }
21162
21601
  listFromDir(dir, source) {
21163
- if (!fs29.existsSync(dir)) return [];
21602
+ if (!fs30.existsSync(dir)) return [];
21164
21603
  try {
21165
- return fs29.readdirSync(dir).filter((d) => {
21166
- const fullPath = path31.join(dir, d);
21167
- return fs29.statSync(fullPath).isDirectory() && fs29.existsSync(path31.join(fullPath, "SKILL.md"));
21168
- }).map((d) => this.loadMetadataFromPath(path31.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
21604
+ return fs30.readdirSync(dir).filter((d) => {
21605
+ const fullPath = path33.join(dir, d);
21606
+ return fs30.statSync(fullPath).isDirectory() && fs30.existsSync(path33.join(fullPath, "SKILL.md"));
21607
+ }).map((d) => this.loadMetadataFromPath(path33.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
21169
21608
  } catch {
21170
21609
  return [];
21171
21610
  }
21172
21611
  }
21173
21612
  loadMetadataFromPath(skillPath, skillName, source) {
21174
- if (!fs29.existsSync(skillPath)) return null;
21613
+ if (!fs30.existsSync(skillPath)) return null;
21175
21614
  try {
21176
- const raw = fs29.readFileSync(skillPath, "utf-8");
21615
+ const raw = fs30.readFileSync(skillPath, "utf-8");
21177
21616
  const parsed = this.parseFrontmatter(raw);
21178
21617
  return {
21179
21618
  name: parsed.name || skillName,
@@ -21195,12 +21634,12 @@ var SkillLoader = class _SkillLoader {
21195
21634
  */
21196
21635
  load(name) {
21197
21636
  if (this.cache.has(name)) return this.cache.get(name);
21198
- const bundledPath = path31.join(this.bundledSkillsDir, name, "SKILL.md");
21199
- const projectPath = path31.join(this.projectSkillsDir, name, "SKILL.md");
21200
- const globalPath = path31.join(this.globalSkillsDir, name, "SKILL.md");
21201
- const existsBundled = fs29.existsSync(bundledPath);
21202
- const existsProject = fs29.existsSync(projectPath);
21203
- const existsGlobal = fs29.existsSync(globalPath);
21637
+ const bundledPath = path33.join(this.bundledSkillsDir, name, "SKILL.md");
21638
+ const projectPath = path33.join(this.projectSkillsDir, name, "SKILL.md");
21639
+ const globalPath = path33.join(this.globalSkillsDir, name, "SKILL.md");
21640
+ const existsBundled = fs30.existsSync(bundledPath);
21641
+ const existsProject = fs30.existsSync(projectPath);
21642
+ const existsGlobal = fs30.existsSync(globalPath);
21204
21643
  if (existsBundled && (existsProject || existsGlobal)) {
21205
21644
  const conflictSource = existsProject ? "project" : "global";
21206
21645
  const conflictPath = existsProject ? projectPath : globalPath;
@@ -21239,9 +21678,9 @@ var SkillLoader = class _SkillLoader {
21239
21678
  }
21240
21679
  loadFromPath(skillPath, name, source) {
21241
21680
  try {
21242
- const raw = fs29.readFileSync(skillPath, "utf-8");
21681
+ const raw = fs30.readFileSync(skillPath, "utf-8");
21243
21682
  const parsed = this.parseFrontmatter(raw);
21244
- const skillDir = path31.dirname(skillPath);
21683
+ const skillDir = path33.dirname(skillPath);
21245
21684
  return {
21246
21685
  name: parsed.name || name,
21247
21686
  description: parsed.description || "",
@@ -21250,22 +21689,22 @@ var SkillLoader = class _SkillLoader {
21250
21689
  version: parsed.version,
21251
21690
  author: parsed.author,
21252
21691
  license: parsed.license,
21253
- references: this.scanAssets(path31.join(skillDir, "references")),
21254
- scripts: this.scanAssets(path31.join(skillDir, "scripts"))
21692
+ references: this.scanAssets(path33.join(skillDir, "references")),
21693
+ scripts: this.scanAssets(path33.join(skillDir, "scripts"))
21255
21694
  };
21256
21695
  } catch {
21257
21696
  return null;
21258
21697
  }
21259
21698
  }
21260
21699
  scanAssets(dir) {
21261
- if (!fs29.existsSync(dir)) return [];
21700
+ if (!fs30.existsSync(dir)) return [];
21262
21701
  try {
21263
- return fs29.readdirSync(dir).filter((f) => {
21264
- const fp = path31.join(dir, f);
21265
- return fs29.statSync(fp).isFile();
21702
+ return fs30.readdirSync(dir).filter((f) => {
21703
+ const fp = path33.join(dir, f);
21704
+ return fs30.statSync(fp).isFile();
21266
21705
  }).map((f) => ({
21267
21706
  name: f,
21268
- path: path31.resolve(dir, f)
21707
+ path: path33.resolve(dir, f)
21269
21708
  }));
21270
21709
  } catch {
21271
21710
  return [];
@@ -21322,10 +21761,10 @@ var SkillLoader = class _SkillLoader {
21322
21761
  this.cache.clear();
21323
21762
  }
21324
21763
  exists(name) {
21325
- const bundledPath = path31.join(this.bundledSkillsDir, name, "SKILL.md");
21326
- const projectPath = path31.join(this.projectSkillsDir, name, "SKILL.md");
21327
- const globalPath = path31.join(this.globalSkillsDir, name, "SKILL.md");
21328
- return fs29.existsSync(bundledPath) || fs29.existsSync(projectPath) || fs29.existsSync(globalPath);
21764
+ const bundledPath = path33.join(this.bundledSkillsDir, name, "SKILL.md");
21765
+ const projectPath = path33.join(this.projectSkillsDir, name, "SKILL.md");
21766
+ const globalPath = path33.join(this.globalSkillsDir, name, "SKILL.md");
21767
+ return fs30.existsSync(bundledPath) || fs30.existsSync(projectPath) || fs30.existsSync(globalPath);
21329
21768
  }
21330
21769
  /**
21331
21770
  * Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
@@ -21357,13 +21796,13 @@ var SkillLoader = class _SkillLoader {
21357
21796
  };
21358
21797
 
21359
21798
  // src/app/agent/core/prompt/workspace_snapshot.ts
21360
- import fs31 from "fs";
21361
- import path33 from "path";
21799
+ import fs32 from "fs";
21800
+ import path35 from "path";
21362
21801
  import { execSync as execSync2 } from "child_process";
21363
21802
 
21364
21803
  // src/app/agent/utils/blumamd.ts
21365
- import fs30 from "fs";
21366
- import path32 from "path";
21804
+ import fs31 from "fs";
21805
+ import path34 from "path";
21367
21806
  import os21 from "os";
21368
21807
  import { execSync } from "child_process";
21369
21808
  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.";
@@ -21493,12 +21932,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
21493
21932
  function expandIncludePath(includePath, baseDir) {
21494
21933
  const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
21495
21934
  if (cleanPath.startsWith("~")) {
21496
- return path32.join(os21.homedir(), cleanPath.slice(1));
21935
+ return path34.join(os21.homedir(), cleanPath.slice(1));
21497
21936
  }
21498
- if (path32.isAbsolute(cleanPath)) {
21937
+ if (path34.isAbsolute(cleanPath)) {
21499
21938
  return cleanPath;
21500
21939
  }
21501
- return path32.resolve(baseDir, cleanPath);
21940
+ return path34.resolve(baseDir, cleanPath);
21502
21941
  }
21503
21942
  function processIncludes(content, baseDir, processedFiles) {
21504
21943
  const lines = content.split("\n");
@@ -21507,20 +21946,20 @@ function processIncludes(content, baseDir, processedFiles) {
21507
21946
  const includeMatch = line.match(/^@\s*([^\s]+)/);
21508
21947
  if (includeMatch) {
21509
21948
  const includePath = expandIncludePath(includeMatch[1], baseDir);
21510
- const normalizedPath = path32.normalize(includePath);
21949
+ const normalizedPath = path34.normalize(includePath);
21511
21950
  if (processedFiles.has(normalizedPath)) {
21512
21951
  result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
21513
21952
  continue;
21514
21953
  }
21515
- const ext = path32.extname(includePath).toLowerCase();
21954
+ const ext = path34.extname(includePath).toLowerCase();
21516
21955
  if (!TEXT_FILE_EXTENSIONS.has(ext)) {
21517
21956
  result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
21518
21957
  continue;
21519
21958
  }
21520
21959
  try {
21521
- const includedContent = fs30.readFileSync(includePath, "utf-8");
21960
+ const includedContent = fs31.readFileSync(includePath, "utf-8");
21522
21961
  processedFiles.add(normalizedPath);
21523
- const processedContent = processIncludes(includedContent, path32.dirname(includePath), processedFiles);
21962
+ const processedContent = processIncludes(includedContent, path34.dirname(includePath), processedFiles);
21524
21963
  result.push(`
21525
21964
  <!-- BEGIN INCLUDE ${includeMatch[1]} -->
21526
21965
  `);
@@ -21566,9 +22005,9 @@ function parseFrontmatterPaths(paths2) {
21566
22005
  }
21567
22006
  function readMemoryFile(filePath, type, includeBasePath) {
21568
22007
  try {
21569
- const rawContent = fs30.readFileSync(filePath, "utf-8");
21570
- const baseDir = includeBasePath || path32.dirname(filePath);
21571
- const processedFiles = /* @__PURE__ */ new Set([path32.normalize(filePath)]);
22008
+ const rawContent = fs31.readFileSync(filePath, "utf-8");
22009
+ const baseDir = includeBasePath || path34.dirname(filePath);
22010
+ const processedFiles = /* @__PURE__ */ new Set([path34.normalize(filePath)]);
21572
22011
  const { frontmatter, content: withoutFrontmatter } = parseFrontmatter(rawContent);
21573
22012
  const globs = parseFrontmatterPaths(frontmatter.paths);
21574
22013
  const processedContent = processIncludes(withoutFrontmatter, baseDir, processedFiles);
@@ -21590,15 +22029,15 @@ function readMemoryFile(filePath, type, includeBasePath) {
21590
22029
  }
21591
22030
  function findGitRoot(startDir) {
21592
22031
  let current = startDir;
21593
- while (current !== path32.dirname(current)) {
21594
- const gitPath = path32.join(current, ".git");
22032
+ while (current !== path34.dirname(current)) {
22033
+ const gitPath = path34.join(current, ".git");
21595
22034
  try {
21596
- if (fs30.existsSync(gitPath)) {
22035
+ if (fs31.existsSync(gitPath)) {
21597
22036
  return current;
21598
22037
  }
21599
22038
  } catch {
21600
22039
  }
21601
- current = path32.dirname(current);
22040
+ current = path34.dirname(current);
21602
22041
  }
21603
22042
  return null;
21604
22043
  }
@@ -21623,17 +22062,17 @@ function getGitUserInfo(cwd2) {
21623
22062
  }
21624
22063
  function processRulesDirectory(rulesDir, type, processedPaths, conditionalRule = false) {
21625
22064
  const result = [];
21626
- if (!fs30.existsSync(rulesDir)) {
22065
+ if (!fs31.existsSync(rulesDir)) {
21627
22066
  return result;
21628
22067
  }
21629
22068
  try {
21630
- const entries = fs30.readdirSync(rulesDir, { withFileTypes: true });
22069
+ const entries = fs31.readdirSync(rulesDir, { withFileTypes: true });
21631
22070
  for (const entry of entries) {
21632
- const entryPath = path32.join(rulesDir, entry.name);
22071
+ const entryPath = path34.join(rulesDir, entry.name);
21633
22072
  if (entry.isDirectory()) {
21634
22073
  result.push(...processRulesDirectory(entryPath, type, processedPaths, conditionalRule));
21635
22074
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
21636
- const normalizedPath = path32.normalize(entryPath);
22075
+ const normalizedPath = path34.normalize(entryPath);
21637
22076
  if (processedPaths.has(normalizedPath)) {
21638
22077
  continue;
21639
22078
  }
@@ -21669,13 +22108,13 @@ function loadManagedMemory() {
21669
22108
  function loadUserMemory() {
21670
22109
  const files = [];
21671
22110
  const homeDir = os21.homedir();
21672
- const userBlumaDir = path32.join(homeDir, ".bluma");
21673
- const userBlumaMd = path32.join(userBlumaDir, "BLUMA.md");
22111
+ const userBlumaDir = path34.join(homeDir, ".bluma");
22112
+ const userBlumaMd = path34.join(userBlumaDir, "BLUMA.md");
21674
22113
  const userFile = readMemoryFile(userBlumaMd, "User");
21675
22114
  if (userFile && userFile.content.trim()) {
21676
22115
  files.push(userFile);
21677
22116
  }
21678
- const userRulesDir = path32.join(userBlumaDir, "rules");
22117
+ const userRulesDir = path34.join(userBlumaDir, "rules");
21679
22118
  const processedPaths = /* @__PURE__ */ new Set();
21680
22119
  files.push(...processRulesDirectory(userRulesDir, "User", processedPaths, false));
21681
22120
  return files;
@@ -21687,10 +22126,10 @@ function loadProjectMemory(cwd2) {
21687
22126
  let currentDir = cwd2;
21688
22127
  const MAX_TRAVERSAL_DEPTH = 20;
21689
22128
  let depth = 0;
21690
- const normalizedGitRoot = path32.resolve(gitRoot);
21691
- while (currentDir !== path32.dirname(currentDir) && path32.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
22129
+ const normalizedGitRoot = path34.resolve(gitRoot);
22130
+ while (currentDir !== path34.dirname(currentDir) && path34.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
21692
22131
  dirs.push(currentDir);
21693
- currentDir = path32.dirname(currentDir);
22132
+ currentDir = path34.dirname(currentDir);
21694
22133
  depth++;
21695
22134
  }
21696
22135
  if (!dirs.includes(gitRoot)) {
@@ -21698,7 +22137,7 @@ function loadProjectMemory(cwd2) {
21698
22137
  }
21699
22138
  const processedPaths = /* @__PURE__ */ new Set();
21700
22139
  for (const dir of dirs.reverse()) {
21701
- const projectBlumaMd = path32.join(dir, "BLUMA.md");
22140
+ const projectBlumaMd = path34.join(dir, "BLUMA.md");
21702
22141
  if (!processedPaths.has(projectBlumaMd)) {
21703
22142
  processedPaths.add(projectBlumaMd);
21704
22143
  const projectFile = readMemoryFile(projectBlumaMd, "Project");
@@ -21706,7 +22145,7 @@ function loadProjectMemory(cwd2) {
21706
22145
  files.push(projectFile);
21707
22146
  }
21708
22147
  }
21709
- const blumaDirBlumaMd = path32.join(dir, ".bluma", "BLUMA.md");
22148
+ const blumaDirBlumaMd = path34.join(dir, ".bluma", "BLUMA.md");
21710
22149
  if (!processedPaths.has(blumaDirBlumaMd)) {
21711
22150
  processedPaths.add(blumaDirBlumaMd);
21712
22151
  const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "Project");
@@ -21714,10 +22153,10 @@ function loadProjectMemory(cwd2) {
21714
22153
  files.push(blumaDirFile);
21715
22154
  }
21716
22155
  }
21717
- const rulesDir = path32.join(dir, ".bluma", "rules");
22156
+ const rulesDir = path34.join(dir, ".bluma", "rules");
21718
22157
  files.push(...processRulesDirectory(rulesDir, "Project", processedPaths, false));
21719
22158
  }
21720
- const localBlumaMd = path32.join(cwd2, "BLUMA.local.md");
22159
+ const localBlumaMd = path34.join(cwd2, "BLUMA.local.md");
21721
22160
  if (!processedPaths.has(localBlumaMd)) {
21722
22161
  processedPaths.add(localBlumaMd);
21723
22162
  const localFile = readMemoryFile(localBlumaMd, "Local");
@@ -21803,10 +22242,10 @@ var LIMITS = {
21803
22242
  };
21804
22243
  function safeReadFile(filePath, maxChars) {
21805
22244
  try {
21806
- if (!fs31.existsSync(filePath)) return null;
21807
- const st = fs31.statSync(filePath);
22245
+ if (!fs32.existsSync(filePath)) return null;
22246
+ const st = fs32.statSync(filePath);
21808
22247
  if (!st.isFile()) return null;
21809
- const raw = fs31.readFileSync(filePath, "utf8");
22248
+ const raw = fs32.readFileSync(filePath, "utf8");
21810
22249
  if (raw.includes("\0")) return null;
21811
22250
  if (raw.length <= maxChars) return raw;
21812
22251
  return `${raw.slice(0, maxChars)}
@@ -21818,7 +22257,7 @@ function safeReadFile(filePath, maxChars) {
21818
22257
  }
21819
22258
  function tryReadReadme(cwd2) {
21820
22259
  for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
21821
- const c = safeReadFile(path33.join(cwd2, name), LIMITS.readme);
22260
+ const c = safeReadFile(path35.join(cwd2, name), LIMITS.readme);
21822
22261
  if (c) return `(${name})
21823
22262
  ${c}`;
21824
22263
  }
@@ -21826,14 +22265,14 @@ ${c}`;
21826
22265
  }
21827
22266
  function tryReadBluMaMd(cwd2) {
21828
22267
  const paths2 = [
21829
- path33.join(cwd2, "BluMa.md"),
21830
- path33.join(cwd2, "BLUMA.md"),
21831
- path33.join(cwd2, ".bluma", "BluMa.md")
22268
+ path35.join(cwd2, "BluMa.md"),
22269
+ path35.join(cwd2, "BLUMA.md"),
22270
+ path35.join(cwd2, ".bluma", "BluMa.md")
21832
22271
  ];
21833
22272
  for (const p of paths2) {
21834
22273
  const c = safeReadFile(p, LIMITS.blumaMd);
21835
22274
  if (c) {
21836
- const rel = path33.relative(cwd2, p) || p;
22275
+ const rel = path35.relative(cwd2, p) || p;
21837
22276
  return `(${rel})
21838
22277
  ${c}`;
21839
22278
  }
@@ -21841,10 +22280,10 @@ ${c}`;
21841
22280
  return null;
21842
22281
  }
21843
22282
  function summarizePackageJson(cwd2) {
21844
- const p = path33.join(cwd2, "package.json");
22283
+ const p = path35.join(cwd2, "package.json");
21845
22284
  try {
21846
- if (!fs31.existsSync(p)) return null;
21847
- const pkg = JSON.parse(fs31.readFileSync(p, "utf8"));
22285
+ if (!fs32.existsSync(p)) return null;
22286
+ const pkg = JSON.parse(fs32.readFileSync(p, "utf8"));
21848
22287
  const scripts = pkg.scripts;
21849
22288
  let scriptKeys = "";
21850
22289
  if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
@@ -21877,7 +22316,7 @@ function summarizePackageJson(cwd2) {
21877
22316
  }
21878
22317
  function topLevelListing(cwd2) {
21879
22318
  try {
21880
- const names = fs31.readdirSync(cwd2, { withFileTypes: true });
22319
+ const names = fs32.readdirSync(cwd2, { withFileTypes: true });
21881
22320
  const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
21882
22321
  const limited = sorted.slice(0, LIMITS.topDirEntries);
21883
22322
  const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
@@ -21935,7 +22374,7 @@ function buildWorkspaceSnapshot(cwd2) {
21935
22374
  parts.push(pkg);
21936
22375
  parts.push("```\n");
21937
22376
  }
21938
- const py = safeReadFile(path33.join(cwd2, "pyproject.toml"), LIMITS.pyproject);
22377
+ const py = safeReadFile(path35.join(cwd2, "pyproject.toml"), LIMITS.pyproject);
21939
22378
  if (py) {
21940
22379
  parts.push("### pyproject.toml (excerpt)\n```toml");
21941
22380
  parts.push(py);
@@ -21953,15 +22392,15 @@ function buildWorkspaceSnapshot(cwd2) {
21953
22392
  parts.push(bluma);
21954
22393
  parts.push("```\n");
21955
22394
  }
21956
- const contrib = safeReadFile(path33.join(cwd2, "CONTRIBUTING.md"), LIMITS.contributing);
22395
+ const contrib = safeReadFile(path35.join(cwd2, "CONTRIBUTING.md"), LIMITS.contributing);
21957
22396
  if (contrib) {
21958
22397
  parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
21959
22398
  parts.push(contrib);
21960
22399
  parts.push("```\n");
21961
22400
  }
21962
- const chlog = safeReadFile(path33.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
22401
+ const chlog = safeReadFile(path35.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
21963
22402
  if (!chlog) {
21964
- const alt = safeReadFile(path33.join(cwd2, "CHANGES.md"), LIMITS.changelog);
22403
+ const alt = safeReadFile(path35.join(cwd2, "CHANGES.md"), LIMITS.changelog);
21965
22404
  if (alt) {
21966
22405
  parts.push("### CHANGES.md (excerpt)\n```markdown");
21967
22406
  parts.push(alt);
@@ -21997,14 +22436,14 @@ function buildWorkspaceSnapshot(cwd2) {
21997
22436
  } else {
21998
22437
  parts.push("### Git\n(not a git work tree, or `git` unavailable)\n");
21999
22438
  }
22000
- const tsconfig = safeReadFile(path33.join(cwd2, "tsconfig.json"), LIMITS.tsconfig);
22439
+ const tsconfig = safeReadFile(path35.join(cwd2, "tsconfig.json"), LIMITS.tsconfig);
22001
22440
  if (tsconfig) {
22002
22441
  parts.push("### tsconfig.json (excerpt)\n```json");
22003
22442
  parts.push(tsconfig);
22004
22443
  parts.push("```\n");
22005
22444
  }
22006
22445
  for (const name of ["Dockerfile", "dockerfile", "Dockerfile.prod", "Dockerfile.dev"]) {
22007
- const df = safeReadFile(path33.join(cwd2, name), LIMITS.dockerfile);
22446
+ const df = safeReadFile(path35.join(cwd2, name), LIMITS.dockerfile);
22008
22447
  if (df) {
22009
22448
  parts.push(`### ${name} (excerpt)
22010
22449
  `);
@@ -22014,7 +22453,7 @@ function buildWorkspaceSnapshot(cwd2) {
22014
22453
  }
22015
22454
  }
22016
22455
  for (const name of ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"]) {
22017
- const dc = safeReadFile(path33.join(cwd2, name), LIMITS.dockerfile);
22456
+ const dc = safeReadFile(path35.join(cwd2, name), LIMITS.dockerfile);
22018
22457
  if (dc) {
22019
22458
  parts.push(`### ${name} (excerpt)
22020
22459
  `);
@@ -22029,12 +22468,12 @@ function buildWorkspaceSnapshot(cwd2) {
22029
22468
  ".circleci/config.yml",
22030
22469
  "Jenkinsfile"
22031
22470
  ]) {
22032
- const ciFile = path33.join(cwd2, ciPath);
22033
- if (fs31.existsSync(ciFile)) {
22034
- const st = fs31.statSync(ciFile);
22471
+ const ciFile = path35.join(cwd2, ciPath);
22472
+ if (fs32.existsSync(ciFile)) {
22473
+ const st = fs32.statSync(ciFile);
22035
22474
  if (st.isDirectory()) {
22036
22475
  try {
22037
- const wfFiles = fs31.readdirSync(ciFile).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
22476
+ const wfFiles = fs32.readdirSync(ciFile).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
22038
22477
  if (wfFiles.length > 0) {
22039
22478
  parts.push(`### GitHub Actions workflows
22040
22479
  \`${wfFiles.join("`, `")}\`
@@ -22045,7 +22484,7 @@ function buildWorkspaceSnapshot(cwd2) {
22045
22484
  } else {
22046
22485
  const ci = safeReadFile(ciFile, LIMITS.ciConfig);
22047
22486
  if (ci) {
22048
- parts.push(`### CI config (${path33.basename(ciPath)})
22487
+ parts.push(`### CI config (${path35.basename(ciPath)})
22049
22488
  `);
22050
22489
  parts.push(ci);
22051
22490
  parts.push("\n");
@@ -22054,8 +22493,8 @@ function buildWorkspaceSnapshot(cwd2) {
22054
22493
  }
22055
22494
  }
22056
22495
  for (const depFile of ["requirements.txt", "Pipfile", "poetry.lock", "Gemfile", "go.sum", "Cargo.lock"]) {
22057
- const depPath = path33.join(cwd2, depFile);
22058
- if (fs31.existsSync(depPath)) {
22496
+ const depPath = path35.join(cwd2, depFile);
22497
+ if (fs32.existsSync(depPath)) {
22059
22498
  const depContent = safeReadFile(depPath, 1500);
22060
22499
  if (depContent) {
22061
22500
  parts.push(`### ${depFile} (top entries)
@@ -22068,10 +22507,10 @@ function buildWorkspaceSnapshot(cwd2) {
22068
22507
  }
22069
22508
  }
22070
22509
  for (const envFile of [".env.example", ".env.sample", ".env.template"]) {
22071
- const envPath = path33.join(cwd2, envFile);
22072
- if (fs31.existsSync(envPath)) {
22510
+ const envPath = path35.join(cwd2, envFile);
22511
+ if (fs32.existsSync(envPath)) {
22073
22512
  try {
22074
- const raw = fs31.readFileSync(envPath, "utf-8");
22513
+ const raw = fs32.readFileSync(envPath, "utf-8");
22075
22514
  const keys = raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).map((l) => {
22076
22515
  const eqIndex = l.indexOf("=");
22077
22516
  return eqIndex >= 0 ? l.slice(0, eqIndex).trim() : l.trim();
@@ -22094,15 +22533,15 @@ init_runtime_config();
22094
22533
 
22095
22534
  // src/app/agent/runtime/plugin_registry.ts
22096
22535
  init_sandbox_policy();
22097
- import fs32 from "fs";
22536
+ import fs33 from "fs";
22098
22537
  import os22 from "os";
22099
- import path34 from "path";
22538
+ import path36 from "path";
22100
22539
  function getProjectPluginsDir() {
22101
22540
  const policy = getSandboxPolicy();
22102
- return path34.join(policy.workspaceRoot, ".bluma", "plugins");
22541
+ return path36.join(policy.workspaceRoot, ".bluma", "plugins");
22103
22542
  }
22104
22543
  function getGlobalPluginsDir() {
22105
- return path34.join(process.env.HOME || os22.homedir(), ".bluma", "plugins");
22544
+ return path36.join(process.env.HOME || os22.homedir(), ".bluma", "plugins");
22106
22545
  }
22107
22546
  function getPluginDirs() {
22108
22547
  return {
@@ -22111,11 +22550,11 @@ function getPluginDirs() {
22111
22550
  };
22112
22551
  }
22113
22552
  function readManifest(manifestPath, fallbackName) {
22114
- if (!fs32.existsSync(manifestPath)) {
22553
+ if (!fs33.existsSync(manifestPath)) {
22115
22554
  return null;
22116
22555
  }
22117
22556
  try {
22118
- const parsed = JSON.parse(fs32.readFileSync(manifestPath, "utf-8"));
22557
+ const parsed = JSON.parse(fs33.readFileSync(manifestPath, "utf-8"));
22119
22558
  return {
22120
22559
  name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
22121
22560
  description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
@@ -22128,22 +22567,22 @@ function readManifest(manifestPath, fallbackName) {
22128
22567
  }
22129
22568
  function findManifestPath(pluginDir) {
22130
22569
  const candidates = [
22131
- path34.join(pluginDir, ".codex-plugin", "plugin.json"),
22132
- path34.join(pluginDir, "plugin.json")
22570
+ path36.join(pluginDir, ".codex-plugin", "plugin.json"),
22571
+ path36.join(pluginDir, "plugin.json")
22133
22572
  ];
22134
22573
  for (const candidate of candidates) {
22135
- if (fs32.existsSync(candidate)) {
22574
+ if (fs33.existsSync(candidate)) {
22136
22575
  return candidate;
22137
22576
  }
22138
22577
  }
22139
22578
  return null;
22140
22579
  }
22141
22580
  function listFromDir(baseDir, source) {
22142
- if (!fs32.existsSync(baseDir)) {
22581
+ if (!fs33.existsSync(baseDir)) {
22143
22582
  return [];
22144
22583
  }
22145
- return fs32.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
22146
- const pluginDir = path34.join(baseDir, entry.name);
22584
+ return fs33.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
22585
+ const pluginDir = path36.join(baseDir, entry.name);
22147
22586
  const manifestPath = findManifestPath(pluginDir);
22148
22587
  if (!manifestPath) {
22149
22588
  return [];
@@ -22588,8 +23027,8 @@ function buildModelInfoSection(modelId) {
22588
23027
 
22589
23028
  // src/app/agent/runtime/hook_registry.ts
22590
23029
  init_sandbox_policy();
22591
- import fs33 from "fs";
22592
- import path35 from "path";
23030
+ import fs34 from "fs";
23031
+ import path37 from "path";
22593
23032
  var DEFAULT_STATE = {
22594
23033
  enabled: true,
22595
23034
  maxEvents: 120,
@@ -22600,7 +23039,7 @@ var cache3 = null;
22600
23039
  var cachePath2 = null;
22601
23040
  function getStatePath() {
22602
23041
  const policy = getSandboxPolicy();
22603
- return path35.join(policy.workspaceRoot, ".bluma", "hooks.json");
23042
+ return path37.join(policy.workspaceRoot, ".bluma", "hooks.json");
22604
23043
  }
22605
23044
  function getHookStatePath() {
22606
23045
  return getStatePath();
@@ -22619,8 +23058,8 @@ function ensureLoaded2() {
22619
23058
  return cache3;
22620
23059
  }
22621
23060
  try {
22622
- if (fs33.existsSync(statePath)) {
22623
- const parsed = JSON.parse(fs33.readFileSync(statePath, "utf-8"));
23061
+ if (fs34.existsSync(statePath)) {
23062
+ const parsed = JSON.parse(fs34.readFileSync(statePath, "utf-8"));
22624
23063
  cache3 = {
22625
23064
  enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
22626
23065
  maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
@@ -22646,9 +23085,9 @@ function ensureLoaded2() {
22646
23085
  }
22647
23086
  function persist2(state2) {
22648
23087
  const statePath = getStatePath();
22649
- fs33.mkdirSync(path35.dirname(statePath), { recursive: true });
23088
+ fs34.mkdirSync(path37.dirname(statePath), { recursive: true });
22650
23089
  state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
22651
- fs33.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
23090
+ fs34.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
22652
23091
  cache3 = state2;
22653
23092
  cachePath2 = statePath;
22654
23093
  }
@@ -22898,18 +23337,18 @@ Usa s\xF3 facts do pedido; campos em falta \u2192 "n\xE3o fornecido" \u2014 nunc
22898
23337
  }
22899
23338
 
22900
23339
  // src/app/agent/core/prompt/auto_memory.ts
22901
- import fs34 from "fs";
22902
- import path36 from "path";
23340
+ import fs35 from "fs";
23341
+ import path38 from "path";
22903
23342
  import os23 from "os";
22904
- var AUTO_MEMORY_FILE = path36.join(os23.homedir(), ".bluma", "auto_memory.md");
23343
+ var AUTO_MEMORY_FILE = path38.join(os23.homedir(), ".bluma", "auto_memory.md");
22905
23344
  var MAX_AUTO_MEMORY_CHARS = 25e3;
22906
23345
  var MAX_AUTO_MEMORY_LINES = 200;
22907
23346
  function getAutoMemoryForPrompt() {
22908
23347
  try {
22909
- if (!fs34.existsSync(AUTO_MEMORY_FILE)) {
23348
+ if (!fs35.existsSync(AUTO_MEMORY_FILE)) {
22910
23349
  return "";
22911
23350
  }
22912
- let content = fs34.readFileSync(AUTO_MEMORY_FILE, "utf-8");
23351
+ let content = fs35.readFileSync(AUTO_MEMORY_FILE, "utf-8");
22913
23352
  if (!content.trim()) {
22914
23353
  return "";
22915
23354
  }
@@ -22935,6 +23374,103 @@ function isAutoMemoryEnabled() {
22935
23374
  return true;
22936
23375
  }
22937
23376
 
23377
+ // src/app/agent/core/prompt/factorai_sh_prompt.ts
23378
+ var FACTORAI_SH_SECTION = `
23379
+ <factorai_sh_workflow>
23380
+ ## FactorAI.sh \u2014 Next.js hosting (mandatory workflow)
23381
+
23382
+ **When to load full checklist:** \`load_skill("factorai-sh")\` before any create/deploy/edit/redeploy task.
23383
+
23384
+ ### Backend URL (already in your environment)
23385
+ | Variable | Used by |
23386
+ |----------|---------|
23387
+ | \`FACTORAI_BASE_URL\` | \`factorai.sh.get_app_status\`, \`factorai.sh.apply_app_changes\`, \`factorai.sh.redeploy_app\` |
23388
+ | \`SEVERINO_URL\` | \`factorai.sh.deploy_app\` (ZIP upload to \`/api/v1/deploy\`) |
23389
+ | \`FACTORAI_API_KEY\` / \`SEVERINO_API_KEY\` | Optional Bearer on write endpoints |
23390
+
23391
+ The orchestrator configures these \u2014 **use the values already set**. Do not invent hosts or assume prod vs dev; tools call whatever base URL the environment provides.
23392
+
23393
+ ### Source of truth: \`factorai.sh.json\`
23394
+ - Written/updated by \`factorai.sh.deploy_app\` and FactorAI tools \u2014 **read with normal file tools**, never a separate manifest tool.
23395
+ - Before any edit/redeploy on an **existing** app: read \`factorai.sh.json\` \u2192 use \`appContext.appId\` (UUID).
23396
+ - Never invent \`appId\` or duplicate state outside this file.
23397
+
23398
+ ### Tools (sandbox-only)
23399
+ | Tool | When |
23400
+ |------|------|
23401
+ | \`factorai.sh.create_next_app\` | New Next.js project in workspace |
23402
+ | \`factorai.sh.deploy_app\` | First deploy (ZIP source only) |
23403
+ | \`factorai.sh.apply_app_changes\` | **App already online** \u2014 patch files + rebuild (Vercel-like) |
23404
+ | \`factorai.sh.get_app_status\` | Poll build status after deploy/apply |
23405
+ | \`factorai.sh.redeploy_app\` | Rebuild current sources without file patches |
23406
+
23407
+ ---
23408
+
23409
+ ## Flow A \u2014 New app (no \`factorai.sh.json\` yet)
23410
+
23411
+ 1. \`factorai.sh.create_next_app({ name, template: "full" })\` \u2014 prefer **full** for deploy (App Router + \`app/\` + shadcn base). Minimal also includes \`app/\` but full is safer for UI tasks.
23412
+ 2. Customize with \`edit_tool\` / \`file_write\` / \`shell_command\` in the project directory (e.g. \`./{name}/app/page.tsx\`).
23413
+ 3. Optional: \`npx tsc --noEmit\` to catch TS errors \u2014 **avoid** \`npm run build\` before deploy (creates \`.next/\` and inflates the ZIP).
23414
+ 4. \`factorai.sh.deploy_app({ projectDir: "./{name}", name })\` \u2014 tool excludes \`node_modules\`, \`.next\`, \`.env*\` automatically. **Never** manual \`zip\` including \`node_modules\` or \`.next\`.
23415
+ 5. Persist returned IDs into \`factorai.sh.json\` (tool does this when possible). Report live URL in \`message(result)\` via \`factor-sh-url-app\` (from manifest \`appUrl\`, absolute URL \u2014 prefix with \`SEVERINO_URL\` only when manifest has a path).
23416
+
23417
+ ---
23418
+
23419
+ ## Session scope (mandatory)
23420
+
23421
+ - App state lives **only** in **this** sandbox workspace (\`factorai.sh.json\` + project dir for this \`session_id\`).
23422
+ - **Never** assume an app from another chat/session \u2014 no cross-session \`appId\` guessing.
23423
+ - For edits: read \`factorai.sh.json\` in the **current** cwd first; if missing, the user must deploy/create the app **in this same session** before Flow B.
23424
+
23425
+ ## Flow B \u2014 App already deployed in this session (Vercel-style edit + rebuild)
23426
+
23427
+ **Do NOT** re-run \`deploy_app\` for small edits. **Do NOT** only \`file_write\` locally and stop \u2014 that does not update the running host.
23428
+
23429
+ 1. Read \`factorai.sh.json\` in this workspace \u2192 \`appId\`.
23430
+ 2. After \`edit_tool\` / \`file_write\`, **read the full file** you changed (\`read_file_lines\` or read entire file).
23431
+ 3. \`factorai.sh.apply_app_changes({ appId, files: [{ path: "app/page.tsx", content: "<ENTIRE file contents>" }, ...], deploy: true })\`
23432
+ - **CRITICAL:** \`content\` must be the **complete** file body. The server **replaces** the whole file \u2014 a footer snippet or JSX fragment **destroys** the page.
23433
+ - One entry per changed path; each \`content\` = full file as on disk after your edit.
23434
+ - \`deploy\` defaults to \`true\`. Paths relative to app root (e.g. \`app/page.tsx\`). Blocked: \`node_modules\`, \`.next\`, \`.git\`.
23435
+ 4. Poll until rebuild completes (see Polling below).
23436
+ 5. Confirm live page, then \`message(result)\` with \`factor-sh-url-app\` from **this** session's manifest.
23437
+
23438
+ Use \`factorai.sh.redeploy_app({ appId })\` only when sources on the server are already correct but build must run again (no file changes).
23439
+
23440
+ ---
23441
+
23442
+ ## Polling after deploy / apply_app_changes
23443
+
23444
+ 1. \`factorai.sh.get_app_status({ appId })\` or GET \`/api/v1/apps/{appId}\`.
23445
+ 2. Expect \`status: "building"\` during \`npm install\` / \`next build\` (~30\u201390s).
23446
+ 3. **Do not** report "live" on the first \`ready\` if you have not seen \`building\` since this operation \u2014 stale \`ready\` can appear briefly.
23447
+ 4. Wait until \`status\` is \`ready\` (or \`running\` / \`live\`) **after** a \`building\` phase, then verify the public route returns HTML (not \`Service Unavailable\`).
23448
+ 5. On \`failed\` / \`error\`: read server/build logs if available; fix TS/scaffold issues; retry \`apply_app_changes\` or \`redeploy_app\`.
23449
+
23450
+ ---
23451
+
23452
+ ## Deploy constraints (first deploy)
23453
+
23454
+ - \`next.config.js\` must keep \`output: 'standalone'\` (included in scaffold).
23455
+ - ZIP max ~50MB \u2014 source only; Severino runs \`npm install\` + \`next build\` on the server.
23456
+ - shadcn components in scaffold must compile (no missing Radix imports).
23457
+
23458
+ ---
23459
+
23460
+ ## Reporting to orchestrator (Severino)
23461
+
23462
+ - Progress: \`message({ message_type: "info", ... })\` every few steps.
23463
+ - Done: \`message({ message_type: "result", content: "...", attachments: [...], "factor-sh-url-app": "<live url>" })\` \u2014 **only \`result\` ends the worker turn**.
23464
+ - **Mandatory for Severino:** on every \`message(result)\` after deploy/redeploy, set \`"factor-sh-url-app"\` to the absolute live app URL from \`factorai.sh.json\` (\`appContext.appUrl\` + \`SEVERINO_URL\` when needed). Auto-filled if omitted; verify after polling \`ready\`.
23465
+ - File deliverables still use \`attachments[]\` under \`.bluma/artifacts/\` \u2014 separate from the app URL field.
23466
+ </factorai_sh_workflow>
23467
+ `;
23468
+ function isFactorAiShPromptEnabled() {
23469
+ const isSandbox = process.env.BLUMA_SANDBOX === "true";
23470
+ const baseUrl = (process.env.FACTORAI_BASE_URL || process.env.FACTORAI_URL || "").trim();
23471
+ return isSandbox && Boolean(baseUrl);
23472
+ }
23473
+
22938
23474
  // src/app/agent/core/prompt/prompt_builder.ts
22939
23475
  function getNodeVersion() {
22940
23476
  return process.version;
@@ -22966,17 +23502,17 @@ function getGitBranch(dir) {
22966
23502
  }
22967
23503
  }
22968
23504
  function getPackageManager(dir) {
22969
- if (fs35.existsSync(path37.join(dir, "pnpm-lock.yaml"))) return "pnpm";
22970
- if (fs35.existsSync(path37.join(dir, "yarn.lock"))) return "yarn";
22971
- if (fs35.existsSync(path37.join(dir, "bun.lockb"))) return "bun";
22972
- if (fs35.existsSync(path37.join(dir, "package-lock.json"))) return "npm";
23505
+ if (fs36.existsSync(path39.join(dir, "pnpm-lock.yaml"))) return "pnpm";
23506
+ if (fs36.existsSync(path39.join(dir, "yarn.lock"))) return "yarn";
23507
+ if (fs36.existsSync(path39.join(dir, "bun.lockb"))) return "bun";
23508
+ if (fs36.existsSync(path39.join(dir, "package-lock.json"))) return "npm";
22973
23509
  return "unknown";
22974
23510
  }
22975
23511
  function getProjectType(dir) {
22976
23512
  try {
22977
- const files = fs35.readdirSync(dir);
23513
+ const files = fs36.readdirSync(dir);
22978
23514
  if (files.includes("package.json")) {
22979
- const pkg = JSON.parse(fs35.readFileSync(path37.join(dir, "package.json"), "utf-8"));
23515
+ const pkg = JSON.parse(fs36.readFileSync(path39.join(dir, "package.json"), "utf-8"));
22980
23516
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
22981
23517
  if (deps.next) return "Next.js";
22982
23518
  if (deps.react) return "React";
@@ -22996,9 +23532,9 @@ function getProjectType(dir) {
22996
23532
  }
22997
23533
  function getTestFramework(dir) {
22998
23534
  try {
22999
- const pkgPath = path37.join(dir, "package.json");
23000
- if (fs35.existsSync(pkgPath)) {
23001
- const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
23535
+ const pkgPath = path39.join(dir, "package.json");
23536
+ if (fs36.existsSync(pkgPath)) {
23537
+ const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
23002
23538
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
23003
23539
  if (deps.jest) return "jest";
23004
23540
  if (deps.vitest) return "vitest";
@@ -23007,7 +23543,7 @@ function getTestFramework(dir) {
23007
23543
  if (deps["@playwright/test"]) return "playwright";
23008
23544
  if (deps.cypress) return "cypress";
23009
23545
  }
23010
- if (fs35.existsSync(path37.join(dir, "pytest.ini")) || fs35.existsSync(path37.join(dir, "conftest.py"))) return "pytest";
23546
+ if (fs36.existsSync(path39.join(dir, "pytest.ini")) || fs36.existsSync(path39.join(dir, "conftest.py"))) return "pytest";
23011
23547
  return "unknown";
23012
23548
  } catch {
23013
23549
  return "unknown";
@@ -23015,9 +23551,9 @@ function getTestFramework(dir) {
23015
23551
  }
23016
23552
  function getTestCommand(dir) {
23017
23553
  try {
23018
- const pkgPath = path37.join(dir, "package.json");
23019
- if (fs35.existsSync(pkgPath)) {
23020
- const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
23554
+ const pkgPath = path39.join(dir, "package.json");
23555
+ if (fs36.existsSync(pkgPath)) {
23556
+ const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
23021
23557
  if (pkg.scripts?.test) return "npm test";
23022
23558
  if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
23023
23559
  }
@@ -23032,8 +23568,8 @@ function getTestCommand(dir) {
23032
23568
  }
23033
23569
  function isGitRepo(dir) {
23034
23570
  try {
23035
- const p = path37.join(dir, ".git");
23036
- return fs35.existsSync(p) && fs35.lstatSync(p).isDirectory();
23571
+ const p = path39.join(dir, ".git");
23572
+ return fs36.existsSync(p) && fs36.lstatSync(p).isDirectory();
23037
23573
  } catch {
23038
23574
  return false;
23039
23575
  }
@@ -23234,7 +23770,8 @@ Timeouts mean the orchestrator should raise the limit \u2014 not that the sandbo
23234
23770
  - **Step 2:** \`message(result)\` with those paths in \`attachments[]\`; use \`content\` for a short summary only.
23235
23771
  - Never create a top-level \`./artifacts/\` folder \u2014 it is auto-remapped to \`.bluma/artifacts/\`.
23236
23772
  - **Never invent URLs** or paths outside \`.bluma/artifacts/\` in \`attachments[]\`.
23237
- - Live deploy URLs (if any) may appear in \`content\` when returned by \`factorai.sh.deploy_app\`; downloadable files still go through \`attachments[]\`.
23773
+ - FactorAI.sh live app URL: on \`message(result)\` after deploy, set \`"factor-sh-url-app"\` (absolute URL). File paths still use \`attachments[]\` only.
23774
+ - Next.js hosting on FactorAI.sh: \`load_skill("factorai-sh")\` and follow \`<factorai_sh_workflow>\` when present.
23238
23775
  - **PDFs for mixed audiences:** load skill \`pdf\`; use \`create_report.py --from-json\` with \`audience: "mixed"\` \u2014 rigor in tables, \`plain_language\` callouts per section for non-specialists.
23239
23776
  - Remove temp files and generator scripts before finishing.
23240
23777
  </sandbox_context>
@@ -23288,12 +23825,18 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
23288
23825
 
23289
23826
  ${SUBJECT_MACHINE_MODEL_XML}`;
23290
23827
  prompt += interpolate(SANDBOX_SECTION);
23828
+ if (isFactorAiShPromptEnabled()) {
23829
+ prompt += `
23830
+
23831
+ ${FACTORAI_SH_SECTION}`;
23832
+ }
23291
23833
  const appContext = loadFactorAiAppContext();
23292
23834
  if (appContext) {
23293
23835
  prompt += `
23294
23836
 
23295
- <factorai_app_context>
23837
+ <factorai_app_context active="true">
23296
23838
  ${formatFactorAiAppContextSummary(appContext)}
23839
+ Read factorai.sh.json before apply_app_changes or redeploy. Prefer apply_app_changes for edits to an already deployed app.
23297
23840
  </factorai_app_context>`;
23298
23841
  }
23299
23842
  }
@@ -23655,13 +24198,17 @@ var HistoryCompressor = class {
23655
24198
  */
23656
24199
  async buildContextWindow(fullHistory, llmService, userContext, options) {
23657
24200
  if (!fullHistory.length) {
23658
- return { messages: [], prunedHistory: null };
24201
+ return { messages: [], prunedHistory: null, archivedMessages: [] };
23659
24202
  }
23660
- const toolDefinitionsTokens = countToolDefinitionsTokens(options?.toolDefinitions ?? []);
23661
- const tokenBudget = Math.max(
23662
- 0,
23663
- (options?.tokenBudget ?? DEFAULT_CONTEXT_INPUT_BUDGET) - toolDefinitionsTokens
24203
+ const budgetBreakdown = computeEffectiveInputBudget(
24204
+ options?.tokenBudget ?? DEFAULT_CONTEXT_INPUT_BUDGET,
24205
+ options?.toolDefinitions ?? [],
24206
+ {
24207
+ outputReserveTokens: options?.outputReserveTokens,
24208
+ protocolOverheadTokens: options?.protocolOverheadTokens
24209
+ }
23664
24210
  );
24211
+ const tokenBudget = budgetBreakdown.effectiveBudget;
23665
24212
  const compressThreshold = options?.compressThreshold ?? COMPRESS_THRESHOLD;
23666
24213
  const keepRecentTurns = options?.keepRecentTurns ?? KEEP_RECENT_TURNS;
23667
24214
  const sanitized = sanitizeConversationForProvider(fullHistory);
@@ -23692,6 +24239,7 @@ var HistoryCompressor = class {
23692
24239
  });
23693
24240
  let compressionHappened = false;
23694
24241
  let compressionFailed = false;
24242
+ const archivedMessages = [];
23695
24243
  while (tokens >= thresholdTokens && pendingSlices.length > 0 && !compressionFailed) {
23696
24244
  try {
23697
24245
  contextEventBus.emit("context:compression_progress", {
@@ -23701,6 +24249,7 @@ var HistoryCompressor = class {
23701
24249
  tokenCount: tokens,
23702
24250
  tokenBudget
23703
24251
  });
24252
+ archivedMessages.push(...pendingFlat);
23704
24253
  this.anchor = await this.compressWithTimeout(
23705
24254
  pendingFlat,
23706
24255
  this.anchor,
@@ -23718,6 +24267,7 @@ var HistoryCompressor = class {
23718
24267
  "[HistoryCompressor] Compression failed/timeout, dropping old turns:",
23719
24268
  compressError?.message
23720
24269
  );
24270
+ archivedMessages.push(...pendingFlat);
23721
24271
  pendingSlices = [];
23722
24272
  pendingFlat = [];
23723
24273
  sliceCount = recentStart;
@@ -23754,7 +24304,11 @@ var HistoryCompressor = class {
23754
24304
  } else {
23755
24305
  this.compressedSliceCount = sliceCount;
23756
24306
  }
23757
- return { messages, prunedHistory };
24307
+ return {
24308
+ messages,
24309
+ prunedHistory,
24310
+ archivedMessages: compressionHappened ? archivedMessages : []
24311
+ };
23758
24312
  }
23759
24313
  // ── Private helpers ──────────────────────────────────────────────────────────
23760
24314
  /** Assembles system + anchor + pending + recent into the final message array. */
@@ -24436,9 +24990,9 @@ var LLMService = class {
24436
24990
  };
24437
24991
 
24438
24992
  // src/app/agent/utils/user_message_images.ts
24439
- import fs36 from "fs";
24993
+ import fs37 from "fs";
24440
24994
  import os26 from "os";
24441
- import path38 from "path";
24995
+ import path40 from "path";
24442
24996
  import { fileURLToPath as fileURLToPath4 } from "url";
24443
24997
  var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp)$/i;
24444
24998
  var MAX_IMAGE_BYTES = 4 * 1024 * 1024;
@@ -24454,22 +25008,22 @@ var MIME = {
24454
25008
  function expandUserPath(p) {
24455
25009
  const t = p.trim();
24456
25010
  if (t.startsWith("~")) {
24457
- return path38.join(os26.homedir(), t.slice(1).replace(/^\//, ""));
25011
+ return path40.join(os26.homedir(), t.slice(1).replace(/^\//, ""));
24458
25012
  }
24459
25013
  return t;
24460
25014
  }
24461
25015
  function isPathAllowed(absResolved, cwd2) {
24462
- const resolved = path38.normalize(path38.resolve(absResolved));
24463
- const cwdR = path38.normalize(path38.resolve(cwd2));
24464
- const homeR = path38.normalize(path38.resolve(os26.homedir()));
24465
- const tmpR = path38.normalize(path38.resolve(os26.tmpdir()));
24466
- const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path38.sep);
24467
- const underHome = resolved === homeR || resolved.startsWith(homeR + path38.sep);
24468
- const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path38.sep);
25016
+ const resolved = path40.normalize(path40.resolve(absResolved));
25017
+ const cwdR = path40.normalize(path40.resolve(cwd2));
25018
+ const homeR = path40.normalize(path40.resolve(os26.homedir()));
25019
+ const tmpR = path40.normalize(path40.resolve(os26.tmpdir()));
25020
+ const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path40.sep);
25021
+ const underHome = resolved === homeR || resolved.startsWith(homeR + path40.sep);
25022
+ const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path40.sep);
24469
25023
  return underCwd || underHome || underTmp;
24470
25024
  }
24471
25025
  function mimeFor(abs) {
24472
- const ext = path38.extname(abs).toLowerCase();
25026
+ const ext = path40.extname(abs).toLowerCase();
24473
25027
  return MIME[ext] || "application/octet-stream";
24474
25028
  }
24475
25029
  var IMAGE_EXT_SRC = String.raw`(?:png|jpe?g|gif|webp|bmp)`;
@@ -24513,10 +25067,10 @@ function collectImagePathStrings(raw) {
24513
25067
  }
24514
25068
  function resolveImagePath(candidate, cwd2) {
24515
25069
  const expanded = expandUserPath(candidate);
24516
- const abs = path38.isAbsolute(expanded) ? path38.normalize(expanded) : path38.normalize(path38.resolve(cwd2, expanded));
25070
+ const abs = path40.isAbsolute(expanded) ? path40.normalize(expanded) : path40.normalize(path40.resolve(cwd2, expanded));
24517
25071
  if (!isPathAllowed(abs, cwd2)) return null;
24518
25072
  try {
24519
- if (!fs36.existsSync(abs) || !fs36.statSync(abs).isFile()) return null;
25073
+ if (!fs37.existsSync(abs) || !fs37.statSync(abs).isFile()) return null;
24520
25074
  } catch {
24521
25075
  return null;
24522
25076
  }
@@ -24536,11 +25090,11 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
24536
25090
  if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
24537
25091
  s = s.slice(1, -1).trim();
24538
25092
  }
24539
- if (!IMAGE_EXT.test(path38.extname(s))) return null;
25093
+ if (!IMAGE_EXT.test(path40.extname(s))) return null;
24540
25094
  const abs = resolveImagePath(s, cwd2);
24541
25095
  if (!abs) return null;
24542
25096
  try {
24543
- const st = fs36.statSync(abs);
25097
+ const st = fs37.statSync(abs);
24544
25098
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
24545
25099
  return null;
24546
25100
  }
@@ -24575,7 +25129,7 @@ function tryPasteChunkAsSingleImagePath(chunk, cwd2) {
24575
25129
  return null;
24576
25130
  }
24577
25131
  try {
24578
- const st = fs36.statSync(abs);
25132
+ const st = fs37.statSync(abs);
24579
25133
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
24580
25134
  return null;
24581
25135
  }
@@ -24604,7 +25158,7 @@ function buildUserMessageContent(raw, cwd2) {
24604
25158
  const abs = resolveImagePath(c, cwd2);
24605
25159
  if (!abs) continue;
24606
25160
  try {
24607
- const st = fs36.statSync(abs);
25161
+ const st = fs37.statSync(abs);
24608
25162
  if (st.size > MAX_IMAGE_BYTES) continue;
24609
25163
  } catch {
24610
25164
  continue;
@@ -24621,7 +25175,7 @@ function buildUserMessageContent(raw, cwd2) {
24621
25175
  }
24622
25176
  const parts = [{ type: "text", text: textPart }];
24623
25177
  for (const abs of resolvedAbs) {
24624
- const buf = fs36.readFileSync(abs);
25178
+ const buf = fs37.readFileSync(abs);
24625
25179
  const b64 = buf.toString("base64");
24626
25180
  const mime = mimeFor(abs);
24627
25181
  parts.push({
@@ -24639,7 +25193,7 @@ function buildUserMessageContent(raw, cwd2) {
24639
25193
  init_sandbox_policy();
24640
25194
  init_runtime_config();
24641
25195
  init_permission_rules();
24642
- import path39 from "path";
25196
+ import path41 from "path";
24643
25197
  var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
24644
25198
  function getToolPermissionLayer(metadata) {
24645
25199
  if (metadata.riskLevel === "safe") return "read";
@@ -24654,11 +25208,11 @@ function checkFilePermissionRules(toolName, filePath, policy) {
24654
25208
  if (!filePath) {
24655
25209
  return { allowed: false, reason: "No file path provided for permission check." };
24656
25210
  }
24657
- const resolvedPath = path39.resolve(filePath);
25211
+ const resolvedPath = path41.resolve(filePath);
24658
25212
  if (!isPathInsideWorkspace(resolvedPath, policy)) {
24659
25213
  return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
24660
25214
  }
24661
- const relativePath = path39.relative(policy.workspaceRoot, resolvedPath);
25215
+ const relativePath = path41.relative(policy.workspaceRoot, resolvedPath);
24662
25216
  const toolPattern = `${toolName}(${relativePath})`;
24663
25217
  const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
24664
25218
  if (ruleDecision === "deny") {
@@ -24667,7 +25221,7 @@ function checkFilePermissionRules(toolName, filePath, policy) {
24667
25221
  if (ruleDecision === "allow") {
24668
25222
  return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
24669
25223
  }
24670
- const dirPath = path39.dirname(relativePath);
25224
+ const dirPath = path41.dirname(relativePath);
24671
25225
  const dirPattern = `${toolName}(${dirPath}/**)`;
24672
25226
  const dirRuleDecision = permissionRulesEngine.checkPermission(dirPattern, { filepath: filePath });
24673
25227
  if (dirRuleDecision === "allow") {
@@ -24884,11 +25438,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
24884
25438
  }
24885
25439
 
24886
25440
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
24887
- import * as fs37 from "fs";
24888
- import * as path40 from "path";
25441
+ import * as fs38 from "fs";
25442
+ import * as path42 from "path";
24889
25443
  import os27 from "os";
24890
25444
  function memoryPath2() {
24891
- return path40.join(process.env.HOME || os27.homedir(), ".bluma", "coding_memory.json");
25445
+ return path42.join(process.env.HOME || os27.homedir(), ".bluma", "coding_memory.json");
24892
25446
  }
24893
25447
  function normalizeNote2(note) {
24894
25448
  return note.trim().toLowerCase().replace(/\s+/g, " ");
@@ -24898,18 +25452,18 @@ function uniqTags(a, b) {
24898
25452
  }
24899
25453
  function consolidateCodingMemoryFile() {
24900
25454
  const p = memoryPath2();
24901
- if (!fs37.existsSync(p)) {
25455
+ if (!fs38.existsSync(p)) {
24902
25456
  return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
24903
25457
  }
24904
25458
  const bak = `${p}.bak`;
24905
25459
  try {
24906
- fs37.copyFileSync(p, bak);
25460
+ fs38.copyFileSync(p, bak);
24907
25461
  } catch (e) {
24908
25462
  return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
24909
25463
  }
24910
25464
  let data;
24911
25465
  try {
24912
- data = JSON.parse(fs37.readFileSync(p, "utf-8"));
25466
+ data = JSON.parse(fs38.readFileSync(p, "utf-8"));
24913
25467
  } catch (e) {
24914
25468
  return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
24915
25469
  }
@@ -24944,7 +25498,7 @@ function consolidateCodingMemoryFile() {
24944
25498
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
24945
25499
  };
24946
25500
  try {
24947
- fs37.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
25501
+ fs38.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
24948
25502
  } catch (e) {
24949
25503
  return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
24950
25504
  }
@@ -25448,6 +26002,22 @@ var BluMaToolRunner = class {
25448
26002
  }
25449
26003
  };
25450
26004
 
26005
+ // src/app/agent/session_manager/session_archive.ts
26006
+ import path43 from "path";
26007
+ import { promises as fs39 } from "fs";
26008
+ async function archivePrunedConversationMessages(sessionId, messages) {
26009
+ if (!sessionId || messages.length === 0) {
26010
+ return null;
26011
+ }
26012
+ const appDir = getPreferredAppDir();
26013
+ const dir = path43.join(appDir, "sessions", "archive", sessionId);
26014
+ await fs39.mkdir(dir, { recursive: true });
26015
+ const archiveFile = path43.join(dir, `${Date.now()}.jsonl`);
26016
+ const lines = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
26017
+ await fs39.appendFile(archiveFile, lines, "utf-8");
26018
+ return archiveFile;
26019
+ }
26020
+
25451
26021
  // src/app/agent/core/llm/llm_errors.ts
25452
26022
  function isContextWindowValidationError(error) {
25453
26023
  const rawMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "";
@@ -25792,7 +26362,11 @@ var BluMaTurnCoordinator = class {
25792
26362
  for (let attempt = 0; attempt < tokenBudgets.length; attempt += 1) {
25793
26363
  const tokenBudget = tokenBudgets[attempt];
25794
26364
  try {
25795
- const { messages: contextWindow, prunedHistory } = await this.deps.compressor.buildContextWindow(
26365
+ const {
26366
+ messages: contextWindow,
26367
+ prunedHistory,
26368
+ archivedMessages
26369
+ } = await this.deps.compressor.buildContextWindow(
25796
26370
  this.deps.history,
25797
26371
  this.deps.llm,
25798
26372
  this.deps.getLlmUserContext(),
@@ -25805,13 +26379,19 @@ var BluMaTurnCoordinator = class {
25805
26379
  history_length: this.deps.history.length,
25806
26380
  context_window_length: contextWindow.length,
25807
26381
  pruned_history_length: prunedHistory?.length ?? null,
26382
+ archived_messages: archivedMessages.length,
25808
26383
  token_budget: tokenBudget,
25809
26384
  context_preview: summarizeHistoryForLog(contextWindow, 8)
25810
26385
  });
25811
26386
  if (prunedHistory) {
26387
+ if (archivedMessages.length > 0) {
26388
+ await archivePrunedConversationMessages(this.deps.sessionId, archivedMessages);
26389
+ }
25812
26390
  this.deps.history.splice(0, this.deps.history.length, ...prunedHistory);
26391
+ await this.deps.persistSessionAfterPrune();
26392
+ } else {
26393
+ this.deps.persistSession();
25813
26394
  }
25814
- this.deps.persistSession();
25815
26395
  if (typeof llmService.chatCompletionStream === "function") {
25816
26396
  await this.handleStreamingResponse(contextWindow);
25817
26397
  } else {
@@ -26229,6 +26809,7 @@ var BluMaAgent = class {
26229
26809
  getLlmUserContext: () => this.getLlmUserContext(),
26230
26810
  persistSession: () => this.persistSession(),
26231
26811
  persistSessionSync: () => this.persistSessionSync(),
26812
+ persistSessionAfterPrune: () => this.persistSessionAfterPrune(),
26232
26813
  notifyFactorTurnEndIfNeeded: (reason) => this.notifyFactorTurnEndIfNeeded(reason),
26233
26814
  emitTurnCompleted: () => this.emitTurnCompleted(),
26234
26815
  generateEditPreview: (toolArgs) => this._generateEditPreview(toolArgs),
@@ -26268,13 +26849,13 @@ var BluMaAgent = class {
26268
26849
  if (!this.sessionFile) return;
26269
26850
  try {
26270
26851
  const sessionData = {
26271
- session_id: path41.basename(this.sessionFile, ".json"),
26852
+ session_id: path44.basename(this.sessionFile, ".json"),
26272
26853
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
26273
26854
  conversation_history: this.history,
26274
26855
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
26275
26856
  ...this.compressor.getSnapshot()
26276
26857
  };
26277
- fs38.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
26858
+ fs40.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
26278
26859
  } catch (error) {
26279
26860
  console.error("[Bluma] Failed to persist session synchronously:", error);
26280
26861
  }
@@ -26284,6 +26865,11 @@ var BluMaAgent = class {
26284
26865
  if (!this.sessionFile) return;
26285
26866
  void saveSessionHistory(this.sessionFile, this.history, this.getMemorySnapshot());
26286
26867
  }
26868
+ /** Gravação imediata após prune (histórico cortado + anchor no JSON de sessão). */
26869
+ async persistSessionAfterPrune() {
26870
+ if (!this.sessionFile) return;
26871
+ await saveSessionHistoryNow(this.sessionFile, this.history, this.getMemorySnapshot());
26872
+ }
26287
26873
  recordUiSlashCommand(command, mode = "visible_only") {
26288
26874
  const text = String(command ?? "").trim();
26289
26875
  if (!text) return;
@@ -26475,7 +27061,7 @@ var BluMaAgent = class {
26475
27061
 
26476
27062
  ${editData.error.display}`;
26477
27063
  }
26478
- const filename = path41.basename(toolArgs.file_path);
27064
+ const filename = path44.basename(toolArgs.file_path);
26479
27065
  return createDiff(filename, editData.currentContent || "", editData.newContent);
26480
27066
  } catch (e) {
26481
27067
  return `An unexpected error occurred while generating the edit preview: ${e.message}`;
@@ -27201,16 +27787,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
27201
27787
  }
27202
27788
 
27203
27789
  // src/app/agent/core/memory/session_memory.ts
27204
- import fs39 from "fs";
27790
+ import fs41 from "fs";
27205
27791
  import os30 from "os";
27206
- import path42 from "path";
27792
+ import path45 from "path";
27207
27793
  import { v4 as uuidv49 } from "uuid";
27208
27794
  var SessionMemoryExtractor = class {
27209
27795
  llmClient;
27210
27796
  memoryFile;
27211
27797
  constructor(options = {}) {
27212
27798
  this.llmClient = options.llmClient;
27213
- this.memoryFile = options.memoryFile || path42.join(os30.homedir(), ".bluma", "session_memory.json");
27799
+ this.memoryFile = options.memoryFile || path45.join(os30.homedir(), ".bluma", "session_memory.json");
27214
27800
  }
27215
27801
  /**
27216
27802
  * Extract memories from conversation using LLM
@@ -27267,15 +27853,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
27267
27853
  );
27268
27854
  unique.sort((a, b) => b.accessCount - a.accessCount);
27269
27855
  const trimmed = unique.slice(0, 200);
27270
- fs39.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
27856
+ fs41.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
27271
27857
  }
27272
27858
  /**
27273
27859
  * Load memories from disk
27274
27860
  */
27275
27861
  async loadMemories() {
27276
27862
  try {
27277
- if (!fs39.existsSync(this.memoryFile)) return [];
27278
- const data = fs39.readFileSync(this.memoryFile, "utf-8");
27863
+ if (!fs41.existsSync(this.memoryFile)) return [];
27864
+ const data = fs41.readFileSync(this.memoryFile, "utf-8");
27279
27865
  return JSON.parse(data);
27280
27866
  } catch {
27281
27867
  return [];
@@ -27819,14 +28405,14 @@ var RouteManager = class {
27819
28405
  this.subAgents = subAgents;
27820
28406
  this.core = core;
27821
28407
  }
27822
- registerRoute(path54, handler) {
27823
- this.routeHandlers.set(path54, handler);
28408
+ registerRoute(path57, handler) {
28409
+ this.routeHandlers.set(path57, handler);
27824
28410
  }
27825
28411
  async handleRoute(payload) {
27826
28412
  const inputText = String(payload.content || "").trim();
27827
28413
  const { userContext, options } = payload;
27828
- for (const [path54, handler] of this.routeHandlers) {
27829
- if (inputText === path54 || inputText.startsWith(`${path54} `)) {
28414
+ for (const [path57, handler] of this.routeHandlers) {
28415
+ if (inputText === path57 || inputText.startsWith(`${path57} `)) {
27830
28416
  return handler({ content: inputText, userContext });
27831
28417
  }
27832
28418
  }
@@ -27835,13 +28421,13 @@ var RouteManager = class {
27835
28421
  };
27836
28422
 
27837
28423
  // src/app/agent/runtime/plugin_runtime.ts
27838
- import path43 from "path";
28424
+ import path46 from "path";
27839
28425
  import { pathToFileURL as pathToFileURL2 } from "url";
27840
28426
  async function loadPluginsAtStartup() {
27841
28427
  for (const p of listPlugins()) {
27842
28428
  const entry = p.manifest.entry?.trim();
27843
28429
  if (!entry) continue;
27844
- const abs = path43.resolve(p.root, entry);
28430
+ const abs = path46.resolve(p.root, entry);
27845
28431
  try {
27846
28432
  const href = pathToFileURL2(abs).href;
27847
28433
  const mod = await import(href);
@@ -27862,7 +28448,7 @@ async function loadPluginsAtStartup() {
27862
28448
  }
27863
28449
 
27864
28450
  // src/app/agent/agent.ts
27865
- var globalEnvPath = path44.join(os31.homedir(), ".bluma", ".env");
28451
+ var globalEnvPath = path47.join(os31.homedir(), ".bluma", ".env");
27866
28452
  dotenv.config({ path: globalEnvPath });
27867
28453
  var Agent = class {
27868
28454
  sessionId;
@@ -29718,10 +30304,10 @@ function resolveToolPayload(result) {
29718
30304
 
29719
30305
  // src/app/ui/components/FilePathLink.tsx
29720
30306
  import { pathToFileURL as pathToFileURL3 } from "node:url";
29721
- import path46 from "node:path";
30307
+ import path49 from "node:path";
29722
30308
 
29723
30309
  // src/app/ui/utils/pathDisplay.ts
29724
- import path45 from "node:path";
30310
+ import path48 from "node:path";
29725
30311
  import os32 from "node:os";
29726
30312
  function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
29727
30313
  let s = String(pathInput ?? "").trim();
@@ -29729,17 +30315,17 @@ function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
29729
30315
  s = s.replace(/[/\\]+$/, "");
29730
30316
  }
29731
30317
  if (!s) return "";
29732
- const abs = path45.isAbsolute(s) ? path45.normalize(s) : path45.resolve(cwd2, s);
29733
- const resolvedCwd = path45.resolve(cwd2);
29734
- const rel = path45.relative(resolvedCwd, abs);
29735
- if (rel === "" || !rel.startsWith("..") && !path45.isAbsolute(rel)) {
30318
+ const abs = path48.isAbsolute(s) ? path48.normalize(s) : path48.resolve(cwd2, s);
30319
+ const resolvedCwd = path48.resolve(cwd2);
30320
+ const rel = path48.relative(resolvedCwd, abs);
30321
+ if (rel === "" || !rel.startsWith("..") && !path48.isAbsolute(rel)) {
29736
30322
  return rel === "" ? "." : rel;
29737
30323
  }
29738
- const home = path45.normalize(os32.homedir());
29739
- if (abs === home || abs.startsWith(home + path45.sep)) {
30324
+ const home = path48.normalize(os32.homedir());
30325
+ if (abs === home || abs.startsWith(home + path48.sep)) {
29740
30326
  return "~" + abs.slice(home.length);
29741
30327
  }
29742
- return path45.basename(abs);
30328
+ return path48.basename(abs);
29743
30329
  }
29744
30330
 
29745
30331
  // src/app/ui/components/FilePathLink.tsx
@@ -29749,7 +30335,7 @@ function FilePathLink({ filePath, children, cwd: cwd2 = process.cwd(), color })
29749
30335
  if (!raw) {
29750
30336
  return null;
29751
30337
  }
29752
- const abs = path46.isAbsolute(raw) ? path46.normalize(raw) : path46.resolve(cwd2, raw);
30338
+ const abs = path49.isAbsolute(raw) ? path49.normalize(raw) : path49.resolve(cwd2, raw);
29753
30339
  const href = pathToFileURL3(abs).href;
29754
30340
  const label = formatPathForDisplay(abs, cwd2);
29755
30341
  const text = typeof children === "string" ? children : label;
@@ -31701,12 +32287,12 @@ function patchToUnifiedDiffText(filePath, patch) {
31701
32287
  }
31702
32288
 
31703
32289
  // src/app/ui/utils/readEditContext.ts
31704
- import { promises as fs40 } from "fs";
32290
+ import { promises as fs42 } from "fs";
31705
32291
  var CHUNK_SIZE = 64 * 1024;
31706
32292
  var CONTEXT_LINES2 = 3;
31707
32293
  async function openForScan(filePath) {
31708
32294
  try {
31709
- return await fs40.open(filePath, "r");
32295
+ return await fs42.open(filePath, "r");
31710
32296
  } catch (e) {
31711
32297
  if (e.code === "ENOENT") return null;
31712
32298
  throw e;
@@ -32384,13 +32970,13 @@ function EditToolDiffPanel({
32384
32970
  newString,
32385
32971
  replaceAll = false
32386
32972
  }) {
32387
- const path54 = filePath.trim() || "unknown file";
32973
+ const path57 = filePath.trim() || "unknown file";
32388
32974
  const hasPreviewArgs = oldString !== void 0 && newString !== void 0;
32389
32975
  const hasDiffText = diffText && diffText.trim().length > 0;
32390
32976
  return /* @__PURE__ */ jsx43(Box_default, { flexDirection: "column", children: hasPreviewArgs ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(
32391
32977
  FileEditToolDiff,
32392
32978
  {
32393
- filePath: path54,
32979
+ filePath: path57,
32394
32980
  oldString,
32395
32981
  newString,
32396
32982
  replaceAll,
@@ -32428,7 +33014,7 @@ function renderToolUseMessage12({ args }) {
32428
33014
  return /* @__PURE__ */ jsx44(Text, { color: BLUMA_TERMINAL.blue, children: p });
32429
33015
  }
32430
33016
  function renderToolHeader12({ args }) {
32431
- const path54 = args?.file_path ?? ".";
33017
+ const path57 = args?.file_path ?? ".";
32432
33018
  const oldText = typeof args?.old_string === "string" ? args.old_string : "";
32433
33019
  const newText = typeof args?.new_string === "string" ? args.new_string : "";
32434
33020
  const counts = countLineDiff(oldText, newText);
@@ -32438,7 +33024,7 @@ function renderToolHeader12({ args }) {
32438
33024
  action,
32439
33025
  /* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
32440
33026
  " ",
32441
- /* @__PURE__ */ jsx44(FilePathLink, { filePath: path54, color: BLUMA_TERMINAL.dim })
33027
+ /* @__PURE__ */ jsx44(FilePathLink, { filePath: path57, color: BLUMA_TERMINAL.dim })
32442
33028
  ] })
32443
33029
  ] }),
32444
33030
  /* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
@@ -32830,11 +33416,11 @@ function userFacingName13() {
32830
33416
  }
32831
33417
  function renderToolUseMessage14({ args }) {
32832
33418
  const q = args?.query ? `"${args.query}"` : "...";
32833
- const path54 = args?.path || ".";
33419
+ const path57 = args?.path || ".";
32834
33420
  return /* @__PURE__ */ jsxs30(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
32835
33421
  /* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.blue, children: q }),
32836
33422
  /* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.dim, children: " " }),
32837
- /* @__PURE__ */ jsx47(FilePathLink, { filePath: path54, color: BLUMA_TERMINAL.dim })
33423
+ /* @__PURE__ */ jsx47(FilePathLink, { filePath: path57, color: BLUMA_TERMINAL.dim })
32838
33424
  ] });
32839
33425
  }
32840
33426
  function renderToolHeader14({ args }) {
@@ -33356,7 +33942,7 @@ var loadSkillTool = createTool({
33356
33942
  });
33357
33943
 
33358
33944
  // src/app/agent/tools/FileWriteTool/UI.tsx
33359
- import fs41 from "fs";
33945
+ import fs43 from "fs";
33360
33946
  import { diffLines as diffLines3 } from "diff";
33361
33947
  import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
33362
33948
  function getFilePath(args) {
@@ -33371,7 +33957,7 @@ function getFilePath(args) {
33371
33957
  function readExistingFileText(filePath) {
33372
33958
  if (!filePath) return "";
33373
33959
  try {
33374
- return fs41.readFileSync(filePath, "utf-8");
33960
+ return fs43.readFileSync(filePath, "utf-8");
33375
33961
  } catch {
33376
33962
  return "";
33377
33963
  }
@@ -36301,8 +36887,8 @@ import {
36301
36887
 
36302
36888
  // src/app/ui/hooks/useAtCompletion.ts
36303
36889
  import { useEffect as useEffect11, useRef as useRef4, useState as useState13 } from "react";
36304
- import fs42 from "fs";
36305
- import path47 from "path";
36890
+ import fs44 from "fs";
36891
+ import path50 from "path";
36306
36892
  var MAX_RESULTS3 = 50;
36307
36893
  var DEFAULT_RECURSIVE_DEPTH = 2;
36308
36894
  function listPathSuggestions(baseDir, pattern) {
@@ -36310,7 +36896,7 @@ function listPathSuggestions(baseDir, pattern) {
36310
36896
  const patternEndsWithSlash = raw.endsWith("/");
36311
36897
  const relDir = raw.replace(/^\/+|\/+$/g, "");
36312
36898
  const filterPrefix = patternEndsWithSlash ? "" : relDir.split("/").slice(-1)[0] || "";
36313
- const listDir = path47.resolve(baseDir, relDir || ".");
36899
+ const listDir = path50.resolve(baseDir, relDir || ".");
36314
36900
  const results = [];
36315
36901
  const IGNORED_DIRS = ["node_modules", ".git", ".venv", "dist", "build"];
36316
36902
  const IGNORED_EXTS = [".pyc", ".class", ".o", ".map", ".log", ".tmp"];
@@ -36327,7 +36913,7 @@ function listPathSuggestions(baseDir, pattern) {
36327
36913
  }
36328
36914
  function pushEntry(entryPath, label, isDir) {
36329
36915
  if (results.length >= MAX_RESULTS3) return;
36330
- const clean = label.split(path47.sep).join("/").replace(/[]+/g, "");
36916
+ const clean = label.split(path50.sep).join("/").replace(/[]+/g, "");
36331
36917
  results.push({ label: clean + (isDir ? "/" : ""), fullPath: entryPath, isDir });
36332
36918
  }
36333
36919
  try {
@@ -36336,11 +36922,11 @@ function listPathSuggestions(baseDir, pattern) {
36336
36922
  while (queue.length && results.length < MAX_RESULTS3) {
36337
36923
  const node = queue.shift();
36338
36924
  try {
36339
- const entries = fs42.readdirSync(node.dir, { withFileTypes: true });
36925
+ const entries = fs44.readdirSync(node.dir, { withFileTypes: true });
36340
36926
  for (const entry of entries) {
36341
36927
  if (isIgnoredName(entry.name)) continue;
36342
- const entryAbs = path47.join(node.dir, entry.name);
36343
- const entryRel = node.rel ? path47.posix.join(node.rel, entry.name) : entry.name;
36928
+ const entryAbs = path50.join(node.dir, entry.name);
36929
+ const entryRel = node.rel ? path50.posix.join(node.rel, entry.name) : entry.name;
36344
36930
  if (entryRel.split("/").includes("node_modules")) continue;
36345
36931
  if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
36346
36932
  pushEntry(entryAbs, entryRel, entry.isDirectory());
@@ -36353,13 +36939,13 @@ function listPathSuggestions(baseDir, pattern) {
36353
36939
  }
36354
36940
  }
36355
36941
  } else {
36356
- const entries = fs42.readdirSync(listDir, { withFileTypes: true });
36942
+ const entries = fs44.readdirSync(listDir, { withFileTypes: true });
36357
36943
  for (const entry of entries) {
36358
36944
  if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
36359
36945
  if (isIgnoredName(entry.name)) continue;
36360
36946
  if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
36361
- const entryAbs = path47.join(listDir, entry.name);
36362
- const label = relDir ? path47.posix.join(relDir, entry.name) : entry.name;
36947
+ const entryAbs = path50.join(listDir, entry.name);
36948
+ const label = relDir ? path50.posix.join(relDir, entry.name) : entry.name;
36363
36949
  pushEntry(entryAbs, label, entry.isDirectory());
36364
36950
  if (results.length >= MAX_RESULTS3) break;
36365
36951
  }
@@ -36560,15 +37146,15 @@ var SlashSubmenuInlineComponent = ({ menu }) => {
36560
37146
  var SlashSubmenuInline = memo15(SlashSubmenuInlineComponent);
36561
37147
 
36562
37148
  // src/app/ui/utils/clipboardImage.ts
36563
- import fs43 from "fs";
37149
+ import fs45 from "fs";
36564
37150
  import os33 from "os";
36565
- import path48 from "path";
37151
+ import path51 from "path";
36566
37152
  import { spawn as spawn5, execFile as execFileCb, execSync as execSync4 } from "child_process";
36567
37153
  import { promisify as promisify2 } from "util";
36568
37154
 
36569
37155
  // src/app/utils/clipboardNative.ts
36570
37156
  import { existsSync as existsSync7 } from "fs";
36571
- import { createRequire } from "module";
37157
+ import { createRequire as createRequire2 } from "module";
36572
37158
  import { dirname as dirname4, join as join8 } from "path";
36573
37159
  import { fileURLToPath as fileURLToPath5 } from "url";
36574
37160
  var __dirname;
@@ -36616,7 +37202,7 @@ function getNativeModule() {
36616
37202
  throw loadError;
36617
37203
  }
36618
37204
  try {
36619
- const require2 = createRequire(import.meta.url);
37205
+ const require2 = createRequire2(import.meta.url);
36620
37206
  const mod = require2(nativePath);
36621
37207
  nativeModule = mod;
36622
37208
  return nativeModule;
@@ -36691,8 +37277,8 @@ function commandOnPath(cmd) {
36691
37277
  function unixClipboardHelperDirs() {
36692
37278
  const h = os33.homedir();
36693
37279
  return [
36694
- path48.join(h, ".local", "bin"),
36695
- path48.join(h, "bin"),
37280
+ path51.join(h, ".local", "bin"),
37281
+ path51.join(h, "bin"),
36696
37282
  "/usr/bin",
36697
37283
  "/usr/local/bin",
36698
37284
  "/bin",
@@ -36710,16 +37296,16 @@ function resolveHelperBinary(cmd) {
36710
37296
  return cmd;
36711
37297
  }
36712
37298
  for (const dir of unixClipboardHelperDirs()) {
36713
- const full = path48.join(dir, cmd);
37299
+ const full = path51.join(dir, cmd);
36714
37300
  try {
36715
- fs43.accessSync(full, fs43.constants.X_OK);
37301
+ fs45.accessSync(full, fs45.constants.X_OK);
36716
37302
  return full;
36717
37303
  } catch {
36718
37304
  }
36719
37305
  }
36720
37306
  for (const dir of unixClipboardHelperDirs()) {
36721
- const full = path48.join(dir, cmd);
36722
- if (fs43.existsSync(full)) {
37307
+ const full = path51.join(dir, cmd);
37308
+ if (fs45.existsSync(full)) {
36723
37309
  return full;
36724
37310
  }
36725
37311
  }
@@ -36760,17 +37346,17 @@ function writeBufferIfImage(baseDir, buf) {
36760
37346
  if (!ext) {
36761
37347
  return null;
36762
37348
  }
36763
- const out = path48.join(
37349
+ const out = path51.join(
36764
37350
  baseDir,
36765
37351
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
36766
37352
  );
36767
- fs43.writeFileSync(out, buf);
37353
+ fs45.writeFileSync(out, buf);
36768
37354
  return out;
36769
37355
  }
36770
37356
  function unlinkQuiet(p) {
36771
37357
  try {
36772
- if (fs43.existsSync(p)) {
36773
- fs43.unlinkSync(p);
37358
+ if (fs45.existsSync(p)) {
37359
+ fs45.unlinkSync(p);
36774
37360
  }
36775
37361
  } catch {
36776
37362
  }
@@ -36787,25 +37373,25 @@ async function tryDarwinClipboardy(baseDir) {
36787
37373
  return null;
36788
37374
  }
36789
37375
  for (const src of tmpPaths) {
36790
- if (!src || !fs43.existsSync(src)) {
37376
+ if (!src || !fs45.existsSync(src)) {
36791
37377
  continue;
36792
37378
  }
36793
37379
  let st;
36794
37380
  try {
36795
- st = fs43.statSync(src);
37381
+ st = fs45.statSync(src);
36796
37382
  } catch {
36797
37383
  continue;
36798
37384
  }
36799
37385
  if (st.size < 80 || st.size > CLIPBOARD_MAX_BYTES) {
36800
37386
  continue;
36801
37387
  }
36802
- const ext = path48.extname(src).toLowerCase();
37388
+ const ext = path51.extname(src).toLowerCase();
36803
37389
  const safeExt = ext && /^\.(png|jpe?g|gif|webp)$/i.test(ext) ? ext : ".png";
36804
- const out = path48.join(
37390
+ const out = path51.join(
36805
37391
  baseDir,
36806
37392
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
36807
37393
  );
36808
- fs43.copyFileSync(src, out);
37394
+ fs45.copyFileSync(src, out);
36809
37395
  for (const p of tmpPaths) {
36810
37396
  unlinkQuiet(p);
36811
37397
  }
@@ -36819,14 +37405,14 @@ async function tryDarwinClipboardy(baseDir) {
36819
37405
  return null;
36820
37406
  }
36821
37407
  async function tryWindowsPowerShell(outFile) {
36822
- const ps = process.env.SystemRoot != null ? path48.join(
37408
+ const ps = process.env.SystemRoot != null ? path51.join(
36823
37409
  process.env.SystemRoot,
36824
37410
  "System32",
36825
37411
  "WindowsPowerShell",
36826
37412
  "v1.0",
36827
37413
  "powershell.exe"
36828
37414
  ) : "powershell.exe";
36829
- if (!fs43.existsSync(ps)) {
37415
+ if (!fs45.existsSync(ps)) {
36830
37416
  return false;
36831
37417
  }
36832
37418
  const script = "$ErrorActionPreference='Stop';Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$img=[System.Windows.Forms.Clipboard]::GetImage();if($null -eq $img){exit 2};$img.Save($env:BLUMA_CLIP_OUT,[System.Drawing.Imaging.ImageFormat]::Png);exit 0";
@@ -36848,7 +37434,7 @@ async function tryWindowsPowerShell(outFile) {
36848
37434
  return false;
36849
37435
  }
36850
37436
  try {
36851
- const st = fs43.statSync(outFile);
37437
+ const st = fs45.statSync(outFile);
36852
37438
  return st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES;
36853
37439
  } catch {
36854
37440
  return false;
@@ -36920,8 +37506,8 @@ function parseClipboardTextAsImagePath(raw) {
36920
37506
  s = s.slice(1, -1);
36921
37507
  }
36922
37508
  s = s.trim();
36923
- if (!CLIPBOARD_PATH_IMAGE_EXT.test(path48.extname(s))) return null;
36924
- const abs = path48.isAbsolute(s) ? path48.normalize(s) : path48.resolve(process.cwd(), s);
37509
+ if (!CLIPBOARD_PATH_IMAGE_EXT.test(path51.extname(s))) return null;
37510
+ const abs = path51.isAbsolute(s) ? path51.normalize(s) : path51.resolve(process.cwd(), s);
36925
37511
  return abs;
36926
37512
  }
36927
37513
  async function tryClipboardTextAsImageFile(baseDir) {
@@ -36935,19 +37521,19 @@ async function tryClipboardTextAsImageFile(baseDir) {
36935
37521
  const abs = parseClipboardTextAsImagePath(t);
36936
37522
  if (!abs) return null;
36937
37523
  try {
36938
- if (!fs43.existsSync(abs)) return null;
36939
- const st = fs43.statSync(abs);
37524
+ if (!fs45.existsSync(abs)) return null;
37525
+ const st = fs45.statSync(abs);
36940
37526
  if (!st.isFile() || st.size > CLIPBOARD_MAX_BYTES || st.size < 20) return null;
36941
37527
  } catch {
36942
37528
  return null;
36943
37529
  }
36944
- const ext = path48.extname(abs).toLowerCase();
36945
- const out = path48.join(
37530
+ const ext = path51.extname(abs).toLowerCase();
37531
+ const out = path51.join(
36946
37532
  baseDir,
36947
37533
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
36948
37534
  );
36949
37535
  try {
36950
- fs43.copyFileSync(abs, out);
37536
+ fs45.copyFileSync(abs, out);
36951
37537
  return out;
36952
37538
  } catch {
36953
37539
  return null;
@@ -36957,7 +37543,7 @@ async function tryLinuxShellPipelineSave(baseDir) {
36957
37543
  if (process.platform !== "linux" && process.platform !== "freebsd") {
36958
37544
  return null;
36959
37545
  }
36960
- const outPath = path48.join(
37546
+ const outPath = path51.join(
36961
37547
  baseDir,
36962
37548
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.tmp`
36963
37549
  );
@@ -36979,11 +37565,11 @@ printf '%s' "$OUT"
36979
37565
  maxBuffer: 4096
36980
37566
  });
36981
37567
  const written = String(stdout ?? "").trim();
36982
- if (written !== outPath || !fs43.existsSync(outPath)) {
37568
+ if (written !== outPath || !fs45.existsSync(outPath)) {
36983
37569
  unlinkQuiet(outPath);
36984
37570
  return null;
36985
37571
  }
36986
- const buf = fs43.readFileSync(outPath);
37572
+ const buf = fs45.readFileSync(outPath);
36987
37573
  unlinkQuiet(outPath);
36988
37574
  return writeBufferIfImage(baseDir, buf);
36989
37575
  } catch {
@@ -37004,8 +37590,8 @@ async function tryNativeClipboardImage() {
37004
37590
  }
37005
37591
  try {
37006
37592
  const result = readClipboardImageNative();
37007
- if (fs43.existsSync(result.path)) {
37008
- const st = fs43.statSync(result.path);
37593
+ if (fs45.existsSync(result.path)) {
37594
+ const st = fs45.statSync(result.path);
37009
37595
  if (st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES) {
37010
37596
  return result.path;
37011
37597
  }
@@ -37015,8 +37601,8 @@ async function tryNativeClipboardImage() {
37015
37601
  return null;
37016
37602
  }
37017
37603
  async function readClipboardImageToTempFile() {
37018
- const baseDir = path48.join(os33.homedir(), ".cache", "bluma", "clipboard");
37019
- fs43.mkdirSync(baseDir, { recursive: true });
37604
+ const baseDir = path51.join(os33.homedir(), ".cache", "bluma", "clipboard");
37605
+ fs45.mkdirSync(baseDir, { recursive: true });
37020
37606
  const nativeResult = await tryNativeClipboardImage();
37021
37607
  if (nativeResult) {
37022
37608
  return nativeResult;
@@ -37034,7 +37620,7 @@ async function readClipboardImageToTempFile() {
37034
37620
  }
37035
37621
  }
37036
37622
  if (process.platform === "win32") {
37037
- const outFile = path48.join(
37623
+ const outFile = path51.join(
37038
37624
  baseDir,
37039
37625
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.png`
37040
37626
  );
@@ -37075,7 +37661,7 @@ function expandLargePastePlaceholder(value, pending) {
37075
37661
  }
37076
37662
 
37077
37663
  // src/app/ui/components/InputPrompt.tsx
37078
- import fs44 from "fs";
37664
+ import fs46 from "fs";
37079
37665
  import { Fragment as Fragment7, jsx as jsx79, jsxs as jsxs62 } from "react/jsx-runtime";
37080
37666
  var persistedInputState = { text: "", cursorPosition: 0 };
37081
37667
  var StaticCursor = () => /* @__PURE__ */ jsx79(Box_default, { flexDirection: "row", flexWrap: "nowrap", children: /* @__PURE__ */ jsx79(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "\u2588 " }) });
@@ -37242,7 +37828,7 @@ var InputPrompt = memo16(({
37242
37828
  return;
37243
37829
  }
37244
37830
  if (routeText.startsWith("/")) {
37245
- const isFilePath = map.size > 0 && fs44.existsSync(routeText.split(/\s+/)[0]);
37831
+ const isFilePath = map.size > 0 && fs46.existsSync(routeText.split(/\s+/)[0]);
37246
37832
  if (isFilePath) {
37247
37833
  uiEventBus.emit("user_overlay", {
37248
37834
  kind: "message",
@@ -39522,8 +40108,8 @@ var renderCode = () => {
39522
40108
  };
39523
40109
 
39524
40110
  // src/app/agent/core/thread/thread_store.ts
39525
- import path49 from "path";
39526
- import { promises as fs45 } from "fs";
40111
+ import path52 from "path";
40112
+ import { promises as fs47 } from "fs";
39527
40113
  import { randomUUID as randomUUID2 } from "crypto";
39528
40114
  var INDEX_VERSION = 1;
39529
40115
  var fileLocks2 = /* @__PURE__ */ new Map();
@@ -39548,9 +40134,9 @@ var ThreadStore = class {
39548
40134
  packageVersion;
39549
40135
  constructor() {
39550
40136
  const appDir = getPreferredAppDir();
39551
- this.threadsDir = path49.join(appDir, "threads");
39552
- this.archiveDir = path49.join(this.threadsDir, "archive");
39553
- this.indexPath = path49.join(this.threadsDir, "index.json");
40137
+ this.threadsDir = path52.join(appDir, "threads");
40138
+ this.archiveDir = path52.join(this.threadsDir, "archive");
40139
+ this.indexPath = path52.join(this.threadsDir, "index.json");
39554
40140
  this.packageVersion = process.env.npm_package_version || "0.0.0";
39555
40141
  }
39556
40142
  // ==================== Inicialização ====================
@@ -39558,10 +40144,10 @@ var ThreadStore = class {
39558
40144
  * Inicializa o diretório de threads
39559
40145
  */
39560
40146
  async initialize() {
39561
- await fs45.mkdir(this.threadsDir, { recursive: true });
39562
- await fs45.mkdir(this.archiveDir, { recursive: true });
40147
+ await fs47.mkdir(this.threadsDir, { recursive: true });
40148
+ await fs47.mkdir(this.archiveDir, { recursive: true });
39563
40149
  try {
39564
- await fs45.access(this.indexPath);
40150
+ await fs47.access(this.indexPath);
39565
40151
  } catch {
39566
40152
  await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
39567
40153
  }
@@ -39590,7 +40176,7 @@ var ThreadStore = class {
39590
40176
  async loadIndex() {
39591
40177
  return withFileLock2(this.indexPath, async () => {
39592
40178
  try {
39593
- const content = await fs45.readFile(this.indexPath, "utf-8");
40179
+ const content = await fs47.readFile(this.indexPath, "utf-8");
39594
40180
  return JSON.parse(content);
39595
40181
  } catch {
39596
40182
  return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
@@ -39601,8 +40187,8 @@ var ThreadStore = class {
39601
40187
  return withFileLock2(this.indexPath, async () => {
39602
40188
  index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
39603
40189
  const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
39604
- await fs45.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
39605
- await fs45.rename(tempPath, this.indexPath);
40190
+ await fs47.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
40191
+ await fs47.rename(tempPath, this.indexPath);
39606
40192
  });
39607
40193
  }
39608
40194
  // ==================== Git Info ====================
@@ -39672,7 +40258,7 @@ var ThreadStore = class {
39672
40258
  messages: params.initialMessages || []
39673
40259
  };
39674
40260
  const historyPath = this.buildDatedThreadHistoryPath(threadId);
39675
- await fs45.mkdir(path49.dirname(historyPath), { recursive: true });
40261
+ await fs47.mkdir(path52.dirname(historyPath), { recursive: true });
39676
40262
  await this.saveHistoryAtPath(historyPath, history);
39677
40263
  const index = await this.loadIndex();
39678
40264
  index.threads.unshift({
@@ -39727,7 +40313,7 @@ var ThreadStore = class {
39727
40313
  compressedSliceCount: source.history.compressedSliceCount
39728
40314
  };
39729
40315
  const historyPath = this.buildDatedThreadHistoryPath(newThreadId);
39730
- await fs45.mkdir(path49.dirname(historyPath), { recursive: true });
40316
+ await fs47.mkdir(path52.dirname(historyPath), { recursive: true });
39731
40317
  await this.saveHistoryAtPath(historyPath, history);
39732
40318
  const index = await this.loadIndex();
39733
40319
  index.threads.unshift({
@@ -39800,9 +40386,9 @@ var ThreadStore = class {
39800
40386
  const entry = index.threads[entryIndex];
39801
40387
  if (entry.status === "archived") return true;
39802
40388
  const oldPath = entry.historyPath || this.getLegacyHistoryPath(threadId);
39803
- const newPath = path49.join(this.archiveDir, `${threadId}.jsonl`);
40389
+ const newPath = path52.join(this.archiveDir, `${threadId}.jsonl`);
39804
40390
  try {
39805
- await fs45.rename(oldPath, newPath);
40391
+ await fs47.rename(oldPath, newPath);
39806
40392
  } catch (e) {
39807
40393
  if (e.code !== "ENOENT") throw e;
39808
40394
  }
@@ -39823,11 +40409,11 @@ var ThreadStore = class {
39823
40409
  if (entryIndex === -1) return false;
39824
40410
  const entry = index.threads[entryIndex];
39825
40411
  if (entry.status === "active") return true;
39826
- const oldPath = path49.join(this.archiveDir, `${threadId}.jsonl`);
40412
+ const oldPath = path52.join(this.archiveDir, `${threadId}.jsonl`);
39827
40413
  const newPath = this.buildDatedThreadHistoryPath(threadId);
39828
- await fs45.mkdir(path49.dirname(newPath), { recursive: true });
40414
+ await fs47.mkdir(path52.dirname(newPath), { recursive: true });
39829
40415
  try {
39830
- await fs45.rename(oldPath, newPath);
40416
+ await fs47.rename(oldPath, newPath);
39831
40417
  } catch (e) {
39832
40418
  if (e.code !== "ENOENT") throw e;
39833
40419
  }
@@ -39848,7 +40434,7 @@ var ThreadStore = class {
39848
40434
  if (entryIndex === -1) return false;
39849
40435
  const entry = index.threads[entryIndex];
39850
40436
  try {
39851
- await fs45.unlink(entry.historyPath);
40437
+ await fs47.unlink(entry.historyPath);
39852
40438
  } catch {
39853
40439
  }
39854
40440
  index.threads.splice(entryIndex, 1);
@@ -39858,28 +40444,28 @@ var ThreadStore = class {
39858
40444
  }
39859
40445
  // ==================== Histórico ====================
39860
40446
  getLegacyHistoryPath(threadId) {
39861
- return path49.join(this.threadsDir, `${threadId}.jsonl`);
40447
+ return path52.join(this.threadsDir, `${threadId}.jsonl`);
39862
40448
  }
39863
40449
  /** ~/.bluma/threads/YYYY/MM/DD/<threadId>.jsonl (data local de criação). */
39864
40450
  buildDatedThreadHistoryPath(threadId, at = /* @__PURE__ */ new Date()) {
39865
40451
  const y = String(at.getFullYear());
39866
40452
  const mo = String(at.getMonth() + 1).padStart(2, "0");
39867
40453
  const d = String(at.getDate()).padStart(2, "0");
39868
- return path49.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
40454
+ return path52.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
39869
40455
  }
39870
40456
  async resolveHistoryPath(threadId) {
39871
40457
  const index = await this.loadIndex();
39872
40458
  const entry = index.threads.find((t) => t.threadId === threadId);
39873
40459
  if (entry?.historyPath) {
39874
40460
  try {
39875
- await fs45.access(entry.historyPath);
40461
+ await fs47.access(entry.historyPath);
39876
40462
  return entry.historyPath;
39877
40463
  } catch {
39878
40464
  }
39879
40465
  }
39880
40466
  const legacy = this.getLegacyHistoryPath(threadId);
39881
40467
  try {
39882
- await fs45.access(legacy);
40468
+ await fs47.access(legacy);
39883
40469
  return legacy;
39884
40470
  } catch {
39885
40471
  return entry?.historyPath ?? legacy;
@@ -39896,9 +40482,9 @@ var ThreadStore = class {
39896
40482
  for (const msg of history.messages) {
39897
40483
  lines.push(JSON.stringify({ type: "message", ...msg }));
39898
40484
  }
39899
- await fs45.mkdir(path49.dirname(historyPath), { recursive: true }).catch(() => {
40485
+ await fs47.mkdir(path52.dirname(historyPath), { recursive: true }).catch(() => {
39900
40486
  });
39901
- await fs45.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
40487
+ await fs47.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
39902
40488
  }
39903
40489
  /**
39904
40490
  * Guarda o histórico de uma thread
@@ -39920,7 +40506,7 @@ var ThreadStore = class {
39920
40506
  ].filter((p, i, arr) => Boolean(p) && arr.indexOf(p) === i);
39921
40507
  for (const historyPath of pathsToTry) {
39922
40508
  try {
39923
- const content = await fs45.readFile(historyPath, "utf-8");
40509
+ const content = await fs47.readFile(historyPath, "utf-8");
39924
40510
  const lines = content.split("\n").filter(Boolean);
39925
40511
  const history = {
39926
40512
  threadId,
@@ -39955,7 +40541,7 @@ var ThreadStore = class {
39955
40541
  const entry = index.threads.find((t) => t.threadId === threadId);
39956
40542
  if (!entry) throw new Error(`Thread not found: ${threadId}`);
39957
40543
  const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
39958
- await fs45.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
40544
+ await fs47.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
39959
40545
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
39960
40546
  await this.saveIndex(index);
39961
40547
  }
@@ -42828,16 +43414,16 @@ import latestVersion from "latest-version";
42828
43414
  import semverGt from "semver/functions/gt.js";
42829
43415
  import semverValid from "semver/functions/valid.js";
42830
43416
  import { fileURLToPath as fileURLToPath6 } from "url";
42831
- import path50 from "path";
42832
- import fs46 from "fs";
43417
+ import path53 from "path";
43418
+ import fs48 from "fs";
42833
43419
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
42834
43420
  function findBlumaPackageJson(startDir) {
42835
43421
  let dir = startDir;
42836
43422
  for (let i = 0; i < 12; i++) {
42837
- const candidate = path50.join(dir, "package.json");
42838
- if (fs46.existsSync(candidate)) {
43423
+ const candidate = path53.join(dir, "package.json");
43424
+ if (fs48.existsSync(candidate)) {
42839
43425
  try {
42840
- const raw = fs46.readFileSync(candidate, "utf8");
43426
+ const raw = fs48.readFileSync(candidate, "utf8");
42841
43427
  const parsed = JSON.parse(raw);
42842
43428
  if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
42843
43429
  return { name: parsed.name, version: String(parsed.version) };
@@ -42845,7 +43431,7 @@ function findBlumaPackageJson(startDir) {
42845
43431
  } catch {
42846
43432
  }
42847
43433
  }
42848
- const parent = path50.dirname(dir);
43434
+ const parent = path53.dirname(dir);
42849
43435
  if (parent === dir) break;
42850
43436
  dir = parent;
42851
43437
  }
@@ -42854,13 +43440,13 @@ function findBlumaPackageJson(startDir) {
42854
43440
  function resolveInstalledBlumaPackage() {
42855
43441
  const tried = /* @__PURE__ */ new Set();
42856
43442
  const tryFrom = (dir) => {
42857
- const abs = path50.resolve(dir);
43443
+ const abs = path53.resolve(dir);
42858
43444
  if (tried.has(abs)) return null;
42859
43445
  tried.add(abs);
42860
43446
  return findBlumaPackageJson(abs);
42861
43447
  };
42862
43448
  try {
42863
- const fromBundle = tryFrom(path50.dirname(fileURLToPath6(import.meta.url)));
43449
+ const fromBundle = tryFrom(path53.dirname(fileURLToPath6(import.meta.url)));
42864
43450
  if (fromBundle) return fromBundle;
42865
43451
  } catch {
42866
43452
  }
@@ -42868,12 +43454,12 @@ function resolveInstalledBlumaPackage() {
42868
43454
  if (argv1 && !argv1.startsWith("-")) {
42869
43455
  try {
42870
43456
  let resolved = argv1;
42871
- if (path50.isAbsolute(argv1) && fs46.existsSync(argv1)) {
42872
- resolved = fs46.realpathSync(argv1);
43457
+ if (path53.isAbsolute(argv1) && fs48.existsSync(argv1)) {
43458
+ resolved = fs48.realpathSync(argv1);
42873
43459
  } else {
42874
- resolved = path50.resolve(process.cwd(), argv1);
43460
+ resolved = path53.resolve(process.cwd(), argv1);
42875
43461
  }
42876
- const fromArgv = tryFrom(path50.dirname(resolved));
43462
+ const fromArgv = tryFrom(path53.dirname(resolved));
42877
43463
  if (fromArgv) return fromArgv;
42878
43464
  } catch {
42879
43465
  }
@@ -43692,16 +44278,16 @@ function usePlanMode() {
43692
44278
 
43693
44279
  // src/app/hooks/useAgentMode.ts
43694
44280
  import { useState as useState22, useEffect as useEffect21, useCallback as useCallback9 } from "react";
43695
- import * as fs47 from "fs";
43696
- import * as path51 from "path";
44281
+ import * as fs49 from "fs";
44282
+ import * as path54 from "path";
43697
44283
  import { homedir as homedir3 } from "os";
43698
- var SETTINGS_PATH = path51.join(homedir3(), ".bluma", "settings.json");
44284
+ var SETTINGS_PATH = path54.join(homedir3(), ".bluma", "settings.json");
43699
44285
  function readAgentModeFromFile() {
43700
44286
  try {
43701
- if (!fs47.existsSync(SETTINGS_PATH)) {
44287
+ if (!fs49.existsSync(SETTINGS_PATH)) {
43702
44288
  return "default";
43703
44289
  }
43704
- const content = fs47.readFileSync(SETTINGS_PATH, "utf-8");
44290
+ const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
43705
44291
  const settings = JSON.parse(content);
43706
44292
  return settings.agentMode || "default";
43707
44293
  } catch (error) {
@@ -43720,16 +44306,16 @@ function useAgentMode() {
43720
44306
  }, []);
43721
44307
  const updateAgentMode = useCallback9((mode) => {
43722
44308
  try {
43723
- if (!fs47.existsSync(SETTINGS_PATH)) {
43724
- fs47.mkdirSync(path51.dirname(SETTINGS_PATH), { recursive: true });
44309
+ if (!fs49.existsSync(SETTINGS_PATH)) {
44310
+ fs49.mkdirSync(path54.dirname(SETTINGS_PATH), { recursive: true });
43725
44311
  }
43726
44312
  let settings = {};
43727
- if (fs47.existsSync(SETTINGS_PATH)) {
43728
- const content = fs47.readFileSync(SETTINGS_PATH, "utf-8");
44313
+ if (fs49.existsSync(SETTINGS_PATH)) {
44314
+ const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
43729
44315
  settings = JSON.parse(content);
43730
44316
  }
43731
44317
  settings.agentMode = mode;
43732
- fs47.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
44318
+ fs49.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
43733
44319
  setAgentMode(mode);
43734
44320
  } catch (error) {
43735
44321
  console.error("Failed to update agent mode:", error);
@@ -44992,10 +45578,10 @@ import React39 from "react";
44992
45578
  import { memo as memo26, useCallback as useCallback11, useEffect as useEffect23, useMemo as useMemo8, useState as useState25 } from "react";
44993
45579
 
44994
45580
  // src/app/agent/session_manager/session_resume_browser.ts
44995
- import path52 from "path";
44996
- import { promises as fs48 } from "fs";
45581
+ import path55 from "path";
45582
+ import { promises as fs50 } from "fs";
44997
45583
  function getSessionsRoot() {
44998
- return path52.join(getPreferredAppDir(), "sessions");
45584
+ return path55.join(getPreferredAppDir(), "sessions");
44999
45585
  }
45000
45586
  function normalizeSessionsCwd(cwd2) {
45001
45587
  return cwd2.replace(/\\/g, "/").split("/").filter((p) => p && p !== "." && p !== "..").join("/");
@@ -45016,9 +45602,9 @@ async function sessionEntryFromFile(absPath, sessionId) {
45016
45602
  let preview = "(no messages)";
45017
45603
  let lastActivityMs = Date.now();
45018
45604
  try {
45019
- const st = await fs48.stat(absPath);
45605
+ const st = await fs50.stat(absPath);
45020
45606
  lastActivityMs = st.mtimeMs;
45021
- const raw = await fs48.readFile(absPath, "utf-8");
45607
+ const raw = await fs50.readFile(absPath, "utf-8");
45022
45608
  const data = JSON.parse(raw);
45023
45609
  preview = previewFromHistory(data.conversation_history);
45024
45610
  const iso = data.last_updated || data.created_at;
@@ -45046,15 +45632,15 @@ function compareDirNames(a, b) {
45046
45632
  async function listSessionBrowserEntries(cwdRel) {
45047
45633
  const cwd2 = normalizeSessionsCwd(cwdRel);
45048
45634
  const root = getSessionsRoot();
45049
- const absDir = cwd2 ? path52.join(root, ...cwd2.split("/")) : root;
45635
+ const absDir = cwd2 ? path55.join(root, ...cwd2.split("/")) : root;
45050
45636
  const out = [];
45051
45637
  if (cwd2) {
45052
45638
  out.push({ kind: "up", label: ".." });
45053
45639
  }
45054
45640
  let dirents;
45055
45641
  try {
45056
- await fs48.mkdir(absDir, { recursive: true });
45057
- dirents = await fs48.readdir(absDir, { withFileTypes: true });
45642
+ await fs50.mkdir(absDir, { recursive: true });
45643
+ dirents = await fs50.readdir(absDir, { withFileTypes: true });
45058
45644
  } catch {
45059
45645
  return out;
45060
45646
  }
@@ -45063,7 +45649,7 @@ async function listSessionBrowserEntries(cwdRel) {
45063
45649
  for (const e of dirents) {
45064
45650
  const name = String(e.name);
45065
45651
  if (name.startsWith(".")) continue;
45066
- const full = path52.join(absDir, name);
45652
+ const full = path55.join(absDir, name);
45067
45653
  if (e.isDirectory()) {
45068
45654
  dirNames.push(name);
45069
45655
  } else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
@@ -45073,7 +45659,7 @@ async function listSessionBrowserEntries(cwdRel) {
45073
45659
  dirNames.sort(compareDirNames);
45074
45660
  const sessions = [];
45075
45661
  for (const { name, full } of sessionFiles) {
45076
- const sessionId = path52.basename(name, ".json");
45662
+ const sessionId = path55.basename(name, ".json");
45077
45663
  sessions.push(await sessionEntryFromFile(full, sessionId));
45078
45664
  }
45079
45665
  sessions.sort((a, b) => {
@@ -45380,15 +45966,19 @@ function resolveResultUserMessage(lastAssistantMessage, lastAttachments) {
45380
45966
  }
45381
45967
  return "Tarefa conclu\xEDda.";
45382
45968
  }
45383
- function buildSuccessResultData(envelope, sessionId, lastAssistantMessage, lastAttachments) {
45969
+ function buildSuccessResultData(envelope, sessionId, lastAssistantMessage, lastAttachments, lastFactorShUrlApp) {
45384
45970
  const message2 = resolveResultUserMessage(lastAssistantMessage, lastAttachments);
45385
- return {
45971
+ const data = {
45386
45972
  message_id: envelope.message_id || sessionId,
45387
45973
  action: envelope.action || "unknown",
45388
45974
  last_assistant_message: message2,
45389
45975
  message: message2,
45390
45976
  attachments: lastAttachments
45391
45977
  };
45978
+ if (lastFactorShUrlApp) {
45979
+ data[FACTOR_SH_URL_APP_FIELD] = lastFactorShUrlApp;
45980
+ }
45981
+ return data;
45392
45982
  }
45393
45983
  function finalizeSession(sessionId, status, metadata) {
45394
45984
  updateSession(sessionId, {
@@ -45434,9 +46024,9 @@ async function runAgentMode() {
45434
46024
  try {
45435
46025
  if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
45436
46026
  const filePath = args[inputFileIndex + 1];
45437
- rawPayload = fs49.readFileSync(filePath, "utf-8");
46027
+ rawPayload = fs51.readFileSync(filePath, "utf-8");
45438
46028
  } else {
45439
- rawPayload = fs49.readFileSync(0, "utf-8");
46029
+ rawPayload = fs51.readFileSync(0, "utf-8");
45440
46030
  }
45441
46031
  } catch (err) {
45442
46032
  writeAgentEvent(registrySessionId, {
@@ -45472,6 +46062,15 @@ async function runAgentMode() {
45472
46062
  if (envelope.metadata?.sandbox_name) {
45473
46063
  process.env.BLUMA_SANDBOX_NAME = String(envelope.metadata.sandbox_name);
45474
46064
  }
46065
+ } else if (process.env.BLUMA_SANDBOX_WORKSPACE?.trim()) {
46066
+ process.env.BLUMA_SANDBOX = "true";
46067
+ if (!process.env.BLUMA_SANDBOX_NAME?.trim()) {
46068
+ process.env.BLUMA_SANDBOX_NAME = "sandbox-api";
46069
+ }
46070
+ }
46071
+ const severinoUrl = (process.env.SEVERINO_URL || "").trim();
46072
+ if (!(process.env.FACTORAI_BASE_URL || process.env.FACTORAI_URL || "").trim() && severinoUrl) {
46073
+ process.env.FACTORAI_BASE_URL = severinoUrl;
45475
46074
  }
45476
46075
  const envelopeUserRequest = typeof envelope.context === "object" && envelope.context !== null ? String(
45477
46076
  envelope.context.user_request ?? envelope.context.userRequest ?? ""
@@ -45510,6 +46109,7 @@ async function runAgentMode() {
45510
46109
  });
45511
46110
  let lastAssistantMessage = null;
45512
46111
  let lastAttachments = null;
46112
+ let lastFactorShUrlApp = null;
45513
46113
  let resultEmitted = false;
45514
46114
  let agentRef = null;
45515
46115
  let reasoningSequence = 0;
@@ -45560,6 +46160,16 @@ async function runAgentMode() {
45560
46160
  if (Array.isArray(attachments)) {
45561
46161
  lastAttachments = attachments.filter((p) => typeof p === "string");
45562
46162
  }
46163
+ const factorUrl = messagePayload?.[FACTOR_SH_URL_APP_FIELD] ?? parsed?.[FACTOR_SH_URL_APP_FIELD] ?? parsed?.data?.[FACTOR_SH_URL_APP_FIELD];
46164
+ if (typeof factorUrl === "string" && factorUrl.trim()) {
46165
+ lastFactorShUrlApp = factorUrl.trim();
46166
+ }
46167
+ if (!lastFactorShUrlApp && extractMessageToolPayload(parsed)?.message_type === "result") {
46168
+ const fromArgs = payload?.arguments?.[FACTOR_SH_URL_APP_FIELD];
46169
+ if (typeof fromArgs === "string" && fromArgs.trim()) {
46170
+ lastFactorShUrlApp = fromArgs.trim();
46171
+ }
46172
+ }
45563
46173
  } catch {
45564
46174
  }
45565
46175
  }
@@ -45576,7 +46186,8 @@ async function runAgentMode() {
45576
46186
  envelope,
45577
46187
  sessionId,
45578
46188
  lastAssistantMessage,
45579
- lastAttachments
46189
+ lastAttachments,
46190
+ lastFactorShUrlApp
45580
46191
  )
45581
46192
  });
45582
46193
  finalizeSession(sessionId, "completed", { finishedBy: "done-event" });
@@ -45628,7 +46239,8 @@ async function runAgentMode() {
45628
46239
  envelope,
45629
46240
  sessionId,
45630
46241
  lastAssistantMessage,
45631
- lastAttachments
46242
+ lastAttachments,
46243
+ lastFactorShUrlApp
45632
46244
  )
45633
46245
  });
45634
46246
  finalizeSession(sessionId, "completed", { finishedBy: "post-turn-fallback" });
@@ -45655,9 +46267,9 @@ async function runAgentMode() {
45655
46267
  }
45656
46268
  function readCliPackageVersion() {
45657
46269
  try {
45658
- const base = path53.dirname(fileURLToPath7(import.meta.url));
45659
- const pkgPath = path53.join(base, "..", "package.json");
45660
- const j = JSON.parse(fs49.readFileSync(pkgPath, "utf8"));
46270
+ const base = path56.dirname(fileURLToPath7(import.meta.url));
46271
+ const pkgPath = path56.join(base, "..", "package.json");
46272
+ const j = JSON.parse(fs51.readFileSync(pkgPath, "utf8"));
45661
46273
  return String(j.version || "0.0.0");
45662
46274
  } catch {
45663
46275
  return "0.0.0";
@@ -45783,7 +46395,7 @@ function startBackgroundAgent() {
45783
46395
  process.exit(1);
45784
46396
  }
45785
46397
  const filePath = args[inputFileIndex + 1];
45786
- const rawPayload = fs49.readFileSync(filePath, "utf-8");
46398
+ const rawPayload = fs51.readFileSync(filePath, "utf-8");
45787
46399
  const envelope = JSON.parse(rawPayload);
45788
46400
  const sessionId = envelope.session_id || envelope.message_id || uuidv412();
45789
46401
  registerSession({