elanous 0.2.20 → 0.2.22

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 (456) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/aBi6EyMtLsrl7J8eH4kF7/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/088d8e8a.8069543d082bec12.js +160 -0
  5. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +148 -0
  6. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +32 -0
  7. package/apps/pwa/out/_next/static/chunks/119fc678.6f591cdb84ca5791.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +309 -0
  9. package/apps/pwa/out/_next/static/chunks/1307-08b716476f1d50af.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +122 -0
  11. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +149 -0
  12. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +79 -0
  13. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +24 -0
  15. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +27 -0
  19. package/apps/pwa/out/_next/static/chunks/1948.5f35b39eef222a94.js +5 -0
  20. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +36 -0
  25. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +286 -0
  26. package/apps/pwa/out/_next/static/chunks/{2489-ff55a35b202c0f94.js → 2489-d238d3e2eb5b2bf5.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +94 -0
  34. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3522-1de1319d028ac89c.js +25 -0
  38. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +74 -0
  41. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +34 -0
  45. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +115 -0
  47. package/apps/pwa/out/_next/static/chunks/4588.ad3364cb7fb97ada.js +86 -0
  48. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +77 -0
  50. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +90 -0
  55. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +8 -0
  60. package/apps/pwa/out/_next/static/chunks/{6158.af2e93b7adff709b.js → 6158.566c890adc06a459.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +64 -0
  62. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +287 -0
  64. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +226 -0
  65. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6767.8e6c5f6e744fcd9f.js +38 -0
  67. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/7175-350f4ce2873bc038.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +41 -0
  74. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +24 -0
  75. package/apps/pwa/out/_next/static/chunks/7630.2b9a68659053ff4e.js +193 -0
  76. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/7899.597af4a9996c907f.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/79b78388.663188133449d707.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/8194.484f51e273d9abd2.js +217 -0
  82. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +104 -0
  83. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/88729267.9c6c000157c83f2f.js +62 -0
  85. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +93 -0
  87. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -0
  89. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +62 -0
  90. package/apps/pwa/out/_next/static/chunks/9832.2ab2e818013574c7.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/a9f2b54d-40c16bd0efdb04b8.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-c855c24de7db74fe.js → page-88459e0903da2803.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-85b316bb4235980b.js → page-d85c43f46f58716b.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/app/board/{page-9b2b26a33cb2cd42.js → page-4e645600579ae952.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c9955fde67b1bcb5.js → page-87edbfd19117823b.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/app/chat/{page-4ba4abe096b6c2a8.js → page-cbaf66b1e1804adf.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/app/consult/{page-575d4a8b4f7becb2.js → page-80ed511e02c81412.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/app/control/{page-f5d56ff5f45ad164.js → page-9f5610caaa06178c.js} +1 -1
  101. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-2260e9eef0357421.js → page-ec8f3575e339de74.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/app/exec/{page-466bf0ee16ad1432.js → page-2a19c5cd3b562580.js} +1 -1
  105. package/apps/pwa/out/_next/static/chunks/app/field/{page-a23bc71713bcc08f.js → page-b6a09cde2e07521a.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/app/loops/{page-35bfec2e631d3eae.js → page-abd86cd832aaa011.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-f9ccb1b1adbea14d.js → page-8c3f168fc3f91939.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/app/morning/{page-ec102c9b8b691e37.js → page-786fb4504d0f2fa9.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-db03ae3589a81c9e.js → page-7ec8e289b4c14df6.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-4a6463b97f0ad94f.js → page-8c8ac17d52be2a79.js} +1 -1
  124. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-75bffecc9aa1004e.js → page-d359ab048d63a9d9.js} +1 -1
  125. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/settings/{page-31d5b875760bb62d.js → page-f151cc10ad026940.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/share/{page-46d5201d2207a511.js → page-bf495a1e284f4e00.js} +1 -1
  130. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/term/{page-cf0a4b4abead71ea.js → page-9abcfac07e02a542.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-0acc8e198f7b0dfb.js → page-69cb93d1a2e57e0e.js} +1 -1
  138. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-34882c84f84021c5.js → page-72ec543928d67c3e.js} +1 -1
  139. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/d70879db.c66c56a95a91efc6.js +66 -0
  141. package/apps/pwa/out/_next/static/chunks/f8626309.a4e5d362b1d55e59.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +1 -0
  143. package/apps/pwa/out/_next/static/css/{d3427996da11e480.css → b9d8151e48998417.css} +1 -1
  144. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +1 -0
  145. package/apps/pwa/out/approvals/index.html +1 -1
  146. package/apps/pwa/out/approvals/index.txt +14 -14
  147. package/apps/pwa/out/autopilot/index.html +1 -1
  148. package/apps/pwa/out/autopilot/index.txt +14 -14
  149. package/apps/pwa/out/board/index.html +1 -1
  150. package/apps/pwa/out/board/index.txt +14 -14
  151. package/apps/pwa/out/botlab/index.html +1 -1
  152. package/apps/pwa/out/botlab/index.txt +14 -14
  153. package/apps/pwa/out/bots/index.html +1 -1
  154. package/apps/pwa/out/bots/index.txt +14 -14
  155. package/apps/pwa/out/ceo/index.html +1 -1
  156. package/apps/pwa/out/ceo/index.txt +14 -14
  157. package/apps/pwa/out/chat/index.html +1 -1
  158. package/apps/pwa/out/chat/index.txt +14 -14
  159. package/apps/pwa/out/consult/index.html +1 -1
  160. package/apps/pwa/out/consult/index.txt +14 -14
  161. package/apps/pwa/out/control/index.html +1 -1
  162. package/apps/pwa/out/control/index.txt +14 -14
  163. package/apps/pwa/out/decisions/index.html +1 -1
  164. package/apps/pwa/out/decisions/index.txt +14 -14
  165. package/apps/pwa/out/design-check/index.html +1 -1
  166. package/apps/pwa/out/design-check/index.txt +14 -14
  167. package/apps/pwa/out/editor/index.html +1 -1
  168. package/apps/pwa/out/editor/index.txt +15 -15
  169. package/apps/pwa/out/exec/index.html +1 -1
  170. package/apps/pwa/out/exec/index.txt +14 -14
  171. package/apps/pwa/out/field/index.html +1 -1
  172. package/apps/pwa/out/field/index.txt +14 -14
  173. package/apps/pwa/out/healthz +1 -1
  174. package/apps/pwa/out/index.html +1 -1
  175. package/apps/pwa/out/index.txt +14 -14
  176. package/apps/pwa/out/inside/index.html +1 -1
  177. package/apps/pwa/out/inside/index.txt +15 -15
  178. package/apps/pwa/out/intake/index.html +1 -1
  179. package/apps/pwa/out/intake/index.txt +14 -14
  180. package/apps/pwa/out/live/index.html +1 -1
  181. package/apps/pwa/out/live/index.txt +14 -14
  182. package/apps/pwa/out/live-run/index.html +1 -0
  183. package/apps/pwa/out/live-run/index.txt +35 -0
  184. package/apps/pwa/out/loops/index.html +1 -1
  185. package/apps/pwa/out/loops/index.txt +14 -14
  186. package/apps/pwa/out/market/index.html +1 -1
  187. package/apps/pwa/out/market/index.txt +14 -14
  188. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  189. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  190. package/apps/pwa/out/morning/index.html +1 -1
  191. package/apps/pwa/out/morning/index.txt +14 -14
  192. package/apps/pwa/out/observatory/index.html +1 -1
  193. package/apps/pwa/out/observatory/index.txt +14 -14
  194. package/apps/pwa/out/ops/checklist/index.html +1 -1
  195. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  196. package/apps/pwa/out/ops/release/index.html +1 -1
  197. package/apps/pwa/out/ops/release/index.txt +14 -14
  198. package/apps/pwa/out/ops/seats/index.html +1 -1
  199. package/apps/pwa/out/ops/seats/index.txt +14 -14
  200. package/apps/pwa/out/outputs/index.html +1 -1
  201. package/apps/pwa/out/outputs/index.txt +14 -14
  202. package/apps/pwa/out/reflection/index.html +1 -1
  203. package/apps/pwa/out/reflection/index.txt +15 -15
  204. package/apps/pwa/out/scheduler/index.html +1 -1
  205. package/apps/pwa/out/scheduler/index.txt +14 -14
  206. package/apps/pwa/out/sessions/index.html +1 -1
  207. package/apps/pwa/out/sessions/index.txt +14 -14
  208. package/apps/pwa/out/settings/devices/index.html +1 -1
  209. package/apps/pwa/out/settings/devices/index.txt +14 -14
  210. package/apps/pwa/out/settings/index.html +1 -1
  211. package/apps/pwa/out/settings/index.txt +14 -14
  212. package/apps/pwa/out/setup/done/index.html +1 -1
  213. package/apps/pwa/out/setup/done/index.txt +14 -14
  214. package/apps/pwa/out/setup/index.html +1 -1
  215. package/apps/pwa/out/setup/index.txt +14 -14
  216. package/apps/pwa/out/share/index.html +1 -1
  217. package/apps/pwa/out/share/index.txt +14 -14
  218. package/apps/pwa/out/showroom/index.html +1 -1
  219. package/apps/pwa/out/showroom/index.txt +14 -14
  220. package/apps/pwa/out/tasks/index.html +1 -1
  221. package/apps/pwa/out/tasks/index.txt +14 -14
  222. package/apps/pwa/out/term/index.html +1 -1
  223. package/apps/pwa/out/term/index.txt +14 -14
  224. package/apps/pwa/out/today/index.html +1 -1
  225. package/apps/pwa/out/today/index.txt +14 -14
  226. package/apps/pwa/out/trace/index.html +1 -1
  227. package/apps/pwa/out/trace/index.txt +14 -14
  228. package/apps/pwa/out/vault/index.html +1 -1
  229. package/apps/pwa/out/vault/index.txt +14 -14
  230. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  231. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  232. package/apps/pwa/out/workflows/index.html +1 -1
  233. package/apps/pwa/out/workflows/index.txt +14 -14
  234. package/apps/pwa/out/workspace/index.html +1 -1
  235. package/apps/pwa/out/workspace/index.txt +14 -14
  236. package/apps/pwa/out/worktrees/index.html +1 -1
  237. package/apps/pwa/out/worktrees/index.txt +14 -14
  238. package/graphs/catalog/catalog.yaml +1 -1
  239. package/graphs/drafts/recipes.yaml +6 -6
  240. package/graphs/implement-loop.yaml +277 -5
  241. package/graphs/test-diet/recipes.yaml +3 -0
  242. package/graphs/test-diet/test-diet.yaml +3 -1
  243. package/package.json +1 -1
  244. package/scripts/coord-channel-watch.sh +31 -4
  245. package/scripts/docs-lint.ts +4 -1
  246. package/scripts/f-session-monitor.ts +69 -0
  247. package/scripts/hq/standby-snapshot.ts +7 -3
  248. package/scripts/knowledge-compress.ts +11 -0
  249. package/scripts/marketing/generate-ax-brief.ts +18 -1
  250. package/scripts/measure-nl-routing-corpus.ts +11 -3
  251. package/scripts/nightly-docops.ts +90 -3
  252. package/scripts/release-loop/gate-node.ts +5 -5
  253. package/scripts/release-loop/shard-plan.ts +50 -5
  254. package/scripts/research-coo-admin-dates.ts +39 -0
  255. package/scripts/rhythm/daily.ts +2 -1
  256. package/scripts/skills/public-check.ts +7 -2
  257. package/scripts/smart-merge/resync-shadow.ts +5 -3
  258. package/scripts/test-diet/lib.ts +81 -1
  259. package/scripts/test-diet/node.ts +48 -2
  260. package/scripts/verify-coo-admin-evidence.ts +83 -0
  261. package/src/acp/client.ts +4 -2
  262. package/src/agent/run-context.ts +4 -0
  263. package/src/agent-env/profile.ts +25 -5
  264. package/src/agent-mission/driver.ts +26 -12
  265. package/src/agent-mission/supervisor-metrics.ts +17 -2
  266. package/src/agent-substrate/acp-reviewer.ts +12 -5
  267. package/src/agent-substrate/pr-reviewer.ts +11 -4
  268. package/src/agent-substrate/review-intent.ts +102 -3
  269. package/src/autopilot/build/llm-conflict-merge.ts +22 -8
  270. package/src/autopilot/pr-manager.ts +18 -8
  271. package/src/away/release-watch.ts +47 -8
  272. package/src/briefing/brief-items.ts +104 -11
  273. package/src/browser-cdp/client.ts +10 -5
  274. package/src/chat/index.ts +1 -0
  275. package/src/cli/brief-cli.ts +20 -2
  276. package/src/cli/config-classify.ts +83 -0
  277. package/src/cli/control-cli.ts +14 -0
  278. package/src/cli/decisions-cli.ts +26 -16
  279. package/src/cli/logs-model-input.ts +1 -1
  280. package/src/cli/ops-cli.ts +11 -0
  281. package/src/cli/ops-overview-cli.ts +50 -0
  282. package/src/cli/pod-cli.ts +1 -0
  283. package/src/cli/pr-cli.ts +65 -8
  284. package/src/cli/release-cli.ts +21 -4
  285. package/src/cli/tasks-cli.ts +214 -1
  286. package/src/connectors/types.ts +6 -6
  287. package/src/context-bus/context-now-surfaces.ts +18 -0
  288. package/src/context-bus/coord-events.ts +32 -4
  289. package/src/context-bus/long-term-memory.ts +23 -6
  290. package/src/context-bus/nightly-condense.ts +169 -21
  291. package/src/control/control-status.ts +87 -0
  292. package/src/control-plane/light-member.ts +5 -3
  293. package/src/control-plane/member.ts +172 -7
  294. package/src/core-turn/run-goal-loop.ts +22 -1
  295. package/src/dashboard/index.ts +40 -4
  296. package/src/dashboard/slash-runtime/dashboard-handlers.ts +9 -0
  297. package/src/decisions/decision-cards.ts +20 -1
  298. package/src/decisions/decision-ledger.ts +11 -8
  299. package/src/decisions/web-push-decision-cards.ts +19 -3
  300. package/src/discord-self-message.ts +2 -2
  301. package/src/domains/coo-admin-dates.evidence.json +876 -0
  302. package/src/domains/coo-admin-tool.ts +48 -14
  303. package/src/domains/ops-status.ts +53 -0
  304. package/src/domains/outbound-alert.ts +24 -1
  305. package/src/esc-abort-gate.ts +3 -1
  306. package/src/exec-requests/planner.ts +6 -0
  307. package/src/graph-kinds/registry.ts +6 -6
  308. package/src/graph-runner/graph-cli.ts +91 -2
  309. package/src/graph-runner/graph-run-api.ts +20 -0
  310. package/src/graph-runner/graph-tick.ts +73 -12
  311. package/src/graph-runner/runner.ts +43 -11
  312. package/src/graph-unify/wf-to-graph.ts +79 -0
  313. package/src/graph-wizard/archetypes.ts +88 -0
  314. package/src/graph-wizard/generate.ts +577 -0
  315. package/src/graph-wizard/saved-steps.ts +120 -0
  316. package/src/graph-wizard/steps.ts +206 -0
  317. package/src/harness/harness-cli-command.ts +186 -32
  318. package/src/harness/harness-queue.ts +58 -14
  319. package/src/harness/launch-advice.ts +23 -0
  320. package/src/index.ts +43 -11
  321. package/src/intake-plane/digest.ts +18 -11
  322. package/src/knowledge/conclusion-compress.ts +93 -0
  323. package/src/knowledge/know-find.ts +9 -2
  324. package/src/knowledge/query.ts +20 -0
  325. package/src/llm.ts +26 -16
  326. package/src/loops/neighbor-runtime.ts +88 -0
  327. package/src/loops/orchestrator/author-depth.ts +29 -11
  328. package/src/loops/orchestrator/overlap.ts +7 -1
  329. package/src/loops/orchestrator/resync-shadow.ts +1 -1
  330. package/src/loops/orchestrator/tick.ts +35 -2
  331. package/src/loops/orchestrator/traffic.ts +2 -0
  332. package/src/loops/registry.ts +5 -0
  333. package/src/market/publish.ts +17 -1
  334. package/src/maturity/feature-maturity.ts +4 -2
  335. package/src/mss/logging/redaction.ts +17 -0
  336. package/src/nexus/api/draft-metrics.ts +128 -0
  337. package/src/nexus/api/graph-peer-edit.ts +77 -0
  338. package/src/nexus/api/graph-wizard-api.ts +61 -0
  339. package/src/nexus/api/graphs-api.ts +193 -17
  340. package/src/nexus/api/harness-traversal.ts +205 -0
  341. package/src/nexus/api/http-server.ts +122 -6
  342. package/src/nexus/api/plugins-market.ts +24 -0
  343. package/src/nexus/index.ts +3 -2
  344. package/src/nexus/outbound/router.ts +28 -8
  345. package/src/oauth/claude-pty-maturity.ts +24 -0
  346. package/src/oauth/fallback-chain.ts +27 -14
  347. package/src/onboarding/first-run.ts +3 -0
  348. package/src/ops/overview-collect.ts +345 -0
  349. package/src/ops/overview-seat-era.ts +90 -0
  350. package/src/ops/overview.ts +223 -0
  351. package/src/plugins/maker/market-bundle.ts +9 -0
  352. package/src/plugins/maker/plugin-maker.ts +9 -3
  353. package/src/plugins/maker/wizard-generate.ts +73 -9
  354. package/src/policy/llm-policy.ts +11 -6
  355. package/src/release-loop/checklist-board.ts +27 -0
  356. package/src/release-loop/checklist.ts +38 -0
  357. package/src/release-loop/gate-shards.ts +7 -2
  358. package/src/seat-address/seat-address.ts +1 -0
  359. package/src/seat-loop/seat-loop.ts +174 -20
  360. package/src/seat-loop/seat-report.ts +29 -1
  361. package/src/self-dev/ask-launch-flow.ts +4 -2
  362. package/src/self-dev/ask-launch-io.ts +3 -1
  363. package/src/self-dev/dev-cli.ts +90 -8
  364. package/src/self-dev/dev-pipeline.ts +20 -1
  365. package/src/self-dev/draft-sweep.ts +45 -8
  366. package/src/self-dev/graph-journey-nodes.ts +157 -0
  367. package/src/self-dev/graph-journey-route.ts +105 -0
  368. package/src/self-dev/launch-preflight.ts +43 -3
  369. package/src/self-dev/orchestrate.ts +3 -0
  370. package/src/self-dev/release-path-guard.ts +7 -4
  371. package/src/self-dev/review-provider-fallback.ts +83 -0
  372. package/src/self-dev/run-supervisor.ts +5 -1
  373. package/src/self-implement/frozen-merges.ts +23 -0
  374. package/src/self-implement/goal-author.ts +14 -0
  375. package/src/self-implement/harness-policy-gates.ts +7 -5
  376. package/src/self-implement/heal-intake.ts +3 -0
  377. package/src/self-implement/host-regate.ts +28 -14
  378. package/src/self-implement/l8-merge-queue.ts +80 -8
  379. package/src/self-implement/orchestrator.ts +84 -28
  380. package/src/self-implement/seams.ts +8 -3
  381. package/src/self-implement/self-implement-cli.ts +4 -0
  382. package/src/self-implement/self-implement-runtime.ts +12 -0
  383. package/src/session-runtime/index.ts +5 -3
  384. package/src/shell-env-bootstrap.ts +35 -6
  385. package/src/steward/triage.ts +25 -8
  386. package/src/storage/file-lock.ts +33 -4
  387. package/src/task-agent/card-evidence.ts +6 -0
  388. package/src/task-agent/completion-evidence.ts +31 -3
  389. package/src/task-agent/cover.ts +61 -0
  390. package/src/task-agent/live-moves.ts +126 -3
  391. package/src/task-agent/parent-host.ts +315 -0
  392. package/src/task-agent/shadow.ts +19 -4
  393. package/src/task-agent/task-hand.ts +11 -2
  394. package/src/task-orchestrator/surfaces/pod-command-job.ts +41 -6
  395. package/src/task-orchestrator/surfaces/pod-gate-deps.ts +123 -0
  396. package/src/task-orchestrator/surfaces/pod-pool.ts +138 -15
  397. package/src/task-orchestrator/surfaces/self-implement-pod.ts +39 -6
  398. package/src/task-orchestrator/surfaces/self-implement.ts +7 -0
  399. package/src/telegram-commands.ts +8 -0
  400. package/src/telegram-project-command.ts +6 -0
  401. package/src/telegram.ts +2 -2
  402. package/src/tool-runtime/browser-runtime.ts +87 -3
  403. package/src/tool-runtime/index.ts +1 -0
  404. package/src/typecheck-ratchet.ts +2 -1
  405. package/src/user-config.ts +55 -8
  406. package/src/version/packed-revision.json +1 -1
  407. package/src/workflow-runtime/executor.ts +24 -2
  408. package/src/workflow-runtime/triggers/registry.ts +27 -0
  409. package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
  410. package/apps/pwa/out/_next/static/Bam1YziK2iH9bN4K0utGk/_buildManifest.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/1307-77fad80cf54477c6.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
  416. package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/7748-6e51bfe927019f5c.js +0 -25
  425. package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/a9f2b54d-33c0689b70ed8cf3.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/app/bots/page-7a852956c74015a3.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/app/design-check/page-7c058069c5e7fa63.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/app/inside/page-ed4a6f799f005d09.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/app/intake/page-9cd565005b93aca6.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/live/page-474cb5888ea0e8f3.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/market/page-9ca21f9ad3208a61.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/not-found-a9c2cfa8dda922a5.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-f6055495e9ed7ea8.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/app/setup/page-68793c6e10331d66.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/app/showroom/page-d81c07a7b4f618ae.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/app/tasks/page-be1c4e8697f22360.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/app/trace/page-0fd895fab9b62c1b.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c6e4364d9de8f717.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/webpack-c260571513bf43c2.js +0 -1
  454. package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +0 -1
  455. /package/apps/pwa/out/_next/static/{Bam1YziK2iH9bN4K0utGk → aBi6EyMtLsrl7J8eH4kF7}/_ssgManifest.js +0 -0
  456. /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
@@ -19,10 +19,13 @@ import { setInProcessOutbound } from '../../domains/outbound-alert.js';
19
19
  import { getUserConfig } from '../../user-config.js';
20
20
  import type { TelegramEvent } from '../../workflow-runtime/triggers/telegram-source.js';
21
21
  import { spawnSync } from 'node:child_process';
22
- import { isAbsolute } from 'node:path';
22
+ import { isAbsolute, join, dirname, relative } from 'node:path';
23
+ import { homedir } from 'node:os';
24
+ import { readdirSync, realpathSync, statSync } from 'node:fs';
23
25
  import { createProject, listProjects } from '../../project/project-store.js';
24
26
  import { handleOutputsGet, type OutputsDeps } from './outputs-route.js';
25
27
  import { buildGridData, type GridDeps } from './grid.js';
28
+ import { createDraftMetricsSource, type DraftMetricsSource } from './draft-metrics.js';
26
29
  import { DECISIONS_PATH, handleDecisions, type DecisionsRouteDeps } from './decisions-route.js';
27
30
  import { OPS_BOARD_PATH, handleOpsBoard, type OpsBoardDeps } from './ops-board-route.js';
28
31
  import { CARD_FOLLOWUP_PATH, createCardFollowupJobs, type CardFollowupJobs } from './card-followup-route.js';
@@ -31,6 +34,7 @@ import { compareTokenConstTime } from '../../acp/transport/auth.js';
31
34
  import { ensureAuthToken } from '../../auth/acp-token.js';
32
35
  import type { NexusState } from '../state/state.js';
33
36
  import { DEFAULT_NEXUS_HTTP_PORT } from '../default-port.js';
37
+
34
38
  export { DEFAULT_NEXUS_HTTP_PORT } from '../default-port.js';
35
39
  import type { TabRegistry } from '../state/tab-registry.js';
36
40
  import type { FeedbackEnvelope } from '../../feedback/envelope.js';
@@ -164,6 +168,7 @@ import {
164
168
  } from './vault-api.js';
165
169
  import { handleBuildsGet, parseBuildsPath } from './builds-api.js';
166
170
  import { handleHarnessAskPost, handleHarnessAskStatusGet, handleHarnessRunEventsGet, handleHarnessRunScreenGet, handleHarnessRunsGet, handleHarnessStopPost } from './harness-api.js';
171
+ import { handleHarnessRunTraversalGet } from './harness-traversal.js';
167
172
  import { handleLoopEdgesGet } from './loop-edges.js';
168
173
  import { handleFabricPlans, type FabricPlansRouteOpts } from './fabric-plans.js';
169
174
  import { dispatchPersonaRoute } from './personas.js';
@@ -365,11 +370,12 @@ import {
365
370
  } from './workflows.js';
366
371
  import { handleTriggersSnapshot } from './triggers.js';
367
372
  import { handleWorkflowTemplatesList } from './workflow-templates.js';
368
- import { handleGraphVersionsGet, handleGraphsGet, handleGraphsMutation } from './graphs-api.js';
373
+ import { graphAccessForRequest, handleGraphAccess, handleGraphAccessPut, handleGraphVersionsGet, handleGraphsGet, handleGraphsMutation } from './graphs-api.js';
369
374
  import { handleGraphKindsGet, handleGraphsValidatePost } from './graph-kinds.js';
375
+ import { handleGraphWizardPost, handleGraphWizardPacksGet } from './graph-wizard-api.js';
370
376
  import { syncInstalledPluginNodes } from '../../graph-kinds/installed-plugin-nodes.js';
371
377
  import { elanousStateRoot } from '../../autopilot/state-paths.js';
372
- import { handlePluginsIndexGet, handlePluginsGet, handlePluginsMarketRefresh, handlePluginsInstall, handlePluginsRemove } from './plugins-market.js';
378
+ import { handlePluginsIndexGet, handlePluginsGet, handlePluginsMarketRefresh, handlePluginsInstall, handlePluginsRemove, handlePluginWizardList, handlePluginWizardSave } from './plugins-market.js';
373
379
  import { handlePluginsCredentialsGet, handlePluginsCredentialsPut } from './plugins-credentials-api.js';
374
380
  import {
375
381
  handleWorkflowsList,
@@ -426,6 +432,9 @@ import {
426
432
  parseShowroomLayoutPath,
427
433
  } from './showroom-layouts.js';
428
434
 
435
+ /** Process-wide DRAFT-METRIC cache (one background collection per TTL, shared by every page load). */
436
+ let sharedDraftMetrics: DraftMetricsSource | undefined;
437
+
429
438
  export const NEXUS_HTTP_PORT_RANGE = 16; // try 31415..31430
430
439
  /** Short timeout for the pre-bind localhost occupancy probe. */
431
440
  export const NEXUS_PORT_PROBE_TIMEOUT_MS = 400;
@@ -480,11 +489,17 @@ export type NexusWsBridgeInit = Omit<WsBridgeOpts, 'hostname' | 'port'>;
480
489
  export interface NexusHttpServerOpts {
481
490
  /** Optional fabric-plan ledger and decomposition seam; production uses the instance root and existing fabric decomposer. */
482
491
  fabricPlans?: FabricPlansRouteOpts;
492
+ /** GRAPH-WIZARD 시험 주입(LLM·템플릿 뿌리). 운영은 비워 둔다. */
493
+ graphWizard?: import('../../graph-wizard/generate.js').GraphWizardDeps;
494
+ /** Roots the remote folder picker may browse (default: the daemon user's home). Tests inject a temp root. */
495
+ projectFolderRoots?: string[];
483
496
  /** Optional installer state root for isolated API consumers and tests. */
484
497
  pluginStateRoot?: string;
485
498
  outputs?: OutputsDeps;
486
499
  /** Read-only grid measurement seam; omitted in production for live HQ and Pod pool reads. */
487
500
  grid?: GridDeps;
501
+ /** DRAFT-METRIC cached source seam; omitted in production for the process-wide TTL cache. */
502
+ draftMetrics?: DraftMetricsSource;
488
503
  decisions?: Pick<DecisionsRouteDeps, 'ledger'>;
489
504
  opsBoard?: Pick<OpsBoardDeps, 'openStore'>;
490
505
  cardFollowup?: CardFollowupJobs;
@@ -835,6 +850,55 @@ export function handleProjectsGet(): Response {
835
850
  return jsonResponse({ projects: listProjects() });
836
851
  }
837
852
 
853
+ function isWithinRoot(path: string, root: string): boolean {
854
+ const rel = relative(root, path);
855
+ return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
856
+ }
857
+
858
+ /**
859
+ * Remote folder picker: lists sub-folders under an allow-listed root only.
860
+ * Every path is resolved with realpath first, so `..` segments and symlinks cannot escape the roots;
861
+ * dot-folders are hidden and symlinked entries that point outside the roots are not listed.
862
+ */
863
+ export function handleProjectFoldersGet(req: Request, roots: string[] = [homedir()]): Response {
864
+ const allowed: string[] = [];
865
+ for (const root of roots) {
866
+ try { allowed.push(realpathSync(root)); } catch { /* a missing root grants nothing */ }
867
+ }
868
+ if (allowed.length === 0) return jsonResponse({ error: 'folder_unavailable' }, 404);
869
+ const requested = new URL(req.url).searchParams.get('path') ?? allowed[0]!;
870
+ if (!isAbsolute(requested) || requested.includes('\0')) return jsonResponse({ error: 'invalid_folder' }, 400);
871
+ let path: string;
872
+ try { path = realpathSync(requested); }
873
+ catch {
874
+ debug.log('nexus.projects', 'folders-unavailable', { reason: 'missing' });
875
+ return jsonResponse({ error: 'folder_unavailable' }, 404);
876
+ }
877
+ if (!allowed.some(root => isWithinRoot(path, root))) {
878
+ debug.log('nexus.projects', 'folders-refused', { reason: 'outside-roots' });
879
+ return jsonResponse({ error: 'outside_allowed_roots' }, 403);
880
+ }
881
+ try {
882
+ if (!statSync(path).isDirectory()) return jsonResponse({ error: 'not_directory' }, 400);
883
+ const folders = readdirSync(path, { withFileTypes: true })
884
+ .filter(entry => {
885
+ if (entry.name.startsWith('.')) return false;
886
+ if (entry.isDirectory()) return true;
887
+ if (!entry.isSymbolicLink()) return false;
888
+ try {
889
+ const target = realpathSync(join(path, entry.name));
890
+ return statSync(target).isDirectory() && allowed.some(root => isWithinRoot(target, root));
891
+ } catch { return false; }
892
+ })
893
+ .map(entry => ({ name: entry.name, path: join(path, entry.name) }))
894
+ .sort((a, b) => a.name.localeCompare(b.name));
895
+ const atRoot = allowed.includes(path);
896
+ return jsonResponse({ path, parent: atRoot ? null : dirname(path), folders });
897
+ } catch {
898
+ return jsonResponse({ error: 'folder_unavailable' }, 404);
899
+ }
900
+ }
901
+
838
902
  export async function handleProjectsPost(req: Request): Promise<Response> {
839
903
  let body: unknown;
840
904
  try { body = await req.json(); }
@@ -985,7 +1049,9 @@ export async function routeRequest(
985
1049
  const offered = req.headers.get('authorization');
986
1050
  const fallbackAuthorized = fallbackToken !== undefined && offered?.startsWith('Bearer ') === true
987
1051
  && compareTokenConstTime(offered.slice('Bearer '.length).trim(), fallbackToken);
988
- if (!(opts.metaApi ? checkAuth(req, opts.metaApi) : fallbackAuthorized)) {
1052
+ const graphPeerAuthorized = (method === 'GET' || method === 'PUT')
1053
+ && graphAccessForRequest(pathname, method, offered?.startsWith('Bearer ') ? offered.slice(7).trim() : null);
1054
+ if (!(opts.metaApi ? checkAuth(req, opts.metaApi) : fallbackAuthorized) && !graphPeerAuthorized) {
989
1055
  const name = method === 'POST' && (pathname === '/v1/tasks' || pathname === '/v1/reports/ingest') && opts.metaApi
990
1056
  ? matchIngestAuthorization(req)?.name
991
1057
  : undefined;
@@ -1360,6 +1426,13 @@ export async function routeRequest(
1360
1426
  return handleHarnessRunEventsGet(req, opts.metaApi);
1361
1427
  }
1362
1428
 
1429
+ // HARNESS-RUN-LIVE-GRAPH — one run's ordered node steps ⊕ journey band (PWA /live-run).
1430
+ if (pathname === '/v1/harness/run-traversal' && method === 'GET') {
1431
+ if (!opts.metaApi) return jsonResponse({ error: 'meta-api-runtime-not-wired' }, 503);
1432
+ if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1433
+ return handleHarnessRunTraversalGet(req);
1434
+ }
1435
+
1363
1436
  // Design direction is a write: check owner auth before the handler reads the body.
1364
1437
  // Keep this POST ahead of the mutation fallback, and never make it a public route.
1365
1438
  if (method === 'POST' && pathname === DESIGN_DIRECTION_PATH) {
@@ -1389,11 +1462,27 @@ export async function routeRequest(
1389
1462
  return handleGraphsValidatePost(req, opts.metaApi);
1390
1463
  }
1391
1464
 
1465
+ // GRAPH-WIZARD — 설치된 팩 목록과 말 → 그래프 YAML(저장 안 함). 같은 소유자 인증.
1466
+ if (method === 'GET' && pathname === '/v1/graphs/wizard/packs') {
1467
+ if (!opts.metaApi) return jsonResponse({ error: 'meta-api-not-wired' }, 503);
1468
+ if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1469
+ return handleGraphWizardPacksGet(opts.graphWizard);
1470
+ }
1471
+ if (method === 'POST' && pathname === '/v1/graphs/wizard') {
1472
+ if (!opts.metaApi) return jsonResponse({ error: 'meta-api-not-wired' }, 503);
1473
+ if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1474
+ return handleGraphWizardPost(req, opts.graphWizard);
1475
+ }
1476
+
1392
1477
  if (pathname === '/v1/fabric/decompose' || pathname.startsWith('/v1/fabric/plans/')) {
1393
1478
  if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1394
1479
  return handleFabricPlans(req, opts.fabricPlans);
1395
1480
  }
1396
1481
 
1482
+ if (pathname === '/v1/projects/folders' && method === 'GET') {
1483
+ if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1484
+ return handleProjectFoldersGet(req, opts.projectFolderRoots);
1485
+ }
1397
1486
  if (pathname === '/v1/projects' && (method === 'GET' || method === 'POST')) {
1398
1487
  if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1399
1488
  return method === 'GET' ? handleProjectsGet() : handleProjectsPost(req);
@@ -1437,6 +1526,12 @@ export async function routeRequest(
1437
1526
  if (method !== 'GET') return jsonResponse({ error: 'method-not-allowed', method }, 405);
1438
1527
  return jsonResponse(buildGridData(opts.grid));
1439
1528
  }
1529
+ // DRAFT-METRIC: answers from memory; a stale/cold cache starts one background collection (never per request).
1530
+ if (pathname === '/v1/drafts/metrics') {
1531
+ if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1532
+ if (method !== 'GET') return jsonResponse({ error: 'method-not-allowed', method }, 405);
1533
+ return jsonResponse((opts.draftMetrics ?? (sharedDraftMetrics ??= createDraftMetricsSource())).read());
1534
+ }
1440
1535
  if (method === 'GET' && pathname === '/v1/outputs') {
1441
1536
  if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1442
1537
  return handleOutputsGet(req, opts.outputs);
@@ -1484,6 +1579,10 @@ export async function routeRequest(
1484
1579
  ...opts.decisions,
1485
1580
  });
1486
1581
  }
1582
+ if (method === 'POST' && pathname === '/v1/plugins/wizard') {
1583
+ if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
1584
+ return handlePluginWizardSave(req, opts.metaApi, opts.pluginStateRoot);
1585
+ }
1487
1586
  if (method === 'POST' && pathname === '/v1/plugins/install') {
1488
1587
  if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
1489
1588
  return handlePluginsInstall(req, opts.metaApi, opts.pluginStateRoot);
@@ -1503,10 +1602,19 @@ export async function routeRequest(
1503
1602
  if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
1504
1603
  return handlePluginsRemove(req, pluginRemove[1]!, opts.metaApi, opts.pluginStateRoot);
1505
1604
  }
1605
+ if (method === 'PUT' && /^\/v1\/graphs\/[^/]+\/access$/.test(pathname)) {
1606
+ if (!opts.metaApi || bearerCredential(req, opts.metaApi) === undefined) return jsonResponse({ error: 'unauthorized' }, 401);
1607
+ return handleGraphAccessPut(pathname, req);
1608
+ }
1506
1609
  if ((method === 'PUT' || method === 'POST') && (pathname.startsWith('/v1/graphs/') || pathname === '/v1/graphs')) {
1507
1610
  if (!opts.metaApi) return jsonResponse({ error: 'meta-api-not-wired' }, 503);
1508
- if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1509
- const mutated = await handleGraphsMutation(pathname, req);
1611
+ const peerMayEdit = method === 'PUT' && graphAccessForRequest(pathname, method,
1612
+ req.headers.get('authorization')?.startsWith('Bearer ') ? req.headers.get('authorization')!.slice(7).trim() : null);
1613
+ const ownerAuthorized = checkAuth(req, opts.metaApi);
1614
+ if (!ownerAuthorized && !peerMayEdit) return jsonResponse({ error: 'unauthorized' }, 401);
1615
+ // A peer save is recorded on the graph so every run path refuses it until the owner approves (W9c run guard).
1616
+ const mutated = await handleGraphsMutation(pathname, req, {}, ownerAuthorized ? null
1617
+ : { peerToken: req.headers.get('authorization')!.slice(7).trim() });
1510
1618
  if (mutated) return mutated;
1511
1619
  }
1512
1620
  const cardClose = /^\/v1\/task-cards\/([^/]+)\/close$/.exec(pathname);
@@ -2798,6 +2906,10 @@ export async function routeRequest(
2798
2906
  // surface (`/v1/workflows` · `~/.elanous/workflows-runs/`) covers the same
2799
2907
  // user need.
2800
2908
 
2909
+ if (method === 'GET' && pathname === '/v1/plugins/wizard') {
2910
+ if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
2911
+ return handlePluginWizardList(req, opts.metaApi, opts.pluginStateRoot);
2912
+ }
2801
2913
  const pluginCredentialsGet = /^\/v1\/plugins\/([^/]+)\/credentials$/.exec(pathname);
2802
2914
  if (method === 'GET' && pluginCredentialsGet) {
2803
2915
  if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
@@ -2813,6 +2925,10 @@ export async function routeRequest(
2813
2925
  catch (error) { debug.log('graph.kinds', 'installed-sync-failed', { reason: String(error) }); }
2814
2926
  return handleGraphKindsGet(req, opts.metaApi);
2815
2927
  }
2928
+ if (method === 'GET' && /^\/v1\/graphs\/[^/]+\/access$/.test(pathname)) {
2929
+ if (!opts.metaApi || bearerCredential(req, opts.metaApi) === undefined) return jsonResponse({ error: 'unauthorized' }, 401);
2930
+ return handleGraphAccess(pathname, req);
2931
+ }
2816
2932
  // F-M1 read + P-F1 raw YAML. Writes live in the mutation block and never touch core graphs/.
2817
2933
  if (method === 'GET' && (pathname === '/v1/graphs' || /^\/v1\/graphs\/[^/]+$/.test(pathname) || /^\/v1\/graphs\/[^/]+\/yaml$/.test(pathname))) {
2818
2934
  return handleGraphsGet(pathname);
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import { listWizardDrafts, saveWizardDraft, type WizardResearchDraft } from '../../plugins/maker/wizard-generate.js';
2
3
  import { dirname, join } from 'node:path';
3
4
  import { elanousStateRoot } from '../../autopilot/state-paths.js';
4
5
  import { debug } from '../../debug/log.js';
@@ -120,6 +121,29 @@ export function handlePluginsIndexGet(req: Request, opts: MetaApiOpts, root?: st
120
121
  return jsonResponse(readMarketIndex(root, configuredTrustedKeys(configPath)));
121
122
  }
122
123
 
124
+ export function handlePluginWizardList(req: Request, opts: MetaApiOpts, root = elanousStateRoot()): Response {
125
+ if (!checkAuth(req, opts)) return jsonResponse({ error: 'unauthorized' }, 401);
126
+ return jsonResponse({ plugins: listWizardDrafts(join(root, 'plugins-local')) });
127
+ }
128
+
129
+ export async function handlePluginWizardSave(req: Request, opts: MetaApiOpts, root = elanousStateRoot()): Promise<Response> {
130
+ if (!checkAuth(req, opts)) return jsonResponse({ error: 'unauthorized' }, 401);
131
+ let body: unknown;
132
+ try { body = await req.json(); } catch { return jsonResponse({ error: 'bad_request' }, 400); }
133
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'bad_request' }, 400);
134
+ const input = body as { name?: unknown; draft?: unknown; regenerate?: unknown };
135
+ if (typeof input.name !== 'string' || !/^[a-z0-9][a-z0-9-]{1,31}$/.test(input.name) ||
136
+ !input.draft || typeof input.draft !== 'object' || Array.isArray(input.draft) ||
137
+ (input.regenerate !== undefined && typeof input.regenerate !== 'boolean')) return jsonResponse({ error: 'bad_request' }, 400);
138
+ try {
139
+ saveWizardDraft(join(root, 'plugins-local'), input.name, input.draft as WizardResearchDraft, input.regenerate === true);
140
+ return jsonResponse({ name: input.name, saved: true });
141
+ } catch (error) {
142
+ const reason = error instanceof Error ? error.message : 'invalid draft';
143
+ return jsonResponse({ error: 'invalid_wizard_draft', reason: /already exists|research draft/.test(reason) ? reason : 'invalid draft' }, 422);
144
+ }
145
+ }
146
+
123
147
  export function handlePluginsGet(req: Request, opts: MetaApiOpts, root?: string): Response {
124
148
  if (!checkAuth(req, opts)) return jsonResponse({ error: 'unauthorized' }, 401);
125
149
  return jsonResponse(listInstalledPlugins(root));
@@ -2743,11 +2743,12 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
2743
2743
  if ((getUserConfig().raw?.decisions as { cards?: unknown } | undefined)?.cards !== false && !isAutonomousRunContext()) {
2744
2744
  try {
2745
2745
  const { DecisionCardService } = await import('../decisions/decision-cards.js');
2746
- const { webPushDecisionTransport } = await import('../decisions/web-push-decision-cards.js');
2746
+ const { webPushDecisionTransport, webPushUrgency } = await import('../decisions/web-push-decision-cards.js');
2747
2747
  const { listSubscriptions } = await import('../web-push/subscriptions.js');
2748
2748
  const { DecisionLedger } = await import('../decisions/decision-ledger.js');
2749
2749
  const ledger = new DecisionLedger();
2750
- const service = new DecisionCardService({ transport: webPushDecisionTransport(ledger), ownerIds: [], ledger });
2750
+ const service = new DecisionCardService({ transport: webPushDecisionTransport(ledger), ownerIds: [], ledger,
2751
+ sendFilter: (entry, now) => webPushUrgency(entry, now) !== 'not-urgent', deferredReason: webPushUrgency });
2751
2752
  await service.tick();
2752
2753
  webPushDecisionTimer = setInterval(() => {
2753
2754
  void service.tick().catch((error: unknown) => {
@@ -16,7 +16,8 @@
16
16
  // { "type": "discord", "webhookUrl": "https://discord.com/api/webhooks/..." },
17
17
  // { "type": "pushcut", "webhookUrl": "https://api.pushcut.io/.../notifications/..." }
18
18
  // ],
19
- // "routes": { "alert": ["telegram", "pushcut"], "report": ["telegram", "discord"] }
19
+ // "routes": { "alert": ["telegram", "pushcut"], "report": ["telegram", "discord"] },
20
+ // "awayRoutes": { "alert": ["discord"] }
20
21
  // }
21
22
  //
22
23
  // Backward compat: when `outbound` is absent/malformed the resolved
@@ -27,6 +28,7 @@
27
28
  // that boolean before falling back to direct Telegram).
28
29
 
29
30
  import { createHash } from 'node:crypto';
31
+ import { readPresence } from '../../away/presence.js';
30
32
  import { debug } from '../../debug/log.js';
31
33
  import { sendTelegramReport } from '../../telegram-report.js';
32
34
  import { getPushcutClient } from '../../pushcut/client.js';
@@ -58,6 +60,8 @@ export interface OutboundFanoutConfig {
58
60
  * `routes.default`, then to ALL configured channels. An explicitly
59
61
  * empty route (`"heartbeat": []`) suppresses that kind. */
60
62
  routes?: Record<string, string[]>;
63
+ /** 외출 중 kind → channel types. 미지정 kind는 awayRoutes.default, 이것도 없으면 기존 routes를 유지한다. */
64
+ awayRoutes?: Record<string, string[]>;
61
65
  }
62
66
 
63
67
  export interface ChannelResult {
@@ -118,14 +122,17 @@ export function normalizeOutbound(raw: unknown): OutboundFanoutConfig | undefine
118
122
  channels.push({ type, ...(webhookUrl ? { webhookUrl } : {}), ...(notification ? { notification } : {}) });
119
123
  }
120
124
  if (channels.length === 0) return undefined;
121
- let routes: Record<string, string[]> | undefined;
122
- if (o.routes && typeof o.routes === 'object' && !Array.isArray(o.routes)) {
123
- routes = {};
124
- for (const [kind, v] of Object.entries(o.routes as Record<string, unknown>)) {
125
+ const parseRoutes = (value: unknown): Record<string, string[]> | undefined => {
126
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
127
+ const routes: Record<string, string[]> = {};
128
+ for (const [kind, v] of Object.entries(value as Record<string, unknown>)) {
125
129
  if (Array.isArray(v)) routes[kind] = v.filter((t): t is string => typeof t === 'string');
126
130
  }
127
- }
128
- return { channels, ...(routes ? { routes } : {}) };
131
+ return routes;
132
+ };
133
+ const routes = parseRoutes(o.routes);
134
+ const awayRoutes = parseRoutes(o.awayRoutes);
135
+ return { channels, ...(routes ? { routes } : {}), ...(awayRoutes ? { awayRoutes } : {}) };
129
136
  }
130
137
 
131
138
  /** Resolve the channel list for one message kind. No/empty config →
@@ -219,7 +226,20 @@ export async function routeOutbound(cfg: UserConfig, msg: OutboundMsg, deps: Rou
219
226
  } catch { /* 원장 조회 실패 — 억제 없이 진행 */ }
220
227
  }
221
228
 
222
- const list = resolveChannels(outbound, msg.kind);
229
+ // 상태 파일이 없거나 읽기에 실패하면 기존 kind 경로를 그대로 사용한다.
230
+ let list = resolveChannels(outbound, msg.kind);
231
+ if (outbound?.awayRoutes) {
232
+ try {
233
+ if (readPresence().away) {
234
+ const awayRoute = outbound.awayRoutes[msg.kind] ?? outbound.awayRoutes.default;
235
+ // kind 도 default 도 없으면 기존 경로로 · 명시적인 빈 배열은 기존 routes 와 같은 규칙(채널 0)을 따른다.
236
+ if (awayRoute !== undefined) {
237
+ list = outbound.channels.filter(ch => awayRoute.includes(ch.type));
238
+ try { debug.log('outbound.send', 'away-route', { kind: msg.kind, channels: list.map(ch => ch.type) }); } catch { /* 관측 실패는 발송 결과에 영향 없음 */ }
239
+ }
240
+ }
241
+ } catch { /* 상태 조회 실패 — 기존 경로 유지 */ }
242
+ }
223
243
  const channels = await Promise.all(list.map(async (ch): Promise<ChannelResult> => {
224
244
  try {
225
245
  if (ch.type === 'telegram') return await deliverTelegram(cfg, outMsg, deps);
@@ -0,0 +1,24 @@
1
+ export type ClaudePtyMaturity =
2
+ | { readonly mature: true }
3
+ | { readonly mature: false; readonly why: 'too-few-samples' | 'completion-below-70' | 'unknown-screen-above-10' | 'intervention-above-0.2' | 'sample-unreadable' };
4
+
5
+ export type ClaudePtyMaturitySample = {
6
+ readonly runCount: number;
7
+ readonly completedCount: number;
8
+ readonly unknownScreenCount: number;
9
+ readonly interventionCount: number;
10
+ };
11
+
12
+ /** 최근 런의 계수만 판정한다. 표본을 못 읽거나 계수가 불가능하면 성숙으로 추정하지 않는다. */
13
+ export function judgeClaudePtyMaturity(sample: ClaudePtyMaturitySample | null | undefined): ClaudePtyMaturity {
14
+ if (!sample || ![sample.runCount, sample.completedCount, sample.unknownScreenCount, sample.interventionCount]
15
+ .every((count) => Number.isSafeInteger(count) && count >= 0)
16
+ || sample.completedCount > sample.runCount || sample.unknownScreenCount > sample.runCount) {
17
+ return { mature: false, why: 'sample-unreadable' };
18
+ }
19
+ if (sample.runCount < 10) return { mature: false, why: 'too-few-samples' };
20
+ if (sample.completedCount / sample.runCount < 0.7) return { mature: false, why: 'completion-below-70' };
21
+ if (sample.unknownScreenCount / sample.runCount > 0.1) return { mature: false, why: 'unknown-screen-above-10' };
22
+ if (sample.interventionCount / sample.runCount > 0.2) return { mature: false, why: 'intervention-above-0.2' };
23
+ return { mature: true };
24
+ }
@@ -19,9 +19,10 @@
19
19
  // 지갑을 여는» 일이 없다. (2026-08-13 실측 · RESEARCH-grok-oauth-… §7b)
20
20
 
21
21
  import type { RotationCandidate } from './codex-account-rotation.js';
22
+ import type { ClaudePtyMaturity } from './claude-pty-maturity.js';
22
23
 
23
24
  /** 체인에 놓을 수 있는 칸. ⛔ 모르는 이름은 «조용히 건너뛰지 않고» 거부한다(오타 방어). */
24
- export const FALLBACK_STEPS = ['codex-rotate', 'grok'] as const;
25
+ export const FALLBACK_STEPS = ['codex-rotate', 'grok', 'claude-pty'] as const;
25
26
  export type FallbackStep = (typeof FALLBACK_STEPS)[number];
26
27
 
27
28
  /** 기본 체인 — ⛔⭐ **2026-08-20 에 `grok` 이 «기본»으로 들어왔다**(대표 지시).
@@ -75,8 +76,8 @@ export type RotationOutcome =
75
76
  export type FallbackDecision =
76
77
  /** codex 계정을 갈아탄다(종전 동작). */
77
78
  | { readonly action: 'codex-rotate'; readonly to: RotationCandidate }
78
- /** 백엔드를 통째로 바꾼다. 지금은 grok 뿐. */
79
- | { readonly action: 'switch-backend'; readonly backend: 'grok' }
79
+ /** 백엔드를 통째로 바꾼다. */
80
+ | { readonly action: 'switch-backend'; readonly backend: 'grok' | 'claude' }
80
81
  /** 아무것도 안 한다 — 이유를 «값으로» 남긴다. */
81
82
  | { readonly action: 'stay'; readonly why: StayReason };
82
83
 
@@ -96,7 +97,9 @@ export type StayReason =
96
97
  /** 다음 칸이 grok 인데 grok 자격이 «없다». */
97
98
  | 'grok-unavailable'
98
99
  /** grok 자격은 있는데 «잔량이 소진»됐다 — 자격 부재와 «다른 값»이다. */
99
- | 'grok-exhausted';
100
+ | 'grok-exhausted'
101
+ /** claude PTY 의 최근 표본이 성숙 관문을 통과하지 못했거나 읽히지 않는다. */
102
+ | 'claude-pty-immature';
100
103
 
101
104
  type FallbackRunPosition =
102
105
  | { readonly currentCredentialRateLimited?: false; readonly currentStep?: FallbackStep }
@@ -120,6 +123,8 @@ export type FallbackInput = {
120
123
  * (⇒ 그리고 그 사실은 관측에 값으로 남는다 — 「모르고 갔다」를 셀 수 있다.)
121
124
  */
122
125
  readonly grokQuota?: 'usable' | 'exhausted' | 'unknown';
126
+ /** 최근 claude PTY 표본의 순수 성숙 판정. 생략·못 읽음은 전환 불가. */
127
+ readonly claudePtyMaturity?: ClaudePtyMaturity;
123
128
  /**
124
129
  * `reset-credit-available` 에서 체인을 타지 않고 머문다.
125
130
  * 기본(생략·false)은 `no-candidate` 와 같이 `codex-rotate` 다음 칸을 찾는다.
@@ -148,12 +153,12 @@ export type FallbackInput = {
148
153
  export function decideFallback(input: FallbackInput): FallbackDecision {
149
154
  const {
150
155
  rotation, chain, currentStep, currentCredentialRateLimited = false,
151
- grokAvailable, grokQuota = 'unknown', stayOnResetCreditAvailable = false,
156
+ grokAvailable, grokQuota = 'unknown', claudePtyMaturity, stayOnResetCreditAvailable = false,
152
157
  } = input;
153
158
 
154
159
  if (currentCredentialRateLimited) {
155
160
  if (currentStep === undefined) return { action: 'stay', why: 'chain-exhausted' };
156
- return stepAfter(chain, currentStep, grokAvailable, grokQuota, 'chain-exhausted');
161
+ return stepAfter(chain, currentStep, grokAvailable, grokQuota, claudePtyMaturity, 'chain-exhausted');
157
162
  }
158
163
  if (rotation.reason === 'explicit') return { action: 'stay', why: 'explicit' };
159
164
  if (rotation.reason === 'not-reached' || (rotation.reason === 'credit-pace' && !('to' in rotation))) {
@@ -167,13 +172,13 @@ export function decideFallback(input: FallbackInput): FallbackDecision {
167
172
  // 회전이 답을 냈는데 체인이 codex-rotate 를 «빼» 놨다면 그 뜻을 존중하고
168
173
  // 다음 칸으로 간다(회전을 원치 않는 구성).
169
174
  if (chain.includes('codex-rotate')) return { action: 'codex-rotate', to: rotation.to };
170
- return stepAfterCodex(chain, grokAvailable, grokQuota, 'chain-exhausted');
175
+ return stepAfterCodex(chain, grokAvailable, grokQuota, claudePtyMaturity, 'chain-exhausted');
171
176
  }
172
177
 
173
178
  // no-candidate | disabled | reset-credit-available(기본) — codex 축이 끝났다. 다음 칸을 본다.
174
179
  // ⛔ 다음 칸이 «없을 때»의 이름만 고른다. 진행(stepAfterCodex)은 그대로다.
175
180
  const fallbackWhy = stayReasonWhenCodexAxisEnds(rotation.reason === 'credit-pace' ? 'no-candidate' : rotation.reason);
176
- return stepAfterCodex(chain, grokAvailable, grokQuota, fallbackWhy);
181
+ return stepAfterCodex(chain, grokAvailable, grokQuota, claudePtyMaturity, fallbackWhy);
177
182
  }
178
183
 
179
184
  /** codex 축이 끝났는데 체인 다음 칸도 없을 때 머무는 이유.
@@ -191,9 +196,10 @@ function stepAfterCodex(
191
196
  chain: readonly FallbackStep[],
192
197
  grokAvailable: boolean,
193
198
  grokQuota: 'usable' | 'exhausted' | 'unknown',
199
+ claudePtyMaturity: ClaudePtyMaturity | undefined,
194
200
  whyIfNone: StayReason,
195
201
  ): FallbackDecision {
196
- return stepAfter(chain, 'codex-rotate', grokAvailable, grokQuota, whyIfNone);
202
+ return stepAfter(chain, 'codex-rotate', grokAvailable, grokQuota, claudePtyMaturity, whyIfNone);
197
203
  }
198
204
 
199
205
  function stepAfter(
@@ -201,20 +207,27 @@ function stepAfter(
201
207
  currentStep: FallbackStep | undefined,
202
208
  grokAvailable: boolean,
203
209
  grokQuota: 'usable' | 'exhausted' | 'unknown',
210
+ claudePtyMaturity: ClaudePtyMaturity | undefined,
204
211
  whyIfNone: StayReason,
205
212
  ): FallbackDecision {
206
213
  const idx = currentStep === undefined ? -1 : chain.indexOf(currentStep);
207
214
  const rest = idx === -1 ? chain : chain.slice(idx + 1);
215
+ let unavailableWhy: StayReason | undefined;
208
216
  for (const step of rest) {
209
217
  if (step === 'grok') {
210
- // ⛔ 자격 «먼저» — 자격이 없으면 잔량을 물을 것도 없다(그리고 두 사유는 다른 값이다).
211
- if (!grokAvailable) return { action: 'stay', why: 'grok-unavailable' };
212
- // ⛔ 「모른다」는 통과시킨다 — 못 읽었다고 갈 곳을 없애지 않는다(위 계약 주석).
213
- if (grokQuota === 'exhausted') return { action: 'stay', why: 'grok-exhausted' };
218
+ // 자격 부재와 소진은 다른 값이다. 다음 칸이 있으면 그 칸을 살핀다.
219
+ if (!grokAvailable) { unavailableWhy = 'grok-unavailable'; continue; }
220
+ // 「모른다」는 통과시킨다 — 못 읽었다고 갈 곳을 없애지 않는다.
221
+ if (grokQuota === 'exhausted') { unavailableWhy = 'grok-exhausted'; continue; }
214
222
  return { action: 'switch-backend', backend: 'grok' };
215
223
  }
224
+ if (step === 'claude-pty') {
225
+ // 미성숙·못 읽음은 «건너뛴다» — 뒤에 다른 칸이 있으면 그 칸을 본다(없으면 이 사유로 머문다).
226
+ if (claudePtyMaturity?.mature !== true) { unavailableWhy = 'claude-pty-immature'; continue; }
227
+ return { action: 'switch-backend', backend: 'claude' };
228
+ }
216
229
  }
217
- return { action: 'stay', why: whyIfNone };
230
+ return { action: 'stay', why: unavailableWhy ?? whyIfNone };
218
231
  }
219
232
 
220
233
  /** 사람이 읽는 한 줄 — 관측·CLI 표면용. ⛔ 토큰·계정 홈은 넣지 않는다. */
@@ -1,6 +1,7 @@
1
1
  import * as readline from 'node:readline/promises';
2
2
  import { detectProviders, type DetectedProvider } from '../llm/provider-detect.js';
3
3
  import { debug } from '../debug/log.js';
4
+ import { unattendedNextStepLine } from './entry-hints.js';
4
5
  import { resolveProviderCredential } from '../llm/provider-credentials.js';
5
6
  import {
6
7
  getUserConfig, saveUserConfig, userConfigPath,
@@ -57,6 +58,8 @@ export async function runFirstRun(deps: FirstRunDeps = {}): Promise<{
57
58
  if (!provider) {
58
59
  if ((deps.isTTY ?? process.stdin.isTTY) !== true) {
59
60
  print('eln setup llm 으로 고르세요');
61
+ // FIRST-CHAT-NONTTY — bare `elanous` with no TTY gets the same one-line next step as the other entrances.
62
+ print(unattendedNextStepLine());
60
63
  observe('needs-llm', 0);
61
64
  return { outcome: 'needs-llm', inputs: 0 };
62
65
  }