@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.
- package/LICENSE +21 -0
- package/README.md +323 -0
- package/bin/hearth-agentd.js +4 -0
- package/bin/hearth.js +4 -0
- package/bin/run-entrypoint.js +20 -0
- package/dist/agent-tools.js +738 -0
- package/dist/app-version.js +16 -0
- package/dist/apply-patch.js +402 -0
- package/dist/artifact-error.js +8 -0
- package/dist/artifact-tools.js +377 -0
- package/dist/cli-workspace.js +36 -0
- package/dist/cli.js +1086 -0
- package/dist/config-migration.js +82 -0
- package/dist/config-schema.js +136 -0
- package/dist/config.js +115 -0
- package/dist/context/context-tools.js +150 -0
- package/dist/context/repo-map.js +102 -0
- package/dist/dashboard/app.js +226 -0
- package/dist/dashboard/index.html +45 -0
- package/dist/dashboard/landing.html +57 -0
- package/dist/dashboard/styles.css +23 -0
- package/dist/dashboard.js +271 -0
- package/dist/db/client.js +29 -0
- package/dist/db/migrations.js +262 -0
- package/dist/db/schema.js +85 -0
- package/dist/device-tokens.js +61 -0
- package/dist/git-worktrees.js +134 -0
- package/dist/git.js +41 -0
- package/dist/incoming-artifacts.js +378 -0
- package/dist/local-agent-acp.js +768 -0
- package/dist/local-agent-adapters.js +20 -0
- package/dist/local-agent-availability.js +92 -0
- package/dist/local-agent-catalog.js +51 -0
- package/dist/local-agent-claude.js +334 -0
- package/dist/local-agent-client.js +519 -0
- package/dist/local-agent-codex.js +532 -0
- package/dist/local-agent-config.js +34 -0
- package/dist/local-agent-daemon-lifecycle.js +195 -0
- package/dist/local-agent-daemon-main.js +86 -0
- package/dist/local-agent-daemon-protocol.js +302 -0
- package/dist/local-agent-daemon.js +407 -0
- package/dist/local-agent-errors.js +378 -0
- package/dist/local-agent-grok.js +202 -0
- package/dist/local-agent-manager.js +715 -0
- package/dist/local-agent-opencode.js +463 -0
- package/dist/local-agent-path.js +24 -0
- package/dist/local-agent-pi-sandbox.js +472 -0
- package/dist/local-agent-pi.js +298 -0
- package/dist/local-agent-presentation.js +104 -0
- package/dist/local-agent-profiles.js +127 -0
- package/dist/local-agent-runtime-pool.js +481 -0
- package/dist/local-agent-runtime.js +1 -0
- package/dist/local-agent-store.js +193 -0
- package/dist/local-agent-targets.js +97 -0
- package/dist/logger.js +97 -0
- package/dist/machine-id.js +35 -0
- package/dist/mcp-modern-server.js +65 -0
- package/dist/oauth-provider.js +331 -0
- package/dist/oauth-store.js +144 -0
- package/dist/onboarding.js +35 -0
- package/dist/orchestration/brief.js +70 -0
- package/dist/orchestration/lanes.js +145 -0
- package/dist/orchestration/result.js +46 -0
- package/dist/orchestration/scope.js +61 -0
- package/dist/pi-tools.js +58 -0
- package/dist/policy/command-policy.js +104 -0
- package/dist/policy/env-allowlist.js +96 -0
- package/dist/policy/sandbox.js +68 -0
- package/dist/policy/workspace-profiles.js +47 -0
- package/dist/process-journal.js +116 -0
- package/dist/process-platform.js +49 -0
- package/dist/process-sessions.js +388 -0
- package/dist/rate-limit.js +77 -0
- package/dist/request-meta.js +9 -0
- package/dist/review-checkpoints.js +349 -0
- package/dist/roots.js +37 -0
- package/dist/server-shutdown.js +12 -0
- package/dist/server.js +980 -0
- package/dist/skills.js +84 -0
- package/dist/snapshot-manager.js +123 -0
- package/dist/stdio-server.js +80 -0
- package/dist/task-store.js +203 -0
- package/dist/task-tools.js +455 -0
- package/dist/tool-surfaces/claude.js +220 -0
- package/dist/tool-surfaces/codex.js +255 -0
- package/dist/tool-surfaces/index.js +15 -0
- package/dist/tool-surfaces/shared.js +228 -0
- package/dist/tool-surfaces/types.js +27 -0
- package/dist/ui/.vite/manifest.json +2544 -0
- package/dist/ui/assets/abap-CLvhMVsD.js +1 -0
- package/dist/ui/assets/actionscript-3--17pq3dv.js +1 -0
- package/dist/ui/assets/ada-C5qYipkI.js +1 -0
- package/dist/ui/assets/andromeeda-vGVdxbeo.js +1 -0
- package/dist/ui/assets/angular-html-DrbO3_Xe.js +1 -0
- package/dist/ui/assets/angular-ts-CmTVzHJx.js +1 -0
- package/dist/ui/assets/apache-U0d_L8uA.js +1 -0
- package/dist/ui/assets/apex-CGTLDQj6.js +1 -0
- package/dist/ui/assets/apl-BOpCpqXf.js +1 -0
- package/dist/ui/assets/applescript-CCn79oCD.js +1 -0
- package/dist/ui/assets/ara-4CJ0cIlV.js +1 -0
- package/dist/ui/assets/asciidoc-DE70LPWp.js +1 -0
- package/dist/ui/assets/asm-Cmm7eHzH.js +1 -0
- package/dist/ui/assets/astro-CxPeBwei.js +1 -0
- package/dist/ui/assets/aurora-x-CDeNXAV0.js +1 -0
- package/dist/ui/assets/awk-BWXHIvNe.js +1 -0
- package/dist/ui/assets/ayu-dark-DluEY0Gj.js +1 -0
- package/dist/ui/assets/ayu-light-C3h-C4tm.js +1 -0
- package/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +1 -0
- package/dist/ui/assets/ballerina-B7ZEbQpA.js +1 -0
- package/dist/ui/assets/bat-Bo4NYOV-.js +1 -0
- package/dist/ui/assets/beancount-D-usSTwE.js +1 -0
- package/dist/ui/assets/berry-DKpUyyne.js +1 -0
- package/dist/ui/assets/bibtex-Ci_nEsc7.js +1 -0
- package/dist/ui/assets/bicep-CUHmPFLl.js +1 -0
- package/dist/ui/assets/bird2-C2hNVINV.js +1 -0
- package/dist/ui/assets/blade-DtsAAG4P.js +1 -0
- package/dist/ui/assets/bsl-BkkzgIyY.js +1 -0
- package/dist/ui/assets/c-D9hLjNUM.js +1 -0
- package/dist/ui/assets/c3-CnJL0r0V.js +1 -0
- package/dist/ui/assets/cadence-CQ2zXKGN.js +1 -0
- package/dist/ui/assets/cairo-DLTphjLi.js +1 -0
- package/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
- package/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
- package/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
- package/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
- package/dist/ui/assets/clarity-SemFz856.js +1 -0
- package/dist/ui/assets/clojure-DqKBuwfJ.js +1 -0
- package/dist/ui/assets/cmake-Bj61d0ZC.js +1 -0
- package/dist/ui/assets/cobol-C1SWlvZm.js +1 -0
- package/dist/ui/assets/codeowners-C8r90Shi.js +1 -0
- package/dist/ui/assets/codeql-oeQT6MSM.js +1 -0
- package/dist/ui/assets/coffee-B71R3v2U.js +1 -0
- package/dist/ui/assets/common-lisp-Cv5bFMCO.js +1 -0
- package/dist/ui/assets/coq-BrsZFFmf.js +1 -0
- package/dist/ui/assets/cpp--5a4c3v4.js +1 -0
- package/dist/ui/assets/crystal-AtqWYtE0.js +1 -0
- package/dist/ui/assets/csharp-Ct8U2NOr.js +1 -0
- package/dist/ui/assets/css-CbqKSHI-.js +1 -0
- package/dist/ui/assets/csv-Dx-8-gkx.js +1 -0
- package/dist/ui/assets/cue-CE9AQfxI.js +1 -0
- package/dist/ui/assets/cypher-ClKdZ_lG.js +1 -0
- package/dist/ui/assets/d-qD-0Kul2.js +1 -0
- package/dist/ui/assets/dark-plus-Cs2F2srj.js +1 -0
- package/dist/ui/assets/dart-DkHntEIa.js +1 -0
- package/dist/ui/assets/dax-BkyTk9wS.js +1 -0
- package/dist/ui/assets/desktop-Dlh5hvp9.js +1 -0
- package/dist/ui/assets/diff-woXpYk--.js +1 -0
- package/dist/ui/assets/docker-IyjqRm3v.js +1 -0
- package/dist/ui/assets/dotenv-_5a1GRtc.js +1 -0
- package/dist/ui/assets/dracula-BHWKrbxM.js +1 -0
- package/dist/ui/assets/dracula-soft-5eyTD99u.js +1 -0
- package/dist/ui/assets/dream-maker-DW3nJb8Q.js +1 -0
- package/dist/ui/assets/edge-BPwY_RrJ.js +1 -0
- package/dist/ui/assets/elixir-DVH2yLJf.js +1 -0
- package/dist/ui/assets/elm-BrleA6gS.js +1 -0
- package/dist/ui/assets/emacs-lisp-C9PiwqqW.js +1 -0
- package/dist/ui/assets/erb-2SHiYe30.js +1 -0
- package/dist/ui/assets/erlang-Cphh6RMH.js +1 -0
- package/dist/ui/assets/everforest-dark-sB-x3p7T.js +1 -0
- package/dist/ui/assets/everforest-light-Df2xbC6M.js +1 -0
- package/dist/ui/assets/fennel-DQxkIbk2.js +1 -0
- package/dist/ui/assets/fish-BJitypiv.js +1 -0
- package/dist/ui/assets/fluent-C03EYrpw.js +1 -0
- package/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
- package/dist/ui/assets/fortran-free-form-CYNrtFtB.js +1 -0
- package/dist/ui/assets/fsharp-D13ZGOAj.js +1 -0
- package/dist/ui/assets/gdresource-C0sCabJj.js +1 -0
- package/dist/ui/assets/gdscript-Cp2uCuqX.js +1 -0
- package/dist/ui/assets/gdshader-CBce3t8t.js +1 -0
- package/dist/ui/assets/genie-CV2tkWYe.js +1 -0
- package/dist/ui/assets/gherkin-DExj1W_8.js +1 -0
- package/dist/ui/assets/git-commit-BSykSTBG.js +1 -0
- package/dist/ui/assets/git-rebase-Bc7atMaV.js +1 -0
- package/dist/ui/assets/github-dark-C-LZuMrd.js +1 -0
- package/dist/ui/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/dist/ui/assets/github-light-EUqPIrTm.js +1 -0
- package/dist/ui/assets/github-light-default-BXViO-2h.js +1 -0
- package/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +1 -0
- package/dist/ui/assets/gleam-CSRkHgEL.js +1 -0
- package/dist/ui/assets/glimmer-js-DXeTDPtW.js +1 -0
- package/dist/ui/assets/glimmer-ts-CEOoXHU4.js +1 -0
- package/dist/ui/assets/glsl-5-43-Gsg.js +1 -0
- package/dist/ui/assets/gn-ilITqXS6.js +1 -0
- package/dist/ui/assets/gnuplot-7GGW24-e.js +1 -0
- package/dist/ui/assets/go-BJwz_mda.js +1 -0
- package/dist/ui/assets/graphql-Bk7Ah1KA.js +1 -0
- package/dist/ui/assets/groovy-CacY0gHj.js +1 -0
- package/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/dist/ui/assets/hack-DUHz4rCJ.js +1 -0
- package/dist/ui/assets/haml-BPCuuTPR.js +1 -0
- package/dist/ui/assets/handlebars-CHxmUsnU.js +1 -0
- package/dist/ui/assets/haskell-D8IpX4py.js +1 -0
- package/dist/ui/assets/haxe-OTjmBuCE.js +1 -0
- package/dist/ui/assets/hcl-Dh228itO.js +1 -0
- package/dist/ui/assets/hjson-CxZEssPk.js +1 -0
- package/dist/ui/assets/hlsl-Cvrh5tZx.js +1 -0
- package/dist/ui/assets/horizon-CE9ld1lL.js +1 -0
- package/dist/ui/assets/horizon-bright-Br1oVSNq.js +1 -0
- package/dist/ui/assets/houston-CsvMBhTu.js +1 -0
- package/dist/ui/assets/html-BBJqizz0.js +1 -0
- package/dist/ui/assets/html-derivative-ShTPCcKN.js +1 -0
- package/dist/ui/assets/http-VFTyziwA.js +1 -0
- package/dist/ui/assets/hurl-B8yxTa4r.js +1 -0
- package/dist/ui/assets/hxml-B0Qn7Nwc.js +1 -0
- package/dist/ui/assets/hy-CZbG8q4J.js +1 -0
- package/dist/ui/assets/imba-DsUTQ-LC.js +1 -0
- package/dist/ui/assets/ini-B5eOa1yu.js +1 -0
- package/dist/ui/assets/java-BZ5DAgJZ.js +1 -0
- package/dist/ui/assets/javascript-C_zuIEaC.js +1 -0
- package/dist/ui/assets/jinja-DWd6xfNr.js +1 -0
- package/dist/ui/assets/jison-BbY63d1V.js +1 -0
- package/dist/ui/assets/json-C4DnajZs.js +1 -0
- package/dist/ui/assets/json5-BR5RXkoi.js +1 -0
- package/dist/ui/assets/jsonc-CYpm1nAK.js +1 -0
- package/dist/ui/assets/jsonl-CmCQp5Yx.js +1 -0
- package/dist/ui/assets/jsonnet-CJTPZ8u_.js +1 -0
- package/dist/ui/assets/jssm-DXw9l8Rf.js +1 -0
- package/dist/ui/assets/jsx-C4AACieZ.js +1 -0
- package/dist/ui/assets/julia-sRu7EFMW.js +1 -0
- package/dist/ui/assets/just-DpZ9Sx1v.js +1 -0
- package/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
- package/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +1 -0
- package/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +1 -0
- package/dist/ui/assets/kdl-CsD5j6eV.js +1 -0
- package/dist/ui/assets/kotlin-DhhofPvG.js +1 -0
- package/dist/ui/assets/kusto-BUv0MjJC.js +1 -0
- package/dist/ui/assets/laserwave-C_8bwKvT.js +1 -0
- package/dist/ui/assets/latex-B6a_uum7.js +1 -0
- package/dist/ui/assets/lean-CewbzKMR.js +1 -0
- package/dist/ui/assets/less-DVTAwKKz.js +1 -0
- package/dist/ui/assets/light-plus-DVQuIRkW.js +1 -0
- package/dist/ui/assets/liquid-B2sVpjjr.js +1 -0
- package/dist/ui/assets/llvm-Cm23YOpf.js +1 -0
- package/dist/ui/assets/log-BNLmms1o.js +1 -0
- package/dist/ui/assets/logo-Cluzi2Zq.js +1 -0
- package/dist/ui/assets/lua-DxJJll61.js +1 -0
- package/dist/ui/assets/luau-CNKltnaQ.js +1 -0
- package/dist/ui/assets/make-Dixweg8N.js +1 -0
- package/dist/ui/assets/markdown-BYOwaDjH.js +1 -0
- package/dist/ui/assets/marko-Dx8mw-Wx.js +1 -0
- package/dist/ui/assets/material-theme-Bm3Qr25_.js +1 -0
- package/dist/ui/assets/material-theme-darker-2IIEA8gg.js +1 -0
- package/dist/ui/assets/material-theme-lighter-uhdI0v04.js +1 -0
- package/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +1 -0
- package/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +1 -0
- package/dist/ui/assets/matlab-D7qyCx1q.js +1 -0
- package/dist/ui/assets/mdc-vNKeXEwR.js +1 -0
- package/dist/ui/assets/mdx-DQZ5AkYe.js +1 -0
- package/dist/ui/assets/mermaid-Bk4SNUv9.js +1 -0
- package/dist/ui/assets/min-dark-BSWPekZh.js +1 -0
- package/dist/ui/assets/min-light-DDpmG2fV.js +1 -0
- package/dist/ui/assets/mipsasm-BMqwQI7S.js +1 -0
- package/dist/ui/assets/mojo-BgCJLMeH.js +1 -0
- package/dist/ui/assets/monokai-CdkpiU2Y.js +1 -0
- package/dist/ui/assets/moonbit-CaWjb8XO.js +1 -0
- package/dist/ui/assets/move-B1IS1UjX.js +1 -0
- package/dist/ui/assets/narrat-_X_XdTYD.js +1 -0
- package/dist/ui/assets/nextflow-Bbiyy34d.js +1 -0
- package/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +1 -0
- package/dist/ui/assets/nginx-DScQAGLT.js +1 -0
- package/dist/ui/assets/night-owl-DhmEMT88.js +1 -0
- package/dist/ui/assets/night-owl-light-eJ-hLW7d.js +1 -0
- package/dist/ui/assets/nim-lAJch9uT.js +1 -0
- package/dist/ui/assets/nix-IvuFDN5E.js +1 -0
- package/dist/ui/assets/nord-Cb4Vim4T.js +1 -0
- package/dist/ui/assets/nushell-DcLAeLz5.js +1 -0
- package/dist/ui/assets/objective-c-D1A_Heim.js +1 -0
- package/dist/ui/assets/objective-cpp-BsSzOQcm.js +1 -0
- package/dist/ui/assets/ocaml-O90oeIOV.js +1 -0
- package/dist/ui/assets/odin-B1RWQWA5.js +1 -0
- package/dist/ui/assets/one-dark-pro-CLwyXe_n.js +1 -0
- package/dist/ui/assets/one-light-D7Lr4KcI.js +1 -0
- package/dist/ui/assets/openscad-BUDT5pXO.js +1 -0
- package/dist/ui/assets/pascal-4ZHwLPI5.js +1 -0
- package/dist/ui/assets/perl-C_GkVo_E.js +1 -0
- package/dist/ui/assets/php-DziV3K6J.js +1 -0
- package/dist/ui/assets/pierre-dark-CpLgRqie.js +1 -0
- package/dist/ui/assets/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
- package/dist/ui/assets/pierre-dark-soft-kZQmAZld.js +1 -0
- package/dist/ui/assets/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
- package/dist/ui/assets/pierre-dark-vibrant-CpQYzh95.js +1 -0
- package/dist/ui/assets/pierre-light-CoaEpmwp.js +1 -0
- package/dist/ui/assets/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
- package/dist/ui/assets/pierre-light-soft-lWLdNTOI.js +1 -0
- package/dist/ui/assets/pierre-light-tritanopia-CEbqgOJL.js +1 -0
- package/dist/ui/assets/pierre-light-vibrant-D80Fkn33.js +1 -0
- package/dist/ui/assets/pkl-ot-7Btpt.js +1 -0
- package/dist/ui/assets/plastic-DQwYfKfQ.js +1 -0
- package/dist/ui/assets/plsql-DGHpHOYJ.js +1 -0
- package/dist/ui/assets/po-BiJDBrnU.js +1 -0
- package/dist/ui/assets/poimandres-DRFjx7u4.js +1 -0
- package/dist/ui/assets/polar-C7UOKdEL.js +1 -0
- package/dist/ui/assets/postcss-BXeXVLqQ.js +1 -0
- package/dist/ui/assets/powerquery-DNMTfnFr.js +1 -0
- package/dist/ui/assets/powershell-DshXNtvi.js +1 -0
- package/dist/ui/assets/prisma-BsRQq5mF.js +1 -0
- package/dist/ui/assets/prolog-iXnhIJG7.js +1 -0
- package/dist/ui/assets/proto-DB4EqR-F.js +1 -0
- package/dist/ui/assets/pug-z75UIWVK.js +1 -0
- package/dist/ui/assets/puppet-CDv2pdJW.js +1 -0
- package/dist/ui/assets/purescript-9MfHhQsQ.js +1 -0
- package/dist/ui/assets/python-gzcpVVnB.js +1 -0
- package/dist/ui/assets/qml-Cmm4coz1.js +1 -0
- package/dist/ui/assets/qmldir-DCQb3MpD.js +1 -0
- package/dist/ui/assets/qss-Fe1Jh2GI.js +1 -0
- package/dist/ui/assets/r-Brd0axBE.js +1 -0
- package/dist/ui/assets/racket-DcIDlBhZ.js +1 -0
- package/dist/ui/assets/raku-B3gFvitq.js +1 -0
- package/dist/ui/assets/razor-DSbwmoiu.js +1 -0
- package/dist/ui/assets/red-CJ3rzSJv.js +1 -0
- package/dist/ui/assets/reg-CRGYupPL.js +1 -0
- package/dist/ui/assets/regexp-Bs3Uk-pY.js +1 -0
- package/dist/ui/assets/rel-BtDbiS_P.js +1 -0
- package/dist/ui/assets/review-payload-CbwCBKB1.js +1580 -0
- package/dist/ui/assets/riscv-Ckw8ddFX.js +1 -0
- package/dist/ui/assets/ron-VUp2lXgN.js +1 -0
- package/dist/ui/assets/rose-pine-BthvhNj6.js +1 -0
- package/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
- package/dist/ui/assets/rose-pine-moon-hon4tzzS.js +1 -0
- package/dist/ui/assets/rosmsg-CAekHB0j.js +1 -0
- package/dist/ui/assets/rst-ChIyKpUC.js +1 -0
- package/dist/ui/assets/ruby-BdAmvOMO.js +1 -0
- package/dist/ui/assets/rust-Cfkwpbl8.js +1 -0
- package/dist/ui/assets/sas-DpXn8hc5.js +1 -0
- package/dist/ui/assets/sass-DXrisJhu.js +1 -0
- package/dist/ui/assets/scala-DKOlJaKm.js +1 -0
- package/dist/ui/assets/scheme-DQCgrYNe.js +1 -0
- package/dist/ui/assets/scss-atX1Uuef.js +1 -0
- package/dist/ui/assets/sdbl-bTVj8UrX.js +1 -0
- package/dist/ui/assets/shaderlab-TOUzSsQk.js +1 -0
- package/dist/ui/assets/shellscript-BqgcAeMK.js +1 -0
- package/dist/ui/assets/shellsession-DfQSMRN5.js +1 -0
- package/dist/ui/assets/slack-dark-DnToyrRv.js +1 -0
- package/dist/ui/assets/slack-ochin-B2OO5cIa.js +1 -0
- package/dist/ui/assets/smalltalk-B16xEiuN.js +1 -0
- package/dist/ui/assets/snazzy-light-4G7pJPwS.js +1 -0
- package/dist/ui/assets/solarized-dark-DV17i1UV.js +1 -0
- package/dist/ui/assets/solarized-light-DSh2HLQt.js +1 -0
- package/dist/ui/assets/solidity-CKzVLygQ.js +1 -0
- package/dist/ui/assets/soy-DzP7zZTB.js +1 -0
- package/dist/ui/assets/sparql-D_iOobhT.js +1 -0
- package/dist/ui/assets/splunk-BC2Px7Mm.js +1 -0
- package/dist/ui/assets/sql-gmaLk66z.js +1 -0
- package/dist/ui/assets/ssh-config-BgfXC-Er.js +1 -0
- package/dist/ui/assets/stata-CJ0YTMcV.js +1 -0
- package/dist/ui/assets/stylus-B6D30XZt.js +1 -0
- package/dist/ui/assets/surrealql-LH7FciQz.js +1 -0
- package/dist/ui/assets/svelte-BME2PU6V.js +1 -0
- package/dist/ui/assets/swift-DonLKvLd.js +1 -0
- package/dist/ui/assets/synthwave-84-nFMaYfgc.js +1 -0
- package/dist/ui/assets/system-verilog-DJ5XKQeo.js +1 -0
- package/dist/ui/assets/systemd-BxMlprV5.js +1 -0
- package/dist/ui/assets/talonscript-CohzipZa.js +1 -0
- package/dist/ui/assets/tasl-DMoTqEGO.js +1 -0
- package/dist/ui/assets/tcl-CZd0xW_V.js +1 -0
- package/dist/ui/assets/templ-DHBNIfYW.js +1 -0
- package/dist/ui/assets/terraform-DswuEJGm.js +1 -0
- package/dist/ui/assets/tex-C8-kPEzO.js +1 -0
- package/dist/ui/assets/tokyo-night-oM2G3aXe.js +1 -0
- package/dist/ui/assets/toml-CcmNWLt0.js +1 -0
- package/dist/ui/assets/ts-tags-CiewV0vR.js +1 -0
- package/dist/ui/assets/tsv-sltzmVWM.js +1 -0
- package/dist/ui/assets/tsx-CUYPqmnq.js +1 -0
- package/dist/ui/assets/turtle-ByJddavk.js +1 -0
- package/dist/ui/assets/twig-BTkvsl0b.js +1 -0
- package/dist/ui/assets/typescript-DDQMaPwj.js +1 -0
- package/dist/ui/assets/typespec-B88KGewJ.js +1 -0
- package/dist/ui/assets/typst-DI99ib-x.js +1 -0
- package/dist/ui/assets/v-DETTlOr0.js +1 -0
- package/dist/ui/assets/vala-zf12oZj6.js +1 -0
- package/dist/ui/assets/vb-Djn5o6TS.js +1 -0
- package/dist/ui/assets/verilog-CiiDBU1e.js +1 -0
- package/dist/ui/assets/vesper-D5bVUKB1.js +1 -0
- package/dist/ui/assets/vhdl-BroJfC0k.js +1 -0
- package/dist/ui/assets/viml-DvXPmvsu.js +1 -0
- package/dist/ui/assets/vitesse-black-fwtXNY1n.js +1 -0
- package/dist/ui/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/dist/ui/assets/vitesse-light-VbXTXTou.js +1 -0
- package/dist/ui/assets/vue-BH5SEWRH.js +1 -0
- package/dist/ui/assets/vue-html-BT7yeYez.js +1 -0
- package/dist/ui/assets/vue-vine-Cs5IN2rs.js +1 -0
- package/dist/ui/assets/vyper-CgoNMtux.js +1 -0
- package/dist/ui/assets/wasm-BnjxR4X6.js +1 -0
- package/dist/ui/assets/wasm-ByWQv1Qj.js +1 -0
- package/dist/ui/assets/wenyan-C8pVoKbM.js +1 -0
- package/dist/ui/assets/wgsl-BsKzXJz4.js +1 -0
- package/dist/ui/assets/wikitext-ClFFjSW2.js +1 -0
- package/dist/ui/assets/wit-DdvCle-K.js +1 -0
- package/dist/ui/assets/wolfram-DLL8P-h_.js +1 -0
- package/dist/ui/assets/workspace-app-B_BnSEHO.css +1 -0
- package/dist/ui/assets/workspace-app-DSFpnrIC.js +115 -0
- package/dist/ui/assets/xml-D3_1BEcO.js +1 -0
- package/dist/ui/assets/xsl-BBOCDn4Y.js +1 -0
- package/dist/ui/assets/yaml-lThBDT45.js +1 -0
- package/dist/ui/assets/zenscript-BnlCZFoB.js +1 -0
- package/dist/ui/assets/zig-CMLA9XwU.js +1 -0
- package/dist/ui/workspace-app.html +15 -0
- package/dist/user-config.js +228 -0
- package/dist/verification.js +74 -0
- package/dist/workspace-store.js +133 -0
- package/dist/workspaces.js +433 -0
- package/docs/adr/devspace-x-implementation-adrs.md +159 -0
- package/docs/agent-profile-schema.md +203 -0
- package/docs/artifact-exchange.md +44 -0
- package/docs/assets/devspace-logo-light.png +0 -0
- package/docs/assets/devspace-screenshot.png +0 -0
- package/docs/assets/v11-review-ui-after.png +0 -0
- package/docs/assets/v11-review-ui-before.png +0 -0
- package/docs/chatgpt-coding-workflow.md +215 -0
- package/docs/configuration.md +217 -0
- package/docs/gotchas.md +294 -0
- package/docs/local-agent-daemon.md +101 -0
- package/docs/security.md +197 -0
- package/docs/setup.md +174 -0
- package/examples/agents/claude-implementer.md +30 -0
- package/examples/agents/codex-explorer.md +27 -0
- package/examples/agents/codex-qa-tester.md +29 -0
- package/examples/agents/codex-worker.md +25 -0
- package/examples/agents/copilot-reviewer.md +25 -0
- package/examples/agents/cursor-agent-worker.md +26 -0
- package/examples/agents/grok-builder.md +10 -0
- package/examples/agents/opencode-explorer.md +27 -0
- package/examples/agents/pi-reviewer.md +27 -0
- package/package.json +102 -0
- package/schema/v1/hearth.schema.json +529 -0
- package/scripts/copy-dashboard.mjs +9 -0
- package/scripts/fix-node-pty-permissions.mjs +22 -0
- package/scripts/generate-config-schema.ts +9 -0
- package/skills/subagents/SKILL.md +57 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { AgentProviderProtocolError, AgentProviderUnavailableError, captureAgentProviderResult, } from "./local-agent-errors.js";
|
|
2
|
+
const OPENCODE_SESSION_POLL_INTERVAL_MS = 250;
|
|
3
|
+
const OPENCODE_SESSION_POLL_TIMEOUT_MS = 5 * 60_000;
|
|
4
|
+
/**
|
|
5
|
+
* Allocate a free loopback port for an opencode server instance. The SDK
|
|
6
|
+
* defaults every server to port 4096, so concurrent runtimes (and servers
|
|
7
|
+
* leaked by crashed runs) collide; a distinct port per runtime removes the
|
|
8
|
+
* whole class. Best-effort: a tiny bind-then-close race remains, and a
|
|
9
|
+
* collision surfaces as an unavailable-provider error with retry.
|
|
10
|
+
*/
|
|
11
|
+
export async function allocateLoopbackPort() {
|
|
12
|
+
const { createServer } = await import("node:net");
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const probe = createServer();
|
|
15
|
+
probe.once("error", reject);
|
|
16
|
+
probe.listen(0, "127.0.0.1", () => {
|
|
17
|
+
const address = probe.address();
|
|
18
|
+
const port = typeof address === "object" && address ? address.port : 0;
|
|
19
|
+
probe.close(() => resolve(port));
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export class OpencodeRuntime {
|
|
24
|
+
client;
|
|
25
|
+
server;
|
|
26
|
+
provider = "opencode";
|
|
27
|
+
alive = true;
|
|
28
|
+
closed = false;
|
|
29
|
+
/**
|
|
30
|
+
* Last agent known set on each provider session. The opencode server
|
|
31
|
+
* records an `agent-switched` message per switchAgent call, and redundant
|
|
32
|
+
* switches (e.g. right after create, which already sets the agent) fail
|
|
33
|
+
* server-side — so switch only on actual change.
|
|
34
|
+
*/
|
|
35
|
+
sessionAgents = new Map();
|
|
36
|
+
constructor(client, server) {
|
|
37
|
+
this.client = client;
|
|
38
|
+
this.server = server;
|
|
39
|
+
}
|
|
40
|
+
async run(input, callbacks) {
|
|
41
|
+
return captureAgentProviderResult({
|
|
42
|
+
provider: this.provider,
|
|
43
|
+
operation: "run",
|
|
44
|
+
run: async () => {
|
|
45
|
+
if (!this.alive) {
|
|
46
|
+
throw new AgentProviderUnavailableError({
|
|
47
|
+
code: "PROVIDER_UNAVAILABLE",
|
|
48
|
+
provider: this.provider,
|
|
49
|
+
operation: "run",
|
|
50
|
+
retryable: true,
|
|
51
|
+
message: "OpenCode runtime is not running.",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
await assertOpencodeHealthy(this.client);
|
|
56
|
+
const resumed = Boolean(input.providerSessionId);
|
|
57
|
+
const initialModel = input.model ? parseOpencodeModel(input.model, input.effort) : undefined;
|
|
58
|
+
const desiredAgent = opencodeAgentFor(input.writeMode);
|
|
59
|
+
const sessionId = input.providerSessionId ?? await createOpencodeSession(this.client, input, initialModel);
|
|
60
|
+
if (!input.providerSessionId)
|
|
61
|
+
this.sessionAgents.set(sessionId, desiredAgent);
|
|
62
|
+
await callbacks?.onSessionId?.(sessionId);
|
|
63
|
+
if (this.sessionAgents.get(sessionId) !== desiredAgent) {
|
|
64
|
+
await this.client.v2.session.switchAgent({
|
|
65
|
+
sessionID: sessionId,
|
|
66
|
+
agent: desiredAgent,
|
|
67
|
+
}, { throwOnError: true });
|
|
68
|
+
this.sessionAgents.set(sessionId, desiredAgent);
|
|
69
|
+
}
|
|
70
|
+
const model = initialModel ?? (input.effort ? await modelWithEffort(this.client, sessionId, input.effort) : undefined);
|
|
71
|
+
if (model && (resumed || !initialModel)) {
|
|
72
|
+
await this.client.v2.session.switchModel({ sessionID: sessionId, model }, { throwOnError: true });
|
|
73
|
+
}
|
|
74
|
+
const promptResult = await promptOpencodeSession(this.client, sessionId, input);
|
|
75
|
+
await waitForOpencodeSession(this.client, sessionId, promptResult);
|
|
76
|
+
const promptId = extractOpenCodePromptId(promptResult);
|
|
77
|
+
const messages = await readOpencodeMessages(this.client, sessionId, promptId);
|
|
78
|
+
const finalResponse = requireFinalResponse(extractOpenCodeFinalResponse(messages) || extractOpenCodeFinalResponse(promptResult));
|
|
79
|
+
return {
|
|
80
|
+
provider: this.provider,
|
|
81
|
+
providerSessionId: sessionId,
|
|
82
|
+
finalResponse,
|
|
83
|
+
items: [promptResult, messages],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (isOpenCodeTransportFailure(error)) {
|
|
88
|
+
this.alive = false;
|
|
89
|
+
throw new AgentProviderUnavailableError({
|
|
90
|
+
code: "PROVIDER_UNAVAILABLE",
|
|
91
|
+
provider: this.provider,
|
|
92
|
+
operation: "run",
|
|
93
|
+
retryable: true,
|
|
94
|
+
cause: error,
|
|
95
|
+
message: "OpenCode provider is unavailable.",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async releaseSession(_providerSessionId) {
|
|
104
|
+
// OpenCode keeps durable sessions independently of this process.
|
|
105
|
+
}
|
|
106
|
+
isAlive() {
|
|
107
|
+
return this.alive && !this.closed;
|
|
108
|
+
}
|
|
109
|
+
async close() {
|
|
110
|
+
if (this.closed)
|
|
111
|
+
return;
|
|
112
|
+
this.closed = true;
|
|
113
|
+
this.alive = false;
|
|
114
|
+
this.server.close();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export class OpencodeLocalAgentDriver {
|
|
118
|
+
factory;
|
|
119
|
+
provider = "opencode";
|
|
120
|
+
idleTimeoutMs = 5 * 60_000;
|
|
121
|
+
constructor(factory = defaultOpencodeFactory) {
|
|
122
|
+
this.factory = factory;
|
|
123
|
+
}
|
|
124
|
+
runtimeKey(_context) {
|
|
125
|
+
return "opencode:default";
|
|
126
|
+
}
|
|
127
|
+
async createRuntime(context) {
|
|
128
|
+
return captureAgentProviderResult({
|
|
129
|
+
provider: this.provider,
|
|
130
|
+
agentId: context.agentId,
|
|
131
|
+
operation: "create_runtime",
|
|
132
|
+
run: async () => {
|
|
133
|
+
const { client, server } = await this.factory(context);
|
|
134
|
+
return new OpencodeRuntime(client, server);
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function defaultOpencodeFactory() {
|
|
140
|
+
const { createOpencode } = await import("@opencode-ai/sdk/v2");
|
|
141
|
+
return createOpencode({ port: await allocateLoopbackPort(), config: {
|
|
142
|
+
agent: {
|
|
143
|
+
hearth_read_only: opencodeAgentConfig("read_only"),
|
|
144
|
+
hearth_allowed: opencodeAgentConfig("allowed"),
|
|
145
|
+
hearth_full_access: opencodeAgentConfig("full_access"),
|
|
146
|
+
},
|
|
147
|
+
} });
|
|
148
|
+
}
|
|
149
|
+
export function opencodeAgentConfig(writeMode) {
|
|
150
|
+
return {
|
|
151
|
+
mode: "primary",
|
|
152
|
+
permission: opencodePermissionFor(writeMode),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async function createOpencodeSession(client, input, model) {
|
|
156
|
+
const result = await client.v2.session.create({
|
|
157
|
+
location: { directory: input.workspaceRoot },
|
|
158
|
+
agent: opencodeAgentFor(input.writeMode),
|
|
159
|
+
...(model ? { model } : {}),
|
|
160
|
+
}, { throwOnError: true });
|
|
161
|
+
return requireSessionId(result.data.data);
|
|
162
|
+
}
|
|
163
|
+
export function opencodeAgentFor(writeMode) {
|
|
164
|
+
switch (writeMode) {
|
|
165
|
+
case "read_only": return "hearth_read_only";
|
|
166
|
+
case "full_access": return "hearth_full_access";
|
|
167
|
+
case "allowed":
|
|
168
|
+
case undefined: return "hearth_allowed";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export function opencodePermissionFor(writeMode) {
|
|
172
|
+
const allowed = writeMode !== "read_only";
|
|
173
|
+
const unrestricted = writeMode === "full_access";
|
|
174
|
+
return {
|
|
175
|
+
read: "allow",
|
|
176
|
+
edit: allowed ? "allow" : "deny",
|
|
177
|
+
glob: "allow",
|
|
178
|
+
grep: "allow",
|
|
179
|
+
list: "allow",
|
|
180
|
+
bash: allowed ? "allow" : "deny",
|
|
181
|
+
task: "deny",
|
|
182
|
+
external_directory: unrestricted ? "allow" : "deny",
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async function assertOpencodeHealthy(client) {
|
|
186
|
+
const health = client.v2.health;
|
|
187
|
+
if (!health)
|
|
188
|
+
return;
|
|
189
|
+
try {
|
|
190
|
+
await health.get({ throwOnError: true });
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
throw new OpencodeHealthError(errorMessage(error));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function isOpenCodeTransportFailure(error) {
|
|
197
|
+
if (error instanceof OpencodeHealthError)
|
|
198
|
+
return true;
|
|
199
|
+
const code = transportErrorCode(error);
|
|
200
|
+
return code === "ECONNREFUSED"
|
|
201
|
+
|| code === "ECONNRESET"
|
|
202
|
+
|| code === "EPIPE"
|
|
203
|
+
|| code === "ENETDOWN"
|
|
204
|
+
|| code === "ENETUNREACH"
|
|
205
|
+
|| code === "ETIMEDOUT";
|
|
206
|
+
}
|
|
207
|
+
function transportErrorCode(error) {
|
|
208
|
+
if (!error || typeof error !== "object")
|
|
209
|
+
return undefined;
|
|
210
|
+
const code = error.code;
|
|
211
|
+
if (typeof code === "string")
|
|
212
|
+
return code;
|
|
213
|
+
const cause = error.cause;
|
|
214
|
+
return cause && typeof cause === "object" && typeof cause.code === "string"
|
|
215
|
+
? cause.code
|
|
216
|
+
: undefined;
|
|
217
|
+
}
|
|
218
|
+
class OpencodeHealthError extends Error {
|
|
219
|
+
constructor(message) {
|
|
220
|
+
super(`OpenCode server health check failed: ${message}`);
|
|
221
|
+
this.name = "OpencodeHealthError";
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async function modelWithEffort(client, sessionId, effort) {
|
|
225
|
+
const result = await client.v2.session.get({ sessionID: sessionId }, { throwOnError: true });
|
|
226
|
+
const model = result.data.data.model;
|
|
227
|
+
if (!model) {
|
|
228
|
+
throw new AgentProviderProtocolError({
|
|
229
|
+
code: "PROVIDER_PROTOCOL_ERROR",
|
|
230
|
+
provider: "opencode",
|
|
231
|
+
operation: "resolve_model",
|
|
232
|
+
retryable: false,
|
|
233
|
+
message: "OpenCode did not return the current session model for an effort override.",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return { ...model, variant: effort };
|
|
237
|
+
}
|
|
238
|
+
async function promptOpencodeSession(client, sessionId, input) {
|
|
239
|
+
const prompt = { text: input.prompt };
|
|
240
|
+
return client.v2.session.prompt({
|
|
241
|
+
sessionID: sessionId,
|
|
242
|
+
prompt,
|
|
243
|
+
}, { throwOnError: true });
|
|
244
|
+
}
|
|
245
|
+
async function waitForOpencodeSession(client, sessionId, promptResult) {
|
|
246
|
+
// OpenCode 1.18 accepts the prompt before its foreground drain is ready.
|
|
247
|
+
// Its wait endpoint rejects that state and can keep rejecting after the
|
|
248
|
+
// session has completed, so use the v2 active-session lifecycle instead.
|
|
249
|
+
const active = typeof client.v2.session.active === "function"
|
|
250
|
+
? client.v2.session.active.bind(client.v2.session)
|
|
251
|
+
: undefined;
|
|
252
|
+
if (!active) {
|
|
253
|
+
await client.v2.session.wait({ sessionID: sessionId }, { throwOnError: true });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const promptId = extractOpenCodePromptId(promptResult);
|
|
257
|
+
const deadline = Date.now() + OPENCODE_SESSION_POLL_TIMEOUT_MS;
|
|
258
|
+
let observedActive = false;
|
|
259
|
+
while (true) {
|
|
260
|
+
const messages = await readOpencodeMessages(client, sessionId, promptId);
|
|
261
|
+
const activity = await active({ throwOnError: true });
|
|
262
|
+
const running = isOpenCodeSessionActive(activity, sessionId);
|
|
263
|
+
if (running)
|
|
264
|
+
observedActive = true;
|
|
265
|
+
const completed = hasCompletedOpenCodeTurn(messages, promptId);
|
|
266
|
+
if (completed && (promptId !== undefined || (observedActive && !running)))
|
|
267
|
+
return;
|
|
268
|
+
if (Date.now() >= deadline) {
|
|
269
|
+
throw new AgentProviderProtocolError({
|
|
270
|
+
code: "PROVIDER_PROTOCOL_ERROR",
|
|
271
|
+
provider: "opencode",
|
|
272
|
+
operation: "wait_for_session",
|
|
273
|
+
retryable: false,
|
|
274
|
+
message: "OpenCode did not finish the session before the provider timeout.",
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
await delay(OPENCODE_SESSION_POLL_INTERVAL_MS);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function readOpencodeMessages(client, sessionId, promptId) {
|
|
281
|
+
const messages = [];
|
|
282
|
+
const seenCursors = new Set();
|
|
283
|
+
let cursor;
|
|
284
|
+
while (true) {
|
|
285
|
+
const result = await client.v2.session.messages({
|
|
286
|
+
sessionID: sessionId,
|
|
287
|
+
limit: 100,
|
|
288
|
+
...(cursor ? { cursor } : { order: "asc" }),
|
|
289
|
+
}, { throwOnError: true });
|
|
290
|
+
const page = result.data;
|
|
291
|
+
messages.push(...page.data);
|
|
292
|
+
// A prompt-specific read can stop as soon as the submitted turn is
|
|
293
|
+
// complete. Reads without a prompt id still walk the full history because
|
|
294
|
+
// they are used to extract the final response after the wait fallback.
|
|
295
|
+
if (promptId !== undefined && hasCompletedOpenCodeTurn({ data: messages }, promptId)) {
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
const nextCursor = page.cursor?.next;
|
|
299
|
+
if (!nextCursor || seenCursors.has(nextCursor))
|
|
300
|
+
break;
|
|
301
|
+
seenCursors.add(nextCursor);
|
|
302
|
+
cursor = nextCursor;
|
|
303
|
+
}
|
|
304
|
+
return { data: messages, cursor: {} };
|
|
305
|
+
}
|
|
306
|
+
function extractOpenCodePromptId(value) {
|
|
307
|
+
const id = asRecord(unwrapProviderPayload(value))?.id;
|
|
308
|
+
return typeof id === "string" ? id : undefined;
|
|
309
|
+
}
|
|
310
|
+
function isOpenCodeSessionActive(value, sessionId) {
|
|
311
|
+
const activeSessions = asRecord(unwrapProviderPayload(value));
|
|
312
|
+
return activeSessions?.[sessionId] !== undefined;
|
|
313
|
+
}
|
|
314
|
+
function hasCompletedOpenCodeTurn(value, promptId) {
|
|
315
|
+
const root = unwrapProviderPayload(value);
|
|
316
|
+
const messages = Array.isArray(root) ? root : readArray(root, "messages");
|
|
317
|
+
if (!messages)
|
|
318
|
+
return false;
|
|
319
|
+
let promptSeen = promptId === undefined;
|
|
320
|
+
for (const message of messages) {
|
|
321
|
+
const record = asRecord(message);
|
|
322
|
+
if (!record)
|
|
323
|
+
continue;
|
|
324
|
+
const info = asRecord(record.info) ?? record;
|
|
325
|
+
const role = typeof info.role === "string" ? info.role : record.type;
|
|
326
|
+
if (promptId !== undefined && info.id === promptId && role === "user") {
|
|
327
|
+
promptSeen = true;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (!promptSeen || role !== "assistant")
|
|
331
|
+
continue;
|
|
332
|
+
const time = asRecord(info.time) ?? asRecord(record.time);
|
|
333
|
+
if (typeof info.finish === "string" || typeof record.finish === "string")
|
|
334
|
+
return true;
|
|
335
|
+
if (typeof time?.completed === "number")
|
|
336
|
+
return true;
|
|
337
|
+
if (info.error !== undefined || record.error !== undefined)
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
function delay(milliseconds) {
|
|
343
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
344
|
+
}
|
|
345
|
+
function parseOpencodeModel(model, variant) {
|
|
346
|
+
const separator = model.indexOf("/");
|
|
347
|
+
const reference = separator === -1
|
|
348
|
+
? { providerID: "opencode", id: model }
|
|
349
|
+
: { providerID: model.slice(0, separator), id: model.slice(separator + 1) };
|
|
350
|
+
return variant ? { ...reference, variant } : reference;
|
|
351
|
+
}
|
|
352
|
+
function requireSessionId(session) {
|
|
353
|
+
if (!session.id) {
|
|
354
|
+
throw new AgentProviderProtocolError({
|
|
355
|
+
code: "PROVIDER_PROTOCOL_ERROR",
|
|
356
|
+
provider: "opencode",
|
|
357
|
+
operation: "create_session",
|
|
358
|
+
retryable: false,
|
|
359
|
+
message: "OpenCode did not return a session id.",
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return session.id;
|
|
363
|
+
}
|
|
364
|
+
export function extractOpenCodeFinalResponse(value) {
|
|
365
|
+
const root = unwrapProviderPayload(value);
|
|
366
|
+
const messages = Array.isArray(root) ? root : readArray(root, "messages");
|
|
367
|
+
if (messages)
|
|
368
|
+
return extractLastOpenCodeAssistantMessageText(messages);
|
|
369
|
+
return extractOpenCodeAssistantMessageText(root);
|
|
370
|
+
}
|
|
371
|
+
function extractLastOpenCodeAssistantMessageText(messages) {
|
|
372
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
373
|
+
const message = asRecord(messages[index]);
|
|
374
|
+
if (!message)
|
|
375
|
+
continue;
|
|
376
|
+
const info = asRecord(message.info);
|
|
377
|
+
const role = typeof info?.role === "string" ? info.role : message.role;
|
|
378
|
+
const type = typeof message.type === "string" ? message.type : undefined;
|
|
379
|
+
if (role !== "assistant" && type !== "assistant")
|
|
380
|
+
continue;
|
|
381
|
+
const text = extractOpenCodeAssistantMessageText(message);
|
|
382
|
+
if (text)
|
|
383
|
+
return text;
|
|
384
|
+
}
|
|
385
|
+
return "";
|
|
386
|
+
}
|
|
387
|
+
function extractOpenCodeAssistantMessageText(value) {
|
|
388
|
+
const message = asRecord(value);
|
|
389
|
+
if (!message)
|
|
390
|
+
return "";
|
|
391
|
+
for (const key of ["content", "parts"]) {
|
|
392
|
+
const parts = readArray(message, key);
|
|
393
|
+
if (!parts)
|
|
394
|
+
continue;
|
|
395
|
+
const text = parts
|
|
396
|
+
.map((part) => {
|
|
397
|
+
const record = asRecord(part);
|
|
398
|
+
return record?.type === "text" && typeof record.text === "string" ? record.text : "";
|
|
399
|
+
})
|
|
400
|
+
.filter(Boolean)
|
|
401
|
+
.join("");
|
|
402
|
+
if (text.trim())
|
|
403
|
+
return text.trim();
|
|
404
|
+
}
|
|
405
|
+
const info = asRecord(message.info) ?? message;
|
|
406
|
+
return stringifyStructuredMessage(info.structured);
|
|
407
|
+
}
|
|
408
|
+
function stringifyStructuredMessage(value) {
|
|
409
|
+
if (value === undefined || value === null)
|
|
410
|
+
return "";
|
|
411
|
+
if (typeof value === "string")
|
|
412
|
+
return value.trim();
|
|
413
|
+
return JSON.stringify(value);
|
|
414
|
+
}
|
|
415
|
+
function unwrapProviderPayload(value) {
|
|
416
|
+
let current = value;
|
|
417
|
+
for (let depth = 0; depth < 3; depth += 1) {
|
|
418
|
+
const record = asRecord(current);
|
|
419
|
+
if (!record)
|
|
420
|
+
return current;
|
|
421
|
+
if (record.data !== undefined) {
|
|
422
|
+
current = record.data;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (record.result !== undefined) {
|
|
426
|
+
current = record.result;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
return current;
|
|
430
|
+
}
|
|
431
|
+
return current;
|
|
432
|
+
}
|
|
433
|
+
function readArray(value, key) {
|
|
434
|
+
const result = asRecord(value)?.[key];
|
|
435
|
+
return Array.isArray(result) ? result : undefined;
|
|
436
|
+
}
|
|
437
|
+
function readNestedString(value, path) {
|
|
438
|
+
let current = value;
|
|
439
|
+
for (const key of path)
|
|
440
|
+
current = asRecord(current)?.[key];
|
|
441
|
+
return typeof current === "string" ? current : undefined;
|
|
442
|
+
}
|
|
443
|
+
function asRecord(value) {
|
|
444
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
445
|
+
? value
|
|
446
|
+
: undefined;
|
|
447
|
+
}
|
|
448
|
+
function requireFinalResponse(response) {
|
|
449
|
+
const trimmed = response.trim();
|
|
450
|
+
if (!trimmed) {
|
|
451
|
+
throw new AgentProviderProtocolError({
|
|
452
|
+
code: "PROVIDER_PROTOCOL_ERROR",
|
|
453
|
+
provider: "opencode",
|
|
454
|
+
operation: "run",
|
|
455
|
+
retryable: false,
|
|
456
|
+
message: "OpenCode did not return a final assistant response.",
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
return trimmed;
|
|
460
|
+
}
|
|
461
|
+
function errorMessage(error) {
|
|
462
|
+
return error instanceof Error ? error.message : String(error);
|
|
463
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { delimiter, resolve, sep } from "node:path";
|
|
3
|
+
export function removeHearthNodeModulesBinFromPath(pathValue) {
|
|
4
|
+
return pathValue
|
|
5
|
+
.split(delimiter)
|
|
6
|
+
.filter((entry) => entry && !isHearthNodeModulesBin(entry))
|
|
7
|
+
.join(delimiter);
|
|
8
|
+
}
|
|
9
|
+
function isHearthNodeModulesBin(pathEntry) {
|
|
10
|
+
const resolvedEntry = resolve(pathEntry);
|
|
11
|
+
if (!resolvedEntry.endsWith(`${sep}node_modules${sep}.bin`)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
const packageJson = resolve(resolvedEntry, "..", "..", "package.json");
|
|
15
|
+
if (!existsSync(packageJson))
|
|
16
|
+
return false;
|
|
17
|
+
try {
|
|
18
|
+
const packageInfo = JSON.parse(readFileSync(packageJson, "utf8"));
|
|
19
|
+
return packageInfo.name === "@sunshinelife83/hearth";
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|