elanous 0.2.35 → 0.2.37
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/{T1mBQqmkwKDLGtyisDCOd → 8kITumG9GAZ1LwT9POrAR}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/1041-5841aae848fb6403.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1087.3177e54f672dc526.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1105.efe1e13503ac7a33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1224.2ceca6c180d5130b.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{9952.5daf5f94063a2b3e.js → 1262.a89bb27a5b2f844e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1417-1768233ac92f8a5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1612.81bbc19af06af16c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1746.3563617ea64023a3.js +115 -0
- package/apps/pwa/out/_next/static/chunks/1766.728d42053070bb2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1773.d38e945481e04125.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1974-c12f9600da3d3c26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1dc5a567.9b092bfae3d7099b.js → 1ed5e8af.cf424815c7c0efad.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4705-194e64e47ca0fbdb.js → 2002-a43a3f380df873ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2061.7232dc9dc058417f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{13.02a07436dec15f89.js → 210.90b2cbd64926cf34.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9512.5a76207a730e0491.js → 2283.edd9627b7bdfb546.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{6866-79aa5b4e47cac6aa.js → 2318-450e75d07605b8fe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{938.66ad1ef0e5abae1c.js → 2375.bf364c85dbb6f085.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/244-a810d282a20f1783.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2479-fe7e188bd9402d14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2514.03b60f266531a015.js +148 -0
- package/apps/pwa/out/_next/static/chunks/2526-a657538869888604.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2533-6f0341ff075809e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2676.e7994c695ef0f30d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2734.3ffdf54254fb6f99.js +27 -0
- package/apps/pwa/out/_next/static/chunks/2770-23252bf3a37880b9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2969.66b8b545139d1544.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3128.a9210d08800ea6a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3199.6a1a9b0e6cd01dc6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9214.63a510d39178e997.js → 3578.4e9f9948a9a6f2f4.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3760-2909ddb9279281cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3825.1921b5901bf33dce.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3994-63eb884ed0547011.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4036.d54e55e1ebf5acdc.js +90 -0
- package/apps/pwa/out/_next/static/chunks/4115.5b438b99f0ae729f.js +226 -0
- package/apps/pwa/out/_next/static/chunks/4269.d8f9f34bb0fc6c43.js +287 -0
- package/apps/pwa/out/_next/static/chunks/431-4eed5bd2c5fe1719.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2018.a741297bce759490.js → 4313.76f232c304a15bde.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1165.bad13fc9151a7288.js → 4324.c99ba3e83eb1b691.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3f7c4ee5.3dde54f01a816613.js → 4337e36d.f42c3b391b3d8f20.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{7385.50a308a477bdfc21.js → 4383.18e5c54db3a22c88.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4599-96eff3f2a9c242c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8557-e7898769499cd56f.js → 4725-34bed25720cc326b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4825-a9d9d4e9f5892309.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5483-dd5d7697f6bdb106.js → 4833-7934c592c5569f00.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4879-68518f547a207aff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4896.f9b70285363b27b2.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{8444.4c26b379ca2b9596.js → 4931.95cc6dbd230774ca.js} +17 -17
- package/apps/pwa/out/_next/static/chunks/4937-28d437999b77c9dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/49aa54ab.80f39398e0ed0f30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3408.21d1570602f4b6c0.js → 5046.7fd5cbd859299448.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5056.482687319b82638e.js +104 -0
- package/apps/pwa/out/_next/static/chunks/506.16b83d56a437a9ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5260-20593b0a25d0afbc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5404.cc4e9fec26fd5925.js +1 -0
- package/apps/pwa/out/_next/static/chunks/576.485e83e3f3dec19a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5832-a6be14e663f65726.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5854-56648a9fe646b9a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/58edce7d.f1b0c72acf13b123.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +93 -0
- package/apps/pwa/out/_next/static/chunks/5952.294fc574a8625184.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b6705a4f-b9a9047de9eecfd0.js → 5f6af561-f90b7a67f4f75d13.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6168.17749aa3905fa073.js +74 -0
- package/apps/pwa/out/_next/static/chunks/6252.240578f10952e091.js +34 -0
- package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5101.e32282718f99ed6f.js → 6680.6a693c28aae4cd19.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4718-17d2af636796a330.js → 6745-5a63492933959dd9.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6758.e7fbf532953c0783.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6795.0cb8a59730d99703.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{6236.e18cced2d5ba6afb.js → 6819.be5c9c4d67f22922.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/6877-84d47b7e85d0c41e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2833-0fe47af99ed13c29.js → 6971-fb5f9dc0a2cb5d2d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{da5176c6.6773b6c5994e1d99.js → 709a918c.4fd3b4e2a32cb4e3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/717.522b08685979b814.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7191.e41eb8f455c7f089.js +41 -0
- package/apps/pwa/out/_next/static/chunks/7233.8e744b7513119409.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7238-97c2226f80504bd8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7285.307963afcf7e7660.js +24 -0
- package/apps/pwa/out/_next/static/chunks/7299-fbe4b8c47a87614a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7314.df0d95930b0c3f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7406.c0589fdf2ee66629.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7424.c0fe2a595d4232b5.js +23 -0
- package/apps/pwa/out/_next/static/chunks/752-90e3e7daad3d8e39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7524-bcd3d9928c6842d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7549.9a4de3bf231cfc52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/757-4fbaf50942b8c424.js +1 -0
- package/apps/pwa/out/_next/static/chunks/765.76ae970ceb597b5b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7644.17e37ba6f1d4226e.js → 7867.e8725745ddd1d940.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/7893-e444a1b0809cd1e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{0368f25d-ad84af92fefee68f.js → 7c5ed21f-f286708e57cf88f7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8030-aa66b478b53dbfca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8051.c88c07dacbbc5f21.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{359-2250789a2d3499e7.js → 8080-40d781e7704d3671.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8149.80e402d4345e8dae.js +77 -0
- package/apps/pwa/out/_next/static/chunks/8205.50a5268f4e12ba0b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8231.a128feaa6f8b6079.js +1 -0
- package/apps/pwa/out/_next/static/chunks/85.4ddb1dda2ea3ffa2.js +286 -0
- package/apps/pwa/out/_next/static/chunks/8537.26517a09e8247beb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8595-e9b7081ab773a61f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8821-30cc65a61a60951f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8905.b3385aadb7b133e6.js +79 -0
- package/apps/pwa/out/_next/static/chunks/8983.246bb3a1ac38f50f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b2f6ba4d.fb56fdbbf798df33.js → 8c58d09b.e8724a299658d86f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9072.18342f5941a1f3ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9123.9df04866b2d6e00f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1530.0e612369b6fd7ea0.js → 9151.a2c51e93f9156dae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4441-875b97f5821ed58a.js → 9175-7d383df15a82e2be.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9181-bf89c07ce5b8f599.js +1 -0
- package/apps/pwa/out/_next/static/chunks/920-429bbc4504b45f71.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9207.625edf45d656151c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9378.d2575c6ffd6dd756.js +94 -0
- package/apps/pwa/out/_next/static/chunks/957.db5b5c4d32a35240.js +1 -0
- package/apps/pwa/out/_next/static/chunks/958.53a8dcf4729e6997.js +64 -0
- package/apps/pwa/out/_next/static/chunks/9647.8f0cd0898c592a29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9665-26abb02665704c2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9703.e7d81b6faef1050a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9882-7a9164b3c2dacbe5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9998.b74e033efadecd10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ec2d8a4979ca9075.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cb6add08ad3c7660.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-e2eaa321ec223fe6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5b8a09c475b5f844.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-91a77daedfbac191.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-f37fc5e974bda39b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-91429a0b78415a94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-b95798b58263b910.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-763d53e175e17aa1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-d55023204055b12f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-89d2140d78abc810.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-82dc069abfe27f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-25d9eee7beb552c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-770b4594c713ee97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ba8e0dcae4f83985.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-5c112c6d7c33656e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-09788e1aa9a87db6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-3337c6a1328c2674.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-89acd5d68c0bc560.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5c13e2c76371d797.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-653a9f18642825c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-fe986fe79b107546.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-47c5d4db587266bd.js → page-1e5da297557d2354.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-91ea9f5869a40943.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-7824e21b999962e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-53d00a60cc75a922.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-1f5a60f70cb4dd0c.js → page-88a1edbff62eaf1d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e931248b3f9574b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-bac7dee94e5eae4a.js → page-443364c9e4a23b31.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-b9af2ac1c4737ce8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-25ab9c614c14129b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-dbcdcfac0a395a8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-bf1de6645089ec58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c4201761a5e839c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-c304f469ea5d70e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8e5a238cf5e50c9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-32b526069ee50b3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-fb9e639c107f5f72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-84220c776608cedc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-e475618cbfd5ca2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-dc0a532f5b9196ec.js → page-ac2c1e6169cfce57.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-fccd3020af2f8806.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-9c0d7198ff7b5a91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-d54b1ec9b9c6430d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8c4bef1791d732ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-46fcc0b0bbc6fbfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c1c80e345fa9d6f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7dfda00afd172f5b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{d06f9160.a3d9062f95de8ad7.js → d9815f84.4918f682a45a8502.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{cbe38a15.5a032598e39970c6.js → de8446d3.31b73eb239fecd7b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/f21fd540.63ac75328926ed30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-77fdcc873519a6cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-d0d620d2ce235713.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-e0ef79c79337ba90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-84b1bc71e3f9ce6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-2acb61f1363128cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-462ff5727ceb7824.js +1 -0
- package/apps/pwa/out/_next/static/css/{b5d88eba78a3d7bf.css → be9019f696217521.css} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/docs/goal-context/05-authoring-lessons.jsonl +47 -0
- package/docs/goal-context/README.md +3 -0
- package/graphs/default-loop.yaml +6 -1
- package/graphs/drafts/recipes.yaml +27 -7
- package/graphs/implement-loop.yaml +24 -0
- package/graphs/release/recipes.yaml +6 -0
- package/graphs/release/release-loop.yaml +17 -4
- package/graphs/wizard/recipes.yaml +44 -0
- package/graphs/wizard/wizard-loop.yaml +44 -0
- package/graphs/work/draft-close.yaml +54 -0
- package/graphs/work/green-close.yaml +37 -0
- package/graphs/work/recipes.yaml +46 -1
- package/graphs/work/research-report.yaml +2 -0
- package/graphs/work/run-research.yaml +35 -0
- package/package.json +1 -1
- package/scripts/__fixtures__/authoring-paths-10x3.json +32 -0
- package/scripts/boundary-proof-mapping-audit.ts +88 -0
- package/scripts/ci-typecheck-changed.ts +139 -5
- package/scripts/codex-fallback-live-probe.ts +17 -15
- package/scripts/coord-role-pack.ts +1 -1
- package/scripts/first10-gate.ts +416 -0
- package/scripts/fleet/node-setup.sh +32 -2
- package/scripts/fleet/run-light-worker.sh +9 -0
- package/scripts/fleet/wsl-node-setup.sh +432 -0
- package/scripts/gate-0226-failure-census.ts +507 -0
- package/scripts/goals/authoring-metrics.ts +258 -0
- package/scripts/graph-units/draft-close-node.ts +222 -0
- package/scripts/hook-order-sweep.ts +7 -4
- package/scripts/land-global-checks.ts +69 -1
- package/scripts/measure-nl-routing-corpus.ts +207 -2
- package/scripts/release-loop/docs-node.ts +344 -8
- package/scripts/release-loop/gate-closure.ts +48 -0
- package/scripts/release-loop/gate-diff.ts +2 -1
- package/scripts/release-loop/gate-node.ts +249 -53
- package/scripts/release-loop/known-issues-node.ts +6 -4
- package/scripts/release-loop/node-verdict.ts +2 -0
- package/scripts/release-loop/notes-repair.ts +216 -0
- package/scripts/release-loop/prepare-node.ts +61 -5
- package/scripts/release-loop/resume-cap.ts +14 -34
- package/scripts/release-loop/resume-release.ts +151 -15
- package/scripts/release-loop/resume-test-traffic.ts +10 -0
- package/scripts/release-loop/traffic-node.ts +59 -0
- package/scripts/release-loop/traffic-transition.ts +159 -0
- package/scripts/release-loop/version-node.ts +81 -12
- package/scripts/rhythm/daily.ts +137 -3
- package/scripts/train/green-leg.ts +176 -0
- package/scripts/tui-continue-real-evidence.ts +47 -0
- package/scripts/ux-sim.ts +53 -0
- package/skills/README.md +1 -0
- package/src/acp/client.ts +31 -5
- package/src/acp/core-turn-bridge.ts +54 -6
- package/src/acp/stderr-summary.ts +49 -0
- package/src/agent/delegate-hint.ts +16 -0
- package/src/agent/monad-agent-turn.ts +1 -0
- package/src/agent/runner.ts +82 -2
- package/src/agent/task-notification.ts +56 -10
- package/src/agent/terminal-surface.ts +217 -0
- package/src/agent/types.ts +4 -0
- package/src/agent-substrate/pr-reviewer.ts +64 -4
- package/src/agent-substrate/review-intent.ts +29 -2
- package/src/agent-substrate/review-observation.ts +3 -0
- package/src/agent-substrate/self-review-cli.ts +490 -26
- package/src/autopilot/lineage/sources.ts +1 -1
- package/src/autopilot/mission-codebase-gate.ts +141 -12
- package/src/autopilot/pr-manager.ts +89 -18
- package/src/boot/daemon-runtime.ts +35 -3
- package/src/boot/daemon-tools/pty-shell.ts +2 -1
- package/src/boot/daemon-tools/self-implement.ts +1 -0
- package/src/budget/run-usage-rollup.ts +12 -7
- package/src/budget/unified-usage.ts +20 -0
- package/src/budget/usage-outcomes.ts +8 -4
- package/src/capture/tui-self-report.ts +132 -1
- package/src/chat/goal-continue-verdict.ts +41 -0
- package/src/chat/index.ts +30 -4
- package/src/chat/internal-turn-origin.ts +1 -0
- package/src/chat/paste-fold.ts +86 -0
- package/src/chat/tool-render/block.ts +32 -0
- package/src/chat/tool-render/dispatch.ts +2 -20
- package/src/chat/tool-render/result-text.ts +98 -0
- package/src/chat/turn-typeahead.ts +70 -7
- package/src/cli/autopilot-cli.ts +24 -9
- package/src/cli/config-audit.ts +119 -0
- package/src/cli/decisions-cli.ts +7 -4
- package/src/cli/doctor-cli.ts +137 -6
- package/src/cli/guardian-cli.ts +10 -0
- package/src/cli/herdr-cli.ts +34 -0
- package/src/cli/light-entry-route.ts +6 -3
- package/src/cli/log-event-names.ts +2 -0
- package/src/cli/logs-cli.ts +357 -2
- package/src/cli/make-cli.ts +247 -11
- package/src/cli/memory-cli.ts +30 -1
- package/src/cli/msg-cli.ts +132 -14
- package/src/cli/pr-cli.ts +29 -16
- package/src/cli/pr-granularity.ts +5 -3
- package/src/cli/pr-land-base-read.ts +11 -2
- package/src/cli/pr-land-heavy-remote.ts +6 -3
- package/src/cli/pr-land-overlap-evidence.ts +23 -4
- package/src/cli/pr-land-recovery.ts +117 -1
- package/src/cli/pty-takeover-cli.ts +8 -7
- package/src/cli/release-checklist-read-cli.ts +109 -4
- package/src/cli/release-cli.ts +64 -15
- package/src/cli/release-single-flight.ts +1 -1
- package/src/cli/self-update.ts +23 -5
- package/src/cli/self-utterances-cli.ts +13 -16
- package/src/cli/tasks-card-show.ts +162 -0
- package/src/cli/tasks-cli.ts +868 -352
- package/src/cli/tasks-read-cli.ts +197 -0
- package/src/cli/usage-cli.ts +12 -10
- package/src/context-bus/claude-memory-import.ts +99 -0
- package/src/context-bus/long-term-memory.ts +125 -0
- package/src/core-turn/run-core-turn.ts +4 -2
- package/src/core-turn/run-goal-loop.ts +76 -16
- package/src/core-turn/types.ts +8 -1
- package/src/dashboard/chat/acp-chat.ts +9 -0
- package/src/dashboard/context/keys.ts +8 -31
- package/src/dashboard/index.ts +299 -62
- package/src/dashboard/input/agent-wake.ts +50 -1
- package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +146 -3
- package/src/dashboard/input/chat-main-slash-command.ts +4 -2
- package/src/dashboard/input/goal-continue.ts +210 -0
- package/src/dashboard/input/goal-interim.ts +2 -0
- package/src/dashboard/input/internal-turn.ts +2 -8
- package/src/dashboard/input/seat-autowake.ts +9 -7
- package/src/dashboard/input/turn-autoheal.ts +52 -0
- package/src/dashboard/replay-preview.ts +25 -0
- package/src/dashboard/seat-boot-preamble.ts +132 -5
- package/src/dashboard/slash-runtime/chat-persona.ts +65 -8
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -10
- package/src/dashboard/slash-runtime/model-choices.ts +15 -0
- package/src/dashboard/stale-install-check.ts +3 -99
- package/src/dashboard/synchronized-frame-output.ts +74 -0
- package/src/debug/batch-queue.ts +119 -0
- package/src/debug/tui-log-tag.ts +67 -0
- package/src/display/agent-surface.ts +7 -0
- package/src/display/coordinator.ts +45 -12
- package/src/display/resize-listener.ts +41 -20
- package/src/display/synchronized-output-region.ts +60 -0
- package/src/domains/standalone-log-sink.ts +15 -2
- package/src/domains/surface-events.ts +80 -1
- package/src/esc-abort-gate.ts +69 -0
- package/src/exec-requests/planner.ts +37 -2
- package/src/execution-loop/launch-gate.ts +4 -3
- package/src/git-fs/cli-stderr.ts +45 -0
- package/src/git-fs/gh-cli.ts +6 -3
- package/src/git-fs/git-cli.ts +11 -5
- package/src/git-fs/worktree.ts +95 -1
- package/src/graph-runner/graph-cli.ts +44 -2
- package/src/graph-runner/graph-run-api.ts +3 -2
- package/src/graph-runner/runner.ts +37 -16
- package/src/graph-wizard/generate.ts +131 -4
- package/src/graph-wizard/steps.ts +15 -0
- package/src/grounding/proactive-ground.ts +86 -0
- package/src/harness/control-inbox.ts +49 -1
- package/src/harness/harness-cli-command.ts +230 -40
- package/src/harness/harness-incidents.ts +52 -1
- package/src/harness/pod-control-send.ts +33 -5
- package/src/harness/review-adapter.ts +26 -6
- package/src/harness/self-send-remote.ts +69 -0
- package/src/harness/self-send-routing-guard.ts +6 -0
- package/src/harness/self-send-target.ts +4 -3
- package/src/index.ts +443 -25
- package/src/input-core/context-key-runtime.ts +21 -0
- package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
- package/src/light-cli.ts +6 -1
- package/src/llm/route-decision.ts +13 -0
- package/src/llm-tool-name.ts +37 -0
- package/src/llm-tool-schema.ts +35 -0
- package/src/llm-wellformed.ts +70 -0
- package/src/llm.ts +158 -15
- package/src/loops/checker.ts +100 -0
- package/src/loops/consensus.ts +26 -2
- package/src/loops/loop-cli.ts +9 -4
- package/src/loops/msg-wake.ts +35 -39
- package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
- package/src/loops/orchestrator/inbox.ts +59 -11
- package/src/loops/orchestrator/knowhow-pack.ts +364 -0
- package/src/loops/orchestrator/tick.ts +54 -5
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/model-tier/llm-tier-map.ts +12 -17
- package/src/models/prompts.ts +17 -2
- package/src/msg/msg-post-office.ts +116 -0
- package/src/msg/msg-store.ts +17 -0
- package/src/mss/logging/log-store.ts +79 -22
- package/src/native-tool-catalog.ts +1 -1
- package/src/nexus/api/decisions-route.ts +38 -18
- package/src/nexus/api/graph-wizard-api.ts +46 -3
- package/src/nexus/api/grid.ts +24 -4
- package/src/nexus/api/http-server.ts +1 -1
- package/src/nexus/api/ops-api.ts +14 -6
- package/src/nexus/boot/register-mcp-clients.ts +263 -38
- package/src/nexus/onboarding/first-boot-wizard.ts +56 -8
- package/src/nexus/state/state.ts +7 -1
- package/src/oauth/codex-account-store.ts +34 -1
- package/src/onboarding/first-run.ts +37 -6
- package/src/onboarding/web-first.ts +43 -11
- package/src/ops/loops-map.ts +46 -0
- package/src/ops/overview-cache.ts +22 -4
- package/src/ops/overview-collect.ts +12 -6
- package/src/ops/overview.ts +44 -8
- package/src/persona/loader.ts +11 -0
- package/src/persona/types.ts +8 -0
- package/src/policy/llm-policy.ts +3 -1
- package/src/prompt-enhance/enhance.ts +4 -4
- package/src/prompt-library/session-guidance.ts +14 -0
- package/src/provider-summary.ts +8 -0
- package/src/pty-shell/availability.ts +7 -0
- package/src/pty-shell/herdr-agent-report.ts +155 -37
- package/src/pty-shell/herdr-prompt.ts +266 -0
- package/src/pty-shell/herdr-prune-stale.ts +17 -0
- package/src/pty-shell/herdr-sequence-lease.ts +60 -0
- package/src/pty-shell/pty-control-ipc.ts +327 -15
- package/src/pty-shell/pty-special-keys.ts +42 -2
- package/src/pty-shell/registry.ts +25 -15
- package/src/pty-shell/stale-install-check.ts +99 -0
- package/src/pty-shell/tui-run-state.ts +249 -3
- package/src/release-loop/auto-start.ts +20 -2
- package/src/release-loop/checklist.ts +126 -9
- package/src/release-loop/feature-store.ts +9 -9
- package/src/release-loop/landed-but-yellow.ts +93 -12
- package/src/release-loop/rehearsal.ts +36 -20
- package/src/repl/index.ts +27 -1
- package/src/seat-loop/knowhow-lint.ts +1 -1
- package/src/seat-loop/seat-report.ts +1 -0
- package/src/self-dev/ask-launch-flow.ts +86 -10
- package/src/self-dev/decompose-proposal.ts +183 -20
- package/src/self-dev/decompose.ts +61 -0
- package/src/self-dev/dev-cli.ts +37 -3
- package/src/self-dev/dev-pipeline.ts +37 -9
- package/src/self-dev/draft-sweep.ts +241 -16
- package/src/self-dev/draft-triage-rules.ts +50 -5
- package/src/self-dev/glm-credential-guard.ts +77 -0
- package/src/self-dev/graph-journey-nodes.ts +1 -0
- package/src/self-dev/graph-journey-route.ts +12 -2
- package/src/self-dev/orchestrate-cli.ts +68 -3
- package/src/self-dev/orchestrate.ts +116 -17
- package/src/self-dev/review-provider-fallback.ts +45 -7
- package/src/self-dev/run-supervisor.ts +55 -2
- package/src/self-dev/run-types.ts +1 -1
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-run.ts +50 -3
- package/src/self-health/autoheal.ts +153 -0
- package/src/self-health/signatures.ts +6 -0
- package/src/self-implement/device-first.ts +116 -0
- package/src/self-implement/doctor-visit.ts +322 -1
- package/src/self-implement/evidence-command-budget.ts +80 -0
- package/src/self-implement/gate-artifacts.ts +6 -2
- package/src/self-implement/gate-baseline.ts +64 -1
- package/src/self-implement/gate-cli.ts +93 -9
- package/src/self-implement/gate-remote.ts +28 -4
- package/src/self-implement/gate-scope.ts +13 -1
- package/src/self-implement/goal-author-cli.ts +1 -0
- package/src/self-implement/goal-author-decompose.ts +265 -4
- package/src/self-implement/goal-author.ts +366 -26
- package/src/self-implement/goal-elaborate.ts +176 -22
- package/src/self-implement/goal-refine.ts +3 -0
- package/src/self-implement/goal-supervisor-fill.ts +75 -26
- package/src/self-implement/graph-variant.ts +40 -6
- package/src/self-implement/headless-elanous-driver.ts +19 -3
- package/src/self-implement/importer-test-index.ts +215 -2
- package/src/self-implement/knowhow-pack.ts +5 -5
- package/src/self-implement/off-diff-evidence.ts +58 -6
- package/src/self-implement/orchestrator.ts +536 -42
- package/src/self-implement/pod-warm-pool-profile.ts +1 -0
- package/src/self-implement/pwa-import-graph.ts +1 -1
- package/src/self-implement/review-round-brief.ts +300 -0
- package/src/self-implement/rework-policy.ts +44 -0
- package/src/self-implement/run-ledger.ts +278 -6
- package/src/self-implement/run-stop.ts +1 -1
- package/src/self-implement/run-ttl.ts +38 -24
- package/src/self-implement/running-runs.ts +17 -3
- package/src/self-implement/scope-adjacency.ts +3 -1
- package/src/self-implement/seams.ts +72 -10
- package/src/self-implement/stop-autoheal.ts +1 -0
- package/src/self-implement/test-seams.ts +13 -1
- package/src/self-implement/train-reach-grouping.ts +8 -2
- package/src/self-implement/wiring-check.ts +358 -0
- package/src/session/chat.ts +7 -1
- package/src/session/id-prefix.ts +60 -0
- package/src/session/index.ts +4 -0
- package/src/session-runtime/index.ts +9 -1
- package/src/shell-env-bootstrap.ts +11 -4
- package/src/skills/runner.ts +4 -2
- package/src/skills/tools/agent.ts +290 -48
- package/src/skills/tools/pty.ts +5 -0
- package/src/skills/tools/schedule-wakeup.ts +23 -4
- package/src/skills/tools/shell.ts +61 -6
- package/src/skills/tools/terminal-modal-inject.ts +1 -2
- package/src/skills/tools/terminal-modal.ts +10 -8
- package/src/skills/url-router.ts +41 -1
- package/src/smart-wizard/run.ts +66 -0
- package/src/status/bar.ts +34 -6
- package/src/status/metrics.ts +12 -1
- package/src/status/status-provider-info.ts +37 -6
- package/src/steward/rejudge-tick.ts +22 -7
- package/src/steward/resync-tick.ts +31 -4
- package/src/steward/ta-repair-event-collect.ts +17 -6
- package/src/task-agent/actions.ts +202 -5
- package/src/task-agent/boundary-proof-mapping.ts +12 -0
- package/src/task-agent/boundary-proof-producer.ts +147 -0
- package/src/task-agent/completion-evidence.ts +2 -1
- package/src/task-agent/green-close-recipe.ts +239 -0
- package/src/task-agent/green-sweep.ts +64 -10
- package/src/task-agent/land-queue.ts +21 -8
- package/src/task-agent/live-moves.ts +70 -9
- package/src/task-agent/map-host-adapter.ts +134 -0
- package/src/task-agent/map-reduce.ts +158 -0
- package/src/task-agent/map-run-ledger.ts +226 -0
- package/src/task-agent/mission.ts +30 -0
- package/src/task-agent/parent-host.ts +21 -11
- package/src/task-agent/rejudge-on-head.ts +87 -5
- package/src/task-agent/repair-cycle.ts +179 -32
- package/src/task-agent/repair-next-move.ts +42 -4
- package/src/task-agent/repair-outcomes.ts +29 -0
- package/src/task-agent/repair-worker.ts +14 -4
- package/src/task-agent/review-prior-fix.ts +12 -2
- package/src/task-agent/shadow.ts +229 -8
- package/src/task-agent/task-card-prefix.ts +2 -0
- package/src/task-agent/task-hand.ts +630 -30
- package/src/task-agent/task-parent-role-llm.ts +45 -0
- package/src/task-agent/tier-split.ts +80 -10
- package/src/task-agent/work-order.ts +53 -2
- package/src/task-orchestrator/dispatcher.ts +46 -1
- package/src/task-orchestrator/generator-prompt.ts +6 -0
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +9 -0
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
- package/src/task-orchestrator/surfaces/pod-child-reuse.ts +146 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +86 -5
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +215 -3
- package/src/task-orchestrator/surfaces/pod-launch-history.ts +133 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +8 -6
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +97 -0
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +11 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +126 -8
- package/src/task-orchestrator/surfaces/pod-provenance.ts +8 -2
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +18 -3
- package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +16 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +224 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +201 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-provider.ts +444 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-runner.ts +387 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-transport.ts +157 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +491 -36
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +3 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +612 -54
- package/src/task-orchestrator/surfaces/self-implement.ts +46 -2
- package/src/testing/linux-env.ts +177 -0
- package/src/tool-runtime/index.ts +15 -2
- package/src/tool-runtime/mcp-proxy-bootstrap.ts +29 -0
- package/src/tty-foreground.ts +155 -0
- package/src/tui/chat/goal-interim.ts +177 -0
- package/src/tui/chat/log-view.ts +205 -18
- package/src/tui/chat/self-health-status.ts +232 -0
- package/src/tui/entry.ts +623 -80
- package/src/tui/input/at-picker.ts +55 -0
- package/src/tui/input/composer.ts +8 -1
- package/src/tui/slash/compact-command.ts +7 -0
- package/src/tui/slash/compact.ts +1 -7
- package/src/tui/slash/resume.ts +98 -0
- package/src/tui/slash/signals.ts +114 -0
- package/src/tui/slash/term-next.ts +71 -0
- package/src/tui-client/local-chat-turn.ts +49 -5
- package/src/tui-client/next-dashboard-runtime.ts +8 -0
- package/src/tui-client/next-loop-command.ts +70 -0
- package/src/tui-client/next-wakeup.ts +191 -0
- package/src/tui.ts +37 -0
- package/src/ui/modal-adapter.ts +2 -2
- package/src/user-config.ts +19 -1
- package/src/ux-sim/boot-daemon-tools-pty-shell-device.ts +136 -0
- package/src/ux-sim/classic-tui-seat-scenarios.ts +670 -0
- package/src/ux-sim/cli-config-audit-device.ts +121 -0
- package/src/ux-sim/dashboard-event-loop-stall-monitor-device.ts +238 -0
- package/src/ux-sim/event-monkey.ts +3 -2
- package/src/ux-sim/fake-bin.ts +45 -0
- package/src/ux-sim/fake-gh.ts +174 -0
- package/src/ux-sim/fake-herdr.ts +238 -0
- package/src/ux-sim/fake-kubectl.ts +142 -0
- package/src/ux-sim/fake-llm.ts +116 -6
- package/src/ux-sim/fault-glm-no-credential.ts +116 -0
- package/src/ux-sim/fault-nextmove-dispatch.ts +102 -1
- package/src/ux-sim/fault-scope-escape-lift.ts +33 -6
- package/src/ux-sim/fault-tui-400-autoheal.ts +297 -0
- package/src/ux-sim/goal-device.ts +619 -0
- package/src/ux-sim/in-process-cli.ts +22 -5
- package/src/ux-sim/mock-authoring-first-run-child.ts +35 -0
- package/src/ux-sim/mock-authoring-first-run.ts +97 -0
- package/src/ux-sim/mock-seat-pty-frame-child.ts +29 -0
- package/src/ux-sim/mock-seat-pty-frame.ts +297 -0
- package/src/ux-sim/mock-seat-pty-sequence-child.ts +21 -0
- package/src/ux-sim/mock-seat-pty-sequence.ts +206 -0
- package/src/ux-sim/next-tui-seat-scenarios.ts +749 -0
- package/src/ux-sim/onboarding-first-run-device.ts +269 -0
- package/src/ux-sim/pty-key-modifiers-device.ts +85 -0
- package/src/ux-sim/resume-picker-device.ts +173 -0
- package/src/ux-sim/session-id-seed.ts +60 -0
- package/src/ux-sim/skills-tools-shell-device.ts +156 -0
- package/src/ux-sim/subagent-tools-device.ts +81 -0
- package/src/ux-sim/task-agent-parent-host-device.ts +264 -0
- package/src/ux-sim/task-agent-rejudge-on-head-device.ts +114 -0
- package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +250 -0
- package/src/ux-sim/tty-foreground-steal-child.ts +21 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +0 -41
- package/apps/pwa/out/_next/static/chunks/1674.2a65fd5668e1cc5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +0 -24
- package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +0 -74
- package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1993-c0007b7ad0c0ef41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +0 -148
- package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2625.5d870aa999f33db1.js +0 -34
- package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +0 -77
- package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +0 -62
- package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +0 -90
- package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +0 -36
- package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3677-4215f826f6aa68e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +0 -79
- package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4346.8601952e73bf5803.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +0 -24
- package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +0 -23
- package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +0 -94
- package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/56d372f2.681ab4ec4c998fae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +0 -287
- package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +0 -1
- package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +0 -8
- package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +0 -27
- package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +0 -93
- package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6b89c8bd.24df687a820500b3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +0 -149
- package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +0 -104
- package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8219.baa715d1deee80e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +0 -226
- package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +0 -64
- package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8637.47f5b35afcae5e6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +0 -115
- package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/967-2aa39b2d04b4c690.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-406b47745b53f269.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +0 -1
- /package/apps/pwa/out/_next/static/{T1mBQqmkwKDLGtyisDCOd → 8kITumG9GAZ1LwT9POrAR}/_ssgManifest.js +0 -0
|
@@ -21,7 +21,8 @@ import { POD_CREDENTIAL_TOKEN_ENV, POD_CREDENTIAL_URL_ENV } from '../../grok/cre
|
|
|
21
21
|
import { isVerifiedOpenGatewayModel } from '../../llm/opengateway-models.js';
|
|
22
22
|
import { configuredOpenGatewayKeys } from '../../llm/provider-credentials.js';
|
|
23
23
|
import { collectPodLedgers, createPodLedgerFollower } from './pod-ledger-collect.js';
|
|
24
|
-
import {
|
|
24
|
+
import { captureFailedPodJobs } from './pod-ledger-events.js';
|
|
25
|
+
import { collectPodArtifacts, podNoResultDiagnostic, POD_SALVAGE_BUNDLE_FILE_LIMIT, POD_SALVAGE_BUNDLE_TOTAL_LIMIT } from './pod-artifact-return.js';
|
|
25
26
|
import { readPodRepairArtifact } from './pod-repair-commit.js';
|
|
26
27
|
import { isValidAccountName, resolveCodexAccount } from '../../oauth/codex-account.js';
|
|
27
28
|
import { createCodexAccessMirror } from '../../oauth/codex-access-mirror.js';
|
|
@@ -30,13 +31,19 @@ import { controlInboxEnv } from '../../harness/control-inbox.js';
|
|
|
30
31
|
import { finishPodFragment, writePodFragment } from '../../harness/self-send-target.js';
|
|
31
32
|
import { mintRunId, normalizeRunId } from '../../harness/harness-space.js';
|
|
32
33
|
import { TA_LAND_ENV } from '../../task-agent/task-hand.js';
|
|
33
|
-
import { appendRunLedgerEntry, loadRunLedger, runLedgerDir, runLedgerPath } from '../../self-implement/run-ledger.js';
|
|
34
|
+
import { appendRunLedgerEntry, loadRunLedger, runLedgerDir, runLedgerPath, type RunLedgerEntry } from '../../self-implement/run-ledger.js';
|
|
35
|
+
import { recordRunStop } from '../../self-implement/run-stop.js';
|
|
34
36
|
import { runSuffixKey } from '../../self-implement/salvage-branches.js';
|
|
35
37
|
import type { GoalExecutionRecord } from '../../self-implement/orchestrator.js';
|
|
36
38
|
import { hostRepositoryUnavailable, runHostRegate, type HostRegateResult } from '../../self-implement/host-regate.js';
|
|
37
39
|
import { resolvePodHostAcpReview, runPodHostAcpReview, type PodHostAcpReviewOutcome } from './pod-host-acp-review.js';
|
|
38
40
|
import { REVIEW_DEFERRED_TO_HOST_ENV, REVIEW_DEFERRED_TO_HOST_MERGE_REASON } from '../../self-implement/review-deferred-to-host.js';
|
|
39
41
|
import { classifyPodTerminal, applyPodTerminal, podTerminalPrBody, type PrTerminalRecord, type DraftResidueContext } from '../../self-dev/draft-residue.js';
|
|
42
|
+
import { assessSiWarmReply, decideSiWarmNextStep, type SiWarmCommand, type SiWarmReply,
|
|
43
|
+
type SiWarmReturnedBytes } from './pod-si-warm-protocol.js';
|
|
44
|
+
import { cleanAndReturnChildPod, type ChildPodReuseOperations } from './pod-child-reuse.js';
|
|
45
|
+
import { executeSiWarmTransport, type SiWarmFixedRunner,
|
|
46
|
+
type SiWarmPodIdentity } from './pod-si-warm-transport.js';
|
|
40
47
|
|
|
41
48
|
export const POD_CONTROL_INBOX_DIR = '/tmp/elanous-control.inbox';
|
|
42
49
|
/** Pod Job 수명 상한(`activeDeadlineSeconds`) — 대표 2026-09-26 90분 → 180분(90분에 `DeadlineExceeded` 로 죽은 런 둘). */
|
|
@@ -59,14 +66,17 @@ export const POD_JOB_DEADLINE_SECONDS = 10_800;
|
|
|
59
66
|
// POD-DIET (10-06 03:47~05:18 · node-b goal Pods · 89 one-minute samples · 42 Pods): per-Pod observed max median 4.6Gi ·
|
|
60
67
|
// p75 6.2Gi · p90 11.7Gi — 23/42 went above the old 4Gi request, so the scheduler over-packed. 6Gi ≈ p75; limits stay.
|
|
61
68
|
// Node allocatable ≈ 343Gi ≫ 25 × 6Gi. One-minute samples can miss the true peak (lower bound).
|
|
62
|
-
import {
|
|
69
|
+
import { POD_LAUNCH_FINISHED_EVENT, POD_LAUNCH_STARTED_EVENT, provenanceRowFromAnnotations, siTaskPodProvenance } from './pod-launch-history.js';
|
|
70
|
+
import { podProvenanceMetadata, type PodProvenanceMetadata } from './pod-provenance.js';
|
|
71
|
+
import { packageVersion } from '../../version/code-revision.js';
|
|
72
|
+
import { minihLocationFrom, podWorkLabels, type PodWorkIdentity, type PodPlacement, type PodPlacementKind, type PodPoolMember, type PodPoolScheduler } from './pod-pool.js';
|
|
63
73
|
export { POD_CHILD_REQUESTS } from './pod-lease.js';
|
|
64
74
|
import { measurePoolLease, recommendConcurrency, POD_CHILD_REQUESTS, POD_SI_ADMISSION_CAP_BYTES, POD_HOST_LEASE_ANNOTATION, POD_JOB_TTL_AFTER_FINISHED_SECONDS, LEASE_KUBECTL_MAX_BUFFER, memoryQuantityBytes } from './pod-lease.js';
|
|
65
75
|
import { ACTUAL_SUBSTRATE_ENV, RUN_CONTRACT_ENV, carryRunContract, completionFloorFor } from '../../self-implement/graph-run-contract.js';
|
|
66
|
-
import { execFile, spawnSync } from 'node:child_process';
|
|
76
|
+
import { execFile, execFileSync, spawnSync } from 'node:child_process';
|
|
67
77
|
import { createHash } from 'node:crypto';
|
|
68
|
-
import { existsSync, mkdirSync, readFileSync, realpathSync } from 'node:fs';
|
|
69
|
-
import { homedir } from 'node:os';
|
|
78
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from 'node:fs';
|
|
79
|
+
import { homedir, tmpdir } from 'node:os';
|
|
70
80
|
import { isAbsolute, join, normalize, relative, resolve, sep } from 'node:path';
|
|
71
81
|
import { findGitDir } from '../../git-fs/locate.js';
|
|
72
82
|
import { hostLeaseBaseDir } from '../../pod-lease/host-lease.js';
|
|
@@ -97,7 +107,7 @@ import { ZAI_POD_EGRESS_LABEL, checkZaiPodSecret, countActiveZaiJobs, decideZaiP
|
|
|
97
107
|
import { ZAI_CODING_DEFAULT_MODEL, ZAI_OPENROUTER_FALLBACK_MODEL, zaiSettingsFromConfig, type ZaiCodingPlanSettings } from '../../llm/zai-coding-plan.js';
|
|
98
108
|
import { normalizePlanningFallbackReason, selectEnhanceLlm } from '../../prompt-enhance/enhance.js';
|
|
99
109
|
import { inferProviderFromModel } from '../../llm.js';
|
|
100
|
-
import { classifyPodExit, extractPodFailureReason, podTerminalRow, type PodExitClass } from './pod-failure-reason.js';
|
|
110
|
+
import { classifyPodExit, extractPodFailureReason, podChildFailureAttribution, podTerminalRow, POD_TERMINATION_JOB_JSONPATH, POD_TERMINATION_PODS_JSONPATH, readPodTermination, type PodExitClass, type PodTermination } from './pod-failure-reason.js';
|
|
101
111
|
import { kubectlTransientReason, retryKubectlTransient } from './kubectl-transient-retry.js';
|
|
102
112
|
import { OLD_DOOR_STAMP_ENV } from '../../self-dev/old-door.js';
|
|
103
113
|
import { resolveDefaultSourceHead, seedMirrorOnce, type MirrorSeedResult } from './pod-mirror-seed.js';
|
|
@@ -105,7 +115,22 @@ import { decideWarmReuse, type WarmReuseEvidenceByConsumer, type WarmReuseSwitch
|
|
|
105
115
|
import type { WarmWorkerCommand } from './pod-warm-worker-protocol.js';
|
|
106
116
|
import type { WarmPodTransportResult } from './pod-warm-worker-pod.js';
|
|
107
117
|
|
|
108
|
-
export type Kubectl = (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
|
|
118
|
+
export type Kubectl = (args: readonly string[], input?: string, timeoutMs?: number) => { status: number | null; stdout: string; stderr: string };
|
|
119
|
+
|
|
120
|
+
/** TC's si-v1 transport supplies this receipt. No lease is considered reusable from a Job exit alone. */
|
|
121
|
+
export interface SiWarmChildAttempt {
|
|
122
|
+
leaseFenced: boolean;
|
|
123
|
+
command: SiWarmCommand | null;
|
|
124
|
+
/** The consumer executes this fenced transport. A provider cannot assert its own reply verdict. */
|
|
125
|
+
transport: { pod: SiWarmPodIdentity; runner: SiWarmFixedRunner; now?: () => number };
|
|
126
|
+
payload: Uint8Array;
|
|
127
|
+
credentialBundle: Uint8Array;
|
|
128
|
+
/** Collect verified ledger/artifact bytes into the existing host stores. */
|
|
129
|
+
adopt: (command: SiWarmCommand, reply: SiWarmReply, bytes: SiWarmReturnedBytes) => Promise<void>;
|
|
130
|
+
cleanup: ChildPodReuseOperations;
|
|
131
|
+
/** The provider fences a worker even when a transport failure hides its command receipt. */
|
|
132
|
+
quarantineUnknown: (reason: string) => Promise<void>;
|
|
133
|
+
}
|
|
109
134
|
type OpenPrByBranch = { status: 'found'; prNumber: number; prUrl: string }
|
|
110
135
|
| { status: 'not-found' | 'failed'; reason: string };
|
|
111
136
|
|
|
@@ -123,6 +148,13 @@ const APP_GH_AUTH_COMMAND = `bun -e '${APP_GH_AUTH_SCRIPT}'`;
|
|
|
123
148
|
const APP_GH_EXEC = `unset GH_TOKEN GITHUB_TOKEN; GH_CONFIG_DIR="${APP_GH_CONFIG_PATH}" exec "$@"`;
|
|
124
149
|
|
|
125
150
|
export interface PodSpawnOptions {
|
|
151
|
+
/** si-v1 living worker consumer. Absent means the existing one-shot Job path. */
|
|
152
|
+
siWarmAttempt?: (input: Parameters<SelfImplementJobSpawn>[0]) => Promise<SiWarmChildAttempt | null>;
|
|
153
|
+
/** POD-REUSE-SI-PROVIDER-WIRE: must apply cold-path host regate, ACP, terminal and repair checks before warm adoption. */
|
|
154
|
+
siWarmHostPostprocess?: (result: SelfImplementJobDone, attempt: SiWarmChildAttempt,
|
|
155
|
+
input: Parameters<SelfImplementJobSpawn>[0]) => Promise<SelfImplementJobDone>;
|
|
156
|
+
/** Injected test ledger; production writes to the existing run ledger. */
|
|
157
|
+
siWarmLedger?: (entry: RunLedgerEntry) => void;
|
|
126
158
|
/** 이미지 판(라벨 elanous.commit) — 주입(시험·감독기가 이미 잰 값). 없으면 docker 로 잰다. */
|
|
127
159
|
imageCommit?: string | null;
|
|
128
160
|
/** codex 계정 이름(`~/.elanous/auth.json` 의 `openai-codex:<이름>`). 기본 team. */
|
|
@@ -198,6 +230,8 @@ export interface PodSpawnOptions {
|
|
|
198
230
|
/** LAUNCH-STALL: ms a launch may wait in one stage before `launch-stalled` (test seam; default harness.launchStallMinutes · 15 min). */
|
|
199
231
|
launchStallMs?: number;
|
|
200
232
|
kubectl?: Kubectl;
|
|
233
|
+
/** Capture SSH transport test seam; host selection and placement validation remain in the production path. */
|
|
234
|
+
failureCaptureRemote?: (host: string, args: readonly string[]) => ReturnType<Kubectl>;
|
|
201
235
|
/** Host-side regate (injected for Pod tests). */
|
|
202
236
|
hostRegate?: (input: { prNumber: number; headCommit: string; repoRoot: string; goalFile?: string; noMerge?: true }) => Promise<HostRegateResult>;
|
|
203
237
|
/** REVIEW-ACP-POD — Pod merge-ready PR 을 호스트 Claude ACP 로 리뷰한 뒤 재게이트(opt-in · 옵션 → env `ELANOUS_POD_HOST_ACP_REVIEW` → config `llm.podHostAcpReview`). */
|
|
@@ -774,6 +808,48 @@ export function podGithubWatchdogScript(staleSeconds: number, intervalSeconds =
|
|
|
774
808
|
].join('\n');
|
|
775
809
|
}
|
|
776
810
|
|
|
811
|
+
export function podSalvageBundleScript(): string {
|
|
812
|
+
return `if [ "\${rc:-0}" -ne 0 ] && grep -Eq 'draft PR 생성 실패|PR (gh|guard) 실패|same-cell reuse:' "\${ELANOUS_POD_CHILD_OUTPUT:-/tmp/si.out}"; then
|
|
813
|
+
mkdir -p "$HOME/outbox/pod-salvage"
|
|
814
|
+
found_commits=0
|
|
815
|
+
preservation_failed=0
|
|
816
|
+
bundle_bytes=0
|
|
817
|
+
worktree_rows=$(git worktree list --porcelain) || preservation_failed=1
|
|
818
|
+
while IFS= read -r wt; do
|
|
819
|
+
[ -n "$wt" ] || continue
|
|
820
|
+
git -C "$wt" rev-parse --is-inside-work-tree >/dev/null 2>&1 || { preservation_failed=1; continue; }
|
|
821
|
+
wt_name=$(basename -- "$wt")
|
|
822
|
+
case "$wt_name" in ''|.*|*/*|*'..'*) preservation_failed=1; continue ;; esac
|
|
823
|
+
base=$(git -C "$wt" rev-parse --verify '@{u}' 2>/dev/null || git -C "$wt" rev-parse --verify origin/HEAD 2>/dev/null || git -C "$wt" rev-list --max-parents=0 HEAD | tail -n 1)
|
|
824
|
+
head=$(git -C "$wt" rev-parse HEAD) || { preservation_failed=1; continue; }
|
|
825
|
+
[ -n "$base" ] || { preservation_failed=1; continue; }
|
|
826
|
+
commitCount=$(git -C "$wt" rev-list --count "$base..$head") || { preservation_failed=1; continue; }
|
|
827
|
+
if [ "$commitCount" -eq 0 ]; then continue; fi
|
|
828
|
+
found_commits=1
|
|
829
|
+
diffStats=$(git -C "$wt" diff --numstat "$base..$head") || { preservation_failed=1; continue; }
|
|
830
|
+
diffLines=$(printf '%s\\n' "$diffStats" | awk '{added += $1; removed += $2} END {print added + removed}')
|
|
831
|
+
bundle="$HOME/outbox/pod-salvage/$wt_name.bundle"
|
|
832
|
+
branch=$(git -C "$wt" symbolic-ref --quiet --short HEAD) || { preservation_failed=1; continue; }
|
|
833
|
+
if git -C "$wt" bundle create "$bundle" "refs/heads/$branch" && [ -s "$bundle" ]; then
|
|
834
|
+
size=$(( $(wc -c < "$bundle") ))
|
|
835
|
+
if [ "$size" -gt ${POD_SALVAGE_BUNDLE_FILE_LIMIT} ] || [ $((bundle_bytes + size)) -gt ${POD_SALVAGE_BUNDLE_TOTAL_LIMIT} ]; then
|
|
836
|
+
preservation_failed=1
|
|
837
|
+
rm -f -- "$bundle"
|
|
838
|
+
printf 'ELANOUS_POD_SALVAGE_FAILED size-limit %s %s %s\\n' "$wt_name" "$size" "$head"
|
|
839
|
+
else
|
|
840
|
+
bundle_bytes=$((bundle_bytes + size))
|
|
841
|
+
printf 'ELANOUS_POD_SALVAGE_BUNDLE pod-salvage/%s.bundle %s %s %s\\n' "$wt_name" "$commitCount" "$diffLines" "$head"
|
|
842
|
+
fi
|
|
843
|
+
else
|
|
844
|
+
preservation_failed=1
|
|
845
|
+
printf 'ELANOUS_POD_SALVAGE_FAILED bundle-create %s\\n' "$wt_name"
|
|
846
|
+
fi
|
|
847
|
+
done < <(printf '%s\\n' "$worktree_rows" | awk '/^worktree / {sub(/^worktree /, ""); print}')
|
|
848
|
+
if [ "$preservation_failed" -ne 0 ]; then printf 'ELANOUS_POD_SALVAGE_FAILED incomplete\\n'
|
|
849
|
+
elif [ "$found_commits" -eq 0 ]; then printf 'ELANOUS_POD_SALVAGE_NONE no-commits\\n'; fi
|
|
850
|
+
fi`;
|
|
851
|
+
}
|
|
852
|
+
|
|
777
853
|
export function podSalvageScript(): string {
|
|
778
854
|
return [
|
|
779
855
|
'if [ "${rc:-0}" -ne 0 ]; then',
|
|
@@ -1116,7 +1192,7 @@ export function podSigtermTrapScript(seqFile: string = POD_USAGE_PARTIAL_SEQ_FIL
|
|
|
1116
1192
|
return `trap '{ [ -n "$si_pid" ] && kill "$si_pid" 2>/dev/null || true; [ -n "\${usage_partial_pid:-}" ] && kill "$usage_partial_pid" 2>/dev/null || true; si_seq=$(cat ${seqFile} 2>/dev/null || true); case "$si_seq" in ""|*[!0-9]*) si_seq=0;; esac; si_seq=$((10#$si_seq + 1)); { [ -f scripts/usage-rollup.ts ] && bun scripts/usage-rollup.ts --since 12h --partial --seq "$si_seq"; } & si_rollup_pid=$!; for si_i in 1 2 3 4 5 6 7 8; do kill -0 "$si_rollup_pid" 2>/dev/null || break; sleep 1; done; kill "$si_rollup_pid" 2>/dev/null || true; wait "$si_rollup_pid" 2>/dev/null || true; exit 143; }' TERM`;
|
|
1117
1193
|
}
|
|
1118
1194
|
|
|
1119
|
-
export function podJobManifest(o: { name: string; namespace: string; image: string; workIdentity?: PodWorkIdentity; boundPrNumber?: number; /** 레지스트리 이미지면 IfNotPresent(노드가 pull) · 반입 이미지면 Never. */ imagePullPolicy?: 'Never' | 'IfNotPresent'; repoUrl: string; source?: PodSource; repairCommitOnly?: { pr: number; baseHead: string }; hostMirror?: string; args: readonly string[]; passEnv: readonly string[]; deadlineSeconds: number; runId?: string; parentRunId?: string; armEnv?: Readonly<Record<string, string>>; hostId?: string; imageCommit?: string | null; skillEnvs?: readonly string[]; memoryLimit?: string; memoryRequest?: string; goalDoc?: string; /** AUTHOR-POD2 — run `harness say` on /creds/feature inside the Pod (authoring happens off the host). */ authorSentence?: boolean; authorGrade?: 'full' | 'lite'; grokCredential?: 'subscription' | 'api_key'; codexAccounts?: readonly string[]; appCredential?: boolean; /** Test seam — default POD_GH_STALE_SECONDS. */ githubStaleSeconds?: number; /** Which goal execution and attempt this Job is — a resumed host verifies it before following the Job (POD9). */ execution?: { key: string; attempt: number }; hostLeaseAdmitted?: boolean; /** POD-ROLE-LLM — OpenRouter 자식 Pod 에 리뷰어용 codex 자격을 깐다. */ reviewerCodexAuth?: boolean; /** BEDROCK-PROVIDER ② — bedrock 자식만: 이름 있는 AWS Secret 의 키(secretKeyRef) ⊕ 리전 ⊕ 이그레스 라벨. */ bedrock?: BedrockPodSpec; /** ZAI-POD-CHILD — glm(ZAI) 자식만: 이름 있는 ZAI Secret 의 키(secretKeyRef) ⊕ 이그레스·동시 상한 라벨(Pod ⊕ Job). */ zai?: ZaiPodSpec; /** Codex Job may use the same named Secret only for an explicitly admitted ZAI fallback. */ zaiFallback?: ZaiPodSpec; fallbackChain?: { steps: readonly FallbackStep[]; source: 'host' | 'default' }; /** TA-POD-WARMBOOT-PARITY — the child's final line (podResultMarkerShellLine), just before `exit $rc`. */ resultMarkerLine?: string }): Record<string, unknown> {
|
|
1195
|
+
export function podJobManifest(o: { name: string; namespace: string; image: string; workIdentity?: PodWorkIdentity; boundPrNumber?: number; /** 레지스트리 이미지면 IfNotPresent(노드가 pull) · 반입 이미지면 Never. */ imagePullPolicy?: 'Never' | 'IfNotPresent'; repoUrl: string; source?: PodSource; repairCommitOnly?: { pr: number; baseHead: string }; hostMirror?: string; args: readonly string[]; passEnv: readonly string[]; deadlineSeconds: number; runId?: string; parentRunId?: string; armEnv?: Readonly<Record<string, string>>; hostId?: string; imageCommit?: string | null; skillEnvs?: readonly string[]; memoryLimit?: string; memoryRequest?: string; goalDoc?: string; /** AUTHOR-POD2 — run `harness say` on /creds/feature inside the Pod (authoring happens off the host). */ authorSentence?: boolean; authorGrade?: 'full' | 'lite'; grokCredential?: 'subscription' | 'api_key'; codexAccounts?: readonly string[]; appCredential?: boolean; /** Test seam — default POD_GH_STALE_SECONDS. */ githubStaleSeconds?: number; /** Which goal execution and attempt this Job is — a resumed host verifies it before following the Job (POD9). */ execution?: { key: string; attempt: number }; hostLeaseAdmitted?: boolean; /** POD-ROLE-LLM — OpenRouter 자식 Pod 에 리뷰어용 codex 자격을 깐다. */ reviewerCodexAuth?: boolean; /** BEDROCK-PROVIDER ② — bedrock 자식만: 이름 있는 AWS Secret 의 키(secretKeyRef) ⊕ 리전 ⊕ 이그레스 라벨. */ bedrock?: BedrockPodSpec; /** ZAI-POD-CHILD — glm(ZAI) 자식만: 이름 있는 ZAI Secret 의 키(secretKeyRef) ⊕ 이그레스·동시 상한 라벨(Pod ⊕ Job). */ zai?: ZaiPodSpec; /** Codex Job may use the same named Secret only for an explicitly admitted ZAI fallback. */ zaiFallback?: ZaiPodSpec; fallbackChain?: { steps: readonly FallbackStep[]; source: 'host' | 'default' }; /** TA-POD-WARMBOOT-PARITY — the child's final line (podResultMarkerShellLine), just before `exit $rc`. */ resultMarkerLine?: string; /** POD-LAUNCH-PROVENANCE — the shared provenance set (labels ⊕ annotations ⊕ child env) for this si-task Job. */ provenance?: PodProvenanceMetadata }): Record<string, unknown> {
|
|
1120
1196
|
if (o.repairCommitOnly && (o.source?.kind !== 'pr' || o.source.number !== o.repairCommitOnly.pr || !/^[0-9a-f]{40}$/.test(o.repairCommitOnly.baseHead) || o.args.includes('--open-pr') || o.args.includes('--merge-by-host') || o.args.includes('--auto-merge'))) {
|
|
1121
1197
|
throw new Error('repair commit-only requires matching PR source and no PR/merge flags');
|
|
1122
1198
|
}
|
|
@@ -1183,6 +1259,9 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
1183
1259
|
// Record that fact before the main command, without treating clone completion as install completion.
|
|
1184
1260
|
`printf 'ELANOUS_POD_DEPS skipped %s image-prepared-no-install\\n' "$(date +%s)" || true`,
|
|
1185
1261
|
...(o.repairCommitOnly ? [`[ "$(git rev-parse HEAD)" = '${o.repairCommitOnly.baseHead}' ] || { echo 'ELANOUS_POD_REPAIR_FAILED source-head-mismatch'; exit 8; }`] : []),
|
|
1262
|
+
// PR checkout and, when requested, the repair HEAD pin have passed before this child starts.
|
|
1263
|
+
// A supervisor input may continue the repair, but cannot launch its source again.
|
|
1264
|
+
...(o.source?.kind === 'pr' ? ['export ELANOUS_POD_VERIFIED_PR_SOURCE=1'] : []),
|
|
1186
1265
|
...(goalPath ? [
|
|
1187
1266
|
`mkdir -p -- "$(dirname -- ${goalPath})" && cp -- /creds/goal-doc ${goalPath} || exit 6`,
|
|
1188
1267
|
] : []),
|
|
@@ -1201,13 +1280,16 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
1201
1280
|
// AUTHOR-LITE2-POD — 등급은 환경변수 `ELANOUS_AUTHOR_GRADE` 로 싣는다(옛 Pod elanous 는 모르는 환경변수를 무시하고 full 로 돈다).
|
|
1202
1281
|
siTaskPodStageShell('run', 'start'),
|
|
1203
1282
|
o.repairCommitOnly
|
|
1204
|
-
? `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json --no-open-pr ${quoted} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; if
|
|
1283
|
+
? `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json --no-open-pr ${quoted} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; if elanous self repair-commit-export '${o.repairCommitOnly.pr}' '${o.repairCommitOnly.baseHead}' /tmp/si.out "$PWD" "$HOME/outbox"; then rc=0; elif [ "$rc" -eq 0 ]; then rc=8; fi`
|
|
1205
1284
|
: o.authorSentence
|
|
1206
1285
|
? `echo "ELANOUS_AUTHOR_ON_POD started host=$(hostname) at=$(date -u +%Y-%m-%dT%H:%M:%SZ)"; author_t0=$(date +%s); ${o.authorGrade === 'lite' || o.authorGrade === 'full' ? `ELANOUS_AUTHOR_GRADE=${o.authorGrade} ` : ''}elanous harness say --substrate local --json${quotedAsk}${quotedChildLlm} -- "$(cat /creds/feature)" > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; echo "ELANOUS_AUTHOR_ON_POD finished host=$(hostname) rc=$rc seconds=$(( $(date +%s) - author_t0 ))"; ${POD_RESULT_FLATTEN}`
|
|
1207
1286
|
: goalPath
|
|
1208
1287
|
? `elanous harness ask ${goalPath} --json${quotedAsk}${quotedChildLlm} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; ${POD_RESULT_FLATTEN}`
|
|
1209
1288
|
: `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json ${quoted} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; ${POD_RESULT_FLATTEN}`,
|
|
1210
1289
|
'cat /tmp/si.out',
|
|
1290
|
+
...(o.appCredential && !o.repairCommitOnly ? ['kill "$gh_watch_pid" 2>/dev/null || true'] : []),
|
|
1291
|
+
...(o.repairCommitOnly ? [] : [podSalvageScript()]),
|
|
1292
|
+
podSalvageBundleScript(),
|
|
1211
1293
|
'[ -f scripts/usage-rollup.ts ] && bun scripts/usage-rollup.ts --since 12h || echo "ELANOUS_USAGE_ROLLUP {\"measured\":false,\"reason\":\"no rollup script\"}"',
|
|
1212
1294
|
// A long run's log export can pass the 5MB artifact limit; skipping it whole left no window into why a
|
|
1213
1295
|
// 2h+ child failed (🅢 09-27). Keep the newest lines under the limit instead, and say so.
|
|
@@ -1224,23 +1306,27 @@ else
|
|
|
1224
1306
|
fi`,
|
|
1225
1307
|
`set -o pipefail
|
|
1226
1308
|
artifact_bytes=0
|
|
1309
|
+
salvage_bundle_bytes=0
|
|
1227
1310
|
if [ -d "$HOME/outbox" ]; then
|
|
1228
1311
|
while IFS= read -r -d '' file; do
|
|
1229
1312
|
[ -f "$file" ] && [ ! -L "$file" ] || continue
|
|
1230
1313
|
relative=\${file#"$HOME/outbox/"}
|
|
1231
1314
|
size=$(( $(wc -c < "$file") ))
|
|
1232
|
-
|
|
1315
|
+
salvage_bundle=0
|
|
1316
|
+
case "$relative" in pod-salvage/*.bundle) salvage_bundle=1 ;; esac
|
|
1317
|
+
if { [ "$salvage_bundle" -eq 0 ] && { [ "$size" -gt 5242880 ] || [ $((artifact_bytes + size)) -gt 20971520 ]; }; } || { [ "$salvage_bundle" -eq 1 ] && { [ "$size" -gt ${POD_SALVAGE_BUNDLE_FILE_LIMIT} ] || [ $((salvage_bundle_bytes + size)) -gt ${POD_SALVAGE_BUNDLE_TOTAL_LIMIT} ]; }; }; then
|
|
1233
1318
|
printf 'ELANOUS_POD_ARTIFACT_SKIPPED %s %s\\n' "$relative" "$size"
|
|
1234
1319
|
continue
|
|
1235
1320
|
fi
|
|
1236
1321
|
path_token=$(printf '%s' "$relative" | base64 | tr -d '\\n' | tr '+/' '-_' | tr -d '=')
|
|
1237
1322
|
if encoded=$(gzip -c "$file" | base64 | tr -d '\\n'); then
|
|
1238
|
-
artifact_bytes=$((artifact_bytes + size))
|
|
1323
|
+
if [ "$salvage_bundle" -eq 1 ]; then salvage_bundle_bytes=$((salvage_bundle_bytes + size)); else artifact_bytes=$((artifact_bytes + size)); fi
|
|
1239
1324
|
total=$(( (\${#encoded} + 7999) / 8000 ))
|
|
1240
|
-
|
|
1241
|
-
|
|
1325
|
+
n=1
|
|
1326
|
+
while IFS= read -r chunk || [ -n "$chunk" ]; do
|
|
1242
1327
|
printf 'ELANOUS_POD_ARTIFACT %s %s/%s %s\\n' "$path_token" "$n" "$total" "$chunk"
|
|
1243
|
-
|
|
1328
|
+
n=$((n + 1))
|
|
1329
|
+
done < <(printf '%s' "$encoded" | fold -w 8000)
|
|
1244
1330
|
else
|
|
1245
1331
|
printf 'ELANOUS_POD_ARTIFACT_SKIPPED %s %s\\n' "$relative" "$size"
|
|
1246
1332
|
fi
|
|
@@ -1263,12 +1349,10 @@ for ledger in "\${ELANOUS_STATE_DIR:-$HOME/.elanous}"/run-ledger/*.jsonl; do
|
|
|
1263
1349
|
done
|
|
1264
1350
|
if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
1265
1351
|
'kill "$mem_sampler_pid" 2>/dev/null || true',
|
|
1266
|
-
...(o.appCredential && !o.repairCommitOnly ? ['kill "$gh_watch_pid" 2>/dev/null || true'] : []),
|
|
1267
1352
|
'[ -n "${usage_partial_pid:-}" ] && kill "$usage_partial_pid" 2>/dev/null || true',
|
|
1268
1353
|
'wait "$mem_sampler_pid" 2>/dev/null || true',
|
|
1269
1354
|
'[ -n "${usage_partial_pid:-}" ] && wait "$usage_partial_pid" 2>/dev/null || true',
|
|
1270
1355
|
'tail -n 1 /tmp/si.out',
|
|
1271
|
-
...(o.repairCommitOnly ? [] : [podSalvageScript()]),
|
|
1272
1356
|
// TA-POD-WARMBOOT-PARITY: the last line says «the child is done» so the host need not wait for the Job condition.
|
|
1273
1357
|
...(o.resultMarkerLine ? [o.resultMarkerLine] : []),
|
|
1274
1358
|
'exit $rc',
|
|
@@ -1276,15 +1360,15 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
|
1276
1360
|
return {
|
|
1277
1361
|
apiVersion: 'batch/v1',
|
|
1278
1362
|
kind: 'Job',
|
|
1279
|
-
metadata: { name: o.name, namespace: o.namespace, labels: { 'elanous.substrate': 'pod', 'elanous.job': o.name, ...podRunLabels(o), ...(o.workIdentity ? podWorkLabels(o.workIdentity) : {}), ...(o.zai || o.zaiFallback ? { [ZAI_POD_EGRESS_LABEL]: 'true' } : {}) },
|
|
1280
|
-
...((o.execution || o.hostLeaseAdmitted) ? { annotations: { ...(o.execution ? { [POD_EXECUTION_KEY_ANNOTATION]: o.execution.key, [POD_ATTEMPT_ANNOTATION]: String(o.execution.attempt) } : {}), ...(o.hostLeaseAdmitted ? { [POD_HOST_LEASE_ANNOTATION]: 'true' } : {}) } } : {}) },
|
|
1363
|
+
metadata: { name: o.name, namespace: o.namespace, labels: { 'elanous.substrate': 'pod', 'elanous.job': o.name, ...podRunLabels(o), ...(o.workIdentity ? podWorkLabels(o.workIdentity) : {}), ...(o.zai || o.zaiFallback ? { [ZAI_POD_EGRESS_LABEL]: 'true' } : {}), ...o.provenance?.labels },
|
|
1364
|
+
...((o.execution || o.hostLeaseAdmitted || o.provenance) ? { annotations: { ...o.provenance?.annotations, ...(o.execution ? { [POD_EXECUTION_KEY_ANNOTATION]: o.execution.key, [POD_ATTEMPT_ANNOTATION]: String(o.execution.attempt) } : {}), ...(o.hostLeaseAdmitted ? { [POD_HOST_LEASE_ANNOTATION]: 'true' } : {}) } } : {}) },
|
|
1281
1365
|
spec: {
|
|
1282
1366
|
backoffLimit: 0,
|
|
1283
1367
|
ttlSecondsAfterFinished: POD_JOB_TTL_AFTER_FINISHED_SECONDS,
|
|
1284
1368
|
activeDeadlineSeconds: o.deadlineSeconds,
|
|
1285
1369
|
template: {
|
|
1286
1370
|
// ⭐ Pod 라벨 — local 팔만 `elanous.egress/local-llm` 을 단다. docker/h1/policy-local-llm.yaml 이 그 라벨에만 호스트 LLM 포트 «하나»를 연다.
|
|
1287
|
-
metadata: { labels: { 'elanous.job': o.name, ...podRunLabels(o), ...(o.workIdentity ? podWorkLabels(o.workIdentity) : {}), ...(o.armEnv?.ELANOUS_LLM_PROVIDER === 'local' ? { 'elanous.egress/local-llm': 'true' } : {}), ...(o.bedrock ? { [BEDROCK_POD_EGRESS_LABEL]: 'true' } : {}), ...(o.zai || o.zaiFallback ? { [ZAI_POD_EGRESS_LABEL]: 'true' } : {}) } },
|
|
1371
|
+
metadata: { labels: { 'elanous.job': o.name, ...podRunLabels(o), ...(o.workIdentity ? podWorkLabels(o.workIdentity) : {}), ...(o.armEnv?.ELANOUS_LLM_PROVIDER === 'local' ? { 'elanous.egress/local-llm': 'true' } : {}), ...(o.bedrock ? { [BEDROCK_POD_EGRESS_LABEL]: 'true' } : {}), ...(o.zai || o.zaiFallback ? { [ZAI_POD_EGRESS_LABEL]: 'true' } : {}), ...o.provenance?.labels }, ...(o.provenance ? { annotations: o.provenance.annotations } : {}) },
|
|
1288
1372
|
spec: {
|
|
1289
1373
|
restartPolicy: 'Never',
|
|
1290
1374
|
securityContext: { runAsUser: 1000, fsGroup: 1000 },
|
|
@@ -1311,6 +1395,8 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
|
1311
1395
|
{ name: 'ELANOUS_POD_NAMESPACE', valueFrom: { fieldRef: { fieldPath: 'metadata.namespace' } } },
|
|
1312
1396
|
...(o.hostId ? [{ name: 'ELANOUS_HOST_ID', value: o.hostId }] : []),
|
|
1313
1397
|
...(o.imageCommit ? [{ name: 'ELANOUS_IMAGE_COMMIT', value: o.imageCommit }] : []),
|
|
1398
|
+
// POD-LAUNCH-PROVENANCE — the child knows who launched it, why and when (ELANOUS_POD_<key> · no secrets).
|
|
1399
|
+
...(o.provenance?.env ?? []),
|
|
1314
1400
|
// ⭐ 런 계약(graph-run-contract.ts) — 이 칸이 «실제로» Pod 이고, 부모가 정한 계약도 Pod 다. 안의 그래프가 노드마다 대조한다.
|
|
1315
1401
|
{ name: ACTUAL_SUBSTRATE_ENV, value: 'pod' }, { name: RUN_CONTRACT_ENV, value: carryRunContract({ substrate: 'pod' }) },
|
|
1316
1402
|
...Object.entries(o.armEnv ?? {}).map(([name, value]) => ({ name, value })),
|
|
@@ -1421,6 +1507,14 @@ function readJobPodImage(kubectl: Kubectl, namespace: string, job: string, jobUi
|
|
|
1421
1507
|
return { podName: pod.metadata.name, podUid, podCreatedAt, imageID, digest, digestUnavailable: digest ? null : 'nonstandard-image-id' };
|
|
1422
1508
|
}
|
|
1423
1509
|
|
|
1510
|
+
/** A repair Pod succeeds only with a verified result/bundle, even if its child gate exited 1. */
|
|
1511
|
+
export function repairPodExitCode(input: { state: string; repairCommitOnly: boolean; repairArtifactReady: boolean;
|
|
1512
|
+
disposition?: { stage?: string; ok?: boolean } }): 0 | 1 {
|
|
1513
|
+
if (input.state !== 'complete') return 1;
|
|
1514
|
+
if (input.repairCommitOnly) return input.repairArtifactReady ? 0 : 1;
|
|
1515
|
+
return input.disposition?.stage !== 'host-regate-failed' && input.disposition?.ok !== false ? 0 : 1;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1424
1518
|
export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplementJobSpawn {
|
|
1425
1519
|
if (options.repairCommitOnly && (options.source?.kind !== 'pr' || options.source.number !== options.repairCommitOnly.pr || !/^[0-9a-f]{40}$/.test(options.repairCommitOnly.baseHead))) {
|
|
1426
1520
|
throw new Error('repair commit-only requires a matching PR source and exact head');
|
|
@@ -1436,10 +1530,290 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1436
1530
|
if (hostMirror && !isAbsolute(hostMirror)) throw new Error('pod.hostMirror must be an absolute directory path');
|
|
1437
1531
|
const hostSupervised = options.hostSupervised ?? true;
|
|
1438
1532
|
const resultMarkerOn = options.resultMarker ?? !options.kubectl;
|
|
1533
|
+
const writeWarmLedger = options.siWarmLedger
|
|
1534
|
+
?? ((entry: RunLedgerEntry) => appendRunLedgerEntry(entry, runLedgerDir(env.ELANOUS_STATE_DIR)));
|
|
1439
1535
|
return (input) => {
|
|
1440
1536
|
if (options.repairCommitOnly && (input.autoMerge || input.openPr || input.base !== `pr-${options.repairCommitOnly.pr}`)) {
|
|
1441
1537
|
throw new Error('repair commit-only refuses auto merge, new PR, or a different base');
|
|
1442
1538
|
}
|
|
1539
|
+
if (options.siWarmAttempt && !options.siWarmHostPostprocess) {
|
|
1540
|
+
throw new Error('si-v1 warm execution requires cold-path host postprocess parity');
|
|
1541
|
+
}
|
|
1542
|
+
if (options.siWarmAttempt) {
|
|
1543
|
+
const done = (async (): Promise<SelfImplementJobDone> => {
|
|
1544
|
+
let attempt: SiWarmChildAttempt;
|
|
1545
|
+
try {
|
|
1546
|
+
const acquired = await options.siWarmAttempt!(input);
|
|
1547
|
+
if (!acquired) {
|
|
1548
|
+
// No lease was fenced: use the existing one-shot Job path.
|
|
1549
|
+
const { siWarmAttempt: _unused, siWarmHostPostprocess: _postprocess, ...coldOptions } = options;
|
|
1550
|
+
return podSelfImplementSpawn(coldOptions)(input).done;
|
|
1551
|
+
}
|
|
1552
|
+
attempt = acquired;
|
|
1553
|
+
}
|
|
1554
|
+
catch (error) {
|
|
1555
|
+
// A lost exec reply may conceal an already opened PR. Never start another child here.
|
|
1556
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1557
|
+
debug.log('pod.reuse', 'attempt-unavailable', { spaceId: input.spaceId, reason: message }, { level: 'warn' });
|
|
1558
|
+
return { exitCode: 1, output: '', error: { code: 'si-warm-reconcile-only', message } };
|
|
1559
|
+
}
|
|
1560
|
+
let transport: Awaited<ReturnType<typeof executeSiWarmTransport>>;
|
|
1561
|
+
try {
|
|
1562
|
+
transport = await executeSiWarmTransport({ command: attempt.command,
|
|
1563
|
+
pod: attempt.transport.pod, runner: attempt.transport.runner,
|
|
1564
|
+
leaseFenced: attempt.leaseFenced, payload: attempt.payload,
|
|
1565
|
+
credentialBundle: attempt.credentialBundle, now: attempt.transport.now });
|
|
1566
|
+
} catch (error) {
|
|
1567
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1568
|
+
try { await attempt.quarantineUnknown('transport-exception'); }
|
|
1569
|
+
catch (quarantineError) {
|
|
1570
|
+
// Both failures must survive: the first transport error names the cause, the
|
|
1571
|
+
// quarantine error names the unfinished disposition. Record the judgement.
|
|
1572
|
+
const quarantineMessage = quarantineError instanceof Error ? quarantineError.message : String(quarantineError);
|
|
1573
|
+
const combined = `transport:${message}; quarantine:${quarantineMessage}`;
|
|
1574
|
+
try { writeWarmLedger({ runId: attempt.command?.runId ?? input.spaceId,
|
|
1575
|
+
event: 'pod-reuse-quarantine-failed',
|
|
1576
|
+
data: { spaceId: input.spaceId, workerId: attempt.command?.workerId ?? null,
|
|
1577
|
+
leaseId: attempt.command?.leaseId ?? null, reason: combined } }); }
|
|
1578
|
+
catch (ledgerError) { debug.log('pod.reuse', 'ledger-unavailable', { spaceId: input.spaceId,
|
|
1579
|
+
event: 'pod-reuse-quarantine-failed', reason: ledgerError instanceof Error ? ledgerError.message : String(ledgerError) }, { level: 'warn' }); }
|
|
1580
|
+
debug.log('pod.reuse', 'quarantine-failed', { spaceId: input.spaceId, reason: combined }, { level: 'error' });
|
|
1581
|
+
return { exitCode: 1, output: '', error: { code: 'si-warm-worker-state-unknown', message: combined } };
|
|
1582
|
+
}
|
|
1583
|
+
// The quarantine is confirmed: record the settled disposal (fail-soft — the
|
|
1584
|
+
// disposition survives a ledger write failure) before the reconcile-only return.
|
|
1585
|
+
try { writeWarmLedger({ runId: attempt.command?.runId ?? input.spaceId,
|
|
1586
|
+
event: 'pod-reuse-quarantined',
|
|
1587
|
+
data: { spaceId: input.spaceId, workerId: attempt.command?.workerId ?? null,
|
|
1588
|
+
leaseId: attempt.command?.leaseId ?? null, reason: 'transport-exception' } }); }
|
|
1589
|
+
catch (ledgerError) { debug.log('pod.reuse', 'ledger-unavailable', { spaceId: input.spaceId,
|
|
1590
|
+
event: 'pod-reuse-quarantined', reason: ledgerError instanceof Error ? ledgerError.message : String(ledgerError) }, { level: 'warn' }); }
|
|
1591
|
+
return { exitCode: 1, output: '', error: { code: 'si-warm-reconcile-only', message } };
|
|
1592
|
+
}
|
|
1593
|
+
const validation = transport.boundary === 'exec-attempted'
|
|
1594
|
+
? assessSiWarmReply(attempt.command, transport.reply, transport.bytes,
|
|
1595
|
+
attempt.payload, attempt.credentialBundle)
|
|
1596
|
+
: undefined;
|
|
1597
|
+
const next = decideSiWarmNextStep({ boundary: transport.boundary,
|
|
1598
|
+
leaseFenced: attempt.leaseFenced, ...(validation ? { validation } : {}) });
|
|
1599
|
+
debug.log('pod.reuse', 'admission-decision', { spaceId: input.spaceId, boundary: transport.boundary,
|
|
1600
|
+
next: next.next, reason: transport.reason, validationReason: transport.validationReason ?? null });
|
|
1601
|
+
if (next.next === 'cold-job' && transport.boundary === 'not-entered' && attempt.leaseFenced) {
|
|
1602
|
+
const { siWarmAttempt: _unused, ...coldOptions } = options;
|
|
1603
|
+
// The acquired lease must not silently ride along into a cold Job: dispose it first,
|
|
1604
|
+
// record the disposition, and refuse to start the cold Job when disposal fails.
|
|
1605
|
+
const coldLease = attempt.command ? { workerId: attempt.command.workerId,
|
|
1606
|
+
podUid: attempt.command.podUid, leaseId: attempt.command.leaseId,
|
|
1607
|
+
generation: attempt.command.generation, key: attempt.command.key,
|
|
1608
|
+
sourceSha: attempt.command.sourceSha } : null;
|
|
1609
|
+
if (coldLease) {
|
|
1610
|
+
const recordColdDisposition = (event: string, action: string, residue: Record<string, unknown> = {}) => {
|
|
1611
|
+
debug.log('pod.reuse', event, { spaceId: input.spaceId, workerId: coldLease.workerId,
|
|
1612
|
+
leaseId: coldLease.leaseId, action, reason: transport.reason, ...residue });
|
|
1613
|
+
try { writeWarmLedger({ runId: attempt.command?.runId ?? input.spaceId, event,
|
|
1614
|
+
data: { spaceId: input.spaceId, workerId: coldLease.workerId, leaseId: coldLease.leaseId,
|
|
1615
|
+
action, reason: transport.reason, ...residue } }); }
|
|
1616
|
+
catch (error) { debug.log('pod.reuse', 'ledger-unavailable', { spaceId: input.spaceId, event,
|
|
1617
|
+
reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
1618
|
+
};
|
|
1619
|
+
try {
|
|
1620
|
+
// The acquired lease is disposed by the same eligibility standard as the
|
|
1621
|
+
// entered path: process count zero, pinned-source git reset/clean/status,
|
|
1622
|
+
// credential count zero, and an observable return decision — before the
|
|
1623
|
+
// lease can go back into the warm pool or a cold Job starts.
|
|
1624
|
+
const returned = await cleanAndReturnChildPod(coldLease, { ...attempt.cleanup,
|
|
1625
|
+
observe: (event, data) => {
|
|
1626
|
+
debug.log('pod.reuse', event, { spaceId: input.spaceId, ...data },
|
|
1627
|
+
{ level: event === 'return-decision'
|
|
1628
|
+
&& ((data as { outcome?: string }).outcome === 'eligible'
|
|
1629
|
+
|| (data as { outcome?: string }).outcome === 'returned') ? 'info' : 'warn' });
|
|
1630
|
+
attempt.cleanup.observe(event, data);
|
|
1631
|
+
// A ledger write failure is an observation failure: the settled return
|
|
1632
|
+
// or quarantine disposition must survive it (fail-soft after the decision).
|
|
1633
|
+
try { writeWarmLedger({ runId: attempt.command?.runId ?? input.spaceId,
|
|
1634
|
+
event: `pod-reuse-${event}`,
|
|
1635
|
+
data: { spaceId: input.spaceId, ...data } }); }
|
|
1636
|
+
catch (ledgerError) { debug.log('pod.reuse', 'ledger-unavailable', { spaceId: input.spaceId,
|
|
1637
|
+
runId: attempt.command?.runId ?? input.spaceId, event: `pod-reuse-${event}`,
|
|
1638
|
+
reason: ledgerError instanceof Error ? ledgerError.message : String(ledgerError) },
|
|
1639
|
+
{ level: 'warn' }); }
|
|
1640
|
+
} });
|
|
1641
|
+
recordColdDisposition('pod-reuse-cold-fallback-dispose', returned.outcome,
|
|
1642
|
+
{ dispositionReason: returned.reason });
|
|
1643
|
+
} catch (error) {
|
|
1644
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1645
|
+
try { await attempt.cleanup.quarantine(coldLease, { clean: false, reason: 'cold-fallback-dispose-failed', observedResidue: null }); }
|
|
1646
|
+
catch (quarantineError) {
|
|
1647
|
+
const quarantineMessage = quarantineError instanceof Error ? quarantineError.message : String(quarantineError);
|
|
1648
|
+
recordColdDisposition('pod-reuse-cold-fallback-dispose-failed', 'quarantine-failed',
|
|
1649
|
+
{ error: `disposal:${message}; quarantine:${quarantineMessage}` });
|
|
1650
|
+
return { exitCode: 1, output: '', error: { code: 'si-warm-worker-state-unknown',
|
|
1651
|
+
message: `disposal:${message}; quarantine:${quarantineMessage}` } };
|
|
1652
|
+
}
|
|
1653
|
+
recordColdDisposition('pod-reuse-cold-fallback-dispose-failed', 'quarantined', { error: message });
|
|
1654
|
+
return { exitCode: 1, output: '', error: { code: 'si-warm-worker-state-unknown', message } };
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
return podSelfImplementSpawn(coldOptions)(input).done;
|
|
1658
|
+
}
|
|
1659
|
+
const command = attempt.command;
|
|
1660
|
+
const lease = command && { workerId: command.workerId, podUid: command.podUid,
|
|
1661
|
+
leaseId: command.leaseId, generation: command.generation, key: command.key,
|
|
1662
|
+
sourceSha: command.sourceSha };
|
|
1663
|
+
const recordQuarantine = (event: string, reason: string, residue: Record<string, unknown> = {}) => {
|
|
1664
|
+
debug.log('pod.reuse', event, { spaceId: input.spaceId, workerId: command?.workerId ?? null,
|
|
1665
|
+
leaseId: command?.leaseId ?? null, reason, ...residue }, { level: 'warn' });
|
|
1666
|
+
try { writeWarmLedger({ runId: command?.runId ?? input.spaceId, event,
|
|
1667
|
+
data: { spaceId: input.spaceId, workerId: command?.workerId ?? null,
|
|
1668
|
+
leaseId: command?.leaseId ?? null, reason, ...residue } }); }
|
|
1669
|
+
catch (error) { debug.log('pod.reuse', 'ledger-unavailable', { spaceId: input.spaceId,
|
|
1670
|
+
event, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
1671
|
+
};
|
|
1672
|
+
const retireLease = async (reason: string) => {
|
|
1673
|
+
recordQuarantine('pod-reuse-quarantine-intent', reason);
|
|
1674
|
+
if (!lease) {
|
|
1675
|
+
try { await attempt.quarantineUnknown(reason); }
|
|
1676
|
+
catch (error) {
|
|
1677
|
+
// The unsettled quarantine is an account of its own: keep both the
|
|
1678
|
+
// primary path reason and the quarantine cause in the run ledger,
|
|
1679
|
+
// then let the caller surface the unknown worker state.
|
|
1680
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1681
|
+
recordQuarantine('pod-reuse-quarantine-failed', `${reason}; quarantine:${message}`);
|
|
1682
|
+
throw error;
|
|
1683
|
+
}
|
|
1684
|
+
recordQuarantine('pod-reuse-quarantined', reason);
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
// An uncertain exec may still be running. Keep it fenced and revoke its run credentials.
|
|
1688
|
+
let processRemaining: number | null = null;
|
|
1689
|
+
let credentialRemaining: number | null = null;
|
|
1690
|
+
try { processRemaining = (await attempt.cleanup.stopChildProcesses(lease)).remaining; }
|
|
1691
|
+
catch (error) { debug.log('pod.reuse', 'process-stop-failed', { spaceId: input.spaceId,
|
|
1692
|
+
reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
1693
|
+
try { credentialRemaining = (await attempt.cleanup.removeRunCredentials(lease)).remaining; }
|
|
1694
|
+
catch (error) { debug.log('pod.reuse', 'credential-revoke-failed', { spaceId: input.spaceId,
|
|
1695
|
+
reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
1696
|
+
const observedResidue = [processRemaining, credentialRemaining]
|
|
1697
|
+
.filter((value): value is number => typeof value === 'number'
|
|
1698
|
+
&& Number.isSafeInteger(value) && value >= 0)
|
|
1699
|
+
.reduce((sum, value) => sum + value, 0);
|
|
1700
|
+
try {
|
|
1701
|
+
await attempt.cleanup.quarantine(lease, { clean: false, reason,
|
|
1702
|
+
observedResidue: observedResidue > 0 ? observedResidue : null });
|
|
1703
|
+
} catch (error) {
|
|
1704
|
+
// The lease stays unfenced and the worker state unknown: record the
|
|
1705
|
+
// unsettled disposal (primary reason plus the quarantine cause)
|
|
1706
|
+
// before the caller reports the unknown worker state.
|
|
1707
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1708
|
+
recordQuarantine('pod-reuse-quarantine-failed', `${reason}; quarantine:${message}`);
|
|
1709
|
+
throw error;
|
|
1710
|
+
}
|
|
1711
|
+
recordQuarantine('pod-reuse-quarantined', reason, { processRemaining, credentialRemaining });
|
|
1712
|
+
};
|
|
1713
|
+
if (next.next !== 'adopt-warm' || !transport.measured || !command
|
|
1714
|
+
|| !transport.reply || !transport.bytes) {
|
|
1715
|
+
const reconcileDisposition = transport.verifiedBytesForReconcile
|
|
1716
|
+
? parseSelfImplementJson(new TextDecoder().decode(transport.verifiedBytesForReconcile.terminal))
|
|
1717
|
+
: null;
|
|
1718
|
+
const hostDisposition = reconcileDisposition ? (({ worktreePath: _podOnly, ...host }) => host)(reconcileDisposition) : null;
|
|
1719
|
+
let quarantineError: string | undefined;
|
|
1720
|
+
try { await retireLease(validation?.reason ?? 'response-unknown'); }
|
|
1721
|
+
catch (error) { debug.log('pod.reuse', 'quarantine-failed', { spaceId: input.spaceId,
|
|
1722
|
+
reason: error instanceof Error ? error.message : String(error) }, { level: 'error' });
|
|
1723
|
+
quarantineError = error instanceof Error ? error.message : String(error); }
|
|
1724
|
+
return { exitCode: 1, output: '',
|
|
1725
|
+
...(hostDisposition ? { disposition: hostDisposition } : {}),
|
|
1726
|
+
error: { code: quarantineError
|
|
1727
|
+
|| (transport.quarantineWorker && !transport.quarantineRecorded)
|
|
1728
|
+
? 'si-warm-worker-state-unknown' : 'si-warm-reconcile-only',
|
|
1729
|
+
message: `si-v1 exec may have run; reconcile before retry (${transport.reason})${transport.quarantineError ? `; transport quarantine: ${transport.quarantineError}` : ''}${quarantineError ? `; worker-state-unknown: ${quarantineError}` : ''}` } };
|
|
1730
|
+
}
|
|
1731
|
+
const terminal = new TextDecoder().decode(transport.bytes.terminal);
|
|
1732
|
+
const disposition = parseSelfImplementJson(terminal);
|
|
1733
|
+
if (!disposition) {
|
|
1734
|
+
let quarantineError: string | undefined;
|
|
1735
|
+
try { await retireLease('terminal-disposition-unreadable'); }
|
|
1736
|
+
catch (error) { debug.log('pod.reuse', 'quarantine-failed', { spaceId: input.spaceId,
|
|
1737
|
+
reason: error instanceof Error ? error.message : String(error) }, { level: 'error' });
|
|
1738
|
+
quarantineError = error instanceof Error ? error.message : String(error); }
|
|
1739
|
+
return { exitCode: 1, output: '', error: { code: quarantineError
|
|
1740
|
+
? 'si-warm-worker-state-unknown' : 'si-warm-terminal-unreadable',
|
|
1741
|
+
message: quarantineError ?? 'validated si-v1 terminal was not a self-implement disposition' } };
|
|
1742
|
+
}
|
|
1743
|
+
try { await attempt.adopt(command, transport.reply, transport.bytes); }
|
|
1744
|
+
catch (error) {
|
|
1745
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1746
|
+
let quarantineError: string | undefined;
|
|
1747
|
+
try { await retireLease('adopt-failed'); }
|
|
1748
|
+
catch (quarantineFailure) { debug.log('pod.reuse', 'quarantine-failed', { spaceId: input.spaceId,
|
|
1749
|
+
reason: quarantineFailure instanceof Error ? quarantineFailure.message : String(quarantineFailure) }, { level: 'error' });
|
|
1750
|
+
quarantineError = quarantineFailure instanceof Error ? quarantineFailure.message : String(quarantineFailure); }
|
|
1751
|
+
debug.log('pod.reuse', 'adopt-failed', { spaceId: input.spaceId, reason: message }, { level: 'warn' });
|
|
1752
|
+
return { exitCode: 1, output: '', disposition, error: { code: quarantineError
|
|
1753
|
+
? 'si-warm-worker-state-unknown' : 'si-warm-reconcile-only', message: quarantineError ?? message } };
|
|
1754
|
+
}
|
|
1755
|
+
let returnOutcome: 'returned' | 'quarantined' = 'quarantined';
|
|
1756
|
+
let workerStateUnknown: string | undefined;
|
|
1757
|
+
try {
|
|
1758
|
+
const returned = await cleanAndReturnChildPod(lease!, { ...attempt.cleanup,
|
|
1759
|
+
observe: (event, data) => {
|
|
1760
|
+
debug.log('pod.reuse', event, { spaceId: input.spaceId, ...data },
|
|
1761
|
+
{ level: event === 'return-decision' && (data.outcome === 'eligible' || data.outcome === 'returned') ? 'info' : 'warn' });
|
|
1762
|
+
attempt.cleanup.observe(event, data);
|
|
1763
|
+
// The lease disposition is already settled by the time this callback runs; the warm
|
|
1764
|
+
// ledger is an observation of it. A ledger write failure must stay an observation
|
|
1765
|
+
// failure — escaping here would quarantine a lease that was already returned.
|
|
1766
|
+
try { writeWarmLedger({ runId: command.runId, event: `pod-reuse-${event}`,
|
|
1767
|
+
data: { spaceId: input.spaceId, ...data } }); }
|
|
1768
|
+
catch (error) { debug.log('pod.reuse', 'ledger-unavailable', { spaceId: input.spaceId, runId: command.runId,
|
|
1769
|
+
event: `pod-reuse-${event}`, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
1770
|
+
} });
|
|
1771
|
+
returnOutcome = returned.outcome;
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
workerStateUnknown = error instanceof Error ? error.message : String(error);
|
|
1774
|
+
debug.log('pod.reuse', 'return-unavailable', { spaceId: input.spaceId,
|
|
1775
|
+
reason: workerStateUnknown }, { level: 'error' });
|
|
1776
|
+
// The inner return/quarantine result is unclear: quarantine the lease explicitly,
|
|
1777
|
+
// and only a second quarantine failure leaves the worker state unknown.
|
|
1778
|
+
try {
|
|
1779
|
+
await attempt.quarantineUnknown(`return-quarantine-unavailable:${workerStateUnknown}`);
|
|
1780
|
+
try { writeWarmLedger({ runId: command!.runId, event: 'pod-reuse-quarantined',
|
|
1781
|
+
data: { spaceId: input.spaceId, workerId: command!.workerId, leaseId: command!.leaseId,
|
|
1782
|
+
reason: `return-quarantine-unavailable:${workerStateUnknown}` } }); }
|
|
1783
|
+
catch { /* ledger loss must not mask the quarantine outcome */ }
|
|
1784
|
+
// The explicit quarantine settled the disposal: the caller result must
|
|
1785
|
+
// account for it as quarantined instead of claiming an unknown state.
|
|
1786
|
+
workerStateUnknown = undefined;
|
|
1787
|
+
} catch (quarantineError) {
|
|
1788
|
+
const quarantineMessage = quarantineError instanceof Error ? quarantineError.message : String(quarantineError);
|
|
1789
|
+
workerStateUnknown = `return:${workerStateUnknown}; quarantine:${quarantineMessage}`;
|
|
1790
|
+
try { writeWarmLedger({ runId: command!.runId, event: 'pod-reuse-quarantine-failed',
|
|
1791
|
+
data: { spaceId: input.spaceId, workerId: command!.workerId, leaseId: command!.leaseId,
|
|
1792
|
+
reason: workerStateUnknown } }); }
|
|
1793
|
+
catch { debug.log('pod.reuse', 'ledger-unavailable', { spaceId: input.spaceId,
|
|
1794
|
+
event: 'pod-reuse-quarantine-failed', reason: workerStateUnknown }, { level: 'warn' }); }
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
const log = new TextDecoder().decode(transport.bytes.log);
|
|
1798
|
+
// Pod checkout paths cannot be used by the host supervisor.
|
|
1799
|
+
const { worktreePath: _podOnly, ...hostDisposition } = disposition;
|
|
1800
|
+
if (workerStateUnknown) return { exitCode: 1, output: log.slice(-4096), disposition: hostDisposition,
|
|
1801
|
+
error: { code: 'si-warm-worker-state-unknown', message: workerStateUnknown } };
|
|
1802
|
+
debug.log('pod.reuse', 'child-result', { spaceId: input.spaceId, workerId: command.workerId,
|
|
1803
|
+
leaseId: command.leaseId, returnOutcome, stage: disposition.stage ?? null });
|
|
1804
|
+
try {
|
|
1805
|
+
return await options.siWarmHostPostprocess!({ exitCode: transport.reply.exitCode,
|
|
1806
|
+
output: log.slice(-4096), disposition: hostDisposition }, attempt, input);
|
|
1807
|
+
} catch (error) {
|
|
1808
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1809
|
+
debug.log('pod.reuse', 'host-postprocess-failed', { spaceId: input.spaceId,
|
|
1810
|
+
reason: message }, { level: 'error' });
|
|
1811
|
+
return { exitCode: 1, output: log.slice(-4096), disposition: hostDisposition,
|
|
1812
|
+
error: { code: 'si-warm-host-postprocess-failed', message } };
|
|
1813
|
+
}
|
|
1814
|
+
})();
|
|
1815
|
+
return { address: `self-impl:${input.spaceId}`, done };
|
|
1816
|
+
}
|
|
1443
1817
|
const name = podJobName(input.spaceId);
|
|
1444
1818
|
let parentGoal: string | undefined;
|
|
1445
1819
|
let parentGoalUnreadable = false;
|
|
@@ -1574,13 +1948,16 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1574
1948
|
const acquireSlot = async (): Promise<PodPoolMember | null> => {
|
|
1575
1949
|
if (!options.pool) return null;
|
|
1576
1950
|
const stop = watchStall('pool-slot');
|
|
1951
|
+
// MINIH-MODE (0.2.37): the location is declared by the launch env — absent/unknown keeps the configured
|
|
1952
|
+
// capacities, so this launch path behaves exactly as before unless the declaration is there.
|
|
1953
|
+
const minihLocation = minihLocationFrom(env.ELANOUS_MINIH_LOCATION);
|
|
1577
1954
|
try {
|
|
1578
1955
|
for (;;) {
|
|
1579
1956
|
if (input.signal?.aborted) return null;
|
|
1580
1957
|
// The reason comes back inside this call, so a concurrent launch cannot overwrite it.
|
|
1581
1958
|
const kind = podPlacementKind(oomRetried ? retryTier : memoryTier, selectedGoalType, requestKind);
|
|
1582
1959
|
const acquired = await options.pool.tryAcquire(unfitContexts, (reason) => { stageReason['pool-slot'] = reason; },
|
|
1583
|
-
{ kind, onPlacement: (placement) => { lastPlacement = placement; } });
|
|
1960
|
+
{ kind, ...(minihLocation ? { minihLocation } : {}), onPlacement: (placement) => { lastPlacement = placement; } });
|
|
1584
1961
|
if (acquired) {
|
|
1585
1962
|
debug.log('self-implement.pod', 'pool-slot', { spaceId: input.spaceId, context: acquired.context, inflight: options.pool.snapshot(), ...(lastPlacement ? { placement: lastPlacement } : {}) });
|
|
1586
1963
|
if (lastPlacement) console.warn(`[pod] 배치: ${lastPlacement.reason}`);
|
|
@@ -1631,6 +2008,21 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1631
2008
|
const currentContext = member ? null : baseKubectl(['config', 'current-context']);
|
|
1632
2009
|
let context = member?.context ?? (currentContext?.status === 0 ? currentContext.stdout.trim() : '');
|
|
1633
2010
|
const kubectl: Kubectl = (args, stdin) => baseKubectl(['--context', context, ...args], stdin);
|
|
2011
|
+
const captureKubectl = (host: string, args: readonly string[]): ReturnType<Kubectl> => {
|
|
2012
|
+
if (member?.sshHost ? member.sshHost !== host : host !== resolveHostId(env))
|
|
2013
|
+
return { status: 1, stdout: '', stderr: 'ledger host is not the selected pool host' };
|
|
2014
|
+
if (!member?.sshHost) return baseKubectl(args);
|
|
2015
|
+
if (options.failureCaptureRemote) return options.failureCaptureRemote(host, args);
|
|
2016
|
+
const result = spawnSync('ssh', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', '--', host, 'kubectl', ...args],
|
|
2017
|
+
{ encoding: 'utf8', timeout: 30_000, maxBuffer: LEASE_KUBECTL_MAX_BUFFER });
|
|
2018
|
+
return { status: result.status, stdout: result.stdout ?? '', stderr: (result.stderr ?? '') + (result.error ? String(result.error) : '') };
|
|
2019
|
+
};
|
|
2020
|
+
const captureFailedJob = (childRunId: string): void => {
|
|
2021
|
+
try {
|
|
2022
|
+
captureFailedPodJobs({ runId: ledgerRunId, job: name, childRunId, ledgerPath: runLedgerPath(ledgerRunId, ledgerDir), artifactDir: join(effectiveInstanceRoot(), 'pod-artifacts'),
|
|
2023
|
+
kubectl: captureKubectl, record: (event, data) => appendRunLedgerEntry({ runId: ledgerRunId, event, data }, ledgerDir), now: options.now });
|
|
2024
|
+
} catch (error) { debug.log('self-implement.pod', 'failure-capture-ledger-unavailable', { job: name, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
2025
|
+
};
|
|
1634
2026
|
let recorded = false;
|
|
1635
2027
|
let groundingMinted: string | null = null;
|
|
1636
2028
|
try {
|
|
@@ -1672,6 +2064,44 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1672
2064
|
collectPodArtifacts(full.stdout, { dir: join(effectiveInstanceRoot(), 'pod-artifacts'), job: name, log: record });
|
|
1673
2065
|
}
|
|
1674
2066
|
catch (error) { record('self-implement.pod', 'artifact-collect-incomplete', { job: name, reason: error instanceof Error ? error.message : String(error) }); }
|
|
2067
|
+
for (const line of full.stdout.split(/\r?\n/)) {
|
|
2068
|
+
const bundle = /^ELANOUS_POD_SALVAGE_BUNDLE (pod-salvage\/[a-zA-Z0-9._-]+\.bundle) ([0-9]+) ([0-9]+)(?: ([0-9a-f]{40,64}))?$/.exec(line);
|
|
2069
|
+
if (bundle) {
|
|
2070
|
+
const bundlePath = join(effectiveInstanceRoot(), 'pod-artifacts', name, bundle[1]!);
|
|
2071
|
+
if (!existsSync(bundlePath)) {
|
|
2072
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-salvage-bundle', data: { job: name, disposition: '보존 실패', reason: 'bundle artifact missing' } }, ledgerDir); }
|
|
2073
|
+
catch (error) { record('self-implement.pod', 'salvage-bundle-ledger-incomplete', { job: name, reason: error instanceof Error ? error.message : String(error) }); }
|
|
2074
|
+
record('self-implement.pod', 'salvage-bundle-collect-incomplete', { job: name, path: bundlePath, reason: 'bundle artifact missing' });
|
|
2075
|
+
continue;
|
|
2076
|
+
}
|
|
2077
|
+
try {
|
|
2078
|
+
const refs = execFileSync('git', ['bundle', 'list-heads', bundlePath], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim().split(/\r?\n/);
|
|
2079
|
+
const [bundleHead, bundleRef] = refs[0]?.trim().split(/\s+/) ?? [];
|
|
2080
|
+
if (!bundleHead || !bundleRef?.startsWith('refs/heads/')) throw new Error('bundle contains no branch ref');
|
|
2081
|
+
const verifyDir = mkdtempSync(join(tmpdir(), 'pod-salvage-verify-'));
|
|
2082
|
+
try {
|
|
2083
|
+
execFileSync('git', ['clone', '--quiet', '--branch', bundleRef.slice('refs/heads/'.length), bundlePath, verifyDir], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
2084
|
+
const cloneHead = execFileSync('git', ['-C', verifyDir, 'rev-parse', 'HEAD'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
2085
|
+
if (cloneHead !== bundleHead || (bundle[4] && cloneHead !== bundle[4])) throw new Error('bundle clone HEAD mismatch');
|
|
2086
|
+
} finally { rmSync(verifyDir, { recursive: true, force: true }); }
|
|
2087
|
+
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-salvage-bundle', data: {
|
|
2088
|
+
job: name, path: bundlePath,
|
|
2089
|
+
commitCount: Number(bundle[2]), diffLines: Number(bundle[3]), ...(bundle[4] ? { head: bundle[4] } : {}),
|
|
2090
|
+
} }, ledgerDir);
|
|
2091
|
+
} catch (error) {
|
|
2092
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2093
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-salvage-bundle', data: { job: name, disposition: '보존 실패', reason: `bundle validation failed: ${reason}` } }, ledgerDir); }
|
|
2094
|
+
catch (ledgerError) { record('self-implement.pod', 'salvage-bundle-ledger-incomplete', { job: name, reason: ledgerError instanceof Error ? ledgerError.message : String(ledgerError) }); }
|
|
2095
|
+
record('self-implement.pod', 'salvage-bundle-collect-incomplete', { job: name, reason });
|
|
2096
|
+
}
|
|
2097
|
+
} else if (line === 'ELANOUS_POD_SALVAGE_NONE no-commits') {
|
|
2098
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-salvage-bundle', data: { job: name, disposition: '보존할 것 없음', commitCount: 0, diffLines: 0 } }, ledgerDir); }
|
|
2099
|
+
catch (error) { record('self-implement.pod', 'salvage-bundle-ledger-incomplete', { job: name, reason: error instanceof Error ? error.message : String(error) }); }
|
|
2100
|
+
} else if (line.startsWith('ELANOUS_POD_SALVAGE_FAILED ')) {
|
|
2101
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-salvage-bundle', data: { job: name, disposition: '보존 실패', reason: line.slice('ELANOUS_POD_SALVAGE_FAILED '.length) } }, ledgerDir); }
|
|
2102
|
+
catch (error) { record('self-implement.pod', 'salvage-bundle-ledger-incomplete', { job: name, reason: error instanceof Error ? error.message : String(error) }); }
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
1675
2105
|
if (options.repairCommitOnly) {
|
|
1676
2106
|
const artifactDir = join(effectiveInstanceRoot(), 'pod-artifacts', name, 'repair');
|
|
1677
2107
|
try {
|
|
@@ -1699,9 +2129,11 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1699
2129
|
// 상한 안 → ZAI(이름 있는 Secret · 없으면 거부) · 넘침 → 정책대로(openrouter·balance = 기존 OpenRouter GLM · stop = 거부 · apply 0).
|
|
1700
2130
|
let zaiRoute: 'zai' | 'openrouter' | undefined;
|
|
1701
2131
|
let zaiSpec: ZaiPodSpec | undefined;
|
|
2132
|
+
let zaiLaunchOverage: 'stop' | 'balance' | 'openrouter' | undefined;
|
|
1702
2133
|
zaiPendingApply.delete(name);
|
|
1703
2134
|
if (options.provider === 'glm') {
|
|
1704
2135
|
const settings = (options.zaiSettings ?? (() => zaiSettingsFromConfig(getUserConfig().llm)))();
|
|
2136
|
+
zaiLaunchOverage = settings.overage;
|
|
1705
2137
|
const poolMembers = (options.pool as { members?: readonly PodPoolMember[] } | undefined)?.members;
|
|
1706
2138
|
const contexts = Array.isArray(poolMembers) && poolMembers.length > 0 ? poolMembers.map((m) => m.context) : [context];
|
|
1707
2139
|
let active: number | null = 0;
|
|
@@ -1733,6 +2165,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1733
2165
|
const anthropic = options.provider === 'anthropic';
|
|
1734
2166
|
const bedrockChild = options.provider === 'bedrock';
|
|
1735
2167
|
const zaiChild = zaiRoute === 'zai';
|
|
2168
|
+
/** ZAI 로 입장한 자식도 플랜 한도 오류 뒤 OpenRouter 로 전환할 수 있다. 실행 중 폴백 자격을 발사 전에 확보한다. */
|
|
2169
|
+
const openrouterCredentialRequired = openrouter || (zaiChild && zaiLaunchOverage !== 'stop');
|
|
1736
2170
|
/** API 키 자식 — codex 계정·자격 파일을 안 쓴다(키는 작업별 creds Secret 의 `env-<KEY>` · bedrock·glm 은 이름 있는 Secret 으로만). */
|
|
1737
2171
|
const apiKeyChild = openrouter || openGateway || anthropic || bedrockChild || zaiChild;
|
|
1738
2172
|
const plannedAccount = retryAccount ?? (grok ? 'grok' : openrouter ? 'openrouter' : openGateway ? 'opengateway' : anthropic ? 'anthropic' : bedrockChild ? 'bedrock' : zaiChild ? 'glm' : options.accountBroker?.() ?? options.account ?? 'team');
|
|
@@ -1776,8 +2210,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1776
2210
|
}
|
|
1777
2211
|
return credential;
|
|
1778
2212
|
}) : undefined;
|
|
1779
|
-
const openrouterKey =
|
|
1780
|
-
if (
|
|
2213
|
+
const openrouterKey = openrouterCredentialRequired ? env.OPENROUTER_API_KEY?.trim() || (options.readKeyCache ?? defaultReadKeyCache)('OPENROUTER_API_KEY')?.trim() : undefined;
|
|
2214
|
+
if (openrouterCredentialRequired && !openrouterKey) throw new Error('pod: Pod 에 openrouter 키 없음 — 호스트 키 캐시 또는 OPENROUTER_API_KEY 필요');
|
|
1781
2215
|
const openGatewayEnvKey = openGateway ? env.OPENGATEWAY_API_KEY?.trim() : undefined;
|
|
1782
2216
|
const openGatewayCachedKey = openGateway ? (options.readKeyCache ?? defaultReadKeyCache)('OPENGATEWAY_API_KEY')?.trim() : undefined;
|
|
1783
2217
|
if (openGatewayEnvKey && openGatewayCachedKey && openGatewayEnvKey !== openGatewayCachedKey)
|
|
@@ -1859,7 +2293,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1859
2293
|
}
|
|
1860
2294
|
}
|
|
1861
2295
|
// Grok 자격은 전용 Secret 키 하나로만 보낸다 — passEnv 의 API 키 중복 전달은 막는다.
|
|
1862
|
-
const passKeys = [...new Set([...(options.passEnv ?? []), ...(
|
|
2296
|
+
const passKeys = [...new Set([...(options.passEnv ?? []), ...(openrouterCredentialRequired ? ['OPENROUTER_API_KEY'] : []), ...(openGateway ? ['OPENGATEWAY_API_KEY'] : []), ...(anthropic ? ['ANTHROPIC_API_KEY'] : [])])].filter((key) => key !== 'ELANOUS_LIVE_DETAIL_UNTIL'
|
|
1863
2297
|
&& key !== POD_GITHUB_CREDENTIAL_TOKEN_ENV && key !== POD_GITHUB_CREDENTIAL_URL_ENV
|
|
1864
2298
|
&& (!grok || !['XAI_API_KEY', 'GROK_API_KEY', 'GROK_CODE_XAI_API_KEY'].includes(key)));
|
|
1865
2299
|
const skillEnvs: Record<string, string> = options.skillEnv
|
|
@@ -1984,6 +2418,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1984
2418
|
if (key.startsWith('ELANOUS_PROMPT_ENHANCE_') && key !== 'ELANOUS_PROMPT_ENHANCE_MODEL' && key !== 'ELANOUS_PROMPT_ENHANCE_EFFORT') delete hostAuthorEnv[key];
|
|
1985
2419
|
}
|
|
1986
2420
|
let authorChoice = authorOnPod ? selectEnhanceLlm({ config: (options.authorConfig ?? getUserConfig)() }, hostAuthorEnv) : undefined;
|
|
2421
|
+
// A named child model is this launch's model when planning has no explicit choice.
|
|
2422
|
+
// Relay it to authoring too: the Pod's persistent grounding goal loop otherwise
|
|
2423
|
+
// receives the host's default "better" model and silently spends sol for a luna child.
|
|
2424
|
+
const namedChildModel = podDelegatedChildLlm(jobArgs)?.model;
|
|
2425
|
+
if (authorChoice?.modelSource === 'default' && namedChildModel) {
|
|
2426
|
+
authorChoice = { ...authorChoice, model: namedChildModel, modelSource: 'child' };
|
|
2427
|
+
}
|
|
1987
2428
|
let authorFallbackReason: string | undefined;
|
|
1988
2429
|
let authorRequestedModel: string | undefined;
|
|
1989
2430
|
let authorRequestedSource: string | undefined;
|
|
@@ -2005,8 +2446,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2005
2446
|
authorChoice = { ...authorChoice, model: childModel, modelSource: 'fallback-child' };
|
|
2006
2447
|
} else debug.log('self-implement.pod', 'role-llm-armed', { job: name, role: 'planning', model: authorChoice.model, source: authorChoice.modelSource, account: roleCodexAccount });
|
|
2007
2448
|
}
|
|
2008
|
-
const hostKey = (k: string): string | undefined => k === 'OPENROUTER_API_KEY' &&
|
|
2009
|
-
const
|
|
2449
|
+
const hostKey = (k: string): string | undefined => k === 'OPENROUTER_API_KEY' && openrouterCredentialRequired ? openrouterKey : k === 'OPENGATEWAY_API_KEY' && openGateway ? openGatewayKey : k === 'ANTHROPIC_API_KEY' && anthropic ? anthropicKey : env[k] ?? (options.readKeyCache ?? defaultReadKeyCache)(k);
|
|
2450
|
+
const hostConfig = (options.fallbackConfig ?? getUserConfig)();
|
|
2451
|
+
const hostFallbackNames = hostConfig.llm.fallbackChain;
|
|
2452
|
+
const zaiOverage = zaiLaunchOverage ?? zaiSettingsFromConfig(hostConfig.llm).overage;
|
|
2010
2453
|
const normalizedFallback = normalizeFallbackChain(hostFallbackNames);
|
|
2011
2454
|
let fallbackSteps = normalizedFallback.chain;
|
|
2012
2455
|
let zaiFallbackSpec: ZaiPodSpec | undefined;
|
|
@@ -2058,7 +2501,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2058
2501
|
? Object.fromEntries(codexCredentials.map((candidate, i) => [`codex-${i}.json`, JSON.stringify({ elanousAuth: candidate.elanousAuth, codexAuth: candidate.codexAuth })]))
|
|
2059
2502
|
: { 'elanous-auth.json': (creds as ReturnType<typeof hostCredentials>).elanousAuth, 'codex-auth.json': (creds as ReturnType<typeof hostCredentials>).codexAuth }),
|
|
2060
2503
|
'gh-token': podGhToken, feature: input.feature,
|
|
2061
|
-
'llm-fallback-config.json': JSON.stringify({ llm: { fallbackChain: fallbackChain.steps } }),
|
|
2504
|
+
'llm-fallback-config.json': JSON.stringify({ llm: { fallbackChain: fallbackChain.steps, zaiCodingPlan: { overage: zaiOverage } } }),
|
|
2062
2505
|
...(goalDocument !== undefined ? { 'goal-doc': goalDocument } : {}),
|
|
2063
2506
|
...Object.fromEntries(Object.entries(skillEnvs).map(([n, text]) => [`skillenv-${n}`, text])),
|
|
2064
2507
|
...(grounding ? { [`env-${GROUNDING_TOKEN_ENV}`]: grounding.token } : {}),
|
|
@@ -2125,6 +2568,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2125
2568
|
}
|
|
2126
2569
|
}
|
|
2127
2570
|
if (failedExisting) {
|
|
2571
|
+
captureFailedJob(liveChildRunId);
|
|
2128
2572
|
const pods = kubectl(['-n', namespace, 'get', 'pods', '-l', `job-name=${name}`, '-o', 'jsonpath={range .items[*]}{.metadata.creationTimestamp}{"\\t"}{range .status.containerStatuses[?(@.name=="child")]}{.state.terminated.reason}{"\\t"}{.state.terminated.exitCode}{end}{"\\n"}{end}']);
|
|
2129
2573
|
const latestPod = pods.status === 0 ? pods.stdout.trimEnd().split('\n').map((line) => line.split('\t'))
|
|
2130
2574
|
.filter(([created]) => created && !Number.isNaN(Date.parse(created)))
|
|
@@ -2313,7 +2757,22 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2313
2757
|
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 });
|
|
2314
2758
|
jobDeadlineSeconds = fit.deadlineSeconds;
|
|
2315
2759
|
}
|
|
2316
|
-
|
|
2760
|
+
// POD-LAUNCH-PROVENANCE — fail-closed: a si-task Job without its ★ identity is never applied.
|
|
2761
|
+
let provenance: PodProvenanceMetadata;
|
|
2762
|
+
try {
|
|
2763
|
+
let version: string | undefined;
|
|
2764
|
+
try { version = packageVersion(); } catch { version = undefined; }
|
|
2765
|
+
provenance = podProvenanceMetadata(siTaskPodProvenance({ env, runId: launchRunId, ...(parentRunId ? { parentRunId } : {}), workIdentity,
|
|
2766
|
+
requestedAt: new Date((options.now ?? Date.now)()).toISOString(), feature: input.feature, launchHost: resolveHostId(env),
|
|
2767
|
+
commit: seedSource.kind === 'commit' ? seedSource.sha : seedCommit ?? jobImageCommit, ...(version ? { version } : {}),
|
|
2768
|
+
...(memoryRequest ? { memReq: memoryRequest } : {}) }));
|
|
2769
|
+
} catch (error) {
|
|
2770
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2771
|
+
debug.log('pod.provenance', 'si-task-refused', { job: name, reason: message }, { level: 'warn' });
|
|
2772
|
+
cleanupSecret();
|
|
2773
|
+
return { exitCode: 1, output: '', error: { code: 'pod-provenance', message } };
|
|
2774
|
+
}
|
|
2775
|
+
const job = podJobManifest({ provenance, 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 를 부르지 않는다
|
|
2317
2776
|
const carriedBranch = goalDoc ? 'goal-doc' as const : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? 'author' as const : undefined;
|
|
2318
2777
|
const carriedChildLlm = carriedBranch ? podDelegatedChildLlm(jobArgs) : undefined;
|
|
2319
2778
|
jobApplyStartedAt = Date.now();
|
|
@@ -2322,6 +2781,11 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2322
2781
|
if (a.status !== 0) { cleanupSecret(); return { exitCode: 1, output: a.stderr, error: { code: 'pod-apply', message: a.stderr.trim() } }; }
|
|
2323
2782
|
releaseAdmission?.applied?.(name, context, namespace);
|
|
2324
2783
|
const imageFacts = { job: name, childRunId: liveChildRunId, image: jobImage, imageCommit: jobImageCommit, context };
|
|
2784
|
+
// POD-LAUNCH-PROVENANCE — append-only «started» row: who · why · when, on the parent's existing run ledger.
|
|
2785
|
+
const startedRow = { job: name, run: launchRunId, attempt: oomRetried ? 2 : 1, provenance: provenanceRowFromAnnotations(provenance.annotations) };
|
|
2786
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: POD_LAUNCH_STARTED_EVENT, timestamp: new Date((options.now ?? Date.now)()).toISOString(), data: startedRow }, ledgerDir); }
|
|
2787
|
+
catch (error) { debug.log('pod.provenance', 'history-unavailable', { job: name, row: 'started', reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
2788
|
+
debug.log('pod.provenance', 'job-started', startedRow);
|
|
2325
2789
|
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-job-image', data: imageFacts }, ledgerDir); }
|
|
2326
2790
|
catch (error) { debug.log('self-implement.pod', 'job-image-ledger-unavailable', { job: name, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
2327
2791
|
debug.log('self-implement.pod', 'job-image', imageFacts);
|
|
@@ -2380,6 +2844,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2380
2844
|
}
|
|
2381
2845
|
try {
|
|
2382
2846
|
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-child-run', data: { childRunId: liveChildRunId, job: name, attempt: oomRetried ? 2 : 1, ...(goalFile ? { goalFile } : {}) } }, ledgerDir);
|
|
2847
|
+
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-job-applied', data: { job: name, childRunId: liveChildRunId, jobUid: lifecycleJobUid, host: member?.sshHost ?? resolveHostId(env), context, namespace } }, ledgerDir);
|
|
2383
2848
|
if (lastPlacement) appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-placement', data: { job: name, childRunId: liveChildRunId, attempt: oomRetried ? 2 : 1, member: lastPlacement.member, kind: lastPlacement.kind, outcome: lastPlacement.outcome, preferred: lastPlacement.preferred, reason: lastPlacement.reason } }, ledgerDir);
|
|
2384
2849
|
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-memory-selected', data: { job: name, childRunId: liveChildRunId, attempt: oomRetried ? 2 : 1, limit: memoryLimit, ...(appliedMemoryRequest ? { request: appliedMemoryRequest } : {}), tier: oomRetried ? retryTier : memoryTier, source: oomRetried ? 'oom-retry' : memorySource, reason: oomRetried ? 'OOMKilled' : memoryReason, declaredGoalType: selectedGoalType } }, ledgerDir);
|
|
2385
2850
|
if (oomRetried && retryFromChildRunId) appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry', data: { attempt: 2, from: retryFromMemoryLimit ?? selectedMemoryLimit, to: memoryLimit, tier: retryTier, reason: 'OOMKilled', job: name, childRunId: liveChildRunId, fromChildRunId: retryFromChildRunId } }, ledgerDir);
|
|
@@ -2391,15 +2856,18 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2391
2856
|
}
|
|
2392
2857
|
writePodFragment({ spaceId: input.spaceId, context, namespace, job: name, inboxDir: POD_CONTROL_INBOX_DIR, ...(liveChildRunId === normalizeRunId(liveChildRunId) ? { runId: liveChildRunId } : {}), ...(parentRunId && normalizeRunId(parentRunId) === parentRunId ? { parentRunId } : {}) }, env);
|
|
2393
2858
|
recorded = true;
|
|
2394
|
-
debug.log('self-implement.pod', 'job-applied', { job: name, namespace, ...(member ? { context: member.context } : {}), image: jobImage, spaceId: input.spaceId, passEnv, ...(
|
|
2859
|
+
debug.log('self-implement.pod', 'job-applied', { job: name, namespace, ...(member ? { context: member.context } : {}), image: jobImage, spaceId: input.spaceId, passEnv, ...(openrouterCredentialRequired ? { 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 } : {}) });
|
|
2395
2860
|
let state: 'complete' | 'failed' | 'aborted' = 'failed';
|
|
2396
2861
|
let failedReason = '';
|
|
2397
2862
|
let containerReason: string | null = null;
|
|
2398
2863
|
let containerExitCode: number | null = null;
|
|
2864
|
+
// JOB-FAILURE-TERMINATION-CAPTURE — this Job's Kubernetes facts, read once at the failure verdict (before GC).
|
|
2865
|
+
// Observation only: failedReason · containerReason · containerExitCode keep driving the classification.
|
|
2866
|
+
let podTermination: PodTermination | null = null;
|
|
2399
2867
|
// METER-POD-ROLLUP-GAP — abort 때 Job 삭제 «전»에 거둔 로그 사본(삭제 뒤엔 PARTIAL 줄도 못 읽는다).
|
|
2400
2868
|
let abortLogs = '';
|
|
2401
2869
|
// ⭐ 런 «도중» 원장 증분 회수 — 호스트 슈퍼바이저가 Pod 걸음을 실시간으로 본다(🅣 요청 · 힐 루프 runtime 성형의 전제).
|
|
2402
|
-
const follower = createPodLedgerFollower({ runId: liveChildRunId, dir: ledgerDir, exec: (script) => kubectl(['-n', namespace, 'exec', `job/${name}`, '-c', 'child', '--', 'sh', '-c', script]) });
|
|
2870
|
+
const follower = createPodLedgerFollower({ runId: liveChildRunId, dir: ledgerDir, now, exec: (script) => kubectl(['-n', namespace, 'exec', `job/${name}`, '-c', 'child', '--', 'sh', '-c', script]) });
|
|
2403
2871
|
// ⭐ METER-POD-ROLLUP-GAP-3 — 런 «도중» 마지막 PARTIAL 줄을 호스트가 쥔다: Pod 가 지워지면(SIGTERM 등) 종료 로그에서 PARTIAL 이 사라진다.
|
|
2404
2872
|
let livePartialLine: string | null = null;
|
|
2405
2873
|
let pollsSinceCapture = 0;
|
|
@@ -2428,6 +2896,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2428
2896
|
const capturePolls = Math.max(1, options.usagePartialCapturePolls ?? Math.round(POD_USAGE_PARTIAL_CAPTURE_MS / Math.max(1, options.pollMs ?? 15_000)));
|
|
2429
2897
|
const markerWait = markerToken ? await import('./pod-result-marker-wait.js') : null;
|
|
2430
2898
|
const waitStartedAt = Date.now();
|
|
2899
|
+
let consecutiveJobNotFound = 0;
|
|
2900
|
+
let podVanished = false;
|
|
2901
|
+
let unmeasurableSince: number | null = null;
|
|
2902
|
+
let unmeasurableIncidentRecorded = false;
|
|
2431
2903
|
for (;;) {
|
|
2432
2904
|
if (input.signal?.aborted) {
|
|
2433
2905
|
recordPodImage(true);
|
|
@@ -2442,15 +2914,48 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2442
2914
|
}
|
|
2443
2915
|
if (!imageSnapshotRecorded) recordPodImage(false);
|
|
2444
2916
|
const g = kubectl(['-n', namespace, 'get', 'job', name, '-o', 'jsonpath={.status.conditions[*].type}']);
|
|
2917
|
+
if (g.status !== 0) {
|
|
2918
|
+
// A transport/5xx error says nothing about Job existence. Only Kubernetes Job NotFound counts.
|
|
2919
|
+
const jobNotFound = /\(NotFound\)/i.test(g.stderr) || /\bjobs?(?:\.batch)?\b[^\n]*\bnot found\b/i.test(g.stderr);
|
|
2920
|
+
consecutiveJobNotFound = jobNotFound ? consecutiveJobNotFound + 1 : 0;
|
|
2921
|
+
unmeasurableSince = null;
|
|
2922
|
+
if (consecutiveJobNotFound >= 3) {
|
|
2923
|
+
state = 'failed';
|
|
2924
|
+
failedReason = 'JobNotFound';
|
|
2925
|
+
podVanished = true;
|
|
2926
|
+
const facts = { job: name, childRunId: liveChildRunId, class: 'pod-vanished', consecutiveNotFound: consecutiveJobNotFound };
|
|
2927
|
+
debug.log('self-implement.pod', 'pod-vanished', facts, { level: 'error' });
|
|
2928
|
+
try {
|
|
2929
|
+
recordRunStop({ runId: ledgerRunId, class: 'pod-vanished', site: 'pod-job-poll',
|
|
2930
|
+
cause: `Job ${name} NotFound on ${consecutiveJobNotFound} consecutive polls`,
|
|
2931
|
+
evidenceRef: `run-ledger:${ledgerRunId}:pod-job-applied`,
|
|
2932
|
+
nextMove: 'Inspect Job deletion and child artifacts before relaunch' },
|
|
2933
|
+
(entry) => appendRunLedgerEntry(entry, ledgerDir));
|
|
2934
|
+
} catch (error) {
|
|
2935
|
+
debug.log('self-implement.pod', 'pod-vanished-ledger-unavailable', { ...facts, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
|
|
2936
|
+
}
|
|
2937
|
+
break;
|
|
2938
|
+
}
|
|
2939
|
+
await sleep(options.pollMs ?? 15_000);
|
|
2940
|
+
continue;
|
|
2941
|
+
}
|
|
2942
|
+
consecutiveJobNotFound = 0;
|
|
2445
2943
|
const types = g.stdout;
|
|
2446
2944
|
if (/Complete|SuccessCriteriaMet/.test(types)) { releaseAdmission?.observed?.(); state = 'complete'; break; }
|
|
2447
2945
|
if (/Failed|FailureTarget/.test(types)) {
|
|
2448
2946
|
releaseAdmission?.observed?.();
|
|
2449
2947
|
state = 'failed';
|
|
2450
|
-
|
|
2451
|
-
|
|
2948
|
+
// One Job read serves both: column one is the classification reason, all three the capture (no re-read a GC could empty).
|
|
2949
|
+
let jobRead: { status: number | null; stdout: string } | null = null;
|
|
2950
|
+
let jobReadError: unknown;
|
|
2951
|
+
try { jobRead = kubectl(['-n', namespace, 'get', 'job', name, '-o', `jsonpath=${POD_TERMINATION_JOB_JSONPATH}`]); }
|
|
2952
|
+
catch (error) { jobReadError = error; }
|
|
2953
|
+
failedReason = jobRead?.status === 0 ? (jobRead.stdout.split('\t')[0] ?? '').trim() : '';
|
|
2954
|
+
// One Pods read serves both: its first three columns are the classification layout, the rest the termination capture.
|
|
2955
|
+
let podsRead: { status: number | null; stdout: string } | null = null;
|
|
2452
2956
|
try {
|
|
2453
|
-
const pods = kubectl(['-n', namespace, 'get', 'pods', '-l', `job-name=${name}`, '-o',
|
|
2957
|
+
const pods = kubectl(['-n', namespace, 'get', 'pods', '-l', `job-name=${name}`, '-o', `jsonpath=${POD_TERMINATION_PODS_JSONPATH}`]);
|
|
2958
|
+
podsRead = pods;
|
|
2454
2959
|
if (pods.status === 0 && pods.stdout) {
|
|
2455
2960
|
const rows = pods.stdout.trimEnd().split('\n').map((line) => line.split('\t'));
|
|
2456
2961
|
const latest = rows.filter(([created]) => created && !Number.isNaN(Date.parse(created)))
|
|
@@ -2463,6 +2968,16 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2463
2968
|
}
|
|
2464
2969
|
} catch { /* Pod 가 사라졌거나 조회 불가 — 종료 사유는 미상. */ }
|
|
2465
2970
|
debug.log('self-implement.pod', 'container-terminated', { job: name, container: 'child', reason: containerReason, exitCode: containerExitCode, jobReason: failedReason || null, memoryLimit });
|
|
2971
|
+
podTermination = readPodTermination({ kubectl, namespace, job: name, path: 'job-failed-condition', jobRead, podsRead, jobUid: lifecycleJobUid });
|
|
2972
|
+
// Keep the verdict's single Job read authoritative; collecting files may re-read a Job that GC has removed.
|
|
2973
|
+
captureFailedJob(liveChildRunId);
|
|
2974
|
+
if (jobReadError !== undefined) {
|
|
2975
|
+
// Today's outcome stays (the throw ends the spawn as pod-error); the capture is in the ledger first.
|
|
2976
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-child-evidence', data: { childEvidence: null, childRunId: liveChildRunId, job: name, podTermination } }, ledgerDir); }
|
|
2977
|
+
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' }); }
|
|
2978
|
+
debug.log('self-implement.pod', 'pod-termination-captured', { job: name, podTermination, jobReadThrew: true }, { level: 'warn' });
|
|
2979
|
+
throw jobReadError;
|
|
2980
|
+
}
|
|
2466
2981
|
break;
|
|
2467
2982
|
}
|
|
2468
2983
|
// TA-POD-WARMBOOT-PARITY: the condition is not there yet — the child may already be done (k3d condition lag).
|
|
@@ -2474,6 +2989,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2474
2989
|
state = seen === 0 ? 'complete' : 'failed';
|
|
2475
2990
|
// The child's own rc (the Pod's terminated state may lag like the condition) — exit-code paths read it as before.
|
|
2476
2991
|
if (seen !== 0) containerExitCode = seen;
|
|
2992
|
+
if (seen !== 0) podTermination = readPodTermination({ kubectl, namespace, job: name, path: 'result-marker', jobUid: lifecycleJobUid });
|
|
2993
|
+
if (seen !== 0) captureFailedJob(liveChildRunId);
|
|
2477
2994
|
debug.log('self-implement.pod', 'result-marker-early', { job: name, waitedMs: Date.now() - waitStartedAt, rc: seen });
|
|
2478
2995
|
break;
|
|
2479
2996
|
}
|
|
@@ -2501,7 +3018,19 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2501
3018
|
debug.log('self-implement.pod', 'gh-token-refresh-failed', { job: name, reason: 'mint-verification-or-exec-failed' });
|
|
2502
3019
|
}
|
|
2503
3020
|
}
|
|
2504
|
-
|
|
3021
|
+
let measurement = { ledgerRead: false, activityMeasured: false };
|
|
3022
|
+
try { measurement = follower.poll(); } catch (e) { debug.log('self-implement.pod', 'ledger-live-unavailable', { job: name, reason: e instanceof Error ? e.message : String(e) }); }
|
|
3023
|
+
if (!measurement.ledgerRead && !measurement.activityMeasured) {
|
|
3024
|
+
unmeasurableSince ??= now();
|
|
3025
|
+
const unmeasurableMs = Math.max(0, now() - unmeasurableSince);
|
|
3026
|
+
if (!unmeasurableIncidentRecorded && unmeasurableMs >= 120 * 60_000) {
|
|
3027
|
+
unmeasurableIncidentRecorded = true;
|
|
3028
|
+
const incident = { job: name, childRunId: liveChildRunId, unmeasurableMs, jobStatus: 'alive', ledgerRead: false, activityMeasured: false };
|
|
3029
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-stall-unmeasurable-incident', data: incident }, ledgerDir); }
|
|
3030
|
+
catch (error) { debug.log('self-implement.pod', 'pod-stall-incident-ledger-unavailable', { job: name, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
3031
|
+
debug.log('self-implement.pod', 'pod-stall-unmeasurable-incident', incident, { level: 'warn' });
|
|
3032
|
+
}
|
|
3033
|
+
} else unmeasurableSince = null;
|
|
2505
3034
|
if (++pollsSinceCapture >= capturePolls) {
|
|
2506
3035
|
pollsSinceCapture = 0;
|
|
2507
3036
|
try {
|
|
@@ -2720,32 +3249,52 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2720
3249
|
}
|
|
2721
3250
|
let noResultDiagnostic: string | null = null;
|
|
2722
3251
|
// 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' &&
|
|
3252
|
+
const noResultCandidate = !podVanished && !disposition && !oomKilled && failedReason !== 'DeadlineExceeded' &&
|
|
2724
3253
|
(state === 'complete' || (state === 'failed' && containerExitCode !== 0));
|
|
2725
3254
|
if (noResultCandidate) {
|
|
2726
3255
|
try { noResultDiagnostic = podNoResultDiagnostic(readFileSync(runLedgerPath(liveChildRunId, ledgerDir), 'utf8')); }
|
|
2727
3256
|
catch { /* ledger not returned: do not invent a stage */ }
|
|
2728
3257
|
noResultDiagnostic ??= 'child terminal result missing; last ledger stage=unknown; round=unknown; mustFix=unknown';
|
|
2729
3258
|
}
|
|
2730
|
-
// POD-
|
|
2731
|
-
//
|
|
2732
|
-
//
|
|
3259
|
+
// POD-FAIL-EVIDENCE-CAPTURE: failed Pods are collected/deleted immediately after terminal handling.
|
|
3260
|
+
// Preserve a bounded log tail for every non-OOM/non-deadline failure, including known child stages:
|
|
3261
|
+
// the terminal result line can be followed by the useful stderr that explains the actual exit.
|
|
2733
3262
|
let noResultLogEvidence: Record<string, unknown> | null = null;
|
|
2734
|
-
|
|
3263
|
+
const shouldCaptureFailureLog = state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded';
|
|
3264
|
+
if (shouldCaptureFailureLog || noResultDiagnostic?.includes('stage=unknown')) {
|
|
3265
|
+
const missingStage = noResultDiagnostic?.includes('stage=unknown') ?? false;
|
|
2735
3266
|
const logRead = readPodNoResultLogTail({ run: kubectl, namespace, job: name, maxLines: POD_NO_RESULT_LOG_TAIL_LINES });
|
|
2736
3267
|
if ('tail' in logRead) {
|
|
2737
|
-
noResultLogEvidence =
|
|
2738
|
-
|
|
3268
|
+
noResultLogEvidence = missingStage
|
|
3269
|
+
? { stderr: logRead.tail.join('\n'), logTail: logRead.tail, logTailLines: logRead.tail.length }
|
|
3270
|
+
: { podLogTail: logRead.tail, podLogTailLines: logRead.tail.length };
|
|
3271
|
+
debug.log('self-implement.pod', missingStage ? 'no-result-log-tail' : 'failed-pod-log-tail', {
|
|
3272
|
+
job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic, lines: logRead.tail.length, logTail: logRead.tail,
|
|
3273
|
+
}, { compact: { stringMax: 500 } });
|
|
2739
3274
|
} else {
|
|
2740
|
-
noResultLogEvidence =
|
|
2741
|
-
|
|
3275
|
+
noResultLogEvidence = missingStage
|
|
3276
|
+
? { stderr: `log-unavailable: ${logRead.unavailable}` }
|
|
3277
|
+
: { podLogUnavailable: logRead.unavailable };
|
|
3278
|
+
debug.log('self-implement.pod', missingStage ? 'no-result-log-tail' : 'failed-pod-log-tail', {
|
|
3279
|
+
job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic, logUnavailable: logRead.unavailable,
|
|
3280
|
+
}, { level: 'warn' });
|
|
2742
3281
|
}
|
|
2743
3282
|
}
|
|
2744
3283
|
if (childFailure) debug.log('self-implement.pod', 'child-error', { job: name, stage: childFailure.stage, error: childFailure.error, ...(childPrFailure ?? {}) }, { compact: { stringMax: 500 } });
|
|
2745
3284
|
if (noResultDiagnostic && !noResultDiagnostic.includes('stage=unknown')) {
|
|
2746
3285
|
debug.log('self-implement.pod', 'no-result-ledger-diagnostic', { job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic }, { compact: { stringMax: 500 } });
|
|
2747
3286
|
}
|
|
3287
|
+
// POD-LAUNCH-PROVENANCE — append-only «finished» row paired with its started row on job·run.
|
|
3288
|
+
// Keyed on launchRunId like the started row; a reattached Job's inherited child run rides along as childRun.
|
|
3289
|
+
// exitCode stays null when the container's code was not read (never filled in as 0).
|
|
3290
|
+
const finishedRow = { job: name, run: launchRunId, ...(liveChildRunId !== launchRunId ? { childRun: liveChildRunId } : {}),
|
|
3291
|
+
attempt: oomRetried ? 2 : 1, phase: state, exitCode: containerExitCode,
|
|
3292
|
+
reason: failedReason || containerReason || (state === 'complete' ? 'complete' : 'unknown') };
|
|
3293
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: POD_LAUNCH_FINISHED_EVENT, timestamp: new Date((options.now ?? Date.now)()).toISOString(), data: finishedRow }, ledgerDir); }
|
|
3294
|
+
catch (error) { debug.log('pod.provenance', 'history-unavailable', { job: name, row: 'finished', reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
3295
|
+
debug.log('pod.provenance', 'job-finished', finishedRow);
|
|
2748
3296
|
debug.log('self-implement.pod', 'job-finished', { job: name, ...(member ? { context: member.context } : {}), state, containerReason, stage: disposition?.stage ?? null, prUrl: disposition?.prUrl ?? null, childRunId: liveChildRunId, ledgerCompleteness,
|
|
3297
|
+
...(podTermination ? { podTermination } : {}),
|
|
2749
3298
|
...(state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
2750
3299
|
? { ...(childFailure ? { childStage: childFailure.stage } : disposition?.stage ? { childStage: disposition.stage } : {}),
|
|
2751
3300
|
// 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).
|
|
@@ -2820,7 +3369,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2820
3369
|
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry-finished', data: { job: name, executionKey, fromChildRunId: retryFromChildRunId, childRunId: liveChildRunId, state } }, ledgerDir);
|
|
2821
3370
|
}
|
|
2822
3371
|
if (goalFile) {
|
|
2823
|
-
const noResultOutcome = state === 'failed' && containerExitCode !== null && noResultDiagnostic ? 'abandoned' : 'pod-no-result';
|
|
3372
|
+
const noResultOutcome = podVanished || (state === 'failed' && containerExitCode !== null && noResultDiagnostic) ? 'abandoned' : 'pod-no-result';
|
|
2824
3373
|
const record: GoalExecutionRecord = {
|
|
2825
3374
|
runId: liveChildRunId,
|
|
2826
3375
|
stage: (state === 'aborted' || !disposition ? 'aborted' : disposition.stage) as GoalExecutionRecord['stage'],
|
|
@@ -2837,8 +3386,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2837
3386
|
if (state === 'failed' || state === 'aborted' || noResultLogEvidence || (state === 'complete' && noResultDiagnostic !== null)) {
|
|
2838
3387
|
// Every terminal attempt writes a marker, including one with no parseable child facts.
|
|
2839
3388
|
// The next attempt must never inherit an earlier Pod's classification.
|
|
2840
|
-
const childEvidence = noResultLogEvidence
|
|
2841
|
-
|
|
3389
|
+
const childEvidence = noResultLogEvidence
|
|
3390
|
+
? { ...(disposition?.childEvidence ?? {}), ...noResultLogEvidence }
|
|
3391
|
+
: disposition?.childEvidence ?? null;
|
|
3392
|
+
try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-child-evidence', data: { childEvidence, childRunId: liveChildRunId, job: name, ...(podTermination ? { podTermination } : {}) } }, ledgerDir); }
|
|
2842
3393
|
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' }); }
|
|
2843
3394
|
}
|
|
2844
3395
|
let prLookup: OpenPrByBranch | undefined;
|
|
@@ -2846,7 +3397,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2846
3397
|
if (disposition && (state === 'failed' || state === 'aborted' || disposition.ok === false)
|
|
2847
3398
|
&& (!Number.isSafeInteger(disposition.prNumber) || (disposition.prNumber ?? 0) <= 0) && terminalBranch) {
|
|
2848
3399
|
try { prLookup = await (options.lookupOpenPrByBranch ?? defaultLookupOpenPrByBranch)(terminalBranch, repoUrl); }
|
|
2849
|
-
catch { prLookup = { status: 'failed', reason:
|
|
3400
|
+
catch (error) { prLookup = { status: 'failed', reason: `pr-lookup-failed: ${error instanceof Error ? error.message : String(error)}`.slice(0, 200) }; }
|
|
2850
3401
|
}
|
|
2851
3402
|
const terminalDisposition = prLookup?.status === 'found' && disposition
|
|
2852
3403
|
? { ...disposition, prNumber: prLookup.prNumber, prUrl: prLookup.prUrl } : disposition;
|
|
@@ -2888,11 +3439,14 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2888
3439
|
const finishedDisposition = deadlineExceeded && disposition
|
|
2889
3440
|
? { ...disposition, failureClassification: 'run-deadline-exceeded' as const }
|
|
2890
3441
|
: disposition;
|
|
2891
|
-
|
|
3442
|
+
// A gate environment deficit can exit the child with 1 while still yielding a
|
|
3443
|
+
// verified repair bundle. The host rejudges the new PR head after CAS apply.
|
|
3444
|
+
const podExitCode = podVanished ? 1 : repairPodExitCode({ state, repairCommitOnly: !!options.repairCommitOnly,
|
|
3445
|
+
repairArtifactReady, disposition });
|
|
2892
3446
|
// SCHED-CONTRACT: a failed child must say why outside the Pod, not «no error diagnostic».
|
|
2893
3447
|
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
3448
|
const podReason = rawPodReason
|
|
2895
|
-
? noResultLogEvidence ? boundedPodLogText(rawPodReason, POD_NO_RESULT_LOG_REASON_MAX_CHARS) : rawPodReason
|
|
3449
|
+
? redactSecretText(noResultLogEvidence ? boundedPodLogText(rawPodReason, POD_NO_RESULT_LOG_REASON_MAX_CHARS) : rawPodReason)
|
|
2896
3450
|
: undefined;
|
|
2897
3451
|
if (podReason) debug.log('self-implement.pod', 'failure-reason', { job: name, reason: podReason });
|
|
2898
3452
|
if (podExitCode === 1) {
|
|
@@ -2904,7 +3458,9 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
2904
3458
|
return {
|
|
2905
3459
|
exitCode: podExitCode,
|
|
2906
3460
|
output: tail,
|
|
2907
|
-
...(
|
|
3461
|
+
...(podVanished
|
|
3462
|
+
? { error: { code: 'pod-vanished', message: `Job ${name} disappeared after 3 consecutive NotFound polls` } }
|
|
3463
|
+
: deadlineExceeded
|
|
2908
3464
|
? { error: { code: 'pod-deadline-exceeded', message: `Job ${name} 이 수명 상한 ${deadlineSeconds}초에 닿았다` } }
|
|
2909
3465
|
: state === 'failed'
|
|
2910
3466
|
? containerExitCode === 7
|
|
@@ -3016,6 +3572,10 @@ function podPrOpenFailure(ledger: Record<string, unknown> | undefined | null, te
|
|
|
3016
3572
|
}
|
|
3017
3573
|
|
|
3018
3574
|
function lastPodChildFailure(logs: string): { stage: string; error: string; rawError: string } | null {
|
|
3575
|
+
// Keep canonical duplicate-cap attribution while preserving the raw terminal error
|
|
3576
|
+
// used by PR-open failure diagnosis below.
|
|
3577
|
+
const attribution = podChildFailureAttribution(logs);
|
|
3578
|
+
if (!attribution) return null;
|
|
3019
3579
|
for (const line of logs.split('\n').reverse()) {
|
|
3020
3580
|
if (!line.trimStart().startsWith('{')) continue;
|
|
3021
3581
|
let result: unknown;
|
|
@@ -3025,10 +3585,8 @@ function lastPodChildFailure(logs: string): { stage: string; error: string; rawE
|
|
|
3025
3585
|
const terminal = podTerminalRow(row);
|
|
3026
3586
|
if (!terminal) continue;
|
|
3027
3587
|
if (terminal.ok !== false || typeof terminal.error !== 'string') return null;
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
const last = lines.at(-1)!.slice(0, 240);
|
|
3031
|
-
return { stage: terminal.stage, error: first === last ? first : `${first}\n${last}`, rawError: terminal.error };
|
|
3588
|
+
if (terminal.stage !== attribution.stage) return null;
|
|
3589
|
+
return { ...attribution, error: redactSecretText(attribution.error), rawError: terminal.error };
|
|
3032
3590
|
}
|
|
3033
3591
|
return null;
|
|
3034
3592
|
}
|