elanous 0.2.22 → 0.2.24
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/{aBi6EyMtLsrl7J8eH4kF7 → Q-7iofoxOEv1WX18S35of}/_buildManifest.js +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-0eed0fb0aab716b6.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-aaae04ed1d32622f.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-3b9fc39362473ae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-d7602d489edf5f37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-24282bf9f90031a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-4eb84d454f8c3415.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-b2e494bdad57fcb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-80ed511e02c81412.js → page-c0ab229673a8b0eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-bf88c0f558d8ef54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-52fec523e5b5d3ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.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-22725e2570f1126b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-1891335950d68b70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-c314b75c11234998.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-9f9c85415f61782b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8526c17ae38e6a50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-f998637a8402115c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6f23159fedb41f67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1d809a87139dd024.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-da9e570234d481fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b92caddb5c715f30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-57ffeeb6019440fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2c2b3f29cec1efa4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-9369279f72fc7b51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.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-901a1391b81053bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b5c0c996f329371e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-89881ccbcf728978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-58d4e5cb30ef4871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-acf868c64d77f950.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-bc79f0833533fa84.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d5c4398eea03d8f3.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/72becfd42807b50d.css +1 -0
- package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
- 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/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
- package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
- package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
- package/catalog/providers/openrouter.yaml +2 -0
- package/graphs/implement-loop.yaml +31 -0
- package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
- package/graphs/log-horizon/recipes.yaml +3 -0
- package/graphs/ops/harness-gc.yaml +17 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/overlays/README.md +1 -1
- package/graphs/overlays/stop-heal-ladder.yaml +105 -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/add-api-key.sh +1 -1
- package/scripts/bench-report.ts +82 -3
- package/scripts/ci-typecheck-changed.ts +80 -14
- package/scripts/fleet/bedrock-pod-secret.ts +122 -0
- package/scripts/fleet/node-setup-linux.sh +97 -0
- package/scripts/fleet/node-setup.sh +11 -0
- package/scripts/fleet/zai-pod-secret.ts +86 -0
- package/scripts/hq/hq-fence-wrapper.sh +148 -0
- package/scripts/land-global-checks.ts +3 -0
- package/scripts/lib/typecheck-ratchet.ts +51 -0
- package/scripts/measure-log-diet-2.ts +213 -0
- package/scripts/measure-tsc-affected-roots.ts +289 -0
- package/scripts/openai-relay-server.ts +5 -1
- package/scripts/release-loop/docs-node.ts +258 -43
- package/scripts/release-loop/gate-executor.ts +28 -0
- package/scripts/release-loop/gate-method.ts +59 -0
- package/scripts/release-loop/gate-node.ts +335 -33
- 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 +40 -4
- package/scripts/release-loop/resume.ts +94 -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 +29 -7
- package/scripts/rhythm/daily.ts +51 -2
- package/scripts/rhythm/weekly.ts +3 -2
- package/scripts/tsc-baseline-cache.ts +147 -0
- package/scripts/tsc-main-safety-net.ts +189 -0
- package/scripts/usage-rollup.ts +64 -8
- package/src/acp/codex-plugins.ts +48 -6
- package/src/agent/monad-agent-turn.ts +67 -5
- package/src/agent/run-context.ts +20 -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 +19 -4
- package/src/agent-mission/pr-review-watch.ts +6 -2
- package/src/agent-mission/resource-ladder.ts +8 -3
- package/src/agent-mission/review-loop.ts +41 -7
- package/src/agent-substrate/acp-implementer.ts +139 -0
- package/src/agent-substrate/acp-reviewer.ts +17 -8
- package/src/agent-substrate/pr-reviewer.ts +191 -28
- package/src/agent-substrate/review-intent.ts +6 -4
- package/src/agent-substrate/self-review-cli.ts +155 -21
- 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/mission-critique.ts +18 -5
- package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
- package/src/autopilot/mission-trace.ts +12 -1
- package/src/autopilot/pr-manager.ts +12 -3
- package/src/autopilot/system-lookback-run.ts +16 -10
- package/src/bench/bench.ts +183 -0
- 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/budget/fetchers/grok.ts +15 -5
- package/src/budget/llm-cost.ts +37 -5
- package/src/budget/types.ts +8 -2
- package/src/budget/unified-usage.ts +88 -6
- package/src/chat/index.ts +12 -3
- package/src/cli/agent-cli.ts +20 -0
- package/src/cli/bench-cli.ts +34 -0
- package/src/cli/brief-cli.ts +27 -21
- package/src/cli/cli-user-error.ts +9 -2
- package/src/cli/guardian-cli.ts +26 -1
- package/src/cli/image-cli.ts +101 -0
- package/src/cli/log-event-names.ts +7 -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 +90 -5
- package/src/cli/pr-land-overlap-evidence.ts +279 -0
- package/src/cli/release-cli.ts +63 -12
- package/src/cli/rfc-status.ts +52 -2
- package/src/cli/self-update.ts +277 -10
- package/src/cli/skills-cli.ts +12 -3
- package/src/cli/tasks-cli.ts +241 -30
- package/src/cli/usage-cli.ts +91 -8
- 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 +87 -2
- package/src/dashboard/ad-run-setup-bindings.ts +279 -0
- package/src/dashboard/chat-fold.ts +20 -0
- package/src/dashboard/first-screen-band.ts +151 -1
- package/src/dashboard/index.ts +45 -281
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -3
- package/src/dashboard/stale-install-check.ts +24 -2
- package/src/dashboard/turn-stream-formatter.ts +25 -0
- package/src/debug/log-diet.ts +190 -0
- package/src/debug/log.ts +70 -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 +158 -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/error-report/report.ts +21 -4
- 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 +2 -0
- package/src/graph-runner/graph-cli.ts +42 -1
- package/src/graph-runner/runner.ts +218 -8
- package/src/grok/usage.ts +23 -2
- package/src/grounding/ref-index.ts +253 -0
- package/src/harness/dispatch-backpressure.ts +267 -0
- package/src/harness/harness-clean.ts +199 -78
- package/src/harness/harness-cli-command.ts +229 -35
- package/src/harness/harness-gc.ts +331 -0
- package/src/harness/harness-seams.ts +2 -20
- package/src/harness/harness-stop.ts +4 -2
- package/src/harness/harness-substrate-default.ts +214 -8
- package/src/harness/launch-registry.ts +6 -0
- package/src/harness/nested-elanous-policy.ts +4 -2
- package/src/harness/worktree-branch-prefix.ts +9 -0
- package/src/hq/fence-wrapper.ts +7 -0
- package/src/hq/fence.ts +187 -0
- package/src/hq/hq.ts +33 -2
- package/src/hq/lease.ts +13 -0
- package/src/hq/seats.ts +133 -21
- package/src/images/image-ledger.ts +179 -0
- package/src/index.ts +250 -69
- package/src/instance/child-scope.ts +32 -1
- package/src/instance/dev-project.ts +133 -0
- package/src/instance/resolve.ts +24 -0
- package/src/instance/spawn-elanous.ts +45 -0
- 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/intake-plane/ref-prefetch.ts +110 -0
- package/src/intelligence-map/model-alias.ts +6 -0
- package/src/intelligence-map/model-catalog.ts +113 -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-eventstream.ts +215 -0
- package/src/llm/bedrock-provider.ts +248 -0
- package/src/llm/bedrock.ts +491 -0
- package/src/llm/openrouter-byok-cost.ts +96 -0
- package/src/llm/openrouter-deepseek.ts +151 -0
- package/src/llm/openrouter-upstream-pins.config.ts +25 -0
- package/src/llm/provider-credentials.ts +3 -1
- package/src/llm/zai-coding-plan.ts +700 -0
- package/src/llm.ts +383 -41
- package/src/loops/budget.ts +150 -0
- package/src/loops/checker.ts +148 -9
- package/src/loops/orchestrator/resync-shadow.ts +34 -7
- 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/maturity/feature-maturity.ts +2 -2
- package/src/mcp/mcp-oauth.ts +136 -17
- package/src/model-tier/llm-tier-map.ts +52 -4
- package/src/models/prompts.ts +23 -1
- package/src/mss/logging/horizon-watch.ts +168 -0
- package/src/mss/logging/log-store.ts +262 -26
- 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/missions.ts +73 -2
- package/src/nexus/api/ops-api.ts +18 -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 +17 -75
- package/src/nexus/outbound/format.ts +2 -0
- package/src/nexus/outbound/router.ts +198 -13
- package/src/nexus/tools-kind.ts +78 -0
- package/src/oauth/codex-account-rotation.ts +55 -20
- package/src/oauth/codex-account-store.ts +451 -23
- package/src/oauth/fallback-chain.ts +75 -15
- package/src/onboarding/first-run.ts +37 -18
- package/src/onboarding/web-first.ts +20 -2
- package/src/onboarding.ts +2 -1
- package/src/ops/machine-heartbeat.ts +185 -0
- package/src/ops/overview-cache.ts +175 -0
- package/src/ops/overview.ts +8 -0
- 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 +13 -6
- package/src/prompt-cache/openrouter.ts +94 -0
- package/src/prompt-cache/types.ts +5 -0
- package/src/prompt-enhance/enhance.ts +69 -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 +107 -23
- 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/requirements/requirement-ledger.ts +536 -0
- package/src/retro/daily.ts +26 -12
- package/src/seat-loop/seat-loop.ts +66 -11
- 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 +109 -11
- package/src/self-dev/dev-pipeline.ts +11 -8
- package/src/self-dev/draft-residue.ts +475 -0
- package/src/self-dev/draft-sweep.ts +254 -10
- package/src/self-dev/draft-triage-rules.ts +27 -1
- package/src/self-dev/graph-journey-route.ts +6 -0
- 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 +82 -5
- package/src/self-dev/orchestrate-summary-line.ts +13 -0
- package/src/self-dev/orchestrate.ts +49 -141
- 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 +190 -36
- package/src/self-dev/run-types.ts +191 -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 +3 -4
- package/src/self-implement/blocked-draft-policy.ts +3 -19
- package/src/self-implement/budget-gate.ts +53 -13
- package/src/self-implement/child-context-composition.ts +139 -0
- package/src/self-implement/child-llm-preference.ts +13 -5
- package/src/self-implement/claude-acp-implement.ts +67 -0
- package/src/self-implement/draft-on-stop-mode.ts +18 -0
- package/src/self-implement/flex-signals.ts +59 -0
- package/src/self-implement/follow-up-goals.ts +114 -38
- package/src/self-implement/frozen-merges.ts +8 -3
- 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-decompose.ts +10 -2
- package/src/self-implement/goal-author.ts +48 -13
- package/src/self-implement/goal-digest.ts +19 -0
- package/src/self-implement/graph-overlay-condition.ts +32 -0
- package/src/self-implement/graph-overlay-yaml.ts +41 -7
- package/src/self-implement/graph-templates.ts +33 -1
- package/src/self-implement/graph-yaml.ts +56 -0
- package/src/self-implement/headless-elanous-driver.ts +11 -9
- package/src/self-implement/heal-intake.ts +5 -1
- 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 +271 -25
- 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/running-runs.ts +75 -5
- package/src/self-implement/seams.ts +83 -4
- package/src/self-implement/self-implement-cli.ts +12 -5
- package/src/self-implement/stop-autoheal.ts +69 -2
- 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/session-runtime/retry-policy.ts +2 -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 +22 -10
- package/src/steward/triage-budget.ts +62 -0
- package/src/steward/triage.ts +87 -24
- package/src/storage/file-lock.ts +8 -1
- package/src/task-agent/actions.ts +66 -5
- package/src/task-agent/card-evidence.ts +59 -13
- package/src/task-agent/completion-evidence.ts +5 -0
- package/src/task-agent/cover.ts +101 -3
- package/src/task-agent/judge.ts +20 -3
- package/src/task-agent/land-order-ledger.ts +95 -0
- package/src/task-agent/land-worktree.ts +201 -0
- package/src/task-agent/live-move-history.ts +219 -0
- package/src/task-agent/live-moves.ts +451 -34
- package/src/task-agent/parent-host.ts +117 -8
- package/src/task-agent/rejudge-on-head.ts +352 -0
- package/src/task-agent/review-prior-fix.ts +146 -0
- package/src/task-agent/review-result-capture.ts +218 -0
- package/src/task-agent/run-adopt.ts +136 -0
- package/src/task-agent/shadow.ts +154 -14
- package/src/task-agent/task-hand.ts +186 -14
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
- package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
- package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +42 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +94 -7
- package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +110 -17
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +32 -3
- package/src/task-orchestrator/surfaces/pod-pool.ts +86 -18
- package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +463 -50
- package/src/task-orchestrator/surfaces/self-implement.ts +25 -3
- 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 +1 -0
- package/src/user-config.ts +180 -32
- 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/registry.ts +59 -0
- package/src/workflow-runtime/triggers/scheduler.ts +1 -0
- package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
- 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 → Q-7iofoxOEv1WX18S35of}/_ssgManifest.js +0 -0
|
@@ -35,6 +35,18 @@ import {
|
|
|
35
35
|
type MissionStatus,
|
|
36
36
|
} from '../../task-orchestrator/mission.js';
|
|
37
37
|
import type { Task } from '../../task-orchestrator/types.js';
|
|
38
|
+
import {
|
|
39
|
+
openSchedulesDb,
|
|
40
|
+
listSchedules,
|
|
41
|
+
scheduleHealth,
|
|
42
|
+
} from '../../domains/schedule-registry.js';
|
|
43
|
+
import {
|
|
44
|
+
cronRowsToDerived,
|
|
45
|
+
rollupDerived,
|
|
46
|
+
taskStatusToDerived,
|
|
47
|
+
type DerivedJob,
|
|
48
|
+
type MissionRollup,
|
|
49
|
+
} from '../../autopilot/mission-trace.js';
|
|
38
50
|
|
|
39
51
|
import { checkAuth, type MetaApiOpts } from './meta-api.js';
|
|
40
52
|
import { jsonResponse } from './http-server.js';
|
|
@@ -68,12 +80,63 @@ export interface MissionCardWire {
|
|
|
68
80
|
* Mission. 자율 Mission 이면 실행모델·apm 상태·계보 키를 담아 통합 Missions
|
|
69
81
|
* 표면이 autopilot 여부와 lineage 를 한눈에 렌더한다. */
|
|
70
82
|
autopilot: MissionAutopilot | null;
|
|
83
|
+
/** 파생 작업 롤업(태스크 + 크론). ⛔ **결손 수리(MISSIONS-STALE-MARK · 2026-10-11)**:
|
|
84
|
+
* 이 칸이 없어 PWA 미션 카드의 `RollupChips` 는 항상 `undefined` 를 받아
|
|
85
|
+
* «아무것도» 안 그렸다 — 그래서 «파생 0»과 «파생 미측정»을 화면이 «구분 못 했다».
|
|
86
|
+
* 카드는 목록 엔드포인트만 보므로(미션별 `traceAutopilotMission` 을 안 부름)
|
|
87
|
+
* 여기서 «이미 로드한 태스크» ⊕ «요청당 한 번 읽은 schedule_registry」로
|
|
88
|
+
* 계산한다. 자율행동(surface_events)·자식 미션은 상세 트레이스
|
|
89
|
+
* (`ops_status action=mission`)의 몫으로 남는다(목록에서 N× 무거운 조인 금지).
|
|
90
|
+
* 매핑은 `rollupDerived`·`taskStatusToDerived`·`cronRowsToDerived` — «단일 소유»라
|
|
91
|
+
* 트레이스와 갈림이 없다. `total: 0` = «파생이 정말 0». */
|
|
92
|
+
derived: MissionRollup;
|
|
71
93
|
createdAt: number;
|
|
72
94
|
updatedAt: number;
|
|
73
95
|
closedAt: number | null;
|
|
74
96
|
}
|
|
75
97
|
|
|
76
|
-
|
|
98
|
+
/** 태스크 행 → 파생물(순수). 트레이스의 `fetchTaskDerived` 와 같은 상태 매핑
|
|
99
|
+
* (`taskStatusToDerived`)을 쓴다 — «두 가지 상태 어휘»를 만들지 않는다. */
|
|
100
|
+
export function taskRowsToDerived(tasks: readonly Task[]): DerivedJob[] {
|
|
101
|
+
return tasks.map((t) => ({
|
|
102
|
+
kind: 'task',
|
|
103
|
+
id: t.id,
|
|
104
|
+
name: t.title,
|
|
105
|
+
status: taskStatusToDerived(t.status),
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 요청당 «한 번» schedule_registry 를 읽어 apmId 별 크론 파생물을 나눠 담는다.
|
|
110
|
+
* ⛔ 미션별로 DB 를 열면 목록 N건 = N× 열기 — 그래서 한 번 읽고 in-process 분배.
|
|
111
|
+
* fail-soft: 레지스트리가 없으면 빈 Map(derived 는 태스크만 집계). */
|
|
112
|
+
export function loadCronDerivedByMission(): Map<string, DerivedJob[]> {
|
|
113
|
+
const out = new Map<string, DerivedJob[]>();
|
|
114
|
+
try {
|
|
115
|
+
const db = openSchedulesDb();
|
|
116
|
+
try {
|
|
117
|
+
const rows = listSchedules(db);
|
|
118
|
+
const staleIds = new Set(scheduleHealth(rows).stale.map((s) => s.id));
|
|
119
|
+
const rowsByMission = new Map<string, typeof rows>();
|
|
120
|
+
for (const row of rows) {
|
|
121
|
+
const apmId = row.autopilot_id;
|
|
122
|
+
if (!apmId) continue;
|
|
123
|
+
const bucket = rowsByMission.get(apmId);
|
|
124
|
+
if (bucket) bucket.push(row);
|
|
125
|
+
else rowsByMission.set(apmId, [row]);
|
|
126
|
+
}
|
|
127
|
+
for (const [apmId, missionRows] of rowsByMission) {
|
|
128
|
+
out.set(apmId, cronRowsToDerived(missionRows, staleIds));
|
|
129
|
+
}
|
|
130
|
+
} finally { db.close(); }
|
|
131
|
+
} catch { /* fail-soft */ }
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function summarise(
|
|
136
|
+
mission: Mission,
|
|
137
|
+
tasks: readonly Task[],
|
|
138
|
+
crons: readonly DerivedJob[] = [],
|
|
139
|
+
): MissionCardWire {
|
|
77
140
|
const taskStatusCounts: Record<string, number> = {};
|
|
78
141
|
const surfaceKindCounts: Record<string, number> = {};
|
|
79
142
|
for (const t of tasks) {
|
|
@@ -92,6 +155,7 @@ export function summarise(mission: Mission, tasks: readonly Task[]): MissionCard
|
|
|
92
155
|
taskStatusCounts,
|
|
93
156
|
surfaceKindCounts,
|
|
94
157
|
autopilot: mission.autopilot ?? null,
|
|
158
|
+
derived: rollupDerived([...taskRowsToDerived(tasks), ...crons]),
|
|
95
159
|
createdAt: mission.createdAt,
|
|
96
160
|
updatedAt: mission.updatedAt,
|
|
97
161
|
closedAt: mission.closedAt ?? null,
|
|
@@ -115,8 +179,15 @@ export function handleMissionsList(
|
|
|
115
179
|
const store = new TaskStore();
|
|
116
180
|
try {
|
|
117
181
|
const missions = store.listMissions(filter);
|
|
182
|
+
// 크론 레지스트리는 «요청당 한 번»만 읽고 apmId 로 분배(미션별 DB 열기 금지).
|
|
183
|
+
const cronsByMission = loadCronDerivedByMission();
|
|
118
184
|
const cards = missions.map((m) =>
|
|
119
|
-
summarise(
|
|
185
|
+
summarise(
|
|
186
|
+
m,
|
|
187
|
+
store.listTasksForMission(m.id),
|
|
188
|
+
// schedule_registry.autopilot_id 는 계보 키(apmId)다 — TOX 미션 id 와 다를 수 있다.
|
|
189
|
+
cronsByMission.get(m.autopilot?.apmId ?? m.id) ?? cronsByMission.get(m.id) ?? [],
|
|
190
|
+
),
|
|
120
191
|
);
|
|
121
192
|
return jsonResponse({
|
|
122
193
|
total: cards.length,
|
package/src/nexus/api/ops-api.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { jsonResponse } from './json-response.js';
|
|
|
9
9
|
import { operatorSignal } from './operator.js';
|
|
10
10
|
import { createSeatsCache, todayKst } from './ops-seats.js';
|
|
11
11
|
import { liveSeatsSources } from './ops-seats-sources.js';
|
|
12
|
+
import { createOverviewCache } from '../../ops/overview-cache.js';
|
|
13
|
+
import { parseSince } from '../../ops/overview-collect.js';
|
|
12
14
|
import type { MetaApiOpts } from './meta-api.js';
|
|
13
15
|
|
|
14
16
|
const VERSION = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
|
|
@@ -17,6 +19,11 @@ const DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
|
17
19
|
let seatsCache: ReturnType<typeof createSeatsCache> | null = null;
|
|
18
20
|
/** Tests swap the sources; production builds them lazily on first use. */
|
|
19
21
|
export function setSeatsCacheForTest(cache: ReturnType<typeof createSeatsCache> | null): void { seatsCache = cache; }
|
|
22
|
+
let overviewCache: ReturnType<typeof createOverviewCache> | null = null;
|
|
23
|
+
export function setOverviewCacheForTest(cache: ReturnType<typeof createOverviewCache> | null): void {
|
|
24
|
+
overviewCache?.stop();
|
|
25
|
+
overviewCache = cache;
|
|
26
|
+
}
|
|
20
27
|
const LOG_PATH = /^\/v1\/ops\/release\/runs\/([^/]+)\/nodes\/([^/]+)\/log$/;
|
|
21
28
|
|
|
22
29
|
interface RunNode { nodeId: string; ok?: unknown; output?: unknown; startedAt?: unknown; endedAt?: unknown }
|
|
@@ -190,6 +197,17 @@ export function handleOpsApi(req: Request, metaApi: MetaApiOpts | undefined): Re
|
|
|
190
197
|
response.headers.set('cache-control', 'no-store');
|
|
191
198
|
return response;
|
|
192
199
|
};
|
|
200
|
+
if (pathname === '/v1/ops/overview') {
|
|
201
|
+
let since: ReturnType<typeof parseSince>;
|
|
202
|
+
try { since = parseSince(url.searchParams.get('since') ?? undefined); }
|
|
203
|
+
catch { return jsonResponse({ error: 'invalid-since' }, 400); }
|
|
204
|
+
const version = url.searchParams.get('version');
|
|
205
|
+
if (version !== null && !VERSION.test(version)) return jsonResponse({ error: 'invalid-version' }, 400);
|
|
206
|
+
overviewCache ??= createOverviewCache();
|
|
207
|
+
return overviewCache.get({ sinceMs: since.ms, sinceLabel: since.label,
|
|
208
|
+
...(version ? { version } : {}), ...(url.searchParams.has('owner') ? { owner: url.searchParams.get('owner')! } : {}) })
|
|
209
|
+
.then((overview) => served(jsonResponse(overview)), () => jsonResponse({ error: 'overview-unavailable' }, 500));
|
|
210
|
+
}
|
|
193
211
|
if (pathname === '/v1/ops/seats') {
|
|
194
212
|
const date = url.searchParams.get('date') ?? todayKst();
|
|
195
213
|
if (!DATE.test(date) || Number.isNaN(Date.parse(`${date}T00:00:00+09:00`))) return jsonResponse({ error: 'invalid-date' }, 400);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// so ALL outbound routes through elanous's delivery channels. Send-only;
|
|
9
9
|
// auth via bearer (acp-token).
|
|
10
10
|
//
|
|
11
|
-
// Body: { text: string, markdown?: boolean, kind?: string }
|
|
11
|
+
// Body: { text: string, markdown?: boolean, kind?: string, role?: string }
|
|
12
12
|
// kind routes per-channel since R6 Phase 1 (2026-07-07): the body is
|
|
13
13
|
// handed to routeOutbound (src/nexus/outbound/router.ts) which fans out
|
|
14
14
|
// to telegram/discord/pushcut per the `outbound` user-config section.
|
|
@@ -53,6 +53,7 @@ interface OutboundBody {
|
|
|
53
53
|
text?: string;
|
|
54
54
|
markdown?: boolean;
|
|
55
55
|
kind?: string;
|
|
56
|
+
role?: string;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function json(body: unknown, status: number): Response {
|
|
@@ -85,10 +86,14 @@ export async function handleOutboundReport(req: Request, metaApi: MetaApiOpts, r
|
|
|
85
86
|
const cfg = getUserConfig();
|
|
86
87
|
const kind = typeof body.kind === 'string' ? body.kind : 'report';
|
|
87
88
|
try {
|
|
88
|
-
//
|
|
89
|
+
// routeOutbound dispatches via the channel adapter registry; legacy fan-out
|
|
90
|
+
// remains per-channel fail-soft and `delivered` = any channel ok.
|
|
89
91
|
// Producers (outbound-alert.ts deliver()) gate their direct-telegram
|
|
90
92
|
// fallback on this top-level boolean — keep it stable.
|
|
91
|
-
const
|
|
93
|
+
const role = typeof body.role === 'string' && /^[a-zA-Z0-9_-]{1,80}$/.test(body.role) ? body.role : undefined;
|
|
94
|
+
// A request that names a role intends that role's recipients — never fall back to the kind route silently.
|
|
95
|
+
if (body.role !== undefined && role === undefined) return json({ error: 'invalid-role' }, 400);
|
|
96
|
+
const result = await routeOutbound(cfg, { text, markdown: body.markdown ?? true, kind, ...(role ? { role } : {}) }, routerDeps);
|
|
92
97
|
const clientFallback = req.headers.get('X-Elanous-Client-Fallback') === 'direct';
|
|
93
98
|
const source = req.headers.get('X-Elanous-Outbound-Source') ?? 'daemon-http';
|
|
94
99
|
if (result.delivered || !clientFallback) observeDaemonSend(cfg, text, kind, result, source);
|
|
@@ -36,6 +36,7 @@ import { parseTelegramEndpoint, isOwnInstanceEndpoint, matchesBot } from '../../
|
|
|
36
36
|
import { createTelegramStreamSink } from '../../session/streaming/telegram-stream-sink.js';
|
|
37
37
|
import { splitMarkdownForTelegram } from '../../telegram-format.js';
|
|
38
38
|
import { formatTablesAndRules } from '../../discord-markdown.js';
|
|
39
|
+
import { receiveChannelEvent } from '../outbound/router.js';
|
|
39
40
|
|
|
40
41
|
export interface NexusTelegramTriggerBotOpts {
|
|
41
42
|
/** Bot token (without `Bot ` prefix). Production reads
|
|
@@ -117,11 +118,13 @@ export function createTelegramTriggerLogger(
|
|
|
117
118
|
* comment) so the wire is a near-identity with `raw` carrying the
|
|
118
119
|
* full payload (messageId · isDm) for debugging. */
|
|
119
120
|
export function toTelegramEvent(tap: TgTriggerEvent): TelegramEvent {
|
|
121
|
+
const envelope = receiveChannelEvent('telegram', tap);
|
|
122
|
+
if (!envelope || envelope instanceof Promise) throw new Error('telegram connector cannot normalize accepted tap');
|
|
120
123
|
return {
|
|
121
124
|
kind: tap.kind,
|
|
122
|
-
chat: tap.chat,
|
|
123
|
-
user: tap.user,
|
|
124
|
-
body:
|
|
125
|
+
chat: envelope.channel ?? tap.chat,
|
|
126
|
+
user: envelope.senderId ?? tap.user,
|
|
127
|
+
body: envelope.text,
|
|
125
128
|
...(tap.command !== undefined ? { command: tap.command } : {}),
|
|
126
129
|
raw: tap,
|
|
127
130
|
};
|
package/src/nexus/index.ts
CHANGED
|
@@ -410,6 +410,8 @@ export interface RunNexusOptions {
|
|
|
410
410
|
* detachForTesting; production runs always start the server. Tests
|
|
411
411
|
* that DO want the server can pass false explicitly. */
|
|
412
412
|
skipHttpServer?: boolean;
|
|
413
|
+
/** Test seam: replaces the daily error-report prune timer (the boot prune still runs for real). */
|
|
414
|
+
errorReportPruneScheduleForTesting?: (tick: () => void, ms: number) => { unref?: () => void };
|
|
413
415
|
/** Override starting port for the HTTP server. Defaults to 31415. */
|
|
414
416
|
httpStartPort?: number;
|
|
415
417
|
/** Control-plane port; passing 0 also enables an isolated listener in detached tests. */
|
|
@@ -1266,6 +1268,13 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
1266
1268
|
// 60s 틱마다 grace 유예 초과 구독자를 left 로 이탈 확정(reconcileSessionPresence).
|
|
1267
1269
|
// cleared in shutdown hook.
|
|
1268
1270
|
let presenceSweepHandle: ReturnType<typeof setInterval> | undefined;
|
|
1271
|
+
// ERROR-REPORT-PRUNE-TICK — 30-day error-report retention must not wait for the next receipt:
|
|
1272
|
+
// prune once at boot and once a day (unconditional · fail-soft · unref). Cleared in shutdown hook.
|
|
1273
|
+
let errorReportPrune: { stop(): void } | undefined;
|
|
1274
|
+
try {
|
|
1275
|
+
const { startErrorReportPruneTick } = await import('./api/error-reports.js');
|
|
1276
|
+
errorReportPrune = startErrorReportPruneTick({ ...(opts.errorReportPruneScheduleForTesting ? { schedule: opts.errorReportPruneScheduleForTesting } : {}) });
|
|
1277
|
+
} catch (err) { console.warn(`[nexus] error-report prune wiring failed: ${(err as Error).message}`); }
|
|
1269
1278
|
// ★ UR4d — 조율자 저지연 push watcher(logs.db tail edge-trigger). opt-in·기본 OFF.
|
|
1270
1279
|
// cleared in shutdown hook(내부 타이머는 unref 이나 명시 정지).
|
|
1271
1280
|
let coordinatorPushWatcher: { stop(): void } | undefined;
|
|
@@ -3383,6 +3392,7 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
3383
3392
|
if (replayArmerHandle) { clearInterval(replayArmerHandle); replayArmerHandle = undefined; }
|
|
3384
3393
|
// §P2 — stop the session presence grace sweeper.
|
|
3385
3394
|
if (presenceSweepHandle) { clearInterval(presenceSweepHandle); presenceSweepHandle = undefined; }
|
|
3395
|
+
if (errorReportPrune) { errorReportPrune.stop(); errorReportPrune = undefined; }
|
|
3386
3396
|
// ★ UR4d — stop the coordinator low-latency push watcher(logs.db tail + debounce).
|
|
3387
3397
|
if (coordinatorPushWatcher) { try { coordinatorPushWatcher.stop(); } catch { /* ignore */ } coordinatorPushWatcher = undefined; }
|
|
3388
3398
|
// Phase 2 (2026-05-13) — dispose the TOX boot handle. dispose()
|
|
@@ -4011,78 +4021,10 @@ function shouldRegisterWebterm(): boolean {
|
|
|
4011
4021
|
// `boot/register-daemon.ts` and re-exported above (line 32) so existing
|
|
4012
4022
|
// test imports (`from '../src/nexus/index.js'`) keep working.
|
|
4013
4023
|
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
* 2. `ELANOUS_TOOLS` env ← shell session
|
|
4022
|
-
* 3. UserConfig switch `global.tools` (PWA Settings 토글이 여기) ← 영구
|
|
4023
|
-
* 4. switch default = `'webterm'` (`builtins/global.ts:12`) ← fallback
|
|
4024
|
-
*
|
|
4025
|
-
* Pre-fix the in-process branch fell back to `'none'` while
|
|
4026
|
-
* `deriveChildEnv` fell back to the switch default `'webterm'`. That
|
|
4027
|
-
* meant a clean-machine `elanous nexus` (no flag/env) gave the in-process
|
|
4028
|
-
* LLM call an empty tool catalog while a separately-launched
|
|
4029
|
-
* `elanous serve` daemon honored the switch default's full surface —
|
|
4030
|
-
* surprising divergence the user had no way to discover.
|
|
4031
|
-
*
|
|
4032
|
-
* `'all'` is in the switch enum but has no `toolSurface()` impl yet;
|
|
4033
|
-
* fall back to `'webterm'` (the most-capable wired surface). */
|
|
4034
|
-
const daemonToolSurfaceKinds: Record<DaemonToolSurfaceKind, true> = {
|
|
4035
|
-
none: true,
|
|
4036
|
-
readonly: true,
|
|
4037
|
-
chat: true,
|
|
4038
|
-
webterm: true,
|
|
4039
|
-
};
|
|
4040
|
-
|
|
4041
|
-
function resolveKnownToolsKind(raw: string): DaemonToolSurfaceKind | undefined {
|
|
4042
|
-
return Object.hasOwn(daemonToolSurfaceKinds, raw)
|
|
4043
|
-
? raw as DaemonToolSurfaceKind
|
|
4044
|
-
: undefined;
|
|
4045
|
-
}
|
|
4046
|
-
|
|
4047
|
-
export function resolveToolsKind(opts: { tools?: string }): DaemonToolSurfaceKind {
|
|
4048
|
-
let raw: string | undefined = opts.tools;
|
|
4049
|
-
if (!raw) raw = process.env.ELANOUS_TOOLS?.trim();
|
|
4050
|
-
if (!raw) {
|
|
4051
|
-
try {
|
|
4052
|
-
const cfg = readUserConfig();
|
|
4053
|
-
const v = readSwitchValue(cfg, 'global.tools');
|
|
4054
|
-
if (typeof v === 'string') raw = v;
|
|
4055
|
-
} catch { /* swallow — boot must not fail on a malformed config */ }
|
|
4056
|
-
}
|
|
4057
|
-
if (!raw) raw = 'webterm';
|
|
4058
|
-
return resolveKnownToolsKind(raw) ?? 'webterm';
|
|
4059
|
-
}
|
|
4060
|
-
|
|
4061
|
-
/** Resolve the `:agent` (통합 모드 webterm dock) tool surface kind.
|
|
4062
|
-
*
|
|
4063
|
-
* Distinct from {@link resolveToolsKind} because the integrated
|
|
4064
|
-
* webterm-dock chat is **defined by** its PTY surface — WebTerminal*
|
|
4065
|
-
* tools (Snapshot · Input · Screenshot) are what make `:agent`
|
|
4066
|
-
* meaningful next to a live terminal. If the user has narrowed their
|
|
4067
|
-
* user-config (`global.tools chat` / `readonly`) to keep the
|
|
4068
|
-
* standalone `/chat` lighter, that preference must NOT bleed into the
|
|
4069
|
-
* integrated dock — otherwise the `:agent` LLM loses access to the
|
|
4070
|
-
* very terminal it's supposed to drive.
|
|
4071
|
-
*
|
|
4072
|
-
* Resolution order:
|
|
4073
|
-
* 1. CLI flag (`opts.tools`) ← explicit operator intent wins
|
|
4074
|
-
* 2. **`'webterm'`** (hardcoded) ← env + global.tools intentionally
|
|
4075
|
-
* skipped vs. resolveToolsKind
|
|
4076
|
-
*
|
|
4077
|
-
* CLI flag is honored so a developer debugging tool selection
|
|
4078
|
-
* (`elanous nexus run --tools readonly`) still sees a readonly `:agent`;
|
|
4079
|
-
* but a clean-machine boot or a config-narrowed boot always gets
|
|
4080
|
-
* webterm for the integrated dock. */
|
|
4081
|
-
export function resolveAgentTurnToolsKind(opts: { tools?: string }): DaemonToolSurfaceKind {
|
|
4082
|
-
const raw = opts.tools?.trim();
|
|
4083
|
-
if (!raw) return 'webterm';
|
|
4084
|
-
// 'all' (or any unrecognized string) → webterm fallback, same as
|
|
4085
|
-
// resolveToolsKind so the two resolvers stay consistent for valid
|
|
4086
|
-
// CLI input.
|
|
4087
|
-
return resolveKnownToolsKind(raw) ?? 'webterm';
|
|
4088
|
-
}
|
|
4024
|
+
// C-2a (same pattern as `shouldRegisterDaemon` above): the tool-surface
|
|
4025
|
+
// resolution block (`daemonToolSurfaceKinds` · `resolveKnownToolsKind` ·
|
|
4026
|
+
// `resolveToolsKind` · `resolveAgentTurnToolsKind`) moved to
|
|
4027
|
+
// `nexus/tools-kind.ts` and re-exported here so existing test imports
|
|
4028
|
+
// (`from '../src/nexus/index.js'`) keep working.
|
|
4029
|
+
import { resolveAgentTurnToolsKind, resolveToolsKind } from './tools-kind.js';
|
|
4030
|
+
export { resolveToolsKind, resolveAgentTurnToolsKind } from './tools-kind.js';
|
|
@@ -43,8 +43,66 @@ import {
|
|
|
43
43
|
export { chunkForDiscord } from './format.js';
|
|
44
44
|
export type { OutboundChannelType, OutboundMsg } from './format.js';
|
|
45
45
|
|
|
46
|
+
/** The adapter's capability declaration is explicit: a feature is unavailable unless
|
|
47
|
+
* the connector implements it. Send-only webhooks must not claim inbound support. */
|
|
48
|
+
export interface ChannelCapabilities {
|
|
49
|
+
send: boolean;
|
|
50
|
+
receive: boolean;
|
|
51
|
+
thread: boolean;
|
|
52
|
+
topic: boolean;
|
|
53
|
+
persona: boolean;
|
|
54
|
+
channelLifecycle: boolean;
|
|
55
|
+
permissions: boolean;
|
|
56
|
+
commands: boolean;
|
|
57
|
+
attachments: boolean;
|
|
58
|
+
buttons: boolean;
|
|
59
|
+
choices: boolean;
|
|
60
|
+
deliveryConfirmation: boolean;
|
|
61
|
+
rateLimit: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ChannelEnvelope {
|
|
65
|
+
text: string;
|
|
66
|
+
kind: string;
|
|
67
|
+
channel?: string;
|
|
68
|
+
senderId?: string;
|
|
69
|
+
messageId?: string | number;
|
|
70
|
+
threadId?: string;
|
|
71
|
+
topicId?: string;
|
|
72
|
+
persona?: { name: string; avatarUrl?: string };
|
|
73
|
+
attachments?: readonly { url: string; name?: string }[];
|
|
74
|
+
buttons?: readonly { label: string; value: string }[];
|
|
75
|
+
choices?: readonly { label: string; value: string }[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Plugin contract; optional operations may only be advertised when implemented.
|
|
79
|
+
* `send` returns the same ChannelResult shape used by the existing delivery ledger. */
|
|
80
|
+
export interface ChannelAdapter {
|
|
81
|
+
readonly type: string;
|
|
82
|
+
readonly capabilities: ChannelCapabilities;
|
|
83
|
+
send(config: OutboundChannelConfig, message: OutboundMsg, cfg: UserConfig, deps: RouterDeps): Promise<ChannelResult>;
|
|
84
|
+
receive?: (payload: unknown) => ChannelEnvelope | null | Promise<ChannelEnvelope | null>;
|
|
85
|
+
createChannel?: (name: string) => Promise<string>;
|
|
86
|
+
archiveChannel?: (id: string) => Promise<void>;
|
|
87
|
+
checkPermission?: (action: string) => Promise<boolean>;
|
|
88
|
+
registerCommands?: (commands: readonly string[]) => Promise<void>;
|
|
89
|
+
confirmDelivery?: (messageId: string) => Promise<boolean>;
|
|
90
|
+
retryAfterMs?: (result: ChannelResult) => number | undefined;
|
|
91
|
+
/** Validate a plugin's channel entry before it becomes eligible for routing.
|
|
92
|
+
* Unknown or invalid entries stay disabled rather than falling back to a different channel. */
|
|
93
|
+
parseConfig?: (raw: Record<string, unknown>) => OutboundChannelConfig | undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Existing bot receive paths can normalize an accepted event through the same
|
|
97
|
+
* adapter contract before dispatching to their unchanged workflow/chat handlers. */
|
|
98
|
+
export function receiveChannelEvent(type: string, payload: unknown): ChannelEnvelope | null | Promise<ChannelEnvelope | null> {
|
|
99
|
+
const adapter = outboundAdapters.get(type) ?? installedAdapters.get(type);
|
|
100
|
+
if (!adapter?.capabilities.receive || !adapter.receive) return null;
|
|
101
|
+
return adapter.receive(payload);
|
|
102
|
+
}
|
|
103
|
+
|
|
46
104
|
export interface OutboundChannelConfig {
|
|
47
|
-
type: OutboundChannelType;
|
|
105
|
+
type: OutboundChannelType | (string & {});
|
|
48
106
|
/** discord: full webhook URL (required). pushcut: webhook URL — when
|
|
49
107
|
* omitted the adapter falls back to the API-key client
|
|
50
108
|
* (~/.elanous/pushcut.json) with `notification`. */
|
|
@@ -54,6 +112,24 @@ export interface OutboundChannelConfig {
|
|
|
54
112
|
notification?: string;
|
|
55
113
|
}
|
|
56
114
|
|
|
115
|
+
/** Connector providers register while the host is running; unregister on plugin
|
|
116
|
+
* deactivation. Built-ins cannot be replaced by a plugin or removed. */
|
|
117
|
+
const installedAdapters = new Map<string, ChannelAdapter>();
|
|
118
|
+
export function registerOutboundAdapter(adapter: ChannelAdapter): () => void {
|
|
119
|
+
if (!/^[a-z][a-z0-9-]*$/.test(adapter.type) || outboundAdapters.has(adapter.type) || installedAdapters.has(adapter.type)
|
|
120
|
+
|| !adapter.capabilities.send || typeof adapter.send !== 'function' || typeof adapter.parseConfig !== 'function'
|
|
121
|
+
|| (adapter.capabilities.receive && typeof adapter.receive !== 'function')
|
|
122
|
+
|| (adapter.capabilities.channelLifecycle && (typeof adapter.createChannel !== 'function' || typeof adapter.archiveChannel !== 'function'))
|
|
123
|
+
|| (adapter.capabilities.permissions && typeof adapter.checkPermission !== 'function')
|
|
124
|
+
|| (adapter.capabilities.commands && typeof adapter.registerCommands !== 'function')
|
|
125
|
+
|| (adapter.capabilities.deliveryConfirmation && typeof adapter.confirmDelivery !== 'function')
|
|
126
|
+
|| (adapter.capabilities.rateLimit && typeof adapter.retryAfterMs !== 'function')) {
|
|
127
|
+
throw new Error(`invalid or duplicate outbound connector: ${adapter.type}`);
|
|
128
|
+
}
|
|
129
|
+
installedAdapters.set(adapter.type, adapter);
|
|
130
|
+
return () => { if (installedAdapters.get(adapter.type) === adapter) installedAdapters.delete(adapter.type); };
|
|
131
|
+
}
|
|
132
|
+
|
|
57
133
|
export interface OutboundFanoutConfig {
|
|
58
134
|
channels: OutboundChannelConfig[];
|
|
59
135
|
/** kind → channel types. A kind missing here falls back to
|
|
@@ -62,6 +138,11 @@ export interface OutboundFanoutConfig {
|
|
|
62
138
|
routes?: Record<string, string[]>;
|
|
63
139
|
/** 외출 중 kind → channel types. 미지정 kind는 awayRoutes.default, 이것도 없으면 기존 routes를 유지한다. */
|
|
64
140
|
awayRoutes?: Record<string, string[]>;
|
|
141
|
+
/** Optional primary/fallback per message kind or role; legacy routes remain fan-out. */
|
|
142
|
+
primary?: Record<string, string>;
|
|
143
|
+
fallback?: Record<string, string>;
|
|
144
|
+
/** Optional role → eligible channel types; no role retains kind routes. */
|
|
145
|
+
roleRoutes?: Record<string, string[]>;
|
|
65
146
|
}
|
|
66
147
|
|
|
67
148
|
export interface ChannelResult {
|
|
@@ -95,6 +176,8 @@ export interface RouterDeps {
|
|
|
95
176
|
/** 롱콘텐츠 spill 주입(테스트/대체). 기본 = spillLongContent(S3 게이트·fail-soft).
|
|
96
177
|
* 긴 본문을 S3 업로드+링크로 대체 → 전 메신저가 짧은 링크 버전을 받음. */
|
|
97
178
|
spill?: (text: string) => { text: string; spilled: boolean; url?: string };
|
|
179
|
+
/** Test/host supplied connector registry; built-ins remain the default. */
|
|
180
|
+
adapters?: ReadonlyMap<string, ChannelAdapter>;
|
|
98
181
|
}
|
|
99
182
|
|
|
100
183
|
/** Parse the sparse `outbound` section. Malformed input → undefined
|
|
@@ -109,7 +192,16 @@ export function normalizeOutbound(raw: unknown): OutboundFanoutConfig | undefine
|
|
|
109
192
|
if (!entry || typeof entry !== 'object') continue;
|
|
110
193
|
const ch = entry as Record<string, unknown>;
|
|
111
194
|
const type = ch.type;
|
|
112
|
-
if (type !== 'telegram' && type !== 'discord' && type !== 'pushcut')
|
|
195
|
+
if (type !== 'telegram' && type !== 'discord' && type !== 'pushcut') {
|
|
196
|
+
if (typeof type === 'string') {
|
|
197
|
+
const connector = installedAdapters.get(type);
|
|
198
|
+
try {
|
|
199
|
+
const parsed = connector?.parseConfig?.(ch);
|
|
200
|
+
if (parsed?.type === type) channels.push(parsed);
|
|
201
|
+
} catch { /* invalid plugin config is not a routable channel */ }
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
113
205
|
const webhookUrl = typeof ch.webhookUrl === 'string' && ch.webhookUrl.trim()
|
|
114
206
|
? ch.webhookUrl.trim() : undefined;
|
|
115
207
|
const notification = typeof ch.notification === 'string' && ch.notification.trim()
|
|
@@ -132,7 +224,18 @@ export function normalizeOutbound(raw: unknown): OutboundFanoutConfig | undefine
|
|
|
132
224
|
};
|
|
133
225
|
const routes = parseRoutes(o.routes);
|
|
134
226
|
const awayRoutes = parseRoutes(o.awayRoutes);
|
|
135
|
-
|
|
227
|
+
const roleRoutes = parseRoutes(o.roleRoutes);
|
|
228
|
+
const parseSelection = (value: unknown): Record<string, string> | undefined => {
|
|
229
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
230
|
+
return Object.fromEntries(Object.entries(value as Record<string, unknown>)
|
|
231
|
+
// Keep a selection even when its channel is not configured: an explicit main/backup must
|
|
232
|
+
// never silently turn back into legacy fan-out (it is reported as not-routed instead).
|
|
233
|
+
.filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].trim().length > 0));
|
|
234
|
+
};
|
|
235
|
+
const primary = parseSelection(o.primary);
|
|
236
|
+
const fallback = parseSelection(o.fallback);
|
|
237
|
+
return { channels, ...(routes ? { routes } : {}), ...(awayRoutes ? { awayRoutes } : {}),
|
|
238
|
+
...(roleRoutes ? { roleRoutes } : {}), ...(primary ? { primary } : {}), ...(fallback ? { fallback } : {}) };
|
|
136
239
|
}
|
|
137
240
|
|
|
138
241
|
/** Resolve the channel list for one message kind. No/empty config →
|
|
@@ -148,14 +251,14 @@ export function resolveChannels(
|
|
|
148
251
|
return outbound.channels.filter(ch => route.includes(ch.type));
|
|
149
252
|
}
|
|
150
253
|
|
|
151
|
-
async function deliverTelegram(
|
|
254
|
+
async function deliverTelegram(_ch: OutboundChannelConfig, msg: OutboundMsg, cfg: UserConfig, deps: RouterDeps): Promise<ChannelResult> {
|
|
152
255
|
const fmt = formatForChannel('telegram', msg);
|
|
153
256
|
const send = deps.telegramSend ?? sendTelegramReport;
|
|
154
257
|
const ok = await send(cfg, fmt.text, { markdown: fmt.markdown ?? msg.markdown, fetchImpl: deps.fetchImpl, kind: msg.kind });
|
|
155
258
|
return ok ? { type: 'telegram', ok: true } : { type: 'telegram', ok: false, error: 'not-configured' };
|
|
156
259
|
}
|
|
157
260
|
|
|
158
|
-
async function deliverDiscord(ch: OutboundChannelConfig, msg: OutboundMsg, deps: RouterDeps): Promise<ChannelResult> {
|
|
261
|
+
async function deliverDiscord(ch: OutboundChannelConfig, msg: OutboundMsg, _cfg: UserConfig, deps: RouterDeps): Promise<ChannelResult> {
|
|
159
262
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
160
263
|
// webhookUrl guaranteed by normalizeOutbound; guard for direct callers.
|
|
161
264
|
if (!ch.webhookUrl) return { type: 'discord', ok: false, error: 'missing-webhook-url' };
|
|
@@ -172,7 +275,7 @@ async function deliverDiscord(ch: OutboundChannelConfig, msg: OutboundMsg, deps:
|
|
|
172
275
|
return { type: 'discord', ok: true };
|
|
173
276
|
}
|
|
174
277
|
|
|
175
|
-
async function deliverPushcut(ch: OutboundChannelConfig, msg: OutboundMsg, deps: RouterDeps): Promise<ChannelResult> {
|
|
278
|
+
async function deliverPushcut(ch: OutboundChannelConfig, msg: OutboundMsg, _cfg: UserConfig, deps: RouterDeps): Promise<ChannelResult> {
|
|
176
279
|
const fmt = formatForChannel('pushcut', msg);
|
|
177
280
|
const title = fmt.title ?? `elanous ${msg.kind}`;
|
|
178
281
|
if (ch.webhookUrl) {
|
|
@@ -194,6 +297,40 @@ async function deliverPushcut(ch: OutboundChannelConfig, msg: OutboundMsg, deps:
|
|
|
194
297
|
return r.ok ? { type: 'pushcut', ok: true } : { type: 'pushcut', ok: false, error: r.reason ?? 'pushcut-failed' };
|
|
195
298
|
}
|
|
196
299
|
|
|
300
|
+
/** Built-in channel connectors. Telegram and Discord receive adapters normalize
|
|
301
|
+
* already-authorized trigger taps; the webhook and Pushcut senders do not poll. */
|
|
302
|
+
export const outboundAdapters: ReadonlyMap<string, ChannelAdapter> = new Map<string, ChannelAdapter>([
|
|
303
|
+
['telegram', { type: 'telegram', capabilities: {
|
|
304
|
+
send: true, receive: true, thread: false, topic: false, persona: false,
|
|
305
|
+
channelLifecycle: false, permissions: false, commands: false, attachments: false,
|
|
306
|
+
buttons: false, choices: false, deliveryConfirmation: false, rateLimit: false,
|
|
307
|
+
}, send: deliverTelegram, receive: (payload): ChannelEnvelope | null => {
|
|
308
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
309
|
+
const tap = payload as Record<string, unknown>;
|
|
310
|
+
if (typeof tap.body !== 'string' || typeof tap.kind !== 'string' || typeof tap.chat !== 'string') return null;
|
|
311
|
+
return { text: tap.body, kind: tap.kind, channel: tap.chat,
|
|
312
|
+
...(typeof tap.user === 'string' ? { senderId: tap.user } : {}),
|
|
313
|
+
...(typeof tap.messageId === 'number' ? { messageId: tap.messageId } : {}) };
|
|
314
|
+
} }],
|
|
315
|
+
['discord', { type: 'discord', capabilities: {
|
|
316
|
+
send: true, receive: true, thread: false, topic: false, persona: false,
|
|
317
|
+
channelLifecycle: false, permissions: false, commands: false, attachments: false,
|
|
318
|
+
buttons: false, choices: false, deliveryConfirmation: false, rateLimit: false,
|
|
319
|
+
}, send: deliverDiscord, receive: (payload): ChannelEnvelope | null => {
|
|
320
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
321
|
+
const tap = payload as Record<string, unknown>;
|
|
322
|
+
if (typeof tap.body !== 'string' || typeof tap.kind !== 'string' || typeof tap.channel !== 'string') return null;
|
|
323
|
+
return { text: tap.body, kind: tap.kind, channel: tap.channel,
|
|
324
|
+
...(typeof tap.user === 'string' ? { senderId: tap.user } : {}),
|
|
325
|
+
...(typeof tap.messageId === 'string' ? { messageId: tap.messageId } : {}) };
|
|
326
|
+
} }],
|
|
327
|
+
['pushcut', { type: 'pushcut', capabilities: {
|
|
328
|
+
send: true, receive: false, thread: false, topic: false, persona: false,
|
|
329
|
+
channelLifecycle: false, permissions: false, commands: false, attachments: false,
|
|
330
|
+
buttons: false, choices: false, deliveryConfirmation: false, rateLimit: false,
|
|
331
|
+
}, send: deliverPushcut }],
|
|
332
|
+
]);
|
|
333
|
+
|
|
197
334
|
/** Fan one message out to every routed channel. Per-channel fail-soft —
|
|
198
335
|
* a throwing adapter records `{ok:false}` and never blocks siblings.
|
|
199
336
|
* 구조: 채널별 포맷(format.ts) + 중복 발사 제어·배송 원장(delivery-ledger.ts).
|
|
@@ -228,27 +365,75 @@ export async function routeOutbound(cfg: UserConfig, msg: OutboundMsg, deps: Rou
|
|
|
228
365
|
|
|
229
366
|
// 상태 파일이 없거나 읽기에 실패하면 기존 kind 경로를 그대로 사용한다.
|
|
230
367
|
let list = resolveChannels(outbound, msg.kind);
|
|
368
|
+
// Whether the route that decided `list` is explicitly empty — that means «suppress», which is
|
|
369
|
+
// different from a route whose channels are simply not configured.
|
|
370
|
+
const kindRoute = outbound ? (outbound.routes?.[msg.kind] ?? outbound.routes?.default) : undefined;
|
|
371
|
+
let explicitlyEmpty = kindRoute !== undefined && kindRoute.length === 0;
|
|
372
|
+
const role = msg.role;
|
|
373
|
+
const roleRoute = role ? outbound?.roleRoutes?.[role] : undefined;
|
|
374
|
+
if (roleRoute && outbound) {
|
|
375
|
+
list = outbound.channels.filter(ch => roleRoute.includes(ch.type));
|
|
376
|
+
explicitlyEmpty = roleRoute.length === 0;
|
|
377
|
+
}
|
|
231
378
|
if (outbound?.awayRoutes) {
|
|
232
379
|
try {
|
|
233
380
|
if (readPresence().away) {
|
|
234
381
|
const awayRoute = outbound.awayRoutes[msg.kind] ?? outbound.awayRoutes.default;
|
|
235
382
|
// kind 도 default 도 없으면 기존 경로로 · 명시적인 빈 배열은 기존 routes 와 같은 규칙(채널 0)을 따른다.
|
|
236
383
|
if (awayRoute !== undefined) {
|
|
237
|
-
|
|
384
|
+
// Away mode narrows the route; it never widens a role's allowed channels.
|
|
385
|
+
list = outbound.channels.filter(ch => awayRoute.includes(ch.type) && (!roleRoute || roleRoute.includes(ch.type)));
|
|
386
|
+
// A role's explicitly empty route stays suppressed while away.
|
|
387
|
+
explicitlyEmpty = awayRoute.length === 0 || roleRoute?.length === 0;
|
|
238
388
|
try { debug.log('outbound.send', 'away-route', { kind: msg.kind, channels: list.map(ch => ch.type) }); } catch { /* 관측 실패는 발송 결과에 영향 없음 */ }
|
|
239
389
|
}
|
|
240
390
|
}
|
|
241
391
|
} catch { /* 상태 조회 실패 — 기존 경로 유지 */ }
|
|
242
392
|
}
|
|
243
|
-
const
|
|
393
|
+
const send = async (ch: OutboundChannelConfig): Promise<ChannelResult> => {
|
|
244
394
|
try {
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
|
|
395
|
+
const adapter = deps.adapters?.get(ch.type) ?? outboundAdapters.get(ch.type) ?? installedAdapters.get(ch.type);
|
|
396
|
+
if (!adapter?.capabilities.send) return { type: ch.type, ok: false, error: 'unsupported-channel' };
|
|
397
|
+
const result = await adapter.send(ch, outMsg, cfg, deps);
|
|
398
|
+
// A connector may not impersonate another channel or echo credentials
|
|
399
|
+
// through the response. Legacy built-in result fields stay unchanged.
|
|
400
|
+
if (!outboundAdapters.has(ch.type)) return { type: ch.type, ok: result.ok,
|
|
401
|
+
...(result.ok ? {} : { error: 'delivery-failed' }) };
|
|
402
|
+
return { ...result, type: ch.type };
|
|
248
403
|
} catch (e) {
|
|
249
|
-
|
|
404
|
+
// Connector exceptions may include secret-bearing URLs or configuration.
|
|
405
|
+
// Keep the old built-in error reasons unless they expose the webhook;
|
|
406
|
+
// a plugin's arbitrary configuration cannot safely be echoed at all.
|
|
407
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
408
|
+
const plugin = !outboundAdapters.has(ch.type);
|
|
409
|
+
return { type: ch.type, ok: false,
|
|
410
|
+
error: plugin || (ch.webhookUrl && reason.includes(ch.webhookUrl)) ? 'delivery-failed' : reason };
|
|
250
411
|
}
|
|
251
|
-
}
|
|
412
|
+
};
|
|
413
|
+
const primaryType = (role ? outbound?.primary?.[role] : undefined) ?? outbound?.primary?.[msg.kind] ?? outbound?.primary?.default;
|
|
414
|
+
const fallbackType = (role ? outbound?.fallback?.[role] : undefined) ?? outbound?.fallback?.[msg.kind] ?? outbound?.fallback?.default;
|
|
415
|
+
// Legacy routes still fan out. An explicit primary switches only this kind/role to ordered
|
|
416
|
+
// failover: main, then the configured backup — never the rest of the route. A selected channel
|
|
417
|
+
// that the current route does not carry is recorded as not-routed and the backup is tried.
|
|
418
|
+
// An explicitly empty route stays suppressed (no rows).
|
|
419
|
+
// Only an explicitly empty route suppresses the selection entirely (no rows).
|
|
420
|
+
const suppressed = list.length === 0 && explicitlyEmpty;
|
|
421
|
+
let channels: ChannelResult[];
|
|
422
|
+
if (suppressed) {
|
|
423
|
+
channels = [];
|
|
424
|
+
} else if (primaryType) {
|
|
425
|
+
channels = [];
|
|
426
|
+
for (const type of fallbackType && fallbackType !== primaryType ? [primaryType, fallbackType] : [primaryType]) {
|
|
427
|
+
const ch = list.find(candidate => candidate.type === type);
|
|
428
|
+
const result = ch ? await send(ch) : { type, ok: false, error: 'not-routed' };
|
|
429
|
+
channels.push(result);
|
|
430
|
+
if (result.ok) break;
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
// A backup without a main has nothing to back up: the allowed route already fans out to every
|
|
434
|
+
// channel it may reach, and a backup outside that route must not bypass role/away limits.
|
|
435
|
+
channels = await Promise.all(list.map(send));
|
|
436
|
+
}
|
|
252
437
|
|
|
253
438
|
// 배송 기록(리드 동기화·중복 제어 토대) — fail-soft.
|
|
254
439
|
if (ledger) {
|