elanous 0.2.22 → 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-80ed511e02c81412.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/{aBi6EyMtLsrl7J8eH4kF7 → 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/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/package.json +4 -1
- package/scripts/bench-report.ts +82 -3
- package/scripts/ci-typecheck-changed.ts +10 -1
- package/scripts/hq/hq-fence-wrapper.sh +148 -0
- package/scripts/lib/typecheck-ratchet.ts +51 -0
- 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/rhythm/daily.ts +51 -2
- package/scripts/rhythm/weekly.ts +3 -2
- package/scripts/tsc-main-safety-net.ts +189 -0
- package/scripts/usage-rollup.ts +8 -4
- package/src/acp/codex-plugins.ts +48 -6
- package/src/agent/monad-agent-turn.ts +67 -5
- package/src/agent/run-context.ts +10 -0
- package/src/agent/tool-profile.ts +28 -2
- package/src/agent-mission/acp-judge.ts +8 -2
- package/src/agent-mission/capabilities-cli.ts +16 -1
- package/src/agent-mission/driver.ts +1 -1
- package/src/agent-mission/pr-review-watch.ts +6 -2
- package/src/agent-mission/review-loop.ts +33 -5
- package/src/agent-substrate/acp-reviewer.ts +17 -8
- package/src/agent-substrate/pr-reviewer.ts +164 -28
- package/src/agent-substrate/review-intent.ts +6 -4
- 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 +216 -12
- 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 +12 -3
- package/src/boot/acp-server.ts +4 -7
- package/src/boot/acp-socket-path.ts +7 -0
- package/src/briefing/brief-items.ts +21 -13
- package/src/chat/index.ts +12 -3
- package/src/cli/agent-cli.ts +20 -0
- package/src/cli/brief-cli.ts +27 -21
- package/src/cli/cli-user-error.ts +9 -2
- package/src/cli/log-event-names.ts +3 -0
- package/src/cli/logs-cli.ts +1 -0
- 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 +30 -2
- package/src/cli/pr-land-overlap-evidence.ts +131 -0
- package/src/cli/release-cli.ts +53 -11
- package/src/cli/self-update.ts +173 -10
- package/src/cli/skills-cli.ts +12 -3
- package/src/cli/tasks-cli.ts +133 -27
- package/src/context-bus/context-first.ts +51 -2
- package/src/context-bus/context-now-surfaces.ts +13 -2
- package/src/context-bus/long-term-memory.ts +3 -1
- package/src/context-bus/nightly-condense.ts +3 -2
- package/src/core-turn/run-goal-loop.ts +86 -2
- 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 +5 -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/display/coordinator.ts +25 -2
- package/src/domains/coo-admin-tool.ts +27 -0
- package/src/domains/cron-match.ts +1 -0
- package/src/domains/outbound-alert.ts +17 -6
- 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/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-runner/runner.ts +54 -5
- package/src/harness/dispatch-backpressure.ts +259 -0
- package/src/harness/harness-cli-command.ts +107 -28
- package/src/harness/harness-seams.ts +2 -20
- package/src/harness/harness-substrate-default.ts +72 -1
- 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 +163 -64
- 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/kgs/pack.ts +5 -2
- package/src/knowledge/kgs/sqlite-store.ts +15 -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 +306 -35
- package/src/loops/budget.ts +150 -0
- package/src/loops/orchestrator/seat-share.ts +6 -49
- package/src/loops/orchestrator/traffic.ts +5 -81
- package/src/market/signed-index.ts +42 -0
- 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/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/error-reports.ts +30 -8
- package/src/nexus/api/http-server.ts +8 -0
- package/src/nexus/api/loop-resources.ts +42 -0
- package/src/nexus/api/outbound-report.ts +8 -3
- package/src/nexus/api/telegram-trigger-bot.ts +6 -3
- package/src/nexus/index.ts +10 -0
- package/src/nexus/outbound/format.ts +2 -0
- package/src/nexus/outbound/router.ts +198 -13
- package/src/oauth/codex-account-rotation.ts +16 -18
- package/src/oauth/codex-account-store.ts +134 -5
- package/src/oauth/fallback-chain.ts +28 -6
- 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/plugin-maker.ts +25 -20
- package/src/plugins/maker/wizard-generate.ts +16 -2
- package/src/policy/llm-policy.ts +14 -1
- 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.ts +24 -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-loop/seat-loop.ts +34 -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 +67 -3
- package/src/self-dev/dev-pipeline.ts +4 -6
- package/src/self-dev/draft-residue.ts +463 -0
- package/src/self-dev/draft-sweep.ts +151 -10
- 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 +8 -135
- package/src/self-dev/review-provider-fallback.ts +115 -19
- package/src/self-dev/run-store.ts +1 -3
- package/src/self-dev/run-supervisor.ts +3 -22
- 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/headless-elanous-driver.ts +11 -9
- package/src/self-implement/host-regate.ts +120 -29
- package/src/self-implement/l8-merge-queue.ts +67 -5
- 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 +133 -16
- 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 +36 -3
- package/src/self-implement/self-implement-cli.ts +3 -1
- 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/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 +4 -2
- package/src/storage/file-lock.ts +8 -1
- package/src/task-agent/actions.ts +4 -0
- package/src/task-agent/card-evidence.ts +59 -13
- 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 +106 -19
- 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 +103 -7
- package/src/task-agent/task-hand.ts +136 -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 +66 -14
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +259 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +6 -2
- 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/user-config.ts +132 -24
- 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/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-08b716476f1d50af.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-d238d3e2eb5b2bf5.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-5985236d9307f676.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/5633-d6852385000738ef.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-5b9e8a45a5b0c823.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-aa1cc4ac90af4d40.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-55bcfbb0e8057f59.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/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-88459e0903da2803.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d85c43f46f58716b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4e645600579ae952.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-87edbfd19117823b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-cbaf66b1e1804adf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-9f5610caaa06178c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-ec8f3575e339de74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-2a19c5cd3b562580.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-b6a09cde2e07521a.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-f7eae1518ca0c353.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-786fb4504d0f2fa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-7ec8e289b4c14df6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-8c8ac17d52be2a79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d359ab048d63a9d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-f151cc10ad026940.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.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-9789328af9a9aa62.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-bf495a1e284f4e00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-69cb93d1a2e57e0e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72ec543928d67c3e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.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/f2bd5257e6af88d8.css +0 -1
- /package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → 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
|
+
}
|
|
@@ -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
|
}
|
|
@@ -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,88 +49,12 @@ function realOrResolved(path: string): string {
|
|
|
49
49
|
try { return realpathSync(path); } catch { return resolve(path); }
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const IS_LAUNCH = /(?:^|\s)(?:\S*\/)?elanous\.mjs\s+(?:(?:--test|--config-dir\s+\S+)\s+)?harness\s+(?:ask|say)(?=\s|$)/;
|
|
53
52
|
/** A `harness ask|say` launch command line — the only processes `trafficTick` counts. */
|
|
54
|
-
export
|
|
53
|
+
export { isTrafficLaunch } from '../budget.js';
|
|
55
54
|
|
|
56
|
-
/** Process
|
|
57
|
-
export function trafficTick(
|
|
58
|
-
|
|
59
|
-
now: Date;
|
|
60
|
-
caps: Readonly<Record<OrchestratorSeat, number>>;
|
|
61
|
-
lastLaunchAt?: Partial<Record<OrchestratorSeat, Date | null>>;
|
|
62
|
-
idleSince?: Partial<Record<OrchestratorSeat, Date | null>>;
|
|
63
|
-
openCells: readonly TrafficCell[];
|
|
64
|
-
nextRound?: readonly TrafficCell[];
|
|
65
|
-
totalSlots?: number;
|
|
66
|
-
idleMinutes?: number;
|
|
67
|
-
}): TrafficResult {
|
|
68
|
-
const launches = processes.filter(process => IS_LAUNCH.test(process.command));
|
|
69
|
-
const slotBudget = totalSlots ?? TRAFFIC_SEATS.reduce((sum, seat) => sum + caps[seat], 0);
|
|
70
|
-
// A missing next round is unknown, not an empty round with zero remaining work.
|
|
71
|
-
const baseShares = nextRound === undefined ? null : calculateSeatBaseShares({
|
|
72
|
-
totalSlots: slotBudget, currentRound: openCells, nextRound, seatCaps: caps,
|
|
73
|
-
});
|
|
74
|
-
const seats = TRAFFIC_SEATS.map((seat): TrafficSeatRow => {
|
|
75
|
-
const owned = launches.filter(process => process.seat === seat);
|
|
76
|
-
const latest = owned.reduce<number | null>((current, process) => {
|
|
77
|
-
if (!Number.isFinite(process.elapsedSeconds) || process.elapsedSeconds < 0) return current;
|
|
78
|
-
const start = now.getTime() - process.elapsedSeconds * 1000;
|
|
79
|
-
return current === null ? start : Math.max(current, start);
|
|
80
|
-
}, null);
|
|
81
|
-
const fallback = owned.length ? lastLaunchAt?.[seat]?.getTime() : undefined;
|
|
82
|
-
const idleStart = idleSince?.[seat]?.getTime();
|
|
83
|
-
const last = idleStart !== undefined && Number.isFinite(idleStart) ? idleStart
|
|
84
|
-
: latest ?? (fallback !== undefined && Number.isFinite(fallback) ? fallback : null);
|
|
85
|
-
const idleFor = last === null ? null : Math.max(0, Math.floor((now.getTime() - last) / 60_000));
|
|
86
|
-
const nextCell = openCells.find(cell => cell.status !== 'done' && cell.owner?.split('/')[0] === seat) ?? null;
|
|
87
|
-
const baseShare = baseShares?.[seat] ?? null;
|
|
88
|
-
return { seat, running: owned.length, cap: caps[seat], baseShare, borrowed: 0, lent: 0,
|
|
89
|
-
launchCap: baseShare === null ? caps[seat] : Math.max(owned.length, baseShare), idleFor,
|
|
90
|
-
idle: owned.length < caps[seat] && last !== null && now.getTime() - last > idleMinutes * 60_000, nextCell };
|
|
91
|
-
});
|
|
92
|
-
if (baseShares !== null) {
|
|
93
|
-
// Account for outstanding loans before lending new capacity. A returning
|
|
94
|
-
// lender cannot launch into a slot still occupied by the borrower's run.
|
|
95
|
-
for (const borrower of seats) {
|
|
96
|
-
let excess = Math.max(0, borrower.running - borrower.baseShare!);
|
|
97
|
-
borrower.borrowed += excess;
|
|
98
|
-
for (const lender of seats) {
|
|
99
|
-
if (!excess) break;
|
|
100
|
-
if (lender === borrower) continue;
|
|
101
|
-
const amount = Math.min(excess, Math.max(0, lender.baseShare! - lender.running - lender.lent));
|
|
102
|
-
lender.lent += amount;
|
|
103
|
-
excess -= amount;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
for (const lender of seats) {
|
|
107
|
-
if (lender.nextCell) continue;
|
|
108
|
-
let available = Math.max(0, lender.baseShare! - lender.running - lender.lent);
|
|
109
|
-
for (const borrower of seats) {
|
|
110
|
-
if (!available) break;
|
|
111
|
-
if (borrower === lender || !borrower.nextCell) continue;
|
|
112
|
-
const needed = Math.max(0, Math.min(borrower.cap, borrower.running + 1) - borrower.launchCap);
|
|
113
|
-
const amount = Math.min(available, needed);
|
|
114
|
-
lender.lent += amount;
|
|
115
|
-
borrower.borrowed += amount;
|
|
116
|
-
borrower.launchCap += amount;
|
|
117
|
-
available -= amount;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
for (const lender of seats) lender.launchCap = Math.max(lender.running, lender.launchCap - lender.lent);
|
|
121
|
-
}
|
|
122
|
-
// Reserve one available global slot per possible request, including unknown-seat
|
|
123
|
-
// launches in the occupied count; returning a lent share does not end its old run.
|
|
124
|
-
let freeSlots = Math.max(0, slotBudget - launches.length);
|
|
125
|
-
for (const row of seats) {
|
|
126
|
-
if (row.nextCell && row.idle && row.running < row.launchCap) {
|
|
127
|
-
if (freeSlots > 0) freeSlots--;
|
|
128
|
-
else row.launchCap = row.running;
|
|
129
|
-
}
|
|
130
|
-
row.idle = row.idle && (row.nextCell === null || row.running < row.launchCap);
|
|
131
|
-
if (!row.idle) row.nextCell = null;
|
|
132
|
-
}
|
|
133
|
-
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);
|
|
134
58
|
}
|
|
135
59
|
|
|
136
60
|
export function withFinishAdvice(result: TrafficResult, deps: MeasureFinishDeps = {}): TrafficResult {
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import { createHash, createPublicKey, generateKeyPairSync, sign, verify } from 'node:crypto';
|
|
2
2
|
|
|
3
|
+
export interface MarketplaceArtifact {
|
|
4
|
+
sha256: string;
|
|
5
|
+
bytes: number;
|
|
6
|
+
key: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface MarketplaceScopedPack {
|
|
10
|
+
name: string;
|
|
11
|
+
version: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
artifact: MarketplaceArtifact;
|
|
14
|
+
visibility: 'public' | 'internal';
|
|
15
|
+
enterpriseId?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MarketplaceLoopBundle extends MarketplaceScopedPack {
|
|
19
|
+
graphs?: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
3
22
|
export interface MarketplaceIndex {
|
|
4
23
|
name: string;
|
|
5
24
|
interface: { displayName: string };
|
|
6
25
|
sequence: number;
|
|
7
26
|
generatedAt?: string;
|
|
27
|
+
knowledgePacks?: MarketplaceScopedPack[];
|
|
28
|
+
loopBundles?: MarketplaceLoopBundle[];
|
|
8
29
|
plugins: Array<{
|
|
9
30
|
name: string;
|
|
10
31
|
source: { source: string; [key: string]: unknown };
|
|
@@ -57,6 +78,27 @@ function invalidIndex(value: unknown): string | null {
|
|
|
57
78
|
if (value.generatedAt !== undefined && !string(value.generatedAt)) return 'generatedAt: expected string';
|
|
58
79
|
if (!Array.isArray(value.plugins)) return 'plugins: expected array';
|
|
59
80
|
|
|
81
|
+
for (const collection of ['knowledgePacks', 'loopBundles'] as const) {
|
|
82
|
+
const entries = value[collection];
|
|
83
|
+
if (entries === undefined) continue;
|
|
84
|
+
if (!Array.isArray(entries)) return `${collection}: expected array`;
|
|
85
|
+
for (const [i, entry] of entries.entries()) {
|
|
86
|
+
const field = (name: string) => `${collection}[${i}] (${record(entry) && string(entry.name) ? entry.name : 'unnamed'}).${name}`;
|
|
87
|
+
if (!record(entry)) return field('expected object');
|
|
88
|
+
if (!string(entry.name) || !entry.name.trim()) return field('name: expected non-empty string');
|
|
89
|
+
if (!string(entry.version) || !entry.version.trim()) return field('version: expected non-empty string');
|
|
90
|
+
if (entry.description !== undefined && !string(entry.description)) return field('description: expected string');
|
|
91
|
+
if (entry.visibility !== 'public' && entry.visibility !== 'internal') return field('visibility: expected public or internal');
|
|
92
|
+
if (entry.visibility === 'internal' && (!string(entry.enterpriseId) || !entry.enterpriseId.trim())) return field('enterpriseId: required for internal entry');
|
|
93
|
+
if (entry.visibility === 'public' && entry.enterpriseId !== undefined) return field('enterpriseId: not allowed for public entry');
|
|
94
|
+
if (!record(entry.artifact)) return field('artifact: expected object');
|
|
95
|
+
if (!string(entry.artifact.sha256) || !/^[a-fA-F0-9]{64}$/.test(entry.artifact.sha256)) return field('artifact.sha256: expected 64 hex characters');
|
|
96
|
+
if (!Number.isSafeInteger(entry.artifact.bytes) || (entry.artifact.bytes as number) < 0) return field('artifact.bytes: expected non-negative integer');
|
|
97
|
+
if (!string(entry.artifact.key) || !entry.artifact.key.trim()) return field('artifact.key: expected non-empty string');
|
|
98
|
+
if (collection === 'loopBundles' && entry.graphs !== undefined && !stringArray(entry.graphs)) return field('graphs: expected string array');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
60
102
|
for (const [i, plugin] of value.plugins.entries()) {
|
|
61
103
|
const field = (name: string) => `plugins[${i}] (${record(plugin) && string(plugin.name) ? plugin.name : 'unnamed'}).${name}`;
|
|
62
104
|
if (!record(plugin)) return field('expected object');
|
package/src/mcp/mcp-oauth.ts
CHANGED
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
// 다시 실어 보내는 «잇는» 모듈. 주소를 코드에 박지 않는다 — 호스트는
|
|
6
6
|
// 전부 그 401 안내에서 그때 얻는다.
|
|
7
7
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// 경계: 장치 코드 흐름은 다루지 않는다.
|
|
8
|
+
// 브라우저 승인은 호출자(aside/CLI)의 책임이다. 브라우저를 쓸 수 없고
|
|
9
|
+
// 인가 서버가 RFC 8628 을 광고할 때는 장치 코드로 자격을 얻는다.
|
|
11
10
|
// 경계: 새 CLI 하위 명령은 더하지 않는다.
|
|
12
11
|
|
|
13
12
|
import { join } from 'node:path';
|
|
@@ -36,6 +35,7 @@ export type McpOAuthErrorCode =
|
|
|
36
35
|
| 'identity-mismatch'
|
|
37
36
|
| 's256-unsupported'
|
|
38
37
|
| 'registration'
|
|
38
|
+
| 'device'
|
|
39
39
|
| 'state-mismatch'
|
|
40
40
|
| 'token'
|
|
41
41
|
| 'refresh';
|
|
@@ -70,9 +70,10 @@ export interface ProtectedResourceMetadata {
|
|
|
70
70
|
|
|
71
71
|
export interface AuthorizationServerMetadata {
|
|
72
72
|
issuer: string;
|
|
73
|
-
authorizationEndpoint
|
|
73
|
+
authorizationEndpoint?: string;
|
|
74
74
|
tokenEndpoint: string;
|
|
75
75
|
registrationEndpoint?: string;
|
|
76
|
+
deviceAuthorizationEndpoint?: string;
|
|
76
77
|
codeChallengeMethodsSupported: string[];
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -81,6 +82,9 @@ export interface ClientRegistration {
|
|
|
81
82
|
clientSecret?: string;
|
|
82
83
|
/** 동적 등록 때 쓴 redirect_uri. 옛 등록에는 없다. */
|
|
83
84
|
redirectUri?: string;
|
|
85
|
+
/** Registered now but not stored yet: the store still holds another client's tokens, so this registration is
|
|
86
|
+
* committed only with its own tokens (exchangeAuthorizationCode). A cancelled login leaves the store as it was. */
|
|
87
|
+
pending?: boolean;
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
export interface AuthorizationRequest {
|
|
@@ -90,6 +94,8 @@ export interface AuthorizationRequest {
|
|
|
90
94
|
redirectUri: string;
|
|
91
95
|
issuer: string;
|
|
92
96
|
clientId: string;
|
|
97
|
+
/** Set when the client registration is pending (see ClientRegistration.pending); in memory only. */
|
|
98
|
+
pendingRegistration?: { clientSecret?: string };
|
|
93
99
|
scope?: string;
|
|
94
100
|
resource?: string;
|
|
95
101
|
}
|
|
@@ -364,14 +370,14 @@ export async function discoverAuthorizationServer(
|
|
|
364
370
|
const issuer = stringField(body, 'issuer');
|
|
365
371
|
const authorizationEndpoint = stringField(body, 'authorization_endpoint');
|
|
366
372
|
const tokenEndpoint = stringField(body, 'token_endpoint');
|
|
367
|
-
if (!issuer || !
|
|
373
|
+
if (!issuer || !tokenEndpoint || (!authorizationEndpoint && !stringField(body, 'device_authorization_endpoint'))) {
|
|
368
374
|
throw new McpOAuthError(
|
|
369
375
|
'discovery',
|
|
370
376
|
'authorization server metadata is missing issuer, authorization_endpoint, or token_endpoint',
|
|
371
377
|
);
|
|
372
378
|
}
|
|
373
379
|
requireHttpUrl(issuer, 'issuer');
|
|
374
|
-
requireHttpUrl(authorizationEndpoint, 'authorization_endpoint');
|
|
380
|
+
if (authorizationEndpoint) requireHttpUrl(authorizationEndpoint, 'authorization_endpoint');
|
|
375
381
|
requireHttpUrl(tokenEndpoint, 'token_endpoint');
|
|
376
382
|
// ⛔⭐⭐ RFC 8414 §3.3 — 돌려받은 `issuer` 는 «우리가 물어본» 인가서버
|
|
377
383
|
// 식별자와 «같아야» 한다. 안 대조하면 metadata 를 쥔 쪽이 남의 issuer 를
|
|
@@ -385,11 +391,14 @@ export async function discoverAuthorizationServer(
|
|
|
385
391
|
}
|
|
386
392
|
const registrationEndpoint = stringField(body, 'registration_endpoint');
|
|
387
393
|
if (registrationEndpoint) requireHttpUrl(registrationEndpoint, 'registration_endpoint');
|
|
394
|
+
const deviceAuthorizationEndpoint = stringField(body, 'device_authorization_endpoint');
|
|
395
|
+
if (deviceAuthorizationEndpoint) requireHttpUrl(deviceAuthorizationEndpoint, 'device_authorization_endpoint');
|
|
388
396
|
return {
|
|
389
397
|
issuer,
|
|
390
|
-
authorizationEndpoint,
|
|
398
|
+
...(authorizationEndpoint ? { authorizationEndpoint } : {}),
|
|
391
399
|
tokenEndpoint,
|
|
392
400
|
...(registrationEndpoint ? { registrationEndpoint } : {}),
|
|
401
|
+
...(deviceAuthorizationEndpoint ? { deviceAuthorizationEndpoint } : {}),
|
|
393
402
|
codeChallengeMethodsSupported: stringList(body, 'code_challenge_methods_supported'),
|
|
394
403
|
};
|
|
395
404
|
}
|
|
@@ -468,13 +477,15 @@ export function loadStoredRegistration(
|
|
|
468
477
|
|
|
469
478
|
export async function ensureClientRegistration(
|
|
470
479
|
metadata: AuthorizationServerMetadata,
|
|
471
|
-
opts: McpOAuthRuntimeOpts & { forceReregister?: boolean } = {},
|
|
480
|
+
opts: McpOAuthRuntimeOpts & { forceReregister?: boolean; deviceCode?: boolean } = {},
|
|
472
481
|
): Promise<ClientRegistration> {
|
|
473
482
|
const path = storePath(opts);
|
|
474
483
|
if (!opts.forceReregister) {
|
|
475
484
|
const stored = loadStoredRegistration(metadata.issuer, { storePath: path });
|
|
476
|
-
//
|
|
477
|
-
|
|
485
|
+
// Browser registrations require a bound loopback URI. Device authorization reuses a client without one
|
|
486
|
+
// (a prior device client — its stored refresh token stays paired with it), or any client when it cannot register.
|
|
487
|
+
if (stored && opts.deviceCode && (!stored.redirectUri || !metadata.registrationEndpoint)) return stored;
|
|
488
|
+
if (stored?.redirectUri && !opts.deviceCode) return stored;
|
|
478
489
|
}
|
|
479
490
|
if (!metadata.registrationEndpoint) {
|
|
480
491
|
throw new McpOAuthError(
|
|
@@ -493,9 +504,9 @@ export async function ensureClientRegistration(
|
|
|
493
504
|
},
|
|
494
505
|
body: JSON.stringify({
|
|
495
506
|
client_name: CLIENT_NAME,
|
|
496
|
-
redirect_uris: [redirectUri],
|
|
497
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
498
|
-
response_types: ['code'],
|
|
507
|
+
...(opts.deviceCode ? {} : { redirect_uris: [redirectUri] }),
|
|
508
|
+
grant_types: opts.deviceCode ? ['urn:ietf:params:oauth:grant-type:device_code', 'refresh_token'] : ['authorization_code', 'refresh_token'],
|
|
509
|
+
response_types: opts.deviceCode ? [] : ['code'],
|
|
499
510
|
token_endpoint_auth_method: 'none',
|
|
500
511
|
application_type: 'native',
|
|
501
512
|
}),
|
|
@@ -519,8 +530,15 @@ export async function ensureClientRegistration(
|
|
|
519
530
|
const registration: ClientRegistration = {
|
|
520
531
|
clientId,
|
|
521
532
|
...(clientSecret ? { clientSecret } : {}),
|
|
522
|
-
redirectUri,
|
|
533
|
+
...(opts.deviceCode ? {} : { redirectUri }),
|
|
523
534
|
};
|
|
535
|
+
// A device client is stored only with its first tokens (authorizeMcpDevice): until a device login succeeds,
|
|
536
|
+
// the stored registration and the refresh token paired with it stay as they were.
|
|
537
|
+
if (opts.deviceCode) return registration;
|
|
538
|
+
// A browser registration over another client's live tokens (e.g. after a device login) waits for its own
|
|
539
|
+
// tokens: storing it now would orphan the stored refresh token if this approval is cancelled or fails.
|
|
540
|
+
const existing = loadTokens(metadata.issuer, path);
|
|
541
|
+
if (existing?.tokens.refreshToken && existing.accountUuid && existing.accountUuid !== clientId) return { ...registration, pending: true };
|
|
524
542
|
persistRegistration(metadata.issuer, registration, path);
|
|
525
543
|
return registration;
|
|
526
544
|
}
|
|
@@ -546,6 +564,7 @@ export function buildAuthorizationRequest(
|
|
|
546
564
|
'authorization server does not advertise code_challenge_methods_supported=S256',
|
|
547
565
|
);
|
|
548
566
|
}
|
|
567
|
+
if (!metadata.authorizationEndpoint) throw new McpOAuthError('discovery', 'authorization server has no authorization_endpoint for browser login');
|
|
549
568
|
const pkce = generatePkcePair();
|
|
550
569
|
const redirectUri = opts.redirectUri ?? DEFAULT_REDIRECT_URI;
|
|
551
570
|
const url = new URL(metadata.authorizationEndpoint);
|
|
@@ -565,6 +584,7 @@ export function buildAuthorizationRequest(
|
|
|
565
584
|
redirectUri,
|
|
566
585
|
issuer: metadata.issuer,
|
|
567
586
|
clientId: registration.clientId,
|
|
587
|
+
...(registration.pending ? { pendingRegistration: registration.clientSecret ? { clientSecret: registration.clientSecret } : {} } : {}),
|
|
568
588
|
...(scope ? { scope } : {}),
|
|
569
589
|
...(opts.resource ? { resource: opts.resource } : {}),
|
|
570
590
|
};
|
|
@@ -673,13 +693,107 @@ function persistTokens(
|
|
|
673
693
|
authMode: AUTH_MODE,
|
|
674
694
|
...(clientId ? { accountUuid: clientId } : {}),
|
|
675
695
|
...(clientSecret ? { organizationUuid: clientSecret } : {}),
|
|
676
|
-
|
|
696
|
+
// A loopback URI belongs to the client that registered it — never carry it onto a different (device) client.
|
|
697
|
+
...(existing?.redirectUri && (!registration || registration.clientId === existing.accountUuid) ? { redirectUri: existing.redirectUri } : {}),
|
|
677
698
|
mirrorCodex: false,
|
|
678
699
|
},
|
|
679
700
|
path,
|
|
680
701
|
);
|
|
681
702
|
}
|
|
682
703
|
|
|
704
|
+
export interface McpDeviceAuthorization {
|
|
705
|
+
userCode: string;
|
|
706
|
+
verificationUri: string;
|
|
707
|
+
verificationUriComplete?: string;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/** RFC 8628: only an advertised endpoint enables the fallback; tokens use the same issuer store. */
|
|
711
|
+
export async function authorizeMcpDevice(
|
|
712
|
+
metadata: AuthorizationServerMetadata,
|
|
713
|
+
registration: ClientRegistration,
|
|
714
|
+
opts: McpOAuthRuntimeOpts & {
|
|
715
|
+
scope?: string;
|
|
716
|
+
resource?: string;
|
|
717
|
+
onDeviceCode: (code: McpDeviceAuthorization) => Promise<void> | void;
|
|
718
|
+
sleep?: (ms: number) => Promise<void>;
|
|
719
|
+
now?: () => number;
|
|
720
|
+
},
|
|
721
|
+
): Promise<OAuthTokens> {
|
|
722
|
+
const endpoint = metadata.deviceAuthorizationEndpoint;
|
|
723
|
+
if (!endpoint) throw new McpOAuthError('device', 'authorization server does not advertise device_authorization_endpoint');
|
|
724
|
+
const fetchFn = fetchImpl(opts);
|
|
725
|
+
const post = async (url: string, fields: Record<string, string | undefined>) => {
|
|
726
|
+
let response: Awaited<ReturnType<McpOAuthFetch>>;
|
|
727
|
+
try {
|
|
728
|
+
response = await fetchFn(url, {
|
|
729
|
+
method: 'POST',
|
|
730
|
+
headers: { accept: 'application/json', 'content-type': 'application/x-www-form-urlencoded' },
|
|
731
|
+
body: formBody(fields),
|
|
732
|
+
});
|
|
733
|
+
} catch (error) {
|
|
734
|
+
throw new McpOAuthError('device', `device authorization request failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
735
|
+
}
|
|
736
|
+
const text = await response.text();
|
|
737
|
+
return { status: response.status, body: parseJsonObject(text, 'device authorization response', 'device') };
|
|
738
|
+
};
|
|
739
|
+
const started = await post(endpoint, {
|
|
740
|
+
client_id: registration.clientId,
|
|
741
|
+
client_secret: registration.clientSecret,
|
|
742
|
+
scope: opts.scope,
|
|
743
|
+
resource: opts.resource,
|
|
744
|
+
});
|
|
745
|
+
const deviceCode = stringField(started.body, 'device_code');
|
|
746
|
+
const userCode = stringField(started.body, 'user_code');
|
|
747
|
+
const verificationUri = stringField(started.body, 'verification_uri');
|
|
748
|
+
if (started.status < 200 || started.status >= 300 || !deviceCode || !userCode || !verificationUri) {
|
|
749
|
+
const error = stringField(started.body, 'error');
|
|
750
|
+
throw new McpOAuthError('device', `device authorization returned HTTP ${started.status}${error ? ` (${error})` : ''} or missing required fields`);
|
|
751
|
+
}
|
|
752
|
+
requireHttpUrl(verificationUri, 'verification_uri');
|
|
753
|
+
const verificationUriComplete = stringField(started.body, 'verification_uri_complete');
|
|
754
|
+
if (verificationUriComplete) requireHttpUrl(verificationUriComplete, 'verification_uri_complete');
|
|
755
|
+
const seconds = started.body.expires_in;
|
|
756
|
+
if (typeof seconds !== 'number' || !Number.isFinite(seconds) || seconds <= 0) {
|
|
757
|
+
throw new McpOAuthError('device', 'device authorization response has invalid expires_in');
|
|
758
|
+
}
|
|
759
|
+
const now = opts.now ?? Date.now;
|
|
760
|
+
const deadline = now() + Math.min(seconds * 1000, 15 * 60_000);
|
|
761
|
+
let interval = typeof started.body.interval === 'number' && Number.isFinite(started.body.interval) && started.body.interval > 0
|
|
762
|
+
? Math.max(1000, started.body.interval * 1000) : 5000;
|
|
763
|
+
await opts.onDeviceCode({ userCode, verificationUri, ...(verificationUriComplete ? { verificationUriComplete } : {}) });
|
|
764
|
+
const sleep = opts.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));
|
|
765
|
+
let attempts = 0;
|
|
766
|
+
while (now() < deadline) {
|
|
767
|
+
if (++attempts > Math.ceil(Math.min(seconds, 900)) + 1) throw new McpOAuthError('device', 'device authorization polling limit exceeded');
|
|
768
|
+
await sleep(Math.min(interval, deadline - now()));
|
|
769
|
+
if (now() >= deadline) break;
|
|
770
|
+
const polled = await post(metadata.tokenEndpoint, {
|
|
771
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
772
|
+
device_code: deviceCode,
|
|
773
|
+
client_id: registration.clientId,
|
|
774
|
+
client_secret: registration.clientSecret,
|
|
775
|
+
resource: opts.resource,
|
|
776
|
+
});
|
|
777
|
+
if (polled.status >= 200 && polled.status < 300) {
|
|
778
|
+
const accessToken = stringField(polled.body, 'access_token');
|
|
779
|
+
if (!accessToken) throw new McpOAuthError('token', 'device token response is missing access_token');
|
|
780
|
+
const tokens: OAuthTokens = {
|
|
781
|
+
accessToken,
|
|
782
|
+
refreshToken: stringField(polled.body, 'refresh_token') ?? '',
|
|
783
|
+
expiresAt: expiresAtFromSeconds(polled.body.expires_in),
|
|
784
|
+
tokenType: stringField(polled.body, 'token_type') ?? 'Bearer',
|
|
785
|
+
...(stringField(polled.body, 'scope') ? { scope: stringField(polled.body, 'scope') } : {}),
|
|
786
|
+
};
|
|
787
|
+
persistTokens(metadata.issuer, tokens, registration, storePath(opts));
|
|
788
|
+
return tokens;
|
|
789
|
+
}
|
|
790
|
+
const error = stringField(polled.body, 'error');
|
|
791
|
+
if (error === 'slow_down') interval += 5000;
|
|
792
|
+
else if (error !== 'authorization_pending') throw new McpOAuthError('device', `device token request returned HTTP ${polled.status} (${error ?? 'unknown'})`);
|
|
793
|
+
}
|
|
794
|
+
throw new McpOAuthError('device', 'device authorization expired before approval');
|
|
795
|
+
}
|
|
796
|
+
|
|
683
797
|
export async function exchangeAuthorizationCode(
|
|
684
798
|
metadata: AuthorizationServerMetadata,
|
|
685
799
|
request: AuthorizationRequest,
|
|
@@ -688,7 +802,10 @@ export async function exchangeAuthorizationCode(
|
|
|
688
802
|
): Promise<OAuthTokens> {
|
|
689
803
|
const code = verifyAuthorizationCallback(request, callback);
|
|
690
804
|
const path = storePath(opts);
|
|
691
|
-
const
|
|
805
|
+
const pending = request.pendingRegistration;
|
|
806
|
+
const registration: ClientRegistration | null = pending
|
|
807
|
+
? { clientId: request.clientId, ...(pending.clientSecret ? { clientSecret: pending.clientSecret } : {}), redirectUri: request.redirectUri }
|
|
808
|
+
: loadStoredRegistration(metadata.issuer, { storePath: path });
|
|
692
809
|
const tokens = await postToken(
|
|
693
810
|
metadata.tokenEndpoint,
|
|
694
811
|
{
|
|
@@ -703,7 +820,9 @@ export async function exchangeAuthorizationCode(
|
|
|
703
820
|
fetchImpl(opts),
|
|
704
821
|
'token',
|
|
705
822
|
);
|
|
706
|
-
|
|
823
|
+
// A pending registration is committed together with the tokens it just earned.
|
|
824
|
+
if (pending) persistRegistration(metadata.issuer, registration!, path, tokens);
|
|
825
|
+
else persistTokens(
|
|
707
826
|
metadata.issuer,
|
|
708
827
|
tokens,
|
|
709
828
|
registration ?? { clientId: request.clientId },
|