elanous 0.2.29 → 0.2.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{cIpv5nU9AycwYWZI9H589 → IVKr3AlRUVLKsw2fh7eXF}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{058a98ba.7eb28d5e6dc03565.js → 0bb0ff36.b8d418980951e5ba.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +287 -0
- package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +62 -0
- package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6013-2f769370d8b4c2c6.js → 1335-6731f392c35216eb.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8332.6f0f49df772e163b.js → 1654.32de4b324981a188.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{8795.32573a185af18c7b.js → 1913.7cc6fb6055b1ed3f.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{419.736c0d6f426249a7.js → 2001.6c7e0fd02190886c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6767.1fcfc9b8be83975f.js → 2213.2ccbada92c33ea0f.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4759.1f76713aefb87db5.js → 2871.0bca6137343b3f76.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6776.06ff1e3b464cc607.js → 2898.fd781bc7cf01a7b7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1782-59db1b5aa06df38a.js → 2940-619bfd8ef337c611.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8471.8d2a8c74f09c36ff.js → 2985.39ccdecd36848b12.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +115 -0
- package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +64 -0
- package/apps/pwa/out/_next/static/chunks/{bc324b7c.02a09dd93f6a399b.js → 3267c0e6.d34854e7b3984a6e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3278.2e5d15b53a4091c8.js +2 -0
- package/apps/pwa/out/_next/static/chunks/{7725-4f83d5541fbdb81b.js → 3541-ce3a5424ed5ad7f1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3902.e1e71aaac850a045.js +34 -0
- package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{46bd8bea.2f4b4099172fcdb7.js → 409f1e57.d0649fc47691ebfe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4111.2bfedbb233ad206b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1142.65d002789ab3af7d.js → 4174.92ed04922709ac9e.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +36 -0
- package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +286 -0
- package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +77 -0
- package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +226 -0
- package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +79 -0
- package/apps/pwa/out/_next/static/chunks/5173-233188df2f42c626.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +148 -0
- package/apps/pwa/out/_next/static/chunks/5307.60f2209e809bd627.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3396-97b97928c49190c9.js → 5458-16cd7a2b51b74c8c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3403.7199205e65124486.js → 5525.86debf231e181813.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9143.ff14585db1d275ff.js → 5543.a438b8ab5a13ea47.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +74 -0
- package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +94 -0
- package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8398.73a19b39715eda04.js → 6092.a7f99e68b188ca66.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/60fb96c9.360b01d8d4927b7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5591.a35e7a217734a8b9.js → 6157.0c9af021de4fb31b.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7375.050ea0d72968e49f.js → 6433.fc573af22bbc9ab7.js} +17 -17
- package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +90 -0
- package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +32 -0
- package/apps/pwa/out/_next/static/chunks/6544cb48.a87fe20ddf137be2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +104 -0
- package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +24 -0
- package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +93 -0
- package/apps/pwa/out/_next/static/chunks/69-0a2c8209c6d4a158.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7371.80c05c653b08e907.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7391.ebae168025c91b6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1852.7f7b53991fcb99ef.js → 7502.ccd5b8065545edd0.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +8 -0
- package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6534.1ab00f370db7b105.js → 7862.30a8d6482bb238eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7947-d0759430f8042240.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{285adb1a.5654e605dddf86d2.js → 7a1887c4.f36b88fba31da8fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8018.a32663f04a6fc5f9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +1 -0
- package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +24 -0
- package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1516.dfaceff3fd2d0e0d.js → 9138.b6e80031afb1d973.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9907.49be0fcadaf214b5.js → 9221.7d22eaf353a2d4e7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8767-2f6a634f5abeaa01.js → 9279-d28c5b7754d010ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3160-95c96f4d2a21dd26.js → 9398-6aed28f0a96d0d6f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{328bb3ab-6628a1114fe3fb3b.js → 997ce8a5-2ad501927003b41b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6853.cc5174a9dee59593.js → 9989.3694300e5f000b14.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-a27b1e5a0d1b308f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-32866485913d6a04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-8c66a3b24a745630.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-29900ed0011b2c83.js → page-c219f2ed171131be.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-04d00ae9b0890349.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{425cfc4d.fb91158e3971c39f.js → b313b242.cb1265eea908db61.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +122 -0
- package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +1 -0
- package/apps/pwa/out/_next/static/css/b5d88eba78a3d7bf.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +26 -23
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/bin/elanous.mjs +7 -3
- package/graphs/context/nightly-condense.yaml +1 -0
- package/graphs/implement-loop.yaml +5 -0
- package/graphs/intake/intake-daily.yaml +8 -1
- package/graphs/intake/recipes.yaml +5 -0
- package/graphs/lecture/lecture-note.yaml +2 -2
- package/graphs/ops/harness-gc.yaml +1 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +9 -5
- package/graphs/units/recipes.yaml +3 -0
- package/graphs/units/release-gate-recheck.yaml +15 -0
- package/package.json +2 -1
- package/scripts/check-native-tool-wiring.ts +3 -2
- package/scripts/ci-android-unit-tests.ts +16 -0
- package/scripts/ci-typecheck-changed.ts +63 -10
- package/scripts/coord-channel-watch.sh +22 -16
- package/scripts/coord-role-pack.ts +81 -2
- package/scripts/cron-run.ts +96 -3
- package/scripts/detect-test-interference.ts +38 -5
- package/scripts/gate-pr-cache.ts +65 -0
- package/scripts/gh-surface/capture.ts +126 -0
- package/scripts/gh-surface/gh +40 -0
- package/scripts/import-graph-scc.ts +38 -0
- package/scripts/install.ps1 +19 -0
- package/scripts/intake-social.ts +181 -0
- package/scripts/lecture-note/node.ts +71 -4
- package/scripts/lib/android-toolchain.ts +32 -0
- package/scripts/lib/deterministic-env.ts +62 -0
- package/scripts/lib/gate-test-env.ts +19 -0
- package/scripts/measure-goal-grounding-corpus.ts +2 -1
- package/scripts/measure-si-task-cpu.ts +143 -0
- package/scripts/ratchets/test-interference-patterns.allowlist.txt +255 -0
- package/scripts/ratchets/test-interference-patterns.ts +367 -0
- package/scripts/release-loop/cut-branch.ts +4 -3
- package/scripts/release-loop/export-check-node.ts +0 -1
- package/scripts/release-loop/fixtures/warm-failure-assertion/sample.junit.xml +8 -0
- package/scripts/release-loop/fixtures/warm-job-029/sample.json +1 -0
- package/scripts/release-loop/fixtures/warm-job-029/sample.junit.xml +10 -0
- package/scripts/release-loop/gate-closure.ts +12 -8
- package/scripts/release-loop/gate-diff.ts +1 -1
- package/scripts/release-loop/gate-host-mirror.ts +66 -0
- package/scripts/release-loop/gate-node.ts +523 -75
- package/scripts/release-loop/gate-partial.ts +106 -9
- package/scripts/release-loop/gate-pr-cache.ts +66 -0
- package/scripts/release-loop/gate-prewarm-plan.ts +66 -0
- package/scripts/release-loop/gate-r2-verdict.ts +189 -0
- package/scripts/release-loop/gate-recheck-plan-node.ts +52 -0
- package/scripts/release-loop/gate-warm-artifacts.ts +253 -0
- package/scripts/release-loop/import-graph-scc-run.ts +54 -0
- package/scripts/release-loop/import-graph-scc.ts +215 -0
- package/scripts/release-loop/node-verdict.ts +2 -0
- package/scripts/release-loop/prepare-node.ts +102 -4
- package/scripts/release-loop/probe-pod-host-dirs-live.ts +113 -0
- package/scripts/release-loop/resume-release.ts +33 -4
- package/scripts/release-loop/resume.ts +13 -5
- package/scripts/release-loop/spawn-lane.ts +2 -19
- package/scripts/release-loop/tui-sim-node.ts +3 -2
- package/scripts/release-loop/unattended-release.ts +16 -0
- package/scripts/test-deterministic.ts +5 -61
- package/scripts/train-reach-grouping.ts +48 -0
- package/scripts/tsc-incremental-cache.ts +86 -0
- package/scripts/tsc-incremental-diagnostics.ts +76 -0
- package/scripts/tsc-main-safety-net.ts +2 -2
- package/src/acp/client.ts +4 -1
- package/src/acp/core-turn-bridge.ts +65 -8
- package/src/acp/elanous-extensions.ts +3 -0
- package/src/acp/server.ts +3 -0
- package/src/acp/tool-trace.ts +186 -11
- package/src/ad-pipeline/run.ts +4 -2
- package/src/agent/runner.ts +169 -3
- package/src/agent/self-ambient-cache.ts +156 -0
- package/src/agent/self-ambient.ts +8 -1
- package/src/agent/subagent-callable.ts +3 -2
- package/src/agent/task-notification.ts +5 -0
- package/src/agent-mission/review-loop.ts +6 -3
- package/src/agent-substrate/acp-implementer.ts +14 -2
- package/src/agent-substrate/pr-reviewer.ts +14 -0
- package/src/agent-substrate/self-review-cli.ts +30 -4
- package/src/ask-user-question/tool.ts +22 -0
- package/src/autopilot/build/llm-conflict-merge.ts +56 -10
- package/src/autopilot/pr-manager.ts +80 -7
- package/src/briefing/brief-items.ts +24 -22
- package/src/capture/frame-state-detect.ts +39 -0
- package/src/capture/tui-self-report.ts +149 -6
- package/src/chat/chat-cli.ts +10 -2
- package/src/chat/index.ts +138 -26
- package/src/chat/input-control-key.ts +5 -3
- package/src/chat/pickers/modal-runtime.ts +6 -0
- package/src/chat/pickers/modals.ts +8 -2
- package/src/chat/tool-interrupt-behavior.ts +38 -0
- package/src/chat/tool-render/dispatch.ts +5 -1
- package/src/chat/turn-typeahead.ts +75 -20
- package/src/cli/__fixtures__/agent-help-before/agent.txt +4 -2
- package/src/cli/agent-cli.ts +41 -13
- package/src/cli/ask-bare.ts +1 -0
- package/src/cli/config-audit.ts +123 -0
- package/src/cli/decisions-cli.ts +80 -6
- package/src/cli/import-cli.ts +12 -0
- package/src/cli/log-event-names.ts +17 -0
- package/src/cli/mcp-login.ts +21 -5
- package/src/cli/msg-cli.ts +89 -0
- package/src/cli/pod-cli.ts +14 -3
- package/src/cli/pr-cli.ts +273 -81
- package/src/cli/pr-land-base-read.ts +34 -0
- package/src/cli/pr-land-heavy-remote.ts +82 -26
- package/src/cli/pr-land-overlap-evidence.ts +109 -11
- package/src/cli/pr-land-pod-shadow-job.ts +106 -0
- package/src/cli/pr-land-pod-shadow.ts +16 -3
- package/src/cli/pr-land-refusal.ts +20 -5
- package/src/cli/pr-land-virtual-rebase.ts +108 -0
- package/src/cli/pty-takeover-cli.ts +88 -14
- package/src/cli/release-cli.ts +80 -14
- package/src/cli/seat-cli.ts +138 -3
- package/src/cli/self-update.ts +5 -2
- package/src/cli/sim-cli.ts +18 -0
- package/src/cli/tasks-cli.ts +97 -19
- package/src/context-bus/context-first.ts +13 -0
- package/src/context-bus/context-now.ts +13 -2
- package/src/context-bus/ops-events.ts +79 -0
- package/src/control-plane/lease-holder.ts +12 -8
- package/src/control-plane/ledger.ts +163 -4
- package/src/control-plane/server.ts +91 -11
- package/src/core-turn/run-core-turn.ts +2 -0
- package/src/core-turn/run-goal-loop.ts +243 -3
- package/src/core-turn/seat-turn-cap.ts +79 -0
- package/src/core-turn/short-question.ts +38 -6
- package/src/core-turn/types.ts +6 -0
- package/src/dashboard/acp-active-model.ts +42 -1
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/background-jobs-status.ts +66 -0
- package/src/dashboard/index.ts +339 -657
- package/src/dashboard/input/agent-wake.ts +242 -0
- package/src/dashboard/input/chat-main-entry-runtime.ts +6 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +4 -1
- package/src/dashboard/input/chat-main-session.ts +8 -1
- package/src/dashboard/input/chat-main-turn.ts +12 -3
- package/src/dashboard/input/compact-prepass-recovery.ts +33 -0
- package/src/dashboard/input/external-input-host.ts +49 -9
- package/src/dashboard/input/internal-turn.ts +261 -0
- package/src/dashboard/input/mouse-wiring.ts +108 -11
- package/src/dashboard/input/seat-autowake.ts +318 -0
- package/src/dashboard/input/turn-steer-bridge.ts +70 -0
- package/src/dashboard/log-click-runtime.ts +118 -2
- package/src/dashboard/loop-scheduler.ts +72 -0
- package/src/dashboard/seat-boot-preamble.ts +18 -0
- package/src/dashboard/skill-runtime.ts +2 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +126 -1181
- package/src/dashboard/slash-runtime/help-from-registry.ts +2 -1
- package/src/dashboard/slash-runtime/index.ts +13 -0
- package/src/dashboard/turn-busy-teardown.ts +50 -0
- package/src/dashboard/turn-preamble.ts +116 -40
- package/src/dashboard/turn-session-layers.ts +4 -2
- package/src/dashboard/turn-start-segments.ts +222 -0
- package/src/decisions/decision-ledger.ts +45 -4
- package/src/discord-self-message.ts +4 -4
- package/src/display/prompt-row-owner.ts +146 -0
- package/src/display/resize-listener.ts +33 -2
- package/src/domains/release-tool.ts +18 -1
- package/src/field-note/transcribe.ts +17 -0
- package/src/fold-stack.ts +4 -2
- package/src/git-fs/index.ts +263 -36
- package/src/github/remote-call.ts +81 -0
- package/src/goals/registry.ts +46 -6
- package/src/goals/types.ts +7 -1
- package/src/graph-runner/runner.ts +1 -0
- package/src/grok/credential.ts +26 -0
- package/src/harness/gate-reserve.ts +87 -0
- package/src/harness/harness-clean.ts +2 -1
- package/src/harness/harness-cli-command.ts +52 -5
- package/src/harness/harness-gc.ts +2 -1
- package/src/harness/harness-incidents.ts +70 -20
- package/src/harness/harness-pod-dispatch.ts +87 -10
- package/src/harness/harness-queue.ts +68 -4
- package/src/harness/harness-stop.ts +4 -3
- package/src/harness/harness-substrate-default.ts +78 -7
- package/src/harness/harness-worktree-auto.ts +2 -1
- package/src/harness/harness-worktrees.ts +3 -2
- package/src/hooks/dispatch.ts +2 -1
- package/src/index.ts +156 -27
- package/src/input/external-input-queue.ts +64 -7
- package/src/intake-plane/slash.ts +14 -3
- package/src/keybindings.ts +4 -1
- package/src/llm/route-decision.ts +36 -0
- package/src/llm/tool-loop-steered-rejection.ts +41 -0
- package/src/llm-request-shape.ts +209 -0
- package/src/llm.ts +301 -283
- package/src/loops/consensus.ts +348 -0
- package/src/loops/msg-wake.ts +206 -4
- package/src/loops/observe.ts +19 -3
- package/src/loops/orchestrator/tick.ts +88 -12
- package/src/loops/orchestrator/traffic.ts +64 -1
- package/src/loops/registry.ts +30 -0
- package/src/market/signed-index.ts +14 -0
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/mcp/mcp-oauth.ts +54 -5
- package/src/models/prompts.ts +28 -0
- package/src/msg/msg-sse-client.ts +223 -0
- package/src/msg/msg-store.ts +86 -2
- package/src/mss/logging/log-store.ts +225 -1
- package/src/mss/logging/render-categories.ts +5 -0
- package/src/nexus/DAEMON-THIN-inventory-2026-10-15.md +122 -0
- package/src/nexus/api/http-server.ts +54 -15
- package/src/nexus/api/loop-resources.ts +19 -3
- package/src/nexus/api/ops-api.ts +11 -0
- package/src/nexus/daemon-thin-inventory.md +37 -0
- package/src/nexus/index.ts +70 -0
- package/src/nexus/state/state.ts +1 -0
- package/src/oauth/store.ts +32 -7
- package/src/onboarding/first-run.ts +28 -3
- package/src/ops/overview-cache.ts +16 -4
- package/src/ops/overview.ts +59 -2
- package/src/ops/resources.ts +82 -0
- package/src/plugins/maker/connector-maker.ts +41 -0
- package/src/plugins/maker/skill-maker.ts +42 -0
- package/src/pr-tool/census.ts +303 -0
- package/src/pr-tool/inflight-registry.ts +143 -0
- package/src/prompt-library/chat-conciseness.ts +13 -0
- package/src/prompt-library/universal-preamble.ts +10 -5
- package/src/pty-shell/pty-manifest.ts +12 -3
- package/src/pty-shell/tui-run-state.ts +34 -3
- package/src/release-loop/gate-host-reservation.ts +97 -0
- package/src/release-loop/landing-freeze.ts +5 -0
- package/src/release-loop/light-rc.ts +5 -2
- package/src/release-loop/placement.ts +92 -1
- package/src/release-loop/rehearsal-shard-plan.ts +149 -0
- package/src/release-loop/rehearsal.ts +73 -17
- package/src/repl/index.ts +20 -2
- package/src/seat-loop/seat-knowhow.ts +282 -0
- package/src/seat-loop/seat-loop.ts +41 -2
- package/src/seat-loop/traffic-signal.ts +5 -1
- package/src/self-dev/ask-launch-flow.ts +37 -3
- package/src/self-dev/decompose-compose-graph.ts +28 -27
- package/src/self-dev/decompose-proposal.ts +53 -38
- package/src/self-dev/decompose.ts +17 -0
- package/src/self-dev/dev-pipeline.ts +31 -2
- package/src/self-dev/draft-residue.ts +267 -33
- package/src/self-dev/draft-sweep.ts +59 -4
- package/src/self-dev/external-call-zero.ts +77 -0
- package/src/self-dev/launch-preflight-text.ts +6 -0
- package/src/self-dev/launch-preflight.ts +25 -3
- package/src/self-dev/local-supervisor-judge.ts +185 -0
- package/src/self-dev/run-supervisor.ts +229 -19
- package/src/self-dev/run-types.ts +20 -0
- package/src/self-dev/shard-aggregate.ts +13 -2
- package/src/self-dev/shard-plan.ts +56 -1
- package/src/self-dev/shard-run.ts +207 -12
- package/src/self-dev/spawn-lane-files.ts +19 -0
- package/src/self-implement/app-api-budget.ts +51 -11
- package/src/self-implement/config-gate.ts +2 -1
- package/src/self-implement/elanous-tui-spawn.ts +14 -0
- package/src/self-implement/expose-gate.ts +2 -1
- package/src/self-implement/follow-up-placement.ts +79 -0
- package/src/self-implement/gate-artifacts.ts +416 -0
- package/src/self-implement/gate-baseline.ts +96 -5
- package/src/self-implement/gate-cli.ts +207 -17
- package/src/self-implement/gate-remote.ts +136 -26
- package/src/self-implement/gate-scenario.ts +85 -0
- package/src/self-implement/goal-author.ts +160 -8
- package/src/self-implement/graph-authority.ts +38 -6
- package/src/self-implement/graph-overlay-condition.ts +92 -1
- package/src/self-implement/heal-triage.ts +79 -0
- package/src/self-implement/host-regate.ts +97 -7
- package/src/self-implement/orchestrator.ts +115 -134
- package/src/self-implement/pr-title.ts +123 -0
- package/src/self-implement/run-checkpoint.ts +235 -0
- package/src/self-implement/run-ledger.ts +162 -15
- package/src/self-implement/run-stop.ts +4 -3
- package/src/self-implement/run-ttl.ts +28 -4
- package/src/self-implement/seams.ts +2 -0
- package/src/self-implement/shadow-stage.ts +7 -6
- package/src/self-implement/stop-autoheal.ts +2 -1
- package/src/self-implement/train-gate-remote.ts +37 -8
- package/src/self-implement/train-reach-grouping.ts +157 -0
- package/src/self-implement/train-scenario-layers.ts +133 -0
- package/src/self-implement/uncommitted-work.ts +2 -0
- package/src/session/pending-input.ts +19 -0
- package/src/session/session-start.ts +224 -0
- package/src/session-runtime/index.ts +22 -267
- package/src/session-runtime/parallel-dispatch.ts +22 -5
- package/src/shell-runner/background-surface.ts +15 -5
- package/src/skills/tools/agent.ts +39 -6
- package/src/skills/tools/dev-harness.ts +6 -4
- package/src/skills/tools/list-dir.ts +11 -0
- package/src/skills/tools/schedule-wakeup.ts +408 -0
- package/src/skills/tools/shell.ts +246 -4
- package/src/status/bar.ts +26 -0
- package/src/steward/digest-delivery.ts +118 -0
- package/src/steward/hold-hygiene.ts +120 -0
- package/src/steward/resync-tick.ts +114 -23
- package/src/steward/ta-repair-event-collect.ts +158 -0
- package/src/steward/triage.ts +10 -0
- package/src/surface/shell-runner-boot.ts +5 -2
- package/src/task-agent/card-evidence.ts +25 -1
- package/src/task-agent/green-sweep.ts +8 -3
- package/src/task-agent/judge.ts +76 -0
- package/src/task-agent/land-queue.ts +79 -0
- package/src/task-agent/live-moves.ts +30 -9
- package/src/task-agent/postland-card.ts +5 -0
- package/src/task-agent/prelaunch-already-done.ts +98 -0
- package/src/task-agent/real-launch-guard.ts +37 -0
- package/src/task-agent/rejudge-on-head.ts +89 -12
- package/src/task-agent/remote-rejudge-bind.ts +28 -8
- package/src/task-agent/repair-cycle.ts +45 -7
- package/src/task-agent/repair-worker.ts +7 -0
- package/src/task-agent/run-identity.ts +200 -0
- package/src/task-agent/shadow.ts +50 -3
- package/src/task-agent/task-hand.ts +182 -5
- package/src/task-agent/train-landing.ts +77 -0
- package/src/task-agent/work-order.ts +104 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +151 -37
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +88 -19
- package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +2 -1
- package/src/task-orchestrator/surfaces/pod-image-source.ts +2 -1
- package/src/task-orchestrator/surfaces/pod-install-slots.ts +121 -9
- package/src/task-orchestrator/surfaces/pod-lease.ts +118 -6
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +169 -0
- package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +27 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +3 -1
- package/src/task-orchestrator/surfaces/pod-result-marker-wait.ts +33 -0
- package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +67 -0
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +19 -3
- package/src/task-orchestrator/surfaces/pod-warm-switch.ts +141 -0
- package/src/task-orchestrator/surfaces/pod-warm-ta-shadow.ts +85 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +525 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +153 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runner.ts +90 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +362 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +138 -19
- package/src/telegram.ts +2 -4
- package/src/tui/chat/execution-badge.ts +5 -1
- package/src/tui/input/pickers.ts +732 -0
- package/src/tui/input-lag-probe.ts +230 -0
- package/src/tui/slash/mission.ts +492 -0
- package/src/tui/slash/term.ts +877 -0
- package/src/tui-client/dashboard-session.ts +9 -0
- package/src/tui.ts +52 -0
- package/src/user-config/gemini-server-tools.ts +9 -0
- package/src/user-config.ts +53 -16
- package/src/util/spawn-sync-output.ts +7 -0
- package/src/ux-sim/fake-bin.ts +154 -0
- package/src/ux-sim/fake-clock.ts +36 -0
- package/src/ux-sim/headless-tui.ts +72 -0
- package/src/ux-sim/in-process-cli.ts +2 -1
- package/src/ux-sim/scenario.ts +255 -0
- package/src/ux-sim/temp-git-remote.ts +82 -0
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/vlog.ts +25 -7
- package/apps/pwa/out/_next/static/chunks/1015-9cba22a5a4554797.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1084-4e66af7376a18db7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1106.3a6410b7b0c70ad4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1127.32fc642f70423506.js +0 -8
- package/apps/pwa/out/_next/static/chunks/1189-d9b8e1995a831fae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1224.6da98c7ad0eb7dd8.js +0 -34
- package/apps/pwa/out/_next/static/chunks/1370.4e3e2558f111fd5d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1514-b7da377fdc4eb17a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1577.3c24c32584e10d70.js +0 -286
- package/apps/pwa/out/_next/static/chunks/161.130a569855c4d68c.js +0 -24
- package/apps/pwa/out/_next/static/chunks/1864.16da9556e78a9236.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1b6f6b2a.aff090122be3726e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2341.e8a9135e5b910dd5.js +0 -36
- package/apps/pwa/out/_next/static/chunks/257.10afdbe3520f2fc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2658.87550e692dc493f6.js +0 -64
- package/apps/pwa/out/_next/static/chunks/2673.85e2f02efae7a85f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2766-bee623b37472ed63.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2794.6d836872b0bf43e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2806.eb57ced1a528ed39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/283.7a121c4910e6dad7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2870.1dc29aa5fc01f09f.js +0 -115
- package/apps/pwa/out/_next/static/chunks/293.fd38e77ce5d48103.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2993-5952a1cd3fd5d441.js +0 -1
- package/apps/pwa/out/_next/static/chunks/32-a523427c5bd5ed00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3280.af38002a6424a905.js +0 -90
- package/apps/pwa/out/_next/static/chunks/3292-5ddffdb2f517b44f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3523.d3231daaf7aba993.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3763.577de3181e1e6ea0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4138-cc56b0938ef72783.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4255-8350c43d307a8875.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4352.a4954693499c2714.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4421-f2dcbc6fd80ec013.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4513-51b94aeee37c9905.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4928-c3b72f99aa588699.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4986-f9fc0283fb325219.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5014.671a7b9f69c2b7b1.js +0 -94
- package/apps/pwa/out/_next/static/chunks/5137.d398e24710b3feb6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5298.0bea74885cabe754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5315.74c53a39739b11c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5317-8b1df8d02105cd07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5358-33051f41a2b239af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5388.a2ec02501f714ad5.js +0 -62
- package/apps/pwa/out/_next/static/chunks/5395.e6ac948a2d15058d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5409-f282bf533c151849.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5466.d0f4aba9d52acfa8.js +0 -27
- package/apps/pwa/out/_next/static/chunks/5564.3d28b353470fe305.js +0 -104
- package/apps/pwa/out/_next/static/chunks/569.b0cbfb4dee4a167a.js +0 -93
- package/apps/pwa/out/_next/static/chunks/5835-72465b73a8bb8d74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5941.045d6093139e1f7a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6228.47cabb83564dfe54.js +0 -24
- package/apps/pwa/out/_next/static/chunks/6457-8d1993002a87d0fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6513.ef54cfae52351982.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6582.13d245bf87df69f1.js +0 -148
- package/apps/pwa/out/_next/static/chunks/6655.c33f6acc2c61bb89.js +0 -149
- package/apps/pwa/out/_next/static/chunks/6731-9d7195af289c8d4c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6733.0be05b1db9279fc5.js +0 -79
- package/apps/pwa/out/_next/static/chunks/6742-90937146253a8b9e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/687-816858f77a396a4e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6888.72c7b8cfb02a7e61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6993.c17c828ac3ef3253.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7076.9f27ef666b0cefdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7113.1403c5d451804e8c.js +0 -287
- package/apps/pwa/out/_next/static/chunks/7169.9dd603c726c2cd7c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7247-e3ec21f761f86710.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7261-a47b72f96f5199df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7335-5cda81ac90310077.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7356-c2e4af090546e2d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/738-59518742078fda71.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7385.a7375f10db73243a.js +0 -77
- package/apps/pwa/out/_next/static/chunks/7445.cf1cf2fe109bcd96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7505.a2057432ab6ef99a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7515.31f45fac1f9c70cb.js +0 -74
- package/apps/pwa/out/_next/static/chunks/7658-d4ce8c72a9548a91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8107-0b1ae46bc5bcb788.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8132.bfea2f016ce8d992.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8672.e299832bc7d18ee2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8735.f1ef798a5f260f6f.js +0 -226
- package/apps/pwa/out/_next/static/chunks/8774-88f67f7d8d53d299.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8782.252e5a405d86e4ed.js +0 -32
- package/apps/pwa/out/_next/static/chunks/909-9ed7aa0dba804316.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9100.22b0301256827007.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9140-dde77575570bc71e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9292-a7ad9a01c7044b93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9517.00627dc51b02821c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9725.dcbb585652d8e9c5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9761.a3ebf5c389c24ddc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9972.834d2b2b7d48f1ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9995-1a90486f5ae01546.js +0 -1
- package/apps/pwa/out/_next/static/chunks/a435ad95.577add051b4facb2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-83372d8b5863ebf7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d69256d7613c140a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-352c5e9ba3f9b858.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-69f1d725524201a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-a5635eba0d73b84e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ad9181d84285e4ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-86feb08080261a2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-ffa15987e4756d03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-11a3ab44ac327cc1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-6309b11c4264d24d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-8800472079612e87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-d387bf3f2301882a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-048f88d8c8658da6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-6c14f9739b7f6880.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-2834f44f64e98d74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-83372d8b5863ebf7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-a13df6f88e824887.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-8a4397248754cf2b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-daf0bd329360a336.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-f7ef1c28764cb476.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-61e74f28b9353dac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-8aaa6dedc4c10dfb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-294602ba649be829.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-5fa5f41ed3fce323.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-33bf667a5fb297bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-5ae3cbd6e5dba515.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-eb867d16ad6fdede.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1ab789fa68682f7e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6fd41b5b6ff13a71.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fb3d5f7642c99fba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-70f705b2b5ed42ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-c9c93b358a7667b5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-1563c556b81c96ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ce89c20c45b3a673.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-423ae63d786f4ee9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-591a5378a4c4447c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-a250496b19a5824b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c36a9277febc5911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-83372d8b5863ebf7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-a5de57edc6d7f942.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-16bdd4c9f40498df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-75e946e6e71cc6d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-9eb56166774eb256.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-1c5f9ef35dbb22c5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-657ff9985e508498.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-22dc72499da68fb2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9f92737b8766009a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-094ad334e40f8169.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8bcd379852ff6287.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-6b04413f280cd344.js +0 -1
- package/apps/pwa/out/_next/static/chunks/de70ab42-d28d47437208d458.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f800c5e3.46dbd7f2af8c3fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f8bc19ad.47579f6d684b0760.js +0 -122
- package/apps/pwa/out/_next/static/chunks/framework-ca9ac4609fb9fed8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-0dd08d32d15a030f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-f0399d33955c4e01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-d42caca6f8ac2211.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-42e713c13aa41ca8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-8fbf8f61a377aa16.js +0 -1
- package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +0 -1
- /package/apps/pwa/out/_next/static/{cIpv5nU9AycwYWZI9H589 → IVKr3AlRUVLKsw2fh7eXF}/_ssgManifest.js +0 -0
package/src/dashboard/index.ts
CHANGED
|
@@ -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';
|
|
@@ -175,6 +176,7 @@ import {
|
|
|
175
176
|
} from '../chat/turn-typeahead.js';
|
|
176
177
|
import { dispatchStreamingTurnTypeaheadSubmission } from './input/streaming-turn-typeahead.js';
|
|
177
178
|
import { composeVertical, type LayoutZone } from '../layout/composer.js';
|
|
179
|
+
import { createPromptBandRenderObserver, createPromptRowOwner, renderPromptOwnerHintRow, withPromptRowOwner } from '../display/prompt-row-owner.js';
|
|
178
180
|
import type { CoreTurnDispatchTool } from '../core-turn/index.js';
|
|
179
181
|
// streamGrok kept as back-compat shim in chat.ts — not imported here
|
|
180
182
|
// directly anymore. Dashboard runs every turn through the in-process
|
|
@@ -200,6 +202,7 @@ import { startThinking, startPinnedThinking, type ThinkingHandle } from '../thin
|
|
|
200
202
|
import { goalLoopIterationThinking } from './goal-loop-thinking.js';
|
|
201
203
|
import { debug, getAmbientSessionId } from '../debug/log.js';
|
|
202
204
|
import { startEventLoopStallMonitor, thinkingVerbFromFooter } from './event-loop-stall-monitor.js';
|
|
205
|
+
import { createInputLagProbe, noteInputLagPaint, setInputLagProbe } from '../tui/input-lag-probe.js';
|
|
203
206
|
import { getPushcutClient } from '../pushcut/client.js';
|
|
204
207
|
// P2-3 + P4 + P5 — input-core wiring. Aliased on import to dodge the
|
|
205
208
|
// many existing `resolveInputEvent`-ish names elsewhere in this file.
|
|
@@ -433,6 +436,7 @@ import { collectSignals, signalsSummary } from '../tool-hints/signals.js';
|
|
|
433
436
|
import { resetProbes } from '../tool-hints/probe.js';
|
|
434
437
|
import type { HintScope } from '../tool-hints/types.js';
|
|
435
438
|
import { globalAgentRegistry, type AgentToolCallEvent } from '../agent/registry.js';
|
|
439
|
+
import { createBackgroundJobsStatus } from './background-jobs-status.js';
|
|
436
440
|
import { SELF_IMPLEMENT_TOOL_NAMES } from '../boot/daemon-tools/self-implement-names.js';
|
|
437
441
|
import { type FoldMode } from '../log-entry.js';
|
|
438
442
|
import { renderToolCallEvent, renderToolResultVariants } from '../chat/tool-render/index.js';
|
|
@@ -557,21 +561,50 @@ export function resumeInProcessDashboardSession(
|
|
|
557
561
|
setAttachedSessionId: (sessionId: string) => void;
|
|
558
562
|
setActiveSessionId: (sessionId: string) => void;
|
|
559
563
|
},
|
|
560
|
-
):
|
|
564
|
+
):
|
|
565
|
+
| { resumed: true; sessionId: string; turns: number; repaired?: { removedCalls: number; removedResults: number } }
|
|
566
|
+
| { resumed: false; reason: string } {
|
|
561
567
|
const resolvedId = deps.resolveSessionId(resumeSessionId);
|
|
562
568
|
const loaded = resolvedId ? deps.historyFromSession(resolvedId) : null;
|
|
563
569
|
if (!resolvedId || !loaded) return { resumed: false, reason: resumeSessionId };
|
|
564
570
|
|
|
571
|
+
// ④ 깨진 기록 방어(TUI-H8 · #69) — 도구 호출 도중 죽은 프로세스의 기록은 호출·결과 짝이 깨진 꼬리를 남겨
|
|
572
|
+
// 이어받은 첫 턴이 Codex 400 으로 죽는다. 고아 호출·결과만 잘라(`repairCodexToolPairs` · 짝이 맞는 것은 그대로)
|
|
573
|
+
// 이어받고, 같은 id 가 겹쳐 짝을 못 가리면 이어받지 않는다(새 세션 권고).
|
|
574
|
+
const pairs = repairCodexToolPairs(loaded.history as unknown as LLMMessage[]);
|
|
575
|
+
if (pairs.ambiguousIds.length > 0) {
|
|
576
|
+
try {
|
|
577
|
+
debug.log('session', 'resume-refused', {
|
|
578
|
+
surface: 'tui', sessionId: resolvedId, reason: 'ambiguous-tool-pairs', ambiguousIds: pairs.ambiguousIds.length,
|
|
579
|
+
});
|
|
580
|
+
} catch { /* observation is best-effort */ }
|
|
581
|
+
return { resumed: false, reason: `${resumeSessionId} — 도구 호출·결과 짝이 모호한 기록이다 · 새 세션으로 시작한다(권고)` };
|
|
582
|
+
}
|
|
583
|
+
const repairedCount = pairs.removedCalls + pairs.removedResults;
|
|
584
|
+
const history = repairedCount > 0 ? pairs.messages as unknown as ChatMessage[] : loaded.history;
|
|
585
|
+
if (repairedCount > 0) {
|
|
586
|
+
try {
|
|
587
|
+
debug.log('session', 'resume-history-repaired', {
|
|
588
|
+
surface: 'tui', sessionId: resolvedId, removedCalls: pairs.removedCalls, removedResults: pairs.removedResults,
|
|
589
|
+
});
|
|
590
|
+
} catch { /* observation is best-effort */ }
|
|
591
|
+
}
|
|
592
|
+
|
|
565
593
|
const sysFromDash = deps.chatHistory.find((message) => message.role === 'system');
|
|
566
594
|
deps.chatHistory.length = 0;
|
|
567
595
|
if (sysFromDash) deps.chatHistory.push(sysFromDash);
|
|
568
|
-
for (const message of
|
|
596
|
+
for (const message of history) {
|
|
569
597
|
if (message.role === 'system' && sysFromDash && message.content === sysFromDash.content) continue;
|
|
570
598
|
deps.chatHistory.push(message);
|
|
571
599
|
}
|
|
572
600
|
deps.setAttachedSessionId(resolvedId);
|
|
573
601
|
try { deps.setActiveSessionId(resolvedId); } catch { /* best-effort */ }
|
|
574
|
-
return {
|
|
602
|
+
return {
|
|
603
|
+
resumed: true,
|
|
604
|
+
sessionId: resolvedId,
|
|
605
|
+
turns: history.length,
|
|
606
|
+
...(repairedCount > 0 ? { repaired: { removedCalls: pairs.removedCalls, removedResults: pairs.removedResults } } : {}),
|
|
607
|
+
};
|
|
575
608
|
}
|
|
576
609
|
|
|
577
610
|
/**
|
|
@@ -601,7 +634,7 @@ import {
|
|
|
601
634
|
} from '../user-config.js';
|
|
602
635
|
import { streamLLM, anyProviderAvailable, getProviderForConfig, nextReasoningLevel, reasoningLevelLabel, effectiveReasoningLevel, modelSupportsReasoning, type LLMMessage, type LLMToolSpec } from '../llm.js';
|
|
603
636
|
import { currentRouteDecision, formatRouteDecisionSummary } from '../llm/route-decision.js';
|
|
604
|
-
import { resolveDashboardActiveModel } from './acp-active-model.js';
|
|
637
|
+
import { recordDashboardTurnExecution, resolveDashboardActiveModel } from './acp-active-model.js';
|
|
605
638
|
import { getModelFamily, getModelTier } from '../models/prompts.js';
|
|
606
639
|
import { buildPromptInjection, getPromptBankStore } from '../prompt-bank/index.js';
|
|
607
640
|
import { getInputHistoryStore } from '../input-history.js';
|
|
@@ -635,7 +668,7 @@ import { registerDashboardPaneArtifactRuntimes } from './pane-artifact-runtime-r
|
|
|
635
668
|
import { handleDashboardAttachmentPopupAction } from './attachment-popup-actions.js';
|
|
636
669
|
import { renderDashboardAttachmentSummary } from './attachment-summary.js';
|
|
637
670
|
import { openDashboardFolderAttachModal } from './folder-attach-modal-runtime.js';
|
|
638
|
-
import { createDashboardLogClickDeps } from './log-click-runtime.js';
|
|
671
|
+
import { createDashboardLogClickDeps, createFoldClickScrollAnchor } from './log-click-runtime.js';
|
|
639
672
|
import { runDashboardSidebarSessionJoin } from './sidebar-session-join-runtime.js';
|
|
640
673
|
import {
|
|
641
674
|
runDashboardAutoRouteCountdown,
|
|
@@ -791,6 +824,7 @@ import { bootBannerStatusLines, normalizeBootBannerMode, renderMinimalBootBand,
|
|
|
791
824
|
import { attachDashboardFilePathToken } from './file-path-attachment.js';
|
|
792
825
|
import { bootDashboardSlashExecutor } from './slash-executor-boot.js';
|
|
793
826
|
import { buildDashboardSlashRegistry, type DashboardSlashContext } from './slash-runtime/index.js';
|
|
827
|
+
import type { LoopScheduler } from './loop-scheduler.js';
|
|
794
828
|
import { unknownSlashReply } from './slash-runtime/unknown-slash-reply.js';
|
|
795
829
|
import { resolveSurfaceUx } from '../agent/surface-ux/build.js';
|
|
796
830
|
import type { SurfaceUx } from '../agent/surface-ux/types.js';
|
|
@@ -846,7 +880,9 @@ import {
|
|
|
846
880
|
getGitStatusView,
|
|
847
881
|
subscribeGitChanges,
|
|
848
882
|
refreshDirtyAsync as refreshGitDirtyAsync,
|
|
883
|
+
refreshGitSnapshotAsync,
|
|
849
884
|
} from '../git-fs/index.js';
|
|
885
|
+
import { refreshSelfAmbientCache } from '../agent/self-ambient-cache.js';
|
|
850
886
|
import { getPty, listPty, requestPtyTakeover } from '../pty-shell/registry.js';
|
|
851
887
|
import { listPtyManifestRowsAt } from '../pty-shell/pty-manifest.js';
|
|
852
888
|
import { ptyManifestTargets } from '../domains/fleet.js';
|
|
@@ -984,13 +1020,19 @@ import {
|
|
|
984
1020
|
} from '../render.js';
|
|
985
1021
|
import type { SyncMode } from '../types.js';
|
|
986
1022
|
import type { ChatMessage } from '../chat/index.js';
|
|
1023
|
+
import { repairCodexToolPairs } from './input/compact-prepass-recovery.js';
|
|
1024
|
+
import { observeSessionStart } from '../session/session-start.js';
|
|
987
1025
|
import type { PlainTurnSubmit } from '../input/turn-submit.js';
|
|
988
1026
|
// U-4.2 · `renderLogPane` no longer called from dashboard directly —
|
|
989
1027
|
// wd-log widget owns log rendering via renderLogViaWidget. Only
|
|
990
1028
|
// `findBlock` still used here (chat-input block navigation).
|
|
991
1029
|
import { findBlock, logPaneContentHeight, maxLogPaneScroll, visibleSearchRowOffset } from '../panes/log-pane.js';
|
|
992
1030
|
import { createQuitConfirmationState, gracefulQuit, type QuitConfirmationState } from './graceful-quit.js';
|
|
993
|
-
import { handoffTurnTypeaheadAtTurnEnd, releaseTurnTypeaheadPending, steerLastTurnTypeaheadSubmission } from './input/turn-steer-bridge.js';
|
|
1031
|
+
import { consumeTurnEndInterrupted, handoffTurnTypeaheadAtTurnEnd, releaseTurnTypeaheadPending, runTurnTypeaheadPushNow, steerLastTurnTypeaheadSubmission } from './input/turn-steer-bridge.js';
|
|
1032
|
+
import { abortTurnOnly } from '../turn-abort-scope.js';
|
|
1033
|
+
import { consumeInternalTurnYield, createInternalTurnGate, formatInternalTurnBanner, resolveInternalTurnSubmitIntent, type InternalTurnRequest } from './input/internal-turn.js';
|
|
1034
|
+
import { createSeatAutowake } from './input/seat-autowake.js';
|
|
1035
|
+
import { createDashboardWakeupScheduler, registerScheduleWakeupHostTool } from '../skills/tools/schedule-wakeup.js';
|
|
994
1036
|
import { createAttachmentRowMap } from '../log-pane/attachment-row-map.js';
|
|
995
1037
|
import { createAttachmentPopup, type AttachmentPopupAction } from '../log-pane/attachment-popup.js';
|
|
996
1038
|
import { findLogMatches, findVisibleLogMatches, type LogSearchResult } from '../log-pane/search.js';
|
|
@@ -1022,7 +1064,6 @@ import { enqueueSoftStop, readSoftStopRequestStatus } from '../harness/control-i
|
|
|
1022
1064
|
import { setSelfImplementSoftStopSpaceObserver } from '../self-implement/self-implement-runtime.js';
|
|
1023
1065
|
import { createViEditor, type ViEditorHandle } from '../vi-editor.js';
|
|
1024
1066
|
import { launchEditor, canLaunchEditor } from '../editor-launcher.js';
|
|
1025
|
-
import { createSshPickerModal } from '../ssh/ssh-picker-modal.js';
|
|
1026
1067
|
import {
|
|
1027
1068
|
refreshRemoteWorkingDir,
|
|
1028
1069
|
refreshRemoteWorkingDirPreview,
|
|
@@ -1032,12 +1073,10 @@ import {
|
|
|
1032
1073
|
remoteBadge,
|
|
1033
1074
|
} from '../ssh/remote-browser.js';
|
|
1034
1075
|
import { editRemoteFile } from '../ssh/remote-edit.js';
|
|
1035
|
-
import {
|
|
1036
|
-
import {
|
|
1037
|
-
import {
|
|
1038
|
-
import {
|
|
1039
|
-
import { listTransferTargets, type TransferTarget } from '../transfer/transfer-targets.js';
|
|
1040
|
-
import { createTransferPickerModal } from '../transfer/transfer-picker-modal.js';
|
|
1076
|
+
import { createFolderPickerModal } from '../finder/finder-modal.js';
|
|
1077
|
+
import { hostname } from 'node:os';
|
|
1078
|
+
import { type TransferTarget } from '../transfer/transfer-targets.js';
|
|
1079
|
+
import { createInputPickers } from '../tui/input/pickers.js';
|
|
1041
1080
|
import { sshTransfer, type SshTransferProgress } from '../transfer/ssh-transfer.js';
|
|
1042
1081
|
import { iphoneTransfer } from '../transfer/iphone-transfer.js';
|
|
1043
1082
|
import { DashboardAcpChat, displayBackendName, parseAcpSlash, type AcpBackendId } from './chat/acp-chat.js';
|
|
@@ -1081,7 +1120,6 @@ import {
|
|
|
1081
1120
|
} from '../agent/channel-router.js';
|
|
1082
1121
|
import { initTtySnapshotTools } from '../skills/tools/tty-snapshot.js';
|
|
1083
1122
|
import { initAgentHandoffTool } from '../skills/tools/agent-handoff.js';
|
|
1084
|
-
import { touchSshHost } from '../ssh/ssh-hosts.js';
|
|
1085
1123
|
import { DisplayCoordinator } from '../display/coordinator.js';
|
|
1086
1124
|
import {
|
|
1087
1125
|
createLivePlaygroundHarness,
|
|
@@ -1115,6 +1153,8 @@ import { agentBatchScratchTitle, renderAgentBatchScratch } from '../display/agen
|
|
|
1115
1153
|
import { type ExecutionSurfaceHandle } from '../display/index.js';
|
|
1116
1154
|
import { AgentSurfaceStore, renderAgentDetail, renderAgentRoster, type AgentSurfaceState } from '../display/agent-surface.js';
|
|
1117
1155
|
import { globalAgentFlash } from '../display/agent-flash.js';
|
|
1156
|
+
import { globalTaskNotificationQueue } from '../agent/task-notification.js';
|
|
1157
|
+
import { createDashboardAgentWakeNotify, installAgentWake, wrapAgentWakePort } from './input/agent-wake.js';
|
|
1118
1158
|
import { renderDebugEventDetail, renderDebugEventRows, renderDebugStack, renderPromptInjectionDebug } from '../display/debug-surface.js';
|
|
1119
1159
|
import { buildDebugCallStack } from '../debug/call-stack.js';
|
|
1120
1160
|
import { buildAstGrepHostTool } from '../skills/tools/ast-grep.js';
|
|
@@ -1162,7 +1202,6 @@ import {
|
|
|
1162
1202
|
encodeBrowserScopedSubmitText,
|
|
1163
1203
|
resolveBrowserActionContextFromHit,
|
|
1164
1204
|
} from '../browser-pane/actions.js';
|
|
1165
|
-
import { resolveBrowserTransferCapability } from '../browser-pane/capabilities.js';
|
|
1166
1205
|
import {
|
|
1167
1206
|
cyclePreviewSourceForView,
|
|
1168
1207
|
formatPreviewSourceLabel,
|
|
@@ -1276,7 +1315,7 @@ import {
|
|
|
1276
1315
|
type SpaceLongPressDetector,
|
|
1277
1316
|
} from './input/space-longpress-detector.js';
|
|
1278
1317
|
import { isAllowlistedDictationSurface } from './input/dictation-surface-allowlist.js';
|
|
1279
|
-
import { createDashboardExternalInputHost } from './input/external-input-host.js';
|
|
1318
|
+
import { createDashboardExternalInputHost, stopTurnViaEscGate, updateDashboardExternalInputQueueNotice } from './input/external-input-host.js';
|
|
1280
1319
|
import { registerExternalInputHost } from '../input/external-input-queue.js';
|
|
1281
1320
|
// H3 #6 follow-up #3 — background sessions render as a distinct sidebar
|
|
1282
1321
|
// kind with live state badge + tap-to-Join. Dashboard pulls BG records
|
|
@@ -1882,8 +1921,11 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
|
|
|
1882
1921
|
const dashboardQuitConfirmationState = createQuitConfirmationState();
|
|
1883
1922
|
const signalQuitConfirmationState = createQuitConfirmationState();
|
|
1884
1923
|
let disposeDashboardEventLoopMonitor = (): void => {};
|
|
1924
|
+
// TUI-AGENT-IDLE-WAKE — 이 대시보드 «자기» 완료 알림 구독만 푼다(배선 지점에서 채운다).
|
|
1925
|
+
let disposeDashboardAgentWake = (): void => {};
|
|
1885
1926
|
const closeDashboardTui = (): void => {
|
|
1886
1927
|
disposeDashboardEventLoopMonitor();
|
|
1928
|
+
try { disposeDashboardAgentWake(); } catch { /* never block exit */ }
|
|
1887
1929
|
closeTui();
|
|
1888
1930
|
};
|
|
1889
1931
|
const exitDashboardTui = (): void => {
|
|
@@ -2202,6 +2244,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
|
|
|
2202
2244
|
pillRowGetter: () => pillRowGetterForClick?.() ?? null,
|
|
2203
2245
|
onPillClick: () => onPillClickForClick?.(),
|
|
2204
2246
|
foldStack,
|
|
2247
|
+
foldClickAnchor,
|
|
2205
2248
|
});
|
|
2206
2249
|
|
|
2207
2250
|
// Dispatcher for attachment-popup actions (log-pane click → popup
|
|
@@ -2793,6 +2836,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
2793
2836
|
// execute slashes, etc.
|
|
2794
2837
|
const chatModeState = createSessionPostureState();
|
|
2795
2838
|
|
|
2839
|
+
// TUI-LOOP-COMMAND — 실행 중 스케줄러를 쥐는 ref. showDashboard 인스턴스당
|
|
2840
|
+
// 하나로, 입력 루프 «밖»에서 한 번만 선언한다. slashCtx 는 명령마다 다시
|
|
2841
|
+
// 만들어지므로 리터럴 옆에 선언하면 /loop start 로 만든 스케줄러 ref 가
|
|
2842
|
+
// 다음 명령에서 사라진다 — /loop stop · /session new|clear 세션 끝 정지가
|
|
2843
|
+
// 같은 ref 를 써야 «stop 0»(판정)가 성립한다.
|
|
2844
|
+
const dashboardLoopSchedulerRef: { current: LoopScheduler | null } = { current: null };
|
|
2845
|
+
|
|
2796
2846
|
// T3-C2 — vi-editor mounted in the preview pane. Set by the
|
|
2797
2847
|
// browser pane's `e` key on a file; cleared on :q / :q!. When
|
|
2798
2848
|
// non-null it overrides the preview pane's normal render path.
|
|
@@ -2865,6 +2915,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
2865
2915
|
// current value so log analysis can filter "what keys fired while
|
|
2866
2916
|
// a turn was in flight".
|
|
2867
2917
|
let streamingInFlight = false;
|
|
2918
|
+
/** 지금 턴의 escGate 중단 — 외부 `/stop`(TUI-STOP-INPUT-BYPASS)이 대기열을 건너뛰어 부른다. 턴 밖이면 null. */
|
|
2919
|
+
let currentTurnExternalStop: (() => void) | null = null;
|
|
2868
2920
|
const resolveTmuxStatusLabel = (): string | null => {
|
|
2869
2921
|
if (!process.env['TMUX']) return null;
|
|
2870
2922
|
const fallback = process.env['TMUX_PANE'] ?? 'tmux';
|
|
@@ -3333,6 +3385,31 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3333
3385
|
// 즉시 에코 · 턴 종료 시 loop-head 핸드오프가 prefill/자동제출로 넘긴다.
|
|
3334
3386
|
// (외곽 스코프 선언 — 스트리밍 디스패처와 메인 키 루프 양쪽에서 참조.)
|
|
3335
3387
|
const turnTypeaheadRef = { state: createTurnTypeaheadState() };
|
|
3388
|
+
// «지금 밀어 넣기»로 끊은 턴 — 턴 종료가 큐를 composer 로 «복원»하지 않고 곧바로 «제출»하게 한다.
|
|
3389
|
+
const steerInterruptRef = { pending: false };
|
|
3390
|
+
// ⭐ H2 내부 턴 문(TUI-H2-INTERNAL-TURN) — 자율 재개(H3·H4·H5)가 지나갈 «문 하나». 판정은 모듈,
|
|
3391
|
+
// 여기선 상태를 읽어 넘기기만 한다(⚠️ promptCtl·가시성 함수는 아래에서 선언 — 호출 시점엔 있다).
|
|
3392
|
+
const internalTurnGate = createInternalTurnGate({
|
|
3393
|
+
isTurnRunning: () => streamingInFlight,
|
|
3394
|
+
isInputForeground: () =>
|
|
3395
|
+
chatMainInputVisibilityState().inputOwnership?.owner === 'chat-main' && !topBlockingForegroundModal(),
|
|
3396
|
+
getBufferText: () => promptCtl.getBufferText?.(),
|
|
3397
|
+
getTypeaheadCount: () =>
|
|
3398
|
+
turnTypeaheadRef.state.queuedSubmissions.length + (turnTypeaheadRef.state.buffer ? 1 : 0),
|
|
3399
|
+
});
|
|
3400
|
+
// ⭐ H3 턴 끝 자동 재개(OPT-QUEUE-AUTOWAKE) — 자리 TUI ∧ autowake 파일일 때만. 턴 끝에 «남은 일»(안 읽은
|
|
3401
|
+
// 우편 · queue.md «## 대기»)을 재고 H2 문으로 다음 턴을 청한다. 판정·상한은 모듈, 여기선 배선만.
|
|
3402
|
+
const seatAutowake = createSeatAutowake({
|
|
3403
|
+
startTurnIfIdle: (text, opts) => internalTurnGate.startTurnIfIdle(text, opts),
|
|
3404
|
+
getCwd: () => workingDir.cwd,
|
|
3405
|
+
notify: (line) => { chatLines.push(C.muted(line)); draw(); },
|
|
3406
|
+
});
|
|
3407
|
+
const internalTurnYieldPort = wrapAgentWakePort(seatAutowake.wrapPort(internalTurnGate.port));
|
|
3408
|
+
// ⭐ H5 자기 예약 깨움(TUI-H5-SCHEDULE-WAKEUP) — 프로세스 안 타이머가 그 시각에 H2 문으로 한 턴을 청한다.
|
|
3409
|
+
// 직전 회차 판정 = 그 예약이 연 내부 턴이 아직 도나. 도구 등록은 아래 host tool 자리(하니스 자식 제외).
|
|
3410
|
+
const wakeupScheduler = createDashboardWakeupScheduler(internalTurnGate, {
|
|
3411
|
+
notify: (line) => { chatLines.push(C.muted(line)); draw(); },
|
|
3412
|
+
});
|
|
3336
3413
|
// ⭐ `B3` — 큐를 «도는 턴»에 넣으려면 그 턴이 도는 ***ACP 세션 id*** 가 필요하다.
|
|
3337
3414
|
// ⛔ `attachedSessionId`(대화 세션)와 «다른 축»이다 — 루프는 ACP 세션으로 스코프를 연다.
|
|
3338
3415
|
// ⚠️ 세션은 이 함수 «뒤»에서 만들어지므로 참조로 둔다(호출 시점엔 채워져 있다).
|
|
@@ -3361,6 +3438,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3361
3438
|
});
|
|
3362
3439
|
}
|
|
3363
3440
|
try { process.stdout.write(paint); } catch { /* fail-soft */ }
|
|
3441
|
+
noteInputLagPaint();
|
|
3364
3442
|
};
|
|
3365
3443
|
|
|
3366
3444
|
// Use this instead of bare `chatScrollOffset = -1` inside streaming
|
|
@@ -3390,9 +3468,17 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3390
3468
|
}),
|
|
3391
3469
|
log: (category, event, data) => debug.log(category, event, data),
|
|
3392
3470
|
});
|
|
3471
|
+
// ⌨️ TUI-TYPING-LAG-OBS — 키→칠 ms ⊕ 턴 시작 창 루프 지연(`tui.input-lag`). 훅은 tui.ts·chat·turn-preamble.
|
|
3472
|
+
const inputLagProbe = createInputLagProbe({
|
|
3473
|
+
isBusy: () => streamingInFlight,
|
|
3474
|
+
log: (category, event, data) => debug.log(category, event, { ...data }),
|
|
3475
|
+
});
|
|
3476
|
+
setInputLagProbe(inputLagProbe);
|
|
3393
3477
|
const disposeEventLoopStallMonitorOnBeforeExit = (): void => disposeDashboardEventLoopMonitor();
|
|
3394
3478
|
disposeDashboardEventLoopMonitor = (): void => {
|
|
3395
3479
|
disposeEventLoopStallMonitor();
|
|
3480
|
+
setInputLagProbe(null);
|
|
3481
|
+
inputLagProbe.dispose();
|
|
3396
3482
|
process.removeListener('beforeExit', disposeEventLoopStallMonitorOnBeforeExit);
|
|
3397
3483
|
};
|
|
3398
3484
|
process.once('beforeExit', disposeEventLoopStallMonitorOnBeforeExit);
|
|
@@ -3401,14 +3487,24 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3401
3487
|
// batch status during skill execution, plus static folds already
|
|
3402
3488
|
// committed to chatLines. The runtime helper owns registration;
|
|
3403
3489
|
// `f` just toggles the most recent target.
|
|
3490
|
+
// A click toggle keeps the viewport top (#81 — pinning to the tail
|
|
3491
|
+
// moved the clicked header, so the same spot no longer folded back).
|
|
3492
|
+
const foldClickAnchor = createFoldClickScrollAnchor({
|
|
3493
|
+
getScrollOffset: () => chatScrollOffset,
|
|
3494
|
+
setScrollOffset: (offset) => { chatScrollOffset = offset; },
|
|
3495
|
+
maxScroll: () => logScrollMax(computeLogH(termSize().rows)),
|
|
3496
|
+
getFrozenTail: () => logFrozenTailIndex,
|
|
3497
|
+
setFrozenTail: (index) => { logFrozenTailIndex = index; },
|
|
3498
|
+
lineCount: () => chatLines.length,
|
|
3499
|
+
}, () => {
|
|
3500
|
+
// Pin back to tail if the user hadn't scrolled away — expanding
|
|
3501
|
+
// a batch tree adds rows, and losing the bottom anchor is
|
|
3502
|
+
// disorienting when `f` was supposed to reveal more content.
|
|
3503
|
+
chatScrollOffset = -1;
|
|
3504
|
+
});
|
|
3404
3505
|
const foldStack = new FoldStack({
|
|
3405
3506
|
chatLines,
|
|
3406
|
-
onAfterToggle: () =>
|
|
3407
|
-
// Pin back to tail if the user hadn't scrolled away — expanding
|
|
3408
|
-
// a batch tree adds rows, and losing the bottom anchor is
|
|
3409
|
-
// disorienting when `f` was supposed to reveal more content.
|
|
3410
|
-
chatScrollOffset = -1;
|
|
3411
|
-
},
|
|
3507
|
+
onAfterToggle: (target, delta) => foldClickAnchor.onAfterToggle(target, delta),
|
|
3412
3508
|
});
|
|
3413
3509
|
// The most recent streamed assistant response — kept in both raw and
|
|
3414
3510
|
// formatted form so Ctrl+Y can copy clean markdown and `r` in log
|
|
@@ -3640,7 +3736,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3640
3736
|
// boot block via bgSurface.onUpdate; read on every status redraw.
|
|
3641
3737
|
// Null until the first handle is tracked so we don't render an empty
|
|
3642
3738
|
// pill on a fresh session.
|
|
3643
|
-
let latestShellRollup: { running: number; backgrounded: number } | null = null;
|
|
3739
|
+
let latestShellRollup: { running: number; backgrounded: number; background: number } | null = null;
|
|
3740
|
+
// TUI-BG-JOB-VISIBILITY (#71) — «⧗ 백그라운드 N»(턴을 넘어 사는 셸 일). 관측은 모듈, 여기선 배선만
|
|
3741
|
+
// (깨우기 0 — TUI-AGENT-IDLE-WAKE 몫).
|
|
3742
|
+
const backgroundJobsStatus = createBackgroundJobsStatus();
|
|
3644
3743
|
// The ACP bridge is created later during dashboard boot. Keep status renders
|
|
3645
3744
|
// safe before then, and bind this reader once the bridge exists.
|
|
3646
3745
|
let activeAcpRuntimeModel = (): string | undefined => undefined;
|
|
@@ -3680,6 +3779,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3680
3779
|
sshLabel: sshStatusLabel,
|
|
3681
3780
|
host: hostLabel,
|
|
3682
3781
|
runningAgents: globalAgentRegistry.list().filter((agent) => agent.state === 'running').length,
|
|
3782
|
+
backgroundJobs: backgroundJobsStatus.summary(),
|
|
3683
3783
|
controller: process.env.ELANOUS_CONTROLLER?.trim() || undefined,
|
|
3684
3784
|
shellCount: liveShells,
|
|
3685
3785
|
shellRollup: latestShellRollup ?? undefined,
|
|
@@ -3707,7 +3807,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3707
3807
|
let drawQueued = false;
|
|
3708
3808
|
let queuedDrawForce = false;
|
|
3709
3809
|
let lastDrawDebugAt = 0;
|
|
3710
|
-
|
|
3810
|
+
const promptRowOwner = createPromptRowOwner({ requestFrame: () => draw() });
|
|
3811
|
+
const observePromptBand = createPromptBandRenderObserver();
|
|
3711
3812
|
const displayEvents = createDisplayEventBus();
|
|
3712
3813
|
const recentTerminalMouseIntents: Array<{
|
|
3713
3814
|
surfaceId: string;
|
|
@@ -3864,11 +3965,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3864
3965
|
// and fires onPush/onPop. The modal-surface-adapter (registered
|
|
3865
3966
|
// below after widgetHost discovery) fans those into the global
|
|
3866
3967
|
// SurfaceRegistry. Existing pushModal callsites unchanged.
|
|
3867
|
-
const identityHooks = composeIdentityHooks({
|
|
3868
|
-
afterRender: (request) => {
|
|
3869
|
-
try { repaintPromptAfterRender({ force: request.force }); } catch { /* noop */ }
|
|
3870
|
-
},
|
|
3871
|
-
});
|
|
3968
|
+
const identityHooks = composeIdentityHooks({});
|
|
3872
3969
|
// U4 Bundle B — layer the workspace host wiring on top of modal
|
|
3873
3970
|
// identity wiring so popup/dialog/terminal modal mounts
|
|
3874
3971
|
// automatically project into a host-managed workspace set. This is
|
|
@@ -4050,7 +4147,17 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4050
4147
|
// out-of-process modifications (external editor, another PTY,
|
|
4051
4148
|
// etc.). Throttle inside git-fs prevents back-to-back subprocess
|
|
4052
4149
|
// spawns when this and a post-Edit refresh overlap.
|
|
4150
|
+
// TUI-TYPING-LAG — keep the turn-preamble caches warm off the event loop (boot ⊕ every tick):
|
|
4151
|
+
// the git snapshot (async subprocesses) and the self-ambient store scan (worker thread). A turn
|
|
4152
|
+
// then serves them from cache instead of rebuilding synchronously on the turn path. Both are
|
|
4153
|
+
// no-ops while the cached value is younger than the threshold; failures are fail-soft.
|
|
4154
|
+
const warmTurnPreambleCaches = (): void => {
|
|
4155
|
+
try { void refreshGitSnapshotAsync(getSessionCwd(), { ifOlderThanMs: 30_000 }); } catch { /* noop */ }
|
|
4156
|
+
try { void refreshSelfAmbientCache({}, { ifOlderThanMs: 25_000 }); } catch { /* noop */ }
|
|
4157
|
+
};
|
|
4158
|
+
warmTurnPreambleCaches();
|
|
4053
4159
|
const gitDirtyTimer: ReturnType<typeof setInterval> = setInterval(() => {
|
|
4160
|
+
warmTurnPreambleCaches();
|
|
4054
4161
|
try {
|
|
4055
4162
|
const before = getGitStatusView(getSessionCwd());
|
|
4056
4163
|
// TUI-TYPING-LAG — the 5s probe ran `git status` synchronously (130ms+, hundreds under load).
|
|
@@ -4176,7 +4283,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4176
4283
|
wireShellRunnerSurface,
|
|
4177
4284
|
createFileCaptureEngine,
|
|
4178
4285
|
setShellRunnerDeps,
|
|
4179
|
-
setLatestShellRollup: (rollup) => {
|
|
4286
|
+
setLatestShellRollup: (rollup) => {
|
|
4287
|
+
latestShellRollup = rollup;
|
|
4288
|
+
// 유휴 중 백그라운드가 끝나도 상태줄이 고쳐지도록 — 수가 바뀔 때만 다시 그린다.
|
|
4289
|
+
if (backgroundJobsStatus.noteRollup(rollup)) {
|
|
4290
|
+
try { draw(); } catch { /* TUI torn down */ }
|
|
4291
|
+
}
|
|
4292
|
+
},
|
|
4180
4293
|
});
|
|
4181
4294
|
}
|
|
4182
4295
|
const { setShellApprover } = await import('../shell-primitive/index.js');
|
|
@@ -6458,8 +6571,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6458
6571
|
const attachChatStreamingKeys = (abortCtrl: AbortController): () => void => {
|
|
6459
6572
|
const chatLinesStartIndex = chatLines.length;
|
|
6460
6573
|
userScrolledDuringStream = false;
|
|
6461
|
-
streamingInFlight = true;
|
|
6462
|
-
reportTuiTurn('busy');
|
|
6463
6574
|
const SCROLL_KEYS = new Set(['j', 'k', 'down', 'up', 'pageup', 'pagedown', 'home', 'end', 'g', 'G']);
|
|
6464
6575
|
// T3-B2: ESC abort gate. When the user hits Esc AND sub-agent
|
|
6465
6576
|
// work is running, pop a confirmation modal before tearing down
|
|
@@ -6759,7 +6870,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6759
6870
|
// ⛔ 중복 전송 방지가 계약이다: 레지스트리가 배수되면 `onDrained` 가
|
|
6760
6871
|
// ***여기 큐도 같이 비운다*** — 안 그러면 같은 발화가 턴 끝에 «또» 나간다.
|
|
6761
6872
|
// ⚠️ `immediate`(슬래시 등)는 지금 실행되므로 «넣지 않는다».
|
|
6762
|
-
|
|
6873
|
+
// ⛔ H2 — 내부 턴 중의 사람 입력은 그 턴에 끼워 넣지 않는다(턴 끝 FIFO 로 기다린다).
|
|
6874
|
+
if (r.submissionDisposition === 'fifo' && !r.immediateSubmission && !internalTurnGate.isInternalTurnActive()) {
|
|
6763
6875
|
const queuedText = turnTypeaheadRef.state.queuedSubmissions.at(-1);
|
|
6764
6876
|
const sid = activeSessionIdForTypeahead();
|
|
6765
6877
|
if (queuedText && sid) {
|
|
@@ -6790,6 +6902,22 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6790
6902
|
}
|
|
6791
6903
|
}
|
|
6792
6904
|
if (r.releasedPendingIds) releaseTurnTypeaheadPending(r.releasedPendingIds, 'recalled');
|
|
6905
|
+
if (r.pushNowRequested) {
|
|
6906
|
+
const decision = runTurnTypeaheadPushNow(turnTypeaheadRef, {
|
|
6907
|
+
runningToolNames: () => getEscAbortWaitingToolNames(escAbortToolState),
|
|
6908
|
+
interruptTurn: () => {
|
|
6909
|
+
steerInterruptRef.pending = true;
|
|
6910
|
+
try { globalAgentRegistry.markBackgroundAllRunning(); } catch { /* 승격 실패가 끊기를 막지 않는다 */ }
|
|
6911
|
+
abortTurnOnly(abortCtrl);
|
|
6912
|
+
},
|
|
6913
|
+
pushNotice: (line) => pushChatLine(C.muted(line)),
|
|
6914
|
+
});
|
|
6915
|
+
debug.log('dashboard.turn-typeahead', 'push-now', {
|
|
6916
|
+
kind: decision.kind,
|
|
6917
|
+
tools: decision.kind === 'interrupt' ? decision.runningTools : decision.kind === 'nothing' ? [] : decision.blockingTools,
|
|
6918
|
+
});
|
|
6919
|
+
draw();
|
|
6920
|
+
}
|
|
6793
6921
|
if (r.immediateSubmission) {
|
|
6794
6922
|
turnTypeaheadRef.state = await dispatchStreamingTurnTypeaheadSubmission(
|
|
6795
6923
|
turnTypeaheadRef.state,
|
|
@@ -7015,19 +7143,29 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
7015
7143
|
// `runStreamingKeyUnifiedDispatch` above for the route body.
|
|
7016
7144
|
await runStreamingKeyUnifiedDispatch(key);
|
|
7017
7145
|
});
|
|
7146
|
+
// ⛔ TUI-STALE-BUSY-MARKER — busy 는 «끄는 closure 를 돌려주기 직전»에 켠다. 위 준비(ESC 게이트·키 리스너)가
|
|
7147
|
+
// 던지면 호출자는 cleanup 을 못 받으므로, 먼저 켜 두면 아무도 못 끄는 busy 가 남는다(#37).
|
|
7148
|
+
streamingInFlight = true;
|
|
7149
|
+
reportTuiTurn('busy');
|
|
7150
|
+
const externalStop = (): void => { stopTurnViaEscGate(escGate); };
|
|
7151
|
+
currentTurnExternalStop = externalStop;
|
|
7018
7152
|
return () => {
|
|
7019
|
-
|
|
7020
|
-
|
|
7153
|
+
if (currentTurnExternalStop === externalStop) currentTurnExternalStop = null;
|
|
7154
|
+
// 단계마다 격리 — 앞 단계가 던져도 settle·idle 전이에 닿는다(키 층 유휴 ∧ 턴 층 busy 이원 판정 #55 차단).
|
|
7155
|
+
runTurnTeardownStep('stream-keys-detach', cleanup);
|
|
7156
|
+
runTurnTeardownStep('esc-gate-dispose', () => { escGate.dispose(); });
|
|
7021
7157
|
try {
|
|
7022
7158
|
if (!stoppedHarnessSpace) settleEscAbortPendingLines(chatLines, chatLinesStartIndex);
|
|
7023
7159
|
} catch (err) {
|
|
7024
7160
|
const message = err instanceof Error ? err.message : String(err);
|
|
7025
7161
|
const stackHead = err instanceof Error ? err.stack?.split('\n')[1]?.trim() : undefined;
|
|
7026
7162
|
debug.log('dashboard.turn', 'settle-failed', { message, stackHead });
|
|
7163
|
+
logTurnThrew('settle-esc-lines', err);
|
|
7027
7164
|
} finally {
|
|
7028
7165
|
userScrolledDuringStream = false;
|
|
7029
7166
|
streamingInFlight = false;
|
|
7030
7167
|
reportTuiTurn('idle');
|
|
7168
|
+
seatAutowake.noteTurnEnded();
|
|
7031
7169
|
}
|
|
7032
7170
|
};
|
|
7033
7171
|
};
|
|
@@ -9889,6 +10027,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
9889
10027
|
// kept alive per cwd; reaped on 10-min idle. See
|
|
9890
10028
|
// 내부 문서 `CAPABILITIES-lsp` for the op surface.
|
|
9891
10029
|
pluginHost.registerHostTool(buildLspHostTool());
|
|
10030
|
+
// H5 — 이 TUI 로 돌아오는 자기 예약. 하니스 자식(ELANOUS_HARNESS_ROLE 있음)은 무인 런이라 싣지 않는다.
|
|
10031
|
+
registerScheduleWakeupHostTool(pluginHost, wakeupScheduler);
|
|
9892
10032
|
pluginHost.registerHostTool({
|
|
9893
10033
|
name: 'view_getConfig',
|
|
9894
10034
|
description: 'Return dashboard view configuration: native and custom views, order, enabled state, shortcuts, baseView, pane rows, ratios, primary pane, and active view.',
|
|
@@ -11216,6 +11356,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11216
11356
|
// actually scrolled up. If something else (streaming, slash reply)
|
|
11217
11357
|
// pulls the offset back to -1 (tail), the freeze is no-op'd so the
|
|
11218
11358
|
// user sees full content at tail regardless of stale index state.
|
|
11359
|
+
foldClickAnchor.reconcile();
|
|
11219
11360
|
const logProjection = buildDashboardLogProjection({
|
|
11220
11361
|
chatScrollOffset,
|
|
11221
11362
|
logFrozenTailIndex,
|
|
@@ -11276,7 +11417,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11276
11417
|
height: 1,
|
|
11277
11418
|
render: (_h, c) => [suppressPromptArea ? ' '.repeat(c) : hLine(c)],
|
|
11278
11419
|
});
|
|
11279
|
-
const inputPromptZone = buildDashboardTurnTypeaheadPromptZone({
|
|
11420
|
+
const inputPromptZone = withPromptRowOwner(buildDashboardTurnTypeaheadPromptZone({
|
|
11280
11421
|
frame: promptFrame,
|
|
11281
11422
|
height: promptFrame.inputHeight,
|
|
11282
11423
|
placeholder: C.muted('\u276f '),
|
|
@@ -11286,7 +11427,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11286
11427
|
width: totalW,
|
|
11287
11428
|
suppressPromptArea,
|
|
11288
11429
|
onPaint: paint => { promptCaret = paint.caret; },
|
|
11289
|
-
});
|
|
11430
|
+
}), promptRowOwner, { suppressPromptArea, onCaret: (caret) => { promptCaret = caret; }, observe: observePromptBand });
|
|
11290
11431
|
zones.push(observeDashboardTurnTypeaheadPromptZone(inputPromptZone, {
|
|
11291
11432
|
promptBottomRow: promptFrame.promptBottomRow,
|
|
11292
11433
|
state: turnTypeaheadRef.state,
|
|
@@ -11298,7 +11439,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11298
11439
|
});
|
|
11299
11440
|
|
|
11300
11441
|
// Essential bottom area: status line only; no dock row.
|
|
11301
|
-
zones.push({ id: 'status-gap-top', height: 1, render: () => [
|
|
11442
|
+
zones.push({ id: 'status-gap-top', height: 1, render: () => [renderPromptOwnerHintRow(promptRowOwner, { suppressPromptArea })] });
|
|
11302
11443
|
zones.push({
|
|
11303
11444
|
id: 'status',
|
|
11304
11445
|
height: 1,
|
|
@@ -11519,11 +11660,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11519
11660
|
if (debug.enabled) {
|
|
11520
11661
|
debug.log('dashboard.draw', 'flush-microtask', {
|
|
11521
11662
|
force,
|
|
11522
|
-
promptRepainted:
|
|
11663
|
+
promptRepainted: false,
|
|
11523
11664
|
});
|
|
11524
11665
|
}
|
|
11525
11666
|
drawNow({ force });
|
|
11526
|
-
try { repaintPromptAfterRender({ force }); } catch { /* noop */ }
|
|
11527
11667
|
});
|
|
11528
11668
|
};
|
|
11529
11669
|
|
|
@@ -11561,63 +11701,53 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11561
11701
|
let agentSearchModal: SearchModalHandle | null = null;
|
|
11562
11702
|
let agentSearchHandle: { dispose: () => void } | null = null;
|
|
11563
11703
|
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
},
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
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
|
-
}
|
|
11704
|
+
// TUI-DIET-SEAM-PICKERS — the five input pickers (agent-roster search,
|
|
11705
|
+
// transfer, Ctrl+T file finder, Alt+C directory finder, Ctrl+K SSH)
|
|
11706
|
+
// live in src/tui/input/pickers.ts. Mutable dashboard state crosses
|
|
11707
|
+
// the seam as live get/set accessors (never a value copy); `draw` is
|
|
11708
|
+
// re-read at call time because it is reassigned after mount.
|
|
11709
|
+
const {
|
|
11710
|
+
openAgentRosterSearch,
|
|
11711
|
+
openTransferPicker,
|
|
11712
|
+
openFinderPicker,
|
|
11713
|
+
openDirectoryPicker,
|
|
11714
|
+
openSshPicker,
|
|
11715
|
+
} = createInputPickers({
|
|
11716
|
+
state: {
|
|
11717
|
+
get agentSearchModal() { return agentSearchModal; },
|
|
11718
|
+
set agentSearchModal(v: SearchModalHandle | null) { agentSearchModal = v; },
|
|
11719
|
+
get agentSearchHandle() { return agentSearchHandle; },
|
|
11720
|
+
set agentSearchHandle(v: { dispose: () => void } | null) { agentSearchHandle = v; },
|
|
11721
|
+
get agentRosterCursor() { return agentRosterCursor; },
|
|
11722
|
+
set agentRosterCursor(v: number) { agentRosterCursor = v; },
|
|
11723
|
+
get agentCursorLockedId() { return agentCursorLockedId; },
|
|
11724
|
+
set agentCursorLockedId(v: string) { agentCursorLockedId = v; },
|
|
11725
|
+
get agentCursorManual() { return agentCursorManual; },
|
|
11726
|
+
set agentCursorManual(v: boolean) { agentCursorManual = v; },
|
|
11727
|
+
get chatScrollOffset() { return chatScrollOffset; },
|
|
11728
|
+
set chatScrollOffset(v: number) { chatScrollOffset = v; },
|
|
11729
|
+
get chatLines() { return chatLines; },
|
|
11730
|
+
},
|
|
11731
|
+
display,
|
|
11732
|
+
agentSurfaceStore,
|
|
11733
|
+
workingDir,
|
|
11734
|
+
dockedPreview,
|
|
11735
|
+
transferFeedbackRuntime,
|
|
11736
|
+
finderFeedbackRuntime,
|
|
11737
|
+
currentThemeTokens,
|
|
11738
|
+
currentWorkspaceOwnerId,
|
|
11739
|
+
activeBrowserWidgetId,
|
|
11740
|
+
resolveBrowserStateById,
|
|
11741
|
+
fmtBytes,
|
|
11742
|
+
runTransfer,
|
|
11743
|
+
closeAgentRosterSearch,
|
|
11744
|
+
pushDebugLine,
|
|
11745
|
+
refreshRemotePreviewBridge,
|
|
11746
|
+
refreshWorkingDirPreview,
|
|
11747
|
+
setWorkingFocus,
|
|
11748
|
+
startRemoteModeBridge,
|
|
11749
|
+
draw: () => draw(),
|
|
11750
|
+
});
|
|
11621
11751
|
|
|
11622
11752
|
function closeAgentRosterSearch(): void {
|
|
11623
11753
|
if (agentSearchHandle) { agentSearchHandle.dispose(); agentSearchHandle = null; }
|
|
@@ -11625,75 +11755,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11625
11755
|
draw();
|
|
11626
11756
|
}
|
|
11627
11757
|
|
|
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
11758
|
async function runTransfer(
|
|
11698
11759
|
target: TransferTarget,
|
|
11699
11760
|
files: Array<{ localPath: string; size?: number }>,
|
|
@@ -11720,465 +11781,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11720
11781
|
}
|
|
11721
11782
|
}
|
|
11722
11783
|
|
|
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
11784
|
// FU-1 — register ssh-picker / finder-picker openers as display
|
|
12183
11785
|
// key bindings. Moves the dashboard hard branches onto the same
|
|
12184
11786
|
// coordinator dispatch path the rest of the modals / bindings use.
|
|
@@ -12864,18 +12466,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
12864
12466
|
submit?: (request?: string | TextInputExternalSubmitRequest) => void;
|
|
12865
12467
|
getBufferText?: () => string;
|
|
12866
12468
|
} = { repaint: () => {} };
|
|
12867
|
-
repaintPromptAfterRender = () => {
|
|
12868
|
-
if (shouldSuppressDashboardBottomArea()) {
|
|
12869
|
-
display.setCursor(null);
|
|
12870
|
-
return;
|
|
12871
|
-
}
|
|
12872
|
-
promptCtl.repaint();
|
|
12873
|
-
};
|
|
12874
|
-
// V3 note: repaintPromptAfterRender receives {force} from the
|
|
12875
|
-
// coordinator hook but promptCtl.repaint() has no diff state of its
|
|
12876
|
-
// own — the main render's force=true already did eraseDown before
|
|
12877
|
-
// this runs, so prompt rows are clean. Forwarding force is a no-op
|
|
12878
|
-
// today but the signature is there for future direct-paint callers.
|
|
12879
12469
|
|
|
12880
12470
|
// Terminal resize wiring — CAPABILITIES-display.md §1.3 defines the
|
|
12881
12471
|
// coordinator entry triplet as "readKey / mouse / resize" but the
|
|
@@ -12884,13 +12474,36 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
12884
12474
|
// resize to a forced dashboard redraw plus the prompt repaint hook;
|
|
12885
12475
|
// the hook is a no-op while input is idle so the call is always
|
|
12886
12476
|
// safe. Dispose on process exit to keep the listener set clean.
|
|
12887
|
-
|
|
12477
|
+
// TUI-PICKER-RESIZE-SHRINK (#76) — the forced coordinator frame
|
|
12478
|
+
// re-emits open overlays (slash picker) AFTER the prompt chrome, so
|
|
12479
|
+
// the divider repaint cannot erase the picker's bottom border.
|
|
12480
|
+
const disposeResizeListener = installResizeListener({
|
|
12481
|
+
draw: (opts) => draw(opts),
|
|
12482
|
+
promptCtl,
|
|
12483
|
+
requestOverlayFrame: () => display.requestRender({ force: true }),
|
|
12484
|
+
});
|
|
12888
12485
|
process.on('exit', disposeResizeListener);
|
|
12889
12486
|
// 외부 주입(`pty text`)을 사용자 타이핑과 섞지 않고 별도 대기열로 — 결함 #6(10-14).
|
|
12487
|
+
let externalInputQueueNotice: string | null = null;
|
|
12488
|
+
const setExternalInputQueueNotice = (line: string | null): void => {
|
|
12489
|
+
const changed = updateDashboardExternalInputQueueNotice(
|
|
12490
|
+
chatLines,
|
|
12491
|
+
externalInputQueueNotice,
|
|
12492
|
+
line,
|
|
12493
|
+
(notice) => C.muted(` ${notice}`),
|
|
12494
|
+
);
|
|
12495
|
+
externalInputQueueNotice = line;
|
|
12496
|
+
if (changed) {
|
|
12497
|
+
chatScrollOffset = -1;
|
|
12498
|
+
draw();
|
|
12499
|
+
}
|
|
12500
|
+
};
|
|
12890
12501
|
const disposeExternalInputHost = registerExternalInputHost(createDashboardExternalInputHost({
|
|
12891
12502
|
isTurnActive: () => streamingInFlight,
|
|
12503
|
+
getTurnStop: () => currentTurnExternalStop,
|
|
12892
12504
|
promptCtl,
|
|
12893
12505
|
pushNotice: (line) => { chatLines.push(C.muted(` ${line}`)); chatScrollOffset = -1; draw(); },
|
|
12506
|
+
setQueueNotice: setExternalInputQueueNotice,
|
|
12894
12507
|
}));
|
|
12895
12508
|
process.on('exit', disposeExternalInputHost);
|
|
12896
12509
|
|
|
@@ -13486,6 +13099,17 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
13486
13099
|
try { draw(); } catch { /* TUI torn down */ }
|
|
13487
13100
|
});
|
|
13488
13101
|
process.once('beforeExit', () => agentDoneRedraw.dispose());
|
|
13102
|
+
// ⭐ TUI-AGENT-IDLE-WAKE — 배경 자식(⊕ RunShell bg) 완료 큐의 «유일한» 구독(onEnqueue 구독 0 → 1).
|
|
13103
|
+
// 채팅 한 줄 «✓ 위임 끝: <이름> — 다음 턴에 결과» · 결과는 다음 턴 preamble 이 싣는다.
|
|
13104
|
+
// ⛔ 입력칸·injectKey·pty 주입 0(대표 비침투). dedupe·수명은 모듈, 여기선 배선만.
|
|
13105
|
+
const agentWake = installAgentWake({
|
|
13106
|
+
subscribe: (listener) => globalTaskNotificationQueue.onEnqueue(listener),
|
|
13107
|
+
// 그리기 실패는 삼키지 않는다 — 모듈이 잡아 notified{shown:false} 로 남긴다.
|
|
13108
|
+
notify: createDashboardAgentWakeNotify((line) => { chatLines.push(C.muted(line)); }, () => draw()),
|
|
13109
|
+
// 조각 2 — 자리 TUI ∧ 유휴면 H2 문으로 다음 턴(origin child-complete) · 판정은 모듈.
|
|
13110
|
+
openTurn: { startTurnIfIdle: (text, opts) => internalTurnGate.startTurnIfIdle(text, opts), getCwd: () => workingDir.cwd, isPending: (taskId) => globalTaskNotificationQueue.has(taskId) },
|
|
13111
|
+
});
|
|
13112
|
+
disposeDashboardAgentWake = () => agentWake.dispose();
|
|
13489
13113
|
|
|
13490
13114
|
// PLAN-chat-hud-multi-surface-port-2026-05-13 §4 M5 — 1Hz ticker
|
|
13491
13115
|
// that pushes the current token-gauge + variant values onto the
|
|
@@ -15218,10 +14842,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15218
14842
|
userText,
|
|
15219
14843
|
hostTools: pluginHost.hostLLMTools(),
|
|
15220
14844
|
runtimeTools: runtime.listToolRuntimes('tui'),
|
|
15221
|
-
pluginTools: pluginHost.activePluginLLMTools(),
|
|
15222
14845
|
optionalTools: acpTurnRef.optionalSpecs,
|
|
15223
14846
|
surface: acpTurnRef.turnProfile?.surface,
|
|
15224
|
-
rich: false,
|
|
15225
14847
|
})
|
|
15226
14848
|
: [];
|
|
15227
14849
|
return [
|
|
@@ -15237,7 +14859,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15237
14859
|
// codex-only behavioral addendum (fix L-1) inside the
|
|
15238
14860
|
// universal preamble.
|
|
15239
14861
|
modelFamily: getModelFamily(inspectActiveProvider().model),
|
|
15240
|
-
rich: false,
|
|
15241
14862
|
enabledTools: tools.map(t => t.name),
|
|
15242
14863
|
seatBoot: true,
|
|
15243
14864
|
}),
|
|
@@ -15250,10 +14871,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15250
14871
|
userText,
|
|
15251
14872
|
hostTools: pluginHost.hostLLMTools(),
|
|
15252
14873
|
runtimeTools: runtime.listToolRuntimes('tui'),
|
|
15253
|
-
pluginTools: pluginHost.activePluginLLMTools(),
|
|
15254
14874
|
optionalTools: acpTurnRef.optionalSpecs,
|
|
15255
14875
|
surface: acpTurnRef.turnProfile?.surface,
|
|
15256
|
-
rich: false,
|
|
15257
14876
|
});
|
|
15258
14877
|
// ⭐ Remember this turn's catalog so a spawned sub-agent can inherit it.
|
|
15259
14878
|
//
|
|
@@ -15422,8 +15041,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15422
15041
|
// — the boot fell back to a fresh session. Surface the reason
|
|
15423
15042
|
// so the user knows they're on a different session than asked.
|
|
15424
15043
|
// - new (no resume requested): nothing extra to render.
|
|
15044
|
+
// 대표 #73 — 인자 없는 기동 = 새 세션. 이어받았는지는 이 값 하나로 판정한다(관측 ⊕ 자리 부트 카드).
|
|
15045
|
+
let tuiBootSessionStart: { origin: 'new' | 'resume'; reason: string; restoredSessionId?: string } = opts.resumeSessionId
|
|
15046
|
+
? { origin: 'new', reason: 'resume-failed' }
|
|
15047
|
+
: { origin: 'new', reason: 'default-new' };
|
|
15425
15048
|
if (dashboardAcpBootResult.mode === 'resumed' && dashboardAcpBootResult.sessionId) {
|
|
15426
15049
|
const sid = dashboardAcpBootResult.sessionId;
|
|
15050
|
+
tuiBootSessionStart = { origin: 'resume', reason: 'resume-daemon', restoredSessionId: sid };
|
|
15427
15051
|
chatLines.push(C.success(` ✓ resumed daemon session: ${sid}`));
|
|
15428
15052
|
// Best-effort REST history replay over the remote HTTP/WS endpoint.
|
|
15429
15053
|
if (opts.remote) {
|
|
@@ -15485,10 +15109,27 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15485
15109
|
if (!resume.resumed) {
|
|
15486
15110
|
chatLines.push(C.warning(` ⚠ resume failed (${resume.reason}); started new session`));
|
|
15487
15111
|
} else {
|
|
15112
|
+
tuiBootSessionStart = {
|
|
15113
|
+
origin: 'resume',
|
|
15114
|
+
reason: resume.repaired ? 'resume-local-repaired' : 'resume-local',
|
|
15115
|
+
restoredSessionId: resume.sessionId,
|
|
15116
|
+
};
|
|
15488
15117
|
chatLines.push(C.success(` ✓ resumed local session: ${resume.sessionId}`));
|
|
15118
|
+
if (resume.repaired) {
|
|
15119
|
+
chatLines.push(C.muted(` 깨진 도구 호출·결과 짝을 잘라 이어받았다(호출 ${resume.repaired.removedCalls} · 결과 ${resume.repaired.removedResults})`));
|
|
15120
|
+
}
|
|
15489
15121
|
chatLines.push(C.muted(` ${resume.turns} turns restored. Next turn continues this conversation.`));
|
|
15490
15122
|
}
|
|
15491
15123
|
}
|
|
15124
|
+
observeSessionStart({
|
|
15125
|
+
surface: 'tui',
|
|
15126
|
+
origin: tuiBootSessionStart.origin,
|
|
15127
|
+
reason: tuiBootSessionStart.reason,
|
|
15128
|
+
// sessionId = 이 대화가 이어받은(또는 새로 연) 대화 기록의 id · acpSessionId = 백엔드 ACP 세션 id(둘이 다를 수 있다).
|
|
15129
|
+
sessionId: tuiBootSessionStart.restoredSessionId ?? dashboardAcpSession.currentSessionId ?? null,
|
|
15130
|
+
acpSessionId: dashboardAcpSession.currentSessionId ?? null,
|
|
15131
|
+
resumeRequested: opts.resumeSessionId !== undefined,
|
|
15132
|
+
});
|
|
15492
15133
|
// 제안은 세션 작업 폴더 기준 — 시작 폴더 한 번이 아니라 폴더가 바뀔 때마다 다시 계산한다(IA1 리뷰 must-fix).
|
|
15493
15134
|
const nextProjectSuggestion = createProjectSuggester(suggestProjectForFolder);
|
|
15494
15135
|
const pushProjectSuggestion = (folder: string): void => {
|
|
@@ -15529,7 +15170,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15529
15170
|
installSeatSessionRecorder({ getCwd: () => getSessionCwd(), getHistory: () => chat.history });
|
|
15530
15171
|
const seatBoot = describeSeatBoot(getSessionCwd());
|
|
15531
15172
|
if (seatBoot) chatLines.push(C.muted(` 🪪 ${seatBoot.seat} 자리 · 안 읽은 우편 ${seatBoot.unread ?? '미관측'} — 부트 카드를 턴마다 싣는다`));
|
|
15532
|
-
|
|
15173
|
+
// 새 세션(이어받기 실패 포함)이면 자리 부트 카드로 시작한다 — 자리 맥락은 세션이 아니라 자리 문서가 싣는다.
|
|
15174
|
+
if (tuiBootSessionStart.origin === 'new') startSeatBootConversation({ reason: 'boot' });
|
|
15533
15175
|
tuiSeatAskTimer = setInterval(pollTuiSeatAnswers, 10_000);
|
|
15534
15176
|
tuiSeatAskTimer.unref();
|
|
15535
15177
|
pollTuiSeatAnswers();
|
|
@@ -16587,7 +16229,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
16587
16229
|
// 다음 inner-loop 반복이 남은 항목도 같은 순서로 제출한다.
|
|
16588
16230
|
// ⭐ 드레인 순서 자체는 `chat/turn-typeahead.ts` 가 소유한다 — 여기서 베끼면
|
|
16589
16231
|
// 테스트가 그 순서를 다시 베껴야 하고, 베낀 테스트는 배선 회귀를 못 잡는다.
|
|
16590
|
-
|
|
16232
|
+
internalTurnGate.noteTurnSettled();
|
|
16233
|
+
// «지금 밀어 넣기»로 끊은 턴은 «중단»이 아니다 — 복원하지 않고 큐 머리를 곧바로 제출한다.
|
|
16234
|
+
const interrupted = consumeTurnEndInterrupted(lastTurnFinalStatus, steerInterruptRef);
|
|
16591
16235
|
const queuedBeforeDrain = turnTypeaheadRef.state.queuedSubmissions.length;
|
|
16592
16236
|
// ⭐ 드레인 → 레지스트리 해제 → Enter 주입은 다리 모듈이 한 함수로 한다(시험이 같은 함수를 문다).
|
|
16593
16237
|
const drained = handoffTurnTypeaheadAtTurnEnd(turnTypeaheadRef, nextInitial, { interrupted }, () => {
|
|
@@ -16619,6 +16263,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
16619
16263
|
// C-d-1 — 하단 슬롯 결정 뷰 활성 시 composer 페인트 정지(단일 소유자).
|
|
16620
16264
|
shouldPaint: () => !shouldSuppressDashboardBottomArea() && !bottomSlotModalActive(),
|
|
16621
16265
|
promptCtl,
|
|
16266
|
+
promptRowOwner,
|
|
16622
16267
|
setInputLines: (n) => dashboardState.setInputLines(n),
|
|
16623
16268
|
redraw: draw,
|
|
16624
16269
|
dispatchGlobalAction: runChatMainGlobalAction,
|
|
@@ -16632,6 +16277,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
16632
16277
|
: '/quit /clear (run `elanous setup` — no LLM provider available)',
|
|
16633
16278
|
debugLog: debug.enabled ? (event, label, payload) => debug.log(event, label, payload) : undefined,
|
|
16634
16279
|
textInputOpts: {
|
|
16280
|
+
internalTurnYield: internalTurnYieldPort,
|
|
16635
16281
|
onScrollKey: (name) => {
|
|
16636
16282
|
const { rows } = termSize();
|
|
16637
16283
|
const viewport = computePromptFrameLogViewportBounds(
|
|
@@ -17073,6 +16719,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
17073
16719
|
}),
|
|
17074
16720
|
},
|
|
17075
16721
|
});
|
|
16722
|
+
// ⭐ H2 — 입력칸이 내부 턴 문에 «비켜 줬다». 꺼낸 요청을 같은 턴 실행 경로로 보낸다
|
|
16723
|
+
// (슬래시 해석·히스토리·사람 발화 에코 없음). 꺼낼 게 없으면 입력으로 되돌아간다.
|
|
16724
|
+
const consumed = consumeInternalTurnYield(internalTurnGate, input);
|
|
16725
|
+
if (consumed.kind === 'reenter') continue;
|
|
16726
|
+
const internalTurn: InternalTurnRequest | null = consumed.kind === 'internal' ? consumed.request : null;
|
|
16727
|
+
input = consumed.input;
|
|
17076
16728
|
|
|
17077
16729
|
const postTurn = resolveDashboardChatMainPostTurn(input, {
|
|
17078
16730
|
inputLines: dashboardState.getInputLines(),
|
|
@@ -17124,7 +16776,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
17124
16776
|
|
|
17125
16777
|
// Save to durable input history. Up/down reads the in-memory
|
|
17126
16778
|
// tail, while slash/LLM tools can search the full store.
|
|
17127
|
-
if (
|
|
16779
|
+
if (!internalTurn) internalTurnGate.noteHumanTurn();
|
|
16780
|
+
if (postTurn.historyRecord && !internalTurn) {
|
|
17128
16781
|
inputHistoryStore.record(postTurn.historyRecord);
|
|
17129
16782
|
refreshInputHistory();
|
|
17130
16783
|
}
|
|
@@ -17282,14 +16935,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
17282
16935
|
// resource_link blocks so dropped screenshots and PDFs flow
|
|
17283
16936
|
// naturally to the agent. debug.log gates the snapshot so
|
|
17284
16937
|
// off-state stays zero-cost.
|
|
17285
|
-
const submitIntent =
|
|
17286
|
-
|
|
17287
|
-
|
|
17288
|
-
|
|
16938
|
+
const submitIntent = internalTurn
|
|
16939
|
+
? resolveInternalTurnSubmitIntent(internalTurn, dashboardAcpSession.currentSessionId)
|
|
16940
|
+
: resolveDashboardHarnessSubmitIntent(input.text, {
|
|
16941
|
+
stickyBackend: dashboardAcpChat.getSticky(),
|
|
16942
|
+
source: input.externalSubmitSource,
|
|
16943
|
+
});
|
|
17289
16944
|
const cmdText = submitIntent.kind === 'control-turn'
|
|
17290
16945
|
? (submitIntent.commandText ?? '')
|
|
17291
16946
|
: submitIntent.text;
|
|
17292
|
-
if (submitIntent.kind === 'submit-turn' && parseSeatAsk(submitIntent.text)) {
|
|
16947
|
+
if (submitIntent.kind === 'submit-turn' && !internalTurn && parseSeatAsk(submitIntent.text)) {
|
|
17293
16948
|
try {
|
|
17294
16949
|
const receipt = await askSeat(submitIntent.text, { channel: 'tui', clientId: tuiSeatAskClientId },
|
|
17295
16950
|
ceoTaskDeps(getUserConfig(), null), tuiSeatAskDeps);
|
|
@@ -17959,6 +17614,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
17959
17614
|
? { url: opts.remote.url, token: opts.remote.token }
|
|
17960
17615
|
: null,
|
|
17961
17616
|
acpSwapTo: (sessionId, cwd) => dashboardAcpSession.swapTo(sessionId, cwd),
|
|
17617
|
+
acpRenew: async (cwd) => {
|
|
17618
|
+
const renewed = await dashboardAcpSession.renew(cwd);
|
|
17619
|
+
acpSessionIdRef.current = renewed;
|
|
17620
|
+
return renewed;
|
|
17621
|
+
},
|
|
17962
17622
|
},
|
|
17963
17623
|
shellSlash: {
|
|
17964
17624
|
slashRuntime: shellSlashRuntime,
|
|
@@ -18021,6 +17681,21 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18021
17681
|
setWorkingFocusPreview: () => { setWorkingFocus('preview', 'slash-term-move-preview'); },
|
|
18022
17682
|
nextTerminalVwSlotId: (terminalId) => `term-slot:${terminalId}`,
|
|
18023
17683
|
},
|
|
17684
|
+
// TUI-LOOP-COMMAND — /loop <간격> <프롬프트> · 쉬는 동안만 발사
|
|
17685
|
+
// (src/dashboard/loop-scheduler.ts · 판정: idle 발사 1 · busy 미룸 · stop 0).
|
|
17686
|
+
// idle/busy 신호는 대시보드 루프가 이미 쓰는 것을 그대로 쓴다 —
|
|
17687
|
+
// 스트리밍 턴(streamingInFlight · reportTuiTurn busy/idle) +
|
|
17688
|
+
// ACP 세션 바쁨(dashboardAcpChat.isBusy()). 프롬프트 제출은
|
|
17689
|
+
// 플러그인 submitDashboardText 와 같은 기존 제출 틈
|
|
17690
|
+
// (dispatchDashboardSubmitText)으로 낸다 — 새 제출 경로를 만들지 않는다.
|
|
17691
|
+
loopCommand: {
|
|
17692
|
+
isIdle: () => !streamingInFlight && !dashboardAcpChat.isBusy(),
|
|
17693
|
+
submitPrompt: (prompt: string) => {
|
|
17694
|
+
if (dispatchDashboardSubmitText) dispatchDashboardSubmitText(prompt);
|
|
17695
|
+
else chatLines.push(C.muted(' /loop: 입력 루프가 아직 제출을 받지 않는다 — 다음 틱을 기다린다'));
|
|
17696
|
+
},
|
|
17697
|
+
schedulerRef: dashboardLoopSchedulerRef,
|
|
17698
|
+
},
|
|
18024
17699
|
};
|
|
18025
17700
|
const slashOutcome = await dashboardSlashRegistry.dispatch(cmdLower, args, slashCtx);
|
|
18026
17701
|
if (debug.enabled) {
|
|
@@ -18468,7 +18143,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18468
18143
|
// pattern. Without this draw(), the echo sits in the chatLines
|
|
18469
18144
|
// array but doesn't paint until after the skill router's
|
|
18470
18145
|
// classifier LLM call returns (~1-2s of visual silence).
|
|
18471
|
-
|
|
18146
|
+
if (internalTurn) {
|
|
18147
|
+
chatLines.push(C.muted(` ${formatInternalTurnBanner(internalTurn)}`));
|
|
18148
|
+
chatScrollOffset = -1;
|
|
18149
|
+
} else {
|
|
18150
|
+
echoSubmittedUserText(tok.text);
|
|
18151
|
+
}
|
|
18472
18152
|
if (queuedTurnEndText !== undefined && input.text === queuedTurnEndText) {
|
|
18473
18153
|
debug.log('dashboard.turn-typeahead', 'echoed', { path: 'turn-end', length: queuedTurnEndText.length });
|
|
18474
18154
|
}
|
|
@@ -18686,7 +18366,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18686
18366
|
let latestTurnUsage: import('../prompt-cache/types.js').LLMUsage | undefined;
|
|
18687
18367
|
const runPlainSubmitTurn = (submit: PlainTurnSubmit) => runDashboardChatMainPlainTurn({
|
|
18688
18368
|
// ⛔⭐ 관측 심을 «실제로» 넘긴다 — 타입만 선택적으로 넓히면 조용히 안 온다(`F12`).
|
|
18689
|
-
debugLog: (category, event, payload) => { try { debug.log(category, event, payload); } catch { /* fail-soft */ } },
|
|
18369
|
+
debugLog: (category, event, payload, opts) => { try { debug.log(category, event, payload, opts); } catch { /* fail-soft */ } },
|
|
18690
18370
|
userText: tok.text,
|
|
18691
18371
|
intent: submit.intent,
|
|
18692
18372
|
contextText: ctx,
|
|
@@ -18759,10 +18439,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18759
18439
|
userText: args.userText,
|
|
18760
18440
|
hostTools: pluginHost.hostLLMTools(),
|
|
18761
18441
|
runtimeTools: runtime.listToolRuntimes('tui'),
|
|
18762
|
-
pluginTools: pluginHost.activePluginLLMTools(),
|
|
18763
18442
|
optionalTools: acpTurnRef.optionalSpecs,
|
|
18764
18443
|
surface: acpTurnRef.turnProfile?.surface,
|
|
18765
|
-
rich: false,
|
|
18766
18444
|
})
|
|
18767
18445
|
: [];
|
|
18768
18446
|
return [
|
|
@@ -18772,7 +18450,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18772
18450
|
cwd: args.cwd,
|
|
18773
18451
|
turnProfile: args.turnProfile as SessionTurnProfile,
|
|
18774
18452
|
userConfig: args.userConfig,
|
|
18775
|
-
rich: false,
|
|
18776
18453
|
enabledTools: tools.map(t => t.name),
|
|
18777
18454
|
seatBoot: true,
|
|
18778
18455
|
}),
|
|
@@ -18897,6 +18574,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18897
18574
|
},
|
|
18898
18575
|
runTurnUsageRuntime: (usage) => {
|
|
18899
18576
|
latestTurnUsage = usage as import('../prompt-cache/types.js').LLMUsage;
|
|
18577
|
+
// 이번 턴의 «실제» 공급자·모델(폴백이면 넘어간 쪽) — 완료 배지가 «A → B» 로 보인다(TUI-MODEL-BADGE-ACTUAL).
|
|
18578
|
+
// 원격 데몬 세션은 적지 않는다(결정이 데몬 쪽 · 후속 칸 TUI-MODEL-BADGE-REMOTE).
|
|
18579
|
+
recordDashboardTurnExecution(usage as { model?: string; routeProvider?: string }, { remote: daemonAttached });
|
|
18900
18580
|
runDashboardTurnUsageRuntime({
|
|
18901
18581
|
// The plain-turn pipeline threads usage as `unknown` for
|
|
18902
18582
|
// decoupling; at this consumer it is the concrete LLM usage
|
|
@@ -19046,7 +18726,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19046
18726
|
const { resumeGoalFromUserFollowUp } = await import('../goals/chat-loop-bridge.js');
|
|
19047
18727
|
const { hydrateGoalFromSnapshot } = await import('../goals/registry.js');
|
|
19048
18728
|
hydrateGoalFromSnapshot();
|
|
19049
|
-
|
|
18729
|
+
// ⛔ H2 — 내부 턴은 사람의 «정정·재개» 발화가 아니다: 멈춘 /goal 을 되살리지 않는다.
|
|
18730
|
+
const followUpAction = tok.text.startsWith('/goal') || internalTurn
|
|
19050
18731
|
? { kind: 'no-loop' as const }
|
|
19051
18732
|
: resumeGoalFromUserFollowUp(tok.text);
|
|
19052
18733
|
let goalLoopUserText: string | null = followUpAction.kind === 'continue'
|
|
@@ -19233,5 +18914,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19233
18914
|
} finally {
|
|
19234
18915
|
setSelfImplementSoftStopSpaceObserver(null);
|
|
19235
18916
|
if (tuiSeatAskTimer) clearInterval(tuiSeatAskTimer);
|
|
18917
|
+
wakeupScheduler.dispose();
|
|
19236
18918
|
}
|
|
19237
18919
|
}
|