elanous 0.2.22 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (705) hide show
  1. package/NOTICE +7 -0
  2. package/README.md +32 -22
  3. package/apps/pwa/out/404/index.html +1 -1
  4. package/apps/pwa/out/404.html +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{119fc678.6f591cdb84ca5791.js → 0673cc54.8e2ef1c23e49e190.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/{a9f2b54d-40c16bd0efdb04b8.js → 0a09e062-34f747a17d8d8c2e.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/{f8626309.a4e5d362b1d55e59.js → 0e3acd34.d42b2ef87fdf4234.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +77 -0
  9. package/apps/pwa/out/_next/static/chunks/{7175-350f4ce2873bc038.js → 1325-dc3f3c21eb181cb4.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +34 -0
  11. package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +24 -0
  12. package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{6767.8e6c5f6e744fcd9f.js → 1505.5826671109111e75.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 1669-da843ed73aa301f5.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +36 -0
  23. package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +122 -0
  24. package/apps/pwa/out/_next/static/chunks/2528.0af5c0e1fad88df6.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +62 -0
  27. package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{7899.597af4a9996c907f.js → 2721.bc9a8f36bd85c07a.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +149 -0
  35. package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +309 -0
  39. package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{d70879db.c66c56a95a91efc6.js → 4c5e95ce.83e9b16add6fcd8c.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/{6158.566c890adc06a459.js → 5313.1697309c532b9856.js} +2 -2
  52. package/apps/pwa/out/_next/static/chunks/{79b78388.663188133449d707.js → 53746282.1be36038d1abd949.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5578.ac963634373b28c6.js +286 -0
  56. package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5967-c0a80f8ffe5d4050.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 5974.360f8441eb0ac1ec.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +74 -0
  64. package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/6571-926ddf024a6ad5df.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +64 -0
  69. package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/{9832.2ab2e818013574c7.js → 6702.ebbd165d5e4a4a14.js} +1 -1
  71. package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +32 -0
  73. package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +24 -0
  74. package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +93 -0
  78. package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 740.90b31555290236a9.js} +2 -2
  83. package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +148 -0
  84. package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +226 -0
  85. package/apps/pwa/out/_next/static/chunks/{9070.067c106528ec1ae6.js → 7518.49161e8f5814abdb.js} +1 -1
  86. package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +27 -0
  87. package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +115 -0
  90. package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7905.b230b9d06cb4ad5d.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{8194.484f51e273d9abd2.js → 8264.00e434fbd02e9311.js} +16 -16
  97. package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +8 -0
  99. package/apps/pwa/out/_next/static/chunks/{088d8e8a.8069543d082bec12.js → 83cfc5c8.349535543c1548b6.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +23 -0
  101. package/apps/pwa/out/_next/static/chunks/{1948.5f35b39eef222a94.js → 8438.ee7f0df3b3f6fcb4.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/{4588.ad3364cb7fb97ada.js → 8532.a86e99acf9b761cb.js} +2 -2
  105. package/apps/pwa/out/_next/static/chunks/8569.b853a8daa8f31d12.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8925-2abfb48af477fb7f.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{88729267.9c6c000157c83f2f.js → 89849432.9eaa2f4c9c010965.js} +3 -3
  110. package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +90 -0
  111. package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +94 -0
  112. package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +41 -0
  114. package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +287 -0
  116. package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +79 -0
  117. package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/{3522-1de1319d028ac89c.js → 9502-32c5dc22e8f120f5.js} +3 -3
  120. package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +104 -0
  123. package/apps/pwa/out/_next/static/chunks/{7630.2b9a68659053ff4e.js → 9976.8f28864317639fe6.js} +16 -16
  124. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ad3bd8084048bee4.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/consult/{page-80ed511e02c81412.js → page-6168a7c43e6ef8f5.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-48fb31810693c357.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cb154acadb025414.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/b4e12462-5fab4a813a63dadd.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → ce383d63.d2ede338788ed18e.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{framework-5c169757551134ed.js → framework-b2763b10dc6b3836.js} +1 -1
  179. package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +1 -0
  184. package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +1 -0
  185. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
  186. package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → rfkMm4movqkqMebBrCXiR}/_buildManifest.js +1 -1
  187. package/apps/pwa/out/approvals/index.html +1 -1
  188. package/apps/pwa/out/approvals/index.txt +23 -23
  189. package/apps/pwa/out/autopilot/index.html +1 -1
  190. package/apps/pwa/out/autopilot/index.txt +23 -23
  191. package/apps/pwa/out/board/index.html +1 -1
  192. package/apps/pwa/out/board/index.txt +23 -23
  193. package/apps/pwa/out/botlab/index.html +1 -1
  194. package/apps/pwa/out/botlab/index.txt +22 -22
  195. package/apps/pwa/out/bots/index.html +1 -1
  196. package/apps/pwa/out/bots/index.txt +22 -22
  197. package/apps/pwa/out/ceo/index.html +1 -1
  198. package/apps/pwa/out/ceo/index.txt +23 -23
  199. package/apps/pwa/out/chat/index.html +1 -1
  200. package/apps/pwa/out/chat/index.txt +22 -22
  201. package/apps/pwa/out/consult/index.html +1 -1
  202. package/apps/pwa/out/consult/index.txt +23 -23
  203. package/apps/pwa/out/control/index.html +1 -1
  204. package/apps/pwa/out/control/index.txt +23 -23
  205. package/apps/pwa/out/decisions/index.html +1 -1
  206. package/apps/pwa/out/decisions/index.txt +22 -22
  207. package/apps/pwa/out/design-check/index.html +1 -1
  208. package/apps/pwa/out/design-check/index.txt +23 -23
  209. package/apps/pwa/out/editor/index.html +1 -1
  210. package/apps/pwa/out/editor/index.txt +24 -23
  211. package/apps/pwa/out/exec/index.html +1 -1
  212. package/apps/pwa/out/exec/index.txt +23 -23
  213. package/apps/pwa/out/field/index.html +1 -1
  214. package/apps/pwa/out/field/index.txt +23 -23
  215. package/apps/pwa/out/healthz +1 -1
  216. package/apps/pwa/out/index.html +1 -1
  217. package/apps/pwa/out/index.txt +22 -22
  218. package/apps/pwa/out/inside/index.html +1 -1
  219. package/apps/pwa/out/inside/index.txt +22 -22
  220. package/apps/pwa/out/intake/index.html +1 -1
  221. package/apps/pwa/out/intake/index.txt +23 -23
  222. package/apps/pwa/out/live/index.html +1 -1
  223. package/apps/pwa/out/live/index.txt +23 -23
  224. package/apps/pwa/out/live-run/index.html +1 -1
  225. package/apps/pwa/out/live-run/index.txt +24 -23
  226. package/apps/pwa/out/loops/index.html +1 -1
  227. package/apps/pwa/out/loops/index.txt +22 -22
  228. package/apps/pwa/out/market/index.html +1 -1
  229. package/apps/pwa/out/market/index.txt +22 -22
  230. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  231. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  232. package/apps/pwa/out/morning/index.html +1 -1
  233. package/apps/pwa/out/morning/index.txt +23 -23
  234. package/apps/pwa/out/observatory/index.html +1 -1
  235. package/apps/pwa/out/observatory/index.txt +23 -23
  236. package/apps/pwa/out/ops/checklist/index.html +1 -1
  237. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  238. package/apps/pwa/out/ops/release/index.html +1 -1
  239. package/apps/pwa/out/ops/release/index.txt +22 -22
  240. package/apps/pwa/out/ops/seats/index.html +1 -1
  241. package/apps/pwa/out/ops/seats/index.txt +22 -22
  242. package/apps/pwa/out/outputs/index.html +1 -1
  243. package/apps/pwa/out/outputs/index.txt +22 -22
  244. package/apps/pwa/out/reflection/index.html +1 -1
  245. package/apps/pwa/out/reflection/index.txt +23 -23
  246. package/apps/pwa/out/scheduler/index.html +1 -1
  247. package/apps/pwa/out/scheduler/index.txt +23 -23
  248. package/apps/pwa/out/sessions/index.html +1 -1
  249. package/apps/pwa/out/sessions/index.txt +22 -22
  250. package/apps/pwa/out/settings/devices/index.html +1 -1
  251. package/apps/pwa/out/settings/devices/index.txt +23 -23
  252. package/apps/pwa/out/settings/index.html +1 -1
  253. package/apps/pwa/out/settings/index.txt +23 -23
  254. package/apps/pwa/out/setup/done/index.html +1 -1
  255. package/apps/pwa/out/setup/done/index.txt +23 -23
  256. package/apps/pwa/out/setup/index.html +1 -1
  257. package/apps/pwa/out/setup/index.txt +23 -23
  258. package/apps/pwa/out/share/index.html +1 -1
  259. package/apps/pwa/out/share/index.txt +23 -23
  260. package/apps/pwa/out/showroom/index.html +1 -1
  261. package/apps/pwa/out/showroom/index.txt +22 -22
  262. package/apps/pwa/out/tasks/index.html +1 -1
  263. package/apps/pwa/out/tasks/index.txt +23 -23
  264. package/apps/pwa/out/term/index.html +1 -1
  265. package/apps/pwa/out/term/index.txt +23 -23
  266. package/apps/pwa/out/today/index.html +1 -1
  267. package/apps/pwa/out/today/index.txt +23 -23
  268. package/apps/pwa/out/trace/index.html +1 -1
  269. package/apps/pwa/out/trace/index.txt +23 -23
  270. package/apps/pwa/out/vault/index.html +1 -1
  271. package/apps/pwa/out/vault/index.txt +23 -23
  272. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  273. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  274. package/apps/pwa/out/workflows/index.html +1 -1
  275. package/apps/pwa/out/workflows/index.txt +23 -23
  276. package/apps/pwa/out/workspace/index.html +1 -1
  277. package/apps/pwa/out/workspace/index.txt +23 -23
  278. package/apps/pwa/out/worktrees/index.html +1 -1
  279. package/apps/pwa/out/worktrees/index.txt +23 -23
  280. package/catalog/models/anthropic/claude-haiku-5-5.yaml +32 -0
  281. package/catalog/models/anthropic/claude-opus-5-5.yaml +5 -0
  282. package/catalog/models/anthropic/claude-sonnet-5-5.yaml +31 -0
  283. package/catalog/models/anthropic/claude-sonnet-5.yaml +8 -2
  284. package/catalog/models/openai/gpt-6-astra.yaml +6 -0
  285. package/catalog/models/openai/gpt-6-luna.yaml +6 -0
  286. package/catalog/models/openai/gpt-6-sol.yaml +6 -0
  287. package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
  288. package/graphs/log-horizon/recipes.yaml +3 -0
  289. package/graphs/release/recipes.yaml +1 -1
  290. package/graphs/release/release-loop.yaml +6 -2
  291. package/graphs/release-rehearsal/recipes.yaml +12 -0
  292. package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
  293. package/package.json +4 -1
  294. package/scripts/bench-report.ts +82 -3
  295. package/scripts/ci-typecheck-changed.ts +10 -1
  296. package/scripts/hq/hq-fence-wrapper.sh +148 -0
  297. package/scripts/lib/typecheck-ratchet.ts +51 -0
  298. package/scripts/openai-relay-server.ts +5 -1
  299. package/scripts/release-loop/docs-node.ts +258 -43
  300. package/scripts/release-loop/gate-method.ts +59 -0
  301. package/scripts/release-loop/gate-node.ts +73 -7
  302. package/scripts/release-loop/gate-wall.ts +208 -0
  303. package/scripts/release-loop/known-issues-node.ts +53 -8
  304. package/scripts/release-loop/publish-at.ts +14 -0
  305. package/scripts/release-loop/publish-node.ts +17 -2
  306. package/scripts/release-loop/resume-release.ts +34 -4
  307. package/scripts/release-loop/resume.ts +53 -0
  308. package/scripts/release-loop/shard-plan.ts +3 -11
  309. package/scripts/release-loop/tui-node-checks.ts +22 -0
  310. package/scripts/release-loop/unattended-release.ts +8 -1
  311. package/scripts/rhythm/daily.ts +51 -2
  312. package/scripts/rhythm/weekly.ts +3 -2
  313. package/scripts/tsc-main-safety-net.ts +189 -0
  314. package/scripts/usage-rollup.ts +8 -4
  315. package/src/acp/codex-plugins.ts +48 -6
  316. package/src/agent/monad-agent-turn.ts +67 -5
  317. package/src/agent/run-context.ts +10 -0
  318. package/src/agent/tool-profile.ts +28 -2
  319. package/src/agent-mission/acp-judge.ts +8 -2
  320. package/src/agent-mission/capabilities-cli.ts +16 -1
  321. package/src/agent-mission/driver.ts +1 -1
  322. package/src/agent-mission/pr-review-watch.ts +6 -2
  323. package/src/agent-mission/review-loop.ts +33 -5
  324. package/src/agent-substrate/acp-reviewer.ts +17 -8
  325. package/src/agent-substrate/pr-reviewer.ts +164 -28
  326. package/src/agent-substrate/review-intent.ts +6 -4
  327. package/src/agent-substrate/self-review-cli.ts +111 -16
  328. package/src/agent-substrate/shard-boundary-intent.ts +92 -26
  329. package/src/ask-user-question/pending-questions.ts +8 -2
  330. package/src/autopilot/build/llm-conflict-merge.ts +216 -12
  331. package/src/autopilot/codebase-grounding.ts +44 -0
  332. package/src/autopilot/llm-control-brain.ts +13 -1
  333. package/src/autopilot/mission-codebase-gate.ts +8 -77
  334. package/src/autopilot/pr-manager.ts +12 -3
  335. package/src/boot/acp-server.ts +4 -7
  336. package/src/boot/acp-socket-path.ts +7 -0
  337. package/src/briefing/brief-items.ts +21 -13
  338. package/src/chat/index.ts +12 -3
  339. package/src/cli/agent-cli.ts +20 -0
  340. package/src/cli/brief-cli.ts +27 -21
  341. package/src/cli/cli-user-error.ts +9 -2
  342. package/src/cli/log-event-names.ts +3 -0
  343. package/src/cli/logs-cli.ts +1 -0
  344. package/src/cli/mcp-login.ts +111 -37
  345. package/src/cli/plugin-cli.ts +21 -1
  346. package/src/cli/pod-cli.ts +26 -0
  347. package/src/cli/pr-cli.ts +30 -2
  348. package/src/cli/pr-land-overlap-evidence.ts +131 -0
  349. package/src/cli/release-cli.ts +53 -11
  350. package/src/cli/self-update.ts +173 -10
  351. package/src/cli/skills-cli.ts +12 -3
  352. package/src/cli/tasks-cli.ts +133 -27
  353. package/src/context-bus/context-first.ts +51 -2
  354. package/src/context-bus/context-now-surfaces.ts +13 -2
  355. package/src/context-bus/long-term-memory.ts +3 -1
  356. package/src/context-bus/nightly-condense.ts +3 -2
  357. package/src/core-turn/run-goal-loop.ts +86 -2
  358. package/src/dashboard/chat-fold.ts +20 -0
  359. package/src/dashboard/first-screen-band.ts +151 -1
  360. package/src/dashboard/index.ts +27 -5
  361. package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -3
  362. package/src/dashboard/stale-install-check.ts +13 -2
  363. package/src/dashboard/turn-stream-formatter.ts +25 -0
  364. package/src/debug/log-diet.ts +159 -0
  365. package/src/debug/log.ts +52 -17
  366. package/src/display/coordinator.ts +25 -2
  367. package/src/domains/coo-admin-tool.ts +27 -0
  368. package/src/domains/cron-match.ts +1 -0
  369. package/src/domains/outbound-alert.ts +17 -6
  370. package/src/domains/release-tool.ts +119 -10
  371. package/src/domains/schedule-registry.ts +10 -7
  372. package/src/domains/schedule-runner.ts +1 -0
  373. package/src/elanous-config-dir.ts +2 -2
  374. package/src/elanous-daemon.ts +3 -3
  375. package/src/execution-loop/launch-gate.ts +1 -1
  376. package/src/expression/i18n/messages.en.ts +5 -0
  377. package/src/expression/i18n/messages.ja.ts +5 -0
  378. package/src/expression/i18n/messages.ko.ts +5 -0
  379. package/src/expression/i18n/messages.zh.ts +5 -0
  380. package/src/expression/i18n/types.ts +8 -0
  381. package/src/github/pr-labels.ts +1 -0
  382. package/src/graph-runner/runner.ts +54 -5
  383. package/src/harness/dispatch-backpressure.ts +259 -0
  384. package/src/harness/harness-cli-command.ts +107 -28
  385. package/src/harness/harness-seams.ts +2 -20
  386. package/src/harness/harness-substrate-default.ts +72 -1
  387. package/src/harness/nested-elanous-policy.ts +4 -2
  388. package/src/hq/fence-wrapper.ts +7 -0
  389. package/src/hq/hq.ts +33 -2
  390. package/src/hq/lease.ts +13 -0
  391. package/src/hq/seats.ts +114 -18
  392. package/src/index.ts +163 -64
  393. package/src/intake-plane/collect-github.ts +19 -1
  394. package/src/intake-plane/digest.ts +45 -20
  395. package/src/intake-plane/lens.ts +36 -8
  396. package/src/intelligence-map/model-alias.ts +6 -0
  397. package/src/intelligence-map/model-catalog.ts +41 -0
  398. package/src/knowledge/kgs/pack.ts +5 -2
  399. package/src/knowledge/kgs/sqlite-store.ts +15 -0
  400. package/src/knowledge/tools/knowledge-query.ts +20 -2
  401. package/src/knowledge/write.ts +234 -2
  402. package/src/llm/bedrock-provider.ts +175 -0
  403. package/src/llm/bedrock.ts +345 -0
  404. package/src/llm/provider-credentials.ts +3 -1
  405. package/src/llm.ts +306 -35
  406. package/src/loops/budget.ts +150 -0
  407. package/src/loops/orchestrator/seat-share.ts +6 -49
  408. package/src/loops/orchestrator/traffic.ts +5 -81
  409. package/src/market/signed-index.ts +42 -0
  410. package/src/mcp/mcp-oauth.ts +136 -17
  411. package/src/model-tier/llm-tier-map.ts +48 -4
  412. package/src/models/prompts.ts +22 -1
  413. package/src/mss/logging/horizon-watch.ts +168 -0
  414. package/src/mss/logging/log-store.ts +204 -15
  415. package/src/native-tool-catalog.ts +1 -1
  416. package/src/nexus/api/consult-requests.ts +7 -10
  417. package/src/nexus/api/discord-trigger-bot.ts +6 -3
  418. package/src/nexus/api/error-reports.ts +30 -8
  419. package/src/nexus/api/http-server.ts +8 -0
  420. package/src/nexus/api/loop-resources.ts +42 -0
  421. package/src/nexus/api/outbound-report.ts +8 -3
  422. package/src/nexus/api/telegram-trigger-bot.ts +6 -3
  423. package/src/nexus/index.ts +10 -0
  424. package/src/nexus/outbound/format.ts +2 -0
  425. package/src/nexus/outbound/router.ts +198 -13
  426. package/src/oauth/codex-account-rotation.ts +16 -18
  427. package/src/oauth/codex-account-store.ts +134 -5
  428. package/src/oauth/fallback-chain.ts +28 -6
  429. package/src/onboarding.ts +2 -1
  430. package/src/plugins/adapters/claude-package.ts +4 -12
  431. package/src/plugins/core/capability-policy.ts +141 -0
  432. package/src/plugins/core/host.ts +21 -8
  433. package/src/plugins/core/manifest.ts +19 -2
  434. package/src/plugins/core/trust-store.ts +15 -0
  435. package/src/plugins/core/types.ts +12 -0
  436. package/src/plugins/install/market-fetch.ts +68 -16
  437. package/src/plugins/install/plugin-install.ts +187 -16
  438. package/src/plugins/maker/plugin-maker.ts +25 -20
  439. package/src/plugins/maker/wizard-generate.ts +16 -2
  440. package/src/policy/llm-policy.ts +14 -1
  441. package/src/process/pid-liveness.ts +1 -1
  442. package/src/prompt-cache/openai.ts +10 -6
  443. package/src/prompt-cache/openrouter.ts +94 -0
  444. package/src/prompt-enhance/enhance.ts +46 -5
  445. package/src/prompt-library/family-prompt-coverage.ts +1 -0
  446. package/src/prompt-library/universal-preamble.ts +2 -2
  447. package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
  448. package/src/registry/loader.ts +6 -1
  449. package/src/release-loop/checklist.ts +24 -9
  450. package/src/release-loop/feature-store.ts +3 -0
  451. package/src/release-loop/gate-shards.ts +48 -4
  452. package/src/release-loop/landed-to-green.ts +81 -0
  453. package/src/release-loop/light-rc.ts +11 -1
  454. package/src/release-loop/merged-pr-checklist.ts +10 -0
  455. package/src/release-loop/placement.ts +91 -22
  456. package/src/release-loop/rehearsal.ts +317 -0
  457. package/src/release-loop/release-schedule.ts +13 -1
  458. package/src/release-loop/rubric-types.ts +1 -0
  459. package/src/release-loop/rubric.ts +2 -2
  460. package/src/retro/daily.ts +23 -9
  461. package/src/seat-loop/seat-loop.ts +34 -1
  462. package/src/seat-loop/traffic-signal.ts +44 -0
  463. package/src/self-dev/ask-launch-flow.ts +2 -2
  464. package/src/self-dev/dev-cli.ts +67 -3
  465. package/src/self-dev/dev-pipeline.ts +4 -6
  466. package/src/self-dev/draft-residue.ts +463 -0
  467. package/src/self-dev/draft-sweep.ts +151 -10
  468. package/src/self-dev/draft-triage-rules.ts +27 -1
  469. package/src/self-dev/launch-preflight-text.ts +549 -0
  470. package/src/self-dev/launch-preflight.ts +10 -545
  471. package/src/self-dev/orchestrate-cli.ts +57 -2
  472. package/src/self-dev/orchestrate-summary-line.ts +13 -0
  473. package/src/self-dev/orchestrate.ts +8 -135
  474. package/src/self-dev/review-provider-fallback.ts +115 -19
  475. package/src/self-dev/run-store.ts +1 -3
  476. package/src/self-dev/run-supervisor.ts +3 -22
  477. package/src/self-dev/run-types.ts +178 -0
  478. package/src/self-dev/shard-aggregate.ts +61 -13
  479. package/src/self-dev/shard-plan.ts +5 -1
  480. package/src/self-dev/shard-run.ts +23 -5
  481. package/src/self-dev/supervisor-verdict-edges.ts +2 -4
  482. package/src/self-implement/blocked-draft-policy.ts +3 -19
  483. package/src/self-implement/draft-on-stop-mode.ts +18 -0
  484. package/src/self-implement/follow-up-goals.ts +114 -38
  485. package/src/self-implement/gate-baseline-reap.ts +157 -0
  486. package/src/self-implement/gate-baseline.ts +78 -16
  487. package/src/self-implement/gate-cli.ts +12 -2
  488. package/src/self-implement/gate-remote.ts +17 -6
  489. package/src/self-implement/goal-author.ts +24 -4
  490. package/src/self-implement/goal-digest.ts +19 -0
  491. package/src/self-implement/graph-overlay-condition.ts +4 -0
  492. package/src/self-implement/headless-elanous-driver.ts +11 -9
  493. package/src/self-implement/host-regate.ts +120 -29
  494. package/src/self-implement/l8-merge-queue.ts +67 -5
  495. package/src/self-implement/merge-train.ts +131 -0
  496. package/src/self-implement/off-diff-evidence.ts +9 -0
  497. package/src/self-implement/orchestrator.ts +133 -16
  498. package/src/self-implement/review-finding-recurrence.ts +28 -0
  499. package/src/self-implement/rework-policy.ts +2 -1
  500. package/src/self-implement/round-summary-card.ts +63 -0
  501. package/src/self-implement/run-ledger.ts +4 -3
  502. package/src/self-implement/run-step-projection.ts +147 -0
  503. package/src/self-implement/run-stop.ts +1 -1
  504. package/src/self-implement/seams.ts +36 -3
  505. package/src/self-implement/self-implement-cli.ts +3 -1
  506. package/src/self-implement/train-gate-remote.ts +158 -0
  507. package/src/session-runtime/index.ts +10 -1
  508. package/src/session-runtime/retry-api.ts +11 -1
  509. package/src/skills/gift-install.ts +119 -0
  510. package/src/skills/runner.ts +59 -2
  511. package/src/skills/skill-feedback.ts +31 -1
  512. package/src/skills/skill-tier.ts +1 -0
  513. package/src/smart-wizard/capability-index.ts +231 -0
  514. package/src/smart-wizard/rung.ts +74 -0
  515. package/src/steward/launch.ts +20 -9
  516. package/src/steward/triage.ts +4 -2
  517. package/src/storage/file-lock.ts +8 -1
  518. package/src/task-agent/actions.ts +4 -0
  519. package/src/task-agent/card-evidence.ts +59 -13
  520. package/src/task-agent/judge.ts +4 -3
  521. package/src/task-agent/land-worktree.ts +201 -0
  522. package/src/task-agent/live-move-history.ts +133 -0
  523. package/src/task-agent/live-moves.ts +106 -19
  524. package/src/task-agent/parent-host.ts +117 -8
  525. package/src/task-agent/rejudge-on-head.ts +297 -0
  526. package/src/task-agent/review-result-capture.ts +218 -0
  527. package/src/task-agent/shadow.ts +103 -7
  528. package/src/task-agent/task-hand.ts +136 -11
  529. package/src/task-orchestrator/surfaces/pod-child-providers.ts +29 -0
  530. package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
  531. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +16 -2
  532. package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
  533. package/src/task-orchestrator/surfaces/pod-lease.ts +93 -12
  534. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +25 -2
  535. package/src/task-orchestrator/surfaces/pod-pool.ts +66 -14
  536. package/src/task-orchestrator/surfaces/self-implement-pod.ts +259 -36
  537. package/src/task-orchestrator/surfaces/self-implement.ts +6 -2
  538. package/src/telegram-service.ts +5 -1
  539. package/src/telegram.ts +17 -4
  540. package/src/time/format.ts +13 -4
  541. package/src/tool-runtime/goal-author-runtime.ts +1 -0
  542. package/src/tool-runtime/self-cognition-catalog.ts +23 -0
  543. package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
  544. package/src/user-config.ts +132 -24
  545. package/src/ux-sim/slash-menu-frames.ts +168 -0
  546. package/src/version/packed-revision.json +1 -1
  547. package/src/web-terminal/preview-tap-registry.ts +5 -2
  548. package/src/workflow-runtime/triggers/scheduler.ts +1 -0
  549. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
  550. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
  551. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
  552. package/apps/pwa/out/_next/static/chunks/1307-08b716476f1d50af.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
  554. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
  555. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
  556. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
  558. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
  563. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
  572. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
  573. package/apps/pwa/out/_next/static/chunks/2489-d238d3e2eb5b2bf5.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
  586. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
  594. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
  599. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
  601. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
  605. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
  610. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
  615. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
  616. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
  618. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
  619. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
  627. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
  630. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
  640. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
  643. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
  647. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
  648. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d85c43f46f58716b.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/board/page-4e645600579ae952.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/botlab/page-87edbfd19117823b.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/chat/page-cbaf66b1e1804adf.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/control/page-9f5610caaa06178c.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/decisions/page-ec8f3575e339de74.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/exec/page-2a19c5cd3b562580.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/field/page-b6a09cde2e07521a.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/morning/page-786fb4504d0f2fa9.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7ec8e289b4c14df6.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-8c8ac17d52be2a79.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d359ab048d63a9d9.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/settings/page-f151cc10ad026940.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/share/page-bf495a1e284f4e00.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/workflows/page-69cb93d1a2e57e0e.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72ec543928d67c3e.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
  704. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +0 -1
  705. /package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → rfkMm4movqkqMebBrCXiR}/_ssgManifest.js +0 -0
@@ -400,21 +400,50 @@ const GROK: TierMap = {
400
400
  // kimi·qwen·glm 의 «첫 실제 경로». 사다리는 이름 기억이 아니라 ***실물 `/api/v1/models` 사실***로 골랐다
401
401
  // (2026-09-23 실측 · 전 칸 `tools`·`reasoning` 지원 · 가격 $/MTok 입력/출력):
402
402
  // budget qwen3.8-flash 0.15/0.47 1M
403
- // balanced glm-5.3 0.84/2.64 1.31M ← 기본(OPENROUTER_MODEL)
403
+ // balanced glm-5.3 0.039/4.8 1.05M ← 기본(OPENROUTER_MODEL)
404
+ // 별도 모델 glm-5.3-flash 가격·창 미측정 (0으로 기록하지 않는다)
405
+ // 📏 OP 10-10 OpenRouter 실측: GLM-5.3 입력 $0.039/MTok · 출력 $4.8/MTok · 창 1.05M.
404
406
  // better qwen3.8-max-0902 2/6 1M
405
407
  // best kimi-k3 3/15 1.05M
406
- // ⇒ 가격순이면서 세 벤더를 «다» 덮는다. ⛔ 가격·창은 늙는다 — 카탈로그(`openrouter/*` 폴드)가 canonical.
408
+ // ⚠️ GLM 가격 갱신·flash 단가 미측정으로 가격순 주장은 보류한다. 가격·창은 늙는다 — 카탈로그(`openrouter/*` 폴드)가 canonical.
407
409
  // `shipping` = «생성된다»(배선 계약). 도구 호출·도구 루프·elanous 실제 프롬프트 3/3 실측(리서치 문서 §9).
408
410
  // ⚠️ `loaded` 의 reasoningLevel 은 wire 로 «안» 간다 — 측정상 OpenRouter `reasoning.effort` 를 보내면
409
411
  // kimi·glm 이 «덜» 생각한다(필드 없음이 최대). 그래서 옮기지 않았다 — 리서치 문서 §9.
410
412
  const OPENROUTER: TierMap = {
411
413
  budget: { model: 'openrouter/qwen/qwen3.8-flash', label: 'Qwen 3.8 Flash (OpenRouter)', rationale: 'Fast · cheap · 1M ctx', status: 'shipping' },
412
- balanced: { model: 'openrouter/z-ai/glm-5.3', label: 'GLM 5.3 (OpenRouter)', rationale: 'Default · 1.31M ctx', status: 'shipping' },
414
+ balanced: { model: 'openrouter/z-ai/glm-5.3', label: 'GLM 5.3 (OpenRouter)', rationale: 'Default · $0.039/$4.8 per MTok · 1.05M ctx', status: 'shipping' },
413
415
  better: { model: 'openrouter/qwen/qwen3.8-max-0902', label: 'Qwen 3.8 Max (OpenRouter)', rationale: 'Higher quality', status: 'shipping' },
414
416
  best: { model: 'openrouter/moonshotai/kimi-k3', label: 'Kimi K3 (OpenRouter)', rationale: 'Best · Kimi flagship', status: 'shipping' },
415
417
  loaded: { model: 'openrouter/moonshotai/kimi-k3', reasoningLevel: 'high', label: 'Kimi K3 · reasoning high (OpenRouter)', rationale: 'Loaded · deep multi-step', status: 'shipping' },
416
418
  };
417
419
 
420
+ // ── AWS Bedrock (BEDROCK-PROVIDER · 2026-10-11) ───────────────────
421
+ // ⭐ 판단: ANTHROPIC 사다리의 «추론 수준·역할 배치»를 그대로 두고, 모델은 Bedrock 메시지 엔드포인트에서
422
+ // 확인한 5.5 계열 id(`src/llm/bedrock.ts` BEDROCK_MODELS · 출처 platform.claude.com «Claude in Amazon Bedrock»
423
+ // Supported models)로만 채운다. 아래 칸(budget·balanced)은 ANTHROPIC 이 haiku-4-5·sonnet-5 를 쓰지만, 이 PR 이
424
+ // 카탈로그에 올린 Bedrock id 는 5.5 셋뿐이라 같은 등급의 5.5(haiku-5-5·sonnet-5-5)로 올렸다.
425
+ // ⛔ bedrock 은 명시 선택 전용 — 이 표는 `--role-llm <role>=bedrock/<tier>` 같은 «명시» 해석에만 쓰인다.
426
+ // ⚠️ status = `wip`: 실 AWS 호출(계정별 5.5 접근 조건)을 아직 안 쟀고, 셋업 키 감지 표(PROVIDER_ENV_SPEC)에도
427
+ // 없다(자격이 API 키가 아니라 AWS 체인이라). 첫 실호출 스모크 뒤 `shipping` 으로 올린다.
428
+ const BEDROCK: TierMap = {
429
+ budget: { model: 'anthropic.claude-haiku-5-5', reasoningLevel: 'off', label: 'Claude Haiku 5.5 (Bedrock)', rationale: 'Fast · cheap · bulk/classification', status: 'wip' },
430
+ balanced: { model: 'anthropic.claude-sonnet-5-5', reasoningLevel: 'off', label: 'Claude Sonnet 5.5 (Bedrock)', rationale: 'Default · everyday coding (BEDROCK_DEFAULT_MODEL)', status: 'wip' },
431
+ better: { model: 'anthropic.claude-opus-5-5', reasoningLevel: 'medium', label: 'Claude Opus 5.5 · thinking medium (Bedrock)', rationale: 'ANTHROPIC better 와 같은 배치(구현 = Opus 5.5)', status: 'wip' },
432
+ best: { model: 'anthropic.claude-opus-5-5', reasoningLevel: 'medium', label: 'Claude Opus 5.5 · thinking medium (Bedrock)', rationale: 'Best reasoning · architecture', status: 'wip' },
433
+ loaded: { model: 'anthropic.claude-opus-5-5', reasoningLevel: 'high', label: 'Claude Opus 5.5 · extended thinking (Bedrock)', rationale: 'Loaded · deep multi-step', status: 'wip' },
434
+ };
435
+
436
+ /** 가격·창 미측정이라 기존 tier 배치를 바꾸지 않고 명시 모델로만 고르는 칸.
437
+ * 소비 경로 = `lookupOpenRouterModelSpec()` ← `resolveRunFallback()`(openrouter 폴백 판정의 관측·ROUTE 결정). */
438
+ const OPENROUTER_EXPLICIT_MODELS: Readonly<Record<string, LlmTierSpec>> = {
439
+ 'z-ai/glm-5.3-flash': {
440
+ model: 'openrouter/z-ai/glm-5.3-flash',
441
+ label: 'GLM 5.3 Flash (OpenRouter)',
442
+ rationale: 'Fast · 가격/창 미측정',
443
+ status: 'shipping',
444
+ },
445
+ };
446
+
418
447
  // ── Combined lookup ─────────────────────────────────────────────────
419
448
 
420
449
  /** Subset of LLMProviderName that has a tier ladder defined here.
@@ -433,6 +462,7 @@ export const LLM_TIER_MAP_BY_PROVIDER: Readonly<Record<LlmTierProvider, TierMap>
433
462
  glm: GLM,
434
463
  grok: GROK,
435
464
  openrouter: OPENROUTER,
465
+ bedrock: BEDROCK,
436
466
  } as const;
437
467
 
438
468
  /** 사람이 쓰는 tier 별칭 → canonical ModelTier. "grok low tier" 같은 자연어 헷갈림을 SSOT 로 흡수한다.
@@ -454,9 +484,23 @@ export function parseTierArg(raw: string): ModelTier | undefined {
454
484
  * tsc 는 배열 누락을 못 잡는다(2026-09-23 `#19900` 이 `openrouter` 를 빠뜨려 배선 계약 자가 그것을
455
485
  * 한 번도 누르지 않았다). 자 = `provider-wiring-contract.test.ts`. */
456
486
  export const TIER_PROVIDERS: readonly LlmTierProvider[] = [
457
- 'anthropic', 'openai', 'openai-codex', 'gemini', 'grok', 'local', 'kimi', 'qwen', 'glm', 'openrouter',
487
+ 'anthropic', 'openai', 'openai-codex', 'gemini', 'grok', 'local', 'kimi', 'qwen', 'glm', 'openrouter', 'bedrock',
458
488
  ];
459
489
 
490
+ /** OpenRouter 모델 id(`openrouter/<vendor>/<model>` 또는 `<vendor>/<model>`)의 tier-map 칸.
491
+ * 명시 칸(`glm-5.3-flash` 등) → 5-tier 사다리 순으로 찾는다. 모르면 undefined(«미등록»을 기본값으로 접지 않는다). */
492
+ export function lookupOpenRouterModelSpec(model: string): LlmTierSpec | undefined {
493
+ const bare = model.trim().replace(/^openrouter\//, '');
494
+ if (!bare) return undefined;
495
+ const explicit = OPENROUTER_EXPLICIT_MODELS[bare];
496
+ if (explicit) return explicit;
497
+ const wanted = `openrouter/${bare}`;
498
+ for (const tier of ['budget', 'balanced', 'better', 'best'] as const) {
499
+ if (OPENROUTER[tier].model === wanted) return OPENROUTER[tier];
500
+ }
501
+ return undefined;
502
+ }
503
+
460
504
  /** Resolve the spec for (provider, tier).
461
505
  * `auto` and unknown providers have no concrete provider ladder, so both use
462
506
  * Anthropic's balanced default instead of crossing into the Codex family.
@@ -29,8 +29,12 @@ export type ModelFamily =
29
29
  | 'grok'
30
30
  | 'gemini'
31
31
  | 'local'
32
+ | 'open-weight'
32
33
  | 'other';
33
34
 
35
+ /** OpenRouter gateway prefix for OpenAI-served models (see OR-FAMILY-GPT6 in getModelFamily). */
36
+ const OPENROUTER_OPENAI_PREFIX = 'openrouter/openai/';
37
+
34
38
  /** Infer the family from a model id. Mirrors `inferProviderFromModel`
35
39
  * in llm.ts but returns a finer-grained tag (codex vs other gpt,
36
40
  * specifically, because codex-family has the worst observed tool-
@@ -45,6 +49,23 @@ export function getModelFamily(modelId: string | undefined): ModelFamily {
45
49
  if (m.includes('grok')) return 'grok';
46
50
  if (m.startsWith('gemini-')) return 'gemini';
47
51
  if (m.startsWith('local:')) return 'local';
52
+ // OR-FAMILY-GPT6 — `openrouter/openai/<gpt-* | o1-/o3-/o4-*>` is the same model the direct
53
+ // path serves; when its bare id is 'gpt' there (gpt-6-*, gpt-4*, o-series) it gets 'gpt' here
54
+ // too, so it carries the gpt prompt pieces (hygiene addon · preamble addendum · chat variant ·
55
+ // tool-hint cap). Without this it fell to 'other'. ⛔ A 'codex' verdict (bare gpt-5*) is NOT
56
+ // taken: in llm.ts 'codex' also switches tool-loop behaviour (inspect/repair stops, Read
57
+ // auto-narrowing, exploration exemptions, idle timeout) — not just prompt text — and that is a
58
+ // separate decision. Ids with `codex` in them were already 'codex' above. Gateway-prefixed ids
59
+ // only — bare `openai/...` ids are not touched (same LM Studio reason as below).
60
+ if (m.startsWith(OPENROUTER_OPENAI_PREFIX) && getModelFamily(m.slice(OPENROUTER_OPENAI_PREFIX.length)) === 'gpt') {
61
+ return 'gpt';
62
+ }
63
+ // OpenRouter gateway ids only (`openrouter/<vendor>/<model>`). Bare vendor ids such as
64
+ // `qwen/qwen3-coder-30b` stay 'other' on purpose: LM Studio publishes native ids in that
65
+ // same shape (see getModelTier's local pattern), and this family must not reach local runs.
66
+ if (m.startsWith('openrouter/') && (m.includes('glm') || m.includes('kimi') || m.includes('qwen'))) {
67
+ return 'open-weight';
68
+ }
48
69
  return 'other';
49
70
  }
50
71
 
@@ -68,7 +89,7 @@ export function getModelFamily(modelId: string | undefined): ModelFamily {
68
89
  // localhost).
69
90
 
70
91
  import type { LLMProviderName } from '../user-config.js';
71
- import type { SkillTier } from '../skills/runner.js';
92
+ import type { SkillTier } from '../skills/skill-tier.js';
72
93
  import { LLM_TIER_MAP_BY_PROVIDER, TIER_PROVIDERS } from '../model-tier/llm-tier-map.js';
73
94
 
74
95
  function matchesAny(s: string, needles: string[]): boolean {
@@ -0,0 +1,168 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+
6
+ import { elanousStateRoot } from '../../autopilot/state-paths.js';
7
+ import { debug } from '../../debug/log.js';
8
+ import { registerStandaloneLogSink } from '../../domains/standalone-log-sink.js';
9
+ import { LogStore, logsDbPath } from './log-store.js';
10
+
11
+ type HorizonStatus = 'present' | 'empty' | 'unreadable';
12
+ type Attribution = 'first-sample' | 'none' | 'attributed' | 'unattributed-loss';
13
+
14
+ export interface HorizonSample {
15
+ atMs: number;
16
+ oldestTsMs: number | null;
17
+ status: HorizonStatus;
18
+ spanHours: number | null;
19
+ advancedMs: number;
20
+ deletedEvents: number;
21
+ attribution: Attribution;
22
+ }
23
+
24
+ export type HorizonTick = HorizonSample & { outcome: 'ok' | 'fail' };
25
+
26
+ export interface HorizonWatchOptions { db?: string; state?: string }
27
+ export interface HorizonWatchDeps {
28
+ openStore?: (path: string) => Pick<LogStore, 'horizon' | 'queryAll' | 'close'>;
29
+ now?: () => number;
30
+ stateDir?: string;
31
+ }
32
+
33
+ function lastReadableSample(path: string): HorizonSample | null {
34
+ let content = '';
35
+ try { content = readFileSync(path, 'utf8'); }
36
+ catch (error) {
37
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
38
+ }
39
+ for (const line of content.trimEnd().split('\n').reverse()) {
40
+ if (!line) continue;
41
+ const sample = JSON.parse(line) as HorizonSample;
42
+ if (sample.status !== 'unreadable') return sample;
43
+ }
44
+ try { return JSON.parse(readFileSync(join(dirname(path), 'last-readable.json'), 'utf8')) as HorizonSample; }
45
+ catch (error) {
46
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ function saveReadableCheckpoint(path: string, sample: HorizonSample): void {
52
+ const checkpoint = join(dirname(path), 'last-readable.json');
53
+ mkdirSync(dirname(path), { recursive: true });
54
+ const temporary = `${checkpoint}.${process.pid}.${randomUUID()}.tmp`;
55
+ try {
56
+ writeFileSync(temporary, JSON.stringify(sample) + '\n');
57
+ renameSync(temporary, checkpoint);
58
+ } finally { rmSync(temporary, { force: true }); }
59
+ }
60
+
61
+ function saveSample(path: string, sample: HorizonSample): void {
62
+ let lines: string[];
63
+ try { lines = readFileSync(path, 'utf8').trimEnd().split('\n').filter(Boolean); }
64
+ catch (error) {
65
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
66
+ lines = [];
67
+ }
68
+ mkdirSync(dirname(path), { recursive: true });
69
+ const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
70
+ try {
71
+ writeFileSync(temporary, [...lines.slice(-499), JSON.stringify(sample)].join('\n') + '\n');
72
+ renameSync(temporary, path);
73
+ } finally { rmSync(temporary, { force: true }); }
74
+ if (sample.status !== 'unreadable') saveReadableCheckpoint(path, sample);
75
+ }
76
+
77
+ /** Read the store only; samples live outside logs.db and observations go through debug sinks. */
78
+ export function runHorizonWatchTick(opts: HorizonWatchOptions = {}, deps: HorizonWatchDeps = {}): HorizonTick {
79
+ const path = join(deps.stateDir ?? opts.state ?? join(elanousStateRoot(), 'log-horizon'), 'samples.jsonl');
80
+ // Serialize the entire read/compare/append transaction across cron and manual processes.
81
+ // This lock database is in the sample state directory, never in logs.db.
82
+ mkdirSync(dirname(path), { recursive: true });
83
+ const lock = new Database(join(dirname(path), 'samples.lock.db'));
84
+ try {
85
+ lock.run('PRAGMA busy_timeout = 120000');
86
+ lock.run('BEGIN IMMEDIATE');
87
+ try {
88
+ return recordSample();
89
+ } finally {
90
+ lock.run('ROLLBACK');
91
+ }
92
+ } finally {
93
+ lock.close();
94
+ }
95
+
96
+ function recordSample(): HorizonTick {
97
+ const now = deps.now ?? Date.now;
98
+ // The tick clock is read *after* the horizon read (see below), so every `deleted` event logged before the
99
+ // horizon was observed has ts <= atMs. Windows are half-open (prev.atMs, atMs]: each event is counted once.
100
+ let atMs: number | undefined;
101
+ let oldestTsMs: number | null = null;
102
+ let status: HorizonStatus = 'unreadable';
103
+ let deletedEvents = 0;
104
+ let store: ReturnType<NonNullable<HorizonWatchDeps['openStore']>> | undefined;
105
+ let previous: HorizonSample | null = null;
106
+ try {
107
+ store = (deps.openStore ?? LogStore.openReadOnly)(opts.db ?? logsDbPath());
108
+ const horizon = store.horizon();
109
+ atMs = now();
110
+ status = horizon.status;
111
+ oldestTsMs = horizon.status === 'present' ? horizon.oldestTsMs : null;
112
+ previous = lastReadableSample(path);
113
+ if (previous) {
114
+ deletedEvents = store.queryAll({
115
+ exactCategories: ['log-store.retention'], events: ['deleted'], sinceMs: previous.atMs + 1, untilMs: atMs,
116
+ }).length;
117
+ }
118
+ } catch (error) {
119
+ debug.log('log-store.horizon', 'read-failed', { reason: String(error) }, { level: 'warn' });
120
+ status = 'unreadable';
121
+ oldestTsMs = null;
122
+ } finally { store?.close(); }
123
+ atMs ??= now();
124
+
125
+ const advancedMs = status === 'present' && previous?.status === 'present' && previous.oldestTsMs !== null
126
+ ? Math.max(0, oldestTsMs! - previous.oldestTsMs) : 0;
127
+ const attribution: Attribution = status === 'unreadable' ? 'none'
128
+ : !previous ? 'first-sample'
129
+ : advancedMs === 0 ? 'none'
130
+ : deletedEvents > 0 ? 'attributed' : 'unattributed-loss';
131
+ const sample: HorizonSample = {
132
+ atMs, oldestTsMs, status,
133
+ spanHours: oldestTsMs === null ? null : (atMs - oldestTsMs) / 3_600_000,
134
+ advancedMs, deletedEvents, attribution,
135
+ };
136
+ try { saveSample(path, sample); }
137
+ catch (error) {
138
+ debug.log('log-store.horizon', 'sample-write-failed', { reason: String(error) }, { level: 'warn' });
139
+ return { ...sample, outcome: 'fail' };
140
+ }
141
+ debug.log('log-store.horizon', 'sample', {
142
+ oldestTs: oldestTsMs === null ? null : new Date(oldestTsMs).toISOString(),
143
+ spanHours: sample.spanHours, advancedMs, deletedEvents, attribution,
144
+ }, { level: attribution === 'unattributed-loss' ? 'warn' : 'info' });
145
+ return { ...sample, outcome: status === 'unreadable' ? 'fail' : 'ok' };
146
+ }
147
+ }
148
+
149
+ if (import.meta.main) {
150
+ const args = process.argv.slice(2);
151
+ const opts: HorizonWatchOptions = {};
152
+ let json = false;
153
+ for (let i = 0; i < args.length; i++) {
154
+ const arg = args[i];
155
+ if (arg === '--json') json = true;
156
+ else if (arg === '--db' && args[i + 1]) opts.db = args[++i];
157
+ else if (arg === '--state' && args[i + 1]) opts.state = args[++i];
158
+ else { process.stderr.write(`Unknown or incomplete option: ${arg}\n`); process.exit(1); }
159
+ }
160
+ const sinkReady = await registerStandaloneLogSink('log-horizon');
161
+ // A sink failure must not suppress the sample: the store read (and an 'unreadable' record) still happens,
162
+ // but the tick cannot claim success because its observation never reached logs.db.
163
+ const tick = runHorizonWatchTick(opts);
164
+ const result = sinkReady ? tick : { ...tick, outcome: 'fail' as const, reason: 'log-sink-registration-failed' };
165
+ if (json) process.stdout.write(`${JSON.stringify(result)}\n`);
166
+ else process.stdout.write(`${result.outcome}: ${result.attribution} (${result.status})${sinkReady ? '' : ' · log-sink-registration-failed'}\n`);
167
+ if (result.outcome === 'fail') process.exitCode = 1;
168
+ }
@@ -17,7 +17,8 @@
17
17
  // severity 는 SCHEME(이벤트 접미사) → `level` 컬럼으로 물질화.
18
18
 
19
19
  import { Database } from 'bun:sqlite';
20
- import { mkdirSync } from 'node:fs';
20
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmdirSync, rmSync, writeFileSync } from 'node:fs';
21
+ import { randomUUID } from 'node:crypto';
21
22
  import { elanousStateRoot } from '../../autopilot/state-paths.js';
22
23
  import { dirname, join } from 'node:path';
23
24
 
@@ -81,9 +82,13 @@ export interface LogStoreRetention {
81
82
  maxAgeDays: number;
82
83
  /** DB 상한(MB). 초과 시 오래된 행부터 삭제. 0 이하 = 크기 정리 안 함. 기본 500. */
83
84
  maxDbMb: number;
85
+ /** 🩸 LOGS-RETENTION-WINDOW-1(10-10): size-cap floor in hours — the size pass never deletes rows newer than
86
+ * this; when rows outside the floor are not enough it stops and logs `retention-floor-held`.
87
+ * 0 = legacy behaviour (oldest-first with no floor). Missing = 24. Age pass (`maxAgeDays`) is unaffected. */
88
+ minRetainHours?: number;
84
89
  }
85
90
 
86
- export const LOG_RETENTION_DEFAULTS: LogStoreRetention = { maxAgeDays: 7, maxDbMb: 500 };
91
+ export const LOG_RETENTION_DEFAULTS: LogStoreRetention = { maxAgeDays: 7, maxDbMb: 500, minRetainHours: 24 };
87
92
 
88
93
  /** 스토어 OOM 백스톱 — 정책 상한이 아니다. 정책은 각 경계(HTTP·툴)가 갖는다. */
89
94
  export const STORE_SAFETY_MAX = 100_000;
@@ -360,22 +365,65 @@ export class LogStore {
360
365
  }
361
366
  if (policy.maxDbMb > 0) {
362
367
  const pageCount = (this.db.query('PRAGMA page_count').get() as { page_count: number }).page_count;
368
+ const freePages = (this.db.query('PRAGMA freelist_count').get() as { freelist_count: number }).freelist_count;
363
369
  const pageSize = (this.db.query('PRAGMA page_size').get() as { page_size: number }).page_size;
364
- const sizeMb = (pageCount * pageSize) / (1024 * 1024);
370
+ // 🩸 OBS-RET-RATCHET(2026-10-09): 빈 페이지까지 세면 VACUUM 없는 파일은 안 줄어든다 → 뜰 때마다 오래된 행 ≈5% 를
371
+ // 또 지우는 래칫이 됐다(운영 logs.db 528MB · 01:08Z 이전 행 소실). 상한은 «쓰는 페이지»로 잰다.
372
+ const sizeMb = ((pageCount - freePages) * pageSize) / (1024 * 1024);
365
373
  if (sizeMb > policy.maxDbMb) {
366
374
  // 초과분 비율만큼 오래된 행 삭제(대략 — 정밀 계산보다 반복 수렴 선호).
367
375
  const total = (this.db.query('SELECT COUNT(*) AS c FROM logs').get() as { c: number }).c;
368
376
  const dropCount = Math.max(1, Math.floor(total * Math.min(0.5, 1 - policy.maxDbMb / sizeMb)));
369
- const r = this.db.run(
370
- 'DELETE FROM logs WHERE id IN (SELECT id FROM logs ORDER BY ts_ms ASC LIMIT ?)',
371
- [dropCount],
372
- );
373
- deletedBySize = Number(r.changes ?? 0);
377
+ // 🩸 LOGS-RETENTION-WINDOW-1(10-10): prod write volume made this pass keep only ≈2h of rows, so evidence
378
+ // for incidents a few hours old read as «0 rows». Rows inside the floor are never size-deleted.
379
+ const floorHours = policy.minRetainHours ?? LOG_RETENTION_DEFAULTS.minRetainHours ?? 0;
380
+ if (floorHours > 0) {
381
+ const floorCutoff = Date.now() - floorHours * 3_600_000;
382
+ // Byte-weighted split (payload text + fixed per-row overhead) — row counts alone misjudge mixed row sizes.
383
+ const split = this.db.query(
384
+ `SELECT COALESCE(SUM(CASE WHEN ts_ms < ? THEN w ELSE 0 END), 0) AS outsideBytes, COALESCE(SUM(w), 0) AS allBytes
385
+ FROM (SELECT ts_ms, 64 + LENGTH(ts) + LENGTH(category) + LENGTH(event) + COALESCE(LENGTH(data), 0) AS w FROM logs)`,
386
+ ).get(floorCutoff) as { outsideBytes: number; allBytes: number };
387
+ const r = this.db.run(
388
+ 'DELETE FROM logs WHERE id IN (SELECT id FROM logs WHERE ts_ms < ? ORDER BY ts_ms ASC LIMIT ?)',
389
+ [floorCutoff, dropCount],
390
+ );
391
+ deletedBySize = Number(r.changes ?? 0);
392
+ // dropCount is capped at 50% per run; judge the floor against the full overshoot (by bytes) so a cap the
393
+ // floor can never reach is reported even when this run's capped batch was fully served from outside rows.
394
+ const neededShare = 1 - policy.maxDbMb / sizeMb;
395
+ const outsideShare = split.allBytes > 0 ? split.outsideBytes / split.allBytes : 0;
396
+ if (outsideShare < neededShare) {
397
+ debug.log('mss.logging', 'retention-floor-held', {
398
+ totalMb: Math.round(sizeMb * 10) / 10, maxTotalMb: policy.maxDbMb, floorHours,
399
+ neededShare: Math.round(neededShare * 1000) / 1000, outsideShare: Math.round(outsideShare * 1000) / 1000,
400
+ deletedRows: deletedBySize,
401
+ });
402
+ }
403
+ } else {
404
+ const r = this.db.run(
405
+ 'DELETE FROM logs WHERE id IN (SELECT id FROM logs ORDER BY ts_ms ASC LIMIT ?)',
406
+ [dropCount],
407
+ );
408
+ deletedBySize = Number(r.changes ?? 0);
409
+ }
374
410
  }
375
411
  }
412
+ if (deletedByAge > 0 || deletedBySize > 0) {
413
+ const oldest = (this.db.query('SELECT MIN(ts_ms) AS t FROM logs').get() as { t: number | null }).t;
414
+ debug.log('log-store.retention', 'deleted', { deletedByAge, deletedBySize, oldestTs: oldest === null ? null : new Date(oldest).toISOString() });
415
+ }
376
416
  return { deletedByAge, deletedBySize };
377
417
  }
378
418
 
419
+ /** Reclaim a bounded number of free pages after retention; legacy databases with auto_vacuum=NONE remain unchanged. */
420
+ reclaimFreePages(): void {
421
+ const mode = (this.db.query('PRAGMA auto_vacuum').get() as { auto_vacuum: number }).auto_vacuum;
422
+ if (mode !== 2) return;
423
+ const free = (this.db.query('PRAGMA freelist_count').get() as { freelist_count: number }).freelist_count;
424
+ if (free > 0) this.db.run(`PRAGMA incremental_vacuum(${Math.min(free, 128)})`);
425
+ }
426
+
379
427
  /**
380
428
  * ⛔⭐⭐⭐ **이 스토어에 «실제로 뜬» 카테고리와 그 수**(2026-08-19 · `OBS-T122`).
381
429
  *
@@ -658,6 +706,12 @@ export class LogStore {
658
706
  return (this.db.query('SELECT COUNT(*) AS c FROM logs').get() as { c: number }).c;
659
707
  }
660
708
 
709
+ /** Earliest retained event timestamp, regardless of insertion order. No schema or row writes. */
710
+ horizon(): { status: 'present'; oldestTsMs: number } | { status: 'empty' } {
711
+ const { oldestTsMs } = this.db.query('SELECT MIN(ts_ms) AS oldestTsMs FROM logs').get() as { oldestTsMs: number | null };
712
+ return oldestTsMs === null ? { status: 'empty' } : { status: 'present', oldestTsMs };
713
+ }
714
+
661
715
  close(): void {
662
716
  try { this.db.close(); } catch { /* noop */ }
663
717
  }
@@ -850,28 +904,163 @@ export class StoreSink implements LogSink {
850
904
  }
851
905
  }
852
906
 
907
+ const RETENTION_INTERVAL_MS = 6 * 3600 * 1000;
908
+ type RetentionLockOwner = { token: string; pid: number; start: string | null };
909
+
910
+ /** PID reuse must not let a new process inherit a dead process's ownership. */
911
+ function processStart(pid: number): string | null {
912
+ try {
913
+ const stat = readFileSync(`/proc/${pid}/stat`, 'utf8');
914
+ return stat.slice(stat.lastIndexOf(')') + 2).split(' ')[19] ?? null;
915
+ } catch { return null; }
916
+ }
917
+
918
+ function ownerDead(owner: RetentionLockOwner): boolean {
919
+ if (!Number.isSafeInteger(owner.pid) || owner.pid <= 0) return false;
920
+ try {
921
+ process.kill(owner.pid, 0);
922
+ const currentStart = processStart(owner.pid);
923
+ if (owner.start !== null && currentStart !== null && owner.start !== currentStart) return true;
924
+ // A zombie still answers kill(pid, 0), but can no longer run retention.
925
+ try {
926
+ const stat = readFileSync(`/proc/${owner.pid}/stat`, 'utf8');
927
+ return stat.slice(stat.lastIndexOf(')') + 2).startsWith('Z ');
928
+ } catch { return false; }
929
+ } catch (error) {
930
+ return (error as NodeJS.ErrnoException).code === 'ESRCH';
931
+ }
932
+ }
933
+
934
+ /** Only the guard row can authorize removal. A pre-publication temp file belongs to its row's token. */
935
+ function removeRetentionLockDirectory(lockPath: string, token: string, interrupted: boolean): void {
936
+ if (!existsSync(lockPath)) return;
937
+ const marker = join(lockPath, 'owner');
938
+ const temporaryName = `owner.${token}.tmp`;
939
+ if (readdirSync(lockPath).some((name) => name !== 'owner' && name !== temporaryName)) {
940
+ throw new Error('retention lock has unknown entries');
941
+ }
942
+ if (existsSync(marker)) {
943
+ const published = readFileSync(marker, 'utf8');
944
+ if (published !== token && !(interrupted && token.startsWith(published))) {
945
+ throw new Error('retention lock owner changed');
946
+ }
947
+ rmSync(marker);
948
+ }
949
+ rmSync(join(lockPath, temporaryName), { force: true });
950
+ rmdirSync(lockPath);
951
+ }
952
+
953
+ /** The SQLite guard serializes stale-lock recovery and ownership changes, including process death mid-acquisition. */
954
+ function acquireRetentionLock(lockPath: string): (() => void) | null {
955
+ const guard = new Database(`${lockPath}.db`);
956
+ const token = randomUUID();
957
+ let handedOff = false;
958
+ try {
959
+ guard.run('PRAGMA busy_timeout = 100');
960
+ guard.run('CREATE TABLE IF NOT EXISTS owner (slot INTEGER PRIMARY KEY CHECK(slot = 1), token TEXT NOT NULL, pid INTEGER NOT NULL, start TEXT)');
961
+ let acquired = false;
962
+ guard.run('BEGIN IMMEDIATE');
963
+ try {
964
+ const previous = guard.query('SELECT token, pid, start FROM owner WHERE slot = 1').get() as RetentionLockOwner | null;
965
+ if (previous && !ownerDead(previous)) return null;
966
+ if (existsSync(lockPath)) {
967
+ // A pre-guard lock has no provable owner: never remove it.
968
+ if (!previous) return null;
969
+ try { removeRetentionLockDirectory(lockPath, previous.token, true); }
970
+ catch { return null; }
971
+ }
972
+ guard.run('INSERT OR REPLACE INTO owner (slot, token, pid, start) VALUES (1, ?, ?, ?)', [token, process.pid, processStart(process.pid)]);
973
+ guard.run('COMMIT');
974
+ acquired = true;
975
+ } finally {
976
+ if (!acquired) guard.run('ROLLBACK');
977
+ }
978
+ // The ownership row is committed before mkdir: a crash in this gap is recoverable too.
979
+ try {
980
+ mkdirSync(lockPath);
981
+ const temporary = join(lockPath, `owner.${token}.tmp`);
982
+ writeFileSync(temporary, token, { flag: 'wx' });
983
+ renameSync(temporary, join(lockPath, 'owner'));
984
+ } catch (error) {
985
+ guard.run('BEGIN IMMEDIATE');
986
+ try {
987
+ // Keep the ownership row if cleanup fails: the next process can then
988
+ // prove who left the directory behind and recover it after this PID dies.
989
+ removeRetentionLockDirectory(lockPath, token, true);
990
+ guard.run('DELETE FROM owner WHERE slot = 1 AND token = ?', [token]);
991
+ guard.run('COMMIT');
992
+ } catch { guard.run('ROLLBACK'); }
993
+ throw error;
994
+ }
995
+ handedOff = true;
996
+ return () => {
997
+ try {
998
+ guard.run('BEGIN IMMEDIATE');
999
+ try {
1000
+ const current = guard.query('SELECT token FROM owner WHERE slot = 1').get() as { token: string } | null;
1001
+ if (current?.token === token) {
1002
+ removeRetentionLockDirectory(lockPath, token, false);
1003
+ guard.run('DELETE FROM owner WHERE slot = 1 AND token = ?', [token]);
1004
+ }
1005
+ guard.run('COMMIT');
1006
+ } catch (error) { guard.run('ROLLBACK'); throw error; }
1007
+ } finally { guard.close(); }
1008
+ };
1009
+ } finally { if (!handedOff) guard.close(); }
1010
+ }
1011
+
1012
+ /** A shared stamp prevents short-lived non-owner processes from repeatedly pruning the same DB. */
1013
+ function runStampedRetention(store: LogStore, retention: LogStoreRetention, owner: boolean): void {
1014
+ const stampPath = `${store.path}.retention.stamp`;
1015
+ const lockPath = `${store.path}.retention.lock`;
1016
+ const isFresh = (): boolean => {
1017
+ try {
1018
+ const age = Date.now() - Number(readFileSync(stampPath, 'utf8'));
1019
+ return age >= 0 && age < RETENTION_INTERVAL_MS;
1020
+ } catch { return false; }
1021
+ };
1022
+ if (!owner && isFresh()) return;
1023
+ const release = acquireRetentionLock(lockPath);
1024
+ if (!release) return;
1025
+ try {
1026
+ if (!owner && isFresh()) return;
1027
+ store.enforceRetention(retention);
1028
+ try { store.reclaimFreePages(); }
1029
+ catch (error) { debug.log('log-store.retention', 'reclaim-failed', { reason: error instanceof Error ? error.message : String(error) }); }
1030
+ const temporary = `${stampPath}.${process.pid}.${randomUUID()}.tmp`;
1031
+ try {
1032
+ writeFileSync(temporary, String(Date.now()));
1033
+ renameSync(temporary, stampPath);
1034
+ } finally { rmSync(temporary, { force: true }); }
1035
+ } finally { release(); }
1036
+ }
1037
+
853
1038
  /** 부팅 1줄 등록 헬퍼 — 데몬/TUI/러너가 자기 surface 를 선언하고 스토어
854
1039
  * 싱크를 단다. retention 은 호출측이 config 에서 읽어 전달(user-config
855
- * 순환 의존 회피). 반환 = unregister(테스트용) 또는 null(store 불가). */
1040
+ * 순환 의존 회피). 반환 = unregister(테스트용) 또는 null(store 불가).
1041
+ * nexus owns the periodic job unless the caller explicitly opts in. */
856
1042
  export function registerLogStoreSink(
857
1043
  registerSink: (sink: LogSink) => () => void,
858
1044
  surface: string,
859
1045
  retention: LogStoreRetention = LOG_RETENTION_DEFAULTS,
1046
+ opts: { retentionOwner?: boolean } = {},
860
1047
  ): (() => void) | null {
861
1048
  const store = getDefaultLogStore();
862
1049
  if (!store) return null;
863
1050
  const sink = new StoreSink(store, surface, { installExitHandlers: true });
864
1051
  const off = registerSink(sink);
865
1052
  try { nestDepth.observeNestAtBoot(); } catch { /* fail-soft — 관측 실패가 싱크 등록을 막지 않는다. */ }
866
- // 보존정책 — 부팅 직후 1회(디퍼) + 6시간 주기. 실패는 조용히.
1053
+ const owner = opts.retentionOwner ?? surface === 'nexus';
867
1054
  const runRetention = (): void => {
868
- try { store.enforceRetention(retention); } catch { /* fail-soft */ }
1055
+ try { runStampedRetention(store, retention, owner); }
1056
+ catch (error) { debug.log('log-store.retention', 'failed', { reason: error instanceof Error ? error.message : String(error) }); }
869
1057
  };
870
- setImmediate(runRetention);
871
- const h = setInterval(runRetention, 6 * 3600 * 1000);
872
- (h as unknown as { unref?: () => void }).unref?.();
1058
+ const startup = setImmediate(runRetention);
1059
+ const h = owner ? setInterval(runRetention, RETENTION_INTERVAL_MS) : null;
1060
+ (h as unknown as { unref?: () => void } | null)?.unref?.();
873
1061
  return () => {
874
- clearInterval(h);
1062
+ clearImmediate(startup);
1063
+ if (h) clearInterval(h);
875
1064
  sink.close();
876
1065
  off();
877
1066
  };
@@ -9,7 +9,7 @@ import type { VerifierSpec } from './verifier/types.js';
9
9
  import type { GuardianSpec } from './guardian/types.js';
10
10
  import { ptyAvailable } from './pty-shell/registry.js';
11
11
  import { krFlowAvailable } from './skills/tools/kr-flow.js';
12
- import { SELF_COGNITION_MCP_CATALOG_ENTRIES } from './tool-runtime/self-cognition-runtimes.js';
12
+ import { SELF_COGNITION_MCP_CATALOG_ENTRIES } from './tool-runtime/self-cognition-catalog.js';
13
13
  import { SKILLS_LIST_DESCRIPTION } from './onboarding/entry-hints.js';
14
14
 
15
15
  export type { NativeToolHost } from './tool-surface.js';
@@ -2,7 +2,7 @@ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, re
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { debug } from '../../debug/log.js';
5
- import { routeOutbound } from '../outbound/router.js';
5
+ import { sendTelegramReport } from '../../telegram-report.js';
6
6
  import { getUserConfig } from '../../user-config.js';
7
7
  import { effectiveInstanceRoot } from '../../instance/resolve.js';
8
8
  import { jsonResponse } from './json-response.js';
@@ -23,7 +23,7 @@ interface ConsultRequest {
23
23
  export interface ConsultRequestsDeps {
24
24
  root?: () => string;
25
25
  now?: () => string;
26
- /** Owner alert. Default = the daemon's own in-process router (the same one `/v1/outbound` uses).
26
+ /** Owner alert. Default = the daemon's in-process Telegram sender (operations role only).
27
27
  * ⛔ Never `sendOutbound` here: it is a synchronous curl to this very daemon's `/v1/outbound`, so inside the
28
28
  * daemon it blocks the event loop until it times out (~25 s · `unreachable`) and the alert is lost (CS1 · 10-01). */
29
29
  send?: (text: string, kind: string) => unknown;
@@ -88,9 +88,9 @@ export async function handleConsultRequests(req: Request, deps: ConsultRequestsD
88
88
  if (!body || typeof body !== 'object' || Array.isArray(body)) return rejected('body');
89
89
  const input = body as Record<string, unknown>;
90
90
  for (const field of ['name', 'contact'] as const) {
91
- if (typeof input[field] !== 'string' || !input[field].trim()) return rejected(field);
91
+ if (typeof input[field] !== 'string' || !input[field].trim() || input[field].length > (field === 'name' ? 256 : 512)) return rejected(field);
92
92
  }
93
- if (input.org !== undefined && (typeof input.org !== 'string' || !input.org.trim())) return rejected('org');
93
+ if (input.org !== undefined && (typeof input.org !== 'string' || !input.org.trim() || input.org.length > 256)) return rejected('org');
94
94
  if (input.kind !== 'company' && input.kind !== 'personal') return rejected('kind');
95
95
  if (input.interest !== 'A' && input.interest !== 'B') return rejected('interest');
96
96
  if (input.consent !== true) return rejected('consent');
@@ -114,15 +114,12 @@ export async function handleConsultRequests(req: Request, deps: ConsultRequestsD
114
114
  const alert = `📮 상담 문의 ${entry.receiptId} · ${entry.name}(${entry.kind === 'personal' ? '개인' : (entry.org ?? '회사')}) · 관심 ${entry.interest}\n앱에서 보기`;
115
115
  // Fire and forget — the receipt is already on disk; the answer must not wait for the channel.
116
116
  void Promise.resolve()
117
- .then(() => (deps.send ?? notifyInProcess)(alert, 'alert'))
117
+ .then(() => (deps.send ?? notifyInProcess)(alert, 'ops-alert'))
118
118
  .catch((error: unknown) => { debug.log('consult.request', 'notify-failed', { receiptId: entry.receiptId, reason: error instanceof Error ? error.message : String(error) }); });
119
119
  return jsonResponse({ receiptId: entry.receiptId, receivedAt: entry.receivedAt }, 202);
120
120
  }
121
121
 
122
122
  async function notifyInProcess(text: string, kind: string): Promise<void> {
123
- const result = await routeOutbound(getUserConfig(), { text, markdown: false, kind });
124
- debug.log('consult.request', result.delivered ? 'notified' : 'notify-failed', {
125
- delivered: result.delivered,
126
- channels: result.channels.map((channel) => `${channel.type}:${channel.ok ? 'ok' : 'fail'}`),
127
- });
123
+ const delivered = await sendTelegramReport(getUserConfig(), text, { markdown: false, kind });
124
+ debug.log('consult.request', delivered ? 'notified' : 'notify-failed', { delivered, channels: [`telegram:${delivered ? 'ok' : 'fail'}`] });
128
125
  }