@nomad-e/bluma-cli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/skills/factorai-sh/SKILL.md +97 -0
- package/dist/main.js +1384 -828
- package/package.json +5 -3
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
|
|
410
|
-
import
|
|
409
|
+
import fs10 from "fs";
|
|
410
|
+
import path9 from "path";
|
|
411
411
|
function getStorePath() {
|
|
412
412
|
const policy = getSandboxPolicy();
|
|
413
|
-
return
|
|
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 (
|
|
430
|
-
const raw =
|
|
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
|
-
|
|
449
|
+
fs10.mkdirSync(path9.dirname(storePath), { recursive: true });
|
|
450
450
|
state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
451
|
-
|
|
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
|
|
838
|
+
import fs16 from "fs";
|
|
839
839
|
import os11 from "os";
|
|
840
|
-
import
|
|
840
|
+
import path17 from "path";
|
|
841
841
|
function getRegistryDir() {
|
|
842
|
-
return
|
|
842
|
+
return path17.join(process.env.HOME || os11.homedir(), ".bluma", "registry");
|
|
843
843
|
}
|
|
844
844
|
function getRegistryFile() {
|
|
845
|
-
return
|
|
845
|
+
return path17.join(getRegistryDir(), "sessions.json");
|
|
846
846
|
}
|
|
847
847
|
function ensureRegistryDir() {
|
|
848
|
-
|
|
848
|
+
fs16.mkdirSync(getRegistryDir(), { recursive: true });
|
|
849
849
|
}
|
|
850
850
|
function readRegistry() {
|
|
851
851
|
ensureRegistryDir();
|
|
852
852
|
const file = getRegistryFile();
|
|
853
|
-
if (!
|
|
853
|
+
if (!fs16.existsSync(file)) {
|
|
854
854
|
return { entries: [] };
|
|
855
855
|
}
|
|
856
856
|
try {
|
|
857
|
-
return JSON.parse(
|
|
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
|
-
|
|
864
|
+
fs16.writeFileSync(getRegistryFile(), JSON.stringify(state2, null, 2), "utf-8");
|
|
865
865
|
}
|
|
866
866
|
function getSessionLogPath(sessionId) {
|
|
867
867
|
ensureRegistryDir();
|
|
868
|
-
return
|
|
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
|
-
|
|
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 (!
|
|
919
|
-
return
|
|
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
|
|
928
|
+
import fs17 from "fs";
|
|
929
929
|
import os12 from "os";
|
|
930
|
-
import
|
|
930
|
+
import path18 from "path";
|
|
931
931
|
function getLogDir() {
|
|
932
|
-
const dir =
|
|
933
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1069
|
+
import fs18 from "fs";
|
|
1070
1070
|
import os13 from "os";
|
|
1071
|
-
import
|
|
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 =
|
|
1077
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
1182
|
+
const filePath = path19.join(dir, file);
|
|
1183
1183
|
try {
|
|
1184
|
-
const content =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
1285
|
-
if (!
|
|
1286
|
-
|
|
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 =
|
|
1297
|
-
if (
|
|
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
|
|
1316
|
+
import fs19 from "fs";
|
|
1317
1317
|
import os14 from "os";
|
|
1318
|
-
import
|
|
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 =
|
|
1433
|
-
const payloadPath =
|
|
1432
|
+
const payloadDir = fs19.mkdtempSync(path20.join(os14.tmpdir(), "bluma-worker-"));
|
|
1433
|
+
const payloadPath = path20.join(payloadDir, `${sessionId}.json`);
|
|
1434
1434
|
try {
|
|
1435
|
-
|
|
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(
|
|
2205
|
-
const _ = slowLogging`fs.appendFileSync(${
|
|
2204
|
+
appendFileSync(path56, data, options) {
|
|
2205
|
+
const _ = slowLogging`fs.appendFileSync(${path56}, ${data.length} chars)`;
|
|
2206
2206
|
if (options?.mode !== void 0) {
|
|
2207
2207
|
try {
|
|
2208
|
-
const fd = fs.openSync(
|
|
2208
|
+
const fd = fs.openSync(path56, "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(
|
|
2219
|
+
fs.appendFileSync(path56, 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(
|
|
2226
|
-
const _ = slowLogging`fs.unlinkSync(${
|
|
2227
|
-
fs.unlinkSync(
|
|
2225
|
+
unlinkSync(path56) {
|
|
2226
|
+
const _ = slowLogging`fs.unlinkSync(${path56})`;
|
|
2227
|
+
fs.unlinkSync(path56);
|
|
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,
|
|
2234
|
-
const _ = slowLogging`fs.linkSync(${target} → ${
|
|
2235
|
-
fs.linkSync(target,
|
|
2233
|
+
linkSync(target, path56) {
|
|
2234
|
+
const _ = slowLogging`fs.linkSync(${target} → ${path56})`;
|
|
2235
|
+
fs.linkSync(target, path56);
|
|
2236
2236
|
},
|
|
2237
|
-
symlinkSync(target,
|
|
2238
|
-
const _ = slowLogging`fs.symlinkSync(${target} → ${
|
|
2239
|
-
fs.symlinkSync(target,
|
|
2237
|
+
symlinkSync(target, path56, type) {
|
|
2238
|
+
const _ = slowLogging`fs.symlinkSync(${target} → ${path56})`;
|
|
2239
|
+
fs.symlinkSync(target, path56, type);
|
|
2240
2240
|
},
|
|
2241
|
-
readlinkSync(
|
|
2242
|
-
const _ = slowLogging`fs.readlinkSync(${
|
|
2243
|
-
return fs.readlinkSync(
|
|
2241
|
+
readlinkSync(path56) {
|
|
2242
|
+
const _ = slowLogging`fs.readlinkSync(${path56})`;
|
|
2243
|
+
return fs.readlinkSync(path56);
|
|
2244
2244
|
},
|
|
2245
|
-
realpathSync(
|
|
2246
|
-
const _ = slowLogging`fs.realpathSync(${
|
|
2247
|
-
return fs.realpathSync(
|
|
2245
|
+
realpathSync(path56) {
|
|
2246
|
+
const _ = slowLogging`fs.realpathSync(${path56})`;
|
|
2247
|
+
return fs.realpathSync(path56).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(
|
|
2281
|
-
const _ = slowLogging`fs.rmSync(${
|
|
2282
|
-
fs.rmSync(
|
|
2280
|
+
rmSync(path56, options) {
|
|
2281
|
+
const _ = slowLogging`fs.rmSync(${path56})`;
|
|
2282
|
+
fs.rmSync(path56, options);
|
|
2283
2283
|
},
|
|
2284
|
-
createWriteStream(
|
|
2285
|
-
return fs.createWriteStream(
|
|
2284
|
+
createWriteStream(path56) {
|
|
2285
|
+
return fs.createWriteStream(path56);
|
|
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,
|
|
2392
|
+
async function appendAsync(needMkdir, dir, path56, content) {
|
|
2393
2393
|
if (needMkdir) {
|
|
2394
2394
|
await mkdir(dir, { recursive: true }).catch(() => {
|
|
2395
2395
|
});
|
|
2396
2396
|
}
|
|
2397
|
-
await appendFile(
|
|
2397
|
+
await appendFile(path56, 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
|
|
2408
|
-
const dir = dirname(
|
|
2407
|
+
const path56 = getDebugLogPath();
|
|
2408
|
+
const dir = dirname(path56);
|
|
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(
|
|
2418
|
+
getFsImplementation().appendFileSync(path56, content);
|
|
2419
2419
|
void updateLatestDebugLogSymlink();
|
|
2420
2420
|
return;
|
|
2421
2421
|
}
|
|
2422
|
-
pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir,
|
|
2422
|
+
pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path56, 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 = (
|
|
8610
|
-
return
|
|
8609
|
+
var cleanupPath = (path56) => {
|
|
8610
|
+
return path56?.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
|
|
12588
|
-
import
|
|
12587
|
+
import fs51 from "fs";
|
|
12588
|
+
import path55 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,7 +13479,7 @@ function cancelSlashSubmenu() {
|
|
|
13479
13479
|
|
|
13480
13480
|
// src/app/agent/agent.ts
|
|
13481
13481
|
import * as dotenv from "dotenv";
|
|
13482
|
-
import
|
|
13482
|
+
import path46 from "path";
|
|
13483
13483
|
import os31 from "os";
|
|
13484
13484
|
|
|
13485
13485
|
// src/app/agent/tool_invoker.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/
|
|
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 (!
|
|
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 =
|
|
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 ${
|
|
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
|
|
14209
|
-
import
|
|
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
|
|
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 =
|
|
14256
|
-
const stat = await
|
|
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
|
|
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 =
|
|
14272
|
-
const relativePath =
|
|
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(
|
|
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(
|
|
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
|
|
14317
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
14694
|
-
const relativePath =
|
|
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
|
|
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
|
|
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 =
|
|
14871
|
-
const base =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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,
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
15279
|
-
import { promises as
|
|
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 =
|
|
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 =
|
|
15287
|
-
await
|
|
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 =
|
|
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
|
|
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 =
|
|
15421
|
-
const content = await
|
|
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
|
|
15966
|
-
import * as
|
|
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 =
|
|
15976
|
-
const legacyPath =
|
|
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 (
|
|
15980
|
-
raw =
|
|
15981
|
-
} else if (
|
|
15982
|
-
raw =
|
|
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
|
|
16191
|
+
return path14.join(os10.homedir(), ".bluma", "coding_memory.json");
|
|
16021
16192
|
}
|
|
16022
16193
|
function getLegacyMemoryFilePath() {
|
|
16023
|
-
return
|
|
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 =
|
|
16204
|
+
const legacyDistinct = path14.resolve(legacy) !== path14.resolve(filePath);
|
|
16034
16205
|
const readIntoStore = (p) => {
|
|
16035
|
-
const raw =
|
|
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 (
|
|
16213
|
+
if (fs12.existsSync(filePath)) {
|
|
16043
16214
|
readIntoStore(filePath);
|
|
16044
16215
|
}
|
|
16045
|
-
if (memoryStore.length === 0 && legacyDistinct &&
|
|
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 =
|
|
16060
|
-
if (!
|
|
16061
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
16681
|
-
import * as
|
|
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 (!
|
|
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 =
|
|
16905
|
+
const content = fs14.readFileSync(resolved, "utf-8");
|
|
16735
16906
|
const uri = pathToFileURL(resolved).href;
|
|
16736
|
-
const root =
|
|
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
|
|
16857
|
-
import
|
|
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 =
|
|
17046
|
+
const dir = path16.dirname(resolvedPath);
|
|
16876
17047
|
let existed = false;
|
|
16877
17048
|
try {
|
|
16878
|
-
const st = await
|
|
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
|
|
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
|
|
17069
|
+
await fs15.mkdir(dir, { recursive: true });
|
|
16899
17070
|
}
|
|
16900
|
-
await
|
|
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
|
|
17380
|
+
import fs20 from "fs";
|
|
17210
17381
|
import os15 from "os";
|
|
17211
|
-
import
|
|
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 =
|
|
17224
|
-
|
|
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 =
|
|
17230
|
-
|
|
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 =
|
|
17236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
17611
|
-
import * as
|
|
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 =
|
|
17631
|
-
if (
|
|
17632
|
-
const stat =
|
|
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
|
|
17672
|
-
import * as
|
|
17860
|
+
import * as fs22 from "fs";
|
|
17861
|
+
import * as path23 from "path";
|
|
17673
17862
|
import os16 from "os";
|
|
17674
17863
|
function memoryPath() {
|
|
17675
|
-
return
|
|
17864
|
+
return path23.join(process.env.HOME || os16.homedir(), ".bluma", "coding_memory.json");
|
|
17676
17865
|
}
|
|
17677
17866
|
function sessionsDir() {
|
|
17678
|
-
return
|
|
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 (
|
|
17892
|
+
if (fs22.existsSync(memPath)) {
|
|
17704
17893
|
try {
|
|
17705
|
-
memData = JSON.parse(
|
|
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 (
|
|
17971
|
+
if (fs22.existsSync(sessDir)) {
|
|
17783
17972
|
try {
|
|
17784
|
-
const sessionFiles =
|
|
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(
|
|
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
|
-
|
|
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
|
|
17996
|
-
import
|
|
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
|
|
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-
|
|
18580
|
-
destructive:
|
|
18581
|
-
|
|
18582
|
-
|
|
18583
|
-
|
|
18584
|
-
|
|
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,
|
|
18611
|
-
|
|
18612
|
-
|
|
18613
|
-
|
|
18614
|
-
|
|
18615
|
-
|
|
18616
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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 =
|
|
18771
|
-
await
|
|
18985
|
+
const dir = path24.dirname(filePath);
|
|
18986
|
+
await fs23.mkdir(dir, { recursive: true });
|
|
18772
18987
|
const replacedContent = content.replace(/{{NAME}}/g, projectName);
|
|
18773
|
-
await
|
|
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 =
|
|
19007
|
+
const projectPath = path24.join(baseDir, name);
|
|
18793
19008
|
try {
|
|
18794
|
-
await
|
|
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
|
|
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 =
|
|
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 =
|
|
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,8 +19065,11 @@ async function createNextApp(args) {
|
|
|
18850
19065
|
|
|
18851
19066
|
// src/app/agent/tools/DeployAppTool/DeployAppTool.ts
|
|
18852
19067
|
init_sandbox_policy();
|
|
18853
|
-
import { promises as
|
|
18854
|
-
import
|
|
19068
|
+
import { promises as fs24 } from "fs";
|
|
19069
|
+
import path25 from "path";
|
|
19070
|
+
function shQuote(value) {
|
|
19071
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
19072
|
+
}
|
|
18855
19073
|
var EXCLUDE_PATTERNS = [
|
|
18856
19074
|
"node_modules",
|
|
18857
19075
|
".next",
|
|
@@ -18867,12 +19085,15 @@ var EXCLUDE_PATTERNS = [
|
|
|
18867
19085
|
".vercel",
|
|
18868
19086
|
".turbo"
|
|
18869
19087
|
];
|
|
18870
|
-
|
|
18871
|
-
|
|
18872
|
-
|
|
18873
|
-
excludes.push("--exclude", pattern);
|
|
19088
|
+
function zipExcludeArg(pattern) {
|
|
19089
|
+
if (pattern.includes("*") || pattern.startsWith(".")) {
|
|
19090
|
+
return pattern;
|
|
18874
19091
|
}
|
|
18875
|
-
|
|
19092
|
+
return `${pattern}/*`;
|
|
19093
|
+
}
|
|
19094
|
+
async function createProjectZip(projectDir, zipPath) {
|
|
19095
|
+
const excludeFlags = EXCLUDE_PATTERNS.flatMap((pattern) => ["-x", zipExcludeArg(pattern)]);
|
|
19096
|
+
const zipCommand = ["zip", "-r", shQuote(zipPath), ".", ...excludeFlags.map(shQuote)].join(" ");
|
|
18876
19097
|
const result = await shellCommand({
|
|
18877
19098
|
command: zipCommand,
|
|
18878
19099
|
cwd: projectDir,
|
|
@@ -18888,6 +19109,7 @@ async function createProjectZip(projectDir, zipPath) {
|
|
|
18888
19109
|
async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
|
|
18889
19110
|
const deployUrl = `${severinoUrl.replace(/\/$/, "")}/api/v1/deploy`;
|
|
18890
19111
|
const curlArgs = [
|
|
19112
|
+
"-sS",
|
|
18891
19113
|
"-X",
|
|
18892
19114
|
"POST",
|
|
18893
19115
|
deployUrl,
|
|
@@ -18905,7 +19127,7 @@ async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
|
|
|
18905
19127
|
curlArgs.push("-H", `X-API-Key: ${apiKey}`);
|
|
18906
19128
|
}
|
|
18907
19129
|
curlArgs.push("-H", "Accept: application/json");
|
|
18908
|
-
const curlCommand = `curl ${curlArgs.join(" ")}`;
|
|
19130
|
+
const curlCommand = `curl ${curlArgs.map(shQuote).join(" ")}`;
|
|
18909
19131
|
const result = await shellCommand({
|
|
18910
19132
|
command: curlCommand,
|
|
18911
19133
|
timeout: 60
|
|
@@ -18992,8 +19214,10 @@ function buildFactorAiManifest(appContext, deployResult, appName) {
|
|
|
18992
19214
|
mode: "tool-first",
|
|
18993
19215
|
instructions: [
|
|
18994
19216
|
"Read factorai.sh.json as the source of truth before editing files.",
|
|
18995
|
-
"
|
|
18996
|
-
"
|
|
19217
|
+
"Load skill factorai-sh for the full FactorAI.sh workflow.",
|
|
19218
|
+
"For an app already online: use factorai.sh.apply_app_changes with deploy:true (not deploy_app again).",
|
|
19219
|
+
"Poll factorai.sh.get_app_status until building\u2192ready before reporting the live URL.",
|
|
19220
|
+
"Use factorai.sh.redeploy_app only when rebuilding without new file patches."
|
|
18997
19221
|
]
|
|
18998
19222
|
},
|
|
18999
19223
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -19001,8 +19225,8 @@ function buildFactorAiManifest(appContext, deployResult, appName) {
|
|
|
19001
19225
|
}
|
|
19002
19226
|
async function writeFactorAiManifest(projectDir, appContext, deployResult, appName) {
|
|
19003
19227
|
const manifest = buildFactorAiManifest(appContext, deployResult, appName);
|
|
19004
|
-
const manifestPath =
|
|
19005
|
-
await
|
|
19228
|
+
const manifestPath = path25.join(projectDir, "factorai.sh.json");
|
|
19229
|
+
await fs24.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
19006
19230
|
`, "utf-8");
|
|
19007
19231
|
return manifest;
|
|
19008
19232
|
}
|
|
@@ -19022,23 +19246,23 @@ async function deployApp(args) {
|
|
|
19022
19246
|
}
|
|
19023
19247
|
const resolvedProjectDir = resolveWorkspacePath(projectDir);
|
|
19024
19248
|
try {
|
|
19025
|
-
await
|
|
19249
|
+
await fs24.access(resolvedProjectDir);
|
|
19026
19250
|
} catch {
|
|
19027
19251
|
return {
|
|
19028
19252
|
success: false,
|
|
19029
19253
|
error: `Project directory not found: ${resolvedProjectDir}`
|
|
19030
19254
|
};
|
|
19031
19255
|
}
|
|
19032
|
-
const packageJsonPath =
|
|
19256
|
+
const packageJsonPath = path25.join(resolvedProjectDir, "package.json");
|
|
19033
19257
|
try {
|
|
19034
|
-
await
|
|
19258
|
+
await fs24.access(packageJsonPath);
|
|
19035
19259
|
} catch {
|
|
19036
19260
|
return {
|
|
19037
19261
|
success: false,
|
|
19038
19262
|
error: "Not a Next.js project: package.json not found"
|
|
19039
19263
|
};
|
|
19040
19264
|
}
|
|
19041
|
-
const packageJsonContent = await
|
|
19265
|
+
const packageJsonContent = await fs24.readFile(packageJsonPath, "utf-8");
|
|
19042
19266
|
const packageJson = JSON.parse(packageJsonContent);
|
|
19043
19267
|
const hasNext = packageJson.dependencies?.next || packageJson.devDependencies?.next;
|
|
19044
19268
|
if (!hasNext) {
|
|
@@ -19047,18 +19271,18 @@ async function deployApp(args) {
|
|
|
19047
19271
|
error: "Not a Next.js project: next not found in dependencies"
|
|
19048
19272
|
};
|
|
19049
19273
|
}
|
|
19050
|
-
const appName = name || packageJson.name ||
|
|
19051
|
-
const tempDir =
|
|
19052
|
-
await
|
|
19053
|
-
const zipPath =
|
|
19274
|
+
const appName = name || packageJson.name || path25.basename(resolvedProjectDir);
|
|
19275
|
+
const tempDir = path25.join(resolvedProjectDir, ".tmp");
|
|
19276
|
+
await fs24.mkdir(tempDir, { recursive: true });
|
|
19277
|
+
const zipPath = path25.join(tempDir, `${appName}-${Date.now()}.zip`);
|
|
19054
19278
|
console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
|
|
19055
19279
|
await createProjectZip(resolvedProjectDir, zipPath);
|
|
19056
|
-
const zipStats = await
|
|
19280
|
+
const zipStats = await fs24.stat(zipPath);
|
|
19057
19281
|
const zipSizeMB = zipStats.size / 1024 / 1024;
|
|
19058
19282
|
if (zipSizeMB > 50) {
|
|
19059
|
-
await
|
|
19283
|
+
await fs24.unlink(zipPath).catch(() => {
|
|
19060
19284
|
});
|
|
19061
|
-
await
|
|
19285
|
+
await fs24.rmdir(tempDir).catch(() => {
|
|
19062
19286
|
});
|
|
19063
19287
|
return {
|
|
19064
19288
|
success: false,
|
|
@@ -19069,8 +19293,8 @@ async function deployApp(args) {
|
|
|
19069
19293
|
console.log(`[deploy-app] Uploading to ${severinoUrl}...`);
|
|
19070
19294
|
const deployResult = await uploadToSeverino(zipPath, severinoUrl, appName, apiKey, appId);
|
|
19071
19295
|
try {
|
|
19072
|
-
await
|
|
19073
|
-
await
|
|
19296
|
+
await fs24.unlink(zipPath);
|
|
19297
|
+
await fs24.rmdir(tempDir);
|
|
19074
19298
|
} catch (e) {
|
|
19075
19299
|
console.warn("[deploy-app] Cleanup warning:", e);
|
|
19076
19300
|
}
|
|
@@ -19083,7 +19307,7 @@ async function deployApp(args) {
|
|
|
19083
19307
|
appName
|
|
19084
19308
|
);
|
|
19085
19309
|
deployResult.factoraiManifest = manifest;
|
|
19086
|
-
deployResult.factoraiManifestPath =
|
|
19310
|
+
deployResult.factoraiManifestPath = path25.join(resolvedProjectDir, "factorai.sh.json");
|
|
19087
19311
|
}
|
|
19088
19312
|
console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
|
|
19089
19313
|
}
|
|
@@ -19097,147 +19321,6 @@ async function deployApp(args) {
|
|
|
19097
19321
|
}
|
|
19098
19322
|
}
|
|
19099
19323
|
|
|
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
19324
|
// src/app/agent/runtime/native_tool_catalog.ts
|
|
19242
19325
|
init_sandbox_policy();
|
|
19243
19326
|
function getFactorAiBaseUrl() {
|
|
@@ -19389,7 +19472,7 @@ function getFactorAiSandboxToolDefinitions() {
|
|
|
19389
19472
|
type: "function",
|
|
19390
19473
|
function: {
|
|
19391
19474
|
name: "factorai.sh.deploy_app",
|
|
19392
|
-
description: "
|
|
19475
|
+
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
19476
|
parameters: {
|
|
19394
19477
|
type: "object",
|
|
19395
19478
|
properties: {
|
|
@@ -19407,7 +19490,7 @@ function getFactorAiSandboxToolDefinitions() {
|
|
|
19407
19490
|
type: "function",
|
|
19408
19491
|
function: {
|
|
19409
19492
|
name: "factorai.sh.get_app_status",
|
|
19410
|
-
description: "
|
|
19493
|
+
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
19494
|
parameters: {
|
|
19412
19495
|
type: "object",
|
|
19413
19496
|
properties: {
|
|
@@ -19422,7 +19505,7 @@ function getFactorAiSandboxToolDefinitions() {
|
|
|
19422
19505
|
type: "function",
|
|
19423
19506
|
function: {
|
|
19424
19507
|
name: "factorai.sh.apply_app_changes",
|
|
19425
|
-
description: "
|
|
19508
|
+
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
19509
|
parameters: {
|
|
19427
19510
|
type: "object",
|
|
19428
19511
|
properties: {
|
|
@@ -20086,7 +20169,7 @@ var NATIVE_TOOL_ENTRIES = [
|
|
|
20086
20169
|
autoApproveInLocal: false,
|
|
20087
20170
|
autoApproveInSandbox: true,
|
|
20088
20171
|
sandboxOnly: true,
|
|
20089
|
-
description: "
|
|
20172
|
+
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
20173
|
},
|
|
20091
20174
|
implementation: factorAiApplyAppChanges
|
|
20092
20175
|
},
|
|
@@ -20145,7 +20228,7 @@ var SANDBOX_COMMUNICATION_TOOL_DEFINITIONS = [
|
|
|
20145
20228
|
type: "function",
|
|
20146
20229
|
function: {
|
|
20147
20230
|
name: "message",
|
|
20148
|
-
description: 'Deliver user-visible output. message_type "info": progress (does not end turn). message_type "result": ends turn; file deliverables
|
|
20231
|
+
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
20232
|
parameters: {
|
|
20150
20233
|
type: "object",
|
|
20151
20234
|
properties: {
|
|
@@ -20159,6 +20242,10 @@ var SANDBOX_COMMUNICATION_TOOL_DEFINITIONS = [
|
|
|
20159
20242
|
type: "array",
|
|
20160
20243
|
items: { type: "string" },
|
|
20161
20244
|
description: "Workspace file paths under .bluma/artifacts/ (required for file deliverables on result)."
|
|
20245
|
+
},
|
|
20246
|
+
"factor-sh-url-app": {
|
|
20247
|
+
type: "string",
|
|
20248
|
+
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
20249
|
}
|
|
20163
20250
|
},
|
|
20164
20251
|
required: ["content", "message_type"],
|
|
@@ -20628,17 +20715,17 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
|
|
|
20628
20715
|
};
|
|
20629
20716
|
|
|
20630
20717
|
// src/app/agent/bluma/core/bluma.ts
|
|
20631
|
-
import
|
|
20632
|
-
import
|
|
20718
|
+
import path43 from "path";
|
|
20719
|
+
import fs40 from "fs";
|
|
20633
20720
|
import { v4 as uuidv48 } from "uuid";
|
|
20634
20721
|
|
|
20635
20722
|
// src/app/agent/session_manager/session_manager.ts
|
|
20636
|
-
import
|
|
20637
|
-
import { promises as
|
|
20723
|
+
import path31 from "path";
|
|
20724
|
+
import { promises as fs29 } from "fs";
|
|
20638
20725
|
|
|
20639
20726
|
// src/app/agent/session_manager/agent_session_paths.ts
|
|
20640
|
-
import
|
|
20641
|
-
import { promises as
|
|
20727
|
+
import path30 from "path";
|
|
20728
|
+
import { promises as fs28 } from "fs";
|
|
20642
20729
|
|
|
20643
20730
|
// src/app/agent/session_manager/bluma_app_dir.ts
|
|
20644
20731
|
import path28 from "path";
|
|
@@ -20659,24 +20746,268 @@ function getPreferredAppDir() {
|
|
|
20659
20746
|
return path28.resolve(expandHome(fixed));
|
|
20660
20747
|
}
|
|
20661
20748
|
|
|
20662
|
-
// src/app/agent/session_manager/
|
|
20749
|
+
// src/app/agent/session_manager/session_index_db.ts
|
|
20750
|
+
import path29 from "path";
|
|
20751
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
20752
|
+
import { promises as fs27 } from "fs";
|
|
20753
|
+
import { createRequire } from "module";
|
|
20663
20754
|
var AGENT_SESSION_PATHS_JSONL = "agent_session_paths.jsonl";
|
|
20755
|
+
var BLUMA_SESSION_DB_FILE = "bluma.sqlite";
|
|
20756
|
+
var SCHEMA_VERSION = 1;
|
|
20757
|
+
var dbInstance = null;
|
|
20758
|
+
var dbAppDir = null;
|
|
20759
|
+
var migratePromise = null;
|
|
20760
|
+
function loadSqlite() {
|
|
20761
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
20762
|
+
return nodeRequire("better-sqlite3");
|
|
20763
|
+
}
|
|
20764
|
+
function getSessionDbPath(appDir = getPreferredAppDir()) {
|
|
20765
|
+
return path29.join(appDir, BLUMA_SESSION_DB_FILE);
|
|
20766
|
+
}
|
|
20767
|
+
function normalizeRelativePath(relativePath) {
|
|
20768
|
+
return relativePath.split(path29.sep).join("/");
|
|
20769
|
+
}
|
|
20770
|
+
async function walkSessionJsonFiles(dir, onFile) {
|
|
20771
|
+
let entries;
|
|
20772
|
+
try {
|
|
20773
|
+
entries = await fs27.readdir(dir, { withFileTypes: true });
|
|
20774
|
+
} catch {
|
|
20775
|
+
return;
|
|
20776
|
+
}
|
|
20777
|
+
for (const e of entries) {
|
|
20778
|
+
const name = String(e.name);
|
|
20779
|
+
const full = path29.join(dir, name);
|
|
20780
|
+
if (e.isDirectory()) {
|
|
20781
|
+
await walkSessionJsonFiles(full, onFile);
|
|
20782
|
+
} else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
|
|
20783
|
+
await onFile(full);
|
|
20784
|
+
}
|
|
20785
|
+
}
|
|
20786
|
+
}
|
|
20787
|
+
async function loadJsonlEntries(appDir) {
|
|
20788
|
+
const map = /* @__PURE__ */ new Map();
|
|
20789
|
+
const indexFile = path29.join(appDir, AGENT_SESSION_PATHS_JSONL);
|
|
20790
|
+
let raw;
|
|
20791
|
+
try {
|
|
20792
|
+
raw = await fs27.readFile(indexFile, "utf-8");
|
|
20793
|
+
} catch {
|
|
20794
|
+
return map;
|
|
20795
|
+
}
|
|
20796
|
+
for (const line of raw.split("\n")) {
|
|
20797
|
+
const trimmed = line.trim();
|
|
20798
|
+
if (!trimmed) continue;
|
|
20799
|
+
try {
|
|
20800
|
+
const row = JSON.parse(trimmed);
|
|
20801
|
+
if (row.sessionId && typeof row.relativePath === "string") {
|
|
20802
|
+
map.set(row.sessionId, {
|
|
20803
|
+
relativePath: normalizeRelativePath(row.relativePath),
|
|
20804
|
+
updatedAt: row.updatedAt ?? (/* @__PURE__ */ new Date(0)).toISOString()
|
|
20805
|
+
});
|
|
20806
|
+
}
|
|
20807
|
+
} catch {
|
|
20808
|
+
}
|
|
20809
|
+
}
|
|
20810
|
+
return map;
|
|
20811
|
+
}
|
|
20812
|
+
function previewFromConversationHistory(history) {
|
|
20813
|
+
for (const msg of history ?? []) {
|
|
20814
|
+
if (msg.role !== "user") continue;
|
|
20815
|
+
const c = msg.content;
|
|
20816
|
+
if (typeof c !== "string") continue;
|
|
20817
|
+
const t = c.trim();
|
|
20818
|
+
if (!t || t.startsWith("[SKILL:")) continue;
|
|
20819
|
+
const oneLine = t.replace(/\s+/g, " ");
|
|
20820
|
+
return oneLine.length > 72 ? `${oneLine.slice(0, 72)}\u2026` : oneLine;
|
|
20821
|
+
}
|
|
20822
|
+
return "(no messages)";
|
|
20823
|
+
}
|
|
20824
|
+
async function readSessionMetaFromJson(absPath) {
|
|
20825
|
+
const fallback = {
|
|
20826
|
+
preview: "(no messages)",
|
|
20827
|
+
createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
20828
|
+
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
20829
|
+
};
|
|
20830
|
+
try {
|
|
20831
|
+
const raw = await fs27.readFile(absPath, "utf-8");
|
|
20832
|
+
const data = JSON.parse(raw);
|
|
20833
|
+
const createdAt = data.created_at ?? fallback.createdAt;
|
|
20834
|
+
const updatedAt = data.last_updated ?? data.created_at ?? fallback.updatedAt;
|
|
20835
|
+
return {
|
|
20836
|
+
preview: previewFromConversationHistory(data.conversation_history),
|
|
20837
|
+
createdAt,
|
|
20838
|
+
updatedAt
|
|
20839
|
+
};
|
|
20840
|
+
} catch {
|
|
20841
|
+
try {
|
|
20842
|
+
const st = await fs27.stat(absPath);
|
|
20843
|
+
const iso = new Date(st.mtimeMs).toISOString();
|
|
20844
|
+
return { preview: fallback.preview, createdAt: iso, updatedAt: iso };
|
|
20845
|
+
} catch {
|
|
20846
|
+
return fallback;
|
|
20847
|
+
}
|
|
20848
|
+
}
|
|
20849
|
+
}
|
|
20850
|
+
async function runMigrationV1(db, appDir) {
|
|
20851
|
+
const jsonl = await loadJsonlEntries(appDir);
|
|
20852
|
+
const upsert = db.prepare(`
|
|
20853
|
+
INSERT INTO agent_sessions (session_id, relative_path, created_at, updated_at, preview)
|
|
20854
|
+
VALUES (@sessionId, @relativePath, @createdAt, @updatedAt, @preview)
|
|
20855
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
20856
|
+
relative_path = excluded.relative_path,
|
|
20857
|
+
updated_at = CASE
|
|
20858
|
+
WHEN excluded.updated_at > agent_sessions.updated_at THEN excluded.updated_at
|
|
20859
|
+
ELSE agent_sessions.updated_at
|
|
20860
|
+
END,
|
|
20861
|
+
preview = CASE
|
|
20862
|
+
WHEN excluded.updated_at > agent_sessions.updated_at THEN excluded.preview
|
|
20863
|
+
ELSE agent_sessions.preview
|
|
20864
|
+
END
|
|
20865
|
+
`);
|
|
20866
|
+
const insertFromPath = async (sessionId, absPath, jsonlUpdatedAt) => {
|
|
20867
|
+
let rel;
|
|
20868
|
+
try {
|
|
20869
|
+
rel = normalizeRelativePath(path29.relative(appDir, absPath));
|
|
20870
|
+
} catch {
|
|
20871
|
+
return;
|
|
20872
|
+
}
|
|
20873
|
+
const meta = await readSessionMetaFromJson(absPath);
|
|
20874
|
+
const updatedAt = jsonlUpdatedAt && Date.parse(jsonlUpdatedAt) > Date.parse(meta.updatedAt) ? jsonlUpdatedAt : meta.updatedAt;
|
|
20875
|
+
upsert.run({
|
|
20876
|
+
sessionId,
|
|
20877
|
+
relativePath: rel,
|
|
20878
|
+
createdAt: meta.createdAt,
|
|
20879
|
+
updatedAt,
|
|
20880
|
+
preview: meta.preview
|
|
20881
|
+
});
|
|
20882
|
+
};
|
|
20883
|
+
for (const [sessionId, entry] of jsonl) {
|
|
20884
|
+
const full = path29.join(appDir, entry.relativePath.replace(/\//g, path29.sep));
|
|
20885
|
+
try {
|
|
20886
|
+
await fs27.access(full);
|
|
20887
|
+
await insertFromPath(sessionId, full, entry.updatedAt);
|
|
20888
|
+
} catch {
|
|
20889
|
+
upsert.run({
|
|
20890
|
+
sessionId,
|
|
20891
|
+
relativePath: entry.relativePath,
|
|
20892
|
+
createdAt: entry.updatedAt,
|
|
20893
|
+
updatedAt: entry.updatedAt,
|
|
20894
|
+
preview: "(no messages)"
|
|
20895
|
+
});
|
|
20896
|
+
}
|
|
20897
|
+
}
|
|
20898
|
+
const sessionsRoot = path29.join(appDir, "sessions");
|
|
20899
|
+
const existing = db.prepare("SELECT session_id FROM agent_sessions").all();
|
|
20900
|
+
const seen = new Set(existing.map((r) => r.session_id));
|
|
20901
|
+
await walkSessionJsonFiles(sessionsRoot, async (absPath) => {
|
|
20902
|
+
const sessionId = path29.basename(absPath, ".json");
|
|
20903
|
+
if (!sessionId || seen.has(sessionId)) return;
|
|
20904
|
+
seen.add(sessionId);
|
|
20905
|
+
await insertFromPath(sessionId, absPath);
|
|
20906
|
+
});
|
|
20907
|
+
db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
20908
|
+
}
|
|
20909
|
+
function applySchema(db) {
|
|
20910
|
+
db.exec(`
|
|
20911
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
20912
|
+
session_id TEXT PRIMARY KEY NOT NULL,
|
|
20913
|
+
relative_path TEXT NOT NULL,
|
|
20914
|
+
created_at TEXT NOT NULL,
|
|
20915
|
+
updated_at TEXT NOT NULL,
|
|
20916
|
+
preview TEXT NOT NULL DEFAULT '(no messages)'
|
|
20917
|
+
);
|
|
20918
|
+
CREATE INDEX IF NOT EXISTS idx_agent_sessions_updated
|
|
20919
|
+
ON agent_sessions(updated_at DESC);
|
|
20920
|
+
`);
|
|
20921
|
+
}
|
|
20922
|
+
async function ensureSessionIndexDb(appDir = getPreferredAppDir()) {
|
|
20923
|
+
if (dbInstance && dbAppDir === appDir) {
|
|
20924
|
+
return dbInstance;
|
|
20925
|
+
}
|
|
20926
|
+
if (!migratePromise || dbAppDir !== appDir) {
|
|
20927
|
+
migratePromise = (async () => {
|
|
20928
|
+
if (dbInstance) {
|
|
20929
|
+
try {
|
|
20930
|
+
dbInstance.close();
|
|
20931
|
+
} catch {
|
|
20932
|
+
}
|
|
20933
|
+
dbInstance = null;
|
|
20934
|
+
}
|
|
20935
|
+
const BetterSqlite3 = loadSqlite();
|
|
20936
|
+
const dbPath = getSessionDbPath(appDir);
|
|
20937
|
+
mkdirSync3(path29.dirname(dbPath), { recursive: true });
|
|
20938
|
+
const db = new BetterSqlite3(dbPath);
|
|
20939
|
+
db.pragma("journal_mode = WAL");
|
|
20940
|
+
applySchema(db);
|
|
20941
|
+
const version = db.pragma("user_version", { simple: true });
|
|
20942
|
+
if (version < SCHEMA_VERSION) {
|
|
20943
|
+
await runMigrationV1(db, appDir);
|
|
20944
|
+
}
|
|
20945
|
+
dbInstance = db;
|
|
20946
|
+
dbAppDir = appDir;
|
|
20947
|
+
})();
|
|
20948
|
+
}
|
|
20949
|
+
await migratePromise;
|
|
20950
|
+
return dbInstance;
|
|
20951
|
+
}
|
|
20952
|
+
async function upsertSessionIndexRow(row, appDir = getPreferredAppDir()) {
|
|
20953
|
+
const db = await ensureSessionIndexDb(appDir);
|
|
20954
|
+
const stmt = db.prepare(`
|
|
20955
|
+
INSERT INTO agent_sessions (session_id, relative_path, created_at, updated_at, preview)
|
|
20956
|
+
VALUES (@sessionId, @relativePath, @createdAt, @updatedAt, @preview)
|
|
20957
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
20958
|
+
relative_path = excluded.relative_path,
|
|
20959
|
+
updated_at = excluded.updated_at,
|
|
20960
|
+
preview = excluded.preview
|
|
20961
|
+
`);
|
|
20962
|
+
stmt.run({
|
|
20963
|
+
sessionId: row.sessionId,
|
|
20964
|
+
relativePath: normalizeRelativePath(row.relativePath),
|
|
20965
|
+
createdAt: row.createdAt,
|
|
20966
|
+
updatedAt: row.updatedAt,
|
|
20967
|
+
preview: row.preview
|
|
20968
|
+
});
|
|
20969
|
+
}
|
|
20970
|
+
async function getSessionPathFromIndex(sessionId, appDir = getPreferredAppDir()) {
|
|
20971
|
+
const db = await ensureSessionIndexDb(appDir);
|
|
20972
|
+
const row = db.prepare("SELECT relative_path FROM agent_sessions WHERE session_id = ?").get(sessionId);
|
|
20973
|
+
return row?.relative_path?.replace(/\//g, path29.sep) ?? null;
|
|
20974
|
+
}
|
|
20975
|
+
async function listSessionsFromIndex(limit = 50, appDir = getPreferredAppDir()) {
|
|
20976
|
+
const db = await ensureSessionIndexDb(appDir);
|
|
20977
|
+
const rows = db.prepare(
|
|
20978
|
+
`SELECT session_id, relative_path, created_at, updated_at, preview
|
|
20979
|
+
FROM agent_sessions
|
|
20980
|
+
ORDER BY updated_at DESC
|
|
20981
|
+
LIMIT ?`
|
|
20982
|
+
).all(limit);
|
|
20983
|
+
return rows.map((r) => ({
|
|
20984
|
+
sessionId: r.session_id,
|
|
20985
|
+
relativePath: r.relative_path.replace(/\//g, path29.sep),
|
|
20986
|
+
createdAt: r.created_at,
|
|
20987
|
+
updatedAt: r.updated_at,
|
|
20988
|
+
preview: r.preview
|
|
20989
|
+
}));
|
|
20990
|
+
}
|
|
20991
|
+
|
|
20992
|
+
// src/app/agent/session_manager/agent_session_paths.ts
|
|
20993
|
+
var AGENT_SESSION_PATHS_JSONL2 = "agent_session_paths.jsonl";
|
|
20664
20994
|
async function appendAgentSessionPath(sessionId, relativePath) {
|
|
20665
20995
|
const app = getPreferredAppDir();
|
|
20666
|
-
const
|
|
20667
|
-
|
|
20668
|
-
|
|
20996
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
20997
|
+
const rel = relativePath.split(path30.sep).join("/");
|
|
20998
|
+
await upsertSessionIndexRow({
|
|
20669
20999
|
sessionId,
|
|
20670
|
-
relativePath:
|
|
20671
|
-
|
|
20672
|
-
|
|
20673
|
-
|
|
21000
|
+
relativePath: rel,
|
|
21001
|
+
createdAt: now2,
|
|
21002
|
+
updatedAt: now2,
|
|
21003
|
+
preview: "(no messages)"
|
|
21004
|
+
});
|
|
20674
21005
|
}
|
|
20675
|
-
async function
|
|
20676
|
-
const indexFile =
|
|
21006
|
+
async function resolveSessionRelativePathFromJsonl(sessionId) {
|
|
21007
|
+
const indexFile = path30.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
|
|
20677
21008
|
let raw;
|
|
20678
21009
|
try {
|
|
20679
|
-
raw = await
|
|
21010
|
+
raw = await fs28.readFile(indexFile, "utf-8");
|
|
20680
21011
|
} catch {
|
|
20681
21012
|
return null;
|
|
20682
21013
|
}
|
|
@@ -20685,37 +21016,42 @@ async function resolveSessionRelativePathFromIndex(sessionId) {
|
|
|
20685
21016
|
try {
|
|
20686
21017
|
const row = JSON.parse(lines[i]);
|
|
20687
21018
|
if (row.sessionId === sessionId && typeof row.relativePath === "string") {
|
|
20688
|
-
return row.relativePath.replace(/\//g,
|
|
21019
|
+
return row.relativePath.replace(/\//g, path30.sep);
|
|
20689
21020
|
}
|
|
20690
21021
|
} catch {
|
|
20691
21022
|
}
|
|
20692
21023
|
}
|
|
20693
21024
|
return null;
|
|
20694
21025
|
}
|
|
20695
|
-
async function
|
|
21026
|
+
async function resolveSessionRelativePathFromIndex(sessionId) {
|
|
21027
|
+
const fromDb = await getSessionPathFromIndex(sessionId);
|
|
21028
|
+
if (fromDb) return fromDb;
|
|
21029
|
+
return resolveSessionRelativePathFromJsonl(sessionId);
|
|
21030
|
+
}
|
|
21031
|
+
async function walkSessionJsonFiles2(dir, onFile) {
|
|
20696
21032
|
let entries;
|
|
20697
21033
|
try {
|
|
20698
|
-
entries = await
|
|
21034
|
+
entries = await fs28.readdir(dir, { withFileTypes: true });
|
|
20699
21035
|
} catch {
|
|
20700
21036
|
return;
|
|
20701
21037
|
}
|
|
20702
21038
|
for (const e of entries) {
|
|
20703
21039
|
const name = String(e.name);
|
|
20704
|
-
const full =
|
|
21040
|
+
const full = path30.join(dir, name);
|
|
20705
21041
|
if (e.isDirectory()) {
|
|
20706
|
-
await
|
|
21042
|
+
await walkSessionJsonFiles2(full, onFile);
|
|
20707
21043
|
} else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
|
|
20708
21044
|
await onFile(full);
|
|
20709
21045
|
}
|
|
20710
21046
|
}
|
|
20711
21047
|
}
|
|
20712
21048
|
async function findSessionFileGlobFallback(sessionId) {
|
|
20713
|
-
const sessionsRoot =
|
|
21049
|
+
const sessionsRoot = path30.join(getPreferredAppDir(), "sessions");
|
|
20714
21050
|
const state2 = { best: null };
|
|
20715
|
-
await
|
|
20716
|
-
if (
|
|
21051
|
+
await walkSessionJsonFiles2(sessionsRoot, async (abs) => {
|
|
21052
|
+
if (path30.basename(abs, ".json") !== sessionId) return;
|
|
20717
21053
|
try {
|
|
20718
|
-
const st = await
|
|
21054
|
+
const st = await fs28.stat(abs);
|
|
20719
21055
|
if (!st.isFile()) return;
|
|
20720
21056
|
if (!state2.best || st.mtimeMs > state2.best.m) {
|
|
20721
21057
|
state2.best = { p: abs, m: st.mtimeMs };
|
|
@@ -20726,11 +21062,11 @@ async function findSessionFileGlobFallback(sessionId) {
|
|
|
20726
21062
|
return state2.best !== null ? state2.best.p : null;
|
|
20727
21063
|
}
|
|
20728
21064
|
async function loadLatestIndexEntriesBySessionId() {
|
|
20729
|
-
const indexFile =
|
|
21065
|
+
const indexFile = path30.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
|
|
20730
21066
|
const map = /* @__PURE__ */ new Map();
|
|
20731
21067
|
let raw;
|
|
20732
21068
|
try {
|
|
20733
|
-
raw = await
|
|
21069
|
+
raw = await fs28.readFile(indexFile, "utf-8");
|
|
20734
21070
|
} catch {
|
|
20735
21071
|
return map;
|
|
20736
21072
|
}
|
|
@@ -20763,40 +21099,36 @@ function dateFolderFromRelativePath(relativePath) {
|
|
|
20763
21099
|
}
|
|
20764
21100
|
return null;
|
|
20765
21101
|
}
|
|
20766
|
-
function
|
|
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) {
|
|
21102
|
+
async function enrichCandidateFromFile(absPath, sessionId, lastMtimeMs, opts) {
|
|
20779
21103
|
let rel;
|
|
20780
21104
|
try {
|
|
20781
|
-
rel =
|
|
21105
|
+
rel = path30.relative(getPreferredAppDir(), absPath);
|
|
20782
21106
|
} catch {
|
|
20783
21107
|
rel = absPath;
|
|
20784
21108
|
}
|
|
20785
|
-
const dateFolder = dateFolderFromRelativePath(rel.split(
|
|
20786
|
-
let preview = "(no messages)";
|
|
21109
|
+
const dateFolder = dateFolderFromRelativePath(rel.split(path30.sep).join("/"));
|
|
21110
|
+
let preview = opts?.preview ?? "(no messages)";
|
|
20787
21111
|
let lastActivityMs = lastMtimeMs;
|
|
20788
|
-
|
|
20789
|
-
const
|
|
20790
|
-
|
|
20791
|
-
|
|
20792
|
-
|
|
20793
|
-
|
|
20794
|
-
|
|
20795
|
-
|
|
20796
|
-
|
|
21112
|
+
if (opts?.updatedAtIso) {
|
|
21113
|
+
const parsed = Date.parse(opts.updatedAtIso);
|
|
21114
|
+
if (Number.isFinite(parsed)) {
|
|
21115
|
+
lastActivityMs = Math.max(lastMtimeMs, parsed);
|
|
21116
|
+
}
|
|
21117
|
+
}
|
|
21118
|
+
if (!opts?.preview || opts.preview === "(no messages)") {
|
|
21119
|
+
try {
|
|
21120
|
+
const raw = await fs28.readFile(absPath, "utf-8");
|
|
21121
|
+
const data = JSON.parse(raw);
|
|
21122
|
+
preview = previewFromConversationHistory(data.conversation_history);
|
|
21123
|
+
const iso = data.last_updated || data.created_at;
|
|
21124
|
+
if (iso) {
|
|
21125
|
+
const parsed = Date.parse(iso);
|
|
21126
|
+
if (Number.isFinite(parsed)) {
|
|
21127
|
+
lastActivityMs = Math.max(lastActivityMs, parsed);
|
|
21128
|
+
}
|
|
20797
21129
|
}
|
|
21130
|
+
} catch {
|
|
20798
21131
|
}
|
|
20799
|
-
} catch {
|
|
20800
21132
|
}
|
|
20801
21133
|
return {
|
|
20802
21134
|
sessionId,
|
|
@@ -20809,13 +21141,29 @@ async function enrichCandidateFromFile(absPath, sessionId, lastMtimeMs) {
|
|
|
20809
21141
|
}
|
|
20810
21142
|
async function listAgentSessionsForResume(limit = 50) {
|
|
20811
21143
|
const appDir = getPreferredAppDir();
|
|
20812
|
-
|
|
21144
|
+
await ensureSessionIndexDb(appDir);
|
|
21145
|
+
const fromIndex = await listSessionsFromIndex(limit * 2, appDir);
|
|
20813
21146
|
const map = /* @__PURE__ */ new Map();
|
|
20814
|
-
|
|
20815
|
-
const
|
|
21147
|
+
for (const row of fromIndex) {
|
|
21148
|
+
const full = path30.join(appDir, row.relativePath);
|
|
21149
|
+
try {
|
|
21150
|
+
const st = await fs28.stat(full);
|
|
21151
|
+
if (!st.isFile()) continue;
|
|
21152
|
+
map.set(row.sessionId, {
|
|
21153
|
+
path: full,
|
|
21154
|
+
lastMtimeMs: st.mtimeMs,
|
|
21155
|
+
preview: row.preview,
|
|
21156
|
+
updatedAt: row.updatedAt
|
|
21157
|
+
});
|
|
21158
|
+
} catch {
|
|
21159
|
+
}
|
|
21160
|
+
}
|
|
21161
|
+
const sessionsRoot = path30.join(appDir, "sessions");
|
|
21162
|
+
await walkSessionJsonFiles2(sessionsRoot, async (absPath) => {
|
|
21163
|
+
const id = path30.basename(absPath, ".json");
|
|
20816
21164
|
if (!id) return;
|
|
20817
21165
|
try {
|
|
20818
|
-
const st = await
|
|
21166
|
+
const st = await fs28.stat(absPath);
|
|
20819
21167
|
if (!st.isFile()) return;
|
|
20820
21168
|
const cur = map.get(id);
|
|
20821
21169
|
if (!cur || st.mtimeMs > cur.lastMtimeMs) {
|
|
@@ -20827,9 +21175,9 @@ async function listAgentSessionsForResume(limit = 50) {
|
|
|
20827
21175
|
const index = await loadLatestIndexEntriesBySessionId();
|
|
20828
21176
|
for (const [sessionId, entry] of index) {
|
|
20829
21177
|
if (map.has(sessionId)) continue;
|
|
20830
|
-
const full =
|
|
21178
|
+
const full = path30.join(appDir, entry.relativePath.replace(/\//g, path30.sep));
|
|
20831
21179
|
try {
|
|
20832
|
-
const st = await
|
|
21180
|
+
const st = await fs28.stat(full);
|
|
20833
21181
|
if (st.isFile()) {
|
|
20834
21182
|
map.set(sessionId, { path: full, lastMtimeMs: st.mtimeMs });
|
|
20835
21183
|
}
|
|
@@ -20838,7 +21186,10 @@ async function listAgentSessionsForResume(limit = 50) {
|
|
|
20838
21186
|
}
|
|
20839
21187
|
const enriched = await Promise.all(
|
|
20840
21188
|
[...map.entries()].map(
|
|
20841
|
-
([sessionId, v]) => enrichCandidateFromFile(v.path, sessionId, v.lastMtimeMs
|
|
21189
|
+
([sessionId, v]) => enrichCandidateFromFile(v.path, sessionId, v.lastMtimeMs, {
|
|
21190
|
+
preview: v.preview,
|
|
21191
|
+
updatedAtIso: v.updatedAt
|
|
21192
|
+
})
|
|
20842
21193
|
)
|
|
20843
21194
|
);
|
|
20844
21195
|
return enriched.sort((a, b) => b.lastActivityMs - a.lastActivityMs).slice(0, limit);
|
|
@@ -20883,10 +21234,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
20883
21234
|
const isWin = process.platform === "win32";
|
|
20884
21235
|
while (attempt <= maxRetries) {
|
|
20885
21236
|
try {
|
|
20886
|
-
const dir =
|
|
20887
|
-
await
|
|
21237
|
+
const dir = path31.dirname(dest);
|
|
21238
|
+
await fs29.mkdir(dir, { recursive: true }).catch(() => {
|
|
20888
21239
|
});
|
|
20889
|
-
await
|
|
21240
|
+
await fs29.rename(src, dest);
|
|
20890
21241
|
return;
|
|
20891
21242
|
} catch (e) {
|
|
20892
21243
|
lastErr = e;
|
|
@@ -20899,13 +21250,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
20899
21250
|
}
|
|
20900
21251
|
}
|
|
20901
21252
|
try {
|
|
20902
|
-
await
|
|
20903
|
-
const data = await
|
|
20904
|
-
const dir =
|
|
20905
|
-
await
|
|
21253
|
+
await fs29.access(src);
|
|
21254
|
+
const data = await fs29.readFile(src);
|
|
21255
|
+
const dir = path31.dirname(dest);
|
|
21256
|
+
await fs29.mkdir(dir, { recursive: true }).catch(() => {
|
|
20906
21257
|
});
|
|
20907
|
-
await
|
|
20908
|
-
await
|
|
21258
|
+
await fs29.writeFile(dest, data);
|
|
21259
|
+
await fs29.unlink(src).catch(() => {
|
|
20909
21260
|
});
|
|
20910
21261
|
return;
|
|
20911
21262
|
} catch (fallbackErr) {
|
|
@@ -20918,23 +21269,23 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
20918
21269
|
}
|
|
20919
21270
|
async function ensureSessionDir() {
|
|
20920
21271
|
const appDir = getPreferredAppDir();
|
|
20921
|
-
const sessionDir =
|
|
20922
|
-
await
|
|
21272
|
+
const sessionDir = path31.join(appDir, "sessions");
|
|
21273
|
+
await fs29.mkdir(sessionDir, { recursive: true });
|
|
20923
21274
|
return sessionDir;
|
|
20924
21275
|
}
|
|
20925
21276
|
async function resolveAgentSessionFilePath(sessionId) {
|
|
20926
21277
|
const appDir = getPreferredAppDir();
|
|
20927
|
-
const legacy =
|
|
21278
|
+
const legacy = path31.join(appDir, "sessions", `${sessionId}.json`);
|
|
20928
21279
|
try {
|
|
20929
|
-
await
|
|
21280
|
+
await fs29.access(legacy);
|
|
20930
21281
|
return legacy;
|
|
20931
21282
|
} catch {
|
|
20932
21283
|
}
|
|
20933
21284
|
const rel = await resolveSessionRelativePathFromIndex(sessionId);
|
|
20934
21285
|
if (rel) {
|
|
20935
|
-
const full =
|
|
21286
|
+
const full = path31.join(appDir, rel);
|
|
20936
21287
|
try {
|
|
20937
|
-
await
|
|
21288
|
+
await fs29.access(full);
|
|
20938
21289
|
return full;
|
|
20939
21290
|
} catch {
|
|
20940
21291
|
}
|
|
@@ -20947,7 +21298,7 @@ async function loadSession(sessionId) {
|
|
|
20947
21298
|
return null;
|
|
20948
21299
|
}
|
|
20949
21300
|
try {
|
|
20950
|
-
const fileContent = await
|
|
21301
|
+
const fileContent = await fs29.readFile(sessionFile, "utf-8");
|
|
20951
21302
|
const sessionData = JSON.parse(fileContent);
|
|
20952
21303
|
const memory = {
|
|
20953
21304
|
historyAnchor: sessionData.history_anchor ?? null,
|
|
@@ -20968,16 +21319,16 @@ async function loadOrcreateSession(sessionId) {
|
|
|
20968
21319
|
const y = String(now2.getFullYear());
|
|
20969
21320
|
const mo = String(now2.getMonth() + 1).padStart(2, "0");
|
|
20970
21321
|
const d = String(now2.getDate()).padStart(2, "0");
|
|
20971
|
-
const datedDir =
|
|
20972
|
-
await
|
|
20973
|
-
const sessionFile =
|
|
21322
|
+
const datedDir = path31.join(sessionsRoot, y, mo, d);
|
|
21323
|
+
await fs29.mkdir(datedDir, { recursive: true });
|
|
21324
|
+
const sessionFile = path31.join(datedDir, `${sessionId}.json`);
|
|
20974
21325
|
const newSessionData = {
|
|
20975
21326
|
session_id: sessionId,
|
|
20976
21327
|
created_at: now2.toISOString(),
|
|
20977
21328
|
conversation_history: []
|
|
20978
21329
|
};
|
|
20979
|
-
await
|
|
20980
|
-
const relToApp =
|
|
21330
|
+
await fs29.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
21331
|
+
const relToApp = path31.relative(getPreferredAppDir(), sessionFile);
|
|
20981
21332
|
await appendAgentSessionPath(sessionId, relToApp);
|
|
20982
21333
|
const emptyMemory = {
|
|
20983
21334
|
historyAnchor: null,
|
|
@@ -20989,12 +21340,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
20989
21340
|
await withFileLock(sessionFile, async () => {
|
|
20990
21341
|
let sessionData;
|
|
20991
21342
|
try {
|
|
20992
|
-
const dir =
|
|
20993
|
-
await
|
|
21343
|
+
const dir = path31.dirname(sessionFile);
|
|
21344
|
+
await fs29.mkdir(dir, { recursive: true });
|
|
20994
21345
|
} catch {
|
|
20995
21346
|
}
|
|
20996
21347
|
try {
|
|
20997
|
-
const fileContent = await
|
|
21348
|
+
const fileContent = await fs29.readFile(sessionFile, "utf-8");
|
|
20998
21349
|
sessionData = JSON.parse(fileContent);
|
|
20999
21350
|
} catch (error) {
|
|
21000
21351
|
const code = error && error.code;
|
|
@@ -21005,14 +21356,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
21005
21356
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
21006
21357
|
}
|
|
21007
21358
|
}
|
|
21008
|
-
const sessionId =
|
|
21359
|
+
const sessionId = path31.basename(sessionFile, ".json");
|
|
21009
21360
|
sessionData = {
|
|
21010
21361
|
session_id: sessionId,
|
|
21011
21362
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21012
21363
|
conversation_history: []
|
|
21013
21364
|
};
|
|
21014
21365
|
try {
|
|
21015
|
-
await
|
|
21366
|
+
await fs29.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
21016
21367
|
} catch {
|
|
21017
21368
|
}
|
|
21018
21369
|
}
|
|
@@ -21028,8 +21379,26 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
21028
21379
|
}
|
|
21029
21380
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
21030
21381
|
try {
|
|
21031
|
-
await
|
|
21382
|
+
await fs29.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
21032
21383
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
21384
|
+
const sessionId = sessionData.session_id ?? path31.basename(sessionFile, ".json");
|
|
21385
|
+
const updatedAt = sessionData.last_updated ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
21386
|
+
const createdAt = sessionData.created_at ?? updatedAt;
|
|
21387
|
+
let relToApp;
|
|
21388
|
+
try {
|
|
21389
|
+
relToApp = path31.relative(getPreferredAppDir(), sessionFile);
|
|
21390
|
+
} catch {
|
|
21391
|
+
relToApp = sessionFile;
|
|
21392
|
+
}
|
|
21393
|
+
await upsertSessionIndexRow({
|
|
21394
|
+
sessionId,
|
|
21395
|
+
relativePath: relToApp,
|
|
21396
|
+
createdAt,
|
|
21397
|
+
updatedAt,
|
|
21398
|
+
preview: previewFromConversationHistory(
|
|
21399
|
+
history
|
|
21400
|
+
)
|
|
21401
|
+
});
|
|
21033
21402
|
} catch (writeError) {
|
|
21034
21403
|
if (writeError instanceof Error) {
|
|
21035
21404
|
console.error(`Fatal error saving session to ${sessionFile}: ${writeError.message}`);
|
|
@@ -21037,7 +21406,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
21037
21406
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
21038
21407
|
}
|
|
21039
21408
|
try {
|
|
21040
|
-
await
|
|
21409
|
+
await fs29.unlink(tempSessionFile);
|
|
21041
21410
|
} catch {
|
|
21042
21411
|
}
|
|
21043
21412
|
}
|
|
@@ -21052,16 +21421,30 @@ async function saveSessionHistory(sessionFile, history, memory) {
|
|
|
21052
21421
|
});
|
|
21053
21422
|
debouncedSave(sessionFile, cleanHistory, memory);
|
|
21054
21423
|
}
|
|
21424
|
+
async function saveSessionHistoryNow(sessionFile, history, memory) {
|
|
21425
|
+
const pending = pendingSaves.get(sessionFile);
|
|
21426
|
+
if (pending) {
|
|
21427
|
+
clearTimeout(pending.timer);
|
|
21428
|
+
pendingSaves.delete(sessionFile);
|
|
21429
|
+
}
|
|
21430
|
+
const cleanHistory = history.filter((msg) => {
|
|
21431
|
+
if (msg.role === "user" && typeof msg.content === "string") {
|
|
21432
|
+
return !msg.content.startsWith("[SKILL:");
|
|
21433
|
+
}
|
|
21434
|
+
return true;
|
|
21435
|
+
});
|
|
21436
|
+
await doSaveSessionHistory(sessionFile, cleanHistory, memory);
|
|
21437
|
+
}
|
|
21055
21438
|
|
|
21056
21439
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
21057
21440
|
import os24 from "os";
|
|
21058
|
-
import
|
|
21059
|
-
import
|
|
21441
|
+
import fs36 from "fs";
|
|
21442
|
+
import path38 from "path";
|
|
21060
21443
|
import { execSync as execSync3 } from "child_process";
|
|
21061
21444
|
|
|
21062
21445
|
// src/app/agent/skills/skill_loader.ts
|
|
21063
|
-
import
|
|
21064
|
-
import
|
|
21446
|
+
import fs30 from "fs";
|
|
21447
|
+
import path32 from "path";
|
|
21065
21448
|
import os20 from "os";
|
|
21066
21449
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
21067
21450
|
var SkillLoader = class _SkillLoader {
|
|
@@ -21071,8 +21454,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
21071
21454
|
cache = /* @__PURE__ */ new Map();
|
|
21072
21455
|
conflicts = [];
|
|
21073
21456
|
constructor(projectRoot, bundledDir) {
|
|
21074
|
-
this.projectSkillsDir =
|
|
21075
|
-
this.globalSkillsDir =
|
|
21457
|
+
this.projectSkillsDir = path32.join(projectRoot, ".bluma", "skills");
|
|
21458
|
+
this.globalSkillsDir = path32.join(os20.homedir(), ".bluma", "skills");
|
|
21076
21459
|
this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
|
|
21077
21460
|
}
|
|
21078
21461
|
/**
|
|
@@ -21081,48 +21464,48 @@ var SkillLoader = class _SkillLoader {
|
|
|
21081
21464
|
*/
|
|
21082
21465
|
static resolveBundledDir() {
|
|
21083
21466
|
if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
|
|
21084
|
-
return
|
|
21467
|
+
return path32.join(process.cwd(), "dist", "config", "skills");
|
|
21085
21468
|
}
|
|
21086
21469
|
const candidates = [];
|
|
21087
21470
|
const push = (p) => {
|
|
21088
|
-
const abs =
|
|
21471
|
+
const abs = path32.resolve(p);
|
|
21089
21472
|
if (!candidates.includes(abs)) {
|
|
21090
21473
|
candidates.push(abs);
|
|
21091
21474
|
}
|
|
21092
21475
|
};
|
|
21093
21476
|
let argvBundled = null;
|
|
21094
21477
|
try {
|
|
21095
|
-
const bundleDir =
|
|
21096
|
-
push(
|
|
21478
|
+
const bundleDir = path32.dirname(fileURLToPath3(import.meta.url));
|
|
21479
|
+
push(path32.join(bundleDir, "config", "skills"));
|
|
21097
21480
|
} catch {
|
|
21098
21481
|
}
|
|
21099
21482
|
const argv1 = process.argv[1];
|
|
21100
21483
|
if (argv1 && !argv1.startsWith("-")) {
|
|
21101
21484
|
try {
|
|
21102
21485
|
let resolved = argv1;
|
|
21103
|
-
if (
|
|
21104
|
-
resolved =
|
|
21105
|
-
} else if (!
|
|
21106
|
-
resolved =
|
|
21486
|
+
if (path32.isAbsolute(argv1) && fs30.existsSync(argv1)) {
|
|
21487
|
+
resolved = fs30.realpathSync(argv1);
|
|
21488
|
+
} else if (!path32.isAbsolute(argv1)) {
|
|
21489
|
+
resolved = path32.resolve(process.cwd(), argv1);
|
|
21107
21490
|
}
|
|
21108
|
-
const scriptDir =
|
|
21109
|
-
argvBundled =
|
|
21491
|
+
const scriptDir = path32.dirname(resolved);
|
|
21492
|
+
argvBundled = path32.join(scriptDir, "config", "skills");
|
|
21110
21493
|
push(argvBundled);
|
|
21111
21494
|
} catch {
|
|
21112
21495
|
}
|
|
21113
21496
|
}
|
|
21114
21497
|
for (const abs of candidates) {
|
|
21115
|
-
if (
|
|
21498
|
+
if (fs30.existsSync(abs)) {
|
|
21116
21499
|
return abs;
|
|
21117
21500
|
}
|
|
21118
21501
|
}
|
|
21119
21502
|
try {
|
|
21120
|
-
return
|
|
21503
|
+
return path32.join(path32.dirname(fileURLToPath3(import.meta.url)), "config", "skills");
|
|
21121
21504
|
} catch {
|
|
21122
21505
|
if (argvBundled) {
|
|
21123
21506
|
return argvBundled;
|
|
21124
21507
|
}
|
|
21125
|
-
return
|
|
21508
|
+
return path32.join(os20.homedir(), ".bluma", "__bundled_skills_unresolved__");
|
|
21126
21509
|
}
|
|
21127
21510
|
}
|
|
21128
21511
|
/**
|
|
@@ -21151,8 +21534,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
21151
21534
|
this.conflicts.push({
|
|
21152
21535
|
name: skill.name,
|
|
21153
21536
|
userSource: source,
|
|
21154
|
-
userPath:
|
|
21155
|
-
bundledPath:
|
|
21537
|
+
userPath: path32.join(dir, skill.name, "SKILL.md"),
|
|
21538
|
+
bundledPath: path32.join(this.bundledSkillsDir, skill.name, "SKILL.md")
|
|
21156
21539
|
});
|
|
21157
21540
|
continue;
|
|
21158
21541
|
}
|
|
@@ -21160,20 +21543,20 @@ var SkillLoader = class _SkillLoader {
|
|
|
21160
21543
|
}
|
|
21161
21544
|
}
|
|
21162
21545
|
listFromDir(dir, source) {
|
|
21163
|
-
if (!
|
|
21546
|
+
if (!fs30.existsSync(dir)) return [];
|
|
21164
21547
|
try {
|
|
21165
|
-
return
|
|
21166
|
-
const fullPath =
|
|
21167
|
-
return
|
|
21168
|
-
}).map((d) => this.loadMetadataFromPath(
|
|
21548
|
+
return fs30.readdirSync(dir).filter((d) => {
|
|
21549
|
+
const fullPath = path32.join(dir, d);
|
|
21550
|
+
return fs30.statSync(fullPath).isDirectory() && fs30.existsSync(path32.join(fullPath, "SKILL.md"));
|
|
21551
|
+
}).map((d) => this.loadMetadataFromPath(path32.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
|
|
21169
21552
|
} catch {
|
|
21170
21553
|
return [];
|
|
21171
21554
|
}
|
|
21172
21555
|
}
|
|
21173
21556
|
loadMetadataFromPath(skillPath, skillName, source) {
|
|
21174
|
-
if (!
|
|
21557
|
+
if (!fs30.existsSync(skillPath)) return null;
|
|
21175
21558
|
try {
|
|
21176
|
-
const raw =
|
|
21559
|
+
const raw = fs30.readFileSync(skillPath, "utf-8");
|
|
21177
21560
|
const parsed = this.parseFrontmatter(raw);
|
|
21178
21561
|
return {
|
|
21179
21562
|
name: parsed.name || skillName,
|
|
@@ -21195,12 +21578,12 @@ var SkillLoader = class _SkillLoader {
|
|
|
21195
21578
|
*/
|
|
21196
21579
|
load(name) {
|
|
21197
21580
|
if (this.cache.has(name)) return this.cache.get(name);
|
|
21198
|
-
const bundledPath =
|
|
21199
|
-
const projectPath =
|
|
21200
|
-
const globalPath =
|
|
21201
|
-
const existsBundled =
|
|
21202
|
-
const existsProject =
|
|
21203
|
-
const existsGlobal =
|
|
21581
|
+
const bundledPath = path32.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
21582
|
+
const projectPath = path32.join(this.projectSkillsDir, name, "SKILL.md");
|
|
21583
|
+
const globalPath = path32.join(this.globalSkillsDir, name, "SKILL.md");
|
|
21584
|
+
const existsBundled = fs30.existsSync(bundledPath);
|
|
21585
|
+
const existsProject = fs30.existsSync(projectPath);
|
|
21586
|
+
const existsGlobal = fs30.existsSync(globalPath);
|
|
21204
21587
|
if (existsBundled && (existsProject || existsGlobal)) {
|
|
21205
21588
|
const conflictSource = existsProject ? "project" : "global";
|
|
21206
21589
|
const conflictPath = existsProject ? projectPath : globalPath;
|
|
@@ -21239,9 +21622,9 @@ var SkillLoader = class _SkillLoader {
|
|
|
21239
21622
|
}
|
|
21240
21623
|
loadFromPath(skillPath, name, source) {
|
|
21241
21624
|
try {
|
|
21242
|
-
const raw =
|
|
21625
|
+
const raw = fs30.readFileSync(skillPath, "utf-8");
|
|
21243
21626
|
const parsed = this.parseFrontmatter(raw);
|
|
21244
|
-
const skillDir =
|
|
21627
|
+
const skillDir = path32.dirname(skillPath);
|
|
21245
21628
|
return {
|
|
21246
21629
|
name: parsed.name || name,
|
|
21247
21630
|
description: parsed.description || "",
|
|
@@ -21250,22 +21633,22 @@ var SkillLoader = class _SkillLoader {
|
|
|
21250
21633
|
version: parsed.version,
|
|
21251
21634
|
author: parsed.author,
|
|
21252
21635
|
license: parsed.license,
|
|
21253
|
-
references: this.scanAssets(
|
|
21254
|
-
scripts: this.scanAssets(
|
|
21636
|
+
references: this.scanAssets(path32.join(skillDir, "references")),
|
|
21637
|
+
scripts: this.scanAssets(path32.join(skillDir, "scripts"))
|
|
21255
21638
|
};
|
|
21256
21639
|
} catch {
|
|
21257
21640
|
return null;
|
|
21258
21641
|
}
|
|
21259
21642
|
}
|
|
21260
21643
|
scanAssets(dir) {
|
|
21261
|
-
if (!
|
|
21644
|
+
if (!fs30.existsSync(dir)) return [];
|
|
21262
21645
|
try {
|
|
21263
|
-
return
|
|
21264
|
-
const fp =
|
|
21265
|
-
return
|
|
21646
|
+
return fs30.readdirSync(dir).filter((f) => {
|
|
21647
|
+
const fp = path32.join(dir, f);
|
|
21648
|
+
return fs30.statSync(fp).isFile();
|
|
21266
21649
|
}).map((f) => ({
|
|
21267
21650
|
name: f,
|
|
21268
|
-
path:
|
|
21651
|
+
path: path32.resolve(dir, f)
|
|
21269
21652
|
}));
|
|
21270
21653
|
} catch {
|
|
21271
21654
|
return [];
|
|
@@ -21322,10 +21705,10 @@ var SkillLoader = class _SkillLoader {
|
|
|
21322
21705
|
this.cache.clear();
|
|
21323
21706
|
}
|
|
21324
21707
|
exists(name) {
|
|
21325
|
-
const bundledPath =
|
|
21326
|
-
const projectPath =
|
|
21327
|
-
const globalPath =
|
|
21328
|
-
return
|
|
21708
|
+
const bundledPath = path32.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
21709
|
+
const projectPath = path32.join(this.projectSkillsDir, name, "SKILL.md");
|
|
21710
|
+
const globalPath = path32.join(this.globalSkillsDir, name, "SKILL.md");
|
|
21711
|
+
return fs30.existsSync(bundledPath) || fs30.existsSync(projectPath) || fs30.existsSync(globalPath);
|
|
21329
21712
|
}
|
|
21330
21713
|
/**
|
|
21331
21714
|
* Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
|
|
@@ -21357,13 +21740,13 @@ var SkillLoader = class _SkillLoader {
|
|
|
21357
21740
|
};
|
|
21358
21741
|
|
|
21359
21742
|
// src/app/agent/core/prompt/workspace_snapshot.ts
|
|
21360
|
-
import
|
|
21361
|
-
import
|
|
21743
|
+
import fs32 from "fs";
|
|
21744
|
+
import path34 from "path";
|
|
21362
21745
|
import { execSync as execSync2 } from "child_process";
|
|
21363
21746
|
|
|
21364
21747
|
// src/app/agent/utils/blumamd.ts
|
|
21365
|
-
import
|
|
21366
|
-
import
|
|
21748
|
+
import fs31 from "fs";
|
|
21749
|
+
import path33 from "path";
|
|
21367
21750
|
import os21 from "os";
|
|
21368
21751
|
import { execSync } from "child_process";
|
|
21369
21752
|
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 +21876,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
21493
21876
|
function expandIncludePath(includePath, baseDir) {
|
|
21494
21877
|
const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
|
|
21495
21878
|
if (cleanPath.startsWith("~")) {
|
|
21496
|
-
return
|
|
21879
|
+
return path33.join(os21.homedir(), cleanPath.slice(1));
|
|
21497
21880
|
}
|
|
21498
|
-
if (
|
|
21881
|
+
if (path33.isAbsolute(cleanPath)) {
|
|
21499
21882
|
return cleanPath;
|
|
21500
21883
|
}
|
|
21501
|
-
return
|
|
21884
|
+
return path33.resolve(baseDir, cleanPath);
|
|
21502
21885
|
}
|
|
21503
21886
|
function processIncludes(content, baseDir, processedFiles) {
|
|
21504
21887
|
const lines = content.split("\n");
|
|
@@ -21507,20 +21890,20 @@ function processIncludes(content, baseDir, processedFiles) {
|
|
|
21507
21890
|
const includeMatch = line.match(/^@\s*([^\s]+)/);
|
|
21508
21891
|
if (includeMatch) {
|
|
21509
21892
|
const includePath = expandIncludePath(includeMatch[1], baseDir);
|
|
21510
|
-
const normalizedPath =
|
|
21893
|
+
const normalizedPath = path33.normalize(includePath);
|
|
21511
21894
|
if (processedFiles.has(normalizedPath)) {
|
|
21512
21895
|
result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
|
|
21513
21896
|
continue;
|
|
21514
21897
|
}
|
|
21515
|
-
const ext =
|
|
21898
|
+
const ext = path33.extname(includePath).toLowerCase();
|
|
21516
21899
|
if (!TEXT_FILE_EXTENSIONS.has(ext)) {
|
|
21517
21900
|
result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
|
|
21518
21901
|
continue;
|
|
21519
21902
|
}
|
|
21520
21903
|
try {
|
|
21521
|
-
const includedContent =
|
|
21904
|
+
const includedContent = fs31.readFileSync(includePath, "utf-8");
|
|
21522
21905
|
processedFiles.add(normalizedPath);
|
|
21523
|
-
const processedContent = processIncludes(includedContent,
|
|
21906
|
+
const processedContent = processIncludes(includedContent, path33.dirname(includePath), processedFiles);
|
|
21524
21907
|
result.push(`
|
|
21525
21908
|
<!-- BEGIN INCLUDE ${includeMatch[1]} -->
|
|
21526
21909
|
`);
|
|
@@ -21566,9 +21949,9 @@ function parseFrontmatterPaths(paths2) {
|
|
|
21566
21949
|
}
|
|
21567
21950
|
function readMemoryFile(filePath, type, includeBasePath) {
|
|
21568
21951
|
try {
|
|
21569
|
-
const rawContent =
|
|
21570
|
-
const baseDir = includeBasePath ||
|
|
21571
|
-
const processedFiles = /* @__PURE__ */ new Set([
|
|
21952
|
+
const rawContent = fs31.readFileSync(filePath, "utf-8");
|
|
21953
|
+
const baseDir = includeBasePath || path33.dirname(filePath);
|
|
21954
|
+
const processedFiles = /* @__PURE__ */ new Set([path33.normalize(filePath)]);
|
|
21572
21955
|
const { frontmatter, content: withoutFrontmatter } = parseFrontmatter(rawContent);
|
|
21573
21956
|
const globs = parseFrontmatterPaths(frontmatter.paths);
|
|
21574
21957
|
const processedContent = processIncludes(withoutFrontmatter, baseDir, processedFiles);
|
|
@@ -21590,15 +21973,15 @@ function readMemoryFile(filePath, type, includeBasePath) {
|
|
|
21590
21973
|
}
|
|
21591
21974
|
function findGitRoot(startDir) {
|
|
21592
21975
|
let current = startDir;
|
|
21593
|
-
while (current !==
|
|
21594
|
-
const gitPath =
|
|
21976
|
+
while (current !== path33.dirname(current)) {
|
|
21977
|
+
const gitPath = path33.join(current, ".git");
|
|
21595
21978
|
try {
|
|
21596
|
-
if (
|
|
21979
|
+
if (fs31.existsSync(gitPath)) {
|
|
21597
21980
|
return current;
|
|
21598
21981
|
}
|
|
21599
21982
|
} catch {
|
|
21600
21983
|
}
|
|
21601
|
-
current =
|
|
21984
|
+
current = path33.dirname(current);
|
|
21602
21985
|
}
|
|
21603
21986
|
return null;
|
|
21604
21987
|
}
|
|
@@ -21623,17 +22006,17 @@ function getGitUserInfo(cwd2) {
|
|
|
21623
22006
|
}
|
|
21624
22007
|
function processRulesDirectory(rulesDir, type, processedPaths, conditionalRule = false) {
|
|
21625
22008
|
const result = [];
|
|
21626
|
-
if (!
|
|
22009
|
+
if (!fs31.existsSync(rulesDir)) {
|
|
21627
22010
|
return result;
|
|
21628
22011
|
}
|
|
21629
22012
|
try {
|
|
21630
|
-
const entries =
|
|
22013
|
+
const entries = fs31.readdirSync(rulesDir, { withFileTypes: true });
|
|
21631
22014
|
for (const entry of entries) {
|
|
21632
|
-
const entryPath =
|
|
22015
|
+
const entryPath = path33.join(rulesDir, entry.name);
|
|
21633
22016
|
if (entry.isDirectory()) {
|
|
21634
22017
|
result.push(...processRulesDirectory(entryPath, type, processedPaths, conditionalRule));
|
|
21635
22018
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
21636
|
-
const normalizedPath =
|
|
22019
|
+
const normalizedPath = path33.normalize(entryPath);
|
|
21637
22020
|
if (processedPaths.has(normalizedPath)) {
|
|
21638
22021
|
continue;
|
|
21639
22022
|
}
|
|
@@ -21669,13 +22052,13 @@ function loadManagedMemory() {
|
|
|
21669
22052
|
function loadUserMemory() {
|
|
21670
22053
|
const files = [];
|
|
21671
22054
|
const homeDir = os21.homedir();
|
|
21672
|
-
const userBlumaDir =
|
|
21673
|
-
const userBlumaMd =
|
|
22055
|
+
const userBlumaDir = path33.join(homeDir, ".bluma");
|
|
22056
|
+
const userBlumaMd = path33.join(userBlumaDir, "BLUMA.md");
|
|
21674
22057
|
const userFile = readMemoryFile(userBlumaMd, "User");
|
|
21675
22058
|
if (userFile && userFile.content.trim()) {
|
|
21676
22059
|
files.push(userFile);
|
|
21677
22060
|
}
|
|
21678
|
-
const userRulesDir =
|
|
22061
|
+
const userRulesDir = path33.join(userBlumaDir, "rules");
|
|
21679
22062
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
21680
22063
|
files.push(...processRulesDirectory(userRulesDir, "User", processedPaths, false));
|
|
21681
22064
|
return files;
|
|
@@ -21687,10 +22070,10 @@ function loadProjectMemory(cwd2) {
|
|
|
21687
22070
|
let currentDir = cwd2;
|
|
21688
22071
|
const MAX_TRAVERSAL_DEPTH = 20;
|
|
21689
22072
|
let depth = 0;
|
|
21690
|
-
const normalizedGitRoot =
|
|
21691
|
-
while (currentDir !==
|
|
22073
|
+
const normalizedGitRoot = path33.resolve(gitRoot);
|
|
22074
|
+
while (currentDir !== path33.dirname(currentDir) && path33.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
|
|
21692
22075
|
dirs.push(currentDir);
|
|
21693
|
-
currentDir =
|
|
22076
|
+
currentDir = path33.dirname(currentDir);
|
|
21694
22077
|
depth++;
|
|
21695
22078
|
}
|
|
21696
22079
|
if (!dirs.includes(gitRoot)) {
|
|
@@ -21698,7 +22081,7 @@ function loadProjectMemory(cwd2) {
|
|
|
21698
22081
|
}
|
|
21699
22082
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
21700
22083
|
for (const dir of dirs.reverse()) {
|
|
21701
|
-
const projectBlumaMd =
|
|
22084
|
+
const projectBlumaMd = path33.join(dir, "BLUMA.md");
|
|
21702
22085
|
if (!processedPaths.has(projectBlumaMd)) {
|
|
21703
22086
|
processedPaths.add(projectBlumaMd);
|
|
21704
22087
|
const projectFile = readMemoryFile(projectBlumaMd, "Project");
|
|
@@ -21706,7 +22089,7 @@ function loadProjectMemory(cwd2) {
|
|
|
21706
22089
|
files.push(projectFile);
|
|
21707
22090
|
}
|
|
21708
22091
|
}
|
|
21709
|
-
const blumaDirBlumaMd =
|
|
22092
|
+
const blumaDirBlumaMd = path33.join(dir, ".bluma", "BLUMA.md");
|
|
21710
22093
|
if (!processedPaths.has(blumaDirBlumaMd)) {
|
|
21711
22094
|
processedPaths.add(blumaDirBlumaMd);
|
|
21712
22095
|
const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "Project");
|
|
@@ -21714,10 +22097,10 @@ function loadProjectMemory(cwd2) {
|
|
|
21714
22097
|
files.push(blumaDirFile);
|
|
21715
22098
|
}
|
|
21716
22099
|
}
|
|
21717
|
-
const rulesDir =
|
|
22100
|
+
const rulesDir = path33.join(dir, ".bluma", "rules");
|
|
21718
22101
|
files.push(...processRulesDirectory(rulesDir, "Project", processedPaths, false));
|
|
21719
22102
|
}
|
|
21720
|
-
const localBlumaMd =
|
|
22103
|
+
const localBlumaMd = path33.join(cwd2, "BLUMA.local.md");
|
|
21721
22104
|
if (!processedPaths.has(localBlumaMd)) {
|
|
21722
22105
|
processedPaths.add(localBlumaMd);
|
|
21723
22106
|
const localFile = readMemoryFile(localBlumaMd, "Local");
|
|
@@ -21803,10 +22186,10 @@ var LIMITS = {
|
|
|
21803
22186
|
};
|
|
21804
22187
|
function safeReadFile(filePath, maxChars) {
|
|
21805
22188
|
try {
|
|
21806
|
-
if (!
|
|
21807
|
-
const st =
|
|
22189
|
+
if (!fs32.existsSync(filePath)) return null;
|
|
22190
|
+
const st = fs32.statSync(filePath);
|
|
21808
22191
|
if (!st.isFile()) return null;
|
|
21809
|
-
const raw =
|
|
22192
|
+
const raw = fs32.readFileSync(filePath, "utf8");
|
|
21810
22193
|
if (raw.includes("\0")) return null;
|
|
21811
22194
|
if (raw.length <= maxChars) return raw;
|
|
21812
22195
|
return `${raw.slice(0, maxChars)}
|
|
@@ -21818,7 +22201,7 @@ function safeReadFile(filePath, maxChars) {
|
|
|
21818
22201
|
}
|
|
21819
22202
|
function tryReadReadme(cwd2) {
|
|
21820
22203
|
for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
|
|
21821
|
-
const c = safeReadFile(
|
|
22204
|
+
const c = safeReadFile(path34.join(cwd2, name), LIMITS.readme);
|
|
21822
22205
|
if (c) return `(${name})
|
|
21823
22206
|
${c}`;
|
|
21824
22207
|
}
|
|
@@ -21826,14 +22209,14 @@ ${c}`;
|
|
|
21826
22209
|
}
|
|
21827
22210
|
function tryReadBluMaMd(cwd2) {
|
|
21828
22211
|
const paths2 = [
|
|
21829
|
-
|
|
21830
|
-
|
|
21831
|
-
|
|
22212
|
+
path34.join(cwd2, "BluMa.md"),
|
|
22213
|
+
path34.join(cwd2, "BLUMA.md"),
|
|
22214
|
+
path34.join(cwd2, ".bluma", "BluMa.md")
|
|
21832
22215
|
];
|
|
21833
22216
|
for (const p of paths2) {
|
|
21834
22217
|
const c = safeReadFile(p, LIMITS.blumaMd);
|
|
21835
22218
|
if (c) {
|
|
21836
|
-
const rel =
|
|
22219
|
+
const rel = path34.relative(cwd2, p) || p;
|
|
21837
22220
|
return `(${rel})
|
|
21838
22221
|
${c}`;
|
|
21839
22222
|
}
|
|
@@ -21841,10 +22224,10 @@ ${c}`;
|
|
|
21841
22224
|
return null;
|
|
21842
22225
|
}
|
|
21843
22226
|
function summarizePackageJson(cwd2) {
|
|
21844
|
-
const p =
|
|
22227
|
+
const p = path34.join(cwd2, "package.json");
|
|
21845
22228
|
try {
|
|
21846
|
-
if (!
|
|
21847
|
-
const pkg = JSON.parse(
|
|
22229
|
+
if (!fs32.existsSync(p)) return null;
|
|
22230
|
+
const pkg = JSON.parse(fs32.readFileSync(p, "utf8"));
|
|
21848
22231
|
const scripts = pkg.scripts;
|
|
21849
22232
|
let scriptKeys = "";
|
|
21850
22233
|
if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
@@ -21877,7 +22260,7 @@ function summarizePackageJson(cwd2) {
|
|
|
21877
22260
|
}
|
|
21878
22261
|
function topLevelListing(cwd2) {
|
|
21879
22262
|
try {
|
|
21880
|
-
const names =
|
|
22263
|
+
const names = fs32.readdirSync(cwd2, { withFileTypes: true });
|
|
21881
22264
|
const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
|
|
21882
22265
|
const limited = sorted.slice(0, LIMITS.topDirEntries);
|
|
21883
22266
|
const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
|
|
@@ -21935,7 +22318,7 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
21935
22318
|
parts.push(pkg);
|
|
21936
22319
|
parts.push("```\n");
|
|
21937
22320
|
}
|
|
21938
|
-
const py = safeReadFile(
|
|
22321
|
+
const py = safeReadFile(path34.join(cwd2, "pyproject.toml"), LIMITS.pyproject);
|
|
21939
22322
|
if (py) {
|
|
21940
22323
|
parts.push("### pyproject.toml (excerpt)\n```toml");
|
|
21941
22324
|
parts.push(py);
|
|
@@ -21953,15 +22336,15 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
21953
22336
|
parts.push(bluma);
|
|
21954
22337
|
parts.push("```\n");
|
|
21955
22338
|
}
|
|
21956
|
-
const contrib = safeReadFile(
|
|
22339
|
+
const contrib = safeReadFile(path34.join(cwd2, "CONTRIBUTING.md"), LIMITS.contributing);
|
|
21957
22340
|
if (contrib) {
|
|
21958
22341
|
parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
|
|
21959
22342
|
parts.push(contrib);
|
|
21960
22343
|
parts.push("```\n");
|
|
21961
22344
|
}
|
|
21962
|
-
const chlog = safeReadFile(
|
|
22345
|
+
const chlog = safeReadFile(path34.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
|
|
21963
22346
|
if (!chlog) {
|
|
21964
|
-
const alt = safeReadFile(
|
|
22347
|
+
const alt = safeReadFile(path34.join(cwd2, "CHANGES.md"), LIMITS.changelog);
|
|
21965
22348
|
if (alt) {
|
|
21966
22349
|
parts.push("### CHANGES.md (excerpt)\n```markdown");
|
|
21967
22350
|
parts.push(alt);
|
|
@@ -21997,14 +22380,14 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
21997
22380
|
} else {
|
|
21998
22381
|
parts.push("### Git\n(not a git work tree, or `git` unavailable)\n");
|
|
21999
22382
|
}
|
|
22000
|
-
const tsconfig = safeReadFile(
|
|
22383
|
+
const tsconfig = safeReadFile(path34.join(cwd2, "tsconfig.json"), LIMITS.tsconfig);
|
|
22001
22384
|
if (tsconfig) {
|
|
22002
22385
|
parts.push("### tsconfig.json (excerpt)\n```json");
|
|
22003
22386
|
parts.push(tsconfig);
|
|
22004
22387
|
parts.push("```\n");
|
|
22005
22388
|
}
|
|
22006
22389
|
for (const name of ["Dockerfile", "dockerfile", "Dockerfile.prod", "Dockerfile.dev"]) {
|
|
22007
|
-
const df = safeReadFile(
|
|
22390
|
+
const df = safeReadFile(path34.join(cwd2, name), LIMITS.dockerfile);
|
|
22008
22391
|
if (df) {
|
|
22009
22392
|
parts.push(`### ${name} (excerpt)
|
|
22010
22393
|
`);
|
|
@@ -22014,7 +22397,7 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22014
22397
|
}
|
|
22015
22398
|
}
|
|
22016
22399
|
for (const name of ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"]) {
|
|
22017
|
-
const dc = safeReadFile(
|
|
22400
|
+
const dc = safeReadFile(path34.join(cwd2, name), LIMITS.dockerfile);
|
|
22018
22401
|
if (dc) {
|
|
22019
22402
|
parts.push(`### ${name} (excerpt)
|
|
22020
22403
|
`);
|
|
@@ -22029,12 +22412,12 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22029
22412
|
".circleci/config.yml",
|
|
22030
22413
|
"Jenkinsfile"
|
|
22031
22414
|
]) {
|
|
22032
|
-
const ciFile =
|
|
22033
|
-
if (
|
|
22034
|
-
const st =
|
|
22415
|
+
const ciFile = path34.join(cwd2, ciPath);
|
|
22416
|
+
if (fs32.existsSync(ciFile)) {
|
|
22417
|
+
const st = fs32.statSync(ciFile);
|
|
22035
22418
|
if (st.isDirectory()) {
|
|
22036
22419
|
try {
|
|
22037
|
-
const wfFiles =
|
|
22420
|
+
const wfFiles = fs32.readdirSync(ciFile).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
22038
22421
|
if (wfFiles.length > 0) {
|
|
22039
22422
|
parts.push(`### GitHub Actions workflows
|
|
22040
22423
|
\`${wfFiles.join("`, `")}\`
|
|
@@ -22045,7 +22428,7 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22045
22428
|
} else {
|
|
22046
22429
|
const ci = safeReadFile(ciFile, LIMITS.ciConfig);
|
|
22047
22430
|
if (ci) {
|
|
22048
|
-
parts.push(`### CI config (${
|
|
22431
|
+
parts.push(`### CI config (${path34.basename(ciPath)})
|
|
22049
22432
|
`);
|
|
22050
22433
|
parts.push(ci);
|
|
22051
22434
|
parts.push("\n");
|
|
@@ -22054,8 +22437,8 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22054
22437
|
}
|
|
22055
22438
|
}
|
|
22056
22439
|
for (const depFile of ["requirements.txt", "Pipfile", "poetry.lock", "Gemfile", "go.sum", "Cargo.lock"]) {
|
|
22057
|
-
const depPath =
|
|
22058
|
-
if (
|
|
22440
|
+
const depPath = path34.join(cwd2, depFile);
|
|
22441
|
+
if (fs32.existsSync(depPath)) {
|
|
22059
22442
|
const depContent = safeReadFile(depPath, 1500);
|
|
22060
22443
|
if (depContent) {
|
|
22061
22444
|
parts.push(`### ${depFile} (top entries)
|
|
@@ -22068,10 +22451,10 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22068
22451
|
}
|
|
22069
22452
|
}
|
|
22070
22453
|
for (const envFile of [".env.example", ".env.sample", ".env.template"]) {
|
|
22071
|
-
const envPath =
|
|
22072
|
-
if (
|
|
22454
|
+
const envPath = path34.join(cwd2, envFile);
|
|
22455
|
+
if (fs32.existsSync(envPath)) {
|
|
22073
22456
|
try {
|
|
22074
|
-
const raw =
|
|
22457
|
+
const raw = fs32.readFileSync(envPath, "utf-8");
|
|
22075
22458
|
const keys = raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).map((l) => {
|
|
22076
22459
|
const eqIndex = l.indexOf("=");
|
|
22077
22460
|
return eqIndex >= 0 ? l.slice(0, eqIndex).trim() : l.trim();
|
|
@@ -22094,15 +22477,15 @@ init_runtime_config();
|
|
|
22094
22477
|
|
|
22095
22478
|
// src/app/agent/runtime/plugin_registry.ts
|
|
22096
22479
|
init_sandbox_policy();
|
|
22097
|
-
import
|
|
22480
|
+
import fs33 from "fs";
|
|
22098
22481
|
import os22 from "os";
|
|
22099
|
-
import
|
|
22482
|
+
import path35 from "path";
|
|
22100
22483
|
function getProjectPluginsDir() {
|
|
22101
22484
|
const policy = getSandboxPolicy();
|
|
22102
|
-
return
|
|
22485
|
+
return path35.join(policy.workspaceRoot, ".bluma", "plugins");
|
|
22103
22486
|
}
|
|
22104
22487
|
function getGlobalPluginsDir() {
|
|
22105
|
-
return
|
|
22488
|
+
return path35.join(process.env.HOME || os22.homedir(), ".bluma", "plugins");
|
|
22106
22489
|
}
|
|
22107
22490
|
function getPluginDirs() {
|
|
22108
22491
|
return {
|
|
@@ -22111,11 +22494,11 @@ function getPluginDirs() {
|
|
|
22111
22494
|
};
|
|
22112
22495
|
}
|
|
22113
22496
|
function readManifest(manifestPath, fallbackName) {
|
|
22114
|
-
if (!
|
|
22497
|
+
if (!fs33.existsSync(manifestPath)) {
|
|
22115
22498
|
return null;
|
|
22116
22499
|
}
|
|
22117
22500
|
try {
|
|
22118
|
-
const parsed = JSON.parse(
|
|
22501
|
+
const parsed = JSON.parse(fs33.readFileSync(manifestPath, "utf-8"));
|
|
22119
22502
|
return {
|
|
22120
22503
|
name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
|
|
22121
22504
|
description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
|
|
@@ -22128,22 +22511,22 @@ function readManifest(manifestPath, fallbackName) {
|
|
|
22128
22511
|
}
|
|
22129
22512
|
function findManifestPath(pluginDir) {
|
|
22130
22513
|
const candidates = [
|
|
22131
|
-
|
|
22132
|
-
|
|
22514
|
+
path35.join(pluginDir, ".codex-plugin", "plugin.json"),
|
|
22515
|
+
path35.join(pluginDir, "plugin.json")
|
|
22133
22516
|
];
|
|
22134
22517
|
for (const candidate of candidates) {
|
|
22135
|
-
if (
|
|
22518
|
+
if (fs33.existsSync(candidate)) {
|
|
22136
22519
|
return candidate;
|
|
22137
22520
|
}
|
|
22138
22521
|
}
|
|
22139
22522
|
return null;
|
|
22140
22523
|
}
|
|
22141
22524
|
function listFromDir(baseDir, source) {
|
|
22142
|
-
if (!
|
|
22525
|
+
if (!fs33.existsSync(baseDir)) {
|
|
22143
22526
|
return [];
|
|
22144
22527
|
}
|
|
22145
|
-
return
|
|
22146
|
-
const pluginDir =
|
|
22528
|
+
return fs33.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
22529
|
+
const pluginDir = path35.join(baseDir, entry.name);
|
|
22147
22530
|
const manifestPath = findManifestPath(pluginDir);
|
|
22148
22531
|
if (!manifestPath) {
|
|
22149
22532
|
return [];
|
|
@@ -22588,8 +22971,8 @@ function buildModelInfoSection(modelId) {
|
|
|
22588
22971
|
|
|
22589
22972
|
// src/app/agent/runtime/hook_registry.ts
|
|
22590
22973
|
init_sandbox_policy();
|
|
22591
|
-
import
|
|
22592
|
-
import
|
|
22974
|
+
import fs34 from "fs";
|
|
22975
|
+
import path36 from "path";
|
|
22593
22976
|
var DEFAULT_STATE = {
|
|
22594
22977
|
enabled: true,
|
|
22595
22978
|
maxEvents: 120,
|
|
@@ -22600,7 +22983,7 @@ var cache3 = null;
|
|
|
22600
22983
|
var cachePath2 = null;
|
|
22601
22984
|
function getStatePath() {
|
|
22602
22985
|
const policy = getSandboxPolicy();
|
|
22603
|
-
return
|
|
22986
|
+
return path36.join(policy.workspaceRoot, ".bluma", "hooks.json");
|
|
22604
22987
|
}
|
|
22605
22988
|
function getHookStatePath() {
|
|
22606
22989
|
return getStatePath();
|
|
@@ -22619,8 +23002,8 @@ function ensureLoaded2() {
|
|
|
22619
23002
|
return cache3;
|
|
22620
23003
|
}
|
|
22621
23004
|
try {
|
|
22622
|
-
if (
|
|
22623
|
-
const parsed = JSON.parse(
|
|
23005
|
+
if (fs34.existsSync(statePath)) {
|
|
23006
|
+
const parsed = JSON.parse(fs34.readFileSync(statePath, "utf-8"));
|
|
22624
23007
|
cache3 = {
|
|
22625
23008
|
enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
|
|
22626
23009
|
maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
|
|
@@ -22646,9 +23029,9 @@ function ensureLoaded2() {
|
|
|
22646
23029
|
}
|
|
22647
23030
|
function persist2(state2) {
|
|
22648
23031
|
const statePath = getStatePath();
|
|
22649
|
-
|
|
23032
|
+
fs34.mkdirSync(path36.dirname(statePath), { recursive: true });
|
|
22650
23033
|
state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
22651
|
-
|
|
23034
|
+
fs34.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
|
|
22652
23035
|
cache3 = state2;
|
|
22653
23036
|
cachePath2 = statePath;
|
|
22654
23037
|
}
|
|
@@ -22898,18 +23281,18 @@ Usa s\xF3 facts do pedido; campos em falta \u2192 "n\xE3o fornecido" \u2014 nunc
|
|
|
22898
23281
|
}
|
|
22899
23282
|
|
|
22900
23283
|
// src/app/agent/core/prompt/auto_memory.ts
|
|
22901
|
-
import
|
|
22902
|
-
import
|
|
23284
|
+
import fs35 from "fs";
|
|
23285
|
+
import path37 from "path";
|
|
22903
23286
|
import os23 from "os";
|
|
22904
|
-
var AUTO_MEMORY_FILE =
|
|
23287
|
+
var AUTO_MEMORY_FILE = path37.join(os23.homedir(), ".bluma", "auto_memory.md");
|
|
22905
23288
|
var MAX_AUTO_MEMORY_CHARS = 25e3;
|
|
22906
23289
|
var MAX_AUTO_MEMORY_LINES = 200;
|
|
22907
23290
|
function getAutoMemoryForPrompt() {
|
|
22908
23291
|
try {
|
|
22909
|
-
if (!
|
|
23292
|
+
if (!fs35.existsSync(AUTO_MEMORY_FILE)) {
|
|
22910
23293
|
return "";
|
|
22911
23294
|
}
|
|
22912
|
-
let content =
|
|
23295
|
+
let content = fs35.readFileSync(AUTO_MEMORY_FILE, "utf-8");
|
|
22913
23296
|
if (!content.trim()) {
|
|
22914
23297
|
return "";
|
|
22915
23298
|
}
|
|
@@ -22935,6 +23318,103 @@ function isAutoMemoryEnabled() {
|
|
|
22935
23318
|
return true;
|
|
22936
23319
|
}
|
|
22937
23320
|
|
|
23321
|
+
// src/app/agent/core/prompt/factorai_sh_prompt.ts
|
|
23322
|
+
var FACTORAI_SH_SECTION = `
|
|
23323
|
+
<factorai_sh_workflow>
|
|
23324
|
+
## FactorAI.sh \u2014 Next.js hosting (mandatory workflow)
|
|
23325
|
+
|
|
23326
|
+
**When to load full checklist:** \`load_skill("factorai-sh")\` before any create/deploy/edit/redeploy task.
|
|
23327
|
+
|
|
23328
|
+
### Backend URL (already in your environment)
|
|
23329
|
+
| Variable | Used by |
|
|
23330
|
+
|----------|---------|
|
|
23331
|
+
| \`FACTORAI_BASE_URL\` | \`factorai.sh.get_app_status\`, \`factorai.sh.apply_app_changes\`, \`factorai.sh.redeploy_app\` |
|
|
23332
|
+
| \`SEVERINO_URL\` | \`factorai.sh.deploy_app\` (ZIP upload to \`/api/v1/deploy\`) |
|
|
23333
|
+
| \`FACTORAI_API_KEY\` / \`SEVERINO_API_KEY\` | Optional Bearer on write endpoints |
|
|
23334
|
+
|
|
23335
|
+
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.
|
|
23336
|
+
|
|
23337
|
+
### Source of truth: \`factorai.sh.json\`
|
|
23338
|
+
- Written/updated by \`factorai.sh.deploy_app\` and FactorAI tools \u2014 **read with normal file tools**, never a separate manifest tool.
|
|
23339
|
+
- Before any edit/redeploy on an **existing** app: read \`factorai.sh.json\` \u2192 use \`appContext.appId\` (UUID).
|
|
23340
|
+
- Never invent \`appId\` or duplicate state outside this file.
|
|
23341
|
+
|
|
23342
|
+
### Tools (sandbox-only)
|
|
23343
|
+
| Tool | When |
|
|
23344
|
+
|------|------|
|
|
23345
|
+
| \`factorai.sh.create_next_app\` | New Next.js project in workspace |
|
|
23346
|
+
| \`factorai.sh.deploy_app\` | First deploy (ZIP source only) |
|
|
23347
|
+
| \`factorai.sh.apply_app_changes\` | **App already online** \u2014 patch files + rebuild (Vercel-like) |
|
|
23348
|
+
| \`factorai.sh.get_app_status\` | Poll build status after deploy/apply |
|
|
23349
|
+
| \`factorai.sh.redeploy_app\` | Rebuild current sources without file patches |
|
|
23350
|
+
|
|
23351
|
+
---
|
|
23352
|
+
|
|
23353
|
+
## Flow A \u2014 New app (no \`factorai.sh.json\` yet)
|
|
23354
|
+
|
|
23355
|
+
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.
|
|
23356
|
+
2. Customize with \`edit_tool\` / \`file_write\` / \`shell_command\` in the project directory (e.g. \`./{name}/app/page.tsx\`).
|
|
23357
|
+
3. Optional: \`npx tsc --noEmit\` to catch TS errors \u2014 **avoid** \`npm run build\` before deploy (creates \`.next/\` and inflates the ZIP).
|
|
23358
|
+
4. \`factorai.sh.deploy_app({ projectDir: "./{name}", name })\` \u2014 tool excludes \`node_modules\`, \`.next\`, \`.env*\` automatically. **Never** manual \`zip\` including \`node_modules\` or \`.next\`.
|
|
23359
|
+
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).
|
|
23360
|
+
|
|
23361
|
+
---
|
|
23362
|
+
|
|
23363
|
+
## Session scope (mandatory)
|
|
23364
|
+
|
|
23365
|
+
- App state lives **only** in **this** sandbox workspace (\`factorai.sh.json\` + project dir for this \`session_id\`).
|
|
23366
|
+
- **Never** assume an app from another chat/session \u2014 no cross-session \`appId\` guessing.
|
|
23367
|
+
- 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.
|
|
23368
|
+
|
|
23369
|
+
## Flow B \u2014 App already deployed in this session (Vercel-style edit + rebuild)
|
|
23370
|
+
|
|
23371
|
+
**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.
|
|
23372
|
+
|
|
23373
|
+
1. Read \`factorai.sh.json\` in this workspace \u2192 \`appId\`.
|
|
23374
|
+
2. After \`edit_tool\` / \`file_write\`, **read the full file** you changed (\`read_file_lines\` or read entire file).
|
|
23375
|
+
3. \`factorai.sh.apply_app_changes({ appId, files: [{ path: "app/page.tsx", content: "<ENTIRE file contents>" }, ...], deploy: true })\`
|
|
23376
|
+
- **CRITICAL:** \`content\` must be the **complete** file body. The server **replaces** the whole file \u2014 a footer snippet or JSX fragment **destroys** the page.
|
|
23377
|
+
- One entry per changed path; each \`content\` = full file as on disk after your edit.
|
|
23378
|
+
- \`deploy\` defaults to \`true\`. Paths relative to app root (e.g. \`app/page.tsx\`). Blocked: \`node_modules\`, \`.next\`, \`.git\`.
|
|
23379
|
+
4. Poll until rebuild completes (see Polling below).
|
|
23380
|
+
5. Confirm live page, then \`message(result)\` with \`factor-sh-url-app\` from **this** session's manifest.
|
|
23381
|
+
|
|
23382
|
+
Use \`factorai.sh.redeploy_app({ appId })\` only when sources on the server are already correct but build must run again (no file changes).
|
|
23383
|
+
|
|
23384
|
+
---
|
|
23385
|
+
|
|
23386
|
+
## Polling after deploy / apply_app_changes
|
|
23387
|
+
|
|
23388
|
+
1. \`factorai.sh.get_app_status({ appId })\` or GET \`/api/v1/apps/{appId}\`.
|
|
23389
|
+
2. Expect \`status: "building"\` during \`npm install\` / \`next build\` (~30\u201390s).
|
|
23390
|
+
3. **Do not** report "live" on the first \`ready\` if you have not seen \`building\` since this operation \u2014 stale \`ready\` can appear briefly.
|
|
23391
|
+
4. Wait until \`status\` is \`ready\` (or \`running\` / \`live\`) **after** a \`building\` phase, then verify the public route returns HTML (not \`Service Unavailable\`).
|
|
23392
|
+
5. On \`failed\` / \`error\`: read server/build logs if available; fix TS/scaffold issues; retry \`apply_app_changes\` or \`redeploy_app\`.
|
|
23393
|
+
|
|
23394
|
+
---
|
|
23395
|
+
|
|
23396
|
+
## Deploy constraints (first deploy)
|
|
23397
|
+
|
|
23398
|
+
- \`next.config.js\` must keep \`output: 'standalone'\` (included in scaffold).
|
|
23399
|
+
- ZIP max ~50MB \u2014 source only; Severino runs \`npm install\` + \`next build\` on the server.
|
|
23400
|
+
- shadcn components in scaffold must compile (no missing Radix imports).
|
|
23401
|
+
|
|
23402
|
+
---
|
|
23403
|
+
|
|
23404
|
+
## Reporting to orchestrator (Severino)
|
|
23405
|
+
|
|
23406
|
+
- Progress: \`message({ message_type: "info", ... })\` every few steps.
|
|
23407
|
+
- Done: \`message({ message_type: "result", content: "...", attachments: [...], "factor-sh-url-app": "<live url>" })\` \u2014 **only \`result\` ends the worker turn**.
|
|
23408
|
+
- **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\`.
|
|
23409
|
+
- File deliverables still use \`attachments[]\` under \`.bluma/artifacts/\` \u2014 separate from the app URL field.
|
|
23410
|
+
</factorai_sh_workflow>
|
|
23411
|
+
`;
|
|
23412
|
+
function isFactorAiShPromptEnabled() {
|
|
23413
|
+
const isSandbox = process.env.BLUMA_SANDBOX === "true";
|
|
23414
|
+
const baseUrl = (process.env.FACTORAI_BASE_URL || process.env.FACTORAI_URL || "").trim();
|
|
23415
|
+
return isSandbox && Boolean(baseUrl);
|
|
23416
|
+
}
|
|
23417
|
+
|
|
22938
23418
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
22939
23419
|
function getNodeVersion() {
|
|
22940
23420
|
return process.version;
|
|
@@ -22966,17 +23446,17 @@ function getGitBranch(dir) {
|
|
|
22966
23446
|
}
|
|
22967
23447
|
}
|
|
22968
23448
|
function getPackageManager(dir) {
|
|
22969
|
-
if (
|
|
22970
|
-
if (
|
|
22971
|
-
if (
|
|
22972
|
-
if (
|
|
23449
|
+
if (fs36.existsSync(path38.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
23450
|
+
if (fs36.existsSync(path38.join(dir, "yarn.lock"))) return "yarn";
|
|
23451
|
+
if (fs36.existsSync(path38.join(dir, "bun.lockb"))) return "bun";
|
|
23452
|
+
if (fs36.existsSync(path38.join(dir, "package-lock.json"))) return "npm";
|
|
22973
23453
|
return "unknown";
|
|
22974
23454
|
}
|
|
22975
23455
|
function getProjectType(dir) {
|
|
22976
23456
|
try {
|
|
22977
|
-
const files =
|
|
23457
|
+
const files = fs36.readdirSync(dir);
|
|
22978
23458
|
if (files.includes("package.json")) {
|
|
22979
|
-
const pkg = JSON.parse(
|
|
23459
|
+
const pkg = JSON.parse(fs36.readFileSync(path38.join(dir, "package.json"), "utf-8"));
|
|
22980
23460
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
22981
23461
|
if (deps.next) return "Next.js";
|
|
22982
23462
|
if (deps.react) return "React";
|
|
@@ -22996,9 +23476,9 @@ function getProjectType(dir) {
|
|
|
22996
23476
|
}
|
|
22997
23477
|
function getTestFramework(dir) {
|
|
22998
23478
|
try {
|
|
22999
|
-
const pkgPath =
|
|
23000
|
-
if (
|
|
23001
|
-
const pkg = JSON.parse(
|
|
23479
|
+
const pkgPath = path38.join(dir, "package.json");
|
|
23480
|
+
if (fs36.existsSync(pkgPath)) {
|
|
23481
|
+
const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
|
|
23002
23482
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
23003
23483
|
if (deps.jest) return "jest";
|
|
23004
23484
|
if (deps.vitest) return "vitest";
|
|
@@ -23007,7 +23487,7 @@ function getTestFramework(dir) {
|
|
|
23007
23487
|
if (deps["@playwright/test"]) return "playwright";
|
|
23008
23488
|
if (deps.cypress) return "cypress";
|
|
23009
23489
|
}
|
|
23010
|
-
if (
|
|
23490
|
+
if (fs36.existsSync(path38.join(dir, "pytest.ini")) || fs36.existsSync(path38.join(dir, "conftest.py"))) return "pytest";
|
|
23011
23491
|
return "unknown";
|
|
23012
23492
|
} catch {
|
|
23013
23493
|
return "unknown";
|
|
@@ -23015,9 +23495,9 @@ function getTestFramework(dir) {
|
|
|
23015
23495
|
}
|
|
23016
23496
|
function getTestCommand(dir) {
|
|
23017
23497
|
try {
|
|
23018
|
-
const pkgPath =
|
|
23019
|
-
if (
|
|
23020
|
-
const pkg = JSON.parse(
|
|
23498
|
+
const pkgPath = path38.join(dir, "package.json");
|
|
23499
|
+
if (fs36.existsSync(pkgPath)) {
|
|
23500
|
+
const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
|
|
23021
23501
|
if (pkg.scripts?.test) return "npm test";
|
|
23022
23502
|
if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
|
|
23023
23503
|
}
|
|
@@ -23032,8 +23512,8 @@ function getTestCommand(dir) {
|
|
|
23032
23512
|
}
|
|
23033
23513
|
function isGitRepo(dir) {
|
|
23034
23514
|
try {
|
|
23035
|
-
const p =
|
|
23036
|
-
return
|
|
23515
|
+
const p = path38.join(dir, ".git");
|
|
23516
|
+
return fs36.existsSync(p) && fs36.lstatSync(p).isDirectory();
|
|
23037
23517
|
} catch {
|
|
23038
23518
|
return false;
|
|
23039
23519
|
}
|
|
@@ -23234,7 +23714,8 @@ Timeouts mean the orchestrator should raise the limit \u2014 not that the sandbo
|
|
|
23234
23714
|
- **Step 2:** \`message(result)\` with those paths in \`attachments[]\`; use \`content\` for a short summary only.
|
|
23235
23715
|
- Never create a top-level \`./artifacts/\` folder \u2014 it is auto-remapped to \`.bluma/artifacts/\`.
|
|
23236
23716
|
- **Never invent URLs** or paths outside \`.bluma/artifacts/\` in \`attachments[]\`.
|
|
23237
|
-
-
|
|
23717
|
+
- FactorAI.sh live app URL: on \`message(result)\` after deploy, set \`"factor-sh-url-app"\` (absolute URL). File paths still use \`attachments[]\` only.
|
|
23718
|
+
- Next.js hosting on FactorAI.sh: \`load_skill("factorai-sh")\` and follow \`<factorai_sh_workflow>\` when present.
|
|
23238
23719
|
- **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
23720
|
- Remove temp files and generator scripts before finishing.
|
|
23240
23721
|
</sandbox_context>
|
|
@@ -23288,12 +23769,18 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
|
|
|
23288
23769
|
|
|
23289
23770
|
${SUBJECT_MACHINE_MODEL_XML}`;
|
|
23290
23771
|
prompt += interpolate(SANDBOX_SECTION);
|
|
23772
|
+
if (isFactorAiShPromptEnabled()) {
|
|
23773
|
+
prompt += `
|
|
23774
|
+
|
|
23775
|
+
${FACTORAI_SH_SECTION}`;
|
|
23776
|
+
}
|
|
23291
23777
|
const appContext = loadFactorAiAppContext();
|
|
23292
23778
|
if (appContext) {
|
|
23293
23779
|
prompt += `
|
|
23294
23780
|
|
|
23295
|
-
<factorai_app_context>
|
|
23781
|
+
<factorai_app_context active="true">
|
|
23296
23782
|
${formatFactorAiAppContextSummary(appContext)}
|
|
23783
|
+
Read factorai.sh.json before apply_app_changes or redeploy. Prefer apply_app_changes for edits to an already deployed app.
|
|
23297
23784
|
</factorai_app_context>`;
|
|
23298
23785
|
}
|
|
23299
23786
|
}
|
|
@@ -23655,13 +24142,17 @@ var HistoryCompressor = class {
|
|
|
23655
24142
|
*/
|
|
23656
24143
|
async buildContextWindow(fullHistory, llmService, userContext, options) {
|
|
23657
24144
|
if (!fullHistory.length) {
|
|
23658
|
-
return { messages: [], prunedHistory: null };
|
|
24145
|
+
return { messages: [], prunedHistory: null, archivedMessages: [] };
|
|
23659
24146
|
}
|
|
23660
|
-
const
|
|
23661
|
-
|
|
23662
|
-
|
|
23663
|
-
|
|
24147
|
+
const budgetBreakdown = computeEffectiveInputBudget(
|
|
24148
|
+
options?.tokenBudget ?? DEFAULT_CONTEXT_INPUT_BUDGET,
|
|
24149
|
+
options?.toolDefinitions ?? [],
|
|
24150
|
+
{
|
|
24151
|
+
outputReserveTokens: options?.outputReserveTokens,
|
|
24152
|
+
protocolOverheadTokens: options?.protocolOverheadTokens
|
|
24153
|
+
}
|
|
23664
24154
|
);
|
|
24155
|
+
const tokenBudget = budgetBreakdown.effectiveBudget;
|
|
23665
24156
|
const compressThreshold = options?.compressThreshold ?? COMPRESS_THRESHOLD;
|
|
23666
24157
|
const keepRecentTurns = options?.keepRecentTurns ?? KEEP_RECENT_TURNS;
|
|
23667
24158
|
const sanitized = sanitizeConversationForProvider(fullHistory);
|
|
@@ -23692,6 +24183,7 @@ var HistoryCompressor = class {
|
|
|
23692
24183
|
});
|
|
23693
24184
|
let compressionHappened = false;
|
|
23694
24185
|
let compressionFailed = false;
|
|
24186
|
+
const archivedMessages = [];
|
|
23695
24187
|
while (tokens >= thresholdTokens && pendingSlices.length > 0 && !compressionFailed) {
|
|
23696
24188
|
try {
|
|
23697
24189
|
contextEventBus.emit("context:compression_progress", {
|
|
@@ -23701,6 +24193,7 @@ var HistoryCompressor = class {
|
|
|
23701
24193
|
tokenCount: tokens,
|
|
23702
24194
|
tokenBudget
|
|
23703
24195
|
});
|
|
24196
|
+
archivedMessages.push(...pendingFlat);
|
|
23704
24197
|
this.anchor = await this.compressWithTimeout(
|
|
23705
24198
|
pendingFlat,
|
|
23706
24199
|
this.anchor,
|
|
@@ -23718,6 +24211,7 @@ var HistoryCompressor = class {
|
|
|
23718
24211
|
"[HistoryCompressor] Compression failed/timeout, dropping old turns:",
|
|
23719
24212
|
compressError?.message
|
|
23720
24213
|
);
|
|
24214
|
+
archivedMessages.push(...pendingFlat);
|
|
23721
24215
|
pendingSlices = [];
|
|
23722
24216
|
pendingFlat = [];
|
|
23723
24217
|
sliceCount = recentStart;
|
|
@@ -23754,7 +24248,11 @@ var HistoryCompressor = class {
|
|
|
23754
24248
|
} else {
|
|
23755
24249
|
this.compressedSliceCount = sliceCount;
|
|
23756
24250
|
}
|
|
23757
|
-
return {
|
|
24251
|
+
return {
|
|
24252
|
+
messages,
|
|
24253
|
+
prunedHistory,
|
|
24254
|
+
archivedMessages: compressionHappened ? archivedMessages : []
|
|
24255
|
+
};
|
|
23758
24256
|
}
|
|
23759
24257
|
// ── Private helpers ──────────────────────────────────────────────────────────
|
|
23760
24258
|
/** Assembles system + anchor + pending + recent into the final message array. */
|
|
@@ -24436,9 +24934,9 @@ var LLMService = class {
|
|
|
24436
24934
|
};
|
|
24437
24935
|
|
|
24438
24936
|
// src/app/agent/utils/user_message_images.ts
|
|
24439
|
-
import
|
|
24937
|
+
import fs37 from "fs";
|
|
24440
24938
|
import os26 from "os";
|
|
24441
|
-
import
|
|
24939
|
+
import path39 from "path";
|
|
24442
24940
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
24443
24941
|
var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp)$/i;
|
|
24444
24942
|
var MAX_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
@@ -24454,22 +24952,22 @@ var MIME = {
|
|
|
24454
24952
|
function expandUserPath(p) {
|
|
24455
24953
|
const t = p.trim();
|
|
24456
24954
|
if (t.startsWith("~")) {
|
|
24457
|
-
return
|
|
24955
|
+
return path39.join(os26.homedir(), t.slice(1).replace(/^\//, ""));
|
|
24458
24956
|
}
|
|
24459
24957
|
return t;
|
|
24460
24958
|
}
|
|
24461
24959
|
function isPathAllowed(absResolved, cwd2) {
|
|
24462
|
-
const resolved =
|
|
24463
|
-
const cwdR =
|
|
24464
|
-
const homeR =
|
|
24465
|
-
const tmpR =
|
|
24466
|
-
const underCwd = resolved === cwdR || resolved.startsWith(cwdR +
|
|
24467
|
-
const underHome = resolved === homeR || resolved.startsWith(homeR +
|
|
24468
|
-
const underTmp = resolved === tmpR || resolved.startsWith(tmpR +
|
|
24960
|
+
const resolved = path39.normalize(path39.resolve(absResolved));
|
|
24961
|
+
const cwdR = path39.normalize(path39.resolve(cwd2));
|
|
24962
|
+
const homeR = path39.normalize(path39.resolve(os26.homedir()));
|
|
24963
|
+
const tmpR = path39.normalize(path39.resolve(os26.tmpdir()));
|
|
24964
|
+
const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path39.sep);
|
|
24965
|
+
const underHome = resolved === homeR || resolved.startsWith(homeR + path39.sep);
|
|
24966
|
+
const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path39.sep);
|
|
24469
24967
|
return underCwd || underHome || underTmp;
|
|
24470
24968
|
}
|
|
24471
24969
|
function mimeFor(abs) {
|
|
24472
|
-
const ext =
|
|
24970
|
+
const ext = path39.extname(abs).toLowerCase();
|
|
24473
24971
|
return MIME[ext] || "application/octet-stream";
|
|
24474
24972
|
}
|
|
24475
24973
|
var IMAGE_EXT_SRC = String.raw`(?:png|jpe?g|gif|webp|bmp)`;
|
|
@@ -24513,10 +25011,10 @@ function collectImagePathStrings(raw) {
|
|
|
24513
25011
|
}
|
|
24514
25012
|
function resolveImagePath(candidate, cwd2) {
|
|
24515
25013
|
const expanded = expandUserPath(candidate);
|
|
24516
|
-
const abs =
|
|
25014
|
+
const abs = path39.isAbsolute(expanded) ? path39.normalize(expanded) : path39.normalize(path39.resolve(cwd2, expanded));
|
|
24517
25015
|
if (!isPathAllowed(abs, cwd2)) return null;
|
|
24518
25016
|
try {
|
|
24519
|
-
if (!
|
|
25017
|
+
if (!fs37.existsSync(abs) || !fs37.statSync(abs).isFile()) return null;
|
|
24520
25018
|
} catch {
|
|
24521
25019
|
return null;
|
|
24522
25020
|
}
|
|
@@ -24536,11 +25034,11 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
|
|
|
24536
25034
|
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
|
|
24537
25035
|
s = s.slice(1, -1).trim();
|
|
24538
25036
|
}
|
|
24539
|
-
if (!IMAGE_EXT.test(
|
|
25037
|
+
if (!IMAGE_EXT.test(path39.extname(s))) return null;
|
|
24540
25038
|
const abs = resolveImagePath(s, cwd2);
|
|
24541
25039
|
if (!abs) return null;
|
|
24542
25040
|
try {
|
|
24543
|
-
const st =
|
|
25041
|
+
const st = fs37.statSync(abs);
|
|
24544
25042
|
if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
|
|
24545
25043
|
return null;
|
|
24546
25044
|
}
|
|
@@ -24575,7 +25073,7 @@ function tryPasteChunkAsSingleImagePath(chunk, cwd2) {
|
|
|
24575
25073
|
return null;
|
|
24576
25074
|
}
|
|
24577
25075
|
try {
|
|
24578
|
-
const st =
|
|
25076
|
+
const st = fs37.statSync(abs);
|
|
24579
25077
|
if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
|
|
24580
25078
|
return null;
|
|
24581
25079
|
}
|
|
@@ -24604,7 +25102,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
24604
25102
|
const abs = resolveImagePath(c, cwd2);
|
|
24605
25103
|
if (!abs) continue;
|
|
24606
25104
|
try {
|
|
24607
|
-
const st =
|
|
25105
|
+
const st = fs37.statSync(abs);
|
|
24608
25106
|
if (st.size > MAX_IMAGE_BYTES) continue;
|
|
24609
25107
|
} catch {
|
|
24610
25108
|
continue;
|
|
@@ -24621,7 +25119,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
24621
25119
|
}
|
|
24622
25120
|
const parts = [{ type: "text", text: textPart }];
|
|
24623
25121
|
for (const abs of resolvedAbs) {
|
|
24624
|
-
const buf =
|
|
25122
|
+
const buf = fs37.readFileSync(abs);
|
|
24625
25123
|
const b64 = buf.toString("base64");
|
|
24626
25124
|
const mime = mimeFor(abs);
|
|
24627
25125
|
parts.push({
|
|
@@ -24639,7 +25137,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
24639
25137
|
init_sandbox_policy();
|
|
24640
25138
|
init_runtime_config();
|
|
24641
25139
|
init_permission_rules();
|
|
24642
|
-
import
|
|
25140
|
+
import path40 from "path";
|
|
24643
25141
|
var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
|
|
24644
25142
|
function getToolPermissionLayer(metadata) {
|
|
24645
25143
|
if (metadata.riskLevel === "safe") return "read";
|
|
@@ -24654,11 +25152,11 @@ function checkFilePermissionRules(toolName, filePath, policy) {
|
|
|
24654
25152
|
if (!filePath) {
|
|
24655
25153
|
return { allowed: false, reason: "No file path provided for permission check." };
|
|
24656
25154
|
}
|
|
24657
|
-
const resolvedPath =
|
|
25155
|
+
const resolvedPath = path40.resolve(filePath);
|
|
24658
25156
|
if (!isPathInsideWorkspace(resolvedPath, policy)) {
|
|
24659
25157
|
return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
|
|
24660
25158
|
}
|
|
24661
|
-
const relativePath =
|
|
25159
|
+
const relativePath = path40.relative(policy.workspaceRoot, resolvedPath);
|
|
24662
25160
|
const toolPattern = `${toolName}(${relativePath})`;
|
|
24663
25161
|
const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
|
|
24664
25162
|
if (ruleDecision === "deny") {
|
|
@@ -24667,7 +25165,7 @@ function checkFilePermissionRules(toolName, filePath, policy) {
|
|
|
24667
25165
|
if (ruleDecision === "allow") {
|
|
24668
25166
|
return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
|
|
24669
25167
|
}
|
|
24670
|
-
const dirPath =
|
|
25168
|
+
const dirPath = path40.dirname(relativePath);
|
|
24671
25169
|
const dirPattern = `${toolName}(${dirPath}/**)`;
|
|
24672
25170
|
const dirRuleDecision = permissionRulesEngine.checkPermission(dirPattern, { filepath: filePath });
|
|
24673
25171
|
if (dirRuleDecision === "allow") {
|
|
@@ -24884,11 +25382,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
|
|
|
24884
25382
|
}
|
|
24885
25383
|
|
|
24886
25384
|
// src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
|
|
24887
|
-
import * as
|
|
24888
|
-
import * as
|
|
25385
|
+
import * as fs38 from "fs";
|
|
25386
|
+
import * as path41 from "path";
|
|
24889
25387
|
import os27 from "os";
|
|
24890
25388
|
function memoryPath2() {
|
|
24891
|
-
return
|
|
25389
|
+
return path41.join(process.env.HOME || os27.homedir(), ".bluma", "coding_memory.json");
|
|
24892
25390
|
}
|
|
24893
25391
|
function normalizeNote2(note) {
|
|
24894
25392
|
return note.trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -24898,18 +25396,18 @@ function uniqTags(a, b) {
|
|
|
24898
25396
|
}
|
|
24899
25397
|
function consolidateCodingMemoryFile() {
|
|
24900
25398
|
const p = memoryPath2();
|
|
24901
|
-
if (!
|
|
25399
|
+
if (!fs38.existsSync(p)) {
|
|
24902
25400
|
return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
|
|
24903
25401
|
}
|
|
24904
25402
|
const bak = `${p}.bak`;
|
|
24905
25403
|
try {
|
|
24906
|
-
|
|
25404
|
+
fs38.copyFileSync(p, bak);
|
|
24907
25405
|
} catch (e) {
|
|
24908
25406
|
return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
|
|
24909
25407
|
}
|
|
24910
25408
|
let data;
|
|
24911
25409
|
try {
|
|
24912
|
-
data = JSON.parse(
|
|
25410
|
+
data = JSON.parse(fs38.readFileSync(p, "utf-8"));
|
|
24913
25411
|
} catch (e) {
|
|
24914
25412
|
return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
|
|
24915
25413
|
}
|
|
@@ -24944,7 +25442,7 @@ function consolidateCodingMemoryFile() {
|
|
|
24944
25442
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24945
25443
|
};
|
|
24946
25444
|
try {
|
|
24947
|
-
|
|
25445
|
+
fs38.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
|
|
24948
25446
|
} catch (e) {
|
|
24949
25447
|
return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
|
|
24950
25448
|
}
|
|
@@ -25448,6 +25946,22 @@ var BluMaToolRunner = class {
|
|
|
25448
25946
|
}
|
|
25449
25947
|
};
|
|
25450
25948
|
|
|
25949
|
+
// src/app/agent/session_manager/session_archive.ts
|
|
25950
|
+
import path42 from "path";
|
|
25951
|
+
import { promises as fs39 } from "fs";
|
|
25952
|
+
async function archivePrunedConversationMessages(sessionId, messages) {
|
|
25953
|
+
if (!sessionId || messages.length === 0) {
|
|
25954
|
+
return null;
|
|
25955
|
+
}
|
|
25956
|
+
const appDir = getPreferredAppDir();
|
|
25957
|
+
const dir = path42.join(appDir, "sessions", "archive", sessionId);
|
|
25958
|
+
await fs39.mkdir(dir, { recursive: true });
|
|
25959
|
+
const archiveFile = path42.join(dir, `${Date.now()}.jsonl`);
|
|
25960
|
+
const lines = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
25961
|
+
await fs39.appendFile(archiveFile, lines, "utf-8");
|
|
25962
|
+
return archiveFile;
|
|
25963
|
+
}
|
|
25964
|
+
|
|
25451
25965
|
// src/app/agent/core/llm/llm_errors.ts
|
|
25452
25966
|
function isContextWindowValidationError(error) {
|
|
25453
25967
|
const rawMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
@@ -25792,7 +26306,11 @@ var BluMaTurnCoordinator = class {
|
|
|
25792
26306
|
for (let attempt = 0; attempt < tokenBudgets.length; attempt += 1) {
|
|
25793
26307
|
const tokenBudget = tokenBudgets[attempt];
|
|
25794
26308
|
try {
|
|
25795
|
-
const {
|
|
26309
|
+
const {
|
|
26310
|
+
messages: contextWindow,
|
|
26311
|
+
prunedHistory,
|
|
26312
|
+
archivedMessages
|
|
26313
|
+
} = await this.deps.compressor.buildContextWindow(
|
|
25796
26314
|
this.deps.history,
|
|
25797
26315
|
this.deps.llm,
|
|
25798
26316
|
this.deps.getLlmUserContext(),
|
|
@@ -25805,13 +26323,19 @@ var BluMaTurnCoordinator = class {
|
|
|
25805
26323
|
history_length: this.deps.history.length,
|
|
25806
26324
|
context_window_length: contextWindow.length,
|
|
25807
26325
|
pruned_history_length: prunedHistory?.length ?? null,
|
|
26326
|
+
archived_messages: archivedMessages.length,
|
|
25808
26327
|
token_budget: tokenBudget,
|
|
25809
26328
|
context_preview: summarizeHistoryForLog(contextWindow, 8)
|
|
25810
26329
|
});
|
|
25811
26330
|
if (prunedHistory) {
|
|
26331
|
+
if (archivedMessages.length > 0) {
|
|
26332
|
+
await archivePrunedConversationMessages(this.deps.sessionId, archivedMessages);
|
|
26333
|
+
}
|
|
25812
26334
|
this.deps.history.splice(0, this.deps.history.length, ...prunedHistory);
|
|
26335
|
+
await this.deps.persistSessionAfterPrune();
|
|
26336
|
+
} else {
|
|
26337
|
+
this.deps.persistSession();
|
|
25813
26338
|
}
|
|
25814
|
-
this.deps.persistSession();
|
|
25815
26339
|
if (typeof llmService.chatCompletionStream === "function") {
|
|
25816
26340
|
await this.handleStreamingResponse(contextWindow);
|
|
25817
26341
|
} else {
|
|
@@ -26229,6 +26753,7 @@ var BluMaAgent = class {
|
|
|
26229
26753
|
getLlmUserContext: () => this.getLlmUserContext(),
|
|
26230
26754
|
persistSession: () => this.persistSession(),
|
|
26231
26755
|
persistSessionSync: () => this.persistSessionSync(),
|
|
26756
|
+
persistSessionAfterPrune: () => this.persistSessionAfterPrune(),
|
|
26232
26757
|
notifyFactorTurnEndIfNeeded: (reason) => this.notifyFactorTurnEndIfNeeded(reason),
|
|
26233
26758
|
emitTurnCompleted: () => this.emitTurnCompleted(),
|
|
26234
26759
|
generateEditPreview: (toolArgs) => this._generateEditPreview(toolArgs),
|
|
@@ -26268,13 +26793,13 @@ var BluMaAgent = class {
|
|
|
26268
26793
|
if (!this.sessionFile) return;
|
|
26269
26794
|
try {
|
|
26270
26795
|
const sessionData = {
|
|
26271
|
-
session_id:
|
|
26796
|
+
session_id: path43.basename(this.sessionFile, ".json"),
|
|
26272
26797
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26273
26798
|
conversation_history: this.history,
|
|
26274
26799
|
last_updated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26275
26800
|
...this.compressor.getSnapshot()
|
|
26276
26801
|
};
|
|
26277
|
-
|
|
26802
|
+
fs40.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
26278
26803
|
} catch (error) {
|
|
26279
26804
|
console.error("[Bluma] Failed to persist session synchronously:", error);
|
|
26280
26805
|
}
|
|
@@ -26284,6 +26809,11 @@ var BluMaAgent = class {
|
|
|
26284
26809
|
if (!this.sessionFile) return;
|
|
26285
26810
|
void saveSessionHistory(this.sessionFile, this.history, this.getMemorySnapshot());
|
|
26286
26811
|
}
|
|
26812
|
+
/** Gravação imediata após prune (histórico cortado + anchor no JSON de sessão). */
|
|
26813
|
+
async persistSessionAfterPrune() {
|
|
26814
|
+
if (!this.sessionFile) return;
|
|
26815
|
+
await saveSessionHistoryNow(this.sessionFile, this.history, this.getMemorySnapshot());
|
|
26816
|
+
}
|
|
26287
26817
|
recordUiSlashCommand(command, mode = "visible_only") {
|
|
26288
26818
|
const text = String(command ?? "").trim();
|
|
26289
26819
|
if (!text) return;
|
|
@@ -26475,7 +27005,7 @@ var BluMaAgent = class {
|
|
|
26475
27005
|
|
|
26476
27006
|
${editData.error.display}`;
|
|
26477
27007
|
}
|
|
26478
|
-
const filename =
|
|
27008
|
+
const filename = path43.basename(toolArgs.file_path);
|
|
26479
27009
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
26480
27010
|
} catch (e) {
|
|
26481
27011
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -27201,16 +27731,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
|
|
|
27201
27731
|
}
|
|
27202
27732
|
|
|
27203
27733
|
// src/app/agent/core/memory/session_memory.ts
|
|
27204
|
-
import
|
|
27734
|
+
import fs41 from "fs";
|
|
27205
27735
|
import os30 from "os";
|
|
27206
|
-
import
|
|
27736
|
+
import path44 from "path";
|
|
27207
27737
|
import { v4 as uuidv49 } from "uuid";
|
|
27208
27738
|
var SessionMemoryExtractor = class {
|
|
27209
27739
|
llmClient;
|
|
27210
27740
|
memoryFile;
|
|
27211
27741
|
constructor(options = {}) {
|
|
27212
27742
|
this.llmClient = options.llmClient;
|
|
27213
|
-
this.memoryFile = options.memoryFile ||
|
|
27743
|
+
this.memoryFile = options.memoryFile || path44.join(os30.homedir(), ".bluma", "session_memory.json");
|
|
27214
27744
|
}
|
|
27215
27745
|
/**
|
|
27216
27746
|
* Extract memories from conversation using LLM
|
|
@@ -27267,15 +27797,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
|
|
|
27267
27797
|
);
|
|
27268
27798
|
unique.sort((a, b) => b.accessCount - a.accessCount);
|
|
27269
27799
|
const trimmed = unique.slice(0, 200);
|
|
27270
|
-
|
|
27800
|
+
fs41.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
|
|
27271
27801
|
}
|
|
27272
27802
|
/**
|
|
27273
27803
|
* Load memories from disk
|
|
27274
27804
|
*/
|
|
27275
27805
|
async loadMemories() {
|
|
27276
27806
|
try {
|
|
27277
|
-
if (!
|
|
27278
|
-
const data =
|
|
27807
|
+
if (!fs41.existsSync(this.memoryFile)) return [];
|
|
27808
|
+
const data = fs41.readFileSync(this.memoryFile, "utf-8");
|
|
27279
27809
|
return JSON.parse(data);
|
|
27280
27810
|
} catch {
|
|
27281
27811
|
return [];
|
|
@@ -27819,14 +28349,14 @@ var RouteManager = class {
|
|
|
27819
28349
|
this.subAgents = subAgents;
|
|
27820
28350
|
this.core = core;
|
|
27821
28351
|
}
|
|
27822
|
-
registerRoute(
|
|
27823
|
-
this.routeHandlers.set(
|
|
28352
|
+
registerRoute(path56, handler) {
|
|
28353
|
+
this.routeHandlers.set(path56, handler);
|
|
27824
28354
|
}
|
|
27825
28355
|
async handleRoute(payload) {
|
|
27826
28356
|
const inputText = String(payload.content || "").trim();
|
|
27827
28357
|
const { userContext, options } = payload;
|
|
27828
|
-
for (const [
|
|
27829
|
-
if (inputText ===
|
|
28358
|
+
for (const [path56, handler] of this.routeHandlers) {
|
|
28359
|
+
if (inputText === path56 || inputText.startsWith(`${path56} `)) {
|
|
27830
28360
|
return handler({ content: inputText, userContext });
|
|
27831
28361
|
}
|
|
27832
28362
|
}
|
|
@@ -27835,13 +28365,13 @@ var RouteManager = class {
|
|
|
27835
28365
|
};
|
|
27836
28366
|
|
|
27837
28367
|
// src/app/agent/runtime/plugin_runtime.ts
|
|
27838
|
-
import
|
|
28368
|
+
import path45 from "path";
|
|
27839
28369
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
27840
28370
|
async function loadPluginsAtStartup() {
|
|
27841
28371
|
for (const p of listPlugins()) {
|
|
27842
28372
|
const entry = p.manifest.entry?.trim();
|
|
27843
28373
|
if (!entry) continue;
|
|
27844
|
-
const abs =
|
|
28374
|
+
const abs = path45.resolve(p.root, entry);
|
|
27845
28375
|
try {
|
|
27846
28376
|
const href = pathToFileURL2(abs).href;
|
|
27847
28377
|
const mod = await import(href);
|
|
@@ -27862,7 +28392,7 @@ async function loadPluginsAtStartup() {
|
|
|
27862
28392
|
}
|
|
27863
28393
|
|
|
27864
28394
|
// src/app/agent/agent.ts
|
|
27865
|
-
var globalEnvPath =
|
|
28395
|
+
var globalEnvPath = path46.join(os31.homedir(), ".bluma", ".env");
|
|
27866
28396
|
dotenv.config({ path: globalEnvPath });
|
|
27867
28397
|
var Agent = class {
|
|
27868
28398
|
sessionId;
|
|
@@ -29718,10 +30248,10 @@ function resolveToolPayload(result) {
|
|
|
29718
30248
|
|
|
29719
30249
|
// src/app/ui/components/FilePathLink.tsx
|
|
29720
30250
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
29721
|
-
import
|
|
30251
|
+
import path48 from "node:path";
|
|
29722
30252
|
|
|
29723
30253
|
// src/app/ui/utils/pathDisplay.ts
|
|
29724
|
-
import
|
|
30254
|
+
import path47 from "node:path";
|
|
29725
30255
|
import os32 from "node:os";
|
|
29726
30256
|
function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
|
|
29727
30257
|
let s = String(pathInput ?? "").trim();
|
|
@@ -29729,17 +30259,17 @@ function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
|
|
|
29729
30259
|
s = s.replace(/[/\\]+$/, "");
|
|
29730
30260
|
}
|
|
29731
30261
|
if (!s) return "";
|
|
29732
|
-
const abs =
|
|
29733
|
-
const resolvedCwd =
|
|
29734
|
-
const rel =
|
|
29735
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
30262
|
+
const abs = path47.isAbsolute(s) ? path47.normalize(s) : path47.resolve(cwd2, s);
|
|
30263
|
+
const resolvedCwd = path47.resolve(cwd2);
|
|
30264
|
+
const rel = path47.relative(resolvedCwd, abs);
|
|
30265
|
+
if (rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel)) {
|
|
29736
30266
|
return rel === "" ? "." : rel;
|
|
29737
30267
|
}
|
|
29738
|
-
const home =
|
|
29739
|
-
if (abs === home || abs.startsWith(home +
|
|
30268
|
+
const home = path47.normalize(os32.homedir());
|
|
30269
|
+
if (abs === home || abs.startsWith(home + path47.sep)) {
|
|
29740
30270
|
return "~" + abs.slice(home.length);
|
|
29741
30271
|
}
|
|
29742
|
-
return
|
|
30272
|
+
return path47.basename(abs);
|
|
29743
30273
|
}
|
|
29744
30274
|
|
|
29745
30275
|
// src/app/ui/components/FilePathLink.tsx
|
|
@@ -29749,7 +30279,7 @@ function FilePathLink({ filePath, children, cwd: cwd2 = process.cwd(), color })
|
|
|
29749
30279
|
if (!raw) {
|
|
29750
30280
|
return null;
|
|
29751
30281
|
}
|
|
29752
|
-
const abs =
|
|
30282
|
+
const abs = path48.isAbsolute(raw) ? path48.normalize(raw) : path48.resolve(cwd2, raw);
|
|
29753
30283
|
const href = pathToFileURL3(abs).href;
|
|
29754
30284
|
const label = formatPathForDisplay(abs, cwd2);
|
|
29755
30285
|
const text = typeof children === "string" ? children : label;
|
|
@@ -31701,12 +32231,12 @@ function patchToUnifiedDiffText(filePath, patch) {
|
|
|
31701
32231
|
}
|
|
31702
32232
|
|
|
31703
32233
|
// src/app/ui/utils/readEditContext.ts
|
|
31704
|
-
import { promises as
|
|
32234
|
+
import { promises as fs42 } from "fs";
|
|
31705
32235
|
var CHUNK_SIZE = 64 * 1024;
|
|
31706
32236
|
var CONTEXT_LINES2 = 3;
|
|
31707
32237
|
async function openForScan(filePath) {
|
|
31708
32238
|
try {
|
|
31709
|
-
return await
|
|
32239
|
+
return await fs42.open(filePath, "r");
|
|
31710
32240
|
} catch (e) {
|
|
31711
32241
|
if (e.code === "ENOENT") return null;
|
|
31712
32242
|
throw e;
|
|
@@ -32384,13 +32914,13 @@ function EditToolDiffPanel({
|
|
|
32384
32914
|
newString,
|
|
32385
32915
|
replaceAll = false
|
|
32386
32916
|
}) {
|
|
32387
|
-
const
|
|
32917
|
+
const path56 = filePath.trim() || "unknown file";
|
|
32388
32918
|
const hasPreviewArgs = oldString !== void 0 && newString !== void 0;
|
|
32389
32919
|
const hasDiffText = diffText && diffText.trim().length > 0;
|
|
32390
32920
|
return /* @__PURE__ */ jsx43(Box_default, { flexDirection: "column", children: hasPreviewArgs ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(
|
|
32391
32921
|
FileEditToolDiff,
|
|
32392
32922
|
{
|
|
32393
|
-
filePath:
|
|
32923
|
+
filePath: path56,
|
|
32394
32924
|
oldString,
|
|
32395
32925
|
newString,
|
|
32396
32926
|
replaceAll,
|
|
@@ -32428,7 +32958,7 @@ function renderToolUseMessage12({ args }) {
|
|
|
32428
32958
|
return /* @__PURE__ */ jsx44(Text, { color: BLUMA_TERMINAL.blue, children: p });
|
|
32429
32959
|
}
|
|
32430
32960
|
function renderToolHeader12({ args }) {
|
|
32431
|
-
const
|
|
32961
|
+
const path56 = args?.file_path ?? ".";
|
|
32432
32962
|
const oldText = typeof args?.old_string === "string" ? args.old_string : "";
|
|
32433
32963
|
const newText = typeof args?.new_string === "string" ? args.new_string : "";
|
|
32434
32964
|
const counts = countLineDiff(oldText, newText);
|
|
@@ -32438,7 +32968,7 @@ function renderToolHeader12({ args }) {
|
|
|
32438
32968
|
action,
|
|
32439
32969
|
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
32440
32970
|
" ",
|
|
32441
|
-
/* @__PURE__ */ jsx44(FilePathLink, { filePath:
|
|
32971
|
+
/* @__PURE__ */ jsx44(FilePathLink, { filePath: path56, color: BLUMA_TERMINAL.dim })
|
|
32442
32972
|
] })
|
|
32443
32973
|
] }),
|
|
32444
32974
|
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
@@ -32830,11 +33360,11 @@ function userFacingName13() {
|
|
|
32830
33360
|
}
|
|
32831
33361
|
function renderToolUseMessage14({ args }) {
|
|
32832
33362
|
const q = args?.query ? `"${args.query}"` : "...";
|
|
32833
|
-
const
|
|
33363
|
+
const path56 = args?.path || ".";
|
|
32834
33364
|
return /* @__PURE__ */ jsxs30(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
|
|
32835
33365
|
/* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.blue, children: q }),
|
|
32836
33366
|
/* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.dim, children: " " }),
|
|
32837
|
-
/* @__PURE__ */ jsx47(FilePathLink, { filePath:
|
|
33367
|
+
/* @__PURE__ */ jsx47(FilePathLink, { filePath: path56, color: BLUMA_TERMINAL.dim })
|
|
32838
33368
|
] });
|
|
32839
33369
|
}
|
|
32840
33370
|
function renderToolHeader14({ args }) {
|
|
@@ -33356,7 +33886,7 @@ var loadSkillTool = createTool({
|
|
|
33356
33886
|
});
|
|
33357
33887
|
|
|
33358
33888
|
// src/app/agent/tools/FileWriteTool/UI.tsx
|
|
33359
|
-
import
|
|
33889
|
+
import fs43 from "fs";
|
|
33360
33890
|
import { diffLines as diffLines3 } from "diff";
|
|
33361
33891
|
import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
33362
33892
|
function getFilePath(args) {
|
|
@@ -33371,7 +33901,7 @@ function getFilePath(args) {
|
|
|
33371
33901
|
function readExistingFileText(filePath) {
|
|
33372
33902
|
if (!filePath) return "";
|
|
33373
33903
|
try {
|
|
33374
|
-
return
|
|
33904
|
+
return fs43.readFileSync(filePath, "utf-8");
|
|
33375
33905
|
} catch {
|
|
33376
33906
|
return "";
|
|
33377
33907
|
}
|
|
@@ -36301,8 +36831,8 @@ import {
|
|
|
36301
36831
|
|
|
36302
36832
|
// src/app/ui/hooks/useAtCompletion.ts
|
|
36303
36833
|
import { useEffect as useEffect11, useRef as useRef4, useState as useState13 } from "react";
|
|
36304
|
-
import
|
|
36305
|
-
import
|
|
36834
|
+
import fs44 from "fs";
|
|
36835
|
+
import path49 from "path";
|
|
36306
36836
|
var MAX_RESULTS3 = 50;
|
|
36307
36837
|
var DEFAULT_RECURSIVE_DEPTH = 2;
|
|
36308
36838
|
function listPathSuggestions(baseDir, pattern) {
|
|
@@ -36310,7 +36840,7 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
36310
36840
|
const patternEndsWithSlash = raw.endsWith("/");
|
|
36311
36841
|
const relDir = raw.replace(/^\/+|\/+$/g, "");
|
|
36312
36842
|
const filterPrefix = patternEndsWithSlash ? "" : relDir.split("/").slice(-1)[0] || "";
|
|
36313
|
-
const listDir =
|
|
36843
|
+
const listDir = path49.resolve(baseDir, relDir || ".");
|
|
36314
36844
|
const results = [];
|
|
36315
36845
|
const IGNORED_DIRS = ["node_modules", ".git", ".venv", "dist", "build"];
|
|
36316
36846
|
const IGNORED_EXTS = [".pyc", ".class", ".o", ".map", ".log", ".tmp"];
|
|
@@ -36327,7 +36857,7 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
36327
36857
|
}
|
|
36328
36858
|
function pushEntry(entryPath, label, isDir) {
|
|
36329
36859
|
if (results.length >= MAX_RESULTS3) return;
|
|
36330
|
-
const clean = label.split(
|
|
36860
|
+
const clean = label.split(path49.sep).join("/").replace(/[]+/g, "");
|
|
36331
36861
|
results.push({ label: clean + (isDir ? "/" : ""), fullPath: entryPath, isDir });
|
|
36332
36862
|
}
|
|
36333
36863
|
try {
|
|
@@ -36336,11 +36866,11 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
36336
36866
|
while (queue.length && results.length < MAX_RESULTS3) {
|
|
36337
36867
|
const node = queue.shift();
|
|
36338
36868
|
try {
|
|
36339
|
-
const entries =
|
|
36869
|
+
const entries = fs44.readdirSync(node.dir, { withFileTypes: true });
|
|
36340
36870
|
for (const entry of entries) {
|
|
36341
36871
|
if (isIgnoredName(entry.name)) continue;
|
|
36342
|
-
const entryAbs =
|
|
36343
|
-
const entryRel = node.rel ?
|
|
36872
|
+
const entryAbs = path49.join(node.dir, entry.name);
|
|
36873
|
+
const entryRel = node.rel ? path49.posix.join(node.rel, entry.name) : entry.name;
|
|
36344
36874
|
if (entryRel.split("/").includes("node_modules")) continue;
|
|
36345
36875
|
if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
|
|
36346
36876
|
pushEntry(entryAbs, entryRel, entry.isDirectory());
|
|
@@ -36353,13 +36883,13 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
36353
36883
|
}
|
|
36354
36884
|
}
|
|
36355
36885
|
} else {
|
|
36356
|
-
const entries =
|
|
36886
|
+
const entries = fs44.readdirSync(listDir, { withFileTypes: true });
|
|
36357
36887
|
for (const entry of entries) {
|
|
36358
36888
|
if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
|
|
36359
36889
|
if (isIgnoredName(entry.name)) continue;
|
|
36360
36890
|
if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
|
|
36361
|
-
const entryAbs =
|
|
36362
|
-
const label = relDir ?
|
|
36891
|
+
const entryAbs = path49.join(listDir, entry.name);
|
|
36892
|
+
const label = relDir ? path49.posix.join(relDir, entry.name) : entry.name;
|
|
36363
36893
|
pushEntry(entryAbs, label, entry.isDirectory());
|
|
36364
36894
|
if (results.length >= MAX_RESULTS3) break;
|
|
36365
36895
|
}
|
|
@@ -36560,15 +37090,15 @@ var SlashSubmenuInlineComponent = ({ menu }) => {
|
|
|
36560
37090
|
var SlashSubmenuInline = memo15(SlashSubmenuInlineComponent);
|
|
36561
37091
|
|
|
36562
37092
|
// src/app/ui/utils/clipboardImage.ts
|
|
36563
|
-
import
|
|
37093
|
+
import fs45 from "fs";
|
|
36564
37094
|
import os33 from "os";
|
|
36565
|
-
import
|
|
37095
|
+
import path50 from "path";
|
|
36566
37096
|
import { spawn as spawn5, execFile as execFileCb, execSync as execSync4 } from "child_process";
|
|
36567
37097
|
import { promisify as promisify2 } from "util";
|
|
36568
37098
|
|
|
36569
37099
|
// src/app/utils/clipboardNative.ts
|
|
36570
37100
|
import { existsSync as existsSync7 } from "fs";
|
|
36571
|
-
import { createRequire } from "module";
|
|
37101
|
+
import { createRequire as createRequire2 } from "module";
|
|
36572
37102
|
import { dirname as dirname4, join as join8 } from "path";
|
|
36573
37103
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
36574
37104
|
var __dirname;
|
|
@@ -36616,7 +37146,7 @@ function getNativeModule() {
|
|
|
36616
37146
|
throw loadError;
|
|
36617
37147
|
}
|
|
36618
37148
|
try {
|
|
36619
|
-
const require2 =
|
|
37149
|
+
const require2 = createRequire2(import.meta.url);
|
|
36620
37150
|
const mod = require2(nativePath);
|
|
36621
37151
|
nativeModule = mod;
|
|
36622
37152
|
return nativeModule;
|
|
@@ -36691,8 +37221,8 @@ function commandOnPath(cmd) {
|
|
|
36691
37221
|
function unixClipboardHelperDirs() {
|
|
36692
37222
|
const h = os33.homedir();
|
|
36693
37223
|
return [
|
|
36694
|
-
|
|
36695
|
-
|
|
37224
|
+
path50.join(h, ".local", "bin"),
|
|
37225
|
+
path50.join(h, "bin"),
|
|
36696
37226
|
"/usr/bin",
|
|
36697
37227
|
"/usr/local/bin",
|
|
36698
37228
|
"/bin",
|
|
@@ -36710,16 +37240,16 @@ function resolveHelperBinary(cmd) {
|
|
|
36710
37240
|
return cmd;
|
|
36711
37241
|
}
|
|
36712
37242
|
for (const dir of unixClipboardHelperDirs()) {
|
|
36713
|
-
const full =
|
|
37243
|
+
const full = path50.join(dir, cmd);
|
|
36714
37244
|
try {
|
|
36715
|
-
|
|
37245
|
+
fs45.accessSync(full, fs45.constants.X_OK);
|
|
36716
37246
|
return full;
|
|
36717
37247
|
} catch {
|
|
36718
37248
|
}
|
|
36719
37249
|
}
|
|
36720
37250
|
for (const dir of unixClipboardHelperDirs()) {
|
|
36721
|
-
const full =
|
|
36722
|
-
if (
|
|
37251
|
+
const full = path50.join(dir, cmd);
|
|
37252
|
+
if (fs45.existsSync(full)) {
|
|
36723
37253
|
return full;
|
|
36724
37254
|
}
|
|
36725
37255
|
}
|
|
@@ -36760,17 +37290,17 @@ function writeBufferIfImage(baseDir, buf) {
|
|
|
36760
37290
|
if (!ext) {
|
|
36761
37291
|
return null;
|
|
36762
37292
|
}
|
|
36763
|
-
const out =
|
|
37293
|
+
const out = path50.join(
|
|
36764
37294
|
baseDir,
|
|
36765
37295
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
|
|
36766
37296
|
);
|
|
36767
|
-
|
|
37297
|
+
fs45.writeFileSync(out, buf);
|
|
36768
37298
|
return out;
|
|
36769
37299
|
}
|
|
36770
37300
|
function unlinkQuiet(p) {
|
|
36771
37301
|
try {
|
|
36772
|
-
if (
|
|
36773
|
-
|
|
37302
|
+
if (fs45.existsSync(p)) {
|
|
37303
|
+
fs45.unlinkSync(p);
|
|
36774
37304
|
}
|
|
36775
37305
|
} catch {
|
|
36776
37306
|
}
|
|
@@ -36787,25 +37317,25 @@ async function tryDarwinClipboardy(baseDir) {
|
|
|
36787
37317
|
return null;
|
|
36788
37318
|
}
|
|
36789
37319
|
for (const src of tmpPaths) {
|
|
36790
|
-
if (!src || !
|
|
37320
|
+
if (!src || !fs45.existsSync(src)) {
|
|
36791
37321
|
continue;
|
|
36792
37322
|
}
|
|
36793
37323
|
let st;
|
|
36794
37324
|
try {
|
|
36795
|
-
st =
|
|
37325
|
+
st = fs45.statSync(src);
|
|
36796
37326
|
} catch {
|
|
36797
37327
|
continue;
|
|
36798
37328
|
}
|
|
36799
37329
|
if (st.size < 80 || st.size > CLIPBOARD_MAX_BYTES) {
|
|
36800
37330
|
continue;
|
|
36801
37331
|
}
|
|
36802
|
-
const ext =
|
|
37332
|
+
const ext = path50.extname(src).toLowerCase();
|
|
36803
37333
|
const safeExt = ext && /^\.(png|jpe?g|gif|webp)$/i.test(ext) ? ext : ".png";
|
|
36804
|
-
const out =
|
|
37334
|
+
const out = path50.join(
|
|
36805
37335
|
baseDir,
|
|
36806
37336
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
|
|
36807
37337
|
);
|
|
36808
|
-
|
|
37338
|
+
fs45.copyFileSync(src, out);
|
|
36809
37339
|
for (const p of tmpPaths) {
|
|
36810
37340
|
unlinkQuiet(p);
|
|
36811
37341
|
}
|
|
@@ -36819,14 +37349,14 @@ async function tryDarwinClipboardy(baseDir) {
|
|
|
36819
37349
|
return null;
|
|
36820
37350
|
}
|
|
36821
37351
|
async function tryWindowsPowerShell(outFile) {
|
|
36822
|
-
const ps = process.env.SystemRoot != null ?
|
|
37352
|
+
const ps = process.env.SystemRoot != null ? path50.join(
|
|
36823
37353
|
process.env.SystemRoot,
|
|
36824
37354
|
"System32",
|
|
36825
37355
|
"WindowsPowerShell",
|
|
36826
37356
|
"v1.0",
|
|
36827
37357
|
"powershell.exe"
|
|
36828
37358
|
) : "powershell.exe";
|
|
36829
|
-
if (!
|
|
37359
|
+
if (!fs45.existsSync(ps)) {
|
|
36830
37360
|
return false;
|
|
36831
37361
|
}
|
|
36832
37362
|
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 +37378,7 @@ async function tryWindowsPowerShell(outFile) {
|
|
|
36848
37378
|
return false;
|
|
36849
37379
|
}
|
|
36850
37380
|
try {
|
|
36851
|
-
const st =
|
|
37381
|
+
const st = fs45.statSync(outFile);
|
|
36852
37382
|
return st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES;
|
|
36853
37383
|
} catch {
|
|
36854
37384
|
return false;
|
|
@@ -36920,8 +37450,8 @@ function parseClipboardTextAsImagePath(raw) {
|
|
|
36920
37450
|
s = s.slice(1, -1);
|
|
36921
37451
|
}
|
|
36922
37452
|
s = s.trim();
|
|
36923
|
-
if (!CLIPBOARD_PATH_IMAGE_EXT.test(
|
|
36924
|
-
const abs =
|
|
37453
|
+
if (!CLIPBOARD_PATH_IMAGE_EXT.test(path50.extname(s))) return null;
|
|
37454
|
+
const abs = path50.isAbsolute(s) ? path50.normalize(s) : path50.resolve(process.cwd(), s);
|
|
36925
37455
|
return abs;
|
|
36926
37456
|
}
|
|
36927
37457
|
async function tryClipboardTextAsImageFile(baseDir) {
|
|
@@ -36935,19 +37465,19 @@ async function tryClipboardTextAsImageFile(baseDir) {
|
|
|
36935
37465
|
const abs = parseClipboardTextAsImagePath(t);
|
|
36936
37466
|
if (!abs) return null;
|
|
36937
37467
|
try {
|
|
36938
|
-
if (!
|
|
36939
|
-
const st =
|
|
37468
|
+
if (!fs45.existsSync(abs)) return null;
|
|
37469
|
+
const st = fs45.statSync(abs);
|
|
36940
37470
|
if (!st.isFile() || st.size > CLIPBOARD_MAX_BYTES || st.size < 20) return null;
|
|
36941
37471
|
} catch {
|
|
36942
37472
|
return null;
|
|
36943
37473
|
}
|
|
36944
|
-
const ext =
|
|
36945
|
-
const out =
|
|
37474
|
+
const ext = path50.extname(abs).toLowerCase();
|
|
37475
|
+
const out = path50.join(
|
|
36946
37476
|
baseDir,
|
|
36947
37477
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
|
|
36948
37478
|
);
|
|
36949
37479
|
try {
|
|
36950
|
-
|
|
37480
|
+
fs45.copyFileSync(abs, out);
|
|
36951
37481
|
return out;
|
|
36952
37482
|
} catch {
|
|
36953
37483
|
return null;
|
|
@@ -36957,7 +37487,7 @@ async function tryLinuxShellPipelineSave(baseDir) {
|
|
|
36957
37487
|
if (process.platform !== "linux" && process.platform !== "freebsd") {
|
|
36958
37488
|
return null;
|
|
36959
37489
|
}
|
|
36960
|
-
const outPath =
|
|
37490
|
+
const outPath = path50.join(
|
|
36961
37491
|
baseDir,
|
|
36962
37492
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.tmp`
|
|
36963
37493
|
);
|
|
@@ -36979,11 +37509,11 @@ printf '%s' "$OUT"
|
|
|
36979
37509
|
maxBuffer: 4096
|
|
36980
37510
|
});
|
|
36981
37511
|
const written = String(stdout ?? "").trim();
|
|
36982
|
-
if (written !== outPath || !
|
|
37512
|
+
if (written !== outPath || !fs45.existsSync(outPath)) {
|
|
36983
37513
|
unlinkQuiet(outPath);
|
|
36984
37514
|
return null;
|
|
36985
37515
|
}
|
|
36986
|
-
const buf =
|
|
37516
|
+
const buf = fs45.readFileSync(outPath);
|
|
36987
37517
|
unlinkQuiet(outPath);
|
|
36988
37518
|
return writeBufferIfImage(baseDir, buf);
|
|
36989
37519
|
} catch {
|
|
@@ -37004,8 +37534,8 @@ async function tryNativeClipboardImage() {
|
|
|
37004
37534
|
}
|
|
37005
37535
|
try {
|
|
37006
37536
|
const result = readClipboardImageNative();
|
|
37007
|
-
if (
|
|
37008
|
-
const st =
|
|
37537
|
+
if (fs45.existsSync(result.path)) {
|
|
37538
|
+
const st = fs45.statSync(result.path);
|
|
37009
37539
|
if (st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES) {
|
|
37010
37540
|
return result.path;
|
|
37011
37541
|
}
|
|
@@ -37015,8 +37545,8 @@ async function tryNativeClipboardImage() {
|
|
|
37015
37545
|
return null;
|
|
37016
37546
|
}
|
|
37017
37547
|
async function readClipboardImageToTempFile() {
|
|
37018
|
-
const baseDir =
|
|
37019
|
-
|
|
37548
|
+
const baseDir = path50.join(os33.homedir(), ".cache", "bluma", "clipboard");
|
|
37549
|
+
fs45.mkdirSync(baseDir, { recursive: true });
|
|
37020
37550
|
const nativeResult = await tryNativeClipboardImage();
|
|
37021
37551
|
if (nativeResult) {
|
|
37022
37552
|
return nativeResult;
|
|
@@ -37034,7 +37564,7 @@ async function readClipboardImageToTempFile() {
|
|
|
37034
37564
|
}
|
|
37035
37565
|
}
|
|
37036
37566
|
if (process.platform === "win32") {
|
|
37037
|
-
const outFile =
|
|
37567
|
+
const outFile = path50.join(
|
|
37038
37568
|
baseDir,
|
|
37039
37569
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.png`
|
|
37040
37570
|
);
|
|
@@ -37075,7 +37605,7 @@ function expandLargePastePlaceholder(value, pending) {
|
|
|
37075
37605
|
}
|
|
37076
37606
|
|
|
37077
37607
|
// src/app/ui/components/InputPrompt.tsx
|
|
37078
|
-
import
|
|
37608
|
+
import fs46 from "fs";
|
|
37079
37609
|
import { Fragment as Fragment7, jsx as jsx79, jsxs as jsxs62 } from "react/jsx-runtime";
|
|
37080
37610
|
var persistedInputState = { text: "", cursorPosition: 0 };
|
|
37081
37611
|
var StaticCursor = () => /* @__PURE__ */ jsx79(Box_default, { flexDirection: "row", flexWrap: "nowrap", children: /* @__PURE__ */ jsx79(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "\u2588 " }) });
|
|
@@ -37242,7 +37772,7 @@ var InputPrompt = memo16(({
|
|
|
37242
37772
|
return;
|
|
37243
37773
|
}
|
|
37244
37774
|
if (routeText.startsWith("/")) {
|
|
37245
|
-
const isFilePath = map.size > 0 &&
|
|
37775
|
+
const isFilePath = map.size > 0 && fs46.existsSync(routeText.split(/\s+/)[0]);
|
|
37246
37776
|
if (isFilePath) {
|
|
37247
37777
|
uiEventBus.emit("user_overlay", {
|
|
37248
37778
|
kind: "message",
|
|
@@ -39522,8 +40052,8 @@ var renderCode = () => {
|
|
|
39522
40052
|
};
|
|
39523
40053
|
|
|
39524
40054
|
// src/app/agent/core/thread/thread_store.ts
|
|
39525
|
-
import
|
|
39526
|
-
import { promises as
|
|
40055
|
+
import path51 from "path";
|
|
40056
|
+
import { promises as fs47 } from "fs";
|
|
39527
40057
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
39528
40058
|
var INDEX_VERSION = 1;
|
|
39529
40059
|
var fileLocks2 = /* @__PURE__ */ new Map();
|
|
@@ -39548,9 +40078,9 @@ var ThreadStore = class {
|
|
|
39548
40078
|
packageVersion;
|
|
39549
40079
|
constructor() {
|
|
39550
40080
|
const appDir = getPreferredAppDir();
|
|
39551
|
-
this.threadsDir =
|
|
39552
|
-
this.archiveDir =
|
|
39553
|
-
this.indexPath =
|
|
40081
|
+
this.threadsDir = path51.join(appDir, "threads");
|
|
40082
|
+
this.archiveDir = path51.join(this.threadsDir, "archive");
|
|
40083
|
+
this.indexPath = path51.join(this.threadsDir, "index.json");
|
|
39554
40084
|
this.packageVersion = process.env.npm_package_version || "0.0.0";
|
|
39555
40085
|
}
|
|
39556
40086
|
// ==================== Inicialização ====================
|
|
@@ -39558,10 +40088,10 @@ var ThreadStore = class {
|
|
|
39558
40088
|
* Inicializa o diretório de threads
|
|
39559
40089
|
*/
|
|
39560
40090
|
async initialize() {
|
|
39561
|
-
await
|
|
39562
|
-
await
|
|
40091
|
+
await fs47.mkdir(this.threadsDir, { recursive: true });
|
|
40092
|
+
await fs47.mkdir(this.archiveDir, { recursive: true });
|
|
39563
40093
|
try {
|
|
39564
|
-
await
|
|
40094
|
+
await fs47.access(this.indexPath);
|
|
39565
40095
|
} catch {
|
|
39566
40096
|
await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
|
|
39567
40097
|
}
|
|
@@ -39590,7 +40120,7 @@ var ThreadStore = class {
|
|
|
39590
40120
|
async loadIndex() {
|
|
39591
40121
|
return withFileLock2(this.indexPath, async () => {
|
|
39592
40122
|
try {
|
|
39593
|
-
const content = await
|
|
40123
|
+
const content = await fs47.readFile(this.indexPath, "utf-8");
|
|
39594
40124
|
return JSON.parse(content);
|
|
39595
40125
|
} catch {
|
|
39596
40126
|
return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -39601,8 +40131,8 @@ var ThreadStore = class {
|
|
|
39601
40131
|
return withFileLock2(this.indexPath, async () => {
|
|
39602
40132
|
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
39603
40133
|
const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
|
|
39604
|
-
await
|
|
39605
|
-
await
|
|
40134
|
+
await fs47.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
|
|
40135
|
+
await fs47.rename(tempPath, this.indexPath);
|
|
39606
40136
|
});
|
|
39607
40137
|
}
|
|
39608
40138
|
// ==================== Git Info ====================
|
|
@@ -39672,7 +40202,7 @@ var ThreadStore = class {
|
|
|
39672
40202
|
messages: params.initialMessages || []
|
|
39673
40203
|
};
|
|
39674
40204
|
const historyPath = this.buildDatedThreadHistoryPath(threadId);
|
|
39675
|
-
await
|
|
40205
|
+
await fs47.mkdir(path51.dirname(historyPath), { recursive: true });
|
|
39676
40206
|
await this.saveHistoryAtPath(historyPath, history);
|
|
39677
40207
|
const index = await this.loadIndex();
|
|
39678
40208
|
index.threads.unshift({
|
|
@@ -39727,7 +40257,7 @@ var ThreadStore = class {
|
|
|
39727
40257
|
compressedSliceCount: source.history.compressedSliceCount
|
|
39728
40258
|
};
|
|
39729
40259
|
const historyPath = this.buildDatedThreadHistoryPath(newThreadId);
|
|
39730
|
-
await
|
|
40260
|
+
await fs47.mkdir(path51.dirname(historyPath), { recursive: true });
|
|
39731
40261
|
await this.saveHistoryAtPath(historyPath, history);
|
|
39732
40262
|
const index = await this.loadIndex();
|
|
39733
40263
|
index.threads.unshift({
|
|
@@ -39800,9 +40330,9 @@ var ThreadStore = class {
|
|
|
39800
40330
|
const entry = index.threads[entryIndex];
|
|
39801
40331
|
if (entry.status === "archived") return true;
|
|
39802
40332
|
const oldPath = entry.historyPath || this.getLegacyHistoryPath(threadId);
|
|
39803
|
-
const newPath =
|
|
40333
|
+
const newPath = path51.join(this.archiveDir, `${threadId}.jsonl`);
|
|
39804
40334
|
try {
|
|
39805
|
-
await
|
|
40335
|
+
await fs47.rename(oldPath, newPath);
|
|
39806
40336
|
} catch (e) {
|
|
39807
40337
|
if (e.code !== "ENOENT") throw e;
|
|
39808
40338
|
}
|
|
@@ -39823,11 +40353,11 @@ var ThreadStore = class {
|
|
|
39823
40353
|
if (entryIndex === -1) return false;
|
|
39824
40354
|
const entry = index.threads[entryIndex];
|
|
39825
40355
|
if (entry.status === "active") return true;
|
|
39826
|
-
const oldPath =
|
|
40356
|
+
const oldPath = path51.join(this.archiveDir, `${threadId}.jsonl`);
|
|
39827
40357
|
const newPath = this.buildDatedThreadHistoryPath(threadId);
|
|
39828
|
-
await
|
|
40358
|
+
await fs47.mkdir(path51.dirname(newPath), { recursive: true });
|
|
39829
40359
|
try {
|
|
39830
|
-
await
|
|
40360
|
+
await fs47.rename(oldPath, newPath);
|
|
39831
40361
|
} catch (e) {
|
|
39832
40362
|
if (e.code !== "ENOENT") throw e;
|
|
39833
40363
|
}
|
|
@@ -39848,7 +40378,7 @@ var ThreadStore = class {
|
|
|
39848
40378
|
if (entryIndex === -1) return false;
|
|
39849
40379
|
const entry = index.threads[entryIndex];
|
|
39850
40380
|
try {
|
|
39851
|
-
await
|
|
40381
|
+
await fs47.unlink(entry.historyPath);
|
|
39852
40382
|
} catch {
|
|
39853
40383
|
}
|
|
39854
40384
|
index.threads.splice(entryIndex, 1);
|
|
@@ -39858,28 +40388,28 @@ var ThreadStore = class {
|
|
|
39858
40388
|
}
|
|
39859
40389
|
// ==================== Histórico ====================
|
|
39860
40390
|
getLegacyHistoryPath(threadId) {
|
|
39861
|
-
return
|
|
40391
|
+
return path51.join(this.threadsDir, `${threadId}.jsonl`);
|
|
39862
40392
|
}
|
|
39863
40393
|
/** ~/.bluma/threads/YYYY/MM/DD/<threadId>.jsonl (data local de criação). */
|
|
39864
40394
|
buildDatedThreadHistoryPath(threadId, at = /* @__PURE__ */ new Date()) {
|
|
39865
40395
|
const y = String(at.getFullYear());
|
|
39866
40396
|
const mo = String(at.getMonth() + 1).padStart(2, "0");
|
|
39867
40397
|
const d = String(at.getDate()).padStart(2, "0");
|
|
39868
|
-
return
|
|
40398
|
+
return path51.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
|
|
39869
40399
|
}
|
|
39870
40400
|
async resolveHistoryPath(threadId) {
|
|
39871
40401
|
const index = await this.loadIndex();
|
|
39872
40402
|
const entry = index.threads.find((t) => t.threadId === threadId);
|
|
39873
40403
|
if (entry?.historyPath) {
|
|
39874
40404
|
try {
|
|
39875
|
-
await
|
|
40405
|
+
await fs47.access(entry.historyPath);
|
|
39876
40406
|
return entry.historyPath;
|
|
39877
40407
|
} catch {
|
|
39878
40408
|
}
|
|
39879
40409
|
}
|
|
39880
40410
|
const legacy = this.getLegacyHistoryPath(threadId);
|
|
39881
40411
|
try {
|
|
39882
|
-
await
|
|
40412
|
+
await fs47.access(legacy);
|
|
39883
40413
|
return legacy;
|
|
39884
40414
|
} catch {
|
|
39885
40415
|
return entry?.historyPath ?? legacy;
|
|
@@ -39896,9 +40426,9 @@ var ThreadStore = class {
|
|
|
39896
40426
|
for (const msg of history.messages) {
|
|
39897
40427
|
lines.push(JSON.stringify({ type: "message", ...msg }));
|
|
39898
40428
|
}
|
|
39899
|
-
await
|
|
40429
|
+
await fs47.mkdir(path51.dirname(historyPath), { recursive: true }).catch(() => {
|
|
39900
40430
|
});
|
|
39901
|
-
await
|
|
40431
|
+
await fs47.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
|
|
39902
40432
|
}
|
|
39903
40433
|
/**
|
|
39904
40434
|
* Guarda o histórico de uma thread
|
|
@@ -39920,7 +40450,7 @@ var ThreadStore = class {
|
|
|
39920
40450
|
].filter((p, i, arr) => Boolean(p) && arr.indexOf(p) === i);
|
|
39921
40451
|
for (const historyPath of pathsToTry) {
|
|
39922
40452
|
try {
|
|
39923
|
-
const content = await
|
|
40453
|
+
const content = await fs47.readFile(historyPath, "utf-8");
|
|
39924
40454
|
const lines = content.split("\n").filter(Boolean);
|
|
39925
40455
|
const history = {
|
|
39926
40456
|
threadId,
|
|
@@ -39955,7 +40485,7 @@ var ThreadStore = class {
|
|
|
39955
40485
|
const entry = index.threads.find((t) => t.threadId === threadId);
|
|
39956
40486
|
if (!entry) throw new Error(`Thread not found: ${threadId}`);
|
|
39957
40487
|
const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
|
|
39958
|
-
await
|
|
40488
|
+
await fs47.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
|
|
39959
40489
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
39960
40490
|
await this.saveIndex(index);
|
|
39961
40491
|
}
|
|
@@ -42828,16 +43358,16 @@ import latestVersion from "latest-version";
|
|
|
42828
43358
|
import semverGt from "semver/functions/gt.js";
|
|
42829
43359
|
import semverValid from "semver/functions/valid.js";
|
|
42830
43360
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
42831
|
-
import
|
|
42832
|
-
import
|
|
43361
|
+
import path52 from "path";
|
|
43362
|
+
import fs48 from "fs";
|
|
42833
43363
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
42834
43364
|
function findBlumaPackageJson(startDir) {
|
|
42835
43365
|
let dir = startDir;
|
|
42836
43366
|
for (let i = 0; i < 12; i++) {
|
|
42837
|
-
const candidate =
|
|
42838
|
-
if (
|
|
43367
|
+
const candidate = path52.join(dir, "package.json");
|
|
43368
|
+
if (fs48.existsSync(candidate)) {
|
|
42839
43369
|
try {
|
|
42840
|
-
const raw =
|
|
43370
|
+
const raw = fs48.readFileSync(candidate, "utf8");
|
|
42841
43371
|
const parsed = JSON.parse(raw);
|
|
42842
43372
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
42843
43373
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -42845,7 +43375,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
42845
43375
|
} catch {
|
|
42846
43376
|
}
|
|
42847
43377
|
}
|
|
42848
|
-
const parent =
|
|
43378
|
+
const parent = path52.dirname(dir);
|
|
42849
43379
|
if (parent === dir) break;
|
|
42850
43380
|
dir = parent;
|
|
42851
43381
|
}
|
|
@@ -42854,13 +43384,13 @@ function findBlumaPackageJson(startDir) {
|
|
|
42854
43384
|
function resolveInstalledBlumaPackage() {
|
|
42855
43385
|
const tried = /* @__PURE__ */ new Set();
|
|
42856
43386
|
const tryFrom = (dir) => {
|
|
42857
|
-
const abs =
|
|
43387
|
+
const abs = path52.resolve(dir);
|
|
42858
43388
|
if (tried.has(abs)) return null;
|
|
42859
43389
|
tried.add(abs);
|
|
42860
43390
|
return findBlumaPackageJson(abs);
|
|
42861
43391
|
};
|
|
42862
43392
|
try {
|
|
42863
|
-
const fromBundle = tryFrom(
|
|
43393
|
+
const fromBundle = tryFrom(path52.dirname(fileURLToPath6(import.meta.url)));
|
|
42864
43394
|
if (fromBundle) return fromBundle;
|
|
42865
43395
|
} catch {
|
|
42866
43396
|
}
|
|
@@ -42868,12 +43398,12 @@ function resolveInstalledBlumaPackage() {
|
|
|
42868
43398
|
if (argv1 && !argv1.startsWith("-")) {
|
|
42869
43399
|
try {
|
|
42870
43400
|
let resolved = argv1;
|
|
42871
|
-
if (
|
|
42872
|
-
resolved =
|
|
43401
|
+
if (path52.isAbsolute(argv1) && fs48.existsSync(argv1)) {
|
|
43402
|
+
resolved = fs48.realpathSync(argv1);
|
|
42873
43403
|
} else {
|
|
42874
|
-
resolved =
|
|
43404
|
+
resolved = path52.resolve(process.cwd(), argv1);
|
|
42875
43405
|
}
|
|
42876
|
-
const fromArgv = tryFrom(
|
|
43406
|
+
const fromArgv = tryFrom(path52.dirname(resolved));
|
|
42877
43407
|
if (fromArgv) return fromArgv;
|
|
42878
43408
|
} catch {
|
|
42879
43409
|
}
|
|
@@ -43692,16 +44222,16 @@ function usePlanMode() {
|
|
|
43692
44222
|
|
|
43693
44223
|
// src/app/hooks/useAgentMode.ts
|
|
43694
44224
|
import { useState as useState22, useEffect as useEffect21, useCallback as useCallback9 } from "react";
|
|
43695
|
-
import * as
|
|
43696
|
-
import * as
|
|
44225
|
+
import * as fs49 from "fs";
|
|
44226
|
+
import * as path53 from "path";
|
|
43697
44227
|
import { homedir as homedir3 } from "os";
|
|
43698
|
-
var SETTINGS_PATH =
|
|
44228
|
+
var SETTINGS_PATH = path53.join(homedir3(), ".bluma", "settings.json");
|
|
43699
44229
|
function readAgentModeFromFile() {
|
|
43700
44230
|
try {
|
|
43701
|
-
if (!
|
|
44231
|
+
if (!fs49.existsSync(SETTINGS_PATH)) {
|
|
43702
44232
|
return "default";
|
|
43703
44233
|
}
|
|
43704
|
-
const content =
|
|
44234
|
+
const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
|
|
43705
44235
|
const settings = JSON.parse(content);
|
|
43706
44236
|
return settings.agentMode || "default";
|
|
43707
44237
|
} catch (error) {
|
|
@@ -43720,16 +44250,16 @@ function useAgentMode() {
|
|
|
43720
44250
|
}, []);
|
|
43721
44251
|
const updateAgentMode = useCallback9((mode) => {
|
|
43722
44252
|
try {
|
|
43723
|
-
if (!
|
|
43724
|
-
|
|
44253
|
+
if (!fs49.existsSync(SETTINGS_PATH)) {
|
|
44254
|
+
fs49.mkdirSync(path53.dirname(SETTINGS_PATH), { recursive: true });
|
|
43725
44255
|
}
|
|
43726
44256
|
let settings = {};
|
|
43727
|
-
if (
|
|
43728
|
-
const content =
|
|
44257
|
+
if (fs49.existsSync(SETTINGS_PATH)) {
|
|
44258
|
+
const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
|
|
43729
44259
|
settings = JSON.parse(content);
|
|
43730
44260
|
}
|
|
43731
44261
|
settings.agentMode = mode;
|
|
43732
|
-
|
|
44262
|
+
fs49.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
|
|
43733
44263
|
setAgentMode(mode);
|
|
43734
44264
|
} catch (error) {
|
|
43735
44265
|
console.error("Failed to update agent mode:", error);
|
|
@@ -44992,10 +45522,10 @@ import React39 from "react";
|
|
|
44992
45522
|
import { memo as memo26, useCallback as useCallback11, useEffect as useEffect23, useMemo as useMemo8, useState as useState25 } from "react";
|
|
44993
45523
|
|
|
44994
45524
|
// src/app/agent/session_manager/session_resume_browser.ts
|
|
44995
|
-
import
|
|
44996
|
-
import { promises as
|
|
45525
|
+
import path54 from "path";
|
|
45526
|
+
import { promises as fs50 } from "fs";
|
|
44997
45527
|
function getSessionsRoot() {
|
|
44998
|
-
return
|
|
45528
|
+
return path54.join(getPreferredAppDir(), "sessions");
|
|
44999
45529
|
}
|
|
45000
45530
|
function normalizeSessionsCwd(cwd2) {
|
|
45001
45531
|
return cwd2.replace(/\\/g, "/").split("/").filter((p) => p && p !== "." && p !== "..").join("/");
|
|
@@ -45016,9 +45546,9 @@ async function sessionEntryFromFile(absPath, sessionId) {
|
|
|
45016
45546
|
let preview = "(no messages)";
|
|
45017
45547
|
let lastActivityMs = Date.now();
|
|
45018
45548
|
try {
|
|
45019
|
-
const st = await
|
|
45549
|
+
const st = await fs50.stat(absPath);
|
|
45020
45550
|
lastActivityMs = st.mtimeMs;
|
|
45021
|
-
const raw = await
|
|
45551
|
+
const raw = await fs50.readFile(absPath, "utf-8");
|
|
45022
45552
|
const data = JSON.parse(raw);
|
|
45023
45553
|
preview = previewFromHistory(data.conversation_history);
|
|
45024
45554
|
const iso = data.last_updated || data.created_at;
|
|
@@ -45046,15 +45576,15 @@ function compareDirNames(a, b) {
|
|
|
45046
45576
|
async function listSessionBrowserEntries(cwdRel) {
|
|
45047
45577
|
const cwd2 = normalizeSessionsCwd(cwdRel);
|
|
45048
45578
|
const root = getSessionsRoot();
|
|
45049
|
-
const absDir = cwd2 ?
|
|
45579
|
+
const absDir = cwd2 ? path54.join(root, ...cwd2.split("/")) : root;
|
|
45050
45580
|
const out = [];
|
|
45051
45581
|
if (cwd2) {
|
|
45052
45582
|
out.push({ kind: "up", label: ".." });
|
|
45053
45583
|
}
|
|
45054
45584
|
let dirents;
|
|
45055
45585
|
try {
|
|
45056
|
-
await
|
|
45057
|
-
dirents = await
|
|
45586
|
+
await fs50.mkdir(absDir, { recursive: true });
|
|
45587
|
+
dirents = await fs50.readdir(absDir, { withFileTypes: true });
|
|
45058
45588
|
} catch {
|
|
45059
45589
|
return out;
|
|
45060
45590
|
}
|
|
@@ -45063,7 +45593,7 @@ async function listSessionBrowserEntries(cwdRel) {
|
|
|
45063
45593
|
for (const e of dirents) {
|
|
45064
45594
|
const name = String(e.name);
|
|
45065
45595
|
if (name.startsWith(".")) continue;
|
|
45066
|
-
const full =
|
|
45596
|
+
const full = path54.join(absDir, name);
|
|
45067
45597
|
if (e.isDirectory()) {
|
|
45068
45598
|
dirNames.push(name);
|
|
45069
45599
|
} else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
|
|
@@ -45073,7 +45603,7 @@ async function listSessionBrowserEntries(cwdRel) {
|
|
|
45073
45603
|
dirNames.sort(compareDirNames);
|
|
45074
45604
|
const sessions = [];
|
|
45075
45605
|
for (const { name, full } of sessionFiles) {
|
|
45076
|
-
const sessionId =
|
|
45606
|
+
const sessionId = path54.basename(name, ".json");
|
|
45077
45607
|
sessions.push(await sessionEntryFromFile(full, sessionId));
|
|
45078
45608
|
}
|
|
45079
45609
|
sessions.sort((a, b) => {
|
|
@@ -45380,15 +45910,19 @@ function resolveResultUserMessage(lastAssistantMessage, lastAttachments) {
|
|
|
45380
45910
|
}
|
|
45381
45911
|
return "Tarefa conclu\xEDda.";
|
|
45382
45912
|
}
|
|
45383
|
-
function buildSuccessResultData(envelope, sessionId, lastAssistantMessage, lastAttachments) {
|
|
45913
|
+
function buildSuccessResultData(envelope, sessionId, lastAssistantMessage, lastAttachments, lastFactorShUrlApp) {
|
|
45384
45914
|
const message2 = resolveResultUserMessage(lastAssistantMessage, lastAttachments);
|
|
45385
|
-
|
|
45915
|
+
const data = {
|
|
45386
45916
|
message_id: envelope.message_id || sessionId,
|
|
45387
45917
|
action: envelope.action || "unknown",
|
|
45388
45918
|
last_assistant_message: message2,
|
|
45389
45919
|
message: message2,
|
|
45390
45920
|
attachments: lastAttachments
|
|
45391
45921
|
};
|
|
45922
|
+
if (lastFactorShUrlApp) {
|
|
45923
|
+
data[FACTOR_SH_URL_APP_FIELD] = lastFactorShUrlApp;
|
|
45924
|
+
}
|
|
45925
|
+
return data;
|
|
45392
45926
|
}
|
|
45393
45927
|
function finalizeSession(sessionId, status, metadata) {
|
|
45394
45928
|
updateSession(sessionId, {
|
|
@@ -45434,9 +45968,9 @@ async function runAgentMode() {
|
|
|
45434
45968
|
try {
|
|
45435
45969
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
45436
45970
|
const filePath = args[inputFileIndex + 1];
|
|
45437
|
-
rawPayload =
|
|
45971
|
+
rawPayload = fs51.readFileSync(filePath, "utf-8");
|
|
45438
45972
|
} else {
|
|
45439
|
-
rawPayload =
|
|
45973
|
+
rawPayload = fs51.readFileSync(0, "utf-8");
|
|
45440
45974
|
}
|
|
45441
45975
|
} catch (err) {
|
|
45442
45976
|
writeAgentEvent(registrySessionId, {
|
|
@@ -45472,6 +46006,15 @@ async function runAgentMode() {
|
|
|
45472
46006
|
if (envelope.metadata?.sandbox_name) {
|
|
45473
46007
|
process.env.BLUMA_SANDBOX_NAME = String(envelope.metadata.sandbox_name);
|
|
45474
46008
|
}
|
|
46009
|
+
} else if (process.env.BLUMA_SANDBOX_WORKSPACE?.trim()) {
|
|
46010
|
+
process.env.BLUMA_SANDBOX = "true";
|
|
46011
|
+
if (!process.env.BLUMA_SANDBOX_NAME?.trim()) {
|
|
46012
|
+
process.env.BLUMA_SANDBOX_NAME = "sandbox-api";
|
|
46013
|
+
}
|
|
46014
|
+
}
|
|
46015
|
+
const severinoUrl = (process.env.SEVERINO_URL || "").trim();
|
|
46016
|
+
if (!(process.env.FACTORAI_BASE_URL || process.env.FACTORAI_URL || "").trim() && severinoUrl) {
|
|
46017
|
+
process.env.FACTORAI_BASE_URL = severinoUrl;
|
|
45475
46018
|
}
|
|
45476
46019
|
const envelopeUserRequest = typeof envelope.context === "object" && envelope.context !== null ? String(
|
|
45477
46020
|
envelope.context.user_request ?? envelope.context.userRequest ?? ""
|
|
@@ -45510,6 +46053,7 @@ async function runAgentMode() {
|
|
|
45510
46053
|
});
|
|
45511
46054
|
let lastAssistantMessage = null;
|
|
45512
46055
|
let lastAttachments = null;
|
|
46056
|
+
let lastFactorShUrlApp = null;
|
|
45513
46057
|
let resultEmitted = false;
|
|
45514
46058
|
let agentRef = null;
|
|
45515
46059
|
let reasoningSequence = 0;
|
|
@@ -45560,6 +46104,16 @@ async function runAgentMode() {
|
|
|
45560
46104
|
if (Array.isArray(attachments)) {
|
|
45561
46105
|
lastAttachments = attachments.filter((p) => typeof p === "string");
|
|
45562
46106
|
}
|
|
46107
|
+
const factorUrl = messagePayload?.[FACTOR_SH_URL_APP_FIELD] ?? parsed?.[FACTOR_SH_URL_APP_FIELD] ?? parsed?.data?.[FACTOR_SH_URL_APP_FIELD];
|
|
46108
|
+
if (typeof factorUrl === "string" && factorUrl.trim()) {
|
|
46109
|
+
lastFactorShUrlApp = factorUrl.trim();
|
|
46110
|
+
}
|
|
46111
|
+
if (!lastFactorShUrlApp && extractMessageToolPayload(parsed)?.message_type === "result") {
|
|
46112
|
+
const fromArgs = payload?.arguments?.[FACTOR_SH_URL_APP_FIELD];
|
|
46113
|
+
if (typeof fromArgs === "string" && fromArgs.trim()) {
|
|
46114
|
+
lastFactorShUrlApp = fromArgs.trim();
|
|
46115
|
+
}
|
|
46116
|
+
}
|
|
45563
46117
|
} catch {
|
|
45564
46118
|
}
|
|
45565
46119
|
}
|
|
@@ -45576,7 +46130,8 @@ async function runAgentMode() {
|
|
|
45576
46130
|
envelope,
|
|
45577
46131
|
sessionId,
|
|
45578
46132
|
lastAssistantMessage,
|
|
45579
|
-
lastAttachments
|
|
46133
|
+
lastAttachments,
|
|
46134
|
+
lastFactorShUrlApp
|
|
45580
46135
|
)
|
|
45581
46136
|
});
|
|
45582
46137
|
finalizeSession(sessionId, "completed", { finishedBy: "done-event" });
|
|
@@ -45628,7 +46183,8 @@ async function runAgentMode() {
|
|
|
45628
46183
|
envelope,
|
|
45629
46184
|
sessionId,
|
|
45630
46185
|
lastAssistantMessage,
|
|
45631
|
-
lastAttachments
|
|
46186
|
+
lastAttachments,
|
|
46187
|
+
lastFactorShUrlApp
|
|
45632
46188
|
)
|
|
45633
46189
|
});
|
|
45634
46190
|
finalizeSession(sessionId, "completed", { finishedBy: "post-turn-fallback" });
|
|
@@ -45655,9 +46211,9 @@ async function runAgentMode() {
|
|
|
45655
46211
|
}
|
|
45656
46212
|
function readCliPackageVersion() {
|
|
45657
46213
|
try {
|
|
45658
|
-
const base =
|
|
45659
|
-
const pkgPath =
|
|
45660
|
-
const j = JSON.parse(
|
|
46214
|
+
const base = path55.dirname(fileURLToPath7(import.meta.url));
|
|
46215
|
+
const pkgPath = path55.join(base, "..", "package.json");
|
|
46216
|
+
const j = JSON.parse(fs51.readFileSync(pkgPath, "utf8"));
|
|
45661
46217
|
return String(j.version || "0.0.0");
|
|
45662
46218
|
} catch {
|
|
45663
46219
|
return "0.0.0";
|
|
@@ -45783,7 +46339,7 @@ function startBackgroundAgent() {
|
|
|
45783
46339
|
process.exit(1);
|
|
45784
46340
|
}
|
|
45785
46341
|
const filePath = args[inputFileIndex + 1];
|
|
45786
|
-
const rawPayload =
|
|
46342
|
+
const rawPayload = fs51.readFileSync(filePath, "utf-8");
|
|
45787
46343
|
const envelope = JSON.parse(rawPayload);
|
|
45788
46344
|
const sessionId = envelope.session_id || envelope.message_id || uuidv412();
|
|
45789
46345
|
registerSession({
|