elanous 0.2.35 → 0.2.36
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/chunks/{d06f9160.a3d9062f95de8ad7.js → 053a429e.9a63436527cc751c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/109.f196abf3484bb97f.js +287 -0
- package/apps/pwa/out/_next/static/chunks/{4346.8601952e73bf5803.js → 1201.afbfc5b1c5e5cbe4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1517.9275b89572098ff3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1574.ad11f8db8396ab45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1614.7a8c5c40aee0261f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1871-f1eb29d8dbb4065b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/190.285c832bd9626f8c.js +64 -0
- package/apps/pwa/out/_next/static/chunks/2193.0ea24c4a7aa1d004.js +36 -0
- package/apps/pwa/out/_next/static/chunks/223-ad865c1e80491d78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2265-f0d90b6dbec05f81.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2267.eb4a62a209f09ec4.js +149 -0
- package/apps/pwa/out/_next/static/chunks/2341-86cd06c0cf01aa33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2352.9abc42422bade876.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2397-c1653a816061842d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3103.0df8da390948bb84.js → 2500.cbc1257b774aae52.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{7644.17e37ba6f1d4226e.js → 2683.c9773a0c589b031b.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/2709.8e1889034255fc5f.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{4718-17d2af636796a330.js → 275-02ea4c2a33980422.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/2812ca6a.3b9f4a7a08885744.js +1 -0
- package/apps/pwa/out/_next/static/chunks/284.ac77c0db55bfd3a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8714.ff0b7ce3ec3e3ae2.js → 2941.3c3546f96bc6efa7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2976.b000698a3c6512dc.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{9214.63a510d39178e997.js → 2986.45d7652e4f13557e.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3031-85328a92fcce4da6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3136.30aaaa5fa03662a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3677-4215f826f6aa68e5.js → 3148-98efd4869d660a87.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3155.2587a55196ecb394.js +226 -0
- package/apps/pwa/out/_next/static/chunks/3277.785db1cf09f98601.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3320-f9c77381f85a2c0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3324.a09703033abcbd2f.js +34 -0
- package/apps/pwa/out/_next/static/chunks/3386-91049feabff9a94e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/339.80afb7e8839372af.js +8 -0
- package/apps/pwa/out/_next/static/chunks/3410.7f9af5ab77cbe65f.js +115 -0
- package/apps/pwa/out/_next/static/chunks/3538.a445e50bb359d068.js +32 -0
- package/apps/pwa/out/_next/static/chunks/3540c645.2595a6af01a97672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3621.581b2054cc14bee4.js +286 -0
- package/apps/pwa/out/_next/static/chunks/3676.8c28d16fd84ca843.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9512.5a76207a730e0491.js → 3727.7949ccaacaa71242.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/375-b84540853523fe52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8557-e7898769499cd56f.js → 3757-180308bc61021b05.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/379.ee262e37586fe1ae.js +74 -0
- package/apps/pwa/out/_next/static/chunks/{359-2250789a2d3499e7.js → 3840-5825d56d46976a92.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3969-81054381eaec4c4c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{da5176c6.6773b6c5994e1d99.js → 3a3d9966.cc4d4585a2708e7c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4067-bf9aa60547972937.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4116.a84a92a2492c080e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4176.e5729c93c29151eb.js +104 -0
- package/apps/pwa/out/_next/static/chunks/4227-e6c84686bc8946ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9952.5daf5f94063a2b3e.js → 4286.b70c4520306817e8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4304.e9b2512834a9e23e.js +23 -0
- package/apps/pwa/out/_next/static/chunks/4327.b9e08a71c0009348.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{938.66ad1ef0e5abae1c.js → 4615.8525eb0add35eba6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4946.bd106eafd668f248.js +148 -0
- package/apps/pwa/out/_next/static/chunks/5085-bce4904931767ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5125.2eb9968b83767193.js +93 -0
- package/apps/pwa/out/_next/static/chunks/5128-75b733fd74a9a1eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6236.e18cced2d5ba6afb.js → 528.38ce20ef8aa251ff.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/5489f3fe.4bb1458b09f6dead.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{3408.21d1570602f4b6c0.js → 552.b7d061ad91eeac51.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5560-2a4648f6a30f16e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5711.957b095705c12560.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1530.0e612369b6fd7ea0.js → 575.d929793a53f27c4b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5772-e29e14f8ff127122.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5887.5b2f5b44ce0f7e9a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5899-4c112a2d908cf97f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/599.c39c571b6733f2a2.js +41 -0
- package/apps/pwa/out/_next/static/chunks/6041.1b65efb796ebc2a5.js +79 -0
- package/apps/pwa/out/_next/static/chunks/6057-765c0a26028c86b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6142.f289e0d039f453a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6199-6bcd61d2a3910e04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6236.cdf7293e710a9b2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6387-237e079dac9a3ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6448-b6d272d074276e14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6466-92e65a48490b68a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6566.926a1d1b7c911442.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6580-cdc0e4a634ce4708.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6659-b242dc31b20b3f29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6690.c5d9b321b1f79384.js +1 -0
- package/apps/pwa/out/_next/static/chunks/671-e9b6ad0c771c5514.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6862.bcbf89dd0f4496b3.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6912-7b375ee48732c767.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6925.562d532110879886.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7385.50a308a477bdfc21.js → 6943.c6b24124853a47b2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{630.fe5a46428d63b013.js → 727.7deb87749b0a343a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2018.a741297bce759490.js → 7481.39f7a4654907bf6e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7545.b1aa42cd15503319.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7551-416d298c2dc4a9ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8444.4c26b379ca2b9596.js → 771.c3cdb6c1f7f47c02.js} +17 -17
- package/apps/pwa/out/_next/static/chunks/7784.1f0ac4a16d190cf4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7870-309de463786ac1fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7888-e29b151bb6a3bb8b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3589.5444a06bcf6e775c.js → 7891.03f910124a737985.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7905-6ba78d9234608f79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8037.e254fd797aa224d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8207.f527886b7c464529.js +1 -0
- package/apps/pwa/out/_next/static/chunks/856.50bda61439d465e6.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{b2f6ba4d.fb56fdbbf798df33.js → 873d6c12.09bd499f57171543.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5101.e32282718f99ed6f.js → 8904.7d51af2dc033965c.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9031-92e9b487edb681ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9060.5410d9f6a95f6287.js +90 -0
- package/apps/pwa/out/_next/static/chunks/{5637.281e5dec4ea6ecf1.js → 914.8abefd48d579547b.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9187-c87a2207d4ac5d46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4705-194e64e47ca0fbdb.js → 9250-072df22eaadaa6b5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9289.3e92f278821e18e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9319.7816d3cc741cd34f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9327-e016bb9f1674d24f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9365.3de48bf75193ff9a.js +77 -0
- package/apps/pwa/out/_next/static/chunks/9383.5b5e9aa56b5fea97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9387-cb929573ef436f39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9489.c27becacc3283dec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8637.47f5b35afcae5e6b.js → 9514.131c52cc75fe6439.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9536.28962663c7734376.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9686-12286f5bbf15a5a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9776.289d40cc5a4c342a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1674.2a65fd5668e1cc5f.js → 9789.60f723d18097ec30.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/989.f0c17523c5e61f65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9972-1df393aa8d4f7d52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9f31e478.fefcbcd3c388ebc5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-96750aba7430959f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4105e5bbbb043024.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5d4fa75c2acb113e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-a79a9bdb3b2142bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f1c2047484992a35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-15ffc49b2d8c70e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-67d53a3fc8734135.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-e30c8dc696491336.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-2136e04fa77696e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-fecd2814790c2e12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-280aa4a98fc5bb44.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c14e96b65fb4b883.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-187dac74825ed6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-6bc7246ea8c92448.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-083747f1d4518f61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-96750aba7430959f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-15ca95a16ec96e1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a8ac031b3810eb64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-8835f93110494cda.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-e2de2e59111c9aad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f2b2e4bf6e981450.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5b92b7f75df20948.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-468eddf2b1a84ecb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2af16e62c12f22df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-18b40867a77a5793.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-fe934f2979f5a4a2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-3eec435537357170.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ff88cbfd2aa99fc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a5b39fb780c70ab5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ae465dde1d1b3933.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e7abc34a48a6f71e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-cab9674fde9ead26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-1da49bc8712284d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5e8aa657551ac912.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-fcc36f36c6dc1fe2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d22db22f2a7296ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-d12dc7b1773878f0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-99debec9ea7fbec0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-96750aba7430959f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-2a088a78bad7e924.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-2e8ed57db7e69993.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-4b3a16213c2ae8fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f098747ddf38c98b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-c15958835534404b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-68279e3c843c1fd7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a34a74453367a39d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8767a7db2155a5bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-bd5976683460d7f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-b7364835e7f2ef54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-311af16b3ee0ffca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fb670757274ae990.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{0368f25d-ad84af92fefee68f.js → ccae1c4b-0cf414f5eff1b10d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{cbe38a15.5a032598e39970c6.js → ceba0382.650712b56b85b582.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{b6705a4f-b9a9047de9eecfd0.js → da5ecc5c-b005ad8cac737d57.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{56d372f2.681ab4ec4c998fae.js → f9a61e4d.1fc2c1dbdd49fe46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-6d497cbf1eef04c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-012f5edec932f337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-da5aeb809edc94a2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-5ee319af6f749dfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-4bc84653a718d6c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-8a96cf6775c73d6e.js +1 -0
- package/apps/pwa/out/_next/static/{T1mBQqmkwKDLGtyisDCOd → uC-N0emedeNOFuaBQFv-I}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/docs/goal-context/05-authoring-lessons.jsonl +47 -0
- package/docs/goal-context/README.md +3 -0
- package/graphs/default-loop.yaml +6 -1
- package/graphs/implement-loop.yaml +24 -0
- package/graphs/release/recipes.yaml +6 -0
- package/graphs/release/release-loop.yaml +17 -4
- package/graphs/wizard/recipes.yaml +44 -0
- package/graphs/wizard/wizard-loop.yaml +44 -0
- package/graphs/work/research-report.yaml +2 -0
- package/package.json +1 -1
- package/scripts/ci-typecheck-changed.ts +139 -5
- package/scripts/coord-role-pack.ts +1 -1
- package/scripts/first10-gate.ts +416 -0
- package/scripts/fleet/wsl-node-setup.sh +432 -0
- package/scripts/release-loop/docs-node.ts +204 -8
- package/scripts/release-loop/gate-node.ts +249 -53
- package/scripts/release-loop/node-verdict.ts +2 -0
- package/scripts/release-loop/resume-cap.ts +14 -34
- package/scripts/release-loop/resume-release.ts +37 -11
- package/scripts/release-loop/traffic-node.ts +59 -0
- package/scripts/release-loop/traffic-transition.ts +159 -0
- package/scripts/rhythm/daily.ts +137 -3
- package/scripts/ux-sim.ts +53 -0
- package/skills/README.md +1 -0
- package/src/acp/core-turn-bridge.ts +48 -6
- package/src/agent/delegate-hint.ts +16 -0
- package/src/agent/runner.ts +82 -2
- package/src/agent/task-notification.ts +56 -10
- package/src/agent/types.ts +4 -0
- package/src/agent-substrate/pr-reviewer.ts +55 -3
- package/src/agent-substrate/review-intent.ts +29 -2
- package/src/agent-substrate/review-observation.ts +3 -0
- package/src/agent-substrate/self-review-cli.ts +168 -16
- package/src/autopilot/mission-codebase-gate.ts +141 -12
- package/src/autopilot/pr-manager.ts +25 -5
- package/src/boot/daemon-tools/self-implement.ts +1 -0
- package/src/budget/usage-outcomes.ts +8 -4
- package/src/capture/tui-self-report.ts +102 -0
- package/src/chat/goal-continue-verdict.ts +41 -0
- package/src/chat/index.ts +30 -4
- package/src/chat/internal-turn-origin.ts +1 -0
- package/src/chat/paste-fold.ts +86 -0
- package/src/chat/tool-render/block.ts +32 -0
- package/src/chat/tool-render/dispatch.ts +2 -20
- package/src/chat/tool-render/result-text.ts +98 -0
- package/src/chat/turn-typeahead.ts +70 -7
- package/src/cli/config-audit.ts +119 -0
- package/src/cli/doctor-cli.ts +22 -0
- package/src/cli/guardian-cli.ts +10 -0
- package/src/cli/herdr-cli.ts +34 -0
- package/src/cli/light-entry-route.ts +6 -3
- package/src/cli/log-event-names.ts +2 -0
- package/src/cli/make-cli.ts +247 -11
- package/src/cli/msg-cli.ts +117 -3
- package/src/cli/pr-cli.ts +29 -16
- package/src/cli/pr-land-base-read.ts +11 -2
- package/src/cli/pr-land-heavy-remote.ts +6 -3
- package/src/cli/pr-land-overlap-evidence.ts +23 -4
- package/src/cli/pr-land-recovery.ts +117 -1
- package/src/cli/pty-takeover-cli.ts +8 -7
- package/src/cli/release-cli.ts +21 -5
- package/src/cli/release-single-flight.ts +1 -1
- package/src/cli/tasks-card-show.ts +162 -0
- package/src/cli/tasks-cli.ts +574 -356
- package/src/cli/tasks-read-cli.ts +197 -0
- package/src/core-turn/run-core-turn.ts +4 -2
- package/src/core-turn/run-goal-loop.ts +73 -16
- package/src/core-turn/types.ts +6 -1
- package/src/dashboard/context/keys.ts +8 -31
- package/src/dashboard/index.ts +202 -49
- package/src/dashboard/input/agent-wake.ts +7 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +146 -3
- package/src/dashboard/input/chat-main-slash-command.ts +4 -2
- package/src/dashboard/input/goal-continue.ts +210 -0
- package/src/dashboard/input/goal-interim.ts +2 -0
- package/src/dashboard/input/internal-turn.ts +2 -8
- package/src/dashboard/input/seat-autowake.ts +9 -7
- package/src/dashboard/input/turn-autoheal.ts +52 -0
- package/src/dashboard/seat-boot-preamble.ts +132 -5
- package/src/dashboard/slash-runtime/chat-persona.ts +65 -8
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +3 -2
- package/src/dashboard/slash-runtime/model-choices.ts +15 -0
- package/src/dashboard/stale-install-check.ts +3 -99
- package/src/debug/batch-queue.ts +119 -0
- package/src/debug/tui-log-tag.ts +67 -0
- package/src/display/agent-surface.ts +7 -0
- package/src/domains/standalone-log-sink.ts +15 -2
- package/src/esc-abort-gate.ts +69 -0
- package/src/git-fs/worktree.ts +95 -1
- package/src/graph-runner/graph-cli.ts +18 -0
- package/src/graph-runner/runner.ts +20 -11
- package/src/graph-wizard/generate.ts +108 -4
- package/src/graph-wizard/steps.ts +15 -0
- package/src/grounding/proactive-ground.ts +86 -0
- package/src/harness/control-inbox.ts +49 -1
- package/src/harness/harness-cli-command.ts +51 -7
- package/src/harness/harness-incidents.ts +52 -1
- package/src/harness/pod-control-send.ts +33 -5
- package/src/harness/self-send-remote.ts +69 -0
- package/src/harness/self-send-routing-guard.ts +6 -0
- package/src/harness/self-send-target.ts +4 -3
- package/src/index.ts +327 -12
- package/src/input-core/context-key-runtime.ts +21 -0
- package/src/light-cli.ts +6 -1
- package/src/llm-wellformed.ts +70 -0
- package/src/llm.ts +35 -4
- package/src/loops/msg-wake.ts +35 -39
- package/src/loops/orchestrator/inbox.ts +59 -11
- package/src/loops/orchestrator/knowhow-pack.ts +364 -0
- package/src/loops/orchestrator/tick.ts +5 -1
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/models/prompts.ts +17 -2
- package/src/msg/msg-post-office.ts +116 -0
- package/src/mss/logging/log-store.ts +79 -22
- package/src/nexus/api/decisions-route.ts +38 -18
- package/src/nexus/api/graph-wizard-api.ts +20 -2
- package/src/nexus/boot/register-mcp-clients.ts +263 -38
- package/src/nexus/onboarding/first-boot-wizard.ts +56 -8
- package/src/onboarding/first-run.ts +37 -6
- package/src/onboarding/web-first.ts +43 -11
- package/src/ops/overview-collect.ts +12 -6
- package/src/ops/overview.ts +3 -3
- package/src/persona/loader.ts +11 -0
- package/src/persona/types.ts +8 -0
- package/src/prompt-library/session-guidance.ts +14 -0
- package/src/provider-summary.ts +8 -0
- package/src/pty-shell/herdr-agent-report.ts +118 -9
- package/src/pty-shell/herdr-prompt.ts +266 -0
- package/src/pty-shell/pty-control-ipc.ts +151 -8
- package/src/pty-shell/pty-special-keys.ts +42 -2
- package/src/pty-shell/stale-install-check.ts +99 -0
- package/src/pty-shell/tui-run-state.ts +14 -2
- package/src/release-loop/auto-start.ts +20 -2
- package/src/release-loop/checklist.ts +76 -4
- package/src/release-loop/rehearsal.ts +36 -20
- package/src/repl/index.ts +19 -1
- package/src/seat-loop/knowhow-lint.ts +1 -1
- package/src/self-dev/decompose-proposal.ts +172 -20
- package/src/self-dev/decompose.ts +61 -0
- package/src/self-dev/dev-cli.ts +37 -3
- package/src/self-dev/dev-pipeline.ts +8 -2
- package/src/self-dev/draft-triage-rules.ts +31 -0
- package/src/self-dev/glm-credential-guard.ts +77 -0
- package/src/self-dev/graph-journey-nodes.ts +1 -0
- package/src/self-dev/graph-journey-route.ts +12 -2
- package/src/self-dev/orchestrate-cli.ts +68 -3
- package/src/self-dev/orchestrate.ts +116 -17
- package/src/self-dev/run-supervisor.ts +55 -2
- package/src/self-dev/run-types.ts +1 -1
- package/src/self-health/autoheal.ts +153 -0
- package/src/self-health/signatures.ts +6 -0
- package/src/self-implement/device-first.ts +116 -0
- package/src/self-implement/doctor-visit.ts +239 -1
- package/src/self-implement/evidence-command-budget.ts +80 -0
- package/src/self-implement/goal-author-cli.ts +1 -0
- package/src/self-implement/goal-author-decompose.ts +265 -4
- package/src/self-implement/goal-author.ts +33 -8
- package/src/self-implement/goal-elaborate.ts +176 -22
- package/src/self-implement/goal-refine.ts +3 -0
- package/src/self-implement/graph-variant.ts +40 -6
- package/src/self-implement/headless-elanous-driver.ts +19 -3
- package/src/self-implement/off-diff-evidence.ts +58 -6
- package/src/self-implement/orchestrator.ts +222 -21
- package/src/self-implement/pod-warm-pool-profile.ts +1 -0
- package/src/self-implement/pwa-import-graph.ts +1 -1
- package/src/self-implement/rework-policy.ts +30 -0
- package/src/self-implement/run-ledger.ts +108 -0
- package/src/self-implement/run-ttl.ts +38 -24
- package/src/self-implement/scope-adjacency.ts +3 -1
- package/src/self-implement/seams.ts +60 -4
- package/src/self-implement/wiring-check.ts +358 -0
- package/src/shell-env-bootstrap.ts +11 -4
- package/src/skills/runner.ts +4 -2
- package/src/skills/tools/agent.ts +290 -48
- package/src/skills/tools/schedule-wakeup.ts +23 -4
- package/src/skills/tools/shell.ts +61 -6
- package/src/skills/tools/terminal-modal-inject.ts +1 -2
- package/src/skills/tools/terminal-modal.ts +10 -8
- package/src/skills/url-router.ts +41 -1
- package/src/smart-wizard/run.ts +66 -0
- package/src/status/bar.ts +34 -6
- package/src/status/status-provider-info.ts +37 -6
- package/src/steward/rejudge-tick.ts +10 -2
- package/src/steward/ta-repair-event-collect.ts +17 -6
- package/src/task-agent/actions.ts +3 -1
- package/src/task-agent/land-queue.ts +21 -8
- package/src/task-agent/live-moves.ts +22 -6
- package/src/task-agent/mission.ts +30 -0
- package/src/task-agent/rejudge-on-head.ts +15 -0
- package/src/task-agent/repair-cycle.ts +138 -32
- package/src/task-agent/repair-next-move.ts +31 -3
- package/src/task-agent/repair-outcomes.ts +29 -0
- package/src/task-agent/repair-worker.ts +14 -4
- package/src/task-agent/shadow.ts +41 -3
- package/src/task-agent/task-card-prefix.ts +2 -0
- package/src/task-agent/task-hand.ts +477 -22
- package/src/task-agent/task-parent-role-llm.ts +36 -0
- package/src/task-agent/tier-split.ts +78 -9
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +9 -0
- package/src/task-orchestrator/surfaces/pod-child-reuse.ts +146 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +86 -5
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +215 -3
- package/src/task-orchestrator/surfaces/pod-launch-history.ts +133 -0
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +11 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +76 -4
- package/src/task-orchestrator/surfaces/pod-provenance.ts +8 -2
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +18 -3
- package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +16 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +224 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +201 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-provider.ts +444 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-runner.ts +387 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-transport.ts +157 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +485 -36
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +3 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +396 -19
- package/src/task-orchestrator/surfaces/self-implement.ts +42 -2
- package/src/tool-runtime/index.ts +15 -2
- package/src/tool-runtime/mcp-proxy-bootstrap.ts +29 -0
- package/src/tty-foreground.ts +155 -0
- package/src/tui/chat/goal-interim.ts +173 -0
- package/src/tui/chat/log-view.ts +205 -18
- package/src/tui/chat/self-health-status.ts +232 -0
- package/src/tui/entry.ts +370 -81
- package/src/tui/input/composer.ts +8 -1
- package/src/tui/slash/compact-command.ts +7 -0
- package/src/tui/slash/compact.ts +1 -7
- package/src/tui/slash/resume.ts +75 -0
- package/src/tui-client/local-chat-turn.ts +45 -2
- package/src/tui-client/next-dashboard-runtime.ts +8 -0
- package/src/tui-client/next-loop-command.ts +70 -0
- package/src/tui-client/next-wakeup.ts +191 -0
- package/src/tui.ts +37 -0
- package/src/ui/modal-adapter.ts +2 -2
- package/src/ux-sim/classic-tui-seat-scenarios.ts +516 -0
- package/src/ux-sim/cli-config-audit-device.ts +121 -0
- package/src/ux-sim/dashboard-event-loop-stall-monitor-device.ts +238 -0
- package/src/ux-sim/event-monkey.ts +3 -2
- package/src/ux-sim/fake-bin.ts +45 -0
- package/src/ux-sim/fake-herdr.ts +238 -0
- package/src/ux-sim/fake-llm.ts +111 -6
- package/src/ux-sim/fault-glm-no-credential.ts +116 -0
- package/src/ux-sim/fault-nextmove-dispatch.ts +102 -1
- package/src/ux-sim/fault-scope-escape-lift.ts +33 -6
- package/src/ux-sim/fault-tui-400-autoheal.ts +297 -0
- package/src/ux-sim/goal-device.ts +619 -0
- package/src/ux-sim/in-process-cli.ts +22 -5
- package/src/ux-sim/mock-authoring-first-run-child.ts +35 -0
- package/src/ux-sim/mock-authoring-first-run.ts +97 -0
- package/src/ux-sim/mock-seat-pty-frame-child.ts +29 -0
- package/src/ux-sim/mock-seat-pty-frame.ts +297 -0
- package/src/ux-sim/mock-seat-pty-sequence-child.ts +21 -0
- package/src/ux-sim/mock-seat-pty-sequence.ts +206 -0
- package/src/ux-sim/next-tui-seat-scenarios.ts +631 -0
- package/src/ux-sim/onboarding-first-run-device.ts +269 -0
- package/src/ux-sim/pty-key-modifiers-device.ts +85 -0
- package/src/ux-sim/subagent-tools-device.ts +81 -0
- package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +250 -0
- package/src/ux-sim/tty-foreground-steal-child.ts +21 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +0 -1
- package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +0 -32
- package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +0 -41
- package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +0 -24
- package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +0 -74
- package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1993-c0007b7ad0c0ef41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +0 -148
- package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2625.5d870aa999f33db1.js +0 -34
- package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +0 -77
- package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +0 -62
- package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +0 -90
- package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +0 -36
- package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +0 -79
- package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +0 -122
- package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4441-875b97f5821ed58a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +0 -24
- package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +0 -23
- package/apps/pwa/out/_next/static/chunks/5483-dd5d7697f6bdb106.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +0 -287
- package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +0 -1
- package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +0 -8
- package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +0 -27
- package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +0 -93
- package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6b89c8bd.24df687a820500b3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +0 -149
- package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +0 -104
- package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8219.baa715d1deee80e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +0 -226
- package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +0 -64
- package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +0 -115
- package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/967-2aa39b2d04b4c690.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-406b47745b53f269.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-47c5d4db587266bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-dc0a532f5b9196ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +0 -1
- /package/apps/pwa/out/_next/static/{T1mBQqmkwKDLGtyisDCOd → uC-N0emedeNOFuaBQFv-I}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
// N03 key-sequence scenarios for next TUI (`/new` · `/compact`) on the network-free fake LLM.
|
|
2
|
+
// The TUI under test is whatever executable/entry is given (installed `elanous.mjs` or a tree's
|
|
3
|
+
// `bin/elanous.mjs`); this file only types keys, seeds the store through the real session API,
|
|
4
|
+
// and reads the universe back through the same binary's `logs` CLI.
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { chmodSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { join, resolve } from 'node:path';
|
|
8
|
+
import { appendMessage, createSession, getActiveSessionId, loadSession, setActiveSessionId } from '../session/index.js';
|
|
9
|
+
import { mockSeatPtySequence, type SeatPtySequenceEvidence, type SeatPtyStep } from './mock-seat-pty-sequence.js';
|
|
10
|
+
import { pasteFoldToken } from '../chat/paste-fold.js';
|
|
11
|
+
import { DEFAULT_GOAL_CONTINUE_CAP } from '../dashboard/input/goal-continue.js';
|
|
12
|
+
import {
|
|
13
|
+
TEST_FAKE_LLM_DELAY_MS_ENV, TEST_FAKE_LLM_DELAY_WHEN_ENV, TEST_FAKE_LLM_ENV, TEST_FAKE_LLM_RESPONSE_ENV, TEST_FAKE_LLM_TOOL_CALL_ENV,
|
|
14
|
+
} from './fake-llm.js';
|
|
15
|
+
|
|
16
|
+
export const NEXT_TUI_SCENARIOS = ['next-new-session', 'next-compact', 'next-queue', 'next-wakeup', 'next-tool-fold', 'next-tool-fold-read', 'next-goal-continue', 'next-goal-interim', 'next-proactive-continue', 'next-paste'] as const;
|
|
17
|
+
export type NextTuiScenario = (typeof NEXT_TUI_SCENARIOS)[number];
|
|
18
|
+
|
|
19
|
+
export interface NextTuiScenarioSpec {
|
|
20
|
+
scenario: NextTuiScenario;
|
|
21
|
+
/** Kept universe: config-dir = state-dir = this root. */
|
|
22
|
+
root: string;
|
|
23
|
+
executable: string;
|
|
24
|
+
entry: string;
|
|
25
|
+
expectedBinarySha256?: string;
|
|
26
|
+
reply?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface NextTuiScenarioResult {
|
|
30
|
+
scenario: NextTuiScenario;
|
|
31
|
+
universe: string;
|
|
32
|
+
run: SeatPtySequenceEvidence;
|
|
33
|
+
store: Record<string, unknown>;
|
|
34
|
+
/** Raw `logs --json` lines read through the same binary in the same universe. */
|
|
35
|
+
logs: { command: string; rc: number | null; lines: string[]; eventCount: number; limit: number };
|
|
36
|
+
checks: Record<string, boolean>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DEFAULT_REPLY = '[fake-llm] 답했습니다';
|
|
40
|
+
|
|
41
|
+
// Assumes the session store resolves its root per call (sessionRoot()/stateRoot() read env each time —
|
|
42
|
+
// session/index.ts); a root cached at import would send the seed to the wrong universe.
|
|
43
|
+
function withStore<T>(root: string, fn: () => T): T {
|
|
44
|
+
const saved = { state: process.env.ELANOUS_STATE_DIR, source: process.env.ELANOUS_STATE_DIR_SOURCE, sessions: process.env.ELANOUS_SESSION_ROOT };
|
|
45
|
+
process.env.ELANOUS_STATE_DIR = root;
|
|
46
|
+
process.env.ELANOUS_STATE_DIR_SOURCE = 'explicit';
|
|
47
|
+
process.env.ELANOUS_SESSION_ROOT = join(root, 'sessions');
|
|
48
|
+
try { return fn(); } finally {
|
|
49
|
+
if (saved.state === undefined) delete process.env.ELANOUS_STATE_DIR; else process.env.ELANOUS_STATE_DIR = saved.state;
|
|
50
|
+
if (saved.source === undefined) delete process.env.ELANOUS_STATE_DIR_SOURCE; else process.env.ELANOUS_STATE_DIR_SOURCE = saved.source;
|
|
51
|
+
if (saved.sessions === undefined) delete process.env.ELANOUS_SESSION_ROOT; else process.env.ELANOUS_SESSION_ROOT = saved.sessions;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function userTexts(root: string, id: string): string[] {
|
|
56
|
+
return withStore(root, () => (loadSession(id)?.messages ?? []).filter((m) => m.role === 'user').map((m) => m.content));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function readLogs(spec: Pick<NextTuiScenarioSpec, 'root' | 'executable' | 'entry'>, categories: string): NextTuiScenarioResult['logs'] {
|
|
60
|
+
const args = [spec.entry, '--config-dir', spec.root, '--test-state-dir', spec.root, 'logs', '--category', categories, '--since', '2h', '--limit', '200', '--json'];
|
|
61
|
+
// Same scrubbed shape as the PTY child: no provider keys, no login-shell import.
|
|
62
|
+
const env = { PATH: process.env.PATH ?? '', HOME: spec.root, ELANOUS_STATE_DIR: spec.root, ELANOUS_STATE_DIR_SOURCE: 'explicit', ELANOUS_SKIP_LOGIN_ENV: '1', LANG: process.env.LANG ?? 'C.UTF-8' } as unknown as NodeJS.ProcessEnv;
|
|
63
|
+
const r = spawnSync(spec.executable, args, { encoding: 'utf8', timeout: 60_000, env });
|
|
64
|
+
const lines = `${r.stdout ?? ''}`.split('\n').filter((l) => l.trim().startsWith('{'));
|
|
65
|
+
return {
|
|
66
|
+
command: `${spec.executable} ${args.join(' ')}`,
|
|
67
|
+
rc: r.status,
|
|
68
|
+
lines,
|
|
69
|
+
// Rows read (meta lines excluded) against the --limit, so a truncated read is visible in the evidence.
|
|
70
|
+
eventCount: lines.filter((l) => !l.includes('"_meta"')).length,
|
|
71
|
+
limit: 200,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const count = (screen: string, text: string) => screen.split(text).length - 1;
|
|
76
|
+
|
|
77
|
+
/** A malformed `data` field becomes `{}` so the check reads false instead of the scenario throwing. */
|
|
78
|
+
export function parseData<T>(data: unknown): Partial<T> {
|
|
79
|
+
if (typeof data !== 'string') return {};
|
|
80
|
+
try { return JSON.parse(data) as Partial<T>; } catch { return {}; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function runNextTuiScenario(spec: NextTuiScenarioSpec): Promise<NextTuiScenarioResult> {
|
|
84
|
+
const root = resolve(spec.root);
|
|
85
|
+
mkdirSync(root, { recursive: true });
|
|
86
|
+
// First-run onboarding would otherwise ask for a provider before the TUI opens.
|
|
87
|
+
writeFileSync(join(root, 'config.json'), JSON.stringify({ onboarding: { completed: true }, errorReports: { enabled: false } }));
|
|
88
|
+
const reply = spec.reply ?? DEFAULT_REPLY;
|
|
89
|
+
const base = {
|
|
90
|
+
executable: spec.executable,
|
|
91
|
+
args: [spec.entry, '--config-dir', '{root}', '--test-state-dir', '{root}', '--tui', 'next'],
|
|
92
|
+
root,
|
|
93
|
+
rows: 24,
|
|
94
|
+
cols: 80,
|
|
95
|
+
...(spec.expectedBinarySha256 ? { expectedBinarySha256: spec.expectedBinarySha256 } : {}),
|
|
96
|
+
env: { ELANOUS_SESSION_ROOT: join(root, 'sessions'), [TEST_FAKE_LLM_ENV]: '1', [TEST_FAKE_LLM_RESPONSE_ENV]: reply },
|
|
97
|
+
ready: { label: 'boot', until: (s: string) => s.includes('elanous next ·') && s.includes('Ctrl+Q 종료') },
|
|
98
|
+
};
|
|
99
|
+
const done = (n: number, also?: string) => (s: string) => count(s, reply) === n && s.includes('응답 완료') && (!also || s.includes(also));
|
|
100
|
+
|
|
101
|
+
if (spec.scenario === 'next-new-session') {
|
|
102
|
+
// Step 1–2: two turns · step 3: /new · step 4: one turn · step 5: a turn with /new typed while it is busy.
|
|
103
|
+
let oldId = '';
|
|
104
|
+
const steps: SeatPtyStep[] = [
|
|
105
|
+
{ label: 'after message 1', text: '첫 메시지', until: done(1) },
|
|
106
|
+
{ label: 'before /new (2 messages)', text: '둘째 메시지', until: done(2) },
|
|
107
|
+
{ label: 'after /new', text: '/new', until: (s) => /새 대화 · 이전 = [0-9a-f]{8}/.test(s) },
|
|
108
|
+
{ label: 'after message 3 (new session)', text: '셋째 메시지', until: done(1, '셋째 메시지') },
|
|
109
|
+
// `/new` lands while the 4th turn is in flight: the TUI must refuse it and keep the session.
|
|
110
|
+
{ label: 'busy /new', chunks: ['넷째 메시지', '\r', '/new\r'], until: (s) => s.includes('응답 중에는 /new') || done(2, '넷째 메시지')(s) },
|
|
111
|
+
{ label: 'after busy turn', until: done(2, '넷째 메시지') },
|
|
112
|
+
];
|
|
113
|
+
const run = await mockSeatPtySequence({ ...base, steps });
|
|
114
|
+
const ids = withStore(root, () => ({ active: getActiveSessionId() ?? '' }));
|
|
115
|
+
const logs = readLogs(spec, 'tui.next,llm.route-decision');
|
|
116
|
+
const events = logs.lines.map((l) => { try { return JSON.parse(l) as { category?: string; event?: string; data?: string }; } catch { return {}; } });
|
|
117
|
+
const sessionNew = events.filter((e) => e.category === 'tui.next' && e.event === 'session-new');
|
|
118
|
+
const firstNew = sessionNew[0];
|
|
119
|
+
const parsed = parseData<{ from: string; to: string }>(firstNew?.data);
|
|
120
|
+
const fromTo = { from: parsed.from ?? '', to: parsed.to ?? '' };
|
|
121
|
+
oldId = fromTo.from;
|
|
122
|
+
const store = { oldId, newId: fromTo.to, active: ids.active, oldUser: oldId ? userTexts(root, oldId) : [], newUser: fromTo.to ? userTexts(root, fromTo.to) : [] };
|
|
123
|
+
const routes = events.filter((e) => e.category === 'llm.route-decision' && e.event === 'local-chat-executed').map((e) => parseData<{ provider: string; sessionId: string }>(e.data));
|
|
124
|
+
const afterNew = run.frames.find((f) => f.label === 'after /new')?.screen ?? '';
|
|
125
|
+
const checks = {
|
|
126
|
+
screenClearedWithNewTitle: afterNew.includes('elanous next · 새 대화') && !afterNew.includes('첫 메시지') && !afterNew.includes(reply),
|
|
127
|
+
statusNamesPrevious: Boolean(oldId) && afterNew.includes(`새 대화 · 이전 = ${oldId.slice(0, 8)}`),
|
|
128
|
+
sessionNewObservedOnce: sessionNew.length === 1,
|
|
129
|
+
nextTurnInNewSession: routes.length >= 3 && routes[2]?.sessionId === fromTo.to && routes[0]?.sessionId === oldId,
|
|
130
|
+
oldSessionKept: JSON.stringify(store.oldUser) === JSON.stringify(['첫 메시지', '둘째 메시지']),
|
|
131
|
+
busyNewRejected: events.some((e) => e.category === 'tui.next' && e.event === 'session-new-rejected'),
|
|
132
|
+
busyKeptSession: ids.active === fromTo.to && JSON.stringify(store.newUser) === JSON.stringify(['셋째 메시지', '넷째 메시지']),
|
|
133
|
+
onlyFakeProvider: routes.length > 0 && routes.every((r) => r.provider === 'test-fake-llm'),
|
|
134
|
+
exitedClean: run.exitCode === 0,
|
|
135
|
+
};
|
|
136
|
+
return { scenario: spec.scenario, universe: root, run, store, logs, checks };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (spec.scenario === 'next-tool-fold') return runNextToolFoldScenario(spec, root, base, reply, done);
|
|
140
|
+
if (spec.scenario === 'next-tool-fold-read') return runNextToolFoldReadScenario(spec, root, base, done);
|
|
141
|
+
if (spec.scenario === 'next-queue') return runNextQueueScenario(spec, root, base, reply, done);
|
|
142
|
+
if (spec.scenario === 'next-proactive-continue') return runNextProactiveContinueScenario(spec, root, base);
|
|
143
|
+
if (spec.scenario === 'next-wakeup') return runNextWakeupScenario(spec, root, base, reply, done);
|
|
144
|
+
if (spec.scenario === 'next-paste') return runNextPasteScenario(spec, root, base, reply);
|
|
145
|
+
if (spec.scenario === 'next-goal-continue') return runNextGoalContinueScenario(spec, root, base);
|
|
146
|
+
if (spec.scenario === 'next-goal-interim') return runNextGoalInterimScenario(spec, root, base);
|
|
147
|
+
|
|
148
|
+
// next-compact: 12 × ~3k tokens ≈ 36k > 0.85 × 32k (the fake model's default window) seeded
|
|
149
|
+
// through the real session store, then one turn (auto) and one `/compact` (manual).
|
|
150
|
+
const id = withStore(root, () => {
|
|
151
|
+
const sid = createSession({ source: 'tui' }).id;
|
|
152
|
+
for (let i = 0; i < 12; i++) {
|
|
153
|
+
appendMessage(sid, { role: i % 2 === 0 ? 'user' : 'assistant', content: `${i}:${'x'.repeat(12_000)}`, ts: new Date().toISOString() });
|
|
154
|
+
}
|
|
155
|
+
setActiveSessionId(sid);
|
|
156
|
+
return sid;
|
|
157
|
+
});
|
|
158
|
+
const steps: SeatPtyStep[] = [
|
|
159
|
+
{ label: 'after turn (auto compact)', text: '긴 대화 뒤 질문', until: (s) => s.includes('응답 완료') && s.includes(reply) },
|
|
160
|
+
{ label: 'after /compact', text: '/compact', until: /압축 \d+→\d+ 메시지 · Enter 전송/ },
|
|
161
|
+
];
|
|
162
|
+
const run = await mockSeatPtySequence({
|
|
163
|
+
...base,
|
|
164
|
+
ready: { label: 'before (12 seeded messages)', until: (s) => base.ready.until(s) && s.includes('xxxx') },
|
|
165
|
+
steps,
|
|
166
|
+
});
|
|
167
|
+
const logs = readLogs(spec, 'tui.next,llm.route-decision');
|
|
168
|
+
const events = logs.lines.map((l) => { try { return JSON.parse(l) as { category?: string; event?: string; data?: string }; } catch { return {}; } });
|
|
169
|
+
const compacts = events.filter((e) => e.category === 'tui.next' && e.event === 'compact').map((e) => parseData<{ auto: boolean; before: number; after: number; ms: number }>(e.data));
|
|
170
|
+
const routes = events.filter((e) => e.category === 'llm.route-decision' && e.event === 'local-chat-executed').map((e) => parseData<{ provider: string }>(e.data));
|
|
171
|
+
const stored = withStore(root, () => loadSession(id)?.messages ?? []);
|
|
172
|
+
const summary = stored.find((m) => m.role === 'system' && m.content.includes('<context_summary>'));
|
|
173
|
+
const auto = compacts.filter((c) => c.auto);
|
|
174
|
+
const manual = compacts.filter((c) => !c.auto);
|
|
175
|
+
const checks = {
|
|
176
|
+
autoCompactOnce: auto.length === 1 && Number(auto[0]!.before) > Number(auto[0]!.after),
|
|
177
|
+
autoBeforeIsSeeded12: auto[0]?.before === 12,
|
|
178
|
+
manualCompactOnce: manual.length === 1 && Number(manual[0]!.before) > Number(manual[0]!.after),
|
|
179
|
+
screenShowsAuto: /압축 12→\d+ 메시지/.test(run.frames.find((f) => f.label === 'after turn (auto compact)')?.screen ?? ''),
|
|
180
|
+
summaryFromFakeLlm: Boolean(summary?.content.includes(reply)),
|
|
181
|
+
onlyFakeProvider: routes.length > 0 && routes.every((r) => r.provider === 'test-fake-llm'),
|
|
182
|
+
exitedClean: run.exitCode === 0,
|
|
183
|
+
};
|
|
184
|
+
return { scenario: spec.scenario, universe: root, run, store: { sessionId: id, messages: stored.length, summaryHead: summary?.content.slice(0, 80) ?? null }, logs, checks };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
type ScenarioBase = Omit<Parameters<typeof mockSeatPtySequence>[0], 'steps'> & { env: Record<string, string> };
|
|
188
|
+
type Done = (n: number, also?: string) => (s: string) => boolean;
|
|
189
|
+
type LogEvent = { category?: string; event?: string; data?: string };
|
|
190
|
+
|
|
191
|
+
function parseEvents(logs: NextTuiScenarioResult['logs']): LogEvent[] {
|
|
192
|
+
return logs.lines.map((l) => { try { return JSON.parse(l) as LogEvent; } catch { return {}; } });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function onlyFake(events: LogEvent[]): boolean {
|
|
196
|
+
const routes = events.filter((e) => e.category === 'llm.route-decision' && e.event === 'local-chat-executed').map((e) => parseData<{ provider: string }>(e.data));
|
|
197
|
+
return routes.length > 0 && routes.every((r) => r.provider === 'test-fake-llm');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function activeUserTexts(root: string): { id: string; user: string[] } {
|
|
201
|
+
const id = withStore(root, () => getActiveSessionId() ?? '');
|
|
202
|
+
return { id, user: id ? userTexts(root, id) : [] };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** TUI-NEXT-QUEUE-AND-WAKE ① — two sentences ⊕ a draft typed while one slow turn runs; then Esc over a queued one. */
|
|
206
|
+
async function runNextQueueScenario(spec: NextTuiScenarioSpec, root: string, base: ScenarioBase, reply: string, done: Done): Promise<NextTuiScenarioResult> {
|
|
207
|
+
const slow = '천천히';
|
|
208
|
+
const steps: SeatPtyStep[] = [
|
|
209
|
+
// One slow turn; while it runs: two submissions and an unsent draft.
|
|
210
|
+
{ label: 'busy with two queued', chunks: [`첫 질문 ${slow}`, '\r', '둘째 질문', '\r', '셋째 질문', '\r', '쓰던 초안'], gapMs: 300, until: (s) => s.includes('대기 2') && s.includes('쓰던 초안') },
|
|
211
|
+
{ label: 'after settle', until: (s) => done(3)(s) && s.includes('셋째 질문') && s.includes('쓰던 초안') && !s.includes('대기'), timeoutMs: 60_000 },
|
|
212
|
+
// The draft becomes a slow turn; one more is queued behind it; Esc cancels the turn → the queued one returns to the prompt.
|
|
213
|
+
{ label: 'esc restores queue', chunks: [` ${slow}`, '\r', '다섯째', '\r', '\x1b'], gapMs: 400, until: (s) => s.includes('대기 1건을 입력칸으로') && s.includes('다섯째'), timeoutMs: 60_000 },
|
|
214
|
+
];
|
|
215
|
+
const run = await mockSeatPtySequence({
|
|
216
|
+
...base,
|
|
217
|
+
env: { ...base.env, [TEST_FAKE_LLM_DELAY_MS_ENV]: '3000', [TEST_FAKE_LLM_DELAY_WHEN_ENV]: slow },
|
|
218
|
+
steps,
|
|
219
|
+
});
|
|
220
|
+
const logs = readLogs(spec, 'tui.next,llm.route-decision');
|
|
221
|
+
const events = parseEvents(logs);
|
|
222
|
+
const store = activeUserTexts(root);
|
|
223
|
+
const frame = (label: string) => run.frames.find((f) => f.label === label)?.screen ?? '';
|
|
224
|
+
const promptLine = (screen: string) => screen.split('\n').at(-1) ?? '';
|
|
225
|
+
const checks = {
|
|
226
|
+
queueShownWhileBusy: frame('busy with two queued').includes('대기 2'),
|
|
227
|
+
userTurnsInOrder: JSON.stringify(store.user.slice(0, 3)) === JSON.stringify([`첫 질문 ${slow}`, '둘째 질문', '셋째 질문']),
|
|
228
|
+
threeReplies: count(frame('after settle'), reply) === 3,
|
|
229
|
+
draftKept: promptLine(frame('after settle')).includes('쓰던 초안'),
|
|
230
|
+
escRestoresQueue: promptLine(frame('esc restores queue')).includes('다섯째') && !store.user.includes('다섯째'),
|
|
231
|
+
onlyFakeProvider: onlyFake(events),
|
|
232
|
+
exitedClean: run.exitCode === 0,
|
|
233
|
+
};
|
|
234
|
+
return { scenario: spec.scenario, universe: root, run, store, logs, checks };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** TUI-PROACTIVE-CONTINUE — unfinished in-scope work continues; human-only work stops; repeated remainder stops. */
|
|
238
|
+
async function runNextProactiveContinueScenario(spec: NextTuiScenarioSpec, root: string, base: ScenarioBase): Promise<NextTuiScenarioResult> {
|
|
239
|
+
const cases = [
|
|
240
|
+
{ name: 'remaining', prompt: '요청한 파일을 천천히 끝까지 확인해줘', reply: '남은 작업: 자막 검증을 아직 확정하지 못했습니다.', turns: 3 },
|
|
241
|
+
{ name: 'human', prompt: '남은 것도 계속해줘', reply: '로그인이 필요합니다. 로그인해 주세요.', turns: 1 },
|
|
242
|
+
{ name: 'cap', prompt: '요청한 자막 검증을 반복해서 끝내줘', reply: '남은 작업: 자막 검증 {step}단계는 아직 완료되지 않았습니다.', turns: 4 },
|
|
243
|
+
{ name: 'buffer', prompt: '천천히 확인해줘', reply: '남은 작업: 파일 확인을 아직 마치지 못했습니다.', turns: 1 },
|
|
244
|
+
] as const;
|
|
245
|
+
const results: Array<{ name: string; run: SeatPtySequenceEvidence; store: ReturnType<typeof activeUserTexts>; logs: NextTuiScenarioResult['logs'] }> = [];
|
|
246
|
+
for (const c of cases) {
|
|
247
|
+
const caseRoot = join(root, c.name);
|
|
248
|
+
mkdirSync(caseRoot, { recursive: true });
|
|
249
|
+
writeFileSync(join(caseRoot, 'config.json'), JSON.stringify({ onboarding: { completed: true }, errorReports: { enabled: false } }));
|
|
250
|
+
const caseSpec = { ...spec, root: caseRoot };
|
|
251
|
+
const caseBase = { ...base, root: caseRoot, args: [spec.entry, '--config-dir', '{root}', '--test-state-dir', '{root}', '--tui', 'next'], env: { ...base.env, [TEST_FAKE_LLM_RESPONSE_ENV]: c.reply, ...(c.name === 'remaining' || c.name === 'buffer' ? { [TEST_FAKE_LLM_DELAY_MS_ENV]: '1500', [TEST_FAKE_LLM_DELAY_WHEN_ENV]: '천천히' } : {}) } };
|
|
252
|
+
const steps: SeatPtyStep[] = c.name === 'remaining'
|
|
253
|
+
? [
|
|
254
|
+
{ label: 'remaining: user input queues before turn boundary', chunks: [c.prompt, '\r', '검증 결과를 먼저 보여줘', '\r'], gapMs: 300, until: (s) => s.includes('대기 1'), timeoutMs: 30_000 },
|
|
255
|
+
{ label: 'remaining: queued user turn is next', until: (s) => count(s, c.reply) >= 2 && s.includes('응답 완료'), timeoutMs: 30_000 },
|
|
256
|
+
{ label: 'remaining: auto continuation settled', until: (s) => count(s, c.reply) >= c.turns && s.includes('응답 완료'), timeoutMs: 30_000 },
|
|
257
|
+
]
|
|
258
|
+
: c.name === 'cap'
|
|
259
|
+
? [{ label: 'cap: three automatic turns stop', text: c.prompt, until: (s) => s.includes('골 이어가기 상한(3회 연속)') && s.includes('응답 완료'), timeoutMs: 60_000 }]
|
|
260
|
+
: c.name === 'buffer'
|
|
261
|
+
? [
|
|
262
|
+
{ label: 'buffer: draft typed while the turn is busy', chunks: [c.prompt, '\r', '미전송 초안'], gapMs: 300, until: (s) => s.includes('응답 중') && s.includes('미전송 초안'), timeoutMs: 30_000 },
|
|
263
|
+
{ label: 'buffer: response ends with draft preserved', until: (s) => s.includes(c.reply) && s.includes('미전송 초안') && s.includes('응답 완료'), timeoutMs: 30_000 },
|
|
264
|
+
]
|
|
265
|
+
: [{ label: `${c.name}: human turn`, text: c.prompt, until: (s) => count(s, c.reply) >= 1 && s.includes('응답 완료'), timeoutMs: 30_000 }];
|
|
266
|
+
const run = await mockSeatPtySequence({ ...caseBase, steps });
|
|
267
|
+
results.push({ name: c.name, run, store: activeUserTexts(caseRoot), logs: readLogs(caseSpec, 'tui.next,tui.proactive,tui.goal-continue,tui.autoturn,llm.route-decision') });
|
|
268
|
+
}
|
|
269
|
+
const events = results.flatMap((r) => parseEvents(r.logs));
|
|
270
|
+
const proactive = events.filter((e) => e.category === 'tui.proactive').map((e) => ({ event: e.event, ...parseData<Record<string, unknown>>(e.data) }));
|
|
271
|
+
const humanUsers = results.find((r) => r.name === 'human')?.store.user ?? [];
|
|
272
|
+
const remainingUsers = results.find((r) => r.name === 'remaining')?.store.user ?? [];
|
|
273
|
+
const queuedFrame = results.find((r) => r.name === 'remaining')?.run.frames.find((f) => f.label.includes('queued user turn is next'))?.screen ?? '';
|
|
274
|
+
const autoFrame = results.find((r) => r.name === 'remaining')?.run.frames.find((f) => f.label.includes('auto continuation settled'))?.screen ?? '';
|
|
275
|
+
const bufferCase = results.find((r) => r.name === 'buffer');
|
|
276
|
+
const bufferEvents = bufferCase ? parseEvents(bufferCase.logs) : [];
|
|
277
|
+
const nextQueueDispatched = events.findIndex((e) => e.category === 'tui.next' && e.event === 'queue-dispatched');
|
|
278
|
+
const autoContinuedAfterQueue = events.findIndex((e, i) => i > nextQueueDispatched && e.category === 'tui.proactive' && e.event === 'continue');
|
|
279
|
+
const combinedLines = results.flatMap((r) => r.logs.lines);
|
|
280
|
+
const logs: NextTuiScenarioResult['logs'] = {
|
|
281
|
+
command: results.map((r) => r.logs.command).join(' ⊕ '), rc: results.every((r) => r.logs.rc === 0) ? 0 : 1,
|
|
282
|
+
lines: combinedLines, eventCount: combinedLines.filter((l) => l.trimStart().startsWith('{') && !l.includes('"_meta"')).length, limit: 400,
|
|
283
|
+
};
|
|
284
|
+
const run = { ...results[0]!.run, frames: results.flatMap((r) => r.run.frames.map((f) => ({ ...f, label: `${r.name}: ${f.label}` }))), exitCode: results.every((r) => r.run.exitCode === 0) ? 0 : 1 };
|
|
285
|
+
return {
|
|
286
|
+
scenario: spec.scenario, universe: root, run,
|
|
287
|
+
store: { remainingUsers, humanUsers, proactiveEvents: proactive }, logs,
|
|
288
|
+
checks: {
|
|
289
|
+
queuedUserInputWonBoundary: proactive.some((e) => e.event === 'yield-user-input') && nextQueueDispatched >= 0 && autoContinuedAfterQueue > nextQueueDispatched && queuedFrame.includes('나: 검증 결과를 먼저 보여줘'),
|
|
290
|
+
remainingWorkContinued: proactive.some((e) => e.event === 'continue' && e.reason === 'work-remaining') && autoFrame.includes('↻ 자동 재개(goal-continue)'),
|
|
291
|
+
repeatedRemainderStopped: proactive.some((e) => e.event === 'stop-no-progress' && e.reason === 'same-remaining'),
|
|
292
|
+
humanWorkStopped: proactive.some((e) => e.event === 'stop-human' && e.reason === 'human-action') && results.find((r) => r.name === 'human')?.run.frames.at(-1)?.screen.includes('로그인이 필요합니다') === true,
|
|
293
|
+
autoTurnCapStopsInPractice: DEFAULT_GOAL_CONTINUE_CAP === 3 && proactive.some((e) => e.event === 'stop-cap' && e.cap === 3 && e.autoTurn === 3)
|
|
294
|
+
&& results.find((r) => r.name === 'cap')?.run.frames.at(-1)?.screen.includes('골 이어가기 상한(3회 연속)') === true,
|
|
295
|
+
typedBufferWinsAndStaysVisible: bufferEvents.some((e) => e.category === 'tui.proactive' && e.event === 'yield-user-input' && parseData<{ bufferedChars: number }>(e.data).bufferedChars > 0)
|
|
296
|
+
&& bufferCase?.run.frames.at(-1)?.screen.includes('미전송 초안') === true
|
|
297
|
+
&& !bufferEvents.some((e) => e.category === 'tui.proactive' && e.event === 'continue'),
|
|
298
|
+
onlyFakeProvider: events.filter((e) => e.category === 'llm.route-decision' && e.event === 'local-chat-executed').every((e) => parseData<{ provider: string }>(e.data).provider === 'test-fake-llm'),
|
|
299
|
+
isolatedLogsRead: logs.rc === 0 && logs.eventCount > 0,
|
|
300
|
+
exitedClean: results.every((r) => r.run.exitCode === 0),
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** TUI-NEXT-QUEUE-AND-WAKE ② — a turn calls ScheduleWakeup(60s); a slow human turn covers the due time
|
|
306
|
+
* (NotIdle → deferred a minute), then the wakeup opens one internal turn on its own. */
|
|
307
|
+
async function runNextWakeupScenario(spec: NextTuiScenarioSpec, root: string, base: ScenarioBase, reply: string, done: Done): Promise<NextTuiScenarioResult> {
|
|
308
|
+
let scheduledAt = 0;
|
|
309
|
+
const banner = '↻ 자동 재개(scheduled): 점검';
|
|
310
|
+
const steps: SeatPtyStep[] = [
|
|
311
|
+
{
|
|
312
|
+
label: 'scheduled',
|
|
313
|
+
text: '60초 뒤 예약해줘',
|
|
314
|
+
until: (s) => {
|
|
315
|
+
const ok = done(1)(s) && s.includes('⏰ 예약 깨움');
|
|
316
|
+
if (ok && scheduledAt === 0) scheduledAt = Date.now();
|
|
317
|
+
return ok;
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
{ label: 'before due', until: () => scheduledAt > 0 && Date.now() >= scheduledAt + 45_000, timeoutMs: 60_000 },
|
|
321
|
+
{ label: 'busy over the due time', text: '긴 작업 하나', until: (s) => s.includes('응답 중') },
|
|
322
|
+
{ label: 'long turn done', until: done(2, '긴 작업 하나'), timeoutMs: 60_000 },
|
|
323
|
+
{ label: 'woke', until: (s) => s.includes(banner) && done(3)(s), timeoutMs: 150_000 },
|
|
324
|
+
];
|
|
325
|
+
const run = await mockSeatPtySequence({
|
|
326
|
+
...base,
|
|
327
|
+
env: {
|
|
328
|
+
...base.env,
|
|
329
|
+
[TEST_FAKE_LLM_TOOL_CALL_ENV]: JSON.stringify({ when: '예약해줘', name: 'ScheduleWakeup', args: { delaySeconds: 60, reason: '점검' } }),
|
|
330
|
+
[TEST_FAKE_LLM_DELAY_MS_ENV]: '25000',
|
|
331
|
+
[TEST_FAKE_LLM_DELAY_WHEN_ENV]: '긴 작업',
|
|
332
|
+
},
|
|
333
|
+
steps,
|
|
334
|
+
});
|
|
335
|
+
const logs = readLogs(spec, 'tui.wakeup,tui.autoturn,llm.route-decision');
|
|
336
|
+
const events = parseEvents(logs);
|
|
337
|
+
const wake = (event: string) => events.filter((e) => e.category === 'tui.wakeup' && e.event === event).map((e) => parseData<{ id: string; reason: string }>(e.data));
|
|
338
|
+
const opened = events.filter((e) => e.category === 'tui.autoturn' && e.event === 'opened').map((e) => parseData<{ origin: string }>(e.data));
|
|
339
|
+
const store = activeUserTexts(root);
|
|
340
|
+
const checks = {
|
|
341
|
+
scheduledOnce: wake('scheduled').length === 1,
|
|
342
|
+
noticeOnScreen: (run.frames.find((f) => f.label === 'scheduled')?.screen ?? '').includes('⏰ 예약 깨움'),
|
|
343
|
+
deferredWhileBusy: wake('rejected').some((r) => r.id === 'w1' && r.reason === 'NotIdle'),
|
|
344
|
+
firedOnce: wake('fired').length === 1,
|
|
345
|
+
internalTurnOpened: opened.length === 1 && opened[0]?.origin === 'scheduled',
|
|
346
|
+
bannerOnScreen: (run.frames.at(-1)?.screen ?? '').includes(banner),
|
|
347
|
+
wakeTurnStored: store.user.some((t) => t.startsWith('[예약 깨움 w1] 점검')),
|
|
348
|
+
onlyFakeProvider: onlyFake(events),
|
|
349
|
+
exitedClean: run.exitCode === 0,
|
|
350
|
+
};
|
|
351
|
+
return { scenario: spec.scenario, universe: root, run, store: { ...store, reply }, logs, checks };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** TUI-NEXT-FOLD — one real turn: the fake model calls the registered Bash tool, which prints 30 lines
|
|
355
|
+
* (`seq`), so the stored tool result comes from the real tool path (not a seed). Then PageUp (log focus)
|
|
356
|
+
* → Enter (expand) → Enter (fold again). */
|
|
357
|
+
export const NEXT_TOOL_FOLD_COMMAND = 'seq -f fold-line-%02g 0 29';
|
|
358
|
+
async function runNextToolFoldScenario(spec: NextTuiScenarioSpec, root: string, base: ScenarioBase, reply: string, done: Done): Promise<NextTuiScenarioResult> {
|
|
359
|
+
const ask = '도구 출력 접기 시험';
|
|
360
|
+
const summaryRe = /▸ 도구 Bash · (\d+)줄 \(접힘\)/;
|
|
361
|
+
// Whole stored session, read at the moment each screen is reached: before the toggles and after both.
|
|
362
|
+
const snapshot = (): string => withStore(root, () => {
|
|
363
|
+
const sid = getActiveSessionId() ?? '';
|
|
364
|
+
return sid ? JSON.stringify({ sid, messages: loadSession(sid)?.messages ?? null }) : '';
|
|
365
|
+
});
|
|
366
|
+
let beforeToggle = '';
|
|
367
|
+
let afterToggles = '';
|
|
368
|
+
const steps: SeatPtyStep[] = [
|
|
369
|
+
{
|
|
370
|
+
label: 'after tool turn (folded)', text: ask, timeoutMs: 60_000,
|
|
371
|
+
until: (s) => { const ok = done(1)(s) && summaryRe.test(s); if (ok && !beforeToggle) beforeToggle = snapshot(); return ok; },
|
|
372
|
+
},
|
|
373
|
+
{ label: 'log focus (PageUp)', keys: '\x1b[5~', until: '로그 탐색' },
|
|
374
|
+
{ label: 'expanded (Enter)', keys: '\r', until: (s) => s.includes('fold-line-29') && !summaryRe.test(s), timeoutMs: 15_000 },
|
|
375
|
+
{
|
|
376
|
+
label: 'folded again (Enter)', keys: '\r', timeoutMs: 15_000,
|
|
377
|
+
until: (s) => { const ok = summaryRe.test(s) && !s.includes('fold-line-29'); if (ok && !afterToggles) afterToggles = snapshot(); return ok; },
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
const run = await mockSeatPtySequence({
|
|
381
|
+
...base,
|
|
382
|
+
env: {
|
|
383
|
+
...base.env,
|
|
384
|
+
[TEST_FAKE_LLM_TOOL_CALL_ENV]: JSON.stringify({ when: ask, name: 'Bash', args: { command: NEXT_TOOL_FOLD_COMMAND } }),
|
|
385
|
+
},
|
|
386
|
+
steps,
|
|
387
|
+
});
|
|
388
|
+
const logs = readLogs(spec, 'tui.next,llm.route-decision');
|
|
389
|
+
const events = parseEvents(logs);
|
|
390
|
+
const id = withStore(root, () => getActiveSessionId() ?? '');
|
|
391
|
+
const stored = id ? withStore(root, () => loadSession(id)?.messages ?? []) : [];
|
|
392
|
+
const tool = stored.find((m) => m.role === 'tool');
|
|
393
|
+
const result = typeof tool?.toolResult === 'string' ? tool.toolResult : '';
|
|
394
|
+
// The Bash tool stores a JSON envelope ({stdout, output, exitCode, …}); its stdout must be the 30 lines `seq` printed.
|
|
395
|
+
const stdoutLines = (() => {
|
|
396
|
+
try { const parsed = JSON.parse(result) as { stdout?: unknown }; return typeof parsed.stdout === 'string' ? parsed.stdout.trimEnd().split('\n') : []; } catch { return result.trimEnd().split('\n'); }
|
|
397
|
+
})();
|
|
398
|
+
const screen = (label: string) => run.frames.find((f) => f.label === label)?.screen ?? '';
|
|
399
|
+
const folded0 = screen('after tool turn (folded)');
|
|
400
|
+
const expanded = screen('expanded (Enter)');
|
|
401
|
+
const folded1 = screen('folded again (Enter)');
|
|
402
|
+
const checks = {
|
|
403
|
+
// The 30 lines come from the Bash tool actually running in the turn loop.
|
|
404
|
+
realToolResultStored: tool?.toolName === 'Bash' && stdoutLines.length === 30
|
|
405
|
+
&& stdoutLines.every((l, i) => l === `fold-line-${String(i).padStart(2, '0')}`),
|
|
406
|
+
collapsedAfterTurn: summaryRe.test(folded0) && !folded0.includes('fold-line-12'),
|
|
407
|
+
expandedOnEnter: expanded.includes('fold-line-29') && !summaryRe.test(expanded),
|
|
408
|
+
foldedAgainOnEnter: summaryRe.test(folded1) && !folded1.includes('fold-line-29'),
|
|
409
|
+
// Fold state is screen-only: the whole stored session read right after the first folded screen
|
|
410
|
+
// equals the one read right after the second Enter (and it holds the real tool result).
|
|
411
|
+
sessionUnchanged: beforeToggle !== '' && beforeToggle === afterToggles && beforeToggle.includes('fold-line-29'),
|
|
412
|
+
onlyFakeProvider: onlyFake(events),
|
|
413
|
+
exitedClean: run.exitCode === 0,
|
|
414
|
+
};
|
|
415
|
+
return { scenario: spec.scenario, universe: root, run, store: { sessionId: id, messages: stored.length, toolName: tool?.toolName ?? null, stdoutLines: stdoutLines.length, foldedAs: summaryRe.exec(folded0)?.[0] ?? null, sessionBytesBeforeToggle: beforeToggle.length, sessionBytesAfterToggles: afterToggles.length }, logs, checks };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** Lines of the file the Read scenario reads (the 10-22 incident shape: « 1\tfold-line-1 …», 20 lines). */
|
|
419
|
+
export const NEXT_TOOL_FOLD_READ_LINES = 20;
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* TUI-NEXT-TOOL-RESULT-RENDER — a real Read tool call over a 20-line file. The stored result is the
|
|
423
|
+
* JSON envelope ({"output":" 1\tfold-line-1\n…","linesRead":20,…}); the log must show it as the
|
|
424
|
+
* file's lines (folded to «▸ 도구 Read · 20줄 (접힘)», then expanded with a «20줄 · NB» status) —
|
|
425
|
+
* never the raw envelope with escaped \n.
|
|
426
|
+
*/
|
|
427
|
+
async function runNextToolFoldReadScenario(spec: NextTuiScenarioSpec, root: string, base: ScenarioBase, done: Done): Promise<NextTuiScenarioResult> {
|
|
428
|
+
const ask = '읽기 출력 접기 시험';
|
|
429
|
+
const summaryRe = /▸ 도구 Read · 20줄 \(접힘\)/;
|
|
430
|
+
const headerRe = /도구: Read · 20줄 · \d+B/;
|
|
431
|
+
const file = join(root, 'fold-read.txt');
|
|
432
|
+
writeFileSync(file, Array.from({ length: NEXT_TOOL_FOLD_READ_LINES }, (_, i) => `fold-line-${i + 1}`).join('\n') + '\n');
|
|
433
|
+
const steps: SeatPtyStep[] = [
|
|
434
|
+
{ label: 'after Read turn (folded)', text: ask, timeoutMs: 60_000, until: (s) => done(1)(s) && summaryRe.test(s) },
|
|
435
|
+
{ label: 'log focus (PageUp)', keys: '\x1b[5~', until: '로그 탐색' },
|
|
436
|
+
{ label: 'expanded (Enter)', keys: '\r', until: (s) => s.includes('fold-line-20') && !summaryRe.test(s), timeoutMs: 15_000 },
|
|
437
|
+
// 24 rows cut the block's top — scroll up to its header line.
|
|
438
|
+
{ label: 'expanded top (↑)', keys: '\x1b[A'.repeat(6), until: (s) => headerRe.test(s), timeoutMs: 15_000 },
|
|
439
|
+
];
|
|
440
|
+
const run = await mockSeatPtySequence({
|
|
441
|
+
...base,
|
|
442
|
+
env: { ...base.env, [TEST_FAKE_LLM_TOOL_CALL_ENV]: JSON.stringify({ when: ask, name: 'Read', args: { file_path: file } }) },
|
|
443
|
+
steps,
|
|
444
|
+
});
|
|
445
|
+
const logs = readLogs(spec, 'tui.next,llm.route-decision');
|
|
446
|
+
const events = parseEvents(logs);
|
|
447
|
+
const id = withStore(root, () => getActiveSessionId() ?? '');
|
|
448
|
+
const stored = id ? withStore(root, () => loadSession(id)?.messages ?? []) : [];
|
|
449
|
+
const tool = stored.find((m) => m.role === 'tool');
|
|
450
|
+
const result = typeof tool?.toolResult === 'string' ? tool.toolResult : '';
|
|
451
|
+
const envelope = (() => { try { return JSON.parse(result) as { output?: unknown; linesRead?: unknown }; } catch { return null; } })();
|
|
452
|
+
const screen = (label: string) => run.frames.find((f) => f.label === label)?.screen ?? '';
|
|
453
|
+
const folded = screen('after Read turn (folded)');
|
|
454
|
+
const expanded = screen('expanded (Enter)');
|
|
455
|
+
const top = screen('expanded top (↑)');
|
|
456
|
+
// Every file line that is on screen sits on its own row as «<n> fold-line-<n>» (both expanded frames together cover all 20).
|
|
457
|
+
const fileRows = new Set([...`${expanded}\n${top}`.matchAll(/^ +(\d+) fold-line-(\d+) *$/gm)].filter((m) => m[1] === m[2]).map((m) => Number(m[1])));
|
|
458
|
+
const checks = {
|
|
459
|
+
// The stored result is the real Read envelope (one JSON line) — the shape the incident showed raw.
|
|
460
|
+
realReadEnvelopeStored: tool?.toolName === 'Read' && envelope?.linesRead === NEXT_TOOL_FOLD_READ_LINES
|
|
461
|
+
&& typeof envelope.output === 'string' && envelope.output.includes('\tfold-line-1\n'),
|
|
462
|
+
collapsedAfterTurn: summaryRe.test(folded) && !folded.includes('fold-line-12'),
|
|
463
|
+
// Expanded = the file's lines on their own rows with the status on the header, no raw envelope.
|
|
464
|
+
expandedAsFileLines: headerRe.test(top) && fileRows.size === NEXT_TOOL_FOLD_READ_LINES,
|
|
465
|
+
noRawEnvelope: ![folded, expanded, top].some((s) => s.includes('{"output"') || s.includes('\\n') || s.includes('"linesRead"')),
|
|
466
|
+
onlyFakeProvider: onlyFake(events),
|
|
467
|
+
exitedClean: run.exitCode === 0,
|
|
468
|
+
};
|
|
469
|
+
return { scenario: spec.scenario, universe: root, run, store: { sessionId: id, toolName: tool?.toolName ?? null, resultChars: result.length, foldedAs: summaryRe.exec(folded)?.[0] ?? null }, logs, checks };
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** Short and 50-line pastes for TUI-PASTE-ECHO (shared with the classic scenario). */
|
|
473
|
+
export const PASTE_SHORT = 'paste-short-hello';
|
|
474
|
+
export const PASTE_LONG = Array.from({ length: 50 }, (_, i) => `paste-line-${String(i).padStart(2, '0')}`).join('\n');
|
|
475
|
+
export const PASTE_LONG_TOKEN = pasteFoldToken(1, PASTE_LONG);
|
|
476
|
+
export const bracketedPaste = (text: string): string => `\x1b[200~${text}\x1b[201~`;
|
|
477
|
+
/** Clipboard text the Ctrl+V step reads (through the PATH shim below) — 6 lines, so it folds as paste #2. */
|
|
478
|
+
export const CLIP_TEXT = Array.from({ length: 6 }, (_, i) => `clip-line-${i}`).join('\n');
|
|
479
|
+
export const CLIP_TOKEN = pasteFoldToken(2, CLIP_TEXT);
|
|
480
|
+
|
|
481
|
+
/** PATH shims so Ctrl+V reads a known clipboard instead of the person's: `pbpaste` prints CLIP_TEXT and
|
|
482
|
+
* `osascript` answers «no image». The TUI's own clipboard module spawns them by name from PATH. */
|
|
483
|
+
export function clipboardShimPath(root: string): string {
|
|
484
|
+
const bin = join(root, 'clip-bin');
|
|
485
|
+
mkdirSync(bin, { recursive: true });
|
|
486
|
+
writeFileSync(join(bin, 'pbpaste'), `#!/bin/sh\nprintf '%s' '${CLIP_TEXT.replace(/\n/g, '\\n')}' | sed 's/\\\\n/\\n/g'\n`);
|
|
487
|
+
writeFileSync(join(bin, 'osascript'), '#!/bin/sh\necho no\n');
|
|
488
|
+
chmodSync(join(bin, 'pbpaste'), 0o755);
|
|
489
|
+
chmodSync(join(bin, 'osascript'), 0o755);
|
|
490
|
+
return `${bin}:${process.env.PATH ?? ''}`;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/** TUI-PASTE-ECHO — a short bracketed paste shows as typed; a 50-line paste shows one placeholder and the
|
|
494
|
+
* submitted turn carries the whole original text. */
|
|
495
|
+
async function runNextPasteScenario(spec: NextTuiScenarioSpec, root: string, base: ScenarioBase, reply: string): Promise<NextTuiScenarioResult> {
|
|
496
|
+
const steps: SeatPtyStep[] = [
|
|
497
|
+
{ label: 'short pasted', keys: bracketedPaste(PASTE_SHORT), until: (s) => s.includes(`❯ ${PASTE_SHORT}`) },
|
|
498
|
+
{ label: 'cleared', keys: '\x15', until: (s) => !s.includes(PASTE_SHORT) },
|
|
499
|
+
{ label: 'long pasted', keys: bracketedPaste(PASTE_LONG), until: (s) => s.includes(PASTE_LONG_TOKEN) },
|
|
500
|
+
{ label: 'submitted', keys: '\r', until: (s) => s.includes(reply) && s.includes('응답 완료'), timeoutMs: 30_000 },
|
|
501
|
+
{ label: 'ctrl+v', keys: '\x16', until: (s) => s.includes(CLIP_TOKEN) },
|
|
502
|
+
{ label: 'clip submitted', keys: '\r', until: (s) => s.split(reply).length - 1 === 2 && s.includes('응답 완료'), timeoutMs: 30_000 },
|
|
503
|
+
];
|
|
504
|
+
const run = await mockSeatPtySequence({ ...base, env: { ...base.env, PATH: clipboardShimPath(root) }, steps });
|
|
505
|
+
const logs = readLogs(spec, 'tui.paste,llm.route-decision');
|
|
506
|
+
const events = parseEvents(logs);
|
|
507
|
+
const store = activeUserTexts(root);
|
|
508
|
+
const screen = (label: string) => run.frames.find((f) => f.label === label)?.screen ?? '';
|
|
509
|
+
const paste = (event: string) => events.filter((e) => e.category === 'tui.paste' && e.event === event).map((e) => parseData<{ lines: number; chars: number; count: number }>(e.data));
|
|
510
|
+
const checks = {
|
|
511
|
+
shortEchoed: screen('short pasted').includes(`❯ ${PASTE_SHORT}`),
|
|
512
|
+
longPlaceholder: screen('long pasted').includes(PASTE_LONG_TOKEN) && !screen('long pasted').includes('paste-line-49'),
|
|
513
|
+
submittedWhole: store.user.length === 2 && store.user[0] === PASTE_LONG,
|
|
514
|
+
ctrlVPlaceholder: screen('ctrl+v').includes(CLIP_TOKEN),
|
|
515
|
+
ctrlVSubmittedWhole: store.user[1] === CLIP_TEXT,
|
|
516
|
+
clipboardReadObserved: paste('clipboard-read').length === 1,
|
|
517
|
+
foldedObserved: paste('folded').length === 2 && paste('folded')[0]?.lines === 50 && paste('folded')[0]?.chars === 699,
|
|
518
|
+
expandedObserved: paste('expanded').length === 2,
|
|
519
|
+
onlyFakeProvider: onlyFake(events),
|
|
520
|
+
exitedClean: run.exitCode === 0,
|
|
521
|
+
};
|
|
522
|
+
return { scenario: spec.scenario, universe: root, run, store: { ...store, reply }, logs, checks };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** Reply the fake model gives on every call in the goal-continue scenarios — a promise with no result. */
|
|
526
|
+
export const GOAL_CONTINUE_PLEDGE_REPLY = '[fake-llm] 이어서 렌더를 진행하겠습니다.';
|
|
527
|
+
|
|
528
|
+
/** TURN-END-BEFORE-GOAL — one human turn whose reply only promises («…진행하겠습니다.») opens one
|
|
529
|
+
* goal-continue internal turn on its own; the identical second reply does not open a third (no loop). */
|
|
530
|
+
async function runNextGoalContinueScenario(spec: NextTuiScenarioSpec, root: string, base: ScenarioBase): Promise<NextTuiScenarioResult> {
|
|
531
|
+
const reply = GOAL_CONTINUE_PLEDGE_REPLY;
|
|
532
|
+
const banner = '↻ 자동 재개(goal-continue)';
|
|
533
|
+
const replies = (s: string) => s.split(reply).length - 1;
|
|
534
|
+
let settledAt = 0;
|
|
535
|
+
const steps: SeatPtyStep[] = [
|
|
536
|
+
// The goal-continue turn may open before the first poll sees the settled reply — «at least one» answer.
|
|
537
|
+
{ label: 'human turn', text: '영상 만들어줘', until: (s) => replies(s) >= 1 },
|
|
538
|
+
// The door's 10 s human-quiet window covers the Enter just typed; the controller retries once it passes.
|
|
539
|
+
{
|
|
540
|
+
label: 'continued',
|
|
541
|
+
until: (s) => {
|
|
542
|
+
const ok = s.includes(banner) && replies(s) === 2 && s.includes('응답 완료');
|
|
543
|
+
if (ok && settledAt === 0) settledAt = Date.now();
|
|
544
|
+
return ok;
|
|
545
|
+
},
|
|
546
|
+
timeoutMs: 60_000,
|
|
547
|
+
},
|
|
548
|
+
// Long enough for another retry window: an identical reply must not open a third turn.
|
|
549
|
+
{ label: 'no loop', until: (s) => settledAt > 0 && Date.now() >= settledAt + 25_000 && replies(s) === 2, timeoutMs: 40_000 },
|
|
550
|
+
];
|
|
551
|
+
const run = await mockSeatPtySequence({ ...base, env: { ...base.env, [TEST_FAKE_LLM_RESPONSE_ENV]: reply }, steps });
|
|
552
|
+
const logs = readLogs(spec, 'tui.goal-continue,tui.autoturn,llm.route-decision');
|
|
553
|
+
const events = parseEvents(logs);
|
|
554
|
+
const decided = events.filter((e) => e.category === 'tui.goal-continue' && e.event === 'decided').map((e) => parseData<{ opened?: boolean; rejected?: boolean; reason: string }>(e.data));
|
|
555
|
+
const opened = events.filter((e) => e.category === 'tui.autoturn' && e.event === 'opened').map((e) => parseData<{ origin: string }>(e.data));
|
|
556
|
+
const store = activeUserTexts(root);
|
|
557
|
+
const last = run.frames.at(-1)?.screen ?? '';
|
|
558
|
+
const checks = {
|
|
559
|
+
goalContinueOpenedOnce: decided.filter((d) => d.opened === true).length === 1,
|
|
560
|
+
internalTurnOrigin: opened.length === 1 && opened[0]?.origin === 'goal-continue',
|
|
561
|
+
bannerOnScreen: (run.frames.find((f) => f.label === 'continued')?.screen ?? '').includes(banner),
|
|
562
|
+
continueTurnStored: store.user.length === 2 && store.user[0] === '영상 만들어줘' && (store.user[1] ?? '').startsWith('[골 이어가기]'),
|
|
563
|
+
sameReplyStopped: decided.some((d) => d.rejected === true && d.reason === 'same-reply'),
|
|
564
|
+
noLoop: replies(last) === 2,
|
|
565
|
+
onlyFakeProvider: onlyFake(events),
|
|
566
|
+
exitedClean: run.exitCode === 0,
|
|
567
|
+
};
|
|
568
|
+
return { scenario: spec.scenario, universe: root, run, store: { ...store, reply }, logs, checks };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
/** TUI-GOAL-INTERIM-QUIET — every step ends with «아직 못 했다» (a different reply per turn via `{step}`, so the
|
|
573
|
+
* same-reply stop does not end it early). The fake reply's honesty sentence, counted on screen. */
|
|
574
|
+
export const GOAL_INTERIM_NOT_DONE_REPLY = '[fake-llm] {step}단계 렌더를 진행했습니다.\n남은 작업 {step}단계: 완료 영상 경로는 아직 보고할 수 없습니다 — 아직 못 했습니다.';
|
|
575
|
+
const GOAL_INTERIM_STEP_MARK = '단계 렌더를 진행했습니다';
|
|
576
|
+
const GOAL_INTERIM_NOT_DONE_MARK = '아직 못 했습니다';
|
|
577
|
+
/** An internal turn drawn as if a person typed it (classic «❯ …» · next «나: …»). */
|
|
578
|
+
/** The first (human) turn also calls one tool, so a folded reply must leave that turn's tool lines on screen.
|
|
579
|
+
* ScheduleWakeup an hour out: a real registered tool with no approval prompt that never fires inside the run. */
|
|
580
|
+
export const GOAL_INTERIM_TOOL_CALL = JSON.stringify({ when: '영상 만들어줘', name: 'ScheduleWakeup', args: { delaySeconds: 3600, reason: '렌더 점검' } });
|
|
581
|
+
export const INTERNAL_TURN_USER_HEADER = /(?:나:|❯)\s*\[(?:골 이어가기|자동 재개)\]/u;
|
|
582
|
+
|
|
583
|
+
export interface GoalInterimScreenCounts { steps: number; notDone: number; folded: number; userHeader: boolean }
|
|
584
|
+
export function goalInterimScreenCounts(screen: string): GoalInterimScreenCounts {
|
|
585
|
+
const n = (t: string) => screen.split(t).length - 1;
|
|
586
|
+
return { steps: n(GOAL_INTERIM_STEP_MARK), notDone: n(GOAL_INTERIM_NOT_DONE_MARK), folded: n('(접힘)'), userHeader: INTERNAL_TURN_USER_HEADER.test(screen) };
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** One human turn → goal-continue runs to its cap on «not done yet» replies. The conversation must show
|
|
590
|
+
* each step (folded to one line) but the «not done» paragraph at most once, and no user-looking internal input. */
|
|
591
|
+
async function runNextGoalInterimScenario(spec: NextTuiScenarioSpec, root: string, base: ScenarioBase): Promise<NextTuiScenarioResult> {
|
|
592
|
+
const capLine = '골 이어가기 상한';
|
|
593
|
+
let settledAt = 0;
|
|
594
|
+
const steps: SeatPtyStep[] = [
|
|
595
|
+
{ label: 'human turn', text: '영상 만들어줘', until: (s) => goalInterimScreenCounts(s).steps >= 1 },
|
|
596
|
+
{
|
|
597
|
+
label: 'settled',
|
|
598
|
+
// goal-continue stops at its consecutive cap (a «⏸ … 상한» line) after the last internal turn settles.
|
|
599
|
+
until: (s) => {
|
|
600
|
+
const ok = s.includes(capLine) && s.includes('응답 완료');
|
|
601
|
+
if (ok && settledAt === 0) settledAt = Date.now();
|
|
602
|
+
return ok;
|
|
603
|
+
},
|
|
604
|
+
timeoutMs: 90_000,
|
|
605
|
+
},
|
|
606
|
+
{ label: 'quiet', until: () => settledAt > 0 && Date.now() >= settledAt + 12_000, timeoutMs: 30_000 },
|
|
607
|
+
];
|
|
608
|
+
const run = await mockSeatPtySequence({ ...base, rows: 40, env: { ...base.env, [TEST_FAKE_LLM_RESPONSE_ENV]: GOAL_INTERIM_NOT_DONE_REPLY, [TEST_FAKE_LLM_TOOL_CALL_ENV]: GOAL_INTERIM_TOOL_CALL }, steps });
|
|
609
|
+
const logs = readLogs(spec, 'tui.goal-continue,tui.goal-interim,tui.autoturn,llm.route-decision');
|
|
610
|
+
const events = parseEvents(logs);
|
|
611
|
+
const opened = events.filter((e) => e.category === 'tui.goal-continue' && e.event === 'decided')
|
|
612
|
+
.map((e) => parseData<{ opened?: boolean }>(e.data)).filter((d) => d.opened === true).length;
|
|
613
|
+
const foldedLogged = events.filter((e) => e.category === 'tui.goal-interim' && e.event === 'folded')
|
|
614
|
+
.map((e) => parseData<{ folded: number }>(e.data).folded ?? 0);
|
|
615
|
+
const store = activeUserTexts(root);
|
|
616
|
+
const last = goalInterimScreenCounts(run.frames.at(-1)?.screen ?? '');
|
|
617
|
+
const checks = {
|
|
618
|
+
continuedSeveral: opened >= 2,
|
|
619
|
+
everyStepOnScreen: last.steps === opened + 1,
|
|
620
|
+
notDoneAtMostOnce: last.notDone <= 1,
|
|
621
|
+
finalReportShown: last.notDone === 1,
|
|
622
|
+
foldedEachContinued: last.folded === opened && Math.max(0, ...foldedLogged) === opened,
|
|
623
|
+
noUserHeaderForInternal: run.frames.every((f) => !INTERNAL_TURN_USER_HEADER.test(f.screen)),
|
|
624
|
+
// The first turn's tool line survives the fold of that turn's reply (only the reply block folds).
|
|
625
|
+
toolLineKept: (run.frames.at(-1)?.screen ?? '').includes('ScheduleWakeup'),
|
|
626
|
+
internalTurnsStored: store.user.filter((u) => u.startsWith('[골 이어가기]')).length === opened,
|
|
627
|
+
onlyFakeProvider: onlyFake(events),
|
|
628
|
+
exitedClean: run.exitCode === 0,
|
|
629
|
+
};
|
|
630
|
+
return { scenario: spec.scenario, universe: root, run, store: { ...store, opened, last }, logs, checks };
|
|
631
|
+
}
|