smol-symphony 0.2.0 → 0.3.0

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 (716) hide show
  1. package/AGENTS.md +41 -22
  2. package/DESIGN.md +494 -273
  3. package/README.md +109 -57
  4. package/SPEC.md +33 -24
  5. package/WORKFLOW.minimal.yaml +34 -0
  6. package/{WORKFLOW.template.md → WORKFLOW.template.yaml} +409 -256
  7. package/WORKFLOW.yaml +487 -0
  8. package/assets/skills/symphony-issues/SKILL.md +136 -0
  9. package/assets/symphony-mise.system.toml +68 -0
  10. package/dist/src/bin/symphony.js +30 -0
  11. package/dist/src/bin/symphony.js.map +1 -0
  12. package/dist/src/core/actions/context.js +109 -0
  13. package/dist/src/core/actions/context.js.map +1 -0
  14. package/dist/{actions/parsing.js → src/core/actions/parse.js} +33 -114
  15. package/dist/src/core/actions/parse.js.map +1 -0
  16. package/dist/src/core/actions/plan.js +197 -0
  17. package/dist/src/core/actions/plan.js.map +1 -0
  18. package/dist/src/core/actions/predicates.js +111 -0
  19. package/dist/src/core/actions/predicates.js.map +1 -0
  20. package/dist/src/core/actions/run-fold.js +248 -0
  21. package/dist/src/core/actions/run-fold.js.map +1 -0
  22. package/dist/src/core/actions/template.js +118 -0
  23. package/dist/src/core/actions/template.js.map +1 -0
  24. package/dist/src/core/cli/args.js +116 -0
  25. package/dist/src/core/cli/args.js.map +1 -0
  26. package/dist/src/core/coerce.js +75 -0
  27. package/dist/src/core/coerce.js.map +1 -0
  28. package/dist/src/core/credential/account-id.js +20 -0
  29. package/dist/src/core/credential/account-id.js.map +1 -0
  30. package/dist/src/core/credential/adapter-config.js +136 -0
  31. package/dist/src/core/credential/adapter-config.js.map +1 -0
  32. package/dist/src/core/credential/availability.js +98 -0
  33. package/dist/src/core/credential/availability.js.map +1 -0
  34. package/dist/src/core/credential/extract.js +228 -0
  35. package/dist/src/core/credential/extract.js.map +1 -0
  36. package/dist/src/core/credential/fake-creds.js +171 -0
  37. package/dist/src/core/credential/fake-creds.js.map +1 -0
  38. package/dist/src/core/credential/identity.js +125 -0
  39. package/dist/src/core/credential/identity.js.map +1 -0
  40. package/dist/src/core/credential/shape.js +230 -0
  41. package/dist/src/core/credential/shape.js.map +1 -0
  42. package/dist/src/core/credential/strings.js +15 -0
  43. package/dist/src/core/credential/strings.js.map +1 -0
  44. package/dist/src/core/doctor/checks.js +303 -0
  45. package/dist/src/core/doctor/checks.js.map +1 -0
  46. package/dist/src/core/git/result.js +107 -0
  47. package/dist/src/core/git/result.js.map +1 -0
  48. package/dist/src/core/http/decisions.js +225 -0
  49. package/dist/src/core/http/decisions.js.map +1 -0
  50. package/dist/{http.js → src/core/http/render.js} +472 -738
  51. package/dist/src/core/http/render.js.map +1 -0
  52. package/dist/{http-handlers.js → src/core/http/routes.js} +52 -87
  53. package/dist/src/core/http/routes.js.map +1 -0
  54. package/dist/src/core/http/views.js +181 -0
  55. package/dist/src/core/http/views.js.map +1 -0
  56. package/dist/src/core/image/managed-image.js +95 -0
  57. package/dist/src/core/image/managed-image.js.map +1 -0
  58. package/dist/src/core/issue/file.js +149 -0
  59. package/dist/src/core/issue/file.js.map +1 -0
  60. package/dist/src/core/issue/parse.js +210 -0
  61. package/dist/src/core/issue/parse.js.map +1 -0
  62. package/dist/src/core/mcp/dispatch.js +239 -0
  63. package/dist/src/core/mcp/dispatch.js.map +1 -0
  64. package/dist/src/core/mcp/post-move.js +92 -0
  65. package/dist/src/core/mcp/post-move.js.map +1 -0
  66. package/dist/src/core/mcp/protocol.js +293 -0
  67. package/dist/src/core/mcp/protocol.js.map +1 -0
  68. package/dist/src/core/mcp/url.js +162 -0
  69. package/dist/src/core/mcp/url.js.map +1 -0
  70. package/dist/src/core/path.js +63 -0
  71. package/dist/src/core/path.js.map +1 -0
  72. package/dist/src/core/reconcile/image-decide.js +48 -0
  73. package/dist/src/core/reconcile/image-decide.js.map +1 -0
  74. package/dist/src/core/reconcile/ledger.js +142 -0
  75. package/dist/src/core/reconcile/ledger.js.map +1 -0
  76. package/dist/src/core/reconcile/pr-classify.js +62 -0
  77. package/dist/src/core/reconcile/pr-classify.js.map +1 -0
  78. package/dist/{reconciler → src/core/reconcile}/pr-decide.js +25 -12
  79. package/dist/src/core/reconcile/pr-decide.js.map +1 -0
  80. package/dist/src/core/reconcile/pr-loop.js +161 -0
  81. package/dist/src/core/reconcile/pr-loop.js.map +1 -0
  82. package/dist/src/core/reconcile/pr-notes.js +35 -0
  83. package/dist/src/core/reconcile/pr-notes.js.map +1 -0
  84. package/dist/src/core/reconcile/vm-decide.js +70 -0
  85. package/dist/src/core/reconcile/vm-decide.js.map +1 -0
  86. package/dist/src/core/reconcile/vm-reap.js +207 -0
  87. package/dist/src/core/reconcile/vm-reap.js.map +1 -0
  88. package/dist/src/core/reconcile/workspace-decide.js +162 -0
  89. package/dist/src/core/reconcile/workspace-decide.js.map +1 -0
  90. package/dist/src/core/runlog/summary.js +231 -0
  91. package/dist/src/core/runlog/summary.js.map +1 -0
  92. package/dist/src/core/runner/dispatch-config.js +95 -0
  93. package/dist/src/core/runner/dispatch-config.js.map +1 -0
  94. package/dist/src/core/runner/injection.js +61 -0
  95. package/dist/src/core/runner/injection.js.map +1 -0
  96. package/dist/src/core/runner/mise.js +210 -0
  97. package/dist/src/core/runner/mise.js.map +1 -0
  98. package/dist/src/core/runner/prompt.js +720 -0
  99. package/dist/src/core/runner/prompt.js.map +1 -0
  100. package/dist/src/core/runner/turn.js +242 -0
  101. package/dist/src/core/runner/turn.js.map +1 -0
  102. package/dist/src/core/runner/vm-plan.js +390 -0
  103. package/dist/src/core/runner/vm-plan.js.map +1 -0
  104. package/dist/src/core/schedule/admission.js +123 -0
  105. package/dist/src/core/schedule/admission.js.map +1 -0
  106. package/dist/src/core/schedule/circuit-breaker.js +111 -0
  107. package/dist/src/core/schedule/circuit-breaker.js.map +1 -0
  108. package/dist/src/core/schedule/eligibility.js +83 -0
  109. package/dist/src/core/schedule/eligibility.js.map +1 -0
  110. package/dist/src/core/schedule/reconcile-issue.js +82 -0
  111. package/dist/src/core/schedule/reconcile-issue.js.map +1 -0
  112. package/dist/src/core/schedule/retry.js +96 -0
  113. package/dist/src/core/schedule/retry.js.map +1 -0
  114. package/dist/src/core/schedule/sleep-cycle.js +133 -0
  115. package/dist/src/core/schedule/sleep-cycle.js.map +1 -0
  116. package/dist/src/core/schedule/slots.js +124 -0
  117. package/dist/src/core/schedule/slots.js.map +1 -0
  118. package/dist/src/core/schedule/tick.js +553 -0
  119. package/dist/src/core/schedule/tick.js.map +1 -0
  120. package/dist/src/core/schedule/token-fold.js +181 -0
  121. package/dist/src/core/schedule/token-fold.js.map +1 -0
  122. package/dist/src/core/state-resolve.js +86 -0
  123. package/dist/src/core/state-resolve.js.map +1 -0
  124. package/dist/src/core/vm-guards.js +278 -0
  125. package/dist/src/core/vm-guards.js.map +1 -0
  126. package/dist/src/core/workflow/derive.js +107 -0
  127. package/dist/src/core/workflow/derive.js.map +1 -0
  128. package/dist/src/core/workflow/parse.js +687 -0
  129. package/dist/src/core/workflow/parse.js.map +1 -0
  130. package/dist/src/core/workflow/prompt-probe.js +78 -0
  131. package/dist/src/core/workflow/prompt-probe.js.map +1 -0
  132. package/dist/src/core/workflow/validate.js +189 -0
  133. package/dist/src/core/workflow/validate.js.map +1 -0
  134. package/dist/src/core/workspace-key.js +19 -0
  135. package/dist/src/core/workspace-key.js.map +1 -0
  136. package/dist/src/shell/actions-runner.js +356 -0
  137. package/dist/src/shell/actions-runner.js.map +1 -0
  138. package/dist/src/shell/adapter/adapter-registry.js +45 -0
  139. package/dist/src/shell/adapter/adapter-registry.js.map +1 -0
  140. package/dist/src/shell/adapter/clock-random.js +96 -0
  141. package/dist/src/shell/adapter/clock-random.js.map +1 -0
  142. package/dist/src/shell/adapter/gondolin-dispatch-helpers.js +158 -0
  143. package/dist/src/shell/adapter/gondolin-dispatch-helpers.js.map +1 -0
  144. package/dist/src/shell/adapter/gondolin-dispatch.js +385 -0
  145. package/dist/src/shell/adapter/gondolin-dispatch.js.map +1 -0
  146. package/dist/src/shell/adapter/gondolin-image-converter.js +233 -0
  147. package/dist/src/shell/adapter/gondolin-image-converter.js.map +1 -0
  148. package/dist/src/shell/adapter/gondolin-image-fetch.js +180 -0
  149. package/dist/src/shell/adapter/gondolin-image-fetch.js.map +1 -0
  150. package/dist/src/shell/adapter/launcher-asset.js +57 -0
  151. package/dist/src/shell/adapter/launcher-asset.js.map +1 -0
  152. package/dist/src/shell/adapter/mise-config-asset.js +65 -0
  153. package/dist/src/shell/adapter/mise-config-asset.js.map +1 -0
  154. package/dist/src/shell/adapter/workflow-loader.js +304 -0
  155. package/dist/src/shell/adapter/workflow-loader.js.map +1 -0
  156. package/dist/src/shell/cli/doctor.js +268 -0
  157. package/dist/src/shell/cli/doctor.js.map +1 -0
  158. package/dist/src/shell/effect-interpreter-families.js +314 -0
  159. package/dist/src/shell/effect-interpreter-families.js.map +1 -0
  160. package/dist/src/shell/effect-interpreter.js +29 -0
  161. package/dist/src/shell/effect-interpreter.js.map +1 -0
  162. package/dist/src/shell/interp/acp-frame.js +137 -0
  163. package/dist/src/shell/interp/acp-frame.js.map +1 -0
  164. package/dist/src/shell/interp/acp-ws-conn.js +320 -0
  165. package/dist/src/shell/interp/acp-ws-conn.js.map +1 -0
  166. package/dist/src/shell/interp/acp-ws-frames.js +159 -0
  167. package/dist/src/shell/interp/acp-ws-frames.js.map +1 -0
  168. package/dist/src/shell/interp/acp-ws.js +197 -0
  169. package/dist/src/shell/interp/acp-ws.js.map +1 -0
  170. package/dist/src/shell/interp/acp.js +319 -0
  171. package/dist/src/shell/interp/acp.js.map +1 -0
  172. package/dist/src/shell/interp/credential-defaults.js +128 -0
  173. package/dist/src/shell/interp/credential-defaults.js.map +1 -0
  174. package/dist/src/shell/interp/credential-hooks.js +149 -0
  175. package/dist/src/shell/interp/credential-hooks.js.map +1 -0
  176. package/dist/src/shell/interp/credential-registry.js +226 -0
  177. package/dist/src/shell/interp/credential-registry.js.map +1 -0
  178. package/dist/src/shell/interp/credential.js +103 -0
  179. package/dist/src/shell/interp/credential.js.map +1 -0
  180. package/dist/src/shell/interp/gh.js +163 -0
  181. package/dist/src/shell/interp/gh.js.map +1 -0
  182. package/dist/src/shell/interp/git.js +28 -0
  183. package/dist/src/shell/interp/git.js.map +1 -0
  184. package/dist/src/shell/interp/log.js +213 -0
  185. package/dist/src/shell/interp/log.js.map +1 -0
  186. package/dist/src/shell/interp/process.js +178 -0
  187. package/dist/src/shell/interp/process.js.map +1 -0
  188. package/dist/src/shell/interp/runlog.js +193 -0
  189. package/dist/src/shell/interp/runlog.js.map +1 -0
  190. package/dist/src/shell/interp/timer.js +64 -0
  191. package/dist/src/shell/interp/timer.js.map +1 -0
  192. package/dist/src/shell/interp/tracker-disk.js +99 -0
  193. package/dist/src/shell/interp/tracker-disk.js.map +1 -0
  194. package/dist/src/shell/interp/tracker-parse.js +71 -0
  195. package/dist/src/shell/interp/tracker-parse.js.map +1 -0
  196. package/dist/src/shell/interp/tracker-scan.js +238 -0
  197. package/dist/src/shell/interp/tracker-scan.js.map +1 -0
  198. package/dist/src/shell/interp/tracker-write.js +91 -0
  199. package/dist/src/shell/interp/tracker-write.js.map +1 -0
  200. package/dist/src/shell/interp/tracker.js +41 -0
  201. package/dist/src/shell/interp/tracker.js.map +1 -0
  202. package/dist/src/shell/interp/tty.js +48 -0
  203. package/dist/src/shell/interp/tty.js.map +1 -0
  204. package/dist/src/shell/interp/vm.js +199 -0
  205. package/dist/src/shell/interp/vm.js.map +1 -0
  206. package/dist/src/shell/interp/workspace.js +310 -0
  207. package/dist/src/shell/interp/workspace.js.map +1 -0
  208. package/dist/src/shell/main-acp.js +78 -0
  209. package/dist/src/shell/main-acp.js.map +1 -0
  210. package/dist/src/shell/main-adapters.js +222 -0
  211. package/dist/src/shell/main-adapters.js.map +1 -0
  212. package/dist/src/shell/main-credential.js +122 -0
  213. package/dist/src/shell/main-credential.js.map +1 -0
  214. package/dist/src/shell/main-doctor.js +22 -0
  215. package/dist/src/shell/main-doctor.js.map +1 -0
  216. package/dist/src/shell/main-entry.js +46 -0
  217. package/dist/src/shell/main-entry.js.map +1 -0
  218. package/dist/src/shell/main-http-csrf.js +45 -0
  219. package/dist/src/shell/main-http-csrf.js.map +1 -0
  220. package/dist/src/shell/main-http-handler.js +389 -0
  221. package/dist/src/shell/main-http-handler.js.map +1 -0
  222. package/dist/src/shell/main-http-mcp.js +122 -0
  223. package/dist/src/shell/main-http-mcp.js.map +1 -0
  224. package/dist/src/shell/main-http-views.js +253 -0
  225. package/dist/src/shell/main-http-views.js.map +1 -0
  226. package/dist/src/shell/main-http.js +76 -0
  227. package/dist/src/shell/main-http.js.map +1 -0
  228. package/dist/src/shell/main-loops.js +130 -0
  229. package/dist/src/shell/main-loops.js.map +1 -0
  230. package/dist/src/shell/main-mcp.js +129 -0
  231. package/dist/src/shell/main-mcp.js.map +1 -0
  232. package/dist/src/shell/main-orchestrator.js +120 -0
  233. package/dist/src/shell/main-orchestrator.js.map +1 -0
  234. package/dist/src/shell/main-preflight.js +43 -0
  235. package/dist/src/shell/main-preflight.js.map +1 -0
  236. package/dist/src/shell/main-reconcilers-helpers.js +244 -0
  237. package/dist/src/shell/main-reconcilers-helpers.js.map +1 -0
  238. package/dist/src/shell/main-reconcilers-pr.js +148 -0
  239. package/dist/src/shell/main-reconcilers-pr.js.map +1 -0
  240. package/dist/src/shell/main-reconcilers.js +225 -0
  241. package/dist/src/shell/main-reconcilers.js.map +1 -0
  242. package/dist/src/shell/main-runner.js +355 -0
  243. package/dist/src/shell/main-runner.js.map +1 -0
  244. package/dist/src/shell/main-scaffold.js +116 -0
  245. package/dist/src/shell/main-scaffold.js.map +1 -0
  246. package/dist/src/shell/main-shutdown.js +115 -0
  247. package/dist/src/shell/main-shutdown.js.map +1 -0
  248. package/dist/src/shell/main-startup.js +48 -0
  249. package/dist/src/shell/main-startup.js.map +1 -0
  250. package/dist/src/shell/main-substrates.js +43 -0
  251. package/dist/src/shell/main-substrates.js.map +1 -0
  252. package/dist/src/shell/main.js +385 -0
  253. package/dist/src/shell/main.js.map +1 -0
  254. package/dist/src/shell/orchestrator-feedback.js +69 -0
  255. package/dist/src/shell/orchestrator-feedback.js.map +1 -0
  256. package/dist/src/shell/orchestrator-image.js +167 -0
  257. package/dist/src/shell/orchestrator-image.js.map +1 -0
  258. package/dist/src/shell/orchestrator-loop.js +468 -0
  259. package/dist/src/shell/orchestrator-loop.js.map +1 -0
  260. package/dist/src/shell/orchestrator-reconcile.js +36 -0
  261. package/dist/src/shell/orchestrator-reconcile.js.map +1 -0
  262. package/dist/src/shell/reconciler-loop.js +228 -0
  263. package/dist/src/shell/reconciler-loop.js.map +1 -0
  264. package/dist/src/shell/runner-loop-turn.js +301 -0
  265. package/dist/src/shell/runner-loop-turn.js.map +1 -0
  266. package/dist/src/shell/runner-loop.js +338 -0
  267. package/dist/src/shell/runner-loop.js.map +1 -0
  268. package/dist/src/shell/server/http.js +208 -0
  269. package/dist/src/shell/server/http.js.map +1 -0
  270. package/dist/src/shell/server/mcp-runtime-effects.js +237 -0
  271. package/dist/src/shell/server/mcp-runtime-effects.js.map +1 -0
  272. package/dist/src/shell/server/mcp-runtime.js +99 -0
  273. package/dist/src/shell/server/mcp-runtime.js.map +1 -0
  274. package/dist/src/shell/workspace-key.js +14 -0
  275. package/dist/src/shell/workspace-key.js.map +1 -0
  276. package/dist/src/types/acp.js +8 -0
  277. package/dist/src/types/acp.js.map +1 -0
  278. package/dist/src/types/actions/plan.js +6 -0
  279. package/dist/src/types/actions/plan.js.map +1 -0
  280. package/dist/src/types/actions/predicates.js +6 -0
  281. package/dist/src/types/actions/predicates.js.map +1 -0
  282. package/dist/src/types/actions/run-fold.js +8 -0
  283. package/dist/src/types/actions/run-fold.js.map +1 -0
  284. package/dist/src/types/actions.js +7 -0
  285. package/dist/src/types/actions.js.map +1 -0
  286. package/dist/src/types/adapter/clock-random.js +4 -0
  287. package/dist/src/types/adapter/clock-random.js.map +1 -0
  288. package/dist/src/types/adapter/gondolin-image-converter.js +5 -0
  289. package/dist/src/types/adapter/gondolin-image-converter.js.map +1 -0
  290. package/dist/src/types/adapter/gondolin-image-fetch.js +5 -0
  291. package/dist/src/types/adapter/gondolin-image-fetch.js.map +1 -0
  292. package/dist/src/types/adapter/workflow-loader.js +4 -0
  293. package/dist/src/types/adapter/workflow-loader.js.map +1 -0
  294. package/dist/src/types/cli/args.js +8 -0
  295. package/dist/src/types/cli/args.js.map +1 -0
  296. package/dist/src/types/config.js +8 -0
  297. package/dist/src/types/config.js.map +1 -0
  298. package/dist/src/types/credential-interp.js +6 -0
  299. package/dist/src/types/credential-interp.js.map +1 -0
  300. package/dist/src/types/credentials.js +10 -0
  301. package/dist/src/types/credentials.js.map +1 -0
  302. package/dist/src/types/doctor.js +7 -0
  303. package/dist/src/types/doctor.js.map +1 -0
  304. package/dist/src/types/domain.js +7 -0
  305. package/dist/src/types/domain.js.map +1 -0
  306. package/dist/src/types/effect.js +15 -0
  307. package/dist/src/types/effect.js.map +1 -0
  308. package/dist/src/types/errors.js +39 -0
  309. package/dist/src/types/errors.js.map +1 -0
  310. package/dist/src/types/http/decisions.js +6 -0
  311. package/dist/src/types/http/decisions.js.map +1 -0
  312. package/dist/src/types/http/render.js +10 -0
  313. package/dist/src/types/http/render.js.map +1 -0
  314. package/dist/src/types/http/views.js +6 -0
  315. package/dist/src/types/http/views.js.map +1 -0
  316. package/dist/src/types/http.js +9 -0
  317. package/dist/src/types/http.js.map +1 -0
  318. package/dist/src/types/image/managed-image.js +7 -0
  319. package/dist/src/types/image/managed-image.js.map +1 -0
  320. package/dist/src/types/interp/effect-interpreter.js +8 -0
  321. package/dist/src/types/interp/effect-interpreter.js.map +1 -0
  322. package/dist/src/types/interp/tracker.js +7 -0
  323. package/dist/src/types/interp/tracker.js.map +1 -0
  324. package/dist/src/types/issue/file.js +6 -0
  325. package/dist/src/types/issue/file.js.map +1 -0
  326. package/dist/src/types/issue/parse.js +8 -0
  327. package/dist/src/types/issue/parse.js.map +1 -0
  328. package/dist/src/types/main-acp.js +13 -0
  329. package/dist/src/types/main-acp.js.map +1 -0
  330. package/dist/src/types/main-adapters.js +5 -0
  331. package/dist/src/types/main-adapters.js.map +1 -0
  332. package/dist/src/types/main-credential.js +21 -0
  333. package/dist/src/types/main-credential.js.map +1 -0
  334. package/dist/src/types/main-doctor.js +6 -0
  335. package/dist/src/types/main-doctor.js.map +1 -0
  336. package/dist/src/types/main-http-handler.js +12 -0
  337. package/dist/src/types/main-http-handler.js.map +1 -0
  338. package/dist/src/types/main-http.js +5 -0
  339. package/dist/src/types/main-http.js.map +1 -0
  340. package/dist/src/types/main-loops.js +5 -0
  341. package/dist/src/types/main-loops.js.map +1 -0
  342. package/dist/src/types/main-mcp.js +12 -0
  343. package/dist/src/types/main-mcp.js.map +1 -0
  344. package/dist/src/types/main-orchestrator.js +5 -0
  345. package/dist/src/types/main-orchestrator.js.map +1 -0
  346. package/dist/src/types/main-reconcilers.js +11 -0
  347. package/dist/src/types/main-reconcilers.js.map +1 -0
  348. package/dist/src/types/main-runner.js +13 -0
  349. package/dist/src/types/main-runner.js.map +1 -0
  350. package/dist/src/types/main-startup.js +5 -0
  351. package/dist/src/types/main-startup.js.map +1 -0
  352. package/dist/src/types/main-substrates.js +5 -0
  353. package/dist/src/types/main-substrates.js.map +1 -0
  354. package/dist/src/types/mcp/dispatch.js +4 -0
  355. package/dist/src/types/mcp/dispatch.js.map +1 -0
  356. package/dist/src/types/mcp/post-move.js +7 -0
  357. package/dist/src/types/mcp/post-move.js.map +1 -0
  358. package/dist/src/types/mcp.js +9 -0
  359. package/dist/src/types/mcp.js.map +1 -0
  360. package/dist/src/types/ports.js +12 -0
  361. package/dist/src/types/ports.js.map +1 -0
  362. package/dist/src/types/reconcile/image-decide.js +5 -0
  363. package/dist/src/types/reconcile/image-decide.js.map +1 -0
  364. package/dist/src/types/reconcile/ledger.js +7 -0
  365. package/dist/src/types/reconcile/ledger.js.map +1 -0
  366. package/dist/src/types/reconcile/pr-loop.js +8 -0
  367. package/dist/src/types/reconcile/pr-loop.js.map +1 -0
  368. package/dist/src/types/reconcile/vm-reap.js +8 -0
  369. package/dist/src/types/reconcile/vm-reap.js.map +1 -0
  370. package/dist/src/types/reconcile/workspace-decide.js +7 -0
  371. package/dist/src/types/reconcile/workspace-decide.js.map +1 -0
  372. package/dist/src/types/reconcile.js +9 -0
  373. package/dist/src/types/reconcile.js.map +1 -0
  374. package/dist/src/types/runlog.js +7 -0
  375. package/dist/src/types/runlog.js.map +1 -0
  376. package/dist/src/types/runner/actions-runner.js +12 -0
  377. package/dist/src/types/runner/actions-runner.js.map +1 -0
  378. package/dist/src/types/runner/gondolin-dispatch.js +5 -0
  379. package/dist/src/types/runner/gondolin-dispatch.js.map +1 -0
  380. package/dist/src/types/runner/injection.js +6 -0
  381. package/dist/src/types/runner/injection.js.map +1 -0
  382. package/dist/src/types/runner/runner-loop.js +5 -0
  383. package/dist/src/types/runner/runner-loop.js.map +1 -0
  384. package/dist/src/types/runner/turn.js +4 -0
  385. package/dist/src/types/runner/turn.js.map +1 -0
  386. package/dist/src/types/runner/vm-plan.js +4 -0
  387. package/dist/src/types/runner/vm-plan.js.map +1 -0
  388. package/dist/src/types/runtime.js +9 -0
  389. package/dist/src/types/runtime.js.map +1 -0
  390. package/dist/src/types/schedule/admission.js +7 -0
  391. package/dist/src/types/schedule/admission.js.map +1 -0
  392. package/dist/src/types/schedule/circuit-breaker.js +2 -0
  393. package/dist/src/types/schedule/circuit-breaker.js.map +1 -0
  394. package/dist/src/types/schedule/eligibility.js +9 -0
  395. package/dist/src/types/schedule/eligibility.js.map +1 -0
  396. package/dist/src/types/schedule/orchestrator-loop.js +10 -0
  397. package/dist/src/types/schedule/orchestrator-loop.js.map +1 -0
  398. package/dist/src/types/schedule/sleep-cycle.js +4 -0
  399. package/dist/src/types/schedule/sleep-cycle.js.map +1 -0
  400. package/dist/src/types/schedule/slots.js +8 -0
  401. package/dist/src/types/schedule/slots.js.map +1 -0
  402. package/dist/src/types/schedule/tick.js +9 -0
  403. package/dist/src/types/schedule/tick.js.map +1 -0
  404. package/dist/src/types/server/mcp-runtime.js +8 -0
  405. package/dist/src/types/server/mcp-runtime.js.map +1 -0
  406. package/dist/src/types/workflow/parse.js +4 -0
  407. package/dist/src/types/workflow/parse.js.map +1 -0
  408. package/dist/tests/core/account-id.test.js +35 -0
  409. package/dist/tests/core/account-id.test.js.map +1 -0
  410. package/dist/tests/core/actions-parse.test.js +176 -0
  411. package/dist/tests/core/actions-parse.test.js.map +1 -0
  412. package/dist/tests/core/adapter-config.test.js +133 -0
  413. package/dist/tests/core/adapter-config.test.js.map +1 -0
  414. package/dist/tests/core/admission.test.js +215 -0
  415. package/dist/tests/core/admission.test.js.map +1 -0
  416. package/dist/tests/core/args.test.js +132 -0
  417. package/dist/tests/core/args.test.js.map +1 -0
  418. package/dist/tests/core/availability.test.js +62 -0
  419. package/dist/tests/core/availability.test.js.map +1 -0
  420. package/dist/tests/core/checks.test.js +395 -0
  421. package/dist/tests/core/checks.test.js.map +1 -0
  422. package/dist/tests/core/circuit-breaker.test.js +172 -0
  423. package/dist/tests/core/circuit-breaker.test.js.map +1 -0
  424. package/dist/tests/core/coerce.test.js +87 -0
  425. package/dist/tests/core/coerce.test.js.map +1 -0
  426. package/dist/tests/core/context.test.js +228 -0
  427. package/dist/tests/core/context.test.js.map +1 -0
  428. package/dist/tests/core/decisions.test.js +310 -0
  429. package/dist/tests/core/decisions.test.js.map +1 -0
  430. package/dist/tests/core/derive.test.js +205 -0
  431. package/dist/tests/core/derive.test.js.map +1 -0
  432. package/dist/tests/core/dispatch-config.test.js +164 -0
  433. package/dist/tests/core/dispatch-config.test.js.map +1 -0
  434. package/dist/tests/core/dispatch.test.js +302 -0
  435. package/dist/tests/core/dispatch.test.js.map +1 -0
  436. package/dist/tests/core/eligibility.test.js +163 -0
  437. package/dist/tests/core/eligibility.test.js.map +1 -0
  438. package/dist/tests/core/extract.test.js +139 -0
  439. package/dist/tests/core/extract.test.js.map +1 -0
  440. package/dist/tests/core/fake-creds.test.js +134 -0
  441. package/dist/tests/core/fake-creds.test.js.map +1 -0
  442. package/dist/tests/core/file.test.js +197 -0
  443. package/dist/tests/core/file.test.js.map +1 -0
  444. package/dist/tests/core/git-result.test.js +113 -0
  445. package/dist/tests/core/git-result.test.js.map +1 -0
  446. package/dist/tests/core/identity.test.js +180 -0
  447. package/dist/tests/core/identity.test.js.map +1 -0
  448. package/dist/tests/core/image-decide.test.js +59 -0
  449. package/dist/tests/core/image-decide.test.js.map +1 -0
  450. package/dist/tests/core/injection.test.js +163 -0
  451. package/dist/tests/core/injection.test.js.map +1 -0
  452. package/dist/tests/core/ledger.test.js +218 -0
  453. package/dist/tests/core/ledger.test.js.map +1 -0
  454. package/dist/tests/core/managed-image.test.js +68 -0
  455. package/dist/tests/core/managed-image.test.js.map +1 -0
  456. package/dist/tests/core/mise.test.js +138 -0
  457. package/dist/tests/core/mise.test.js.map +1 -0
  458. package/dist/tests/core/parse.test.js +174 -0
  459. package/dist/tests/core/parse.test.js.map +1 -0
  460. package/dist/tests/core/path.test.js +50 -0
  461. package/dist/tests/core/path.test.js.map +1 -0
  462. package/dist/tests/core/plan.test.js +218 -0
  463. package/dist/tests/core/plan.test.js.map +1 -0
  464. package/dist/tests/core/post-move.test.js +162 -0
  465. package/dist/tests/core/post-move.test.js.map +1 -0
  466. package/dist/tests/core/pr-classify.test.js +117 -0
  467. package/dist/tests/core/pr-classify.test.js.map +1 -0
  468. package/dist/tests/core/pr-decide.test.js +298 -0
  469. package/dist/tests/core/pr-decide.test.js.map +1 -0
  470. package/dist/tests/core/pr-loop.test.js +301 -0
  471. package/dist/tests/core/pr-loop.test.js.map +1 -0
  472. package/dist/tests/core/pr-notes.test.js +165 -0
  473. package/dist/tests/core/pr-notes.test.js.map +1 -0
  474. package/dist/tests/core/predicates.test.js +154 -0
  475. package/dist/tests/core/predicates.test.js.map +1 -0
  476. package/dist/tests/core/prompt.test.js +189 -0
  477. package/dist/tests/core/prompt.test.js.map +1 -0
  478. package/dist/tests/core/protocol.test.js +195 -0
  479. package/dist/tests/core/protocol.test.js.map +1 -0
  480. package/dist/tests/core/reconcile-issue.test.js +116 -0
  481. package/dist/tests/core/reconcile-issue.test.js.map +1 -0
  482. package/dist/tests/core/render.test.js +549 -0
  483. package/dist/tests/core/render.test.js.map +1 -0
  484. package/dist/tests/core/retry.test.js +186 -0
  485. package/dist/tests/core/retry.test.js.map +1 -0
  486. package/dist/tests/core/routes.test.js +247 -0
  487. package/dist/tests/core/routes.test.js.map +1 -0
  488. package/dist/tests/core/run-fold.test.js +299 -0
  489. package/dist/tests/core/run-fold.test.js.map +1 -0
  490. package/dist/tests/core/shape.test.js +185 -0
  491. package/dist/tests/core/shape.test.js.map +1 -0
  492. package/dist/tests/core/sleep-cycle.test.js +150 -0
  493. package/dist/tests/core/sleep-cycle.test.js.map +1 -0
  494. package/dist/tests/core/slots.test.js +201 -0
  495. package/dist/tests/core/slots.test.js.map +1 -0
  496. package/dist/tests/core/state-resolve.test.js +80 -0
  497. package/dist/tests/core/state-resolve.test.js.map +1 -0
  498. package/dist/tests/core/summary.test.js +200 -0
  499. package/dist/tests/core/summary.test.js.map +1 -0
  500. package/dist/tests/core/template.test.js +116 -0
  501. package/dist/tests/core/template.test.js.map +1 -0
  502. package/dist/tests/core/tick.test.js +558 -0
  503. package/dist/tests/core/tick.test.js.map +1 -0
  504. package/dist/tests/core/token-fold.test.js +176 -0
  505. package/dist/tests/core/token-fold.test.js.map +1 -0
  506. package/dist/tests/core/turn.test.js +388 -0
  507. package/dist/tests/core/turn.test.js.map +1 -0
  508. package/dist/tests/core/url.test.js +118 -0
  509. package/dist/tests/core/url.test.js.map +1 -0
  510. package/dist/tests/core/validate.test.js +247 -0
  511. package/dist/tests/core/validate.test.js.map +1 -0
  512. package/dist/tests/core/views.test.js +252 -0
  513. package/dist/tests/core/views.test.js.map +1 -0
  514. package/dist/tests/core/vm-decide.test.js +110 -0
  515. package/dist/tests/core/vm-decide.test.js.map +1 -0
  516. package/dist/tests/core/vm-guards.test.js +153 -0
  517. package/dist/tests/core/vm-guards.test.js.map +1 -0
  518. package/dist/tests/core/vm-plan.test.js +332 -0
  519. package/dist/tests/core/vm-plan.test.js.map +1 -0
  520. package/dist/tests/core/vm-reap.test.js +196 -0
  521. package/dist/tests/core/vm-reap.test.js.map +1 -0
  522. package/dist/tests/core/workflow-parse.test.js +493 -0
  523. package/dist/tests/core/workflow-parse.test.js.map +1 -0
  524. package/dist/tests/core/workspace-decide.test.js +236 -0
  525. package/dist/tests/core/workspace-decide.test.js.map +1 -0
  526. package/dist/tests/helpers/fixtures.js +167 -0
  527. package/dist/tests/helpers/fixtures.js.map +1 -0
  528. package/dist/tests/shell/acp-substrate.test.js +101 -0
  529. package/dist/tests/shell/acp-substrate.test.js.map +1 -0
  530. package/dist/tests/shell/actions-runner-push.test.js +203 -0
  531. package/dist/tests/shell/actions-runner-push.test.js.map +1 -0
  532. package/dist/tests/shell/credential-hooks.test.js +36 -0
  533. package/dist/tests/shell/credential-hooks.test.js.map +1 -0
  534. package/dist/tests/shell/credential-registry.test.js +165 -0
  535. package/dist/tests/shell/credential-registry.test.js.map +1 -0
  536. package/dist/tests/shell/credential-substrate.test.js +179 -0
  537. package/dist/tests/shell/credential-substrate.test.js.map +1 -0
  538. package/dist/tests/shell/dockerfile-mise-pin.test.js +51 -0
  539. package/dist/tests/shell/dockerfile-mise-pin.test.js.map +1 -0
  540. package/dist/tests/shell/doctor.test.js +101 -0
  541. package/dist/tests/shell/doctor.test.js.map +1 -0
  542. package/dist/tests/shell/effect-vm-create.test.js +52 -0
  543. package/dist/tests/shell/effect-vm-create.test.js.map +1 -0
  544. package/dist/tests/shell/gh-port.test.js +63 -0
  545. package/dist/tests/shell/gh-port.test.js.map +1 -0
  546. package/dist/tests/shell/gondolin-dispatch-guard.test.js +144 -0
  547. package/dist/tests/shell/gondolin-dispatch-guard.test.js.map +1 -0
  548. package/dist/tests/shell/gondolin-dispatch-shquote.test.js +168 -0
  549. package/dist/tests/shell/gondolin-dispatch-shquote.test.js.map +1 -0
  550. package/dist/tests/shell/gondolin-image-converter.test.js +208 -0
  551. package/dist/tests/shell/gondolin-image-converter.test.js.map +1 -0
  552. package/dist/tests/shell/gondolin-image-fetch.test.js +93 -0
  553. package/dist/tests/shell/gondolin-image-fetch.test.js.map +1 -0
  554. package/dist/tests/shell/http-handler.test.js +608 -0
  555. package/dist/tests/shell/http-handler.test.js.map +1 -0
  556. package/dist/tests/shell/http-server.test.js +53 -0
  557. package/dist/tests/shell/http-server.test.js.map +1 -0
  558. package/dist/tests/shell/mcp-runtime.test.js +366 -0
  559. package/dist/tests/shell/mcp-runtime.test.js.map +1 -0
  560. package/dist/tests/shell/mise-config-asset.test.js +87 -0
  561. package/dist/tests/shell/mise-config-asset.test.js.map +1 -0
  562. package/dist/tests/shell/orchestrator-loop.test.js +583 -0
  563. package/dist/tests/shell/orchestrator-loop.test.js.map +1 -0
  564. package/dist/tests/shell/reconciler-passes.test.js +314 -0
  565. package/dist/tests/shell/reconciler-passes.test.js.map +1 -0
  566. package/dist/tests/shell/runner-loop-turn.test.js +97 -0
  567. package/dist/tests/shell/runner-loop-turn.test.js.map +1 -0
  568. package/dist/tests/shell/runner-slice.test.js +536 -0
  569. package/dist/tests/shell/runner-slice.test.js.map +1 -0
  570. package/dist/tests/shell/scaffold.test.js +65 -0
  571. package/dist/tests/shell/scaffold.test.js.map +1 -0
  572. package/dist/tests/shell/tick-config.test.js +83 -0
  573. package/dist/tests/shell/tick-config.test.js.map +1 -0
  574. package/dist/tests/shell/tracker-parse-dates.test.js +44 -0
  575. package/dist/tests/shell/tracker-parse-dates.test.js.map +1 -0
  576. package/dist/tests/shell/tracker-write-issue.test.js +154 -0
  577. package/dist/tests/shell/tracker-write-issue.test.js.map +1 -0
  578. package/dist/tests/shell/workflow-prompt-split.test.js +208 -0
  579. package/dist/tests/shell/workflow-prompt-split.test.js.map +1 -0
  580. package/dist/tests/shell/workspace-live-config.test.js +140 -0
  581. package/dist/tests/shell/workspace-live-config.test.js.map +1 -0
  582. package/package.json +21 -9
  583. package/patches/@earendil-works+gondolin+0.12.0.patch +173 -0
  584. package/prompts/Reflect.md +91 -0
  585. package/prompts/Review.md +97 -0
  586. package/prompts/Todo.md +96 -0
  587. package/prompts/_footer.md +41 -0
  588. package/prompts/_preamble.md +42 -0
  589. package/prompts-minimal/Todo.md +26 -0
  590. package/scripts/postinstall.mjs +63 -0
  591. package/scripts/vm-agent.mjs +312 -90
  592. package/WORKFLOW.md +0 -744
  593. package/dist/acp-bridge.js +0 -324
  594. package/dist/acp-bridge.js.map +0 -1
  595. package/dist/actions/cache.js +0 -191
  596. package/dist/actions/cache.js.map +0 -1
  597. package/dist/actions/effects.js +0 -41
  598. package/dist/actions/effects.js.map +0 -1
  599. package/dist/actions/executor.js +0 -570
  600. package/dist/actions/executor.js.map +0 -1
  601. package/dist/actions/index.js +0 -13
  602. package/dist/actions/index.js.map +0 -1
  603. package/dist/actions/parsing.js.map +0 -1
  604. package/dist/actions/predicate-env.js +0 -27
  605. package/dist/actions/predicate-env.js.map +0 -1
  606. package/dist/actions/predicates.js +0 -49
  607. package/dist/actions/predicates.js.map +0 -1
  608. package/dist/actions/templating.js +0 -66
  609. package/dist/actions/templating.js.map +0 -1
  610. package/dist/actions/types.js +0 -15
  611. package/dist/actions/types.js.map +0 -1
  612. package/dist/agent/acp.js +0 -473
  613. package/dist/agent/acp.js.map +0 -1
  614. package/dist/agent/adapter-names.js +0 -159
  615. package/dist/agent/adapter-names.js.map +0 -1
  616. package/dist/agent/adapters.js +0 -511
  617. package/dist/agent/adapters.js.map +0 -1
  618. package/dist/agent/credential-extractors.js +0 -342
  619. package/dist/agent/credential-extractors.js.map +0 -1
  620. package/dist/agent/credential-secrets.js +0 -628
  621. package/dist/agent/credential-secrets.js.map +0 -1
  622. package/dist/agent/credential-ticker.js +0 -57
  623. package/dist/agent/credential-ticker.js.map +0 -1
  624. package/dist/agent/gondolin-creds-staging.js +0 -356
  625. package/dist/agent/gondolin-creds-staging.js.map +0 -1
  626. package/dist/agent/gondolin-dispatch.js +0 -375
  627. package/dist/agent/gondolin-dispatch.js.map +0 -1
  628. package/dist/agent/gondolin.js +0 -124
  629. package/dist/agent/gondolin.js.map +0 -1
  630. package/dist/agent/runner-decisions.js +0 -134
  631. package/dist/agent/runner-decisions.js.map +0 -1
  632. package/dist/agent/runner.js +0 -1456
  633. package/dist/agent/runner.js.map +0 -1
  634. package/dist/agent/tool-call-summary.js +0 -102
  635. package/dist/agent/tool-call-summary.js.map +0 -1
  636. package/dist/agent/vm-acp-mapping.js +0 -73
  637. package/dist/agent/vm-acp-mapping.js.map +0 -1
  638. package/dist/agent/vm-guards.js +0 -262
  639. package/dist/agent/vm-guards.js.map +0 -1
  640. package/dist/agent/vm-port.js +0 -22
  641. package/dist/agent/vm-port.js.map +0 -1
  642. package/dist/agent/vm-process-registry.js +0 -79
  643. package/dist/agent/vm-process-registry.js.map +0 -1
  644. package/dist/bin/cli-args.js +0 -105
  645. package/dist/bin/cli-args.js.map +0 -1
  646. package/dist/bin/symphony.js +0 -794
  647. package/dist/bin/symphony.js.map +0 -1
  648. package/dist/errors.js +0 -15
  649. package/dist/errors.js.map +0 -1
  650. package/dist/http-disk.js +0 -135
  651. package/dist/http-disk.js.map +0 -1
  652. package/dist/http-handlers.js.map +0 -1
  653. package/dist/http.js.map +0 -1
  654. package/dist/issues.js +0 -178
  655. package/dist/issues.js.map +0 -1
  656. package/dist/logging.js +0 -203
  657. package/dist/logging.js.map +0 -1
  658. package/dist/mcp.js +0 -706
  659. package/dist/mcp.js.map +0 -1
  660. package/dist/memory.js +0 -85
  661. package/dist/memory.js.map +0 -1
  662. package/dist/orchestrator-decisions.js +0 -331
  663. package/dist/orchestrator-decisions.js.map +0 -1
  664. package/dist/orchestrator.js +0 -1569
  665. package/dist/orchestrator.js.map +0 -1
  666. package/dist/prompt.js +0 -65
  667. package/dist/prompt.js.map +0 -1
  668. package/dist/reconciler/cache.js +0 -65
  669. package/dist/reconciler/cache.js.map +0 -1
  670. package/dist/reconciler/index.js +0 -448
  671. package/dist/reconciler/index.js.map +0 -1
  672. package/dist/reconciler/ledger.js +0 -131
  673. package/dist/reconciler/ledger.js.map +0 -1
  674. package/dist/reconciler/pr-adapters.js +0 -174
  675. package/dist/reconciler/pr-adapters.js.map +0 -1
  676. package/dist/reconciler/pr-decide.js.map +0 -1
  677. package/dist/reconciler/pr.js +0 -422
  678. package/dist/reconciler/pr.js.map +0 -1
  679. package/dist/reconciler/types.js +0 -12
  680. package/dist/reconciler/types.js.map +0 -1
  681. package/dist/reconciler/vm.js +0 -243
  682. package/dist/reconciler/vm.js.map +0 -1
  683. package/dist/reconciler/workspace-defaults.js +0 -83
  684. package/dist/reconciler/workspace-defaults.js.map +0 -1
  685. package/dist/reconciler/workspace.js +0 -272
  686. package/dist/reconciler/workspace.js.map +0 -1
  687. package/dist/runlog.js +0 -403
  688. package/dist/runlog.js.map +0 -1
  689. package/dist/scaffold.js +0 -165
  690. package/dist/scaffold.js.map +0 -1
  691. package/dist/trackers/local.js +0 -445
  692. package/dist/trackers/local.js.map +0 -1
  693. package/dist/trackers/types.js +0 -10
  694. package/dist/trackers/types.js.map +0 -1
  695. package/dist/types.js +0 -3
  696. package/dist/types.js.map +0 -1
  697. package/dist/util/clock.js +0 -12
  698. package/dist/util/clock.js.map +0 -1
  699. package/dist/util/crypto.js +0 -25
  700. package/dist/util/crypto.js.map +0 -1
  701. package/dist/util/frontmatter.js +0 -70
  702. package/dist/util/frontmatter.js.map +0 -1
  703. package/dist/util/fs-issues.js +0 -22
  704. package/dist/util/fs-issues.js.map +0 -1
  705. package/dist/util/process.js +0 -152
  706. package/dist/util/process.js.map +0 -1
  707. package/dist/util/workspace-key.js +0 -10
  708. package/dist/util/workspace-key.js.map +0 -1
  709. package/dist/workflow-loader.js +0 -147
  710. package/dist/workflow-loader.js.map +0 -1
  711. package/dist/workflow.js +0 -822
  712. package/dist/workflow.js.map +0 -1
  713. package/dist/workspace-types.js +0 -8
  714. package/dist/workspace-types.js.map +0 -1
  715. package/dist/workspace.js +0 -443
  716. package/dist/workspace.js.map +0 -1
@@ -1,324 +0,0 @@
1
- // ACP TCP bridge — host-side transport for the in-VM ACP adapter.
2
- //
3
- // Before this module, symphony spoke ACP to the in-VM adapter over the earlier
4
- // in-VM-exec stdio channel directly. Two problems with that:
5
- //
6
- // 1. The in-VM-exec stdin pump does not reliably wake the in-VM reader for new
7
- // event-loop iterations unless host-side stdin keeps writing — so once symphony
8
- // stopped writing (after `session/prompt`), the adapter would freeze waiting for
9
- // events that the kernel had already delivered. The previous workaround was a
10
- // 1.5 s `\n` keepalive on the in-VM-exec stdin.
11
- //
12
- // 2. The in-VM-exec stdio path tightly coupled symphony to the exec channel. If we
13
- // ever swap the sandbox out for another (firecracker, gvisor, Kubernetes job, …)
14
- // we'd have to relearn the same per-sandbox-stdio quirks.
15
- //
16
- // This bridge replaces that path: symphony binds a TCP listener; the in-VM `vm-agent`
17
- // dials back, authenticates with a per-dispatch bearer token, and from then on ACP
18
- // JSON-RPC frames flow over a regular TCP socket. The in-VM exec is reduced to a process
19
- // launcher — its stdio is used only for diagnostic stderr from the in-VM agent. Any
20
- // sandbox that can (a) exec a process with env vars and (b) reach the host loopback
21
- // can now run our agent stack.
22
- //
23
- // Lifecycle:
24
- // 1. Orchestrator calls `bridge.register(issueId)` BEFORE launching the VM.
25
- // Returns a `{ token, accepted }` pair; `token` goes into the launch env as
26
- // SYMPHONY_ACP_TOKEN.
27
- // 2. Launch command exec's `node /opt/symphony/vm-agent.mjs`, which dials
28
- // `tcp://<bridge.host>:<bridge.port>` and writes `Bearer <token>\n` as its first
29
- // line.
30
- // 3. Bridge accepts the socket, parses the bearer line, looks up the registration,
31
- // validates the token (constant time), and resolves the registration's
32
- // `accepted` promise with the now-authenticated socket.
33
- // 4. Caller hands the socket to AcpClient as its stdin/stdout.
34
- // 5. On attempt teardown the caller closes the socket; vm-agent reacts by killing
35
- // the adapter and exiting; the in-VM exec sees the in-VM agent exit; the orchestrator
36
- // destroys the VM (per current lifecycle).
37
- import { createServer } from 'node:net';
38
- import { randomBytes, timingSafeEqual } from 'node:crypto';
39
- import { log } from './logging.js';
40
- /**
41
- * Loopback host literals `start()` accepts under `loopbackOnly`. We match the exact
42
- * IPv4 loopback `127.0.0.1`, the whole `127.0.0.0/8` range textually, the IPv6
43
- * loopback `::1`, and the `localhost` name. A `0.0.0.0`/`::`/LAN-IP/hostname bind is
44
- * rejected — those are wider than loopback.
45
- */
46
- function isLoopbackHost(host) {
47
- if (host === '::1' || host === 'localhost')
48
- return true;
49
- return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host);
50
- }
51
- export class AcpBridge {
52
- server = null;
53
- boundPort = null;
54
- // Pending registrations keyed by token. Lookups are constant time because we walk and
55
- // compare with `timingSafeEqual`; the map is just for fast membership filtering of
56
- // expired entries during teardown.
57
- pending = new Map();
58
- // Live, post-auth sockets we've handed to callers. We track them so `stop()` can force
59
- // them closed instead of waiting on `server.close()` to drain — without that, SIGTERM
60
- // during an active attempt would block indefinitely while the adapter side fails to
61
- // unwind. Added in response to a review finding that pointed out the shutdown hang.
62
- liveSockets = new Set();
63
- // Pre-auth sockets: connected but not yet authenticated (or failing auth). Tracked so
64
- // stop() can destroy them too; otherwise `server.close()` waits for the 10s auth
65
- // deadline to expire on each idle connection before SIGTERM completes.
66
- preAuthSockets = new Set();
67
- stopped = false;
68
- authTimeoutMs;
69
- loopbackOnly;
70
- constructor(opts = {}) {
71
- this.authTimeoutMs = opts.authTimeoutMs ?? 10_000;
72
- this.loopbackOnly = opts.loopbackOnly ?? false;
73
- }
74
- /**
75
- * Start listening on (host, port). Pass 0 for an ephemeral port; the actually-bound
76
- * port is available via `port()` and is what the in-VM agent must dial. Throws on
77
- * bind failure so the symphony bootstrap can surface the error early.
78
- */
79
- async start(host, port) {
80
- if (this.server)
81
- return;
82
- if (this.loopbackOnly && !isLoopbackHost(host)) {
83
- throw new Error(`acp bridge: loopbackOnly is set but host ${JSON.stringify(host)} is not a ` +
84
- `loopback address; refusing to bind a bearer-gated control channel wider than loopback`);
85
- }
86
- this.server = createServer((socket) => this.handleConnection(socket));
87
- await new Promise((resolve, reject) => {
88
- const onError = (err) => {
89
- this.server?.removeListener('listening', onListening);
90
- reject(err);
91
- };
92
- const onListening = () => {
93
- this.server?.removeListener('error', onError);
94
- resolve();
95
- };
96
- this.server.once('error', onError);
97
- this.server.once('listening', onListening);
98
- this.server.listen(port, host);
99
- });
100
- const addr = this.server.address();
101
- if (typeof addr === 'object' && addr !== null) {
102
- this.boundPort = addr.port;
103
- }
104
- // Re-attach a permanent error handler so post-bind runtime errors don't crash the
105
- // process — they land in the log and the listener stays up.
106
- this.server.on('error', (err) => log.warn('acp bridge runtime error', { error: err.message }));
107
- log.info('acp bridge listening', { host, port: this.boundPort });
108
- }
109
- /**
110
- * Actually-bound port (differs from the requested port when 0 was passed). Returns
111
- * `null` before `start()` has bound the listener.
112
- */
113
- port() {
114
- return this.boundPort;
115
- }
116
- /**
117
- * Register a pending dispatch. Returns a token to inject into the in-VM agent's env
118
- * and a promise that resolves when the agent connects + authenticates. The promise
119
- * rejects if `cancel()` is called or if `stop()` runs before the agent connects.
120
- */
121
- register(issueId, identifier) {
122
- if (this.stopped) {
123
- throw new Error('acp bridge is stopped');
124
- }
125
- if (!this.server) {
126
- throw new Error('acp bridge is not listening; call start() first');
127
- }
128
- const token = randomBytes(24).toString('base64url');
129
- let resolveFn;
130
- let rejectFn;
131
- const accepted = new Promise((res, rej) => {
132
- resolveFn = res;
133
- rejectFn = rej;
134
- });
135
- // Defensive: attach an internal noop catch so a `cancel()` that lands before the
136
- // caller has wired up its own handler can't escalate into an unhandled-rejection
137
- // crash. The caller's eventual `await accepted` / `.then()` still sees the rejection
138
- // via its own handler chain — this `.catch` is a separate promise consumer and only
139
- // exists to keep Node's unhandled-rejection tracking quiet.
140
- accepted.catch(() => undefined);
141
- const reg = {
142
- issueId,
143
- identifier,
144
- token,
145
- resolve: (s) => {
146
- if (reg.settled)
147
- return;
148
- reg.settled = true;
149
- this.pending.delete(token);
150
- resolveFn(s);
151
- },
152
- reject: (err) => {
153
- if (reg.settled)
154
- return;
155
- reg.settled = true;
156
- this.pending.delete(token);
157
- rejectFn(err);
158
- },
159
- settled: false,
160
- };
161
- this.pending.set(token, reg);
162
- log.debug('acp bridge registered', { issue_id: issueId, issue_identifier: identifier });
163
- return {
164
- token,
165
- accepted,
166
- cancel: (reason) => reg.reject(new Error(`acp bridge cancel: ${reason}`)),
167
- };
168
- }
169
- /**
170
- * Stop accepting new connections, reject all pending registrations, and force-close
171
- * any live sockets. We deliberately tear down live sockets rather than waiting on
172
- * them — `server.close()` blocks until every active connection ends, and an unhappy
173
- * adapter / abruptly-killed VM can leave sockets half-open and stall SIGTERM
174
- * indefinitely. The runner's cleanup path already destroys its end on attempt exit,
175
- * so the only time this matters is symphony shutdown while an attempt is mid-flight.
176
- */
177
- async stop() {
178
- if (this.stopped)
179
- return;
180
- this.stopped = true;
181
- for (const reg of [...this.pending.values()]) {
182
- reg.reject(new Error('acp bridge stopped'));
183
- }
184
- this.pending.clear();
185
- // Tear down BOTH pre-auth and live sockets. Pre-auth left to the 10s auth deadline
186
- // would delay SIGTERM by exactly that long for every idle client (e.g. a port scan
187
- // or a broken in-VM proxy that connects but never sends the bearer).
188
- for (const s of [...this.preAuthSockets, ...this.liveSockets]) {
189
- try {
190
- s.destroy();
191
- }
192
- catch {
193
- /* ignore */
194
- }
195
- }
196
- this.preAuthSockets.clear();
197
- this.liveSockets.clear();
198
- const srv = this.server;
199
- this.server = null;
200
- if (!srv)
201
- return;
202
- await new Promise((resolve) => srv.close(() => resolve()));
203
- log.info('acp bridge stopped');
204
- }
205
- /**
206
- * Per-connection handshake. We accumulate bytes on the socket until we see the first
207
- * newline, parse `Bearer <token>` from that line, validate against a pending
208
- * registration in constant time, and either hand the socket back to the awaiter
209
- * (consuming any post-newline bytes that arrived in the same packet) or close.
210
- */
211
- handleConnection(socket) {
212
- socket.setNoDelay(true);
213
- // Track every accepted connection (even pre-auth) so stop() can tear them down.
214
- // Removed from preAuthSockets either when fail() destroys them or when the bearer
215
- // handshake succeeds and they move into liveSockets.
216
- this.preAuthSockets.add(socket);
217
- socket.once('close', () => this.preAuthSockets.delete(socket));
218
- let buf = Buffer.alloc(0);
219
- let handled = false;
220
- const fail = (reason) => {
221
- if (handled)
222
- return;
223
- handled = true;
224
- log.warn('acp bridge rejected connection', {
225
- remote: `${socket.remoteAddress}:${socket.remotePort}`,
226
- reason,
227
- });
228
- // Destroy (not just end) the socket. A misbehaving peer that ignores our half-close
229
- // would otherwise keep the connection counted by `server.close()` and stall
230
- // symphony shutdown indefinitely.
231
- try {
232
- socket.destroy();
233
- }
234
- catch {
235
- /* ignore */
236
- }
237
- };
238
- // Authentication deadline: if the in-VM agent doesn't send the bearer line within
239
- // the configured window of connecting, the connection is almost certainly stuck or
240
- // malformed. The dispatch's own timeout would also catch this; this is just a tighter
241
- // guard.
242
- const authDeadline = setTimeout(() => fail('auth timeout'), this.authTimeoutMs);
243
- const onData = (chunk) => {
244
- buf = Buffer.concat([buf, chunk]);
245
- const nl = buf.indexOf(0x0a);
246
- if (nl === -1) {
247
- // Cap the unauthenticated buffer at 1 KiB so a misbehaving client can't grow
248
- // memory before failing auth.
249
- if (buf.length > 1024)
250
- fail('auth header too large');
251
- return;
252
- }
253
- const headerLine = buf.subarray(0, nl).toString('utf8').trim();
254
- const remainder = buf.subarray(nl + 1);
255
- clearTimeout(authDeadline);
256
- socket.removeListener('data', onData);
257
- const matched = this.authenticate(socket, headerLine, fail);
258
- if (!matched)
259
- return;
260
- handled = true;
261
- this.promoteToLive(socket, remainder);
262
- matched.resolve(socket);
263
- };
264
- socket.on('data', onData);
265
- socket.on('error', (err) => {
266
- // Connection errors before auth get swallowed; after auth the AcpClient owns the
267
- // socket and will see the error itself.
268
- if (!handled) {
269
- clearTimeout(authDeadline);
270
- fail(`socket error: ${err.message}`);
271
- }
272
- });
273
- }
274
- authenticate(socket, headerLine, fail) {
275
- const match = /^Bearer\s+(\S+)$/.exec(headerLine);
276
- if (!match) {
277
- fail('malformed bearer line');
278
- return null;
279
- }
280
- const matched = this.findPendingByToken(match[1]);
281
- if (!matched) {
282
- fail('invalid token');
283
- return null;
284
- }
285
- log.info('acp bridge accepted', {
286
- issue_id: matched.issueId,
287
- issue_identifier: matched.identifier,
288
- remote: `${socket.remoteAddress}:${socket.remotePort}`,
289
- });
290
- return matched;
291
- }
292
- // Constant-time compare against each pending token. With max_concurrent_agents = 1
293
- // there's typically ONE pending entry, but loop anyway for correctness.
294
- findPendingByToken(presented) {
295
- const presentedBuf = Buffer.from(presented, 'utf8');
296
- for (const reg of this.pending.values()) {
297
- const expected = Buffer.from(reg.token, 'utf8');
298
- if (expected.length !== presentedBuf.length)
299
- continue;
300
- if (timingSafeEqual(expected, presentedBuf))
301
- return reg;
302
- }
303
- return null;
304
- }
305
- // CRITICAL: the temporary `data` listener in handleConnection put the socket in flowing
306
- // mode. Removing that listener does NOT auto-pause it — any bytes already buffered in
307
- // the kernel or arriving immediately after will be re-emitted in flowing mode and
308
- // dropped if no listener is attached yet, which is exactly the window between
309
- // `resolve(socket)` and AcpClient's microtask wiring up `ndJsonStream`. Pause
310
- // explicitly so the next consumer is responsible for resuming. `unshift` puts the
311
- // residual bytes (bearer + ACP-frame in one packet case) back in front of any
312
- // subsequent kernel data.
313
- promoteToLive(socket, remainder) {
314
- socket.pause();
315
- if (remainder.length > 0)
316
- socket.unshift(remainder);
317
- // Move from pre-auth tracking into live tracking. Both sets are tied into stop()
318
- // for forced teardown on shutdown.
319
- this.preAuthSockets.delete(socket);
320
- this.liveSockets.add(socket);
321
- socket.once('close', () => this.liveSockets.delete(socket));
322
- }
323
- }
324
- //# sourceMappingURL=acp-bridge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"acp-bridge.js","sourceRoot":"","sources":["../src/acp-bridge.ts"],"names":[],"mappings":"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"}
@@ -1,191 +0,0 @@
1
- // Content-hash cache for run_in_vm actions (issue 36 AC4).
2
- //
3
- // Cache layout:
4
- // <cacheRoot>/actions/run_in_vm/<name>/<sha256(workspace_tree ⊕ cmd ⊕ env)>/result.json
5
- //
6
- // The name is the first path segment so `symphony rerun --check=<name>` can
7
- // drop an entire check's cache without recomputing the workspace-dependent
8
- // hash (which the CLI doesn't know — it has no per-issue workspace). The
9
- // per-execution path computes the hash against the actual per-issue
10
- // workspace; the CLI just removes the namespace dir, and the next execution
11
- // re-runs because no cached hash entry exists.
12
- //
13
- // `workspace_tree` hashes the live workspace contents the VM command will
14
- // see: every file `git ls-files --cached --others --exclude-standard` would
15
- // list (tracked, untracked-not-gitignored), read from the working tree (not
16
- // from the index), so an uncommitted edit to a tracked file or a newly
17
- // added untracked source file forces a cache miss. This is the whole point
18
- // of `run_in_vm`: CI-style checks against the user's project as it exists
19
- // in the per-issue workspace, post-agent-edits.
20
- import { createHash } from 'node:crypto';
21
- import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
22
- import path from 'node:path';
23
- import { actionCacheDir, defaultCacheRoot } from '../reconciler/cache.js';
24
- import { runProcess } from '../util/process.js';
25
- export const RUN_IN_VM_CACHE_KIND = 'run_in_vm';
26
- /**
27
- * Hash the cache key. Order-stable over env keys so two callers with the
28
- * same env (but different key insertion order) hit the same cache entry.
29
- */
30
- export async function computeCacheHash(key) {
31
- const h = createHash('sha256');
32
- const tree = await workspaceTreeHash(key.workspacePath);
33
- h.update('tree:');
34
- h.update(tree);
35
- h.update('\0cmd:');
36
- for (const a of key.cmd) {
37
- h.update(a);
38
- h.update('\0');
39
- }
40
- h.update('env:');
41
- const keys = Object.keys(key.env).sort();
42
- for (const k of keys) {
43
- h.update(k);
44
- h.update('=');
45
- h.update(key.env[k] ?? '');
46
- h.update('\0');
47
- }
48
- return h.digest('hex');
49
- }
50
- /**
51
- * Hash of the workspace's live, .gitignore-aware contents — what the VM
52
- * command will actually see. Uses `git ls-files --cached --others
53
- * --exclude-standard -z` to enumerate (cheap, handles .gitignore correctly),
54
- * then reads each file from the working tree (so uncommitted modifications
55
- * are captured). Tracked-but-deleted-in-worktree paths are folded in as a
56
- * deletion marker so removing a file still bumps the hash.
57
- *
58
- * Returns `'no-git'` when the workspace is not a git repo so the cache key
59
- * still computes; the cmd/env contribution still differentiates calls.
60
- */
61
- async function workspaceTreeHash(workspacePath) {
62
- const lsZ = await runGitCapture(['ls-files', '-z', '--cached', '--others', '--exclude-standard'], workspacePath);
63
- if (lsZ === null)
64
- return 'no-git';
65
- // -z output: NUL-separated paths, possibly with a trailing NUL.
66
- const paths = lsZ.split('\0').filter((p) => p.length > 0);
67
- // A path can appear in both --cached and --others (a freshly added but
68
- // git-add'd file may show up in both lists depending on git's index
69
- // state); dedupe and sort so the hash is order-stable.
70
- const unique = Array.from(new Set(paths)).sort();
71
- const h = createHash('sha256');
72
- for (const rel of unique) {
73
- await foldWorkspacePathIntoHash(h, workspacePath, rel);
74
- }
75
- return h.digest('hex');
76
- }
77
- // Read one workspace path and fold it (or its deletion / error marker) into
78
- // `h`. Extracted from `workspaceTreeHash` to keep that function under the
79
- // shell-statement budget; the per-path branching is the same as before.
80
- async function foldWorkspacePathIntoHash(h, workspacePath, rel) {
81
- const abs = path.join(workspacePath, rel);
82
- let buf;
83
- try {
84
- buf = await readFile(abs);
85
- }
86
- catch (err) {
87
- const code = err.code;
88
- // ENOENT: tracked file deleted in worktree; record deletion so the
89
- // cache key differs from "file still present." EISDIR / other errors:
90
- // record as an error marker so we don't silently bucket two
91
- // distinguishable states together.
92
- h.update(code === 'ENOENT' ? 'D\0' : 'E\0');
93
- h.update(rel);
94
- h.update('\0');
95
- return;
96
- }
97
- h.update('F\0');
98
- h.update(rel);
99
- h.update('\0');
100
- h.update(buf.length.toString(10));
101
- h.update('\0');
102
- h.update(buf);
103
- }
104
- // Thin shape adapter: workspace-tree hashing wants `stdout | null` (null on
105
- // any non-zero/error). The unified `runProcess` returns the full result; we
106
- // throw away stderr (workspace-tree probes are quiet on the happy path) and
107
- // project exit_code into the nullable signal. `maxBytes` is raised well above
108
- // the unified default because `ls-files` output for a non-trivial workspace
109
- // quickly exceeds 64 KiB; the cache hash MUST see the complete file list.
110
- async function runGitCapture(args, cwd) {
111
- const r = await runProcess('git', args, {
112
- cwd,
113
- maxBytes: 16 * 1024 * 1024,
114
- appendErrorToStderr: false,
115
- });
116
- return r.exit_code === 0 ? r.stdout : null;
117
- }
118
- /**
119
- * Sanitize an action name for use as a path segment. We validate at parse
120
- * time (see parseActionsBlock for run_in_vm) but defensively encode here so
121
- * a future relaxation of the parser rule can't silently make two names
122
- * collide on the filesystem.
123
- */
124
- function sanitizeNameSegment(name) {
125
- // Map every char outside [A-Za-z0-9._-] to `_<hex>`; the underscore prefix
126
- // guarantees the encoded form is unambiguous against a literal "_" because
127
- // a literal underscore would not be followed by two hex chars... actually
128
- // since `_` is in the safe set, `_AB` is a possible literal name. To keep
129
- // collision-freedom we use `%` as the escape sentinel (which is unsafe and
130
- // therefore always escaped if it appears literally).
131
- let out = '';
132
- for (const ch of name) {
133
- if (/[A-Za-z0-9._-]/.test(ch)) {
134
- out += ch;
135
- }
136
- else {
137
- out += '%' + ch.charCodeAt(0).toString(16).padStart(2, '0');
138
- }
139
- }
140
- return out.length > 0 ? out : '_';
141
- }
142
- function cacheNameDir(cacheRoot, name) {
143
- return path.join(actionCacheDir(cacheRoot, RUN_IN_VM_CACHE_KIND), sanitizeNameSegment(name));
144
- }
145
- function cacheEntryDir(cacheRoot, name, hash) {
146
- return path.join(cacheNameDir(cacheRoot, name), hash);
147
- }
148
- export async function readCache(cacheRoot, name, hash) {
149
- const dir = cacheEntryDir(cacheRoot, name, hash);
150
- try {
151
- const buf = await readFile(path.join(dir, 'result.json'), 'utf8');
152
- const parsed = JSON.parse(buf);
153
- if (typeof parsed.exit_code !== 'number')
154
- return null;
155
- return parsed;
156
- }
157
- catch {
158
- return null;
159
- }
160
- }
161
- export async function writeCache(cacheRoot, name, hash, result) {
162
- const dir = cacheEntryDir(cacheRoot, name, hash);
163
- await mkdir(dir, { recursive: true });
164
- await writeFile(path.join(dir, 'result.json'), JSON.stringify(result), 'utf8');
165
- }
166
- /**
167
- * Drop every cache entry for a named check. Used by `symphony rerun
168
- * --check=<name>`: the operator names the check, not the workspace, and the
169
- * orchestrator's next dispatch into the state hosting it re-executes
170
- * because the namespace directory is empty.
171
- *
172
- * Per-name (rather than per-hash) invalidation is the cache-layout fix for
173
- * the rerun CLI: the CLI has no per-issue workspace to hash against, so any
174
- * hash-based invalidation would key off the wrong workspace and miss the
175
- * entry the per-issue execution actually wrote. Dropping the namespace dir
176
- * sidesteps the problem entirely.
177
- */
178
- export async function invalidateCacheByName(cacheRoot, name) {
179
- const dir = cacheNameDir(cacheRoot, name);
180
- try {
181
- await rm(dir, { recursive: true, force: true });
182
- }
183
- catch {
184
- // Best-effort.
185
- }
186
- }
187
- /** Default cache root mirrors the bake resource. */
188
- export function runInVmCacheRoot() {
189
- return defaultCacheRoot();
190
- }
191
- //# sourceMappingURL=cache.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/actions/cache.ts"],"names":[],"mappings":"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"}
@@ -1,41 +0,0 @@
1
- // Effects-as-data foundation (issue 68): pure planner half of the
2
- // planActions/runEffects split. Effects are plain data describing IO so the
3
- // "decide what to do" half is unit-testable without spawning processes.
4
- // Issues 69/70 build on the `Effect` union.
5
- import { renderTree, TemplateError } from './templating.js';
6
- const DEFAULT_RETRY_COUNT = 3, DEFAULT_RETRY_BACKOFF_MS = 1_000;
7
- /** Pure planner — same inputs always produce the same `Effect[]`. */
8
- export function planActions(actions, ctx) {
9
- return actions.map((action, index) => {
10
- const snapshotKey = actionSnapshotKey(action, index);
11
- try {
12
- const rendered = renderTree(action, ctx);
13
- return {
14
- kind: 'run',
15
- snapshotKey,
16
- rendered,
17
- predicate: rendered.if ?? undefined,
18
- policy: effectivePolicy(action.on_error),
19
- };
20
- }
21
- catch (err) {
22
- const error = err instanceof TemplateError ? err.message : err.message;
23
- return { kind: 'render_failed', snapshotKey, error };
24
- }
25
- });
26
- }
27
- function effectivePolicy(p) {
28
- return {
29
- retry: {
30
- count: p?.retry?.count ?? DEFAULT_RETRY_COUNT,
31
- backoff_ms: p?.retry?.backoff_ms ?? DEFAULT_RETRY_BACKOFF_MS,
32
- },
33
- then: p?.then ?? 'abort',
34
- };
35
- }
36
- function actionSnapshotKey(action, idx) {
37
- if (action.name && action.name.length > 0)
38
- return `${action.kind}:${action.name}`;
39
- return `${action.kind}:#${idx}`;
40
- }
41
- //# sourceMappingURL=effects.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"effects.js","sourceRoot":"","sources":["../../src/actions/effects.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,4EAA4E;AAC5E,wEAAwE;AACxE,4CAA4C;AAG5C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE5D,MAAM,mBAAmB,GAAG,CAAC,EAAE,wBAAwB,GAAG,KAAK,CAAC;AAqBhE,qEAAqE;AACrE,MAAM,UAAU,WAAW,CAAC,OAAkC,EAAE,GAAkB;IAChF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QACnC,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACzC,OAAO;gBACL,IAAI,EAAE,KAAK;gBACX,WAAW;gBACX,QAAQ;gBACR,SAAS,EAAE,QAAQ,CAAC,EAAE,IAAI,SAAS;gBACnC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC;aACzC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,GAAG,YAAY,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAE,GAAa,CAAC,OAAO,CAAC;YAClF,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe,CAAC,CAAgC;IACvD,OAAO;QACL,KAAK,EAAE;YACL,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,mBAAmB;YAC7C,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,IAAI,wBAAwB;SAC7D;QACD,IAAI,EAAE,CAAC,EAAE,IAAI,IAAI,OAAO;KACzB,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAsB,EAAE,GAAW;IAC5D,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;IAClF,OAAO,GAAG,MAAM,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;AAClC,CAAC"}