elanous 0.2.21 → 0.2.23
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/NOTICE +7 -0
- package/README.md +32 -22
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{119fc678.6f591cdb84ca5791.js → 0673cc54.8e2ef1c23e49e190.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{a9f2b54d-40c16bd0efdb04b8.js → 0a09e062-34f747a17d8d8c2e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f8626309.a4e5d362b1d55e59.js → 0e3acd34.d42b2ef87fdf4234.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +77 -0
- package/apps/pwa/out/_next/static/chunks/{7175-350f4ce2873bc038.js → 1325-dc3f3c21eb181cb4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +34 -0
- package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +24 -0
- package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6767.8e6c5f6e744fcd9f.js → 1505.5826671109111e75.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 1669-da843ed73aa301f5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +36 -0
- package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +122 -0
- package/apps/pwa/out/_next/static/chunks/2528.0af5c0e1fad88df6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +62 -0
- package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7899.597af4a9996c907f.js → 2721.bc9a8f36bd85c07a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +149 -0
- package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +309 -0
- package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{d70879db.c66c56a95a91efc6.js → 4c5e95ce.83e9b16add6fcd8c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.566c890adc06a459.js → 5313.1697309c532b9856.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{79b78388.663188133449d707.js → 53746282.1be36038d1abd949.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5578.ac963634373b28c6.js +286 -0
- package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5967-c0a80f8ffe5d4050.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 5974.360f8441eb0ac1ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +74 -0
- package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6571-926ddf024a6ad5df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +64 -0
- package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9832.2ab2e818013574c7.js → 6702.ebbd165d5e4a4a14.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +32 -0
- package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +24 -0
- package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +93 -0
- package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 740.90b31555290236a9.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +148 -0
- package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{9070.067c106528ec1ae6.js → 7518.49161e8f5814abdb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +27 -0
- package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +115 -0
- package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7905.b230b9d06cb4ad5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8194.484f51e273d9abd2.js → 8264.00e434fbd02e9311.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{088d8e8a.8069543d082bec12.js → 83cfc5c8.349535543c1548b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{1948.5f35b39eef222a94.js → 8438.ee7f0df3b3f6fcb4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4588.ad3364cb7fb97ada.js → 8532.a86e99acf9b761cb.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/8569.b853a8daa8f31d12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8925-2abfb48af477fb7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{88729267.9c6c000157c83f2f.js → 89849432.9eaa2f4c9c010965.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +90 -0
- package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +94 -0
- package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +41 -0
- package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +287 -0
- package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +79 -0
- package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3522-1de1319d028ac89c.js → 9502-32c5dc22e8f120f5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{7630.2b9a68659053ff4e.js → 9976.8f28864317639fe6.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-ad3bd8084048bee4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-111cabe4d1fd69ae.js → page-6168a7c43e6ef8f5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-48fb31810693c357.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cb154acadb025414.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +1 -0
- package/apps/pwa/out/_next/static/chunks/b4e12462-5fab4a813a63dadd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → ce383d63.d2ede338788ed18e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{framework-5c169757551134ed.js → framework-b2763b10dc6b3836.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +1 -0
- package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +1 -0
- package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
- package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → rfkMm4movqkqMebBrCXiR}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +24 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +24 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/catalog/models/anthropic/claude-haiku-5-5.yaml +32 -0
- package/catalog/models/anthropic/claude-opus-5-5.yaml +5 -0
- package/catalog/models/anthropic/claude-sonnet-5-5.yaml +31 -0
- package/catalog/models/anthropic/claude-sonnet-5.yaml +8 -2
- package/catalog/models/openai/gpt-6-astra.yaml +6 -0
- package/catalog/models/openai/gpt-6-luna.yaml +6 -0
- package/catalog/models/openai/gpt-6-sol.yaml +6 -0
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/drafts/recipes.yaml +2 -2
- package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
- package/graphs/log-horizon/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +1 -1
- package/graphs/release/release-loop.yaml +6 -2
- package/graphs/release-rehearsal/recipes.yaml +12 -0
- package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
- package/graphs/test-diet/recipes.yaml +3 -0
- package/graphs/test-diet/test-diet.yaml +3 -1
- package/package.json +4 -1
- package/scripts/bench-report.ts +82 -3
- package/scripts/ci-typecheck-changed.ts +10 -1
- package/scripts/f-session-monitor.ts +69 -0
- package/scripts/hq/hq-fence-wrapper.sh +148 -0
- package/scripts/hq/standby-snapshot.ts +7 -3
- package/scripts/knowledge-compress.ts +11 -0
- package/scripts/lib/typecheck-ratchet.ts +51 -0
- package/scripts/nightly-docops.ts +90 -3
- package/scripts/openai-relay-server.ts +5 -1
- package/scripts/release-loop/docs-node.ts +258 -43
- package/scripts/release-loop/gate-method.ts +59 -0
- package/scripts/release-loop/gate-node.ts +73 -7
- package/scripts/release-loop/gate-wall.ts +208 -0
- package/scripts/release-loop/known-issues-node.ts +53 -8
- package/scripts/release-loop/publish-at.ts +14 -0
- package/scripts/release-loop/publish-node.ts +17 -2
- package/scripts/release-loop/resume-release.ts +34 -4
- package/scripts/release-loop/resume.ts +53 -0
- package/scripts/release-loop/shard-plan.ts +3 -11
- package/scripts/release-loop/tui-node-checks.ts +22 -0
- package/scripts/release-loop/unattended-release.ts +8 -1
- package/scripts/research-coo-admin-dates.ts +39 -0
- package/scripts/rhythm/daily.ts +51 -2
- package/scripts/rhythm/weekly.ts +3 -2
- package/scripts/skills/public-check.ts +7 -2
- package/scripts/smart-merge/resync-shadow.ts +5 -3
- package/scripts/test-diet/lib.ts +81 -1
- package/scripts/test-diet/node.ts +48 -2
- package/scripts/tsc-main-safety-net.ts +189 -0
- package/scripts/usage-rollup.ts +8 -4
- package/scripts/verify-coo-admin-evidence.ts +83 -0
- package/src/acp/client.ts +4 -2
- package/src/acp/codex-plugins.ts +48 -6
- package/src/agent/monad-agent-turn.ts +67 -5
- package/src/agent/run-context.ts +14 -0
- package/src/agent/tool-profile.ts +28 -2
- package/src/agent-env/profile.ts +25 -5
- package/src/agent-mission/acp-judge.ts +8 -2
- package/src/agent-mission/capabilities-cli.ts +16 -1
- package/src/agent-mission/driver.ts +27 -13
- package/src/agent-mission/pr-review-watch.ts +6 -2
- package/src/agent-mission/review-loop.ts +33 -5
- package/src/agent-mission/supervisor-metrics.ts +17 -2
- package/src/agent-substrate/acp-reviewer.ts +25 -9
- package/src/agent-substrate/pr-reviewer.ts +175 -32
- package/src/agent-substrate/review-intent.ts +106 -5
- package/src/agent-substrate/self-review-cli.ts +111 -16
- package/src/agent-substrate/shard-boundary-intent.ts +92 -26
- package/src/ask-user-question/pending-questions.ts +8 -2
- package/src/autopilot/build/llm-conflict-merge.ts +238 -20
- package/src/autopilot/codebase-grounding.ts +44 -0
- package/src/autopilot/llm-control-brain.ts +13 -1
- package/src/autopilot/mission-codebase-gate.ts +8 -77
- package/src/autopilot/pr-manager.ts +30 -11
- package/src/away/release-watch.ts +47 -8
- package/src/boot/acp-server.ts +4 -7
- package/src/boot/acp-socket-path.ts +7 -0
- package/src/briefing/brief-items.ts +123 -22
- package/src/chat/index.ts +13 -3
- package/src/cli/agent-cli.ts +20 -0
- package/src/cli/brief-cli.ts +46 -22
- package/src/cli/cli-user-error.ts +9 -2
- package/src/cli/config-classify.ts +83 -0
- package/src/cli/control-cli.ts +14 -0
- package/src/cli/decisions-cli.ts +26 -16
- package/src/cli/log-event-names.ts +3 -0
- package/src/cli/logs-cli.ts +1 -0
- package/src/cli/logs-model-input.ts +1 -1
- package/src/cli/mcp-login.ts +111 -37
- package/src/cli/plugin-cli.ts +21 -1
- package/src/cli/pod-cli.ts +26 -0
- package/src/cli/pr-cli.ts +94 -9
- package/src/cli/pr-land-overlap-evidence.ts +131 -0
- package/src/cli/release-cli.ts +74 -15
- package/src/cli/self-update.ts +173 -10
- package/src/cli/skills-cli.ts +12 -3
- package/src/cli/tasks-cli.ts +241 -28
- package/src/connectors/types.ts +6 -6
- package/src/context-bus/context-first.ts +51 -2
- package/src/context-bus/context-now-surfaces.ts +29 -0
- package/src/context-bus/coord-events.ts +32 -4
- package/src/context-bus/long-term-memory.ts +26 -7
- package/src/context-bus/nightly-condense.ts +170 -21
- package/src/control/control-status.ts +87 -0
- package/src/control-plane/light-member.ts +5 -3
- package/src/control-plane/member.ts +172 -7
- package/src/core-turn/run-goal-loop.ts +108 -3
- package/src/dashboard/chat-fold.ts +20 -0
- package/src/dashboard/first-screen-band.ts +151 -1
- package/src/dashboard/index.ts +27 -5
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -3
- package/src/dashboard/stale-install-check.ts +13 -2
- package/src/dashboard/turn-stream-formatter.ts +25 -0
- package/src/debug/log-diet.ts +159 -0
- package/src/debug/log.ts +52 -17
- package/src/decisions/decision-cards.ts +20 -1
- package/src/decisions/decision-ledger.ts +11 -8
- package/src/decisions/web-push-decision-cards.ts +19 -3
- package/src/discord-self-message.ts +2 -2
- package/src/display/coordinator.ts +25 -2
- package/src/domains/coo-admin-dates.evidence.json +876 -0
- package/src/domains/coo-admin-tool.ts +75 -14
- package/src/domains/cron-match.ts +1 -0
- package/src/domains/ops-status.ts +53 -0
- package/src/domains/outbound-alert.ts +41 -7
- package/src/domains/release-tool.ts +119 -10
- package/src/domains/schedule-registry.ts +10 -7
- package/src/domains/schedule-runner.ts +1 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/elanous-daemon.ts +3 -3
- package/src/exec-requests/planner.ts +6 -0
- package/src/execution-loop/launch-gate.ts +1 -1
- package/src/expression/i18n/messages.en.ts +5 -0
- package/src/expression/i18n/messages.ja.ts +5 -0
- package/src/expression/i18n/messages.ko.ts +5 -0
- package/src/expression/i18n/messages.zh.ts +5 -0
- package/src/expression/i18n/types.ts +8 -0
- package/src/github/pr-labels.ts +1 -0
- package/src/graph-kinds/registry.ts +6 -6
- package/src/graph-runner/graph-cli.ts +34 -2
- package/src/graph-runner/graph-run-api.ts +7 -0
- package/src/graph-runner/graph-tick.ts +73 -12
- package/src/graph-runner/runner.ts +94 -13
- package/src/graph-unify/wf-to-graph.ts +79 -0
- package/src/graph-wizard/generate.ts +34 -2
- package/src/graph-wizard/steps.ts +13 -0
- package/src/harness/dispatch-backpressure.ts +259 -0
- package/src/harness/harness-cli-command.ts +227 -50
- package/src/harness/harness-queue.ts +58 -14
- package/src/harness/harness-seams.ts +2 -20
- package/src/harness/harness-substrate-default.ts +72 -1
- package/src/harness/launch-advice.ts +23 -0
- package/src/harness/nested-elanous-policy.ts +4 -2
- package/src/hq/fence-wrapper.ts +7 -0
- package/src/hq/hq.ts +33 -2
- package/src/hq/lease.ts +13 -0
- package/src/hq/seats.ts +114 -18
- package/src/index.ts +199 -71
- package/src/intake-plane/collect-github.ts +19 -1
- package/src/intake-plane/digest.ts +45 -20
- package/src/intake-plane/lens.ts +36 -8
- package/src/intelligence-map/model-alias.ts +6 -0
- package/src/intelligence-map/model-catalog.ts +41 -0
- package/src/knowledge/conclusion-compress.ts +93 -0
- package/src/knowledge/kgs/pack.ts +5 -2
- package/src/knowledge/kgs/sqlite-store.ts +15 -0
- package/src/knowledge/know-find.ts +9 -2
- package/src/knowledge/query.ts +20 -0
- package/src/knowledge/tools/knowledge-query.ts +20 -2
- package/src/knowledge/write.ts +234 -2
- package/src/llm/bedrock-provider.ts +175 -0
- package/src/llm/bedrock.ts +345 -0
- package/src/llm/provider-credentials.ts +3 -1
- package/src/llm.ts +332 -51
- package/src/loops/budget.ts +150 -0
- package/src/loops/neighbor-runtime.ts +88 -0
- package/src/loops/orchestrator/author-depth.ts +29 -11
- package/src/loops/orchestrator/overlap.ts +7 -1
- package/src/loops/orchestrator/resync-shadow.ts +1 -1
- package/src/loops/orchestrator/seat-share.ts +6 -49
- package/src/loops/orchestrator/tick.ts +35 -2
- package/src/loops/orchestrator/traffic.ts +6 -80
- package/src/loops/registry.ts +5 -0
- package/src/market/publish.ts +17 -1
- package/src/market/signed-index.ts +42 -0
- package/src/maturity/feature-maturity.ts +2 -1
- package/src/mcp/mcp-oauth.ts +136 -17
- package/src/model-tier/llm-tier-map.ts +48 -4
- package/src/models/prompts.ts +22 -1
- package/src/mss/logging/horizon-watch.ts +168 -0
- package/src/mss/logging/log-store.ts +204 -15
- package/src/mss/logging/redaction.ts +17 -0
- package/src/native-tool-catalog.ts +1 -1
- package/src/nexus/api/consult-requests.ts +7 -10
- package/src/nexus/api/discord-trigger-bot.ts +6 -3
- package/src/nexus/api/draft-metrics.ts +22 -8
- package/src/nexus/api/error-reports.ts +30 -8
- package/src/nexus/api/graph-peer-edit.ts +77 -0
- package/src/nexus/api/graph-wizard-api.ts +9 -1
- package/src/nexus/api/graphs-api.ts +145 -7
- package/src/nexus/api/http-server.ts +101 -8
- package/src/nexus/api/loop-resources.ts +42 -0
- package/src/nexus/api/outbound-report.ts +8 -3
- package/src/nexus/api/plugins-market.ts +24 -0
- package/src/nexus/api/telegram-trigger-bot.ts +6 -3
- package/src/nexus/index.ts +13 -2
- package/src/nexus/outbound/format.ts +2 -0
- package/src/nexus/outbound/router.ts +198 -13
- package/src/oauth/claude-pty-maturity.ts +24 -0
- package/src/oauth/codex-account-rotation.ts +16 -18
- package/src/oauth/codex-account-store.ts +134 -5
- package/src/oauth/fallback-chain.ts +50 -15
- package/src/onboarding.ts +2 -1
- package/src/plugins/adapters/claude-package.ts +4 -12
- package/src/plugins/core/capability-policy.ts +141 -0
- package/src/plugins/core/host.ts +21 -8
- package/src/plugins/core/manifest.ts +19 -2
- package/src/plugins/core/trust-store.ts +15 -0
- package/src/plugins/core/types.ts +12 -0
- package/src/plugins/install/market-fetch.ts +68 -16
- package/src/plugins/install/plugin-install.ts +187 -16
- package/src/plugins/maker/market-bundle.ts +9 -0
- package/src/plugins/maker/plugin-maker.ts +32 -21
- package/src/plugins/maker/wizard-generate.ts +87 -9
- package/src/policy/llm-policy.ts +24 -6
- package/src/process/pid-liveness.ts +1 -1
- package/src/prompt-cache/openai.ts +10 -6
- package/src/prompt-cache/openrouter.ts +94 -0
- package/src/prompt-enhance/enhance.ts +46 -5
- package/src/prompt-library/family-prompt-coverage.ts +1 -0
- package/src/prompt-library/universal-preamble.ts +2 -2
- package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
- package/src/registry/loader.ts +6 -1
- package/src/release-loop/checklist-board.ts +27 -0
- package/src/release-loop/checklist.ts +62 -9
- package/src/release-loop/feature-store.ts +3 -0
- package/src/release-loop/gate-shards.ts +48 -4
- package/src/release-loop/landed-to-green.ts +81 -0
- package/src/release-loop/light-rc.ts +11 -1
- package/src/release-loop/merged-pr-checklist.ts +10 -0
- package/src/release-loop/placement.ts +91 -22
- package/src/release-loop/rehearsal.ts +317 -0
- package/src/release-loop/release-schedule.ts +13 -1
- package/src/release-loop/rubric-types.ts +1 -0
- package/src/release-loop/rubric.ts +2 -2
- package/src/retro/daily.ts +23 -9
- package/src/seat-address/seat-address.ts +1 -0
- package/src/seat-loop/seat-loop.ts +208 -21
- package/src/seat-loop/seat-report.ts +29 -1
- package/src/seat-loop/traffic-signal.ts +44 -0
- package/src/self-dev/ask-launch-flow.ts +2 -2
- package/src/self-dev/dev-cli.ts +157 -11
- package/src/self-dev/dev-pipeline.ts +18 -1
- package/src/self-dev/draft-residue.ts +463 -0
- package/src/self-dev/draft-sweep.ts +195 -17
- package/src/self-dev/draft-triage-rules.ts +27 -1
- package/src/self-dev/launch-preflight-text.ts +549 -0
- package/src/self-dev/launch-preflight.ts +10 -545
- package/src/self-dev/orchestrate-cli.ts +57 -2
- package/src/self-dev/orchestrate-summary-line.ts +13 -0
- package/src/self-dev/orchestrate.ts +9 -133
- package/src/self-dev/review-provider-fallback.ts +179 -0
- package/src/self-dev/run-store.ts +1 -3
- package/src/self-dev/run-supervisor.ts +8 -23
- package/src/self-dev/run-types.ts +178 -0
- package/src/self-dev/shard-aggregate.ts +61 -13
- package/src/self-dev/shard-plan.ts +5 -1
- package/src/self-dev/shard-run.ts +23 -5
- package/src/self-dev/supervisor-verdict-edges.ts +2 -4
- package/src/self-implement/blocked-draft-policy.ts +3 -19
- package/src/self-implement/draft-on-stop-mode.ts +18 -0
- package/src/self-implement/follow-up-goals.ts +114 -38
- package/src/self-implement/gate-baseline-reap.ts +157 -0
- package/src/self-implement/gate-baseline.ts +78 -16
- package/src/self-implement/gate-cli.ts +12 -2
- package/src/self-implement/gate-remote.ts +17 -6
- package/src/self-implement/goal-author.ts +24 -4
- package/src/self-implement/goal-digest.ts +19 -0
- package/src/self-implement/graph-overlay-condition.ts +4 -0
- package/src/self-implement/harness-policy-gates.ts +7 -5
- package/src/self-implement/headless-elanous-driver.ts +11 -9
- package/src/self-implement/host-regate.ts +148 -43
- package/src/self-implement/l8-merge-queue.ts +147 -13
- package/src/self-implement/merge-train.ts +131 -0
- package/src/self-implement/off-diff-evidence.ts +9 -0
- package/src/self-implement/orchestrator.ts +201 -43
- package/src/self-implement/review-finding-recurrence.ts +28 -0
- package/src/self-implement/rework-policy.ts +2 -1
- package/src/self-implement/round-summary-card.ts +63 -0
- package/src/self-implement/run-ledger.ts +4 -3
- package/src/self-implement/run-step-projection.ts +147 -0
- package/src/self-implement/run-stop.ts +1 -1
- package/src/self-implement/seams.ts +41 -6
- package/src/self-implement/self-implement-cli.ts +6 -0
- package/src/self-implement/train-gate-remote.ts +158 -0
- package/src/session-runtime/index.ts +10 -1
- package/src/session-runtime/retry-api.ts +11 -1
- package/src/shell-env-bootstrap.ts +19 -4
- package/src/skills/gift-install.ts +119 -0
- package/src/skills/runner.ts +59 -2
- package/src/skills/skill-feedback.ts +31 -1
- package/src/skills/skill-tier.ts +1 -0
- package/src/smart-wizard/capability-index.ts +231 -0
- package/src/smart-wizard/rung.ts +74 -0
- package/src/steward/launch.ts +20 -9
- package/src/steward/triage.ts +29 -10
- package/src/storage/file-lock.ts +40 -4
- package/src/task-agent/actions.ts +4 -0
- package/src/task-agent/card-evidence.ts +59 -13
- package/src/task-agent/completion-evidence.ts +31 -3
- package/src/task-agent/cover.ts +61 -0
- package/src/task-agent/judge.ts +4 -3
- package/src/task-agent/land-worktree.ts +201 -0
- package/src/task-agent/live-move-history.ts +133 -0
- package/src/task-agent/live-moves.ts +222 -12
- package/src/task-agent/parent-host.ts +117 -8
- package/src/task-agent/rejudge-on-head.ts +297 -0
- package/src/task-agent/review-result-capture.ts +218 -0
- package/src/task-agent/shadow.ts +118 -7
- package/src/task-agent/task-hand.ts +138 -11
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +29 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +16 -2
- package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +93 -12
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +25 -2
- package/src/task-orchestrator/surfaces/pod-pool.ts +203 -28
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +259 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +11 -0
- package/src/telegram-commands.ts +8 -0
- package/src/telegram-project-command.ts +6 -0
- package/src/telegram-service.ts +5 -1
- package/src/telegram.ts +17 -4
- package/src/time/format.ts +13 -4
- package/src/tool-runtime/goal-author-runtime.ts +1 -0
- package/src/tool-runtime/self-cognition-catalog.ts +23 -0
- package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
- package/src/typecheck-ratchet.ts +2 -1
- package/src/user-config.ts +167 -28
- package/src/ux-sim/slash-menu-frames.ts +168 -0
- package/src/version/packed-revision.json +1 -1
- package/src/web-terminal/preview-tap-registry.ts +5 -2
- package/src/workflow-runtime/executor.ts +24 -2
- package/src/workflow-runtime/triggers/registry.ts +27 -0
- package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
- package/src/workflow-runtime/triggers/scheduler.ts +1 -0
- package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
- package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
- package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
- package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
- package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
- package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
- package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
- package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
- package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
- package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
- package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
- package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
- package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
- package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
- package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +0 -1
- package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
- package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
- package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
- package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
- package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
- package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
- package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
- package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
- package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d781be3e3a9cfd74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b0a80997903852d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-2b1263165630d9c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-e2d6bed48ac20120.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-544dd4bd4847eeb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-3ee191319bf97d87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-a449f1ed472b81c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-9045094d6d3b5cd0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c06673596849ef45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-9f81233f29dcd409.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-7a12f61be4a579d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-c85b0589591a99c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-725a4da02dd129eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-21bec4afeb2f0917.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-dc39ee7a36d26385.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-84ae759f76053672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-3db58ddb61fe2be9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c5315e30f3528dc4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-2fe368efb6de3b6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-c69762987b751174.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-7f8d7fcdf058a845.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-22163556cd38bc88.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
- package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +0 -1
- /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → rfkMm4movqkqMebBrCXiR}/_ssgManifest.js +0 -0
package/src/loops/budget.ts
CHANGED
|
@@ -1,4 +1,61 @@
|
|
|
1
1
|
import { ORCHESTRATOR_DEFAULTS, type OrchestratorSeat } from '../user-config.js';
|
|
2
|
+
import type { ChecklistItem } from '../release-loop/checklist.js';
|
|
3
|
+
import type { TrafficCell, TrafficProcess, TrafficResult, TrafficSeatRow } from './orchestrator/traffic.js';
|
|
4
|
+
|
|
5
|
+
const RESOURCE_SEATS: readonly OrchestratorSeat[] = ['OP', 'TC', 'MK', 'UX'];
|
|
6
|
+
|
|
7
|
+
type WorkCell = Pick<ChecklistItem, 'owner' | 'status'>;
|
|
8
|
+
|
|
9
|
+
/** Allocate whole base slots by remaining work; this calculation does not decide launches. */
|
|
10
|
+
export function calculateSeatBaseShares({ totalSlots, currentRound, nextRound, seatCaps }: {
|
|
11
|
+
totalSlots: number;
|
|
12
|
+
currentRound: readonly WorkCell[];
|
|
13
|
+
nextRound: readonly WorkCell[];
|
|
14
|
+
seatCaps: Readonly<Record<OrchestratorSeat, number>>;
|
|
15
|
+
}): Record<OrchestratorSeat, number> {
|
|
16
|
+
if (!Number.isSafeInteger(totalSlots) || totalSlots < 0 ||
|
|
17
|
+
RESOURCE_SEATS.some(seat => !Number.isSafeInteger(seatCaps[seat]) || seatCaps[seat] < 0)) {
|
|
18
|
+
throw new RangeError('totalSlots and seatCaps must be nonnegative safe integers');
|
|
19
|
+
}
|
|
20
|
+
const shares: Record<OrchestratorSeat, number> = { OP: 0, TC: 0, MK: 0, UX: 0 };
|
|
21
|
+
const weights: Record<OrchestratorSeat, number> = { OP: 0, TC: 0, MK: 0, UX: 0 };
|
|
22
|
+
for (const [cells, weight] of [[currentRound, 2], [nextRound, 1]] as const) {
|
|
23
|
+
for (const cell of cells) {
|
|
24
|
+
if (cell.status !== 'yellow') continue;
|
|
25
|
+
const seat = cell.owner?.split('/')[0];
|
|
26
|
+
if (RESOURCE_SEATS.some(id => id === seat)) weights[seat as OrchestratorSeat] += weight;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let available = Math.min(totalSlots, RESOURCE_SEATS.reduce((sum, seat) => sum + seatCaps[seat], 0));
|
|
31
|
+
let eligible = RESOURCE_SEATS.filter(seat => weights[seat] > 0 && seatCaps[seat] > 0);
|
|
32
|
+
while (available > 0 && eligible.length > 0) {
|
|
33
|
+
const totalWeight = eligible.reduce((sum, seat) => sum + weights[seat], 0);
|
|
34
|
+
const ideal = (seat: OrchestratorSeat) => available * weights[seat] / totalWeight;
|
|
35
|
+
const capped = eligible.filter(seat => ideal(seat) >= seatCaps[seat]);
|
|
36
|
+
if (capped.length > 0) {
|
|
37
|
+
for (const seat of capped) {
|
|
38
|
+
shares[seat] = seatCaps[seat];
|
|
39
|
+
available -= seatCaps[seat];
|
|
40
|
+
}
|
|
41
|
+
eligible = eligible.filter(seat => !capped.includes(seat));
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const remainders = eligible.map(seat => ({ seat, exact: ideal(seat) }));
|
|
45
|
+
for (const { seat, exact } of remainders) {
|
|
46
|
+
shares[seat] = Math.floor(exact);
|
|
47
|
+
available -= shares[seat];
|
|
48
|
+
}
|
|
49
|
+
remainders.sort((a, b) => (b.exact - Math.floor(b.exact)) - (a.exact - Math.floor(a.exact)));
|
|
50
|
+
for (const { seat } of remainders) {
|
|
51
|
+
if (available === 0) break;
|
|
52
|
+
shares[seat]++;
|
|
53
|
+
available--;
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
return shares;
|
|
58
|
+
}
|
|
2
59
|
|
|
3
60
|
export interface SpawnBudgetInput {
|
|
4
61
|
seat: OrchestratorSeat;
|
|
@@ -97,3 +154,96 @@ export function seatCapExclusion(subject: SeatCapSubject, now: number,
|
|
|
97
154
|
if (idle !== null && now - subject.progressAt! >= staleRunMinutes * 60_000) return { reason: 'stale', idleMin: idle };
|
|
98
155
|
return null;
|
|
99
156
|
}
|
|
157
|
+
|
|
158
|
+
/** Only harness ask/say launches occupy ORCH traffic seats. */
|
|
159
|
+
export const isTrafficLaunch = (command: string): boolean =>
|
|
160
|
+
/(?:^|\s)(?:\S*\/)?elanous\.mjs\s+(?:(?:--test|--config-dir\s+\S+)\s+)?harness\s+(?:ask|say)(?=\s|$)/.test(command);
|
|
161
|
+
|
|
162
|
+
/** ORCH traffic supplies a physical cap, which can exceed the default spawn release gate. */
|
|
163
|
+
function decideTrafficLaunch(row: TrafficSeatRow): boolean {
|
|
164
|
+
return decideSpawn({ seat: row.seat, running: row.running, caps: { [row.seat]: row.cap },
|
|
165
|
+
gate: { [row.seat]: row.cap }, injectedCap: row.launchCap }).allow;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Process-to-seat resolution occurs at the boundary; this decision does no IO. */
|
|
169
|
+
export function budgetTrafficTick({ processes, now, caps, lastLaunchAt, idleSince, openCells, nextRound, totalSlots, idleMinutes = 30, retainQueuedCells = false }: {
|
|
170
|
+
processes: readonly TrafficProcess[];
|
|
171
|
+
now: Date;
|
|
172
|
+
caps: Readonly<Record<OrchestratorSeat, number>>;
|
|
173
|
+
lastLaunchAt?: Partial<Record<OrchestratorSeat, Date | null>>;
|
|
174
|
+
idleSince?: Partial<Record<OrchestratorSeat, Date | null>>;
|
|
175
|
+
openCells: readonly TrafficCell[];
|
|
176
|
+
nextRound?: readonly TrafficCell[];
|
|
177
|
+
totalSlots?: number;
|
|
178
|
+
idleMinutes?: number;
|
|
179
|
+
/** Observation only: expose queued work even when no launch has been observed yet. */
|
|
180
|
+
retainQueuedCells?: boolean;
|
|
181
|
+
}): TrafficResult {
|
|
182
|
+
const launches = processes.filter(process => isTrafficLaunch(process.command));
|
|
183
|
+
const slotBudget = totalSlots ?? RESOURCE_SEATS.reduce((sum, seat) => sum + caps[seat], 0);
|
|
184
|
+
// A missing next round is unknown, not an empty round with zero remaining work.
|
|
185
|
+
const baseShares = nextRound === undefined ? null : calculateSeatBaseShares({
|
|
186
|
+
totalSlots: slotBudget, currentRound: openCells, nextRound, seatCaps: caps,
|
|
187
|
+
});
|
|
188
|
+
const seats = RESOURCE_SEATS.map((seat): TrafficSeatRow => {
|
|
189
|
+
const owned = launches.filter(process => process.seat === seat);
|
|
190
|
+
const latest = owned.reduce<number | null>((current, process) => {
|
|
191
|
+
if (!Number.isFinite(process.elapsedSeconds) || process.elapsedSeconds < 0) return current;
|
|
192
|
+
const start = now.getTime() - process.elapsedSeconds * 1000;
|
|
193
|
+
return current === null ? start : Math.max(current, start);
|
|
194
|
+
}, null);
|
|
195
|
+
const fallback = owned.length ? lastLaunchAt?.[seat]?.getTime() : undefined;
|
|
196
|
+
const idleStart = idleSince?.[seat]?.getTime();
|
|
197
|
+
const last = idleStart !== undefined && Number.isFinite(idleStart) ? idleStart
|
|
198
|
+
: latest ?? (fallback !== undefined && Number.isFinite(fallback) ? fallback : null);
|
|
199
|
+
const idleFor = last === null ? null : Math.max(0, Math.floor((now.getTime() - last) / 60_000));
|
|
200
|
+
const nextCell = openCells.find(cell => cell.status !== 'done' && cell.owner?.split('/')[0] === seat) ?? null;
|
|
201
|
+
const baseShare = baseShares?.[seat] ?? null;
|
|
202
|
+
return { seat, running: owned.length, cap: caps[seat], baseShare, borrowed: 0, lent: 0,
|
|
203
|
+
launchCap: baseShare === null ? caps[seat] : Math.max(owned.length, baseShare), idleFor,
|
|
204
|
+
idle: decideSpawn({ seat, running: owned.length, caps: { [seat]: caps[seat] },
|
|
205
|
+
gate: { [seat]: caps[seat] } }).allow && last !== null && now.getTime() - last > idleMinutes * 60_000, nextCell };
|
|
206
|
+
});
|
|
207
|
+
if (baseShares !== null) {
|
|
208
|
+
// Account for outstanding loans before lending new capacity. A returning
|
|
209
|
+
// lender cannot launch into a slot still occupied by the borrower's run.
|
|
210
|
+
for (const borrower of seats) {
|
|
211
|
+
let excess = Math.max(0, borrower.running - borrower.baseShare!);
|
|
212
|
+
borrower.borrowed += excess;
|
|
213
|
+
for (const lender of seats) {
|
|
214
|
+
if (!excess) break;
|
|
215
|
+
if (lender === borrower) continue;
|
|
216
|
+
const amount = Math.min(excess, Math.max(0, lender.baseShare! - lender.running - lender.lent));
|
|
217
|
+
lender.lent += amount;
|
|
218
|
+
excess -= amount;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
for (const lender of seats) {
|
|
222
|
+
if (lender.nextCell) continue;
|
|
223
|
+
let available = Math.max(0, lender.baseShare! - lender.running - lender.lent);
|
|
224
|
+
for (const borrower of seats) {
|
|
225
|
+
if (!available) break;
|
|
226
|
+
if (borrower === lender || !borrower.nextCell) continue;
|
|
227
|
+
const needed = Math.max(0, Math.min(borrower.cap, borrower.running + 1) - borrower.launchCap);
|
|
228
|
+
const amount = Math.min(available, needed);
|
|
229
|
+
lender.lent += amount;
|
|
230
|
+
borrower.borrowed += amount;
|
|
231
|
+
borrower.launchCap += amount;
|
|
232
|
+
available -= amount;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const lender of seats) lender.launchCap = Math.max(lender.running, lender.launchCap - lender.lent);
|
|
236
|
+
}
|
|
237
|
+
// Reserve one available global slot per possible request, including unknown-seat
|
|
238
|
+
// launches in the occupied count; returning a lent share does not end its old run.
|
|
239
|
+
let freeSlots = Math.max(0, slotBudget - launches.length);
|
|
240
|
+
for (const row of seats) {
|
|
241
|
+
if (row.nextCell && row.idle && decideTrafficLaunch(row)) {
|
|
242
|
+
if (freeSlots > 0) freeSlots--;
|
|
243
|
+
else row.launchCap = row.running;
|
|
244
|
+
}
|
|
245
|
+
row.idle = row.idle && (row.nextCell === null || decideTrafficLaunch(row));
|
|
246
|
+
if (!row.idle && !retainQueuedCells) row.nextCell = null;
|
|
247
|
+
}
|
|
248
|
+
return { seats, unassigned: launches.filter(process => process.seat === null).length, now, idleMinutes };
|
|
249
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { debug } from '../debug/log.js';
|
|
2
|
+
import { openMsgStore } from '../msg/msg-store.js';
|
|
3
|
+
import { judgeNeighbor, type Neighbor } from './neighbors.js';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
export type LoopHealth = 'healthy' | 'degraded';
|
|
7
|
+
export type NeighborIncident = { observer: string; neighbor: string; state: 'absent'; reason: string; action: Neighbor['onAbsent']['action']; to: string; at: string; lastSeenAt: string | null };
|
|
8
|
+
|
|
9
|
+
export const SEAT_NEIGHBORS: Record<string, Neighbor[]> = Object.fromEntries(['OP', 'TC', 'MK', 'UX'].map(seat => [seat,
|
|
10
|
+
[
|
|
11
|
+
...['OP', 'TC', 'MK', 'UX'].filter(peer => peer !== seat).map(peer => ({
|
|
12
|
+
id: peer === 'MK' ? 'cmo-seat' : `${peer.toLowerCase()}-seat`, exchange: ['status'] as Neighbor['exchange'],
|
|
13
|
+
heartbeat: { everyMinutes: 15, missedTicks: 2 },
|
|
14
|
+
onAbsent: peer === 'OP' ? { action: 'delegate' as const, delegateTo: 'steward' }
|
|
15
|
+
: peer === 'TC' ? { action: 'escalate' as const, delegateTo: 'orchestrator' }
|
|
16
|
+
: { action: 'defer' as const, delegateTo: 'orchestrator' },
|
|
17
|
+
})),
|
|
18
|
+
{ id: 'orchestrator', exchange: ['status'], heartbeat: { everyMinutes: 120, missedTicks: 2 },
|
|
19
|
+
onAbsent: { action: 'escalate', delegateTo: 'human' } },
|
|
20
|
+
],
|
|
21
|
+
]));
|
|
22
|
+
|
|
23
|
+
type Signal = { at: string; health: LoopHealth; ends_at: string | null };
|
|
24
|
+
|
|
25
|
+
function withStore<T>(root: string, fn: (store: ReturnType<typeof openMsgStore>) => T): T {
|
|
26
|
+
const store = openMsgStore(join(root, 'msg', 'messages.db'));
|
|
27
|
+
try {
|
|
28
|
+
store.db.exec(`CREATE TABLE IF NOT EXISTS loop_signals (loop_id TEXT PRIMARY KEY, at TEXT NOT NULL, health TEXT NOT NULL, ends_at TEXT);
|
|
29
|
+
CREATE TABLE IF NOT EXISTS loop_watches (observer TEXT NOT NULL, neighbor TEXT NOT NULL, first_at TEXT NOT NULL, PRIMARY KEY(observer, neighbor));
|
|
30
|
+
CREATE TABLE IF NOT EXISTS loop_incidents (observer TEXT NOT NULL, neighbor TEXT NOT NULL, signal_at TEXT NOT NULL,
|
|
31
|
+
reason TEXT NOT NULL, action TEXT NOT NULL, recipient TEXT NOT NULL, at TEXT NOT NULL,
|
|
32
|
+
PRIMARY KEY(observer, neighbor, signal_at, reason));`);
|
|
33
|
+
return fn(store);
|
|
34
|
+
} finally { store.close(); }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Durable signal, independent of chat sessions or the seat work-item ledger. */
|
|
38
|
+
export function emitLoopHeartbeat(root: string, loopId: string, health: LoopHealth, now: Date, endsAt?: string): void {
|
|
39
|
+
if (!/^[a-z][a-z0-9-]*$/.test(loopId) || !Number.isFinite(now.getTime())) throw new Error('invalid loop heartbeat');
|
|
40
|
+
if (endsAt && !Number.isFinite(Date.parse(endsAt))) throw new Error('invalid heartbeat end');
|
|
41
|
+
withStore(root, store => {
|
|
42
|
+
store.db.query(`INSERT INTO loop_signals(loop_id, at, health, ends_at) VALUES(?, ?, ?, ?)
|
|
43
|
+
ON CONFLICT(loop_id) DO UPDATE SET at=excluded.at, health=excluded.health, ends_at=excluded.ends_at
|
|
44
|
+
WHERE loop_signals.at <= excluded.at`).run(loopId, now.toISOString(), health, endsAt ?? null);
|
|
45
|
+
});
|
|
46
|
+
debug.log(`loop.${loopId}`, 'heartbeat', { loopId, at: now.toISOString(), health, endsAt: endsAt ?? null });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** First watch starts the missing-signal clock. Action receipt and message commit atomically. */
|
|
50
|
+
export function checkLoopNeighbors(root: string, observer: string, neighbors: readonly Neighbor[], now: Date): NeighborIncident[] {
|
|
51
|
+
return withStore(root, store => {
|
|
52
|
+
const incidents: NeighborIncident[] = [];
|
|
53
|
+
for (const neighbor of neighbors) {
|
|
54
|
+
if (neighbor.id === observer) continue;
|
|
55
|
+
try {
|
|
56
|
+
store.db.exec('BEGIN IMMEDIATE');
|
|
57
|
+
store.db.query('INSERT OR IGNORE INTO loop_watches(observer, neighbor, first_at) VALUES(?, ?, ?)').run(observer, neighbor.id, now.toISOString());
|
|
58
|
+
const signal = store.db.query('SELECT at, health, ends_at FROM loop_signals WHERE loop_id=?').get(neighbor.id) as Signal | null;
|
|
59
|
+
const watch = store.db.query('SELECT first_at FROM loop_watches WHERE observer=? AND neighbor=?').get(observer, neighbor.id) as { first_at: string };
|
|
60
|
+
const judgment = judgeNeighbor({ neighbor, lastSeenAt: signal?.at ?? watch.first_at, now });
|
|
61
|
+
const expired = !!signal?.ends_at && Date.parse(signal.ends_at) <= now.getTime();
|
|
62
|
+
// A declared lease (ends_at in the future) outranks the heartbeat interval: a scheduled loop idle between
|
|
63
|
+
// windows is not absent until its own declared end passes.
|
|
64
|
+
const leased = !!signal?.ends_at && !expired;
|
|
65
|
+
const absent = expired || signal?.health === 'degraded' || (!leased && judgment.state === 'absent');
|
|
66
|
+
if (!absent) { store.db.exec('COMMIT'); continue; }
|
|
67
|
+
const reason = expired ? 'end-expired' : signal?.health === 'degraded' ? 'degraded' : judgment.reason;
|
|
68
|
+
const signalAt = signal?.at ?? watch.first_at;
|
|
69
|
+
const { action, delegateTo: to } = neighbor.onAbsent;
|
|
70
|
+
const inserted = store.db.query(`INSERT OR IGNORE INTO loop_incidents(observer, neighbor, signal_at, reason, action, recipient, at)
|
|
71
|
+
VALUES(?, ?, ?, ?, ?, ?, ?)`).run(observer, neighbor.id, signalAt, reason, action, to, now.toISOString());
|
|
72
|
+
if (inserted.changes) {
|
|
73
|
+
const incident: NeighborIncident = { observer, neighbor: neighbor.id, state: 'absent', reason, action, to,
|
|
74
|
+
at: now.toISOString(), lastSeenAt: signal?.at ?? null };
|
|
75
|
+
store.append({ from: observer, to, kind: `neighbor-${action}`,
|
|
76
|
+
body: `[${observer} → ${to}] ${neighbor.id} ${reason}: ${action}` });
|
|
77
|
+
incidents.push(incident);
|
|
78
|
+
}
|
|
79
|
+
store.db.exec('COMMIT');
|
|
80
|
+
} catch (error) {
|
|
81
|
+
try { store.db.exec('ROLLBACK'); } catch { /* no open transaction */ }
|
|
82
|
+
debug.log('loop.neighbors', 'check-failed', { observer, neighbor: neighbor.id, error: String(error) }, { level: 'warn' });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const incident of incidents) debug.log('loop.neighbors', 'absent', incident, { level: 'warn' });
|
|
86
|
+
return incidents;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -12,7 +12,7 @@ import { getUserConfig, ORCHESTRATOR_DEFAULTS, type OrchestratorSeat } from '../
|
|
|
12
12
|
import { checkAuthorInput } from './author-input-check.js';
|
|
13
13
|
import { AuthorLedger } from './author-ledger.js';
|
|
14
14
|
import { collectWork, findOverlaps, type WorkItem } from './overlap.js';
|
|
15
|
-
import { seatOfTree, trafficTick, TRAFFIC_SEATS } from './traffic.js';
|
|
15
|
+
import { isTrafficLaunch, seatOfTree, trafficTick, TRAFFIC_SEATS } from './traffic.js';
|
|
16
16
|
|
|
17
17
|
export type AuthorCell = Pick<ChecklistItem, 'id' | 'title' | 'owner' | 'status' | 'evidence'> & { version: string };
|
|
18
18
|
export type AuthorQueueRow = Pick<QueueItem, 'seat' | 'status'> & Partial<Pick<QueueItem, 'input' | 'kind'>>;
|
|
@@ -101,8 +101,12 @@ export interface CollectAuthorDepthDeps {
|
|
|
101
101
|
versions?: () => readonly [string, string];
|
|
102
102
|
ledger?: Pick<AuthorLedger, 'request'>;
|
|
103
103
|
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
104
|
+
listProcesses?: typeof defaultListHarnessProcesses;
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
/** Budget for the shadow's process read (it runs inside the queue lock, whose waiters give up after 30 s). */
|
|
108
|
+
export const AUTHOR_DEPTH_PROCESS_DEADLINE_MS = 10_000;
|
|
109
|
+
|
|
106
110
|
function defaultVersions(now: Date, root = releaseLedgerRoot()): readonly [string, string] {
|
|
107
111
|
if (!existsSync(join(root, 'release', 'features.sqlite'))) throw new Error('release schedule ledger unavailable');
|
|
108
112
|
const upcoming = listSchedules(root).filter(row => row.cutAt && Date.parse(row.cutAt) > now.getTime())
|
|
@@ -122,15 +126,6 @@ export function collectAuthorDepth(deps: CollectAuthorDepthDeps = {}): AuthorDep
|
|
|
122
126
|
};
|
|
123
127
|
const cfg = deps.caps && deps.running ? null : observe('config', () => getUserConfig().loops?.orchestrator ?? ORCHESTRATOR_DEFAULTS);
|
|
124
128
|
const caps = deps.caps ? observe('caps', deps.caps) : cfg === null ? null : observe('caps', () => cfg.seatCaps);
|
|
125
|
-
const running = deps.running ? observe('running', deps.running) : cfg === null ? null : observe('running', () => {
|
|
126
|
-
const observation = defaultListHarnessProcesses();
|
|
127
|
-
if (observation.status !== 'ok') throw new Error(`process observation ${observation.status}`);
|
|
128
|
-
const processes = observation.records.map(process => ({ ...process,
|
|
129
|
-
seat: seatOfTree(process.cwdStatus === 'unknown' ? undefined : process.cwd, cfg) }));
|
|
130
|
-
const measured = trafficTick({ processes, now, caps: cfg.seatCaps, openCells: [] });
|
|
131
|
-
if (measured.unassigned) throw new Error(`${measured.unassigned} unassigned harness launch process(es)`);
|
|
132
|
-
return Object.fromEntries(measured.seats.map(row => [row.seat, row.running]));
|
|
133
|
-
});
|
|
134
129
|
const queued = observe('queue', deps.queued ?? (() => listHarnessQueue({ root: deps.root })));
|
|
135
130
|
const releaseRoot = deps.root ?? releaseLedgerRoot();
|
|
136
131
|
const versions = observe('versions', deps.versions ?? (() => defaultVersions(now, releaseRoot)));
|
|
@@ -139,7 +134,30 @@ export function collectAuthorDepth(deps: CollectAuthorDepthDeps = {}): AuthorDep
|
|
|
139
134
|
throw new Error('checklist ledger unavailable');
|
|
140
135
|
return deps.cells?.(version) ?? listChecklist(version, deps.root).items.map(item => ({ ...item, version }));
|
|
141
136
|
}));
|
|
142
|
-
|
|
137
|
+
// Without release cells there is no candidate to compare, and the host reads below are the expensive ones (every launch's
|
|
138
|
+
// cwd · open PRs over the network) — a fresh --test universe has no release ledger, so it skips them (10-08 tick hang).
|
|
139
|
+
const noCells = cells === null && !deps.cells;
|
|
140
|
+
if (noCells && !deps.running && cfg !== null) unreadable.push({ source: 'running', reason: 'skipped: no release cells to compare' });
|
|
141
|
+
const running = deps.running ? observe('running', deps.running) : cfg === null || noCells ? null : observe('running', () => {
|
|
142
|
+
// Only launch processes are counted, so only they need a cwd; the whole read is bounded because the queue tick runs
|
|
143
|
+
// this shadow inside its lock (10-08: ~490 host processes × lsof kept a --test tick for minutes).
|
|
144
|
+
const started = Date.now();
|
|
145
|
+
const observation = (deps.listProcesses ?? defaultListHarnessProcesses)({ include: record => isTrafficLaunch(record.command),
|
|
146
|
+
cwdOnly: true, deadlineMs: AUTHOR_DEPTH_PROCESS_DEADLINE_MS });
|
|
147
|
+
if (observation.status !== 'ok') throw new Error(`process observation ${observation.status}`);
|
|
148
|
+
const late = observation.records.filter(process => process.cwdFailureReason?.startsWith('observation deadline')).length;
|
|
149
|
+
(deps.log ?? ((category: string, event: string, data: Record<string, unknown>) => debug.log(category, event, data)))(
|
|
150
|
+
'loop.orchestrator', 'author-depth-processes', { launches: observation.records.length, late, ms: Date.now() - started,
|
|
151
|
+
deadlineMs: AUTHOR_DEPTH_PROCESS_DEADLINE_MS });
|
|
152
|
+
if (late) throw new Error(`process observation deadline ${AUTHOR_DEPTH_PROCESS_DEADLINE_MS}ms: ${late} launch cwd(s) unread`);
|
|
153
|
+
const processes = observation.records.map(process => ({ ...process,
|
|
154
|
+
seat: seatOfTree(process.cwdStatus === 'unknown' ? undefined : process.cwd, cfg) }));
|
|
155
|
+
const measured = trafficTick({ processes, now, caps: cfg.seatCaps, openCells: [] });
|
|
156
|
+
if (measured.unassigned) throw new Error(`${measured.unassigned} unassigned harness launch process(es)`);
|
|
157
|
+
return Object.fromEntries(measured.seats.map(row => [row.seat, row.running]));
|
|
158
|
+
});
|
|
159
|
+
if (noCells && !deps.overlaps) unreadable.push({ source: 'overlap', reason: 'skipped: no release cells to compare' });
|
|
160
|
+
const overlaps = noCells && !deps.overlaps ? null : observe('overlap', deps.overlaps ?? (() => {
|
|
143
161
|
const observed = collectWork();
|
|
144
162
|
if (observed.unreadableTrees || observed.unreadableGoals || observed.work.some(item => item.unreadable))
|
|
145
163
|
throw new Error(`incomplete work observation: trees=${observed.unreadableTrees} goals=${observed.unreadableGoals}`);
|
|
@@ -19,6 +19,9 @@ export interface CollectWorkDeps {
|
|
|
19
19
|
config?: Pick<OrchestratorLoopConfig, 'seatTrees'>;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/** Budget for collectWork's per-process cwd reads. */
|
|
23
|
+
export const COLLECT_WORK_PROCESS_DEADLINE_MS = 10_000;
|
|
24
|
+
|
|
22
25
|
const repoRoot = resolve(import.meta.dir, '../../..');
|
|
23
26
|
const defaultRunGh = (args: string[]): string => execFileSync('bun', ['bin/elanous.mjs', '--test', 'gh', ...args], {
|
|
24
27
|
cwd: repoRoot, encoding: 'utf8', timeout: 60_000, maxBuffer: 20 * 1024 * 1024,
|
|
@@ -63,7 +66,10 @@ function goalFiles(text: string): string[] {
|
|
|
63
66
|
export function collectWork(deps: CollectWorkDeps = {}): CollectWorkResult {
|
|
64
67
|
const prs = JSON.parse((deps.runGh ?? defaultRunGh)(['pr', 'list', '--state', 'open', '--limit', '1000', '--json', 'number,title,body,headRefName,files,isDraft,labels'])) as OpenPr[];
|
|
65
68
|
if (!Array.isArray(prs)) throw new Error('PR observation is not a list');
|
|
66
|
-
|
|
69
|
+
// Only goal-carrying launches become work, so only they need a cwd; bounded so a host with hundreds of harness processes
|
|
70
|
+
// cannot hold the queue lock for minutes (10-08). Records past the budget stay cwd-unknown → their goal reads unreadable.
|
|
71
|
+
const observation = deps.listProcesses ? deps.listProcesses()
|
|
72
|
+
: defaultListHarnessProcesses({ include: record => goalPath(record.command) !== null, cwdOnly: true, deadlineMs: COLLECT_WORK_PROCESS_DEADLINE_MS });
|
|
67
73
|
if (observation.status !== 'ok') throw new Error(`process observation ${observation.status}`);
|
|
68
74
|
const cfg = deps.config ?? getUserConfig().loops?.orchestrator ?? ORCHESTRATOR_DEFAULTS;
|
|
69
75
|
const excluded = { stalled: 0, superseded: 0, draft: 0 };
|
|
@@ -171,7 +171,7 @@ export async function runResyncShadow(max = 3, deps: ResyncShadowDeps = {}): Pro
|
|
|
171
171
|
entry.resolvedFiles = outcome.resolvedFiles ?? [];
|
|
172
172
|
if (outcome.sizeCollapse?.length || outcome.providerFailure?.length || outcome.testDeclarationLoss?.length) {
|
|
173
173
|
entry.verdict = 'guard-tripped';
|
|
174
|
-
} else if (outcome.status === 'merged' || outcome.status === 'llm-resolved') {
|
|
174
|
+
} else if (outcome.status === 'merged' || outcome.status === 'llm-resolved' || outcome.status === 'deterministic-resolved') {
|
|
175
175
|
if (entry.testFiles.length === 0) entry.verdict = 'merge-ok-no-tests';
|
|
176
176
|
else {
|
|
177
177
|
const test = (deps.runTests ?? defaultRunTests)(tmp, entry.testFiles);
|
|
@@ -1,56 +1,13 @@
|
|
|
1
1
|
import type { ChecklistItem } from '../../release-loop/checklist.js';
|
|
2
2
|
import type { OrchestratorSeat } from '../../user-config.js';
|
|
3
|
+
import { calculateSeatBaseShares as budgetSeatBaseShares } from '../budget.js';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/** Allocate whole base slots by remaining work; this calculation does not decide launches. */
|
|
8
|
-
export function calculateSeatBaseShares({ totalSlots, currentRound, nextRound, seatCaps }: {
|
|
5
|
+
/** Existing orchestrator callers keep their signature; the resource loop owns the decision. */
|
|
6
|
+
export function calculateSeatBaseShares(input: {
|
|
9
7
|
totalSlots: number;
|
|
10
|
-
currentRound: readonly
|
|
11
|
-
nextRound: readonly
|
|
8
|
+
currentRound: readonly Pick<ChecklistItem, 'owner' | 'status'>[];
|
|
9
|
+
nextRound: readonly Pick<ChecklistItem, 'owner' | 'status'>[];
|
|
12
10
|
seatCaps: Readonly<Record<OrchestratorSeat, number>>;
|
|
13
11
|
}): Record<OrchestratorSeat, number> {
|
|
14
|
-
|
|
15
|
-
SEATS.some(seat => !Number.isSafeInteger(seatCaps[seat]) || seatCaps[seat] < 0)) {
|
|
16
|
-
throw new RangeError('totalSlots and seatCaps must be nonnegative safe integers');
|
|
17
|
-
}
|
|
18
|
-
const shares: Record<OrchestratorSeat, number> = { OP: 0, TC: 0, MK: 0, UX: 0 };
|
|
19
|
-
const weights: Record<OrchestratorSeat, number> = { OP: 0, TC: 0, MK: 0, UX: 0 };
|
|
20
|
-
for (const [cells, weight] of [[currentRound, 2], [nextRound, 1]] as const) {
|
|
21
|
-
for (const cell of cells) {
|
|
22
|
-
if (cell.status !== 'yellow') continue;
|
|
23
|
-
const seat = cell.owner?.split('/')[0];
|
|
24
|
-
if (SEATS.some(id => id === seat)) weights[seat as OrchestratorSeat] += weight;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let available = Math.min(totalSlots, SEATS.reduce((sum, seat) => sum + seatCaps[seat], 0));
|
|
29
|
-
let eligible = SEATS.filter(seat => weights[seat] > 0 && seatCaps[seat] > 0);
|
|
30
|
-
while (available > 0 && eligible.length > 0) {
|
|
31
|
-
const totalWeight = eligible.reduce((sum, seat) => sum + weights[seat], 0);
|
|
32
|
-
const ideal = (seat: OrchestratorSeat) => available * weights[seat] / totalWeight;
|
|
33
|
-
const capped = eligible.filter(seat => ideal(seat) >= seatCaps[seat]);
|
|
34
|
-
if (capped.length > 0) {
|
|
35
|
-
for (const seat of capped) {
|
|
36
|
-
shares[seat] = seatCaps[seat];
|
|
37
|
-
available -= seatCaps[seat];
|
|
38
|
-
}
|
|
39
|
-
eligible = eligible.filter(seat => !capped.includes(seat));
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
const remainders = eligible.map(seat => ({ seat, exact: ideal(seat) }));
|
|
43
|
-
for (const { seat, exact } of remainders) {
|
|
44
|
-
shares[seat] = Math.floor(exact);
|
|
45
|
-
available -= shares[seat];
|
|
46
|
-
}
|
|
47
|
-
remainders.sort((a, b) => (b.exact - Math.floor(b.exact)) - (a.exact - Math.floor(a.exact)));
|
|
48
|
-
for (const { seat } of remainders) {
|
|
49
|
-
if (available === 0) break;
|
|
50
|
-
shares[seat]++;
|
|
51
|
-
available--;
|
|
52
|
-
}
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
return shares;
|
|
12
|
+
return budgetSeatBaseShares(input);
|
|
56
13
|
}
|
|
@@ -8,6 +8,7 @@ import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
|
8
8
|
import { getUserConfig, type OrchestratorLoopConfig } from '../../user-config.js';
|
|
9
9
|
import { rubricPriority } from '../../release-loop/rubric.js';
|
|
10
10
|
import { debug } from '../../debug/log.js';
|
|
11
|
+
import { emitLoopHeartbeat } from '../neighbor-runtime.js';
|
|
11
12
|
import { listSelfDevRuns, runSummaryLine, selfDevRunsDir } from '../../self-dev/run-store.js';
|
|
12
13
|
import { planAction, runSeatLoopOnce, type SeatDeps, type SeatItem } from '../../seat-loop/seat-loop.js';
|
|
13
14
|
import { addHarnessQueue, harnessQueueOutcome, setQueuedPoolHint } from '../../harness/harness-queue.js';
|
|
@@ -48,7 +49,8 @@ export type PlacementInput = { id: string; title: string; owner: string; priorit
|
|
|
48
49
|
export type PlaceAdapter = (cell: PlacementInput, deps?: { dryRun?: boolean; now?: Date }) => Promise<{ version?: string } | null> | { version?: string } | null;
|
|
49
50
|
export type RebalanceAdapter = (version: string) => Promise<{ decisions: unknown[]; blocked: Array<{ id: string; reason: string }> }> | { decisions: unknown[]; blocked: Array<{ id: string; reason: string }> };
|
|
50
51
|
export interface TickDeps {
|
|
51
|
-
root?: string; now?: Date; mode?: Mode; runId?: string; window?: Window;
|
|
52
|
+
root?: string; now?: Date | (() => Date); mode?: Mode; runId?: string; window?: Window;
|
|
53
|
+
heartbeatIntervalMs?: number;
|
|
52
54
|
cards?: () => TaskCard[]; split?: SplitAdapter; placeCell?: PlaceAdapter;
|
|
53
55
|
degradation?: () => { llm: boolean; board: boolean };
|
|
54
56
|
gridHosts?: readonly GridHost[];
|
|
@@ -101,6 +103,19 @@ export function kstWindow(now: Date): Window {
|
|
|
101
103
|
const hour = Number(new Intl.DateTimeFormat('en-US', { timeZone: 'Asia/Seoul', hour: '2-digit', hourCycle: 'h23' }).format(now));
|
|
102
104
|
return hour < 12 ? '08' : hour < 18 ? '12' : '18';
|
|
103
105
|
}
|
|
106
|
+
/** KST schedule of graphs/orchestrator/orchestrator.yaml (`0 8,12,18 * * *`). */
|
|
107
|
+
const ORCHESTRATOR_HOURS_KST = [8, 12, 18] as const;
|
|
108
|
+
/** Grace past the next scheduled window before neighbors may call the orchestrator absent. */
|
|
109
|
+
export const ORCHESTRATOR_LEASE_GRACE_MS = 60 * 60_000;
|
|
110
|
+
/** The next scheduled window strictly after `now` — the orchestrator is idle, not absent, until then. */
|
|
111
|
+
export function nextOrchestratorWindowAt(now: Date): Date {
|
|
112
|
+
const kst = new Date(now.getTime() + 9 * 3_600_000);
|
|
113
|
+
for (let day = 0; day < 2; day++) for (const hour of ORCHESTRATOR_HOURS_KST) {
|
|
114
|
+
const at = Date.UTC(kst.getUTCFullYear(), kst.getUTCMonth(), kst.getUTCDate() + day, hour) - 9 * 3_600_000;
|
|
115
|
+
if (at > now.getTime()) return new Date(at);
|
|
116
|
+
}
|
|
117
|
+
throw new Error('unreachable: no orchestrator window within two days');
|
|
118
|
+
}
|
|
104
119
|
function kstDay(now: Date): string {
|
|
105
120
|
return new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit' }).format(now);
|
|
106
121
|
}
|
|
@@ -371,7 +386,8 @@ async function optionalAdapter<T>(path: string, name: string): Promise<T | undef
|
|
|
371
386
|
|
|
372
387
|
/** A graph command invokes one node; only the per-run file carries data across processes. */
|
|
373
388
|
export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Promise<TickState> {
|
|
374
|
-
const
|
|
389
|
+
const currentTime = () => typeof deps.now === 'function' ? deps.now() : deps.now ?? new Date();
|
|
390
|
+
const now = currentTime();
|
|
375
391
|
const window = deps.window ?? kstWindow(now);
|
|
376
392
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
377
393
|
const runId = deps.runId ?? graphRunId() ?? `${kstDay(now)}-${window}`;
|
|
@@ -379,6 +395,16 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
379
395
|
const orchestratorConfig = getUserConfig().loops?.orchestrator;
|
|
380
396
|
const configured = deps.mode ?? orchestratorConfig?.mode;
|
|
381
397
|
const mode: Mode = configured === 'off' || configured === 'live' ? configured : 'shadow';
|
|
398
|
+
const pulse = (health: 'healthy' | 'degraded', at: Date): void => {
|
|
399
|
+
try { emitLoopHeartbeat(root, 'orchestrator', health, at,
|
|
400
|
+
new Date(nextOrchestratorWindowAt(at).getTime() + ORCHESTRATOR_LEASE_GRACE_MS).toISOString()); }
|
|
401
|
+
catch (error) { debug.log('loop.orchestrator', 'heartbeat-failed', { error: String(error).slice(0, 200) }, { level: 'warn' }); }
|
|
402
|
+
};
|
|
403
|
+
if (mode !== 'off') pulse('healthy', now);
|
|
404
|
+
const heartbeatTimer = mode === 'off' ? undefined : setInterval(() => pulse('healthy', currentTime()), deps.heartbeatIntervalMs ?? 60 * 60_000);
|
|
405
|
+
heartbeatTimer?.unref();
|
|
406
|
+
let nodeError: unknown;
|
|
407
|
+
try {
|
|
382
408
|
const state: TickState = existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) as TickState : initial(runId, window, mode, kstDay(now));
|
|
383
409
|
state.steps ??= [];
|
|
384
410
|
if (state.window !== window || state.mode !== mode) throw new Error('orchestrator run window/mode changed');
|
|
@@ -739,6 +765,13 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
739
765
|
mkdirSync(dirname(path), { recursive: true });
|
|
740
766
|
writeFileSync(path, JSON.stringify(state));
|
|
741
767
|
return state;
|
|
768
|
+
} catch (error) {
|
|
769
|
+
nodeError = error;
|
|
770
|
+
throw error;
|
|
771
|
+
} finally {
|
|
772
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
773
|
+
if (mode !== 'off') pulse(nodeError === undefined ? 'healthy' : 'degraded', currentTime());
|
|
774
|
+
}
|
|
742
775
|
}
|
|
743
776
|
|
|
744
777
|
/** COORD-HA — the production supply of `TickDeps.degradation` (config `loops.orchestrator.degradationSignal`). Shadow (the
|
|
@@ -9,7 +9,7 @@ import { withFileLockSync } from '../../storage/file-lock.js';
|
|
|
9
9
|
import { getUserConfig, ORCHESTRATOR_DEFAULTS, type OrchestratorLoopConfig, type OrchestratorSeat } from '../../user-config.js';
|
|
10
10
|
import { finishAdvice, measureFinish, type FinishAdvice, type FinishMetrics, type MeasureFinishDeps } from './finish-rate.js';
|
|
11
11
|
import { collectAuthorDepth, type AuthorDepthResult, type CollectAuthorDepthDeps } from './author-depth.js';
|
|
12
|
-
import {
|
|
12
|
+
import { budgetTrafficTick } from '../budget.js';
|
|
13
13
|
|
|
14
14
|
export const TRAFFIC_SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
|
|
15
15
|
export type TrafficCell = Pick<ChecklistItem, 'id' | 'title' | 'owner' | 'status'>;
|
|
@@ -49,86 +49,12 @@ function realOrResolved(path: string): string {
|
|
|
49
49
|
try { return realpathSync(path); } catch { return resolve(path); }
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
/** A `harness ask|say` launch command line — the only processes `trafficTick` counts. */
|
|
53
|
+
export { isTrafficLaunch } from '../budget.js';
|
|
53
54
|
|
|
54
|
-
/** Process
|
|
55
|
-
export function trafficTick(
|
|
56
|
-
|
|
57
|
-
now: Date;
|
|
58
|
-
caps: Readonly<Record<OrchestratorSeat, number>>;
|
|
59
|
-
lastLaunchAt?: Partial<Record<OrchestratorSeat, Date | null>>;
|
|
60
|
-
idleSince?: Partial<Record<OrchestratorSeat, Date | null>>;
|
|
61
|
-
openCells: readonly TrafficCell[];
|
|
62
|
-
nextRound?: readonly TrafficCell[];
|
|
63
|
-
totalSlots?: number;
|
|
64
|
-
idleMinutes?: number;
|
|
65
|
-
}): TrafficResult {
|
|
66
|
-
const launches = processes.filter(process => IS_LAUNCH.test(process.command));
|
|
67
|
-
const slotBudget = totalSlots ?? TRAFFIC_SEATS.reduce((sum, seat) => sum + caps[seat], 0);
|
|
68
|
-
// A missing next round is unknown, not an empty round with zero remaining work.
|
|
69
|
-
const baseShares = nextRound === undefined ? null : calculateSeatBaseShares({
|
|
70
|
-
totalSlots: slotBudget, currentRound: openCells, nextRound, seatCaps: caps,
|
|
71
|
-
});
|
|
72
|
-
const seats = TRAFFIC_SEATS.map((seat): TrafficSeatRow => {
|
|
73
|
-
const owned = launches.filter(process => process.seat === seat);
|
|
74
|
-
const latest = owned.reduce<number | null>((current, process) => {
|
|
75
|
-
if (!Number.isFinite(process.elapsedSeconds) || process.elapsedSeconds < 0) return current;
|
|
76
|
-
const start = now.getTime() - process.elapsedSeconds * 1000;
|
|
77
|
-
return current === null ? start : Math.max(current, start);
|
|
78
|
-
}, null);
|
|
79
|
-
const fallback = owned.length ? lastLaunchAt?.[seat]?.getTime() : undefined;
|
|
80
|
-
const idleStart = idleSince?.[seat]?.getTime();
|
|
81
|
-
const last = idleStart !== undefined && Number.isFinite(idleStart) ? idleStart
|
|
82
|
-
: latest ?? (fallback !== undefined && Number.isFinite(fallback) ? fallback : null);
|
|
83
|
-
const idleFor = last === null ? null : Math.max(0, Math.floor((now.getTime() - last) / 60_000));
|
|
84
|
-
const nextCell = openCells.find(cell => cell.status !== 'done' && cell.owner?.split('/')[0] === seat) ?? null;
|
|
85
|
-
const baseShare = baseShares?.[seat] ?? null;
|
|
86
|
-
return { seat, running: owned.length, cap: caps[seat], baseShare, borrowed: 0, lent: 0,
|
|
87
|
-
launchCap: baseShare === null ? caps[seat] : Math.max(owned.length, baseShare), idleFor,
|
|
88
|
-
idle: owned.length < caps[seat] && last !== null && now.getTime() - last > idleMinutes * 60_000, nextCell };
|
|
89
|
-
});
|
|
90
|
-
if (baseShares !== null) {
|
|
91
|
-
// Account for outstanding loans before lending new capacity. A returning
|
|
92
|
-
// lender cannot launch into a slot still occupied by the borrower's run.
|
|
93
|
-
for (const borrower of seats) {
|
|
94
|
-
let excess = Math.max(0, borrower.running - borrower.baseShare!);
|
|
95
|
-
borrower.borrowed += excess;
|
|
96
|
-
for (const lender of seats) {
|
|
97
|
-
if (!excess) break;
|
|
98
|
-
if (lender === borrower) continue;
|
|
99
|
-
const amount = Math.min(excess, Math.max(0, lender.baseShare! - lender.running - lender.lent));
|
|
100
|
-
lender.lent += amount;
|
|
101
|
-
excess -= amount;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
for (const lender of seats) {
|
|
105
|
-
if (lender.nextCell) continue;
|
|
106
|
-
let available = Math.max(0, lender.baseShare! - lender.running - lender.lent);
|
|
107
|
-
for (const borrower of seats) {
|
|
108
|
-
if (!available) break;
|
|
109
|
-
if (borrower === lender || !borrower.nextCell) continue;
|
|
110
|
-
const needed = Math.max(0, Math.min(borrower.cap, borrower.running + 1) - borrower.launchCap);
|
|
111
|
-
const amount = Math.min(available, needed);
|
|
112
|
-
lender.lent += amount;
|
|
113
|
-
borrower.borrowed += amount;
|
|
114
|
-
borrower.launchCap += amount;
|
|
115
|
-
available -= amount;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
for (const lender of seats) lender.launchCap = Math.max(lender.running, lender.launchCap - lender.lent);
|
|
119
|
-
}
|
|
120
|
-
// Reserve one available global slot per possible request, including unknown-seat
|
|
121
|
-
// launches in the occupied count; returning a lent share does not end its old run.
|
|
122
|
-
let freeSlots = Math.max(0, slotBudget - launches.length);
|
|
123
|
-
for (const row of seats) {
|
|
124
|
-
if (row.nextCell && row.idle && row.running < row.launchCap) {
|
|
125
|
-
if (freeSlots > 0) freeSlots--;
|
|
126
|
-
else row.launchCap = row.running;
|
|
127
|
-
}
|
|
128
|
-
row.idle = row.idle && (row.nextCell === null || row.running < row.launchCap);
|
|
129
|
-
if (!row.idle) row.nextCell = null;
|
|
130
|
-
}
|
|
131
|
-
return { seats, unassigned: launches.filter(process => process.seat === null).length, now, idleMinutes };
|
|
55
|
+
/** Process observation stays at the boundary; the shared resource loop owns the decision. */
|
|
56
|
+
export function trafficTick(input: Parameters<typeof budgetTrafficTick>[0]): TrafficResult {
|
|
57
|
+
return budgetTrafficTick(input);
|
|
132
58
|
}
|
|
133
59
|
|
|
134
60
|
export function withFinishAdvice(result: TrafficResult, deps: MeasureFinishDeps = {}): TrafficResult {
|
package/src/loops/registry.ts
CHANGED
|
@@ -685,6 +685,11 @@ export function unregisteredCronLoops(entries: AllLoopEntry[], opts: LoopRegistr
|
|
|
685
685
|
return warnings;
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
+
/** Newest-first run history of one loop without re-listing the registry (health reads a longer window than `loop status`). */
|
|
689
|
+
export function loopRecentRuns(id: string, opts: LoopRegistryOptions = {}, count = 5): LoopRun[] {
|
|
690
|
+
return recentRuns(id, opts.stateRoot ?? effectiveInstanceRoot(), count);
|
|
691
|
+
}
|
|
692
|
+
|
|
688
693
|
export function loopStatus(id: string, opts: LoopRegistryOptions = {}): LoopEntry & { recentRuns: LoopRun[] } {
|
|
689
694
|
const loop = listLoops(opts).find(entry => entry.id === id);
|
|
690
695
|
if (!loop) throw new Error(`loop not found: ${id}`);
|