elanous 0.2.28 → 0.2.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{y1ltt7oCZxVpC6zVPhZ2O → LSXfY0u9QtfDbejY0Ukf_}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{ab4d57c2.e90bc2cb31949045.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/{5492-e05e26a033ba80d2.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/1654.32de4b324981a188.js +23 -0
- package/apps/pwa/out/_next/static/chunks/1834-50625904996a8fa8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +149 -0
- package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +41 -0
- package/apps/pwa/out/_next/static/chunks/2001.6c7e0fd02190886c.js +1 -0
- 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/{8578.dff22d988cc2b106.js → 2213.2ccbada92c33ea0f.js} +3 -3
- 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/{5081.a240b63fad28663a.js → 2871.0bca6137343b3f76.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2898.fd781bc7cf01a7b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2940-619bfd8ef337c611.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2846.d6a433b8663819a5.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/{f4dfbfc0.a69fb12f854882aa.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/3541-ce3a5424ed5ad7f1.js +1 -0
- 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/{3143.257997a5f1ed79df.js → 3902.e1e71aaac850a045.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{16e57f67.3ea19e893461e611.js → 409f1e57.d0649fc47691ebfe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1146.80d15ea7c6e6ee95.js → 4111.2bfedbb233ad206b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1516.83dd2c1b86b4e507.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/{9938.b23e4d4dfa0114cd.js → 4999.c064a4000c2a9e94.js} +2 -2
- 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/{1157.f08bf2b70915e176.js → 5304.04d24756f242a094.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{7752.5cbdd0b433c54af9.js → 5307.60f2209e809bd627.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9917-9e26abc5f7772735.js → 5458-16cd7a2b51b74c8c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6196.34d6b229079e9e28.js → 5525.86debf231e181813.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4593.10f5b9bd702d4a6c.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/{7483.ba01c6c209b81bba.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/{9974.e77b4ff4884557b3.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/{3309.364573ceedbdfd5f.js → 6323.8672b4817a306928.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{94.4cfed21c0b781a06.js → 6433.fc573af22bbc9ab7.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{949.1e7d45ccb71580aa.js → 6517.4147878ac06d584d.js} +2 -2
- 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/{2528.3868070ef1be69f9.js → 6767.ea607cf0c20a25af.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +93 -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/{1743.563d3168e95825ef.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/{5408.377c8c543635b81f.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/{65ebff95.081daeff622d238e.js → 7a1887c4.f36b88fba31da8fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2697.76ca2ac3efef6f3b.js → 8018.a32663f04a6fc5f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8097-92f87bd108322f66.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/9138.b6e80031afb1d973.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4204.a409ab6250c0f6a8.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/{5785-ea8d2c633b6688e3.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/9398-6aed28f0a96d0d6f.js +1 -0
- 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/997ce8a5-2ad501927003b41b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9311.24c8f5ea793253dd.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-bf3634a117322df7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-3535a4a69f1fafaa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b9f51447f757fbf2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-fa46d4588e8442c2.js → page-2ebb8fd59b9461f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a2fc4e49a4fa4ba8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-5a503541353e60a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-6025101384e361a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f4b630cc8b5273cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-a645f00a06483e9a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-61ac22cbab1ee4ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e9bab7413b028b28.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c992eafe028b642e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-e547e6ce55f1fb33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d27e1b9850fd54bf.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-805ac2e137b7a121.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-0fec29c7ba01894b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-339a07410cd6f36b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-ced0bcfc6542b3fc.js → page-349f2752d8cd37ad.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-d5c6dcf31c25cecc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2fd93ed8c9f16580.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9b564dbff0fefc35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-62f4475d3c38fb9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-814ba51385225776.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-397d479626e5a459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-af1bba4e3251b728.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3ad3be5e7d3fdce9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-f219ccdbc0bf27df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4da93ad35d4a8d28.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-456666766e5ce6fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-7404e377e8adb8c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-14456240ed2a36ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-3a658d5fa6d16a25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-0157de6e85f8bb6b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-cb249e81ed339318.js → page-0a07363b130182a1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-841d73d4ac44ed49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6f43a3d1146e6377.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-521ee23ac8c0abfa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-a9992ee285efaa83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-3d48e85fa89ec04f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d2ced71132fac9b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-f08358bdf251adb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-233c71cd5f68f0d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-70151d78de2f375e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-dcac33a3e2cb72c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-149dc1caba411bb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-0448145ccd5c45c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9cb0c444903da65d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-17e504c396e0e389.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6def8a8e.c81f9ca41028eccf.js → b313b242.cb1265eea908db61.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{e87d89b9.2e71c0ae475d28a1.js → e5012ffe.f727e23c231f4342.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{e117f801.e92bf10242653497.js → ebc7d829.264a0bcb6c750b50.js} +1 -1
- 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/{72becfd42807b50d.css → 3f5fc6f96e2cf7b2.css} +1 -1
- 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 +23 -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/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 +15 -7
- package/graphs/units/recipes.yaml +3 -0
- package/graphs/units/release-gate-recheck.yaml +15 -0
- package/package.json +2 -1
- package/plugins/agent-team/agents/research.md +1 -0
- package/scripts/check-native-tool-wiring.ts +3 -2
- package/scripts/ci-android-unit-tests.ts +16 -0
- package/scripts/ci-typecheck-changed.ts +40 -11
- package/scripts/coord-channel-watch.sh +45 -19
- package/scripts/coord-role-pack.ts +222 -0
- 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/lib/morning-report-options.ts +41 -0
- package/scripts/measure-goal-grounding-corpus.ts +2 -1
- package/scripts/pr-land-extra-remote-gates.ts +49 -0
- package/scripts/ratchets/test-interference-patterns.allowlist.txt +257 -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-batch-safe.ts +23 -0
- package/scripts/release-loop/gate-closure.ts +12 -8
- package/scripts/release-loop/gate-diff.ts +1 -1
- package/scripts/release-loop/gate-history.ts +422 -0
- package/scripts/release-loop/gate-host-mirror.ts +66 -0
- package/scripts/release-loop/gate-node.ts +806 -94
- 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-triage-node.ts +190 -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/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 +51 -0
- package/scripts/release-loop/tui-node-checks.ts +23 -0
- package/scripts/release-loop/tui-sim-node.ts +3 -2
- package/scripts/release-loop/unattended-release.ts +16 -0
- package/scripts/release-loop/version-node.ts +62 -4
- package/scripts/test-deterministic.ts +46 -73
- 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/scripts/usage-rollup.ts +11 -3
- package/src/acp/client.ts +4 -1
- package/src/acp/core-turn-bridge.ts +145 -24
- package/src/acp/elanous-extensions.ts +2 -0
- package/src/acp/server.ts +2 -0
- package/src/ad-pipeline/run.ts +4 -2
- package/src/agent/ref-grounding.ts +18 -0
- package/src/agent/runner.ts +169 -3
- package/src/agent/subagent-callable.ts +3 -2
- 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 +41 -21
- package/src/agent-substrate/self-review-cli.ts +30 -4
- package/src/autopilot/pr-manager.ts +62 -7
- package/src/briefing/brief-items.ts +24 -22
- package/src/budget/pty-usage-reemit.ts +7 -0
- package/src/budget/run-usage-rollup.ts +9 -0
- package/src/budget/usage-daily-store.ts +98 -0
- package/src/capture/tui-self-report.ts +37 -4
- package/src/chat/chat-cli.ts +10 -2
- package/src/chat/index.ts +107 -20
- package/src/chat/input-control-key.ts +5 -3
- package/src/chat/pickers/modal-runtime.ts +1 -0
- package/src/chat/pickers/modals.ts +3 -1
- package/src/chat/tool-interrupt-behavior.ts +38 -0
- package/src/chat/tool-render/dispatch.ts +5 -1
- package/src/chat/turn-typeahead.ts +191 -29
- package/src/cli/__fixtures__/agent-help-before/agent.txt +4 -2
- package/src/cli/agent-cli.ts +41 -13
- package/src/cli/config-audit.ts +123 -0
- package/src/cli/dashboard-name-flag.ts +48 -0
- package/src/cli/decisions-cli.ts +80 -6
- package/src/cli/doctor-cli.ts +80 -0
- package/src/cli/log-event-names.ts +19 -0
- package/src/cli/mcp-login.ts +21 -5
- package/src/cli/msg-cli.ts +133 -2
- package/src/cli/pr-cli.ts +546 -112
- package/src/cli/pr-land-heavy-remote.ts +233 -0
- package/src/cli/pr-land-overlap-evidence.ts +167 -14
- package/src/cli/pr-land-pod-shadow-job.ts +83 -0
- package/src/cli/pr-land-pod-shadow.ts +16 -3
- package/src/cli/pr-land-virtual-rebase.ts +108 -0
- package/src/cli/pty-takeover-cli.ts +125 -16
- package/src/cli/release-cli.ts +122 -14
- package/src/cli/remote-resolve.ts +2 -1
- package/src/cli/seat-cli.ts +164 -19
- package/src/cli/self-update.ts +5 -2
- package/src/cli/tasks-cli.ts +105 -21
- package/src/cli/usage-cli.ts +146 -12
- package/src/cli/usage-daily-cli.ts +84 -0
- package/src/context-bus/context-first.ts +13 -0
- package/src/context-bus/context-now.ts +95 -7
- package/src/context-bus/ops-events.ts +75 -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/auth-rejected-notice.ts +41 -0
- package/src/core-turn/run-core-turn.ts +14 -2
- package/src/core-turn/run-goal-loop.ts +239 -4
- package/src/core-turn/seat-turn-cap.ts +79 -0
- package/src/core-turn/short-question.ts +78 -13
- package/src/core-turn/types.ts +10 -0
- package/src/dashboard/acp-active-model.ts +40 -0
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/auto-compact-runtime.ts +2 -0
- package/src/dashboard/boot-banner.ts +80 -0
- package/src/dashboard/dashboard-status-lines.ts +11 -0
- package/src/dashboard/detail-viewer-runtime.ts +15 -2
- package/src/dashboard/event-loop-stall-monitor.ts +4 -0
- package/src/dashboard/goal-loop-thinking.ts +16 -0
- package/src/dashboard/hitl-boot.ts +9 -4
- package/src/dashboard/index.ts +448 -998
- package/src/dashboard/input/chat-main-entry-runtime.ts +2 -0
- package/src/dashboard/input/chat-main-log-actions.ts +4 -0
- package/src/dashboard/input/chat-main-log-command.ts +12 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +176 -4
- package/src/dashboard/input/chat-main-session.ts +14 -3
- package/src/dashboard/input/chat-main-slash-command.ts +7 -0
- package/src/dashboard/input/chat-main-turn.ts +14 -4
- package/src/dashboard/input/compact-prepass-recovery.ts +130 -0
- package/src/dashboard/input/dashboard-chord-actions.ts +10 -1
- package/src/dashboard/input/dashboard-chord-runtime.ts +2 -0
- package/src/dashboard/input/external-input-host.ts +71 -0
- package/src/dashboard/input/internal-turn.ts +261 -0
- package/src/dashboard/input/log-fold-binding.ts +18 -0
- package/src/dashboard/input/mouse-wiring.ts +17 -4
- package/src/dashboard/input/seat-autowake.ts +318 -0
- package/src/dashboard/input/turn-steer-bridge.ts +164 -0
- package/src/dashboard/log-click-runtime.ts +5 -3
- package/src/dashboard/loop-scheduler.ts +72 -0
- package/src/dashboard/running-build-status.ts +74 -0
- package/src/dashboard/seat-boot-preamble.ts +131 -0
- package/src/dashboard/seat-local-instructions.ts +305 -0
- package/src/dashboard/skill-runtime.ts +2 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +220 -1203
- package/src/dashboard/slash-runtime/help-from-registry.ts +2 -1
- package/src/dashboard/slash-runtime/index.ts +13 -0
- package/src/dashboard/slash-runtime/model-choices.ts +35 -0
- package/src/dashboard/term-slash-runtime.ts +15 -0
- package/src/dashboard/turn-busy-teardown.ts +50 -0
- package/src/dashboard/turn-preamble.ts +30 -2
- package/src/dashboard/turn-session-layers.ts +176 -0
- package/src/dashboard/turn-stream-formatter.ts +14 -6
- package/src/dashboard/turn-stream-presentation-applier.ts +8 -0
- package/src/decisions/decision-ledger.ts +45 -4
- package/src/discord-self-message.ts +4 -4
- package/src/domains/release-tool.ts +26 -10
- package/src/domains/surface-events.ts +4 -1
- package/src/domains/watch-default-scheduler.ts +8 -9
- package/src/field-note/transcribe.ts +17 -0
- package/src/fold-stack.ts +24 -1
- package/src/git-fs/dirty.ts +32 -4
- package/src/git-fs/index.ts +43 -1
- 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 +2 -0
- package/src/grok/credential.ts +26 -0
- package/src/harness/harness-clean.ts +2 -1
- package/src/harness/harness-cli-command.ts +52 -5
- package/src/harness/harness-gc.ts +34 -6
- package/src/harness/harness-incidents.ts +57 -20
- package/src/harness/harness-pod-dispatch.ts +87 -10
- package/src/harness/harness-queue.ts +53 -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 +166 -26
- package/src/input/external-input-queue.ts +251 -0
- package/src/intake-plane/slash.ts +14 -3
- package/src/interactive-terminal-modal.ts +13 -0
- package/src/keybindings.ts +4 -1
- package/src/llm/route-decision.ts +25 -0
- package/src/llm/tool-loop-steered-rejection.ts +33 -0
- package/src/llm-stream-retry-display.ts +59 -0
- package/src/llm.ts +391 -74
- package/src/log-entry.ts +3 -3
- package/src/log-pane/search.ts +29 -4
- package/src/loops/agent-reply.ts +256 -0
- package/src/loops/budget.ts +37 -7
- package/src/loops/consensus.ts +348 -0
- package/src/loops/loop-cli.ts +3 -2
- package/src/loops/msg-address.ts +57 -0
- package/src/loops/msg-wake.ts +484 -0
- package/src/loops/observe.ts +19 -3
- package/src/loops/orchestrator/inbox.ts +60 -3
- package/src/loops/orchestrator/tick.ts +80 -6
- package/src/loops/orchestrator/traffic.ts +64 -1
- package/src/loops/registry.ts +72 -4
- package/src/market/signed-index.ts +14 -0
- package/src/mcp/mcp-oauth.ts +54 -5
- package/src/memory.ts +20 -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/native-tool-catalog.ts +125 -0
- package/src/nexus/DAEMON-THIN-inventory-2026-10-15.md +122 -0
- package/src/nexus/api/http-server.ts +59 -15
- package/src/nexus/api/loop-resources.ts +19 -3
- package/src/nexus/api/ops-api.ts +13 -0
- package/src/nexus/api/project-autopsy.ts +127 -0
- package/src/nexus/api/terminals.ts +3 -0
- package/src/nexus/api/workflow-approvals.ts +2 -0
- package/src/nexus/daemon-thin-inventory.md +37 -0
- package/src/nexus/index.ts +70 -0
- package/src/nexus/onboarding/first-boot-wizard.ts +56 -2
- package/src/nexus/state/state.ts +1 -0
- package/src/oauth/codex-account-store.ts +15 -3
- package/src/oauth/codex-billing-route.ts +31 -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-collect.ts +19 -2
- package/src/ops/resources.ts +82 -0
- package/src/panes/log-pane.ts +67 -8
- package/src/persona/presets.ts +44 -0
- package/src/plugins/maker/connector-maker.ts +41 -0
- package/src/plugins/maker/skill-maker.ts +42 -0
- package/src/plugins/maker/wizard-generate.ts +6 -4
- package/src/pr-tool/census.ts +288 -0
- package/src/preview/terminal.ts +14 -1
- package/src/prompt-cache/types.ts +2 -0
- package/src/prompt-enhance/enhance.ts +24 -8
- package/src/prompt-library/chat-conciseness.ts +13 -0
- package/src/prompt-library/universal-preamble.ts +283 -61
- package/src/pty-shell/pty-control-ipc.ts +4 -1
- package/src/pty-shell/pty-manifest.ts +77 -5
- package/src/pty-shell/registry.ts +19 -2
- package/src/pty-shell/tui-run-state.ts +253 -0
- 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 +32 -4
- 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/render.ts +5 -1
- 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/seat-report.ts +42 -2
- package/src/seat-loop/traffic-signal.ts +3 -0
- package/src/self-dev/ask-launch-flow.ts +24 -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 +24 -9
- 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 +300 -24
- package/src/self-dev/run-types.ts +20 -0
- package/src/self-dev/shard-aggregate.ts +15 -2
- package/src/self-dev/shard-plan.ts +47 -0
- package/src/self-dev/shard-run.ts +135 -12
- package/src/self-dev/spawn-lane-files.ts +19 -0
- package/src/self-health/signatures.ts +132 -0
- package/src/self-implement/app-api-budget.ts +51 -11
- package/src/self-implement/config-gate.ts +2 -1
- package/src/self-implement/doctor-visit.ts +118 -0
- package/src/self-implement/expose-gate.ts +2 -1
- package/src/self-implement/gate-artifacts.ts +405 -0
- package/src/self-implement/gate-cli.ts +212 -18
- package/src/self-implement/gate-remote.ts +110 -25
- package/src/self-implement/gate-scenario.ts +85 -0
- package/src/self-implement/goal-author-decompose.ts +80 -16
- package/src/self-implement/goal-author.ts +270 -76
- package/src/self-implement/goal-authoring-grounding.ts +1 -1
- package/src/self-implement/goal-file-name.ts +65 -0
- package/src/self-implement/graph-authority.ts +38 -6
- package/src/self-implement/graph-overlay-condition.ts +92 -1
- package/src/self-implement/ground-goal.ts +2 -1
- package/src/self-implement/heal-triage.ts +79 -0
- package/src/self-implement/host-regate.ts +74 -2
- package/src/self-implement/orchestrator.ts +14 -123
- package/src/self-implement/pr-title.ts +123 -0
- package/src/self-implement/review-mustfix-class.ts +115 -0
- package/src/self-implement/run-checkpoint.ts +235 -0
- package/src/self-implement/run-ledger.ts +266 -17
- package/src/self-implement/run-stop.ts +4 -3
- package/src/self-implement/run-ttl.ts +28 -4
- package/src/self-implement/running-runs.ts +10 -6
- package/src/self-implement/seams.ts +2 -0
- package/src/self-implement/self-implement-runtime.ts +32 -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 +81 -11
- package/src/session/session-start.ts +224 -0
- package/src/session-runtime/index.ts +7 -1
- package/src/session-runtime/parallel-dispatch.ts +22 -5
- package/src/skills/tools/dev-harness.ts +6 -4
- package/src/skills/tools/ops-ledger.ts +707 -0
- package/src/skills/tools/persistent-grounding.ts +47 -7
- package/src/skills/tools/shell.ts +117 -1
- package/src/status/bar.ts +32 -2
- package/src/status/status-provider-info.ts +58 -0
- package/src/steward/resync-tick.ts +114 -23
- package/src/steward/ta-repair-event-collect.ts +158 -0
- 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 +93 -13
- package/src/task-agent/remote-rejudge-bind.ts +62 -13
- package/src/task-agent/repair-cycle.ts +58 -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 +158 -5
- package/src/task-agent/train-landing.ts +77 -0
- package/src/task-agent/work-order.ts +104 -0
- package/src/task-orchestrator/surfaces/kubectl-transient-retry.ts +51 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +138 -22
- 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 +122 -15
- package/src/task-orchestrator/surfaces/pod-lease.ts +20 -6
- package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +169 -0
- package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +103 -0
- package/src/task-orchestrator/surfaces/pod-result-marker-wait.ts +33 -0
- package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +63 -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-protocol.ts +153 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +208 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +237 -46
- package/src/telegram.ts +2 -4
- package/src/terminal/session-registry.ts +6 -0
- package/src/thinking-line.ts +12 -3
- package/src/tool-runtime/index.ts +4 -0
- package/src/tool-runtime/ops-ledger-runtimes.ts +84 -0
- package/src/tool-runtime/ref-consult-runtime.ts +17 -2
- package/src/tool-runtime/run-shell-runtime.ts +4 -1
- package/src/tool-runtime/self-cognition-runtimes.ts +5 -1
- package/src/tui/input/pickers.ts +732 -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 +53 -0
- package/src/user-config/gemini-server-tools.ts +9 -0
- package/src/user-config.ts +106 -21
- 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/fake-llm.ts +29 -0
- package/src/ux-sim/headless-tui.ts +72 -0
- package/src/ux-sim/in-process-cli.ts +176 -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/1027.a0f04663d24c2779.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +0 -1
- package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +0 -36
- package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1454-29d1d773d5996a49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +0 -27
- package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +0 -1
- package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +0 -32
- package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3259-86f3be56e43eaf6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +0 -287
- package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4084.15ecaf4240bd9e77.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +0 -8
- package/apps/pwa/out/_next/static/chunks/4565-67e4c620597965c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +0 -104
- package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +0 -149
- package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +0 -1
- package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/599-56f2c0a5cc4a7295.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +0 -23
- package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +0 -77
- package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +0 -64
- package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +0 -62
- package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-1e5fce59500a2cc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +0 -226
- package/apps/pwa/out/_next/static/chunks/83a80514-b897b64df43ce108.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +0 -93
- package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +0 -24
- package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +0 -115
- package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +0 -41
- package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +0 -74
- package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/973cbf1b.3bbcf2b41548eca7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-256ffd365d32e100.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e0de78ae7b99f01c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4c741eb760f2f77c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-e9ba98f5f1440a21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-2f6f303fa114ec24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-bb1f05cd51c38944.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f736324afa2117c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6f131b51193a36f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-435f5e2faf02bc01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-497e0eedf20e7890.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a8d28fdf995e16ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-66a67eb19ebd976b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c71267e7681da982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-781cfc5114e097da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-478157214db6a023.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-86141f12cf090b7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-7256c73bb1c36751.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-979d318a534e92da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-601fb77340e63c9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-11bf9e5725a72c22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +0 -1
- package/src/dashboard/bench-slash-runtime.ts +0 -40
- package/src/dashboard/input/search-modal-router.ts +0 -9
- package/src/dashboard/modals/pane-key-router.ts +0 -27
- package/src/dashboard/view-slash-runtime.ts +0 -48
- /package/apps/pwa/out/_next/static/{y1ltt7oCZxVpC6zVPhZ2O → LSXfY0u9QtfDbejY0Ukf_}/_ssgManifest.js +0 -0
package/src/dashboard/index.ts
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { createRunningBuildStatus } from './running-build-status.js';
|
|
10
|
+
import { reportTuiTurn } from '../pty-shell/tui-run-state.js';
|
|
11
|
+
import { logTurnThrew, runTurnTeardownStep } from './turn-busy-teardown.js';
|
|
9
12
|
import { createStaleInstallCheck, formatStaleInstallNotice, readCurrentInstalledCommit, readDashboardBootCommit, STALE_INSTALL_CHECK_INTERVAL_MS, versionedDashboardInstallDir } from './stale-install-check.js';
|
|
10
13
|
import { spawn, spawnSync } from 'node:child_process';
|
|
11
14
|
import { $ } from 'bun';
|
|
12
|
-
import { LOCAL_SKILLS_DIR,
|
|
15
|
+
import { LOCAL_SKILLS_DIR, DATA_DIR, OBSIDIAN_VAULT } from '../config.js';
|
|
13
16
|
import { dispatchDashboardSessionRuntimeTool } from './session-runtime-dispatch.js';
|
|
14
17
|
import { nextChatScrollOffset } from '../chat/log-scroll-key.js';
|
|
15
18
|
import { suggestProjectForFolder } from '../project/project-store.js';
|
|
@@ -160,8 +163,8 @@ function loadLastSelection(): LastSelection | null {
|
|
|
160
163
|
}
|
|
161
164
|
import { getLocalSkills, executeSync } from '../sync.js';
|
|
162
165
|
import { computeDiff } from '../smart.js';
|
|
163
|
-
import {
|
|
164
|
-
import { C, ICONS, ansi, initTui, closeTui, setExitNotice, readKey, setKeyTracer, render,
|
|
166
|
+
import { summarizeDiff, isAnalyzerAvailable } from '../grok.js';
|
|
167
|
+
import { C, ICONS, ansi, initTui, closeTui, setExitNotice, readKey, setKeyTracer, render, termSize, pad, stripAnsi, hLine, showHelp, invalidateRenderCacheRow, wrapAnsiByWidth, injectKey } from '../tui.js';
|
|
165
168
|
import { paintCursor, type CursorState } from '../display/cursor-state.js';
|
|
166
169
|
import type { CursorDecision } from '../display/cursor-owner.js';
|
|
167
170
|
import type { Key, RenderOptions } from '../tui.js';
|
|
@@ -169,7 +172,6 @@ import {
|
|
|
169
172
|
createTurnTypeaheadState,
|
|
170
173
|
applyTurnTypeaheadKey,
|
|
171
174
|
renderTurnTypeaheadQueueRow,
|
|
172
|
-
drainTurnTypeaheadOnce,
|
|
173
175
|
renderTurnTypeaheadEcho,
|
|
174
176
|
} from '../chat/turn-typeahead.js';
|
|
175
177
|
import { dispatchStreamingTurnTypeaheadSubmission } from './input/streaming-turn-typeahead.js';
|
|
@@ -180,13 +182,11 @@ import type { CoreTurnDispatchTool } from '../core-turn/index.js';
|
|
|
180
182
|
// ACP pair (see `bootDashboardAcpSession`), which internally routes
|
|
181
183
|
// to streamLLMWithTools via runCoreTurn.
|
|
182
184
|
import {
|
|
183
|
-
startSpinner,
|
|
184
185
|
createChatState,
|
|
185
186
|
buildContext,
|
|
186
187
|
formatResponse,
|
|
187
188
|
onEscAbort,
|
|
188
189
|
attachStreamingKeys,
|
|
189
|
-
SLASH_COMMANDS,
|
|
190
190
|
type InputResult,
|
|
191
191
|
type TextInputExternalSubmitRequest,
|
|
192
192
|
} from '../chat/index.js';
|
|
@@ -198,6 +198,7 @@ import {
|
|
|
198
198
|
} from '../chat/hud-segments-wire.js';
|
|
199
199
|
import { PreviewTerminal } from '../preview/terminal.js';
|
|
200
200
|
import { startThinking, startPinnedThinking, type ThinkingHandle } from '../thinking-line.js';
|
|
201
|
+
import { goalLoopIterationThinking } from './goal-loop-thinking.js';
|
|
201
202
|
import { debug, getAmbientSessionId } from '../debug/log.js';
|
|
202
203
|
import { startEventLoopStallMonitor, thinkingVerbFromFooter } from './event-loop-stall-monitor.js';
|
|
203
204
|
import { getPushcutClient } from '../pushcut/client.js';
|
|
@@ -220,11 +221,6 @@ import {
|
|
|
220
221
|
registerAction as inputCoreRegisterAction,
|
|
221
222
|
addDefaultBinding as inputCoreAddDefaultBinding,
|
|
222
223
|
initUserBindings as initInputCoreUserBindings,
|
|
223
|
-
readAuditTail,
|
|
224
|
-
isInputAuditEntry,
|
|
225
|
-
formatAuditEntry,
|
|
226
|
-
parseDuration as parseAuditDuration,
|
|
227
|
-
runRebindCommand,
|
|
228
224
|
type DispatchContext as InputCoreDispatchContext,
|
|
229
225
|
type MouseInputEvent as InputCoreMouseInputEvent,
|
|
230
226
|
type RouteCallbacks as InputCoreRouteCallbacks,
|
|
@@ -255,7 +251,6 @@ import { wireInputModeContextBridge } from '../input-core/mode-context-bridge.js
|
|
|
255
251
|
import { getDashboardContextKeyService } from './context/keys.js';
|
|
256
252
|
import {
|
|
257
253
|
getDashboardContextMenuRegistry,
|
|
258
|
-
handleForPill,
|
|
259
254
|
} from './context-menu/registry.js';
|
|
260
255
|
import { createTransientOverlayHost } from '../display/transient-overlay-host.js';
|
|
261
256
|
import { configureThemeIconsGetter } from '../theme/icons.js';
|
|
@@ -267,7 +262,6 @@ import {
|
|
|
267
262
|
} from '../log-pane/buffer-policy.js';
|
|
268
263
|
import { publishFocusContextKeysFromService } from './context/focus-bridge.js';
|
|
269
264
|
import {
|
|
270
|
-
modalBlocksBackground,
|
|
271
265
|
modalParticipatesInViewMode,
|
|
272
266
|
renderModalStack,
|
|
273
267
|
topBlockingForegroundModalSurface,
|
|
@@ -285,7 +279,6 @@ import {
|
|
|
285
279
|
debugWorkbenchIndexForTarget,
|
|
286
280
|
getDebugCompanionSpec,
|
|
287
281
|
listDebugCompanionKeys,
|
|
288
|
-
resolveDebugCompanionTargets,
|
|
289
282
|
type DebugWorkbenchPane,
|
|
290
283
|
} from '../window/debug-window-consumers.js';
|
|
291
284
|
import {
|
|
@@ -295,7 +288,6 @@ import {
|
|
|
295
288
|
} from '../window/tablet-workspace-policy.js';
|
|
296
289
|
import {
|
|
297
290
|
type PromptFrame,
|
|
298
|
-
bottomFixedRowsForPromptFrame,
|
|
299
291
|
resolvePromptFrame,
|
|
300
292
|
} from '../display/prompt-frame.js';
|
|
301
293
|
import { resolveBottomSlotBounds } from '../display/bottom-slot.js';
|
|
@@ -341,27 +333,8 @@ import { attachDashboardQuickPassConsumers } from './input/control-signal-consum
|
|
|
341
333
|
import { resolveDashboardChatMainSlashCommand } from './input/chat-main-slash-command.js';
|
|
342
334
|
import { TuiChatPersonaSelection, tuiChatPersonaMessages } from './slash-runtime/chat-persona.js';
|
|
343
335
|
import { awaitGlobalPersonaLoad } from '../persona/global-registry.js';
|
|
344
|
-
import { resolveDashboardChatMainCacheCommand } from './input/chat-main-cache-command.js';
|
|
345
|
-
import {
|
|
346
|
-
dashboardDeltaHelpLines,
|
|
347
|
-
resolveDashboardChatMainDeltaCommand,
|
|
348
|
-
} from './input/chat-main-delta-command.js';
|
|
349
|
-
import {
|
|
350
|
-
DASHBOARD_SETUP_STEPS,
|
|
351
|
-
dashboardSetupHelpLines,
|
|
352
|
-
resolveDashboardChatMainSetupCommand,
|
|
353
|
-
} from './input/chat-main-setup-command.js';
|
|
354
336
|
import { createDashboardInlineSetupFlow } from './setup-inline.js';
|
|
355
|
-
import { resolveDashboardChatMainThemeCommand } from './input/chat-main-theme-command.js';
|
|
356
|
-
import { resolveDashboardChatMainLogCommand } from './input/chat-main-log-command.js';
|
|
357
337
|
import { noProviderReply } from './input/no-provider-reply.js';
|
|
358
|
-
import {
|
|
359
|
-
dashboardLogHelpLines,
|
|
360
|
-
resolveDashboardLogFilterAction,
|
|
361
|
-
resolveDashboardLogSearchAction,
|
|
362
|
-
resolveDashboardLogSizeAction,
|
|
363
|
-
resolveDashboardLogTurnAction,
|
|
364
|
-
} from './input/chat-main-log-actions.js';
|
|
365
338
|
import type { LogTurnSeparatorMode } from './log-turn-separator-mode.js';
|
|
366
339
|
import { createDashboardChatMainInteractionOpts } from './input/chat-main-interaction-opts.js';
|
|
367
340
|
import { createDashboardChatMainCompletionOpts } from './input/chat-main-completion-opts.js';
|
|
@@ -379,6 +352,7 @@ import {
|
|
|
379
352
|
matchDashboardChordAction,
|
|
380
353
|
} from './input/dashboard-chord-actions.js';
|
|
381
354
|
import { createDashboardChordRuntime } from './input/dashboard-chord-runtime.js';
|
|
355
|
+
import { registerLogFoldInputBinding } from './input/log-fold-binding.js';
|
|
382
356
|
import { handleDashboardDisplayKeyRoute } from './input/dashboard-display-key-route.js';
|
|
383
357
|
import { routeDashboardPriorityKey } from './input/dashboard-priority-key-route.js';
|
|
384
358
|
import { shouldPassthroughDashboardHostOwnedInputCoreAction } from './input/input-core-host-action.js';
|
|
@@ -425,7 +399,6 @@ import {
|
|
|
425
399
|
} from './voice-host-boot.js';
|
|
426
400
|
import {
|
|
427
401
|
bootDashboardAutoTts,
|
|
428
|
-
handleAutoTtsSlash,
|
|
429
402
|
} from './auto-tts/auto-tts-host-boot.js';
|
|
430
403
|
import {
|
|
431
404
|
bootDashboardVoiceChat,
|
|
@@ -443,7 +416,6 @@ import {
|
|
|
443
416
|
import {
|
|
444
417
|
resolveLegacyForegroundModalFocusPlan,
|
|
445
418
|
resolveLegacyForegroundModalDemotionTransition,
|
|
446
|
-
resolveLegacyForegroundModalFallbackPane,
|
|
447
419
|
} from './input/foreground-modal-focus.js';
|
|
448
420
|
import {
|
|
449
421
|
resolveDashboardInitialWorkingFocus,
|
|
@@ -460,18 +432,10 @@ import { addHint, listHints, removeHint, resetScope } from '../tool-hints/regist
|
|
|
460
432
|
import { resetGateCache } from '../tool-hints/gate.js';
|
|
461
433
|
import { collectSignals, signalsSummary } from '../tool-hints/signals.js';
|
|
462
434
|
import { resetProbes } from '../tool-hints/probe.js';
|
|
463
|
-
import {
|
|
464
|
-
addAllowed,
|
|
465
|
-
hostOf,
|
|
466
|
-
listAllowed,
|
|
467
|
-
rateLimitStatus,
|
|
468
|
-
removeAllowed,
|
|
469
|
-
} from '../tool-hints/api-allowlist.js';
|
|
470
435
|
import type { HintScope } from '../tool-hints/types.js';
|
|
471
|
-
import { listSkillNames, describeSkill } from '../skills/runner.js';
|
|
472
436
|
import { globalAgentRegistry, type AgentToolCallEvent } from '../agent/registry.js';
|
|
473
437
|
import { SELF_IMPLEMENT_TOOL_NAMES } from '../boot/daemon-tools/self-implement-names.js';
|
|
474
|
-
import {
|
|
438
|
+
import { type FoldMode } from '../log-entry.js';
|
|
475
439
|
import { renderToolCallEvent, renderToolResultVariants } from '../chat/tool-render/index.js';
|
|
476
440
|
import { FoldStack } from '../fold-stack.js';
|
|
477
441
|
import { getSkillIndex, reloadSkillIndex, applySkillFilter, skillIndexProblems } from '../skills/index.js';
|
|
@@ -546,7 +510,11 @@ export function formatEscAbortWaitingTargets(targets: readonly string[]): string
|
|
|
546
510
|
export function settleEscAbortPendingLines(lines: string[], fromIndex: number): void {
|
|
547
511
|
let settled = false;
|
|
548
512
|
for (let index = fromIndex; index < lines.length;) {
|
|
549
|
-
const line = lines[index]
|
|
513
|
+
const line = lines[index];
|
|
514
|
+
if (typeof line !== 'string') {
|
|
515
|
+
index += 1;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
550
518
|
const isAbortPending = line.startsWith(' ⏳ 중단 요청됨 — ');
|
|
551
519
|
const isAbortRepeat = /^ ⏳ ESC 재시도 \d+회 — /.test(line);
|
|
552
520
|
if (!isAbortPending && !isAbortRepeat) {
|
|
@@ -590,21 +558,50 @@ export function resumeInProcessDashboardSession(
|
|
|
590
558
|
setAttachedSessionId: (sessionId: string) => void;
|
|
591
559
|
setActiveSessionId: (sessionId: string) => void;
|
|
592
560
|
},
|
|
593
|
-
):
|
|
561
|
+
):
|
|
562
|
+
| { resumed: true; sessionId: string; turns: number; repaired?: { removedCalls: number; removedResults: number } }
|
|
563
|
+
| { resumed: false; reason: string } {
|
|
594
564
|
const resolvedId = deps.resolveSessionId(resumeSessionId);
|
|
595
565
|
const loaded = resolvedId ? deps.historyFromSession(resolvedId) : null;
|
|
596
566
|
if (!resolvedId || !loaded) return { resumed: false, reason: resumeSessionId };
|
|
597
567
|
|
|
568
|
+
// ④ 깨진 기록 방어(TUI-H8 · #69) — 도구 호출 도중 죽은 프로세스의 기록은 호출·결과 짝이 깨진 꼬리를 남겨
|
|
569
|
+
// 이어받은 첫 턴이 Codex 400 으로 죽는다. 고아 호출·결과만 잘라(`repairCodexToolPairs` · 짝이 맞는 것은 그대로)
|
|
570
|
+
// 이어받고, 같은 id 가 겹쳐 짝을 못 가리면 이어받지 않는다(새 세션 권고).
|
|
571
|
+
const pairs = repairCodexToolPairs(loaded.history as unknown as LLMMessage[]);
|
|
572
|
+
if (pairs.ambiguousIds.length > 0) {
|
|
573
|
+
try {
|
|
574
|
+
debug.log('session', 'resume-refused', {
|
|
575
|
+
surface: 'tui', sessionId: resolvedId, reason: 'ambiguous-tool-pairs', ambiguousIds: pairs.ambiguousIds.length,
|
|
576
|
+
});
|
|
577
|
+
} catch { /* observation is best-effort */ }
|
|
578
|
+
return { resumed: false, reason: `${resumeSessionId} — 도구 호출·결과 짝이 모호한 기록이다 · 새 세션으로 시작한다(권고)` };
|
|
579
|
+
}
|
|
580
|
+
const repairedCount = pairs.removedCalls + pairs.removedResults;
|
|
581
|
+
const history = repairedCount > 0 ? pairs.messages as unknown as ChatMessage[] : loaded.history;
|
|
582
|
+
if (repairedCount > 0) {
|
|
583
|
+
try {
|
|
584
|
+
debug.log('session', 'resume-history-repaired', {
|
|
585
|
+
surface: 'tui', sessionId: resolvedId, removedCalls: pairs.removedCalls, removedResults: pairs.removedResults,
|
|
586
|
+
});
|
|
587
|
+
} catch { /* observation is best-effort */ }
|
|
588
|
+
}
|
|
589
|
+
|
|
598
590
|
const sysFromDash = deps.chatHistory.find((message) => message.role === 'system');
|
|
599
591
|
deps.chatHistory.length = 0;
|
|
600
592
|
if (sysFromDash) deps.chatHistory.push(sysFromDash);
|
|
601
|
-
for (const message of
|
|
593
|
+
for (const message of history) {
|
|
602
594
|
if (message.role === 'system' && sysFromDash && message.content === sysFromDash.content) continue;
|
|
603
595
|
deps.chatHistory.push(message);
|
|
604
596
|
}
|
|
605
597
|
deps.setAttachedSessionId(resolvedId);
|
|
606
598
|
try { deps.setActiveSessionId(resolvedId); } catch { /* best-effort */ }
|
|
607
|
-
return {
|
|
599
|
+
return {
|
|
600
|
+
resumed: true,
|
|
601
|
+
sessionId: resolvedId,
|
|
602
|
+
turns: history.length,
|
|
603
|
+
...(repairedCount > 0 ? { repaired: { removedCalls: pairs.removedCalls, removedResults: pairs.removedResults } } : {}),
|
|
604
|
+
};
|
|
608
605
|
}
|
|
609
606
|
|
|
610
607
|
/**
|
|
@@ -625,38 +622,33 @@ export function getEscAbortRunningCount(
|
|
|
625
622
|
return Math.max(1, registeredRunning);
|
|
626
623
|
}
|
|
627
624
|
}
|
|
628
|
-
import { resetOnboardingMarker } from '../onboarding.js';
|
|
625
|
+
import { needsOnboarding, resetOnboardingMarker } from '../onboarding.js';
|
|
629
626
|
import {
|
|
630
627
|
getUserConfig, reloadUserConfig, saveUserConfig,
|
|
631
628
|
rotateNextProvider, jumpToRotationEntry, rotationEntryLabel, applyRotationEntry,
|
|
632
|
-
|
|
629
|
+
resolveAcpHopCapFromConfig,
|
|
633
630
|
VOICE_HARDCODED_DEFAULTS,
|
|
634
631
|
} from '../user-config.js';
|
|
635
|
-
import {
|
|
636
|
-
import {
|
|
637
|
-
import
|
|
632
|
+
import { streamLLM, anyProviderAvailable, getProviderForConfig, nextReasoningLevel, reasoningLevelLabel, effectiveReasoningLevel, modelSupportsReasoning, type LLMMessage, type LLMToolSpec } from '../llm.js';
|
|
633
|
+
import { currentRouteDecision, formatRouteDecisionSummary } from '../llm/route-decision.js';
|
|
634
|
+
import { resolveDashboardActiveModel } from './acp-active-model.js';
|
|
638
635
|
import { getModelFamily, getModelTier } from '../models/prompts.js';
|
|
639
636
|
import { buildPromptInjection, getPromptBankStore } from '../prompt-bank/index.js';
|
|
640
|
-
import { getInputHistoryStore
|
|
637
|
+
import { getInputHistoryStore } from '../input-history.js';
|
|
641
638
|
import { inspectActiveProvider } from '../provider-summary.js';
|
|
639
|
+
import { createStatusProviderInfoReader } from '../status/status-provider-info.js';
|
|
642
640
|
import { activeCodexAccountView } from '../oauth/codex-account-store.js';
|
|
643
|
-
import { buildDashboardStatusLines } from './dashboard-status-lines.js';
|
|
644
|
-
import { readSlashContextNow, seatsNowLine } from '../context-bus/context-now-surfaces.js';
|
|
641
|
+
import { buildDashboardStatusLines, readDashboardSeatsNow, type DashboardStatusLinesInput } from './dashboard-status-lines.js';
|
|
645
642
|
import { buildFirstScreenBand, resolveTuiDaemonLink, type TuiDaemonLink } from './first-screen-band.js';
|
|
646
643
|
import { detectLocale } from '../expression/locale.js';
|
|
647
644
|
import { runNexusShow, type NexusShowResult } from '../cli/nexus-show.js';
|
|
648
645
|
import {
|
|
649
|
-
workingDirSegment, sessionCwdSegment, modelSegment, gitSegment,
|
|
650
|
-
ctxBarSegment, costSegment, speedSegment, elapsedSegment, cacheSegment,
|
|
651
|
-
ptyShellCountSegment,
|
|
652
|
-
voiceCostSegment,
|
|
653
646
|
type GitSegmentState,
|
|
654
647
|
} from '../status/bar.js';
|
|
655
648
|
// PR-S1V.5 — voice month-to-date USD pill on the secondary status row.
|
|
656
649
|
// The tracker is process-wide so the same singleton is read here and
|
|
657
650
|
// updated by `voice-input-bridge.transcribe` (TUI path) + daemon REST
|
|
658
651
|
// handler (PWA path).
|
|
659
|
-
import { globalVoiceCostTracker } from '../voice/cost-tracker.js';
|
|
660
652
|
import { createDashboardMouseWiring } from './input/mouse-wiring.js';
|
|
661
653
|
import { createMouseConversationPopupRuntime } from './input/mouse-conversation-popup-runtime.js';
|
|
662
654
|
import { createMouseModalHitRuntime } from './input/mouse-modal-hit-runtime.js';
|
|
@@ -740,7 +732,7 @@ import { createDashboardContextMenuFeedbackRuntime } from './context-menu-feedba
|
|
|
740
732
|
import { createDashboardStatusFeedbackRuntime } from './status-feedback-runtime.js';
|
|
741
733
|
import { createDashboardTransferFeedbackRuntime } from './transfer-feedback-runtime.js';
|
|
742
734
|
import { createDashboardFinderFeedbackRuntime } from './finder-feedback-runtime.js';
|
|
743
|
-
import { createDashboardTermSlashRuntime } from './term-slash-runtime.js';
|
|
735
|
+
import { createDashboardTermSlashRuntime, resolveTuiShellSeat } from './term-slash-runtime.js';
|
|
744
736
|
import { createDashboardShellSlashRuntime } from './shell-slash-runtime.js';
|
|
745
737
|
import { createDashboardRunSkillSlashRuntime } from './run-skill-slash-runtime.js';
|
|
746
738
|
import { createDashboardSkillTriggersSlashRuntime } from './skill-triggers-slash-runtime.js';
|
|
@@ -790,7 +782,6 @@ import { createPfcScreenshotAttacher, type PfcScreenshotAttacher } from '../cond
|
|
|
790
782
|
import { createStatusShellPostureRuntime } from './status-shell-posture-runtime.js';
|
|
791
783
|
import { createTerminalHostFacade } from '../shell-runner/terminal-host-facade.js';
|
|
792
784
|
import {
|
|
793
|
-
buildTerminalMouseIntentEvent,
|
|
794
785
|
interpretTerminalSurfaceIntent,
|
|
795
786
|
} from './terminal-surface-intent.js';
|
|
796
787
|
import {
|
|
@@ -821,18 +812,16 @@ import { bootDashboardApproverRuntimes } from './approver-runtime-boot.js';
|
|
|
821
812
|
import { bootDashboardCaptureSourceProviders } from './capture-source-provider-boot.js';
|
|
822
813
|
import { bootDashboardContextMenuProviders } from './context-menu-provider-boot.js';
|
|
823
814
|
import { bootDashboardContextMenuRegistry } from './context-menu-registry-boot.js';
|
|
824
|
-
import { buildPillMenu } from '../ui/context-menu-registry.js';
|
|
825
|
-
import { createActionPickerRecipe } from '../mouse-action-recipes.js';
|
|
826
|
-
import type { PopupPlacement } from '../ui/chrome/picker-popup-placement.js';
|
|
827
|
-
import { deriveSubmenuPopupRoleTheme } from '../ui/submenu-popup-theme.js';
|
|
828
815
|
import { bootDashboardContextRuntime } from './context-runtime-boot.js';
|
|
829
816
|
import { bootDashboardContextWindowRuntime } from './context-window-runtime-boot.js';
|
|
830
817
|
import { bootDashboardEagerTools } from './eager-tool-boot.js';
|
|
831
818
|
import { bootDashboardEmbodiedTools } from './embodied-tools-boot.js';
|
|
832
819
|
import { bootDashboardHitl } from './hitl-boot.js';
|
|
820
|
+
import { bootBannerStatusLines, normalizeBootBannerMode, renderMinimalBootBand, type BootBannerHeld } from './boot-banner.js';
|
|
833
821
|
import { attachDashboardFilePathToken } from './file-path-attachment.js';
|
|
834
822
|
import { bootDashboardSlashExecutor } from './slash-executor-boot.js';
|
|
835
823
|
import { buildDashboardSlashRegistry, type DashboardSlashContext } from './slash-runtime/index.js';
|
|
824
|
+
import type { LoopScheduler } from './loop-scheduler.js';
|
|
836
825
|
import { unknownSlashReply } from './slash-runtime/unknown-slash-reply.js';
|
|
837
826
|
import { resolveSurfaceUx } from '../agent/surface-ux/build.js';
|
|
838
827
|
import type { SurfaceUx } from '../agent/surface-ux/types.js';
|
|
@@ -880,14 +869,14 @@ import {
|
|
|
880
869
|
} from '../dashboard-pane-context-menu.js';
|
|
881
870
|
// writeClipboard imported below with other clipboard helpers (line ~185).
|
|
882
871
|
import {
|
|
883
|
-
|
|
872
|
+
paneAtColumn, paneNavLabel,
|
|
884
873
|
type PaneNavHitArea,
|
|
885
874
|
} from './panes/nav.js';
|
|
886
875
|
import { getSessionCwd, setSessionCwd, subscribeSessionCwd, pickSwdTargetFromBrowser } from '../session/working-dir.js';
|
|
887
876
|
import {
|
|
888
877
|
getGitStatusView,
|
|
889
878
|
subscribeGitChanges,
|
|
890
|
-
|
|
879
|
+
refreshDirtyAsync as refreshGitDirtyAsync,
|
|
891
880
|
} from '../git-fs/index.js';
|
|
892
881
|
import { getPty, listPty, requestPtyTakeover } from '../pty-shell/registry.js';
|
|
893
882
|
import { listPtyManifestRowsAt } from '../pty-shell/pty-manifest.js';
|
|
@@ -899,16 +888,13 @@ import { formatPtyCallLine, formatPtyResultLine } from '../pty-activity-log.js';
|
|
|
899
888
|
import { getSessionMetrics } from '../status/metrics.js';
|
|
900
889
|
import {
|
|
901
890
|
createContextRegistry, tokenizeInput, pruneUnreferenced,
|
|
902
|
-
addAttachment as ctxAdd,
|
|
903
891
|
addAttachmentEx as ctxAddEx,
|
|
904
|
-
clearAll as ctxClearAll, clearLarge as ctxClearLarge,
|
|
905
892
|
dropAttachment as ctxDrop, listAttachments as ctxList,
|
|
906
893
|
totalContextBytes as ctxTotalBytes,
|
|
907
|
-
DEFAULT_LARGE_THRESHOLD_BYTES,
|
|
908
894
|
type Attachment,
|
|
909
895
|
} from '../context.js';
|
|
910
896
|
import { loadAllAttachments } from '../extractors.js';
|
|
911
|
-
import { grabClipboardImage, pruneOldPastes, isClipboardSupported, writeClipboard, writeClipboardDetailed
|
|
897
|
+
import { grabClipboardImage, pruneOldPastes, isClipboardSupported, writeClipboard, writeClipboardDetailed } from '../clipboard/index.js';
|
|
912
898
|
import { startClipboardHistory, type ClipboardHistory } from '../clipboard/history.js';
|
|
913
899
|
import { renderImagePreview } from '../image/preview.js';
|
|
914
900
|
import { isKgpTerminal } from '../kgp/capabilities.js';
|
|
@@ -917,23 +903,19 @@ import { _internal as previewRouterInternal } from '../preview/router.js';
|
|
|
917
903
|
import { showPreviewModal } from './modals/preview.js';
|
|
918
904
|
import { terminalModalRouter } from './input/terminal-modal-router.js';
|
|
919
905
|
import {
|
|
920
|
-
computeDashboardViewMode,
|
|
921
906
|
syncDashboardViewModeToContextKeys,
|
|
922
907
|
type DashboardViewModeSignals,
|
|
923
908
|
} from './runtime/view-mode.js';
|
|
924
909
|
import { IDLE_VIEW_MODE, type ViewMode } from '../input-core/view-mode.js';
|
|
925
|
-
import { createInteractiveTerminalModal } from '../interactive-terminal-modal.js';
|
|
926
910
|
import { showToast } from './render/toast.js';
|
|
927
911
|
import {
|
|
928
912
|
browserWidgetInstanceIdForView,
|
|
929
|
-
shortcutFor,
|
|
930
913
|
} from './modals/pane.js';
|
|
931
914
|
import { showTransientTerminalModal, currentTransientTerminalModal } from './modals/transient.js';
|
|
932
|
-
import {
|
|
915
|
+
import { showLivePaneMultiModal, type PaneMultiModalHandle } from './modals/pane-multi.js';
|
|
933
916
|
import {
|
|
934
917
|
resolveCompanionPopupChrome,
|
|
935
918
|
resolveCompanionPopupTitleControls,
|
|
936
|
-
resolveWindowScopedCompanionPopupChrome,
|
|
937
919
|
} from './modals/companion-popup-chrome.js';
|
|
938
920
|
import {
|
|
939
921
|
resolveDebugPaneMultiChrome,
|
|
@@ -951,30 +933,22 @@ import {
|
|
|
951
933
|
BroadcastBus,
|
|
952
934
|
PreviewSlotAdapter,
|
|
953
935
|
getChannelBus,
|
|
954
|
-
type TerminalPlacement,
|
|
955
936
|
} from '../terminal-matrix/index.js';
|
|
956
|
-
import { resolveTerminalMoveDestination } from '../terminal-matrix/mobility.js';
|
|
957
937
|
import { initElementObservability } from '../element-registry/index.js';
|
|
958
938
|
import {
|
|
959
|
-
armSessionQuickControl,
|
|
960
939
|
createSearchPlannerState,
|
|
961
940
|
buildSessionRuntimeToolSpecs,
|
|
962
941
|
buildSessionDashboardStatusLines,
|
|
963
|
-
buildSessionSurfaceStatusLines,
|
|
964
942
|
consumeSessionQuickControl,
|
|
965
943
|
createSessionPostureState,
|
|
966
944
|
enterSessionControlMode,
|
|
967
945
|
exitSessionControlMode,
|
|
968
946
|
isSessionControlActive,
|
|
969
947
|
routeDevHarnessSubmitIntent,
|
|
970
|
-
parseSessionControlSlash,
|
|
971
|
-
parseSessionSurfaceSlash,
|
|
972
948
|
resolveSessionInputModeFromChatMode,
|
|
973
949
|
resolveSessionModeSnapshot,
|
|
974
950
|
resolveSessionSurfaceStatus,
|
|
975
|
-
setSessionPreferredSurface,
|
|
976
951
|
toggleSessionControlMode,
|
|
977
|
-
type SearchPlannerState,
|
|
978
952
|
} from '../session-runtime/index.js';
|
|
979
953
|
import {
|
|
980
954
|
initDashboardApprovers,
|
|
@@ -987,12 +961,13 @@ import {
|
|
|
987
961
|
bootDashboardAcpSession,
|
|
988
962
|
deriveDaemonHttpBase,
|
|
989
963
|
fetchDaemonSessionHistory,
|
|
990
|
-
fetchDaemonSessionList,
|
|
991
964
|
} from './acp-boot.js';
|
|
992
965
|
import { renderReplayPreviewLines } from './replay-preview.js';
|
|
993
966
|
import {
|
|
994
967
|
buildDashboardTurnPreamble,
|
|
995
968
|
} from './turn-preamble.js';
|
|
969
|
+
import { describeSeatBoot, startSeatBootConversation } from './seat-boot-preamble.js';
|
|
970
|
+
import { ensureSeatLocalInstructions, installSeatSessionRecorder } from './seat-local-instructions.js';
|
|
996
971
|
import type { DashboardSession } from '../tui-client/dashboard-session.js';
|
|
997
972
|
import type { SessionTurnProfile } from '../session-runtime/index.js';
|
|
998
973
|
import { createCompanionSurfaceHostRegistry } from './companion-surface-host-registry.js';
|
|
@@ -1021,7 +996,6 @@ import {
|
|
|
1021
996
|
} from '../display/workspace-affinity.js';
|
|
1022
997
|
import { initDashboardHitl, stopDashboardHitl } from './runtime/hitl.js';
|
|
1023
998
|
import { openDashboardShellRollupPopup } from './shell-rollup-popup.js';
|
|
1024
|
-
import { expandPromptReferences } from '../prompt/references.js';
|
|
1025
999
|
import { spawnCodingAgent, CodingAgentBinaryMissing } from '../terminal/coding-agent.js';
|
|
1026
1000
|
import type { TerminalSession } from '../terminal/session-registry.js';
|
|
1027
1001
|
import { createSessionPickerModal } from '../session/picker-modal.js';
|
|
@@ -1031,28 +1005,31 @@ import { listSessions as listChatSessionsForResume, adoptSession as adoptChatSes
|
|
|
1031
1005
|
// 영속은 세션 스토어(forkSessionFromHistory)가 담당한다.
|
|
1032
1006
|
import { userMessagePositions, truncateBeforeNthUser } from '../acp/session-fork.js';
|
|
1033
1007
|
import { resolveEscEscRewind } from '../chat/esc-esc-rewind.js';
|
|
1034
|
-
import { wirePersistence
|
|
1008
|
+
import { wirePersistence } from '../terminal/session-persistence.js';
|
|
1035
1009
|
import { renderMarkdown as renderMarkdownGlow } from '../markdown-preview.js';
|
|
1036
1010
|
import { basename, dirname } from 'path';
|
|
1037
1011
|
import { statSync } from 'fs';
|
|
1038
1012
|
import {
|
|
1039
|
-
FIGURES,
|
|
1040
|
-
|
|
1041
|
-
sectionHeader, progressIndicator, renderTaskStatus,
|
|
1042
|
-
type StatusColor, type TaskStatus,
|
|
1013
|
+
FIGURES, toolHeader, toolResult,
|
|
1014
|
+
renderMarkdown, successLine, errorLine, warningLine,
|
|
1043
1015
|
} from '../render.js';
|
|
1044
1016
|
import type { SyncMode } from '../types.js';
|
|
1045
1017
|
import type { ChatMessage } from '../chat/index.js';
|
|
1018
|
+
import { repairCodexToolPairs } from './input/compact-prepass-recovery.js';
|
|
1019
|
+
import { observeSessionStart } from '../session/session-start.js';
|
|
1046
1020
|
import type { PlainTurnSubmit } from '../input/turn-submit.js';
|
|
1047
1021
|
// U-4.2 · `renderLogPane` no longer called from dashboard directly —
|
|
1048
1022
|
// wd-log widget owns log rendering via renderLogViaWidget. Only
|
|
1049
1023
|
// `findBlock` still used here (chat-input block navigation).
|
|
1050
|
-
import { findBlock } from '../panes/log-pane.js';
|
|
1024
|
+
import { findBlock, logPaneContentHeight, maxLogPaneScroll, visibleSearchRowOffset } from '../panes/log-pane.js';
|
|
1051
1025
|
import { createQuitConfirmationState, gracefulQuit, type QuitConfirmationState } from './graceful-quit.js';
|
|
1052
|
-
import {
|
|
1026
|
+
import { consumeTurnEndInterrupted, handoffTurnTypeaheadAtTurnEnd, releaseTurnTypeaheadPending, runTurnTypeaheadPushNow, steerLastTurnTypeaheadSubmission } from './input/turn-steer-bridge.js';
|
|
1027
|
+
import { abortTurnOnly } from '../turn-abort-scope.js';
|
|
1028
|
+
import { consumeInternalTurnYield, createInternalTurnGate, formatInternalTurnBanner, resolveInternalTurnSubmitIntent, type InternalTurnRequest } from './input/internal-turn.js';
|
|
1029
|
+
import { createSeatAutowake } from './input/seat-autowake.js';
|
|
1053
1030
|
import { createAttachmentRowMap } from '../log-pane/attachment-row-map.js';
|
|
1054
1031
|
import { createAttachmentPopup, type AttachmentPopupAction } from '../log-pane/attachment-popup.js';
|
|
1055
|
-
import { findLogMatches, type LogSearchResult } from '../log-pane/search.js';
|
|
1032
|
+
import { findLogMatches, findVisibleLogMatches, type LogSearchResult } from '../log-pane/search.js';
|
|
1056
1033
|
import { createLogSearchModal, type LogSearchModalOpts } from '../log-pane/search-modal.js';
|
|
1057
1034
|
// U-4.3 · direct imports of tryAttachmentHitAtBodyRow /
|
|
1058
1035
|
// tryHandleLogAreaClick are gone · wd-log.onMouse owns the hit-test.
|
|
@@ -1081,7 +1058,6 @@ import { enqueueSoftStop, readSoftStopRequestStatus } from '../harness/control-i
|
|
|
1081
1058
|
import { setSelfImplementSoftStopSpaceObserver } from '../self-implement/self-implement-runtime.js';
|
|
1082
1059
|
import { createViEditor, type ViEditorHandle } from '../vi-editor.js';
|
|
1083
1060
|
import { launchEditor, canLaunchEditor } from '../editor-launcher.js';
|
|
1084
|
-
import { createSshPickerModal } from '../ssh/ssh-picker-modal.js';
|
|
1085
1061
|
import {
|
|
1086
1062
|
refreshRemoteWorkingDir,
|
|
1087
1063
|
refreshRemoteWorkingDirPreview,
|
|
@@ -1091,12 +1067,10 @@ import {
|
|
|
1091
1067
|
remoteBadge,
|
|
1092
1068
|
} from '../ssh/remote-browser.js';
|
|
1093
1069
|
import { editRemoteFile } from '../ssh/remote-edit.js';
|
|
1094
|
-
import {
|
|
1095
|
-
import {
|
|
1096
|
-
import {
|
|
1097
|
-
import {
|
|
1098
|
-
import { listTransferTargets, type TransferTarget } from '../transfer/transfer-targets.js';
|
|
1099
|
-
import { createTransferPickerModal } from '../transfer/transfer-picker-modal.js';
|
|
1070
|
+
import { createFolderPickerModal } from '../finder/finder-modal.js';
|
|
1071
|
+
import { hostname } from 'node:os';
|
|
1072
|
+
import { type TransferTarget } from '../transfer/transfer-targets.js';
|
|
1073
|
+
import { createInputPickers } from '../tui/input/pickers.js';
|
|
1100
1074
|
import { sshTransfer, type SshTransferProgress } from '../transfer/ssh-transfer.js';
|
|
1101
1075
|
import { iphoneTransfer } from '../transfer/iphone-transfer.js';
|
|
1102
1076
|
import { DashboardAcpChat, displayBackendName, parseAcpSlash, type AcpBackendId } from './chat/acp-chat.js';
|
|
@@ -1111,7 +1085,6 @@ import {
|
|
|
1111
1085
|
} from '../skills/tools/dashboard-config.js';
|
|
1112
1086
|
import {
|
|
1113
1087
|
initDashboardWidgetTools,
|
|
1114
|
-
type WidgetInfo,
|
|
1115
1088
|
} from '../skills/tools/dashboard-widget.js';
|
|
1116
1089
|
import { initWidgetInspectorTools } from '../skills/tools/widget-inspector.js';
|
|
1117
1090
|
import {
|
|
@@ -1124,7 +1097,6 @@ import {
|
|
|
1124
1097
|
} from '../agent/spawn-embodied-agent-in-vw.js';
|
|
1125
1098
|
import { findSessionObserver } from '../agent/observer-registry.js';
|
|
1126
1099
|
import {
|
|
1127
|
-
conversationHoverHudLabel,
|
|
1128
1100
|
conversationModalTitle,
|
|
1129
1101
|
conversationModalWidgetId,
|
|
1130
1102
|
findConversationSessionEntry,
|
|
@@ -1142,7 +1114,6 @@ import {
|
|
|
1142
1114
|
} from '../agent/channel-router.js';
|
|
1143
1115
|
import { initTtySnapshotTools } from '../skills/tools/tty-snapshot.js';
|
|
1144
1116
|
import { initAgentHandoffTool } from '../skills/tools/agent-handoff.js';
|
|
1145
|
-
import { touchSshHost } from '../ssh/ssh-hosts.js';
|
|
1146
1117
|
import { DisplayCoordinator } from '../display/coordinator.js';
|
|
1147
1118
|
import {
|
|
1148
1119
|
createLivePlaygroundHarness,
|
|
@@ -1170,10 +1141,10 @@ import {
|
|
|
1170
1141
|
import { computeWorkingFocusSync } from '../display/working-focus-sync.js';
|
|
1171
1142
|
import { dispatchPaneClick } from '../display/pane-click-dispatch.js';
|
|
1172
1143
|
import { installResizeListener } from '../display/resize-listener.js';
|
|
1173
|
-
import { isPtyForwardMouseEventType
|
|
1144
|
+
import { isPtyForwardMouseEventType } from '../display/types.js';
|
|
1174
1145
|
import { createDisplayEventBus } from '../display/events.js';
|
|
1175
1146
|
import { agentBatchScratchTitle, renderAgentBatchScratch } from '../display/agent-batch.js';
|
|
1176
|
-
import {
|
|
1147
|
+
import { type ExecutionSurfaceHandle } from '../display/index.js';
|
|
1177
1148
|
import { AgentSurfaceStore, renderAgentDetail, renderAgentRoster, type AgentSurfaceState } from '../display/agent-surface.js';
|
|
1178
1149
|
import { globalAgentFlash } from '../display/agent-flash.js';
|
|
1179
1150
|
import { renderDebugEventDetail, renderDebugEventRows, renderDebugStack, renderPromptInjectionDebug } from '../display/debug-surface.js';
|
|
@@ -1196,9 +1167,9 @@ export function registerDashboardCoreHostTools(host: Pick<PluginHost, 'registerH
|
|
|
1196
1167
|
}
|
|
1197
1168
|
}
|
|
1198
1169
|
import { registerDashboardPaneHostTools } from './panes/host-tools.js';
|
|
1199
|
-
import {
|
|
1200
|
-
import { resolveActiveTheme
|
|
1201
|
-
import { getTheme
|
|
1170
|
+
import { type ThemeTokens } from '../theme/tokens.js';
|
|
1171
|
+
import { resolveActiveTheme } from './render/theme-resolver.js';
|
|
1172
|
+
import { getTheme } from '../themes/index.js';
|
|
1202
1173
|
import type { PaneFocus, PreviewSource, WorkingDirView } from '../workspace-types.js';
|
|
1203
1174
|
import type { DisplayMouseEvent } from '../display/types.js';
|
|
1204
1175
|
import type { Key as TuiKey } from '../tui.js';
|
|
@@ -1215,7 +1186,6 @@ import {
|
|
|
1215
1186
|
readDirEntries,
|
|
1216
1187
|
sortEntries,
|
|
1217
1188
|
splitAtPrefix,
|
|
1218
|
-
dirname as wdDirname,
|
|
1219
1189
|
type WorkingDirState,
|
|
1220
1190
|
type FsEntry,
|
|
1221
1191
|
} from '../working-dir/index.js';
|
|
@@ -1224,9 +1194,6 @@ import {
|
|
|
1224
1194
|
encodeBrowserScopedSubmitText,
|
|
1225
1195
|
resolveBrowserActionContextFromHit,
|
|
1226
1196
|
} from '../browser-pane/actions.js';
|
|
1227
|
-
import { resolveBrowserTransferCapability } from '../browser-pane/capabilities.js';
|
|
1228
|
-
import { openBrowserPaneModal } from '../browser-pane/mount.js';
|
|
1229
|
-
import { openPreviewPaneModal } from '../preview-pane/mount.js';
|
|
1230
1197
|
import {
|
|
1231
1198
|
cyclePreviewSourceForView,
|
|
1232
1199
|
formatPreviewSourceLabel,
|
|
@@ -1239,7 +1206,7 @@ import {
|
|
|
1239
1206
|
} from '../preview-pane/model.js';
|
|
1240
1207
|
import { PreviewPaneRegistry } from '../preview-pane/registry.js';
|
|
1241
1208
|
import { FileIndexCache, absolutize } from '../file-index.js';
|
|
1242
|
-
import { firstPaneOfView
|
|
1209
|
+
import { firstPaneOfView } from '../working-dir/focus.js';
|
|
1243
1210
|
import { applyVisibleFocusRepair } from './focus-repair.js';
|
|
1244
1211
|
import {
|
|
1245
1212
|
nextVisiblePaneFocus,
|
|
@@ -1250,11 +1217,7 @@ import {
|
|
|
1250
1217
|
} from '../views/pane-policy.js';
|
|
1251
1218
|
import {
|
|
1252
1219
|
autoTabletModeForViewport,
|
|
1253
|
-
orderDashboardViewsForCompactMode,
|
|
1254
1220
|
effectiveChatOnlyModeForViewport,
|
|
1255
|
-
orderSurfaceCatalogForCompactMode,
|
|
1256
|
-
pruneDashboardViewsForCompactMode,
|
|
1257
|
-
pruneSurfaceCatalogForCompactMode,
|
|
1258
1221
|
productCompactModeForViewport,
|
|
1259
1222
|
} from '../views/product-compact-mode.js';
|
|
1260
1223
|
import { readScopedRenderLogs, resolveRenderSuppressed, readScopedDebugLevel, resolveStartupDebugLevel, hotPathGateOpen } from '../mss/logging/scoped-level.js';
|
|
@@ -1265,7 +1228,6 @@ import {
|
|
|
1265
1228
|
compileDashboardViewLayout,
|
|
1266
1229
|
describeDashboardViewsForPrompt,
|
|
1267
1230
|
findDashboardView,
|
|
1268
|
-
nextDashboardView,
|
|
1269
1231
|
panesForDashboardView,
|
|
1270
1232
|
serializeDashboardViewsConfig,
|
|
1271
1233
|
validateDashboardViewsConfig,
|
|
@@ -1345,6 +1307,8 @@ import {
|
|
|
1345
1307
|
type SpaceLongPressDetector,
|
|
1346
1308
|
} from './input/space-longpress-detector.js';
|
|
1347
1309
|
import { isAllowlistedDictationSurface } from './input/dictation-surface-allowlist.js';
|
|
1310
|
+
import { createDashboardExternalInputHost, stopTurnViaEscGate } from './input/external-input-host.js';
|
|
1311
|
+
import { registerExternalInputHost } from '../input/external-input-queue.js';
|
|
1348
1312
|
// H3 #6 follow-up #3 — background sessions render as a distinct sidebar
|
|
1349
1313
|
// kind with live state badge + tap-to-Join. Dashboard pulls BG records
|
|
1350
1314
|
// through backgroundToStub + maps state via bgStateToSessionStatus,
|
|
@@ -1932,6 +1896,17 @@ export function renderDashboardFirstScreenBand(input: {
|
|
|
1932
1896
|
return Promise.all([linkPending, pwaPending]).then(() => {});
|
|
1933
1897
|
}
|
|
1934
1898
|
|
|
1899
|
+
/** The /status reader used by the live dashboard slash callback. */
|
|
1900
|
+
export function renderDashboardStatusLines(input: Omit<DashboardStatusLinesInput, 'seatsNow'>, now = Date.now()): string[] {
|
|
1901
|
+
let seatsNow: string | null = null;
|
|
1902
|
+
try {
|
|
1903
|
+
seatsNow = readDashboardSeatsNow(now);
|
|
1904
|
+
} catch (error) {
|
|
1905
|
+
debug.log('dashboard.status', 'seats-unreadable', { error: String(error) });
|
|
1906
|
+
}
|
|
1907
|
+
return buildDashboardStatusLines({ ...input, seatsNow });
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1935
1910
|
export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<DashboardAction> {
|
|
1936
1911
|
let firstDashboardFrameRendered = false;
|
|
1937
1912
|
await awaitGlobalPersonaLoad().catch(() => {});
|
|
@@ -2594,9 +2569,17 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
2594
2569
|
};
|
|
2595
2570
|
const clearLogFilter = (): void => {
|
|
2596
2571
|
logFilterQuery = '';
|
|
2572
|
+
if (logSearchQuery) {
|
|
2573
|
+
logSearchResults = findLogMatches(chatLines, logSearchQuery);
|
|
2574
|
+
logSearchCursor = Math.min(logSearchCursor, Math.max(0, logSearchResults.length - 1));
|
|
2575
|
+
}
|
|
2597
2576
|
};
|
|
2598
2577
|
const applyLogFilter = (query: string): void => {
|
|
2599
2578
|
logFilterQuery = query.trim();
|
|
2579
|
+
if (logSearchQuery) {
|
|
2580
|
+
logSearchResults = findVisibleLogMatches(chatLines, logSearchQuery, logFilterQuery);
|
|
2581
|
+
logSearchCursor = Math.min(logSearchCursor, Math.max(0, logSearchResults.length - 1));
|
|
2582
|
+
}
|
|
2600
2583
|
if (debug.enabled) {
|
|
2601
2584
|
debug.log('log-pane.filter.apply', `q="${logFilterQuery}"`, {
|
|
2602
2585
|
active: logFilterQuery.length > 0,
|
|
@@ -2606,7 +2589,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
2606
2589
|
const applyLogSearch = (query: string): void => {
|
|
2607
2590
|
logSearchQuery = query.trim();
|
|
2608
2591
|
logSearchResults = logSearchQuery
|
|
2609
|
-
?
|
|
2592
|
+
? findVisibleLogMatches(chatLines, logSearchQuery, logFilterQuery)
|
|
2610
2593
|
: [];
|
|
2611
2594
|
logSearchCursor = 0;
|
|
2612
2595
|
if (debug.enabled) {
|
|
@@ -2618,13 +2601,18 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
2618
2601
|
/** Center the viewport on a search-result line. Sets chatScrollOffset
|
|
2619
2602
|
* so the match row lands mid-pane when possible; clamps at bounds. */
|
|
2620
2603
|
const scrollToSearchResult = (result: LogSearchResult): void => {
|
|
2604
|
+
// An explicit search may jump to output that arrived while the pane
|
|
2605
|
+
// was frozen. Release that snapshot before computing the row bound.
|
|
2606
|
+
if (logFrozenTailIndex !== null && result.lineIdx >= logFrozenTailIndex) {
|
|
2607
|
+
logFrozenTailIndex = null;
|
|
2608
|
+
}
|
|
2621
2609
|
const { rows: tr } = termSize();
|
|
2622
2610
|
const sH = computeLogH(tr);
|
|
2623
|
-
const bodyH = Math.max(0, sH - 1);
|
|
2624
2611
|
const hasFooter = chatFooterLine.current != null && chatFooterLine.current !== '';
|
|
2625
|
-
const
|
|
2626
|
-
const
|
|
2627
|
-
const
|
|
2612
|
+
const hasQueueRow = hasFooter && !!renderTurnTypeaheadQueueRow(turnTypeaheadRef.state, Math.max(20, termSize().cols - 6));
|
|
2613
|
+
const contentH = logPaneContentHeight(sH, hasFooter, hasQueueRow);
|
|
2614
|
+
const target = Math.max(0, visibleSearchRowOffset(chatLines, result, logFilterQuery) - Math.floor(contentH / 2));
|
|
2615
|
+
const maxScroll = logScrollMax(sH);
|
|
2628
2616
|
chatScrollOffset = target >= maxScroll ? -1 : target;
|
|
2629
2617
|
// Entering a scrolled state may set the freeze — the
|
|
2630
2618
|
// handleLogPaneKey transition only fires for user keystrokes, so
|
|
@@ -2836,6 +2824,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
2836
2824
|
// execute slashes, etc.
|
|
2837
2825
|
const chatModeState = createSessionPostureState();
|
|
2838
2826
|
|
|
2827
|
+
// TUI-LOOP-COMMAND — 실행 중 스케줄러를 쥐는 ref. showDashboard 인스턴스당
|
|
2828
|
+
// 하나로, 입력 루프 «밖»에서 한 번만 선언한다. slashCtx 는 명령마다 다시
|
|
2829
|
+
// 만들어지므로 리터럴 옆에 선언하면 /loop start 로 만든 스케줄러 ref 가
|
|
2830
|
+
// 다음 명령에서 사라진다 — /loop stop · /session new|clear 세션 끝 정지가
|
|
2831
|
+
// 같은 ref 를 써야 «stop 0»(판정)가 성립한다.
|
|
2832
|
+
const dashboardLoopSchedulerRef: { current: LoopScheduler | null } = { current: null };
|
|
2833
|
+
|
|
2839
2834
|
// T3-C2 — vi-editor mounted in the preview pane. Set by the
|
|
2840
2835
|
// browser pane's `e` key on a file; cleared on :q / :q!. When
|
|
2841
2836
|
// non-null it overrides the preview pane's normal render path.
|
|
@@ -2908,6 +2903,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
2908
2903
|
// current value so log analysis can filter "what keys fired while
|
|
2909
2904
|
// a turn was in flight".
|
|
2910
2905
|
let streamingInFlight = false;
|
|
2906
|
+
/** 지금 턴의 escGate 중단 — 외부 `/stop`(TUI-STOP-INPUT-BYPASS)이 대기열을 건너뛰어 부른다. 턴 밖이면 null. */
|
|
2907
|
+
let currentTurnExternalStop: (() => void) | null = null;
|
|
2911
2908
|
const resolveTmuxStatusLabel = (): string | null => {
|
|
2912
2909
|
if (!process.env['TMUX']) return null;
|
|
2913
2910
|
const fallback = process.env['TMUX_PANE'] ?? 'tmux';
|
|
@@ -3376,6 +3373,26 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3376
3373
|
// 즉시 에코 · 턴 종료 시 loop-head 핸드오프가 prefill/자동제출로 넘긴다.
|
|
3377
3374
|
// (외곽 스코프 선언 — 스트리밍 디스패처와 메인 키 루프 양쪽에서 참조.)
|
|
3378
3375
|
const turnTypeaheadRef = { state: createTurnTypeaheadState() };
|
|
3376
|
+
// «지금 밀어 넣기»로 끊은 턴 — 턴 종료가 큐를 composer 로 «복원»하지 않고 곧바로 «제출»하게 한다.
|
|
3377
|
+
const steerInterruptRef = { pending: false };
|
|
3378
|
+
// ⭐ H2 내부 턴 문(TUI-H2-INTERNAL-TURN) — 자율 재개(H3·H4·H5)가 지나갈 «문 하나». 판정은 모듈,
|
|
3379
|
+
// 여기선 상태를 읽어 넘기기만 한다(⚠️ promptCtl·가시성 함수는 아래에서 선언 — 호출 시점엔 있다).
|
|
3380
|
+
const internalTurnGate = createInternalTurnGate({
|
|
3381
|
+
isTurnRunning: () => streamingInFlight,
|
|
3382
|
+
isInputForeground: () =>
|
|
3383
|
+
chatMainInputVisibilityState().inputOwnership?.owner === 'chat-main' && !topBlockingForegroundModal(),
|
|
3384
|
+
getBufferText: () => promptCtl.getBufferText?.(),
|
|
3385
|
+
getTypeaheadCount: () =>
|
|
3386
|
+
turnTypeaheadRef.state.queuedSubmissions.length + (turnTypeaheadRef.state.buffer ? 1 : 0),
|
|
3387
|
+
});
|
|
3388
|
+
// ⭐ H3 턴 끝 자동 재개(OPT-QUEUE-AUTOWAKE) — 자리 TUI ∧ autowake 파일일 때만. 턴 끝에 «남은 일»(안 읽은
|
|
3389
|
+
// 우편 · queue.md «## 대기»)을 재고 H2 문으로 다음 턴을 청한다. 판정·상한은 모듈, 여기선 배선만.
|
|
3390
|
+
const seatAutowake = createSeatAutowake({
|
|
3391
|
+
startTurnIfIdle: (text, opts) => internalTurnGate.startTurnIfIdle(text, opts),
|
|
3392
|
+
getCwd: () => workingDir.cwd,
|
|
3393
|
+
notify: (line) => { chatLines.push(C.muted(line)); draw(); },
|
|
3394
|
+
});
|
|
3395
|
+
const internalTurnYieldPort = seatAutowake.wrapPort(internalTurnGate.port);
|
|
3379
3396
|
// ⭐ `B3` — 큐를 «도는 턴»에 넣으려면 그 턴이 도는 ***ACP 세션 id*** 가 필요하다.
|
|
3380
3397
|
// ⛔ `attachedSessionId`(대화 세션)와 «다른 축»이다 — 루프는 ACP 세션으로 스코프를 연다.
|
|
3381
3398
|
// ⚠️ 세션은 이 함수 «뒤»에서 만들어지므로 참조로 둔다(호출 시점엔 채워져 있다).
|
|
@@ -3416,6 +3433,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3416
3433
|
// indicator below the scrolling log body. Mutated by startPinnedThinking
|
|
3417
3434
|
// via this ref. `null` = no footer, body uses full log height.
|
|
3418
3435
|
const chatFooterLine: { current: string | null } = { current: null };
|
|
3436
|
+
const logScrollMax = (height: number): number => {
|
|
3437
|
+
const hasFooter = chatFooterLine.current != null && chatFooterLine.current !== '';
|
|
3438
|
+
const hasQueueRow = hasFooter && !!renderTurnTypeaheadQueueRow(turnTypeaheadRef.state, Math.max(20, termSize().cols - 6));
|
|
3439
|
+
return maxLogPaneScroll(chatLines, height, {
|
|
3440
|
+
filterQuery: logFilterQuery,
|
|
3441
|
+
frozenTailIndex: logFrozenTailIndex,
|
|
3442
|
+
hasFooter,
|
|
3443
|
+
hasQueueRow,
|
|
3444
|
+
});
|
|
3445
|
+
};
|
|
3419
3446
|
const disposeEventLoopStallMonitor = startEventLoopStallMonitor({
|
|
3420
3447
|
getContext: () => ({
|
|
3421
3448
|
streamingInFlight,
|
|
@@ -3538,21 +3565,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3538
3565
|
* actual view model instead of drifting toward an old 60/40 guess.
|
|
3539
3566
|
*/
|
|
3540
3567
|
const dashboardState = new DashboardStateStore();
|
|
3541
|
-
const getFallbackPromptFrame = (termRows: number) =>
|
|
3542
|
-
dashboardState.getFallbackPromptFrame(termRows);
|
|
3543
3568
|
const getLayoutPromptFrame = (termRows: number): PromptFrame =>
|
|
3544
3569
|
dashboardState.getLayoutPromptFrame(termRows);
|
|
3545
3570
|
const currentPromptFrame = (termRows = termSize().rows): PromptFrame =>
|
|
3546
3571
|
dashboardState.getCurrentPromptFrame(termRows);
|
|
3547
3572
|
|
|
3548
|
-
/** Rows taken up by everything BELOW the log zone (hud excluded — it
|
|
3549
|
-
* sits between grid and log, and is reserved separately when the
|
|
3550
|
-
* grid is present). Used to compute paneH below and to size the
|
|
3551
|
-
* embedded grid when logEmbedded=true. Must match the number of
|
|
3552
|
-
* rows pushed by zones 'input-divider-upper' through 'dock'. */
|
|
3553
|
-
const bottomFixedRows = (inputHeight: number) =>
|
|
3554
|
-
bottomFixedRowsForPromptFrame(inputHeight);
|
|
3555
|
-
|
|
3556
3573
|
/** Compute paneH (height reserved for the top pane/grid in
|
|
3557
3574
|
* non-chat-only, non-embedded modes) given termRows. Factored out
|
|
3558
3575
|
* so layoutTools.getCurrentLayout can reuse the same math. */
|
|
@@ -3687,6 +3704,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3687
3704
|
// The ACP bridge is created later during dashboard boot. Keep status renders
|
|
3688
3705
|
// safe before then, and bind this reader once the bridge exists.
|
|
3689
3706
|
let activeAcpRuntimeModel = (): string | undefined => undefined;
|
|
3707
|
+
// 상태줄은 provider·model 만 쓴다 — 렌더마다 inspectActiveProvider(자격·authDetail 동기 읽기)를 안 부른다.
|
|
3708
|
+
const readStatusProviderInfo = createStatusProviderInfoReader();
|
|
3709
|
+
const runningBuildStatus = createRunningBuildStatus();
|
|
3690
3710
|
/** Primary pill row — wd · git · provider/model · elapsed.
|
|
3691
3711
|
* MX11b — delegates to mouseWiring.buildStatusLine so pill column
|
|
3692
3712
|
* bounds are captured as a side effect; the return string is
|
|
@@ -3714,7 +3734,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3714
3734
|
return mouseWiring.buildStatusLine({
|
|
3715
3735
|
swd,
|
|
3716
3736
|
gitState,
|
|
3717
|
-
providerInfo:
|
|
3737
|
+
providerInfo: readStatusProviderInfo(activeAcpRuntimeModel()),
|
|
3718
3738
|
contextUsedTokens: metrics.lastContextUsed,
|
|
3719
3739
|
tmuxLabel: tmuxStatusLabel,
|
|
3720
3740
|
sshLabel: sshStatusLabel,
|
|
@@ -3736,44 +3756,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3736
3756
|
// dashboardVoiceChat.onPhaseChange). The status-bar pill is
|
|
3737
3757
|
// intentionally null so we don't render the same state twice.
|
|
3738
3758
|
voiceLabel: null,
|
|
3759
|
+
runningBuild: runningBuildStatus(),
|
|
3739
3760
|
theme,
|
|
3740
3761
|
});
|
|
3741
3762
|
} catch { return ''; }
|
|
3742
3763
|
};
|
|
3743
3764
|
|
|
3744
|
-
/** Secondary metrics row — CTX bar · total cost · t/s · cache hit %.
|
|
3745
|
-
* Reads from the session-metrics singleton populated by runTurn +
|
|
3746
|
-
* dashboard streaming paths, plus the prompt-cache metrics
|
|
3747
|
-
* singleton populated by onUsage. Renders an all-zero row when no
|
|
3748
|
-
* turns have completed yet (matches Claude Code's "0k/N.Nk"
|
|
3749
|
-
* placeholder). */
|
|
3750
|
-
const buildSecondaryStatusLine = (): string => {
|
|
3751
|
-
try {
|
|
3752
|
-
const m = getSessionMetrics();
|
|
3753
|
-
const theme = currentThemeTokens();
|
|
3754
|
-
// Prompt-cache summary is ambient — import synchronously from a
|
|
3755
|
-
// side-effect-free module; no fetch, no state beyond the session
|
|
3756
|
-
// singleton that was already populated by onUsage events.
|
|
3757
|
-
const { getSessionSummary } = require('../prompt-cache/metrics.js') as
|
|
3758
|
-
typeof import('../prompt-cache/metrics.js');
|
|
3759
|
-
const cachePct = getSessionSummary().hitRatePct;
|
|
3760
|
-
// PR-S1V.5 — voice cost month-to-date pill. Reads the
|
|
3761
|
-
// process-wide tracker that voice-input-bridge.transcribe and
|
|
3762
|
-
// the daemon's /v1/voice/transcribe REST handler both write to,
|
|
3763
|
-
// so the pill is the canonical view across surfaces. Empty
|
|
3764
|
-
// string when nothing was spent this month.
|
|
3765
|
-
const voiceMonthly = globalVoiceCostTracker().getMonthSummary().totalUsd;
|
|
3766
|
-
const parts = [
|
|
3767
|
-
ctxBarSegment(m.lastContextUsed, m.lastContextMax, 24),
|
|
3768
|
-
costSegment(m.totalCostUsd, { theme }, m.unpricedTurns),
|
|
3769
|
-
voiceCostSegment(voiceMonthly, { theme }),
|
|
3770
|
-
speedSegment(m.lastTokensPerSec, { theme }),
|
|
3771
|
-
cacheSegment(cachePct, { theme }),
|
|
3772
|
-
];
|
|
3773
|
-
return parts.filter(Boolean).join(' ');
|
|
3774
|
-
} catch { return ''; }
|
|
3775
|
-
};
|
|
3776
|
-
|
|
3777
3765
|
let draw: (opts?: { force?: boolean }) => void = () => {};
|
|
3778
3766
|
let drawNow: (opts?: { force?: boolean }) => void = () => {};
|
|
3779
3767
|
let drawQueued = false;
|
|
@@ -3930,9 +3918,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
3930
3918
|
if (debug.enabled) {
|
|
3931
3919
|
debug.log('status.shell-posture.boot', 'ready', statusShellPostureRuntime.current());
|
|
3932
3920
|
}
|
|
3933
|
-
const blankRows = (height: number, width: number): string[] => (
|
|
3934
|
-
Array.from({ length: Math.max(0, height) }, () => ' '.repeat(Math.max(0, width)))
|
|
3935
|
-
);
|
|
3936
3921
|
// IUL Phase M (Bundle 1) → Phase S·a (Bundle 2) → Bundle 3 wiring.
|
|
3937
3922
|
// Wraps the existing afterRender hook with composeIdentityHooks so
|
|
3938
3923
|
// every modal-kind surface mount/dispose gets a stable ModalIdentity
|
|
@@ -4064,9 +4049,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4064
4049
|
// The 5s poll would eventually catch the change; forcing here
|
|
4065
4050
|
// makes the "*N" suffix tick up within the same frame so the
|
|
4066
4051
|
// user sees feedback for their own action immediately.
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4052
|
+
// TUI-TYPING-LAG — async probe: `git status` must not block key input.
|
|
4053
|
+
void refreshGitDirtyAsync(getSessionCwd(), { force: true })
|
|
4054
|
+
.then(() => { try { draw(); } catch { /* TUI torn down */ } }, () => { /* noop */ });
|
|
4070
4055
|
});
|
|
4071
4056
|
}
|
|
4072
4057
|
// WD2 — session working directory subscription. Status bar pills
|
|
@@ -4098,10 +4083,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4098
4083
|
// Kick off the first dirty probe in the background so the
|
|
4099
4084
|
// "*N" suffix appears on the first meaningful redraw. Throttle
|
|
4100
4085
|
// handles repeated calls; we just fire-and-forget.
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
try { draw(); } catch { /* noop */ }
|
|
4104
|
-
} catch { /* noop */ }
|
|
4086
|
+
void refreshGitDirtyAsync(swd, { force: true })
|
|
4087
|
+
.then(() => { try { draw(); } catch { /* noop */ } }, () => { /* noop */ });
|
|
4105
4088
|
};
|
|
4106
4089
|
wireGitForSwd();
|
|
4107
4090
|
|
|
@@ -4130,14 +4113,15 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4130
4113
|
const gitDirtyTimer: ReturnType<typeof setInterval> = setInterval(() => {
|
|
4131
4114
|
try {
|
|
4132
4115
|
const before = getGitStatusView(getSessionCwd());
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4116
|
+
// TUI-TYPING-LAG — the 5s probe ran `git status` synchronously (130ms+, hundreds under load).
|
|
4117
|
+
void refreshGitDirtyAsync(getSessionCwd(), { minIntervalMs: 4_000 }).then((after) => {
|
|
4118
|
+
if (
|
|
4119
|
+
(before.dirty?.total ?? -1) !== (after.dirty?.total ?? -1) ||
|
|
4120
|
+
(before.aheadBehind?.ahead ?? -1) !== (after.aheadBehind?.ahead ?? -1)
|
|
4121
|
+
) {
|
|
4122
|
+
try { draw(); } catch { /* noop */ }
|
|
4123
|
+
}
|
|
4124
|
+
}, () => { /* noop */ });
|
|
4141
4125
|
} catch { /* noop */ }
|
|
4142
4126
|
}, 5_000);
|
|
4143
4127
|
(gitDirtyTimer as unknown as { unref?: () => void }).unref?.();
|
|
@@ -4398,6 +4382,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4398
4382
|
// requestConfirmation() / HitlConfirm tool reaches the iPhone.
|
|
4399
4383
|
// Fire-and-forget: a listener start failure warns but doesn't
|
|
4400
4384
|
// block dashboard init (HITL degrades to terminal-only / no-op).
|
|
4385
|
+
// #28 — the minimal boot banner holds healthy-boot rows for /status instead of the transcript.
|
|
4386
|
+
const bootBannerMode = normalizeBootBannerMode(getUserConfig().dashboard.bootBanner);
|
|
4387
|
+
const bootBannerHeld: BootBannerHeld = {};
|
|
4401
4388
|
bootDashboardHitl({
|
|
4402
4389
|
enabled: process.env['ELANOUS_HITL_DISABLE'] !== '1',
|
|
4403
4390
|
initDashboardHitl,
|
|
@@ -4407,6 +4394,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4407
4394
|
try { return getPushcutClient().configured; } catch { return false; }
|
|
4408
4395
|
},
|
|
4409
4396
|
pushWarningLine: (message) => { chatLines.push(C.warning(message)); },
|
|
4397
|
+
holdWarningLine: bootBannerMode === 'minimal' ? (message) => { bootBannerHeld.hitl = message; } : undefined,
|
|
4410
4398
|
draw: () => { draw(); },
|
|
4411
4399
|
registerBeforeExit: (cb) => { process.once('beforeExit', cb); },
|
|
4412
4400
|
warnConsole: (message) => { console.warn(message); },
|
|
@@ -4900,6 +4888,15 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4900
4888
|
inputCoreAddDefaultBinding({ matcher: 'ctrl+x s', actionId: 'mode.enter.sync' });
|
|
4901
4889
|
inputCoreAddDefaultBinding({ matcher: 'ctrl+x c', actionId: 'mode.enter.control' });
|
|
4902
4890
|
inputCoreAddDefaultBinding({ matcher: 'ctrl+x g', actionId: 'mode.enter.general' });
|
|
4891
|
+
// #19 — the log-pane `F` bulk fold toggle, shared by the input (`ctrl+x f`) and pane chords.
|
|
4892
|
+
const toggleAllLogFolds = (): void => {
|
|
4893
|
+
const changed = foldStack.toggleAll();
|
|
4894
|
+
debug.log('log.fold', 'toggle-all', { via: 'ctrl+x f', changed });
|
|
4895
|
+
if (changed === 0) pushDebugLine(C.muted('(nothing to fold/unfold)'));
|
|
4896
|
+
chatScrollOffset = -1;
|
|
4897
|
+
draw();
|
|
4898
|
+
};
|
|
4899
|
+
registerLogFoldInputBinding(toggleAllLogFolds);
|
|
4903
4900
|
|
|
4904
4901
|
initTui(true);
|
|
4905
4902
|
|
|
@@ -6246,14 +6243,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6246
6243
|
const active = inspectActiveProvider(cfg);
|
|
6247
6244
|
const reasoningProvider = (['openai-codex', 'openai', 'anthropic', 'gemini', 'grok', 'local', 'openrouter'] as const)
|
|
6248
6245
|
.find(provider => active.provider === provider || active.provider === `auto:${provider}`);
|
|
6249
|
-
|
|
6250
|
-
try {
|
|
6251
|
-
seatsNow = seatsNowLine(readSlashContextNow([]), Date.now());
|
|
6252
|
-
} catch (error) {
|
|
6253
|
-
debug.log('dashboard.status', 'seats-unreadable', { error: String(error) });
|
|
6254
|
-
}
|
|
6255
|
-
return buildDashboardStatusLines({
|
|
6256
|
-
seatsNow,
|
|
6246
|
+
return renderDashboardStatusLines({
|
|
6257
6247
|
provider: active.provider,
|
|
6258
6248
|
model: active.model,
|
|
6259
6249
|
reasoning: reasoningProvider
|
|
@@ -6263,6 +6253,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6263
6253
|
: undefined,
|
|
6264
6254
|
daemonAddress: opts.remote?.url,
|
|
6265
6255
|
daemonConnected: daemonAttached,
|
|
6256
|
+
bootHeld: bootBannerStatusLines(bootBannerHeld),
|
|
6266
6257
|
accountName: active.provider === 'openai-codex' || active.provider === 'auto:openai-codex'
|
|
6267
6258
|
? activeCodexAccountView().name : undefined,
|
|
6268
6259
|
sessionSurfaceLines: buildSessionDashboardStatusLines(resolveSessionSurfaceStatus({ chatModeState })),
|
|
@@ -6337,7 +6328,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6337
6328
|
const { rows: tr } = termSize();
|
|
6338
6329
|
const sH = computeLogH(tr);
|
|
6339
6330
|
const halfSH = Math.max(1, Math.floor(sH / 2));
|
|
6340
|
-
const maxScr =
|
|
6331
|
+
const maxScr = logScrollMax(sH);
|
|
6341
6332
|
// Scroll keys → Scrollable behavior on wd-log. Sync pre/post.
|
|
6342
6333
|
const SCROLL_KEYS = new Set([
|
|
6343
6334
|
'j', 'k', 'down', 'up',
|
|
@@ -6527,7 +6518,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6527
6518
|
const attachChatStreamingKeys = (abortCtrl: AbortController): () => void => {
|
|
6528
6519
|
const chatLinesStartIndex = chatLines.length;
|
|
6529
6520
|
userScrolledDuringStream = false;
|
|
6530
|
-
streamingInFlight = true;
|
|
6531
6521
|
const SCROLL_KEYS = new Set(['j', 'k', 'down', 'up', 'pageup', 'pagedown', 'home', 'end', 'g', 'G']);
|
|
6532
6522
|
// T3-B2: ESC abort gate. When the user hits Esc AND sub-agent
|
|
6533
6523
|
// work is running, pop a confirmation modal before tearing down
|
|
@@ -6597,7 +6587,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6597
6587
|
const scrollLogBy = (delta: number): void => {
|
|
6598
6588
|
const { rows: tr } = termSize();
|
|
6599
6589
|
const sH = computeLogH(tr);
|
|
6600
|
-
const maxScr =
|
|
6590
|
+
const maxScr = logScrollMax(sH);
|
|
6601
6591
|
const cur = chatScrollOffset < 0 ? maxScr : chatScrollOffset;
|
|
6602
6592
|
let next = Math.max(0, Math.min(cur + delta, maxScr));
|
|
6603
6593
|
if (delta > 0 && next >= maxScr) next = -1; // re-pin tail
|
|
@@ -6827,7 +6817,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6827
6817
|
// ⛔ 중복 전송 방지가 계약이다: 레지스트리가 배수되면 `onDrained` 가
|
|
6828
6818
|
// ***여기 큐도 같이 비운다*** — 안 그러면 같은 발화가 턴 끝에 «또» 나간다.
|
|
6829
6819
|
// ⚠️ `immediate`(슬래시 등)는 지금 실행되므로 «넣지 않는다».
|
|
6830
|
-
|
|
6820
|
+
// ⛔ H2 — 내부 턴 중의 사람 입력은 그 턴에 끼워 넣지 않는다(턴 끝 FIFO 로 기다린다).
|
|
6821
|
+
if (r.submissionDisposition === 'fifo' && !r.immediateSubmission && !internalTurnGate.isInternalTurnActive()) {
|
|
6831
6822
|
const queuedText = turnTypeaheadRef.state.queuedSubmissions.at(-1);
|
|
6832
6823
|
const sid = activeSessionIdForTypeahead();
|
|
6833
6824
|
if (queuedText && sid) {
|
|
@@ -6842,22 +6833,38 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6842
6833
|
},
|
|
6843
6834
|
});
|
|
6844
6835
|
if (queuedHistoryEntry) refreshInputHistory();
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
for (const text of drained) {
|
|
6836
|
+
// ⭐ 레지스트리 id 로 짝짓는다 — 배수·되꺼내기·턴 종료 승격이 같은 항목을 «한 번만» 보낸다.
|
|
6837
|
+
// ⛔ 슬래시는 안 넣는다(턴 종료 FIFO 로만 실행).
|
|
6838
|
+
steerLastTurnTypeaheadSubmission(turnTypeaheadRef, sid, ({ texts, remaining }) => {
|
|
6839
|
+
for (const text of texts) {
|
|
6850
6840
|
echoSubmittedUserText(text, { live: true });
|
|
6851
6841
|
debug.log('dashboard.turn-typeahead', 'echoed', { path: 'live', length: text.length });
|
|
6852
6842
|
}
|
|
6853
|
-
if (
|
|
6843
|
+
if (texts.length > 0) draw();
|
|
6854
6844
|
debug.log('dashboard.turn-typeahead', 'drained-into-live-turn', {
|
|
6855
|
-
drained:
|
|
6845
|
+
drained: texts.length, remaining,
|
|
6856
6846
|
});
|
|
6857
6847
|
});
|
|
6858
6848
|
}
|
|
6859
6849
|
}
|
|
6860
6850
|
}
|
|
6851
|
+
if (r.releasedPendingIds) releaseTurnTypeaheadPending(r.releasedPendingIds, 'recalled');
|
|
6852
|
+
if (r.pushNowRequested) {
|
|
6853
|
+
const decision = runTurnTypeaheadPushNow(turnTypeaheadRef, {
|
|
6854
|
+
runningToolNames: () => getEscAbortWaitingToolNames(escAbortToolState),
|
|
6855
|
+
interruptTurn: () => {
|
|
6856
|
+
steerInterruptRef.pending = true;
|
|
6857
|
+
try { globalAgentRegistry.markBackgroundAllRunning(); } catch { /* 승격 실패가 끊기를 막지 않는다 */ }
|
|
6858
|
+
abortTurnOnly(abortCtrl);
|
|
6859
|
+
},
|
|
6860
|
+
pushNotice: (line) => pushChatLine(C.muted(line)),
|
|
6861
|
+
});
|
|
6862
|
+
debug.log('dashboard.turn-typeahead', 'push-now', {
|
|
6863
|
+
kind: decision.kind,
|
|
6864
|
+
tools: decision.kind === 'interrupt' ? decision.runningTools : decision.kind === 'nothing' ? [] : decision.blockingTools,
|
|
6865
|
+
});
|
|
6866
|
+
draw();
|
|
6867
|
+
}
|
|
6861
6868
|
if (r.immediateSubmission) {
|
|
6862
6869
|
turnTypeaheadRef.state = await dispatchStreamingTurnTypeaheadSubmission(
|
|
6863
6870
|
turnTypeaheadRef.state,
|
|
@@ -7083,12 +7090,30 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
7083
7090
|
// `runStreamingKeyUnifiedDispatch` above for the route body.
|
|
7084
7091
|
await runStreamingKeyUnifiedDispatch(key);
|
|
7085
7092
|
});
|
|
7093
|
+
// ⛔ TUI-STALE-BUSY-MARKER — busy 는 «끄는 closure 를 돌려주기 직전»에 켠다. 위 준비(ESC 게이트·키 리스너)가
|
|
7094
|
+
// 던지면 호출자는 cleanup 을 못 받으므로, 먼저 켜 두면 아무도 못 끄는 busy 가 남는다(#37).
|
|
7095
|
+
streamingInFlight = true;
|
|
7096
|
+
reportTuiTurn('busy');
|
|
7097
|
+
const externalStop = (): void => { stopTurnViaEscGate(escGate); };
|
|
7098
|
+
currentTurnExternalStop = externalStop;
|
|
7086
7099
|
return () => {
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7100
|
+
if (currentTurnExternalStop === externalStop) currentTurnExternalStop = null;
|
|
7101
|
+
// 단계마다 격리 — 앞 단계가 던져도 settle·idle 전이에 닿는다(키 층 유휴 ∧ 턴 층 busy 이원 판정 #55 차단).
|
|
7102
|
+
runTurnTeardownStep('stream-keys-detach', cleanup);
|
|
7103
|
+
runTurnTeardownStep('esc-gate-dispose', () => { escGate.dispose(); });
|
|
7104
|
+
try {
|
|
7105
|
+
if (!stoppedHarnessSpace) settleEscAbortPendingLines(chatLines, chatLinesStartIndex);
|
|
7106
|
+
} catch (err) {
|
|
7107
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7108
|
+
const stackHead = err instanceof Error ? err.stack?.split('\n')[1]?.trim() : undefined;
|
|
7109
|
+
debug.log('dashboard.turn', 'settle-failed', { message, stackHead });
|
|
7110
|
+
logTurnThrew('settle-esc-lines', err);
|
|
7111
|
+
} finally {
|
|
7112
|
+
userScrolledDuringStream = false;
|
|
7113
|
+
streamingInFlight = false;
|
|
7114
|
+
reportTuiTurn('idle');
|
|
7115
|
+
seatAutowake.noteTurnEnded();
|
|
7116
|
+
}
|
|
7092
7117
|
};
|
|
7093
7118
|
};
|
|
7094
7119
|
|
|
@@ -7332,9 +7357,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
7332
7357
|
?? viewRegistry.views[0]!;
|
|
7333
7358
|
setActiveDashboardView(def);
|
|
7334
7359
|
};
|
|
7335
|
-
const reloadDashboardViews = (): void => {
|
|
7336
|
-
applyDashboardViewsRuntime(getUserConfig().dashboard.views as any);
|
|
7337
|
-
};
|
|
7338
7360
|
const userClosedPanes = new Set<PaneFocus>();
|
|
7339
7361
|
const closedPaneSet = (): ReadonlySet<PaneFocus> => {
|
|
7340
7362
|
const closed = new Set(userClosedPanes);
|
|
@@ -8298,7 +8320,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
8298
8320
|
}
|
|
8299
8321
|
return;
|
|
8300
8322
|
}
|
|
8301
|
-
const maxScr =
|
|
8323
|
+
const maxScr = logScrollMax(logViewport.height);
|
|
8302
8324
|
const delta = m.type === 'scroll-up' ? -3 : 3;
|
|
8303
8325
|
const before = chatScrollOffset;
|
|
8304
8326
|
chatScrollOffset = Math.max(0, Math.min(
|
|
@@ -9367,24 +9389,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
9367
9389
|
limit,
|
|
9368
9390
|
});
|
|
9369
9391
|
};
|
|
9370
|
-
const describePaneStateForPrompt = (): string => {
|
|
9371
|
-
const state = paneStateSnapshot();
|
|
9372
|
-
const rows = state.panes.map(p => {
|
|
9373
|
-
const bits = [
|
|
9374
|
-
p.visible ? 'visible' : 'hidden',
|
|
9375
|
-
p.closed ? 'closed' : '',
|
|
9376
|
-
p.closeable ? 'closeable' : 'primary',
|
|
9377
|
-
p.omittedReason ? `omitted=${p.omittedReason}` : '',
|
|
9378
|
-
].filter(Boolean).join(', ');
|
|
9379
|
-
return `- ${p.pane}: ${bits}`;
|
|
9380
|
-
});
|
|
9381
|
-
return [
|
|
9382
|
-
'## Dashboard Pane State',
|
|
9383
|
-
'',
|
|
9384
|
-
`Active view: ${state.activeViewId} "${state.viewLabel}" (baseView=${state.baseView}, compact=${state.compactLevel}, focus=${state.focused})`,
|
|
9385
|
-
...rows,
|
|
9386
|
-
].join('\n');
|
|
9387
|
-
};
|
|
9388
9392
|
// Surface-unification v2.2 V2.2-5 Part 2 (2026-05-11) — schedulerView
|
|
9389
9393
|
// state · openSchedulerForm · activeSchedulerFormValue /
|
|
9390
9394
|
// setActiveSchedulerFormValue · submitSchedulerForm 모두 폐기.
|
|
@@ -9446,11 +9450,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
9446
9450
|
);
|
|
9447
9451
|
};
|
|
9448
9452
|
|
|
9449
|
-
// Plain row label used as the list widget's identity (selection
|
|
9450
|
-
// matching, cursor display string). Kept ANSI-free so visibleWidth
|
|
9451
|
-
// and the selected-set string lookup work correctly.
|
|
9452
|
-
const fmtEntry = (e: FsEntry): string => e.isDir ? `${e.name}/` : e.name;
|
|
9453
|
-
|
|
9454
9453
|
// Colored variant for the list widget's `items` array — yazi-style
|
|
9455
9454
|
// accent for folders, per-extension fileColor for files. The list
|
|
9456
9455
|
// widget renders these straight through when preserveAnsi is true.
|
|
@@ -9887,92 +9886,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
9887
9886
|
);
|
|
9888
9887
|
};
|
|
9889
9888
|
|
|
9890
|
-
// ── Workspace context builder ──
|
|
9891
|
-
// Emitted once per chat turn (prepended to the user message) so the
|
|
9892
|
-
// LLM knows what widgets are currently on screen and how to write
|
|
9893
|
-
// to them via layout_setWidgetState / layout_addWidget without
|
|
9894
|
-
// having to probe layout_getState first. Kept compact — iteration
|
|
9895
|
-
// order matches visual layout (top-to-bottom, left-to-right).
|
|
9896
|
-
//
|
|
9897
|
-
// Writable surfaces:
|
|
9898
|
-
// wd-scratch — primary canvas for notes / reports. Persists
|
|
9899
|
-
// until the user clears or switches to memo mode.
|
|
9900
|
-
// wd-preview — secondary canvas. Persists until the user
|
|
9901
|
-
// navigates to a different file in the browser
|
|
9902
|
-
// (file nav overwrites, which is by design).
|
|
9903
|
-
//
|
|
9904
|
-
// Reserved (LLM writes rejected with a helpful error):
|
|
9905
|
-
// wd-browser, wd-log — dashboard re-renders these every frame
|
|
9906
|
-
// from its own buffers (file list, chat
|
|
9907
|
-
// transcript). Use layout_addWidget to
|
|
9908
|
-
// spawn a fresh widget the dashboard will
|
|
9909
|
-
// NOT touch.
|
|
9910
|
-
const buildWorkspaceContext = (): string => {
|
|
9911
|
-
try {
|
|
9912
|
-
const pluginLayout = pluginHost.activeLayout();
|
|
9913
|
-
let layoutForCtx: Layout;
|
|
9914
|
-
if (pluginLayout) {
|
|
9915
|
-
layoutForCtx = pluginLayout;
|
|
9916
|
-
} else {
|
|
9917
|
-
const { cols, rows: tr } = termSize();
|
|
9918
|
-
const promptFrame = getLayoutPromptFrame(tr);
|
|
9919
|
-
const topHeight = computePromptFrameGridHeight(tr, promptFrame);
|
|
9920
|
-
layoutForCtx = workingDirLayoutForView(cols - 1, topHeight);
|
|
9921
|
-
}
|
|
9922
|
-
const visibleWidgets: Array<{ id: string; type: string; title?: string }> = [];
|
|
9923
|
-
for (const row of layoutForCtx.rows) {
|
|
9924
|
-
for (const cell of row.cells) {
|
|
9925
|
-
if (!cell.widgetInstanceId) continue;
|
|
9926
|
-
const inst = widgetHost.get(cell.widgetInstanceId);
|
|
9927
|
-
if (!inst) continue;
|
|
9928
|
-
visibleWidgets.push({
|
|
9929
|
-
id: inst.id,
|
|
9930
|
-
type: inst.type,
|
|
9931
|
-
title: inst.character || undefined,
|
|
9932
|
-
});
|
|
9933
|
-
}
|
|
9934
|
-
}
|
|
9935
|
-
if (visibleWidgets.length === 0) return '';
|
|
9936
|
-
const rows = visibleWidgets.map(w => {
|
|
9937
|
-
const tag =
|
|
9938
|
-
w.id === 'wd-scratch' ? ' ← writable canvas; persistent'
|
|
9939
|
-
: w.id === 'wd-preview' ? ' ← writable canvas; overwritten on file-nav'
|
|
9940
|
-
: w.id === 'wd-browser' ? ' ← READ-ONLY (dashboard-managed file list)'
|
|
9941
|
-
: w.id === 'wd-log' ? ' ← READ-ONLY (chat transcript)'
|
|
9942
|
-
: w.id.startsWith('wd-debug-') ? ' ← READ-ONLY (debug event surface; use debug_* tools)'
|
|
9943
|
-
: ' ← persistent (spawned by LLM)';
|
|
9944
|
-
const label = w.title ? ` "${w.title}"` : '';
|
|
9945
|
-
return `- ${w.id} (${w.type})${label}${tag}`;
|
|
9946
|
-
}).join('\n');
|
|
9947
|
-
return [
|
|
9948
|
-
'## Current dashboard workspace',
|
|
9949
|
-
'',
|
|
9950
|
-
'The following widgets are visible right now. You have three host-level tools to mutate the layout:',
|
|
9951
|
-
'- `layout_getState` — refresh view of the grid.',
|
|
9952
|
-
'- `layout_setWidgetState({id, patch})` — merge a patch into a widget\'s state. For markdown widgets patch `{text: "..."}`.',
|
|
9953
|
-
'- `layout_addWidget({type, row, col, character, config})` — spawn a new widget; dashboard will NOT re-render its state.',
|
|
9954
|
-
'',
|
|
9955
|
-
rows,
|
|
9956
|
-
'',
|
|
9957
|
-
'Writing rules:',
|
|
9958
|
-
'- To SHOW the user multi-line output (notes, analyses, lists, reports), write to `wd-scratch` via `layout_setWidgetState({id:"wd-scratch", patch:{text:"<content>"}})`. The dashboard auto-reopens that pane if the user had it closed.',
|
|
9959
|
-
'- For a secondary canvas (e.g. side-by-side comparison), write to `wd-preview` the same way.',
|
|
9960
|
-
'- DO NOT write to `wd-browser` or `wd-log` — the dashboard rejects those patches with an error.',
|
|
9961
|
-
'- DO NOT write to `wd-debug-*`; use `debug_getState`, `debug_getCallStack`, `debug_setLevel`, `debug_openView`, and `debug_selectEvent`.',
|
|
9962
|
-
'- For your own persistent widget (won\'t be overwritten by anything), spawn with `layout_addWidget` and pick your own id.',
|
|
9963
|
-
'',
|
|
9964
|
-
describeDashboardViewsForPrompt(viewRegistry),
|
|
9965
|
-
'',
|
|
9966
|
-
describePaneStateForPrompt(),
|
|
9967
|
-
].join('\n');
|
|
9968
|
-
} catch {
|
|
9969
|
-
// Workspace snapshot is best-effort — a failure here must not
|
|
9970
|
-
// break the chat turn. Return empty so the user message flows
|
|
9971
|
-
// through unchanged.
|
|
9972
|
-
return '';
|
|
9973
|
-
}
|
|
9974
|
-
};
|
|
9975
|
-
|
|
9976
9889
|
// ── LLM layout tools (W5) ──
|
|
9977
9890
|
// Host-level tools that let the chat LLM reshape the dashboard.
|
|
9978
9891
|
// When a plugin is active the tools edit its layout directly; when
|
|
@@ -11733,63 +11646,53 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11733
11646
|
let agentSearchModal: SearchModalHandle | null = null;
|
|
11734
11647
|
let agentSearchHandle: { dispose: () => void } | null = null;
|
|
11735
11648
|
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
},
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
|
|
11783
|
-
}
|
|
11784
|
-
closeAgentRosterSearch();
|
|
11785
|
-
},
|
|
11786
|
-
onCancel: () => closeAgentRosterSearch(),
|
|
11787
|
-
theme: currentThemeTokens(),
|
|
11788
|
-
});
|
|
11789
|
-
attachSurfaceToWorkspace(handle.surface, currentWorkspaceOwnerId());
|
|
11790
|
-
agentSearchModal = handle;
|
|
11791
|
-
agentSearchHandle = display.pushModal(handle.surface);
|
|
11792
|
-
}
|
|
11649
|
+
// TUI-DIET-SEAM-PICKERS — the five input pickers (agent-roster search,
|
|
11650
|
+
// transfer, Ctrl+T file finder, Alt+C directory finder, Ctrl+K SSH)
|
|
11651
|
+
// live in src/tui/input/pickers.ts. Mutable dashboard state crosses
|
|
11652
|
+
// the seam as live get/set accessors (never a value copy); `draw` is
|
|
11653
|
+
// re-read at call time because it is reassigned after mount.
|
|
11654
|
+
const {
|
|
11655
|
+
openAgentRosterSearch,
|
|
11656
|
+
openTransferPicker,
|
|
11657
|
+
openFinderPicker,
|
|
11658
|
+
openDirectoryPicker,
|
|
11659
|
+
openSshPicker,
|
|
11660
|
+
} = createInputPickers({
|
|
11661
|
+
state: {
|
|
11662
|
+
get agentSearchModal() { return agentSearchModal; },
|
|
11663
|
+
set agentSearchModal(v: SearchModalHandle | null) { agentSearchModal = v; },
|
|
11664
|
+
get agentSearchHandle() { return agentSearchHandle; },
|
|
11665
|
+
set agentSearchHandle(v: { dispose: () => void } | null) { agentSearchHandle = v; },
|
|
11666
|
+
get agentRosterCursor() { return agentRosterCursor; },
|
|
11667
|
+
set agentRosterCursor(v: number) { agentRosterCursor = v; },
|
|
11668
|
+
get agentCursorLockedId() { return agentCursorLockedId; },
|
|
11669
|
+
set agentCursorLockedId(v: string) { agentCursorLockedId = v; },
|
|
11670
|
+
get agentCursorManual() { return agentCursorManual; },
|
|
11671
|
+
set agentCursorManual(v: boolean) { agentCursorManual = v; },
|
|
11672
|
+
get chatScrollOffset() { return chatScrollOffset; },
|
|
11673
|
+
set chatScrollOffset(v: number) { chatScrollOffset = v; },
|
|
11674
|
+
get chatLines() { return chatLines; },
|
|
11675
|
+
},
|
|
11676
|
+
display,
|
|
11677
|
+
agentSurfaceStore,
|
|
11678
|
+
workingDir,
|
|
11679
|
+
dockedPreview,
|
|
11680
|
+
transferFeedbackRuntime,
|
|
11681
|
+
finderFeedbackRuntime,
|
|
11682
|
+
currentThemeTokens,
|
|
11683
|
+
currentWorkspaceOwnerId,
|
|
11684
|
+
activeBrowserWidgetId,
|
|
11685
|
+
resolveBrowserStateById,
|
|
11686
|
+
fmtBytes,
|
|
11687
|
+
runTransfer,
|
|
11688
|
+
closeAgentRosterSearch,
|
|
11689
|
+
pushDebugLine,
|
|
11690
|
+
refreshRemotePreviewBridge,
|
|
11691
|
+
refreshWorkingDirPreview,
|
|
11692
|
+
setWorkingFocus,
|
|
11693
|
+
startRemoteModeBridge,
|
|
11694
|
+
draw: () => draw(),
|
|
11695
|
+
});
|
|
11793
11696
|
|
|
11794
11697
|
function closeAgentRosterSearch(): void {
|
|
11795
11698
|
if (agentSearchHandle) { agentSearchHandle.dispose(); agentSearchHandle = null; }
|
|
@@ -11797,75 +11700,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11797
11700
|
draw();
|
|
11798
11701
|
}
|
|
11799
11702
|
|
|
11800
|
-
/** T5-J5 — open the transfer picker. Files arg = local paths to
|
|
11801
|
-
* ship; destination comes from the picker. Handles both ssh and
|
|
11802
|
-
* iphone target kinds. Remote-mode browser isn't supported here
|
|
11803
|
-
* yet — the user is transferring LOCAL files (focused entry +
|
|
11804
|
-
* selected set) to a remote destination. Transferring REMOTE→X
|
|
11805
|
-
* is a separate flow (not this phase). */
|
|
11806
|
-
function openTransferPicker(
|
|
11807
|
-
files: Array<{ localPath: string; size?: number }>,
|
|
11808
|
-
browserId: string = activeBrowserWidgetId(),
|
|
11809
|
-
): void {
|
|
11810
|
-
const targetBrowser = resolveBrowserStateById(browserId) ?? (workingDir as BrowserPaneModel);
|
|
11811
|
-
if (files.length === 0) {
|
|
11812
|
-
transferFeedbackRuntime.onNoFilesToTransfer();
|
|
11813
|
-
return;
|
|
11814
|
-
}
|
|
11815
|
-
const capability = resolveBrowserTransferCapability(
|
|
11816
|
-
targetBrowser,
|
|
11817
|
-
listTransferTargets(),
|
|
11818
|
-
);
|
|
11819
|
-
if (!capability.enabled) {
|
|
11820
|
-
transferFeedbackRuntime.onTransferDisabled(capability.reason);
|
|
11821
|
-
return;
|
|
11822
|
-
}
|
|
11823
|
-
const targets = capability.targets;
|
|
11824
|
-
if (targets.length === 0) {
|
|
11825
|
-
transferFeedbackRuntime.onNoTransferTargets();
|
|
11826
|
-
return;
|
|
11827
|
-
}
|
|
11828
|
-
const totalBytes = files.reduce((a, f) => a + (f.size ?? 0), 0);
|
|
11829
|
-
const summary = `${files.length} file${files.length === 1 ? '' : 's'}${
|
|
11830
|
-
totalBytes > 0 ? ` (${fmtBytes(totalBytes)})` : ''
|
|
11831
|
-
}`;
|
|
11832
|
-
|
|
11833
|
-
const { cols, rows } = termSize();
|
|
11834
|
-
const width = Math.min(74, Math.max(46, cols - 6));
|
|
11835
|
-
const visibleRows = Math.min(10, targets.length);
|
|
11836
|
-
const height = visibleRows + 3;
|
|
11837
|
-
const bounds = {
|
|
11838
|
-
row: Math.max(1, Math.floor((rows - height) / 2)),
|
|
11839
|
-
col: Math.max(1, Math.floor((cols - width) / 2)),
|
|
11840
|
-
width,
|
|
11841
|
-
height,
|
|
11842
|
-
};
|
|
11843
|
-
let handle: ReturnType<typeof display.pushModal> | null = null;
|
|
11844
|
-
const picker = createTransferPickerModal({
|
|
11845
|
-
targets,
|
|
11846
|
-
bounds,
|
|
11847
|
-
width,
|
|
11848
|
-
maxVisible: visibleRows,
|
|
11849
|
-
summary,
|
|
11850
|
-
theme: currentThemeTokens(),
|
|
11851
|
-
onAccept: (target) => {
|
|
11852
|
-
agentSearchModal = null;
|
|
11853
|
-
handle?.dispose();
|
|
11854
|
-
void runTransfer(target, files);
|
|
11855
|
-
draw();
|
|
11856
|
-
},
|
|
11857
|
-
onCancel: () => {
|
|
11858
|
-
agentSearchModal = null;
|
|
11859
|
-
handle?.dispose();
|
|
11860
|
-
draw();
|
|
11861
|
-
},
|
|
11862
|
-
});
|
|
11863
|
-
attachSurfaceToWorkspace(picker.surface, currentWorkspaceOwnerId());
|
|
11864
|
-
handle = display.pushModal(picker.surface);
|
|
11865
|
-
agentSearchModal = picker;
|
|
11866
|
-
draw();
|
|
11867
|
-
}
|
|
11868
|
-
|
|
11869
11703
|
async function runTransfer(
|
|
11870
11704
|
target: TransferTarget,
|
|
11871
11705
|
files: Array<{ localPath: string; size?: number }>,
|
|
@@ -11892,465 +11726,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
11892
11726
|
}
|
|
11893
11727
|
}
|
|
11894
11728
|
|
|
11895
|
-
/** T4-F2 / T4-F3 — open the Ctrl+T finder modal. Scans the
|
|
11896
|
-
* current working dir (local only at MVP; remote scan is a
|
|
11897
|
-
* follow-up). On accept, cd's the browser pane to the file's
|
|
11898
|
-
* parent dir, focuses the file in the listing, and switches
|
|
11899
|
-
* pane focus to browser so the user can e/enter next. */
|
|
11900
|
-
async function openFinderPicker(): Promise<void> {
|
|
11901
|
-
const targetBrowserId = activeBrowserWidgetId();
|
|
11902
|
-
const targetBrowser = resolveBrowserStateById(targetBrowserId) ?? (workingDir as BrowserPaneModel);
|
|
11903
|
-
// T5-I2 — branch on remote vs local. Both paths produce the
|
|
11904
|
-
// same {absPath, relPath} finder items so the modal + onAccept
|
|
11905
|
-
// handler stay unified.
|
|
11906
|
-
let finderItems: FinderItem[] = [];
|
|
11907
|
-
let backend = 'fd';
|
|
11908
|
-
let truncated = false;
|
|
11909
|
-
let scanDurationMs = 0;
|
|
11910
|
-
|
|
11911
|
-
if (workingDir.remote) {
|
|
11912
|
-
const host = workingDir.remote.host;
|
|
11913
|
-
const remoteRoot = workingDir.remote.cwd;
|
|
11914
|
-
finderFeedbackRuntime.onFinderScanStarted(`${host.name}:${remoteRoot}`);
|
|
11915
|
-
draw();
|
|
11916
|
-
const scan = await scanRemoteFinder({ host, root: remoteRoot });
|
|
11917
|
-
const rels = relativizeRemoteResults(scan.paths, remoteRoot);
|
|
11918
|
-
finderItems = scan.paths.map((absPath, i) => ({
|
|
11919
|
-
absPath, relPath: rels[i] ?? absPath,
|
|
11920
|
-
}));
|
|
11921
|
-
backend = `${scan.backend}@${scan.host}`;
|
|
11922
|
-
truncated = scan.truncated;
|
|
11923
|
-
scanDurationMs = scan.durationMs;
|
|
11924
|
-
} else {
|
|
11925
|
-
// 2026-05-03 — scan from the Session Working Directory (the
|
|
11926
|
-
// "project root"), NOT the browser pane's current navigation
|
|
11927
|
-
// cursor. SWD is mutable only via Ctrl+W / /wd / SetWorkingDir
|
|
11928
|
-
// tool — exactly the user's intent for "project root". Browser
|
|
11929
|
-
// pane's cwd is purely UI navigation and should not narrow
|
|
11930
|
-
// Ctrl+T's search scope (matches working-dir.ts:5-8 contract).
|
|
11931
|
-
const scanRoot = getSessionCwd();
|
|
11932
|
-
finderFeedbackRuntime.onFinderScanStarted(scanRoot);
|
|
11933
|
-
draw();
|
|
11934
|
-
const scan = await scanFinder({ root: scanRoot });
|
|
11935
|
-
const rels = relativizeResults(scan.paths, scanRoot);
|
|
11936
|
-
finderItems = scan.paths.map((absPath, i) => ({
|
|
11937
|
-
absPath, relPath: rels[i] ?? absPath,
|
|
11938
|
-
}));
|
|
11939
|
-
backend = scan.backend;
|
|
11940
|
-
truncated = scan.truncated;
|
|
11941
|
-
scanDurationMs = scan.durationMs;
|
|
11942
|
-
}
|
|
11943
|
-
if (finderItems.length === 0) {
|
|
11944
|
-
finderFeedbackRuntime.onFinderScanEmpty();
|
|
11945
|
-
draw();
|
|
11946
|
-
return;
|
|
11947
|
-
}
|
|
11948
|
-
finderFeedbackRuntime.onFinderScanCompleted({
|
|
11949
|
-
count: finderItems.length,
|
|
11950
|
-
backend,
|
|
11951
|
-
truncated,
|
|
11952
|
-
durationMs: scanDurationMs,
|
|
11953
|
-
});
|
|
11954
|
-
|
|
11955
|
-
const { cols, rows } = termSize();
|
|
11956
|
-
const width = Math.min(90, Math.max(50, cols - 6));
|
|
11957
|
-
const visibleRows = Math.min(15, Math.max(6, rows - 8));
|
|
11958
|
-
const height = visibleRows + 3;
|
|
11959
|
-
const bounds = {
|
|
11960
|
-
row: Math.max(1, Math.floor((rows - height) / 2)),
|
|
11961
|
-
col: Math.max(1, Math.floor((cols - width) / 2)),
|
|
11962
|
-
width,
|
|
11963
|
-
height,
|
|
11964
|
-
};
|
|
11965
|
-
let handle: ReturnType<typeof display.pushModal> | null = null;
|
|
11966
|
-
// Title reflects the actual scan root — SWD for local (project
|
|
11967
|
-
// root), remote cwd for remote mode. Browser pane's nav cursor
|
|
11968
|
-
// is intentionally NOT shown because the scan ignores it.
|
|
11969
|
-
const titleRoot = workingDir.remote
|
|
11970
|
-
? `${workingDir.remote.host.name}:${workingDir.remote.cwd}`
|
|
11971
|
-
: getSessionCwd().replace(homedir(), '~');
|
|
11972
|
-
// 2026-05-03 — preview modes (yazi/fzf-style, on-demand).
|
|
11973
|
-
//
|
|
11974
|
-
// Default mode is OFF — the picker opens cleanly with NO preview
|
|
11975
|
-
// so it's never annoying on tablet / narrow terminals. The user
|
|
11976
|
-
// toggles preview behaviour with `Ctrl+/` (matches zshrc
|
|
11977
|
-
// `--bind 'ctrl-/:toggle-preview'`):
|
|
11978
|
-
//
|
|
11979
|
-
// off → hover → 2pane → off (cycle)
|
|
11980
|
-
//
|
|
11981
|
-
// • off — no preview, ever. The cursor moves freely.
|
|
11982
|
-
// • hover — popup appears after the cursor stays still for
|
|
11983
|
-
// 1 second (HOVER_DELAY_MS). Popup auto-dismisses
|
|
11984
|
-
// after 4s. Centered, the same width as legacy
|
|
11985
|
-
// behaviour but on-demand.
|
|
11986
|
-
// • 2pane — browser-preview style: persistent preview popup
|
|
11987
|
-
// docked to the right of the picker. Updates
|
|
11988
|
-
// instantly as the cursor moves. Falls back to
|
|
11989
|
-
// hover if the terminal is too narrow (<140 cols).
|
|
11990
|
-
//
|
|
11991
|
-
// showPreviewModal supports remote-skip by itself (preview
|
|
11992
|
-
// handlers read local fs), so we simply guard with workingDir
|
|
11993
|
-
// .remote before scheduling.
|
|
11994
|
-
type PreviewMode = 'off' | 'hover' | '2pane';
|
|
11995
|
-
const HOVER_DELAY_MS = 1000;
|
|
11996
|
-
const TWO_PANE_MIN_COLS = 140;
|
|
11997
|
-
const TWO_PANE_GROUP = 'finder-2pane-preview';
|
|
11998
|
-
|
|
11999
|
-
let previewMode: PreviewMode = 'off';
|
|
12000
|
-
let lastSelected: FinderItem | null = null;
|
|
12001
|
-
let hoverTimer: ReturnType<typeof setTimeout> | null = null;
|
|
12002
|
-
let lastPreviewHandle: { dispose(): void } | null = null;
|
|
12003
|
-
|
|
12004
|
-
const clearHoverTimer = () => {
|
|
12005
|
-
if (hoverTimer !== null) { clearTimeout(hoverTimer); hoverTimer = null; }
|
|
12006
|
-
};
|
|
12007
|
-
const disposeActivePreview = () => {
|
|
12008
|
-
if (lastPreviewHandle) {
|
|
12009
|
-
try { lastPreviewHandle.dispose(); } catch { /* ignore */ }
|
|
12010
|
-
lastPreviewHandle = null;
|
|
12011
|
-
}
|
|
12012
|
-
};
|
|
12013
|
-
|
|
12014
|
-
/** Compute the docked-preview bounds for 2-pane mode. Picker
|
|
12015
|
-
* centred at width≈90; preview takes the right gutter. Returns
|
|
12016
|
-
* null when there isn't enough room (caller falls back to
|
|
12017
|
-
* hover). */
|
|
12018
|
-
const computeDockedPreviewBounds = (): {
|
|
12019
|
-
row: number; col: number; width: number; height: number;
|
|
12020
|
-
} | null => {
|
|
12021
|
-
const { cols: tc, rows: tr } = termSize();
|
|
12022
|
-
if (tc < TWO_PANE_MIN_COLS) return null;
|
|
12023
|
-
const pickerWidth = Math.min(90, Math.max(50, tc - 6));
|
|
12024
|
-
const pickerCol = Math.max(1, Math.floor((tc - pickerWidth) / 2));
|
|
12025
|
-
const previewCol = pickerCol + pickerWidth + 2;
|
|
12026
|
-
const previewWidth = Math.max(40, tc - previewCol - 2);
|
|
12027
|
-
if (previewWidth < 40) return null;
|
|
12028
|
-
const previewHeight = Math.max(8, Math.min(tr - 6, 28));
|
|
12029
|
-
const previewRow = Math.max(1, Math.floor((tr - previewHeight) / 2));
|
|
12030
|
-
return { row: previewRow, col: previewCol, width: previewWidth, height: previewHeight };
|
|
12031
|
-
};
|
|
12032
|
-
|
|
12033
|
-
const paintPreviewFor = (item: FinderItem) => {
|
|
12034
|
-
if (workingDir.remote) return; // remote previews unsupported
|
|
12035
|
-
const { rows: tr, cols: tc } = termSize();
|
|
12036
|
-
const docked = previewMode === '2pane' ? computeDockedPreviewBounds() : null;
|
|
12037
|
-
void showPreviewModal(item.absPath, {
|
|
12038
|
-
coordinator: display,
|
|
12039
|
-
termCols: tc,
|
|
12040
|
-
termRows: tr,
|
|
12041
|
-
title: item.relPath,
|
|
12042
|
-
ttlMs: previewMode === '2pane' ? 0 : 4000,
|
|
12043
|
-
bounds: docked ?? undefined,
|
|
12044
|
-
group: previewMode === '2pane' ? TWO_PANE_GROUP : 'image-preview',
|
|
12045
|
-
}).then((r) => {
|
|
12046
|
-
lastPreviewHandle = r?.handle ?? null;
|
|
12047
|
-
}).catch(() => { /* preview failure is non-fatal */ });
|
|
12048
|
-
};
|
|
12049
|
-
|
|
12050
|
-
const scheduleHoverPreview = (item: FinderItem | null) => {
|
|
12051
|
-
lastSelected = item;
|
|
12052
|
-
clearHoverTimer();
|
|
12053
|
-
if (previewMode === 'off') return;
|
|
12054
|
-
if (!item || workingDir.remote) {
|
|
12055
|
-
// Off-screen / remote — kill any straggling popup so the
|
|
12056
|
-
// user doesn't see a preview that no longer matches the
|
|
12057
|
-
// selection.
|
|
12058
|
-
disposeActivePreview();
|
|
12059
|
-
return;
|
|
12060
|
-
}
|
|
12061
|
-
if (previewMode === '2pane') {
|
|
12062
|
-
// Instant update — no debounce. Persistent popup replaces
|
|
12063
|
-
// itself via group dedup.
|
|
12064
|
-
paintPreviewFor(item);
|
|
12065
|
-
return;
|
|
12066
|
-
}
|
|
12067
|
-
// HOVER mode. Selection just changed → the popup currently on
|
|
12068
|
-
// screen (if any) is stale. Drop it immediately so the user
|
|
12069
|
-
// doesn't keep seeing the previous file's preview while their
|
|
12070
|
-
// cursor is on a new file. The next paint waits HOVER_DELAY_MS
|
|
12071
|
-
// of stillness before re-appearing.
|
|
12072
|
-
disposeActivePreview();
|
|
12073
|
-
hoverTimer = setTimeout(() => {
|
|
12074
|
-
hoverTimer = null;
|
|
12075
|
-
paintPreviewFor(item);
|
|
12076
|
-
}, HOVER_DELAY_MS);
|
|
12077
|
-
};
|
|
12078
|
-
|
|
12079
|
-
/** Cycle off → hover → 2pane → off. Surfaces a status line so
|
|
12080
|
-
* the user knows which mode they're in (Ctrl+/ has no chrome
|
|
12081
|
-
* feedback otherwise). 2pane on a narrow terminal degrades
|
|
12082
|
-
* to hover with a warning. */
|
|
12083
|
-
const cyclePreviewMode = (): void => {
|
|
12084
|
-
const next: PreviewMode = previewMode === 'off' ? 'hover'
|
|
12085
|
-
: previewMode === 'hover' ? '2pane'
|
|
12086
|
-
: 'off';
|
|
12087
|
-
previewMode = next;
|
|
12088
|
-
clearHoverTimer();
|
|
12089
|
-
// Drop the active popup whenever the mode changes. The new
|
|
12090
|
-
// mode (if any) re-paints below.
|
|
12091
|
-
disposeActivePreview();
|
|
12092
|
-
|
|
12093
|
-
if (next === 'off') {
|
|
12094
|
-
chatLines.push(C.muted(' finder preview: OFF (Ctrl+/ to cycle modes)'));
|
|
12095
|
-
} else if (next === 'hover') {
|
|
12096
|
-
chatLines.push(C.muted(' finder preview: HOVER (popup after 1 s · Ctrl+/ next)'));
|
|
12097
|
-
} else {
|
|
12098
|
-
// 2pane — refuse if the terminal is too narrow; degrade to
|
|
12099
|
-
// hover so the user gets *some* preview without overlap.
|
|
12100
|
-
const docked = computeDockedPreviewBounds();
|
|
12101
|
-
if (!docked) {
|
|
12102
|
-
previewMode = 'hover';
|
|
12103
|
-
chatLines.push(C.warning(` finder preview: 2-pane needs ≥${TWO_PANE_MIN_COLS} cols — falling back to HOVER`));
|
|
12104
|
-
} else {
|
|
12105
|
-
chatLines.push(C.muted(' finder preview: 2-PANE (docked right · Ctrl+/ to disable)'));
|
|
12106
|
-
if (lastSelected) paintPreviewFor(lastSelected);
|
|
12107
|
-
}
|
|
12108
|
-
}
|
|
12109
|
-
chatScrollOffset = -1;
|
|
12110
|
-
draw();
|
|
12111
|
-
};
|
|
12112
|
-
|
|
12113
|
-
// Declared up here so `onAccept`/`onCancel` closures below can
|
|
12114
|
-
// reference both; assigned right after the picker mounts.
|
|
12115
|
-
let previewToggleDisposable: { dispose(): void } | null = null;
|
|
12116
|
-
let previewDismissDisposable: { dispose(): void } | null = null;
|
|
12117
|
-
const picker = createFinderModal({
|
|
12118
|
-
items: finderItems,
|
|
12119
|
-
bounds, width, maxVisible: visibleRows,
|
|
12120
|
-
truncated,
|
|
12121
|
-
title: `Find file in ${titleRoot}`,
|
|
12122
|
-
theme: currentThemeTokens(),
|
|
12123
|
-
onHover: scheduleHoverPreview,
|
|
12124
|
-
onAccept: (item) => {
|
|
12125
|
-
clearHoverTimer();
|
|
12126
|
-
disposeActivePreview();
|
|
12127
|
-
previewToggleDisposable?.dispose();
|
|
12128
|
-
previewDismissDisposable?.dispose();
|
|
12129
|
-
agentSearchModal = null;
|
|
12130
|
-
handle?.dispose();
|
|
12131
|
-
const parentDir = item.absPath.slice(0, item.absPath.lastIndexOf('/')) || '/';
|
|
12132
|
-
const basename = item.absPath.slice(item.absPath.lastIndexOf('/') + 1);
|
|
12133
|
-
if (workingDir.remote) {
|
|
12134
|
-
// T5-I2 — remote cd + focus-by-basename.
|
|
12135
|
-
enterRemoteDirectory(workingDir, parentDir);
|
|
12136
|
-
(async () => {
|
|
12137
|
-
await refreshRemoteWorkingDir(workingDir, {}, dockedPreview);
|
|
12138
|
-
const idx = workingDir.entries.findIndex(e => e.name === basename);
|
|
12139
|
-
if (idx >= 0) {
|
|
12140
|
-
workingDir.cursor = idx;
|
|
12141
|
-
workingDir.offset = Math.max(0, idx - 3);
|
|
12142
|
-
}
|
|
12143
|
-
await refreshRemotePreviewBridge();
|
|
12144
|
-
pushDebugLine(C.muted(` ${workingDir.remote?.host.name}:${item.relPath}`));
|
|
12145
|
-
chatScrollOffset = -1;
|
|
12146
|
-
draw();
|
|
12147
|
-
})().catch(() => {});
|
|
12148
|
-
} else {
|
|
12149
|
-
enterDirectory(targetBrowser as WorkingDirState, parentDir);
|
|
12150
|
-
refreshWorkingDir(targetBrowser as WorkingDirState);
|
|
12151
|
-
const idx = targetBrowser.entries.findIndex(e => e.name === basename);
|
|
12152
|
-
if (idx >= 0) {
|
|
12153
|
-
targetBrowser.cursor = idx;
|
|
12154
|
-
targetBrowser.offset = Math.max(0, idx - 3);
|
|
12155
|
-
}
|
|
12156
|
-
refreshWorkingDirPreview();
|
|
12157
|
-
pushDebugLine(C.muted(` ${item.relPath}`));
|
|
12158
|
-
chatScrollOffset = -1;
|
|
12159
|
-
}
|
|
12160
|
-
setWorkingFocus('browser', 'agent-roster-search-pick');
|
|
12161
|
-
draw();
|
|
12162
|
-
},
|
|
12163
|
-
onCancel: () => {
|
|
12164
|
-
clearHoverTimer();
|
|
12165
|
-
disposeActivePreview();
|
|
12166
|
-
previewToggleDisposable?.dispose();
|
|
12167
|
-
previewDismissDisposable?.dispose();
|
|
12168
|
-
agentSearchModal = null;
|
|
12169
|
-
handle?.dispose();
|
|
12170
|
-
draw();
|
|
12171
|
-
},
|
|
12172
|
-
});
|
|
12173
|
-
// Ctrl+/ (and Ctrl+_ which some terminals send for the same
|
|
12174
|
-
// physical chord) cycles preview modes. Active for the lifetime
|
|
12175
|
-
// of THIS picker only — disposed in both onAccept and onCancel
|
|
12176
|
-
// above. Q4 (substrate Occam): no per-binding pipe alias, register
|
|
12177
|
-
// each variant separately. (Korean IME aliases unnecessary — Ctrl+/
|
|
12178
|
-
// has no jamo mapping on the 2-set layout.)
|
|
12179
|
-
const togglePreviewMode = (): void => { cyclePreviewMode(); };
|
|
12180
|
-
const previewSlash = display.registerKeyBinding({
|
|
12181
|
-
id: `dashboard:finder-picker-preview-toggle-slash:${Date.now().toString(36)}`,
|
|
12182
|
-
key: 'C-/',
|
|
12183
|
-
scope: 'global',
|
|
12184
|
-
when: () => agentSearchModal === picker,
|
|
12185
|
-
handler: togglePreviewMode,
|
|
12186
|
-
});
|
|
12187
|
-
const previewUnder = display.registerKeyBinding({
|
|
12188
|
-
id: `dashboard:finder-picker-preview-toggle-under:${Date.now().toString(36)}`,
|
|
12189
|
-
key: 'C-_',
|
|
12190
|
-
scope: 'global',
|
|
12191
|
-
when: () => agentSearchModal === picker,
|
|
12192
|
-
handler: togglePreviewMode,
|
|
12193
|
-
});
|
|
12194
|
-
previewToggleDisposable = {
|
|
12195
|
-
dispose: () => { previewSlash.dispose(); previewUnder.dispose(); },
|
|
12196
|
-
};
|
|
12197
|
-
// Ctrl+] — instantly dismiss the active preview popup WITHOUT
|
|
12198
|
-
// closing the picker or changing the mode. Esc would be more
|
|
12199
|
-
// intuitive but the picker captures Esc internally to cancel
|
|
12200
|
-
// itself. Ctrl+\ is unsafe because it raises SIGQUIT in many
|
|
12201
|
-
// terminal drivers. Ctrl+] (historically "telnet escape" =
|
|
12202
|
-
// "exit this context") is universally safe + semantically right.
|
|
12203
|
-
// Active only while a preview is currently visible — otherwise
|
|
12204
|
-
// the binding silently no-ops (no flicker, no log spam).
|
|
12205
|
-
// Mode stays as set; if user is in HOVER mode, the next
|
|
12206
|
-
// cursor stillness re-triggers the popup. To stop the popup
|
|
12207
|
-
// from re-appearing entirely, cycle Ctrl+/ back to OFF.
|
|
12208
|
-
previewDismissDisposable = display.registerKeyBinding({
|
|
12209
|
-
id: `dashboard:finder-picker-preview-dismiss:${Date.now().toString(36)}`,
|
|
12210
|
-
key: 'C-]',
|
|
12211
|
-
scope: 'global',
|
|
12212
|
-
when: () => agentSearchModal === picker && lastPreviewHandle !== null,
|
|
12213
|
-
handler: () => {
|
|
12214
|
-
clearHoverTimer();
|
|
12215
|
-
disposeActivePreview();
|
|
12216
|
-
draw();
|
|
12217
|
-
},
|
|
12218
|
-
});
|
|
12219
|
-
attachSurfaceToWorkspace(picker.surface, currentWorkspaceOwnerId());
|
|
12220
|
-
handle = display.pushModal(picker.surface);
|
|
12221
|
-
agentSearchModal = picker;
|
|
12222
|
-
draw();
|
|
12223
|
-
}
|
|
12224
|
-
|
|
12225
|
-
/** Yazi-style Alt+C — directory finder. Symmetric with openFinder
|
|
12226
|
-
* Picker but enumerates directories only (`scanFinder kind:'dir'`)
|
|
12227
|
-
* and on accept cd's the browser pane to the chosen directory.
|
|
12228
|
-
* Remote-mode is currently unsupported (surfaces a warning) — the
|
|
12229
|
-
* remote finder would need a parallel kind-aware backend. */
|
|
12230
|
-
async function openDirectoryPicker(): Promise<void> {
|
|
12231
|
-
if (workingDir.remote) {
|
|
12232
|
-
chatLines.push(C.warning(' Alt+C: directory finder is local-only — Esc remote first.'));
|
|
12233
|
-
chatScrollOffset = -1;
|
|
12234
|
-
draw();
|
|
12235
|
-
return;
|
|
12236
|
-
}
|
|
12237
|
-
const targetBrowserId = activeBrowserWidgetId();
|
|
12238
|
-
const targetBrowser = resolveBrowserStateById(targetBrowserId) ?? (workingDir as BrowserPaneModel);
|
|
12239
|
-
// Symmetric with openFinderPicker — scan from SWD (project root),
|
|
12240
|
-
// not the browser pane's nav cursor. The accept handler still
|
|
12241
|
-
// mutates targetBrowser to navigate into the chosen dir.
|
|
12242
|
-
const scanRoot = getSessionCwd();
|
|
12243
|
-
finderFeedbackRuntime.onFinderScanStarted(`${scanRoot} (dirs)`);
|
|
12244
|
-
draw();
|
|
12245
|
-
const scan = await scanFinder({ root: scanRoot, kind: 'dir' });
|
|
12246
|
-
const rels = relativizeResults(scan.paths, scanRoot);
|
|
12247
|
-
const dirItems: FinderItem[] = scan.paths.map((absPath, i) => ({
|
|
12248
|
-
absPath, relPath: rels[i] ?? absPath,
|
|
12249
|
-
}));
|
|
12250
|
-
if (dirItems.length === 0) {
|
|
12251
|
-
finderFeedbackRuntime.onFinderScanEmpty();
|
|
12252
|
-
draw();
|
|
12253
|
-
return;
|
|
12254
|
-
}
|
|
12255
|
-
finderFeedbackRuntime.onFinderScanCompleted({
|
|
12256
|
-
count: dirItems.length,
|
|
12257
|
-
backend: scan.backend,
|
|
12258
|
-
truncated: scan.truncated,
|
|
12259
|
-
durationMs: scan.durationMs,
|
|
12260
|
-
});
|
|
12261
|
-
|
|
12262
|
-
const { cols, rows } = termSize();
|
|
12263
|
-
const width = Math.min(90, Math.max(50, cols - 6));
|
|
12264
|
-
const visibleRows = Math.min(15, Math.max(6, rows - 8));
|
|
12265
|
-
const height = visibleRows + 3;
|
|
12266
|
-
const bounds = {
|
|
12267
|
-
row: Math.max(1, Math.floor((rows - height) / 2)),
|
|
12268
|
-
col: Math.max(1, Math.floor((cols - width) / 2)),
|
|
12269
|
-
width,
|
|
12270
|
-
height,
|
|
12271
|
-
};
|
|
12272
|
-
let handle: ReturnType<typeof display.pushModal> | null = null;
|
|
12273
|
-
const titleRoot = scanRoot.replace(homedir(), '~');
|
|
12274
|
-
const picker = createFinderModal({
|
|
12275
|
-
items: dirItems,
|
|
12276
|
-
bounds, width, maxVisible: visibleRows,
|
|
12277
|
-
truncated: scan.truncated,
|
|
12278
|
-
title: `Find directory in ${titleRoot}`,
|
|
12279
|
-
theme: currentThemeTokens(),
|
|
12280
|
-
onAccept: (item) => {
|
|
12281
|
-
agentSearchModal = null;
|
|
12282
|
-
handle?.dispose();
|
|
12283
|
-
enterDirectory(targetBrowser as WorkingDirState, item.absPath);
|
|
12284
|
-
refreshWorkingDir(targetBrowser as WorkingDirState);
|
|
12285
|
-
refreshWorkingDirPreview();
|
|
12286
|
-
pushDebugLine(C.muted(` cd ${item.relPath}`));
|
|
12287
|
-
chatScrollOffset = -1;
|
|
12288
|
-
setWorkingFocus('browser', 'alt-c-dir-pick');
|
|
12289
|
-
draw();
|
|
12290
|
-
},
|
|
12291
|
-
onCancel: () => {
|
|
12292
|
-
agentSearchModal = null;
|
|
12293
|
-
handle?.dispose();
|
|
12294
|
-
draw();
|
|
12295
|
-
},
|
|
12296
|
-
});
|
|
12297
|
-
attachSurfaceToWorkspace(picker.surface, currentWorkspaceOwnerId());
|
|
12298
|
-
handle = display.pushModal(picker.surface);
|
|
12299
|
-
agentSearchModal = picker;
|
|
12300
|
-
draw();
|
|
12301
|
-
}
|
|
12302
|
-
|
|
12303
|
-
/** T4-E4 — open the SSH host picker (Ctrl+K). Accept connects
|
|
12304
|
-
* the browser pane to the remote host; Esc cancels silently.
|
|
12305
|
-
* Routes keys through agentSearchModal (shared pattern with
|
|
12306
|
-
* window + session pickers). */
|
|
12307
|
-
function openSshPicker(): void {
|
|
12308
|
-
const { cols, rows } = termSize();
|
|
12309
|
-
const width = Math.min(72, Math.max(44, cols - 6));
|
|
12310
|
-
const visibleRows = 8;
|
|
12311
|
-
const height = visibleRows + 3;
|
|
12312
|
-
const bounds = {
|
|
12313
|
-
row: Math.max(1, Math.floor((rows - height) / 2)),
|
|
12314
|
-
col: Math.max(1, Math.floor((cols - width) / 2)),
|
|
12315
|
-
width,
|
|
12316
|
-
height,
|
|
12317
|
-
};
|
|
12318
|
-
let handle: ReturnType<typeof display.pushModal> | null = null;
|
|
12319
|
-
const picker = createSshPickerModal({
|
|
12320
|
-
bounds, width, maxVisible: visibleRows,
|
|
12321
|
-
activeHostName: workingDir.remote?.host.name ?? null,
|
|
12322
|
-
theme: currentThemeTokens(),
|
|
12323
|
-
onAccept: async (host) => {
|
|
12324
|
-
agentSearchModal = null;
|
|
12325
|
-
handle?.dispose();
|
|
12326
|
-
touchSshHost(host.name);
|
|
12327
|
-
startRemoteModeBridge(host, '.');
|
|
12328
|
-
finderFeedbackRuntime.onSshConnectStarted({ name: host.name, host: host.host });
|
|
12329
|
-
draw();
|
|
12330
|
-
try {
|
|
12331
|
-
await refreshRemoteWorkingDir(workingDir, {}, dockedPreview);
|
|
12332
|
-
await refreshRemotePreviewBridge();
|
|
12333
|
-
finderFeedbackRuntime.onSshConnectSucceeded(host.name);
|
|
12334
|
-
} catch (err) {
|
|
12335
|
-
finderFeedbackRuntime.onSshConnectFailed(
|
|
12336
|
-
host.name,
|
|
12337
|
-
err instanceof Error ? err.message : String(err),
|
|
12338
|
-
);
|
|
12339
|
-
}
|
|
12340
|
-
draw();
|
|
12341
|
-
},
|
|
12342
|
-
onCancel: () => {
|
|
12343
|
-
agentSearchModal = null;
|
|
12344
|
-
handle?.dispose();
|
|
12345
|
-
draw();
|
|
12346
|
-
},
|
|
12347
|
-
});
|
|
12348
|
-
attachSurfaceToWorkspace(picker.surface, currentWorkspaceOwnerId());
|
|
12349
|
-
handle = display.pushModal(picker.surface);
|
|
12350
|
-
agentSearchModal = picker;
|
|
12351
|
-
draw();
|
|
12352
|
-
}
|
|
12353
|
-
|
|
12354
11729
|
// FU-1 — register ssh-picker / finder-picker openers as display
|
|
12355
11730
|
// key bindings. Moves the dashboard hard branches onto the same
|
|
12356
11731
|
// coordinator dispatch path the rest of the modals / bindings use.
|
|
@@ -12811,43 +12186,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
12811
12186
|
onContextMenuMouse: (ev) => ctxMenuWire.onMouse(ev),
|
|
12812
12187
|
});
|
|
12813
12188
|
};
|
|
12814
|
-
const openBrowserOnlyModal = (): void => {
|
|
12815
|
-
const { cols: tc, rows: tr } = termSize();
|
|
12816
|
-
const liveMode = resolveBrowserPreviewModalLiveMode(process.env.ELANOUS_PANE_MODAL_LIVE);
|
|
12817
|
-
void openBrowserPaneModal({
|
|
12818
|
-
browserWidgetInstanceId: browserWidgetInstanceIdForView(workingDir.view),
|
|
12819
|
-
liveMode,
|
|
12820
|
-
browserPaneRegistry,
|
|
12821
|
-
widgetHost,
|
|
12822
|
-
coordinator: display,
|
|
12823
|
-
termCols: tc,
|
|
12824
|
-
termRows: tr,
|
|
12825
|
-
captureSnapshot: () => captureDeferredPane('browser'),
|
|
12826
|
-
theme: currentThemeTokens(),
|
|
12827
|
-
fmtEntryColored,
|
|
12828
|
-
iconForEntry,
|
|
12829
|
-
onSubmit: dispatchSidebarSubmit,
|
|
12830
|
-
onDispose: () => { draw(); },
|
|
12831
|
-
onCancel: () => { draw(); },
|
|
12832
|
-
});
|
|
12833
|
-
};
|
|
12834
|
-
const openPreviewOnlyModal = (): void => {
|
|
12835
|
-
const { cols: tc, rows: tr } = termSize();
|
|
12836
|
-
const liveMode = (process.env.ELANOUS_PANE_MODAL_LIVE ?? 'on').toLowerCase() !== 'off';
|
|
12837
|
-
void openPreviewPaneModal({
|
|
12838
|
-
preview: dockedPreview,
|
|
12839
|
-
previewWidgetInstanceId: 'wd-preview',
|
|
12840
|
-
liveMode,
|
|
12841
|
-
widgetHost,
|
|
12842
|
-
coordinator: display,
|
|
12843
|
-
termCols: tc,
|
|
12844
|
-
termRows: tr,
|
|
12845
|
-
captureSnapshot: () => captureDeferredPane('preview'),
|
|
12846
|
-
theme: currentThemeTokens(),
|
|
12847
|
-
onDispose: () => { draw(); },
|
|
12848
|
-
onCancel: () => { draw(); },
|
|
12849
|
-
});
|
|
12850
|
-
};
|
|
12851
12189
|
registerDashboardGlobalKeys({
|
|
12852
12190
|
enableSupplementalGlobalKeys: getUserConfig().dashboard.enableSupplementalGlobalKeys,
|
|
12853
12191
|
register: (binding) => { display.registerKeyBinding(binding); },
|
|
@@ -13071,6 +12409,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
13071
12409
|
repaint: () => void;
|
|
13072
12410
|
insertAtCursor?: (text: string) => void;
|
|
13073
12411
|
submit?: (request?: string | TextInputExternalSubmitRequest) => void;
|
|
12412
|
+
getBufferText?: () => string;
|
|
13074
12413
|
} = { repaint: () => {} };
|
|
13075
12414
|
repaintPromptAfterRender = () => {
|
|
13076
12415
|
if (shouldSuppressDashboardBottomArea()) {
|
|
@@ -13094,6 +12433,14 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
13094
12433
|
// safe. Dispose on process exit to keep the listener set clean.
|
|
13095
12434
|
const disposeResizeListener = installResizeListener({ draw: (opts) => draw(opts), promptCtl });
|
|
13096
12435
|
process.on('exit', disposeResizeListener);
|
|
12436
|
+
// 외부 주입(`pty text`)을 사용자 타이핑과 섞지 않고 별도 대기열로 — 결함 #6(10-14).
|
|
12437
|
+
const disposeExternalInputHost = registerExternalInputHost(createDashboardExternalInputHost({
|
|
12438
|
+
isTurnActive: () => streamingInFlight,
|
|
12439
|
+
getTurnStop: () => currentTurnExternalStop,
|
|
12440
|
+
promptCtl,
|
|
12441
|
+
pushNotice: (line) => { chatLines.push(C.muted(` ${line}`)); chatScrollOffset = -1; draw(); },
|
|
12442
|
+
}));
|
|
12443
|
+
process.on('exit', disposeExternalInputHost);
|
|
13097
12444
|
|
|
13098
12445
|
const publishScratchSurface = (
|
|
13099
12446
|
source: string,
|
|
@@ -13112,6 +12459,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
13112
12459
|
const detailViewerRuntime = createDashboardDetailViewerRuntime({
|
|
13113
12460
|
setOpen: (open) => { setCompanionPopupOpen('detail', open); },
|
|
13114
12461
|
requestRender: () => { requestDashboardRender('detail'); },
|
|
12462
|
+
// 채팅 단독 화면에선 그리드 투영이 안 돌아 팝업이 «(no preview content)» 였다 — 그리기 전에 싣는다.
|
|
12463
|
+
project: (snapshot) => {
|
|
12464
|
+
const detailWidget = widgetHost.get('wd-detail') as WidgetInstance<Record<string, unknown>> | null;
|
|
12465
|
+
if (detailWidget?.state && typeof detailWidget.state === 'object') {
|
|
12466
|
+
companionWidgetRuntime.projectDetailWidget(detailWidget, snapshot);
|
|
12467
|
+
}
|
|
12468
|
+
},
|
|
13115
12469
|
});
|
|
13116
12470
|
const companionWidgetRuntime = createDashboardCompanionWidgetRuntime();
|
|
13117
12471
|
const clipboardHistoryRuntime = createDashboardClipboardHistoryRuntime();
|
|
@@ -13801,6 +13155,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
13801
13155
|
let session: TerminalSession;
|
|
13802
13156
|
try {
|
|
13803
13157
|
if (req.kind === 'shell') {
|
|
13158
|
+
const shellSeat = resolveTuiShellSeat(req.cwd, process.env.COORD_ID);
|
|
13804
13159
|
session = sessionRegistry.spawn(
|
|
13805
13160
|
{
|
|
13806
13161
|
title: 'terminal',
|
|
@@ -13808,6 +13163,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
13808
13163
|
command: req.command,
|
|
13809
13164
|
env: req.env,
|
|
13810
13165
|
kind: 'shell',
|
|
13166
|
+
useRegistry: true,
|
|
13167
|
+
registryKind: 'shell',
|
|
13168
|
+
registryNickname: `tui-shell:${req.cwd}`,
|
|
13169
|
+
registryOrigin: { originRoot: 'elanous-internal', controller: 'tui-shell' },
|
|
13170
|
+
registryPurpose: `tui-shell:${shellSeat}`,
|
|
13171
|
+
registrySeat: shellSeat,
|
|
13811
13172
|
onExit: handleExit,
|
|
13812
13173
|
onTitleAction: handleTitleAction,
|
|
13813
13174
|
},
|
|
@@ -13899,8 +13260,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
13899
13260
|
openInteractiveTerminalPopup,
|
|
13900
13261
|
});
|
|
13901
13262
|
const TerminalPopup = terminalPopupRuntime;
|
|
13902
|
-
const spawnGlobalTerminalModal = (): void => {
|
|
13903
|
-
|
|
13263
|
+
const spawnGlobalTerminalModal = (command?: string): void => {
|
|
13264
|
+
openInteractiveTerminalPopup({ kind: 'shell', cwd: workingDir.cwd, command });
|
|
13904
13265
|
};
|
|
13905
13266
|
|
|
13906
13267
|
// E3 (2026-05-17) — copyLastAssistantTurnToClipboard /
|
|
@@ -14022,7 +13383,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
14022
13383
|
},
|
|
14023
13384
|
toggleLogZoom: () => { toggleChatOnlyLayout(); },
|
|
14024
13385
|
copyLastBlock: copyLastAssistantTurnToClipboard,
|
|
14025
|
-
spawnTerminalModal: spawnGlobalTerminalModal,
|
|
13386
|
+
spawnTerminalModal: () => spawnGlobalTerminalModal(),
|
|
14026
13387
|
copyLogPane: copyLogPaneToClipboard,
|
|
14027
13388
|
// Ctrl+L — 터미널 클리어 후 전체 강제 리페인트(resize 리스너와 동일 레시피).
|
|
14028
13389
|
// 외부 출력/아티팩트로 더러워진 화면 복구용 · 대화(chatLines)는 보존.
|
|
@@ -14091,6 +13452,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
14091
13452
|
},
|
|
14092
13453
|
onPreviewTerminalExpandChanged: chordFeedbackRuntime.onPreviewTerminalExpandChanged,
|
|
14093
13454
|
onMissingPreviewTerminal: chordFeedbackRuntime.onMissingPreviewTerminal,
|
|
13455
|
+
// #19 — Ctrl+X f from a pane; the input path is the `ctrl+x f` input-core binding.
|
|
13456
|
+
toggleAllFolds: toggleAllLogFolds,
|
|
14094
13457
|
});
|
|
14095
13458
|
|
|
14096
13459
|
// ══════════════════════════════════════════════════════════════
|
|
@@ -15369,6 +14732,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15369
14732
|
// adds turn concurrency must key this by turn (or session+turn) first.
|
|
15370
14733
|
let acpTurnToolSpecs: LLMToolSpec[] = [];
|
|
15371
14734
|
let acpDispatchToolSelf: CoreTurnDispatchTool | null = null;
|
|
14735
|
+
let acpToolDispatchCount = 0;
|
|
15372
14736
|
const trMod = await import('../tool-runtime/index.js');
|
|
15373
14737
|
trMod.registerAllDefaultToolRuntimes();
|
|
15374
14738
|
acpToolRuntime = trMod;
|
|
@@ -15423,6 +14787,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15423
14787
|
modelFamily: getModelFamily(inspectActiveProvider().model),
|
|
15424
14788
|
rich: false,
|
|
15425
14789
|
enabledTools: tools.map(t => t.name),
|
|
14790
|
+
seatBoot: true,
|
|
15426
14791
|
}),
|
|
15427
14792
|
];
|
|
15428
14793
|
},
|
|
@@ -15452,35 +14817,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15452
14817
|
// Codex-first decision as every other surface. The core bridge now
|
|
15453
14818
|
// supplies userText to resolveModel, preserving explicit pins and
|
|
15454
14819
|
// making this the actual model override rather than a display hint.
|
|
15455
|
-
|
|
15456
|
-
|
|
15457
|
-
const decision = resolveRouteDecision({
|
|
15458
|
-
provider: cfg.llm.provider,
|
|
15459
|
-
configuredModel: inspectActiveProvider().model,
|
|
15460
|
-
text: userText,
|
|
15461
|
-
routePolicy: cfg.llm.routePolicy,
|
|
15462
|
-
});
|
|
15463
|
-
// Badge truthfulness(2026-07-19 goal-exec) — resolveRouteDecision 의 codex
|
|
15464
|
-
// tier-policy 브랜치는 curated effort(예: balanced=low)를 찍지만, codex provider 는
|
|
15465
|
-
// 그 값을 wire 로 보내지 않는다: 실제 effort 는 effectiveReasoningLevel(cfg)(config
|
|
15466
|
-
// 주도, reasoningLevel 미설정 시 model-default high). 그대로 두면 완료 뱃지가
|
|
15467
|
-
// "terra(low)" 로 떠 사용자가 모델이 약하게 돈다고 오해한다(실제 high). 실제 wire
|
|
15468
|
-
// effort 로 덮어써 뱃지를 진실하게. decision.effort 는 표시 전용이라 라우팅 무영향.
|
|
15469
|
-
const trueEffort = effectiveReasoningLevel(cfg.llm, decision.provider, decision.model);
|
|
15470
|
-
const badgedDecision = trueEffort && trueEffort !== 'off'
|
|
15471
|
-
? { ...decision, effort: trueEffort }
|
|
15472
|
-
: decision;
|
|
15473
|
-
recordCurrentRouteDecision('dashboard', badgedDecision);
|
|
15474
|
-
if (debug.enabled) debug.log('llm.route-decision', 'dashboard-acp', {
|
|
15475
|
-
provider: decision.provider, model: decision.model, mission: decision.mission, source: decision.source,
|
|
15476
|
-
});
|
|
15477
|
-
return decision.model || null;
|
|
15478
|
-
},
|
|
14820
|
+
// 본체 = acp-active-model.ts(실물 경로를 시험이 그대로 부르도록 뺐다 · TUI-MODEL-SESSION-SCOPE).
|
|
14821
|
+
getActiveModel: ({ userText }) => resolveDashboardActiveModel(userText),
|
|
15479
14822
|
// ⭐ The assignment is deliberate, not a typo: it captures a reference to
|
|
15480
14823
|
// this very dispatcher so `agentDispatchTool` below can route a
|
|
15481
14824
|
// sub-agent's tool calls back through it. A plain named const would need
|
|
15482
14825
|
// the closure hoisted out of this literal; this keeps the diff local.
|
|
15483
14826
|
dispatchTool: acpDispatchToolSelf = async (name, args, ctx) => {
|
|
14827
|
+
// Count at the server-side boundary, before any tool effect and
|
|
14828
|
+
// before the ACP notification can reach the TUI client.
|
|
14829
|
+
acpToolDispatchCount++;
|
|
15484
14830
|
const runtime = acpToolRuntime;
|
|
15485
14831
|
const persist = acpPersistToolOutputPreview;
|
|
15486
14832
|
if (!runtime || !persist) {
|
|
@@ -15624,8 +14970,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15624
14970
|
// — the boot fell back to a fresh session. Surface the reason
|
|
15625
14971
|
// so the user knows they're on a different session than asked.
|
|
15626
14972
|
// - new (no resume requested): nothing extra to render.
|
|
14973
|
+
// 대표 #73 — 인자 없는 기동 = 새 세션. 이어받았는지는 이 값 하나로 판정한다(관측 ⊕ 자리 부트 카드).
|
|
14974
|
+
let tuiBootSessionStart: { origin: 'new' | 'resume'; reason: string; restoredSessionId?: string } = opts.resumeSessionId
|
|
14975
|
+
? { origin: 'new', reason: 'resume-failed' }
|
|
14976
|
+
: { origin: 'new', reason: 'default-new' };
|
|
15627
14977
|
if (dashboardAcpBootResult.mode === 'resumed' && dashboardAcpBootResult.sessionId) {
|
|
15628
14978
|
const sid = dashboardAcpBootResult.sessionId;
|
|
14979
|
+
tuiBootSessionStart = { origin: 'resume', reason: 'resume-daemon', restoredSessionId: sid };
|
|
15629
14980
|
chatLines.push(C.success(` ✓ resumed daemon session: ${sid}`));
|
|
15630
14981
|
// Best-effort REST history replay over the remote HTTP/WS endpoint.
|
|
15631
14982
|
if (opts.remote) {
|
|
@@ -15687,10 +15038,27 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15687
15038
|
if (!resume.resumed) {
|
|
15688
15039
|
chatLines.push(C.warning(` ⚠ resume failed (${resume.reason}); started new session`));
|
|
15689
15040
|
} else {
|
|
15041
|
+
tuiBootSessionStart = {
|
|
15042
|
+
origin: 'resume',
|
|
15043
|
+
reason: resume.repaired ? 'resume-local-repaired' : 'resume-local',
|
|
15044
|
+
restoredSessionId: resume.sessionId,
|
|
15045
|
+
};
|
|
15690
15046
|
chatLines.push(C.success(` ✓ resumed local session: ${resume.sessionId}`));
|
|
15047
|
+
if (resume.repaired) {
|
|
15048
|
+
chatLines.push(C.muted(` 깨진 도구 호출·결과 짝을 잘라 이어받았다(호출 ${resume.repaired.removedCalls} · 결과 ${resume.repaired.removedResults})`));
|
|
15049
|
+
}
|
|
15691
15050
|
chatLines.push(C.muted(` ${resume.turns} turns restored. Next turn continues this conversation.`));
|
|
15692
15051
|
}
|
|
15693
15052
|
}
|
|
15053
|
+
observeSessionStart({
|
|
15054
|
+
surface: 'tui',
|
|
15055
|
+
origin: tuiBootSessionStart.origin,
|
|
15056
|
+
reason: tuiBootSessionStart.reason,
|
|
15057
|
+
// sessionId = 이 대화가 이어받은(또는 새로 연) 대화 기록의 id · acpSessionId = 백엔드 ACP 세션 id(둘이 다를 수 있다).
|
|
15058
|
+
sessionId: tuiBootSessionStart.restoredSessionId ?? dashboardAcpSession.currentSessionId ?? null,
|
|
15059
|
+
acpSessionId: dashboardAcpSession.currentSessionId ?? null,
|
|
15060
|
+
resumeRequested: opts.resumeSessionId !== undefined,
|
|
15061
|
+
});
|
|
15694
15062
|
// 제안은 세션 작업 폴더 기준 — 시작 폴더 한 번이 아니라 폴더가 바뀔 때마다 다시 계산한다(IA1 리뷰 must-fix).
|
|
15695
15063
|
const nextProjectSuggestion = createProjectSuggester(suggestProjectForFolder);
|
|
15696
15064
|
const pushProjectSuggestion = (folder: string): void => {
|
|
@@ -15702,16 +15070,37 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15702
15070
|
}
|
|
15703
15071
|
subscribeSessionCwd((state) => { pushProjectSuggestion(state.cwd); });
|
|
15704
15072
|
chatScrollOffset = -1;
|
|
15705
|
-
|
|
15073
|
+
const firstScreenBandInput = {
|
|
15706
15074
|
history: chat.history,
|
|
15707
15075
|
lines: chatLines,
|
|
15708
15076
|
width: Math.max(0, termSize().cols - 4),
|
|
15709
15077
|
// The band ships ko/en only; any other detected locale keeps the English rows.
|
|
15710
|
-
locale: detectLocale() === 'ko' ? 'ko' : 'en',
|
|
15078
|
+
locale: detectLocale() === 'ko' ? 'ko' as const : 'en' as const,
|
|
15711
15079
|
draw: () => draw(),
|
|
15712
|
-
observe: (data) => { try { debug.log('dashboard.first-screen-band', 'resolved', data); } catch { /* observation is best-effort */ } },
|
|
15713
|
-
}
|
|
15080
|
+
observe: (data: { daemon: boolean; hasPwa: boolean; hasTailnet: boolean }) => { try { debug.log('dashboard.first-screen-band', 'resolved', data); } catch { /* observation is best-effort */ } },
|
|
15081
|
+
};
|
|
15082
|
+
void (bootBannerMode === 'full'
|
|
15083
|
+
? renderDashboardFirstScreenBand(firstScreenBandInput)
|
|
15084
|
+
: renderMinimalBootBand({
|
|
15085
|
+
...firstScreenBandInput,
|
|
15086
|
+
guidance: needsOnboarding(getUserConfig()),
|
|
15087
|
+
held: bootBannerHeld,
|
|
15088
|
+
show: () => runNexusShow({ format: 'json', out: { log: () => {}, error: () => {} } }),
|
|
15089
|
+
daemonLink: resolveTuiDaemonLink,
|
|
15090
|
+
styleFailure: (line) => C.warning(line),
|
|
15091
|
+
observeHeld: (data) => { try { debug.log('dashboard.first-screen-band', 'held', data); } catch { /* observation is best-effort */ } },
|
|
15092
|
+
}));
|
|
15714
15093
|
|
|
15094
|
+
// 🪪 L1 — 자리 부트(seat-boot-preamble.ts): 자리면 첫 화면에 한 줄.
|
|
15095
|
+
// 🪪 L2-lite(seat-local-instructions.ts): 자리 로컬 지시가 없으면 템플릿 생성(덮어쓰기 0) ⊕ 대화 끝 «지난 세션» 기록.
|
|
15096
|
+
const seatLocal = ensureSeatLocalInstructions(getSessionCwd());
|
|
15097
|
+
if (seatLocal.status === 'created') chatLines.push(C.muted(` 📝 자리 로컬 지시를 만들었다 — ${seatLocal.path} (사람이 고쳐도 된다)`));
|
|
15098
|
+
else if (seatLocal.status === 'exists' && seatLocal.missingPrinciple) chatLines.push(C.muted(` 💡 ${seatLocal.path} 에 제1원칙 절이 없다 — 넣을지 사람이 정한다(덮어쓰지 않음)`));
|
|
15099
|
+
installSeatSessionRecorder({ getCwd: () => getSessionCwd(), getHistory: () => chat.history });
|
|
15100
|
+
const seatBoot = describeSeatBoot(getSessionCwd());
|
|
15101
|
+
if (seatBoot) chatLines.push(C.muted(` 🪪 ${seatBoot.seat} 자리 · 안 읽은 우편 ${seatBoot.unread ?? '미관측'} — 부트 카드를 턴마다 싣는다`));
|
|
15102
|
+
// 새 세션(이어받기 실패 포함)이면 자리 부트 카드로 시작한다 — 자리 맥락은 세션이 아니라 자리 문서가 싣는다.
|
|
15103
|
+
if (tuiBootSessionStart.origin === 'new') startSeatBootConversation({ reason: 'boot' });
|
|
15715
15104
|
tuiSeatAskTimer = setInterval(pollTuiSeatAnswers, 10_000);
|
|
15716
15105
|
tuiSeatAskTimer.unref();
|
|
15717
15106
|
pollTuiSeatAnswers();
|
|
@@ -16696,7 +16085,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
16696
16085
|
sync.cursors[sync.focus] = Math.max(0, Math.min(sync.cursors[sync.focus]! + delta, sync.lists[sync.focus]!.length - 1));
|
|
16697
16086
|
} else if (mr > logViewport.startRow && mr <= logViewport.endRow) {
|
|
16698
16087
|
// Scroll the log pane
|
|
16699
|
-
const maxScr =
|
|
16088
|
+
const maxScr = logScrollMax(logViewport.height);
|
|
16700
16089
|
chatScrollOffset = Math.max(0, Math.min((chatScrollOffset < 0 ? maxScr : chatScrollOffset) + delta, maxScr));
|
|
16701
16090
|
if (delta > 0 && chatScrollOffset >= maxScr) chatScrollOffset = -1;
|
|
16702
16091
|
}
|
|
@@ -16769,19 +16158,23 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
16769
16158
|
// 다음 inner-loop 반복이 남은 항목도 같은 순서로 제출한다.
|
|
16770
16159
|
// ⭐ 드레인 순서 자체는 `chat/turn-typeahead.ts` 가 소유한다 — 여기서 베끼면
|
|
16771
16160
|
// 테스트가 그 순서를 다시 베껴야 하고, 베낀 테스트는 배선 회귀를 못 잡는다.
|
|
16772
|
-
|
|
16161
|
+
internalTurnGate.noteTurnSettled();
|
|
16162
|
+
// «지금 밀어 넣기»로 끊은 턴은 «중단»이 아니다 — 복원하지 않고 큐 머리를 곧바로 제출한다.
|
|
16163
|
+
const interrupted = consumeTurnEndInterrupted(lastTurnFinalStatus, steerInterruptRef);
|
|
16773
16164
|
const queuedBeforeDrain = turnTypeaheadRef.state.queuedSubmissions.length;
|
|
16774
|
-
|
|
16165
|
+
// ⭐ 드레인 → 레지스트리 해제 → Enter 주입은 다리 모듈이 한 함수로 한다(시험이 같은 함수를 문다).
|
|
16166
|
+
const drained = handoffTurnTypeaheadAtTurnEnd(turnTypeaheadRef, nextInitial, { interrupted }, () => {
|
|
16167
|
+
injectKey({ name: 'enter', ctrl: false, shift: false });
|
|
16168
|
+
});
|
|
16775
16169
|
lastTurnFinalStatus = undefined;
|
|
16776
|
-
turnTypeaheadRef.state = drained.state;
|
|
16777
16170
|
nextInitial = drained.nextInitial;
|
|
16778
16171
|
const queuedTurnEndText = drained.injectEnter ? drained.nextInitial : undefined;
|
|
16779
16172
|
if (interrupted && queuedBeforeDrain > 0) {
|
|
16780
16173
|
debug.log('dashboard.turn-typeahead', 'restored-after-interrupt', { count: queuedBeforeDrain });
|
|
16781
16174
|
}
|
|
16782
|
-
if (drained.injectEnter) injectKey({ name: 'enter', ctrl: false, shift: false });
|
|
16783
16175
|
draw();
|
|
16784
|
-
const
|
|
16176
|
+
const getInputCols = () => termSize().cols;
|
|
16177
|
+
const inputCols = getInputCols();
|
|
16785
16178
|
// Row is published by draw() via the `input-prompt` zone's
|
|
16786
16179
|
// resolved geometry — no `termRows - N` math here anymore.
|
|
16787
16180
|
// The composer is the single source of truth; if the layout
|
|
@@ -16792,6 +16185,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
16792
16185
|
promptFrame: currentPromptFrame(termSize().rows),
|
|
16793
16186
|
getPromptFrame: () => currentPromptFrame(termSize().rows),
|
|
16794
16187
|
inputCols,
|
|
16188
|
+
getInputCols,
|
|
16795
16189
|
surfaceRegistry: getSurfaceRegistry(),
|
|
16796
16190
|
invalidateRenderCacheRow,
|
|
16797
16191
|
buildPromptFrameDividerRows,
|
|
@@ -16805,11 +16199,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
16805
16199
|
paintFrameNow: () => drawNow(),
|
|
16806
16200
|
preferHistoryArrowKeys: slashQuickMode,
|
|
16807
16201
|
history: inputHistory,
|
|
16202
|
+
// TUI-INPUT-PLACEHOLDER-OFF — provider 가 있으면 빈 입력창은 비워 둔다(대표 «불필요한 가이드 없애라»).
|
|
16808
16203
|
placeholder: anyProviderAvailable()
|
|
16809
|
-
? '
|
|
16204
|
+
? ''
|
|
16810
16205
|
: '/quit /clear (run `elanous setup` — no LLM provider available)',
|
|
16811
16206
|
debugLog: debug.enabled ? (event, label, payload) => debug.log(event, label, payload) : undefined,
|
|
16812
16207
|
textInputOpts: {
|
|
16208
|
+
internalTurnYield: internalTurnYieldPort,
|
|
16813
16209
|
onScrollKey: (name) => {
|
|
16814
16210
|
const { rows } = termSize();
|
|
16815
16211
|
const viewport = computePromptFrameLogViewportBounds(
|
|
@@ -17251,6 +16647,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
17251
16647
|
}),
|
|
17252
16648
|
},
|
|
17253
16649
|
});
|
|
16650
|
+
// ⭐ H2 — 입력칸이 내부 턴 문에 «비켜 줬다». 꺼낸 요청을 같은 턴 실행 경로로 보낸다
|
|
16651
|
+
// (슬래시 해석·히스토리·사람 발화 에코 없음). 꺼낼 게 없으면 입력으로 되돌아간다.
|
|
16652
|
+
const consumed = consumeInternalTurnYield(internalTurnGate, input);
|
|
16653
|
+
if (consumed.kind === 'reenter') continue;
|
|
16654
|
+
const internalTurn: InternalTurnRequest | null = consumed.kind === 'internal' ? consumed.request : null;
|
|
16655
|
+
input = consumed.input;
|
|
17254
16656
|
|
|
17255
16657
|
const postTurn = resolveDashboardChatMainPostTurn(input, {
|
|
17256
16658
|
inputLines: dashboardState.getInputLines(),
|
|
@@ -17302,7 +16704,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
17302
16704
|
|
|
17303
16705
|
// Save to durable input history. Up/down reads the in-memory
|
|
17304
16706
|
// tail, while slash/LLM tools can search the full store.
|
|
17305
|
-
if (
|
|
16707
|
+
if (!internalTurn) internalTurnGate.noteHumanTurn();
|
|
16708
|
+
if (postTurn.historyRecord && !internalTurn) {
|
|
17306
16709
|
inputHistoryStore.record(postTurn.historyRecord);
|
|
17307
16710
|
refreshInputHistory();
|
|
17308
16711
|
}
|
|
@@ -17460,14 +16863,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
17460
16863
|
// resource_link blocks so dropped screenshots and PDFs flow
|
|
17461
16864
|
// naturally to the agent. debug.log gates the snapshot so
|
|
17462
16865
|
// off-state stays zero-cost.
|
|
17463
|
-
const submitIntent =
|
|
17464
|
-
|
|
17465
|
-
|
|
17466
|
-
|
|
16866
|
+
const submitIntent = internalTurn
|
|
16867
|
+
? resolveInternalTurnSubmitIntent(internalTurn, dashboardAcpSession.currentSessionId)
|
|
16868
|
+
: resolveDashboardHarnessSubmitIntent(input.text, {
|
|
16869
|
+
stickyBackend: dashboardAcpChat.getSticky(),
|
|
16870
|
+
source: input.externalSubmitSource,
|
|
16871
|
+
});
|
|
17467
16872
|
const cmdText = submitIntent.kind === 'control-turn'
|
|
17468
16873
|
? (submitIntent.commandText ?? '')
|
|
17469
16874
|
: submitIntent.text;
|
|
17470
|
-
if (submitIntent.kind === 'submit-turn' && parseSeatAsk(submitIntent.text)) {
|
|
16875
|
+
if (submitIntent.kind === 'submit-turn' && !internalTurn && parseSeatAsk(submitIntent.text)) {
|
|
17471
16876
|
try {
|
|
17472
16877
|
const receipt = await askSeat(submitIntent.text, { channel: 'tui', clientId: tuiSeatAskClientId },
|
|
17473
16878
|
ceoTaskDeps(getUserConfig(), null), tuiSeatAskDeps);
|
|
@@ -17662,7 +17067,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
17662
17067
|
openDetailViewer: (title, lines) => { setDetailViewer(title, lines); },
|
|
17663
17068
|
},
|
|
17664
17069
|
undo: {
|
|
17665
|
-
refreshGitDirty: () => {
|
|
17070
|
+
refreshGitDirty: () => { void refreshGitDirtyAsync(getSessionCwd(), { force: true }).then(() => { try { draw(); } catch { /* noop */ } }, () => { /* noop */ }); },
|
|
17666
17071
|
bold: C.bold,
|
|
17667
17072
|
},
|
|
17668
17073
|
delta: {
|
|
@@ -18048,6 +17453,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18048
17453
|
pushTurnSeparator: () => { pushTurnSeparator(); },
|
|
18049
17454
|
getLogFoldMode: () => logFoldMode,
|
|
18050
17455
|
setLogFoldMode: (mode) => { logFoldMode = mode; },
|
|
17456
|
+
setAllFolds: (expanded) => foldStack.setAll(expanded),
|
|
18051
17457
|
copyEntireLog: async () => { await copyLogBlock(-1, 'all'); },
|
|
18052
17458
|
returnFocusToInput: () => {
|
|
18053
17459
|
const sourceFocus = workingDir.focus;
|
|
@@ -18136,6 +17542,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18136
17542
|
? { url: opts.remote.url, token: opts.remote.token }
|
|
18137
17543
|
: null,
|
|
18138
17544
|
acpSwapTo: (sessionId, cwd) => dashboardAcpSession.swapTo(sessionId, cwd),
|
|
17545
|
+
acpRenew: async (cwd) => {
|
|
17546
|
+
const renewed = await dashboardAcpSession.renew(cwd);
|
|
17547
|
+
acpSessionIdRef.current = renewed;
|
|
17548
|
+
return renewed;
|
|
17549
|
+
},
|
|
18139
17550
|
},
|
|
18140
17551
|
shellSlash: {
|
|
18141
17552
|
slashRuntime: shellSlashRuntime,
|
|
@@ -18145,6 +17556,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18145
17556
|
},
|
|
18146
17557
|
termSlash: {
|
|
18147
17558
|
slashRuntime: termSlashRuntime,
|
|
17559
|
+
openNewTerminal: spawnGlobalTerminalModal,
|
|
18148
17560
|
sessionRegistry: sessionRegistry as unknown as DashboardSlashContext['termSlash']['sessionRegistry'],
|
|
18149
17561
|
terminalMatrix: terminalMatrix as unknown as DashboardSlashContext['termSlash']['terminalMatrix'],
|
|
18150
17562
|
vwRegistry: {
|
|
@@ -18197,6 +17609,21 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18197
17609
|
setWorkingFocusPreview: () => { setWorkingFocus('preview', 'slash-term-move-preview'); },
|
|
18198
17610
|
nextTerminalVwSlotId: (terminalId) => `term-slot:${terminalId}`,
|
|
18199
17611
|
},
|
|
17612
|
+
// TUI-LOOP-COMMAND — /loop <간격> <프롬프트> · 쉬는 동안만 발사
|
|
17613
|
+
// (src/dashboard/loop-scheduler.ts · 판정: idle 발사 1 · busy 미룸 · stop 0).
|
|
17614
|
+
// idle/busy 신호는 대시보드 루프가 이미 쓰는 것을 그대로 쓴다 —
|
|
17615
|
+
// 스트리밍 턴(streamingInFlight · reportTuiTurn busy/idle) +
|
|
17616
|
+
// ACP 세션 바쁨(dashboardAcpChat.isBusy()). 프롬프트 제출은
|
|
17617
|
+
// 플러그인 submitDashboardText 와 같은 기존 제출 틈
|
|
17618
|
+
// (dispatchDashboardSubmitText)으로 낸다 — 새 제출 경로를 만들지 않는다.
|
|
17619
|
+
loopCommand: {
|
|
17620
|
+
isIdle: () => !streamingInFlight && !dashboardAcpChat.isBusy(),
|
|
17621
|
+
submitPrompt: (prompt: string) => {
|
|
17622
|
+
if (dispatchDashboardSubmitText) dispatchDashboardSubmitText(prompt);
|
|
17623
|
+
else chatLines.push(C.muted(' /loop: 입력 루프가 아직 제출을 받지 않는다 — 다음 틱을 기다린다'));
|
|
17624
|
+
},
|
|
17625
|
+
schedulerRef: dashboardLoopSchedulerRef,
|
|
17626
|
+
},
|
|
18200
17627
|
};
|
|
18201
17628
|
const slashOutcome = await dashboardSlashRegistry.dispatch(cmdLower, args, slashCtx);
|
|
18202
17629
|
if (debug.enabled) {
|
|
@@ -18644,7 +18071,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18644
18071
|
// pattern. Without this draw(), the echo sits in the chatLines
|
|
18645
18072
|
// array but doesn't paint until after the skill router's
|
|
18646
18073
|
// classifier LLM call returns (~1-2s of visual silence).
|
|
18647
|
-
|
|
18074
|
+
if (internalTurn) {
|
|
18075
|
+
chatLines.push(C.muted(` ${formatInternalTurnBanner(internalTurn)}`));
|
|
18076
|
+
chatScrollOffset = -1;
|
|
18077
|
+
} else {
|
|
18078
|
+
echoSubmittedUserText(tok.text);
|
|
18079
|
+
}
|
|
18648
18080
|
if (queuedTurnEndText !== undefined && input.text === queuedTurnEndText) {
|
|
18649
18081
|
debug.log('dashboard.turn-typeahead', 'echoed', { path: 'turn-end', length: queuedTurnEndText.length });
|
|
18650
18082
|
}
|
|
@@ -18862,7 +18294,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18862
18294
|
let latestTurnUsage: import('../prompt-cache/types.js').LLMUsage | undefined;
|
|
18863
18295
|
const runPlainSubmitTurn = (submit: PlainTurnSubmit) => runDashboardChatMainPlainTurn({
|
|
18864
18296
|
// ⛔⭐ 관측 심을 «실제로» 넘긴다 — 타입만 선택적으로 넓히면 조용히 안 온다(`F12`).
|
|
18865
|
-
debugLog: (category, event, payload) => { try { debug.log(category, event, payload); } catch { /* fail-soft */ } },
|
|
18297
|
+
debugLog: (category, event, payload, opts) => { try { debug.log(category, event, payload, opts); } catch { /* fail-soft */ } },
|
|
18866
18298
|
userText: tok.text,
|
|
18867
18299
|
intent: submit.intent,
|
|
18868
18300
|
contextText: ctx,
|
|
@@ -18950,10 +18382,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18950
18382
|
userConfig: args.userConfig,
|
|
18951
18383
|
rich: false,
|
|
18952
18384
|
enabledTools: tools.map(t => t.name),
|
|
18385
|
+
seatBoot: true,
|
|
18953
18386
|
}),
|
|
18954
18387
|
];
|
|
18955
18388
|
},
|
|
18956
|
-
runAutoCompact: async ({ preamble, chatHistory, userMsg }) => {
|
|
18389
|
+
runAutoCompact: async ({ preamble, chatHistory, userMsg, onPrePassApplied }) => {
|
|
18957
18390
|
const compactMod = await import('../compact/index.js');
|
|
18958
18391
|
const { shouldAutoCompact, compactConversationPartial, compactConversation, runCompactPipeline, setVerifyProbeEnabled } = compactMod;
|
|
18959
18392
|
const { renderCompactBoundary } = await import('../chat/compact-boundary.js');
|
|
@@ -18992,8 +18425,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18992
18425
|
getCompactProvider: () => getDefaultCompactProvider(),
|
|
18993
18426
|
setVerifyProbeEnabled,
|
|
18994
18427
|
sessionId: attachedSessionId ?? undefined,
|
|
18428
|
+
onPrePassApplied,
|
|
18995
18429
|
});
|
|
18996
18430
|
},
|
|
18431
|
+
readToolDispatchCount: () => opts.remote ? null : acpToolDispatchCount,
|
|
18997
18432
|
attachChatStreamingKeys,
|
|
18998
18433
|
createOptionalSpecs: async () => {
|
|
18999
18434
|
const uc = (await import('../user-config.js')).getUserConfig();
|
|
@@ -19117,7 +18552,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19117
18552
|
recordTurnMetrics: async (fullResponse) => {
|
|
19118
18553
|
await recordDashboardTurnMetrics({
|
|
19119
18554
|
chatHistory: chat.history as ChatMessage[],
|
|
19120
|
-
|
|
18555
|
+
// 실제 턴 모델로 기록한다 — 설정 모델이면 ctx 최대치·단가가 다른 모델 것이 된다.
|
|
18556
|
+
// ACP 서버가 사용량에 실어 보낸 턴 모델 → 없으면 백엔드가 확인한 runtime 모델 → 설정.
|
|
18557
|
+
model: latestTurnUsage?.model ?? inspectActiveProvider(undefined, activeAcpRuntimeModel()).model,
|
|
19121
18558
|
fullResponse,
|
|
19122
18559
|
turnStartedAt,
|
|
19123
18560
|
usage: latestTurnUsage,
|
|
@@ -19180,6 +18617,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19180
18617
|
},
|
|
19181
18618
|
warningLine: (message) => C.warning(`${ICONS.warning} ${message}`),
|
|
19182
18619
|
pushErrorLine: (message) => { chatLines.push(C.error(`Error: ${message}`)); },
|
|
18620
|
+
pushFailureNotice: (notice, raw) => {
|
|
18621
|
+
chatLines.push(C.warning(notice));
|
|
18622
|
+
detailViewerRuntime.stage('turn error', raw.split(/\r?\n/));
|
|
18623
|
+
},
|
|
19183
18624
|
finalizeTurn: (finalStatus, finalError) => {
|
|
19184
18625
|
lastTurnFinalStatus = finalStatus;
|
|
19185
18626
|
clearEscAbortToolState(escAbortToolState);
|
|
@@ -19213,7 +18654,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19213
18654
|
const { resumeGoalFromUserFollowUp } = await import('../goals/chat-loop-bridge.js');
|
|
19214
18655
|
const { hydrateGoalFromSnapshot } = await import('../goals/registry.js');
|
|
19215
18656
|
hydrateGoalFromSnapshot();
|
|
19216
|
-
|
|
18657
|
+
// ⛔ H2 — 내부 턴은 사람의 «정정·재개» 발화가 아니다: 멈춘 /goal 을 되살리지 않는다.
|
|
18658
|
+
const followUpAction = tok.text.startsWith('/goal') || internalTurn
|
|
19217
18659
|
? { kind: 'no-loop' as const }
|
|
19218
18660
|
: resumeGoalFromUserFollowUp(tok.text);
|
|
19219
18661
|
let goalLoopUserText: string | null = followUpAction.kind === 'continue'
|
|
@@ -19224,7 +18666,15 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19224
18666
|
}
|
|
19225
18667
|
let goalLoopBudget = 100; // hard safety cap independent of /goal budget
|
|
19226
18668
|
let lastTurnAborted = false;
|
|
18669
|
+
let goalLoopIteration = 0;
|
|
19227
18670
|
do {
|
|
18671
|
+
// 앞 반복의 finalizeTurn 이 손잡이를 멈췄다 — 반복이 «실제로 시작될 때만» 새 표시를 단다(#5).
|
|
18672
|
+
thinking = goalLoopIterationThinking(goalLoopIteration++, thinking, () => startPinnedThinking({
|
|
18673
|
+
target: chatFooterLine,
|
|
18674
|
+
onFrame: () => { pinChatTail(); draw(); },
|
|
18675
|
+
message: 'Thinking',
|
|
18676
|
+
metrics: { startedAt: Date.now(), hint: 'esc 중단' },
|
|
18677
|
+
}));
|
|
19228
18678
|
const turnText = goalLoopUserText ?? tok.text;
|
|
19229
18679
|
await runDashboardChatMainSubmitIntent({
|
|
19230
18680
|
submit: createDashboardChatMainTurnSubmit({
|