elanous 0.2.22 → 0.2.24

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 (803) 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/{aBi6EyMtLsrl7J8eH4kF7 → Q-7iofoxOEv1WX18S35of}/_buildManifest.js +1 -1
  6. package/apps/pwa/out/_next/static/chunks/{119fc678.6f591cdb84ca5791.js → 0673cc54.8e2ef1c23e49e190.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/{a9f2b54d-40c16bd0efdb04b8.js → 0a09e062-34f747a17d8d8c2e.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/{f8626309.a4e5d362b1d55e59.js → 0e3acd34.d42b2ef87fdf4234.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +77 -0
  10. package/apps/pwa/out/_next/static/chunks/{7175-350f4ce2873bc038.js → 1325-dc3f3c21eb181cb4.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +34 -0
  12. package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +24 -0
  13. package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/{6767.8e6c5f6e744fcd9f.js → 1505.5826671109111e75.js} +2 -2
  15. package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 1669-da843ed73aa301f5.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +36 -0
  24. package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +122 -0
  25. package/apps/pwa/out/_next/static/chunks/2528.0af5c0e1fad88df6.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +62 -0
  28. package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/272-0eed0fb0aab716b6.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/{7899.597af4a9996c907f.js → 2721.bc9a8f36bd85c07a.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +149 -0
  36. package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3961-aaae04ed1d32622f.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +309 -0
  40. package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{d70879db.c66c56a95a91efc6.js → 4c5e95ce.83e9b16add6fcd8c.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/{6158.566c890adc06a459.js → 5313.1697309c532b9856.js} +2 -2
  53. package/apps/pwa/out/_next/static/chunks/{79b78388.663188133449d707.js → 53746282.1be36038d1abd949.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5578.ac963634373b28c6.js +286 -0
  57. package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5967-c0a80f8ffe5d4050.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 5974.360f8441eb0ac1ec.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +74 -0
  65. package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6571-926ddf024a6ad5df.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +64 -0
  70. package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/{9832.2ab2e818013574c7.js → 6702.ebbd165d5e4a4a14.js} +1 -1
  72. package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +32 -0
  74. package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +24 -0
  75. package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +93 -0
  79. package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 740.90b31555290236a9.js} +2 -2
  84. package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +148 -0
  85. package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +226 -0
  86. package/apps/pwa/out/_next/static/chunks/{9070.067c106528ec1ae6.js → 7518.49161e8f5814abdb.js} +1 -1
  87. package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +27 -0
  88. package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +115 -0
  91. package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7905.b230b9d06cb4ad5d.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/{8194.484f51e273d9abd2.js → 8264.00e434fbd02e9311.js} +16 -16
  98. package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +8 -0
  100. package/apps/pwa/out/_next/static/chunks/{088d8e8a.8069543d082bec12.js → 83cfc5c8.349535543c1548b6.js} +1 -1
  101. package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +23 -0
  102. package/apps/pwa/out/_next/static/chunks/{1948.5f35b39eef222a94.js → 8438.ee7f0df3b3f6fcb4.js} +1 -1
  103. package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{4588.ad3364cb7fb97ada.js → 8532.a86e99acf9b761cb.js} +2 -2
  106. package/apps/pwa/out/_next/static/chunks/8569.b853a8daa8f31d12.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/8925-2abfb48af477fb7f.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{88729267.9c6c000157c83f2f.js → 89849432.9eaa2f4c9c010965.js} +3 -3
  111. package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +90 -0
  112. package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +94 -0
  113. package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +41 -0
  115. package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +287 -0
  117. package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +79 -0
  118. package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/{3522-1de1319d028ac89c.js → 9502-32c5dc22e8f120f5.js} +3 -3
  121. package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +104 -0
  124. package/apps/pwa/out/_next/static/chunks/{7630.2b9a68659053ff4e.js → 9976.8f28864317639fe6.js} +16 -16
  125. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/approvals/page-3b9fc39362473ae7.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/board/page-d7602d489edf5f37.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/botlab/page-24282bf9f90031a5.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/ceo/page-4eb84d454f8c3415.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/chat/page-b2e494bdad57fcb5.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/consult/{page-80ed511e02c81412.js → page-c0ab229673a8b0eb.js} +1 -1
  134. package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/editor/page-bf88c0f558d8ef54.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/exec/page-52fec523e5b5d3ad.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/inside/page-22725e2570f1126b.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/intake/page-1891335950d68b70.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live/page-c314b75c11234998.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/live-run/page-9f9c85415f61782b.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8526c17ae38e6a50.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/morning/page-f998637a8402115c.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6f23159fedb41f67.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1d809a87139dd024.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-da9e570234d481fd.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b92caddb5c715f30.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/reflection/page-57ffeeb6019440fd.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2c2b3f29cec1efa4.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-9369279f72fc7b51.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/page-901a1391b81053bc.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/showroom/page-b5c0c996f329371e.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/tasks/page-89881ccbcf728978.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/today/page-58d4e5cb30ef4871.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-acf868c64d77f950.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workspace/page-bc79f0833533fa84.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d5c4398eea03d8f3.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/b4e12462-5fab4a813a63dadd.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → ce383d63.d2ede338788ed18e.js} +1 -1
  179. package/apps/pwa/out/_next/static/chunks/{framework-5c169757551134ed.js → framework-b2763b10dc6b3836.js} +1 -1
  180. package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +1 -0
  185. package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
  186. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
  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/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
  288. package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
  289. package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
  290. package/catalog/providers/openrouter.yaml +2 -0
  291. package/graphs/implement-loop.yaml +31 -0
  292. package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
  293. package/graphs/log-horizon/recipes.yaml +3 -0
  294. package/graphs/ops/harness-gc.yaml +17 -0
  295. package/graphs/ops/recipes.yaml +3 -0
  296. package/graphs/overlays/README.md +1 -1
  297. package/graphs/overlays/stop-heal-ladder.yaml +105 -0
  298. package/graphs/release/recipes.yaml +1 -1
  299. package/graphs/release/release-loop.yaml +6 -2
  300. package/graphs/release-rehearsal/recipes.yaml +12 -0
  301. package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
  302. package/package.json +4 -1
  303. package/scripts/add-api-key.sh +1 -1
  304. package/scripts/bench-report.ts +82 -3
  305. package/scripts/ci-typecheck-changed.ts +80 -14
  306. package/scripts/fleet/bedrock-pod-secret.ts +122 -0
  307. package/scripts/fleet/node-setup-linux.sh +97 -0
  308. package/scripts/fleet/node-setup.sh +11 -0
  309. package/scripts/fleet/zai-pod-secret.ts +86 -0
  310. package/scripts/hq/hq-fence-wrapper.sh +148 -0
  311. package/scripts/land-global-checks.ts +3 -0
  312. package/scripts/lib/typecheck-ratchet.ts +51 -0
  313. package/scripts/measure-log-diet-2.ts +213 -0
  314. package/scripts/measure-tsc-affected-roots.ts +289 -0
  315. package/scripts/openai-relay-server.ts +5 -1
  316. package/scripts/release-loop/docs-node.ts +258 -43
  317. package/scripts/release-loop/gate-executor.ts +28 -0
  318. package/scripts/release-loop/gate-method.ts +59 -0
  319. package/scripts/release-loop/gate-node.ts +335 -33
  320. package/scripts/release-loop/gate-wall.ts +208 -0
  321. package/scripts/release-loop/known-issues-node.ts +53 -8
  322. package/scripts/release-loop/publish-at.ts +14 -0
  323. package/scripts/release-loop/publish-node.ts +17 -2
  324. package/scripts/release-loop/resume-release.ts +40 -4
  325. package/scripts/release-loop/resume.ts +94 -0
  326. package/scripts/release-loop/shard-plan.ts +3 -11
  327. package/scripts/release-loop/tui-node-checks.ts +22 -0
  328. package/scripts/release-loop/unattended-release.ts +29 -7
  329. package/scripts/rhythm/daily.ts +51 -2
  330. package/scripts/rhythm/weekly.ts +3 -2
  331. package/scripts/tsc-baseline-cache.ts +147 -0
  332. package/scripts/tsc-main-safety-net.ts +189 -0
  333. package/scripts/usage-rollup.ts +64 -8
  334. package/src/acp/codex-plugins.ts +48 -6
  335. package/src/agent/monad-agent-turn.ts +67 -5
  336. package/src/agent/run-context.ts +20 -0
  337. package/src/agent/tool-profile.ts +28 -2
  338. package/src/agent-mission/acp-judge.ts +8 -2
  339. package/src/agent-mission/capabilities-cli.ts +16 -1
  340. package/src/agent-mission/driver.ts +19 -4
  341. package/src/agent-mission/pr-review-watch.ts +6 -2
  342. package/src/agent-mission/resource-ladder.ts +8 -3
  343. package/src/agent-mission/review-loop.ts +41 -7
  344. package/src/agent-substrate/acp-implementer.ts +139 -0
  345. package/src/agent-substrate/acp-reviewer.ts +17 -8
  346. package/src/agent-substrate/pr-reviewer.ts +191 -28
  347. package/src/agent-substrate/review-intent.ts +6 -4
  348. package/src/agent-substrate/self-review-cli.ts +155 -21
  349. package/src/agent-substrate/shard-boundary-intent.ts +92 -26
  350. package/src/ask-user-question/pending-questions.ts +8 -2
  351. package/src/autopilot/build/llm-conflict-merge.ts +216 -12
  352. package/src/autopilot/codebase-grounding.ts +44 -0
  353. package/src/autopilot/llm-control-brain.ts +13 -1
  354. package/src/autopilot/mission-codebase-gate.ts +8 -77
  355. package/src/autopilot/mission-critique.ts +18 -5
  356. package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
  357. package/src/autopilot/mission-trace.ts +12 -1
  358. package/src/autopilot/pr-manager.ts +12 -3
  359. package/src/autopilot/system-lookback-run.ts +16 -10
  360. package/src/bench/bench.ts +183 -0
  361. package/src/boot/acp-server.ts +4 -7
  362. package/src/boot/acp-socket-path.ts +7 -0
  363. package/src/briefing/brief-items.ts +21 -13
  364. package/src/budget/fetchers/grok.ts +15 -5
  365. package/src/budget/llm-cost.ts +37 -5
  366. package/src/budget/types.ts +8 -2
  367. package/src/budget/unified-usage.ts +88 -6
  368. package/src/chat/index.ts +12 -3
  369. package/src/cli/agent-cli.ts +20 -0
  370. package/src/cli/bench-cli.ts +34 -0
  371. package/src/cli/brief-cli.ts +27 -21
  372. package/src/cli/cli-user-error.ts +9 -2
  373. package/src/cli/guardian-cli.ts +26 -1
  374. package/src/cli/image-cli.ts +101 -0
  375. package/src/cli/log-event-names.ts +7 -0
  376. package/src/cli/logs-cli.ts +1 -0
  377. package/src/cli/mcp-login.ts +111 -37
  378. package/src/cli/plugin-cli.ts +21 -1
  379. package/src/cli/pod-cli.ts +26 -0
  380. package/src/cli/pr-cli.ts +90 -5
  381. package/src/cli/pr-land-overlap-evidence.ts +279 -0
  382. package/src/cli/release-cli.ts +63 -12
  383. package/src/cli/rfc-status.ts +52 -2
  384. package/src/cli/self-update.ts +277 -10
  385. package/src/cli/skills-cli.ts +12 -3
  386. package/src/cli/tasks-cli.ts +241 -30
  387. package/src/cli/usage-cli.ts +91 -8
  388. package/src/context-bus/context-first.ts +51 -2
  389. package/src/context-bus/context-now-surfaces.ts +13 -2
  390. package/src/context-bus/long-term-memory.ts +3 -1
  391. package/src/context-bus/nightly-condense.ts +3 -2
  392. package/src/core-turn/run-goal-loop.ts +87 -2
  393. package/src/dashboard/ad-run-setup-bindings.ts +279 -0
  394. package/src/dashboard/chat-fold.ts +20 -0
  395. package/src/dashboard/first-screen-band.ts +151 -1
  396. package/src/dashboard/index.ts +45 -281
  397. package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -3
  398. package/src/dashboard/stale-install-check.ts +24 -2
  399. package/src/dashboard/turn-stream-formatter.ts +25 -0
  400. package/src/debug/log-diet.ts +190 -0
  401. package/src/debug/log.ts +70 -17
  402. package/src/display/coordinator.ts +25 -2
  403. package/src/domains/coo-admin-tool.ts +27 -0
  404. package/src/domains/cron-match.ts +1 -0
  405. package/src/domains/outbound-alert.ts +17 -6
  406. package/src/domains/release-tool.ts +158 -10
  407. package/src/domains/schedule-registry.ts +10 -7
  408. package/src/domains/schedule-runner.ts +1 -0
  409. package/src/elanous-config-dir.ts +2 -2
  410. package/src/elanous-daemon.ts +3 -3
  411. package/src/error-report/report.ts +21 -4
  412. package/src/execution-loop/launch-gate.ts +1 -1
  413. package/src/expression/i18n/messages.en.ts +5 -0
  414. package/src/expression/i18n/messages.ja.ts +5 -0
  415. package/src/expression/i18n/messages.ko.ts +5 -0
  416. package/src/expression/i18n/messages.zh.ts +5 -0
  417. package/src/expression/i18n/types.ts +8 -0
  418. package/src/github/pr-labels.ts +2 -0
  419. package/src/graph-runner/graph-cli.ts +42 -1
  420. package/src/graph-runner/runner.ts +218 -8
  421. package/src/grok/usage.ts +23 -2
  422. package/src/grounding/ref-index.ts +253 -0
  423. package/src/harness/dispatch-backpressure.ts +267 -0
  424. package/src/harness/harness-clean.ts +199 -78
  425. package/src/harness/harness-cli-command.ts +229 -35
  426. package/src/harness/harness-gc.ts +331 -0
  427. package/src/harness/harness-seams.ts +2 -20
  428. package/src/harness/harness-stop.ts +4 -2
  429. package/src/harness/harness-substrate-default.ts +214 -8
  430. package/src/harness/launch-registry.ts +6 -0
  431. package/src/harness/nested-elanous-policy.ts +4 -2
  432. package/src/harness/worktree-branch-prefix.ts +9 -0
  433. package/src/hq/fence-wrapper.ts +7 -0
  434. package/src/hq/fence.ts +187 -0
  435. package/src/hq/hq.ts +33 -2
  436. package/src/hq/lease.ts +13 -0
  437. package/src/hq/seats.ts +133 -21
  438. package/src/images/image-ledger.ts +179 -0
  439. package/src/index.ts +250 -69
  440. package/src/instance/child-scope.ts +32 -1
  441. package/src/instance/dev-project.ts +133 -0
  442. package/src/instance/resolve.ts +24 -0
  443. package/src/instance/spawn-elanous.ts +45 -0
  444. package/src/intake-plane/collect-github.ts +19 -1
  445. package/src/intake-plane/digest.ts +45 -20
  446. package/src/intake-plane/lens.ts +36 -8
  447. package/src/intake-plane/ref-prefetch.ts +110 -0
  448. package/src/intelligence-map/model-alias.ts +6 -0
  449. package/src/intelligence-map/model-catalog.ts +113 -0
  450. package/src/knowledge/kgs/pack.ts +5 -2
  451. package/src/knowledge/kgs/sqlite-store.ts +15 -0
  452. package/src/knowledge/tools/knowledge-query.ts +20 -2
  453. package/src/knowledge/write.ts +234 -2
  454. package/src/llm/bedrock-eventstream.ts +215 -0
  455. package/src/llm/bedrock-provider.ts +248 -0
  456. package/src/llm/bedrock.ts +491 -0
  457. package/src/llm/openrouter-byok-cost.ts +96 -0
  458. package/src/llm/openrouter-deepseek.ts +151 -0
  459. package/src/llm/openrouter-upstream-pins.config.ts +25 -0
  460. package/src/llm/provider-credentials.ts +3 -1
  461. package/src/llm/zai-coding-plan.ts +700 -0
  462. package/src/llm.ts +383 -41
  463. package/src/loops/budget.ts +150 -0
  464. package/src/loops/checker.ts +148 -9
  465. package/src/loops/orchestrator/resync-shadow.ts +34 -7
  466. package/src/loops/orchestrator/seat-share.ts +6 -49
  467. package/src/loops/orchestrator/traffic.ts +5 -81
  468. package/src/market/signed-index.ts +42 -0
  469. package/src/maturity/feature-maturity.ts +2 -2
  470. package/src/mcp/mcp-oauth.ts +136 -17
  471. package/src/model-tier/llm-tier-map.ts +52 -4
  472. package/src/models/prompts.ts +23 -1
  473. package/src/mss/logging/horizon-watch.ts +168 -0
  474. package/src/mss/logging/log-store.ts +262 -26
  475. package/src/native-tool-catalog.ts +1 -1
  476. package/src/nexus/api/consult-requests.ts +7 -10
  477. package/src/nexus/api/discord-trigger-bot.ts +6 -3
  478. package/src/nexus/api/error-reports.ts +30 -8
  479. package/src/nexus/api/http-server.ts +8 -0
  480. package/src/nexus/api/loop-resources.ts +42 -0
  481. package/src/nexus/api/missions.ts +73 -2
  482. package/src/nexus/api/ops-api.ts +18 -0
  483. package/src/nexus/api/outbound-report.ts +8 -3
  484. package/src/nexus/api/telegram-trigger-bot.ts +6 -3
  485. package/src/nexus/index.ts +17 -75
  486. package/src/nexus/outbound/format.ts +2 -0
  487. package/src/nexus/outbound/router.ts +198 -13
  488. package/src/nexus/tools-kind.ts +78 -0
  489. package/src/oauth/codex-account-rotation.ts +55 -20
  490. package/src/oauth/codex-account-store.ts +451 -23
  491. package/src/oauth/fallback-chain.ts +75 -15
  492. package/src/onboarding/first-run.ts +37 -18
  493. package/src/onboarding/web-first.ts +20 -2
  494. package/src/onboarding.ts +2 -1
  495. package/src/ops/machine-heartbeat.ts +185 -0
  496. package/src/ops/overview-cache.ts +175 -0
  497. package/src/ops/overview.ts +8 -0
  498. package/src/plugins/adapters/claude-package.ts +4 -12
  499. package/src/plugins/core/capability-policy.ts +141 -0
  500. package/src/plugins/core/host.ts +21 -8
  501. package/src/plugins/core/manifest.ts +19 -2
  502. package/src/plugins/core/trust-store.ts +15 -0
  503. package/src/plugins/core/types.ts +12 -0
  504. package/src/plugins/install/market-fetch.ts +68 -16
  505. package/src/plugins/install/plugin-install.ts +187 -16
  506. package/src/plugins/maker/plugin-maker.ts +25 -20
  507. package/src/plugins/maker/wizard-generate.ts +16 -2
  508. package/src/policy/llm-policy.ts +14 -1
  509. package/src/process/pid-liveness.ts +1 -1
  510. package/src/prompt-cache/openai.ts +13 -6
  511. package/src/prompt-cache/openrouter.ts +94 -0
  512. package/src/prompt-cache/types.ts +5 -0
  513. package/src/prompt-enhance/enhance.ts +69 -5
  514. package/src/prompt-library/family-prompt-coverage.ts +1 -0
  515. package/src/prompt-library/universal-preamble.ts +2 -2
  516. package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
  517. package/src/registry/loader.ts +6 -1
  518. package/src/release-loop/checklist.ts +24 -9
  519. package/src/release-loop/feature-store.ts +3 -0
  520. package/src/release-loop/gate-shards.ts +48 -4
  521. package/src/release-loop/landed-to-green.ts +81 -0
  522. package/src/release-loop/light-rc.ts +11 -1
  523. package/src/release-loop/merged-pr-checklist.ts +10 -0
  524. package/src/release-loop/placement.ts +107 -23
  525. package/src/release-loop/rehearsal.ts +317 -0
  526. package/src/release-loop/release-schedule.ts +13 -1
  527. package/src/release-loop/rubric-types.ts +1 -0
  528. package/src/release-loop/rubric.ts +2 -2
  529. package/src/requirements/requirement-ledger.ts +536 -0
  530. package/src/retro/daily.ts +26 -12
  531. package/src/seat-loop/seat-loop.ts +66 -11
  532. package/src/seat-loop/traffic-signal.ts +44 -0
  533. package/src/self-dev/ask-launch-flow.ts +2 -2
  534. package/src/self-dev/dev-cli.ts +109 -11
  535. package/src/self-dev/dev-pipeline.ts +11 -8
  536. package/src/self-dev/draft-residue.ts +475 -0
  537. package/src/self-dev/draft-sweep.ts +254 -10
  538. package/src/self-dev/draft-triage-rules.ts +27 -1
  539. package/src/self-dev/graph-journey-route.ts +6 -0
  540. package/src/self-dev/launch-preflight-text.ts +549 -0
  541. package/src/self-dev/launch-preflight.ts +10 -545
  542. package/src/self-dev/orchestrate-cli.ts +82 -5
  543. package/src/self-dev/orchestrate-summary-line.ts +13 -0
  544. package/src/self-dev/orchestrate.ts +49 -141
  545. package/src/self-dev/review-provider-fallback.ts +115 -19
  546. package/src/self-dev/run-store.ts +1 -3
  547. package/src/self-dev/run-supervisor.ts +190 -36
  548. package/src/self-dev/run-types.ts +191 -0
  549. package/src/self-dev/shard-aggregate.ts +61 -13
  550. package/src/self-dev/shard-plan.ts +5 -1
  551. package/src/self-dev/shard-run.ts +23 -5
  552. package/src/self-dev/supervisor-verdict-edges.ts +3 -4
  553. package/src/self-implement/blocked-draft-policy.ts +3 -19
  554. package/src/self-implement/budget-gate.ts +53 -13
  555. package/src/self-implement/child-context-composition.ts +139 -0
  556. package/src/self-implement/child-llm-preference.ts +13 -5
  557. package/src/self-implement/claude-acp-implement.ts +67 -0
  558. package/src/self-implement/draft-on-stop-mode.ts +18 -0
  559. package/src/self-implement/flex-signals.ts +59 -0
  560. package/src/self-implement/follow-up-goals.ts +114 -38
  561. package/src/self-implement/frozen-merges.ts +8 -3
  562. package/src/self-implement/gate-baseline-reap.ts +157 -0
  563. package/src/self-implement/gate-baseline.ts +78 -16
  564. package/src/self-implement/gate-cli.ts +12 -2
  565. package/src/self-implement/gate-remote.ts +17 -6
  566. package/src/self-implement/goal-author-decompose.ts +10 -2
  567. package/src/self-implement/goal-author.ts +48 -13
  568. package/src/self-implement/goal-digest.ts +19 -0
  569. package/src/self-implement/graph-overlay-condition.ts +32 -0
  570. package/src/self-implement/graph-overlay-yaml.ts +41 -7
  571. package/src/self-implement/graph-templates.ts +33 -1
  572. package/src/self-implement/graph-yaml.ts +56 -0
  573. package/src/self-implement/headless-elanous-driver.ts +11 -9
  574. package/src/self-implement/heal-intake.ts +5 -1
  575. package/src/self-implement/host-regate.ts +120 -29
  576. package/src/self-implement/l8-merge-queue.ts +67 -5
  577. package/src/self-implement/merge-train.ts +131 -0
  578. package/src/self-implement/off-diff-evidence.ts +9 -0
  579. package/src/self-implement/orchestrator.ts +271 -25
  580. package/src/self-implement/review-finding-recurrence.ts +28 -0
  581. package/src/self-implement/rework-policy.ts +2 -1
  582. package/src/self-implement/round-summary-card.ts +63 -0
  583. package/src/self-implement/run-ledger.ts +4 -3
  584. package/src/self-implement/run-step-projection.ts +147 -0
  585. package/src/self-implement/run-stop.ts +1 -1
  586. package/src/self-implement/running-runs.ts +75 -5
  587. package/src/self-implement/seams.ts +83 -4
  588. package/src/self-implement/self-implement-cli.ts +12 -5
  589. package/src/self-implement/stop-autoheal.ts +69 -2
  590. package/src/self-implement/train-gate-remote.ts +158 -0
  591. package/src/session-runtime/index.ts +10 -1
  592. package/src/session-runtime/retry-api.ts +11 -1
  593. package/src/session-runtime/retry-policy.ts +2 -1
  594. package/src/skills/gift-install.ts +119 -0
  595. package/src/skills/runner.ts +59 -2
  596. package/src/skills/skill-feedback.ts +31 -1
  597. package/src/skills/skill-tier.ts +1 -0
  598. package/src/smart-wizard/capability-index.ts +231 -0
  599. package/src/smart-wizard/rung.ts +74 -0
  600. package/src/steward/launch.ts +22 -10
  601. package/src/steward/triage-budget.ts +62 -0
  602. package/src/steward/triage.ts +87 -24
  603. package/src/storage/file-lock.ts +8 -1
  604. package/src/task-agent/actions.ts +66 -5
  605. package/src/task-agent/card-evidence.ts +59 -13
  606. package/src/task-agent/completion-evidence.ts +5 -0
  607. package/src/task-agent/cover.ts +101 -3
  608. package/src/task-agent/judge.ts +20 -3
  609. package/src/task-agent/land-order-ledger.ts +95 -0
  610. package/src/task-agent/land-worktree.ts +201 -0
  611. package/src/task-agent/live-move-history.ts +219 -0
  612. package/src/task-agent/live-moves.ts +451 -34
  613. package/src/task-agent/parent-host.ts +117 -8
  614. package/src/task-agent/rejudge-on-head.ts +352 -0
  615. package/src/task-agent/review-prior-fix.ts +146 -0
  616. package/src/task-agent/review-result-capture.ts +218 -0
  617. package/src/task-agent/run-adopt.ts +136 -0
  618. package/src/task-agent/shadow.ts +154 -14
  619. package/src/task-agent/task-hand.ts +186 -14
  620. package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
  621. package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
  622. package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
  623. package/src/task-orchestrator/surfaces/pod-child-providers.ts +42 -0
  624. package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
  625. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +94 -7
  626. package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
  627. package/src/task-orchestrator/surfaces/pod-lease.ts +110 -17
  628. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +32 -3
  629. package/src/task-orchestrator/surfaces/pod-pool.ts +86 -18
  630. package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
  631. package/src/task-orchestrator/surfaces/self-implement-pod.ts +463 -50
  632. package/src/task-orchestrator/surfaces/self-implement.ts +25 -3
  633. package/src/telegram-service.ts +5 -1
  634. package/src/telegram.ts +17 -4
  635. package/src/time/format.ts +13 -4
  636. package/src/tool-runtime/goal-author-runtime.ts +1 -0
  637. package/src/tool-runtime/self-cognition-catalog.ts +23 -0
  638. package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
  639. package/src/typecheck-ratchet.ts +1 -0
  640. package/src/user-config.ts +180 -32
  641. package/src/ux-sim/slash-menu-frames.ts +168 -0
  642. package/src/version/packed-revision.json +1 -1
  643. package/src/web-terminal/preview-tap-registry.ts +5 -2
  644. package/src/workflow-runtime/triggers/registry.ts +59 -0
  645. package/src/workflow-runtime/triggers/scheduler.ts +1 -0
  646. package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
  647. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
  648. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
  649. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
  650. package/apps/pwa/out/_next/static/chunks/1307-08b716476f1d50af.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
  652. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
  653. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
  654. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
  656. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
  661. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
  670. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
  671. package/apps/pwa/out/_next/static/chunks/2489-d238d3e2eb5b2bf5.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
  684. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
  692. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
  697. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
  699. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
  703. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
  708. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
  713. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
  714. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
  716. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
  717. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
  725. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
  728. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
  738. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
  741. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
  745. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
  746. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d85c43f46f58716b.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/app/board/page-4e645600579ae952.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/app/botlab/page-87edbfd19117823b.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/app/chat/page-cbaf66b1e1804adf.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/app/control/page-9f5610caaa06178c.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/app/decisions/page-ec8f3575e339de74.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +0 -1
  758. package/apps/pwa/out/_next/static/chunks/app/exec/page-2a19c5cd3b562580.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/app/field/page-b6a09cde2e07521a.js +0 -1
  760. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +0 -1
  763. package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +0 -1
  764. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +0 -1
  765. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +0 -1
  766. package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +0 -1
  767. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +0 -1
  768. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +0 -1
  769. package/apps/pwa/out/_next/static/chunks/app/morning/page-786fb4504d0f2fa9.js +0 -1
  770. package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +0 -1
  771. package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +0 -1
  772. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +0 -1
  773. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +0 -1
  774. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +0 -1
  775. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +0 -1
  776. package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +0 -1
  777. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7ec8e289b4c14df6.js +0 -1
  778. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-8c8ac17d52be2a79.js +0 -1
  779. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d359ab048d63a9d9.js +0 -1
  780. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +0 -1
  781. package/apps/pwa/out/_next/static/chunks/app/settings/page-f151cc10ad026940.js +0 -1
  782. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +0 -1
  783. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
  784. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +0 -1
  785. package/apps/pwa/out/_next/static/chunks/app/share/page-bf495a1e284f4e00.js +0 -1
  786. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +0 -1
  787. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +0 -1
  788. package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +0 -1
  789. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +0 -1
  790. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +0 -1
  791. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +0 -1
  792. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +0 -1
  793. package/apps/pwa/out/_next/static/chunks/app/workflows/page-69cb93d1a2e57e0e.js +0 -1
  794. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72ec543928d67c3e.js +0 -1
  795. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +0 -1
  796. package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
  797. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
  798. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
  799. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
  800. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
  801. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
  802. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +0 -1
  803. /package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → Q-7iofoxOEv1WX18S35of}/_ssgManifest.js +0 -0
@@ -35,6 +35,18 @@ import {
35
35
  type MissionStatus,
36
36
  } from '../../task-orchestrator/mission.js';
37
37
  import type { Task } from '../../task-orchestrator/types.js';
38
+ import {
39
+ openSchedulesDb,
40
+ listSchedules,
41
+ scheduleHealth,
42
+ } from '../../domains/schedule-registry.js';
43
+ import {
44
+ cronRowsToDerived,
45
+ rollupDerived,
46
+ taskStatusToDerived,
47
+ type DerivedJob,
48
+ type MissionRollup,
49
+ } from '../../autopilot/mission-trace.js';
38
50
 
39
51
  import { checkAuth, type MetaApiOpts } from './meta-api.js';
40
52
  import { jsonResponse } from './http-server.js';
@@ -68,12 +80,63 @@ export interface MissionCardWire {
68
80
  * Mission. 자율 Mission 이면 실행모델·apm 상태·계보 키를 담아 통합 Missions
69
81
  * 표면이 autopilot 여부와 lineage 를 한눈에 렌더한다. */
70
82
  autopilot: MissionAutopilot | null;
83
+ /** 파생 작업 롤업(태스크 + 크론). ⛔ **결손 수리(MISSIONS-STALE-MARK · 2026-10-11)**:
84
+ * 이 칸이 없어 PWA 미션 카드의 `RollupChips` 는 항상 `undefined` 를 받아
85
+ * «아무것도» 안 그렸다 — 그래서 «파생 0»과 «파생 미측정»을 화면이 «구분 못 했다».
86
+ * 카드는 목록 엔드포인트만 보므로(미션별 `traceAutopilotMission` 을 안 부름)
87
+ * 여기서 «이미 로드한 태스크» ⊕ «요청당 한 번 읽은 schedule_registry」로
88
+ * 계산한다. 자율행동(surface_events)·자식 미션은 상세 트레이스
89
+ * (`ops_status action=mission`)의 몫으로 남는다(목록에서 N× 무거운 조인 금지).
90
+ * 매핑은 `rollupDerived`·`taskStatusToDerived`·`cronRowsToDerived` — «단일 소유»라
91
+ * 트레이스와 갈림이 없다. `total: 0` = «파생이 정말 0». */
92
+ derived: MissionRollup;
71
93
  createdAt: number;
72
94
  updatedAt: number;
73
95
  closedAt: number | null;
74
96
  }
75
97
 
76
- export function summarise(mission: Mission, tasks: readonly Task[]): MissionCardWire {
98
+ /** 태스크 행 → 파생물(순수). 트레이스의 `fetchTaskDerived` 와 같은 상태 매핑
99
+ * (`taskStatusToDerived`)을 쓴다 — «두 가지 상태 어휘»를 만들지 않는다. */
100
+ export function taskRowsToDerived(tasks: readonly Task[]): DerivedJob[] {
101
+ return tasks.map((t) => ({
102
+ kind: 'task',
103
+ id: t.id,
104
+ name: t.title,
105
+ status: taskStatusToDerived(t.status),
106
+ }));
107
+ }
108
+
109
+ /** 요청당 «한 번» schedule_registry 를 읽어 apmId 별 크론 파생물을 나눠 담는다.
110
+ * ⛔ 미션별로 DB 를 열면 목록 N건 = N× 열기 — 그래서 한 번 읽고 in-process 분배.
111
+ * fail-soft: 레지스트리가 없으면 빈 Map(derived 는 태스크만 집계). */
112
+ export function loadCronDerivedByMission(): Map<string, DerivedJob[]> {
113
+ const out = new Map<string, DerivedJob[]>();
114
+ try {
115
+ const db = openSchedulesDb();
116
+ try {
117
+ const rows = listSchedules(db);
118
+ const staleIds = new Set(scheduleHealth(rows).stale.map((s) => s.id));
119
+ const rowsByMission = new Map<string, typeof rows>();
120
+ for (const row of rows) {
121
+ const apmId = row.autopilot_id;
122
+ if (!apmId) continue;
123
+ const bucket = rowsByMission.get(apmId);
124
+ if (bucket) bucket.push(row);
125
+ else rowsByMission.set(apmId, [row]);
126
+ }
127
+ for (const [apmId, missionRows] of rowsByMission) {
128
+ out.set(apmId, cronRowsToDerived(missionRows, staleIds));
129
+ }
130
+ } finally { db.close(); }
131
+ } catch { /* fail-soft */ }
132
+ return out;
133
+ }
134
+
135
+ export function summarise(
136
+ mission: Mission,
137
+ tasks: readonly Task[],
138
+ crons: readonly DerivedJob[] = [],
139
+ ): MissionCardWire {
77
140
  const taskStatusCounts: Record<string, number> = {};
78
141
  const surfaceKindCounts: Record<string, number> = {};
79
142
  for (const t of tasks) {
@@ -92,6 +155,7 @@ export function summarise(mission: Mission, tasks: readonly Task[]): MissionCard
92
155
  taskStatusCounts,
93
156
  surfaceKindCounts,
94
157
  autopilot: mission.autopilot ?? null,
158
+ derived: rollupDerived([...taskRowsToDerived(tasks), ...crons]),
95
159
  createdAt: mission.createdAt,
96
160
  updatedAt: mission.updatedAt,
97
161
  closedAt: mission.closedAt ?? null,
@@ -115,8 +179,15 @@ export function handleMissionsList(
115
179
  const store = new TaskStore();
116
180
  try {
117
181
  const missions = store.listMissions(filter);
182
+ // 크론 레지스트리는 «요청당 한 번»만 읽고 apmId 로 분배(미션별 DB 열기 금지).
183
+ const cronsByMission = loadCronDerivedByMission();
118
184
  const cards = missions.map((m) =>
119
- summarise(m, store.listTasksForMission(m.id)),
185
+ summarise(
186
+ m,
187
+ store.listTasksForMission(m.id),
188
+ // schedule_registry.autopilot_id 는 계보 키(apmId)다 — TOX 미션 id 와 다를 수 있다.
189
+ cronsByMission.get(m.autopilot?.apmId ?? m.id) ?? cronsByMission.get(m.id) ?? [],
190
+ ),
120
191
  );
121
192
  return jsonResponse({
122
193
  total: cards.length,
@@ -9,6 +9,8 @@ import { jsonResponse } from './json-response.js';
9
9
  import { operatorSignal } from './operator.js';
10
10
  import { createSeatsCache, todayKst } from './ops-seats.js';
11
11
  import { liveSeatsSources } from './ops-seats-sources.js';
12
+ import { createOverviewCache } from '../../ops/overview-cache.js';
13
+ import { parseSince } from '../../ops/overview-collect.js';
12
14
  import type { MetaApiOpts } from './meta-api.js';
13
15
 
14
16
  const VERSION = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
@@ -17,6 +19,11 @@ const DATE = /^\d{4}-\d{2}-\d{2}$/;
17
19
  let seatsCache: ReturnType<typeof createSeatsCache> | null = null;
18
20
  /** Tests swap the sources; production builds them lazily on first use. */
19
21
  export function setSeatsCacheForTest(cache: ReturnType<typeof createSeatsCache> | null): void { seatsCache = cache; }
22
+ let overviewCache: ReturnType<typeof createOverviewCache> | null = null;
23
+ export function setOverviewCacheForTest(cache: ReturnType<typeof createOverviewCache> | null): void {
24
+ overviewCache?.stop();
25
+ overviewCache = cache;
26
+ }
20
27
  const LOG_PATH = /^\/v1\/ops\/release\/runs\/([^/]+)\/nodes\/([^/]+)\/log$/;
21
28
 
22
29
  interface RunNode { nodeId: string; ok?: unknown; output?: unknown; startedAt?: unknown; endedAt?: unknown }
@@ -190,6 +197,17 @@ export function handleOpsApi(req: Request, metaApi: MetaApiOpts | undefined): Re
190
197
  response.headers.set('cache-control', 'no-store');
191
198
  return response;
192
199
  };
200
+ if (pathname === '/v1/ops/overview') {
201
+ let since: ReturnType<typeof parseSince>;
202
+ try { since = parseSince(url.searchParams.get('since') ?? undefined); }
203
+ catch { return jsonResponse({ error: 'invalid-since' }, 400); }
204
+ const version = url.searchParams.get('version');
205
+ if (version !== null && !VERSION.test(version)) return jsonResponse({ error: 'invalid-version' }, 400);
206
+ overviewCache ??= createOverviewCache();
207
+ return overviewCache.get({ sinceMs: since.ms, sinceLabel: since.label,
208
+ ...(version ? { version } : {}), ...(url.searchParams.has('owner') ? { owner: url.searchParams.get('owner')! } : {}) })
209
+ .then((overview) => served(jsonResponse(overview)), () => jsonResponse({ error: 'overview-unavailable' }, 500));
210
+ }
193
211
  if (pathname === '/v1/ops/seats') {
194
212
  const date = url.searchParams.get('date') ?? todayKst();
195
213
  if (!DATE.test(date) || Number.isNaN(Date.parse(`${date}T00:00:00+09:00`))) return jsonResponse({ error: 'invalid-date' }, 400);
@@ -8,7 +8,7 @@
8
8
  // so ALL outbound routes through elanous's delivery channels. Send-only;
9
9
  // auth via bearer (acp-token).
10
10
  //
11
- // Body: { text: string, markdown?: boolean, kind?: string }
11
+ // Body: { text: string, markdown?: boolean, kind?: string, role?: string }
12
12
  // kind routes per-channel since R6 Phase 1 (2026-07-07): the body is
13
13
  // handed to routeOutbound (src/nexus/outbound/router.ts) which fans out
14
14
  // to telegram/discord/pushcut per the `outbound` user-config section.
@@ -53,6 +53,7 @@ interface OutboundBody {
53
53
  text?: string;
54
54
  markdown?: boolean;
55
55
  kind?: string;
56
+ role?: string;
56
57
  }
57
58
 
58
59
  function json(body: unknown, status: number): Response {
@@ -85,10 +86,14 @@ export async function handleOutboundReport(req: Request, metaApi: MetaApiOpts, r
85
86
  const cfg = getUserConfig();
86
87
  const kind = typeof body.kind === 'string' ? body.kind : 'report';
87
88
  try {
88
- // Router is per-channel fail-soft; `delivered` = any channel ok.
89
+ // routeOutbound dispatches via the channel adapter registry; legacy fan-out
90
+ // remains per-channel fail-soft and `delivered` = any channel ok.
89
91
  // Producers (outbound-alert.ts deliver()) gate their direct-telegram
90
92
  // fallback on this top-level boolean — keep it stable.
91
- const result = await routeOutbound(cfg, { text, markdown: body.markdown ?? true, kind }, routerDeps);
93
+ const role = typeof body.role === 'string' && /^[a-zA-Z0-9_-]{1,80}$/.test(body.role) ? body.role : undefined;
94
+ // A request that names a role intends that role's recipients — never fall back to the kind route silently.
95
+ if (body.role !== undefined && role === undefined) return json({ error: 'invalid-role' }, 400);
96
+ const result = await routeOutbound(cfg, { text, markdown: body.markdown ?? true, kind, ...(role ? { role } : {}) }, routerDeps);
92
97
  const clientFallback = req.headers.get('X-Elanous-Client-Fallback') === 'direct';
93
98
  const source = req.headers.get('X-Elanous-Outbound-Source') ?? 'daemon-http';
94
99
  if (result.delivered || !clientFallback) observeDaemonSend(cfg, text, kind, result, source);
@@ -36,6 +36,7 @@ import { parseTelegramEndpoint, isOwnInstanceEndpoint, matchesBot } from '../../
36
36
  import { createTelegramStreamSink } from '../../session/streaming/telegram-stream-sink.js';
37
37
  import { splitMarkdownForTelegram } from '../../telegram-format.js';
38
38
  import { formatTablesAndRules } from '../../discord-markdown.js';
39
+ import { receiveChannelEvent } from '../outbound/router.js';
39
40
 
40
41
  export interface NexusTelegramTriggerBotOpts {
41
42
  /** Bot token (without `Bot ` prefix). Production reads
@@ -117,11 +118,13 @@ export function createTelegramTriggerLogger(
117
118
  * comment) so the wire is a near-identity with `raw` carrying the
118
119
  * full payload (messageId · isDm) for debugging. */
119
120
  export function toTelegramEvent(tap: TgTriggerEvent): TelegramEvent {
121
+ const envelope = receiveChannelEvent('telegram', tap);
122
+ if (!envelope || envelope instanceof Promise) throw new Error('telegram connector cannot normalize accepted tap');
120
123
  return {
121
124
  kind: tap.kind,
122
- chat: tap.chat,
123
- user: tap.user,
124
- body: tap.body,
125
+ chat: envelope.channel ?? tap.chat,
126
+ user: envelope.senderId ?? tap.user,
127
+ body: envelope.text,
125
128
  ...(tap.command !== undefined ? { command: tap.command } : {}),
126
129
  raw: tap,
127
130
  };
@@ -410,6 +410,8 @@ export interface RunNexusOptions {
410
410
  * detachForTesting; production runs always start the server. Tests
411
411
  * that DO want the server can pass false explicitly. */
412
412
  skipHttpServer?: boolean;
413
+ /** Test seam: replaces the daily error-report prune timer (the boot prune still runs for real). */
414
+ errorReportPruneScheduleForTesting?: (tick: () => void, ms: number) => { unref?: () => void };
413
415
  /** Override starting port for the HTTP server. Defaults to 31415. */
414
416
  httpStartPort?: number;
415
417
  /** Control-plane port; passing 0 also enables an isolated listener in detached tests. */
@@ -1266,6 +1268,13 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
1266
1268
  // 60s 틱마다 grace 유예 초과 구독자를 left 로 이탈 확정(reconcileSessionPresence).
1267
1269
  // cleared in shutdown hook.
1268
1270
  let presenceSweepHandle: ReturnType<typeof setInterval> | undefined;
1271
+ // ERROR-REPORT-PRUNE-TICK — 30-day error-report retention must not wait for the next receipt:
1272
+ // prune once at boot and once a day (unconditional · fail-soft · unref). Cleared in shutdown hook.
1273
+ let errorReportPrune: { stop(): void } | undefined;
1274
+ try {
1275
+ const { startErrorReportPruneTick } = await import('./api/error-reports.js');
1276
+ errorReportPrune = startErrorReportPruneTick({ ...(opts.errorReportPruneScheduleForTesting ? { schedule: opts.errorReportPruneScheduleForTesting } : {}) });
1277
+ } catch (err) { console.warn(`[nexus] error-report prune wiring failed: ${(err as Error).message}`); }
1269
1278
  // ★ UR4d — 조율자 저지연 push watcher(logs.db tail edge-trigger). opt-in·기본 OFF.
1270
1279
  // cleared in shutdown hook(내부 타이머는 unref 이나 명시 정지).
1271
1280
  let coordinatorPushWatcher: { stop(): void } | undefined;
@@ -3383,6 +3392,7 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
3383
3392
  if (replayArmerHandle) { clearInterval(replayArmerHandle); replayArmerHandle = undefined; }
3384
3393
  // §P2 — stop the session presence grace sweeper.
3385
3394
  if (presenceSweepHandle) { clearInterval(presenceSweepHandle); presenceSweepHandle = undefined; }
3395
+ if (errorReportPrune) { errorReportPrune.stop(); errorReportPrune = undefined; }
3386
3396
  // ★ UR4d — stop the coordinator low-latency push watcher(logs.db tail + debounce).
3387
3397
  if (coordinatorPushWatcher) { try { coordinatorPushWatcher.stop(); } catch { /* ignore */ } coordinatorPushWatcher = undefined; }
3388
3398
  // Phase 2 (2026-05-13) — dispose the TOX boot handle. dispose()
@@ -4011,78 +4021,10 @@ function shouldRegisterWebterm(): boolean {
4011
4021
  // `boot/register-daemon.ts` and re-exported above (line 32) so existing
4012
4022
  // test imports (`from '../src/nexus/index.js'`) keep working.
4013
4023
 
4014
- /** Resolve the active daemon tool surface kind for runNexus.
4015
- *
4016
- * Resolution order (parity with `deriveChildEnv` for daemon tab spawn —
4017
- * the same switch fed into a daemon child env should govern the in-
4018
- * process path too):
4019
- *
4020
- * 1. CLI flag (`opts.tools`) ← 1회 boot
4021
- * 2. `ELANOUS_TOOLS` env ← shell session
4022
- * 3. UserConfig switch `global.tools` (PWA Settings 토글이 여기) ← 영구
4023
- * 4. switch default = `'webterm'` (`builtins/global.ts:12`) ← fallback
4024
- *
4025
- * Pre-fix the in-process branch fell back to `'none'` while
4026
- * `deriveChildEnv` fell back to the switch default `'webterm'`. That
4027
- * meant a clean-machine `elanous nexus` (no flag/env) gave the in-process
4028
- * LLM call an empty tool catalog while a separately-launched
4029
- * `elanous serve` daemon honored the switch default's full surface —
4030
- * surprising divergence the user had no way to discover.
4031
- *
4032
- * `'all'` is in the switch enum but has no `toolSurface()` impl yet;
4033
- * fall back to `'webterm'` (the most-capable wired surface). */
4034
- const daemonToolSurfaceKinds: Record<DaemonToolSurfaceKind, true> = {
4035
- none: true,
4036
- readonly: true,
4037
- chat: true,
4038
- webterm: true,
4039
- };
4040
-
4041
- function resolveKnownToolsKind(raw: string): DaemonToolSurfaceKind | undefined {
4042
- return Object.hasOwn(daemonToolSurfaceKinds, raw)
4043
- ? raw as DaemonToolSurfaceKind
4044
- : undefined;
4045
- }
4046
-
4047
- export function resolveToolsKind(opts: { tools?: string }): DaemonToolSurfaceKind {
4048
- let raw: string | undefined = opts.tools;
4049
- if (!raw) raw = process.env.ELANOUS_TOOLS?.trim();
4050
- if (!raw) {
4051
- try {
4052
- const cfg = readUserConfig();
4053
- const v = readSwitchValue(cfg, 'global.tools');
4054
- if (typeof v === 'string') raw = v;
4055
- } catch { /* swallow — boot must not fail on a malformed config */ }
4056
- }
4057
- if (!raw) raw = 'webterm';
4058
- return resolveKnownToolsKind(raw) ?? 'webterm';
4059
- }
4060
-
4061
- /** Resolve the `:agent` (통합 모드 webterm dock) tool surface kind.
4062
- *
4063
- * Distinct from {@link resolveToolsKind} because the integrated
4064
- * webterm-dock chat is **defined by** its PTY surface — WebTerminal*
4065
- * tools (Snapshot · Input · Screenshot) are what make `:agent`
4066
- * meaningful next to a live terminal. If the user has narrowed their
4067
- * user-config (`global.tools chat` / `readonly`) to keep the
4068
- * standalone `/chat` lighter, that preference must NOT bleed into the
4069
- * integrated dock — otherwise the `:agent` LLM loses access to the
4070
- * very terminal it's supposed to drive.
4071
- *
4072
- * Resolution order:
4073
- * 1. CLI flag (`opts.tools`) ← explicit operator intent wins
4074
- * 2. **`'webterm'`** (hardcoded) ← env + global.tools intentionally
4075
- * skipped vs. resolveToolsKind
4076
- *
4077
- * CLI flag is honored so a developer debugging tool selection
4078
- * (`elanous nexus run --tools readonly`) still sees a readonly `:agent`;
4079
- * but a clean-machine boot or a config-narrowed boot always gets
4080
- * webterm for the integrated dock. */
4081
- export function resolveAgentTurnToolsKind(opts: { tools?: string }): DaemonToolSurfaceKind {
4082
- const raw = opts.tools?.trim();
4083
- if (!raw) return 'webterm';
4084
- // 'all' (or any unrecognized string) → webterm fallback, same as
4085
- // resolveToolsKind so the two resolvers stay consistent for valid
4086
- // CLI input.
4087
- return resolveKnownToolsKind(raw) ?? 'webterm';
4088
- }
4024
+ // C-2a (same pattern as `shouldRegisterDaemon` above): the tool-surface
4025
+ // resolution block (`daemonToolSurfaceKinds` · `resolveKnownToolsKind` ·
4026
+ // `resolveToolsKind` · `resolveAgentTurnToolsKind`) moved to
4027
+ // `nexus/tools-kind.ts` and re-exported here so existing test imports
4028
+ // (`from '../src/nexus/index.js'`) keep working.
4029
+ import { resolveAgentTurnToolsKind, resolveToolsKind } from './tools-kind.js';
4030
+ export { resolveToolsKind, resolveAgentTurnToolsKind } from './tools-kind.js';
@@ -12,6 +12,8 @@ export interface OutboundMsg {
12
12
  text: string;
13
13
  markdown: boolean;
14
14
  kind: string;
15
+ /** Optional routing role; absent preserves per-kind fan-out. */
16
+ role?: string;
15
17
  }
16
18
 
17
19
  export interface FormattedMessage {
@@ -43,8 +43,66 @@ import {
43
43
  export { chunkForDiscord } from './format.js';
44
44
  export type { OutboundChannelType, OutboundMsg } from './format.js';
45
45
 
46
+ /** The adapter's capability declaration is explicit: a feature is unavailable unless
47
+ * the connector implements it. Send-only webhooks must not claim inbound support. */
48
+ export interface ChannelCapabilities {
49
+ send: boolean;
50
+ receive: boolean;
51
+ thread: boolean;
52
+ topic: boolean;
53
+ persona: boolean;
54
+ channelLifecycle: boolean;
55
+ permissions: boolean;
56
+ commands: boolean;
57
+ attachments: boolean;
58
+ buttons: boolean;
59
+ choices: boolean;
60
+ deliveryConfirmation: boolean;
61
+ rateLimit: boolean;
62
+ }
63
+
64
+ export interface ChannelEnvelope {
65
+ text: string;
66
+ kind: string;
67
+ channel?: string;
68
+ senderId?: string;
69
+ messageId?: string | number;
70
+ threadId?: string;
71
+ topicId?: string;
72
+ persona?: { name: string; avatarUrl?: string };
73
+ attachments?: readonly { url: string; name?: string }[];
74
+ buttons?: readonly { label: string; value: string }[];
75
+ choices?: readonly { label: string; value: string }[];
76
+ }
77
+
78
+ /** Plugin contract; optional operations may only be advertised when implemented.
79
+ * `send` returns the same ChannelResult shape used by the existing delivery ledger. */
80
+ export interface ChannelAdapter {
81
+ readonly type: string;
82
+ readonly capabilities: ChannelCapabilities;
83
+ send(config: OutboundChannelConfig, message: OutboundMsg, cfg: UserConfig, deps: RouterDeps): Promise<ChannelResult>;
84
+ receive?: (payload: unknown) => ChannelEnvelope | null | Promise<ChannelEnvelope | null>;
85
+ createChannel?: (name: string) => Promise<string>;
86
+ archiveChannel?: (id: string) => Promise<void>;
87
+ checkPermission?: (action: string) => Promise<boolean>;
88
+ registerCommands?: (commands: readonly string[]) => Promise<void>;
89
+ confirmDelivery?: (messageId: string) => Promise<boolean>;
90
+ retryAfterMs?: (result: ChannelResult) => number | undefined;
91
+ /** Validate a plugin's channel entry before it becomes eligible for routing.
92
+ * Unknown or invalid entries stay disabled rather than falling back to a different channel. */
93
+ parseConfig?: (raw: Record<string, unknown>) => OutboundChannelConfig | undefined;
94
+ }
95
+
96
+ /** Existing bot receive paths can normalize an accepted event through the same
97
+ * adapter contract before dispatching to their unchanged workflow/chat handlers. */
98
+ export function receiveChannelEvent(type: string, payload: unknown): ChannelEnvelope | null | Promise<ChannelEnvelope | null> {
99
+ const adapter = outboundAdapters.get(type) ?? installedAdapters.get(type);
100
+ if (!adapter?.capabilities.receive || !adapter.receive) return null;
101
+ return adapter.receive(payload);
102
+ }
103
+
46
104
  export interface OutboundChannelConfig {
47
- type: OutboundChannelType;
105
+ type: OutboundChannelType | (string & {});
48
106
  /** discord: full webhook URL (required). pushcut: webhook URL — when
49
107
  * omitted the adapter falls back to the API-key client
50
108
  * (~/.elanous/pushcut.json) with `notification`. */
@@ -54,6 +112,24 @@ export interface OutboundChannelConfig {
54
112
  notification?: string;
55
113
  }
56
114
 
115
+ /** Connector providers register while the host is running; unregister on plugin
116
+ * deactivation. Built-ins cannot be replaced by a plugin or removed. */
117
+ const installedAdapters = new Map<string, ChannelAdapter>();
118
+ export function registerOutboundAdapter(adapter: ChannelAdapter): () => void {
119
+ if (!/^[a-z][a-z0-9-]*$/.test(adapter.type) || outboundAdapters.has(adapter.type) || installedAdapters.has(adapter.type)
120
+ || !adapter.capabilities.send || typeof adapter.send !== 'function' || typeof adapter.parseConfig !== 'function'
121
+ || (adapter.capabilities.receive && typeof adapter.receive !== 'function')
122
+ || (adapter.capabilities.channelLifecycle && (typeof adapter.createChannel !== 'function' || typeof adapter.archiveChannel !== 'function'))
123
+ || (adapter.capabilities.permissions && typeof adapter.checkPermission !== 'function')
124
+ || (adapter.capabilities.commands && typeof adapter.registerCommands !== 'function')
125
+ || (adapter.capabilities.deliveryConfirmation && typeof adapter.confirmDelivery !== 'function')
126
+ || (adapter.capabilities.rateLimit && typeof adapter.retryAfterMs !== 'function')) {
127
+ throw new Error(`invalid or duplicate outbound connector: ${adapter.type}`);
128
+ }
129
+ installedAdapters.set(adapter.type, adapter);
130
+ return () => { if (installedAdapters.get(adapter.type) === adapter) installedAdapters.delete(adapter.type); };
131
+ }
132
+
57
133
  export interface OutboundFanoutConfig {
58
134
  channels: OutboundChannelConfig[];
59
135
  /** kind → channel types. A kind missing here falls back to
@@ -62,6 +138,11 @@ export interface OutboundFanoutConfig {
62
138
  routes?: Record<string, string[]>;
63
139
  /** 외출 중 kind → channel types. 미지정 kind는 awayRoutes.default, 이것도 없으면 기존 routes를 유지한다. */
64
140
  awayRoutes?: Record<string, string[]>;
141
+ /** Optional primary/fallback per message kind or role; legacy routes remain fan-out. */
142
+ primary?: Record<string, string>;
143
+ fallback?: Record<string, string>;
144
+ /** Optional role → eligible channel types; no role retains kind routes. */
145
+ roleRoutes?: Record<string, string[]>;
65
146
  }
66
147
 
67
148
  export interface ChannelResult {
@@ -95,6 +176,8 @@ export interface RouterDeps {
95
176
  /** 롱콘텐츠 spill 주입(테스트/대체). 기본 = spillLongContent(S3 게이트·fail-soft).
96
177
  * 긴 본문을 S3 업로드+링크로 대체 → 전 메신저가 짧은 링크 버전을 받음. */
97
178
  spill?: (text: string) => { text: string; spilled: boolean; url?: string };
179
+ /** Test/host supplied connector registry; built-ins remain the default. */
180
+ adapters?: ReadonlyMap<string, ChannelAdapter>;
98
181
  }
99
182
 
100
183
  /** Parse the sparse `outbound` section. Malformed input → undefined
@@ -109,7 +192,16 @@ export function normalizeOutbound(raw: unknown): OutboundFanoutConfig | undefine
109
192
  if (!entry || typeof entry !== 'object') continue;
110
193
  const ch = entry as Record<string, unknown>;
111
194
  const type = ch.type;
112
- if (type !== 'telegram' && type !== 'discord' && type !== 'pushcut') continue;
195
+ if (type !== 'telegram' && type !== 'discord' && type !== 'pushcut') {
196
+ if (typeof type === 'string') {
197
+ const connector = installedAdapters.get(type);
198
+ try {
199
+ const parsed = connector?.parseConfig?.(ch);
200
+ if (parsed?.type === type) channels.push(parsed);
201
+ } catch { /* invalid plugin config is not a routable channel */ }
202
+ }
203
+ continue;
204
+ }
113
205
  const webhookUrl = typeof ch.webhookUrl === 'string' && ch.webhookUrl.trim()
114
206
  ? ch.webhookUrl.trim() : undefined;
115
207
  const notification = typeof ch.notification === 'string' && ch.notification.trim()
@@ -132,7 +224,18 @@ export function normalizeOutbound(raw: unknown): OutboundFanoutConfig | undefine
132
224
  };
133
225
  const routes = parseRoutes(o.routes);
134
226
  const awayRoutes = parseRoutes(o.awayRoutes);
135
- return { channels, ...(routes ? { routes } : {}), ...(awayRoutes ? { awayRoutes } : {}) };
227
+ const roleRoutes = parseRoutes(o.roleRoutes);
228
+ const parseSelection = (value: unknown): Record<string, string> | undefined => {
229
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
230
+ return Object.fromEntries(Object.entries(value as Record<string, unknown>)
231
+ // Keep a selection even when its channel is not configured: an explicit main/backup must
232
+ // never silently turn back into legacy fan-out (it is reported as not-routed instead).
233
+ .filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].trim().length > 0));
234
+ };
235
+ const primary = parseSelection(o.primary);
236
+ const fallback = parseSelection(o.fallback);
237
+ return { channels, ...(routes ? { routes } : {}), ...(awayRoutes ? { awayRoutes } : {}),
238
+ ...(roleRoutes ? { roleRoutes } : {}), ...(primary ? { primary } : {}), ...(fallback ? { fallback } : {}) };
136
239
  }
137
240
 
138
241
  /** Resolve the channel list for one message kind. No/empty config →
@@ -148,14 +251,14 @@ export function resolveChannels(
148
251
  return outbound.channels.filter(ch => route.includes(ch.type));
149
252
  }
150
253
 
151
- async function deliverTelegram(cfg: UserConfig, msg: OutboundMsg, deps: RouterDeps): Promise<ChannelResult> {
254
+ async function deliverTelegram(_ch: OutboundChannelConfig, msg: OutboundMsg, cfg: UserConfig, deps: RouterDeps): Promise<ChannelResult> {
152
255
  const fmt = formatForChannel('telegram', msg);
153
256
  const send = deps.telegramSend ?? sendTelegramReport;
154
257
  const ok = await send(cfg, fmt.text, { markdown: fmt.markdown ?? msg.markdown, fetchImpl: deps.fetchImpl, kind: msg.kind });
155
258
  return ok ? { type: 'telegram', ok: true } : { type: 'telegram', ok: false, error: 'not-configured' };
156
259
  }
157
260
 
158
- async function deliverDiscord(ch: OutboundChannelConfig, msg: OutboundMsg, deps: RouterDeps): Promise<ChannelResult> {
261
+ async function deliverDiscord(ch: OutboundChannelConfig, msg: OutboundMsg, _cfg: UserConfig, deps: RouterDeps): Promise<ChannelResult> {
159
262
  const fetchImpl = deps.fetchImpl ?? fetch;
160
263
  // webhookUrl guaranteed by normalizeOutbound; guard for direct callers.
161
264
  if (!ch.webhookUrl) return { type: 'discord', ok: false, error: 'missing-webhook-url' };
@@ -172,7 +275,7 @@ async function deliverDiscord(ch: OutboundChannelConfig, msg: OutboundMsg, deps:
172
275
  return { type: 'discord', ok: true };
173
276
  }
174
277
 
175
- async function deliverPushcut(ch: OutboundChannelConfig, msg: OutboundMsg, deps: RouterDeps): Promise<ChannelResult> {
278
+ async function deliverPushcut(ch: OutboundChannelConfig, msg: OutboundMsg, _cfg: UserConfig, deps: RouterDeps): Promise<ChannelResult> {
176
279
  const fmt = formatForChannel('pushcut', msg);
177
280
  const title = fmt.title ?? `elanous ${msg.kind}`;
178
281
  if (ch.webhookUrl) {
@@ -194,6 +297,40 @@ async function deliverPushcut(ch: OutboundChannelConfig, msg: OutboundMsg, deps:
194
297
  return r.ok ? { type: 'pushcut', ok: true } : { type: 'pushcut', ok: false, error: r.reason ?? 'pushcut-failed' };
195
298
  }
196
299
 
300
+ /** Built-in channel connectors. Telegram and Discord receive adapters normalize
301
+ * already-authorized trigger taps; the webhook and Pushcut senders do not poll. */
302
+ export const outboundAdapters: ReadonlyMap<string, ChannelAdapter> = new Map<string, ChannelAdapter>([
303
+ ['telegram', { type: 'telegram', capabilities: {
304
+ send: true, receive: true, thread: false, topic: false, persona: false,
305
+ channelLifecycle: false, permissions: false, commands: false, attachments: false,
306
+ buttons: false, choices: false, deliveryConfirmation: false, rateLimit: false,
307
+ }, send: deliverTelegram, receive: (payload): ChannelEnvelope | null => {
308
+ if (!payload || typeof payload !== 'object') return null;
309
+ const tap = payload as Record<string, unknown>;
310
+ if (typeof tap.body !== 'string' || typeof tap.kind !== 'string' || typeof tap.chat !== 'string') return null;
311
+ return { text: tap.body, kind: tap.kind, channel: tap.chat,
312
+ ...(typeof tap.user === 'string' ? { senderId: tap.user } : {}),
313
+ ...(typeof tap.messageId === 'number' ? { messageId: tap.messageId } : {}) };
314
+ } }],
315
+ ['discord', { type: 'discord', capabilities: {
316
+ send: true, receive: true, thread: false, topic: false, persona: false,
317
+ channelLifecycle: false, permissions: false, commands: false, attachments: false,
318
+ buttons: false, choices: false, deliveryConfirmation: false, rateLimit: false,
319
+ }, send: deliverDiscord, receive: (payload): ChannelEnvelope | null => {
320
+ if (!payload || typeof payload !== 'object') return null;
321
+ const tap = payload as Record<string, unknown>;
322
+ if (typeof tap.body !== 'string' || typeof tap.kind !== 'string' || typeof tap.channel !== 'string') return null;
323
+ return { text: tap.body, kind: tap.kind, channel: tap.channel,
324
+ ...(typeof tap.user === 'string' ? { senderId: tap.user } : {}),
325
+ ...(typeof tap.messageId === 'string' ? { messageId: tap.messageId } : {}) };
326
+ } }],
327
+ ['pushcut', { type: 'pushcut', capabilities: {
328
+ send: true, receive: false, thread: false, topic: false, persona: false,
329
+ channelLifecycle: false, permissions: false, commands: false, attachments: false,
330
+ buttons: false, choices: false, deliveryConfirmation: false, rateLimit: false,
331
+ }, send: deliverPushcut }],
332
+ ]);
333
+
197
334
  /** Fan one message out to every routed channel. Per-channel fail-soft —
198
335
  * a throwing adapter records `{ok:false}` and never blocks siblings.
199
336
  * 구조: 채널별 포맷(format.ts) + 중복 발사 제어·배송 원장(delivery-ledger.ts).
@@ -228,27 +365,75 @@ export async function routeOutbound(cfg: UserConfig, msg: OutboundMsg, deps: Rou
228
365
 
229
366
  // 상태 파일이 없거나 읽기에 실패하면 기존 kind 경로를 그대로 사용한다.
230
367
  let list = resolveChannels(outbound, msg.kind);
368
+ // Whether the route that decided `list` is explicitly empty — that means «suppress», which is
369
+ // different from a route whose channels are simply not configured.
370
+ const kindRoute = outbound ? (outbound.routes?.[msg.kind] ?? outbound.routes?.default) : undefined;
371
+ let explicitlyEmpty = kindRoute !== undefined && kindRoute.length === 0;
372
+ const role = msg.role;
373
+ const roleRoute = role ? outbound?.roleRoutes?.[role] : undefined;
374
+ if (roleRoute && outbound) {
375
+ list = outbound.channels.filter(ch => roleRoute.includes(ch.type));
376
+ explicitlyEmpty = roleRoute.length === 0;
377
+ }
231
378
  if (outbound?.awayRoutes) {
232
379
  try {
233
380
  if (readPresence().away) {
234
381
  const awayRoute = outbound.awayRoutes[msg.kind] ?? outbound.awayRoutes.default;
235
382
  // kind 도 default 도 없으면 기존 경로로 · 명시적인 빈 배열은 기존 routes 와 같은 규칙(채널 0)을 따른다.
236
383
  if (awayRoute !== undefined) {
237
- list = outbound.channels.filter(ch => awayRoute.includes(ch.type));
384
+ // Away mode narrows the route; it never widens a role's allowed channels.
385
+ list = outbound.channels.filter(ch => awayRoute.includes(ch.type) && (!roleRoute || roleRoute.includes(ch.type)));
386
+ // A role's explicitly empty route stays suppressed while away.
387
+ explicitlyEmpty = awayRoute.length === 0 || roleRoute?.length === 0;
238
388
  try { debug.log('outbound.send', 'away-route', { kind: msg.kind, channels: list.map(ch => ch.type) }); } catch { /* 관측 실패는 발송 결과에 영향 없음 */ }
239
389
  }
240
390
  }
241
391
  } catch { /* 상태 조회 실패 — 기존 경로 유지 */ }
242
392
  }
243
- const channels = await Promise.all(list.map(async (ch): Promise<ChannelResult> => {
393
+ const send = async (ch: OutboundChannelConfig): Promise<ChannelResult> => {
244
394
  try {
245
- if (ch.type === 'telegram') return await deliverTelegram(cfg, outMsg, deps);
246
- if (ch.type === 'discord') return await deliverDiscord(ch, outMsg, deps);
247
- return await deliverPushcut(ch, outMsg, deps);
395
+ const adapter = deps.adapters?.get(ch.type) ?? outboundAdapters.get(ch.type) ?? installedAdapters.get(ch.type);
396
+ if (!adapter?.capabilities.send) return { type: ch.type, ok: false, error: 'unsupported-channel' };
397
+ const result = await adapter.send(ch, outMsg, cfg, deps);
398
+ // A connector may not impersonate another channel or echo credentials
399
+ // through the response. Legacy built-in result fields stay unchanged.
400
+ if (!outboundAdapters.has(ch.type)) return { type: ch.type, ok: result.ok,
401
+ ...(result.ok ? {} : { error: 'delivery-failed' }) };
402
+ return { ...result, type: ch.type };
248
403
  } catch (e) {
249
- return { type: ch.type, ok: false, error: e instanceof Error ? e.message : String(e) };
404
+ // Connector exceptions may include secret-bearing URLs or configuration.
405
+ // Keep the old built-in error reasons unless they expose the webhook;
406
+ // a plugin's arbitrary configuration cannot safely be echoed at all.
407
+ const reason = e instanceof Error ? e.message : String(e);
408
+ const plugin = !outboundAdapters.has(ch.type);
409
+ return { type: ch.type, ok: false,
410
+ error: plugin || (ch.webhookUrl && reason.includes(ch.webhookUrl)) ? 'delivery-failed' : reason };
250
411
  }
251
- }));
412
+ };
413
+ const primaryType = (role ? outbound?.primary?.[role] : undefined) ?? outbound?.primary?.[msg.kind] ?? outbound?.primary?.default;
414
+ const fallbackType = (role ? outbound?.fallback?.[role] : undefined) ?? outbound?.fallback?.[msg.kind] ?? outbound?.fallback?.default;
415
+ // Legacy routes still fan out. An explicit primary switches only this kind/role to ordered
416
+ // failover: main, then the configured backup — never the rest of the route. A selected channel
417
+ // that the current route does not carry is recorded as not-routed and the backup is tried.
418
+ // An explicitly empty route stays suppressed (no rows).
419
+ // Only an explicitly empty route suppresses the selection entirely (no rows).
420
+ const suppressed = list.length === 0 && explicitlyEmpty;
421
+ let channels: ChannelResult[];
422
+ if (suppressed) {
423
+ channels = [];
424
+ } else if (primaryType) {
425
+ channels = [];
426
+ for (const type of fallbackType && fallbackType !== primaryType ? [primaryType, fallbackType] : [primaryType]) {
427
+ const ch = list.find(candidate => candidate.type === type);
428
+ const result = ch ? await send(ch) : { type, ok: false, error: 'not-routed' };
429
+ channels.push(result);
430
+ if (result.ok) break;
431
+ }
432
+ } else {
433
+ // A backup without a main has nothing to back up: the allowed route already fans out to every
434
+ // channel it may reach, and a backup outside that route must not bypass role/away limits.
435
+ channels = await Promise.all(list.map(send));
436
+ }
252
437
 
253
438
  // 배송 기록(리드 동기화·중복 제어 토대) — fail-soft.
254
439
  if (ledger) {