elanous 0.2.40 → 0.2.42

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 (685) hide show
  1. package/README.md +7 -7
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_buildManifest.js +1 -1
  5. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{4769.9fd582071301f382.js → 1063.2fb8a9dc55c731f2.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +36 -0
  8. package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{470.51a72a2348306455.js → 1262.e804feb8fd1acb8c.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{7219-15bdd9a80661af8e.js → 1455-530ca16c7e71baff.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/{1281.d81877e35b9068f4.js → 1487.2f67b79444062da6.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +32 -0
  16. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +286 -0
  22. package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +23 -0
  23. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +34 -0
  26. package/apps/pwa/out/_next/static/chunks/{1350.59f40e39e02d91b1.js → 2456.b6de0e1c05f28843.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +115 -0
  32. package/apps/pwa/out/_next/static/chunks/{5436.fdf6fa8ae3e9588d.js → 2974.595d1e29d040cb6a.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/{5383.bdfa5d886e39a826.js → 3207.a756c95e9ec8c3c8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +93 -0
  35. package/apps/pwa/out/_next/static/chunks/{2177.df8547f9d7215c4e.js → 3347.f846266b5e5f3ede.js} +3 -3
  36. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +41 -0
  37. package/apps/pwa/out/_next/static/chunks/{9184.45afbf44fec87574.js → 3482.a9ae9d2ad6a5bd97.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/3507.97e1977bc4d5fb0a.js +2 -0
  39. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +24 -0
  41. package/apps/pwa/out/_next/static/chunks/{8099.1cccf31c1baa7e1c.js → 3741.9cf76acaa2ee5298.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{4864a2ca.1c1c280a7772c5e5.js → 3d0154af.5331fec737ffe8c3.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4043.6b91203436c9c86b.js +309 -0
  48. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +77 -0
  49. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/{3330.7443183b7e6fdbdf.js → 4276.336d76d858fcdf1b.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +148 -0
  55. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +74 -0
  56. package/apps/pwa/out/_next/static/chunks/{3b355b3d.6f73c98bf6f9c749.js → 50061469.0326382241ef3b2d.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +149 -0
  58. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +27 -0
  60. package/apps/pwa/out/_next/static/chunks/{8d173e38.18a2f7b4e1848bdb.js → 53c73f84.373b6160adfa89a4.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{1537-6bb97b5214928ea9.js → 5431-72a1dbde59400691.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +287 -0
  70. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/{261.0d471e8aded799f3.js → 6131.348b7d6bdcd267ce.js} +3 -3
  73. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +90 -0
  74. package/apps/pwa/out/_next/static/chunks/{9175.2de5209a5ce6f68a.js → 6377.bc7df239edbff244.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{2226.2cf61ec781827219.js → 6632.0eac1c554f8f2ef3.js} +2 -2
  78. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +104 -0
  82. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +94 -0
  83. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/{b84cad00.594027ebe0531fe2.js → 73d2eb0e.4966ad55293c801a.js} +3 -3
  90. package/apps/pwa/out/_next/static/chunks/{6257.c0037b8949239f5e.js → 7411.9b4b8b747dc7b802.js} +2 -2
  91. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +24 -0
  92. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/{9189505a.980427ac048c8c60.js → 7a98bc80.fa935989e829f9c2.js} +1 -1
  98. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8138.6c10b2a356d52fca.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/{2566.beaffc3a5e1fcd2e.js → 8262.ddc03e06b733f184.js} +1 -1
  101. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +8 -0
  103. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +79 -0
  105. package/apps/pwa/out/_next/static/chunks/{8272-025653884f9645d2.js → 8546-6f03f8ea34dd61d9.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/{6051-0ae955eedb10adb9.js → 8673-bab689a55f7e13b9.js} +3 -3
  107. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/{4351.0f37a6db2b34d50b.js → 9249.976f1e69a9d80754.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{6749.0744ce6eb08d40e9.js → 9427.96aa912ddb5801e0.js} +16 -16
  119. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{7611.85f045bf232053ac.js → 9633.ce5bddc56784da72.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/{3746-f80a78f220bf37b5.js → 9656-374232558fd528d0.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +62 -0
  124. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83d27757e2b8060a.js → page-0e3d7b9421b79dac.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/term/{page-139d722de1dcd6c1.js → page-bf067887b3307423.js} +1 -1
  170. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{409c787b.e48c5bec8ec10456.js → b450776b.3db34d8d965f82d3.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{ca3702f8.2913182e73889fa2.js → e8543907.5b040a5f22a0b0e5.js} +7 -7
  179. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +1 -0
  185. package/apps/pwa/out/_next/static/css/{b9d8151e48998417.css → 62c858ad53854f47.css} +1 -1
  186. package/apps/pwa/out/approvals/index.html +1 -1
  187. package/apps/pwa/out/approvals/index.txt +22 -22
  188. package/apps/pwa/out/autopilot/index.html +1 -1
  189. package/apps/pwa/out/autopilot/index.txt +22 -22
  190. package/apps/pwa/out/board/index.html +1 -1
  191. package/apps/pwa/out/board/index.txt +22 -22
  192. package/apps/pwa/out/botlab/index.html +1 -1
  193. package/apps/pwa/out/botlab/index.txt +21 -21
  194. package/apps/pwa/out/bots/index.html +1 -1
  195. package/apps/pwa/out/bots/index.txt +21 -21
  196. package/apps/pwa/out/ceo/index.html +1 -1
  197. package/apps/pwa/out/ceo/index.txt +22 -22
  198. package/apps/pwa/out/chat/index.html +1 -1
  199. package/apps/pwa/out/chat/index.txt +21 -21
  200. package/apps/pwa/out/consult/index.html +1 -1
  201. package/apps/pwa/out/consult/index.txt +22 -22
  202. package/apps/pwa/out/control/index.html +1 -1
  203. package/apps/pwa/out/control/index.txt +22 -22
  204. package/apps/pwa/out/decisions/index.html +1 -1
  205. package/apps/pwa/out/decisions/index.txt +21 -21
  206. package/apps/pwa/out/design-check/index.html +1 -1
  207. package/apps/pwa/out/design-check/index.txt +22 -22
  208. package/apps/pwa/out/editor/index.html +1 -1
  209. package/apps/pwa/out/editor/index.txt +23 -24
  210. package/apps/pwa/out/exec/index.html +1 -1
  211. package/apps/pwa/out/exec/index.txt +22 -22
  212. package/apps/pwa/out/field/index.html +1 -1
  213. package/apps/pwa/out/field/index.txt +22 -22
  214. package/apps/pwa/out/healthz +1 -1
  215. package/apps/pwa/out/index.html +1 -1
  216. package/apps/pwa/out/index.txt +21 -21
  217. package/apps/pwa/out/inside/index.html +1 -1
  218. package/apps/pwa/out/inside/index.txt +22 -22
  219. package/apps/pwa/out/intake/index.html +1 -1
  220. package/apps/pwa/out/intake/index.txt +22 -22
  221. package/apps/pwa/out/live/index.html +1 -1
  222. package/apps/pwa/out/live/index.txt +23 -24
  223. package/apps/pwa/out/live-run/index.html +1 -1
  224. package/apps/pwa/out/live-run/index.txt +23 -24
  225. package/apps/pwa/out/loops/index.html +1 -1
  226. package/apps/pwa/out/loops/index.txt +21 -21
  227. package/apps/pwa/out/market/index.html +1 -1
  228. package/apps/pwa/out/market/index.txt +21 -21
  229. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  230. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  231. package/apps/pwa/out/morning/index.html +1 -1
  232. package/apps/pwa/out/morning/index.txt +22 -22
  233. package/apps/pwa/out/observatory/index.html +1 -1
  234. package/apps/pwa/out/observatory/index.txt +22 -22
  235. package/apps/pwa/out/ops/checklist/index.html +1 -1
  236. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  237. package/apps/pwa/out/ops/release/index.html +1 -1
  238. package/apps/pwa/out/ops/release/index.txt +21 -21
  239. package/apps/pwa/out/ops/seats/index.html +1 -1
  240. package/apps/pwa/out/ops/seats/index.txt +21 -21
  241. package/apps/pwa/out/outputs/index.html +1 -1
  242. package/apps/pwa/out/outputs/index.txt +21 -21
  243. package/apps/pwa/out/reflection/index.html +1 -1
  244. package/apps/pwa/out/reflection/index.txt +22 -22
  245. package/apps/pwa/out/scheduler/index.html +1 -1
  246. package/apps/pwa/out/scheduler/index.txt +22 -22
  247. package/apps/pwa/out/sessions/index.html +1 -1
  248. package/apps/pwa/out/sessions/index.txt +21 -21
  249. package/apps/pwa/out/settings/devices/index.html +1 -1
  250. package/apps/pwa/out/settings/devices/index.txt +22 -22
  251. package/apps/pwa/out/settings/index.html +1 -1
  252. package/apps/pwa/out/settings/index.txt +22 -22
  253. package/apps/pwa/out/setup/done/index.html +1 -1
  254. package/apps/pwa/out/setup/done/index.txt +22 -22
  255. package/apps/pwa/out/setup/index.html +1 -1
  256. package/apps/pwa/out/setup/index.txt +22 -22
  257. package/apps/pwa/out/share/index.html +1 -1
  258. package/apps/pwa/out/share/index.txt +22 -22
  259. package/apps/pwa/out/showroom/index.html +1 -1
  260. package/apps/pwa/out/showroom/index.txt +21 -21
  261. package/apps/pwa/out/tasks/index.html +1 -1
  262. package/apps/pwa/out/tasks/index.txt +22 -22
  263. package/apps/pwa/out/term/index.html +1 -1
  264. package/apps/pwa/out/term/index.txt +22 -22
  265. package/apps/pwa/out/today/index.html +1 -1
  266. package/apps/pwa/out/today/index.txt +22 -22
  267. package/apps/pwa/out/trace/index.html +1 -1
  268. package/apps/pwa/out/trace/index.txt +22 -22
  269. package/apps/pwa/out/vault/index.html +1 -1
  270. package/apps/pwa/out/vault/index.txt +22 -22
  271. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  272. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  273. package/apps/pwa/out/workflows/index.html +1 -1
  274. package/apps/pwa/out/workflows/index.txt +22 -22
  275. package/apps/pwa/out/workspace/index.html +1 -1
  276. package/apps/pwa/out/workspace/index.txt +22 -22
  277. package/apps/pwa/out/worktrees/index.html +1 -1
  278. package/apps/pwa/out/worktrees/index.txt +22 -22
  279. package/bin/elanous.mjs +6 -1
  280. package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
  281. package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
  282. package/docs/goal-context/04-mock-scenarios.md +13 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
  285. package/graphs/demo/persona-teacher/recipes.yaml +17 -0
  286. package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
  287. package/graphs/heal/heal-loop.yaml +1 -0
  288. package/graphs/overlays/stop-heal-ladder.yaml +136 -2
  289. package/graphs/release/release-loop.yaml +3 -1
  290. package/graphs/units/gate/gate.yaml +20 -0
  291. package/graphs/units/git/git.yaml +26 -0
  292. package/graphs/units/implement/implement.yaml +19 -0
  293. package/graphs/units/landing/landing.yaml +18 -0
  294. package/graphs/units/review/review.yaml +20 -0
  295. package/graphs/units/verbs/recipes.yaml +13 -10
  296. package/graphs/units/verbs/research.yaml +20 -0
  297. package/graphs/work/research-report.yaml +21 -3
  298. package/package.json +1 -1
  299. package/scripts/disk-steward.ts +46 -0
  300. package/scripts/fleet/node-setup.sh +50 -0
  301. package/scripts/graph-units/verbs/research.ts +55 -0
  302. package/scripts/heal/run-role.ts +99 -5
  303. package/scripts/land-global-checks.ts +6 -1
  304. package/scripts/lib/decompose-ab.ts +1 -0
  305. package/scripts/lib/fixtures/tui-regress/help-insufficient-commands.txt +15 -0
  306. package/scripts/lib/fixtures/tui-regress/help-live.txt +42 -0
  307. package/scripts/lib/fixtures/tui-regress/help-missing-keys.txt +21 -0
  308. package/scripts/lib/fixtures/tui-regress/help-original.txt +51 -0
  309. package/scripts/lib/fixtures/tui-regress/help-shifted-input.txt +26 -0
  310. package/scripts/lib/tui-regress-checks.ts +33 -13
  311. package/scripts/llm-cost-ledger.ts +179 -8
  312. package/scripts/measure-nl-routing-corpus.ts +10 -2
  313. package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
  314. package/scripts/public-copy-lint.ts +163 -19
  315. package/scripts/ratchets/train-side-invariants.sh +0 -1
  316. package/scripts/release-loop/gate-autoheal.ts +117 -0
  317. package/scripts/release-loop/gate-baseline-receipt.ts +55 -10
  318. package/scripts/release-loop/gate-diff.ts +22 -3
  319. package/scripts/release-loop/gate-image-pin.ts +81 -0
  320. package/scripts/release-loop/gate-node-sim.ts +207 -0
  321. package/scripts/release-loop/gate-node.ts +489 -62
  322. package/scripts/release-loop/gate-pod-image-sticky-plan.ts +99 -0
  323. package/scripts/release-loop/gate-radius-live.ts +97 -13
  324. package/scripts/release-loop/gate-shard-dispatch.ts +43 -0
  325. package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
  326. package/scripts/release-loop/gate-train-receipts.ts +122 -0
  327. package/scripts/release-loop/gate-triage-node.ts +70 -10
  328. package/scripts/release-loop/resume-release.ts +330 -23
  329. package/scripts/release-loop/traffic-node.ts +6 -3
  330. package/scripts/release-loop/unattended-release.ts +4 -2
  331. package/scripts/shadow-authoring-probe.ts +20 -0
  332. package/scripts/ux-sim.ts +37 -3
  333. package/scripts/warm-pool-provider-config.ts +263 -0
  334. package/skills/omni-crawl/SKILL.md +5 -2
  335. package/skills/omni-crawl/scripts/main.ts +84 -23
  336. package/src/acp/core-turn-bridge.ts +99 -4
  337. package/src/agent/registry.ts +2 -0
  338. package/src/agent/task-notification.ts +25 -5
  339. package/src/agent/types.ts +6 -0
  340. package/src/agent-mission/mission-cli.ts +24 -0
  341. package/src/agent-mission/mission-out.ts +595 -0
  342. package/src/agent-mission/tool-install.ts +88 -2
  343. package/src/agent-substrate/context-strategy.ts +4 -4
  344. package/src/agent-substrate/self-review-cli.ts +267 -40
  345. package/src/auto-research/research-turn.ts +133 -51
  346. package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
  347. package/src/autopilot/pr-manager.ts +38 -7
  348. package/src/autopilot/pty-control-loop.ts +2 -0
  349. package/src/boot/daemon-tools/self-implement.ts +46 -9
  350. package/src/budget/shadow-arms.ts +52 -0
  351. package/src/capture/tui-self-report.ts +22 -6
  352. package/src/chat/index.ts +1 -1
  353. package/src/chat/pickers/state.ts +7 -1
  354. package/src/cli/fixtures/review-loop-breaker/opwaiver-32646-1694ccc7a170.json +1 -0
  355. package/src/cli/fixtures/review-loop-breaker/opwaiver-32853-bd8f73595c83.json +1 -0
  356. package/src/cli/fixtures/review-loop-breaker/opwaiver-33097-1cc85900413c.json +1 -0
  357. package/src/cli/fixtures/review-loop-breaker/opwaiver-33106-972f31e4a3ca.json +1 -0
  358. package/src/cli/fixtures/review-loop-breaker/opwaiver-33113-3ab51f05968f.json +1 -0
  359. package/src/cli/fixtures/review-loop-breaker/pr-33097-1cc85900413c.json +1 -0
  360. package/src/cli/log-event-names.ts +5 -0
  361. package/src/cli/mcp-login.ts +8 -18
  362. package/src/cli/msg-cli.ts +57 -1
  363. package/src/cli/pr-land-heavy-remote.ts +47 -9
  364. package/src/cli/pr-land-overlap-evidence.ts +100 -1
  365. package/src/cli/pr-land-pod-shadow-job.ts +60 -0
  366. package/src/cli/release-checklist-read-cli.ts +20 -10
  367. package/src/cli/release-cli.ts +27 -1
  368. package/src/cli/research-cli.ts +4 -5
  369. package/src/cli/sim-cli.ts +7 -1
  370. package/src/cli/tasks-cli.ts +12 -4
  371. package/src/codex/models.ts +30 -15
  372. package/src/compact/auto.ts +8 -1
  373. package/src/control-plane/ledger.ts +52 -2
  374. package/src/control-plane/server.ts +16 -2
  375. package/src/core-turn/run-core-turn.ts +313 -89
  376. package/src/core-turn/run-goal-loop.ts +106 -2
  377. package/src/core-turn/types.ts +2 -0
  378. package/src/dashboard/acp-boot.ts +72 -3
  379. package/src/dashboard/classic-sidetab-mission-job.ts +58 -0
  380. package/src/dashboard/classic-sidetab-mission.ts +185 -0
  381. package/src/dashboard/index.ts +155 -15
  382. package/src/dashboard/input/agent-wake.ts +23 -1
  383. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +9 -0
  384. package/src/dashboard/mission-cli-consumer.ts +153 -0
  385. package/src/dashboard/seat-boot-preamble.ts +171 -1
  386. package/src/dashboard/side-basic-runtime.ts +54 -1
  387. package/src/dashboard/sidetab-runtime.ts +90 -1
  388. package/src/dashboard/skill-catalog-preamble.ts +101 -0
  389. package/src/dashboard/skill-detection-runtime.ts +55 -15
  390. package/src/dashboard/slash-runtime/dashboard-handlers.ts +15 -0
  391. package/src/dashboard/turn-preamble.ts +65 -16
  392. package/src/dashboard/turn-prompt-runtime.ts +4 -2
  393. package/src/disk-steward/judge.ts +73 -0
  394. package/src/disk-steward/ledger.ts +122 -0
  395. package/src/disk-steward/steward.ts +373 -0
  396. package/src/domains/guardian-tick.ts +191 -2
  397. package/src/graph-runner/graph-cli.ts +44 -7
  398. package/src/graph-runner/graph-tick.ts +10 -2
  399. package/src/graph-runner/in-process-registry.ts +37 -0
  400. package/src/graph-runner/runner.ts +34 -4
  401. package/src/grok/agent-search.ts +14 -4
  402. package/src/grok/credential.ts +28 -11
  403. package/src/grounding/ref-index.ts +19 -0
  404. package/src/grounding/sources-cli.ts +9 -1
  405. package/src/harness/harness-cli-command.ts +267 -14
  406. package/src/harness/harness-incidents.ts +312 -7
  407. package/src/harness/harness-pod-dispatch.ts +81 -15
  408. package/src/harness/harness-space.ts +15 -1
  409. package/src/harness/harness-stop.ts +155 -11
  410. package/src/harness/progress-worker-entry.ts +59 -0
  411. package/src/harness/progress-worker-runtime.ts +11 -0
  412. package/src/index.ts +52 -7
  413. package/src/instance/dev-project.ts +25 -0
  414. package/src/instance/spawn-elanous.ts +6 -4
  415. package/src/intelligence-map/model-alias.ts +6 -5
  416. package/src/intelligence-map/model-catalog.ts +59 -2
  417. package/src/llm/model-defaults.ts +4 -4
  418. package/src/llm.ts +208 -19
  419. package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
  420. package/src/loops/orchestrator/tick.ts +61 -0
  421. package/src/maturity/feature-maturity.ts +1 -1
  422. package/src/mcp/mcp-oauth.ts +3 -1
  423. package/src/model-tier/llm-tier-map.ts +9 -9
  424. package/src/model-tier/turn-tier.ts +1 -1
  425. package/src/msg/msg-mirror-github.ts +80 -0
  426. package/src/msg/msg-mirror-policy.ts +61 -0
  427. package/src/msg/msg-mirror-runner.ts +219 -0
  428. package/src/msg/msg-mirror-state.ts +33 -0
  429. package/src/msg/msg-store.ts +11 -0
  430. package/src/native-tool-catalog.ts +2 -2
  431. package/src/nexus/api/harness-traversal.ts +11 -0
  432. package/src/output-truncation.ts +41 -12
  433. package/src/pr-tool/census.ts +3 -2
  434. package/src/project/project-store.ts +48 -1
  435. package/src/pty-shell/tui-run-state.ts +188 -2
  436. package/src/release-loop/checklist-target-path-hint.ts +13 -0
  437. package/src/release-loop/checklist.ts +9 -1
  438. package/src/release-loop/gate-host-reservation.ts +144 -0
  439. package/src/release-loop/release-schedule.ts +90 -1
  440. package/src/repl/index.ts +43 -2
  441. package/src/research/axis-fanout.ts +121 -16
  442. package/src/research/bloom-exemplars.ts +137 -0
  443. package/src/research/gap-followup.ts +13 -1
  444. package/src/research/plugin-discovery.ts +36 -14
  445. package/src/research-bridge/invoke.ts +22 -5
  446. package/src/research-bridge/types.ts +2 -0
  447. package/src/self-dev/ask-launch-flow.ts +39 -25
  448. package/src/self-dev/decompose.ts +119 -5
  449. package/src/self-dev/dev-cli.ts +17 -17
  450. package/src/self-dev/graph-front-nodes.ts +23 -0
  451. package/src/self-dev/launch-preflight-text.ts +5 -6
  452. package/src/self-dev/launch-preflight.ts +9 -4
  453. package/src/self-dev/orchestrate.ts +27 -2
  454. package/src/self-dev/review-provider-fallback.ts +9 -2
  455. package/src/self-dev/run-supervisor.ts +226 -6
  456. package/src/self-dev/run-types.ts +7 -0
  457. package/src/self-implement/doctor-visit.ts +88 -0
  458. package/src/self-implement/gate-artifacts.ts +13 -0
  459. package/src/self-implement/gate-cli.ts +54 -24
  460. package/src/self-implement/gate-remote.ts +23 -5
  461. package/src/self-implement/goal-author-clarification.ts +31 -12
  462. package/src/self-implement/goal-author.ts +403 -8
  463. package/src/self-implement/graph-overlay-yaml.ts +7 -0
  464. package/src/self-implement/graph-stage-units.ts +172 -0
  465. package/src/self-implement/graph-templates.ts +162 -1
  466. package/src/self-implement/graph-variant.ts +37 -4
  467. package/src/self-implement/headless-elanous-driver.ts +31 -2
  468. package/src/self-implement/heal-intake.ts +112 -4
  469. package/src/self-implement/heal-triage.ts +20 -0
  470. package/src/self-implement/host-regate.ts +31 -8
  471. package/src/self-implement/knowhow-pack.ts +38 -0
  472. package/src/self-implement/merge-train.ts +5 -0
  473. package/src/self-implement/orchestrator.ts +359 -30
  474. package/src/self-implement/regate-detail.ts +38 -1
  475. package/src/self-implement/rework-child-provider.ts +25 -0
  476. package/src/self-implement/run-chart.ts +119 -0
  477. package/src/self-implement/run-ledger.ts +176 -17
  478. package/src/self-implement/run-stop.ts +208 -5
  479. package/src/self-implement/running-runs.ts +35 -12
  480. package/src/self-implement/self-implement-cli.ts +36 -4
  481. package/src/self-implement/stop-autoheal-operational.ts +43 -0
  482. package/src/self-implement/stop-autoheal.ts +28 -6
  483. package/src/session/chat.ts +69 -58
  484. package/src/session/request-size-guard.ts +123 -0
  485. package/src/session-runtime/index.ts +98 -4
  486. package/src/session-runtime/tier-flip.ts +3 -3
  487. package/src/sidetab-core/model.ts +150 -0
  488. package/src/sidetab-core/visible-agent-mission.ts +186 -0
  489. package/src/skills/name-match.ts +20 -0
  490. package/src/skills/tools/agent.ts +30 -2
  491. package/src/skills/tools/omni-search.ts +67 -5
  492. package/src/task-agent/parent-host.ts +14 -1
  493. package/src/task-agent/pod-distributor-entry.ts +3 -0
  494. package/src/task-agent/task-hand.ts +31 -1
  495. package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
  496. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -0
  497. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
  498. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
  499. package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +33 -6
  500. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +45 -9
  501. package/src/task-orchestrator/surfaces/pod-source-receive.ts +163 -13
  502. package/src/task-orchestrator/surfaces/pod-source-spec.ts +3 -3
  503. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +209 -39
  504. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +80 -3
  505. package/src/task-orchestrator/surfaces/self-implement-pod.ts +280 -60
  506. package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
  507. package/src/tokens.ts +35 -2
  508. package/src/tool-runtime/agent-runtime.ts +1 -0
  509. package/src/tool-runtime/elanous-skills-list-runtime.ts +5 -1
  510. package/src/tool-runtime/skill-exec-runtime.ts +33 -13
  511. package/src/tool-runtime/types.ts +1 -0
  512. package/src/tui/entry.ts +24 -7
  513. package/src/tui/sidetab/attach.ts +47 -0
  514. package/src/tui/sidetab/list.ts +45 -0
  515. package/src/tui/sidetab/nl-tool.ts +2 -136
  516. package/src/tui/sidetab/peek.ts +52 -0
  517. package/src/tui-client/dashboard-session.ts +3 -0
  518. package/src/tui-client/local-chat-turn.ts +26 -13
  519. package/src/tui.ts +22 -0
  520. package/src/user-config.ts +11 -5
  521. package/src/ux-sim/fake-kubectl.ts +37 -3
  522. package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
  523. package/src/ux-sim/fault-pod-child-result.ts +42 -0
  524. package/src/ux-sim/fault-pool-cap.ts +11 -6
  525. package/src/ux-sim/mock-scenario-catalog.json +41 -0
  526. package/src/ux-sim/mock-scenario-catalog.ts +156 -0
  527. package/src/ux-sim/release-sim-rig.ts +262 -0
  528. package/src/ux-sim/scenario.ts +116 -23
  529. package/src/ux-sim/seminar-rehearsal.ts +109 -0
  530. package/src/ux-sim/subagent-tools-device.ts +2 -2
  531. package/src/version/packed-revision.json +1 -1
  532. package/src/voice/voice-tts-singleton.ts +18 -3
  533. package/src/web-search/grok.ts +5 -9
  534. package/apps/pwa/out/_next/static/chunks/1060.014f1f01a17aec26.js +0 -115
  535. package/apps/pwa/out/_next/static/chunks/1415.79821ec7ae99acdb.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/1452.162bb2d9d699e82a.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/147.f92f63bbc5e85fa5.js +0 -286
  538. package/apps/pwa/out/_next/static/chunks/1661.fb425a1d8a065b72.js +0 -149
  539. package/apps/pwa/out/_next/static/chunks/1671-1c22b40b50b208f2.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/1758.0204d3f48783c49d.js +0 -90
  541. package/apps/pwa/out/_next/static/chunks/1806.027bf78b6a94d340.js +0 -104
  542. package/apps/pwa/out/_next/static/chunks/2047.8f23e210995d4548.js +0 -2
  543. package/apps/pwa/out/_next/static/chunks/2061-7f2bbc53ae96c88b.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/2066.a77257c8c350770a.js +0 -23
  545. package/apps/pwa/out/_next/static/chunks/2096-a938496bc996dcbd.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/2200-9a453b8f99687d49.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/2281-b8cede695c6c650e.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/2499.02fbfd18126c10c0.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/2522.f2766b76d22c8bdf.js +0 -34
  550. package/apps/pwa/out/_next/static/chunks/2615.3c9738153de3ef3f.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/2727.e703d99ac414916d.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/2752.7904b8df0254d0ae.js +0 -27
  553. package/apps/pwa/out/_next/static/chunks/2762-5051a862dfa40e99.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/2767-31784bfedf8e245d.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/2774-719820e3f5b2a3eb.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/2812-f016aa0dda149287.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/2829-734fe8bf2eee7347.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/311-32dcac79970985e8.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/3175-f93f7495cd28253c.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/3184.8da993f5502d2140.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/3231-d7c981db0ff8bdcc.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/3250.151309c5176d6b4b.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/3517.a5aa1797a9dbf2ee.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/3567-45f676877ed9e8a5.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/3819.8a1de79b2e8c7298.js +0 -24
  566. package/apps/pwa/out/_next/static/chunks/39.9b37b0be4c128a83.js +0 -77
  567. package/apps/pwa/out/_next/static/chunks/4023.a19045970a9ce16a.js +0 -36
  568. package/apps/pwa/out/_next/static/chunks/413-24c8284bda08241d.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/4135.e45d6ebc29e20647.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/4283.65502f5d0e6ff6c0.js +0 -287
  571. package/apps/pwa/out/_next/static/chunks/4305-4e28fa92a7b34a5b.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/4335-50efa61fd7613142.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/4720.ecbd1693fa151068.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/483.725e4a61be922788.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/4969-4b209d7307410c00.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/5068-c7f53862f83c0917.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/5315.bc96711c24dfefe2.js +0 -74
  578. package/apps/pwa/out/_next/static/chunks/5542.efbf14f16baed4dd.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/5622.94893f5aea7185d0.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/5644.8f401a25601d05fc.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/5658.35f70eb9871a54bf.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/5721.7bdc0e904c76ab51.js +0 -8
  583. package/apps/pwa/out/_next/static/chunks/5790-eef0ca20884a6bdf.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/5802.ef0d8598ca68fe84.js +0 -62
  585. package/apps/pwa/out/_next/static/chunks/5890.fb974729788dcb71.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/5924.5ef25cddd36d5dbf.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/5979-e59c099ebf8d1f35.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/6020.714cfeada2315e2e.js +0 -94
  589. package/apps/pwa/out/_next/static/chunks/6027-dfe0349d8c50f698.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/6178.bc4730c86cf74536.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/6273.d808b0dcb3c2b155.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/6292.6ef1df64fa6a4c8a.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/647-af8dd920ba46533c.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/6541-170dc5c59e135c0c.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/6624.981ef7caeb680560.js +0 -32
  596. package/apps/pwa/out/_next/static/chunks/6742-a1ad4a94ba7bc37a.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/6836-493dc55c39187949.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/6856-53ac421c06039a18.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/6908.06008c67f6380ea9.js +0 -148
  600. package/apps/pwa/out/_next/static/chunks/6915.096931864206f522.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/6925.5e181fb7dcf90193.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/6cc1d1d6.9a98dd4622f1afc6.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/7203-800e11037adc41eb.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/7279.6170f591105936bb.js +0 -93
  605. package/apps/pwa/out/_next/static/chunks/7349-373165c7fbdcc3df.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/7514-a533ba0ca1cae4f4.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/7515.1ad6efe157fa4448.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/7521.d919cc1fab80c1d9.js +0 -24
  609. package/apps/pwa/out/_next/static/chunks/7538.90e97bbdf9178cff.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/7825-39203196a12579b2.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/7869.5e1a9a12fd70d6c7.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/8086.1ab9103c41be884f.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/8179-e61fffbdb70f1152.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/8265.bdeb991b34db6a06.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/8357.1aaa713b052a78b1.js +0 -309
  616. package/apps/pwa/out/_next/static/chunks/8638-e42615f2166165b5.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/8639-92c11d069792429e.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/8691.54e22831d5e8ccf8.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/9069.657590a84afb8fac.js +0 -41
  620. package/apps/pwa/out/_next/static/chunks/9125-ae588d88a0293b93.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/9311.f9f641a46a508611.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/9371-a6fee8378ebfa3ee.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/9421-3aa9ff4fa7f63d8a.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/9992.07ce73c10bd5e6a1.js +0 -79
  625. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76394dc618d322c8.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1b948637c37b3abd.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-c63400f1bcf3e47c.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/app/board/page-95d0957267819586.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ba957ff875a026a0.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/app/bots/page-bb6fd0714879cb87.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8f9038520d2acbef.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/app/chat/page-d4b48397e05fce2c.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/app/consult/page-72d5744245dc0c27.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/app/control/page-37625dc8300fa43a.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/app/decisions/page-fbef9d160272dbc1.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/app/design-check/page-58424168df84928e.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/app/editor/page-72c12ba430ac57c0.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/app/exec/page-d53f3e9ed806d7e0.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/app/field/page-4d2fbd45feee0829.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76394dc618d322c8.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/app/inside/page-8783dc0ed7590a2c.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/app/intake/page-3706cb390606e930.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/app/layout-9b0a86598e0111fd.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/app/live/page-4edb8df90f4537ce.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/loops/page-47398b9ba1cc5e1b.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/market/page-fb86f55cfe2e681d.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e22345fc8fd47b1b.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/morning/page-fca2e59a81744d9e.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/not-found-6e96bda63100b31e.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4d17ad8e0db8cea4.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-121f92766c33beaa.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-43f14212c545f92d.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ccec2023e7a974ec.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b04f33f548b89a94.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/page-c8792823c0917021.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/reflection/page-8c0e32782ceef4cb.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-94bd3922eb230ce8.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bca565887bafa03c.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ac1dffafe649fc6c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/settings/page-52374edb5e23e455.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ad4ebca120d43ca7.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76394dc618d322c8.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/setup/page-718b89ce4b4ae4e4.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/share/page-204eca33eeeaddec.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/showroom/page-e58ba9bcdb9ac0f9.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/tasks/page-98f6dd50bafd7734.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/today/page-5a4bdd72bff27057.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/trace/page-6f6ab17d44f984ce.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/vault/page-aabb7bd15fb72da9.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e4322319164a4436.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/workflows/page-e9efc0c8f32e2371.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/workspace/page-ccde99f07aaf71f7.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-80464aced9614390.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/c696986b.5dc8bb1143af16d1.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/d5d3cefc-373c4eb722ed848b.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/d8de0de2-dd0f4ac8e5ab1832.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/framework-002b472a66935d31.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/main-49b88d47292c0476.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/main-app-fff2549cb10a133f.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/pages/_app-32abbbba3f3b76b7.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/pages/_error-9d9cfef60c60dd7a.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/webpack-cc7f483fa294034a.js +0 -1
  683. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
  684. package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
  685. /package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_ssgManifest.js +0 -0
@@ -18,6 +18,7 @@ import type { LogStore } from '../../mss/logging/log-store.js';
18
18
  const POD_READY_WAIT_SECONDS = 900;
19
19
  const POD_READY_RECHECK_SECONDS = 300;
20
20
  const POD_READY_RETRY_DELAY_MS = 1_000;
21
+ const WARM_CONTROL_REQUEST_TIMEOUT_MS = 2_000;
21
22
 
22
23
  function throwIfPrepareAborted(signal?: AbortSignal): void {
23
24
  if (!signal?.aborted) return;
@@ -71,6 +72,11 @@ export interface WarmPodProviderOptions {
71
72
  /** Gate file this pool serves, used to bind cold prepare legs to their 1-file gate measurement key. */
72
73
  pairingGateFile?: string;
73
74
  }
75
+ type WarmPodProviderRuntimeOverrides = Pick<WarmPodProviderOptions, 'fetch' | 'kubectl' | 'prepareLockDirectory'>;
76
+ export interface WarmPodProviderLoadOptions {
77
+ pairingGateFile?: string;
78
+ runtimeOverrides?: Partial<WarmPodProviderRuntimeOverrides>;
79
+ }
74
80
 
75
81
  export interface WarmPoolReconcileResult {
76
82
  profile: string;
@@ -293,6 +299,15 @@ export class WarmPodProvider {
293
299
  return { status: response.status, value };
294
300
  }
295
301
 
302
+ private async controlBounded(path: string, credential: string, body: Record<string, unknown>, timeoutMs: number): Promise<{ status: number; value: any }> {
303
+ let timer: ReturnType<typeof setTimeout> | undefined;
304
+ try {
305
+ return await Promise.race([this.control(path, credential, body), new Promise<never>((_, reject) => {
306
+ timer = setTimeout(() => reject(new Error('warm control request timed out')), timeoutMs);
307
+ })]);
308
+ } finally { if (timer) clearTimeout(timer); }
309
+ }
310
+
296
311
  /** The child owns kubectl. Timeout terminates only this child process group and fences its lease. */
297
312
  private async runTransport(spec: WarmPodSpec, command: WarmWorkerCommand): Promise<WarmPodTransportResult> {
298
313
  if (this.options.transport) return this.options.transport(spec).request(command);
@@ -468,12 +483,37 @@ export class WarmPodProvider {
468
483
  && Object.entries(metadata.annotations).every(([key, value]) => pod.metadata.annotations?.[key] === value)
469
484
  && pod.spec?.nodeName === nodeName
470
485
  && sameSpec(normalizedSpec(pod.spec), template);
471
- // Read the whole namespace: a Pod for this host on another node must block
472
- // creation, and another host's standing worker must not consume this slot.
473
- const listed = parse(kubectl(['get', 'pods', '-o', 'json']));
474
- if (!Array.isArray(listed?.items)) return false;
475
- const hostPods = listed.items.filter((row: any) => row?.metadata?.name === spec.name
486
+ // Resolve the pinned Pod independently; namespace size must not affect this read.
487
+ const namedResult = kubectl(['get', 'pods', `--field-selector=metadata.name=${spec.name}`, '-o', 'json']);
488
+ const namedList = parse(namedResult);
489
+ if (!Array.isArray(namedList?.items) || !namedList.metadata || typeof namedList.metadata !== 'object'
490
+ || Array.isArray(namedList.metadata)) return false;
491
+ const namedMatches = namedList.items.filter((row: any) => row?.metadata?.name === spec.name);
492
+ if (namedMatches.length > 1) return false;
493
+ const namedPod = namedMatches[0] ?? null;
494
+
495
+ // Kubectl's continuation token is consumed explicitly so no partial page can
496
+ // authorize a create. A hard page cap turns a non-terminating listing into failure.
497
+ const warmPods: any[] = [];
498
+ let continuation = '';
499
+ const seenContinuations = new Set<string>();
500
+ for (let page = 0; page < 100; page++) {
501
+ const args = ['get', 'pods', '-l', 'elanous.kind=warm-worker', '--limit=10', '-o', 'json'];
502
+ if (continuation) args.splice(5, 0, '--continue', continuation);
503
+ const result = kubectl(args);
504
+ const listed = parse(result);
505
+ if (!listed || !Array.isArray(listed.items) || !listed.metadata || typeof listed.metadata !== 'object'
506
+ || Array.isArray(listed.metadata)
507
+ || (listed.metadata.continue !== undefined && typeof listed.metadata.continue !== 'string')) return false;
508
+ warmPods.push(...listed.items);
509
+ continuation = listed.metadata?.continue ?? '';
510
+ if (!continuation) break;
511
+ if (seenContinuations.has(continuation) || page === 99) return false;
512
+ seenContinuations.add(continuation);
513
+ }
514
+ const hostPods = warmPods.filter((row: any) => row?.metadata?.name === spec.name
476
515
  || row?.metadata?.annotations?.['elanous.dev/warm-worker-id'] === spec.workerId);
516
+ if (namedPod && !hostPods.some((row: any) => row?.metadata?.name === spec.name)) hostPods.push(namedPod);
477
517
  if (hostPods.length > 1) return false;
478
518
  let pod = hostPods[0];
479
519
  const preexisting = !!pod;
@@ -481,7 +521,7 @@ export class WarmPodProvider {
481
521
  if (pod && !matches(pod)) return false;
482
522
  // Old generations still occupy memory even when their ledger rows are quarantined.
483
523
  // Count physical warm Pods on the pinned node before admitting a new one.
484
- const physical = listed.items.filter((row: any) => row?.metadata?.labels?.['elanous.kind'] === 'warm-worker'
524
+ const physical = warmPods.filter((row: any) => row?.metadata?.labels?.['elanous.kind'] === 'warm-worker'
485
525
  && row?.spec?.nodeName === nodeName && !row?.metadata?.deletionTimestamp
486
526
  && row?.status?.phase !== 'Succeeded' && row?.status?.phase !== 'Failed'
487
527
  && row?.spec?.containers?.[0]?.resources?.limits?.memory === spec.memoryLimit).length;
@@ -602,13 +642,77 @@ export class WarmPodProvider {
602
642
  const contextFailure = probeWarmPodContext(spec.kubectlContext, this.options.kubectl);
603
643
  if (contextFailure) { lastFallback = { command: null, transport: contextFailure }; continue; }
604
644
  let acquired: { status: number; value: any };
645
+ const acquisition = this.control('/v1/leases/pod-worker', this.options.memberToken, {
646
+ machine: host, key: spec.key, runId: input.runId, attemptId: input.attemptId,
647
+ ttlMs: this.options.leaseTtlMs ?? 30 * 60_000,
648
+ });
649
+ const timedOut = Symbol('warm-acquisition-timeout');
650
+ let deadlineTimer: ReturnType<typeof setTimeout> | undefined;
651
+ let acquisitionFailed = false;
652
+ let acquisitionResult: typeof timedOut | { status: number; value: any };
605
653
  try {
606
- acquired = await this.control('/v1/leases/pod-worker', this.options.memberToken, {
607
- machine: host, key: spec.key, runId: input.runId, attemptId: input.attemptId,
608
- ttlMs: this.options.leaseTtlMs ?? 30 * 60_000,
609
- });
610
- } catch { lastFallback = fallback('lease-unavailable'); continue; }
654
+ const remainingMs = input.deadlineAt - this.now();
655
+ if (remainingMs <= 0) acquisitionResult = timedOut;
656
+ else acquisitionResult = await Promise.race([acquisition, new Promise<typeof timedOut>(resolve => {
657
+ deadlineTimer = setTimeout(() => resolve(timedOut), remainingMs);
658
+ })]);
659
+ } catch { acquisitionFailed = true; acquisitionResult = timedOut; }
660
+ finally { if (deadlineTimer) clearTimeout(deadlineTimer); }
661
+ if (acquisitionResult === timedOut) {
662
+ void acquisition.then(async late => {
663
+ const row = late.value;
664
+ const lateWorkerId = row?.name;
665
+ const leaseId = row?.attrs?.leaseId;
666
+ const generation = row?.attrs?.generation;
667
+ const podUid = row?.attrs?.podUid;
668
+ const lateCandidate = this.candidates.find(candidate => candidate.spec.workerId === lateWorkerId
669
+ && candidate.host === host && keyEquals(candidate.spec.key, spec.key));
670
+ if (late.status !== 200 || !lateCandidate || !row?.attrs?.key || !keyEquals(row.attrs.key, lateCandidate.spec.key)
671
+ || typeof leaseId !== 'string' || !leaseId
672
+ || !Number.isSafeInteger(generation) || typeof podUid !== 'string' || !podUid) return;
673
+ const lateSpec = lateCandidate.spec;
674
+ const command: WarmWorkerCommand = { ...input, leaseId, generation, podUid,
675
+ podName: lateSpec.name, podContext: lateSpec.kubectlContext, image: lateSpec.image,
676
+ requestedAt: new Date(this.now()).toISOString(), workerId: lateSpec.workerId, key: lateSpec.key };
677
+ debug.log('pod.warm', 'lease-response-after-deadline', { workerId: command.workerId,
678
+ leaseId, generation, podUid, runId: command.runId, attemptId: command.attemptId }, { level: 'warn' });
679
+ debug.log('pod.provenance', 'lease-start', { pod: lateSpec.name, podUid, leaseId,
680
+ runId: command.runId, attemptId: command.attemptId, requestedAt: command.requestedAt,
681
+ metadata: warmLeaseMetadata(command).annotations });
682
+ try {
683
+ const fenced = await this.controlBounded(`/v1/leases/pod-worker/${encodeURIComponent(lateWorkerId)}/timeout`,
684
+ this.options.memberToken, { runId: input.runId, attemptId: input.attemptId }, WARM_CONTROL_REQUEST_TIMEOUT_MS);
685
+ if (fenced.status !== 200) throw new Error(`warm timeout fence returned ${fenced.status}`);
686
+ } catch (error) { debug.log('pod.warm', 'late-lease-timeout-fence-failed', { workerId: lateWorkerId,
687
+ leaseId, generation, error: String(error) }, { level: 'warn' }); }
688
+ let quarantineTimer: ReturnType<typeof setTimeout> | undefined;
689
+ try {
690
+ await Promise.race([this.quarantine(command, 'lease-response-after-deadline'), new Promise<never>((_, reject) => {
691
+ quarantineTimer = setTimeout(() => reject(new Error('warm late-lease quarantine timed out')), WARM_CONTROL_REQUEST_TIMEOUT_MS);
692
+ })]);
693
+ }
694
+ catch (error) { debug.log('pod.warm', 'late-lease-quarantine-failed', { workerId: command.workerId,
695
+ leaseId, generation, error: String(error) }, { level: 'warn' }); }
696
+ finally { if (quarantineTimer) clearTimeout(quarantineTimer); }
697
+ }, error => debug.log('pod.warm', 'late-lease-response-failed', { workerId: spec.workerId,
698
+ runId: input.runId, attemptId: input.attemptId, error: String(error) }, { level: 'warn' }));
699
+ if (acquisitionFailed) continue;
700
+ return fallback('lease-unavailable');
701
+ }
702
+ acquired = acquisitionResult;
611
703
  if (acquired.status === 409 || acquired.status === 503) {
704
+ if (acquired.status === 409) {
705
+ try {
706
+ const query = new URLSearchParams({ kind: 'pod-worker-lease', machine: host, name: spec.workerId });
707
+ const observed = await this.control(`/v1/resources?${query}`, this.options.memberToken);
708
+ const row = observed.status === 200 && Array.isArray(observed.value?.resources)
709
+ ? observed.value.resources.find((entry: any) => entry?.name === spec.workerId) : undefined;
710
+ if (row?.attrs?.state === 'quarantined' || row?.attrs?.state === 'timeout-fenced') {
711
+ debug.log('pod.warm', 'quarantine-holds-slot', { workerId: spec.workerId,
712
+ state: row.attrs.state, at: new Date(this.now()).toISOString() }, { level: 'warn' });
713
+ }
714
+ } catch { /* Slot observation never changes the Job's cold fallback. */ }
715
+ }
612
716
  lastFallback = fallback('lease-unavailable');
613
717
  continue;
614
718
  }
@@ -620,6 +724,16 @@ export class WarmPodProvider {
620
724
  if (!workerId(row?.name) || typeof leasedId !== 'string' || !leasedId
621
725
  || typeof podUid !== 'string' || !podUid
622
726
  || !Number.isSafeInteger(generation) || generation < 1) return fallback('lease-unavailable');
727
+ // One terminal transition per workerId·leaseId·generation·Pod UID. A lease the
728
+ // control plane already settled (finalize/quarantine raced this acquisition)
729
+ // must never be re-adopted — not by this late reply, not by the next warm request.
730
+ // An uncertain ending keeps its quarantined / cold-Job verdict.
731
+ const terminalKey = JSON.stringify([row.name, leasedId, generation, podUid]);
732
+ if (this.terminalLeases.has(terminalKey) || this.quarantineRequested.has(terminalKey)) {
733
+ debug.log('pod.warm', 'lease-terminal-refused', { workerId: row.name, leaseId: leasedId,
734
+ generation, podUid, runId: input.runId, attemptId: input.attemptId }, { level: 'warn' });
735
+ return fallback('lease-unavailable');
736
+ }
623
737
  // Acquisition selects by host/key, not by worker ID. Resolve the returned worker
624
738
  // before choosing its transport; an unknown worker must not be executed or fenced.
625
739
  const leasedCandidate = this.candidates.find(entry => entry.host === host
@@ -641,6 +755,20 @@ export class WarmPodProvider {
641
755
  podName: leasedCandidate.spec.name, podContext: leasedCandidate.spec.kubectlContext, image: leasedCandidate.spec.image,
642
756
  requestedAt: new Date(this.now()).toISOString(),
643
757
  workerId: row.name, key: leasedCandidate.spec.key };
758
+ if (command.deadlineAt <= this.now()) {
759
+ debug.log('pod.warm', 'lease-response-after-deadline', { workerId: command.workerId,
760
+ leaseId: command.leaseId, generation: command.generation, podUid: command.podUid,
761
+ runId: command.runId, attemptId: command.attemptId }, { level: 'warn' });
762
+ // The control plane granted a lease, even though the caller's budget expired before Pod adoption.
763
+ // Record its short lifetime as a paired start/end and quarantine it without running transport.
764
+ debug.log('pod.provenance', 'lease-start', { pod: leasedCandidate.spec.name, podUid: command.podUid,
765
+ leaseId: command.leaseId, runId: command.runId, attemptId: command.attemptId,
766
+ requestedAt: command.requestedAt, metadata: warmLeaseMetadata(command).annotations });
767
+ try { await this.quarantine(command, 'lease-response-after-deadline'); }
768
+ catch (error) { debug.log('pod.warm', 'late-lease-quarantine-failed', { workerId: command.workerId,
769
+ leaseId: command.leaseId, generation: command.generation, error: String(error) }, { level: 'warn' }); }
770
+ return fallback('lease-unavailable');
771
+ }
644
772
  // Acquisition is by pool key. A healthy sibling or replacement Pod must remain eligible.
645
773
  // Never run transport on the old Pod while its previous lease is being quarantined.
646
774
  if (this.quarantiningPods.has(JSON.stringify([command.workerId, command.podUid]))) {
@@ -710,20 +838,26 @@ export class WarmPodProvider {
710
838
  }
711
839
 
712
840
  private async finalizeOnce(command: WarmWorkerCommand): Promise<'sanitized' | 'quarantined'> {
841
+ const key = JSON.stringify([command.workerId, command.leaseId, command.generation, command.podUid]);
713
842
  const candidate = this.candidates.find(row => row.spec.workerId === command.workerId);
714
843
  if (!candidate) throw new Error('warm finalize candidate missing');
715
844
  const released = await this.transition(command, 'release');
716
845
  const machineToken = this.options.machineTokens?.[candidate.host];
717
846
  // Sanitizing is the producer's machine-scoped acknowledgement, after transport's cleanup and ACK.
718
- const cleared = released ? patchWarmLeaseMetadata(candidate.spec, command, 'idle', this.options.kubectl)
847
+ // Always verify and clear this exact Pod UID/lease metadata before telling the control plane it is reusable.
848
+ // A missing local adoption record is not evidence that no pod-side leased state exists.
849
+ const cleared = released
850
+ ? patchWarmLeaseMetadata(candidate.spec, command, 'idle', this.options.kubectl)
719
851
  : { ok: false, reason: 'release-failed' };
720
852
  if (!cleared.ok) debug.log('pod.provenance', 'lease-patch-refused', { pod: candidate.spec.name,
721
853
  podUid: command.podUid, leaseId: command.leaseId, reason: cleared.reason }, { level: 'warn' });
722
854
  if (!cleared.ok || !token(machineToken) || !await this.transition(command, 'sanitize', machineToken)) {
723
- await this.quarantineOnce(command, 'sanitize-unavailable', true);
855
+ if (!this.quarantineRequested.has(key)) {
856
+ this.quarantineRequested.add(key);
857
+ await this.quarantineOnce(command, 'sanitize-unavailable', true);
858
+ }
724
859
  return 'quarantined';
725
860
  }
726
- const key = JSON.stringify([command.workerId, command.leaseId, command.generation, command.podUid]);
727
861
  if (!this.quarantineRequested.has(key)) {
728
862
  debug.log('pod.provenance', 'lease-end', { pod: candidate.spec.name, podUid: command.podUid,
729
863
  leaseId: command.leaseId, runId: command.runId, attemptId: command.attemptId,
@@ -740,19 +874,21 @@ export class WarmPodProvider {
740
874
  this.quarantiningPods.add(podKey);
741
875
  const prior = this.terminalLeases.get(key);
742
876
  if (prior) {
877
+ if (this.quarantineRequested.has(key)) {
878
+ try { await prior; this.quarantiningPods.delete(podKey); }
879
+ catch (error) { if (this.terminalLeases.get(key) === prior) this.terminalLeases.delete(key); throw error; }
880
+ return;
881
+ }
743
882
  this.quarantineRequested.add(key);
744
- const terminal = prior.then(async outcome => {
745
- if (outcome === 'sanitized') {
746
- await this.quarantineOnce(command, reason, leaseStarted);
747
- return 'quarantined' as const;
748
- }
749
- return outcome;
750
- });
883
+ // Fence the control-plane lease immediately. Waiting for sanitize to settle leaves a window
884
+ // where another request could acquire the just-idled worker before this quarantine lands.
885
+ const terminal = this.quarantineOnce(command, reason, leaseStarted).then(() => 'quarantined' as const);
751
886
  this.terminalLeases.set(key, terminal);
752
887
  try { await terminal; this.quarantiningPods.delete(podKey); }
753
888
  catch (error) { if (this.terminalLeases.get(key) === terminal) this.terminalLeases.delete(key); throw error; }
754
889
  return;
755
890
  }
891
+ this.quarantineRequested.add(key);
756
892
  const terminal = this.quarantineOnce(command, reason, leaseStarted).then(() => 'quarantined' as const);
757
893
  this.terminalLeases.set(key, terminal);
758
894
  try { await terminal; this.quarantiningPods.delete(podKey); }
@@ -783,7 +919,11 @@ let configuredProvider: WarmPodProvider | null = null;
783
919
  /** The trusted producer configures this once; an absent provider fails closed without creating a Pod. */
784
920
  export function configureWarmPodProvider(provider: WarmPodProvider | null): void { configuredProvider = provider; }
785
921
  /** Optional private producer config; a missing/unsafe file leaves the Job path untouched. */
786
- export function loadWarmPodProvider(path: string, pairingGateFile?: string): WarmPodProvider {
922
+ export function loadWarmPodProvider(path: string, pairingGateFileOrOptions?: string | WarmPodProviderLoadOptions): WarmPodProvider {
923
+ const pairingGateFile = typeof pairingGateFileOrOptions === 'string'
924
+ ? pairingGateFileOrOptions : pairingGateFileOrOptions?.pairingGateFile;
925
+ const runtimeOverrides = typeof pairingGateFileOrOptions === 'object'
926
+ ? pairingGateFileOrOptions?.runtimeOverrides ?? {} : {};
787
927
  if (!isAbsolute(path) || (statSync(path).mode & 0o077) !== 0) throw new Error('warm provider config must be private');
788
928
  const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'));
789
929
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('invalid warm provider config');
@@ -797,7 +937,7 @@ export function loadWarmPodProvider(path: string, pairingGateFile?: string): War
797
937
  if (!pairingGateFile.trim()) throw new Error('warm provider pairing gate file must not be empty');
798
938
  options.pairingGateFile = pairingGateFile;
799
939
  }
800
- return new WarmPodProvider(options);
940
+ return new WarmPodProvider({ ...options, ...runtimeOverrides });
801
941
  }
802
942
  function currentProvider(): WarmPodProvider | null {
803
943
  if (configuredProvider) return configuredProvider;
@@ -851,35 +991,55 @@ export function parseWarmPodProviderArgs(argv: readonly string[]): { action: '--
851
991
  throw usage();
852
992
  }
853
993
 
854
- if (import.meta.main) {
855
- const { action, workerId, gateFile } = parseWarmPodProviderArgs(process.argv.slice(2));
994
+ export interface WarmPodProviderCliDependencies {
995
+ providerRuntime?: Partial<WarmPodProviderRuntimeOverrides>;
996
+ write?: (line: string) => void;
997
+ registerLogSink?: (name: string) => Promise<void>;
998
+ sleep?: (milliseconds: number) => Promise<void>;
999
+ }
1000
+
1001
+ export async function runWarmPodProviderCli(argv: readonly string[] = process.argv.slice(2),
1002
+ dependencies: WarmPodProviderCliDependencies = {}): Promise<{
1003
+ exitCode: number;
1004
+ result?: WarmPoolReconcileResult | null;
1005
+ prepared?: boolean;
1006
+ }> {
1007
+ const { action, workerId, gateFile } = parseWarmPodProviderArgs(argv);
1008
+ const write = dependencies.write ?? ((line: string) => { process.stdout.write(line); });
856
1009
  const file = process.env.ELANOUS_WARM_POD_PROVIDER_CONFIG;
857
1010
  // The 1-file gate key this pool serves, bound into every cold prepare leg of this process.
858
- const load = () => (file ? loadWarmPodProvider(file, gateFile) : null);
1011
+ const load = () => {
1012
+ if (!file) return null;
1013
+ return loadWarmPodProvider(file, { pairingGateFile: gateFile, runtimeOverrides: dependencies.providerRuntime });
1014
+ };
859
1015
  if (action === '--pairs') {
860
1016
  // Read-only: join the cold CLI legs and the warm gate legs both processes wrote to logs.db.
861
1017
  try {
862
1018
  const storeMod = await import('../../mss/logging/log-store.js');
863
1019
  const dbPath = storeMod.logsDbPath();
864
1020
  // No logs.db yet means no leg was ever measured: an empty pair list, not a failed query.
865
- if (!existsSync(dbPath)) process.stdout.write(JSON.stringify({ warmReuseTimePairs: [] }) + '\n');
1021
+ if (!existsSync(dbPath)) write(JSON.stringify({ warmReuseTimePairs: [] }) + '\n');
866
1022
  else {
867
1023
  const store = storeMod.LogStore.openReadOnly(dbPath);
868
- try { process.stdout.write(JSON.stringify({ warmReuseTimePairs: readWarmReuseTimePairs(store) }) + '\n'); }
1024
+ try { write(JSON.stringify({ warmReuseTimePairs: readWarmReuseTimePairs(store) }) + '\n'); }
869
1025
  finally { store.close(); }
870
1026
  }
871
1027
  } catch (error) {
872
- process.stdout.write(JSON.stringify({ warmReuseTimePairs: null, reason: 'logs-unavailable', detail: String(error) }) + '\n');
873
- process.exitCode = 2;
1028
+ write(JSON.stringify({ warmReuseTimePairs: null, reason: 'logs-unavailable', detail: String(error) }) + '\n');
1029
+ return { exitCode: 2 };
874
1030
  }
1031
+ return { exitCode: 0 };
875
1032
  }
876
1033
  // The cold legs must reach the same logs.db the warm gate process (gate-node) writes; fail-open.
877
- if (action !== '--pairs') try { await (await import('../../domains/standalone-log-sink.js')).registerStandaloneLogSink('pod-warm'); }
1034
+ try {
1035
+ if (dependencies.registerLogSink) await dependencies.registerLogSink('pod-warm');
1036
+ else await (await import('../../domains/standalone-log-sink.js')).registerStandaloneLogSink('pod-warm');
1037
+ }
878
1038
  catch { /* the file trail stays the source of truth */ }
879
1039
  // This process's own legs, echoed for the caller; the pair itself is read with --pairs.
880
1040
  const emitPairs = (provider: WarmPodProvider) => {
881
1041
  const legs = provider.warmReuseTimeLegs();
882
- if (legs.length) process.stdout.write(JSON.stringify({ warmReuseTimeLegs: legs }) + '\n');
1042
+ if (legs.length) write(JSON.stringify({ warmReuseTimeLegs: legs }) + '\n');
883
1043
  };
884
1044
  if (action === '--serve') {
885
1045
  // Reloading the private config lets the owner rotate a quarantined slot to a new worker ID.
@@ -888,30 +1048,40 @@ if (import.meta.main) {
888
1048
  try {
889
1049
  const provider = load();
890
1050
  const result = provider ? await provider.reconcilePool() : null;
891
- process.stdout.write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
1051
+ write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
892
1052
  if (provider) emitPairs(provider);
893
1053
  } catch {
894
1054
  debug.log('pod.warm', 'pool-reconcile-unavailable', { reason: 'config-unavailable' }, { level: 'warn' });
895
- process.stdout.write(JSON.stringify({ target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
1055
+ write(JSON.stringify({ target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
896
1056
  }
897
- await Bun.sleep(60_000);
1057
+ await (dependencies.sleep ?? Bun.sleep)(60_000);
898
1058
  }
1059
+ return { exitCode: 0 };
899
1060
  }
900
1061
  if (action === '--reconcile') {
901
1062
  let result: WarmPoolReconcileResult | null = null;
902
1063
  let provider: WarmPodProvider | null = null;
903
1064
  try { provider = load(); result = provider ? await provider.reconcilePool() : null; }
904
1065
  catch { /* An invalid private config is a failed pass, never a Pod mutation. */ }
905
- process.stdout.write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
1066
+ write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
906
1067
  if (provider) emitPairs(provider);
907
- if (!result || result.ready !== result.target) process.exitCode = 2;
1068
+ return { exitCode: !result || result.ready !== result.target ? 2 : 0, result };
908
1069
  } else if (action === '--prepare') {
909
1070
  let prepared = false;
910
1071
  let provider: WarmPodProvider | null = null;
911
1072
  try { provider = load(); prepared = provider ? await provider.prepareWorker(workerId!) : false; }
912
1073
  catch { /* An invalid private config is a failed preparation, never a Pod mutation. */ }
913
- process.stdout.write(JSON.stringify({ workerId, prepared }) + '\n');
1074
+ write(JSON.stringify({ workerId, prepared }) + '\n');
914
1075
  if (provider) emitPairs(provider);
915
- if (!prepared) process.exitCode = 2;
1076
+ return { exitCode: prepared ? 0 : 2, prepared };
1077
+ }
1078
+ return { exitCode: 2 };
1079
+ }
1080
+
1081
+ if (import.meta.main) {
1082
+ try { process.exitCode = (await runWarmPodProviderCli()).exitCode; }
1083
+ catch (error) {
1084
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
1085
+ process.exitCode = 2;
916
1086
  }
917
1087
  }
@@ -7,6 +7,7 @@ import { warmReferenceFromJobArtifacts } from '../../../scripts/release-loop/gat
7
7
  import { stateDirSourceForChild } from '../../agent/identity-env.js';
8
8
  import { debug } from '../../debug/log.js';
9
9
  import { decidePodReuseCleanup } from './pod-reuse-cleanup.js';
10
+ import { podSourceScript, type PodSource } from './pod-source-receive.js';
10
11
  import type { WarmWorkerCommand, WarmWorkerFileResult, WarmWorkerReply } from './pod-warm-worker-protocol.js';
11
12
 
12
13
  const token = (s: unknown): s is string => typeof s === 'string' && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(s);
@@ -24,6 +25,31 @@ const withoutGitEnvironment = (): NodeJS.ProcessEnv => {
24
25
  return env;
25
26
  };
26
27
 
28
+ const installedLockHashPath = (repoDir: string): string => join(repoDir, 'node_modules', '.elanous-installed-lock-hash');
29
+
30
+ function readInstalledLockHash(repoDir: string): string | null {
31
+ try {
32
+ const modules = lstatSync(join(repoDir, 'node_modules'));
33
+ if (!modules.isDirectory() || modules.isSymbolicLink()) return null;
34
+ const marker = installedLockHashPath(repoDir);
35
+ const stat = lstatSync(marker);
36
+ if (!stat.isFile() || stat.isSymbolicLink()) return null;
37
+ const value = readFileSync(marker, 'utf8').trim();
38
+ return /^[a-f0-9]{64}$/.test(value) ? value : null;
39
+ } catch { return null; }
40
+ }
41
+
42
+ /** Record the image's installed dependency lock once source verification ties it to the worker key. */
43
+ function rememberInstalledLockHash(repoDir: string, lockHash: string): void {
44
+ try {
45
+ const modules = lstatSync(join(repoDir, 'node_modules'));
46
+ if (!modules.isDirectory() || modules.isSymbolicLink()) return;
47
+ const marker = installedLockHashPath(repoDir);
48
+ if (existsSync(marker)) return;
49
+ writeFileSync(marker, `${lockHash}\n`, { mode: 0o600, flag: 'wx' });
50
+ } catch { /* an unknown installation stays unknown and min-sync will install before reuse */ }
51
+ }
52
+
27
53
  function resolveThroughExistingAncestor(path: string): string | null {
28
54
  let ancestor = resolve(path);
29
55
  const suffix: string[] = [];
@@ -147,6 +173,12 @@ export class WarmWorkerRuntime {
147
173
  runGit?: (args: readonly string[]) => WarmWorkerGitResult;
148
174
  removeScratch?: (path: string) => void;
149
175
  now?: () => number;
176
+ /** POD-REUSE-MIN-SYNC — 새 base 요청의 최소 동기화 원천: 호스트 미러 마운트(기본 /host-mirror). */
177
+ mirrorPath?: string;
178
+ /** POD-REUSE-MIN-SYNC — 부모가 넘긴 git bundle(있으면 호스트 미러보다 먼저). */
179
+ minSyncBundle?: { bundlePath: string; sha256: string; sizeBytes: number };
180
+ /** 시험 심 — min-sync 수신 스크립트 실행(기본: 체크아웃 안에서 bash -c). */
181
+ runMinSync?: (script: string) => { status: number | null; stdout: string; stderr: string };
150
182
  }) {}
151
183
 
152
184
  handle(command: WarmWorkerCommand): Promise<WarmWorkerOutcome> {
@@ -177,9 +209,18 @@ export class WarmWorkerRuntime {
177
209
  return { reply: null, quarantineWorker: true, reason: 'execution-error' };
178
210
  }
179
211
  const repo = realpathSync(this.options.repoDir);
180
- if (!warmWorkerSourceMatches(repo, command.cutSha, command.key.lockHash)) {
181
- this.quarantined = true;
182
- return { reply: null, quarantineWorker: true, reason: 'quarantined' };
212
+ const sourceMatches = warmWorkerSourceMatches(repo, command.cutSha, command.key.lockHash);
213
+ if (sourceMatches && readInstalledLockHash(repo) === null) rememberInstalledLockHash(repo, command.key.lockHash);
214
+ if (!sourceMatches || readInstalledLockHash(repo) !== command.key.lockHash) {
215
+ // POD-REUSE-MIN-SYNC — 같은 웜 Pod 로 들어온 새 base 요청: 상주 체크아웃을 호스트 미러(또는 부모가
216
+ // 넘긴 bundle)에서만 최소 동기화한 뒤 같은 검증으로 다시 본다. min-sync 가 아니거나 실패면
217
+ // 기존 폐기(quarantine) 판정이 그대로 선다.
218
+ if (!this.minSyncReceive(repo, command)
219
+ || !warmWorkerSourceMatches(repo, command.cutSha, command.key.lockHash)
220
+ || readInstalledLockHash(repo) !== command.key.lockHash) {
221
+ this.quarantined = true;
222
+ return { reply: null, quarantineWorker: true, reason: 'quarantined' };
223
+ }
183
224
  }
184
225
  debug.log('pod.warm', 'resident-checkout-verified', { runId: command.runId,
185
226
  attemptId: command.attemptId, workerId: command.workerId,
@@ -363,4 +404,40 @@ export class WarmWorkerRuntime {
363
404
  }
364
405
  return { reply, quarantineWorker: false, reason: 'complete', artifactsDir };
365
406
  }
407
+
408
+ /** POD-REUSE-MIN-SYNC — 체크아웃 head 또는 실제 설치 lock hash 가 요청과 다를 때만 시도한다.
409
+ * dirty source bytes 는 min-sync 대상으로 삼지 않는다. base 는 호스트 미러/부모 bundle 에서만 받는다(GitHub 호출 0) ·
410
+ * 차이 파일만 checkout/reset 하고 남은 untracked·빌드 산출만 clean 한다. 성공 표지
411
+ * `ELANOUS_POD_STAGE min-sync <호출 수> <바뀐 파일 수> <소요 초>` 를 관측(pod.warm/min-sync)으로 남긴다. */
412
+ private minSyncReceive(repo: string, command: WarmWorkerCommand): boolean {
413
+ const git = (args: readonly string[]): WarmWorkerGitResult => {
414
+ try {
415
+ return this.options.runGit?.(args) ?? spawnSync('git', ['-C', repo, ...args], {
416
+ encoding: 'utf8', env: withoutGitEnvironment(), maxBuffer: 16 * 1024 * 1024,
417
+ });
418
+ } catch { return { status: null, stdout: null }; }
419
+ };
420
+ const resident = git(['rev-parse', 'HEAD']);
421
+ const previousHead = resident.status === 0 ? (resident.stdout ?? '').trim() : '';
422
+ if (!sha(previousHead)) return false;
423
+ const source: PodSource = {
424
+ kind: 'min-sync', baseSha: command.cutSha, previousHead, lockHash: command.key.lockHash,
425
+ ...(this.options.minSyncBundle ? { bundle: this.options.minSyncBundle } : {}),
426
+ };
427
+ const script = podSourceScript(source, 'https://github.com/unused/repo', this.options.mirrorPath ?? '/host-mirror', { checkoutDir: repo });
428
+ let run: { status: number | null; stdout?: string | null };
429
+ try {
430
+ run = this.options.runMinSync?.(script) ?? spawnSync('bash', ['-c', script], {
431
+ encoding: 'utf8', cwd: repo, env: withoutGitEnvironment(), maxBuffer: 16 * 1024 * 1024,
432
+ });
433
+ } catch { return false; }
434
+ const out = run.stdout ?? '';
435
+ const stage = /^ELANOUS_POD_STAGE min-sync (\d+) (\d+) (\d+)\r?$/m.exec(out);
436
+ try {
437
+ debug.log('pod.warm', 'min-sync', { runId: command.runId, attemptId: command.attemptId, workerId: command.workerId,
438
+ baseSha: command.cutSha, previousHead, ok: run.status === 0,
439
+ githubCalls: stage ? Number(stage[1]) : null, changedFiles: stage ? Number(stage[2]) : null, seconds: stage ? Number(stage[3]) : null });
440
+ } catch { /* observation is fail-soft */ }
441
+ return run.status === 0 && !!stage;
442
+ }
366
443
  }