elanous 0.2.34 → 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 (611) 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/{L04y73dlwKeTW-GetaTF3 → T1mBQqmkwKDLGtyisDCOd}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{5014f970-22bef0b5210f7d60.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/{2124.ed5bf480cea7f85b.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/1674.2a65fd5668e1cc5f.js +1 -0
  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/1993-c0007b7ad0c0ef41.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{8368.014caa8de2f5af34.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/2625.5d870aa999f33db1.js +34 -0
  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/{1644.35dd25240ae24e4b.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/{3121.779fafe3e44612c5.js → 3589.5444a06bcf6e775c.js} +1 -1
  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/{9131-fce7256c4180c4a5.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/4346.8601952e73bf5803.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/{6053-d893974de92e6198.js → 4441-875b97f5821ed58a.js} +1 -1
  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/{1895-60ed0e341c658bd4.js → 4705-194e64e47ca0fbdb.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/{5576-82d3703636d5c4b9.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/{7431.3051e9b8a4372d2d.js → 5101.e32282718f99ed6f.js} +2 -2
  57. package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +1 -0
  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/{2987-30cf9c2eb7b8bdd5.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/56d372f2.681ab4ec4c998fae.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/{7386.5d8e7e42e818dfb6.js → 5852.db1f7449f4f74612.js} +2 -2
  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/{3110.0a7065f25c841818.js → 6236.e18cced2d5ba6afb.js} +3 -3
  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/6b89c8bd.24df687a820500b3.js +1 -0
  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/{7005.1623d2aea78eb85f.js → 7385.50a308a477bdfc21.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/{6086.913a69e6fa3e4a8b.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/8219.baa715d1deee80e2.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +1 -0
  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/{7193-cf355cca5ba6c5d5.js → 8437-8d5492094b166cfa.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/{2554.3b5e200f5f5cfcd6.js → 8444.4c26b379ca2b9596.js} +4 -4
  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/{2547.dd27303624cb4da8.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/{9550.d5f76828f972058c.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/{8548.036c344973968847.js → 938.66ad1ef0e5abae1c.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/{3414.172ad4ca2d9b08e8.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/967-2aa39b2d04b4c690.js +1 -0
  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/{6740.43de73fc427c9fe2.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-406b47745b53f269.js +1 -0
  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-d7bff0f0905a662c.js → page-26e6c18a1a750cf6.js} +1 -1
  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-47c5d4db587266bd.js +1 -0
  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-df3445b207ceceab.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/{9a8cb466.cc9fc9e6e46038aa.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/{4fa33ebd.21c1bd5b294496e6.js → cbe38a15.5a032598e39970c6.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/{abb4b98b.163e0f56e84ae35d.js → d06f9160.a3d9062f95de8ad7.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/{9b48c871.204d5d13e7809bd0.js → da5176c6.6773b6c5994e1d99.js} +1 -1
  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 +3 -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 +4 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/default-loop.yaml +57 -3
  285. package/graphs/document-loop.yaml +51 -3
  286. package/graphs/implement-loop.yaml +53 -4
  287. package/graphs/journey/ta-journey.yaml +11 -1
  288. package/graphs/journey/units/author-dialogue/author-dialogue.yaml +42 -0
  289. package/graphs/journey/units/author-dialogue/observe.ts +96 -0
  290. package/graphs/journey/units/author-dialogue/recipes.yaml +10 -0
  291. package/graphs/operate-loop.yaml +51 -3
  292. package/graphs/research-loop.yaml +137 -3
  293. package/graphs/test-diet/test-diet.yaml +3 -0
  294. package/package.json +1 -1
  295. package/scripts/coord-role-pack.ts +19 -6
  296. package/scripts/hq/standby-snapshot.ts +17 -12
  297. package/scripts/release-loop/docs-land-node.ts +21 -5
  298. package/scripts/release-loop/gate-baseline-receipt.ts +155 -0
  299. package/scripts/release-loop/gate-node.ts +181 -22
  300. package/scripts/release-loop/gate-partial.ts +65 -11
  301. package/scripts/release-loop/gate-radius-live.ts +177 -0
  302. package/scripts/release-loop/gate-radius-shadow.ts +1 -1
  303. package/scripts/release-loop/node-verdict.ts +12 -1
  304. package/scripts/release-loop/npm-publish-node.ts +66 -12
  305. package/scripts/release-loop/probe-pod-host-dirs-live.ts +5 -0
  306. package/scripts/release-loop/pwa-remote.ts +166 -0
  307. package/scripts/release-loop/pwa-scenarios-node.ts +19 -3
  308. package/scripts/release-loop/resume-release.ts +41 -4
  309. package/scripts/release-loop/unattended-release.ts +2 -0
  310. package/scripts/test-diet/lib.ts +6 -1
  311. package/scripts/test-diet/node.ts +194 -13
  312. package/scripts/ux-sim.ts +39 -0
  313. package/src/agent/self-ambient-cache.ts +8 -0
  314. package/src/agent-substrate/acp-reviewer.ts +39 -4
  315. package/src/agent-substrate/pr-reviewer.ts +17 -0
  316. package/src/agent-substrate/self-review-cli.ts +62 -13
  317. package/src/autopilot/pr-manager.ts +215 -36
  318. package/src/budget/run-usage-rollup.ts +1 -0
  319. package/src/chat/index.ts +0 -2
  320. package/src/chat/pickers/modal-runtime.ts +10 -27
  321. package/src/cli/agent-cli.ts +13 -2
  322. package/src/cli/doctor-cli.ts +4 -1
  323. package/src/cli/light-entry-route.ts +46 -6
  324. package/src/cli/pod-cli.ts +77 -3
  325. package/src/cli/pr-cli.ts +5 -2
  326. package/src/cli/pr-land-pod-shadow-job.ts +3 -0
  327. package/src/cli/release-checklist-read-cli.ts +79 -0
  328. package/src/cli/release-cli.ts +72 -102
  329. package/src/cli/remote-resolve.ts +1 -1
  330. package/src/cli/root-tui-entry.ts +48 -0
  331. package/src/cli/sim-cli.ts +22 -0
  332. package/src/cli/tasks-cli.ts +540 -31
  333. package/src/cli/usage-cli.ts +13 -11
  334. package/src/clipboard/history.ts +179 -13
  335. package/src/config.ts +8 -1
  336. package/src/dashboard/chat/acp-chat.ts +8 -0
  337. package/src/dashboard/index.ts +78 -10
  338. package/src/dashboard/render/theme-resolver.ts +33 -4
  339. package/src/domains/schedule-registry.ts +16 -2
  340. package/src/git-fs/idle-refresh.ts +76 -0
  341. package/src/git-fs/index.ts +18 -7
  342. package/src/git-fs/worktree.ts +42 -4
  343. package/src/graph-kinds/registry.ts +2 -2
  344. package/src/graph-runner/runner.ts +153 -10
  345. package/src/grok/credential.ts +10 -3
  346. package/src/harness/harness-cli-command.ts +112 -28
  347. package/src/harness/harness-incidents.ts +33 -0
  348. package/src/harness/launch-utterance-ledger.ts +208 -0
  349. package/src/impl-discipline/detector.ts +16 -4
  350. package/src/impl-discipline/system-prompt.ts +11 -0
  351. package/src/impl-discipline/types.ts +2 -2
  352. package/src/index.ts +496 -143
  353. package/src/intake-plane/already-built-hint.ts +4 -2
  354. package/src/intake-plane/requirement-funnel.ts +29 -1
  355. package/src/light-cli.ts +27 -11
  356. package/src/llm/opengateway-models.ts +11 -0
  357. package/src/llm/provider-credentials.ts +14 -0
  358. package/src/llm/provider-detect.ts +8 -1
  359. package/src/llm/runtime-model-family.ts +2 -1
  360. package/src/llm.ts +213 -56
  361. package/src/model-tier/llm-tier-map.ts +15 -1
  362. package/src/native-tool-catalog.ts +15 -1
  363. package/src/nexus/api/loop-resources.ts +13 -1
  364. package/src/oauth/codex.ts +7 -0
  365. package/src/ops/overview-collect.ts +18 -3
  366. package/src/ops/overview.ts +20 -7
  367. package/src/pr-tool/census.ts +261 -27
  368. package/src/pty-shell/pty-manifest.ts +7 -0
  369. package/src/release-loop/checklist.ts +24 -0
  370. package/src/release-loop/landed-but-yellow.ts +129 -5
  371. package/src/release-loop/light-rc.ts +1 -0
  372. package/src/release-loop/rehearsal.ts +2 -2
  373. package/src/repl/index.ts +174 -17
  374. package/src/repl/ops-msg-bridge.ts +32 -0
  375. package/src/self-dev/ask-launch-flow.ts +44 -5
  376. package/src/self-dev/cli-help-text.ts +17 -0
  377. package/src/self-dev/dev-cli.ts +11 -9
  378. package/src/self-dev/draft-residue.ts +37 -1
  379. package/src/self-dev/draft-sweep.ts +137 -11
  380. package/src/self-dev/draft-triage-rules.ts +107 -0
  381. package/src/self-dev/fleet-reduce.ts +3 -2
  382. package/src/self-dev/graph-front-nodes.ts +112 -0
  383. package/src/self-dev/graph-journey-nodes.ts +1 -0
  384. package/src/self-dev/harness-run-cli.ts +3 -15
  385. package/src/self-dev/orchestrate-cli-text.ts +10 -0
  386. package/src/self-dev/review-provider-fallback.ts +35 -1
  387. package/src/self-dev/self-orchestrate-runtime.ts +1 -11
  388. package/src/self-dev/shard-aggregate.ts +42 -5
  389. package/src/self-dev/shard-run.ts +8 -1
  390. package/src/self-implement/blocked-draft-policy.ts +20 -0
  391. package/src/self-implement/classify-shadow.ts +3 -7
  392. package/src/self-implement/doctor-visit.ts +18 -1
  393. package/src/self-implement/gate-baseline.ts +56 -17
  394. package/src/self-implement/gate-cli.ts +1 -0
  395. package/src/self-implement/gate-remote.ts +31 -20
  396. package/src/self-implement/goal-author-decompose.ts +275 -47
  397. package/src/self-implement/goal-author.ts +250 -0
  398. package/src/self-implement/goal-elaborate.ts +295 -0
  399. package/src/self-implement/goal-refine.ts +270 -0
  400. package/src/self-implement/graph-overlay-yaml.ts +14 -3
  401. package/src/self-implement/orchestrator.ts +152 -45
  402. package/src/self-implement/rework-budget-judgment.ts +5 -8
  403. package/src/self-implement/run-stop.ts +10 -1
  404. package/src/self-implement/running-runs.ts +29 -1
  405. package/src/self-implement/scope-adjacency.ts +113 -0
  406. package/src/self-implement/seams.ts +127 -24
  407. package/src/self-implement/stop-autoheal.ts +87 -1
  408. package/src/session-runtime/index.ts +1 -1
  409. package/src/setup/llm-env-detect.ts +5 -0
  410. package/src/skills/tools/agent.ts +140 -14
  411. package/src/skills/tools/msg-ask.ts +100 -0
  412. package/src/skills/tools/ops-ledger.ts +2 -1
  413. package/src/steward/hold-hygiene.ts +24 -6
  414. package/src/steward/triage-budget.ts +194 -0
  415. package/src/steward/triage.ts +20 -3
  416. package/src/task-agent/actions.ts +69 -1
  417. package/src/task-agent/green-sweep.ts +4 -0
  418. package/src/task-agent/live-moves.ts +160 -4
  419. package/src/task-agent/parent-host.ts +123 -3
  420. package/src/task-agent/shadow.ts +7 -1
  421. package/src/task-agent/task-hand.ts +54 -41
  422. package/src/task-agent/tier-split.ts +127 -0
  423. package/src/task-agent/work-order-build.ts +2 -0
  424. package/src/task-agent/work-order.ts +3 -2
  425. package/src/task-orchestrator/generator-prompt.ts +60 -11
  426. package/src/task-orchestrator/surfaces/pod-account-broker.ts +33 -0
  427. package/src/task-orchestrator/surfaces/pod-child-providers.ts +1 -1
  428. package/src/task-orchestrator/surfaces/pod-command-job.ts +45 -7
  429. package/src/task-orchestrator/surfaces/pod-image-source.ts +89 -1
  430. package/src/task-orchestrator/surfaces/pod-lease.ts +17 -8
  431. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +8 -2
  432. package/src/task-orchestrator/surfaces/pod-provenance.ts +76 -0
  433. package/src/task-orchestrator/surfaces/pod-source-receive.ts +18 -0
  434. package/src/task-orchestrator/surfaces/pod-warm-provenance.ts +81 -0
  435. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +6 -2
  436. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +3 -0
  437. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +37 -5
  438. package/src/task-orchestrator/surfaces/self-implement-pod.ts +368 -36
  439. package/src/task-orchestrator/surfaces/self-implement.ts +23 -0
  440. package/src/tool-runtime/ops-ledger-runtimes.ts +10 -2
  441. package/src/tui/README.md +2 -0
  442. package/src/tui/chat/background-job-status.ts +54 -0
  443. package/src/tui/chat/log-view.ts +80 -0
  444. package/src/tui/entry.ts +406 -0
  445. package/src/tui/input/composer.ts +144 -0
  446. package/src/tui/seat-mail-boot.ts +59 -0
  447. package/src/tui/slash/compact.ts +100 -0
  448. package/src/tui/slash/new-session.ts +35 -0
  449. package/src/tui-client/local-chat-turn.ts +239 -0
  450. package/src/tui-client/local-harness-command.ts +63 -0
  451. package/src/tui-client/local-provider-command.ts +184 -0
  452. package/src/user-config.ts +4 -2
  453. package/src/ux-sim/event-monkey.ts +378 -0
  454. package/src/ux-sim/fake-bin.ts +42 -7
  455. package/src/ux-sim/fault-nextmove-dispatch.ts +266 -0
  456. package/src/ux-sim/fault-pod-codex-503.ts +211 -0
  457. package/src/ux-sim/fault-scope-escape-lift.ts +189 -0
  458. package/src/version/packed-revision.json +1 -1
  459. package/apps/pwa/out/_next/static/chunks/1162.c69abda138c7ce43.js +0 -79
  460. package/apps/pwa/out/_next/static/chunks/1449-1fede62af84c3e43.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/1598-13fc845935e816df.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/1639.62c5e16fa20d242f.js +0 -94
  463. package/apps/pwa/out/_next/static/chunks/1661-adb8b2babc9aaf8c.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/2147.107c0fc6146fb15a.js +0 -23
  465. package/apps/pwa/out/_next/static/chunks/2202-ef1c9b000c3074fc.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/2247.1e326638ee43e672.js +0 -115
  467. package/apps/pwa/out/_next/static/chunks/2268.56e1e3d58180abae.js +0 -8
  468. package/apps/pwa/out/_next/static/chunks/2469-606819c812414021.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/2593.e53c1620c0bb3599.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/2690.e96671114222676a.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/2898-79ee1a5cc0cd9d13.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/298.26099a9f65e5c8ff.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/2992.51fc2a7295bd8139.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/3001-5a52788c813b6113.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/3014-291245fba9df7b5b.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/3068.ee78201f0251a18d.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/3083.cd399a17e9365f78.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/3103.84470c8445962f04.js +0 -34
  479. package/apps/pwa/out/_next/static/chunks/3117-e1c474683fd1f824.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/3251-2c17bd5a91f8de2e.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/326.3fb32322bb2ddf6d.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/3411-f87314c6c66ebc73.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/3421-7ff7fbaa156985a7.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/3498.cf2a15a8a9e5b926.js +0 -226
  485. package/apps/pwa/out/_next/static/chunks/381.f55dc63ea87735e6.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/3819.163d4777f0a597f6.js +0 -32
  487. package/apps/pwa/out/_next/static/chunks/3837.722e053fb9f321ad.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/4129-ab0a6189f1778633.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/4361.93d14ba594b3a035.js +0 -104
  490. package/apps/pwa/out/_next/static/chunks/4364.7a7eb19cceb67d2f.js +0 -286
  491. package/apps/pwa/out/_next/static/chunks/4406.d71f506f7c3ee9d3.js +0 -149
  492. package/apps/pwa/out/_next/static/chunks/4461.2ad1514180ae7e29.js +0 -148
  493. package/apps/pwa/out/_next/static/chunks/4479.bd309cbe0e3842ee.js +0 -64
  494. package/apps/pwa/out/_next/static/chunks/4522-05966f777d66d13b.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/4543-9a3e9812e845de65.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/4557.f8f1d84a0d90d590.js +0 -74
  497. package/apps/pwa/out/_next/static/chunks/4589-79b11542179ef58c.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/4633-9f8f9f88ffbb8f5c.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/4831.23af4d10c0697b5e.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/4983.5951b85724f61f2a.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/5028-f0722e6114c313e7.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/5249.96a848656b582a93.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/5311.16e0ffbfa5468b80.js +0 -62
  504. package/apps/pwa/out/_next/static/chunks/5446.edf141cff9ef647d.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/5489.ec2dd1b5272a19dc.js +0 -24
  506. package/apps/pwa/out/_next/static/chunks/559.df87695df637920c.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/5618-5c88f2f70178c4ad.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/5764.05487bf0b73ddb6b.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/5930.e678826c3f831aab.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/5dfb57f7.f2e3305326202474.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/6200-cefd6f33f59d765e.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/6314-edeba650ae686ac9.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/6316-bc9a9b74d7842d79.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/6476-13b9779b73528008.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/666-04db3ac4c8e3c66f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/670.682e55106ef915a6.js +0 -77
  517. package/apps/pwa/out/_next/static/chunks/6816.b7ba9d0a1a4a46a7.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/690-fbbcc777f189af37.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/6989.0d3d4020e5cb3df6.js +0 -90
  520. package/apps/pwa/out/_next/static/chunks/7217-d17393fe7af78fa6.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7232.24edbd938a69c8d0.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/7295cd1e.b4b557908888eeee.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/7323.adb02de21991883c.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/7448.478958ffbbba0ac0.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/7537.ba2208c5c656b8a3.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/7562.948255cbc3e79d33.js +0 -41
  527. package/apps/pwa/out/_next/static/chunks/7596-962ae6db6cfc9c83.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/7671.cff51beac212a2ba.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/7883.3916734ee16d3e58.js +0 -27
  530. package/apps/pwa/out/_next/static/chunks/7952.b192c31b6a0e00cc.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/801.5b24e81eded22d1b.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/8176.8c9a8613f5a01ac1.js +0 -36
  533. package/apps/pwa/out/_next/static/chunks/819e78a6.fa3dd7f7da19bf61.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/8220-78978450c3183d8d.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/8221-eb60b5bc692a3361.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/826.e9a26064f474949e.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/8459-a351487a4480f584.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/8482-75823d513a4ce151.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/8488.8da515248cda3f3a.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/8584.388a1779d9b203af.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/8637.a55e098e61ffefb8.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/8817.61bb7ffbba502ecb.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/9207-8e6a12db30373f32.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/9263-00ae932c62696a86.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/9318.caafc3a130e8769c.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/932.da3fb55951882562.js +0 -93
  547. package/apps/pwa/out/_next/static/chunks/9433.002f8426ac75b6b7.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/9464.dd631a5a14b1d660.js +0 -24
  549. package/apps/pwa/out/_next/static/chunks/9739.8d5f36ac67a3b236.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/9743.beeabb9d5f86abe4.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/9843-a23d3f32dc43f715.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/994-28211077c7c587d5.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-3b4c947f174e33d8.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/approvals/page-369d5ce99483f859.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-63c0ed0eb26e5fce.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/board/page-62802c6003bc85cc.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ecb41560d4e77be9.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/bots/page-7b65ad8130c93e95.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8755477c15a9060c.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/chat/page-5725447dd972c471.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/consult/page-b979de0591a69375.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/control/page-6d0796bf4633751a.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/decisions/page-59c31dc0394e67b4.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/design-check/page-12dfc95f6cc5c718.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/editor/page-25788244e66aa4dd.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/exec/page-a2555dc705466322.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/field/page-4fb31eb476dcbad3.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/healthz/route-3b4c947f174e33d8.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/inside/page-578729adbeaa40cb.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/intake/page-a1d20fffbec35205.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/layout-4a558f94516b8004.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/live/page-9e3834705b86145e.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/loops/page-cb1c5fe746ea4014.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/market/page-9ee320507356582a.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66fd183b74835886.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/morning/page-3272a064a01a9927.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/not-found-f281b7a7f6ef2529.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/observatory/page-d45a65f8b7124fcd.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-e9c1fe684dd19a66.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-b8322932fc6e34de.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-82c66d9af2290575.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/outputs/page-63c6752009c97b05.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/page-bb33d42ebc358aec.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7e4c9379197d7214.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-38bf1e9a7001732b.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2e5b752012389d56.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-398c0faa173203f7.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/settings/page-002f53a965b6445c.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7f75997f61a26269.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/setup/layout-3b4c947f174e33d8.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/setup/page-05631753294f23fb.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/share/page-edd700fd2cc3d7f8.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/showroom/page-105f83ecb4594365.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/tasks/page-5823e87d2017f19c.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/today/page-b51ca09566c03de7.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/app/trace/page-8aacd5ded5dbac60.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/app/vault/page-bae3365a0b3c6a8e.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ed52b48ff1aa7fd.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/app/workflows/page-b6f8cad35fe00946.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6d25ceda500d2457.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-b6401a5f2c962fe8.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/ebf6cf5e.0d2f887b040e80fb.js +0 -122
  603. package/apps/pwa/out/_next/static/chunks/f09ec7d7-d29abe23edcbb193.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/ffdc3a04.b9c23c1b28dd4a96.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/framework-9322cb8a56ac081c.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/main-2fbab294e49a1561.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/main-app-f36729f56621a8f3.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/pages/_app-2a62e450b47c6114.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/pages/_error-5e17f7b445f34d43.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/webpack-b4263e2190ea7adf.js +0 -1
  611. /package/apps/pwa/out/_next/static/{L04y73dlwKeTW-GetaTF3 → T1mBQqmkwKDLGtyisDCOd}/_ssgManifest.js +0 -0
@@ -33,6 +33,12 @@ import { getTheme } from '../../themes/index.js';
33
33
  // same shape `pluginHost.loadThemeTokens` produces — not a full ThemeTokens.
34
34
  export type PluginThemeLoader = (id: string) => ThemeTokenInput | null | undefined;
35
35
 
36
+ export interface PluginThemeAppliedEvent {
37
+ id: string;
38
+ tokensCount: number;
39
+ source: 'plugin';
40
+ }
41
+
36
42
  export interface ResolveActiveThemeOptions {
37
43
  /** Raw `dashboard.theme` value from user-config. Shape is flexible:
38
44
  * `null` | `string` | `{ active: string, ... }` | `{ tokens: {...} }`. */
@@ -44,17 +50,19 @@ export interface ResolveActiveThemeOptions {
44
50
  * (so a broken plugin doesn't crash the dashboard) but reported
45
51
  * here for visibility. */
46
52
  onPluginError?: (message: string) => void;
53
+ /** Called after plugin tokens have been merged into the resolved theme. */
54
+ onPluginApplied?: (event: PluginThemeAppliedEvent) => void;
47
55
  }
48
56
 
49
57
  /** Resolve the active dashboard theme from user-config, with preset
50
58
  * + plugin + default fallback layers. Pure function — no I/O. */
51
59
  export function resolveActiveTheme(opts: ResolveActiveThemeOptions): ThemeTokens {
52
- const { raw, loadPlugin, onPluginError } = opts;
60
+ const { raw, loadPlugin, onPluginError, onPluginApplied } = opts;
53
61
 
54
62
  // Extract active name + user overrides from the raw user-config
55
63
  // value. The config shape evolved over time — handle all known
56
64
  // forms defensively.
57
- const activeName = extractActiveName(raw);
65
+ const activeName = getActiveThemeName(raw);
58
66
  const rawOverrides: ThemeTokenInput | undefined = extractOverrides(raw);
59
67
 
60
68
  // Branch 1: preset registry hit.
@@ -70,10 +78,20 @@ export function resolveActiveTheme(opts: ResolveActiveThemeOptions): ThemeTokens
70
78
  try {
71
79
  const contributed = loadPlugin(activeName);
72
80
  if (contributed) {
73
- return mergeThemeTokens(
81
+ const resolved = mergeThemeTokens(
74
82
  mergeThemeTokens(DEFAULT_THEME_TOKENS, contributed),
75
83
  rawOverrides ?? {},
76
84
  );
85
+ try {
86
+ onPluginApplied?.({
87
+ id: activeName,
88
+ tokensCount: countTokenLeaves(contributed),
89
+ source: 'plugin',
90
+ });
91
+ } catch {
92
+ // Observation failures must not discard an already-resolved theme.
93
+ }
94
+ return resolved;
77
95
  }
78
96
  } catch (err: unknown) {
79
97
  const message =
@@ -86,7 +104,18 @@ export function resolveActiveTheme(opts: ResolveActiveThemeOptions): ThemeTokens
86
104
  return resolveThemeTokens(raw);
87
105
  }
88
106
 
89
- function extractActiveName(raw: unknown): string | null {
107
+ function countTokenLeaves(value: unknown, isRoot = true): number {
108
+ if (value === null || value === undefined) return 0;
109
+ if (typeof value !== 'object') return 1;
110
+ if (Array.isArray(value)) {
111
+ return value.reduce((count, item) => count + countTokenLeaves(item, false), 0);
112
+ }
113
+ return Object.entries(value as Record<string, unknown>)
114
+ .filter(([key]) => !isRoot || key !== 'name')
115
+ .reduce((count, [, item]) => count + countTokenLeaves(item, false), 0);
116
+ }
117
+
118
+ export function getActiveThemeName(raw: unknown): string | null {
90
119
  if (typeof raw === 'string') return raw.trim() || null;
91
120
  if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
92
121
  const active = (raw as Record<string, unknown>).active;
@@ -381,9 +381,17 @@ export function readCrontab(): string {
381
381
  export function inventoryCrontab(db: Database, opts: { crontab?: string; now?: string } = {}): { total: number; added: number; disabled: number; vanished: number } {
382
382
  const text = opts.crontab ?? readCrontab();
383
383
  const now = opts.now ?? new Date().toISOString();
384
- const existing = new Map((db.prepare(`SELECT id, name, command, source, run_via FROM schedule_registry`).all() as Array<{
384
+ const nowMs = Date.parse(now);
385
+ const existing = new Map((db.prepare(`SELECT id, name, command, source, run_via, cron, category, enabled, disabled_reason, last_seen, raw FROM schedule_registry`).all() as Array<{
385
386
  id: string; name: string; command: string | null; source: string; run_via: string;
387
+ cron: string | null; category: string; enabled: number;
388
+ disabled_reason: string | null; last_seen: string | null; raw: string | null;
386
389
  }>).map(r => [r.id, r]));
390
+ const lastSeenDue = (lastSeen: string | null): boolean => {
391
+ if (!lastSeen) return true;
392
+ const lastSeenMs = Date.parse(lastSeen);
393
+ return !Number.isFinite(lastSeenMs) || !Number.isFinite(nowMs) || nowMs - lastSeenMs >= 10 * 60_000;
394
+ };
387
395
  let total = 0, added = 0, disabled = 0, vanished = 0;
388
396
  const seenIds = new Set<string>();
389
397
  for (const line of text.split('\n')) {
@@ -398,8 +406,10 @@ export function inventoryCrontab(db: Database, opts: { crontab?: string; now?: s
398
406
  const off = parseDisabledCronLine(line);
399
407
  if (!off) continue;
400
408
  const offId = cronEntryId(off.cron, unwrapCronCommand(off.command));
401
- if (existing.get(offId)?.source !== 'crontab' || existing.get(offId)?.run_via === 'trigger') continue;
409
+ const prior = existing.get(offId);
410
+ if (prior?.source !== 'crontab' || prior.run_via === 'trigger') continue;
402
411
  seenIds.add(offId);
412
+ if (prior.enabled === 0 && prior.disabled_reason === 'manual' && !lastSeenDue(prior.last_seen)) continue;
403
413
  db.run(`UPDATE schedule_registry SET enabled = 0, disabled_reason = 'manual', last_seen = ? WHERE id = ?`, [now, offId]);
404
414
  disabled++;
405
415
  continue;
@@ -417,6 +427,10 @@ export function inventoryCrontab(db: Database, opts: { crontab?: string; now?: s
417
427
  // trigger's state (including an explicit disable). Keep the migrated row intact.
418
428
  if (prior?.run_via === 'trigger') continue;
419
429
  if (!prior) added++;
430
+ const unchanged = prior?.source === 'crontab' && prior.name === name && prior.cron === parsed.cron
431
+ && prior.command === parsed.command && prior.category === category
432
+ && prior.enabled === 1 && prior.disabled_reason == null && prior.raw === line.trim();
433
+ if (unchanged && !lastSeenDue(prior.last_seen)) continue;
420
434
  db.run(
421
435
  `INSERT INTO schedule_registry (id, name, source, cron, command, category, domain, enabled, last_seen, managed_by, raw)
422
436
  VALUES (?, ?, 'crontab', ?, ?, ?, 'finance', 1, ?, 'manual', ?)
@@ -0,0 +1,76 @@
1
+ import { debug } from '../debug/log.js';
2
+
3
+ /** Keep git status responsive while active, then back off after two idle minutes. */
4
+ export function dashboardIdleMs(now: number, lastInputAt: number, streaming: boolean): number {
5
+ // A model turn is active work even when it produces no terminal input.
6
+ // Do not age the dashboard into idle cadence until that stream finishes.
7
+ return streaming ? 0 : Math.max(0, now - lastInputAt);
8
+ }
9
+
10
+ export function gitDirtyRefreshIntervalMs(idleMs: number): number {
11
+ return idleMs >= 2 * 60_000 ? 60_000 : 5_000;
12
+ }
13
+
14
+ // CLASSIC-IDLE-GIT-POLL (대표 10-21 «classic 백그라운드가 너무 무겁다») — a classic TUI left alone for five
15
+ // minutes spawned 63 git children (dirty probe ⊕ snapshot keep-warm). After this many idle ms the poller
16
+ // stops spawning git; the next input (or any activity that resets the idle clock) warms once and resumes.
17
+ export const GIT_POLL_IDLE_PAUSE_MS = 2 * 60_000;
18
+
19
+ export interface IdleGitPollState { active: boolean; reason: string }
20
+
21
+ export interface IdleGitPollDeps {
22
+ /** Idle ms right now (`dashboardIdleMs` — a running stream counts as active). */
23
+ idleMs: () => number;
24
+ /** One regular pass while active: dirty probe ⊕ snapshot keep-warm. */
25
+ poll: (idleMs: number) => void;
26
+ /** The one warm when polling resumes after a pause. */
27
+ warm: () => void;
28
+ /** Run the warm off the input path (default: a 0ms timer — after the key has been handled and painted). */
29
+ defer?: (fn: () => void) => void;
30
+ /** Transition record (default: `debug.log('git-fs.idle', 'poll-state', …)`). */
31
+ log?: (state: IdleGitPollState) => void;
32
+ pauseAfterMs?: number;
33
+ }
34
+
35
+ export interface IdleGitPoll {
36
+ /** The periodic timer body: pauses once idle, resumes (with one warm) when activity is back. */
37
+ tick(): void;
38
+ /** Called on the input path. Costs one boolean check while active; when paused it only schedules the warm. */
39
+ noteActivity(reason?: string): void;
40
+ isActive(): boolean;
41
+ }
42
+
43
+ export function createIdleGitPoll(deps: IdleGitPollDeps): IdleGitPoll {
44
+ const pauseAfterMs = deps.pauseAfterMs ?? GIT_POLL_IDLE_PAUSE_MS;
45
+ const log = deps.log ?? ((state: IdleGitPollState) => debug.log('git-fs.idle', 'poll-state', state));
46
+ const defer = deps.defer ?? ((fn: () => void) => {
47
+ const timer = setTimeout(fn, 0);
48
+ (timer as unknown as { unref?: () => void }).unref?.();
49
+ });
50
+ let active = true;
51
+ const setActive = (next: boolean, reason: string): boolean => {
52
+ if (active === next) return false;
53
+ active = next;
54
+ try { log({ active, reason }); } catch { /* observation must not break polling */ }
55
+ return true;
56
+ };
57
+ const warm = (): void => { try { deps.warm(); } catch { /* fail-soft */ } };
58
+ return {
59
+ tick() {
60
+ const idleMs = deps.idleMs();
61
+ if (idleMs >= pauseAfterMs) {
62
+ setActive(false, 'idle');
63
+ return;
64
+ }
65
+ // Activity that did not come through `noteActivity` (a stream, an edit) — resume here.
66
+ if (setActive(true, 'activity')) warm();
67
+ deps.poll(idleMs);
68
+ },
69
+ noteActivity(reason = 'input') {
70
+ if (active) return;
71
+ setActive(true, reason);
72
+ defer(warm);
73
+ },
74
+ isActive: () => active,
75
+ };
76
+ }
@@ -94,6 +94,8 @@ interface CacheEntry {
94
94
  dirty: DirtyCount | null;
95
95
  aheadBehind: AheadBehind | null;
96
96
  lastDirtyProbeAt: number;
97
+ /** Prevent an older asynchronous probe from replacing a synchronous refresh. */
98
+ dirtyVersion: number;
97
99
  watcher: GitFileWatcher;
98
100
  listeners: Set<() => void>;
99
101
  }
@@ -139,6 +141,7 @@ function ensureEntry(cwd: string): CacheEntry | null {
139
141
  dirty: null,
140
142
  aheadBehind: null,
141
143
  lastDirtyProbeAt: 0,
144
+ dirtyVersion: 0,
142
145
  watcher,
143
146
  listeners: new Set(),
144
147
  };
@@ -212,6 +215,8 @@ export function refreshDirty(
212
215
  lastDirtyProbeAt: entry.lastDirtyProbeAt,
213
216
  };
214
217
  }
218
+ // A synchronous save wins over any asynchronous probe that began before it.
219
+ entry.dirtyVersion += 1;
215
220
  entry.dirty = probeDirty(entry.located.root);
216
221
  entry.aheadBehind = probeAheadBehind(entry.located.root);
217
222
  entry.lastDirtyProbeAt = Date.now();
@@ -268,13 +273,19 @@ function probeDirtyAsyncFor(entry: CacheEntry, opts: { fresh: boolean }): Promis
268
273
  const running = dirtyInFlight.get(entry);
269
274
  if (running && !opts.fresh) return running;
270
275
  const root = entry.located.root;
271
- const start = (): Promise<GitStatusView> => Promise.all([probeDirtyAsync(root), probeAheadBehindAsync(root)])
272
- .then(([dirty, aheadBehind]) => {
273
- entry.dirty = dirty;
274
- entry.aheadBehind = aheadBehind;
275
- entry.lastDirtyProbeAt = Date.now();
276
- return viewOf(entry);
277
- });
276
+ const start = (): Promise<GitStatusView> => {
277
+ const version = entry.dirtyVersion;
278
+ return Promise.all([probeDirtyAsync(root), probeAheadBehindAsync(root)])
279
+ .then(([dirty, aheadBehind]) => {
280
+ if (entry.dirtyVersion === version) {
281
+ entry.dirtyVersion += 1;
282
+ entry.dirty = dirty;
283
+ entry.aheadBehind = aheadBehind;
284
+ entry.lastDirtyProbeAt = Date.now();
285
+ }
286
+ return viewOf(entry);
287
+ });
288
+ };
278
289
  const probe = (running ? running.then(start, start) : start())
279
290
  .finally(() => { if (dirtyInFlight.get(entry) === probe) dirtyInFlight.delete(entry); });
280
291
  dirtyInFlight.set(entry, probe);
@@ -24,15 +24,51 @@ const GIT_TIMEOUT_MS = 30_000;
24
24
  * ⛔ 깊이 없이 얕은 저장소로 다른 브랜치를 fetch 하면 그 브랜치의 «전 이력»을 받는다(실측 193s vs 4s · 30s 에 SIGTERM). */
25
25
  const SHALLOW_BASE_FETCH_DEPTH = 50;
26
26
  const WORKTREE_ADD_TIMEOUT_MS = 5 * 60_000;
27
+ const WORKTREE_ADD_TIMEOUT_PER_TRACKED_FILE_MS = 600_000 / 25_000;
28
+ const WORKTREE_ADD_TIMEOUT_MAX_MS = 15 * 60_000;
29
+ const TRACKED_FILE_COUNT_MAX_BUFFER = 16 * 1024 * 1024;
27
30
 
28
31
  export interface WorktreeAddDeps {
29
32
  spawn?: (command: string, args: string[], options: { cwd: string; encoding: 'utf8'; timeout: number; maxBuffer: number }) => SpawnSyncReturns<string>;
33
+ countTrackedFiles?: (repoRoot: string, ref?: string) => number | null;
30
34
  }
31
35
 
32
- function worktreeAddTimeoutMs(): number {
36
+ function countTrackedFiles(repoRoot: string, ref?: string): number | null {
37
+ const args = ref ? ['ls-tree', '-r', '-z', '--name-only', ref] : ['ls-files', '-z'];
38
+ const result = spawnSync('git', args, {
39
+ cwd: repoRoot,
40
+ encoding: 'utf8',
41
+ timeout: GIT_TIMEOUT_MS,
42
+ maxBuffer: TRACKED_FILE_COUNT_MAX_BUFFER,
43
+ });
44
+ if (result.status !== 0 || result.error) return null;
45
+ const output = result.stdout ?? '';
46
+ if (output.length === 0) return 0;
47
+ let count = 0;
48
+ for (const character of output) if (character === '\0') count++;
49
+ return count;
50
+ }
51
+
52
+ type WorktreeAddTimeout = { ms: number; trackedFileCount: number | null; source: 'config' | 'tracked-files' | 'fallback' };
53
+
54
+ function worktreeAddTimeoutForTrackedFileCount(trackedFileCount: number | null): WorktreeAddTimeout {
55
+ if (trackedFileCount === null || !Number.isSafeInteger(trackedFileCount) || trackedFileCount < 0) {
56
+ return { ms: WORKTREE_ADD_TIMEOUT_MS, trackedFileCount: null, source: 'fallback' };
57
+ }
58
+ const scaledMs = Math.ceil(trackedFileCount * WORKTREE_ADD_TIMEOUT_PER_TRACKED_FILE_MS);
59
+ return { ms: Math.min(WORKTREE_ADD_TIMEOUT_MAX_MS, Math.max(WORKTREE_ADD_TIMEOUT_MS, scaledMs)), trackedFileCount, source: 'tracked-files' };
60
+ }
61
+
62
+ export function worktreeAddTimeoutForRepo(
63
+ repoRoot: string,
64
+ countTrackedFilesForRepo: (repoRoot: string, ref?: string) => number | null = countTrackedFiles,
65
+ trackedFileRef?: string,
66
+ ): WorktreeAddTimeout {
33
67
  const seconds = getUserConfig().harness?.worktreeAddTimeoutSec;
34
- return typeof seconds === 'number' && Number.isSafeInteger(seconds) && seconds > 0 && seconds <= 2_147_483
35
- ? seconds * 1000 : WORKTREE_ADD_TIMEOUT_MS;
68
+ if (typeof seconds === 'number' && Number.isSafeInteger(seconds) && seconds > 0 && seconds <= 2_147_483) {
69
+ return { ms: seconds * 1000, trackedFileCount: null, source: 'config' };
70
+ }
71
+ return worktreeAddTimeoutForTrackedFileCount(countTrackedFilesForRepo(repoRoot, trackedFileRef));
36
72
  }
37
73
 
38
74
  function worktreeAddFailureLines(stderr: string): { lastProgress: string | null; details: string[] } {
@@ -729,7 +765,9 @@ export function createWorktree(opts: CreateWorktreeOpts, deps: WorktreeAddDeps =
729
765
  // ★ 병렬 안전 재시도(2026-07-21) — 동시 worktree add 가 git 락에서 경쟁하면 즉시 실패 → 락/일시적
730
766
  // 에러면 백오프 재시도(패자가 승자의 락 해제를 기다렸다 성공). non-transient 는 재시도 없이 throw.
731
767
  const MAX_ATTEMPTS = 6;
732
- const addTimeoutMs = worktreeAddTimeoutMs();
768
+ const addTimeout = worktreeAddTimeoutForRepo(repoRoot, deps.countTrackedFiles ?? countTrackedFiles, baseCheckout);
769
+ const addTimeoutMs = addTimeout.ms;
770
+ debug.log('git-fs.worktree', 'add.start', { branch, timeoutMs: addTimeoutMs, trackedFileCount: addTimeout.trackedFileCount, timeoutSource: addTimeout.source });
733
771
  const addSpawn = deps.spawn ?? spawnSync;
734
772
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
735
773
  const res = addSpawn(
@@ -20,13 +20,13 @@ export interface NodeKindEntry {
20
20
  run?: NodeKindRun;
21
21
  }
22
22
 
23
- export const HARNESS_CORE_KINDS = ['agent', 'gate', 'git', 'judge', 'observe', 'hitl', 'subgraph', 'prompt'] as const;
23
+ export const HARNESS_CORE_KINDS = ['agent', 'gate', 'git', 'judge', 'observe', 'hitl', 'subgraph', 'prompt', 'classify'] as const;
24
24
 
25
25
  // Workflow validation, the palette, and executor variant labels read the same keys.
26
26
  export const WORKFLOW_CORE_KINDS = ['prompt', 'bash', 'skill', 'cft', 'approval', 'if', 'switch', 'iteration', 'classify', 'extract', 'set', 'filter', 'template', 'http', 'showroom', 'task', 'scheduleTrigger', 'webhookTrigger', 'discordTrigger', 'telegramTrigger', 'manualTrigger', 'chatTrigger', 'subworkflow', 'knowledge'] as const;
27
27
 
28
28
  const descriptions: Record<GraphKind, readonly string[]> = {
29
- harness: ['Run an agent', 'Check an execution gate', 'Perform git operations', 'Judge an outcome', 'Observe a result', 'Request human input', 'Run a child graph', 'Run an LLM prompt'],
29
+ harness: ['Run an agent', 'Check an execution gate', 'Perform git operations', 'Judge an outcome', 'Observe a result', 'Request human input', 'Run a child graph', 'Run an LLM prompt', 'Classify input'],
30
30
  workflow: ['Run an LLM prompt', 'Run a shell command', 'Invoke a skill', 'Invoke a CFT method', 'Request approval', 'Branch on a condition', 'Select a case', 'Iterate items', 'Classify input', 'Extract structured data', 'Assign variables', 'Filter items', 'Render a template', 'Make an HTTP request', 'Run a showroom', 'Create a task', 'Start on a schedule', 'Start on a webhook', 'Start on Discord', 'Start on Telegram', 'Start manually', 'Start on chat', 'Call another workflow', 'Search the vault for context'],
31
31
  };
32
32
 
@@ -1,7 +1,8 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
2
  import { execFile, spawnSync } from 'node:child_process';
3
- import { existsSync, linkSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { existsSync, linkSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
4
4
  import { dirname, join, resolve, sep } from 'node:path';
5
+ import { tmpdir } from 'node:os';
5
6
  import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
6
7
  import { getElanousConfigDir, getElanousConfigDirOverride } from '../elanous-config-dir.js';
7
8
  import { debug } from '../debug/log.js';
@@ -23,8 +24,9 @@ export { UnitNotFoundError } from '../self-implement/graph-unit-resolver.js';
23
24
  import { createGraphVariant, type GraphVariantPlan } from '../self-implement/graph-variant.js';
24
25
  import { psProcessStartMs, START_TOLERANCE_MS } from '../harness/harness-stop.js';
25
26
  import { executeBashNode } from '../workflow-runtime/nodes/bash.js';
27
+ import { executeClassifyNode } from '../workflow-runtime/nodes/classify.js';
26
28
  import { interpolate } from '../workflow-runtime/variables.js';
27
- import type { BashNode, NodeExecContext, WorkflowDeps } from '../workflow-runtime/types.js';
29
+ import type { BashNode, ClassifyNode, NodeExecContext, NodeOutput, WorkflowDeps } from '../workflow-runtime/types.js';
28
30
  import { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeWaiver, type AcceptedRegression, type GateExecutorOverride, type GatePodPoolOverride } from '../../scripts/release-loop/resume.js';
29
31
 
30
32
  type BashRun = WorkflowDeps['runBash'];
@@ -83,6 +85,47 @@ function topLevelTerminalStatus(graphId: string, terminal: string): 'done' | 'fa
83
85
  return terminal === 'done' || terminal === 'failed' ? terminal : undefined;
84
86
  }
85
87
 
88
+ /** HARNESS-FULL-GRAPH-LOOPS — the four goal loops are self-implement harness templates: their recipes are
89
+ * catalog roles (no `recipes.yaml` sidecar) and some edges branch on `changed-files`. `graph run` can only
90
+ * *plan* them: `--dry-run` walks the declared success route; a real run is refused (the harness owns effects). */
91
+ const HARNESS_LOOP_GRAPH_IDS: ReadonlySet<string> = new Set(['default-loop', 'document-loop', 'operate-loop', 'research-loop']);
92
+
93
+ type PlannedStep = { outcome?: string; to: string };
94
+
95
+ /** Shortest declared route (map/to edges only — fallbacks are not a success branch) to a terminal that maps to `done`.
96
+ * Ties keep the first declared outcome, so the plan follows the YAML's own order. A unit call only takes its first
97
+ * declared exit (the same stand-in a plain dry run uses) — its `rework` exit is a shortcut, not the success path. */
98
+ function planHarnessLoopRoute(graph: GraphTemplateSpec, unitSuccessExit: (nodeId: string) => string | undefined): Map<string, PlannedStep> {
99
+ const candidates = new Map<string, PlannedStep[]>();
100
+ for (const node of graph.nodes) {
101
+ const edge = graph.edges.find((e) => e.from === node.nodeId);
102
+ if (!edge) continue;
103
+ const exit = unitSuccessExit(node.nodeId);
104
+ candidates.set(node.nodeId, edge.to !== undefined ? [{ to: edge.to }]
105
+ : Object.entries(edge.map ?? {}).filter(([outcome]) => exit === undefined || outcome === exit).map(([outcome, to]) => ({ outcome, to })));
106
+ }
107
+ const distance = new Map<string, number>();
108
+ for (const terminal of graph.terminalNodes) if (topLevelTerminalStatus(graph.graphId, terminal) === 'done') distance.set(terminal, 0);
109
+ const route = new Map<string, PlannedStep>();
110
+ for (let changed = true; changed;) {
111
+ changed = false;
112
+ for (const [nodeId, steps] of candidates) {
113
+ if (graph.terminalNodes.includes(nodeId)) continue;
114
+ let best: { step: PlannedStep; d: number } | undefined;
115
+ for (const step of steps) {
116
+ const d = distance.get(step.to);
117
+ if (d !== undefined && (!best || d + 1 < best.d)) best = { step, d: d + 1 };
118
+ }
119
+ if (best && (distance.get(nodeId) ?? Infinity) > best.d) {
120
+ distance.set(nodeId, best.d);
121
+ route.set(nodeId, best.step);
122
+ changed = true;
123
+ }
124
+ }
125
+ }
126
+ return route;
127
+ }
128
+
86
129
  export interface GraphRunState {
87
130
  graphId: string;
88
131
  runId: string;
@@ -125,6 +168,46 @@ export interface GraphRunState {
125
168
  statePath: string;
126
169
  }
127
170
 
171
+ export interface GraphClassifyInput {
172
+ arguments: string;
173
+ classify: ClassifyNode['classify'];
174
+ /** Production consumer that asked for the verdict, so one ledger shape separates callers. */
175
+ caller?: string;
176
+ }
177
+
178
+ /** Run the workflow classifier as a persisted harness graph node. The workflow verdict is unchanged. */
179
+ export async function runGraphClassifyNode(node: ClassifyNode, ctx: NodeExecContext, callLLM: WorkflowDeps['callLLM'],
180
+ meta: { caller?: string } = {}): Promise<NodeOutput> {
181
+ const dir = mkdtempSync(join(tmpdir(), 'elanous-graph-classify-'));
182
+ try {
183
+ const path = join(dir, 'graph.yaml');
184
+ writeFileSync(join(dir, 'recipes.yaml'), '{}\n');
185
+ writeFileSync(path, stringifyYaml({
186
+ graph_id: 'self-implement-classify', version: 1, entry_node: 'classify', terminal_nodes: ['done', 'failed'],
187
+ nodes: [
188
+ { node_id: 'classify', kind: 'classify', recipe: 'none', max_visits: 1 },
189
+ { node_id: 'done', kind: 'gate', recipe: 'none', max_visits: 1 },
190
+ { node_id: 'failed', kind: 'gate', recipe: 'none', max_visits: 1 },
191
+ ],
192
+ edges: [{ from: 'classify', on: 'outcome', map: { ok: 'done', fail: 'failed' } }],
193
+ }));
194
+ const run = await runGraph(path, { input: { arguments: ctx.arguments, classify: node.classify,
195
+ ...(meta.caller === undefined ? {} : { caller: meta.caller }) } satisfies GraphClassifyInput,
196
+ quiet: true,
197
+ deps: { callLLM: async (prompt) => callLLM({ prompt, ...(ctx.resolvedModel === undefined ? {} : { model: ctx.resolvedModel }),
198
+ ...(ctx.resolvedProvider === undefined ? {} : { provider: ctx.resolvedProvider }),
199
+ ...(ctx.signal === undefined ? {} : { signal: ctx.signal }) }) } });
200
+ const recorded = lastJsonObject(run.nodes[0]?.output);
201
+ if (!recorded || typeof recorded.durationMs !== 'number' || typeof recorded.ok !== 'boolean' || !('output' in recorded)) {
202
+ throw new Error(`classify graph did not record a result: ${run.statePath}`);
203
+ }
204
+ return { ok: recorded.ok, output: recorded.output, durationMs: recorded.durationMs,
205
+ ...(typeof recorded.error === 'string' ? { error: recorded.error } : {}) };
206
+ } finally {
207
+ rmSync(dir, { recursive: true, force: true });
208
+ }
209
+ }
210
+
128
211
  export interface GraphRunOptions {
129
212
  input?: unknown;
130
213
  /** Bytes already checked by the tick gate, used for a current-graph start or legacy resume. */
@@ -135,6 +218,8 @@ export interface GraphRunOptions {
135
218
  resumeRunId?: string;
136
219
  resumeGraphId?: string;
137
220
  fromNodeId?: string;
221
+ /** Embedded runs (a classify verdict inside self-implement) keep the per-node `[graph]` stderr lines off the caller's terminal; debug.log still records every node. */
222
+ quiet?: boolean;
138
223
  /** Release-only: accept the recorded failed node without executing it, and expose its regression to known-issues. */
139
224
  releaseWaiver?: { nodeId: string; reason: string; requestedRegressions: AcceptedRegression[]; acceptedRegressions: AcceptedRegression[]; unmeasured?: string };
140
225
  /** Release-only: add accepted issues independently of a waiver at a restart boundary. */
@@ -614,7 +699,15 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
614
699
  throw new Error(`unsupported terminal node: ${terminal} (expected ${graph.unit ? graph.unit.exits.join(' or ') : 'done or failed'})`);
615
700
  }
616
701
  }
617
- const recipeSource = readFileSync(graphMode === 'snapshot' ? join(snapshotDir!, 'recipes.yaml') : join(dirname(path), 'recipes.yaml'), 'utf8');
702
+ const recipePath = graphMode === 'snapshot' ? join(snapshotDir!, 'recipes.yaml') : join(dirname(path), 'recipes.yaml');
703
+ // A harness loop template without a command sidecar is planned, never executed, by this runner.
704
+ const harnessLoopTemplate = !graph.unit && HARNESS_LOOP_GRAPH_IDS.has(graph.graphId) && graphMode === 'current' && !existsSync(recipePath);
705
+ if (harnessLoopTemplate && (options.dryRun !== true || options.resumeRunId)) {
706
+ debug.log('graph.run', 'harness-loop-refused', { graphId: graph.graphId, path: resolve(path), why: 'real-run' });
707
+ throw new Error(`harness loop template ${graph.graphId} runs through the self-implement harness — graph run only plans it with --dry-run`);
708
+ }
709
+ const harnessLoopRoute = harnessLoopTemplate ? planHarnessLoopRoute(graph, (nodeId) => units.get(nodeId)?.template.unit?.exits[0]) : undefined;
710
+ const recipeSource = harnessLoopTemplate ? '{}\n' : readFileSync(recipePath, 'utf8');
618
711
  if (graphMode === 'snapshot' && saved?.graphSnapshot &&
619
712
  (createHash('sha256').update(source).digest('hex') !== saved.graphSnapshot.graphSha ||
620
713
  createHash('sha256').update(recipeSource).digest('hex') !== saved.graphSnapshot.recipesSha)) {
@@ -626,10 +719,20 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
626
719
  const approvalSourceHash = createHash('sha256').update(variantSource).update('\0').update(recipeSource).digest('hex');
627
720
  // Resolve all commands before executing any node; an unknown recipe must never produce a partial publication.
628
721
  for (const node of graph.nodes) {
722
+ if (node.kind === 'classify' && node.recipe !== 'none') throw new Error(`classify node cannot execute a recipe: ${node.nodeId}`);
629
723
  if (node.recipe === 'none') continue;
630
724
  const resolved = resolveNodeRecipe(node, recipes, catalogRoles);
725
+ // Harness loop recipes are labels the self-implement harness dispatches by node id (most are outside the
726
+ // node catalog — graph-catalog.test.ts); a planned loop never executes them, and a real run is refused above.
727
+ if (harnessLoopRoute) continue;
631
728
  if (!resolved.command && !resolved.prompt && !resolved.approval) throw new Error(`unknown command recipe for ${node.nodeId}: ${node.recipe}`);
632
729
  }
730
+ if (harnessLoopRoute) {
731
+ if (!harnessLoopRoute.has(graph.entryNode)) throw new Error(`harness loop ${graph.graphId} has no declared route from ${graph.entryNode} to a success terminal`);
732
+ const planned: string[] = [graph.entryNode];
733
+ for (let at = harnessLoopRoute.get(graph.entryNode); at; at = harnessLoopRoute.get(at.to)) planned.push(at.to);
734
+ debug.log('graph.run', 'harness-loop-plan', { graphId: graph.graphId, path: resolve(path), route: planned });
735
+ }
633
736
  const graphId = safeSegment(graph.graphId);
634
737
  const isReleaseLoop = graphId === 'release-loop';
635
738
  // A copied or saved release graph is still a release graph. Its path cannot grant or revoke
@@ -951,7 +1054,7 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
951
1054
  const startedAt = performance.now();
952
1055
  const nodeStartedAt = new Date().toISOString();
953
1056
  if (!resumingCompleted) state.currentNode = { nodeId: current, startedAt: nodeStartedAt };
954
- console.error(`[graph] ${current} start (0.00s)`);
1057
+ if (!options.quiet) console.error(`[graph] ${current} start (0.00s)`);
955
1058
  debug.log('graph.run', 'node', { graphId, runId, nodeId: current, phase: 'start', dryRun: state.dryRun });
956
1059
  publishInsideEvent({ kind: 'node', graphId, runId, nodeId: current, phase: 'start' });
957
1060
  log('node-start', { graphId, runId, nodeId: current, visit: visits.get(current), dryRun: state.dryRun });
@@ -1030,7 +1133,12 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
1030
1133
  state.executed++;
1031
1134
  }
1032
1135
  let unitExecuted = false;
1033
- if (units.has(current) && !waiving && state.dryRun && !resumingCompleted) {
1136
+ const plannedStep: PlannedStep | undefined = harnessLoopRoute?.get(current);
1137
+ if (harnessLoopRoute && !graph.terminalNodes.includes(current)) {
1138
+ if (!plannedStep) throw new Error(`harness loop ${graphId} has no declared success route from ${current}`);
1139
+ // The planned outcome stands in for the harness node (or unit) that a real loop run would execute.
1140
+ output = JSON.stringify({ outcome: plannedStep.outcome ?? 'ok', dryRun: true, plannedNext: plannedStep.to });
1141
+ } else if (units.has(current) && !waiving && state.dryRun && !resumingCompleted) {
1034
1142
  // Dry run plans the walk only: no child run, no unit event, no unitCalls entry; the first declared exit stands in.
1035
1143
  output = JSON.stringify({ outcome: units.get(current)!.template.unit!.exits[0], dryRun: true });
1036
1144
  } else if (units.has(current) && !waiving && !resumingCompleted) {
@@ -1132,6 +1240,39 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
1132
1240
  }
1133
1241
  state.executed++;
1134
1242
  }
1243
+ if (node.kind === 'classify' && !waiving && !state.dryRun && !resumingCompleted) {
1244
+ const input = state.input as GraphClassifyInput | undefined;
1245
+ const classify = input?.classify;
1246
+ if (!input || typeof input.arguments !== 'string' || !classify || typeof classify.input !== 'string' ||
1247
+ !Array.isArray(classify.classes) || !classify.classes.length || !classify.classes.every(c => typeof c === 'string' && c.length > 0) ||
1248
+ (classify.retries !== undefined && (!Number.isSafeInteger(classify.retries) || classify.retries < 0)) ||
1249
+ (classify.retryDelayMs !== undefined && (!Number.isSafeInteger(classify.retryDelayMs) || classify.retryDelayMs < 0))) {
1250
+ throw new Error('classify node requires input.arguments and a valid input.classify');
1251
+ }
1252
+ try {
1253
+ const result = await executeClassifyNode({ id: current, classify }, {
1254
+ arguments: input.arguments, artifactsDir: dirname(statePath), outputs: {},
1255
+ resolvedProvider: undefined, resolvedModel: undefined, toolPolicy: {},
1256
+ }, { callLLM: async ({ prompt }) => {
1257
+ if (!options.deps?.callLLM) throw new Error('classify node requires an injected LLM (deps.callLLM)');
1258
+ return options.deps.callLLM(prompt);
1259
+ }, runBash: async () => ({ stdout: '', stderr: '', exitCode: 0 }) });
1260
+ output = JSON.stringify({ input: input.arguments, ok: result.ok, verdict: result.output, output: result.output,
1261
+ durationMs: result.durationMs, ...(result.error ? { error: result.error } : {}) });
1262
+ ok = result.ok;
1263
+ error = result.error;
1264
+ } catch (cause) {
1265
+ ok = false;
1266
+ error = cause instanceof Error ? cause.message : String(cause);
1267
+ output = JSON.stringify({ input: input.arguments, ok, verdict: 'unknown', output: 'unknown', durationMs: 0, error });
1268
+ }
1269
+ const recorded = lastJsonObject(output);
1270
+ debug.log('graph.classify', 'node-result', { graphId, runId, nodeId: current, caller: input.caller ?? null,
1271
+ input: input.arguments, inputChars: input.arguments.length, classes: classify.classes,
1272
+ verdict: recorded?.verdict ?? 'unknown', output: recorded?.output ?? 'unknown', ok,
1273
+ ...(error ? { error } : {}) });
1274
+ state.executed++;
1275
+ }
1135
1276
  const seconds = Number(((performance.now() - startedAt) / 1000).toFixed(2));
1136
1277
  // A wall clock stepped back mid-node must not record an end before the start.
1137
1278
  const nodeEndedAt = new Date(Math.max(Date.now(), Date.parse(nodeStartedAt))).toISOString();
@@ -1152,7 +1293,7 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
1152
1293
  output = JSON.stringify({ outcome: ok ? 'approved' : 'rejected', graphId, runId, nodeId: current,
1153
1294
  ...(state.pending?.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending?.decidedAt ?? null });
1154
1295
  }
1155
- state.nodes.push({ nodeId: current, ok, exit, executed: !waiving && ((!!command && (!state.dryRun || !!command.dry_run_command)) || (!!prompt && !state.dryRun) || unitExecuted), ...((waiving || units.has(current) || ((command || prompt || approval) && (!state.dryRun || !!command?.dry_run_command))) && output !== undefined ? { output } : {}), ...(error ? { error } : {}),
1296
+ state.nodes.push({ nodeId: current, ok, exit, executed: !waiving && ((!!command && (!state.dryRun || !!command.dry_run_command)) || (!!prompt && !state.dryRun) || (node.kind === 'classify' && !state.dryRun) || unitExecuted), ...((waiving || harnessLoopRoute || units.has(current) || ((command || prompt || approval || node.kind === 'classify') && (!state.dryRun || !!command?.dry_run_command))) && output !== undefined ? { output } : {}), ...(error ? { error } : {}),
1156
1297
  ...(waiving ? { waiver: { reason: options.releaseWaiver!.reason, at: nodeEndedAt, ...(options.releaseWaiver!.unmeasured === undefined ? {} : { unmeasured: options.releaseWaiver!.unmeasured }), original: { exit: waivedRecord!.exit, ...(waivedRecord!.output === undefined ? {} : { output: waivedRecord!.output }), ...(waivedRecord!.error === undefined ? {} : { error: waivedRecord!.error }) } } } : {}),
1157
1298
  ...(approval && state.pending ? { ...(state.pending.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending.decidedAt } : {}),
1158
1299
  startedAt: nodeStartedAt, endedAt: nodeEndedAt, seconds });
@@ -1165,9 +1306,9 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
1165
1306
  delete state.pending;
1166
1307
  delete state.approvalSourceHash;
1167
1308
  }
1168
- const reported = waiving || units.has(current) || (prompt && !state.dryRun) || (command && (!state.dryRun || command.dry_run_command)) ? lastJsonObject(resumingCompleted ? completed?.output : output)?.outcome : undefined;
1169
- const namedOutcome = typeof reported === 'string' ? reported : undefined;
1170
- console.error(`[graph] ${current} ${ok ? 'ok' : 'fail'} (${seconds.toFixed(2)}s)`);
1309
+ const reported: unknown = waiving || harnessLoopRoute || units.has(current) || (prompt && !state.dryRun) || (command && (!state.dryRun || command.dry_run_command)) ? lastJsonObject(resumingCompleted ? completed?.output : output)?.outcome : undefined;
1310
+ const namedOutcome: string | undefined = typeof reported === 'string' ? reported : undefined;
1311
+ if (!options.quiet) console.error(`[graph] ${current} ${ok ? 'ok' : 'fail'} (${seconds.toFixed(2)}s)`);
1171
1312
  debug.log('graph.run', 'node', { graphId, runId, nodeId: current, phase: ok ? 'ok' : 'fail', seconds, exit });
1172
1313
  publishInsideEvent({ kind: 'node', graphId, runId, nodeId: current, phase: ok ? 'ok' : 'fail', seconds });
1173
1314
  publishInsideEvent({ kind: 'verdict', graphId, runId, nodeId: current, verdict: ok ? 'ok' : 'fail', ...(namedOutcome === undefined ? {} : { outcome: namedOutcome }) });
@@ -1225,7 +1366,9 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
1225
1366
  }
1226
1367
  persist();
1227
1368
  }
1228
- current = nextNode(graph.edges, current, state.dryRun && !command?.dry_run_command ? 'ok' : ok ? 'ok' : 'fail', namedOutcome);
1369
+ // A planned harness loop follows its route directly: `changed-files` edges have no runner condition.
1370
+ current = plannedStep ? plannedStep.to
1371
+ : nextNode(graph.edges, current, state.dryRun && !command?.dry_run_command ? 'ok' : ok ? 'ok' : 'fail', namedOutcome);
1229
1372
  resumedGrowth = false;
1230
1373
  if (current !== undefined) publishInsideEvent({ kind: 'edge', graphId, runId, from: fromNodeId, to: current });
1231
1374
  if (current === undefined) {
@@ -87,6 +87,8 @@ export interface GrokCredential {
87
87
  baseUrl: string;
88
88
  /** Bearer 값. ⛔ 로그·관측에 실지 마라. */
89
89
  token: string;
90
+ /** Expiry for the selected subscription scope; absent for API-key credentials. */
91
+ expiresAt?: string | null;
90
92
  /** 이 자격에 «반드시» 붙어야 하는 헤더(Authorization 제외). */
91
93
  headers: Record<string, string>;
92
94
  /** 관측용 — 값이 아니라 «출처»만. */
@@ -120,8 +122,10 @@ function readSubscriptionToken(
120
122
  }
121
123
  if (!parsed || typeof parsed !== 'object') return null;
122
124
 
123
- let best: { token: string; expiresAt: string | null; userId: string | null; ms: number } | null = null;
124
- for (const scope of Object.values(parsed as Record<string, unknown>)) {
125
+ type SubscriptionToken = { token: string; expiresAt: string | null; userId: string | null; ms: number };
126
+ let best: SubscriptionToken | null = null;
127
+ let hqMirror: SubscriptionToken | null = null;
128
+ for (const [scopeName, scope] of Object.entries(parsed as Record<string, unknown>)) {
125
129
  if (!scope || typeof scope !== 'object') continue;
126
130
  const s = scope as GrokAuthScope;
127
131
  if (typeof s.key !== 'string' || s.key.length === 0) continue;
@@ -133,9 +137,11 @@ function readSubscriptionToken(
133
137
  userId: typeof s.user_id === 'string' ? s.user_id : null,
134
138
  ms: Number.isNaN(ms) ? Number.NEGATIVE_INFINITY : ms,
135
139
  };
140
+ if (scopeName === 'https://auth.x.ai::hq-mirror') hqMirror = cand;
136
141
  if (best === null || cand.ms > best.ms) best = cand;
137
142
  }
138
- return best ? { token: best.token, expiresAt: best.expiresAt, userId: best.userId } : null;
143
+ const selected = hqMirror ?? best;
144
+ return selected ? { token: selected.token, expiresAt: selected.expiresAt, userId: selected.userId } : null;
139
145
  }
140
146
 
141
147
  /** 호스트 발급 문이 응답에 실을 access. user id 는 반환하지 않는다. */
@@ -302,6 +308,7 @@ function readGrokCredential(opts: {
302
308
  kind: 'subscription',
303
309
  baseUrl: GROK_SUBSCRIPTION_BASE_URL,
304
310
  token: sub.token,
311
+ expiresAt: sub.expiresAt,
305
312
  headers: grokSubscriptionHeaders({
306
313
  ...(opts.model ? { model: opts.model } : {}),
307
314
  userId: sub.userId,