elanous 0.2.41 → 0.2.43

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 (796) 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/{GiOLWmt4MqDZePubBjA4Q → Om5xS9jpjSXJCeTNGYEi4}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{2fe2c302.41b517f88d6fe502.js → 0966f8a1.745fcd44f64a87ea.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{385e4574.e9886ab32759eda3.js → 0ad6802e.ae7afb63fdcb4491.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/114.2b90424d1a3ce84c.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1151-5c2bb8f002ca2dd6.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1200.6a37c202f9f17e9a.js +226 -0
  9. package/apps/pwa/out/_next/static/chunks/1261-a616d5728e02e624.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1313-19f737e0222c4444.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1357.3bbef3976c37f94a.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1362-20adddfaf662b0fa.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{6874.d5cf7eb8edb2cabe.js → 1406.a1dc2ab6f8449374.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/1737-9491fc6ab29b9ec6.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1803-4f91a8e7dcaf042c.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/{5744.b9415e18ad623100.js → 1961.a81b12872c3e93a9.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/1970cf59.2d4d8ce5d6c81853.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1971-8890f3962ba7ee0a.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/2073-ba274e00e4f9efb4.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/{5471.1e611302ed3a7c53.js → 2144.25b292396712196d.js} +2 -2
  21. package/apps/pwa/out/_next/static/chunks/{3230-49298c3b73ef2268.js → 2268-53861f339e0b65a2.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/{9834.63ab2bbc3153172e.js → 2312.0c8b61e22d3136d7.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/{1800.f7ff51dc4978a958.js → 2573.387a707f6f9d720c.js} +2 -2
  24. package/apps/pwa/out/_next/static/chunks/273.f156eee28f11af77.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2873.d2140e5a82546401.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/{9048.212083de42d25be3.js → 3072.01338851ca21a6f7.js} +2 -2
  27. package/apps/pwa/out/_next/static/chunks/3091.a5a4143dea33d197.js +90 -0
  28. package/apps/pwa/out/_next/static/chunks/3095.dc44586a5f83b86d.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{2263.e09b6edea33bad2d.js → 3113.d7472bbc27d580c7.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/3228-9650b6447b6fa288.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/3402.44b565b314f2263c.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{addc2b32.8c4ff8f0aefe987d.js → 3557ac4a.c8931684475018ad.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/360.ae9f9f4c33c395e8.js +79 -0
  34. package/apps/pwa/out/_next/static/chunks/3638.80412ea63aae8f3f.js +286 -0
  35. package/apps/pwa/out/_next/static/chunks/{a0a01613.6d1cca322238d7fe.js → 363bfab3.9623b9f89363541f.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/3694.e0099884345f0198.js +64 -0
  37. package/apps/pwa/out/_next/static/chunks/3744.54f87db52acfd1ab.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3903.3ef4656c67572764.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3935.16701e33f8982dc9.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3955.6922286237f2ac2b.js +24 -0
  41. package/apps/pwa/out/_next/static/chunks/3969.272ee7f9fd6c62e5.js +34 -0
  42. package/apps/pwa/out/_next/static/chunks/4199.ce86b281fd0a60b7.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4311-6dd3144002cc0c2d.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4426-2b230fad00c88930.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{530.d26346e18089640a.js → 4445.7990c76b3751a918.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4447-14c5161fa59166c7.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4856-b0b8075bba8c53f4.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4870-de370d65e73c0bb5.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4885-2c989165c4b29fad.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4894.fec2ca9e99caefa2.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/5093.8f04b7f8cb899a95.js +94 -0
  52. package/apps/pwa/out/_next/static/chunks/5122-2bb177cebf89e19a.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5154.5f56dc3902e3d275.js +36 -0
  54. package/apps/pwa/out/_next/static/chunks/529.64ff7257180b2ac9.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5394.5dabeb0c62293a28.js +93 -0
  56. package/apps/pwa/out/_next/static/chunks/5461.08bf2404ff38ec85.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5474-22e08a85583209b2.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{1219.7e17964e0e7cd8e5.js → 5564.9e72532188f57586.js} +3 -3
  59. package/apps/pwa/out/_next/static/chunks/5567-265df39ed8fe9dcb.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5570-a94ae19811cb2046.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5574-b47c9ef2dceff1fd.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5628.6ddb2d5ffba4ff4c.js +309 -0
  63. package/apps/pwa/out/_next/static/chunks/5730.43d7ccc09988e646.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{1178.1aa9cad0e7695427.js → 5789.2da35c258292e1e5.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/{4133.2056bb4d78f621af.js → 5824.da9527b15a3221e5.js} +1 -1
  66. package/apps/pwa/out/_next/static/chunks/5866.366966af47fdde5d.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/{4527.a69f2894d1bc9f4a.js → 5930.8b4df3b46c32b05a.js} +1 -1
  68. package/apps/pwa/out/_next/static/chunks/5963-c4d8469c52a0828b.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/6001.ec4049ceb3c43418.js +27 -0
  70. package/apps/pwa/out/_next/static/chunks/6120-471f03db1e299c0c.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6175-41479e643aca1878.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6291.a2de4f9787637075.js +104 -0
  73. package/apps/pwa/out/_next/static/chunks/6400-6699907c3537436a.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/6428.1f4327d6ae5fdf9a.js +287 -0
  75. package/apps/pwa/out/_next/static/chunks/644.02cb026fa44bcd0a.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6466-6c87d60d3dd174fb.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{9507.7026657a2f47eb64.js → 6540.c7bef57ad8f90a87.js} +16 -16
  78. package/apps/pwa/out/_next/static/chunks/6773.d4264665ab31bec0.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6822.3c789f1a8443e774.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/{5574.f35accf4f132b29d.js → 6944.309f4e3ea54d1537.js} +1 -1
  81. package/apps/pwa/out/_next/static/chunks/7261-8ff69d3b2ee671fd.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7266.084c6bbc70035140.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/7271.5c0100e7320aa856.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7429-bf5731358c8f2f68.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7464.01f7a0ae1215fbe7.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7479.0fcad5f37eb64f0c.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7483.427a2fe2049e388d.js +148 -0
  88. package/apps/pwa/out/_next/static/chunks/7502-be74718178e13e56.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7530.0889722b19572065.js +24 -0
  90. package/apps/pwa/out/_next/static/chunks/7645.5b2ee3ad45c55ea3.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7788.7d1c79f1a8e18946.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7900.096f455c7c1efc53.js +77 -0
  93. package/apps/pwa/out/_next/static/chunks/7910-6f139bcafd238b9f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7966.f71c8958d1637548.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{5947-135e26537d80a6b9.js → 8062-7cd0bcc7f01455ba.js} +3 -3
  96. package/apps/pwa/out/_next/static/chunks/8073.f6cf2f2c71aafbd0.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/8201.190b94088980f430.js +74 -0
  98. package/apps/pwa/out/_next/static/chunks/8205.282ca4bb62d54a0a.js +62 -0
  99. package/apps/pwa/out/_next/static/chunks/8313-dfa1021bb58a46d6.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/837-81336ebaca8198a8.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8447.7963352ef5f71b76.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8574-868e8129525ef6d9.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8588.d1c6a2903727ce87.js +149 -0
  104. package/apps/pwa/out/_next/static/chunks/8637.90f92d5c8db4732c.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{2743.5d3f76948be32b34.js → 868.6e953be05bcf6dca.js} +2 -2
  106. package/apps/pwa/out/_next/static/chunks/8706-e1fe0309343a2273.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8749-06384cdfc4520add.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/{5964-7e21164a8f2cf0ce.js → 8861-cc1fa1c7b920fa8e.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/8967-7dfebf710c9dec9d.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{5159.115f1c2ca471386c.js → 8986.8e2e4efbfa655c59.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/8997-3c564dbb3b58b055.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/923.2034d10ae91e9c20.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9312.bc1b89c5d4368033.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/943.39ae5e82f4f1893f.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9469.0500480de55fb205.js +32 -0
  116. package/apps/pwa/out/_next/static/chunks/9770.5f75bc6088b16109.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9810-2543267050e58b82.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9870.8419702475254745.js +8 -0
  119. package/apps/pwa/out/_next/static/chunks/9941-376d1b74d51b01a4.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9958.385434ab04241758.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{b2271e54.c1de3347976e90b0.js → aa57532f.510551854ff239b1.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-deea1450b17cb8dc.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-70985b904f8650ef.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a2e15e33e56a4353.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-fc72ce2db12d6ca9.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-be03e41f9436ebd9.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-35a35999382da3f2.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-3ff5b1e6ca88b848.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-a65101300a0ee5c1.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-3aca5b6d67dba511.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-45b8ab0a71e61314.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-53046e282d4cda8e.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-17809e60e8354c17.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-fd2442d347bf5898.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-e9d720d6fc082e21.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-6b8b4267bab0a677.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-deea1450b17cb8dc.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-8bcf6233ac7c17af.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-7cc25591cfc2126f.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-053e871f35c9beba.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-eff819f0c2d5e084.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-4d7e9d8db5274a87.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-2b2d8f5aa5f69ad4.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-0b8f92c5062e68b1.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1bd9d0fdac2ced3d.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-9ee28f7df07428ea.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-44b7a1ff468c6648.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-cbc6523711dc1b65.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-fdad47899c2695be.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-40f9e19149024298.js → page-79a8b5fee4c20871.js} +1 -1
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-03e8ac9606aeaffb.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-4cd55c55ec287e2f.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-db17b4305404161e.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-30041d848350fd02.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c540da022dbf86b0.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9399efc538da057f.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-a142dfb0331a05f5.js → page-ca2d09128c585ca4.js} +1 -1
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-0e2635aecd343038.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ef7f4399b6b7726e.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-deea1450b17cb8dc.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-caf530535b875468.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-eaa20c1543a5fd8e.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c541eb1b75120f61.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-ab5e213eda9479f4.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-0a815f34367b6db6.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-e858ba0ed9393b68.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-1638ee9dd6e0582f.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-4cf5a399109391f2.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b4396321238798ec.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-cb72bfd21006781a.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6135d0242afbc434.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a1dccf907eddb856.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{53aeb4ee-51c92f8d683f5220.js → c7945435-b44d4dbe90ba0dcb.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/{2fdf2931.b431eee4fb468bc3.js → cb0f8df8.09a4a6be97e3fc5f.js} +1 -1
  175. package/apps/pwa/out/_next/static/chunks/{c0c1a699.0ba79f97005965d4.js → d96660fb.cbc21a2a88539dbb.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/ea514caa.05a24f78d9dffe5f.js +122 -0
  177. package/apps/pwa/out/_next/static/chunks/{83184dcd-0ee6247ff41eb3f2.js → fb7d20d5-6892502b6ee39ca6.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/framework-e8a911e19b1be41f.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-7022f418fabba021.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-e5f15df755756a1e.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-4525f91e36940c9b.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-140f0e519c085b0f.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-8f22ef63dcb865b1.js +1 -0
  184. package/apps/pwa/out/_next/static/css/{b9d8151e48998417.css → 62c858ad53854f47.css} +1 -1
  185. package/apps/pwa/out/_next/static/css/d1f8bb401740a549.css +1 -0
  186. package/apps/pwa/out/approvals/index.html +1 -1
  187. package/apps/pwa/out/approvals/index.txt +23 -23
  188. package/apps/pwa/out/autopilot/index.html +1 -1
  189. package/apps/pwa/out/autopilot/index.txt +23 -23
  190. package/apps/pwa/out/board/index.html +1 -1
  191. package/apps/pwa/out/board/index.txt +23 -23
  192. package/apps/pwa/out/botlab/index.html +1 -1
  193. package/apps/pwa/out/botlab/index.txt +22 -22
  194. package/apps/pwa/out/bots/index.html +1 -1
  195. package/apps/pwa/out/bots/index.txt +22 -22
  196. package/apps/pwa/out/ceo/index.html +1 -1
  197. package/apps/pwa/out/ceo/index.txt +23 -23
  198. package/apps/pwa/out/chat/index.html +1 -1
  199. package/apps/pwa/out/chat/index.txt +22 -22
  200. package/apps/pwa/out/consult/index.html +1 -1
  201. package/apps/pwa/out/consult/index.txt +23 -23
  202. package/apps/pwa/out/control/index.html +1 -1
  203. package/apps/pwa/out/control/index.txt +23 -23
  204. package/apps/pwa/out/decisions/index.html +1 -1
  205. package/apps/pwa/out/decisions/index.txt +22 -22
  206. package/apps/pwa/out/design-check/index.html +1 -1
  207. package/apps/pwa/out/design-check/index.txt +23 -23
  208. package/apps/pwa/out/editor/index.html +1 -1
  209. package/apps/pwa/out/editor/index.txt +24 -25
  210. package/apps/pwa/out/exec/index.html +1 -1
  211. package/apps/pwa/out/exec/index.txt +23 -23
  212. package/apps/pwa/out/field/index.html +1 -1
  213. package/apps/pwa/out/field/index.txt +23 -23
  214. package/apps/pwa/out/healthz +1 -1
  215. package/apps/pwa/out/index.html +1 -1
  216. package/apps/pwa/out/index.txt +22 -22
  217. package/apps/pwa/out/inside/index.html +1 -1
  218. package/apps/pwa/out/inside/index.txt +23 -23
  219. package/apps/pwa/out/intake/index.html +1 -1
  220. package/apps/pwa/out/intake/index.txt +23 -23
  221. package/apps/pwa/out/live/index.html +1 -1
  222. package/apps/pwa/out/live/index.txt +24 -25
  223. package/apps/pwa/out/live-run/index.html +1 -1
  224. package/apps/pwa/out/live-run/index.txt +24 -25
  225. package/apps/pwa/out/loops/index.html +1 -1
  226. package/apps/pwa/out/loops/index.txt +22 -22
  227. package/apps/pwa/out/market/index.html +1 -1
  228. package/apps/pwa/out/market/index.txt +22 -22
  229. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  230. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  231. package/apps/pwa/out/morning/index.html +1 -1
  232. package/apps/pwa/out/morning/index.txt +23 -23
  233. package/apps/pwa/out/observatory/index.html +1 -1
  234. package/apps/pwa/out/observatory/index.txt +23 -23
  235. package/apps/pwa/out/ops/checklist/index.html +1 -1
  236. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  237. package/apps/pwa/out/ops/release/index.html +1 -1
  238. package/apps/pwa/out/ops/release/index.txt +22 -22
  239. package/apps/pwa/out/ops/seats/index.html +1 -1
  240. package/apps/pwa/out/ops/seats/index.txt +22 -22
  241. package/apps/pwa/out/outputs/index.html +1 -1
  242. package/apps/pwa/out/outputs/index.txt +22 -22
  243. package/apps/pwa/out/reflection/index.html +1 -1
  244. package/apps/pwa/out/reflection/index.txt +23 -23
  245. package/apps/pwa/out/scheduler/index.html +1 -1
  246. package/apps/pwa/out/scheduler/index.txt +23 -23
  247. package/apps/pwa/out/sessions/index.html +1 -1
  248. package/apps/pwa/out/sessions/index.txt +22 -22
  249. package/apps/pwa/out/settings/devices/index.html +1 -1
  250. package/apps/pwa/out/settings/devices/index.txt +23 -23
  251. package/apps/pwa/out/settings/index.html +1 -1
  252. package/apps/pwa/out/settings/index.txt +23 -23
  253. package/apps/pwa/out/setup/done/index.html +1 -1
  254. package/apps/pwa/out/setup/done/index.txt +23 -23
  255. package/apps/pwa/out/setup/index.html +1 -1
  256. package/apps/pwa/out/setup/index.txt +23 -23
  257. package/apps/pwa/out/share/index.html +1 -1
  258. package/apps/pwa/out/share/index.txt +23 -23
  259. package/apps/pwa/out/showroom/index.html +1 -1
  260. package/apps/pwa/out/showroom/index.txt +22 -22
  261. package/apps/pwa/out/tasks/index.html +1 -1
  262. package/apps/pwa/out/tasks/index.txt +23 -23
  263. package/apps/pwa/out/term/index.html +1 -1
  264. package/apps/pwa/out/term/index.txt +23 -23
  265. package/apps/pwa/out/today/index.html +1 -1
  266. package/apps/pwa/out/today/index.txt +23 -23
  267. package/apps/pwa/out/trace/index.html +1 -1
  268. package/apps/pwa/out/trace/index.txt +23 -23
  269. package/apps/pwa/out/vault/index.html +1 -1
  270. package/apps/pwa/out/vault/index.txt +23 -23
  271. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  272. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  273. package/apps/pwa/out/workflows/index.html +1 -1
  274. package/apps/pwa/out/workflows/index.txt +23 -23
  275. package/apps/pwa/out/workspace/index.html +1 -1
  276. package/apps/pwa/out/workspace/index.txt +23 -23
  277. package/apps/pwa/out/worktrees/index.html +1 -1
  278. package/apps/pwa/out/worktrees/index.txt +23 -23
  279. package/catalog/external-commands.yaml +5 -3
  280. package/catalog/meta/catalogVersion.yaml +2 -1
  281. package/catalog/signing/catalog-signing-public.pem +3 -0
  282. package/docs/goal-context/04-mock-scenarios.md +13 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/heal/heal-loop.yaml +1 -0
  285. package/graphs/intake/intake-daily.yaml +16 -0
  286. package/graphs/intake/news-sources.yaml +1 -0
  287. package/graphs/intake/recipes.yaml +7 -0
  288. package/graphs/overlays/examples/research-runtime-empty-terminal.yaml +28 -0
  289. package/graphs/overlays/stop-heal-ladder.yaml +113 -0
  290. package/graphs/profiles/local.yaml +5 -0
  291. package/graphs/profiles/pod.yaml +4 -0
  292. package/graphs/units/verbs/recipes.yaml +11 -11
  293. package/graphs/work/research-report.yaml +10 -2
  294. package/package.json +6 -2
  295. package/plugins/demo-absorption-briefing/plugin.json +18 -0
  296. package/plugins/demo-absorption-briefing/skills/demo-absorption-briefing/SKILL.md +8 -0
  297. package/plugins/demo-browser-hand/plugin.json +13 -0
  298. package/plugins/demo-browser-hand/skills/demo-browser-hand/SKILL.md +8 -0
  299. package/plugins/demo-linear-secretary/plugin.json +15 -0
  300. package/plugins/demo-linear-secretary/skills/demo-linear-secretary/SKILL.md +8 -0
  301. package/plugins/demo-plugin-wizard/plugin.json +11 -0
  302. package/plugins/demo-plugin-wizard/skills/demo-plugin-wizard/SKILL.md +8 -0
  303. package/plugins/demo-research/plugin.json +16 -0
  304. package/plugins/demo-research/skills/demo-research/SKILL.md +8 -0
  305. package/plugins/demo-video-briefing/plugin.json +15 -0
  306. package/plugins/demo-video-briefing/skills/demo-video-briefing/SKILL.md +8 -0
  307. package/scripts/build-signed-catalog.ts +48 -0
  308. package/scripts/ci-typecheck-main-delta.sh +88 -0
  309. package/scripts/codex-fallback-live-probe.ts +1 -1
  310. package/scripts/demo/demo1104-parallel-scenes.ts +301 -0
  311. package/scripts/demo/demo1104-stage.config.ts +21 -0
  312. package/scripts/demo/demo1104-stage.ts +316 -0
  313. package/scripts/goal-method-audit.ts +17 -1
  314. package/scripts/graph-units/land-enqueue.ts +90 -0
  315. package/scripts/heal/run-role.ts +99 -5
  316. package/scripts/hooks/codex-notify.sh +32 -0
  317. package/scripts/install.sh +10 -38
  318. package/scripts/land-global-checks.ts +6 -1
  319. package/scripts/landing-heal/land-plan.ts +57 -0
  320. package/scripts/lib/decompose-ab.ts +1 -0
  321. package/scripts/lib/fixtures/tui-regress/help-insufficient-commands.txt +15 -0
  322. package/scripts/lib/fixtures/tui-regress/help-live.txt +42 -0
  323. package/scripts/lib/fixtures/tui-regress/help-missing-keys.txt +21 -0
  324. package/scripts/lib/fixtures/tui-regress/help-original.txt +51 -0
  325. package/scripts/lib/fixtures/tui-regress/help-shifted-input.txt +26 -0
  326. package/scripts/lib/tui-regress-checks.ts +33 -13
  327. package/scripts/release-loop/auto-approve-node.ts +1 -1
  328. package/scripts/release-loop/gate-autoheal-sim-evidence.ts +60 -0
  329. package/scripts/release-loop/gate-baseline-receipt.ts +47 -9
  330. package/scripts/release-loop/gate-image-pin.ts +81 -0
  331. package/scripts/release-loop/gate-method.ts +139 -0
  332. package/scripts/release-loop/gate-node-sim.ts +207 -0
  333. package/scripts/release-loop/gate-node.ts +463 -64
  334. package/scripts/release-loop/gate-pod-image-sticky-plan.ts +99 -0
  335. package/scripts/release-loop/gate-radius-live.ts +97 -13
  336. package/scripts/release-loop/gate-shard-dispatch.ts +43 -0
  337. package/scripts/release-loop/gate-traffic-mode.ts +151 -3
  338. package/scripts/release-loop/gate-train-receipts.ts +122 -0
  339. package/scripts/release-loop/node-verdict.ts +38 -25
  340. package/scripts/release-loop/preflight.ts +362 -8
  341. package/scripts/release-loop/resume-release.ts +83 -8
  342. package/scripts/release-loop/unattended-release.ts +20 -9
  343. package/scripts/rhythm/daily.ts +36 -4
  344. package/scripts/rhythm/linear-progress-sync.ts +298 -0
  345. package/scripts/ux-sim.ts +18 -1
  346. package/scripts/video-pipeline.ts +9 -1
  347. package/scripts/warm-pool-provider-config.ts +286 -0
  348. package/skills/omni-crawl/SKILL.md +5 -2
  349. package/skills/omni-crawl/scripts/main.ts +84 -23
  350. package/src/acp/agent-manager.ts +1 -0
  351. package/src/acp/codex-app-server-agent.ts +33 -0
  352. package/src/acp/codex-app-server-client.ts +153 -16
  353. package/src/acp/core-turn-bridge.ts +27 -9
  354. package/src/acp/event-router.ts +20 -1
  355. package/src/acp/server.ts +6 -0
  356. package/src/agent/registry.ts +2 -0
  357. package/src/agent/task-notification.ts +25 -5
  358. package/src/agent/types.ts +6 -0
  359. package/src/agent-mission/browser-roundtrip-coordinator.ts +85 -0
  360. package/src/agent-mission/claude-headless.ts +4 -1
  361. package/src/agent-mission/codex-exec.ts +105 -0
  362. package/src/agent-mission/driver.ts +192 -11
  363. package/src/agent-mission/grok-auth-status.ts +47 -0
  364. package/src/agent-mission/hand-hooks.ts +101 -0
  365. package/src/agent-mission/mission-cli.ts +63 -4
  366. package/src/agent-mission/mission-hand-reply.ts +118 -0
  367. package/src/agent-mission/mission-line.ts +180 -0
  368. package/src/agent-mission/mission-out.ts +833 -0
  369. package/src/agent-mission/tool-install.ts +200 -3
  370. package/src/agent-substrate/context-strategy.ts +4 -4
  371. package/src/agent-substrate/self-review-cli.ts +178 -13
  372. package/src/agents/general-purpose.md +1 -1
  373. package/src/auto-research/research-turn.ts +211 -53
  374. package/src/autopilot/pr-manager.ts +69 -10
  375. package/src/autopilot/pty-control-loop.ts +2 -0
  376. package/src/boot/daemon-tools/self-implement.ts +3 -1
  377. package/src/boot/daemon-tools/web-search.ts +1 -2
  378. package/src/briefing/brief-items.ts +55 -11
  379. package/src/capture/tui-self-report.ts +22 -6
  380. package/src/chat/index.ts +1 -1
  381. package/src/cli/agent-cli.ts +2 -1
  382. package/src/cli/catalog-cli.ts +31 -0
  383. package/src/cli/connector-cli.ts +16 -0
  384. package/src/cli/doctor-fix.ts +2 -2
  385. package/src/cli/fixtures/review-loop-breaker/opwaiver-32646-1694ccc7a170.json +1 -0
  386. package/src/cli/fixtures/review-loop-breaker/opwaiver-32853-bd8f73595c83.json +1 -0
  387. package/src/cli/fixtures/review-loop-breaker/opwaiver-33097-1cc85900413c.json +1 -0
  388. package/src/cli/fixtures/review-loop-breaker/opwaiver-33106-972f31e4a3ca.json +1 -0
  389. package/src/cli/fixtures/review-loop-breaker/opwaiver-33113-3ab51f05968f.json +1 -0
  390. package/src/cli/fixtures/review-loop-breaker/pr-33097-1cc85900413c.json +1 -0
  391. package/src/cli/intake-source-cli.ts +44 -8
  392. package/src/cli/msg-cli.ts +32 -3
  393. package/src/cli/pr-cli.ts +21 -1
  394. package/src/cli/pr-land-heavy-remote.ts +75 -5
  395. package/src/cli/pr-land-overlap-evidence.ts +113 -2
  396. package/src/cli/pr-land-pod-shadow-job.ts +202 -4
  397. package/src/cli/pr-land-pod-shadow.ts +9 -2
  398. package/src/cli/release-checklist-read-cli.ts +57 -19
  399. package/src/cli/release-cli.ts +23 -3
  400. package/src/cli/research-cli.ts +13 -5
  401. package/src/cli/setup-cli.ts +166 -6
  402. package/src/cli/setup-keys.ts +187 -0
  403. package/src/cli/task-plugin-wizard-host.ts +114 -0
  404. package/src/cli/tasks-cli.ts +61 -9
  405. package/src/cli/tui-test-cli.ts +186 -0
  406. package/src/compact/auto.ts +8 -1
  407. package/src/config.ts +4 -2
  408. package/src/connectors/linear-daily-progress.ts +228 -0
  409. package/src/connectors/linear-projector.ts +34 -12
  410. package/src/connectors/linear.ts +151 -7
  411. package/src/control-plane/ledger.ts +52 -2
  412. package/src/control-plane/server.ts +16 -2
  413. package/src/core-turn/deep-turn-bloom.ts +67 -0
  414. package/src/core-turn/research-policy.ts +37 -0
  415. package/src/core-turn/run-core-turn.ts +606 -95
  416. package/src/core-turn/run-goal-loop.ts +18 -0
  417. package/src/core-turn/types.ts +4 -0
  418. package/src/dashboard/chat/acp-chat.ts +10 -0
  419. package/src/dashboard/classic-sidetab-mission-job.ts +161 -0
  420. package/src/dashboard/classic-sidetab-mission-pair.ts +81 -0
  421. package/src/dashboard/classic-sidetab-mission-trio.ts +219 -0
  422. package/src/dashboard/classic-sidetab-mission.ts +347 -0
  423. package/src/dashboard/index.ts +517 -18
  424. package/src/dashboard/input/agent-wake.ts +23 -1
  425. package/src/dashboard/input/chat-main-acp-dispatch.ts +23 -0
  426. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +17 -0
  427. package/src/dashboard/mission-cli-consumer.ts +165 -0
  428. package/src/dashboard/plugin-wizard-runtime.ts +50 -0
  429. package/src/dashboard/plugin-wizard-tool.ts +55 -0
  430. package/src/dashboard/seat-boot-preamble.ts +171 -1
  431. package/src/dashboard/side-basic-runtime.ts +54 -1
  432. package/src/dashboard/sidetab-runtime.ts +90 -1
  433. package/src/dashboard/skill-catalog-preamble.ts +101 -0
  434. package/src/dashboard/skill-detection-runtime.ts +55 -15
  435. package/src/dashboard/slash-runtime/dashboard-handlers.ts +15 -0
  436. package/src/dashboard/terminal-popup-runtime.ts +6 -3
  437. package/src/dashboard/turn-preamble.ts +53 -15
  438. package/src/dashboard/turn-prompt-runtime.ts +4 -2
  439. package/src/decisions/linear-progress-summary.ts +78 -0
  440. package/src/decisions/linear-progress-sync.ts +152 -0
  441. package/src/domains/outbound-alert.ts +49 -11
  442. package/src/domains/telegram-kind-route.ts +16 -9
  443. package/src/graph-runner/arg-allowance.ts +13 -0
  444. package/src/graph-runner/graph-cli.ts +23 -7
  445. package/src/graph-runner/graph-run-api.ts +3 -1
  446. package/src/graph-runner/graph-tick.ts +10 -2
  447. package/src/graph-runner/runner.ts +86 -18
  448. package/src/graph-wizard/mission-compose.ts +407 -12
  449. package/src/grok/agent-search.ts +14 -4
  450. package/src/grok/credential.ts +28 -11
  451. package/src/harness/dispatch-backpressure.ts +54 -1
  452. package/src/harness/dispatch-shadow-compare.ts +74 -0
  453. package/src/harness/harness-cli-command.ts +11 -1
  454. package/src/harness/harness-pod-dispatch.ts +81 -15
  455. package/src/harness/harness-stop.ts +155 -11
  456. package/src/harness/skill-exec.ts +21 -9
  457. package/src/harness/skill-select.ts +1 -1
  458. package/src/hooks/error-report-cloud.ts +101 -0
  459. package/src/hooks/receiver.ts +29 -2
  460. package/src/index.ts +159 -13
  461. package/src/instance/spawn-elanous.ts +6 -4
  462. package/src/intake-plane/actions.ts +354 -0
  463. package/src/intake-plane/adapters/telegram-ambient.ts +34 -18
  464. package/src/intake-plane/intake-sources.ts +78 -8
  465. package/src/intake-plane/items.ts +14 -4
  466. package/src/intake-plane/linear-mission-sync.ts +80 -0
  467. package/src/intake-plane/linear-secretary-runtime.ts +178 -0
  468. package/src/intake-plane/linear-wish.ts +227 -1
  469. package/src/life/life-cli.ts +168 -0
  470. package/src/life/life-kinds.ts +160 -0
  471. package/src/live-supply/budget-consumer.ts +78 -0
  472. package/src/live-supply/read.ts +3 -0
  473. package/src/live-supply/snapshot.ts +45 -0
  474. package/src/llm/provider-credentials.ts +8 -2
  475. package/src/llm.ts +168 -9
  476. package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
  477. package/src/loops/orchestrator/tick.ts +13 -0
  478. package/src/maturity/feature-maturity.ts +4 -4
  479. package/src/msg/msg-sse-server.ts +3 -3
  480. package/src/msg/msg-store.ts +177 -19
  481. package/src/native-tool-catalog.ts +38 -4
  482. package/src/nexus/api/harness-traversal.ts +11 -0
  483. package/src/nexus/api/http-server.ts +37 -1
  484. package/src/nexus/api/mcp-public-allowlist.ts +2 -0
  485. package/src/nexus/index.ts +7 -2
  486. package/src/nexus/outbound/format.ts +9 -0
  487. package/src/nexus/outbound/router.ts +98 -13
  488. package/src/nexus/setup-status.ts +9 -0
  489. package/src/nexus/state/state.ts +3 -0
  490. package/src/oauth/codex-account-store.ts +20 -7
  491. package/src/oauth/codex-owner-mirror.ts +44 -0
  492. package/src/oauth/codex.ts +17 -2
  493. package/src/oauth/fallback-chain.ts +47 -17
  494. package/src/onboarding/first-run-doctor.ts +8 -1
  495. package/src/onboarding/first-run.ts +85 -26
  496. package/src/onboarding/key-setup.ts +103 -0
  497. package/src/onboarding/menu-answer.ts +13 -0
  498. package/src/onboarding/secret-prompt.ts +37 -0
  499. package/src/plugins/install/plugin-install.ts +59 -0
  500. package/src/pr-tool/census.ts +133 -2
  501. package/src/pty-shell/pty-control-ipc.ts +4 -1
  502. package/src/pty-shell/tui-run-state.ts +188 -2
  503. package/src/registry/catalog-server.ts +232 -0
  504. package/src/registry/loader.ts +45 -1
  505. package/src/registry/types.ts +1 -2
  506. package/src/release-loop/checklist-target-path-hint.ts +13 -0
  507. package/src/release-loop/checklist.ts +9 -1
  508. package/src/repl/index.ts +43 -2
  509. package/src/research/axis-fanout.ts +136 -16
  510. package/src/research/bloom-exemplars.ts +3 -1
  511. package/src/research/gap-followup.ts +13 -1
  512. package/src/research/plugin-discovery.ts +36 -14
  513. package/src/research-bridge/invoke.ts +22 -5
  514. package/src/research-bridge/types.ts +2 -0
  515. package/src/self-dev/ask-launch-flow.ts +72 -1
  516. package/src/self-dev/decompose.ts +119 -5
  517. package/src/self-dev/dev-cli.ts +4 -1
  518. package/src/self-dev/dev-pipeline.ts +5 -0
  519. package/src/self-dev/draft-residue.ts +64 -5
  520. package/src/self-dev/graph-front-nodes.ts +2 -1
  521. package/src/self-dev/graph-journey-route.ts +76 -21
  522. package/src/self-dev/launch-preflight-text.ts +5 -6
  523. package/src/self-dev/launch-preflight.ts +58 -0
  524. package/src/self-dev/orchestrate.ts +13 -3
  525. package/src/self-dev/review-provider-fallback.ts +9 -2
  526. package/src/self-dev/run-supervisor.ts +42 -4
  527. package/src/self-dev/run-types.ts +4 -0
  528. package/src/self-dev/shard-aggregate.ts +4 -0
  529. package/src/self-dev/shard-run.ts +36 -15
  530. package/src/self-implement/abandoned-classification.ts +12 -3
  531. package/src/self-implement/budget-gate.ts +7 -0
  532. package/src/self-implement/child-llm-preference.ts +10 -1
  533. package/src/self-implement/doctor-visit.ts +118 -8
  534. package/src/self-implement/elanous-tui-spawn.ts +3 -0
  535. package/src/self-implement/fixtures/tdd-green-missing-passing-child-transcript.txt +2 -0
  536. package/src/self-implement/gate-artifacts.ts +2 -1
  537. package/src/self-implement/goal-author-cli.ts +4 -2
  538. package/src/self-implement/goal-author.ts +684 -7
  539. package/src/self-implement/graph-overlay-yaml.ts +37 -4
  540. package/src/self-implement/graph-overlay.ts +3 -1
  541. package/src/self-implement/graph-profile-yaml.ts +107 -0
  542. package/src/self-implement/graph-yaml.ts +26 -3
  543. package/src/self-implement/headless-elanous-driver.ts +31 -2
  544. package/src/self-implement/heal-intake.ts +112 -4
  545. package/src/self-implement/heal-triage.ts +20 -0
  546. package/src/self-implement/host-regate.ts +27 -4
  547. package/src/self-implement/merge-train.ts +5 -0
  548. package/src/self-implement/off-diff-evidence.ts +249 -5
  549. package/src/self-implement/orchestrator.ts +365 -25
  550. package/src/self-implement/regate-detail.ts +38 -1
  551. package/src/self-implement/rework-child-provider.ts +25 -0
  552. package/src/self-implement/run-chart.ts +122 -0
  553. package/src/self-implement/run-ledger.ts +162 -11
  554. package/src/self-implement/run-stop.ts +188 -4
  555. package/src/self-implement/runtime-overlay-run-steps.ts +95 -0
  556. package/src/self-implement/stop-autoheal.ts +13 -0
  557. package/src/self-implement/tdd-evidence-tags.ts +9 -0
  558. package/src/self-implement/train-gate-receipt.ts +166 -0
  559. package/src/self-implement/train-gate-remote.ts +62 -18
  560. package/src/session/chat.ts +40 -4
  561. package/src/session/session-fanout.ts +2 -0
  562. package/src/session/streaming/acp-stream-sink.ts +2 -1
  563. package/src/session/streaming/chunk-producer.ts +6 -0
  564. package/src/session/streaming/stream-compositor.ts +3 -2
  565. package/src/session-runtime/index.ts +135 -5
  566. package/src/session-runtime/retry-policy.ts +1 -1
  567. package/src/session-runtime/tier-flip.ts +3 -3
  568. package/src/setup/llm-env-detect.ts +3 -0
  569. package/src/setup/service-key-catalog.ts +46 -0
  570. package/src/setup/service-keys.ts +241 -0
  571. package/src/sidetab-core/model.ts +19 -5
  572. package/src/sidetab-core/visible-agent-mission.ts +262 -0
  573. package/src/skills/name-match.ts +20 -0
  574. package/src/skills/tools/agent.ts +53 -6
  575. package/src/skills/tools/omni-search.ts +173 -13
  576. package/src/task-agent/parent-host.ts +10 -0
  577. package/src/task-agent/pod-distributor-entry.ts +3 -0
  578. package/src/task-agent/repair-outcomes.ts +65 -0
  579. package/src/task-agent/task-hand.ts +47 -7
  580. package/src/task-orchestrator/dispatcher.ts +14 -1
  581. package/src/task-orchestrator/linear-capture-events.ts +55 -0
  582. package/src/task-orchestrator/store.ts +16 -5
  583. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +4 -0
  584. package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
  585. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +92 -1
  586. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
  587. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +113 -14
  588. package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +33 -6
  589. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +45 -9
  590. package/src/task-orchestrator/surfaces/pod-source-receive.ts +178 -16
  591. package/src/task-orchestrator/surfaces/pod-source-spec.ts +3 -3
  592. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +2 -0
  593. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +237 -41
  594. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +80 -3
  595. package/src/task-orchestrator/surfaces/self-implement-pod.ts +337 -51
  596. package/src/task-orchestrator/surfaces/self-implement.ts +27 -3
  597. package/src/task-orchestrator/surfaces/warm-reuse-evidence.ts +169 -0
  598. package/src/task-orchestrator/types.ts +14 -0
  599. package/src/telegram.ts +14 -16
  600. package/src/terminal/coding-agent.ts +14 -5
  601. package/src/terminal/session-registry.ts +1 -1
  602. package/src/tokens.ts +35 -2
  603. package/src/tool-hints/gate.ts +1 -0
  604. package/src/tool-runtime/agent-runtime.ts +2 -0
  605. package/src/tool-runtime/browser-hand-runtime.ts +381 -0
  606. package/src/tool-runtime/browser-runtime.ts +2 -1
  607. package/src/tool-runtime/elanous-linear-issue-runtime.ts +76 -0
  608. package/src/tool-runtime/elanous-linear-secretary-runtime.ts +57 -0
  609. package/src/tool-runtime/elanous-skills-list-runtime.ts +5 -1
  610. package/src/tool-runtime/graph-mcp-runtimes.ts +240 -0
  611. package/src/tool-runtime/index.ts +6 -0
  612. package/src/tool-runtime/skill-exec-runtime.ts +33 -13
  613. package/src/tool-runtime/types.ts +4 -0
  614. package/src/tui/entry.ts +38 -5
  615. package/src/tui/sidetab/attach.ts +177 -1
  616. package/src/tui/sidetab/herdr-portal.ts +225 -0
  617. package/src/tui-client/local-chat-turn.ts +26 -13
  618. package/src/tui.ts +22 -0
  619. package/src/user-config.ts +34 -9
  620. package/src/user-intent/types.ts +4 -3
  621. package/src/ux-sim/browser-hand.ts +165 -0
  622. package/src/ux-sim/draft-inventory-builder.ts +198 -0
  623. package/src/ux-sim/fake-browser-page.ts +81 -0
  624. package/src/ux-sim/fake-kubectl.ts +37 -3
  625. package/src/ux-sim/fake-linear-device.ts +31 -0
  626. package/src/ux-sim/fault-pool-cap.ts +11 -6
  627. package/src/ux-sim/graph-engine-sim.ts +47 -0
  628. package/src/ux-sim/linear-secretary.ts +216 -0
  629. package/src/ux-sim/mock-scenario-catalog.json +93 -0
  630. package/src/ux-sim/mock-scenario-catalog.ts +274 -0
  631. package/src/ux-sim/onboarding-first-run-device.ts +31 -21
  632. package/src/ux-sim/scenario.ts +185 -48
  633. package/src/ux-sim/service-key-setup.ts +50 -0
  634. package/src/ux-sim/term-slash-device.ts +74 -0
  635. package/src/ux-sim/turn-throw-sim.ts +31 -0
  636. package/src/version/packed-revision.json +1 -1
  637. package/src/voice/voice-tts-singleton.ts +18 -3
  638. package/src/web-search/exa.ts +77 -0
  639. package/src/web-search/grok.ts +5 -9
  640. package/src/web-search/index.ts +30 -9
  641. package/src/web-search/provider.ts +4 -1
  642. package/src/web-search/x-intent.ts +5 -0
  643. package/apps/pwa/out/_next/static/chunks/1115.434ae2ecb0ad986d.js +0 -149
  644. package/apps/pwa/out/_next/static/chunks/1121.667cec0bcdbb3fdb.js +0 -36
  645. package/apps/pwa/out/_next/static/chunks/1197-54da59d250a6e476.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/1245.1cfd13b26dbd71d6.js +0 -287
  647. package/apps/pwa/out/_next/static/chunks/1494-f93046ff5c5a124d.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/1609.2b312797599b8e45.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/1661.0dd330f3d89f73f3.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/167-0c121e4b47e0f9da.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/1700.cdc2bf8cc685e221.js +0 -90
  652. package/apps/pwa/out/_next/static/chunks/1703-78e46acad29f3cfc.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/1765.679b29625c8e88d4.js +0 -77
  654. package/apps/pwa/out/_next/static/chunks/1971-7efb628c471dc5a0.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/1999-8de7b7bb36801913.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/2073.689dcb2680113d76.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/2198-557d49184104d165.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/221-440ce67fa9f5c839.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/2427-8ed8e98624cbd664.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/2457-725c5827bc003795.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/2491-2718b15a5ccb75a5.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/2527-5c636a43c7cc5dde.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/2692-1027cb16fe4ccf49.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/2773.0e80b9d325fb5635.js +0 -32
  665. package/apps/pwa/out/_next/static/chunks/2873-0f3cb56c8e3528ea.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/3097.864e92bdaa2f4d15.js +0 -79
  667. package/apps/pwa/out/_next/static/chunks/3136-d6870ba63300a93e.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/3173-0289113d434e83b7.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/325-dd9208a1a0ab7ce4.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/3283.528f430dd043fb4e.js +0 -74
  671. package/apps/pwa/out/_next/static/chunks/3295.cc8cb13b8f0dae1a.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/3688-86e3b88abf18cb3b.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/3804-7220467a5473a8df.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/3808.3e1e247ce7bdf889.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/3927.b5473ad109da44c5.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/3934-98ea94832e194796.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/4156.ba4de4a3664f04b4.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/4297-239ca82f293faab0.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/4320-dcc5a7734126f4c9.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/4434.94b4c552a2d91e69.js +0 -148
  681. package/apps/pwa/out/_next/static/chunks/4579.03e4092f73950965.js +0 -226
  682. package/apps/pwa/out/_next/static/chunks/4667.97e31aa38c95c475.js +0 -309
  683. package/apps/pwa/out/_next/static/chunks/4702.4a4adc1f16d7c93b.js +0 -64
  684. package/apps/pwa/out/_next/static/chunks/4815.481501c57b3568d9.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/487-d16e698ef14662d9.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/4888-7a38bf522c9cc6fe.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/5034-30e429d6bb086881.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/5040-35c494e60dadd97d.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/50c6ab1e.81a96e419350909d.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/5154.4b6a836179356f30.js +0 -94
  691. package/apps/pwa/out/_next/static/chunks/5228-a8200df486278f2d.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/5379.97af106d09566f69.js +0 -8
  693. package/apps/pwa/out/_next/static/chunks/5479-b6372e762719f92f.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/5534-699c5cbcbc96c5d7.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/5701.9c4e8f250312eeee.js +0 -24
  696. package/apps/pwa/out/_next/static/chunks/5892.2526d962339662d8.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/5937.c2df9e3447e1f667.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/5941.8210cc93c9202c0a.js +0 -286
  699. package/apps/pwa/out/_next/static/chunks/5975.4004f820ae3cecd5.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/6142-7ccd48795d7abd55.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/6160.bf9177b8c1bfb243.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/6398.71ba0690c8027555.js +0 -27
  703. package/apps/pwa/out/_next/static/chunks/6492.2bd5c10edc1a064e.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/6552.a3840aa7da35ad68.js +0 -62
  705. package/apps/pwa/out/_next/static/chunks/6736.1e36d3ac5fa5e41d.js +0 -24
  706. package/apps/pwa/out/_next/static/chunks/6900.3ccbe069bdd4556a.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/71327eb4.add50b365f94bf3a.js +0 -122
  708. package/apps/pwa/out/_next/static/chunks/7136.deca3e3a2ddaa374.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/7139-ee9f8ed2677a40b3.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/7191.fdc955f7d96064a4.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/7423-e0aa5d9e2962120e.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/7453.afc1ea7e05d56a8c.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/7503-5e6a3d7fe67e4a0c.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/7761-476fd7bf768abd34.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/7975.eafb12097a0a9e62.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/8050-9b364bdef8f71927.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/8152.64e253c35cccfaae.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/8190.620f0231b3924bcb.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/8448.45b6b1f1a99eeb35.js +0 -104
  720. package/apps/pwa/out/_next/static/chunks/8464-e24f10451b658aac.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/8477.e4c944bb3aa5d2f3.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/8710.ca385257d5ca422e.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/8830.627ccf294ec2465c.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/8880.64c445592f04acaf.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/8981.c706964c8ba27aff.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/9053.bd0fffaff8d3f836.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/9161-c66e723d7ec9d682.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/9185.1ecdf9b65c0bb684.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/9356.fccd5f58086b4ff5.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/9446.dc0406109371d329.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/9506.dcf25d467a42627e.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/9541.eb8e2f8480c7feca.js +0 -93
  733. package/apps/pwa/out/_next/static/chunks/9577.ed7582186efa5482.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/9579.4a8169600fff2058.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/9642-ff2e039eb62e3b92.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/9659-8f2323348113ad80.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/9724.4c5623af2db43153.js +0 -34
  738. package/apps/pwa/out/_next/static/chunks/9869.81a1e24ca682d5a0.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-8d9d76ea322d855c.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed2c4e59f9f1fe21.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-2dc99e03c5d76ca0.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/board/page-b5a3a788330e920f.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/app/botlab/page-14949db884bfa672.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea76c85b36388a97.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/app/ceo/page-01716d20af62d325.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/app/chat/page-62baf6881a64b1e9.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/app/consult/page-f89ba590c2af8afa.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/app/control/page-22f8b0f78d9637e5.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/app/decisions/page-817f9d7e6820be80.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/app/design-check/page-ffb9bc0d2650249a.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/app/editor/page-f6d0e64873c0f629.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/app/exec/page-8bfa0d4ba23a209d.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/app/field/page-f5060462249e32c7.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/app/healthz/route-8d9d76ea322d855c.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2049f8b1ef79117.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/app/intake/page-1446c5fa93907f11.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/app/layout-b48a7a07c5204156.js +0 -1
  758. package/apps/pwa/out/_next/static/chunks/app/live/page-e974b8c4ca47614c.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/app/live-run/page-8a919b869bd28594.js +0 -1
  760. package/apps/pwa/out/_next/static/chunks/app/loops/page-d31b8de6bfcd72ed.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/app/market/page-64765f817909db29.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-d128c8569a36ee9b.js +0 -1
  763. package/apps/pwa/out/_next/static/chunks/app/morning/page-b2679d357e9b9270.js +0 -1
  764. package/apps/pwa/out/_next/static/chunks/app/not-found-6fabec98a9e7b269.js +0 -1
  765. package/apps/pwa/out/_next/static/chunks/app/observatory/page-048f30149dcf1349.js +0 -1
  766. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-9113181d6b5a2725.js +0 -1
  767. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-1e7a755a13227611.js +0 -1
  768. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3c8f8b67a203cf07.js +0 -1
  769. package/apps/pwa/out/_next/static/chunks/app/page-18032f5437215062.js +0 -1
  770. package/apps/pwa/out/_next/static/chunks/app/reflection/page-da3b739192fd0ee3.js +0 -1
  771. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d332545571bd6fd1.js +0 -1
  772. package/apps/pwa/out/_next/static/chunks/app/sessions/page-5237aa5b3f3b8ec5.js +0 -1
  773. package/apps/pwa/out/_next/static/chunks/app/settings/page-d16e56c3989c147e.js +0 -1
  774. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6b6593600869e2cd.js +0 -1
  775. package/apps/pwa/out/_next/static/chunks/app/setup/layout-8d9d76ea322d855c.js +0 -1
  776. package/apps/pwa/out/_next/static/chunks/app/setup/page-29e8b8a8396b55cb.js +0 -1
  777. package/apps/pwa/out/_next/static/chunks/app/share/page-b3414d856a1a7e2f.js +0 -1
  778. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c3affa52b7ce961b.js +0 -1
  779. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f21dddf9f95b6fab.js +0 -1
  780. package/apps/pwa/out/_next/static/chunks/app/term/page-f5a30a24745bf088.js +0 -1
  781. package/apps/pwa/out/_next/static/chunks/app/today/page-8c8569a09b4d4e34.js +0 -1
  782. package/apps/pwa/out/_next/static/chunks/app/trace/page-2ce32722c0243483.js +0 -1
  783. package/apps/pwa/out/_next/static/chunks/app/vault/page-4767b0d9c0a98c6c.js +0 -1
  784. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-116ee1339c387a75.js +0 -1
  785. package/apps/pwa/out/_next/static/chunks/app/workflows/page-35e8c50ca4ef7ffc.js +0 -1
  786. package/apps/pwa/out/_next/static/chunks/app/workspace/page-52e17113356bb891.js +0 -1
  787. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7458ae05ed2ac1a0.js +0 -1
  788. package/apps/pwa/out/_next/static/chunks/framework-ad6c9dd524ad6ef8.js +0 -1
  789. package/apps/pwa/out/_next/static/chunks/main-app-2ddd1f2b44d6aa59.js +0 -1
  790. package/apps/pwa/out/_next/static/chunks/main-e3d82bdc358d20c5.js +0 -1
  791. package/apps/pwa/out/_next/static/chunks/pages/_app-71d5aa3896e1655f.js +0 -1
  792. package/apps/pwa/out/_next/static/chunks/pages/_error-03ffc54e00438546.js +0 -1
  793. package/apps/pwa/out/_next/static/chunks/webpack-56bb89af5a611d0b.js +0 -1
  794. package/apps/pwa/out/_next/static/css/be9019f696217521.css +0 -1
  795. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
  796. /package/apps/pwa/out/_next/static/{GiOLWmt4MqDZePubBjA4Q → Om5xS9jpjSXJCeTNGYEi4}/_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;
@@ -61,7 +62,9 @@ export interface WarmPodProviderOptions {
61
62
  transport?: (spec: WarmPodSpec) => Pick<WarmPodTransport, 'request'>;
62
63
  maxShadowMs?: number;
63
64
  now?: () => number;
64
- kubectl?: (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
65
+ kubectl?: (args: readonly string[], input?: string) => {
66
+ status: number | null; stdout: string; stderr: string; error?: NodeJS.ErrnoException;
67
+ };
65
68
  /** Private directory for cross-process prepare leases; defaults to a per-user directory under tmpdir(). */
66
69
  prepareLockDirectory?: string;
67
70
  /** Cancels a prepare waiting for another process to finish the same host/worker slot. */
@@ -71,6 +74,11 @@ export interface WarmPodProviderOptions {
71
74
  /** Gate file this pool serves, used to bind cold prepare legs to their 1-file gate measurement key. */
72
75
  pairingGateFile?: string;
73
76
  }
77
+ type WarmPodProviderRuntimeOverrides = Pick<WarmPodProviderOptions, 'fetch' | 'kubectl' | 'prepareLockDirectory'>;
78
+ export interface WarmPodProviderLoadOptions {
79
+ pairingGateFile?: string;
80
+ runtimeOverrides?: Partial<WarmPodProviderRuntimeOverrides>;
81
+ }
74
82
 
75
83
  export interface WarmPoolReconcileResult {
76
84
  profile: string;
@@ -293,6 +301,15 @@ export class WarmPodProvider {
293
301
  return { status: response.status, value };
294
302
  }
295
303
 
304
+ private async controlBounded(path: string, credential: string, body: Record<string, unknown>, timeoutMs: number): Promise<{ status: number; value: any }> {
305
+ let timer: ReturnType<typeof setTimeout> | undefined;
306
+ try {
307
+ return await Promise.race([this.control(path, credential, body), new Promise<never>((_, reject) => {
308
+ timer = setTimeout(() => reject(new Error('warm control request timed out')), timeoutMs);
309
+ })]);
310
+ } finally { if (timer) clearTimeout(timer); }
311
+ }
312
+
296
313
  /** The child owns kubectl. Timeout terminates only this child process group and fences its lease. */
297
314
  private async runTransport(spec: WarmPodSpec, command: WarmWorkerCommand): Promise<WarmPodTransportResult> {
298
315
  if (this.options.transport) return this.options.transport(spec).request(command);
@@ -416,7 +433,8 @@ export class WarmPodProvider {
416
433
  const result = spawnSync('kubectl', argv, { encoding: 'utf8', input, env,
417
434
  timeout: argv[4] === 'wait' ? (POD_READY_WAIT_SECONDS + 30) * 1000 : 360_000,
418
435
  maxBuffer: 2 * 1024 * 1024 });
419
- return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
436
+ return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '',
437
+ error: result.error };
420
438
  };
421
439
  const parse = (result: { status: number | null; stdout: string }) => {
422
440
  if (result.status !== 0) return null;
@@ -468,12 +486,60 @@ export class WarmPodProvider {
468
486
  && Object.entries(metadata.annotations).every(([key, value]) => pod.metadata.annotations?.[key] === value)
469
487
  && pod.spec?.nodeName === nodeName
470
488
  && 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
489
+ const skipDiscovery = (reason: string): false => {
490
+ debug.log('pod.warm', 'warm-pod-list-skipped', { reason }, { level: 'warn' });
491
+ return false;
492
+ };
493
+ const kubectlErrorCode = (error: unknown): string | undefined => {
494
+ if (!error || typeof error !== 'object' || !('code' in error)) return undefined;
495
+ return typeof error.code === 'string' ? error.code : undefined;
496
+ };
497
+ // Resolve the pinned Pod independently; namespace size must not affect this read.
498
+ let namedResult: ReturnType<typeof kubectl>;
499
+ try { namedResult = kubectl(['get', 'pods', `--field-selector=metadata.name=${spec.name}`, '-o', 'json']); }
500
+ catch (error) {
501
+ return skipDiscovery(kubectlErrorCode(error) === 'ENOBUFS' ? 'output-too-large' : 'kubectl-failed');
502
+ }
503
+ if (kubectlErrorCode(namedResult.error) === 'ENOBUFS') return skipDiscovery('output-too-large');
504
+ if (namedResult.status !== 0) return skipDiscovery('kubectl-failed');
505
+ const namedList = parse(namedResult);
506
+ if (!Array.isArray(namedList?.items) || !namedList.metadata || typeof namedList.metadata !== 'object'
507
+ || Array.isArray(namedList.metadata)) return skipDiscovery('malformed-output');
508
+ const namedMatches = namedList.items.filter((row: any) => row?.metadata?.name === spec.name);
509
+ if (namedMatches.length > 1) return false;
510
+ const namedPod = namedMatches[0] ?? null;
511
+
512
+ // Kubectl's continuation token is consumed explicitly so no partial page can
513
+ // authorize a create. A hard page cap turns a non-terminating listing into failure.
514
+ const warmPods: any[] = [];
515
+ let continuation = '';
516
+ const seenContinuations = new Set<string>();
517
+ let completedPages = false;
518
+ for (let page = 0; page < 100; page++) {
519
+ const args = ['get', 'pods', '-l', 'elanous.kind=warm-worker', '--chunk-size=50', '-o', 'json'];
520
+ if (continuation) args.splice(5, 0, '--continue', continuation);
521
+ let result: ReturnType<typeof kubectl>;
522
+ try { result = kubectl(args); }
523
+ catch (error) {
524
+ return skipDiscovery(kubectlErrorCode(error) === 'ENOBUFS' ? 'output-too-large' : 'kubectl-failed');
525
+ }
526
+ if (kubectlErrorCode(result.error) === 'ENOBUFS') return skipDiscovery('output-too-large');
527
+ if (result.status !== 0) return skipDiscovery('kubectl-failed');
528
+ const listed = parse(result);
529
+ if (!listed || !Array.isArray(listed.items) || !listed.metadata || typeof listed.metadata !== 'object'
530
+ || Array.isArray(listed.metadata)
531
+ || (listed.metadata.continue !== undefined && typeof listed.metadata.continue !== 'string'))
532
+ return skipDiscovery('malformed-output');
533
+ warmPods.push(...listed.items);
534
+ continuation = listed.metadata?.continue ?? '';
535
+ if (!continuation) { completedPages = true; break; }
536
+ if (seenContinuations.has(continuation) || page === 99) return skipDiscovery('malformed-output');
537
+ seenContinuations.add(continuation);
538
+ }
539
+ if (!completedPages) return skipDiscovery('malformed-output');
540
+ const hostPods = warmPods.filter((row: any) => row?.metadata?.name === spec.name
476
541
  || row?.metadata?.annotations?.['elanous.dev/warm-worker-id'] === spec.workerId);
542
+ if (namedPod && !hostPods.some((row: any) => row?.metadata?.name === spec.name)) hostPods.push(namedPod);
477
543
  if (hostPods.length > 1) return false;
478
544
  let pod = hostPods[0];
479
545
  const preexisting = !!pod;
@@ -481,7 +547,7 @@ export class WarmPodProvider {
481
547
  if (pod && !matches(pod)) return false;
482
548
  // Old generations still occupy memory even when their ledger rows are quarantined.
483
549
  // 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'
550
+ const physical = warmPods.filter((row: any) => row?.metadata?.labels?.['elanous.kind'] === 'warm-worker'
485
551
  && row?.spec?.nodeName === nodeName && !row?.metadata?.deletionTimestamp
486
552
  && row?.status?.phase !== 'Succeeded' && row?.status?.phase !== 'Failed'
487
553
  && row?.spec?.containers?.[0]?.resources?.limits?.memory === spec.memoryLimit).length;
@@ -602,13 +668,77 @@ export class WarmPodProvider {
602
668
  const contextFailure = probeWarmPodContext(spec.kubectlContext, this.options.kubectl);
603
669
  if (contextFailure) { lastFallback = { command: null, transport: contextFailure }; continue; }
604
670
  let acquired: { status: number; value: any };
671
+ const acquisition = this.control('/v1/leases/pod-worker', this.options.memberToken, {
672
+ machine: host, key: spec.key, runId: input.runId, attemptId: input.attemptId,
673
+ ttlMs: this.options.leaseTtlMs ?? 30 * 60_000,
674
+ });
675
+ const timedOut = Symbol('warm-acquisition-timeout');
676
+ let deadlineTimer: ReturnType<typeof setTimeout> | undefined;
677
+ let acquisitionFailed = false;
678
+ let acquisitionResult: typeof timedOut | { status: number; value: any };
605
679
  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; }
680
+ const remainingMs = input.deadlineAt - this.now();
681
+ if (remainingMs <= 0) acquisitionResult = timedOut;
682
+ else acquisitionResult = await Promise.race([acquisition, new Promise<typeof timedOut>(resolve => {
683
+ deadlineTimer = setTimeout(() => resolve(timedOut), remainingMs);
684
+ })]);
685
+ } catch { acquisitionFailed = true; acquisitionResult = timedOut; }
686
+ finally { if (deadlineTimer) clearTimeout(deadlineTimer); }
687
+ if (acquisitionResult === timedOut) {
688
+ void acquisition.then(async late => {
689
+ const row = late.value;
690
+ const lateWorkerId = row?.name;
691
+ const leaseId = row?.attrs?.leaseId;
692
+ const generation = row?.attrs?.generation;
693
+ const podUid = row?.attrs?.podUid;
694
+ const lateCandidate = this.candidates.find(candidate => candidate.spec.workerId === lateWorkerId
695
+ && candidate.host === host && keyEquals(candidate.spec.key, spec.key));
696
+ if (late.status !== 200 || !lateCandidate || !row?.attrs?.key || !keyEquals(row.attrs.key, lateCandidate.spec.key)
697
+ || typeof leaseId !== 'string' || !leaseId
698
+ || !Number.isSafeInteger(generation) || typeof podUid !== 'string' || !podUid) return;
699
+ const lateSpec = lateCandidate.spec;
700
+ const command: WarmWorkerCommand = { ...input, leaseId, generation, podUid,
701
+ podName: lateSpec.name, podContext: lateSpec.kubectlContext, image: lateSpec.image,
702
+ requestedAt: new Date(this.now()).toISOString(), workerId: lateSpec.workerId, key: lateSpec.key };
703
+ debug.log('pod.warm', 'lease-response-after-deadline', { workerId: command.workerId,
704
+ leaseId, generation, podUid, runId: command.runId, attemptId: command.attemptId }, { level: 'warn' });
705
+ debug.log('pod.provenance', 'lease-start', { pod: lateSpec.name, podUid, leaseId,
706
+ runId: command.runId, attemptId: command.attemptId, requestedAt: command.requestedAt,
707
+ metadata: warmLeaseMetadata(command).annotations });
708
+ try {
709
+ const fenced = await this.controlBounded(`/v1/leases/pod-worker/${encodeURIComponent(lateWorkerId)}/timeout`,
710
+ this.options.memberToken, { runId: input.runId, attemptId: input.attemptId }, WARM_CONTROL_REQUEST_TIMEOUT_MS);
711
+ if (fenced.status !== 200) throw new Error(`warm timeout fence returned ${fenced.status}`);
712
+ } catch (error) { debug.log('pod.warm', 'late-lease-timeout-fence-failed', { workerId: lateWorkerId,
713
+ leaseId, generation, error: String(error) }, { level: 'warn' }); }
714
+ let quarantineTimer: ReturnType<typeof setTimeout> | undefined;
715
+ try {
716
+ await Promise.race([this.quarantine(command, 'lease-response-after-deadline'), new Promise<never>((_, reject) => {
717
+ quarantineTimer = setTimeout(() => reject(new Error('warm late-lease quarantine timed out')), WARM_CONTROL_REQUEST_TIMEOUT_MS);
718
+ })]);
719
+ }
720
+ catch (error) { debug.log('pod.warm', 'late-lease-quarantine-failed', { workerId: command.workerId,
721
+ leaseId, generation, error: String(error) }, { level: 'warn' }); }
722
+ finally { if (quarantineTimer) clearTimeout(quarantineTimer); }
723
+ }, error => debug.log('pod.warm', 'late-lease-response-failed', { workerId: spec.workerId,
724
+ runId: input.runId, attemptId: input.attemptId, error: String(error) }, { level: 'warn' }));
725
+ if (acquisitionFailed) continue;
726
+ return fallback('lease-unavailable');
727
+ }
728
+ acquired = acquisitionResult;
611
729
  if (acquired.status === 409 || acquired.status === 503) {
730
+ if (acquired.status === 409) {
731
+ try {
732
+ const query = new URLSearchParams({ kind: 'pod-worker-lease', machine: host, name: spec.workerId });
733
+ const observed = await this.control(`/v1/resources?${query}`, this.options.memberToken);
734
+ const row = observed.status === 200 && Array.isArray(observed.value?.resources)
735
+ ? observed.value.resources.find((entry: any) => entry?.name === spec.workerId) : undefined;
736
+ if (row?.attrs?.state === 'quarantined' || row?.attrs?.state === 'timeout-fenced') {
737
+ debug.log('pod.warm', 'quarantine-holds-slot', { workerId: spec.workerId,
738
+ state: row.attrs.state, at: new Date(this.now()).toISOString() }, { level: 'warn' });
739
+ }
740
+ } catch { /* Slot observation never changes the Job's cold fallback. */ }
741
+ }
612
742
  lastFallback = fallback('lease-unavailable');
613
743
  continue;
614
744
  }
@@ -620,6 +750,16 @@ export class WarmPodProvider {
620
750
  if (!workerId(row?.name) || typeof leasedId !== 'string' || !leasedId
621
751
  || typeof podUid !== 'string' || !podUid
622
752
  || !Number.isSafeInteger(generation) || generation < 1) return fallback('lease-unavailable');
753
+ // One terminal transition per workerId·leaseId·generation·Pod UID. A lease the
754
+ // control plane already settled (finalize/quarantine raced this acquisition)
755
+ // must never be re-adopted — not by this late reply, not by the next warm request.
756
+ // An uncertain ending keeps its quarantined / cold-Job verdict.
757
+ const terminalKey = JSON.stringify([row.name, leasedId, generation, podUid]);
758
+ if (this.terminalLeases.has(terminalKey) || this.quarantineRequested.has(terminalKey)) {
759
+ debug.log('pod.warm', 'lease-terminal-refused', { workerId: row.name, leaseId: leasedId,
760
+ generation, podUid, runId: input.runId, attemptId: input.attemptId }, { level: 'warn' });
761
+ return fallback('lease-unavailable');
762
+ }
623
763
  // Acquisition selects by host/key, not by worker ID. Resolve the returned worker
624
764
  // before choosing its transport; an unknown worker must not be executed or fenced.
625
765
  const leasedCandidate = this.candidates.find(entry => entry.host === host
@@ -641,6 +781,20 @@ export class WarmPodProvider {
641
781
  podName: leasedCandidate.spec.name, podContext: leasedCandidate.spec.kubectlContext, image: leasedCandidate.spec.image,
642
782
  requestedAt: new Date(this.now()).toISOString(),
643
783
  workerId: row.name, key: leasedCandidate.spec.key };
784
+ if (command.deadlineAt <= this.now()) {
785
+ debug.log('pod.warm', 'lease-response-after-deadline', { workerId: command.workerId,
786
+ leaseId: command.leaseId, generation: command.generation, podUid: command.podUid,
787
+ runId: command.runId, attemptId: command.attemptId }, { level: 'warn' });
788
+ // The control plane granted a lease, even though the caller's budget expired before Pod adoption.
789
+ // Record its short lifetime as a paired start/end and quarantine it without running transport.
790
+ debug.log('pod.provenance', 'lease-start', { pod: leasedCandidate.spec.name, podUid: command.podUid,
791
+ leaseId: command.leaseId, runId: command.runId, attemptId: command.attemptId,
792
+ requestedAt: command.requestedAt, metadata: warmLeaseMetadata(command).annotations });
793
+ try { await this.quarantine(command, 'lease-response-after-deadline'); }
794
+ catch (error) { debug.log('pod.warm', 'late-lease-quarantine-failed', { workerId: command.workerId,
795
+ leaseId: command.leaseId, generation: command.generation, error: String(error) }, { level: 'warn' }); }
796
+ return fallback('lease-unavailable');
797
+ }
644
798
  // Acquisition is by pool key. A healthy sibling or replacement Pod must remain eligible.
645
799
  // Never run transport on the old Pod while its previous lease is being quarantined.
646
800
  if (this.quarantiningPods.has(JSON.stringify([command.workerId, command.podUid]))) {
@@ -710,20 +864,26 @@ export class WarmPodProvider {
710
864
  }
711
865
 
712
866
  private async finalizeOnce(command: WarmWorkerCommand): Promise<'sanitized' | 'quarantined'> {
867
+ const key = JSON.stringify([command.workerId, command.leaseId, command.generation, command.podUid]);
713
868
  const candidate = this.candidates.find(row => row.spec.workerId === command.workerId);
714
869
  if (!candidate) throw new Error('warm finalize candidate missing');
715
870
  const released = await this.transition(command, 'release');
716
871
  const machineToken = this.options.machineTokens?.[candidate.host];
717
872
  // 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)
873
+ // Always verify and clear this exact Pod UID/lease metadata before telling the control plane it is reusable.
874
+ // A missing local adoption record is not evidence that no pod-side leased state exists.
875
+ const cleared = released
876
+ ? patchWarmLeaseMetadata(candidate.spec, command, 'idle', this.options.kubectl)
719
877
  : { ok: false, reason: 'release-failed' };
720
878
  if (!cleared.ok) debug.log('pod.provenance', 'lease-patch-refused', { pod: candidate.spec.name,
721
879
  podUid: command.podUid, leaseId: command.leaseId, reason: cleared.reason }, { level: 'warn' });
722
880
  if (!cleared.ok || !token(machineToken) || !await this.transition(command, 'sanitize', machineToken)) {
723
- await this.quarantineOnce(command, 'sanitize-unavailable', true);
881
+ if (!this.quarantineRequested.has(key)) {
882
+ this.quarantineRequested.add(key);
883
+ await this.quarantineOnce(command, 'sanitize-unavailable', true);
884
+ }
724
885
  return 'quarantined';
725
886
  }
726
- const key = JSON.stringify([command.workerId, command.leaseId, command.generation, command.podUid]);
727
887
  if (!this.quarantineRequested.has(key)) {
728
888
  debug.log('pod.provenance', 'lease-end', { pod: candidate.spec.name, podUid: command.podUid,
729
889
  leaseId: command.leaseId, runId: command.runId, attemptId: command.attemptId,
@@ -740,19 +900,21 @@ export class WarmPodProvider {
740
900
  this.quarantiningPods.add(podKey);
741
901
  const prior = this.terminalLeases.get(key);
742
902
  if (prior) {
903
+ if (this.quarantineRequested.has(key)) {
904
+ try { await prior; this.quarantiningPods.delete(podKey); }
905
+ catch (error) { if (this.terminalLeases.get(key) === prior) this.terminalLeases.delete(key); throw error; }
906
+ return;
907
+ }
743
908
  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
- });
909
+ // Fence the control-plane lease immediately. Waiting for sanitize to settle leaves a window
910
+ // where another request could acquire the just-idled worker before this quarantine lands.
911
+ const terminal = this.quarantineOnce(command, reason, leaseStarted).then(() => 'quarantined' as const);
751
912
  this.terminalLeases.set(key, terminal);
752
913
  try { await terminal; this.quarantiningPods.delete(podKey); }
753
914
  catch (error) { if (this.terminalLeases.get(key) === terminal) this.terminalLeases.delete(key); throw error; }
754
915
  return;
755
916
  }
917
+ this.quarantineRequested.add(key);
756
918
  const terminal = this.quarantineOnce(command, reason, leaseStarted).then(() => 'quarantined' as const);
757
919
  this.terminalLeases.set(key, terminal);
758
920
  try { await terminal; this.quarantiningPods.delete(podKey); }
@@ -783,7 +945,11 @@ let configuredProvider: WarmPodProvider | null = null;
783
945
  /** The trusted producer configures this once; an absent provider fails closed without creating a Pod. */
784
946
  export function configureWarmPodProvider(provider: WarmPodProvider | null): void { configuredProvider = provider; }
785
947
  /** Optional private producer config; a missing/unsafe file leaves the Job path untouched. */
786
- export function loadWarmPodProvider(path: string, pairingGateFile?: string): WarmPodProvider {
948
+ export function loadWarmPodProvider(path: string, pairingGateFileOrOptions?: string | WarmPodProviderLoadOptions): WarmPodProvider {
949
+ const pairingGateFile = typeof pairingGateFileOrOptions === 'string'
950
+ ? pairingGateFileOrOptions : pairingGateFileOrOptions?.pairingGateFile;
951
+ const runtimeOverrides = typeof pairingGateFileOrOptions === 'object'
952
+ ? pairingGateFileOrOptions?.runtimeOverrides ?? {} : {};
787
953
  if (!isAbsolute(path) || (statSync(path).mode & 0o077) !== 0) throw new Error('warm provider config must be private');
788
954
  const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'));
789
955
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('invalid warm provider config');
@@ -797,7 +963,7 @@ export function loadWarmPodProvider(path: string, pairingGateFile?: string): War
797
963
  if (!pairingGateFile.trim()) throw new Error('warm provider pairing gate file must not be empty');
798
964
  options.pairingGateFile = pairingGateFile;
799
965
  }
800
- return new WarmPodProvider(options);
966
+ return new WarmPodProvider({ ...options, ...runtimeOverrides });
801
967
  }
802
968
  function currentProvider(): WarmPodProvider | null {
803
969
  if (configuredProvider) return configuredProvider;
@@ -851,35 +1017,55 @@ export function parseWarmPodProviderArgs(argv: readonly string[]): { action: '--
851
1017
  throw usage();
852
1018
  }
853
1019
 
854
- if (import.meta.main) {
855
- const { action, workerId, gateFile } = parseWarmPodProviderArgs(process.argv.slice(2));
1020
+ export interface WarmPodProviderCliDependencies {
1021
+ providerRuntime?: Partial<WarmPodProviderRuntimeOverrides>;
1022
+ write?: (line: string) => void;
1023
+ registerLogSink?: (name: string) => Promise<void>;
1024
+ sleep?: (milliseconds: number) => Promise<void>;
1025
+ }
1026
+
1027
+ export async function runWarmPodProviderCli(argv: readonly string[] = process.argv.slice(2),
1028
+ dependencies: WarmPodProviderCliDependencies = {}): Promise<{
1029
+ exitCode: number;
1030
+ result?: WarmPoolReconcileResult | null;
1031
+ prepared?: boolean;
1032
+ }> {
1033
+ const { action, workerId, gateFile } = parseWarmPodProviderArgs(argv);
1034
+ const write = dependencies.write ?? ((line: string) => { process.stdout.write(line); });
856
1035
  const file = process.env.ELANOUS_WARM_POD_PROVIDER_CONFIG;
857
1036
  // 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);
1037
+ const load = () => {
1038
+ if (!file) return null;
1039
+ return loadWarmPodProvider(file, { pairingGateFile: gateFile, runtimeOverrides: dependencies.providerRuntime });
1040
+ };
859
1041
  if (action === '--pairs') {
860
1042
  // Read-only: join the cold CLI legs and the warm gate legs both processes wrote to logs.db.
861
1043
  try {
862
1044
  const storeMod = await import('../../mss/logging/log-store.js');
863
1045
  const dbPath = storeMod.logsDbPath();
864
1046
  // 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');
1047
+ if (!existsSync(dbPath)) write(JSON.stringify({ warmReuseTimePairs: [] }) + '\n');
866
1048
  else {
867
1049
  const store = storeMod.LogStore.openReadOnly(dbPath);
868
- try { process.stdout.write(JSON.stringify({ warmReuseTimePairs: readWarmReuseTimePairs(store) }) + '\n'); }
1050
+ try { write(JSON.stringify({ warmReuseTimePairs: readWarmReuseTimePairs(store) }) + '\n'); }
869
1051
  finally { store.close(); }
870
1052
  }
871
1053
  } catch (error) {
872
- process.stdout.write(JSON.stringify({ warmReuseTimePairs: null, reason: 'logs-unavailable', detail: String(error) }) + '\n');
873
- process.exitCode = 2;
1054
+ write(JSON.stringify({ warmReuseTimePairs: null, reason: 'logs-unavailable', detail: String(error) }) + '\n');
1055
+ return { exitCode: 2 };
874
1056
  }
1057
+ return { exitCode: 0 };
875
1058
  }
876
1059
  // 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'); }
1060
+ try {
1061
+ if (dependencies.registerLogSink) await dependencies.registerLogSink('pod-warm');
1062
+ else await (await import('../../domains/standalone-log-sink.js')).registerStandaloneLogSink('pod-warm');
1063
+ }
878
1064
  catch { /* the file trail stays the source of truth */ }
879
1065
  // This process's own legs, echoed for the caller; the pair itself is read with --pairs.
880
1066
  const emitPairs = (provider: WarmPodProvider) => {
881
1067
  const legs = provider.warmReuseTimeLegs();
882
- if (legs.length) process.stdout.write(JSON.stringify({ warmReuseTimeLegs: legs }) + '\n');
1068
+ if (legs.length) write(JSON.stringify({ warmReuseTimeLegs: legs }) + '\n');
883
1069
  };
884
1070
  if (action === '--serve') {
885
1071
  // Reloading the private config lets the owner rotate a quarantined slot to a new worker ID.
@@ -888,30 +1074,40 @@ if (import.meta.main) {
888
1074
  try {
889
1075
  const provider = load();
890
1076
  const result = provider ? await provider.reconcilePool() : null;
891
- process.stdout.write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
1077
+ write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
892
1078
  if (provider) emitPairs(provider);
893
1079
  } catch {
894
1080
  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');
1081
+ write(JSON.stringify({ target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
896
1082
  }
897
- await Bun.sleep(60_000);
1083
+ await (dependencies.sleep ?? Bun.sleep)(60_000);
898
1084
  }
1085
+ return { exitCode: 0 };
899
1086
  }
900
1087
  if (action === '--reconcile') {
901
1088
  let result: WarmPoolReconcileResult | null = null;
902
1089
  let provider: WarmPodProvider | null = null;
903
1090
  try { provider = load(); result = provider ? await provider.reconcilePool() : null; }
904
1091
  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');
1092
+ write(JSON.stringify(result ?? { target: 0, ready: 0, failed: ['config-unavailable'] }) + '\n');
906
1093
  if (provider) emitPairs(provider);
907
- if (!result || result.ready !== result.target) process.exitCode = 2;
1094
+ return { exitCode: !result || result.ready !== result.target ? 2 : 0, result };
908
1095
  } else if (action === '--prepare') {
909
1096
  let prepared = false;
910
1097
  let provider: WarmPodProvider | null = null;
911
1098
  try { provider = load(); prepared = provider ? await provider.prepareWorker(workerId!) : false; }
912
1099
  catch { /* An invalid private config is a failed preparation, never a Pod mutation. */ }
913
- process.stdout.write(JSON.stringify({ workerId, prepared }) + '\n');
1100
+ write(JSON.stringify({ workerId, prepared }) + '\n');
914
1101
  if (provider) emitPairs(provider);
915
- if (!prepared) process.exitCode = 2;
1102
+ return { exitCode: prepared ? 0 : 2, prepared };
1103
+ }
1104
+ return { exitCode: 2 };
1105
+ }
1106
+
1107
+ if (import.meta.main) {
1108
+ try { process.exitCode = (await runWarmPodProviderCli()).exitCode; }
1109
+ catch (error) {
1110
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
1111
+ process.exitCode = 2;
916
1112
  }
917
1113
  }
@@ -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
  }