nolo-cli 0.157.0-alpha.1 → 0.157.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.
Files changed (164) hide show
  1. package/{_executeModel-5WS2PA62.js → _executeModel-ZIQHOREX.js} +55 -55
  2. package/{accountResolver-7RYB4D2P.js → accountResolver-55EDKEIK.js} +1 -1
  3. package/{action-ZJYUBXFC.js → action-GPPJC5TI.js} +5 -5
  4. package/{addContentAction-TVHB4526.js → addContentAction-XJQEJ4XW.js} +8 -8
  5. package/{addDialogAgentAction-FDPBU6K6.js → addDialogAgentAction-5WZX5CAY.js} +7 -7
  6. package/{agentDeleteCommand-Q6M4F3U6.js → agentDeleteCommand-LQSOJEXY.js} +75 -76
  7. package/{agentEmailCommands-FNH6FEHS.js → agentEmailCommands-HTPKXONW.js} +78 -79
  8. package/{agentListCommands-JUNMFS6E.js → agentListCommands-GOCGOQUB.js} +36 -36
  9. package/{agentMachineCommands-VNLDEKNT.js → agentMachineCommands-T7DAZG77.js} +35 -32
  10. package/{agentProviderCommands-X2HQE6HY.js → agentProviderCommands-CXBHNONN.js} +5 -5
  11. package/{agentPullCommand-FA43RBRU.js → agentPullCommand-GXXEDOSA.js} +4 -4
  12. package/{agentRecordCommands-47XXRVPI.js → agentRecordCommands-HSW3RJ4H.js} +72 -73
  13. package/{agentRunCommand-QEXFJUXQ.js → agentRunCommand-TIRJ57DK.js} +75 -75
  14. package/{agentRunControl-2C5GM3BW.js → agentRunControl-QWFFIAQY.js} +9 -7
  15. package/{agentRuntimeLocal-O5GDAFTN.js → agentRuntimeLocal-J6I63NZC.js} +21 -19
  16. package/{agentSlice-E3XS5FTQ.js → agentSlice-JNM3HF3V.js} +8 -8
  17. package/{appDeleteCommands-C2J5RQ3I.js → appDeleteCommands-KKLAK2UR.js} +31 -29
  18. package/{appDeployCommands-3YRK7PSU.js → appDeployCommands-PXWK4FAX.js} +31 -29
  19. package/{appDeployStatusCommands-FPAX7MOK.js → appDeployStatusCommands-3NGWDZTX.js} +31 -29
  20. package/{appGetCommands-XAC2V766.js → appGetCommands-APWYPEO3.js} +31 -29
  21. package/{appListCommands-ZJL2W5O7.js → appListCommands-OP7IRALO.js} +31 -29
  22. package/{authCommand-J3XILHWW.js → authCommand-3Z52KJID.js} +22 -302
  23. package/{authCommands-77CHYBGF.js → authCommands-QOCKVPX3.js} +5 -5
  24. package/{builtinSkillRegistry-A6VSPJEJ.js → builtinSkillRegistry-LJV7FHNI.js} +4 -4
  25. package/{chromeCommands-OBBNJCLS.js → chromeCommands-IY4NVSXZ.js} +17 -10
  26. package/{chunk-SW4S4XYB.js → chunk-2AB2HA4M.js} +84 -34
  27. package/{chunk-C5VPAHIU.js → chunk-32ZKZET7.js} +1 -1
  28. package/{chunk-XPWGOTBQ.js → chunk-3L2WKC3W.js} +1 -8
  29. package/{chunk-3XZKV7QQ.js → chunk-3L5MSPWX.js} +1 -1
  30. package/{chunk-QLQ573SU.js → chunk-3M52COK5.js} +120 -0
  31. package/{chunk-VSOWPETY.js → chunk-4226O6F7.js} +10 -10
  32. package/{chunk-Z6KQDSZW.js → chunk-43OFVLWJ.js} +6 -6
  33. package/{chunk-TJ2VPCBE.js → chunk-4JCAW7TO.js} +2 -2
  34. package/{chunk-6TF64IF2.js → chunk-4L4K6YR7.js} +16 -2
  35. package/{chunk-PEO24UB5.js → chunk-5E5VB7PS.js} +324 -57
  36. package/{chunk-ZD2BLOY6.js → chunk-5EDQTOUR.js} +7 -7
  37. package/{chunk-HKT6QXP3.js → chunk-5IKRTRRR.js} +5 -5
  38. package/{chunk-P5QACQDK.js → chunk-62TBKWTV.js} +1 -1
  39. package/{chunk-TRW43TSC.js → chunk-6EQD4AQL.js} +41 -1
  40. package/{chunk-YLMMECCH.js → chunk-6XIE5ZGI.js} +1 -1
  41. package/{chunk-OH2M7WEU.js → chunk-6Y3ETA2Y.js} +3 -3
  42. package/{chunk-7A4LJCDR.js → chunk-B3ZIFOFI.js} +5 -5
  43. package/{chunk-WZ6OU4NX.js → chunk-BAWAKW7X.js} +2 -2
  44. package/{chunk-UUPR3TD7.js → chunk-BL4HFN6L.js} +2 -4
  45. package/{chunk-2GGXHQIW.js → chunk-BLVSFYQ3.js} +4 -2
  46. package/{chunk-HP6O6LTV.js → chunk-CDLP56BS.js} +9 -9
  47. package/{chunk-UTIMX333.js → chunk-DJ2BOPJY.js} +1 -1
  48. package/{chunk-36OMEXXR.js → chunk-DLV3H464.js} +8 -8
  49. package/{chunk-CE7PIHQD.js → chunk-ESMZW5PE.js} +3 -3
  50. package/{chunk-PSJWPSKS.js → chunk-EWO77ZGF.js} +9 -43
  51. package/{chunk-ZWG3C7VI.js → chunk-F3SOW2EU.js} +81 -1
  52. package/{chunk-FY4SDK2E.js → chunk-FDLDMJTM.js} +1 -1
  53. package/{chunk-KOB75UNU.js → chunk-FHG46N5C.js} +53 -4
  54. package/{chunk-7KYVUFDG.js → chunk-G3VYRPTV.js} +5 -267
  55. package/{chunk-DW7MGLMG.js → chunk-GGUTTH6C.js} +35 -207
  56. package/{chunk-6MJNEXUU.js → chunk-HGDKT2D6.js} +4 -0
  57. package/{chunk-B2EGWZJG.js → chunk-I7S63QUU.js} +1 -1
  58. package/{chunk-ROPSATAB.js → chunk-IF7V3CBU.js} +1 -1
  59. package/chunk-IKOKYBYJ.js +2685 -0
  60. package/{chunk-T6CBQ6BN.js → chunk-INFHX73M.js} +1 -1
  61. package/{chunk-YBNHPX4S.js → chunk-IW64YHXL.js} +155 -31
  62. package/{chunk-K43ZF2C3.js → chunk-JPPQ3GUO.js} +1 -1
  63. package/{chunk-TZTWMGE3.js → chunk-JWEXJ573.js} +3 -3
  64. package/{chunk-RUNKPFM5.js → chunk-K6Y5T4GE.js} +12 -8
  65. package/{chunk-MVJVF4ED.js → chunk-KESKU5BG.js} +18 -17
  66. package/{chunk-I2ZMGXXF.js → chunk-KFHREHUQ.js} +1 -1
  67. package/{chunk-3C2QLERX.js → chunk-KMDDG73K.js} +1 -1
  68. package/{chunk-5MM7NNO6.js → chunk-KSTEXKL6.js} +78 -39
  69. package/{chunk-EM3YOZJ4.js → chunk-L2JMH4I5.js} +3 -3
  70. package/{chunk-MM3662RS.js → chunk-LPO6AMQU.js} +780 -64
  71. package/{chunk-D4OWHT5Y.js → chunk-MAYW6V3E.js} +2 -2
  72. package/{chunk-IPAW7XLD.js → chunk-MFVOJ6KF.js} +2 -2
  73. package/{chunk-FDT27L7S.js → chunk-MLEANT7D.js} +19 -17
  74. package/{chunk-7GC3EKUG.js → chunk-MMSQTZCJ.js} +3 -3
  75. package/{chunk-XFKZ3MCV.js → chunk-MR3ITQEV.js} +344 -88
  76. package/{chunk-CBHGZRCE.js → chunk-MYOH447Z.js} +1 -1
  77. package/{chunk-6MPAEODB.js → chunk-NHEWV5FD.js} +17 -3
  78. package/{chunk-ZEHTARJA.js → chunk-NLFTBC6S.js} +321 -150
  79. package/{chunk-SKPPKHD3.js → chunk-OLUKIUX6.js} +2 -2
  80. package/{chunk-SUKSWFLP.js → chunk-PIG2IUVZ.js} +1 -1
  81. package/{chunk-SEJ5OS6F.js → chunk-PXYBUFDB.js} +4 -0
  82. package/{chunk-2MTE2PEH.js → chunk-QA2C5NKI.js} +5 -739
  83. package/{chunk-L6EQV5OC.js → chunk-QXZH5UDF.js} +3 -3
  84. package/{chunk-TNIX77YM.js → chunk-R5GO3YZP.js} +34 -1
  85. package/{chunk-X5SUT4CN.js → chunk-R7GCGOGZ.js} +6 -6
  86. package/{chunk-4Z245KOA.js → chunk-SH3XD4E2.js} +8 -4
  87. package/{chunk-JGRPBIZZ.js → chunk-SQX235S2.js} +1 -1
  88. package/chunk-TT644UJD.js +188 -0
  89. package/{chunk-IYBIRCQV.js → chunk-U34KT53V.js} +29 -11
  90. package/{chunk-ANJTRG52.js → chunk-ULQAD7ZY.js} +18 -43
  91. package/{chunk-QDA4Y5O6.js → chunk-UVYSBQ2M.js} +21 -17
  92. package/{chunk-I2RMFLLH.js → chunk-V7PCFWX3.js} +1 -1
  93. package/{chunk-HXHSTRGH.js → chunk-VYNMQ6HW.js} +0 -27
  94. package/{chunk-G47IPDOK.js → chunk-W7FGZRLK.js} +1 -1
  95. package/{chunk-7BSQK2UC.js → chunk-WRCZXJ4N.js} +2 -2
  96. package/{chunk-KX6LPG7W.js → chunk-WYJTNSVL.js} +9 -9
  97. package/{chunk-H4KACZMG.js → chunk-XZIW3Q5B.js} +271 -138
  98. package/chunk-Y5GCAMGP.js +9 -0
  99. package/{chunk-JZX2XLKC.js → chunk-YCRROBDY.js} +1 -1
  100. package/{chunk-FPWT3RH2.js → chunk-YN5DT4JS.js} +22 -22
  101. package/{chunk-SCJD2PNK.js → chunk-YPG7TUQD.js} +3 -3
  102. package/{chunk-EJU23ST5.js → chunk-ZKVS36H3.js} +5069 -3730
  103. package/{chunk-YANF2RR4.js → chunk-ZPKUJROL.js} +1 -1
  104. package/{chunk-TYND6RDQ.js → chunk-ZZDVBZOP.js} +74 -3
  105. package/{commandRegistry-OUBQIF62.js → commandRegistry-DR2RPWK6.js} +88 -96
  106. package/{contentThunks-B3FRQGP7.js → contentThunks-CSJDJDEJ.js} +21 -21
  107. package/{createAgentAutomationAction-T2YFJ6MY.js → createAgentAutomationAction-ISEUEQSY.js} +21 -21
  108. package/{createDialogAction-2NFGFBXW.js → createDialogAction-HUCFOZY3.js} +25 -25
  109. package/{createPageAction-LDAVZDN7.js → createPageAction-DT5CYXCL.js} +24 -25
  110. package/{dbSlice-5QGKMQLO.js → dbSlice-6PVPY44C.js} +7 -7
  111. package/{deleteFile-EW6QNRRT.js → deleteFile-YM6NDPAR.js} +5 -5
  112. package/{dialogCommands-ZLBEAQHE.js → dialogCommands-3W6AIUXT.js} +35 -32
  113. package/{dialogSlice-64S3J6CP.js → dialogSlice-WJEEPG4W.js} +20 -20
  114. package/{dist-NJC2W7I4.js → dist-Q2JSMAKA.js} +7 -361
  115. package/{docCreateCommands-XOG5BBFQ.js → docCreateCommands-KQHQNLRX.js} +32 -30
  116. package/{docDeleteCommands-NCW3BBRX.js → docDeleteCommands-O3DWFX2W.js} +33 -31
  117. package/{docReadCommands-OT7X44Y3.js → docReadCommands-2U2BUONC.js} +32 -30
  118. package/{docStore-NIP7OO4I.js → docStore-7JPYT25V.js} +8 -8
  119. package/{docUpdateCommands-JW7OWW2J.js → docUpdateCommands-MF3C52XI.js} +32 -30
  120. package/{getPricing-TX35D7PI.js → getPricing-XQ3N3RBP.js} +2 -2
  121. package/{handleSendMessageAction-TOKA7ED7.js → handleSendMessageAction-BV5FMID4.js} +43 -39
  122. package/index.js +8 -8
  123. package/{localRuntimeAdapter-EQM6LFYC.js → localRuntimeAdapter-6FBEJJ4D.js} +70 -69
  124. package/{localRuntimeAuthority-JDAD7CB6.js → localRuntimeAuthority-BPN5OAQG.js} +2 -2
  125. package/{localRuntimeDb-CVC62PM5.js → localRuntimeDb-QIVAXMUL.js} +1 -1
  126. package/{machineCommands-WJ7FZ67T.js → machineCommands-WCAFDI5N.js} +80 -79
  127. package/{machineWsRunDispatch-BQT4DEA2.js → machineWsRunDispatch-ZF6BBQ3X.js} +70 -71
  128. package/{memberThunks-KRV5YXOB.js → memberThunks-AZINUM4S.js} +10 -10
  129. package/package.json +6 -2
  130. package/{profileConfig-ZGOFORWV.js → profileConfig-KN3N2BLF.js} +7 -1
  131. package/{providerPresetApply-CRZFMQD5.js → providerPresetApply-PKUOHQJT.js} +5 -5
  132. package/{providerRegistry-7IS43D3X.js → providerRegistry-HNQLIFN2.js} +5 -5
  133. package/{read-5KAZHHDM.js → read-7JPIWRBQ.js} +5 -5
  134. package/{readDocTool-5QO34OSX.js → readDocTool-AAQTB2Y3.js} +1 -1
  135. package/{readlineWorkspace-JHJSU5DS.js → readlineWorkspace-I5MHMEUE.js} +375 -200
  136. package/{removeDialogAgentAction-6X63HMI3.js → removeDialogAgentAction-RMVRILLK.js} +9 -9
  137. package/{runAgentClientLoop-5QIGEALK.js → runAgentClientLoop-TSNSRQEP.js} +59 -54
  138. package/{runtimeDoctorCommands-56NWKNON.js → runtimeDoctorCommands-MMCP4B4U.js} +12 -12
  139. package/{secretCommands-ZXZRLR3R.js → secretCommands-DNY56NSU.js} +11 -1
  140. package/{setDialogExtraReferencesAction-4MAYKKYW.js → setDialogExtraReferencesAction-L3V7JTMG.js} +8 -8
  141. package/{setPrimaryDialogAgentAction-EGBAMREH.js → setPrimaryDialogAgentAction-X52JQGTJ.js} +9 -9
  142. package/settingThunks-N6XFWPOU.js +923 -0
  143. package/{spaceCommands-ZWDPTURU.js → spaceCommands-NDPGHDMD.js} +29 -27
  144. package/{streamAgentChatTurn-456DWXNY.js → streamAgentChatTurn-RJLDV4WZ.js} +154 -95
  145. package/{switchDialogAgentAction-YBKTD6DO.js → switchDialogAgentAction-ZJ5CGU7J.js} +7 -7
  146. package/{tableCommands-U3QDY4L6.js → tableCommands-IVC5S7Y3.js} +11 -11
  147. package/{toolApiClient-7WSJJJBV.js → toolApiClient-HGNO53TC.js} +2 -2
  148. package/{tools-IWNXKK4Y.js → tools-X6QNIYFJ.js} +43 -44
  149. package/{tuiSummaryLlmCaller-KLK6K3Q3.js → tuiSummaryLlmCaller-FESHBDTT.js} +7 -7
  150. package/{updateCommands-NQXAGFM7.js → updateCommands-FJHGWD3Q.js} +5 -3
  151. package/{updateDialogTitleAction-SX53FZWO.js → updateDialogTitleAction-6LURCTMG.js} +21 -21
  152. package/{workloadLifecycle-36DXRZKU.js → workloadLifecycle-6O6PMISX.js} +2 -2
  153. package/chunk-IA7TRNNR.js +0 -11
  154. package/chunk-UKZX3NHN.js +0 -48
  155. package/chunk-VAZHH6FO.js +0 -7012
  156. package/chunk-W4S6OTSC.js +0 -35
  157. package/clipboardy-GCO3GT3I.js +0 -11793
  158. package/settingThunks-FTDRZNSJ.js +0 -1371
  159. package/{agentGrantCommands-HOJ3JBBQ.js → agentGrantCommands-5DPHYEKP.js} +3 -3
  160. package/{chromeOpsCommands-3Z34RKSE.js → chromeOpsCommands-GZVVK7HU.js} +4 -4
  161. package/{chunk-XETG4RIX.js → chunk-AETZ7AD6.js} +4 -4
  162. package/{chunk-2IXDJNZI.js → chunk-GAGGEA7D.js} +3 -3
  163. package/{memoryCommands-CUNBFRVS.js → memoryCommands-ZS3VQJAL.js} +3 -3
  164. package/{offlineMarxistsAgentCommand-2QTHJCHV.js → offlineMarxistsAgentCommand-3GLX4QQY.js} +3 -3
@@ -2,13 +2,13 @@ import {
2
2
  isAgentRunTerminalStatus,
3
3
  normalizeRunTitle
4
4
  } from "./chunk-PJNFNEJE.js";
5
- import {
6
- resolvePlatformAuthToken
7
- } from "./chunk-SUKSWFLP.js";
8
5
  import {
9
6
  isCompiledBinary,
10
7
  resolveCliEntrypointPath
11
8
  } from "./chunk-A7GB5UGH.js";
9
+ import {
10
+ resolvePlatformAuthToken
11
+ } from "./chunk-PIG2IUVZ.js";
12
12
 
13
13
  // packages/cli/client/originShellApprovalTransport.ts
14
14
  import { realpathSync } from "node:fs";
@@ -320,10 +320,635 @@ function createShellApprovalChild(channel, timeoutMs = TTL) {
320
320
 
321
321
  // packages/cli/agentRunControl.ts
322
322
  import { homedir as nodeHomedir } from "node:os";
323
- import { dirname, isAbsolute as isAbsolute2, join, resolve } from "node:path";
324
- import * as nodeFs from "node:fs";
323
+ import { dirname, isAbsolute as isAbsolute2, join as join2, resolve } from "node:path";
324
+ import * as nodeFs2 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
+
554
+ // packages/cli/client/hostMemoryReservation.ts
555
+ import { join } from "path";
556
+ import * as nodeFs from "fs";
557
+
558
+ // packages/cli/client/hostMemoryAdmission.ts
559
+ var LOCAL_AGENT_RUN_MIN_AVAILABLE_MB = 512;
560
+ var STARTUP_WINDOW_MS = 1e4;
561
+ var STARTUP_WINDOW_RESERVED_MB_PER_RUN = 384;
562
+ var HostMemoryAdmissionError = class extends Error {
563
+ code = "HOST_MEMORY_ADMISSION_DENIED";
564
+ details;
565
+ constructor(message, details) {
566
+ super(message);
567
+ this.name = "HostMemoryAdmissionError";
568
+ this.details = details;
569
+ }
570
+ };
571
+ function assertHostMemoryAdmission(deps = {}) {
572
+ const readMem = deps.readHostMemory ?? readHostMemory;
573
+ const snapshot = readMem(deps.hostMemoryDeps);
574
+ const minAvailableMb = deps.minAvailableMb ?? LOCAL_AGENT_RUN_MIN_AVAILABLE_MB;
575
+ const startupWindowMs = deps.startupWindowMs ?? STARTUP_WINDOW_MS;
576
+ const reservedMbPerRun = deps.reservedMbPerRun ?? STARTUP_WINDOW_RESERVED_MB_PER_RUN;
577
+ const now = deps.now ? deps.now().getTime() : Date.now();
578
+ for (const [name, value] of Object.entries({ minAvailableMb, startupWindowMs, reservedMbPerRun, now })) {
579
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) throw new Error(`Invalid ${name}`);
580
+ }
581
+ if (!snapshot) {
582
+ throw new HostMemoryAdmissionError(
583
+ `[host-memory-admission] \u65E0\u6CD5\u6D4B\u5B9A\u672C\u673A\u53EF\u7528\u5185\u5B58\uFF08\u5FEB\u7167\u4E3A null\uFF09\uFF0C\u62D2\u7EDD\u672C\u5730 fork\u3002\u9700\u786E\u4FDD /proc/meminfo \u6216 OS \u5185\u5B58\u6307\u6807\u53EF\u8BFB\u3002`,
584
+ {
585
+ effectiveAvailableMb: null,
586
+ requiredMinMb: minAvailableMb,
587
+ source: "null",
588
+ limitingSource: "unknown",
589
+ activeStartupRuns: 0,
590
+ reservedStartupMb: 0,
591
+ unreservedAvailableMb: null
592
+ }
593
+ );
594
+ }
595
+ if (!["cgroup", "proc-meminfo", "os"].includes(snapshot.source)) throw new Error("Unknown host memory source");
596
+ let effectiveAvailableMb = null;
597
+ let limitingSource = "host";
598
+ if (snapshot.source === "cgroup") {
599
+ const cgroupAvail = snapshot.availableMb;
600
+ const hostAvail = snapshot.hostAvailableMb;
601
+ if (cgroupAvail === null || typeof cgroupAvail !== "number" || Number.isNaN(cgroupAvail) || !Number.isFinite(cgroupAvail) || cgroupAvail < 0) {
602
+ throw new HostMemoryAdmissionError(
603
+ `[host-memory-admission] cgroup \u914D\u989D\u5185\u5B58\u5360\u7528\u672A\u77E5\u6216\u65E0\u6548\uFF0C\u62D2\u7EDD\u65E0\u58F0\u5F53\u5145\u8DB3\u3002`,
604
+ {
605
+ effectiveAvailableMb: null,
606
+ requiredMinMb: minAvailableMb,
607
+ source: snapshot.source,
608
+ limitingSource: "cgroup",
609
+ activeStartupRuns: 0,
610
+ reservedStartupMb: 0,
611
+ unreservedAvailableMb: null
612
+ }
613
+ );
614
+ }
615
+ if (typeof hostAvail !== "number" || !Number.isFinite(hostAvail) || hostAvail < 0) {
616
+ throw new HostMemoryAdmissionError("[host-memory-admission] \u5BBF\u4E3B\u53EF\u7528\u5185\u5B58\u672A\u77E5\u6216\u65E0\u6548\uFF0C\u62D2\u7EDD\u672C\u5730 fork\u3002", {
617
+ effectiveAvailableMb: null,
618
+ requiredMinMb: minAvailableMb,
619
+ source: snapshot.source,
620
+ limitingSource: "host",
621
+ activeStartupRuns: 0,
622
+ reservedStartupMb: 0,
623
+ unreservedAvailableMb: null
624
+ });
625
+ }
626
+ if (Number.isFinite(hostAvail)) {
627
+ if (cgroupAvail <= hostAvail) {
628
+ effectiveAvailableMb = cgroupAvail;
629
+ limitingSource = "cgroup";
630
+ } else {
631
+ effectiveAvailableMb = hostAvail;
632
+ limitingSource = "host";
633
+ }
634
+ } else {
635
+ effectiveAvailableMb = cgroupAvail;
636
+ limitingSource = "cgroup";
637
+ }
638
+ } else {
639
+ if (snapshot.availableMb === null || typeof snapshot.availableMb !== "number" || Number.isNaN(snapshot.availableMb) || !Number.isFinite(snapshot.availableMb) || snapshot.availableMb < 0) {
640
+ throw new HostMemoryAdmissionError(
641
+ `[host-memory-admission] \u5BBF\u4E3B\u53EF\u7528\u5185\u5B58\u672A\u77E5\u6216\u65E0\u6548\uFF0C\u62D2\u7EDD\u65E0\u58F0\u5F53\u5145\u8DB3\u3002`,
642
+ {
643
+ effectiveAvailableMb: null,
644
+ requiredMinMb: minAvailableMb,
645
+ source: snapshot.source,
646
+ limitingSource: "host",
647
+ activeStartupRuns: 0,
648
+ reservedStartupMb: 0,
649
+ unreservedAvailableMb: null
650
+ }
651
+ );
652
+ }
653
+ effectiveAvailableMb = snapshot.availableMb;
654
+ limitingSource = "host";
655
+ }
656
+ let activeStartupRuns = 0;
657
+ if (deps.listRecentRuns) {
658
+ const records = deps.listRecentRuns();
659
+ if (!Array.isArray(records)) {
660
+ throw new HostMemoryAdmissionError(
661
+ `[host-memory-admission] \u83B7\u53D6\u6700\u8FD1\u8FD0\u884C\u8BB0\u5F55\u5217\u8868\u5931\u8D25\uFF0C\u62D2\u7EDD\u672A\u77E5\u5F53\u5F52\u96F6\u3002`,
662
+ {
663
+ effectiveAvailableMb,
664
+ requiredMinMb: minAvailableMb,
665
+ source: snapshot.source,
666
+ limitingSource,
667
+ activeStartupRuns: 0,
668
+ reservedStartupMb: 0,
669
+ unreservedAvailableMb: effectiveAvailableMb
670
+ }
671
+ );
672
+ }
673
+ for (const rec of records) {
674
+ if (!rec || typeof rec !== "object") {
675
+ throw new HostMemoryAdmissionError(
676
+ `[host-memory-admission] \u8FD0\u884C\u8BB0\u5F55\u635F\u574F\uFF0C\u62D2\u7EDD\u672A\u77E5\u5F53\u5F52\u96F6\u3002`,
677
+ {
678
+ effectiveAvailableMb,
679
+ requiredMinMb: minAvailableMb,
680
+ source: snapshot.source,
681
+ limitingSource,
682
+ activeStartupRuns: 0,
683
+ reservedStartupMb: 0,
684
+ unreservedAvailableMb: effectiveAvailableMb
685
+ }
686
+ );
687
+ }
688
+ if (rec.status !== "running") continue;
689
+ const startedTime = new Date(rec.startedAt).getTime();
690
+ if (!Number.isFinite(startedTime) || Number.isNaN(startedTime)) {
691
+ throw new HostMemoryAdmissionError(
692
+ `[host-memory-admission] \u8FD0\u884C\u8BB0\u5F55 startedAt \u65E0\u6548\uFF0C\u62D2\u7EDD\u672A\u77E5\u5F53\u5F52\u96F6\u3002`,
693
+ {
694
+ effectiveAvailableMb,
695
+ requiredMinMb: minAvailableMb,
696
+ source: snapshot.source,
697
+ limitingSource,
698
+ activeStartupRuns: 0,
699
+ reservedStartupMb: 0,
700
+ unreservedAvailableMb: effectiveAvailableMb
701
+ }
702
+ );
703
+ }
704
+ if (now - startedTime >= 0 && now - startedTime < startupWindowMs) {
705
+ activeStartupRuns += 1;
706
+ }
707
+ }
708
+ }
709
+ const reservedStartupMb = activeStartupRuns * reservedMbPerRun;
710
+ const netAvailableMb = effectiveAvailableMb - reservedStartupMb;
711
+ if (netAvailableMb < minAvailableMb) {
712
+ const detailMsg = `\u5F53\u524D\u6709\u6548\u53EF\u7528 ${formatHostMemoryMb(effectiveAvailableMb)}\uFF08\u53D7\u9650\u6E90: ${limitingSource}\uFF09` + (activeStartupRuns > 0 ? `\uFF0C\u5E76\u53D1\u542F\u52A8\u7A97\u53E3\u5185\u6709 ${activeStartupRuns} \u4E2A\u8FDB\u884C\u4E2D\u4EFB\u52A1\uFF08\u9884\u7559 ${formatHostMemoryMb(reservedStartupMb)}\uFF0C\u6298\u7B97\u5269\u4F59 ${formatHostMemoryMb(netAvailableMb)}\uFF09` : "") + `\uFF0C\u4F4E\u4E8E\u5B89\u5168\u9608\u503C ${formatHostMemoryMb(minAvailableMb)}\u3002`;
713
+ throw new HostMemoryAdmissionError(
714
+ `[host-memory-admission] \u62D2\u7EDD\u672C\u5730\u6D3E\u53D1\uFF1A${detailMsg}\u4E3A\u907F\u514D\u89E6\u53D1\u5BBF\u4E3B/cgroup OOM-killer \u5D29\u6E83\uFF0C\u8BF7\u7B49\u5F85\u5176\u4ED6\u4EFB\u52A1\u5B8C\u6210\u6216\u91CA\u653E\u5185\u5B58\u540E\u518D\u8BD5\u3002`,
715
+ {
716
+ effectiveAvailableMb: netAvailableMb,
717
+ requiredMinMb: minAvailableMb,
718
+ source: snapshot.source,
719
+ limitingSource,
720
+ activeStartupRuns,
721
+ reservedStartupMb,
722
+ unreservedAvailableMb: effectiveAvailableMb
723
+ }
724
+ );
725
+ }
726
+ return {
727
+ allowed: true,
728
+ effectiveAvailableMb: netAvailableMb,
729
+ requiredMinMb: minAvailableMb,
730
+ activeStartupRuns,
731
+ snapshot
732
+ };
733
+ }
734
+
735
+ // packages/cli/client/hostMemoryReservation.ts
736
+ var MEMORY_RESERVATION_LOCK_ID = "__host_memory_reservation__";
737
+ function resolveReservationsDir(runsDir) {
738
+ return join(runsDir, "reservations");
739
+ }
740
+ function defaultIsProcessAlive(pid) {
741
+ if (!Number.isInteger(pid) || pid <= 0) throw new Error("Invalid PID");
742
+ try {
743
+ process.kill(pid, 0);
744
+ return true;
745
+ } catch (err) {
746
+ if (err && err.code === "ESRCH") {
747
+ return false;
748
+ }
749
+ return true;
750
+ }
751
+ }
752
+ function sleepSync(ms) {
753
+ if (ms <= 0) return;
754
+ try {
755
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
756
+ } catch {
757
+ const end = Date.now() + ms;
758
+ while (Date.now() < end) {
759
+ }
760
+ }
761
+ }
762
+ function updateReservationFile(filePath, fs, updater) {
763
+ const record = JSON.parse(fs.readFileSync(filePath, "utf8"));
764
+ updater(record);
765
+ const payload = JSON.stringify(record, null, 2);
766
+ if (fs.renameSync) {
767
+ const tmpPath = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
768
+ fs.writeFileSync(tmpPath, payload);
769
+ fs.renameSync(tmpPath, filePath);
770
+ } else {
771
+ fs.writeFileSync(filePath, payload);
772
+ }
773
+ return record;
774
+ }
775
+ function scanActiveReservations(reservationsDir, deps) {
776
+ const { fs, nowMs, startupWindowMs, isProcessAlive } = deps;
777
+ if (!fs.existsSync(reservationsDir)) {
778
+ return { activeStartupRuns: 0, reservedStartupMb: 0 };
779
+ }
780
+ let entries;
781
+ try {
782
+ entries = fs.readdirSync(reservationsDir);
783
+ } catch (err) {
784
+ throw new Error(
785
+ `[host-memory-reservation] \u8BFB\u53D6\u9884\u7559\u76EE\u5F55\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
786
+ );
787
+ }
788
+ let activeStartupRuns = 0;
789
+ let reservedStartupMb = 0;
790
+ for (const entry of entries) {
791
+ if (!entry.endsWith(".json") || !entry.startsWith("reservation-")) continue;
792
+ const filePath = join(reservationsDir, entry);
793
+ let record;
794
+ try {
795
+ const raw = fs.readFileSync(filePath, "utf8");
796
+ record = JSON.parse(raw);
797
+ } catch (err) {
798
+ throw new Error(
799
+ `[host-memory-reservation] \u89E3\u6790\u9884\u7559\u8BB0\u5F55\u6587\u4EF6\u5931\u8D25 (${filePath}): ${err instanceof Error ? err.message : String(err)}\uFF0C\u62D2\u7EDD\u672A\u77E5\u5F53\u5F52\u96F6`
800
+ );
801
+ }
802
+ if (!record || typeof record !== "object") {
803
+ throw new Error(`[host-memory-reservation] \u9884\u7559\u8BB0\u5F55\u683C\u5F0F\u635F\u574F (${filePath})\uFF0C\u62D2\u7EDD\u672A\u77E5\u5F53\u5F52\u96F6`);
804
+ }
805
+ if (typeof record.reservedMb !== "number" || Number.isNaN(record.reservedMb) || !Number.isFinite(record.reservedMb) || record.reservedMb < 0) {
806
+ throw new Error(`[host-memory-reservation] \u9884\u7559\u8BB0\u5F55\u5305\u542B\u65E0\u6548 reservedMb (${filePath})`);
807
+ }
808
+ if (typeof record.createdAtMs !== "number" || Number.isNaN(record.createdAtMs) || !Number.isFinite(record.createdAtMs) || record.createdAtMs < 0) {
809
+ throw new Error(`[host-memory-reservation] \u9884\u7559\u8BB0\u5F55\u5305\u542B\u65E0\u6548 createdAtMs (${filePath})`);
810
+ }
811
+ if (record.status === "released" || record.status === "reclaimed") {
812
+ continue;
813
+ }
814
+ if (record.status !== "reserved" && record.status !== "active") {
815
+ throw new Error(`[host-memory-reservation] invalid status: ${filePath}`);
816
+ }
817
+ const ageMs = nowMs - record.createdAtMs;
818
+ const pid = record.childPid ?? record.holderPid;
819
+ if (!Number.isInteger(pid) || pid <= 0 || ageMs < 0) {
820
+ throw new Error(`[host-memory-reservation] invalid process/time: ${filePath}`);
821
+ }
822
+ const expectedStart = record.childPid ? record.childStartedAt : record.holderStartedAt;
823
+ let alive = true;
824
+ let actualStart;
825
+ try {
826
+ alive = isProcessAlive(pid);
827
+ if (alive && expectedStart) actualStart = deps.getProcessStartTime?.(pid);
828
+ } catch {
829
+ }
830
+ const reused = expectedStart && actualStart && Number.isFinite(actualStart.getTime()) && actualStart.toISOString() !== expectedStart;
831
+ if (!alive || reused || record.status === "active" && ageMs >= startupWindowMs) {
832
+ updateReservationFile(filePath, fs, (r) => {
833
+ r.status = "reclaimed";
834
+ r.reclaimedAt = new Date(nowMs).toISOString();
835
+ r.reason = !alive ? "stale_process_died" : reused ? "pid_reused" : "startup_window_elapsed_rss_accounted";
836
+ });
837
+ continue;
838
+ }
839
+ activeStartupRuns += 1;
840
+ reservedStartupMb += record.reservedMb;
841
+ if (!Number.isFinite(reservedStartupMb)) throw new Error("Invalid reservation total");
842
+ }
843
+ return { activeStartupRuns, reservedStartupMb };
844
+ }
845
+ function reserveHostMemory(deps) {
846
+ const fs = deps.fs ?? nodeFs;
847
+ const runsDir = deps.runsDir;
848
+ const reservationsDir = resolveReservationsDir(runsDir);
849
+ const now = deps.now ? deps.now() : /* @__PURE__ */ new Date();
850
+ let nowMs = now.getTime();
851
+ const minAvailableMb = deps.minAvailableMb ?? LOCAL_AGENT_RUN_MIN_AVAILABLE_MB;
852
+ const startupWindowMs = deps.startupWindowMs ?? STARTUP_WINDOW_MS;
853
+ const requestedMb = deps.requestedMb ?? STARTUP_WINDOW_RESERVED_MB_PER_RUN;
854
+ const isProcessAlive = deps.isProcessAlive ?? defaultIsProcessAlive;
855
+ const maxAttempts = deps.maxLockAttempts ?? 5;
856
+ const delayMs = deps.lockRetryDelayMs ?? 15;
857
+ for (const [name, value] of Object.entries({ requestedMb, minAvailableMb, startupWindowMs, maxAttempts, delayMs, nowMs })) {
858
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
859
+ throw new Error(`[host-memory-reservation] invalid ${name}: ${value}`);
860
+ }
861
+ }
862
+ if (!Number.isInteger(maxAttempts) || maxAttempts < 1) throw new Error("Invalid lock attempts");
863
+ if (!deps.withLock) throw new Error("[host-memory-reservation] exclusive lock required");
864
+ fs.mkdirSync(reservationsDir, { recursive: true });
865
+ const locked = (fn) => {
866
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
867
+ const result = deps.withLock(MEMORY_RESERVATION_LOCK_ID, () => ({ value: fn() }), { strict: true });
868
+ if (result !== void 0) return result.value;
869
+ if (attempt + 1 < maxAttempts) sleepSync(delayMs);
870
+ }
871
+ throw new Error("[host-memory-reservation] exclusive lock timeout");
872
+ };
873
+ const executeReservation = () => {
874
+ nowMs = (deps.now ? deps.now() : /* @__PURE__ */ new Date()).getTime();
875
+ if (!Number.isFinite(nowMs)) throw new Error("Invalid reservation time");
876
+ const { activeStartupRuns, reservedStartupMb } = scanActiveReservations(reservationsDir, {
877
+ fs,
878
+ nowMs,
879
+ startupWindowMs,
880
+ isProcessAlive,
881
+ getProcessStartTime: deps.getProcessStartTime
882
+ });
883
+ assertHostMemoryAdmission({
884
+ readHostMemory: deps.readHostMemory,
885
+ hostMemoryDeps: deps.hostMemoryDeps,
886
+ minAvailableMb: minAvailableMb + reservedStartupMb + requestedMb,
887
+ now: () => new Date(nowMs)
888
+ });
889
+ const id = `res-${nowMs.toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
890
+ const recordPath = join(reservationsDir, `reservation-${id}.json`);
891
+ const record = {
892
+ id,
893
+ runId: deps.runId,
894
+ status: "reserved",
895
+ reservedMb: requestedMb,
896
+ createdAt: new Date(nowMs).toISOString(),
897
+ createdAtMs: nowMs,
898
+ holderPid: process.pid,
899
+ holderStartedAt: deps.getProcessStartTime?.(process.pid)?.toISOString()
900
+ };
901
+ const payload = JSON.stringify(record, null, 2);
902
+ fs.writeFileSync(recordPath, payload, "utf8");
903
+ return {
904
+ id,
905
+ reservedMb: requestedMb,
906
+ recordPath,
907
+ associate(params) {
908
+ if (!params.runId || !Number.isInteger(params.pid) || params.pid <= 0) throw new Error("Invalid child association");
909
+ const validStart = !!params.processStartedAt && Number.isFinite(Date.parse(params.processStartedAt));
910
+ locked(() => updateReservationFile(recordPath, fs, (r) => {
911
+ if (r.status !== "reserved") throw new Error("Reservation is not reserved");
912
+ if (validStart) {
913
+ r.status = "active";
914
+ r.childStartedAt = params.processStartedAt;
915
+ }
916
+ if (params.runId) r.runId = params.runId;
917
+ if (typeof params.pid === "number") r.childPid = params.pid;
918
+ }));
919
+ if (!validStart) throw new Error("Child starttime unknown; reservation retained");
920
+ },
921
+ release(reason) {
922
+ locked(() => updateReservationFile(recordPath, fs, (r) => {
923
+ if (r.status === "reserved" || r.status === "active") {
924
+ r.status = "released";
925
+ r.releasedAt = (/* @__PURE__ */ new Date()).toISOString();
926
+ if (reason) r.reason = reason;
927
+ }
928
+ }));
929
+ }
930
+ };
931
+ };
932
+ if (deps.withLock) {
933
+ const withLock = deps.withLock;
934
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
935
+ const handle = withLock(MEMORY_RESERVATION_LOCK_ID, executeReservation, {
936
+ strict: true
937
+ });
938
+ if (handle !== void 0) {
939
+ return handle;
940
+ }
941
+ if (attempt < maxAttempts - 1) {
942
+ sleepSync(delayMs);
943
+ }
944
+ }
945
+ throw new Error(
946
+ `[host-memory-reservation] \u83B7\u53D6\u9884\u7559\u4E92\u65A5\u9501\u8D85\u65F6\uFF08\u5C1D\u8BD5 ${maxAttempts} \u6B21\uFF09\uFF0C\u62D2\u7EDD\u5E76\u53D1\u51FB\u7A7F`
947
+ );
948
+ }
949
+ throw new Error("[host-memory-reservation] exclusive lock required");
950
+ }
951
+
327
952
  // packages/cli/client/localRuntimeHelpers.ts
328
953
  var REMOTE_DIALOG_SYNC_TIMEOUT_MS = 1e4;
329
954
  var remoteDialogSyncTimeoutMsForTest;
@@ -468,19 +1093,19 @@ function isRunTerminalStatus(status) {
468
1093
  function resolveNoloHome(env, homedir = nodeHomedir) {
469
1094
  const fromEnv = env?.NOLO_HOME;
470
1095
  if (typeof fromEnv === "string" && fromEnv.length > 0) return fromEnv;
471
- return join(homedir(), ".nolo");
1096
+ return join2(homedir(), ".nolo");
472
1097
  }
473
1098
  function resolveRunsDir(env, homedir = nodeHomedir) {
474
- return join(resolveNoloHome(env, homedir), "runs");
1099
+ return join2(resolveNoloHome(env, homedir), "runs");
475
1100
  }
476
1101
  function resolveRunRecordPath(runId, env, homedir = nodeHomedir) {
477
- return join(resolveRunsDir(env, homedir), `${runId}.json`);
1102
+ return join2(resolveRunsDir(env, homedir), `${runId}.json`);
478
1103
  }
479
1104
  function resolveRunLogPath(runId, env, homedir = nodeHomedir) {
480
- return join(resolveRunsDir(env, homedir), `${runId}.log`);
1105
+ return join2(resolveRunsDir(env, homedir), `${runId}.log`);
481
1106
  }
482
1107
  function resolveRunQueuePath(runId, env, homedir = nodeHomedir) {
483
- return join(resolveRunsDir(env, homedir), `${runId}.queue.jsonl`);
1108
+ return join2(resolveRunsDir(env, homedir), `${runId}.queue.jsonl`);
484
1109
  }
485
1110
  function defaultGenerateQueueEntryId() {
486
1111
  const ts = Date.now().toString(36);
@@ -519,7 +1144,7 @@ function _serializeQueueEntries(entries) {
519
1144
  return entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
520
1145
  }
521
1146
  var serializeQueueEntries = _serializeQueueEntries;
522
- function _countQueueMessagesLocked(queuePath, fs = nodeFs) {
1147
+ function _countQueueMessagesLocked(queuePath, fs = nodeFs2) {
523
1148
  if (!fs.existsSync(queuePath)) return 0;
524
1149
  try {
525
1150
  const content = fs.readFileSync(queuePath, "utf8");
@@ -528,7 +1153,7 @@ function _countQueueMessagesLocked(queuePath, fs = nodeFs) {
528
1153
  return 0;
529
1154
  }
530
1155
  }
531
- function _readQueueMessagesLocked(queuePath, fs = nodeFs) {
1156
+ function _readQueueMessagesLocked(queuePath, fs = nodeFs2) {
532
1157
  if (!fs.existsSync(queuePath)) return [];
533
1158
  try {
534
1159
  const content = fs.readFileSync(queuePath, "utf8");
@@ -537,7 +1162,7 @@ function _readQueueMessagesLocked(queuePath, fs = nodeFs) {
537
1162
  return [];
538
1163
  }
539
1164
  }
540
- function _readQueueEntriesLocked(queuePath, fs = nodeFs) {
1165
+ function _readQueueEntriesLocked(queuePath, fs = nodeFs2) {
541
1166
  if (!fs.existsSync(queuePath)) return [];
542
1167
  try {
543
1168
  const content = fs.readFileSync(queuePath, "utf8");
@@ -604,13 +1229,13 @@ async function withQueueLock(queuePath, fnOrDeps, depsOrFnOrOpts, maybeOpts) {
604
1229
  fn = depsOrFnOrOpts;
605
1230
  }
606
1231
  const deps = normalizeQueueDeps(rawDeps);
607
- const fs = deps.fs ?? nodeFs;
1232
+ const fs = deps.fs ?? nodeFs2;
608
1233
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
609
1234
  const staleLockMs = opts.staleLockMs ?? QUEUE_LOCK_STALE_MS;
610
1235
  const retries = opts.retries ?? QUEUE_LOCK_RETRIES;
611
1236
  const retryIntervalMs = opts.retryIntervalMs ?? QUEUE_LOCK_RETRY_INTERVAL_MS;
612
1237
  const lockPath = `${queuePath}.lock`;
613
- const tokenPath = join(lockPath, "owner.json");
1238
+ const tokenPath = join2(lockPath, "owner.json");
614
1239
  try {
615
1240
  fs.mkdirSync(dirname(lockPath), { recursive: true });
616
1241
  } catch {
@@ -703,7 +1328,7 @@ async function withQueueLock(queuePath, fnOrDeps, depsOrFnOrOpts, maybeOpts) {
703
1328
  }
704
1329
  async function appendRunQueue(queuePath, input, depsOrFs = {}) {
705
1330
  const deps = normalizeQueueDeps(depsOrFs);
706
- const fs = deps.fs ?? nodeFs;
1331
+ const fs = deps.fs ?? nodeFs2;
707
1332
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
708
1333
  const text = typeof input === "string" ? input : input.text;
709
1334
  const entryId = typeof input === "object" && typeof input.id === "string" && input.id.trim() ? input.id.trim() : defaultGenerateQueueEntryId();
@@ -722,7 +1347,7 @@ async function appendRunQueue(queuePath, input, depsOrFs = {}) {
722
1347
  }
723
1348
  async function countQueueMessages(queuePath, depsOrFs = {}) {
724
1349
  const deps = normalizeQueueDeps(depsOrFs);
725
- const fs = deps.fs ?? nodeFs;
1350
+ const fs = deps.fs ?? nodeFs2;
726
1351
  if (!fs.existsSync(queuePath)) return 0;
727
1352
  return await withQueueLock(queuePath, deps, async () => {
728
1353
  return _countQueueMessagesLocked(queuePath, fs);
@@ -730,7 +1355,7 @@ async function countQueueMessages(queuePath, depsOrFs = {}) {
730
1355
  }
731
1356
  async function readQueueMessages(queuePath, depsOrFs = {}) {
732
1357
  const deps = normalizeQueueDeps(depsOrFs);
733
- const fs = deps.fs ?? nodeFs;
1358
+ const fs = deps.fs ?? nodeFs2;
734
1359
  if (!fs.existsSync(queuePath)) return [];
735
1360
  return await withQueueLock(queuePath, deps, async () => {
736
1361
  return _readQueueMessagesLocked(queuePath, fs);
@@ -738,7 +1363,7 @@ async function readQueueMessages(queuePath, depsOrFs = {}) {
738
1363
  }
739
1364
  async function readQueueEntries(queuePath, depsOrFs = {}) {
740
1365
  const deps = normalizeQueueDeps(depsOrFs);
741
- const fs = deps.fs ?? nodeFs;
1366
+ const fs = deps.fs ?? nodeFs2;
742
1367
  if (!fs.existsSync(queuePath)) return [];
743
1368
  return await withQueueLock(queuePath, deps, async () => {
744
1369
  return _readQueueEntriesLocked(queuePath, fs);
@@ -746,7 +1371,7 @@ async function readQueueEntries(queuePath, depsOrFs = {}) {
746
1371
  }
747
1372
  async function popAllQueueEntries(queuePath, depsOrFs = {}) {
748
1373
  const deps = normalizeQueueDeps(depsOrFs);
749
- const fs = deps.fs ?? nodeFs;
1374
+ const fs = deps.fs ?? nodeFs2;
750
1375
  return await withQueueLock(queuePath, deps, async () => {
751
1376
  if (!fs.existsSync(queuePath)) return [];
752
1377
  const content = fs.readFileSync(queuePath, "utf8");
@@ -757,7 +1382,7 @@ async function popAllQueueEntries(queuePath, depsOrFs = {}) {
757
1382
  }
758
1383
  async function popSingleQueueMessage(queuePath, depsOrFs = {}) {
759
1384
  const deps = normalizeQueueDeps(depsOrFs);
760
- const fs = deps.fs ?? nodeFs;
1385
+ const fs = deps.fs ?? nodeFs2;
761
1386
  return await withQueueLock(queuePath, deps, async () => {
762
1387
  if (!fs.existsSync(queuePath)) return null;
763
1388
  const content = fs.readFileSync(queuePath, "utf8");
@@ -792,15 +1417,19 @@ function defaultGenerateBatchId() {
792
1417
  var LOCK_STALE_MS = 5e3;
793
1418
  var TMP_STALE_MS = 6e4;
794
1419
  function withRunRecordLock(runId, deps, fn, opts = {}) {
795
- const fs = deps.fs ?? nodeFs;
1420
+ const fs = deps.fs ?? nodeFs2;
796
1421
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
797
1422
  const lockPath = `${resolveRunRecordPath(runId, deps.env, deps.homedir)}.lock`;
798
1423
  let held = false;
799
1424
  try {
1425
+ fs.mkdirSync(dirname(lockPath), { recursive: true });
800
1426
  try {
801
1427
  fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
802
1428
  held = true;
803
- } catch {
1429
+ } catch (err) {
1430
+ if (err?.code !== "EEXIST") {
1431
+ throw err;
1432
+ }
804
1433
  try {
805
1434
  const stat = fs.statSync?.(lockPath);
806
1435
  const ageMs = stat ? now().getTime() - stat.mtimeMs : Number.POSITIVE_INFINITY;
@@ -824,7 +1453,7 @@ function withRunRecordLock(runId, deps, fn, opts = {}) {
824
1453
  }
825
1454
  }
826
1455
  function writeRunRecord(record, deps = {}) {
827
- const fs = deps.fs ?? nodeFs;
1456
+ const fs = deps.fs ?? nodeFs2;
828
1457
  const path = resolveRunRecordPath(record.runId, deps.env, deps.homedir);
829
1458
  fs.mkdirSync(resolveRunsDir(deps.env, deps.homedir), { recursive: true });
830
1459
  const payload = JSON.stringify(record, null, 2);
@@ -846,7 +1475,7 @@ function writeRunRecord(record, deps = {}) {
846
1475
  var FILE_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["writeFile", "editFile"]);
847
1476
  var DEFAULT_ACTIVITY_WRITE_INTERVAL_MS = 2e3;
848
1477
  function createRunActivityTracker(runId, deps = {}, options = {}) {
849
- const fs = deps.fs ?? nodeFs;
1478
+ const fs = deps.fs ?? nodeFs2;
850
1479
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
851
1480
  const minWriteIntervalMs = options.minWriteIntervalMs ?? DEFAULT_ACTIVITY_WRITE_INTERVAL_MS;
852
1481
  const keepAlive = options.keepAlive ?? true;
@@ -941,7 +1570,7 @@ function createRunActivityTracker(runId, deps = {}, options = {}) {
941
1570
  return { onLoopEvent, getActivity, flush, dispose };
942
1571
  }
943
1572
  function readRunRecord(runId, deps = {}) {
944
- const fs = deps.fs ?? nodeFs;
1573
+ const fs = deps.fs ?? nodeFs2;
945
1574
  const path = resolveRunRecordPath(runId, deps.env, deps.homedir);
946
1575
  for (let attempt = 0; attempt < 2; attempt += 1) {
947
1576
  try {
@@ -963,7 +1592,7 @@ function backfillRunRecordParentDialog(runId, parentDialogId, deps = {}) {
963
1592
  return updated === true;
964
1593
  }
965
1594
  function listRunRecords(deps = {}) {
966
- const fs = deps.fs ?? nodeFs;
1595
+ const fs = deps.fs ?? nodeFs2;
967
1596
  const dir = resolveRunsDir(deps.env, deps.homedir);
968
1597
  let entries = [];
969
1598
  try {
@@ -1034,7 +1663,7 @@ function queryRunRecords(query, deps = {}) {
1034
1663
  }
1035
1664
  var DEFAULT_GC_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
1036
1665
  async function gcRunRecords(deps = {}, options = {}) {
1037
- const fs = deps.fs ?? nodeFs;
1666
+ const fs = deps.fs ?? nodeFs2;
1038
1667
  const now = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
1039
1668
  const nowMs = now instanceof Date ? now.getTime() : Date.parse(String(now));
1040
1669
  const retentionMs = typeof options.retentionMs === "number" && options.retentionMs >= 0 ? options.retentionMs : DEFAULT_GC_RETENTION_MS;
@@ -1049,7 +1678,7 @@ async function gcRunRecords(deps = {}, options = {}) {
1049
1678
  const jsonPath = resolveRunRecordPath(record.runId, deps.env, deps.homedir);
1050
1679
  const logPath = resolveRunLogPath(record.runId, deps.env, deps.homedir);
1051
1680
  const queuePath = record.queuePath ?? resolveRunQueuePath(record.runId, deps.env, deps.homedir);
1052
- const msgPath = record.msgFile ?? join(resolveRunsDir(deps.env, deps.homedir), `${record.runId}.msg.md`);
1681
+ const msgPath = record.msgFile ?? join2(resolveRunsDir(deps.env, deps.homedir), `${record.runId}.msg.md`);
1053
1682
  const logOk = tryUnlinkFile(fs, logPath);
1054
1683
  const msgOk = tryUnlinkFile(fs, msgPath);
1055
1684
  let queueOk = true;
@@ -1063,9 +1692,9 @@ async function gcRunRecords(deps = {}, options = {}) {
1063
1692
  }
1064
1693
  }
1065
1694
  const runsDirForLegacy = resolveRunsDir(deps.env, deps.homedir);
1066
- tryUnlinkFile(fs, join(runsDirForLegacy, `${record.runId}.report.md`));
1067
- tryUnlinkFile(fs, join(runsDirForLegacy, `${record.runId}.report.json`));
1068
- tryUnlinkFile(fs, record.resultFile ?? join(runsDirForLegacy, `${record.runId}.result.md`));
1695
+ tryUnlinkFile(fs, join2(runsDirForLegacy, `${record.runId}.report.md`));
1696
+ tryUnlinkFile(fs, join2(runsDirForLegacy, `${record.runId}.report.json`));
1697
+ tryUnlinkFile(fs, record.resultFile ?? join2(runsDirForLegacy, `${record.runId}.result.md`));
1069
1698
  if (!logOk || !msgOk || !queueOk) {
1070
1699
  failedIds.push(record.runId);
1071
1700
  continue;
@@ -1082,7 +1711,7 @@ async function gcRunRecords(deps = {}, options = {}) {
1082
1711
  const statSync = fs.statSync;
1083
1712
  for (const entry of fs.readdirSync(dir)) {
1084
1713
  if (typeof entry !== "string" || !entry.endsWith(".tmp")) continue;
1085
- const tmpPath = join(dir, entry);
1714
+ const tmpPath = join2(dir, entry);
1086
1715
  try {
1087
1716
  const stat = statSync?.(tmpPath);
1088
1717
  if (stat && nowMs - stat.mtimeMs <= TMP_STALE_MS) continue;
@@ -1143,7 +1772,7 @@ function rewriteMsgFileArg(args, messagePath) {
1143
1772
  }
1144
1773
  return result;
1145
1774
  }
1146
- function resolveValidCliEntrypoint(cliEntrypointPath, fs = nodeFs) {
1775
+ function resolveValidCliEntrypoint(cliEntrypointPath, fs = nodeFs2) {
1147
1776
  if (cliEntrypointPath && fs.existsSync(cliEntrypointPath)) {
1148
1777
  return cliEntrypointPath;
1149
1778
  }
@@ -1152,11 +1781,11 @@ function resolveValidCliEntrypoint(cliEntrypointPath, fs = nodeFs) {
1152
1781
  return fallback;
1153
1782
  }
1154
1783
  const candidates = [
1155
- join(dirname(fallback), "cli", "index.ts"),
1156
- join(dirname(fallback), "cli", "index.js"),
1157
- join(dirname(fallback), "..", "packages", "cli", "index.ts"),
1158
- join(dirname(fallback), "..", "packages", "cli", "index.js"),
1159
- join(dirname(fallback), "..", "index.js")
1784
+ join2(dirname(fallback), "cli", "index.ts"),
1785
+ join2(dirname(fallback), "cli", "index.js"),
1786
+ join2(dirname(fallback), "..", "packages", "cli", "index.ts"),
1787
+ join2(dirname(fallback), "..", "packages", "cli", "index.js"),
1788
+ join2(dirname(fallback), "..", "index.js")
1160
1789
  ];
1161
1790
  for (const cand of candidates) {
1162
1791
  if (fs.existsSync(cand)) {
@@ -1166,7 +1795,7 @@ function resolveValidCliEntrypoint(cliEntrypointPath, fs = nodeFs) {
1166
1795
  return fallback;
1167
1796
  }
1168
1797
  function buildAgentRunChildCommand(options, deps = {}) {
1169
- const fs = deps.fs ?? nodeFs;
1798
+ const fs = deps.fs ?? nodeFs2;
1170
1799
  const execPath = process.execPath;
1171
1800
  const commandParts = options.commandPath ?? [];
1172
1801
  let strippedArgs = stripBackgroundFlag(options.rawArgs);
@@ -1183,10 +1812,27 @@ function buildAgentRunChildCommand(options, deps = {}) {
1183
1812
  }
1184
1813
  return { execPath, childArgs: [entrypoint, ...commandParts, ...strippedArgs] };
1185
1814
  }
1815
+ function emitHostMemoryNotice(mem, output) {
1816
+ if (!mem || !output) return void 0;
1817
+ let notice;
1818
+ try {
1819
+ notice = formatHostMemoryLine(mem);
1820
+ } catch {
1821
+ return void 0;
1822
+ }
1823
+ try {
1824
+ output.write(`${notice}
1825
+ `);
1826
+ } catch {
1827
+ return notice;
1828
+ }
1829
+ return notice;
1830
+ }
1186
1831
  async function spawnLocalBackgroundRun(input, deps = {}) {
1187
- const env = deps.env ?? process.env;
1188
1832
  const homedir = deps.homedir ?? nodeHomedir;
1189
- const fs = deps.fs ?? nodeFs;
1833
+ const env = deps.env ?? (deps.homedir ? { ...process.env, NOLO_HOME: join2(homedir(), ".nolo") } : process.env);
1834
+ const resolvedDeps = { ...deps, env, homedir };
1835
+ const fs = deps.fs ?? nodeFs2;
1190
1836
  const spawn = deps.spawn ?? nodeSpawn;
1191
1837
  const generateRunId = deps.generateRunId ?? defaultGenerateRunId;
1192
1838
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
@@ -1199,7 +1845,7 @@ async function spawnLocalBackgroundRun(input, deps = {}) {
1199
1845
  fs.mkdirSync(runsDir, { recursive: true });
1200
1846
  let messagePath;
1201
1847
  if (typeof input.message === "string") {
1202
- messagePath = join(runsDir, `${runId}.msg.md`);
1848
+ messagePath = join2(runsDir, `${runId}.msg.md`);
1203
1849
  fs.writeFileSync(messagePath, input.message);
1204
1850
  }
1205
1851
  const rawDodCommands = [];
@@ -1266,33 +1912,100 @@ async function spawnLocalBackgroundRun(input, deps = {}) {
1266
1912
  NOLO_AGENT_RUN_CHILD: "1",
1267
1913
  NOLO_AGENT_RUN_ID: runId
1268
1914
  };
1915
+ let hostMemory = null;
1916
+ let reservation;
1269
1917
  const logFd = fs.openSync(logPath, "a");
1270
- const proc = spawn(execPath, childArgs, {
1271
- cwd: input.cwd,
1272
- env: childEnv,
1273
- detached: true,
1274
- stdio: deps.shellApprovalHost ? ["ignore", logFd, logFd, "ipc"] : ["ignore", logFd, logFd]
1918
+ const reservationFs = {
1919
+ existsSync: (path) => fs.existsSync(path),
1920
+ readFileSync: (path, encoding) => fs.readFileSync(path, encoding),
1921
+ writeFileSync: (path, data, options) => fs.writeFileSync(path, data, options),
1922
+ mkdirSync: (path, options) => fs.mkdirSync(path, options),
1923
+ readdirSync: (path) => fs.readdirSync(path),
1924
+ ...fs.renameSync ? { renameSync: (from, to) => fs.renameSync(from, to) } : {},
1925
+ ...fs.statSync ? { statSync: (path) => ({ mtimeMs: fs.statSync(path).mtimeMs }) } : {}
1926
+ };
1927
+ let proc;
1928
+ try {
1929
+ reservation = reserveHostMemory({
1930
+ runsDir,
1931
+ runId,
1932
+ fs: reservationFs,
1933
+ now: resolvedDeps.now,
1934
+ readHostMemory: () => hostMemory = (resolvedDeps.readHostMemory ?? readHostMemory)(),
1935
+ getProcessStartTime: resolvedDeps.getProcessStartTime ?? defaultGetProcessStartTime,
1936
+ withLock: (id, fn, opts) => withRunRecordLock(id, resolvedDeps, fn, opts)
1937
+ });
1938
+ proc = spawn(execPath, childArgs, {
1939
+ cwd: input.cwd,
1940
+ env: childEnv,
1941
+ detached: true,
1942
+ stdio: deps.shellApprovalHost ? ["ignore", logFd, logFd, "ipc"] : ["ignore", logFd, logFd]
1943
+ });
1944
+ } catch (error) {
1945
+ try {
1946
+ reservation?.release("spawn_sync_failed");
1947
+ } finally {
1948
+ finalizeRunRecord(runId, { status: "failed", exitCode: 1, note: String(error) }, resolvedDeps);
1949
+ }
1950
+ throw error;
1951
+ } finally {
1952
+ fs.closeSync?.(logFd);
1953
+ }
1954
+ proc.once?.("error", (error) => {
1955
+ try {
1956
+ reservation.release("child_error");
1957
+ } catch (releaseError) {
1958
+ finalizeRunRecord(runId, {
1959
+ status: "failed",
1960
+ exitCode: 1,
1961
+ note: `${error}; reservation release failed: ${releaseError}`
1962
+ }, resolvedDeps);
1963
+ return;
1964
+ }
1965
+ finalizeRunRecord(runId, { status: "failed", exitCode: 1, note: String(error) }, resolvedDeps);
1275
1966
  });
1967
+ const hostMemoryNotice = emitHostMemoryNotice(hostMemory, input.output);
1968
+ if (typeof proc.pid === "number") {
1969
+ let processStartedAt;
1970
+ try {
1971
+ processStartedAt = (resolvedDeps.getProcessStartTime ?? defaultGetProcessStartTime)(proc.pid);
1972
+ if (processStartedAt && !Number.isFinite(processStartedAt.getTime())) processStartedAt = null;
1973
+ } catch {
1974
+ processStartedAt = null;
1975
+ }
1976
+ withRunRecordLock(runId, resolvedDeps, () => {
1977
+ const latest = readRunRecord(runId, resolvedDeps) ?? record;
1978
+ latest.pid = proc.pid;
1979
+ if (processStartedAt) latest.processStartedAt = processStartedAt.toISOString();
1980
+ writeRunRecord(latest, resolvedDeps);
1981
+ return true;
1982
+ }, { strict: true });
1983
+ try {
1984
+ const persisted = readRunRecord(runId, resolvedDeps);
1985
+ reservation.associate({ runId, pid: proc.pid, processStartedAt: processStartedAt?.toISOString() });
1986
+ if (persisted?.pid !== proc.pid || !persisted.processStartedAt) throw new Error("Child identity persistence failed");
1987
+ } catch (error) {
1988
+ throw new Error(`[host-memory-reservation] child ${proc.pid}, run ${runId}: association failed: ${error}`);
1989
+ }
1990
+ }
1276
1991
  if (deps.shellApprovalHost) {
1277
1992
  attachShellApprovalHost(proc, { ...deps.shellApprovalHost, binding: {
1278
1993
  ...deps.shellApprovalHost.binding,
1279
1994
  runId,
1280
1995
  agentKey: input.agentKey,
1281
- workspaceRoot: nodeFs.realpathSync(input.cwd ?? process.cwd())
1996
+ workspaceRoot: nodeFs2.realpathSync(input.cwd ?? process.cwd())
1282
1997
  } });
1283
1998
  proc.channel?.unref?.();
1284
1999
  }
1285
2000
  proc.unref();
1286
- if (typeof proc.pid === "number") {
1287
- const processStartedAt = (deps.getProcessStartTime ?? defaultGetProcessStartTime)(proc.pid);
1288
- withRunRecordLock(runId, deps, () => {
1289
- const latest = readRunRecord(runId, deps) ?? record;
1290
- latest.pid = proc.pid;
1291
- if (processStartedAt) latest.processStartedAt = processStartedAt.toISOString();
1292
- writeRunRecord(latest, deps);
1293
- });
1294
- }
1295
- return { runId, pid: proc.pid, logPath, queuePath, batchId };
2001
+ return {
2002
+ runId,
2003
+ pid: proc.pid,
2004
+ logPath,
2005
+ queuePath,
2006
+ batchId,
2007
+ ...hostMemoryNotice ? { hostMemoryNotice } : {}
2008
+ };
1296
2009
  }
1297
2010
  function ackRunRecord(runId, deps = {}) {
1298
2011
  withRunRecordLock(runId, deps, () => {
@@ -1402,8 +2115,8 @@ function transitionRunToTerminal(runId, update, deps = {}, options = {}) {
1402
2115
  record2.endedAt = now().toISOString();
1403
2116
  if (typeof update.lastAssistantText === "string" && update.lastAssistantText) {
1404
2117
  try {
1405
- const fs = deps.fs ?? nodeFs;
1406
- const resultFile = join(resolveRunsDir(deps.env, deps.homedir), `${runId}.result.md`);
2118
+ const fs = deps.fs ?? nodeFs2;
2119
+ const resultFile = join2(resolveRunsDir(deps.env, deps.homedir), `${runId}.result.md`);
1407
2120
  const startedMs = Date.parse(record2.startedAt);
1408
2121
  const endedMs = Date.parse(record2.endedAt);
1409
2122
  const content = buildResultFileContent(
@@ -1570,7 +2283,7 @@ function formatDuration(startedAt, endedAt) {
1570
2283
  return `${seconds}s`;
1571
2284
  }
1572
2285
  function readLastLogLines(logPath, count, deps) {
1573
- const fs = deps.fs ?? nodeFs;
2286
+ const fs = deps.fs ?? nodeFs2;
1574
2287
  try {
1575
2288
  const content = fs.readFileSync(logPath, "utf8");
1576
2289
  const lines = content.split("\n");
@@ -1581,7 +2294,7 @@ function readLastLogLines(logPath, count, deps) {
1581
2294
  }
1582
2295
  }
1583
2296
  function readLogContent(logPath, tailCount, deps) {
1584
- const fs = deps.fs ?? nodeFs;
2297
+ const fs = deps.fs ?? nodeFs2;
1585
2298
  try {
1586
2299
  const content = fs.readFileSync(logPath, "utf8");
1587
2300
  if (typeof tailCount === "number" && tailCount > 0) {
@@ -1868,7 +2581,7 @@ async function runAgentLogsCommand(args, deps) {
1868
2581
  `);
1869
2582
  return 1;
1870
2583
  }
1871
- const fs = deps.fs ?? nodeFs;
2584
+ const fs = deps.fs ?? nodeFs2;
1872
2585
  if (!fs.existsSync(record.logPath)) {
1873
2586
  deps.output.write(`Log not found: ${record.logPath}
1874
2587
  `);
@@ -1971,6 +2684,8 @@ async function runAgentKillCommand(args, deps) {
1971
2684
 
1972
2685
  export {
1973
2686
  createShellApprovalChild,
2687
+ readHostMemory,
2688
+ assertHostMemoryAdmission,
1974
2689
  setRemoteDialogSyncTimeoutForTest,
1975
2690
  remoteDialogSyncTimeout,
1976
2691
  resolveRuntimeServerUrl,
@@ -2008,6 +2723,7 @@ export {
2008
2723
  popQueueMessages,
2009
2724
  defaultGenerateRunId,
2010
2725
  defaultGenerateBatchId,
2726
+ withRunRecordLock,
2011
2727
  writeRunRecord,
2012
2728
  createRunActivityTracker,
2013
2729
  readRunRecord,