elanous 0.2.21 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (780) hide show
  1. package/NOTICE +7 -0
  2. package/README.md +32 -22
  3. package/apps/pwa/out/404/index.html +1 -1
  4. package/apps/pwa/out/404.html +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{119fc678.6f591cdb84ca5791.js → 0673cc54.8e2ef1c23e49e190.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/{a9f2b54d-40c16bd0efdb04b8.js → 0a09e062-34f747a17d8d8c2e.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/{f8626309.a4e5d362b1d55e59.js → 0e3acd34.d42b2ef87fdf4234.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +77 -0
  9. package/apps/pwa/out/_next/static/chunks/{7175-350f4ce2873bc038.js → 1325-dc3f3c21eb181cb4.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +34 -0
  11. package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +24 -0
  12. package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{6767.8e6c5f6e744fcd9f.js → 1505.5826671109111e75.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 1669-da843ed73aa301f5.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +36 -0
  23. package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +122 -0
  24. package/apps/pwa/out/_next/static/chunks/2528.0af5c0e1fad88df6.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +62 -0
  27. package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{7899.597af4a9996c907f.js → 2721.bc9a8f36bd85c07a.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +149 -0
  35. package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +309 -0
  39. package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{d70879db.c66c56a95a91efc6.js → 4c5e95ce.83e9b16add6fcd8c.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/{6158.566c890adc06a459.js → 5313.1697309c532b9856.js} +2 -2
  52. package/apps/pwa/out/_next/static/chunks/{79b78388.663188133449d707.js → 53746282.1be36038d1abd949.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5578.ac963634373b28c6.js +286 -0
  56. package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5967-c0a80f8ffe5d4050.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 5974.360f8441eb0ac1ec.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +74 -0
  64. package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/6571-926ddf024a6ad5df.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +64 -0
  69. package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/{9832.2ab2e818013574c7.js → 6702.ebbd165d5e4a4a14.js} +1 -1
  71. package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +32 -0
  73. package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +24 -0
  74. package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +93 -0
  78. package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 740.90b31555290236a9.js} +2 -2
  83. package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +148 -0
  84. package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +226 -0
  85. package/apps/pwa/out/_next/static/chunks/{9070.067c106528ec1ae6.js → 7518.49161e8f5814abdb.js} +1 -1
  86. package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +27 -0
  87. package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +115 -0
  90. package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7905.b230b9d06cb4ad5d.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{8194.484f51e273d9abd2.js → 8264.00e434fbd02e9311.js} +16 -16
  97. package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +8 -0
  99. package/apps/pwa/out/_next/static/chunks/{088d8e8a.8069543d082bec12.js → 83cfc5c8.349535543c1548b6.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +23 -0
  101. package/apps/pwa/out/_next/static/chunks/{1948.5f35b39eef222a94.js → 8438.ee7f0df3b3f6fcb4.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/{4588.ad3364cb7fb97ada.js → 8532.a86e99acf9b761cb.js} +2 -2
  105. package/apps/pwa/out/_next/static/chunks/8569.b853a8daa8f31d12.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8925-2abfb48af477fb7f.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{88729267.9c6c000157c83f2f.js → 89849432.9eaa2f4c9c010965.js} +3 -3
  110. package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +90 -0
  111. package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +94 -0
  112. package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +41 -0
  114. package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +287 -0
  116. package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +79 -0
  117. package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/{3522-1de1319d028ac89c.js → 9502-32c5dc22e8f120f5.js} +3 -3
  120. package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +104 -0
  123. package/apps/pwa/out/_next/static/chunks/{7630.2b9a68659053ff4e.js → 9976.8f28864317639fe6.js} +16 -16
  124. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ad3bd8084048bee4.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/consult/{page-111cabe4d1fd69ae.js → page-6168a7c43e6ef8f5.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-48fb31810693c357.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cb154acadb025414.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/b4e12462-5fab4a813a63dadd.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → ce383d63.d2ede338788ed18e.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{framework-5c169757551134ed.js → framework-b2763b10dc6b3836.js} +1 -1
  179. package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +1 -0
  184. package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +1 -0
  185. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
  186. package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → rfkMm4movqkqMebBrCXiR}/_buildManifest.js +1 -1
  187. package/apps/pwa/out/approvals/index.html +1 -1
  188. package/apps/pwa/out/approvals/index.txt +23 -23
  189. package/apps/pwa/out/autopilot/index.html +1 -1
  190. package/apps/pwa/out/autopilot/index.txt +23 -23
  191. package/apps/pwa/out/board/index.html +1 -1
  192. package/apps/pwa/out/board/index.txt +23 -23
  193. package/apps/pwa/out/botlab/index.html +1 -1
  194. package/apps/pwa/out/botlab/index.txt +22 -22
  195. package/apps/pwa/out/bots/index.html +1 -1
  196. package/apps/pwa/out/bots/index.txt +22 -22
  197. package/apps/pwa/out/ceo/index.html +1 -1
  198. package/apps/pwa/out/ceo/index.txt +23 -23
  199. package/apps/pwa/out/chat/index.html +1 -1
  200. package/apps/pwa/out/chat/index.txt +22 -22
  201. package/apps/pwa/out/consult/index.html +1 -1
  202. package/apps/pwa/out/consult/index.txt +23 -23
  203. package/apps/pwa/out/control/index.html +1 -1
  204. package/apps/pwa/out/control/index.txt +23 -23
  205. package/apps/pwa/out/decisions/index.html +1 -1
  206. package/apps/pwa/out/decisions/index.txt +22 -22
  207. package/apps/pwa/out/design-check/index.html +1 -1
  208. package/apps/pwa/out/design-check/index.txt +23 -23
  209. package/apps/pwa/out/editor/index.html +1 -1
  210. package/apps/pwa/out/editor/index.txt +24 -23
  211. package/apps/pwa/out/exec/index.html +1 -1
  212. package/apps/pwa/out/exec/index.txt +23 -23
  213. package/apps/pwa/out/field/index.html +1 -1
  214. package/apps/pwa/out/field/index.txt +23 -23
  215. package/apps/pwa/out/healthz +1 -1
  216. package/apps/pwa/out/index.html +1 -1
  217. package/apps/pwa/out/index.txt +22 -22
  218. package/apps/pwa/out/inside/index.html +1 -1
  219. package/apps/pwa/out/inside/index.txt +22 -22
  220. package/apps/pwa/out/intake/index.html +1 -1
  221. package/apps/pwa/out/intake/index.txt +23 -23
  222. package/apps/pwa/out/live/index.html +1 -1
  223. package/apps/pwa/out/live/index.txt +23 -23
  224. package/apps/pwa/out/live-run/index.html +1 -1
  225. package/apps/pwa/out/live-run/index.txt +24 -23
  226. package/apps/pwa/out/loops/index.html +1 -1
  227. package/apps/pwa/out/loops/index.txt +22 -22
  228. package/apps/pwa/out/market/index.html +1 -1
  229. package/apps/pwa/out/market/index.txt +22 -22
  230. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  231. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  232. package/apps/pwa/out/morning/index.html +1 -1
  233. package/apps/pwa/out/morning/index.txt +23 -23
  234. package/apps/pwa/out/observatory/index.html +1 -1
  235. package/apps/pwa/out/observatory/index.txt +23 -23
  236. package/apps/pwa/out/ops/checklist/index.html +1 -1
  237. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  238. package/apps/pwa/out/ops/release/index.html +1 -1
  239. package/apps/pwa/out/ops/release/index.txt +22 -22
  240. package/apps/pwa/out/ops/seats/index.html +1 -1
  241. package/apps/pwa/out/ops/seats/index.txt +22 -22
  242. package/apps/pwa/out/outputs/index.html +1 -1
  243. package/apps/pwa/out/outputs/index.txt +22 -22
  244. package/apps/pwa/out/reflection/index.html +1 -1
  245. package/apps/pwa/out/reflection/index.txt +23 -23
  246. package/apps/pwa/out/scheduler/index.html +1 -1
  247. package/apps/pwa/out/scheduler/index.txt +23 -23
  248. package/apps/pwa/out/sessions/index.html +1 -1
  249. package/apps/pwa/out/sessions/index.txt +22 -22
  250. package/apps/pwa/out/settings/devices/index.html +1 -1
  251. package/apps/pwa/out/settings/devices/index.txt +23 -23
  252. package/apps/pwa/out/settings/index.html +1 -1
  253. package/apps/pwa/out/settings/index.txt +23 -23
  254. package/apps/pwa/out/setup/done/index.html +1 -1
  255. package/apps/pwa/out/setup/done/index.txt +23 -23
  256. package/apps/pwa/out/setup/index.html +1 -1
  257. package/apps/pwa/out/setup/index.txt +23 -23
  258. package/apps/pwa/out/share/index.html +1 -1
  259. package/apps/pwa/out/share/index.txt +23 -23
  260. package/apps/pwa/out/showroom/index.html +1 -1
  261. package/apps/pwa/out/showroom/index.txt +22 -22
  262. package/apps/pwa/out/tasks/index.html +1 -1
  263. package/apps/pwa/out/tasks/index.txt +23 -23
  264. package/apps/pwa/out/term/index.html +1 -1
  265. package/apps/pwa/out/term/index.txt +23 -23
  266. package/apps/pwa/out/today/index.html +1 -1
  267. package/apps/pwa/out/today/index.txt +23 -23
  268. package/apps/pwa/out/trace/index.html +1 -1
  269. package/apps/pwa/out/trace/index.txt +23 -23
  270. package/apps/pwa/out/vault/index.html +1 -1
  271. package/apps/pwa/out/vault/index.txt +23 -23
  272. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  273. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  274. package/apps/pwa/out/workflows/index.html +1 -1
  275. package/apps/pwa/out/workflows/index.txt +23 -23
  276. package/apps/pwa/out/workspace/index.html +1 -1
  277. package/apps/pwa/out/workspace/index.txt +23 -23
  278. package/apps/pwa/out/worktrees/index.html +1 -1
  279. package/apps/pwa/out/worktrees/index.txt +23 -23
  280. package/catalog/models/anthropic/claude-haiku-5-5.yaml +32 -0
  281. package/catalog/models/anthropic/claude-opus-5-5.yaml +5 -0
  282. package/catalog/models/anthropic/claude-sonnet-5-5.yaml +31 -0
  283. package/catalog/models/anthropic/claude-sonnet-5.yaml +8 -2
  284. package/catalog/models/openai/gpt-6-astra.yaml +6 -0
  285. package/catalog/models/openai/gpt-6-luna.yaml +6 -0
  286. package/catalog/models/openai/gpt-6-sol.yaml +6 -0
  287. package/graphs/catalog/catalog.yaml +1 -1
  288. package/graphs/drafts/recipes.yaml +2 -2
  289. package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
  290. package/graphs/log-horizon/recipes.yaml +3 -0
  291. package/graphs/release/recipes.yaml +1 -1
  292. package/graphs/release/release-loop.yaml +6 -2
  293. package/graphs/release-rehearsal/recipes.yaml +12 -0
  294. package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
  295. package/graphs/test-diet/recipes.yaml +3 -0
  296. package/graphs/test-diet/test-diet.yaml +3 -1
  297. package/package.json +4 -1
  298. package/scripts/bench-report.ts +82 -3
  299. package/scripts/ci-typecheck-changed.ts +10 -1
  300. package/scripts/f-session-monitor.ts +69 -0
  301. package/scripts/hq/hq-fence-wrapper.sh +148 -0
  302. package/scripts/hq/standby-snapshot.ts +7 -3
  303. package/scripts/knowledge-compress.ts +11 -0
  304. package/scripts/lib/typecheck-ratchet.ts +51 -0
  305. package/scripts/nightly-docops.ts +90 -3
  306. package/scripts/openai-relay-server.ts +5 -1
  307. package/scripts/release-loop/docs-node.ts +258 -43
  308. package/scripts/release-loop/gate-method.ts +59 -0
  309. package/scripts/release-loop/gate-node.ts +73 -7
  310. package/scripts/release-loop/gate-wall.ts +208 -0
  311. package/scripts/release-loop/known-issues-node.ts +53 -8
  312. package/scripts/release-loop/publish-at.ts +14 -0
  313. package/scripts/release-loop/publish-node.ts +17 -2
  314. package/scripts/release-loop/resume-release.ts +34 -4
  315. package/scripts/release-loop/resume.ts +53 -0
  316. package/scripts/release-loop/shard-plan.ts +3 -11
  317. package/scripts/release-loop/tui-node-checks.ts +22 -0
  318. package/scripts/release-loop/unattended-release.ts +8 -1
  319. package/scripts/research-coo-admin-dates.ts +39 -0
  320. package/scripts/rhythm/daily.ts +51 -2
  321. package/scripts/rhythm/weekly.ts +3 -2
  322. package/scripts/skills/public-check.ts +7 -2
  323. package/scripts/smart-merge/resync-shadow.ts +5 -3
  324. package/scripts/test-diet/lib.ts +81 -1
  325. package/scripts/test-diet/node.ts +48 -2
  326. package/scripts/tsc-main-safety-net.ts +189 -0
  327. package/scripts/usage-rollup.ts +8 -4
  328. package/scripts/verify-coo-admin-evidence.ts +83 -0
  329. package/src/acp/client.ts +4 -2
  330. package/src/acp/codex-plugins.ts +48 -6
  331. package/src/agent/monad-agent-turn.ts +67 -5
  332. package/src/agent/run-context.ts +14 -0
  333. package/src/agent/tool-profile.ts +28 -2
  334. package/src/agent-env/profile.ts +25 -5
  335. package/src/agent-mission/acp-judge.ts +8 -2
  336. package/src/agent-mission/capabilities-cli.ts +16 -1
  337. package/src/agent-mission/driver.ts +27 -13
  338. package/src/agent-mission/pr-review-watch.ts +6 -2
  339. package/src/agent-mission/review-loop.ts +33 -5
  340. package/src/agent-mission/supervisor-metrics.ts +17 -2
  341. package/src/agent-substrate/acp-reviewer.ts +25 -9
  342. package/src/agent-substrate/pr-reviewer.ts +175 -32
  343. package/src/agent-substrate/review-intent.ts +106 -5
  344. package/src/agent-substrate/self-review-cli.ts +111 -16
  345. package/src/agent-substrate/shard-boundary-intent.ts +92 -26
  346. package/src/ask-user-question/pending-questions.ts +8 -2
  347. package/src/autopilot/build/llm-conflict-merge.ts +238 -20
  348. package/src/autopilot/codebase-grounding.ts +44 -0
  349. package/src/autopilot/llm-control-brain.ts +13 -1
  350. package/src/autopilot/mission-codebase-gate.ts +8 -77
  351. package/src/autopilot/pr-manager.ts +30 -11
  352. package/src/away/release-watch.ts +47 -8
  353. package/src/boot/acp-server.ts +4 -7
  354. package/src/boot/acp-socket-path.ts +7 -0
  355. package/src/briefing/brief-items.ts +123 -22
  356. package/src/chat/index.ts +13 -3
  357. package/src/cli/agent-cli.ts +20 -0
  358. package/src/cli/brief-cli.ts +46 -22
  359. package/src/cli/cli-user-error.ts +9 -2
  360. package/src/cli/config-classify.ts +83 -0
  361. package/src/cli/control-cli.ts +14 -0
  362. package/src/cli/decisions-cli.ts +26 -16
  363. package/src/cli/log-event-names.ts +3 -0
  364. package/src/cli/logs-cli.ts +1 -0
  365. package/src/cli/logs-model-input.ts +1 -1
  366. package/src/cli/mcp-login.ts +111 -37
  367. package/src/cli/plugin-cli.ts +21 -1
  368. package/src/cli/pod-cli.ts +26 -0
  369. package/src/cli/pr-cli.ts +94 -9
  370. package/src/cli/pr-land-overlap-evidence.ts +131 -0
  371. package/src/cli/release-cli.ts +74 -15
  372. package/src/cli/self-update.ts +173 -10
  373. package/src/cli/skills-cli.ts +12 -3
  374. package/src/cli/tasks-cli.ts +241 -28
  375. package/src/connectors/types.ts +6 -6
  376. package/src/context-bus/context-first.ts +51 -2
  377. package/src/context-bus/context-now-surfaces.ts +29 -0
  378. package/src/context-bus/coord-events.ts +32 -4
  379. package/src/context-bus/long-term-memory.ts +26 -7
  380. package/src/context-bus/nightly-condense.ts +170 -21
  381. package/src/control/control-status.ts +87 -0
  382. package/src/control-plane/light-member.ts +5 -3
  383. package/src/control-plane/member.ts +172 -7
  384. package/src/core-turn/run-goal-loop.ts +108 -3
  385. package/src/dashboard/chat-fold.ts +20 -0
  386. package/src/dashboard/first-screen-band.ts +151 -1
  387. package/src/dashboard/index.ts +27 -5
  388. package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -3
  389. package/src/dashboard/stale-install-check.ts +13 -2
  390. package/src/dashboard/turn-stream-formatter.ts +25 -0
  391. package/src/debug/log-diet.ts +159 -0
  392. package/src/debug/log.ts +52 -17
  393. package/src/decisions/decision-cards.ts +20 -1
  394. package/src/decisions/decision-ledger.ts +11 -8
  395. package/src/decisions/web-push-decision-cards.ts +19 -3
  396. package/src/discord-self-message.ts +2 -2
  397. package/src/display/coordinator.ts +25 -2
  398. package/src/domains/coo-admin-dates.evidence.json +876 -0
  399. package/src/domains/coo-admin-tool.ts +75 -14
  400. package/src/domains/cron-match.ts +1 -0
  401. package/src/domains/ops-status.ts +53 -0
  402. package/src/domains/outbound-alert.ts +41 -7
  403. package/src/domains/release-tool.ts +119 -10
  404. package/src/domains/schedule-registry.ts +10 -7
  405. package/src/domains/schedule-runner.ts +1 -0
  406. package/src/elanous-config-dir.ts +2 -2
  407. package/src/elanous-daemon.ts +3 -3
  408. package/src/exec-requests/planner.ts +6 -0
  409. package/src/execution-loop/launch-gate.ts +1 -1
  410. package/src/expression/i18n/messages.en.ts +5 -0
  411. package/src/expression/i18n/messages.ja.ts +5 -0
  412. package/src/expression/i18n/messages.ko.ts +5 -0
  413. package/src/expression/i18n/messages.zh.ts +5 -0
  414. package/src/expression/i18n/types.ts +8 -0
  415. package/src/github/pr-labels.ts +1 -0
  416. package/src/graph-kinds/registry.ts +6 -6
  417. package/src/graph-runner/graph-cli.ts +34 -2
  418. package/src/graph-runner/graph-run-api.ts +7 -0
  419. package/src/graph-runner/graph-tick.ts +73 -12
  420. package/src/graph-runner/runner.ts +94 -13
  421. package/src/graph-unify/wf-to-graph.ts +79 -0
  422. package/src/graph-wizard/generate.ts +34 -2
  423. package/src/graph-wizard/steps.ts +13 -0
  424. package/src/harness/dispatch-backpressure.ts +259 -0
  425. package/src/harness/harness-cli-command.ts +227 -50
  426. package/src/harness/harness-queue.ts +58 -14
  427. package/src/harness/harness-seams.ts +2 -20
  428. package/src/harness/harness-substrate-default.ts +72 -1
  429. package/src/harness/launch-advice.ts +23 -0
  430. package/src/harness/nested-elanous-policy.ts +4 -2
  431. package/src/hq/fence-wrapper.ts +7 -0
  432. package/src/hq/hq.ts +33 -2
  433. package/src/hq/lease.ts +13 -0
  434. package/src/hq/seats.ts +114 -18
  435. package/src/index.ts +199 -71
  436. package/src/intake-plane/collect-github.ts +19 -1
  437. package/src/intake-plane/digest.ts +45 -20
  438. package/src/intake-plane/lens.ts +36 -8
  439. package/src/intelligence-map/model-alias.ts +6 -0
  440. package/src/intelligence-map/model-catalog.ts +41 -0
  441. package/src/knowledge/conclusion-compress.ts +93 -0
  442. package/src/knowledge/kgs/pack.ts +5 -2
  443. package/src/knowledge/kgs/sqlite-store.ts +15 -0
  444. package/src/knowledge/know-find.ts +9 -2
  445. package/src/knowledge/query.ts +20 -0
  446. package/src/knowledge/tools/knowledge-query.ts +20 -2
  447. package/src/knowledge/write.ts +234 -2
  448. package/src/llm/bedrock-provider.ts +175 -0
  449. package/src/llm/bedrock.ts +345 -0
  450. package/src/llm/provider-credentials.ts +3 -1
  451. package/src/llm.ts +332 -51
  452. package/src/loops/budget.ts +150 -0
  453. package/src/loops/neighbor-runtime.ts +88 -0
  454. package/src/loops/orchestrator/author-depth.ts +29 -11
  455. package/src/loops/orchestrator/overlap.ts +7 -1
  456. package/src/loops/orchestrator/resync-shadow.ts +1 -1
  457. package/src/loops/orchestrator/seat-share.ts +6 -49
  458. package/src/loops/orchestrator/tick.ts +35 -2
  459. package/src/loops/orchestrator/traffic.ts +6 -80
  460. package/src/loops/registry.ts +5 -0
  461. package/src/market/publish.ts +17 -1
  462. package/src/market/signed-index.ts +42 -0
  463. package/src/maturity/feature-maturity.ts +2 -1
  464. package/src/mcp/mcp-oauth.ts +136 -17
  465. package/src/model-tier/llm-tier-map.ts +48 -4
  466. package/src/models/prompts.ts +22 -1
  467. package/src/mss/logging/horizon-watch.ts +168 -0
  468. package/src/mss/logging/log-store.ts +204 -15
  469. package/src/mss/logging/redaction.ts +17 -0
  470. package/src/native-tool-catalog.ts +1 -1
  471. package/src/nexus/api/consult-requests.ts +7 -10
  472. package/src/nexus/api/discord-trigger-bot.ts +6 -3
  473. package/src/nexus/api/draft-metrics.ts +22 -8
  474. package/src/nexus/api/error-reports.ts +30 -8
  475. package/src/nexus/api/graph-peer-edit.ts +77 -0
  476. package/src/nexus/api/graph-wizard-api.ts +9 -1
  477. package/src/nexus/api/graphs-api.ts +145 -7
  478. package/src/nexus/api/http-server.ts +101 -8
  479. package/src/nexus/api/loop-resources.ts +42 -0
  480. package/src/nexus/api/outbound-report.ts +8 -3
  481. package/src/nexus/api/plugins-market.ts +24 -0
  482. package/src/nexus/api/telegram-trigger-bot.ts +6 -3
  483. package/src/nexus/index.ts +13 -2
  484. package/src/nexus/outbound/format.ts +2 -0
  485. package/src/nexus/outbound/router.ts +198 -13
  486. package/src/oauth/claude-pty-maturity.ts +24 -0
  487. package/src/oauth/codex-account-rotation.ts +16 -18
  488. package/src/oauth/codex-account-store.ts +134 -5
  489. package/src/oauth/fallback-chain.ts +50 -15
  490. package/src/onboarding.ts +2 -1
  491. package/src/plugins/adapters/claude-package.ts +4 -12
  492. package/src/plugins/core/capability-policy.ts +141 -0
  493. package/src/plugins/core/host.ts +21 -8
  494. package/src/plugins/core/manifest.ts +19 -2
  495. package/src/plugins/core/trust-store.ts +15 -0
  496. package/src/plugins/core/types.ts +12 -0
  497. package/src/plugins/install/market-fetch.ts +68 -16
  498. package/src/plugins/install/plugin-install.ts +187 -16
  499. package/src/plugins/maker/market-bundle.ts +9 -0
  500. package/src/plugins/maker/plugin-maker.ts +32 -21
  501. package/src/plugins/maker/wizard-generate.ts +87 -9
  502. package/src/policy/llm-policy.ts +24 -6
  503. package/src/process/pid-liveness.ts +1 -1
  504. package/src/prompt-cache/openai.ts +10 -6
  505. package/src/prompt-cache/openrouter.ts +94 -0
  506. package/src/prompt-enhance/enhance.ts +46 -5
  507. package/src/prompt-library/family-prompt-coverage.ts +1 -0
  508. package/src/prompt-library/universal-preamble.ts +2 -2
  509. package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
  510. package/src/registry/loader.ts +6 -1
  511. package/src/release-loop/checklist-board.ts +27 -0
  512. package/src/release-loop/checklist.ts +62 -9
  513. package/src/release-loop/feature-store.ts +3 -0
  514. package/src/release-loop/gate-shards.ts +48 -4
  515. package/src/release-loop/landed-to-green.ts +81 -0
  516. package/src/release-loop/light-rc.ts +11 -1
  517. package/src/release-loop/merged-pr-checklist.ts +10 -0
  518. package/src/release-loop/placement.ts +91 -22
  519. package/src/release-loop/rehearsal.ts +317 -0
  520. package/src/release-loop/release-schedule.ts +13 -1
  521. package/src/release-loop/rubric-types.ts +1 -0
  522. package/src/release-loop/rubric.ts +2 -2
  523. package/src/retro/daily.ts +23 -9
  524. package/src/seat-address/seat-address.ts +1 -0
  525. package/src/seat-loop/seat-loop.ts +208 -21
  526. package/src/seat-loop/seat-report.ts +29 -1
  527. package/src/seat-loop/traffic-signal.ts +44 -0
  528. package/src/self-dev/ask-launch-flow.ts +2 -2
  529. package/src/self-dev/dev-cli.ts +157 -11
  530. package/src/self-dev/dev-pipeline.ts +18 -1
  531. package/src/self-dev/draft-residue.ts +463 -0
  532. package/src/self-dev/draft-sweep.ts +195 -17
  533. package/src/self-dev/draft-triage-rules.ts +27 -1
  534. package/src/self-dev/launch-preflight-text.ts +549 -0
  535. package/src/self-dev/launch-preflight.ts +10 -545
  536. package/src/self-dev/orchestrate-cli.ts +57 -2
  537. package/src/self-dev/orchestrate-summary-line.ts +13 -0
  538. package/src/self-dev/orchestrate.ts +9 -133
  539. package/src/self-dev/review-provider-fallback.ts +179 -0
  540. package/src/self-dev/run-store.ts +1 -3
  541. package/src/self-dev/run-supervisor.ts +8 -23
  542. package/src/self-dev/run-types.ts +178 -0
  543. package/src/self-dev/shard-aggregate.ts +61 -13
  544. package/src/self-dev/shard-plan.ts +5 -1
  545. package/src/self-dev/shard-run.ts +23 -5
  546. package/src/self-dev/supervisor-verdict-edges.ts +2 -4
  547. package/src/self-implement/blocked-draft-policy.ts +3 -19
  548. package/src/self-implement/draft-on-stop-mode.ts +18 -0
  549. package/src/self-implement/follow-up-goals.ts +114 -38
  550. package/src/self-implement/gate-baseline-reap.ts +157 -0
  551. package/src/self-implement/gate-baseline.ts +78 -16
  552. package/src/self-implement/gate-cli.ts +12 -2
  553. package/src/self-implement/gate-remote.ts +17 -6
  554. package/src/self-implement/goal-author.ts +24 -4
  555. package/src/self-implement/goal-digest.ts +19 -0
  556. package/src/self-implement/graph-overlay-condition.ts +4 -0
  557. package/src/self-implement/harness-policy-gates.ts +7 -5
  558. package/src/self-implement/headless-elanous-driver.ts +11 -9
  559. package/src/self-implement/host-regate.ts +148 -43
  560. package/src/self-implement/l8-merge-queue.ts +147 -13
  561. package/src/self-implement/merge-train.ts +131 -0
  562. package/src/self-implement/off-diff-evidence.ts +9 -0
  563. package/src/self-implement/orchestrator.ts +201 -43
  564. package/src/self-implement/review-finding-recurrence.ts +28 -0
  565. package/src/self-implement/rework-policy.ts +2 -1
  566. package/src/self-implement/round-summary-card.ts +63 -0
  567. package/src/self-implement/run-ledger.ts +4 -3
  568. package/src/self-implement/run-step-projection.ts +147 -0
  569. package/src/self-implement/run-stop.ts +1 -1
  570. package/src/self-implement/seams.ts +41 -6
  571. package/src/self-implement/self-implement-cli.ts +6 -0
  572. package/src/self-implement/train-gate-remote.ts +158 -0
  573. package/src/session-runtime/index.ts +10 -1
  574. package/src/session-runtime/retry-api.ts +11 -1
  575. package/src/shell-env-bootstrap.ts +19 -4
  576. package/src/skills/gift-install.ts +119 -0
  577. package/src/skills/runner.ts +59 -2
  578. package/src/skills/skill-feedback.ts +31 -1
  579. package/src/skills/skill-tier.ts +1 -0
  580. package/src/smart-wizard/capability-index.ts +231 -0
  581. package/src/smart-wizard/rung.ts +74 -0
  582. package/src/steward/launch.ts +20 -9
  583. package/src/steward/triage.ts +29 -10
  584. package/src/storage/file-lock.ts +40 -4
  585. package/src/task-agent/actions.ts +4 -0
  586. package/src/task-agent/card-evidence.ts +59 -13
  587. package/src/task-agent/completion-evidence.ts +31 -3
  588. package/src/task-agent/cover.ts +61 -0
  589. package/src/task-agent/judge.ts +4 -3
  590. package/src/task-agent/land-worktree.ts +201 -0
  591. package/src/task-agent/live-move-history.ts +133 -0
  592. package/src/task-agent/live-moves.ts +222 -12
  593. package/src/task-agent/parent-host.ts +117 -8
  594. package/src/task-agent/rejudge-on-head.ts +297 -0
  595. package/src/task-agent/review-result-capture.ts +218 -0
  596. package/src/task-agent/shadow.ts +118 -7
  597. package/src/task-agent/task-hand.ts +138 -11
  598. package/src/task-orchestrator/surfaces/pod-child-providers.ts +29 -0
  599. package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
  600. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +16 -2
  601. package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
  602. package/src/task-orchestrator/surfaces/pod-lease.ts +93 -12
  603. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +25 -2
  604. package/src/task-orchestrator/surfaces/pod-pool.ts +203 -28
  605. package/src/task-orchestrator/surfaces/self-implement-pod.ts +259 -36
  606. package/src/task-orchestrator/surfaces/self-implement.ts +11 -0
  607. package/src/telegram-commands.ts +8 -0
  608. package/src/telegram-project-command.ts +6 -0
  609. package/src/telegram-service.ts +5 -1
  610. package/src/telegram.ts +17 -4
  611. package/src/time/format.ts +13 -4
  612. package/src/tool-runtime/goal-author-runtime.ts +1 -0
  613. package/src/tool-runtime/self-cognition-catalog.ts +23 -0
  614. package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
  615. package/src/typecheck-ratchet.ts +2 -1
  616. package/src/user-config.ts +167 -28
  617. package/src/ux-sim/slash-menu-frames.ts +168 -0
  618. package/src/version/packed-revision.json +1 -1
  619. package/src/web-terminal/preview-tap-registry.ts +5 -2
  620. package/src/workflow-runtime/executor.ts +24 -2
  621. package/src/workflow-runtime/triggers/registry.ts +27 -0
  622. package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
  623. package/src/workflow-runtime/triggers/scheduler.ts +1 -0
  624. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
  625. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
  626. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
  627. package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
  629. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
  630. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
  631. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
  633. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
  638. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
  647. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
  648. package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
  661. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
  669. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
  674. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
  676. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
  680. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
  684. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
  689. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
  690. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
  692. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
  693. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
  701. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
  704. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
  714. package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
  717. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
  721. package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
  723. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d781be3e3a9cfd74.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/board/page-b0a80997903852d8.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/botlab/page-2b1263165630d9c8.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/chat/page-e2d6bed48ac20120.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/control/page-544dd4bd4847eeb7.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/decisions/page-3ee191319bf97d87.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/exec/page-a449f1ed472b81c2.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/field/page-9045094d6d3b5cd0.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/inside/page-c06673596849ef45.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/layout-9f81233f29dcd409.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/app/morning/page-7a12f61be4a579d1.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/app/not-found-c85b0589591a99c9.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/app/observatory/page-725a4da02dd129eb.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/app/page-21bec4afeb2f0917.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/app/reflection/page-dc39ee7a36d26385.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-84ae759f76053672.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/app/sessions/page-3db58ddb61fe2be9.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c5315e30f3528dc4.js +0 -1
  758. package/apps/pwa/out/_next/static/chunks/app/settings/page-2fe368efb6de3b6a.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +0 -1
  760. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/app/share/page-c69762987b751174.js +0 -1
  763. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +0 -1
  764. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +0 -1
  765. package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +0 -1
  766. package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +0 -1
  767. package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +0 -1
  768. package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +0 -1
  769. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +0 -1
  770. package/apps/pwa/out/_next/static/chunks/app/workflows/page-7f8d7fcdf058a845.js +0 -1
  771. package/apps/pwa/out/_next/static/chunks/app/workspace/page-22163556cd38bc88.js +0 -1
  772. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +0 -1
  773. package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
  774. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
  775. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
  776. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
  777. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
  778. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
  779. package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +0 -1
  780. /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → rfkMm4movqkqMebBrCXiR}/_ssgManifest.js +0 -0
@@ -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
- // Router is per-channel fail-soft; `delivered` = any channel ok.
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 result = await routeOutbound(cfg, { text, markdown: body.markdown ?? true, kind }, routerDeps);
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);
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import { listWizardDrafts, saveWizardDraft, type WizardResearchDraft } from '../../plugins/maker/wizard-generate.js';
2
3
  import { dirname, join } from 'node:path';
3
4
  import { elanousStateRoot } from '../../autopilot/state-paths.js';
4
5
  import { debug } from '../../debug/log.js';
@@ -120,6 +121,29 @@ export function handlePluginsIndexGet(req: Request, opts: MetaApiOpts, root?: st
120
121
  return jsonResponse(readMarketIndex(root, configuredTrustedKeys(configPath)));
121
122
  }
122
123
 
124
+ export function handlePluginWizardList(req: Request, opts: MetaApiOpts, root = elanousStateRoot()): Response {
125
+ if (!checkAuth(req, opts)) return jsonResponse({ error: 'unauthorized' }, 401);
126
+ return jsonResponse({ plugins: listWizardDrafts(join(root, 'plugins-local')) });
127
+ }
128
+
129
+ export async function handlePluginWizardSave(req: Request, opts: MetaApiOpts, root = elanousStateRoot()): Promise<Response> {
130
+ if (!checkAuth(req, opts)) return jsonResponse({ error: 'unauthorized' }, 401);
131
+ let body: unknown;
132
+ try { body = await req.json(); } catch { return jsonResponse({ error: 'bad_request' }, 400); }
133
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'bad_request' }, 400);
134
+ const input = body as { name?: unknown; draft?: unknown; regenerate?: unknown };
135
+ if (typeof input.name !== 'string' || !/^[a-z0-9][a-z0-9-]{1,31}$/.test(input.name) ||
136
+ !input.draft || typeof input.draft !== 'object' || Array.isArray(input.draft) ||
137
+ (input.regenerate !== undefined && typeof input.regenerate !== 'boolean')) return jsonResponse({ error: 'bad_request' }, 400);
138
+ try {
139
+ saveWizardDraft(join(root, 'plugins-local'), input.name, input.draft as WizardResearchDraft, input.regenerate === true);
140
+ return jsonResponse({ name: input.name, saved: true });
141
+ } catch (error) {
142
+ const reason = error instanceof Error ? error.message : 'invalid draft';
143
+ return jsonResponse({ error: 'invalid_wizard_draft', reason: /already exists|research draft/.test(reason) ? reason : 'invalid draft' }, 422);
144
+ }
145
+ }
146
+
123
147
  export function handlePluginsGet(req: Request, opts: MetaApiOpts, root?: string): Response {
124
148
  if (!checkAuth(req, opts)) return jsonResponse({ error: 'unauthorized' }, 401);
125
149
  return jsonResponse(listInstalledPlugins(root));
@@ -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: tap.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
  };
@@ -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;
@@ -2743,11 +2752,12 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
2743
2752
  if ((getUserConfig().raw?.decisions as { cards?: unknown } | undefined)?.cards !== false && !isAutonomousRunContext()) {
2744
2753
  try {
2745
2754
  const { DecisionCardService } = await import('../decisions/decision-cards.js');
2746
- const { webPushDecisionTransport } = await import('../decisions/web-push-decision-cards.js');
2755
+ const { webPushDecisionTransport, webPushUrgency } = await import('../decisions/web-push-decision-cards.js');
2747
2756
  const { listSubscriptions } = await import('../web-push/subscriptions.js');
2748
2757
  const { DecisionLedger } = await import('../decisions/decision-ledger.js');
2749
2758
  const ledger = new DecisionLedger();
2750
- const service = new DecisionCardService({ transport: webPushDecisionTransport(ledger), ownerIds: [], ledger });
2759
+ const service = new DecisionCardService({ transport: webPushDecisionTransport(ledger), ownerIds: [], ledger,
2760
+ sendFilter: (entry, now) => webPushUrgency(entry, now) !== 'not-urgent', deferredReason: webPushUrgency });
2751
2761
  await service.tick();
2752
2762
  webPushDecisionTimer = setInterval(() => {
2753
2763
  void service.tick().catch((error: unknown) => {
@@ -3382,6 +3392,7 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
3382
3392
  if (replayArmerHandle) { clearInterval(replayArmerHandle); replayArmerHandle = undefined; }
3383
3393
  // §P2 — stop the session presence grace sweeper.
3384
3394
  if (presenceSweepHandle) { clearInterval(presenceSweepHandle); presenceSweepHandle = undefined; }
3395
+ if (errorReportPrune) { errorReportPrune.stop(); errorReportPrune = undefined; }
3385
3396
  // ★ UR4d — stop the coordinator low-latency push watcher(logs.db tail + debounce).
3386
3397
  if (coordinatorPushWatcher) { try { coordinatorPushWatcher.stop(); } catch { /* ignore */ } coordinatorPushWatcher = undefined; }
3387
3398
  // Phase 2 (2026-05-13) — dispose the TOX boot handle. dispose()
@@ -12,6 +12,8 @@ export interface OutboundMsg {
12
12
  text: string;
13
13
  markdown: boolean;
14
14
  kind: string;
15
+ /** Optional routing role; absent preserves per-kind fan-out. */
16
+ role?: string;
15
17
  }
16
18
 
17
19
  export interface FormattedMessage {
@@ -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') continue;
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
- return { channels, ...(routes ? { routes } : {}), ...(awayRoutes ? { awayRoutes } : {}) };
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(cfg: UserConfig, msg: OutboundMsg, deps: RouterDeps): Promise<ChannelResult> {
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
- list = outbound.channels.filter(ch => awayRoute.includes(ch.type));
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 channels = await Promise.all(list.map(async (ch): Promise<ChannelResult> => {
393
+ const send = async (ch: OutboundChannelConfig): Promise<ChannelResult> => {
244
394
  try {
245
- if (ch.type === 'telegram') return await deliverTelegram(cfg, outMsg, deps);
246
- if (ch.type === 'discord') return await deliverDiscord(ch, outMsg, deps);
247
- return await deliverPushcut(ch, outMsg, deps);
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
- return { type: ch.type, ok: false, error: e instanceof Error ? e.message : String(e) };
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) {
@@ -0,0 +1,24 @@
1
+ export type ClaudePtyMaturity =
2
+ | { readonly mature: true }
3
+ | { readonly mature: false; readonly why: 'too-few-samples' | 'completion-below-70' | 'unknown-screen-above-10' | 'intervention-above-0.2' | 'sample-unreadable' };
4
+
5
+ export type ClaudePtyMaturitySample = {
6
+ readonly runCount: number;
7
+ readonly completedCount: number;
8
+ readonly unknownScreenCount: number;
9
+ readonly interventionCount: number;
10
+ };
11
+
12
+ /** 최근 런의 계수만 판정한다. 표본을 못 읽거나 계수가 불가능하면 성숙으로 추정하지 않는다. */
13
+ export function judgeClaudePtyMaturity(sample: ClaudePtyMaturitySample | null | undefined): ClaudePtyMaturity {
14
+ if (!sample || ![sample.runCount, sample.completedCount, sample.unknownScreenCount, sample.interventionCount]
15
+ .every((count) => Number.isSafeInteger(count) && count >= 0)
16
+ || sample.completedCount > sample.runCount || sample.unknownScreenCount > sample.runCount) {
17
+ return { mature: false, why: 'sample-unreadable' };
18
+ }
19
+ if (sample.runCount < 10) return { mature: false, why: 'too-few-samples' };
20
+ if (sample.completedCount / sample.runCount < 0.7) return { mature: false, why: 'completion-below-70' };
21
+ if (sample.unknownScreenCount / sample.runCount > 0.1) return { mature: false, why: 'unknown-screen-above-10' };
22
+ if (sample.interventionCount / sample.runCount > 0.2) return { mature: false, why: 'intervention-above-0.2' };
23
+ return { mature: true };
24
+ }
@@ -4,7 +4,7 @@
4
4
  // 주간 창이 찬 계정으로 계속 쏘면 런이 「구현 결손」으로 죽는다(그 어휘는 `#7123` 이 냈다).
5
5
  //
6
6
  // ⛔⭐⭐ **결정 다섯** (대표):
7
- // ① 계정 «전환»만 자동이다 — 리셋 크레딧의 실제 소비는 이 판정 밖의 기존 경로가 맡는다.
7
+ // ① 리셋권 소비는 순수 판정 밖의 적용 경로가 기존 소비 연산으로 맡는다.
8
8
  // ② 기본 ON. config `llm.codexAccountRotation: false` 로 끈다.
9
9
  // ③ 사람이 «명시»한 계정은 전환하지 않는다 — 의도가 이긴다.
10
10
  // ④ 현재 사용률을 모르면 안전한 기본 계정에 고정하지 않는다 — 쓸 후보가 있으면 넘긴다.
@@ -185,23 +185,6 @@ export function decideCodexRotation(input: RotationInput): RotationDecision {
185
185
  });
186
186
  if (input.explicit) return { reason: 'explicit', candidateCount, accountThresholds };
187
187
  if (!input.enabled) return { reason: 'disabled', candidateCount, accountThresholds, ...(input.disabledProvenance ? { disabledProvenance: input.disabledProvenance } : {}) };
188
- if (input.creditPace?.active) {
189
- const self = input.candidates.find((candidate) => candidate.name === input.current.name);
190
- const eligible = (name: string, balance: number | undefined, hasCredits: boolean | undefined, home: string | undefined): boolean =>
191
- !!home?.trim() && typeof balance === 'number' && Number.isFinite(balance) && balance > 0
192
- && hasCredits !== false && accountThresholds.some((row) => row.name === name
193
- && (row.status === 'reached' || row.status === 'threshold-reached'));
194
- const currentBalance = input.currentCreditBalance ?? self?.creditBalance;
195
- const currentHas = input.currentHasCredits ?? self?.hasCredits;
196
- const to = otherCandidates.filter((candidate) => eligible(candidate.name, candidate.creditBalance, candidate.hasCredits, candidate.home))
197
- .sort((a, b) => (b.creditBalance! - a.creditBalance!) || (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))[0];
198
- if (eligible(input.current.name, currentBalance, currentHas, self?.home ?? input.current.home)) {
199
- return { reason: 'credit-pace', candidateCount, accountThresholds };
200
- }
201
- if (to) {
202
- return { reason: 'credit-pace', candidateCount, accountThresholds, to };
203
- }
204
- }
205
188
  const currentUsageUnknown = input.currentReached === undefined && input.currentUsedPercent == null;
206
189
  if (input.currentReached !== true && !currentUsageUnknown && accountThresholds[0]?.status !== 'threshold-reached') {
207
190
  return { reason: 'not-reached', candidateCount, accountThresholds };
@@ -232,6 +215,21 @@ export function decideCodexRotation(input: RotationInput): RotationDecision {
232
215
  const to = usable[0];
233
216
  if (!to) {
234
217
  if (input.resetCreditAvailability === 'available') return { reason: 'reset-credit-available', candidateCount, accountThresholds };
218
+ if (input.creditPace?.active) {
219
+ const self = input.candidates.find((candidate) => candidate.name === input.current.name);
220
+ const eligible = (name: string, balance: number | undefined, hasCredits: boolean | undefined, home: string | undefined): boolean =>
221
+ !!home?.trim() && typeof balance === 'number' && Number.isFinite(balance) && balance > 0
222
+ && hasCredits !== false && accountThresholds.some((row) => row.name === name
223
+ && (row.status === 'reached' || row.status === 'threshold-reached'));
224
+ const currentBalance = input.currentCreditBalance ?? self?.creditBalance;
225
+ const currentHas = input.currentHasCredits ?? self?.hasCredits;
226
+ const richest = otherCandidates.filter((candidate) => eligible(candidate.name, candidate.creditBalance, candidate.hasCredits, candidate.home))
227
+ .sort((a, b) => (b.creditBalance! - a.creditBalance!) || (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))[0];
228
+ if (eligible(input.current.name, currentBalance, currentHas, self?.home ?? input.current.home)) {
229
+ return { reason: 'credit-pace', candidateCount, accountThresholds };
230
+ }
231
+ if (richest) return { reason: 'credit-pace', candidateCount, accountThresholds, to: richest };
232
+ }
235
233
  // 대표 크레딧 허가: 구독 잔량이 남은 계정이 없으면 크레딧으로 계속 — 한도가 찬 요청은 서버가 선불 크레딧으로 넘긴다.
236
234
  // 🩸 09-28 실측: default 크레딧만 시간당 ~5,200 씩 줄고 team·third(합 ~5.5만)는 0 — «지금 계정에 머문다»만으로는
237
235
  // 한 계정 크레딧이 바닥나면 남은 크레딧을 두고 폴백(grok)으로 갔다. ⇒ 크레딧이 «더 많이» 남은 계정으로 옮긴다.