nolo-cli 0.159.0-alpha.4 → 0.160.0-alpha.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.
Files changed (33) hide show
  1. package/{agentDeleteCommand-VXWLHIUC.js → agentDeleteCommand-ON2KVSJ5.js} +3 -3
  2. package/{agentEmailCommands-2TQN6UIZ.js → agentEmailCommands-2QPD5VP3.js} +4 -4
  3. package/{agentListCommands-44UDONIY.js → agentListCommands-L33M3I6V.js} +1 -1
  4. package/{agentMachineCommands-KH7OSRWP.js → agentMachineCommands-V44DPSED.js} +2 -2
  5. package/{agentRecordCommands-6BNH5KK2.js → agentRecordCommands-CKILCNQZ.js} +4 -4
  6. package/{agentRunCommand-7MFXU4GK.js → agentRunCommand-6IN7NK7H.js} +4 -4
  7. package/{agentRunControl-USFIAK7X.js → agentRunControl-XBSNHHZE.js} +1 -1
  8. package/{agentRuntimeLocal-BAQON6BB.js → agentRuntimeLocal-XCKO5FVP.js} +1 -1
  9. package/{appDeleteCommands-R73W7UJM.js → appDeleteCommands-2PJFNLAJ.js} +1 -1
  10. package/{appDeployCommands-F65F6N63.js → appDeployCommands-AZCMCMK6.js} +1 -1
  11. package/{appDeployStatusCommands-CTRCXJJX.js → appDeployStatusCommands-HSIKPVJN.js} +1 -1
  12. package/{appGetCommands-VTQERXLK.js → appGetCommands-IUZCPAP7.js} +1 -1
  13. package/{appListCommands-MY3WFDYA.js → appListCommands-JLFVS44M.js} +1 -1
  14. package/{chunk-HBW2XA3L.js → chunk-2XDQAWBM.js} +1 -1
  15. package/{chunk-YNOXGQ7X.js → chunk-EVPKKBSI.js} +2 -2
  16. package/{chunk-TK2AX7M6.js → chunk-JSB2OPQT.js} +68 -4
  17. package/{chunk-B5JIGIKM.js → chunk-K25WQPP3.js} +186 -28
  18. package/{chunk-QV3VGSX6.js → chunk-SNAWGDXT.js} +260 -1
  19. package/{chunk-SCQT4CRF.js → chunk-UY73HNIN.js} +4 -4
  20. package/{chunk-NWKPVIHL.js → chunk-ZSLIYVIQ.js} +1 -1
  21. package/{commandRegistry-3JCFSTBQ.js → commandRegistry-A4OR4B4L.js} +45 -45
  22. package/{dialogCommands-FDQPV6GO.js → dialogCommands-KMECTGCW.js} +3 -3
  23. package/{docCreateCommands-Z5KKWS2C.js → docCreateCommands-4IT5PUAL.js} +1 -1
  24. package/{docDeleteCommands-YTGMEQ4I.js → docDeleteCommands-QS66YNZV.js} +1 -1
  25. package/{docReadCommands-SK2CLD2S.js → docReadCommands-3SAI46YC.js} +1 -1
  26. package/{docUpdateCommands-SI4PL2L3.js → docUpdateCommands-OGYG5BRC.js} +1 -1
  27. package/index.js +3 -3
  28. package/{localRuntimeAdapter-TVIGJ3PU.js → localRuntimeAdapter-MO64KEOS.js} +3 -3
  29. package/{machineCommands-EYP73LBU.js → machineCommands-7MBJJY4K.js} +4 -4
  30. package/{machineWsRunDispatch-6QWYWIHP.js → machineWsRunDispatch-T4LFRJL3.js} +4 -4
  31. package/package.json +1 -1
  32. package/{readlineWorkspace-QBN246OU.js → readlineWorkspace-QHPB5LUC.js} +6 -43
  33. package/{spaceCommands-5WANPZMM.js → spaceCommands-WQMUCGV7.js} +1 -1
@@ -14050,8 +14050,11 @@ function detectExecShellSynchronousWarnings(command, shell) {
14050
14050
  }
14051
14051
 
14052
14052
  // packages/agent-runtime/workspaceShell.ts
14053
- import { resolve } from "node:path";
14053
+ import { createWriteStream, mkdirSync as mkdirSync2 } from "node:fs";
14054
+ import { dirname, join as join4, resolve } from "node:path";
14054
14055
  import { spawn as spawnChildProcess } from "node:child_process";
14056
+ import { randomUUID } from "node:crypto";
14057
+ import { tmpdir } from "node:os";
14055
14058
 
14056
14059
  // packages/agent-runtime/runtimeCompat.ts
14057
14060
  import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
@@ -14987,39 +14990,168 @@ function truncateToolOutput(value, limit = 2e4, options) {
14987
14990
  ${tail}`;
14988
14991
  }
14989
14992
  var DEFAULT_MAX_STREAM_BUFFER_BYTES = 5 * 1024 * 1024;
14990
- function readNodeStream(stream, maxBytes = DEFAULT_MAX_STREAM_BUFFER_BYTES) {
14993
+ function readNodeStream(stream, maxBytesOrOptions = DEFAULT_MAX_STREAM_BUFFER_BYTES) {
14991
14994
  if (!stream) return Promise.resolve("");
14995
+ const options = typeof maxBytesOrOptions === "number" ? { maxBytes: maxBytesOrOptions } : maxBytesOrOptions ?? {};
14996
+ const maxBytes = typeof options.maxBytes === "number" && Number.isFinite(options.maxBytes) && options.maxBytes >= 0 ? options.maxBytes : DEFAULT_MAX_STREAM_BUFFER_BYTES;
14997
+ const spillPath = asOptionalTrimmedString(options.spillPath);
14998
+ const createSink = options.spillSinkFactory ?? ((path) => createWriteStream(path));
14992
14999
  return new Promise((resolveStream, rejectStream) => {
14993
15000
  const chunks2 = [];
14994
15001
  let totalBytes = 0;
14995
15002
  let settled = false;
15003
+ let truncated = false;
15004
+ let spillState = spillPath ? "idle" : "disabled";
15005
+ let spillError = null;
15006
+ let spillSink = null;
15007
+ let sourcePausedForSpill = false;
15008
+ let sinkFinished = false;
14996
15009
  const formatLimit = maxBytes >= 1024 * 1024 ? `${Math.round(maxBytes / (1024 * 1024))}MB` : `${Math.round(maxBytes / 1024)}KB`;
14997
- const finalize2 = (truncated = false) => {
15010
+ const resumeSourceIfPaused = () => {
15011
+ if (!sourcePausedForSpill) return;
15012
+ sourcePausedForSpill = false;
15013
+ try {
15014
+ stream.resume?.();
15015
+ } catch {
15016
+ }
15017
+ };
15018
+ const markSpillFailed = (error) => {
15019
+ if (spillState === "failed") return;
15020
+ spillState = "failed";
15021
+ if (!spillError) {
15022
+ spillError = toErrorMessage(error);
15023
+ }
15024
+ const sink = spillSink;
15025
+ spillSink = null;
15026
+ if (sink) {
15027
+ try {
15028
+ sink.destroy?.();
15029
+ } catch {
15030
+ }
15031
+ }
15032
+ resumeSourceIfPaused();
15033
+ };
15034
+ const writeToSink = (buf) => {
15035
+ const sink = spillSink;
15036
+ if (!sink || spillState !== "open") return;
15037
+ if (sink.destroyed === true) {
15038
+ markSpillFailed(new Error("spill writable closed before finish"));
15039
+ return;
15040
+ }
15041
+ let accepted = true;
15042
+ try {
15043
+ accepted = sink.write(buf) !== false;
15044
+ } catch (error) {
15045
+ markSpillFailed(error);
15046
+ return;
15047
+ }
15048
+ if (!accepted && !sourcePausedForSpill) {
15049
+ sourcePausedForSpill = true;
15050
+ try {
15051
+ stream.pause?.();
15052
+ } catch {
15053
+ sourcePausedForSpill = false;
15054
+ }
15055
+ }
15056
+ };
15057
+ const openSpill = (headChunks, crossingChunk) => {
15058
+ if (!spillPath || spillState !== "idle") return;
15059
+ let sink;
15060
+ try {
15061
+ mkdirSync2(dirname(spillPath), { recursive: true });
15062
+ sink = createSink(spillPath);
15063
+ sink.on("error", (error) => markSpillFailed(error));
15064
+ sink.on("drain", () => {
15065
+ if (settled) return;
15066
+ resumeSourceIfPaused();
15067
+ });
15068
+ sink.on("finish", () => {
15069
+ sinkFinished = true;
15070
+ });
15071
+ sink.on("close", () => {
15072
+ if (sinkFinished || spillState !== "open") return;
15073
+ markSpillFailed(new Error("spill writable closed before finish"));
15074
+ });
15075
+ } catch (error) {
15076
+ spillState = "failed";
15077
+ spillError = toErrorMessage(error);
15078
+ spillSink = null;
15079
+ return;
15080
+ }
15081
+ spillSink = sink;
15082
+ spillState = "open";
15083
+ for (const head3 of headChunks) writeToSink(head3);
15084
+ writeToSink(crossingChunk);
15085
+ };
15086
+ const finalize2 = (wasTruncated) => {
14998
15087
  if (settled) return;
14999
15088
  settled = true;
15000
- let text2 = Buffer.concat(chunks2).toString("utf8");
15001
- if (truncated) {
15002
- text2 += `
15089
+ const truncatedNow = wasTruncated || truncated;
15090
+ const baseText = Buffer.concat(chunks2).toString("utf8");
15091
+ const done4 = () => {
15092
+ let text2 = baseText;
15093
+ if (truncatedNow) {
15094
+ const notes = [`output exceeded ${formatLimit} buffer limit`];
15095
+ if (spillState === "ok") {
15096
+ notes.push(`full output: ${spillPath}`);
15097
+ } else if (spillPath) {
15098
+ notes.push(
15099
+ `spill failed${spillError ? ` (${spillError})` : ""}; output incomplete`
15100
+ );
15101
+ }
15102
+ text2 += `
15003
15103
 
15004
- [stream pipe truncated: output exceeded ${formatLimit} buffer limit]`;
15104
+ [stream pipe truncated: ${notes.join("; ")}]`;
15105
+ }
15106
+ resolveStream(text2);
15107
+ };
15108
+ const sink = spillSink;
15109
+ if (!sink || spillState !== "open") {
15110
+ done4();
15111
+ return;
15112
+ }
15113
+ let closed = false;
15114
+ const settleSink = (failed, error) => {
15115
+ if (closed) return;
15116
+ closed = true;
15117
+ if (failed) {
15118
+ markSpillFailed(error ?? new Error("spill write failed"));
15119
+ } else if (spillState === "open") {
15120
+ spillState = "ok";
15121
+ }
15122
+ done4();
15123
+ };
15124
+ if (sink.destroyed === true) {
15125
+ settleSink(true, new Error("spill writable closed before finish"));
15126
+ return;
15127
+ }
15128
+ sink.once("error", (error) => settleSink(true, error));
15129
+ sink.once("finish", () => settleSink(false));
15130
+ sink.once("close", () => {
15131
+ settleSink(!sinkFinished, new Error("spill writable closed before finish"));
15132
+ });
15133
+ try {
15134
+ sink.end?.();
15135
+ } catch (error) {
15136
+ settleSink(true, error);
15005
15137
  }
15006
- resolveStream(text2);
15007
15138
  };
15008
15139
  stream.on("data", (chunk) => {
15009
15140
  if (settled) return;
15010
15141
  const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
15142
+ if (truncated) {
15143
+ writeToSink(buf);
15144
+ return;
15145
+ }
15011
15146
  if (totalBytes + buf.length >= maxBytes) {
15012
15147
  const remaining = Math.max(0, maxBytes - totalBytes);
15148
+ const headChunks = chunks2.slice();
15013
15149
  if (remaining > 0) {
15014
15150
  chunks2.push(buf.subarray(0, remaining));
15015
15151
  }
15016
- try {
15017
- if (typeof stream.destroy === "function") {
15018
- stream.destroy();
15019
- }
15020
- } catch {
15021
- }
15022
- finalize2(true);
15152
+ totalBytes += buf.length;
15153
+ truncated = true;
15154
+ openSpill(headChunks, buf);
15023
15155
  return;
15024
15156
  }
15025
15157
  totalBytes += buf.length;
@@ -15028,6 +15160,19 @@ function readNodeStream(stream, maxBytes = DEFAULT_MAX_STREAM_BUFFER_BYTES) {
15028
15160
  stream.on("error", (error) => {
15029
15161
  if (settled) return;
15030
15162
  settled = true;
15163
+ const sink = spillSink;
15164
+ spillSink = null;
15165
+ if (!spillError) spillError = toErrorMessage(error);
15166
+ if (spillState !== "failed" && spillState !== "disabled") {
15167
+ spillState = "failed";
15168
+ }
15169
+ if (sink) {
15170
+ try {
15171
+ sink.destroy?.();
15172
+ } catch {
15173
+ }
15174
+ }
15175
+ resumeSourceIfPaused();
15031
15176
  rejectStream(error);
15032
15177
  });
15033
15178
  stream.on("end", () => finalize2(false));
@@ -15140,8 +15285,12 @@ async function runWorkspaceCommand(args2) {
15140
15285
  process.removeListener("SIGINT", cleanupChildOnHostSignal);
15141
15286
  };
15142
15287
  exitPromise.then(detachSignalCleanup, detachSignalCleanup);
15143
- const stdoutCapture = stdoutPromiseResult(readNodeStream(proc.stdout));
15144
- const stderrCapture = stdoutPromiseResult(readNodeStream(proc.stderr));
15288
+ const stdoutCapture = stdoutPromiseResult(
15289
+ readNodeStream(proc.stdout, { spillPath: join4(tmpdir(), `nolo-stream-${randomUUID()}.log`) })
15290
+ );
15291
+ const stderrCapture = stdoutPromiseResult(
15292
+ readNodeStream(proc.stderr, { spillPath: join4(tmpdir(), `nolo-stream-${randomUUID()}-stderr.log`) })
15293
+ );
15145
15294
  const killChild = (signal) => {
15146
15295
  if (detached && typeof proc.pid === "number") {
15147
15296
  try {
@@ -15550,11 +15699,11 @@ async function invokeCapability(capabilityOrName, input, ctx = {}) {
15550
15699
  }
15551
15700
 
15552
15701
  // packages/agent-runtime/localWorkspaceTools.ts
15553
- import { existsSync as existsSync3 } from "node:fs";
15702
+ import { existsSync as existsSync4 } from "node:fs";
15554
15703
  import { createHash as createHash4 } from "node:crypto";
15555
15704
  import { lstat, mkdir, readdir as readdir2, readFile as readFile3, realpath, stat as stat3, writeFile } from "node:fs/promises";
15556
15705
  import { homedir } from "node:os";
15557
- import { dirname, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve4, sep } from "node:path";
15706
+ import { dirname as dirname2, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve4, sep } from "node:path";
15558
15707
  import { spawn as spawnChildProcess3 } from "node:child_process";
15559
15708
 
15560
15709
  // packages/core/readRangeLedger.ts
@@ -16836,7 +16985,7 @@ function resolveRipgrepBinary(env = process.env) {
16836
16985
  const fromEnv = asOptionalTrimmedString(env.NOLO_BUNDLED_RG);
16837
16986
  if (fromEnv) {
16838
16987
  try {
16839
- if (existsSync3(fromEnv)) return fromEnv;
16988
+ if (existsSync4(fromEnv)) return fromEnv;
16840
16989
  } catch {
16841
16990
  }
16842
16991
  }
@@ -16942,6 +17091,7 @@ ${tail}`,
16942
17091
  };
16943
17092
  }
16944
17093
  var READ_FILE_LEDGER_MAX_RECORDS = 64;
17094
+ var READ_FILE_LEDGER_MAX_KEYS = 256;
16945
17095
  async function readFileTool(args2) {
16946
17096
  const parsed = parseWorkspaceToolArguments(args2.call.arguments);
16947
17097
  const requestedPath = requireWorkspaceToolPath(parsed);
@@ -16980,6 +17130,8 @@ async function readFileTool(args2) {
16980
17130
  const retainedCap = resolveHistoricalToolContentCap("readFile", 0);
16981
17131
  const retained = filterRetainedLedgerRecords(entry.records, retainedCap);
16982
17132
  if (retained.length > 0 && isLineRangeCovered(requestRange, retained)) {
17133
+ ledger.delete(relativePath);
17134
+ ledger.set(relativePath, entry);
16983
17135
  return {
16984
17136
  content: `[readFile] ${relativePath} lines ${requestRange.startLine}-${requestRange.endLine} were already delivered earlier and the file is unchanged; not resending. Pass force:true to refetch.`,
16985
17137
  metadata: {
@@ -17012,10 +17164,16 @@ async function readFileTool(args2) {
17012
17164
  const persistedChars = truncatedRead.content.length + "\n\n[tool metadata]\n".length + JSON.stringify(metadata).length;
17013
17165
  const records = entry?.records ?? [];
17014
17166
  records.push({ ...requestRange, chars: persistedChars });
17167
+ ledger.delete(relativePath);
17015
17168
  ledger.set(relativePath, {
17016
17169
  fingerprint,
17017
17170
  records: records.slice(-READ_FILE_LEDGER_MAX_RECORDS)
17018
17171
  });
17172
+ while (ledger.size > READ_FILE_LEDGER_MAX_KEYS) {
17173
+ const oldestKey = ledger.keys().next().value;
17174
+ if (oldestKey === void 0) break;
17175
+ ledger.delete(oldestKey);
17176
+ }
17019
17177
  }
17020
17178
  return { content: truncatedRead.content, metadata };
17021
17179
  }
@@ -17028,7 +17186,7 @@ async function writeFileTool(args2) {
17028
17186
  requestedPath,
17029
17187
  ...args2.confirmExternalFileAccess ? { confirmExternalFileAccess: args2.confirmExternalFileAccess } : {}
17030
17188
  });
17031
- await mkdir(dirname(absolutePath), { recursive: true });
17189
+ await mkdir(dirname2(absolutePath), { recursive: true });
17032
17190
  await writeFile(absolutePath, content, "utf8");
17033
17191
  const relativePath = normalizeWorkspaceRelativePath({
17034
17192
  workspaceRoot: resolve4(args2.workspaceRoot),
@@ -17485,7 +17643,7 @@ async function execShellTool(args2) {
17485
17643
  );
17486
17644
  }
17487
17645
  var LAUNCH_PROCESS_STREAM_RING_CHARS = 65536;
17488
- var LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS = 4e6;
17646
+ var LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS = 1e6;
17489
17647
  function createLaunchProcessStreamCapture() {
17490
17648
  let ring = "";
17491
17649
  let full = null;
@@ -18709,7 +18867,7 @@ var normalizeContextBlockScopes = (legacyBlocks = void 0, scopes = void 0) => {
18709
18867
  };
18710
18868
 
18711
18869
  // packages/agent-runtime/localAutoCompaction.ts
18712
- import { randomUUID } from "node:crypto";
18870
+ import { randomUUID as randomUUID2 } from "node:crypto";
18713
18871
  function toPlanCompressionMessages(history) {
18714
18872
  return history.map((message, index) => ({
18715
18873
  id: typeof message.id === "string" && message.id.length > 0 ? message.id : `local-${index}`,
@@ -18944,7 +19102,7 @@ async function maybeAutoCompactLocalHistory(args2) {
18944
19102
  };
18945
19103
  }
18946
19104
  emitPhase({ kind: "compaction-start" });
18947
- const compactionAttemptId = randomUUID();
19105
+ const compactionAttemptId = randomUUID2();
18948
19106
  let compactionT0 = Date.now();
18949
19107
  let compactionInputTokens;
18950
19108
  let compactionPrevSummaryTokens;
@@ -21787,7 +21945,7 @@ function resolveSessionKey(args2) {
21787
21945
  }
21788
21946
 
21789
21947
  // packages/agent-runtime/devin/devinProvider.ts
21790
- import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
21948
+ import { randomBytes, randomUUID as randomUUID3 } from "node:crypto";
21791
21949
  var CLIENT_NAME = "chisel";
21792
21950
  var CLIENT_VERSION = "3000.11.3";
21793
21951
  function writeVarint(fieldNum, value) {
@@ -21847,7 +22005,7 @@ function encodeDevinClientMetadata(token) {
21847
22005
  ]);
21848
22006
  }
21849
22007
  function devinId(id, purpose) {
21850
- return id?.sessionKey && isStableSessionIdentityEnabled() ? deriveStableUuid(id.sessionKey, purpose) : randomUUID2();
22008
+ return id?.sessionKey && isStableSessionIdentityEnabled() ? deriveStableUuid(id.sessionKey, purpose) : randomUUID3();
21851
22009
  }
21852
22010
  function encodeDevinChatMessage(msg, identity2, index = 0) {
21853
22011
  const source = msg.role === "assistant" ? 2 : msg.role === "tool" ? 4 : 1;
@@ -21941,7 +22099,7 @@ ${item.text}`;
21941
22099
  const chatMessageBuffers = coalesced.map((m, i) => encodeDevinChatMessage(m, identity2, i));
21942
22100
  const completionConfig = buildCompletionConfig({ temperature });
21943
22101
  const modelConfig = Buffer.concat([
21944
- writeStringField(1, randomUUID2()),
22102
+ writeStringField(1, randomUUID3()),
21945
22103
  writeVarint(2, 1),
21946
22104
  writeVarint(3, 4)
21947
22105
  ]);
@@ -21953,7 +22111,7 @@ ${item.text}`;
21953
22111
  writeMessageField(8, completionConfig),
21954
22112
  ...tools.map((tool) => writeMessageField(10, encodeDevinToolDefinition(tool))),
21955
22113
  writeMessageField(15, modelConfig),
21956
- writeStringField(16, randomUUID2()),
22114
+ writeStringField(16, randomUUID3()),
21957
22115
  writeVarint(20, 1),
21958
22116
  writeStringField(21, model)
21959
22117
  ]);
@@ -324,6 +324,233 @@ import { dirname, isAbsolute as isAbsolute2, join, resolve } from "node:path";
324
324
  import * as nodeFs from "node:fs";
325
325
  import { execFileSync as nodeExecFileSync, spawn as nodeSpawn } from "node:child_process";
326
326
 
327
+ // packages/cli/client/hostMemory.ts
328
+ import { readFileSync, existsSync } from "node:fs";
329
+ import { freemem as osFreemem, totalmem as osTotalmem } from "node:os";
330
+ var PROC_MEMINFO_PATH = "/proc/meminfo";
331
+ var PROC_MOUNTS_PATH = "/proc/mounts";
332
+ var DEFAULT_CGROUP2_MOUNT = "/sys/fs/cgroup";
333
+ var SANDBOX_MARKER_PATHS = ["/.dockerenv", "/run/.containerenv"];
334
+ var PROC_SELF_CGROUP_PATH = "/proc/self/cgroup";
335
+ var SANDBOX_CGROUP_PATTERN = /(docker|kubepods|instabox|containerd)/;
336
+ var MEMORY_AVAILABLE_RATIO_THRESHOLD = 0.15;
337
+ var SWAP_USED_RATIO_THRESHOLD = 0.7;
338
+ var HOST_MEMORY_PRESSURE_HINT = "\u26A0 \u5185\u5B58\u504F\u7D27\uFF0C\u5EFA\u8BAE\u91CD\u6D3B\u653E\u6C99\u76D2/\u8FDC\u7AEF\u6216\u51CF\u5C11\u5E76\u53D1";
339
+ var BYTES_PER_MB = 1024 * 1024;
340
+ function toMbFromBytes(bytes) {
341
+ return Math.round(bytes / BYTES_PER_MB);
342
+ }
343
+ function kbToMb(kb) {
344
+ return Math.round(kb / 1024);
345
+ }
346
+ function tryReadText(deps, path) {
347
+ try {
348
+ const reader = deps.readFile ?? ((p) => readFileSync(p, "utf8"));
349
+ const text = reader(path);
350
+ return typeof text === "string" ? text : null;
351
+ } catch {
352
+ return null;
353
+ }
354
+ }
355
+ function tryExists(deps, path) {
356
+ try {
357
+ const checker = deps.exists ?? ((p) => existsSync(p));
358
+ return checker(path) === true;
359
+ } catch {
360
+ return false;
361
+ }
362
+ }
363
+ function readKbValue(text, key) {
364
+ const match = new RegExp(`^${key}:\\s*(\\d+)`, "m").exec(text);
365
+ if (!match) return null;
366
+ const value = Number.parseInt(match[1], 10);
367
+ return Number.isFinite(value) ? value : null;
368
+ }
369
+ function parseProcMeminfo(text) {
370
+ if (typeof text !== "string" || text.length === 0) return null;
371
+ const totalKb = readKbValue(text, "MemTotal");
372
+ const availableKb = readKbValue(text, "MemAvailable");
373
+ if (totalKb === null || availableKb === null) return null;
374
+ if (!(totalKb > 0)) return null;
375
+ const swapTotalKb = readKbValue(text, "SwapTotal") ?? 0;
376
+ const swapFreeKb = readKbValue(text, "SwapFree") ?? 0;
377
+ return {
378
+ source: "proc-meminfo",
379
+ totalMb: kbToMb(totalKb),
380
+ availableMb: kbToMb(Math.max(0, availableKb)),
381
+ swapTotalMb: kbToMb(Math.max(0, swapTotalKb)),
382
+ swapUsedMb: kbToMb(Math.max(0, swapTotalKb - swapFreeKb)),
383
+ hostAvailableMb: null
384
+ };
385
+ }
386
+ function detectSandbox(deps = {}) {
387
+ const platform = deps.platform ?? process.platform;
388
+ if (platform !== "linux") return false;
389
+ for (const marker of SANDBOX_MARKER_PATHS) {
390
+ if (tryExists(deps, marker)) return true;
391
+ }
392
+ const selfCgroup = tryReadText(deps, PROC_SELF_CGROUP_PATH);
393
+ return selfCgroup !== null && SANDBOX_CGROUP_PATTERN.test(selfCgroup);
394
+ }
395
+ function readOsView(deps) {
396
+ const os = deps.os ?? { totalmem: osTotalmem, freemem: osFreemem };
397
+ try {
398
+ const totalMb = toMbFromBytes(os.totalmem());
399
+ const availableMb = toMbFromBytes(os.freemem());
400
+ if (!(totalMb > 0) || !Number.isFinite(availableMb)) return null;
401
+ return {
402
+ source: "os",
403
+ totalMb,
404
+ availableMb: Math.max(0, availableMb),
405
+ // os 没有可移植的 swap 口径:按契约显示 n/a,而不是编一个数字。
406
+ swapTotalMb: null,
407
+ swapUsedMb: null,
408
+ hostAvailableMb: null
409
+ };
410
+ } catch {
411
+ return null;
412
+ }
413
+ }
414
+ function readHostView(deps) {
415
+ const platform = deps.platform ?? process.platform;
416
+ if (platform === "linux") {
417
+ const text = tryReadText(deps, PROC_MEMINFO_PATH);
418
+ if (text !== null) {
419
+ const parsed = parseProcMeminfo(text);
420
+ if (parsed) return parsed;
421
+ }
422
+ }
423
+ return readOsView(deps);
424
+ }
425
+ function parseCgroup2RelativePath(text) {
426
+ for (const line of text.split("\n")) {
427
+ const trimmed = line.trim();
428
+ if (!trimmed) continue;
429
+ const parts = trimmed.split(":");
430
+ if (parts.length < 3 || parts[0] !== "0" || parts[1] !== "") continue;
431
+ const path = parts.slice(2).join(":").trim();
432
+ if (!path) continue;
433
+ return path.startsWith("/") ? path : `/${path}`;
434
+ }
435
+ return null;
436
+ }
437
+ function parseCgroup2MountPoint(mountsText) {
438
+ for (const line of mountsText.split("\n")) {
439
+ const fields = line.trim().split(/\s+/);
440
+ if (fields.length < 3) continue;
441
+ if (fields[2] !== "cgroup2") continue;
442
+ const mountPoint = fields[1].replace(/\\040/g, " ");
443
+ if (mountPoint.startsWith("/")) return mountPoint;
444
+ }
445
+ return null;
446
+ }
447
+ function cgroupAncestorDirs(mountPoint, relativePath) {
448
+ const base = mountPoint.length > 1 && mountPoint.endsWith("/") ? mountPoint.slice(0, -1) : mountPoint;
449
+ const dirs = [];
450
+ if (relativePath && relativePath !== "/") {
451
+ const rel = relativePath.startsWith("/") ? relativePath : `/${relativePath}`;
452
+ const segments = rel.split("/").filter(Boolean);
453
+ for (let depth = segments.length; depth >= 1; depth -= 1) {
454
+ dirs.push(`${base}/${segments.slice(0, depth).join("/")}`);
455
+ }
456
+ }
457
+ dirs.push(base);
458
+ return dirs;
459
+ }
460
+ function readCgroupMaxMb(deps, dir) {
461
+ const maxText = tryReadText(deps, `${dir}/memory.max`);
462
+ if (maxText === null) return null;
463
+ const maxRaw = maxText.trim();
464
+ if (maxRaw === "" || maxRaw === "max") return null;
465
+ const maxBytes = Number.parseInt(maxRaw, 10);
466
+ if (!Number.isFinite(maxBytes) || maxBytes <= 0) return null;
467
+ const totalMb = toMbFromBytes(maxBytes);
468
+ return totalMb > 0 ? totalMb : null;
469
+ }
470
+ function buildCgroupSnapshot(deps, dir, totalMb, hostView) {
471
+ const currentText = tryReadText(deps, `${dir}/memory.current`);
472
+ let usedMb = null;
473
+ if (currentText !== null) {
474
+ const currentBytes = Number.parseInt(currentText.trim(), 10);
475
+ if (Number.isFinite(currentBytes)) {
476
+ usedMb = Math.min(totalMb, Math.max(0, toMbFromBytes(currentBytes)));
477
+ }
478
+ }
479
+ return {
480
+ source: "cgroup",
481
+ totalMb,
482
+ availableMb: usedMb === null ? null : Math.max(0, totalMb - usedMb),
483
+ // swap 不是 cgroup 配额的一部分,只能借宿主视角(Linux 上就是 /proc/meminfo)。
484
+ swapTotalMb: hostView?.swapTotalMb ?? null,
485
+ swapUsedMb: hostView?.swapUsedMb ?? null,
486
+ hostAvailableMb: hostView?.availableMb ?? null
487
+ };
488
+ }
489
+ function readCgroupLimitView(deps, hostView) {
490
+ const selfCgroupText = tryReadText(deps, PROC_SELF_CGROUP_PATH) ?? "";
491
+ const relativePath = parseCgroup2RelativePath(selfCgroupText);
492
+ const mountsText = tryReadText(deps, PROC_MOUNTS_PATH) ?? "";
493
+ const mountPoint = parseCgroup2MountPoint(mountsText) ?? DEFAULT_CGROUP2_MOUNT;
494
+ let limitingDir = null;
495
+ let limitingMb = Number.POSITIVE_INFINITY;
496
+ for (const dir of cgroupAncestorDirs(mountPoint, relativePath)) {
497
+ const totalMb = readCgroupMaxMb(deps, dir);
498
+ if (totalMb === null) continue;
499
+ if (totalMb < limitingMb) {
500
+ limitingMb = totalMb;
501
+ limitingDir = dir;
502
+ }
503
+ }
504
+ if (limitingDir === null) return null;
505
+ return buildCgroupSnapshot(deps, limitingDir, limitingMb, hostView);
506
+ }
507
+ function readHostMemory(deps = {}) {
508
+ const platform = deps.platform ?? process.platform;
509
+ if (platform !== "linux") {
510
+ return readHostView(deps);
511
+ }
512
+ const hostView = readHostView(deps);
513
+ const cgroupView = readCgroupLimitView(deps, hostView);
514
+ if (!cgroupView) return hostView;
515
+ return {
516
+ ...cgroupView,
517
+ scope: detectSandbox(deps) ? "sandbox" : "cgroup-limit"
518
+ };
519
+ }
520
+ function isHostMemoryTight(mem) {
521
+ if (!(mem.totalMb > 0)) return false;
522
+ if (mem.availableMb !== null && mem.availableMb / mem.totalMb < MEMORY_AVAILABLE_RATIO_THRESHOLD) {
523
+ return true;
524
+ }
525
+ if (mem.swapTotalMb !== null && mem.swapUsedMb !== null && mem.swapTotalMb > 0 && mem.swapUsedMb / mem.swapTotalMb > SWAP_USED_RATIO_THRESHOLD) {
526
+ return true;
527
+ }
528
+ return false;
529
+ }
530
+ function formatHostMemoryMb(mb) {
531
+ const value = Math.max(0, Math.round(Number.isFinite(mb) ? mb : 0));
532
+ if (value >= 1024) return `${(value / 1024).toFixed(1)}G`;
533
+ return `${value}M`;
534
+ }
535
+ function formatSwapPart(mem) {
536
+ if (mem.swapTotalMb === null || mem.swapUsedMb === null) return "swap n/a";
537
+ return `swap ${formatHostMemoryMb(mem.swapUsedMb)}/${formatHostMemoryMb(mem.swapTotalMb)}`;
538
+ }
539
+ function formatHostMemoryLine(mem) {
540
+ const usedMb = mem.availableMb === null ? null : Math.max(0, mem.totalMb - mem.availableMb);
541
+ let line;
542
+ if (mem.source === "cgroup") {
543
+ const hostAvailable = mem.hostAvailableMb === null ? "n/a" : formatHostMemoryMb(mem.hostAvailableMb);
544
+ const prefix = (mem.scope ?? "sandbox") === "cgroup-limit" ? "[\u9650\u989D] \u672C\u8FDB\u7A0B\u5185\u5B58" : "[sandbox] \u5BB9\u5668\u5185\u5B58";
545
+ const usage = usedMb === null ? "\u5360\u7528\u672A\u77E5" : formatHostMemoryMb(usedMb);
546
+ line = `${prefix} ${usage}/${formatHostMemoryMb(mem.totalMb)} \xB7 \u5BBF\u4E3B\u53EF\u7528 ${hostAvailable}`;
547
+ } else {
548
+ const available = mem.availableMb === null ? "n/a" : formatHostMemoryMb(mem.availableMb);
549
+ line = `\u672C\u5730\u5185\u5B58 ${formatHostMemoryMb(usedMb ?? 0)}/${formatHostMemoryMb(mem.totalMb)}\uFF08\u53EF\u7528 ${available}\uFF09\xB7 ${formatSwapPart(mem)}`;
550
+ }
551
+ return isHostMemoryTight(mem) ? `${line} \xB7 ${HOST_MEMORY_PRESSURE_HINT}` : line;
552
+ }
553
+
327
554
  // packages/cli/client/localRuntimeHelpers.ts
328
555
  var REMOTE_DIALOG_SYNC_TIMEOUT_MS = 1e4;
329
556
  var remoteDialogSyncTimeoutMsForTest;
@@ -1183,6 +1410,29 @@ function buildAgentRunChildCommand(options, deps = {}) {
1183
1410
  }
1184
1411
  return { execPath, childArgs: [entrypoint, ...commandParts, ...strippedArgs] };
1185
1412
  }
1413
+ function safeReadHostMemory(reader) {
1414
+ try {
1415
+ return reader();
1416
+ } catch {
1417
+ return null;
1418
+ }
1419
+ }
1420
+ function emitHostMemoryNotice(mem, output) {
1421
+ if (!mem || !output) return void 0;
1422
+ let notice;
1423
+ try {
1424
+ notice = formatHostMemoryLine(mem);
1425
+ } catch {
1426
+ return void 0;
1427
+ }
1428
+ try {
1429
+ output.write(`${notice}
1430
+ `);
1431
+ } catch {
1432
+ return notice;
1433
+ }
1434
+ return notice;
1435
+ }
1186
1436
  async function spawnLocalBackgroundRun(input, deps = {}) {
1187
1437
  const env = deps.env ?? process.env;
1188
1438
  const homedir = deps.homedir ?? nodeHomedir;
@@ -1266,6 +1516,7 @@ async function spawnLocalBackgroundRun(input, deps = {}) {
1266
1516
  NOLO_AGENT_RUN_CHILD: "1",
1267
1517
  NOLO_AGENT_RUN_ID: runId
1268
1518
  };
1519
+ const hostMemory = safeReadHostMemory(deps.readHostMemory ?? readHostMemory);
1269
1520
  const logFd = fs.openSync(logPath, "a");
1270
1521
  const proc = spawn(execPath, childArgs, {
1271
1522
  cwd: input.cwd,
@@ -1283,6 +1534,7 @@ async function spawnLocalBackgroundRun(input, deps = {}) {
1283
1534
  proc.channel?.unref?.();
1284
1535
  }
1285
1536
  proc.unref();
1537
+ const hostMemoryNotice = emitHostMemoryNotice(hostMemory, input.output);
1286
1538
  if (typeof proc.pid === "number") {
1287
1539
  const processStartedAt = (deps.getProcessStartTime ?? defaultGetProcessStartTime)(proc.pid);
1288
1540
  withRunRecordLock(runId, deps, () => {
@@ -1292,7 +1544,14 @@ async function spawnLocalBackgroundRun(input, deps = {}) {
1292
1544
  writeRunRecord(latest, deps);
1293
1545
  });
1294
1546
  }
1295
- return { runId, pid: proc.pid, logPath, queuePath, batchId };
1547
+ return {
1548
+ runId,
1549
+ pid: proc.pid,
1550
+ logPath,
1551
+ queuePath,
1552
+ batchId,
1553
+ ...hostMemoryNotice ? { hostMemoryNotice } : {}
1554
+ };
1296
1555
  }
1297
1556
  function ackRunRecord(runId, deps = {}) {
1298
1557
  withRunRecordLock(runId, deps, () => {
@@ -170,7 +170,7 @@ import {
170
170
  runExecShellGates,
171
171
  validateConfiguredRootExecutionLease,
172
172
  verifyOwnerRouteAck
173
- } from "./chunk-B5JIGIKM.js";
173
+ } from "./chunk-K25WQPP3.js";
174
174
  import {
175
175
  accumulateGeminiChunks,
176
176
  buildGeminiGenerateContentRequest,
@@ -298,7 +298,7 @@ import {
298
298
  terminateRunProcess,
299
299
  transitionRunToTerminal,
300
300
  withQueueLock
301
- } from "./chunk-QV3VGSX6.js";
301
+ } from "./chunk-SNAWGDXT.js";
302
302
  import {
303
303
  TASK_PREVIEW_MAX,
304
304
  formatListRunsCard,
@@ -10766,7 +10766,7 @@ function createCliLocalRuntimeAdapter(deps) {
10766
10766
  const recordRunExecutionIsolation = async () => {
10767
10767
  try {
10768
10768
  const { writeFileSync: writeFileSync4, mkdirSync: mkdirSync4 } = await import("node:fs");
10769
- const { resolveRunsDir } = await import("./agentRunControl-USFIAK7X.js");
10769
+ const { resolveRunsDir } = await import("./agentRunControl-XBSNHHZE.js");
10770
10770
  const fact = resolveLocalRunExecutionIsolationFact({
10771
10771
  // F1:显式点名宿主执行的 run 在审计里必须与 docker 沙盒区分开
10772
10772
  // (reason: host-explicit-writes vs docker-workload)。
@@ -10798,7 +10798,7 @@ function createCliLocalRuntimeAdapter(deps) {
10798
10798
  dockerWorkloadReleased = true;
10799
10799
  dockerWorkloadLoad = null;
10800
10800
  if (loaded) {
10801
- const { resolveRunsDir } = await import("./agentRunControl-USFIAK7X.js");
10801
+ const { resolveRunsDir } = await import("./agentRunControl-XBSNHHZE.js");
10802
10802
  const exportRunDiff = deps.exportDockerRunDiffArtifact ?? exportDockerRunDiffArtifact;
10803
10803
  const warn = (message) => process.stderr.write(`[nolo] ${message}
10804
10804
  `);