@sunshinelife83/hearth 1.0.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 (436) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +323 -0
  3. package/bin/hearth-agentd.js +4 -0
  4. package/bin/hearth.js +4 -0
  5. package/bin/run-entrypoint.js +20 -0
  6. package/dist/agent-tools.js +738 -0
  7. package/dist/app-version.js +16 -0
  8. package/dist/apply-patch.js +402 -0
  9. package/dist/artifact-error.js +8 -0
  10. package/dist/artifact-tools.js +377 -0
  11. package/dist/cli-workspace.js +36 -0
  12. package/dist/cli.js +1086 -0
  13. package/dist/config-migration.js +82 -0
  14. package/dist/config-schema.js +136 -0
  15. package/dist/config.js +115 -0
  16. package/dist/context/context-tools.js +150 -0
  17. package/dist/context/repo-map.js +102 -0
  18. package/dist/dashboard/app.js +226 -0
  19. package/dist/dashboard/index.html +45 -0
  20. package/dist/dashboard/landing.html +57 -0
  21. package/dist/dashboard/styles.css +23 -0
  22. package/dist/dashboard.js +271 -0
  23. package/dist/db/client.js +29 -0
  24. package/dist/db/migrations.js +262 -0
  25. package/dist/db/schema.js +85 -0
  26. package/dist/device-tokens.js +61 -0
  27. package/dist/git-worktrees.js +134 -0
  28. package/dist/git.js +41 -0
  29. package/dist/incoming-artifacts.js +378 -0
  30. package/dist/local-agent-acp.js +768 -0
  31. package/dist/local-agent-adapters.js +20 -0
  32. package/dist/local-agent-availability.js +92 -0
  33. package/dist/local-agent-catalog.js +51 -0
  34. package/dist/local-agent-claude.js +334 -0
  35. package/dist/local-agent-client.js +519 -0
  36. package/dist/local-agent-codex.js +532 -0
  37. package/dist/local-agent-config.js +34 -0
  38. package/dist/local-agent-daemon-lifecycle.js +195 -0
  39. package/dist/local-agent-daemon-main.js +86 -0
  40. package/dist/local-agent-daemon-protocol.js +302 -0
  41. package/dist/local-agent-daemon.js +407 -0
  42. package/dist/local-agent-errors.js +378 -0
  43. package/dist/local-agent-grok.js +202 -0
  44. package/dist/local-agent-manager.js +715 -0
  45. package/dist/local-agent-opencode.js +463 -0
  46. package/dist/local-agent-path.js +24 -0
  47. package/dist/local-agent-pi-sandbox.js +472 -0
  48. package/dist/local-agent-pi.js +298 -0
  49. package/dist/local-agent-presentation.js +104 -0
  50. package/dist/local-agent-profiles.js +127 -0
  51. package/dist/local-agent-runtime-pool.js +481 -0
  52. package/dist/local-agent-runtime.js +1 -0
  53. package/dist/local-agent-store.js +193 -0
  54. package/dist/local-agent-targets.js +97 -0
  55. package/dist/logger.js +97 -0
  56. package/dist/machine-id.js +35 -0
  57. package/dist/mcp-modern-server.js +65 -0
  58. package/dist/oauth-provider.js +331 -0
  59. package/dist/oauth-store.js +144 -0
  60. package/dist/onboarding.js +35 -0
  61. package/dist/orchestration/brief.js +70 -0
  62. package/dist/orchestration/lanes.js +145 -0
  63. package/dist/orchestration/result.js +46 -0
  64. package/dist/orchestration/scope.js +61 -0
  65. package/dist/pi-tools.js +58 -0
  66. package/dist/policy/command-policy.js +104 -0
  67. package/dist/policy/env-allowlist.js +96 -0
  68. package/dist/policy/sandbox.js +68 -0
  69. package/dist/policy/workspace-profiles.js +47 -0
  70. package/dist/process-journal.js +116 -0
  71. package/dist/process-platform.js +49 -0
  72. package/dist/process-sessions.js +388 -0
  73. package/dist/rate-limit.js +77 -0
  74. package/dist/request-meta.js +9 -0
  75. package/dist/review-checkpoints.js +349 -0
  76. package/dist/roots.js +37 -0
  77. package/dist/server-shutdown.js +12 -0
  78. package/dist/server.js +980 -0
  79. package/dist/skills.js +84 -0
  80. package/dist/snapshot-manager.js +123 -0
  81. package/dist/stdio-server.js +80 -0
  82. package/dist/task-store.js +203 -0
  83. package/dist/task-tools.js +455 -0
  84. package/dist/tool-surfaces/claude.js +220 -0
  85. package/dist/tool-surfaces/codex.js +255 -0
  86. package/dist/tool-surfaces/index.js +15 -0
  87. package/dist/tool-surfaces/shared.js +228 -0
  88. package/dist/tool-surfaces/types.js +27 -0
  89. package/dist/ui/.vite/manifest.json +2544 -0
  90. package/dist/ui/assets/abap-CLvhMVsD.js +1 -0
  91. package/dist/ui/assets/actionscript-3--17pq3dv.js +1 -0
  92. package/dist/ui/assets/ada-C5qYipkI.js +1 -0
  93. package/dist/ui/assets/andromeeda-vGVdxbeo.js +1 -0
  94. package/dist/ui/assets/angular-html-DrbO3_Xe.js +1 -0
  95. package/dist/ui/assets/angular-ts-CmTVzHJx.js +1 -0
  96. package/dist/ui/assets/apache-U0d_L8uA.js +1 -0
  97. package/dist/ui/assets/apex-CGTLDQj6.js +1 -0
  98. package/dist/ui/assets/apl-BOpCpqXf.js +1 -0
  99. package/dist/ui/assets/applescript-CCn79oCD.js +1 -0
  100. package/dist/ui/assets/ara-4CJ0cIlV.js +1 -0
  101. package/dist/ui/assets/asciidoc-DE70LPWp.js +1 -0
  102. package/dist/ui/assets/asm-Cmm7eHzH.js +1 -0
  103. package/dist/ui/assets/astro-CxPeBwei.js +1 -0
  104. package/dist/ui/assets/aurora-x-CDeNXAV0.js +1 -0
  105. package/dist/ui/assets/awk-BWXHIvNe.js +1 -0
  106. package/dist/ui/assets/ayu-dark-DluEY0Gj.js +1 -0
  107. package/dist/ui/assets/ayu-light-C3h-C4tm.js +1 -0
  108. package/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +1 -0
  109. package/dist/ui/assets/ballerina-B7ZEbQpA.js +1 -0
  110. package/dist/ui/assets/bat-Bo4NYOV-.js +1 -0
  111. package/dist/ui/assets/beancount-D-usSTwE.js +1 -0
  112. package/dist/ui/assets/berry-DKpUyyne.js +1 -0
  113. package/dist/ui/assets/bibtex-Ci_nEsc7.js +1 -0
  114. package/dist/ui/assets/bicep-CUHmPFLl.js +1 -0
  115. package/dist/ui/assets/bird2-C2hNVINV.js +1 -0
  116. package/dist/ui/assets/blade-DtsAAG4P.js +1 -0
  117. package/dist/ui/assets/bsl-BkkzgIyY.js +1 -0
  118. package/dist/ui/assets/c-D9hLjNUM.js +1 -0
  119. package/dist/ui/assets/c3-CnJL0r0V.js +1 -0
  120. package/dist/ui/assets/cadence-CQ2zXKGN.js +1 -0
  121. package/dist/ui/assets/cairo-DLTphjLi.js +1 -0
  122. package/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
  123. package/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
  124. package/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
  125. package/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
  126. package/dist/ui/assets/clarity-SemFz856.js +1 -0
  127. package/dist/ui/assets/clojure-DqKBuwfJ.js +1 -0
  128. package/dist/ui/assets/cmake-Bj61d0ZC.js +1 -0
  129. package/dist/ui/assets/cobol-C1SWlvZm.js +1 -0
  130. package/dist/ui/assets/codeowners-C8r90Shi.js +1 -0
  131. package/dist/ui/assets/codeql-oeQT6MSM.js +1 -0
  132. package/dist/ui/assets/coffee-B71R3v2U.js +1 -0
  133. package/dist/ui/assets/common-lisp-Cv5bFMCO.js +1 -0
  134. package/dist/ui/assets/coq-BrsZFFmf.js +1 -0
  135. package/dist/ui/assets/cpp--5a4c3v4.js +1 -0
  136. package/dist/ui/assets/crystal-AtqWYtE0.js +1 -0
  137. package/dist/ui/assets/csharp-Ct8U2NOr.js +1 -0
  138. package/dist/ui/assets/css-CbqKSHI-.js +1 -0
  139. package/dist/ui/assets/csv-Dx-8-gkx.js +1 -0
  140. package/dist/ui/assets/cue-CE9AQfxI.js +1 -0
  141. package/dist/ui/assets/cypher-ClKdZ_lG.js +1 -0
  142. package/dist/ui/assets/d-qD-0Kul2.js +1 -0
  143. package/dist/ui/assets/dark-plus-Cs2F2srj.js +1 -0
  144. package/dist/ui/assets/dart-DkHntEIa.js +1 -0
  145. package/dist/ui/assets/dax-BkyTk9wS.js +1 -0
  146. package/dist/ui/assets/desktop-Dlh5hvp9.js +1 -0
  147. package/dist/ui/assets/diff-woXpYk--.js +1 -0
  148. package/dist/ui/assets/docker-IyjqRm3v.js +1 -0
  149. package/dist/ui/assets/dotenv-_5a1GRtc.js +1 -0
  150. package/dist/ui/assets/dracula-BHWKrbxM.js +1 -0
  151. package/dist/ui/assets/dracula-soft-5eyTD99u.js +1 -0
  152. package/dist/ui/assets/dream-maker-DW3nJb8Q.js +1 -0
  153. package/dist/ui/assets/edge-BPwY_RrJ.js +1 -0
  154. package/dist/ui/assets/elixir-DVH2yLJf.js +1 -0
  155. package/dist/ui/assets/elm-BrleA6gS.js +1 -0
  156. package/dist/ui/assets/emacs-lisp-C9PiwqqW.js +1 -0
  157. package/dist/ui/assets/erb-2SHiYe30.js +1 -0
  158. package/dist/ui/assets/erlang-Cphh6RMH.js +1 -0
  159. package/dist/ui/assets/everforest-dark-sB-x3p7T.js +1 -0
  160. package/dist/ui/assets/everforest-light-Df2xbC6M.js +1 -0
  161. package/dist/ui/assets/fennel-DQxkIbk2.js +1 -0
  162. package/dist/ui/assets/fish-BJitypiv.js +1 -0
  163. package/dist/ui/assets/fluent-C03EYrpw.js +1 -0
  164. package/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
  165. package/dist/ui/assets/fortran-free-form-CYNrtFtB.js +1 -0
  166. package/dist/ui/assets/fsharp-D13ZGOAj.js +1 -0
  167. package/dist/ui/assets/gdresource-C0sCabJj.js +1 -0
  168. package/dist/ui/assets/gdscript-Cp2uCuqX.js +1 -0
  169. package/dist/ui/assets/gdshader-CBce3t8t.js +1 -0
  170. package/dist/ui/assets/genie-CV2tkWYe.js +1 -0
  171. package/dist/ui/assets/gherkin-DExj1W_8.js +1 -0
  172. package/dist/ui/assets/git-commit-BSykSTBG.js +1 -0
  173. package/dist/ui/assets/git-rebase-Bc7atMaV.js +1 -0
  174. package/dist/ui/assets/github-dark-C-LZuMrd.js +1 -0
  175. package/dist/ui/assets/github-dark-default-DXG-b-1a.js +1 -0
  176. package/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
  177. package/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
  178. package/dist/ui/assets/github-light-EUqPIrTm.js +1 -0
  179. package/dist/ui/assets/github-light-default-BXViO-2h.js +1 -0
  180. package/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +1 -0
  181. package/dist/ui/assets/gleam-CSRkHgEL.js +1 -0
  182. package/dist/ui/assets/glimmer-js-DXeTDPtW.js +1 -0
  183. package/dist/ui/assets/glimmer-ts-CEOoXHU4.js +1 -0
  184. package/dist/ui/assets/glsl-5-43-Gsg.js +1 -0
  185. package/dist/ui/assets/gn-ilITqXS6.js +1 -0
  186. package/dist/ui/assets/gnuplot-7GGW24-e.js +1 -0
  187. package/dist/ui/assets/go-BJwz_mda.js +1 -0
  188. package/dist/ui/assets/graphql-Bk7Ah1KA.js +1 -0
  189. package/dist/ui/assets/groovy-CacY0gHj.js +1 -0
  190. package/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
  191. package/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
  192. package/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
  193. package/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
  194. package/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
  195. package/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
  196. package/dist/ui/assets/hack-DUHz4rCJ.js +1 -0
  197. package/dist/ui/assets/haml-BPCuuTPR.js +1 -0
  198. package/dist/ui/assets/handlebars-CHxmUsnU.js +1 -0
  199. package/dist/ui/assets/haskell-D8IpX4py.js +1 -0
  200. package/dist/ui/assets/haxe-OTjmBuCE.js +1 -0
  201. package/dist/ui/assets/hcl-Dh228itO.js +1 -0
  202. package/dist/ui/assets/hjson-CxZEssPk.js +1 -0
  203. package/dist/ui/assets/hlsl-Cvrh5tZx.js +1 -0
  204. package/dist/ui/assets/horizon-CE9ld1lL.js +1 -0
  205. package/dist/ui/assets/horizon-bright-Br1oVSNq.js +1 -0
  206. package/dist/ui/assets/houston-CsvMBhTu.js +1 -0
  207. package/dist/ui/assets/html-BBJqizz0.js +1 -0
  208. package/dist/ui/assets/html-derivative-ShTPCcKN.js +1 -0
  209. package/dist/ui/assets/http-VFTyziwA.js +1 -0
  210. package/dist/ui/assets/hurl-B8yxTa4r.js +1 -0
  211. package/dist/ui/assets/hxml-B0Qn7Nwc.js +1 -0
  212. package/dist/ui/assets/hy-CZbG8q4J.js +1 -0
  213. package/dist/ui/assets/imba-DsUTQ-LC.js +1 -0
  214. package/dist/ui/assets/ini-B5eOa1yu.js +1 -0
  215. package/dist/ui/assets/java-BZ5DAgJZ.js +1 -0
  216. package/dist/ui/assets/javascript-C_zuIEaC.js +1 -0
  217. package/dist/ui/assets/jinja-DWd6xfNr.js +1 -0
  218. package/dist/ui/assets/jison-BbY63d1V.js +1 -0
  219. package/dist/ui/assets/json-C4DnajZs.js +1 -0
  220. package/dist/ui/assets/json5-BR5RXkoi.js +1 -0
  221. package/dist/ui/assets/jsonc-CYpm1nAK.js +1 -0
  222. package/dist/ui/assets/jsonl-CmCQp5Yx.js +1 -0
  223. package/dist/ui/assets/jsonnet-CJTPZ8u_.js +1 -0
  224. package/dist/ui/assets/jssm-DXw9l8Rf.js +1 -0
  225. package/dist/ui/assets/jsx-C4AACieZ.js +1 -0
  226. package/dist/ui/assets/julia-sRu7EFMW.js +1 -0
  227. package/dist/ui/assets/just-DpZ9Sx1v.js +1 -0
  228. package/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
  229. package/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +1 -0
  230. package/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +1 -0
  231. package/dist/ui/assets/kdl-CsD5j6eV.js +1 -0
  232. package/dist/ui/assets/kotlin-DhhofPvG.js +1 -0
  233. package/dist/ui/assets/kusto-BUv0MjJC.js +1 -0
  234. package/dist/ui/assets/laserwave-C_8bwKvT.js +1 -0
  235. package/dist/ui/assets/latex-B6a_uum7.js +1 -0
  236. package/dist/ui/assets/lean-CewbzKMR.js +1 -0
  237. package/dist/ui/assets/less-DVTAwKKz.js +1 -0
  238. package/dist/ui/assets/light-plus-DVQuIRkW.js +1 -0
  239. package/dist/ui/assets/liquid-B2sVpjjr.js +1 -0
  240. package/dist/ui/assets/llvm-Cm23YOpf.js +1 -0
  241. package/dist/ui/assets/log-BNLmms1o.js +1 -0
  242. package/dist/ui/assets/logo-Cluzi2Zq.js +1 -0
  243. package/dist/ui/assets/lua-DxJJll61.js +1 -0
  244. package/dist/ui/assets/luau-CNKltnaQ.js +1 -0
  245. package/dist/ui/assets/make-Dixweg8N.js +1 -0
  246. package/dist/ui/assets/markdown-BYOwaDjH.js +1 -0
  247. package/dist/ui/assets/marko-Dx8mw-Wx.js +1 -0
  248. package/dist/ui/assets/material-theme-Bm3Qr25_.js +1 -0
  249. package/dist/ui/assets/material-theme-darker-2IIEA8gg.js +1 -0
  250. package/dist/ui/assets/material-theme-lighter-uhdI0v04.js +1 -0
  251. package/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +1 -0
  252. package/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +1 -0
  253. package/dist/ui/assets/matlab-D7qyCx1q.js +1 -0
  254. package/dist/ui/assets/mdc-vNKeXEwR.js +1 -0
  255. package/dist/ui/assets/mdx-DQZ5AkYe.js +1 -0
  256. package/dist/ui/assets/mermaid-Bk4SNUv9.js +1 -0
  257. package/dist/ui/assets/min-dark-BSWPekZh.js +1 -0
  258. package/dist/ui/assets/min-light-DDpmG2fV.js +1 -0
  259. package/dist/ui/assets/mipsasm-BMqwQI7S.js +1 -0
  260. package/dist/ui/assets/mojo-BgCJLMeH.js +1 -0
  261. package/dist/ui/assets/monokai-CdkpiU2Y.js +1 -0
  262. package/dist/ui/assets/moonbit-CaWjb8XO.js +1 -0
  263. package/dist/ui/assets/move-B1IS1UjX.js +1 -0
  264. package/dist/ui/assets/narrat-_X_XdTYD.js +1 -0
  265. package/dist/ui/assets/nextflow-Bbiyy34d.js +1 -0
  266. package/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +1 -0
  267. package/dist/ui/assets/nginx-DScQAGLT.js +1 -0
  268. package/dist/ui/assets/night-owl-DhmEMT88.js +1 -0
  269. package/dist/ui/assets/night-owl-light-eJ-hLW7d.js +1 -0
  270. package/dist/ui/assets/nim-lAJch9uT.js +1 -0
  271. package/dist/ui/assets/nix-IvuFDN5E.js +1 -0
  272. package/dist/ui/assets/nord-Cb4Vim4T.js +1 -0
  273. package/dist/ui/assets/nushell-DcLAeLz5.js +1 -0
  274. package/dist/ui/assets/objective-c-D1A_Heim.js +1 -0
  275. package/dist/ui/assets/objective-cpp-BsSzOQcm.js +1 -0
  276. package/dist/ui/assets/ocaml-O90oeIOV.js +1 -0
  277. package/dist/ui/assets/odin-B1RWQWA5.js +1 -0
  278. package/dist/ui/assets/one-dark-pro-CLwyXe_n.js +1 -0
  279. package/dist/ui/assets/one-light-D7Lr4KcI.js +1 -0
  280. package/dist/ui/assets/openscad-BUDT5pXO.js +1 -0
  281. package/dist/ui/assets/pascal-4ZHwLPI5.js +1 -0
  282. package/dist/ui/assets/perl-C_GkVo_E.js +1 -0
  283. package/dist/ui/assets/php-DziV3K6J.js +1 -0
  284. package/dist/ui/assets/pierre-dark-CpLgRqie.js +1 -0
  285. package/dist/ui/assets/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
  286. package/dist/ui/assets/pierre-dark-soft-kZQmAZld.js +1 -0
  287. package/dist/ui/assets/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
  288. package/dist/ui/assets/pierre-dark-vibrant-CpQYzh95.js +1 -0
  289. package/dist/ui/assets/pierre-light-CoaEpmwp.js +1 -0
  290. package/dist/ui/assets/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
  291. package/dist/ui/assets/pierre-light-soft-lWLdNTOI.js +1 -0
  292. package/dist/ui/assets/pierre-light-tritanopia-CEbqgOJL.js +1 -0
  293. package/dist/ui/assets/pierre-light-vibrant-D80Fkn33.js +1 -0
  294. package/dist/ui/assets/pkl-ot-7Btpt.js +1 -0
  295. package/dist/ui/assets/plastic-DQwYfKfQ.js +1 -0
  296. package/dist/ui/assets/plsql-DGHpHOYJ.js +1 -0
  297. package/dist/ui/assets/po-BiJDBrnU.js +1 -0
  298. package/dist/ui/assets/poimandres-DRFjx7u4.js +1 -0
  299. package/dist/ui/assets/polar-C7UOKdEL.js +1 -0
  300. package/dist/ui/assets/postcss-BXeXVLqQ.js +1 -0
  301. package/dist/ui/assets/powerquery-DNMTfnFr.js +1 -0
  302. package/dist/ui/assets/powershell-DshXNtvi.js +1 -0
  303. package/dist/ui/assets/prisma-BsRQq5mF.js +1 -0
  304. package/dist/ui/assets/prolog-iXnhIJG7.js +1 -0
  305. package/dist/ui/assets/proto-DB4EqR-F.js +1 -0
  306. package/dist/ui/assets/pug-z75UIWVK.js +1 -0
  307. package/dist/ui/assets/puppet-CDv2pdJW.js +1 -0
  308. package/dist/ui/assets/purescript-9MfHhQsQ.js +1 -0
  309. package/dist/ui/assets/python-gzcpVVnB.js +1 -0
  310. package/dist/ui/assets/qml-Cmm4coz1.js +1 -0
  311. package/dist/ui/assets/qmldir-DCQb3MpD.js +1 -0
  312. package/dist/ui/assets/qss-Fe1Jh2GI.js +1 -0
  313. package/dist/ui/assets/r-Brd0axBE.js +1 -0
  314. package/dist/ui/assets/racket-DcIDlBhZ.js +1 -0
  315. package/dist/ui/assets/raku-B3gFvitq.js +1 -0
  316. package/dist/ui/assets/razor-DSbwmoiu.js +1 -0
  317. package/dist/ui/assets/red-CJ3rzSJv.js +1 -0
  318. package/dist/ui/assets/reg-CRGYupPL.js +1 -0
  319. package/dist/ui/assets/regexp-Bs3Uk-pY.js +1 -0
  320. package/dist/ui/assets/rel-BtDbiS_P.js +1 -0
  321. package/dist/ui/assets/review-payload-CbwCBKB1.js +1580 -0
  322. package/dist/ui/assets/riscv-Ckw8ddFX.js +1 -0
  323. package/dist/ui/assets/ron-VUp2lXgN.js +1 -0
  324. package/dist/ui/assets/rose-pine-BthvhNj6.js +1 -0
  325. package/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
  326. package/dist/ui/assets/rose-pine-moon-hon4tzzS.js +1 -0
  327. package/dist/ui/assets/rosmsg-CAekHB0j.js +1 -0
  328. package/dist/ui/assets/rst-ChIyKpUC.js +1 -0
  329. package/dist/ui/assets/ruby-BdAmvOMO.js +1 -0
  330. package/dist/ui/assets/rust-Cfkwpbl8.js +1 -0
  331. package/dist/ui/assets/sas-DpXn8hc5.js +1 -0
  332. package/dist/ui/assets/sass-DXrisJhu.js +1 -0
  333. package/dist/ui/assets/scala-DKOlJaKm.js +1 -0
  334. package/dist/ui/assets/scheme-DQCgrYNe.js +1 -0
  335. package/dist/ui/assets/scss-atX1Uuef.js +1 -0
  336. package/dist/ui/assets/sdbl-bTVj8UrX.js +1 -0
  337. package/dist/ui/assets/shaderlab-TOUzSsQk.js +1 -0
  338. package/dist/ui/assets/shellscript-BqgcAeMK.js +1 -0
  339. package/dist/ui/assets/shellsession-DfQSMRN5.js +1 -0
  340. package/dist/ui/assets/slack-dark-DnToyrRv.js +1 -0
  341. package/dist/ui/assets/slack-ochin-B2OO5cIa.js +1 -0
  342. package/dist/ui/assets/smalltalk-B16xEiuN.js +1 -0
  343. package/dist/ui/assets/snazzy-light-4G7pJPwS.js +1 -0
  344. package/dist/ui/assets/solarized-dark-DV17i1UV.js +1 -0
  345. package/dist/ui/assets/solarized-light-DSh2HLQt.js +1 -0
  346. package/dist/ui/assets/solidity-CKzVLygQ.js +1 -0
  347. package/dist/ui/assets/soy-DzP7zZTB.js +1 -0
  348. package/dist/ui/assets/sparql-D_iOobhT.js +1 -0
  349. package/dist/ui/assets/splunk-BC2Px7Mm.js +1 -0
  350. package/dist/ui/assets/sql-gmaLk66z.js +1 -0
  351. package/dist/ui/assets/ssh-config-BgfXC-Er.js +1 -0
  352. package/dist/ui/assets/stata-CJ0YTMcV.js +1 -0
  353. package/dist/ui/assets/stylus-B6D30XZt.js +1 -0
  354. package/dist/ui/assets/surrealql-LH7FciQz.js +1 -0
  355. package/dist/ui/assets/svelte-BME2PU6V.js +1 -0
  356. package/dist/ui/assets/swift-DonLKvLd.js +1 -0
  357. package/dist/ui/assets/synthwave-84-nFMaYfgc.js +1 -0
  358. package/dist/ui/assets/system-verilog-DJ5XKQeo.js +1 -0
  359. package/dist/ui/assets/systemd-BxMlprV5.js +1 -0
  360. package/dist/ui/assets/talonscript-CohzipZa.js +1 -0
  361. package/dist/ui/assets/tasl-DMoTqEGO.js +1 -0
  362. package/dist/ui/assets/tcl-CZd0xW_V.js +1 -0
  363. package/dist/ui/assets/templ-DHBNIfYW.js +1 -0
  364. package/dist/ui/assets/terraform-DswuEJGm.js +1 -0
  365. package/dist/ui/assets/tex-C8-kPEzO.js +1 -0
  366. package/dist/ui/assets/tokyo-night-oM2G3aXe.js +1 -0
  367. package/dist/ui/assets/toml-CcmNWLt0.js +1 -0
  368. package/dist/ui/assets/ts-tags-CiewV0vR.js +1 -0
  369. package/dist/ui/assets/tsv-sltzmVWM.js +1 -0
  370. package/dist/ui/assets/tsx-CUYPqmnq.js +1 -0
  371. package/dist/ui/assets/turtle-ByJddavk.js +1 -0
  372. package/dist/ui/assets/twig-BTkvsl0b.js +1 -0
  373. package/dist/ui/assets/typescript-DDQMaPwj.js +1 -0
  374. package/dist/ui/assets/typespec-B88KGewJ.js +1 -0
  375. package/dist/ui/assets/typst-DI99ib-x.js +1 -0
  376. package/dist/ui/assets/v-DETTlOr0.js +1 -0
  377. package/dist/ui/assets/vala-zf12oZj6.js +1 -0
  378. package/dist/ui/assets/vb-Djn5o6TS.js +1 -0
  379. package/dist/ui/assets/verilog-CiiDBU1e.js +1 -0
  380. package/dist/ui/assets/vesper-D5bVUKB1.js +1 -0
  381. package/dist/ui/assets/vhdl-BroJfC0k.js +1 -0
  382. package/dist/ui/assets/viml-DvXPmvsu.js +1 -0
  383. package/dist/ui/assets/vitesse-black-fwtXNY1n.js +1 -0
  384. package/dist/ui/assets/vitesse-dark-BZCL-v6S.js +1 -0
  385. package/dist/ui/assets/vitesse-light-VbXTXTou.js +1 -0
  386. package/dist/ui/assets/vue-BH5SEWRH.js +1 -0
  387. package/dist/ui/assets/vue-html-BT7yeYez.js +1 -0
  388. package/dist/ui/assets/vue-vine-Cs5IN2rs.js +1 -0
  389. package/dist/ui/assets/vyper-CgoNMtux.js +1 -0
  390. package/dist/ui/assets/wasm-BnjxR4X6.js +1 -0
  391. package/dist/ui/assets/wasm-ByWQv1Qj.js +1 -0
  392. package/dist/ui/assets/wenyan-C8pVoKbM.js +1 -0
  393. package/dist/ui/assets/wgsl-BsKzXJz4.js +1 -0
  394. package/dist/ui/assets/wikitext-ClFFjSW2.js +1 -0
  395. package/dist/ui/assets/wit-DdvCle-K.js +1 -0
  396. package/dist/ui/assets/wolfram-DLL8P-h_.js +1 -0
  397. package/dist/ui/assets/workspace-app-B_BnSEHO.css +1 -0
  398. package/dist/ui/assets/workspace-app-DSFpnrIC.js +115 -0
  399. package/dist/ui/assets/xml-D3_1BEcO.js +1 -0
  400. package/dist/ui/assets/xsl-BBOCDn4Y.js +1 -0
  401. package/dist/ui/assets/yaml-lThBDT45.js +1 -0
  402. package/dist/ui/assets/zenscript-BnlCZFoB.js +1 -0
  403. package/dist/ui/assets/zig-CMLA9XwU.js +1 -0
  404. package/dist/ui/workspace-app.html +15 -0
  405. package/dist/user-config.js +228 -0
  406. package/dist/verification.js +74 -0
  407. package/dist/workspace-store.js +133 -0
  408. package/dist/workspaces.js +433 -0
  409. package/docs/adr/devspace-x-implementation-adrs.md +159 -0
  410. package/docs/agent-profile-schema.md +203 -0
  411. package/docs/artifact-exchange.md +44 -0
  412. package/docs/assets/devspace-logo-light.png +0 -0
  413. package/docs/assets/devspace-screenshot.png +0 -0
  414. package/docs/assets/v11-review-ui-after.png +0 -0
  415. package/docs/assets/v11-review-ui-before.png +0 -0
  416. package/docs/chatgpt-coding-workflow.md +215 -0
  417. package/docs/configuration.md +217 -0
  418. package/docs/gotchas.md +294 -0
  419. package/docs/local-agent-daemon.md +101 -0
  420. package/docs/security.md +197 -0
  421. package/docs/setup.md +174 -0
  422. package/examples/agents/claude-implementer.md +30 -0
  423. package/examples/agents/codex-explorer.md +27 -0
  424. package/examples/agents/codex-qa-tester.md +29 -0
  425. package/examples/agents/codex-worker.md +25 -0
  426. package/examples/agents/copilot-reviewer.md +25 -0
  427. package/examples/agents/cursor-agent-worker.md +26 -0
  428. package/examples/agents/grok-builder.md +10 -0
  429. package/examples/agents/opencode-explorer.md +27 -0
  430. package/examples/agents/pi-reviewer.md +27 -0
  431. package/package.json +102 -0
  432. package/schema/v1/hearth.schema.json +529 -0
  433. package/scripts/copy-dashboard.mjs +9 -0
  434. package/scripts/fix-node-pty-permissions.mjs +22 -0
  435. package/scripts/generate-config-schema.ts +9 -0
  436. package/skills/subagents/SKILL.md +57 -0
@@ -0,0 +1,481 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Result } from "better-result";
3
+ import { AgentProviderUnavailableError, } from "./local-agent-errors.js";
4
+ const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60_000;
5
+ const DEFAULT_SESSION_IDLE_TIMEOUT_MS = 60_000;
6
+ /**
7
+ * Owns live provider resources, not logical agent identity. Acquisition is
8
+ * single-flight per runtime key and an entry is removed before its close
9
+ * begins, so a new caller can never race with a closing runtime.
10
+ */
11
+ export class LocalAgentRuntimePool {
12
+ entries = new Map();
13
+ now;
14
+ logger;
15
+ sessionIdleTimeoutMs;
16
+ closing = false;
17
+ closePromise;
18
+ constructor(options = {}) {
19
+ this.now = options.now ?? Date.now;
20
+ this.logger = options.logger;
21
+ this.sessionIdleTimeoutMs = options.sessionIdleTimeoutMs ?? DEFAULT_SESSION_IDLE_TIMEOUT_MS;
22
+ if (!Number.isFinite(this.sessionIdleTimeoutMs) || this.sessionIdleTimeoutMs < 0) {
23
+ throw new Error("Local agent session idle timeout must be a non-negative finite duration.");
24
+ }
25
+ }
26
+ async run(driver, context, input, inputCallbacks) {
27
+ if (this.closing)
28
+ return Result.err(poolClosedError(driver, context));
29
+ let acquired = await this.acquire(driver, context);
30
+ if (acquired.isErr())
31
+ return acquired;
32
+ let entry = acquired.value;
33
+ let runtime = entry.runtime;
34
+ if (!runtime)
35
+ throw new Error("Local agent runtime was created without a runtime.");
36
+ if (!runtime.isAlive()) {
37
+ await this.discardRuntime(entry, driver.provider, "runtime_not_alive");
38
+ acquired = await this.acquire(driver, context);
39
+ if (acquired.isErr())
40
+ return acquired;
41
+ entry = acquired.value;
42
+ runtime = entry.runtime;
43
+ if (!runtime || !runtime.isAlive()) {
44
+ await this.discardRuntime(entry, driver.provider, "runtime_not_alive");
45
+ return Result.err(new AgentProviderUnavailableError({
46
+ code: "PROVIDER_UNAVAILABLE",
47
+ provider: driver.provider,
48
+ agentId: context.agentId,
49
+ operation: "acquire_runtime",
50
+ retryable: true,
51
+ message: "Local agent runtime exited during startup.",
52
+ }));
53
+ }
54
+ }
55
+ this.clearIdleTimer(entry);
56
+ entry.activeRuns += 1;
57
+ const sessionIds = new Set();
58
+ const reserveSession = async (providerSessionId) => {
59
+ if (!providerSessionId || sessionIds.has(providerSessionId))
60
+ return undefined;
61
+ while (true) {
62
+ const existing = entry.sessions.get(providerSessionId);
63
+ if (existing?.releasePromise) {
64
+ await existing.releasePromise;
65
+ continue;
66
+ }
67
+ if (entry.closing)
68
+ return poolClosedError(driver, context);
69
+ const session = existing ?? { activeRuns: 0, lastUsedAt: this.now() };
70
+ sessionIds.add(providerSessionId);
71
+ session.activeRuns += 1;
72
+ session.lastUsedAt = this.now();
73
+ entry.sessions.set(providerSessionId, session);
74
+ return undefined;
75
+ }
76
+ };
77
+ const callbacks = {
78
+ onSessionId: async (providerSessionId) => {
79
+ const reservationError = await reserveSession(providerSessionId);
80
+ if (reservationError)
81
+ throw reservationError;
82
+ await inputCallbacks?.onSessionId?.(providerSessionId);
83
+ },
84
+ onOutput: (delta) => {
85
+ inputCallbacks?.onOutput?.(delta);
86
+ },
87
+ };
88
+ const startedAt = this.now();
89
+ try {
90
+ const inputReservationError = await reserveSession(input.providerSessionId ?? "");
91
+ if (inputReservationError)
92
+ return Result.err(inputReservationError);
93
+ const result = await runtime.run(input, callbacks);
94
+ if (result.isErr()) {
95
+ if (!runtime.isAlive()) {
96
+ try {
97
+ await this.removeAndClose(entry, "runtime_crashed");
98
+ }
99
+ catch (cleanupError) {
100
+ this.log("warn", "harness_runtime_close_failed", {
101
+ provider: driver.provider,
102
+ runtimeKeyHash: hashRuntimeKey(entry.key),
103
+ reason: "runtime_crashed",
104
+ error: errorMessage(cleanupError),
105
+ });
106
+ }
107
+ this.log("warn", "harness_runtime_crashed", {
108
+ provider: driver.provider,
109
+ runtimeKeyHash: hashRuntimeKey(entry.key),
110
+ agentId: context.agentId,
111
+ providerSessionIdPrefix: input.providerSessionId?.slice(0, 8),
112
+ durationMs: Math.max(0, Math.round(this.now() - startedAt)),
113
+ error: result.error.message,
114
+ });
115
+ }
116
+ return result;
117
+ }
118
+ const outputReservationError = await reserveSession(result.value.providerSessionId ?? "");
119
+ if (outputReservationError) {
120
+ this.log("warn", "harness_session_reservation_failed", {
121
+ provider: driver.provider,
122
+ runtimeKeyHash: hashRuntimeKey(entry.key),
123
+ agentId: context.agentId,
124
+ error: outputReservationError.message,
125
+ });
126
+ }
127
+ return result;
128
+ }
129
+ catch (error) {
130
+ if (!runtime.isAlive()) {
131
+ try {
132
+ await this.removeAndClose(entry, "runtime_crashed");
133
+ }
134
+ catch (cleanupError) {
135
+ this.log("warn", "harness_runtime_close_failed", {
136
+ provider: driver.provider,
137
+ runtimeKeyHash: hashRuntimeKey(entry.key),
138
+ reason: "runtime_crashed",
139
+ error: errorMessage(cleanupError),
140
+ });
141
+ }
142
+ this.log("warn", "harness_runtime_crashed", {
143
+ provider: driver.provider,
144
+ runtimeKeyHash: hashRuntimeKey(entry.key),
145
+ agentId: context.agentId,
146
+ providerSessionIdPrefix: input.providerSessionId?.slice(0, 8),
147
+ durationMs: Math.max(0, Math.round(this.now() - startedAt)),
148
+ error: errorMessage(error),
149
+ });
150
+ }
151
+ throw error;
152
+ }
153
+ finally {
154
+ for (const providerSessionId of sessionIds) {
155
+ const session = entry.sessions.get(providerSessionId);
156
+ if (!session)
157
+ continue;
158
+ session.activeRuns = Math.max(0, session.activeRuns - 1);
159
+ session.lastUsedAt = this.now();
160
+ }
161
+ entry.activeRuns -= 1;
162
+ if (entry.activeRuns === 0) {
163
+ for (const resolve of entry.activeRunWaiters)
164
+ resolve();
165
+ entry.activeRunWaiters.clear();
166
+ }
167
+ entry.lastUsedAt = this.now();
168
+ if (entry.activeRuns === 0 && !entry.closing)
169
+ this.scheduleIdleClose(entry);
170
+ }
171
+ }
172
+ async discardRuntime(entry, provider, reason) {
173
+ try {
174
+ await this.removeAndClose(entry, reason);
175
+ }
176
+ catch (error) {
177
+ this.log("warn", "harness_runtime_close_failed", {
178
+ provider,
179
+ runtimeKeyHash: hashRuntimeKey(entry.key),
180
+ reason,
181
+ error: errorMessage(error),
182
+ });
183
+ }
184
+ }
185
+ /** Evict entries whose runtime has been idle beyond their driver's TTL. */
186
+ async evictIdle(now = this.now()) {
187
+ const evictions = [];
188
+ for (const entry of this.entries.values()) {
189
+ if (entry.closing || !entry.runtime)
190
+ continue;
191
+ await this.releaseIdleSessions(entry, now);
192
+ if (entry.activeRuns === 0 && now - entry.lastUsedAt >= entry.idleTimeoutMs) {
193
+ evictions.push(this.removeAndClose(entry, "idle_timeout"));
194
+ }
195
+ }
196
+ await Promise.all(evictions);
197
+ }
198
+ async close() {
199
+ if (this.closePromise)
200
+ return this.closePromise;
201
+ this.closing = true;
202
+ const entries = Array.from(this.entries.values());
203
+ this.entries.clear();
204
+ this.closePromise = Promise.allSettled(entries.map((entry) => this.closeEntry(entry, "server_shutdown")))
205
+ .then(() => undefined);
206
+ return this.closePromise;
207
+ }
208
+ get size() {
209
+ return this.entries.size;
210
+ }
211
+ /**
212
+ * Force-close the runtime serving a runtime key (used for agent
213
+ * stop/cancel). Any in-flight provider run on that runtime fails as a
214
+ * consequence; the caller owns translating that into a terminal status.
215
+ * Returns false when no live runtime exists for the key.
216
+ */
217
+ async cancelRuntimeKey(key, reason) {
218
+ const entry = this.entries.get(key);
219
+ if (!entry)
220
+ return false;
221
+ if (entry.createPromise) {
222
+ // Wait for single-flight creation to settle so closeEntry always sees
223
+ // either a runtime or a creation failure instead of racing it.
224
+ await entry.createPromise.catch(() => undefined);
225
+ }
226
+ await this.removeAndClose(entry, reason === "agent_paused" || reason === "agent_stopped" ? "agent_cancelled" : reason);
227
+ return true;
228
+ }
229
+ async acquire(driver, context) {
230
+ const key = driver.runtimeKey(context);
231
+ while (true) {
232
+ const existing = this.entries.get(key);
233
+ if (existing && !existing.closing) {
234
+ if (!existing.runtime || existing.runtime.isAlive()) {
235
+ this.clearIdleTimer(existing);
236
+ if (existing.runtime) {
237
+ this.log("info", "harness_runtime_reused", {
238
+ provider: driver.provider,
239
+ runtimeKeyHash: hashRuntimeKey(key),
240
+ agentId: context.agentId,
241
+ });
242
+ }
243
+ const created = await existing.createPromise;
244
+ if (created.isErr())
245
+ return created;
246
+ if (!this.closing &&
247
+ !existing.closing &&
248
+ this.entries.get(key) === existing &&
249
+ existing.runtime?.isAlive()) {
250
+ return Result.ok(existing);
251
+ }
252
+ }
253
+ await this.removeAndClose(existing, "runtime_not_alive");
254
+ continue;
255
+ }
256
+ if (this.closing)
257
+ return Result.err(poolClosedError(driver, context));
258
+ let entry;
259
+ const createPromise = Promise.resolve()
260
+ .then(() => driver.createRuntime(context))
261
+ .then((result) => {
262
+ if (result.isErr()) {
263
+ if (this.entries.get(key) === entry)
264
+ this.entries.delete(key);
265
+ return result;
266
+ }
267
+ const runtime = result.value;
268
+ entry.runtime = runtime;
269
+ entry.lastUsedAt = this.now();
270
+ this.log("info", "harness_runtime_started", {
271
+ provider: driver.provider,
272
+ runtimeKeyHash: hashRuntimeKey(key),
273
+ agentId: context.agentId,
274
+ });
275
+ return result;
276
+ })
277
+ .catch((error) => {
278
+ if (this.entries.get(key) === entry)
279
+ this.entries.delete(key);
280
+ throw error;
281
+ });
282
+ entry = {
283
+ key,
284
+ driver,
285
+ idleTimeoutMs: driver.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS,
286
+ sessionIdleTimeoutMs: this.sessionIdleTimeoutMs,
287
+ createPromise,
288
+ activeRuns: 0,
289
+ lastUsedAt: this.now(),
290
+ closing: false,
291
+ sessions: new Map(),
292
+ activeRunWaiters: new Set(),
293
+ };
294
+ this.entries.set(key, entry);
295
+ const created = await createPromise;
296
+ if (created.isErr())
297
+ return created;
298
+ if (this.closing || entry.closing || this.entries.get(key) !== entry) {
299
+ await this.closeEntry(entry, "pool_shutdown_during_creation");
300
+ return Result.err(poolClosedError(driver, context));
301
+ }
302
+ return Result.ok(entry);
303
+ }
304
+ }
305
+ scheduleIdleClose(entry) {
306
+ this.clearIdleTimer(entry);
307
+ if (!Number.isFinite(entry.idleTimeoutMs) || entry.idleTimeoutMs <= 0)
308
+ return;
309
+ entry.idleTimer = setTimeout(() => {
310
+ void this.evictIdle().catch((error) => {
311
+ this.log("warn", "harness_runtime_close_failed", {
312
+ provider: entry.driver.provider,
313
+ runtimeKeyHash: hashRuntimeKey(entry.key),
314
+ reason: "idle_timeout",
315
+ error: errorMessage(error),
316
+ });
317
+ });
318
+ }, entry.idleTimeoutMs);
319
+ entry.idleTimer.unref();
320
+ }
321
+ clearIdleTimer(entry) {
322
+ if (!entry.idleTimer)
323
+ return;
324
+ clearTimeout(entry.idleTimer);
325
+ entry.idleTimer = undefined;
326
+ }
327
+ async removeAndClose(entry, reason) {
328
+ if (this.entries.get(entry.key) === entry)
329
+ this.entries.delete(entry.key);
330
+ await this.closeEntry(entry, reason);
331
+ }
332
+ async closeEntry(entry, reason) {
333
+ if (entry.closePromise)
334
+ return entry.closePromise;
335
+ entry.closing = true;
336
+ this.clearIdleTimer(entry);
337
+ entry.closePromise = (async () => {
338
+ let runtime;
339
+ try {
340
+ const created = await entry.createPromise;
341
+ if (created.isErr())
342
+ return;
343
+ runtime = created.value;
344
+ }
345
+ catch {
346
+ return;
347
+ }
348
+ if (!runtime)
349
+ return;
350
+ if (reason !== "server_shutdown"
351
+ && reason !== "runtime_crashed"
352
+ && reason !== "runtime_not_alive"
353
+ && reason !== "agent_cancelled") {
354
+ await this.waitForNoActiveRuns(entry);
355
+ }
356
+ if (reason === "server_shutdown") {
357
+ // Shutdown is terminal for the provider runtime. Closing it first
358
+ // aborts stuck turns and avoids waiting forever before process cleanup.
359
+ // Do not start new individual releases here: that would race an active
360
+ // turn, and the provider runtime owns their final cleanup. Existing
361
+ // idle-release work is awaited so provider cleanup never overlaps it.
362
+ await this.waitForSessionReleases(entry);
363
+ try {
364
+ await runtime.close();
365
+ this.log("info", "harness_runtime_closed", {
366
+ provider: entry.driver.provider,
367
+ runtimeKeyHash: hashRuntimeKey(entry.key),
368
+ reason,
369
+ });
370
+ }
371
+ catch (error) {
372
+ this.log("warn", "harness_runtime_close_failed", {
373
+ provider: entry.driver.provider,
374
+ runtimeKeyHash: hashRuntimeKey(entry.key),
375
+ reason,
376
+ error: errorMessage(error),
377
+ });
378
+ }
379
+ entry.sessions.clear();
380
+ return;
381
+ }
382
+ await this.releaseSessions(entry, runtime, reason);
383
+ try {
384
+ await runtime.close();
385
+ this.log("info", "harness_runtime_closed", {
386
+ provider: entry.driver.provider,
387
+ runtimeKeyHash: hashRuntimeKey(entry.key),
388
+ reason,
389
+ });
390
+ }
391
+ catch (error) {
392
+ this.log("warn", "harness_runtime_close_failed", {
393
+ provider: entry.driver.provider,
394
+ runtimeKeyHash: hashRuntimeKey(entry.key),
395
+ reason,
396
+ error: errorMessage(error),
397
+ });
398
+ throw error;
399
+ }
400
+ })();
401
+ return entry.closePromise;
402
+ }
403
+ async releaseIdleSessions(entry, now) {
404
+ const releases = [];
405
+ for (const [providerSessionId, session] of entry.sessions) {
406
+ if (session.activeRuns > 0 || now - session.lastUsedAt < entry.sessionIdleTimeoutMs)
407
+ continue;
408
+ releases.push(this.releaseSession(entry, providerSessionId));
409
+ }
410
+ await Promise.all(releases);
411
+ }
412
+ async releaseSessions(entry, runtime, reason) {
413
+ const releases = Array.from(entry.sessions.keys()).map((providerSessionId) => this.releaseSession(entry, providerSessionId, runtime, reason));
414
+ await Promise.all(releases);
415
+ entry.sessions.clear();
416
+ }
417
+ async releaseSession(entry, providerSessionId, runtime = entry.runtime, reason = "idle_timeout") {
418
+ const session = entry.sessions.get(providerSessionId);
419
+ if (!runtime || !session)
420
+ return;
421
+ if (entry.closing && reason === "idle_timeout")
422
+ return;
423
+ if (session.releasePromise)
424
+ return session.releasePromise;
425
+ const releasePromise = (async () => {
426
+ try {
427
+ await runtime.releaseSession(providerSessionId);
428
+ if (entry.sessions.get(providerSessionId) === session && session.activeRuns === 0) {
429
+ entry.sessions.delete(providerSessionId);
430
+ }
431
+ }
432
+ catch (error) {
433
+ this.log("warn", "harness_session_release_failed", {
434
+ provider: entry.driver.provider,
435
+ runtimeKeyHash: hashRuntimeKey(entry.key),
436
+ providerSessionIdPrefix: providerSessionId.slice(0, 8),
437
+ reason,
438
+ error: errorMessage(error),
439
+ });
440
+ }
441
+ })();
442
+ session.releasePromise = releasePromise;
443
+ try {
444
+ await releasePromise;
445
+ }
446
+ finally {
447
+ if (entry.sessions.get(providerSessionId) === session)
448
+ session.releasePromise = undefined;
449
+ }
450
+ }
451
+ async waitForNoActiveRuns(entry) {
452
+ if (entry.activeRuns === 0)
453
+ return;
454
+ await new Promise((resolve) => entry.activeRunWaiters.add(resolve));
455
+ }
456
+ async waitForSessionReleases(entry) {
457
+ const releases = Array.from(entry.sessions.values())
458
+ .map((session) => session.releasePromise)
459
+ .filter((release) => Boolean(release));
460
+ await Promise.all(releases);
461
+ }
462
+ log(level, event, fields) {
463
+ this.logger?.(level, event, fields);
464
+ }
465
+ }
466
+ function poolClosedError(driver, context) {
467
+ return new AgentProviderUnavailableError({
468
+ code: "PROVIDER_UNAVAILABLE",
469
+ provider: driver.provider,
470
+ agentId: context.agentId,
471
+ operation: "acquire_runtime",
472
+ retryable: true,
473
+ message: "Local agent runtime pool is closed.",
474
+ });
475
+ }
476
+ function hashRuntimeKey(key) {
477
+ return createHash("sha256").update(key).digest("hex").slice(0, 12);
478
+ }
479
+ function errorMessage(error) {
480
+ return error instanceof Error ? error.message : String(error);
481
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,193 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import { Result } from "better-result";
4
+ import { openDatabase } from "./db/client.js";
5
+ import { AgentStoreError, isProgrammerDefect } from "./local-agent-errors.js";
6
+ export class LocalAgentStore {
7
+ database;
8
+ constructor(stateDir) {
9
+ this.database = openDatabase(stateDir);
10
+ }
11
+ list(scope = {}) {
12
+ let rows;
13
+ if (scope.workspaceId && scope.workspaceRoot) {
14
+ rows = this.database.sqlite
15
+ .prepare(`select * from local_agent_sessions
16
+ where workspace_id = ? and workspace_root = ?
17
+ order by updated_at desc`)
18
+ .all(scope.workspaceId, resolve(scope.workspaceRoot));
19
+ }
20
+ else if (scope.workspaceId) {
21
+ rows = this.database.sqlite
22
+ .prepare(`select * from local_agent_sessions
23
+ where workspace_id = ?
24
+ order by updated_at desc`)
25
+ .all(scope.workspaceId);
26
+ }
27
+ else if (scope.workspaceRoot) {
28
+ rows = this.database.sqlite
29
+ .prepare(`select * from local_agent_sessions
30
+ where workspace_root = ?
31
+ order by updated_at desc`)
32
+ .all(resolve(scope.workspaceRoot));
33
+ }
34
+ else {
35
+ rows = this.database.sqlite
36
+ .prepare("select * from local_agent_sessions order by updated_at desc")
37
+ .all();
38
+ }
39
+ return rows.map(rowToLocalAgentRecord);
40
+ }
41
+ listResult(scope = {}) {
42
+ return storeResult("list", () => this.list(scope));
43
+ }
44
+ create(input) {
45
+ const now = new Date().toISOString();
46
+ const record = {
47
+ id: `agt_${randomUUID().replaceAll("-", "").slice(0, 8)}`,
48
+ workspaceId: input.workspaceId,
49
+ workspaceRoot: resolve(input.workspaceRoot),
50
+ profileName: input.profileName,
51
+ provider: input.provider,
52
+ model: input.model,
53
+ effort: input.effort,
54
+ status: "starting",
55
+ createdAt: now,
56
+ updatedAt: now,
57
+ };
58
+ this.database.sqlite
59
+ .prepare(`insert into local_agent_sessions (
60
+ id,
61
+ workspace_id,
62
+ workspace_root,
63
+ profile_name,
64
+ provider,
65
+ model,
66
+ effort,
67
+ status,
68
+ created_at,
69
+ updated_at
70
+ ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
71
+ .run(record.id, record.workspaceId ?? null, record.workspaceRoot, record.profileName, record.provider, record.model ?? null, record.effort ?? null, record.status, record.createdAt, record.updatedAt);
72
+ return record;
73
+ }
74
+ createResult(input) {
75
+ return storeResult("create", () => this.create(input));
76
+ }
77
+ getById(id) {
78
+ const exact = this.database.sqlite
79
+ .prepare(`select * from local_agent_sessions
80
+ where id = ?
81
+ limit 1`)
82
+ .get(id);
83
+ return exact ? rowToLocalAgentRecord(exact) : undefined;
84
+ }
85
+ getByIdResult(id) {
86
+ return storeResult("get", () => this.getById(id));
87
+ }
88
+ /**
89
+ * Compatibility alias for callers that already use the store directly.
90
+ * Identity lookup is exact and never falls back to provider session IDs.
91
+ */
92
+ get(id) {
93
+ return this.getById(id);
94
+ }
95
+ update(id, patch) {
96
+ const current = this.getById(id);
97
+ if (!current)
98
+ throw new Error(`Unknown subagent id: ${id}`);
99
+ const updated = {
100
+ ...current,
101
+ ...patch,
102
+ updatedAt: new Date().toISOString(),
103
+ };
104
+ this.database.sqlite
105
+ .prepare(`update local_agent_sessions set
106
+ workspace_id = ?,
107
+ workspace_root = ?,
108
+ profile_name = ?,
109
+ provider = ?,
110
+ model = ?,
111
+ effort = ?,
112
+ provider_session_id = ?,
113
+ status = ?,
114
+ latest_response = ?,
115
+ latest_output = ?,
116
+ error = ?,
117
+ error_code = ?,
118
+ error_retryable = ?,
119
+ updated_at = ?
120
+ where id = ?`)
121
+ .run(updated.workspaceId ?? null, resolve(updated.workspaceRoot), updated.profileName, updated.provider, updated.model ?? null, updated.effort ?? null, updated.providerSessionId ?? null, updated.status, updated.latestResponse ?? null, updated.latestOutput ?? null, updated.error ?? null, updated.errorCode ?? null, updated.errorRetryable === undefined ? null : String(updated.errorRetryable), updated.updatedAt, updated.id);
122
+ return updated;
123
+ }
124
+ updateResult(id, patch) {
125
+ return storeResult("update", () => this.update(id, patch));
126
+ }
127
+ reconcileActiveRuns(message = "Hearth restarted while this agent turn was running.") {
128
+ const now = new Date().toISOString();
129
+ const result = this.database.sqlite
130
+ .prepare(`update local_agent_sessions
131
+ set status = 'error', error = ?, error_code = 'DAEMON_UNAVAILABLE', error_retryable = 'true', updated_at = ?
132
+ where status in ('starting', 'running')`)
133
+ .run(message, now);
134
+ return Number(result.changes);
135
+ }
136
+ reconcileActiveRunsResult(message = "Hearth restarted while this agent turn was running.") {
137
+ return storeResult("reconcile_active_runs", () => this.reconcileActiveRuns(message));
138
+ }
139
+ close() {
140
+ this.database.close();
141
+ }
142
+ }
143
+ export function createLocalAgentStore(stateDir) {
144
+ return new LocalAgentStore(stateDir);
145
+ }
146
+ function rowToLocalAgentRecord(row) {
147
+ return {
148
+ id: row.id,
149
+ workspaceId: row.workspace_id ?? undefined,
150
+ workspaceRoot: row.workspace_root,
151
+ profileName: row.profile_name,
152
+ provider: row.provider,
153
+ model: row.model ?? undefined,
154
+ effort: row.effort ?? undefined,
155
+ providerSessionId: row.provider_session_id ?? undefined,
156
+ status: readStatus(row.status),
157
+ latestResponse: row.latest_response ?? undefined,
158
+ latestOutput: row.latest_output ?? undefined,
159
+ error: row.error ?? undefined,
160
+ errorCode: row.error_code ?? undefined,
161
+ errorRetryable: readOptionalBoolean(row.error_retryable),
162
+ createdAt: row.created_at,
163
+ updatedAt: row.updated_at,
164
+ };
165
+ }
166
+ function readOptionalBoolean(value) {
167
+ if (value === "true")
168
+ return true;
169
+ if (value === "false")
170
+ return false;
171
+ return undefined;
172
+ }
173
+ function storeResult(operation, run) {
174
+ try {
175
+ return Result.ok(run());
176
+ }
177
+ catch (cause) {
178
+ if (isProgrammerDefect(cause))
179
+ throw cause;
180
+ return Result.err(new AgentStoreError(operation, cause));
181
+ }
182
+ }
183
+ function readStatus(status) {
184
+ if (status === "starting" ||
185
+ status === "running" ||
186
+ status === "idle" ||
187
+ status === "paused" ||
188
+ status === "error" ||
189
+ status === "stopped") {
190
+ return status;
191
+ }
192
+ return "error";
193
+ }