elanous 0.2.23 → 0.2.25

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 (688) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/chunks/1027.a0f04663d24c2779.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +36 -0
  8. package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +148 -0
  10. package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1454-37fd16f479f5554a.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/{8532.a86e99acf9b761cb.js → 1516.83dd2c1b86b4e507.js} +2 -2
  16. package/apps/pwa/out/_next/static/chunks/{89849432.9eaa2f4c9c010965.js → 16e57f67.3ea19e893461e611.js} +3 -3
  17. package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{1505.5826671109111e75.js → 1743.563d3168e95825ef.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +27 -0
  20. package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +24 -0
  25. package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +32 -0
  28. package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2846.d6a433b8663819a5.js +309 -0
  30. package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +34 -0
  31. package/apps/pwa/out/_next/static/chunks/{5967-c0a80f8ffe5d4050.js → 3259-86f3be56e43eaf6a.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +287 -0
  36. package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{5578.ac963634373b28c6.js → 4084.15ecaf4240bd9e77.js} +2 -2
  41. package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{8438.ee7f0df3b3f6fcb4.js → 4204.a409ab6250c0f6a8.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +8 -0
  47. package/apps/pwa/out/_next/static/chunks/4565-67e4c620597965c8.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{7905.b230b9d06cb4ad5d.js → 4593.10f5b9bd702d4a6c.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +104 -0
  51. package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +149 -0
  53. package/apps/pwa/out/_next/static/chunks/{8569.b853a8daa8f31d12.js → 5081.a240b63fad28663a.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/{2528.0af5c0e1fad88df6.js → 5408.377c8c543635b81f.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/{9502-32c5dc22e8f120f5.js → 5492-e05e26a033ba80d2.js} +3 -3
  61. package/apps/pwa/out/_next/static/chunks/5785-ea8d2c633b6688e3.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/599-a914cc0468f56143.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +23 -0
  67. package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{5974.360f8441eb0ac1ec.js → 6196.34d6b229079e9e28.js} +1 -1
  70. package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +77 -0
  72. package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +64 -0
  74. package/apps/pwa/out/_next/static/chunks/{ce383d63.d2ede338788ed18e.js → 65ebff95.081daeff622d238e.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +2 -0
  77. package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/{83cfc5c8.349535543c1548b6.js → 6def8a8e.c81f9ca41028eccf.js} +1 -1
  79. package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/{2721.bc9a8f36bd85c07a.js → 7483.ba01c6c209b81bba.js} +1 -1
  84. package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +62 -0
  87. package/apps/pwa/out/_next/static/chunks/{6702.ebbd165d5e4a4a14.js → 7752.5cbdd0b433c54af9.js} +1 -1
  88. package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/{8925-2abfb48af477fb7f.js → 8085-1e5fce59500a2cc2.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +226 -0
  96. package/apps/pwa/out/_next/static/chunks/{b4e12462-5fab4a813a63dadd.js → 83a80514-b897b64df43ce108.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +93 -0
  98. package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +24 -0
  99. package/apps/pwa/out/_next/static/chunks/{8264.00e434fbd02e9311.js → 8578.dff22d988cc2b106.js} +3 -3
  100. package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +115 -0
  103. package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +41 -0
  105. package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +74 -0
  106. package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +94 -0
  107. package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{7518.49161e8f5814abdb.js → 9311.24c8f5ea793253dd.js} +1 -1
  110. package/apps/pwa/out/_next/static/chunks/{9976.8f28864317639fe6.js → 94.4cfed21c0b781a06.js} +3 -3
  111. package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +90 -0
  113. package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/{0e3acd34.d42b2ef87fdf4234.js → 973cbf1b.3bbcf2b41548eca7.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/{6571-926ddf024a6ad5df.js → 9917-9e26abc5f7772735.js} +1 -1
  118. package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +79 -0
  119. package/apps/pwa/out/_next/static/chunks/{740.90b31555290236a9.js → 9974.e77b4ff4884557b3.js} +2 -2
  120. package/apps/pwa/out/_next/static/chunks/{4c5e95ce.83e9b16add6fcd8c.js → ab4d57c2.e90bc2cb31949045.js} +7 -7
  121. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-ad3bd8084048bee4.js → page-b12149c51f7d04b1.js} +1 -1
  126. package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/decisions/page-305cd294e754906e.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-a648bc464c9dc754.js → page-dea4b795741126fd.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/exec/page-45889e7070d11126.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/field/page-16a2e893f83ae4d0.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/intake/page-56888bc744e08dac.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/live/page-952c6ceec6d9de38.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live-run/page-6d0202af568f4c63.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4fb6fc34e20c07b7.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/morning/page-e9502921f4f01b1f.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/not-found-b6169ea2f09cf287.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6a14333d1b1b3967.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8c91a9a66c9ec90a.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-48fb31810693c357.js → page-4e634c5b98fd4e93.js} +1 -1
  150. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-cb154acadb025414.js → page-b9cc8a10bc6078b4.js} +1 -1
  152. package/apps/pwa/out/_next/static/chunks/app/page-3d3fc73dd187ed52.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-9c04dac0831c2bbb.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-04de9ca6d52a3893.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/page-62527677bbc2fdc4.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9a2e09c4a311764c.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/page-bfeb35d69e8b24bb.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/share/page-89cb33bced824e4a.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/today/page-6445ac4cbb8dd869.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/page-20162174b77f96cf.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-05a4b2c9b91034aa.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{53746282.1be36038d1abd949.js → e117f801.e92bf10242653497.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +122 -0
  175. package/apps/pwa/out/_next/static/chunks/{0673cc54.8e2ef1c23e49e190.js → f4dfbfc0.a69fb12f854882aa.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +1 -0
  182. package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
  183. package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → dRS6lEhSRRBr1abbzp7fJ}/_buildManifest.js +1 -1
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +23 -23
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +23 -23
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +23 -23
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +22 -22
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +22 -22
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +23 -23
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +22 -22
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +23 -23
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +23 -23
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +22 -22
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +23 -23
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +23 -23
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +23 -23
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +23 -23
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +22 -22
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +22 -22
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +23 -23
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +23 -23
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +23 -23
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +22 -22
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +22 -22
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +23 -23
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +23 -23
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +22 -22
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +22 -22
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +22 -22
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +23 -23
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +23 -23
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +22 -22
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +23 -23
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +23 -23
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +23 -23
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +23 -23
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +23 -23
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +22 -22
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +23 -23
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +23 -23
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +23 -23
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +23 -23
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +23 -23
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +23 -23
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +23 -23
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +23 -23
  277. package/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
  278. package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
  279. package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
  280. package/catalog/providers/openrouter.yaml +2 -0
  281. package/graphs/implement-loop.yaml +31 -0
  282. package/graphs/ops/harness-gc.yaml +17 -0
  283. package/graphs/ops/recipes.yaml +3 -0
  284. package/graphs/overlays/README.md +1 -1
  285. package/graphs/overlays/stop-heal-ladder.yaml +105 -0
  286. package/graphs/release/release-loop.yaml +5 -5
  287. package/graphs/steward/recipes.yaml +4 -0
  288. package/graphs/steward/steward.yaml +10 -1
  289. package/package.json +1 -1
  290. package/scripts/add-api-key.sh +1 -1
  291. package/scripts/bench-report.ts +46 -1
  292. package/scripts/ci-typecheck-changed.ts +73 -14
  293. package/scripts/fleet/bedrock-pod-secret.ts +122 -0
  294. package/scripts/fleet/node-setup-linux.sh +97 -0
  295. package/scripts/fleet/node-setup.sh +11 -0
  296. package/scripts/fleet/zai-pod-secret.ts +86 -0
  297. package/scripts/land-global-checks.ts +3 -0
  298. package/scripts/measure-log-diet-2.ts +213 -0
  299. package/scripts/measure-tsc-affected-roots.ts +289 -0
  300. package/scripts/release-loop/auto-approve-node.ts +6 -0
  301. package/scripts/release-loop/gate-closure.ts +286 -0
  302. package/scripts/release-loop/gate-executor.ts +28 -0
  303. package/scripts/release-loop/gate-node.ts +333 -37
  304. package/scripts/release-loop/gate-partial.ts +34 -6
  305. package/scripts/release-loop/ops-upgrade-node.ts +63 -42
  306. package/scripts/release-loop/publish-node.ts +6 -0
  307. package/scripts/release-loop/resume-release.ts +46 -13
  308. package/scripts/release-loop/resume.ts +110 -4
  309. package/scripts/release-loop/unattended-release.ts +22 -7
  310. package/scripts/tsc-baseline-cache.ts +172 -0
  311. package/scripts/usage-rollup.ts +72 -6
  312. package/scripts/ux-sim.ts +30 -1
  313. package/skills/youtube-master/scripts/main.ts +4 -1
  314. package/skills/youtube-master/src/obsidian.ts +9 -0
  315. package/src/agent/run-context.ts +10 -0
  316. package/src/agent-mission/driver.ts +18 -3
  317. package/src/agent-mission/resource-ladder.ts +8 -3
  318. package/src/agent-mission/review-loop.ts +8 -2
  319. package/src/agent-substrate/acp-implementer.ts +146 -0
  320. package/src/agent-substrate/pr-reviewer.ts +60 -13
  321. package/src/agent-substrate/self-review-cli.ts +170 -15
  322. package/src/autopilot/mission-critique.ts +18 -5
  323. package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
  324. package/src/autopilot/mission-trace.ts +12 -1
  325. package/src/autopilot/system-lookback-run.ts +16 -10
  326. package/src/bench/bench.ts +183 -0
  327. package/src/budget/cost-tier-advisor.ts +91 -0
  328. package/src/budget/fetchers/grok.ts +15 -5
  329. package/src/budget/llm-cost.ts +37 -5
  330. package/src/budget/shadow-arms.ts +77 -0
  331. package/src/budget/types.ts +10 -2
  332. package/src/budget/unified-usage.ts +256 -10
  333. package/src/cli/bench-cli.ts +34 -0
  334. package/src/cli/doctor-cli.ts +21 -0
  335. package/src/cli/friendly-errors.ts +113 -0
  336. package/src/cli/guardian-cli.ts +26 -1
  337. package/src/cli/image-cli.ts +101 -0
  338. package/src/cli/intake-cli.ts +3 -1
  339. package/src/cli/log-event-names.ts +4 -0
  340. package/src/cli/memory-cli.ts +182 -0
  341. package/src/cli/model-watch-cli.ts +4 -1
  342. package/src/cli/pr-cli.ts +62 -5
  343. package/src/cli/pr-land-overlap-evidence.ts +160 -12
  344. package/src/cli/pr-land-pod-shadow.ts +54 -0
  345. package/src/cli/release-cli.ts +27 -16
  346. package/src/cli/release-run-if-ready.ts +11 -0
  347. package/src/cli/resource-cli.ts +48 -0
  348. package/src/cli/retro-cli.ts +12 -3
  349. package/src/cli/rfc-status.ts +52 -2
  350. package/src/cli/seat-requests-cli.ts +25 -0
  351. package/src/cli/self-update.ts +104 -0
  352. package/src/cli/tasks-cli.ts +139 -10
  353. package/src/cli/usage-cli.ts +93 -8
  354. package/src/compact/verify-probe.ts +23 -3
  355. package/src/context-card/index.ts +50 -0
  356. package/src/control/control-status.ts +30 -1
  357. package/src/core-turn/run-goal-loop.ts +5 -4
  358. package/src/dashboard/ad-run-setup-bindings.ts +279 -0
  359. package/src/dashboard/index.ts +18 -276
  360. package/src/dashboard/slash-runtime/dashboard-handlers.ts +4 -138
  361. package/src/dashboard/slash-runtime/shell-slash.ts +145 -0
  362. package/src/dashboard/stale-install-check.ts +11 -0
  363. package/src/debug/log-diet.ts +53 -10
  364. package/src/debug/log.ts +19 -1
  365. package/src/domains/morning-essential.ts +18 -3
  366. package/src/domains/release-tool.ts +43 -0
  367. package/src/error-report/report.ts +21 -4
  368. package/src/execution-loop/launch-gate.ts +158 -2
  369. package/src/fleet/host-doctor.ts +375 -0
  370. package/src/github/pr-labels.ts +1 -0
  371. package/src/goals/judge.ts +20 -6
  372. package/src/graph-runner/graph-cli.ts +42 -1
  373. package/src/graph-runner/runner.ts +176 -11
  374. package/src/graph-wizard/archetypes.ts +14 -8
  375. package/src/graph-wizard/generate.ts +77 -1
  376. package/src/grok/usage.ts +23 -2
  377. package/src/grok.ts +13 -7
  378. package/src/grounding/ref-index.ts +253 -0
  379. package/src/harness/dispatch-backpressure.ts +10 -2
  380. package/src/harness/harness-clean.ts +199 -78
  381. package/src/harness/harness-cli-command.ts +176 -52
  382. package/src/harness/harness-gc.ts +331 -0
  383. package/src/harness/harness-stop.ts +82 -10
  384. package/src/harness/harness-substrate-default.ts +142 -7
  385. package/src/harness/launch-registry.ts +6 -0
  386. package/src/harness/worktree-branch-prefix.ts +9 -0
  387. package/src/hq/fence.ts +187 -0
  388. package/src/hq/seats.ts +38 -22
  389. package/src/images/image-ledger.ts +179 -0
  390. package/src/index.ts +110 -186
  391. package/src/instance/child-scope.ts +32 -1
  392. package/src/instance/dev-project.ts +133 -0
  393. package/src/instance/resolve.ts +24 -0
  394. package/src/instance/spawn-elanous.ts +45 -0
  395. package/src/intake-plane/collect-github.ts +7 -2
  396. package/src/intake-plane/lens.ts +46 -4
  397. package/src/intake-plane/ref-prefetch.ts +110 -0
  398. package/src/intake-plane/requirement-funnel.ts +53 -1
  399. package/src/intelligence-map/catalog-feed.ts +119 -0
  400. package/src/intelligence-map/model-catalog.ts +72 -0
  401. package/src/intelligence-map/model-watch-intake.ts +40 -0
  402. package/src/intelligence-map/model-watch-mission.ts +14 -1
  403. package/src/llm/bedrock-eventstream.ts +215 -0
  404. package/src/llm/bedrock-provider.ts +114 -41
  405. package/src/llm/bedrock.ts +174 -28
  406. package/src/llm/open-weight-discipline.ts +21 -0
  407. package/src/llm/openrouter-byok-cost.ts +96 -0
  408. package/src/llm/openrouter-deepseek.ts +151 -0
  409. package/src/llm/openrouter-upstream-pins.config.ts +25 -0
  410. package/src/llm/zai-coding-plan.ts +700 -0
  411. package/src/llm.ts +91 -24
  412. package/src/loops/checker.ts +148 -9
  413. package/src/loops/orchestrator/resync-shadow.ts +34 -7
  414. package/src/maturity/feature-maturity.ts +3 -3
  415. package/src/model-tier/llm-tier-map.ts +9 -5
  416. package/src/models/prompts.ts +2 -1
  417. package/src/mss/logging/log-store.ts +58 -11
  418. package/src/nexus/api/http-server.ts +22 -0
  419. package/src/nexus/api/missions.ts +73 -2
  420. package/src/nexus/api/ops-api.ts +18 -0
  421. package/src/nexus/index.ts +7 -75
  422. package/src/nexus/tools-kind.ts +78 -0
  423. package/src/oauth/codex-account-rotation.ts +43 -3
  424. package/src/oauth/codex-account-store.ts +339 -27
  425. package/src/oauth/codex-quota-policy.ts +33 -4
  426. package/src/oauth/fallback-chain.ts +53 -15
  427. package/src/onboarding/first-run.ts +37 -18
  428. package/src/onboarding/web-first.ts +20 -2
  429. package/src/ops/machine-heartbeat.ts +185 -0
  430. package/src/ops/overview-cache.ts +175 -0
  431. package/src/ops/overview.ts +8 -0
  432. package/src/preview/terminal.ts +15 -5
  433. package/src/project/project-store.ts +20 -0
  434. package/src/prompt-cache/openai.ts +3 -0
  435. package/src/prompt-cache/types.ts +5 -0
  436. package/src/prompt-enhance/enhance.ts +23 -0
  437. package/src/release-loop/auto-start.ts +7 -5
  438. package/src/release-loop/checklist.ts +11 -1
  439. package/src/release-loop/gate-shards.ts +4 -0
  440. package/src/release-loop/light-rc.ts +84 -13
  441. package/src/release-loop/merged-pr-checklist.ts +35 -5
  442. package/src/release-loop/placement.ts +16 -1
  443. package/src/release-loop/rehearsal.ts +1 -1
  444. package/src/requirements/requirement-ledger.ts +536 -0
  445. package/src/retro/daily.ts +5 -5
  446. package/src/seat-loop/duty-ledger.ts +64 -0
  447. package/src/seat-loop/seat-loop.ts +32 -10
  448. package/src/self-dev/decompose.ts +37 -3
  449. package/src/self-dev/dev-cli.ts +44 -10
  450. package/src/self-dev/dev-pipeline.ts +39 -9
  451. package/src/self-dev/draft-residue.ts +27 -5
  452. package/src/self-dev/draft-sweep.ts +151 -0
  453. package/src/self-dev/fabric-decompose-adapter.ts +9 -2
  454. package/src/self-dev/graph-journey-route.ts +6 -0
  455. package/src/self-dev/orchestrate-cli.ts +25 -3
  456. package/src/self-dev/orchestrate.ts +41 -6
  457. package/src/self-dev/run-supervisor.ts +218 -15
  458. package/src/self-dev/run-types.ts +13 -0
  459. package/src/self-dev/supervisor-verdict-edges.ts +1 -0
  460. package/src/self-implement/budget-gate.ts +53 -13
  461. package/src/self-implement/child-context-composition.ts +139 -0
  462. package/src/self-implement/child-llm-preference.ts +13 -5
  463. package/src/self-implement/claude-acp-implement.ts +67 -0
  464. package/src/self-implement/flex-signals.ts +59 -0
  465. package/src/self-implement/frozen-merges.ts +8 -3
  466. package/src/self-implement/gate-cli.ts +29 -10
  467. package/src/self-implement/gate-remote.ts +4 -1
  468. package/src/self-implement/goal-author-decompose.ts +10 -2
  469. package/src/self-implement/goal-author.ts +36 -11
  470. package/src/self-implement/goal-new-paths.ts +42 -0
  471. package/src/self-implement/graph-overlay-condition.ts +28 -0
  472. package/src/self-implement/graph-overlay-yaml.ts +41 -7
  473. package/src/self-implement/graph-templates.ts +33 -1
  474. package/src/self-implement/graph-yaml.ts +56 -0
  475. package/src/self-implement/heal-intake.ts +5 -1
  476. package/src/self-implement/host-regate.ts +124 -30
  477. package/src/self-implement/importer-test-index.ts +3 -1
  478. package/src/self-implement/l8-merge-queue.ts +80 -2
  479. package/src/self-implement/merge-train.ts +19 -0
  480. package/src/self-implement/off-diff-evidence.ts +40 -1
  481. package/src/self-implement/orchestrator.ts +230 -32
  482. package/src/self-implement/premise-review.ts +159 -0
  483. package/src/self-implement/regate-detail.ts +79 -0
  484. package/src/self-implement/review-deferred-to-host.ts +21 -0
  485. package/src/self-implement/rework-budget-judgment.ts +23 -3
  486. package/src/self-implement/running-runs.ts +75 -5
  487. package/src/self-implement/scope-adjacency.ts +23 -0
  488. package/src/self-implement/seams.ts +89 -8
  489. package/src/self-implement/self-implement-cli.ts +9 -4
  490. package/src/self-implement/stop-autoheal.ts +69 -2
  491. package/src/session-runtime/retry-policy.ts +2 -1
  492. package/src/skills/tools/agent.ts +16 -1
  493. package/src/skills/tools/persistent-grounding.ts +11 -1
  494. package/src/skills/tools/shell.ts +33 -1
  495. package/src/steward/launch.ts +5 -4
  496. package/src/steward/rejudge-tick.ts +142 -0
  497. package/src/steward/triage-budget.ts +62 -0
  498. package/src/steward/triage.ts +83 -22
  499. package/src/task-agent/actions.ts +62 -5
  500. package/src/task-agent/board.ts +92 -3
  501. package/src/task-agent/card-evidence.ts +88 -1
  502. package/src/task-agent/completion-evidence.ts +5 -0
  503. package/src/task-agent/cover.ts +166 -3
  504. package/src/task-agent/judge.ts +16 -0
  505. package/src/task-agent/land-order-ledger.ts +95 -0
  506. package/src/task-agent/live-move-history.ts +87 -1
  507. package/src/task-agent/live-moves.ts +350 -20
  508. package/src/task-agent/parent-host.ts +27 -3
  509. package/src/task-agent/rejudge-on-head.ts +223 -24
  510. package/src/task-agent/review-prior-fix.ts +146 -0
  511. package/src/task-agent/review-result-capture.ts +3 -1
  512. package/src/task-agent/run-adopt.ts +136 -0
  513. package/src/task-agent/shadow.ts +65 -9
  514. package/src/task-agent/task-hand.ts +57 -7
  515. package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
  516. package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
  517. package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
  518. package/src/task-orchestrator/surfaces/pod-child-providers.ts +14 -1
  519. package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -0
  520. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +79 -6
  521. package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +122 -0
  522. package/src/task-orchestrator/surfaces/pod-lease.ts +17 -5
  523. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
  524. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +23 -0
  525. package/src/task-orchestrator/surfaces/pod-pool.ts +73 -4
  526. package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
  527. package/src/task-orchestrator/surfaces/self-implement-pod.ts +432 -35
  528. package/src/task-orchestrator/surfaces/self-implement.ts +19 -1
  529. package/src/typecheck-ratchet.ts +1 -0
  530. package/src/user-config.ts +69 -15
  531. package/src/ux-sim/fault-cred-expiry.ts +100 -0
  532. package/src/ux-sim/fault-pod-death.ts +74 -0
  533. package/src/ux-sim/fault-pool-cap.ts +81 -0
  534. package/src/version/packed-revision.json +1 -1
  535. package/src/webclone/qa-table.ts +22 -0
  536. package/src/workflow-runtime/triggers/registry.ts +59 -0
  537. package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
  538. package/apps/pwa/out/_next/static/chunks/0a09e062-34f747a17d8d8c2e.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +0 -77
  540. package/apps/pwa/out/_next/static/chunks/1325-dc3f3c21eb181cb4.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +0 -34
  542. package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +0 -24
  543. package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/1669-da843ed73aa301f5.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +0 -36
  553. package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +0 -122
  554. package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +0 -62
  556. package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +0 -149
  563. package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +0 -309
  567. package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/5313.1697309c532b9856.js +0 -2
  579. package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +0 -74
  587. package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +0 -64
  591. package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +0 -32
  594. package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +0 -24
  595. package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +0 -93
  599. package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +0 -148
  604. package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +0 -226
  605. package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +0 -27
  606. package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +0 -115
  609. package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +0 -8
  616. package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +0 -23
  617. package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +0 -90
  622. package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +0 -94
  623. package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +0 -41
  625. package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +0 -287
  627. package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +0 -79
  628. package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +0 -104
  633. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/app/consult/page-6168a7c43e6ef8f5.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/framework-b2763b10dc6b3836.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +0 -1
  687. package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +0 -1
  688. /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → dRS6lEhSRRBr1abbzp7fJ}/_ssgManifest.js +0 -0
@@ -6,7 +6,7 @@
6
6
  // ⛔ 토큰 «값»은 어디에도 출력하지 않는다 — accountId 앞 8자만(R-LLM2).
7
7
 
8
8
  import { readFile } from 'node:fs/promises';
9
- import { mkdirSync, openSync, closeSync, unlinkSync, readFileSync, writeFileSync, statSync } from 'node:fs';
9
+ import { mkdirSync, openSync, closeSync, unlinkSync, readFileSync, writeFileSync, statSync, renameSync } from 'node:fs';
10
10
  import { dirname, join, resolve } from 'node:path';
11
11
  import { consumeCodexResetCredits, listCodexResetCredits } from '../budget/codex-reset-credits.js';
12
12
  import { codexCredentialRoot, readAvailabilityState, writeAvailabilityState } from '../budget/codex-reset-credit-state.js';
@@ -20,16 +20,16 @@ import { creditPaceStatus } from '../budget/codex-credit-pace.js';
20
20
  import { loadLlmPolicy } from '../policy/llm-policy.js';
21
21
  import { getUserConfig } from '../user-config.js';
22
22
  import {
23
- decideCodexRotation, observeRotation, applyRotation, readCodexAccountRotationConfig, rotatedChildEnv,
23
+ decideCodexRotation, observeRotation, applyRotation, readCodexAccountRotationConfig, rotatedChildEnv, creditCandidatesByBalance, CODEX_CREDIT_RESERVE_DEFAULT,
24
24
  codexAccountThresholdPercent,
25
25
  type RotationCandidate,
26
26
  } from './codex-account-rotation.js';
27
- import { codexPolicyAllowsCredits, codexPolicyAllowsFallback, resolveCodexQuotaPolicy, type CodexQuotaPolicy, type ResolvedCodexQuotaPolicy } from './codex-quota-policy.js';
27
+ import { codexPolicyAllowsCredits, codexPolicyAllowsFallback, resolveCodexQuotaPolicy, type CodexQuotaPolicy, type CodexQuotaPolicyFileValue, type ResolvedCodexQuotaPolicy } from './codex-quota-policy.js';
28
28
  import { emitDecision } from '../live/detail-switch.js';
29
29
  import { debug } from '../debug/log.js';
30
30
  import {
31
31
  decideFallback, normalizeFallbackChain, grokQuotaFromUsageSnapshot,
32
- type FallbackDecision, type FallbackInput, type RotationOutcome,
32
+ type FallbackDecision, type FallbackInput, type FallbackStep, type RotationOutcome,
33
33
  } from './fallback-chain.js';
34
34
  import { resolveGrokCredential } from '../grok/credential.js';
35
35
  import { getOpenRouterApiKey } from '../config.js';
@@ -52,7 +52,7 @@ export function _setCodexAccountOutboundSenderForTesting(sender: CodexAccountOut
52
52
  /** ⛔⭐ 정적 import 로 읽는다 — 종전 `require()` 는 ESM 에서 «정의되지 않을 수 있고», 그러면
53
53
  * fail-soft 가 삼켜 ***config false 가 영영 무시된다***(리뷰 should-fix: 노브가 no-op 이 된다).
54
54
  * ⚠️ 순환 없음을 확인했다 — user-config 는 oauth 를 안 끌어온다. */
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 } } };
55
+ type RotationConfig = { llm?: { codexCreditReserve?: unknown; codexAccountRotation?: boolean; codexAccountAlerts?: boolean; codexAccountRotationThresholdPercent?: unknown; codexAccountRotationThresholdPercentByAccount?: Readonly<Record<string, unknown>>; codexCreditsAllowed?: boolean; codexResetAutoConsumePerDay?: number; codexQuotaPolicy?: unknown; fallbackChain?: unknown; openrouter?: { fallbackModel?: unknown } } };
56
56
 
57
57
  function readUserConfig(): RotationConfig {
58
58
  return getUserConfig();
@@ -238,11 +238,21 @@ function pinnedAccountExhausted(home: string, accountName: string): boolean {
238
238
 
239
239
  /** codex 한도 정책(`llm.codexQuotaPolicy` · 옛 `codexCreditsAllowed`) — 못 읽으면 `fallback`(크레딧은 돈이라 fail-closed). */
240
240
  export function codexQuotaPolicyFromConfig(): CodexQuotaPolicy {
241
- try { return resolveCodexQuotaPolicy(configReader().llm).policy; } catch { return 'fallback'; }
241
+ try { return resolveCodexQuotaPolicy(configReader().llm, codexQuotaPolicyFileValue()).policy; } catch { return 'fallback'; }
242
+ }
243
+
244
+ /** `policy/llm.yaml` 의 `credits.codex` — 파일 출처일 때만 값을 낸다(없거나 다른 출처면 undefined). 파일이 깨졌거나 못 읽으면 `unreadable`. */
245
+ function codexQuotaPolicyFileValue(): CodexQuotaPolicyFileValue | undefined {
246
+ try {
247
+ const loaded = loadLlmPolicy({ legacyConfig: {} });
248
+ if (!loaded.filePresent) return undefined;
249
+ if (!loaded.valid) return 'unreadable';
250
+ return loaded.sources['credits.codex'] === 'file' ? loaded.policy.credits.codex : undefined;
251
+ } catch { return 'unreadable'; }
242
252
  }
243
253
 
244
254
  function inspectedQuotaPolicy(): ResolvedCodexQuotaPolicy {
245
- try { return resolveCodexQuotaPolicy(configReader().llm); } catch { return { policy: 'fallback', source: 'default' }; }
255
+ try { return resolveCodexQuotaPolicy(configReader().llm, codexQuotaPolicyFileValue()); } catch { return { policy: 'fallback', source: 'default' }; }
246
256
  }
247
257
 
248
258
  /** 지금 계정의 크레딧 신호 — 판정 입력 두 칸. 신호를 이 계정에 귀속할 수 없으면 비운다(모른다). */
@@ -259,6 +269,64 @@ function codexCreditsAllowedFromConfig(): boolean {
259
269
  return codexPolicyAllowsCredits(codexQuotaPolicyFromConfig());
260
270
  }
261
271
 
272
+ /** SUBSCRIPTION-EXHAUST-REROUTE — 크레딧 예비분 기본. 10-11 운영 실측: 잔액 29,401 · 하루 소모 25,132 → 2,000 ≈ 하루 소모의 8%
273
+ * (폴백이 서는 동안 계정을 0 으로 만들지 않는 몫). `llm.codexCreditReserve` 로 바꾼다(0 = 잔액 0 일 때만 넘김). */
274
+ export const DEFAULT_CODEX_CREDIT_RESERVE = CODEX_CREDIT_RESERVE_DEFAULT;
275
+ /** 예비분 넘김의 OpenRouter 모델(설정 `llm.openrouter.fallbackModel` 이 없을 때) — 대표 라우팅 «중» 시작 모델. */
276
+ export const CREDIT_RESERVE_OPENROUTER_MODEL = 'openrouter/z-ai/glm-5.3';
277
+
278
+ /** `llm.codexCreditReserve` — 유효하지 않거나 못 읽으면 기본(크레딧은 돈이라 예비분을 지킨다). */
279
+ export function codexCreditReserveFromConfig(): number {
280
+ try {
281
+ const raw = configReader().llm?.codexCreditReserve;
282
+ return typeof raw === 'number' && Number.isFinite(raw) && raw >= 0 ? raw : DEFAULT_CODEX_CREDIT_RESERVE;
283
+ } catch { return DEFAULT_CODEX_CREDIT_RESERVE; }
284
+ }
285
+
286
+ /** grok 자격 «존재» 판독 — 테스트 격리 심(실 자격을 안 읽게). */
287
+ let grokCredentialPresent: () => boolean = () => resolveGrokCredential() !== null;
288
+ export function _setGrokCredentialPresentForTesting(fn: (() => boolean) | null): void {
289
+ grokCredentialPresent = fn ?? (() => resolveGrokCredential() !== null);
290
+ }
291
+
292
+ /** 회전 판정이 쓸 크레딧 허가와 그 출처.
293
+ * - `policy` 정책이 credits(옛 codexCreditsAllowed=true) — 종전 그대로.
294
+ * - `chain-step` CODEX-CREDITS-STEP: 정책은 fallback 이지만 체인이 `codex-credits` 칸에 «닿는다»
295
+ * (앞 칸 grok 등을 지금 못 쓰고 · 잔액 확인된 계정이 있다) ⇒ 이번 판정만 크레딧 계정을 고른다.
296
+ * - `none` 크레딧 ✗(`why` = 칸을 못 쓴 이유 · 칸이 없으면 생략).
297
+ * ⛔ 칸 판정은 `decideFallback` 를 그대로 부른다 — 체인 의미를 여기서 다시 짓지 않는다. 이 판독은 네트워크를 안 친다. */
298
+ type RotationCreditsGrant =
299
+ | { readonly allowed: true; readonly via: 'policy' }
300
+ | { readonly allowed: true; readonly via: 'chain-step'; readonly skipped: readonly { readonly step: FallbackStep; readonly why: string }[]; readonly creditAccountCount: number }
301
+ | { readonly allowed: false; readonly via: 'none'; readonly why?: string };
302
+
303
+ function rotationCreditsGrant(candidates: readonly RotationCandidate[]): RotationCreditsGrant {
304
+ const policy = codexQuotaPolicyFromConfig();
305
+ if (codexPolicyAllowsCredits(policy)) return { allowed: true, via: 'policy' };
306
+ if (!codexPolicyAllowsFallback(policy)) return { allowed: false, via: 'none' };
307
+ try {
308
+ const { chain } = normalizeFallbackChain(configReader().llm?.fallbackChain);
309
+ if (!chain.includes('codex-credits')) return { allowed: false, via: 'none' };
310
+ const creditAccountCount = codexCreditAccountNames(candidates, codexCreditReserveFromConfig()).length;
311
+ const fallbackModel = configReader().llm?.openrouter?.fallbackModel;
312
+ const decision = decideFallback({
313
+ rotation: { reason: 'no-candidate' },
314
+ chain,
315
+ grokAvailable: grokCredentialPresent(),
316
+ grokQuota: readCachedGrokQuota(),
317
+ ...(chain.includes('openrouter') ? {
318
+ openrouterFallbackModel: typeof fallbackModel === 'string' ? fallbackModel : undefined,
319
+ openrouterAvailable: Boolean(getOpenRouterApiKey()?.trim()),
320
+ } : {}),
321
+ codexCreditsAvailable: creditAccountCount > 0,
322
+ });
323
+ if (decision.action === 'codex-credits') return { allowed: true, via: 'chain-step', skipped: decision.skipped, creditAccountCount };
324
+ return { allowed: false, via: 'none', why: decision.action === 'stay' ? decision.why : `earlier-step:${decision.action === 'switch-backend' ? decision.backend : decision.action}` };
325
+ } catch {
326
+ return { allowed: false, via: 'none', why: 'chain-unreadable' };
327
+ }
328
+ }
329
+
262
330
  function buildRotationCandidates(path: string, now: number, env: NodeJS.ProcessEnv = process.env): RotationCandidate[] {
263
331
  return listCodexAccountsInStore(path)
264
332
  .map((row) => {
@@ -277,12 +345,13 @@ function buildRotationCandidates(path: string, now: number, env: NodeJS.ProcessE
277
345
  storeKey: row.storeKey,
278
346
  home,
279
347
  reached: readQuotaSignal(now, home),
348
+ resetCreditAvailability: resetCreditAvailability(home, now),
280
349
  ...(usedPercent === undefined ? {} : { usedPercent }),
281
350
  ...(credits.balance === undefined ? {} : { creditBalance: credits.balance }),
282
351
  ...(credits.hasCredits === undefined ? {} : { hasCredits: credits.hasCredits }),
283
- };
352
+ } satisfies RotationCandidate;
284
353
  })
285
- .filter((c): c is RotationCandidate => c !== null);
354
+ .filter((c): c is NonNullable<typeof c> => c !== null);
286
355
  }
287
356
 
288
357
  function rotationCreditPace(candidates: readonly RotationCandidate[], now: number) {
@@ -364,7 +433,7 @@ function codexAccountAlertsEnabled(): boolean {
364
433
  }
365
434
  }
366
435
 
367
- function notifyCodexAccountEvent(event: 'rotation' | 'reset-credit-consumed', details: Record<string, unknown>, text: string): void {
436
+ function notifyCodexAccountEvent(event: 'rotation' | 'reset-credit-consumed' | 'credit-spend-start', details: Record<string, unknown>, text: string): boolean {
368
437
  // ⛔ 끄더라도 «조용히» 사라지지 않는다 — 「왜 알림이 안 오나」를 나중에 로그로 답할 수 있어야 한다.
369
438
  if (!codexAccountAlertsEnabled()) {
370
439
  debug.log('oauth.codex-account', 'outbound-suppressed', {
@@ -373,20 +442,106 @@ function notifyCodexAccountEvent(event: 'rotation' | 'reset-credit-consumed', de
373
442
  knob: 'llm.codexAccountAlerts',
374
443
  ...details,
375
444
  });
376
- return;
445
+ return false;
377
446
  }
378
- const attempt = () => {
447
+ try {
379
448
  const delivered = codexAccountOutboundSender(text, 'alert');
380
449
  debug.log('oauth.codex-account', 'outbound', { event, delivered, ...details });
381
- };
382
- try {
383
- attempt();
450
+ return delivered;
384
451
  } catch (error) {
385
452
  debug.log('oauth.codex-account', 'outbound-failed', {
386
453
  event,
387
454
  message: error instanceof Error ? error.message : String(error),
388
455
  ...details,
389
456
  }, { level: 'warn' });
457
+ return false;
458
+ }
459
+ }
460
+
461
+ /** 크레딧 알림 잠금 — 이식 가능한 `wx` 잠금 파일(⛔ `flock(1)` 은 macOS 운영 호스트에 «없다» — mbp·node-b 실측 10-11).
462
+ * 기다림 상한을 넘기거나 오래된(죽은 소유자) 잠금이면 «알림을 잃지 않는 쪽»으로 간다:
463
+ * 오래된 잠금은 걷고, 상한을 넘기면 잠금 없이 보낸다(최악은 같은 날 중복 1통 — 누락보다 낫다). */
464
+ const CREDIT_SPEND_ALERT_LOCK_STALE_MS = 60_000;
465
+ let creditSpendAlertLockWaitMs = 5_000;
466
+ /** 시험 전용 — 기다림 상한을 줄인다(null = 기본 5초). */
467
+ export function _setCreditSpendAlertLockWaitMsForTesting(ms: number | null): void {
468
+ creditSpendAlertLockWaitMs = ms ?? 5_000;
469
+ }
470
+
471
+ function sleepSync(ms: number): void {
472
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
473
+ }
474
+
475
+ /** 잠갔으면 true(호출자가 풀어야 한다) · 상한을 넘겨 잠금 없이 가면 false. */
476
+ function acquireCreditSpendAlertLock(path: string, account: string): boolean {
477
+ const deadline = Date.now() + creditSpendAlertLockWaitMs;
478
+ for (;;) {
479
+ try {
480
+ const fd = openSync(path, 'wx');
481
+ try { writeFileSync(fd, String(process.pid)); } finally { closeSync(fd); }
482
+ return true;
483
+ } catch (error) {
484
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
485
+ }
486
+ try {
487
+ if (Date.now() - statSync(path).mtimeMs > CREDIT_SPEND_ALERT_LOCK_STALE_MS) {
488
+ debug.log('oauth.codex-account', 'credit-spend-alert-lock-stale', { account });
489
+ try { unlinkSync(path); } catch { /* 다른 소유자가 먼저 걷었다 */ }
490
+ continue;
491
+ }
492
+ } catch { continue; /* 그 사이 풀렸다 */ }
493
+ if (Date.now() >= deadline) {
494
+ debug.log('oauth.codex-account', 'credit-spend-alert-lock-timeout', { account, waitMs: creditSpendAlertLockWaitMs }, { level: 'warn' });
495
+ return false;
496
+ }
497
+ sleepSync(50);
498
+ }
499
+ }
500
+
501
+ /** UTC 일자별 성공한 계정 알림만 기록한다. 잠근 채 발송하여 동시 런도 중복 발송하지 않는다. */
502
+ function notifyCreditSpendStart(
503
+ account: string,
504
+ reason: 'credits-allowed' | 'credit-pace' | 'credits-rotated',
505
+ usage: readonly RotationCandidate[],
506
+ now: number,
507
+ counterPath: string,
508
+ ): void {
509
+ const day = new Date(now).toISOString().slice(0, 10);
510
+ const lockPath = `${counterPath}.lock`;
511
+ let locked = false;
512
+ try {
513
+ mkdirSync(dirname(counterPath), { recursive: true });
514
+ locked = acquireCreditSpendAlertLock(lockPath, account);
515
+ let accounts: string[] = [];
516
+ try {
517
+ const parsed = JSON.parse(readFileSync(counterPath, 'utf8')) as { day?: unknown; accounts?: unknown };
518
+ if (parsed.day === day && Array.isArray(parsed.accounts)) {
519
+ accounts = parsed.accounts.filter((value): value is string => typeof value === 'string');
520
+ }
521
+ } catch (error) {
522
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
523
+ }
524
+ if (accounts.includes(account)) {
525
+ debug.log('oauth.codex-account', 'credit-spend-alert-skipped', { account, reason: 'already-alerted-today' });
526
+ return;
527
+ }
528
+ const usageSummary = usage.map((candidate) => `${candidate.name}=${formatUsedPercent(candidate.usedPercent)}`).join(', ') || 'unknown';
529
+ if (!notifyCodexAccountEvent('credit-spend-start', { account, reason, day },
530
+ `💳 Codex credits in use\naccount: ${account}\nreason: ${reason}\nusage: ${usageSummary}`)) return;
531
+ const tempPath = `${counterPath}.${process.pid}.${Date.now()}.tmp`;
532
+ try {
533
+ writeFileSync(tempPath, `${JSON.stringify({ day, accounts: [...accounts, account] })}\n`, 'utf8');
534
+ renameSync(tempPath, counterPath);
535
+ } finally {
536
+ try { unlinkSync(tempPath); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
537
+ }
538
+ debug.log('oauth.codex-account', 'credit-spend-start', { account, reason, day }, { level: 'warn' });
539
+ } catch (error) {
540
+ debug.log('oauth.codex-account', 'credit-spend-alert-failed', {
541
+ account, message: error instanceof Error ? error.message : String(error),
542
+ }, { level: 'warn' });
543
+ } finally {
544
+ if (locked) { try { unlinkSync(lockPath); } catch { /* 이미 없다 */ } }
390
545
  }
391
546
  }
392
547
 
@@ -511,6 +666,8 @@ export function inspectCodexRotation(
511
666
  readonly candidateCount: number | undefined;
512
667
  readonly accountThresholds: ReturnType<typeof decideCodexRotation>['accountThresholds'];
513
668
  readonly to: string | undefined;
669
+ /** SUBSCRIPTION-EXHAUST-REROUTE — 크레딧 예비분에 닿아 `no-candidate` 가 됐을 때만 있다. */
670
+ readonly creditReserveHit?: { readonly balance: number; readonly reserve: number };
514
671
  } {
515
672
  const path = deps.storePath ?? authStorePath();
516
673
  const now = deps.now ?? Date.now();
@@ -531,7 +688,7 @@ export function inspectCodexRotation(
531
688
  const rawThreshold = rotationThresholdFromConfig();
532
689
  const thresholdPercentByAccount = rotationThresholdsByAccountFromConfig();
533
690
  const decision = decideCodexRotation({
534
- current, explicit, enabled, disabledProvenance: rotationConfig.state, currentReached, currentUsedPercent, accountOrder: accountOrderFromConfig(), resetCreditAvailability: resetCreditAvailability(currentHome, now), thresholdPercent: rawThreshold, thresholdPercentByAccount, creditsAllowed: codexCreditsAllowedFromConfig(), ...currentCredits, candidates, creditPace: rotationCreditPace(candidates, now),
691
+ current, explicit, enabled, disabledProvenance: rotationConfig.state, currentReached, currentUsedPercent, accountOrder: accountOrderFromConfig(), resetCreditAvailability: resetCreditAvailability(currentHome, now), thresholdPercent: rawThreshold, thresholdPercentByAccount, creditsAllowed: rotationCreditsGrant(candidates).allowed, creditReserve: codexCreditReserveFromConfig(), ...currentCredits, candidates, creditPace: rotationCreditPace(candidates, now),
535
692
  });
536
693
  // ⛔⭐ 나이는 «만료돼도» 낸다 — 「65분 전」과 「3일 전」은 다른 진단이다(리뷰 must-fix).
537
694
  const observedAtByHome: Record<string, number> = {};
@@ -553,6 +710,7 @@ export function inspectCodexRotation(
553
710
  candidates, observedAtByHome, freshByHome,
554
711
  knownAccountCount: listCodexAccountsInStore(path).length,
555
712
  reason: decision.reason, candidateCount: decision.candidateCount, accountThresholds: decision.accountThresholds, to: decision.to?.name,
713
+ ...(decision.creditReserveHit ? { creditReserveHit: decision.creditReserveHit } : {}),
556
714
  };
557
715
  }
558
716
 
@@ -561,7 +719,7 @@ export function resolveCodexAccountForRun(
561
719
  /** ⛔ 「설정을 우회하는 노브」를 두지 않는다(리뷰 must-fix) — 종전 `rotationEnabled` 는
562
720
  * 아무도 안 주면서 «config 를 덮을 수 있는» 인자였다. 그런 인자는 수용 기준을 스스로 깬다.
563
721
  * ⊕ `now` 도 아무도 안 주는 죽은 계약이라 지웠다. 남는 것은 «테스트 격리»뿐이다. */
564
- deps: { readonly storePath?: string } = {},
722
+ deps: { readonly storePath?: string; readonly counterPath?: string } = {},
565
723
  ): CodexAccountResolution {
566
724
  // ⛔ 「이름만 주고 홈을 안 준」 경우를 정본 기록으로 메운다 — 안 그러면 조용히 default 로 떨어지고
567
725
  // 관측은 `explicit` 이라 말한다(2026-08-11 72차 실측).
@@ -618,6 +776,7 @@ export function resolveCodexAccountForRun(
618
776
  const currentSignalAttributable = signalAttributableToAccount(currentHomeInfo);
619
777
 
620
778
  const creditPace = rotationCreditPace(candidates, now);
779
+ const creditsGrant = rotationCreditsGrant(candidates);
621
780
  const decision = decideCodexRotation({
622
781
  current,
623
782
  explicit,
@@ -629,11 +788,13 @@ export function resolveCodexAccountForRun(
629
788
  resetCreditAvailability: resetCreditAvailability(currentHome, now),
630
789
  thresholdPercent: rotationThresholdFromConfig(),
631
790
  thresholdPercentByAccount: rotationThresholdsByAccountFromConfig(),
632
- creditsAllowed: codexCreditsAllowedFromConfig(),
791
+ creditsAllowed: creditsGrant.allowed,
792
+ creditReserve: codexCreditReserveFromConfig(),
633
793
  ...currentCreditsInput(currentSignalAttributable ? currentHome : undefined, now),
634
794
  candidates,
635
795
  creditPace,
636
796
  });
797
+ observeCodexCreditsStep(creditsGrant, decision, current.name);
637
798
  debug.log('codex.rotation', 'credit-pace', {
638
799
  active: creditPace.active,
639
800
  todaySpent: creditPace.todaySpent,
@@ -641,6 +802,17 @@ export function resolveCodexAccountForRun(
641
802
  to: decision.reason === 'credit-pace' ? decision.to?.name ?? current.name : decision.to?.name,
642
803
  });
643
804
  observeRotation(decision, current.name);
805
+ // 발송 의무는 이 실행 경로에서 잠금 해제까지 기다린 뒤 하루 카운터에 기록한다.
806
+ // CREDIT-SPEND-ORDER(10-12) — «다른 계정의 선불 크레딧으로 옮김»(`rotated` ⊕ creditEntry)도 크레딧 진입이다.
807
+ const creditSpendReason = decision.reason === 'credits-allowed' || decision.reason === 'credit-pace' ? decision.reason
808
+ : decision.reason === 'rotated' && decision.creditEntry ? 'credits-rotated' as const : undefined;
809
+ if (creditSpendReason && !deps.counterPath && isUnderTestRunner(process.env)) {
810
+ // 시험 러너 가드(autoConsume 의 test-runtime-guard 와 같은 관용구) — 카운터를 주입 안 한 시험이 실제 카운터·송신기에 닿지 않는다.
811
+ debug.log('oauth.codex-account', 'credit-spend-alert-skipped', { account: decision.to?.name ?? current.name, reason: 'test-runtime-guard' });
812
+ } else if (creditSpendReason) {
813
+ notifyCreditSpendStart(decision.to?.name ?? current.name, creditSpendReason, candidates, now,
814
+ deps.counterPath ?? join(codexCredentialRoot(), 'budget', 'codex-credit-spend-alert.json'));
815
+ }
644
816
  // 순서 ② — 구독 남은 계정이 없고 리셋권이 있으면 «스스로 1개» 쓴다(일일 상한 · 비동기 · 이번 호출은 종전대로 머문다).
645
817
  // ⛔ 판정이 `reset-credit-available` 일 때만 닿는다 — 구독 남은 계정이 있으면(rotated) 여기 안 온다.
646
818
  if (decision.reason === 'reset-credit-available') void autoConsumeCodexResetCredit({ storePath: path });
@@ -844,6 +1016,65 @@ export function listCodexAccountsInStore(storePath?: string): StoredCodexAccount
844
1016
  * ⛔ 실패는 삼키되 «조용히»는 아니다 — 못 정했으면 그 사실을 관측에 남기고 `stay` 로 답한다.
845
1017
  * (판정이 못 돌아서 백엔드가 안 뜨는 일은 없어야 한다.)
846
1018
  */
1019
+ /** CODEX-CREDITS-STEP 관측 — 회전이 «구독이 다 찬» 판정에 닿았을 때만(크레딧으로 갔든 · 칸을 못 써 건너뛰었든) 한 줄.
1020
+ * ⛔ 비밀·홈 경로를 넣지 않는다(계정 이름 · 이유 · 건너뛴 칸). */
1021
+ function observeCodexCreditsStep(
1022
+ grant: RotationCreditsGrant,
1023
+ decision: { readonly reason: string; readonly to?: RotationCandidate; readonly accountThresholds?: readonly { readonly name: string; readonly status: string }[] },
1024
+ currentName: string,
1025
+ ): void {
1026
+ if (grant.via === 'policy') return;
1027
+ // 회전이 «크레딧 계정»으로 옮겼나 — 고른 계정의 구독 창이 이미 찼으면 크레딧 회전이다(구독 회전과 가른다).
1028
+ const toStatus = decision.to ? decision.accountThresholds?.find((row) => row.name === decision.to!.name)?.status : undefined;
1029
+ const creditRotation = decision.reason === 'rotated' && (toStatus === 'reached' || toStatus === 'threshold-reached');
1030
+ if (grant.via === 'chain-step' && (decision.reason === 'credits-allowed' || creditRotation)) {
1031
+ debug.log('oauth.fallback-chain', 'codex-credits-step', {
1032
+ site: 'rotation', outcome: 'credits', rotationReason: decision.reason, from: currentName, to: decision.to?.name ?? currentName,
1033
+ skipped: grant.skipped, creditAccountCount: grant.creditAccountCount,
1034
+ }, { level: 'warn' });
1035
+ } else if (grant.via === 'none' && decision.reason === 'no-candidate' && grant.why === 'codex-credits-unavailable') {
1036
+ debug.log('oauth.fallback-chain', 'codex-credits-step', {
1037
+ site: 'rotation', outcome: 'skipped', rotationReason: decision.reason, why: grant.why,
1038
+ });
1039
+ }
1040
+ }
1041
+
1042
+ /** 크레딧 잔액이 «확인된» codex 계정 이름(잔액 > 0 · 보유 ≠ false · 홈을 안다). 잔액을 모르면 넣지 않는다 — 돈이라 fail-closed.
1043
+ * (budget-gate `codexCreditCandidate` 와 같은 자.) */
1044
+ export function codexCreditAccountNames(candidates: readonly RotationCandidate[], reserve = 0): string[] {
1045
+ return creditCandidatesByBalance(candidates, reserve).map((c) => c.name);
1046
+ }
1047
+
1048
+ /** 명시 계정 «자신»의 크레딧 잔액이 확인되나(잔액 > 0 · 보유 ≠ false) — 신호를 귀속 못 하면 후보 목록의 같은 이름 줄. */
1049
+ function explicitAccountHasCredits(snapshot: {
1050
+ readonly current: { readonly name: string };
1051
+ readonly candidates: readonly RotationCandidate[];
1052
+ readonly currentCreditBalance?: number;
1053
+ readonly currentHasCredits?: boolean;
1054
+ }, reserve = 0): boolean {
1055
+ const self = snapshot.candidates.find((c) => c.name === snapshot.current.name);
1056
+ const balance = snapshot.currentCreditBalance ?? self?.creditBalance;
1057
+ const has = snapshot.currentHasCredits ?? self?.hasCredits;
1058
+ return typeof balance === 'number' && Number.isFinite(balance) && balance > reserve && has !== false;
1059
+ }
1060
+
1061
+ /** 명시 계정 자신을 후보 모양으로 — 홈을 모르면 없다(그때는 호스트 홈 그대로). */
1062
+ function explicitAccountCandidate(snapshot: {
1063
+ readonly current: { readonly name: string; readonly storeKey: string };
1064
+ readonly currentHome?: string;
1065
+ readonly candidates: readonly RotationCandidate[];
1066
+ }): RotationCandidate | undefined {
1067
+ const self = snapshot.candidates.find((c) => c.name === snapshot.current.name && c.home.length > 0);
1068
+ if (self) return self;
1069
+ const home = snapshot.currentHome?.trim();
1070
+ return home ? { name: snapshot.current.name, storeKey: snapshot.current.storeKey, home, reached: undefined } : undefined;
1071
+ }
1072
+
1073
+ /** 잔액이 가장 많은(확인된) 계정 — 크레딧 칸이 실제로 쓸 계정. */
1074
+ function richestCreditCandidate(candidates: readonly RotationCandidate[], reserve = 0): RotationCandidate | undefined {
1075
+ return creditCandidatesByBalance(candidates, reserve)[0];
1076
+ }
1077
+
847
1078
  export function resolveRunFallback(
848
1079
  env: NodeJS.ProcessEnv = process.env,
849
1080
  deps: {
@@ -853,6 +1084,8 @@ export function resolveRunFallback(
853
1084
  readonly grokQuota?: 'usable' | 'exhausted' | 'unknown';
854
1085
  /** OpenRouter 키 «존재» seam(테스트 격리) — 안 주면 `getOpenRouterApiKey()` 로 잰다. 키 값은 기록하지 않는다. */
855
1086
  readonly openrouterAvailable?: boolean;
1087
+ /** codex 크레딧 잔액 «확인» seam — 안 주면 회전 후보의 크레딧 신호(잔액 > 0 · 보유 ≠ false)로 잰다. */
1088
+ readonly codexCreditsAvailable?: boolean;
856
1089
  } & Pick<FallbackInput, 'currentStep' | 'currentCredentialRateLimited'> = {},
857
1090
  ): FallbackDecision {
858
1091
  try {
@@ -888,26 +1121,62 @@ export function resolveRunFallback(
888
1121
  // ⛔ grok 가용성은 «호출자가 확정»해서 줄 수 있다(테스트 격리). 안 주면 여기서 잰다.
889
1122
  // ⛔ «가용성 판정»이라 갱신을 유도하지 않는다 — 「자격이 있나」만 묻지 「지금 쓸 수 있나」를 묻지 않는다.
890
1123
  // (쓸 수 있는지는 실제 요청 경로가 접힌 해석으로 확인한다.)
891
- const grokAvailable = deps.grokAvailable ?? resolveGrokCredential() !== null;
1124
+ const grokAvailable = deps.grokAvailable ?? grokCredentialPresent();
892
1125
  // ⛔ 「지금 쓸 수 있나」는 «자격과 다른 축»이다. 안 주면 'unknown' — 그리고 unknown 은 «통과»다.
893
1126
  // ⇒ 「모르고 갔다」와 「알고 갔다」가 관측에서 갈린다(아래 grokQuota).
894
1127
  const grokQuota = deps.grokQuota ?? readCachedGrokQuota();
895
1128
  const fallbackModel = configReader().llm?.openrouter?.fallbackModel;
896
- const openrouterFallbackModel = typeof fallbackModel === 'string' ? fallbackModel : undefined;
897
- const openrouterAvailable = chain.includes('openrouter')
1129
+ // SUBSCRIPTION-EXHAUST-REROUTE — 크레딧 예비분에 닿은 «넘김»은 OpenRouter 가 먼저다(대표 OP 10-11: grok 이 아니다).
1130
+ // 운영 체인 `['codex-rotate','grok']` 에는 openrouter 칸이 없다 ⇒ 이 경로에만 codex 바로 뒤에 openrouter 를 끼운다
1131
+ // (나머지 칸은 그 뒤 · 키가 없으면 종전 체인으로). 모델은 `llm.openrouter.fallbackModel`, 없으면 «중» 기본 glm-5.3.
1132
+ // ⛔ 정책이 within-quota 면 체인 밖으로 안 나간다(종전 규칙 그대로).
1133
+ const reserveHit = snapshot.creditReserveHit;
1134
+ const reserveReroute = reserveHit !== undefined && codexPolicyAllowsFallback(quotaPolicy);
1135
+ const effectiveChain: readonly FallbackStep[] = reserveReroute
1136
+ ? ['codex-rotate', 'openrouter', ...chain.filter((step) => step !== 'codex-rotate' && step !== 'openrouter')]
1137
+ : chain;
1138
+ const openrouterFallbackModel = typeof fallbackModel === 'string' && fallbackModel.trim()
1139
+ ? fallbackModel
1140
+ : reserveReroute ? CREDIT_RESERVE_OPENROUTER_MODEL : undefined;
1141
+ const openrouterAvailable = effectiveChain.includes('openrouter')
898
1142
  && (deps.openrouterAvailable ?? Boolean(getOpenRouterApiKey()?.trim()));
899
- const fallbackBase = { rotation, chain, grokAvailable, grokQuota, openrouterFallbackModel, openrouterAvailable };
1143
+ // CODEX-CREDITS-STEP — 체인에 그 칸이 있을 때만 잰다(없으면 판정이 안 읽는다). 잔액을 «모르면» 없다고 본다(돈 · fail-closed).
1144
+ // ⛔ 사람이 계정을 «명시»했으면 그 계정의 잔액만 본다 — 다른 계정 잔액으로 칸을 열고 명시 계정으로 쏘면 fail-closed 가 깨진다.
1145
+ const explicitAccount = snapshot.reason === 'explicit';
1146
+ const creditReserve = codexCreditReserveFromConfig();
1147
+ const creditAccounts = !effectiveChain.includes('codex-credits') ? []
1148
+ : explicitAccount ? (explicitAccountHasCredits(snapshot, creditReserve) ? [snapshot.current.name] : [])
1149
+ : codexCreditAccountNames(snapshot.candidates, creditReserve);
1150
+ const codexCreditsAvailable = effectiveChain.includes('codex-credits') && (deps.codexCreditsAvailable ?? creditAccounts.length > 0);
1151
+ const fallbackBase = { rotation, chain: effectiveChain, grokAvailable, grokQuota, openrouterFallbackModel, openrouterAvailable, codexCreditsAvailable };
900
1152
  const fallbackInput: FallbackInput = deps.currentCredentialRateLimited === true && deps.currentStep
901
1153
  ? { ...fallbackBase, currentStep: deps.currentStep, currentCredentialRateLimited: true }
902
1154
  : { ...fallbackBase, ...(deps.currentStep ? { currentStep: deps.currentStep } : {}) };
903
- const decision = decideFallback(fallbackInput);
1155
+ const judged = decideFallback(fallbackInput);
1156
+ // CODEX-CREDITS-STEP — 칸에 닿았으면 «어느 계정 크레딧으로» 갈지도 정한다(잔액 많은 순 · 홈을 안다).
1157
+ // 런타임 재시도(grok 429)는 회전을 다시 거치지 않으므로 여기서 계정을 실어 보내야 자식이 그 계정으로 뜬다.
1158
+ // ⛔ 사람이 계정을 «명시»했으면(explicit) 계정을 바꾸지 않는다 — 의도가 이긴다(그 계정 그대로 크레딧).
1159
+ // 명시 계정이면 «그 계정»의 홈을 싣는다 — 계정은 안 바꾸되, 잔액을 확인한 계정과 발사 홈이 갈리지 않게.
1160
+ const creditTo = judged.action !== 'codex-credits' ? undefined
1161
+ : explicitAccount ? explicitAccountCandidate(snapshot)
1162
+ : richestCreditCandidate(snapshot.candidates, creditReserve);
1163
+ const decision: FallbackDecision = judged.action === 'codex-credits' && creditTo ? { ...judged, to: creditTo } : judged;
1164
+ if (reserveHit) {
1165
+ debug.log('llm.codex', 'credit-reserve-hit', {
1166
+ balance: reserveHit.balance, reserve: reserveHit.reserve,
1167
+ next: decision.action === 'switch-backend' ? decision.backend : decision.action === 'codex-rotate' ? 'codex-rotate' : decision.action === 'codex-credits' ? 'codex-credits' : `stay:${decision.why}`,
1168
+ ...(decision.action === 'switch-backend' && decision.backend === 'openrouter' ? { model: decision.model } : {}),
1169
+ openrouterAvailable, rerouted: reserveReroute,
1170
+ }, { level: 'warn' });
1171
+ }
904
1172
  // tier-map 칸(가격·창 근거) — 유료 전환은 «무슨 값으로 가는지»를 관측에 남긴다. 미등록이면 'unregistered'.
905
1173
  const openrouterSpec = decision.action === 'switch-backend' && decision.backend === 'openrouter'
906
1174
  ? lookupOpenRouterModelSpec(decision.model) : undefined;
907
1175
 
908
1176
  debug.log('oauth.fallback-chain', 'decide', {
909
1177
  rotationReason: rotation.reason,
910
- chain, usedDefault, quotaPolicy,
1178
+ chain: effectiveChain, usedDefault, quotaPolicy,
1179
+ ...(reserveHit ? { creditReserveHit: true } : {}),
911
1180
  rotationResult: rotation.reason,
912
1181
  // ⛔⭐ 「안 줬다」를 «false 로 접지 않는다»(무인 리뷰 must-fix · UNKNOWN-DEFAULT).
913
1182
  // 호출자가 이 값을 생략하면 그것은 「한도가 아니었다」가 «아니라» ***「안 쟀다」***다.
@@ -915,11 +1184,11 @@ export function resolveRunFallback(
915
1184
  credentialRateLimited: fallbackInput.currentCredentialRateLimited === undefined
916
1185
  ? 'unknown'
917
1186
  : fallbackInput.currentCredentialRateLimited === true,
918
- selectedChainStep: decision.action === 'codex-rotate' ? 'codex-rotate' : decision.action === 'switch-backend' ? decision.backend : null,
1187
+ selectedChainStep: decision.action === 'codex-rotate' ? 'codex-rotate' : decision.action === 'switch-backend' ? decision.backend : decision.action === 'codex-credits' ? 'codex-credits' : null,
919
1188
  ...(dropped.length > 0 ? { droppedSteps: dropped } : {}),
920
1189
  grokAvailable,
921
1190
  grokQuota,
922
- ...(chain.includes('openrouter') ? { openrouterAvailable, openrouterModelConfigured: Boolean(openrouterFallbackModel?.trim()) } : {}),
1191
+ ...(effectiveChain.includes('openrouter') ? { openrouterAvailable, openrouterModelConfigured: Boolean(openrouterFallbackModel?.trim()) } : {}),
923
1192
  action: decision.action,
924
1193
  ...(decision.action === 'switch-backend' ? { backend: decision.backend } : {}),
925
1194
  ...(decision.action === 'switch-backend' && decision.backend === 'openrouter'
@@ -928,7 +1197,50 @@ export function resolveRunFallback(
928
1197
  ...(decision.action === 'stay' ? { why: decision.why } : {}),
929
1198
  ...(decision.action === 'codex-rotate' ? { to: decision.to.name } : {}),
930
1199
  });
931
- if (decision.action === 'switch-backend') {
1200
+ // 칸에 «닿았나» — 판정이 체인을 걸었고(한도 오류 재시도 · codex 축이 끝남) 그 걸음이 칸을 지났나.
1201
+ // 최종 결정과 무관하다: 칸을 건너뛰고 뒤 칸으로 갔든, 뒤 칸까지 못 써 머물렀든 도달은 도달이다.
1202
+ const selectedStep: FallbackStep | undefined = decision.action === 'switch-backend'
1203
+ ? (decision.backend === 'claude' ? 'claude-pty' : decision.backend) : undefined;
1204
+ const walked = fallbackInput.currentCredentialRateLimited === true
1205
+ || rotation.reason === 'no-candidate' || rotation.reason === 'disabled' || rotation.reason === 'reset-credit-available'
1206
+ || ('to' in rotation && !chain.includes('codex-rotate'));
1207
+ const walkStart = fallbackInput.currentCredentialRateLimited === true ? fallbackInput.currentStep : 'codex-rotate';
1208
+ const walkIdx = walkStart === undefined ? -1 : chain.indexOf(walkStart);
1209
+ const walkRest = walkIdx === -1 ? chain : chain.slice(walkIdx + 1);
1210
+ const reachedCredits = walked && walkRest.includes('codex-credits')
1211
+ && (selectedStep === undefined || walkRest.indexOf('codex-credits') < walkRest.indexOf(selectedStep));
1212
+ if (reachedCredits && decision.action !== 'codex-credits') {
1213
+ // 칸에 «닿았지만» 잔액 확인된 계정이 없어 못 썼다 — 실패한 도달도 이유와 함께 남긴다.
1214
+ debug.log('oauth.fallback-chain', 'codex-credits-step', {
1215
+ site: 'chain', outcome: 'skipped', why: 'codex-credits-unavailable', next: selectedStep ?? null, rotationReason: rotation.reason, quotaPolicy, chain,
1216
+ grokAvailable, grokQuota, currentStep: fallbackInput.currentStep ?? null,
1217
+ credentialRateLimited: fallbackInput.currentCredentialRateLimited === true,
1218
+ });
1219
+ }
1220
+ if (decision.action === 'codex-credits') {
1221
+ // ⭐ 체인이 «마지막 칸»(유료)에 닿을 때마다 한 줄 — 왜 앞 칸들을 못 썼나를 값으로(비밀·홈 경로 없음).
1222
+ debug.log('oauth.fallback-chain', 'codex-credits-step', {
1223
+ site: 'chain', outcome: 'credits',
1224
+ to: decision.to?.name ?? null,
1225
+ rotationReason: rotation.reason,
1226
+ quotaPolicy,
1227
+ chain,
1228
+ skipped: decision.skipped,
1229
+ grokAvailable,
1230
+ grokQuota,
1231
+ currentStep: fallbackInput.currentStep ?? null,
1232
+ credentialRateLimited: fallbackInput.currentCredentialRateLimited === true,
1233
+ creditAccountCount: deps.codexCreditsAvailable === undefined ? creditAccounts.length : 'seam',
1234
+ }, { level: 'warn' });
1235
+ emitDecision({
1236
+ kind: 'ROUTE',
1237
+ what: '공급자 codex(크레딧) — 체인 마지막 칸',
1238
+ reason: `회전 ${rotation.reason} · 정책 ${quotaPolicy} · 건너뜀 ${decision.skipped.map((x) => `${x.step}:${x.why}`).join(', ') || '없음'}${fallbackInput.currentCredentialRateLimited ? ' · 한도 오류' : ''}`,
1239
+ purpose: '구독·grok 이 모두 막혀 선불 크레딧으로 작업을 이어 간다',
1240
+ target: 'openai-codex(credits)',
1241
+ paths: chain.length,
1242
+ });
1243
+ } else if (decision.action === 'switch-backend') {
932
1244
  const openrouterNote = decision.backend === 'openrouter'
933
1245
  ? ` · API 과금 ${decision.model} (${openrouterSpec ? `${openrouterSpec.label} · ${openrouterSpec.rationale}` : 'tier-map 미등록'})`
934
1246
  : '';
@@ -9,15 +9,20 @@
9
9
  // credits — 한도 안 ⊕ 크레딧(찬 계정을 선불 크레딧으로 계속). 크레딧도 떨어져 요청이 한도 오류를 내면 폴백 체인.
10
10
  //
11
11
  // ⛔ 옛 키 `llm.codexCreditsAllowed: true` 는 `credits` 로 읽는다(새 키가 있으면 새 키가 이긴다).
12
+ // ⭐ 새 자리 = `policy/llm.yaml` 의 `credits.codex`(use→credits · fallback→fallback · never→within-quota).
13
+ // 풀이 순서: env → 정책 파일(파일 출처일 때만) → 옛 `llm.codexQuotaPolicy` → 옛 `llm.codexCreditsAllowed` → fallback.
14
+ // 옛 키는 지우지 않고 읽기만 한다(적용·가려짐은 `codex.policy` 관측으로 남는다).
12
15
  // ⛔ 못 읽거나 모르는 값이면 `fallback` — 크레딧은 돈이라 fail-closed(종전 기본과 같다).
13
16
 
17
+ import { debug } from '../debug/log.js';
18
+
14
19
  export const CODEX_QUOTA_POLICIES = ['within-quota', 'fallback', 'credits'] as const;
15
20
  export type CodexQuotaPolicy = (typeof CODEX_QUOTA_POLICIES)[number];
16
21
 
17
22
  export interface ResolvedCodexQuotaPolicy {
18
23
  readonly policy: CodexQuotaPolicy;
19
- /** 어디서 정해졌나 — `config`(새 키) · `legacy-credits`(옛 `codexCreditsAllowed`) · `default` · `invalid`(모르는 값 → 기본). */
20
- readonly source: 'env' | 'config' | 'legacy-credits' | 'default' | 'invalid';
24
+ /** 어디서 정해졌나 — `policy-file`(policy/llm.yaml credits.codex) · `config`(옛 `codexQuotaPolicy`) · `legacy-credits`(옛 `codexCreditsAllowed`) · `default` · `invalid`(모르는 값 → 기본). */
25
+ readonly source: 'env' | 'policy-file' | 'config' | 'legacy-credits' | 'default' | 'invalid';
21
26
  }
22
27
 
23
28
  export const CODEX_QUOTA_POLICY_LABEL: Readonly<Record<CodexQuotaPolicy, string>> = {
@@ -30,16 +35,40 @@ export function isCodexQuotaPolicy(value: unknown): value is CodexQuotaPolicy {
30
35
  return typeof value === 'string' && (CODEX_QUOTA_POLICIES as readonly string[]).includes(value);
31
36
  }
32
37
 
38
+ /** `policy/llm.yaml` 의 `credits.codex` 값. `unreadable` = 파일이 있는데 깨졌거나 못 읽음(fail-closed 로 fallback). */
39
+ export type CodexQuotaPolicyFileValue = 'use' | 'fallback' | 'never' | 'unreadable';
40
+
41
+ const FILE_VALUE_TO_POLICY: Readonly<Record<Exclude<CodexQuotaPolicyFileValue, 'unreadable'>, CodexQuotaPolicy>> = {
42
+ use: 'credits',
43
+ fallback: 'fallback',
44
+ never: 'within-quota',
45
+ };
46
+
33
47
  export function resolveCodexQuotaPolicy(
34
48
  llm: { readonly codexQuotaPolicy?: unknown; readonly codexCreditsAllowed?: unknown } | undefined,
49
+ file?: CodexQuotaPolicyFileValue,
35
50
  ): ResolvedCodexQuotaPolicy {
36
51
  // A Pod has no copy of the host config; the launcher hands the host's policy down in this variable
37
52
  // (09-29: without it every Pod child fell back to grok while all three accounts still had credits).
38
53
  const fromEnv = process.env.ELANOUS_CODEX_QUOTA_POLICY?.trim();
39
54
  if (fromEnv && isCodexQuotaPolicy(fromEnv)) return { policy: fromEnv, source: 'env' };
55
+ // 정책 파일이 깨졌으면 옛 키가 credits 라 해도 따르지 않는다 — 크레딧은 돈이라 닫는다.
56
+ if (file === 'unreadable') return { policy: 'fallback', source: 'invalid' };
40
57
  const raw = llm?.codexQuotaPolicy;
41
- if (isCodexQuotaPolicy(raw)) return { policy: raw, source: 'config' };
42
- if (llm?.codexCreditsAllowed === true) return { policy: 'credits', source: 'legacy-credits' };
58
+ const legacy: { key: string; value: unknown; policy: CodexQuotaPolicy } | undefined = isCodexQuotaPolicy(raw)
59
+ ? { key: 'llm.codexQuotaPolicy', value: raw, policy: raw }
60
+ : llm?.codexCreditsAllowed === true ? { key: 'llm.codexCreditsAllowed', value: true, policy: 'credits' } : undefined;
61
+ if (file !== undefined) {
62
+ const policy = FILE_VALUE_TO_POLICY[file];
63
+ if (legacy && legacy.policy !== policy) debug.log('codex.policy', 'legacy-key-shadowed', { key: legacy.key, legacy: legacy.value, file });
64
+ return { policy, source: 'policy-file' };
65
+ }
66
+ if (legacy) {
67
+ debug.log('codex.policy', 'legacy-key-applied', { key: legacy.key, value: legacy.value });
68
+ return legacy.key === 'llm.codexQuotaPolicy'
69
+ ? { policy: legacy.policy, source: 'config' }
70
+ : { policy: 'credits', source: 'legacy-credits' };
71
+ }
43
72
  if (raw !== undefined) return { policy: 'fallback', source: 'invalid' };
44
73
  return { policy: 'fallback', source: 'default' };
45
74
  }