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
@@ -25,19 +25,21 @@ function storeDir(root: string): string { return join(root, 'error-reports'); }
25
25
  /** Removes day folders older than the keep window. Returns how many days were removed. */
26
26
  export function pruneErrorReports(root: string, now: number): number {
27
27
  const base = storeDir(root);
28
- if (!existsSync(base)) return 0;
29
28
  let removed = 0;
30
- for (const year of readdirSync(base).filter((n) => /^\d{4}$/.test(n))) {
31
- for (const month of readdirSync(join(base, year)).filter((n) => /^\d{2}$/.test(n))) {
32
- for (const day of readdirSync(join(base, year, month)).filter((n) => /^\d{2}$/.test(n))) {
33
- const at = Date.parse(`${year}-${month}-${day}T00:00:00Z`);
34
- if (Number.isFinite(at) && now - at > ERROR_REPORT_KEEP_DAYS * DAY_MS + DAY_MS) {
35
- rmSync(join(base, year, month, day), { recursive: true, force: true });
36
- removed++;
29
+ if (existsSync(base)) {
30
+ for (const year of readdirSync(base).filter((n) => /^\d{4}$/.test(n))) {
31
+ for (const month of readdirSync(join(base, year)).filter((n) => /^\d{2}$/.test(n))) {
32
+ for (const day of readdirSync(join(base, year, month)).filter((n) => /^\d{2}$/.test(n))) {
33
+ const at = Date.parse(`${year}-${month}-${day}T00:00:00Z`);
34
+ if (Number.isFinite(at) && now - at > ERROR_REPORT_KEEP_DAYS * DAY_MS + DAY_MS) {
35
+ rmSync(join(base, year, month, day), { recursive: true, force: true });
36
+ removed++;
37
+ }
37
38
  }
38
39
  }
39
40
  }
40
41
  }
42
+ debug.log('error-report.prune', 'done', { pruned: removed });
41
43
  return removed;
42
44
  }
43
45
 
@@ -77,6 +79,26 @@ function count24h(root: string, dedupe: string, now: number): number {
77
79
  return n;
78
80
  }
79
81
 
82
+ /** Retention without a receipt: prune once at daemon boot and then once a day (the ingest path still prunes too). */
83
+ export function startErrorReportPruneTick(deps: {
84
+ root?: () => string;
85
+ now?: () => number;
86
+ schedule?: (tick: () => void, ms: number) => { unref?: () => void };
87
+ } = {}): { stop(): void } {
88
+ const root = deps.root ?? effectiveInstanceRoot;
89
+ const now = deps.now ?? Date.now;
90
+ const run = (trigger: 'boot' | 'tick') => {
91
+ // pruneErrorReports emits error-report.prune/done { pruned }; only a failure is logged here.
92
+ try { pruneErrorReports(root(), now()); }
93
+ catch (error) { debug.log('error-report.prune', 'failed', { trigger, reason: error instanceof Error ? error.message : String(error) }); }
94
+ };
95
+ run('boot');
96
+ let stopped = false;
97
+ const handle = (deps.schedule ?? ((tick, ms) => setInterval(tick, ms)))(() => { if (!stopped) run('tick'); }, DAY_MS);
98
+ handle.unref?.();
99
+ return { stop: () => { stopped = true; if (!deps.schedule) clearInterval(handle as ReturnType<typeof setInterval>); } };
100
+ }
101
+
80
102
  /** POST /v1/reports/ingest — the caller (router) has already checked the ingest token. */
81
103
  export async function handleErrorReportIngest(req: Request, deps: ErrorReportIngestDeps = {}): Promise<Response> {
82
104
  const root = (deps.root ?? effectiveInstanceRoot)();
@@ -0,0 +1,77 @@
1
+ // W9c run guard (TC security review of #25123) — a peer «edit» grant lets another token rewrite a «mine» graph.
2
+ // Running that graph would run the peer's change with this machine's privileges, so a peer save leaves a
3
+ // marker next to the graph's access grants (`<mine>/.access/<id>.peer-edit.json`) and every run entry for a
4
+ // «mine» graph refuses it until the owner approves that exact version (or re-saves it themselves).
5
+ // Fail closed: a marker that exists but cannot be read or parsed refuses the run.
6
+ import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { randomUUID } from 'node:crypto';
9
+
10
+ export const PEER_EDIT_REFUSAL = '상대가 바꾼 그래프 — 변경을 확인하고 승인해야 실행할 수 있다';
11
+
12
+ export interface PeerEditRecord {
13
+ /** `peer:<first 8 hex of the peer token's sha256>` — never the token itself. */
14
+ editedBy: string;
15
+ /** The version id the peer save produced (`pending` while the save is in flight). */
16
+ version: string;
17
+ at: string;
18
+ approved?: { version: string; at: string };
19
+ }
20
+
21
+ export type PeerEditGate =
22
+ | { ok: true }
23
+ | { ok: false; error: 'peer-edit-unapproved'; record: PeerEditRecord }
24
+ | { ok: false; error: 'peer-edit-unreadable'; reason: string };
25
+
26
+ export function peerEditFile(mineDir: string, id: string): string {
27
+ return join(mineDir, '.access', `${id}.peer-edit.json`);
28
+ }
29
+
30
+ /** null = no peer ever saved this graph. Throws when the marker exists but is unreadable or malformed. */
31
+ export function readPeerEdit(mineDir: string, id: string): PeerEditRecord | null {
32
+ let text: string;
33
+ try { text = readFileSync(peerEditFile(mineDir, id), 'utf8'); }
34
+ catch (error) {
35
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;
36
+ throw error;
37
+ }
38
+ const parsed: unknown = JSON.parse(text);
39
+ const record = parsed as PeerEditRecord;
40
+ if (!parsed || typeof parsed !== 'object' || typeof record.editedBy !== 'string' || typeof record.version !== 'string'
41
+ || typeof record.at !== 'string'
42
+ || (record.approved !== undefined && (typeof record.approved !== 'object' || record.approved === null
43
+ || typeof record.approved.version !== 'string' || typeof record.approved.at !== 'string'))) {
44
+ throw new Error('invalid peer-edit marker');
45
+ }
46
+ return record;
47
+ }
48
+
49
+ export function writePeerEdit(mineDir: string, id: string, record: PeerEditRecord): void {
50
+ mkdirSync(join(mineDir, '.access'), { recursive: true, mode: 0o700 });
51
+ const target = peerEditFile(mineDir, id);
52
+ const temp = `${target}.${process.pid}.${randomUUID()}.tmp`;
53
+ try {
54
+ writeFileSync(temp, JSON.stringify(record), { mode: 0o600 });
55
+ renameSync(temp, target);
56
+ } finally { rmSync(temp, { force: true }); }
57
+ }
58
+
59
+ /** An owner-authorized save replaces the peer's bytes with bytes the owner chose. */
60
+ export function clearPeerEdit(mineDir: string, id: string): void {
61
+ rmSync(peerEditFile(mineDir, id), { force: true });
62
+ }
63
+
64
+ export function peerEditRunGate(mineDir: string, id: string): PeerEditGate {
65
+ let record: PeerEditRecord | null;
66
+ try { record = readPeerEdit(mineDir, id); }
67
+ catch (error) { return { ok: false, error: 'peer-edit-unreadable', reason: String(error).slice(0, 200) }; }
68
+ if (!record || record.approved?.version === record.version) return { ok: true };
69
+ return { ok: false, error: 'peer-edit-unapproved', record };
70
+ }
71
+
72
+ /** The 409 body every refusing run entry returns — `reason` is what the editor shows. */
73
+ export function peerEditRefusalBody(id: string, gate: Exclude<PeerEditGate, { ok: true }>): Record<string, unknown> {
74
+ return gate.error === 'peer-edit-unapproved'
75
+ ? { error: gate.error, id, reason: PEER_EDIT_REFUSAL, editedBy: gate.record.editedBy, version: gate.record.version, editedAt: gate.record.at }
76
+ : { error: gate.error, id, reason: PEER_EDIT_REFUSAL, detail: gate.reason };
77
+ }
@@ -1,7 +1,11 @@
1
1
  import { debug } from '../../debug/log.js';
2
- import { GraphWizardInputError, generateGraphFromPrompt, type GraphWizardDeps, type GraphWizardTurn } from '../../graph-wizard/generate.js';
2
+ import { GraphWizardInputError, generateGraphFromPrompt, wizardInstalledPacks, type GraphWizardDeps, type GraphWizardTurn } from '../../graph-wizard/generate.js';
3
3
  import { jsonResponse } from './json-response.js';
4
4
 
5
+ export function handleGraphWizardPacksGet(deps: GraphWizardDeps = {}): Response {
6
+ return jsonResponse({ packs: wizardInstalledPacks(deps) });
7
+ }
8
+
5
9
  const MAX_PROMPT = 4000;
6
10
  const MAX_YAML = 200_000;
7
11
 
@@ -28,6 +32,9 @@ export async function handleGraphWizardPost(req: Request, deps: GraphWizardDeps
28
32
  if (body.currentYaml !== undefined && (typeof body.currentYaml !== 'string' || body.currentYaml.length > MAX_YAML)) {
29
33
  return jsonResponse({ error: 'bad_request', reason: 'currentYaml must be a string' }, 400);
30
34
  }
35
+ if (body.packId !== undefined && typeof body.packId !== 'string') {
36
+ return jsonResponse({ error: 'bad_request', reason: 'packId must be a string' }, 400);
37
+ }
31
38
  let history: GraphWizardTurn[] | undefined;
32
39
  if (body.history !== undefined) {
33
40
  if (!Array.isArray(body.history) || !body.history.every((t) => t && typeof t === 'object' &&
@@ -42,6 +49,7 @@ export async function handleGraphWizardPost(req: Request, deps: GraphWizardDeps
42
49
  ...(body.kind ? { kind: body.kind as 'harness' | 'workflow' } : {}),
43
50
  ...(typeof body.currentYaml === 'string' && body.currentYaml.trim() ? { currentYaml: body.currentYaml } : {}),
44
51
  ...(history ? { history } : {}),
52
+ ...(typeof body.packId === 'string' ? { packId: body.packId } : {}),
45
53
  }, deps);
46
54
  return jsonResponse({ ...result, base: result.base ?? null }, result.ok ? 200 : 422);
47
55
  } catch (error) {
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { tmpdir } from 'node:os';
3
3
  import { join } from 'node:path';
4
+ import { createHash, randomUUID, timingSafeEqual } from 'node:crypto';
4
5
  import { parse as parseYaml } from 'yaml';
5
6
  import { elanousStateRoot } from '../../autopilot/state-paths.js';
6
7
  import { debug } from '../../debug/log.js';
@@ -10,6 +11,7 @@ import { validateGraphYaml } from './graph-kinds.js';
10
11
  import { parseWizardSteps, readWizardSteps, withWizardSteps } from '../../graph-wizard/saved-steps.js';
11
12
  import type { WizardNodeStep } from '../../graph-wizard/steps.js';
12
13
  import { jsonResponse } from './json-response.js';
14
+ import { clearPeerEdit, peerEditRefusalBody, peerEditRunGate, readPeerEdit, writePeerEdit } from './graph-peer-edit.js';
13
15
 
14
16
  /** Declared graph ids only. A user string is never joined onto a filesystem path. */
15
17
  const GRAPH_ID = /^[a-z0-9-]+$/;
@@ -44,6 +46,93 @@ function mineGraphsDir(): string {
44
46
  return join(elanousStateRoot(), 'graphs');
45
47
  }
46
48
 
49
+ export type GraphPermission = 'view' | 'edit';
50
+ type GraphGrant = { recipient: string; permission: GraphPermission };
51
+ const TOKEN_PATTERN = /^eg_[A-Za-z0-9_-]{32,}$/;
52
+
53
+ function accessFile(dir: string, id: string): string {
54
+ return join(dir, '.access', `${id}.json`);
55
+ }
56
+ function tokenHash(token: string): string {
57
+ return createHash('sha256').update(token).digest('hex');
58
+ }
59
+ function accessGrants(dir: string, id: string): GraphGrant[] {
60
+ let text: string;
61
+ try { text = readFileSync(accessFile(dir, id), 'utf8'); }
62
+ catch (error) {
63
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return [];
64
+ throw error;
65
+ }
66
+ const parsed: unknown = JSON.parse(text);
67
+ if (!Array.isArray(parsed) || !parsed.every((entry): entry is GraphGrant => entry !== null && typeof entry === 'object'
68
+ && typeof entry.recipient === 'string' && /^[a-f0-9]{64}$/.test(entry.recipient)
69
+ && (entry.permission === 'view' || entry.permission === 'edit'))) throw new Error('invalid graph access store');
70
+ return parsed;
71
+ }
72
+
73
+ /** Only a hash is persisted; no owner bearer or raw recipient credential goes into a graph/YAML. */
74
+ export function handleGraphAccess(pathname: string, req: Request, deps: GraphsApiDeps = {}): Response {
75
+ const match = /^\/v1\/graphs\/([^/]+)\/access$/.exec(pathname);
76
+ const id = match ? resolveId(match[1]!) : null;
77
+ if (!id) return jsonResponse({ error: 'bad_request' }, 400);
78
+ const owned = mineOwnership(id, deps);
79
+ if (owned instanceof Response) return owned;
80
+ if (req.method !== 'GET') return jsonResponse({ error: 'method-not-allowed' }, 405);
81
+ try { return jsonResponse({ grants: accessGrants(owned.mineDir, id) }); }
82
+ catch (error) { return saveFailed(id, error); }
83
+ }
84
+
85
+ export async function handleGraphAccessPut(pathname: string, req: Request, deps: GraphsApiDeps = {}): Promise<Response> {
86
+ const match = /^\/v1\/graphs\/([^/]+)\/access$/.exec(pathname);
87
+ const id = match ? resolveId(match[1]!) : null;
88
+ if (!id) return jsonResponse({ error: 'bad_request' }, 400);
89
+ const owned = mineOwnership(id, deps);
90
+ if (owned instanceof Response) return owned;
91
+ // Detail GET resolves a core id first; do not grant a token for a shadowed mine id.
92
+ if (indexByDeclaredId(deps.coreDir ?? defaultGraphsDir()).has(id)) return jsonResponse({ error: 'core-id-conflict', id }, 409);
93
+ let body: { recipient?: unknown; permission?: unknown };
94
+ try { body = await req.json() as typeof body; }
95
+ catch { return jsonResponse({ error: 'bad_request' }, 400); }
96
+ if (!body || typeof body.recipient !== 'string' || !TOKEN_PATTERN.test(body.recipient)
97
+ || (body.permission !== 'view' && body.permission !== 'edit')) {
98
+ return jsonResponse({ error: 'bad_request', reason: 'recipient access token and view/edit permission required' }, 400);
99
+ }
100
+ const recipient = tokenHash(body.recipient);
101
+ const dir = join(owned.mineDir, '.access');
102
+ try {
103
+ const grants = accessGrants(owned.mineDir, id).filter((grant) => grant.recipient !== recipient);
104
+ grants.push({ recipient, permission: body.permission });
105
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
106
+ const target = accessFile(owned.mineDir, id);
107
+ const temp = `${target}.${process.pid}.${randomUUID()}.tmp`;
108
+ try {
109
+ writeFileSync(temp, JSON.stringify(grants), { mode: 0o600 });
110
+ renameSync(temp, target);
111
+ } finally { rmSync(temp, { force: true }); }
112
+ } catch (error) { return saveFailed(id, error); }
113
+ return jsonResponse({ recipient, permission: body.permission });
114
+ }
115
+
116
+ /** The peer token works only on this graph and the requested read/edit operation. */
117
+ export function graphAccessForRequest(pathname: string, method: string, token: string | null, deps: GraphsApiDeps = {}): boolean {
118
+ const match = /^\/v1\/graphs\/([^/]+)(?:\/(yaml))?$/.exec(pathname);
119
+ const id = match ? resolveId(match[1]!) : null;
120
+ if (!id || !token || !TOKEN_PATTERN.test(token)) return false;
121
+ if (method !== 'GET' && !(method === 'PUT' && match?.[2] === 'yaml')) return false;
122
+ try {
123
+ if (!indexByDeclaredId(deps.mineDir ?? mineGraphsDir()).has(id)) return false;
124
+ if (indexByDeclaredId(deps.coreDir ?? defaultGraphsDir()).has(id)) return false;
125
+ const wanted = tokenHash(token);
126
+ return accessGrants(deps.mineDir ?? mineGraphsDir(), id).some((grant) => {
127
+ const stored = Buffer.from(grant.recipient, 'hex');
128
+ return timingSafeEqual(stored, Buffer.from(wanted, 'hex')) && (method === 'GET' || grant.permission === 'edit');
129
+ });
130
+ } catch (error) {
131
+ debug.log('nexus.graphs', 'access-read-failed', { id, reason: String(error) }, { level: 'warn' });
132
+ return false;
133
+ }
134
+ }
135
+
47
136
  function resolveId(raw: string): string | null {
48
137
  let id: string;
49
138
  try { id = decodeURIComponent(raw); }
@@ -203,7 +292,10 @@ export function handleGraphsGet(pathname: string, dir = defaultGraphsDir(), deps
203
292
  }
204
293
 
205
294
  /** PUT writes only under the state-root graphs directory. Core YAML is never opened for write. */
206
- export async function handleGraphsPut(pathname: string, req: Request, deps: GraphsApiDeps = {}): Promise<Response> {
295
+ /** Who authorized a save: the owner, or a peer token holding an «edit» grant (raw token; only a hash prefix is kept). */
296
+ export type GraphSaveActor = { peerToken: string } | null;
297
+
298
+ export async function handleGraphsPut(pathname: string, req: Request, deps: GraphsApiDeps = {}, actor: GraphSaveActor = null): Promise<Response> {
207
299
  const match = /^\/v1\/graphs\/([^/]+)\/yaml$/.exec(pathname);
208
300
  if (!match) return jsonResponse({ error: 'not-found' }, 404);
209
301
  const id = resolveId(match[1]!);
@@ -229,11 +321,21 @@ export async function handleGraphsPut(pathname: string, req: Request, deps: Grap
229
321
  // Writes go only to <mine>/<id>.yaml; a hand-placed file under another name is not edited over HTTP.
230
322
  if (existing.file !== `${id}.yaml`) return jsonResponse({ error: 'file-name-mismatch', id, file: existing.file }, 409);
231
323
  let written: { version: string; previous: string };
232
- try { written = withWizardSteps(mineDir, id, steps, () => writeWithVersion(mineDir, id, existing, body.yaml as string)); }
324
+ const editedBy = actor ? `peer:${tokenHash(actor.peerToken).slice(0, 8)}` : null;
325
+ try {
326
+ written = withWizardSteps(mineDir, id, steps, () => {
327
+ // Peer save: the marker goes down «before» the bytes, so a crash in between still refuses the run.
328
+ if (editedBy) writePeerEdit(mineDir, id, { editedBy, version: 'pending', at: new Date().toISOString() });
329
+ const result = writeWithVersion(mineDir, id, existing, body.yaml as string);
330
+ if (editedBy) writePeerEdit(mineDir, id, { editedBy, version: result.version, at: new Date().toISOString() });
331
+ else clearPeerEdit(mineDir, id);
332
+ return result;
333
+ });
334
+ }
233
335
  catch (error) { return saveFailed(id, error); }
234
336
  const { version, previous } = written;
235
- debug.log('nexus.graphs', 'saved', { id, version, previous, steps: steps ? Object.keys(steps).length : 0 });
236
- return jsonResponse({ id, source: 'mine', editable: true, saved: true, version, previous });
337
+ debug.log('nexus.graphs', 'saved', { id, version, previous, steps: steps ? Object.keys(steps).length : 0, ...(editedBy ? { editedBy } : {}) });
338
+ return jsonResponse({ id, source: 'mine', editable: true, saved: true, version, previous, ...(editedBy ? { editedBy } : {}) });
237
339
  }
238
340
 
239
341
  const VERSION_ID = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z(?:-\d{4})?$/;
@@ -408,8 +510,14 @@ export async function handleGraphsClone(pathname: string, req: Request, deps: Gr
408
510
  if (reservedId) return reservedId;
409
511
  const coreDir = deps.coreDir ?? defaultGraphsDir();
410
512
  const mineDir = deps.mineDir ?? mineGraphsDir();
411
- const source = indexByDeclaredId(coreDir).get(id) ?? indexByDeclaredId(mineDir).get(id);
513
+ const fromCore = indexByDeclaredId(coreDir).get(id);
514
+ const source = fromCore ?? indexByDeclaredId(mineDir).get(id);
412
515
  if (!source) return jsonResponse({ error: 'not-found', id }, 404);
516
+ // A clone would carry a peer's unapproved change into a graph with no marker — approve first.
517
+ if (!fromCore) {
518
+ const gate = peerEditRunGate(mineDir, id);
519
+ if (!gate.ok) return jsonResponse(peerEditRefusalBody(id, gate), 409);
520
+ }
413
521
  if (indexByDeclaredId(mineDir).has(body.newId) || indexByDeclaredId(coreDir).has(body.newId)) {
414
522
  return jsonResponse({ error: 'conflict', id: body.newId }, 409);
415
523
  }
@@ -422,9 +530,39 @@ export async function handleGraphsClone(pathname: string, req: Request, deps: Gr
422
530
  return jsonResponse({ id: body.newId, source: 'mine', editable: true, clonedFrom: id }, 201);
423
531
  }
424
532
 
425
- export async function handleGraphsMutation(pathname: string, req: Request, deps: GraphsApiDeps = {}): Promise<Response | null> {
533
+ /** POST /v1/graphs/<id>/approve {version} — owner only (the route never accepts a peer token). Approves the
534
+ * exact peer version the run refusal named; a newer peer save is a new version and needs a new approval. */
535
+ export async function handleGraphsApprove(pathname: string, req: Request, deps: GraphsApiDeps = {}): Promise<Response> {
536
+ const match = /^\/v1\/graphs\/([^/]+)\/approve$/.exec(pathname);
537
+ if (!match) return jsonResponse({ error: 'not-found' }, 404);
538
+ const id = resolveId(match[1]!);
539
+ if (!id) return jsonResponse({ error: 'bad_request', reason: 'invalid graph id' }, 400);
540
+ const owned = mineOwnership(id, deps);
541
+ if (owned instanceof Response) return owned;
542
+ let body: { version?: unknown };
543
+ try { body = await req.json() as { version?: unknown }; }
544
+ catch { return jsonResponse({ error: 'bad_request', reason: 'json body required' }, 400); }
545
+ if (typeof body?.version !== 'string' || !body.version) return jsonResponse({ error: 'bad_request', reason: 'version string required' }, 400);
546
+ let record;
547
+ try { record = readPeerEdit(owned.mineDir, id); }
548
+ catch (error) { return saveFailed(id, error); }
549
+ if (!record) return jsonResponse({ id, approved: true, version: body.version, pending: false });
550
+ if (record.version !== body.version) {
551
+ return jsonResponse({ error: 'version-mismatch', id, version: record.version, reason: '승인하려는 판이 최신 상대 변경과 다르다 — 다시 확인하라' }, 409);
552
+ }
553
+ const approved = { version: record.version, at: new Date().toISOString() };
554
+ try { writePeerEdit(owned.mineDir, id, { ...record, approved }); }
555
+ catch (error) { return saveFailed(id, error); }
556
+ debug.log('nexus.graphs', 'peer-edit-approved', { id, version: record.version, editedBy: record.editedBy });
557
+ return jsonResponse({ id, approved: true, version: record.version, editedBy: record.editedBy });
558
+ }
559
+
560
+ export async function handleGraphsMutation(pathname: string, req: Request, deps: GraphsApiDeps = {}, actor: GraphSaveActor = null): Promise<Response | null> {
561
+ // Only PUT …/yaml is open to a peer token; every other mutation is the owner's alone.
562
+ if (actor && !(req.method === 'PUT' && /^\/v1\/graphs\/[^/]+\/yaml$/.test(pathname))) return jsonResponse({ error: 'unauthorized' }, 401);
426
563
  if (req.method === 'POST' && pathname === '/v1/graphs') return handleGraphsCreate(req, deps);
427
- if (req.method === 'PUT' && /^\/v1\/graphs\/[^/]+\/yaml$/.test(pathname)) return handleGraphsPut(pathname, req, deps);
564
+ if (req.method === 'PUT' && /^\/v1\/graphs\/[^/]+\/yaml$/.test(pathname)) return handleGraphsPut(pathname, req, deps, actor);
565
+ if (req.method === 'POST' && /^\/v1\/graphs\/[^/]+\/approve$/.test(pathname)) return handleGraphsApprove(pathname, req, deps);
428
566
  if (req.method === 'POST' && /^\/v1\/graphs\/[^/]+\/clone$/.test(pathname)) return handleGraphsClone(pathname, req, deps);
429
567
  if (req.method === 'POST' && /^\/v1\/graphs\/[^/]+\/revert$/.test(pathname)) return handleGraphsRevert(pathname, req, deps);
430
568
  return null;
@@ -19,7 +19,9 @@ import { setInProcessOutbound } from '../../domains/outbound-alert.js';
19
19
  import { getUserConfig } from '../../user-config.js';
20
20
  import type { TelegramEvent } from '../../workflow-runtime/triggers/telegram-source.js';
21
21
  import { spawnSync } from 'node:child_process';
22
- import { isAbsolute } from 'node:path';
22
+ import { isAbsolute, join, dirname, relative } from 'node:path';
23
+ import { homedir } from 'node:os';
24
+ import { readdirSync, realpathSync, statSync } from 'node:fs';
23
25
  import { createProject, listProjects } from '../../project/project-store.js';
24
26
  import { handleOutputsGet, type OutputsDeps } from './outputs-route.js';
25
27
  import { buildGridData, type GridDeps } from './grid.js';
@@ -168,6 +170,7 @@ import { handleBuildsGet, parseBuildsPath } from './builds-api.js';
168
170
  import { handleHarnessAskPost, handleHarnessAskStatusGet, handleHarnessRunEventsGet, handleHarnessRunScreenGet, handleHarnessRunsGet, handleHarnessStopPost } from './harness-api.js';
169
171
  import { handleHarnessRunTraversalGet } from './harness-traversal.js';
170
172
  import { handleLoopEdgesGet } from './loop-edges.js';
173
+ import { handleLoopResourcesGet } from './loop-resources.js';
171
174
  import { handleFabricPlans, type FabricPlansRouteOpts } from './fabric-plans.js';
172
175
  import { dispatchPersonaRoute } from './personas.js';
173
176
  import { handleMe, operatorSignal } from './operator.js';
@@ -368,12 +371,12 @@ import {
368
371
  } from './workflows.js';
369
372
  import { handleTriggersSnapshot } from './triggers.js';
370
373
  import { handleWorkflowTemplatesList } from './workflow-templates.js';
371
- import { handleGraphVersionsGet, handleGraphsGet, handleGraphsMutation } from './graphs-api.js';
374
+ import { graphAccessForRequest, handleGraphAccess, handleGraphAccessPut, handleGraphVersionsGet, handleGraphsGet, handleGraphsMutation } from './graphs-api.js';
372
375
  import { handleGraphKindsGet, handleGraphsValidatePost } from './graph-kinds.js';
373
- import { handleGraphWizardPost } from './graph-wizard-api.js';
376
+ import { handleGraphWizardPost, handleGraphWizardPacksGet } from './graph-wizard-api.js';
374
377
  import { syncInstalledPluginNodes } from '../../graph-kinds/installed-plugin-nodes.js';
375
378
  import { elanousStateRoot } from '../../autopilot/state-paths.js';
376
- import { handlePluginsIndexGet, handlePluginsGet, handlePluginsMarketRefresh, handlePluginsInstall, handlePluginsRemove } from './plugins-market.js';
379
+ import { handlePluginsIndexGet, handlePluginsGet, handlePluginsMarketRefresh, handlePluginsInstall, handlePluginsRemove, handlePluginWizardList, handlePluginWizardSave } from './plugins-market.js';
377
380
  import { handlePluginsCredentialsGet, handlePluginsCredentialsPut } from './plugins-credentials-api.js';
378
381
  import {
379
382
  handleWorkflowsList,
@@ -489,11 +492,15 @@ export interface NexusHttpServerOpts {
489
492
  fabricPlans?: FabricPlansRouteOpts;
490
493
  /** GRAPH-WIZARD 시험 주입(LLM·템플릿 뿌리). 운영은 비워 둔다. */
491
494
  graphWizard?: import('../../graph-wizard/generate.js').GraphWizardDeps;
495
+ /** Roots the remote folder picker may browse (default: the daemon user's home). Tests inject a temp root. */
496
+ projectFolderRoots?: string[];
492
497
  /** Optional installer state root for isolated API consumers and tests. */
493
498
  pluginStateRoot?: string;
494
499
  outputs?: OutputsDeps;
495
500
  /** Read-only grid measurement seam; omitted in production for live HQ and Pod pool reads. */
496
501
  grid?: GridDeps;
502
+ /** Resource observation seam; production reads the same ORCH traffic decision. */
503
+ loopResources?: Parameters<typeof handleLoopResourcesGet>[0];
497
504
  /** DRAFT-METRIC cached source seam; omitted in production for the process-wide TTL cache. */
498
505
  draftMetrics?: DraftMetricsSource;
499
506
  decisions?: Pick<DecisionsRouteDeps, 'ledger'>;
@@ -846,6 +853,55 @@ export function handleProjectsGet(): Response {
846
853
  return jsonResponse({ projects: listProjects() });
847
854
  }
848
855
 
856
+ function isWithinRoot(path: string, root: string): boolean {
857
+ const rel = relative(root, path);
858
+ return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
859
+ }
860
+
861
+ /**
862
+ * Remote folder picker: lists sub-folders under an allow-listed root only.
863
+ * Every path is resolved with realpath first, so `..` segments and symlinks cannot escape the roots;
864
+ * dot-folders are hidden and symlinked entries that point outside the roots are not listed.
865
+ */
866
+ export function handleProjectFoldersGet(req: Request, roots: string[] = [homedir()]): Response {
867
+ const allowed: string[] = [];
868
+ for (const root of roots) {
869
+ try { allowed.push(realpathSync(root)); } catch { /* a missing root grants nothing */ }
870
+ }
871
+ if (allowed.length === 0) return jsonResponse({ error: 'folder_unavailable' }, 404);
872
+ const requested = new URL(req.url).searchParams.get('path') ?? allowed[0]!;
873
+ if (!isAbsolute(requested) || requested.includes('\0')) return jsonResponse({ error: 'invalid_folder' }, 400);
874
+ let path: string;
875
+ try { path = realpathSync(requested); }
876
+ catch {
877
+ debug.log('nexus.projects', 'folders-unavailable', { reason: 'missing' });
878
+ return jsonResponse({ error: 'folder_unavailable' }, 404);
879
+ }
880
+ if (!allowed.some(root => isWithinRoot(path, root))) {
881
+ debug.log('nexus.projects', 'folders-refused', { reason: 'outside-roots' });
882
+ return jsonResponse({ error: 'outside_allowed_roots' }, 403);
883
+ }
884
+ try {
885
+ if (!statSync(path).isDirectory()) return jsonResponse({ error: 'not_directory' }, 400);
886
+ const folders = readdirSync(path, { withFileTypes: true })
887
+ .filter(entry => {
888
+ if (entry.name.startsWith('.')) return false;
889
+ if (entry.isDirectory()) return true;
890
+ if (!entry.isSymbolicLink()) return false;
891
+ try {
892
+ const target = realpathSync(join(path, entry.name));
893
+ return statSync(target).isDirectory() && allowed.some(root => isWithinRoot(target, root));
894
+ } catch { return false; }
895
+ })
896
+ .map(entry => ({ name: entry.name, path: join(path, entry.name) }))
897
+ .sort((a, b) => a.name.localeCompare(b.name));
898
+ const atRoot = allowed.includes(path);
899
+ return jsonResponse({ path, parent: atRoot ? null : dirname(path), folders });
900
+ } catch {
901
+ return jsonResponse({ error: 'folder_unavailable' }, 404);
902
+ }
903
+ }
904
+
849
905
  export async function handleProjectsPost(req: Request): Promise<Response> {
850
906
  let body: unknown;
851
907
  try { body = await req.json(); }
@@ -996,7 +1052,9 @@ export async function routeRequest(
996
1052
  const offered = req.headers.get('authorization');
997
1053
  const fallbackAuthorized = fallbackToken !== undefined && offered?.startsWith('Bearer ') === true
998
1054
  && compareTokenConstTime(offered.slice('Bearer '.length).trim(), fallbackToken);
999
- if (!(opts.metaApi ? checkAuth(req, opts.metaApi) : fallbackAuthorized)) {
1055
+ const graphPeerAuthorized = (method === 'GET' || method === 'PUT')
1056
+ && graphAccessForRequest(pathname, method, offered?.startsWith('Bearer ') ? offered.slice(7).trim() : null);
1057
+ if (!(opts.metaApi ? checkAuth(req, opts.metaApi) : fallbackAuthorized) && !graphPeerAuthorized) {
1000
1058
  const name = method === 'POST' && (pathname === '/v1/tasks' || pathname === '/v1/reports/ingest') && opts.metaApi
1001
1059
  ? matchIngestAuthorization(req)?.name
1002
1060
  : undefined;
@@ -1341,6 +1399,11 @@ export async function routeRequest(
1341
1399
 
1342
1400
  // Same owner auth as the neighbouring GETs (short-lived owner tokens · same-origin PWA); #23932 compared the admin
1343
1401
  // bearer only, so a PWA on a short-lived token got 401 and the loop map stayed empty.
1402
+ if (pathname === '/v1/loops/resources' && method === 'GET') {
1403
+ if (!opts.metaApi) return jsonResponse({ error: 'meta-api-runtime-not-wired' }, 503);
1404
+ if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1405
+ return handleLoopResourcesGet(opts.loopResources);
1406
+ }
1344
1407
  if (pathname === '/v1/loops/edges' && method === 'GET') {
1345
1408
  if (!opts.metaApi) return jsonResponse({ error: 'meta-api-runtime-not-wired' }, 503);
1346
1409
  if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
@@ -1407,7 +1470,12 @@ export async function routeRequest(
1407
1470
  return handleGraphsValidatePost(req, opts.metaApi);
1408
1471
  }
1409
1472
 
1410
- // GRAPH-WIZARD — 말 → 그래프 YAML(저장 안 함). 다른 /v1/graphs 쓰기와 같은 인증.
1473
+ // GRAPH-WIZARD — 설치된 팩 목록과 말 → 그래프 YAML(저장 안 함). 같은 소유자 인증.
1474
+ if (method === 'GET' && pathname === '/v1/graphs/wizard/packs') {
1475
+ if (!opts.metaApi) return jsonResponse({ error: 'meta-api-not-wired' }, 503);
1476
+ if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1477
+ return handleGraphWizardPacksGet(opts.graphWizard);
1478
+ }
1411
1479
  if (method === 'POST' && pathname === '/v1/graphs/wizard') {
1412
1480
  if (!opts.metaApi) return jsonResponse({ error: 'meta-api-not-wired' }, 503);
1413
1481
  if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
@@ -1419,6 +1487,10 @@ export async function routeRequest(
1419
1487
  return handleFabricPlans(req, opts.fabricPlans);
1420
1488
  }
1421
1489
 
1490
+ if (pathname === '/v1/projects/folders' && method === 'GET') {
1491
+ if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1492
+ return handleProjectFoldersGet(req, opts.projectFolderRoots);
1493
+ }
1422
1494
  if (pathname === '/v1/projects' && (method === 'GET' || method === 'POST')) {
1423
1495
  if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1424
1496
  return method === 'GET' ? handleProjectsGet() : handleProjectsPost(req);
@@ -1515,6 +1587,10 @@ export async function routeRequest(
1515
1587
  ...opts.decisions,
1516
1588
  });
1517
1589
  }
1590
+ if (method === 'POST' && pathname === '/v1/plugins/wizard') {
1591
+ if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
1592
+ return handlePluginWizardSave(req, opts.metaApi, opts.pluginStateRoot);
1593
+ }
1518
1594
  if (method === 'POST' && pathname === '/v1/plugins/install') {
1519
1595
  if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
1520
1596
  return handlePluginsInstall(req, opts.metaApi, opts.pluginStateRoot);
@@ -1534,10 +1610,19 @@ export async function routeRequest(
1534
1610
  if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
1535
1611
  return handlePluginsRemove(req, pluginRemove[1]!, opts.metaApi, opts.pluginStateRoot);
1536
1612
  }
1613
+ if (method === 'PUT' && /^\/v1\/graphs\/[^/]+\/access$/.test(pathname)) {
1614
+ if (!opts.metaApi || bearerCredential(req, opts.metaApi) === undefined) return jsonResponse({ error: 'unauthorized' }, 401);
1615
+ return handleGraphAccessPut(pathname, req);
1616
+ }
1537
1617
  if ((method === 'PUT' || method === 'POST') && (pathname.startsWith('/v1/graphs/') || pathname === '/v1/graphs')) {
1538
1618
  if (!opts.metaApi) return jsonResponse({ error: 'meta-api-not-wired' }, 503);
1539
- if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
1540
- const mutated = await handleGraphsMutation(pathname, req);
1619
+ const peerMayEdit = method === 'PUT' && graphAccessForRequest(pathname, method,
1620
+ req.headers.get('authorization')?.startsWith('Bearer ') ? req.headers.get('authorization')!.slice(7).trim() : null);
1621
+ const ownerAuthorized = checkAuth(req, opts.metaApi);
1622
+ if (!ownerAuthorized && !peerMayEdit) return jsonResponse({ error: 'unauthorized' }, 401);
1623
+ // A peer save is recorded on the graph so every run path refuses it until the owner approves (W9c run guard).
1624
+ const mutated = await handleGraphsMutation(pathname, req, {}, ownerAuthorized ? null
1625
+ : { peerToken: req.headers.get('authorization')!.slice(7).trim() });
1541
1626
  if (mutated) return mutated;
1542
1627
  }
1543
1628
  const cardClose = /^\/v1\/task-cards\/([^/]+)\/close$/.exec(pathname);
@@ -2829,6 +2914,10 @@ export async function routeRequest(
2829
2914
  // surface (`/v1/workflows` · `~/.elanous/workflows-runs/`) covers the same
2830
2915
  // user need.
2831
2916
 
2917
+ if (method === 'GET' && pathname === '/v1/plugins/wizard') {
2918
+ if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
2919
+ return handlePluginWizardList(req, opts.metaApi, opts.pluginStateRoot);
2920
+ }
2832
2921
  const pluginCredentialsGet = /^\/v1\/plugins\/([^/]+)\/credentials$/.exec(pathname);
2833
2922
  if (method === 'GET' && pluginCredentialsGet) {
2834
2923
  if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
@@ -2844,6 +2933,10 @@ export async function routeRequest(
2844
2933
  catch (error) { debug.log('graph.kinds', 'installed-sync-failed', { reason: String(error) }); }
2845
2934
  return handleGraphKindsGet(req, opts.metaApi);
2846
2935
  }
2936
+ if (method === 'GET' && /^\/v1\/graphs\/[^/]+\/access$/.test(pathname)) {
2937
+ if (!opts.metaApi || bearerCredential(req, opts.metaApi) === undefined) return jsonResponse({ error: 'unauthorized' }, 401);
2938
+ return handleGraphAccess(pathname, req);
2939
+ }
2847
2940
  // F-M1 read + P-F1 raw YAML. Writes live in the mutation block and never touch core graphs/.
2848
2941
  if (method === 'GET' && (pathname === '/v1/graphs' || /^\/v1\/graphs\/[^/]+$/.test(pathname) || /^\/v1\/graphs\/[^/]+\/yaml$/.test(pathname))) {
2849
2942
  return handleGraphsGet(pathname);
@@ -0,0 +1,42 @@
1
+ import { defaultListHarnessProcesses } from '../../harness/harness-cli-command.js';
2
+ import { devVersion, listChecklist } from '../../release-loop/checklist.js';
3
+ import { listSchedules } from '../../release-loop/release-schedule.js';
4
+ import { releaseLedgerRoot } from '../../instance/resolve.js';
5
+ import { getUserConfig, ORCHESTRATOR_DEFAULTS } from '../../user-config.js';
6
+ import { seatOfTree, trafficTick, TRAFFIC_SEATS, type TrafficCell, type TrafficResult } from '../../loops/orchestrator/traffic.js';
7
+ import { finishAdvice, measureFinish } from '../../loops/orchestrator/finish-rate.js';
8
+ import { jsonResponse } from './json-response.js';
9
+
10
+ /** Read-only snapshot of the same ORCH traffic decision used by the scheduled tick. */
11
+ export function observeLoopResources(deps: {
12
+ now?: Date;
13
+ processes?: ReturnType<typeof defaultListHarnessProcesses>;
14
+ version?: string;
15
+ openCells?: readonly TrafficCell[];
16
+ nextRound?: readonly TrafficCell[];
17
+ totalSlots?: number;
18
+ } = {}): TrafficResult {
19
+ const now = deps.now ?? new Date();
20
+ const cfg = getUserConfig().loops?.orchestrator ?? ORCHESTRATOR_DEFAULTS;
21
+ const observation = deps.processes ?? defaultListHarnessProcesses();
22
+ if (observation.status !== 'ok') throw new Error(`process observation ${observation.status}`);
23
+ const version = deps.version ?? devVersion().replace(/-dev\.\d+$/, '');
24
+ const upcoming = deps.nextRound === undefined ? listSchedules(releaseLedgerRoot()).filter(row => Date.parse(row.cutAt) > now.getTime())
25
+ .sort((a, b) => Date.parse(a.cutAt) - Date.parse(b.cutAt)) : [];
26
+ const currentIndex = upcoming.findIndex(row => row.version === version);
27
+ const nextVersion = currentIndex < 0 ? upcoming[0]?.version : upcoming[currentIndex + 1]?.version;
28
+ const totalSlots = deps.totalSlots ?? finishAdvice(measureFinish({}, now),
29
+ TRAFFIC_SEATS.reduce((sum, seat) => sum + cfg.seatCaps[seat], 0)).launchSlots;
30
+ return trafficTick({
31
+ processes: observation.records.map(process => ({ ...process, seat: seatOfTree(process.cwdStatus === 'unknown' ? undefined : process.cwd, cfg) })),
32
+ now, caps: cfg.seatCaps, openCells: deps.openCells ?? listChecklist(version).items, totalSlots,
33
+ ...(deps.nextRound !== undefined ? { nextRound: deps.nextRound }
34
+ : nextVersion ? { nextRound: listChecklist(nextVersion).items } : {}),
35
+ retainQueuedCells: true,
36
+ });
37
+ }
38
+
39
+ export function handleLoopResourcesGet(observe: () => TrafficResult = observeLoopResources): Response {
40
+ try { return jsonResponse({ resource: observe() }); }
41
+ catch { return jsonResponse({ error: 'resource-unavailable' }, 503); }
42
+ }