lody 0.44.3-next.1 → 0.44.4-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +422 -181
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -45,13 +45,13 @@ import require$$1$6 from "string_decoder";
|
|
|
45
45
|
import * as http$2 from "http";
|
|
46
46
|
import http__default from "http";
|
|
47
47
|
import require$$1$7 from "https";
|
|
48
|
-
import require$$0$a, { execSync, exec, execFile as execFile$1 } from "child_process";
|
|
48
|
+
import require$$0$a, { execSync, exec, execFileSync, execFile as execFile$1 } from "child_process";
|
|
49
49
|
import { randomFillSync, randomUUID as randomUUID$1, createHash as createHash$1 } from "node:crypto";
|
|
50
50
|
import require$$0$b from "net";
|
|
51
51
|
import require$$4$3 from "tls";
|
|
52
52
|
import { i as imports, _ as __wbg_set_wasm$1, r as rawWasm, L as LoroDoc, E as EphemeralStoreWasm, U as UndoManager, c as callPendingEvents$3, a as LoroTree, b as LoroText, d as LoroMovableList, e as LoroList, f as LoroMap, g as __vite__initWasm, V as VersionVector, h as decodeImportBlobMeta, __tla as __tla_0 } from "./chunks/loro_wasm_bg-DgxHrrrp.js";
|
|
53
53
|
import * as fs$5 from "fs/promises";
|
|
54
|
-
import fs__default$1, { stat as stat$1, readFile as readFile$1 } from "fs/promises";
|
|
54
|
+
import fs__default$1, { stat as stat$1, readFile as readFile$1, statfs } from "fs/promises";
|
|
55
55
|
import fsPromises, { stat, open } from "node:fs/promises";
|
|
56
56
|
import { fileURLToPath } from "node:url";
|
|
57
57
|
import require$$2$7 from "assert";
|
|
@@ -36820,7 +36820,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36820
36820
|
return client;
|
|
36821
36821
|
}
|
|
36822
36822
|
const name = "lody";
|
|
36823
|
-
const version$4 = "0.44.
|
|
36823
|
+
const version$4 = "0.44.4-next.1";
|
|
36824
36824
|
const description = "Lody Agent CLI tool for managing remote command execution";
|
|
36825
36825
|
const type = "module";
|
|
36826
36826
|
const main$3 = "dist/index.js";
|
|
@@ -78436,6 +78436,7 @@ Task description:
|
|
|
78436
78436
|
"session_init_failed",
|
|
78437
78437
|
"session_restore_failed",
|
|
78438
78438
|
"session_not_found",
|
|
78439
|
+
"memory_pressure",
|
|
78439
78440
|
"acp_not_ready",
|
|
78440
78441
|
"agent_disconnected",
|
|
78441
78442
|
"turn_pre_prompt_failed",
|
|
@@ -120216,6 +120217,221 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120216
120217
|
}
|
|
120217
120218
|
return resolveAgentConfigEnvForSessionResume(repo, options.agentConfigId);
|
|
120218
120219
|
};
|
|
120220
|
+
function getMemoryPressureSnapshot() {
|
|
120221
|
+
const windowsStatus = getWindowsMemoryStatus();
|
|
120222
|
+
const systemAvailable = getSystemAvailableMemoryBytes(windowsStatus);
|
|
120223
|
+
const cgroupAvailable = getCgroupAvailableMemoryBytes();
|
|
120224
|
+
const availableMemoryBytes = cgroupAvailable !== null ? Math.min(systemAvailable, cgroupAvailable) : systemAvailable;
|
|
120225
|
+
const effectiveMemoryLimitBytes = getEffectiveMemoryLimitBytes();
|
|
120226
|
+
return {
|
|
120227
|
+
availableMemoryBytes,
|
|
120228
|
+
effectiveMemoryLimitBytes,
|
|
120229
|
+
...windowsStatus ? {
|
|
120230
|
+
availableCommitBytes: windowsStatus.availableCommitBytes,
|
|
120231
|
+
commitLimitBytes: windowsStatus.commitLimitBytes,
|
|
120232
|
+
committedBytes: windowsStatus.committedBytes
|
|
120233
|
+
} : {}
|
|
120234
|
+
};
|
|
120235
|
+
}
|
|
120236
|
+
function getEffectiveMemoryLimitBytes() {
|
|
120237
|
+
const totalMem = os__default.totalmem();
|
|
120238
|
+
const cgroupMax = getCgroupMemoryMaxBytes();
|
|
120239
|
+
if (cgroupMax !== null) {
|
|
120240
|
+
return Math.min(totalMem, cgroupMax);
|
|
120241
|
+
}
|
|
120242
|
+
return totalMem;
|
|
120243
|
+
}
|
|
120244
|
+
function getSystemAvailableMemoryBytes(windowsStatus) {
|
|
120245
|
+
if (windowsStatus) {
|
|
120246
|
+
return Math.max(windowsStatus.availableBytes, os__default.freemem());
|
|
120247
|
+
}
|
|
120248
|
+
const darwinAvailable = getDarwinAvailableMemoryBytes();
|
|
120249
|
+
if (darwinAvailable !== null) {
|
|
120250
|
+
return darwinAvailable;
|
|
120251
|
+
}
|
|
120252
|
+
try {
|
|
120253
|
+
const meminfo = readFileSync("/proc/meminfo", "utf8");
|
|
120254
|
+
const match5 = meminfo.match(/MemAvailable:\s+(\d+)/);
|
|
120255
|
+
if (match5?.[1]) {
|
|
120256
|
+
return parseInt(match5[1], 10) * 1024;
|
|
120257
|
+
}
|
|
120258
|
+
} catch {
|
|
120259
|
+
}
|
|
120260
|
+
return os__default.freemem();
|
|
120261
|
+
}
|
|
120262
|
+
function getWindowsMemoryStatus() {
|
|
120263
|
+
if (process.platform !== "win32") {
|
|
120264
|
+
return null;
|
|
120265
|
+
}
|
|
120266
|
+
try {
|
|
120267
|
+
const script = `
|
|
120268
|
+
$mem = Get-CimInstance -ClassName Win32_PerfFormattedData_PerfOS_Memory |
|
|
120269
|
+
Select-Object AvailableBytes, CommitLimit, CommittedBytes
|
|
120270
|
+
$mem | ConvertTo-Json -Compress
|
|
120271
|
+
`;
|
|
120272
|
+
const output = execFileSync("powershell.exe", [
|
|
120273
|
+
"-NoProfile",
|
|
120274
|
+
"-NonInteractive",
|
|
120275
|
+
"-ExecutionPolicy",
|
|
120276
|
+
"Bypass",
|
|
120277
|
+
"-Command",
|
|
120278
|
+
script
|
|
120279
|
+
], {
|
|
120280
|
+
encoding: "utf8"
|
|
120281
|
+
});
|
|
120282
|
+
return parseWindowsMemoryStatus(output);
|
|
120283
|
+
} catch {
|
|
120284
|
+
return null;
|
|
120285
|
+
}
|
|
120286
|
+
}
|
|
120287
|
+
function parseWindowsMemoryStatus(rawJson) {
|
|
120288
|
+
try {
|
|
120289
|
+
const parsed = JSON.parse(rawJson);
|
|
120290
|
+
const availableBytes = Number(parsed.AvailableBytes);
|
|
120291
|
+
const commitLimitBytes = Number(parsed.CommitLimit);
|
|
120292
|
+
const committedBytes = Number(parsed.CommittedBytes);
|
|
120293
|
+
if (!Number.isFinite(availableBytes) || availableBytes < 0 || !Number.isFinite(commitLimitBytes) || commitLimitBytes <= 0 || !Number.isFinite(committedBytes) || committedBytes < 0) {
|
|
120294
|
+
return null;
|
|
120295
|
+
}
|
|
120296
|
+
return {
|
|
120297
|
+
availableBytes,
|
|
120298
|
+
commitLimitBytes,
|
|
120299
|
+
committedBytes,
|
|
120300
|
+
availableCommitBytes: Math.max(0, commitLimitBytes - committedBytes)
|
|
120301
|
+
};
|
|
120302
|
+
} catch {
|
|
120303
|
+
return null;
|
|
120304
|
+
}
|
|
120305
|
+
}
|
|
120306
|
+
function getDarwinAvailableMemoryBytes() {
|
|
120307
|
+
if (process.platform !== "darwin") {
|
|
120308
|
+
return null;
|
|
120309
|
+
}
|
|
120310
|
+
try {
|
|
120311
|
+
const vmStatOutput = execFileSync("vm_stat", {
|
|
120312
|
+
encoding: "utf8"
|
|
120313
|
+
});
|
|
120314
|
+
const parsed = parseDarwinAvailableMemoryBytes(vmStatOutput);
|
|
120315
|
+
if (parsed !== null) {
|
|
120316
|
+
return Math.max(parsed, os__default.freemem());
|
|
120317
|
+
}
|
|
120318
|
+
} catch {
|
|
120319
|
+
}
|
|
120320
|
+
return null;
|
|
120321
|
+
}
|
|
120322
|
+
function parseDarwinAvailableMemoryBytes(vmStatOutput) {
|
|
120323
|
+
const pageSizeMatch = vmStatOutput.match(/page size of\s+(\d+)\s+bytes/i);
|
|
120324
|
+
if (!pageSizeMatch?.[1]) {
|
|
120325
|
+
return null;
|
|
120326
|
+
}
|
|
120327
|
+
const pageSize = parseInt(pageSizeMatch[1], 10);
|
|
120328
|
+
if (!Number.isFinite(pageSize) || pageSize <= 0) {
|
|
120329
|
+
return null;
|
|
120330
|
+
}
|
|
120331
|
+
const counters = /* @__PURE__ */ new Map();
|
|
120332
|
+
for (const rawLine of vmStatOutput.split("\n")) {
|
|
120333
|
+
const line3 = rawLine.trim();
|
|
120334
|
+
const match5 = line3.match(/^"?([^":]+?)"?:\s+(\d+)\.?$/);
|
|
120335
|
+
if (!match5?.[1] || !match5[2]) {
|
|
120336
|
+
continue;
|
|
120337
|
+
}
|
|
120338
|
+
counters.set(match5[1].toLowerCase(), parseInt(match5[2], 10));
|
|
120339
|
+
}
|
|
120340
|
+
const freePages = counters.get("pages free") ?? 0;
|
|
120341
|
+
const speculativePages = counters.get("pages speculative") ?? 0;
|
|
120342
|
+
const purgeablePages = counters.get("pages purgeable") ?? 0;
|
|
120343
|
+
const inactivePages = counters.get("pages inactive") ?? 0;
|
|
120344
|
+
const fileBackedPages = counters.get("file-backed pages");
|
|
120345
|
+
if (freePages === 0 && speculativePages === 0 && purgeablePages === 0 && inactivePages === 0) {
|
|
120346
|
+
return null;
|
|
120347
|
+
}
|
|
120348
|
+
const reclaimableCachedPages = fileBackedPages !== void 0 ? Math.min(fileBackedPages, inactivePages) : inactivePages;
|
|
120349
|
+
const availablePages = freePages + speculativePages + purgeablePages + reclaimableCachedPages;
|
|
120350
|
+
return availablePages * pageSize;
|
|
120351
|
+
}
|
|
120352
|
+
function getCgroupMemoryMaxBytes() {
|
|
120353
|
+
try {
|
|
120354
|
+
const cgroupPath = readSelfCgroupPath();
|
|
120355
|
+
if (cgroupPath === null) return null;
|
|
120356
|
+
let tightest = null;
|
|
120357
|
+
let current2 = cgroupPath;
|
|
120358
|
+
for (let depth = 0; depth < 20; depth++) {
|
|
120359
|
+
const memMaxPath = `/sys/fs/cgroup${current2 === "/" ? "" : current2}/memory.max`;
|
|
120360
|
+
const raw = readFileSafe(memMaxPath);
|
|
120361
|
+
if (raw !== null) {
|
|
120362
|
+
const trimmed = raw.trim();
|
|
120363
|
+
if (trimmed !== "max") {
|
|
120364
|
+
const value = parseInt(trimmed, 10);
|
|
120365
|
+
if (Number.isFinite(value) && value > 0) {
|
|
120366
|
+
tightest = tightest === null ? value : Math.min(tightest, value);
|
|
120367
|
+
}
|
|
120368
|
+
}
|
|
120369
|
+
}
|
|
120370
|
+
if (current2 === "/" || current2 === "") break;
|
|
120371
|
+
const parent = current2.substring(0, current2.lastIndexOf("/")) || "/";
|
|
120372
|
+
if (parent === current2) break;
|
|
120373
|
+
current2 = parent;
|
|
120374
|
+
}
|
|
120375
|
+
return tightest;
|
|
120376
|
+
} catch {
|
|
120377
|
+
return null;
|
|
120378
|
+
}
|
|
120379
|
+
}
|
|
120380
|
+
function getCgroupAvailableMemoryBytes() {
|
|
120381
|
+
try {
|
|
120382
|
+
const cgroupPath = readSelfCgroupPath();
|
|
120383
|
+
if (cgroupPath === null) return null;
|
|
120384
|
+
let tightestMax = null;
|
|
120385
|
+
let tightestPath = null;
|
|
120386
|
+
let current2 = cgroupPath;
|
|
120387
|
+
for (let depth = 0; depth < 20; depth++) {
|
|
120388
|
+
const prefix = `/sys/fs/cgroup${current2 === "/" ? "" : current2}`;
|
|
120389
|
+
const raw = readFileSafe(`${prefix}/memory.max`);
|
|
120390
|
+
if (raw !== null) {
|
|
120391
|
+
const trimmed = raw.trim();
|
|
120392
|
+
if (trimmed !== "max") {
|
|
120393
|
+
const value = parseInt(trimmed, 10);
|
|
120394
|
+
if (Number.isFinite(value) && value > 0) {
|
|
120395
|
+
if (tightestMax === null || value < tightestMax) {
|
|
120396
|
+
tightestMax = value;
|
|
120397
|
+
tightestPath = prefix;
|
|
120398
|
+
}
|
|
120399
|
+
}
|
|
120400
|
+
}
|
|
120401
|
+
}
|
|
120402
|
+
if (current2 === "/" || current2 === "") break;
|
|
120403
|
+
const parent = current2.substring(0, current2.lastIndexOf("/")) || "/";
|
|
120404
|
+
if (parent === current2) break;
|
|
120405
|
+
current2 = parent;
|
|
120406
|
+
}
|
|
120407
|
+
if (tightestMax === null || tightestPath === null) return null;
|
|
120408
|
+
const currentRaw = readFileSafe(`${tightestPath}/memory.current`);
|
|
120409
|
+
if (currentRaw === null) return null;
|
|
120410
|
+
const currentUsage = parseInt(currentRaw.trim(), 10);
|
|
120411
|
+
if (!Number.isFinite(currentUsage)) return null;
|
|
120412
|
+
return Math.max(0, tightestMax - currentUsage);
|
|
120413
|
+
} catch {
|
|
120414
|
+
return null;
|
|
120415
|
+
}
|
|
120416
|
+
}
|
|
120417
|
+
function readSelfCgroupPath() {
|
|
120418
|
+
try {
|
|
120419
|
+
const content = readFileSync("/proc/self/cgroup", "utf8");
|
|
120420
|
+
const line3 = content.split("\n").map((l) => l.trim()).find((l) => l.startsWith("0::"));
|
|
120421
|
+
if (!line3) return null;
|
|
120422
|
+
const cgroupPath = line3.slice(3).trim();
|
|
120423
|
+
return cgroupPath || "/";
|
|
120424
|
+
} catch {
|
|
120425
|
+
return null;
|
|
120426
|
+
}
|
|
120427
|
+
}
|
|
120428
|
+
function readFileSafe(filePath) {
|
|
120429
|
+
try {
|
|
120430
|
+
return readFileSync(filePath, "utf8");
|
|
120431
|
+
} catch {
|
|
120432
|
+
return null;
|
|
120433
|
+
}
|
|
120434
|
+
}
|
|
120219
120435
|
class SessionTurnCancelled extends TaggedError("SessionTurnCancelled") {
|
|
120220
120436
|
}
|
|
120221
120437
|
class SessionTurnHalted extends TaggedError("SessionTurnHalted") {
|
|
@@ -120236,6 +120452,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120236
120452
|
AUTH_REQUIRED: -32e3,
|
|
120237
120453
|
RESOURCE_NOT_FOUND: -32002
|
|
120238
120454
|
};
|
|
120455
|
+
const BYTES_PER_GIB = 1024 * 1024 * 1024;
|
|
120239
120456
|
const shouldRedactEnvKey = (key2) => /token|secret|password|passwd|key/i.test(key2);
|
|
120240
120457
|
const redactEnvForLog = (env2) => {
|
|
120241
120458
|
if (!env2) {
|
|
@@ -120271,6 +120488,46 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120271
120488
|
canceledTurnBySession = /* @__PURE__ */ new Map();
|
|
120272
120489
|
currentTurnBySession = /* @__PURE__ */ new Map();
|
|
120273
120490
|
turnRuntimeBySession = /* @__PURE__ */ new Map();
|
|
120491
|
+
formatGiB(bytes) {
|
|
120492
|
+
if (bytes === null || !Number.isFinite(bytes) || bytes < 0) {
|
|
120493
|
+
return "unavailable";
|
|
120494
|
+
}
|
|
120495
|
+
return `${(bytes / BYTES_PER_GIB).toFixed(1)}GB`;
|
|
120496
|
+
}
|
|
120497
|
+
formatPercent(value) {
|
|
120498
|
+
if (value === null || !Number.isFinite(value) || value < 0) {
|
|
120499
|
+
return "unavailable";
|
|
120500
|
+
}
|
|
120501
|
+
return `${value.toFixed(1)}%`;
|
|
120502
|
+
}
|
|
120503
|
+
async getFreeDiskBytes() {
|
|
120504
|
+
try {
|
|
120505
|
+
const stats = await statfs(os__default.homedir());
|
|
120506
|
+
const bsize = typeof stats.bsize === "bigint" ? Number(stats.bsize) : stats.bsize;
|
|
120507
|
+
const bavail = typeof stats.bavail === "bigint" ? Number(stats.bavail) : stats.bavail;
|
|
120508
|
+
if (!Number.isFinite(bsize) || !Number.isFinite(bavail) || bsize <= 0 || bavail < 0) {
|
|
120509
|
+
return null;
|
|
120510
|
+
}
|
|
120511
|
+
return bsize * bavail;
|
|
120512
|
+
} catch {
|
|
120513
|
+
return null;
|
|
120514
|
+
}
|
|
120515
|
+
}
|
|
120516
|
+
async logTurnStartResources(sessionId, mode2) {
|
|
120517
|
+
try {
|
|
120518
|
+
const memorySnapshot = getMemoryPressureSnapshot();
|
|
120519
|
+
const availableMemoryBytes = memorySnapshot.availableMemoryBytes;
|
|
120520
|
+
const memoryFreePercent = memorySnapshot.effectiveMemoryLimitBytes > 0 ? availableMemoryBytes / memorySnapshot.effectiveMemoryLimitBytes * 100 : null;
|
|
120521
|
+
const [resources, freeDiskBytes] = await Promise.all([
|
|
120522
|
+
this.deps.collectMachineResources().catch(() => null),
|
|
120523
|
+
this.getFreeDiskBytes()
|
|
120524
|
+
]);
|
|
120525
|
+
const cpuUsagePercent = resources?.cpuUsagePercent ?? null;
|
|
120526
|
+
this.deps.logger.info(`[${sessionId}] Turn start resources (mode=${mode2}): memoryAvailable=${this.formatGiB(availableMemoryBytes)} memoryAvailablePercent=${this.formatPercent(memoryFreePercent)} cpuUsage=${this.formatPercent(cpuUsagePercent)} diskFree=${this.formatGiB(freeDiskBytes)}`);
|
|
120527
|
+
} catch (error2) {
|
|
120528
|
+
this.deps.logger.debug(`[${sessionId}] Failed to log turn start resources: ${formatErrorMessage(error2)}`);
|
|
120529
|
+
}
|
|
120530
|
+
}
|
|
120274
120531
|
createTurnRuntime(sessionId, turnId, userTurnId, session) {
|
|
120275
120532
|
return {
|
|
120276
120533
|
sessionId,
|
|
@@ -120453,10 +120710,19 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120453
120710
|
async recordKnownChatFailure(options) {
|
|
120454
120711
|
await this.deps.recordChatFailure(options.sessionDoc, options.reason, options.message);
|
|
120455
120712
|
if (options.userTurnId) {
|
|
120456
|
-
await this.
|
|
120713
|
+
await this.markTurnFailed(options.sessionId, options.sessionDoc, options.userTurnId);
|
|
120457
120714
|
}
|
|
120458
120715
|
await options.sessionDoc.setStatus(SessionStatusFactory.idle());
|
|
120459
120716
|
}
|
|
120717
|
+
formatMemoryPressureWarningMessage(result) {
|
|
120718
|
+
const availableMb = Math.round(result.availableMemoryBytes / 1024 / 1024);
|
|
120719
|
+
const thresholdMb = Math.round(result.thresholdBytes / 1024 / 1024);
|
|
120720
|
+
const commitText = result.availableCommitBytes !== void 0 && (result.pressureReason === "commit" || result.pressureReason === "physical_and_commit") && result.commitThresholdBytes !== void 0 ? ` Commit headroom is ${Math.round(result.availableCommitBytes / 1024 / 1024)}MB (threshold: ${Math.round(result.commitThresholdBytes / 1024 / 1024)}MB).` : "";
|
|
120721
|
+
return `The machine is under memory pressure (${availableMb}MB available, ${thresholdMb}MB required to start a turn). Proceeding anyway because the new turn may be used to free resources.${commitText}`;
|
|
120722
|
+
}
|
|
120723
|
+
warnOnMemoryPressure(sessionId, result) {
|
|
120724
|
+
this.deps.logger.warn(`[${sessionId}] ${this.formatMemoryPressureWarningMessage(result)}`);
|
|
120725
|
+
}
|
|
120460
120726
|
recordKnownChatFailureAndHaltEffect(options) {
|
|
120461
120727
|
return this.tryPromise(() => this.recordKnownChatFailure(options)).pipe(flatMap$1(() => fail(new SessionTurnHalted({
|
|
120462
120728
|
sessionId: options.sessionId,
|
|
@@ -120480,7 +120746,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120480
120746
|
}
|
|
120481
120747
|
this.deps.logger.error(options.describe(options.error), options.error);
|
|
120482
120748
|
if (options.userTurnId) {
|
|
120483
|
-
await this.
|
|
120749
|
+
await this.markTurnFailed(options.sessionId, options.sessionDoc, options.userTurnId);
|
|
120484
120750
|
}
|
|
120485
120751
|
await this.handleTurnError(options.sessionId, options.sessionDoc, options.error);
|
|
120486
120752
|
await options.onUnhandledError?.(options.error);
|
|
@@ -120816,8 +121082,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120816
121082
|
await this.setUserTurnStatus(sessionDoc, userTurnId, "processing");
|
|
120817
121083
|
await this.upsertSessionMeta(sessionId, {
|
|
120818
121084
|
latestUserMsgId: userTurnId,
|
|
120819
|
-
processingUserMsgId: userTurnId
|
|
120820
|
-
dispatchError: void 0
|
|
121085
|
+
processingUserMsgId: userTurnId
|
|
120821
121086
|
});
|
|
120822
121087
|
}
|
|
120823
121088
|
async clearDispatchProcessing(sessionId) {
|
|
@@ -120866,34 +121131,31 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120866
121131
|
await this.upsertSessionMeta(sessionId, {
|
|
120867
121132
|
latestUserMsgId: existingMeta?.latestUserMsgId ?? cancelledUserMsgId,
|
|
120868
121133
|
lastHandledUserMsgId: cancelledUserMsgId,
|
|
120869
|
-
processingUserMsgId: void 0
|
|
120870
|
-
dispatchError: void 0
|
|
121134
|
+
processingUserMsgId: void 0
|
|
120871
121135
|
});
|
|
120872
121136
|
return;
|
|
120873
121137
|
}
|
|
120874
121138
|
await this.clearDispatchProcessing(sessionId);
|
|
120875
121139
|
}
|
|
121140
|
+
resolveLatestUserMsgIdForTerminalTurn(meta, terminalUserTurnId) {
|
|
121141
|
+
return meta?.latestUserMsgId && meta.latestUserMsgId !== terminalUserTurnId ? meta.latestUserMsgId : terminalUserTurnId;
|
|
121142
|
+
}
|
|
120876
121143
|
async setDispatchHandled(sessionId, sessionDoc, userTurnId) {
|
|
121144
|
+
const existingMeta = await this.getSessionMeta(sessionId);
|
|
120877
121145
|
await this.setUserTurnStatus(sessionDoc, userTurnId, "handled");
|
|
120878
121146
|
await this.upsertSessionMeta(sessionId, {
|
|
120879
|
-
latestUserMsgId: userTurnId,
|
|
121147
|
+
latestUserMsgId: this.resolveLatestUserMsgIdForTerminalTurn(existingMeta, userTurnId),
|
|
120880
121148
|
lastHandledUserMsgId: userTurnId,
|
|
120881
|
-
processingUserMsgId: void 0
|
|
120882
|
-
dispatchError: void 0
|
|
121149
|
+
processingUserMsgId: void 0
|
|
120883
121150
|
});
|
|
120884
121151
|
}
|
|
120885
|
-
async
|
|
121152
|
+
async markTurnFailed(sessionId, sessionDoc, userTurnId) {
|
|
121153
|
+
const existingMeta = await this.getSessionMeta(sessionId);
|
|
120886
121154
|
await this.setUserTurnStatus(sessionDoc, userTurnId, "failed");
|
|
120887
121155
|
await this.upsertSessionMeta(sessionId, {
|
|
120888
|
-
latestUserMsgId: userTurnId,
|
|
120889
|
-
|
|
120890
|
-
|
|
120891
|
-
code: code2,
|
|
120892
|
-
...message ? {
|
|
120893
|
-
message
|
|
120894
|
-
} : {},
|
|
120895
|
-
at: getServerNow()
|
|
120896
|
-
}
|
|
121156
|
+
latestUserMsgId: this.resolveLatestUserMsgIdForTerminalTurn(existingMeta, userTurnId),
|
|
121157
|
+
lastHandledUserMsgId: userTurnId,
|
|
121158
|
+
processingUserMsgId: void 0
|
|
120897
121159
|
});
|
|
120898
121160
|
}
|
|
120899
121161
|
resolveGitHubProjectBranch(meta, preferredBranch) {
|
|
@@ -120987,15 +121249,18 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120987
121249
|
}
|
|
120988
121250
|
async continueSession(message) {
|
|
120989
121251
|
const { sessionId, acpSessionConfig, userId, userName, userEmail, userTurnId } = message;
|
|
120990
|
-
await this.deps.evictForMemoryPressure(sessionId);
|
|
121252
|
+
const memoryPressureResult = await this.deps.evictForMemoryPressure(sessionId);
|
|
121253
|
+
if (memoryPressureResult.stillUnderPressure) {
|
|
121254
|
+
this.warnOnMemoryPressure(sessionId, memoryPressureResult);
|
|
121255
|
+
}
|
|
121256
|
+
await this.logTurnStartResources(sessionId, "continue");
|
|
121257
|
+
const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
|
|
120991
121258
|
this.deps.touchSession(sessionId);
|
|
120992
121259
|
this.deps.logger.info(`Session chat received: ${sessionId}`);
|
|
120993
121260
|
this.deps.logger.debug(`[${sessionId}] Received chat request (userTurnId=${userTurnId})`);
|
|
120994
121261
|
await this.upsertSessionMeta(sessionId, {
|
|
120995
|
-
latestUserMsgId: userTurnId
|
|
120996
|
-
dispatchError: void 0
|
|
121262
|
+
latestUserMsgId: userTurnId
|
|
120997
121263
|
});
|
|
120998
|
-
const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
|
|
120999
121264
|
const incomingProjectBranch = message.project?.branch?.trim();
|
|
121000
121265
|
if (incomingProjectBranch) {
|
|
121001
121266
|
await sessionDoc.setBaseBranch(incomingProjectBranch);
|
|
@@ -121332,7 +121597,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
121332
121597
|
}));
|
|
121333
121598
|
}
|
|
121334
121599
|
async startSession(message) {
|
|
121335
|
-
await this.deps.evictForMemoryPressure(message.sessionId);
|
|
121600
|
+
const memoryPressureResult = await this.deps.evictForMemoryPressure(message.sessionId);
|
|
121336
121601
|
const { sessionId, acpSessionConfig, workspaceId, env: env2 } = message;
|
|
121337
121602
|
const userTurnId = typeof message.userTurnId === "string" && message.userTurnId.trim() ? message.userTurnId.trim() : void 0;
|
|
121338
121603
|
const project = message.project;
|
|
@@ -121345,6 +121610,10 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
121345
121610
|
const promptText = agentConfig.prompt ?? "";
|
|
121346
121611
|
const promptBytes = Buffer.byteLength(promptText, "utf8");
|
|
121347
121612
|
const promptPreview = promptText.length > 200 ? `${promptText.slice(0, 200)}\u2026` : promptText;
|
|
121613
|
+
if (memoryPressureResult.stillUnderPressure) {
|
|
121614
|
+
this.warnOnMemoryPressure(sessionId, memoryPressureResult);
|
|
121615
|
+
}
|
|
121616
|
+
await this.logTurnStartResources(sessionId, "start");
|
|
121348
121617
|
const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
|
|
121349
121618
|
const existingMeta = await sessionDoc.getMetaState();
|
|
121350
121619
|
const fromFeedbackPostId = message.meta?.fromFeedbackPostId?.trim() || existingMeta?.fromFeedbackPostId?.trim() || void 0;
|
|
@@ -121368,12 +121637,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
121368
121637
|
fromFeedbackPostId
|
|
121369
121638
|
};
|
|
121370
121639
|
this.deps.logger.debug(`[${sessionId}] session/create summary`, configForLog);
|
|
121371
|
-
if (userTurnId) {
|
|
121372
|
-
await this.upsertSessionMeta(sessionId, {
|
|
121373
|
-
latestUserMsgId: userTurnId,
|
|
121374
|
-
dispatchError: void 0
|
|
121375
|
-
});
|
|
121376
|
-
}
|
|
121377
121640
|
const sessionConfig = {
|
|
121378
121641
|
sessionId,
|
|
121379
121642
|
workspaceId,
|
|
@@ -121394,6 +121657,11 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
121394
121657
|
if (project) {
|
|
121395
121658
|
await sessionDoc.setProject(project);
|
|
121396
121659
|
}
|
|
121660
|
+
if (userTurnId) {
|
|
121661
|
+
await this.upsertSessionMeta(sessionId, {
|
|
121662
|
+
latestUserMsgId: userTurnId
|
|
121663
|
+
});
|
|
121664
|
+
}
|
|
121397
121665
|
if (branch) {
|
|
121398
121666
|
await sessionDoc.setBaseBranch(branch);
|
|
121399
121667
|
}
|
|
@@ -122057,9 +122325,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
122057
122325
|
if (meta.lastCanceledTurn && meta.lastCanceledTurn !== this.cancelSeenTurn.get(meta.id)) {
|
|
122058
122326
|
return true;
|
|
122059
122327
|
}
|
|
122060
|
-
if (meta.dispatchError?.code === SessionDispatchWatcher.DISPATCH_HISTORY_SYNC_TIMEOUT_CODE) {
|
|
122061
|
-
return false;
|
|
122062
|
-
}
|
|
122063
122328
|
if (!meta.lastHandledUserMsgId) {
|
|
122064
122329
|
return true;
|
|
122065
122330
|
}
|
|
@@ -122155,12 +122420,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
122155
122420
|
await this.deps.workspaceDocument.repo.upsertDocMeta?.(getSessionRoomId(sessionId), {
|
|
122156
122421
|
latestUserMsgId: userTurnId,
|
|
122157
122422
|
lastHandledUserMsgId: userTurnId,
|
|
122158
|
-
processingUserMsgId: void 0
|
|
122159
|
-
dispatchError: {
|
|
122160
|
-
code: reason === "cli_token_invalid" ? "cli_token_invalid" : "machine_access_denied",
|
|
122161
|
-
message,
|
|
122162
|
-
at: getServerNow()
|
|
122163
|
-
}
|
|
122423
|
+
processingUserMsgId: void 0
|
|
122164
122424
|
});
|
|
122165
122425
|
await sessionDoc.setStatus(SessionStatusFactory.idle());
|
|
122166
122426
|
}
|
|
@@ -122290,7 +122550,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
122290
122550
|
static HISTORY_SYNC_WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
122291
122551
|
static HISTORY_RECONNECT_JITTER_MIN_MS = 500;
|
|
122292
122552
|
static HISTORY_RECONNECT_JITTER_MAX_MS = 1500;
|
|
122293
|
-
static DISPATCH_HISTORY_SYNC_TIMEOUT_CODE = "dispatch_recovery_unhealthy";
|
|
122294
122553
|
static setUnrefTimeout(callback, delayMs) {
|
|
122295
122554
|
const timer2 = setTimeout(callback, delayMs);
|
|
122296
122555
|
if (typeof timer2 === "object" && "unref" in timer2) {
|
|
@@ -122450,12 +122709,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
122450
122709
|
}
|
|
122451
122710
|
const pendingUserMsgId = meta.processingUserMsgId ?? meta.latestUserMsgId ?? meta.lastHandledUserMsgId;
|
|
122452
122711
|
const recoveryPatch = {
|
|
122453
|
-
status: SessionStatusFactory.idle()
|
|
122454
|
-
dispatchError: {
|
|
122455
|
-
code: SessionDispatchWatcher.DISPATCH_HISTORY_SYNC_TIMEOUT_CODE,
|
|
122456
|
-
message: "Dispatch recovery could not reconnect to this session after 5 minutes. Send a new message to retry.",
|
|
122457
|
-
at: getServerNow()
|
|
122458
|
-
}
|
|
122712
|
+
status: SessionStatusFactory.idle()
|
|
122459
122713
|
};
|
|
122460
122714
|
if (pendingUserMsgId) {
|
|
122461
122715
|
recoveryPatch.lastHandledUserMsgId = pendingUserMsgId;
|
|
@@ -123208,8 +123462,14 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
123208
123462
|
permissionRequestStartTimes = /* @__PURE__ */ new Map();
|
|
123209
123463
|
machineHeartbeatTimer = null;
|
|
123210
123464
|
static MACHINE_HEARTBEAT_INTERVAL_MS = 2e4;
|
|
123211
|
-
evictForMemoryPressureFn = async () => {
|
|
123212
|
-
|
|
123465
|
+
evictForMemoryPressureFn = async () => ({
|
|
123466
|
+
availableMemoryBytes: 0,
|
|
123467
|
+
thresholdBytes: 0,
|
|
123468
|
+
hadMemoryPressure: false,
|
|
123469
|
+
stillUnderPressure: false,
|
|
123470
|
+
evictedSessionIds: [],
|
|
123471
|
+
pressureReason: null
|
|
123472
|
+
});
|
|
123213
123473
|
executionService;
|
|
123214
123474
|
sessionDispatchWatcher;
|
|
123215
123475
|
autoPromptRunner;
|
|
@@ -125221,6 +125481,16 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125221
125481
|
hasPendingUpdates(sessionId) {
|
|
125222
125482
|
return this.store.has(sessionId) && this.store.get(sessionId).acpUpdateBuffer.length > 0;
|
|
125223
125483
|
}
|
|
125484
|
+
async hasPendingUserWork(sessionId) {
|
|
125485
|
+
const meta = (await this.workspaceDocument.repo.getDocMeta(getSessionRoomId(sessionId)))?.meta;
|
|
125486
|
+
if (!meta) {
|
|
125487
|
+
return false;
|
|
125488
|
+
}
|
|
125489
|
+
if (meta.processingUserMsgId) {
|
|
125490
|
+
return true;
|
|
125491
|
+
}
|
|
125492
|
+
return Boolean(meta.latestUserMsgId && meta.latestUserMsgId !== meta.lastHandledUserMsgId);
|
|
125493
|
+
}
|
|
125224
125494
|
isArchiveInFlight(sessionId) {
|
|
125225
125495
|
return this.archiveInFlight.has(sessionId);
|
|
125226
125496
|
}
|
|
@@ -125577,116 +125847,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125577
125847
|
}
|
|
125578
125848
|
}
|
|
125579
125849
|
}
|
|
125580
|
-
function getAvailableMemoryBytes() {
|
|
125581
|
-
const systemAvailable = getSystemAvailableMemoryBytes();
|
|
125582
|
-
const cgroupAvailable = getCgroupAvailableMemoryBytes();
|
|
125583
|
-
if (cgroupAvailable !== null) {
|
|
125584
|
-
return Math.min(systemAvailable, cgroupAvailable);
|
|
125585
|
-
}
|
|
125586
|
-
return systemAvailable;
|
|
125587
|
-
}
|
|
125588
|
-
function getEffectiveMemoryLimitBytes() {
|
|
125589
|
-
const totalMem = os__default.totalmem();
|
|
125590
|
-
const cgroupMax = getCgroupMemoryMaxBytes();
|
|
125591
|
-
if (cgroupMax !== null) {
|
|
125592
|
-
return Math.min(totalMem, cgroupMax);
|
|
125593
|
-
}
|
|
125594
|
-
return totalMem;
|
|
125595
|
-
}
|
|
125596
|
-
function getSystemAvailableMemoryBytes() {
|
|
125597
|
-
try {
|
|
125598
|
-
const meminfo = readFileSync("/proc/meminfo", "utf8");
|
|
125599
|
-
const match5 = meminfo.match(/MemAvailable:\s+(\d+)/);
|
|
125600
|
-
if (match5?.[1]) {
|
|
125601
|
-
return parseInt(match5[1], 10) * 1024;
|
|
125602
|
-
}
|
|
125603
|
-
} catch {
|
|
125604
|
-
}
|
|
125605
|
-
return os__default.freemem();
|
|
125606
|
-
}
|
|
125607
|
-
function getCgroupMemoryMaxBytes() {
|
|
125608
|
-
try {
|
|
125609
|
-
const cgroupPath = readSelfCgroupPath();
|
|
125610
|
-
if (cgroupPath === null) return null;
|
|
125611
|
-
let tightest = null;
|
|
125612
|
-
let current2 = cgroupPath;
|
|
125613
|
-
for (let depth = 0; depth < 20; depth++) {
|
|
125614
|
-
const memMaxPath = `/sys/fs/cgroup${current2 === "/" ? "" : current2}/memory.max`;
|
|
125615
|
-
const raw = readFileSafe(memMaxPath);
|
|
125616
|
-
if (raw !== null) {
|
|
125617
|
-
const trimmed = raw.trim();
|
|
125618
|
-
if (trimmed !== "max") {
|
|
125619
|
-
const value = parseInt(trimmed, 10);
|
|
125620
|
-
if (Number.isFinite(value) && value > 0) {
|
|
125621
|
-
tightest = tightest === null ? value : Math.min(tightest, value);
|
|
125622
|
-
}
|
|
125623
|
-
}
|
|
125624
|
-
}
|
|
125625
|
-
if (current2 === "/" || current2 === "") break;
|
|
125626
|
-
const parent = current2.substring(0, current2.lastIndexOf("/")) || "/";
|
|
125627
|
-
if (parent === current2) break;
|
|
125628
|
-
current2 = parent;
|
|
125629
|
-
}
|
|
125630
|
-
return tightest;
|
|
125631
|
-
} catch {
|
|
125632
|
-
return null;
|
|
125633
|
-
}
|
|
125634
|
-
}
|
|
125635
|
-
function getCgroupAvailableMemoryBytes() {
|
|
125636
|
-
try {
|
|
125637
|
-
const cgroupPath = readSelfCgroupPath();
|
|
125638
|
-
if (cgroupPath === null) return null;
|
|
125639
|
-
let tightestMax = null;
|
|
125640
|
-
let tightestPath = null;
|
|
125641
|
-
let current2 = cgroupPath;
|
|
125642
|
-
for (let depth = 0; depth < 20; depth++) {
|
|
125643
|
-
const prefix = `/sys/fs/cgroup${current2 === "/" ? "" : current2}`;
|
|
125644
|
-
const raw = readFileSafe(`${prefix}/memory.max`);
|
|
125645
|
-
if (raw !== null) {
|
|
125646
|
-
const trimmed = raw.trim();
|
|
125647
|
-
if (trimmed !== "max") {
|
|
125648
|
-
const value = parseInt(trimmed, 10);
|
|
125649
|
-
if (Number.isFinite(value) && value > 0) {
|
|
125650
|
-
if (tightestMax === null || value < tightestMax) {
|
|
125651
|
-
tightestMax = value;
|
|
125652
|
-
tightestPath = prefix;
|
|
125653
|
-
}
|
|
125654
|
-
}
|
|
125655
|
-
}
|
|
125656
|
-
}
|
|
125657
|
-
if (current2 === "/" || current2 === "") break;
|
|
125658
|
-
const parent = current2.substring(0, current2.lastIndexOf("/")) || "/";
|
|
125659
|
-
if (parent === current2) break;
|
|
125660
|
-
current2 = parent;
|
|
125661
|
-
}
|
|
125662
|
-
if (tightestMax === null || tightestPath === null) return null;
|
|
125663
|
-
const currentRaw = readFileSafe(`${tightestPath}/memory.current`);
|
|
125664
|
-
if (currentRaw === null) return null;
|
|
125665
|
-
const currentUsage = parseInt(currentRaw.trim(), 10);
|
|
125666
|
-
if (!Number.isFinite(currentUsage)) return null;
|
|
125667
|
-
return Math.max(0, tightestMax - currentUsage);
|
|
125668
|
-
} catch {
|
|
125669
|
-
return null;
|
|
125670
|
-
}
|
|
125671
|
-
}
|
|
125672
|
-
function readSelfCgroupPath() {
|
|
125673
|
-
try {
|
|
125674
|
-
const content = readFileSync("/proc/self/cgroup", "utf8");
|
|
125675
|
-
const line3 = content.split("\n").map((l) => l.trim()).find((l) => l.startsWith("0::"));
|
|
125676
|
-
if (!line3) return null;
|
|
125677
|
-
const cgroupPath = line3.slice(3).trim();
|
|
125678
|
-
return cgroupPath || "/";
|
|
125679
|
-
} catch {
|
|
125680
|
-
return null;
|
|
125681
|
-
}
|
|
125682
|
-
}
|
|
125683
|
-
function readFileSafe(filePath) {
|
|
125684
|
-
try {
|
|
125685
|
-
return readFileSync(filePath, "utf8");
|
|
125686
|
-
} catch {
|
|
125687
|
-
return null;
|
|
125688
|
-
}
|
|
125689
|
-
}
|
|
125690
125850
|
function readEnvNumber(key2, fallback2) {
|
|
125691
125851
|
const raw = process.env[key2];
|
|
125692
125852
|
if (!raw) return fallback2;
|
|
@@ -125694,6 +125854,8 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125694
125854
|
return Number.isFinite(parsed) ? parsed : fallback2;
|
|
125695
125855
|
}
|
|
125696
125856
|
const GIB = 1024 * 1024 * 1024;
|
|
125857
|
+
const WINDOWS_COMMIT_THRESHOLD_FLOOR_BYTES = 512 * 1024 * 1024;
|
|
125858
|
+
const WINDOWS_COMMIT_THRESHOLD_CEILING_BYTES = 2 * GIB;
|
|
125697
125859
|
function defaultMemoryThresholdBytes() {
|
|
125698
125860
|
const tenPercent = Math.floor(getEffectiveMemoryLimitBytes() * 0.1);
|
|
125699
125861
|
return Math.max(GIB, Math.min(4 * GIB, tenPercent));
|
|
@@ -125706,6 +125868,9 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125706
125868
|
memoryThresholdBytes: readEnvNumber("LODY_SESSION_GC_MEMORY_THRESHOLD_BYTES", defaultMemoryThresholdBytes())
|
|
125707
125869
|
};
|
|
125708
125870
|
}
|
|
125871
|
+
function getWindowsCommitThresholdBytes(memoryThresholdBytes) {
|
|
125872
|
+
return Math.max(WINDOWS_COMMIT_THRESHOLD_FLOOR_BYTES, Math.min(WINDOWS_COMMIT_THRESHOLD_CEILING_BYTES, memoryThresholdBytes));
|
|
125873
|
+
}
|
|
125709
125874
|
class SessionGCManager {
|
|
125710
125875
|
constructor(config2, deps) {
|
|
125711
125876
|
this.config = config2;
|
|
@@ -125729,7 +125894,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125729
125894
|
}
|
|
125730
125895
|
async sweep() {
|
|
125731
125896
|
const sweepStart = Date.now();
|
|
125732
|
-
const candidates = this.getIdleCandidates();
|
|
125897
|
+
const candidates = await this.getIdleCandidates();
|
|
125733
125898
|
if (candidates.length === 0) {
|
|
125734
125899
|
return;
|
|
125735
125900
|
}
|
|
@@ -125737,7 +125902,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125737
125902
|
let cleaned = 0;
|
|
125738
125903
|
let skipped = 0;
|
|
125739
125904
|
for (const { sessionId } of candidates) {
|
|
125740
|
-
if (!this.isStillEligibleForGC(sessionId)) {
|
|
125905
|
+
if (!await this.isStillEligibleForGC(sessionId)) {
|
|
125741
125906
|
skipped++;
|
|
125742
125907
|
continue;
|
|
125743
125908
|
}
|
|
@@ -125755,19 +125920,50 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125755
125920
|
this.deps.logger.debug(`[GC] Sweep completed: cleaned ${cleaned}/${candidates.length} sessions in ${sweepDuration}ms`);
|
|
125756
125921
|
}
|
|
125757
125922
|
async evictForMemoryPressure(excludeSessionId) {
|
|
125923
|
+
const thresholdBytes = this.config.memoryThresholdBytes;
|
|
125924
|
+
const commitThresholdBytes = getWindowsCommitThresholdBytes(thresholdBytes);
|
|
125925
|
+
let memorySnapshot = getMemoryPressureSnapshot();
|
|
125926
|
+
let availableMemory = memorySnapshot.availableMemoryBytes;
|
|
125927
|
+
let pressureReason = this.getPressureReason(memorySnapshot, thresholdBytes, commitThresholdBytes);
|
|
125758
125928
|
if (!this.config.enabled) {
|
|
125759
|
-
return
|
|
125929
|
+
return {
|
|
125930
|
+
availableMemoryBytes: availableMemory,
|
|
125931
|
+
thresholdBytes,
|
|
125932
|
+
hadMemoryPressure: false,
|
|
125933
|
+
stillUnderPressure: false,
|
|
125934
|
+
evictedSessionIds: [],
|
|
125935
|
+
pressureReason: null,
|
|
125936
|
+
...memorySnapshot.availableCommitBytes !== void 0 ? {
|
|
125937
|
+
availableCommitBytes: memorySnapshot.availableCommitBytes,
|
|
125938
|
+
commitThresholdBytes,
|
|
125939
|
+
commitLimitBytes: memorySnapshot.commitLimitBytes,
|
|
125940
|
+
committedBytes: memorySnapshot.committedBytes
|
|
125941
|
+
} : {}
|
|
125942
|
+
};
|
|
125760
125943
|
}
|
|
125761
|
-
|
|
125762
|
-
|
|
125763
|
-
|
|
125944
|
+
if (pressureReason === null) {
|
|
125945
|
+
return {
|
|
125946
|
+
availableMemoryBytes: availableMemory,
|
|
125947
|
+
thresholdBytes,
|
|
125948
|
+
hadMemoryPressure: false,
|
|
125949
|
+
stillUnderPressure: false,
|
|
125950
|
+
evictedSessionIds: [],
|
|
125951
|
+
pressureReason: null,
|
|
125952
|
+
...memorySnapshot.availableCommitBytes !== void 0 ? {
|
|
125953
|
+
availableCommitBytes: memorySnapshot.availableCommitBytes,
|
|
125954
|
+
commitThresholdBytes,
|
|
125955
|
+
commitLimitBytes: memorySnapshot.commitLimitBytes,
|
|
125956
|
+
committedBytes: memorySnapshot.committedBytes
|
|
125957
|
+
} : {}
|
|
125958
|
+
};
|
|
125764
125959
|
}
|
|
125765
|
-
|
|
125960
|
+
const commitText = memorySnapshot.availableCommitBytes !== void 0 ? `, commit headroom ${Math.round(memorySnapshot.availableCommitBytes / 1024 / 1024)}MB (threshold: ${Math.round(commitThresholdBytes / 1024 / 1024)}MB)` : "";
|
|
125961
|
+
this.deps.logger.debug(`[GC] Memory pressure detected: ${Math.round(availableMemory / 1024 / 1024)}MB available (threshold: ${Math.round(thresholdBytes / 1024 / 1024)}MB)${commitText}`);
|
|
125766
125962
|
const sessions = this.getSessionsWithIdleTime();
|
|
125767
125963
|
sessions.sort((a, b) => b.idleMs - a.idleMs);
|
|
125768
|
-
|
|
125964
|
+
const evictedSessionIds = [];
|
|
125769
125965
|
for (const { sessionId, idleMs } of sessions) {
|
|
125770
|
-
if (
|
|
125966
|
+
if (pressureReason === null) {
|
|
125771
125967
|
break;
|
|
125772
125968
|
}
|
|
125773
125969
|
if (excludeSessionId && sessionId === excludeSessionId) {
|
|
@@ -125776,30 +125972,63 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125776
125972
|
if (idleMs === 0) {
|
|
125777
125973
|
continue;
|
|
125778
125974
|
}
|
|
125779
|
-
if (!this.isEligibleForCleanup(sessionId)) {
|
|
125975
|
+
if (!await this.isEligibleForCleanup(sessionId)) {
|
|
125780
125976
|
continue;
|
|
125781
125977
|
}
|
|
125782
125978
|
try {
|
|
125783
125979
|
this.deps.logger.debug(`[GC] Evicting session ${sessionId} (idle ${Math.round(idleMs / 1e3)}s) due to memory pressure`);
|
|
125784
125980
|
await this.deps.cleanSession(sessionId);
|
|
125785
|
-
|
|
125786
|
-
|
|
125981
|
+
evictedSessionIds.push(sessionId);
|
|
125982
|
+
memorySnapshot = getMemoryPressureSnapshot();
|
|
125983
|
+
availableMemory = memorySnapshot.availableMemoryBytes;
|
|
125984
|
+
pressureReason = this.getPressureReason(memorySnapshot, thresholdBytes, commitThresholdBytes);
|
|
125787
125985
|
} catch (error2) {
|
|
125788
125986
|
this.deps.logger.error(`[GC] Failed to evict session ${sessionId}: ${formatErrorMessage(error2)}`);
|
|
125789
125987
|
}
|
|
125790
125988
|
}
|
|
125791
|
-
|
|
125792
|
-
|
|
125989
|
+
const stillUnderPressure = pressureReason !== null;
|
|
125990
|
+
if (evictedSessionIds.length > 0) {
|
|
125991
|
+
this.deps.logger.debug(`[GC] Memory pressure eviction complete: evicted ${evictedSessionIds.length} sessions, available memory now ${Math.round(availableMemory / 1024 / 1024)}MB`);
|
|
125992
|
+
} else if (stillUnderPressure) {
|
|
125993
|
+
this.deps.logger.debug("[GC] Memory pressure persists but no idle sessions were eligible for eviction");
|
|
125793
125994
|
}
|
|
125995
|
+
return {
|
|
125996
|
+
availableMemoryBytes: availableMemory,
|
|
125997
|
+
thresholdBytes,
|
|
125998
|
+
hadMemoryPressure: true,
|
|
125999
|
+
stillUnderPressure,
|
|
126000
|
+
evictedSessionIds,
|
|
126001
|
+
pressureReason,
|
|
126002
|
+
...memorySnapshot.availableCommitBytes !== void 0 ? {
|
|
126003
|
+
availableCommitBytes: memorySnapshot.availableCommitBytes,
|
|
126004
|
+
commitThresholdBytes,
|
|
126005
|
+
commitLimitBytes: memorySnapshot.commitLimitBytes,
|
|
126006
|
+
committedBytes: memorySnapshot.committedBytes
|
|
126007
|
+
} : {}
|
|
126008
|
+
};
|
|
126009
|
+
}
|
|
126010
|
+
getPressureReason(snapshot, thresholdBytes, commitThresholdBytes) {
|
|
126011
|
+
const physicalPressure = snapshot.availableMemoryBytes < thresholdBytes;
|
|
126012
|
+
const commitPressure = snapshot.availableCommitBytes !== void 0 && snapshot.availableCommitBytes < commitThresholdBytes;
|
|
126013
|
+
if (physicalPressure && commitPressure) {
|
|
126014
|
+
return "physical_and_commit";
|
|
126015
|
+
}
|
|
126016
|
+
if (physicalPressure) {
|
|
126017
|
+
return "physical";
|
|
126018
|
+
}
|
|
126019
|
+
if (commitPressure) {
|
|
126020
|
+
return "commit";
|
|
126021
|
+
}
|
|
126022
|
+
return null;
|
|
125794
126023
|
}
|
|
125795
|
-
getIdleCandidates() {
|
|
126024
|
+
async getIdleCandidates() {
|
|
125796
126025
|
const sessions = this.getSessionsWithIdleTime();
|
|
125797
126026
|
const candidates = [];
|
|
125798
126027
|
for (const session of sessions) {
|
|
125799
126028
|
if (session.idleMs < this.config.idleTimeoutMs) {
|
|
125800
126029
|
continue;
|
|
125801
126030
|
}
|
|
125802
|
-
if (!this.isEligibleForCleanup(session.sessionId)) {
|
|
126031
|
+
if (!await this.isEligibleForCleanup(session.sessionId)) {
|
|
125803
126032
|
continue;
|
|
125804
126033
|
}
|
|
125805
126034
|
candidates.push(session);
|
|
@@ -125827,20 +126056,23 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125827
126056
|
}
|
|
125828
126057
|
return result;
|
|
125829
126058
|
}
|
|
125830
|
-
isEligibleForCleanup(sessionId) {
|
|
126059
|
+
async isEligibleForCleanup(sessionId) {
|
|
125831
126060
|
if (this.deps.hasActiveTurn(sessionId)) {
|
|
125832
126061
|
return false;
|
|
125833
126062
|
}
|
|
125834
126063
|
if (this.deps.hasPendingUpdates(sessionId)) {
|
|
125835
126064
|
return false;
|
|
125836
126065
|
}
|
|
126066
|
+
if (await this.deps.hasPendingUserWork(sessionId)) {
|
|
126067
|
+
return false;
|
|
126068
|
+
}
|
|
125837
126069
|
if (this.deps.isArchiveInFlight(sessionId)) {
|
|
125838
126070
|
return false;
|
|
125839
126071
|
}
|
|
125840
126072
|
return true;
|
|
125841
126073
|
}
|
|
125842
|
-
isStillEligibleForGC(sessionId) {
|
|
125843
|
-
if (!this.isEligibleForCleanup(sessionId)) {
|
|
126074
|
+
async isStillEligibleForGC(sessionId) {
|
|
126075
|
+
if (!await this.isEligibleForCleanup(sessionId)) {
|
|
125844
126076
|
return false;
|
|
125845
126077
|
}
|
|
125846
126078
|
const lastActivity = this.deps.getSessionLastActivity(sessionId);
|
|
@@ -125968,6 +126200,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125968
126200
|
getSessionLastActivity: (sessionId) => handler.getLastActivity(sessionId),
|
|
125969
126201
|
hasActiveTurn: (sessionId) => handler.hasActiveTurn(sessionId),
|
|
125970
126202
|
hasPendingUpdates: (sessionId) => handler.hasPendingUpdates(sessionId),
|
|
126203
|
+
hasPendingUserWork: async (sessionId) => await handler.hasPendingUserWork(sessionId),
|
|
125971
126204
|
isArchiveInFlight: (sessionId) => handler.isArchiveInFlight(sessionId),
|
|
125972
126205
|
cleanSession: (sessionId) => handler.cleanSessionForGC(sessionId),
|
|
125973
126206
|
getSessionIds: () => handler.getTrackedSessionIds(),
|
|
@@ -125976,8 +126209,16 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125976
126209
|
this.gcManager.start();
|
|
125977
126210
|
handler.setEvictForMemoryPressure(async (excludeSessionId) => {
|
|
125978
126211
|
if (this.gcManager) {
|
|
125979
|
-
await this.gcManager.evictForMemoryPressure(excludeSessionId);
|
|
126212
|
+
return await this.gcManager.evictForMemoryPressure(excludeSessionId);
|
|
125980
126213
|
}
|
|
126214
|
+
return {
|
|
126215
|
+
availableMemoryBytes: 0,
|
|
126216
|
+
thresholdBytes: 0,
|
|
126217
|
+
hadMemoryPressure: false,
|
|
126218
|
+
stillUnderPressure: false,
|
|
126219
|
+
evictedSessionIds: [],
|
|
126220
|
+
pressureReason: null
|
|
126221
|
+
};
|
|
125981
126222
|
});
|
|
125982
126223
|
}
|
|
125983
126224
|
requireSessionManager() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lody",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.4-next.1",
|
|
4
4
|
"description": "Lody Agent CLI tool for managing remote command execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -72,11 +72,11 @@
|
|
|
72
72
|
"winston-transport": "^4.7.1",
|
|
73
73
|
"ws": "^8.18.3",
|
|
74
74
|
"zod": "^4.1.5",
|
|
75
|
-
"@lody/cli-supervisor": "0.0.1",
|
|
76
75
|
"@lody/convex": "0.0.1",
|
|
77
|
-
"@lody/loro-streams-rpc": "0.0.1",
|
|
78
76
|
"@lody/shared": "0.0.1",
|
|
79
|
-
"loro-code": "0.0.1"
|
|
77
|
+
"loro-code": "0.0.1",
|
|
78
|
+
"@lody/cli-supervisor": "0.0.1",
|
|
79
|
+
"@lody/loro-streams-rpc": "0.0.1"
|
|
80
80
|
},
|
|
81
81
|
"files": [
|
|
82
82
|
"dist",
|