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
@@ -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,
@@ -19,9 +19,10 @@
19
19
  // 지갑을 여는» 일이 없다. (2026-08-13 실측 · RESEARCH-grok-oauth-… §7b)
20
20
 
21
21
  import type { RotationCandidate } from './codex-account-rotation.js';
22
+ import type { ClaudePtyMaturity } from './claude-pty-maturity.js';
22
23
 
23
24
  /** 체인에 놓을 수 있는 칸. ⛔ 모르는 이름은 «조용히 건너뛰지 않고» 거부한다(오타 방어). */
24
- export const FALLBACK_STEPS = ['codex-rotate', 'grok'] as const;
25
+ export const FALLBACK_STEPS = ['codex-rotate', 'grok', 'claude-pty', 'openrouter'] as const;
25
26
  export type FallbackStep = (typeof FALLBACK_STEPS)[number];
26
27
 
27
28
  /** 기본 체인 — ⛔⭐ **2026-08-20 에 `grok` 이 «기본»으로 들어왔다**(대표 지시).
@@ -75,8 +76,10 @@ export type RotationOutcome =
75
76
  export type FallbackDecision =
76
77
  /** codex 계정을 갈아탄다(종전 동작). */
77
78
  | { readonly action: 'codex-rotate'; readonly to: RotationCandidate }
78
- /** 백엔드를 통째로 바꾼다. 지금은 grok 뿐. */
79
- | { readonly action: 'switch-backend'; readonly backend: 'grok' }
79
+ /** 백엔드를 통째로 바꾼다. */
80
+ | { readonly action: 'switch-backend'; readonly backend: 'grok' | 'claude' }
81
+ /** 명시한 유료 OpenRouter 모델로 전환한다. 호출부가 모델을 바꾸지 못하면 실행하지 않는다. */
82
+ | { readonly action: 'switch-backend'; readonly backend: 'openrouter'; readonly model: string }
80
83
  /** 아무것도 안 한다 — 이유를 «값으로» 남긴다. */
81
84
  | { readonly action: 'stay'; readonly why: StayReason };
82
85
 
@@ -96,7 +99,11 @@ export type StayReason =
96
99
  /** 다음 칸이 grok 인데 grok 자격이 «없다». */
97
100
  | 'grok-unavailable'
98
101
  /** grok 자격은 있는데 «잔량이 소진»됐다 — 자격 부재와 «다른 값»이다. */
99
- | 'grok-exhausted';
102
+ | 'grok-exhausted'
103
+ /** 유료 OpenRouter 모델 또는 키가 없다. */
104
+ | 'openrouter-unavailable'
105
+ /** claude PTY 의 최근 표본이 성숙 관문을 통과하지 못했거나 읽히지 않는다. */
106
+ | 'claude-pty-immature';
100
107
 
101
108
  type FallbackRunPosition =
102
109
  | { readonly currentCredentialRateLimited?: false; readonly currentStep?: FallbackStep }
@@ -110,6 +117,10 @@ export type FallbackInput = {
110
117
  /** grok 자격이 «있나». ⛔ 「모른다」를 boolean 에 접지 마라 — 호출자가
111
118
  * `resolveGrokCredential() !== null` 로 확정해서 준다. */
112
119
  readonly grokAvailable: boolean;
120
+ /** `llm.openrouter.fallbackModel` — 기본 모델로의 유료 폴백은 허용하지 않는다. */
121
+ readonly openrouterFallbackModel?: string;
122
+ /** OpenRouter 키 존재 여부(키 값 자체는 판정·관측에 넣지 않는다). */
123
+ readonly openrouterAvailable?: boolean;
113
124
  /**
114
125
  * grok 이 «지금 쓸 수 있나» — ⛔ 자격(`grokAvailable`)과 «다른 축»이다.
115
126
  * 'usable' 쓸 수 있다(잔량 있음 · 무제한 · 임계 미만)
@@ -120,6 +131,8 @@ export type FallbackInput = {
120
131
  * (⇒ 그리고 그 사실은 관측에 값으로 남는다 — 「모르고 갔다」를 셀 수 있다.)
121
132
  */
122
133
  readonly grokQuota?: 'usable' | 'exhausted' | 'unknown';
134
+ /** 최근 claude PTY 표본의 순수 성숙 판정. 생략·못 읽음은 전환 불가. */
135
+ readonly claudePtyMaturity?: ClaudePtyMaturity;
123
136
  /**
124
137
  * `reset-credit-available` 에서 체인을 타지 않고 머문다.
125
138
  * 기본(생략·false)은 `no-candidate` 와 같이 `codex-rotate` 다음 칸을 찾는다.
@@ -148,12 +161,14 @@ export type FallbackInput = {
148
161
  export function decideFallback(input: FallbackInput): FallbackDecision {
149
162
  const {
150
163
  rotation, chain, currentStep, currentCredentialRateLimited = false,
151
- grokAvailable, grokQuota = 'unknown', stayOnResetCreditAvailable = false,
164
+ grokAvailable, grokQuota = 'unknown', claudePtyMaturity, stayOnResetCreditAvailable = false,
165
+ openrouterFallbackModel, openrouterAvailable = false,
152
166
  } = input;
167
+ const openrouter = { model: openrouterFallbackModel, available: openrouterAvailable };
153
168
 
154
169
  if (currentCredentialRateLimited) {
155
170
  if (currentStep === undefined) return { action: 'stay', why: 'chain-exhausted' };
156
- return stepAfter(chain, currentStep, grokAvailable, grokQuota, 'chain-exhausted');
171
+ return stepAfter(chain, currentStep, grokAvailable, grokQuota, claudePtyMaturity, openrouter, 'chain-exhausted');
157
172
  }
158
173
  if (rotation.reason === 'explicit') return { action: 'stay', why: 'explicit' };
159
174
  if (rotation.reason === 'not-reached' || (rotation.reason === 'credit-pace' && !('to' in rotation))) {
@@ -167,13 +182,13 @@ export function decideFallback(input: FallbackInput): FallbackDecision {
167
182
  // 회전이 답을 냈는데 체인이 codex-rotate 를 «빼» 놨다면 그 뜻을 존중하고
168
183
  // 다음 칸으로 간다(회전을 원치 않는 구성).
169
184
  if (chain.includes('codex-rotate')) return { action: 'codex-rotate', to: rotation.to };
170
- return stepAfterCodex(chain, grokAvailable, grokQuota, 'chain-exhausted');
185
+ return stepAfterCodex(chain, grokAvailable, grokQuota, claudePtyMaturity, openrouter, 'chain-exhausted');
171
186
  }
172
187
 
173
188
  // no-candidate | disabled | reset-credit-available(기본) — codex 축이 끝났다. 다음 칸을 본다.
174
189
  // ⛔ 다음 칸이 «없을 때»의 이름만 고른다. 진행(stepAfterCodex)은 그대로다.
175
190
  const fallbackWhy = stayReasonWhenCodexAxisEnds(rotation.reason === 'credit-pace' ? 'no-candidate' : rotation.reason);
176
- return stepAfterCodex(chain, grokAvailable, grokQuota, fallbackWhy);
191
+ return stepAfterCodex(chain, grokAvailable, grokQuota, claudePtyMaturity, openrouter, fallbackWhy);
177
192
  }
178
193
 
179
194
  /** codex 축이 끝났는데 체인 다음 칸도 없을 때 머무는 이유.
@@ -191,9 +206,11 @@ function stepAfterCodex(
191
206
  chain: readonly FallbackStep[],
192
207
  grokAvailable: boolean,
193
208
  grokQuota: 'usable' | 'exhausted' | 'unknown',
209
+ claudePtyMaturity: ClaudePtyMaturity | undefined,
210
+ openrouter: { readonly model?: string; readonly available: boolean },
194
211
  whyIfNone: StayReason,
195
212
  ): FallbackDecision {
196
- return stepAfter(chain, 'codex-rotate', grokAvailable, grokQuota, whyIfNone);
213
+ return stepAfter(chain, 'codex-rotate', grokAvailable, grokQuota, claudePtyMaturity, openrouter, whyIfNone);
197
214
  }
198
215
 
199
216
  function stepAfter(
@@ -201,20 +218,36 @@ function stepAfter(
201
218
  currentStep: FallbackStep | undefined,
202
219
  grokAvailable: boolean,
203
220
  grokQuota: 'usable' | 'exhausted' | 'unknown',
221
+ claudePtyMaturity: ClaudePtyMaturity | undefined,
222
+ openrouter: { readonly model?: string; readonly available: boolean },
204
223
  whyIfNone: StayReason,
205
224
  ): FallbackDecision {
206
225
  const idx = currentStep === undefined ? -1 : chain.indexOf(currentStep);
207
226
  const rest = idx === -1 ? chain : chain.slice(idx + 1);
227
+ let unavailableWhy: StayReason | undefined;
208
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
+ }
209
237
  if (step === 'grok') {
210
- // ⛔ 자격 «먼저» — 자격이 없으면 잔량을 물을 것도 없다(그리고 두 사유는 다른 값이다).
211
- if (!grokAvailable) return { action: 'stay', why: 'grok-unavailable' };
212
- // ⛔ 「모른다」는 통과시킨다 — 못 읽었다고 갈 곳을 없애지 않는다(위 계약 주석).
213
- if (grokQuota === 'exhausted') return { action: 'stay', why: 'grok-exhausted' };
238
+ // 자격 부재와 소진은 다른 값이다. 다음 칸이 있으면 그 칸을 살핀다.
239
+ if (!grokAvailable) { unavailableWhy = 'grok-unavailable'; continue; }
240
+ // 「모른다」는 통과시킨다 — 못 읽었다고 갈 곳을 없애지 않는다.
241
+ if (grokQuota === 'exhausted') { unavailableWhy = 'grok-exhausted'; continue; }
214
242
  return { action: 'switch-backend', backend: 'grok' };
215
243
  }
244
+ if (step === 'claude-pty') {
245
+ // 미성숙·못 읽음은 «건너뛴다» — 뒤에 다른 칸이 있으면 그 칸을 본다(없으면 이 사유로 머문다).
246
+ if (claudePtyMaturity?.mature !== true) { unavailableWhy = 'claude-pty-immature'; continue; }
247
+ return { action: 'switch-backend', backend: 'claude' };
248
+ }
216
249
  }
217
- return { action: 'stay', why: whyIfNone };
250
+ return { action: 'stay', why: unavailableWhy ?? whyIfNone };
218
251
  }
219
252
 
220
253
  /** 사람이 읽는 한 줄 — 관측·CLI 표면용. ⛔ 토큰·계정 홈은 넣지 않는다. */
@@ -223,7 +256,9 @@ export function describeFallback(decision: FallbackDecision): string {
223
256
  case 'codex-rotate':
224
257
  return `codex 계정 전환 → ${decision.to.name}`;
225
258
  case 'switch-backend':
226
- return `백엔드 전환 → ${decision.backend} (구독)`;
259
+ return decision.backend === 'openrouter'
260
+ ? `백엔드 전환 → openrouter (${decision.model} · API 과금)`
261
+ : `백엔드 전환 → ${decision.backend} (구독)`;
227
262
  case 'stay':
228
263
  return `전환 없음 (${decision.why})`;
229
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
  }