elanous 0.2.35 → 0.2.37

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 (827) 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/{T1mBQqmkwKDLGtyisDCOd → 8kITumG9GAZ1LwT9POrAR}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1041-5841aae848fb6403.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1087.3177e54f672dc526.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1105.efe1e13503ac7a33.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1224.2ceca6c180d5130b.js +62 -0
  8. package/apps/pwa/out/_next/static/chunks/{9952.5daf5f94063a2b3e.js → 1262.a89bb27a5b2f844e.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/1417-1768233ac92f8a5d.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1612.81bbc19af06af16c.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1746.3563617ea64023a3.js +115 -0
  12. package/apps/pwa/out/_next/static/chunks/1766.728d42053070bb2d.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1773.d38e945481e04125.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1974-c12f9600da3d3c26.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/{1dc5a567.9b092bfae3d7099b.js → 1ed5e8af.cf424815c7c0efad.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/{4705-194e64e47ca0fbdb.js → 2002-a43a3f380df873ec.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/2061.7232dc9dc058417f.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{13.02a07436dec15f89.js → 210.90b2cbd64926cf34.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/{9512.5a76207a730e0491.js → 2283.edd9627b7bdfb546.js} +2 -2
  20. package/apps/pwa/out/_next/static/chunks/{6866-79aa5b4e47cac6aa.js → 2318-450e75d07605b8fe.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/{938.66ad1ef0e5abae1c.js → 2375.bf364c85dbb6f085.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/244-a810d282a20f1783.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2479-fe7e188bd9402d14.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2514.03b60f266531a015.js +148 -0
  25. package/apps/pwa/out/_next/static/chunks/2526-a657538869888604.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2533-6f0341ff075809e9.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2676.e7994c695ef0f30d.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2734.3ffdf54254fb6f99.js +27 -0
  29. package/apps/pwa/out/_next/static/chunks/2770-23252bf3a37880b9.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2969.66b8b545139d1544.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/3128.a9210d08800ea6a8.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/3199.6a1a9b0e6cd01dc6.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{9214.63a510d39178e997.js → 3578.4e9f9948a9a6f2f4.js} +2 -2
  35. package/apps/pwa/out/_next/static/chunks/3760-2909ddb9279281cd.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3825.1921b5901bf33dce.js +36 -0
  37. package/apps/pwa/out/_next/static/chunks/3994-63eb884ed0547011.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/4036.d54e55e1ebf5acdc.js +90 -0
  39. package/apps/pwa/out/_next/static/chunks/4115.5b438b99f0ae729f.js +226 -0
  40. package/apps/pwa/out/_next/static/chunks/4269.d8f9f34bb0fc6c43.js +287 -0
  41. package/apps/pwa/out/_next/static/chunks/431-4eed5bd2c5fe1719.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{2018.a741297bce759490.js → 4313.76f232c304a15bde.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/{1165.bad13fc9151a7288.js → 4324.c99ba3e83eb1b691.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/{3f7c4ee5.3dde54f01a816613.js → 4337e36d.f42c3b391b3d8f20.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/{7385.50a308a477bdfc21.js → 4383.18e5c54db3a22c88.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4599-96eff3f2a9c242c1.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{8557-e7898769499cd56f.js → 4725-34bed25720cc326b.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/4825-a9d9d4e9f5892309.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/{5483-dd5d7697f6bdb106.js → 4833-7934c592c5569f00.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/4879-68518f547a207aff.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4896.f9b70285363b27b2.js +24 -0
  52. package/apps/pwa/out/_next/static/chunks/{8444.4c26b379ca2b9596.js → 4931.95cc6dbd230774ca.js} +17 -17
  53. package/apps/pwa/out/_next/static/chunks/4937-28d437999b77c9dd.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/49aa54ab.80f39398e0ed0f30.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/{3408.21d1570602f4b6c0.js → 5046.7fd5cbd859299448.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/5056.482687319b82638e.js +104 -0
  57. package/apps/pwa/out/_next/static/chunks/506.16b83d56a437a9ee.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5260-20593b0a25d0afbc.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5404.cc4e9fec26fd5925.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/576.485e83e3f3dec19a.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5832-a6be14e663f65726.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5854-56648a9fe646b9a0.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/58edce7d.f1b0c72acf13b123.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +93 -0
  65. package/apps/pwa/out/_next/static/chunks/5952.294fc574a8625184.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/{b6705a4f-b9a9047de9eecfd0.js → 5f6af561-f90b7a67f4f75d13.js} +1 -1
  67. package/apps/pwa/out/_next/static/chunks/6168.17749aa3905fa073.js +74 -0
  68. package/apps/pwa/out/_next/static/chunks/6252.240578f10952e091.js +34 -0
  69. package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/{5101.e32282718f99ed6f.js → 6680.6a693c28aae4cd19.js} +1 -1
  71. package/apps/pwa/out/_next/static/chunks/{4718-17d2af636796a330.js → 6745-5a63492933959dd9.js} +3 -3
  72. package/apps/pwa/out/_next/static/chunks/6758.e7fbf532953c0783.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6795.0cb8a59730d99703.js +149 -0
  74. package/apps/pwa/out/_next/static/chunks/{6236.e18cced2d5ba6afb.js → 6819.be5c9c4d67f22922.js} +16 -16
  75. package/apps/pwa/out/_next/static/chunks/6877-84d47b7e85d0c41e.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/{2833-0fe47af99ed13c29.js → 6971-fb5f9dc0a2cb5d2d.js} +1 -1
  77. package/apps/pwa/out/_next/static/chunks/{da5176c6.6773b6c5994e1d99.js → 709a918c.4fd3b4e2a32cb4e3.js} +1 -1
  78. package/apps/pwa/out/_next/static/chunks/717.522b08685979b814.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/7191.e41eb8f455c7f089.js +41 -0
  80. package/apps/pwa/out/_next/static/chunks/7233.8e744b7513119409.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7238-97c2226f80504bd8.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7285.307963afcf7e7660.js +24 -0
  83. package/apps/pwa/out/_next/static/chunks/7299-fbe4b8c47a87614a.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7314.df0d95930b0c3f50.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7406.c0589fdf2ee66629.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7424.c0fe2a595d4232b5.js +23 -0
  87. package/apps/pwa/out/_next/static/chunks/752-90e3e7daad3d8e39.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7524-bcd3d9928c6842d9.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7549.9a4de3bf231cfc52.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/757-4fbaf50942b8c424.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/765.76ae970ceb597b5b.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{7644.17e37ba6f1d4226e.js → 7867.e8725745ddd1d940.js} +4 -4
  93. package/apps/pwa/out/_next/static/chunks/7893-e444a1b0809cd1e4.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{0368f25d-ad84af92fefee68f.js → 7c5ed21f-f286708e57cf88f7.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/8030-aa66b478b53dbfca.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/8051.c88c07dacbbc5f21.js +8 -0
  98. package/apps/pwa/out/_next/static/chunks/{359-2250789a2d3499e7.js → 8080-40d781e7704d3671.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/8149.80e402d4345e8dae.js +77 -0
  100. package/apps/pwa/out/_next/static/chunks/8205.50a5268f4e12ba0b.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8231.a128feaa6f8b6079.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/85.4ddb1dda2ea3ffa2.js +286 -0
  103. package/apps/pwa/out/_next/static/chunks/8537.26517a09e8247beb.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8595-e9b7081ab773a61f.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8821-30cc65a61a60951f.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8905.b3385aadb7b133e6.js +79 -0
  107. package/apps/pwa/out/_next/static/chunks/8983.246bb3a1ac38f50f.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/{b2f6ba4d.fb56fdbbf798df33.js → 8c58d09b.e8724a299658d86f.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/9072.18342f5941a1f3ec.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/9123.9df04866b2d6e00f.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/{1530.0e612369b6fd7ea0.js → 9151.a2c51e93f9156dae.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/{4441-875b97f5821ed58a.js → 9175-7d383df15a82e2be.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/9181-bf89c07ce5b8f599.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/920-429bbc4504b45f71.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9207.625edf45d656151c.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9378.d2575c6ffd6dd756.js +94 -0
  117. package/apps/pwa/out/_next/static/chunks/957.db5b5c4d32a35240.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/958.53a8dcf4729e6997.js +64 -0
  119. package/apps/pwa/out/_next/static/chunks/9647.8f0cd0898c592a29.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9665-26abb02665704c2c.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9703.e7d81b6faef1050a.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9882-7a9164b3c2dacbe5.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/9998.b74e033efadecd10.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c604fb980a634d88.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ec2d8a4979ca9075.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cb6add08ad3c7660.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/board/page-e2eaa321ec223fe6.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5b8a09c475b5f844.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/bots/page-91a77daedfbac191.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/ceo/page-f37fc5e974bda39b.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/chat/page-91429a0b78415a94.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/consult/page-b95798b58263b910.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/control/page-763d53e175e17aa1.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/decisions/page-d55023204055b12f.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/design-check/page-89d2140d78abc810.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/editor/page-82dc069abfe27f55.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/exec/page-25d9eee7beb552c0.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/field/page-770b4594c713ee97.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c604fb980a634d88.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/inside/page-ba8e0dcae4f83985.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/intake/page-5c112c6d7c33656e.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/layout-09788e1aa9a87db6.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live/page-3337c6a1328c2674.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/page-89acd5d68c0bc560.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/loops/page-5c13e2c76371d797.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/market/page-653a9f18642825c6.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-fe986fe79b107546.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/morning/{page-47c5d4db587266bd.js → page-1e5da297557d2354.js} +1 -1
  149. package/apps/pwa/out/_next/static/chunks/app/not-found-91ea9f5869a40943.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/observatory/page-7824e21b999962e2.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-53d00a60cc75a922.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-1f5a60f70cb4dd0c.js → page-88a1edbff62eaf1d.js} +1 -1
  153. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e931248b3f9574b.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-bac7dee94e5eae4a.js → page-443364c9e4a23b31.js} +1 -1
  155. package/apps/pwa/out/_next/static/chunks/app/page-b9af2ac1c4737ce8.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/reflection/page-25ab9c614c14129b.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-dbcdcfac0a395a8a.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bf1de6645089ec58.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c4201761a5e839c9.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/settings/page-c304f469ea5d70e4.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8e5a238cf5e50c9c.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c604fb980a634d88.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/page-32b526069ee50b3c.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/share/page-fb9e639c107f5f72.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/showroom/page-84220c776608cedc.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/tasks/page-e475618cbfd5ca2b.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/term/{page-dc0a532f5b9196ec.js → page-ac2c1e6169cfce57.js} +1 -1
  168. package/apps/pwa/out/_next/static/chunks/app/today/page-fccd3020af2f8806.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/trace/page-9c0d7198ff7b5a91.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/vault/page-d54b1ec9b9c6430d.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8c4bef1791d732ca.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/page-46fcc0b0bbc6fbfe.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c1c80e345fa9d6f5.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7dfda00afd172f5b.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/{d06f9160.a3d9062f95de8ad7.js → d9815f84.4918f682a45a8502.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/{cbe38a15.5a032598e39970c6.js → de8446d3.31b73eb239fecd7b.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/f21fd540.63ac75328926ed30.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/framework-77fdcc873519a6cd.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-app-d0d620d2ce235713.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-e0ef79c79337ba90.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-84b1bc71e3f9ce6a.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-2acb61f1363128cb.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-462ff5727ceb7824.js +1 -0
  184. package/apps/pwa/out/_next/static/css/{b5d88eba78a3d7bf.css → be9019f696217521.css} +1 -1
  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/docs/goal-context/05-authoring-lessons.jsonl +47 -0
  279. package/docs/goal-context/README.md +3 -0
  280. package/graphs/default-loop.yaml +6 -1
  281. package/graphs/drafts/recipes.yaml +27 -7
  282. package/graphs/implement-loop.yaml +24 -0
  283. package/graphs/release/recipes.yaml +6 -0
  284. package/graphs/release/release-loop.yaml +17 -4
  285. package/graphs/wizard/recipes.yaml +44 -0
  286. package/graphs/wizard/wizard-loop.yaml +44 -0
  287. package/graphs/work/draft-close.yaml +54 -0
  288. package/graphs/work/green-close.yaml +37 -0
  289. package/graphs/work/recipes.yaml +46 -1
  290. package/graphs/work/research-report.yaml +2 -0
  291. package/graphs/work/run-research.yaml +35 -0
  292. package/package.json +1 -1
  293. package/scripts/__fixtures__/authoring-paths-10x3.json +32 -0
  294. package/scripts/boundary-proof-mapping-audit.ts +88 -0
  295. package/scripts/ci-typecheck-changed.ts +139 -5
  296. package/scripts/codex-fallback-live-probe.ts +17 -15
  297. package/scripts/coord-role-pack.ts +1 -1
  298. package/scripts/first10-gate.ts +416 -0
  299. package/scripts/fleet/node-setup.sh +32 -2
  300. package/scripts/fleet/run-light-worker.sh +9 -0
  301. package/scripts/fleet/wsl-node-setup.sh +432 -0
  302. package/scripts/gate-0226-failure-census.ts +507 -0
  303. package/scripts/goals/authoring-metrics.ts +258 -0
  304. package/scripts/graph-units/draft-close-node.ts +222 -0
  305. package/scripts/hook-order-sweep.ts +7 -4
  306. package/scripts/land-global-checks.ts +69 -1
  307. package/scripts/measure-nl-routing-corpus.ts +207 -2
  308. package/scripts/release-loop/docs-node.ts +344 -8
  309. package/scripts/release-loop/gate-closure.ts +48 -0
  310. package/scripts/release-loop/gate-diff.ts +2 -1
  311. package/scripts/release-loop/gate-node.ts +249 -53
  312. package/scripts/release-loop/known-issues-node.ts +6 -4
  313. package/scripts/release-loop/node-verdict.ts +2 -0
  314. package/scripts/release-loop/notes-repair.ts +216 -0
  315. package/scripts/release-loop/prepare-node.ts +61 -5
  316. package/scripts/release-loop/resume-cap.ts +14 -34
  317. package/scripts/release-loop/resume-release.ts +151 -15
  318. package/scripts/release-loop/resume-test-traffic.ts +10 -0
  319. package/scripts/release-loop/traffic-node.ts +59 -0
  320. package/scripts/release-loop/traffic-transition.ts +159 -0
  321. package/scripts/release-loop/version-node.ts +81 -12
  322. package/scripts/rhythm/daily.ts +137 -3
  323. package/scripts/train/green-leg.ts +176 -0
  324. package/scripts/tui-continue-real-evidence.ts +47 -0
  325. package/scripts/ux-sim.ts +53 -0
  326. package/skills/README.md +1 -0
  327. package/src/acp/client.ts +31 -5
  328. package/src/acp/core-turn-bridge.ts +54 -6
  329. package/src/acp/stderr-summary.ts +49 -0
  330. package/src/agent/delegate-hint.ts +16 -0
  331. package/src/agent/monad-agent-turn.ts +1 -0
  332. package/src/agent/runner.ts +82 -2
  333. package/src/agent/task-notification.ts +56 -10
  334. package/src/agent/terminal-surface.ts +217 -0
  335. package/src/agent/types.ts +4 -0
  336. package/src/agent-substrate/pr-reviewer.ts +64 -4
  337. package/src/agent-substrate/review-intent.ts +29 -2
  338. package/src/agent-substrate/review-observation.ts +3 -0
  339. package/src/agent-substrate/self-review-cli.ts +490 -26
  340. package/src/autopilot/lineage/sources.ts +1 -1
  341. package/src/autopilot/mission-codebase-gate.ts +141 -12
  342. package/src/autopilot/pr-manager.ts +89 -18
  343. package/src/boot/daemon-runtime.ts +35 -3
  344. package/src/boot/daemon-tools/pty-shell.ts +2 -1
  345. package/src/boot/daemon-tools/self-implement.ts +1 -0
  346. package/src/budget/run-usage-rollup.ts +12 -7
  347. package/src/budget/unified-usage.ts +20 -0
  348. package/src/budget/usage-outcomes.ts +8 -4
  349. package/src/capture/tui-self-report.ts +132 -1
  350. package/src/chat/goal-continue-verdict.ts +41 -0
  351. package/src/chat/index.ts +30 -4
  352. package/src/chat/internal-turn-origin.ts +1 -0
  353. package/src/chat/paste-fold.ts +86 -0
  354. package/src/chat/tool-render/block.ts +32 -0
  355. package/src/chat/tool-render/dispatch.ts +2 -20
  356. package/src/chat/tool-render/result-text.ts +98 -0
  357. package/src/chat/turn-typeahead.ts +70 -7
  358. package/src/cli/autopilot-cli.ts +24 -9
  359. package/src/cli/config-audit.ts +119 -0
  360. package/src/cli/decisions-cli.ts +7 -4
  361. package/src/cli/doctor-cli.ts +137 -6
  362. package/src/cli/guardian-cli.ts +10 -0
  363. package/src/cli/herdr-cli.ts +34 -0
  364. package/src/cli/light-entry-route.ts +6 -3
  365. package/src/cli/log-event-names.ts +2 -0
  366. package/src/cli/logs-cli.ts +357 -2
  367. package/src/cli/make-cli.ts +247 -11
  368. package/src/cli/memory-cli.ts +30 -1
  369. package/src/cli/msg-cli.ts +132 -14
  370. package/src/cli/pr-cli.ts +29 -16
  371. package/src/cli/pr-granularity.ts +5 -3
  372. package/src/cli/pr-land-base-read.ts +11 -2
  373. package/src/cli/pr-land-heavy-remote.ts +6 -3
  374. package/src/cli/pr-land-overlap-evidence.ts +23 -4
  375. package/src/cli/pr-land-recovery.ts +117 -1
  376. package/src/cli/pty-takeover-cli.ts +8 -7
  377. package/src/cli/release-checklist-read-cli.ts +109 -4
  378. package/src/cli/release-cli.ts +64 -15
  379. package/src/cli/release-single-flight.ts +1 -1
  380. package/src/cli/self-update.ts +23 -5
  381. package/src/cli/self-utterances-cli.ts +13 -16
  382. package/src/cli/tasks-card-show.ts +162 -0
  383. package/src/cli/tasks-cli.ts +868 -352
  384. package/src/cli/tasks-read-cli.ts +197 -0
  385. package/src/cli/usage-cli.ts +12 -10
  386. package/src/context-bus/claude-memory-import.ts +99 -0
  387. package/src/context-bus/long-term-memory.ts +125 -0
  388. package/src/core-turn/run-core-turn.ts +4 -2
  389. package/src/core-turn/run-goal-loop.ts +76 -16
  390. package/src/core-turn/types.ts +8 -1
  391. package/src/dashboard/chat/acp-chat.ts +9 -0
  392. package/src/dashboard/context/keys.ts +8 -31
  393. package/src/dashboard/index.ts +299 -62
  394. package/src/dashboard/input/agent-wake.ts +50 -1
  395. package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
  396. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +146 -3
  397. package/src/dashboard/input/chat-main-slash-command.ts +4 -2
  398. package/src/dashboard/input/goal-continue.ts +210 -0
  399. package/src/dashboard/input/goal-interim.ts +2 -0
  400. package/src/dashboard/input/internal-turn.ts +2 -8
  401. package/src/dashboard/input/seat-autowake.ts +9 -7
  402. package/src/dashboard/input/turn-autoheal.ts +52 -0
  403. package/src/dashboard/replay-preview.ts +25 -0
  404. package/src/dashboard/seat-boot-preamble.ts +132 -5
  405. package/src/dashboard/slash-runtime/chat-persona.ts +65 -8
  406. package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -10
  407. package/src/dashboard/slash-runtime/model-choices.ts +15 -0
  408. package/src/dashboard/stale-install-check.ts +3 -99
  409. package/src/dashboard/synchronized-frame-output.ts +74 -0
  410. package/src/debug/batch-queue.ts +119 -0
  411. package/src/debug/tui-log-tag.ts +67 -0
  412. package/src/display/agent-surface.ts +7 -0
  413. package/src/display/coordinator.ts +45 -12
  414. package/src/display/resize-listener.ts +41 -20
  415. package/src/display/synchronized-output-region.ts +60 -0
  416. package/src/domains/standalone-log-sink.ts +15 -2
  417. package/src/domains/surface-events.ts +80 -1
  418. package/src/esc-abort-gate.ts +69 -0
  419. package/src/exec-requests/planner.ts +37 -2
  420. package/src/execution-loop/launch-gate.ts +4 -3
  421. package/src/git-fs/cli-stderr.ts +45 -0
  422. package/src/git-fs/gh-cli.ts +6 -3
  423. package/src/git-fs/git-cli.ts +11 -5
  424. package/src/git-fs/worktree.ts +95 -1
  425. package/src/graph-runner/graph-cli.ts +44 -2
  426. package/src/graph-runner/graph-run-api.ts +3 -2
  427. package/src/graph-runner/runner.ts +37 -16
  428. package/src/graph-wizard/generate.ts +131 -4
  429. package/src/graph-wizard/steps.ts +15 -0
  430. package/src/grounding/proactive-ground.ts +86 -0
  431. package/src/harness/control-inbox.ts +49 -1
  432. package/src/harness/harness-cli-command.ts +230 -40
  433. package/src/harness/harness-incidents.ts +52 -1
  434. package/src/harness/pod-control-send.ts +33 -5
  435. package/src/harness/review-adapter.ts +26 -6
  436. package/src/harness/self-send-remote.ts +69 -0
  437. package/src/harness/self-send-routing-guard.ts +6 -0
  438. package/src/harness/self-send-target.ts +4 -3
  439. package/src/index.ts +443 -25
  440. package/src/input-core/context-key-runtime.ts +21 -0
  441. package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
  442. package/src/light-cli.ts +6 -1
  443. package/src/llm/route-decision.ts +13 -0
  444. package/src/llm-tool-name.ts +37 -0
  445. package/src/llm-tool-schema.ts +35 -0
  446. package/src/llm-wellformed.ts +70 -0
  447. package/src/llm.ts +158 -15
  448. package/src/loops/checker.ts +100 -0
  449. package/src/loops/consensus.ts +26 -2
  450. package/src/loops/loop-cli.ts +9 -4
  451. package/src/loops/msg-wake.ts +35 -39
  452. package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
  453. package/src/loops/orchestrator/inbox.ts +59 -11
  454. package/src/loops/orchestrator/knowhow-pack.ts +364 -0
  455. package/src/loops/orchestrator/tick.ts +54 -5
  456. package/src/maturity/feature-maturity.ts +1 -1
  457. package/src/model-tier/llm-tier-map.ts +12 -17
  458. package/src/models/prompts.ts +17 -2
  459. package/src/msg/msg-post-office.ts +116 -0
  460. package/src/msg/msg-store.ts +17 -0
  461. package/src/mss/logging/log-store.ts +79 -22
  462. package/src/native-tool-catalog.ts +1 -1
  463. package/src/nexus/api/decisions-route.ts +38 -18
  464. package/src/nexus/api/graph-wizard-api.ts +46 -3
  465. package/src/nexus/api/grid.ts +24 -4
  466. package/src/nexus/api/http-server.ts +1 -1
  467. package/src/nexus/api/ops-api.ts +14 -6
  468. package/src/nexus/boot/register-mcp-clients.ts +263 -38
  469. package/src/nexus/onboarding/first-boot-wizard.ts +56 -8
  470. package/src/nexus/state/state.ts +7 -1
  471. package/src/oauth/codex-account-store.ts +34 -1
  472. package/src/onboarding/first-run.ts +37 -6
  473. package/src/onboarding/web-first.ts +43 -11
  474. package/src/ops/loops-map.ts +46 -0
  475. package/src/ops/overview-cache.ts +22 -4
  476. package/src/ops/overview-collect.ts +12 -6
  477. package/src/ops/overview.ts +44 -8
  478. package/src/persona/loader.ts +11 -0
  479. package/src/persona/types.ts +8 -0
  480. package/src/policy/llm-policy.ts +3 -1
  481. package/src/prompt-enhance/enhance.ts +4 -4
  482. package/src/prompt-library/session-guidance.ts +14 -0
  483. package/src/provider-summary.ts +8 -0
  484. package/src/pty-shell/availability.ts +7 -0
  485. package/src/pty-shell/herdr-agent-report.ts +155 -37
  486. package/src/pty-shell/herdr-prompt.ts +266 -0
  487. package/src/pty-shell/herdr-prune-stale.ts +17 -0
  488. package/src/pty-shell/herdr-sequence-lease.ts +60 -0
  489. package/src/pty-shell/pty-control-ipc.ts +327 -15
  490. package/src/pty-shell/pty-special-keys.ts +42 -2
  491. package/src/pty-shell/registry.ts +25 -15
  492. package/src/pty-shell/stale-install-check.ts +99 -0
  493. package/src/pty-shell/tui-run-state.ts +249 -3
  494. package/src/release-loop/auto-start.ts +20 -2
  495. package/src/release-loop/checklist.ts +126 -9
  496. package/src/release-loop/feature-store.ts +9 -9
  497. package/src/release-loop/landed-but-yellow.ts +93 -12
  498. package/src/release-loop/rehearsal.ts +36 -20
  499. package/src/repl/index.ts +27 -1
  500. package/src/seat-loop/knowhow-lint.ts +1 -1
  501. package/src/seat-loop/seat-report.ts +1 -0
  502. package/src/self-dev/ask-launch-flow.ts +86 -10
  503. package/src/self-dev/decompose-proposal.ts +183 -20
  504. package/src/self-dev/decompose.ts +61 -0
  505. package/src/self-dev/dev-cli.ts +37 -3
  506. package/src/self-dev/dev-pipeline.ts +37 -9
  507. package/src/self-dev/draft-sweep.ts +241 -16
  508. package/src/self-dev/draft-triage-rules.ts +50 -5
  509. package/src/self-dev/glm-credential-guard.ts +77 -0
  510. package/src/self-dev/graph-journey-nodes.ts +1 -0
  511. package/src/self-dev/graph-journey-route.ts +12 -2
  512. package/src/self-dev/orchestrate-cli.ts +68 -3
  513. package/src/self-dev/orchestrate.ts +116 -17
  514. package/src/self-dev/review-provider-fallback.ts +45 -7
  515. package/src/self-dev/run-supervisor.ts +55 -2
  516. package/src/self-dev/run-types.ts +1 -1
  517. package/src/self-dev/shard-plan.ts +48 -0
  518. package/src/self-dev/shard-run.ts +50 -3
  519. package/src/self-health/autoheal.ts +153 -0
  520. package/src/self-health/signatures.ts +6 -0
  521. package/src/self-implement/device-first.ts +116 -0
  522. package/src/self-implement/doctor-visit.ts +322 -1
  523. package/src/self-implement/evidence-command-budget.ts +80 -0
  524. package/src/self-implement/gate-artifacts.ts +6 -2
  525. package/src/self-implement/gate-baseline.ts +64 -1
  526. package/src/self-implement/gate-cli.ts +93 -9
  527. package/src/self-implement/gate-remote.ts +28 -4
  528. package/src/self-implement/gate-scope.ts +13 -1
  529. package/src/self-implement/goal-author-cli.ts +1 -0
  530. package/src/self-implement/goal-author-decompose.ts +265 -4
  531. package/src/self-implement/goal-author.ts +366 -26
  532. package/src/self-implement/goal-elaborate.ts +176 -22
  533. package/src/self-implement/goal-refine.ts +3 -0
  534. package/src/self-implement/goal-supervisor-fill.ts +75 -26
  535. package/src/self-implement/graph-variant.ts +40 -6
  536. package/src/self-implement/headless-elanous-driver.ts +19 -3
  537. package/src/self-implement/importer-test-index.ts +215 -2
  538. package/src/self-implement/knowhow-pack.ts +5 -5
  539. package/src/self-implement/off-diff-evidence.ts +58 -6
  540. package/src/self-implement/orchestrator.ts +536 -42
  541. package/src/self-implement/pod-warm-pool-profile.ts +1 -0
  542. package/src/self-implement/pwa-import-graph.ts +1 -1
  543. package/src/self-implement/review-round-brief.ts +300 -0
  544. package/src/self-implement/rework-policy.ts +44 -0
  545. package/src/self-implement/run-ledger.ts +278 -6
  546. package/src/self-implement/run-stop.ts +1 -1
  547. package/src/self-implement/run-ttl.ts +38 -24
  548. package/src/self-implement/running-runs.ts +17 -3
  549. package/src/self-implement/scope-adjacency.ts +3 -1
  550. package/src/self-implement/seams.ts +72 -10
  551. package/src/self-implement/stop-autoheal.ts +1 -0
  552. package/src/self-implement/test-seams.ts +13 -1
  553. package/src/self-implement/train-reach-grouping.ts +8 -2
  554. package/src/self-implement/wiring-check.ts +358 -0
  555. package/src/session/chat.ts +7 -1
  556. package/src/session/id-prefix.ts +60 -0
  557. package/src/session/index.ts +4 -0
  558. package/src/session-runtime/index.ts +9 -1
  559. package/src/shell-env-bootstrap.ts +11 -4
  560. package/src/skills/runner.ts +4 -2
  561. package/src/skills/tools/agent.ts +290 -48
  562. package/src/skills/tools/pty.ts +5 -0
  563. package/src/skills/tools/schedule-wakeup.ts +23 -4
  564. package/src/skills/tools/shell.ts +61 -6
  565. package/src/skills/tools/terminal-modal-inject.ts +1 -2
  566. package/src/skills/tools/terminal-modal.ts +10 -8
  567. package/src/skills/url-router.ts +41 -1
  568. package/src/smart-wizard/run.ts +66 -0
  569. package/src/status/bar.ts +34 -6
  570. package/src/status/metrics.ts +12 -1
  571. package/src/status/status-provider-info.ts +37 -6
  572. package/src/steward/rejudge-tick.ts +22 -7
  573. package/src/steward/resync-tick.ts +31 -4
  574. package/src/steward/ta-repair-event-collect.ts +17 -6
  575. package/src/task-agent/actions.ts +202 -5
  576. package/src/task-agent/boundary-proof-mapping.ts +12 -0
  577. package/src/task-agent/boundary-proof-producer.ts +147 -0
  578. package/src/task-agent/completion-evidence.ts +2 -1
  579. package/src/task-agent/green-close-recipe.ts +239 -0
  580. package/src/task-agent/green-sweep.ts +64 -10
  581. package/src/task-agent/land-queue.ts +21 -8
  582. package/src/task-agent/live-moves.ts +70 -9
  583. package/src/task-agent/map-host-adapter.ts +134 -0
  584. package/src/task-agent/map-reduce.ts +158 -0
  585. package/src/task-agent/map-run-ledger.ts +226 -0
  586. package/src/task-agent/mission.ts +30 -0
  587. package/src/task-agent/parent-host.ts +21 -11
  588. package/src/task-agent/rejudge-on-head.ts +87 -5
  589. package/src/task-agent/repair-cycle.ts +179 -32
  590. package/src/task-agent/repair-next-move.ts +42 -4
  591. package/src/task-agent/repair-outcomes.ts +29 -0
  592. package/src/task-agent/repair-worker.ts +14 -4
  593. package/src/task-agent/review-prior-fix.ts +12 -2
  594. package/src/task-agent/shadow.ts +229 -8
  595. package/src/task-agent/task-card-prefix.ts +2 -0
  596. package/src/task-agent/task-hand.ts +630 -30
  597. package/src/task-agent/task-parent-role-llm.ts +45 -0
  598. package/src/task-agent/tier-split.ts +80 -10
  599. package/src/task-agent/work-order.ts +53 -2
  600. package/src/task-orchestrator/dispatcher.ts +46 -1
  601. package/src/task-orchestrator/generator-prompt.ts +6 -0
  602. package/src/task-orchestrator/surfaces/pod-account-broker.ts +9 -0
  603. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
  604. package/src/task-orchestrator/surfaces/pod-child-reuse.ts +146 -0
  605. package/src/task-orchestrator/surfaces/pod-command-job.ts +86 -5
  606. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +215 -3
  607. package/src/task-orchestrator/surfaces/pod-launch-history.ts +133 -0
  608. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +8 -6
  609. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +97 -0
  610. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +11 -0
  611. package/src/task-orchestrator/surfaces/pod-pool.ts +126 -8
  612. package/src/task-orchestrator/surfaces/pod-provenance.ts +8 -2
  613. package/src/task-orchestrator/surfaces/pod-repair-commit.ts +18 -3
  614. package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +16 -0
  615. package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +224 -0
  616. package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +201 -0
  617. package/src/task-orchestrator/surfaces/pod-si-warm-provider.ts +444 -0
  618. package/src/task-orchestrator/surfaces/pod-si-warm-runner.ts +387 -0
  619. package/src/task-orchestrator/surfaces/pod-si-warm-transport.ts +157 -0
  620. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +4 -0
  621. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +491 -36
  622. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +3 -0
  623. package/src/task-orchestrator/surfaces/self-implement-pod.ts +612 -54
  624. package/src/task-orchestrator/surfaces/self-implement.ts +46 -2
  625. package/src/testing/linux-env.ts +177 -0
  626. package/src/tool-runtime/index.ts +15 -2
  627. package/src/tool-runtime/mcp-proxy-bootstrap.ts +29 -0
  628. package/src/tty-foreground.ts +155 -0
  629. package/src/tui/chat/goal-interim.ts +177 -0
  630. package/src/tui/chat/log-view.ts +205 -18
  631. package/src/tui/chat/self-health-status.ts +232 -0
  632. package/src/tui/entry.ts +623 -80
  633. package/src/tui/input/at-picker.ts +55 -0
  634. package/src/tui/input/composer.ts +8 -1
  635. package/src/tui/slash/compact-command.ts +7 -0
  636. package/src/tui/slash/compact.ts +1 -7
  637. package/src/tui/slash/resume.ts +98 -0
  638. package/src/tui/slash/signals.ts +114 -0
  639. package/src/tui/slash/term-next.ts +71 -0
  640. package/src/tui-client/local-chat-turn.ts +49 -5
  641. package/src/tui-client/next-dashboard-runtime.ts +8 -0
  642. package/src/tui-client/next-loop-command.ts +70 -0
  643. package/src/tui-client/next-wakeup.ts +191 -0
  644. package/src/tui.ts +37 -0
  645. package/src/ui/modal-adapter.ts +2 -2
  646. package/src/user-config.ts +19 -1
  647. package/src/ux-sim/boot-daemon-tools-pty-shell-device.ts +136 -0
  648. package/src/ux-sim/classic-tui-seat-scenarios.ts +670 -0
  649. package/src/ux-sim/cli-config-audit-device.ts +121 -0
  650. package/src/ux-sim/dashboard-event-loop-stall-monitor-device.ts +238 -0
  651. package/src/ux-sim/event-monkey.ts +3 -2
  652. package/src/ux-sim/fake-bin.ts +45 -0
  653. package/src/ux-sim/fake-gh.ts +174 -0
  654. package/src/ux-sim/fake-herdr.ts +238 -0
  655. package/src/ux-sim/fake-kubectl.ts +142 -0
  656. package/src/ux-sim/fake-llm.ts +116 -6
  657. package/src/ux-sim/fault-glm-no-credential.ts +116 -0
  658. package/src/ux-sim/fault-nextmove-dispatch.ts +102 -1
  659. package/src/ux-sim/fault-scope-escape-lift.ts +33 -6
  660. package/src/ux-sim/fault-tui-400-autoheal.ts +297 -0
  661. package/src/ux-sim/goal-device.ts +619 -0
  662. package/src/ux-sim/in-process-cli.ts +22 -5
  663. package/src/ux-sim/mock-authoring-first-run-child.ts +35 -0
  664. package/src/ux-sim/mock-authoring-first-run.ts +97 -0
  665. package/src/ux-sim/mock-seat-pty-frame-child.ts +29 -0
  666. package/src/ux-sim/mock-seat-pty-frame.ts +297 -0
  667. package/src/ux-sim/mock-seat-pty-sequence-child.ts +21 -0
  668. package/src/ux-sim/mock-seat-pty-sequence.ts +206 -0
  669. package/src/ux-sim/next-tui-seat-scenarios.ts +749 -0
  670. package/src/ux-sim/onboarding-first-run-device.ts +269 -0
  671. package/src/ux-sim/pty-key-modifiers-device.ts +85 -0
  672. package/src/ux-sim/resume-picker-device.ts +173 -0
  673. package/src/ux-sim/session-id-seed.ts +60 -0
  674. package/src/ux-sim/skills-tools-shell-device.ts +156 -0
  675. package/src/ux-sim/subagent-tools-device.ts +81 -0
  676. package/src/ux-sim/task-agent-parent-host-device.ts +264 -0
  677. package/src/ux-sim/task-agent-rejudge-on-head-device.ts +114 -0
  678. package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +250 -0
  679. package/src/ux-sim/tty-foreground-steal-child.ts +21 -0
  680. package/src/version/packed-revision.json +1 -1
  681. package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +0 -41
  682. package/apps/pwa/out/_next/static/chunks/1674.2a65fd5668e1cc5f.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +0 -24
  684. package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +0 -74
  685. package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/1993-c0007b7ad0c0ef41.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +0 -148
  694. package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/2625.5d870aa999f33db1.js +0 -34
  696. package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +0 -77
  697. package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +0 -62
  699. package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +0 -90
  701. package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +0 -36
  705. package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/3677-4215f826f6aa68e5.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +0 -79
  708. package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/4346.8601952e73bf5803.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +0 -286
  712. package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +0 -24
  715. package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +0 -23
  724. package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +0 -94
  725. package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/56d372f2.681ab4ec4c998fae.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +0 -287
  729. package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +0 -8
  736. package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +0 -27
  739. package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +0 -93
  741. package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/6b89c8bd.24df687a820500b3.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +0 -149
  747. package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +0 -104
  748. package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/8219.baa715d1deee80e2.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +0 -226
  758. package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +0 -64
  760. package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/8637.47f5b35afcae5e6b.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +0 -1
  763. package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +0 -1
  764. package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +0 -1
  765. package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +0 -1
  766. package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +0 -115
  767. package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +0 -1
  768. package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +0 -1
  769. package/apps/pwa/out/_next/static/chunks/967-2aa39b2d04b4c690.js +0 -1
  770. package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +0 -1
  771. package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +0 -1
  772. package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +0 -1
  773. package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +0 -1
  774. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +0 -1
  775. package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +0 -1
  776. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +0 -1
  777. package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +0 -1
  778. package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +0 -1
  779. package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +0 -1
  780. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +0 -1
  781. package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +0 -1
  782. package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +0 -1
  783. package/apps/pwa/out/_next/static/chunks/app/control/page-406b47745b53f269.js +0 -1
  784. package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +0 -1
  785. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +0 -1
  786. package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +0 -1
  787. package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +0 -1
  788. package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +0 -1
  789. package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +0 -1
  790. package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +0 -1
  791. package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +0 -1
  792. package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +0 -1
  793. package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +0 -1
  794. package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +0 -1
  795. package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +0 -1
  796. package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +0 -1
  797. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +0 -1
  798. package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +0 -1
  799. package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +0 -1
  800. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +0 -1
  801. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +0 -1
  802. package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +0 -1
  803. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +0 -1
  804. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +0 -1
  805. package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +0 -1
  806. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +0 -1
  807. package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +0 -1
  808. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +0 -1
  809. package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +0 -1
  810. package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +0 -1
  811. package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +0 -1
  812. package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +0 -1
  813. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +0 -1
  814. package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +0 -1
  815. package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +0 -1
  816. package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +0 -1
  817. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +0 -1
  818. package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +0 -1
  819. package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +0 -1
  820. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +0 -1
  821. package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +0 -1
  822. package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +0 -1
  823. package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +0 -1
  824. package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +0 -1
  825. package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +0 -1
  826. package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +0 -1
  827. /package/apps/pwa/out/_next/static/{T1mBQqmkwKDLGtyisDCOd → 8kITumG9GAZ1LwT9POrAR}/_ssgManifest.js +0 -0
@@ -1,24 +1,53 @@
1
- /** Fenced warm-Pod lease bridge with one-worker-per-host preparation. The Job remains authoritative. */
1
+ /** Fenced warm-Pod lease bridge. The Job remains authoritative. */
2
2
  import { spawn, spawnSync } from 'node:child_process';
3
3
  import { isAbsolute, join } from 'node:path';
4
- import { mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
5
- import { tmpdir } from 'node:os';
4
+ import { constants, closeSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir, userInfo } from 'node:os';
6
+ import { dlopen, read, type Pointer } from 'bun:ffi';
7
+ import { createHash, randomUUID } from 'node:crypto';
6
8
  import { fileURLToPath } from 'node:url';
7
9
  import { debug } from '../../debug/log.js';
8
10
  import { getUserConfig } from '../../user-config.js';
9
- import { resolveWarmPoolProfile, resolveWarmPoolQuota, type WarmPoolTier } from '../../self-implement/pod-warm-pool-profile.js';
11
+ import { resolveWarmPoolProfile, resolveWarmPoolQuota, type WarmPoolProfile, type WarmPoolTier } from '../../self-implement/pod-warm-pool-profile.js';
10
12
  import { WarmPodTransport, probeWarmPodContext, warmWorkerPodManifest, type WarmPodSpec, type WarmPodTransportResult } from './pod-warm-worker-pod.js';
11
13
  import type { WarmWorkerCommand } from './pod-warm-worker-protocol.js';
12
14
  import { patchWarmLeaseMetadata, warmLeaseMetadata } from './pod-warm-provenance.js';
15
+ import type { LogStore } from '../../mss/logging/log-store.js';
13
16
 
14
- // Cold source install on node-b exceeded the old five-minute Ready wait (2026-10-20).
17
+ // Cold source install on node-b exceeded five minutes; recheck the pinned Pod UID in bounded intervals.
15
18
  const POD_READY_WAIT_SECONDS = 900;
19
+ const POD_READY_RECHECK_SECONDS = 300;
20
+ const POD_READY_RETRY_DELAY_MS = 1_000;
21
+
22
+ function throwIfPrepareAborted(signal?: AbortSignal): void {
23
+ if (!signal?.aborted) return;
24
+ const reason = signal.reason;
25
+ throw reason instanceof Error ? reason : new Error(reason === undefined ? 'warm prepare cancelled' : String(reason));
26
+ }
27
+
28
+ function waitForPrepareRetry(signal?: AbortSignal): Promise<void> {
29
+ throwIfPrepareAborted(signal);
30
+ return new Promise((resolve, reject) => {
31
+ const timer = setTimeout(() => {
32
+ signal?.removeEventListener('abort', abort);
33
+ resolve();
34
+ }, POD_READY_RETRY_DELAY_MS);
35
+ const abort = () => {
36
+ clearTimeout(timer);
37
+ signal?.removeEventListener('abort', abort);
38
+ try { throwIfPrepareAborted(signal); } catch (error) { reject(error); }
39
+ };
40
+ signal?.addEventListener('abort', abort, { once: true });
41
+ });
42
+ }
16
43
 
17
44
  export type WarmReplayInput = Omit<WarmWorkerCommand, 'leaseId' | 'generation' | 'workerId' | 'key'>;
18
45
  export interface WarmReplayResult { command: WarmWorkerCommand | null; transport: WarmPodTransportResult }
19
46
  export interface WarmWorkerCandidate { host: string; tier: WarmPoolTier; spec: WarmPodSpec }
20
47
  export interface WarmPodProviderOptions {
21
48
  candidates: readonly WarmWorkerCandidate[];
49
+ /** One pinned template per host/tier. Each expands to the measured profile quota. */
50
+ poolTemplates?: readonly WarmWorkerCandidate[];
22
51
  /** This address is resolved by the control-plane join/primary mechanism, never guessed from a Pod context. */
23
52
  controlUrl: string;
24
53
  memberToken: string;
@@ -31,6 +60,84 @@ export interface WarmPodProviderOptions {
31
60
  maxShadowMs?: number;
32
61
  now?: () => number;
33
62
  kubectl?: (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
63
+ /** Private directory for cross-process prepare leases; defaults to a per-user directory under tmpdir(). */
64
+ prepareLockDirectory?: string;
65
+ /** Cancels a prepare waiting for another process to finish the same host/worker slot. */
66
+ prepareAbortSignal?: AbortSignal;
67
+ /** Gate file this pool serves, used to bind cold prepare legs to their 1-file gate measurement key. */
68
+ pairingGateFile?: string;
69
+ }
70
+
71
+ export interface WarmPoolReconcileResult {
72
+ profile: string;
73
+ target: number;
74
+ ready: number;
75
+ failed: readonly string[];
76
+ }
77
+
78
+ /** One leg of a read-only warm/cold prepare-time measurement pair, bound to one 1-file gate key. */
79
+ export interface WarmPreparePairLeg {
80
+ file: string;
81
+ commit: string;
82
+ host: string;
83
+ /** True when an already-standing Pod was reused; false when this leg created the Pod. */
84
+ reuse: boolean;
85
+ prepareDurationMs: number;
86
+ totalDurationMs: number;
87
+ podUid: string;
88
+ success: boolean;
89
+ }
90
+ /** A read-only measurement row pairing the cold create and the warm reuse of the same 1-file gate. */
91
+ export interface WarmReuseTimePair {
92
+ gateKey: string;
93
+ file: string;
94
+ commit: string;
95
+ host: string;
96
+ cold: WarmPreparePairLeg | null;
97
+ warm: WarmPreparePairLeg | null;
98
+ }
99
+ /** The shared observation row each process emits (`pod.warm` / `warm-reuse-time-pair` in logs.db). */
100
+ export type WarmReuseTimeLegRow = WarmPreparePairLeg & { gateKey: string; leg: 'cold' | 'warm' };
101
+ const PAIR_CATEGORY = 'pod.warm';
102
+ const PAIR_EVENT = 'warm-reuse-time-pair';
103
+
104
+ function parseLegRow(value: unknown): WarmReuseTimeLegRow | null {
105
+ if (!value || typeof value !== 'object') return null;
106
+ const row = value as Record<string, unknown>;
107
+ const text = (key: string) => typeof row[key] === 'string';
108
+ const ms = (key: string) => typeof row[key] === 'number' && Number.isFinite(row[key] as number) && (row[key] as number) >= 0;
109
+ if (!(row.leg === 'cold' || row.leg === 'warm') || !text('gateKey') || !text('file') || !row.file
110
+ || !text('commit') || !text('host') || !text('podUid') || typeof row.reuse !== 'boolean'
111
+ || typeof row.success !== 'boolean' || !ms('prepareDurationMs') || !ms('totalDurationMs')) return null;
112
+ // The key must be the row's own file·commit·host; a mismatched key cannot join another gate.
113
+ if (row.gateKey !== `${row.file}/${row.commit}/${row.host}`) return null;
114
+ return row as unknown as WarmReuseTimeLegRow;
115
+ }
116
+
117
+ /** Pure read-only join: oldest→newest leg rows into one pair per 1-file gate key (newest leg wins). */
118
+ export function pairWarmReuseTimeLegs(rows: readonly unknown[]): WarmReuseTimePair[] {
119
+ const pairs = new Map<string, WarmReuseTimePair>();
120
+ for (const value of rows) {
121
+ const row = parseLegRow(value);
122
+ if (!row) continue;
123
+ const { gateKey, leg, ...measured } = row;
124
+ const pair = pairs.get(gateKey) ?? { gateKey, file: row.file, commit: row.commit, host: row.host, cold: null, warm: null };
125
+ pair[leg] = { file: measured.file, commit: measured.commit, host: measured.host, reuse: measured.reuse,
126
+ prepareDurationMs: measured.prepareDurationMs, totalDurationMs: measured.totalDurationMs,
127
+ podUid: measured.podUid, success: measured.success };
128
+ pairs.set(gateKey, pair);
129
+ }
130
+ return [...pairs.values()];
131
+ }
132
+
133
+ /** Read-only measurement rows from the logs.db both the cold CLI and the warm gate process write. */
134
+ export function readWarmReuseTimePairs(store: Pick<LogStore, 'queryAll'>, opts: { sinceMs?: number } = {}): WarmReuseTimePair[] {
135
+ const rows = store.queryAll({ exactCategories: [PAIR_CATEGORY], events: [PAIR_EVENT],
136
+ ...(opts.sinceMs !== undefined ? { sinceMs: opts.sinceMs } : {}) });
137
+ // queryAll is newest-first; replay oldest-first so the newest leg per key wins.
138
+ return pairWarmReuseTimeLegs(rows.slice().reverse().map(row => {
139
+ try { return row.data ? JSON.parse(row.data) : null; } catch { return null; }
140
+ }));
34
141
  }
35
142
 
36
143
  const fallback = (reason: 'no-candidate' | 'lease-unavailable'): WarmReplayResult =>
@@ -42,19 +149,121 @@ const token = (value: string | undefined): value is string => typeof value === '
42
149
  const workerId = (value: unknown): value is string => typeof value === 'string'
43
150
  && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,95}$/.test(value);
44
151
 
152
+ function checkedPrivateDirectory(path: string): void {
153
+ try { mkdirSync(path, { mode: 0o700 }); } catch (error) {
154
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
155
+ }
156
+ const info = lstatSync(path);
157
+ if (!info.isDirectory() || info.isSymbolicLink() || (info.mode & 0o077) !== 0
158
+ || (typeof process.getuid === 'function' && info.uid !== process.getuid()))
159
+ throw new Error('unsafe warm prepare lock directory');
160
+ }
161
+
162
+ const prepareLockLib = process.platform === 'linux' || process.platform === 'darwin'
163
+ ? dlopen(process.platform === 'linux' ? 'libc.so.6' : 'libSystem.B.dylib', {
164
+ flock: { args: ['int', 'int'], returns: 'int' },
165
+ [process.platform === 'linux' ? '__errno_location' : '__error']: { args: [], returns: 'ptr' },
166
+ }) : null;
167
+ const prepareFlock = prepareLockLib?.symbols.flock ?? null;
168
+ const prepareErrnoLocation = prepareLockLib?.symbols[process.platform === 'linux' ? '__errno_location' : '__error'];
169
+ const readPrepareErrno = (): number => (prepareErrnoLocation ? read.i32(prepareErrnoLocation() as Pointer) : -1);
170
+ const LOCK_EX = 2;
171
+ const LOCK_NB = 4;
172
+ const LOCK_UN = 8;
173
+ const PREPARE_LOCK_WAIT_MS = (POD_READY_WAIT_SECONDS * 1_000) + 30_000;
174
+
175
+ async function withPrepareLease<T>(candidate: WarmWorkerCandidate, directory: string | undefined,
176
+ work: () => Promise<T>, signal?: AbortSignal): Promise<T | null> {
177
+ const uid = typeof process.getuid === 'function' ? process.getuid() : userInfo().uid;
178
+ const root = directory ?? join(tmpdir(), `elanous-warm-prepare-${uid}`);
179
+ const identity = createHash('sha256').update(`${candidate.host}/${candidate.spec.workerId}`).digest('hex');
180
+ let fd: number | undefined;
181
+ let acquired = false;
182
+ try {
183
+ checkedPrivateDirectory(root);
184
+ const lock = join(root, `${identity}.flock`);
185
+ fd = openSync(lock, constants.O_CREAT | constants.O_RDWR | (constants.O_NOFOLLOW ?? 0), 0o600);
186
+ const info = lstatSync(lock);
187
+ if (!info.isFile() || info.isSymbolicLink() || info.uid !== uid || (info.mode & 0o077) !== 0)
188
+ throw new Error('unsafe warm prepare lock file');
189
+ if (!prepareFlock) throw new Error('warm prepare OS locks are unavailable on this platform');
190
+ const lockDeadline = performance.now() + PREPARE_LOCK_WAIT_MS;
191
+ while (!acquired) {
192
+ if (signal?.aborted) throw new Error('warm prepare lock wait cancelled');
193
+ if (prepareFlock(fd, LOCK_EX | LOCK_NB) === 0) { acquired = true; break; }
194
+ const errno = readPrepareErrno();
195
+ // EWOULDBLOCK/EAGAIN: Linux 11, Darwin 35. EINTR is safe to retry.
196
+ if (errno !== 11 && errno !== 35 && errno !== 4)
197
+ throw new Error(`warm prepare flock failed (errno ${errno})`);
198
+ if (performance.now() >= lockDeadline) throw new Error('warm prepare lock wait timed out');
199
+ await new Promise<void>((resolve, reject) => {
200
+ const timer = setTimeout(done, Math.min(20, Math.max(1, lockDeadline - performance.now())));
201
+ const abort = () => { clearTimeout(timer); signal?.removeEventListener('abort', abort); reject(new Error('warm prepare lock wait cancelled')); };
202
+ function done() { signal?.removeEventListener('abort', abort); resolve(); }
203
+ signal?.addEventListener('abort', abort, { once: true });
204
+ });
205
+ }
206
+ return await work();
207
+ } catch (error) {
208
+ debug.log('pod.warm', 'prepare-lease-failed', { host: candidate.host,
209
+ workerId: candidate.spec.workerId, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
210
+ throw error;
211
+ } finally {
212
+ if (acquired && fd !== undefined) prepareFlock?.(fd, LOCK_UN);
213
+ if (fd !== undefined) closeSync(fd);
214
+ }
215
+ }
216
+
45
217
  export class WarmPodProvider {
46
218
  private readonly fetcher: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
47
219
  private readonly now: () => number;
220
+ private readonly candidates: readonly WarmWorkerCandidate[];
221
+ private readonly profile: WarmPoolProfile;
222
+ /** This process's own legs, echoed on CLI stdout. Pairing never reads this — see readWarmReuseTimePairs. */
223
+ private readonly legs: WarmReuseTimeLegRow[] = [];
224
+ /** One terminal transition per fenced lease, including concurrent callers. Failed transitions remain retryable. */
225
+ private readonly terminalLeases = new Map<string, Promise<'sanitized' | 'quarantined'>>();
226
+ private readonly standingWorkers = new Map<string, Promise<boolean>>();
48
227
  constructor(private readonly options: WarmPodProviderOptions) {
49
228
  const url = new URL(options.controlUrl);
50
229
  if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash
51
230
  || url.pathname !== '/' || !token(options.memberToken)) throw new Error('invalid warm control address');
52
- if (options.candidates.some(candidate => !workerId(candidate.spec.workerId)
53
- || !candidate.host.trim() || resolveWarmPoolQuota(resolveWarmPoolProfile(options.profileConfig ?? getUserConfig()), candidate.host, candidate.tier) < 1
231
+ const profile = resolveWarmPoolProfile(options.profileConfig ?? getUserConfig());
232
+ this.profile = profile;
233
+ const templates = options.poolTemplates ?? [];
234
+ if (new Set(templates.map(candidate => `${candidate.host}/${candidate.tier}`)).size !== templates.length)
235
+ throw new Error('duplicate warm pool template');
236
+ const expanded = templates.flatMap(candidate => {
237
+ const quota = resolveWarmPoolQuota(profile, candidate.host, candidate.tier);
238
+ if (!quota || !candidate.spec.nodeName) throw new Error('unmeasured warm pool template');
239
+ return Array.from({ length: quota }, (_, index): WarmWorkerCandidate => ({
240
+ host: candidate.host, tier: candidate.tier, spec: {
241
+ ...candidate.spec,
242
+ name: `warm-${candidate.host}-${candidate.tier}-${index + 1}`,
243
+ // The template worker ID is a generation token. Rotate it when a slot is quarantined.
244
+ workerId: `${candidate.spec.workerId}-${candidate.tier}-${index + 1}`,
245
+ },
246
+ }));
247
+ });
248
+ this.candidates = [...options.candidates, ...expanded];
249
+ if (this.candidates.some(candidate => !workerId(candidate.spec.workerId)
250
+ || !candidate.host.trim() || resolveWarmPoolQuota(profile, candidate.host, candidate.tier) < 1
54
251
  || candidate.spec.memoryLimit !== (candidate.tier === 'lite' ? '2Gi' : '16Gi')
55
252
  || !warmWorkerPodManifest(candidate.spec))) throw new Error('invalid warm candidate');
56
- if (new Set(options.candidates.map(candidate => candidate.spec.workerId)).size !== options.candidates.length)
253
+ if (new Set(this.candidates.map(candidate => candidate.spec.workerId)).size !== this.candidates.length)
57
254
  throw new Error('duplicate warm worker ID');
255
+ const names = new Set<string>();
256
+ const counts = new Map<string, number>();
257
+ for (const candidate of this.candidates) {
258
+ const identity = `${candidate.spec.kubectlContext}/${candidate.spec.namespace}/${candidate.spec.name}`;
259
+ if (names.has(identity)) throw new Error('duplicate warm Pod name');
260
+ names.add(identity);
261
+ const slot = `${candidate.host}/${candidate.tier}`;
262
+ const count = (counts.get(slot) ?? 0) + 1;
263
+ if (count > resolveWarmPoolQuota(profile, candidate.host, candidate.tier))
264
+ throw new Error('warm candidate quota exceeded');
265
+ counts.set(slot, count);
266
+ }
58
267
  this.fetcher = options.fetch ?? fetch;
59
268
  this.now = options.now ?? Date.now;
60
269
  }
@@ -118,14 +327,61 @@ export class WarmPodProvider {
118
327
  } finally { rmSync(directory, { recursive: true, force: true }); }
119
328
  }
120
329
 
121
- /** Prepare at most one standing worker per host. A foreign or replaced Pod is never deleted. */
330
+ /** Emit one measurement leg into the shared logs.db row the other process's leg joins. */
331
+ private recordPair(leg: 'cold' | 'warm', file: string, commit: string, host: string,
332
+ row: Omit<WarmPreparePairLeg, 'file' | 'commit' | 'host'>): void {
333
+ const legRow: WarmReuseTimeLegRow = { gateKey: `${file}/${commit}/${host}`, leg, file, commit, host, ...row };
334
+ this.legs.push(legRow);
335
+ debug.log(PAIR_CATEGORY, PAIR_EVENT, legRow, { level: 'info' });
336
+ }
337
+
338
+ /** The legs this process emitted (stdout echo only — pairing is readWarmReuseTimePairs over logs.db). */
339
+ warmReuseTimeLegs(): readonly WarmReuseTimeLegRow[] { return [...this.legs]; }
340
+
341
+ /** Prepare one named slot. A foreign or replaced Pod is never deleted. */
122
342
  async ensureStandingWorker(candidate: WarmWorkerCandidate): Promise<boolean> {
123
- const configured = this.options.candidates.find(row => row.host === candidate.host
343
+ const configured = this.candidates.find(row => row.host === candidate.host
344
+ && row.spec.workerId === candidate.spec.workerId);
345
+ if (!configured) return false;
346
+ const key = `${configured.host}/${configured.spec.workerId}`;
347
+ const inFlight = this.standingWorkers.get(key);
348
+ if (inFlight) return inFlight;
349
+ const preparation = withPrepareLease(configured, this.options.prepareLockDirectory,
350
+ () => this.prepareStandingWorker(configured, this.options.prepareAbortSignal), this.options.prepareAbortSignal)
351
+ .then(result => result === true);
352
+ this.standingWorkers.set(key, preparation);
353
+ try { return await preparation; }
354
+ finally { if (this.standingWorkers.get(key) === preparation) this.standingWorkers.delete(key); }
355
+ }
356
+
357
+ private async prepareStandingWorker(candidate: WarmWorkerCandidate, signal?: AbortSignal): Promise<boolean> {
358
+ throwIfPrepareAborted(signal);
359
+ const startedAt = this.now();
360
+ const outcome: { preexisting: boolean; podUid: string | null } = { preexisting: false, podUid: null };
361
+ const ok = await this.coldPrepare(candidate, outcome, signal);
362
+ const totalDurationMs = this.now() - startedAt;
363
+ const gateFile = this.options.pairingGateFile;
364
+ // An unset gate file skips the measurement row entirely instead of recording an unpaired one.
365
+ if (gateFile) this.recordPair('cold', gateFile, candidate.spec.cutSha, candidate.host,
366
+ { reuse: outcome.preexisting, prepareDurationMs: outcome.preexisting ? 0 : totalDurationMs,
367
+ totalDurationMs, podUid: outcome.podUid ?? '', success: ok });
368
+ return ok;
369
+ }
370
+
371
+ private async coldPrepare(candidate: WarmWorkerCandidate,
372
+ outcome: { preexisting: boolean; podUid: string | null }, signal?: AbortSignal): Promise<boolean> {
373
+ const configured = this.candidates.find(row => row.host === candidate.host
124
374
  && row.spec.workerId === candidate.spec.workerId && row.spec.cutSha === candidate.spec.cutSha
125
375
  && keyEquals(row.spec.key, candidate.spec.key));
126
- // A single API name per host makes concurrent creates contend on Kubernetes object identity.
127
- if (!configured || !configured.spec.nodeName || configured.spec.name !== `warm-${configured.host}`
376
+ // Legacy slot warm-HOST and numbered profile slots can coexist during migration.
377
+ const prefix = configured ? `warm-${configured.host}` : '';
378
+ const suffix = configured?.spec.name.startsWith(`${prefix}-`)
379
+ ? configured.spec.name.slice(prefix.length + 1) : '';
380
+ if (!configured || !configured.spec.nodeName
381
+ || (configured.spec.name !== prefix && !/^[1-9][0-9]*$/.test(suffix)
382
+ && !/^(lite|standard)-[1-9][0-9]*$/.test(suffix))
128
383
  || candidate.spec.name !== configured.spec.name || candidate.spec.nodeName !== configured.spec.nodeName) return false;
384
+ throwIfPrepareAborted(signal);
129
385
  const { spec, host } = configured;
130
386
  const nodeName = spec.nodeName;
131
387
  if (!nodeName) return false;
@@ -206,7 +462,19 @@ export class WarmPodProvider {
206
462
  if (hostPods.length > 1) return false;
207
463
  let pod = hostPods[0];
208
464
  const preexisting = !!pod;
465
+ outcome.preexisting = preexisting;
209
466
  if (pod && !matches(pod)) return false;
467
+ // Old generations still occupy memory even when their ledger rows are quarantined.
468
+ // Count physical warm Pods on the pinned node before admitting a new one.
469
+ const physical = listed.items.filter((row: any) => row?.metadata?.labels?.['elanous.kind'] === 'warm-worker'
470
+ && row?.spec?.nodeName === nodeName && !row?.metadata?.deletionTimestamp
471
+ && row?.status?.phase !== 'Succeeded' && row?.status?.phase !== 'Failed'
472
+ && row?.spec?.containers?.[0]?.resources?.limits?.memory === spec.memoryLimit).length;
473
+ if (!preexisting && physical >= resolveWarmPoolQuota(this.profile, host, configured.tier)) {
474
+ debug.log('pod.warm', 'pool-slot-refused', { host, tier: configured.tier,
475
+ workerId: spec.workerId, reason: 'physical-quota', physical }, { level: 'warn' });
476
+ return false;
477
+ }
210
478
  const readLedger = async (): Promise<any[] | null> => {
211
479
  try {
212
480
  const query = new URLSearchParams({ kind: 'pod-worker-lease', machine: host, name: spec.workerId });
@@ -218,15 +486,31 @@ export class WarmPodProvider {
218
486
  if (!pod) {
219
487
  // A worker ID already bound to another UID must not leave a newly created, unregistered Pod.
220
488
  const before = await readLedger();
489
+ throwIfPrepareAborted(signal);
221
490
  if (!before || before.length !== 0) return false;
222
491
  pod = parse(kubectl(['create', '-f', '-', '-o', 'json'], JSON.stringify(manifest)));
223
492
  if (!matches(pod)) return false;
224
493
  }
225
494
  const uid = pod.metadata.uid as string;
226
- if (kubectl(['wait', `pod/${spec.name}`, '--for=condition=Ready', `--timeout=${POD_READY_WAIT_SECONDS}s`]).status !== 0) return false;
227
- const ready = parse(kubectl(['get', `pod/${spec.name}`, '-o', 'json']));
228
- if (!matches(ready) || ready.metadata.uid !== uid || ready.status?.phase !== 'Running'
229
- || !ready.status?.conditions?.some((row: any) => row.type === 'Ready' && row.status === 'True')) return false;
495
+ outcome.podUid = uid;
496
+ const ready = (value: any) => matches(value) && value.metadata.uid === uid
497
+ && value.status?.phase === 'Running'
498
+ && value.status?.conditions?.some((row: any) => row.type === 'Ready' && row.status === 'True');
499
+ const deadline = this.now() + POD_READY_WAIT_SECONDS * 1000;
500
+ let isReady = false;
501
+ while (this.now() < deadline) {
502
+ const remainingSeconds = Math.ceil((deadline - this.now()) / 1000);
503
+ const waitSeconds = Math.min(POD_READY_RECHECK_SECONDS, remainingSeconds);
504
+ throwIfPrepareAborted(signal);
505
+ kubectl(['wait', `pod/${spec.name}`, '--for=condition=Ready', `--timeout=${waitSeconds}s`]);
506
+ throwIfPrepareAborted(signal);
507
+ const observed = parse(kubectl(['get', `pod/${spec.name}`, '-o', 'json']));
508
+ throwIfPrepareAborted(signal);
509
+ if (ready(observed)) { isReady = true; break; }
510
+ if (!matches(observed) || observed.metadata.uid !== uid) return false;
511
+ if (this.now() < deadline) await waitForPrepareRetry(signal);
512
+ }
513
+ if (!isReady) return false;
230
514
  // Re-read after create: a registration racing the preflight must fail before our POST.
231
515
  const rows = await readLedger();
232
516
  if (!rows) return false;
@@ -244,6 +528,7 @@ export class WarmPodProvider {
244
528
  // An unregistered Pod could have been created by anyone with namespace access.
245
529
  // Only this invocation's successful create response may become a new ledger row.
246
530
  if (preexisting) return false;
531
+ throwIfPrepareAborted(signal);
247
532
  let registered: { status: number; value: any };
248
533
  try {
249
534
  registered = await this.control('/v1/workers/pod', credential, {
@@ -258,27 +543,60 @@ export class WarmPodProvider {
258
543
  }
259
544
 
260
545
  async prepareWorker(workerId: string): Promise<boolean> {
261
- const candidate = this.options.candidates.find(row => row.spec.workerId === workerId);
546
+ const candidate = this.candidates.find(row => row.spec.workerId === workerId);
262
547
  if (!candidate) return false;
263
548
  return this.ensureStandingWorker(candidate);
264
549
  }
265
550
 
551
+ /** One bounded reconciliation pass. Failed slots stay unavailable until a fresh worker ID is configured. */
552
+ async reconcilePool(): Promise<WarmPoolReconcileResult> {
553
+ const failed: string[] = [];
554
+ let ready = 0;
555
+ for (const candidate of this.candidates) {
556
+ let prepared = false;
557
+ try { prepared = await this.ensureStandingWorker(candidate); }
558
+ catch { /* Uncertain state is never counted as ready. */ }
559
+ if (prepared) ready++;
560
+ else failed.push(candidate.spec.workerId);
561
+ debug.log('pod.warm', 'pool-slot', { profile: this.profile,
562
+ host: candidate.host, tier: candidate.tier, workerId: candidate.spec.workerId,
563
+ pod: candidate.spec.name, ready: prepared }, { level: prepared ? 'info' : 'warn' });
564
+ }
565
+ const result = { profile: this.profile,
566
+ target: this.candidates.length, ready, failed };
567
+ debug.log('pod.warm', 'pool-reconcile', result, { level: failed.length ? 'warn' : 'info' });
568
+ return result;
569
+ }
570
+
266
571
  /** Acquire a matching idle worker before exec. Every uncertain result is fenced out of reuse. */
267
572
  async request(input: WarmReplayInput): Promise<WarmReplayResult> {
573
+ const requestedAt = this.now();
268
574
  if (input.protocol !== 1 || input.envProfile !== 'gate-v1' || !Array.isArray(input.files)
269
575
  || !input.files.length || !Number.isFinite(input.deadlineAt) || input.deadlineAt <= this.now()) return fallback('no-candidate');
270
- const candidate = this.options.candidates.find(row => row.spec.cutSha === input.cutSha);
271
- if (!candidate) return fallback('no-candidate');
576
+ const matches = this.candidates.filter(row => row.spec.cutSha === input.cutSha);
577
+ if (!matches.length) return fallback('no-candidate');
578
+ let lastFallback: WarmReplayResult = fallback('lease-unavailable');
579
+ // The control plane chooses an idle worker within a host/key. Probe each distinct
580
+ // host/key once so a busy first host cannot strand an idle second pool.
581
+ const tried = new Set<string>();
582
+ for (const candidate of matches) {
272
583
  const { spec, host } = candidate;
584
+ const poolKey = `${host}/${spec.key.context}/${spec.key.imageDigest}/${spec.key.lockHash}/${spec.key.architecture}`;
585
+ if (tried.has(poolKey)) continue;
586
+ tried.add(poolKey);
273
587
  const contextFailure = probeWarmPodContext(spec.kubectlContext, this.options.kubectl);
274
- if (contextFailure) return { command: null, transport: contextFailure };
588
+ if (contextFailure) { lastFallback = { command: null, transport: contextFailure }; continue; }
275
589
  let acquired: { status: number; value: any };
276
590
  try {
277
591
  acquired = await this.control('/v1/leases/pod-worker', this.options.memberToken, {
278
592
  machine: host, key: spec.key, runId: input.runId, attemptId: input.attemptId,
279
593
  ttlMs: this.options.leaseTtlMs ?? 30 * 60_000,
280
594
  });
281
- } catch { return fallback('lease-unavailable'); }
595
+ } catch { lastFallback = fallback('lease-unavailable'); continue; }
596
+ if (acquired.status === 409 || acquired.status === 503) {
597
+ lastFallback = fallback('lease-unavailable');
598
+ continue;
599
+ }
282
600
  if (acquired.status !== 200) return fallback('lease-unavailable');
283
601
  const row = acquired.value;
284
602
  const leasedId = row?.attrs?.leaseId;
@@ -289,7 +607,7 @@ export class WarmPodProvider {
289
607
  || !Number.isSafeInteger(generation) || generation < 1) return fallback('lease-unavailable');
290
608
  // Acquisition selects by host/key, not by worker ID. Resolve the returned worker
291
609
  // before choosing its transport; an unknown worker must not be executed or fenced.
292
- const leasedCandidate = this.options.candidates.find(entry => entry.host === host
610
+ const leasedCandidate = this.candidates.find(entry => entry.host === host
293
611
  && entry.spec.workerId === row.name && entry.spec.cutSha === input.cutSha
294
612
  && keyEquals(entry.spec.key, spec.key));
295
613
  if (!leasedCandidate) {
@@ -305,6 +623,7 @@ export class WarmPodProvider {
305
623
  return fallback('lease-unavailable');
306
624
  }
307
625
  const command: WarmWorkerCommand = { ...input, leaseId: leasedId, generation, podUid,
626
+ podName: leasedCandidate.spec.name, podContext: leasedCandidate.spec.kubectlContext, image: leasedCandidate.spec.image,
308
627
  requestedAt: new Date(this.now()).toISOString(),
309
628
  workerId: row.name, key: leasedCandidate.spec.key };
310
629
  const admitted = patchWarmLeaseMetadata(leasedCandidate.spec, command, 'leased', this.options.kubectl);
@@ -320,6 +639,16 @@ export class WarmPodProvider {
320
639
  let transport: WarmPodTransportResult;
321
640
  try { transport = await this.runTransport(leasedCandidate.spec, command); }
322
641
  catch { transport = { status: 'fallback', reason: 'exec-failed', reply: null }; }
642
+ // Warm leg: the lease reused an already-standing Pod, so no prepare interval elapsed.
643
+ // Only a 1-file gate has a pairing key; a multi-file gate is skipped (with its reason), never keyed by files[0].
644
+ const gateFile = command.files.length === 1 ? command.files[0] : undefined;
645
+ if (command.files.length !== 1) debug.log(PAIR_CATEGORY, `${PAIR_EVENT}-skipped`, { reason: 'not-single-file-gate',
646
+ fileCount: command.files.length, commit: command.cutSha, host }, { level: 'info' });
647
+ if (gateFile) this.recordPair('warm', gateFile, command.cutSha, host, {
648
+ reuse: true, prepareDurationMs: 0,
649
+ totalDurationMs: this.now() - requestedAt, podUid,
650
+ success: transport.status === 'observed' && transport.reason === 'complete',
651
+ });
323
652
  if (transport.status !== 'observed' || transport.reason !== 'complete') {
324
653
  await this.quarantine(command, transport.reason);
325
654
  return { command, transport };
@@ -327,6 +656,8 @@ export class WarmPodProvider {
327
656
  // Keep the lease fenced until the gate has compared these bytes with its Job baseline.
328
657
  // A missing finalization leaves the worker leased and therefore unavailable.
329
658
  return { command, transport };
659
+ }
660
+ return lastFallback;
330
661
  }
331
662
 
332
663
  private async transition(command: WarmWorkerCommand, action: 'release' | 'sanitize' | 'quarantine',
@@ -340,7 +671,25 @@ export class WarmPodProvider {
340
671
  }
341
672
 
342
673
  async finalize(command: WarmWorkerCommand): Promise<void> {
343
- const candidate = this.options.candidates.find(row => row.spec.workerId === command.workerId);
674
+ const key = JSON.stringify([command.workerId, command.leaseId, command.generation, command.podUid]);
675
+ const prior = this.terminalLeases.get(key);
676
+ if (prior) {
677
+ const outcome = await prior;
678
+ debug.log('pod.provenance', 'lease-terminal-suppressed', { workerId: command.workerId,
679
+ leaseId: command.leaseId, generation: command.generation, requested: 'finalize', outcome });
680
+ if (outcome === 'quarantined') throw new Error('warm lease already quarantined');
681
+ return;
682
+ }
683
+ const terminal = this.finalizeOnce(command);
684
+ this.terminalLeases.set(key, terminal);
685
+ let outcome: 'sanitized' | 'quarantined';
686
+ try { outcome = await terminal; }
687
+ catch (error) { if (this.terminalLeases.get(key) === terminal) this.terminalLeases.delete(key); throw error; }
688
+ if (outcome === 'quarantined') throw new Error('warm sanitize unavailable');
689
+ }
690
+
691
+ private async finalizeOnce(command: WarmWorkerCommand): Promise<'sanitized' | 'quarantined'> {
692
+ const candidate = this.candidates.find(row => row.spec.workerId === command.workerId);
344
693
  if (!candidate) throw new Error('warm finalize candidate missing');
345
694
  const released = await this.transition(command, 'release');
346
695
  const machineToken = this.options.machineTokens?.[candidate.host];
@@ -350,20 +699,36 @@ export class WarmPodProvider {
350
699
  if (!cleared.ok) debug.log('pod.provenance', 'lease-patch-refused', { pod: candidate.spec.name,
351
700
  podUid: command.podUid, leaseId: command.leaseId, reason: cleared.reason }, { level: 'warn' });
352
701
  if (!cleared.ok || !token(machineToken) || !await this.transition(command, 'sanitize', machineToken)) {
353
- await this.quarantine(command, 'sanitize-unavailable');
354
- throw new Error('warm sanitize unavailable');
702
+ await this.quarantineOnce(command, 'sanitize-unavailable', true);
703
+ return 'quarantined';
355
704
  }
356
705
  debug.log('pod.provenance', 'lease-end', { pod: candidate.spec.name, podUid: command.podUid,
357
706
  leaseId: command.leaseId, runId: command.runId, attemptId: command.attemptId,
358
707
  releasedAt: new Date(this.now()).toISOString(), result: 'sanitized' });
708
+ return 'sanitized';
359
709
  }
360
710
 
361
711
  /** `leaseStarted=false` marks a refusal before `lease-start`, so start/end history stays paired. */
362
712
  async quarantine(command: WarmWorkerCommand, reason: string, leaseStarted = true): Promise<void> {
713
+ const key = JSON.stringify([command.workerId, command.leaseId, command.generation, command.podUid]);
714
+ const prior = this.terminalLeases.get(key);
715
+ if (prior) {
716
+ const outcome = await prior;
717
+ debug.log('pod.provenance', 'lease-terminal-suppressed', { workerId: command.workerId,
718
+ leaseId: command.leaseId, generation: command.generation, requested: 'quarantine', outcome });
719
+ return;
720
+ }
721
+ const terminal = this.quarantineOnce(command, reason, leaseStarted).then(() => 'quarantined' as const);
722
+ this.terminalLeases.set(key, terminal);
723
+ try { await terminal; }
724
+ catch (error) { if (this.terminalLeases.get(key) === terminal) this.terminalLeases.delete(key); throw error; }
725
+ }
726
+
727
+ private async quarantineOnce(command: WarmWorkerCommand, reason: string, leaseStarted: boolean): Promise<void> {
363
728
  if (!await this.transition(command, 'quarantine', this.options.memberToken, reason.slice(0, 120) || 'mismatch')) {
364
729
  throw new Error('warm quarantine unavailable');
365
730
  }
366
- const candidate = this.options.candidates.find(row => row.spec.workerId === command.workerId);
731
+ const candidate = this.candidates.find(row => row.spec.workerId === command.workerId);
367
732
  if (candidate) {
368
733
  const cleared = patchWarmLeaseMetadata(candidate.spec, command, 'quarantined', this.options.kubectl);
369
734
  if (!cleared.ok && reason !== 'provenance-unavailable') debug.log('pod.provenance', 'lease-patch-refused', { pod: candidate.spec.name,
@@ -381,11 +746,17 @@ let configuredProvider: WarmPodProvider | null = null;
381
746
  /** The trusted producer configures this once; an absent provider fails closed without creating a Pod. */
382
747
  export function configureWarmPodProvider(provider: WarmPodProvider | null): void { configuredProvider = provider; }
383
748
  /** Optional private producer config; a missing/unsafe file leaves the Job path untouched. */
384
- export function loadWarmPodProvider(path: string): WarmPodProvider {
749
+ export function loadWarmPodProvider(path: string, pairingGateFile?: string): WarmPodProvider {
385
750
  if (!isAbsolute(path) || (statSync(path).mode & 0o077) !== 0) throw new Error('warm provider config must be private');
386
751
  const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'));
387
752
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('invalid warm provider config');
388
- return new WarmPodProvider(parsed as WarmPodProviderOptions);
753
+ const options = parsed as WarmPodProviderOptions;
754
+ // The production entry points (--prepare/--reconcile/--serve) bind the 1-file gate key here.
755
+ if (pairingGateFile !== undefined) {
756
+ if (!pairingGateFile.trim()) throw new Error('warm provider pairing gate file must not be empty');
757
+ options.pairingGateFile = pairingGateFile;
758
+ }
759
+ return new WarmPodProvider(options);
389
760
  }
390
761
  function currentProvider(): WarmPodProvider | null {
391
762
  if (configuredProvider) return configuredProvider;
@@ -396,7 +767,13 @@ function currentProvider(): WarmPodProvider | null {
396
767
  return configuredProvider;
397
768
  }
398
769
  export async function requestWarmPodReplay(input: WarmReplayInput): Promise<WarmReplayResult> {
399
- return currentProvider() ? currentProvider()!.request(input) : fallback('no-candidate');
770
+ const provider = currentProvider();
771
+ const result = provider ? await provider.request(input) : fallback('no-candidate');
772
+ debug.log('pod.warm', 'request-selection', { runId: input.runId, attemptId: input.attemptId,
773
+ cutSha: input.cutSha, selected: result.command !== null,
774
+ workerId: result.command?.workerId ?? null, status: result.transport.status,
775
+ reason: result.transport.reason });
776
+ return result;
400
777
  }
401
778
  export async function quarantineWarmPodReplay(command: WarmWorkerCommand, reason: string): Promise<void> {
402
779
  const provider = currentProvider();
@@ -409,13 +786,91 @@ export async function finalizeWarmPodReplay(command: WarmWorkerCommand): Promise
409
786
  await provider.finalize(command);
410
787
  }
411
788
 
789
+
790
+ /** CLI 인자 — `--prepare <workerId> [gateFile]` · `--reconcile [gateFile]` · `--serve [gateFile]` · `--pairs`.
791
+ * gateFile 은 --prepare 에서만 셋째 칸, 나머지는 둘째 칸이다. `--pairs` 는 logs.db 읽기 전용(인자 없음). */
792
+ export function parseWarmPodProviderArgs(argv: readonly string[]): { action: '--prepare' | '--reconcile' | '--serve' | '--pairs'; workerId?: string; gateFile?: string } {
793
+ const usage = () => new Error('usage: --prepare <workerId> [gateFile] | --reconcile [gateFile] | --serve [gateFile]'
794
+ + ' with private provider config | --pairs');
795
+ if (argv[0] === '--pairs') {
796
+ if (argv.length !== 1) throw usage();
797
+ return { action: '--pairs' };
798
+ }
799
+ const [action, ...rest] = argv;
800
+ if (action === '--prepare') {
801
+ const [workerId, gateFile, extra] = rest;
802
+ if (!workerId || extra !== undefined || (gateFile !== undefined && !gateFile.trim())) throw usage();
803
+ return { action, workerId, ...(gateFile !== undefined ? { gateFile } : {}) };
804
+ }
805
+ if (action === '--reconcile' || action === '--serve') {
806
+ const [gateFile, extra] = rest;
807
+ if (extra !== undefined || (gateFile !== undefined && !gateFile.trim())) throw usage();
808
+ return { action, ...(gateFile !== undefined ? { gateFile } : {}) };
809
+ }
810
+ throw usage();
811
+ }
812
+
412
813
  if (import.meta.main) {
413
- const [action, workerId] = process.argv.slice(2);
814
+ const { action, workerId, gateFile } = parseWarmPodProviderArgs(process.argv.slice(2));
414
815
  const file = process.env.ELANOUS_WARM_POD_PROVIDER_CONFIG;
415
- if (action !== '--prepare' || !workerId) throw new Error('usage: --prepare <workerId> with private provider config');
416
- let prepared = false;
417
- try { if (file) prepared = await loadWarmPodProvider(file).prepareWorker(workerId); }
418
- catch { /* An invalid private config is a failed preparation, never a Pod mutation. */ }
419
- process.stdout.write(JSON.stringify({ workerId, prepared }) + '\n');
420
- if (!prepared) process.exitCode = 2;
816
+ // The 1-file gate key this pool serves, bound into every cold prepare leg of this process.
817
+ const load = () => (file ? loadWarmPodProvider(file, gateFile) : null);
818
+ if (action === '--pairs') {
819
+ // Read-only: join the cold CLI legs and the warm gate legs both processes wrote to logs.db.
820
+ try {
821
+ const storeMod = await import('../../mss/logging/log-store.js');
822
+ const dbPath = storeMod.logsDbPath();
823
+ // No logs.db yet means no leg was ever measured: an empty pair list, not a failed query.
824
+ if (!existsSync(dbPath)) process.stdout.write(JSON.stringify({ warmReuseTimePairs: [] }) + '\n');
825
+ else {
826
+ const store = storeMod.LogStore.openReadOnly(dbPath);
827
+ try { process.stdout.write(JSON.stringify({ warmReuseTimePairs: readWarmReuseTimePairs(store) }) + '\n'); }
828
+ finally { store.close(); }
829
+ }
830
+ } catch (error) {
831
+ process.stdout.write(JSON.stringify({ warmReuseTimePairs: null, reason: 'logs-unavailable', detail: String(error) }) + '\n');
832
+ process.exitCode = 2;
833
+ }
834
+ }
835
+ // The cold legs must reach the same logs.db the warm gate process (gate-node) writes; fail-open.
836
+ if (action !== '--pairs') try { await (await import('../../domains/standalone-log-sink.js')).registerStandaloneLogSink('pod-warm'); }
837
+ catch { /* the file trail stays the source of truth */ }
838
+ // This process's own legs, echoed for the caller; the pair itself is read with --pairs.
839
+ const emitPairs = (provider: WarmPodProvider) => {
840
+ const legs = provider.warmReuseTimeLegs();
841
+ if (legs.length) process.stdout.write(JSON.stringify({ warmReuseTimeLegs: legs }) + '\n');
842
+ };
843
+ if (action === '--serve') {
844
+ // Reloading the private config lets the owner rotate a quarantined slot to a new worker ID.
845
+ // The old ID remains fenced in the ledger; this loop never deletes a Pod or rewrites the ledger.
846
+ while (true) {
847
+ try {
848
+ const provider = load();
849
+ const result = provider ? await provider.reconcilePool() : null;
850
+ process.stdout.write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
851
+ if (provider) emitPairs(provider);
852
+ } catch {
853
+ debug.log('pod.warm', 'pool-reconcile-unavailable', { reason: 'config-unavailable' }, { level: 'warn' });
854
+ process.stdout.write(JSON.stringify({ target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
855
+ }
856
+ await Bun.sleep(60_000);
857
+ }
858
+ }
859
+ if (action === '--reconcile') {
860
+ let result: WarmPoolReconcileResult | null = null;
861
+ let provider: WarmPodProvider | null = null;
862
+ try { provider = load(); result = provider ? await provider.reconcilePool() : null; }
863
+ catch { /* An invalid private config is a failed pass, never a Pod mutation. */ }
864
+ process.stdout.write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
865
+ if (provider) emitPairs(provider);
866
+ if (!result || result.ready !== result.target) process.exitCode = 2;
867
+ } else if (action === '--prepare') {
868
+ let prepared = false;
869
+ let provider: WarmPodProvider | null = null;
870
+ try { provider = load(); prepared = provider ? await provider.prepareWorker(workerId!) : false; }
871
+ catch { /* An invalid private config is a failed preparation, never a Pod mutation. */ }
872
+ process.stdout.write(JSON.stringify({ workerId, prepared }) + '\n');
873
+ if (provider) emitPairs(provider);
874
+ if (!prepared) process.exitCode = 2;
875
+ }
421
876
  }