elanous 0.2.32 → 0.2.35

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 (753) 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/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{997ce8a5-2ad501927003b41b.js → 0368f25d-ad84af92fefee68f.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +32 -0
  7. package/apps/pwa/out/_next/static/chunks/{5525.86debf231e181813.js → 1530.0e612369b6fd7ea0.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +41 -0
  9. package/apps/pwa/out/_next/static/chunks/{7391.ebae168025c91b6a.js → 1674.2a65fd5668e1cc5f.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +24 -0
  11. package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +74 -0
  12. package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/{7947-d0759430f8042240.js → 1993-c0007b7ad0c0ef41.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/{60fb96c9.360b01d8d4927b7b.js → 1c735de4.f993a30bbd3c57c4.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{5307.60f2209e809bd627.js → 2018.a741297bce759490.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +148 -0
  23. package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/{3902.e1e71aaac850a045.js → 2625.5d870aa999f33db1.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +77 -0
  26. package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +62 -0
  29. package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +90 -0
  31. package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{4174.92ed04922709ac9e.js → 3408.21d1570602f4b6c0.js} +2 -2
  33. package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/359-2250789a2d3499e7.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +36 -0
  37. package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{9398-6aed28f0a96d0d6f.js → 3677-4215f826f6aa68e5.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +79 -0
  40. package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +122 -0
  42. package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/{7371.80c05c653b08e907.js → 4346.8601952e73bf5803.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/4441-875b97f5821ed58a.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +286 -0
  46. package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +24 -0
  49. package/apps/pwa/out/_next/static/chunks/{2940-619bfd8ef337c611.js → 4705-194e64e47ca0fbdb.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/{1335-6731f392c35216eb.js → 4718-17d2af636796a330.js} +3 -3
  51. package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/{7502.ccd5b8065545edd0.js → 5101.e32282718f99ed6f.js} +2 -2
  57. package/apps/pwa/out/_next/static/chunks/{2001.6c7e0fd02190886c.js → 5154.a118f3957ed14e36.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +23 -0
  61. package/apps/pwa/out/_next/static/chunks/{5173-233188df2f42c626.js → 5483-dd5d7697f6bdb106.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +94 -0
  63. package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{3267c0e6.d34854e7b3984a6e.js → 56d372f2.681ab4ec4c998fae.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +287 -0
  67. package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/{6433.fc573af22bbc9ab7.js → 6236.e18cced2d5ba6afb.js} +17 -17
  74. package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +8 -0
  75. package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +27 -0
  78. package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +93 -0
  80. package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/{6544cb48.a87fe20ddf137be2.js → 6b89c8bd.24df687a820500b3.js} +1 -1
  83. package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +149 -0
  87. package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +104 -0
  88. package/apps/pwa/out/_next/static/chunks/{2871.0bca6137343b3f76.js → 7385.50a308a477bdfc21.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/{2985.39ccdecd36848b12.js → 7644.17e37ba6f1d4226e.js} +4 -4
  90. package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/{8018.a32663f04a6fc5f9.js → 8219.baa715d1deee80e2.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/{4111.2bfedbb233ad206b.js → 8256.e3ac8421a71f82f4.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/{2213.2ccbada92c33ea0f.js → 8444.4c26b379ca2b9596.js} +17 -17
  100. package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +226 -0
  101. package/apps/pwa/out/_next/static/chunks/8557-e7898769499cd56f.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +64 -0
  104. package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{6092.a7f99e68b188ca66.js → 8637.47f5b35afcae5e6b.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{3278.2e5d15b53a4091c8.js → 9214.63a510d39178e997.js} +2 -2
  111. package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +115 -0
  112. package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/{9221.7d22eaf353a2d4e7.js → 938.66ad1ef0e5abae1c.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/{6157.0c9af021de4fb31b.js → 9512.5a76207a730e0491.js} +2 -2
  115. package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{69-0a2c8209c6d4a158.js → 967-2aa39b2d04b4c690.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/{7862.30a8d6482bb238eb.js → 9952.5daf5f94063a2b3e.js} +1 -1
  120. package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/{page-a27b1e5a0d1b308f.js → page-406b47745b53f269.js} +1 -1
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/{page-32866485913d6a04.js → page-47c5d4db587266bd.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/{page-04d00ae9b0890349.js → page-dc0a532f5b9196ec.js} +1 -1
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{7a1887c4.f36b88fba31da8fd.js → b2f6ba4d.fb56fdbbf798df33.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/b6705a4f-b9a9047de9eecfd0.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/{b313b242.cb1265eea908db61.js → cbe38a15.5a032598e39970c6.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/{409f1e57.d0649fc47691ebfe.js → d06f9160.a3d9062f95de8ad7.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/{0bb0ff36.b8d418980951e5ba.js → da5176c6.6773b6c5994e1d99.js} +7 -7
  178. package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +1 -0
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +22 -22
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +22 -22
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +22 -22
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +21 -21
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +21 -21
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +22 -22
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +21 -21
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +22 -22
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +22 -22
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +21 -21
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +22 -22
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +22 -22
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +22 -22
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +22 -22
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +21 -21
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +21 -21
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +22 -22
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +22 -22
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +22 -22
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +21 -21
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +21 -21
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +22 -22
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +22 -22
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +21 -21
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +21 -21
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +21 -21
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +22 -22
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +22 -22
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +21 -21
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +22 -22
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +22 -22
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +22 -22
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +22 -22
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +22 -22
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +21 -21
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +22 -22
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +22 -22
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +22 -22
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +22 -22
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +22 -22
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +22 -22
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +22 -22
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +22 -22
  277. package/bin/elanous.mjs +10 -2
  278. package/catalog/providers/opengateway.yaml +25 -0
  279. package/docs/goal-context/01-goal-authoring-checks.md +8 -0
  280. package/docs/goal-context/03-verification-menu.md +24 -0
  281. package/docs/goal-context/04-mock-scenarios.md +100 -0
  282. package/docs/goal-context/README.md +5 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/default-loop.yaml +76 -5
  285. package/graphs/document-loop.yaml +71 -4
  286. package/graphs/implement-loop.yaml +59 -4
  287. package/graphs/journey/SCENARIOS.md +33 -0
  288. package/graphs/journey/recipes.yaml +1 -0
  289. package/graphs/journey/ta-journey.yaml +94 -0
  290. package/graphs/journey/units/author/author.yaml +20 -0
  291. package/graphs/journey/units/author/recipes.yaml +2 -0
  292. package/graphs/journey/units/author-dialogue/author-dialogue.yaml +42 -0
  293. package/graphs/journey/units/author-dialogue/observe.ts +96 -0
  294. package/graphs/journey/units/author-dialogue/recipes.yaml +10 -0
  295. package/graphs/journey/units/green/green.yaml +20 -0
  296. package/graphs/journey/units/green/recipes.yaml +2 -0
  297. package/graphs/journey/units/pr/pr.yaml +20 -0
  298. package/graphs/journey/units/pr/recipes.yaml +2 -0
  299. package/graphs/journey/units/remember/recipes.yaml +2 -0
  300. package/graphs/journey/units/remember/remember.yaml +20 -0
  301. package/graphs/operate-loop.yaml +71 -4
  302. package/graphs/overlays/stop-heal-ladder.yaml +1 -1
  303. package/graphs/release-rehearsal/recipes.yaml +1 -1
  304. package/graphs/release-rehearsal/release-rehearsal.yaml +2 -2
  305. package/graphs/research-loop.yaml +159 -4
  306. package/graphs/test-diet/test-diet.yaml +3 -0
  307. package/graphs/units/dispatch-pod/dispatch-pod.yaml +50 -0
  308. package/graphs/units/dispatch-pod/recipes.yaml +14 -0
  309. package/graphs/units/entry/entry.yaml +50 -0
  310. package/graphs/units/entry/recipes.yaml +14 -0
  311. package/graphs/units/harvest/harvest.yaml +25 -0
  312. package/graphs/units/harvest/recipes.yaml +4 -0
  313. package/graphs/units/land/land.yaml +45 -0
  314. package/graphs/units/land/recipes.yaml +3 -0
  315. package/graphs/units/recipes.yaml +7 -0
  316. package/graphs/units/recover.yaml +39 -0
  317. package/graphs/units/reflect/recipes.yaml +3 -0
  318. package/graphs/units/reflect/reflect.yaml +15 -0
  319. package/graphs/units/task-agent-recover-review.yaml +17 -0
  320. package/graphs/units/task-agent-recover-wait.yaml +17 -0
  321. package/graphs/units/verbs/make-doc.yaml +34 -0
  322. package/graphs/units/verbs/recipes.yaml +33 -0
  323. package/graphs/units/verbs/verify.yaml +55 -0
  324. package/graphs/work/recipes.yaml +2 -0
  325. package/graphs/work/research-report.yaml +28 -0
  326. package/package.json +1 -1
  327. package/scripts/ci-typecheck-changed.ts +69 -16
  328. package/scripts/coord-role-pack.ts +19 -6
  329. package/scripts/docs/generate-graph-anatomy.ts +19 -2
  330. package/scripts/graph-harvest-unit.ts +22 -0
  331. package/scripts/graph-units/journey-stub.ts +6 -0
  332. package/scripts/graph-units/land-enqueue.ts +78 -0
  333. package/scripts/graph-units/recover-plan.ts +15 -0
  334. package/scripts/graph-units/reflect-node.ts +88 -0
  335. package/scripts/graph-units/ta-journey-observe.ts +109 -0
  336. package/scripts/graph-units/verbs/headless-goal-loop.ts +152 -0
  337. package/scripts/graph-units/verbs/verify-stage.ts +75 -0
  338. package/scripts/graph-units/verbs-envelope.ts +139 -0
  339. package/scripts/hq/standby-snapshot.ts +30 -0
  340. package/scripts/public-export.ts +43 -1
  341. package/scripts/ratchets/train-side-invariants.sh +45 -0
  342. package/scripts/release-loop/auto-approve-node.ts +30 -16
  343. package/scripts/release-loop/docs-land-node.ts +21 -5
  344. package/scripts/release-loop/gate-baseline-receipt.ts +155 -0
  345. package/scripts/release-loop/gate-closure.ts +5 -1
  346. package/scripts/release-loop/gate-diff.ts +1 -1
  347. package/scripts/release-loop/gate-node.ts +675 -49
  348. package/scripts/release-loop/gate-partial.ts +68 -14
  349. package/scripts/release-loop/gate-radius-live.ts +177 -0
  350. package/scripts/release-loop/gate-radius-shadow.ts +138 -0
  351. package/scripts/release-loop/gate-traffic-mode.ts +128 -0
  352. package/scripts/release-loop/gate-triage-node.ts +5 -2
  353. package/scripts/release-loop/known-issues-0.2.33-approved.json +194 -0
  354. package/scripts/release-loop/known-issues-node.ts +77 -8
  355. package/scripts/release-loop/node-verdict.ts +12 -1
  356. package/scripts/release-loop/npm-publish-node.ts +66 -12
  357. package/scripts/release-loop/preflight.ts +93 -7
  358. package/scripts/release-loop/probe-pod-host-dirs-live.ts +5 -0
  359. package/scripts/release-loop/publish-node.ts +60 -9
  360. package/scripts/release-loop/pwa-remote.ts +166 -0
  361. package/scripts/release-loop/pwa-scenarios-node.ts +19 -3
  362. package/scripts/release-loop/resume-cap.ts +56 -0
  363. package/scripts/release-loop/resume-release.ts +155 -19
  364. package/scripts/release-loop/resume.ts +24 -1
  365. package/scripts/release-loop/tui-sim-node.ts +8 -2
  366. package/scripts/release-loop/unattended-release.ts +10 -2
  367. package/scripts/test-diet/lib.ts +6 -1
  368. package/scripts/test-diet/node.ts +194 -13
  369. package/scripts/ux-sim.ts +39 -0
  370. package/src/acp/core-turn-bridge.ts +20 -5
  371. package/src/acp/reasoning-thought-buffer.ts +45 -0
  372. package/src/agent/self-ambient-cache.ts +8 -0
  373. package/src/agent-substrate/acp-reviewer.ts +90 -15
  374. package/src/agent-substrate/pr-reviewer.ts +17 -0
  375. package/src/agent-substrate/review-finding-key.ts +53 -0
  376. package/src/agent-substrate/self-review-cli.ts +188 -18
  377. package/src/ask-user-question/pending-questions.ts +1 -1
  378. package/src/autopilot/pr-manager.ts +393 -65
  379. package/src/budget/run-usage-rollup.ts +1 -0
  380. package/src/chat/index.ts +0 -2
  381. package/src/chat/pickers/modal-runtime.ts +10 -27
  382. package/src/chat/tool-render/dispatch.ts +62 -10
  383. package/src/cli/agent-cli.ts +13 -2
  384. package/src/cli/control-cli.ts +24 -7
  385. package/src/cli/doctor-cli.ts +4 -1
  386. package/src/cli/guardian-cli.ts +10 -0
  387. package/src/cli/light-entry-route.ts +75 -0
  388. package/src/cli/llm-detect-cli.ts +23 -1
  389. package/src/cli/pod-cli.ts +77 -3
  390. package/src/cli/pr-cli.ts +81 -14
  391. package/src/cli/pr-land-base-read.ts +2 -0
  392. package/src/cli/pr-land-overlap-evidence.ts +154 -16
  393. package/src/cli/pr-land-pod-shadow-job.ts +3 -0
  394. package/src/cli/pr-land-recovery.ts +64 -0
  395. package/src/cli/pty-takeover-cli.ts +6 -5
  396. package/src/cli/release-checklist-read-cli.ts +79 -0
  397. package/src/cli/release-cli.ts +189 -132
  398. package/src/cli/release-run-if-ready.ts +29 -28
  399. package/src/cli/release-single-flight.ts +178 -0
  400. package/src/cli/remote-resolve.ts +1 -1
  401. package/src/cli/root-tui-entry.ts +48 -0
  402. package/src/cli/seat-cli.ts +37 -0
  403. package/src/cli/setup-cli.ts +40 -1
  404. package/src/cli/sim-cli.ts +22 -0
  405. package/src/cli/tasks-cli.ts +667 -14
  406. package/src/cli/usage-cli.ts +13 -11
  407. package/src/clipboard/history.ts +179 -13
  408. package/src/config.ts +8 -1
  409. package/src/control/control-status.ts +8 -3
  410. package/src/control-plane/ledger.ts +5 -3
  411. package/src/control-plane/server.ts +5 -3
  412. package/src/core-turn/short-question.ts +5 -1
  413. package/src/dashboard/chat/acp-chat.ts +8 -0
  414. package/src/dashboard/index.ts +114 -77
  415. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +3 -0
  416. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  417. package/src/dashboard/input/seat-autowake.ts +56 -3
  418. package/src/dashboard/render/theme-resolver.ts +33 -4
  419. package/src/dashboard/seat-boot-preamble.ts +1 -1
  420. package/src/dashboard/slash-runtime/ask-clarification.ts +48 -0
  421. package/src/dashboard/slash-runtime/dashboard-delegated-slashes.ts +68 -0
  422. package/src/dashboard/slash-runtime/dashboard-handlers.ts +62 -322
  423. package/src/dashboard/slash-runtime/dashboard-log-slash.ts +208 -0
  424. package/src/dashboard/turn-preamble.ts +47 -1
  425. package/src/dashboard/turn-stream-formatter.ts +34 -1
  426. package/src/dashboard/turn-stream-runtime.ts +2 -0
  427. package/src/dashboard/turn-typeahead-prompt-zone.ts +67 -0
  428. package/src/domains/guardian-tick.ts +54 -2
  429. package/src/domains/outbound-alert.ts +3 -2
  430. package/src/domains/schedule-registry.ts +16 -2
  431. package/src/extractors.ts +3 -1
  432. package/src/git-fs/idle-refresh.ts +76 -0
  433. package/src/git-fs/index.ts +18 -7
  434. package/src/git-fs/retry.ts +4 -0
  435. package/src/git-fs/runner.ts +2 -0
  436. package/src/git-fs/worktree.ts +42 -4
  437. package/src/graph-kinds/registry.ts +2 -2
  438. package/src/graph-runner/runner.ts +267 -54
  439. package/src/grok/credential.ts +15 -3
  440. package/src/harness/harness-cli-command.ts +151 -38
  441. package/src/harness/harness-incidents.ts +37 -0
  442. package/src/harness/harness-pod-dispatch.ts +38 -1
  443. package/src/harness/harness-salvage-cli.ts +14 -2
  444. package/src/harness/launch-utterance-ledger.ts +208 -0
  445. package/src/impl-discipline/detector.ts +16 -4
  446. package/src/impl-discipline/system-prompt.ts +11 -0
  447. package/src/impl-discipline/types.ts +2 -2
  448. package/src/index.ts +683 -146
  449. package/src/intake-plane/already-built-hint.ts +91 -0
  450. package/src/intake-plane/requirement-funnel.ts +29 -1
  451. package/src/knowhow/pack.ts +199 -0
  452. package/src/light-cli.ts +111 -0
  453. package/src/llm/opengateway-models.ts +11 -0
  454. package/src/llm/provider-credentials.ts +14 -0
  455. package/src/llm/provider-detect.ts +8 -1
  456. package/src/llm/runtime-model-family.ts +2 -1
  457. package/src/llm.ts +268 -65
  458. package/src/loops/budget.ts +95 -0
  459. package/src/loops/msg-wake.ts +8 -2
  460. package/src/loops/orchestrator/traffic.ts +132 -1
  461. package/src/model-tier/llm-tier-map.ts +15 -1
  462. package/src/mss/logging/log-store.ts +4 -5
  463. package/src/native-tool-catalog.ts +15 -1
  464. package/src/nexus/api/graphs-api.ts +22 -3
  465. package/src/nexus/api/http-server.ts +1 -1
  466. package/src/nexus/api/loop-resources.ts +13 -1
  467. package/src/nexus/api/ops-api.ts +12 -0
  468. package/src/oauth/claude-pty-maturity.ts +66 -0
  469. package/src/oauth/codex-access-mirror.ts +55 -0
  470. package/src/oauth/codex.ts +7 -0
  471. package/src/oauth/fallback-chain.ts +1 -1
  472. package/src/onboarding/first-run.ts +5 -3
  473. package/src/onboarding/non-interactive.ts +64 -13
  474. package/src/onboarding.ts +10 -2
  475. package/src/ops/overview-collect.ts +54 -4
  476. package/src/ops/overview.ts +31 -7
  477. package/src/pr-tool/census.ts +282 -30
  478. package/src/pr-tool/inflight-registry.ts +160 -7
  479. package/src/prompt-library/gpt6-family-addendum.ts +14 -1
  480. package/src/pty-shell/herdr-agent-report.ts +442 -0
  481. package/src/pty-shell/pty-control-ipc.ts +5 -5
  482. package/src/pty-shell/pty-manifest.ts +7 -0
  483. package/src/pty-shell/pty-write-outcome.ts +24 -6
  484. package/src/pty-shell/tui-run-state.ts +117 -3
  485. package/src/release-loop/checklist.ts +103 -5
  486. package/src/release-loop/feature-store.ts +19 -2
  487. package/src/release-loop/landed-but-yellow.ts +129 -5
  488. package/src/release-loop/light-rc.ts +1 -0
  489. package/src/release-loop/placement.ts +10 -3
  490. package/src/release-loop/rehearsal.ts +205 -25
  491. package/src/release-loop/rubric.ts +16 -2
  492. package/src/repl/index.ts +174 -17
  493. package/src/repl/ops-msg-bridge.ts +32 -0
  494. package/src/self-dev/ask-launch-flow.ts +44 -5
  495. package/src/self-dev/cli-help-text.ts +17 -0
  496. package/src/self-dev/dev-cli.ts +11 -9
  497. package/src/self-dev/draft-residue.ts +37 -1
  498. package/src/self-dev/draft-sweep.ts +188 -13
  499. package/src/self-dev/draft-triage-rules.ts +107 -0
  500. package/src/self-dev/fleet-reduce.ts +3 -2
  501. package/src/self-dev/graph-front-nodes.ts +112 -0
  502. package/src/self-dev/graph-journey-nodes.ts +11 -6
  503. package/src/self-dev/graph-journey-route.ts +60 -6
  504. package/src/self-dev/harness-run-cli.ts +3 -15
  505. package/src/self-dev/orchestrate-cli-text.ts +10 -0
  506. package/src/self-dev/orchestrate.ts +79 -3
  507. package/src/self-dev/review-provider-fallback.ts +35 -1
  508. package/src/self-dev/run-supervisor.ts +107 -12
  509. package/src/self-dev/run-types.ts +5 -1
  510. package/src/self-dev/self-orchestrate-runtime.ts +1 -11
  511. package/src/self-dev/shard-aggregate.ts +42 -5
  512. package/src/self-dev/shard-run.ts +8 -1
  513. package/src/self-implement/blocked-draft-policy.ts +20 -0
  514. package/src/self-implement/classify-shadow.ts +3 -7
  515. package/src/self-implement/doctor-visit.ts +18 -1
  516. package/src/self-implement/gate-baseline.ts +95 -12
  517. package/src/self-implement/gate-cli.ts +1 -0
  518. package/src/self-implement/gate-remote.ts +40 -25
  519. package/src/self-implement/goal-author-decompose.ts +275 -47
  520. package/src/self-implement/goal-author-identity.ts +66 -0
  521. package/src/self-implement/goal-author-preservation-obligation.ts +35 -0
  522. package/src/self-implement/goal-author.ts +254 -120
  523. package/src/self-implement/goal-elaborate.ts +295 -0
  524. package/src/self-implement/goal-refine.ts +270 -0
  525. package/src/self-implement/graph-overlay-yaml.ts +28 -2
  526. package/src/self-implement/graph-unit-flatten.ts +103 -0
  527. package/src/self-implement/graph-unit-resolver.ts +107 -0
  528. package/src/self-implement/graph-yaml.ts +151 -1
  529. package/src/self-implement/headless-elanous-driver.ts +1 -0
  530. package/src/self-implement/knowhow-pack.ts +103 -0
  531. package/src/self-implement/orchestrator.ts +160 -45
  532. package/src/self-implement/pod-warm-pool-profile.ts +45 -0
  533. package/src/self-implement/review-mustfix-class.ts +203 -0
  534. package/src/self-implement/rework-budget-judgment.ts +5 -8
  535. package/src/self-implement/run-stop.ts +17 -2
  536. package/src/self-implement/running-runs.ts +29 -1
  537. package/src/self-implement/scope-adjacency.ts +113 -0
  538. package/src/self-implement/seams.ts +138 -4
  539. package/src/self-implement/stop-autoheal.ts +112 -2
  540. package/src/session/chat.ts +27 -0
  541. package/src/session-runtime/index.ts +1 -1
  542. package/src/session-runtime/retry-policy.ts +2 -1
  543. package/src/setup/llm-env-detect.ts +5 -0
  544. package/src/skills/tools/agent.ts +140 -14
  545. package/src/skills/tools/msg-ask.ts +100 -0
  546. package/src/skills/tools/ops-ledger.ts +2 -1
  547. package/src/skills/tools/schedule-wakeup.ts +66 -17
  548. package/src/steward/hold-hygiene.ts +24 -6
  549. package/src/steward/release-day.ts +7 -3
  550. package/src/steward/triage-budget.ts +194 -0
  551. package/src/steward/triage.ts +57 -7
  552. package/src/task-agent/actions.ts +69 -1
  553. package/src/task-agent/green-sweep.ts +4 -0
  554. package/src/task-agent/harvest-unit.ts +141 -0
  555. package/src/task-agent/live-moves.ts +257 -15
  556. package/src/task-agent/parent-host.ts +216 -6
  557. package/src/task-agent/postland-card.ts +80 -1
  558. package/src/task-agent/rejudge-on-head.ts +112 -26
  559. package/src/task-agent/repair-branch-cas.ts +4 -1
  560. package/src/task-agent/repair-cycle.ts +16 -3
  561. package/src/task-agent/run-identity.ts +4 -3
  562. package/src/task-agent/shadow.ts +19 -3
  563. package/src/task-agent/task-hand.ts +96 -36
  564. package/src/task-agent/tier-split.ts +127 -0
  565. package/src/task-agent/work-order-build.ts +24 -0
  566. package/src/task-agent/work-order.ts +3 -2
  567. package/src/task-orchestrator/generator-prompt.ts +60 -11
  568. package/src/task-orchestrator/surfaces/pod-account-broker.ts +72 -3
  569. package/src/task-orchestrator/surfaces/pod-child-providers.ts +1 -1
  570. package/src/task-orchestrator/surfaces/pod-command-job.ts +47 -8
  571. package/src/task-orchestrator/surfaces/pod-image-source.ts +89 -1
  572. package/src/task-orchestrator/surfaces/pod-lease.ts +17 -8
  573. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +86 -8
  574. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +8 -2
  575. package/src/task-orchestrator/surfaces/pod-pool.ts +73 -17
  576. package/src/task-orchestrator/surfaces/pod-provenance.ts +76 -0
  577. package/src/task-orchestrator/surfaces/pod-source-receive.ts +72 -4
  578. package/src/task-orchestrator/surfaces/pod-warm-provenance.ts +81 -0
  579. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +32 -5
  580. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +3 -0
  581. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +421 -0
  582. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +2 -1
  583. package/src/task-orchestrator/surfaces/self-implement-pod.ts +588 -71
  584. package/src/task-orchestrator/surfaces/self-implement.ts +106 -2
  585. package/src/tool-runtime/ops-ledger-runtimes.ts +10 -2
  586. package/src/tool-runtime/truncation-store.ts +4 -0
  587. package/src/tui/README.md +2 -0
  588. package/src/tui/chat/background-job-status.ts +54 -0
  589. package/src/tui/chat/log-view.ts +80 -0
  590. package/src/tui/entry.ts +406 -0
  591. package/src/tui/input/composer.ts +144 -0
  592. package/src/tui/seat-mail-boot.ts +59 -0
  593. package/src/tui/slash/compact.ts +100 -0
  594. package/src/tui/slash/new-session.ts +35 -0
  595. package/src/tui-client/dashboard-session.ts +10 -1
  596. package/src/tui-client/local-chat-turn.ts +239 -0
  597. package/src/tui-client/local-harness-command.ts +63 -0
  598. package/src/tui-client/local-provider-command.ts +184 -0
  599. package/src/user-config/coercion.ts +36 -0
  600. package/src/user-config.ts +22 -43
  601. package/src/ux-sim/event-monkey.ts +378 -0
  602. package/src/ux-sim/fake-bin.ts +42 -7
  603. package/src/ux-sim/fault-nextmove-dispatch.ts +266 -0
  604. package/src/ux-sim/fault-pod-codex-503.ts +211 -0
  605. package/src/ux-sim/fault-scope-escape-lift.ts +189 -0
  606. package/src/ux-sim/scenario.ts +72 -13
  607. package/src/version/packed-revision.json +1 -1
  608. package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +0 -27
  611. package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +0 -287
  612. package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +0 -62
  613. package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/1654.32de4b324981a188.js +0 -23
  617. package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +0 -149
  619. package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +0 -41
  620. package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/2898.fd781bc7cf01a7b7.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +0 -115
  629. package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +0 -64
  630. package/apps/pwa/out/_next/static/chunks/3541-ce3a5424ed5ad7f1.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +0 -36
  636. package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +0 -286
  640. package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +0 -77
  641. package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +0 -226
  642. package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +0 -79
  644. package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +0 -148
  647. package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/5458-16cd7a2b51b74c8c.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/5543.a438b8ab5a13ea47.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +0 -74
  653. package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +0 -94
  655. package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +0 -90
  661. package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +0 -32
  662. package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +0 -104
  663. package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +0 -24
  668. package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +0 -93
  669. package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +0 -8
  674. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +0 -24
  685. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/9138.b6e80031afb1d973.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/9279-d28c5b7754d010ae.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/9989.3694300e5f000b14.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-8c66a3b24a745630.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/setup/page-c219f2ed171131be.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +0 -122
  744. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +0 -1
  752. package/src/cli/g6a-registration-probe.ts +0 -16
  753. /package/apps/pwa/out/_next/static/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_ssgManifest.js +0 -0
@@ -16,6 +16,9 @@ import { createHash } from 'node:crypto';
16
16
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
17
17
  import { join, resolve } from 'node:path';
18
18
  import { debug } from '../debug/log.js';
19
+ import { resolveFreshGrokCredential } from '../grok/credential.js';
20
+ import { memoryQuantityBytes } from '../task-orchestrator/surfaces/pod-lease.js';
21
+ import { parsePodPool } from '../task-orchestrator/surfaces/pod-pool.js';
19
22
  import { shellQuote } from './task-hand.js';
20
23
 
21
24
  /** `taskAgent.parentHost` 설정 칸. */
@@ -42,6 +45,11 @@ const DEFAULT_REMOTE_ELANOUS = '$HOME/.local/share/elanous/bin/elanous';
42
45
  const DEFAULT_REMOTE_CONFIG_DIR = '$HOME/.elanous';
43
46
  const HOST_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]{0,127}$/;
44
47
 
48
+ /** Missing child-provider metadata keeps the established Pod default: the HQ Codex mirror is needed. */
49
+ export function shouldInstallRemoteCodexAccess(effectiveChildProvider: string | undefined): boolean {
50
+ return effectiveChildProvider === undefined || effectiveChildProvider === 'openai-codex';
51
+ }
52
+
45
53
  /**
46
54
  * 켜졌나 — env 가 이긴다(`off`·`0`·빈 값이면 끔). env 가 호스트를 주면 나머지 칸(cwd 등)은 설정에서 받는다.
47
55
  * cwd 가 없으면 켜지지 않는다(어디서 띄울지 모르는 원격 발사는 하지 않는다).
@@ -74,6 +82,50 @@ export function resolveParentHosts(config: ParentHostSetting | undefined, env: N
74
82
 
75
83
  /** Capacity sample is read on the host before launch. Unknown or saturated capacity is not a positive weight. */
76
84
  export interface ParentHostCapacity { approvalHeadroom: number; load: number }
85
+
86
+ export interface ParentHostPodFit {
87
+ state: 'fits' | 'never-fits' | 'unmeasured';
88
+ memoryLimit: string;
89
+ requiredBytes: number | null;
90
+ allocatableBytes: number | null;
91
+ reason: string;
92
+ }
93
+
94
+ /** Read node allocatable memory only. This preflight never creates a probe Pod. */
95
+ export function readParentHostPodFit(target: ParentHostConfig, memoryLimit: string, ssh: SshRunner = defaultSshRunner): ParentHostPodFit {
96
+ const requiredBytes = memoryQuantityBytes(memoryLimit);
97
+ const unknown = (reason: string, allocatableBytes: number | null = null): ParentHostPodFit =>
98
+ ({ state: 'unmeasured', memoryLimit, requiredBytes, allocatableBytes, reason });
99
+ if (requiredBytes === null || requiredBytes <= 0) return unknown('memory-limit-unreadable');
100
+ // Without an explicit pool, the remote current kubectl context may be unrelated to the launched Job.
101
+ if (!target.podPool) return unknown('pod-pool-unset');
102
+ let members: ReturnType<typeof parsePodPool>;
103
+ try { members = parsePodPool(target.podPool); }
104
+ catch { return unknown('pod-pool-unreadable'); }
105
+ if (!members.length) return unknown('pod-pool-empty');
106
+ let maximum = 0;
107
+ let unreadable = false;
108
+ for (const member of members) {
109
+ let result: ReturnType<SshRunner>;
110
+ try { result = ssh(target.host, `kubectl --context ${shellQuote(member.context)} get nodes -o json`, 15_000); }
111
+ catch { unreadable = true; continue; }
112
+ if (result.status !== 0) { unreadable = true; continue; }
113
+ try {
114
+ const parsed = JSON.parse(result.stdout) as { items?: Array<{ status?: { allocatable?: { memory?: unknown } } }> };
115
+ if (!Array.isArray(parsed.items) || parsed.items.length === 0) { unreadable = true; continue; }
116
+ for (const node of parsed.items) {
117
+ const raw = node?.status?.allocatable?.memory;
118
+ const bytes = typeof raw === 'string' ? memoryQuantityBytes(raw) : null;
119
+ if (bytes === null || bytes <= 0) { unreadable = true; continue; }
120
+ maximum = Math.max(maximum, bytes);
121
+ }
122
+ } catch { unreadable = true; }
123
+ }
124
+ if (maximum >= requiredBytes) return { state: 'fits', memoryLimit, requiredBytes, allocatableBytes: maximum, reason: 'node-allocatable-fits' };
125
+ if (unreadable || maximum === 0) return unknown('node-allocatable-unmeasured', maximum || null);
126
+ return { state: 'never-fits', memoryLimit, requiredBytes, allocatableBytes: maximum, reason: 'memory-never-fits' };
127
+ }
128
+
77
129
  const CAPACITY_MARKER = '__ELANOUS_PARENT_CAPACITY__';
78
130
  const APPROVAL_MARKER = '__ELANOUS_PARENT_APPROVALS__';
79
131
  const LOAD_MARKER = '__ELANOUS_PARENT_LOAD__';
@@ -159,7 +211,9 @@ export function remoteLaunchScript(target: ParentHostConfig, args: readonly stri
159
211
  const elanous = target.elanous ?? DEFAULT_REMOTE_ELANOUS;
160
212
  const configDir = target.configDir ?? DEFAULT_REMOTE_CONFIG_DIR;
161
213
  const log = remoteParentLogPath(target, env.ELANOUS_RUN_ID ?? 'unknown');
162
- const envPart = [...Object.entries(env).filter(([key]) => key !== 'ELANOUS_STATE_DIR' && /^[A-Z_][A-Z0-9_]*$/.test(key)).map(([key, value]) => `${key}=${shellQuote(value)}`), `ELANOUS_STATE_DIR=${remotePath(configDir)}`].join(' ');
214
+ // --config-dir keeps the parent's operational config/state; authStorePath()
215
+ // reads XDG_CONFIG_HOME/elanous/auth.json first, as the entrypoint test proves.
216
+ const envPart = [...Object.entries(env).filter(([key]) => key !== 'ELANOUS_STATE_DIR' && /^[A-Z_][A-Z0-9_]*$/.test(key)).map(([key, value]) => `${key}=${key === 'XDG_CONFIG_HOME' || key === 'CODEX_HOME' || key === 'GH_CONFIG_DIR' ? remotePath(value) : shellQuote(value)}`), `ELANOUS_STATE_DIR=${remotePath(configDir)}`].join(' ');
163
217
  const argv = [remotePath(elanous), '--config-dir', remotePath(configDir), ...remoteParentArgs(args, target.podPool).map(shellQuote)].join(' ');
164
218
  // 준비가 실패하면 «확실히 안 띄웠다»(not-launched=…)를 찍고 끝낸다 — 그때만 호출부가 로컬로 돌아간다.
165
219
  // 띄운 뒤에는 짧게 살아 있는지 보고 pid 를 찍는다(조기 종료면 early-exit — 역시 «안 돈다»가 확실).
@@ -170,10 +224,12 @@ export function remoteLaunchScript(target: ParentHostConfig, args: readonly stri
170
224
  }
171
225
 
172
226
  /** ssh 실행기(시험 주입). */
173
- export type SshRunner = (host: string, script: string, timeoutMs: number) => { status: number | null; stdout: string; stderr: string };
227
+ export type SshRunner = (host: string, script: string, timeoutMs: number, stdin?: string) => { status: number | null; stdout: string; stderr: string };
174
228
 
175
- export const defaultSshRunner: SshRunner = (host, script, timeoutMs) => {
176
- const result = spawnSync('ssh', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=8', '-o', 'ControlPath=none', host, script], { encoding: 'utf8', timeout: timeoutMs, maxBuffer: 32 * 1024 * 1024 });
229
+ export const defaultSshRunner: SshRunner = (host, script, timeoutMs, stdin) => {
230
+ const result = spawnSync('ssh', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=8', '-o', 'ControlPath=none', host, script], {
231
+ encoding: 'utf8', timeout: timeoutMs, maxBuffer: 32 * 1024 * 1024, ...(stdin !== undefined ? { input: stdin } : {}),
232
+ });
177
233
  return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? (result.error ? String(result.error) : '') };
178
234
  };
179
235
 
@@ -289,8 +345,27 @@ export class RemoteParentNotLaunchedError extends Error {
289
345
  * 그 밖(ssh 끊김·시간 초과·알 수 없는 답)은 «떴는지 모른다» — 일반 Error 로 던져 카드가 launch-failed 로 남게 한다
290
346
  * (⛔ 로컬로 또 띄우면 원격에 이미 뜬 부모와 중복이 될 수 있다).
291
347
  */
292
- export function launchRemoteParent(target: ParentHostConfig, args: readonly string[], env: Record<string, string>, ssh: SshRunner = defaultSshRunner): { pid: number; log: string } {
293
- const result = ssh(target.host, remoteLaunchScript(target, args, env), 30_000);
348
+ export function launchRemoteParent(
349
+ target: ParentHostConfig,
350
+ args: readonly string[],
351
+ env: Record<string, string>,
352
+ ssh: SshRunner = defaultSshRunner,
353
+ grokMirrorResolver: () => OwnerGrokAccessMirror = resolveOwnerGrokAccessMirror,
354
+ effectiveChildProvider?: string,
355
+ now: () => number = Date.now,
356
+ ): { pid: number; log: string } {
357
+ const argvProvider = childProviderFromArgs(args);
358
+ if (effectiveChildProvider && argvProvider && argvProvider !== effectiveChildProvider) {
359
+ throw new Error(`effective child provider mismatch (context=${effectiveChildProvider}, argv=${argvProvider})`);
360
+ }
361
+ const launchArgs = effectiveChildProvider && !argvProvider
362
+ ? [...args.slice(0, 2), '--child-llm-provider', effectiveChildProvider, ...args.slice(2)]
363
+ : args;
364
+ if ((effectiveChildProvider ?? argvProvider) === 'grok') {
365
+ const mirror = grokMirrorResolver();
366
+ installRemoteGrokAccess(target, mirror, ssh, now);
367
+ }
368
+ const result = ssh(target.host, remoteLaunchScript(target, launchArgs, env), 30_000);
294
369
  const log = remoteParentLogPath(target, env.ELANOUS_RUN_ID ?? 'unknown');
295
370
  const pid = Number(/^pid=(\d+)$/m.exec(result.stdout)?.[1]);
296
371
  if (result.status === 0 && Number.isSafeInteger(pid) && pid > 0) return { pid, log };
@@ -302,6 +377,141 @@ export function launchRemoteParent(target: ParentHostConfig, args: readonly stri
302
377
  throw new Error(`원격 부모 발사 결과 불확실(${target.host} · rc=${result.status}) — 중복을 피해 로컬로 다시 띄우지 않는다 · 원격에서 ELANOUS_RUN_ID=${env.ELANOUS_RUN_ID ?? '?'} 를 확인: ${(result.stderr.trim().split('\n').pop() || '').slice(0, 160)}`);
303
378
  }
304
379
 
380
+ /** The provider selection travels as an argv pair or assignment; absent remains the existing Codex default. */
381
+ function childProviderFromArgs(args: readonly string[]): string | undefined {
382
+ for (let i = 0; i < args.length; i++) {
383
+ const arg = args[i]!;
384
+ if (arg === '--child-llm-provider') return args[i + 1];
385
+ if (arg.startsWith('--child-llm-provider=')) return arg.slice('--child-llm-provider='.length);
386
+ }
387
+ return undefined;
388
+ }
389
+
390
+ export interface OwnerGrokAccessMirror { key: string; expiresAt: string }
391
+
392
+ /** Resolve/refresh on HQ and return only a still-usable subscription access token. */
393
+ export function resolveOwnerGrokAccessMirror(opts: {
394
+ home?: string;
395
+ env?: NodeJS.ProcessEnv;
396
+ now?: () => number;
397
+ execImpl?: (cmd: string, args: string[], timeoutMs: number) => void;
398
+ } = {}): OwnerGrokAccessMirror {
399
+ const credential = resolveFreshGrokCredential({
400
+ ...(opts.home ? { home: opts.home } : {}),
401
+ ...(opts.env ? { env: opts.env } : {}),
402
+ ...(opts.now ? { now: opts.now } : {}),
403
+ ...(opts.execImpl ? { execImpl: opts.execImpl } : {}),
404
+ });
405
+ if (!credential || credential.kind !== 'subscription') throw new Error('Grok subscription mirror unavailable: no-usable-subscription');
406
+ // Expiry travels with the selected credential so a second auth-file read cannot pair another scope's
407
+ // expiry with this access token if the file changes between reads.
408
+ if (!credential.expiresAt) throw new Error('Grok subscription mirror unavailable: access-state-incomplete');
409
+ const expiresAt = Date.parse(credential.expiresAt);
410
+ // Keep the same two-minute safety margin that triggers HQ refresh before remote installation.
411
+ if (!Number.isFinite(expiresAt) || expiresAt <= (opts.now ?? Date.now)() + 120_000) {
412
+ throw new Error('Grok subscription mirror unavailable: expired-or-refresh-did-not-produce-fresh-access');
413
+ }
414
+ return { key: credential.token, expiresAt: credential.expiresAt };
415
+ }
416
+
417
+ /** Install one HQ-selected Grok access token over SSH stdin; never copy refresh or other scopes. */
418
+ export function installRemoteGrokAccess(
419
+ target: ParentHostConfig,
420
+ mirror: OwnerGrokAccessMirror,
421
+ ssh: SshRunner = defaultSshRunner,
422
+ now: () => number = Date.now,
423
+ ): void {
424
+ if (!mirror || typeof mirror.key !== 'string' || !mirror.key || typeof mirror.expiresAt !== 'string'
425
+ || !Number.isFinite(Date.parse(mirror.expiresAt)) || Date.parse(mirror.expiresAt) <= now() + 120_000) {
426
+ throw new Error('invalid Grok access mirror');
427
+ }
428
+ const payload = JSON.stringify({ key: mirror.key, expires_at: mirror.expiresAt });
429
+ const code = [
430
+ 'const fs=require("node:fs"),path=require("node:path"),os=require("node:os");',
431
+ 'const p=JSON.parse(fs.readFileSync(0,"utf8"));if(!p||typeof p.key!=="string"||!p.key||typeof p.expires_at!=="string"||!Number.isFinite(Date.parse(p.expires_at))||Date.parse(p.expires_at)<=Date.now()+120000||Object.keys(p).some(k=>!new Set(["key","expires_at"]).has(k)))process.exit(2);',
432
+ 'const dir=path.join(os.homedir(),".grok"),file=path.join(dir,"auth.json"),temp=file+"."+process.pid+".tmp";fs.mkdirSync(dir,{recursive:true,mode:0o700});fs.chmodSync(dir,0o700);',
433
+ 'try{let auth={};try{const prior=JSON.parse(fs.readFileSync(file,"utf8"));if(!prior||typeof prior!=="object"||Array.isArray(prior))process.exit(3);auth=prior}catch(e){if(e.code!=="ENOENT")throw e}auth["https://auth.x.ai::hq-mirror"]={key:p.key,expires_at:p.expires_at};fs.writeFileSync(temp,JSON.stringify(auth),{mode:0o600});fs.chmodSync(temp,0o600);fs.renameSync(temp,file)}catch(e){try{fs.unlinkSync(temp)}catch{};throw e}',
434
+ ].join('');
435
+ const result = ssh(target.host, `umask 077; bun -e ${shellQuote(code)}`, 20_000, payload);
436
+ if (result.status !== 0) throw new Error(`remote Grok access mirror failed (ssh rc=${result.status ?? 'unknown'})`);
437
+ }
438
+
439
+ /** Install access-only Codex copies over SSH stdin; credentials never enter argv, shell text, or logs. */
440
+ export function installRemoteCodexAccess(target: ParentHostConfig, mirror: { elanousAuth: string; codexAuth: string; account?: string }, ssh: SshRunner = defaultSshRunner): void {
441
+ let elanous: { version?: number; providers?: Record<string, Record<string, unknown>> };
442
+ let codex: { auth_mode?: string; tokens?: Record<string, unknown> };
443
+ try {
444
+ elanous = JSON.parse(mirror.elanousAuth) as typeof elanous;
445
+ codex = JSON.parse(mirror.codexAuth) as typeof codex;
446
+ } catch { throw new Error('invalid local Codex access mirror'); }
447
+ const provider = elanous.providers?.['openai-codex'];
448
+ const elanousTokens = provider?.tokens as Record<string, unknown> | undefined;
449
+ const codexTokens = codex.tokens;
450
+ if (!Number.isSafeInteger(elanous.version) || Number(elanous.version) < 1
451
+ || typeof elanousTokens?.accessToken !== 'string' || !elanousTokens.accessToken || elanousTokens.refreshToken !== '' || elanousTokens.refresh_token !== ''
452
+ || codex.auth_mode !== 'chatgpt'
453
+ || typeof codexTokens?.access_token !== 'string' || !codexTokens.access_token || codexTokens.refresh_token !== '' || codexTokens.refreshToken !== '')
454
+ throw new Error('Codex access mirror must contain a versioned store, auth mode, access tokens, and empty refresh tokens');
455
+
456
+ const account = mirror.account ?? 'default';
457
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(account)) throw new Error('invalid Codex mirror account name');
458
+
459
+ const payload = JSON.stringify({ elanousVersion: elanous.version, provider, codexAuth: codex, account, storeKey: account === 'default' ? 'openai-codex' : `openai-codex:${account}` });
460
+ const configDir = remotePath(target.configDir ?? DEFAULT_REMOTE_CONFIG_DIR);
461
+ // HQ owns token metadata; the remote keeps only the selected named Codex credential in managed account homes.
462
+ const code = [
463
+ 'const fs=require("node:fs"),path=require("node:path"),os=require("node:os");',
464
+ 'const payload=JSON.parse(fs.readFileSync(0,"utf8"));',
465
+ 'const read=(file)=>{try{return JSON.parse(fs.readFileSync(file,"utf8"))}catch{return {}}};',
466
+ 'const write=(file,value)=>{const temp=file+"."+process.pid+".tmp";try{fs.writeFileSync(temp,JSON.stringify(value),{mode:0o600});fs.chmodSync(temp,0o600);fs.renameSync(temp,file)}catch(error){try{fs.unlinkSync(temp)}catch{};throw error}};',
467
+ 'const elanousFile=path.join(process.argv[1],"auth.json"),configDir=path.dirname(elanousFile);fs.mkdirSync(configDir,{recursive:true,mode:0o700});',
468
+ 'const elanous=read(elanousFile);if(typeof elanous.version!=="number")elanous.version=payload.elanousVersion;elanous.providers={...(elanous.providers||{})};const defaultHome=path.join(os.homedir(),".codex"),managedAccounts=path.join(configDir,"codex-accounts");for(const key of Object.keys(elanous.providers)){if(key.startsWith("openai-codex:"))delete elanous.providers[key]}fs.rmSync(managedAccounts,{recursive:true,force:true});fs.rmSync(path.join(configDir,"remote-codex-mirror.json"),{force:true});',
469
+ 'const accountHome=payload.account==="default"?defaultHome:path.join(configDir,"codex-accounts",payload.account),tokens={...(payload.provider.tokens||{}),refreshToken:"",refresh_token:""};const upsert=(key,codexHome)=>{const current=elanous.providers[key]||{};elanous.providers[key]={...current,...payload.provider,codexHome,chatGPT:{...(current.chatGPT||{}),...(payload.provider.chatGPT||{})},tokens}};upsert("openai-codex",defaultHome);if(payload.storeKey!=="openai-codex")upsert(payload.storeKey,accountHome);write(elanousFile,elanous);',
470
+ 'const incoming=payload.codexAuth;const installCodex=(home)=>{const file=path.join(home,"auth.json");fs.mkdirSync(home,{recursive:true,mode:0o700});const current=read(file),merged={...current,...incoming};merged.auth_mode=incoming.auth_mode;if(Object.prototype.hasOwnProperty.call(incoming,"last_refresh"))merged.last_refresh=incoming.last_refresh;else delete merged.last_refresh;merged.tokens={...(incoming.tokens||{}),refresh_token:"",refreshToken:""};write(file,merged)};installCodex(defaultHome);if(accountHome!==defaultHome)installCodex(accountHome);',
471
+ ].join('');
472
+ const result = ssh(target.host, `umask 077; mkdir -p ${configDir} "$HOME/.codex" && bun -e ${shellQuote(code)} ${configDir}`, 20_000, payload);
473
+ if (result.status !== 0) throw new Error(`remote Codex access mirror failed (ssh rc=${result.status ?? 'unknown'})`);
474
+ }
475
+
476
+ /** Replace the remote Codex account set with one HQ-owned, refresh-free snapshot. */
477
+ export function installRemoteCodexAccessBatch(target: ParentHostConfig, mirrors: readonly { elanousAuth: string; codexAuth: string; account: string }[], ssh: SshRunner = defaultSshRunner): void {
478
+ if (!mirrors.length) throw new Error('Codex access mirror batch is empty');
479
+ const seen = new Set<string>();
480
+ const accounts = mirrors.map((mirror) => {
481
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(mirror.account) || seen.has(mirror.account)) throw new Error('invalid or duplicate Codex mirror account name');
482
+ seen.add(mirror.account);
483
+ let elanous: { version?: number; providers?: Record<string, Record<string, unknown>> };
484
+ let codex: { auth_mode?: string; tokens?: Record<string, unknown> };
485
+ try { elanous = JSON.parse(mirror.elanousAuth); codex = JSON.parse(mirror.codexAuth); }
486
+ catch { throw new Error('invalid local Codex access mirror'); }
487
+ const provider = elanous.providers?.['openai-codex'];
488
+ const token = provider?.tokens as Record<string, unknown> | undefined;
489
+ if (!Number.isSafeInteger(elanous.version) || Number(elanous.version) < 1
490
+ || typeof token?.accessToken !== 'string' || !token.accessToken || token.refreshToken !== '' || token.refresh_token !== ''
491
+ || codex.auth_mode !== 'chatgpt' || typeof codex.tokens?.access_token !== 'string' || !codex.tokens.access_token
492
+ || codex.tokens.refresh_token !== '' || codex.tokens.refreshToken !== '')
493
+ throw new Error('Codex access mirror batch requires access tokens and empty refresh tokens');
494
+ return { account: mirror.account, provider, codexAuth: codex, version: elanous.version };
495
+ });
496
+ const configDir = remotePath(target.configDir ?? DEFAULT_REMOTE_CONFIG_DIR);
497
+ const code = [
498
+ 'const fs=require("node:fs"),path=require("node:path"),os=require("node:os");',
499
+ 'const items=JSON.parse(fs.readFileSync(0,"utf8"));const dir=process.argv[1],file=path.join(dir,"auth.json"),defaultHome=path.join(dir,"codex-default"),managed=path.join(dir,"codex-accounts");',
500
+ 'const read=(p)=>{try{return JSON.parse(fs.readFileSync(p,"utf8"))}catch{return {}}};',
501
+ 'const write=(p,v)=>{fs.mkdirSync(path.dirname(p),{recursive:true,mode:0o700});const tmp=p+"."+process.pid+".tmp";try{fs.writeFileSync(tmp,JSON.stringify(v),{mode:0o600});fs.chmodSync(tmp,0o600);fs.renameSync(tmp,p)}catch(e){try{fs.unlinkSync(tmp)}catch{};throw e}};',
502
+ // A remote parent can launch concurrently with another parent. Serialize the
503
+ // entire auth-store + named-home transition, including stale-home pruning.
504
+ 'const lock=path.join(dir,"remote-codex-mirror.lock"),deadline=Date.now()+15000,sleep=new Int32Array(new SharedArrayBuffer(4));fs.mkdirSync(dir,{recursive:true,mode:0o700});while(true){try{fs.writeFileSync(lock,String(process.pid),{flag:"wx",mode:0o600});break}catch(e){if(e.code!=="EEXIST")throw e;try{if(Date.now()-fs.statSync(lock).mtimeMs>60000)fs.rmSync(lock,{force:true})}catch{}if(Date.now()>=deadline)throw new Error("remote Codex mirror lock timeout");Atomics.wait(sleep,0,0,25)}}try{',
505
+ 'const store=read(file);store.version=typeof store.version==="number"?store.version:items[0].version;store.providers={...(store.providers||{})};for(const key of Object.keys(store.providers)){if(key==="openai-codex"||key.startsWith("openai-codex:"))delete store.providers[key]}',
506
+ 'for(const item of items){const home=item.account==="default"?defaultHome:path.join(managed,item.account),key=item.account==="default"?"openai-codex":"openai-codex:"+item.account;const current=read(path.join(home,"auth.json")),auth={...current,...item.codexAuth,auth_mode:"chatgpt",tokens:{...(item.codexAuth.tokens||{}),refresh_token:"",refreshToken:""}};write(path.join(home,"auth.json"),auth);store.providers[key]={...item.provider,codexHome:home,tokens:{...(item.provider.tokens||{}),refreshToken:"",refresh_token:""}}}',
507
+ 'if(!store.providers["openai-codex"]){const first=items[0],home=path.join(managed,first.account);write(path.join(defaultHome,"auth.json"),{...first.codexAuth,auth_mode:"chatgpt",tokens:{...(first.codexAuth.tokens||{}),refresh_token:"",refreshToken:""}});store.providers["openai-codex"]={...store.providers["openai-codex:"+first.account],codexHome:defaultHome}}',
508
+ 'write(file,store);const keep=new Set(items.filter(x=>x.account!=="default").map(x=>x.account));try{for(const name of fs.readdirSync(managed)){if(!keep.has(name))fs.rmSync(path.join(managed,name),{recursive:true,force:true})}}catch(e){if(e.code!=="ENOENT")throw e}fs.rmSync(path.join(dir,"remote-codex-mirror.json"),{force:true});',
509
+ '}finally{fs.rmSync(lock,{force:true})}',
510
+ ].join('');
511
+ const result = ssh(target.host, `umask 077; mkdir -p ${configDir} "$HOME/.codex" && bun -e ${shellQuote(code)} ${configDir}`, 20_000, JSON.stringify(accounts));
512
+ if (result.status !== 0) throw new Error(`remote Codex access mirror batch failed (ssh rc=${result.status ?? 'unknown'})`);
513
+ }
514
+
305
515
  // ── 관측: 원격 런 원장 거울 ─────────────────────────────────────────────────────────────
306
516
 
307
517
  const RUN_ID_RE = /^run-[A-Za-z0-9_-]{4,64}$/;
@@ -1,7 +1,8 @@
1
+ import { spawnSync } from 'node:child_process';
1
2
  import { debug } from '../debug/log.js';
2
3
  import { listChecklist, setItemIfSnapshot, type Checklist } from '../release-loop/checklist.js';
3
4
  import { judgeLandedCell, parseJudgeSignals, type GreenVerdict } from '../release-loop/landed-to-green.js';
4
- import { cardPrNumber, readTaskAgentState, taskAgentStatePath, updateTaskCard, type TaskCard } from './task-hand.js';
5
+ import { HAND_MOVE_HISTORY_EVENT, LIVE_MOVE_HISTORY_EVENT, LIVE_MOVE_RESULT_EVENT, cardPrNumber, readTaskAgentState, taskAgentStatePath, updateTaskCard, type TaskCard } from './task-hand.js';
5
6
  import { isGoalOnlyPr } from './judge.js';
6
7
 
7
8
  export interface LandedTaskPr { number: number; url: string; headRefOid: string; state: string; baseRefName: string; mergedAt: string; files?: Array<{ path: string }> }
@@ -142,3 +143,81 @@ export function closeMergedTaskCard(pr: LandedTaskPr, cwd: string, deps: Postlan
142
143
  log('task-agent', 'postland-card-closed', { pr: pr.number, card: card.id, version, id: item.id, outcome: complete ? 'complete' : 'partial', verdict });
143
144
  return item.id;
144
145
  }
146
+
147
+ const defaultGit = (cwd: string, args: string[]): { status: number; stdout: string; stderr: string } => {
148
+ const run = spawnSync('git', args, { cwd, encoding: 'utf8' });
149
+ return { status: run.status ?? 1, stdout: run.stdout ?? '', stderr: run.stderr ?? '' };
150
+ };
151
+
152
+ export type HandMergeReconcileOutcome =
153
+ | { outcome: 'reconciled'; card: string; status: 'landed-by-hand' | 'landed'; pr: number; head: string; mergeSha: string; mergedAt: string }
154
+ | { outcome: 'not-reconciled'; reason: 'pr-not-merged' | 'no-merge-sha' | 'no-bound-card' | 'ambiguous-card' | 'card-not-failed' | 'not-ancestor-of-main' | 'card-binding-changed-before-write' };
155
+
156
+ export interface HandMergeReconcileDeps {
157
+ statePath?: string;
158
+ mainRef?: string;
159
+ updateCard?: typeof updateTaskCard;
160
+ git?: (cwd: string, args: string[]) => { status: number; stdout: string; stderr: string };
161
+ log?: typeof debug.log;
162
+ }
163
+
164
+ /**
165
+ * TA-CARD-MERGE-RECONCILE — 실패 카드에 묶인 PR을 사람이 손수 병합했을 때만 카드를 정산한다.
166
+ * MERGED + merge SHA가 `${mainRef}`(기본 `origin/main`) 조상일 때 status=landed-by-hand(또는 merge 커밋이
167
+ * land attempt head와 일치하면 landed)로 `updateTaskCard` 파일-락 트랜잭션 하나로 갱신하고,
168
+ * 이전 status·사유는 history `hand-move` 줄에 보존한다. OPEN·비조상·묶임 없음은 카드를 그대로 둔다.
169
+ */
170
+ export function reconcileHandMergedCard(pr: LandedTaskPr & { mergeCommit?: string }, cwd: string, deps: HandMergeReconcileDeps = {}): HandMergeReconcileOutcome {
171
+ const log = deps.log ?? debug.log.bind(debug);
172
+ if (pr.state !== 'MERGED' || !pr.url) return { outcome: 'not-reconciled', reason: 'pr-not-merged' };
173
+ const mergeSha = pr.mergeCommit ?? '';
174
+ if (!/^[a-f0-9]{40}$/i.test(mergeSha)) return { outcome: 'not-reconciled', reason: 'no-merge-sha' };
175
+ const path = deps.statePath ?? taskAgentStatePath();
176
+ const tasks = readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(path).tasks ?? {};
177
+ const cards = Object.values(tasks).filter((card) => card.taLand && cardPrNumber(card) === pr.number
178
+ && typeof card.pr === 'object' && card.pr.url?.replace(/\/+$/, '') === pr.url.replace(/\/+$/, ''));
179
+ if (cards.length !== 1) {
180
+ log('task-agent', 'postland-card-hand-skipped', { pr: pr.number, reason: cards.length ? 'ambiguous-card' : 'no-bound-card' });
181
+ return { outcome: 'not-reconciled', reason: cards.length ? 'ambiguous-card' : 'no-bound-card' };
182
+ }
183
+ const card = cards[0]!;
184
+ if (card.status !== 'failed') {
185
+ log('task-agent', 'postland-card-hand-skipped', { pr: pr.number, card: card.id, reason: 'card-not-failed', status: card.status });
186
+ return { outcome: 'not-reconciled', reason: 'card-not-failed' };
187
+ }
188
+ const mainRef = deps.mainRef ?? 'origin/main';
189
+ if ((deps.git ?? defaultGit)(cwd, ['merge-base', '--is-ancestor', mergeSha, mainRef]).status !== 0) {
190
+ log('task-agent', 'postland-card-hand-skipped', { pr: pr.number, card: card.id, reason: 'not-ancestor-of-main', mainRef });
191
+ return { outcome: 'not-reconciled', reason: 'not-ancestor-of-main' };
192
+ }
193
+ const at = new Date().toISOString();
194
+ let reconciled: { status: 'landed-by-hand' | 'landed' } | undefined;
195
+ // The card lock keeps the settlement atomic with its binding checks: a card that flipped off
196
+ // `failed` mid-flight is left untouched for its owning path to finish.
197
+ (deps.updateCard ?? updateTaskCard)(path, card.id, (current, currentTasks) => {
198
+ const matching = Object.values(currentTasks).filter((candidate) => candidate.taLand && cardPrNumber(candidate) === pr.number
199
+ && typeof candidate.pr === 'object' && candidate.pr.url?.replace(/\/+$/, '') === pr.url.replace(/\/+$/, ''));
200
+ if (matching.length !== 1 || matching[0]?.id !== card.id) return undefined;
201
+ if (!current || current.status !== 'failed' || cardPrNumber(current) !== pr.number || typeof current.pr !== 'object'
202
+ || current.pr.url?.replace(/\/+$/, '') !== pr.url.replace(/\/+$/, '')) return undefined;
203
+ const status = (current.landAttempts ?? []).some((attempt) => attempt.pr === pr.number
204
+ && attempt.head.toLowerCase() === mergeSha.toLowerCase()) ? 'landed' : 'landed-by-hand';
205
+ const priorReason = [...current.history].reverse().find((event) => event.event !== LIVE_MOVE_HISTORY_EVENT
206
+ && event.event !== LIVE_MOVE_RESULT_EVENT && event.event !== HAND_MOVE_HISTORY_EVENT)?.detail ?? '사유 기록 없음';
207
+ const detail = status === 'landed'
208
+ ? `PR #${pr.number} 병합 정산: merge ${mergeSha}가 land attempt head와 일치 — status landed; 이전 status: failed; 이전 사유: ${priorReason}`
209
+ : `PR #${pr.number} 손 병합 정산: merge ${mergeSha}가 ${mainRef} 조상 — status landed-by-hand; 이전 status: failed; 이전 사유: ${priorReason}`;
210
+ reconciled = { status };
211
+ return { ...current,
212
+ status,
213
+ prTerminal: { pr: pr.number, url: pr.url, head: mergeSha, state: 'MERGED' as const, at },
214
+ history: [...current.history, { at, event: HAND_MOVE_HISTORY_EVENT, pr: pr.number, head: mergeSha, detail }],
215
+ };
216
+ });
217
+ if (!reconciled) {
218
+ log('task-agent', 'postland-card-hand-pending', { pr: pr.number, card: card.id, reason: 'card-binding-changed-before-write' });
219
+ return { outcome: 'not-reconciled', reason: 'card-binding-changed-before-write' };
220
+ }
221
+ log('task-agent', 'postland-card-hand-reconciled', { pr: pr.number, card: card.id, status: reconciled.status, mergeSha, mainRef });
222
+ return { outcome: 'reconciled', card: card.id, status: reconciled.status, pr: pr.number, head: pr.headRefOid, mergeSha, mergedAt: pr.mergedAt };
223
+ }
@@ -29,10 +29,13 @@
29
29
  * - 관측: `task-agent` · `rejudge-on-head {card, pr, fromHead, toHead, step, decision, reason}` — 수가 난 판단은 카드 history 에도
30
30
  * 한 줄(`event:'rejudge-on-head'` · 같은 머리·같은 단계·같은 판단은 한 번).
31
31
  */
32
- import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
32
+ import { closeSync, mkdirSync, openSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
33
+ import { randomUUID } from 'node:crypto';
33
34
  import { spawn, spawnSync } from 'node:child_process';
34
35
  import { tmpdir } from 'node:os';
35
36
  import { dirname, join } from 'node:path';
37
+ import { planPrLandRecovery, type PrLandRecoveryPlan } from '../cli/pr-land-recovery.js';
38
+ import { lastJsonObject, runGraph } from '../graph-runner/runner.js';
36
39
  import { debug } from '../debug/log.js';
37
40
  import { prodInstanceRoot } from '../instance/resolve.js';
38
41
  import { getUserConfig } from '../user-config.js';
@@ -44,7 +47,7 @@ import { recordLiveMoveHistory } from './live-move-history.js';
44
47
  import { runRepairCycle, type RepairLaunchRequest } from './repair-cycle.js';
45
48
  import { repairMustFixFromReview, repairReviewDigest } from './repair-review.js';
46
49
  import type { RepairCasOutcome, RepairCasRequest } from './repair-branch-cas.js';
47
- import { defaultLandPrHead, defaultPrHead, defaultReviewRepoCandidates, executeLiveLand, executeLiveReview, landRetryPending, type LiveMoveDeps, type LiveMoveResult, type PrHeadView, type ReviewRepoCandidate, type TaskAgentLiveMove } from './live-moves.js';
50
+ import { configuredMsb1Worker, defaultLandPrHead, defaultPrHead, defaultReviewRepoCandidates, executeLiveLand, executeLiveReview, landRetryPending, remoteTaWorkerCommand, type LiveMoveDeps, type LiveMoveResult, type PrHeadView, type ReviewRepoCandidate, type TaskAgentLiveMove } from './live-moves.js';
48
51
  import { capturedLandReview, captureReviewResults, parseReviewResultOutput, pendingReviewResults, reviewResultPath, type ParsedReviewResult } from './review-result-capture.js';
49
52
  import { boundPrTerminal, cardPrNumber, LIVE_MOVE_RESULT_EVENT, readTaskCard, taskAgentStatePath, updateTaskCard, type TaskCard } from './task-hand.js';
50
53
  import { verifiedTrainLanding, type TrainLanding } from './train-landing.js';
@@ -98,6 +101,10 @@ export interface RejudgeDeps {
98
101
  notifyRepair?: (seat: string, body: string) => void;
99
102
  /** Shared new-launch budget for this `tasks rejudge --all` call. */
100
103
  repairBudget?: import('./repair-cycle.js').RepairTickBudget;
104
+ /** One review request across all candidates in one --all tick; standalone card rejudge is uncapped. */
105
+ reviewBudget?: { max: number; requested: number };
106
+ /** One land or queue attempt across all candidates in one --all tick. */
107
+ landBudget?: { max: number; requested: number };
101
108
  /** TA-REJUDGE-REBASE-LOOP — origin/main 을 PR 가지에 3-way merge 하고 일반 push. 시험은 주입한다. */
102
109
  catchUpMain?: (request: CatchUpRequest) => Promise<CatchUpResult>;
103
110
  log?: (category: string, event: string, data: Record<string, unknown>) => void;
@@ -265,12 +272,28 @@ export function regateRunArgs(entry: string, configDir: string, request: RegateR
265
272
  return [entry, '--config-dir', configDir, 'tasks', 'regate-run', '--card', request.card, '--pr', String(request.pr), '--head', request.head, '--repo', request.repoRoot, '--out', request.resultPath];
266
273
  }
267
274
 
268
- export async function defaultRequestRegate(request: RegateRequest, opts: { spawn?: DetachedSpawn; entry?: string; configDir?: string } = {}): Promise<void> {
275
+ export async function defaultRequestRegate(request: RegateRequest, opts: { spawn?: DetachedSpawn; entry?: string; configDir?: string; remote?: import('./parent-host.js').ParentHostConfig | null } = {}): Promise<void> {
269
276
  const { spawnDetachedConfirmed, ELANOUS_CLI_ENTRY } = await import('../cli/tasks-cli.js');
270
277
  const { getElanousConfigDirOverride } = await import('../elanous-config-dir.js');
271
278
  const { effectiveInstanceRoot } = await import('../instance/resolve.js');
272
279
  const configDir = opts.configDir ?? getElanousConfigDirOverride() ?? effectiveInstanceRoot();
273
- await spawnDetachedConfirmed(opts.spawn ?? (spawn as unknown as DetachedSpawn), process.execPath, regateRunArgs(opts.entry ?? ELANOUS_CLI_ENTRY, configDir, request), undefined, request.repoRoot);
280
+ const base = opts.spawn ?? (spawn as unknown as DetachedSpawn);
281
+ const remote = opts.remote === undefined ? (opts.spawn ? null : configuredMsb1Worker()) : opts.remote;
282
+ if (!remote) {
283
+ await spawnDetachedConfirmed(base, process.execPath, regateRunArgs(opts.entry ?? ELANOUS_CLI_ENTRY, configDir, request), undefined, request.repoRoot);
284
+ return;
285
+ }
286
+ // The remote worker writes its own result, then SSH carries only that JSON into the HQ result path.
287
+ const remoteResult = `/tmp/elanous-ta-regate-${randomUUID()}.json`;
288
+ const script = `${remoteTaWorkerCommand(remote, ['tasks', 'regate-run', '--card', request.card, '--pr', String(request.pr),
289
+ '--head', request.head, '--repo', remote.cwd, '--out', remoteResult])} >/dev/null; rc=$?; cat '${remoteResult}'; rm -f '${remoteResult}'; exit $rc`;
290
+ mkdirSync(dirname(request.resultPath), { recursive: true });
291
+ const fd = openSync(request.resultPath, 'w');
292
+ const toFile: DetachedSpawn = (command, args, options) =>
293
+ (base as unknown as (c: string, a: string[], o: unknown) => ReturnType<DetachedSpawn>)(command, args, { ...options, stdio: ['ignore', fd, 'ignore'] });
294
+ try {
295
+ await spawnDetachedConfirmed(toFile, 'ssh', ['-T', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', remote.host, script]);
296
+ } finally { closeSync(fd); }
274
297
  }
275
298
 
276
299
  /** 재게이트 결과 파일 — `tasks regate-run` 이 쓴다. */
@@ -513,6 +536,19 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
513
536
  }
514
537
  const step = (s: RejudgeStep, decision: string, reason: string, record: boolean, extra: Partial<RejudgeOutcome> = {}) =>
515
538
  emit({ card: card!.id, pr, fromHead, toHead, step: s, decision, reason, ...extra }, record);
539
+ const requestReview = async (candidate: TaskCard, reason: string, forceNewReview = false): Promise<void> => {
540
+ const budget = deps.reviewBudget;
541
+ if (budget && budget.requested >= budget.max) {
542
+ step('review', 'tick-cap', `review request cap ${budget.max} reached — next tick`, false);
543
+ return;
544
+ }
545
+ // Reserve before awaiting the worker so another candidate cannot launch a second request.
546
+ if (budget) budget.requested++;
547
+ const before = { reviewRequests: candidate.reviewRequests, landAttempts: candidate.landAttempts };
548
+ const result = await executeLiveReview(candidate, pr, { runId: candidate.runId ?? null, stopReason: reason, cwd, forceNewReview }, { ...live, ...(deps.log ? { log: deps.log } : {}) });
549
+ recordMove(statePath, candidate.id, candidate.runId, 'review', result, pr, before, now, deps.log);
550
+ step('review', result.executed ? 'review-requested' : 'review-request-failed', result.detail, true);
551
+ };
516
552
 
517
553
  // Adoption is a first judgement for the current head, even when the implementation run
518
554
  // already asked for a review. A captured failure needs repair; an accepted result must
@@ -526,10 +562,7 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
526
562
  } else if (!deps.liveMoves.has('review')) {
527
563
  step('review', 'would-review', 'adopted PR first judgement · review not enabled in HQ taskAgent.liveMoves — shadow', false);
528
564
  } else {
529
- const before = { reviewRequests: card.reviewRequests, landAttempts: card.landAttempts };
530
- const result = await executeLiveReview(card, pr, { runId: card.runId ?? null, stopReason: 'adopted-pr-first-judgement', cwd }, { ...live, ...(deps.log ? { log: deps.log } : {}) });
531
- recordMove(statePath, card.id, card.runId, 'review', result, pr, before, now, deps.log);
532
- step('review', result.executed ? 'review-requested' : 'review-request-failed', result.detail, true);
565
+ await requestReview(card, 'adopted-pr-first-judgement');
533
566
  }
534
567
  return outcomes;
535
568
  }
@@ -618,12 +651,7 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
618
651
 
619
652
  if (!reviews.length) {
620
653
  if (!deps.liveMoves.has('review')) step('review', 'would-review', 'remote run terminal proven · review not enabled in HQ taskAgent.liveMoves — shadow', false);
621
- else {
622
- const before = { reviewRequests: card.reviewRequests, landAttempts: card.landAttempts };
623
- const result = await executeLiveReview(card, pr, { runId: card.runId ?? null, stopReason: 'remote-run-terminal', cwd }, { ...live, ...(deps.log ? { log: deps.log } : {}) });
624
- recordMove(statePath, card.id, card.runId, 'review', result, pr, before, now, deps.log);
625
- step('review', result.executed ? 'review-requested' : 'review-request-failed', result.detail, true);
626
- }
654
+ else await requestReview(card, 'remote-run-terminal');
627
655
  return outcomes;
628
656
  }
629
657
 
@@ -642,10 +670,7 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
642
670
  if (pendingReviewResults(latest).some((entry) => entry.pr === pr && sameHead(entry.head, toHead))) {
643
671
  step('review', 'awaiting-review-result', 'exact-head review result pending', false);
644
672
  } else if (!latest.reviewRequests?.some((entry) => entry.pr === pr && sameHead(entry.head, toHead)) && deps.liveMoves.has('review')) {
645
- const before = { reviewRequests: latest.reviewRequests, landAttempts: latest.landAttempts };
646
- const result = await executeLiveReview(latest, pr, { runId: latest.runId ?? null, stopReason: 'queue-exact-head', cwd }, { ...live, ...(deps.log ? { log: deps.log } : {}) });
647
- recordMove(statePath, latest.id, latest.runId, 'review', result, pr, before, now, deps.log);
648
- step('review', result.executed ? 'review-requested' : 'review-request-failed', result.detail, true);
673
+ await requestReview(latest, 'queue-exact-head');
649
674
  } else step('review', 'not-landing', 'exact-head PASS/WARN mf0 review not captured', true);
650
675
  return outcomes;
651
676
  }
@@ -654,8 +679,13 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
654
679
  const reviewResult = latest.reviewRequests?.find((entry) => entry.pr === pr && sameHead(entry.head, toHead) && entry.resultPath)?.resultPath;
655
680
  if (!reviewResult) { step('land', 'not-landing', 'exact-head review JSON path missing', true); return outcomes; }
656
681
  const before = { reviewRequests: latest.reviewRequests, landAttempts: latest.landAttempts };
682
+ if (deps.landBudget && deps.landBudget.requested >= deps.landBudget.max) {
683
+ step('land', 'tick-cap', `land attempt cap ${deps.landBudget.max} reached — next tick`, false);
684
+ return outcomes;
685
+ }
657
686
  const queued = await executeLiveLand(latest, pr, { runId: latest.runId ?? null, cycleId: `queue:${latest.id}:${toHead}`, cwd,
658
687
  review, reviewResultPath: reviewResult }, { ...live, landQueueConfig: queueMode, ...(deps.log ? { log: deps.log } : {}) });
688
+ if (deps.landBudget && queued.executed) deps.landBudget.requested++;
659
689
  recordMove(statePath, latest.id, latest.runId, 'propose-land', queued, pr, before, now, deps.log);
660
690
  step('land', queued.queued ? 'queued' : queued.ok && !queued.executed ? 'queue-skipped' : 'queue-refused', queued.detail, true);
661
691
  return outcomes;
@@ -778,12 +808,7 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
778
808
  // ② 리뷰 — 지금 머리로 요청한 적이 없으면 다시(같은 머리는 한 번 · 상한은 ① 의 머리 수가 이미 막았다).
779
809
  if (!reviews.some((entry) => sameHead(entry.head, toHead))) {
780
810
  if (!deps.liveMoves.has('review')) step('review', 'would-review', 'review not enabled in taskAgent.liveMoves — shadow', false);
781
- else {
782
- const before = { reviewRequests: card.reviewRequests, landAttempts: card.landAttempts };
783
- const result = await executeLiveReview(card, pr, { runId: card.runId ?? null, stopReason: 'rejudge-on-head', cwd }, { ...live, ...(deps.log ? { log: deps.log } : {}) });
784
- recordMove(statePath, card.id, card.runId, 'review', result, pr, before, now, deps.log);
785
- step('review', result.executed ? 'review-requested' : 'review-request-failed', result.detail, true);
786
- }
811
+ else await requestReview(card, 'rejudge-on-head');
787
812
  return outcomes;
788
813
  }
789
814
  const latest = readTaskCard(card.id, statePath) ?? card;
@@ -828,10 +853,15 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
828
853
  return outcomes;
829
854
  }
830
855
  const before = { reviewRequests: latest.reviewRequests, landAttempts: latest.landAttempts };
856
+ if (deps.landBudget && deps.landBudget.requested >= deps.landBudget.max) {
857
+ step('land', 'tick-cap', `land attempt cap ${deps.landBudget.max} reached — next tick`, false);
858
+ return outcomes;
859
+ }
831
860
  // 주기 id — base-stale 재게이트 뒤의 land 는 새 base 를 붙여 앞 land 의 주기 표시(모듈 전역)와 갈린다 · gateAt 이 앞 base-stale 시도를 소비한다.
832
861
  const cycleId = `rejudge:${card.id}:${toHead}${regate.staleBase ? `:${regate.baseCommit.slice(0, 12)}` : ''}`;
833
862
  const landed = await executeLiveLand(latest, pr, { runId: card.runId ?? null, cycleId, cwd, review, reviewResultPath: reviewResult, overlapEvidence, gateAt: regate.at },
834
863
  { ...live, ...(deps.log ? { log: deps.log } : {}) });
864
+ if (deps.landBudget && landed.executed) deps.landBudget.requested++;
835
865
  // LAND-ORDER-LEDGER — executeLiveLand 직후 원장 한 줄(같은 pr+head 는 한 줄만). 쓰기가 실패해도 land 결과는 그대로(삼킨다).
836
866
  const landOrderLog = (event: 'appended' | 'append-failed') => { try { log('task-agent.land-order', event, { pr, head: toHead }); } catch { /* fail-soft */ } };
837
867
  try {
@@ -844,6 +874,38 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
844
874
  landOrderLog('append-failed');
845
875
  }
846
876
  recordMove(statePath, card.id, card.runId, 'propose-land', landed, pr, before, now, deps.log);
877
+ if (landed.executed && !landed.ok && /^✗/.test(landed.detail)) {
878
+ const context = { refusal: landed.detail, pr, head: toHead };
879
+ const preliminary = planPrLandRecovery(context);
880
+ if (preliminary.result.action === 'request-review' || preliminary.result.action === 'wait-retry') {
881
+ const entry = preliminary.result.action;
882
+ try {
883
+ const graphPath = entry === 'request-review' ? '../../graphs/units/task-agent-recover-review.yaml' : '../../graphs/units/task-agent-recover-wait.yaml';
884
+ const graph = await runGraph(join(import.meta.dir, graphPath), {
885
+ input: { ask: `TA land refusal for PR #${pr}`, context, prior: {} },
886
+ pinChildUniverse: true,
887
+ deps: { root: dirname(statePath) },
888
+ });
889
+ const output = lastJsonObject(graph.nodes.find((node) => node.nodeId === 'recover')?.output);
890
+ const plan = graph.status === 'done' && graph.unitCalls?.[0]?.entry === entry
891
+ && graph.unitCalls[0].exit === 'rework' ? output as unknown as PrLandRecoveryPlan | undefined : undefined;
892
+ const action = plan?.result?.action;
893
+ const matched = plan?.outcome === 'rework' && plan.result.pr === pr && plan.result.head === toHead
894
+ && action === preliminary.result.action && plan.evidence?.refusal === context.refusal.slice(0, 500)
895
+ && (action !== 'wait-retry' || plan.result.delayMs === 30_000);
896
+ if (!matched) step('land', 'recover-untrusted', `recover@1 ${entry} produced no pinned matching action`, true);
897
+ else if (action === 'request-review' && deps.liveMoves.has('review')) {
898
+ await requestReview(readTaskCard(card.id, statePath) ?? card, 'land-refusal-review-missing', true);
899
+ } else if (action === 'request-review') {
900
+ step('review', 'recover-review-disabled', 'recover@1 selected review but taskAgent.liveMoves excludes review', true);
901
+ } else if (action === 'wait-retry') {
902
+ step('land', 'recover-wait-retry', `recover@1 ${entry} selected existing retry admission after ${plan.result.delayMs}ms`, true);
903
+ } else step('land', 'recover-needs-human', `recover@1 ${entry}: ${plan.result.reason}`, true);
904
+ } catch (error) {
905
+ step('land', 'recover-failed', `recover@1 ${entry}: ${String(error).slice(0, 200)}`, true);
906
+ }
907
+ }
908
+ }
847
909
  // TA-LAND-RETRY-ON-INFRA — 실패한 시도는 갈래·사유·재시도 여부를 사유 줄에 싣는다(다음 틱이 왜 다시 하는지/안 하는지).
848
910
  const attempt = landed.landAttempt?.class
849
911
  ? ` · land-class ${landed.landAttempt.class} (${landed.landAttempt.reason}) · attempt ${landed.landAttempt.attempt} · ${landed.landAttempt.class === 'base-stale' ? 'base-stale → re-gate on a new base next tick (cap counts)' : landed.landAttempt.retryAllowed ? 'retry allowed on this head' : 'no retry on this head'}`
@@ -862,11 +924,35 @@ function recordMove(statePath: string, cardId: string, runId: string | undefined
862
924
  }
863
925
 
864
926
  /** 재판정 대상 카드 — 기존 리뷰 또는 HQ 가 종결 원장을 검증할 원격 부모 카드. */
865
- export function rejudgeCandidates(tasks: Readonly<Record<string, TaskCard>>): string[] {
927
+ export function rejudgeCandidates(tasks: Readonly<Record<string, TaskCard>>, opts: {
928
+ now?: Date; maxAgeHours?: number; onAgeSkip?: (card: TaskCard, ageHours: number | null) => void;
929
+ /** Fresh remote PR heads; mf0 only gets priority when it matches this head. */
930
+ currentHeads?: ReadonlyMap<string, string>;
931
+ } = {}): string[] {
932
+ const now = opts.now?.getTime() ?? Date.now();
933
+ const maxAgeHours = opts.maxAgeHours;
866
934
  return Object.values(tasks).filter((card) => {
867
935
  const pr = cardPrNumber(card);
868
- return pr !== undefined && !boundPrTerminal(card) && ((card.reviewRequests ?? []).some((entry) => entry.pr === pr)
936
+ const eligible = pr !== undefined && !boundPrTerminal(card) && ((card.reviewRequests ?? []).some((entry) => entry.pr === pr)
869
937
  || (card.status === 'launched' && card.taLand === true && ((!!card.parentHost && !!card.runId && !!card.runChildId)
870
938
  || (card.adoptedPr?.pr === pr && typeof card.pr === 'object' && !!card.pr.url && sameUrl(card.adoptedPr.url, card.pr.url)))));
939
+ if (!eligible) return false;
940
+ if (maxAgeHours !== undefined) {
941
+ const created = Date.parse(card.createdAt);
942
+ const ageHours = Number.isFinite(created) ? Math.max(0, (now - created) / 3_600_000) : null;
943
+ if (ageHours === null || ageHours > maxAgeHours) { opts.onAgeSkip?.(card, ageHours); return false; }
944
+ }
945
+ return true;
946
+ }).sort((a, b) => {
947
+ const hasMf0 = (card: TaskCard): number => {
948
+ const pr = cardPrNumber(card);
949
+ const head = card.reviewRequests?.filter((entry) => entry.pr === pr).at(-1)?.head;
950
+ const currentHead = opts.currentHeads?.get(card.id);
951
+ return pr !== undefined && head && currentHead?.toLowerCase() === head.toLowerCase()
952
+ && capturedLandReview(card, pr, head)?.mustFixCount === 0 ? 1 : 0;
953
+ };
954
+ const ageOrder = (Number.isFinite(Date.parse(b.createdAt)) ? Date.parse(b.createdAt) : 0)
955
+ - (Number.isFinite(Date.parse(a.createdAt)) ? Date.parse(a.createdAt) : 0);
956
+ return hasMf0(b) - hasMf0(a) || ageOrder || a.id.localeCompare(b.id);
871
957
  }).map((card) => card.id);
872
958
  }