@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,298 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { AgentProviderExecutionError, AgentProviderProtocolError, AgentProviderUnavailableError, captureAgentProviderResult, } from "./local-agent-errors.js";
|
|
3
|
+
import { createPiSandboxExtension, createPiSandboxModeRef, registerPiSandboxSession, releasePiSandboxSession, updatePiSandboxSession, } from "./local-agent-pi-sandbox.js";
|
|
4
|
+
const PI_READ_ONLY_TOOLS = ["read", "grep", "find", "ls"];
|
|
5
|
+
const PI_WORKSPACE_TOOLS = ["read", "grep", "find", "ls", "edit", "write", "bash"];
|
|
6
|
+
const PI_FULL_ACCESS_TOOLS = [...PI_WORKSPACE_TOOLS];
|
|
7
|
+
const MAX_PI_EVENTS = 10_000;
|
|
8
|
+
export class PiSessionRuntime {
|
|
9
|
+
session;
|
|
10
|
+
provider = "pi";
|
|
11
|
+
unsubscribe;
|
|
12
|
+
alive = true;
|
|
13
|
+
closed = false;
|
|
14
|
+
collectingEvents = false;
|
|
15
|
+
events = [];
|
|
16
|
+
constructor(session) {
|
|
17
|
+
this.session = session;
|
|
18
|
+
this.unsubscribe = session.subscribe((event) => {
|
|
19
|
+
if (!this.collectingEvents)
|
|
20
|
+
return;
|
|
21
|
+
if (this.events.length >= MAX_PI_EVENTS)
|
|
22
|
+
this.events.shift();
|
|
23
|
+
this.events.push(event);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async run(input, callbacks) {
|
|
27
|
+
return captureAgentProviderResult({
|
|
28
|
+
provider: this.provider,
|
|
29
|
+
operation: "run",
|
|
30
|
+
run: async () => {
|
|
31
|
+
if (!this.isAlive()) {
|
|
32
|
+
throw new AgentProviderUnavailableError({
|
|
33
|
+
code: "PROVIDER_UNAVAILABLE",
|
|
34
|
+
provider: this.provider,
|
|
35
|
+
operation: "run",
|
|
36
|
+
retryable: true,
|
|
37
|
+
message: "Pi runtime is not running.",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
await callbacks?.onSessionId?.(this.session.sessionId);
|
|
41
|
+
await this.applyOverrides(input);
|
|
42
|
+
this.events = [];
|
|
43
|
+
const messageStart = this.session.messages.length;
|
|
44
|
+
this.collectingEvents = true;
|
|
45
|
+
try {
|
|
46
|
+
await this.session.prompt(input.prompt);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
this.collectingEvents = false;
|
|
50
|
+
}
|
|
51
|
+
const currentMessages = this.session.messages.slice(messageStart);
|
|
52
|
+
const finalResponse = extractPiFinalResponse({ messages: currentMessages });
|
|
53
|
+
if (!finalResponse) {
|
|
54
|
+
const providerError = extractPiProviderError(this.events) || extractPiProviderError(currentMessages);
|
|
55
|
+
if (providerError) {
|
|
56
|
+
throw new AgentProviderExecutionError({
|
|
57
|
+
code: "PROVIDER_EXECUTION_ERROR",
|
|
58
|
+
provider: this.provider,
|
|
59
|
+
operation: "run",
|
|
60
|
+
retryable: false,
|
|
61
|
+
cause: new Error(providerError),
|
|
62
|
+
message: "Pi agent turn failed.",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
throw new AgentProviderProtocolError({
|
|
66
|
+
code: "PROVIDER_PROTOCOL_ERROR",
|
|
67
|
+
provider: this.provider,
|
|
68
|
+
operation: "run",
|
|
69
|
+
retryable: false,
|
|
70
|
+
message: "Pi did not return a final assistant response.",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
provider: this.provider,
|
|
75
|
+
providerSessionId: this.session.sessionId,
|
|
76
|
+
finalResponse,
|
|
77
|
+
items: [...this.events, ...currentMessages],
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async releaseSession(_providerSessionId) {
|
|
83
|
+
// The runtime is already scoped to one logical Pi session.
|
|
84
|
+
}
|
|
85
|
+
isAlive() {
|
|
86
|
+
return this.alive && !this.closed;
|
|
87
|
+
}
|
|
88
|
+
async close() {
|
|
89
|
+
if (this.closed)
|
|
90
|
+
return;
|
|
91
|
+
this.closed = true;
|
|
92
|
+
this.alive = false;
|
|
93
|
+
this.unsubscribe();
|
|
94
|
+
try {
|
|
95
|
+
await releasePiSandboxSession(this.session);
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
this.session.dispose();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async applyOverrides(input) {
|
|
102
|
+
await updatePiSandboxSession(this.session, input.workspaceRoot, input.writeMode ?? "allowed");
|
|
103
|
+
this.session.setActiveToolsByName([...piToolsForWriteMode(input.writeMode)]);
|
|
104
|
+
if (input.model) {
|
|
105
|
+
const model = resolvePiModel(this.session.modelRegistry, input.model);
|
|
106
|
+
if (!model) {
|
|
107
|
+
throw new AgentProviderProtocolError({
|
|
108
|
+
code: "PROVIDER_PROTOCOL_ERROR",
|
|
109
|
+
provider: "pi",
|
|
110
|
+
operation: "configure_model",
|
|
111
|
+
retryable: false,
|
|
112
|
+
message: `Pi model not found: ${input.model}.`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
await this.session.setModel(model);
|
|
116
|
+
}
|
|
117
|
+
if (input.effort) {
|
|
118
|
+
this.session.setThinkingLevel(input.effort);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export class PiLocalAgentDriver {
|
|
123
|
+
factory;
|
|
124
|
+
provider = "pi";
|
|
125
|
+
idleTimeoutMs = 3 * 60_000;
|
|
126
|
+
constructor(factory = defaultPiSessionFactory) {
|
|
127
|
+
this.factory = factory;
|
|
128
|
+
}
|
|
129
|
+
runtimeKey(context) {
|
|
130
|
+
return `pi:${context.agentId}`;
|
|
131
|
+
}
|
|
132
|
+
async createRuntime(context) {
|
|
133
|
+
return captureAgentProviderResult({
|
|
134
|
+
provider: this.provider,
|
|
135
|
+
agentId: context.agentId,
|
|
136
|
+
operation: "create_runtime",
|
|
137
|
+
run: async () => {
|
|
138
|
+
const input = {
|
|
139
|
+
prompt: "",
|
|
140
|
+
workspaceRoot: context.workspaceRoot,
|
|
141
|
+
providerSessionId: context.providerSessionId,
|
|
142
|
+
writeMode: context.writeMode,
|
|
143
|
+
model: context.model,
|
|
144
|
+
effort: context.effort,
|
|
145
|
+
};
|
|
146
|
+
const session = await this.factory(context, input);
|
|
147
|
+
return new PiSessionRuntime(session);
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function defaultPiSessionFactory(context, input) {
|
|
153
|
+
const { AuthStorage, ModelRegistry, SessionManager, DefaultResourceLoader, createAgentSession, getAgentDir, } = await import("@earendil-works/pi-coding-agent");
|
|
154
|
+
// Hearth's agentDir is the compatibility directory used for instructions;
|
|
155
|
+
// Pi keeps its own native auth, model, and session state under getAgentDir().
|
|
156
|
+
const agentDir = getAgentDir();
|
|
157
|
+
const authStorage = AuthStorage.create(join(agentDir, "auth.json"));
|
|
158
|
+
const modelRegistry = ModelRegistry.create(authStorage, join(agentDir, "models.json"));
|
|
159
|
+
const sessionManager = await resolveSessionManager(SessionManager, input.workspaceRoot, input.providerSessionId);
|
|
160
|
+
const model = input.model ? resolvePiModel(modelRegistry, input.model) : undefined;
|
|
161
|
+
if (input.model && !model) {
|
|
162
|
+
throw new AgentProviderProtocolError({
|
|
163
|
+
code: "PROVIDER_PROTOCOL_ERROR",
|
|
164
|
+
provider: "pi",
|
|
165
|
+
agentId: context.agentId,
|
|
166
|
+
operation: "configure_model",
|
|
167
|
+
retryable: false,
|
|
168
|
+
message: `Pi model not found: ${input.model}.`,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const modeRef = createPiSandboxModeRef(input.writeMode ?? "allowed");
|
|
172
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
173
|
+
cwd: input.workspaceRoot,
|
|
174
|
+
agentDir,
|
|
175
|
+
extensionFactories: [createPiSandboxExtension(input.workspaceRoot, modeRef)],
|
|
176
|
+
});
|
|
177
|
+
let session;
|
|
178
|
+
try {
|
|
179
|
+
const result = await createAgentSession({
|
|
180
|
+
cwd: input.workspaceRoot,
|
|
181
|
+
agentDir,
|
|
182
|
+
authStorage,
|
|
183
|
+
modelRegistry,
|
|
184
|
+
sessionManager: sessionManager,
|
|
185
|
+
resourceLoader,
|
|
186
|
+
...(model ? { model: model } : {}),
|
|
187
|
+
...(input.effort ? { thinkingLevel: input.effort } : {}),
|
|
188
|
+
// Keep the full built-in registry available so warm turns can narrow or
|
|
189
|
+
// broaden active tools without recreating the session.
|
|
190
|
+
tools: [...PI_FULL_ACCESS_TOOLS],
|
|
191
|
+
});
|
|
192
|
+
session = result.session;
|
|
193
|
+
await registerPiSandboxSession(session, input.workspaceRoot, modeRef, input.writeMode ?? "allowed");
|
|
194
|
+
session.setActiveToolsByName([...piToolsForWriteMode(input.writeMode)]);
|
|
195
|
+
return session;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (session) {
|
|
199
|
+
try {
|
|
200
|
+
await releasePiSandboxSession(session);
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
session.dispose();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
export function piToolsForWriteMode(writeMode) {
|
|
210
|
+
switch (writeMode) {
|
|
211
|
+
case "read_only": return PI_READ_ONLY_TOOLS;
|
|
212
|
+
case "full_access": return PI_FULL_ACCESS_TOOLS;
|
|
213
|
+
case "allowed":
|
|
214
|
+
case undefined:
|
|
215
|
+
return PI_WORKSPACE_TOOLS;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function resolveSessionManager(SessionManager, workspaceRoot, providerSessionId) {
|
|
219
|
+
if (!providerSessionId)
|
|
220
|
+
return SessionManager.create(workspaceRoot);
|
|
221
|
+
const sessions = await SessionManager.list(workspaceRoot);
|
|
222
|
+
const match = sessions.find((session) => session.id === providerSessionId);
|
|
223
|
+
if (!match) {
|
|
224
|
+
throw new AgentProviderProtocolError({
|
|
225
|
+
code: "PROVIDER_PROTOCOL_ERROR",
|
|
226
|
+
provider: "pi",
|
|
227
|
+
operation: "session",
|
|
228
|
+
retryable: false,
|
|
229
|
+
message: `Pi session not found: ${providerSessionId}.`,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return SessionManager.open(match.path);
|
|
233
|
+
}
|
|
234
|
+
function resolvePiModel(registry, reference) {
|
|
235
|
+
const separator = reference.indexOf("/");
|
|
236
|
+
if (separator !== -1) {
|
|
237
|
+
return registry.find(reference.slice(0, separator), reference.slice(separator + 1));
|
|
238
|
+
}
|
|
239
|
+
const all = registry.getAll?.() ?? [];
|
|
240
|
+
return all.find((model) => asRecord(model)?.id === reference);
|
|
241
|
+
}
|
|
242
|
+
export function extractPiFinalResponse(value) {
|
|
243
|
+
const root = unwrapProviderPayload(value);
|
|
244
|
+
const messages = Array.isArray(root) ? root : readArray(root, "messages");
|
|
245
|
+
if (!messages)
|
|
246
|
+
return "";
|
|
247
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
248
|
+
const message = asRecord(messages[index]);
|
|
249
|
+
if (!message || message.role !== "assistant")
|
|
250
|
+
continue;
|
|
251
|
+
const content = message.content;
|
|
252
|
+
if (!Array.isArray(content))
|
|
253
|
+
continue;
|
|
254
|
+
const text = content
|
|
255
|
+
.map((part) => {
|
|
256
|
+
const record = asRecord(part);
|
|
257
|
+
return record?.type === "text" && typeof record.text === "string" ? record.text : "";
|
|
258
|
+
})
|
|
259
|
+
.filter(Boolean)
|
|
260
|
+
.join("\n\n")
|
|
261
|
+
.trim();
|
|
262
|
+
if (text)
|
|
263
|
+
return text;
|
|
264
|
+
}
|
|
265
|
+
return "";
|
|
266
|
+
}
|
|
267
|
+
export function extractPiProviderError(value) {
|
|
268
|
+
const root = unwrapProviderPayload(value);
|
|
269
|
+
if (Array.isArray(root)) {
|
|
270
|
+
for (let index = root.length - 1; index >= 0; index -= 1) {
|
|
271
|
+
const error = extractPiProviderError(root[index]);
|
|
272
|
+
if (error)
|
|
273
|
+
return error;
|
|
274
|
+
}
|
|
275
|
+
return "";
|
|
276
|
+
}
|
|
277
|
+
const messages = readArray(root, "messages");
|
|
278
|
+
if (messages)
|
|
279
|
+
return extractPiProviderError(messages);
|
|
280
|
+
const record = asRecord(asRecord(root)?.message ?? root);
|
|
281
|
+
if (!record)
|
|
282
|
+
return "";
|
|
283
|
+
const error = record.errorMessage ?? record.error;
|
|
284
|
+
return typeof error === "string" ? error.trim() : "";
|
|
285
|
+
}
|
|
286
|
+
function unwrapProviderPayload(value) {
|
|
287
|
+
const record = asRecord(value);
|
|
288
|
+
return record ? record.data ?? record.result ?? value : value;
|
|
289
|
+
}
|
|
290
|
+
function readArray(value, key) {
|
|
291
|
+
const result = asRecord(value)?.[key];
|
|
292
|
+
return Array.isArray(result) ? result : undefined;
|
|
293
|
+
}
|
|
294
|
+
function asRecord(value) {
|
|
295
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
296
|
+
? value
|
|
297
|
+
: undefined;
|
|
298
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export function presentAgentTargetCatalog(catalog) {
|
|
2
|
+
return {
|
|
3
|
+
targets: [
|
|
4
|
+
...catalog.providers
|
|
5
|
+
.filter((provider) => provider.usable)
|
|
6
|
+
.map((provider) => ({
|
|
7
|
+
name: provider.id,
|
|
8
|
+
kind: "provider",
|
|
9
|
+
...(provider.model ? { model: provider.model } : {}),
|
|
10
|
+
...(provider.effort ? { effort: provider.effort } : {}),
|
|
11
|
+
})),
|
|
12
|
+
...catalog.profiles.map((profile) => ({
|
|
13
|
+
name: profile.name,
|
|
14
|
+
kind: "profile",
|
|
15
|
+
provider: profile.provider,
|
|
16
|
+
description: profile.description,
|
|
17
|
+
...(profile.model ? { model: profile.model } : {}),
|
|
18
|
+
...(profile.effort ? { effort: profile.effort } : {}),
|
|
19
|
+
})),
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function presentAgentReceipt(record) {
|
|
24
|
+
return { id: record.id, status: presentAgentStatus(record.status) };
|
|
25
|
+
}
|
|
26
|
+
export function presentAgentSummary(record) {
|
|
27
|
+
return { ...presentAgentReceipt(record), target: record.profileName };
|
|
28
|
+
}
|
|
29
|
+
export function presentAgentObservation(record) {
|
|
30
|
+
const receipt = presentAgentReceipt(record);
|
|
31
|
+
switch (receipt.status) {
|
|
32
|
+
case "completed":
|
|
33
|
+
return {
|
|
34
|
+
...receipt,
|
|
35
|
+
status: "completed",
|
|
36
|
+
...(record.latestResponse === undefined ? {} : { response: record.latestResponse }),
|
|
37
|
+
};
|
|
38
|
+
case "failed":
|
|
39
|
+
return { ...receipt, status: "failed", error: presentAgentFailure(record) };
|
|
40
|
+
case "stopped":
|
|
41
|
+
return {
|
|
42
|
+
...receipt,
|
|
43
|
+
status: "stopped",
|
|
44
|
+
...(hasAgentFailure(record) ? { error: presentAgentFailure(record) } : {}),
|
|
45
|
+
};
|
|
46
|
+
case "running":
|
|
47
|
+
return { id: receipt.id, status: "running" };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function formatAgentTargetCatalog(catalog) {
|
|
51
|
+
if (catalog.targets.length === 0)
|
|
52
|
+
return "No usable subagent targets.";
|
|
53
|
+
return catalog.targets.map((target) => {
|
|
54
|
+
const settings = [
|
|
55
|
+
target.model ? `model=${target.model}` : undefined,
|
|
56
|
+
target.effort ? `effort=${target.effort}` : undefined,
|
|
57
|
+
].filter(Boolean).join(" ");
|
|
58
|
+
if (target.kind === "provider") {
|
|
59
|
+
return `${target.name} [provider]${settings ? ` ${settings}` : ""}`;
|
|
60
|
+
}
|
|
61
|
+
return `${target.name} [profile, ${target.provider}]${settings ? ` ${settings}` : ""} - ${target.description}`;
|
|
62
|
+
}).join("\n");
|
|
63
|
+
}
|
|
64
|
+
export function formatAgentReceipt(receipt) {
|
|
65
|
+
return `${receipt.id} ${receipt.status}`;
|
|
66
|
+
}
|
|
67
|
+
export function formatAgentSummary(summary) {
|
|
68
|
+
return `${formatAgentReceipt(summary)} ${summary.target}`;
|
|
69
|
+
}
|
|
70
|
+
export function formatAgentObservation(observation) {
|
|
71
|
+
const line = formatAgentReceipt(observation);
|
|
72
|
+
if (observation.status === "completed" && observation.response !== undefined) {
|
|
73
|
+
return `${line}\n\n${observation.response}`;
|
|
74
|
+
}
|
|
75
|
+
if ((observation.status === "failed" || observation.status === "stopped") && observation.error) {
|
|
76
|
+
const retryable = observation.error.retryable ? " [retryable]" : "";
|
|
77
|
+
return `${line} ${observation.error.code}: ${observation.error.message}${retryable}`;
|
|
78
|
+
}
|
|
79
|
+
return line;
|
|
80
|
+
}
|
|
81
|
+
function presentAgentStatus(status) {
|
|
82
|
+
switch (status) {
|
|
83
|
+
case "starting":
|
|
84
|
+
case "running":
|
|
85
|
+
return "running";
|
|
86
|
+
case "idle":
|
|
87
|
+
case "paused":
|
|
88
|
+
return "completed";
|
|
89
|
+
case "error":
|
|
90
|
+
return "failed";
|
|
91
|
+
case "stopped":
|
|
92
|
+
return "stopped";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function hasAgentFailure(record) {
|
|
96
|
+
return record.error !== undefined || record.errorCode !== undefined || record.errorRetryable !== undefined;
|
|
97
|
+
}
|
|
98
|
+
function presentAgentFailure(record) {
|
|
99
|
+
return {
|
|
100
|
+
code: record.errorCode ?? "AGENT_FAILED",
|
|
101
|
+
message: record.error ?? "Subagent failed without an error message.",
|
|
102
|
+
retryable: record.errorRetryable ?? false,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { basename, join, resolve } from "node:path";
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
export const LOCAL_AGENT_PROVIDERS = [
|
|
6
|
+
"codex",
|
|
7
|
+
"claude",
|
|
8
|
+
"opencode",
|
|
9
|
+
"pi",
|
|
10
|
+
"cursor",
|
|
11
|
+
"copilot",
|
|
12
|
+
"grok",
|
|
13
|
+
];
|
|
14
|
+
const FRONTMATTER_DELIMITER = "---";
|
|
15
|
+
const PROVIDERS = new Set(LOCAL_AGENT_PROVIDERS);
|
|
16
|
+
export async function loadLocalAgentProfiles(config, workspaceRoot, options = {}) {
|
|
17
|
+
if (!config.subagents.enabled)
|
|
18
|
+
return [];
|
|
19
|
+
const profileDirs = [
|
|
20
|
+
config.hearthAgentsDir,
|
|
21
|
+
join(workspaceRoot, ".hearth", "agents"),
|
|
22
|
+
];
|
|
23
|
+
const profilesByName = new Map();
|
|
24
|
+
for (const directory of profileDirs) {
|
|
25
|
+
for (const profile of await loadProfilesFromDirectory(directory)) {
|
|
26
|
+
profilesByName.set(profile.name, profile);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return Array.from(profilesByName.values())
|
|
30
|
+
.filter((profile) => options.includeDisabled || !profile.disabled)
|
|
31
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
32
|
+
}
|
|
33
|
+
async function loadProfilesFromDirectory(directory) {
|
|
34
|
+
const resolvedDirectory = resolve(directory);
|
|
35
|
+
if (!existsSync(resolvedDirectory))
|
|
36
|
+
return [];
|
|
37
|
+
const entries = await readdir(resolvedDirectory, { withFileTypes: true });
|
|
38
|
+
const profiles = [];
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
if (!entry.isFile())
|
|
41
|
+
continue;
|
|
42
|
+
if (!entry.name.endsWith(".md"))
|
|
43
|
+
continue;
|
|
44
|
+
const filePath = join(resolvedDirectory, entry.name);
|
|
45
|
+
try {
|
|
46
|
+
profiles.push(await loadProfileFile(filePath));
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.warn(`Skipping invalid subagent profile ${filePath}: ${errorMessage(error)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return profiles;
|
|
53
|
+
}
|
|
54
|
+
async function loadProfileFile(filePath) {
|
|
55
|
+
const content = await readFile(filePath, "utf8");
|
|
56
|
+
const parsed = parseFrontmatter(content, filePath);
|
|
57
|
+
return profileFromFrontmatter(parsed.frontmatter, parsed.body, filePath);
|
|
58
|
+
}
|
|
59
|
+
function parseFrontmatter(content, filePath) {
|
|
60
|
+
const normalized = content.replace(/^\uFEFF/, "");
|
|
61
|
+
const lines = normalized.split(/\r?\n/);
|
|
62
|
+
if (lines[0]?.trim() !== FRONTMATTER_DELIMITER) {
|
|
63
|
+
throw new Error(`Subagent profile is missing frontmatter: ${filePath}`);
|
|
64
|
+
}
|
|
65
|
+
const endIndex = lines.findIndex((line, index) => index > 0 && line.trim() === FRONTMATTER_DELIMITER);
|
|
66
|
+
if (endIndex === -1) {
|
|
67
|
+
throw new Error(`Subagent profile frontmatter is not closed: ${filePath}`);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
frontmatter: parseProfileYaml(lines.slice(1, endIndex).join("\n"), filePath),
|
|
71
|
+
body: lines.slice(endIndex + 1).join("\n").trim(),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function parseProfileYaml(source, filePath) {
|
|
75
|
+
let parsed;
|
|
76
|
+
try {
|
|
77
|
+
parsed = parseYaml(source) ?? {};
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw new Error(`Unable to parse subagent profile frontmatter: ${filePath}: ${errorMessage(error)}`);
|
|
81
|
+
}
|
|
82
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
83
|
+
throw new Error(`Subagent profile frontmatter must be a mapping: ${filePath}`);
|
|
84
|
+
}
|
|
85
|
+
return parsed;
|
|
86
|
+
}
|
|
87
|
+
function profileFromFrontmatter(frontmatter, body, filePath) {
|
|
88
|
+
const name = readString(frontmatter, "name") ?? basename(filePath, ".md");
|
|
89
|
+
const description = readString(frontmatter, "description");
|
|
90
|
+
const provider = readProvider(frontmatter, filePath);
|
|
91
|
+
if (!description) {
|
|
92
|
+
throw new Error(`Subagent profile is missing description: ${filePath}`);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
name,
|
|
96
|
+
description,
|
|
97
|
+
provider,
|
|
98
|
+
model: readString(frontmatter, "model"),
|
|
99
|
+
effort: readString(frontmatter, "effort"),
|
|
100
|
+
filePath,
|
|
101
|
+
body,
|
|
102
|
+
disabled: frontmatter.disabled === true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function readProvider(frontmatter, filePath) {
|
|
106
|
+
const provider = readString(frontmatter, "provider");
|
|
107
|
+
if (!provider) {
|
|
108
|
+
throw new Error(`Subagent profile is missing provider: ${filePath}`);
|
|
109
|
+
}
|
|
110
|
+
if (!PROVIDERS.has(provider)) {
|
|
111
|
+
throw new Error(`Subagent profile provider must be codex, claude, opencode, pi, cursor, copilot, or grok: ${filePath}`);
|
|
112
|
+
}
|
|
113
|
+
return provider;
|
|
114
|
+
}
|
|
115
|
+
export function isLocalAgentProvider(value) {
|
|
116
|
+
return PROVIDERS.has(value);
|
|
117
|
+
}
|
|
118
|
+
function readString(frontmatter, key) {
|
|
119
|
+
const value = frontmatter[key];
|
|
120
|
+
if (typeof value !== "string")
|
|
121
|
+
return undefined;
|
|
122
|
+
const trimmed = value.trim();
|
|
123
|
+
return trimmed || undefined;
|
|
124
|
+
}
|
|
125
|
+
function errorMessage(error) {
|
|
126
|
+
return error instanceof Error ? error.message : String(error);
|
|
127
|
+
}
|