@ebrindley/build 0.0.0-stage → 1.42.1-rc28
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/.build-project/providers/catalog.json +13951 -0
- package/.build-project/providers/pricing.json +1978 -0
- package/.build-project/providers/registry.json +4103 -0
- package/.github/SECURITY.md +75 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +661 -0
- package/CODE_OF_CONDUCT.md +44 -0
- package/CONTRIBUTING.md +24 -0
- package/INSTALL.md +623 -0
- package/LICENSE +21 -0
- package/README.md +118 -2
- package/THIRD_PARTY_NOTICES.txt +26 -0
- package/dist/ai-judge-UE5UOYOO.js +191 -0
- package/dist/allowlist-grounding-KPLN6NC5.js +16 -0
- package/dist/allowlist-utils-MZZKKXRD.js +31 -0
- package/dist/apply-completion-mode-EM37UZHB.js +110 -0
- package/dist/artifact-migrator-WPACNSEP.js +276 -0
- package/dist/ask-2OZ4JFQR.js +176 -0
- package/dist/auth-6UKBTJUQ.js +185 -0
- package/dist/auth-H5KJL6HL.js +165 -0
- package/dist/auth-LVJRJDMU.js +12 -0
- package/dist/auth-NHZOCNRR.js +94 -0
- package/dist/auth-PBXHWXGW.js +95 -0
- package/dist/auth-T7Y5DCAH.js +91 -0
- package/dist/auth-TVVKCHQW.js +13 -0
- package/dist/auth-UHB2UCSS.js +97 -0
- package/dist/auth-VACA3VUL.js +98 -0
- package/dist/auth-VVLC77S3.js +44 -0
- package/dist/auth-liveness-XO4AGDDD.js +53 -0
- package/dist/autoloop-cleanup-SI3VGAAR.js +106 -0
- package/dist/autoloop-evidence-gates-J5GG5YQB.js +39 -0
- package/dist/autoloop-gate-policy-SUNAEP6Q.js +169 -0
- package/dist/autoloop-helpers-VQGQNTT6.js +52 -0
- package/dist/autoloop-ledger-P6TIDP7C.js +96 -0
- package/dist/autoloop-ledger-subscriber-HFAGOZXH.js +161 -0
- package/dist/autoloop-run-defaults-GWGZBNT7.js +27 -0
- package/dist/autoloop-run-lock-GBWRM5A2.js +197 -0
- package/dist/autoloop-success-C32QVWOP.js +93 -0
- package/dist/autoloop-termination-summary-GI6IBQWD.js +58 -0
- package/dist/autoloop-trajectory-AMKSQISD.js +70 -0
- package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
- package/dist/automatic-branch-cleanup-R62X2C5O.js +49 -0
- package/dist/autonomous-loop-controller-2WL363LW.js +4386 -0
- package/dist/backend-YUTFDZXZ.js +30 -0
- package/dist/background-executor-Z3ODTMD7.js +260 -0
- package/dist/backlog-75QPEAMW.js +403 -0
- package/dist/backlog-execution-intent-GWUV5TUK.js +19 -0
- package/dist/backup-active-store-XHWLN34M.js +16 -0
- package/dist/backup-machine-stores-UI4LTJOY.js +251 -0
- package/dist/backup-telemetry-db-Z4ICYCNW.js +26 -0
- package/dist/branch-archive-manager-FMSQ7YJF.js +18 -0
- package/dist/branch-cleanup-manager-ST6FJGND.js +35 -0
- package/dist/build-gate-7NKWPY62.js +68 -0
- package/dist/build-root-S7DINODY.js +18 -0
- package/dist/change-summary-R6CB6VXJ.js +50 -0
- package/dist/chunk-24MNKKOM.js +173 -0
- package/dist/chunk-2H6VZFIO.js +235 -0
- package/dist/chunk-2JIX3EKV.js +39 -0
- package/dist/chunk-2KO7HVZM.js +106 -0
- package/dist/chunk-2L36U25O.js +100 -0
- package/dist/chunk-2NMNFUQA.js +475 -0
- package/dist/chunk-2OUAEH67.js +83 -0
- package/dist/chunk-2VLJUXJU.js +115 -0
- package/dist/chunk-2VUEDIUM.js +77 -0
- package/dist/chunk-2Y2IIHPI.js +430 -0
- package/dist/chunk-2YRG3LHZ.js +220 -0
- package/dist/chunk-2Z5Q3SUY.js +65 -0
- package/dist/chunk-32IYXIE5.js +873 -0
- package/dist/chunk-33IGPPUI.js +292 -0
- package/dist/chunk-352PCJLK.js +283 -0
- package/dist/chunk-35J4NE6S.js +226 -0
- package/dist/chunk-3C7WBIVK.js +1016 -0
- package/dist/chunk-3H3EY6U7.js +1225 -0
- package/dist/chunk-3H4MWYIC.js +198 -0
- package/dist/chunk-3HHWQR3N.js +2479 -0
- package/dist/chunk-3JKNRYEE.js +14 -0
- package/dist/chunk-3LECCYZY.js +128 -0
- package/dist/chunk-3LOY6YZI.js +852 -0
- package/dist/chunk-3M57DADF.js +44 -0
- package/dist/chunk-3NOTLVLG.js +3803 -0
- package/dist/chunk-3OPP2OZB.js +1480 -0
- package/dist/chunk-3P5IN5E7.js +9 -0
- package/dist/chunk-3PL22IYZ.js +20 -0
- package/dist/chunk-3REDAJUJ.js +14 -0
- package/dist/chunk-3S2R4XJI.js +203 -0
- package/dist/chunk-3UBZI5DW.js +61 -0
- package/dist/chunk-3VV5OBGG.js +308 -0
- package/dist/chunk-3WY5TU2O.js +823 -0
- package/dist/chunk-43OKHTW4.js +82 -0
- package/dist/chunk-45KUJBDI.js +112 -0
- package/dist/chunk-47X5LM7B.js +224 -0
- package/dist/chunk-4B6FQJ77.js +268 -0
- package/dist/chunk-4BTDUT2S.js +42 -0
- package/dist/chunk-4DJL2VXA.js +24 -0
- package/dist/chunk-4EBSXZ73.js +48 -0
- package/dist/chunk-4QKP6I4C.js +294 -0
- package/dist/chunk-4U6XK2DG.js +300 -0
- package/dist/chunk-4UMRUFBD.js +132 -0
- package/dist/chunk-4US4SYJ4.js +569 -0
- package/dist/chunk-4ZPLWEZO.js +154 -0
- package/dist/chunk-56WPYDJH.js +624 -0
- package/dist/chunk-57WRCJJW.js +50 -0
- package/dist/chunk-5AW4RMNG.js +24 -0
- package/dist/chunk-5EQN56OZ.js +155 -0
- package/dist/chunk-5F43TUIN.js +38 -0
- package/dist/chunk-5FKHIKTS.js +115 -0
- package/dist/chunk-5HDP7XZG.js +91 -0
- package/dist/chunk-5KQETTNS.js +152 -0
- package/dist/chunk-5PDVN2KO.js +498 -0
- package/dist/chunk-5UBIFVRD.js +509 -0
- package/dist/chunk-5YDE3Q62.js +46 -0
- package/dist/chunk-626AJVMU.js +302 -0
- package/dist/chunk-633XZ7UF.js +3583 -0
- package/dist/chunk-63DHNHGA.js +236 -0
- package/dist/chunk-666656AR.js +568 -0
- package/dist/chunk-67YRJPJO.js +19 -0
- package/dist/chunk-6CAU7VBA.js +20 -0
- package/dist/chunk-6CB3KJRY.js +21 -0
- package/dist/chunk-6DBMALXL.js +29 -0
- package/dist/chunk-6E25C4IA.js +269 -0
- package/dist/chunk-6FIBNIQU.js +73 -0
- package/dist/chunk-6G57FYBV.js +189 -0
- package/dist/chunk-6KZKRTG3.js +4885 -0
- package/dist/chunk-6LAH2UV2.js +525 -0
- package/dist/chunk-6PZY2TTU.js +921 -0
- package/dist/chunk-6T6NHXK4.js +230 -0
- package/dist/chunk-6TBEX4R4.js +432 -0
- package/dist/chunk-6TRD7TEE.js +911 -0
- package/dist/chunk-6XCP2RAI.js +47 -0
- package/dist/chunk-726G6WDL.js +351 -0
- package/dist/chunk-75EMT7BF.js +616 -0
- package/dist/chunk-762ACDZJ.js +4696 -0
- package/dist/chunk-77I6G4CO.js +1 -0
- package/dist/chunk-7DJ6SQH3.js +510 -0
- package/dist/chunk-7HPCIOMA.js +1383 -0
- package/dist/chunk-7HYBSRPZ.js +17503 -0
- package/dist/chunk-7KBEOPVB.js +73 -0
- package/dist/chunk-7LHJDJB3.js +2222 -0
- package/dist/chunk-7NVJMH52.js +306 -0
- package/dist/chunk-7RMARDVH.js +33 -0
- package/dist/chunk-7SEWU4QG.js +36 -0
- package/dist/chunk-7TUTZTI2.js +911 -0
- package/dist/chunk-7VBDFPUM.js +340 -0
- package/dist/chunk-7VRJHTGZ.js +6406 -0
- package/dist/chunk-7YAOUUKK.js +493 -0
- package/dist/chunk-AAAODV76.js +860 -0
- package/dist/chunk-AB4HUJVQ.js +339 -0
- package/dist/chunk-AEQWV746.js +611 -0
- package/dist/chunk-AHU3FKPA.js +11639 -0
- package/dist/chunk-AISTV5XU.js +226 -0
- package/dist/chunk-AJBTKGJZ.js +48 -0
- package/dist/chunk-AJJNW7XT.js +248 -0
- package/dist/chunk-AJN5Y5YZ.js +160 -0
- package/dist/chunk-ALDTFNZ3.js +150 -0
- package/dist/chunk-ARARVIZK.js +281 -0
- package/dist/chunk-ATBM6AW5.js +53 -0
- package/dist/chunk-ATKEKM3E.js +206 -0
- package/dist/chunk-AUJLW6JZ.js +92 -0
- package/dist/chunk-AY3MHEFM.js +24 -0
- package/dist/chunk-B7PYTV72.js +236 -0
- package/dist/chunk-B7YRSULE.js +1423 -0
- package/dist/chunk-BAWJNMWF.js +339 -0
- package/dist/chunk-BBPIILO2.js +88 -0
- package/dist/chunk-BD3PGW6A.js +957 -0
- package/dist/chunk-BDPYFAB4.js +44 -0
- package/dist/chunk-BIUV23HH.js +729 -0
- package/dist/chunk-BQ3VAGXH.js +243 -0
- package/dist/chunk-BQJOGAWT.js +31 -0
- package/dist/chunk-BT7EG7CO.js +68 -0
- package/dist/chunk-BV3XQQLU.js +27 -0
- package/dist/chunk-BY6ODHNZ.js +78 -0
- package/dist/chunk-BZRLSZNV.js +79 -0
- package/dist/chunk-C3DH3NCY.js +2773 -0
- package/dist/chunk-C3KCMAJP.js +756 -0
- package/dist/chunk-C7AGUJ5K.js +267 -0
- package/dist/chunk-CBSWKQCX.js +342 -0
- package/dist/chunk-CE5OZBYY.js +21 -0
- package/dist/chunk-CFCVYHSW.js +373 -0
- package/dist/chunk-CHA2HDE5.js +126 -0
- package/dist/chunk-CIHWPGS5.js +146 -0
- package/dist/chunk-CJBSZX3F.js +127 -0
- package/dist/chunk-CLA6QIJM.js +43 -0
- package/dist/chunk-CPSVQR2I.js +365 -0
- package/dist/chunk-CRPRVNQ4.js +254 -0
- package/dist/chunk-CSIE4Y7C.js +1093 -0
- package/dist/chunk-CSOPSYDY.js +142 -0
- package/dist/chunk-CSXLLEGB.js +205 -0
- package/dist/chunk-CTDLERWU.js +293 -0
- package/dist/chunk-CULBXMSK.js +110 -0
- package/dist/chunk-D2M2Y5ID.js +21 -0
- package/dist/chunk-D37WY7SH.js +1060 -0
- package/dist/chunk-D4VJFCGZ.js +89 -0
- package/dist/chunk-D6WTZMZB.js +48 -0
- package/dist/chunk-DBHOWDUH.js +976 -0
- package/dist/chunk-DCACX7BV.js +19364 -0
- package/dist/chunk-DFHX4XYA.js +128 -0
- package/dist/chunk-DIKBRRKE.js +176 -0
- package/dist/chunk-DJG5RIOR.js +9 -0
- package/dist/chunk-DNIOIISW.js +616 -0
- package/dist/chunk-DOA5G7IA.js +315 -0
- package/dist/chunk-DP7FKP6W.js +855 -0
- package/dist/chunk-DS2JZ677.js +79 -0
- package/dist/chunk-DSRLVYBS.js +82 -0
- package/dist/chunk-EAEVKNJA.js +209 -0
- package/dist/chunk-EEUESN3T.js +575 -0
- package/dist/chunk-EJ4LUU5Y.js +73 -0
- package/dist/chunk-EJIWTPSZ.js +1063 -0
- package/dist/chunk-EJVCDTAN.js +116 -0
- package/dist/chunk-EKLUBDBQ.js +627 -0
- package/dist/chunk-EN62WLFD.js +849 -0
- package/dist/chunk-ENYRCL43.js +26 -0
- package/dist/chunk-EP22ZMFO.js +273 -0
- package/dist/chunk-EP4SVAVI.js +87 -0
- package/dist/chunk-EPAMVGI3.js +1562 -0
- package/dist/chunk-EPRMH6HO.js +24 -0
- package/dist/chunk-ES6NWJ36.js +4057 -0
- package/dist/chunk-ETA2K4KI.js +30 -0
- package/dist/chunk-ETAD4NKD.js +31 -0
- package/dist/chunk-EUWIIDQ7.js +93 -0
- package/dist/chunk-EVE3OD2W.js +141 -0
- package/dist/chunk-EXA26FFC.js +459 -0
- package/dist/chunk-EXSPAA35.js +244 -0
- package/dist/chunk-F55YABKV.js +258 -0
- package/dist/chunk-F5DNGU5W.js +27 -0
- package/dist/chunk-F622MJYV.js +103 -0
- package/dist/chunk-F6NIQD74.js +5111 -0
- package/dist/chunk-FAWJTC34.js +43 -0
- package/dist/chunk-FAZBRHGG.js +47 -0
- package/dist/chunk-FIEUSGNJ.js +39 -0
- package/dist/chunk-FKCG7K6H.js +1146 -0
- package/dist/chunk-FOVVXV45.js +229 -0
- package/dist/chunk-FRBPKBFE.js +30 -0
- package/dist/chunk-FSA5BPFI.js +268 -0
- package/dist/chunk-FSJ5TTZ3.js +29 -0
- package/dist/chunk-FT7SGLXT.js +193 -0
- package/dist/chunk-FUUVZZUB.js +62 -0
- package/dist/chunk-FYMRVUNZ.js +1697 -0
- package/dist/chunk-FYTAA5FG.js +162 -0
- package/dist/chunk-G3VB2UCZ.js +3892 -0
- package/dist/chunk-G6OIMVXH.js +405 -0
- package/dist/chunk-G7PQCXVH.js +228 -0
- package/dist/chunk-GAB6UW52.js +1087 -0
- package/dist/chunk-GAFJEYLK.js +29 -0
- package/dist/chunk-GC4CRMTS.js +642 -0
- package/dist/chunk-GFHSIWHL.js +5297 -0
- package/dist/chunk-GI5M67YA.js +735 -0
- package/dist/chunk-GIJWHUWF.js +191 -0
- package/dist/chunk-GO6DRECI.js +1 -0
- package/dist/chunk-GOFVBTMN.js +314 -0
- package/dist/chunk-GQIP72ZJ.js +13 -0
- package/dist/chunk-GQXLXRFA.js +28 -0
- package/dist/chunk-GT6HFKJB.js +48 -0
- package/dist/chunk-GVJGSSKM.js +174 -0
- package/dist/chunk-GWHGTG37.js +36 -0
- package/dist/chunk-GZAJHLKO.js +18 -0
- package/dist/chunk-GZK62I3N.js +314 -0
- package/dist/chunk-H3HGMISG.js +483 -0
- package/dist/chunk-H3ISZJMJ.js +403 -0
- package/dist/chunk-H4HXVWEC.js +9 -0
- package/dist/chunk-H6MJGKAW.js +120 -0
- package/dist/chunk-HBTTGVR5.js +353 -0
- package/dist/chunk-HBZ322WZ.js +35 -0
- package/dist/chunk-HEP3ILP4.js +34 -0
- package/dist/chunk-HGFXODKO.js +106 -0
- package/dist/chunk-HHWDTB6S.js +59 -0
- package/dist/chunk-HKO45NLZ.js +286 -0
- package/dist/chunk-HLVTEA6A.js +89 -0
- package/dist/chunk-HS3SY6HN.js +160 -0
- package/dist/chunk-HT5OCGKN.js +899 -0
- package/dist/chunk-HXIIEMGF.js +78 -0
- package/dist/chunk-I56TH7VM.js +46 -0
- package/dist/chunk-I6FN3R5D.js +352 -0
- package/dist/chunk-I7SCBHJN.js +655 -0
- package/dist/chunk-IJM4GKAT.js +529 -0
- package/dist/chunk-IMEYU36R.js +344 -0
- package/dist/chunk-INJ3SS6A.js +936 -0
- package/dist/chunk-IUKOPCCE.js +441 -0
- package/dist/chunk-IVEPZQJO.js +33 -0
- package/dist/chunk-IVZEONFC.js +43 -0
- package/dist/chunk-IWBHRSG7.js +936 -0
- package/dist/chunk-IYY3HQAD.js +484 -0
- package/dist/chunk-J2ICMYDA.js +48 -0
- package/dist/chunk-J2QCFLUN.js +505 -0
- package/dist/chunk-J5HM6V5P.js +1039 -0
- package/dist/chunk-J6NZX6QH.js +398 -0
- package/dist/chunk-JBBB2Y47.js +432 -0
- package/dist/chunk-JDCZQZU7.js +42 -0
- package/dist/chunk-JEDNXGSS.js +169 -0
- package/dist/chunk-JHP7OT6K.js +89 -0
- package/dist/chunk-JLS65NUQ.js +14 -0
- package/dist/chunk-JNMWYMC6.js +38 -0
- package/dist/chunk-JNSTWNNW.js +25 -0
- package/dist/chunk-JOSK6GEU.js +9564 -0
- package/dist/chunk-JQUMSIGC.js +100 -0
- package/dist/chunk-JVHGMURQ.js +1106 -0
- package/dist/chunk-JYXS47FQ.js +951 -0
- package/dist/chunk-K2ZMW2Z5.js +2147 -0
- package/dist/chunk-K7FAFORM.js +14 -0
- package/dist/chunk-KBJXX3ON.js +333 -0
- package/dist/chunk-KHFKVQWY.js +486 -0
- package/dist/chunk-KHMEI3DQ.js +173 -0
- package/dist/chunk-KHW3UD7B.js +69 -0
- package/dist/chunk-KKQKWAMF.js +365 -0
- package/dist/chunk-KOKXJV2U.js +455 -0
- package/dist/chunk-KQ4NGOJI.js +14 -0
- package/dist/chunk-KQYQOXMS.js +342 -0
- package/dist/chunk-KTMW65UX.js +14 -0
- package/dist/chunk-KUEWV2JH.js +593 -0
- package/dist/chunk-KXYDV3HP.js +1523 -0
- package/dist/chunk-KZ4JMGZ5.js +96 -0
- package/dist/chunk-KZWGBUIO.js +2659 -0
- package/dist/chunk-L2CBG5IX.js +1892 -0
- package/dist/chunk-L2S235CU.js +314 -0
- package/dist/chunk-L32WD57F.js +98 -0
- package/dist/chunk-LDJCKAJP.js +123 -0
- package/dist/chunk-LDWNBEN7.js +541 -0
- package/dist/chunk-LJVH57G2.js +241 -0
- package/dist/chunk-LPQ7NZEN.js +209 -0
- package/dist/chunk-LRGH3CCV.js +148 -0
- package/dist/chunk-LULOGMMM.js +1133 -0
- package/dist/chunk-LWGKN2GY.js +8284 -0
- package/dist/chunk-LX4F24ZH.js +157 -0
- package/dist/chunk-M4BUEXXF.js +839 -0
- package/dist/chunk-M6UHOC4I.js +553 -0
- package/dist/chunk-MCBNWZNO.js +657 -0
- package/dist/chunk-MEMSY6VY.js +223 -0
- package/dist/chunk-MHLEZQQI.js +145 -0
- package/dist/chunk-MJ6WNSIX.js +192 -0
- package/dist/chunk-MKASIKWZ.js +442 -0
- package/dist/chunk-MNOVBYFU.js +60 -0
- package/dist/chunk-MO2BYAAF.js +2896 -0
- package/dist/chunk-MP6P7Y2A.js +180 -0
- package/dist/chunk-MQ4TFJAG.js +3365 -0
- package/dist/chunk-MT2WAIR3.js +131 -0
- package/dist/chunk-MTU7VTNN.js +1122 -0
- package/dist/chunk-MULMXX4V.js +150 -0
- package/dist/chunk-MW2DWWVA.js +147 -0
- package/dist/chunk-MY3ZPUN4.js +151 -0
- package/dist/chunk-MYLH2S4G.js +64 -0
- package/dist/chunk-MZ5WYKNA.js +28 -0
- package/dist/chunk-N3Q2JNPV.js +249 -0
- package/dist/chunk-N5DX4JAK.js +12 -0
- package/dist/chunk-NAINGKHF.js +35 -0
- package/dist/chunk-NEQUQHOX.js +223 -0
- package/dist/chunk-NHNPEZS4.js +1408 -0
- package/dist/chunk-NHTIXJCR.js +17 -0
- package/dist/chunk-NKLPMBNV.js +1154 -0
- package/dist/chunk-NMVZ5YES.js +199 -0
- package/dist/chunk-NRFRY3GE.js +256 -0
- package/dist/chunk-NTD6CSI6.js +1266 -0
- package/dist/chunk-NUO7B3SH.js +318 -0
- package/dist/chunk-NVOHIJKG.js +1319 -0
- package/dist/chunk-NXH3RX7O.js +523 -0
- package/dist/chunk-NXZ3DURF.js +257 -0
- package/dist/chunk-O26L6F7F.js +209 -0
- package/dist/chunk-O5RRIUEP.js +124 -0
- package/dist/chunk-O5X6BKAO.js +10 -0
- package/dist/chunk-O6ZQUVTR.js +31 -0
- package/dist/chunk-O7FE75QZ.js +306 -0
- package/dist/chunk-OC7Z6AJQ.js +81 -0
- package/dist/chunk-OH7IEZ63.js +307 -0
- package/dist/chunk-OI734GRS.js +115 -0
- package/dist/chunk-OJBH62DK.js +684 -0
- package/dist/chunk-OKJLVSTJ.js +247 -0
- package/dist/chunk-OMX5VL43.js +79 -0
- package/dist/chunk-ONHAMBN6.js +12 -0
- package/dist/chunk-ONYPLIVE.js +753 -0
- package/dist/chunk-OSK7QWPD.js +637 -0
- package/dist/chunk-OTXLMJKP.js +438 -0
- package/dist/chunk-OV5PKQMA.js +213 -0
- package/dist/chunk-OXK5HA4B.js +31 -0
- package/dist/chunk-OYDWFQA4.js +272 -0
- package/dist/chunk-OZAUKWHB.js +22 -0
- package/dist/chunk-P5P2EKGH.js +94 -0
- package/dist/chunk-P6SSWAKY.js +29 -0
- package/dist/chunk-P7CQGPLS.js +30 -0
- package/dist/chunk-P7RVCHQV.js +197 -0
- package/dist/chunk-PEBWDYIW.js +726 -0
- package/dist/chunk-PFCXFO7A.js +9193 -0
- package/dist/chunk-PFLDWMX6.js +168 -0
- package/dist/chunk-PHZGLQSK.js +15883 -0
- package/dist/chunk-PNFTPNSR.js +20 -0
- package/dist/chunk-PPHXX4W7.js +175 -0
- package/dist/chunk-PPZESPJT.js +262 -0
- package/dist/chunk-PRDHI2L5.js +296 -0
- package/dist/chunk-PSTKWHHN.js +32 -0
- package/dist/chunk-PV53ODOT.js +555 -0
- package/dist/chunk-PVJBTI7P.js +24 -0
- package/dist/chunk-PXKERBKX.js +100 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q3DHEZA5.js +85 -0
- package/dist/chunk-Q5RZDVQK.js +204 -0
- package/dist/chunk-Q64IR2Z2.js +4933 -0
- package/dist/chunk-Q6Z5UGPL.js +398 -0
- package/dist/chunk-QAMAV4DT.js +627 -0
- package/dist/chunk-QBWF4444.js +112 -0
- package/dist/chunk-QDKYT4CI.js +22805 -0
- package/dist/chunk-QFLBRWCJ.js +126 -0
- package/dist/chunk-QILAQACE.js +107 -0
- package/dist/chunk-QJ6OEH4K.js +3683 -0
- package/dist/chunk-QK3NLYTV.js +320 -0
- package/dist/chunk-QOFLUS24.js +535 -0
- package/dist/chunk-QOIASNEJ.js +98 -0
- package/dist/chunk-QP4UTSOV.js +3587 -0
- package/dist/chunk-QP5ST7RI.js +904 -0
- package/dist/chunk-QS5AGRZN.js +167 -0
- package/dist/chunk-QTAOU53N.js +107 -0
- package/dist/chunk-QTCLJHLZ.js +351 -0
- package/dist/chunk-QTOLTG4L.js +119 -0
- package/dist/chunk-QWDV4OSL.js +142 -0
- package/dist/chunk-QWMQ74KD.js +508 -0
- package/dist/chunk-QX6LOMMH.js +158 -0
- package/dist/chunk-QXBKIHY7.js +238 -0
- package/dist/chunk-QXBKTJT6.js +270 -0
- package/dist/chunk-QYQOVOLR.js +196 -0
- package/dist/chunk-R2CKS6WT.js +73 -0
- package/dist/chunk-R2TIYG7U.js +68 -0
- package/dist/chunk-R2W3LESH.js +465 -0
- package/dist/chunk-R5EXHMUN.js +100 -0
- package/dist/chunk-RBBTBCHX.js +289 -0
- package/dist/chunk-RBKLOUXA.js +121 -0
- package/dist/chunk-RCP2Y6DQ.js +86 -0
- package/dist/chunk-RCYEJSD5.js +260 -0
- package/dist/chunk-RFDVSI4N.js +134 -0
- package/dist/chunk-RGJU63FV.js +1717 -0
- package/dist/chunk-RI4EX2QE.js +18 -0
- package/dist/chunk-RIMARPCF.js +152 -0
- package/dist/chunk-RJ6RSNT6.js +42 -0
- package/dist/chunk-RNI2GUIF.js +131 -0
- package/dist/chunk-RPHUWJ3K.js +264 -0
- package/dist/chunk-RPIMAMV7.js +361 -0
- package/dist/chunk-RQFX6YRN.js +49 -0
- package/dist/chunk-RRC5BL7B.js +285 -0
- package/dist/chunk-RSYTSBOO.js +79 -0
- package/dist/chunk-RWQQIKIC.js +35 -0
- package/dist/chunk-RZUBOZH2.js +373 -0
- package/dist/chunk-S2E632FQ.js +380 -0
- package/dist/chunk-S2VQCZO4.js +22 -0
- package/dist/chunk-S2XIMQJQ.js +681 -0
- package/dist/chunk-S47ISY27.js +183 -0
- package/dist/chunk-S6I4KISU.js +142 -0
- package/dist/chunk-S6TAFQI5.js +2403 -0
- package/dist/chunk-SBBNT5ZJ.js +27 -0
- package/dist/chunk-SDJV2DTQ.js +491 -0
- package/dist/chunk-SDPIFNLD.js +33 -0
- package/dist/chunk-SF6YK3JI.js +835 -0
- package/dist/chunk-SK3ZFONO.js +976 -0
- package/dist/chunk-SKTFRFQ7.js +1775 -0
- package/dist/chunk-SNGM452K.js +2002 -0
- package/dist/chunk-STW7OFOA.js +581 -0
- package/dist/chunk-SU6UFUQY.js +1351 -0
- package/dist/chunk-SVJM3LJA.js +27 -0
- package/dist/chunk-SVUNZOPG.js +129 -0
- package/dist/chunk-T5XLUYV3.js +34 -0
- package/dist/chunk-T7E5JS6N.js +57 -0
- package/dist/chunk-TA3N5MMO.js +450 -0
- package/dist/chunk-TAJPCXSB.js +13 -0
- package/dist/chunk-TDEMDRN2.js +1639 -0
- package/dist/chunk-TDSEX5CF.js +24 -0
- package/dist/chunk-TGCVL74Y.js +28 -0
- package/dist/chunk-TGXXYDL4.js +1012 -0
- package/dist/chunk-TIKTYUJO.js +296 -0
- package/dist/chunk-TIZAJD4M.js +6029 -0
- package/dist/chunk-TLSEZZCG.js +5549 -0
- package/dist/chunk-TMBDRHAX.js +19 -0
- package/dist/chunk-TRURQUBP.js +1217 -0
- package/dist/chunk-TSQ256N5.js +152 -0
- package/dist/chunk-TUQMMFEQ.js +2244 -0
- package/dist/chunk-U424MJLC.js +81 -0
- package/dist/chunk-U4DRS6ZW.js +11 -0
- package/dist/chunk-U5KS22IN.js +204 -0
- package/dist/chunk-UAPHFJPN.js +673 -0
- package/dist/chunk-UB7LQITD.js +1040 -0
- package/dist/chunk-UBVRHGAZ.js +237 -0
- package/dist/chunk-UDNNAQN5.js +98 -0
- package/dist/chunk-UJP3XM54.js +342 -0
- package/dist/chunk-UJYDH2MW.js +305 -0
- package/dist/chunk-UKMPASIC.js +399 -0
- package/dist/chunk-UKUL3EHK.js +763 -0
- package/dist/chunk-UPHU64NB.js +1327 -0
- package/dist/chunk-URMSZXAD.js +43 -0
- package/dist/chunk-US3RT6CY.js +525 -0
- package/dist/chunk-UTDEOPKV.js +30 -0
- package/dist/chunk-UUUDKV7N.js +13 -0
- package/dist/chunk-UVKWU3SM.js +39 -0
- package/dist/chunk-V2TVPO3X.js +521 -0
- package/dist/chunk-V3KLVK7B.js +398 -0
- package/dist/chunk-V3YFFHDR.js +17 -0
- package/dist/chunk-VAG6GUBR.js +1162 -0
- package/dist/chunk-VBENAHDA.js +1814 -0
- package/dist/chunk-VBNCKCCI.js +16 -0
- package/dist/chunk-VCGSWN5M.js +1300 -0
- package/dist/chunk-VE2O2I7M.js +115 -0
- package/dist/chunk-VIFHYTI5.js +31 -0
- package/dist/chunk-VJER3FUS.js +5080 -0
- package/dist/chunk-VLMDM56Q.js +268 -0
- package/dist/chunk-VMFZEVVX.js +99 -0
- package/dist/chunk-VNDPQVUZ.js +1853 -0
- package/dist/chunk-VSPJRLGA.js +4783 -0
- package/dist/chunk-VSWC5ZXI.js +103 -0
- package/dist/chunk-VYI4DSZP.js +2017 -0
- package/dist/chunk-W4BAIQ6L.js +1212 -0
- package/dist/chunk-W7VQAL2K.js +2203 -0
- package/dist/chunk-WEI3ZDZH.js +33 -0
- package/dist/chunk-WEIUS5NT.js +26 -0
- package/dist/chunk-WEW7UFWE.js +546 -0
- package/dist/chunk-WGQAXLNA.js +384 -0
- package/dist/chunk-WJAL4JL3.js +206 -0
- package/dist/chunk-WKFOJ5ZG.js +141 -0
- package/dist/chunk-WMWQEFLN.js +2177 -0
- package/dist/chunk-WNECKKO2.js +154 -0
- package/dist/chunk-WPKLYOAM.js +195 -0
- package/dist/chunk-WSH37YQN.js +243 -0
- package/dist/chunk-WV3KQDPE.js +84 -0
- package/dist/chunk-WZFC4V4U.js +389 -0
- package/dist/chunk-XCOIBNET.js +13 -0
- package/dist/chunk-XDM65LBX.js +53 -0
- package/dist/chunk-XGU6PBO5.js +645 -0
- package/dist/chunk-XHOI4V62.js +43 -0
- package/dist/chunk-XIBVFMLQ.js +6858 -0
- package/dist/chunk-XJ2E7YEJ.js +117 -0
- package/dist/chunk-XON4ZCYX.js +69 -0
- package/dist/chunk-XQPEWJPI.js +127 -0
- package/dist/chunk-XSJTIFUY.js +31 -0
- package/dist/chunk-XT7OVWM3.js +64 -0
- package/dist/chunk-XTXYIISX.js +1104 -0
- package/dist/chunk-XV7NZL4B.js +57 -0
- package/dist/chunk-XWIZ6B5S.js +109 -0
- package/dist/chunk-XWLIKOBX.js +240 -0
- package/dist/chunk-XZOXEIN4.js +156 -0
- package/dist/chunk-XZRS7I7W.js +3673 -0
- package/dist/chunk-Y4ZDDHB6.js +44 -0
- package/dist/chunk-Y733RB7T.js +194 -0
- package/dist/chunk-YBHUB4XR.js +14931 -0
- package/dist/chunk-YKC75XK4.js +147 -0
- package/dist/chunk-YKMXFVK7.js +27 -0
- package/dist/chunk-YO5BAJ55.js +225 -0
- package/dist/chunk-YPDCLXNZ.js +568 -0
- package/dist/chunk-YSQPGVFK.js +116 -0
- package/dist/chunk-YTVVTIWJ.js +716 -0
- package/dist/chunk-YVCMDVBB.js +698 -0
- package/dist/chunk-YYTOA6DP.js +114 -0
- package/dist/chunk-Z5WGNULV.js +88 -0
- package/dist/chunk-Z6NGVVXG.js +66 -0
- package/dist/chunk-Z7CDQNH3.js +20 -0
- package/dist/chunk-ZH4G3FU5.js +187 -0
- package/dist/chunk-ZJB6SQPA.js +257 -0
- package/dist/chunk-ZL6E4HMY.js +42 -0
- package/dist/chunk-ZMLOC3DW.js +62 -0
- package/dist/chunk-ZORW64QH.js +101 -0
- package/dist/chunk-ZVVD2XGV.js +1369 -0
- package/dist/cli-utils-ER33ODC5.js +31 -0
- package/dist/cli-validation-6CHPTANV.js +146 -0
- package/dist/commit-utils-3STAY7C7.js +49 -0
- package/dist/compatibility-notice-IHNXR2JR.js +21 -0
- package/dist/compete-config-resolver-V3GDNJMM.js +125 -0
- package/dist/compete-results-XUM3PJHS.js +318 -0
- package/dist/competition-outcome-tracker-NWSMVUPG.js +62 -0
- package/dist/config-4HHE4OIK.js +238 -0
- package/dist/config-D6YAZASH.js +10 -0
- package/dist/config-MIPRDYYV.js +46 -0
- package/dist/config-audit-GA5DCUAI.js +371 -0
- package/dist/config-explain-4LMO556H.js +71 -0
- package/dist/config-loader-RGP634PC.js +51 -0
- package/dist/config-manager-J2UTO635.js +114 -0
- package/dist/config-merger-TVUJBZTS.js +14 -0
- package/dist/config-validator-H4ZVIE56.js +122 -0
- package/dist/cost-HSIV5U2Y.js +85 -0
- package/dist/data-migration-state-OAUL2WPQ.js +35 -0
- package/dist/db-migrator-NUOW5NKR.js +34 -0
- package/dist/default-paths-UILB62BN.js +49 -0
- package/dist/discovery-V6GYUJUC.js +53 -0
- package/dist/doctor-BBSMCDWE.js +271 -0
- package/dist/doctor-context-blocks-LPY75VZA.js +231 -0
- package/dist/doctor-instructions-import-JXHDDOE5.js +70 -0
- package/dist/doctor-purpose-6YJOXPBE.js +30 -0
- package/dist/domain-analyzer-S35ABUNA.js +48 -0
- package/dist/ensure-initialized-Q6PEU55O.js +78 -0
- package/dist/entry.js +922 -0
- package/dist/error-parser-core-MZFMFANN.js +70 -0
- package/dist/evaluation-context-GQBYDAEV.js +63 -0
- package/dist/evidence-plan-resolver-5FUAP3SA.js +111 -0
- package/dist/execution-data-writer-YCG5UH37.js +72 -0
- package/dist/execution-policy-applier-PWZM5LNA.js +63 -0
- package/dist/execution-preflight-DASJWWQF.js +223 -0
- package/dist/execution-roles-KNSNNB2D.js +112 -0
- package/dist/exit-code-error-QIY5DOKF.js +18 -0
- package/dist/external-temp-cleanup-CAW7LQ6R.js +360 -0
- package/dist/factory-YCYCB7AE.js +209 -0
- package/dist/file-utils-ZQYNBQBW.js +45 -0
- package/dist/flywheel-allowlist-revert-H22HA6JE.js +21 -0
- package/dist/flywheel-autoloop-executor-PDOASJEZ.js +316 -0
- package/dist/flywheel-autoloop-unverified-TOKYFCDC.js +36 -0
- package/dist/flywheel-competition-executor-YJUMMEJO.js +320 -0
- package/dist/flywheel-executor-JWU43KAY.js +437 -0
- package/dist/flywheel-git-isolation-FQ2TRWMB.js +56 -0
- package/dist/flywheel-manifest-HDPNMWWF.js +114 -0
- package/dist/flywheel-model-defaults-UHAKTZT3.js +110 -0
- package/dist/flywheel-preflight-III2C7TH.js +163 -0
- package/dist/flywheel-resume-2O45MS7Y.js +31 -0
- package/dist/flywheel-resume-execution-lease-P2BK257J.js +59 -0
- package/dist/flywheel-safety-AVAVFXIJ.js +19 -0
- package/dist/flywheel-scope-decision-ALJUUNGG.js +14 -0
- package/dist/get-telemetry-logger-MPDVO2JA.js +155 -0
- package/dist/git-message-style-KCXGMTDT.js +104 -0
- package/dist/guidance-profile-config-G3K5YQOO.js +113 -0
- package/dist/guidance-profile-renderer-TX4TNDFL.js +23 -0
- package/dist/identity-XLQJ4YJF.js +25 -0
- package/dist/index-query-6NUY4FKX.js +37 -0
- package/dist/index.js +85588 -0
- package/dist/indexer-DL54CM3K.js +44 -0
- package/dist/init-OUZ72344.js +245 -0
- package/dist/instructions-import-offer-NSYXSITC.js +363 -0
- package/dist/instructions-run-NBII7W36.js +114 -0
- package/dist/interrupted-work-E4TLKJLE.js +43 -0
- package/dist/invoke-provider-auth-NEWEIHCI.js +38 -0
- package/dist/judge-scoring.json +64 -0
- package/dist/lab-mode-YXQ2Y3NG.js +44 -0
- package/dist/lab-utils-5QAMDJR5.js +29 -0
- package/dist/linux-host-class-YDWQX65O.js +31 -0
- package/dist/llm-judge-executor-OJSNCTFJ.js +179 -0
- package/dist/local-ci-config-VZ4WWDT5.js +48 -0
- package/dist/local-executor-DCMDDRX2.js +280 -0
- package/dist/logger-5KNKS3O4.js +154 -0
- package/dist/matrix-prompt-builder-4ZBOGZSB.js +527 -0
- package/dist/matrix-result-parser-EZJM4YXP.js +14 -0
- package/dist/merge-batch-RTEEMVDB.js +313 -0
- package/dist/merge-batch-members-VIWGVRHP.js +24 -0
- package/dist/merge-queue-TIIUSOY6.js +126 -0
- package/dist/merge-queue-trust-DUNZ4J65.js +21 -0
- package/dist/metadata-UKSMWM6J.js +29 -0
- package/dist/model-catalog-readiness-R5KJBP5M.js +202 -0
- package/dist/model-invocations-db-XTUPPQ34.js +70 -0
- package/dist/model-resolver-JFZ2MHOJ.js +39 -0
- package/dist/model-resolver-SCQ57KAQ.js +217 -0
- package/dist/model-roles-D3OR4NAH.js +110 -0
- package/dist/monitor-F4SIU5G2.js +96 -0
- package/dist/nested-worktrees-2WUWQ75N.js +23 -0
- package/dist/next-S3IRLVER.js +96 -0
- package/dist/next-UKPFRXOO.js +148 -0
- package/dist/opportunistic-TL4S26VX.js +86 -0
- package/dist/optimization-config-resolver-NAEIYU6A.js +148 -0
- package/dist/package-HJR4QJT4.js +10 -0
- package/dist/parse-git-status-7PIYFPS2.js +21 -0
- package/dist/path-security-5WJSY56G.js +41 -0
- package/dist/paths-JUI7C4KU.js +45 -0
- package/dist/plan-backlog-list-fast-SSTQL5XP.js +89 -0
- package/dist/plan-backlog-show-fast-PFRAQ3JB.js +114 -0
- package/dist/plan-sprint-list-fast-HD2KBZSY.js +110 -0
- package/dist/plan-sprint-status-fast-YFYH7WQF.js +116 -0
- package/dist/plan-task-status-fast-JU5PNLUU.js +114 -0
- package/dist/plan-to-flywheel-PY5MSY2H.js +529 -0
- package/dist/preflight-PJHGTJNZ.js +12 -0
- package/dist/process-registry-B4ZUCN7T.js +28 -0
- package/dist/process-scanner-TU3QHWVB.js +23 -0
- package/dist/processes-6FHVGT72.js +28 -0
- package/dist/processes-json-fast-YWTMUWXB.js +72 -0
- package/dist/profile-HWTC56SG.js +209 -0
- package/dist/project-runtime-paths-2UQNG5Y4.js +41 -0
- package/dist/protected-branches-N36LDBFY.js +71 -0
- package/dist/provider-aware-resource-manager-YTFAHNGJ.js +40 -0
- package/dist/provider-matrix-2KO2V5OM.js +214 -0
- package/dist/provider-matrix-renderer-MEC3PER7.js +139 -0
- package/dist/provider-output-forwarding-2AHL7YMA.js +26 -0
- package/dist/provider-registry-WCZ65QHC.js +136 -0
- package/dist/provider-temp-cleanup-IUNPJCZF.js +14 -0
- package/dist/prune-engine-EOQVWEP6.js +448 -0
- package/dist/publication-turn-S42RZTQF.js +44 -0
- package/dist/quality-gate-JQMVCKOO.js +72 -0
- package/dist/quickstart-3O6FRCFB.js +105 -0
- package/dist/readiness-GW6ABRHB.js +139 -0
- package/dist/readiness-KFZCXDTT.js +215 -0
- package/dist/registry-7XG376SY.js +217 -0
- package/dist/repair-helpers-TTV5FEF4.js +117 -0
- package/dist/repo-root-YG6MQZDW.js +28 -0
- package/dist/resolution-engine-FFEK7HQ2.js +187 -0
- package/dist/resolve-provider-cli-ZEXVZKET.js +44 -0
- package/dist/restore-telemetry-db-E7BR5PO7.js +459 -0
- package/dist/routing-events-AW6SRZ6G.js +13 -0
- package/dist/routing-history-store-FMDDQL6W.js +39 -0
- package/dist/routing-planner-C6OHO3EY.js +221 -0
- package/dist/runner-KHDNQ5KU.js +1194 -0
- package/dist/runner-PIS2BW7Y.js +1801 -0
- package/dist/safety-E3JJVSFL.js +67 -0
- package/dist/safety-stanza-HE6GXP4G.js +49 -0
- package/dist/sandbox-7LBZC5PE.js +72 -0
- package/dist/sandbox-ASWCDYZL.js +80 -0
- package/dist/schema-extensions.sql +234 -0
- package/dist/security-JI22TBEB.js +17 -0
- package/dist/setup-bb-plugin-FQXLA76Y.js +531 -0
- package/dist/setup-claude-code-NF4ZSHVE.js +197 -0
- package/dist/setup-temp-cleanup-2APZ4HMA.js +29 -0
- package/dist/shared-utils-34HJZ6JX.js +40 -0
- package/dist/simple-artifact-goal-MQLDDMMX.js +29 -0
- package/dist/sprint-N7JFTEGL.js +403 -0
- package/dist/sprint-bridge-KJXIBNRJ.js +117 -0
- package/dist/sprint-completion-dependents-34SR7JVL.js +43 -0
- package/dist/sprint-execution-service-A6JPUBWY.js +376 -0
- package/dist/sprint-manager-MU2ZUWN2.js +140 -0
- package/dist/sqlite-timeout-config-U2CT4LKU.js +13 -0
- package/dist/sqlite-wrapper-FSGJ2QEW.js +19 -0
- package/dist/stale-cleanup-orchestrator-4X6324VF.js +71 -0
- package/dist/storage-access-I3V5LER7.js +31 -0
- package/dist/store-XVG64ZR2.js +25 -0
- package/dist/task-classifier-MN2DYE5R.js +558 -0
- package/dist/task-list-fast-7AT6LTTA.js +112 -0
- package/dist/task-manager-LHPBBRRE.js +136 -0
- package/dist/task-report-OLPAWBTK.js +64 -0
- package/dist/task-type-detector-6RG3YNZF.js +27 -0
- package/dist/task-type-resolver-EQHOUIHT.js +64 -0
- package/dist/telemetry-5VYSK772.js +212 -0
- package/dist/telemetry-health-tracker-4GFW2EMP.js +12 -0
- package/dist/telemetry-path-resolver-H3NFHHQY.js +34 -0
- package/dist/telemetry-reliability-URJDTNPZ.js +76 -0
- package/dist/transports-NREZQTJK.js +106 -0
- package/dist/unified-cost-tracker-5MTBJ2CL.js +59 -0
- package/dist/unified-state-cleanup-GGAXLHXF.js +292 -0
- package/dist/universal-cost-calculator-JTFEJVWK.js +70 -0
- package/dist/user-agents-QLIGPLP2.js +30 -0
- package/dist/variant-execution-I4ZZUAWY.js +319 -0
- package/dist/variant-opt-parser-JHCXKY2K.js +251 -0
- package/dist/variant-request-input-PL37N3AN.js +11 -0
- package/dist/variant-state-cleanup-KHMXRBZN.js +287 -0
- package/dist/variant-worker-COB5YTJX.js +4517 -0
- package/dist/variant-worker.js +26 -0
- package/dist/variants-XGDSXYG3.js +267 -0
- package/dist/verification-command-YSPU4VBH.js +66 -0
- package/dist/verify-command-timeout-DQUYH33Z.js +11 -0
- package/dist/verify-commands-LOHMOFKC.js +65 -0
- package/dist/warning-JSKSFKBT.js +13 -0
- package/dist/work-edge-K65NHVWY.js +50 -0
- package/dist/work-item-writer-KY5ZWZWW.js +30 -0
- package/dist/worker-JNXXEIT6.js +1504 -0
- package/dist/worker.js +21 -0
- package/dist/workspace-tree-identity-JO5PSKTW.js +52 -0
- package/dist/worktree-environment-WTVABAQW.js +64 -0
- package/dist/worktree-metrics-VZRQI26T.js +66 -0
- package/dist/writeback-TQFQBJNU.js +50 -0
- package/docs/CLI_REFERENCE.md +8287 -0
- package/docs/PROVIDER_SETUP.md +1737 -0
- package/docs/README.md +96 -0
- package/docs/TROUBLESHOOTING.md +2209 -0
- package/docs/getting-started/QUICK_START.md +260 -0
- package/docs/getting-started/README.md +198 -0
- package/docs/getting-started/SETUP.md +25 -0
- package/docs/reference/AGENT_CONTEXT.md +84 -0
- package/docs/reference/DELIVERY_ONBOARDING.md +198 -0
- package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
- package/docs/reference/SECURITY.md +291 -0
- package/docs/reference/TELEMETRY_PRIVACY.md +259 -0
- package/integrations/bb-plugin-build/README.md +391 -0
- package/integrations/bb-plugin-build/app.css +431 -0
- package/integrations/bb-plugin-build/app.tsx +6210 -0
- package/integrations/bb-plugin-build/package.json +45 -0
- package/integrations/bb-plugin-build/server.ts +488 -0
- package/integrations/bb-plugin-build/src/adapter.ts +3672 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-model.ts +150 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-schema.ts +458 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-service.ts +467 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-view.ts +1025 -0
- package/integrations/bb-plugin-build/src/command-evidence-schema.ts +13 -0
- package/integrations/bb-plugin-build/src/competition-defaults-schema.ts +122 -0
- package/integrations/bb-plugin-build/src/competition-defaults-service.ts +105 -0
- package/integrations/bb-plugin-build/src/competition-defaults-view.ts +138 -0
- package/integrations/bb-plugin-build/src/contract.ts +718 -0
- package/integrations/bb-plugin-build/src/execution-activity-view.ts +98 -0
- package/integrations/bb-plugin-build/src/finalize-model.ts +205 -0
- package/integrations/bb-plugin-build/src/finalize-schema.ts +150 -0
- package/integrations/bb-plugin-build/src/finalize-service.ts +330 -0
- package/integrations/bb-plugin-build/src/finalize-view.ts +159 -0
- package/integrations/bb-plugin-build/src/judge-operations-readback.ts +303 -0
- package/integrations/bb-plugin-build/src/judge-operations-schema.ts +33 -0
- package/integrations/bb-plugin-build/src/judge-operations-view.ts +459 -0
- package/integrations/bb-plugin-build/src/model.ts +1318 -0
- package/integrations/bb-plugin-build/src/monitor-service.ts +233 -0
- package/integrations/bb-plugin-build/src/panel-read-ux.ts +97 -0
- package/integrations/bb-plugin-build/src/patch-preview-schema.ts +166 -0
- package/integrations/bb-plugin-build/src/patch-preview-service.ts +480 -0
- package/integrations/bb-plugin-build/src/patch-preview-view.ts +109 -0
- package/integrations/bb-plugin-build/src/planning-readback.ts +228 -0
- package/integrations/bb-plugin-build/src/planning-service.ts +647 -0
- package/integrations/bb-plugin-build/src/planning-sync-evidence.ts +37 -0
- package/integrations/bb-plugin-build/src/planning-workspace-readback.ts +33 -0
- package/integrations/bb-plugin-build/src/planning-workspace-schema.ts +131 -0
- package/integrations/bb-plugin-build/src/planning-workspace.ts +333 -0
- package/integrations/bb-plugin-build/src/poll-handoff.ts +136 -0
- package/integrations/bb-plugin-build/src/project-target-server.ts +21 -0
- package/integrations/bb-plugin-build/src/project-target.ts +45 -0
- package/integrations/bb-plugin-build/src/reference-index.ts +218 -0
- package/integrations/bb-plugin-build/src/repository-identity.ts +17 -0
- package/integrations/bb-plugin-build/src/repository-schema.ts +30 -0
- package/integrations/bb-plugin-build/src/result-explorer-view.ts +597 -0
- package/integrations/bb-plugin-build/src/result-readback-model.ts +163 -0
- package/integrations/bb-plugin-build/src/result-readback-schema.ts +319 -0
- package/integrations/bb-plugin-build/src/result-readback-service.ts +333 -0
- package/integrations/bb-plugin-build/src/run-page-view.ts +332 -0
- package/integrations/bb-plugin-build/src/setup-config-schema.ts +293 -0
- package/integrations/bb-plugin-build/src/setup-config-service.ts +620 -0
- package/integrations/bb-plugin-build/src/setup-config-view.ts +388 -0
- package/integrations/bb-plugin-build/src/sprint-close-model.ts +150 -0
- package/integrations/bb-plugin-build/src/sprint-close-schema.ts +128 -0
- package/integrations/bb-plugin-build/src/sprint-close-service.ts +293 -0
- package/integrations/bb-plugin-build/src/sprint-close-view.ts +116 -0
- package/integrations/bb-plugin-build/src/sprint-composition-model.ts +709 -0
- package/integrations/bb-plugin-build/src/sprint-composition-readback.ts +231 -0
- package/integrations/bb-plugin-build/src/sprint-composition-schema.ts +597 -0
- package/integrations/bb-plugin-build/src/sprint-composition-service.ts +773 -0
- package/integrations/bb-plugin-build/src/sprint-composition-view.ts +1221 -0
- package/integrations/bb-plugin-build/src/sprint-execution-schema.ts +188 -0
- package/integrations/bb-plugin-build/src/sprint-execution-service.ts +289 -0
- package/integrations/bb-plugin-build/src/sprint-execution-view.ts +54 -0
- package/integrations/bb-plugin-build/src/task-authoring-model.ts +563 -0
- package/integrations/bb-plugin-build/src/task-authoring-schema.ts +629 -0
- package/integrations/bb-plugin-build/src/task-authoring-service.ts +842 -0
- package/integrations/bb-plugin-build/src/task-authoring-view.ts +1227 -0
- package/integrations/bb-plugin-build/src/task-catalog-schema.ts +46 -0
- package/integrations/bb-plugin-build/src/task-catalog-service.ts +44 -0
- package/integrations/bb-plugin-build/src/task-detail-readback.ts +56 -0
- package/integrations/bb-plugin-build/src/task-detail-schema.ts +171 -0
- package/integrations/bb-plugin-build/src/task-navigation.ts +446 -0
- package/integrations/bb-plugin-build/src/workbench-route.ts +74 -0
- package/package.json +306 -4
- package/schemas/README.md +80 -0
- package/schemas/build.config.schema.json +2110 -0
- package/schemas/config-v1.schema.json +136 -0
- package/schemas/execution-config.schema.json +47 -0
- package/schemas/judge-scoring.schema.json +257 -0
- package/schemas/provider-config.schema.json +179 -0
- package/schemas/telemetry-config.schema.json +79 -0
- package/schemas/user-preferences.schema.json +141 -0
- package/scripts/assert-node-runtime.mjs +146 -0
- package/scripts/preflight-native.mjs +49 -0
- package/scripts/setup-git-hooks.mjs +39 -0
- package/scripts/sync.sh +2871 -0
- package/scripts/write-node-launcher.sh +138 -0
- package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
- package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
- package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
- package/templates/.build-project/README.md +41 -0
- package/templates/.build-project/agents/README.md +50 -0
- package/templates/.build-project/agents/architect.md +10 -0
- package/templates/.build-project/agents/code-reviewer.md +10 -0
- package/templates/.build-project/agents/debugger.md +10 -0
- package/templates/.build-project/agents/documenter.md +10 -0
- package/templates/.build-project/agents/test-automator.md +10 -0
- package/templates/.build-project/config/build.config.jsonc +64 -0
- package/templates/.build-project/config/project-context.template.json +6 -0
- package/templates/.build-project/config/task-type-aliases.presets/kanban.yaml +14 -0
- package/templates/.build-project/config/task-type-aliases.presets/scrum.yaml +17 -0
- package/templates/.build-project/config/task-type-aliases.presets/xp.yaml +12 -0
- package/templates/.build-project/config/task-type-aliases.yaml +29 -0
- package/templates/.build-project/gitignore.template +70 -0
- package/templates/.build-project/task-types/analysis.yaml +38 -0
- package/templates/.build-project/task-types/architecture.yaml +35 -0
- package/templates/.build-project/task-types/doc.yaml +34 -0
- package/templates/.build-project/task-types/feature.yaml +22 -0
- package/templates/.build-project/task-types/general.yaml +4 -0
- package/templates/.build-project/task-types/security.yaml +38 -0
- package/templates/AGENTS.template.md +85 -0
- package/templates/CLAUDE.template.md +1 -0
- package/templates/GEMINI.template.md +1 -0
- package/templates/builtin-workflows/code-review.yaml +73 -0
- package/templates/builtin-workflows/skills/code-review/SKILL.md +61 -0
- package/templates/builtin-workflows/skills/execution-contract-analysis/SKILL.md +24 -0
- package/templates/builtin-workflows/skills/execution-contract-code/SKILL.md +19 -0
- package/templates/builtin-workflows/skills/execution-contract-structured/SKILL.md +17 -0
- package/templates/ci/incident-intake.mjs +787 -0
- package/templates/ci/incidents.yml.template +103 -0
- package/templates/ci/node.yml.template +17 -0
- package/templates/ci/python.yml.template +17 -0
- package/templates/guard-kit/GUARD_SETUP.md.template +258 -0
- package/templates/guard-kit/build-guard.yml.template +189 -0
- package/templates/guard-kit/check.mjs.template +932 -0
- package/templates/guard-kit/config.json.template +7 -0
- package/templates/guard-kit/policy.json.template +1 -0
- package/templates/profiles/README.md +57 -0
- package/templates/profiles/frontier-claude.json +23 -0
- package/templates/profiles/frontier-codex.json +23 -0
|
@@ -0,0 +1,2177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cleanupStaleGitIndexLock
|
|
3
|
+
} from "./chunk-OMX5VL43.js";
|
|
4
|
+
import {
|
|
5
|
+
classifyTask
|
|
6
|
+
} from "./chunk-JHP7OT6K.js";
|
|
7
|
+
import {
|
|
8
|
+
analyzeTask
|
|
9
|
+
} from "./chunk-EKLUBDBQ.js";
|
|
10
|
+
import {
|
|
11
|
+
parsePromptSpec
|
|
12
|
+
} from "./chunk-INJ3SS6A.js";
|
|
13
|
+
import {
|
|
14
|
+
reapOrphanedTmuxSessions
|
|
15
|
+
} from "./chunk-B7PYTV72.js";
|
|
16
|
+
import {
|
|
17
|
+
calculateGuidanceTier,
|
|
18
|
+
generatePromptInjection,
|
|
19
|
+
hasUnfilledMarkers,
|
|
20
|
+
validateTaskSpec
|
|
21
|
+
} from "./chunk-UAPHFJPN.js";
|
|
22
|
+
import {
|
|
23
|
+
MemoryGuardian,
|
|
24
|
+
killAllFallbackTrackedSubprocesses
|
|
25
|
+
} from "./chunk-633XZ7UF.js";
|
|
26
|
+
import {
|
|
27
|
+
signalProcessGroup
|
|
28
|
+
} from "./chunk-6CB3KJRY.js";
|
|
29
|
+
import {
|
|
30
|
+
isDependencyManifestPath,
|
|
31
|
+
isGeneratedLockfilePath,
|
|
32
|
+
manifestKind
|
|
33
|
+
} from "./chunk-D4VJFCGZ.js";
|
|
34
|
+
import {
|
|
35
|
+
isProjectConfigPath
|
|
36
|
+
} from "./chunk-RPIMAMV7.js";
|
|
37
|
+
import {
|
|
38
|
+
MAX_CONSECUTIVE_PROGRESS_FAILURES,
|
|
39
|
+
PROGRESSIVE_CHECK_INTERVALS_MS
|
|
40
|
+
} from "./chunk-RWQQIKIC.js";
|
|
41
|
+
import {
|
|
42
|
+
isQuietOutputRequested
|
|
43
|
+
} from "./chunk-XWIZ6B5S.js";
|
|
44
|
+
import {
|
|
45
|
+
emitWarning
|
|
46
|
+
} from "./chunk-7VRJHTGZ.js";
|
|
47
|
+
|
|
48
|
+
// src/orchestrator/process-lifecycle-manager.ts
|
|
49
|
+
import { execFileSync } from "child_process";
|
|
50
|
+
import { promises as fs, readFileSync } from "fs";
|
|
51
|
+
import chalk from "chalk";
|
|
52
|
+
var PROGRESS_EARLY_TERMINATION_COMMANDS = /* @__PURE__ */ new Set([
|
|
53
|
+
// Provider CLIs can be legitimately quiet for long stretches while waiting on model output.
|
|
54
|
+
// Early termination on "no progress" causes false SIGTERM kills.
|
|
55
|
+
"claude",
|
|
56
|
+
"gemini",
|
|
57
|
+
"cursor-agent",
|
|
58
|
+
"codex",
|
|
59
|
+
"opencode",
|
|
60
|
+
"copilot"
|
|
61
|
+
]);
|
|
62
|
+
var PROVIDER_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
63
|
+
"claude",
|
|
64
|
+
"cursor",
|
|
65
|
+
"cursor-agent",
|
|
66
|
+
"codex",
|
|
67
|
+
"gemini",
|
|
68
|
+
"opencode",
|
|
69
|
+
"copilot"
|
|
70
|
+
]);
|
|
71
|
+
var HEAVY_TEST_COMMAND_TOKENS = /* @__PURE__ */ new Set(["vitest", "jest", "mocha", "ava", "tap", "test"]);
|
|
72
|
+
var HEAVY_BUILD_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
73
|
+
"build",
|
|
74
|
+
"typecheck",
|
|
75
|
+
"tsc",
|
|
76
|
+
"tsup",
|
|
77
|
+
"webpack",
|
|
78
|
+
"vite"
|
|
79
|
+
]);
|
|
80
|
+
var MERGE_CLEANUP_COMMAND_TOKENS = /* @__PURE__ */ new Set(["merge", "cleanup", "git"]);
|
|
81
|
+
var LIGHT_CLI_COMMAND_TOKENS = /* @__PURE__ */ new Set(["gh", "npm", "node"]);
|
|
82
|
+
var FORCE_KILL_GRACE_MS = 5e3;
|
|
83
|
+
var GROUP_EXIT_CONFIRMATION_MS = 1e3;
|
|
84
|
+
var GROUP_EXIT_POLL_MS = 50;
|
|
85
|
+
var CHILD_EXIT_CONFIRMATION_MS = 1e3;
|
|
86
|
+
var CHILD_EXIT_POLL_MS = 50;
|
|
87
|
+
function normalizeCommandToken(raw) {
|
|
88
|
+
const unquoted = raw.trim().replace(/^['"]|['"]$/g, "");
|
|
89
|
+
const normalizedSeparators = unquoted.replaceAll("\\", "/");
|
|
90
|
+
const base = normalizedSeparators.split("/").pop() ?? normalizedSeparators;
|
|
91
|
+
const lower = base.toLowerCase();
|
|
92
|
+
if (lower.endsWith(".exe") || lower.endsWith(".cmd")) {
|
|
93
|
+
return lower.slice(0, -4);
|
|
94
|
+
}
|
|
95
|
+
return lower;
|
|
96
|
+
}
|
|
97
|
+
function tokenizeCommand(command) {
|
|
98
|
+
if (typeof command !== "string" || command.trim().length === 0) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return command.split(/\s+/).map(normalizeCommandToken).filter((token) => token.length > 0 && !token.startsWith("-"));
|
|
102
|
+
}
|
|
103
|
+
function hasCommandToken(tokens, candidates) {
|
|
104
|
+
return tokens.some((token) => candidates.has(token));
|
|
105
|
+
}
|
|
106
|
+
function inferMemoryWorkloadClassFromMetadata(metadata) {
|
|
107
|
+
const tokens = tokenizeCommand(metadata?.command);
|
|
108
|
+
const provider = typeof metadata?.provider === "string" ? metadata.provider.trim() : "";
|
|
109
|
+
if (provider.length > 0 || hasCommandToken(tokens, PROVIDER_COMMAND_TOKENS)) {
|
|
110
|
+
return "provider-cli";
|
|
111
|
+
}
|
|
112
|
+
if (hasCommandToken(tokens, HEAVY_TEST_COMMAND_TOKENS)) {
|
|
113
|
+
return "heavy-test";
|
|
114
|
+
}
|
|
115
|
+
if (hasCommandToken(tokens, HEAVY_BUILD_COMMAND_TOKENS)) {
|
|
116
|
+
return "heavy-build";
|
|
117
|
+
}
|
|
118
|
+
if (hasCommandToken(tokens, MERGE_CLEANUP_COMMAND_TOKENS)) {
|
|
119
|
+
return "merge-cleanup";
|
|
120
|
+
}
|
|
121
|
+
if (hasCommandToken(tokens, LIGHT_CLI_COMMAND_TOKENS)) {
|
|
122
|
+
return "light-cli";
|
|
123
|
+
}
|
|
124
|
+
return "unknown";
|
|
125
|
+
}
|
|
126
|
+
var ProcessLifecycleManager = class _ProcessLifecycleManager {
|
|
127
|
+
static instance = null;
|
|
128
|
+
processes = /* @__PURE__ */ new Map();
|
|
129
|
+
config;
|
|
130
|
+
isShuttingDown = false;
|
|
131
|
+
progressMonitoringTimers = /* @__PURE__ */ new Map();
|
|
132
|
+
// Process type statistics
|
|
133
|
+
stats = {
|
|
134
|
+
total: 0,
|
|
135
|
+
running: 0,
|
|
136
|
+
completed: 0,
|
|
137
|
+
failed: 0,
|
|
138
|
+
terminated: 0,
|
|
139
|
+
timeout: 0
|
|
140
|
+
};
|
|
141
|
+
// Resource monitoring
|
|
142
|
+
resourceMonitoringTimer;
|
|
143
|
+
totalCpuUsage = 0;
|
|
144
|
+
resourcesKilledByMonitor = 0;
|
|
145
|
+
// Track the child handle and start identity together. A bare PID is not an
|
|
146
|
+
// ownership proof because the operating system may recycle it.
|
|
147
|
+
trackedChildProcesses = /* @__PURE__ */ new Map();
|
|
148
|
+
activeForcedKillTimers = /* @__PURE__ */ new Set();
|
|
149
|
+
// Automatic cleanup timer for old processes
|
|
150
|
+
oldProcessCleanupTimer;
|
|
151
|
+
// Configuration for automatic cleanup
|
|
152
|
+
OLD_PROCESS_TIMEOUT_MS = 2 * 60 * 60 * 1e3;
|
|
153
|
+
// 2 hours
|
|
154
|
+
CLEANUP_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
155
|
+
// Check every 5 minutes
|
|
156
|
+
// Retention window for terminal (completed/failed/terminated/timeout) registrations.
|
|
157
|
+
// Generous so callers can still inspect recent results, but bounded so long-lived
|
|
158
|
+
// managers do not retain completionPromise/childProcess refs and resource histories forever.
|
|
159
|
+
TERMINAL_REGISTRATION_RETENTION_MS = 2 * 60 * 60 * 1e3;
|
|
160
|
+
// 2 hours
|
|
161
|
+
// Count of terminal registrations evicted from the map after their retention
|
|
162
|
+
// window. Folded into stats.total so cumulative totals survive eviction.
|
|
163
|
+
evictedTerminalRegistrations = 0;
|
|
164
|
+
constructor(config = {}) {
|
|
165
|
+
this.config = {
|
|
166
|
+
defaultTimeoutMinutes: config.defaultTimeoutMinutes ?? 60,
|
|
167
|
+
cleanupOnTermination: config.cleanupOnTermination ?? false,
|
|
168
|
+
verbose: config.verbose ?? false,
|
|
169
|
+
maxConcurrentProcesses: config.maxConcurrentProcesses ?? 50,
|
|
170
|
+
verificationIntervalMs: config.verificationIntervalMs ?? 5e3,
|
|
171
|
+
resourceMonitoringIntervalMs: config.resourceMonitoringIntervalMs ?? 6e4,
|
|
172
|
+
maxCpuPercentage: config.maxCpuPercentage ?? 1e3,
|
|
173
|
+
maxProcessesWarning: config.maxProcessesWarning ?? 50,
|
|
174
|
+
progressiveCheckIntervals: config.progressiveCheckIntervals ?? PROGRESSIVE_CHECK_INTERVALS_MS,
|
|
175
|
+
maxConsecutiveProgressFailures: config.maxConsecutiveProgressFailures ?? MAX_CONSECUTIVE_PROGRESS_FAILURES
|
|
176
|
+
};
|
|
177
|
+
this.startResourceMonitoring();
|
|
178
|
+
this.startOldProcessCleanup();
|
|
179
|
+
if (this.config.verbose) {
|
|
180
|
+
console.log(chalk.gray("ProcessLifecycleManager initialized"));
|
|
181
|
+
console.log(chalk.gray(` Default timeout: ${this.config.defaultTimeoutMinutes} minutes`));
|
|
182
|
+
console.log(chalk.gray(` Max concurrent: ${this.config.maxConcurrentProcesses}`));
|
|
183
|
+
console.log(chalk.gray(` Verification interval: ${this.config.verificationIntervalMs}ms`));
|
|
184
|
+
console.log(
|
|
185
|
+
chalk.gray(` Resource monitoring: ${this.config.resourceMonitoringIntervalMs}ms`)
|
|
186
|
+
);
|
|
187
|
+
console.log(chalk.gray(` Max CPU: ${this.config.maxCpuPercentage}%`));
|
|
188
|
+
console.log(
|
|
189
|
+
chalk.gray(` Old process timeout: ${this.OLD_PROCESS_TIMEOUT_MS / 1e3 / 60 / 60} hours`)
|
|
190
|
+
);
|
|
191
|
+
console.log(
|
|
192
|
+
chalk.gray(
|
|
193
|
+
` Progressive checks: ${this.config.progressiveCheckIntervals.map((i) => `${i / 1e3 / 60}m`).join(", ")}`
|
|
194
|
+
)
|
|
195
|
+
);
|
|
196
|
+
console.log(
|
|
197
|
+
chalk.gray(` Max consecutive failures: ${this.config.maxConsecutiveProgressFailures}`)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
shouldSkipEarlyTerminationOnNoProgress(registration) {
|
|
202
|
+
const commandTokens = tokenizeCommand(registration.metadata?.command);
|
|
203
|
+
return commandTokens.some((token) => PROGRESS_EARLY_TERMINATION_COMMANDS.has(token)) || inferMemoryWorkloadClassFromMetadata(registration.metadata) === "provider-cli";
|
|
204
|
+
}
|
|
205
|
+
async cleanupStaleGitIndexLockForRegistration(registration) {
|
|
206
|
+
const candidates = [
|
|
207
|
+
registration.metadata?.cwd,
|
|
208
|
+
registration.metadata?.worktreePath,
|
|
209
|
+
registration.metadata?.repoRoot,
|
|
210
|
+
registration.metadata?.workingDirectory
|
|
211
|
+
].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
212
|
+
for (const cwd of candidates) {
|
|
213
|
+
try {
|
|
214
|
+
const cleaned = await cleanupStaleGitIndexLock(cwd);
|
|
215
|
+
if (cleaned) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get singleton instance of ProcessLifecycleManager
|
|
224
|
+
* Creates a new instance if one doesn't exist
|
|
225
|
+
*/
|
|
226
|
+
static getInstance(config) {
|
|
227
|
+
if (!_ProcessLifecycleManager.instance) {
|
|
228
|
+
_ProcessLifecycleManager.instance = new _ProcessLifecycleManager(config);
|
|
229
|
+
}
|
|
230
|
+
return _ProcessLifecycleManager.instance;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Reset the singleton instance (primarily for testing)
|
|
234
|
+
*/
|
|
235
|
+
static resetInstance() {
|
|
236
|
+
if (_ProcessLifecycleManager.instance) {
|
|
237
|
+
_ProcessLifecycleManager.instance.close().catch(console.error);
|
|
238
|
+
_ProcessLifecycleManager.instance = null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Register a new process using object-style parameters (preferred for new code)
|
|
243
|
+
*
|
|
244
|
+
* @param options - Process registration options
|
|
245
|
+
* @returns Promise that resolves to unique process ID
|
|
246
|
+
*/
|
|
247
|
+
async registerProcessWithOptions(options) {
|
|
248
|
+
const processId = `PLM-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
249
|
+
await this.registerProcess(
|
|
250
|
+
processId,
|
|
251
|
+
options.processType ?? "local",
|
|
252
|
+
options.completionPromise,
|
|
253
|
+
options.verificationFn,
|
|
254
|
+
options.terminateFn,
|
|
255
|
+
options.timeoutMs,
|
|
256
|
+
options.metadata,
|
|
257
|
+
options.progressCallback,
|
|
258
|
+
options.childProcess,
|
|
259
|
+
options.ownsProcessGroup
|
|
260
|
+
);
|
|
261
|
+
return processId;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Register a new process for lifecycle management
|
|
265
|
+
*
|
|
266
|
+
* @param processId - Unique process identifier
|
|
267
|
+
* @param processType - Process execution location
|
|
268
|
+
* @param completionPromise - Promise that resolves when process completes
|
|
269
|
+
* @param verificationFn - Function to verify actual completion
|
|
270
|
+
* @param terminateFn - Function to force terminate the process
|
|
271
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
272
|
+
* @param metadata - Additional metadata
|
|
273
|
+
* @param progressCallback - Optional callback to check if process is making progress
|
|
274
|
+
* @param childProcess - Optional child process reference for group kills
|
|
275
|
+
* @returns Process registration record
|
|
276
|
+
*/
|
|
277
|
+
async registerProcess(processId, processType, completionPromise, verificationFn, terminateFn, timeoutMs, metadata, progressCallback, childProcess, ownsProcessGroup = false) {
|
|
278
|
+
if (this.processes.size >= this.config.maxConcurrentProcesses) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`Maximum concurrent processes (${this.config.maxConcurrentProcesses}) reached`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (this.processes.has(processId)) {
|
|
284
|
+
throw new Error(`Process ${processId} already registered`);
|
|
285
|
+
}
|
|
286
|
+
const resolvedTimeoutMs = timeoutMs ?? this.config.defaultTimeoutMinutes * 60 * 1e3;
|
|
287
|
+
const timeoutEnabled = resolvedTimeoutMs > 0;
|
|
288
|
+
const actualTimeoutMs = timeoutEnabled ? resolvedTimeoutMs : 0;
|
|
289
|
+
const childPid = childProcess?.pid;
|
|
290
|
+
const processStartIdentity = childPid ? this.readProcessStartIdentity(childPid) : void 0;
|
|
291
|
+
const registration = {
|
|
292
|
+
processId,
|
|
293
|
+
processType,
|
|
294
|
+
childProcess,
|
|
295
|
+
ownsProcessGroup,
|
|
296
|
+
processStartIdentity,
|
|
297
|
+
completionPromise,
|
|
298
|
+
verificationFn,
|
|
299
|
+
terminateFn,
|
|
300
|
+
progressCallback,
|
|
301
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
302
|
+
timeoutMs: actualTimeoutMs,
|
|
303
|
+
timeoutEnabled,
|
|
304
|
+
status: "running",
|
|
305
|
+
metadata,
|
|
306
|
+
consecutiveProgressFailures: 0,
|
|
307
|
+
consecutiveFrequentProgressFailures: 0
|
|
308
|
+
};
|
|
309
|
+
this.processes.set(processId, registration);
|
|
310
|
+
this.trackChildProcess(registration);
|
|
311
|
+
this.updateStats();
|
|
312
|
+
if (this.config.verbose) {
|
|
313
|
+
console.log(chalk.cyan(`Registered process: ${processId} (${processType})`));
|
|
314
|
+
console.log(chalk.gray(` Timeout: ${actualTimeoutMs / 1e3}s`));
|
|
315
|
+
if (metadata) {
|
|
316
|
+
console.log(chalk.gray(` Metadata: ${JSON.stringify(metadata, null, 2)}`));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
this.monitorProcess(processId);
|
|
320
|
+
return registration;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Unregister a process from lifecycle management
|
|
324
|
+
* Cleans up timers and removes from tracking
|
|
325
|
+
*
|
|
326
|
+
* @param processId - Process ID to unregister
|
|
327
|
+
*/
|
|
328
|
+
unregisterProcess(processId) {
|
|
329
|
+
const registration = this.processes.get(processId);
|
|
330
|
+
if (registration) {
|
|
331
|
+
this.untrackRegistrationChild(registration);
|
|
332
|
+
this.clearHardTimeout(registration);
|
|
333
|
+
const progressTimer = this.progressMonitoringTimers.get(processId);
|
|
334
|
+
if (progressTimer) {
|
|
335
|
+
this.progressMonitoringTimers.delete(processId);
|
|
336
|
+
clearInterval(progressTimer);
|
|
337
|
+
}
|
|
338
|
+
if (registration.forceKillTimeoutHandle) {
|
|
339
|
+
this.clearForcedKillTimer(registration.forceKillTimeoutHandle);
|
|
340
|
+
registration.forceKillTimeoutHandle = void 0;
|
|
341
|
+
}
|
|
342
|
+
if (registration.progressCheckTimers) {
|
|
343
|
+
for (const timer of registration.progressCheckTimers) {
|
|
344
|
+
clearTimeout(timer);
|
|
345
|
+
}
|
|
346
|
+
registration.progressCheckTimers = void 0;
|
|
347
|
+
}
|
|
348
|
+
this.processes.delete(processId);
|
|
349
|
+
this.updateStats();
|
|
350
|
+
if (this.config.verbose) {
|
|
351
|
+
console.log(chalk.gray(`Unregistered process: ${processId}`));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
clearForcedKillTimer(timer) {
|
|
356
|
+
clearTimeout(timer);
|
|
357
|
+
this.activeForcedKillTimers.delete(timer);
|
|
358
|
+
}
|
|
359
|
+
/** Test seam around the OS process-start identity probe. */
|
|
360
|
+
readProcessStartIdentity(pid) {
|
|
361
|
+
if (!Number.isInteger(pid) || pid <= 0 || process.platform === "win32") return void 0;
|
|
362
|
+
try {
|
|
363
|
+
if (process.platform === "linux") {
|
|
364
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
365
|
+
const closeParen = stat.lastIndexOf(")");
|
|
366
|
+
if (closeParen < 0) return void 0;
|
|
367
|
+
const fieldsAfterCommand = stat.slice(closeParen + 2).trim().split(/\s+/);
|
|
368
|
+
const startTicks = fieldsAfterCommand[19];
|
|
369
|
+
return startTicks ? `proc-start-ticks:${startTicks}` : void 0;
|
|
370
|
+
}
|
|
371
|
+
const value = execFileSync(
|
|
372
|
+
"ps",
|
|
373
|
+
["-o", "lstart=", "-o", "ppid=", "-o", "command=", "-p", String(pid)],
|
|
374
|
+
{
|
|
375
|
+
timeout: 2e3,
|
|
376
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
377
|
+
encoding: "utf8"
|
|
378
|
+
}
|
|
379
|
+
).trim();
|
|
380
|
+
return value.length > 0 ? value : void 0;
|
|
381
|
+
} catch {
|
|
382
|
+
return void 0;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
readProcessGroupMembers(processGroupId) {
|
|
386
|
+
if (!Number.isInteger(processGroupId) || processGroupId <= 0 || process.platform === "win32") {
|
|
387
|
+
return void 0;
|
|
388
|
+
}
|
|
389
|
+
try {
|
|
390
|
+
const output = execFileSync("ps", ["-axo", "pid=,pgid="], {
|
|
391
|
+
timeout: 2e3,
|
|
392
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
393
|
+
encoding: "utf8"
|
|
394
|
+
});
|
|
395
|
+
const members = /* @__PURE__ */ new Map();
|
|
396
|
+
for (const line of output.split("\n")) {
|
|
397
|
+
const match = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
398
|
+
if (!match || Number(match[2]) !== processGroupId) continue;
|
|
399
|
+
const pid = Number(match[1]);
|
|
400
|
+
const identity = this.readProcessStartIdentity(pid);
|
|
401
|
+
if (!identity) return void 0;
|
|
402
|
+
members.set(pid, identity);
|
|
403
|
+
}
|
|
404
|
+
return members;
|
|
405
|
+
} catch {
|
|
406
|
+
return void 0;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
childHasExited(childProcess) {
|
|
410
|
+
return childProcess.exitCode != null || childProcess.signalCode != null;
|
|
411
|
+
}
|
|
412
|
+
probePidLiveness(pid) {
|
|
413
|
+
try {
|
|
414
|
+
process.kill(pid, 0);
|
|
415
|
+
return "alive";
|
|
416
|
+
} catch (error) {
|
|
417
|
+
if (this.isNoSuchProcessError(error)) return "gone";
|
|
418
|
+
if (error?.code === "EPERM") return "alive";
|
|
419
|
+
return "unavailable";
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
probeTrackedIdentity(tracked) {
|
|
423
|
+
const expectedIdentity = tracked.processStartIdentity;
|
|
424
|
+
const currentIdentity = this.readProcessStartIdentity(tracked.pid);
|
|
425
|
+
if (expectedIdentity && currentIdentity) {
|
|
426
|
+
return currentIdentity === expectedIdentity ? "match" : "mismatch";
|
|
427
|
+
}
|
|
428
|
+
const liveness = this.probePidLiveness(tracked.pid);
|
|
429
|
+
return liveness === "gone" ? "gone" : "unavailable";
|
|
430
|
+
}
|
|
431
|
+
probeOwnedProcessGroup(tracked) {
|
|
432
|
+
if (!tracked.ownsProcessGroup || process.platform === "win32") return "unavailable";
|
|
433
|
+
try {
|
|
434
|
+
signalProcessGroup(tracked.pid, 0);
|
|
435
|
+
return "alive";
|
|
436
|
+
} catch (error) {
|
|
437
|
+
if (this.isNoSuchProcessError(error)) return "gone";
|
|
438
|
+
return "unavailable";
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async waitForOwnedProcessGroupGone(tracked) {
|
|
442
|
+
const deadline = Date.now() + GROUP_EXIT_CONFIRMATION_MS;
|
|
443
|
+
let lastProbe = this.probeOwnedProcessGroup(tracked);
|
|
444
|
+
while (lastProbe !== "gone" && Date.now() < deadline) {
|
|
445
|
+
await new Promise((resolve) => {
|
|
446
|
+
setTimeout(resolve, GROUP_EXIT_POLL_MS);
|
|
447
|
+
});
|
|
448
|
+
lastProbe = this.probeOwnedProcessGroup(tracked);
|
|
449
|
+
}
|
|
450
|
+
return lastProbe;
|
|
451
|
+
}
|
|
452
|
+
async waitForTrackedChildGone(tracked) {
|
|
453
|
+
const deadline = Date.now() + CHILD_EXIT_CONFIRMATION_MS;
|
|
454
|
+
let lastProbe = this.probeTrackedIdentity(tracked);
|
|
455
|
+
while (!tracked.childExited && !this.childHasExited(tracked.childProcess) && lastProbe !== "gone" && lastProbe !== "mismatch" && Date.now() < deadline) {
|
|
456
|
+
await new Promise((resolve) => {
|
|
457
|
+
setTimeout(resolve, CHILD_EXIT_POLL_MS);
|
|
458
|
+
});
|
|
459
|
+
lastProbe = this.probeTrackedIdentity(tracked);
|
|
460
|
+
}
|
|
461
|
+
if (tracked.childExited || this.childHasExited(tracked.childProcess)) return "gone";
|
|
462
|
+
return lastProbe;
|
|
463
|
+
}
|
|
464
|
+
removeTrackedChildListeners(tracked) {
|
|
465
|
+
if (tracked.exitListener && typeof tracked.childProcess.removeListener === "function") {
|
|
466
|
+
tracked.childProcess.removeListener("exit", tracked.exitListener);
|
|
467
|
+
}
|
|
468
|
+
if (tracked.closeListener && typeof tracked.childProcess.removeListener === "function") {
|
|
469
|
+
tracked.childProcess.removeListener("close", tracked.closeListener);
|
|
470
|
+
}
|
|
471
|
+
tracked.exitListener = void 0;
|
|
472
|
+
tracked.closeListener = void 0;
|
|
473
|
+
}
|
|
474
|
+
trackChildProcess(registration) {
|
|
475
|
+
const childProcess = registration.childProcess;
|
|
476
|
+
const pid = childProcess?.pid;
|
|
477
|
+
if (!childProcess || !pid) return;
|
|
478
|
+
const previous = this.trackedChildProcesses.get(pid);
|
|
479
|
+
if (previous) {
|
|
480
|
+
this.untrackChildProcess(previous);
|
|
481
|
+
}
|
|
482
|
+
const tracked = {
|
|
483
|
+
processId: registration.processId,
|
|
484
|
+
pid,
|
|
485
|
+
childProcess,
|
|
486
|
+
ownsProcessGroup: registration.ownsProcessGroup,
|
|
487
|
+
processStartIdentity: registration.processStartIdentity,
|
|
488
|
+
childExited: this.childHasExited(childProcess),
|
|
489
|
+
processGroupOwnershipConfirmed: false,
|
|
490
|
+
leaderExitHandled: false
|
|
491
|
+
};
|
|
492
|
+
const onExit = () => {
|
|
493
|
+
tracked.childExited = true;
|
|
494
|
+
if (tracked.leaderExitHandled) return;
|
|
495
|
+
tracked.leaderExitHandled = true;
|
|
496
|
+
this.removeTrackedChildListeners(tracked);
|
|
497
|
+
if (tracked.forceKillPromise && tracked.ownsProcessGroup) {
|
|
498
|
+
if (tracked.processGroupOwnershipConfirmed && this.probeOwnedProcessGroup(tracked) === "gone") {
|
|
499
|
+
this.untrackChildProcess(tracked, { confirmed: true, reason: "group-gone" });
|
|
500
|
+
} else if (!tracked.processGroupOwnershipConfirmed) {
|
|
501
|
+
this.untrackChildProcess(tracked, {
|
|
502
|
+
confirmed: false,
|
|
503
|
+
reason: "group-liveness-unavailable"
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
this.untrackChildProcess(tracked, { confirmed: true, reason: "child-exited" });
|
|
509
|
+
};
|
|
510
|
+
tracked.exitListener = onExit;
|
|
511
|
+
tracked.closeListener = onExit;
|
|
512
|
+
if (typeof childProcess.once === "function") {
|
|
513
|
+
childProcess.once("exit", onExit);
|
|
514
|
+
childProcess.once("close", onExit);
|
|
515
|
+
}
|
|
516
|
+
this.trackedChildProcesses.set(pid, tracked);
|
|
517
|
+
}
|
|
518
|
+
untrackRegistrationChild(registration) {
|
|
519
|
+
const childProcess = registration.childProcess;
|
|
520
|
+
const pid = childProcess?.pid;
|
|
521
|
+
if (!childProcess || !pid) return;
|
|
522
|
+
const tracked = this.trackedChildProcesses.get(pid);
|
|
523
|
+
if (tracked && tracked.processId === registration.processId && tracked.childProcess === childProcess) {
|
|
524
|
+
this.untrackChildProcess(tracked);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
untrackChildProcess(tracked, outcome = { confirmed: false, reason: "tracking-removed" }) {
|
|
528
|
+
if (this.trackedChildProcesses.get(tracked.pid) === tracked) {
|
|
529
|
+
this.trackedChildProcesses.delete(tracked.pid);
|
|
530
|
+
}
|
|
531
|
+
this.removeTrackedChildListeners(tracked);
|
|
532
|
+
tracked.finishForceKill?.(outcome);
|
|
533
|
+
}
|
|
534
|
+
signalTrackedChild(tracked, signal) {
|
|
535
|
+
if (this.trackedChildProcesses.get(tracked.pid) !== tracked) {
|
|
536
|
+
return { delivered: false, identity: "gone", target: "none" };
|
|
537
|
+
}
|
|
538
|
+
const identity = this.probeTrackedIdentity(tracked);
|
|
539
|
+
if (identity === "mismatch" || identity === "gone") {
|
|
540
|
+
return { delivered: false, identity, target: "none" };
|
|
541
|
+
}
|
|
542
|
+
if (tracked.childExited || this.childHasExited(tracked.childProcess)) {
|
|
543
|
+
return { delivered: false, identity: "gone", target: "none" };
|
|
544
|
+
}
|
|
545
|
+
if (this.trackedChildProcesses.get(tracked.pid) !== tracked) {
|
|
546
|
+
return { delivered: false, identity: "gone", target: "none" };
|
|
547
|
+
}
|
|
548
|
+
const processGroupSignalRequested = tracked.ownsProcessGroup && process.platform !== "win32";
|
|
549
|
+
if (processGroupSignalRequested && identity === "match") {
|
|
550
|
+
const groupMembers = this.readProcessGroupMembers(tracked.pid);
|
|
551
|
+
if (groupMembers && groupMembers.get(tracked.pid) === tracked.processStartIdentity) {
|
|
552
|
+
tracked.processGroupMembersAtSignal = groupMembers;
|
|
553
|
+
tracked.processGroupOwnershipConfirmed = true;
|
|
554
|
+
try {
|
|
555
|
+
signalProcessGroup(tracked.pid, signal);
|
|
556
|
+
return { delivered: true, identity, target: "group" };
|
|
557
|
+
} catch (error) {
|
|
558
|
+
if (this.isNoSuchProcessError(error)) {
|
|
559
|
+
return { delivered: false, identity: "gone", target: "none" };
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
const delivered = typeof tracked.childProcess.kill === "function" ? tracked.childProcess.kill(signal) : false;
|
|
566
|
+
return { delivered, identity, target: delivered ? "child" : "none" };
|
|
567
|
+
} catch (error) {
|
|
568
|
+
if (!this.isNoSuchProcessError(error) && this.config.verbose) {
|
|
569
|
+
console.log(chalk.yellow(`Process signal failed: ${error}`));
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
delivered: false,
|
|
573
|
+
identity: this.isNoSuchProcessError(error) ? "gone" : identity,
|
|
574
|
+
target: "none"
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
async forceTrackedChildTermination(tracked, registration) {
|
|
579
|
+
if (tracked.ownsProcessGroup && tracked.processGroupOwnershipConfirmed) {
|
|
580
|
+
const groupState = this.probeOwnedProcessGroup(tracked);
|
|
581
|
+
if (groupState === "gone") {
|
|
582
|
+
return { confirmed: true, reason: "group-gone" };
|
|
583
|
+
}
|
|
584
|
+
const currentMembers = this.readProcessGroupMembers(tracked.pid);
|
|
585
|
+
const confirmedMembers = tracked.processGroupMembersAtSignal;
|
|
586
|
+
if (!currentMembers || !confirmedMembers) {
|
|
587
|
+
return { confirmed: false, reason: "group-liveness-unavailable" };
|
|
588
|
+
}
|
|
589
|
+
for (const [pid, identity] of currentMembers) {
|
|
590
|
+
if (confirmedMembers.get(pid) !== identity) {
|
|
591
|
+
return { confirmed: false, reason: "identity-changed" };
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
signalProcessGroup(tracked.pid, "SIGKILL");
|
|
596
|
+
if (registration) {
|
|
597
|
+
void this.cleanupStaleGitIndexLockForRegistration(registration);
|
|
598
|
+
}
|
|
599
|
+
} catch (error) {
|
|
600
|
+
if (this.isNoSuchProcessError(error)) {
|
|
601
|
+
return { confirmed: true, reason: "group-gone" };
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
confirmed: false,
|
|
605
|
+
reason: groupState === "unavailable" ? "group-liveness-unavailable" : "signal-failed"
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
const finalGroupState = await this.waitForOwnedProcessGroupGone(tracked);
|
|
609
|
+
if (finalGroupState === "gone") {
|
|
610
|
+
return { confirmed: true, reason: "group-gone" };
|
|
611
|
+
}
|
|
612
|
+
return {
|
|
613
|
+
confirmed: false,
|
|
614
|
+
reason: finalGroupState === "unavailable" ? "group-liveness-unavailable" : "group-still-alive"
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
const result = this.signalTrackedChild(tracked, "SIGKILL");
|
|
618
|
+
if (result.identity === "mismatch") {
|
|
619
|
+
return { confirmed: true, reason: "identity-changed" };
|
|
620
|
+
}
|
|
621
|
+
if (result.identity === "gone") {
|
|
622
|
+
return { confirmed: true, reason: "pid-gone" };
|
|
623
|
+
}
|
|
624
|
+
if (result.identity === "unavailable") {
|
|
625
|
+
return { confirmed: false, reason: "identity-unavailable" };
|
|
626
|
+
}
|
|
627
|
+
if (result.delivered) {
|
|
628
|
+
if (registration) {
|
|
629
|
+
void this.cleanupStaleGitIndexLockForRegistration(registration);
|
|
630
|
+
}
|
|
631
|
+
const finalIdentity = await this.waitForTrackedChildGone(tracked);
|
|
632
|
+
if (tracked.ownsProcessGroup) {
|
|
633
|
+
return { confirmed: false, reason: "group-liveness-unavailable" };
|
|
634
|
+
}
|
|
635
|
+
if (finalIdentity === "gone") {
|
|
636
|
+
return { confirmed: true, reason: "pid-gone" };
|
|
637
|
+
}
|
|
638
|
+
if (finalIdentity === "mismatch") {
|
|
639
|
+
return { confirmed: true, reason: "identity-changed" };
|
|
640
|
+
}
|
|
641
|
+
if (finalIdentity === "unavailable") {
|
|
642
|
+
return { confirmed: false, reason: "identity-unavailable" };
|
|
643
|
+
}
|
|
644
|
+
return { confirmed: false, reason: "child-still-alive" };
|
|
645
|
+
}
|
|
646
|
+
return { confirmed: false, reason: "signal-failed" };
|
|
647
|
+
}
|
|
648
|
+
beginTrackedChildTermination(tracked, registration) {
|
|
649
|
+
if (tracked.forceKillPromise) return tracked.forceKillPromise;
|
|
650
|
+
let settled = false;
|
|
651
|
+
let resolvePromise;
|
|
652
|
+
const forceKillPromise = new Promise((resolve) => {
|
|
653
|
+
resolvePromise = resolve;
|
|
654
|
+
});
|
|
655
|
+
tracked.forceKillPromise = forceKillPromise;
|
|
656
|
+
const finish = (outcome) => {
|
|
657
|
+
if (settled) return;
|
|
658
|
+
settled = true;
|
|
659
|
+
const timer2 = tracked.forceKillTimer;
|
|
660
|
+
if (timer2) {
|
|
661
|
+
this.clearForcedKillTimer(timer2);
|
|
662
|
+
tracked.forceKillTimer = void 0;
|
|
663
|
+
}
|
|
664
|
+
if (registration && registration.forceKillTimeoutHandle === timer2) {
|
|
665
|
+
registration.forceKillTimeoutHandle = void 0;
|
|
666
|
+
}
|
|
667
|
+
tracked.finishForceKill = void 0;
|
|
668
|
+
resolvePromise(outcome);
|
|
669
|
+
};
|
|
670
|
+
tracked.finishForceKill = finish;
|
|
671
|
+
const initialSignal = this.signalTrackedChild(tracked, "SIGTERM");
|
|
672
|
+
if (initialSignal.identity === "mismatch" || initialSignal.identity === "gone") {
|
|
673
|
+
this.untrackChildProcess(tracked, {
|
|
674
|
+
confirmed: true,
|
|
675
|
+
reason: initialSignal.identity === "mismatch" ? "identity-changed" : "pid-gone"
|
|
676
|
+
});
|
|
677
|
+
return forceKillPromise;
|
|
678
|
+
}
|
|
679
|
+
if (settled) return forceKillPromise;
|
|
680
|
+
const timer = setTimeout(() => {
|
|
681
|
+
void this.forceTrackedChildTermination(tracked, registration).then((outcome) => {
|
|
682
|
+
this.untrackChildProcess(tracked, outcome);
|
|
683
|
+
finish(outcome);
|
|
684
|
+
}).catch(() => {
|
|
685
|
+
const outcome = {
|
|
686
|
+
confirmed: false,
|
|
687
|
+
reason: "signal-failed"
|
|
688
|
+
};
|
|
689
|
+
this.untrackChildProcess(tracked, outcome);
|
|
690
|
+
finish(outcome);
|
|
691
|
+
});
|
|
692
|
+
}, FORCE_KILL_GRACE_MS);
|
|
693
|
+
tracked.forceKillTimer = timer;
|
|
694
|
+
this.activeForcedKillTimers.add(timer);
|
|
695
|
+
if (registration) {
|
|
696
|
+
registration.forceKillTimeoutHandle = timer;
|
|
697
|
+
}
|
|
698
|
+
return forceKillPromise;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* True when a kill/signal error indicates the target process no longer exists
|
|
702
|
+
* (ESRCH / "no such process"). This is the success case for a late SIGKILL: the
|
|
703
|
+
* process already exited on its own, so the signal harmlessly missed. It must be
|
|
704
|
+
* swallowed silently rather than surfaced as a termination failure.
|
|
705
|
+
*/
|
|
706
|
+
isNoSuchProcessError(error) {
|
|
707
|
+
if (error && typeof error === "object") {
|
|
708
|
+
const code = error.code;
|
|
709
|
+
if (code === "ESRCH") {
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
714
|
+
return /no such process/i.test(message);
|
|
715
|
+
}
|
|
716
|
+
clearHardTimeout(registration) {
|
|
717
|
+
if (registration.hardTimeoutHandle) {
|
|
718
|
+
clearTimeout(registration.hardTimeoutHandle);
|
|
719
|
+
registration.hardTimeoutHandle = void 0;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Monitor a registered process from birth to death
|
|
724
|
+
*/
|
|
725
|
+
async monitorProcess(processId) {
|
|
726
|
+
const registration = this.processes.get(processId);
|
|
727
|
+
if (!registration) {
|
|
728
|
+
emitWarning(
|
|
729
|
+
"PR-001",
|
|
730
|
+
{
|
|
731
|
+
taskId: processId,
|
|
732
|
+
subsystem: "orchestrator",
|
|
733
|
+
executionPhase: "execution"
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
severity: "WARN" /* WARN */,
|
|
737
|
+
metadata: {
|
|
738
|
+
operation: "monitorProcess",
|
|
739
|
+
reason: "Process not found for monitoring"
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
if (registration.timeoutEnabled) {
|
|
746
|
+
this.setupProgressiveTimeoutChecks(processId);
|
|
747
|
+
this.setupFrequentProgressMonitoring(processId);
|
|
748
|
+
registration.hardTimeoutHandle = setTimeout(async () => {
|
|
749
|
+
try {
|
|
750
|
+
const current = this.processes.get(processId);
|
|
751
|
+
if (current && current.status === "running") {
|
|
752
|
+
console.log(
|
|
753
|
+
chalk.yellow(`Process ${processId} timed out after ${registration.timeoutMs / 1e3}s`)
|
|
754
|
+
);
|
|
755
|
+
await this.terminateProcess(processId, "timeout");
|
|
756
|
+
}
|
|
757
|
+
} catch (error) {
|
|
758
|
+
emitWarning(
|
|
759
|
+
"PR-001",
|
|
760
|
+
{
|
|
761
|
+
taskId: processId,
|
|
762
|
+
subsystem: "orchestrator",
|
|
763
|
+
executionPhase: "execution"
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
severity: "WARN" /* WARN */,
|
|
767
|
+
metadata: {
|
|
768
|
+
operation: "timeoutTerminate",
|
|
769
|
+
reason: "terminateProcess_failed",
|
|
770
|
+
error: error instanceof Error ? error.message : String(error)
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
}, registration.timeoutMs);
|
|
776
|
+
registration.hardTimeoutHandle.unref?.();
|
|
777
|
+
}
|
|
778
|
+
try {
|
|
779
|
+
const result = await registration.completionPromise;
|
|
780
|
+
this.clearHardTimeout(registration);
|
|
781
|
+
if (!this.processes.has(processId) || registration.status !== "running") {
|
|
782
|
+
this.updateStats();
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
const isVerified = await this.verifyProcess(processId);
|
|
786
|
+
if (!this.processes.has(processId) || registration.status !== "running") {
|
|
787
|
+
this.updateStats();
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (isVerified) {
|
|
791
|
+
registration.status = "completed";
|
|
792
|
+
registration.terminalAt = Date.now();
|
|
793
|
+
registration.result = result;
|
|
794
|
+
this.stats.completed++;
|
|
795
|
+
this.untrackRegistrationChild(registration);
|
|
796
|
+
if (this.config.verbose) {
|
|
797
|
+
console.log(chalk.green(`Process ${processId} completed and verified`));
|
|
798
|
+
}
|
|
799
|
+
} else {
|
|
800
|
+
console.log(chalk.red(`Process ${processId} claimed success but verification failed`));
|
|
801
|
+
registration.status = "failed";
|
|
802
|
+
registration.terminalAt = Date.now();
|
|
803
|
+
registration.error = new Error("verification_failed");
|
|
804
|
+
this.stats.failed++;
|
|
805
|
+
await this.cleanupProcess(processId);
|
|
806
|
+
this.updateStats();
|
|
807
|
+
}
|
|
808
|
+
} catch (error) {
|
|
809
|
+
this.clearHardTimeout(registration);
|
|
810
|
+
if (!this.processes.has(processId) || registration.status !== "running") {
|
|
811
|
+
this.updateStats();
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
registration.status = "failed";
|
|
815
|
+
registration.terminalAt = Date.now();
|
|
816
|
+
registration.error = error instanceof Error ? error : new Error(String(error));
|
|
817
|
+
this.stats.failed++;
|
|
818
|
+
if (this.config.verbose) {
|
|
819
|
+
console.log(chalk.red(`Process ${processId} failed: ${registration.error.message}`));
|
|
820
|
+
}
|
|
821
|
+
await this.cleanupProcess(processId);
|
|
822
|
+
}
|
|
823
|
+
this.updateStats();
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Verify actual process completion (mandatory verification loop)
|
|
827
|
+
*
|
|
828
|
+
* CRITICAL: Never trust process completion signals without verification
|
|
829
|
+
*/
|
|
830
|
+
async verifyProcess(processId) {
|
|
831
|
+
const registration = this.processes.get(processId);
|
|
832
|
+
if (!registration) {
|
|
833
|
+
return false;
|
|
834
|
+
}
|
|
835
|
+
try {
|
|
836
|
+
const startTime = Date.now();
|
|
837
|
+
const isVerified = await registration.verificationFn();
|
|
838
|
+
const verificationTime = Date.now() - startTime;
|
|
839
|
+
if (this.config.verbose) {
|
|
840
|
+
console.log(
|
|
841
|
+
chalk.gray(
|
|
842
|
+
`Process ${processId} verification: ${isVerified ? "PASSED" : "FAILED"} (${verificationTime}ms)`
|
|
843
|
+
)
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
return isVerified;
|
|
847
|
+
} catch (error) {
|
|
848
|
+
console.log(chalk.red(`Process ${processId} verification _error: ${error}`));
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Set up progressive timeout checks at intermediate intervals
|
|
854
|
+
*
|
|
855
|
+
* This sets up checkpoints at 15min, 30min, 60min etc. to verify the process
|
|
856
|
+
* is still making progress. If no progress is detected for consecutive checks,
|
|
857
|
+
* the process may be terminated early.
|
|
858
|
+
*/
|
|
859
|
+
setupProgressiveTimeoutChecks(processId) {
|
|
860
|
+
const registration = this.processes.get(processId);
|
|
861
|
+
if (!registration) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const relevantIntervals = this.config.progressiveCheckIntervals.filter(
|
|
865
|
+
(interval) => interval < registration.timeoutMs
|
|
866
|
+
);
|
|
867
|
+
const timers = [];
|
|
868
|
+
if (relevantIntervals.length === 0) {
|
|
869
|
+
const minCheckInterval = this.config.verificationIntervalMs;
|
|
870
|
+
if (minCheckInterval < registration.timeoutMs) {
|
|
871
|
+
const checkCount = Math.max(2, Math.floor(registration.timeoutMs / minCheckInterval / 2));
|
|
872
|
+
for (let i = 1; i <= checkCount; i++) {
|
|
873
|
+
const checkTime = minCheckInterval * i;
|
|
874
|
+
if (checkTime < registration.timeoutMs) {
|
|
875
|
+
const timer = setTimeout(async () => {
|
|
876
|
+
await this.performProgressCheck(processId, checkTime);
|
|
877
|
+
}, checkTime);
|
|
878
|
+
timer.unref();
|
|
879
|
+
timers.push(timer);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (this.config.verbose && timers.length > 0) {
|
|
883
|
+
console.log(
|
|
884
|
+
chalk.gray(
|
|
885
|
+
`Set up ${timers.length} minimum progress checks for ${processId} at intervals of ${minCheckInterval}ms (timeout ${registration.timeoutMs}ms)`
|
|
886
|
+
)
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
} else if (this.config.verbose) {
|
|
890
|
+
console.log(
|
|
891
|
+
chalk.gray(
|
|
892
|
+
`No progress checks for ${processId} (timeout ${registration.timeoutMs}ms is too short)`
|
|
893
|
+
)
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
} else {
|
|
897
|
+
for (const interval of relevantIntervals) {
|
|
898
|
+
const timer = setTimeout(async () => {
|
|
899
|
+
await this.performProgressCheck(processId, interval);
|
|
900
|
+
}, interval);
|
|
901
|
+
timer.unref();
|
|
902
|
+
timers.push(timer);
|
|
903
|
+
}
|
|
904
|
+
if (this.config.verbose) {
|
|
905
|
+
console.log(
|
|
906
|
+
chalk.gray(
|
|
907
|
+
`Set up ${relevantIntervals.length} progressive timeout checks for ${processId}: ${relevantIntervals.map((i) => `${i / 1e3 / 60}m`).join(", ")}`
|
|
908
|
+
)
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (timers.length > 0) {
|
|
913
|
+
registration.progressCheckTimers = timers;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Perform a progress check at a specific interval
|
|
918
|
+
*
|
|
919
|
+
* Checks if the process is:
|
|
920
|
+
* - Still alive
|
|
921
|
+
* - Making progress (file changes, output, custom callback)
|
|
922
|
+
* - Within resource limits
|
|
923
|
+
*
|
|
924
|
+
* If no progress detected, increments failure counter.
|
|
925
|
+
* If consecutive failures exceed threshold, considers early termination.
|
|
926
|
+
*/
|
|
927
|
+
async performProgressCheck(processId, intervalMs) {
|
|
928
|
+
const registration = this.processes.get(processId);
|
|
929
|
+
if (!registration) {
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
if (registration.status !== "running") {
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
const elapsedMs = Date.now() - registration.createdAt.getTime();
|
|
936
|
+
const elapsedMinutes = Math.round(elapsedMs / 1e3 / 60);
|
|
937
|
+
if (this.config.verbose) {
|
|
938
|
+
console.log(
|
|
939
|
+
chalk.blue(
|
|
940
|
+
`Progressive check for ${processId} at ${elapsedMinutes}min mark (${intervalMs / 1e3 / 60}min checkpoint)`
|
|
941
|
+
)
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
const isAlive = await this.checkProcessAlive(processId);
|
|
945
|
+
if (!isAlive) {
|
|
946
|
+
console.log(
|
|
947
|
+
chalk.yellow(`Process ${processId} is not alive at ${elapsedMinutes}min checkpoint`)
|
|
948
|
+
);
|
|
949
|
+
registration.consecutiveProgressFailures++;
|
|
950
|
+
await this.handleProgressFailure(processId, elapsedMinutes);
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
const hasProgress = await this.checkProcessProgress(processId);
|
|
954
|
+
if (!hasProgress) {
|
|
955
|
+
if (!this.config.verbose && this.shouldSkipEarlyTerminationOnNoProgress(registration)) {
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
console.log(
|
|
959
|
+
chalk.yellow(
|
|
960
|
+
`Process ${processId} shows no progress at ${elapsedMinutes}min checkpoint (${registration.consecutiveProgressFailures + 1}/${this.config.maxConsecutiveProgressFailures} failures)`
|
|
961
|
+
)
|
|
962
|
+
);
|
|
963
|
+
registration.consecutiveProgressFailures++;
|
|
964
|
+
await this.handleProgressFailure(processId, elapsedMinutes);
|
|
965
|
+
} else {
|
|
966
|
+
if (registration.consecutiveProgressFailures > 0) {
|
|
967
|
+
console.log(
|
|
968
|
+
chalk.green(
|
|
969
|
+
`Process ${processId} showing progress at ${elapsedMinutes}min checkpoint (reset failure counter)`
|
|
970
|
+
)
|
|
971
|
+
);
|
|
972
|
+
registration.consecutiveProgressFailures = 0;
|
|
973
|
+
} else if (this.config.verbose) {
|
|
974
|
+
console.log(
|
|
975
|
+
chalk.green(`Process ${processId} showing progress at ${elapsedMinutes}min checkpoint`)
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Check if a process is still alive (has running child process or recent activity)
|
|
982
|
+
*/
|
|
983
|
+
async checkProcessAlive(processId) {
|
|
984
|
+
const registration = this.processes.get(processId);
|
|
985
|
+
if (!registration) {
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
if (registration.childProcess) {
|
|
989
|
+
return !registration.childProcess.killed && registration.childProcess.exitCode === null;
|
|
990
|
+
}
|
|
991
|
+
return true;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Check if a process is making progress
|
|
995
|
+
*
|
|
996
|
+
* Progress indicators:
|
|
997
|
+
* 1. Custom progress callback (if provided)
|
|
998
|
+
* 2. Resource usage changes (CPU/memory activity)
|
|
999
|
+
* 3. Recent output/activity
|
|
1000
|
+
*
|
|
1001
|
+
*/
|
|
1002
|
+
async checkProcessProgress(processId) {
|
|
1003
|
+
const registration = this.processes.get(processId);
|
|
1004
|
+
if (!registration) {
|
|
1005
|
+
return false;
|
|
1006
|
+
}
|
|
1007
|
+
if (registration.progressCallback) {
|
|
1008
|
+
try {
|
|
1009
|
+
const currentProgress = await registration.progressCallback();
|
|
1010
|
+
registration.lastProgress = currentProgress;
|
|
1011
|
+
const baseline = registration.lastProgressForTermination;
|
|
1012
|
+
if (!baseline) {
|
|
1013
|
+
registration.lastProgressForTermination = currentProgress;
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
1016
|
+
const hasFileChanges = currentProgress.filesChanged > baseline.filesChanged;
|
|
1017
|
+
const hasOutput = currentProgress.outputLines > baseline.outputLines;
|
|
1018
|
+
const hasActivity = currentProgress.lastActivityTimestamp > baseline.lastActivityTimestamp;
|
|
1019
|
+
const hasProgress = hasFileChanges || hasOutput || hasActivity;
|
|
1020
|
+
if (hasProgress) {
|
|
1021
|
+
registration.lastProgressForTermination = currentProgress;
|
|
1022
|
+
}
|
|
1023
|
+
return hasProgress;
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
if (this.config.verbose) {
|
|
1026
|
+
console.log(
|
|
1027
|
+
chalk.yellow(
|
|
1028
|
+
`Progress callback failed for ${processId}: ${error instanceof Error ? error.message : String(error)}`
|
|
1029
|
+
)
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
if (registration.resourceHistory && registration.resourceHistory.length >= 2) {
|
|
1035
|
+
const recent = registration.resourceHistory.slice(-5);
|
|
1036
|
+
const hasCpuActivity = recent.some((usage) => usage.cpuPercentage > 0.1);
|
|
1037
|
+
if (hasCpuActivity) {
|
|
1038
|
+
return true;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
if (registration.childProcess && !registration.resourceHistory) {
|
|
1042
|
+
return true;
|
|
1043
|
+
}
|
|
1044
|
+
if (!registration.progressCallback && !registration.childProcess && (!registration.resourceHistory || registration.resourceHistory.length === 0)) {
|
|
1045
|
+
if (this.config.verbose) {
|
|
1046
|
+
console.log(
|
|
1047
|
+
chalk.gray(`No progress monitoring available for ${processId}, assuming progress`)
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
return true;
|
|
1051
|
+
}
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Handle consecutive progress check failures
|
|
1056
|
+
*
|
|
1057
|
+
* If failures exceed threshold, terminate the process early
|
|
1058
|
+
*/
|
|
1059
|
+
async handleProgressFailure(processId, elapsedMinutes) {
|
|
1060
|
+
const registration = this.processes.get(processId);
|
|
1061
|
+
if (!registration) {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
if (registration.consecutiveProgressFailures >= this.config.maxConsecutiveProgressFailures) {
|
|
1065
|
+
const elapsedMs = Date.now() - registration.createdAt.getTime();
|
|
1066
|
+
const earlyLifecycleMs = Math.min(5 * 60 * 1e3, Math.floor(registration.timeoutMs * 0.1));
|
|
1067
|
+
if (elapsedMs < earlyLifecycleMs) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
if (this.shouldSkipEarlyTerminationOnNoProgress(registration)) {
|
|
1071
|
+
if (this.config.verbose) {
|
|
1072
|
+
console.log(
|
|
1073
|
+
chalk.gray(
|
|
1074
|
+
`Skipping early termination for ${processId} (provider CLI; no-progress heuristics are noisy)`
|
|
1075
|
+
)
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
const nearTimeoutMs = Math.floor(registration.timeoutMs * 0.9);
|
|
1081
|
+
if (elapsedMs < nearTimeoutMs) {
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
console.log(
|
|
1085
|
+
chalk.red(
|
|
1086
|
+
`Process ${processId} failed ${registration.consecutiveProgressFailures} consecutive progress checks - terminating early (${elapsedMinutes}min elapsed)`
|
|
1087
|
+
)
|
|
1088
|
+
);
|
|
1089
|
+
await this.terminateProcess(processId, "progress_check_failed");
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Force terminate a process
|
|
1094
|
+
*
|
|
1095
|
+
* @param processId - Process to terminate
|
|
1096
|
+
* @param reason - Reason for termination
|
|
1097
|
+
*/
|
|
1098
|
+
async terminateProcess(processId, reason) {
|
|
1099
|
+
const registration = this.processes.get(processId);
|
|
1100
|
+
if (!registration) {
|
|
1101
|
+
emitWarning(
|
|
1102
|
+
"PR-001",
|
|
1103
|
+
{
|
|
1104
|
+
taskId: processId,
|
|
1105
|
+
subsystem: "orchestrator",
|
|
1106
|
+
executionPhase: "cleanup"
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
severity: "WARN" /* WARN */,
|
|
1110
|
+
metadata: {
|
|
1111
|
+
operation: "terminateProcess",
|
|
1112
|
+
reason: "Process not found for termination"
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
);
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (registration.status === "completed" || registration.status === "failed" || registration.status === "terminating" || registration.status === "terminated" || registration.status === "timeout") {
|
|
1119
|
+
if (this.config.verbose) {
|
|
1120
|
+
console.log(
|
|
1121
|
+
chalk.gray(
|
|
1122
|
+
`Skipping termination for terminal process ${processId} (${registration.status})`
|
|
1123
|
+
)
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
console.log(chalk.yellow(`Terminating process ${processId} (${reason})`));
|
|
1129
|
+
registration.terminationFailed = false;
|
|
1130
|
+
const statusBeforeTermination = registration.status;
|
|
1131
|
+
if (statusBeforeTermination === "running") {
|
|
1132
|
+
registration.status = "terminating";
|
|
1133
|
+
}
|
|
1134
|
+
try {
|
|
1135
|
+
await registration.terminateFn({ reason, source: "process-lifecycle-manager" });
|
|
1136
|
+
registration.terminationFailed = false;
|
|
1137
|
+
registration.status = reason === "timeout" ? "timeout" : "terminated";
|
|
1138
|
+
registration.terminalAt = Date.now();
|
|
1139
|
+
this.stats[reason === "timeout" ? "timeout" : "terminated"]++;
|
|
1140
|
+
if (this.config.verbose) {
|
|
1141
|
+
console.log(chalk.yellow(`Process ${processId} terminated (${reason})`));
|
|
1142
|
+
}
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
if (registration.status === "terminating") {
|
|
1145
|
+
registration.status = statusBeforeTermination;
|
|
1146
|
+
}
|
|
1147
|
+
registration.error = error instanceof Error ? error : new Error(`Termination failed: ${String(error)}`);
|
|
1148
|
+
registration.terminationFailed = true;
|
|
1149
|
+
emitWarning(
|
|
1150
|
+
"PR-001",
|
|
1151
|
+
{
|
|
1152
|
+
taskId: processId,
|
|
1153
|
+
subsystem: "orchestrator",
|
|
1154
|
+
executionPhase: "cleanup"
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
severity: "ERROR" /* ERROR */,
|
|
1158
|
+
metadata: {
|
|
1159
|
+
operation: "terminateProcess",
|
|
1160
|
+
reason: "Process termination failed",
|
|
1161
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
await this.cleanupProcess(processId);
|
|
1167
|
+
this.updateStats();
|
|
1168
|
+
}
|
|
1169
|
+
async finalizeStaleTrackedRegistration(processId, registration, tracked, identity, terminationReason) {
|
|
1170
|
+
this.untrackChildProcess(tracked, {
|
|
1171
|
+
confirmed: true,
|
|
1172
|
+
reason: identity === "mismatch" ? "identity-changed" : "pid-gone"
|
|
1173
|
+
});
|
|
1174
|
+
if (registration.status === "running") {
|
|
1175
|
+
const terminalStatus = terminationReason === "timeout" ? "timeout" : "terminated";
|
|
1176
|
+
registration.terminationFailed = false;
|
|
1177
|
+
registration.status = terminalStatus;
|
|
1178
|
+
registration.terminalAt = Date.now();
|
|
1179
|
+
this.stats[terminalStatus]++;
|
|
1180
|
+
}
|
|
1181
|
+
await this.cleanupProcess(processId);
|
|
1182
|
+
this.updateStats();
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Gracefully terminate a process: send SIGTERM, wait up to graceMs for the
|
|
1186
|
+
* process to exit on its own, then fall back to forced termination via
|
|
1187
|
+
* terminateProcess() if it is still running.
|
|
1188
|
+
*
|
|
1189
|
+
* terminateProcess()/terminateFn have no graceful option, so the graceful
|
|
1190
|
+
* phase signals the child process directly (process group first, mirroring
|
|
1191
|
+
* cleanupProcess). If the process exits during the grace window, the
|
|
1192
|
+
* regular monitorProcess completion handling owns the final status; no
|
|
1193
|
+
* forced termination is issued.
|
|
1194
|
+
*
|
|
1195
|
+
* @param processId - Process to terminate
|
|
1196
|
+
* @param reason - Reason for termination
|
|
1197
|
+
* @param graceMs - Milliseconds to wait after SIGTERM before forcing
|
|
1198
|
+
*/
|
|
1199
|
+
async terminateProcessWithGrace(processId, reason, graceMs) {
|
|
1200
|
+
const registration = this.processes.get(processId);
|
|
1201
|
+
const child = registration?.childProcess;
|
|
1202
|
+
const childExited = () => !child || child.exitCode !== null || child.signalCode !== null;
|
|
1203
|
+
if (!registration || !child || !child.pid || childExited()) {
|
|
1204
|
+
await this.terminateProcess(processId, reason);
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
try {
|
|
1208
|
+
const tracked = this.trackedChildProcesses.get(child.pid);
|
|
1209
|
+
if (tracked && tracked.processId === processId && tracked.childProcess === child) {
|
|
1210
|
+
const signalResult = this.signalTrackedChild(tracked, "SIGTERM");
|
|
1211
|
+
if (signalResult.identity === "mismatch" || signalResult.identity === "gone") {
|
|
1212
|
+
await this.finalizeStaleTrackedRegistration(
|
|
1213
|
+
processId,
|
|
1214
|
+
registration,
|
|
1215
|
+
tracked,
|
|
1216
|
+
signalResult.identity,
|
|
1217
|
+
reason
|
|
1218
|
+
);
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
} else {
|
|
1222
|
+
await this.terminateProcess(processId, reason);
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
} catch {
|
|
1226
|
+
}
|
|
1227
|
+
await new Promise((resolve) => {
|
|
1228
|
+
const finish = () => {
|
|
1229
|
+
clearTimeout(timer);
|
|
1230
|
+
child.removeListener("exit", finish);
|
|
1231
|
+
resolve();
|
|
1232
|
+
};
|
|
1233
|
+
const timer = setTimeout(finish, graceMs);
|
|
1234
|
+
timer.unref?.();
|
|
1235
|
+
if (childExited()) {
|
|
1236
|
+
finish();
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
child.once("exit", finish);
|
|
1240
|
+
});
|
|
1241
|
+
const current = this.processes.get(processId);
|
|
1242
|
+
if (current && current.status === "running") {
|
|
1243
|
+
await this.terminateProcess(processId, reason);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Clean up process resources
|
|
1248
|
+
*/
|
|
1249
|
+
async cleanupProcess(processId) {
|
|
1250
|
+
const registration = this.processes.get(processId);
|
|
1251
|
+
if (!registration) {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
this.clearHardTimeout(registration);
|
|
1255
|
+
const progressTimer = this.progressMonitoringTimers.get(processId);
|
|
1256
|
+
if (progressTimer) {
|
|
1257
|
+
this.progressMonitoringTimers.delete(processId);
|
|
1258
|
+
clearInterval(progressTimer);
|
|
1259
|
+
}
|
|
1260
|
+
if (registration.progressCheckTimers) {
|
|
1261
|
+
for (const timer of registration.progressCheckTimers) {
|
|
1262
|
+
clearTimeout(timer);
|
|
1263
|
+
}
|
|
1264
|
+
registration.progressCheckTimers = void 0;
|
|
1265
|
+
}
|
|
1266
|
+
const childProcess = registration.childProcess;
|
|
1267
|
+
const pid = childProcess?.pid;
|
|
1268
|
+
if (childProcess && pid) {
|
|
1269
|
+
const tracked = this.trackedChildProcesses.get(pid);
|
|
1270
|
+
if (this.childHasExited(childProcess)) {
|
|
1271
|
+
if (tracked) this.untrackChildProcess(tracked);
|
|
1272
|
+
} else if (tracked && tracked.processId === registration.processId && tracked.childProcess === childProcess) {
|
|
1273
|
+
void this.beginTrackedChildTermination(tracked, registration).then((outcome) => {
|
|
1274
|
+
if (!outcome.confirmed) {
|
|
1275
|
+
registration.terminationFailed = true;
|
|
1276
|
+
registration.error = new Error(`Child process cleanup unconfirmed: ${outcome.reason}`);
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
if (this.config.cleanupOnTermination && registration.status !== "completed") {
|
|
1282
|
+
this.processes.delete(processId);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Set up frequent progress checks for running processes.
|
|
1287
|
+
*
|
|
1288
|
+
* Progressive checks (15m/30m/60m) are useful for long-running jobs, but we also want
|
|
1289
|
+
* short-cadence checks to:
|
|
1290
|
+
* - populate `lastProgress` quickly (for UX and tests)
|
|
1291
|
+
* - detect stalled processes when `verificationIntervalMs` is intentionally small
|
|
1292
|
+
*/
|
|
1293
|
+
setupFrequentProgressMonitoring(processId) {
|
|
1294
|
+
const registration = this.processes.get(processId);
|
|
1295
|
+
if (!registration) {
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
if (this.progressMonitoringTimers.has(processId)) {
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
const intervalMs = Math.max(10, this.config.verificationIntervalMs);
|
|
1302
|
+
let isTickRunning = false;
|
|
1303
|
+
const timer = setInterval(async () => {
|
|
1304
|
+
if (isTickRunning) return;
|
|
1305
|
+
isTickRunning = true;
|
|
1306
|
+
try {
|
|
1307
|
+
const current = this.processes.get(processId);
|
|
1308
|
+
if (!current || current.status !== "running") {
|
|
1309
|
+
clearInterval(timer);
|
|
1310
|
+
this.progressMonitoringTimers.delete(processId);
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
const hasProgress = await this.checkProcessProgress(processId);
|
|
1314
|
+
if (!hasProgress) {
|
|
1315
|
+
current.consecutiveFrequentProgressFailures++;
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
if (current.consecutiveFrequentProgressFailures > 0) {
|
|
1319
|
+
current.consecutiveFrequentProgressFailures = 0;
|
|
1320
|
+
}
|
|
1321
|
+
} finally {
|
|
1322
|
+
isTickRunning = false;
|
|
1323
|
+
}
|
|
1324
|
+
}, intervalMs);
|
|
1325
|
+
timer.unref();
|
|
1326
|
+
this.progressMonitoringTimers.set(processId, timer);
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Terminate all running processes
|
|
1330
|
+
*/
|
|
1331
|
+
async terminateAll(reason = "shutdown") {
|
|
1332
|
+
const runningProcesses = Array.from(this.processes.entries()).filter(
|
|
1333
|
+
([, registration]) => registration.status === "running" || registration.status === "terminating"
|
|
1334
|
+
);
|
|
1335
|
+
if (runningProcesses.length === 0) {
|
|
1336
|
+
if (this.config.verbose) {
|
|
1337
|
+
console.error(chalk.green(`No running processes to terminate (${reason})`));
|
|
1338
|
+
}
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
console.error(chalk.yellow(`Terminating all processes (${reason})...`));
|
|
1342
|
+
const terminationPromises = [];
|
|
1343
|
+
for (const [processId] of runningProcesses) {
|
|
1344
|
+
terminationPromises.push(this.terminateProcess(processId, reason));
|
|
1345
|
+
}
|
|
1346
|
+
await Promise.allSettled(terminationPromises);
|
|
1347
|
+
const failedCount = runningProcesses.filter(
|
|
1348
|
+
([, registration]) => registration.terminationFailed === true
|
|
1349
|
+
).length;
|
|
1350
|
+
if (failedCount > 0) {
|
|
1351
|
+
emitWarning(
|
|
1352
|
+
"PR-001",
|
|
1353
|
+
{
|
|
1354
|
+
subsystem: "orchestrator",
|
|
1355
|
+
executionPhase: "cleanup"
|
|
1356
|
+
},
|
|
1357
|
+
{
|
|
1358
|
+
severity: "ERROR" /* ERROR */,
|
|
1359
|
+
metadata: {
|
|
1360
|
+
operation: "terminateAll",
|
|
1361
|
+
reason: `Process termination completed with ${failedCount} failure(s) during cleanup`,
|
|
1362
|
+
terminationReason: reason,
|
|
1363
|
+
failedCount
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
);
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
console.error(chalk.green(`All processes terminated (${reason})`));
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Get process registration by ID
|
|
1373
|
+
*/
|
|
1374
|
+
getProcess(processId) {
|
|
1375
|
+
return this.processes.get(processId);
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Get all processes (optionally filtered by status)
|
|
1379
|
+
*/
|
|
1380
|
+
getProcesses(status) {
|
|
1381
|
+
const allProcesses = Array.from(this.processes.values());
|
|
1382
|
+
return status ? allProcesses.filter((p) => p.status === status) : allProcesses;
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Get running processes
|
|
1386
|
+
*/
|
|
1387
|
+
getRunningProcesses() {
|
|
1388
|
+
return this.getProcesses("running");
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Get process statistics
|
|
1392
|
+
*/
|
|
1393
|
+
getStats() {
|
|
1394
|
+
return {
|
|
1395
|
+
...this.stats,
|
|
1396
|
+
running: this.getProcesses("running").length,
|
|
1397
|
+
history: this.processes.size
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Update internal statistics
|
|
1402
|
+
*/
|
|
1403
|
+
updateStats() {
|
|
1404
|
+
this.stats.total = this.processes.size + this.evictedTerminalRegistrations;
|
|
1405
|
+
this.stats.running = this.getProcesses("running").length;
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Build a structured shutdown intent without exiting the process.
|
|
1409
|
+
* CLI code is responsible for turning this intent into runtime exit behavior.
|
|
1410
|
+
*/
|
|
1411
|
+
async createShutdownIntent(reason, code) {
|
|
1412
|
+
if (!this.isShuttingDown) {
|
|
1413
|
+
this.isShuttingDown = true;
|
|
1414
|
+
await this.runShutdownCleanup(reason);
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
code,
|
|
1418
|
+
reason,
|
|
1419
|
+
cleanupDone: true
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
async handleSignal(signal) {
|
|
1423
|
+
const code = signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 1;
|
|
1424
|
+
return this.createShutdownIntent(`signal_${signal}`, code);
|
|
1425
|
+
}
|
|
1426
|
+
async handleUncaughtException(error) {
|
|
1427
|
+
console.error(chalk.red(`
|
|
1428
|
+
Uncaught exception: ${error.message}`));
|
|
1429
|
+
return this.createShutdownIntent("uncaught_exception", 1);
|
|
1430
|
+
}
|
|
1431
|
+
async handleUnhandledRejection(reason, promise) {
|
|
1432
|
+
console.error(chalk.red(`
|
|
1433
|
+
Unhandled rejection at: ${promise}`));
|
|
1434
|
+
console.error(chalk.red(` Reason: ${reason}`));
|
|
1435
|
+
return this.createShutdownIntent("unhandled_rejection", 1);
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Create verification function for local processes
|
|
1439
|
+
*/
|
|
1440
|
+
static createLocalVerification(requirements) {
|
|
1441
|
+
return async () => {
|
|
1442
|
+
try {
|
|
1443
|
+
if (requirements.expectedFiles) {
|
|
1444
|
+
for (const file of requirements.expectedFiles) {
|
|
1445
|
+
try {
|
|
1446
|
+
await fs.access(file);
|
|
1447
|
+
} catch (_error) {
|
|
1448
|
+
return false;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
if (requirements.expectedArtifacts) {
|
|
1453
|
+
for (const artifact of requirements.expectedArtifacts) {
|
|
1454
|
+
try {
|
|
1455
|
+
await fs.access(artifact);
|
|
1456
|
+
} catch (_error) {
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
if (requirements.customVerification) {
|
|
1462
|
+
return await requirements.customVerification();
|
|
1463
|
+
}
|
|
1464
|
+
return true;
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
console.log(chalk.red(`Local verification _error: ${error}`));
|
|
1467
|
+
return false;
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Check for orphaned processes and clean them up
|
|
1473
|
+
*/
|
|
1474
|
+
async cleanupOrphanedProcesses() {
|
|
1475
|
+
const runningProcesses = this.getRunningProcesses();
|
|
1476
|
+
const now = Date.now();
|
|
1477
|
+
for (const process2 of runningProcesses) {
|
|
1478
|
+
const elapsed = now - process2.createdAt.getTime();
|
|
1479
|
+
if (!process2.timeoutEnabled) {
|
|
1480
|
+
if (elapsed > this.OLD_PROCESS_TIMEOUT_MS) {
|
|
1481
|
+
emitWarning(
|
|
1482
|
+
"PR-001",
|
|
1483
|
+
{
|
|
1484
|
+
taskId: process2.processId,
|
|
1485
|
+
subsystem: "orchestrator",
|
|
1486
|
+
executionPhase: "cleanup"
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
severity: "WARN" /* WARN */,
|
|
1490
|
+
metadata: {
|
|
1491
|
+
operation: "cleanupOrphanedProcesses",
|
|
1492
|
+
reason: "stale_timeout_disabled",
|
|
1493
|
+
elapsedMs: elapsed
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
);
|
|
1497
|
+
await this.terminateProcess(process2.processId, "stale_timeout_disabled");
|
|
1498
|
+
}
|
|
1499
|
+
continue;
|
|
1500
|
+
}
|
|
1501
|
+
if (elapsed > process2.timeoutMs * 2) {
|
|
1502
|
+
console.log(
|
|
1503
|
+
chalk.yellow(
|
|
1504
|
+
`[orphan] Found orphaned process: ${process2.processId} (running for ${elapsed / 1e3}s)`
|
|
1505
|
+
)
|
|
1506
|
+
);
|
|
1507
|
+
await this.terminateProcess(process2.processId, "orphaned");
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Generate process lifecycle report
|
|
1513
|
+
*/
|
|
1514
|
+
generateReport() {
|
|
1515
|
+
const lines = [];
|
|
1516
|
+
const stats = this.getStats();
|
|
1517
|
+
lines.push(chalk.bold("Process Lifecycle Manager Report"));
|
|
1518
|
+
lines.push("");
|
|
1519
|
+
lines.push(chalk.cyan("Statistics:"));
|
|
1520
|
+
lines.push(` Total processes: ${stats.total}`);
|
|
1521
|
+
lines.push(` Running: ${chalk.green(stats.running)}`);
|
|
1522
|
+
lines.push(` Completed: ${chalk.green(stats.completed)}`);
|
|
1523
|
+
lines.push(` Failed: ${chalk.red(stats.failed)}`);
|
|
1524
|
+
lines.push(` Terminated: ${chalk.yellow(stats.terminated)}`);
|
|
1525
|
+
lines.push(` Timeout: ${chalk.yellow(stats.timeout)}`);
|
|
1526
|
+
lines.push("");
|
|
1527
|
+
if (stats.running > 0) {
|
|
1528
|
+
lines.push(chalk.cyan("Running Processes:"));
|
|
1529
|
+
this.getRunningProcesses().forEach((process2) => {
|
|
1530
|
+
const elapsed = (Date.now() - process2.createdAt.getTime()) / 1e3;
|
|
1531
|
+
lines.push(` ${process2.processId} (${process2.processType}) - ${elapsed.toFixed(1)}s`);
|
|
1532
|
+
});
|
|
1533
|
+
lines.push("");
|
|
1534
|
+
}
|
|
1535
|
+
return lines.join("\n");
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Start automatic cleanup timer for processes older than 2 hours
|
|
1539
|
+
* This prevents accumulation of old processes that might otherwise go unnoticed
|
|
1540
|
+
*/
|
|
1541
|
+
startOldProcessCleanup() {
|
|
1542
|
+
if (this.oldProcessCleanupTimer) {
|
|
1543
|
+
clearInterval(this.oldProcessCleanupTimer);
|
|
1544
|
+
}
|
|
1545
|
+
this.oldProcessCleanupTimer = setInterval(() => {
|
|
1546
|
+
void this.cleanupOldProcesses();
|
|
1547
|
+
}, this.CLEANUP_CHECK_INTERVAL_MS);
|
|
1548
|
+
this.oldProcessCleanupTimer.unref?.();
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Clean up processes that have been running for longer than the timeout threshold
|
|
1552
|
+
* Prevents accumulation of old processes (e.g., from 2+ hours ago)
|
|
1553
|
+
*/
|
|
1554
|
+
async cleanupOldProcesses() {
|
|
1555
|
+
const now = Date.now();
|
|
1556
|
+
const oldProcessesToClean = [];
|
|
1557
|
+
const terminalProcessesToEvict = [];
|
|
1558
|
+
for (const [processId, registration] of this.processes) {
|
|
1559
|
+
const processAge = now - registration.createdAt.getTime();
|
|
1560
|
+
if (processAge > this.OLD_PROCESS_TIMEOUT_MS && registration.timeoutEnabled) {
|
|
1561
|
+
if (registration.status === "running") {
|
|
1562
|
+
oldProcessesToClean.push(processId);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
if (registration.status !== "running" && registration.status !== "terminating" && now - (registration.terminalAt ?? registration.createdAt.getTime()) > this.TERMINAL_REGISTRATION_RETENTION_MS) {
|
|
1566
|
+
terminalProcessesToEvict.push(processId);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
for (const processId of oldProcessesToClean) {
|
|
1570
|
+
const registration = this.processes.get(processId);
|
|
1571
|
+
if (registration) {
|
|
1572
|
+
const ageHours = (now - registration.createdAt.getTime()) / 1e3 / 60 / 60;
|
|
1573
|
+
console.log(
|
|
1574
|
+
chalk.yellow(
|
|
1575
|
+
`Cleaning up old process: ${processId} (running for ${ageHours.toFixed(1)} hours)`
|
|
1576
|
+
)
|
|
1577
|
+
);
|
|
1578
|
+
await this.terminateProcess(processId, "old_process_timeout");
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
for (const processId of terminalProcessesToEvict) {
|
|
1582
|
+
if (this.processes.has(processId)) {
|
|
1583
|
+
this.evictedTerminalRegistrations++;
|
|
1584
|
+
this.unregisterProcess(processId);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Clean up all tracked child processes by PID
|
|
1590
|
+
* This method forcefully kills all tracked child processes
|
|
1591
|
+
*/
|
|
1592
|
+
async cleanupAllProcesses() {
|
|
1593
|
+
if (this.trackedChildProcesses.size === 0) {
|
|
1594
|
+
if (this.config.verbose) {
|
|
1595
|
+
console.log(chalk.gray("No child processes to cleanup"));
|
|
1596
|
+
}
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
console.log(
|
|
1600
|
+
chalk.yellow(`Cleaning up ${this.trackedChildProcesses.size} tracked child processes...`)
|
|
1601
|
+
);
|
|
1602
|
+
const trackedChildren = Array.from(this.trackedChildProcesses.values());
|
|
1603
|
+
const outcomes = await Promise.all(
|
|
1604
|
+
trackedChildren.map((tracked) => {
|
|
1605
|
+
if (tracked.forceKillPromise) {
|
|
1606
|
+
return this.beginTrackedChildTermination(tracked, this.processes.get(tracked.processId));
|
|
1607
|
+
}
|
|
1608
|
+
if (tracked.childExited || this.childHasExited(tracked.childProcess)) {
|
|
1609
|
+
const outcome = {
|
|
1610
|
+
confirmed: true,
|
|
1611
|
+
reason: "child-exited"
|
|
1612
|
+
};
|
|
1613
|
+
this.untrackChildProcess(tracked, outcome);
|
|
1614
|
+
return Promise.resolve(outcome);
|
|
1615
|
+
}
|
|
1616
|
+
const registration = this.processes.get(tracked.processId);
|
|
1617
|
+
return this.beginTrackedChildTermination(tracked, registration);
|
|
1618
|
+
})
|
|
1619
|
+
);
|
|
1620
|
+
const unconfirmed = outcomes.map((outcome, index) => ({ outcome, tracked: trackedChildren[index] })).filter(({ outcome }) => !outcome.confirmed);
|
|
1621
|
+
if (unconfirmed.length > 0) {
|
|
1622
|
+
for (const { outcome, tracked } of unconfirmed) {
|
|
1623
|
+
const registration = tracked ? this.processes.get(tracked.processId) : void 0;
|
|
1624
|
+
if (registration) {
|
|
1625
|
+
registration.terminationFailed = true;
|
|
1626
|
+
registration.error = new Error(`Child process cleanup unconfirmed: ${outcome.reason}`);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
throw new Error(
|
|
1630
|
+
`Child process cleanup unconfirmed for ${unconfirmed.length} process(es): ${unconfirmed.map(({ outcome, tracked }) => `${tracked?.processId ?? "unknown"}=${outcome.reason}`).join(", ")}`
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
console.log(
|
|
1634
|
+
chalk.green(`Child process cleanup completed for ${trackedChildren.length} processes`)
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Start resource monitoring (CPU and memory) for child processes
|
|
1639
|
+
*
|
|
1640
|
+
* Monitors resource usage every 60 seconds and:
|
|
1641
|
+
* - Tracks CPU and memory for each process
|
|
1642
|
+
* - Warns when approaching limits
|
|
1643
|
+
* - Kills oldest processes when limits exceeded
|
|
1644
|
+
*/
|
|
1645
|
+
startResourceMonitoring() {
|
|
1646
|
+
this.resourceMonitoringTimer = setInterval(() => {
|
|
1647
|
+
this.monitorResourceUsage();
|
|
1648
|
+
}, this.config.resourceMonitoringIntervalMs);
|
|
1649
|
+
this.resourceMonitoringTimer.unref();
|
|
1650
|
+
if (this.config.verbose) {
|
|
1651
|
+
console.log(
|
|
1652
|
+
chalk.gray(
|
|
1653
|
+
`Resource monitoring started (interval: ${this.config.resourceMonitoringIntervalMs}ms)`
|
|
1654
|
+
)
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Monitor resource usage and enforce limits
|
|
1660
|
+
*
|
|
1661
|
+
* Thresholds:
|
|
1662
|
+
* - Max 50 processes
|
|
1663
|
+
* - Max 1000% total CPU usage
|
|
1664
|
+
* - Kills oldest processes when limits exceeded
|
|
1665
|
+
*/
|
|
1666
|
+
async monitorResourceUsage() {
|
|
1667
|
+
const runningProcesses = this.getRunningProcesses();
|
|
1668
|
+
if (runningProcesses.length === 0) {
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
try {
|
|
1672
|
+
const RESOURCE_MEASURE_CONCURRENCY = 4;
|
|
1673
|
+
for (let i = 0; i < runningProcesses.length; i += RESOURCE_MEASURE_CONCURRENCY) {
|
|
1674
|
+
const batch = runningProcesses.slice(i, i + RESOURCE_MEASURE_CONCURRENCY);
|
|
1675
|
+
await Promise.all(batch.map((process2) => this.measureProcessResources(process2)));
|
|
1676
|
+
}
|
|
1677
|
+
this.totalCpuUsage = runningProcesses.reduce(
|
|
1678
|
+
(sum, p) => sum + (p.lastResourceUsage?.cpuPercentage ?? 0),
|
|
1679
|
+
0
|
|
1680
|
+
);
|
|
1681
|
+
this.checkResourceThresholds(runningProcesses);
|
|
1682
|
+
} catch (error) {
|
|
1683
|
+
if (this.config.verbose) {
|
|
1684
|
+
console.log(
|
|
1685
|
+
chalk.yellow(
|
|
1686
|
+
`Resource monitoring error: ${error instanceof Error ? error.message : String(error)}`
|
|
1687
|
+
)
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
* Measure CPU and memory usage for a process
|
|
1694
|
+
*/
|
|
1695
|
+
async measureProcessResources(process2) {
|
|
1696
|
+
try {
|
|
1697
|
+
if (!process2.childProcess?.pid) {
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
const pid = process2.childProcess.pid;
|
|
1701
|
+
const now = Date.now();
|
|
1702
|
+
let cpuPercentage = 0;
|
|
1703
|
+
let memoryBytes = 0;
|
|
1704
|
+
try {
|
|
1705
|
+
const { isValidPid } = await import("./security-JI22TBEB.js");
|
|
1706
|
+
if (!isValidPid(pid)) {
|
|
1707
|
+
emitWarning(
|
|
1708
|
+
"CFG-002",
|
|
1709
|
+
{
|
|
1710
|
+
taskId: process2.processId,
|
|
1711
|
+
subsystem: "orchestrator",
|
|
1712
|
+
executionPhase: "execution"
|
|
1713
|
+
},
|
|
1714
|
+
{
|
|
1715
|
+
severity: "ERROR" /* ERROR */,
|
|
1716
|
+
metadata: {
|
|
1717
|
+
operation: "measureProcessResources",
|
|
1718
|
+
pid,
|
|
1719
|
+
reason: "Invalid PID detected (security validation failed)"
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
);
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
const { execFile } = await import("child_process");
|
|
1726
|
+
const { promisify } = await import("util");
|
|
1727
|
+
const execFileAsync = promisify(execFile);
|
|
1728
|
+
const { stdout } = await execFileAsync("ps", ["-o", "%cpu=,rss=", "-p", String(pid)], {
|
|
1729
|
+
encoding: "utf-8",
|
|
1730
|
+
timeout: 5e3
|
|
1731
|
+
});
|
|
1732
|
+
const result = stdout.trim();
|
|
1733
|
+
if (result) {
|
|
1734
|
+
const parts = result.split(/\s+/);
|
|
1735
|
+
if (parts.length >= 2 && parts[0] && parts[1]) {
|
|
1736
|
+
cpuPercentage = parseFloat(parts[0]) || 0;
|
|
1737
|
+
memoryBytes = (parseInt(parts[1], 10) || 0) * 1024;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
} catch (_error) {
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
const usage = {
|
|
1744
|
+
cpuPercentage,
|
|
1745
|
+
memoryBytes,
|
|
1746
|
+
timestamp: now
|
|
1747
|
+
};
|
|
1748
|
+
process2.lastResourceUsage = usage;
|
|
1749
|
+
if (!process2.resourceHistory) {
|
|
1750
|
+
process2.resourceHistory = [];
|
|
1751
|
+
}
|
|
1752
|
+
process2.resourceHistory.push(usage);
|
|
1753
|
+
if (process2.resourceHistory.length > 60) {
|
|
1754
|
+
process2.resourceHistory.shift();
|
|
1755
|
+
}
|
|
1756
|
+
if (this.config.verbose && (cpuPercentage > 10 || memoryBytes > 100 * 1024 * 1024)) {
|
|
1757
|
+
console.log(
|
|
1758
|
+
chalk.gray(
|
|
1759
|
+
`${process2.processId}: CPU ${cpuPercentage.toFixed(1)}% | Memory ${(memoryBytes / 1024 / 1024).toFixed(1)}MB`
|
|
1760
|
+
)
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
} catch (_error) {
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1767
|
+
* Check resource thresholds and take action if exceeded
|
|
1768
|
+
*/
|
|
1769
|
+
async checkResourceThresholds(runningProcesses) {
|
|
1770
|
+
if (runningProcesses.length >= this.config.maxProcessesWarning * 0.8) {
|
|
1771
|
+
console.log(
|
|
1772
|
+
chalk.yellow(
|
|
1773
|
+
`Process count approaching limit: ${runningProcesses.length}/${this.config.maxProcessesWarning}`
|
|
1774
|
+
)
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
if (this.totalCpuUsage > this.config.maxCpuPercentage) {
|
|
1778
|
+
console.log(
|
|
1779
|
+
chalk.red(
|
|
1780
|
+
`Total CPU usage exceeded (${this.totalCpuUsage.toFixed(1)}% > ${this.config.maxCpuPercentage}%)`
|
|
1781
|
+
)
|
|
1782
|
+
);
|
|
1783
|
+
const oldest = this.getOldestRunningProcess();
|
|
1784
|
+
if (oldest) {
|
|
1785
|
+
console.log(
|
|
1786
|
+
chalk.yellow(`Killing oldest process to reduce resource usage: ${oldest.processId}`)
|
|
1787
|
+
);
|
|
1788
|
+
await this.terminateProcess(oldest.processId, "resource_limit_exceeded");
|
|
1789
|
+
this.resourcesKilledByMonitor++;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
for (const process2 of runningProcesses) {
|
|
1793
|
+
const memory = process2.lastResourceUsage?.memoryBytes ?? 0;
|
|
1794
|
+
const memoryMB = memory / 1024 / 1024;
|
|
1795
|
+
const cpuPct = process2.lastResourceUsage?.cpuPercentage ?? 0;
|
|
1796
|
+
const sample = {
|
|
1797
|
+
processId: process2.processId,
|
|
1798
|
+
pid: process2.childProcess?.pid ?? 0,
|
|
1799
|
+
timestampMs: Date.now(),
|
|
1800
|
+
rssMB: memoryMB,
|
|
1801
|
+
cpuPct,
|
|
1802
|
+
command: process2.metadata?.command,
|
|
1803
|
+
provider: process2.metadata?.provider,
|
|
1804
|
+
workloadClass: this.inferWorkloadClass(process2)
|
|
1805
|
+
};
|
|
1806
|
+
const decision = MemoryGuardian.getInstance().evaluateProcessSample(sample);
|
|
1807
|
+
if (memoryMB > 1e3 && decision.action !== "kill") {
|
|
1808
|
+
console.log(
|
|
1809
|
+
chalk.gray(
|
|
1810
|
+
`Legacy 1000MB kill suppressed for ${process2.processId} (${memoryMB.toFixed(1)}MB) - Guardian decision: ${decision.action}`
|
|
1811
|
+
)
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
if (decision.action === "warn") {
|
|
1815
|
+
console.log(
|
|
1816
|
+
chalk.yellow(
|
|
1817
|
+
`Memory warning for ${process2.processId}: ${decision.reason} (${memoryMB.toFixed(1)}MB)`
|
|
1818
|
+
)
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
if (decision.action === "kill") {
|
|
1822
|
+
console.log(
|
|
1823
|
+
chalk.red(
|
|
1824
|
+
`Memory kill for ${process2.processId}: ${decision.reason} (${memoryMB.toFixed(1)}MB, grace=${decision.graceMs}ms)`
|
|
1825
|
+
)
|
|
1826
|
+
);
|
|
1827
|
+
if (decision.graceMs === 0) {
|
|
1828
|
+
await this.terminateProcess(process2.processId, "memory_limit_exceeded");
|
|
1829
|
+
} else {
|
|
1830
|
+
await this.terminateProcessWithGrace(
|
|
1831
|
+
process2.processId,
|
|
1832
|
+
"memory_limit_exceeded",
|
|
1833
|
+
decision.graceMs
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
this.resourcesKilledByMonitor++;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
if (runningProcesses.length >= this.config.maxProcessesWarning) {
|
|
1840
|
+
console.log(
|
|
1841
|
+
chalk.red(
|
|
1842
|
+
`Process limit reached (${runningProcesses.length}/${this.config.maxProcessesWarning})`
|
|
1843
|
+
)
|
|
1844
|
+
);
|
|
1845
|
+
const oldest = this.getOldestRunningProcess();
|
|
1846
|
+
if (oldest) {
|
|
1847
|
+
console.log(chalk.yellow(`Killing oldest process: ${oldest.processId}`));
|
|
1848
|
+
await this.terminateProcess(oldest.processId, "process_limit_exceeded");
|
|
1849
|
+
this.resourcesKilledByMonitor++;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
/**
|
|
1854
|
+
* Get the oldest running process by creation timestamp
|
|
1855
|
+
*/
|
|
1856
|
+
getOldestRunningProcess() {
|
|
1857
|
+
const runningProcesses = this.getRunningProcesses();
|
|
1858
|
+
if (runningProcesses.length === 0) {
|
|
1859
|
+
return void 0;
|
|
1860
|
+
}
|
|
1861
|
+
return runningProcesses.reduce((oldest, current) => {
|
|
1862
|
+
return current.createdAt < oldest.createdAt ? current : oldest;
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Infer workload class from process metadata for memory policy decisions.
|
|
1867
|
+
*/
|
|
1868
|
+
inferWorkloadClass(process2) {
|
|
1869
|
+
return inferMemoryWorkloadClassFromMetadata(process2.metadata);
|
|
1870
|
+
}
|
|
1871
|
+
/**
|
|
1872
|
+
* Get resource usage statistics
|
|
1873
|
+
*/
|
|
1874
|
+
getResourceStats() {
|
|
1875
|
+
return {
|
|
1876
|
+
totalCpuUsage: this.totalCpuUsage,
|
|
1877
|
+
resourcesKilledByMonitor: this.resourcesKilledByMonitor,
|
|
1878
|
+
monitoringInterval: this.config.resourceMonitoringIntervalMs,
|
|
1879
|
+
maxCpuPercentage: this.config.maxCpuPercentage,
|
|
1880
|
+
maxProcessesWarning: this.config.maxProcessesWarning
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* Close the process lifecycle manager
|
|
1885
|
+
*/
|
|
1886
|
+
async close() {
|
|
1887
|
+
if (this.isShuttingDown) {
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
this.isShuttingDown = true;
|
|
1891
|
+
if (this.config.verbose) {
|
|
1892
|
+
console.log(chalk.gray("Closing ProcessLifecycleManager..."));
|
|
1893
|
+
}
|
|
1894
|
+
await this.runShutdownCleanup("manager_shutdown");
|
|
1895
|
+
this.processes.clear();
|
|
1896
|
+
this.trackedChildProcesses.clear();
|
|
1897
|
+
if (this.config.verbose) {
|
|
1898
|
+
console.log(chalk.green("ProcessLifecycleManager closed"));
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
stopBackgroundTimers() {
|
|
1902
|
+
if (this.resourceMonitoringTimer) {
|
|
1903
|
+
clearInterval(this.resourceMonitoringTimer);
|
|
1904
|
+
this.resourceMonitoringTimer = void 0;
|
|
1905
|
+
}
|
|
1906
|
+
if (this.oldProcessCleanupTimer) {
|
|
1907
|
+
clearInterval(this.oldProcessCleanupTimer);
|
|
1908
|
+
this.oldProcessCleanupTimer = void 0;
|
|
1909
|
+
}
|
|
1910
|
+
for (const [_processId, timer] of this.progressMonitoringTimers) {
|
|
1911
|
+
clearInterval(timer);
|
|
1912
|
+
}
|
|
1913
|
+
this.progressMonitoringTimers.clear();
|
|
1914
|
+
for (const registration of this.processes.values()) {
|
|
1915
|
+
this.clearHardTimeout(registration);
|
|
1916
|
+
if (registration.progressCheckTimers) {
|
|
1917
|
+
for (const timer of registration.progressCheckTimers) {
|
|
1918
|
+
clearTimeout(timer);
|
|
1919
|
+
}
|
|
1920
|
+
registration.progressCheckTimers = void 0;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
async runShutdownCleanup(reason) {
|
|
1925
|
+
this.stopBackgroundTimers();
|
|
1926
|
+
await this.terminateAll(reason);
|
|
1927
|
+
let cleanupError;
|
|
1928
|
+
try {
|
|
1929
|
+
await this.cleanupAllProcesses();
|
|
1930
|
+
} catch (error) {
|
|
1931
|
+
cleanupError = error;
|
|
1932
|
+
} finally {
|
|
1933
|
+
killAllFallbackTrackedSubprocesses("SIGTERM");
|
|
1934
|
+
killAllFallbackTrackedSubprocesses("SIGKILL");
|
|
1935
|
+
await reapOrphanedTmuxSessions({ includeOwnPid: true });
|
|
1936
|
+
}
|
|
1937
|
+
if (cleanupError) throw cleanupError;
|
|
1938
|
+
}
|
|
1939
|
+
/**
|
|
1940
|
+
* Retained for backward compatibility.
|
|
1941
|
+
* Signal handlers now live in the CLI layer (`exit-handler`).
|
|
1942
|
+
*/
|
|
1943
|
+
static cleanupGlobalHandlers() {
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1946
|
+
|
|
1947
|
+
// src/utils/cli-breadcrumbs.ts
|
|
1948
|
+
import chalk2 from "chalk";
|
|
1949
|
+
var DEFAULT_IDLE_AFTER_MS = 3e4;
|
|
1950
|
+
function formatBreadcrumbLine(prefix, label, detail) {
|
|
1951
|
+
const message = detail && detail.trim().length > 0 ? `${prefix}: ${label}: ${detail}` : `${prefix}: ${label}`;
|
|
1952
|
+
return `${chalk2.gray(message)}
|
|
1953
|
+
`;
|
|
1954
|
+
}
|
|
1955
|
+
function createCliBreadcrumb(options) {
|
|
1956
|
+
if (isQuietOutputRequested()) {
|
|
1957
|
+
const handle2 = {
|
|
1958
|
+
markActivity() {
|
|
1959
|
+
},
|
|
1960
|
+
complete() {
|
|
1961
|
+
},
|
|
1962
|
+
fail() {
|
|
1963
|
+
},
|
|
1964
|
+
dispose() {
|
|
1965
|
+
},
|
|
1966
|
+
[Symbol.dispose]() {
|
|
1967
|
+
handle2.dispose();
|
|
1968
|
+
}
|
|
1969
|
+
};
|
|
1970
|
+
return handle2;
|
|
1971
|
+
}
|
|
1972
|
+
const label = options.label.trim();
|
|
1973
|
+
const stream = options.stream ?? process.stderr;
|
|
1974
|
+
const idleAfterMs = typeof options.idleAfterMs === "number" && Number.isFinite(options.idleAfterMs) ? Math.max(0, Math.floor(options.idleAfterMs)) : DEFAULT_IDLE_AFTER_MS;
|
|
1975
|
+
let idleTimer;
|
|
1976
|
+
let disposed = false;
|
|
1977
|
+
const clearIdleTimer = () => {
|
|
1978
|
+
if (idleTimer) {
|
|
1979
|
+
clearTimeout(idleTimer);
|
|
1980
|
+
idleTimer = void 0;
|
|
1981
|
+
}
|
|
1982
|
+
};
|
|
1983
|
+
const emit = (symbol, detail) => {
|
|
1984
|
+
if (disposed) return;
|
|
1985
|
+
stream.write(formatBreadcrumbLine(symbol, label, detail));
|
|
1986
|
+
};
|
|
1987
|
+
const armIdleHeartbeat = () => {
|
|
1988
|
+
clearIdleTimer();
|
|
1989
|
+
if (disposed || idleAfterMs <= 0) return;
|
|
1990
|
+
idleTimer = setTimeout(() => {
|
|
1991
|
+
idleTimer = void 0;
|
|
1992
|
+
emit("Info", "still working...");
|
|
1993
|
+
armIdleHeartbeat();
|
|
1994
|
+
}, idleAfterMs);
|
|
1995
|
+
};
|
|
1996
|
+
emit("Info", options.detail);
|
|
1997
|
+
armIdleHeartbeat();
|
|
1998
|
+
const handle = {
|
|
1999
|
+
markActivity(detail) {
|
|
2000
|
+
if (disposed) return;
|
|
2001
|
+
if (detail) emit("Info", detail);
|
|
2002
|
+
armIdleHeartbeat();
|
|
2003
|
+
},
|
|
2004
|
+
complete(detail) {
|
|
2005
|
+
if (disposed) return;
|
|
2006
|
+
clearIdleTimer();
|
|
2007
|
+
if (detail) emit("OK", detail);
|
|
2008
|
+
disposed = true;
|
|
2009
|
+
},
|
|
2010
|
+
fail(detail) {
|
|
2011
|
+
if (disposed) return;
|
|
2012
|
+
clearIdleTimer();
|
|
2013
|
+
if (detail) emit("Error", detail);
|
|
2014
|
+
disposed = true;
|
|
2015
|
+
},
|
|
2016
|
+
dispose() {
|
|
2017
|
+
if (disposed) return;
|
|
2018
|
+
clearIdleTimer();
|
|
2019
|
+
disposed = true;
|
|
2020
|
+
},
|
|
2021
|
+
[Symbol.dispose]() {
|
|
2022
|
+
handle.dispose();
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
return handle;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
// src/planning/execution-spec-signals.ts
|
|
2029
|
+
function evaluateExecutionSpec(taskDescription) {
|
|
2030
|
+
const hasFillPlaceholders = taskDescription.length > 0 && hasUnfilledMarkers(taskDescription);
|
|
2031
|
+
if (!hasFillPlaceholders) {
|
|
2032
|
+
return null;
|
|
2033
|
+
}
|
|
2034
|
+
const hasAdr = /^##\s*Context/im.test(taskDescription) && /^##\s*Decision/im.test(taskDescription);
|
|
2035
|
+
const hasResearch = /^##\s*Research\s+Questions?/im.test(taskDescription);
|
|
2036
|
+
const hasBug = /^##\s*Reproduction\s+Steps/im.test(taskDescription);
|
|
2037
|
+
const mockBoosts = hasAdr ? [{ kind: "adr" }] : hasResearch ? [{ kind: "research" }] : hasBug ? [{ kind: "reproducibility" }] : [{ kind: "scope" }, { kind: "acceptance" }, { kind: "testing" }];
|
|
2038
|
+
const completeness = validateTaskSpec(taskDescription, mockBoosts);
|
|
2039
|
+
const guidanceTier = calculateGuidanceTier(completeness);
|
|
2040
|
+
return {
|
|
2041
|
+
completenessScore: completeness.score,
|
|
2042
|
+
guidanceTier,
|
|
2043
|
+
missingFields: completeness.missing,
|
|
2044
|
+
hasFillPlaceholders,
|
|
2045
|
+
promptInjection: generatePromptInjection(completeness, guidanceTier)
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// src/core/verification/off-scope-config-edits.ts
|
|
2050
|
+
import path from "path";
|
|
2051
|
+
var TEST_CONFIG_PATTERNS = [
|
|
2052
|
+
/^phpunit(?:\.dist)?\.xml(?:\.dist)?$/i,
|
|
2053
|
+
/^pytest\.ini$/i,
|
|
2054
|
+
/^tox\.ini$/i,
|
|
2055
|
+
/^\.coveragerc$/i,
|
|
2056
|
+
/^\.rspec$/i,
|
|
2057
|
+
/^codecov\.ya?ml$/i,
|
|
2058
|
+
/^(?:jest|vitest|karma|playwright|cypress)\.config\.[cm]?[jt]s$/i,
|
|
2059
|
+
/^\.mocharc\.(?:json|ya?ml|[cm]?js)$/i
|
|
2060
|
+
];
|
|
2061
|
+
var AGENT_INSTRUCTION_BASENAMES = /* @__PURE__ */ new Set([
|
|
2062
|
+
"agents.md",
|
|
2063
|
+
"claude.md",
|
|
2064
|
+
"gemini.md",
|
|
2065
|
+
".mcp.json",
|
|
2066
|
+
"opencode.json",
|
|
2067
|
+
"opencode.jsonc",
|
|
2068
|
+
".cursorrules",
|
|
2069
|
+
".windsurfrules"
|
|
2070
|
+
]);
|
|
2071
|
+
var AGENT_INSTRUCTION_DIRS = [
|
|
2072
|
+
".claude/",
|
|
2073
|
+
".cursor/",
|
|
2074
|
+
".grok/",
|
|
2075
|
+
".codex/",
|
|
2076
|
+
".gemini/",
|
|
2077
|
+
".opencode/",
|
|
2078
|
+
".kiro/",
|
|
2079
|
+
".github/instructions/"
|
|
2080
|
+
];
|
|
2081
|
+
function normalize(filePath) {
|
|
2082
|
+
return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
2083
|
+
}
|
|
2084
|
+
function categoryOf(normalizedPath) {
|
|
2085
|
+
const base = path.posix.basename(normalizedPath);
|
|
2086
|
+
const lowerPath = normalizedPath.toLowerCase();
|
|
2087
|
+
if (AGENT_INSTRUCTION_BASENAMES.has(base.toLowerCase()) || lowerPath === ".github/copilot-instructions.md" || AGENT_INSTRUCTION_DIRS.some((dir) => lowerPath.startsWith(dir) || lowerPath.includes(`/${dir}`))) {
|
|
2088
|
+
return "agent-instruction";
|
|
2089
|
+
}
|
|
2090
|
+
if (TEST_CONFIG_PATTERNS.some((pattern) => pattern.test(base))) return "test-config";
|
|
2091
|
+
if (isDependencyManifestPath(normalizedPath) || manifestKind(base) !== null || isGeneratedLockfilePath(normalizedPath) || /^requirements[\w.-]*\.txt$/i.test(base)) {
|
|
2092
|
+
return "build-manifest";
|
|
2093
|
+
}
|
|
2094
|
+
if (isProjectConfigPath(normalizedPath) && !lowerPath.startsWith(".github/workflows/")) {
|
|
2095
|
+
return "gate-config";
|
|
2096
|
+
}
|
|
2097
|
+
return void 0;
|
|
2098
|
+
}
|
|
2099
|
+
function findOffScopeConfigEdits(changedPaths, taskText) {
|
|
2100
|
+
const task = taskText.toLowerCase();
|
|
2101
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2102
|
+
const edits = [];
|
|
2103
|
+
for (const raw of changedPaths) {
|
|
2104
|
+
const normalized = normalize(raw);
|
|
2105
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
2106
|
+
seen.add(normalized);
|
|
2107
|
+
const category = categoryOf(normalized);
|
|
2108
|
+
if (!category) continue;
|
|
2109
|
+
const base = path.posix.basename(normalized).toLowerCase();
|
|
2110
|
+
if (task.includes(normalized.toLowerCase()) || task.includes(base)) continue;
|
|
2111
|
+
edits.push({ path: normalized, category });
|
|
2112
|
+
}
|
|
2113
|
+
return edits;
|
|
2114
|
+
}
|
|
2115
|
+
function formatOffScopeConfigEdits(edits, limit = 8) {
|
|
2116
|
+
const shown = edits.slice(0, limit).map((edit) => `${edit.path} (${edit.category})`);
|
|
2117
|
+
const rest = edits.length - shown.length;
|
|
2118
|
+
return rest > 0 ? `${shown.join(", ")} (+${rest} more)` : shown.join(", ");
|
|
2119
|
+
}
|
|
2120
|
+
function findGateControllingEdits(edits) {
|
|
2121
|
+
return edits.filter((edit) => edit.category === "test-config" || edit.category === "gate-config");
|
|
2122
|
+
}
|
|
2123
|
+
function formatGateConfigUnverifiedReason(edits) {
|
|
2124
|
+
return `gate configuration was changed without the task naming it (${formatOffScopeConfigEdits(edits)}), so passing gates do not verify the change`;
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
// src/core/judge/prompt-spec-summary.ts
|
|
2128
|
+
function toPromptSpecSummary(spec) {
|
|
2129
|
+
return {
|
|
2130
|
+
intent: spec.intent,
|
|
2131
|
+
editsRequired: spec.editsRequired,
|
|
2132
|
+
prefersMinimalChange: spec.prefersMinimalChange,
|
|
2133
|
+
testsRequested: spec.testsRequested,
|
|
2134
|
+
testExecutionRequested: spec.testExecutionRequested,
|
|
2135
|
+
isTestIntent: spec.isTestIntent,
|
|
2136
|
+
isAnalysisIntent: spec.isAnalysisIntent,
|
|
2137
|
+
fileTargets: spec.fileConstraint.targets,
|
|
2138
|
+
deliverableSpecified: spec.deliverableConstraint.specified,
|
|
2139
|
+
deliverableTargets: spec.deliverableConstraint.targets,
|
|
2140
|
+
requirementCount: spec.requirements.length
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// src/core/classification/task-profile.ts
|
|
2145
|
+
function buildTaskProfile(classification, promptSpec, analysis) {
|
|
2146
|
+
return {
|
|
2147
|
+
classification,
|
|
2148
|
+
promptSpec,
|
|
2149
|
+
analysis,
|
|
2150
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2151
|
+
version: "1.0"
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
function buildTaskProfileFromPrompt(prompt, options) {
|
|
2155
|
+
const classification = classifyTask(prompt, {
|
|
2156
|
+
stripInjections: options?.stripInjections,
|
|
2157
|
+
variants: options?.variants
|
|
2158
|
+
});
|
|
2159
|
+
const fullSpec = parsePromptSpec(prompt, {
|
|
2160
|
+
analysisMode: options?.analysisMode
|
|
2161
|
+
});
|
|
2162
|
+
const promptSpec = toPromptSpecSummary(fullSpec);
|
|
2163
|
+
const analysis = analyzeTask(prompt);
|
|
2164
|
+
return buildTaskProfile(classification, promptSpec, analysis);
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
export {
|
|
2168
|
+
ProcessLifecycleManager,
|
|
2169
|
+
findOffScopeConfigEdits,
|
|
2170
|
+
formatOffScopeConfigEdits,
|
|
2171
|
+
findGateControllingEdits,
|
|
2172
|
+
formatGateConfigUnverifiedReason,
|
|
2173
|
+
createCliBreadcrumb,
|
|
2174
|
+
buildTaskProfileFromPrompt,
|
|
2175
|
+
evaluateExecutionSpec
|
|
2176
|
+
};
|
|
2177
|
+
//# sourceMappingURL=chunk-WMWQEFLN.js.map
|