elanous 0.2.42 → 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 (764) 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/{X1ucLw10Cds_h614NoEnh → HMXsxdy5MIIcHPMHv1wD4}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/08334415.f0e80896acbef722.js +1 -0
  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/{2160.a097e7b138ae1c55.js → 118.2a89982fe4276880.js} +1 -1
  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/1306.967240ede9a0f757.js +24 -0
  14. package/apps/pwa/out/_next/static/chunks/1344.3b95f3bd1b77a3fe.js +94 -0
  15. package/apps/pwa/out/_next/static/chunks/{4043.6b91203436c9c86b.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/{6632.0eac1c554f8f2ef3.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/1850-522de4ee34957210.js +1 -0
  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/{3507.97e1977bc4d5fb0a.js → 1978.eeb60a606dc5deac.js} +2 -2
  26. package/apps/pwa/out/_next/static/chunks/2043.e233e3b5a3e790f5.js +1 -0
  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/2216.802962c04f40a26e.js +64 -0
  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/{5885.0a3e52c8faccec98.js → 239.de075467b5523469.js} +2 -2
  33. package/apps/pwa/out/_next/static/chunks/249.ba7051590d603c8f.js +226 -0
  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/2954.bbefc95d6b20e0d8.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/2e0f720a.a0697c08e3806a60.js +1 -0
  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/{1487.2f67b79444062da6.js → 3349.b5f09ee474d9533e.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/3503.a47a9755fb4d97b0.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/3506-5f429b3b7e1392b0.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/3843.bad404d008893291.js +77 -0
  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/4393.28fa5228d0eec217.js +149 -0
  52. package/apps/pwa/out/_next/static/chunks/4404.7281c847c6e10dfc.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/{8673-bab689a55f7e13b9.js → 4415-b9d6a90960920a4a.js} +3 -3
  54. package/apps/pwa/out/_next/static/chunks/447-5b304de1fe098f43.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/4484-8ebea83d431e6fae.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/{8262.ddc03e06b733f184.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/{1262.e804feb8fd1acb8c.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/{1063.2fb8a9dc55c731f2.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/{6377.bc7df239edbff244.js → 4891.3f80ba517a3b2373.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/4998.810ca8833084caa9.js +62 -0
  66. package/apps/pwa/out/_next/static/chunks/4f98febc.ea5841d4d616bfb2.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/{3482.a9ae9d2ad6a5bd97.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/5439.3fec6dbce29ecb36.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/5612-074d9a5e00dfac6a.js +1 -0
  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/5790.5eb06a653ebd8571.js +34 -0
  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/{3347.f846266b5e5f3ede.js → 5989.7ecd362c5c44b747.js} +3 -3
  81. package/apps/pwa/out/_next/static/chunks/{73d2eb0e.4966ad55293c801a.js → 5a34fb2f.52f1b4e1baebb48a.js} +3 -3
  82. package/apps/pwa/out/_next/static/chunks/{7a98bc80.fa935989e829f9c2.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/{7411.9b4b8b747dc7b802.js → 6245.1182e007e9d230d9.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/{b450776b.3db34d8d965f82d3.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/{e8543907.5b040a5f22a0b0e5.js → 6549dad0.2740b25e51bf8f05.js} +1 -1
  92. package/apps/pwa/out/_next/static/chunks/{9427.96aa912ddb5801e0.js → 6881.9e7955847640c7fe.js} +17 -17
  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/7010.1bbd17a7f2b3e376.js +1 -0
  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/7679.32c40cdd14f20b4a.js +1 -0
  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/{8138.6c10b2a356d52fca.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/{9415.69e4a21c60c7d2db.js → 8551.f762945c2cda2e95.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/8635-57a9d8d9db70f4f1.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/86961958-4b0689a5048f756b.js +1 -0
  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/{9a852a62-6192a6ea9346ced3.js → 87f498e5-0881de09f2e44707.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/8843.c3197f904c952b81.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/{3d0154af.5331fec737ffe8c3.js → 892fd990.456232f326cc39c2.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/8965-135a2c9eaa2464cb.js +1 -0
  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/9398.1f56e3f17795f43f.js +1 -0
  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-da787e8a10b8a82e.js +1 -0
  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/_next/static/css/d1f8bb401740a549.css +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +23 -23
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +22 -22
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -23
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +23 -23
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/catalog/external-commands.yaml +5 -3
  279. package/catalog/meta/catalogVersion.yaml +2 -1
  280. package/catalog/signing/catalog-signing-public.pem +3 -0
  281. package/graphs/intake/intake-daily.yaml +16 -0
  282. package/graphs/intake/news-sources.yaml +1 -0
  283. package/graphs/intake/recipes.yaml +7 -0
  284. package/graphs/overlays/README.md +6 -0
  285. package/graphs/overlays/examples/research-runtime-empty-terminal.yaml +28 -0
  286. package/graphs/profiles/local.yaml +5 -0
  287. package/graphs/profiles/pod.yaml +4 -0
  288. package/graphs/release/release-loop.yaml +33 -17
  289. package/graphs/units/verbs/make-doc.yaml +1 -0
  290. package/package.json +6 -2
  291. package/plugins/demo-absorption-briefing/plugin.json +18 -0
  292. package/plugins/demo-absorption-briefing/skills/demo-absorption-briefing/SKILL.md +8 -0
  293. package/plugins/demo-browser-hand/plugin.json +13 -0
  294. package/plugins/demo-browser-hand/skills/demo-browser-hand/SKILL.md +8 -0
  295. package/plugins/demo-linear-secretary/plugin.json +15 -0
  296. package/plugins/demo-linear-secretary/skills/demo-linear-secretary/SKILL.md +8 -0
  297. package/plugins/demo-plugin-wizard/plugin.json +11 -0
  298. package/plugins/demo-plugin-wizard/skills/demo-plugin-wizard/SKILL.md +8 -0
  299. package/plugins/demo-research/plugin.json +16 -0
  300. package/plugins/demo-research/skills/demo-research/SKILL.md +8 -0
  301. package/plugins/demo-video-briefing/plugin.json +15 -0
  302. package/plugins/demo-video-briefing/skills/demo-video-briefing/SKILL.md +8 -0
  303. package/scripts/bench-metering-plan.ts +229 -0
  304. package/scripts/build-signed-catalog.ts +48 -0
  305. package/scripts/ci-typecheck-main-delta.sh +88 -0
  306. package/scripts/codex-fallback-live-probe.ts +1 -1
  307. package/scripts/demo/demo1104-parallel-host.ts +56 -0
  308. package/scripts/demo/demo1104-parallel-scenes.ts +301 -0
  309. package/scripts/demo/demo1104-parallel.ts +72 -0
  310. package/scripts/demo/demo1104-stage.config.ts +21 -0
  311. package/scripts/demo/demo1104-stage.ts +316 -0
  312. package/scripts/goal-method-audit.ts +17 -1
  313. package/scripts/graph-units/land-enqueue.ts +90 -0
  314. package/scripts/hooks/codex-notify.sh +32 -0
  315. package/scripts/install.sh +10 -38
  316. package/scripts/landing-heal/land-plan.ts +57 -0
  317. package/scripts/public-leak-baseline.txt +0 -1
  318. package/scripts/release-loop/auto-approve-node.ts +1 -1
  319. package/scripts/release-loop/docs-node.ts +4 -2
  320. package/scripts/release-loop/gate-autoheal-sim-evidence.ts +60 -0
  321. package/scripts/release-loop/gate-image-pin.ts +116 -2
  322. package/scripts/release-loop/gate-method.ts +139 -0
  323. package/scripts/release-loop/gate-node.ts +395 -43
  324. package/scripts/release-loop/gate-progress-sim.ts +73 -0
  325. package/scripts/release-loop/gate-traffic-mode.ts +151 -3
  326. package/scripts/release-loop/node-verdict.ts +38 -25
  327. package/scripts/release-loop/notes-repair.ts +18 -1
  328. package/scripts/release-loop/preflight.ts +544 -10
  329. package/scripts/release-loop/render-warm-control-manifest.ts +22 -0
  330. package/scripts/release-loop/resume-release.ts +9 -7
  331. package/scripts/release-loop/resume.ts +13 -4
  332. package/scripts/release-loop/unattended-release.ts +20 -9
  333. package/scripts/release-loop/warm-control-forward.ts +178 -0
  334. package/scripts/retro-cycle.ts +30 -8
  335. package/scripts/rhythm/daily.ts +39 -4
  336. package/scripts/rhythm/linear-progress-sync.ts +311 -0
  337. package/scripts/ux-sim.ts +7 -1
  338. package/scripts/video-pipeline.ts +9 -1
  339. package/scripts/warm-pool-provider-config.ts +35 -7
  340. package/src/acp/agent-manager.ts +1 -0
  341. package/src/acp/codex-app-server-agent.ts +33 -0
  342. package/src/acp/codex-app-server-client.ts +153 -16
  343. package/src/acp/core-turn-bridge.ts +17 -12
  344. package/src/acp/event-router.ts +20 -1
  345. package/src/acp/server.ts +6 -0
  346. package/src/agent-mission/browser-roundtrip-coordinator.ts +85 -0
  347. package/src/agent-mission/claude-headless.ts +4 -1
  348. package/src/agent-mission/codex-exec.ts +105 -0
  349. package/src/agent-mission/driver.ts +192 -11
  350. package/src/agent-mission/grok-auth-status.ts +107 -0
  351. package/src/agent-mission/hand-hooks.ts +101 -0
  352. package/src/agent-mission/mission-cli.ts +56 -21
  353. package/src/agent-mission/mission-hand-reply.ts +118 -0
  354. package/src/agent-mission/mission-line.ts +180 -0
  355. package/src/agent-mission/mission-out.ts +256 -18
  356. package/src/agent-mission/tool-install.ts +122 -11
  357. package/src/agent-substrate/pr-reviewer.ts +147 -11
  358. package/src/agent-substrate/self-review-cli.ts +10 -2
  359. package/src/agents/general-purpose.md +1 -1
  360. package/src/auto-research/research-fanout.ts +221 -0
  361. package/src/auto-research/research-turn.ts +362 -31
  362. package/src/autopilot/mission-codebase-gate.ts +7 -3
  363. package/src/autopilot/pr-manager.ts +31 -3
  364. package/src/boot/daemon-tools/self-implement.ts +3 -1
  365. package/src/boot/daemon-tools/web-search.ts +1 -2
  366. package/src/briefing/brief-items.ts +55 -11
  367. package/src/cli/agent-cli.ts +2 -1
  368. package/src/cli/catalog-cli.ts +31 -0
  369. package/src/cli/connector-cli.ts +16 -0
  370. package/src/cli/control-cli.ts +15 -6
  371. package/src/cli/doctor-fix.ts +2 -2
  372. package/src/cli/intake-source-cli.ts +44 -8
  373. package/src/cli/log-event-names.ts +8 -0
  374. package/src/cli/msg-cli.ts +32 -3
  375. package/src/cli/pr-cli.ts +21 -1
  376. package/src/cli/pr-land-heavy-remote.ts +44 -6
  377. package/src/cli/pr-land-overlap-evidence.ts +13 -1
  378. package/src/cli/pr-land-pod-shadow-job.ts +143 -5
  379. package/src/cli/pr-land-pod-shadow.ts +9 -2
  380. package/src/cli/release-checklist-read-cli.ts +44 -16
  381. package/src/cli/release-cli.ts +23 -3
  382. package/src/cli/research-cli.ts +9 -0
  383. package/src/cli/setup-cli.ts +166 -6
  384. package/src/cli/setup-keys.ts +199 -0
  385. package/src/cli/task-plugin-wizard-host.ts +114 -0
  386. package/src/cli/tasks-cli.ts +54 -9
  387. package/src/cli/tui-test-cli.ts +190 -0
  388. package/src/config.ts +4 -2
  389. package/src/connectors/linear-daily-progress.ts +228 -0
  390. package/src/connectors/linear-projector.ts +34 -12
  391. package/src/connectors/linear.ts +151 -7
  392. package/src/control-plane/ledger.ts +42 -4
  393. package/src/control-plane/member-tokens.ts +25 -2
  394. package/src/control-plane/ports.ts +8 -3
  395. package/src/control-plane/server.ts +26 -5
  396. package/src/core-turn/deep-turn-bloom.ts +67 -0
  397. package/src/core-turn/research-policy.ts +43 -0
  398. package/src/core-turn/run-core-turn.ts +488 -51
  399. package/src/core-turn/types.ts +4 -0
  400. package/src/dashboard/chat/acp-chat.ts +10 -0
  401. package/src/dashboard/classic-sidetab-mission-job.ts +192 -0
  402. package/src/dashboard/classic-sidetab-mission-pair.ts +82 -0
  403. package/src/dashboard/classic-sidetab-mission-trio.ts +242 -0
  404. package/src/dashboard/classic-sidetab-mission.ts +169 -5
  405. package/src/dashboard/index.ts +387 -14
  406. package/src/dashboard/input/chat-main-acp-dispatch.ts +23 -0
  407. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +8 -0
  408. package/src/dashboard/mission-cli-consumer.ts +20 -8
  409. package/src/dashboard/plugin-wizard-runtime.ts +50 -0
  410. package/src/dashboard/plugin-wizard-tool.ts +55 -0
  411. package/src/dashboard/sidetab-runtime.ts +3 -1
  412. package/src/dashboard/terminal-popup-runtime.ts +6 -3
  413. package/src/dashboard/visible-mission-diagnostics.ts +16 -0
  414. package/src/decisions/linear-progress-summary.ts +78 -0
  415. package/src/decisions/linear-progress-sync.ts +152 -0
  416. package/src/domains/outbound-alert.ts +49 -11
  417. package/src/domains/retro-cycle-observation.ts +43 -0
  418. package/src/domains/retro-cycle.ts +13 -2
  419. package/src/domains/retro-proposals.ts +92 -0
  420. package/src/domains/retro-report.ts +6 -1
  421. package/src/domains/telegram-kind-route.ts +16 -9
  422. package/src/graph-runner/arg-allowance.ts +13 -0
  423. package/src/graph-runner/graph-cli.ts +24 -1
  424. package/src/graph-runner/graph-grow-llm.ts +3 -1
  425. package/src/graph-runner/graph-run-api.ts +3 -1
  426. package/src/graph-runner/runner.ts +310 -46
  427. package/src/graph-wizard/mission-compose.ts +407 -12
  428. package/src/harness/dispatch-backpressure.ts +54 -1
  429. package/src/harness/dispatch-shadow-compare.ts +74 -0
  430. package/src/harness/harness-cli-command.ts +15 -3
  431. package/src/harness/harness-pod-dispatch.ts +3 -0
  432. package/src/harness/skill-exec.ts +21 -9
  433. package/src/harness/skill-select.ts +1 -1
  434. package/src/hooks/error-report-cloud.ts +101 -0
  435. package/src/hooks/receiver.ts +29 -2
  436. package/src/index.ts +129 -12
  437. package/src/intake-plane/actions.ts +354 -0
  438. package/src/intake-plane/adapters/telegram-ambient.ts +34 -18
  439. package/src/intake-plane/intake-sources.ts +78 -8
  440. package/src/intake-plane/items.ts +14 -4
  441. package/src/intake-plane/linear-mission-sync.ts +80 -0
  442. package/src/intake-plane/linear-secretary-runtime.ts +178 -0
  443. package/src/intake-plane/linear-wish.ts +227 -1
  444. package/src/life/life-cli.ts +168 -0
  445. package/src/life/life-kinds.ts +160 -0
  446. package/src/live-supply/budget-consumer.ts +78 -0
  447. package/src/live-supply/read.ts +3 -0
  448. package/src/live-supply/snapshot.ts +45 -0
  449. package/src/llm/child-first-llm-observation.ts +112 -0
  450. package/src/llm/provider-credentials.ts +68 -6
  451. package/src/llm/provider-failure-evidence.ts +44 -0
  452. package/src/llm.ts +172 -12
  453. package/src/maturity/feature-maturity.ts +3 -3
  454. package/src/mcp/client.ts +7 -0
  455. package/src/mcp/proxy-runtime.ts +6 -1
  456. package/src/mcp/server.ts +54 -0
  457. package/src/msg/msg-sse-server.ts +3 -3
  458. package/src/msg/msg-store.ts +177 -19
  459. package/src/native-tool-catalog.ts +36 -2
  460. package/src/nexus/api/consult-requests.ts +18 -6
  461. package/src/nexus/api/discord-trigger-bot.ts +6 -6
  462. package/src/nexus/api/graphs-api.ts +4 -2
  463. package/src/nexus/api/grokbot-trigger-bot.ts +206 -0
  464. package/src/nexus/api/http-server.ts +37 -1
  465. package/src/nexus/api/mcp-public-allowlist.ts +2 -0
  466. package/src/nexus/api/telegram-trigger-bot.ts +6 -6
  467. package/src/nexus/boot/register-mcp-clients.ts +114 -8
  468. package/src/nexus/index.ts +22 -2
  469. package/src/nexus/outbound/format.ts +9 -0
  470. package/src/nexus/outbound/grokbot-adapter.ts +71 -0
  471. package/src/nexus/outbound/router.ts +121 -16
  472. package/src/nexus/setup-status.ts +9 -0
  473. package/src/nexus/state/state.ts +3 -0
  474. package/src/oauth/codex-account-store.ts +20 -7
  475. package/src/oauth/codex-owner-mirror.ts +81 -0
  476. package/src/oauth/codex.ts +17 -2
  477. package/src/oauth/fallback-chain.ts +47 -17
  478. package/src/onboarding/first-run-doctor.ts +8 -1
  479. package/src/onboarding/first-run.ts +126 -29
  480. package/src/onboarding/key-setup.ts +103 -0
  481. package/src/onboarding/menu-answer.ts +13 -0
  482. package/src/onboarding/secret-prompt.ts +37 -0
  483. package/src/plugins/install/plugin-install.ts +59 -0
  484. package/src/pr-tool/census.ts +130 -0
  485. package/src/preview/terminal.ts +22 -7
  486. package/src/prompt-enhance/enhance.ts +10 -0
  487. package/src/pty-shell/pty-control-ipc.ts +4 -1
  488. package/src/pty-shell/pty-write-provenance.ts +21 -3
  489. package/src/pty-shell/registry.ts +9 -2
  490. package/src/registry/catalog-server.ts +232 -0
  491. package/src/registry/loader.ts +45 -1
  492. package/src/registry/types.ts +1 -2
  493. package/src/release-loop/proposal-auto-place.ts +83 -0
  494. package/src/research/axis-fanout.ts +79 -29
  495. package/src/research/lenses/second-order.ts +312 -0
  496. package/src/self-dev/ask-launch-flow.ts +72 -1
  497. package/src/self-dev/dev-cli.ts +7 -2
  498. package/src/self-dev/dev-pipeline.ts +5 -0
  499. package/src/self-dev/draft-residue.ts +64 -5
  500. package/src/self-dev/draft-sweep.ts +31 -10
  501. package/src/self-dev/draft-triage-rules.ts +60 -3
  502. package/src/self-dev/graph-front-nodes.ts +2 -1
  503. package/src/self-dev/graph-journey-route.ts +76 -21
  504. package/src/self-dev/launch-preflight.ts +58 -0
  505. package/src/self-dev/orchestrate.ts +1 -1
  506. package/src/self-dev/run-supervisor.ts +152 -2
  507. package/src/self-dev/shard-aggregate.ts +4 -0
  508. package/src/self-dev/shard-run.ts +36 -15
  509. package/src/self-implement/abandoned-classification.ts +12 -3
  510. package/src/self-implement/budget-gate.ts +7 -0
  511. package/src/self-implement/child-llm-preference.ts +10 -1
  512. package/src/self-implement/doctor-visit.ts +30 -8
  513. package/src/self-implement/elanous-tui-spawn.ts +3 -0
  514. package/src/self-implement/fixtures/tdd-green-missing-passing-child-transcript.txt +2 -0
  515. package/src/self-implement/gate-artifacts.ts +2 -1
  516. package/src/self-implement/goal-author-cli.ts +4 -2
  517. package/src/self-implement/goal-author.ts +549 -10
  518. package/src/self-implement/graph-overlay-yaml.ts +181 -4
  519. package/src/self-implement/graph-overlay.ts +3 -1
  520. package/src/self-implement/graph-profile-yaml.ts +107 -0
  521. package/src/self-implement/graph-yaml.ts +94 -5
  522. package/src/self-implement/off-diff-evidence.ts +249 -5
  523. package/src/self-implement/orchestrator.ts +298 -26
  524. package/src/self-implement/run-chart.ts +6 -3
  525. package/src/self-implement/run-ledger.ts +51 -1
  526. package/src/self-implement/runtime-overlay-run-steps.ts +95 -0
  527. package/src/self-implement/tdd-evidence-tags.ts +9 -0
  528. package/src/self-implement/test-scenario-request.ts +37 -6
  529. package/src/self-implement/train-gate-receipt.ts +166 -0
  530. package/src/self-implement/train-gate-remote.ts +62 -18
  531. package/src/session/chat.ts +6 -4
  532. package/src/session/session-fanout.ts +2 -0
  533. package/src/session/streaming/acp-stream-sink.ts +2 -1
  534. package/src/session/streaming/chunk-producer.ts +6 -0
  535. package/src/session/streaming/stream-compositor.ts +3 -2
  536. package/src/session-runtime/index.ts +74 -6
  537. package/src/session-runtime/retry-policy.ts +1 -1
  538. package/src/setup/llm-env-detect.ts +3 -0
  539. package/src/setup/service-key-catalog.ts +46 -0
  540. package/src/setup/service-keys.ts +241 -0
  541. package/src/sidetab-core/model.ts +205 -10
  542. package/src/sidetab-core/visible-agent-mission-pair.ts +51 -0
  543. package/src/sidetab-core/visible-agent-mission.ts +182 -48
  544. package/src/skills/tools/agent.ts +23 -4
  545. package/src/skills/tools/omni-search.ts +127 -27
  546. package/src/task-agent/parent-host.ts +29 -1
  547. package/src/task-agent/recall-inject.ts +98 -0
  548. package/src/task-agent/repair-outcomes.ts +65 -0
  549. package/src/task-agent/task-hand.ts +46 -14
  550. package/src/task-orchestrator/dispatcher.ts +14 -1
  551. package/src/task-orchestrator/linear-capture-events.ts +55 -0
  552. package/src/task-orchestrator/store.ts +16 -5
  553. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +23 -1
  554. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +107 -1
  555. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +113 -14
  556. package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +21 -9
  557. package/src/task-orchestrator/surfaces/pod-source-receive.ts +23 -11
  558. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +2 -0
  559. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +1 -1
  560. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +467 -77
  561. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +45 -3
  562. package/src/task-orchestrator/surfaces/self-implement-pod.ts +210 -14
  563. package/src/task-orchestrator/surfaces/self-implement.ts +27 -3
  564. package/src/task-orchestrator/surfaces/warm-reuse-evidence.ts +169 -0
  565. package/src/task-orchestrator/types.ts +14 -0
  566. package/src/telegram.ts +14 -16
  567. package/src/terminal/coding-agent.ts +14 -5
  568. package/src/terminal/session-registry.ts +1 -1
  569. package/src/tool-hints/gate.ts +46 -14
  570. package/src/tool-hints/signals.ts +1 -1
  571. package/src/tool-hints/types.ts +2 -0
  572. package/src/tool-runtime/agent-runtime.ts +1 -0
  573. package/src/tool-runtime/browser-hand-runtime.ts +391 -0
  574. package/src/tool-runtime/browser-runtime.ts +33 -19
  575. package/src/tool-runtime/elanous-linear-issue-runtime.ts +76 -0
  576. package/src/tool-runtime/elanous-linear-secretary-runtime.ts +173 -0
  577. package/src/tool-runtime/graph-mcp-runtimes.ts +240 -0
  578. package/src/tool-runtime/index.ts +6 -0
  579. package/src/tool-runtime/types.ts +5 -0
  580. package/src/tui/entry.ts +51 -8
  581. package/src/tui/sidetab/attach.ts +252 -11
  582. package/src/tui/sidetab/herdr-portal.ts +225 -0
  583. package/src/tui/sidetab/list.ts +3 -2
  584. package/src/tui-client/next-dashboard-runtime.ts +52 -0
  585. package/src/user-config.ts +34 -9
  586. package/src/user-intent/types.ts +4 -3
  587. package/src/ux-sim/browser-hand.ts +165 -0
  588. package/src/ux-sim/draft-inventory-builder.ts +199 -0
  589. package/src/ux-sim/fake-browser-page.ts +81 -0
  590. package/src/ux-sim/fake-linear-device.ts +31 -0
  591. package/src/ux-sim/graph-engine-sim.ts +47 -0
  592. package/src/ux-sim/linear-secretary.ts +208 -0
  593. package/src/ux-sim/mock-scenario-catalog.json +81 -26
  594. package/src/ux-sim/mock-scenario-catalog.ts +175 -5
  595. package/src/ux-sim/onboarding-first-run-device.ts +31 -21
  596. package/src/ux-sim/release-sim-rig.ts +62 -18
  597. package/src/ux-sim/scenario.ts +70 -26
  598. package/src/ux-sim/service-key-setup.ts +113 -0
  599. package/src/ux-sim/term-slash-device.ts +74 -0
  600. package/src/ux-sim/turn-throw-sim.ts +31 -0
  601. package/src/version/packed-revision.json +1 -1
  602. package/src/web-search/exa.ts +77 -0
  603. package/src/web-search/index.ts +30 -9
  604. package/src/web-search/provider.ts +4 -1
  605. package/src/web-search/x-intent.ts +5 -0
  606. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +0 -36
  608. package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/1455-530ca16c7e71baff.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +0 -32
  614. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +0 -286
  620. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +0 -34
  623. package/apps/pwa/out/_next/static/chunks/2456.b6de0e1c05f28843.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +0 -115
  629. package/apps/pwa/out/_next/static/chunks/2974.595d1e29d040cb6a.js +0 -64
  630. package/apps/pwa/out/_next/static/chunks/3207.a756c95e9ec8c3c8.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +0 -93
  632. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +0 -41
  633. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +0 -24
  635. package/apps/pwa/out/_next/static/chunks/3741.9cf76acaa2ee5298.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +0 -77
  641. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/4276.336d76d858fcdf1b.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +0 -148
  647. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +0 -74
  648. package/apps/pwa/out/_next/static/chunks/50061469.0326382241ef3b2d.js +0 -122
  649. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +0 -149
  650. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +0 -27
  652. package/apps/pwa/out/_next/static/chunks/53c73f84.373b6160adfa89a4.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/5431-72a1dbde59400691.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/6131.348b7d6bdcd267ce.js +0 -226
  664. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +0 -90
  665. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +0 -104
  671. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +0 -94
  672. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +0 -24
  679. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +0 -8
  687. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +0 -79
  689. package/apps/pwa/out/_next/static/chunks/8546-6f03f8ea34dd61d9.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/9249.976f1e69a9d80754.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/9633.ce5bddc56784da72.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/9656-374232558fd528d0.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +0 -62
  705. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0e3d7b9421b79dac.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/app/term/page-bf067887b3307423.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +0 -1
  758. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +0 -1
  760. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +0 -1
  763. package/apps/pwa/out/_next/static/css/be9019f696217521.css +0 -1
  764. /package/apps/pwa/out/_next/static/{X1ucLw10Cds_h614NoEnh → 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;
@@ -62,9 +69,19 @@ export interface WarmPodProviderOptions {
62
69
  transport?: (spec: WarmPodSpec) => Pick<WarmPodTransport, 'request'>;
63
70
  maxShadowMs?: number;
64
71
  now?: () => number;
65
- kubectl?: (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
72
+ kubectl?: (args: readonly string[], input?: string) => {
73
+ status: number | null; stdout: string; stderr: string; error?: NodeJS.ErrnoException;
74
+ };
66
75
  /** Private directory for cross-process prepare leases; defaults to a per-user directory under tmpdir(). */
67
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;
68
85
  /** Cancels a prepare waiting for another process to finish the same host/worker slot. */
69
86
  prepareAbortSignal?: AbortSignal;
70
87
  /** Injects the prepare-lease flock binding. Omitted keeps this platform's binding; null/false declares it unsupported. */
@@ -72,7 +89,7 @@ export interface WarmPodProviderOptions {
72
89
  /** Gate file this pool serves, used to bind cold prepare legs to their 1-file gate measurement key. */
73
90
  pairingGateFile?: string;
74
91
  }
75
- type WarmPodProviderRuntimeOverrides = Pick<WarmPodProviderOptions, 'fetch' | 'kubectl' | 'prepareLockDirectory'>;
92
+ type WarmPodProviderRuntimeOverrides = Pick<WarmPodProviderOptions, 'controlUrl' | 'fetch' | 'kubectl' | 'prepareLockDirectory'>;
76
93
  export interface WarmPodProviderLoadOptions {
77
94
  pairingGateFile?: string;
78
95
  runtimeOverrides?: Partial<WarmPodProviderRuntimeOverrides>;
@@ -84,6 +101,19 @@ export interface WarmPoolReconcileResult {
84
101
  ready: number;
85
102
  failed: readonly string[];
86
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
+ }
87
117
 
88
118
  /** One leg of a read-only warm/cold prepare-time measurement pair, bound to one 1-file gate key. */
89
119
  export interface WarmPreparePairLeg {
@@ -227,7 +257,7 @@ async function withPrepareLease<T>(candidate: WarmWorkerCandidate, directory: st
227
257
  export class WarmPodProvider {
228
258
  private readonly fetcher: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
229
259
  private readonly now: () => number;
230
- private readonly candidates: readonly WarmWorkerCandidate[];
260
+ private readonly candidates: WarmWorkerCandidate[];
231
261
  private readonly profile: WarmPoolProfile;
232
262
  /** This process's own legs, echoed on CLI stdout. Pairing never reads this — see readWarmReuseTimePairs. */
233
263
  private readonly legs: WarmReuseTimeLegRow[] = [];
@@ -241,20 +271,24 @@ export class WarmPodProvider {
241
271
  private readonly standingWorkers = new Map<string, Promise<boolean>>();
242
272
  /** This instance's prepare-lease flock binding; null means the platform cannot lock and every slot fails. */
243
273
  private readonly prepareFlock: WarmPrepareFlock | null;
274
+ private readonly quotaResolver: typeof resolveWarmPoolQuota;
244
275
  constructor(private readonly options: WarmPodProviderOptions) {
245
276
  const url = new URL(options.controlUrl);
246
277
  if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash
247
278
  || url.pathname !== '/' || !token(options.memberToken)) throw new Error('invalid warm control address');
279
+ this.quotaResolver = options.quotaResolverForTest ?? resolveWarmPoolQuota;
248
280
  const profile = resolveWarmPoolProfile(options.profileConfig ?? getUserConfig());
249
281
  this.profile = profile;
250
282
  const templates = options.poolTemplates ?? [];
251
283
  if (new Set(templates.map(candidate => `${candidate.host}/${candidate.tier}`)).size !== templates.length)
252
284
  throw new Error('duplicate warm pool template');
253
285
  const expanded = templates.flatMap(candidate => {
254
- const quota = resolveWarmPoolQuota(profile, candidate.host, candidate.tier);
286
+ const quota = this.quotaResolver(profile, candidate.host, candidate.tier);
255
287
  if (!quota || !candidate.spec.nodeName) throw new Error('unmeasured warm pool template');
256
- return Array.from({ length: quota }, (_, index): WarmWorkerCandidate => ({
257
- 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: {
258
292
  ...candidate.spec,
259
293
  name: `warm-${candidate.host}-${candidate.tier}-${index + 1}`,
260
294
  // The template worker ID is a generation token. Rotate it when a slot is quarantined.
@@ -262,9 +296,16 @@ export class WarmPodProvider {
262
296
  },
263
297
  }));
264
298
  });
265
- 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
+ }
266
307
  if (this.candidates.some(candidate => !workerId(candidate.spec.workerId)
267
- || !candidate.host.trim() || resolveWarmPoolQuota(profile, candidate.host, candidate.tier) < 1
308
+ || !candidate.host.trim() || this.quotaResolver(profile, candidate.host, candidate.tier) < 1
268
309
  || candidate.spec.memoryLimit !== (candidate.tier === 'lite' ? '2Gi' : '16Gi')
269
310
  || !warmWorkerPodManifest(candidate.spec))) throw new Error('invalid warm candidate');
270
311
  if (new Set(this.candidates.map(candidate => candidate.spec.workerId)).size !== this.candidates.length)
@@ -277,7 +318,7 @@ export class WarmPodProvider {
277
318
  names.add(identity);
278
319
  const slot = `${candidate.host}/${candidate.tier}`;
279
320
  const count = (counts.get(slot) ?? 0) + 1;
280
- if (count > resolveWarmPoolQuota(profile, candidate.host, candidate.tier))
321
+ if (count > this.quotaResolver(profile, candidate.host, candidate.tier))
281
322
  throw new Error('warm candidate quota exceeded');
282
323
  counts.set(slot, count);
283
324
  }
@@ -299,7 +340,8 @@ export class WarmPodProvider {
299
340
  return { status: response.status, value };
300
341
  }
301
342
 
302
- 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 }> {
303
345
  let timer: ReturnType<typeof setTimeout> | undefined;
304
346
  try {
305
347
  return await Promise.race([this.control(path, credential, body), new Promise<never>((_, reject) => {
@@ -368,7 +410,8 @@ export class WarmPodProvider {
368
410
  warmReuseTimeLegs(): readonly WarmReuseTimeLegRow[] { return [...this.legs]; }
369
411
 
370
412
  /** Prepare one named slot. A foreign or replaced Pod is never deleted. */
371
- async ensureStandingWorker(candidate: WarmWorkerCandidate): Promise<boolean> {
413
+ async ensureStandingWorker(candidate: WarmWorkerCandidate,
414
+ onFailure?: (decision: WarmPoolFailureDecision) => void): Promise<boolean> {
372
415
  const configured = this.candidates.find(row => row.host === candidate.host
373
416
  && row.spec.workerId === candidate.spec.workerId);
374
417
  if (!configured) return false;
@@ -376,7 +419,7 @@ export class WarmPodProvider {
376
419
  const inFlight = this.standingWorkers.get(key);
377
420
  if (inFlight) return inFlight;
378
421
  const preparation = withPrepareLease(configured, this.options.prepareLockDirectory,
379
- () => this.prepareStandingWorker(configured, this.options.prepareAbortSignal), this.options.prepareAbortSignal,
422
+ () => this.prepareStandingWorker(configured, this.options.prepareAbortSignal, onFailure), this.options.prepareAbortSignal,
380
423
  this.prepareFlock)
381
424
  .then(result => result === true);
382
425
  this.standingWorkers.set(key, preparation);
@@ -384,11 +427,32 @@ export class WarmPodProvider {
384
427
  finally { if (this.standingWorkers.get(key) === preparation) this.standingWorkers.delete(key); }
385
428
  }
386
429
 
387
- private async prepareStandingWorker(candidate: WarmWorkerCandidate, signal?: AbortSignal): Promise<boolean> {
430
+ private async prepareStandingWorker(candidate: WarmWorkerCandidate, signal?: AbortSignal,
431
+ onFailure?: (decision: WarmPoolFailureDecision) => void): Promise<boolean> {
388
432
  throwIfPrepareAborted(signal);
389
433
  const startedAt = this.now();
390
434
  const outcome: { preexisting: boolean; podUid: string | null } = { preexisting: false, podUid: null };
391
- 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);
392
456
  const totalDurationMs = this.now() - startedAt;
393
457
  const gateFile = this.options.pairingGateFile;
394
458
  // An unset gate file skips the measurement row entirely instead of recording an unpaired one.
@@ -398,8 +462,256 @@ export class WarmPodProvider {
398
462
  return ok;
399
463
  }
400
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
+
401
700
  private async coldPrepare(candidate: WarmWorkerCandidate,
402
- 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
+ };
403
715
  const configured = this.candidates.find(row => row.host === candidate.host
404
716
  && row.spec.workerId === candidate.spec.workerId && row.spec.cutSha === candidate.spec.cutSha
405
717
  && keyEquals(row.spec.key, candidate.spec.key));
@@ -410,19 +722,26 @@ export class WarmPodProvider {
410
722
  if (!configured || !configured.spec.nodeName
411
723
  || (configured.spec.name !== prefix && !/^[1-9][0-9]*$/.test(suffix)
412
724
  && !/^(lite|standard)-[1-9][0-9]*$/.test(suffix))
413
- || 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');
414
727
  throwIfPrepareAborted(signal);
415
728
  const { spec, host } = configured;
416
729
  const nodeName = spec.nodeName;
417
- if (!nodeName) return false;
730
+ if (!nodeName) return fail('slot-validation', 'node-unpinned');
418
731
  const credential = this.options.machineTokens?.[host];
419
- if (!token(credential)) return false;
732
+ if (!token(credential)) return fail('slot-validation', 'machine-credential-unavailable');
420
733
  const manifest = warmWorkerPodManifest(spec);
421
734
  const metadata = manifest.metadata as { labels: Record<string, string>; annotations: Record<string, string> };
422
735
  const template = manifest.spec as Record<string, unknown>;
423
- 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 } => {
424
737
  const argv = ['--context', spec.kubectlContext, '-n', spec.namespace, ...args];
425
- 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
+ }
426
745
  const env = { ...process.env };
427
746
  for (const name of ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete env[name];
428
747
  // A new standing worker can spend more than five minutes in its cold init
@@ -431,12 +750,32 @@ export class WarmPodProvider {
431
750
  const result = spawnSync('kubectl', argv, { encoding: 'utf8', input, env,
432
751
  timeout: argv[4] === 'wait' ? (POD_READY_WAIT_SECONDS + 30) * 1000 : 360_000,
433
752
  maxBuffer: 2 * 1024 * 1024 });
434
- return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
753
+ return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '',
754
+ error: result.error };
435
755
  };
436
756
  const parse = (result: { status: number | null; stdout: string }) => {
437
757
  if (result.status !== 0) return null;
438
758
  try { return JSON.parse(result.stdout) as any; } catch { return null; }
439
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
+ };
440
779
  const sameSpec = (actual: unknown, expected: unknown): boolean => {
441
780
  if (Array.isArray(expected)) return Array.isArray(actual) && actual.length === expected.length
442
781
  && expected.every((item, index) => sameSpec(actual[index], item));
@@ -483,57 +822,69 @@ export class WarmPodProvider {
483
822
  && Object.entries(metadata.annotations).every(([key, value]) => pod.metadata.annotations?.[key] === value)
484
823
  && pod.spec?.nodeName === nodeName
485
824
  && sameSpec(normalizedSpec(pod.spec), template);
486
- // Resolve the pinned Pod independently; namespace size must not affect this read.
487
- const namedResult = kubectl(['get', 'pods', `--field-selector=metadata.name=${spec.name}`, '-o', 'json']);
488
- const namedList = parse(namedResult);
489
- if (!Array.isArray(namedList?.items) || !namedList.metadata || typeof namedList.metadata !== 'object'
490
- || Array.isArray(namedList.metadata)) return false;
491
- const namedMatches = namedList.items.filter((row: any) => row?.metadata?.name === spec.name);
492
- if (namedMatches.length > 1) return false;
493
- const namedPod = namedMatches[0] ?? null;
494
-
495
- // Kubectl's continuation token is consumed explicitly so no partial page can
496
- // authorize a create. A hard page cap turns a non-terminating listing into failure.
497
- const warmPods: any[] = [];
498
- let continuation = '';
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);
836
+ }
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[] = [];
499
841
  const seenContinuations = new Set<string>();
500
- for (let page = 0; page < 100; page++) {
501
- const args = ['get', 'pods', '-l', 'elanous.kind=warm-worker', '--limit=10', '-o', 'json'];
502
- if (continuation) args.splice(5, 0, '--continue', continuation);
503
- const result = kubectl(args);
504
- const listed = parse(result);
505
- if (!listed || !Array.isArray(listed.items) || !listed.metadata || typeof listed.metadata !== 'object'
506
- || Array.isArray(listed.metadata)
507
- || (listed.metadata.continue !== undefined && typeof listed.metadata.continue !== 'string')) return false;
508
- warmPods.push(...listed.items);
509
- continuation = listed.metadata?.continue ?? '';
510
- if (!continuation) break;
511
- if (seenContinuations.has(continuation) || page === 99) return false;
512
- seenContinuations.add(continuation);
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);
849
+ }
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;
513
860
  }
514
- const hostPods = warmPods.filter((row: any) => row?.metadata?.name === spec.name
515
- || row?.metadata?.annotations?.['elanous.dev/warm-worker-id'] === spec.workerId);
516
- if (namedPod && !hostPods.some((row: any) => row?.metadata?.name === spec.name)) hostPods.push(namedPod);
517
- if (hostPods.length > 1) return false;
518
- 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];
519
869
  const preexisting = !!pod;
520
870
  outcome.preexisting = preexisting;
521
- if (pod && !matches(pod)) return false;
871
+ if (pod && !matches(pod)) return fail('pod-validation', 'candidate-pod-mismatch');
522
872
  // Old generations still occupy memory even when their ledger rows are quarantined.
523
873
  // Count physical warm Pods on the pinned node before admitting a new one.
524
- 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'
525
875
  && row?.spec?.nodeName === nodeName && !row?.metadata?.deletionTimestamp
526
876
  && row?.status?.phase !== 'Succeeded' && row?.status?.phase !== 'Failed'
527
877
  && row?.spec?.containers?.[0]?.resources?.limits?.memory === spec.memoryLimit).length;
528
- if (!preexisting && physical >= resolveWarmPoolQuota(this.profile, host, configured.tier)) {
878
+ if (!preexisting && physical >= this.quotaResolver(this.profile, host, configured.tier)) {
529
879
  debug.log('pod.warm', 'pool-slot-refused', { host, tier: configured.tier,
530
880
  workerId: spec.workerId, reason: 'physical-quota', physical }, { level: 'warn' });
531
- return false;
881
+ return fail('pod-list', 'physical-quota');
532
882
  }
533
883
  const readLedger = async (): Promise<any[] | null> => {
534
884
  try {
535
885
  const query = new URLSearchParams({ kind: 'pod-worker-lease', machine: host, name: spec.workerId });
536
- 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);
537
888
  return ledger.status === 200 && Array.isArray(ledger.value?.resources)
538
889
  && ledger.value.resources.length <= 1 ? ledger.value.resources : null;
539
890
  } catch { return null; }
@@ -542,9 +893,15 @@ export class WarmPodProvider {
542
893
  // A worker ID already bound to another UID must not leave a newly created, unregistered Pod.
543
894
  const before = await readLedger();
544
895
  throwIfPrepareAborted(signal);
545
- if (!before || before.length !== 0) return false;
546
- pod = parse(kubectl(['create', '-f', '-', '-o', 'json'], JSON.stringify(manifest)));
547
- 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');
548
905
  }
549
906
  const uid = pod.metadata.uid as string;
550
907
  outcome.podUid = uid;
@@ -562,15 +919,19 @@ export class WarmPodProvider {
562
919
  const observed = parse(kubectl(['get', `pod/${spec.name}`, '-o', 'json']));
563
920
  throwIfPrepareAborted(signal);
564
921
  if (ready(observed)) { isReady = true; break; }
565
- 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');
566
923
  if (this.now() < deadline) await waitForPrepareRetry(signal);
567
924
  }
568
- if (!isReady) return false;
925
+ if (!isReady) return fail('pod-ready', 'ready-deadline-exceeded');
569
926
  // Re-read after create: a registration racing the preflight must fail before our POST.
570
927
  const rows = await readLedger();
571
- if (!rows) return false;
928
+ if (!rows) return fail('lease-registration', 'lease-read-failed');
572
929
  const existing = rows[0];
573
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');
574
935
  if (existing) return preexisting && existing.kind === 'pod-worker-lease'
575
936
  && existing.machine === host && existing.name === spec.workerId
576
937
  && (existing.attrs?.state === 'idle' || existing.attrs?.state === 'leased')
@@ -582,7 +943,7 @@ export class WarmPodProvider {
582
943
  && existing.attrs?.key && keyEquals(existing.attrs.key, spec.key);
583
944
  // An unregistered Pod could have been created by anyone with namespace access.
584
945
  // Only this invocation's successful create response may become a new ledger row.
585
- if (preexisting) return false;
946
+ if (preexisting) return fail('lease-registration', 'preexisting-pod-unregistered');
586
947
  throwIfPrepareAborted(signal);
587
948
  let registered: { status: number; value: any };
588
949
  try {
@@ -590,11 +951,13 @@ export class WarmPodProvider {
590
951
  workerId: spec.workerId, machine: host, podUid: uid, key: spec.key,
591
952
  ttlMs: leaseTtlMs,
592
953
  });
593
- } catch { return false; }
594
- 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
595
956
  && registered.value?.attrs?.state === 'idle'
596
957
  && registered.value?.attrs?.podUid === uid
597
- && !!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;
598
961
  }
599
962
 
600
963
  async prepareWorker(workerId: string): Promise<boolean> {
@@ -609,10 +972,25 @@ export class WarmPodProvider {
609
972
  let ready = 0;
610
973
  for (const candidate of this.candidates) {
611
974
  let prepared = false;
612
- try { prepared = await this.ensureStandingWorker(candidate); }
613
- 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
+ }
614
983
  if (prepared) ready++;
615
- 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
+ }
616
994
  debug.log('pod.warm', 'pool-slot', { profile: this.profile,
617
995
  host: candidate.host, tier: candidate.tier, workerId: candidate.spec.workerId,
618
996
  pod: candidate.spec.name, ready: prepared }, { level: prepared ? 'info' : 'warn' });
@@ -635,6 +1013,7 @@ export class WarmPodProvider {
635
1013
  // host/key once so a busy first host cannot strand an idle second pool.
636
1014
  const tried = new Set<string>();
637
1015
  for (const candidate of matches) {
1016
+ if (input.deadlineAt <= this.now()) return lastFallback;
638
1017
  const { spec, host } = candidate;
639
1018
  const poolKey = `${host}/${spec.key.context}/${spec.key.imageDigest}/${spec.key.lockHash}/${spec.key.architecture}`;
640
1019
  if (tried.has(poolKey)) continue;
@@ -642,7 +1021,8 @@ export class WarmPodProvider {
642
1021
  const contextFailure = probeWarmPodContext(spec.kubectlContext, this.options.kubectl);
643
1022
  if (contextFailure) { lastFallback = { command: null, transport: contextFailure }; continue; }
644
1023
  let acquired: { status: number; value: any };
645
- 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, {
646
1026
  machine: host, key: spec.key, runId: input.runId, attemptId: input.attemptId,
647
1027
  ttlMs: this.options.leaseTtlMs ?? 30 * 60_000,
648
1028
  });
@@ -704,7 +1084,10 @@ export class WarmPodProvider {
704
1084
  if (acquired.status === 409) {
705
1085
  try {
706
1086
  const query = new URLSearchParams({ kind: 'pod-worker-lease', machine: host, name: spec.workerId });
707
- 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));
708
1091
  const row = observed.status === 200 && Array.isArray(observed.value?.resources)
709
1092
  ? observed.value.resources.find((entry: any) => entry?.name === spec.workerId) : undefined;
710
1093
  if (row?.attrs?.state === 'quarantined' || row?.attrs?.state === 'timeout-fenced') {
@@ -764,9 +1147,16 @@ export class WarmPodProvider {
764
1147
  debug.log('pod.provenance', 'lease-start', { pod: leasedCandidate.spec.name, podUid: command.podUid,
765
1148
  leaseId: command.leaseId, runId: command.runId, attemptId: command.attemptId,
766
1149
  requestedAt: command.requestedAt, metadata: warmLeaseMetadata(command).annotations });
767
- 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
+ }
768
1157
  catch (error) { debug.log('pod.warm', 'late-lease-quarantine-failed', { workerId: command.workerId,
769
1158
  leaseId: command.leaseId, generation: command.generation, error: String(error) }, { level: 'warn' }); }
1159
+ finally { if (quarantineTimer) clearTimeout(quarantineTimer); }
770
1160
  return fallback('lease-unavailable');
771
1161
  }
772
1162
  // Acquisition is by pool key. A healthy sibling or replacement Pod must remain eligible.
@@ -937,7 +1327,7 @@ export function loadWarmPodProvider(path: string, pairingGateFileOrOptions?: str
937
1327
  if (!pairingGateFile.trim()) throw new Error('warm provider pairing gate file must not be empty');
938
1328
  options.pairingGateFile = pairingGateFile;
939
1329
  }
940
- return new WarmPodProvider({ ...options, ...runtimeOverrides });
1330
+ return new WarmPodProvider({ ...options, ...runtimeOverrides, rotationStatePath: options.rotationStatePath ?? `${path}.rotations` });
941
1331
  }
942
1332
  function currentProvider(): WarmPodProvider | null {
943
1333
  if (configuredProvider) return configuredProvider;