elanous 0.2.22 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (705) 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-80ed511e02c81412.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/{aBi6EyMtLsrl7J8eH4kF7 → 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/log-horizon/log-horizon-watch.yaml +25 -0
  288. package/graphs/log-horizon/recipes.yaml +3 -0
  289. package/graphs/release/recipes.yaml +1 -1
  290. package/graphs/release/release-loop.yaml +6 -2
  291. package/graphs/release-rehearsal/recipes.yaml +12 -0
  292. package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
  293. package/package.json +4 -1
  294. package/scripts/bench-report.ts +82 -3
  295. package/scripts/ci-typecheck-changed.ts +10 -1
  296. package/scripts/hq/hq-fence-wrapper.sh +148 -0
  297. package/scripts/lib/typecheck-ratchet.ts +51 -0
  298. package/scripts/openai-relay-server.ts +5 -1
  299. package/scripts/release-loop/docs-node.ts +258 -43
  300. package/scripts/release-loop/gate-method.ts +59 -0
  301. package/scripts/release-loop/gate-node.ts +73 -7
  302. package/scripts/release-loop/gate-wall.ts +208 -0
  303. package/scripts/release-loop/known-issues-node.ts +53 -8
  304. package/scripts/release-loop/publish-at.ts +14 -0
  305. package/scripts/release-loop/publish-node.ts +17 -2
  306. package/scripts/release-loop/resume-release.ts +34 -4
  307. package/scripts/release-loop/resume.ts +53 -0
  308. package/scripts/release-loop/shard-plan.ts +3 -11
  309. package/scripts/release-loop/tui-node-checks.ts +22 -0
  310. package/scripts/release-loop/unattended-release.ts +8 -1
  311. package/scripts/rhythm/daily.ts +51 -2
  312. package/scripts/rhythm/weekly.ts +3 -2
  313. package/scripts/tsc-main-safety-net.ts +189 -0
  314. package/scripts/usage-rollup.ts +8 -4
  315. package/src/acp/codex-plugins.ts +48 -6
  316. package/src/agent/monad-agent-turn.ts +67 -5
  317. package/src/agent/run-context.ts +10 -0
  318. package/src/agent/tool-profile.ts +28 -2
  319. package/src/agent-mission/acp-judge.ts +8 -2
  320. package/src/agent-mission/capabilities-cli.ts +16 -1
  321. package/src/agent-mission/driver.ts +1 -1
  322. package/src/agent-mission/pr-review-watch.ts +6 -2
  323. package/src/agent-mission/review-loop.ts +33 -5
  324. package/src/agent-substrate/acp-reviewer.ts +17 -8
  325. package/src/agent-substrate/pr-reviewer.ts +164 -28
  326. package/src/agent-substrate/review-intent.ts +6 -4
  327. package/src/agent-substrate/self-review-cli.ts +111 -16
  328. package/src/agent-substrate/shard-boundary-intent.ts +92 -26
  329. package/src/ask-user-question/pending-questions.ts +8 -2
  330. package/src/autopilot/build/llm-conflict-merge.ts +216 -12
  331. package/src/autopilot/codebase-grounding.ts +44 -0
  332. package/src/autopilot/llm-control-brain.ts +13 -1
  333. package/src/autopilot/mission-codebase-gate.ts +8 -77
  334. package/src/autopilot/pr-manager.ts +12 -3
  335. package/src/boot/acp-server.ts +4 -7
  336. package/src/boot/acp-socket-path.ts +7 -0
  337. package/src/briefing/brief-items.ts +21 -13
  338. package/src/chat/index.ts +12 -3
  339. package/src/cli/agent-cli.ts +20 -0
  340. package/src/cli/brief-cli.ts +27 -21
  341. package/src/cli/cli-user-error.ts +9 -2
  342. package/src/cli/log-event-names.ts +3 -0
  343. package/src/cli/logs-cli.ts +1 -0
  344. package/src/cli/mcp-login.ts +111 -37
  345. package/src/cli/plugin-cli.ts +21 -1
  346. package/src/cli/pod-cli.ts +26 -0
  347. package/src/cli/pr-cli.ts +30 -2
  348. package/src/cli/pr-land-overlap-evidence.ts +131 -0
  349. package/src/cli/release-cli.ts +53 -11
  350. package/src/cli/self-update.ts +173 -10
  351. package/src/cli/skills-cli.ts +12 -3
  352. package/src/cli/tasks-cli.ts +133 -27
  353. package/src/context-bus/context-first.ts +51 -2
  354. package/src/context-bus/context-now-surfaces.ts +13 -2
  355. package/src/context-bus/long-term-memory.ts +3 -1
  356. package/src/context-bus/nightly-condense.ts +3 -2
  357. package/src/core-turn/run-goal-loop.ts +86 -2
  358. package/src/dashboard/chat-fold.ts +20 -0
  359. package/src/dashboard/first-screen-band.ts +151 -1
  360. package/src/dashboard/index.ts +27 -5
  361. package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -3
  362. package/src/dashboard/stale-install-check.ts +13 -2
  363. package/src/dashboard/turn-stream-formatter.ts +25 -0
  364. package/src/debug/log-diet.ts +159 -0
  365. package/src/debug/log.ts +52 -17
  366. package/src/display/coordinator.ts +25 -2
  367. package/src/domains/coo-admin-tool.ts +27 -0
  368. package/src/domains/cron-match.ts +1 -0
  369. package/src/domains/outbound-alert.ts +17 -6
  370. package/src/domains/release-tool.ts +119 -10
  371. package/src/domains/schedule-registry.ts +10 -7
  372. package/src/domains/schedule-runner.ts +1 -0
  373. package/src/elanous-config-dir.ts +2 -2
  374. package/src/elanous-daemon.ts +3 -3
  375. package/src/execution-loop/launch-gate.ts +1 -1
  376. package/src/expression/i18n/messages.en.ts +5 -0
  377. package/src/expression/i18n/messages.ja.ts +5 -0
  378. package/src/expression/i18n/messages.ko.ts +5 -0
  379. package/src/expression/i18n/messages.zh.ts +5 -0
  380. package/src/expression/i18n/types.ts +8 -0
  381. package/src/github/pr-labels.ts +1 -0
  382. package/src/graph-runner/runner.ts +54 -5
  383. package/src/harness/dispatch-backpressure.ts +259 -0
  384. package/src/harness/harness-cli-command.ts +107 -28
  385. package/src/harness/harness-seams.ts +2 -20
  386. package/src/harness/harness-substrate-default.ts +72 -1
  387. package/src/harness/nested-elanous-policy.ts +4 -2
  388. package/src/hq/fence-wrapper.ts +7 -0
  389. package/src/hq/hq.ts +33 -2
  390. package/src/hq/lease.ts +13 -0
  391. package/src/hq/seats.ts +114 -18
  392. package/src/index.ts +163 -64
  393. package/src/intake-plane/collect-github.ts +19 -1
  394. package/src/intake-plane/digest.ts +45 -20
  395. package/src/intake-plane/lens.ts +36 -8
  396. package/src/intelligence-map/model-alias.ts +6 -0
  397. package/src/intelligence-map/model-catalog.ts +41 -0
  398. package/src/knowledge/kgs/pack.ts +5 -2
  399. package/src/knowledge/kgs/sqlite-store.ts +15 -0
  400. package/src/knowledge/tools/knowledge-query.ts +20 -2
  401. package/src/knowledge/write.ts +234 -2
  402. package/src/llm/bedrock-provider.ts +175 -0
  403. package/src/llm/bedrock.ts +345 -0
  404. package/src/llm/provider-credentials.ts +3 -1
  405. package/src/llm.ts +306 -35
  406. package/src/loops/budget.ts +150 -0
  407. package/src/loops/orchestrator/seat-share.ts +6 -49
  408. package/src/loops/orchestrator/traffic.ts +5 -81
  409. package/src/market/signed-index.ts +42 -0
  410. package/src/mcp/mcp-oauth.ts +136 -17
  411. package/src/model-tier/llm-tier-map.ts +48 -4
  412. package/src/models/prompts.ts +22 -1
  413. package/src/mss/logging/horizon-watch.ts +168 -0
  414. package/src/mss/logging/log-store.ts +204 -15
  415. package/src/native-tool-catalog.ts +1 -1
  416. package/src/nexus/api/consult-requests.ts +7 -10
  417. package/src/nexus/api/discord-trigger-bot.ts +6 -3
  418. package/src/nexus/api/error-reports.ts +30 -8
  419. package/src/nexus/api/http-server.ts +8 -0
  420. package/src/nexus/api/loop-resources.ts +42 -0
  421. package/src/nexus/api/outbound-report.ts +8 -3
  422. package/src/nexus/api/telegram-trigger-bot.ts +6 -3
  423. package/src/nexus/index.ts +10 -0
  424. package/src/nexus/outbound/format.ts +2 -0
  425. package/src/nexus/outbound/router.ts +198 -13
  426. package/src/oauth/codex-account-rotation.ts +16 -18
  427. package/src/oauth/codex-account-store.ts +134 -5
  428. package/src/oauth/fallback-chain.ts +28 -6
  429. package/src/onboarding.ts +2 -1
  430. package/src/plugins/adapters/claude-package.ts +4 -12
  431. package/src/plugins/core/capability-policy.ts +141 -0
  432. package/src/plugins/core/host.ts +21 -8
  433. package/src/plugins/core/manifest.ts +19 -2
  434. package/src/plugins/core/trust-store.ts +15 -0
  435. package/src/plugins/core/types.ts +12 -0
  436. package/src/plugins/install/market-fetch.ts +68 -16
  437. package/src/plugins/install/plugin-install.ts +187 -16
  438. package/src/plugins/maker/plugin-maker.ts +25 -20
  439. package/src/plugins/maker/wizard-generate.ts +16 -2
  440. package/src/policy/llm-policy.ts +14 -1
  441. package/src/process/pid-liveness.ts +1 -1
  442. package/src/prompt-cache/openai.ts +10 -6
  443. package/src/prompt-cache/openrouter.ts +94 -0
  444. package/src/prompt-enhance/enhance.ts +46 -5
  445. package/src/prompt-library/family-prompt-coverage.ts +1 -0
  446. package/src/prompt-library/universal-preamble.ts +2 -2
  447. package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
  448. package/src/registry/loader.ts +6 -1
  449. package/src/release-loop/checklist.ts +24 -9
  450. package/src/release-loop/feature-store.ts +3 -0
  451. package/src/release-loop/gate-shards.ts +48 -4
  452. package/src/release-loop/landed-to-green.ts +81 -0
  453. package/src/release-loop/light-rc.ts +11 -1
  454. package/src/release-loop/merged-pr-checklist.ts +10 -0
  455. package/src/release-loop/placement.ts +91 -22
  456. package/src/release-loop/rehearsal.ts +317 -0
  457. package/src/release-loop/release-schedule.ts +13 -1
  458. package/src/release-loop/rubric-types.ts +1 -0
  459. package/src/release-loop/rubric.ts +2 -2
  460. package/src/retro/daily.ts +23 -9
  461. package/src/seat-loop/seat-loop.ts +34 -1
  462. package/src/seat-loop/traffic-signal.ts +44 -0
  463. package/src/self-dev/ask-launch-flow.ts +2 -2
  464. package/src/self-dev/dev-cli.ts +67 -3
  465. package/src/self-dev/dev-pipeline.ts +4 -6
  466. package/src/self-dev/draft-residue.ts +463 -0
  467. package/src/self-dev/draft-sweep.ts +151 -10
  468. package/src/self-dev/draft-triage-rules.ts +27 -1
  469. package/src/self-dev/launch-preflight-text.ts +549 -0
  470. package/src/self-dev/launch-preflight.ts +10 -545
  471. package/src/self-dev/orchestrate-cli.ts +57 -2
  472. package/src/self-dev/orchestrate-summary-line.ts +13 -0
  473. package/src/self-dev/orchestrate.ts +8 -135
  474. package/src/self-dev/review-provider-fallback.ts +115 -19
  475. package/src/self-dev/run-store.ts +1 -3
  476. package/src/self-dev/run-supervisor.ts +3 -22
  477. package/src/self-dev/run-types.ts +178 -0
  478. package/src/self-dev/shard-aggregate.ts +61 -13
  479. package/src/self-dev/shard-plan.ts +5 -1
  480. package/src/self-dev/shard-run.ts +23 -5
  481. package/src/self-dev/supervisor-verdict-edges.ts +2 -4
  482. package/src/self-implement/blocked-draft-policy.ts +3 -19
  483. package/src/self-implement/draft-on-stop-mode.ts +18 -0
  484. package/src/self-implement/follow-up-goals.ts +114 -38
  485. package/src/self-implement/gate-baseline-reap.ts +157 -0
  486. package/src/self-implement/gate-baseline.ts +78 -16
  487. package/src/self-implement/gate-cli.ts +12 -2
  488. package/src/self-implement/gate-remote.ts +17 -6
  489. package/src/self-implement/goal-author.ts +24 -4
  490. package/src/self-implement/goal-digest.ts +19 -0
  491. package/src/self-implement/graph-overlay-condition.ts +4 -0
  492. package/src/self-implement/headless-elanous-driver.ts +11 -9
  493. package/src/self-implement/host-regate.ts +120 -29
  494. package/src/self-implement/l8-merge-queue.ts +67 -5
  495. package/src/self-implement/merge-train.ts +131 -0
  496. package/src/self-implement/off-diff-evidence.ts +9 -0
  497. package/src/self-implement/orchestrator.ts +133 -16
  498. package/src/self-implement/review-finding-recurrence.ts +28 -0
  499. package/src/self-implement/rework-policy.ts +2 -1
  500. package/src/self-implement/round-summary-card.ts +63 -0
  501. package/src/self-implement/run-ledger.ts +4 -3
  502. package/src/self-implement/run-step-projection.ts +147 -0
  503. package/src/self-implement/run-stop.ts +1 -1
  504. package/src/self-implement/seams.ts +36 -3
  505. package/src/self-implement/self-implement-cli.ts +3 -1
  506. package/src/self-implement/train-gate-remote.ts +158 -0
  507. package/src/session-runtime/index.ts +10 -1
  508. package/src/session-runtime/retry-api.ts +11 -1
  509. package/src/skills/gift-install.ts +119 -0
  510. package/src/skills/runner.ts +59 -2
  511. package/src/skills/skill-feedback.ts +31 -1
  512. package/src/skills/skill-tier.ts +1 -0
  513. package/src/smart-wizard/capability-index.ts +231 -0
  514. package/src/smart-wizard/rung.ts +74 -0
  515. package/src/steward/launch.ts +20 -9
  516. package/src/steward/triage.ts +4 -2
  517. package/src/storage/file-lock.ts +8 -1
  518. package/src/task-agent/actions.ts +4 -0
  519. package/src/task-agent/card-evidence.ts +59 -13
  520. package/src/task-agent/judge.ts +4 -3
  521. package/src/task-agent/land-worktree.ts +201 -0
  522. package/src/task-agent/live-move-history.ts +133 -0
  523. package/src/task-agent/live-moves.ts +106 -19
  524. package/src/task-agent/parent-host.ts +117 -8
  525. package/src/task-agent/rejudge-on-head.ts +297 -0
  526. package/src/task-agent/review-result-capture.ts +218 -0
  527. package/src/task-agent/shadow.ts +103 -7
  528. package/src/task-agent/task-hand.ts +136 -11
  529. package/src/task-orchestrator/surfaces/pod-child-providers.ts +29 -0
  530. package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
  531. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +16 -2
  532. package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
  533. package/src/task-orchestrator/surfaces/pod-lease.ts +93 -12
  534. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +25 -2
  535. package/src/task-orchestrator/surfaces/pod-pool.ts +66 -14
  536. package/src/task-orchestrator/surfaces/self-implement-pod.ts +259 -36
  537. package/src/task-orchestrator/surfaces/self-implement.ts +6 -2
  538. package/src/telegram-service.ts +5 -1
  539. package/src/telegram.ts +17 -4
  540. package/src/time/format.ts +13 -4
  541. package/src/tool-runtime/goal-author-runtime.ts +1 -0
  542. package/src/tool-runtime/self-cognition-catalog.ts +23 -0
  543. package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
  544. package/src/user-config.ts +132 -24
  545. package/src/ux-sim/slash-menu-frames.ts +168 -0
  546. package/src/version/packed-revision.json +1 -1
  547. package/src/web-terminal/preview-tap-registry.ts +5 -2
  548. package/src/workflow-runtime/triggers/scheduler.ts +1 -0
  549. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
  550. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
  551. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
  552. package/apps/pwa/out/_next/static/chunks/1307-08b716476f1d50af.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
  554. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
  555. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
  556. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
  558. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
  563. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
  572. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
  573. package/apps/pwa/out/_next/static/chunks/2489-d238d3e2eb5b2bf5.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
  586. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
  594. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
  599. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
  601. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
  605. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
  610. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
  615. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
  616. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
  618. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
  619. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
  627. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
  630. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
  640. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
  643. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
  647. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
  648. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d85c43f46f58716b.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/board/page-4e645600579ae952.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/botlab/page-87edbfd19117823b.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/chat/page-cbaf66b1e1804adf.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/control/page-9f5610caaa06178c.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/decisions/page-ec8f3575e339de74.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/exec/page-2a19c5cd3b562580.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/field/page-b6a09cde2e07521a.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/morning/page-786fb4504d0f2fa9.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7ec8e289b4c14df6.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-8c8ac17d52be2a79.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d359ab048d63a9d9.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/settings/page-f151cc10ad026940.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/share/page-bf495a1e284f4e00.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/workflows/page-69cb93d1a2e57e0e.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72ec543928d67c3e.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
  704. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +0 -1
  705. /package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → rfkMm4movqkqMebBrCXiR}/_ssgManifest.js +0 -0
@@ -6,12 +6,15 @@
6
6
  // ⛔ 토큰 «값»은 어디에도 출력하지 않는다 — accountId 앞 8자만(R-LLM2).
7
7
 
8
8
  import { readFile } from 'node:fs/promises';
9
- import { join, resolve } from 'node:path';
9
+ import { mkdirSync, openSync, closeSync, unlinkSync, readFileSync, writeFileSync, statSync } from 'node:fs';
10
+ import { dirname, join, resolve } from 'node:path';
11
+ import { consumeCodexResetCredits, listCodexResetCredits } from '../budget/codex-reset-credits.js';
12
+ import { codexCredentialRoot, readAvailabilityState, writeAvailabilityState } from '../budget/codex-reset-credit-state.js';
10
13
  import {
11
14
  DEFAULT_CODEX_ACCOUNT, codexStoreKey, accountNameFromStoreKey, isValidAccountName, isCodexStoreKey,
12
15
  resolveCodexAccount, effectiveCodexHome, type CodexHomeSource, type CodexAccountResolution,
13
16
  } from './codex-account.js';
14
- import { authStorePath, defaultCodexHome, listProviders, loadTokens, saveTokens } from './store.js';
17
+ import { authStorePath, defaultCodexHome, isUnderTestRunner, listProviders, loadTokens, saveTokens } from './store.js';
15
18
  import { readFreshAvailabilityState, quotaSignalDir, readQuotaSignal, readQuotaSignalCredits, readQuotaSignalUsedPercent, readQuotaSignalObservedAt, readQuotaSignalObservedAtRaw } from '../budget/codex-reset-credit-state.js';
16
19
  import { creditPaceStatus } from '../budget/codex-credit-pace.js';
17
20
  import { loadLlmPolicy } from '../policy/llm-policy.js';
@@ -29,6 +32,8 @@ import {
29
32
  type FallbackDecision, type FallbackInput, type RotationOutcome,
30
33
  } from './fallback-chain.js';
31
34
  import { resolveGrokCredential } from '../grok/credential.js';
35
+ import { getOpenRouterApiKey } from '../config.js';
36
+ import { lookupOpenRouterModelSpec } from '../model-tier/llm-tier-map.js';
32
37
  import { sendOutbound } from '../domains/outbound-alert.js';
33
38
 
34
39
  type CodexAccountOutboundSender = (text: string, kind?: string) => boolean;
@@ -47,7 +52,7 @@ export function _setCodexAccountOutboundSenderForTesting(sender: CodexAccountOut
47
52
  /** ⛔⭐ 정적 import 로 읽는다 — 종전 `require()` 는 ESM 에서 «정의되지 않을 수 있고», 그러면
48
53
  * fail-soft 가 삼켜 ***config false 가 영영 무시된다***(리뷰 should-fix: 노브가 no-op 이 된다).
49
54
  * ⚠️ 순환 없음을 확인했다 — user-config 는 oauth 를 안 끌어온다. */
50
- type RotationConfig = { llm?: { codexAccountRotation?: boolean; codexAccountAlerts?: boolean; codexAccountRotationThresholdPercent?: unknown; codexAccountRotationThresholdPercentByAccount?: Readonly<Record<string, unknown>>; codexCreditsAllowed?: boolean; codexQuotaPolicy?: unknown; fallbackChain?: unknown } };
55
+ type RotationConfig = { llm?: { codexAccountRotation?: boolean; codexAccountAlerts?: boolean; codexAccountRotationThresholdPercent?: unknown; codexAccountRotationThresholdPercentByAccount?: Readonly<Record<string, unknown>>; codexCreditsAllowed?: boolean; codexResetAutoConsumePerDay?: number; codexQuotaPolicy?: unknown; fallbackChain?: unknown; openrouter?: { fallbackModel?: unknown } } };
51
56
 
52
57
  function readUserConfig(): RotationConfig {
53
58
  return getUserConfig();
@@ -636,6 +641,9 @@ export function resolveCodexAccountForRun(
636
641
  to: decision.reason === 'credit-pace' ? decision.to?.name ?? current.name : decision.to?.name,
637
642
  });
638
643
  observeRotation(decision, current.name);
644
+ // 순서 ② — 구독 남은 계정이 없고 리셋권이 있으면 «스스로 1개» 쓴다(일일 상한 · 비동기 · 이번 호출은 종전대로 머문다).
645
+ // ⛔ 판정이 `reset-credit-available` 일 때만 닿는다 — 구독 남은 계정이 있으면(rotated) 여기 안 온다.
646
+ if (decision.reason === 'reset-credit-available') void autoConsumeCodexResetCredit({ storePath: path });
639
647
  const resolved = applyRotation(current, decision);
640
648
  if (resolved.source === 'rotated') {
641
649
  notifyCodexAccountEventWithUsage('rotation', path, now,
@@ -658,6 +666,111 @@ export function resolveCodexAccountForRun(
658
666
  return resolved;
659
667
  }
660
668
 
669
+ /** 리셋권 자동 소비 결과 — 소비했거나, 왜 안 했는지(관측 `reset-consume-skipped` 와 같은 이유). */
670
+ export type CodexResetAutoConsumeOutcome =
671
+ | { readonly consumed: true; readonly account: string; readonly expiresAt: string | null; readonly todayCount: number; readonly redecision?: string }
672
+ | { readonly consumed: false; readonly reason: 'disabled' | 'daily-cap-reached' | 'test-runtime-guard' | 'in-flight' | 'lock-held' | 'no-available-credit' | 'consume-failed' | 'error'; readonly detail?: string };
673
+
674
+ export interface CodexResetAutoConsumeDeps {
675
+ readonly storePath?: string;
676
+ readonly now?: number;
677
+ /** ⛔ 테스트는 «반드시» 가짜를 준다 — 안 주면 실물이고, 테스트 러너 안에서는 실물을 «부르지 않는다». */
678
+ readonly consume?: typeof consumeCodexResetCredits;
679
+ readonly list?: typeof listCodexResetCredits;
680
+ /** 일일 카운터 파일(테스트 격리). 기본 = 자격 뿌리 `budget/codex-reset-auto-consume.json`. */
681
+ readonly counterPath?: string;
682
+ readonly env?: NodeJS.ProcessEnv;
683
+ }
684
+
685
+ /** `llm.codexResetAutoConsumePerDay` — 기본 1 · 0 이면 끔 · 못 읽으면 기본. */
686
+ function resetAutoConsumePerDayFromConfig(): number {
687
+ try {
688
+ const v = (configReader().llm as { codexResetAutoConsumePerDay?: unknown } | undefined)?.codexResetAutoConsumePerDay;
689
+ return typeof v === 'number' && Number.isSafeInteger(v) && v >= 0 ? v : 1;
690
+ } catch { return 1; }
691
+ }
692
+
693
+ function readAutoConsumeCount(path: string, day: string): number {
694
+ try {
695
+ const parsed = JSON.parse(readFileSync(path, 'utf8')) as { day?: unknown; count?: unknown };
696
+ return parsed.day === day && typeof parsed.count === 'number' && Number.isFinite(parsed.count) ? parsed.count : 0;
697
+ } catch { return 0; }
698
+ }
699
+
700
+ let autoConsumeInFlight = false;
701
+
702
+ /**
703
+ * 순서 ② «리셋권» — 만료가 가장 이른 계정의 리셋권을 «하나» 쓴다. ⛔ 되돌릴 수 없다.
704
+ * ⛔ 소비는 기존 `consumeCodexResetCredits` 한 연산뿐이다(새 API 경로 없음).
705
+ * ⛔ 하루 상한(UTC 일) — 소비 «전»에 칸을 잡는다(실패해도 그날 칸은 쓴 것으로 센다: 되돌릴 수 없는 일은 덜 하는 쪽).
706
+ * 성공 → `codex.rotation reset-consumed` ⊕ 소비 알림 ⊕ 판정을 한 번 다시 돈다(조회 전용).
707
+ * 실패·상한 → `codex.rotation reset-consume-skipped { reason }` — 호출부는 종전대로 크레딧 단계로 간다.
708
+ */
709
+ export async function autoConsumeCodexResetCredit(deps: CodexResetAutoConsumeDeps = {}): Promise<CodexResetAutoConsumeOutcome> {
710
+ const skip = (reason: Extract<CodexResetAutoConsumeOutcome, { consumed: false }>['reason'], extra: Record<string, unknown> = {}): CodexResetAutoConsumeOutcome => {
711
+ debug.log('codex.rotation', 'reset-consume-skipped', { reason, ...extra });
712
+ return { consumed: false, reason, ...(typeof extra.detail === 'string' ? { detail: extra.detail } : {}) };
713
+ };
714
+ const cap = resetAutoConsumePerDayFromConfig();
715
+ if (cap <= 0) return skip('disabled', { cap });
716
+ const now = deps.now ?? Date.now();
717
+ const day = new Date(now).toISOString().slice(0, 10);
718
+ const counterPath = deps.counterPath ?? join(codexCredentialRoot(), 'budget', 'codex-reset-auto-consume.json');
719
+ const before = readAutoConsumeCount(counterPath, day);
720
+ if (before >= cap) return skip('daily-cap-reached', { cap, todayCount: before });
721
+ // ⛔⭐ 되돌릴 수 없는 실물 소비를 테스트가 «절대» 못 부르게 — 가짜를 안 줬고 테스트 러너면 멈춘다.
722
+ if ((!deps.consume || !deps.list) && isUnderTestRunner(deps.env ?? process.env)) return skip('test-runtime-guard');
723
+ const consume = deps.consume ?? consumeCodexResetCredits;
724
+ const list = deps.list ?? listCodexResetCredits;
725
+ if (autoConsumeInFlight) return skip('in-flight');
726
+ const lockPath = `${counterPath}.lock`;
727
+ let lockFd: number | undefined;
728
+ autoConsumeInFlight = true;
729
+ try {
730
+ mkdirSync(dirname(counterPath), { recursive: true });
731
+ try { lockFd = openSync(lockPath, 'wx'); } catch {
732
+ // 죽은 프로세스가 남긴 잠금(10분 넘음)은 걷고 한 번만 다시 잡는다 — 안 그러면 자동 소비가 영영 꺼진다.
733
+ try { if (now - statSync(lockPath).mtimeMs > 10 * 60_000) { unlinkSync(lockPath); lockFd = openSync(lockPath, 'wx'); } } catch { /* */ }
734
+ if (lockFd === undefined) return skip('lock-held');
735
+ }
736
+ // 다른 프로세스가 방금 썼을 수 있다 — 잠근 «뒤»에 다시 센다.
737
+ const locked = readAutoConsumeCount(counterPath, day);
738
+ if (locked >= cap) return skip('daily-cap-reached', { cap, todayCount: locked });
739
+ const path = deps.storePath ?? authStorePath();
740
+ let earliest: { name: string; home: string; expiresAt: string | null; expiresMs: number } | undefined;
741
+ for (const candidate of buildRotationCandidates(path, now, deps.env ?? process.env)) {
742
+ if (!candidate.home) continue;
743
+ const listed = await list({ authFilePath: join(candidate.home, 'auth.json'), env: { CODEX_HOME: candidate.home } as unknown as NodeJS.ProcessEnv });
744
+ if (!listed.ok) continue;
745
+ for (const credit of listed.value.credits) {
746
+ if (credit.status !== 'available' || credit.redeemed_at) continue;
747
+ const ms = credit.expires_at ? Date.parse(credit.expires_at) : Number.POSITIVE_INFINITY;
748
+ const expiresMs = Number.isFinite(ms) || ms === Number.POSITIVE_INFINITY ? ms : Number.POSITIVE_INFINITY;
749
+ if (!earliest || expiresMs < earliest.expiresMs) earliest = { name: candidate.name, home: candidate.home, expiresAt: credit.expires_at, expiresMs };
750
+ }
751
+ }
752
+ if (!earliest) return skip('no-available-credit');
753
+ const todayCount = locked + 1;
754
+ writeFileSync(counterPath, `${JSON.stringify({ day, count: todayCount, lastAccount: earliest.name, at: new Date(now).toISOString() })}\n`, 'utf8');
755
+ const result = await consume({ authFilePath: join(earliest.home, 'auth.json'), env: { CODEX_HOME: earliest.home } as unknown as NodeJS.ProcessEnv });
756
+ if (!result.ok) return skip('consume-failed', { account: earliest.name, kind: result.kind, detail: result.message.slice(0, 200), todayCount });
757
+ const previous = readAvailabilityState(earliest.home);
758
+ const remaining = typeof previous === 'number' ? Math.max(0, previous - 1) : undefined;
759
+ if (remaining !== undefined) { try { writeAvailabilityState(remaining, earliest.home); } catch { /* 관측 갱신 실패는 소비를 되돌리지 않는다 */ } }
760
+ debug.log('codex.rotation', 'reset-consumed', { account: earliest.name, expiresAt: earliest.expiresAt, todayCount });
761
+ notifyCodexResetCreditConsumed({ name: earliest.name }, remaining, { storePath: path, now });
762
+ // 판정을 한 번 다시 돈다 — 조회 전용(관측·고정 없음). 다음 런 판정이 그 계정을 구독으로 고른다.
763
+ let redecision: string | undefined;
764
+ try { redecision = inspectCodexRotation(deps.env ?? process.env, { storePath: path }).reason; } catch { /* 재판정 실패는 소비 결과를 바꾸지 않는다 */ }
765
+ return { consumed: true, account: earliest.name, expiresAt: earliest.expiresAt, todayCount, ...(redecision ? { redecision } : {}) };
766
+ } catch (error) {
767
+ return skip('error', { detail: error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200) });
768
+ } finally {
769
+ if (lockFd !== undefined) { try { closeSync(lockFd); } catch { /* */ } try { unlinkSync(lockPath); } catch { /* */ } }
770
+ autoConsumeInFlight = false;
771
+ }
772
+ }
773
+
661
774
  /**
662
775
  * ⛔⭐⭐⭐⭐⭐ **회전한 계정을 «자식 env»로만 내려보낸다** (2026-08-07 · 크레딧 유출 차단).
663
776
  *
@@ -738,6 +851,8 @@ export function resolveRunFallback(
738
851
  readonly grokAvailable?: boolean;
739
852
  /** ⛔ grok «잔량» seam — 안 주면 재지 «않고» 'unknown' 이다(이 판정은 네트워크를 안 친다). */
740
853
  readonly grokQuota?: 'usable' | 'exhausted' | 'unknown';
854
+ /** OpenRouter 키 «존재» seam(테스트 격리) — 안 주면 `getOpenRouterApiKey()` 로 잰다. 키 값은 기록하지 않는다. */
855
+ readonly openrouterAvailable?: boolean;
741
856
  } & Pick<FallbackInput, 'currentStep' | 'currentCredentialRateLimited'> = {},
742
857
  ): FallbackDecision {
743
858
  try {
@@ -777,11 +892,18 @@ export function resolveRunFallback(
777
892
  // ⛔ 「지금 쓸 수 있나」는 «자격과 다른 축»이다. 안 주면 'unknown' — 그리고 unknown 은 «통과»다.
778
893
  // ⇒ 「모르고 갔다」와 「알고 갔다」가 관측에서 갈린다(아래 grokQuota).
779
894
  const grokQuota = deps.grokQuota ?? readCachedGrokQuota();
780
- const fallbackBase = { rotation, chain, grokAvailable, grokQuota };
895
+ const fallbackModel = configReader().llm?.openrouter?.fallbackModel;
896
+ const openrouterFallbackModel = typeof fallbackModel === 'string' ? fallbackModel : undefined;
897
+ const openrouterAvailable = chain.includes('openrouter')
898
+ && (deps.openrouterAvailable ?? Boolean(getOpenRouterApiKey()?.trim()));
899
+ const fallbackBase = { rotation, chain, grokAvailable, grokQuota, openrouterFallbackModel, openrouterAvailable };
781
900
  const fallbackInput: FallbackInput = deps.currentCredentialRateLimited === true && deps.currentStep
782
901
  ? { ...fallbackBase, currentStep: deps.currentStep, currentCredentialRateLimited: true }
783
902
  : { ...fallbackBase, ...(deps.currentStep ? { currentStep: deps.currentStep } : {}) };
784
903
  const decision = decideFallback(fallbackInput);
904
+ // tier-map 칸(가격·창 근거) — 유료 전환은 «무슨 값으로 가는지»를 관측에 남긴다. 미등록이면 'unregistered'.
905
+ const openrouterSpec = decision.action === 'switch-backend' && decision.backend === 'openrouter'
906
+ ? lookupOpenRouterModelSpec(decision.model) : undefined;
785
907
 
786
908
  debug.log('oauth.fallback-chain', 'decide', {
787
909
  rotationReason: rotation.reason,
@@ -797,16 +919,23 @@ export function resolveRunFallback(
797
919
  ...(dropped.length > 0 ? { droppedSteps: dropped } : {}),
798
920
  grokAvailable,
799
921
  grokQuota,
922
+ ...(chain.includes('openrouter') ? { openrouterAvailable, openrouterModelConfigured: Boolean(openrouterFallbackModel?.trim()) } : {}),
800
923
  action: decision.action,
801
924
  ...(decision.action === 'switch-backend' ? { backend: decision.backend } : {}),
925
+ ...(decision.action === 'switch-backend' && decision.backend === 'openrouter'
926
+ ? { model: decision.model, modelSpec: openrouterSpec ? `${openrouterSpec.label} · ${openrouterSpec.rationale}` : 'unregistered' }
927
+ : {}),
802
928
  ...(decision.action === 'stay' ? { why: decision.why } : {}),
803
929
  ...(decision.action === 'codex-rotate' ? { to: decision.to.name } : {}),
804
930
  });
805
931
  if (decision.action === 'switch-backend') {
932
+ const openrouterNote = decision.backend === 'openrouter'
933
+ ? ` · API 과금 ${decision.model} (${openrouterSpec ? `${openrouterSpec.label} · ${openrouterSpec.rationale}` : 'tier-map 미등록'})`
934
+ : '';
806
935
  emitDecision({
807
936
  kind: 'ROUTE',
808
937
  what: `공급자 전환 codex → ${decision.backend}`,
809
- reason: `회전 ${rotation.reason} · 정책 ${quotaPolicy}${fallbackInput.currentCredentialRateLimited ? ' · 한도 오류' : ''}`,
938
+ reason: `회전 ${rotation.reason} · 정책 ${quotaPolicy}${openrouterNote}${fallbackInput.currentCredentialRateLimited ? ' · 한도 오류' : ''}`,
810
939
  purpose: '작업을 멈추지 않고 이어 간다',
811
940
  target: decision.backend,
812
941
  paths: chain.length,
@@ -22,7 +22,7 @@ import type { RotationCandidate } from './codex-account-rotation.js';
22
22
  import type { ClaudePtyMaturity } from './claude-pty-maturity.js';
23
23
 
24
24
  /** 체인에 놓을 수 있는 칸. ⛔ 모르는 이름은 «조용히 건너뛰지 않고» 거부한다(오타 방어). */
25
- export const FALLBACK_STEPS = ['codex-rotate', 'grok', 'claude-pty'] as const;
25
+ export const FALLBACK_STEPS = ['codex-rotate', 'grok', 'claude-pty', 'openrouter'] as const;
26
26
  export type FallbackStep = (typeof FALLBACK_STEPS)[number];
27
27
 
28
28
  /** 기본 체인 — ⛔⭐ **2026-08-20 에 `grok` 이 «기본»으로 들어왔다**(대표 지시).
@@ -78,6 +78,8 @@ export type FallbackDecision =
78
78
  | { readonly action: 'codex-rotate'; readonly to: RotationCandidate }
79
79
  /** 백엔드를 통째로 바꾼다. */
80
80
  | { readonly action: 'switch-backend'; readonly backend: 'grok' | 'claude' }
81
+ /** 명시한 유료 OpenRouter 모델로 전환한다. 호출부가 모델을 바꾸지 못하면 실행하지 않는다. */
82
+ | { readonly action: 'switch-backend'; readonly backend: 'openrouter'; readonly model: string }
81
83
  /** 아무것도 안 한다 — 이유를 «값으로» 남긴다. */
82
84
  | { readonly action: 'stay'; readonly why: StayReason };
83
85
 
@@ -98,6 +100,8 @@ export type StayReason =
98
100
  | 'grok-unavailable'
99
101
  /** grok 자격은 있는데 «잔량이 소진»됐다 — 자격 부재와 «다른 값»이다. */
100
102
  | 'grok-exhausted'
103
+ /** 유료 OpenRouter 모델 또는 키가 없다. */
104
+ | 'openrouter-unavailable'
101
105
  /** claude PTY 의 최근 표본이 성숙 관문을 통과하지 못했거나 읽히지 않는다. */
102
106
  | 'claude-pty-immature';
103
107
 
@@ -113,6 +117,10 @@ export type FallbackInput = {
113
117
  /** grok 자격이 «있나». ⛔ 「모른다」를 boolean 에 접지 마라 — 호출자가
114
118
  * `resolveGrokCredential() !== null` 로 확정해서 준다. */
115
119
  readonly grokAvailable: boolean;
120
+ /** `llm.openrouter.fallbackModel` — 기본 모델로의 유료 폴백은 허용하지 않는다. */
121
+ readonly openrouterFallbackModel?: string;
122
+ /** OpenRouter 키 존재 여부(키 값 자체는 판정·관측에 넣지 않는다). */
123
+ readonly openrouterAvailable?: boolean;
116
124
  /**
117
125
  * grok 이 «지금 쓸 수 있나» — ⛔ 자격(`grokAvailable`)과 «다른 축»이다.
118
126
  * 'usable' 쓸 수 있다(잔량 있음 · 무제한 · 임계 미만)
@@ -154,11 +162,13 @@ export function decideFallback(input: FallbackInput): FallbackDecision {
154
162
  const {
155
163
  rotation, chain, currentStep, currentCredentialRateLimited = false,
156
164
  grokAvailable, grokQuota = 'unknown', claudePtyMaturity, stayOnResetCreditAvailable = false,
165
+ openrouterFallbackModel, openrouterAvailable = false,
157
166
  } = input;
167
+ const openrouter = { model: openrouterFallbackModel, available: openrouterAvailable };
158
168
 
159
169
  if (currentCredentialRateLimited) {
160
170
  if (currentStep === undefined) return { action: 'stay', why: 'chain-exhausted' };
161
- return stepAfter(chain, currentStep, grokAvailable, grokQuota, claudePtyMaturity, 'chain-exhausted');
171
+ return stepAfter(chain, currentStep, grokAvailable, grokQuota, claudePtyMaturity, openrouter, 'chain-exhausted');
162
172
  }
163
173
  if (rotation.reason === 'explicit') return { action: 'stay', why: 'explicit' };
164
174
  if (rotation.reason === 'not-reached' || (rotation.reason === 'credit-pace' && !('to' in rotation))) {
@@ -172,13 +182,13 @@ export function decideFallback(input: FallbackInput): FallbackDecision {
172
182
  // 회전이 답을 냈는데 체인이 codex-rotate 를 «빼» 놨다면 그 뜻을 존중하고
173
183
  // 다음 칸으로 간다(회전을 원치 않는 구성).
174
184
  if (chain.includes('codex-rotate')) return { action: 'codex-rotate', to: rotation.to };
175
- return stepAfterCodex(chain, grokAvailable, grokQuota, claudePtyMaturity, 'chain-exhausted');
185
+ return stepAfterCodex(chain, grokAvailable, grokQuota, claudePtyMaturity, openrouter, 'chain-exhausted');
176
186
  }
177
187
 
178
188
  // no-candidate | disabled | reset-credit-available(기본) — codex 축이 끝났다. 다음 칸을 본다.
179
189
  // ⛔ 다음 칸이 «없을 때»의 이름만 고른다. 진행(stepAfterCodex)은 그대로다.
180
190
  const fallbackWhy = stayReasonWhenCodexAxisEnds(rotation.reason === 'credit-pace' ? 'no-candidate' : rotation.reason);
181
- return stepAfterCodex(chain, grokAvailable, grokQuota, claudePtyMaturity, fallbackWhy);
191
+ return stepAfterCodex(chain, grokAvailable, grokQuota, claudePtyMaturity, openrouter, fallbackWhy);
182
192
  }
183
193
 
184
194
  /** codex 축이 끝났는데 체인 다음 칸도 없을 때 머무는 이유.
@@ -197,9 +207,10 @@ function stepAfterCodex(
197
207
  grokAvailable: boolean,
198
208
  grokQuota: 'usable' | 'exhausted' | 'unknown',
199
209
  claudePtyMaturity: ClaudePtyMaturity | undefined,
210
+ openrouter: { readonly model?: string; readonly available: boolean },
200
211
  whyIfNone: StayReason,
201
212
  ): FallbackDecision {
202
- return stepAfter(chain, 'codex-rotate', grokAvailable, grokQuota, claudePtyMaturity, whyIfNone);
213
+ return stepAfter(chain, 'codex-rotate', grokAvailable, grokQuota, claudePtyMaturity, openrouter, whyIfNone);
203
214
  }
204
215
 
205
216
  function stepAfter(
@@ -208,12 +219,21 @@ function stepAfter(
208
219
  grokAvailable: boolean,
209
220
  grokQuota: 'usable' | 'exhausted' | 'unknown',
210
221
  claudePtyMaturity: ClaudePtyMaturity | undefined,
222
+ openrouter: { readonly model?: string; readonly available: boolean },
211
223
  whyIfNone: StayReason,
212
224
  ): FallbackDecision {
213
225
  const idx = currentStep === undefined ? -1 : chain.indexOf(currentStep);
214
226
  const rest = idx === -1 ? chain : chain.slice(idx + 1);
215
227
  let unavailableWhy: StayReason | undefined;
216
228
  for (const step of rest) {
229
+ if (step === 'openrouter') {
230
+ const model = openrouter.model?.trim();
231
+ if (!openrouter.available || !model || !/^openrouter\/[^/\s]+\/[^\s]+$/.test(model)) {
232
+ unavailableWhy = 'openrouter-unavailable';
233
+ continue;
234
+ }
235
+ return { action: 'switch-backend', backend: 'openrouter', model };
236
+ }
217
237
  if (step === 'grok') {
218
238
  // 자격 부재와 소진은 다른 값이다. 다음 칸이 있으면 그 칸을 살핀다.
219
239
  if (!grokAvailable) { unavailableWhy = 'grok-unavailable'; continue; }
@@ -236,7 +256,9 @@ export function describeFallback(decision: FallbackDecision): string {
236
256
  case 'codex-rotate':
237
257
  return `codex 계정 전환 → ${decision.to.name}`;
238
258
  case 'switch-backend':
239
- return `백엔드 전환 → ${decision.backend} (구독)`;
259
+ return decision.backend === 'openrouter'
260
+ ? `백엔드 전환 → openrouter (${decision.model} · API 과금)`
261
+ : `백엔드 전환 → ${decision.backend} (구독)`;
240
262
  case 'stay':
241
263
  return `전환 없음 (${decision.why})`;
242
264
  }
package/src/onboarding.ts CHANGED
@@ -51,6 +51,7 @@ import { DiscordBot } from './discord.js';
51
51
  import { pickCodexAuthMode, pickCodexModel } from './codex/setup.js';
52
52
  import { getInventory, resolveBaseUrl } from './llm/local-manager/manager.js';
53
53
  import { getMessages, format as i18nFormat } from './expression/i18n/index.js';
54
+ import { messagesEn } from './expression/i18n/messages.en.js';
54
55
  import type {
55
56
  ChoiceOption,
56
57
  ChooseOpts,
@@ -1562,7 +1563,7 @@ export async function runOnboarding(opts: RunWizardOpts = {}): Promise<UserConfi
1562
1563
  io.print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
1563
1564
  io.print(` ${m.setupComplete}`);
1564
1565
  // ER1 — first-run consent notice (default on · 대표 10-01): what is sent and how to turn it off.
1565
- io.print(' 오류가 나면 운영팀에 자동으로 알립니다(오류 내용·버전·무작위 설치 ID — 이름·메일 없이). 끄기: elanous config set errorReports.enabled false');
1566
+ io.print(` ${m.setupErrorReportNotice ?? messagesEn.setupErrorReportNotice}`);
1566
1567
  io.print(` Provider : ${marked.llm.provider}${marked.llm.model ? ` (${marked.llm.model})` : ''}`);
1567
1568
  // EN5 — show what the loader will actually read (resolveSkillSources), not the raw preset fields.
1568
1569
  const skillSources = resolveSkillSources(marked).filter((source) => source.enabled);
@@ -72,25 +72,17 @@ export function readClaudePackageLedger(
72
72
  return { status: 'installed-plugins-unreadable', packages: [] };
73
73
  }
74
74
 
75
- const marketplacesRaw = readJsonFile(marketplacesPath);
76
- if (marketplacesRaw === undefined) {
77
- return { status: 'known-marketplaces-unreadable', packages: [] };
78
- }
79
-
80
75
  const plugins = asPluginMap(installedRaw);
81
76
  if (plugins === undefined) {
82
77
  return { status: 'installed-plugins-unreadable', packages: [] };
83
78
  }
84
79
 
85
- const marketplaces = asMarketplaceMap(marketplacesRaw);
86
- if (marketplaces === undefined) {
87
- return { status: 'known-marketplaces-unreadable', packages: [] };
88
- }
89
-
80
+ const marketplaces = asMarketplaceMap(readJsonFile(marketplacesPath));
81
+ const status: ClaudePackageLedgerStatus = marketplaces === undefined ? 'known-marketplaces-unreadable' : 'ok';
90
82
  const packages: ClaudeInstalledPackage[] = [];
91
83
  for (const [key, records] of Object.entries(plugins)) {
92
84
  const { plugin, marketplace } = splitClaudePluginKey(key);
93
- const source = formatMarketplaceSource(marketplaces[marketplace]);
85
+ const source = formatMarketplaceSource(marketplaces?.[marketplace]);
94
86
  for (const record of records) {
95
87
  packages.push({
96
88
  name: key,
@@ -104,7 +96,7 @@ export function readClaudePackageLedger(
104
96
  }
105
97
  }
106
98
 
107
- return { status: 'ok', packages };
99
+ return { status, packages };
108
100
  }
109
101
 
110
102
  function readJsonFile(path: string): unknown {
@@ -1,3 +1,6 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
3
+ import { join, relative, resolve, sep } from 'node:path';
1
4
  import type { ExecutionSurfaceSpec } from '../../display/index.js';
2
5
  import type { PluginCapability, PluginSource } from './manifest.js';
3
6
 
@@ -83,6 +86,144 @@ export class PluginCapabilityPolicy {
83
86
  }
84
87
  }
85
88
 
89
+ export type PluginScanLevel = 'safe' | 'caution' | 'dangerous';
90
+ export interface PluginSecurityFinding { level: Exclude<PluginScanLevel, 'safe'>; code: string }
91
+ export interface PluginSecurityDecision {
92
+ scan: PluginScanLevel;
93
+ findings: PluginSecurityFinding[];
94
+ integrity: string;
95
+ license: 'redistributable' | 'restricted' | 'unknown';
96
+ }
97
+
98
+ /** Scan bytes before consent or installation. Only non-identifying codes leave this boundary. */
99
+ export function inspectPluginSecurity(dir: string): PluginSecurityDecision {
100
+ const findings: PluginSecurityFinding[] = [];
101
+ const hash = createHash('sha256');
102
+ let license: PluginSecurityDecision['license'] = 'unknown';
103
+ // Code files are collected first; import/exec checks run after we know which files are reachable at runtime.
104
+ const code = new Map<string, { text: string; test: boolean }>();
105
+ const entrypoints: string[] = [];
106
+ const visit = (folder: string): void => {
107
+ for (const entry of readdirSync(folder, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
108
+ const file = join(folder, entry.name);
109
+ const rel = relative(dir, file).replaceAll('\\', '/');
110
+ if (entry.isSymbolicLink() || (!entry.isDirectory() && !entry.isFile())) {
111
+ findings.push({ level: 'dangerous', code: 'unsafe-entry' });
112
+ hash.update(rel).update('!\0');
113
+ continue;
114
+ }
115
+ if (entry.isDirectory()) { hash.update(rel).update('/\0'); visit(file); continue; }
116
+ hash.update(rel).update('\0');
117
+ // Size is checked before reading so an oversized import cannot exhaust memory in the scanner.
118
+ const size = statSync(file).size;
119
+ if (size > 2_000_000) {
120
+ hash.update(String(size)).update('\0').update('oversize');
121
+ findings.push({ level: 'dangerous', code: 'unscannable-file' });
122
+ continue;
123
+ }
124
+ const bytes = readFileSync(file);
125
+ hash.update(String(bytes.length)).update('\0').update(bytes);
126
+ if (/(?:^|\/)(?:\.env(?:\.|$)|id_rsa$|credentials?\.json$)/i.test(rel)) {
127
+ findings.push({ level: 'dangerous', code: 'credential-file' });
128
+ }
129
+ if (/(?:api[_-]?key|secret|token)\s*[:=]\s*[A-Za-z0-9_-]{16,}/i.test(rel)) {
130
+ findings.push({ level: 'dangerous', code: 'credential-filename' });
131
+ }
132
+ if (/^(?:LICENSE|LICENCE)(?:\.[^/]*)?$/i.test(rel)) {
133
+ if (/\b(?:MIT License|Apache License, Version 2\.0|BSD [23]-Clause|ISC License)\b/i.test(bytes.toString('utf8'))) license = 'redistributable';
134
+ else if (license !== 'redistributable') license = 'restricted';
135
+ }
136
+ if (bytes.includes(0)) {
137
+ findings.push({ level: 'dangerous', code: 'unscannable-file' });
138
+ continue;
139
+ }
140
+ const text = bytes.toString('utf8');
141
+ if (/^plugin\.json$/.test(rel)) {
142
+ try {
143
+ const manifest: unknown = JSON.parse(text);
144
+ if (manifest && typeof manifest === 'object' && !Array.isArray(manifest)) {
145
+ const raw = manifest as Record<string, unknown>;
146
+ const ext = raw.extensions && typeof raw.extensions === 'object' ? (raw.extensions as Record<string, unknown>)['ai.elanous'] : undefined;
147
+ for (const main of [raw.main, ext && typeof ext === 'object' ? (ext as Record<string, unknown>).main : undefined]) {
148
+ if (typeof main === 'string' && main.trim()) entrypoints.push(resolve(dir, main));
149
+ }
150
+ if (typeof raw.license === 'string' && raw.license.trim()) {
151
+ license = /^(?:MIT|Apache-2\.0|BSD-2-Clause|BSD-3-Clause|ISC|0BSD|CC0-1\.0)$/.test(raw.license.trim())
152
+ ? 'redistributable' : 'restricted';
153
+ }
154
+ const contributes = raw.contributes ?? (ext && typeof ext === 'object' ? (ext as Record<string, unknown>).contributes : undefined);
155
+ if (contributes && typeof contributes === 'object' && Array.isArray((contributes as Record<string, unknown>).hooks)
156
+ && ((contributes as Record<string, unknown>).hooks as unknown[]).length) {
157
+ findings.push({ level: 'caution', code: 'hooks-disabled' });
158
+ }
159
+ }
160
+ } catch { findings.push({ level: 'dangerous', code: 'invalid-manifest' }); }
161
+ }
162
+ // Quotes may be JSON-escaped (a value nested inside a JSON string field), so allow a backslash before them.
163
+ const sensitive = /(?:-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----|(?:\\?["'])?(?:api[_-]?key|secret|token)(?:\\?["'])?\s*[:=]\s*(\\?["'])?([A-Za-z0-9_\-]{16,})(\(?))/gi;
164
+ // Two narrow non-secrets: a documented placeholder (`tvly-YOUR_API_KEY`, `xxxxxxxx…`, `REPLACE_ME`) and an
165
+ // unquoted call result (`apiKey = getUpstageApiKey()`). Anything else that matches — quoted or not — still counts.
166
+ // Both exemptions look at the WHOLE value: a placeholder shape (optional vendor prefix), or a digit-free
167
+ // camelCase function name immediately called. A key with `xxxxxxxx` or `YOUR_` inside it still counts.
168
+ const placeholder = /^(?:[A-Za-z0-9]+[_-])*(?:YOUR[_-][A-Za-z_-]+|PLACEHOLDER[A-Za-z_-]*|REPLACE[_-]?ME[A-Za-z_-]*|x{8,})$/i;
169
+ const callee = /^[a-z_$][A-Za-z_$]*[a-z][A-Z][A-Za-z_$]*$/;
170
+ const hasSecret = [...text.matchAll(sensitive)].some(([, quote, value, call]) => value === undefined
171
+ || !(placeholder.test(value) || (!quote && call === '(' && callee.test(value))));
172
+ if (/(?:^|\/)(?:knowledge|knowledge-packs?)(?:\/|$)/i.test(rel) && hasSecret) {
173
+ findings.push({ level: 'dangerous', code: 'knowledge-dlp' });
174
+ } else if (hasSecret && !/\.lock$/.test(rel)) {
175
+ findings.push({ level: 'dangerous', code: 'embedded-secret' });
176
+ }
177
+ if (/\.[cm]?[jt]sx?$/.test(rel)) code.set(resolve(file), { text, test: /(?:^|\/)[^/]+\.(?:test|spec)\.[cm]?[jt]sx?$/.test(rel) });
178
+ if (/(?:curl|wget)\b[^\n|]*\|\s*(?:sh|bash)\b|\b(?:rm\s+-rf|sudo)\b/i.test(text)) {
179
+ findings.push({ level: 'dangerous', code: 'unsafe-command' });
180
+ }
181
+ }
182
+ };
183
+ visit(dir);
184
+ if (!entrypoints.length) entrypoints.push(resolve(dir, 'plugin.ts'));
185
+ // Shipped test fixtures are skipped only while nothing executable reaches them: the manifest `main`
186
+ // and every non-test code file are roots, and any test file they import (transitively) is scanned too.
187
+ const importsOf = (text: string): string[] =>
188
+ [...text.matchAll(/\b(?:import|export)\s*(?:[\s\S]*?\s+from\s*)?["']([^"']+)["']|\b(?:import|require)\s*\(\s*["']([^"']+)["']/g)]
189
+ .map(match => match[1] ?? match[2] ?? '').filter(Boolean);
190
+ const resolveLocal = (from: string, target: string): string | undefined => {
191
+ if (!target.startsWith('.')) return undefined;
192
+ const base = resolve(from, '..', target);
193
+ const swapped = base.replace(/\.([cm]?)js(x?)$/, '.$1ts$2');
194
+ return [base, swapped, `${base}.ts`, `${base}.tsx`, `${base}.js`, `${base}.mjs`, join(base, 'index.ts'), join(base, 'index.js')]
195
+ .find(candidate => code.has(candidate));
196
+ };
197
+ const reachable = new Set<string>();
198
+ const queue = [...entrypoints.filter(path => code.has(path)), ...[...code].filter(([, info]) => !info.test).map(([path]) => path)];
199
+ while (queue.length) {
200
+ const file = queue.pop()!;
201
+ if (reachable.has(file)) continue;
202
+ reachable.add(file);
203
+ for (const target of importsOf(code.get(file)!.text)) {
204
+ const next = resolveLocal(file, target);
205
+ if (next && !reachable.has(next)) queue.push(next);
206
+ }
207
+ }
208
+ for (const file of reachable) {
209
+ const text = code.get(file)!.text;
210
+ if (/\b(?:eval\s*\(|new\s+Function\s*\(|child_process\b|Bun\.spawn\s*\()/i.test(text)) {
211
+ findings.push({ level: 'caution', code: 'executable-code' });
212
+ }
213
+ for (const target of importsOf(text)) {
214
+ // A relative or absolute import must stay inside the package; the package's own `./src/…` is fine,
215
+ // while `../…/src/`, `/abs/…/src/` or a `file:` URL that can leave the package root is unsafe.
216
+ if (/^file:/i.test(target)
217
+ || ((target.startsWith('.') || target.startsWith('/')) && !resolve(file, '..', target).startsWith(`${resolve(dir)}${sep}`))) {
218
+ findings.push({ level: 'dangerous', code: 'unsafe-import' });
219
+ }
220
+ }
221
+ }
222
+ if ((license as PluginSecurityDecision['license']) !== 'redistributable') findings.push({ level: 'caution', code: 'private-only-license' });
223
+ return { scan: findings.some(f => f.level === 'dangerous') ? 'dangerous' : findings.length ? 'caution' : 'safe',
224
+ findings, integrity: hash.digest('hex'), license };
225
+ }
226
+
86
227
  export function assertCapability(decision: CapabilityDecision): void {
87
228
  if (!decision.ok) throw new Error(decision.reason);
88
229
  }
@@ -38,7 +38,7 @@ import { debug } from '../../debug/log.js';
38
38
  import { getSessionCwd } from '../../session/working-dir.js';
39
39
  import { CommandRegistry, type CommandDisposable, type RegisteredCommand } from '../../command-registry.js';
40
40
  import { PluginCapabilityPolicy, assertCapability } from './capability-policy.js';
41
- import { PluginTrustStore } from './trust-store.js';
41
+ import { PluginTrustStore, enabledPluginHooks } from './trust-store.js';
42
42
  import { PluginTaskService } from './task-service.js';
43
43
  import { readClipboardText, writeClipboard } from '../../clipboard/index.js';
44
44
  import {
@@ -54,6 +54,7 @@ import { getGlobalElementRegistry, publishElementEvent } from '../../element-reg
54
54
  import { mintPluginUri } from '../../mss/uri/builder.js';
55
55
  import type { PluginUri } from '../../mss/uri/brand.js';
56
56
  import { loadPluginNodes } from '../../graph-kinds/plugin-nodes.js';
57
+ import { registerOutboundAdapter } from '../../nexus/outbound/router.js';
57
58
  import { getNodeKindRegistration, listNodeKinds, unregisterPluginNodeKind, type NodeKindEntry } from '../../graph-kinds/registry.js';
58
59
 
59
60
  /** Clamp a tool handler's return value to something short enough for
@@ -113,8 +114,10 @@ export interface ActivePlugin {
113
114
  ownedCommandDisposables: CommandDisposable[];
114
115
  /** Task registrations for this activation. */
115
116
  ownedTaskDisposables: Array<{ dispose(): void }>;
116
- /** PX-3: hook registrations (from manifest.contributes.hooks[] +
117
- * ctx.hooks.register calls). Disposed on deactivate. */
117
+ /** Connector registrations are owner-scoped and removed on deactivate. */
118
+ ownedChannelDisposers: Array<() => void>;
119
+ /** Hook registrations from manifest.contributes.hooks[]; disposed on deactivate.
120
+ * PluginContext does not expose a programmatic hooks registration API. */
118
121
  ownedHookDisposers: Array<() => void>;
119
122
  /** PX-4: mission registrations (one per contributes.missions[] entry).
120
123
  * Disposers tear down globalMissionRegistry entries on deactivate. */
@@ -369,13 +372,12 @@ export class PluginHost {
369
372
  }
370
373
  }
371
374
 
372
- /** PX-3 P4: register all manifest-declared shell hooks for the
373
- * plugin we're activating. Disposers are tracked on the active
374
- * entry so deactivate() unregisters them. In-process hooks go
375
- * through ctx.hooks.register inside onActivate (see buildContext). */
375
+ /** Register manifest-declared shell hooks for the plugin being activated.
376
+ * Installed plugins' hooks stay off (no approval path); disposers unregister them on deactivate. */
376
377
  private registerManifestHooks(entry: PluginEntry): void {
377
378
  if (!this.activeEntry) return;
378
- const specs = entry.manifest.contributes.hooks ?? [];
379
+ const declared = entry.manifest.contributes.hooks ?? [];
380
+ const specs = enabledPluginHooks(declared, entry.source);
379
381
  if (specs.length === 0) return;
380
382
  // Lazy imports so headless tests that don't exercise hooks never
381
383
  // pull in the dispatcher stack.
@@ -770,6 +772,7 @@ export class PluginHost {
770
772
  ownedDisplayDisposables: [],
771
773
  ownedCommandDisposables: [],
772
774
  ownedTaskDisposables: [],
775
+ ownedChannelDisposers: [],
773
776
  ownedHookDisposers: [],
774
777
  ownedMissionDisposers: [],
775
778
  ownedRouteDisposers: [],
@@ -822,6 +825,13 @@ export class PluginHost {
822
825
  const ctx = this.buildContext();
823
826
  try {
824
827
  await plugin.onActivate?.(ctx);
828
+ for (const adapter of plugin.channelAdapters ?? []) {
829
+ if (!entry.manifest.contributes.connectors?.some(connector => connector.id === adapter.type)) {
830
+ throw new Error(`channel connector ${adapter.type} must be declared in contributes.connectors`);
831
+ }
832
+ // The manifest declaration alone is not an active channel.
833
+ this.activeEntry.ownedChannelDisposers.push(registerOutboundAdapter(adapter));
834
+ }
825
835
  // Resolve layout after onActivate so the plugin can initialize
826
836
  // state first (e.g. fetch remote rows). buildLayout can spawn
827
837
  // widgets via the tracked helper — we record ids for dispose.
@@ -848,6 +858,7 @@ export class PluginHost {
848
858
  for (const d of this.activeEntry.ownedDisplayDisposables) d.dispose();
849
859
  for (const d of this.activeEntry.ownedCommandDisposables) d.dispose();
850
860
  for (const d of this.activeEntry.ownedTaskDisposables) d.dispose();
861
+ for (const d of this.activeEntry.ownedChannelDisposers) d();
851
862
  for (const d of this.activeEntry.ownedHookDisposers) d();
852
863
  for (const d of this.activeEntry.ownedMissionDisposers) d();
853
864
  for (const d of this.activeEntry.ownedRouteDisposers) d();
@@ -880,6 +891,7 @@ export class PluginHost {
880
891
  for (const d of this.activeEntry.ownedDisplayDisposables) d.dispose();
881
892
  for (const d of this.activeEntry.ownedCommandDisposables) d.dispose();
882
893
  for (const d of this.activeEntry.ownedTaskDisposables) d.dispose();
894
+ for (const d of this.activeEntry.ownedChannelDisposers) d();
883
895
  const promptStore = getPromptBankStore();
884
896
  for (const id of this.activeEntry.promptFragmentIds) promptStore.delete(id);
885
897
  // PX-3: dispose hook registrations for this plugin.
@@ -1746,6 +1758,7 @@ function normalizePluginExport(plugin: Partial<ElanousPlugin>, manifest: PluginM
1746
1758
  ...(plugin.slashCommands ? { slashCommands: plugin.slashCommands } : {}),
1747
1759
  ...(plugin.keybindings ? { keybindings: plugin.keybindings } : {}),
1748
1760
  ...(plugin.llmTools ? { llmTools: plugin.llmTools } : {}),
1761
+ ...(plugin.channelAdapters ? { channelAdapters: plugin.channelAdapters } : {}),
1749
1762
  ...(plugin.onActivate ? { onActivate: plugin.onActivate } : {}),
1750
1763
  ...(plugin.onDeactivate ? { onDeactivate: plugin.onDeactivate } : {}),
1751
1764
  ...(plugin.isBusy ? { isBusy: plugin.isBusy } : {}),