elanous 0.2.19 → 0.2.21

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 (509) 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/MObU8ujkMciAKr6ljAV05/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/088d8e8a.8069543d082bec12.js +160 -0
  5. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +148 -0
  6. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +32 -0
  7. package/apps/pwa/out/_next/static/chunks/119fc678.6f591cdb84ca5791.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +309 -0
  9. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 1268.0840ff91e083fcbd.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +122 -0
  12. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +149 -0
  13. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +79 -0
  14. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +24 -0
  16. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +27 -0
  21. package/apps/pwa/out/_next/static/chunks/1948.5f35b39eef222a94.js +5 -0
  22. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{2977-e38442e86289f1d3.js → 211-9c9090c59e2c5907.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +36 -0
  31. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +286 -0
  32. package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +94 -0
  45. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/3522-1de1319d028ac89c.js +25 -0
  49. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +74 -0
  54. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +34 -0
  59. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +115 -0
  61. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/4588.ad3364cb7fb97ada.js +86 -0
  63. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +77 -0
  66. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +90 -0
  70. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +8 -0
  75. package/apps/pwa/out/_next/static/chunks/{4847.56f5bc454fbcc827.js → 6110.1297d4093fdfbb0d.js} +2 -2
  76. package/apps/pwa/out/_next/static/chunks/6158.566c890adc06a459.js +2 -0
  77. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +64 -0
  78. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +287 -0
  80. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +226 -0
  81. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6767.8e6c5f6e744fcd9f.js +38 -0
  83. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7175-350f4ce2873bc038.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +41 -0
  91. package/apps/pwa/out/_next/static/chunks/{1470.98a6ab61ccb4131c.js → 726.83532d69452ee24b.js} +1 -1
  92. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 7327-2d69fb321ba73389.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +24 -0
  95. package/apps/pwa/out/_next/static/chunks/7630.2b9a68659053ff4e.js +193 -0
  96. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/7899.597af4a9996c907f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/79b78388.663188133449d707.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8194.484f51e273d9abd2.js +217 -0
  105. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +104 -0
  109. package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/88729267.9c6c000157c83f2f.js +62 -0
  111. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +93 -0
  113. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{7647.2fcd30d894d5ee30.js → 935.4ddaa99fc1d1126c.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -0
  118. package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +62 -0
  120. package/apps/pwa/out/_next/static/chunks/9832.2ab2e818013574c7.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/a9f2b54d-40c16bd0efdb04b8.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d781be3e3a9cfd74.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-b0a80997903852d8.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-2b1263165630d9c8.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-e2d6bed48ac20120.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-111cabe4d1fd69ae.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-544dd4bd4847eeb7.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-3ee191319bf97d87.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-a449f1ed472b81c2.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-9045094d6d3b5cd0.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-c06673596849ef45.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-9f81233f29dcd409.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/{page-b70cd005d9169e25.js → page-be8b4615c919e36c.js} +1 -1
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-7a12f61be4a579d1.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-c85b0589591a99c9.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-725a4da02dd129eb.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-21bec4afeb2f0917.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-dc39ee7a36d26385.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-84ae759f76053672.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-3db58ddb61fe2be9.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c5315e30f3528dc4.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-2fe368efb6de3b6a.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-c69762987b751174.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-7f8d7fcdf058a845.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-22163556cd38bc88.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/d70879db.c66c56a95a91efc6.js +66 -0
  174. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6231284-45726550aba08fca.js} +1 -1
  175. package/apps/pwa/out/_next/static/chunks/{2ea96958.e8c7e112de50b067.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/f8626309.a4e5d362b1d55e59.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +1 -0
  183. package/apps/pwa/out/_next/static/css/{d3427996da11e480.css → b9d8151e48998417.css} +1 -1
  184. package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +24 -24
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +23 -23
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -23
  222. package/apps/pwa/out/live-run/index.html +1 -0
  223. package/apps/pwa/out/live-run/index.txt +35 -0
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/graphs/demo/editor-recipes.yaml +22 -0
  279. package/graphs/drafts/recipes.yaml +4 -4
  280. package/graphs/implement-loop.yaml +277 -5
  281. package/graphs/retro/daily.yaml +25 -0
  282. package/graphs/retro/recipes.yaml +24 -0
  283. package/package.json +1 -1
  284. package/scripts/ask-marker-check.ts +3 -1
  285. package/scripts/coord-channel-watch.sh +31 -4
  286. package/scripts/docs-lint.ts +4 -1
  287. package/scripts/land-global-checks.ts +89 -0
  288. package/scripts/lib/pch-regress-map.ts +2 -1
  289. package/scripts/marketing/generate-ax-brief.ts +18 -1
  290. package/scripts/measure-nl-routing-corpus.ts +11 -3
  291. package/scripts/release-loop/gate-node.ts +296 -25
  292. package/scripts/release-loop/gate-timing.ts +116 -7
  293. package/scripts/release-loop/preflight.ts +6 -4
  294. package/scripts/release-loop/publish-window.ts +141 -0
  295. package/scripts/release-loop/release-docs.ts +1 -4
  296. package/scripts/release-loop/release-universe.ts +38 -0
  297. package/scripts/release-loop/resume-release.ts +4 -1
  298. package/scripts/release-loop/shard-plan.ts +50 -5
  299. package/scripts/release-loop/unattended-release.ts +10 -2
  300. package/scripts/release-story/draft.ts +5 -1
  301. package/scripts/retro/shadow-compare.ts +71 -0
  302. package/scripts/rhythm/daily.ts +8 -2
  303. package/src/agent-mission/driver.ts +34 -6
  304. package/src/autopilot/build/llm-conflict-merge.ts +132 -7
  305. package/src/autopilot/build/merge-intent.ts +135 -7
  306. package/src/away/release-watch.ts +20 -3
  307. package/src/browser-cdp/client.ts +10 -5
  308. package/src/cli/config-drift.ts +513 -0
  309. package/src/cli/log-event-names.ts +4 -0
  310. package/src/cli/ops-cli.ts +11 -0
  311. package/src/cli/ops-overview-cli.ts +50 -0
  312. package/src/cli/pod-cli.ts +1 -0
  313. package/src/cli/pr-cli.ts +20 -0
  314. package/src/cli/release-cli.ts +20 -5
  315. package/src/cli/retro-cli.ts +78 -0
  316. package/src/cli/tasks-cli.ts +200 -8
  317. package/src/dashboard/index.ts +40 -4
  318. package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -4
  319. package/src/dashboard/slash-runtime/help-from-registry.ts +57 -31
  320. package/src/domains/outbound-log-probe.ts +6 -1
  321. package/src/esc-abort-gate.ts +3 -1
  322. package/src/graph-runner/graph-cli.ts +57 -0
  323. package/src/graph-runner/graph-run-api.ts +218 -0
  324. package/src/graph-runner/runner.ts +11 -1
  325. package/src/graph-wizard/archetypes.ts +88 -0
  326. package/src/graph-wizard/generate.ts +545 -0
  327. package/src/graph-wizard/saved-steps.ts +120 -0
  328. package/src/graph-wizard/steps.ts +193 -0
  329. package/src/harness/harness-cli-command.ts +482 -59
  330. package/src/harness/harness-queue.ts +315 -25
  331. package/src/harness/harness-salvage-cli.ts +62 -0
  332. package/src/hq/fence-outcomes.ts +63 -0
  333. package/src/hq/fence-wrapper.ts +33 -0
  334. package/src/hq/hq.ts +11 -1
  335. package/src/index.ts +76 -10
  336. package/src/intake-plane/digest.ts +18 -11
  337. package/src/maturity/feature-maturity.ts +4 -3
  338. package/src/nexus/api/draft-metrics.ts +114 -0
  339. package/src/nexus/api/graph-kinds.ts +10 -4
  340. package/src/nexus/api/graph-wizard-api.ts +53 -0
  341. package/src/nexus/api/graphs-api.ts +220 -10
  342. package/src/nexus/api/harness-traversal.ts +205 -0
  343. package/src/nexus/api/http-server.ts +69 -3
  344. package/src/nexus/api/loop-edges.ts +4 -2
  345. package/src/nexus/api/ops-api.ts +22 -7
  346. package/src/nexus/api/ops-board-route.ts +48 -0
  347. package/src/nexus/outbound/router.ts +28 -8
  348. package/src/onboarding/entry-hints.ts +5 -0
  349. package/src/onboarding/first-run.ts +3 -0
  350. package/src/onboarding.ts +24 -6
  351. package/src/ops/overview-collect.ts +345 -0
  352. package/src/ops/overview-seat-era.ts +90 -0
  353. package/src/ops/overview.ts +223 -0
  354. package/src/release-loop/feature-store.ts +14 -11
  355. package/src/release-loop/gate-shards.ts +163 -0
  356. package/src/release-loop/release-schedule.ts +6 -2
  357. package/src/retro/daily.ts +372 -0
  358. package/src/seat-loop/op-decide.ts +173 -0
  359. package/src/seat-loop/seat-loop.ts +60 -3
  360. package/src/seat-loop/seat-report.ts +58 -3
  361. package/src/self-dev/ask-launch-flow.ts +9 -6
  362. package/src/self-dev/ask-launch-io.ts +52 -2
  363. package/src/self-dev/ask-path-candidates.ts +40 -2
  364. package/src/self-dev/draft-sweep.ts +371 -16
  365. package/src/self-dev/draft-triage-rules.ts +19 -4
  366. package/src/self-dev/graph-journey-nodes.ts +157 -0
  367. package/src/self-dev/graph-journey-route.ts +105 -0
  368. package/src/self-dev/launch-preflight.ts +43 -3
  369. package/src/self-dev/pr-preview.ts +84 -16
  370. package/src/self-dev/release-path-guard.ts +7 -4
  371. package/src/self-implement/blocked-draft-policy.ts +101 -0
  372. package/src/self-implement/execution-clarification.ts +11 -0
  373. package/src/self-implement/frozen-merges.ts +23 -0
  374. package/src/self-implement/gate-env-retry.ts +83 -0
  375. package/src/self-implement/goal-author.ts +14 -0
  376. package/src/self-implement/heal-intake.ts +3 -0
  377. package/src/self-implement/orchestrator.ts +234 -6
  378. package/src/self-implement/run-stop.ts +123 -0
  379. package/src/self-implement/run-ttl.ts +258 -0
  380. package/src/self-implement/salvage-retention.ts +123 -0
  381. package/src/self-implement/seams.ts +7 -0
  382. package/src/self-implement/self-implement-cli.ts +2 -0
  383. package/src/self-implement/self-implement-runtime.ts +12 -0
  384. package/src/self-implement/sibling-resync.ts +223 -0
  385. package/src/self-implement/stop-autoheal.ts +204 -0
  386. package/src/session-runtime/index.ts +5 -3
  387. package/src/shell-env-bootstrap.ts +17 -3
  388. package/src/task-agent/board.ts +1 -1
  389. package/src/task-agent/card-evidence.ts +105 -10
  390. package/src/task-agent/judge.ts +7 -0
  391. package/src/task-agent/live-moves.ts +120 -20
  392. package/src/task-agent/mission.ts +1 -1
  393. package/src/task-agent/parent-host.ts +315 -0
  394. package/src/task-agent/shadow.ts +10 -1
  395. package/src/task-agent/task-hand.ts +16 -4
  396. package/src/task-orchestrator/surfaces/pod-command-job.ts +60 -11
  397. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +4 -0
  398. package/src/task-orchestrator/surfaces/pod-gate-deps.ts +123 -0
  399. package/src/task-orchestrator/surfaces/pod-install-slots.ts +89 -0
  400. package/src/task-orchestrator/surfaces/pod-lease.ts +93 -10
  401. package/src/task-orchestrator/surfaces/pod-pool.ts +147 -34
  402. package/src/task-orchestrator/surfaces/self-implement-pod.ts +58 -9
  403. package/src/telegram-commands.ts +41 -0
  404. package/src/testing/real-cli-spawn.ts +106 -0
  405. package/src/tool-runtime/browser-runtime.ts +87 -3
  406. package/src/tool-runtime/index.ts +1 -0
  407. package/src/user-config.ts +76 -7
  408. package/src/version/packed-revision.json +1 -1
  409. package/apps/pwa/out/_next/static/J5H7BvqgSI8DUjtRRcA_S/_buildManifest.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/2375-e061bb24cd2b5cde.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +0 -2
  419. package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  422. package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +0 -25
  447. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +0 -1
  507. package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +0 -1
  508. /package/apps/pwa/out/_next/static/{J5H7BvqgSI8DUjtRRcA_S → MObU8ujkMciAKr6ljAV05}/_ssgManifest.js +0 -0
  509. /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
@@ -0,0 +1,545 @@
1
+ /**
2
+ * GRAPH-WIZARD — 말 한 줄(또는 대화 한 턴)에서 그래프 YAML 을 짓는다.
3
+ *
4
+ * 목표: «플러그인 마법사처럼 그래프도 말로 말하면 컨셉으로 생겨나게 · 채팅으로 쭉 치면 노드와 간선이 쭉 만들어진다».
5
+ *
6
+ * 흐름: ① 가장 가까운 기존 템플릿(graphs/**)을 고른다(키워드 점수 — LLM 이 목록에서 다시 고를 수 있다)
7
+ * ② LLM 이 «알려진 노드 종류·스키마»만으로 YAML 을 쓴다(편집이면 currentYaml 을 고쳐 «전체»를 돌려준다)
8
+ * ③ API 와 «같은» 검증기(validateGraphYaml)로 재고, 실패면 문제를 되먹여 최대 3회.
9
+ * ⛔ 저장하지 않는다 — 저장은 편집기의 기존 POST /v1/graphs 몫이다.
10
+ */
11
+ import { createHash } from 'node:crypto';
12
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import { parse as parseYaml } from 'yaml';
15
+ import { debug } from '../debug/log.js';
16
+ import { listNodeKinds, type GraphKind } from '../graph-kinds/registry.js';
17
+ import { validateGraphYaml } from '../nexus/api/graph-kinds.js';
18
+ import { defaultGraphsDir } from '../self-implement/graph-templates.js';
19
+ import { WORKFLOW_SYNTH_SYSTEM_PROMPT } from '../workflow-synth/system-prompt.js';
20
+ import { WIZARD_ARCHETYPES } from './archetypes.js';
21
+ import { WIZARD_MAX_RETRIES, WIZARD_STEPS, WIZARD_STEP_IDS, recipesYamlFor, type WizardNodeStep } from './steps.js';
22
+
23
+ export interface GraphWizardTurn { role: 'user' | 'assistant'; text: string }
24
+
25
+ export interface GraphWizardRequest {
26
+ prompt: string;
27
+ kind?: GraphKind;
28
+ /** 있으면 prompt 는 «이 그래프를 고치라»는 지시다 — 결과는 고친 «전체» YAML. */
29
+ currentYaml?: string;
30
+ history?: GraphWizardTurn[];
31
+ }
32
+
33
+ export interface GraphWizardResult {
34
+ ok: boolean;
35
+ id: string;
36
+ yaml: string;
37
+ base?: string;
38
+ /** 왜 그 기반을 골랐나 — 키워드 · LLM 재선택. */
39
+ baseReason?: string;
40
+ issues: string[];
41
+ attempts: number;
42
+ summary: string;
43
+ /** 노드 id → 한국어 이름 (YAML 노드 줄 주석에서 읽는다). */
44
+ labels?: Record<string, string>;
45
+ /** 노드 id → 서재 단계(·인자). 하니스만. */
46
+ steps?: Record<string, WizardNodeStep>;
47
+ /** 이 그래프 옆에 둘 recipes.yaml — `elanous graph run` 이 cmd/approval 을 여기서 찾는다. 하니스만. */
48
+ recipes?: string;
49
+ /** 실행 가능성 증명: 그 recipes 로 `graph run --dry-run` 을 걸어 본 결과. 하니스만. */
50
+ dryRun?: { status: string; path: string[] };
51
+ }
52
+
53
+ export interface GraphWizardDeps {
54
+ /** LLM 한 번 부르기. 기본 = 역할 `graph-grow` 해석 ⊕ 설정 폴백 체인(streamLLM). */
55
+ callLLM?: (prompt: string) => Promise<string>;
56
+ /** 템플릿 뿌리(기본 graphs/). */
57
+ graphsDir?: string;
58
+ /** 이미 쓰인 graph_id(핵심 ⊕ 내 그래프). 기본 = graphs/** ⊕ <state>/graphs. */
59
+ existingIds?: () => ReadonlySet<string>;
60
+ maxAttempts?: number;
61
+ }
62
+
63
+ export const GRAPH_WIZARD_MAX_ATTEMPTS = 3;
64
+
65
+ /** 요청 자체가 틀렸다(400) — LLM 실패(502)와 가른다. */
66
+ export class GraphWizardInputError extends Error { override name = 'GraphWizardInputError'; }
67
+ const GRAPH_ID = /^[a-z0-9-]+$/;
68
+
69
+ interface TemplateEntry { id: string; description: string; text: string; runnable: boolean; keywords?: string[] }
70
+
71
+ function walkYaml(dir: string, depth = 0): string[] {
72
+ if (depth > 3 || !existsSync(dir)) return [];
73
+ let names: string[];
74
+ try { names = readdirSync(dir); } catch { return []; }
75
+ return names.flatMap((name) => {
76
+ if (name.startsWith('.') || name === 'overlays' || name === 'catalog') return [];
77
+ const path = join(dir, name);
78
+ let dirLike = false;
79
+ try { dirLike = statSync(path).isDirectory(); } catch { return []; }
80
+ if (dirLike) return walkYaml(path, depth + 1);
81
+ return /\.ya?ml$/.test(name) && !/^recipes\.ya?ml$/.test(name) ? [path] : [];
82
+ });
83
+ }
84
+
85
+ function graphIdOf(text: string): string | undefined {
86
+ try {
87
+ const raw = parseYaml(text) as { graph_id?: unknown; name?: unknown } | null;
88
+ if (raw && typeof raw === 'object') {
89
+ if (typeof raw.graph_id === 'string') return raw.graph_id;
90
+ if (typeof raw.name === 'string') return raw.name;
91
+ }
92
+ } catch { /* not a graph */ }
93
+ return undefined;
94
+ }
95
+
96
+ /** 검증을 통과하는 하니스 템플릿만 «기반» 후보가 된다 — 깨진 표본을 보여주면 깨진 것을 배운다. */
97
+ export function listWizardTemplates(dir = defaultGraphsDir()): TemplateEntry[] {
98
+ // 원형이 맨 앞 — 사람이 말로 시키는 일의 뼈대(운영 템플릿은 개발·출시 루프라 말과 낱말이 안 겹친다).
99
+ const out: TemplateEntry[] = WIZARD_ARCHETYPES.map((a) => ({ id: a.id, description: a.description, text: a.text, runnable: true, keywords: a.keywords }));
100
+ const seen = new Set<string>(out.map((t) => t.id));
101
+ for (const file of walkYaml(dir)) {
102
+ let text: string;
103
+ try { text = readFileSync(file, 'utf8'); } catch { continue; }
104
+ let raw: Record<string, unknown> | null;
105
+ try { raw = parseYaml(text) as Record<string, unknown> | null; } catch { continue; }
106
+ if (!raw || typeof raw.graph_id !== 'string' || seen.has(raw.graph_id)) continue;
107
+ if (!validateGraphYaml('harness', text).ok) continue;
108
+ const loop = (raw.loop ?? {}) as { title?: unknown; description?: unknown };
109
+ const description = [loop.title, loop.description].filter((v): v is string => typeof v === 'string').join(' — ') || raw.graph_id;
110
+ const terminals = Array.isArray(raw.terminal_nodes) ? raw.terminal_nodes : [];
111
+ seen.add(raw.graph_id);
112
+ out.push({ id: raw.graph_id, description: description.slice(0, 160), text, runnable: terminals.every((t) => t === 'done' || t === 'failed') });
113
+ }
114
+ return out;
115
+ }
116
+
117
+ function grams(text: string): Set<string> {
118
+ const lower = text.toLowerCase();
119
+ const out = new Set<string>();
120
+ for (const word of lower.split(/[^\p{L}\p{N}]+/u).filter(Boolean)) {
121
+ if (/^[a-z0-9]+$/.test(word)) { if (word.length > 2) out.add(word); continue; }
122
+ for (let i = 0; i + 1 < word.length; i += 1) out.add(word.slice(i, i + 2));
123
+ }
124
+ return out;
125
+ }
126
+
127
+ export interface BasePick { template: TemplateEntry; reason: string }
128
+
129
+ /**
130
+ * 원형 키워드(낱말이 말 안에 있으면 3점) ⊕ 설명 한글 2-gram 겹침(1점)으로 가장 가까운 «실행 가능한» 템플릿.
131
+ * 동점이면 원형이 앞선다(목록 순서). 0점이면 «모아서 요약해 보내기» 원형. 이유를 함께 낸다.
132
+ */
133
+ export function pickBaseTemplate(prompt: string, templates: readonly TemplateEntry[]): BasePick | undefined {
134
+ const lower = prompt.toLowerCase();
135
+ const wanted = grams(prompt);
136
+ let best: BasePick | undefined;
137
+ let bestScore = 0;
138
+ for (const template of templates) {
139
+ if (!template.runnable) continue;
140
+ const hits = (template.keywords ?? []).filter((k) => lower.includes(k));
141
+ const have = grams(`${template.id} ${template.description}`);
142
+ let overlap = 0;
143
+ for (const g of wanted) if (have.has(g)) overlap += 1;
144
+ const score = hits.length * 3 + overlap;
145
+ if (score > bestScore) {
146
+ bestScore = score;
147
+ best = { template, reason: hits.length ? `키워드 ${hits.slice(0, 5).join('·')}` : `설명 낱말 겹침 ${overlap}` };
148
+ }
149
+ }
150
+ if (best) return best;
151
+ const fallback = templates.find((t) => t.id === WIZARD_ARCHETYPES[0]!.id) ?? templates.find((t) => t.runnable);
152
+ return fallback ? { template: fallback, reason: '겹치는 낱말이 없어 기본 원형' } : undefined;
153
+ }
154
+
155
+ export function existingGraphIds(dirs: readonly string[]): Set<string> {
156
+ const ids = new Set<string>();
157
+ for (const dir of dirs) {
158
+ for (const file of walkYaml(dir)) {
159
+ try { const id = graphIdOf(readFileSync(file, 'utf8')); if (id) ids.add(id); } catch { /* unreadable */ }
160
+ }
161
+ }
162
+ return ids;
163
+ }
164
+
165
+ /** 슬러그 ⊕ 짧은 해시. 기존 id 와 겹치면 접미로 비켜 간다 — 덮어쓰지 않는다. */
166
+ export function wizardGraphId(prompt: string, hint: string | undefined, taken: ReadonlySet<string>): string {
167
+ const slugify = (s: string) => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').split('-').filter(Boolean).slice(0, 5).join('-').slice(0, 40).replace(/-+$/, '');
168
+ const stem = slugify(hint ?? '') || slugify(prompt) || 'graph';
169
+ const hash = createHash('sha256').update(prompt).digest('hex').slice(0, 6);
170
+ let id = `${stem}-${hash}`;
171
+ for (let n = 2; taken.has(id); n += 1) id = `${stem}-${hash}-${n}`;
172
+ return id;
173
+ }
174
+
175
+ export const HARNESS_EXAMPLE = `graph_id: example-digest
176
+ loop:
177
+ title: 예시 — 뉴스를 모아 요약하고 승인 뒤 보낸다
178
+ description: 뉴스를 검색해 요약하고 품질을 본 뒤 사람 승인을 받아 텔레그램으로 보낸다
179
+ trigger:
180
+ cron: "0 8 * * *"
181
+ version: 1
182
+ entry_node: collect
183
+ terminal_nodes: [done, failed]
184
+ nodes:
185
+ - { node_id: collect, kind: agent, recipe: 'cmd:collect', max_visits: 1 } # 뉴스 수집 | web-search | 오늘 AI 뉴스
186
+ - { node_id: summarize, kind: agent, recipe: 'cmd:summarize', max_visits: 2 } # 요약 | summarize | 핵심 5줄 한국어 요약
187
+ - { node_id: check, kind: judge, recipe: 'cmd:check', max_visits: 2 } # 요약 품질 판정 | check | 사실에 맞고 5줄 이내
188
+ - { node_id: approve, kind: hitl, recipe: 'approval:approve', max_visits: 1 } # 발송 승인 | approval
189
+ - { node_id: send, kind: agent, recipe: 'cmd:send', max_visits: 1 } # 텔레그램 발송 | telegram-send
190
+ - { node_id: done, kind: gate, recipe: none, max_visits: 1 } # 완료
191
+ - { node_id: failed, kind: gate, recipe: none, max_visits: 1 } # 실패
192
+ edges:
193
+ - { from: collect, on: outcome, map: { ok: summarize, fail: failed } }
194
+ - { from: summarize, on: outcome, map: { ok: check, fail: failed } }
195
+ - { from: check, on: outcome, map: { ok: approve, rework: summarize, fail: failed } }
196
+ - { from: approve, on: outcome, map: { ok: send, fail: failed } }
197
+ - { from: send, on: outcome, map: { ok: done, fail: failed } }
198
+ `;
199
+
200
+ export const WORKFLOW_EXAMPLE = `name: example-digest
201
+ description: 자료를 모아 요약하고 승인 뒤 보낸다
202
+ nodes:
203
+ - id: collect
204
+ bash: "echo collect"
205
+ - id: summarize
206
+ prompt: "다음을 다섯 줄로 요약하라: $collect.output"
207
+ depends_on: [collect]
208
+ - id: approve
209
+ approval:
210
+ message: "요약을 보낼까요?"
211
+ depends_on: [summarize]
212
+ - id: send
213
+ bash: "echo send"
214
+ depends_on: [approve]
215
+ `;
216
+
217
+ function harnessRules(kinds: string): string {
218
+ return [
219
+ '스키마(하니스 실행 그래프 · YAML):',
220
+ '- 최상위: graph_id, loop{title, description, trigger{cron|events}}, version: 1, entry_node, terminal_nodes: [done, failed], nodes, edges.',
221
+ `- nodes[]: { node_id(kebab-case 영문), kind, recipe, max_visits(1 이상 정수) }. kind 는 이것만: ${kinds}.`,
222
+ "- recipe 는 셋 중 하나뿐: 'none'(done·failed 같은 끝 노드), 'cmd:<kebab-id>'(실행 단계), 'approval:<kebab-id>'(사람 승인 — kind: hitl).",
223
+ '- 끝 노드는 정확히 done 과 failed 두 개(kind: gate, recipe: none). 다른 끝 노드를 만들지 마라.',
224
+ '- edges[]: { from, on: outcome, map: { ok: <다음>, fail: failed, <다른 결과>: <노드> } } — 모든 노드가 entry 에서 닿아야 하고, done·failed 외 노드는 나가는 간선이 있어야 한다.',
225
+ '- 되돌아가는 고리(재작업)는 map 의 다른 결과로 앞 노드를 가리키고, 그 노드의 max_visits 를 2~3 으로.',
226
+ '- 다른 키(contract, progress 등)는 쓰지 마라.',
227
+ '',
228
+ '노드 줄 형식(필수): 노드는 한 줄 흐름식 `- { node_id: …, kind: …, recipe: …, max_visits: … }` 으로 쓰고, 줄 끝 주석에 `# <한국어 이름> | <단계> | <인자>` 를 단다.',
229
+ '- 한국어 이름은 사용자가 화면에서 보는 노드 이름이다(예: 뉴스 수집). 끝 노드는 `# 완료` · `# 실패`. 승인 노드는 `# <이름> | approval`.',
230
+ "- cmd: 노드의 <단계> 는 아래 서재 중 하나다. 맞는 것이 없을 때만 custom(외부 연동 · 실행 시 «미구현»으로 실패). 인자에 `|`·`#` 을 쓰지 마라.",
231
+ ...WIZARD_STEPS.map((step) => ` · ${step.id} — ${step.use} (결과: ${step.outcomes.join('/')})`),
232
+ '- 간선 map 키는 그 단계가 내는 결과를 쓴다(예: check → ok/rework, retry-gate → retry/give-up, gh-pr-review → ok/must-fix).',
233
+ '- «실패하면 다시 시도하고 N번 넘으면 알려줘» 패턴(간선을 늘리지 않는다): 재시도는 «단계 안»에서 한다 — 각 cmd 노드 주석 끝에 `| retry=N` 을 붙인다(인자가 없으면 `# 이름 | 단계 | | retry=N`). 재시도 노드·되돌이 간선을 만들지 마라. 알림 노드(notify-me) «하나»만 더하고, 실행 노드들의 `fail: failed` 를 `fail: <알림 노드>` 로 바꾸고, 알림 노드는 `{ ok: failed, fail: failed }`.',
234
+ ].join('\n');
235
+ }
236
+
237
+ /** 워크플로 어휘는 `elanous wf synth` 의 시스템 프롬프트를 «그대로» 재사용한다(노드 변형·본문 모양·예시). 출력 형식만 이 마법사 것을 쓴다. */
238
+ function workflowRules(kinds: string): string {
239
+ return [
240
+ '--- wf synth 어휘(재사용 · 그 안의 «# Output» 절은 무시하고 아래 출력 형식을 따른다) ---',
241
+ WORKFLOW_SYNTH_SYSTEM_PROMPT.trim(),
242
+ '--- 끝 ---',
243
+ '스키마(워크플로 · YAML):',
244
+ '- 최상위: name(kebab-case), description, nodes.',
245
+ `- nodes[]: { id, <종류 키 하나>, depends_on?: [id…] }. 종류: ${kinds}. 흔한 것 = prompt: "<지시>", bash: "<명령>", approval: { message }.`,
246
+ '- 순환 금지 · depends_on 은 선언된 id 만.',
247
+ ].join('\n');
248
+ }
249
+
250
+ function extractYaml(text: string): string {
251
+ const fence = /```(?:ya?ml)?\s*\n([\s\S]*?)```/i.exec(text);
252
+ const body = fence ? fence[1]! : text.split('\n').filter((line) => !/^\s*(BASE|SLUG|SUMMARY)\s*:/.test(line)).join('\n');
253
+ return body.trim() + '\n';
254
+ }
255
+
256
+ function lineValue(text: string, key: string): string | undefined {
257
+ const match = new RegExp(`^\\s*${key}\\s*:\\s*(.+)$`, 'mi').exec(text.split('```')[0] ?? '');
258
+ return match?.[1]?.trim().replace(/^['"`]|['"`]$/g, '') || undefined;
259
+ }
260
+
261
+ /** 선언된 id 줄만 바꾼다 — 나머지 바이트(주석 포함)는 그대로. */
262
+ function forceId(yaml: string, kind: GraphKind, id: string): string {
263
+ const key = kind === 'harness' ? 'graph_id' : 'name';
264
+ const re = new RegExp(`^([ \\t]*${key}:[ \\t]*)([^\\n#]*?)([ \\t]*(?:#.*)?)$`, 'm');
265
+ return re.test(yaml) ? yaml.replace(re, `$1${id}$3`) : `${key}: ${id}\n${yaml}`;
266
+ }
267
+
268
+ function fmtIssue(issue: unknown): string {
269
+ if (typeof issue === 'string') return issue;
270
+ if (issue && typeof issue === 'object') {
271
+ const { path, message } = issue as { path?: unknown; message?: unknown };
272
+ if (typeof message === 'string') return typeof path === 'string' && path ? `${path}: ${message}` : message;
273
+ }
274
+ return JSON.stringify(issue);
275
+ }
276
+
277
+ /** API 검증기 ⊕ (하니스) `elanous graph run` 이 실제로 받는 모양 — 통과해도 못 도는 그래프를 «ok» 라 하지 않는다. */
278
+ export function validateWizardYaml(kind: GraphKind, yaml: string): string[] {
279
+ const checked = validateGraphYaml(kind, yaml);
280
+ const issues = checked.errors.map(fmtIssue);
281
+ if (kind !== 'harness' || issues.length > 0) return issues;
282
+ const raw = parseYaml(yaml) as { terminal_nodes?: unknown; nodes?: Array<{ node_id?: unknown; kind?: unknown; recipe?: unknown }> };
283
+ const terminals = Array.isArray(raw.terminal_nodes) ? raw.terminal_nodes : [];
284
+ for (const t of terminals) if (t !== 'done' && t !== 'failed') issues.push(`terminal_nodes: '${String(t)}' — 끝 노드는 done·failed 만 쓴다`);
285
+ for (const node of raw.nodes ?? []) {
286
+ const recipe = typeof node.recipe === 'string' ? node.recipe : '';
287
+ if (recipe !== 'none' && !/^(cmd|approval):[a-z0-9][a-z0-9-]*$/.test(recipe)) {
288
+ issues.push(`nodes/${String(node.node_id)}/recipe: '${recipe}' — none · cmd:<id> · approval:<id> 중 하나여야 한다`);
289
+ }
290
+ }
291
+ return issues;
292
+ }
293
+
294
+ /** 노드 줄 끝 주석 `# 이름 | 단계 | 인자` 를 읽는다. 흐름식 노드 줄에서만. */
295
+ export function parseNodeAnnotations(yaml: string): Record<string, WizardNodeStep> {
296
+ const out: Record<string, WizardNodeStep> = {};
297
+ for (const line of yaml.split('\n')) {
298
+ const match = /node_id:\s*['"]?([a-z0-9][a-z0-9-]*)['"]?[^#]*\}\s*#\s*(.+)$/.exec(line);
299
+ if (!match) continue;
300
+ const parts = match[2]!.split('|').map((part) => part.trim());
301
+ let retries: number | undefined;
302
+ const retryAt = parts.findIndex((part, index) => index >= 2 && /^retry\s*=\s*\d+$/i.test(part));
303
+ if (retryAt >= 0) retries = Number.parseInt(parts.splice(retryAt, 1)[0]!.split('=')[1]!, 10);
304
+ const [label, step, ...rest] = parts;
305
+ const arg = rest.join('|').trim();
306
+ out[match[1]!] = { ...(label ? { label } : {}), ...(step ? { step } : {}), ...(arg ? { arg } : {}), ...(retries ? { retries } : {}) };
307
+ }
308
+ return out;
309
+ }
310
+
311
+ /** cmd 노드마다 서재 단계가 있는지 — 없으면 «이름만 있는» 노드라 실제로 못 돈다. */
312
+ export function stepIssues(yaml: string, steps: Record<string, WizardNodeStep>): string[] {
313
+ const raw = parseYaml(yaml) as {
314
+ nodes?: Array<{ node_id?: unknown; kind?: unknown; recipe?: unknown }>;
315
+ edges?: Array<{ from?: unknown; to?: unknown; map?: Record<string, unknown> }>;
316
+ };
317
+ const issues: string[] = [];
318
+ const recipeOwner = new Map<string, string>();
319
+ const kindOf = new Map((raw.nodes ?? []).map((n) => [String(n.node_id), String(n.kind)]));
320
+ for (const node of raw.nodes ?? []) {
321
+ const id = String(node.node_id);
322
+ const recipe = typeof node.recipe === 'string' ? node.recipe : '';
323
+ const meta = steps[id];
324
+ // recipes.yaml 은 recipe 이름으로 한 줄 — 두 노드가 같은 이름을 쓰면 둘째 노드의 단계·인자가 사라진다.
325
+ if (recipe !== 'none' && recipeOwner.has(recipe)) issues.push(`nodes/${id}: recipe '${recipe}' 를 ${recipeOwner.get(recipe)} 도 쓴다 — 노드마다 고유한 recipe 이름(보통 cmd:<node_id>)`);
326
+ else recipeOwner.set(recipe, id);
327
+ if ((meta?.retries ?? 0) > WIZARD_MAX_RETRIES) issues.push(`nodes/${id}: retry=${meta!.retries} — 최대 ${WIZARD_MAX_RETRIES}`);
328
+ // 머지는 사람 승인 «뒤»에만 — 들어오는 간선이 전부 hitl 노드에서 와야 한다.
329
+ if (meta?.step === 'gh-pr-merge') {
330
+ const sources = (raw.edges ?? []).filter((e) => e.to === id || Object.values(e.map ?? {}).includes(id)).map((e) => String(e.from));
331
+ if (sources.length === 0 || sources.some((from) => kindOf.get(from) !== 'hitl')) issues.push(`nodes/${id}: PR 머지 앞에는 사람 승인(kind: hitl) 노드만 올 수 있다`);
332
+ }
333
+ if (!meta?.label) issues.push(`nodes/${id}: 줄 끝 주석 \`# <한국어 이름> | …\` 이 없다`);
334
+ if (recipe.startsWith('cmd:') && !(meta?.step && WIZARD_STEP_IDS.has(meta.step))) {
335
+ issues.push(`nodes/${id}: cmd 노드의 단계가 서재(${[...WIZARD_STEP_IDS].join(', ')})에 없다 — 주석 \`# 이름 | <단계> | <인자>\``);
336
+ }
337
+ }
338
+ return issues;
339
+ }
340
+
341
+ /** 실제 recipes 로 `graph run --dry-run` 을 걸어 «도는가»를 잰다(명령 실행 0). */
342
+ export async function dryRunWizardGraph(yaml: string, recipes: string): Promise<{ status: string; path: string[]; error?: string }> {
343
+ const { mkdtempSync, writeFileSync, rmSync } = await import('node:fs');
344
+ const { tmpdir } = await import('node:os');
345
+ const { runGraph } = await import('../graph-runner/runner.js');
346
+ const dir = mkdtempSync(join(tmpdir(), 'elanous-graph-wizard-'));
347
+ try {
348
+ writeFileSync(join(dir, 'graph.yaml'), yaml);
349
+ writeFileSync(join(dir, 'recipes.yaml'), recipes);
350
+ const state = await runGraph(join(dir, 'graph.yaml'), { dryRun: true, deps: { root: join(dir, 'state'), log: () => {} } });
351
+ return { status: state.status, path: state.path };
352
+ } catch (error) {
353
+ return { status: 'error', path: [], error: error instanceof Error ? error.message : String(error) };
354
+ } finally { rmSync(dir, { recursive: true, force: true }); }
355
+ }
356
+
357
+ interface Shape { nodes: string[]; labels: Record<string, string>; retries: Record<string, number>; edges: Map<string, string> }
358
+ function shapeOf(kind: GraphKind, yaml: string | undefined): Shape {
359
+ const empty: Shape = { nodes: [], labels: {}, retries: {}, edges: new Map() };
360
+ if (!yaml) return empty;
361
+ try {
362
+ const raw = parseYaml(yaml) as Record<string, unknown>;
363
+ const nodes = Array.isArray(raw.nodes) ? raw.nodes as Array<Record<string, unknown>> : [];
364
+ if (kind === 'workflow') {
365
+ const edges = new Map<string, string>();
366
+ for (const n of nodes) for (const d of Array.isArray(n.depends_on) ? n.depends_on : []) edges.set(`${String(d)}>${String(n.id)}`, String(n.id));
367
+ return { ...empty, nodes: nodes.map((n) => String(n.id)), edges };
368
+ }
369
+ // 간선 = (from, 결과) → to. 같은 칸의 목적지만 바뀌면 «재연결»이지 새 간선이 아니다.
370
+ const edges = new Map<string, string>();
371
+ for (const e of Array.isArray(raw.edges) ? raw.edges as Array<Record<string, unknown>> : []) {
372
+ if (typeof e.to === 'string') { edges.set(`${String(e.from)}:*`, e.to); continue; }
373
+ const map = e.map && typeof e.map === 'object' ? e.map as Record<string, unknown> : {};
374
+ for (const [outcome, to] of Object.entries(map)) edges.set(`${String(e.from)}:${outcome}`, String(to));
375
+ }
376
+ const notes = parseNodeAnnotations(yaml);
377
+ return {
378
+ nodes: nodes.map((n) => String(n.node_id)), edges,
379
+ labels: Object.fromEntries(Object.entries(notes).filter(([, v]) => v.label).map(([k, v]) => [k, v.label!])),
380
+ retries: Object.fromEntries(Object.entries(notes).filter(([, v]) => v.retries).map(([k, v]) => [k, v.retries!])),
381
+ };
382
+ } catch { return empty; }
383
+ }
384
+
385
+ /** 결정적 변경 요약 — LLM 의 한 줄 요약이 없거나 틀려도 «무엇이 바뀌었나»는 센 값으로 남는다. 노드 이름은 한국어 라벨. */
386
+ export function summarizeChange(kind: GraphKind, before: string | undefined, after: string): string {
387
+ const a = shapeOf(kind, before);
388
+ const b = shapeOf(kind, after);
389
+ const name = (id: string) => b.labels[id] ?? a.labels[id] ?? id;
390
+ if (!before) return `새 그래프: 노드 ${b.nodes.length}개 (${b.nodes.map(name).join(' → ')}) · 간선 ${b.edges.size}개`;
391
+ const added = b.nodes.filter((n) => !a.nodes.includes(n));
392
+ const removed = a.nodes.filter((n) => !b.nodes.includes(n));
393
+ let edgesAdded = 0;
394
+ let rewired = 0;
395
+ for (const [key, to] of b.edges) {
396
+ if (!a.edges.has(key)) edgesAdded += 1;
397
+ else if (a.edges.get(key) !== to) rewired += 1;
398
+ }
399
+ const edgesRemoved = [...a.edges.keys()].filter((key) => !b.edges.has(key)).length;
400
+ const retryChanged = b.nodes.filter((n) => (b.retries[n] ?? 0) !== (a.retries[n] ?? 0));
401
+ const parts: string[] = [];
402
+ if (added.length) parts.push(`노드 ${added.length}개 추가: ${added.map(name).join(' → ')}`);
403
+ if (removed.length) parts.push(`노드 ${removed.length}개 삭제: ${removed.map(name).join(', ')}`);
404
+ if (edgesAdded) parts.push(`간선 ${edgesAdded}개 추가`);
405
+ if (edgesRemoved) parts.push(`간선 ${edgesRemoved}개 삭제`);
406
+ if (rewired) parts.push(`간선 ${rewired}개 재연결`);
407
+ if (retryChanged.length) parts.push(`재시도 설정 ${retryChanged.length}개 노드(${retryChanged.map((n) => `${name(n)} ${b.retries[n] ?? 0}회`).join(', ')})`);
408
+ return parts.length ? parts.join(' · ') : '구조 변경 없음';
409
+ }
410
+
411
+ async function defaultCallLLM(prompt: string): Promise<string> {
412
+ const [{ PROVIDERS, streamLLM }, { getUserConfig }, { graphGrowthLlmOptions }] = await Promise.all([
413
+ import('../llm.js'), import('../user-config.js'), import('../graph-runner/graph-grow-llm.js'),
414
+ ]);
415
+ const selected = graphGrowthLlmOptions(getUserConfig());
416
+ return streamLLM([{ role: 'user', content: prompt }], () => {}, {
417
+ ...(selected.model ? { model: selected.model, provider: PROVIDERS[selected.provider!] } : {}),
418
+ reasoningEffort: 'low',
419
+ });
420
+ }
421
+
422
+ function buildPrompt(input: {
423
+ req: GraphWizardRequest; kind: GraphKind; id: string; base?: TemplateEntry; templates: readonly TemplateEntry[];
424
+ previous?: { yaml: string; issues: string[] };
425
+ }): string {
426
+ const { req, kind, id, base, templates, previous } = input;
427
+ const kinds = listNodeKinds(kind).map((k) => k.kind).join(', ');
428
+ const editing = typeof req.currentYaml === 'string' && req.currentYaml.trim() !== '';
429
+ const lines: string[] = [
430
+ '너는 엘라누스 «그래프 마법사»다. 사용자의 말을 실행 그래프 YAML 로 짓는다.',
431
+ kind === 'harness' ? harnessRules(kinds) : workflowRules(kinds),
432
+ `- ${kind === 'harness' ? 'graph_id' : 'name'} 는 정확히 "${id}" 로 쓴다.`,
433
+ '- 노드 id 는 사용자의 업무 단계를 드러내는 짧은 영문 kebab-case(예: collect-news, summarize, send-telegram). 단계 수는 3~10개.',
434
+ '- 사용자가 말한 것만 짓는다. 승인(hitl)·품질 판정(check)·재시도·알림은 사용자가 말했을 때만 넣는다 — 예외는 PR 머지·결제·공개 게시(외부 공개) 앞의 승인뿐이다. 텔레그램 발송·나에게 알림 앞에는 말하지 않은 승인을 넣지 마라.',
435
+ '',
436
+ '예시(사용자가 «품질을 보고, 내가 승인하면 보내»라고 «말했을 때»의 모양 — 말하지 않은 단계는 넣지 않는다):',
437
+ '```yaml', kind === 'harness' ? HARNESS_EXAMPLE.trim() : WORKFLOW_EXAMPLE.trim(), '```',
438
+ ];
439
+ if (kind === 'harness' && !editing) {
440
+ lines.push('', '기존 템플릿 목록(id — 설명):', ...templates.filter((t) => t.runnable).slice(0, 30).map((t) => `- ${t.id} — ${t.description}`));
441
+ if (base) lines.push('', `가장 가까운 템플릿(${base.id}) — 구조를 참고하라:`, '```yaml', base.text.trim(), '```');
442
+ }
443
+ if (req.history?.length) {
444
+ lines.push('', '지금까지의 대화:', ...req.history.slice(-10).map((t) => `${t.role === 'user' ? '사용자' : '마법사'}: ${t.text.slice(0, 500)}`));
445
+ }
446
+ if (editing) {
447
+ lines.push('', '지금 그래프(이것을 고친다 — 바뀌지 않는 노드의 id 는 그대로 둔다):', '```yaml', req.currentYaml!.trim(), '```',
448
+ '', `사용자의 수정 지시: ${req.prompt}`,
449
+ '최소 변경 규칙: 지시가 요구하는 노드·간선만 더하거나 바꾼다. 나머지 노드 줄(id·kind·recipe·주석)과 간선은 글자 그대로 둔다. id 를 바꾸지 마라. loop.title·description 은 바뀐 내용만 반영한다.',
450
+ '고친 «전체» YAML 을 돌려준다.');
451
+ } else {
452
+ lines.push('', `사용자의 말: ${req.prompt}`);
453
+ }
454
+ if (previous) {
455
+ lines.push('', '직전 시도가 검증에 실패했다. 아래 문제를 «전부» 고쳐 전체 YAML 을 다시 써라:', ...previous.issues.map((i) => `- ${i}`),
456
+ '직전 YAML:', '```yaml', previous.yaml.trim(), '```');
457
+ }
458
+ lines.push('', '출력 형식(이 순서 · 다른 설명 금지):',
459
+ ...(editing || kind !== 'harness' ? [] : ['BASE: <참고한 템플릿 id 또는 none>']),
460
+ 'SLUG: <이 그래프를 나타내는 영문 kebab-case 2~4 낱말>',
461
+ 'SUMMARY: <무엇을 만들었/바꿨는지 한국어 한 줄 — 예: 노드 2개 추가: 요약 → 텔레그램 발송 · 간선 2개>',
462
+ '```yaml', '<전체 YAML>', '```');
463
+ return lines.join('\n');
464
+ }
465
+
466
+ export async function generateGraphFromPrompt(req: GraphWizardRequest, deps: GraphWizardDeps = {}): Promise<GraphWizardResult> {
467
+ const kind: GraphKind = req.kind === 'workflow' ? 'workflow' : 'harness';
468
+ const prompt = typeof req.prompt === 'string' ? req.prompt.trim() : '';
469
+ if (!prompt) throw new GraphWizardInputError('prompt is required');
470
+ const editing = typeof req.currentYaml === 'string' && req.currentYaml.trim() !== '';
471
+ const graphsDir = deps.graphsDir ?? defaultGraphsDir();
472
+ const templates = kind === 'harness' ? listWizardTemplates(graphsDir) : [];
473
+ const pick = kind === 'harness' && !editing ? pickBaseTemplate(prompt, templates) : undefined;
474
+ const keywordBase = pick?.template;
475
+ let baseReason = pick?.reason;
476
+ const taken = deps.existingIds?.() ?? existingGraphIds([graphsDir, join((await import('../autopilot/state-paths.js')).elanousStateRoot(), 'graphs')]);
477
+ // 편집이면 지금 그래프의 id 를 지킨다 — 캔버스와 저장 경로가 그 id 를 쥐고 있다.
478
+ const currentId = editing ? graphIdOf(req.currentYaml!) : undefined;
479
+ // 편집인데 id 를 지킬 수 없으면(없음·형식 밖) 새 id 로 바꿔 «다른 그래프»를 돌려주지 않고 거절한다.
480
+ if (editing && !(currentId && GRAPH_ID.test(currentId))) {
481
+ debug.log('graph.wizard', 'rejected', { reason: 'current-id-invalid', kind });
482
+ throw new GraphWizardInputError(`currentYaml has no valid ${kind === 'harness' ? 'graph_id' : 'name'} ([a-z0-9-])`);
483
+ }
484
+ let id = editing ? currentId! : wizardGraphId(prompt, undefined, taken);
485
+ const callLLM = deps.callLLM ?? defaultCallLLM;
486
+ const maxAttempts = Math.max(1, deps.maxAttempts ?? GRAPH_WIZARD_MAX_ATTEMPTS);
487
+ let base = keywordBase?.id;
488
+ let yaml = '';
489
+ let issues: string[] = [];
490
+ let llmSummary: string | undefined;
491
+ let attempts = 0;
492
+ let steps: Record<string, WizardNodeStep> = {};
493
+ let recipes: string | undefined;
494
+ let dryRun: { status: string; path: string[] } | undefined;
495
+ const startedAt = Date.now();
496
+ debug.log('graph.wizard', 'start', { kind, editing, base, id, prompt: prompt.slice(0, 300), historyTurns: req.history?.length ?? 0 });
497
+ for (attempts = 1; attempts <= maxAttempts; attempts += 1) {
498
+ let text: string;
499
+ try {
500
+ text = await callLLM(buildPrompt({ req: { ...req, prompt }, kind, id, base: keywordBase, templates,
501
+ ...(attempts > 1 ? { previous: { yaml, issues } } : {}) }));
502
+ } catch (error) {
503
+ debug.log('graph.wizard', 'error', { id, kind, attempt: attempts, base, message: error instanceof Error ? error.message : String(error) });
504
+ throw error;
505
+ }
506
+ if (attempts === 1 && !editing) {
507
+ const picked = lineValue(text, 'BASE');
508
+ if (picked && picked !== base && templates.some((t) => t.id === picked)) { base = picked; baseReason = `LLM 재선택 (키워드 후보 ${keywordBase?.id ?? '-'})`; }
509
+ const slug = lineValue(text, 'SLUG');
510
+ if (slug && !currentId) id = wizardGraphId(prompt, slug, taken);
511
+ }
512
+ llmSummary = lineValue(text, 'SUMMARY') ?? llmSummary;
513
+ yaml = forceId(extractYaml(text), kind, id);
514
+ try { issues = validateWizardYaml(kind, yaml); }
515
+ catch (error) { issues = [`검증 실패: ${error instanceof Error ? error.message : String(error)}`]; }
516
+ steps = kind === 'harness' ? parseNodeAnnotations(yaml) : {};
517
+ recipes = undefined;
518
+ dryRun = undefined;
519
+ if (kind === 'harness' && issues.length === 0) {
520
+ issues = stepIssues(yaml, steps);
521
+ const nodes = ((parseYaml(yaml) as { nodes?: Array<{ node_id?: unknown; recipe?: unknown }> }).nodes ?? [])
522
+ .map((n) => ({ nodeId: String(n.node_id), recipe: typeof n.recipe === 'string' ? n.recipe : '' }));
523
+ recipes = recipesYamlFor(nodes, steps);
524
+ const walked = await dryRunWizardGraph(yaml, recipes);
525
+ dryRun = { status: walked.status, path: walked.path };
526
+ if (walked.status !== 'done') issues.push(`graph run --dry-run: ${walked.status}${walked.error ? ` — ${walked.error}` : ''} (경로 ${walked.path.join(' → ')})`);
527
+ }
528
+ debug.log('graph.wizard', 'attempt', { id, attempt: attempts, base, ok: issues.length === 0, issues: issues.slice(0, 10) });
529
+ if (issues.length === 0) break;
530
+ }
531
+ attempts = Math.min(attempts, maxAttempts);
532
+ const ok = issues.length === 0;
533
+ const counted = summarizeChange(kind, editing ? req.currentYaml : undefined, yaml);
534
+ const summary = llmSummary ? `${llmSummary} (${counted})` : counted;
535
+ debug.log('graph.wizard', ok ? 'validated' : 'failed', { id, kind, base, attempts, editing, issues: issues.slice(0, 10), summary, dryRun: dryRun?.status, ms: Date.now() - startedAt });
536
+ const labels = Object.fromEntries(Object.entries(steps).filter(([, v]) => v.label).map(([k, v]) => [k, v.label!]));
537
+ return { ok, id, yaml, ...(base ? { base } : {}), ...(base && baseReason ? { baseReason } : {}), issues, attempts, summary, labels, steps, ...(recipes ? { recipes } : {}), ...(dryRun ? { dryRun } : {}) };
538
+ }
539
+
540
+ /** v1 이름 유지 — 생성 YAML 의 노드 주석(단계·인자)으로 실제 recipes.yaml 을 낸다(v1 의 «미구현» 자리표시를 대체). */
541
+ export function stubRecipesFor(yaml: string): string {
542
+ const raw = parseYaml(yaml) as { nodes?: Array<{ node_id?: unknown; recipe?: unknown }> };
543
+ const nodes = (raw.nodes ?? []).map((n) => ({ nodeId: String(n.node_id), recipe: typeof n.recipe === 'string' ? n.recipe : '' }));
544
+ return recipesYamlFor(nodes, parseNodeAnnotations(yaml));
545
+ }