smol-symphony 0.2.0 → 0.3.1

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 (540) 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/bin/symphony.js +22 -786
  11. package/dist/bin/symphony.js.map +1 -1
  12. package/dist/core/actions/context.js +109 -0
  13. package/dist/core/actions/context.js.map +1 -0
  14. package/dist/{actions/parsing.js → core/actions/parse.js} +33 -114
  15. package/dist/core/actions/parse.js.map +1 -0
  16. package/dist/core/actions/plan.js +197 -0
  17. package/dist/core/actions/plan.js.map +1 -0
  18. package/dist/core/actions/predicates.js +111 -0
  19. package/dist/core/actions/predicates.js.map +1 -0
  20. package/dist/core/actions/run-fold.js +248 -0
  21. package/dist/core/actions/run-fold.js.map +1 -0
  22. package/dist/core/actions/template.js +118 -0
  23. package/dist/core/actions/template.js.map +1 -0
  24. package/dist/core/cli/args.js +116 -0
  25. package/dist/core/cli/args.js.map +1 -0
  26. package/dist/core/coerce.js +75 -0
  27. package/dist/core/coerce.js.map +1 -0
  28. package/dist/core/credential/account-id.js +20 -0
  29. package/dist/core/credential/account-id.js.map +1 -0
  30. package/dist/core/credential/adapter-config.js +136 -0
  31. package/dist/core/credential/adapter-config.js.map +1 -0
  32. package/dist/core/credential/availability.js +98 -0
  33. package/dist/core/credential/availability.js.map +1 -0
  34. package/dist/core/credential/extract.js +228 -0
  35. package/dist/core/credential/extract.js.map +1 -0
  36. package/dist/core/credential/fake-creds.js +171 -0
  37. package/dist/core/credential/fake-creds.js.map +1 -0
  38. package/dist/core/credential/identity.js +125 -0
  39. package/dist/core/credential/identity.js.map +1 -0
  40. package/dist/core/credential/shape.js +230 -0
  41. package/dist/core/credential/shape.js.map +1 -0
  42. package/dist/core/credential/strings.js +15 -0
  43. package/dist/core/credential/strings.js.map +1 -0
  44. package/dist/core/doctor/checks.js +303 -0
  45. package/dist/core/doctor/checks.js.map +1 -0
  46. package/dist/core/git/result.js +107 -0
  47. package/dist/core/git/result.js.map +1 -0
  48. package/dist/core/http/decisions.js +225 -0
  49. package/dist/core/http/decisions.js.map +1 -0
  50. package/dist/{http.js → core/http/render.js} +472 -738
  51. package/dist/core/http/render.js.map +1 -0
  52. package/dist/{http-handlers.js → core/http/routes.js} +52 -87
  53. package/dist/core/http/routes.js.map +1 -0
  54. package/dist/core/http/views.js +181 -0
  55. package/dist/core/http/views.js.map +1 -0
  56. package/dist/core/image/managed-image.js +95 -0
  57. package/dist/core/image/managed-image.js.map +1 -0
  58. package/dist/core/issue/file.js +149 -0
  59. package/dist/core/issue/file.js.map +1 -0
  60. package/dist/core/issue/parse.js +210 -0
  61. package/dist/core/issue/parse.js.map +1 -0
  62. package/dist/core/mcp/dispatch.js +239 -0
  63. package/dist/core/mcp/dispatch.js.map +1 -0
  64. package/dist/core/mcp/post-move.js +92 -0
  65. package/dist/core/mcp/post-move.js.map +1 -0
  66. package/dist/core/mcp/protocol.js +293 -0
  67. package/dist/core/mcp/protocol.js.map +1 -0
  68. package/dist/core/mcp/url.js +162 -0
  69. package/dist/core/mcp/url.js.map +1 -0
  70. package/dist/core/path.js +63 -0
  71. package/dist/core/path.js.map +1 -0
  72. package/dist/core/reconcile/image-decide.js +48 -0
  73. package/dist/core/reconcile/image-decide.js.map +1 -0
  74. package/dist/core/reconcile/ledger.js +142 -0
  75. package/dist/core/reconcile/ledger.js.map +1 -0
  76. package/dist/core/reconcile/pr-classify.js +62 -0
  77. package/dist/core/reconcile/pr-classify.js.map +1 -0
  78. package/dist/{reconciler → core/reconcile}/pr-decide.js +25 -12
  79. package/dist/core/reconcile/pr-decide.js.map +1 -0
  80. package/dist/core/reconcile/pr-loop.js +161 -0
  81. package/dist/core/reconcile/pr-loop.js.map +1 -0
  82. package/dist/core/reconcile/pr-notes.js +35 -0
  83. package/dist/core/reconcile/pr-notes.js.map +1 -0
  84. package/dist/core/reconcile/vm-decide.js +70 -0
  85. package/dist/core/reconcile/vm-decide.js.map +1 -0
  86. package/dist/core/reconcile/vm-reap.js +207 -0
  87. package/dist/core/reconcile/vm-reap.js.map +1 -0
  88. package/dist/core/reconcile/workspace-decide.js +162 -0
  89. package/dist/core/reconcile/workspace-decide.js.map +1 -0
  90. package/dist/core/runlog/summary.js +231 -0
  91. package/dist/core/runlog/summary.js.map +1 -0
  92. package/dist/core/runner/dispatch-config.js +95 -0
  93. package/dist/core/runner/dispatch-config.js.map +1 -0
  94. package/dist/core/runner/injection.js +61 -0
  95. package/dist/core/runner/injection.js.map +1 -0
  96. package/dist/core/runner/mise.js +210 -0
  97. package/dist/core/runner/mise.js.map +1 -0
  98. package/dist/core/runner/prompt.js +720 -0
  99. package/dist/core/runner/prompt.js.map +1 -0
  100. package/dist/core/runner/turn.js +242 -0
  101. package/dist/core/runner/turn.js.map +1 -0
  102. package/dist/core/runner/vm-plan.js +390 -0
  103. package/dist/core/runner/vm-plan.js.map +1 -0
  104. package/dist/core/schedule/admission.js +123 -0
  105. package/dist/core/schedule/admission.js.map +1 -0
  106. package/dist/core/schedule/circuit-breaker.js +111 -0
  107. package/dist/core/schedule/circuit-breaker.js.map +1 -0
  108. package/dist/core/schedule/eligibility.js +83 -0
  109. package/dist/core/schedule/eligibility.js.map +1 -0
  110. package/dist/core/schedule/reconcile-issue.js +82 -0
  111. package/dist/core/schedule/reconcile-issue.js.map +1 -0
  112. package/dist/core/schedule/retry.js +96 -0
  113. package/dist/core/schedule/retry.js.map +1 -0
  114. package/dist/core/schedule/sleep-cycle.js +133 -0
  115. package/dist/core/schedule/sleep-cycle.js.map +1 -0
  116. package/dist/core/schedule/slots.js +124 -0
  117. package/dist/core/schedule/slots.js.map +1 -0
  118. package/dist/core/schedule/tick.js +553 -0
  119. package/dist/core/schedule/tick.js.map +1 -0
  120. package/dist/core/schedule/token-fold.js +181 -0
  121. package/dist/core/schedule/token-fold.js.map +1 -0
  122. package/dist/core/state-resolve.js +86 -0
  123. package/dist/core/state-resolve.js.map +1 -0
  124. package/dist/core/vm-guards.js +278 -0
  125. package/dist/core/vm-guards.js.map +1 -0
  126. package/dist/core/workflow/derive.js +107 -0
  127. package/dist/core/workflow/derive.js.map +1 -0
  128. package/dist/core/workflow/parse.js +687 -0
  129. package/dist/core/workflow/parse.js.map +1 -0
  130. package/dist/core/workflow/prompt-probe.js +78 -0
  131. package/dist/core/workflow/prompt-probe.js.map +1 -0
  132. package/dist/core/workflow/validate.js +189 -0
  133. package/dist/core/workflow/validate.js.map +1 -0
  134. package/dist/core/workspace-key.js +19 -0
  135. package/dist/core/workspace-key.js.map +1 -0
  136. package/dist/shell/actions-runner.js +356 -0
  137. package/dist/shell/actions-runner.js.map +1 -0
  138. package/dist/shell/adapter/adapter-registry.js +45 -0
  139. package/dist/shell/adapter/adapter-registry.js.map +1 -0
  140. package/dist/shell/adapter/clock-random.js +96 -0
  141. package/dist/shell/adapter/clock-random.js.map +1 -0
  142. package/dist/shell/adapter/gondolin-dispatch-helpers.js +158 -0
  143. package/dist/shell/adapter/gondolin-dispatch-helpers.js.map +1 -0
  144. package/dist/shell/adapter/gondolin-dispatch.js +385 -0
  145. package/dist/shell/adapter/gondolin-dispatch.js.map +1 -0
  146. package/dist/shell/adapter/gondolin-image-converter.js +233 -0
  147. package/dist/shell/adapter/gondolin-image-converter.js.map +1 -0
  148. package/dist/shell/adapter/gondolin-image-fetch.js +180 -0
  149. package/dist/shell/adapter/gondolin-image-fetch.js.map +1 -0
  150. package/dist/shell/adapter/launcher-asset.js +57 -0
  151. package/dist/shell/adapter/launcher-asset.js.map +1 -0
  152. package/dist/shell/adapter/mise-config-asset.js +65 -0
  153. package/dist/shell/adapter/mise-config-asset.js.map +1 -0
  154. package/dist/shell/adapter/workflow-loader.js +304 -0
  155. package/dist/shell/adapter/workflow-loader.js.map +1 -0
  156. package/dist/shell/cli/doctor.js +268 -0
  157. package/dist/shell/cli/doctor.js.map +1 -0
  158. package/dist/shell/effect-interpreter-families.js +314 -0
  159. package/dist/shell/effect-interpreter-families.js.map +1 -0
  160. package/dist/shell/effect-interpreter.js +29 -0
  161. package/dist/shell/effect-interpreter.js.map +1 -0
  162. package/dist/shell/interp/acp-frame.js +137 -0
  163. package/dist/shell/interp/acp-frame.js.map +1 -0
  164. package/dist/shell/interp/acp-ws-conn.js +320 -0
  165. package/dist/shell/interp/acp-ws-conn.js.map +1 -0
  166. package/dist/shell/interp/acp-ws-frames.js +159 -0
  167. package/dist/shell/interp/acp-ws-frames.js.map +1 -0
  168. package/dist/shell/interp/acp-ws.js +197 -0
  169. package/dist/shell/interp/acp-ws.js.map +1 -0
  170. package/dist/shell/interp/acp.js +319 -0
  171. package/dist/shell/interp/acp.js.map +1 -0
  172. package/dist/shell/interp/credential-defaults.js +128 -0
  173. package/dist/shell/interp/credential-defaults.js.map +1 -0
  174. package/dist/shell/interp/credential-hooks.js +149 -0
  175. package/dist/shell/interp/credential-hooks.js.map +1 -0
  176. package/dist/shell/interp/credential-registry.js +226 -0
  177. package/dist/shell/interp/credential-registry.js.map +1 -0
  178. package/dist/shell/interp/credential.js +103 -0
  179. package/dist/shell/interp/credential.js.map +1 -0
  180. package/dist/shell/interp/gh.js +163 -0
  181. package/dist/shell/interp/gh.js.map +1 -0
  182. package/dist/shell/interp/git.js +28 -0
  183. package/dist/shell/interp/git.js.map +1 -0
  184. package/dist/shell/interp/log.js +213 -0
  185. package/dist/shell/interp/log.js.map +1 -0
  186. package/dist/shell/interp/process.js +178 -0
  187. package/dist/shell/interp/process.js.map +1 -0
  188. package/dist/shell/interp/runlog.js +193 -0
  189. package/dist/shell/interp/runlog.js.map +1 -0
  190. package/dist/shell/interp/timer.js +64 -0
  191. package/dist/shell/interp/timer.js.map +1 -0
  192. package/dist/shell/interp/tracker-disk.js +99 -0
  193. package/dist/shell/interp/tracker-disk.js.map +1 -0
  194. package/dist/shell/interp/tracker-parse.js +71 -0
  195. package/dist/shell/interp/tracker-parse.js.map +1 -0
  196. package/dist/shell/interp/tracker-scan.js +238 -0
  197. package/dist/shell/interp/tracker-scan.js.map +1 -0
  198. package/dist/shell/interp/tracker-write.js +91 -0
  199. package/dist/shell/interp/tracker-write.js.map +1 -0
  200. package/dist/shell/interp/tracker.js +41 -0
  201. package/dist/shell/interp/tracker.js.map +1 -0
  202. package/dist/shell/interp/tty.js +48 -0
  203. package/dist/shell/interp/tty.js.map +1 -0
  204. package/dist/shell/interp/vm.js +199 -0
  205. package/dist/shell/interp/vm.js.map +1 -0
  206. package/dist/shell/interp/workspace.js +310 -0
  207. package/dist/shell/interp/workspace.js.map +1 -0
  208. package/dist/shell/main-acp.js +78 -0
  209. package/dist/shell/main-acp.js.map +1 -0
  210. package/dist/shell/main-adapters.js +222 -0
  211. package/dist/shell/main-adapters.js.map +1 -0
  212. package/dist/shell/main-credential.js +122 -0
  213. package/dist/shell/main-credential.js.map +1 -0
  214. package/dist/shell/main-doctor.js +22 -0
  215. package/dist/shell/main-doctor.js.map +1 -0
  216. package/dist/shell/main-entry.js +46 -0
  217. package/dist/shell/main-entry.js.map +1 -0
  218. package/dist/shell/main-http-csrf.js +45 -0
  219. package/dist/shell/main-http-csrf.js.map +1 -0
  220. package/dist/shell/main-http-handler.js +389 -0
  221. package/dist/shell/main-http-handler.js.map +1 -0
  222. package/dist/shell/main-http-mcp.js +122 -0
  223. package/dist/shell/main-http-mcp.js.map +1 -0
  224. package/dist/shell/main-http-views.js +253 -0
  225. package/dist/shell/main-http-views.js.map +1 -0
  226. package/dist/shell/main-http.js +76 -0
  227. package/dist/shell/main-http.js.map +1 -0
  228. package/dist/shell/main-loops.js +130 -0
  229. package/dist/shell/main-loops.js.map +1 -0
  230. package/dist/shell/main-mcp.js +129 -0
  231. package/dist/shell/main-mcp.js.map +1 -0
  232. package/dist/shell/main-orchestrator.js +120 -0
  233. package/dist/shell/main-orchestrator.js.map +1 -0
  234. package/dist/shell/main-preflight.js +43 -0
  235. package/dist/shell/main-preflight.js.map +1 -0
  236. package/dist/shell/main-reconcilers-helpers.js +244 -0
  237. package/dist/shell/main-reconcilers-helpers.js.map +1 -0
  238. package/dist/shell/main-reconcilers-pr.js +148 -0
  239. package/dist/shell/main-reconcilers-pr.js.map +1 -0
  240. package/dist/shell/main-reconcilers.js +225 -0
  241. package/dist/shell/main-reconcilers.js.map +1 -0
  242. package/dist/shell/main-runner.js +355 -0
  243. package/dist/shell/main-runner.js.map +1 -0
  244. package/dist/shell/main-scaffold.js +116 -0
  245. package/dist/shell/main-scaffold.js.map +1 -0
  246. package/dist/shell/main-shutdown.js +115 -0
  247. package/dist/shell/main-shutdown.js.map +1 -0
  248. package/dist/shell/main-startup.js +48 -0
  249. package/dist/shell/main-startup.js.map +1 -0
  250. package/dist/shell/main-substrates.js +43 -0
  251. package/dist/shell/main-substrates.js.map +1 -0
  252. package/dist/shell/main.js +385 -0
  253. package/dist/shell/main.js.map +1 -0
  254. package/dist/shell/orchestrator-feedback.js +69 -0
  255. package/dist/shell/orchestrator-feedback.js.map +1 -0
  256. package/dist/shell/orchestrator-image.js +167 -0
  257. package/dist/shell/orchestrator-image.js.map +1 -0
  258. package/dist/shell/orchestrator-loop.js +468 -0
  259. package/dist/shell/orchestrator-loop.js.map +1 -0
  260. package/dist/shell/orchestrator-reconcile.js +36 -0
  261. package/dist/shell/orchestrator-reconcile.js.map +1 -0
  262. package/dist/shell/reconciler-loop.js +228 -0
  263. package/dist/shell/reconciler-loop.js.map +1 -0
  264. package/dist/shell/runner-loop-turn.js +301 -0
  265. package/dist/shell/runner-loop-turn.js.map +1 -0
  266. package/dist/shell/runner-loop.js +338 -0
  267. package/dist/shell/runner-loop.js.map +1 -0
  268. package/dist/shell/server/http.js +208 -0
  269. package/dist/shell/server/http.js.map +1 -0
  270. package/dist/shell/server/mcp-runtime-effects.js +237 -0
  271. package/dist/shell/server/mcp-runtime-effects.js.map +1 -0
  272. package/dist/shell/server/mcp-runtime.js +99 -0
  273. package/dist/shell/server/mcp-runtime.js.map +1 -0
  274. package/dist/shell/workspace-key.js +14 -0
  275. package/dist/shell/workspace-key.js.map +1 -0
  276. package/dist/types/acp.js +8 -0
  277. package/dist/types/acp.js.map +1 -0
  278. package/dist/types/actions/plan.js +6 -0
  279. package/dist/types/actions/plan.js.map +1 -0
  280. package/dist/types/actions/predicates.js +6 -0
  281. package/dist/types/actions/predicates.js.map +1 -0
  282. package/dist/types/actions/run-fold.js +8 -0
  283. package/dist/types/actions/run-fold.js.map +1 -0
  284. package/dist/types/actions.js +7 -0
  285. package/dist/types/actions.js.map +1 -0
  286. package/dist/types/adapter/clock-random.js +4 -0
  287. package/dist/types/adapter/clock-random.js.map +1 -0
  288. package/dist/types/adapter/gondolin-image-converter.js +5 -0
  289. package/dist/types/adapter/gondolin-image-converter.js.map +1 -0
  290. package/dist/types/adapter/gondolin-image-fetch.js +5 -0
  291. package/dist/types/adapter/gondolin-image-fetch.js.map +1 -0
  292. package/dist/types/adapter/workflow-loader.js +4 -0
  293. package/dist/types/adapter/workflow-loader.js.map +1 -0
  294. package/dist/types/cli/args.js +8 -0
  295. package/dist/types/cli/args.js.map +1 -0
  296. package/dist/types/config.js +8 -0
  297. package/dist/types/config.js.map +1 -0
  298. package/dist/types/credential-interp.js +6 -0
  299. package/dist/types/credential-interp.js.map +1 -0
  300. package/dist/types/credentials.js +10 -0
  301. package/dist/types/credentials.js.map +1 -0
  302. package/dist/types/doctor.js +7 -0
  303. package/dist/types/doctor.js.map +1 -0
  304. package/dist/types/domain.js +7 -0
  305. package/dist/types/domain.js.map +1 -0
  306. package/dist/types/effect.js +15 -0
  307. package/dist/types/effect.js.map +1 -0
  308. package/dist/types/errors.js +39 -0
  309. package/dist/types/errors.js.map +1 -0
  310. package/dist/types/http/decisions.js +6 -0
  311. package/dist/types/http/decisions.js.map +1 -0
  312. package/dist/types/http/render.js +10 -0
  313. package/dist/types/http/render.js.map +1 -0
  314. package/dist/types/http/views.js +6 -0
  315. package/dist/types/http/views.js.map +1 -0
  316. package/dist/types/http.js +9 -0
  317. package/dist/types/http.js.map +1 -0
  318. package/dist/types/image/managed-image.js +7 -0
  319. package/dist/types/image/managed-image.js.map +1 -0
  320. package/dist/types/interp/effect-interpreter.js +8 -0
  321. package/dist/types/interp/effect-interpreter.js.map +1 -0
  322. package/dist/types/interp/tracker.js +7 -0
  323. package/dist/types/interp/tracker.js.map +1 -0
  324. package/dist/types/issue/file.js +6 -0
  325. package/dist/types/issue/file.js.map +1 -0
  326. package/dist/types/issue/parse.js +8 -0
  327. package/dist/types/issue/parse.js.map +1 -0
  328. package/dist/types/main-acp.js +13 -0
  329. package/dist/types/main-acp.js.map +1 -0
  330. package/dist/types/main-adapters.js +5 -0
  331. package/dist/types/main-adapters.js.map +1 -0
  332. package/dist/types/main-credential.js +21 -0
  333. package/dist/types/main-credential.js.map +1 -0
  334. package/dist/types/main-doctor.js +6 -0
  335. package/dist/types/main-doctor.js.map +1 -0
  336. package/dist/types/main-http-handler.js +12 -0
  337. package/dist/types/main-http-handler.js.map +1 -0
  338. package/dist/types/main-http.js +5 -0
  339. package/dist/types/main-http.js.map +1 -0
  340. package/dist/types/main-loops.js +5 -0
  341. package/dist/types/main-loops.js.map +1 -0
  342. package/dist/types/main-mcp.js +12 -0
  343. package/dist/types/main-mcp.js.map +1 -0
  344. package/dist/types/main-orchestrator.js +5 -0
  345. package/dist/types/main-orchestrator.js.map +1 -0
  346. package/dist/types/main-reconcilers.js +11 -0
  347. package/dist/types/main-reconcilers.js.map +1 -0
  348. package/dist/types/main-runner.js +13 -0
  349. package/dist/types/main-runner.js.map +1 -0
  350. package/dist/types/main-startup.js +5 -0
  351. package/dist/types/main-startup.js.map +1 -0
  352. package/dist/types/main-substrates.js +5 -0
  353. package/dist/types/main-substrates.js.map +1 -0
  354. package/dist/types/mcp/dispatch.js +4 -0
  355. package/dist/types/mcp/dispatch.js.map +1 -0
  356. package/dist/types/mcp/post-move.js +7 -0
  357. package/dist/types/mcp/post-move.js.map +1 -0
  358. package/dist/types/mcp.js +9 -0
  359. package/dist/types/mcp.js.map +1 -0
  360. package/dist/types/ports.js +12 -0
  361. package/dist/types/ports.js.map +1 -0
  362. package/dist/types/reconcile/image-decide.js +5 -0
  363. package/dist/types/reconcile/image-decide.js.map +1 -0
  364. package/dist/types/reconcile/ledger.js +7 -0
  365. package/dist/types/reconcile/ledger.js.map +1 -0
  366. package/dist/types/reconcile/pr-loop.js +8 -0
  367. package/dist/types/reconcile/pr-loop.js.map +1 -0
  368. package/dist/types/reconcile/vm-reap.js +8 -0
  369. package/dist/types/reconcile/vm-reap.js.map +1 -0
  370. package/dist/types/reconcile/workspace-decide.js +7 -0
  371. package/dist/types/reconcile/workspace-decide.js.map +1 -0
  372. package/dist/types/reconcile.js +9 -0
  373. package/dist/types/reconcile.js.map +1 -0
  374. package/dist/types/runlog.js +7 -0
  375. package/dist/types/runlog.js.map +1 -0
  376. package/dist/types/runner/actions-runner.js +12 -0
  377. package/dist/types/runner/actions-runner.js.map +1 -0
  378. package/dist/types/runner/gondolin-dispatch.js +5 -0
  379. package/dist/types/runner/gondolin-dispatch.js.map +1 -0
  380. package/dist/types/runner/injection.js +6 -0
  381. package/dist/types/runner/injection.js.map +1 -0
  382. package/dist/types/runner/runner-loop.js +5 -0
  383. package/dist/types/runner/runner-loop.js.map +1 -0
  384. package/dist/types/runner/turn.js +4 -0
  385. package/dist/types/runner/turn.js.map +1 -0
  386. package/dist/types/runner/vm-plan.js +4 -0
  387. package/dist/types/runner/vm-plan.js.map +1 -0
  388. package/dist/types/runtime.js +9 -0
  389. package/dist/types/runtime.js.map +1 -0
  390. package/dist/types/schedule/admission.js +7 -0
  391. package/dist/types/schedule/admission.js.map +1 -0
  392. package/dist/types/schedule/circuit-breaker.js +2 -0
  393. package/dist/types/schedule/circuit-breaker.js.map +1 -0
  394. package/dist/types/schedule/eligibility.js +9 -0
  395. package/dist/types/schedule/eligibility.js.map +1 -0
  396. package/dist/types/schedule/orchestrator-loop.js +10 -0
  397. package/dist/types/schedule/orchestrator-loop.js.map +1 -0
  398. package/dist/types/schedule/sleep-cycle.js +4 -0
  399. package/dist/types/schedule/sleep-cycle.js.map +1 -0
  400. package/dist/types/schedule/slots.js +8 -0
  401. package/dist/types/schedule/slots.js.map +1 -0
  402. package/dist/types/schedule/tick.js +9 -0
  403. package/dist/types/schedule/tick.js.map +1 -0
  404. package/dist/types/server/mcp-runtime.js +8 -0
  405. package/dist/types/server/mcp-runtime.js.map +1 -0
  406. package/dist/types/workflow/parse.js +4 -0
  407. package/dist/types/workflow/parse.js.map +1 -0
  408. package/package.json +22 -10
  409. package/patches/@earendil-works+gondolin+0.12.0.patch +173 -0
  410. package/prompts/Reflect.md +91 -0
  411. package/prompts/Review.md +97 -0
  412. package/prompts/Todo.md +96 -0
  413. package/prompts/_footer.md +41 -0
  414. package/prompts/_preamble.md +42 -0
  415. package/prompts-minimal/Todo.md +26 -0
  416. package/scripts/postinstall.mjs +63 -0
  417. package/scripts/vm-agent.mjs +312 -90
  418. package/WORKFLOW.md +0 -744
  419. package/dist/acp-bridge.js +0 -324
  420. package/dist/acp-bridge.js.map +0 -1
  421. package/dist/actions/cache.js +0 -191
  422. package/dist/actions/cache.js.map +0 -1
  423. package/dist/actions/effects.js +0 -41
  424. package/dist/actions/effects.js.map +0 -1
  425. package/dist/actions/executor.js +0 -570
  426. package/dist/actions/executor.js.map +0 -1
  427. package/dist/actions/index.js +0 -13
  428. package/dist/actions/index.js.map +0 -1
  429. package/dist/actions/parsing.js.map +0 -1
  430. package/dist/actions/predicate-env.js +0 -27
  431. package/dist/actions/predicate-env.js.map +0 -1
  432. package/dist/actions/predicates.js +0 -49
  433. package/dist/actions/predicates.js.map +0 -1
  434. package/dist/actions/templating.js +0 -66
  435. package/dist/actions/templating.js.map +0 -1
  436. package/dist/actions/types.js +0 -15
  437. package/dist/actions/types.js.map +0 -1
  438. package/dist/agent/acp.js +0 -473
  439. package/dist/agent/acp.js.map +0 -1
  440. package/dist/agent/adapter-names.js +0 -159
  441. package/dist/agent/adapter-names.js.map +0 -1
  442. package/dist/agent/adapters.js +0 -511
  443. package/dist/agent/adapters.js.map +0 -1
  444. package/dist/agent/credential-extractors.js +0 -342
  445. package/dist/agent/credential-extractors.js.map +0 -1
  446. package/dist/agent/credential-secrets.js +0 -628
  447. package/dist/agent/credential-secrets.js.map +0 -1
  448. package/dist/agent/credential-ticker.js +0 -57
  449. package/dist/agent/credential-ticker.js.map +0 -1
  450. package/dist/agent/gondolin-creds-staging.js +0 -356
  451. package/dist/agent/gondolin-creds-staging.js.map +0 -1
  452. package/dist/agent/gondolin-dispatch.js +0 -375
  453. package/dist/agent/gondolin-dispatch.js.map +0 -1
  454. package/dist/agent/gondolin.js +0 -124
  455. package/dist/agent/gondolin.js.map +0 -1
  456. package/dist/agent/runner-decisions.js +0 -134
  457. package/dist/agent/runner-decisions.js.map +0 -1
  458. package/dist/agent/runner.js +0 -1456
  459. package/dist/agent/runner.js.map +0 -1
  460. package/dist/agent/tool-call-summary.js +0 -102
  461. package/dist/agent/tool-call-summary.js.map +0 -1
  462. package/dist/agent/vm-acp-mapping.js +0 -73
  463. package/dist/agent/vm-acp-mapping.js.map +0 -1
  464. package/dist/agent/vm-guards.js +0 -262
  465. package/dist/agent/vm-guards.js.map +0 -1
  466. package/dist/agent/vm-port.js +0 -22
  467. package/dist/agent/vm-port.js.map +0 -1
  468. package/dist/agent/vm-process-registry.js +0 -79
  469. package/dist/agent/vm-process-registry.js.map +0 -1
  470. package/dist/bin/cli-args.js +0 -105
  471. package/dist/bin/cli-args.js.map +0 -1
  472. package/dist/errors.js +0 -15
  473. package/dist/errors.js.map +0 -1
  474. package/dist/http-disk.js +0 -135
  475. package/dist/http-disk.js.map +0 -1
  476. package/dist/http-handlers.js.map +0 -1
  477. package/dist/http.js.map +0 -1
  478. package/dist/issues.js +0 -178
  479. package/dist/issues.js.map +0 -1
  480. package/dist/logging.js +0 -203
  481. package/dist/logging.js.map +0 -1
  482. package/dist/mcp.js +0 -706
  483. package/dist/mcp.js.map +0 -1
  484. package/dist/memory.js +0 -85
  485. package/dist/memory.js.map +0 -1
  486. package/dist/orchestrator-decisions.js +0 -331
  487. package/dist/orchestrator-decisions.js.map +0 -1
  488. package/dist/orchestrator.js +0 -1569
  489. package/dist/orchestrator.js.map +0 -1
  490. package/dist/prompt.js +0 -65
  491. package/dist/prompt.js.map +0 -1
  492. package/dist/reconciler/cache.js +0 -65
  493. package/dist/reconciler/cache.js.map +0 -1
  494. package/dist/reconciler/index.js +0 -448
  495. package/dist/reconciler/index.js.map +0 -1
  496. package/dist/reconciler/ledger.js +0 -131
  497. package/dist/reconciler/ledger.js.map +0 -1
  498. package/dist/reconciler/pr-adapters.js +0 -174
  499. package/dist/reconciler/pr-adapters.js.map +0 -1
  500. package/dist/reconciler/pr-decide.js.map +0 -1
  501. package/dist/reconciler/pr.js +0 -422
  502. package/dist/reconciler/pr.js.map +0 -1
  503. package/dist/reconciler/types.js +0 -12
  504. package/dist/reconciler/types.js.map +0 -1
  505. package/dist/reconciler/vm.js +0 -243
  506. package/dist/reconciler/vm.js.map +0 -1
  507. package/dist/reconciler/workspace-defaults.js +0 -83
  508. package/dist/reconciler/workspace-defaults.js.map +0 -1
  509. package/dist/reconciler/workspace.js +0 -272
  510. package/dist/reconciler/workspace.js.map +0 -1
  511. package/dist/runlog.js +0 -403
  512. package/dist/runlog.js.map +0 -1
  513. package/dist/scaffold.js +0 -165
  514. package/dist/scaffold.js.map +0 -1
  515. package/dist/trackers/local.js +0 -445
  516. package/dist/trackers/local.js.map +0 -1
  517. package/dist/trackers/types.js +0 -10
  518. package/dist/trackers/types.js.map +0 -1
  519. package/dist/types.js +0 -3
  520. package/dist/types.js.map +0 -1
  521. package/dist/util/clock.js +0 -12
  522. package/dist/util/clock.js.map +0 -1
  523. package/dist/util/crypto.js +0 -25
  524. package/dist/util/crypto.js.map +0 -1
  525. package/dist/util/frontmatter.js +0 -70
  526. package/dist/util/frontmatter.js.map +0 -1
  527. package/dist/util/fs-issues.js +0 -22
  528. package/dist/util/fs-issues.js.map +0 -1
  529. package/dist/util/process.js +0 -152
  530. package/dist/util/process.js.map +0 -1
  531. package/dist/util/workspace-key.js +0 -10
  532. package/dist/util/workspace-key.js.map +0 -1
  533. package/dist/workflow-loader.js +0 -147
  534. package/dist/workflow-loader.js.map +0 -1
  535. package/dist/workflow.js +0 -822
  536. package/dist/workflow.js.map +0 -1
  537. package/dist/workspace-types.js +0 -8
  538. package/dist/workspace-types.js.map +0 -1
  539. package/dist/workspace.js +0 -443
  540. package/dist/workspace.js.map +0 -1
@@ -1,27 +0,0 @@
1
- // Production-side adapter for `PredicateEnv` (issue 96). Backs
2
- // `branchExists` with `git rev-parse --verify --quiet` (via the unified
3
- // `runProcess`) and `pathExists` with `fs.stat`. Lives outside the
4
- // functional-core lint group so `src/actions/predicates.ts` can stay free of
5
- // `node:fs/promises` and `runProcess` direct imports; the runner threads
6
- // `defaultPredicateEnv` into the executor.
7
- import { stat } from 'node:fs/promises';
8
- import { runProcess } from '../util/process.js';
9
- export const defaultPredicateEnv = {
10
- async branchExists(ref, workspacePath) {
11
- // `--verify --quiet` keeps both streams empty on the happy path; the
12
- // tiny default byte clamp is plenty and `appendErrorToStderr: false`
13
- // skips stderr decoration for the missing-ref case (the common one).
14
- const r = await runProcess('git', ['rev-parse', '--verify', '--quiet', `refs/heads/${ref}`], { cwd: workspacePath, appendErrorToStderr: false });
15
- return r.exit_code === 0;
16
- },
17
- async pathExists(absPath) {
18
- try {
19
- const st = await stat(absPath);
20
- return st.isFile() || st.isDirectory();
21
- }
22
- catch {
23
- return false;
24
- }
25
- },
26
- };
27
- //# sourceMappingURL=predicate-env.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"predicate-env.js","sourceRoot":"","sources":["../../src/actions/predicate-env.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,wEAAwE;AACxE,mEAAmE;AACnE,6EAA6E;AAC7E,yEAAyE;AACzE,2CAA2C;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,MAAM,CAAC,MAAM,mBAAmB,GAAiB;IAC/C,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,aAAa;QACnC,qEAAqE;QACrE,qEAAqE;QACrE,qEAAqE;QACrE,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,KAAK,EACL,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,GAAG,EAAE,CAAC,EACzD,EAAE,GAAG,EAAE,aAAa,EAAE,mBAAmB,EAAE,KAAK,EAAE,CACnD,CAAC;QACF,OAAO,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,OAAO;QACtB,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -1,49 +0,0 @@
1
- // `if:` predicate evaluation (issue 36).
2
- //
3
- // Three predicate shapes — env-var-truthy, branch-exists, file-present.
4
- // These are the three predicates the hook shell actually exercised across
5
- // today's WORKFLOW.md; the issue body explicitly caps the predicate set
6
- // there: "If users want more, they've outgrown declarative."
7
- import path from 'node:path';
8
- import { renderTemplate } from './templating.js';
9
- /**
10
- * Evaluate a predicate against the context. `null`/undefined → always true
11
- * (no `if:` → run unconditionally).
12
- *
13
- * The string form `"$var"` is a truthiness check on the named context field
14
- * — `if: $repo` matches the issue body's example exactly. A bare literal
15
- * (`if: yes`) is treated as truthy; the literal-false case `if: ""` /
16
- * `if: null` falls through to "always".
17
- *
18
- * IO is routed through the injected `PredicateEnv` so the evaluator core
19
- * stays pure (no `node:fs` / `runProcess` imports). String-truthy predicates
20
- * never touch the env.
21
- */
22
- export async function evaluatePredicate(predicate, ctx, workspacePath, env) {
23
- if (predicate === null || predicate === undefined)
24
- return true;
25
- if (typeof predicate === 'string') {
26
- // Templates may already have been rendered by the executor's renderTree
27
- // pass before evaluatePredicate is called; re-rendering an already-rendered
28
- // empty string is a no-op. An empty (or empty-after-render) predicate is
29
- // explicitly false — the intent of `if: $repo` with $repo unset is "skip
30
- // when the var is empty," not "always run".
31
- const expanded = renderTemplate(predicate, ctx).trim();
32
- return expanded.length > 0;
33
- }
34
- if ('branch_exists' in predicate) {
35
- const ref = renderTemplate(predicate.branch_exists, ctx).trim();
36
- if (ref.length === 0)
37
- return false;
38
- return env.branchExists(ref, workspacePath);
39
- }
40
- if ('file_present' in predicate) {
41
- const file = renderTemplate(predicate.file_present, ctx).trim();
42
- if (file.length === 0)
43
- return false;
44
- const abs = path.isAbsolute(file) ? file : path.join(workspacePath, file);
45
- return env.pathExists(abs);
46
- }
47
- return false;
48
- }
49
- //# sourceMappingURL=predicates.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"predicates.js","sourceRoot":"","sources":["../../src/actions/predicates.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,wEAAwE;AACxE,0EAA0E;AAC1E,wEAAwE;AACxE,6DAA6D;AAE7D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAsC,EACtC,GAAkB,EAClB,aAAqB,EACrB,GAAiB;IAEjB,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC/D,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,wEAAwE;QACxE,4EAA4E;QAC5E,yEAAyE;QACzE,yEAAyE;QACzE,4CAA4C;QAC5C,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,eAAe,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,cAAc,CAAC,SAAS,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAChE,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACnC,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,cAAc,IAAI,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,cAAc,CAAC,SAAS,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAChE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC1E,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,66 +0,0 @@
1
- // $-substitution against a fixed ActionContext namespace (issue 36).
2
- //
3
- // The issue body explicitly says "Mustache or `$`-substitution with a fixed
4
- // namespace is enough; Bazel's Starlark is overkill." We pick `$identifier`
5
- // over `${{ identifier }}` because the Done after_run already uses
6
- // `$SYMPHONY_*` env-var shapes; sharing the prefix style keeps cognitive
7
- // load low for operators converting hooks to actions.
8
- //
9
- // Behavior:
10
- // • `$name` expands to the named ActionContext field.
11
- // • An unknown `$name` throws — silent expansion to "" was the most common
12
- // hook-shell bug (typo'd `$SYMPHONY_PR_TITTLE` is the canonical example).
13
- // • Literal `$` followed by a non-word char is left alone.
14
- // • `\$name` escapes the substitution (literal `$name` in output).
15
- export class TemplateError extends Error {
16
- name;
17
- constructor(name, message) {
18
- super(message);
19
- this.name = name;
20
- this.name = 'TemplateError';
21
- }
22
- }
23
- const VAR_RE = /(\\?)\$([A-Za-z_][A-Za-z0-9_]*)/g;
24
- /**
25
- * Substitute `$name` references in `template` against the ActionContext.
26
- * Throws TemplateError when a referenced variable is not in the fixed
27
- * namespace — silent "" expansion masks operator typos.
28
- */
29
- export function renderTemplate(template, ctx) {
30
- if (typeof template !== 'string') {
31
- return template;
32
- }
33
- return template.replace(VAR_RE, (full, escape, name) => {
34
- if (escape === '\\')
35
- return `$${name}`;
36
- if (!(name in ctx)) {
37
- throw new TemplateError(name, `unknown template variable "$${name}" (available: ${Object.keys(ctx).join(', ')})`);
38
- }
39
- const v = ctx[name];
40
- if (v === null || v === undefined)
41
- return '';
42
- return String(v);
43
- });
44
- }
45
- /**
46
- * Render every string field of an object. Used to render an action's
47
- * declarative fields in one pass before execution. Recurses into arrays and
48
- * plain objects; primitives other than strings (numbers, booleans) pass
49
- * through unchanged.
50
- */
51
- export function renderTree(value, ctx) {
52
- if (typeof value === 'string')
53
- return renderTemplate(value, ctx);
54
- if (Array.isArray(value)) {
55
- return value.map((v) => renderTree(v, ctx));
56
- }
57
- if (value && typeof value === 'object') {
58
- const out = {};
59
- for (const [k, v] of Object.entries(value)) {
60
- out[k] = renderTree(v, ctx);
61
- }
62
- return out;
63
- }
64
- return value;
65
- }
66
- //# sourceMappingURL=templating.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"templating.js","sourceRoot":"","sources":["../../src/actions/templating.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,mEAAmE;AACnE,yEAAyE;AACzE,sDAAsD;AACtD,EAAE;AACF,YAAY;AACZ,wDAAwD;AACxD,6EAA6E;AAC7E,8EAA8E;AAC9E,6DAA6D;AAC7D,qEAAqE;AAIrE,MAAM,OAAO,aAAc,SAAQ,KAAK;IACnB;IAAnB,YAAmB,IAAY,EAAE,OAAe;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QADE,SAAI,GAAJ,IAAI,CAAQ;QAE7B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,MAAM,GAAG,kCAAkC,CAAC;AAElD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,GAAkB;IACjE,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAc,EAAE,IAAY,EAAE,EAAE;QACrE,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,IAAI,aAAa,CACrB,IAAI,EACJ,+BAA+B,IAAI,iBAAiB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACnF,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,GAAI,GAA0C,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAI,KAAQ,EAAE,GAAkB;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,cAAc,CAAC,KAAK,EAAE,GAAG,CAAiB,CAAC;IACjF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAiB,CAAC;IAC9D,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,GAAmB,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,15 +0,0 @@
1
- // Workflow action records (issue 36 — reconciler v2 / typed action DAG).
2
- //
3
- // Per-state `actions:` blocks declare a closed set of typed records that the
4
- // orchestrator runs in place of an `after_run` shell hook. Each kind has a
5
- // fixed schema; the only escape hatch for arbitrary commands is `run_in_vm`,
6
- // which is content-hash cached so identical (workspace, cmd, env) tuples
7
- // don't re-execute.
8
- //
9
- // Action records are pure data; the executor (src/actions/executor.ts) is the
10
- // one place that knows how to apply each kind. This mirrors the reconciler's
11
- // `KillSessionAction`/`RemoveWorkspaceAction`/… pattern (records, not closures)
12
- // so the snapshot machinery and the dashboard can render per-action state
13
- // uniformly across both surfaces.
14
- export {};
15
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/actions/types.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,oBAAoB;AACpB,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,gFAAgF;AAChF,0EAA0E;AAC1E,kCAAkC"}
package/dist/agent/acp.js DELETED
@@ -1,473 +0,0 @@
1
- // ACP (Agent Client Protocol) client wrapper.
2
- //
3
- // Symphony talks the Zed Agent Client Protocol (https://agentclientprotocol.com) so that a
4
- // single integration covers Claude (`claude-agent-acp`), Codex (`codex-acp`), and OpenCode
5
- // (`opencode acp`). The protocol is JSON-RPC over stdio.
6
- //
7
- // This wrapper:
8
- // * spawns the adapter command inside a Gondolin VM (the child process stdio is the
9
- // transport; nothing local is mutated outside the VM)
10
- // * bridges Node child process stdio to the SDK's WHATWG streams via Readable.toWeb / etc.
11
- // * implements the small Client surface ACP requires — session update streaming,
12
- // permission requests, and (optionally) fs/terminal — under a documented high-trust
13
- // posture (auto-approve, no client-side fs writes; the agent uses its own tools in-VM)
14
- // * exposes high-level methods `initSession()` and `runPrompt()` that the agent runner
15
- // uses to drive one turn of work.
16
- import { Readable, Transform, Writable } from 'node:stream';
17
- import { StringDecoder } from 'node:string_decoder';
18
- import { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION, } from '@agentclientprotocol/sdk';
19
- import { log } from '../logging.js';
20
- import { summarizeToolCall, summarizeToolCallUpdate } from './tool-call-summary.js';
21
- // Connection/transport faults surfaced by the in-VM agent's model API client
22
- // (ECONNRESET, socket hang up, "Unable to connect to API", …) arrive as a thrown
23
- // JSON-RPC error on session/prompt — they are NOT an agent decision. Issue 135:
24
- // bucketing these as `refusal` made a transient upstream stream reset look like
25
- // the agent giving up, so a long turn's transport blip got scored an "agent turn
26
- // refusal", the whole run was discarded, and the issue re-dispatched in a loop.
27
- // Label them distinctly (`transport_error`) so the run log / summary stay honest
28
- // and downstream policy can treat them as transient rather than as a refusal.
29
- const TRANSPORT_ERROR_RE = /econnreset|etimedout|econnrefused|epipe|enetunreach|ehostunreach|eai_again|socket hang up|unable to connect to api|fetch failed|network error|other side closed|terminated/i;
30
- /** True when a thrown prompt error is a connection/transport fault, not an agent decision. */
31
- export function isTransportError(message) {
32
- return TRANSPORT_ERROR_RE.test(message);
33
- }
34
- export class AcpProtocolError extends Error {
35
- code;
36
- constructor(code, message) {
37
- super(message);
38
- this.code = code;
39
- this.name = 'AcpProtocolError';
40
- }
41
- }
42
- function nowIso() {
43
- return new Date().toISOString();
44
- }
45
- function summarize(raw, max = 240) {
46
- let s;
47
- try {
48
- s = typeof raw === 'string' ? raw : JSON.stringify(raw);
49
- }
50
- catch {
51
- s = String(raw);
52
- }
53
- if (s.length > max)
54
- s = s.slice(0, max) + '…';
55
- return s;
56
- }
57
- function extractTextContent(content) {
58
- if (!content || typeof content !== 'object')
59
- return '';
60
- const c = content;
61
- if (c.type === 'text' && typeof c.text === 'string')
62
- return c.text;
63
- return '';
64
- }
65
- function truncate(s, max) {
66
- return s.length > max ? s.slice(0, max) + '…' : s;
67
- }
68
- // Transform that hands each newline-delimited frame to `onLine` while passing the raw bytes
69
- // through to downstream consumers unchanged. Used to tap the ACP JSON-RPC transport in both
70
- // directions for the per-issue JSONL run log. Buffering lives in `_transform`; a final flush
71
- // surfaces any unterminated tail so a clean shutdown does not drop the last frame.
72
- //
73
- // We decode with a `StringDecoder` rather than `chunk.toString('utf8')` because TCP/stdout
74
- // chunk boundaries can fall inside a multibyte UTF-8 sequence: an unaccompanied byte split
75
- // would otherwise be silently converted to a U+FFFD replacement character, corrupting the
76
- // JSON we record. The decoder buffers the trailing incomplete sequence and returns it
77
- // prepended to the next chunk's decode.
78
- class LineTap extends Transform {
79
- onLine;
80
- buf = '';
81
- decoder = new StringDecoder('utf8');
82
- constructor(onLine) {
83
- super();
84
- this.onLine = onLine;
85
- }
86
- _transform(chunk, _enc, cb) {
87
- this.buf +=
88
- typeof chunk === 'string' ? chunk : this.decoder.write(chunk);
89
- let idx;
90
- while ((idx = this.buf.indexOf('\n')) !== -1) {
91
- const line = this.buf.slice(0, idx);
92
- this.buf = this.buf.slice(idx + 1);
93
- if (line.length > 0) {
94
- try {
95
- this.onLine(line);
96
- }
97
- catch {
98
- // Tap failures must not interrupt the stream.
99
- }
100
- }
101
- }
102
- cb(null, chunk);
103
- }
104
- _flush(cb) {
105
- // Drain any incomplete multibyte sequence the decoder is still holding, then surface
106
- // an unterminated tail so a clean shutdown does not drop the last frame.
107
- const tail = this.decoder.end();
108
- if (tail.length > 0)
109
- this.buf += tail;
110
- if (this.buf.length > 0) {
111
- try {
112
- this.onLine(this.buf);
113
- }
114
- catch {
115
- /* see _transform */
116
- }
117
- this.buf = '';
118
- }
119
- cb();
120
- }
121
- }
122
- // Record one ACP frame to the run log. Lines that parse as JSON are stored as parsed values
123
- // so the evaluator agent doesn't double-decode; anything else lands as `kind: "unparseable"`
124
- // with the raw bytes preserved for forensic value.
125
- //
126
- // Before recording, capability tokens in the frame are redacted in place. The most common
127
- // one is the symphony MCP bearer that we inject into `session/new` as
128
- // `mcpServers[].headers[].value = "Bearer <token>"`; that token is the agent's per-issue
129
- // capability to call `transition` and `request_human_steering`, and the JSONL log is a
130
- // long-lived append-only file an evaluator may share. Replace any string starting with
131
- // `Bearer ` with `Bearer <redacted>` anywhere in the tree.
132
- function recordAcpFrame(runLog, direction, line) {
133
- if (!runLog)
134
- return;
135
- let frame;
136
- try {
137
- frame = JSON.parse(line);
138
- }
139
- catch {
140
- runLog.record({ channel: 'acp', direction, kind: 'unparseable', raw: line });
141
- return;
142
- }
143
- redactBearerTokens(frame);
144
- runLog.record({ channel: 'acp', direction, frame });
145
- }
146
- // Walk `value` and replace any string of the form `Bearer <token>` with `Bearer <redacted>`.
147
- // Mutates in place; callers must pass the parsed JSON (which is single-use per call). We
148
- // walk arrays and plain objects only; primitives other than strings are skipped, and we
149
- // guard against cycles even though ACP frames shouldn't contain them.
150
- function redactBearerTokens(value, seen = new WeakSet()) {
151
- if (!value || typeof value !== 'object')
152
- return;
153
- if (seen.has(value))
154
- return;
155
- seen.add(value);
156
- if (Array.isArray(value)) {
157
- for (let i = 0; i < value.length; i++) {
158
- const redacted = redactedBearer(value[i]);
159
- if (redacted !== undefined)
160
- value[i] = redacted;
161
- else
162
- redactBearerTokens(value[i], seen);
163
- }
164
- return;
165
- }
166
- const obj = value;
167
- for (const k of Object.keys(obj)) {
168
- const redacted = redactedBearer(obj[k]);
169
- if (redacted !== undefined)
170
- obj[k] = redacted;
171
- else
172
- redactBearerTokens(obj[k], seen);
173
- }
174
- }
175
- // Returns the redacted replacement when `v` is a Bearer-token string, else
176
- // undefined. Non-string and non-Bearer values fall through so the caller can
177
- // decide whether to recurse.
178
- function redactedBearer(v) {
179
- if (typeof v === 'string' && v.startsWith('Bearer '))
180
- return 'Bearer <redacted>';
181
- return undefined;
182
- }
183
- // One open ACP session bound to a single child process. The adapter is expected to be
184
- // already running; `initSession()` performs `initialize` + `session/new`, and `runPrompt()`
185
- // drives one prompt-to-stop_reason cycle.
186
- export class AcpClient {
187
- opts;
188
- conn;
189
- sessionId = null;
190
- closed = false;
191
- cancelled = false;
192
- // Tracked here so the runner snapshot picks up the most recent assistant text per turn.
193
- lastAssistantText = '';
194
- // Held so `handleTransportClose` can forcibly end the SDK's reader/writer streams.
195
- // Without this, destroying the underlying socket emits `'close'` on `opts.stdout` but
196
- // the LineTap Transform in between may not propagate end-of-stream synchronously to
197
- // `Readable.toWeb`'s output. The SDK's `receive()` reader loop then stays parked in
198
- // `reader.read()`, so the in-flight `session/prompt` promise never rejects and the
199
- // runner hangs in `runPrompt()` despite the socket being gone.
200
- inboundTap;
201
- outboundTap;
202
- constructor(opts) {
203
- this.opts = opts;
204
- // Bridge child stdio to the WHATWG streams the SDK speaks. `ndJsonStream` expects raw
205
- // bytes; we use Readable.toWeb / Writable.toWeb for the conversion. Both directions
206
- // pass through a LineTap so the per-issue JSONL run log captures every JSON-RPC frame
207
- // verbatim (parsed JSON when possible, raw bytes when not).
208
- this.inboundTap = new LineTap((line) => recordAcpFrame(opts.runLog, 'vm_to_host', line));
209
- this.outboundTap = new LineTap((line) => recordAcpFrame(opts.runLog, 'host_to_vm', line));
210
- opts.stdout.pipe(this.inboundTap);
211
- this.outboundTap.pipe(opts.stdin);
212
- const input = Readable.toWeb(this.inboundTap);
213
- const output = Writable.toWeb(this.outboundTap);
214
- const stream = ndJsonStream(output, input);
215
- this.conn = new ClientSideConnection((_agent) => this.makeClient(), stream);
216
- // Stderr handling lives in the AgentRunner now — it attaches a tap immediately after
217
- // launching the sandbox process so pre-bridge startup failures (vm-agent missing,
218
- // bad env, adapter crash before connect) are captured in the JSONL run log and the
219
- // per-issue event ring. AcpClient no longer consumes opts.stderr; the field is kept
220
- // on the options type for backward compatibility and so callers retain a single
221
- // place to think about where the stderr stream goes.
222
- opts.stdout.on('close', () => this.handleTransportClose('stdout_closed'));
223
- opts.stdin.on('error', () => {
224
- /* surface through transport close */
225
- });
226
- }
227
- // The Client interface implementation that the adapter calls back into. Every method is
228
- // implemented because we run the agent inside a Gondolin VM; ACP requires the client to handle
229
- // requests it advertises, and the SDK's Client interface lists them all.
230
- makeClient() {
231
- const self = this;
232
- return {
233
- async sessionUpdate(params) {
234
- return self.onSessionUpdate(params);
235
- },
236
- async requestPermission(params) {
237
- return self.onPermissionRequest(params);
238
- },
239
- async readTextFile(_params) {
240
- // §6.1 "high-trust" posture: the agent has direct workspace access inside the VM,
241
- // so client-mediated reads are unsupported. Returning an error keeps the session
242
- // alive (per §6.1 "unsupported dynamic tool calls return failure without stall").
243
- throw new AcpProtocolError('client_capability_not_implemented', 'client fs read not supported');
244
- },
245
- async writeTextFile(_params) {
246
- throw new AcpProtocolError('client_capability_not_implemented', 'client fs write not supported');
247
- },
248
- async createTerminal(_params) {
249
- throw new AcpProtocolError('client_capability_not_implemented', 'client terminal not supported');
250
- },
251
- async terminalOutput(_params) {
252
- throw new AcpProtocolError('client_capability_not_implemented', 'client terminal not supported');
253
- },
254
- async waitForTerminalExit(_params) {
255
- throw new AcpProtocolError('client_capability_not_implemented', 'client terminal not supported');
256
- },
257
- async killTerminal(_params) {
258
- throw new AcpProtocolError('client_capability_not_implemented', 'client terminal not supported');
259
- },
260
- async releaseTerminal(_params) {
261
- throw new AcpProtocolError('client_capability_not_implemented', 'client terminal not supported');
262
- },
263
- };
264
- }
265
- emit(event, message) {
266
- this.opts.onEvent({ at: nowIso(), event, message });
267
- }
268
- onSessionUpdate(params) {
269
- const update = params.update;
270
- switch (update.sessionUpdate) {
271
- case 'agent_message_chunk':
272
- return this.onAgentMessageChunk(update);
273
- case 'agent_thought_chunk':
274
- return this.onAgentThoughtChunk(update);
275
- case 'tool_call':
276
- return this.emit('tool_call', summarizeToolCall(update));
277
- case 'tool_call_update':
278
- return this.emit('tool_call_update', summarizeToolCallUpdate(update));
279
- case 'plan':
280
- return this.emit('plan', summarize(update.entries ?? update));
281
- case 'usage_update':
282
- return this.onUsageUpdate(update);
283
- default:
284
- return this.emit('session_update', `${update.sessionUpdate}: ${summarize(update)}`);
285
- }
286
- }
287
- onAgentMessageChunk(update) {
288
- const text = extractTextContent(update.content);
289
- if (!text)
290
- return;
291
- this.lastAssistantText += text;
292
- this.emit('agent_message_chunk', truncate(text, 80));
293
- }
294
- onAgentThoughtChunk(update) {
295
- const text = extractTextContent(update.content);
296
- if (text)
297
- this.emit('agent_thought_chunk', truncate(text, 80));
298
- }
299
- // ACP usage is "context-window used / size", not cumulative I/O tokens. We map it
300
- // into total_tokens so the existing orchestrator accounting stays meaningful; the
301
- // I/O split is recorded as zero because ACP does not expose it.
302
- onUsageUpdate(update) {
303
- const used = Number(update.used ?? 0);
304
- const size = Number(update.size ?? 0);
305
- this.opts.onTokenUsage({ input_tokens: 0, output_tokens: 0, total_tokens: used });
306
- this.emit('usage_update', `used=${used}/${size}`);
307
- }
308
- onPermissionRequest(params) {
309
- // §6.1 high-trust posture: auto-approve every prompt with "allow_always" so the
310
- // agent doesn't ask twice in the same session. Falls back to the first listed option
311
- // if no "allow_*" kind is present, which keeps the session alive in degraded mode.
312
- const preferred = params.options.find((o) => o.kind === 'allow_always') ??
313
- params.options.find((o) => o.kind === 'allow_once') ??
314
- params.options[0];
315
- const optionId = preferred?.optionId ?? '';
316
- const tool = summarizeToolCallUpdate(params.toolCall);
317
- this.emit('approval_auto_approved', `${optionId || 'unknown'}: ${tool}`);
318
- return { outcome: { outcome: 'selected', optionId } };
319
- }
320
- handleTransportClose(reason) {
321
- if (this.closed)
322
- return;
323
- this.closed = true;
324
- this.emit('subprocess_exit', reason);
325
- // Force-end the LineTap streams so the SDK's `receive()` reader loop observes
326
- // `done: true` from `reader.read()`. That makes the SDK call its internal `close()`,
327
- // which rejects every entry in `pendingResponses` (including the in-flight
328
- // `session/prompt`). Without this, the runner hangs in `runPrompt()` for the full
329
- // `prompt_timeout_ms` (30 min) even though the socket is gone, because `socket.destroy()`
330
- // emits `'close'` on the socket but Node's pipe machinery does not always propagate an
331
- // end-of-stream through the Transform synchronously. `Transform.end()` is idempotent
332
- // and safe to call here even if the natural pipe-EOF would have eventually arrived.
333
- try {
334
- this.inboundTap.end();
335
- }
336
- catch {
337
- /* idempotent — already ended */
338
- }
339
- try {
340
- this.outboundTap.end();
341
- }
342
- catch {
343
- /* idempotent — already ended */
344
- }
345
- }
346
- /**
347
- * Explicit force-close for cancel paths. Idempotent. Triggers `handleTransportClose`
348
- * (and thus the SDK's internal close + pendingResponses rejection) without waiting for
349
- * the underlying socket to surface its own `'close'` event. Safe to call from cancel
350
- * timers that fire repeatedly.
351
- */
352
- forceClose(reason) {
353
- this.handleTransportClose(reason);
354
- }
355
- // Negotiate protocol + open a session. Throws on either failure.
356
- async initSession() {
357
- const init = await withTimeout(this.conn.initialize({
358
- protocolVersion: PROTOCOL_VERSION,
359
- clientInfo: { name: 'smol-symphony', version: '0.1.0' },
360
- clientCapabilities: {
361
- // Advertise the minimum: the agent can read/write the workspace itself inside
362
- // the VM, so we do not offer fs/terminal capabilities.
363
- fs: { readTextFile: false, writeTextFile: false },
364
- terminal: false,
365
- },
366
- }), this.opts.readTimeoutMs, 'initialize');
367
- this.emit('session_init', `protocolVersion=${init.protocolVersion}`);
368
- // MCP is required for symphony operations: if the agent doesn't advertise support
369
- // for a requested transport, we refuse to start the session rather than silently
370
- // dropping the entry and running a degraded agent that has no transition /
371
- // request_human_steering tools. ACP's mcpCapabilities is optional and advertises
372
- // `http`/`sse`/`acp` as booleans; missing/undefined means "not supported." Stdio is
373
- // implicit (the spec treats it as the baseline) and has no `type` discriminator.
374
- const requested = this.opts.mcpServers ?? [];
375
- const caps = (init.agentCapabilities ?? {});
376
- const mcpCaps = caps.mcpCapabilities ?? {};
377
- const supportsKind = (kind) => {
378
- if (kind === 'stdio')
379
- return true;
380
- if (kind === 'http')
381
- return mcpCaps.http === true;
382
- if (kind === 'sse')
383
- return mcpCaps.sse === true;
384
- if (kind === 'acp')
385
- return mcpCaps.acp === true;
386
- return false;
387
- };
388
- const unsupported = requested.filter((s) => {
389
- const kind = (s.type ?? 'stdio');
390
- return !supportsKind(kind);
391
- });
392
- if (unsupported.length > 0) {
393
- const kinds = unsupported
394
- .map((s) => s.type ?? 'stdio')
395
- .join(', ');
396
- this.emit('mcp_capability_mismatch', `unsupported=${kinds} adapter_caps=${JSON.stringify(mcpCaps)}`);
397
- throw new AcpProtocolError('mcp_capability_mismatch', `agent does not support required MCP transport(s): ${kinds}. Adapter caps: ${JSON.stringify(mcpCaps)}`);
398
- }
399
- const session = await withTimeout(this.conn.newSession({ cwd: this.opts.cwd, mcpServers: requested }), this.opts.readTimeoutMs, 'session/new');
400
- this.sessionId = session.sessionId;
401
- this.emit('session_started', `sessionId=${session.sessionId} cwd=${this.opts.cwd} mcp_servers=${requested.length}`);
402
- return { sessionId: session.sessionId };
403
- }
404
- async runPrompt(promptText) {
405
- if (this.closed)
406
- return { reason: 'subprocess_exit', message: 'adapter already closed' };
407
- if (!this.sessionId)
408
- return { reason: 'startup_failed', message: 'no active session' };
409
- this.lastAssistantText = '';
410
- const request = {
411
- sessionId: this.sessionId,
412
- prompt: [{ type: 'text', text: promptText }],
413
- };
414
- let resp;
415
- try {
416
- resp = await withTimeout(this.conn.prompt(request), this.opts.promptTimeoutMs, 'session/prompt');
417
- }
418
- catch (err) {
419
- if (err instanceof TimeoutError) {
420
- // Best-effort cancel so the adapter doesn't keep working in the background.
421
- this.cancel().catch(() => undefined);
422
- return { reason: 'prompt_timeout', message: err.message };
423
- }
424
- if (this.closed)
425
- return { reason: 'subprocess_exit', message: err.message };
426
- const message = err.message;
427
- if (isTransportError(message))
428
- return { reason: 'transport_error', message };
429
- return { reason: 'refusal', message };
430
- }
431
- return mapStopReason(resp.stopReason, this.lastAssistantText);
432
- }
433
- async cancel() {
434
- if (!this.sessionId || this.closed || this.cancelled)
435
- return;
436
- this.cancelled = true;
437
- const note = { sessionId: this.sessionId };
438
- try {
439
- await this.conn.cancel(note);
440
- }
441
- catch (err) {
442
- log.debug('acp cancel failed', { error: err.message });
443
- }
444
- }
445
- }
446
- function mapStopReason(stopReason, lastText) {
447
- switch (stopReason) {
448
- case 'end_turn':
449
- return { reason: 'end_turn', message: summarize(lastText) };
450
- case 'max_tokens':
451
- case 'max_turn_requests':
452
- case 'refusal':
453
- case 'cancelled':
454
- return { reason: stopReason, message: summarize(lastText) };
455
- default:
456
- return { reason: 'refusal', message: `unknown stop_reason ${stopReason}` };
457
- }
458
- }
459
- class TimeoutError extends Error {
460
- }
461
- function withTimeout(p, ms, label) {
462
- return new Promise((resolve, reject) => {
463
- const t = setTimeout(() => reject(new TimeoutError(`${label} timed out after ${ms}ms`)), ms);
464
- p.then((v) => {
465
- clearTimeout(t);
466
- resolve(v);
467
- }, (e) => {
468
- clearTimeout(t);
469
- reject(e);
470
- });
471
- });
472
- }
473
- //# sourceMappingURL=acp.js.map