elanous 0.2.24 → 0.2.25
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/1027.a0f04663d24c2779.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +1 -0
- package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +36 -0
- package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +148 -0
- package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1454-37fd16f479f5554a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8532.a86e99acf9b761cb.js → 1516.83dd2c1b86b4e507.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{89849432.9eaa2f4c9c010965.js → 16e57f67.3ea19e893461e611.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1505.5826671109111e75.js → 1743.563d3168e95825ef.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +24 -0
- package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +32 -0
- package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2846.d6a433b8663819a5.js +309 -0
- package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +34 -0
- package/apps/pwa/out/_next/static/chunks/{5967-c0a80f8ffe5d4050.js → 3259-86f3be56e43eaf6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +287 -0
- package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5578.ac963634373b28c6.js → 4084.15ecaf4240bd9e77.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8438.ee7f0df3b3f6fcb4.js → 4204.a409ab6250c0f6a8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +8 -0
- package/apps/pwa/out/_next/static/chunks/4565-67e4c620597965c8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7905.b230b9d06cb4ad5d.js → 4593.10f5b9bd702d4a6c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +104 -0
- package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{8569.b853a8daa8f31d12.js → 5081.a240b63fad28663a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2528.0af5c0e1fad88df6.js → 5408.377c8c543635b81f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9502-32c5dc22e8f120f5.js → 5492-e05e26a033ba80d2.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/5785-ea8d2c633b6688e3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/599-a914cc0468f56143.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +23 -0
- package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5974.360f8441eb0ac1ec.js → 6196.34d6b229079e9e28.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +77 -0
- package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +64 -0
- package/apps/pwa/out/_next/static/chunks/{ce383d63.d2ede338788ed18e.js → 65ebff95.081daeff622d238e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{83cfc5c8.349535543c1548b6.js → 6def8a8e.c81f9ca41028eccf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2721.bc9a8f36bd85c07a.js → 7483.ba01c6c209b81bba.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{6702.ebbd165d5e4a4a14.js → 7752.5cbdd0b433c54af9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8925-2abfb48af477fb7f.js → 8085-1e5fce59500a2cc2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{b4e12462-5fab4a813a63dadd.js → 83a80514-b897b64df43ce108.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +93 -0
- package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{8264.00e434fbd02e9311.js → 8578.dff22d988cc2b106.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +115 -0
- package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +41 -0
- package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +74 -0
- package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +94 -0
- package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7518.49161e8f5814abdb.js → 9311.24c8f5ea793253dd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9976.8f28864317639fe6.js → 94.4cfed21c0b781a06.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +90 -0
- package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{0e3acd34.d42b2ef87fdf4234.js → 973cbf1b.3bbcf2b41548eca7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6571-926ddf024a6ad5df.js → 9917-9e26abc5f7772735.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +79 -0
- package/apps/pwa/out/_next/static/chunks/{740.90b31555290236a9.js → 9974.e77b4ff4884557b3.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{4c5e95ce.83e9b16add6fcd8c.js → ab4d57c2.e90bc2cb31949045.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-24282bf9f90031a5.js → page-b12149c51f7d04b1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-305cd294e754906e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-dea4b795741126fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-45889e7070d11126.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-16a2e893f83ae4d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-56888bc744e08dac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-952c6ceec6d9de38.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-6d0202af568f4c63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4fb6fc34e20c07b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-e9502921f4f01b1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-b6169ea2f09cf287.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6a14333d1b1b3967.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8c91a9a66c9ec90a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-1d809a87139dd024.js → page-4e634c5b98fd4e93.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-b92caddb5c715f30.js → page-b9cc8a10bc6078b4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-3d3fc73dd187ed52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-9c04dac0831c2bbb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-04de9ca6d52a3893.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-62527677bbc2fdc4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9a2e09c4a311764c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-bfeb35d69e8b24bb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-89cb33bced824e4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-6445ac4cbb8dd869.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-20162174b77f96cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-05a4b2c9b91034aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{53746282.1be36038d1abd949.js → e117f801.e92bf10242653497.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{0673cc54.8e2ef1c23e49e190.js → f4dfbfc0.a69fb12f854882aa.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +1 -0
- package/apps/pwa/out/_next/static/{Q-7iofoxOEv1WX18S35of → dRS6lEhSRRBr1abbzp7fJ}/_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/graphs/release/release-loop.yaml +5 -5
- package/graphs/steward/recipes.yaml +4 -0
- package/graphs/steward/steward.yaml +10 -1
- package/package.json +1 -1
- package/scripts/bench-report.ts +46 -1
- package/scripts/ci-typecheck-changed.ts +7 -5
- package/scripts/release-loop/auto-approve-node.ts +6 -0
- package/scripts/release-loop/gate-closure.ts +286 -0
- package/scripts/release-loop/gate-node.ts +73 -13
- package/scripts/release-loop/gate-partial.ts +34 -6
- package/scripts/release-loop/ops-upgrade-node.ts +63 -42
- package/scripts/release-loop/publish-node.ts +6 -0
- package/scripts/release-loop/resume-release.ts +37 -10
- package/scripts/release-loop/resume.ts +69 -4
- package/scripts/tsc-baseline-cache.ts +28 -3
- package/scripts/usage-rollup.ts +27 -13
- package/scripts/ux-sim.ts +30 -1
- package/skills/youtube-master/scripts/main.ts +4 -1
- package/skills/youtube-master/src/obsidian.ts +9 -0
- package/src/agent-substrate/acp-implementer.ts +14 -7
- package/src/agent-substrate/pr-reviewer.ts +30 -10
- package/src/agent-substrate/self-review-cli.ts +126 -10
- package/src/budget/cost-tier-advisor.ts +91 -0
- package/src/budget/shadow-arms.ts +77 -0
- package/src/budget/types.ts +5 -3
- package/src/budget/unified-usage.ts +172 -8
- package/src/cli/doctor-cli.ts +21 -0
- package/src/cli/friendly-errors.ts +113 -0
- package/src/cli/intake-cli.ts +3 -1
- package/src/cli/memory-cli.ts +182 -0
- package/src/cli/model-watch-cli.ts +4 -1
- package/src/cli/pr-land-pod-shadow.ts +54 -0
- package/src/cli/release-cli.ts +9 -7
- package/src/cli/release-run-if-ready.ts +11 -0
- package/src/cli/resource-cli.ts +48 -0
- package/src/cli/retro-cli.ts +12 -3
- package/src/cli/seat-requests-cli.ts +25 -0
- package/src/cli/tasks-cli.ts +31 -7
- package/src/cli/usage-cli.ts +3 -1
- package/src/compact/verify-probe.ts +23 -3
- package/src/context-card/index.ts +50 -0
- package/src/control/control-status.ts +30 -1
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +4 -138
- package/src/dashboard/slash-runtime/shell-slash.ts +145 -0
- package/src/debug/log-diet.ts +16 -4
- package/src/domains/morning-essential.ts +18 -3
- package/src/domains/release-tool.ts +4 -0
- package/src/execution-loop/launch-gate.ts +158 -2
- package/src/fleet/host-doctor.ts +375 -0
- package/src/goals/judge.ts +20 -6
- package/src/graph-runner/runner.ts +9 -5
- package/src/graph-wizard/archetypes.ts +14 -8
- package/src/graph-wizard/generate.ts +77 -1
- package/src/grok.ts +13 -7
- package/src/harness/harness-cli-command.ts +56 -47
- package/src/harness/harness-stop.ts +82 -12
- package/src/index.ts +17 -175
- package/src/intake-plane/collect-github.ts +7 -2
- package/src/intake-plane/lens.ts +46 -4
- package/src/intake-plane/requirement-funnel.ts +53 -1
- package/src/intelligence-map/catalog-feed.ts +119 -0
- package/src/intelligence-map/model-watch-intake.ts +40 -0
- package/src/intelligence-map/model-watch-mission.ts +14 -1
- package/src/llm/open-weight-discipline.ts +21 -0
- package/src/llm.ts +4 -8
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/nexus/api/http-server.ts +22 -0
- package/src/oauth/codex-account-rotation.ts +4 -1
- package/src/oauth/codex-account-store.ts +20 -7
- package/src/oauth/codex-quota-policy.ts +33 -4
- package/src/preview/terminal.ts +15 -5
- package/src/project/project-store.ts +20 -0
- package/src/release-loop/auto-start.ts +7 -5
- package/src/release-loop/checklist.ts +11 -1
- package/src/release-loop/gate-shards.ts +4 -0
- package/src/release-loop/light-rc.ts +84 -13
- package/src/release-loop/merged-pr-checklist.ts +35 -5
- package/src/release-loop/rehearsal.ts +1 -1
- package/src/seat-loop/duty-ledger.ts +64 -0
- package/src/self-dev/decompose.ts +37 -3
- package/src/self-dev/dev-pipeline.ts +32 -7
- package/src/self-dev/draft-residue.ts +16 -6
- package/src/self-dev/draft-sweep.ts +52 -4
- package/src/self-dev/fabric-decompose-adapter.ts +9 -2
- package/src/self-dev/run-supervisor.ts +37 -7
- package/src/self-implement/gate-cli.ts +29 -10
- package/src/self-implement/gate-remote.ts +4 -1
- package/src/self-implement/goal-author.ts +12 -2
- package/src/self-implement/goal-new-paths.ts +42 -0
- package/src/self-implement/host-regate.ts +124 -30
- package/src/self-implement/importer-test-index.ts +3 -1
- package/src/self-implement/l8-merge-queue.ts +80 -2
- package/src/self-implement/merge-train.ts +19 -0
- package/src/self-implement/off-diff-evidence.ts +40 -1
- package/src/self-implement/orchestrator.ts +88 -19
- package/src/self-implement/premise-review.ts +159 -0
- package/src/self-implement/regate-detail.ts +79 -0
- package/src/self-implement/review-deferred-to-host.ts +21 -0
- package/src/self-implement/rework-budget-judgment.ts +23 -3
- package/src/self-implement/running-runs.ts +2 -2
- package/src/self-implement/scope-adjacency.ts +23 -0
- package/src/self-implement/seams.ts +42 -7
- package/src/skills/tools/agent.ts +16 -1
- package/src/skills/tools/persistent-grounding.ts +11 -1
- package/src/skills/tools/shell.ts +33 -1
- package/src/steward/rejudge-tick.ts +142 -0
- package/src/task-agent/board.ts +92 -3
- package/src/task-agent/card-evidence.ts +88 -1
- package/src/task-agent/cover.ts +65 -0
- package/src/task-agent/parent-host.ts +27 -3
- package/src/task-agent/rejudge-on-head.ts +151 -7
- package/src/task-agent/review-result-capture.ts +3 -1
- package/src/task-agent/shadow.ts +12 -0
- package/src/task-agent/task-hand.ts +4 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -0
- package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +122 -0
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +23 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +53 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +235 -28
- package/src/user-config.ts +18 -4
- package/src/ux-sim/fault-cred-expiry.ts +100 -0
- package/src/ux-sim/fault-pod-death.ts +74 -0
- package/src/ux-sim/fault-pool-cap.ts +81 -0
- package/src/version/packed-revision.json +1 -1
- package/src/webclone/qa-table.ts +22 -0
- package/apps/pwa/out/_next/static/chunks/0a09e062-34f747a17d8d8c2e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +0 -77
- package/apps/pwa/out/_next/static/chunks/1325-dc3f3c21eb181cb4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +0 -34
- package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +0 -24
- package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1669-da843ed73aa301f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +0 -36
- package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +0 -122
- package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +0 -62
- package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/272-0eed0fb0aab716b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +0 -149
- package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3961-aaae04ed1d32622f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +0 -309
- package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5313.1697309c532b9856.js +0 -2
- package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +0 -74
- package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +0 -64
- package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +0 -32
- package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +0 -24
- package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +0 -93
- package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +0 -148
- package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +0 -226
- package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +0 -27
- package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +0 -115
- package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +0 -8
- package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +0 -23
- package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +0 -90
- package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +0 -41
- package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +0 -287
- package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +0 -79
- package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +0 -104
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3b9fc39362473ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d7602d489edf5f37.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-4eb84d454f8c3415.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-b2e494bdad57fcb5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c0ab229673a8b0eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-bf88c0f558d8ef54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-52fec523e5b5d3ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-22725e2570f1126b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-1891335950d68b70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-c314b75c11234998.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-9f9c85415f61782b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8526c17ae38e6a50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-f998637a8402115c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6f23159fedb41f67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-da9e570234d481fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-57ffeeb6019440fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2c2b3f29cec1efa4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-9369279f72fc7b51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-901a1391b81053bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b5c0c996f329371e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-89881ccbcf728978.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-58d4e5cb30ef4871.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-acf868c64d77f950.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-bc79f0833533fa84.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d5c4398eea03d8f3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-b2763b10dc6b3836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +0 -1
- /package/apps/pwa/out/_next/static/{Q-7iofoxOEv1WX18S35of → dRS6lEhSRRBr1abbzp7fJ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { debug } from '../debug/log.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 호스트 닥터 — 첫 조각(FLEET-DOCTOR-1 · 읽기 전용).
|
|
6
|
+
* 설계 정본 = 내부 문서 `RFC-fleet-host-doctor-2026-10-11` §A1·A2·A4·A5.
|
|
7
|
+
* 점검은 코드 분기가 아니라 선언 표(HOST_DOCTOR_CHECKS)다.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type HostDoctorPlatform = 'darwin' | 'linux' | 'wsl';
|
|
11
|
+
export type HostDoctorRole = 'worker' | 'hq';
|
|
12
|
+
export type HostDoctorFixKind = 'auto' | 'sudo' | 'approval' | 'human';
|
|
13
|
+
export type HostDoctorCheckStatus = 'pass' | 'fail' | 'unmeasured';
|
|
14
|
+
export type HostDoctorBundle = 'access' | 'engine' | 'platform';
|
|
15
|
+
|
|
16
|
+
/** 원격 한 발의 결과. `null` = 그 발을 아예 못 쐈다(→ unmeasured). */
|
|
17
|
+
export interface HostDoctorRun {
|
|
18
|
+
status: number | null;
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** 주입 가능한 원격 실행 — 기본 = ssh BatchMode · `<host>=local` 이면 이 기계(bash -c). */
|
|
24
|
+
export type HostDoctorRemote = (host: string, probe: string) => HostDoctorRun | null;
|
|
25
|
+
|
|
26
|
+
/** HQ 실측값(본부의 설치 판 · 이미지 핀) — 없으면 그 점검은 unmeasured 다. */
|
|
27
|
+
export interface HostDoctorExpected {
|
|
28
|
+
installVersion?: string;
|
|
29
|
+
imagePin?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface HostDoctorExpectContext {
|
|
33
|
+
platform: HostDoctorPlatform | null;
|
|
34
|
+
arch: string | null;
|
|
35
|
+
expected: HostDoctorExpected;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type HostDoctorExpectResult =
|
|
39
|
+
| { status: 'pass' | 'fail'; observed: string; expected: string }
|
|
40
|
+
| { status: 'unmeasured'; observed: string; expected: string };
|
|
41
|
+
|
|
42
|
+
export interface HostDoctorCheckSpec {
|
|
43
|
+
id: string;
|
|
44
|
+
bundle: HostDoctorBundle;
|
|
45
|
+
platforms: readonly HostDoctorPlatform[];
|
|
46
|
+
roles: readonly HostDoctorRole[];
|
|
47
|
+
/** 원격 셸 한 줄. */
|
|
48
|
+
probe: string;
|
|
49
|
+
expect: (run: HostDoctorRun, ctx: HostDoctorExpectContext) => HostDoctorExpectResult;
|
|
50
|
+
fixKind: HostDoctorFixKind;
|
|
51
|
+
fix?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface HostDoctorCheckResult {
|
|
55
|
+
id: string;
|
|
56
|
+
status: HostDoctorCheckStatus;
|
|
57
|
+
observed: string;
|
|
58
|
+
expected: string;
|
|
59
|
+
fixKind: HostDoctorFixKind;
|
|
60
|
+
fix?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 대장 레코드 종류 `setup-status {id, ok, missing[], fixHref}` 의 몸 ⊕ 확장 칸(RFC-fleet-host-doctor §A0 · 독자 이름 fleet.host.v1 은 쓰지 않는다). */
|
|
64
|
+
export interface HostDoctorReport {
|
|
65
|
+
/** setup-status.id — 호스트 이름. */
|
|
66
|
+
id: string;
|
|
67
|
+
/** setup-status.ok — fail 0 ⊕ unmeasured 0 일 때만. */
|
|
68
|
+
ok: boolean;
|
|
69
|
+
/** setup-status.missing — fail ⊕ unmeasured 점검 id. */
|
|
70
|
+
missing: string[];
|
|
71
|
+
/** setup-status.fixHref — 고치는 길(빠진 것이 없으면 null). */
|
|
72
|
+
fixHref: string | null;
|
|
73
|
+
// ── 확장 칸 ──
|
|
74
|
+
platform: HostDoctorPlatform | null;
|
|
75
|
+
arch: string | null;
|
|
76
|
+
role: HostDoctorRole;
|
|
77
|
+
measuredAt: string;
|
|
78
|
+
checks: HostDoctorCheckResult[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 빠진 점검이 있을 때의 고치는 길 — 사람 손 처방은 RFC 표, 자동 처방은 node-setup(다음 조각 FLEET-DOCTOR-FIX 가 --fix 로 잇는다). */
|
|
82
|
+
export const HOST_DOCTOR_FIX_HREF = 'docs/RFC-fleet-host-doctor-2026-10-11.md';
|
|
83
|
+
|
|
84
|
+
const ALL_PLATFORMS: readonly HostDoctorPlatform[] = ['darwin', 'linux', 'wsl'];
|
|
85
|
+
const ALL_ROLES: readonly HostDoctorRole[] = ['worker', 'hq'];
|
|
86
|
+
|
|
87
|
+
function line(value: string): string {
|
|
88
|
+
return value.trim().split('\n')[0] ?? '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function nonEmpty(value: string | null | undefined): value is string {
|
|
92
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** uname -m 과 docker 의 아키텍처 이름을 한 이름으로 — x86_64≡amd64 · aarch64≡arm64(10-11 WSL 실물: «x86_64 vs docker amd64» 오판). */
|
|
96
|
+
export function normalizeArch(arch: string): string {
|
|
97
|
+
const lower = arch.trim().toLowerCase();
|
|
98
|
+
if (lower === 'x86_64' || lower === 'x64') return 'amd64';
|
|
99
|
+
if (lower === 'aarch64' || lower === 'arm64v8') return 'arm64';
|
|
100
|
+
return lower;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 묶음 셋(접근 · 엔진 · 판)의 선언 표 — 첫 조각. */
|
|
104
|
+
export const HOST_DOCTOR_CHECKS: readonly HostDoctorCheckSpec[] = [
|
|
105
|
+
// ── 접근 ──
|
|
106
|
+
{
|
|
107
|
+
id: 'ssh.batch',
|
|
108
|
+
bundle: 'access',
|
|
109
|
+
platforms: ALL_PLATFORMS,
|
|
110
|
+
roles: ALL_ROLES,
|
|
111
|
+
// 원격 한 발 자체가 `ssh -o BatchMode=yes <host>` 로 간다 — 그 발이 «ok» 를 돌려주면 그게 증거다.
|
|
112
|
+
// (원격 안에서 다시 `ssh localhost` 를 치면 «그 호스트가 자기 자신에게» 붙는지를 재게 된다 · 10-11 실물 오판)
|
|
113
|
+
probe: 'echo ok',
|
|
114
|
+
expect: (run) => ({
|
|
115
|
+
status: run.status === 0 && line(run.stdout) === 'ok' ? 'pass' : 'fail',
|
|
116
|
+
observed: run.status === 0 ? line(run.stdout) || '(empty)' : `exit ${run.status ?? 'none'}`,
|
|
117
|
+
expected: 'ssh -o BatchMode=yes works without a password prompt',
|
|
118
|
+
}),
|
|
119
|
+
fixKind: 'human',
|
|
120
|
+
fix: 'put this host key in ~/.ssh/authorized_keys of the host user',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'tailnet.online',
|
|
124
|
+
bundle: 'access',
|
|
125
|
+
platforms: ALL_PLATFORMS,
|
|
126
|
+
roles: ALL_ROLES,
|
|
127
|
+
probe: 'command -v tailscale >/dev/null 2>&1 || { echo no-cli; exit 0; }; tailscale status >/dev/null 2>&1 && echo online || echo offline',
|
|
128
|
+
expect: (run) => {
|
|
129
|
+
const out = line(run.stdout);
|
|
130
|
+
if (out === 'no-cli') return { status: 'unmeasured', observed: 'tailscale CLI not on the non-interactive PATH', expected: 'tailscale is up and logged in' };
|
|
131
|
+
return { status: out === 'online' ? 'pass' : 'fail', observed: out || `exit ${run.status ?? 'none'}`, expected: 'tailscale is up and logged in' };
|
|
132
|
+
},
|
|
133
|
+
fixKind: 'human',
|
|
134
|
+
fix: 'run `tailscale up` on the host',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'path.noninteractive',
|
|
138
|
+
bundle: 'access',
|
|
139
|
+
platforms: ALL_PLATFORMS,
|
|
140
|
+
roles: ALL_ROLES,
|
|
141
|
+
probe: 'command -v bun; command -v codex',
|
|
142
|
+
expect: (run) => {
|
|
143
|
+
const found = run.stdout.trim().split('\n').map((entry) => entry.trim()).filter(nonEmpty);
|
|
144
|
+
return {
|
|
145
|
+
status: found.length >= 2 ? 'pass' : 'fail',
|
|
146
|
+
observed: found.length ? found.join(' ') : 'none on PATH',
|
|
147
|
+
expected: 'bun and codex reachable on the default non-interactive PATH',
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
fixKind: 'human',
|
|
151
|
+
fix: 'install bun/codex into a PATH a non-interactive shell already sees (no ~/.bashrc edits)',
|
|
152
|
+
},
|
|
153
|
+
// ── 엔진 ──
|
|
154
|
+
{
|
|
155
|
+
id: 'docker.engine',
|
|
156
|
+
bundle: 'engine',
|
|
157
|
+
platforms: ALL_PLATFORMS,
|
|
158
|
+
roles: ALL_ROLES,
|
|
159
|
+
probe: "docker info --format '{{.ServerVersion}}' 2>/dev/null",
|
|
160
|
+
expect: (run) => ({
|
|
161
|
+
status: run.status === 0 && nonEmpty(line(run.stdout)) ? 'pass' : 'fail',
|
|
162
|
+
observed: run.status === 0 ? line(run.stdout) || '(empty)' : `docker info exit ${run.status ?? 'none'}`,
|
|
163
|
+
expected: 'a real docker engine answers (not a desktop shim)',
|
|
164
|
+
}),
|
|
165
|
+
fixKind: 'human',
|
|
166
|
+
fix: 'start the docker engine on the host',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'k3d.cluster',
|
|
170
|
+
bundle: 'engine',
|
|
171
|
+
platforms: ALL_PLATFORMS,
|
|
172
|
+
roles: ALL_ROLES,
|
|
173
|
+
// ⛔ `--no-header` 는 k3d v5 에 없는 플래그다(«unknown flag» → 빈 출력 → «클러스터 없음» 오판 · 10-11 node-c·WSL 실물).
|
|
174
|
+
probe: 'command -v k3d >/dev/null 2>&1 || { echo no-cli; exit 0; }; out=$(k3d cluster list -o json 2>/dev/null) || { echo error; exit 0; }; printf %s "$out" | grep -q \'"name"\' && echo present || echo none',
|
|
175
|
+
expect: (run) => {
|
|
176
|
+
const out = line(run.stdout);
|
|
177
|
+
if (out === 'no-cli') return { status: 'unmeasured', observed: 'k3d not on the non-interactive PATH', expected: 'at least one k3d cluster exists' };
|
|
178
|
+
if (out === 'error') return { status: 'unmeasured', observed: 'k3d cluster list failed', expected: 'at least one k3d cluster exists' };
|
|
179
|
+
return { status: out === 'present' ? 'pass' : 'fail', observed: out === 'present' ? 'present' : 'no k3d cluster', expected: 'at least one k3d cluster exists' };
|
|
180
|
+
},
|
|
181
|
+
fixKind: 'human',
|
|
182
|
+
fix: 'create the k3d cluster (scripts/fleet/node-setup.sh)',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: 'registry.local',
|
|
186
|
+
bundle: 'engine',
|
|
187
|
+
platforms: ALL_PLATFORMS,
|
|
188
|
+
roles: ALL_ROLES,
|
|
189
|
+
// 풀 레지스트리는 k3d-elanous-registry:5050 이다(호스트 포트 5050) · 5000 은 맨 docker registry 관례라 대체로만 본다.
|
|
190
|
+
probe: 'for port in 5050 5000; do curl -fsS --max-time 3 "http://localhost:$port/v2/" >/dev/null 2>&1 && { echo "reachable:$port"; exit 0; }; done; echo unreachable',
|
|
191
|
+
expect: (run) => {
|
|
192
|
+
const out = line(run.stdout);
|
|
193
|
+
return {
|
|
194
|
+
status: out.startsWith('reachable:') ? 'pass' : 'fail',
|
|
195
|
+
observed: out.startsWith('reachable:') ? `localhost:${out.slice('reachable:'.length)} reachable` : 'localhost:5050/5000 /v2 unreachable',
|
|
196
|
+
expected: 'local registry answers on localhost:5050 (or 5000)',
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
fixKind: 'human',
|
|
200
|
+
fix: 'start the local registry container',
|
|
201
|
+
},
|
|
202
|
+
// ── 판 ──
|
|
203
|
+
{
|
|
204
|
+
id: 'install.version',
|
|
205
|
+
bundle: 'platform',
|
|
206
|
+
platforms: ALL_PLATFORMS,
|
|
207
|
+
roles: ALL_ROLES,
|
|
208
|
+
probe: 'elanous --version 2>/dev/null || echo none',
|
|
209
|
+
expect: (run, ctx) => {
|
|
210
|
+
const observed = line(run.stdout) || 'none';
|
|
211
|
+
if (!nonEmpty(ctx.expected.installVersion)) {
|
|
212
|
+
return { status: 'unmeasured', observed, expected: 'HQ install version (not supplied)' };
|
|
213
|
+
}
|
|
214
|
+
return { status: observed === ctx.expected.installVersion ? 'pass' : 'fail', observed, expected: ctx.expected.installVersion };
|
|
215
|
+
},
|
|
216
|
+
fixKind: 'auto',
|
|
217
|
+
fix: 'reinstall this host from the HQ install line',
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: 'image.pin',
|
|
221
|
+
bundle: 'platform',
|
|
222
|
+
platforms: ALL_PLATFORMS,
|
|
223
|
+
roles: ALL_ROLES,
|
|
224
|
+
probe: "docker ps --format '{{.Image}}' 2>/dev/null | grep -m1 elanous || true",
|
|
225
|
+
expect: (run, ctx) => {
|
|
226
|
+
const observed = line(run.stdout);
|
|
227
|
+
if (!nonEmpty(ctx.expected.imagePin)) {
|
|
228
|
+
return { status: 'unmeasured', observed: observed || 'none', expected: 'HQ image pin (not supplied)' };
|
|
229
|
+
}
|
|
230
|
+
return { status: nonEmpty(observed) && observed === ctx.expected.imagePin ? 'pass' : 'fail', observed: observed || 'no elanous image running', expected: ctx.expected.imagePin };
|
|
231
|
+
},
|
|
232
|
+
fixKind: 'auto',
|
|
233
|
+
fix: 'restart the pod from the pinned image',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: 'image.arch',
|
|
237
|
+
bundle: 'platform',
|
|
238
|
+
platforms: ALL_PLATFORMS,
|
|
239
|
+
roles: ALL_ROLES,
|
|
240
|
+
probe: "uname -m; docker version --format '{{.Server.Arch}}' 2>/dev/null",
|
|
241
|
+
expect: (run) => {
|
|
242
|
+
const [hostArch, engineArch] = run.stdout.trim().split('\n').map((entry) => entry.trim());
|
|
243
|
+
// docker 가 답하지 않으면 엔진 쪽을 «못 쟀다» — fail 이 아니라 unmeasured(k3d·tailscale 과 같은 원칙).
|
|
244
|
+
if (!nonEmpty(engineArch)) {
|
|
245
|
+
return { status: 'unmeasured', observed: `${hostArch || 'none'} vs docker (no answer)`, expected: 'image architecture matches uname -m' };
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
status: nonEmpty(hostArch) && nonEmpty(engineArch) && normalizeArch(hostArch) === normalizeArch(engineArch) ? 'pass' : 'fail',
|
|
249
|
+
observed: nonEmpty(hostArch) || nonEmpty(engineArch) ? `${hostArch ?? 'none'} vs docker ${engineArch ?? 'none'}` : 'none',
|
|
250
|
+
expected: 'image architecture matches uname -m',
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
fixKind: 'auto',
|
|
254
|
+
fix: 'pull the image pinned to this host architecture',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'node.present',
|
|
258
|
+
bundle: 'platform',
|
|
259
|
+
platforms: ALL_PLATFORMS,
|
|
260
|
+
roles: ALL_ROLES,
|
|
261
|
+
probe: 'command -v node || echo none',
|
|
262
|
+
expect: (run) => {
|
|
263
|
+
const observed = line(run.stdout) || 'none';
|
|
264
|
+
return { status: observed !== 'none' && nonEmpty(observed) ? 'pass' : 'fail', observed, expected: 'node is on PATH (codex shebang)' };
|
|
265
|
+
},
|
|
266
|
+
fixKind: 'auto',
|
|
267
|
+
fix: 'install node (scripts/fleet/node-setup.sh)',
|
|
268
|
+
},
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
/** 기본 원격 실행 — `<host>=local` 이면 이 기계의 비대화형 셸, 아니면 ssh BatchMode. 못 쏘면 null. */
|
|
272
|
+
export function defaultHostDoctorRemote(host: string, probe: string): HostDoctorRun | null {
|
|
273
|
+
const viaSsh = host !== 'local';
|
|
274
|
+
const spawned = viaSsh
|
|
275
|
+
// `--` 로 host 를 끊는다(«-o…» 로 시작하는 인자가 ssh 옵션으로 읽히지 않게) · ConnectTimeout 으로 패킷을 떨구는 호스트의 대기를 줄인다.
|
|
276
|
+
? spawnSync('ssh', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', '--', host, probe], { encoding: 'utf8', timeout: 20_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
277
|
+
: spawnSync('bash', ['-c', probe], { encoding: 'utf8', timeout: 15_000, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
278
|
+
return hostDoctorRunFromSpawn(spawned, viaSsh);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** spawn 결과 → 원격 한 발. ⛔ ssh 는 «연결 자체» 실패(거부·이름 못 풂·키 거절)를 exit 255 로 낸다 — 그건 탐침이
|
|
282
|
+
* «못 쐈다» 이므로 null(→ unmeasured)이다. 그대로 넘기면 모든 점검이 «✗ fail» 로 읽힌다(10-11 리뷰 must-fix). */
|
|
283
|
+
export function hostDoctorRunFromSpawn(
|
|
284
|
+
spawned: { error?: Error; status: number | null; stdout?: string | null; stderr?: string | null },
|
|
285
|
+
viaSsh: boolean,
|
|
286
|
+
): HostDoctorRun | null {
|
|
287
|
+
if (spawned.error || spawned.status === null) return null;
|
|
288
|
+
if (viaSsh && spawned.status === 255) return null;
|
|
289
|
+
return { status: spawned.status, stdout: spawned.stdout ?? '', stderr: spawned.stderr ?? '' };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface HostDoctorOptions {
|
|
293
|
+
host: string;
|
|
294
|
+
role?: HostDoctorRole;
|
|
295
|
+
remote?: HostDoctorRemote;
|
|
296
|
+
expected?: HostDoctorExpected;
|
|
297
|
+
now?: () => Date;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** 원격 `uname -s` ⊕ `/proc/version` 의 microsoft 로 darwin|linux|wsl. 못 재면 null. */
|
|
301
|
+
export function detectHostDoctorPlatform(remote: HostDoctorRemote, host: string): HostDoctorPlatform | null {
|
|
302
|
+
const uname = remote(host, 'uname -s');
|
|
303
|
+
if (!uname || uname.status !== 0) return null;
|
|
304
|
+
const kernel = line(uname.stdout).toLowerCase();
|
|
305
|
+
if (kernel === 'darwin') return 'darwin';
|
|
306
|
+
if (kernel === 'linux') {
|
|
307
|
+
const procVersion = remote(host, 'cat /proc/version 2>/dev/null || true');
|
|
308
|
+
return procVersion && procVersion.status === 0 && /microsoft/i.test(procVersion.stdout) ? 'wsl' : 'linux';
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** 원격 아키텍처(`uname -m`). 못 재면 null. */
|
|
314
|
+
export function detectHostDoctorArch(remote: HostDoctorRemote, host: string): string | null {
|
|
315
|
+
const run = remote(host, 'uname -m');
|
|
316
|
+
if (!run || run.status !== 0) return null;
|
|
317
|
+
const arch = line(run.stdout);
|
|
318
|
+
return nonEmpty(arch) ? arch : null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** 못 쏜 점검 — 기대값 칸엔 셸 탐침이 아니라 그 점검의 «기대 문장»을 싣는다(다른 경로와 같은 뜻). */
|
|
322
|
+
function unmeasuredBecauseUnreached(spec: HostDoctorCheckSpec, options: { platform: HostDoctorPlatform | null; arch: string | null; expected: HostDoctorExpected }): HostDoctorCheckResult {
|
|
323
|
+
const expected = spec.expect({ status: 1, stdout: '', stderr: '' }, { platform: options.platform, arch: options.arch, expected: options.expected }).expected;
|
|
324
|
+
return { id: spec.id, fixKind: spec.fixKind, ...(spec.fix !== undefined ? { fix: spec.fix } : {}), status: 'unmeasured', observed: 'remote probe did not run', expected };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** 점검 하나를 돌린다 — 원격 실패는 «pass 가 아닌» unmeasured 로만 간다. */
|
|
328
|
+
function runCheck(spec: HostDoctorCheckSpec, options: Required<Pick<HostDoctorOptions, 'host'>> & { remote: HostDoctorRemote; role: HostDoctorRole; platform: HostDoctorPlatform | null; arch: string | null; expected: HostDoctorExpected }): HostDoctorCheckResult {
|
|
329
|
+
const run = options.remote(options.host, spec.probe);
|
|
330
|
+
const base = { id: spec.id, fixKind: spec.fixKind, ...(spec.fix !== undefined ? { fix: spec.fix } : {}) };
|
|
331
|
+
if (run === null) return unmeasuredBecauseUnreached(spec, options);
|
|
332
|
+
const verdict = spec.expect(run, { platform: options.platform, arch: options.arch, expected: options.expected });
|
|
333
|
+
return { ...base, status: verdict.status, observed: verdict.observed, expected: verdict.expected };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** `elanous doctor host <host>` — 읽기 전용. ok 는 fail 0 ⊕ unmeasured 0 일 때만. */
|
|
337
|
+
export function runHostDoctor(options: HostDoctorOptions): HostDoctorReport {
|
|
338
|
+
const host = options.host;
|
|
339
|
+
const role: HostDoctorRole = options.role === 'hq' ? 'hq' : 'worker';
|
|
340
|
+
const remote = options.remote ?? defaultHostDoctorRemote;
|
|
341
|
+
const expected = options.expected ?? {};
|
|
342
|
+
// 첫 발(uname -s)부터 못 쏘면 그 호스트에 «닿지 않는다» — 남은 탐침을 하나씩 기다리지 않고 전부 unmeasured 로 둔다.
|
|
343
|
+
const reached = remote(host, 'uname -s') !== null;
|
|
344
|
+
const platform = reached ? detectHostDoctorPlatform(remote, host) : null;
|
|
345
|
+
const arch = reached ? detectHostDoctorArch(remote, host) : null;
|
|
346
|
+
const specs = HOST_DOCTOR_CHECKS.filter((spec) => spec.roles.includes(role));
|
|
347
|
+
const checks = reached
|
|
348
|
+
? specs.map((spec) => runCheck(spec, { host, remote, role, platform, arch, expected }))
|
|
349
|
+
: specs.map((spec) => unmeasuredBecauseUnreached(spec, { platform, arch, expected }));
|
|
350
|
+
const fail = checks.filter((check) => check.status === 'fail').map((check) => check.id);
|
|
351
|
+
const unmeasured = checks.filter((check) => check.status === 'unmeasured').map((check) => check.id);
|
|
352
|
+
const measuredAt = (options.now ?? (() => new Date()))().toISOString();
|
|
353
|
+
debug.log('fleet.doctor', 'host-checked', { host, platform, role, fail, unmeasured });
|
|
354
|
+
const missing = [...fail, ...unmeasured];
|
|
355
|
+
return {
|
|
356
|
+
id: host,
|
|
357
|
+
ok: missing.length === 0,
|
|
358
|
+
missing,
|
|
359
|
+
fixHref: missing.length === 0 ? null : HOST_DOCTOR_FIX_HREF,
|
|
360
|
+
platform,
|
|
361
|
+
arch,
|
|
362
|
+
role,
|
|
363
|
+
measuredAt,
|
|
364
|
+
checks,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** 사람 출력 한 줄씩 — «✓|✗|→ <id> <한 줄>» 문법(node-setup.sh --check 와 같다). */
|
|
369
|
+
export function formatHostDoctorLines(report: HostDoctorReport): string[] {
|
|
370
|
+
return report.checks.map((check) => {
|
|
371
|
+
const mark = check.status === 'pass' ? '✓' : check.status === 'fail' ? '✗' : '→';
|
|
372
|
+
const detail = check.status === 'fail' ? `${check.observed} (expected: ${check.expected})` : check.observed;
|
|
373
|
+
return `${mark} ${check.id} ${detail}`;
|
|
374
|
+
});
|
|
375
|
+
}
|
package/src/goals/judge.ts
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
// PAUSE+ASK in the loop. Drift is more dangerous than a confirm.
|
|
12
12
|
|
|
13
13
|
import { debug } from '../debug/log.js';
|
|
14
|
-
import { getProvider, resolveDefaultProvider, type LLMMessage, type LLMOpts } from '../llm.js';
|
|
14
|
+
import { getProvider, logAgentTurnUsage, resolveDefaultProvider, type LLMMessage, type LLMOpts } from '../llm.js';
|
|
15
|
+
import type { LLMUsage } from '../prompt-cache/types.js';
|
|
15
16
|
import type { GoalJudgeVerdict } from './types.js';
|
|
16
17
|
|
|
17
18
|
export interface JudgeInput {
|
|
@@ -180,15 +181,28 @@ export async function judgeGoalTurn(
|
|
|
180
181
|
});
|
|
181
182
|
|
|
182
183
|
let text = '';
|
|
184
|
+
let usage: LLMUsage = {};
|
|
183
185
|
try {
|
|
184
|
-
const stream = provider.chat(messages, llmOpts);
|
|
185
186
|
const t0 = Date.now();
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
if (provider.streamChat) {
|
|
188
|
+
for await (const ev of provider.streamChat(messages, llmOpts)) {
|
|
189
|
+
if (ev.type === 'text' && ev.delta) text += ev.delta;
|
|
190
|
+
else if (ev.type === 'usage') usage = { ...usage, ...ev.usage };
|
|
191
|
+
if (opts.timeoutMs && Date.now() - t0 > opts.timeoutMs) {
|
|
192
|
+
throw new Error(`judge timeout after ${opts.timeoutMs}ms`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
for await (const chunk of provider.chat(messages, llmOpts)) {
|
|
197
|
+
text += chunk;
|
|
198
|
+
if (opts.timeoutMs && Date.now() - t0 > opts.timeoutMs) {
|
|
199
|
+
throw new Error(`judge timeout after ${opts.timeoutMs}ms`);
|
|
200
|
+
}
|
|
190
201
|
}
|
|
191
202
|
}
|
|
203
|
+
try {
|
|
204
|
+
logAgentTurnUsage(opts.model ?? provider.defaultModel, usage, { providerName: provider.name, site: 'goals.judge' });
|
|
205
|
+
} catch { /* best-effort observation */ }
|
|
192
206
|
} catch (err) {
|
|
193
207
|
debug.log('goal', 'judge.error', { attempt, message: (err as Error).message }, { level: 'error' });
|
|
194
208
|
// On timeout / abort, return null so the loop can pause-ask
|
|
@@ -41,7 +41,7 @@ export interface GraphRunState {
|
|
|
41
41
|
/** startedAt/endedAt (ISO) and seconds are written when the node runs; ledgers before GRAPH-NODE-TIMES (0.2.19) have none. */
|
|
42
42
|
nodes: Array<{ nodeId: string; ok: boolean; exit: number | null; executed: boolean; output?: unknown; error?: string; decidedBy?: string; decidedAt?: string;
|
|
43
43
|
startedAt?: string; endedAt?: string; seconds?: number;
|
|
44
|
-
waiver?: { reason: string; at: string; original: { exit: number | null; output?: unknown; error?: string } } }>;
|
|
44
|
+
waiver?: { reason: string; at: string; /** RELEASE-WAIVE-UNMEASURED — why the waived gate had no verdict. */ unmeasured?: string; original: { exit: number | null; output?: unknown; error?: string } } }>;
|
|
45
45
|
/** The node now running and when it started; cleared when its record is written. */
|
|
46
46
|
currentNode?: { nodeId: string; startedAt: string };
|
|
47
47
|
input?: unknown;
|
|
@@ -76,7 +76,7 @@ export interface GraphRunOptions {
|
|
|
76
76
|
resumeGraphId?: string;
|
|
77
77
|
fromNodeId?: string;
|
|
78
78
|
/** Release-only: accept the recorded failed node without executing it, and expose its regression to known-issues. */
|
|
79
|
-
releaseWaiver?: { nodeId: string; reason: string; requestedRegressions: AcceptedRegression[]; acceptedRegressions: AcceptedRegression[] };
|
|
79
|
+
releaseWaiver?: { nodeId: string; reason: string; requestedRegressions: AcceptedRegression[]; acceptedRegressions: AcceptedRegression[]; unmeasured?: string };
|
|
80
80
|
/** Release-only: add accepted issues independently of a waiver at a restart boundary. */
|
|
81
81
|
releaseAcceptedRegressions?: { requested: AcceptedRegression[]; acceptedRegressions: AcceptedRegression[] };
|
|
82
82
|
/** Release-only (GATE-HOST-EXECUTOR): run the restarted gate on this executor — recorded in `inputOverrides`, never a waiver. */
|
|
@@ -358,6 +358,9 @@ export function decideGraphApproval(graphId: string, runId: string, decision: 'a
|
|
|
358
358
|
return applyRecordedDecision(state);
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
/** The record `graph runs stop` writes for the node it cut (release resume reads it as an unmeasured gate). */
|
|
362
|
+
export const STOPPED_NODE_ERROR = 'stopped before node completed';
|
|
363
|
+
|
|
361
364
|
/** 노드 stdout 의 «마지막 비어 있지 않은 줄»이 JSON 객체면 그것을 구조 산출로 본다(아니면 없음). */
|
|
362
365
|
export function lastJsonObject(stdout: unknown): Record<string, unknown> | undefined {
|
|
363
366
|
if (typeof stdout !== 'string') return undefined;
|
|
@@ -611,7 +614,8 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
611
614
|
if (options.releaseWaiver) {
|
|
612
615
|
if (graphId !== 'release-loop') throw new Error('release waiver requires the release-loop graph');
|
|
613
616
|
const checked = releaseResumeWaiver(state, { from: options.fromNodeId, waive: options.releaseWaiver.nodeId, reason: options.releaseWaiver.reason, acceptedRegressions: options.releaseWaiver.requestedRegressions });
|
|
614
|
-
if (!checked || JSON.stringify(checked.acceptedRegressions) !== JSON.stringify(options.releaseWaiver.acceptedRegressions)
|
|
617
|
+
if (!checked || JSON.stringify(checked.acceptedRegressions) !== JSON.stringify(options.releaseWaiver.acceptedRegressions) ||
|
|
618
|
+
checked.unmeasured !== options.releaseWaiver.unmeasured) throw new Error('release waiver input changed before restart');
|
|
615
619
|
const route = new Set<string>();
|
|
616
620
|
let cursor: string | undefined = nextNode(graph.edges, options.fromNodeId, 'ok', 'ok');
|
|
617
621
|
while (cursor && !route.has(cursor) && cursor !== 'known-issues') {
|
|
@@ -995,7 +999,7 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
995
999
|
output = JSON.stringify({ outcome: ok ? 'approved' : 'rejected', ...(state.pending?.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending?.decidedAt ?? null });
|
|
996
1000
|
}
|
|
997
1001
|
state.nodes.push({ nodeId: current, ok, exit, executed: !waiving && ((!!command && (!state.dryRun || !!command.dry_run_command)) || (!!prompt && !state.dryRun) || unitExecuted), ...((waiving || units.has(current) || ((command || prompt || approval) && (!state.dryRun || !!command?.dry_run_command))) && output !== undefined ? { output } : {}), ...(error ? { error } : {}),
|
|
998
|
-
...(waiving ? { waiver: { reason: options.releaseWaiver!.reason, at: nodeEndedAt, original: { exit: waivedRecord!.exit, ...(waivedRecord!.output === undefined ? {} : { output: waivedRecord!.output }), ...(waivedRecord!.error === undefined ? {} : { error: waivedRecord!.error }) } } } : {}),
|
|
1002
|
+
...(waiving ? { waiver: { reason: options.releaseWaiver!.reason, at: nodeEndedAt, ...(options.releaseWaiver!.unmeasured === undefined ? {} : { unmeasured: options.releaseWaiver!.unmeasured }), original: { exit: waivedRecord!.exit, ...(waivedRecord!.output === undefined ? {} : { output: waivedRecord!.output }), ...(waivedRecord!.error === undefined ? {} : { error: waivedRecord!.error }) } } } : {}),
|
|
999
1003
|
...(approval && state.pending ? { ...(state.pending.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending.decidedAt } : {}),
|
|
1000
1004
|
startedAt: nodeStartedAt, endedAt: nodeEndedAt, seconds });
|
|
1001
1005
|
}
|
|
@@ -1160,7 +1164,7 @@ export function manageGraphRun(graphId: string, runId: string, action: 'stop' |
|
|
|
1160
1164
|
// One stop instant for the node end, finishedAt and stoppedAt — never before the running node's start.
|
|
1161
1165
|
const stopMs = running === undefined ? Date.now() : Math.max(Date.now(), Date.parse(running));
|
|
1162
1166
|
if (state.path.length === state.nodes.length + 1) {
|
|
1163
|
-
state.nodes.push({ nodeId: state.path.at(-1)!, ok: false, exit: null, executed: false, error:
|
|
1167
|
+
state.nodes.push({ nodeId: state.path.at(-1)!, ok: false, exit: null, executed: false, error: STOPPED_NODE_ERROR,
|
|
1164
1168
|
// The stop instant is always the node end; a start (and so seconds) exists only when the ledger named the running
|
|
1165
1169
|
// node — a run begun by a runner before GRAPH-NODE-TIMES did not, and a guessed start would be a false time.
|
|
1166
1170
|
endedAt: new Date(stopMs).toISOString(),
|
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
* 마법사 «원형» — 사람이 말로 자주 시키는 일의 뼈대 셋. graphs/** 의 운영 템플릿(개발·출시 루프)은
|
|
3
3
|
* «모아서 요약해 보내기» 같은 말과 낱말이 안 겹쳐 엉뚱한 기반(lecture-note)을 골랐다(10-08 라이브 실측).
|
|
4
4
|
* 원형은 기반 후보 «맨 앞»에 서고, 키워드가 겹치는 만큼 점수를 얻는다.
|
|
5
|
+
* 각 원형은 그래프 값어치 신호를 둘 이상 실린다(G1 검증 judge · G2 되돌림 간선 ⊕ max_visits ≥ 2 · G3 승인 hitl · G4 갈래) —
|
|
6
|
+
* 단순 나열은 스킬 사슬이지 그래프가 아니다(RFC SW-GRAPH-SMELL · generate.ts graphSignals 로 잰다).
|
|
5
7
|
*/
|
|
6
8
|
export interface WizardArchetype { id: string; description: string; keywords: string[]; text: string }
|
|
7
9
|
|
|
8
10
|
export const WIZARD_ARCHETYPES: readonly WizardArchetype[] = [
|
|
9
11
|
{
|
|
10
12
|
id: 'wizard-collect-summarize-send',
|
|
11
|
-
description: '모아서 요약해 보내기 — 검색·수집 → 요약 → 텔레그램/알림 발송 (브리핑·다이제스트·동향)',
|
|
13
|
+
description: '모아서 요약해 보내기 — 검색·수집 → 요약 → 출처 대조(판정·되돌림) → 텔레그램/알림 발송 (브리핑·다이제스트·동향)',
|
|
12
14
|
keywords: ['뉴스', '모아', '수집', '검색', '찾아', '요약', '정리', '보내', '알려', '텔레그램', '브리핑', '동향', '소식', 'news', 'digest', 'summary'],
|
|
13
15
|
text: `graph_id: wizard-collect-summarize-send
|
|
14
16
|
loop:
|
|
15
17
|
title: 모아서 요약해 보내기
|
|
16
|
-
description: 검색으로 모은 내용을
|
|
18
|
+
description: 검색으로 모은 내용을 요약하고 출처와 대조해 텔레그램으로 보낸다
|
|
17
19
|
trigger:
|
|
18
20
|
cron: "0 8 * * *"
|
|
19
21
|
version: 1
|
|
@@ -21,13 +23,15 @@ entry_node: collect
|
|
|
21
23
|
terminal_nodes: [done, failed]
|
|
22
24
|
nodes:
|
|
23
25
|
- { node_id: collect, kind: agent, recipe: 'cmd:collect', max_visits: 1 } # 수집 | web-search | 검색어
|
|
24
|
-
- { node_id: summarize, kind: agent, recipe: 'cmd:summarize', max_visits:
|
|
26
|
+
- { node_id: summarize, kind: agent, recipe: 'cmd:summarize', max_visits: 2 } # 요약 | summarize | 핵심 5줄 한국어
|
|
27
|
+
- { node_id: check, kind: judge, recipe: 'cmd:check', max_visits: 2 } # 출처 대조 | check | 요약이 수집 결과와 일치하는가
|
|
25
28
|
- { node_id: send, kind: agent, recipe: 'cmd:send', max_visits: 1 } # 텔레그램 발송 | telegram-send
|
|
26
29
|
- { node_id: done, kind: gate, recipe: none, max_visits: 1 } # 완료
|
|
27
30
|
- { node_id: failed, kind: gate, recipe: none, max_visits: 1 } # 실패
|
|
28
31
|
edges:
|
|
29
32
|
- { from: collect, on: outcome, map: { ok: summarize, fail: failed } }
|
|
30
|
-
- { from: summarize, on: outcome, map: { ok:
|
|
33
|
+
- { from: summarize, on: outcome, map: { ok: check, fail: failed } }
|
|
34
|
+
- { from: check, on: outcome, map: { ok: send, rework: summarize, fail: failed } }
|
|
31
35
|
- { from: send, on: outcome, map: { ok: done, fail: failed } }
|
|
32
36
|
`,
|
|
33
37
|
},
|
|
@@ -60,12 +64,12 @@ edges:
|
|
|
60
64
|
},
|
|
61
65
|
{
|
|
62
66
|
id: 'wizard-research-approve-publish',
|
|
63
|
-
description: '조사해 정리하고 승인 뒤 게시 — 조사 → 표/초안 정리 → 사람 승인 → 게시',
|
|
67
|
+
description: '조사해 정리하고 승인 뒤 게시 — 조사 → 표/초안 정리 → 출처 대조(판정·되돌림) → 사람 승인 → 게시',
|
|
64
68
|
keywords: ['조사', '가격', '경쟁', '표', '정리', '승인', '올려', '게시', '노션', '초안', '블로그', '보고서'],
|
|
65
69
|
text: `graph_id: wizard-research-approve-publish
|
|
66
70
|
loop:
|
|
67
71
|
title: 조사해 정리하고 승인 뒤 게시
|
|
68
|
-
description: 조사한 내용을 표로 정리하고 사람 승인 뒤 게시한다
|
|
72
|
+
description: 조사한 내용을 표로 정리하고 출처와 대조해 사람 승인 뒤 게시한다
|
|
69
73
|
trigger:
|
|
70
74
|
events: [manual]
|
|
71
75
|
version: 1
|
|
@@ -73,14 +77,16 @@ entry_node: research
|
|
|
73
77
|
terminal_nodes: [done, failed]
|
|
74
78
|
nodes:
|
|
75
79
|
- { node_id: research, kind: agent, recipe: 'cmd:research', max_visits: 1 } # 조사 | web-search | 조사 주제
|
|
76
|
-
- { node_id: tabulate, kind: agent, recipe: 'cmd:tabulate', max_visits:
|
|
80
|
+
- { node_id: tabulate, kind: agent, recipe: 'cmd:tabulate', max_visits: 2 } # 표로 정리 | llm | 결과를 마크다운 표로 정리
|
|
81
|
+
- { node_id: check, kind: judge, recipe: 'cmd:check', max_visits: 2 } # 출처 대조 | check | 표가 조사 결과와 일치하는가
|
|
77
82
|
- { node_id: approve, kind: hitl, recipe: 'approval:approve', max_visits: 1 } # 게시 승인 | approval
|
|
78
83
|
- { node_id: publish, kind: agent, recipe: 'cmd:publish', max_visits: 1 } # 게시 | custom | 게시처에 올리기
|
|
79
84
|
- { node_id: done, kind: gate, recipe: none, max_visits: 1 } # 완료
|
|
80
85
|
- { node_id: failed, kind: gate, recipe: none, max_visits: 1 } # 실패
|
|
81
86
|
edges:
|
|
82
87
|
- { from: research, on: outcome, map: { ok: tabulate, fail: failed } }
|
|
83
|
-
- { from: tabulate, on: outcome, map: { ok:
|
|
88
|
+
- { from: tabulate, on: outcome, map: { ok: check, fail: failed } }
|
|
89
|
+
- { from: check, on: outcome, map: { ok: approve, rework: tabulate, fail: failed } }
|
|
84
90
|
- { from: approve, on: outcome, map: { ok: publish, fail: failed } }
|
|
85
91
|
- { from: publish, on: outcome, map: { ok: done, fail: failed } }
|
|
86
92
|
`,
|
|
@@ -52,6 +52,8 @@ export interface GraphWizardResult {
|
|
|
52
52
|
recipes?: string;
|
|
53
53
|
/** 실행 가능성 증명: 그 recipes 로 `graph run --dry-run` 을 걸어 본 결과. 하니스만. */
|
|
54
54
|
dryRun?: { status: string; path: string[] };
|
|
55
|
+
/** 검증을 통과했지만 곁들인 경고(생성 실패 아님) — 예: «smell: linear-chain» 과 칸 내리기·유닛 끼우기 제안. */
|
|
56
|
+
warnings?: string[];
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
export interface GraphWizardDeps {
|
|
@@ -303,6 +305,74 @@ export function validateWizardYaml(kind: GraphKind, yaml: string): string[] {
|
|
|
303
305
|
return issues;
|
|
304
306
|
}
|
|
305
307
|
|
|
308
|
+
/** 그래프 값어치 신호(RFC SW-GRAPH-SMELL · G1~G7) — 기계로 셀 수 있는 넷만 센다. */
|
|
309
|
+
export interface GraphSignals {
|
|
310
|
+
/** G1 검증 노드 = kind judge|gate 비종단 노드의 결과 map 이 둘 이상으로 갈림 · G2 되돌림 루프 = 뒤로 가는 간선(이미 지난 노드로) ⊕ 그 노드 max_visits ≥ 2 · G3 승인 지점 = kind hitl 노드 · G4 갈래 = 비종단 노드의 결과 map 이 종단 아닌 서로 다른 둘 이상으로 갈림 */
|
|
311
|
+
counted: { G1: number; G2: number; G3: number; G4: number };
|
|
312
|
+
/** 못 셈 표시 — G5(재개)·G6(병렬)·G7(해상도). 0 으로 지어내지 않는다. */
|
|
313
|
+
notCounted: readonly ['G5', 'G6', 'G7'];
|
|
314
|
+
/** counted 넷의 합 — 둘 이상이면 그래프 값어치가 있다. */
|
|
315
|
+
total: number;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const GRAPH_SIGNALS_NOT_COUNTED = ['G5', 'G6', 'G7'] as const;
|
|
319
|
+
|
|
320
|
+
/** 순수 함수 — YAML 텍스트만 받아 센다(LLM·파일·실행 0). 못 세는 신호는 0 으로 지어내지 않고 «못 셈»으로 둔다. */
|
|
321
|
+
export function graphSignals(yaml: string): GraphSignals {
|
|
322
|
+
const counted = { G1: 0, G2: 0, G3: 0, G4: 0 };
|
|
323
|
+
const none = (): GraphSignals => ({ counted, notCounted: GRAPH_SIGNALS_NOT_COUNTED, total: 0 });
|
|
324
|
+
let doc: {
|
|
325
|
+
entry_node?: unknown; terminal_nodes?: unknown;
|
|
326
|
+
nodes?: Array<{ node_id?: unknown; kind?: unknown; max_visits?: unknown }>;
|
|
327
|
+
edges?: Array<{ from?: unknown; to?: unknown; map?: unknown }>;
|
|
328
|
+
} | null;
|
|
329
|
+
try { doc = parseYaml(yaml) as typeof doc; } catch { return none(); }
|
|
330
|
+
if (!doc) return none();
|
|
331
|
+
const nodes = (Array.isArray(doc.nodes) ? doc.nodes : []).filter((n) => n != null && typeof n.node_id === 'string');
|
|
332
|
+
const ids = new Set(nodes.map((n) => n.node_id as string));
|
|
333
|
+
const kindOf = new Map(nodes.map((n) => [n.node_id as string, typeof n.kind === 'string' ? n.kind : '']));
|
|
334
|
+
const visitsOf = new Map(nodes.map((n) => [n.node_id as string, Number(n.max_visits) || 0]));
|
|
335
|
+
const terminals = new Set((Array.isArray(doc.terminal_nodes) ? doc.terminal_nodes : []).map((t) => String(t)));
|
|
336
|
+
const targetsOf = new Map<string, Set<string>>();
|
|
337
|
+
for (const edge of Array.isArray(doc.edges) ? doc.edges : []) {
|
|
338
|
+
if (edge == null || typeof edge.from !== 'string' || !ids.has(edge.from)) continue;
|
|
339
|
+
const tos = typeof edge.to === 'string' ? [edge.to]
|
|
340
|
+
: edge.map && typeof edge.map === 'object'
|
|
341
|
+
? Object.values(edge.map as Record<string, unknown>).filter((to): to is string => typeof to === 'string') : [];
|
|
342
|
+
const targets = targetsOf.get(edge.from) ?? new Set<string>();
|
|
343
|
+
for (const to of tos) if (ids.has(to)) targets.add(to);
|
|
344
|
+
if (targets.size > 0) targetsOf.set(edge.from, targets);
|
|
345
|
+
}
|
|
346
|
+
const entry = typeof doc.entry_node === 'string' && ids.has(doc.entry_node) ? doc.entry_node : undefined;
|
|
347
|
+
/** entry 에서 avoid 노드를 거치지 않고 닿는 노드들 = «이미 지난 노드»(avoid = 되돌림 간선의 출발 노드). entry 자신도 지난 노드로 센다. */
|
|
348
|
+
const passedBefore = (avoid?: string): ReadonlySet<string> => {
|
|
349
|
+
if (!entry) return new Set<string>();
|
|
350
|
+
const visited = new Set<string>([entry]);
|
|
351
|
+
const queue = [entry];
|
|
352
|
+
while (queue.length > 0) {
|
|
353
|
+
const current = queue.shift()!;
|
|
354
|
+
if (current === avoid) continue;
|
|
355
|
+
for (const to of targetsOf.get(current) ?? []) if (!visited.has(to)) { visited.add(to); queue.push(to); }
|
|
356
|
+
}
|
|
357
|
+
return visited;
|
|
358
|
+
};
|
|
359
|
+
for (const node of nodes) {
|
|
360
|
+
const id = node.node_id as string;
|
|
361
|
+
const targets = targetsOf.get(id) ?? new Set<string>();
|
|
362
|
+
if (kindOf.get(id) === 'hitl') counted.G3 += 1;
|
|
363
|
+
if (terminals.has(id)) continue;
|
|
364
|
+
if ((kindOf.get(id) === 'judge' || kindOf.get(id) === 'gate') && targets.size >= 2) counted.G1 += 1;
|
|
365
|
+
if ([...targets].filter((to) => !terminals.has(to)).length >= 2) counted.G4 += 1;
|
|
366
|
+
}
|
|
367
|
+
for (const [from, targets] of targetsOf) {
|
|
368
|
+
for (const to of targets) {
|
|
369
|
+
if (terminals.has(to) || (visitsOf.get(to) ?? 0) < 2) continue;
|
|
370
|
+
if (to === from ? passedBefore().has(from) : passedBefore(from).has(to)) counted.G2 += 1;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return { counted, notCounted: GRAPH_SIGNALS_NOT_COUNTED, total: counted.G1 + counted.G2 + counted.G3 + counted.G4 };
|
|
374
|
+
}
|
|
375
|
+
|
|
306
376
|
/** 노드 줄 끝 주석 `# 이름 | 단계 | 인자` 를 읽는다. 흐름식 노드 줄에서만. */
|
|
307
377
|
export function parseNodeAnnotations(yaml: string): Record<string, WizardNodeStep> {
|
|
308
378
|
const out: Record<string, WizardNodeStep> = {};
|
|
@@ -562,11 +632,17 @@ export async function generateGraphFromPrompt(req: GraphWizardRequest, deps: Gra
|
|
|
562
632
|
}
|
|
563
633
|
attempts = Math.min(attempts, maxAttempts);
|
|
564
634
|
const ok = issues.length === 0;
|
|
635
|
+
// «선형 사슬» 냄새(SW-GRAPH-SMELL) — 경고만, 생성 실패가 아니다. 값어치 신호가 둘 이하면 칸 내리기·유닛 끼우기를 제안한다.
|
|
636
|
+
const warnings: string[] = [];
|
|
637
|
+
if (kind === 'harness' && ok) {
|
|
638
|
+
const signals = graphSignals(yaml);
|
|
639
|
+
if (signals.total <= 1) warnings.push(`smell: linear-chain — G 신호 ${signals.total} · 칸 내리기(스킬 사슬) 또는 유닛 끼우기(unit-verify = G1·G2 · 승인 노드 = G3) 제안`);
|
|
640
|
+
}
|
|
565
641
|
const counted = summarizeChange(kind, editing ? req.currentYaml : undefined, yaml);
|
|
566
642
|
const summary = llmSummary ? `${llmSummary} (${counted})` : counted;
|
|
567
643
|
debug.log('graph.wizard', ok ? 'validated' : 'failed', { id, kind, base, attempts, editing, issues: issues.slice(0, 10), summary, dryRun: dryRun?.status, ms: Date.now() - startedAt });
|
|
568
644
|
const labels = Object.fromEntries(Object.entries(steps).filter(([, v]) => v.label).map(([k, v]) => [k, v.label!]));
|
|
569
|
-
return { ok, id, yaml, ...(base ? { base } : {}), ...(base && baseReason ? { baseReason } : {}), issues, attempts, summary, labels, steps, ...(recipes ? { recipes } : {}), ...(dryRun ? { dryRun } : {}) };
|
|
645
|
+
return { ok, id, yaml, ...(base ? { base } : {}), ...(base && baseReason ? { baseReason } : {}), issues, attempts, summary, labels, steps, ...(recipes ? { recipes } : {}), ...(dryRun ? { dryRun } : {}), ...(warnings.length ? { warnings } : {}) };
|
|
570
646
|
}
|
|
571
647
|
|
|
572
648
|
/** v1 이름 유지 — 생성 YAML 의 노드 주석(단계·인자)으로 실제 recipes.yaml 을 낸다(v1 의 «미구현» 자리표시를 대체). */
|