elanous 0.2.29 → 0.2.31

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 (709) 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 → LSXfY0u9QtfDbejY0Ukf_}/_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-50625904996a8fa8.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/6948.1062d4594652b5bf.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7371.80c05c653b08e907.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7391.ebae168025c91b6a.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{1852.7f7b53991fcb99ef.js → 7502.ccd5b8065545edd0.js} +2 -2
  93. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +8 -0
  94. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{6534.1ab00f370db7b105.js → 7862.30a8d6482bb238eb.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7947-d0759430f8042240.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{285adb1a.5654e605dddf86d2.js → 7a1887c4.f36b88fba31da8fd.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/8018.a32663f04a6fc5f9.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/8097-92f87bd108322f66.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +24 -0
  109. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{1516.dfaceff3fd2d0e0d.js → 9138.b6e80031afb1d973.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/{9907.49be0fcadaf214b5.js → 9221.7d22eaf353a2d4e7.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/{8767-2f6a634f5abeaa01.js → 9279-d28c5b7754d010ae.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{3160-95c96f4d2a21dd26.js → 9398-6aed28f0a96d0d6f.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/{328bb3ab-6628a1114fe3fb3b.js → 997ce8a5-2ad501927003b41b.js} +1 -1
  121. package/apps/pwa/out/_next/static/chunks/{6853.cc5174a9dee59593.js → 9989.3694300e5f000b14.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-bf3634a117322df7.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-3535a4a69f1fafaa.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-b9f51447f757fbf2.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-a5635eba0d73b84e.js → page-2ebb8fd59b9461f9.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-a2fc4e49a4fa4ba8.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5a503541353e60a8.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-6025101384e361a5.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-f4b630cc8b5273cd.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-a645f00a06483e9a.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-61ac22cbab1ee4ec.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e9bab7413b028b28.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-c992eafe028b642e.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-e547e6ce55f1fb33.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-d27e1b9850fd54bf.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-805ac2e137b7a121.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-0fec29c7ba01894b.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-339a07410cd6f36b.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-349f2752d8cd37ad.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-d5c6dcf31c25cecc.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-2fd93ed8c9f16580.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-9b564dbff0fefc35.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-62f4475d3c38fb9b.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-814ba51385225776.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-397d479626e5a459.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-af1bba4e3251b728.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3ad3be5e7d3fdce9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-f219ccdbc0bf27df.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4da93ad35d4a8d28.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-456666766e5ce6fc.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-7404e377e8adb8c5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-14456240ed2a36ad.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-3a658d5fa6d16a25.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-0157de6e85f8bb6b.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0a07363b130182a1.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-841d73d4ac44ed49.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6f43a3d1146e6377.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/{page-29900ed0011b2c83.js → page-521ee23ac8c0abfa.js} +1 -1
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-a9992ee285efaa83.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-3d48e85fa89ec04f.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d2ced71132fac9b2.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-f08358bdf251adb9.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-233c71cd5f68f0d7.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-70151d78de2f375e.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-dcac33a3e2cb72c0.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-9f92737b8766009a.js → page-149dc1caba411bb9.js} +1 -1
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0448145ccd5c45c1.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-9cb0c444903da65d.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-17e504c396e0e389.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{425cfc4d.fb91158e3971c39f.js → b313b242.cb1265eea908db61.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +122 -0
  175. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +1 -0
  183. package/apps/pwa/out/_next/static/css/{72becfd42807b50d.css → 3f5fc6f96e2cf7b2.css} +1 -1
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +23 -23
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +23 -23
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +23 -23
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +22 -22
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +22 -22
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +23 -23
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +22 -22
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +23 -23
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +23 -23
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +22 -22
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +23 -23
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +23 -23
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +23 -23
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +23 -23
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +22 -22
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +22 -22
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +23 -23
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +23 -23
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +23 -23
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +22 -22
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +22 -22
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +23 -23
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +23 -23
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +22 -22
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +22 -22
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +22 -22
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +23 -23
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +23 -23
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +22 -22
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +23 -23
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +23 -23
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +23 -23
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +23 -23
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +23 -23
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +22 -22
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +23 -23
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +23 -23
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +23 -23
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +23 -23
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +23 -23
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +23 -23
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +23 -23
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +23 -23
  277. package/bin/elanous.mjs +7 -3
  278. package/graphs/context/nightly-condense.yaml +1 -0
  279. package/graphs/intake/intake-daily.yaml +8 -1
  280. package/graphs/intake/recipes.yaml +5 -0
  281. package/graphs/lecture/lecture-note.yaml +2 -2
  282. package/graphs/ops/harness-gc.yaml +1 -0
  283. package/graphs/release/recipes.yaml +3 -0
  284. package/graphs/release/release-loop.yaml +9 -5
  285. package/graphs/units/recipes.yaml +3 -0
  286. package/graphs/units/release-gate-recheck.yaml +15 -0
  287. package/package.json +2 -1
  288. package/scripts/check-native-tool-wiring.ts +3 -2
  289. package/scripts/ci-android-unit-tests.ts +16 -0
  290. package/scripts/ci-typecheck-changed.ts +37 -9
  291. package/scripts/coord-channel-watch.sh +22 -16
  292. package/scripts/coord-role-pack.ts +81 -2
  293. package/scripts/cron-run.ts +96 -3
  294. package/scripts/detect-test-interference.ts +38 -5
  295. package/scripts/gate-pr-cache.ts +65 -0
  296. package/scripts/gh-surface/capture.ts +126 -0
  297. package/scripts/gh-surface/gh +40 -0
  298. package/scripts/import-graph-scc.ts +38 -0
  299. package/scripts/install.ps1 +19 -0
  300. package/scripts/intake-social.ts +181 -0
  301. package/scripts/lecture-note/node.ts +71 -4
  302. package/scripts/lib/android-toolchain.ts +32 -0
  303. package/scripts/lib/deterministic-env.ts +62 -0
  304. package/scripts/lib/gate-test-env.ts +19 -0
  305. package/scripts/measure-goal-grounding-corpus.ts +2 -1
  306. package/scripts/ratchets/test-interference-patterns.allowlist.txt +257 -0
  307. package/scripts/ratchets/test-interference-patterns.ts +367 -0
  308. package/scripts/release-loop/cut-branch.ts +4 -3
  309. package/scripts/release-loop/export-check-node.ts +0 -1
  310. package/scripts/release-loop/fixtures/warm-failure-assertion/sample.junit.xml +8 -0
  311. package/scripts/release-loop/fixtures/warm-job-029/sample.json +1 -0
  312. package/scripts/release-loop/fixtures/warm-job-029/sample.junit.xml +10 -0
  313. package/scripts/release-loop/gate-closure.ts +12 -8
  314. package/scripts/release-loop/gate-diff.ts +1 -1
  315. package/scripts/release-loop/gate-host-mirror.ts +66 -0
  316. package/scripts/release-loop/gate-node.ts +523 -75
  317. package/scripts/release-loop/gate-partial.ts +106 -9
  318. package/scripts/release-loop/gate-pr-cache.ts +66 -0
  319. package/scripts/release-loop/gate-prewarm-plan.ts +66 -0
  320. package/scripts/release-loop/gate-r2-verdict.ts +189 -0
  321. package/scripts/release-loop/gate-recheck-plan-node.ts +52 -0
  322. package/scripts/release-loop/gate-warm-artifacts.ts +253 -0
  323. package/scripts/release-loop/import-graph-scc-run.ts +54 -0
  324. package/scripts/release-loop/import-graph-scc.ts +215 -0
  325. package/scripts/release-loop/node-verdict.ts +2 -0
  326. package/scripts/release-loop/probe-pod-host-dirs-live.ts +113 -0
  327. package/scripts/release-loop/resume-release.ts +33 -4
  328. package/scripts/release-loop/resume.ts +13 -5
  329. package/scripts/release-loop/spawn-lane.ts +2 -19
  330. package/scripts/release-loop/tui-sim-node.ts +3 -2
  331. package/scripts/release-loop/unattended-release.ts +16 -0
  332. package/scripts/test-deterministic.ts +5 -61
  333. package/scripts/train-reach-grouping.ts +48 -0
  334. package/scripts/tsc-incremental-cache.ts +86 -0
  335. package/scripts/tsc-incremental-diagnostics.ts +76 -0
  336. package/scripts/tsc-main-safety-net.ts +2 -2
  337. package/src/acp/client.ts +4 -1
  338. package/src/acp/core-turn-bridge.ts +50 -5
  339. package/src/ad-pipeline/run.ts +4 -2
  340. package/src/agent/runner.ts +169 -3
  341. package/src/agent/subagent-callable.ts +3 -2
  342. package/src/agent-mission/review-loop.ts +6 -3
  343. package/src/agent-substrate/acp-implementer.ts +14 -2
  344. package/src/agent-substrate/pr-reviewer.ts +12 -0
  345. package/src/agent-substrate/self-review-cli.ts +30 -4
  346. package/src/autopilot/pr-manager.ts +62 -7
  347. package/src/briefing/brief-items.ts +24 -22
  348. package/src/capture/tui-self-report.ts +15 -0
  349. package/src/chat/chat-cli.ts +10 -2
  350. package/src/chat/index.ts +77 -11
  351. package/src/chat/input-control-key.ts +5 -3
  352. package/src/chat/tool-interrupt-behavior.ts +38 -0
  353. package/src/chat/tool-render/dispatch.ts +5 -1
  354. package/src/chat/turn-typeahead.ts +75 -20
  355. package/src/cli/__fixtures__/agent-help-before/agent.txt +4 -2
  356. package/src/cli/agent-cli.ts +41 -13
  357. package/src/cli/config-audit.ts +123 -0
  358. package/src/cli/decisions-cli.ts +80 -6
  359. package/src/cli/log-event-names.ts +7 -0
  360. package/src/cli/mcp-login.ts +21 -5
  361. package/src/cli/msg-cli.ts +88 -0
  362. package/src/cli/pr-cli.ts +214 -57
  363. package/src/cli/pr-land-heavy-remote.ts +2 -2
  364. package/src/cli/pr-land-overlap-evidence.ts +63 -9
  365. package/src/cli/pr-land-pod-shadow-job.ts +83 -0
  366. package/src/cli/pr-land-pod-shadow.ts +16 -3
  367. package/src/cli/pr-land-virtual-rebase.ts +108 -0
  368. package/src/cli/release-cli.ts +80 -14
  369. package/src/cli/seat-cli.ts +138 -3
  370. package/src/cli/self-update.ts +5 -2
  371. package/src/cli/tasks-cli.ts +97 -19
  372. package/src/context-bus/context-first.ts +13 -0
  373. package/src/context-bus/context-now.ts +13 -2
  374. package/src/context-bus/ops-events.ts +75 -0
  375. package/src/control-plane/lease-holder.ts +12 -8
  376. package/src/control-plane/ledger.ts +163 -4
  377. package/src/control-plane/server.ts +91 -11
  378. package/src/core-turn/run-core-turn.ts +1 -0
  379. package/src/core-turn/run-goal-loop.ts +222 -3
  380. package/src/core-turn/seat-turn-cap.ts +79 -0
  381. package/src/core-turn/short-question.ts +38 -6
  382. package/src/core-turn/types.ts +3 -0
  383. package/src/dashboard/acp-boot.ts +3 -0
  384. package/src/dashboard/index.ts +224 -615
  385. package/src/dashboard/input/chat-main-entry-runtime.ts +1 -1
  386. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +4 -1
  387. package/src/dashboard/input/chat-main-session.ts +8 -1
  388. package/src/dashboard/input/chat-main-turn.ts +12 -3
  389. package/src/dashboard/input/compact-prepass-recovery.ts +33 -0
  390. package/src/dashboard/input/external-input-host.ts +26 -0
  391. package/src/dashboard/input/internal-turn.ts +261 -0
  392. package/src/dashboard/input/seat-autowake.ts +318 -0
  393. package/src/dashboard/input/turn-steer-bridge.ts +70 -0
  394. package/src/dashboard/loop-scheduler.ts +72 -0
  395. package/src/dashboard/seat-boot-preamble.ts +18 -0
  396. package/src/dashboard/skill-runtime.ts +2 -0
  397. package/src/dashboard/slash-runtime/dashboard-handlers.ts +126 -1181
  398. package/src/dashboard/slash-runtime/help-from-registry.ts +2 -1
  399. package/src/dashboard/slash-runtime/index.ts +13 -0
  400. package/src/dashboard/turn-busy-teardown.ts +50 -0
  401. package/src/decisions/decision-ledger.ts +45 -4
  402. package/src/discord-self-message.ts +4 -4
  403. package/src/domains/release-tool.ts +18 -1
  404. package/src/field-note/transcribe.ts +17 -0
  405. package/src/github/remote-call.ts +81 -0
  406. package/src/goals/registry.ts +46 -6
  407. package/src/goals/types.ts +7 -1
  408. package/src/graph-runner/runner.ts +1 -0
  409. package/src/grok/credential.ts +26 -0
  410. package/src/harness/harness-clean.ts +2 -1
  411. package/src/harness/harness-cli-command.ts +52 -5
  412. package/src/harness/harness-gc.ts +2 -1
  413. package/src/harness/harness-incidents.ts +57 -20
  414. package/src/harness/harness-pod-dispatch.ts +87 -10
  415. package/src/harness/harness-queue.ts +53 -4
  416. package/src/harness/harness-stop.ts +4 -3
  417. package/src/harness/harness-substrate-default.ts +78 -7
  418. package/src/harness/harness-worktree-auto.ts +2 -1
  419. package/src/harness/harness-worktrees.ts +3 -2
  420. package/src/hooks/dispatch.ts +2 -1
  421. package/src/index.ts +152 -25
  422. package/src/input/external-input-queue.ts +33 -1
  423. package/src/intake-plane/slash.ts +14 -3
  424. package/src/keybindings.ts +4 -1
  425. package/src/llm/tool-loop-steered-rejection.ts +33 -0
  426. package/src/llm.ts +138 -19
  427. package/src/loops/consensus.ts +348 -0
  428. package/src/loops/msg-wake.ts +206 -4
  429. package/src/loops/observe.ts +19 -3
  430. package/src/loops/orchestrator/tick.ts +64 -10
  431. package/src/loops/orchestrator/traffic.ts +64 -1
  432. package/src/loops/registry.ts +30 -0
  433. package/src/market/signed-index.ts +14 -0
  434. package/src/mcp/mcp-oauth.ts +54 -5
  435. package/src/models/prompts.ts +28 -0
  436. package/src/msg/msg-sse-client.ts +223 -0
  437. package/src/msg/msg-store.ts +86 -2
  438. package/src/mss/logging/log-store.ts +225 -1
  439. package/src/nexus/DAEMON-THIN-inventory-2026-10-15.md +122 -0
  440. package/src/nexus/api/http-server.ts +54 -15
  441. package/src/nexus/api/loop-resources.ts +19 -3
  442. package/src/nexus/api/ops-api.ts +11 -0
  443. package/src/nexus/daemon-thin-inventory.md +37 -0
  444. package/src/nexus/index.ts +70 -0
  445. package/src/nexus/state/state.ts +1 -0
  446. package/src/oauth/store.ts +32 -7
  447. package/src/onboarding/first-run.ts +28 -3
  448. package/src/ops/overview-cache.ts +16 -4
  449. package/src/ops/resources.ts +82 -0
  450. package/src/plugins/maker/connector-maker.ts +41 -0
  451. package/src/plugins/maker/skill-maker.ts +42 -0
  452. package/src/pr-tool/census.ts +288 -0
  453. package/src/prompt-library/chat-conciseness.ts +13 -0
  454. package/src/prompt-library/universal-preamble.ts +10 -5
  455. package/src/release-loop/gate-host-reservation.ts +97 -0
  456. package/src/release-loop/landing-freeze.ts +5 -0
  457. package/src/release-loop/light-rc.ts +5 -2
  458. package/src/release-loop/placement.ts +92 -1
  459. package/src/release-loop/rehearsal-shard-plan.ts +149 -0
  460. package/src/release-loop/rehearsal.ts +73 -17
  461. package/src/repl/index.ts +20 -2
  462. package/src/seat-loop/seat-knowhow.ts +282 -0
  463. package/src/seat-loop/seat-loop.ts +41 -2
  464. package/src/seat-loop/traffic-signal.ts +3 -0
  465. package/src/self-dev/ask-launch-flow.ts +24 -3
  466. package/src/self-dev/decompose-compose-graph.ts +28 -27
  467. package/src/self-dev/decompose-proposal.ts +53 -38
  468. package/src/self-dev/decompose.ts +17 -0
  469. package/src/self-dev/dev-pipeline.ts +31 -2
  470. package/src/self-dev/draft-residue.ts +24 -9
  471. package/src/self-dev/draft-sweep.ts +59 -4
  472. package/src/self-dev/external-call-zero.ts +77 -0
  473. package/src/self-dev/launch-preflight-text.ts +6 -0
  474. package/src/self-dev/launch-preflight.ts +25 -3
  475. package/src/self-dev/local-supervisor-judge.ts +185 -0
  476. package/src/self-dev/run-supervisor.ts +229 -19
  477. package/src/self-dev/run-types.ts +20 -0
  478. package/src/self-dev/shard-aggregate.ts +13 -2
  479. package/src/self-dev/shard-plan.ts +47 -0
  480. package/src/self-dev/shard-run.ts +112 -10
  481. package/src/self-dev/spawn-lane-files.ts +19 -0
  482. package/src/self-implement/app-api-budget.ts +51 -11
  483. package/src/self-implement/config-gate.ts +2 -1
  484. package/src/self-implement/expose-gate.ts +2 -1
  485. package/src/self-implement/gate-artifacts.ts +405 -0
  486. package/src/self-implement/gate-cli.ts +201 -16
  487. package/src/self-implement/gate-remote.ts +110 -25
  488. package/src/self-implement/gate-scenario.ts +85 -0
  489. package/src/self-implement/goal-author.ts +160 -8
  490. package/src/self-implement/graph-authority.ts +38 -6
  491. package/src/self-implement/graph-overlay-condition.ts +92 -1
  492. package/src/self-implement/heal-triage.ts +79 -0
  493. package/src/self-implement/host-regate.ts +61 -3
  494. package/src/self-implement/orchestrator.ts +14 -123
  495. package/src/self-implement/pr-title.ts +123 -0
  496. package/src/self-implement/run-checkpoint.ts +235 -0
  497. package/src/self-implement/run-ledger.ts +155 -10
  498. package/src/self-implement/run-stop.ts +4 -3
  499. package/src/self-implement/run-ttl.ts +28 -4
  500. package/src/self-implement/seams.ts +2 -0
  501. package/src/self-implement/shadow-stage.ts +7 -6
  502. package/src/self-implement/stop-autoheal.ts +2 -1
  503. package/src/self-implement/train-gate-remote.ts +37 -8
  504. package/src/self-implement/train-reach-grouping.ts +157 -0
  505. package/src/self-implement/train-scenario-layers.ts +133 -0
  506. package/src/self-implement/uncommitted-work.ts +2 -0
  507. package/src/session/pending-input.ts +19 -0
  508. package/src/session/session-start.ts +224 -0
  509. package/src/session-runtime/parallel-dispatch.ts +22 -5
  510. package/src/skills/tools/dev-harness.ts +6 -4
  511. package/src/skills/tools/shell.ts +1 -1
  512. package/src/steward/resync-tick.ts +114 -23
  513. package/src/steward/ta-repair-event-collect.ts +158 -0
  514. package/src/task-agent/card-evidence.ts +25 -1
  515. package/src/task-agent/green-sweep.ts +8 -3
  516. package/src/task-agent/judge.ts +76 -0
  517. package/src/task-agent/land-queue.ts +79 -0
  518. package/src/task-agent/live-moves.ts +30 -9
  519. package/src/task-agent/postland-card.ts +5 -0
  520. package/src/task-agent/prelaunch-already-done.ts +98 -0
  521. package/src/task-agent/real-launch-guard.ts +37 -0
  522. package/src/task-agent/rejudge-on-head.ts +89 -12
  523. package/src/task-agent/remote-rejudge-bind.ts +28 -8
  524. package/src/task-agent/repair-cycle.ts +45 -7
  525. package/src/task-agent/repair-worker.ts +7 -0
  526. package/src/task-agent/run-identity.ts +200 -0
  527. package/src/task-agent/shadow.ts +50 -3
  528. package/src/task-agent/task-hand.ts +158 -5
  529. package/src/task-agent/train-landing.ts +77 -0
  530. package/src/task-agent/work-order.ts +104 -0
  531. package/src/task-orchestrator/surfaces/pod-command-job.ts +88 -28
  532. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +88 -19
  533. package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +2 -1
  534. package/src/task-orchestrator/surfaces/pod-image-source.ts +2 -1
  535. package/src/task-orchestrator/surfaces/pod-install-slots.ts +121 -9
  536. package/src/task-orchestrator/surfaces/pod-lease.ts +20 -6
  537. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +169 -0
  538. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +27 -0
  539. package/src/task-orchestrator/surfaces/pod-result-marker-wait.ts +33 -0
  540. package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +63 -0
  541. package/src/task-orchestrator/surfaces/pod-source-receive.ts +19 -3
  542. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +141 -0
  543. package/src/task-orchestrator/surfaces/pod-warm-ta-shadow.ts +85 -0
  544. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +153 -0
  545. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +208 -0
  546. package/src/task-orchestrator/surfaces/self-implement-pod.ts +132 -15
  547. package/src/telegram.ts +2 -4
  548. package/src/tui/input/pickers.ts +732 -0
  549. package/src/tui/slash/mission.ts +492 -0
  550. package/src/tui/slash/term.ts +877 -0
  551. package/src/tui-client/dashboard-session.ts +9 -0
  552. package/src/tui.ts +48 -0
  553. package/src/user-config/gemini-server-tools.ts +9 -0
  554. package/src/user-config.ts +53 -16
  555. package/src/util/spawn-sync-output.ts +7 -0
  556. package/src/ux-sim/fake-bin.ts +154 -0
  557. package/src/ux-sim/fake-clock.ts +36 -0
  558. package/src/ux-sim/headless-tui.ts +72 -0
  559. package/src/ux-sim/in-process-cli.ts +2 -1
  560. package/src/ux-sim/temp-git-remote.ts +82 -0
  561. package/src/version/packed-revision.json +1 -1
  562. package/src/video-pipeline/recipes/vlog.ts +25 -7
  563. package/apps/pwa/out/_next/static/chunks/1015-9cba22a5a4554797.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/1084-4e66af7376a18db7.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/1106.3a6410b7b0c70ad4.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/1127.32fc642f70423506.js +0 -8
  567. package/apps/pwa/out/_next/static/chunks/1189-d9b8e1995a831fae.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/1224.6da98c7ad0eb7dd8.js +0 -34
  569. package/apps/pwa/out/_next/static/chunks/1370.4e3e2558f111fd5d.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/1514-b7da377fdc4eb17a.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/1577.3c24c32584e10d70.js +0 -286
  572. package/apps/pwa/out/_next/static/chunks/161.130a569855c4d68c.js +0 -24
  573. package/apps/pwa/out/_next/static/chunks/1864.16da9556e78a9236.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/1b6f6b2a.aff090122be3726e.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/2341.e8a9135e5b910dd5.js +0 -36
  576. package/apps/pwa/out/_next/static/chunks/257.10afdbe3520f2fc2.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/2658.87550e692dc493f6.js +0 -64
  578. package/apps/pwa/out/_next/static/chunks/2673.85e2f02efae7a85f.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/2766-bee623b37472ed63.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/2794.6d836872b0bf43e2.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/2806.eb57ced1a528ed39.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/283.7a121c4910e6dad7.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/2870.1dc29aa5fc01f09f.js +0 -115
  584. package/apps/pwa/out/_next/static/chunks/293.fd38e77ce5d48103.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/2993-5952a1cd3fd5d441.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/32-a523427c5bd5ed00.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/3280.af38002a6424a905.js +0 -90
  588. package/apps/pwa/out/_next/static/chunks/3292-5ddffdb2f517b44f.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/3523.d3231daaf7aba993.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/3763.577de3181e1e6ea0.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/4138-cc56b0938ef72783.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/4255-8350c43d307a8875.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/4352.a4954693499c2714.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/4421-f2dcbc6fd80ec013.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/4513-51b94aeee37c9905.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/4928-c3b72f99aa588699.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/4986-f9fc0283fb325219.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/5014.671a7b9f69c2b7b1.js +0 -94
  599. package/apps/pwa/out/_next/static/chunks/5137.d398e24710b3feb6.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/5298.0bea74885cabe754.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/5315.74c53a39739b11c4.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/5317-8b1df8d02105cd07.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/5358-33051f41a2b239af.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/5388.a2ec02501f714ad5.js +0 -62
  605. package/apps/pwa/out/_next/static/chunks/5395.e6ac948a2d15058d.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/5409-f282bf533c151849.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/5466.d0f4aba9d52acfa8.js +0 -27
  608. package/apps/pwa/out/_next/static/chunks/5564.3d28b353470fe305.js +0 -104
  609. package/apps/pwa/out/_next/static/chunks/569.b0cbfb4dee4a167a.js +0 -93
  610. package/apps/pwa/out/_next/static/chunks/5835-72465b73a8bb8d74.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/5941.045d6093139e1f7a.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/6228.47cabb83564dfe54.js +0 -24
  613. package/apps/pwa/out/_next/static/chunks/6457-8d1993002a87d0fe.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/6513.ef54cfae52351982.js +0 -2
  615. package/apps/pwa/out/_next/static/chunks/6582.13d245bf87df69f1.js +0 -148
  616. package/apps/pwa/out/_next/static/chunks/6655.c33f6acc2c61bb89.js +0 -149
  617. package/apps/pwa/out/_next/static/chunks/6731-9d7195af289c8d4c.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/6733.0be05b1db9279fc5.js +0 -79
  619. package/apps/pwa/out/_next/static/chunks/6742-90937146253a8b9e.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/687-816858f77a396a4e.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/6888.72c7b8cfb02a7e61.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/6993.c17c828ac3ef3253.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/7076.9f27ef666b0cefdd.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/7113.1403c5d451804e8c.js +0 -287
  625. package/apps/pwa/out/_next/static/chunks/7169.9dd603c726c2cd7c.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/7247-e3ec21f761f86710.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/7261-a47b72f96f5199df.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/7335-5cda81ac90310077.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/7356-c2e4af090546e2d4.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/738-59518742078fda71.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/7385.a7375f10db73243a.js +0 -77
  632. package/apps/pwa/out/_next/static/chunks/7445.cf1cf2fe109bcd96.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7505.a2057432ab6ef99a.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/7515.31f45fac1f9c70cb.js +0 -74
  635. package/apps/pwa/out/_next/static/chunks/7658-d4ce8c72a9548a91.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8107-0b1ae46bc5bcb788.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8132.bfea2f016ce8d992.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8672.e299832bc7d18ee2.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8735.f1ef798a5f260f6f.js +0 -226
  640. package/apps/pwa/out/_next/static/chunks/8774-88f67f7d8d53d299.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/8782.252e5a405d86e4ed.js +0 -32
  642. package/apps/pwa/out/_next/static/chunks/909-9ed7aa0dba804316.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/9100.22b0301256827007.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/9140-dde77575570bc71e.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/9292-a7ad9a01c7044b93.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/9517.00627dc51b02821c.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/9725.dcbb585652d8e9c5.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/9761.a3ebf5c389c24ddc.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/9972.834d2b2b7d48f1ed.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/9995-1a90486f5ae01546.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/a435ad95.577add051b4facb2.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-83372d8b5863ebf7.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d69256d7613c140a.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-352c5e9ba3f9b858.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/board/page-69f1d725524201a8.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/bots/page-ad9181d84285e4ea.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/ceo/page-86feb08080261a2f.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/chat/page-ffa15987e4756d03.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/consult/page-11a3ab44ac327cc1.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/control/page-6309b11c4264d24d.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/decisions/page-8800472079612e87.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/design-check/page-d387bf3f2301882a.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/editor/page-048f88d8c8658da6.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/exec/page-6c14f9739b7f6880.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/field/page-2834f44f64e98d74.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/healthz/route-83372d8b5863ebf7.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/inside/page-a13df6f88e824887.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/intake/page-8a4397248754cf2b.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/layout-daf0bd329360a336.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/live/page-f7ef1c28764cb476.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/live-run/page-61e74f28b9353dac.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/loops/page-8aaa6dedc4c10dfb.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/market/page-294602ba649be829.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-5fa5f41ed3fce323.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/morning/page-33bf667a5fb297bf.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/not-found-5ae3cbd6e5dba515.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eb867d16ad6fdede.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1ab789fa68682f7e.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6fd41b5b6ff13a71.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fb3d5f7642c99fba.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/outputs/page-70f705b2b5ed42ca.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/page-c9c93b358a7667b5.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1563c556b81c96ed.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ce89c20c45b3a673.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/sessions/page-423ae63d786f4ee9.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-591a5378a4c4447c.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/settings/page-a250496b19a5824b.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c36a9277febc5911.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/setup/layout-83372d8b5863ebf7.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/share/page-a5de57edc6d7f942.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/showroom/page-16bdd4c9f40498df.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/tasks/page-75e946e6e71cc6d8.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/term/page-9eb56166774eb256.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/today/page-1c5f9ef35dbb22c5.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/trace/page-657ff9985e508498.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/vault/page-22dc72499da68fb2.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/workflows/page-094ad334e40f8169.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8bcd379852ff6287.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-6b04413f280cd344.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/de70ab42-d28d47437208d458.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/f800c5e3.46dbd7f2af8c3fce.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/f8bc19ad.47579f6d684b0760.js +0 -122
  703. package/apps/pwa/out/_next/static/chunks/framework-ca9ac4609fb9fed8.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/main-app-0dd08d32d15a030f.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/main-f0399d33955c4e01.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/pages/_app-d42caca6f8ac2211.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/pages/_error-42e713c13aa41ca8.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/webpack-8fbf8f61a377aa16.js +0 -1
  709. /package/apps/pwa/out/_next/static/{cIpv5nU9AycwYWZI9H589 → LSXfY0u9QtfDbejY0Ukf_}/_ssgManifest.js +0 -0
@@ -8,6 +8,7 @@ import { join } from 'path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { createRunningBuildStatus } from './running-build-status.js';
10
10
  import { reportTuiTurn } from '../pty-shell/tui-run-state.js';
11
+ import { logTurnThrew, runTurnTeardownStep } from './turn-busy-teardown.js';
11
12
  import { createStaleInstallCheck, formatStaleInstallNotice, readCurrentInstalledCommit, readDashboardBootCommit, STALE_INSTALL_CHECK_INTERVAL_MS, versionedDashboardInstallDir } from './stale-install-check.js';
12
13
  import { spawn, spawnSync } from 'node:child_process';
13
14
  import { $ } from 'bun';
@@ -557,21 +558,50 @@ export function resumeInProcessDashboardSession(
557
558
  setAttachedSessionId: (sessionId: string) => void;
558
559
  setActiveSessionId: (sessionId: string) => void;
559
560
  },
560
- ): { resumed: true; sessionId: string; turns: number } | { resumed: false; reason: string } {
561
+ ):
562
+ | { resumed: true; sessionId: string; turns: number; repaired?: { removedCalls: number; removedResults: number } }
563
+ | { resumed: false; reason: string } {
561
564
  const resolvedId = deps.resolveSessionId(resumeSessionId);
562
565
  const loaded = resolvedId ? deps.historyFromSession(resolvedId) : null;
563
566
  if (!resolvedId || !loaded) return { resumed: false, reason: resumeSessionId };
564
567
 
568
+ // ④ 깨진 기록 방어(TUI-H8 · #69) — 도구 호출 도중 죽은 프로세스의 기록은 호출·결과 짝이 깨진 꼬리를 남겨
569
+ // 이어받은 첫 턴이 Codex 400 으로 죽는다. 고아 호출·결과만 잘라(`repairCodexToolPairs` · 짝이 맞는 것은 그대로)
570
+ // 이어받고, 같은 id 가 겹쳐 짝을 못 가리면 이어받지 않는다(새 세션 권고).
571
+ const pairs = repairCodexToolPairs(loaded.history as unknown as LLMMessage[]);
572
+ if (pairs.ambiguousIds.length > 0) {
573
+ try {
574
+ debug.log('session', 'resume-refused', {
575
+ surface: 'tui', sessionId: resolvedId, reason: 'ambiguous-tool-pairs', ambiguousIds: pairs.ambiguousIds.length,
576
+ });
577
+ } catch { /* observation is best-effort */ }
578
+ return { resumed: false, reason: `${resumeSessionId} — 도구 호출·결과 짝이 모호한 기록이다 · 새 세션으로 시작한다(권고)` };
579
+ }
580
+ const repairedCount = pairs.removedCalls + pairs.removedResults;
581
+ const history = repairedCount > 0 ? pairs.messages as unknown as ChatMessage[] : loaded.history;
582
+ if (repairedCount > 0) {
583
+ try {
584
+ debug.log('session', 'resume-history-repaired', {
585
+ surface: 'tui', sessionId: resolvedId, removedCalls: pairs.removedCalls, removedResults: pairs.removedResults,
586
+ });
587
+ } catch { /* observation is best-effort */ }
588
+ }
589
+
565
590
  const sysFromDash = deps.chatHistory.find((message) => message.role === 'system');
566
591
  deps.chatHistory.length = 0;
567
592
  if (sysFromDash) deps.chatHistory.push(sysFromDash);
568
- for (const message of loaded.history) {
593
+ for (const message of history) {
569
594
  if (message.role === 'system' && sysFromDash && message.content === sysFromDash.content) continue;
570
595
  deps.chatHistory.push(message);
571
596
  }
572
597
  deps.setAttachedSessionId(resolvedId);
573
598
  try { deps.setActiveSessionId(resolvedId); } catch { /* best-effort */ }
574
- return { resumed: true, sessionId: resolvedId, turns: loaded.history.length };
599
+ return {
600
+ resumed: true,
601
+ sessionId: resolvedId,
602
+ turns: history.length,
603
+ ...(repairedCount > 0 ? { repaired: { removedCalls: pairs.removedCalls, removedResults: pairs.removedResults } } : {}),
604
+ };
575
605
  }
576
606
 
577
607
  /**
@@ -791,6 +821,7 @@ import { bootBannerStatusLines, normalizeBootBannerMode, renderMinimalBootBand,
791
821
  import { attachDashboardFilePathToken } from './file-path-attachment.js';
792
822
  import { bootDashboardSlashExecutor } from './slash-executor-boot.js';
793
823
  import { buildDashboardSlashRegistry, type DashboardSlashContext } from './slash-runtime/index.js';
824
+ import type { LoopScheduler } from './loop-scheduler.js';
794
825
  import { unknownSlashReply } from './slash-runtime/unknown-slash-reply.js';
795
826
  import { resolveSurfaceUx } from '../agent/surface-ux/build.js';
796
827
  import type { SurfaceUx } from '../agent/surface-ux/types.js';
@@ -984,13 +1015,18 @@ import {
984
1015
  } from '../render.js';
985
1016
  import type { SyncMode } from '../types.js';
986
1017
  import type { ChatMessage } from '../chat/index.js';
1018
+ import { repairCodexToolPairs } from './input/compact-prepass-recovery.js';
1019
+ import { observeSessionStart } from '../session/session-start.js';
987
1020
  import type { PlainTurnSubmit } from '../input/turn-submit.js';
988
1021
  // U-4.2 · `renderLogPane` no longer called from dashboard directly —
989
1022
  // wd-log widget owns log rendering via renderLogViaWidget. Only
990
1023
  // `findBlock` still used here (chat-input block navigation).
991
1024
  import { findBlock, logPaneContentHeight, maxLogPaneScroll, visibleSearchRowOffset } from '../panes/log-pane.js';
992
1025
  import { createQuitConfirmationState, gracefulQuit, type QuitConfirmationState } from './graceful-quit.js';
993
- import { handoffTurnTypeaheadAtTurnEnd, releaseTurnTypeaheadPending, steerLastTurnTypeaheadSubmission } from './input/turn-steer-bridge.js';
1026
+ import { consumeTurnEndInterrupted, handoffTurnTypeaheadAtTurnEnd, releaseTurnTypeaheadPending, runTurnTypeaheadPushNow, steerLastTurnTypeaheadSubmission } from './input/turn-steer-bridge.js';
1027
+ import { abortTurnOnly } from '../turn-abort-scope.js';
1028
+ import { consumeInternalTurnYield, createInternalTurnGate, formatInternalTurnBanner, resolveInternalTurnSubmitIntent, type InternalTurnRequest } from './input/internal-turn.js';
1029
+ import { createSeatAutowake } from './input/seat-autowake.js';
994
1030
  import { createAttachmentRowMap } from '../log-pane/attachment-row-map.js';
995
1031
  import { createAttachmentPopup, type AttachmentPopupAction } from '../log-pane/attachment-popup.js';
996
1032
  import { findLogMatches, findVisibleLogMatches, type LogSearchResult } from '../log-pane/search.js';
@@ -1022,7 +1058,6 @@ import { enqueueSoftStop, readSoftStopRequestStatus } from '../harness/control-i
1022
1058
  import { setSelfImplementSoftStopSpaceObserver } from '../self-implement/self-implement-runtime.js';
1023
1059
  import { createViEditor, type ViEditorHandle } from '../vi-editor.js';
1024
1060
  import { launchEditor, canLaunchEditor } from '../editor-launcher.js';
1025
- import { createSshPickerModal } from '../ssh/ssh-picker-modal.js';
1026
1061
  import {
1027
1062
  refreshRemoteWorkingDir,
1028
1063
  refreshRemoteWorkingDirPreview,
@@ -1032,12 +1067,10 @@ import {
1032
1067
  remoteBadge,
1033
1068
  } from '../ssh/remote-browser.js';
1034
1069
  import { editRemoteFile } from '../ssh/remote-edit.js';
1035
- import { scanFinder, relativizeResults } from '../finder/finder-scan.js';
1036
- import { scanRemoteFinder, relativizeRemoteResults } from '../finder/finder-scan-remote.js';
1037
- import { createFinderModal, createFolderPickerModal, type FinderItem } from '../finder/finder-modal.js';
1038
- import { homedir, hostname } from 'node:os';
1039
- import { listTransferTargets, type TransferTarget } from '../transfer/transfer-targets.js';
1040
- import { createTransferPickerModal } from '../transfer/transfer-picker-modal.js';
1070
+ import { createFolderPickerModal } from '../finder/finder-modal.js';
1071
+ import { hostname } from 'node:os';
1072
+ import { type TransferTarget } from '../transfer/transfer-targets.js';
1073
+ import { createInputPickers } from '../tui/input/pickers.js';
1041
1074
  import { sshTransfer, type SshTransferProgress } from '../transfer/ssh-transfer.js';
1042
1075
  import { iphoneTransfer } from '../transfer/iphone-transfer.js';
1043
1076
  import { DashboardAcpChat, displayBackendName, parseAcpSlash, type AcpBackendId } from './chat/acp-chat.js';
@@ -1081,7 +1114,6 @@ import {
1081
1114
  } from '../agent/channel-router.js';
1082
1115
  import { initTtySnapshotTools } from '../skills/tools/tty-snapshot.js';
1083
1116
  import { initAgentHandoffTool } from '../skills/tools/agent-handoff.js';
1084
- import { touchSshHost } from '../ssh/ssh-hosts.js';
1085
1117
  import { DisplayCoordinator } from '../display/coordinator.js';
1086
1118
  import {
1087
1119
  createLivePlaygroundHarness,
@@ -1162,7 +1194,6 @@ import {
1162
1194
  encodeBrowserScopedSubmitText,
1163
1195
  resolveBrowserActionContextFromHit,
1164
1196
  } from '../browser-pane/actions.js';
1165
- import { resolveBrowserTransferCapability } from '../browser-pane/capabilities.js';
1166
1197
  import {
1167
1198
  cyclePreviewSourceForView,
1168
1199
  formatPreviewSourceLabel,
@@ -1276,7 +1307,7 @@ import {
1276
1307
  type SpaceLongPressDetector,
1277
1308
  } from './input/space-longpress-detector.js';
1278
1309
  import { isAllowlistedDictationSurface } from './input/dictation-surface-allowlist.js';
1279
- import { createDashboardExternalInputHost } from './input/external-input-host.js';
1310
+ import { createDashboardExternalInputHost, stopTurnViaEscGate } from './input/external-input-host.js';
1280
1311
  import { registerExternalInputHost } from '../input/external-input-queue.js';
1281
1312
  // H3 #6 follow-up #3 — background sessions render as a distinct sidebar
1282
1313
  // kind with live state badge + tap-to-Join. Dashboard pulls BG records
@@ -2793,6 +2824,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
2793
2824
  // execute slashes, etc.
2794
2825
  const chatModeState = createSessionPostureState();
2795
2826
 
2827
+ // TUI-LOOP-COMMAND — 실행 중 스케줄러를 쥐는 ref. showDashboard 인스턴스당
2828
+ // 하나로, 입력 루프 «밖»에서 한 번만 선언한다. slashCtx 는 명령마다 다시
2829
+ // 만들어지므로 리터럴 옆에 선언하면 /loop start 로 만든 스케줄러 ref 가
2830
+ // 다음 명령에서 사라진다 — /loop stop · /session new|clear 세션 끝 정지가
2831
+ // 같은 ref 를 써야 «stop 0»(판정)가 성립한다.
2832
+ const dashboardLoopSchedulerRef: { current: LoopScheduler | null } = { current: null };
2833
+
2796
2834
  // T3-C2 — vi-editor mounted in the preview pane. Set by the
2797
2835
  // browser pane's `e` key on a file; cleared on :q / :q!. When
2798
2836
  // non-null it overrides the preview pane's normal render path.
@@ -2865,6 +2903,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
2865
2903
  // current value so log analysis can filter "what keys fired while
2866
2904
  // a turn was in flight".
2867
2905
  let streamingInFlight = false;
2906
+ /** 지금 턴의 escGate 중단 — 외부 `/stop`(TUI-STOP-INPUT-BYPASS)이 대기열을 건너뛰어 부른다. 턴 밖이면 null. */
2907
+ let currentTurnExternalStop: (() => void) | null = null;
2868
2908
  const resolveTmuxStatusLabel = (): string | null => {
2869
2909
  if (!process.env['TMUX']) return null;
2870
2910
  const fallback = process.env['TMUX_PANE'] ?? 'tmux';
@@ -3333,6 +3373,26 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3333
3373
  // 즉시 에코 · 턴 종료 시 loop-head 핸드오프가 prefill/자동제출로 넘긴다.
3334
3374
  // (외곽 스코프 선언 — 스트리밍 디스패처와 메인 키 루프 양쪽에서 참조.)
3335
3375
  const turnTypeaheadRef = { state: createTurnTypeaheadState() };
3376
+ // «지금 밀어 넣기»로 끊은 턴 — 턴 종료가 큐를 composer 로 «복원»하지 않고 곧바로 «제출»하게 한다.
3377
+ const steerInterruptRef = { pending: false };
3378
+ // ⭐ H2 내부 턴 문(TUI-H2-INTERNAL-TURN) — 자율 재개(H3·H4·H5)가 지나갈 «문 하나». 판정은 모듈,
3379
+ // 여기선 상태를 읽어 넘기기만 한다(⚠️ promptCtl·가시성 함수는 아래에서 선언 — 호출 시점엔 있다).
3380
+ const internalTurnGate = createInternalTurnGate({
3381
+ isTurnRunning: () => streamingInFlight,
3382
+ isInputForeground: () =>
3383
+ chatMainInputVisibilityState().inputOwnership?.owner === 'chat-main' && !topBlockingForegroundModal(),
3384
+ getBufferText: () => promptCtl.getBufferText?.(),
3385
+ getTypeaheadCount: () =>
3386
+ turnTypeaheadRef.state.queuedSubmissions.length + (turnTypeaheadRef.state.buffer ? 1 : 0),
3387
+ });
3388
+ // ⭐ H3 턴 끝 자동 재개(OPT-QUEUE-AUTOWAKE) — 자리 TUI ∧ autowake 파일일 때만. 턴 끝에 «남은 일»(안 읽은
3389
+ // 우편 · queue.md «## 대기»)을 재고 H2 문으로 다음 턴을 청한다. 판정·상한은 모듈, 여기선 배선만.
3390
+ const seatAutowake = createSeatAutowake({
3391
+ startTurnIfIdle: (text, opts) => internalTurnGate.startTurnIfIdle(text, opts),
3392
+ getCwd: () => workingDir.cwd,
3393
+ notify: (line) => { chatLines.push(C.muted(line)); draw(); },
3394
+ });
3395
+ const internalTurnYieldPort = seatAutowake.wrapPort(internalTurnGate.port);
3336
3396
  // ⭐ `B3` — 큐를 «도는 턴»에 넣으려면 그 턴이 도는 ***ACP 세션 id*** 가 필요하다.
3337
3397
  // ⛔ `attachedSessionId`(대화 세션)와 «다른 축»이다 — 루프는 ACP 세션으로 스코프를 연다.
3338
3398
  // ⚠️ 세션은 이 함수 «뒤»에서 만들어지므로 참조로 둔다(호출 시점엔 채워져 있다).
@@ -6458,8 +6518,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6458
6518
  const attachChatStreamingKeys = (abortCtrl: AbortController): () => void => {
6459
6519
  const chatLinesStartIndex = chatLines.length;
6460
6520
  userScrolledDuringStream = false;
6461
- streamingInFlight = true;
6462
- reportTuiTurn('busy');
6463
6521
  const SCROLL_KEYS = new Set(['j', 'k', 'down', 'up', 'pageup', 'pagedown', 'home', 'end', 'g', 'G']);
6464
6522
  // T3-B2: ESC abort gate. When the user hits Esc AND sub-agent
6465
6523
  // work is running, pop a confirmation modal before tearing down
@@ -6759,7 +6817,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6759
6817
  // ⛔ 중복 전송 방지가 계약이다: 레지스트리가 배수되면 `onDrained` 가
6760
6818
  // ***여기 큐도 같이 비운다*** — 안 그러면 같은 발화가 턴 끝에 «또» 나간다.
6761
6819
  // ⚠️ `immediate`(슬래시 등)는 지금 실행되므로 «넣지 않는다».
6762
- if (r.submissionDisposition === 'fifo' && !r.immediateSubmission) {
6820
+ // ⛔ H2 — 내부 턴 중의 사람 입력은 그 턴에 끼워 넣지 않는다(턴 끝 FIFO 로 기다린다).
6821
+ if (r.submissionDisposition === 'fifo' && !r.immediateSubmission && !internalTurnGate.isInternalTurnActive()) {
6763
6822
  const queuedText = turnTypeaheadRef.state.queuedSubmissions.at(-1);
6764
6823
  const sid = activeSessionIdForTypeahead();
6765
6824
  if (queuedText && sid) {
@@ -6790,6 +6849,22 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6790
6849
  }
6791
6850
  }
6792
6851
  if (r.releasedPendingIds) releaseTurnTypeaheadPending(r.releasedPendingIds, 'recalled');
6852
+ if (r.pushNowRequested) {
6853
+ const decision = runTurnTypeaheadPushNow(turnTypeaheadRef, {
6854
+ runningToolNames: () => getEscAbortWaitingToolNames(escAbortToolState),
6855
+ interruptTurn: () => {
6856
+ steerInterruptRef.pending = true;
6857
+ try { globalAgentRegistry.markBackgroundAllRunning(); } catch { /* 승격 실패가 끊기를 막지 않는다 */ }
6858
+ abortTurnOnly(abortCtrl);
6859
+ },
6860
+ pushNotice: (line) => pushChatLine(C.muted(line)),
6861
+ });
6862
+ debug.log('dashboard.turn-typeahead', 'push-now', {
6863
+ kind: decision.kind,
6864
+ tools: decision.kind === 'interrupt' ? decision.runningTools : decision.kind === 'nothing' ? [] : decision.blockingTools,
6865
+ });
6866
+ draw();
6867
+ }
6793
6868
  if (r.immediateSubmission) {
6794
6869
  turnTypeaheadRef.state = await dispatchStreamingTurnTypeaheadSubmission(
6795
6870
  turnTypeaheadRef.state,
@@ -7015,19 +7090,29 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7015
7090
  // `runStreamingKeyUnifiedDispatch` above for the route body.
7016
7091
  await runStreamingKeyUnifiedDispatch(key);
7017
7092
  });
7093
+ // ⛔ TUI-STALE-BUSY-MARKER — busy 는 «끄는 closure 를 돌려주기 직전»에 켠다. 위 준비(ESC 게이트·키 리스너)가
7094
+ // 던지면 호출자는 cleanup 을 못 받으므로, 먼저 켜 두면 아무도 못 끄는 busy 가 남는다(#37).
7095
+ streamingInFlight = true;
7096
+ reportTuiTurn('busy');
7097
+ const externalStop = (): void => { stopTurnViaEscGate(escGate); };
7098
+ currentTurnExternalStop = externalStop;
7018
7099
  return () => {
7019
- cleanup();
7020
- escGate.dispose();
7100
+ if (currentTurnExternalStop === externalStop) currentTurnExternalStop = null;
7101
+ // 단계마다 격리 — 앞 단계가 던져도 settle·idle 전이에 닿는다(키 층 유휴 ∧ 턴 층 busy 이원 판정 #55 차단).
7102
+ runTurnTeardownStep('stream-keys-detach', cleanup);
7103
+ runTurnTeardownStep('esc-gate-dispose', () => { escGate.dispose(); });
7021
7104
  try {
7022
7105
  if (!stoppedHarnessSpace) settleEscAbortPendingLines(chatLines, chatLinesStartIndex);
7023
7106
  } catch (err) {
7024
7107
  const message = err instanceof Error ? err.message : String(err);
7025
7108
  const stackHead = err instanceof Error ? err.stack?.split('\n')[1]?.trim() : undefined;
7026
7109
  debug.log('dashboard.turn', 'settle-failed', { message, stackHead });
7110
+ logTurnThrew('settle-esc-lines', err);
7027
7111
  } finally {
7028
7112
  userScrolledDuringStream = false;
7029
7113
  streamingInFlight = false;
7030
7114
  reportTuiTurn('idle');
7115
+ seatAutowake.noteTurnEnded();
7031
7116
  }
7032
7117
  };
7033
7118
  };
@@ -11561,63 +11646,53 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
11561
11646
  let agentSearchModal: SearchModalHandle | null = null;
11562
11647
  let agentSearchHandle: { dispose: () => void } | null = null;
11563
11648
 
11564
- /** Open the agent-roster search modal. Reads the current roster
11565
- * from agentSurfaceStore + globalAgentRegistry. Items: each
11566
- * entry's name + last 8 chars of id. Enter sets cursor; Esc
11567
- * dismisses. */
11568
- function openAgentRosterSearch(): void {
11569
- if (agentSearchModal) return;
11570
- const roster = agentSurfaceStore.syncTasks(globalAgentRegistry.list());
11571
- const cols = termSize().cols;
11572
- const modalWidth = Math.min(60, cols - 4);
11573
- const modalRow = 3; // top-anchored over the roster pane
11574
- const modalCol = Math.max(2, Math.floor((cols - modalWidth) / 2));
11575
- const filterable = roster.length > 3;
11576
- const handle = createSearchModal({
11577
- id: `agent-roster-search:${Date.now().toString(36)}`,
11578
- bounds: { row: modalRow, col: modalCol, width: modalWidth, height: 12 },
11579
- title: 'Search agents',
11580
- width: modalWidth,
11581
- maxVisible: 8,
11582
- primaryActionLabel: 'jump',
11583
- cancelActionLabel: 'cancel',
11584
- filterable,
11585
- actionButtons: true,
11586
- chromeSpec: {
11587
- titleAlign: 'center',
11588
- },
11589
- onQuery: (q) => {
11590
- const roster = agentSurfaceStore.syncTasks(globalAgentRegistry.list());
11591
- const needle = q.trim().toLowerCase();
11592
- const match = (t: typeof roster[number]) => {
11593
- if (!needle) return true;
11594
- return t.name.toLowerCase().includes(needle)
11595
- || t.definitionName.toLowerCase().includes(needle)
11596
- || (t.summary ?? '').toLowerCase().includes(needle)
11597
- || t.id.toLowerCase().includes(needle);
11598
- };
11599
- return roster.filter(match).map((t): SearchItem => ({
11600
- label: `${t.name} ${t.definitionName} ${t.id.slice(0, 8)}`,
11601
- payload: t.id,
11602
- }));
11603
- },
11604
- onAccept: (item) => {
11605
- const roster = agentSurfaceStore.syncTasks(globalAgentRegistry.list());
11606
- const idx = roster.findIndex(t => t.id === item.payload);
11607
- if (idx >= 0) {
11608
- agentRosterCursor = idx;
11609
- agentCursorLockedId = roster[idx]!.id;
11610
- agentCursorManual = true;
11611
- }
11612
- closeAgentRosterSearch();
11613
- },
11614
- onCancel: () => closeAgentRosterSearch(),
11615
- theme: currentThemeTokens(),
11616
- });
11617
- attachSurfaceToWorkspace(handle.surface, currentWorkspaceOwnerId());
11618
- agentSearchModal = handle;
11619
- agentSearchHandle = display.pushModal(handle.surface);
11620
- }
11649
+ // TUI-DIET-SEAM-PICKERS — the five input pickers (agent-roster search,
11650
+ // transfer, Ctrl+T file finder, Alt+C directory finder, Ctrl+K SSH)
11651
+ // live in src/tui/input/pickers.ts. Mutable dashboard state crosses
11652
+ // the seam as live get/set accessors (never a value copy); `draw` is
11653
+ // re-read at call time because it is reassigned after mount.
11654
+ const {
11655
+ openAgentRosterSearch,
11656
+ openTransferPicker,
11657
+ openFinderPicker,
11658
+ openDirectoryPicker,
11659
+ openSshPicker,
11660
+ } = createInputPickers({
11661
+ state: {
11662
+ get agentSearchModal() { return agentSearchModal; },
11663
+ set agentSearchModal(v: SearchModalHandle | null) { agentSearchModal = v; },
11664
+ get agentSearchHandle() { return agentSearchHandle; },
11665
+ set agentSearchHandle(v: { dispose: () => void } | null) { agentSearchHandle = v; },
11666
+ get agentRosterCursor() { return agentRosterCursor; },
11667
+ set agentRosterCursor(v: number) { agentRosterCursor = v; },
11668
+ get agentCursorLockedId() { return agentCursorLockedId; },
11669
+ set agentCursorLockedId(v: string) { agentCursorLockedId = v; },
11670
+ get agentCursorManual() { return agentCursorManual; },
11671
+ set agentCursorManual(v: boolean) { agentCursorManual = v; },
11672
+ get chatScrollOffset() { return chatScrollOffset; },
11673
+ set chatScrollOffset(v: number) { chatScrollOffset = v; },
11674
+ get chatLines() { return chatLines; },
11675
+ },
11676
+ display,
11677
+ agentSurfaceStore,
11678
+ workingDir,
11679
+ dockedPreview,
11680
+ transferFeedbackRuntime,
11681
+ finderFeedbackRuntime,
11682
+ currentThemeTokens,
11683
+ currentWorkspaceOwnerId,
11684
+ activeBrowserWidgetId,
11685
+ resolveBrowserStateById,
11686
+ fmtBytes,
11687
+ runTransfer,
11688
+ closeAgentRosterSearch,
11689
+ pushDebugLine,
11690
+ refreshRemotePreviewBridge,
11691
+ refreshWorkingDirPreview,
11692
+ setWorkingFocus,
11693
+ startRemoteModeBridge,
11694
+ draw: () => draw(),
11695
+ });
11621
11696
 
11622
11697
  function closeAgentRosterSearch(): void {
11623
11698
  if (agentSearchHandle) { agentSearchHandle.dispose(); agentSearchHandle = null; }
@@ -11625,75 +11700,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
11625
11700
  draw();
11626
11701
  }
11627
11702
 
11628
- /** T5-J5 — open the transfer picker. Files arg = local paths to
11629
- * ship; destination comes from the picker. Handles both ssh and
11630
- * iphone target kinds. Remote-mode browser isn't supported here
11631
- * yet — the user is transferring LOCAL files (focused entry +
11632
- * selected set) to a remote destination. Transferring REMOTE→X
11633
- * is a separate flow (not this phase). */
11634
- function openTransferPicker(
11635
- files: Array<{ localPath: string; size?: number }>,
11636
- browserId: string = activeBrowserWidgetId(),
11637
- ): void {
11638
- const targetBrowser = resolveBrowserStateById(browserId) ?? (workingDir as BrowserPaneModel);
11639
- if (files.length === 0) {
11640
- transferFeedbackRuntime.onNoFilesToTransfer();
11641
- return;
11642
- }
11643
- const capability = resolveBrowserTransferCapability(
11644
- targetBrowser,
11645
- listTransferTargets(),
11646
- );
11647
- if (!capability.enabled) {
11648
- transferFeedbackRuntime.onTransferDisabled(capability.reason);
11649
- return;
11650
- }
11651
- const targets = capability.targets;
11652
- if (targets.length === 0) {
11653
- transferFeedbackRuntime.onNoTransferTargets();
11654
- return;
11655
- }
11656
- const totalBytes = files.reduce((a, f) => a + (f.size ?? 0), 0);
11657
- const summary = `${files.length} file${files.length === 1 ? '' : 's'}${
11658
- totalBytes > 0 ? ` (${fmtBytes(totalBytes)})` : ''
11659
- }`;
11660
-
11661
- const { cols, rows } = termSize();
11662
- const width = Math.min(74, Math.max(46, cols - 6));
11663
- const visibleRows = Math.min(10, targets.length);
11664
- const height = visibleRows + 3;
11665
- const bounds = {
11666
- row: Math.max(1, Math.floor((rows - height) / 2)),
11667
- col: Math.max(1, Math.floor((cols - width) / 2)),
11668
- width,
11669
- height,
11670
- };
11671
- let handle: ReturnType<typeof display.pushModal> | null = null;
11672
- const picker = createTransferPickerModal({
11673
- targets,
11674
- bounds,
11675
- width,
11676
- maxVisible: visibleRows,
11677
- summary,
11678
- theme: currentThemeTokens(),
11679
- onAccept: (target) => {
11680
- agentSearchModal = null;
11681
- handle?.dispose();
11682
- void runTransfer(target, files);
11683
- draw();
11684
- },
11685
- onCancel: () => {
11686
- agentSearchModal = null;
11687
- handle?.dispose();
11688
- draw();
11689
- },
11690
- });
11691
- attachSurfaceToWorkspace(picker.surface, currentWorkspaceOwnerId());
11692
- handle = display.pushModal(picker.surface);
11693
- agentSearchModal = picker;
11694
- draw();
11695
- }
11696
-
11697
11703
  async function runTransfer(
11698
11704
  target: TransferTarget,
11699
11705
  files: Array<{ localPath: string; size?: number }>,
@@ -11720,465 +11726,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
11720
11726
  }
11721
11727
  }
11722
11728
 
11723
- /** T4-F2 / T4-F3 — open the Ctrl+T finder modal. Scans the
11724
- * current working dir (local only at MVP; remote scan is a
11725
- * follow-up). On accept, cd's the browser pane to the file's
11726
- * parent dir, focuses the file in the listing, and switches
11727
- * pane focus to browser so the user can e/enter next. */
11728
- async function openFinderPicker(): Promise<void> {
11729
- const targetBrowserId = activeBrowserWidgetId();
11730
- const targetBrowser = resolveBrowserStateById(targetBrowserId) ?? (workingDir as BrowserPaneModel);
11731
- // T5-I2 — branch on remote vs local. Both paths produce the
11732
- // same {absPath, relPath} finder items so the modal + onAccept
11733
- // handler stay unified.
11734
- let finderItems: FinderItem[] = [];
11735
- let backend = 'fd';
11736
- let truncated = false;
11737
- let scanDurationMs = 0;
11738
-
11739
- if (workingDir.remote) {
11740
- const host = workingDir.remote.host;
11741
- const remoteRoot = workingDir.remote.cwd;
11742
- finderFeedbackRuntime.onFinderScanStarted(`${host.name}:${remoteRoot}`);
11743
- draw();
11744
- const scan = await scanRemoteFinder({ host, root: remoteRoot });
11745
- const rels = relativizeRemoteResults(scan.paths, remoteRoot);
11746
- finderItems = scan.paths.map((absPath, i) => ({
11747
- absPath, relPath: rels[i] ?? absPath,
11748
- }));
11749
- backend = `${scan.backend}@${scan.host}`;
11750
- truncated = scan.truncated;
11751
- scanDurationMs = scan.durationMs;
11752
- } else {
11753
- // 2026-05-03 — scan from the Session Working Directory (the
11754
- // "project root"), NOT the browser pane's current navigation
11755
- // cursor. SWD is mutable only via Ctrl+W / /wd / SetWorkingDir
11756
- // tool — exactly the user's intent for "project root". Browser
11757
- // pane's cwd is purely UI navigation and should not narrow
11758
- // Ctrl+T's search scope (matches working-dir.ts:5-8 contract).
11759
- const scanRoot = getSessionCwd();
11760
- finderFeedbackRuntime.onFinderScanStarted(scanRoot);
11761
- draw();
11762
- const scan = await scanFinder({ root: scanRoot });
11763
- const rels = relativizeResults(scan.paths, scanRoot);
11764
- finderItems = scan.paths.map((absPath, i) => ({
11765
- absPath, relPath: rels[i] ?? absPath,
11766
- }));
11767
- backend = scan.backend;
11768
- truncated = scan.truncated;
11769
- scanDurationMs = scan.durationMs;
11770
- }
11771
- if (finderItems.length === 0) {
11772
- finderFeedbackRuntime.onFinderScanEmpty();
11773
- draw();
11774
- return;
11775
- }
11776
- finderFeedbackRuntime.onFinderScanCompleted({
11777
- count: finderItems.length,
11778
- backend,
11779
- truncated,
11780
- durationMs: scanDurationMs,
11781
- });
11782
-
11783
- const { cols, rows } = termSize();
11784
- const width = Math.min(90, Math.max(50, cols - 6));
11785
- const visibleRows = Math.min(15, Math.max(6, rows - 8));
11786
- const height = visibleRows + 3;
11787
- const bounds = {
11788
- row: Math.max(1, Math.floor((rows - height) / 2)),
11789
- col: Math.max(1, Math.floor((cols - width) / 2)),
11790
- width,
11791
- height,
11792
- };
11793
- let handle: ReturnType<typeof display.pushModal> | null = null;
11794
- // Title reflects the actual scan root — SWD for local (project
11795
- // root), remote cwd for remote mode. Browser pane's nav cursor
11796
- // is intentionally NOT shown because the scan ignores it.
11797
- const titleRoot = workingDir.remote
11798
- ? `${workingDir.remote.host.name}:${workingDir.remote.cwd}`
11799
- : getSessionCwd().replace(homedir(), '~');
11800
- // 2026-05-03 — preview modes (yazi/fzf-style, on-demand).
11801
- //
11802
- // Default mode is OFF — the picker opens cleanly with NO preview
11803
- // so it's never annoying on tablet / narrow terminals. The user
11804
- // toggles preview behaviour with `Ctrl+/` (matches zshrc
11805
- // `--bind 'ctrl-/:toggle-preview'`):
11806
- //
11807
- // off → hover → 2pane → off (cycle)
11808
- //
11809
- // • off — no preview, ever. The cursor moves freely.
11810
- // • hover — popup appears after the cursor stays still for
11811
- // 1 second (HOVER_DELAY_MS). Popup auto-dismisses
11812
- // after 4s. Centered, the same width as legacy
11813
- // behaviour but on-demand.
11814
- // • 2pane — browser-preview style: persistent preview popup
11815
- // docked to the right of the picker. Updates
11816
- // instantly as the cursor moves. Falls back to
11817
- // hover if the terminal is too narrow (<140 cols).
11818
- //
11819
- // showPreviewModal supports remote-skip by itself (preview
11820
- // handlers read local fs), so we simply guard with workingDir
11821
- // .remote before scheduling.
11822
- type PreviewMode = 'off' | 'hover' | '2pane';
11823
- const HOVER_DELAY_MS = 1000;
11824
- const TWO_PANE_MIN_COLS = 140;
11825
- const TWO_PANE_GROUP = 'finder-2pane-preview';
11826
-
11827
- let previewMode: PreviewMode = 'off';
11828
- let lastSelected: FinderItem | null = null;
11829
- let hoverTimer: ReturnType<typeof setTimeout> | null = null;
11830
- let lastPreviewHandle: { dispose(): void } | null = null;
11831
-
11832
- const clearHoverTimer = () => {
11833
- if (hoverTimer !== null) { clearTimeout(hoverTimer); hoverTimer = null; }
11834
- };
11835
- const disposeActivePreview = () => {
11836
- if (lastPreviewHandle) {
11837
- try { lastPreviewHandle.dispose(); } catch { /* ignore */ }
11838
- lastPreviewHandle = null;
11839
- }
11840
- };
11841
-
11842
- /** Compute the docked-preview bounds for 2-pane mode. Picker
11843
- * centred at width≈90; preview takes the right gutter. Returns
11844
- * null when there isn't enough room (caller falls back to
11845
- * hover). */
11846
- const computeDockedPreviewBounds = (): {
11847
- row: number; col: number; width: number; height: number;
11848
- } | null => {
11849
- const { cols: tc, rows: tr } = termSize();
11850
- if (tc < TWO_PANE_MIN_COLS) return null;
11851
- const pickerWidth = Math.min(90, Math.max(50, tc - 6));
11852
- const pickerCol = Math.max(1, Math.floor((tc - pickerWidth) / 2));
11853
- const previewCol = pickerCol + pickerWidth + 2;
11854
- const previewWidth = Math.max(40, tc - previewCol - 2);
11855
- if (previewWidth < 40) return null;
11856
- const previewHeight = Math.max(8, Math.min(tr - 6, 28));
11857
- const previewRow = Math.max(1, Math.floor((tr - previewHeight) / 2));
11858
- return { row: previewRow, col: previewCol, width: previewWidth, height: previewHeight };
11859
- };
11860
-
11861
- const paintPreviewFor = (item: FinderItem) => {
11862
- if (workingDir.remote) return; // remote previews unsupported
11863
- const { rows: tr, cols: tc } = termSize();
11864
- const docked = previewMode === '2pane' ? computeDockedPreviewBounds() : null;
11865
- void showPreviewModal(item.absPath, {
11866
- coordinator: display,
11867
- termCols: tc,
11868
- termRows: tr,
11869
- title: item.relPath,
11870
- ttlMs: previewMode === '2pane' ? 0 : 4000,
11871
- bounds: docked ?? undefined,
11872
- group: previewMode === '2pane' ? TWO_PANE_GROUP : 'image-preview',
11873
- }).then((r) => {
11874
- lastPreviewHandle = r?.handle ?? null;
11875
- }).catch(() => { /* preview failure is non-fatal */ });
11876
- };
11877
-
11878
- const scheduleHoverPreview = (item: FinderItem | null) => {
11879
- lastSelected = item;
11880
- clearHoverTimer();
11881
- if (previewMode === 'off') return;
11882
- if (!item || workingDir.remote) {
11883
- // Off-screen / remote — kill any straggling popup so the
11884
- // user doesn't see a preview that no longer matches the
11885
- // selection.
11886
- disposeActivePreview();
11887
- return;
11888
- }
11889
- if (previewMode === '2pane') {
11890
- // Instant update — no debounce. Persistent popup replaces
11891
- // itself via group dedup.
11892
- paintPreviewFor(item);
11893
- return;
11894
- }
11895
- // HOVER mode. Selection just changed → the popup currently on
11896
- // screen (if any) is stale. Drop it immediately so the user
11897
- // doesn't keep seeing the previous file's preview while their
11898
- // cursor is on a new file. The next paint waits HOVER_DELAY_MS
11899
- // of stillness before re-appearing.
11900
- disposeActivePreview();
11901
- hoverTimer = setTimeout(() => {
11902
- hoverTimer = null;
11903
- paintPreviewFor(item);
11904
- }, HOVER_DELAY_MS);
11905
- };
11906
-
11907
- /** Cycle off → hover → 2pane → off. Surfaces a status line so
11908
- * the user knows which mode they're in (Ctrl+/ has no chrome
11909
- * feedback otherwise). 2pane on a narrow terminal degrades
11910
- * to hover with a warning. */
11911
- const cyclePreviewMode = (): void => {
11912
- const next: PreviewMode = previewMode === 'off' ? 'hover'
11913
- : previewMode === 'hover' ? '2pane'
11914
- : 'off';
11915
- previewMode = next;
11916
- clearHoverTimer();
11917
- // Drop the active popup whenever the mode changes. The new
11918
- // mode (if any) re-paints below.
11919
- disposeActivePreview();
11920
-
11921
- if (next === 'off') {
11922
- chatLines.push(C.muted(' finder preview: OFF (Ctrl+/ to cycle modes)'));
11923
- } else if (next === 'hover') {
11924
- chatLines.push(C.muted(' finder preview: HOVER (popup after 1 s · Ctrl+/ next)'));
11925
- } else {
11926
- // 2pane — refuse if the terminal is too narrow; degrade to
11927
- // hover so the user gets *some* preview without overlap.
11928
- const docked = computeDockedPreviewBounds();
11929
- if (!docked) {
11930
- previewMode = 'hover';
11931
- chatLines.push(C.warning(` finder preview: 2-pane needs ≥${TWO_PANE_MIN_COLS} cols — falling back to HOVER`));
11932
- } else {
11933
- chatLines.push(C.muted(' finder preview: 2-PANE (docked right · Ctrl+/ to disable)'));
11934
- if (lastSelected) paintPreviewFor(lastSelected);
11935
- }
11936
- }
11937
- chatScrollOffset = -1;
11938
- draw();
11939
- };
11940
-
11941
- // Declared up here so `onAccept`/`onCancel` closures below can
11942
- // reference both; assigned right after the picker mounts.
11943
- let previewToggleDisposable: { dispose(): void } | null = null;
11944
- let previewDismissDisposable: { dispose(): void } | null = null;
11945
- const picker = createFinderModal({
11946
- items: finderItems,
11947
- bounds, width, maxVisible: visibleRows,
11948
- truncated,
11949
- title: `Find file in ${titleRoot}`,
11950
- theme: currentThemeTokens(),
11951
- onHover: scheduleHoverPreview,
11952
- onAccept: (item) => {
11953
- clearHoverTimer();
11954
- disposeActivePreview();
11955
- previewToggleDisposable?.dispose();
11956
- previewDismissDisposable?.dispose();
11957
- agentSearchModal = null;
11958
- handle?.dispose();
11959
- const parentDir = item.absPath.slice(0, item.absPath.lastIndexOf('/')) || '/';
11960
- const basename = item.absPath.slice(item.absPath.lastIndexOf('/') + 1);
11961
- if (workingDir.remote) {
11962
- // T5-I2 — remote cd + focus-by-basename.
11963
- enterRemoteDirectory(workingDir, parentDir);
11964
- (async () => {
11965
- await refreshRemoteWorkingDir(workingDir, {}, dockedPreview);
11966
- const idx = workingDir.entries.findIndex(e => e.name === basename);
11967
- if (idx >= 0) {
11968
- workingDir.cursor = idx;
11969
- workingDir.offset = Math.max(0, idx - 3);
11970
- }
11971
- await refreshRemotePreviewBridge();
11972
- pushDebugLine(C.muted(` ${workingDir.remote?.host.name}:${item.relPath}`));
11973
- chatScrollOffset = -1;
11974
- draw();
11975
- })().catch(() => {});
11976
- } else {
11977
- enterDirectory(targetBrowser as WorkingDirState, parentDir);
11978
- refreshWorkingDir(targetBrowser as WorkingDirState);
11979
- const idx = targetBrowser.entries.findIndex(e => e.name === basename);
11980
- if (idx >= 0) {
11981
- targetBrowser.cursor = idx;
11982
- targetBrowser.offset = Math.max(0, idx - 3);
11983
- }
11984
- refreshWorkingDirPreview();
11985
- pushDebugLine(C.muted(` ${item.relPath}`));
11986
- chatScrollOffset = -1;
11987
- }
11988
- setWorkingFocus('browser', 'agent-roster-search-pick');
11989
- draw();
11990
- },
11991
- onCancel: () => {
11992
- clearHoverTimer();
11993
- disposeActivePreview();
11994
- previewToggleDisposable?.dispose();
11995
- previewDismissDisposable?.dispose();
11996
- agentSearchModal = null;
11997
- handle?.dispose();
11998
- draw();
11999
- },
12000
- });
12001
- // Ctrl+/ (and Ctrl+_ which some terminals send for the same
12002
- // physical chord) cycles preview modes. Active for the lifetime
12003
- // of THIS picker only — disposed in both onAccept and onCancel
12004
- // above. Q4 (substrate Occam): no per-binding pipe alias, register
12005
- // each variant separately. (Korean IME aliases unnecessary — Ctrl+/
12006
- // has no jamo mapping on the 2-set layout.)
12007
- const togglePreviewMode = (): void => { cyclePreviewMode(); };
12008
- const previewSlash = display.registerKeyBinding({
12009
- id: `dashboard:finder-picker-preview-toggle-slash:${Date.now().toString(36)}`,
12010
- key: 'C-/',
12011
- scope: 'global',
12012
- when: () => agentSearchModal === picker,
12013
- handler: togglePreviewMode,
12014
- });
12015
- const previewUnder = display.registerKeyBinding({
12016
- id: `dashboard:finder-picker-preview-toggle-under:${Date.now().toString(36)}`,
12017
- key: 'C-_',
12018
- scope: 'global',
12019
- when: () => agentSearchModal === picker,
12020
- handler: togglePreviewMode,
12021
- });
12022
- previewToggleDisposable = {
12023
- dispose: () => { previewSlash.dispose(); previewUnder.dispose(); },
12024
- };
12025
- // Ctrl+] — instantly dismiss the active preview popup WITHOUT
12026
- // closing the picker or changing the mode. Esc would be more
12027
- // intuitive but the picker captures Esc internally to cancel
12028
- // itself. Ctrl+\ is unsafe because it raises SIGQUIT in many
12029
- // terminal drivers. Ctrl+] (historically "telnet escape" =
12030
- // "exit this context") is universally safe + semantically right.
12031
- // Active only while a preview is currently visible — otherwise
12032
- // the binding silently no-ops (no flicker, no log spam).
12033
- // Mode stays as set; if user is in HOVER mode, the next
12034
- // cursor stillness re-triggers the popup. To stop the popup
12035
- // from re-appearing entirely, cycle Ctrl+/ back to OFF.
12036
- previewDismissDisposable = display.registerKeyBinding({
12037
- id: `dashboard:finder-picker-preview-dismiss:${Date.now().toString(36)}`,
12038
- key: 'C-]',
12039
- scope: 'global',
12040
- when: () => agentSearchModal === picker && lastPreviewHandle !== null,
12041
- handler: () => {
12042
- clearHoverTimer();
12043
- disposeActivePreview();
12044
- draw();
12045
- },
12046
- });
12047
- attachSurfaceToWorkspace(picker.surface, currentWorkspaceOwnerId());
12048
- handle = display.pushModal(picker.surface);
12049
- agentSearchModal = picker;
12050
- draw();
12051
- }
12052
-
12053
- /** Yazi-style Alt+C — directory finder. Symmetric with openFinder
12054
- * Picker but enumerates directories only (`scanFinder kind:'dir'`)
12055
- * and on accept cd's the browser pane to the chosen directory.
12056
- * Remote-mode is currently unsupported (surfaces a warning) — the
12057
- * remote finder would need a parallel kind-aware backend. */
12058
- async function openDirectoryPicker(): Promise<void> {
12059
- if (workingDir.remote) {
12060
- chatLines.push(C.warning(' Alt+C: directory finder is local-only — Esc remote first.'));
12061
- chatScrollOffset = -1;
12062
- draw();
12063
- return;
12064
- }
12065
- const targetBrowserId = activeBrowserWidgetId();
12066
- const targetBrowser = resolveBrowserStateById(targetBrowserId) ?? (workingDir as BrowserPaneModel);
12067
- // Symmetric with openFinderPicker — scan from SWD (project root),
12068
- // not the browser pane's nav cursor. The accept handler still
12069
- // mutates targetBrowser to navigate into the chosen dir.
12070
- const scanRoot = getSessionCwd();
12071
- finderFeedbackRuntime.onFinderScanStarted(`${scanRoot} (dirs)`);
12072
- draw();
12073
- const scan = await scanFinder({ root: scanRoot, kind: 'dir' });
12074
- const rels = relativizeResults(scan.paths, scanRoot);
12075
- const dirItems: FinderItem[] = scan.paths.map((absPath, i) => ({
12076
- absPath, relPath: rels[i] ?? absPath,
12077
- }));
12078
- if (dirItems.length === 0) {
12079
- finderFeedbackRuntime.onFinderScanEmpty();
12080
- draw();
12081
- return;
12082
- }
12083
- finderFeedbackRuntime.onFinderScanCompleted({
12084
- count: dirItems.length,
12085
- backend: scan.backend,
12086
- truncated: scan.truncated,
12087
- durationMs: scan.durationMs,
12088
- });
12089
-
12090
- const { cols, rows } = termSize();
12091
- const width = Math.min(90, Math.max(50, cols - 6));
12092
- const visibleRows = Math.min(15, Math.max(6, rows - 8));
12093
- const height = visibleRows + 3;
12094
- const bounds = {
12095
- row: Math.max(1, Math.floor((rows - height) / 2)),
12096
- col: Math.max(1, Math.floor((cols - width) / 2)),
12097
- width,
12098
- height,
12099
- };
12100
- let handle: ReturnType<typeof display.pushModal> | null = null;
12101
- const titleRoot = scanRoot.replace(homedir(), '~');
12102
- const picker = createFinderModal({
12103
- items: dirItems,
12104
- bounds, width, maxVisible: visibleRows,
12105
- truncated: scan.truncated,
12106
- title: `Find directory in ${titleRoot}`,
12107
- theme: currentThemeTokens(),
12108
- onAccept: (item) => {
12109
- agentSearchModal = null;
12110
- handle?.dispose();
12111
- enterDirectory(targetBrowser as WorkingDirState, item.absPath);
12112
- refreshWorkingDir(targetBrowser as WorkingDirState);
12113
- refreshWorkingDirPreview();
12114
- pushDebugLine(C.muted(` cd ${item.relPath}`));
12115
- chatScrollOffset = -1;
12116
- setWorkingFocus('browser', 'alt-c-dir-pick');
12117
- draw();
12118
- },
12119
- onCancel: () => {
12120
- agentSearchModal = null;
12121
- handle?.dispose();
12122
- draw();
12123
- },
12124
- });
12125
- attachSurfaceToWorkspace(picker.surface, currentWorkspaceOwnerId());
12126
- handle = display.pushModal(picker.surface);
12127
- agentSearchModal = picker;
12128
- draw();
12129
- }
12130
-
12131
- /** T4-E4 — open the SSH host picker (Ctrl+K). Accept connects
12132
- * the browser pane to the remote host; Esc cancels silently.
12133
- * Routes keys through agentSearchModal (shared pattern with
12134
- * window + session pickers). */
12135
- function openSshPicker(): void {
12136
- const { cols, rows } = termSize();
12137
- const width = Math.min(72, Math.max(44, cols - 6));
12138
- const visibleRows = 8;
12139
- const height = visibleRows + 3;
12140
- const bounds = {
12141
- row: Math.max(1, Math.floor((rows - height) / 2)),
12142
- col: Math.max(1, Math.floor((cols - width) / 2)),
12143
- width,
12144
- height,
12145
- };
12146
- let handle: ReturnType<typeof display.pushModal> | null = null;
12147
- const picker = createSshPickerModal({
12148
- bounds, width, maxVisible: visibleRows,
12149
- activeHostName: workingDir.remote?.host.name ?? null,
12150
- theme: currentThemeTokens(),
12151
- onAccept: async (host) => {
12152
- agentSearchModal = null;
12153
- handle?.dispose();
12154
- touchSshHost(host.name);
12155
- startRemoteModeBridge(host, '.');
12156
- finderFeedbackRuntime.onSshConnectStarted({ name: host.name, host: host.host });
12157
- draw();
12158
- try {
12159
- await refreshRemoteWorkingDir(workingDir, {}, dockedPreview);
12160
- await refreshRemotePreviewBridge();
12161
- finderFeedbackRuntime.onSshConnectSucceeded(host.name);
12162
- } catch (err) {
12163
- finderFeedbackRuntime.onSshConnectFailed(
12164
- host.name,
12165
- err instanceof Error ? err.message : String(err),
12166
- );
12167
- }
12168
- draw();
12169
- },
12170
- onCancel: () => {
12171
- agentSearchModal = null;
12172
- handle?.dispose();
12173
- draw();
12174
- },
12175
- });
12176
- attachSurfaceToWorkspace(picker.surface, currentWorkspaceOwnerId());
12177
- handle = display.pushModal(picker.surface);
12178
- agentSearchModal = picker;
12179
- draw();
12180
- }
12181
-
12182
11729
  // FU-1 — register ssh-picker / finder-picker openers as display
12183
11730
  // key bindings. Moves the dashboard hard branches onto the same
12184
11731
  // coordinator dispatch path the rest of the modals / bindings use.
@@ -12889,6 +12436,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12889
12436
  // 외부 주입(`pty text`)을 사용자 타이핑과 섞지 않고 별도 대기열로 — 결함 #6(10-14).
12890
12437
  const disposeExternalInputHost = registerExternalInputHost(createDashboardExternalInputHost({
12891
12438
  isTurnActive: () => streamingInFlight,
12439
+ getTurnStop: () => currentTurnExternalStop,
12892
12440
  promptCtl,
12893
12441
  pushNotice: (line) => { chatLines.push(C.muted(` ${line}`)); chatScrollOffset = -1; draw(); },
12894
12442
  }));
@@ -15422,8 +14970,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15422
14970
  // — the boot fell back to a fresh session. Surface the reason
15423
14971
  // so the user knows they're on a different session than asked.
15424
14972
  // - new (no resume requested): nothing extra to render.
14973
+ // 대표 #73 — 인자 없는 기동 = 새 세션. 이어받았는지는 이 값 하나로 판정한다(관측 ⊕ 자리 부트 카드).
14974
+ let tuiBootSessionStart: { origin: 'new' | 'resume'; reason: string; restoredSessionId?: string } = opts.resumeSessionId
14975
+ ? { origin: 'new', reason: 'resume-failed' }
14976
+ : { origin: 'new', reason: 'default-new' };
15425
14977
  if (dashboardAcpBootResult.mode === 'resumed' && dashboardAcpBootResult.sessionId) {
15426
14978
  const sid = dashboardAcpBootResult.sessionId;
14979
+ tuiBootSessionStart = { origin: 'resume', reason: 'resume-daemon', restoredSessionId: sid };
15427
14980
  chatLines.push(C.success(` ✓ resumed daemon session: ${sid}`));
15428
14981
  // Best-effort REST history replay over the remote HTTP/WS endpoint.
15429
14982
  if (opts.remote) {
@@ -15485,10 +15038,27 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15485
15038
  if (!resume.resumed) {
15486
15039
  chatLines.push(C.warning(` ⚠ resume failed (${resume.reason}); started new session`));
15487
15040
  } else {
15041
+ tuiBootSessionStart = {
15042
+ origin: 'resume',
15043
+ reason: resume.repaired ? 'resume-local-repaired' : 'resume-local',
15044
+ restoredSessionId: resume.sessionId,
15045
+ };
15488
15046
  chatLines.push(C.success(` ✓ resumed local session: ${resume.sessionId}`));
15047
+ if (resume.repaired) {
15048
+ chatLines.push(C.muted(` 깨진 도구 호출·결과 짝을 잘라 이어받았다(호출 ${resume.repaired.removedCalls} · 결과 ${resume.repaired.removedResults})`));
15049
+ }
15489
15050
  chatLines.push(C.muted(` ${resume.turns} turns restored. Next turn continues this conversation.`));
15490
15051
  }
15491
15052
  }
15053
+ observeSessionStart({
15054
+ surface: 'tui',
15055
+ origin: tuiBootSessionStart.origin,
15056
+ reason: tuiBootSessionStart.reason,
15057
+ // sessionId = 이 대화가 이어받은(또는 새로 연) 대화 기록의 id · acpSessionId = 백엔드 ACP 세션 id(둘이 다를 수 있다).
15058
+ sessionId: tuiBootSessionStart.restoredSessionId ?? dashboardAcpSession.currentSessionId ?? null,
15059
+ acpSessionId: dashboardAcpSession.currentSessionId ?? null,
15060
+ resumeRequested: opts.resumeSessionId !== undefined,
15061
+ });
15492
15062
  // 제안은 세션 작업 폴더 기준 — 시작 폴더 한 번이 아니라 폴더가 바뀔 때마다 다시 계산한다(IA1 리뷰 must-fix).
15493
15063
  const nextProjectSuggestion = createProjectSuggester(suggestProjectForFolder);
15494
15064
  const pushProjectSuggestion = (folder: string): void => {
@@ -15529,7 +15099,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15529
15099
  installSeatSessionRecorder({ getCwd: () => getSessionCwd(), getHistory: () => chat.history });
15530
15100
  const seatBoot = describeSeatBoot(getSessionCwd());
15531
15101
  if (seatBoot) chatLines.push(C.muted(` 🪪 ${seatBoot.seat} 자리 · 안 읽은 우편 ${seatBoot.unread ?? '미관측'} — 부트 카드를 턴마다 싣는다`));
15532
- if (dashboardAcpBootResult.mode !== 'resumed' && !opts.resumeSessionId) startSeatBootConversation({ reason: 'boot' });
15102
+ // 새 세션(이어받기 실패 포함)이면 자리 부트 카드로 시작한다 — 자리 맥락은 세션이 아니라 자리 문서가 싣는다.
15103
+ if (tuiBootSessionStart.origin === 'new') startSeatBootConversation({ reason: 'boot' });
15533
15104
  tuiSeatAskTimer = setInterval(pollTuiSeatAnswers, 10_000);
15534
15105
  tuiSeatAskTimer.unref();
15535
15106
  pollTuiSeatAnswers();
@@ -16587,7 +16158,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
16587
16158
  // 다음 inner-loop 반복이 남은 항목도 같은 순서로 제출한다.
16588
16159
  // ⭐ 드레인 순서 자체는 `chat/turn-typeahead.ts` 가 소유한다 — 여기서 베끼면
16589
16160
  // 테스트가 그 순서를 다시 베껴야 하고, 베낀 테스트는 배선 회귀를 못 잡는다.
16590
- const interrupted = lastTurnFinalStatus === 'interrupted';
16161
+ internalTurnGate.noteTurnSettled();
16162
+ // «지금 밀어 넣기»로 끊은 턴은 «중단»이 아니다 — 복원하지 않고 큐 머리를 곧바로 제출한다.
16163
+ const interrupted = consumeTurnEndInterrupted(lastTurnFinalStatus, steerInterruptRef);
16591
16164
  const queuedBeforeDrain = turnTypeaheadRef.state.queuedSubmissions.length;
16592
16165
  // ⭐ 드레인 → 레지스트리 해제 → Enter 주입은 다리 모듈이 한 함수로 한다(시험이 같은 함수를 문다).
16593
16166
  const drained = handoffTurnTypeaheadAtTurnEnd(turnTypeaheadRef, nextInitial, { interrupted }, () => {
@@ -16632,6 +16205,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
16632
16205
  : '/quit /clear (run `elanous setup` — no LLM provider available)',
16633
16206
  debugLog: debug.enabled ? (event, label, payload) => debug.log(event, label, payload) : undefined,
16634
16207
  textInputOpts: {
16208
+ internalTurnYield: internalTurnYieldPort,
16635
16209
  onScrollKey: (name) => {
16636
16210
  const { rows } = termSize();
16637
16211
  const viewport = computePromptFrameLogViewportBounds(
@@ -17073,6 +16647,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17073
16647
  }),
17074
16648
  },
17075
16649
  });
16650
+ // ⭐ H2 — 입력칸이 내부 턴 문에 «비켜 줬다». 꺼낸 요청을 같은 턴 실행 경로로 보낸다
16651
+ // (슬래시 해석·히스토리·사람 발화 에코 없음). 꺼낼 게 없으면 입력으로 되돌아간다.
16652
+ const consumed = consumeInternalTurnYield(internalTurnGate, input);
16653
+ if (consumed.kind === 'reenter') continue;
16654
+ const internalTurn: InternalTurnRequest | null = consumed.kind === 'internal' ? consumed.request : null;
16655
+ input = consumed.input;
17076
16656
 
17077
16657
  const postTurn = resolveDashboardChatMainPostTurn(input, {
17078
16658
  inputLines: dashboardState.getInputLines(),
@@ -17124,7 +16704,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17124
16704
 
17125
16705
  // Save to durable input history. Up/down reads the in-memory
17126
16706
  // tail, while slash/LLM tools can search the full store.
17127
- if (postTurn.historyRecord) {
16707
+ if (!internalTurn) internalTurnGate.noteHumanTurn();
16708
+ if (postTurn.historyRecord && !internalTurn) {
17128
16709
  inputHistoryStore.record(postTurn.historyRecord);
17129
16710
  refreshInputHistory();
17130
16711
  }
@@ -17282,14 +16863,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17282
16863
  // resource_link blocks so dropped screenshots and PDFs flow
17283
16864
  // naturally to the agent. debug.log gates the snapshot so
17284
16865
  // off-state stays zero-cost.
17285
- const submitIntent = resolveDashboardHarnessSubmitIntent(input.text, {
17286
- stickyBackend: dashboardAcpChat.getSticky(),
17287
- source: input.externalSubmitSource,
17288
- });
16866
+ const submitIntent = internalTurn
16867
+ ? resolveInternalTurnSubmitIntent(internalTurn, dashboardAcpSession.currentSessionId)
16868
+ : resolveDashboardHarnessSubmitIntent(input.text, {
16869
+ stickyBackend: dashboardAcpChat.getSticky(),
16870
+ source: input.externalSubmitSource,
16871
+ });
17289
16872
  const cmdText = submitIntent.kind === 'control-turn'
17290
16873
  ? (submitIntent.commandText ?? '')
17291
16874
  : submitIntent.text;
17292
- if (submitIntent.kind === 'submit-turn' && parseSeatAsk(submitIntent.text)) {
16875
+ if (submitIntent.kind === 'submit-turn' && !internalTurn && parseSeatAsk(submitIntent.text)) {
17293
16876
  try {
17294
16877
  const receipt = await askSeat(submitIntent.text, { channel: 'tui', clientId: tuiSeatAskClientId },
17295
16878
  ceoTaskDeps(getUserConfig(), null), tuiSeatAskDeps);
@@ -17959,6 +17542,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17959
17542
  ? { url: opts.remote.url, token: opts.remote.token }
17960
17543
  : null,
17961
17544
  acpSwapTo: (sessionId, cwd) => dashboardAcpSession.swapTo(sessionId, cwd),
17545
+ acpRenew: async (cwd) => {
17546
+ const renewed = await dashboardAcpSession.renew(cwd);
17547
+ acpSessionIdRef.current = renewed;
17548
+ return renewed;
17549
+ },
17962
17550
  },
17963
17551
  shellSlash: {
17964
17552
  slashRuntime: shellSlashRuntime,
@@ -18021,6 +17609,21 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18021
17609
  setWorkingFocusPreview: () => { setWorkingFocus('preview', 'slash-term-move-preview'); },
18022
17610
  nextTerminalVwSlotId: (terminalId) => `term-slot:${terminalId}`,
18023
17611
  },
17612
+ // TUI-LOOP-COMMAND — /loop <간격> <프롬프트> · 쉬는 동안만 발사
17613
+ // (src/dashboard/loop-scheduler.ts · 판정: idle 발사 1 · busy 미룸 · stop 0).
17614
+ // idle/busy 신호는 대시보드 루프가 이미 쓰는 것을 그대로 쓴다 —
17615
+ // 스트리밍 턴(streamingInFlight · reportTuiTurn busy/idle) +
17616
+ // ACP 세션 바쁨(dashboardAcpChat.isBusy()). 프롬프트 제출은
17617
+ // 플러그인 submitDashboardText 와 같은 기존 제출 틈
17618
+ // (dispatchDashboardSubmitText)으로 낸다 — 새 제출 경로를 만들지 않는다.
17619
+ loopCommand: {
17620
+ isIdle: () => !streamingInFlight && !dashboardAcpChat.isBusy(),
17621
+ submitPrompt: (prompt: string) => {
17622
+ if (dispatchDashboardSubmitText) dispatchDashboardSubmitText(prompt);
17623
+ else chatLines.push(C.muted(' /loop: 입력 루프가 아직 제출을 받지 않는다 — 다음 틱을 기다린다'));
17624
+ },
17625
+ schedulerRef: dashboardLoopSchedulerRef,
17626
+ },
18024
17627
  };
18025
17628
  const slashOutcome = await dashboardSlashRegistry.dispatch(cmdLower, args, slashCtx);
18026
17629
  if (debug.enabled) {
@@ -18468,7 +18071,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18468
18071
  // pattern. Without this draw(), the echo sits in the chatLines
18469
18072
  // array but doesn't paint until after the skill router's
18470
18073
  // classifier LLM call returns (~1-2s of visual silence).
18471
- echoSubmittedUserText(tok.text);
18074
+ if (internalTurn) {
18075
+ chatLines.push(C.muted(` ${formatInternalTurnBanner(internalTurn)}`));
18076
+ chatScrollOffset = -1;
18077
+ } else {
18078
+ echoSubmittedUserText(tok.text);
18079
+ }
18472
18080
  if (queuedTurnEndText !== undefined && input.text === queuedTurnEndText) {
18473
18081
  debug.log('dashboard.turn-typeahead', 'echoed', { path: 'turn-end', length: queuedTurnEndText.length });
18474
18082
  }
@@ -18686,7 +18294,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18686
18294
  let latestTurnUsage: import('../prompt-cache/types.js').LLMUsage | undefined;
18687
18295
  const runPlainSubmitTurn = (submit: PlainTurnSubmit) => runDashboardChatMainPlainTurn({
18688
18296
  // ⛔⭐ 관측 심을 «실제로» 넘긴다 — 타입만 선택적으로 넓히면 조용히 안 온다(`F12`).
18689
- debugLog: (category, event, payload) => { try { debug.log(category, event, payload); } catch { /* fail-soft */ } },
18297
+ debugLog: (category, event, payload, opts) => { try { debug.log(category, event, payload, opts); } catch { /* fail-soft */ } },
18690
18298
  userText: tok.text,
18691
18299
  intent: submit.intent,
18692
18300
  contextText: ctx,
@@ -19046,7 +18654,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19046
18654
  const { resumeGoalFromUserFollowUp } = await import('../goals/chat-loop-bridge.js');
19047
18655
  const { hydrateGoalFromSnapshot } = await import('../goals/registry.js');
19048
18656
  hydrateGoalFromSnapshot();
19049
- const followUpAction = tok.text.startsWith('/goal')
18657
+ // ⛔ H2 — 내부 턴은 사람의 «정정·재개» 발화가 아니다: 멈춘 /goal 을 되살리지 않는다.
18658
+ const followUpAction = tok.text.startsWith('/goal') || internalTurn
19050
18659
  ? { kind: 'no-loop' as const }
19051
18660
  : resumeGoalFromUserFollowUp(tok.text);
19052
18661
  let goalLoopUserText: string | null = followUpAction.kind === 'continue'