elanous 0.2.29 → 0.2.31

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 (709) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/{cIpv5nU9AycwYWZI9H589 → LSXfY0u9QtfDbejY0Ukf_}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{058a98ba.7eb28d5e6dc03565.js → 0bb0ff36.b8d418980951e5ba.js} +7 -7
  5. package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +27 -0
  8. package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +287 -0
  9. package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +62 -0
  10. package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/{6013-2f769370d8b4c2c6.js → 1335-6731f392c35216eb.js} +3 -3
  12. package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/{8332.6f0f49df772e163b.js → 1654.32de4b324981a188.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/1834-50625904996a8fa8.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +149 -0
  17. package/apps/pwa/out/_next/static/chunks/{8795.32573a185af18c7b.js → 1913.7cc6fb6055b1ed3f.js} +2 -2
  18. package/apps/pwa/out/_next/static/chunks/{419.736c0d6f426249a7.js → 2001.6c7e0fd02190886c.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{6767.1fcfc9b8be83975f.js → 2213.2ccbada92c33ea0f.js} +16 -16
  24. package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/{4759.1f76713aefb87db5.js → 2871.0bca6137343b3f76.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/{6776.06ff1e3b464cc607.js → 2898.fd781bc7cf01a7b7.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/{1782-59db1b5aa06df38a.js → 2940-619bfd8ef337c611.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/{8471.8d2a8c74f09c36ff.js → 2985.39ccdecd36848b12.js} +4 -4
  31. package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +115 -0
  32. package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +64 -0
  33. package/apps/pwa/out/_next/static/chunks/{bc324b7c.02a09dd93f6a399b.js → 3267c0e6.d34854e7b3984a6e.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3278.2e5d15b53a4091c8.js +2 -0
  35. package/apps/pwa/out/_next/static/chunks/{7725-4f83d5541fbdb81b.js → 3541-ce3a5424ed5ad7f1.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3902.e1e71aaac850a045.js +34 -0
  39. package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{46bd8bea.2f4b4099172fcdb7.js → 409f1e57.d0649fc47691ebfe.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/4111.2bfedbb233ad206b.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{1142.65d002789ab3af7d.js → 4174.92ed04922709ac9e.js} +2 -2
  43. package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +36 -0
  45. package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +286 -0
  49. package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +77 -0
  50. package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +226 -0
  51. package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +79 -0
  53. package/apps/pwa/out/_next/static/chunks/5173-233188df2f42c626.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +148 -0
  57. package/apps/pwa/out/_next/static/chunks/5307.60f2209e809bd627.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/{3396-97b97928c49190c9.js → 5458-16cd7a2b51b74c8c.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/{3403.7199205e65124486.js → 5525.86debf231e181813.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{9143.ff14585db1d275ff.js → 5543.a438b8ab5a13ea47.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +74 -0
  65. package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +94 -0
  67. package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{8398.73a19b39715eda04.js → 6092.a7f99e68b188ca66.js} +1 -1
  70. package/apps/pwa/out/_next/static/chunks/60fb96c9.360b01d8d4927b7b.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/{5591.a35e7a217734a8b9.js → 6157.0c9af021de4fb31b.js} +2 -2
  72. package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/{7375.050ea0d72968e49f.js → 6433.fc573af22bbc9ab7.js} +17 -17
  75. package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +90 -0
  77. package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +32 -0
  78. package/apps/pwa/out/_next/static/chunks/6544cb48.a87fe20ddf137be2.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +104 -0
  80. package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +24 -0
  85. package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +93 -0
  86. package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7371.80c05c653b08e907.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7391.ebae168025c91b6a.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{1852.7f7b53991fcb99ef.js → 7502.ccd5b8065545edd0.js} +2 -2
  93. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +8 -0
  94. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{6534.1ab00f370db7b105.js → 7862.30a8d6482bb238eb.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7947-d0759430f8042240.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{285adb1a.5654e605dddf86d2.js → 7a1887c4.f36b88fba31da8fd.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/8018.a32663f04a6fc5f9.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/8097-92f87bd108322f66.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +24 -0
  109. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{1516.dfaceff3fd2d0e0d.js → 9138.b6e80031afb1d973.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/{9907.49be0fcadaf214b5.js → 9221.7d22eaf353a2d4e7.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/{8767-2f6a634f5abeaa01.js → 9279-d28c5b7754d010ae.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{3160-95c96f4d2a21dd26.js → 9398-6aed28f0a96d0d6f.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/{328bb3ab-6628a1114fe3fb3b.js → 997ce8a5-2ad501927003b41b.js} +1 -1
  121. package/apps/pwa/out/_next/static/chunks/{6853.cc5174a9dee59593.js → 9989.3694300e5f000b14.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-bf3634a117322df7.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-3535a4a69f1fafaa.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-b9f51447f757fbf2.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-a5635eba0d73b84e.js → page-2ebb8fd59b9461f9.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-a2fc4e49a4fa4ba8.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5a503541353e60a8.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-6025101384e361a5.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-f4b630cc8b5273cd.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-a645f00a06483e9a.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-61ac22cbab1ee4ec.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e9bab7413b028b28.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-c992eafe028b642e.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-e547e6ce55f1fb33.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-d27e1b9850fd54bf.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-805ac2e137b7a121.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-0fec29c7ba01894b.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-339a07410cd6f36b.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-349f2752d8cd37ad.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-d5c6dcf31c25cecc.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-2fd93ed8c9f16580.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-9b564dbff0fefc35.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-62f4475d3c38fb9b.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-814ba51385225776.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-397d479626e5a459.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-af1bba4e3251b728.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3ad3be5e7d3fdce9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-f219ccdbc0bf27df.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4da93ad35d4a8d28.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-456666766e5ce6fc.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-7404e377e8adb8c5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-14456240ed2a36ad.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-3a658d5fa6d16a25.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-0157de6e85f8bb6b.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0a07363b130182a1.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-841d73d4ac44ed49.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6f43a3d1146e6377.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/{page-29900ed0011b2c83.js → page-521ee23ac8c0abfa.js} +1 -1
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-a9992ee285efaa83.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-3d48e85fa89ec04f.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d2ced71132fac9b2.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-f08358bdf251adb9.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-233c71cd5f68f0d7.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-70151d78de2f375e.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-dcac33a3e2cb72c0.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-9f92737b8766009a.js → page-149dc1caba411bb9.js} +1 -1
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0448145ccd5c45c1.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-9cb0c444903da65d.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-17e504c396e0e389.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{425cfc4d.fb91158e3971c39f.js → b313b242.cb1265eea908db61.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +122 -0
  175. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +1 -0
  183. package/apps/pwa/out/_next/static/css/{72becfd42807b50d.css → 3f5fc6f96e2cf7b2.css} +1 -1
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +23 -23
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +23 -23
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +23 -23
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +22 -22
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +22 -22
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +23 -23
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +22 -22
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +23 -23
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +23 -23
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +22 -22
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +23 -23
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +23 -23
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +23 -23
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +23 -23
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +22 -22
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +22 -22
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +23 -23
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +23 -23
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +23 -23
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +22 -22
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +22 -22
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +23 -23
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +23 -23
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +22 -22
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +22 -22
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +22 -22
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +23 -23
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +23 -23
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +22 -22
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +23 -23
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +23 -23
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +23 -23
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +23 -23
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +23 -23
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +22 -22
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +23 -23
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +23 -23
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +23 -23
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +23 -23
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +23 -23
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +23 -23
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +23 -23
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +23 -23
  277. package/bin/elanous.mjs +7 -3
  278. package/graphs/context/nightly-condense.yaml +1 -0
  279. package/graphs/intake/intake-daily.yaml +8 -1
  280. package/graphs/intake/recipes.yaml +5 -0
  281. package/graphs/lecture/lecture-note.yaml +2 -2
  282. package/graphs/ops/harness-gc.yaml +1 -0
  283. package/graphs/release/recipes.yaml +3 -0
  284. package/graphs/release/release-loop.yaml +9 -5
  285. package/graphs/units/recipes.yaml +3 -0
  286. package/graphs/units/release-gate-recheck.yaml +15 -0
  287. package/package.json +2 -1
  288. package/scripts/check-native-tool-wiring.ts +3 -2
  289. package/scripts/ci-android-unit-tests.ts +16 -0
  290. package/scripts/ci-typecheck-changed.ts +37 -9
  291. package/scripts/coord-channel-watch.sh +22 -16
  292. package/scripts/coord-role-pack.ts +81 -2
  293. package/scripts/cron-run.ts +96 -3
  294. package/scripts/detect-test-interference.ts +38 -5
  295. package/scripts/gate-pr-cache.ts +65 -0
  296. package/scripts/gh-surface/capture.ts +126 -0
  297. package/scripts/gh-surface/gh +40 -0
  298. package/scripts/import-graph-scc.ts +38 -0
  299. package/scripts/install.ps1 +19 -0
  300. package/scripts/intake-social.ts +181 -0
  301. package/scripts/lecture-note/node.ts +71 -4
  302. package/scripts/lib/android-toolchain.ts +32 -0
  303. package/scripts/lib/deterministic-env.ts +62 -0
  304. package/scripts/lib/gate-test-env.ts +19 -0
  305. package/scripts/measure-goal-grounding-corpus.ts +2 -1
  306. package/scripts/ratchets/test-interference-patterns.allowlist.txt +257 -0
  307. package/scripts/ratchets/test-interference-patterns.ts +367 -0
  308. package/scripts/release-loop/cut-branch.ts +4 -3
  309. package/scripts/release-loop/export-check-node.ts +0 -1
  310. package/scripts/release-loop/fixtures/warm-failure-assertion/sample.junit.xml +8 -0
  311. package/scripts/release-loop/fixtures/warm-job-029/sample.json +1 -0
  312. package/scripts/release-loop/fixtures/warm-job-029/sample.junit.xml +10 -0
  313. package/scripts/release-loop/gate-closure.ts +12 -8
  314. package/scripts/release-loop/gate-diff.ts +1 -1
  315. package/scripts/release-loop/gate-host-mirror.ts +66 -0
  316. package/scripts/release-loop/gate-node.ts +523 -75
  317. package/scripts/release-loop/gate-partial.ts +106 -9
  318. package/scripts/release-loop/gate-pr-cache.ts +66 -0
  319. package/scripts/release-loop/gate-prewarm-plan.ts +66 -0
  320. package/scripts/release-loop/gate-r2-verdict.ts +189 -0
  321. package/scripts/release-loop/gate-recheck-plan-node.ts +52 -0
  322. package/scripts/release-loop/gate-warm-artifacts.ts +253 -0
  323. package/scripts/release-loop/import-graph-scc-run.ts +54 -0
  324. package/scripts/release-loop/import-graph-scc.ts +215 -0
  325. package/scripts/release-loop/node-verdict.ts +2 -0
  326. package/scripts/release-loop/probe-pod-host-dirs-live.ts +113 -0
  327. package/scripts/release-loop/resume-release.ts +33 -4
  328. package/scripts/release-loop/resume.ts +13 -5
  329. package/scripts/release-loop/spawn-lane.ts +2 -19
  330. package/scripts/release-loop/tui-sim-node.ts +3 -2
  331. package/scripts/release-loop/unattended-release.ts +16 -0
  332. package/scripts/test-deterministic.ts +5 -61
  333. package/scripts/train-reach-grouping.ts +48 -0
  334. package/scripts/tsc-incremental-cache.ts +86 -0
  335. package/scripts/tsc-incremental-diagnostics.ts +76 -0
  336. package/scripts/tsc-main-safety-net.ts +2 -2
  337. package/src/acp/client.ts +4 -1
  338. package/src/acp/core-turn-bridge.ts +50 -5
  339. package/src/ad-pipeline/run.ts +4 -2
  340. package/src/agent/runner.ts +169 -3
  341. package/src/agent/subagent-callable.ts +3 -2
  342. package/src/agent-mission/review-loop.ts +6 -3
  343. package/src/agent-substrate/acp-implementer.ts +14 -2
  344. package/src/agent-substrate/pr-reviewer.ts +12 -0
  345. package/src/agent-substrate/self-review-cli.ts +30 -4
  346. package/src/autopilot/pr-manager.ts +62 -7
  347. package/src/briefing/brief-items.ts +24 -22
  348. package/src/capture/tui-self-report.ts +15 -0
  349. package/src/chat/chat-cli.ts +10 -2
  350. package/src/chat/index.ts +77 -11
  351. package/src/chat/input-control-key.ts +5 -3
  352. package/src/chat/tool-interrupt-behavior.ts +38 -0
  353. package/src/chat/tool-render/dispatch.ts +5 -1
  354. package/src/chat/turn-typeahead.ts +75 -20
  355. package/src/cli/__fixtures__/agent-help-before/agent.txt +4 -2
  356. package/src/cli/agent-cli.ts +41 -13
  357. package/src/cli/config-audit.ts +123 -0
  358. package/src/cli/decisions-cli.ts +80 -6
  359. package/src/cli/log-event-names.ts +7 -0
  360. package/src/cli/mcp-login.ts +21 -5
  361. package/src/cli/msg-cli.ts +88 -0
  362. package/src/cli/pr-cli.ts +214 -57
  363. package/src/cli/pr-land-heavy-remote.ts +2 -2
  364. package/src/cli/pr-land-overlap-evidence.ts +63 -9
  365. package/src/cli/pr-land-pod-shadow-job.ts +83 -0
  366. package/src/cli/pr-land-pod-shadow.ts +16 -3
  367. package/src/cli/pr-land-virtual-rebase.ts +108 -0
  368. package/src/cli/release-cli.ts +80 -14
  369. package/src/cli/seat-cli.ts +138 -3
  370. package/src/cli/self-update.ts +5 -2
  371. package/src/cli/tasks-cli.ts +97 -19
  372. package/src/context-bus/context-first.ts +13 -0
  373. package/src/context-bus/context-now.ts +13 -2
  374. package/src/context-bus/ops-events.ts +75 -0
  375. package/src/control-plane/lease-holder.ts +12 -8
  376. package/src/control-plane/ledger.ts +163 -4
  377. package/src/control-plane/server.ts +91 -11
  378. package/src/core-turn/run-core-turn.ts +1 -0
  379. package/src/core-turn/run-goal-loop.ts +222 -3
  380. package/src/core-turn/seat-turn-cap.ts +79 -0
  381. package/src/core-turn/short-question.ts +38 -6
  382. package/src/core-turn/types.ts +3 -0
  383. package/src/dashboard/acp-boot.ts +3 -0
  384. package/src/dashboard/index.ts +224 -615
  385. package/src/dashboard/input/chat-main-entry-runtime.ts +1 -1
  386. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +4 -1
  387. package/src/dashboard/input/chat-main-session.ts +8 -1
  388. package/src/dashboard/input/chat-main-turn.ts +12 -3
  389. package/src/dashboard/input/compact-prepass-recovery.ts +33 -0
  390. package/src/dashboard/input/external-input-host.ts +26 -0
  391. package/src/dashboard/input/internal-turn.ts +261 -0
  392. package/src/dashboard/input/seat-autowake.ts +318 -0
  393. package/src/dashboard/input/turn-steer-bridge.ts +70 -0
  394. package/src/dashboard/loop-scheduler.ts +72 -0
  395. package/src/dashboard/seat-boot-preamble.ts +18 -0
  396. package/src/dashboard/skill-runtime.ts +2 -0
  397. package/src/dashboard/slash-runtime/dashboard-handlers.ts +126 -1181
  398. package/src/dashboard/slash-runtime/help-from-registry.ts +2 -1
  399. package/src/dashboard/slash-runtime/index.ts +13 -0
  400. package/src/dashboard/turn-busy-teardown.ts +50 -0
  401. package/src/decisions/decision-ledger.ts +45 -4
  402. package/src/discord-self-message.ts +4 -4
  403. package/src/domains/release-tool.ts +18 -1
  404. package/src/field-note/transcribe.ts +17 -0
  405. package/src/github/remote-call.ts +81 -0
  406. package/src/goals/registry.ts +46 -6
  407. package/src/goals/types.ts +7 -1
  408. package/src/graph-runner/runner.ts +1 -0
  409. package/src/grok/credential.ts +26 -0
  410. package/src/harness/harness-clean.ts +2 -1
  411. package/src/harness/harness-cli-command.ts +52 -5
  412. package/src/harness/harness-gc.ts +2 -1
  413. package/src/harness/harness-incidents.ts +57 -20
  414. package/src/harness/harness-pod-dispatch.ts +87 -10
  415. package/src/harness/harness-queue.ts +53 -4
  416. package/src/harness/harness-stop.ts +4 -3
  417. package/src/harness/harness-substrate-default.ts +78 -7
  418. package/src/harness/harness-worktree-auto.ts +2 -1
  419. package/src/harness/harness-worktrees.ts +3 -2
  420. package/src/hooks/dispatch.ts +2 -1
  421. package/src/index.ts +152 -25
  422. package/src/input/external-input-queue.ts +33 -1
  423. package/src/intake-plane/slash.ts +14 -3
  424. package/src/keybindings.ts +4 -1
  425. package/src/llm/tool-loop-steered-rejection.ts +33 -0
  426. package/src/llm.ts +138 -19
  427. package/src/loops/consensus.ts +348 -0
  428. package/src/loops/msg-wake.ts +206 -4
  429. package/src/loops/observe.ts +19 -3
  430. package/src/loops/orchestrator/tick.ts +64 -10
  431. package/src/loops/orchestrator/traffic.ts +64 -1
  432. package/src/loops/registry.ts +30 -0
  433. package/src/market/signed-index.ts +14 -0
  434. package/src/mcp/mcp-oauth.ts +54 -5
  435. package/src/models/prompts.ts +28 -0
  436. package/src/msg/msg-sse-client.ts +223 -0
  437. package/src/msg/msg-store.ts +86 -2
  438. package/src/mss/logging/log-store.ts +225 -1
  439. package/src/nexus/DAEMON-THIN-inventory-2026-10-15.md +122 -0
  440. package/src/nexus/api/http-server.ts +54 -15
  441. package/src/nexus/api/loop-resources.ts +19 -3
  442. package/src/nexus/api/ops-api.ts +11 -0
  443. package/src/nexus/daemon-thin-inventory.md +37 -0
  444. package/src/nexus/index.ts +70 -0
  445. package/src/nexus/state/state.ts +1 -0
  446. package/src/oauth/store.ts +32 -7
  447. package/src/onboarding/first-run.ts +28 -3
  448. package/src/ops/overview-cache.ts +16 -4
  449. package/src/ops/resources.ts +82 -0
  450. package/src/plugins/maker/connector-maker.ts +41 -0
  451. package/src/plugins/maker/skill-maker.ts +42 -0
  452. package/src/pr-tool/census.ts +288 -0
  453. package/src/prompt-library/chat-conciseness.ts +13 -0
  454. package/src/prompt-library/universal-preamble.ts +10 -5
  455. package/src/release-loop/gate-host-reservation.ts +97 -0
  456. package/src/release-loop/landing-freeze.ts +5 -0
  457. package/src/release-loop/light-rc.ts +5 -2
  458. package/src/release-loop/placement.ts +92 -1
  459. package/src/release-loop/rehearsal-shard-plan.ts +149 -0
  460. package/src/release-loop/rehearsal.ts +73 -17
  461. package/src/repl/index.ts +20 -2
  462. package/src/seat-loop/seat-knowhow.ts +282 -0
  463. package/src/seat-loop/seat-loop.ts +41 -2
  464. package/src/seat-loop/traffic-signal.ts +3 -0
  465. package/src/self-dev/ask-launch-flow.ts +24 -3
  466. package/src/self-dev/decompose-compose-graph.ts +28 -27
  467. package/src/self-dev/decompose-proposal.ts +53 -38
  468. package/src/self-dev/decompose.ts +17 -0
  469. package/src/self-dev/dev-pipeline.ts +31 -2
  470. package/src/self-dev/draft-residue.ts +24 -9
  471. package/src/self-dev/draft-sweep.ts +59 -4
  472. package/src/self-dev/external-call-zero.ts +77 -0
  473. package/src/self-dev/launch-preflight-text.ts +6 -0
  474. package/src/self-dev/launch-preflight.ts +25 -3
  475. package/src/self-dev/local-supervisor-judge.ts +185 -0
  476. package/src/self-dev/run-supervisor.ts +229 -19
  477. package/src/self-dev/run-types.ts +20 -0
  478. package/src/self-dev/shard-aggregate.ts +13 -2
  479. package/src/self-dev/shard-plan.ts +47 -0
  480. package/src/self-dev/shard-run.ts +112 -10
  481. package/src/self-dev/spawn-lane-files.ts +19 -0
  482. package/src/self-implement/app-api-budget.ts +51 -11
  483. package/src/self-implement/config-gate.ts +2 -1
  484. package/src/self-implement/expose-gate.ts +2 -1
  485. package/src/self-implement/gate-artifacts.ts +405 -0
  486. package/src/self-implement/gate-cli.ts +201 -16
  487. package/src/self-implement/gate-remote.ts +110 -25
  488. package/src/self-implement/gate-scenario.ts +85 -0
  489. package/src/self-implement/goal-author.ts +160 -8
  490. package/src/self-implement/graph-authority.ts +38 -6
  491. package/src/self-implement/graph-overlay-condition.ts +92 -1
  492. package/src/self-implement/heal-triage.ts +79 -0
  493. package/src/self-implement/host-regate.ts +61 -3
  494. package/src/self-implement/orchestrator.ts +14 -123
  495. package/src/self-implement/pr-title.ts +123 -0
  496. package/src/self-implement/run-checkpoint.ts +235 -0
  497. package/src/self-implement/run-ledger.ts +155 -10
  498. package/src/self-implement/run-stop.ts +4 -3
  499. package/src/self-implement/run-ttl.ts +28 -4
  500. package/src/self-implement/seams.ts +2 -0
  501. package/src/self-implement/shadow-stage.ts +7 -6
  502. package/src/self-implement/stop-autoheal.ts +2 -1
  503. package/src/self-implement/train-gate-remote.ts +37 -8
  504. package/src/self-implement/train-reach-grouping.ts +157 -0
  505. package/src/self-implement/train-scenario-layers.ts +133 -0
  506. package/src/self-implement/uncommitted-work.ts +2 -0
  507. package/src/session/pending-input.ts +19 -0
  508. package/src/session/session-start.ts +224 -0
  509. package/src/session-runtime/parallel-dispatch.ts +22 -5
  510. package/src/skills/tools/dev-harness.ts +6 -4
  511. package/src/skills/tools/shell.ts +1 -1
  512. package/src/steward/resync-tick.ts +114 -23
  513. package/src/steward/ta-repair-event-collect.ts +158 -0
  514. package/src/task-agent/card-evidence.ts +25 -1
  515. package/src/task-agent/green-sweep.ts +8 -3
  516. package/src/task-agent/judge.ts +76 -0
  517. package/src/task-agent/land-queue.ts +79 -0
  518. package/src/task-agent/live-moves.ts +30 -9
  519. package/src/task-agent/postland-card.ts +5 -0
  520. package/src/task-agent/prelaunch-already-done.ts +98 -0
  521. package/src/task-agent/real-launch-guard.ts +37 -0
  522. package/src/task-agent/rejudge-on-head.ts +89 -12
  523. package/src/task-agent/remote-rejudge-bind.ts +28 -8
  524. package/src/task-agent/repair-cycle.ts +45 -7
  525. package/src/task-agent/repair-worker.ts +7 -0
  526. package/src/task-agent/run-identity.ts +200 -0
  527. package/src/task-agent/shadow.ts +50 -3
  528. package/src/task-agent/task-hand.ts +158 -5
  529. package/src/task-agent/train-landing.ts +77 -0
  530. package/src/task-agent/work-order.ts +104 -0
  531. package/src/task-orchestrator/surfaces/pod-command-job.ts +88 -28
  532. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +88 -19
  533. package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +2 -1
  534. package/src/task-orchestrator/surfaces/pod-image-source.ts +2 -1
  535. package/src/task-orchestrator/surfaces/pod-install-slots.ts +121 -9
  536. package/src/task-orchestrator/surfaces/pod-lease.ts +20 -6
  537. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +169 -0
  538. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +27 -0
  539. package/src/task-orchestrator/surfaces/pod-result-marker-wait.ts +33 -0
  540. package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +63 -0
  541. package/src/task-orchestrator/surfaces/pod-source-receive.ts +19 -3
  542. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +141 -0
  543. package/src/task-orchestrator/surfaces/pod-warm-ta-shadow.ts +85 -0
  544. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +153 -0
  545. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +208 -0
  546. package/src/task-orchestrator/surfaces/self-implement-pod.ts +132 -15
  547. package/src/telegram.ts +2 -4
  548. package/src/tui/input/pickers.ts +732 -0
  549. package/src/tui/slash/mission.ts +492 -0
  550. package/src/tui/slash/term.ts +877 -0
  551. package/src/tui-client/dashboard-session.ts +9 -0
  552. package/src/tui.ts +48 -0
  553. package/src/user-config/gemini-server-tools.ts +9 -0
  554. package/src/user-config.ts +53 -16
  555. package/src/util/spawn-sync-output.ts +7 -0
  556. package/src/ux-sim/fake-bin.ts +154 -0
  557. package/src/ux-sim/fake-clock.ts +36 -0
  558. package/src/ux-sim/headless-tui.ts +72 -0
  559. package/src/ux-sim/in-process-cli.ts +2 -1
  560. package/src/ux-sim/temp-git-remote.ts +82 -0
  561. package/src/version/packed-revision.json +1 -1
  562. package/src/video-pipeline/recipes/vlog.ts +25 -7
  563. package/apps/pwa/out/_next/static/chunks/1015-9cba22a5a4554797.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/1084-4e66af7376a18db7.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/1106.3a6410b7b0c70ad4.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/1127.32fc642f70423506.js +0 -8
  567. package/apps/pwa/out/_next/static/chunks/1189-d9b8e1995a831fae.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/1224.6da98c7ad0eb7dd8.js +0 -34
  569. package/apps/pwa/out/_next/static/chunks/1370.4e3e2558f111fd5d.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/1514-b7da377fdc4eb17a.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/1577.3c24c32584e10d70.js +0 -286
  572. package/apps/pwa/out/_next/static/chunks/161.130a569855c4d68c.js +0 -24
  573. package/apps/pwa/out/_next/static/chunks/1864.16da9556e78a9236.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/1b6f6b2a.aff090122be3726e.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/2341.e8a9135e5b910dd5.js +0 -36
  576. package/apps/pwa/out/_next/static/chunks/257.10afdbe3520f2fc2.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/2658.87550e692dc493f6.js +0 -64
  578. package/apps/pwa/out/_next/static/chunks/2673.85e2f02efae7a85f.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/2766-bee623b37472ed63.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/2794.6d836872b0bf43e2.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/2806.eb57ced1a528ed39.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/283.7a121c4910e6dad7.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/2870.1dc29aa5fc01f09f.js +0 -115
  584. package/apps/pwa/out/_next/static/chunks/293.fd38e77ce5d48103.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/2993-5952a1cd3fd5d441.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/32-a523427c5bd5ed00.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/3280.af38002a6424a905.js +0 -90
  588. package/apps/pwa/out/_next/static/chunks/3292-5ddffdb2f517b44f.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/3523.d3231daaf7aba993.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/3763.577de3181e1e6ea0.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/4138-cc56b0938ef72783.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/4255-8350c43d307a8875.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/4352.a4954693499c2714.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/4421-f2dcbc6fd80ec013.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/4513-51b94aeee37c9905.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/4928-c3b72f99aa588699.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/4986-f9fc0283fb325219.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/5014.671a7b9f69c2b7b1.js +0 -94
  599. package/apps/pwa/out/_next/static/chunks/5137.d398e24710b3feb6.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/5298.0bea74885cabe754.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/5315.74c53a39739b11c4.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/5317-8b1df8d02105cd07.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/5358-33051f41a2b239af.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/5388.a2ec02501f714ad5.js +0 -62
  605. package/apps/pwa/out/_next/static/chunks/5395.e6ac948a2d15058d.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/5409-f282bf533c151849.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/5466.d0f4aba9d52acfa8.js +0 -27
  608. package/apps/pwa/out/_next/static/chunks/5564.3d28b353470fe305.js +0 -104
  609. package/apps/pwa/out/_next/static/chunks/569.b0cbfb4dee4a167a.js +0 -93
  610. package/apps/pwa/out/_next/static/chunks/5835-72465b73a8bb8d74.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/5941.045d6093139e1f7a.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/6228.47cabb83564dfe54.js +0 -24
  613. package/apps/pwa/out/_next/static/chunks/6457-8d1993002a87d0fe.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/6513.ef54cfae52351982.js +0 -2
  615. package/apps/pwa/out/_next/static/chunks/6582.13d245bf87df69f1.js +0 -148
  616. package/apps/pwa/out/_next/static/chunks/6655.c33f6acc2c61bb89.js +0 -149
  617. package/apps/pwa/out/_next/static/chunks/6731-9d7195af289c8d4c.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/6733.0be05b1db9279fc5.js +0 -79
  619. package/apps/pwa/out/_next/static/chunks/6742-90937146253a8b9e.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/687-816858f77a396a4e.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/6888.72c7b8cfb02a7e61.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/6993.c17c828ac3ef3253.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/7076.9f27ef666b0cefdd.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/7113.1403c5d451804e8c.js +0 -287
  625. package/apps/pwa/out/_next/static/chunks/7169.9dd603c726c2cd7c.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/7247-e3ec21f761f86710.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/7261-a47b72f96f5199df.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/7335-5cda81ac90310077.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/7356-c2e4af090546e2d4.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/738-59518742078fda71.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/7385.a7375f10db73243a.js +0 -77
  632. package/apps/pwa/out/_next/static/chunks/7445.cf1cf2fe109bcd96.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7505.a2057432ab6ef99a.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/7515.31f45fac1f9c70cb.js +0 -74
  635. package/apps/pwa/out/_next/static/chunks/7658-d4ce8c72a9548a91.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8107-0b1ae46bc5bcb788.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8132.bfea2f016ce8d992.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8672.e299832bc7d18ee2.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8735.f1ef798a5f260f6f.js +0 -226
  640. package/apps/pwa/out/_next/static/chunks/8774-88f67f7d8d53d299.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/8782.252e5a405d86e4ed.js +0 -32
  642. package/apps/pwa/out/_next/static/chunks/909-9ed7aa0dba804316.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/9100.22b0301256827007.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/9140-dde77575570bc71e.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/9292-a7ad9a01c7044b93.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/9517.00627dc51b02821c.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/9725.dcbb585652d8e9c5.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/9761.a3ebf5c389c24ddc.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/9972.834d2b2b7d48f1ed.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/9995-1a90486f5ae01546.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/a435ad95.577add051b4facb2.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-83372d8b5863ebf7.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d69256d7613c140a.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-352c5e9ba3f9b858.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/board/page-69f1d725524201a8.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/bots/page-ad9181d84285e4ea.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/ceo/page-86feb08080261a2f.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/chat/page-ffa15987e4756d03.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/consult/page-11a3ab44ac327cc1.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/control/page-6309b11c4264d24d.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/decisions/page-8800472079612e87.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/design-check/page-d387bf3f2301882a.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/editor/page-048f88d8c8658da6.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/exec/page-6c14f9739b7f6880.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/field/page-2834f44f64e98d74.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/healthz/route-83372d8b5863ebf7.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/inside/page-a13df6f88e824887.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/intake/page-8a4397248754cf2b.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/layout-daf0bd329360a336.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/live/page-f7ef1c28764cb476.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/live-run/page-61e74f28b9353dac.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/loops/page-8aaa6dedc4c10dfb.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/market/page-294602ba649be829.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-5fa5f41ed3fce323.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/morning/page-33bf667a5fb297bf.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/not-found-5ae3cbd6e5dba515.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eb867d16ad6fdede.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1ab789fa68682f7e.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6fd41b5b6ff13a71.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fb3d5f7642c99fba.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/outputs/page-70f705b2b5ed42ca.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/page-c9c93b358a7667b5.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1563c556b81c96ed.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ce89c20c45b3a673.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/sessions/page-423ae63d786f4ee9.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-591a5378a4c4447c.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/settings/page-a250496b19a5824b.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c36a9277febc5911.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/setup/layout-83372d8b5863ebf7.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/share/page-a5de57edc6d7f942.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/showroom/page-16bdd4c9f40498df.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/tasks/page-75e946e6e71cc6d8.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/term/page-9eb56166774eb256.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/today/page-1c5f9ef35dbb22c5.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/trace/page-657ff9985e508498.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/vault/page-22dc72499da68fb2.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/workflows/page-094ad334e40f8169.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8bcd379852ff6287.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-6b04413f280cd344.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/de70ab42-d28d47437208d458.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/f800c5e3.46dbd7f2af8c3fce.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/f8bc19ad.47579f6d684b0760.js +0 -122
  703. package/apps/pwa/out/_next/static/chunks/framework-ca9ac4609fb9fed8.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/main-app-0dd08d32d15a030f.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/main-f0399d33955c4e01.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/pages/_app-d42caca6f8ac2211.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/pages/_error-42e713c13aa41ca8.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/webpack-8fbf8f61a377aa16.js +0 -1
  709. /package/apps/pwa/out/_next/static/{cIpv5nU9AycwYWZI9H589 → LSXfY0u9QtfDbejY0Ukf_}/_ssgManifest.js +0 -0
@@ -0,0 +1,348 @@
1
+ // 두 대표 합의 체제 — 조율자(일의 흐름)·수호자(몸)가 루프 세계의 공동 대표다.
2
+ // 다섯 범주(①스폰·은퇴 ②shadow→live 승격 ③상대 가지 영향 변경 ④주기·예산 ⑤운영 그래프 구조 변경)는 «둘의 합의» 뒤에 실행된다.
3
+ // 제안 카드(사실 id·비용·되돌리기) → 상대 검토 최대 2왕복 → 합의: 실행·합의 원장 / 불합의·임계 범주: 양쪽 주장 나란히 대표 결정 카드.
4
+ // 메아리 방지 — 대표별 렌즈(입력 원장) 분리 ⊕ 가능하면 다른 LLM 제공자 표식.
5
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
6
+ import { dirname, join } from 'node:path';
7
+ import { elanousStateRoot } from '../autopilot/state-paths.js';
8
+ import { debug } from '../debug/log.js';
9
+ import { DecisionLedger, type DecisionCategory } from '../decisions/decision-ledger.js';
10
+ import type { Versions } from '../directives/version-at.js';
11
+
12
+ export type ConsensusRepresentative = 'coordinator' | 'guardian';
13
+ export const CONSENSUS_REPRESENTATIVES: readonly ConsensusRepresentative[] = ['coordinator', 'guardian'];
14
+ /** 조율자는 일의 흐름, 수호자는 몸을 가진 공동 대표다. */
15
+ export const representativeLabel = (who: ConsensusRepresentative): string =>
16
+ who === 'coordinator' ? '조율자(일의 흐름)' : '수호자(몸)';
17
+
18
+ export type ConsensusCategory =
19
+ | 'spawn-retire'
20
+ | 'shadow-live-promotion'
21
+ | 'cross-branch-change'
22
+ | 'period-budget'
23
+ | 'graph-structure-change';
24
+ export const CONSENSUS_CATEGORIES: readonly ConsensusCategory[] = [
25
+ 'spawn-retire', 'shadow-live-promotion', 'cross-branch-change', 'period-budget', 'graph-structure-change',
26
+ ];
27
+ const CATEGORY_LABELS: Record<ConsensusCategory, string> = {
28
+ 'spawn-retire': '①스폰·은퇴',
29
+ 'shadow-live-promotion': '②shadow→live 승격',
30
+ 'cross-branch-change': '③상대 가지 영향 변경',
31
+ 'period-budget': '④주기·예산',
32
+ 'graph-structure-change': '⑤운영 그래프 구조 변경',
33
+ };
34
+ export const categoryLabel = (category: ConsensusCategory): string => CATEGORY_LABELS[category];
35
+
36
+ /** 상대 검토는 최대 2왕복이다. */
37
+ export const MAX_REVIEW_ROUND_TRIPS = 2;
38
+
39
+ /** 대표 몫 = 불합의 ⊕ 돈·보안·되돌릴 수 없는 것 — 결정 원장의 DecisionCategory 어휘를 그대로 쓴다 (import 만). */
40
+ export const CROWN_THRESHOLD_CATEGORIES: readonly DecisionCategory[] = ['money', 'security', 'irreversible'];
41
+
42
+ /** 대표 카드 표식 — 소스에 글자를 직접 쓰면 공개본 유출 검사(ceo-mark)가 문자열 안까지 문다 — 코드 포인트로 만든다. */
43
+ export const CROWN_MARK = String.fromCodePoint(0x1F451);
44
+
45
+ export interface ProposalCard {
46
+ id: string;
47
+ category: ConsensusCategory;
48
+ title: string;
49
+ /** 사실 id — 제안의 근거 관측. */
50
+ factId: string;
51
+ /** 비용 */
52
+ cost: string;
53
+ /** 되돌리기 */
54
+ undo: string;
55
+ thresholds: DecisionCategory[];
56
+ proposedBy: ConsensusRepresentative;
57
+ proposedAt: string;
58
+ status: 'open' | 'agreed' | 'disagreed';
59
+ roundTrips: number;
60
+ reviews: ReviewRecord[];
61
+ provider?: string;
62
+ }
63
+
64
+ export interface ReviewRecord {
65
+ by: ConsensusRepresentative;
66
+ at: string;
67
+ roundTrip: number;
68
+ verdict: 'revise' | 'agree' | 'disagree';
69
+ claim: string;
70
+ provider?: string;
71
+ }
72
+
73
+ /** 대표 결정 카드 — 양쪽 주장이 나란히 실린다. */
74
+ export interface CrownCard {
75
+ id: string;
76
+ proposalId: string;
77
+ reason: 'disagreement' | 'threshold';
78
+ thresholds: DecisionCategory[];
79
+ coordinatorClaim: string;
80
+ guardianClaim: string;
81
+ raisedAt: string;
82
+ /** 결정 원장 미러 카드 — refs 로 멱등이라 같은 제안의 재발급은 같은 카드를 돌려준다. */
83
+ decisionId?: string;
84
+ }
85
+
86
+ export type ConsensusEvent =
87
+ | { type: 'proposed'; proposal: ProposalCard }
88
+ | { type: 'reviewed'; proposalId: string; review: ReviewRecord }
89
+ | { type: 'agreed'; proposalId: string; at: string; agreedBy: ConsensusRepresentative }
90
+ | { type: 'disagreed'; proposalId: string; at: string; disagreedBy: ConsensusRepresentative }
91
+ | { type: 'crown-card'; card: CrownCard };
92
+
93
+ /** 렌즈(입력 원장) 이벤트 — 행위한 대표의 원장에만 남는다 (메아리 방지). */
94
+ export interface LensEvent {
95
+ action: 'proposed' | 'reviewed';
96
+ proposalId: string;
97
+ at: string;
98
+ verdict?: ReviewRecord['verdict'];
99
+ provider?: string;
100
+ }
101
+
102
+ export interface ConsensusOptions {
103
+ stateDir?: string;
104
+ now?: () => Date;
105
+ resolveVersion?: (at: string) => Versions;
106
+ /** 가능하면 다른 LLM 제공자 — 대표별 기본 표식. */
107
+ providers?: { coordinator?: string; guardian?: string };
108
+ /** 합의 원장의 agreed 이벤트가 실행 권한 기록이다 — 훅은 기록 뒤에 한 번 돈다. */
109
+ execute?: (proposal: ProposalCard) => void;
110
+ ledger?: DecisionLedger;
111
+ }
112
+
113
+ function oneLine(value: string, name: string): string {
114
+ const text = (value ?? '').trim().replace(/[\r\n]+/g, ' ');
115
+ if (!text) throw new Error(`${name} is required`);
116
+ return text;
117
+ }
118
+
119
+ /** ISO 8601 주 키 — 합의 원장 «한 주 N건» 집계 단위. */
120
+ function isoWeek(at: string): string {
121
+ const date = new Date(at);
122
+ const day = date.getUTCDay() || 7;
123
+ date.setUTCDate(date.getUTCDate() + 4 - day);
124
+ const yearStart = Date.UTC(date.getUTCFullYear(), 0, 1);
125
+ const week = Math.ceil(((date.getTime() - yearStart) / 86400000 + 1) / 7);
126
+ return `${date.getUTCFullYear()}-W${String(week).padStart(2, '0')}`;
127
+ }
128
+
129
+ export class ConsensusEngine {
130
+ readonly ledgerPath: string;
131
+ private readonly stateDir: string;
132
+ private readonly now: () => Date;
133
+ private readonly providers: { coordinator?: string; guardian?: string };
134
+ private readonly executeHook: (proposal: ProposalCard) => void;
135
+ private readonly ledger: DecisionLedger;
136
+
137
+ constructor(options: ConsensusOptions = {}) {
138
+ this.stateDir = options.stateDir ?? elanousStateRoot();
139
+ this.ledgerPath = join(this.stateDir, 'consensus', 'consensus.jsonl');
140
+ this.now = options.now ?? (() => new Date());
141
+ this.providers = options.providers ?? {};
142
+ this.executeHook = options.execute ?? (() => {});
143
+ this.ledger = options.ledger ?? new DecisionLedger({
144
+ stateDir: options.stateDir, now: options.now, resolveVersion: options.resolveVersion,
145
+ });
146
+ }
147
+
148
+ /** 대표별 렌즈(입력 원장) 경로 — 두 대표의 렌즈는 서로 분리되어 메아리를 막는다. */
149
+ lensPath(who: ConsensusRepresentative): string {
150
+ return join(this.stateDir, 'consensus', 'lens', `${who}.jsonl`);
151
+ }
152
+
153
+ propose(input: {
154
+ by: ConsensusRepresentative; category: ConsensusCategory; title: string; factId: string; cost: string; undo: string;
155
+ thresholds?: DecisionCategory[]; provider?: string;
156
+ }): ProposalCard {
157
+ if (!CONSENSUS_REPRESENTATIVES.includes(input.by)) throw new Error(`invalid representative: ${input.by} (coordinator|guardian)`);
158
+ if (!CONSENSUS_CATEGORIES.includes(input.category)) throw new Error(`invalid consensus category: ${input.category} (${CONSENSUS_CATEGORIES.join('|')})`);
159
+ const thresholds = [...new Set(input.thresholds ?? [])];
160
+ if (thresholds.some(threshold => !CROWN_THRESHOLD_CATEGORIES.includes(threshold)))
161
+ throw new Error(`invalid threshold category — ${CROWN_MARK} 몫은 ${CROWN_THRESHOLD_CATEGORIES.join('|')}만`);
162
+ const at = this.now().toISOString();
163
+ const provider = input.provider ?? this.providers[input.by];
164
+ const proposal: ProposalCard = {
165
+ id: this.nextId('C', at),
166
+ category: input.category,
167
+ title: oneLine(input.title, 'title'),
168
+ factId: oneLine(input.factId, 'fact id'),
169
+ cost: oneLine(input.cost, 'cost'),
170
+ undo: oneLine(input.undo, 'undo'),
171
+ thresholds,
172
+ proposedBy: input.by,
173
+ proposedAt: at,
174
+ status: 'open',
175
+ roundTrips: 0,
176
+ reviews: [],
177
+ ...(provider ? { provider } : {}),
178
+ };
179
+ this.append([{ type: 'proposed', proposal }]);
180
+ this.appendLens(input.by, { action: 'proposed', proposalId: proposal.id, at, ...(provider ? { provider } : {}) });
181
+ debug.log('loops.consensus', 'proposed', { id: proposal.id, category: proposal.category, by: input.by });
182
+ return proposal;
183
+ }
184
+
185
+ /** 결론 없이 왕복 하나를 소비하는 검토 — 두 번째 왕복은 결론으로 끝내야 한다. */
186
+ review(id: string, input: { by: ConsensusRepresentative; claim: string; provider?: string }): ProposalCard {
187
+ return this.conclude(id, 'revise', input).proposal;
188
+ }
189
+
190
+ /** 합의 — 실행 훅이 돌고 합의 원장에 기록된다. 임계 범주면 대표 카드도 함께 만든다. */
191
+ agree(id: string, input: { by: ConsensusRepresentative; claim?: string; provider?: string }): { proposal: ProposalCard; crownCard?: CrownCard } {
192
+ return this.conclude(id, 'agree', input);
193
+ }
194
+
195
+ /** 불합의 — 양쪽 주장 나란히 대표 결정 카드로 간다. */
196
+ disagree(id: string, input: { by: ConsensusRepresentative; claim: string; provider?: string }): { proposal: ProposalCard; crownCard: CrownCard } {
197
+ const { proposal, crownCard } = this.conclude(id, 'disagree', input);
198
+ if (!crownCard) throw new Error(`${CROWN_MARK} 카드 없는 불합의는 없다: ${id}`);
199
+ return { proposal, crownCard };
200
+ }
201
+
202
+ list(): ProposalCard[] {
203
+ return [...this.state().proposals.values()]
204
+ .sort((a, b) => b.proposedAt.localeCompare(a.proposedAt) || a.id.localeCompare(b.id));
205
+ }
206
+
207
+ show(id: string): ProposalCard {
208
+ const proposal = this.list().find(entry => entry.id === id);
209
+ if (!proposal) throw new Error(`consensus proposal not found: ${id}`);
210
+ return proposal;
211
+ }
212
+
213
+ crownCards(): CrownCard[] {
214
+ return [...this.state().cards]
215
+ .sort((a, b) => b.raisedAt.localeCompare(a.raisedAt) || a.id.localeCompare(b.id));
216
+ }
217
+
218
+ /** 판정선 — 합의 원장의 agreed 이벤트를 ISO 주 단위로 센다 (한 주 N건의 기전). */
219
+ weeklyAgreementCounts(): Array<{ week: string; count: number }> {
220
+ const counts = new Map<string, number>();
221
+ for (const event of this.events()) {
222
+ if (event.type !== 'agreed') continue;
223
+ const week = isoWeek(event.at);
224
+ counts.set(week, (counts.get(week) ?? 0) + 1);
225
+ }
226
+ return [...counts.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([week, count]) => ({ week, count }));
227
+ }
228
+
229
+ /** 상대 검토 — 검토자는 제안자의 상대여야 하고 왕복은 최대 2다. */
230
+ private conclude(id: string, verdict: ReviewRecord['verdict'], input: { by: ConsensusRepresentative; claim?: string; provider?: string }): { proposal: ProposalCard; crownCard?: CrownCard } {
231
+ const proposal = this.show(id);
232
+ if (proposal.status !== 'open') throw new Error(`합의가 이미 결론났다: ${id} (${proposal.status === 'agreed' ? '합의' : '불합의'})`);
233
+ if (input.by === proposal.proposedBy) throw new Error('메아리 방지 — 제안한 대표는 자기 제안을 검토하지 않는다');
234
+ if (verdict === 'revise' && proposal.roundTrips + 1 >= MAX_REVIEW_ROUND_TRIPS)
235
+ throw new Error(`상대 검토는 최대 ${MAX_REVIEW_ROUND_TRIPS}왕복이다 — 마지막 왕복에는 합의 또는 불합의로 끝낸다`);
236
+ const claim = oneLine(input.claim ?? (verdict === 'agree' ? '동의 — 제안대로 실행한다' : ''), 'claim');
237
+ const at = this.now().toISOString();
238
+ const provider = input.provider ?? this.providers[input.by];
239
+ const review: ReviewRecord = { by: input.by, at, roundTrip: proposal.roundTrips + 1, verdict, claim, ...(provider ? { provider } : {}) };
240
+ const events: ConsensusEvent[] = [{ type: 'reviewed', proposalId: proposal.id, review }];
241
+ if (verdict === 'agree') events.push({ type: 'agreed', proposalId: proposal.id, at, agreedBy: input.by });
242
+ if (verdict === 'disagree') events.push({ type: 'disagreed', proposalId: proposal.id, at, disagreedBy: input.by });
243
+ let crownCard: CrownCard | undefined;
244
+ if (verdict === 'disagree' || (verdict === 'agree' && proposal.thresholds.length > 0)) {
245
+ crownCard = this.mintCrown(proposal, review, verdict === 'disagree' ? 'disagreement' : 'threshold', at);
246
+ events.push({ type: 'crown-card', card: crownCard });
247
+ }
248
+ this.append(events);
249
+ this.appendLens(input.by, { action: 'reviewed', proposalId: proposal.id, at, verdict, ...(provider ? { provider } : {}) });
250
+ debug.log('loops.consensus', 'reviewed', { proposalId: proposal.id, verdict, roundTrip: review.roundTrip, by: input.by });
251
+ const updated = this.show(proposal.id);
252
+ if (verdict === 'agree') this.executeHook(updated);
253
+ return { proposal: updated, ...(crownCard ? { crownCard } : {}) };
254
+ }
255
+
256
+ /** 대표 결정 카드 — 결정 원장에 refs 멱등으로 미러하고 양쪽 주장을 나란히 담는다. */
257
+ private mintCrown(proposal: ProposalCard, review: ReviewRecord, reason: 'disagreement' | 'threshold', at: string): CrownCard {
258
+ const proposerClaim = `제안 ${proposal.title} — 사실 ${proposal.factId} · 비용 ${proposal.cost} · 되돌리기 ${proposal.undo}`;
259
+ const coordinatorClaim = proposal.proposedBy === 'coordinator' ? proposerClaim : review.claim;
260
+ const guardianClaim = proposal.proposedBy === 'guardian' ? proposerClaim : review.claim;
261
+ const category = CROWN_THRESHOLD_CATEGORIES.find(threshold => proposal.thresholds.includes(threshold)) ?? 'other';
262
+ const ref = `consensus:${proposal.id}:crown`;
263
+ const decision = this.ledger.raiseOnce({
264
+ title: `${CROWN_MARK} ${reason === 'disagreement' ? '합의 불합의' : '임계 합의 승인'} — ${proposal.title}`,
265
+ category,
266
+ scqa: {
267
+ s: `두 대표 합의 ${categoryLabel(proposal.category)} 제안 ${proposal.id}이 ${reason === 'disagreement' ? '불합의로 끝났다' : '임계 범주 합의에 도달했다'}`,
268
+ c: reason === 'disagreement' ? '두 대표 주장이 갈린다 — 양쪽 주장이 선택지로 나란히 실렸다' : `두 대표가 합의했으나 ${category} 범주다 — ${CROWN_MARK} 승인이 필요하다`,
269
+ q: reason === 'disagreement' ? '어느 대표 주장을 따를까?' : `합의된 ${category} 변경을 승인할까?`,
270
+ },
271
+ options: [
272
+ { key: 'a', label: '조율자', consequence: coordinatorClaim },
273
+ { key: 'b', label: '수호자', consequence: guardianClaim },
274
+ ],
275
+ recommendation: { skipped: true, reason: reason === 'disagreement' ? `두 대표 주장이 갈린다 — ${CROWN_MARK} 판단` : `임계 범주 합의 — ${CROWN_MARK} 승인` },
276
+ raisedBy: { agent: 'consensus' },
277
+ refs: [ref],
278
+ }, ref);
279
+ const card: CrownCard = {
280
+ id: this.nextId('K', at),
281
+ proposalId: proposal.id,
282
+ reason,
283
+ thresholds: [...proposal.thresholds],
284
+ coordinatorClaim,
285
+ guardianClaim,
286
+ raisedAt: at,
287
+ decisionId: decision.id,
288
+ };
289
+ debug.log('loops.consensus', 'crown-card', { id: card.id, proposalId: proposal.id, reason, decisionId: decision.id });
290
+ return card;
291
+ }
292
+
293
+ private state(): { proposals: Map<string, ProposalCard>; cards: CrownCard[] } {
294
+ const proposals = new Map<string, ProposalCard>();
295
+ const cards: CrownCard[] = [];
296
+ for (const event of this.events()) {
297
+ if (event.type === 'proposed') {
298
+ if (proposals.has(event.proposal.id)) throw new Error(`duplicate consensus proposal: ${event.proposal.id}`);
299
+ proposals.set(event.proposal.id, { ...event.proposal, reviews: [...event.proposal.reviews] });
300
+ } else if (event.type === 'crown-card') {
301
+ cards.push(event.card);
302
+ } else {
303
+ const proposal = proposals.get(event.proposalId);
304
+ if (!proposal) throw new Error(`unknown consensus proposal: ${event.proposalId}`);
305
+ if (event.type === 'reviewed') {
306
+ proposal.reviews.push(event.review);
307
+ proposal.roundTrips = event.review.roundTrip;
308
+ } else {
309
+ if (proposal.status !== 'open') throw new Error(`invalid consensus transition: ${event.proposalId}`);
310
+ proposal.status = event.type === 'agreed' ? 'agreed' : 'disagreed';
311
+ }
312
+ }
313
+ }
314
+ return { proposals, cards };
315
+ }
316
+
317
+ private events(): ConsensusEvent[] {
318
+ if (!existsSync(this.ledgerPath)) return [];
319
+ const text = readFileSync(this.ledgerPath, 'utf8');
320
+ if (text && !text.endsWith('\n')) throw new Error('incomplete consensus ledger line');
321
+ return text.split('\n').filter(Boolean).map((line, index) => {
322
+ try { return JSON.parse(line) as ConsensusEvent; }
323
+ catch { throw new Error(`invalid consensus ledger event on line ${index + 1}`); }
324
+ });
325
+ }
326
+
327
+ private nextId(prefix: 'C' | 'K', at: string): string {
328
+ const day = at.slice(0, 10).replaceAll('-', '');
329
+ const head = `${prefix}-${day}-`;
330
+ let next = 1;
331
+ for (const event of this.events()) {
332
+ const id = event.type === 'proposed' ? event.proposal.id : event.type === 'crown-card' ? event.card.id : null;
333
+ if (id?.startsWith(head)) next = Math.max(next, Number(id.slice(head.length)) + 1);
334
+ }
335
+ return `${head}${String(next).padStart(2, '0')}`;
336
+ }
337
+
338
+ private append(events: ConsensusEvent[]): void {
339
+ mkdirSync(dirname(this.ledgerPath), { recursive: true });
340
+ appendFileSync(this.ledgerPath, `${events.map(event => JSON.stringify(event)).join('\n')}\n`);
341
+ }
342
+
343
+ private appendLens(who: ConsensusRepresentative, event: LensEvent): void {
344
+ const path = this.lensPath(who);
345
+ mkdirSync(dirname(path), { recursive: true });
346
+ appendFileSync(path, `${JSON.stringify(event)}\n`);
347
+ }
348
+ }
@@ -14,6 +14,11 @@
14
14
  // ⚠️ wake-inject 는 «사람이 치지 않는 pane»(무인 에이전트 전용)에만 둔다 — 사람이 치는 pane 에는 켜지 말 것.
15
15
  // ⛔ 깨움은 post 의 성패를 바꾸지 않는다 — 실패·시간 초과는 결과 값으로만 돌아온다.
16
16
  // ⛔ 시험 우주에서는 실제 pty/herdr 호출 0 — `ELANOUS_MSG_WAKE_LIVE=1` 로만 연다(격리 실물 시험용).
17
+ // ⭐ 버리지 않고 미룬다(TUI-H4 · 10-16 OPT 우편 36 중 18 깨움 0): pty-inject 대상이 턴 중(busy)이거나 5분 창(debounced)이라
18
+ // 못 두드린 글은 `pending_wakes` 에 «주소당 한 줄»로 접어 두고, 그 TUI 안의 폴러(`startPendingWakePoller`)가
19
+ // 턴이 idle 로 «기록»된 뒤 한 번 다시 두드린다. 5분 창은 다시 두드림에도 걸린다 — 창 안이면 버림이 아니라 다음 틱으로 미룸.
20
+ // 상한(시도 3 · 2시간) → `gave-up`. 관측 = `loops.msg-wake` deferred | redelivered | gave-up.
21
+ // 재두드림 «전»에 그 주소 미읽음을 잰다 — 0 이면 두드리지 않고 줄을 지운다(`dropped-read` · H4-FOLLOW ③).
17
22
 
18
23
  import { spawn } from 'node:child_process';
19
24
  import { Database } from 'bun:sqlite';
@@ -24,11 +29,17 @@ import { dirname, join } from 'node:path';
24
29
 
25
30
  import { debug } from '../debug/log.js';
26
31
  import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
32
+ import { readUnreadCount } from '../msg/msg-store.js';
27
33
  import { listPtyManifestRows } from '../pty-shell/pty-manifest.js';
28
34
  import { messageRouteFor, type MessageWake } from './msg-address.js';
29
35
 
30
36
  export const WAKE_DEBOUNCE_MS = 5 * 60_000;
31
37
  export const WAKE_TIMEOUT_MS = 3_000;
38
+ /** A deferred wake is retried at most this many times (an attempt = idle ∧ window free ∧ injection tried). */
39
+ export const MAX_REDELIVER_ATTEMPTS = 3;
40
+ /** A deferred wake older than this is given up even if the TUI never went idle. */
41
+ export const PENDING_WAKE_TTL_MS = 2 * 60 * 60_000;
42
+ export const PENDING_WAKE_POLL_MS = 15_000;
32
43
  /** Seat-folder file that opts a human pane in to wake injection (presence is the switch). */
33
44
  const WAKE_OPT_IN_FILE = 'wake-inject';
34
45
 
@@ -67,6 +78,8 @@ export interface WakeDeps {
67
78
  herdrStatus?: (paneId: string, signal: AbortSignal) => Promise<string>;
68
79
  herdrInject?: (paneId: string, line: string, signal: AbortSignal) => Promise<void>;
69
80
  warn?: (line: string) => void;
81
+ /** Unread count for an address before a deferred re-knock (H4-FOLLOW ③). null = «모름» (never drops). Default: the msg ledger. */
82
+ readUnread?: (address: string) => number | null;
70
83
  }
71
84
 
72
85
  function isLive(deps: WakeDeps): boolean {
@@ -127,18 +140,28 @@ function claimDbPath(deps: WakeDeps): string {
127
140
 
128
141
  interface WakeClaim { db: string; address: string; token: string }
129
142
 
143
+ function openWakeDb(db: string): Database {
144
+ mkdirSync(dirname(db), { recursive: true });
145
+ const conn = new Database(db);
146
+ try {
147
+ conn.exec('PRAGMA busy_timeout = 2000');
148
+ conn.exec('CREATE TABLE IF NOT EXISTS wake_claims (address TEXT PRIMARY KEY, at INTEGER NOT NULL, token TEXT NOT NULL)');
149
+ conn.exec(`CREATE TABLE IF NOT EXISTS pending_wakes (address TEXT PRIMARY KEY, first_msg_id INTEGER NOT NULL,
150
+ last_msg_id INTEGER NOT NULL, last_from TEXT NOT NULL, count INTEGER NOT NULL, since INTEGER NOT NULL,
151
+ attempts INTEGER NOT NULL DEFAULT 0, reason TEXT NOT NULL)`);
152
+ return conn;
153
+ } catch (error) { conn.close(); throw error; }
154
+ }
155
+
130
156
  /**
131
157
  * Atomically claim the 5-minute wake window for one address. One SQLite upsert decides it under the
132
158
  * write lock — the row is replaced only when the held claim is older than the window — so two
133
159
  * concurrent `msg post` processes cannot both win, including when both saw an expired claim.
134
160
  */
135
161
  export function claimWake(db: string, address: string, now: number): WakeClaim | null {
136
- mkdirSync(dirname(db), { recursive: true });
137
162
  const token = `${now} ${process.pid} ${randomUUID()}`;
138
- const conn = new Database(db);
163
+ const conn = openWakeDb(db);
139
164
  try {
140
- conn.exec('PRAGMA busy_timeout = 2000');
141
- conn.exec('CREATE TABLE IF NOT EXISTS wake_claims (address TEXT PRIMARY KEY, at INTEGER NOT NULL, token TEXT NOT NULL)');
142
165
  const changed = conn.query(`INSERT INTO wake_claims (address, at, token) VALUES (?1, ?2, ?3)
143
166
  ON CONFLICT(address) DO UPDATE SET at = excluded.at, token = excluded.token WHERE wake_claims.at <= ?2 - ?4`)
144
167
  .run(address, now, token, WAKE_DEBOUNCE_MS).changes;
@@ -278,5 +301,184 @@ export async function wakeRecipient(address: string, msgId: number, message: { f
278
301
  debug.log('msg.wake', outcome.result === 'skipped' ? 'skipped' : outcome.wake, { address, msgId, wake: outcome.wake, result: outcome.result, reason: outcome.reason ?? outcome.source,
279
302
  ...(outcome.target ? { target: outcome.target } : {}) });
280
303
  } catch { /* reporting never turns a wake outcome into a throw */ }
304
+ try { settlePending(outcome, message.from, deps); } catch { /* the deferral ledger never turns a post into a throw */ }
281
305
  return outcome;
282
306
  }
307
+
308
+ // ── 미룬 깨움(pending) ─────────────────────────────────────────────────────────
309
+
310
+ interface PendingWakeRow { address: string; first_msg_id: number; last_msg_id: number; last_from: string; count: number; since: number; attempts: number; reason: string }
311
+
312
+ export type PendingWakeEvent = 'deferred' | 'redelivered' | 'gave-up' | 'dropped-read';
313
+
314
+ function logPending(event: PendingWakeEvent, data: Record<string, unknown>): void {
315
+ try { debug.log('loops.msg-wake', event, data); } catch { /* observability is fail-open */ }
316
+ }
317
+
318
+ /**
319
+ * After a fresh wake: a pty-inject TUI that was busy or inside the 5-minute window is remembered (not dropped);
320
+ * an injected wake absorbs whatever was pending for that address (the line tells the reader to list everything).
321
+ */
322
+ function settlePending(outcome: WakeOutcome, from: string, deps: WakeDeps): void {
323
+ if (!isLive(deps)) return;
324
+ // TTL 은 폴러 «밖»에서도 닫는다 — TUI 가 죽은 채 안 돌아오면 그 폴러는 없다. 어느 주소든 post 가 오면 낡은 줄을 거둔다.
325
+ try { prunePendingWakes(deps); } catch { /* fail-soft */ }
326
+ if (outcome.wake !== 'pty-inject') return;
327
+ const address = outcome.address.trim().toUpperCase();
328
+ if (outcome.result === 'injected') {
329
+ // 이 줄은 «그때까지 저장된 글 전부를 읽어라»라 msgId 이하만 덮는다 — 주입 «뒤»에 저장·미뤄진 글(더 큰 id)이 섞인 줄은 남긴다.
330
+ const conn = openWakeDb(claimDbPath(deps));
331
+ try { conn.query('DELETE FROM pending_wakes WHERE address = ?1 AND last_msg_id <= ?2').run(address, outcome.msgId); } finally { conn.close(); }
332
+ return;
333
+ }
334
+ if (outcome.result !== 'busy' && outcome.result !== 'debounced') return;
335
+ const reason = outcome.result === 'debounced' ? 'debounced' : outcome.reason ?? 'busy';
336
+ const conn = openWakeDb(claimDbPath(deps));
337
+ let count = 1;
338
+ try {
339
+ // 주소당 한 줄 — 묶음이 늘어도 다시 두드림은 한 번. 범위는 id 순서로(동시 post 가 역순으로 와도 min·max),
340
+ // 보낸 이는 끝 id 의 것. 끝 id 가 바뀌면(새 글) 시도 수는 0 — 새 글은 자기 몫의 시도를 받는다. 시작 시각(TTL)은 첫 미룸 기준.
341
+ conn.query(`INSERT INTO pending_wakes (address, first_msg_id, last_msg_id, last_from, count, since, attempts, reason)
342
+ VALUES (?1, ?2, ?2, ?3, 1, ?4, 0, ?5)
343
+ ON CONFLICT(address) DO UPDATE SET first_msg_id = min(first_msg_id, excluded.first_msg_id),
344
+ last_msg_id = max(last_msg_id, excluded.last_msg_id),
345
+ last_from = CASE WHEN excluded.last_msg_id > last_msg_id THEN excluded.last_from ELSE last_from END,
346
+ attempts = CASE WHEN excluded.last_msg_id > last_msg_id THEN 0 ELSE attempts END,
347
+ count = count + 1, reason = excluded.reason`)
348
+ .run(address, outcome.msgId, from, (deps.now ?? Date.now)(), reason);
349
+ count = (conn.query('SELECT count FROM pending_wakes WHERE address = ?1').get(address) as { count: number } | null)?.count ?? 1;
350
+ } finally { conn.close(); }
351
+ logPending('deferred', { seat: address, count, reason, msgId: outcome.msgId });
352
+ }
353
+
354
+ function logGaveUp(row: PendingWakeRow, reason: string, attempts = row.attempts): void {
355
+ logPending('gave-up', { seat: row.address, count: row.count, reason, attempts, firstMsgId: row.first_msg_id, lastMsgId: row.last_msg_id });
356
+ }
357
+
358
+ /** Give up deferrals older than the TTL (a TUI that died and never came back has no poller to do it). */
359
+ export function prunePendingWakes(deps: WakeDeps = {}): number {
360
+ const db = claimDbPath(deps);
361
+ if (!existsSync(db)) return 0;
362
+ const cutoff = (deps.now ?? Date.now)() - PENDING_WAKE_TTL_MS;
363
+ const conn = openWakeDb(db);
364
+ try {
365
+ const stale = conn.query('SELECT * FROM pending_wakes WHERE since < ?1').all(cutoff) as PendingWakeRow[];
366
+ for (const row of stale) {
367
+ if (conn.query('DELETE FROM pending_wakes WHERE address = ?1 AND last_msg_id = ?2').run(row.address, row.last_msg_id).changes === 1) logGaveUp(row, 'ttl');
368
+ }
369
+ return stale.length;
370
+ } finally { conn.close(); }
371
+ }
372
+
373
+ export interface RedeliverOutcome { seat: string; result: 'redelivered' | 'gave-up' | 'dropped-read' | 'waiting' | 'failed'; count: number; reason?: string }
374
+
375
+ /** Unread count for a deferred address — injected reader first, else the msg ledger beside the claims db. Never throws. */
376
+ function readUnreadFor(address: string, deps: WakeDeps): number | null {
377
+ try {
378
+ if (deps.readUnread) return deps.readUnread(address);
379
+ return readUnreadCount(address, join(deps.stateRoot ?? effectiveInstanceRoot(), 'msg', 'messages.db'));
380
+ } catch { return null; }
381
+ }
382
+
383
+ export function redeliverLine(row: Pick<PendingWakeRow, 'address' | 'last_from' | 'first_msg_id' | 'last_msg_id' | 'count'>): string {
384
+ const ids = row.first_msg_id === row.last_msg_id ? `#${row.last_msg_id}` : `#${row.first_msg_id}..#${row.last_msg_id}`;
385
+ return `[우편 깨움 · 미룬 ${row.count}통] ${row.last_from} → ${row.address} ${ids} — eln msg list --to ${row.address} 로 읽고 처리`;
386
+ }
387
+
388
+ /**
389
+ * Re-knock deferred wakes whose TUI is now recorded idle. `target` limits the sweep to one pty (the TUI's own poller).
390
+ * Never throws per row; a row stays pending while the TUI is busy or the 5-minute window is held.
391
+ */
392
+ export async function redeliverPendingWakes(deps: WakeDeps = {}, opts: { target?: string } = {}): Promise<RedeliverOutcome[]> {
393
+ if (!isLive(deps)) return [];
394
+ const db = claimDbPath(deps);
395
+ if (!existsSync(db)) return [];
396
+ const conn = openWakeDb(db);
397
+ let rows: PendingWakeRow[];
398
+ try { rows = conn.query('SELECT * FROM pending_wakes ORDER BY since').all() as PendingWakeRow[]; } finally { conn.close(); }
399
+ const results: RedeliverOutcome[] = [];
400
+ const now = () => (deps.now ?? Date.now)();
401
+ // 지울 때는 «읽은 그 묶음»만 — 그 사이 새로 미룬 글이 오면 줄이 남아 한 번 더 두드린다.
402
+ const drop = (row: PendingWakeRow): boolean => {
403
+ const c = openWakeDb(db);
404
+ try { return c.query('DELETE FROM pending_wakes WHERE address = ?1 AND last_msg_id = ?2').run(row.address, row.last_msg_id).changes === 1; }
405
+ finally { c.close(); }
406
+ };
407
+ // 포기는 «실제로 지운» 묶음만 — 그 사이 새 글이 합쳐졌으면(끝 id 바뀜) 줄은 새 묶음으로 남고 시도 수는 0 으로 돌아가 있다.
408
+ const giveUp = (row: PendingWakeRow, reason: string) => {
409
+ if (drop(row)) {
410
+ logGaveUp(row, reason);
411
+ results.push({ seat: row.address, result: 'gave-up', count: row.count, reason });
412
+ } else results.push({ seat: row.address, result: 'waiting', count: row.count, reason: 'batch-changed' });
413
+ };
414
+ for (const row of rows) {
415
+ try {
416
+ if (row.attempts >= MAX_REDELIVER_ATTEMPTS) { giveUp(row, `attempts>=${MAX_REDELIVER_ATTEMPTS}`); continue; }
417
+ if (now() - row.since > PENDING_WAKE_TTL_MS) { giveUp(row, 'ttl'); continue; }
418
+ // H4-FOLLOW ③ — 그 사이 이미 읽었으면(미읽음 0) 재두드림하지 않고 줄을 지운다(10-16 OPT 가 ack 한 #9250 재두드림).
419
+ // «모름»(원장 없음·못 읽음)은 지우지 않는다 — 0 으로 읽지 않는다.
420
+ const unread = readUnreadFor(row.address, deps);
421
+ if (unread === 0) {
422
+ if (drop(row)) {
423
+ logPending('dropped-read', { seat: row.address, count: row.count, attempts: row.attempts, firstMsgId: row.first_msg_id, lastMsgId: row.last_msg_id });
424
+ results.push({ seat: row.address, result: 'dropped-read', count: row.count });
425
+ } else results.push({ seat: row.address, result: 'waiting', count: row.count, reason: 'batch-changed' });
426
+ continue;
427
+ }
428
+ const resolution = resolveWake(row.address, deps);
429
+ if (resolution.optIn === false) { giveUp(row, 'human-pane-no-opt-in'); continue; }
430
+ // TUI 가 없거나(재기동 중) 다른 pty 의 몫이면 이번 틱은 건너뛴다 — 수명은 TTL 이 닫는다.
431
+ if (resolution.wake !== 'pty-inject' || !resolution.target) continue;
432
+ if (opts.target !== undefined && resolution.target !== opts.target) continue;
433
+ const tui = (deps.listPtys ?? listPtyManifestRows)().find(item => item.id === resolution.target);
434
+ if (!tui?.alive || tui.turnState !== 'idle') { results.push({ seat: row.address, result: 'waiting', count: row.count, reason: `turn=${tui?.turnState ?? 'unknown'}` }); continue; }
435
+ const claim = claimWake(db, row.address, now());
436
+ // 5분 창 안 — 버림이 아니라 미룸: 줄은 그대로, 다음 틱.
437
+ if (!claim) { results.push({ seat: row.address, result: 'waiting', count: row.count, reason: 'debounced' }); continue; }
438
+ const c = openWakeDb(db);
439
+ try { c.query('UPDATE pending_wakes SET attempts = attempts + 1 WHERE address = ?1 AND last_msg_id = ?2').run(row.address, row.last_msg_id); } finally { c.close(); }
440
+ let injected = false;
441
+ try {
442
+ const inject = await (deps.ptyInjector ?? defaultPtyInjector)();
443
+ let timer: ReturnType<typeof setTimeout> | undefined;
444
+ await Promise.race([inject(resolution.target, redeliverLine(row)), new Promise<never>((_, reject) => {
445
+ timer = setTimeout(() => reject(new Error('timeout')), deps.timeoutMs ?? WAKE_TIMEOUT_MS);
446
+ })]).finally(() => clearTimeout(timer));
447
+ injected = true;
448
+ } catch (error) {
449
+ releaseClaim(claim);
450
+ const reason = error instanceof Error ? error.message : String(error);
451
+ // 상한에 닿은 실패는 그 자리에서 닫는다 — 다음 틱을 기다리지 않는다.
452
+ if (row.attempts + 1 >= MAX_REDELIVER_ATTEMPTS) giveUp({ ...row, attempts: row.attempts + 1 }, `attempts>=${MAX_REDELIVER_ATTEMPTS}:${reason}`);
453
+ else {
454
+ logPending('deferred', { seat: row.address, count: row.count, reason: `inject-failed:${reason}`, attempts: row.attempts + 1 });
455
+ results.push({ seat: row.address, result: 'failed', count: row.count, reason });
456
+ }
457
+ }
458
+ if (injected) {
459
+ drop(row);
460
+ logPending('redelivered', { seat: row.address, count: row.count, reason: row.reason, attempts: row.attempts + 1, target: resolution.target, firstMsgId: row.first_msg_id, lastMsgId: row.last_msg_id });
461
+ results.push({ seat: row.address, result: 'redelivered', count: row.count });
462
+ }
463
+ } catch (error) {
464
+ results.push({ seat: row.address, result: 'failed', count: row.count, reason: error instanceof Error ? error.message : String(error) });
465
+ }
466
+ }
467
+ return results;
468
+ }
469
+
470
+ /**
471
+ * The TUI's own poller: every tick, re-knock wakes deferred for this pty once its turn is recorded idle.
472
+ * Reuses the manifest `turn_state` the TUI already writes (#27485) — no new IPC. Unref'd; never throws.
473
+ */
474
+ export function startPendingWakePoller(surfaceId: string, deps: WakeDeps = {}, intervalMs = PENDING_WAKE_POLL_MS): () => void {
475
+ let inFlight = false;
476
+ const tick = () => {
477
+ if (inFlight) return;
478
+ inFlight = true;
479
+ void redeliverPendingWakes(deps, { target: surfaceId }).catch(() => []).finally(() => { inFlight = false; });
480
+ };
481
+ const timer = setInterval(tick, intervalMs);
482
+ (timer as { unref?: () => void }).unref?.();
483
+ return () => clearInterval(timer);
484
+ }
@@ -28,7 +28,18 @@ export function loopCategory(id: string): string {
28
28
  return `loop.${id}`;
29
29
  }
30
30
 
31
- /** Emit an RFC loop event, retaining absent context as a measurable list. */
31
+ /** Collapse whitespace runs so a value stays on a single line. */
32
+ function oneLine(text: string): string {
33
+ return text.replace(/\s+/g, ' ').trim();
34
+ }
35
+
36
+ /**
37
+ * Emit an RFC loop event, retaining absent context as a measurable list. The approval
38
+ * observation ('hitl') settles the «왜 한 줄 ⊕ 근거 포인터» convention at this boundary:
39
+ * reason and sourceRef are collapsed onto a copy as single-line strings, so every approval
40
+ * emission carries a one-line why and evidence pointer without mutating caller data or
41
+ * fabricating fields REQUIRED_CONTEXT reports as missing.
42
+ */
32
43
  export function loopEvent(loopId: string, event: string, data: Record<string, unknown>): void {
33
44
  if (!validId(loopId)) {
34
45
  reject('invalid-loop-id');
@@ -44,9 +55,14 @@ export function loopEvent(loopId: string, event: string, data: Record<string, un
44
55
  reject('invalid-data');
45
56
  return;
46
57
  }
58
+ const payload: Record<string, unknown> = { ...data };
59
+ if (event === 'hitl') {
60
+ if (typeof payload.reason === 'string') payload.reason = oneLine(payload.reason);
61
+ if (typeof payload.sourceRef === 'string') payload.sourceRef = oneLine(payload.sourceRef);
62
+ }
47
63
  const missingRequired = REQUIRED_CONTEXT.filter(field =>
48
- typeof data[field] !== 'string' || !(data[field] as string).trim());
49
- debug.log(`loop.${loopId}`, event, { ...data, loopId, missingRequired });
64
+ typeof payload[field] !== 'string' || !(payload[field] as string).trim());
65
+ debug.log(`loop.${loopId}`, event, { ...payload, loopId, missingRequired });
50
66
  if (event === 'tick' && (data.outcome === 'failed' || data.status === 'failed')) {
51
67
  try {
52
68
  recordFailureEvent({ source: 'loop-tick', kind: event, ref: loopId,