elanous 0.2.29 → 0.2.32

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 (775) 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/{cIpv5nU9AycwYWZI9H589 → IVKr3AlRUVLKsw2fh7eXF}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{058a98ba.7eb28d5e6dc03565.js → 0bb0ff36.b8d418980951e5ba.js} +7 -7
  5. package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +27 -0
  8. package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +287 -0
  9. package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +62 -0
  10. package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/{6013-2f769370d8b4c2c6.js → 1335-6731f392c35216eb.js} +3 -3
  12. package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/{8332.6f0f49df772e163b.js → 1654.32de4b324981a188.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +149 -0
  17. package/apps/pwa/out/_next/static/chunks/{8795.32573a185af18c7b.js → 1913.7cc6fb6055b1ed3f.js} +2 -2
  18. package/apps/pwa/out/_next/static/chunks/{419.736c0d6f426249a7.js → 2001.6c7e0fd02190886c.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{6767.1fcfc9b8be83975f.js → 2213.2ccbada92c33ea0f.js} +16 -16
  24. package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/{4759.1f76713aefb87db5.js → 2871.0bca6137343b3f76.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/{6776.06ff1e3b464cc607.js → 2898.fd781bc7cf01a7b7.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/{1782-59db1b5aa06df38a.js → 2940-619bfd8ef337c611.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/{8471.8d2a8c74f09c36ff.js → 2985.39ccdecd36848b12.js} +4 -4
  31. package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +115 -0
  32. package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +64 -0
  33. package/apps/pwa/out/_next/static/chunks/{bc324b7c.02a09dd93f6a399b.js → 3267c0e6.d34854e7b3984a6e.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3278.2e5d15b53a4091c8.js +2 -0
  35. package/apps/pwa/out/_next/static/chunks/{7725-4f83d5541fbdb81b.js → 3541-ce3a5424ed5ad7f1.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3902.e1e71aaac850a045.js +34 -0
  39. package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{46bd8bea.2f4b4099172fcdb7.js → 409f1e57.d0649fc47691ebfe.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/4111.2bfedbb233ad206b.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{1142.65d002789ab3af7d.js → 4174.92ed04922709ac9e.js} +2 -2
  43. package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +36 -0
  45. package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +286 -0
  49. package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +77 -0
  50. package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +226 -0
  51. package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +79 -0
  53. package/apps/pwa/out/_next/static/chunks/5173-233188df2f42c626.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +148 -0
  57. package/apps/pwa/out/_next/static/chunks/5307.60f2209e809bd627.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/{3396-97b97928c49190c9.js → 5458-16cd7a2b51b74c8c.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/{3403.7199205e65124486.js → 5525.86debf231e181813.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{9143.ff14585db1d275ff.js → 5543.a438b8ab5a13ea47.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +74 -0
  65. package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +94 -0
  67. package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{8398.73a19b39715eda04.js → 6092.a7f99e68b188ca66.js} +1 -1
  70. package/apps/pwa/out/_next/static/chunks/60fb96c9.360b01d8d4927b7b.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/{5591.a35e7a217734a8b9.js → 6157.0c9af021de4fb31b.js} +2 -2
  72. package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/{7375.050ea0d72968e49f.js → 6433.fc573af22bbc9ab7.js} +17 -17
  75. package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +90 -0
  77. package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +32 -0
  78. package/apps/pwa/out/_next/static/chunks/6544cb48.a87fe20ddf137be2.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +104 -0
  80. package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +24 -0
  85. package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +93 -0
  86. package/apps/pwa/out/_next/static/chunks/69-0a2c8209c6d4a158.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7371.80c05c653b08e907.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7391.ebae168025c91b6a.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/{1852.7f7b53991fcb99ef.js → 7502.ccd5b8065545edd0.js} +2 -2
  94. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +8 -0
  95. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{6534.1ab00f370db7b105.js → 7862.30a8d6482bb238eb.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7947-d0759430f8042240.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/{285adb1a.5654e605dddf86d2.js → 7a1887c4.f36b88fba31da8fd.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/8018.a32663f04a6fc5f9.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +24 -0
  110. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/{1516.dfaceff3fd2d0e0d.js → 9138.b6e80031afb1d973.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/{9907.49be0fcadaf214b5.js → 9221.7d22eaf353a2d4e7.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/{8767-2f6a634f5abeaa01.js → 9279-d28c5b7754d010ae.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/{3160-95c96f4d2a21dd26.js → 9398-6aed28f0a96d0d6f.js} +1 -1
  118. package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{328bb3ab-6628a1114fe3fb3b.js → 997ce8a5-2ad501927003b41b.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/{6853.cc5174a9dee59593.js → 9989.3694300e5f000b14.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/control/page-a27b1e5a0d1b308f.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/morning/page-32866485913d6a04.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-8c66a3b24a745630.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/{page-29900ed0011b2c83.js → page-c219f2ed171131be.js} +1 -1
  163. package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/term/page-04d00ae9b0890349.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/{425cfc4d.fb91158e3971c39f.js → b313b242.cb1265eea908db61.js} +1 -1
  175. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +122 -0
  176. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +1 -0
  184. package/apps/pwa/out/_next/static/css/b5d88eba78a3d7bf.css +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +23 -23
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +22 -22
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +26 -23
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +23 -23
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/bin/elanous.mjs +7 -3
  279. package/graphs/context/nightly-condense.yaml +1 -0
  280. package/graphs/implement-loop.yaml +5 -0
  281. package/graphs/intake/intake-daily.yaml +8 -1
  282. package/graphs/intake/recipes.yaml +5 -0
  283. package/graphs/lecture/lecture-note.yaml +2 -2
  284. package/graphs/ops/harness-gc.yaml +1 -0
  285. package/graphs/release/recipes.yaml +3 -0
  286. package/graphs/release/release-loop.yaml +9 -5
  287. package/graphs/units/recipes.yaml +3 -0
  288. package/graphs/units/release-gate-recheck.yaml +15 -0
  289. package/package.json +2 -1
  290. package/scripts/check-native-tool-wiring.ts +3 -2
  291. package/scripts/ci-android-unit-tests.ts +16 -0
  292. package/scripts/ci-typecheck-changed.ts +63 -10
  293. package/scripts/coord-channel-watch.sh +22 -16
  294. package/scripts/coord-role-pack.ts +81 -2
  295. package/scripts/cron-run.ts +96 -3
  296. package/scripts/detect-test-interference.ts +38 -5
  297. package/scripts/gate-pr-cache.ts +65 -0
  298. package/scripts/gh-surface/capture.ts +126 -0
  299. package/scripts/gh-surface/gh +40 -0
  300. package/scripts/import-graph-scc.ts +38 -0
  301. package/scripts/install.ps1 +19 -0
  302. package/scripts/intake-social.ts +181 -0
  303. package/scripts/lecture-note/node.ts +71 -4
  304. package/scripts/lib/android-toolchain.ts +32 -0
  305. package/scripts/lib/deterministic-env.ts +62 -0
  306. package/scripts/lib/gate-test-env.ts +19 -0
  307. package/scripts/measure-goal-grounding-corpus.ts +2 -1
  308. package/scripts/measure-si-task-cpu.ts +143 -0
  309. package/scripts/ratchets/test-interference-patterns.allowlist.txt +255 -0
  310. package/scripts/ratchets/test-interference-patterns.ts +367 -0
  311. package/scripts/release-loop/cut-branch.ts +4 -3
  312. package/scripts/release-loop/export-check-node.ts +0 -1
  313. package/scripts/release-loop/fixtures/warm-failure-assertion/sample.junit.xml +8 -0
  314. package/scripts/release-loop/fixtures/warm-job-029/sample.json +1 -0
  315. package/scripts/release-loop/fixtures/warm-job-029/sample.junit.xml +10 -0
  316. package/scripts/release-loop/gate-closure.ts +12 -8
  317. package/scripts/release-loop/gate-diff.ts +1 -1
  318. package/scripts/release-loop/gate-host-mirror.ts +66 -0
  319. package/scripts/release-loop/gate-node.ts +523 -75
  320. package/scripts/release-loop/gate-partial.ts +106 -9
  321. package/scripts/release-loop/gate-pr-cache.ts +66 -0
  322. package/scripts/release-loop/gate-prewarm-plan.ts +66 -0
  323. package/scripts/release-loop/gate-r2-verdict.ts +189 -0
  324. package/scripts/release-loop/gate-recheck-plan-node.ts +52 -0
  325. package/scripts/release-loop/gate-warm-artifacts.ts +253 -0
  326. package/scripts/release-loop/import-graph-scc-run.ts +54 -0
  327. package/scripts/release-loop/import-graph-scc.ts +215 -0
  328. package/scripts/release-loop/node-verdict.ts +2 -0
  329. package/scripts/release-loop/prepare-node.ts +102 -4
  330. package/scripts/release-loop/probe-pod-host-dirs-live.ts +113 -0
  331. package/scripts/release-loop/resume-release.ts +33 -4
  332. package/scripts/release-loop/resume.ts +13 -5
  333. package/scripts/release-loop/spawn-lane.ts +2 -19
  334. package/scripts/release-loop/tui-sim-node.ts +3 -2
  335. package/scripts/release-loop/unattended-release.ts +16 -0
  336. package/scripts/test-deterministic.ts +5 -61
  337. package/scripts/train-reach-grouping.ts +48 -0
  338. package/scripts/tsc-incremental-cache.ts +86 -0
  339. package/scripts/tsc-incremental-diagnostics.ts +76 -0
  340. package/scripts/tsc-main-safety-net.ts +2 -2
  341. package/src/acp/client.ts +4 -1
  342. package/src/acp/core-turn-bridge.ts +65 -8
  343. package/src/acp/elanous-extensions.ts +3 -0
  344. package/src/acp/server.ts +3 -0
  345. package/src/acp/tool-trace.ts +186 -11
  346. package/src/ad-pipeline/run.ts +4 -2
  347. package/src/agent/runner.ts +169 -3
  348. package/src/agent/self-ambient-cache.ts +156 -0
  349. package/src/agent/self-ambient.ts +8 -1
  350. package/src/agent/subagent-callable.ts +3 -2
  351. package/src/agent/task-notification.ts +5 -0
  352. package/src/agent-mission/review-loop.ts +6 -3
  353. package/src/agent-substrate/acp-implementer.ts +14 -2
  354. package/src/agent-substrate/pr-reviewer.ts +14 -0
  355. package/src/agent-substrate/self-review-cli.ts +30 -4
  356. package/src/ask-user-question/tool.ts +22 -0
  357. package/src/autopilot/build/llm-conflict-merge.ts +56 -10
  358. package/src/autopilot/pr-manager.ts +80 -7
  359. package/src/briefing/brief-items.ts +24 -22
  360. package/src/capture/frame-state-detect.ts +39 -0
  361. package/src/capture/tui-self-report.ts +149 -6
  362. package/src/chat/chat-cli.ts +10 -2
  363. package/src/chat/index.ts +138 -26
  364. package/src/chat/input-control-key.ts +5 -3
  365. package/src/chat/pickers/modal-runtime.ts +6 -0
  366. package/src/chat/pickers/modals.ts +8 -2
  367. package/src/chat/tool-interrupt-behavior.ts +38 -0
  368. package/src/chat/tool-render/dispatch.ts +5 -1
  369. package/src/chat/turn-typeahead.ts +75 -20
  370. package/src/cli/__fixtures__/agent-help-before/agent.txt +4 -2
  371. package/src/cli/agent-cli.ts +41 -13
  372. package/src/cli/ask-bare.ts +1 -0
  373. package/src/cli/config-audit.ts +123 -0
  374. package/src/cli/decisions-cli.ts +80 -6
  375. package/src/cli/import-cli.ts +12 -0
  376. package/src/cli/log-event-names.ts +17 -0
  377. package/src/cli/mcp-login.ts +21 -5
  378. package/src/cli/msg-cli.ts +89 -0
  379. package/src/cli/pod-cli.ts +14 -3
  380. package/src/cli/pr-cli.ts +273 -81
  381. package/src/cli/pr-land-base-read.ts +34 -0
  382. package/src/cli/pr-land-heavy-remote.ts +82 -26
  383. package/src/cli/pr-land-overlap-evidence.ts +109 -11
  384. package/src/cli/pr-land-pod-shadow-job.ts +106 -0
  385. package/src/cli/pr-land-pod-shadow.ts +16 -3
  386. package/src/cli/pr-land-refusal.ts +20 -5
  387. package/src/cli/pr-land-virtual-rebase.ts +108 -0
  388. package/src/cli/pty-takeover-cli.ts +88 -14
  389. package/src/cli/release-cli.ts +80 -14
  390. package/src/cli/seat-cli.ts +138 -3
  391. package/src/cli/self-update.ts +5 -2
  392. package/src/cli/sim-cli.ts +18 -0
  393. package/src/cli/tasks-cli.ts +97 -19
  394. package/src/context-bus/context-first.ts +13 -0
  395. package/src/context-bus/context-now.ts +13 -2
  396. package/src/context-bus/ops-events.ts +79 -0
  397. package/src/control-plane/lease-holder.ts +12 -8
  398. package/src/control-plane/ledger.ts +163 -4
  399. package/src/control-plane/server.ts +91 -11
  400. package/src/core-turn/run-core-turn.ts +2 -0
  401. package/src/core-turn/run-goal-loop.ts +243 -3
  402. package/src/core-turn/seat-turn-cap.ts +79 -0
  403. package/src/core-turn/short-question.ts +38 -6
  404. package/src/core-turn/types.ts +6 -0
  405. package/src/dashboard/acp-active-model.ts +42 -1
  406. package/src/dashboard/acp-boot.ts +3 -0
  407. package/src/dashboard/background-jobs-status.ts +66 -0
  408. package/src/dashboard/index.ts +339 -657
  409. package/src/dashboard/input/agent-wake.ts +242 -0
  410. package/src/dashboard/input/chat-main-entry-runtime.ts +6 -1
  411. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +4 -1
  412. package/src/dashboard/input/chat-main-session.ts +8 -1
  413. package/src/dashboard/input/chat-main-turn.ts +12 -3
  414. package/src/dashboard/input/compact-prepass-recovery.ts +33 -0
  415. package/src/dashboard/input/external-input-host.ts +49 -9
  416. package/src/dashboard/input/internal-turn.ts +261 -0
  417. package/src/dashboard/input/mouse-wiring.ts +108 -11
  418. package/src/dashboard/input/seat-autowake.ts +318 -0
  419. package/src/dashboard/input/turn-steer-bridge.ts +70 -0
  420. package/src/dashboard/log-click-runtime.ts +118 -2
  421. package/src/dashboard/loop-scheduler.ts +72 -0
  422. package/src/dashboard/seat-boot-preamble.ts +18 -0
  423. package/src/dashboard/skill-runtime.ts +2 -0
  424. package/src/dashboard/slash-runtime/dashboard-handlers.ts +126 -1181
  425. package/src/dashboard/slash-runtime/help-from-registry.ts +2 -1
  426. package/src/dashboard/slash-runtime/index.ts +13 -0
  427. package/src/dashboard/turn-busy-teardown.ts +50 -0
  428. package/src/dashboard/turn-preamble.ts +116 -40
  429. package/src/dashboard/turn-session-layers.ts +4 -2
  430. package/src/dashboard/turn-start-segments.ts +222 -0
  431. package/src/decisions/decision-ledger.ts +45 -4
  432. package/src/discord-self-message.ts +4 -4
  433. package/src/display/prompt-row-owner.ts +146 -0
  434. package/src/display/resize-listener.ts +33 -2
  435. package/src/domains/release-tool.ts +18 -1
  436. package/src/field-note/transcribe.ts +17 -0
  437. package/src/fold-stack.ts +4 -2
  438. package/src/git-fs/index.ts +263 -36
  439. package/src/github/remote-call.ts +81 -0
  440. package/src/goals/registry.ts +46 -6
  441. package/src/goals/types.ts +7 -1
  442. package/src/graph-runner/runner.ts +1 -0
  443. package/src/grok/credential.ts +26 -0
  444. package/src/harness/gate-reserve.ts +87 -0
  445. package/src/harness/harness-clean.ts +2 -1
  446. package/src/harness/harness-cli-command.ts +52 -5
  447. package/src/harness/harness-gc.ts +2 -1
  448. package/src/harness/harness-incidents.ts +70 -20
  449. package/src/harness/harness-pod-dispatch.ts +87 -10
  450. package/src/harness/harness-queue.ts +68 -4
  451. package/src/harness/harness-stop.ts +4 -3
  452. package/src/harness/harness-substrate-default.ts +78 -7
  453. package/src/harness/harness-worktree-auto.ts +2 -1
  454. package/src/harness/harness-worktrees.ts +3 -2
  455. package/src/hooks/dispatch.ts +2 -1
  456. package/src/index.ts +156 -27
  457. package/src/input/external-input-queue.ts +64 -7
  458. package/src/intake-plane/slash.ts +14 -3
  459. package/src/keybindings.ts +4 -1
  460. package/src/llm/route-decision.ts +36 -0
  461. package/src/llm/tool-loop-steered-rejection.ts +41 -0
  462. package/src/llm-request-shape.ts +209 -0
  463. package/src/llm.ts +301 -283
  464. package/src/loops/consensus.ts +348 -0
  465. package/src/loops/msg-wake.ts +206 -4
  466. package/src/loops/observe.ts +19 -3
  467. package/src/loops/orchestrator/tick.ts +88 -12
  468. package/src/loops/orchestrator/traffic.ts +64 -1
  469. package/src/loops/registry.ts +30 -0
  470. package/src/market/signed-index.ts +14 -0
  471. package/src/maturity/feature-maturity.ts +1 -1
  472. package/src/mcp/mcp-oauth.ts +54 -5
  473. package/src/models/prompts.ts +28 -0
  474. package/src/msg/msg-sse-client.ts +223 -0
  475. package/src/msg/msg-store.ts +86 -2
  476. package/src/mss/logging/log-store.ts +225 -1
  477. package/src/mss/logging/render-categories.ts +5 -0
  478. package/src/nexus/DAEMON-THIN-inventory-2026-10-15.md +122 -0
  479. package/src/nexus/api/http-server.ts +54 -15
  480. package/src/nexus/api/loop-resources.ts +19 -3
  481. package/src/nexus/api/ops-api.ts +11 -0
  482. package/src/nexus/daemon-thin-inventory.md +37 -0
  483. package/src/nexus/index.ts +70 -0
  484. package/src/nexus/state/state.ts +1 -0
  485. package/src/oauth/store.ts +32 -7
  486. package/src/onboarding/first-run.ts +28 -3
  487. package/src/ops/overview-cache.ts +16 -4
  488. package/src/ops/overview.ts +59 -2
  489. package/src/ops/resources.ts +82 -0
  490. package/src/plugins/maker/connector-maker.ts +41 -0
  491. package/src/plugins/maker/skill-maker.ts +42 -0
  492. package/src/pr-tool/census.ts +303 -0
  493. package/src/pr-tool/inflight-registry.ts +143 -0
  494. package/src/prompt-library/chat-conciseness.ts +13 -0
  495. package/src/prompt-library/universal-preamble.ts +10 -5
  496. package/src/pty-shell/pty-manifest.ts +12 -3
  497. package/src/pty-shell/tui-run-state.ts +34 -3
  498. package/src/release-loop/gate-host-reservation.ts +97 -0
  499. package/src/release-loop/landing-freeze.ts +5 -0
  500. package/src/release-loop/light-rc.ts +5 -2
  501. package/src/release-loop/placement.ts +92 -1
  502. package/src/release-loop/rehearsal-shard-plan.ts +149 -0
  503. package/src/release-loop/rehearsal.ts +73 -17
  504. package/src/repl/index.ts +20 -2
  505. package/src/seat-loop/seat-knowhow.ts +282 -0
  506. package/src/seat-loop/seat-loop.ts +41 -2
  507. package/src/seat-loop/traffic-signal.ts +5 -1
  508. package/src/self-dev/ask-launch-flow.ts +37 -3
  509. package/src/self-dev/decompose-compose-graph.ts +28 -27
  510. package/src/self-dev/decompose-proposal.ts +53 -38
  511. package/src/self-dev/decompose.ts +17 -0
  512. package/src/self-dev/dev-pipeline.ts +31 -2
  513. package/src/self-dev/draft-residue.ts +267 -33
  514. package/src/self-dev/draft-sweep.ts +59 -4
  515. package/src/self-dev/external-call-zero.ts +77 -0
  516. package/src/self-dev/launch-preflight-text.ts +6 -0
  517. package/src/self-dev/launch-preflight.ts +25 -3
  518. package/src/self-dev/local-supervisor-judge.ts +185 -0
  519. package/src/self-dev/run-supervisor.ts +229 -19
  520. package/src/self-dev/run-types.ts +20 -0
  521. package/src/self-dev/shard-aggregate.ts +13 -2
  522. package/src/self-dev/shard-plan.ts +56 -1
  523. package/src/self-dev/shard-run.ts +207 -12
  524. package/src/self-dev/spawn-lane-files.ts +19 -0
  525. package/src/self-implement/app-api-budget.ts +51 -11
  526. package/src/self-implement/config-gate.ts +2 -1
  527. package/src/self-implement/elanous-tui-spawn.ts +14 -0
  528. package/src/self-implement/expose-gate.ts +2 -1
  529. package/src/self-implement/follow-up-placement.ts +79 -0
  530. package/src/self-implement/gate-artifacts.ts +416 -0
  531. package/src/self-implement/gate-baseline.ts +96 -5
  532. package/src/self-implement/gate-cli.ts +207 -17
  533. package/src/self-implement/gate-remote.ts +136 -26
  534. package/src/self-implement/gate-scenario.ts +85 -0
  535. package/src/self-implement/goal-author.ts +160 -8
  536. package/src/self-implement/graph-authority.ts +38 -6
  537. package/src/self-implement/graph-overlay-condition.ts +92 -1
  538. package/src/self-implement/heal-triage.ts +79 -0
  539. package/src/self-implement/host-regate.ts +97 -7
  540. package/src/self-implement/orchestrator.ts +115 -134
  541. package/src/self-implement/pr-title.ts +123 -0
  542. package/src/self-implement/run-checkpoint.ts +235 -0
  543. package/src/self-implement/run-ledger.ts +162 -15
  544. package/src/self-implement/run-stop.ts +4 -3
  545. package/src/self-implement/run-ttl.ts +28 -4
  546. package/src/self-implement/seams.ts +2 -0
  547. package/src/self-implement/shadow-stage.ts +7 -6
  548. package/src/self-implement/stop-autoheal.ts +2 -1
  549. package/src/self-implement/train-gate-remote.ts +37 -8
  550. package/src/self-implement/train-reach-grouping.ts +157 -0
  551. package/src/self-implement/train-scenario-layers.ts +133 -0
  552. package/src/self-implement/uncommitted-work.ts +2 -0
  553. package/src/session/pending-input.ts +19 -0
  554. package/src/session/session-start.ts +224 -0
  555. package/src/session-runtime/index.ts +22 -267
  556. package/src/session-runtime/parallel-dispatch.ts +22 -5
  557. package/src/shell-runner/background-surface.ts +15 -5
  558. package/src/skills/tools/agent.ts +39 -6
  559. package/src/skills/tools/dev-harness.ts +6 -4
  560. package/src/skills/tools/list-dir.ts +11 -0
  561. package/src/skills/tools/schedule-wakeup.ts +408 -0
  562. package/src/skills/tools/shell.ts +246 -4
  563. package/src/status/bar.ts +26 -0
  564. package/src/steward/digest-delivery.ts +118 -0
  565. package/src/steward/hold-hygiene.ts +120 -0
  566. package/src/steward/resync-tick.ts +114 -23
  567. package/src/steward/ta-repair-event-collect.ts +158 -0
  568. package/src/steward/triage.ts +10 -0
  569. package/src/surface/shell-runner-boot.ts +5 -2
  570. package/src/task-agent/card-evidence.ts +25 -1
  571. package/src/task-agent/green-sweep.ts +8 -3
  572. package/src/task-agent/judge.ts +76 -0
  573. package/src/task-agent/land-queue.ts +79 -0
  574. package/src/task-agent/live-moves.ts +30 -9
  575. package/src/task-agent/postland-card.ts +5 -0
  576. package/src/task-agent/prelaunch-already-done.ts +98 -0
  577. package/src/task-agent/real-launch-guard.ts +37 -0
  578. package/src/task-agent/rejudge-on-head.ts +89 -12
  579. package/src/task-agent/remote-rejudge-bind.ts +28 -8
  580. package/src/task-agent/repair-cycle.ts +45 -7
  581. package/src/task-agent/repair-worker.ts +7 -0
  582. package/src/task-agent/run-identity.ts +200 -0
  583. package/src/task-agent/shadow.ts +50 -3
  584. package/src/task-agent/task-hand.ts +182 -5
  585. package/src/task-agent/train-landing.ts +77 -0
  586. package/src/task-agent/work-order.ts +104 -0
  587. package/src/task-orchestrator/surfaces/pod-command-job.ts +151 -37
  588. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +88 -19
  589. package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +2 -1
  590. package/src/task-orchestrator/surfaces/pod-image-source.ts +2 -1
  591. package/src/task-orchestrator/surfaces/pod-install-slots.ts +121 -9
  592. package/src/task-orchestrator/surfaces/pod-lease.ts +118 -6
  593. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +1 -1
  594. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +169 -0
  595. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +27 -0
  596. package/src/task-orchestrator/surfaces/pod-pool.ts +3 -1
  597. package/src/task-orchestrator/surfaces/pod-result-marker-wait.ts +33 -0
  598. package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +67 -0
  599. package/src/task-orchestrator/surfaces/pod-source-receive.ts +19 -3
  600. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +141 -0
  601. package/src/task-orchestrator/surfaces/pod-warm-ta-shadow.ts +85 -0
  602. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +525 -0
  603. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +153 -0
  604. package/src/task-orchestrator/surfaces/pod-warm-worker-runner.ts +90 -0
  605. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +362 -0
  606. package/src/task-orchestrator/surfaces/self-implement-pod.ts +138 -19
  607. package/src/telegram.ts +2 -4
  608. package/src/tui/chat/execution-badge.ts +5 -1
  609. package/src/tui/input/pickers.ts +732 -0
  610. package/src/tui/input-lag-probe.ts +230 -0
  611. package/src/tui/slash/mission.ts +492 -0
  612. package/src/tui/slash/term.ts +877 -0
  613. package/src/tui-client/dashboard-session.ts +9 -0
  614. package/src/tui.ts +52 -0
  615. package/src/user-config/gemini-server-tools.ts +9 -0
  616. package/src/user-config.ts +53 -16
  617. package/src/util/spawn-sync-output.ts +7 -0
  618. package/src/ux-sim/fake-bin.ts +154 -0
  619. package/src/ux-sim/fake-clock.ts +36 -0
  620. package/src/ux-sim/headless-tui.ts +72 -0
  621. package/src/ux-sim/in-process-cli.ts +2 -1
  622. package/src/ux-sim/scenario.ts +255 -0
  623. package/src/ux-sim/temp-git-remote.ts +82 -0
  624. package/src/version/packed-revision.json +1 -1
  625. package/src/video-pipeline/recipes/vlog.ts +25 -7
  626. package/apps/pwa/out/_next/static/chunks/1015-9cba22a5a4554797.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/1084-4e66af7376a18db7.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/1106.3a6410b7b0c70ad4.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/1127.32fc642f70423506.js +0 -8
  630. package/apps/pwa/out/_next/static/chunks/1189-d9b8e1995a831fae.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/1224.6da98c7ad0eb7dd8.js +0 -34
  632. package/apps/pwa/out/_next/static/chunks/1370.4e3e2558f111fd5d.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/1514-b7da377fdc4eb17a.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/1577.3c24c32584e10d70.js +0 -286
  635. package/apps/pwa/out/_next/static/chunks/161.130a569855c4d68c.js +0 -24
  636. package/apps/pwa/out/_next/static/chunks/1864.16da9556e78a9236.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/1b6f6b2a.aff090122be3726e.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/2341.e8a9135e5b910dd5.js +0 -36
  639. package/apps/pwa/out/_next/static/chunks/257.10afdbe3520f2fc2.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/2658.87550e692dc493f6.js +0 -64
  641. package/apps/pwa/out/_next/static/chunks/2673.85e2f02efae7a85f.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/2766-bee623b37472ed63.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/2794.6d836872b0bf43e2.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/2806.eb57ced1a528ed39.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/283.7a121c4910e6dad7.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/2870.1dc29aa5fc01f09f.js +0 -115
  647. package/apps/pwa/out/_next/static/chunks/293.fd38e77ce5d48103.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/2993-5952a1cd3fd5d441.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/32-a523427c5bd5ed00.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/3280.af38002a6424a905.js +0 -90
  651. package/apps/pwa/out/_next/static/chunks/3292-5ddffdb2f517b44f.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/3523.d3231daaf7aba993.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/3763.577de3181e1e6ea0.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/4138-cc56b0938ef72783.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/4255-8350c43d307a8875.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/4352.a4954693499c2714.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/4421-f2dcbc6fd80ec013.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/4513-51b94aeee37c9905.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/4928-c3b72f99aa588699.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/4986-f9fc0283fb325219.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/5014.671a7b9f69c2b7b1.js +0 -94
  662. package/apps/pwa/out/_next/static/chunks/5137.d398e24710b3feb6.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/5298.0bea74885cabe754.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/5315.74c53a39739b11c4.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/5317-8b1df8d02105cd07.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/5358-33051f41a2b239af.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/5388.a2ec02501f714ad5.js +0 -62
  668. package/apps/pwa/out/_next/static/chunks/5395.e6ac948a2d15058d.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/5409-f282bf533c151849.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/5466.d0f4aba9d52acfa8.js +0 -27
  671. package/apps/pwa/out/_next/static/chunks/5564.3d28b353470fe305.js +0 -104
  672. package/apps/pwa/out/_next/static/chunks/569.b0cbfb4dee4a167a.js +0 -93
  673. package/apps/pwa/out/_next/static/chunks/5835-72465b73a8bb8d74.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/5941.045d6093139e1f7a.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/6228.47cabb83564dfe54.js +0 -24
  676. package/apps/pwa/out/_next/static/chunks/6457-8d1993002a87d0fe.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/6513.ef54cfae52351982.js +0 -2
  678. package/apps/pwa/out/_next/static/chunks/6582.13d245bf87df69f1.js +0 -148
  679. package/apps/pwa/out/_next/static/chunks/6655.c33f6acc2c61bb89.js +0 -149
  680. package/apps/pwa/out/_next/static/chunks/6731-9d7195af289c8d4c.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/6733.0be05b1db9279fc5.js +0 -79
  682. package/apps/pwa/out/_next/static/chunks/6742-90937146253a8b9e.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/687-816858f77a396a4e.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/6888.72c7b8cfb02a7e61.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/6993.c17c828ac3ef3253.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/7076.9f27ef666b0cefdd.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/7113.1403c5d451804e8c.js +0 -287
  688. package/apps/pwa/out/_next/static/chunks/7169.9dd603c726c2cd7c.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/7247-e3ec21f761f86710.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/7261-a47b72f96f5199df.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/7335-5cda81ac90310077.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/7356-c2e4af090546e2d4.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/738-59518742078fda71.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/7385.a7375f10db73243a.js +0 -77
  695. package/apps/pwa/out/_next/static/chunks/7445.cf1cf2fe109bcd96.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/7505.a2057432ab6ef99a.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/7515.31f45fac1f9c70cb.js +0 -74
  698. package/apps/pwa/out/_next/static/chunks/7658-d4ce8c72a9548a91.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/8107-0b1ae46bc5bcb788.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/8132.bfea2f016ce8d992.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/8672.e299832bc7d18ee2.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/8735.f1ef798a5f260f6f.js +0 -226
  703. package/apps/pwa/out/_next/static/chunks/8774-88f67f7d8d53d299.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/8782.252e5a405d86e4ed.js +0 -32
  705. package/apps/pwa/out/_next/static/chunks/909-9ed7aa0dba804316.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/9100.22b0301256827007.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/9140-dde77575570bc71e.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/9292-a7ad9a01c7044b93.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/9517.00627dc51b02821c.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/9725.dcbb585652d8e9c5.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/9761.a3ebf5c389c24ddc.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/9972.834d2b2b7d48f1ed.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/9995-1a90486f5ae01546.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/a435ad95.577add051b4facb2.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-83372d8b5863ebf7.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d69256d7613c140a.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-352c5e9ba3f9b858.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/app/board/page-69f1d725524201a8.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/app/botlab/page-a5635eba0d73b84e.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/app/bots/page-ad9181d84285e4ea.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/app/ceo/page-86feb08080261a2f.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/app/chat/page-ffa15987e4756d03.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/app/consult/page-11a3ab44ac327cc1.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/control/page-6309b11c4264d24d.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/decisions/page-8800472079612e87.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/design-check/page-d387bf3f2301882a.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/editor/page-048f88d8c8658da6.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/exec/page-6c14f9739b7f6880.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/field/page-2834f44f64e98d74.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/healthz/route-83372d8b5863ebf7.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/inside/page-a13df6f88e824887.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/intake/page-8a4397248754cf2b.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/layout-daf0bd329360a336.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/live/page-f7ef1c28764cb476.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/live-run/page-61e74f28b9353dac.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/loops/page-8aaa6dedc4c10dfb.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/market/page-294602ba649be829.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-5fa5f41ed3fce323.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/morning/page-33bf667a5fb297bf.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/not-found-5ae3cbd6e5dba515.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eb867d16ad6fdede.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1ab789fa68682f7e.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6fd41b5b6ff13a71.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fb3d5f7642c99fba.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/app/outputs/page-70f705b2b5ed42ca.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/app/page-c9c93b358a7667b5.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1563c556b81c96ed.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ce89c20c45b3a673.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/app/sessions/page-423ae63d786f4ee9.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-591a5378a4c4447c.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/app/settings/page-a250496b19a5824b.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c36a9277febc5911.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/app/setup/layout-83372d8b5863ebf7.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/app/share/page-a5de57edc6d7f942.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/app/showroom/page-16bdd4c9f40498df.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/app/tasks/page-75e946e6e71cc6d8.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/app/term/page-9eb56166774eb256.js +0 -1
  758. package/apps/pwa/out/_next/static/chunks/app/today/page-1c5f9ef35dbb22c5.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/app/trace/page-657ff9985e508498.js +0 -1
  760. package/apps/pwa/out/_next/static/chunks/app/vault/page-22dc72499da68fb2.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9f92737b8766009a.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/app/workflows/page-094ad334e40f8169.js +0 -1
  763. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8bcd379852ff6287.js +0 -1
  764. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-6b04413f280cd344.js +0 -1
  765. package/apps/pwa/out/_next/static/chunks/de70ab42-d28d47437208d458.js +0 -1
  766. package/apps/pwa/out/_next/static/chunks/f800c5e3.46dbd7f2af8c3fce.js +0 -1
  767. package/apps/pwa/out/_next/static/chunks/f8bc19ad.47579f6d684b0760.js +0 -122
  768. package/apps/pwa/out/_next/static/chunks/framework-ca9ac4609fb9fed8.js +0 -1
  769. package/apps/pwa/out/_next/static/chunks/main-app-0dd08d32d15a030f.js +0 -1
  770. package/apps/pwa/out/_next/static/chunks/main-f0399d33955c4e01.js +0 -1
  771. package/apps/pwa/out/_next/static/chunks/pages/_app-d42caca6f8ac2211.js +0 -1
  772. package/apps/pwa/out/_next/static/chunks/pages/_error-42e713c13aa41ca8.js +0 -1
  773. package/apps/pwa/out/_next/static/chunks/webpack-8fbf8f61a377aa16.js +0 -1
  774. package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +0 -1
  775. /package/apps/pwa/out/_next/static/{cIpv5nU9AycwYWZI9H589 → IVKr3AlRUVLKsw2fh7eXF}/_ssgManifest.js +0 -0
@@ -9,7 +9,7 @@
9
9
  // on its own cadence (dashboard throttles to ~2s).
10
10
 
11
11
  import { resolve } from 'node:path';
12
- import { spawnSync } from 'node:child_process';
12
+ import { execFile, spawnSync } from 'node:child_process';
13
13
  import { findGitDir, type LocatedGit } from './locate.js';
14
14
  import { readGitHead } from './read-head.js';
15
15
  import { GitFileWatcher } from './watcher.js';
@@ -31,13 +31,62 @@ export { probeDirty, probeAheadBehind, parsePorcelain } from './dirty.js';
31
31
  const SNAPSHOT_TIMEOUT_MS = 5_000;
32
32
  const SNAPSHOT_STATUS_MAX_CHARS = 2_000;
33
33
  const GIT_SNAPSHOT_TTL_MS = 3_000;
34
+ /** A cached snapshot older than this is rebuilt in the background on the next periodic dirty tick. */
35
+ const GIT_SNAPSHOT_KEEP_WARM_MS = 30_000;
34
36
 
35
37
  interface GitSnapshotCacheEntry {
36
38
  at: number;
39
+ /** Monotonic store ordering — `at` can flip within the same millisecond. */
40
+ version: number;
37
41
  value: string | null;
38
42
  }
39
43
 
40
44
  const gitSnapshotCache = new Map<string, GitSnapshotCacheEntry>();
45
+ /** The entry an invalidation (known write · HEAD move) just evicted. Only the
46
+ * stale-while-revalidate turn path reads it, and only while it is younger than
47
+ * its window: the turn is served the last snapshot (its header states when it
48
+ * was taken) while the background rebuild runs, instead of rebuilding
49
+ * synchronously on the event loop. Any later store supersedes it. */
50
+ const gitSnapshotLastKnown = new Map<string, GitSnapshotCacheEntry>();
51
+
52
+ function storeGitSnapshot(key: string, entry: Omit<GitSnapshotCacheEntry, 'version'>): void {
53
+ gitSnapshotCache.set(key, { ...entry, version: bumpGitSnapshotVersion(key) });
54
+ gitSnapshotLastKnown.delete(key);
55
+ }
56
+
57
+ function evictGitSnapshot(key: string): boolean {
58
+ const entry = gitSnapshotCache.get(key);
59
+ if (!entry) return false;
60
+ gitSnapshotCache.delete(key);
61
+ gitSnapshotLastKnown.set(key, entry);
62
+ return true;
63
+ }
64
+ /** One background snapshot rebuild per cwd and invalidation generation. */
65
+ const gitSnapshotInFlight = new Map<string, { generation: string; promise: Promise<string | null> }>();
66
+
67
+ /** Per-key monotonic store version: async rebuilds never overwrite a newer
68
+ * sync value, not even when both land in the same millisecond (`at`
69
+ * comparisons can flip). Generation guards invalidation races instead. */
70
+ const gitSnapshotVersion = new Map<string, number>();
71
+
72
+ function gitSnapshotVersionOf(key: string): number {
73
+ return gitSnapshotVersion.get(key) ?? 0;
74
+ }
75
+
76
+ function bumpGitSnapshotVersion(key: string): number {
77
+ const version = gitSnapshotVersionOf(key) + 1;
78
+ gitSnapshotVersion.set(key, version);
79
+ return version;
80
+ }
81
+
82
+ /** Test-only hold seam (production null): parks an async rebuild after its
83
+ * reads and before its store, so «a write during a rebuild wins» is provable
84
+ * deterministically instead of by racing timers. */
85
+ let gitSnapshotRebuildHold: (() => void | Promise<void>) | null = null;
86
+
87
+ export function __setGitSnapshotRebuildHoldForTest(hold: (() => void | Promise<void>) | null): void {
88
+ gitSnapshotRebuildHold = hold;
89
+ }
41
90
 
42
91
  interface CacheEntry {
43
92
  located: LocatedGit;
@@ -97,8 +146,15 @@ function ensureEntry(cwd: string): CacheEntry | null {
97
146
  // Re-read HEAD + retarget watcher whenever a watched file changes.
98
147
  // One handler covers all listeners so we don't race each subscriber.
99
148
  watcher.subscribe(() => {
149
+ const before = entry.head;
100
150
  entry.head = readGitHead(entry.located);
101
151
  entry.watcher.retargetForHead(entry.head);
152
+ // A branch/ref move makes every cached snapshot's branch + log lines wrong. Only a real
153
+ // move: macOS fs.watch also replays the ref write that happened just before the watch was
154
+ // installed (measured: refs/heads/main «change» right after the first build), and that
155
+ // spurious event must not evict a fresh snapshot.
156
+ if (headMoved(before, entry.head)) invalidateAllGitSnapshots();
157
+ else debug.log('git-fs.snapshot', 'watch-event-head-unchanged', { root: entry.located.root });
102
158
  for (const fn of entry.listeners) {
103
159
  try { fn(); } catch { /* noop */ }
104
160
  }
@@ -179,8 +235,8 @@ function viewOf(entry: CacheEntry): GitStatusView {
179
235
  /** Non-blocking twin of `refreshDirty` for the TUI: the probes run as
180
236
  * async subprocesses so a slow `git status` (large worktree, loaded
181
237
  * host) never stalls key input. Same throttle; concurrent calls share
182
- * one probe, except that `force` (a known write just happened) never
183
- * reuses a probe that started before it. */
238
+ * one probe. `force` also marks cached LLM snapshots stale and rebuilds
239
+ * them in the background — a known write happened. */
184
240
  export function refreshDirtyAsync(
185
241
  cwd: string,
186
242
  opts: { minIntervalMs?: number; force?: boolean } = {},
@@ -188,26 +244,40 @@ export function refreshDirtyAsync(
188
244
  let entry: CacheEntry | null;
189
245
  try { entry = ensureEntry(cwd); } catch (error) { return Promise.reject(error); }
190
246
  if (!entry) return Promise.resolve({ head: null, dirty: null, aheadBehind: null, lastDirtyProbeAt: 0 });
191
- // An in-flight probe (possibly a forced, post-write one) is newer than the last stored view.
247
+ // An in-flight probe (possibly a forced, post-write one) is newer than the last stored view —
248
+ // a throttled call joins it instead of returning the pre-write view (#27493 contract).
192
249
  const running = dirtyInFlight.get(entry);
193
250
  if (running && !opts.force) return running;
194
251
  const min = opts.minIntervalMs ?? 2_000;
195
252
  if (!opts.force && entry.lastDirtyProbeAt > 0 && Date.now() - entry.lastDirtyProbeAt < min) {
196
253
  return Promise.resolve(viewOf(entry));
197
254
  }
198
- const target = entry;
199
- const root = target.located.root;
255
+ const probe = probeDirtyAsyncFor(entry, { fresh: !!opts.force });
256
+ // After the post-write probe is queued, so the snapshot rebuild joins it instead of an older one.
257
+ if (opts.force) invalidateGitSnapshots(cwd);
258
+ // Keep-warm: the dashboard's periodic (non-forced) dirty tick also re-warms a snapshot that is in
259
+ // use, so a turn typed after an idle stretch is served within the stale window instead of falling
260
+ // back to the synchronous build. Only snapshots already cached are kept warm.
261
+ else if (gitSnapshotCache.has(resolve(cwd)) || gitSnapshotLastKnown.has(resolve(cwd))) void refreshGitSnapshotAsync(cwd, { ifOlderThanMs: GIT_SNAPSHOT_KEEP_WARM_MS });
262
+ return probe;
263
+ }
264
+
265
+ /** Share the in-flight probe, unless the caller needs one that started after
266
+ * now (`fresh`: a known write just happened) — then queue a new probe behind it. */
267
+ function probeDirtyAsyncFor(entry: CacheEntry, opts: { fresh: boolean }): Promise<GitStatusView> {
268
+ const running = dirtyInFlight.get(entry);
269
+ if (running && !opts.fresh) return running;
270
+ const root = entry.located.root;
200
271
  const start = (): Promise<GitStatusView> => Promise.all([probeDirtyAsync(root), probeAheadBehindAsync(root)])
201
272
  .then(([dirty, aheadBehind]) => {
202
- target.dirty = dirty;
203
- target.aheadBehind = aheadBehind;
204
- target.lastDirtyProbeAt = Date.now();
205
- return viewOf(target);
273
+ entry.dirty = dirty;
274
+ entry.aheadBehind = aheadBehind;
275
+ entry.lastDirtyProbeAt = Date.now();
276
+ return viewOf(entry);
206
277
  });
207
- // A forced probe queues behind the running one so it reads the post-write tree.
208
278
  const probe = (running ? running.then(start, start) : start())
209
- .finally(() => { if (dirtyInFlight.get(target) === probe) dirtyInFlight.delete(target); });
210
- dirtyInFlight.set(target, probe);
279
+ .finally(() => { if (dirtyInFlight.get(entry) === probe) dirtyInFlight.delete(entry); });
280
+ dirtyInFlight.set(entry, probe);
211
281
  return probe;
212
282
  }
213
283
 
@@ -217,7 +287,7 @@ export function refreshDirtyAsync(
217
287
  * fresh state after mutations. Returns null outside a git repo. */
218
288
  export function buildGitSnapshot(
219
289
  cwd: string,
220
- opts: { now?: () => number; ttlMs?: number } = {},
290
+ opts: { now?: () => number; ttlMs?: number; staleWhileRevalidateMs?: number } = {},
221
291
  ): string | null {
222
292
  const now = opts.now ?? Date.now;
223
293
  const ttlMs = opts.ttlMs ?? GIT_SNAPSHOT_TTL_MS;
@@ -228,41 +298,157 @@ export function buildGitSnapshot(
228
298
  debug.log('git-fs.snapshot', 'cache-hit', { cwd, ageMs: at - cached.at });
229
299
  return cached.value;
230
300
  }
301
+ // Stale-while-revalidate (TUI turn path): serve the recent snapshot now
302
+ // and rebuild it off the event loop for the next turn.
303
+ if (cached && opts.staleWhileRevalidateMs !== undefined && at - cached.at < opts.staleWhileRevalidateMs) {
304
+ debug.log('git-fs.snapshot', 'stale-hit', { cwd, ageMs: at - cached.at });
305
+ void refreshGitSnapshotAsync(cwd);
306
+ return cached.value;
307
+ }
308
+ // Invalidated (known write · HEAD move) and the background rebuild has not
309
+ // stored yet: serve the evicted snapshot rather than a synchronous rebuild —
310
+ // the TYPING-LAG this path exists to remove. Bounded by the same window.
311
+ const evicted = !cached ? gitSnapshotLastKnown.get(key) : undefined;
312
+ if (evicted && opts.staleWhileRevalidateMs !== undefined && at - evicted.at < opts.staleWhileRevalidateMs) {
313
+ debug.log('git-fs.snapshot', 'stale-after-invalidate', { cwd, ageMs: at - evicted.at, rebuilding: gitSnapshotInFlight.has(key) });
314
+ void refreshGitSnapshotAsync(cwd);
315
+ return evicted.value;
316
+ }
231
317
 
232
318
  const view = refreshDirty(cwd, { force: true });
233
319
  let value: string | null = null;
234
320
  if (view.head) {
235
- const branch = view.head.branch ?? `HEAD@${view.head.sha?.slice(0, 7) ?? 'unknown'}`;
236
- const mainBranch = detectDefaultBranch(cwd);
237
- const status = runGit(cwd, ['status', '--short']);
238
- const recentLog = runGit(cwd, ['log', '--oneline', '-n', '5']);
239
- const userName = runGit(cwd, ['config', 'user.name']);
240
- const truncatedStatus = status.length > SNAPSHOT_STATUS_MAX_CHARS
241
- ? `${status.slice(0, SNAPSHOT_STATUS_MAX_CHARS)}\n... (truncated; run git status for more)`
242
- : status;
243
-
244
- const parts = [
245
- 'This is the git status at the start of this turn. It is a snapshot and will not update during the turn.',
246
- `Current branch: ${branch}${view.head.isWorktree ? ' (worktree)' : ''}`,
247
- mainBranch ? `Main branch: ${mainBranch}` : '',
248
- userName ? `Git user: ${userName}` : '',
249
- view.aheadBehind
250
- ? `Upstream: ahead ${view.aheadBehind.ahead}, behind ${view.aheadBehind.behind}`
251
- : '',
252
- `Status:\n${truncatedStatus || '(clean)'}`,
253
- `Recent commits:\n${recentLog || '(none)'}`,
254
- ].filter(Boolean);
255
- value = parts.join('\n\n');
321
+ value = composeGitSnapshot(view, {
322
+ mainBranch: detectDefaultBranch(cwd),
323
+ status: runGit(cwd, ['status', '--short']),
324
+ recentLog: runGit(cwd, ['log', '--oneline', '-n', '5']),
325
+ userName: runGit(cwd, ['config', 'user.name']),
326
+ generatedAt: at,
327
+ });
256
328
  }
257
329
 
258
- gitSnapshotCache.set(key, { at, value });
330
+ storeGitSnapshot(key, { at, value });
259
331
  debug.log('git-fs.snapshot', 'built', { cwd, ageMs: 0 });
260
332
  return value;
261
333
  }
262
334
 
335
+ function composeGitSnapshot(
336
+ view: GitStatusView,
337
+ git: { mainBranch: string | null; status: string; recentLog: string; userName: string; generatedAt: number },
338
+ ): string {
339
+ const head = view.head!;
340
+ const branch = head.branch ?? `HEAD@${head.sha?.slice(0, 7) ?? 'unknown'}`;
341
+ const truncatedStatus = git.status.length > SNAPSHOT_STATUS_MAX_CHARS
342
+ ? `${git.status.slice(0, SNAPSHOT_STATUS_MAX_CHARS)}\n... (truncated; run git status for more)`
343
+ : git.status;
344
+ const parts = [
345
+ `This is the git status as of ${new Date(git.generatedAt).toISOString()}. It is a snapshot and will not update during the turn.`,
346
+ `Current branch: ${branch}${head.isWorktree ? ' (worktree)' : ''}`,
347
+ git.mainBranch ? `Main branch: ${git.mainBranch}` : '',
348
+ git.userName ? `Git user: ${git.userName}` : '',
349
+ view.aheadBehind
350
+ ? `Upstream: ahead ${view.aheadBehind.ahead}, behind ${view.aheadBehind.behind}`
351
+ : '',
352
+ `Status:\n${truncatedStatus || '(clean)'}`,
353
+ `Recent commits:\n${git.recentLog || '(none)'}`,
354
+ ].filter(Boolean);
355
+ return parts.join('\n\n');
356
+ }
357
+
358
+ /** Rebuild the snapshot for `cwd` with async subprocesses and store it.
359
+ * Concurrent calls share one rebuild. Never throws. `ifOlderThanMs` turns it
360
+ * into a keep-warm tick: a cached snapshot younger than that is left alone. */
361
+ export function refreshGitSnapshotAsync(cwd: string, opts: { ifOlderThanMs?: number } = {}): Promise<string | null> {
362
+ const key = resolve(cwd);
363
+ const cached = gitSnapshotCache.get(key);
364
+ if (cached && opts.ifOlderThanMs !== undefined && Date.now() - cached.at < opts.ifOlderThanMs) {
365
+ return Promise.resolve(cached.value);
366
+ }
367
+ const generation = snapshotGeneration(key);
368
+ const running = gitSnapshotInFlight.get(key);
369
+ if (running && running.generation === generation) return running.promise;
370
+ const startedAt = Date.now();
371
+ const startVersion = gitSnapshotVersionOf(key);
372
+ const rebuild = (async (): Promise<string | null> => {
373
+ const entry = ensureEntry(cwd);
374
+ // Joins the in-flight probe; callers that need a post-write/post-move probe queue a fresh one first.
375
+ const view = entry ? await probeDirtyAsyncFor(entry, { fresh: false }) : getGitStatusView(cwd);
376
+ let value: string | null = null;
377
+ if (view.head) {
378
+ const [mainBranch, status, recentLog, userName] = await Promise.all([
379
+ detectDefaultBranchAsync(cwd),
380
+ runGitAsync(cwd, ['status', '--short']),
381
+ runGitAsync(cwd, ['log', '--oneline', '-n', '5']),
382
+ runGitAsync(cwd, ['config', 'user.name']),
383
+ ]);
384
+ // «as of» = when the reads finished, not when the rebuild was queued.
385
+ value = composeGitSnapshot(view, { mainBranch, status, recentLog, userName, generatedAt: Date.now() });
386
+ }
387
+ // Test-only hold seam: reads are done, the store decision is next.
388
+ if (gitSnapshotRebuildHold) await gitSnapshotRebuildHold();
389
+ // An invalidation that landed mid-rebuild wins: keep the entry stale.
390
+ // A newer entry (e.g. a synchronous build that overlapped this rebuild) is
391
+ // kept — ordered by monotonic version, never by `at` (same-ms flips).
392
+ const current = gitSnapshotCache.get(key);
393
+ if (snapshotGeneration(key) === generation && !(current && current.version > startVersion)) {
394
+ storeGitSnapshot(key, { at: startedAt, value });
395
+ }
396
+ debug.log('git-fs.snapshot', 'rebuilt-async', { cwd, ms: Date.now() - startedAt });
397
+ return value;
398
+ })()
399
+ .catch(() => null)
400
+ .finally(() => { if (gitSnapshotInFlight.get(key)?.promise === rebuild) gitSnapshotInFlight.delete(key); });
401
+ gitSnapshotInFlight.set(key, { generation, promise: rebuild });
402
+ return rebuild;
403
+ }
404
+
405
+ /** Invalidation generations: a rebuild only stores its result when no
406
+ * invalidation (per cwd or global) landed while it was running. */
407
+ const gitSnapshotGeneration = new Map<string, number>();
408
+ let allGitSnapshotsGeneration = 0;
409
+
410
+ function snapshotGeneration(key: string): string {
411
+ return `${allGitSnapshotsGeneration}:${gitSnapshotGeneration.get(key) ?? 0}`;
412
+ }
413
+
414
+ function invalidateAllGitSnapshots(): void {
415
+ // Snapshots that were in use (cached or being rebuilt) are re-warmed for the next turn.
416
+ const inUse = new Set([...gitSnapshotCache.keys(), ...gitSnapshotInFlight.keys()]);
417
+ for (const key of [...gitSnapshotCache.keys()]) evictGitSnapshot(key);
418
+ allGitSnapshotsGeneration += 1;
419
+ // Probes already running read the old ref's upstream — queue one fresh probe per repo behind
420
+ // them, so the rebuilds below join it instead.
421
+ const entries = new Set<CacheEntry>();
422
+ for (const key of inUse) {
423
+ const entry = ensureEntry(key);
424
+ if (entry) entries.add(entry);
425
+ }
426
+ for (const entry of entries) void probeDirtyAsyncFor(entry, { fresh: true });
427
+ for (const key of inUse) void refreshGitSnapshotAsync(key);
428
+ }
429
+
430
+ /** A known write happened: the cached snapshot must not be served again.
431
+ * When a snapshot was in use (cached or being rebuilt), rebuild it in the
432
+ * background so the next turn usually still hits. */
433
+ function invalidateGitSnapshots(cwd: string): void {
434
+ const key = resolve(cwd);
435
+ const wasCached = evictGitSnapshot(key);
436
+ const wasRebuilding = gitSnapshotInFlight.has(key);
437
+ gitSnapshotGeneration.set(key, (gitSnapshotGeneration.get(key) ?? 0) + 1);
438
+ // An in-flight rebuild of the old generation will be discarded — schedule its replacement.
439
+ if (wasCached || wasRebuilding) void refreshGitSnapshotAsync(cwd);
440
+ }
441
+
442
+ function headMoved(before: GitHeadInfo | null | undefined, after: GitHeadInfo | null | undefined): boolean {
443
+ if (!before || !after) return before !== after;
444
+ return before.branch !== after.branch || before.sha !== after.sha || before.detached !== after.detached;
445
+ }
446
+
263
447
  /** Explicitly discard cached git snapshots, including cached non-repositories. */
264
448
  export function clearGitSnapshotCache(): void {
265
449
  gitSnapshotCache.clear();
450
+ gitSnapshotLastKnown.clear();
451
+ allGitSnapshotsGeneration += 1;
266
452
  }
267
453
 
268
454
  function detectDefaultBranch(cwd: string): string | null {
@@ -281,6 +467,34 @@ function detectDefaultBranch(cwd: string): string | null {
281
467
  return null;
282
468
  }
283
469
 
470
+ async function detectDefaultBranchAsync(cwd: string): Promise<string | null> {
471
+ const symbolic = await runGitAsync(cwd, ['symbolic-ref', 'refs/remotes/origin/HEAD']);
472
+ if (symbolic.startsWith('refs/remotes/origin/')) {
473
+ return symbolic.slice('refs/remotes/origin/'.length).trim() || null;
474
+ }
475
+ for (const candidate of ['main', 'master', 'develop']) {
476
+ if (await gitSucceeds(cwd, ['show-ref', '--verify', '--quiet', `refs/heads/${candidate}`])) return candidate;
477
+ }
478
+ return null;
479
+ }
480
+
481
+ function gitSucceeds(cwd: string, args: string[]): Promise<boolean> {
482
+ return new Promise((resolveOk) => {
483
+ try { execFile('git', args, { cwd, timeout: SNAPSHOT_TIMEOUT_MS }, (error) => resolveOk(!error)); }
484
+ catch { resolveOk(false); }
485
+ });
486
+ }
487
+
488
+ function runGitAsync(cwd: string, args: string[]): Promise<string> {
489
+ return new Promise((resolveOut) => {
490
+ try {
491
+ execFile('git', args, { cwd, encoding: 'utf8', timeout: SNAPSHOT_TIMEOUT_MS, maxBuffer: 2 * 1024 * 1024 }, (error, stdout) => {
492
+ resolveOut(error ? '' : String(stdout ?? '').trim());
493
+ });
494
+ } catch { resolveOut(''); }
495
+ });
496
+ }
497
+
284
498
  function runGit(cwd: string, args: string[]): string {
285
499
  const res = spawnSync('git', args, {
286
500
  cwd,
@@ -292,10 +506,23 @@ function runGit(cwd: string, args: string[]): string {
292
506
  return String(res.stdout ?? '').trim();
293
507
  }
294
508
 
509
+ /** Test helper — the cached snapshot for `cwd` without building one. */
510
+ export function __peekGitSnapshot(cwd: string): string | null | undefined {
511
+ return gitSnapshotCache.get(resolve(cwd))?.value;
512
+ }
513
+
514
+ /** Test helper — what a HEAD move does to cached snapshots. */
515
+ export function __invalidateAllGitSnapshotsForTest(): void {
516
+ invalidateAllGitSnapshots();
517
+ }
518
+
295
519
  /** Test helper — drop the cache + dispose every watcher. */
296
520
  export function __resetGitFsCache(): void {
297
521
  for (const [, entry] of cache) entry.watcher.dispose();
298
522
  cache.clear();
299
523
  clearGitSnapshotCache();
524
+ gitSnapshotInFlight.clear();
300
525
  dirtyInFlight.clear();
526
+ gitSnapshotGeneration.clear();
527
+ gitSnapshotVersion.clear();
301
528
  }
@@ -0,0 +1,81 @@
1
+ import { debug } from '../debug/log.js';
2
+
3
+ /** One measured execution at a GitHub boundary. Never put an URL, arguments, token, or body here. */
4
+ export interface GithubRemoteCall {
5
+ kind: 'rest' | 'graphql' | 'gh-command' | 'git-clone' | 'git-fetch' | 'git-push';
6
+ caller: string;
7
+ route: string;
8
+ method?: string;
9
+ host: string;
10
+ scopeId?: string;
11
+ runId?: string;
12
+ retry?: number;
13
+ }
14
+
15
+ export interface GithubRemoteOutcome<T> {
16
+ failed: boolean;
17
+ result?: T;
18
+ error?: unknown;
19
+ }
20
+
21
+ export interface GithubRemoteCallOptions<T> {
22
+ /** Return an observed HTTP status only; CLI success alone is not HTTP 200. */
23
+ status?: (outcome: GithubRemoteOutcome<T>) => number | undefined;
24
+ now?: () => number;
25
+ emit?: (category: string, event: string, data: Record<string, unknown>) => void;
26
+ }
27
+
28
+ /** Execute the real call and record exactly one completion, including failures. Observation cannot change the result. */
29
+ export async function runGithubRemoteCall<T>(
30
+ call: GithubRemoteCall,
31
+ execute: () => Promise<T>,
32
+ options: GithubRemoteCallOptions<T> = {},
33
+ ): Promise<T> {
34
+ const now = options.now ?? Date.now;
35
+ const safeNow = (): number | undefined => {
36
+ try { const value = now(); return Number.isFinite(value) ? value : undefined; } catch { return undefined; }
37
+ };
38
+ const started = safeNow();
39
+ let outcome: GithubRemoteOutcome<T> = { failed: false };
40
+ try {
41
+ const result = await execute();
42
+ outcome = { failed: false, result };
43
+ return result;
44
+ } catch (error) {
45
+ outcome = { failed: true, error };
46
+ throw error;
47
+ } finally {
48
+ let status: number | undefined;
49
+ try { status = options.status?.(outcome); } catch { /* telemetry must not alter the call */ }
50
+ const ended = safeNow();
51
+ const data: Record<string, unknown> = {
52
+ kind: call.kind,
53
+ caller: call.caller,
54
+ route: call.route,
55
+ method: call.method ?? 'unmeasured',
56
+ host: call.host,
57
+ scopeId: call.scopeId ?? 'unmeasured',
58
+ runId: call.runId ?? '',
59
+ retry: call.retry ?? 'unmeasured',
60
+ status: status ?? 'unmeasured',
61
+ ok: !outcome.failed,
62
+ durationMs: started === undefined || ended === undefined ? 'unmeasured' : Math.max(0, ended - started),
63
+ };
64
+ try { (options.emit ?? debug.log.bind(debug))('github.transport', 'complete', data); } catch { /* same rule */ }
65
+ }
66
+ }
67
+
68
+ /** Only a real GitHub remote belongs in the Git transport denominator. */
69
+ export function isGithubGitRemote(remote: string): boolean {
70
+ let host: string | undefined;
71
+ if (remote.includes('://')) {
72
+ try {
73
+ const url = new URL(remote);
74
+ if (!['http:', 'https:', 'ssh:', 'git:'].includes(url.protocol)) return false;
75
+ host = url.hostname;
76
+ } catch { return false; }
77
+ } else {
78
+ host = /^(?:[^@\s]+@)?([^:/\s]+):[^\s]+$/.exec(remote)?.[1];
79
+ }
80
+ return host === 'github.com' || host === 'ssh.github.com';
81
+ }
@@ -75,8 +75,18 @@ export function hydrateGoalFromSnapshot(): Goal | null {
75
75
  persistSnapshot(null);
76
76
  return null;
77
77
  }
78
- current = parsed;
79
- debug.log('goal', 'snapshot.hydrated', { id: current.id, status: current.status });
78
+ // TUI-GOAL-LOOP-FRICTION(#35) — 프로세스가 죽어 있던 시간은 이 목표가 «돈» 시간이 아니다.
79
+ // active 로 되살아나면 벽시계 기준을 지금으로 다시 잡는다(낡은 createdAt 기준 4.9억 ms 소진 오판 방지).
80
+ const now = Date.now();
81
+ const staleStart = goalWallClockStart(parsed);
82
+ current = parsed.status === 'active'
83
+ ? { ...parsed, activeSince: now, usage: { ...parsed.usage, elapsedMs: 0 } }
84
+ : parsed;
85
+ debug.log('goal', 'snapshot.hydrated', {
86
+ id: current.id, status: current.status,
87
+ wallClockRestarted: parsed.status === 'active',
88
+ staleElapsedMs: Math.max(0, now - staleStart),
89
+ });
80
90
  notify(current, null);
81
91
  return getCurrentGoal();
82
92
  } catch (error) {
@@ -100,12 +110,25 @@ export function generateGoalId(): string {
100
110
  return `g-${ts}-${rand}`;
101
111
  }
102
112
 
113
+ /** TUI-GOAL-LOOP-FRICTION(#35) — 벽시계 예산의 기준 시각 = 이번에 active 로 들어선 때.
114
+ * 옛 스냅숏(activeSince 없음)만 createdAt 으로 떨어진다. */
115
+ export function goalWallClockStart(goal: Goal): number {
116
+ return typeof goal.activeSince === 'number' && Number.isFinite(goal.activeSince)
117
+ ? goal.activeSince
118
+ : goal.createdAt;
119
+ }
120
+
121
+ /** 이번 활성화 기준 벽시계 경과(ms). 시계가 거꾸로 가도 음수는 내지 않는다. */
122
+ export function goalWallClockElapsedMs(goal: Goal, now: number): number {
123
+ return Math.max(0, now - goalWallClockStart(goal));
124
+ }
125
+
103
126
  export function getCurrentGoal(): Goal | null {
104
127
  if (!current) return null;
105
128
  // Refresh elapsed ms on read so callers don't see stale values.
106
129
  return {
107
130
  ...current,
108
- usage: { ...current.usage, elapsedMs: Date.now() - current.createdAt },
131
+ usage: { ...current.usage, elapsedMs: goalWallClockElapsedMs(current, Date.now()) },
109
132
  };
110
133
  }
111
134
 
@@ -155,6 +178,7 @@ export function startGoal(input: GoalCreateInput): { ok: true; goal: Goal } | {
155
178
  status: 'active',
156
179
  createdAt: now,
157
180
  updatedAt: now,
181
+ activeSince: now,
158
182
  budget,
159
183
  usage: { turnsUsed: 0, tokensUsed: 0, tokensMeasured: true, elapsedMs: 0 },
160
184
  linkedTaskId: input.linkedTaskId,
@@ -173,8 +197,22 @@ export function setStatus(next: GoalStatus, reason?: string): { ok: true; goal:
173
197
  return { ok: false, error: { code: 'GoalNotFound', message: 'No active goal.' } };
174
198
  }
175
199
  const prev = { ...current };
176
- current = { ...current, status: next, updatedAt: Date.now() };
200
+ const now = Date.now();
201
+ // TUI-GOAL-LOOP-FRICTION(#35) — paused·budget-limited·resume_required → active 는 «새 활성화»다.
202
+ // 벽시계는 여기서 다시 잰다 — 멈춰 있던 시간(며칠)을 이번 실행 예산에 싣지 않는다.
203
+ const reactivated = next === 'active' && prev.status !== 'active';
204
+ current = {
205
+ ...current,
206
+ status: next,
207
+ updatedAt: now,
208
+ ...(reactivated ? { activeSince: now, usage: { ...current.usage, elapsedMs: 0 } } : {}),
209
+ };
177
210
  debug.log('goal', 'status', { id: current.id, from: prev.status, to: next, reason: reason ?? '' });
211
+ if (reactivated) {
212
+ debug.log('goal', 'wall-clock-restart', {
213
+ id: current.id, from: prev.status, priorElapsedMs: goalWallClockElapsedMs(prev, now),
214
+ });
215
+ }
178
216
  try {
179
217
  writeGoalTransitionLedger({
180
218
  timestamp: new Date().toISOString(),
@@ -210,7 +248,7 @@ export function recordTurn(opts: { tokens?: number; verdict?: Goal['lastVerdict'
210
248
  turnsUsed: current.usage.turnsUsed + 1,
211
249
  tokensUsed: current.usage.tokensUsed + (opts.tokens ?? 0),
212
250
  tokensMeasured: current.usage.tokensMeasured && opts.tokens !== undefined,
213
- elapsedMs: Date.now() - current.createdAt,
251
+ elapsedMs: goalWallClockElapsedMs(current, Date.now()),
214
252
  },
215
253
  lastVerdict: opts.verdict ?? current.lastVerdict,
216
254
  lastSummary: opts.summary ?? current.lastSummary,
@@ -251,7 +289,9 @@ const NO_GOAL_BUDGET_DIAGNOSTICS: GoalBudgetDiagnostics = {
251
289
  export function evaluateGoalBudget(goal: Goal, now: number): GoalBudgetDiagnostics {
252
290
  const { turnsUsed, tokensUsed, elapsedMs, tokensMeasured } = goal.usage;
253
291
  const { maxTurns, tokenBudget, wallClockMaxMs } = goal.budget;
254
- const elapsedWallClockMs = Math.max(elapsedMs, now - goal.createdAt);
292
+ // TUI-GOAL-LOOP-FRICTION(#35) — 기준 = 이번 활성화 시작(activeSince · 옛 스냅숏만 createdAt).
293
+ // usage.elapsedMs 도 같은 기준으로만 쓰인다(재활성화·hydrate 때 0 으로 되돌린다).
294
+ const elapsedWallClockMs = Math.max(elapsedMs, goalWallClockElapsedMs(goal, now));
255
295
  const exceededAxes = [
256
296
  ...(turnsUsed >= maxTurns ? [{ axis: 'maxTurns' as const, used: turnsUsed, limit: maxTurns }] : []),
257
297
  ...(tokenBudget > 0 && tokensUsed >= tokenBudget
@@ -69,7 +69,8 @@ export interface GoalUsage {
69
69
  tokensUsed: number;
70
70
  /** Whether every recorded auto-turn supplied a token measurement. */
71
71
  tokensMeasured: boolean;
72
- /** Wall-clock since createdAt, refreshed on read. */
72
+ /** Wall-clock since this activation began (`activeSince`, falling back to
73
+ * `createdAt` for old snapshots), refreshed on read. */
73
74
  elapsedMs: number;
74
75
  }
75
76
 
@@ -97,6 +98,11 @@ export interface Goal {
97
98
  status: GoalStatus;
98
99
  /** Epoch ms. */
99
100
  createdAt: number;
101
+ /** TUI-GOAL-LOOP-FRICTION(#35) — epoch ms when the goal last entered `active`
102
+ * (start · resume · process restart hydrate). The wall-clock budget is measured
103
+ * from here, not from `createdAt`: a goal created days ago and resumed now must
104
+ * not report 4.9e8 ms elapsed. Optional so older snapshots still parse. */
105
+ activeSince?: number;
100
106
  /** Last time status transitioned. */
101
107
  updatedAt: number;
102
108
  budget: GoalBudget;
@@ -34,6 +34,7 @@ const RELEASE_NODE_COMMANDS: Readonly<Record<string, { recipe: string; command?:
34
34
  'checklist-gate': { recipe: 'cmd:checklist-gate', command: 'bun scripts/release-loop/checklist-gate-node.ts' },
35
35
  prefetch: { recipe: 'cmd:docs', command: 'bun scripts/release-loop/docs-node.ts --json' },
36
36
  gate: { recipe: 'cmd:gate', command: 'bun scripts/release-loop/gate-node.ts --json' },
37
+ 'gate-triage': { recipe: 'cmd:gate-triage', command: 'bun scripts/release-loop/gate-triage-node.ts' },
37
38
  'mac-smoke': { recipe: 'cmd:mac-smoke', command: 'bun scripts/release-loop/mac-smoke-node.ts' },
38
39
  'export-check': { recipe: 'cmd:export-check', command: 'bun scripts/release-loop/export-check-node.ts' },
39
40
  pwa: { recipe: 'cmd:pwa', command: 'bun scripts/release-loop/node-verdict.ts pwa' },
@@ -478,6 +478,32 @@ export function isGrokSubscriptionExpiring(
478
478
  return (opts.now ?? Date.now)() + (opts.bufferMs ?? GROK_REFRESH_BUFFER_MS) >= ms;
479
479
  }
480
480
 
481
+ /** 구독 만료 상태 — 칸 CRED-EXPIRY-REFRESH 기준 창. 이 안에 만료가 오면 갱신 후보다. */
482
+ export const GROK_EXPIRY_SOON_WINDOW_MS = 2 * 60 * 60 * 1000;
483
+
484
+ /** 만료 상태 셋 — ⛔ boolean 으로 접으면 「모름」과 «여유»가 «같은 값»이 된다. */
485
+ export type GrokSubscriptionExpiryState = 'healthy' | 'expiring' | 'unknown';
486
+
487
+ /** 실제로 쓸 구독(key 를 가진 scope 중 가장 늦게 만료되는 것)의 expires_at 만으로 상태를 가른다.
488
+ *
489
+ * ⭐ 칸 CRED-EXPIRY-REFRESH 첫 조각 — «토큰 없이» 읽는다:
490
+ * · 선택 규칙은 `readSubscriptionToken` 과 «같다»(key 없는 항목의 만료에 속지 않는다).
491
+ * · 반환은 상태 «하나»뿐 — 토큰 값은 어떤 형태로도 나가지 않는다.
492
+ * · 기존 `isGrokSubscriptionExpiring`(boolean)은 모름을 false 로 접는다 — 이 함수는
493
+ * 모름을 'unknown' 으로 «별도의 값»으로 낸다. 갱신 유도·호스트 발사는 후속 조각.
494
+ * · 순수 판정 — 부수효과가 없고, 파일이 못 읽히면 죽이지 않고 'unknown' 을 낸다. */
495
+ export function grokSubscriptionExpiryState(
496
+ opts: { home?: string; bufferMs?: number; now?: () => number } = {},
497
+ ): GrokSubscriptionExpiryState {
498
+ const raw = readSubscriptionToken(grokAuthFilePath(opts.home))?.expiresAt ?? null;
499
+ if (raw === null) return 'unknown';
500
+ const ms = Date.parse(raw);
501
+ if (!Number.isFinite(ms)) return 'unknown';
502
+ return (opts.now ?? Date.now)() + (opts.bufferMs ?? GROK_EXPIRY_SOON_WINDOW_MS) >= ms
503
+ ? 'expiring'
504
+ : 'healthy';
505
+ }
506
+
481
507
  /** 갱신 경로의 관측 — ⛔ 토큰은 «절대» 싣지 않는다(판정·결과·출처만). */
482
508
  function observeGrokAuth(event: string, data: Record<string, unknown>): void {
483
509
  try {