elanous 0.2.34 → 0.2.35
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/{L04y73dlwKeTW-GetaTF3 → T1mBQqmkwKDLGtyisDCOd}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{5014f970-22bef0b5210f7d60.js → 0368f25d-ad84af92fefee68f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +1 -0
- package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +32 -0
- package/apps/pwa/out/_next/static/chunks/{2124.ed5bf480cea7f85b.js → 1530.0e612369b6fd7ea0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +41 -0
- package/apps/pwa/out/_next/static/chunks/1674.2a65fd5668e1cc5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +24 -0
- package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +74 -0
- package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1993-c0007b7ad0c0ef41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8368.014caa8de2f5af34.js → 2018.a741297bce759490.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +148 -0
- package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2625.5d870aa999f33db1.js +34 -0
- package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +77 -0
- package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +62 -0
- package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +90 -0
- package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1644.35dd25240ae24e4b.js → 3408.21d1570602f4b6c0.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3121.779fafe3e44612c5.js → 3589.5444a06bcf6e775c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/359-2250789a2d3499e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9131-fce7256c4180c4a5.js → 3677-4215f826f6aa68e5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +79 -0
- package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +122 -0
- package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4346.8601952e73bf5803.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6053-d893974de92e6198.js → 4441-875b97f5821ed58a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +286 -0
- package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{1895-60ed0e341c658bd4.js → 4705-194e64e47ca0fbdb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5576-82d3703636d5c4b9.js → 4718-17d2af636796a330.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7431.3051e9b8a4372d2d.js → 5101.e32282718f99ed6f.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{2987-30cf9c2eb7b8bdd5.js → 5483-dd5d7697f6bdb106.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +94 -0
- package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/56d372f2.681ab4ec4c998fae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7386.5d8e7e42e818dfb6.js → 5852.db1f7449f4f74612.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +1 -0
- package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3110.0a7065f25c841818.js → 6236.e18cced2d5ba6afb.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +8 -0
- package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +93 -0
- package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6b89c8bd.24df687a820500b3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +149 -0
- package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{7005.1623d2aea78eb85f.js → 7385.50a308a477bdfc21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6086.913a69e6fa3e4a8b.js → 7644.17e37ba6f1d4226e.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8219.baa715d1deee80e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7193-cf355cca5ba6c5d5.js → 8437-8d5492094b166cfa.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2554.3b5e200f5f5cfcd6.js → 8444.4c26b379ca2b9596.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +226 -0
- package/apps/pwa/out/_next/static/chunks/8557-e7898769499cd56f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +64 -0
- package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2547.dd27303624cb4da8.js → 8637.47f5b35afcae5e6b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9550.d5f76828f972058c.js → 9214.63a510d39178e997.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +115 -0
- package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8548.036c344973968847.js → 938.66ad1ef0e5abae1c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3414.172ad4ca2d9b08e8.js → 9512.5a76207a730e0491.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/967-2aa39b2d04b4c690.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6740.43de73fc427c9fe2.js → 9952.5daf5f94063a2b3e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-406b47745b53f269.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-d7bff0f0905a662c.js → page-26e6c18a1a750cf6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-47c5d4db587266bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-df3445b207ceceab.js → page-dc0a532f5b9196ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9a8cb466.cc9fc9e6e46038aa.js → b2f6ba4d.fb56fdbbf798df33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/b6705a4f-b9a9047de9eecfd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4fa33ebd.21c1bd5b294496e6.js → cbe38a15.5a032598e39970c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{abb4b98b.163e0f56e84ae35d.js → d06f9160.a3d9062f95de8ad7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9b48c871.204d5d13e7809bd0.js → da5176c6.6773b6c5994e1d99.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +1 -0
- 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/bin/elanous.mjs +3 -2
- package/catalog/providers/opengateway.yaml +25 -0
- package/docs/goal-context/01-goal-authoring-checks.md +8 -0
- package/docs/goal-context/03-verification-menu.md +24 -0
- package/docs/goal-context/04-mock-scenarios.md +100 -0
- package/docs/goal-context/README.md +4 -0
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/default-loop.yaml +57 -3
- package/graphs/document-loop.yaml +51 -3
- package/graphs/implement-loop.yaml +53 -4
- package/graphs/journey/ta-journey.yaml +11 -1
- package/graphs/journey/units/author-dialogue/author-dialogue.yaml +42 -0
- package/graphs/journey/units/author-dialogue/observe.ts +96 -0
- package/graphs/journey/units/author-dialogue/recipes.yaml +10 -0
- package/graphs/operate-loop.yaml +51 -3
- package/graphs/research-loop.yaml +137 -3
- package/graphs/test-diet/test-diet.yaml +3 -0
- package/package.json +1 -1
- package/scripts/coord-role-pack.ts +19 -6
- package/scripts/hq/standby-snapshot.ts +17 -12
- package/scripts/release-loop/docs-land-node.ts +21 -5
- package/scripts/release-loop/gate-baseline-receipt.ts +155 -0
- package/scripts/release-loop/gate-node.ts +181 -22
- package/scripts/release-loop/gate-partial.ts +65 -11
- package/scripts/release-loop/gate-radius-live.ts +177 -0
- package/scripts/release-loop/gate-radius-shadow.ts +1 -1
- package/scripts/release-loop/node-verdict.ts +12 -1
- package/scripts/release-loop/npm-publish-node.ts +66 -12
- package/scripts/release-loop/probe-pod-host-dirs-live.ts +5 -0
- package/scripts/release-loop/pwa-remote.ts +166 -0
- package/scripts/release-loop/pwa-scenarios-node.ts +19 -3
- package/scripts/release-loop/resume-release.ts +41 -4
- package/scripts/release-loop/unattended-release.ts +2 -0
- package/scripts/test-diet/lib.ts +6 -1
- package/scripts/test-diet/node.ts +194 -13
- package/scripts/ux-sim.ts +39 -0
- package/src/agent/self-ambient-cache.ts +8 -0
- package/src/agent-substrate/acp-reviewer.ts +39 -4
- package/src/agent-substrate/pr-reviewer.ts +17 -0
- package/src/agent-substrate/self-review-cli.ts +62 -13
- package/src/autopilot/pr-manager.ts +215 -36
- package/src/budget/run-usage-rollup.ts +1 -0
- package/src/chat/index.ts +0 -2
- package/src/chat/pickers/modal-runtime.ts +10 -27
- package/src/cli/agent-cli.ts +13 -2
- package/src/cli/doctor-cli.ts +4 -1
- package/src/cli/light-entry-route.ts +46 -6
- package/src/cli/pod-cli.ts +77 -3
- package/src/cli/pr-cli.ts +5 -2
- package/src/cli/pr-land-pod-shadow-job.ts +3 -0
- package/src/cli/release-checklist-read-cli.ts +79 -0
- package/src/cli/release-cli.ts +72 -102
- package/src/cli/remote-resolve.ts +1 -1
- package/src/cli/root-tui-entry.ts +48 -0
- package/src/cli/sim-cli.ts +22 -0
- package/src/cli/tasks-cli.ts +540 -31
- package/src/cli/usage-cli.ts +13 -11
- package/src/clipboard/history.ts +179 -13
- package/src/config.ts +8 -1
- package/src/dashboard/chat/acp-chat.ts +8 -0
- package/src/dashboard/index.ts +78 -10
- package/src/dashboard/render/theme-resolver.ts +33 -4
- package/src/domains/schedule-registry.ts +16 -2
- package/src/git-fs/idle-refresh.ts +76 -0
- package/src/git-fs/index.ts +18 -7
- package/src/git-fs/worktree.ts +42 -4
- package/src/graph-kinds/registry.ts +2 -2
- package/src/graph-runner/runner.ts +153 -10
- package/src/grok/credential.ts +10 -3
- package/src/harness/harness-cli-command.ts +112 -28
- package/src/harness/harness-incidents.ts +33 -0
- package/src/harness/launch-utterance-ledger.ts +208 -0
- package/src/impl-discipline/detector.ts +16 -4
- package/src/impl-discipline/system-prompt.ts +11 -0
- package/src/impl-discipline/types.ts +2 -2
- package/src/index.ts +496 -143
- package/src/intake-plane/already-built-hint.ts +4 -2
- package/src/intake-plane/requirement-funnel.ts +29 -1
- package/src/light-cli.ts +27 -11
- package/src/llm/opengateway-models.ts +11 -0
- package/src/llm/provider-credentials.ts +14 -0
- package/src/llm/provider-detect.ts +8 -1
- package/src/llm/runtime-model-family.ts +2 -1
- package/src/llm.ts +213 -56
- package/src/model-tier/llm-tier-map.ts +15 -1
- package/src/native-tool-catalog.ts +15 -1
- package/src/nexus/api/loop-resources.ts +13 -1
- package/src/oauth/codex.ts +7 -0
- package/src/ops/overview-collect.ts +18 -3
- package/src/ops/overview.ts +20 -7
- package/src/pr-tool/census.ts +261 -27
- package/src/pty-shell/pty-manifest.ts +7 -0
- package/src/release-loop/checklist.ts +24 -0
- package/src/release-loop/landed-but-yellow.ts +129 -5
- package/src/release-loop/light-rc.ts +1 -0
- package/src/release-loop/rehearsal.ts +2 -2
- package/src/repl/index.ts +174 -17
- package/src/repl/ops-msg-bridge.ts +32 -0
- package/src/self-dev/ask-launch-flow.ts +44 -5
- package/src/self-dev/cli-help-text.ts +17 -0
- package/src/self-dev/dev-cli.ts +11 -9
- package/src/self-dev/draft-residue.ts +37 -1
- package/src/self-dev/draft-sweep.ts +137 -11
- package/src/self-dev/draft-triage-rules.ts +107 -0
- package/src/self-dev/fleet-reduce.ts +3 -2
- package/src/self-dev/graph-front-nodes.ts +112 -0
- package/src/self-dev/graph-journey-nodes.ts +1 -0
- package/src/self-dev/harness-run-cli.ts +3 -15
- package/src/self-dev/orchestrate-cli-text.ts +10 -0
- package/src/self-dev/review-provider-fallback.ts +35 -1
- package/src/self-dev/self-orchestrate-runtime.ts +1 -11
- package/src/self-dev/shard-aggregate.ts +42 -5
- package/src/self-dev/shard-run.ts +8 -1
- package/src/self-implement/blocked-draft-policy.ts +20 -0
- package/src/self-implement/classify-shadow.ts +3 -7
- package/src/self-implement/doctor-visit.ts +18 -1
- package/src/self-implement/gate-baseline.ts +56 -17
- package/src/self-implement/gate-cli.ts +1 -0
- package/src/self-implement/gate-remote.ts +31 -20
- package/src/self-implement/goal-author-decompose.ts +275 -47
- package/src/self-implement/goal-author.ts +250 -0
- package/src/self-implement/goal-elaborate.ts +295 -0
- package/src/self-implement/goal-refine.ts +270 -0
- package/src/self-implement/graph-overlay-yaml.ts +14 -3
- package/src/self-implement/orchestrator.ts +152 -45
- package/src/self-implement/rework-budget-judgment.ts +5 -8
- package/src/self-implement/run-stop.ts +10 -1
- package/src/self-implement/running-runs.ts +29 -1
- package/src/self-implement/scope-adjacency.ts +113 -0
- package/src/self-implement/seams.ts +127 -24
- package/src/self-implement/stop-autoheal.ts +87 -1
- package/src/session-runtime/index.ts +1 -1
- package/src/setup/llm-env-detect.ts +5 -0
- package/src/skills/tools/agent.ts +140 -14
- package/src/skills/tools/msg-ask.ts +100 -0
- package/src/skills/tools/ops-ledger.ts +2 -1
- package/src/steward/hold-hygiene.ts +24 -6
- package/src/steward/triage-budget.ts +194 -0
- package/src/steward/triage.ts +20 -3
- package/src/task-agent/actions.ts +69 -1
- package/src/task-agent/green-sweep.ts +4 -0
- package/src/task-agent/live-moves.ts +160 -4
- package/src/task-agent/parent-host.ts +123 -3
- package/src/task-agent/shadow.ts +7 -1
- package/src/task-agent/task-hand.ts +54 -41
- package/src/task-agent/tier-split.ts +127 -0
- package/src/task-agent/work-order-build.ts +2 -0
- package/src/task-agent/work-order.ts +3 -2
- package/src/task-orchestrator/generator-prompt.ts +60 -11
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +33 -0
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +45 -7
- package/src/task-orchestrator/surfaces/pod-image-source.ts +89 -1
- package/src/task-orchestrator/surfaces/pod-lease.ts +17 -8
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +8 -2
- package/src/task-orchestrator/surfaces/pod-provenance.ts +76 -0
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +18 -0
- package/src/task-orchestrator/surfaces/pod-warm-provenance.ts +81 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +6 -2
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +3 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +37 -5
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +368 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +23 -0
- package/src/tool-runtime/ops-ledger-runtimes.ts +10 -2
- package/src/tui/README.md +2 -0
- package/src/tui/chat/background-job-status.ts +54 -0
- package/src/tui/chat/log-view.ts +80 -0
- package/src/tui/entry.ts +406 -0
- package/src/tui/input/composer.ts +144 -0
- package/src/tui/seat-mail-boot.ts +59 -0
- package/src/tui/slash/compact.ts +100 -0
- package/src/tui/slash/new-session.ts +35 -0
- package/src/tui-client/local-chat-turn.ts +239 -0
- package/src/tui-client/local-harness-command.ts +63 -0
- package/src/tui-client/local-provider-command.ts +184 -0
- package/src/user-config.ts +4 -2
- package/src/ux-sim/event-monkey.ts +378 -0
- package/src/ux-sim/fake-bin.ts +42 -7
- package/src/ux-sim/fault-nextmove-dispatch.ts +266 -0
- package/src/ux-sim/fault-pod-codex-503.ts +211 -0
- package/src/ux-sim/fault-scope-escape-lift.ts +189 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1162.c69abda138c7ce43.js +0 -79
- package/apps/pwa/out/_next/static/chunks/1449-1fede62af84c3e43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1598-13fc845935e816df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1639.62c5e16fa20d242f.js +0 -94
- package/apps/pwa/out/_next/static/chunks/1661-adb8b2babc9aaf8c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2147.107c0fc6146fb15a.js +0 -23
- package/apps/pwa/out/_next/static/chunks/2202-ef1c9b000c3074fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2247.1e326638ee43e672.js +0 -115
- package/apps/pwa/out/_next/static/chunks/2268.56e1e3d58180abae.js +0 -8
- package/apps/pwa/out/_next/static/chunks/2469-606819c812414021.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2593.e53c1620c0bb3599.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2690.e96671114222676a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2898-79ee1a5cc0cd9d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/298.26099a9f65e5c8ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2992.51fc2a7295bd8139.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3001-5a52788c813b6113.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3014-291245fba9df7b5b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3068.ee78201f0251a18d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3083.cd399a17e9365f78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3103.84470c8445962f04.js +0 -34
- package/apps/pwa/out/_next/static/chunks/3117-e1c474683fd1f824.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3251-2c17bd5a91f8de2e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/326.3fb32322bb2ddf6d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3411-f87314c6c66ebc73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3421-7ff7fbaa156985a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3498.cf2a15a8a9e5b926.js +0 -226
- package/apps/pwa/out/_next/static/chunks/381.f55dc63ea87735e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3819.163d4777f0a597f6.js +0 -32
- package/apps/pwa/out/_next/static/chunks/3837.722e053fb9f321ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4129-ab0a6189f1778633.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4361.93d14ba594b3a035.js +0 -104
- package/apps/pwa/out/_next/static/chunks/4364.7a7eb19cceb67d2f.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4406.d71f506f7c3ee9d3.js +0 -149
- package/apps/pwa/out/_next/static/chunks/4461.2ad1514180ae7e29.js +0 -148
- package/apps/pwa/out/_next/static/chunks/4479.bd309cbe0e3842ee.js +0 -64
- package/apps/pwa/out/_next/static/chunks/4522-05966f777d66d13b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4543-9a3e9812e845de65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4557.f8f1d84a0d90d590.js +0 -74
- package/apps/pwa/out/_next/static/chunks/4589-79b11542179ef58c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4633-9f8f9f88ffbb8f5c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4831.23af4d10c0697b5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4983.5951b85724f61f2a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5028-f0722e6114c313e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5249.96a848656b582a93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5311.16e0ffbfa5468b80.js +0 -62
- package/apps/pwa/out/_next/static/chunks/5446.edf141cff9ef647d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5489.ec2dd1b5272a19dc.js +0 -24
- package/apps/pwa/out/_next/static/chunks/559.df87695df637920c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5618-5c88f2f70178c4ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5764.05487bf0b73ddb6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5930.e678826c3f831aab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5dfb57f7.f2e3305326202474.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6200-cefd6f33f59d765e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6314-edeba650ae686ac9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6316-bc9a9b74d7842d79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6476-13b9779b73528008.js +0 -1
- package/apps/pwa/out/_next/static/chunks/666-04db3ac4c8e3c66f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/670.682e55106ef915a6.js +0 -77
- package/apps/pwa/out/_next/static/chunks/6816.b7ba9d0a1a4a46a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/690-fbbcc777f189af37.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6989.0d3d4020e5cb3df6.js +0 -90
- package/apps/pwa/out/_next/static/chunks/7217-d17393fe7af78fa6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7232.24edbd938a69c8d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7295cd1e.b4b557908888eeee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7323.adb02de21991883c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7448.478958ffbbba0ac0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7537.ba2208c5c656b8a3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7562.948255cbc3e79d33.js +0 -41
- package/apps/pwa/out/_next/static/chunks/7596-962ae6db6cfc9c83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7671.cff51beac212a2ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7883.3916734ee16d3e58.js +0 -27
- package/apps/pwa/out/_next/static/chunks/7952.b192c31b6a0e00cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/801.5b24e81eded22d1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8176.8c9a8613f5a01ac1.js +0 -36
- package/apps/pwa/out/_next/static/chunks/819e78a6.fa3dd7f7da19bf61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8220-78978450c3183d8d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8221-eb60b5bc692a3361.js +0 -1
- package/apps/pwa/out/_next/static/chunks/826.e9a26064f474949e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8459-a351487a4480f584.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8482-75823d513a4ce151.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8488.8da515248cda3f3a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8584.388a1779d9b203af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8637.a55e098e61ffefb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8817.61bb7ffbba502ecb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9207-8e6a12db30373f32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9263-00ae932c62696a86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9318.caafc3a130e8769c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/932.da3fb55951882562.js +0 -93
- package/apps/pwa/out/_next/static/chunks/9433.002f8426ac75b6b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9464.dd631a5a14b1d660.js +0 -24
- package/apps/pwa/out/_next/static/chunks/9739.8d5f36ac67a3b236.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9743.beeabb9d5f86abe4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9843-a23d3f32dc43f715.js +0 -1
- package/apps/pwa/out/_next/static/chunks/994-28211077c7c587d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-3b4c947f174e33d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-369d5ce99483f859.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-63c0ed0eb26e5fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-62802c6003bc85cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-ecb41560d4e77be9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7b65ad8130c93e95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8755477c15a9060c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5725447dd972c471.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-b979de0591a69375.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-6d0796bf4633751a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-59c31dc0394e67b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-12dfc95f6cc5c718.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-25788244e66aa4dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-a2555dc705466322.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-4fb31eb476dcbad3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-3b4c947f174e33d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-578729adbeaa40cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a1d20fffbec35205.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-4a558f94516b8004.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-9e3834705b86145e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-cb1c5fe746ea4014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-9ee320507356582a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66fd183b74835886.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-3272a064a01a9927.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-f281b7a7f6ef2529.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-d45a65f8b7124fcd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-e9c1fe684dd19a66.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-b8322932fc6e34de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-82c66d9af2290575.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-63c6752009c97b05.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-bb33d42ebc358aec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-7e4c9379197d7214.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-38bf1e9a7001732b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2e5b752012389d56.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-398c0faa173203f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-002f53a965b6445c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7f75997f61a26269.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-3b4c947f174e33d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-05631753294f23fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-edd700fd2cc3d7f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-105f83ecb4594365.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-5823e87d2017f19c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-b51ca09566c03de7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-8aacd5ded5dbac60.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bae3365a0b3c6a8e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ed52b48ff1aa7fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-b6f8cad35fe00946.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-6d25ceda500d2457.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-b6401a5f2c962fe8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ebf6cf5e.0d2f887b040e80fb.js +0 -122
- package/apps/pwa/out/_next/static/chunks/f09ec7d7-d29abe23edcbb193.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ffdc3a04.b9c23c1b28dd4a96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-9322cb8a56ac081c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-2fbab294e49a1561.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-f36729f56621a8f3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-2a62e450b47c6114.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-5e17f7b445f34d43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-b4263e2190ea7adf.js +0 -1
- /package/apps/pwa/out/_next/static/{L04y73dlwKeTW-GetaTF3 → T1mBQqmkwKDLGtyisDCOd}/_ssgManifest.js +0 -0
|
@@ -18,6 +18,8 @@ import { GROUNDING_TOKEN_ENV, GROUNDING_URL_ENV, mintGroundingToken, revokeGroun
|
|
|
18
18
|
import { POD_CREDENTIAL_GROK_PATH, POD_CREDENTIAL_GITHUB_PATH, POD_GITHUB_CREDENTIAL_TOKEN_ENV, POD_GITHUB_CREDENTIAL_URL_ENV, installationRepositories } from '../../nexus/api/pod-credential-api.js';
|
|
19
19
|
import { coalescedInstallationCredential } from '../../auth/github-app-token.js';
|
|
20
20
|
import { POD_CREDENTIAL_TOKEN_ENV, POD_CREDENTIAL_URL_ENV } from '../../grok/credential.js';
|
|
21
|
+
import { isVerifiedOpenGatewayModel } from '../../llm/opengateway-models.js';
|
|
22
|
+
import { configuredOpenGatewayKeys } from '../../llm/provider-credentials.js';
|
|
21
23
|
import { collectPodLedgers, createPodLedgerFollower } from './pod-ledger-collect.js';
|
|
22
24
|
import { collectPodArtifacts, podNoResultDiagnostic } from './pod-artifact-return.js';
|
|
23
25
|
import { readPodRepairArtifact } from './pod-repair-commit.js';
|
|
@@ -29,6 +31,7 @@ import { finishPodFragment, writePodFragment } from '../../harness/self-send-tar
|
|
|
29
31
|
import { mintRunId, normalizeRunId } from '../../harness/harness-space.js';
|
|
30
32
|
import { TA_LAND_ENV } from '../../task-agent/task-hand.js';
|
|
31
33
|
import { appendRunLedgerEntry, loadRunLedger, runLedgerDir, runLedgerPath } from '../../self-implement/run-ledger.js';
|
|
34
|
+
import { runSuffixKey } from '../../self-implement/salvage-branches.js';
|
|
32
35
|
import type { GoalExecutionRecord } from '../../self-implement/orchestrator.js';
|
|
33
36
|
import { hostRepositoryUnavailable, runHostRegate, type HostRegateResult } from '../../self-implement/host-regate.js';
|
|
34
37
|
import { resolvePodHostAcpReview, runPodHostAcpReview, type PodHostAcpReviewOutcome } from './pod-host-acp-review.js';
|
|
@@ -68,7 +71,7 @@ import { isAbsolute, join, normalize, relative, resolve, sep } from 'node:path';
|
|
|
68
71
|
import { findGitDir } from '../../git-fs/locate.js';
|
|
69
72
|
import { hostLeaseBaseDir } from '../../pod-lease/host-lease.js';
|
|
70
73
|
import { acquireLockAsync } from '../../storage/file-lock.js';
|
|
71
|
-
import { debug } from '../../debug/log.js';
|
|
74
|
+
import { debug, redactSecretText } from '../../debug/log.js';
|
|
72
75
|
import { enterJourneyNode, exitJourneyNode, passJourneyNode } from '../../self-dev/graph-journey-nodes.js';
|
|
73
76
|
import { routeJourneyEdge } from '../../self-dev/graph-journey-route.js';
|
|
74
77
|
import { selectLiveDetail } from '../../live/detail-switch.js';
|
|
@@ -103,6 +106,8 @@ import type { WarmWorkerCommand } from './pod-warm-worker-protocol.js';
|
|
|
103
106
|
import type { WarmPodTransportResult } from './pod-warm-worker-pod.js';
|
|
104
107
|
|
|
105
108
|
export type Kubectl = (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
|
|
109
|
+
type OpenPrByBranch = { status: 'found'; prNumber: number; prUrl: string }
|
|
110
|
+
| { status: 'not-found' | 'failed'; reason: string };
|
|
106
111
|
|
|
107
112
|
/** TC supplies the validated candidate/lease transport. UX only forwards its request command. */
|
|
108
113
|
export type WarmPodRequestHook = (request: { command: WarmWorkerCommand }) => Promise<WarmPodTransportResult> | WarmPodTransportResult;
|
|
@@ -169,6 +174,8 @@ export interface PodSpawnOptions {
|
|
|
169
174
|
authorConfig?: () => UserConfig;
|
|
170
175
|
/** Host fallback chain seam; a Pod has no access to the host config. */
|
|
171
176
|
fallbackConfig?: () => UserConfig;
|
|
177
|
+
/** Host OpenGateway key config seam; unit tests inject isolated config without bypassing production reads. */
|
|
178
|
+
openGatewayConfig?: () => UserConfig;
|
|
172
179
|
/** 원천 — 없으면 종전 `git clone --depth 50`. bundle 이면 apply 뒤 kubectl cp 로 싣는다. */
|
|
173
180
|
source?: PodSource;
|
|
174
181
|
/** Existing PR repair: pin the source head and export a commit bundle instead of opening a PR. */
|
|
@@ -205,6 +212,8 @@ export interface PodSpawnOptions {
|
|
|
205
212
|
taskAgentLiveMoves?: () => Promise<ReadonlySet<string>> | ReadonlySet<string>;
|
|
206
213
|
/** PR comment for host-regate failures that never reached runHostRegate (injectable for tests). */
|
|
207
214
|
ghComment?: (prNumber: number, body: string) => void;
|
|
215
|
+
/** Read-only host gh lookup; injected fixtures can stand in for gh without creating or closing a PR. */
|
|
216
|
+
lookupOpenPrByBranch?: (branch: string, repoUrl: string) => Promise<OpenPrByBranch> | OpenPrByBranch;
|
|
208
217
|
draftResidue?: (record: PrTerminalRecord, ctx: DraftResidueContext) => Promise<void> | void;
|
|
209
218
|
/** 🔑 Pod 필수 스킬의 키(.env)를 이 런의 Secret 으로 넘긴다 — 명시 opt-in(유료 크레딧을 쓴다). */
|
|
210
219
|
skillEnv?: boolean;
|
|
@@ -397,6 +406,11 @@ export async function podCodexCredentialWithPrerefresh(account: string, deps: {
|
|
|
397
406
|
// ⑴ 잠금을 기다리는 사이 동료 런이 갱신했으면 그대로 쓴다 — 두 번째 갱신이 회전된 토큰으로 실패하는 자리.
|
|
398
407
|
const first = reread();
|
|
399
408
|
if (!('expiringH' in first)) { observe('refresh-skipped-already-fresh', null); return first; }
|
|
409
|
+
// The lease can transfer while an account lock is queued. Recheck inside the
|
|
410
|
+
// critical section, immediately before the only refresh-token operation.
|
|
411
|
+
let stillHolder = false;
|
|
412
|
+
try { stillHolder = await deps.isLeaseHolder(); } catch { stillHolder = false; }
|
|
413
|
+
if (!stillHolder) { observe('no-lease', null, 'lease-lost-before-refresh'); return null; }
|
|
400
414
|
let refreshed: Awaited<ReturnType<typeof deps.refresh>> | null = null;
|
|
401
415
|
let failReason = 'threw';
|
|
402
416
|
try { refreshed = await deps.refresh(account); }
|
|
@@ -430,6 +444,7 @@ export async function podCodexCredentialWithPrerefresh(account: string, deps: {
|
|
|
430
444
|
|
|
431
445
|
/** 기본 임대 판정 — 기존 HQ 펜스(`ledger-cli` 역할 · fail-open 없음)를 그대로 쓴다. */
|
|
432
446
|
async function defaultHqLeaseHolder(): Promise<boolean> {
|
|
447
|
+
if (process.env.ELANOUS_REMOTE_CODEX_ACCESS_ONLY === '1') return false;
|
|
433
448
|
const { hqFenceDecision } = await import('../../hq/hq.js');
|
|
434
449
|
return hqFenceDecision('ledger-cli').run;
|
|
435
450
|
}
|
|
@@ -649,20 +664,45 @@ function githubRepositoryFromUrl(repoUrl: string): string | null {
|
|
|
649
664
|
return owner && name && ![owner, name].some((part) => part === '.' || part === '..') ? `${owner}/${name}` : null;
|
|
650
665
|
}
|
|
651
666
|
|
|
652
|
-
/** GROK-CHILD-GIT-GUARD — the Pod holds a GitHub write token and our repo cannot protect main (private free plan · API 403)
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
667
|
+
/** GROK-CHILD-GIT-GUARD — the Pod holds a GitHub write token and our repo cannot protect main (private free plan · API 403).
|
|
668
|
+
* The global hook refuses main/tags/unknown refs and most deletes. With a runId, self-impl refs use the same six-hex
|
|
669
|
+
* run suffix as planned branch names; only that suffix and this run's disposable PR scratch create/delete pass.
|
|
670
|
+
* A same-card PR reuse push is the one exception: per-command Git config binds its ref, old head and card;
|
|
671
|
+
* the hook compares the Pod card and proves fast-forward ancestry before accepting it.
|
|
672
|
+
* salvage/* and bench arm refs keep their existing broader rules. A six-hex collision remains possible, so this is
|
|
673
|
+
* an accidental overwrite guard, not a security isolation boundary; child credential isolation is a separate slice. */
|
|
657
674
|
export const POD_GIT_HOOKS_DIR = '$HOME/.elanous-git-hooks';
|
|
658
|
-
export function podGitPushGuardScript(): string {
|
|
675
|
+
export function podGitPushGuardScript(runId?: string): string {
|
|
676
|
+
const runKey = runId ? runSuffixKey(runId) : null;
|
|
677
|
+
const zeroSha = '0'.repeat(40);
|
|
659
678
|
return [
|
|
660
679
|
`mkdir -p "${POD_GIT_HOOKS_DIR}" && cat > "${POD_GIT_HOOKS_DIR}/pre-push" <<'ELANOUS_PRE_PUSH'`,
|
|
661
680
|
'#!/bin/sh',
|
|
662
681
|
'while read -r lref lsha rref rsha; do',
|
|
663
|
-
' case "$lsha" in *[!0]*) ;; *)
|
|
682
|
+
' case "$lsha" in *[!0]*) ;; *)',
|
|
683
|
+
...(runKey ? [
|
|
684
|
+
` case "$rref" in refs/heads/self-impl/pr-create-r${runKey}-*) ;; *) echo "[pod] git-guard: refused remote delete $rref" >&2; exit 1 ;; esac ;;`,
|
|
685
|
+
] : [' echo "[pod] git-guard: refused remote delete $rref" >&2; exit 1 ;;']),
|
|
686
|
+
' esac',
|
|
664
687
|
' case "$rref" in',
|
|
665
|
-
|
|
688
|
+
...(runKey ? [
|
|
689
|
+
` refs/heads/self-impl/pr-create-r${runKey}-*)`,
|
|
690
|
+
` case "$lsha:$rsha" in ${zeroSha}:*|*:${zeroSha}) ;; *) echo "[pod] git-guard: refused scratch ref update $rref" >&2; exit 1 ;; esac ;;`,
|
|
691
|
+
` refs/heads/self-impl/*-r${runKey}|refs/heads/self-impl/*-r${runKey}-early) ;;`,
|
|
692
|
+
' refs/heads/self-impl/*)',
|
|
693
|
+
' reuse_ref=$(git config --get elanous.sameCellReuseRef 2>/dev/null || true)',
|
|
694
|
+
' reuse_head=$(git config --get elanous.sameCellReuseHead 2>/dev/null || true)',
|
|
695
|
+
' reuse_card=$(git config --get elanous.sameCellReuseCard 2>/dev/null || true)',
|
|
696
|
+
' if [ -z "$reuse_ref" ]; then echo "[pod] git-guard: refused push to $rref (another run)" >&2; exit 1; fi',
|
|
697
|
+
' if [ "$rref" != "$reuse_ref" ]; then echo "[pod] git-guard: refused same-cell ref-mismatch $rref" >&2; exit 1; fi',
|
|
698
|
+
' if [ -z "${ELANOUS_TASK_CARD_ID:-}" ] || [ "$reuse_card" != "$ELANOUS_TASK_CARD_ID" ]; then echo "[pod] git-guard: refused same-cell card-mismatch $rref" >&2; exit 1; fi',
|
|
699
|
+
' if [ -z "$reuse_head" ] || [ "$rsha" != "$reuse_head" ]; then echo "[pod] git-guard: refused same-cell remote-head-mismatch $rref" >&2; exit 1; fi',
|
|
700
|
+
' if ! git merge-base --is-ancestor "$rsha" "$lsha" 2>/dev/null; then echo "[pod] git-guard: refused same-cell non-fast-forward $rref" >&2; exit 1; fi',
|
|
701
|
+
' echo "[pod] git-guard: allowed same-cell fast-forward $rref" >&2 ;;',
|
|
702
|
+
] : [
|
|
703
|
+
...(runId ? [' refs/heads/self-impl/*) echo "[pod] git-guard: refused push to $rref (invalid run id)" >&2; exit 1 ;;'] : [' refs/heads/self-impl/*) ;;']),
|
|
704
|
+
]),
|
|
705
|
+
' refs/heads/salvage/*) ;;',
|
|
666
706
|
' refs/heads/bench/*/base) echo "[pod] git-guard: refused push to shared bench base $rref" >&2; exit 1 ;;',
|
|
667
707
|
' refs/heads/bench/*/*) ;;',
|
|
668
708
|
' *) echo "[pod] git-guard: refused push to $rref (only this run\'s branches)" >&2; exit 1 ;;',
|
|
@@ -963,7 +1003,7 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
963
1003
|
}
|
|
964
1004
|
|
|
965
1005
|
/** A named child provider decides the Pod provider; paid OpenRouter requires an explicit key and model. */
|
|
966
|
-
export function podNamedChildProvider(named: string | undefined, credential: { grokSubscription: boolean; grokApiKey: boolean; openrouterKey?: boolean; anthropicKey?: boolean }, childModel?: string): { provider?: PodChildProvider; refuse?: string } {
|
|
1006
|
+
export function podNamedChildProvider(named: string | undefined, credential: { grokSubscription: boolean; grokApiKey: boolean; openrouterKey?: boolean; openGatewayKey?: boolean; anthropicKey?: boolean }, childModel?: string): { provider?: PodChildProvider; refuse?: string } {
|
|
967
1007
|
// 공백·빈 이름 = «미지정»(종전 기본 경로) — 유료 provider 로 흘러가지 않는다(codex 재리뷰 must-fix).
|
|
968
1008
|
if (!named?.trim()) return {};
|
|
969
1009
|
// ⭐ 목록은 한 곳(pod-child-providers.ts) — 발사 관문도 같은 상수·같은 문면으로 거부한다.
|
|
@@ -978,6 +1018,12 @@ export function podNamedChildProvider(named: string | undefined, credential: { g
|
|
|
978
1018
|
if (!childModel?.trim()) return { refuse: 'pod: openrouter 자식 모델 없음 — --child-llm-model 필요' };
|
|
979
1019
|
return { provider: 'openrouter' };
|
|
980
1020
|
}
|
|
1021
|
+
if (named === 'opengateway') {
|
|
1022
|
+
if (!credential.openGatewayKey) return { refuse: 'pod: Pod 에 opengateway 키 없음 — 호스트 키 캐시 또는 OPENGATEWAY_API_KEY 필요' };
|
|
1023
|
+
if (!childModel?.trim()) return { refuse: 'pod: opengateway 자식 모델 없음 — --child-llm-model 필요' };
|
|
1024
|
+
if (!isVerifiedOpenGatewayModel(childModel.trim())) return { refuse: `pod: opengateway model not verified: ${childModel.trim()}` };
|
|
1025
|
+
return { provider: 'opengateway' };
|
|
1026
|
+
}
|
|
981
1027
|
// BEDROCK-PROVIDER ② — 자격은 클러스터의 이름 있는 Secret 에서 온다. 있나·키가 갖춰졌나는 Pod 발사 «전»(apply 0)에 판정한다(checkBedrockPodSecret).
|
|
982
1028
|
if (named === 'bedrock') return { provider: 'bedrock' };
|
|
983
1029
|
// ZAI-POD-CHILD — glm 도 같은 결: 자격은 클러스터의 이름 있는 ZAI Secret · 있나·동시 상한은 Pod 발사 «전»(apply 0)에 판정한다(pod-zai-child.ts).
|
|
@@ -1012,7 +1058,8 @@ export function podChildLlmArgs(options: Pick<PodSpawnOptions, 'provider' | 'chi
|
|
|
1012
1058
|
const provider = options.provider === 'openai-codex' && !options.childProviderExplicit ? undefined : options.provider;
|
|
1013
1059
|
if (!provider) return [];
|
|
1014
1060
|
const model = options.childModel?.trim() || (provider === 'grok' ? defaultGrokModel().id : provider === 'glm' ? ZAI_CODING_DEFAULT_MODEL : undefined);
|
|
1015
|
-
if (provider === 'openrouter' && !model) throw new Error(
|
|
1061
|
+
if ((provider === 'openrouter' || provider === 'opengateway') && !model) throw new Error(`pod: ${provider} 자식 모델 없음 — --child-llm-model 필요`);
|
|
1062
|
+
if (provider === 'opengateway' && !isVerifiedOpenGatewayModel(model!)) throw new Error(`pod: opengateway model not verified: ${model}`);
|
|
1016
1063
|
// `self implement` registers no --child-llm-effort — passing it kills the Pod at argument parsing, so effort stays host-only.
|
|
1017
1064
|
return ['--child-llm-provider', provider, ...(model ? ['--child-llm-model', model] : [])];
|
|
1018
1065
|
}
|
|
@@ -1078,6 +1125,8 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
1078
1125
|
const childModelIndex = o.args.indexOf('--child-llm-model');
|
|
1079
1126
|
const openrouterModel = childProviderIndex >= 0 && o.args[childProviderIndex + 1] === 'openrouter' && childModelIndex >= 0
|
|
1080
1127
|
? o.args[childModelIndex + 1] : undefined;
|
|
1128
|
+
const openGatewayModel = childProviderIndex >= 0 && o.args[childProviderIndex + 1] === 'opengateway' && childModelIndex >= 0
|
|
1129
|
+
? o.args[childModelIndex + 1] : undefined;
|
|
1081
1130
|
// POD-ANTHROPIC-PROVIDER — anthropic 자식은 키(env · secretKeyRef)만 쓴다. codex·grok 자격 파일을 깔지 않는다.
|
|
1082
1131
|
const anthropicChild = childProviderIndex >= 0 && o.args[childProviderIndex + 1] === 'anthropic';
|
|
1083
1132
|
const goalPath = o.goalDoc ? `'${(o.goalDoc.startsWith('-') ? `./${o.goalDoc}` : o.goalDoc).replace(/'/g, `'\\''`)}'` : undefined;
|
|
@@ -1101,7 +1150,9 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
1101
1150
|
'mkdir -p "$HOME/.elanous" && install -m 600 /creds/llm-fallback-config.json "$HOME/.elanous/config.json" || exit 6',
|
|
1102
1151
|
`echo "ELANOUS_POD_FALLBACK_CHAIN source=${o.fallbackChain.source} steps=${o.fallbackChain.steps.join(',')}"`,
|
|
1103
1152
|
] : []),
|
|
1104
|
-
|
|
1153
|
+
openGatewayModel
|
|
1154
|
+
? 'mkdir -p ~/.elanous && export ELANOUS_LLM_PROVIDER=opengateway'
|
|
1155
|
+
: openrouterModel
|
|
1105
1156
|
? o.reviewerCodexAuth
|
|
1106
1157
|
? 'mkdir -p ~/.elanous ~/.codex && cp /creds/elanous-auth.json ~/.elanous/auth.json && cp /creds/codex-auth.json ~/.codex/auth.json && chmod 600 ~/.elanous/auth.json ~/.codex/auth.json && export ELANOUS_LLM_PROVIDER=openrouter'
|
|
1107
1158
|
: 'mkdir -p ~/.elanous && export ELANOUS_LLM_PROVIDER=openrouter'
|
|
@@ -1120,7 +1171,7 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
1120
1171
|
// 🔑 스킬 키(.env) — 이미지엔 없다. 이 런의 Secret 에서 각 스킬 폴더로 0600 복사(값은 로그에 안 나온다).
|
|
1121
1172
|
...(o.skillEnvs?.length ? [`for n in ${o.skillEnvs.join(' ')}; do [ -d ~/.claude/skills/$n ] && install -m 600 /creds/skillenv-$n ~/.claude/skills/$n/.env; done; echo "[pod] skill env: ${o.skillEnvs.join(',')}"`] : []),
|
|
1122
1173
|
'git config --global user.name "elanous pod child" && git config --global user.email "noreply@anthropic.com" && gh auth setup-git',
|
|
1123
|
-
podGitPushGuardScript(),
|
|
1174
|
+
podGitPushGuardScript(o.runId),
|
|
1124
1175
|
'curl -s -m 3 -o /dev/null http://host.orb.internal:31415/health && { echo "[pod] ISOLATION FAIL"; exit 3; }',
|
|
1125
1176
|
// SI-POD-STAGE-MARKERS — Pod 시계 단계 표지(`ELANOUS_POD_STAGE <stage> <start|end> <epoch초>`): 호스트가 Job 로그에서 읽어
|
|
1126
1177
|
// pod.lifecycle stages 의 clone · mirror · run 을 채운다. 실패한 단계는 끝 표지가 없다(→ missing · 지어내지 않는다).
|
|
@@ -1291,6 +1342,85 @@ function defaultGhComment(prNumber: number, body: string): void {
|
|
|
1291
1342
|
if (r.status !== 0) throw new Error((r.stderr || r.error?.message || `gh exited ${r.status}`).trim().slice(0, 200));
|
|
1292
1343
|
}
|
|
1293
1344
|
|
|
1345
|
+
function defaultLookupOpenPrByBranch(branch: string, repoUrl: string): OpenPrByBranch {
|
|
1346
|
+
const repo = /^https:\/\/github\.com\/([\w.-]+\/[\w.-]+?)(?:\.git)?\/?$/u.exec(repoUrl)?.[1];
|
|
1347
|
+
if (!repo) return { status: 'failed', reason: 'repository-unrecognized' };
|
|
1348
|
+
if (!branch || !/^[A-Za-z0-9._/-]+$/u.test(branch) || branch.includes('..')) return { status: 'failed', reason: 'branch-invalid' };
|
|
1349
|
+
const result = spawnSync('gh', ['pr', 'list', '--repo', repo, '--state', 'open', '--head', branch,
|
|
1350
|
+
'--json', 'number,url,headRefName,isCrossRepository', '--limit', '100'], { encoding: 'utf8', timeout: 10_000, maxBuffer: 1_048_576 });
|
|
1351
|
+
if (result.status !== 0) return { status: 'failed', reason: result.error ? 'gh-list-unavailable' : `gh-list-rc-${result.status ?? 'null'}` };
|
|
1352
|
+
let rows: unknown;
|
|
1353
|
+
try { rows = JSON.parse(result.stdout); } catch { return { status: 'failed', reason: 'gh-json-invalid' }; }
|
|
1354
|
+
if (!Array.isArray(rows)) return { status: 'failed', reason: 'gh-json-invalid' };
|
|
1355
|
+
if (rows.some((row) => row === null || typeof row !== 'object'
|
|
1356
|
+
|| typeof (row as { number?: unknown }).number !== 'number'
|
|
1357
|
+
|| !Number.isSafeInteger((row as { number: number }).number)
|
|
1358
|
+
|| typeof (row as { url?: unknown }).url !== 'string'
|
|
1359
|
+
|| typeof (row as { headRefName?: unknown }).headRefName !== 'string'
|
|
1360
|
+
|| typeof (row as { isCrossRepository?: unknown }).isCrossRepository !== 'boolean'))
|
|
1361
|
+
return { status: 'failed', reason: 'gh-json-invalid' };
|
|
1362
|
+
if (rows.length >= 100) return { status: 'failed', reason: 'gh-result-limit-reached' };
|
|
1363
|
+
const matching = rows.filter((row): row is { number: number; url: string; headRefName: string; isCrossRepository: false } => {
|
|
1364
|
+
if (row === null || typeof row !== 'object') return false;
|
|
1365
|
+
const candidate = row as { number?: unknown; url?: unknown; headRefName?: unknown; isCrossRepository?: unknown };
|
|
1366
|
+
return candidate.headRefName === branch && candidate.isCrossRepository === false && typeof candidate.number === 'number'
|
|
1367
|
+
&& Number.isSafeInteger(candidate.number) && candidate.number > 0 && typeof candidate.url === 'string';
|
|
1368
|
+
});
|
|
1369
|
+
if (matching.length === 0) return { status: 'not-found', reason: rows.some((row) =>
|
|
1370
|
+
(row as { headRefName: string }).headRefName === branch && (row as { isCrossRepository: boolean }).isCrossRepository)
|
|
1371
|
+
? 'cross-repository-only' : 'no-open-pr-for-branch' };
|
|
1372
|
+
if (matching.length !== 1) return { status: 'failed', reason: 'ambiguous-open-pr-for-branch' };
|
|
1373
|
+
const row = matching[0]!;
|
|
1374
|
+
if (row.url.replace(/\/$/u, '').toLowerCase() !== `https://github.com/${repo}/pull/${row.number}`.toLowerCase())
|
|
1375
|
+
return { status: 'failed', reason: 'pr-url-mismatch' };
|
|
1376
|
+
return { status: 'found', prNumber: row.number, prUrl: row.url };
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
type PodImageObservation = { podName: string | null; podUid: string | null; podCreatedAt: string | null;
|
|
1380
|
+
imageID: string | null; digest: string | null; digestUnavailable: string | null };
|
|
1381
|
+
|
|
1382
|
+
/** Read the newest Pod owned by this Job. A stale replacement must never be named as the final Pod. */
|
|
1383
|
+
function readJobPodImage(kubectl: Kubectl, namespace: string, job: string, jobUid?: string): PodImageObservation {
|
|
1384
|
+
const unavailable = (reason: string, podName: string | null = null, imageID: string | null = null,
|
|
1385
|
+
podUid: string | null = null, podCreatedAt: string | null = null): PodImageObservation =>
|
|
1386
|
+
({ podName, podUid, podCreatedAt, imageID, digest: null, digestUnavailable: reason });
|
|
1387
|
+
let result: ReturnType<Kubectl>;
|
|
1388
|
+
try { result = kubectl(['-n', namespace, 'get', 'pods', '-l', `job-name=${job}`, '-o', 'json', '--request-timeout=5s']); }
|
|
1389
|
+
catch { return unavailable('pod-status-threw'); }
|
|
1390
|
+
if (result.status !== 0) return unavailable(`pod-status-rc-${result.status ?? 'null'}`);
|
|
1391
|
+
let rows: unknown;
|
|
1392
|
+
try { rows = (JSON.parse(result.stdout) as { items?: unknown }).items; }
|
|
1393
|
+
catch { return unavailable('pod-status-invalid-json'); }
|
|
1394
|
+
if (!Array.isArray(rows)) return unavailable('pod-status-invalid-items');
|
|
1395
|
+
const owned = rows.filter((row): row is {
|
|
1396
|
+
metadata: { name: string; uid?: string; creationTimestamp?: string; ownerReferences: Array<{ kind: string; name: string; uid?: string }> };
|
|
1397
|
+
status?: { containerStatuses?: Array<{ name?: string; imageID?: string }> };
|
|
1398
|
+
} => {
|
|
1399
|
+
if (!row || typeof row !== 'object') return false;
|
|
1400
|
+
const meta = (row as { metadata?: unknown }).metadata;
|
|
1401
|
+
if (!meta || typeof meta !== 'object') return false;
|
|
1402
|
+
const m = meta as { name?: unknown; ownerReferences?: unknown };
|
|
1403
|
+
if (typeof m.name !== 'string' || !Array.isArray(m.ownerReferences)) return false;
|
|
1404
|
+
return m.ownerReferences.some((owner: unknown) => !!owner && typeof owner === 'object'
|
|
1405
|
+
&& (owner as { kind?: unknown }).kind === 'Job' && (owner as { name?: unknown }).name === job
|
|
1406
|
+
&& (!jobUid || (owner as { uid?: unknown }).uid === jobUid));
|
|
1407
|
+
}).sort((a, b) => {
|
|
1408
|
+
const at = Date.parse(a.metadata.creationTimestamp ?? '') || 0;
|
|
1409
|
+
const bt = Date.parse(b.metadata.creationTimestamp ?? '') || 0;
|
|
1410
|
+
return bt - at || b.metadata.name.localeCompare(a.metadata.name);
|
|
1411
|
+
});
|
|
1412
|
+
const pod = owned[0];
|
|
1413
|
+
if (!pod) return unavailable(rows.length ? 'no-job-owned-pod' : 'pod-not-created');
|
|
1414
|
+
const podUid = typeof pod.metadata.uid === 'string' ? pod.metadata.uid : null;
|
|
1415
|
+
const podCreatedAt = typeof pod.metadata.creationTimestamp === 'string' ? pod.metadata.creationTimestamp : null;
|
|
1416
|
+
const child = pod.status?.containerStatuses?.find((entry) => entry.name === 'child');
|
|
1417
|
+
if (!child) return unavailable('child-container-not-started', pod.metadata.name, null, podUid, podCreatedAt);
|
|
1418
|
+
const imageID = typeof child.imageID === 'string' ? child.imageID : null;
|
|
1419
|
+
if (!imageID?.trim()) return unavailable('image-id-missing', pod.metadata.name, imageID, podUid, podCreatedAt);
|
|
1420
|
+
const digest = /(?:^|[@/])(sha256:[0-9a-f]{64})$/iu.exec(imageID.trim())?.[1]?.toLowerCase() ?? null;
|
|
1421
|
+
return { podName: pod.metadata.name, podUid, podCreatedAt, imageID, digest, digestUnavailable: digest ? null : 'nonstandard-image-id' };
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1294
1424
|
export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplementJobSpawn {
|
|
1295
1425
|
if (options.repairCommitOnly && (options.source?.kind !== 'pr' || options.source.number !== options.repairCommitOnly.pr || !/^[0-9a-f]{40}$/.test(options.repairCommitOnly.baseHead))) {
|
|
1296
1426
|
throw new Error('repair commit-only requires a matching PR source and exact head');
|
|
@@ -1599,12 +1729,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1599
1729
|
const jobArgs = zaiRoute === 'openrouter' ? zaiOverflowChildArgs(args, ZAI_OPENROUTER_FALLBACK_MODEL) : args;
|
|
1600
1730
|
const grok = options.provider === 'grok';
|
|
1601
1731
|
const openrouter = options.provider === 'openrouter' || zaiRoute === 'openrouter';
|
|
1732
|
+
const openGateway = options.provider === 'opengateway';
|
|
1602
1733
|
const anthropic = options.provider === 'anthropic';
|
|
1603
1734
|
const bedrockChild = options.provider === 'bedrock';
|
|
1604
1735
|
const zaiChild = zaiRoute === 'zai';
|
|
1605
1736
|
/** API 키 자식 — codex 계정·자격 파일을 안 쓴다(키는 작업별 creds Secret 의 `env-<KEY>` · bedrock·glm 은 이름 있는 Secret 으로만). */
|
|
1606
|
-
const apiKeyChild = openrouter || anthropic || bedrockChild || zaiChild;
|
|
1607
|
-
const plannedAccount = retryAccount ?? (grok ? 'grok' : openrouter ? 'openrouter' : anthropic ? 'anthropic' : bedrockChild ? 'bedrock' : zaiChild ? 'glm' : options.accountBroker?.() ?? options.account ?? 'team');
|
|
1737
|
+
const apiKeyChild = openrouter || openGateway || anthropic || bedrockChild || zaiChild;
|
|
1738
|
+
const plannedAccount = retryAccount ?? (grok ? 'grok' : openrouter ? 'openrouter' : openGateway ? 'opengateway' : anthropic ? 'anthropic' : bedrockChild ? 'bedrock' : zaiChild ? 'glm' : options.accountBroker?.() ?? options.account ?? 'team');
|
|
1608
1739
|
debug.log('self-implement.pod', 'account', { spaceId: input.spaceId, account: plannedAccount, brokered: Boolean(options.accountBroker) });
|
|
1609
1740
|
const plannedCodexAccounts = grok || apiKeyChild || !options.accountBroker || !options.rotationAccounts ? undefined : [plannedAccount, ...options.rotationAccounts.filter((candidate) => candidate !== plannedAccount && candidate !== failedAccount)];
|
|
1610
1741
|
if (plannedCodexAccounts && !options.rotationAccounts!.includes(plannedAccount)) throw new Error(`pod: 배분 계정 ${plannedAccount} 이 회전 계획에 없다`);
|
|
@@ -1647,6 +1778,17 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1647
1778
|
}) : undefined;
|
|
1648
1779
|
const openrouterKey = openrouter ? env.OPENROUTER_API_KEY?.trim() || (options.readKeyCache ?? defaultReadKeyCache)('OPENROUTER_API_KEY')?.trim() : undefined;
|
|
1649
1780
|
if (openrouter && !openrouterKey) throw new Error('pod: Pod 에 openrouter 키 없음 — 호스트 키 캐시 또는 OPENROUTER_API_KEY 필요');
|
|
1781
|
+
const openGatewayEnvKey = openGateway ? env.OPENGATEWAY_API_KEY?.trim() : undefined;
|
|
1782
|
+
const openGatewayCachedKey = openGateway ? (options.readKeyCache ?? defaultReadKeyCache)('OPENGATEWAY_API_KEY')?.trim() : undefined;
|
|
1783
|
+
if (openGatewayEnvKey && openGatewayCachedKey && openGatewayEnvKey !== openGatewayCachedKey)
|
|
1784
|
+
throw new Error('pod: opengateway env와 키 캐시가 충돌 — Pod 발사 거부');
|
|
1785
|
+
const openGatewayHostConfig = openGateway ? (options.openGatewayConfig ?? getUserConfig)() : undefined;
|
|
1786
|
+
const openGatewayConfiguredKeys = openGatewayHostConfig ? configuredOpenGatewayKeys(openGatewayHostConfig.llm) : [];
|
|
1787
|
+
if (openGatewayConfiguredKeys.length > 1 || openGatewayConfiguredKeys.some((key) =>
|
|
1788
|
+
(openGatewayEnvKey && openGatewayEnvKey !== key) || (openGatewayCachedKey && openGatewayCachedKey !== key)))
|
|
1789
|
+
throw new Error('pod: opengateway 호스트 설정과 env/키 캐시가 충돌 — Pod 발사 거부');
|
|
1790
|
+
const openGatewayKey = openGatewayEnvKey || openGatewayCachedKey || openGatewayConfiguredKeys[0];
|
|
1791
|
+
if (openGateway && !openGatewayKey) throw new Error('pod: Pod 에 opengateway 키 없음 — 호스트 키 캐시 또는 OPENGATEWAY_API_KEY 필요');
|
|
1650
1792
|
// POD-ANTHROPIC-PROVIDER — 제품 자격 해석(`getAnthropicApiKey` · 키 캐시 → env)으로 읽는다. 못 읽으면 Pod 를 안 띄운다(fail-closed).
|
|
1651
1793
|
// ⛔ 값은 로그·오류 문면에 안 싣는다 — «있었다» 만.
|
|
1652
1794
|
const anthropicKey = anthropic
|
|
@@ -1717,7 +1859,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1717
1859
|
}
|
|
1718
1860
|
}
|
|
1719
1861
|
// Grok 자격은 전용 Secret 키 하나로만 보낸다 — passEnv 의 API 키 중복 전달은 막는다.
|
|
1720
|
-
const passKeys = [...new Set([...(options.passEnv ?? []), ...(openrouter ? ['OPENROUTER_API_KEY'] : []), ...(anthropic ? ['ANTHROPIC_API_KEY'] : [])])].filter((key) => key !== 'ELANOUS_LIVE_DETAIL_UNTIL'
|
|
1862
|
+
const passKeys = [...new Set([...(options.passEnv ?? []), ...(openrouter ? ['OPENROUTER_API_KEY'] : []), ...(openGateway ? ['OPENGATEWAY_API_KEY'] : []), ...(anthropic ? ['ANTHROPIC_API_KEY'] : [])])].filter((key) => key !== 'ELANOUS_LIVE_DETAIL_UNTIL'
|
|
1721
1863
|
&& key !== POD_GITHUB_CREDENTIAL_TOKEN_ENV && key !== POD_GITHUB_CREDENTIAL_URL_ENV
|
|
1722
1864
|
&& (!grok || !['XAI_API_KEY', 'GROK_API_KEY', 'GROK_CODE_XAI_API_KEY'].includes(key)));
|
|
1723
1865
|
const skillEnvs: Record<string, string> = options.skillEnv
|
|
@@ -1863,7 +2005,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1863
2005
|
authorChoice = { ...authorChoice, model: childModel, modelSource: 'fallback-child' };
|
|
1864
2006
|
} else debug.log('self-implement.pod', 'role-llm-armed', { job: name, role: 'planning', model: authorChoice.model, source: authorChoice.modelSource, account: roleCodexAccount });
|
|
1865
2007
|
}
|
|
1866
|
-
const hostKey = (k: string): string | undefined => k === 'OPENROUTER_API_KEY' && openrouter ? openrouterKey : k === 'ANTHROPIC_API_KEY' && anthropic ? anthropicKey : env[k] ?? (options.readKeyCache ?? defaultReadKeyCache)(k);
|
|
2008
|
+
const hostKey = (k: string): string | undefined => k === 'OPENROUTER_API_KEY' && openrouter ? openrouterKey : k === 'OPENGATEWAY_API_KEY' && openGateway ? openGatewayKey : k === 'ANTHROPIC_API_KEY' && anthropic ? anthropicKey : env[k] ?? (options.readKeyCache ?? defaultReadKeyCache)(k);
|
|
1867
2009
|
const hostFallbackNames = (options.fallbackConfig ?? getUserConfig)().llm.fallbackChain;
|
|
1868
2010
|
const normalizedFallback = normalizeFallbackChain(hostFallbackNames);
|
|
1869
2011
|
let fallbackSteps = normalizedFallback.chain;
|
|
@@ -1932,6 +2074,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1932
2074
|
// 🩸 종전엔 여기서 delete 부터 해서, 재개가 원격에서 멀쩡히 돌던 Job 을 죽였다. 실패로 끝난 Job 만 지우고 다시 만든다.
|
|
1933
2075
|
// ☸️ 노드 레지스트리에 이 판이 있으면 그것을 pull 한다(델타 · 대표 2026-09-26) — 라벨 판정은 로컬 이미지 이름으로 한다.
|
|
1934
2076
|
const jobImage = member?.imageRef ?? image;
|
|
2077
|
+
// A pool member may pull a different image than the host's local one.
|
|
2078
|
+
// The local Docker label cannot prove that member image's commit.
|
|
2079
|
+
const localImageCommit = options.imageCommit !== undefined ? options.imageCommit : options.kubectl ? null : podImageFreshness({ image }).imageCommit;
|
|
2080
|
+
const jobImageCommit = jobImage === image ? localImageCommit : null;
|
|
1935
2081
|
const launchRunId = retryAccount || oomRetried || ghLoginRetried ? mintRunId() : childRunId;
|
|
1936
2082
|
let liveChildRunId = launchRunId;
|
|
1937
2083
|
// TA-POD-WARMBOOT-PARITY — the id the followed Job prints in its result marker (null = wait on the condition only).
|
|
@@ -2167,7 +2313,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2167
2313
|
debug.log('self-implement.pod', fit.fitted ? 'grok-deadline-fitted' : 'grok-deadline-kept', { job: name, requestedSeconds: jobDeadlineSeconds, deadlineSeconds: fit.deadlineSeconds, availableSeconds: fit.availableSeconds, relay: Boolean(credentialRelay), marginMs: POD_GROK_EXPIRY_MARGIN_MS });
|
|
2168
2314
|
jobDeadlineSeconds = fit.deadlineSeconds;
|
|
2169
2315
|
}
|
|
2170
|
-
const job = podJobManifest({ name, namespace, fallbackChain, ...(resultMarkerLine ? { resultMarkerLine } : {}), image: jobImage, workIdentity, ...(env.ELANOUS_TASK_BOUND_PR && /^[1-9]\d*$/.test(env.ELANOUS_TASK_BOUND_PR) && Number.isSafeInteger(Number(env.ELANOUS_TASK_BOUND_PR)) ? { boundPrNumber: Number(env.ELANOUS_TASK_BOUND_PR) } : {}), ...(member?.imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}), repoUrl, ...(options.source ? { source: options.source } : {}), ...(options.repairCommitOnly ? { repairCommitOnly: options.repairCommitOnly } : {}), ...(hostMirror ? { hostMirror } : {}), args: jobArgs, passEnv: jobPassEnv, deadlineSeconds: jobDeadlineSeconds, ...(goalDoc ? { goalDoc } : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? { authorSentence: true, ...(env.ELANOUS_POD_AUTHOR_GRADE === 'lite' || env.ELANOUS_POD_AUTHOR_GRADE === 'full' ? { authorGrade: env.ELANOUS_POD_AUTHOR_GRADE } : {}) } : {}), runId: launchRunId, ...(parentRunId ? { parentRunId } : {}), ...(jobArmEnv ? { armEnv: jobArmEnv } : {}), hostId: resolveHostId(env), skillEnvs: Object.keys(skillEnvs), memoryLimit, memoryRequest, imageCommit:
|
|
2316
|
+
const job = podJobManifest({ name, namespace, fallbackChain, ...(resultMarkerLine ? { resultMarkerLine } : {}), image: jobImage, workIdentity, ...(env.ELANOUS_TASK_BOUND_PR && /^[1-9]\d*$/.test(env.ELANOUS_TASK_BOUND_PR) && Number.isSafeInteger(Number(env.ELANOUS_TASK_BOUND_PR)) ? { boundPrNumber: Number(env.ELANOUS_TASK_BOUND_PR) } : {}), ...(member?.imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}), repoUrl, ...(options.source ? { source: options.source } : {}), ...(options.repairCommitOnly ? { repairCommitOnly: options.repairCommitOnly } : {}), ...(hostMirror ? { hostMirror } : {}), args: jobArgs, passEnv: jobPassEnv, deadlineSeconds: jobDeadlineSeconds, ...(goalDoc ? { goalDoc } : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? { authorSentence: true, ...(env.ELANOUS_POD_AUTHOR_GRADE === 'lite' || env.ELANOUS_POD_AUTHOR_GRADE === 'full' ? { authorGrade: env.ELANOUS_POD_AUTHOR_GRADE } : {}) } : {}), runId: launchRunId, ...(parentRunId ? { parentRunId } : {}), ...(jobArmEnv ? { armEnv: jobArmEnv } : {}), hostId: resolveHostId(env), skillEnvs: Object.keys(skillEnvs), memoryLimit, memoryRequest, imageCommit: jobImageCommit, ...(grok ? { grokCredential: 'grokAuth' in creds && creds.grokAuth ? 'subscription' as const : 'api_key' as const } : {}), ...(codexAccounts ? { codexAccounts } : {}), ...(hostRefresh ? { appCredential: true } : {}), execution: { key: executionKey, attempt: oomRetried ? 2 : 1 }, hostLeaseAdmitted: !!releaseAdmission, ...(roleCodexCredential ? { reviewerCodexAuth: true } : {}), ...(bedrockSpec ? { bedrock: bedrockSpec } : {}), ...(zaiSpec ? { zai: zaiSpec } : {}), ...(zaiFallbackSpec ? { zaiFallback: zaiFallbackSpec } : {}) }); // kubectl 주입(=시험)이면 docker 를 부르지 않는다
|
|
2171
2317
|
const carriedBranch = goalDoc ? 'goal-doc' as const : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? 'author' as const : undefined;
|
|
2172
2318
|
const carriedChildLlm = carriedBranch ? podDelegatedChildLlm(jobArgs) : undefined;
|
|
2173
2319
|
jobApplyStartedAt = Date.now();
|
|
@@ -2175,6 +2321,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2175
2321
|
zaiPendingApply.delete(name); // apply 뒤엔 Job 라벨이 센다(성공) · 실패면 셀 것이 없다
|
|
2176
2322
|
if (a.status !== 0) { cleanupSecret(); return { exitCode: 1, output: a.stderr, error: { code: 'pod-apply', message: a.stderr.trim() } }; }
|
|
2177
2323
|
releaseAdmission?.applied?.(name, context, namespace);
|
|
2324
|
+
const imageFacts = { job: name, childRunId: liveChildRunId, image: jobImage, imageCommit: jobImageCommit, context };
|
|
2325
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-job-image', data: imageFacts }, ledgerDir); }
|
|
2326
|
+
catch (error) { debug.log('self-implement.pod', 'job-image-ledger-unavailable', { job: name, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
2327
|
+
debug.log('self-implement.pod', 'job-image', imageFacts);
|
|
2178
2328
|
// The injected observer does not acquire a production lease. The gate consumer
|
|
2179
2329
|
// must compare the Job result and explicitly finalize before enabling replay.
|
|
2180
2330
|
if (!oomRetried && input.warmWorkerCommand && options.warmPodRequest) {
|
|
@@ -2241,7 +2391,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2241
2391
|
}
|
|
2242
2392
|
writePodFragment({ spaceId: input.spaceId, context, namespace, job: name, inboxDir: POD_CONTROL_INBOX_DIR, ...(liveChildRunId === normalizeRunId(liveChildRunId) ? { runId: liveChildRunId } : {}), ...(parentRunId && normalizeRunId(parentRunId) === parentRunId ? { parentRunId } : {}) }, env);
|
|
2243
2393
|
recorded = true;
|
|
2244
|
-
debug.log('self-implement.pod', 'job-applied', { job: name, namespace, ...(member ? { context: member.context } : {}), image: jobImage, spaceId: input.spaceId, passEnv, ...(openrouter ? { openrouterKeyPresent: passEnv.includes('OPENROUTER_API_KEY') } : {}), ...(anthropic ? { anthropicKeyPresent: passEnv.includes('ANTHROPIC_API_KEY') } : {}), ...(bedrockSpec ? { bedrockSecret: bedrockSpec.secretName, bedrockSecretKeys: bedrockSpec.keys, bedrockRegionFrom: bedrockSpec.region.from } : {}), ...(zaiRoute ? { zaiRoute, ...(zaiSpec ? { zaiSecret: zaiSpec.secretName } : {}) } : {}), extraArgs: options.extraArgs ?? [], ...(options.armEnv?.ELANOUS_ARM_ID ? { armId: options.armEnv.ELANOUS_ARM_ID } : {}) });
|
|
2394
|
+
debug.log('self-implement.pod', 'job-applied', { job: name, namespace, ...(member ? { context: member.context } : {}), image: jobImage, spaceId: input.spaceId, passEnv, ...(openrouter ? { openrouterKeyPresent: passEnv.includes('OPENROUTER_API_KEY') } : {}), ...(openGateway ? { openGatewayKeyPresent: passEnv.includes('OPENGATEWAY_API_KEY') } : {}), ...(anthropic ? { anthropicKeyPresent: passEnv.includes('ANTHROPIC_API_KEY') } : {}), ...(bedrockSpec ? { bedrockSecret: bedrockSpec.secretName, bedrockSecretKeys: bedrockSpec.keys, bedrockRegionFrom: bedrockSpec.region.from } : {}), ...(zaiRoute ? { zaiRoute, ...(zaiSpec ? { zaiSecret: zaiSpec.secretName } : {}) } : {}), extraArgs: options.extraArgs ?? [], ...(options.armEnv?.ELANOUS_ARM_ID ? { armId: options.armEnv.ELANOUS_ARM_ID } : {}) });
|
|
2245
2395
|
let state: 'complete' | 'failed' | 'aborted' = 'failed';
|
|
2246
2396
|
let failedReason = '';
|
|
2247
2397
|
let containerReason: string | null = null;
|
|
@@ -2253,11 +2403,34 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2253
2403
|
// ⭐ METER-POD-ROLLUP-GAP-3 — 런 «도중» 마지막 PARTIAL 줄을 호스트가 쥔다: Pod 가 지워지면(SIGTERM 등) 종료 로그에서 PARTIAL 이 사라진다.
|
|
2254
2404
|
let livePartialLine: string | null = null;
|
|
2255
2405
|
let pollsSinceCapture = 0;
|
|
2406
|
+
let imageSnapshotRecorded = false;
|
|
2407
|
+
let terminalImageRecorded = false;
|
|
2408
|
+
const recordPodImage = (final: boolean): void => {
|
|
2409
|
+
if (final ? terminalImageRecorded : imageSnapshotRecorded) return;
|
|
2410
|
+
const observed = readJobPodImage(kubectl, namespace, name, lifecycleJobUid);
|
|
2411
|
+
if (!final && !observed.podName) return;
|
|
2412
|
+
const append = (isFinal: boolean): void => {
|
|
2413
|
+
const data = { job: name, childRunId: liveChildRunId, attempt: oomRetried ? 2 : 1,
|
|
2414
|
+
image: jobImage, imageCommit: jobImageCommit, context, ...observed, final: isFinal };
|
|
2415
|
+
try {
|
|
2416
|
+
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-image-digest', data }, ledgerDir);
|
|
2417
|
+
if (isFinal) terminalImageRecorded = true;
|
|
2418
|
+
else imageSnapshotRecorded = true;
|
|
2419
|
+
} catch (error) {
|
|
2420
|
+
debug.log('self-implement.pod', 'pod-image-digest-ledger-unavailable', {
|
|
2421
|
+
job: name, final: isFinal, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
|
|
2422
|
+
}
|
|
2423
|
+
debug.log('self-implement.pod', 'pod-image-digest', data);
|
|
2424
|
+
};
|
|
2425
|
+
if (final && !imageSnapshotRecorded && observed.podName) append(false);
|
|
2426
|
+
append(final);
|
|
2427
|
+
};
|
|
2256
2428
|
const capturePolls = Math.max(1, options.usagePartialCapturePolls ?? Math.round(POD_USAGE_PARTIAL_CAPTURE_MS / Math.max(1, options.pollMs ?? 15_000)));
|
|
2257
2429
|
const markerWait = markerToken ? await import('./pod-result-marker-wait.js') : null;
|
|
2258
2430
|
const waitStartedAt = Date.now();
|
|
2259
2431
|
for (;;) {
|
|
2260
2432
|
if (input.signal?.aborted) {
|
|
2433
|
+
recordPodImage(true);
|
|
2261
2434
|
// METER-POD-ROLLUP-GAP — Job 삭제 «전»에 로그를 한 번 거둔다: PARTIAL 줄이 삭제와 함께 사라지면 사용량이 0 처럼 보인다.
|
|
2262
2435
|
try {
|
|
2263
2436
|
const captured = kubectl(['-n', namespace, 'logs', `job/${name}`, '-c', 'child', '--tail=400']);
|
|
@@ -2267,6 +2440,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2267
2440
|
state = 'aborted';
|
|
2268
2441
|
break;
|
|
2269
2442
|
}
|
|
2443
|
+
if (!imageSnapshotRecorded) recordPodImage(false);
|
|
2270
2444
|
const g = kubectl(['-n', namespace, 'get', 'job', name, '-o', 'jsonpath={.status.conditions[*].type}']);
|
|
2271
2445
|
const types = g.stdout;
|
|
2272
2446
|
if (/Complete|SuccessCriteriaMet/.test(types)) { releaseAdmission?.observed?.(); state = 'complete'; break; }
|
|
@@ -2342,6 +2516,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2342
2516
|
}
|
|
2343
2517
|
await sleep(options.pollMs ?? 15_000);
|
|
2344
2518
|
}
|
|
2519
|
+
if (!terminalImageRecorded) recordPodImage(true);
|
|
2345
2520
|
let logs = '';
|
|
2346
2521
|
let logTailReason: string | undefined;
|
|
2347
2522
|
try {
|
|
@@ -2521,14 +2696,52 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2521
2696
|
}
|
|
2522
2697
|
const childFailure = state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
2523
2698
|
? lastPodChildFailure(logs) : null;
|
|
2699
|
+
let childPrFailure: ReturnType<typeof podPrOpenFailure> = null;
|
|
2700
|
+
if (childFailure) {
|
|
2701
|
+
try {
|
|
2702
|
+
const entries = loadRunLedger(liveChildRunId, ledgerDir);
|
|
2703
|
+
const entry = entries?.slice().reverse().find((row) => row.event === 'pr-open-failed');
|
|
2704
|
+
childPrFailure = podPrOpenFailure(entry?.data, childFailure.rawError);
|
|
2705
|
+
} catch { /* child ledger is optional; terminal result remains available */ }
|
|
2706
|
+
childPrFailure ??= podPrOpenFailure(null, childFailure.rawError);
|
|
2707
|
+
}
|
|
2708
|
+
if (childPrFailure && disposition) {
|
|
2709
|
+
disposition.childEvidence = {
|
|
2710
|
+
...(disposition.childEvidence ?? {}),
|
|
2711
|
+
prFailure: {
|
|
2712
|
+
stage: childPrFailure.prFailureStage,
|
|
2713
|
+
source: childPrFailure.prFailureSource,
|
|
2714
|
+
errorTail: childPrFailure.childErrorTail,
|
|
2715
|
+
...(childPrFailure.blockingPrNumber ? { blockingPrNumber: childPrFailure.blockingPrNumber } : {}),
|
|
2716
|
+
...(childPrFailure.candidateCardId ? { candidateCardId: childPrFailure.candidateCardId } : {}),
|
|
2717
|
+
...(childPrFailure.blockingCardId ? { blockingCardId: childPrFailure.blockingCardId } : {}),
|
|
2718
|
+
},
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2524
2721
|
let noResultDiagnostic: string | null = null;
|
|
2525
|
-
// POD-NORESULT:
|
|
2526
|
-
|
|
2722
|
+
// POD-NORESULT: a terminal Job without a parsed disposition is incomplete even when the Job condition says Complete.
|
|
2723
|
+
const noResultCandidate = !disposition && !oomKilled && failedReason !== 'DeadlineExceeded' &&
|
|
2724
|
+
(state === 'complete' || (state === 'failed' && containerExitCode !== 0));
|
|
2725
|
+
if (noResultCandidate) {
|
|
2527
2726
|
try { noResultDiagnostic = podNoResultDiagnostic(readFileSync(runLedgerPath(liveChildRunId, ledgerDir), 'utf8')); }
|
|
2528
2727
|
catch { /* ledger not returned: do not invent a stage */ }
|
|
2529
2728
|
noResultDiagnostic ??= 'child terminal result missing; last ledger stage=unknown; round=unknown; mustFix=unknown';
|
|
2530
2729
|
}
|
|
2531
|
-
|
|
2730
|
+
// POD-NORESULT-LOG-TAIL: the Job runs with backoffLimit 0, so the failed Pod — and its container log —
|
|
2731
|
+
// is collected right after this. When the child ledger cannot even name the stage, read the tail here,
|
|
2732
|
+
// before deletion, so the parent keeps a maskable reason instead of `childEvidence=null`.
|
|
2733
|
+
let noResultLogEvidence: Record<string, unknown> | null = null;
|
|
2734
|
+
if (noResultDiagnostic?.includes('stage=unknown')) {
|
|
2735
|
+
const logRead = readPodNoResultLogTail({ run: kubectl, namespace, job: name, maxLines: POD_NO_RESULT_LOG_TAIL_LINES });
|
|
2736
|
+
if ('tail' in logRead) {
|
|
2737
|
+
noResultLogEvidence = { stderr: logRead.tail.join('\n'), logTail: logRead.tail, logTailLines: logRead.tail.length };
|
|
2738
|
+
debug.log('self-implement.pod', 'no-result-log-tail', { job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic, lines: logRead.tail.length, logTail: logRead.tail }, { compact: { stringMax: 500 } });
|
|
2739
|
+
} else {
|
|
2740
|
+
noResultLogEvidence = { stderr: `log-unavailable: ${logRead.unavailable}` };
|
|
2741
|
+
debug.log('self-implement.pod', 'no-result-log-tail', { job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic, logUnavailable: logRead.unavailable }, { level: 'warn' });
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
if (childFailure) debug.log('self-implement.pod', 'child-error', { job: name, stage: childFailure.stage, error: childFailure.error, ...(childPrFailure ?? {}) }, { compact: { stringMax: 500 } });
|
|
2532
2745
|
if (noResultDiagnostic && !noResultDiagnostic.includes('stage=unknown')) {
|
|
2533
2746
|
debug.log('self-implement.pod', 'no-result-ledger-diagnostic', { job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic }, { compact: { stringMax: 500 } });
|
|
2534
2747
|
}
|
|
@@ -2536,8 +2749,9 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2536
2749
|
...(state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
2537
2750
|
? { ...(childFailure ? { childStage: childFailure.stage } : disposition?.stage ? { childStage: disposition.stage } : {}),
|
|
2538
2751
|
// POD-NORESULT: a parsed result row without error text is not a missing result line (UX 10-07 01:01: 28 of 28 exit-2 Jobs printed one).
|
|
2539
|
-
childError: childFailure?.error ?? (disposition?.stage ? 'result-without-error' : 'no-result-line')
|
|
2540
|
-
|
|
2752
|
+
childError: childFailure?.error ?? (disposition?.stage ? 'result-without-error' : 'no-result-line'),
|
|
2753
|
+
...(childPrFailure ?? {}) } : {}) },
|
|
2754
|
+
(state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded') ? { compact: { stringMax: 500 } } : undefined);
|
|
2541
2755
|
// POD-LIFECYCLE-STAGES: one `pod.lifecycle stages` line — clone · mirror · run come from the Pod's own
|
|
2542
2756
|
// `ELANOUS_POD_STAGE` markers (SI-POD-STAGE-MARKERS), read from that Pod's full named log. ⛔ No `--tail=400` fallback:
|
|
2543
2757
|
// a tail can lose the real `run start` and pair a child-printed look-alike with the real `run end` (review mf) —
|
|
@@ -2606,10 +2820,11 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2606
2820
|
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry-finished', data: { job: name, executionKey, fromChildRunId: retryFromChildRunId, childRunId: liveChildRunId, state } }, ledgerDir);
|
|
2607
2821
|
}
|
|
2608
2822
|
if (goalFile) {
|
|
2823
|
+
const noResultOutcome = state === 'failed' && containerExitCode !== null && noResultDiagnostic ? 'abandoned' : 'pod-no-result';
|
|
2609
2824
|
const record: GoalExecutionRecord = {
|
|
2610
2825
|
runId: liveChildRunId,
|
|
2611
2826
|
stage: (state === 'aborted' || !disposition ? 'aborted' : disposition.stage) as GoalExecutionRecord['stage'],
|
|
2612
|
-
outcome: (!disposition ?
|
|
2827
|
+
outcome: (!disposition ? noResultOutcome : state === 'complete' && disposition.merged ? 'merged' : state === 'complete' && disposition.ok ? 'completed' : 'abandoned') as GoalExecutionRecord['outcome'],
|
|
2613
2828
|
ok: state === 'complete' && (disposition?.ok ?? false),
|
|
2614
2829
|
...(disposition?.prNumber !== undefined ? { prNumber: disposition.prNumber } : {}),
|
|
2615
2830
|
completedAt: new Date().toISOString(),
|
|
@@ -2619,16 +2834,41 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2619
2834
|
}
|
|
2620
2835
|
// The host still has the complete Pod log here. The CLI only retains a short stdout tail,
|
|
2621
2836
|
// so preserve the parsed child facts in the parent ledger before that tail is returned.
|
|
2622
|
-
if (state === 'failed' || state === 'aborted') {
|
|
2837
|
+
if (state === 'failed' || state === 'aborted' || noResultLogEvidence || (state === 'complete' && noResultDiagnostic !== null)) {
|
|
2623
2838
|
// Every terminal attempt writes a marker, including one with no parseable child facts.
|
|
2624
2839
|
// The next attempt must never inherit an earlier Pod's classification.
|
|
2625
|
-
|
|
2840
|
+
const childEvidence = noResultLogEvidence ?? disposition?.childEvidence ?? null;
|
|
2841
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-child-evidence', data: { childEvidence, childRunId: liveChildRunId, job: name } }, ledgerDir); }
|
|
2626
2842
|
catch (error) { debug.log('self-implement.pod', 'child-evidence-write-failed', { job: name, childRunId: liveChildRunId, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
2627
2843
|
}
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2844
|
+
let prLookup: OpenPrByBranch | undefined;
|
|
2845
|
+
const terminalBranch = typeof disposition?.branch === 'string' ? disposition.branch.trim() : '';
|
|
2846
|
+
if (disposition && (state === 'failed' || state === 'aborted' || disposition.ok === false)
|
|
2847
|
+
&& (!Number.isSafeInteger(disposition.prNumber) || (disposition.prNumber ?? 0) <= 0) && terminalBranch) {
|
|
2848
|
+
try { prLookup = await (options.lookupOpenPrByBranch ?? defaultLookupOpenPrByBranch)(terminalBranch, repoUrl); }
|
|
2849
|
+
catch { prLookup = { status: 'failed', reason: 'pr-lookup-threw' }; }
|
|
2850
|
+
}
|
|
2851
|
+
const terminalDisposition = prLookup?.status === 'found' && disposition
|
|
2852
|
+
? { ...disposition, prNumber: prLookup.prNumber, prUrl: prLookup.prUrl } : disposition;
|
|
2853
|
+
if (prLookup?.status === 'found') debug.log('self-implement.pod', 'pr-terminal-bound-by-branch', {
|
|
2854
|
+
branch: terminalBranch, prNumber: prLookup.prNumber, prUrl: prLookup.prUrl, status: 'found', job: name });
|
|
2855
|
+
else if (prLookup) debug.log('self-implement.pod', 'pr-terminal-branch-lookup', {
|
|
2856
|
+
branch: terminalBranch, status: prLookup.status, reason: prLookup.reason, job: name });
|
|
2857
|
+
const terminalRecord = { ...classifyPodTerminal({ childRunId: liveChildRunId, state, disposition: terminalDisposition, env,
|
|
2858
|
+
prBody: options.draftResidue || prLookup?.status === 'found' ? undefined : podTerminalPrBody(terminalDisposition),
|
|
2859
|
+
goalText: parentGoal ?? input.feature, at: new Date().toISOString() }),
|
|
2860
|
+
...(prLookup ? { prLookup: prLookup.status === 'found' ? { status: 'found', source: 'branch-lookup' }
|
|
2861
|
+
: { status: prLookup.status, reason: prLookup.reason } } : {}),
|
|
2862
|
+
...(prLookup?.status === 'found' ? { prNumberSource: 'branch-lookup' } : {}) };
|
|
2863
|
+
try {
|
|
2864
|
+
if (options.draftResidue) await options.draftResidue(terminalRecord, { ledgerRunId, ledgerDir });
|
|
2865
|
+
else if (prLookup?.status === 'found') {
|
|
2866
|
+
// This lookup only repairs the host ledger's PR identity. The draft sweeper owns PR writes.
|
|
2867
|
+
const alreadyRecorded = loadRunLedger(ledgerRunId, ledgerDir)?.some((entry) =>
|
|
2868
|
+
entry.event === 'pr-terminal' && entry.data.childRunId === liveChildRunId);
|
|
2869
|
+
if (!alreadyRecorded) appendRunLedgerEntry({ runId: ledgerRunId, event: 'pr-terminal', data: terminalRecord }, ledgerDir);
|
|
2870
|
+
} else await applyPodTerminal(terminalRecord, { ledgerRunId, ledgerDir });
|
|
2871
|
+
}
|
|
2632
2872
|
catch (error) { debug.log('draft.residue', 'failed', { childRunId: liveChildRunId, prNumber: terminalRecord.prNumber,
|
|
2633
2873
|
terminalClass: terminalRecord.terminalClass, owner: terminalRecord.owner, ownerSource: terminalRecord.ownerSource,
|
|
2634
2874
|
step: 'apply', reason: error instanceof Error ? error.message : String(error) }); }
|
|
@@ -2650,7 +2890,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2650
2890
|
: disposition;
|
|
2651
2891
|
const podExitCode = state === 'complete' && disposition?.stage !== 'host-regate-failed' && disposition?.ok !== false ? 0 : 1;
|
|
2652
2892
|
// SCHED-CONTRACT: a failed child must say why outside the Pod, not «no error diagnostic».
|
|
2653
|
-
const
|
|
2893
|
+
const rawPodReason = podExitCode === 1 ? extractPodFailureReason({ logs, logTailReason, containerReason, jobReason: failedReason || undefined, deadlineSeconds, result: disposition ? { stage: disposition.stage, prUrl: disposition.prUrl ?? null, prNumber: disposition.prNumber ?? null } : null }) : undefined;
|
|
2894
|
+
const podReason = rawPodReason
|
|
2895
|
+
? noResultLogEvidence ? boundedPodLogText(rawPodReason, POD_NO_RESULT_LOG_REASON_MAX_CHARS) : rawPodReason
|
|
2896
|
+
: undefined;
|
|
2654
2897
|
if (podReason) debug.log('self-implement.pod', 'failure-reason', { job: name, reason: podReason });
|
|
2655
2898
|
if (podExitCode === 1) {
|
|
2656
2899
|
// HARNESS-FULL-GRAPH — 실패 쪽 두 노드: 결과 해석(result-parse) → 수확(salvage, Pod Job 이 push 한 줄로 판정).
|
|
@@ -2668,7 +2911,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2668
2911
|
? { error: { code: 'pod-gh-login-failed', message: `Job ${name} GitHub 앱 로그인 실패 (container=${containerReason ?? 'unknown'}/7, attempt=${ghLoginRetried ? 2 : 1}) — 새 토큰으로 ${ghLoginRetried ? '1회 재시도했으나 다시 실패' : '재시도 불가'}` } }
|
|
2669
2912
|
: oomKilled
|
|
2670
2913
|
? { error: { code: 'pod-oom-killed', message: `Job ${name} failed (OOMKilled/${containerExitCode ?? 'unknown'}, memoryLimit=${memoryLimit})${oomRetried || (memoryGi(reattachedMemoryLimit ?? memoryLimit) ?? 0) >= (memoryGi(podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit) ?? Infinity) ? ` — OOM · ${oomRetried ? retryTier : 'high'} 에서도` : ''}\n${formatLastMemSample(samples.at(-1))}` } }
|
|
2671
|
-
|
|
2914
|
+
: { error: { code: 'pod-job-failed', message: `Job ${name} failed${failedReason || containerReason ? ` (${[failedReason, containerReason ? `container=${containerReason}/${containerExitCode ?? 'unknown'}` : ''].filter(Boolean).join(', ')})` : ''}${childFailure ? ` — childStage=${childFailure.stage} · childError=${childFailure.error}` : disposition?.stage ? ` — childStage=${disposition.stage} · reason=${podReason}` : ` — childError=no-result-line · reason=${podReason}`}${noResultDiagnostic && containerExitCode !== null ? ` · ${noResultDiagnostic}` : ''}` } }
|
|
2672
2915
|
: podReason && !childFailure?.error ? { error: { code: 'pod-child-failed', message: podReason } } : {}),
|
|
2673
2916
|
...(finishedDisposition ? { disposition: finishedDisposition } : {}),
|
|
2674
2917
|
};
|
|
@@ -2736,7 +2979,43 @@ export function recordPodSalvage(logs: string, job: string, log: (category: stri
|
|
|
2736
2979
|
return rows.map((row) => row.branch);
|
|
2737
2980
|
}
|
|
2738
2981
|
|
|
2739
|
-
|
|
2982
|
+
type PodPrOpenFailure = {
|
|
2983
|
+
childErrorTail: string;
|
|
2984
|
+
prFailureStage: 'pr-open-failed';
|
|
2985
|
+
prFailureSource: 'child-ledger' | 'terminal-result';
|
|
2986
|
+
blockingPrNumber?: number;
|
|
2987
|
+
candidateCardId?: string;
|
|
2988
|
+
blockingCardId?: string;
|
|
2989
|
+
};
|
|
2990
|
+
|
|
2991
|
+
/** Preserve the actionable tail independently of the bounded human error. Never infer a card from a PR number. */
|
|
2992
|
+
function podPrOpenFailure(ledger: Record<string, unknown> | undefined | null, terminalError: string): PodPrOpenFailure | null {
|
|
2993
|
+
const ledgerError = typeof ledger?.error === 'string' ? ledger.error : '';
|
|
2994
|
+
// A previous rework round can leave a pr-open-failed row behind. Only a terminal PR failure may consume it.
|
|
2995
|
+
if (!/draft PR 생성 실패|PR (?:gh|guard) 실패|same-cell reuse:/u.test(terminalError)) return null;
|
|
2996
|
+
// The terminal must contain the complete ledger error. A shared prefix such as "same-cell"
|
|
2997
|
+
// cannot distinguish an earlier rework round from the current failed attempt.
|
|
2998
|
+
const matchingLedger = ledgerError.length >= 20 && terminalError.includes(ledgerError);
|
|
2999
|
+
const source = terminalError || ledgerError;
|
|
3000
|
+
const sanitized = redactSecretText(source).replace(/\/home\/[^/\s]+\//gu, '~/');
|
|
3001
|
+
const tail = sanitized.split(/\r\n|\n|\r/u).filter(Boolean).at(-1) ?? sanitized;
|
|
3002
|
+
const terminalHasIds = /(?:blockers=|blockingPr=|candidateCardId=)/u.test(terminalError);
|
|
3003
|
+
const detail = `${terminalError}\n${matchingLedger ? ledgerError : ''}`;
|
|
3004
|
+
const blocker = /(?:blockers=|blockingPr=)\s*#?(\d+)(?::([A-Za-z0-9_-]{1,128}))?/u.exec(detail);
|
|
3005
|
+
const prNumber = blocker ? Number(blocker[1]) : null;
|
|
3006
|
+
const candidate = /candidateCardId=([A-Za-z0-9_-]{1,128})/u.exec(detail)?.[1];
|
|
3007
|
+
const card = blocker?.[2];
|
|
3008
|
+
return {
|
|
3009
|
+
childErrorTail: tail.slice(-240),
|
|
3010
|
+
prFailureStage: 'pr-open-failed',
|
|
3011
|
+
prFailureSource: terminalHasIds || !matchingLedger ? 'terminal-result' : 'child-ledger',
|
|
3012
|
+
...(prNumber && Number.isSafeInteger(prNumber) ? { blockingPrNumber: prNumber } : {}),
|
|
3013
|
+
...(candidate && candidate !== 'unknown' ? { candidateCardId: candidate } : {}),
|
|
3014
|
+
...(card && card !== 'unknown' ? { blockingCardId: card } : {}),
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
function lastPodChildFailure(logs: string): { stage: string; error: string; rawError: string } | null {
|
|
2740
3019
|
for (const line of logs.split('\n').reverse()) {
|
|
2741
3020
|
if (!line.trimStart().startsWith('{')) continue;
|
|
2742
3021
|
let result: unknown;
|
|
@@ -2746,14 +3025,67 @@ function lastPodChildFailure(logs: string): { stage: string; error: string } | n
|
|
|
2746
3025
|
const terminal = podTerminalRow(row);
|
|
2747
3026
|
if (!terminal) continue;
|
|
2748
3027
|
if (terminal.ok !== false || typeof terminal.error !== 'string') return null;
|
|
2749
|
-
const lines = terminal.error.replace(/\/home\/[^/\s]+\//g, '~/').split(/\r\n|\n|\r/);
|
|
3028
|
+
const lines = redactSecretText(terminal.error).replace(/\/home\/[^/\s]+\//g, '~/').split(/\r\n|\n|\r/);
|
|
2750
3029
|
const first = lines[0]!.slice(0, 240);
|
|
2751
3030
|
const last = lines.at(-1)!.slice(0, 240);
|
|
2752
|
-
return { stage: terminal.stage, error: first === last ? first : `${first}\n${last}
|
|
3031
|
+
return { stage: terminal.stage, error: first === last ? first : `${first}\n${last}`, rawError: terminal.error };
|
|
2753
3032
|
}
|
|
2754
3033
|
return null;
|
|
2755
3034
|
}
|
|
2756
3035
|
|
|
3036
|
+
/** POD-NORESULT-LOG-TAIL: how many container-log lines a failed Pod leaves behind for the parent. */
|
|
3037
|
+
export const POD_NO_RESULT_LOG_TAIL_LINES = 80;
|
|
3038
|
+
const POD_NO_RESULT_LOG_TAIL_LINE_CHARS = 512;
|
|
3039
|
+
const POD_NO_RESULT_LOG_TAIL_MAX_CHARS = 8_192;
|
|
3040
|
+
const POD_NO_RESULT_LOG_REASON_MAX_CHARS = 512;
|
|
3041
|
+
|
|
3042
|
+
function boundedPodLogText(source: string, maxChars: number): string {
|
|
3043
|
+
const redacted = redactSecretText(source).replace(/[\r\n\t]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
3044
|
+
return redacted.length > maxChars ? `${redacted.slice(0, maxChars)}…[truncated]` : redacted;
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
/** 로그 꼬리 — 마지막 N줄만, 한 줄씩 비밀값 마스킹. */
|
|
3048
|
+
export function podNoResultLogTailLines(source: string, maxLines: number = POD_NO_RESULT_LOG_TAIL_LINES): string[] {
|
|
3049
|
+
const lines = source.replace(/\r\n/g, '\n').split('\n');
|
|
3050
|
+
while (lines.length && lines[lines.length - 1] === '') lines.pop();
|
|
3051
|
+
const result: string[] = [];
|
|
3052
|
+
let chars = 0;
|
|
3053
|
+
for (const line of lines.slice(-Math.max(0, maxLines)).reverse()) {
|
|
3054
|
+
if (chars >= POD_NO_RESULT_LOG_TAIL_MAX_CHARS) break;
|
|
3055
|
+
const bounded = boundedPodLogText(line, Math.min(POD_NO_RESULT_LOG_TAIL_LINE_CHARS, POD_NO_RESULT_LOG_TAIL_MAX_CHARS - chars));
|
|
3056
|
+
if (!bounded) continue;
|
|
3057
|
+
result.push(bounded);
|
|
3058
|
+
chars += bounded.length;
|
|
3059
|
+
}
|
|
3060
|
+
return result.reverse();
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
export type PodNoResultLogRead = { tail: string[] } | { unavailable: string };
|
|
3064
|
+
|
|
3065
|
+
/**
|
|
3066
|
+
* POD-NORESULT-LOG-TAIL: the terminal Job (`backoffLimit 0`) is collected right after it fails, so the
|
|
3067
|
+
* container log has to be read while the Pod still exists. Never throws — an unreadable log becomes a reason.
|
|
3068
|
+
*/
|
|
3069
|
+
export function readPodNoResultLogTail(args: {
|
|
3070
|
+
run: (args: readonly string[]) => { status: number | null; stdout: string; stderr: string };
|
|
3071
|
+
namespace: string;
|
|
3072
|
+
job: string;
|
|
3073
|
+
maxLines?: number;
|
|
3074
|
+
}): PodNoResultLogRead {
|
|
3075
|
+
const maxLines = args.maxLines ?? POD_NO_RESULT_LOG_TAIL_LINES;
|
|
3076
|
+
try {
|
|
3077
|
+
const result = args.run(['-n', args.namespace, 'logs', `job/${args.job}`, `--tail=${maxLines}`]);
|
|
3078
|
+
if (result.status !== 0) {
|
|
3079
|
+
const reason = boundedPodLogText(result.stderr || result.stdout || '', POD_NO_RESULT_LOG_REASON_MAX_CHARS);
|
|
3080
|
+
return { unavailable: reason || `kubectl logs exit ${result.status}` };
|
|
3081
|
+
}
|
|
3082
|
+
const tail = podNoResultLogTailLines(result.stdout, maxLines);
|
|
3083
|
+
return tail.length ? { tail } : { unavailable: 'empty log output' };
|
|
3084
|
+
} catch (error) {
|
|
3085
|
+
return { unavailable: boundedPodLogText(error instanceof Error ? error.message : String(error), POD_NO_RESULT_LOG_REASON_MAX_CHARS) || 'unknown error' };
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
|
|
2757
3089
|
/** 사람용 런 결과 한 줄 — 수확 브랜치가 있으면 «수확할 브랜치: …» 를 붙인다. */
|
|
2758
3090
|
export function podRunResultLine(logs: string, branches: readonly string[]): string {
|
|
2759
3091
|
const result = logs.split('\n').filter((line) => !line.startsWith('ELANOUS_MEM ')).join('\n').slice(-4000);
|