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
package/src/index.ts CHANGED
@@ -613,9 +613,10 @@ function buildHarnessAskSayDevCliSpec(
613
613
  input: { file: string },
614
614
  cliOpts: ReturnType<typeof harnessAskSayOptionsToDevCliOpts>,
615
615
  entranceId: EntranceId,
616
+ runId: string,
616
617
  ) {
617
618
  return {
618
- ...buildDevCliSpec(input, { kind: 'self' }, cliOpts, Object.keys(cliOpts), entranceId),
619
+ ...buildDevCliSpec(input, { kind: 'self' }, cliOpts, Object.keys(cliOpts), entranceId, undefined, undefined, runId),
619
620
  ...(cliOpts.json ? { humanReadableOutput: false } : {}),
620
621
  };
621
622
  }
@@ -661,6 +662,7 @@ async function runDevAskFromAskFile(askPath: string, opts: HarnessAskSayChildLlm
661
662
  { file: flowResult.goalFile },
662
663
  cliOpts,
663
664
  CLI_HARNESS_ASK_ENTRANCE.id,
665
+ identity.runId,
664
666
  ),
665
667
  ...(pieceFeature === undefined ? {} : { input: { text: pieceFeature } }),
666
668
  ...(base === undefined ? {} : { base }),
@@ -765,6 +767,7 @@ async function runDevAskFromGoalFile(goalPath: string, opts: HarnessAskSayChildL
765
767
  { file: goalPath },
766
768
  cliOpts,
767
769
  CLI_HARNESS_ASK_ENTRANCE.id,
770
+ identity.runId,
768
771
  ),
769
772
  runId: identity.runId,
770
773
  runIdSource: identity.source,
@@ -868,6 +871,7 @@ async function runDevSayFromWords(
868
871
  { file: flowResult.goalFile },
869
872
  cliOpts,
870
873
  entrance,
874
+ identity.runId,
871
875
  ),
872
876
  runId: identity.runId,
873
877
  runIdSource: identity.source,
@@ -1271,9 +1275,10 @@ marketCmd.command('publish').description('Publish a local signed marketplace ind
1271
1275
  .option('--source-sha <sha>', 'Git source commit')
1272
1276
  .option('--source-base <path>', 'Git source plugin base directory')
1273
1277
  .option('--bundle-root <dir>', 'Root that extensions["ai.elanous"].bundle paths resolve against (usually the repository root)')
1278
+ .option('--keep-existing', 'Carry over previous index entries whose name has no folder under --dir (unchanged entry and archive)')
1274
1279
  .option('--json', 'JSON result')
1275
1280
  .action(async (opts: { dir: string; out: string; key: string; keyId: string; marketName: string; displayName: string;
1276
- sourceRepo?: string; sourceSha?: string; sourceBase?: string; bundleRoot?: string; json?: boolean }) => {
1281
+ sourceRepo?: string; sourceSha?: string; sourceBase?: string; bundleRoot?: string; keepExisting?: boolean; json?: boolean }) => {
1277
1282
  try {
1278
1283
  const sourceOptions = [opts.sourceRepo, opts.sourceSha, opts.sourceBase];
1279
1284
  if (sourceOptions.some(Boolean) && !sourceOptions.every(Boolean)) throw new Error('source-repo, source-sha and source-base must be provided together');
@@ -1282,12 +1287,14 @@ marketCmd.command('publish').description('Publish a local signed marketplace ind
1282
1287
  market: { name: opts.marketName, displayName: opts.displayName },
1283
1288
  key: { keyId: opts.keyId, privateKeyPem: readFileSync(opts.key, 'utf8') },
1284
1289
  ...(opts.bundleRoot ? { bundleRoot: opts.bundleRoot } : {}),
1290
+ ...(opts.keepExisting ? { keepExisting: true } : {}),
1285
1291
  ...(opts.sourceRepo && opts.sourceSha && opts.sourceBase
1286
1292
  ? { source: { repoUrl: opts.sourceRepo, sha: opts.sourceSha, basePath: opts.sourceBase } } : {}),
1287
1293
  });
1288
1294
  if (opts.json) await writeStdoutJson(JSON.stringify(result) + '\n');
1289
1295
  else {
1290
- console.log(`Published ${result.published.length} plugins (sequence ${result.sequence}); skipped ${result.skipped.length}`);
1296
+ console.log(`Published ${result.published.length} plugins (sequence ${result.sequence}); skipped ${result.skipped.length}` +
1297
+ (opts.keepExisting ? `; kept ${result.kept.length}` : ''));
1291
1298
  for (const warning of result.warnings) console.warn(`⚠ ${warning.dir}: ${warning.graph} uses recipes it does not ship (${warning.recipes.join(', ')}) — third-party graphs should use their own recipes.yaml or custom nodes`);
1292
1299
  }
1293
1300
  for (const item of result.skipped) {
@@ -3477,8 +3484,10 @@ coordCmd.command('event').command('record')
3477
3484
  .requiredOption('--header <line>', '채널 글 첫 줄')
3478
3485
  .option('--url <url>', '채널 코멘트 URL')
3479
3486
  .action(async (opts: { seat: string; header: string; url?: string }) => {
3480
- const { parseCoordHeader, recordCoordEvent } = await import('./context-bus/coord-events.js');
3481
- recordCoordEvent({ ...parseCoordHeader(opts.seat, opts.header), url: opts.url || null });
3487
+ const { parseCoordHeader, recordCoordEvent, mirrorCoordDecision } = await import('./context-bus/coord-events.js');
3488
+ const input = { ...parseCoordHeader(opts.seat, opts.header), url: opts.url || null, at: new Date().toISOString() };
3489
+ try { recordCoordEvent(input); }
3490
+ finally { mirrorCoordDecision(input); }
3482
3491
  });
3483
3492
  coordCmd.command('events')
3484
3493
  .option('--since <ISO|2h>', '조회 시작', '2h')
@@ -4465,8 +4474,10 @@ selfCmd
4465
4474
  };
4466
4475
  try {
4467
4476
  const { dispatchHeavyCheck } = await import('./self-implement/gate-remote.js');
4468
- const { execFileSync } = await import('node:child_process');
4469
- const repo = execFileSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim();
4477
+ const { runGitCommand } = await import('./git-fs/runner.js');
4478
+ const topLevel = runGitCommand(process.cwd(), ['rev-parse', '--show-toplevel'], { encoding: 'utf8' });
4479
+ if (topLevel.status !== 0) throw new Error(`git rev-parse --show-toplevel failed: ${topLevel.stderr.trim() || `rc=${topLevel.status}`}`);
4480
+ const repo = topLevel.stdout.trim();
4470
4481
  // Only a committed --base range is reproducible on another host: the default mode measures uncommitted files, --pr
4471
4482
  // reads files through gh on this host, and the remote checkout runs from the repository root — those stay local.
4472
4483
  const { realpathSync } = await import('node:fs');
@@ -4891,7 +4902,7 @@ const selfOrchestrateCmd = selfCmd
4891
4902
  // ☸️ 풀이 «원격 노드뿐»이면 이 기계의 이미지는 아무도 안 쓴다 — 로컬 판정·굽기를 건너뛰고 기준 판 = HEAD 로 노드 동기화만 한다.
4892
4903
  // 🩸 2026-09-26 실측: 원격 전용 풀인데 로컬 이미지가 없다고 로컬에서 굽다가 로컬 클러스터(elanous-h1)가 없어 런이 시작도 전에 죽었다.
4893
4904
  const remoteOnlyPool = poolMembers.length > 0 && poolMembers.every((m) => Boolean(m.sshHost));
4894
- const headCommit = remoteOnlyPool ? ((await import('node:child_process')).spawnSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).stdout?.trim() || null) : null;
4905
+ const headCommit = remoteOnlyPool ? ((await import('./git-fs/runner.js')).runGitCommand(process.cwd(), ['rev-parse', 'HEAD'], { encoding: 'utf8' }).stdout.trim() || null) : null;
4895
4906
  let image = remoteOnlyPool
4896
4907
  ? { imageCommit: headCommit, headCommit, fresh: true, reason: 'remote-only pool — 노드 쪽에서 맞춘다' }
4897
4908
  : podImageFreshness();
@@ -4901,7 +4912,8 @@ const selfOrchestrateCmd = selfCmd
4901
4912
  } else {
4902
4913
  if (!opts.json) ui.info(`[pod] 이미지 다시 굽기 — ${image.reason} (~1분)`);
4903
4914
  const { spawnSync } = await import('node:child_process');
4904
- const top = spawnSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).stdout.trim() || process.cwd();
4915
+ const { runGitCommand } = await import('./git-fs/runner.js');
4916
+ const top = runGitCommand(process.cwd(), ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).stdout.trim() || process.cwd();
4905
4917
  // bun 1.4 applies the 1 MiB default maxBuffer: a docker build log past it kills the child (ENOBUFS · status null).
4906
4918
  const b = spawnSync('bash', [`${top}/docker/harness/build.sh`], { encoding: 'utf8', timeout: 900_000, maxBuffer: 64 * 1024 * 1024 });
4907
4919
  if (b.status !== 0) { ui.error(`--substrate pod: 이미지 굽기 실패 rc=${b.status}${b.error ? ` (${(b.error as NodeJS.ErrnoException).code ?? b.error.message})` : ''}: ${(b.stdout + b.stderr).slice(-400)}`); process.exit(2); }
@@ -7314,8 +7326,8 @@ const selfDevCmd = program
7314
7326
  console.error(`[dev] 외부 backend 미션의 자기 명령은 \`elanous agent-mission mission\` 입니다; 대응 명령: elanous agent-mission mission --backend ${executor.backend}`);
7315
7327
  }
7316
7328
  let spec = invokedAsDrive
7317
- ? devCli.buildDriveAliasDevSpec(hasText ? textParts.join(' ') : undefined, devOpts, devCli.explicitDevOptionNames(command))
7318
- : buildDevCliSpec(input, executor, devOpts, devCli.explicitDevOptionNames(command));
7329
+ ? devCli.buildDriveAliasDevSpec(hasText ? textParts.join(' ') : undefined, devOpts, devCli.explicitDevOptionNames(command), devRunId)
7330
+ : buildDevCliSpec(input, executor, devOpts, devCli.explicitDevOptionNames(command), 'cli-dev-ask', undefined, undefined, devRunId);
7319
7331
  if (!opts.json && spec.notice) console.error(spec.notice);
7320
7332
  // 역할별 LLM은 현재 프로세스 설정이다. self-mission 구현 자식에 전달되지 않는 조합은 위 검증이 먼저 거부한다.
7321
7333
  if (Array.isArray(opts.roleLlm) && opts.roleLlm.length > 0) {
@@ -7816,6 +7828,26 @@ configCmd
7816
7828
  if (report.prodStatus !== 'ok') process.exit(1);
7817
7829
  });
7818
7830
 
7831
+ configCmd
7832
+ .command('classify')
7833
+ .description('읽기 전용 — 운영 config 잎의 자리(기본값·기계 사실·비밀·운영 정책)와 덮어쓰기 수 (CFG-CLASSIFY)')
7834
+ .option('--json', '잎 전체를 JSON 으로 출력 (비밀 값 제외)')
7835
+ .action(async (o: { json?: boolean }) => {
7836
+ const { getElanousConfigDir } = await import('./elanous-config-dir.js');
7837
+ const { readUniverseConfig } = await import('./cli/config-drift.js');
7838
+ const { classifyConfig, renderClassify } = await import('./cli/config-classify.js');
7839
+ const { buildUserConfig } = await import('./user-config.js');
7840
+ const prod = readUniverseConfig(getElanousConfigDir(), []);
7841
+ if (prod.status !== 'ok' || !prod.raw) {
7842
+ console.error(`config classify — config 못 읽음(${prod.status}${prod.error ? `: ${prod.error}` : ''})`);
7843
+ process.exitCode = 1;
7844
+ return;
7845
+ }
7846
+ const report = classifyConfig(prod.raw, buildUserConfig('/nonexistent/elanous-config-drift/config.json') as unknown as Record<string, unknown>);
7847
+ if (o.json) await (await import('./cli/stdout-flush.js')).writeStdoutFully(JSON.stringify(report, null, 2));
7848
+ else console.log(renderClassify(report));
7849
+ });
7850
+
7819
7851
  configCmd
7820
7852
  .command('explain <key>')
7821
7853
  .description('읽기 전용 — 운영 값 ⊕ 출처 층 ⊕ 파생 시험 우주별 값 (CFG-STORE1)')
@@ -125,6 +125,19 @@ export function buildIntakeDigest(root: string, day: string, readFile: (p: strin
125
125
  };
126
126
  }
127
127
 
128
+ function digestNoteRef(e: DigestEntry): string {
129
+ return e.noteName ? `[[${e.noteName}]]` : e.note?.trim() || e.url?.trim() || e.id;
130
+ }
131
+
132
+ /** 참고 줄에 이름을 다는 상한 — 많은 날에도 화면 한 장을 지킨다(M 은 그대로 센다). */
133
+ const DIGEST_REFERENCE_NAMED = 10;
134
+
135
+ function digestReferenceLine(entries: DigestEntry[]): string {
136
+ const named = entries.slice(0, DIGEST_REFERENCE_NAMED).map(digestNoteRef).join(', ');
137
+ const rest = entries.length - DIGEST_REFERENCE_NAMED;
138
+ return `참고 ${entries.length}편 — 노트: ${named}${rest > 0 ? ` 외 ${rest}편 · 원장 \`elanous intake items\`` : ''}`;
139
+ }
140
+
128
141
  /** 노트 절 — 축별 묶음 · 노트 링크 · 한 줄 요약 · 골 후보. */
129
142
  export function renderDigestMarkdown(d: IntakeDigest): string {
130
143
  const L: string[] = [`## 📰 오늘의 흡수 요약 (${d.absorbed.length})`, ''];
@@ -165,14 +178,9 @@ export function renderDigestMarkdown(d: IntakeDigest): string {
165
178
 
166
179
  /** 텔레그램 — 흡수 렌즈가 대조한 항목만 SCQA 짧은 판으로 낸다. */
167
180
  export function renderDigestTelegram(d: IntakeDigest, _opts: { vaultRoot?: string; notePath?: string } = {}): string {
168
- if (d.seat && !d.absorbed.length) return '오늘 새 소식 없음';
169
- if (d.seat) return [
170
- `흡수 ${d.absorbed.length}`,
171
- ...d.absorbed.map((item) => `- ${item.oneLiner ?? item.noteName ?? item.id}${item.url ? ` · ${item.url}` : ''}`),
172
- ].join('\n');
173
181
  // «Touching» is decided by the lens verdict alone; a missing note summary only changes how S reads (ACP must-fix).
174
182
  const touching = d.absorbed.filter((e) => e.impact);
175
- const L = [`흡수 ${d.absorbed.length} → 우리에게 닿는 것 ${touching.length}`];
183
+ const L = [`흡수 ${d.absorbed.length}편 → 우리에게 닿는 것 ${touching.length}`];
176
184
  // 관심 뉴스는 최대 셋 — 기사마다 S(요약 첫 줄) · A(엘라누스 함의 첫 줄) · 링크를 한 덩어리로(NEWS-INTAKE).
177
185
  for (const article of (d.news ?? []).slice(0, 3)) {
178
186
  L.push(`📰 ${article.title}`);
@@ -181,9 +189,7 @@ export function renderDigestTelegram(d: IntakeDigest, _opts: { vaultRoot?: strin
181
189
  L.push(article.url);
182
190
  }
183
191
  if (d.shadowSuggestions?.length) L.push(`뉴스 칸 제안 ${d.shadowSuggestions.length}건 (그림자·판 미등록): ${d.shadowSuggestions.slice(0, 3).map((s) => `[${s.verdict}] ${s.fact}${s.url ? ` ${s.url}` : ''}`).join(' · ')}`);
184
- if (!touching.length) return L.join('\n');
185
- // At most three items are shown, each as its own S·C·A·link block so context and source stay paired;
186
- // a later item sharing (verdict, target) says «위와 같은 행동» instead of repeating the A sentence (ACP must-fix).
192
+ // At most three items are shown, each as its own S·C·A·note block so context and source stay paired.
187
193
  // S must be the note's own summary: items without one still count as touching but are not shown (ACP must-fix).
188
194
  const shownItems = touching.filter((e) => e.oneLiner?.trim()).slice(0, 3);
189
195
  const actionSeen = new Set<string>();
@@ -205,10 +211,11 @@ export function renderDigestTelegram(d: IntakeDigest, _opts: { vaultRoot?: strin
205
211
  `S 무엇: ${e.oneLiner?.trim() || '노트 한 줄 요약 없음'}`,
206
212
  `C 우리에게 왜: ${impact.why}`,
207
213
  `A 그래서 무엇을 하나: ${action}`,
208
- `🔗 원문 링크: ${e.url?.trim() || '링크 없음'}`,
214
+ `🔗 노트: ${digestNoteRef(e)}`,
209
215
  );
210
216
  }
211
217
  if (touching.length > shownItems.length) L.push('', `닿는 것 ${touching.length - shownItems.length}건 더(요약 없는 것 포함) · 원장 \`elanous intake items\``);
212
- if (d.absorbed.length > touching.length) L.push('', `그 밖 ${d.absorbed.length - touching.length}건 · 참고`);
218
+ const reference = d.absorbed.filter((e) => !e.impact);
219
+ if (reference.length) L.push('', digestReferenceLine(reference));
213
220
  return L.join('\n');
214
221
  }
@@ -0,0 +1,93 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { releaseLedgerRoot } from '../instance/resolve.js';
5
+ import { listChecklist, setItem } from '../release-loop/checklist.js';
6
+ import { withFileLockSync } from '../storage/file-lock.js';
7
+
8
+ export type ConclusionKind = 'evidence' | 'decision' | 'lesson';
9
+ export type ConclusionSource = 'goal' | 'handoff' | 'run-log' | 'channel' | 'pr-discussion';
10
+ export interface ConclusionRecord {
11
+ source: ConclusionSource;
12
+ cell: string;
13
+ kind: ConclusionKind;
14
+ conclusion: string;
15
+ /** Original URL, or a relative path to the original for non-channel sources. */
16
+ ref: string;
17
+ body: string;
18
+ /** The original post's UTC timestamp; required when compressing a channel day. */
19
+ createdAt?: string;
20
+ /** Terminal status is required before a run can be archived. */
21
+ status?: string;
22
+ }
23
+ export interface ConclusionBatch {
24
+ version: string;
25
+ day: string;
26
+ records: ConclusionRecord[];
27
+ }
28
+ export interface ConclusionReceipt {
29
+ archive: string;
30
+ lines: string[];
31
+ originalBytes: number;
32
+ conclusionBytes: number;
33
+ }
34
+
35
+ const kinds = new Set<ConclusionKind>(['evidence', 'decision', 'lesson']);
36
+ const sources = new Set<ConclusionSource>(['goal', 'handoff', 'run-log', 'channel', 'pr-discussion']);
37
+ const closed = new Set(['done', 'failed', 'cancelled', 'superseded', 'completed', 'abandoned']);
38
+ const validDay = (day: string) => /^\d{4}-\d{2}-\d{2}$/.test(day) && new Date(`${day}T00:00:00Z`).toISOString().slice(0, 10) === day;
39
+
40
+ /** Archive a complete input batch; publish only source-linked, explicitly supplied conclusions to the checklist ledger. */
41
+ export function compressConclusions(batch: ConclusionBatch, root = releaseLedgerRoot()): ConclusionReceipt {
42
+ if (!batch || !Array.isArray(batch.records) || !batch.records.length || !validDay(batch.day)) throw new Error('invalid day or empty records');
43
+ if (!/^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/.test(batch.version)) throw new Error('invalid checklist version');
44
+ if (resolve(root) !== resolve(releaseLedgerRoot())) throw new Error('archive and checklist must share a ledger root');
45
+ const checklist = listChecklist(batch.version);
46
+ const digest = createHash('sha256').update(JSON.stringify(batch)).digest('hex');
47
+ const archiveRef = `knowledge-compress/archive/${digest}.json`;
48
+ const byCell = new Map<string, string[]>();
49
+ for (const [index, record] of batch.records.entries()) {
50
+ if (!record || !sources.has(record.source) || !kinds.has(record.kind) ||
51
+ !checklist.items.some(item => item.id === record.cell) || !record.body?.trim() ||
52
+ !record.conclusion?.trim() || /[\r\n]/.test(record.conclusion) || !record.ref?.trim()) {
53
+ throw new Error('unverified source, conclusion or checklist cell');
54
+ }
55
+ if (record.source === 'channel') {
56
+ if (!/^https:\/\/github\.com\/[^\s]+#issuecomment-\d+$/.test(record.ref) ||
57
+ !record.createdAt || !Number.isFinite(Date.parse(record.createdAt)) ||
58
+ new Date(record.createdAt).toISOString().slice(0, 10) !== batch.day) throw new Error('channel comment must link to the selected UTC day');
59
+ }
60
+ if (record.source === 'run-log' && !closed.has(record.status ?? '')) throw new Error('run is not finished');
61
+ const conclusion = record.conclusion.trim().replace(/\s+/g, ' ');
62
+ if (conclusion.length > 160 || !conclusion) throw new Error('conclusion exceeds one line');
63
+ const ref = record.ref.trim();
64
+ if (/\s|[\r\n]/.test(ref) || (record.source !== 'channel' && !/^(https:\/\/|[^/][^\s]*$)/.test(ref))) throw new Error('invalid source pointer');
65
+ const line = `${record.kind}: ${conclusion} (${record.source === 'channel' ? ref : `${archiveRef}#record-${index + 1}`})`;
66
+ byCell.set(record.cell, [...(byCell.get(record.cell) ?? []), line]);
67
+ }
68
+ const lines = [...byCell].flatMap(([cell, entries]) => [...new Set(entries)].map(line => `${cell} · ${line}`));
69
+ const originalBytes = Buffer.byteLength(JSON.stringify(batch.records));
70
+ const conclusionBytes = Buffer.byteLength(lines.join('\n'));
71
+ if (conclusionBytes * 10 > originalBytes) throw new Error(`conclusions exceed 1/10 of original (${conclusionBytes}/${originalBytes} bytes)`);
72
+
73
+ const dir = join(root, 'knowledge-compress');
74
+ const archive = join(root, archiveRef);
75
+ const lock = join(dir, 'conclusions.lock');
76
+ mkdirSync(dirname(archive), { recursive: true });
77
+ return withFileLockSync(lock, () => {
78
+ if (existsSync(archive)) {
79
+ if (readFileSync(archive, 'utf8') !== JSON.stringify(batch)) throw new Error('archive digest collision');
80
+ } else {
81
+ const temp = `${archive}.${randomUUID()}.tmp`;
82
+ writeFileSync(temp, JSON.stringify(batch), { flag: 'wx', mode: 0o600 });
83
+ renameSync(temp, archive);
84
+ }
85
+ for (const [cell, entries] of byCell) {
86
+ const existing = listChecklist(batch.version).items.find(item => item.id === cell);
87
+ if (!existing) throw new Error(`checklist cell disappeared: ${cell}`);
88
+ const additions = [...new Set(entries)].filter(line => !existing.evidence?.split('\n').includes(line));
89
+ if (additions.length) setItem(batch.version, cell, { evidence: [existing.evidence, ...additions].filter(Boolean).join('\n') }, 'knowledge-compress');
90
+ }
91
+ return { archive: resolve(archive), lines, originalBytes, conclusionBytes };
92
+ });
93
+ }
@@ -3,6 +3,7 @@ import { LessonLedger, type LessonRow } from '../lessons/lesson-ledger.js';
3
3
  import { DecisionLedger, type DecisionEntry, type SeatDecisionRecord } from '../decisions/decision-ledger.js';
4
4
  import { devVersion, listChecklist, type Checklist } from '../release-loop/checklist.js';
5
5
  import { debug } from '../debug/log.js';
6
+ import { queryInstalledPack } from './query.js';
6
7
 
7
8
  /** Directive row fields this search reads — kept local because `src/directives/directive-index` is excluded from the public export. */
8
9
  export interface DirectiveRow { ts: string; text: string; source_file: string; line_no: number }
@@ -14,7 +15,7 @@ function openDirectiveIndex(): { search(q: string): DirectiveRow[]; close(): voi
14
15
  return new mod.DirectiveIndex();
15
16
  }
16
17
 
17
- export type KnowSource = 'directive' | 'decision' | 'checklist' | 'lesson';
18
+ export type KnowSource = 'directive' | 'decision' | 'checklist' | 'lesson' | 'pack';
18
19
  export interface KnowHit {
19
20
  source: KnowSource;
20
21
  id: string;
@@ -36,6 +37,7 @@ export interface KnowFindDeps {
36
37
  lessons: (query: string) => LessonRow[];
37
38
  versions: () => string[];
38
39
  now: () => Date;
40
+ pack?: typeof queryInstalledPack;
39
41
  }
40
42
 
41
43
  function versions(): string[] {
@@ -55,6 +57,7 @@ const defaults: KnowFindDeps = {
55
57
  lessons: (query) => new LessonLedger().find(query),
56
58
  versions,
57
59
  now: () => new Date(),
60
+ pack: queryInstalledPack,
58
61
  };
59
62
 
60
63
  function matches(text: string, words: string[]): boolean {
@@ -62,7 +65,7 @@ function matches(text: string, words: string[]): boolean {
62
65
  return words.every((word) => normalized.includes(word));
63
66
  }
64
67
 
65
- export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowResult {
68
+ export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}, packId?: string): KnowResult {
66
69
  const read = { ...defaults, ...deps };
67
70
  const words = query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
68
71
  if (!words.length) throw new Error('know: 질문이 필요합니다');
@@ -106,6 +109,10 @@ export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowR
106
109
  for (const row of read.lessons(query)) rows.push({ source: 'lesson', id: row.id, title: row.incident,
107
110
  status: row.status, at: row.updated_at, current: row.occurrence_count > 1, ref: `lesson show ${row.id}` });
108
111
  });
112
+ if (packId) attempt('pack', () => {
113
+ for (const card of (read.pack ?? queryInstalledPack)(packId, query)) rows.push({ source: 'pack', id: card.id, title: card.title,
114
+ status: 'installed', at: card.updatedAt, current: true, ref: card.ref });
115
+ });
109
116
  rows.sort((a, b) => Number(b.current) - Number(a.current) || (Date.parse(b.at) || 0) - (Date.parse(a.at) || 0)
110
117
  || a.source.localeCompare(b.source) || a.id.localeCompare(b.id));
111
118
  debug.log('knowledge.know', 'query', { query, counts: Object.fromEntries((['directive', 'decision', 'checklist', 'lesson'] as const)
@@ -10,6 +10,26 @@ import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
10
10
  import { join, relative, sep } from 'node:path';
11
11
  import { parseFrontmatter, type ObsidianVault } from '../auto-research/obsidian-bridge.js';
12
12
  import type { KnowledgeKind, KnowledgeNote, KnowledgeQueryInput, KnowledgeQueryResult } from './types.js';
13
+ import { PACK_KINDS, parsePackIdString, packIdString } from './kgs/pack.js';
14
+ import { kgsStoreSingleton, type KgsSqliteStore } from './kgs/sqlite-store.js';
15
+
16
+ /** Search only cards belonging to a pack present in the local KGS index. No vault fallback. */
17
+ export function listInstalledPacks(store: Pick<KgsSqliteStore, 'listPacksByKind'> = kgsStoreSingleton()): Array<{ id: string; title: string }> {
18
+ return PACK_KINDS.flatMap(kind => store.listPacksByKind(kind).map(pack => ({ id: packIdString(pack.metadata.id), title: pack.metadata.title })));
19
+ }
20
+
21
+ export function queryInstalledPack(packId: string, question: string, store: Pick<KgsSqliteStore, 'readPack'> = kgsStoreSingleton()): Array<{ id: string; title: string; body: string; ref: string; updatedAt: string }> {
22
+ const id = parsePackIdString(packId);
23
+ if (!id) throw new Error(`invalid pack id: ${packId}`);
24
+ const pack = store.readPack(id.slug, id.version);
25
+ if (!pack) throw new Error(`pack not installed: ${packId}`);
26
+ const words = question.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
27
+ return pack.cards.map(card => ({ card, score: words.filter(word => `${card.title} ${card.body}`.toLocaleLowerCase().includes(word)).length }))
28
+ .filter(hit => hit.score > 0 || words.length === 0)
29
+ .sort((a, b) => b.score - a.score || a.card.id.localeCompare(b.card.id))
30
+ .map(({ card }) => ({ id: card.id, title: card.title, body: card.body,
31
+ ref: `${packIdString(pack.metadata.id)}#${card.id}`, updatedAt: card.updatedAt }));
32
+ }
13
33
 
14
34
  const DEFAULT_LIMIT = 20;
15
35
  const MAX_LIMIT = 100;
package/src/llm.ts CHANGED
@@ -1746,7 +1746,7 @@ export const REASONING_CYCLE: readonly import('./user-config.js').ReasoningLevel
1746
1746
 
1747
1747
  /** Does the (provider, model) pair support a "think before answering"
1748
1748
  * reasoning surface? Capability-based: gpt-5 family on Codex /
1749
- * Responses API + Anthropic claude-4 / claude-3.7 (extended thinking)
1749
+ * Responses API + Anthropic Claude 3.7+ (extended thinking)
1750
1750
  * + OpenAI o-series. Other providers / older models return false so
1751
1751
  * the HUD pill auto-hides and the wire body skips the reasoning
1752
1752
  * field — no user toggle required. */
@@ -1768,9 +1768,7 @@ export function modelSupportsReasoning(
1768
1768
  return m.startsWith('o1') || m.startsWith('o3') || m.startsWith('o4');
1769
1769
  }
1770
1770
  if (provider === 'anthropic') {
1771
- // Extended thinking is GA on claude-3.7 onward — anything 4-family
1772
- // (opus-4, sonnet-4, haiku-4) plus the 3.7 line.
1773
- return m.includes('-4-') || m.includes('-3-7') || m.includes('3.7') || m.includes('claude-4');
1771
+ return claudeSupportsThinking(model);
1774
1772
  }
1775
1773
  if (provider === 'gemini') {
1776
1774
  // Gemini 2.5+ family supports thinking budget (thinkingConfig).
@@ -1926,16 +1924,32 @@ function mapReasoningLevelToAnthropicEffort(
1926
1924
  * 🩹 그래서 규칙을 이름 목록이 아니라 «수»로 둔다 — 목록은 새 모델마다 늙지만 세대 비교는 안 늙는다.
1927
1925
  * ⚠️ 알 수 없는 형태는 legacy 로 둔다(무회귀). 판정 못 하면 종전 동작이 정답이다. */
1928
1926
  function usesAdaptiveThinking(model: string | undefined): boolean {
1927
+ const version = claudeVersion(model, true);
1928
+ if (!version) return false;
1929
+ if (version.generation >= 5) return true;
1930
+ return version.generation === 4 && version.minor >= 7;
1931
+ }
1932
+
1933
+ /** Shared numeric version parser for adaptive API shape and extended-thinking support. */
1934
+ function claudeVersion(model: string | undefined, requireTerminal = false): { generation: number; minor: number } | undefined {
1929
1935
  // 끝에 붙는 릴리스 날짜(`-20251101`)는 버전이 아니다 — 떼고 본다.
1930
1936
  const m = (model ?? '').toLowerCase().replace(/-\d{8}$/, '');
1931
- const v = /-(\d+)(?:[-.](\d+))?$/.exec(m);
1932
- if (!v) return false;
1933
- const generation = Number(v[1]);
1934
- const minor = v[2] === undefined ? 0 : Number(v[2]);
1935
- if (generation >= 5) return true;
1936
- return generation === 4 && minor >= 7;
1937
+ for (const v of m.matchAll(/-(\d+)(?:[-.](\d+))?(?=-|$)/g)) {
1938
+ if (requireTerminal && v.index + v[0].length !== m.length) continue;
1939
+ return { generation: Number(v[1]), minor: v[2] === undefined ? 0 : Number(v[2]) };
1940
+ }
1941
+ return undefined;
1937
1942
  }
1938
1943
 
1944
+ function claudeSupportsThinking(model: string | undefined): boolean {
1945
+ // includes, not startsWith: prefixed ids (`us.anthropic.claude-…`, `anthropic/claude-…`) passed the old substring check.
1946
+ if (!model?.toLowerCase().includes('claude-')) return false;
1947
+ const version = claudeVersion(model);
1948
+ return version !== undefined && (version.generation > 3 || version.generation === 3 && version.minor >= 7);
1949
+ }
1950
+
1951
+ export const _claudeSupportsThinkingForContractTest = claudeSupportsThinking;
1952
+
1939
1953
  /** ⛔ 테스트 전용 노출이 아니다 — 모델 계열 계약은 «이름을 가진 판정»이라 회귀를 테스트로 물어야 한다.
1940
1954
  * 이 파일 안 두 조립부(:3102·:9591)가 그 값을 쓰고, 그 계약이 틀리면 provider 가 통째로 막힌다. */
1941
1955
  export const _usesAdaptiveThinkingForContractTest = usesAdaptiveThinking;
@@ -3134,13 +3148,9 @@ async function* streamAnthropicEvents(
3134
3148
  // forcing a single thinking block before all tools). Improves
3135
3149
  // multi-tool turn quality — the model can decide what to call
3136
3150
  // next based on the prior tool's output thinking. Only supported
3137
- // on extended-thinking-capable models (claude-3-7 / 4 family).
3138
- const model: string = (body?.model ?? '') as string;
3139
- const m = model.toLowerCase();
3140
- const supportsThinking =
3141
- m.includes('-4-') || m.includes('-3-7') || m.includes('3.7') || m.includes('claude-4');
3151
+ // on extended-thinking-capable models (Claude 3.7+).
3142
3152
  const betas: string[] = ['fine-grained-tool-streaming-2025-05-14'];
3143
- if (supportsThinking) betas.push('interleaved-thinking-2025-05-14');
3153
+ if (claudeSupportsThinking(body?.model)) betas.push('interleaved-thinking-2025-05-14');
3144
3154
 
3145
3155
  const response = await fetchApiWithRetry(
3146
3156
  ANTHROPIC_API_URL,
@@ -0,0 +1,88 @@
1
+ import { debug } from '../debug/log.js';
2
+ import { openMsgStore } from '../msg/msg-store.js';
3
+ import { judgeNeighbor, type Neighbor } from './neighbors.js';
4
+ import { join } from 'node:path';
5
+
6
+ export type LoopHealth = 'healthy' | 'degraded';
7
+ export type NeighborIncident = { observer: string; neighbor: string; state: 'absent'; reason: string; action: Neighbor['onAbsent']['action']; to: string; at: string; lastSeenAt: string | null };
8
+
9
+ export const SEAT_NEIGHBORS: Record<string, Neighbor[]> = Object.fromEntries(['OP', 'TC', 'MK', 'UX'].map(seat => [seat,
10
+ [
11
+ ...['OP', 'TC', 'MK', 'UX'].filter(peer => peer !== seat).map(peer => ({
12
+ id: peer === 'MK' ? 'cmo-seat' : `${peer.toLowerCase()}-seat`, exchange: ['status'] as Neighbor['exchange'],
13
+ heartbeat: { everyMinutes: 15, missedTicks: 2 },
14
+ onAbsent: peer === 'OP' ? { action: 'delegate' as const, delegateTo: 'steward' }
15
+ : peer === 'TC' ? { action: 'escalate' as const, delegateTo: 'orchestrator' }
16
+ : { action: 'defer' as const, delegateTo: 'orchestrator' },
17
+ })),
18
+ { id: 'orchestrator', exchange: ['status'], heartbeat: { everyMinutes: 120, missedTicks: 2 },
19
+ onAbsent: { action: 'escalate', delegateTo: 'human' } },
20
+ ],
21
+ ]));
22
+
23
+ type Signal = { at: string; health: LoopHealth; ends_at: string | null };
24
+
25
+ function withStore<T>(root: string, fn: (store: ReturnType<typeof openMsgStore>) => T): T {
26
+ const store = openMsgStore(join(root, 'msg', 'messages.db'));
27
+ try {
28
+ store.db.exec(`CREATE TABLE IF NOT EXISTS loop_signals (loop_id TEXT PRIMARY KEY, at TEXT NOT NULL, health TEXT NOT NULL, ends_at TEXT);
29
+ CREATE TABLE IF NOT EXISTS loop_watches (observer TEXT NOT NULL, neighbor TEXT NOT NULL, first_at TEXT NOT NULL, PRIMARY KEY(observer, neighbor));
30
+ CREATE TABLE IF NOT EXISTS loop_incidents (observer TEXT NOT NULL, neighbor TEXT NOT NULL, signal_at TEXT NOT NULL,
31
+ reason TEXT NOT NULL, action TEXT NOT NULL, recipient TEXT NOT NULL, at TEXT NOT NULL,
32
+ PRIMARY KEY(observer, neighbor, signal_at, reason));`);
33
+ return fn(store);
34
+ } finally { store.close(); }
35
+ }
36
+
37
+ /** Durable signal, independent of chat sessions or the seat work-item ledger. */
38
+ export function emitLoopHeartbeat(root: string, loopId: string, health: LoopHealth, now: Date, endsAt?: string): void {
39
+ if (!/^[a-z][a-z0-9-]*$/.test(loopId) || !Number.isFinite(now.getTime())) throw new Error('invalid loop heartbeat');
40
+ if (endsAt && !Number.isFinite(Date.parse(endsAt))) throw new Error('invalid heartbeat end');
41
+ withStore(root, store => {
42
+ store.db.query(`INSERT INTO loop_signals(loop_id, at, health, ends_at) VALUES(?, ?, ?, ?)
43
+ ON CONFLICT(loop_id) DO UPDATE SET at=excluded.at, health=excluded.health, ends_at=excluded.ends_at
44
+ WHERE loop_signals.at <= excluded.at`).run(loopId, now.toISOString(), health, endsAt ?? null);
45
+ });
46
+ debug.log(`loop.${loopId}`, 'heartbeat', { loopId, at: now.toISOString(), health, endsAt: endsAt ?? null });
47
+ }
48
+
49
+ /** First watch starts the missing-signal clock. Action receipt and message commit atomically. */
50
+ export function checkLoopNeighbors(root: string, observer: string, neighbors: readonly Neighbor[], now: Date): NeighborIncident[] {
51
+ return withStore(root, store => {
52
+ const incidents: NeighborIncident[] = [];
53
+ for (const neighbor of neighbors) {
54
+ if (neighbor.id === observer) continue;
55
+ try {
56
+ store.db.exec('BEGIN IMMEDIATE');
57
+ store.db.query('INSERT OR IGNORE INTO loop_watches(observer, neighbor, first_at) VALUES(?, ?, ?)').run(observer, neighbor.id, now.toISOString());
58
+ const signal = store.db.query('SELECT at, health, ends_at FROM loop_signals WHERE loop_id=?').get(neighbor.id) as Signal | null;
59
+ const watch = store.db.query('SELECT first_at FROM loop_watches WHERE observer=? AND neighbor=?').get(observer, neighbor.id) as { first_at: string };
60
+ const judgment = judgeNeighbor({ neighbor, lastSeenAt: signal?.at ?? watch.first_at, now });
61
+ const expired = !!signal?.ends_at && Date.parse(signal.ends_at) <= now.getTime();
62
+ // A declared lease (ends_at in the future) outranks the heartbeat interval: a scheduled loop idle between
63
+ // windows is not absent until its own declared end passes.
64
+ const leased = !!signal?.ends_at && !expired;
65
+ const absent = expired || signal?.health === 'degraded' || (!leased && judgment.state === 'absent');
66
+ if (!absent) { store.db.exec('COMMIT'); continue; }
67
+ const reason = expired ? 'end-expired' : signal?.health === 'degraded' ? 'degraded' : judgment.reason;
68
+ const signalAt = signal?.at ?? watch.first_at;
69
+ const { action, delegateTo: to } = neighbor.onAbsent;
70
+ const inserted = store.db.query(`INSERT OR IGNORE INTO loop_incidents(observer, neighbor, signal_at, reason, action, recipient, at)
71
+ VALUES(?, ?, ?, ?, ?, ?, ?)`).run(observer, neighbor.id, signalAt, reason, action, to, now.toISOString());
72
+ if (inserted.changes) {
73
+ const incident: NeighborIncident = { observer, neighbor: neighbor.id, state: 'absent', reason, action, to,
74
+ at: now.toISOString(), lastSeenAt: signal?.at ?? null };
75
+ store.append({ from: observer, to, kind: `neighbor-${action}`,
76
+ body: `[${observer} → ${to}] ${neighbor.id} ${reason}: ${action}` });
77
+ incidents.push(incident);
78
+ }
79
+ store.db.exec('COMMIT');
80
+ } catch (error) {
81
+ try { store.db.exec('ROLLBACK'); } catch { /* no open transaction */ }
82
+ debug.log('loop.neighbors', 'check-failed', { observer, neighbor: neighbor.id, error: String(error) }, { level: 'warn' });
83
+ }
84
+ }
85
+ for (const incident of incidents) debug.log('loop.neighbors', 'absent', incident, { level: 'warn' });
86
+ return incidents;
87
+ });
88
+ }
@@ -12,7 +12,7 @@ import { getUserConfig, ORCHESTRATOR_DEFAULTS, type OrchestratorSeat } from '../
12
12
  import { checkAuthorInput } from './author-input-check.js';
13
13
  import { AuthorLedger } from './author-ledger.js';
14
14
  import { collectWork, findOverlaps, type WorkItem } from './overlap.js';
15
- import { seatOfTree, trafficTick, TRAFFIC_SEATS } from './traffic.js';
15
+ import { isTrafficLaunch, seatOfTree, trafficTick, TRAFFIC_SEATS } from './traffic.js';
16
16
 
17
17
  export type AuthorCell = Pick<ChecklistItem, 'id' | 'title' | 'owner' | 'status' | 'evidence'> & { version: string };
18
18
  export type AuthorQueueRow = Pick<QueueItem, 'seat' | 'status'> & Partial<Pick<QueueItem, 'input' | 'kind'>>;
@@ -101,8 +101,12 @@ export interface CollectAuthorDepthDeps {
101
101
  versions?: () => readonly [string, string];
102
102
  ledger?: Pick<AuthorLedger, 'request'>;
103
103
  log?: (category: string, event: string, data: Record<string, unknown>) => void;
104
+ listProcesses?: typeof defaultListHarnessProcesses;
104
105
  }
105
106
 
107
+ /** Budget for the shadow's process read (it runs inside the queue lock, whose waiters give up after 30 s). */
108
+ export const AUTHOR_DEPTH_PROCESS_DEADLINE_MS = 10_000;
109
+
106
110
  function defaultVersions(now: Date, root = releaseLedgerRoot()): readonly [string, string] {
107
111
  if (!existsSync(join(root, 'release', 'features.sqlite'))) throw new Error('release schedule ledger unavailable');
108
112
  const upcoming = listSchedules(root).filter(row => row.cutAt && Date.parse(row.cutAt) > now.getTime())
@@ -122,15 +126,6 @@ export function collectAuthorDepth(deps: CollectAuthorDepthDeps = {}): AuthorDep
122
126
  };
123
127
  const cfg = deps.caps && deps.running ? null : observe('config', () => getUserConfig().loops?.orchestrator ?? ORCHESTRATOR_DEFAULTS);
124
128
  const caps = deps.caps ? observe('caps', deps.caps) : cfg === null ? null : observe('caps', () => cfg.seatCaps);
125
- const running = deps.running ? observe('running', deps.running) : cfg === null ? null : observe('running', () => {
126
- const observation = defaultListHarnessProcesses();
127
- if (observation.status !== 'ok') throw new Error(`process observation ${observation.status}`);
128
- const processes = observation.records.map(process => ({ ...process,
129
- seat: seatOfTree(process.cwdStatus === 'unknown' ? undefined : process.cwd, cfg) }));
130
- const measured = trafficTick({ processes, now, caps: cfg.seatCaps, openCells: [] });
131
- if (measured.unassigned) throw new Error(`${measured.unassigned} unassigned harness launch process(es)`);
132
- return Object.fromEntries(measured.seats.map(row => [row.seat, row.running]));
133
- });
134
129
  const queued = observe('queue', deps.queued ?? (() => listHarnessQueue({ root: deps.root })));
135
130
  const releaseRoot = deps.root ?? releaseLedgerRoot();
136
131
  const versions = observe('versions', deps.versions ?? (() => defaultVersions(now, releaseRoot)));
@@ -139,7 +134,30 @@ export function collectAuthorDepth(deps: CollectAuthorDepthDeps = {}): AuthorDep
139
134
  throw new Error('checklist ledger unavailable');
140
135
  return deps.cells?.(version) ?? listChecklist(version, deps.root).items.map(item => ({ ...item, version }));
141
136
  }));
142
- const overlaps = observe('overlap', deps.overlaps ?? (() => {
137
+ // Without release cells there is no candidate to compare, and the host reads below are the expensive ones (every launch's
138
+ // cwd · open PRs over the network) — a fresh --test universe has no release ledger, so it skips them (10-08 tick hang).
139
+ const noCells = cells === null && !deps.cells;
140
+ if (noCells && !deps.running && cfg !== null) unreadable.push({ source: 'running', reason: 'skipped: no release cells to compare' });
141
+ const running = deps.running ? observe('running', deps.running) : cfg === null || noCells ? null : observe('running', () => {
142
+ // Only launch processes are counted, so only they need a cwd; the whole read is bounded because the queue tick runs
143
+ // this shadow inside its lock (10-08: ~490 host processes × lsof kept a --test tick for minutes).
144
+ const started = Date.now();
145
+ const observation = (deps.listProcesses ?? defaultListHarnessProcesses)({ include: record => isTrafficLaunch(record.command),
146
+ cwdOnly: true, deadlineMs: AUTHOR_DEPTH_PROCESS_DEADLINE_MS });
147
+ if (observation.status !== 'ok') throw new Error(`process observation ${observation.status}`);
148
+ const late = observation.records.filter(process => process.cwdFailureReason?.startsWith('observation deadline')).length;
149
+ (deps.log ?? ((category: string, event: string, data: Record<string, unknown>) => debug.log(category, event, data)))(
150
+ 'loop.orchestrator', 'author-depth-processes', { launches: observation.records.length, late, ms: Date.now() - started,
151
+ deadlineMs: AUTHOR_DEPTH_PROCESS_DEADLINE_MS });
152
+ if (late) throw new Error(`process observation deadline ${AUTHOR_DEPTH_PROCESS_DEADLINE_MS}ms: ${late} launch cwd(s) unread`);
153
+ const processes = observation.records.map(process => ({ ...process,
154
+ seat: seatOfTree(process.cwdStatus === 'unknown' ? undefined : process.cwd, cfg) }));
155
+ const measured = trafficTick({ processes, now, caps: cfg.seatCaps, openCells: [] });
156
+ if (measured.unassigned) throw new Error(`${measured.unassigned} unassigned harness launch process(es)`);
157
+ return Object.fromEntries(measured.seats.map(row => [row.seat, row.running]));
158
+ });
159
+ if (noCells && !deps.overlaps) unreadable.push({ source: 'overlap', reason: 'skipped: no release cells to compare' });
160
+ const overlaps = noCells && !deps.overlaps ? null : observe('overlap', deps.overlaps ?? (() => {
143
161
  const observed = collectWork();
144
162
  if (observed.unreadableTrees || observed.unreadableGoals || observed.work.some(item => item.unreadable))
145
163
  throw new Error(`incomplete work observation: trees=${observed.unreadableTrees} goals=${observed.unreadableGoals}`);