elanous 0.2.43 → 0.2.44

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 (544) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/{Om5xS9jpjSXJCeTNGYEi4 → HMXsxdy5MIIcHPMHv1wD4}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{3557ac4a.c8931684475018ad.js → 08334415.f0e80896acbef722.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1032-78ba4f6391258cab.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1082-53f86a26e03d309d.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1109-7d0e264f6f2edbc7.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1124-7c26330402481143.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/118.2a89982fe4276880.js +23 -0
  10. package/apps/pwa/out/_next/static/chunks/1244.6c435dbf1bf21d8a.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1252.9e665904d3aa3b1a.js +32 -0
  12. package/apps/pwa/out/_next/static/chunks/1266.a46cc33cb6a72d75.js +104 -0
  13. package/apps/pwa/out/_next/static/chunks/{3955.6922286237f2ac2b.js → 1306.967240ede9a0f757.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/1344.3b95f3bd1b77a3fe.js +94 -0
  15. package/apps/pwa/out/_next/static/chunks/{5628.6ddb2d5ffba4ff4c.js → 1401.22dfe92640a2b1ac.js} +4 -4
  16. package/apps/pwa/out/_next/static/chunks/1431-f7c78bd5be21e011.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1451-cdd35a8a0b31834b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{2573.387a707f6f9d720c.js → 1694.18debfd77562e56d.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/1708-f03d587b7a7545c8.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1775-fa7152bdeff6c95f.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/178-4da509cd7266672a.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/{7910-6f139bcafd238b9f.js → 1850-522de4ee34957210.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/1876-c6f6e0ddcc76c6a1.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/1976.097066b072432242.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{1406.a1dc2ab6f8449374.js → 1978.eeb60a606dc5deac.js} +2 -2
  26. package/apps/pwa/out/_next/static/chunks/{3402.44b565b314f2263c.js → 2043.e233e3b5a3e790f5.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/2150.71b94e3348107c83.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2158.ebc6fc5a10280ab7.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{3694.e0099884345f0198.js → 2216.802962c04f40a26e.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/2294.7890edcfa046c97d.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/235.c89aaae4bcd6233a.js +74 -0
  32. package/apps/pwa/out/_next/static/chunks/239.de075467b5523469.js +287 -0
  33. package/apps/pwa/out/_next/static/chunks/{1200.6a37c202f9f17e9a.js → 249.ba7051590d603c8f.js} +3 -3
  34. package/apps/pwa/out/_next/static/chunks/2571.a6ac4e26c8618403.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/2610.e149cd564a08913e.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/2705.9816f3c244f8b85f.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/2801.afeb8a4dae22f85f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{3935.16701e33f8982dc9.js → 2954.bbefc95d6b20e0d8.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/{aa57532f.510551854ff239b1.js → 2e0f720a.a0697c08e3806a60.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/3042.96ec235fe41c8d4e.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3097-8e358f158acef46b.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3205-93b1abe28b4fb16a.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3273.b4434f0dcae7eb29.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3311.9cbc43ea0c90d60c.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{5930.8b4df3b46c32b05a.js → 3349.b5f09ee474d9533e.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/{5866.366966af47fdde5d.js → 3503.a47a9755fb4d97b0.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/3506-5f429b3b7e1392b0.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{7900.096f455c7c1efc53.js → 3843.bad404d008893291.js} +2 -2
  49. package/apps/pwa/out/_next/static/chunks/3877-4b40e530b47e3bf9.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/435-ce2958253cf7de61.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/{8588.d1c6a2903727ce87.js → 4393.28fa5228d0eec217.js} +3 -3
  52. package/apps/pwa/out/_next/static/chunks/4404.7281c847c6e10dfc.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/{8062-7cd0bcc7f01455ba.js → 4415-b9d6a90960920a4a.js} +3 -3
  54. package/apps/pwa/out/_next/static/chunks/{8313-dfa1021bb58a46d6.js → 447-5b304de1fe098f43.js} +1 -1
  55. package/apps/pwa/out/_next/static/chunks/4484-8ebea83d431e6fae.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/{6944.309f4e3ea54d1537.js → 4502.bdcc877abd437a05.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/4599-f95790fc5c65b8df.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{3072.01338851ca21a6f7.js → 46.57c783fa4884714f.js} +2 -2
  59. package/apps/pwa/out/_next/static/chunks/463-b9114e411f33728e.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/4659.a57d1f0c5d754cd2.js +36 -0
  61. package/apps/pwa/out/_next/static/chunks/4722-a847e7a28115035f.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/{8986.8e2e4efbfa655c59.js → 4757.67c6d7473a038e78.js} +1 -1
  63. package/apps/pwa/out/_next/static/chunks/4874-c86f878ec4502ad3.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/4891.3f80ba517a3b2373.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/{8205.282ca4bb62d54a0a.js → 4998.810ca8833084caa9.js} +2 -2
  66. package/apps/pwa/out/_next/static/chunks/4f98febc.ea5841d4d616bfb2.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/{5789.2da35c258292e1e5.js → 5052.7dbb12a55d4f18f5.js} +1 -1
  68. package/apps/pwa/out/_next/static/chunks/5166-ca73b3e361085a6d.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5180-039f4a18b07ad387.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/5232.5a2ce4daf7a9e11d.js +115 -0
  71. package/apps/pwa/out/_next/static/chunks/{9770.5f75bc6088b16109.js → 5439.3fec6dbce29ecb36.js} +1 -1
  72. package/apps/pwa/out/_next/static/chunks/{1313-19f737e0222c4444.js → 5612-074d9a5e00dfac6a.js} +1 -1
  73. package/apps/pwa/out/_next/static/chunks/5675-01989d524a84c505.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/5751.af641a1adf5ec697.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/5758-c6cb98714d18ab0c.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/578eded8.b582637306e16964.js +122 -0
  77. package/apps/pwa/out/_next/static/chunks/{3969.272ee7f9fd6c62e5.js → 5790.5eb06a653ebd8571.js} +1 -1
  78. package/apps/pwa/out/_next/static/chunks/5853-4033e5bd5afbe06b.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/5904.97d70bb7f46b2db7.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/{6540.c7bef57ad8f90a87.js → 5989.7ecd362c5c44b747.js} +3 -3
  81. package/apps/pwa/out/_next/static/chunks/{0ad6802e.ae7afb63fdcb4491.js → 5a34fb2f.52f1b4e1baebb48a.js} +3 -3
  82. package/apps/pwa/out/_next/static/chunks/{0966f8a1.745fcd44f64a87ea.js → 5a5078f8.2099200e5ea95e16.js} +1 -1
  83. package/apps/pwa/out/_next/static/chunks/6036-c302c34ff0b3d410.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/606.ef81c313381f4264.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/616.1af54a928b1ba704.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/6201.bc936f71f613a8e9.js +41 -0
  87. package/apps/pwa/out/_next/static/chunks/6204-fdc86e0e64c4f92d.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/{2144.25b292396712196d.js → 6245.1182e007e9d230d9.js} +2 -2
  89. package/apps/pwa/out/_next/static/chunks/{363bfab3.9623b9f89363541f.js → 63699a35.17262e162bc514b2.js} +1 -1
  90. package/apps/pwa/out/_next/static/chunks/6420.494801a80030160c.js +27 -0
  91. package/apps/pwa/out/_next/static/chunks/{d96660fb.cbc21a2a88539dbb.js → 6549dad0.2740b25e51bf8f05.js} +7 -7
  92. package/apps/pwa/out/_next/static/chunks/{5564.9e72532188f57586.js → 6881.9e7955847640c7fe.js} +4 -4
  93. package/apps/pwa/out/_next/static/chunks/6931.fcdfb94141e67b3d.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/6991-8e081c6ce38c0ba1.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{3903.3ef4656c67572764.js → 7010.1bbd17a7f2b3e376.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/7029.d1466acf55fae2ed.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7152-a123aa8e129c131f.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7455.196168fffac3b9c4.js +24 -0
  99. package/apps/pwa/out/_next/static/chunks/7638.6ca4770dd1129138.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/7649-f37d76260e5e2a5a.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/{2312.0c8b61e22d3136d7.js → 7679.32c40cdd14f20b4a.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/7751.04bfbd16b4c6cd0a.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/7864.b1ff31d4030b8994.js +148 -0
  104. package/apps/pwa/out/_next/static/chunks/7869-c47bd2c075c1fefa.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{4199.ce86b281fd0a60b7.js → 8442.2f09ca969d631615.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/8445.2738dbd59a78c78c.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8447.5510c439f9938c6d.js +286 -0
  108. package/apps/pwa/out/_next/static/chunks/8551.f762945c2cda2e95.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/8635-57a9d8d9db70f4f1.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{fb7d20d5-6892502b6ee39ca6.js → 86961958-4b0689a5048f756b.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/8709.3c6019bb18f8554f.js +8 -0
  112. package/apps/pwa/out/_next/static/chunks/8759.c999528a0a907bf6.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/87f498e5-0881de09f2e44707.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/8843.c3197f904c952b81.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/{cb0f8df8.09a4a6be97e3fc5f.js → 892fd990.456232f326cc39c2.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/{837-81336ebaca8198a8.js → 8965-135a2c9eaa2464cb.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/8971-8d3b9c96f8a6c958.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9059.c8598d96f8e5e9cd.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9115.90447ae198b84eae.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9147.d9b0a02e0babc9b4.js +93 -0
  121. package/apps/pwa/out/_next/static/chunks/9204-83075f4b70c61cdc.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9244-c32ef14b5dc72bdf.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/930.2a9a30f964350c66.js +90 -0
  124. package/apps/pwa/out/_next/static/chunks/{8637.90f92d5c8db4732c.js → 9398.1f56e3f17795f43f.js} +1 -1
  125. package/apps/pwa/out/_next/static/chunks/9527.3e5ad31c891c5fcb.js +79 -0
  126. package/apps/pwa/out/_next/static/chunks/9571-4ac933e774d1225c.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-9fc644de2d8edae3.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/approvals/page-0c9dace2db62bee8.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f5e65c3dc9d89f2a.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/board/page-2a9c19b9a140b7b1.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/botlab/page-61533ad92141adfb.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/bots/page-112004ced6fd0329.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/ceo/page-f733318c237a49d8.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/chat/page-51ea677d2d33bbc0.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/consult/page-428b41f2f9566e3c.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/control/page-1b29396ea90a61e5.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/decisions/page-cf83ec613ac904bf.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/design-check/page-3f4c0ecbc284bb5f.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/editor/page-b63c322f54f70cc6.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/exec/page-fe328c4ee9aac6b4.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/field/page-bfb855c584f0fb98.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/healthz/route-9fc644de2d8edae3.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/inside/page-47b1710c110eabd7.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/intake/page-42895214458bc6d2.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/layout-d03de0ebd8366e23.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live/page-3ed6c47e4b1c61c2.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/live-run/page-ae653e9bf261c738.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/loops/page-23f3f0a0209c0ac4.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/market/page-88c691273aae66f9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e187ac52a95538b3.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/morning/page-6e45828daed1295e.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/not-found-edb5d7287bb46186.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4a7eaacd93c3f6a5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3ca073d43d74cc84.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1e9fee67b1199436.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-df1283ea963c6816.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/outputs/page-19fcf8d8301ccd18.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/page-5ab699e0303bbb40.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7cde0d29756984c5.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-9d4c598e8ee22f7a.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/sessions/page-cc1609a71f44ec62.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7659a9a1345bfaa9.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/settings/page-0afef60ecc0c4af5.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a3553949f8f34f5f.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/layout-9fc644de2d8edae3.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/setup/page-057a69702f3c67c7.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/share/page-c0f0e570f3f74c73.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/showroom/page-1825071ae34e3b89.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/tasks/page-6244e3e135d8ccfc.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/term/{page-0a815f34367b6db6.js → page-da787e8a10b8a82e.js} +1 -1
  171. package/apps/pwa/out/_next/static/chunks/app/today/page-1316d32498e2be42.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/trace/page-4df94572e343de2e.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/vault/page-708969f7475e88c1.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c58add08c919a1dc.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a24847f45e5d5bf9.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/workspace/page-acfec65a3a60e22e.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-3dfc598e6fbde245.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/framework-c658a626118411b7.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-4c73685fd891445b.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-6310f7e5de7dd01d.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-51c326bf48610be1.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-d0beadf754da59b0.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-acd243bfbf3f6732.js +1 -0
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +22 -22
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +22 -22
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +22 -22
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +21 -21
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +21 -21
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +22 -22
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +21 -21
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +22 -22
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +22 -22
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +21 -21
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +22 -22
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +22 -22
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +22 -22
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +22 -22
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +21 -21
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +21 -21
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +22 -22
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +22 -22
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +22 -22
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +21 -21
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +21 -21
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +22 -22
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +22 -22
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +21 -21
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +21 -21
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +21 -21
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +22 -22
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +22 -22
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +21 -21
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +22 -22
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +22 -22
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +22 -22
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +22 -22
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +22 -22
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +21 -21
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +22 -22
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +22 -22
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +22 -22
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +22 -22
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +22 -22
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +22 -22
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +22 -22
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +22 -22
  277. package/graphs/overlays/README.md +6 -0
  278. package/graphs/release/release-loop.yaml +33 -17
  279. package/graphs/units/verbs/make-doc.yaml +1 -0
  280. package/package.json +1 -1
  281. package/scripts/bench-metering-plan.ts +229 -0
  282. package/scripts/demo/demo1104-parallel-host.ts +56 -0
  283. package/scripts/demo/demo1104-parallel.ts +72 -0
  284. package/scripts/public-leak-baseline.txt +0 -1
  285. package/scripts/release-loop/docs-node.ts +4 -2
  286. package/scripts/release-loop/gate-image-pin.ts +116 -2
  287. package/scripts/release-loop/gate-node.ts +217 -25
  288. package/scripts/release-loop/gate-progress-sim.ts +73 -0
  289. package/scripts/release-loop/notes-repair.ts +18 -1
  290. package/scripts/release-loop/preflight.ts +188 -8
  291. package/scripts/release-loop/render-warm-control-manifest.ts +22 -0
  292. package/scripts/release-loop/resume-release.ts +9 -7
  293. package/scripts/release-loop/resume.ts +13 -4
  294. package/scripts/release-loop/warm-control-forward.ts +178 -0
  295. package/scripts/retro-cycle.ts +30 -8
  296. package/scripts/rhythm/daily.ts +4 -1
  297. package/scripts/rhythm/linear-progress-sync.ts +28 -15
  298. package/scripts/ux-sim.ts +7 -1
  299. package/scripts/warm-pool-provider-config.ts +6 -1
  300. package/src/agent-mission/grok-auth-status.ts +78 -18
  301. package/src/agent-mission/mission-out.ts +2 -2
  302. package/src/agent-substrate/pr-reviewer.ts +147 -11
  303. package/src/agent-substrate/self-review-cli.ts +10 -2
  304. package/src/auto-research/research-fanout.ts +221 -0
  305. package/src/auto-research/research-turn.ts +282 -36
  306. package/src/autopilot/mission-codebase-gate.ts +7 -3
  307. package/src/cli/control-cli.ts +15 -6
  308. package/src/cli/log-event-names.ts +8 -0
  309. package/src/cli/setup-keys.ts +20 -8
  310. package/src/cli/tui-test-cli.ts +9 -5
  311. package/src/control-plane/ledger.ts +42 -4
  312. package/src/control-plane/member-tokens.ts +25 -2
  313. package/src/control-plane/ports.ts +8 -3
  314. package/src/control-plane/server.ts +26 -5
  315. package/src/core-turn/research-policy.ts +7 -1
  316. package/src/core-turn/run-core-turn.ts +186 -39
  317. package/src/core-turn/types.ts +2 -0
  318. package/src/dashboard/classic-sidetab-mission-job.ts +113 -24
  319. package/src/dashboard/classic-sidetab-mission-pair.ts +3 -2
  320. package/src/dashboard/classic-sidetab-mission-trio.ts +35 -12
  321. package/src/dashboard/classic-sidetab-mission.ts +2 -0
  322. package/src/dashboard/index.ts +9 -1
  323. package/src/dashboard/sidetab-runtime.ts +2 -0
  324. package/src/dashboard/visible-mission-diagnostics.ts +16 -0
  325. package/src/domains/retro-cycle-observation.ts +43 -0
  326. package/src/domains/retro-cycle.ts +13 -2
  327. package/src/domains/retro-proposals.ts +92 -0
  328. package/src/domains/retro-report.ts +6 -1
  329. package/src/graph-runner/graph-cli.ts +24 -1
  330. package/src/graph-runner/graph-grow-llm.ts +3 -1
  331. package/src/graph-runner/runner.ts +226 -30
  332. package/src/harness/harness-cli-command.ts +4 -2
  333. package/src/harness/harness-pod-dispatch.ts +3 -0
  334. package/src/index.ts +2 -2
  335. package/src/llm/child-first-llm-observation.ts +112 -0
  336. package/src/llm/provider-credentials.ts +60 -4
  337. package/src/llm/provider-failure-evidence.ts +44 -0
  338. package/src/llm.ts +170 -11
  339. package/src/mcp/client.ts +7 -0
  340. package/src/mcp/proxy-runtime.ts +6 -1
  341. package/src/mcp/server.ts +54 -0
  342. package/src/native-tool-catalog.ts +2 -2
  343. package/src/nexus/api/consult-requests.ts +18 -6
  344. package/src/nexus/api/discord-trigger-bot.ts +6 -6
  345. package/src/nexus/api/graphs-api.ts +4 -2
  346. package/src/nexus/api/grokbot-trigger-bot.ts +206 -0
  347. package/src/nexus/api/telegram-trigger-bot.ts +6 -6
  348. package/src/nexus/boot/register-mcp-clients.ts +114 -8
  349. package/src/nexus/index.ts +15 -0
  350. package/src/nexus/outbound/grokbot-adapter.ts +71 -0
  351. package/src/nexus/outbound/router.ts +33 -13
  352. package/src/oauth/codex-owner-mirror.ts +47 -10
  353. package/src/onboarding/first-run.ts +44 -6
  354. package/src/preview/terminal.ts +22 -7
  355. package/src/prompt-enhance/enhance.ts +10 -0
  356. package/src/pty-shell/pty-write-provenance.ts +21 -3
  357. package/src/pty-shell/registry.ts +9 -2
  358. package/src/release-loop/proposal-auto-place.ts +83 -0
  359. package/src/research/axis-fanout.ts +40 -5
  360. package/src/research/lenses/second-order.ts +312 -0
  361. package/src/self-dev/dev-cli.ts +3 -1
  362. package/src/self-dev/draft-sweep.ts +31 -10
  363. package/src/self-dev/draft-triage-rules.ts +60 -3
  364. package/src/self-dev/run-supervisor.ts +152 -2
  365. package/src/self-implement/goal-author.ts +208 -23
  366. package/src/self-implement/graph-overlay-yaml.ts +151 -0
  367. package/src/self-implement/graph-yaml.ts +68 -2
  368. package/src/self-implement/orchestrator.ts +62 -15
  369. package/src/self-implement/test-scenario-request.ts +37 -6
  370. package/src/session/chat.ts +2 -2
  371. package/src/session-runtime/index.ts +37 -5
  372. package/src/sidetab-core/model.ts +205 -10
  373. package/src/sidetab-core/visible-agent-mission-pair.ts +51 -0
  374. package/src/sidetab-core/visible-agent-mission.ts +105 -47
  375. package/src/skills/tools/omni-search.ts +4 -2
  376. package/src/task-agent/parent-host.ts +19 -1
  377. package/src/task-agent/recall-inject.ts +98 -0
  378. package/src/task-agent/task-hand.ts +26 -7
  379. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +19 -1
  380. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +15 -0
  381. package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +21 -9
  382. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +1 -1
  383. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +460 -96
  384. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +45 -3
  385. package/src/task-orchestrator/surfaces/self-implement-pod.ts +38 -8
  386. package/src/tool-hints/gate.ts +45 -14
  387. package/src/tool-hints/signals.ts +1 -1
  388. package/src/tool-hints/types.ts +2 -0
  389. package/src/tool-runtime/browser-hand-runtime.ts +11 -1
  390. package/src/tool-runtime/browser-runtime.ts +31 -18
  391. package/src/tool-runtime/elanous-linear-secretary-runtime.ts +152 -36
  392. package/src/tool-runtime/types.ts +2 -0
  393. package/src/tui/entry.ts +27 -7
  394. package/src/tui/sidetab/attach.ts +112 -0
  395. package/src/tui/sidetab/list.ts +3 -2
  396. package/src/tui-client/next-dashboard-runtime.ts +52 -0
  397. package/src/ux-sim/draft-inventory-builder.ts +2 -1
  398. package/src/ux-sim/linear-secretary.ts +4 -12
  399. package/src/ux-sim/mock-scenario-catalog.json +32 -29
  400. package/src/ux-sim/mock-scenario-catalog.ts +55 -3
  401. package/src/ux-sim/release-sim-rig.ts +62 -18
  402. package/src/ux-sim/service-key-setup.ts +63 -0
  403. package/src/version/packed-revision.json +1 -1
  404. package/apps/pwa/out/_next/static/chunks/114.2b90424d1a3ce84c.js +0 -1
  405. package/apps/pwa/out/_next/static/chunks/1151-5c2bb8f002ca2dd6.js +0 -1
  406. package/apps/pwa/out/_next/static/chunks/1261-a616d5728e02e624.js +0 -1
  407. package/apps/pwa/out/_next/static/chunks/1357.3bbef3976c37f94a.js +0 -1
  408. package/apps/pwa/out/_next/static/chunks/1362-20adddfaf662b0fa.js +0 -1
  409. package/apps/pwa/out/_next/static/chunks/1737-9491fc6ab29b9ec6.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/1803-4f91a8e7dcaf042c.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/1961.a81b12872c3e93a9.js +0 -23
  412. package/apps/pwa/out/_next/static/chunks/1970cf59.2d4d8ce5d6c81853.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/1971-8890f3962ba7ee0a.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/2073-ba274e00e4f9efb4.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/2268-53861f339e0b65a2.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/273.f156eee28f11af77.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/2873.d2140e5a82546401.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/3091.a5a4143dea33d197.js +0 -90
  419. package/apps/pwa/out/_next/static/chunks/3095.dc44586a5f83b86d.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/3113.d7472bbc27d580c7.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/3228-9650b6447b6fa288.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/360.ae9f9f4c33c395e8.js +0 -79
  423. package/apps/pwa/out/_next/static/chunks/3638.80412ea63aae8f3f.js +0 -286
  424. package/apps/pwa/out/_next/static/chunks/3744.54f87db52acfd1ab.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/4311-6dd3144002cc0c2d.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/4426-2b230fad00c88930.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/4445.7990c76b3751a918.js +0 -115
  428. package/apps/pwa/out/_next/static/chunks/4447-14c5161fa59166c7.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/4856-b0b8075bba8c53f4.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/4870-de370d65e73c0bb5.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/4885-2c989165c4b29fad.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/4894.fec2ca9e99caefa2.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/5093.8f04b7f8cb899a95.js +0 -94
  434. package/apps/pwa/out/_next/static/chunks/5122-2bb177cebf89e19a.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/5154.5f56dc3902e3d275.js +0 -36
  436. package/apps/pwa/out/_next/static/chunks/529.64ff7257180b2ac9.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/5394.5dabeb0c62293a28.js +0 -93
  438. package/apps/pwa/out/_next/static/chunks/5461.08bf2404ff38ec85.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/5474-22e08a85583209b2.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/5567-265df39ed8fe9dcb.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/5570-a94ae19811cb2046.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/5574-b47c9ef2dceff1fd.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/5730.43d7ccc09988e646.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/5824.da9527b15a3221e5.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/5963-c4d8469c52a0828b.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/6001.ec4049ceb3c43418.js +0 -27
  447. package/apps/pwa/out/_next/static/chunks/6120-471f03db1e299c0c.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/6175-41479e643aca1878.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/6291.a2de4f9787637075.js +0 -104
  450. package/apps/pwa/out/_next/static/chunks/6400-6699907c3537436a.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/6428.1f4327d6ae5fdf9a.js +0 -287
  452. package/apps/pwa/out/_next/static/chunks/644.02cb026fa44bcd0a.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/6466-6c87d60d3dd174fb.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/6773.d4264665ab31bec0.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/6822.3c789f1a8443e774.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/7261-8ff69d3b2ee671fd.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/7266.084c6bbc70035140.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/7271.5c0100e7320aa856.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/7429-bf5731358c8f2f68.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/7464.01f7a0ae1215fbe7.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/7479.0fcad5f37eb64f0c.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/7483.427a2fe2049e388d.js +0 -148
  463. package/apps/pwa/out/_next/static/chunks/7502-be74718178e13e56.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/7530.0889722b19572065.js +0 -24
  465. package/apps/pwa/out/_next/static/chunks/7645.5b2ee3ad45c55ea3.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/7788.7d1c79f1a8e18946.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/7966.f71c8958d1637548.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/8073.f6cf2f2c71aafbd0.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/8201.190b94088980f430.js +0 -74
  470. package/apps/pwa/out/_next/static/chunks/8447.7963352ef5f71b76.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/8574-868e8129525ef6d9.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/868.6e953be05bcf6dca.js +0 -41
  473. package/apps/pwa/out/_next/static/chunks/8706-e1fe0309343a2273.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/8749-06384cdfc4520add.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/8861-cc1fa1c7b920fa8e.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/8967-7dfebf710c9dec9d.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/8997-3c564dbb3b58b055.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/923.2034d10ae91e9c20.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/9312.bc1b89c5d4368033.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/943.39ae5e82f4f1893f.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/9469.0500480de55fb205.js +0 -32
  482. package/apps/pwa/out/_next/static/chunks/9810-2543267050e58b82.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/9870.8419702475254745.js +0 -8
  484. package/apps/pwa/out/_next/static/chunks/9941-376d1b74d51b01a4.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/9958.385434ab04241758.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-deea1450b17cb8dc.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/approvals/page-70985b904f8650ef.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a2e15e33e56a4353.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/board/page-fc72ce2db12d6ca9.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/botlab/page-be03e41f9436ebd9.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/bots/page-35a35999382da3f2.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/ceo/page-3ff5b1e6ca88b848.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/chat/page-a65101300a0ee5c1.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/consult/page-3aca5b6d67dba511.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/control/page-45b8ab0a71e61314.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/decisions/page-53046e282d4cda8e.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/design-check/page-17809e60e8354c17.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/editor/page-fd2442d347bf5898.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/exec/page-e9d720d6fc082e21.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/field/page-6b8b4267bab0a677.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/healthz/route-deea1450b17cb8dc.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/inside/page-8bcf6233ac7c17af.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/intake/page-7cc25591cfc2126f.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/layout-053e871f35c9beba.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/live/page-eff819f0c2d5e084.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/live-run/page-4d7e9d8db5274a87.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/loops/page-2b2d8f5aa5f69ad4.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/market/page-0b8f92c5062e68b1.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1bd9d0fdac2ced3d.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/app/morning/page-9ee28f7df07428ea.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/app/not-found-44b7a1ff468c6648.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/app/observatory/page-cbc6523711dc1b65.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-fdad47899c2695be.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-79a8b5fee4c20871.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-03e8ac9606aeaffb.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/app/outputs/page-4cd55c55ec287e2f.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/app/page-db17b4305404161e.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/app/reflection/page-30041d848350fd02.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c540da022dbf86b0.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9399efc538da057f.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ca2d09128c585ca4.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/app/settings/page-0e2635aecd343038.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ef7f4399b6b7726e.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/app/setup/layout-deea1450b17cb8dc.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/app/setup/page-caf530535b875468.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/app/share/page-eaa20c1543a5fd8e.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c541eb1b75120f61.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/app/tasks/page-ab5e213eda9479f4.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/app/today/page-e858ba0ed9393b68.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/app/trace/page-1638ee9dd6e0582f.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/app/vault/page-4cf5a399109391f2.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b4396321238798ec.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/workflows/page-cb72bfd21006781a.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6135d0242afbc434.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a1dccf907eddb856.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/c7945435-b44d4dbe90ba0dcb.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/ea514caa.05a24f78d9dffe5f.js +0 -122
  538. package/apps/pwa/out/_next/static/chunks/framework-e8a911e19b1be41f.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/main-7022f418fabba021.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/main-app-e5f15df755756a1e.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/pages/_app-4525f91e36940c9b.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/pages/_error-140f0e519c085b0f.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/webpack-8f22ef63dcb865b1.js +0 -1
  544. /package/apps/pwa/out/_next/static/{Om5xS9jpjSXJCeTNGYEi4 → HMXsxdy5MIIcHPMHv1wD4}/_ssgManifest.js +0 -0
@@ -1,7 +1,7 @@
1
1
  /** Fenced warm-Pod lease bridge. The Job remains authoritative. */
2
2
  import { spawn, spawnSync } from 'node:child_process';
3
- import { isAbsolute, join } from 'node:path';
4
- import { constants, closeSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
3
+ import { dirname, isAbsolute, join } from 'node:path';
4
+ import { constants, closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
5
5
  import { tmpdir, userInfo } from 'node:os';
6
6
  import { dlopen, read, type Pointer } from 'bun:ffi';
7
7
  import { createHash, randomUUID } from 'node:crypto';
@@ -19,6 +19,8 @@ const POD_READY_WAIT_SECONDS = 900;
19
19
  const POD_READY_RECHECK_SECONDS = 300;
20
20
  const POD_READY_RETRY_DELAY_MS = 1_000;
21
21
  const WARM_CONTROL_REQUEST_TIMEOUT_MS = 2_000;
22
+ const WARM_POD_LIST_PAGE_SIZE = 25;
23
+ const WARM_POD_LIST_MAX_PAGES = 20;
22
24
 
23
25
  function throwIfPrepareAborted(signal?: AbortSignal): void {
24
26
  if (!signal?.aborted) return;
@@ -45,12 +47,17 @@ function waitForPrepareRetry(signal?: AbortSignal): Promise<void> {
45
47
  export type WarmReplayInput = Omit<WarmWorkerCommand, 'leaseId' | 'generation' | 'workerId' | 'key'>;
46
48
  export interface WarmReplayResult { command: WarmWorkerCommand | null; transport: WarmPodTransportResult }
47
49
  export interface WarmWorkerCandidate { host: string; tier: WarmPoolTier; spec: WarmPodSpec }
50
+ export interface WarmPodDeleteOptions {
51
+ apiVersion: 'v1'; kind: 'DeleteOptions'; preconditions: { uid: string };
52
+ }
48
53
  /** The OS `flock(fd, operation)` binding the prepare lease waits on. */
49
54
  export type WarmPrepareFlock = (fd: number, operation: number) => number;
50
55
  export interface WarmPodProviderOptions {
51
56
  candidates: readonly WarmWorkerCandidate[];
52
- /** One pinned template per host/tier. Each expands to the measured profile quota. */
57
+ /** One pinned template per host/tier. It creates one candidate unless expansion is opted in. */
53
58
  poolTemplates?: readonly WarmWorkerCandidate[];
59
+ /** Expand each pool template to its measured profile quota. Defaults to false. */
60
+ expandPoolTemplatesToQuota?: boolean;
54
61
  /** This address is resolved by the control-plane join/primary mechanism, never guessed from a Pod context. */
55
62
  controlUrl: string;
56
63
  memberToken: string;
@@ -67,6 +74,14 @@ export interface WarmPodProviderOptions {
67
74
  };
68
75
  /** Private directory for cross-process prepare leases; defaults to a per-user directory under tmpdir(). */
69
76
  prepareLockDirectory?: string;
77
+ /** Private durable slot-generation file. Set by loadWarmPodProvider for production reconciliation. */
78
+ rotationStatePath?: string;
79
+ /** Test-only loopback API endpoint for exercising the production DeleteOptions request. */
80
+ kubectlProxyUrlForTest?: string;
81
+ /** Test-only quota resolver injection; omitted keeps the production measured-host table. */
82
+ quotaResolverForTest?: typeof resolveWarmPoolQuota;
83
+ /** Separate fetch for the Kubernetes API; control-plane calls continue using fetch. */
84
+ kubernetesFetch?: typeof fetch;
70
85
  /** Cancels a prepare waiting for another process to finish the same host/worker slot. */
71
86
  prepareAbortSignal?: AbortSignal;
72
87
  /** Injects the prepare-lease flock binding. Omitted keeps this platform's binding; null/false declares it unsupported. */
@@ -74,7 +89,7 @@ export interface WarmPodProviderOptions {
74
89
  /** Gate file this pool serves, used to bind cold prepare legs to their 1-file gate measurement key. */
75
90
  pairingGateFile?: string;
76
91
  }
77
- type WarmPodProviderRuntimeOverrides = Pick<WarmPodProviderOptions, 'fetch' | 'kubectl' | 'prepareLockDirectory'>;
92
+ type WarmPodProviderRuntimeOverrides = Pick<WarmPodProviderOptions, 'controlUrl' | 'fetch' | 'kubectl' | 'prepareLockDirectory'>;
78
93
  export interface WarmPodProviderLoadOptions {
79
94
  pairingGateFile?: string;
80
95
  runtimeOverrides?: Partial<WarmPodProviderRuntimeOverrides>;
@@ -86,6 +101,19 @@ export interface WarmPoolReconcileResult {
86
101
  ready: number;
87
102
  failed: readonly string[];
88
103
  }
104
+ type WarmPoolFailureDecision = { stage: string; reason: string; stderr?: string };
105
+
106
+ const WARM_POOL_STDERR_LIMIT = 512;
107
+ function safeWarmPoolStderr(stderr: string, secrets: readonly string[]): string {
108
+ let safe = stderr;
109
+ for (const secret of secrets.filter(value => value.length >= 8)) safe = safe.split(secret).join('[REDACTED]');
110
+ safe = safe.replace(/(authorization\s*[:=]\s*bearer\s+)[^\s,;]+/gi, '$1[REDACTED]')
111
+ .replace(/(bearer\s+)[A-Za-z0-9._~+/=-]+/gi, '$1[REDACTED]')
112
+ .replace(/((?:token|password|secret|api[_-]?key|credential)\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,
113
+ '$1[REDACTED]');
114
+ return safe.length > WARM_POOL_STDERR_LIMIT
115
+ ? `${safe.slice(0, WARM_POOL_STDERR_LIMIT)}…[truncated]` : safe;
116
+ }
89
117
 
90
118
  /** One leg of a read-only warm/cold prepare-time measurement pair, bound to one 1-file gate key. */
91
119
  export interface WarmPreparePairLeg {
@@ -229,7 +257,7 @@ async function withPrepareLease<T>(candidate: WarmWorkerCandidate, directory: st
229
257
  export class WarmPodProvider {
230
258
  private readonly fetcher: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
231
259
  private readonly now: () => number;
232
- private readonly candidates: readonly WarmWorkerCandidate[];
260
+ private readonly candidates: WarmWorkerCandidate[];
233
261
  private readonly profile: WarmPoolProfile;
234
262
  /** This process's own legs, echoed on CLI stdout. Pairing never reads this — see readWarmReuseTimePairs. */
235
263
  private readonly legs: WarmReuseTimeLegRow[] = [];
@@ -243,20 +271,24 @@ export class WarmPodProvider {
243
271
  private readonly standingWorkers = new Map<string, Promise<boolean>>();
244
272
  /** This instance's prepare-lease flock binding; null means the platform cannot lock and every slot fails. */
245
273
  private readonly prepareFlock: WarmPrepareFlock | null;
274
+ private readonly quotaResolver: typeof resolveWarmPoolQuota;
246
275
  constructor(private readonly options: WarmPodProviderOptions) {
247
276
  const url = new URL(options.controlUrl);
248
277
  if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash
249
278
  || url.pathname !== '/' || !token(options.memberToken)) throw new Error('invalid warm control address');
279
+ this.quotaResolver = options.quotaResolverForTest ?? resolveWarmPoolQuota;
250
280
  const profile = resolveWarmPoolProfile(options.profileConfig ?? getUserConfig());
251
281
  this.profile = profile;
252
282
  const templates = options.poolTemplates ?? [];
253
283
  if (new Set(templates.map(candidate => `${candidate.host}/${candidate.tier}`)).size !== templates.length)
254
284
  throw new Error('duplicate warm pool template');
255
285
  const expanded = templates.flatMap(candidate => {
256
- const quota = resolveWarmPoolQuota(profile, candidate.host, candidate.tier);
286
+ const quota = this.quotaResolver(profile, candidate.host, candidate.tier);
257
287
  if (!quota || !candidate.spec.nodeName) throw new Error('unmeasured warm pool template');
258
- return Array.from({ length: quota }, (_, index): WarmWorkerCandidate => ({
259
- host: candidate.host, tier: candidate.tier, spec: {
288
+ const slots = options.expandPoolTemplatesToQuota === true ? quota : 1;
289
+ return Array.from({ length: slots }, (_, index): WarmWorkerCandidate => ({
290
+ host: candidate.host, tier: candidate.tier,
291
+ spec: {
260
292
  ...candidate.spec,
261
293
  name: `warm-${candidate.host}-${candidate.tier}-${index + 1}`,
262
294
  // The template worker ID is a generation token. Rotate it when a slot is quarantined.
@@ -264,9 +296,16 @@ export class WarmPodProvider {
264
296
  },
265
297
  }));
266
298
  });
267
- this.candidates = [...options.candidates, ...expanded];
299
+ this.candidates = [...options.candidates.map(row => ({ ...row, spec: { ...row.spec } })), ...expanded];
300
+ if (options.rotationStatePath) {
301
+ const rotations = this.readRotations(options.rotationStatePath);
302
+ for (const row of this.candidates) {
303
+ const rotated = rotations[`${row.host}/${row.spec.name}`];
304
+ if (rotated) row.spec = { ...row.spec, workerId: rotated };
305
+ }
306
+ }
268
307
  if (this.candidates.some(candidate => !workerId(candidate.spec.workerId)
269
- || !candidate.host.trim() || resolveWarmPoolQuota(profile, candidate.host, candidate.tier) < 1
308
+ || !candidate.host.trim() || this.quotaResolver(profile, candidate.host, candidate.tier) < 1
270
309
  || candidate.spec.memoryLimit !== (candidate.tier === 'lite' ? '2Gi' : '16Gi')
271
310
  || !warmWorkerPodManifest(candidate.spec))) throw new Error('invalid warm candidate');
272
311
  if (new Set(this.candidates.map(candidate => candidate.spec.workerId)).size !== this.candidates.length)
@@ -279,7 +318,7 @@ export class WarmPodProvider {
279
318
  names.add(identity);
280
319
  const slot = `${candidate.host}/${candidate.tier}`;
281
320
  const count = (counts.get(slot) ?? 0) + 1;
282
- if (count > resolveWarmPoolQuota(profile, candidate.host, candidate.tier))
321
+ if (count > this.quotaResolver(profile, candidate.host, candidate.tier))
283
322
  throw new Error('warm candidate quota exceeded');
284
323
  counts.set(slot, count);
285
324
  }
@@ -301,7 +340,8 @@ export class WarmPodProvider {
301
340
  return { status: response.status, value };
302
341
  }
303
342
 
304
- private async controlBounded(path: string, credential: string, body: Record<string, unknown>, timeoutMs: number): Promise<{ status: number; value: any }> {
343
+ private async controlBounded(path: string, credential: string, body: Record<string, unknown> | undefined,
344
+ timeoutMs: number): Promise<{ status: number; value: any }> {
305
345
  let timer: ReturnType<typeof setTimeout> | undefined;
306
346
  try {
307
347
  return await Promise.race([this.control(path, credential, body), new Promise<never>((_, reject) => {
@@ -370,7 +410,8 @@ export class WarmPodProvider {
370
410
  warmReuseTimeLegs(): readonly WarmReuseTimeLegRow[] { return [...this.legs]; }
371
411
 
372
412
  /** Prepare one named slot. A foreign or replaced Pod is never deleted. */
373
- async ensureStandingWorker(candidate: WarmWorkerCandidate): Promise<boolean> {
413
+ async ensureStandingWorker(candidate: WarmWorkerCandidate,
414
+ onFailure?: (decision: WarmPoolFailureDecision) => void): Promise<boolean> {
374
415
  const configured = this.candidates.find(row => row.host === candidate.host
375
416
  && row.spec.workerId === candidate.spec.workerId);
376
417
  if (!configured) return false;
@@ -378,7 +419,7 @@ export class WarmPodProvider {
378
419
  const inFlight = this.standingWorkers.get(key);
379
420
  if (inFlight) return inFlight;
380
421
  const preparation = withPrepareLease(configured, this.options.prepareLockDirectory,
381
- () => this.prepareStandingWorker(configured, this.options.prepareAbortSignal), this.options.prepareAbortSignal,
422
+ () => this.prepareStandingWorker(configured, this.options.prepareAbortSignal, onFailure), this.options.prepareAbortSignal,
382
423
  this.prepareFlock)
383
424
  .then(result => result === true);
384
425
  this.standingWorkers.set(key, preparation);
@@ -386,11 +427,32 @@ export class WarmPodProvider {
386
427
  finally { if (this.standingWorkers.get(key) === preparation) this.standingWorkers.delete(key); }
387
428
  }
388
429
 
389
- private async prepareStandingWorker(candidate: WarmWorkerCandidate, signal?: AbortSignal): Promise<boolean> {
430
+ private async prepareStandingWorker(candidate: WarmWorkerCandidate, signal?: AbortSignal,
431
+ onFailure?: (decision: WarmPoolFailureDecision) => void): Promise<boolean> {
390
432
  throwIfPrepareAborted(signal);
391
433
  const startedAt = this.now();
392
434
  const outcome: { preexisting: boolean; podUid: string | null } = { preexisting: false, podUid: null };
393
- const ok = await this.coldPrepare(candidate, outcome, signal);
435
+ let decision: WarmPoolFailureDecision | undefined;
436
+ let ok = await this.coldPrepare(candidate, outcome, signal, value => { decision = value; });
437
+ // Reaping is only relevant when normal, bounded reconciliation has found
438
+ // this exact Pod and its ledger proves that its generation is fenced.
439
+ // Empty slots must take the ordinary cold-create path without a namespace
440
+ // dump or a preflight recovery request.
441
+ if (!ok && decision?.reason === 'quarantined-pod-needs-reap') {
442
+ try {
443
+ if (await this.reapQuarantinedWorker(candidate)) {
444
+ outcome.preexisting = false;
445
+ outcome.podUid = null;
446
+ decision = undefined;
447
+ ok = await this.coldPrepare(candidate, outcome, signal, value => { decision = value; });
448
+ }
449
+ } catch (error) {
450
+ debug.log('pod.warm', 'quarantine-reap-failed', { host: candidate.host,
451
+ workerId: candidate.spec.workerId,
452
+ reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
453
+ }
454
+ }
455
+ if (!ok && decision) onFailure?.(decision);
394
456
  const totalDurationMs = this.now() - startedAt;
395
457
  const gateFile = this.options.pairingGateFile;
396
458
  // An unset gate file skips the measurement row entirely instead of recording an unpaired one.
@@ -400,8 +462,256 @@ export class WarmPodProvider {
400
462
  return ok;
401
463
  }
402
464
 
465
+ private readRotations(path: string): Record<string, string> {
466
+ if (!existsSync(path)) return {};
467
+ const info = lstatSync(path);
468
+ if (!info.isFile() || info.isSymbolicLink() || (info.mode & 0o077) !== 0)
469
+ throw new Error('unsafe warm rotation state');
470
+ const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'));
471
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)
472
+ || Object.entries(parsed).some(([slot, id]) => !slot.includes('/') || typeof id !== 'string' || !workerId(id)))
473
+ throw new Error('invalid warm rotation state');
474
+ return parsed as Record<string, string>;
475
+ }
476
+
477
+ private writeRotation(slot: string, id: string): void {
478
+ const path = this.options.rotationStatePath;
479
+ if (!path) throw new Error('warm rotation state is not configured');
480
+ if (!this.prepareFlock) throw new Error('warm rotation state lock is unavailable');
481
+ const lockPath = `${path}.lock`;
482
+ const fd = openSync(lockPath, constants.O_CREAT | constants.O_RDWR | (constants.O_NOFOLLOW ?? 0), 0o600);
483
+ try {
484
+ const info = lstatSync(lockPath);
485
+ const uid = typeof process.getuid === 'function' ? process.getuid() : userInfo().uid;
486
+ if (!info.isFile() || info.isSymbolicLink() || info.uid !== uid || (info.mode & 0o077) !== 0
487
+ || this.prepareFlock(fd, LOCK_EX) !== 0) throw new Error('unsafe warm rotation state lock');
488
+ const rotations = this.readRotations(path);
489
+ rotations[slot] = id;
490
+ const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
491
+ writeFileSync(temporary, JSON.stringify(rotations), { mode: 0o600, flag: 'wx' });
492
+ const temporaryFd = openSync(temporary, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
493
+ try { fsyncSync(temporaryFd); } finally { closeSync(temporaryFd); }
494
+ try {
495
+ renameSync(temporary, path);
496
+ const directoryFd = openSync(dirname(path), constants.O_RDONLY | (constants.O_DIRECTORY ?? 0));
497
+ try { fsyncSync(directoryFd); } finally { closeSync(directoryFd); }
498
+ }
499
+ catch (error) { rmSync(temporary, { force: true }); throw error; }
500
+ } finally {
501
+ this.prepareFlock(fd, LOCK_UN);
502
+ closeSync(fd);
503
+ }
504
+ }
505
+
506
+ private async deletePodWithUid(spec: WarmPodSpec, uid: string): Promise<boolean> {
507
+ const deleteOptions: WarmPodDeleteOptions = {
508
+ apiVersion: 'v1', kind: 'DeleteOptions', preconditions: { uid },
509
+ };
510
+ const proxyUrl = this.options.kubectlProxyUrlForTest;
511
+ const path = `/api/v1/namespaces/${encodeURIComponent(spec.namespace)}/pods/${encodeURIComponent(spec.name)}`;
512
+ const acceptPath = `^${path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`;
513
+ // Keep the random-port, loopback proxy alive only for this request and expose only this Pod DELETE path.
514
+ const child = proxyUrl ? null : spawn('kubectl', ['--context', spec.kubectlContext, '-n', spec.namespace,
515
+ 'proxy', '--address=127.0.0.1', '--port=0', `--accept-paths=${acceptPath}`],
516
+ { detached: true, stdio: ['ignore', 'pipe', 'pipe'] });
517
+ let output = '';
518
+ let timer: ReturnType<typeof setTimeout> | undefined;
519
+ let exitPromise: Promise<void> | null = null;
520
+ try {
521
+ let baseUrl = proxyUrl;
522
+ if (!child) {
523
+ if (!baseUrl || !/^http:\/\/127\.0\.0\.1:\d+$/.test(baseUrl)) throw new Error('invalid test Kubernetes API URL');
524
+ } else {
525
+ const port = await new Promise<number>((resolve, reject) => {
526
+ timer = setTimeout(() => reject(new Error('kubectl proxy start timed out')), 10_000);
527
+ const inspect = (chunk: Buffer | string) => {
528
+ output += String(chunk);
529
+ const match = output.match(/127\.0\.0\.1:(\d+)/);
530
+ if (match) { if (timer) clearTimeout(timer); resolve(Number(match[1])); }
531
+ };
532
+ child.stdout.on('data', inspect);
533
+ child.stderr.on('data', inspect);
534
+ child.once('error', reject);
535
+ child.once('exit', (code, signal) => reject(new Error(`kubectl proxy exited (${code ?? signal})`)));
536
+ });
537
+ exitPromise = new Promise(resolve => child.once('exit', () => resolve()));
538
+ baseUrl = `http://127.0.0.1:${port}`;
539
+ }
540
+ const response = await (this.options.kubernetesFetch ?? this.fetcher)(`${baseUrl}${path}`, {
541
+ method: 'DELETE', headers: { 'content-type': 'application/json' },
542
+ body: JSON.stringify(deleteOptions), signal: AbortSignal.timeout(15_000),
543
+ });
544
+ return response.ok;
545
+ } finally {
546
+ if (timer) clearTimeout(timer);
547
+ child?.kill('SIGTERM');
548
+ if (exitPromise) await Promise.race([exitPromise, new Promise(resolve => setTimeout(resolve, 1_000))]);
549
+ }
550
+ }
551
+
552
+ /** Delete only a UID-pinned Pod whose ledger row proves quarantine and has no competing lease. */
553
+ private async reapQuarantinedWorker(candidate: WarmWorkerCandidate): Promise<boolean> {
554
+ const { host, tier, spec } = candidate;
555
+ const credential = this.options.machineTokens?.[host];
556
+ const observeRefusal = (reason: string) => debug.log('pod.warm', 'quarantine-reap-refused', {
557
+ host, workerId: spec.workerId, pod: spec.name, reason,
558
+ }, { level: 'warn' });
559
+ if (!token(credential) || !this.options.rotationStatePath) {
560
+ observeRefusal('rotation-state-or-credential-unavailable');
561
+ return false;
562
+ }
563
+ const kubectl = (args: readonly string[]) => this.options.kubectl
564
+ ? this.options.kubectl(['--context', spec.kubectlContext, '-n', spec.namespace, ...args])
565
+ : spawnSync('kubectl', ['--context', spec.kubectlContext, '-n', spec.namespace, ...args],
566
+ { encoding: 'utf8', timeout: 30_000, maxBuffer: 2 * 1024 * 1024 });
567
+ const parse = (result: { status: number | null; stdout: string }) => {
568
+ if (result.status !== 0) return null;
569
+ try { return JSON.parse(result.stdout) as any; } catch { return null; }
570
+ };
571
+ const readPods = (query: URLSearchParams) => {
572
+ if (this.options.kubectl) {
573
+ const selector = query.get('fieldSelector')
574
+ ? [`--field-selector=${query.get('fieldSelector')}`]
575
+ : ['-l', query.get('labelSelector')!];
576
+ const continuation = query.get('continue');
577
+ return kubectl(['get', 'pods', ...selector, `--limit=${WARM_POD_LIST_PAGE_SIZE}`, '-o', 'json',
578
+ ...(continuation ? [`--continue=${continuation}`] : [])]);
579
+ }
580
+ return kubectl(['get', '--raw', `/api/v1/namespaces/${encodeURIComponent(spec.namespace)}/pods?${query}`]);
581
+ };
582
+ const exactResult = readPods(new URLSearchParams({ fieldSelector: `metadata.name=${spec.name}`,
583
+ limit: String(WARM_POD_LIST_PAGE_SIZE) }));
584
+ const exact = parse(exactResult);
585
+ if (!Array.isArray(exact?.items) || exact.items.length > 1) {
586
+ observeRefusal('exact-pod-lookup-unavailable'); return false;
587
+ }
588
+ const exactPod = exact.items.find((pod: any) => pod?.metadata?.name === spec.name);
589
+ const listedPods: any[] = [];
590
+ const seenContinuations = new Set<string>();
591
+ let continuation: string | undefined;
592
+ for (let page = 0; page < WARM_POD_LIST_MAX_PAGES; page++) {
593
+ const query = new URLSearchParams({ labelSelector: 'elanous.kind=warm-worker',
594
+ limit: String(WARM_POD_LIST_PAGE_SIZE), ...(continuation ? { continue: continuation } : {}) });
595
+ const result = parse(readPods(query));
596
+ if (!Array.isArray(result?.items) || !result.metadata || typeof result.metadata !== 'object'
597
+ || Array.isArray(result.metadata) || result.items.length > WARM_POD_LIST_PAGE_SIZE) {
598
+ observeRefusal('pod-list-page-unavailable'); return false;
599
+ }
600
+ listedPods.push(...result.items);
601
+ const next = result.metadata.continue;
602
+ if (next === undefined || next === '') { continuation = undefined; break; }
603
+ if (typeof next !== 'string' || seenContinuations.has(next)) {
604
+ observeRefusal('pod-list-page-token-invalid'); return false;
605
+ }
606
+ seenContinuations.add(next);
607
+ continuation = next;
608
+ }
609
+ if (continuation) { observeRefusal('pod-list-page-cap-reached'); return false; }
610
+ const matches = [exactPod, ...listedPods].filter((pod: any) => pod?.metadata?.name === spec.name
611
+ || pod?.metadata?.annotations?.['elanous.dev/warm-worker-id'] === spec.workerId);
612
+ const uniqueMatches = [...new Map(matches.map((pod: any) => [`${pod?.metadata?.name}/${pod?.metadata?.uid ?? ''}`, pod])).values()];
613
+ if (uniqueMatches.length === 0) {
614
+ // Recover a crash after successful deletion but before the durable ID rotation.
615
+ const query = new URLSearchParams({ kind: 'pod-worker-lease' });
616
+ try {
617
+ const response = await this.controlBounded(`/v1/resources?${query}`, credential, undefined,
618
+ WARM_CONTROL_REQUEST_TIMEOUT_MS);
619
+ const rows = response.status === 200 && Array.isArray(response.value?.resources) ? response.value.resources : null;
620
+ const own = rows?.filter((row: any) => row?.name === spec.workerId) ?? [];
621
+ // Empty slots are the normal cold-create path, not failed quarantine recovery.
622
+ if (rows && own.length === 0) return false;
623
+ const prior = own.length === 1 ? own[0] : null;
624
+ const uid = prior?.attrs?.podUid;
625
+ if (rows && prior?.kind === 'pod-worker-lease' && prior.machine === host && typeof uid === 'string'
626
+ && prior.attrs?.state === 'reaping'
627
+ && !rows.some((row: any) => row !== prior && row?.attrs?.podUid === uid)) {
628
+ const nextId = `reap-${randomUUID().replaceAll('-', '')}`;
629
+ this.writeRotation(`${host}/${spec.name}`, nextId);
630
+ candidate.spec = { ...spec, workerId: nextId };
631
+ debug.log('pod.warm', 'quarantine-reap-recovered', { host, tier, workerId: spec.workerId,
632
+ nextWorkerId: nextId, pod: spec.name, podUid: uid });
633
+ return true;
634
+ }
635
+ } catch { /* Uncertain state remains unavailable. */ }
636
+ observeRefusal('pod-absence-or-quarantine-unproven');
637
+ return false;
638
+ }
639
+ if (uniqueMatches.length !== 1) { observeRefusal('ambiguous-pod-identity'); return false; }
640
+ const pod = uniqueMatches[0];
641
+ const manifest = warmWorkerPodManifest(spec);
642
+ const expectedMetadata = manifest.metadata as { labels: Record<string, string>; annotations: Record<string, string> };
643
+ const uid = pod?.metadata?.uid;
644
+ if (pod?.metadata?.name !== spec.name || pod?.metadata?.namespace !== spec.namespace
645
+ || typeof uid !== 'string' || !uid || !pod.metadata.resourceVersion || pod.metadata.deletionTimestamp
646
+ || pod.metadata.ownerReferences?.length || pod.spec?.nodeName !== spec.nodeName
647
+ || pod.metadata.labels?.['elanous.kind'] !== 'warm-worker'
648
+ || pod.metadata.annotations?.['elanous.dev/warm-worker-id'] !== spec.workerId
649
+ || Object.entries(expectedMetadata.labels).some(([key, value]) => pod.metadata.labels?.[key] !== value)
650
+ || Object.entries(expectedMetadata.annotations).some(([key, value]) => pod.metadata.annotations?.[key] !== value)
651
+ || pod.spec?.containers?.[0]?.image !== spec.image
652
+ || pod.spec?.containers?.[0]?.resources?.limits?.memory !== spec.memoryLimit) {
653
+ observeRefusal('pod-identity-unproven');
654
+ return false;
655
+ }
656
+ const query = new URLSearchParams({ kind: 'pod-worker-lease' });
657
+ let response: { status: number; value: any };
658
+ try { response = await this.controlBounded(`/v1/resources?${query}`, credential, undefined,
659
+ WARM_CONTROL_REQUEST_TIMEOUT_MS); }
660
+ catch { observeRefusal('lease-ledger-unavailable'); return false; }
661
+ const rows = response.status === 200 && Array.isArray(response.value?.resources) ? response.value.resources : null;
662
+ const own = rows?.filter((row: any) => row?.name === spec.workerId) ?? [];
663
+ const ledger = own.length === 1 ? own[0] : null;
664
+ if (!rows || !ledger || ledger.kind !== 'pod-worker-lease' || ledger.machine !== host
665
+ || ledger.attrs?.podUid !== uid
666
+ || !['quarantined', 'timeout-fenced', 'reaping'].includes(ledger.attrs?.state)
667
+ || rows.some((row: any) => row !== ledger && row?.attrs?.podUid === uid)) {
668
+ observeRefusal('lease-or-identity-unproven');
669
+ return false;
670
+ }
671
+ let reservation: { status: number; value: any };
672
+ try {
673
+ reservation = await this.controlBounded(`/v1/leases/pod-worker/${encodeURIComponent(spec.workerId)}/reap`,
674
+ credential, { podUid: uid }, WARM_CONTROL_REQUEST_TIMEOUT_MS);
675
+ } catch { observeRefusal('reap-reservation-unavailable'); return false; }
676
+ if (reservation.status !== 200 || reservation.value?.attrs?.state !== 'reaping'
677
+ || reservation.value?.attrs?.podUid !== uid) {
678
+ observeRefusal('reap-reservation-refused');
679
+ return false;
680
+ }
681
+ let deleted: boolean;
682
+ try { deleted = await this.deletePodWithUid(spec, uid); }
683
+ catch { deleted = false; }
684
+ if (!deleted) { observeRefusal('uid-precondition-delete-failed'); return false; }
685
+ const after = parse(readPods(new URLSearchParams({ fieldSelector: `metadata.name=${spec.name}`,
686
+ limit: String(WARM_POD_LIST_PAGE_SIZE) })));
687
+ if (!Array.isArray(after?.items) || after.items.some((row: any) => row?.metadata?.name === spec.name)) {
688
+ observeRefusal('pod-removal-unconfirmed');
689
+ return false;
690
+ }
691
+ const nextId = `reap-${randomUUID().replaceAll('-', '')}`;
692
+ const slot = `${host}/${spec.name}`;
693
+ this.writeRotation(slot, nextId);
694
+ candidate.spec = { ...spec, workerId: nextId };
695
+ debug.log('pod.warm', 'quarantine-reaped', { host, tier, workerId: spec.workerId,
696
+ nextWorkerId: nextId, pod: spec.name, podUid: uid });
697
+ return true;
698
+ }
699
+
403
700
  private async coldPrepare(candidate: WarmWorkerCandidate,
404
- outcome: { preexisting: boolean; podUid: string | null }, signal?: AbortSignal): Promise<boolean> {
701
+ outcome: { preexisting: boolean; podUid: string | null }, signal?: AbortSignal,
702
+ onFailure?: (decision: WarmPoolFailureDecision) => void): Promise<boolean> {
703
+ const credentialValues = [this.options.memberToken, ...Object.values(this.options.machineTokens ?? {}),
704
+ ...Object.entries(process.env).filter(([name]) => /TOKEN|SECRET|PASSWORD|CREDENTIAL|API[_-]?KEY|AUTH/i.test(name))
705
+ .map(([, value]) => value ?? '')];
706
+ const fail = (stage: string, reason: string, stderr = ''): false => {
707
+ onFailure?.({ stage, reason, ...(stderr ? { stderr: safeWarmPoolStderr(stderr, credentialValues) } : {}) });
708
+ if (stage === 'pod-exact-lookup' || stage === 'pod-label-page') {
709
+ const listReason = reason === 'invalid-pod-list' || reason === 'invalid-pod-list-page'
710
+ ? 'malformed-output' : reason === 'kubectl-output-limit' ? 'output-too-large' : 'kubectl-failed';
711
+ debug.log('pod.warm', 'warm-pod-list-skipped', { reason: listReason }, { level: 'warn' });
712
+ }
713
+ return false;
714
+ };
405
715
  const configured = this.candidates.find(row => row.host === candidate.host
406
716
  && row.spec.workerId === candidate.spec.workerId && row.spec.cutSha === candidate.spec.cutSha
407
717
  && keyEquals(row.spec.key, candidate.spec.key));
@@ -412,19 +722,26 @@ export class WarmPodProvider {
412
722
  if (!configured || !configured.spec.nodeName
413
723
  || (configured.spec.name !== prefix && !/^[1-9][0-9]*$/.test(suffix)
414
724
  && !/^(lite|standard)-[1-9][0-9]*$/.test(suffix))
415
- || candidate.spec.name !== configured.spec.name || candidate.spec.nodeName !== configured.spec.nodeName) return false;
725
+ || candidate.spec.name !== configured.spec.name || candidate.spec.nodeName !== configured.spec.nodeName)
726
+ return fail('slot-validation', 'candidate-mismatch');
416
727
  throwIfPrepareAborted(signal);
417
728
  const { spec, host } = configured;
418
729
  const nodeName = spec.nodeName;
419
- if (!nodeName) return false;
730
+ if (!nodeName) return fail('slot-validation', 'node-unpinned');
420
731
  const credential = this.options.machineTokens?.[host];
421
- if (!token(credential)) return false;
732
+ if (!token(credential)) return fail('slot-validation', 'machine-credential-unavailable');
422
733
  const manifest = warmWorkerPodManifest(spec);
423
734
  const metadata = manifest.metadata as { labels: Record<string, string>; annotations: Record<string, string> };
424
735
  const template = manifest.spec as Record<string, unknown>;
425
- const kubectl = (args: readonly string[], input?: string) => {
736
+ const kubectl = (args: readonly string[], input?: string): { status: number | null; stdout: string; stderr: string; error?: NodeJS.ErrnoException } => {
426
737
  const argv = ['--context', spec.kubectlContext, '-n', spec.namespace, ...args];
427
- if (this.options.kubectl) return this.options.kubectl(argv, input);
738
+ if (this.options.kubectl) {
739
+ try { return this.options.kubectl(argv, input); }
740
+ catch (error) {
741
+ return { status: null, stdout: '', stderr: '', error: (error instanceof Error ? error : new Error(String(error))
742
+ ) as NodeJS.ErrnoException };
743
+ }
744
+ }
428
745
  const env = { ...process.env };
429
746
  for (const name of ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete env[name];
430
747
  // A new standing worker can spend more than five minutes in its cold init
@@ -440,6 +757,25 @@ export class WarmPodProvider {
440
757
  if (result.status !== 0) return null;
441
758
  try { return JSON.parse(result.stdout) as any; } catch { return null; }
442
759
  };
760
+ const isOutputLimit = (result: { stderr?: string; error?: NodeJS.ErrnoException }) =>
761
+ result.error?.code === 'ENOBUFS'
762
+ || /maxBuffer|ENOBUFS|buffer length/i.test([result.stderr, result.error?.message].filter(Boolean).join('\n'));
763
+ const podListPage = (selector: { name?: string; continuation?: string }) => {
764
+ if (this.options.kubectl) {
765
+ const args = selector.name
766
+ ? ['get', 'pods', `--field-selector=metadata.name=${selector.name}`, '-o', 'json']
767
+ : ['get', 'pods', '-l', 'elanous.kind=warm-worker', `--limit=${WARM_POD_LIST_PAGE_SIZE}`, '-o', 'json',
768
+ ...(selector.continuation ? [`--continue=${selector.continuation}`] : [])];
769
+ return kubectl(args);
770
+ }
771
+ const query = new URLSearchParams({
772
+ ...(selector.name ? { fieldSelector: `metadata.name=${selector.name}` } : { labelSelector: 'elanous.kind=warm-worker' }),
773
+ limit: String(WARM_POD_LIST_PAGE_SIZE),
774
+ ...(selector.continuation ? { continue: selector.continuation } : {}),
775
+ });
776
+ const path = `/api/v1/namespaces/${encodeURIComponent(spec.namespace)}/pods?${query}`;
777
+ return kubectl(['get', '--raw', path]);
778
+ };
443
779
  const sameSpec = (actual: unknown, expected: unknown): boolean => {
444
780
  if (Array.isArray(expected)) return Array.isArray(actual) && actual.length === expected.length
445
781
  && expected.every((item, index) => sameSpec(actual[index], item));
@@ -486,80 +822,69 @@ export class WarmPodProvider {
486
822
  && Object.entries(metadata.annotations).every(([key, value]) => pod.metadata.annotations?.[key] === value)
487
823
  && pod.spec?.nodeName === nodeName
488
824
  && sameSpec(normalizedSpec(pod.spec), template);
489
- const skipDiscovery = (reason: string): false => {
490
- debug.log('pod.warm', 'warm-pod-list-skipped', { reason }, { level: 'warn' });
491
- return false;
492
- };
493
- const kubectlErrorCode = (error: unknown): string | undefined => {
494
- if (!error || typeof error !== 'object' || !('code' in error)) return undefined;
495
- return typeof error.code === 'string' ? error.code : undefined;
496
- };
497
- // Resolve the pinned Pod independently; namespace size must not affect this read.
498
- let namedResult: ReturnType<typeof kubectl>;
499
- try { namedResult = kubectl(['get', 'pods', `--field-selector=metadata.name=${spec.name}`, '-o', 'json']); }
500
- catch (error) {
501
- return skipDiscovery(kubectlErrorCode(error) === 'ENOBUFS' ? 'output-too-large' : 'kubectl-failed');
825
+ // Exact-name lookup finds the pinned candidate. A separate label query is
826
+ // paged so physical quota accounting never needs a namespace-wide dump.
827
+ const exactResult = kubectl(this.options.kubectl
828
+ ? ['get', 'pods', `--field-selector=metadata.name=${spec.name}`, '-o', 'json']
829
+ : ['get', '--raw', `/api/v1/namespaces/${encodeURIComponent(spec.namespace)}/pods?${new URLSearchParams({
830
+ fieldSelector: `metadata.name=${spec.name}`, limit: String(WARM_POD_LIST_PAGE_SIZE),
831
+ })}`]);
832
+ if (exactResult.status !== 0) {
833
+ const detail = [exactResult.stderr, 'error' in exactResult ? exactResult.error : ''].filter(Boolean).join('\n');
834
+ return fail('pod-exact-lookup', isOutputLimit(exactResult)
835
+ ? 'kubectl-output-limit' : 'kubectl-failed', detail);
502
836
  }
503
- if (kubectlErrorCode(namedResult.error) === 'ENOBUFS') return skipDiscovery('output-too-large');
504
- if (namedResult.status !== 0) return skipDiscovery('kubectl-failed');
505
- const namedList = parse(namedResult);
506
- if (!Array.isArray(namedList?.items) || !namedList.metadata || typeof namedList.metadata !== 'object'
507
- || Array.isArray(namedList.metadata)) return skipDiscovery('malformed-output');
508
- const namedMatches = namedList.items.filter((row: any) => row?.metadata?.name === spec.name);
509
- if (namedMatches.length > 1) return false;
510
- const namedPod = namedMatches[0] ?? null;
511
-
512
- // Kubectl's continuation token is consumed explicitly so no partial page can
513
- // authorize a create. A hard page cap turns a non-terminating listing into failure.
514
- const warmPods: any[] = [];
515
- let continuation = '';
837
+ const exact = parse(exactResult);
838
+ if (!Array.isArray(exact?.items)) return fail('pod-exact-lookup', 'invalid-pod-list');
839
+ const exactPod = exact.items.find((row: any) => row?.metadata?.name === spec.name);
840
+ const listed: any[] = [];
516
841
  const seenContinuations = new Set<string>();
517
- let completedPages = false;
518
- for (let page = 0; page < 100; page++) {
519
- const args = ['get', 'pods', '-l', 'elanous.kind=warm-worker', '--chunk-size=50', '-o', 'json'];
520
- if (continuation) args.splice(5, 0, '--continue', continuation);
521
- let result: ReturnType<typeof kubectl>;
522
- try { result = kubectl(args); }
523
- catch (error) {
524
- return skipDiscovery(kubectlErrorCode(error) === 'ENOBUFS' ? 'output-too-large' : 'kubectl-failed');
842
+ let continuation: string | undefined;
843
+ for (let page = 0; page < WARM_POD_LIST_MAX_PAGES; page++) {
844
+ const pageResult = podListPage({ continuation });
845
+ if (pageResult.status !== 0) {
846
+ const detail = [pageResult.stderr, 'error' in pageResult ? pageResult.error : ''].filter(Boolean).join('\n');
847
+ const reason = isOutputLimit(pageResult) ? 'kubectl-output-limit' : 'kubectl-failed';
848
+ return fail('pod-label-page', reason, detail);
525
849
  }
526
- if (kubectlErrorCode(result.error) === 'ENOBUFS') return skipDiscovery('output-too-large');
527
- if (result.status !== 0) return skipDiscovery('kubectl-failed');
528
- const listed = parse(result);
529
- if (!listed || !Array.isArray(listed.items) || !listed.metadata || typeof listed.metadata !== 'object'
530
- || Array.isArray(listed.metadata)
531
- || (listed.metadata.continue !== undefined && typeof listed.metadata.continue !== 'string'))
532
- return skipDiscovery('malformed-output');
533
- warmPods.push(...listed.items);
534
- continuation = listed.metadata?.continue ?? '';
535
- if (!continuation) { completedPages = true; break; }
536
- if (seenContinuations.has(continuation) || page === 99) return skipDiscovery('malformed-output');
537
- seenContinuations.add(continuation);
850
+ const result = parse(pageResult);
851
+ if (!Array.isArray(result?.items) || !result.metadata || typeof result.metadata !== 'object'
852
+ || Array.isArray(result.metadata)) return fail('pod-label-page', 'invalid-pod-list-page');
853
+ if (result.items.length > WARM_POD_LIST_PAGE_SIZE) return fail('pod-label-page', 'pod-list-page-over-cap');
854
+ listed.push(...result.items);
855
+ const next = result.metadata?.continue;
856
+ if (next === undefined || next === '') { continuation = undefined; break; }
857
+ if (typeof next !== 'string' || seenContinuations.has(next)) return fail('pod-label-page', 'pod-list-page-token-invalid');
858
+ seenContinuations.add(next);
859
+ continuation = next;
538
860
  }
539
- if (!completedPages) return skipDiscovery('malformed-output');
540
- const hostPods = warmPods.filter((row: any) => row?.metadata?.name === spec.name
541
- || row?.metadata?.annotations?.['elanous.dev/warm-worker-id'] === spec.workerId);
542
- if (namedPod && !hostPods.some((row: any) => row?.metadata?.name === spec.name)) hostPods.push(namedPod);
543
- if (hostPods.length > 1) return false;
544
- let pod = hostPods[0];
861
+ if (continuation) return fail('pod-label-page', 'pod-list-page-cap-reached');
862
+ // Read the complete label-selected set: a Pod for this host on another node
863
+ // must block creation, and warm Pods on the pinned node count against quota.
864
+ const hostPods = [exactPod, ...listed].filter((row: any) => !!row && (row?.metadata?.name === spec.name
865
+ || row?.metadata?.annotations?.['elanous.dev/warm-worker-id'] === spec.workerId));
866
+ const uniqueHostPods = [...new Map(hostPods.map((row: any) => [`${row?.metadata?.name}/${row?.metadata?.uid ?? ''}`, row])).values()];
867
+ if (uniqueHostPods.length > 1) return fail('pod-list', 'pod-list-over-cap');
868
+ let pod = uniqueHostPods[0];
545
869
  const preexisting = !!pod;
546
870
  outcome.preexisting = preexisting;
547
- if (pod && !matches(pod)) return false;
871
+ if (pod && !matches(pod)) return fail('pod-validation', 'candidate-pod-mismatch');
548
872
  // Old generations still occupy memory even when their ledger rows are quarantined.
549
873
  // Count physical warm Pods on the pinned node before admitting a new one.
550
- const physical = warmPods.filter((row: any) => row?.metadata?.labels?.['elanous.kind'] === 'warm-worker'
874
+ const physical = listed.filter((row: any) => row?.metadata?.labels?.['elanous.kind'] === 'warm-worker'
551
875
  && row?.spec?.nodeName === nodeName && !row?.metadata?.deletionTimestamp
552
876
  && row?.status?.phase !== 'Succeeded' && row?.status?.phase !== 'Failed'
553
877
  && row?.spec?.containers?.[0]?.resources?.limits?.memory === spec.memoryLimit).length;
554
- if (!preexisting && physical >= resolveWarmPoolQuota(this.profile, host, configured.tier)) {
878
+ if (!preexisting && physical >= this.quotaResolver(this.profile, host, configured.tier)) {
555
879
  debug.log('pod.warm', 'pool-slot-refused', { host, tier: configured.tier,
556
880
  workerId: spec.workerId, reason: 'physical-quota', physical }, { level: 'warn' });
557
- return false;
881
+ return fail('pod-list', 'physical-quota');
558
882
  }
559
883
  const readLedger = async (): Promise<any[] | null> => {
560
884
  try {
561
885
  const query = new URLSearchParams({ kind: 'pod-worker-lease', machine: host, name: spec.workerId });
562
- const ledger = await this.control(`/v1/resources?${query}`, credential);
886
+ const ledger = await this.controlBounded(`/v1/resources?${query}`, credential, undefined,
887
+ WARM_CONTROL_REQUEST_TIMEOUT_MS);
563
888
  return ledger.status === 200 && Array.isArray(ledger.value?.resources)
564
889
  && ledger.value.resources.length <= 1 ? ledger.value.resources : null;
565
890
  } catch { return null; }
@@ -568,9 +893,15 @@ export class WarmPodProvider {
568
893
  // A worker ID already bound to another UID must not leave a newly created, unregistered Pod.
569
894
  const before = await readLedger();
570
895
  throwIfPrepareAborted(signal);
571
- if (!before || before.length !== 0) return false;
572
- pod = parse(kubectl(['create', '-f', '-', '-o', 'json'], JSON.stringify(manifest)));
573
- if (!matches(pod)) return false;
896
+ if (!before || before.length !== 0) return fail('lease-preflight', 'lease-unavailable');
897
+ const createResult = kubectl(['create', '-f', '-', '-o', 'json'], JSON.stringify(manifest));
898
+ if (createResult.status !== 0) {
899
+ const detail = [createResult.stderr, 'error' in createResult ? createResult.error : ''].filter(Boolean).join('\n');
900
+ const reason = /maxBuffer|ENOBUFS|buffer length/i.test(detail) ? 'kubectl-output-limit' : 'kubectl-create-denied';
901
+ return fail('pod-create', reason, detail);
902
+ }
903
+ pod = parse(createResult);
904
+ if (!matches(pod)) return fail('pod-create', 'created-pod-mismatch');
574
905
  }
575
906
  const uid = pod.metadata.uid as string;
576
907
  outcome.podUid = uid;
@@ -588,15 +919,19 @@ export class WarmPodProvider {
588
919
  const observed = parse(kubectl(['get', `pod/${spec.name}`, '-o', 'json']));
589
920
  throwIfPrepareAborted(signal);
590
921
  if (ready(observed)) { isReady = true; break; }
591
- if (!matches(observed) || observed.metadata.uid !== uid) return false;
922
+ if (!matches(observed) || observed.metadata.uid !== uid) return fail('pod-ready', 'pod-replaced-or-mismatched');
592
923
  if (this.now() < deadline) await waitForPrepareRetry(signal);
593
924
  }
594
- if (!isReady) return false;
925
+ if (!isReady) return fail('pod-ready', 'ready-deadline-exceeded');
595
926
  // Re-read after create: a registration racing the preflight must fail before our POST.
596
927
  const rows = await readLedger();
597
- if (!rows) return false;
928
+ if (!rows) return fail('lease-registration', 'lease-read-failed');
598
929
  const existing = rows[0];
599
930
  const leaseTtlMs = this.options.leaseTtlMs ?? 30 * 60_000;
931
+ if (existing?.kind === 'pod-worker-lease' && existing.machine === host && existing.name === spec.workerId
932
+ && existing.attrs?.podUid === uid
933
+ && ['quarantined', 'timeout-fenced', 'reaping'].includes(existing.attrs?.state))
934
+ return fail('lease-registration', 'quarantined-pod-needs-reap');
600
935
  if (existing) return preexisting && existing.kind === 'pod-worker-lease'
601
936
  && existing.machine === host && existing.name === spec.workerId
602
937
  && (existing.attrs?.state === 'idle' || existing.attrs?.state === 'leased')
@@ -608,7 +943,7 @@ export class WarmPodProvider {
608
943
  && existing.attrs?.key && keyEquals(existing.attrs.key, spec.key);
609
944
  // An unregistered Pod could have been created by anyone with namespace access.
610
945
  // Only this invocation's successful create response may become a new ledger row.
611
- if (preexisting) return false;
946
+ if (preexisting) return fail('lease-registration', 'preexisting-pod-unregistered');
612
947
  throwIfPrepareAborted(signal);
613
948
  let registered: { status: number; value: any };
614
949
  try {
@@ -616,11 +951,13 @@ export class WarmPodProvider {
616
951
  workerId: spec.workerId, machine: host, podUid: uid, key: spec.key,
617
952
  ttlMs: leaseTtlMs,
618
953
  });
619
- } catch { return false; }
620
- return registered.status === 200 && registered.value?.name === spec.workerId
954
+ } catch { return fail('lease-registration', 'worker-registration-failed'); }
955
+ if (!(registered.status === 200 && registered.value?.name === spec.workerId
621
956
  && registered.value?.attrs?.state === 'idle'
622
957
  && registered.value?.attrs?.podUid === uid
623
- && !!registered.value.attrs.key && keyEquals(registered.value.attrs.key, spec.key);
958
+ && !!registered.value.attrs.key && keyEquals(registered.value.attrs.key, spec.key)))
959
+ return fail('lease-registration', 'worker-registration-rejected');
960
+ return true;
624
961
  }
625
962
 
626
963
  async prepareWorker(workerId: string): Promise<boolean> {
@@ -635,10 +972,25 @@ export class WarmPodProvider {
635
972
  let ready = 0;
636
973
  for (const candidate of this.candidates) {
637
974
  let prepared = false;
638
- try { prepared = await this.ensureStandingWorker(candidate); }
639
- catch { /* Uncertain state is never counted as ready. */ }
975
+ let decision: WarmPoolFailureDecision | undefined;
976
+ try { prepared = await this.ensureStandingWorker(candidate, value => { decision = value; }); }
977
+ catch (error) {
978
+ decision = { stage: 'prepare', reason: 'prepare-threw',
979
+ stderr: safeWarmPoolStderr(error instanceof Error ? error.message : String(error), [
980
+ this.options.memberToken, ...Object.values(this.options.machineTokens ?? {}),
981
+ ]) };
982
+ }
640
983
  if (prepared) ready++;
641
- else failed.push(candidate.spec.workerId);
984
+ else {
985
+ failed.push(candidate.spec.workerId);
986
+ debug.log('pod.warm.provider', 'prepare-refused', { reason: decision?.reason ?? 'prepare-refused',
987
+ host: candidate.host, slot: candidate.spec.workerId }, { level: 'warn' });
988
+ debug.log('pod.warm', 'pool-reconcile-decision', { profile: this.profile,
989
+ host: candidate.host, tier: candidate.tier, workerId: candidate.spec.workerId,
990
+ stage: decision?.stage ?? 'prepare', reason: decision?.reason ?? 'prepare-refused',
991
+ ...(decision?.stderr ? { stderr: decision.stderr } : {}),
992
+ }, { level: 'warn' });
993
+ }
642
994
  debug.log('pod.warm', 'pool-slot', { profile: this.profile,
643
995
  host: candidate.host, tier: candidate.tier, workerId: candidate.spec.workerId,
644
996
  pod: candidate.spec.name, ready: prepared }, { level: prepared ? 'info' : 'warn' });
@@ -661,6 +1013,7 @@ export class WarmPodProvider {
661
1013
  // host/key once so a busy first host cannot strand an idle second pool.
662
1014
  const tried = new Set<string>();
663
1015
  for (const candidate of matches) {
1016
+ if (input.deadlineAt <= this.now()) return lastFallback;
664
1017
  const { spec, host } = candidate;
665
1018
  const poolKey = `${host}/${spec.key.context}/${spec.key.imageDigest}/${spec.key.lockHash}/${spec.key.architecture}`;
666
1019
  if (tried.has(poolKey)) continue;
@@ -668,7 +1021,8 @@ export class WarmPodProvider {
668
1021
  const contextFailure = probeWarmPodContext(spec.kubectlContext, this.options.kubectl);
669
1022
  if (contextFailure) { lastFallback = { command: null, transport: contextFailure }; continue; }
670
1023
  let acquired: { status: number; value: any };
671
- const acquisition = this.control('/v1/leases/pod-worker', this.options.memberToken, {
1024
+ const acquisitionCredential = this.options.machineTokens?.[host] ?? this.options.memberToken;
1025
+ const acquisition = this.control('/v1/leases/pod-worker', acquisitionCredential, {
672
1026
  machine: host, key: spec.key, runId: input.runId, attemptId: input.attemptId,
673
1027
  ttlMs: this.options.leaseTtlMs ?? 30 * 60_000,
674
1028
  });
@@ -730,7 +1084,10 @@ export class WarmPodProvider {
730
1084
  if (acquired.status === 409) {
731
1085
  try {
732
1086
  const query = new URLSearchParams({ kind: 'pod-worker-lease', machine: host, name: spec.workerId });
733
- const observed = await this.control(`/v1/resources?${query}`, this.options.memberToken);
1087
+ const remainingMs = input.deadlineAt - this.now();
1088
+ if (remainingMs <= 0) return lastFallback;
1089
+ const observed = await this.controlBounded(`/v1/resources?${query}`, this.options.memberToken,
1090
+ undefined, Math.min(WARM_CONTROL_REQUEST_TIMEOUT_MS, remainingMs));
734
1091
  const row = observed.status === 200 && Array.isArray(observed.value?.resources)
735
1092
  ? observed.value.resources.find((entry: any) => entry?.name === spec.workerId) : undefined;
736
1093
  if (row?.attrs?.state === 'quarantined' || row?.attrs?.state === 'timeout-fenced') {
@@ -790,9 +1147,16 @@ export class WarmPodProvider {
790
1147
  debug.log('pod.provenance', 'lease-start', { pod: leasedCandidate.spec.name, podUid: command.podUid,
791
1148
  leaseId: command.leaseId, runId: command.runId, attemptId: command.attemptId,
792
1149
  requestedAt: command.requestedAt, metadata: warmLeaseMetadata(command).annotations });
793
- try { await this.quarantine(command, 'lease-response-after-deadline'); }
1150
+ let quarantineTimer: ReturnType<typeof setTimeout> | undefined;
1151
+ try {
1152
+ await Promise.race([this.quarantine(command, 'lease-response-after-deadline'), new Promise<never>((_, reject) => {
1153
+ quarantineTimer = setTimeout(() => reject(new Error('warm late-lease quarantine timed out')),
1154
+ WARM_CONTROL_REQUEST_TIMEOUT_MS);
1155
+ })]);
1156
+ }
794
1157
  catch (error) { debug.log('pod.warm', 'late-lease-quarantine-failed', { workerId: command.workerId,
795
1158
  leaseId: command.leaseId, generation: command.generation, error: String(error) }, { level: 'warn' }); }
1159
+ finally { if (quarantineTimer) clearTimeout(quarantineTimer); }
796
1160
  return fallback('lease-unavailable');
797
1161
  }
798
1162
  // Acquisition is by pool key. A healthy sibling or replacement Pod must remain eligible.
@@ -963,7 +1327,7 @@ export function loadWarmPodProvider(path: string, pairingGateFileOrOptions?: str
963
1327
  if (!pairingGateFile.trim()) throw new Error('warm provider pairing gate file must not be empty');
964
1328
  options.pairingGateFile = pairingGateFile;
965
1329
  }
966
- return new WarmPodProvider({ ...options, ...runtimeOverrides });
1330
+ return new WarmPodProvider({ ...options, ...runtimeOverrides, rotationStatePath: options.rotationStatePath ?? `${path}.rotations` });
967
1331
  }
968
1332
  function currentProvider(): WarmPodProvider | null {
969
1333
  if (configuredProvider) return configuredProvider;