elanous 0.2.21 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (780) hide show
  1. package/NOTICE +7 -0
  2. package/README.md +32 -22
  3. package/apps/pwa/out/404/index.html +1 -1
  4. package/apps/pwa/out/404.html +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{119fc678.6f591cdb84ca5791.js → 0673cc54.8e2ef1c23e49e190.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/{a9f2b54d-40c16bd0efdb04b8.js → 0a09e062-34f747a17d8d8c2e.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/{f8626309.a4e5d362b1d55e59.js → 0e3acd34.d42b2ef87fdf4234.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +77 -0
  9. package/apps/pwa/out/_next/static/chunks/{7175-350f4ce2873bc038.js → 1325-dc3f3c21eb181cb4.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +34 -0
  11. package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +24 -0
  12. package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{6767.8e6c5f6e744fcd9f.js → 1505.5826671109111e75.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 1669-da843ed73aa301f5.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +36 -0
  23. package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +122 -0
  24. package/apps/pwa/out/_next/static/chunks/2528.0af5c0e1fad88df6.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +62 -0
  27. package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{7899.597af4a9996c907f.js → 2721.bc9a8f36bd85c07a.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +149 -0
  35. package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +309 -0
  39. package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{d70879db.c66c56a95a91efc6.js → 4c5e95ce.83e9b16add6fcd8c.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/{6158.566c890adc06a459.js → 5313.1697309c532b9856.js} +2 -2
  52. package/apps/pwa/out/_next/static/chunks/{79b78388.663188133449d707.js → 53746282.1be36038d1abd949.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5578.ac963634373b28c6.js +286 -0
  56. package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5967-c0a80f8ffe5d4050.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 5974.360f8441eb0ac1ec.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +74 -0
  64. package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/6571-926ddf024a6ad5df.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +64 -0
  69. package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/{9832.2ab2e818013574c7.js → 6702.ebbd165d5e4a4a14.js} +1 -1
  71. package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +32 -0
  73. package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +24 -0
  74. package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +93 -0
  78. package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 740.90b31555290236a9.js} +2 -2
  83. package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +148 -0
  84. package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +226 -0
  85. package/apps/pwa/out/_next/static/chunks/{9070.067c106528ec1ae6.js → 7518.49161e8f5814abdb.js} +1 -1
  86. package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +27 -0
  87. package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +115 -0
  90. package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7905.b230b9d06cb4ad5d.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{8194.484f51e273d9abd2.js → 8264.00e434fbd02e9311.js} +16 -16
  97. package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +8 -0
  99. package/apps/pwa/out/_next/static/chunks/{088d8e8a.8069543d082bec12.js → 83cfc5c8.349535543c1548b6.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +23 -0
  101. package/apps/pwa/out/_next/static/chunks/{1948.5f35b39eef222a94.js → 8438.ee7f0df3b3f6fcb4.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/{4588.ad3364cb7fb97ada.js → 8532.a86e99acf9b761cb.js} +2 -2
  105. package/apps/pwa/out/_next/static/chunks/8569.b853a8daa8f31d12.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8925-2abfb48af477fb7f.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{88729267.9c6c000157c83f2f.js → 89849432.9eaa2f4c9c010965.js} +3 -3
  110. package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +90 -0
  111. package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +94 -0
  112. package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +41 -0
  114. package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +287 -0
  116. package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +79 -0
  117. package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/{3522-1de1319d028ac89c.js → 9502-32c5dc22e8f120f5.js} +3 -3
  120. package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +104 -0
  123. package/apps/pwa/out/_next/static/chunks/{7630.2b9a68659053ff4e.js → 9976.8f28864317639fe6.js} +16 -16
  124. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ad3bd8084048bee4.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/consult/{page-111cabe4d1fd69ae.js → page-6168a7c43e6ef8f5.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-48fb31810693c357.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cb154acadb025414.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/b4e12462-5fab4a813a63dadd.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → ce383d63.d2ede338788ed18e.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{framework-5c169757551134ed.js → framework-b2763b10dc6b3836.js} +1 -1
  179. package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +1 -0
  184. package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +1 -0
  185. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
  186. package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → rfkMm4movqkqMebBrCXiR}/_buildManifest.js +1 -1
  187. package/apps/pwa/out/approvals/index.html +1 -1
  188. package/apps/pwa/out/approvals/index.txt +23 -23
  189. package/apps/pwa/out/autopilot/index.html +1 -1
  190. package/apps/pwa/out/autopilot/index.txt +23 -23
  191. package/apps/pwa/out/board/index.html +1 -1
  192. package/apps/pwa/out/board/index.txt +23 -23
  193. package/apps/pwa/out/botlab/index.html +1 -1
  194. package/apps/pwa/out/botlab/index.txt +22 -22
  195. package/apps/pwa/out/bots/index.html +1 -1
  196. package/apps/pwa/out/bots/index.txt +22 -22
  197. package/apps/pwa/out/ceo/index.html +1 -1
  198. package/apps/pwa/out/ceo/index.txt +23 -23
  199. package/apps/pwa/out/chat/index.html +1 -1
  200. package/apps/pwa/out/chat/index.txt +22 -22
  201. package/apps/pwa/out/consult/index.html +1 -1
  202. package/apps/pwa/out/consult/index.txt +23 -23
  203. package/apps/pwa/out/control/index.html +1 -1
  204. package/apps/pwa/out/control/index.txt +23 -23
  205. package/apps/pwa/out/decisions/index.html +1 -1
  206. package/apps/pwa/out/decisions/index.txt +22 -22
  207. package/apps/pwa/out/design-check/index.html +1 -1
  208. package/apps/pwa/out/design-check/index.txt +23 -23
  209. package/apps/pwa/out/editor/index.html +1 -1
  210. package/apps/pwa/out/editor/index.txt +24 -23
  211. package/apps/pwa/out/exec/index.html +1 -1
  212. package/apps/pwa/out/exec/index.txt +23 -23
  213. package/apps/pwa/out/field/index.html +1 -1
  214. package/apps/pwa/out/field/index.txt +23 -23
  215. package/apps/pwa/out/healthz +1 -1
  216. package/apps/pwa/out/index.html +1 -1
  217. package/apps/pwa/out/index.txt +22 -22
  218. package/apps/pwa/out/inside/index.html +1 -1
  219. package/apps/pwa/out/inside/index.txt +22 -22
  220. package/apps/pwa/out/intake/index.html +1 -1
  221. package/apps/pwa/out/intake/index.txt +23 -23
  222. package/apps/pwa/out/live/index.html +1 -1
  223. package/apps/pwa/out/live/index.txt +23 -23
  224. package/apps/pwa/out/live-run/index.html +1 -1
  225. package/apps/pwa/out/live-run/index.txt +24 -23
  226. package/apps/pwa/out/loops/index.html +1 -1
  227. package/apps/pwa/out/loops/index.txt +22 -22
  228. package/apps/pwa/out/market/index.html +1 -1
  229. package/apps/pwa/out/market/index.txt +22 -22
  230. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  231. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  232. package/apps/pwa/out/morning/index.html +1 -1
  233. package/apps/pwa/out/morning/index.txt +23 -23
  234. package/apps/pwa/out/observatory/index.html +1 -1
  235. package/apps/pwa/out/observatory/index.txt +23 -23
  236. package/apps/pwa/out/ops/checklist/index.html +1 -1
  237. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  238. package/apps/pwa/out/ops/release/index.html +1 -1
  239. package/apps/pwa/out/ops/release/index.txt +22 -22
  240. package/apps/pwa/out/ops/seats/index.html +1 -1
  241. package/apps/pwa/out/ops/seats/index.txt +22 -22
  242. package/apps/pwa/out/outputs/index.html +1 -1
  243. package/apps/pwa/out/outputs/index.txt +22 -22
  244. package/apps/pwa/out/reflection/index.html +1 -1
  245. package/apps/pwa/out/reflection/index.txt +23 -23
  246. package/apps/pwa/out/scheduler/index.html +1 -1
  247. package/apps/pwa/out/scheduler/index.txt +23 -23
  248. package/apps/pwa/out/sessions/index.html +1 -1
  249. package/apps/pwa/out/sessions/index.txt +22 -22
  250. package/apps/pwa/out/settings/devices/index.html +1 -1
  251. package/apps/pwa/out/settings/devices/index.txt +23 -23
  252. package/apps/pwa/out/settings/index.html +1 -1
  253. package/apps/pwa/out/settings/index.txt +23 -23
  254. package/apps/pwa/out/setup/done/index.html +1 -1
  255. package/apps/pwa/out/setup/done/index.txt +23 -23
  256. package/apps/pwa/out/setup/index.html +1 -1
  257. package/apps/pwa/out/setup/index.txt +23 -23
  258. package/apps/pwa/out/share/index.html +1 -1
  259. package/apps/pwa/out/share/index.txt +23 -23
  260. package/apps/pwa/out/showroom/index.html +1 -1
  261. package/apps/pwa/out/showroom/index.txt +22 -22
  262. package/apps/pwa/out/tasks/index.html +1 -1
  263. package/apps/pwa/out/tasks/index.txt +23 -23
  264. package/apps/pwa/out/term/index.html +1 -1
  265. package/apps/pwa/out/term/index.txt +23 -23
  266. package/apps/pwa/out/today/index.html +1 -1
  267. package/apps/pwa/out/today/index.txt +23 -23
  268. package/apps/pwa/out/trace/index.html +1 -1
  269. package/apps/pwa/out/trace/index.txt +23 -23
  270. package/apps/pwa/out/vault/index.html +1 -1
  271. package/apps/pwa/out/vault/index.txt +23 -23
  272. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  273. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  274. package/apps/pwa/out/workflows/index.html +1 -1
  275. package/apps/pwa/out/workflows/index.txt +23 -23
  276. package/apps/pwa/out/workspace/index.html +1 -1
  277. package/apps/pwa/out/workspace/index.txt +23 -23
  278. package/apps/pwa/out/worktrees/index.html +1 -1
  279. package/apps/pwa/out/worktrees/index.txt +23 -23
  280. package/catalog/models/anthropic/claude-haiku-5-5.yaml +32 -0
  281. package/catalog/models/anthropic/claude-opus-5-5.yaml +5 -0
  282. package/catalog/models/anthropic/claude-sonnet-5-5.yaml +31 -0
  283. package/catalog/models/anthropic/claude-sonnet-5.yaml +8 -2
  284. package/catalog/models/openai/gpt-6-astra.yaml +6 -0
  285. package/catalog/models/openai/gpt-6-luna.yaml +6 -0
  286. package/catalog/models/openai/gpt-6-sol.yaml +6 -0
  287. package/graphs/catalog/catalog.yaml +1 -1
  288. package/graphs/drafts/recipes.yaml +2 -2
  289. package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
  290. package/graphs/log-horizon/recipes.yaml +3 -0
  291. package/graphs/release/recipes.yaml +1 -1
  292. package/graphs/release/release-loop.yaml +6 -2
  293. package/graphs/release-rehearsal/recipes.yaml +12 -0
  294. package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
  295. package/graphs/test-diet/recipes.yaml +3 -0
  296. package/graphs/test-diet/test-diet.yaml +3 -1
  297. package/package.json +4 -1
  298. package/scripts/bench-report.ts +82 -3
  299. package/scripts/ci-typecheck-changed.ts +10 -1
  300. package/scripts/f-session-monitor.ts +69 -0
  301. package/scripts/hq/hq-fence-wrapper.sh +148 -0
  302. package/scripts/hq/standby-snapshot.ts +7 -3
  303. package/scripts/knowledge-compress.ts +11 -0
  304. package/scripts/lib/typecheck-ratchet.ts +51 -0
  305. package/scripts/nightly-docops.ts +90 -3
  306. package/scripts/openai-relay-server.ts +5 -1
  307. package/scripts/release-loop/docs-node.ts +258 -43
  308. package/scripts/release-loop/gate-method.ts +59 -0
  309. package/scripts/release-loop/gate-node.ts +73 -7
  310. package/scripts/release-loop/gate-wall.ts +208 -0
  311. package/scripts/release-loop/known-issues-node.ts +53 -8
  312. package/scripts/release-loop/publish-at.ts +14 -0
  313. package/scripts/release-loop/publish-node.ts +17 -2
  314. package/scripts/release-loop/resume-release.ts +34 -4
  315. package/scripts/release-loop/resume.ts +53 -0
  316. package/scripts/release-loop/shard-plan.ts +3 -11
  317. package/scripts/release-loop/tui-node-checks.ts +22 -0
  318. package/scripts/release-loop/unattended-release.ts +8 -1
  319. package/scripts/research-coo-admin-dates.ts +39 -0
  320. package/scripts/rhythm/daily.ts +51 -2
  321. package/scripts/rhythm/weekly.ts +3 -2
  322. package/scripts/skills/public-check.ts +7 -2
  323. package/scripts/smart-merge/resync-shadow.ts +5 -3
  324. package/scripts/test-diet/lib.ts +81 -1
  325. package/scripts/test-diet/node.ts +48 -2
  326. package/scripts/tsc-main-safety-net.ts +189 -0
  327. package/scripts/usage-rollup.ts +8 -4
  328. package/scripts/verify-coo-admin-evidence.ts +83 -0
  329. package/src/acp/client.ts +4 -2
  330. package/src/acp/codex-plugins.ts +48 -6
  331. package/src/agent/monad-agent-turn.ts +67 -5
  332. package/src/agent/run-context.ts +14 -0
  333. package/src/agent/tool-profile.ts +28 -2
  334. package/src/agent-env/profile.ts +25 -5
  335. package/src/agent-mission/acp-judge.ts +8 -2
  336. package/src/agent-mission/capabilities-cli.ts +16 -1
  337. package/src/agent-mission/driver.ts +27 -13
  338. package/src/agent-mission/pr-review-watch.ts +6 -2
  339. package/src/agent-mission/review-loop.ts +33 -5
  340. package/src/agent-mission/supervisor-metrics.ts +17 -2
  341. package/src/agent-substrate/acp-reviewer.ts +25 -9
  342. package/src/agent-substrate/pr-reviewer.ts +175 -32
  343. package/src/agent-substrate/review-intent.ts +106 -5
  344. package/src/agent-substrate/self-review-cli.ts +111 -16
  345. package/src/agent-substrate/shard-boundary-intent.ts +92 -26
  346. package/src/ask-user-question/pending-questions.ts +8 -2
  347. package/src/autopilot/build/llm-conflict-merge.ts +238 -20
  348. package/src/autopilot/codebase-grounding.ts +44 -0
  349. package/src/autopilot/llm-control-brain.ts +13 -1
  350. package/src/autopilot/mission-codebase-gate.ts +8 -77
  351. package/src/autopilot/pr-manager.ts +30 -11
  352. package/src/away/release-watch.ts +47 -8
  353. package/src/boot/acp-server.ts +4 -7
  354. package/src/boot/acp-socket-path.ts +7 -0
  355. package/src/briefing/brief-items.ts +123 -22
  356. package/src/chat/index.ts +13 -3
  357. package/src/cli/agent-cli.ts +20 -0
  358. package/src/cli/brief-cli.ts +46 -22
  359. package/src/cli/cli-user-error.ts +9 -2
  360. package/src/cli/config-classify.ts +83 -0
  361. package/src/cli/control-cli.ts +14 -0
  362. package/src/cli/decisions-cli.ts +26 -16
  363. package/src/cli/log-event-names.ts +3 -0
  364. package/src/cli/logs-cli.ts +1 -0
  365. package/src/cli/logs-model-input.ts +1 -1
  366. package/src/cli/mcp-login.ts +111 -37
  367. package/src/cli/plugin-cli.ts +21 -1
  368. package/src/cli/pod-cli.ts +26 -0
  369. package/src/cli/pr-cli.ts +94 -9
  370. package/src/cli/pr-land-overlap-evidence.ts +131 -0
  371. package/src/cli/release-cli.ts +74 -15
  372. package/src/cli/self-update.ts +173 -10
  373. package/src/cli/skills-cli.ts +12 -3
  374. package/src/cli/tasks-cli.ts +241 -28
  375. package/src/connectors/types.ts +6 -6
  376. package/src/context-bus/context-first.ts +51 -2
  377. package/src/context-bus/context-now-surfaces.ts +29 -0
  378. package/src/context-bus/coord-events.ts +32 -4
  379. package/src/context-bus/long-term-memory.ts +26 -7
  380. package/src/context-bus/nightly-condense.ts +170 -21
  381. package/src/control/control-status.ts +87 -0
  382. package/src/control-plane/light-member.ts +5 -3
  383. package/src/control-plane/member.ts +172 -7
  384. package/src/core-turn/run-goal-loop.ts +108 -3
  385. package/src/dashboard/chat-fold.ts +20 -0
  386. package/src/dashboard/first-screen-band.ts +151 -1
  387. package/src/dashboard/index.ts +27 -5
  388. package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -3
  389. package/src/dashboard/stale-install-check.ts +13 -2
  390. package/src/dashboard/turn-stream-formatter.ts +25 -0
  391. package/src/debug/log-diet.ts +159 -0
  392. package/src/debug/log.ts +52 -17
  393. package/src/decisions/decision-cards.ts +20 -1
  394. package/src/decisions/decision-ledger.ts +11 -8
  395. package/src/decisions/web-push-decision-cards.ts +19 -3
  396. package/src/discord-self-message.ts +2 -2
  397. package/src/display/coordinator.ts +25 -2
  398. package/src/domains/coo-admin-dates.evidence.json +876 -0
  399. package/src/domains/coo-admin-tool.ts +75 -14
  400. package/src/domains/cron-match.ts +1 -0
  401. package/src/domains/ops-status.ts +53 -0
  402. package/src/domains/outbound-alert.ts +41 -7
  403. package/src/domains/release-tool.ts +119 -10
  404. package/src/domains/schedule-registry.ts +10 -7
  405. package/src/domains/schedule-runner.ts +1 -0
  406. package/src/elanous-config-dir.ts +2 -2
  407. package/src/elanous-daemon.ts +3 -3
  408. package/src/exec-requests/planner.ts +6 -0
  409. package/src/execution-loop/launch-gate.ts +1 -1
  410. package/src/expression/i18n/messages.en.ts +5 -0
  411. package/src/expression/i18n/messages.ja.ts +5 -0
  412. package/src/expression/i18n/messages.ko.ts +5 -0
  413. package/src/expression/i18n/messages.zh.ts +5 -0
  414. package/src/expression/i18n/types.ts +8 -0
  415. package/src/github/pr-labels.ts +1 -0
  416. package/src/graph-kinds/registry.ts +6 -6
  417. package/src/graph-runner/graph-cli.ts +34 -2
  418. package/src/graph-runner/graph-run-api.ts +7 -0
  419. package/src/graph-runner/graph-tick.ts +73 -12
  420. package/src/graph-runner/runner.ts +94 -13
  421. package/src/graph-unify/wf-to-graph.ts +79 -0
  422. package/src/graph-wizard/generate.ts +34 -2
  423. package/src/graph-wizard/steps.ts +13 -0
  424. package/src/harness/dispatch-backpressure.ts +259 -0
  425. package/src/harness/harness-cli-command.ts +227 -50
  426. package/src/harness/harness-queue.ts +58 -14
  427. package/src/harness/harness-seams.ts +2 -20
  428. package/src/harness/harness-substrate-default.ts +72 -1
  429. package/src/harness/launch-advice.ts +23 -0
  430. package/src/harness/nested-elanous-policy.ts +4 -2
  431. package/src/hq/fence-wrapper.ts +7 -0
  432. package/src/hq/hq.ts +33 -2
  433. package/src/hq/lease.ts +13 -0
  434. package/src/hq/seats.ts +114 -18
  435. package/src/index.ts +199 -71
  436. package/src/intake-plane/collect-github.ts +19 -1
  437. package/src/intake-plane/digest.ts +45 -20
  438. package/src/intake-plane/lens.ts +36 -8
  439. package/src/intelligence-map/model-alias.ts +6 -0
  440. package/src/intelligence-map/model-catalog.ts +41 -0
  441. package/src/knowledge/conclusion-compress.ts +93 -0
  442. package/src/knowledge/kgs/pack.ts +5 -2
  443. package/src/knowledge/kgs/sqlite-store.ts +15 -0
  444. package/src/knowledge/know-find.ts +9 -2
  445. package/src/knowledge/query.ts +20 -0
  446. package/src/knowledge/tools/knowledge-query.ts +20 -2
  447. package/src/knowledge/write.ts +234 -2
  448. package/src/llm/bedrock-provider.ts +175 -0
  449. package/src/llm/bedrock.ts +345 -0
  450. package/src/llm/provider-credentials.ts +3 -1
  451. package/src/llm.ts +332 -51
  452. package/src/loops/budget.ts +150 -0
  453. package/src/loops/neighbor-runtime.ts +88 -0
  454. package/src/loops/orchestrator/author-depth.ts +29 -11
  455. package/src/loops/orchestrator/overlap.ts +7 -1
  456. package/src/loops/orchestrator/resync-shadow.ts +1 -1
  457. package/src/loops/orchestrator/seat-share.ts +6 -49
  458. package/src/loops/orchestrator/tick.ts +35 -2
  459. package/src/loops/orchestrator/traffic.ts +6 -80
  460. package/src/loops/registry.ts +5 -0
  461. package/src/market/publish.ts +17 -1
  462. package/src/market/signed-index.ts +42 -0
  463. package/src/maturity/feature-maturity.ts +2 -1
  464. package/src/mcp/mcp-oauth.ts +136 -17
  465. package/src/model-tier/llm-tier-map.ts +48 -4
  466. package/src/models/prompts.ts +22 -1
  467. package/src/mss/logging/horizon-watch.ts +168 -0
  468. package/src/mss/logging/log-store.ts +204 -15
  469. package/src/mss/logging/redaction.ts +17 -0
  470. package/src/native-tool-catalog.ts +1 -1
  471. package/src/nexus/api/consult-requests.ts +7 -10
  472. package/src/nexus/api/discord-trigger-bot.ts +6 -3
  473. package/src/nexus/api/draft-metrics.ts +22 -8
  474. package/src/nexus/api/error-reports.ts +30 -8
  475. package/src/nexus/api/graph-peer-edit.ts +77 -0
  476. package/src/nexus/api/graph-wizard-api.ts +9 -1
  477. package/src/nexus/api/graphs-api.ts +145 -7
  478. package/src/nexus/api/http-server.ts +101 -8
  479. package/src/nexus/api/loop-resources.ts +42 -0
  480. package/src/nexus/api/outbound-report.ts +8 -3
  481. package/src/nexus/api/plugins-market.ts +24 -0
  482. package/src/nexus/api/telegram-trigger-bot.ts +6 -3
  483. package/src/nexus/index.ts +13 -2
  484. package/src/nexus/outbound/format.ts +2 -0
  485. package/src/nexus/outbound/router.ts +198 -13
  486. package/src/oauth/claude-pty-maturity.ts +24 -0
  487. package/src/oauth/codex-account-rotation.ts +16 -18
  488. package/src/oauth/codex-account-store.ts +134 -5
  489. package/src/oauth/fallback-chain.ts +50 -15
  490. package/src/onboarding.ts +2 -1
  491. package/src/plugins/adapters/claude-package.ts +4 -12
  492. package/src/plugins/core/capability-policy.ts +141 -0
  493. package/src/plugins/core/host.ts +21 -8
  494. package/src/plugins/core/manifest.ts +19 -2
  495. package/src/plugins/core/trust-store.ts +15 -0
  496. package/src/plugins/core/types.ts +12 -0
  497. package/src/plugins/install/market-fetch.ts +68 -16
  498. package/src/plugins/install/plugin-install.ts +187 -16
  499. package/src/plugins/maker/market-bundle.ts +9 -0
  500. package/src/plugins/maker/plugin-maker.ts +32 -21
  501. package/src/plugins/maker/wizard-generate.ts +87 -9
  502. package/src/policy/llm-policy.ts +24 -6
  503. package/src/process/pid-liveness.ts +1 -1
  504. package/src/prompt-cache/openai.ts +10 -6
  505. package/src/prompt-cache/openrouter.ts +94 -0
  506. package/src/prompt-enhance/enhance.ts +46 -5
  507. package/src/prompt-library/family-prompt-coverage.ts +1 -0
  508. package/src/prompt-library/universal-preamble.ts +2 -2
  509. package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
  510. package/src/registry/loader.ts +6 -1
  511. package/src/release-loop/checklist-board.ts +27 -0
  512. package/src/release-loop/checklist.ts +62 -9
  513. package/src/release-loop/feature-store.ts +3 -0
  514. package/src/release-loop/gate-shards.ts +48 -4
  515. package/src/release-loop/landed-to-green.ts +81 -0
  516. package/src/release-loop/light-rc.ts +11 -1
  517. package/src/release-loop/merged-pr-checklist.ts +10 -0
  518. package/src/release-loop/placement.ts +91 -22
  519. package/src/release-loop/rehearsal.ts +317 -0
  520. package/src/release-loop/release-schedule.ts +13 -1
  521. package/src/release-loop/rubric-types.ts +1 -0
  522. package/src/release-loop/rubric.ts +2 -2
  523. package/src/retro/daily.ts +23 -9
  524. package/src/seat-address/seat-address.ts +1 -0
  525. package/src/seat-loop/seat-loop.ts +208 -21
  526. package/src/seat-loop/seat-report.ts +29 -1
  527. package/src/seat-loop/traffic-signal.ts +44 -0
  528. package/src/self-dev/ask-launch-flow.ts +2 -2
  529. package/src/self-dev/dev-cli.ts +157 -11
  530. package/src/self-dev/dev-pipeline.ts +18 -1
  531. package/src/self-dev/draft-residue.ts +463 -0
  532. package/src/self-dev/draft-sweep.ts +195 -17
  533. package/src/self-dev/draft-triage-rules.ts +27 -1
  534. package/src/self-dev/launch-preflight-text.ts +549 -0
  535. package/src/self-dev/launch-preflight.ts +10 -545
  536. package/src/self-dev/orchestrate-cli.ts +57 -2
  537. package/src/self-dev/orchestrate-summary-line.ts +13 -0
  538. package/src/self-dev/orchestrate.ts +9 -133
  539. package/src/self-dev/review-provider-fallback.ts +179 -0
  540. package/src/self-dev/run-store.ts +1 -3
  541. package/src/self-dev/run-supervisor.ts +8 -23
  542. package/src/self-dev/run-types.ts +178 -0
  543. package/src/self-dev/shard-aggregate.ts +61 -13
  544. package/src/self-dev/shard-plan.ts +5 -1
  545. package/src/self-dev/shard-run.ts +23 -5
  546. package/src/self-dev/supervisor-verdict-edges.ts +2 -4
  547. package/src/self-implement/blocked-draft-policy.ts +3 -19
  548. package/src/self-implement/draft-on-stop-mode.ts +18 -0
  549. package/src/self-implement/follow-up-goals.ts +114 -38
  550. package/src/self-implement/gate-baseline-reap.ts +157 -0
  551. package/src/self-implement/gate-baseline.ts +78 -16
  552. package/src/self-implement/gate-cli.ts +12 -2
  553. package/src/self-implement/gate-remote.ts +17 -6
  554. package/src/self-implement/goal-author.ts +24 -4
  555. package/src/self-implement/goal-digest.ts +19 -0
  556. package/src/self-implement/graph-overlay-condition.ts +4 -0
  557. package/src/self-implement/harness-policy-gates.ts +7 -5
  558. package/src/self-implement/headless-elanous-driver.ts +11 -9
  559. package/src/self-implement/host-regate.ts +148 -43
  560. package/src/self-implement/l8-merge-queue.ts +147 -13
  561. package/src/self-implement/merge-train.ts +131 -0
  562. package/src/self-implement/off-diff-evidence.ts +9 -0
  563. package/src/self-implement/orchestrator.ts +201 -43
  564. package/src/self-implement/review-finding-recurrence.ts +28 -0
  565. package/src/self-implement/rework-policy.ts +2 -1
  566. package/src/self-implement/round-summary-card.ts +63 -0
  567. package/src/self-implement/run-ledger.ts +4 -3
  568. package/src/self-implement/run-step-projection.ts +147 -0
  569. package/src/self-implement/run-stop.ts +1 -1
  570. package/src/self-implement/seams.ts +41 -6
  571. package/src/self-implement/self-implement-cli.ts +6 -0
  572. package/src/self-implement/train-gate-remote.ts +158 -0
  573. package/src/session-runtime/index.ts +10 -1
  574. package/src/session-runtime/retry-api.ts +11 -1
  575. package/src/shell-env-bootstrap.ts +19 -4
  576. package/src/skills/gift-install.ts +119 -0
  577. package/src/skills/runner.ts +59 -2
  578. package/src/skills/skill-feedback.ts +31 -1
  579. package/src/skills/skill-tier.ts +1 -0
  580. package/src/smart-wizard/capability-index.ts +231 -0
  581. package/src/smart-wizard/rung.ts +74 -0
  582. package/src/steward/launch.ts +20 -9
  583. package/src/steward/triage.ts +29 -10
  584. package/src/storage/file-lock.ts +40 -4
  585. package/src/task-agent/actions.ts +4 -0
  586. package/src/task-agent/card-evidence.ts +59 -13
  587. package/src/task-agent/completion-evidence.ts +31 -3
  588. package/src/task-agent/cover.ts +61 -0
  589. package/src/task-agent/judge.ts +4 -3
  590. package/src/task-agent/land-worktree.ts +201 -0
  591. package/src/task-agent/live-move-history.ts +133 -0
  592. package/src/task-agent/live-moves.ts +222 -12
  593. package/src/task-agent/parent-host.ts +117 -8
  594. package/src/task-agent/rejudge-on-head.ts +297 -0
  595. package/src/task-agent/review-result-capture.ts +218 -0
  596. package/src/task-agent/shadow.ts +118 -7
  597. package/src/task-agent/task-hand.ts +138 -11
  598. package/src/task-orchestrator/surfaces/pod-child-providers.ts +29 -0
  599. package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
  600. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +16 -2
  601. package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
  602. package/src/task-orchestrator/surfaces/pod-lease.ts +93 -12
  603. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +25 -2
  604. package/src/task-orchestrator/surfaces/pod-pool.ts +203 -28
  605. package/src/task-orchestrator/surfaces/self-implement-pod.ts +259 -36
  606. package/src/task-orchestrator/surfaces/self-implement.ts +11 -0
  607. package/src/telegram-commands.ts +8 -0
  608. package/src/telegram-project-command.ts +6 -0
  609. package/src/telegram-service.ts +5 -1
  610. package/src/telegram.ts +17 -4
  611. package/src/time/format.ts +13 -4
  612. package/src/tool-runtime/goal-author-runtime.ts +1 -0
  613. package/src/tool-runtime/self-cognition-catalog.ts +23 -0
  614. package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
  615. package/src/typecheck-ratchet.ts +2 -1
  616. package/src/user-config.ts +167 -28
  617. package/src/ux-sim/slash-menu-frames.ts +168 -0
  618. package/src/version/packed-revision.json +1 -1
  619. package/src/web-terminal/preview-tap-registry.ts +5 -2
  620. package/src/workflow-runtime/executor.ts +24 -2
  621. package/src/workflow-runtime/triggers/registry.ts +27 -0
  622. package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
  623. package/src/workflow-runtime/triggers/scheduler.ts +1 -0
  624. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
  625. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
  626. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
  627. package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
  629. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
  630. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
  631. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
  633. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
  638. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
  647. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
  648. package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
  661. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
  669. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
  674. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
  676. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
  680. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
  684. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
  689. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
  690. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
  692. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
  693. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
  701. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
  704. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
  714. package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
  717. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
  721. package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
  723. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d781be3e3a9cfd74.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/board/page-b0a80997903852d8.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/botlab/page-2b1263165630d9c8.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/chat/page-e2d6bed48ac20120.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/control/page-544dd4bd4847eeb7.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/decisions/page-3ee191319bf97d87.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/exec/page-a449f1ed472b81c2.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/field/page-9045094d6d3b5cd0.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/inside/page-c06673596849ef45.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/layout-9f81233f29dcd409.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/app/morning/page-7a12f61be4a579d1.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/app/not-found-c85b0589591a99c9.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/app/observatory/page-725a4da02dd129eb.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/app/page-21bec4afeb2f0917.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/app/reflection/page-dc39ee7a36d26385.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-84ae759f76053672.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/app/sessions/page-3db58ddb61fe2be9.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c5315e30f3528dc4.js +0 -1
  758. package/apps/pwa/out/_next/static/chunks/app/settings/page-2fe368efb6de3b6a.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +0 -1
  760. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/app/share/page-c69762987b751174.js +0 -1
  763. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +0 -1
  764. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +0 -1
  765. package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +0 -1
  766. package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +0 -1
  767. package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +0 -1
  768. package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +0 -1
  769. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +0 -1
  770. package/apps/pwa/out/_next/static/chunks/app/workflows/page-7f8d7fcdf058a845.js +0 -1
  771. package/apps/pwa/out/_next/static/chunks/app/workspace/page-22163556cd38bc88.js +0 -1
  772. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +0 -1
  773. package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
  774. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
  775. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
  776. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
  777. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
  778. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
  779. package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +0 -1
  780. /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → rfkMm4movqkqMebBrCXiR}/_ssgManifest.js +0 -0
@@ -1,4 +1,61 @@
1
1
  import { ORCHESTRATOR_DEFAULTS, type OrchestratorSeat } from '../user-config.js';
2
+ import type { ChecklistItem } from '../release-loop/checklist.js';
3
+ import type { TrafficCell, TrafficProcess, TrafficResult, TrafficSeatRow } from './orchestrator/traffic.js';
4
+
5
+ const RESOURCE_SEATS: readonly OrchestratorSeat[] = ['OP', 'TC', 'MK', 'UX'];
6
+
7
+ type WorkCell = Pick<ChecklistItem, 'owner' | 'status'>;
8
+
9
+ /** Allocate whole base slots by remaining work; this calculation does not decide launches. */
10
+ export function calculateSeatBaseShares({ totalSlots, currentRound, nextRound, seatCaps }: {
11
+ totalSlots: number;
12
+ currentRound: readonly WorkCell[];
13
+ nextRound: readonly WorkCell[];
14
+ seatCaps: Readonly<Record<OrchestratorSeat, number>>;
15
+ }): Record<OrchestratorSeat, number> {
16
+ if (!Number.isSafeInteger(totalSlots) || totalSlots < 0 ||
17
+ RESOURCE_SEATS.some(seat => !Number.isSafeInteger(seatCaps[seat]) || seatCaps[seat] < 0)) {
18
+ throw new RangeError('totalSlots and seatCaps must be nonnegative safe integers');
19
+ }
20
+ const shares: Record<OrchestratorSeat, number> = { OP: 0, TC: 0, MK: 0, UX: 0 };
21
+ const weights: Record<OrchestratorSeat, number> = { OP: 0, TC: 0, MK: 0, UX: 0 };
22
+ for (const [cells, weight] of [[currentRound, 2], [nextRound, 1]] as const) {
23
+ for (const cell of cells) {
24
+ if (cell.status !== 'yellow') continue;
25
+ const seat = cell.owner?.split('/')[0];
26
+ if (RESOURCE_SEATS.some(id => id === seat)) weights[seat as OrchestratorSeat] += weight;
27
+ }
28
+ }
29
+
30
+ let available = Math.min(totalSlots, RESOURCE_SEATS.reduce((sum, seat) => sum + seatCaps[seat], 0));
31
+ let eligible = RESOURCE_SEATS.filter(seat => weights[seat] > 0 && seatCaps[seat] > 0);
32
+ while (available > 0 && eligible.length > 0) {
33
+ const totalWeight = eligible.reduce((sum, seat) => sum + weights[seat], 0);
34
+ const ideal = (seat: OrchestratorSeat) => available * weights[seat] / totalWeight;
35
+ const capped = eligible.filter(seat => ideal(seat) >= seatCaps[seat]);
36
+ if (capped.length > 0) {
37
+ for (const seat of capped) {
38
+ shares[seat] = seatCaps[seat];
39
+ available -= seatCaps[seat];
40
+ }
41
+ eligible = eligible.filter(seat => !capped.includes(seat));
42
+ continue;
43
+ }
44
+ const remainders = eligible.map(seat => ({ seat, exact: ideal(seat) }));
45
+ for (const { seat, exact } of remainders) {
46
+ shares[seat] = Math.floor(exact);
47
+ available -= shares[seat];
48
+ }
49
+ remainders.sort((a, b) => (b.exact - Math.floor(b.exact)) - (a.exact - Math.floor(a.exact)));
50
+ for (const { seat } of remainders) {
51
+ if (available === 0) break;
52
+ shares[seat]++;
53
+ available--;
54
+ }
55
+ break;
56
+ }
57
+ return shares;
58
+ }
2
59
 
3
60
  export interface SpawnBudgetInput {
4
61
  seat: OrchestratorSeat;
@@ -97,3 +154,96 @@ export function seatCapExclusion(subject: SeatCapSubject, now: number,
97
154
  if (idle !== null && now - subject.progressAt! >= staleRunMinutes * 60_000) return { reason: 'stale', idleMin: idle };
98
155
  return null;
99
156
  }
157
+
158
+ /** Only harness ask/say launches occupy ORCH traffic seats. */
159
+ export const isTrafficLaunch = (command: string): boolean =>
160
+ /(?:^|\s)(?:\S*\/)?elanous\.mjs\s+(?:(?:--test|--config-dir\s+\S+)\s+)?harness\s+(?:ask|say)(?=\s|$)/.test(command);
161
+
162
+ /** ORCH traffic supplies a physical cap, which can exceed the default spawn release gate. */
163
+ function decideTrafficLaunch(row: TrafficSeatRow): boolean {
164
+ return decideSpawn({ seat: row.seat, running: row.running, caps: { [row.seat]: row.cap },
165
+ gate: { [row.seat]: row.cap }, injectedCap: row.launchCap }).allow;
166
+ }
167
+
168
+ /** Process-to-seat resolution occurs at the boundary; this decision does no IO. */
169
+ export function budgetTrafficTick({ processes, now, caps, lastLaunchAt, idleSince, openCells, nextRound, totalSlots, idleMinutes = 30, retainQueuedCells = false }: {
170
+ processes: readonly TrafficProcess[];
171
+ now: Date;
172
+ caps: Readonly<Record<OrchestratorSeat, number>>;
173
+ lastLaunchAt?: Partial<Record<OrchestratorSeat, Date | null>>;
174
+ idleSince?: Partial<Record<OrchestratorSeat, Date | null>>;
175
+ openCells: readonly TrafficCell[];
176
+ nextRound?: readonly TrafficCell[];
177
+ totalSlots?: number;
178
+ idleMinutes?: number;
179
+ /** Observation only: expose queued work even when no launch has been observed yet. */
180
+ retainQueuedCells?: boolean;
181
+ }): TrafficResult {
182
+ const launches = processes.filter(process => isTrafficLaunch(process.command));
183
+ const slotBudget = totalSlots ?? RESOURCE_SEATS.reduce((sum, seat) => sum + caps[seat], 0);
184
+ // A missing next round is unknown, not an empty round with zero remaining work.
185
+ const baseShares = nextRound === undefined ? null : calculateSeatBaseShares({
186
+ totalSlots: slotBudget, currentRound: openCells, nextRound, seatCaps: caps,
187
+ });
188
+ const seats = RESOURCE_SEATS.map((seat): TrafficSeatRow => {
189
+ const owned = launches.filter(process => process.seat === seat);
190
+ const latest = owned.reduce<number | null>((current, process) => {
191
+ if (!Number.isFinite(process.elapsedSeconds) || process.elapsedSeconds < 0) return current;
192
+ const start = now.getTime() - process.elapsedSeconds * 1000;
193
+ return current === null ? start : Math.max(current, start);
194
+ }, null);
195
+ const fallback = owned.length ? lastLaunchAt?.[seat]?.getTime() : undefined;
196
+ const idleStart = idleSince?.[seat]?.getTime();
197
+ const last = idleStart !== undefined && Number.isFinite(idleStart) ? idleStart
198
+ : latest ?? (fallback !== undefined && Number.isFinite(fallback) ? fallback : null);
199
+ const idleFor = last === null ? null : Math.max(0, Math.floor((now.getTime() - last) / 60_000));
200
+ const nextCell = openCells.find(cell => cell.status !== 'done' && cell.owner?.split('/')[0] === seat) ?? null;
201
+ const baseShare = baseShares?.[seat] ?? null;
202
+ return { seat, running: owned.length, cap: caps[seat], baseShare, borrowed: 0, lent: 0,
203
+ launchCap: baseShare === null ? caps[seat] : Math.max(owned.length, baseShare), idleFor,
204
+ idle: decideSpawn({ seat, running: owned.length, caps: { [seat]: caps[seat] },
205
+ gate: { [seat]: caps[seat] } }).allow && last !== null && now.getTime() - last > idleMinutes * 60_000, nextCell };
206
+ });
207
+ if (baseShares !== null) {
208
+ // Account for outstanding loans before lending new capacity. A returning
209
+ // lender cannot launch into a slot still occupied by the borrower's run.
210
+ for (const borrower of seats) {
211
+ let excess = Math.max(0, borrower.running - borrower.baseShare!);
212
+ borrower.borrowed += excess;
213
+ for (const lender of seats) {
214
+ if (!excess) break;
215
+ if (lender === borrower) continue;
216
+ const amount = Math.min(excess, Math.max(0, lender.baseShare! - lender.running - lender.lent));
217
+ lender.lent += amount;
218
+ excess -= amount;
219
+ }
220
+ }
221
+ for (const lender of seats) {
222
+ if (lender.nextCell) continue;
223
+ let available = Math.max(0, lender.baseShare! - lender.running - lender.lent);
224
+ for (const borrower of seats) {
225
+ if (!available) break;
226
+ if (borrower === lender || !borrower.nextCell) continue;
227
+ const needed = Math.max(0, Math.min(borrower.cap, borrower.running + 1) - borrower.launchCap);
228
+ const amount = Math.min(available, needed);
229
+ lender.lent += amount;
230
+ borrower.borrowed += amount;
231
+ borrower.launchCap += amount;
232
+ available -= amount;
233
+ }
234
+ }
235
+ for (const lender of seats) lender.launchCap = Math.max(lender.running, lender.launchCap - lender.lent);
236
+ }
237
+ // Reserve one available global slot per possible request, including unknown-seat
238
+ // launches in the occupied count; returning a lent share does not end its old run.
239
+ let freeSlots = Math.max(0, slotBudget - launches.length);
240
+ for (const row of seats) {
241
+ if (row.nextCell && row.idle && decideTrafficLaunch(row)) {
242
+ if (freeSlots > 0) freeSlots--;
243
+ else row.launchCap = row.running;
244
+ }
245
+ row.idle = row.idle && (row.nextCell === null || decideTrafficLaunch(row));
246
+ if (!row.idle && !retainQueuedCells) row.nextCell = null;
247
+ }
248
+ return { seats, unassigned: launches.filter(process => process.seat === null).length, now, idleMinutes };
249
+ }
@@ -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}`);
@@ -19,6 +19,9 @@ export interface CollectWorkDeps {
19
19
  config?: Pick<OrchestratorLoopConfig, 'seatTrees'>;
20
20
  }
21
21
 
22
+ /** Budget for collectWork's per-process cwd reads. */
23
+ export const COLLECT_WORK_PROCESS_DEADLINE_MS = 10_000;
24
+
22
25
  const repoRoot = resolve(import.meta.dir, '../../..');
23
26
  const defaultRunGh = (args: string[]): string => execFileSync('bun', ['bin/elanous.mjs', '--test', 'gh', ...args], {
24
27
  cwd: repoRoot, encoding: 'utf8', timeout: 60_000, maxBuffer: 20 * 1024 * 1024,
@@ -63,7 +66,10 @@ function goalFiles(text: string): string[] {
63
66
  export function collectWork(deps: CollectWorkDeps = {}): CollectWorkResult {
64
67
  const prs = JSON.parse((deps.runGh ?? defaultRunGh)(['pr', 'list', '--state', 'open', '--limit', '1000', '--json', 'number,title,body,headRefName,files,isDraft,labels'])) as OpenPr[];
65
68
  if (!Array.isArray(prs)) throw new Error('PR observation is not a list');
66
- const observation = (deps.listProcesses ?? defaultListHarnessProcesses)();
69
+ // Only goal-carrying launches become work, so only they need a cwd; bounded so a host with hundreds of harness processes
70
+ // cannot hold the queue lock for minutes (10-08). Records past the budget stay cwd-unknown → their goal reads unreadable.
71
+ const observation = deps.listProcesses ? deps.listProcesses()
72
+ : defaultListHarnessProcesses({ include: record => goalPath(record.command) !== null, cwdOnly: true, deadlineMs: COLLECT_WORK_PROCESS_DEADLINE_MS });
67
73
  if (observation.status !== 'ok') throw new Error(`process observation ${observation.status}`);
68
74
  const cfg = deps.config ?? getUserConfig().loops?.orchestrator ?? ORCHESTRATOR_DEFAULTS;
69
75
  const excluded = { stalled: 0, superseded: 0, draft: 0 };
@@ -171,7 +171,7 @@ export async function runResyncShadow(max = 3, deps: ResyncShadowDeps = {}): Pro
171
171
  entry.resolvedFiles = outcome.resolvedFiles ?? [];
172
172
  if (outcome.sizeCollapse?.length || outcome.providerFailure?.length || outcome.testDeclarationLoss?.length) {
173
173
  entry.verdict = 'guard-tripped';
174
- } else if (outcome.status === 'merged' || outcome.status === 'llm-resolved') {
174
+ } else if (outcome.status === 'merged' || outcome.status === 'llm-resolved' || outcome.status === 'deterministic-resolved') {
175
175
  if (entry.testFiles.length === 0) entry.verdict = 'merge-ok-no-tests';
176
176
  else {
177
177
  const test = (deps.runTests ?? defaultRunTests)(tmp, entry.testFiles);
@@ -1,56 +1,13 @@
1
1
  import type { ChecklistItem } from '../../release-loop/checklist.js';
2
2
  import type { OrchestratorSeat } from '../../user-config.js';
3
+ import { calculateSeatBaseShares as budgetSeatBaseShares } from '../budget.js';
3
4
 
4
- const SEATS: readonly OrchestratorSeat[] = ['OP', 'TC', 'MK', 'UX'];
5
- type WorkCell = Pick<ChecklistItem, 'owner' | 'status'>;
6
-
7
- /** Allocate whole base slots by remaining work; this calculation does not decide launches. */
8
- export function calculateSeatBaseShares({ totalSlots, currentRound, nextRound, seatCaps }: {
5
+ /** Existing orchestrator callers keep their signature; the resource loop owns the decision. */
6
+ export function calculateSeatBaseShares(input: {
9
7
  totalSlots: number;
10
- currentRound: readonly WorkCell[];
11
- nextRound: readonly WorkCell[];
8
+ currentRound: readonly Pick<ChecklistItem, 'owner' | 'status'>[];
9
+ nextRound: readonly Pick<ChecklistItem, 'owner' | 'status'>[];
12
10
  seatCaps: Readonly<Record<OrchestratorSeat, number>>;
13
11
  }): Record<OrchestratorSeat, number> {
14
- if (!Number.isSafeInteger(totalSlots) || totalSlots < 0 ||
15
- SEATS.some(seat => !Number.isSafeInteger(seatCaps[seat]) || seatCaps[seat] < 0)) {
16
- throw new RangeError('totalSlots and seatCaps must be nonnegative safe integers');
17
- }
18
- const shares: Record<OrchestratorSeat, number> = { OP: 0, TC: 0, MK: 0, UX: 0 };
19
- const weights: Record<OrchestratorSeat, number> = { OP: 0, TC: 0, MK: 0, UX: 0 };
20
- for (const [cells, weight] of [[currentRound, 2], [nextRound, 1]] as const) {
21
- for (const cell of cells) {
22
- if (cell.status !== 'yellow') continue;
23
- const seat = cell.owner?.split('/')[0];
24
- if (SEATS.some(id => id === seat)) weights[seat as OrchestratorSeat] += weight;
25
- }
26
- }
27
-
28
- let available = Math.min(totalSlots, SEATS.reduce((sum, seat) => sum + seatCaps[seat], 0));
29
- let eligible = SEATS.filter(seat => weights[seat] > 0 && seatCaps[seat] > 0);
30
- while (available > 0 && eligible.length > 0) {
31
- const totalWeight = eligible.reduce((sum, seat) => sum + weights[seat], 0);
32
- const ideal = (seat: OrchestratorSeat) => available * weights[seat] / totalWeight;
33
- const capped = eligible.filter(seat => ideal(seat) >= seatCaps[seat]);
34
- if (capped.length > 0) {
35
- for (const seat of capped) {
36
- shares[seat] = seatCaps[seat];
37
- available -= seatCaps[seat];
38
- }
39
- eligible = eligible.filter(seat => !capped.includes(seat));
40
- continue;
41
- }
42
- const remainders = eligible.map(seat => ({ seat, exact: ideal(seat) }));
43
- for (const { seat, exact } of remainders) {
44
- shares[seat] = Math.floor(exact);
45
- available -= shares[seat];
46
- }
47
- remainders.sort((a, b) => (b.exact - Math.floor(b.exact)) - (a.exact - Math.floor(a.exact)));
48
- for (const { seat } of remainders) {
49
- if (available === 0) break;
50
- shares[seat]++;
51
- available--;
52
- }
53
- break;
54
- }
55
- return shares;
12
+ return budgetSeatBaseShares(input);
56
13
  }
@@ -8,6 +8,7 @@ import { effectiveInstanceRoot } from '../../instance/resolve.js';
8
8
  import { getUserConfig, type OrchestratorLoopConfig } from '../../user-config.js';
9
9
  import { rubricPriority } from '../../release-loop/rubric.js';
10
10
  import { debug } from '../../debug/log.js';
11
+ import { emitLoopHeartbeat } from '../neighbor-runtime.js';
11
12
  import { listSelfDevRuns, runSummaryLine, selfDevRunsDir } from '../../self-dev/run-store.js';
12
13
  import { planAction, runSeatLoopOnce, type SeatDeps, type SeatItem } from '../../seat-loop/seat-loop.js';
13
14
  import { addHarnessQueue, harnessQueueOutcome, setQueuedPoolHint } from '../../harness/harness-queue.js';
@@ -48,7 +49,8 @@ export type PlacementInput = { id: string; title: string; owner: string; priorit
48
49
  export type PlaceAdapter = (cell: PlacementInput, deps?: { dryRun?: boolean; now?: Date }) => Promise<{ version?: string } | null> | { version?: string } | null;
49
50
  export type RebalanceAdapter = (version: string) => Promise<{ decisions: unknown[]; blocked: Array<{ id: string; reason: string }> }> | { decisions: unknown[]; blocked: Array<{ id: string; reason: string }> };
50
51
  export interface TickDeps {
51
- root?: string; now?: Date; mode?: Mode; runId?: string; window?: Window;
52
+ root?: string; now?: Date | (() => Date); mode?: Mode; runId?: string; window?: Window;
53
+ heartbeatIntervalMs?: number;
52
54
  cards?: () => TaskCard[]; split?: SplitAdapter; placeCell?: PlaceAdapter;
53
55
  degradation?: () => { llm: boolean; board: boolean };
54
56
  gridHosts?: readonly GridHost[];
@@ -101,6 +103,19 @@ export function kstWindow(now: Date): Window {
101
103
  const hour = Number(new Intl.DateTimeFormat('en-US', { timeZone: 'Asia/Seoul', hour: '2-digit', hourCycle: 'h23' }).format(now));
102
104
  return hour < 12 ? '08' : hour < 18 ? '12' : '18';
103
105
  }
106
+ /** KST schedule of graphs/orchestrator/orchestrator.yaml (`0 8,12,18 * * *`). */
107
+ const ORCHESTRATOR_HOURS_KST = [8, 12, 18] as const;
108
+ /** Grace past the next scheduled window before neighbors may call the orchestrator absent. */
109
+ export const ORCHESTRATOR_LEASE_GRACE_MS = 60 * 60_000;
110
+ /** The next scheduled window strictly after `now` — the orchestrator is idle, not absent, until then. */
111
+ export function nextOrchestratorWindowAt(now: Date): Date {
112
+ const kst = new Date(now.getTime() + 9 * 3_600_000);
113
+ for (let day = 0; day < 2; day++) for (const hour of ORCHESTRATOR_HOURS_KST) {
114
+ const at = Date.UTC(kst.getUTCFullYear(), kst.getUTCMonth(), kst.getUTCDate() + day, hour) - 9 * 3_600_000;
115
+ if (at > now.getTime()) return new Date(at);
116
+ }
117
+ throw new Error('unreachable: no orchestrator window within two days');
118
+ }
104
119
  function kstDay(now: Date): string {
105
120
  return new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit' }).format(now);
106
121
  }
@@ -371,7 +386,8 @@ async function optionalAdapter<T>(path: string, name: string): Promise<T | undef
371
386
 
372
387
  /** A graph command invokes one node; only the per-run file carries data across processes. */
373
388
  export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Promise<TickState> {
374
- const now = deps.now ?? new Date();
389
+ const currentTime = () => typeof deps.now === 'function' ? deps.now() : deps.now ?? new Date();
390
+ const now = currentTime();
375
391
  const window = deps.window ?? kstWindow(now);
376
392
  const root = deps.root ?? effectiveInstanceRoot();
377
393
  const runId = deps.runId ?? graphRunId() ?? `${kstDay(now)}-${window}`;
@@ -379,6 +395,16 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
379
395
  const orchestratorConfig = getUserConfig().loops?.orchestrator;
380
396
  const configured = deps.mode ?? orchestratorConfig?.mode;
381
397
  const mode: Mode = configured === 'off' || configured === 'live' ? configured : 'shadow';
398
+ const pulse = (health: 'healthy' | 'degraded', at: Date): void => {
399
+ try { emitLoopHeartbeat(root, 'orchestrator', health, at,
400
+ new Date(nextOrchestratorWindowAt(at).getTime() + ORCHESTRATOR_LEASE_GRACE_MS).toISOString()); }
401
+ catch (error) { debug.log('loop.orchestrator', 'heartbeat-failed', { error: String(error).slice(0, 200) }, { level: 'warn' }); }
402
+ };
403
+ if (mode !== 'off') pulse('healthy', now);
404
+ const heartbeatTimer = mode === 'off' ? undefined : setInterval(() => pulse('healthy', currentTime()), deps.heartbeatIntervalMs ?? 60 * 60_000);
405
+ heartbeatTimer?.unref();
406
+ let nodeError: unknown;
407
+ try {
382
408
  const state: TickState = existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) as TickState : initial(runId, window, mode, kstDay(now));
383
409
  state.steps ??= [];
384
410
  if (state.window !== window || state.mode !== mode) throw new Error('orchestrator run window/mode changed');
@@ -739,6 +765,13 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
739
765
  mkdirSync(dirname(path), { recursive: true });
740
766
  writeFileSync(path, JSON.stringify(state));
741
767
  return state;
768
+ } catch (error) {
769
+ nodeError = error;
770
+ throw error;
771
+ } finally {
772
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
773
+ if (mode !== 'off') pulse(nodeError === undefined ? 'healthy' : 'degraded', currentTime());
774
+ }
742
775
  }
743
776
 
744
777
  /** COORD-HA — the production supply of `TickDeps.degradation` (config `loops.orchestrator.degradationSignal`). Shadow (the
@@ -9,7 +9,7 @@ import { withFileLockSync } from '../../storage/file-lock.js';
9
9
  import { getUserConfig, ORCHESTRATOR_DEFAULTS, type OrchestratorLoopConfig, type OrchestratorSeat } from '../../user-config.js';
10
10
  import { finishAdvice, measureFinish, type FinishAdvice, type FinishMetrics, type MeasureFinishDeps } from './finish-rate.js';
11
11
  import { collectAuthorDepth, type AuthorDepthResult, type CollectAuthorDepthDeps } from './author-depth.js';
12
- import { calculateSeatBaseShares } from './seat-share.js';
12
+ import { budgetTrafficTick } from '../budget.js';
13
13
 
14
14
  export const TRAFFIC_SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
15
15
  export type TrafficCell = Pick<ChecklistItem, 'id' | 'title' | 'owner' | 'status'>;
@@ -49,86 +49,12 @@ function realOrResolved(path: string): string {
49
49
  try { return realpathSync(path); } catch { return resolve(path); }
50
50
  }
51
51
 
52
- const IS_LAUNCH = /(?:^|\s)(?:\S*\/)?elanous\.mjs\s+(?:(?:--test|--config-dir\s+\S+)\s+)?harness\s+(?:ask|say)(?=\s|$)/;
52
+ /** A `harness ask|say` launch command line — the only processes `trafficTick` counts. */
53
+ export { isTrafficLaunch } from '../budget.js';
53
54
 
54
- /** Process-to-seat resolution occurs at the boundary; this decision does no IO. */
55
- export function trafficTick({ processes, now, caps, lastLaunchAt, idleSince, openCells, nextRound, totalSlots, idleMinutes = 30 }: {
56
- processes: readonly TrafficProcess[];
57
- now: Date;
58
- caps: Readonly<Record<OrchestratorSeat, number>>;
59
- lastLaunchAt?: Partial<Record<OrchestratorSeat, Date | null>>;
60
- idleSince?: Partial<Record<OrchestratorSeat, Date | null>>;
61
- openCells: readonly TrafficCell[];
62
- nextRound?: readonly TrafficCell[];
63
- totalSlots?: number;
64
- idleMinutes?: number;
65
- }): TrafficResult {
66
- const launches = processes.filter(process => IS_LAUNCH.test(process.command));
67
- const slotBudget = totalSlots ?? TRAFFIC_SEATS.reduce((sum, seat) => sum + caps[seat], 0);
68
- // A missing next round is unknown, not an empty round with zero remaining work.
69
- const baseShares = nextRound === undefined ? null : calculateSeatBaseShares({
70
- totalSlots: slotBudget, currentRound: openCells, nextRound, seatCaps: caps,
71
- });
72
- const seats = TRAFFIC_SEATS.map((seat): TrafficSeatRow => {
73
- const owned = launches.filter(process => process.seat === seat);
74
- const latest = owned.reduce<number | null>((current, process) => {
75
- if (!Number.isFinite(process.elapsedSeconds) || process.elapsedSeconds < 0) return current;
76
- const start = now.getTime() - process.elapsedSeconds * 1000;
77
- return current === null ? start : Math.max(current, start);
78
- }, null);
79
- const fallback = owned.length ? lastLaunchAt?.[seat]?.getTime() : undefined;
80
- const idleStart = idleSince?.[seat]?.getTime();
81
- const last = idleStart !== undefined && Number.isFinite(idleStart) ? idleStart
82
- : latest ?? (fallback !== undefined && Number.isFinite(fallback) ? fallback : null);
83
- const idleFor = last === null ? null : Math.max(0, Math.floor((now.getTime() - last) / 60_000));
84
- const nextCell = openCells.find(cell => cell.status !== 'done' && cell.owner?.split('/')[0] === seat) ?? null;
85
- const baseShare = baseShares?.[seat] ?? null;
86
- return { seat, running: owned.length, cap: caps[seat], baseShare, borrowed: 0, lent: 0,
87
- launchCap: baseShare === null ? caps[seat] : Math.max(owned.length, baseShare), idleFor,
88
- idle: owned.length < caps[seat] && last !== null && now.getTime() - last > idleMinutes * 60_000, nextCell };
89
- });
90
- if (baseShares !== null) {
91
- // Account for outstanding loans before lending new capacity. A returning
92
- // lender cannot launch into a slot still occupied by the borrower's run.
93
- for (const borrower of seats) {
94
- let excess = Math.max(0, borrower.running - borrower.baseShare!);
95
- borrower.borrowed += excess;
96
- for (const lender of seats) {
97
- if (!excess) break;
98
- if (lender === borrower) continue;
99
- const amount = Math.min(excess, Math.max(0, lender.baseShare! - lender.running - lender.lent));
100
- lender.lent += amount;
101
- excess -= amount;
102
- }
103
- }
104
- for (const lender of seats) {
105
- if (lender.nextCell) continue;
106
- let available = Math.max(0, lender.baseShare! - lender.running - lender.lent);
107
- for (const borrower of seats) {
108
- if (!available) break;
109
- if (borrower === lender || !borrower.nextCell) continue;
110
- const needed = Math.max(0, Math.min(borrower.cap, borrower.running + 1) - borrower.launchCap);
111
- const amount = Math.min(available, needed);
112
- lender.lent += amount;
113
- borrower.borrowed += amount;
114
- borrower.launchCap += amount;
115
- available -= amount;
116
- }
117
- }
118
- for (const lender of seats) lender.launchCap = Math.max(lender.running, lender.launchCap - lender.lent);
119
- }
120
- // Reserve one available global slot per possible request, including unknown-seat
121
- // launches in the occupied count; returning a lent share does not end its old run.
122
- let freeSlots = Math.max(0, slotBudget - launches.length);
123
- for (const row of seats) {
124
- if (row.nextCell && row.idle && row.running < row.launchCap) {
125
- if (freeSlots > 0) freeSlots--;
126
- else row.launchCap = row.running;
127
- }
128
- row.idle = row.idle && (row.nextCell === null || row.running < row.launchCap);
129
- if (!row.idle) row.nextCell = null;
130
- }
131
- return { seats, unassigned: launches.filter(process => process.seat === null).length, now, idleMinutes };
55
+ /** Process observation stays at the boundary; the shared resource loop owns the decision. */
56
+ export function trafficTick(input: Parameters<typeof budgetTrafficTick>[0]): TrafficResult {
57
+ return budgetTrafficTick(input);
132
58
  }
133
59
 
134
60
  export function withFinishAdvice(result: TrafficResult, deps: MeasureFinishDeps = {}): TrafficResult {
@@ -685,6 +685,11 @@ export function unregisteredCronLoops(entries: AllLoopEntry[], opts: LoopRegistr
685
685
  return warnings;
686
686
  }
687
687
 
688
+ /** Newest-first run history of one loop without re-listing the registry (health reads a longer window than `loop status`). */
689
+ export function loopRecentRuns(id: string, opts: LoopRegistryOptions = {}, count = 5): LoopRun[] {
690
+ return recentRuns(id, opts.stateRoot ?? effectiveInstanceRoot(), count);
691
+ }
692
+
688
693
  export function loopStatus(id: string, opts: LoopRegistryOptions = {}): LoopEntry & { recentRuns: LoopRun[] } {
689
694
  const loop = listLoops(opts).find(entry => entry.id === id);
690
695
  if (!loop) throw new Error(`loop not found: ${id}`);