protolabs-agent 0.101.0__py3-none-any.whl
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.
- a2a_impl/__init__.py +1 -0
- a2a_impl/auth.py +437 -0
- a2a_impl/executor.py +675 -0
- a2a_impl/registry.py +164 -0
- a2a_impl/stores.py +584 -0
- activity/__init__.py +5 -0
- activity/store.py +142 -0
- apps/web/dist/_ds/plugin-kit.css +5454 -0
- apps/web/dist/_ds/plugin-kit.js +140 -0
- apps/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- apps/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- apps/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- apps/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- apps/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- apps/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- apps/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- apps/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- apps/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- apps/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- apps/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- apps/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- apps/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- apps/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- apps/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- apps/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- apps/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- apps/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- apps/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- apps/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- apps/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- apps/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- apps/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- apps/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- apps/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- apps/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- apps/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- apps/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- apps/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- apps/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- apps/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- apps/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- apps/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- apps/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- apps/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- apps/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- apps/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- apps/web/dist/assets/abap-BdImnpbu.js +1 -0
- apps/web/dist/assets/actionscript-3-CoDkCxhg.js +1 -0
- apps/web/dist/assets/ada-bCR0ucgS.js +1 -0
- apps/web/dist/assets/andromeeda-C4gqWexZ.js +1 -0
- apps/web/dist/assets/angular-html-CU67Zn6k.js +1 -0
- apps/web/dist/assets/angular-ts-BwZT4LLn.js +1 -0
- apps/web/dist/assets/apache-Pmp26Uib.js +1 -0
- apps/web/dist/assets/apex-D8_7TLub.js +1 -0
- apps/web/dist/assets/apl-dKokRX4l.js +1 -0
- apps/web/dist/assets/applescript-Co6uUVPk.js +1 -0
- apps/web/dist/assets/ara-BRHolxvo.js +1 -0
- apps/web/dist/assets/asciidoc-Ve4PFQV2.js +1 -0
- apps/web/dist/assets/asm-D_Q5rh1f.js +1 -0
- apps/web/dist/assets/astro-CbQHKStN.js +1 -0
- apps/web/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
- apps/web/dist/assets/awk-DMzUqQB5.js +1 -0
- apps/web/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
- apps/web/dist/assets/ayu-light-BA47KaF1.js +1 -0
- apps/web/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
- apps/web/dist/assets/ballerina-BFfxhgS-.js +1 -0
- apps/web/dist/assets/bat-BkioyH1T.js +1 -0
- apps/web/dist/assets/beancount-k_qm7-4y.js +1 -0
- apps/web/dist/assets/berry-uYugtg8r.js +1 -0
- apps/web/dist/assets/bibtex-CHM0blh-.js +1 -0
- apps/web/dist/assets/bicep-Bmn6On1c.js +1 -0
- apps/web/dist/assets/bird2-DPOp833l.js +1 -0
- apps/web/dist/assets/blade-D4QpJJKB.js +1 -0
- apps/web/dist/assets/bsl-BO_Y6i37.js +1 -0
- apps/web/dist/assets/c-BIGW1oBm.js +1 -0
- apps/web/dist/assets/c3-eo99z4R2.js +1 -0
- apps/web/dist/assets/cadence-Bv_4Rxtq.js +1 -0
- apps/web/dist/assets/cairo-KRGpt6FW.js +1 -0
- apps/web/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- apps/web/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- apps/web/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- apps/web/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- apps/web/dist/assets/clarity-D53aC0YG.js +1 -0
- apps/web/dist/assets/clojure-P80f7IUj.js +1 -0
- apps/web/dist/assets/cmake-D1j8_8rp.js +1 -0
- apps/web/dist/assets/cobol-nwyudZeR.js +1 -0
- apps/web/dist/assets/codeowners-Bp6g37R7.js +1 -0
- apps/web/dist/assets/codeql-DsOJ9woJ.js +1 -0
- apps/web/dist/assets/coffee-Ch7k5sss.js +1 -0
- apps/web/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
- apps/web/dist/assets/coq-DkFqJrB1.js +1 -0
- apps/web/dist/assets/cpp-CofmeUqb.js +1 -0
- apps/web/dist/assets/crystal-tKQVLTB8.js +1 -0
- apps/web/dist/assets/csharp-COcwbKMJ.js +1 -0
- apps/web/dist/assets/css-DPfMkruS.js +1 -0
- apps/web/dist/assets/csv-fuZLfV_i.js +1 -0
- apps/web/dist/assets/cue-D82EKSYY.js +1 -0
- apps/web/dist/assets/cypher-COkxafJQ.js +1 -0
- apps/web/dist/assets/d-85-TOEBH.js +1 -0
- apps/web/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- apps/web/dist/assets/dart-CF10PKvl.js +1 -0
- apps/web/dist/assets/dax-CEL-wOlO.js +1 -0
- apps/web/dist/assets/desktop-BmXAJ9_W.js +1 -0
- apps/web/dist/assets/diff-D97Zzqfu.js +1 -0
- apps/web/dist/assets/docker-BcOcwvcX.js +1 -0
- apps/web/dist/assets/dotenv-Da5cRb03.js +1 -0
- apps/web/dist/assets/dracula-BzJJZx-M.js +1 -0
- apps/web/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
- apps/web/dist/assets/dream-maker-BtqSS_iP.js +1 -0
- apps/web/dist/assets/edge-BkV0erSs.js +1 -0
- apps/web/dist/assets/elixir-CDX3lj18.js +1 -0
- apps/web/dist/assets/elm-DbKCFpqz.js +1 -0
- apps/web/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
- apps/web/dist/assets/erb-B12qg9BL.js +1 -0
- apps/web/dist/assets/erlang-DsQrWhSR.js +1 -0
- apps/web/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
- apps/web/dist/assets/everforest-light-C8M2exoo.js +1 -0
- apps/web/dist/assets/fennel-BYunw83y.js +1 -0
- apps/web/dist/assets/fish-BvzEVeQv.js +1 -0
- apps/web/dist/assets/fluent-C4IJs8-o.js +1 -0
- apps/web/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- apps/web/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
- apps/web/dist/assets/fsharp-CXgrBDvD.js +1 -0
- apps/web/dist/assets/gdresource-BOOCDP_w.js +1 -0
- apps/web/dist/assets/gdscript-C5YyOfLZ.js +1 -0
- apps/web/dist/assets/gdshader-DkwncUOv.js +1 -0
- apps/web/dist/assets/genie-D0YGMca9.js +1 -0
- apps/web/dist/assets/gherkin-DyxjwDmM.js +1 -0
- apps/web/dist/assets/git-commit-F4YmCXRG.js +1 -0
- apps/web/dist/assets/git-rebase-r7XF79zn.js +1 -0
- apps/web/dist/assets/github-dark-DHJKELXO.js +1 -0
- apps/web/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
- apps/web/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- apps/web/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- apps/web/dist/assets/github-light-DAi9KRSo.js +1 -0
- apps/web/dist/assets/github-light-default-D7oLnXFd.js +1 -0
- apps/web/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- apps/web/dist/assets/gleam-BspZqrRM.js +1 -0
- apps/web/dist/assets/glimmer-js-Rg0-pVw9.js +1 -0
- apps/web/dist/assets/glimmer-ts-U6CK756n.js +1 -0
- apps/web/dist/assets/glsl-DplSGwfg.js +1 -0
- apps/web/dist/assets/gn-n2N0HUVH.js +1 -0
- apps/web/dist/assets/gnuplot-DdkO51Og.js +1 -0
- apps/web/dist/assets/go-CxLEBnE3.js +1 -0
- apps/web/dist/assets/graphql-ChdNCCLP.js +1 -0
- apps/web/dist/assets/groovy-gcz8RCvz.js +1 -0
- apps/web/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- apps/web/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- apps/web/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- apps/web/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- apps/web/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- apps/web/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- apps/web/dist/assets/hack-CaT9iCJl.js +1 -0
- apps/web/dist/assets/haml-B8DHNrY2.js +1 -0
- apps/web/dist/assets/handlebars-BL8al0AC.js +1 -0
- apps/web/dist/assets/haskell-Df6bDoY_.js +1 -0
- apps/web/dist/assets/haxe-CzTSHFRz.js +1 -0
- apps/web/dist/assets/hcl-BWvSN4gD.js +1 -0
- apps/web/dist/assets/highlighted-body-OFNGDK62-C7ptW_BO.js +1 -0
- apps/web/dist/assets/hjson-D5-asLiD.js +1 -0
- apps/web/dist/assets/hlsl-D3lLCCz7.js +1 -0
- apps/web/dist/assets/horizon-BUw7H-hv.js +1 -0
- apps/web/dist/assets/horizon-bright-Cn-bp-IR.js +1 -0
- apps/web/dist/assets/houston-DnULxvSX.js +1 -0
- apps/web/dist/assets/html-GMplVEZG.js +1 -0
- apps/web/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
- apps/web/dist/assets/http-jrhK8wxY.js +1 -0
- apps/web/dist/assets/hurl-irOxFIW8.js +1 -0
- apps/web/dist/assets/hxml-Bvhsp5Yf.js +1 -0
- apps/web/dist/assets/hy-DFXneXwc.js +1 -0
- apps/web/dist/assets/imba-DGztddWO.js +1 -0
- apps/web/dist/assets/index-CN6bosuJ.css +1 -0
- apps/web/dist/assets/index-DNmZUlYg.js +739 -0
- apps/web/dist/assets/ini-BEwlwnbL.js +1 -0
- apps/web/dist/assets/java-CylS5w8V.js +1 -0
- apps/web/dist/assets/javascript-wDzz0qaB.js +1 -0
- apps/web/dist/assets/jinja-4LBKfQ-Z.js +1 -0
- apps/web/dist/assets/jison-wvAkD_A8.js +1 -0
- apps/web/dist/assets/json-Cp-IABpG.js +1 -0
- apps/web/dist/assets/json5-C9tS-k6U.js +1 -0
- apps/web/dist/assets/jsonc-Des-eS-w.js +1 -0
- apps/web/dist/assets/jsonl-DcaNXYhu.js +1 -0
- apps/web/dist/assets/jsonnet-DFQXde-d.js +1 -0
- apps/web/dist/assets/jssm-C2t-YnRu.js +1 -0
- apps/web/dist/assets/jsx-g9-lgVsj.js +1 -0
- apps/web/dist/assets/julia-CxzCAyBv.js +1 -0
- apps/web/dist/assets/just-Cw27pwNe.js +1 -0
- apps/web/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- apps/web/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- apps/web/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
- apps/web/dist/assets/kdl-DV7GczEv.js +1 -0
- apps/web/dist/assets/kotlin-BdnUsdx6.js +1 -0
- apps/web/dist/assets/kusto-DZf3V79B.js +1 -0
- apps/web/dist/assets/laserwave-DUszq2jm.js +1 -0
- apps/web/dist/assets/latex-CWtU0Tv5.js +1 -0
- apps/web/dist/assets/lean-BZvkOJ9d.js +1 -0
- apps/web/dist/assets/less-B1dDrJ26.js +1 -0
- apps/web/dist/assets/light-plus-B7mTdjB0.js +1 -0
- apps/web/dist/assets/liquid-DYVedYrR.js +1 -0
- apps/web/dist/assets/llvm-DjAJT7YJ.js +1 -0
- apps/web/dist/assets/log-2UxHyX5q.js +1 -0
- apps/web/dist/assets/logo-BtOb2qkB.js +1 -0
- apps/web/dist/assets/lua-BaeVxFsk.js +1 -0
- apps/web/dist/assets/luau-C-HG3fhB.js +1 -0
- apps/web/dist/assets/lucide-react-DlS3kJDA.js +7156 -0
- apps/web/dist/assets/make-CHLpvVh8.js +1 -0
- apps/web/dist/assets/markdown-Cvjx9yec.js +1 -0
- apps/web/dist/assets/marko-CnJfTvn9.js +1 -0
- apps/web/dist/assets/material-theme-D5KoaKCx.js +1 -0
- apps/web/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
- apps/web/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- apps/web/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
- apps/web/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- apps/web/dist/assets/matlab-D7o27uSR.js +1 -0
- apps/web/dist/assets/mdc-BMNejdWA.js +1 -0
- apps/web/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- apps/web/dist/assets/mermaid-GHXKKRXX-fpq-tWrh.js +544 -0
- apps/web/dist/assets/mermaid-mWjccvbQ.js +1 -0
- apps/web/dist/assets/min-dark-CafNBF8u.js +1 -0
- apps/web/dist/assets/min-light-CTRr51gU.js +1 -0
- apps/web/dist/assets/mipsasm-CKIfxQSi.js +1 -0
- apps/web/dist/assets/mojo-rZm6bMo-.js +1 -0
- apps/web/dist/assets/monokai-D4h5O-jR.js +1 -0
- apps/web/dist/assets/moonbit-_H4v1dQx.js +1 -0
- apps/web/dist/assets/move-IF9eRakj.js +1 -0
- apps/web/dist/assets/narrat-DRg8JJMk.js +1 -0
- apps/web/dist/assets/nextflow-Zz6hmt5N.js +1 -0
- apps/web/dist/assets/nextflow-groovy-BeH2EWoN.js +1 -0
- apps/web/dist/assets/nginx-BpAMiNFr.js +1 -0
- apps/web/dist/assets/night-owl-C39BiMTA.js +1 -0
- apps/web/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
- apps/web/dist/assets/nim-CVrawwO9.js +1 -0
- apps/web/dist/assets/nix-CwoSXNpI.js +1 -0
- apps/web/dist/assets/nord-Ddv68eIx.js +1 -0
- apps/web/dist/assets/nushell-Cz2AlsmD.js +1 -0
- apps/web/dist/assets/objective-c-DXmwc3jG.js +1 -0
- apps/web/dist/assets/objective-cpp-CLxacb5B.js +1 -0
- apps/web/dist/assets/ocaml-C0hk2d4L.js +1 -0
- apps/web/dist/assets/odin-BBf5iR-q.js +1 -0
- apps/web/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- apps/web/dist/assets/one-light-C3Wv6jpd.js +1 -0
- apps/web/dist/assets/openscad-C4EeE6gA.js +1 -0
- apps/web/dist/assets/pascal-D93ZcfNL.js +1 -0
- apps/web/dist/assets/perl-C0TMdlhV.js +1 -0
- apps/web/dist/assets/php-Dhbhpdrm.js +1 -0
- apps/web/dist/assets/pkl-u5AG7uiY.js +1 -0
- apps/web/dist/assets/plastic-3e1v2bzS.js +1 -0
- apps/web/dist/assets/plsql-ChMvpjG-.js +1 -0
- apps/web/dist/assets/po-BTJTHyun.js +1 -0
- apps/web/dist/assets/poimandres-CS3Unz2-.js +1 -0
- apps/web/dist/assets/polar-C0HS_06l.js +1 -0
- apps/web/dist/assets/postcss-CXtECtnM.js +1 -0
- apps/web/dist/assets/powerquery-CEu0bR-o.js +1 -0
- apps/web/dist/assets/powershell-Dpen1YoG.js +1 -0
- apps/web/dist/assets/prisma-Dd19v3D-.js +1 -0
- apps/web/dist/assets/prolog-CbFg5uaA.js +1 -0
- apps/web/dist/assets/proto-C7zT0LnQ.js +1 -0
- apps/web/dist/assets/pug-CGlum2m_.js +1 -0
- apps/web/dist/assets/puppet-BMWR74SV.js +1 -0
- apps/web/dist/assets/purescript-CklMAg4u.js +1 -0
- apps/web/dist/assets/python-B6aJPvgy.js +1 -0
- apps/web/dist/assets/qml-3beO22l8.js +1 -0
- apps/web/dist/assets/qmldir-C8lEn-DE.js +1 -0
- apps/web/dist/assets/qss-IeuSbFQv.js +1 -0
- apps/web/dist/assets/r-Dspwwk_N.js +1 -0
- apps/web/dist/assets/racket-BqYA7rlc.js +1 -0
- apps/web/dist/assets/raku-DXvB9xmW.js +1 -0
- apps/web/dist/assets/razor-Uh8Bk_45.js +1 -0
- apps/web/dist/assets/red-bN70gL4F.js +1 -0
- apps/web/dist/assets/reg-C-SQnVFl.js +1 -0
- apps/web/dist/assets/regexp-CDVJQ6XC.js +1 -0
- apps/web/dist/assets/rel-C3B-1QV4.js +1 -0
- apps/web/dist/assets/riscv-BM1_JUlF.js +1 -0
- apps/web/dist/assets/ron-D8l8udqQ.js +1 -0
- apps/web/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- apps/web/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- apps/web/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
- apps/web/dist/assets/rosmsg-BJDFO7_C.js +1 -0
- apps/web/dist/assets/rst-BrH8l1NY.js +1 -0
- apps/web/dist/assets/ruby-Dw2BHqvy.js +1 -0
- apps/web/dist/assets/rust-B1yitclQ.js +1 -0
- apps/web/dist/assets/sas-cz2c8ADy.js +1 -0
- apps/web/dist/assets/sass-Cj5Yp3dK.js +1 -0
- apps/web/dist/assets/scala-C151Ov-r.js +1 -0
- apps/web/dist/assets/scheme-C98Dy4si.js +1 -0
- apps/web/dist/assets/scss-OYdSNvt2.js +1 -0
- apps/web/dist/assets/sdbl-DVxCFoDh.js +1 -0
- apps/web/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
- apps/web/dist/assets/shellscript-Yzrsuije.js +1 -0
- apps/web/dist/assets/shellsession-BADoaaVG.js +1 -0
- apps/web/dist/assets/slack-dark-BthQWCQV.js +1 -0
- apps/web/dist/assets/slack-ochin-DqwNpetd.js +1 -0
- apps/web/dist/assets/smalltalk-BERRCDM3.js +1 -0
- apps/web/dist/assets/snazzy-light-Bw305WKR.js +1 -0
- apps/web/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
- apps/web/dist/assets/solarized-light-L9t79GZl.js +1 -0
- apps/web/dist/assets/solidity-rGO070M0.js +1 -0
- apps/web/dist/assets/soy-Brmx7dQM.js +1 -0
- apps/web/dist/assets/sparql-rVzFXLq3.js +1 -0
- apps/web/dist/assets/splunk-BtCnVYZw.js +1 -0
- apps/web/dist/assets/sql-BLtJtn59.js +1 -0
- apps/web/dist/assets/ssh-config-_ykCGR6B.js +1 -0
- apps/web/dist/assets/stata-BH5u7GGu.js +1 -0
- apps/web/dist/assets/stylus-BEDo0Tqx.js +1 -0
- apps/web/dist/assets/surrealql-Bq5Q-fJD.js +1 -0
- apps/web/dist/assets/svelte-C_ipcX3V.js +1 -0
- apps/web/dist/assets/swift-D82vCrfD.js +1 -0
- apps/web/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
- apps/web/dist/assets/system-verilog-CnnmHF94.js +1 -0
- apps/web/dist/assets/systemd-4A_iFExJ.js +1 -0
- apps/web/dist/assets/talonscript-CkByrt1z.js +1 -0
- apps/web/dist/assets/tasl-QIJgUcNo.js +1 -0
- apps/web/dist/assets/tcl-dwOrl1Do.js +1 -0
- apps/web/dist/assets/templ-P3uqSqPl.js +1 -0
- apps/web/dist/assets/terraform-BETggiCN.js +1 -0
- apps/web/dist/assets/tex-idrVyKtj.js +1 -0
- apps/web/dist/assets/tokyo-night-hegEt444.js +1 -0
- apps/web/dist/assets/toml-vGWfd6FD.js +1 -0
- apps/web/dist/assets/ts-tags-zn1MmPIZ.js +1 -0
- apps/web/dist/assets/tsv-B_m7g4N7.js +1 -0
- apps/web/dist/assets/tsx-COt5Ahok.js +1 -0
- apps/web/dist/assets/turtle-BsS91CYL.js +1 -0
- apps/web/dist/assets/twig-DNn4PbVi.js +1 -0
- apps/web/dist/assets/typescript-BPQ3VLAy.js +1 -0
- apps/web/dist/assets/typespec-BGHnOYBU.js +1 -0
- apps/web/dist/assets/typst-DHCkPAjA.js +1 -0
- apps/web/dist/assets/v-BcVCzyr7.js +1 -0
- apps/web/dist/assets/vala-CsfeWuGM.js +1 -0
- apps/web/dist/assets/vb-D17OF-Vu.js +1 -0
- apps/web/dist/assets/verilog-BQ8w6xss.js +1 -0
- apps/web/dist/assets/vesper-DU1UobuO.js +1 -0
- apps/web/dist/assets/vhdl-CeAyd5Ju.js +1 -0
- apps/web/dist/assets/viml-CJc9bBzg.js +1 -0
- apps/web/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
- apps/web/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
- apps/web/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
- apps/web/dist/assets/vue-DN_0RTcg.js +1 -0
- apps/web/dist/assets/vue-html-AaS7Mt5G.js +1 -0
- apps/web/dist/assets/vue-vine-CQOfvN7w.js +1 -0
- apps/web/dist/assets/vyper-CDx5xZoG.js +1 -0
- apps/web/dist/assets/wasm-CG6Dc4jp.js +1 -0
- apps/web/dist/assets/wasm-MzD3tlZU.js +1 -0
- apps/web/dist/assets/wenyan-BV7otONQ.js +1 -0
- apps/web/dist/assets/wgsl-Dx-B1_4e.js +1 -0
- apps/web/dist/assets/wikitext-BhOHFoWU.js +1 -0
- apps/web/dist/assets/wit-5i3qLPDT.js +1 -0
- apps/web/dist/assets/wolfram-lXgVvXCa.js +1 -0
- apps/web/dist/assets/xml-sdJ4AIDG.js +1 -0
- apps/web/dist/assets/xsl-CtQFsRM5.js +1 -0
- apps/web/dist/assets/yaml-Buea-lGh.js +1 -0
- apps/web/dist/assets/zenscript-DVFEvuxE.js +1 -0
- apps/web/dist/assets/zig-VOosw3JB.js +1 -0
- apps/web/dist/favicon.svg +10 -0
- apps/web/dist/index.html +33 -0
- apps/web/dist/protolabs-icon-outline.svg +10 -0
- background/__init__.py +8 -0
- background/manager.py +659 -0
- background/store.py +404 -0
- config/SOUL.md +46 -0
- config/langgraph-config.example.yaml +363 -0
- config/mcp-catalog.json +152 -0
- config/plugin-catalog.json +101 -0
- config/skills/web-research/SKILL.md +60 -0
- config/soul-presets/base.md +36 -0
- config/soul-presets/blank.md +24 -0
- config/soul-presets/coding.md +37 -0
- config/soul-presets/generic-assistant.md +33 -0
- config/soul-presets/project-manager.md +40 -0
- config/soul-presets/research.md +35 -0
- enforcement/__init__.py +1 -0
- enforcement/rate_limiter.py +51 -0
- evals/README.md +194 -0
- evals/__init__.py +0 -0
- evals/client.py +405 -0
- evals/compare.py +94 -0
- evals/judge.py +109 -0
- evals/report.py +142 -0
- evals/retrieval.py +269 -0
- evals/retrieval_gold.yaml +130 -0
- evals/runner.py +708 -0
- evals/sweep.py +349 -0
- evals/tasks.json +406 -0
- evals/verify.py +242 -0
- evals/webhook.py +80 -0
- events/__init__.py +11 -0
- events/bus.py +170 -0
- graph/__init__.py +0 -0
- graph/agent.py +1052 -0
- graph/cache_warmer.py +155 -0
- graph/checkpoint_prune.py +222 -0
- graph/checkpointer.py +97 -0
- graph/compaction_op.py +222 -0
- graph/components.py +55 -0
- graph/config.py +1237 -0
- graph/config_explain.py +201 -0
- graph/config_io.py +1328 -0
- graph/conversation_harvest.py +172 -0
- graph/delegations.py +88 -0
- graph/extensions/__init__.py +0 -0
- graph/extensions/skills.py +86 -0
- graph/fleet/__init__.py +7 -0
- graph/fleet/cli.py +67 -0
- graph/fleet/discovery.py +381 -0
- graph/fleet/member_public.py +78 -0
- graph/fleet/proxy.py +234 -0
- graph/fleet/supervisor.py +954 -0
- graph/goals/__init__.py +13 -0
- graph/goals/controller.py +562 -0
- graph/goals/goal_turn.py +55 -0
- graph/goals/hooks.py +42 -0
- graph/goals/store.py +170 -0
- graph/goals/types.py +124 -0
- graph/goals/verifiers.py +372 -0
- graph/guardrails.py +97 -0
- graph/knobs.py +248 -0
- graph/lifecycle/__init__.py +39 -0
- graph/lifecycle/dispatch.py +194 -0
- graph/lifecycle/hooks.py +62 -0
- graph/llm.py +464 -0
- graph/memory_facts.py +207 -0
- graph/middleware/__init__.py +0 -0
- graph/middleware/audit.py +163 -0
- graph/middleware/compaction.py +39 -0
- graph/middleware/enforcement.py +88 -0
- graph/middleware/knowledge.py +471 -0
- graph/middleware/memory.py +491 -0
- graph/middleware/message_capture.py +32 -0
- graph/middleware/model_override.py +73 -0
- graph/middleware/multimodal_tool.py +85 -0
- graph/middleware/prompt_cache.py +93 -0
- graph/middleware/redaction.py +171 -0
- graph/middleware/request_context.py +61 -0
- graph/middleware/stall_guard.py +165 -0
- graph/middleware/steering.py +54 -0
- graph/middleware/subagent_fence.py +57 -0
- graph/middleware/tool_call_repair.py +65 -0
- graph/middleware/tool_deferral.py +110 -0
- graph/middleware/trace_context.py +121 -0
- graph/middleware/wait_yield.py +55 -0
- graph/model_cli.py +151 -0
- graph/model_window.py +107 -0
- graph/multimodal.py +199 -0
- graph/output_format.py +67 -0
- graph/plugins/__init__.py +16 -0
- graph/plugins/chat_surface.py +153 -0
- graph/plugins/cli.py +212 -0
- graph/plugins/host.py +44 -0
- graph/plugins/installer.py +1084 -0
- graph/plugins/loader.py +592 -0
- graph/plugins/manifest.py +438 -0
- graph/plugins/pconfig.py +175 -0
- graph/plugins/registry.py +497 -0
- graph/plugins/scaffold.py +521 -0
- graph/plugins/testkit.py +392 -0
- graph/prompts.py +264 -0
- graph/review/__init__.py +12 -0
- graph/review/findings.py +227 -0
- graph/rewind_op.py +179 -0
- graph/sdk.py +820 -0
- graph/settings_schema.py +1333 -0
- graph/skills/__init__.py +1 -0
- graph/skills/authoring.py +132 -0
- graph/skills/cli.py +144 -0
- graph/skills/curator.py +558 -0
- graph/skills/index.py +445 -0
- graph/skills/layered.py +142 -0
- graph/skills/loader.py +151 -0
- graph/slash_commands.py +263 -0
- graph/state.py +50 -0
- graph/steering.py +75 -0
- graph/structured_skill.py +79 -0
- graph/subagents/__init__.py +0 -0
- graph/subagents/config.py +610 -0
- graph/supervisor.py +330 -0
- graph/telemetry.py +174 -0
- graph/watches/__init__.py +7 -0
- graph/watches/controller.py +289 -0
- graph/watches/hooks.py +37 -0
- graph/watches/store.py +130 -0
- graph/watches/types.py +71 -0
- graph/workspaces/__init__.py +8 -0
- graph/workspaces/cli.py +84 -0
- graph/workspaces/manager.py +545 -0
- inbox/__init__.py +5 -0
- inbox/store.py +207 -0
- infra/__init__.py +1 -0
- infra/autostart.py +279 -0
- infra/cache.py +98 -0
- infra/media.py +287 -0
- infra/paths.py +704 -0
- infra/secrets/__init__.py +56 -0
- infra/secrets/base.py +126 -0
- infra/secrets/hydrate.py +317 -0
- infra/secrets/infisical.py +154 -0
- ingestion/__init__.py +44 -0
- ingestion/engine.py +419 -0
- knowledge/__init__.py +32 -0
- knowledge/backend.py +54 -0
- knowledge/chunking.py +156 -0
- knowledge/hybrid_store.py +486 -0
- knowledge/layered.py +141 -0
- knowledge/store.py +1231 -0
- knowledge/trust.py +88 -0
- observability/__init__.py +1 -0
- observability/audit.py +174 -0
- observability/injection_log.py +184 -0
- observability/logging_config.py +83 -0
- observability/metrics.py +185 -0
- observability/metrics_store.py +143 -0
- observability/pricing.py +82 -0
- observability/telemetry_store.py +264 -0
- observability/trace_export.py +374 -0
- observability/tracing.py +444 -0
- operator_api/__init__.py +1 -0
- operator_api/chat_routes.py +379 -0
- operator_api/config_routes.py +300 -0
- operator_api/console_handlers.py +671 -0
- operator_api/flags_routes.py +26 -0
- operator_api/fleet_routes.py +383 -0
- operator_api/injection_routes.py +144 -0
- operator_api/knowledge_routes.py +753 -0
- operator_api/mcp_routes.py +273 -0
- operator_api/memory_routes.py +252 -0
- operator_api/operations_routes.py +26 -0
- operator_api/paths.py +48 -0
- operator_api/plugin_routes.py +442 -0
- operator_api/routes.py +638 -0
- operator_api/runtime.py +168 -0
- operator_api/runtime_routes.py +63 -0
- operator_api/secrets_routes.py +120 -0
- operator_api/subagents.py +88 -0
- operator_api/telemetry_routes.py +132 -0
- operator_api/theme_routes.py +58 -0
- operator_api/web.py +86 -0
- ops/__init__.py +97 -0
- ops/cli.py +33 -0
- ops/config.py +85 -0
- ops/fleet.py +35 -0
- ops/knowledge.py +211 -0
- ops/plugins.py +121 -0
- protolabs_agent-0.101.0.dist-info/METADATA +35 -0
- protolabs_agent-0.101.0.dist-info/RECORD +609 -0
- protolabs_agent-0.101.0.dist-info/WHEEL +4 -0
- protolabs_agent-0.101.0.dist-info/entry_points.txt +2 -0
- protolabs_agent-0.101.0.dist-info/licenses/LICENSE +21 -0
- runtime/__init__.py +5 -0
- runtime/acp_agents.py +36 -0
- runtime/acp_runtime.py +332 -0
- runtime/cli.py +371 -0
- runtime/context.py +184 -0
- runtime/flags.py +140 -0
- runtime/operator_mcp_tools.py +118 -0
- runtime/state.py +96 -0
- scheduler/__init__.py +16 -0
- scheduler/interface.py +149 -0
- scheduler/local.py +761 -0
- security/__init__.py +1 -0
- security/egress.py +149 -0
- security/policy.py +108 -0
- server/__init__.py +1125 -0
- server/__main__.py +12 -0
- server/a2a.py +736 -0
- server/agent_init.py +2274 -0
- server/chat.py +2063 -0
- server/cli.py +294 -0
- server/knowledge_cli.py +57 -0
- server/media.py +32 -0
- server/operator_mcp.py +147 -0
- static/favicon.svg +10 -0
- static/icons/apple-touch-icon.png +0 -0
- static/icons/icon-192.png +0 -0
- static/icons/icon-192.svg +10 -0
- static/icons/icon-512.png +0 -0
- static/icons/icon-512.svg +10 -0
- static/manifest.json +31 -0
- static/sw.js +98 -0
- tasks/__init__.py +11 -0
- tasks/store.py +228 -0
- tools/__init__.py +0 -0
- tools/a2a_parse.py +37 -0
- tools/fallbacks.py +79 -0
- tools/fs_tools.py +316 -0
- tools/gh_cli.py +68 -0
- tools/lg_tools.py +1774 -0
- tools/mcp_session_pool.py +327 -0
- tools/mcp_tools.py +432 -0
- tools/shell.py +98 -0
a2a_impl/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""A2A protocol layer — request auth/origin, the AgentExecutor, and the durable task/push stores (split out of the root modules)."""
|
a2a_impl/auth.py
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"""Request-time auth + origin enforcement — default-deny posture.
|
|
2
|
+
|
|
3
|
+
``a2a-sdk`` advertises security schemes on the agent card but does NOT enforce
|
|
4
|
+
them on the wire — enforcement is the host's job. This module is a small
|
|
5
|
+
Starlette/FastAPI middleware that guards **every** path except an explicit public
|
|
6
|
+
allowlist:
|
|
7
|
+
|
|
8
|
+
- **Bearer** — ``Authorization: Bearer <token>`` validated against the
|
|
9
|
+
configured token (``auth.token`` in YAML or ``A2A_AUTH_TOKEN`` env). No-op
|
|
10
|
+
when unset (open mode, logged at WARNING).
|
|
11
|
+
- **X-API-Key** — legacy ``<AGENT>_API_KEY`` header, validated when set.
|
|
12
|
+
- **Origin** — ``A2A_ALLOWED_ORIGINS`` allowlist for browser callers. No-op
|
|
13
|
+
when unset or ``*``.
|
|
14
|
+
|
|
15
|
+
Default-deny: anything NOT on the public allowlist requires auth. The SSE
|
|
16
|
+
endpoint ``/api/events`` accepts a short-lived HMAC query-string token so
|
|
17
|
+
browser ``EventSource`` clients (which cannot send ``Authorization`` headers)
|
|
18
|
+
can authenticate.
|
|
19
|
+
|
|
20
|
+
The active bearer token lives in a module-level holder so a wizard/drawer reload
|
|
21
|
+
can update it live via ``set_bearer_token`` without re-registering routes.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import base64
|
|
27
|
+
import hashlib
|
|
28
|
+
import hmac
|
|
29
|
+
import json
|
|
30
|
+
import logging
|
|
31
|
+
import os
|
|
32
|
+
import re
|
|
33
|
+
import time
|
|
34
|
+
|
|
35
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
36
|
+
from starlette.requests import Request
|
|
37
|
+
from starlette.responses import JSONResponse
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
# Live-updatable bearer token (None = open mode for bearer).
|
|
42
|
+
_BEARER: list[str | None] = [None]
|
|
43
|
+
# Optional federation token (ADR 0066) — a second credential confined to the /a2a + /v1
|
|
44
|
+
# consumer surfaces and DENIED the /api operator surface. None = no federation tier.
|
|
45
|
+
_FEDERATION: list[str | None] = [None]
|
|
46
|
+
# X-API-Key (env-seeded at install; constant for the process).
|
|
47
|
+
_API_KEY: list[str] = [""]
|
|
48
|
+
# Allowed origins: None = verification disabled; list = allowlist.
|
|
49
|
+
_ALLOWED_ORIGINS: list[list[str] | None] = [None]
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Public allowlist — paths/prefixes that pass WITHOUT auth.
|
|
53
|
+
# Everything else requires bearer / X-API-Key (default-deny).
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
_PUBLIC_PREFIXES = (
|
|
56
|
+
"/healthz",
|
|
57
|
+
"/.well-known/",
|
|
58
|
+
"/app",
|
|
59
|
+
"/manifest.json",
|
|
60
|
+
"/sw.js",
|
|
61
|
+
"/favicon.svg",
|
|
62
|
+
"/favicon.ico",
|
|
63
|
+
"/static/",
|
|
64
|
+
"/_ds/",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# /metrics is CONDITIONALLY public — see ``_metrics_public``. It carries
|
|
68
|
+
# operational data (model/tool inventory, cost, traffic) so it is exposed without
|
|
69
|
+
# auth only in open mode (no token configured). Once a bearer / X-API-Key gates
|
|
70
|
+
# the surface, the Prometheus scraper must authenticate too — set
|
|
71
|
+
# ``PROTOAGENT_PUBLIC_METRICS=1`` to keep it anonymous behind a network boundary.
|
|
72
|
+
|
|
73
|
+
# Plugin-declared auth-exempt prefixes. Set once at startup (and on reload) from
|
|
74
|
+
# enabled plugins' manifest ``public_paths`` — each already validated to the
|
|
75
|
+
# plugin's own ``/plugins/<id>/`` namespace by the manifest parser. This lets a
|
|
76
|
+
# plugin serve an inbound webhook (no bearer — verified by its own HMAC) or a
|
|
77
|
+
# public view page even when a bearer gates everything else. A plugin can ONLY
|
|
78
|
+
# exempt its own routes; ``set_public_prefixes`` rejects anything else as
|
|
79
|
+
# defence-in-depth.
|
|
80
|
+
_PLUGIN_PUBLIC: list[str] = []
|
|
81
|
+
|
|
82
|
+
# SSE token lifetime (seconds).
|
|
83
|
+
_SSE_TOKEN_LIFETIME = 30
|
|
84
|
+
|
|
85
|
+
# A plugin public-prefix must be a real SUBTREE of its own namespace —
|
|
86
|
+
# ``/plugins/<id>/…`` or ``/api/plugins/<id>/…`` with a trailing slash after the
|
|
87
|
+
# id segment — so a bare core route like ``/api/plugins/install`` can never be
|
|
88
|
+
# prefix-matched into the exempt set (defence-in-depth behind the manifest
|
|
89
|
+
# parser, which applies the same boundary).
|
|
90
|
+
_PLUGIN_NS_RE = re.compile(r"^/(?:api/)?plugins/[^/]+/")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def set_public_prefixes(prefixes) -> None:
|
|
94
|
+
"""Replace the plugin-declared public-prefix set (idempotent + reload-safe).
|
|
95
|
+
|
|
96
|
+
Each prefix must live under a ``/plugins/<id>/`` namespace — a plugin can
|
|
97
|
+
exempt its own routes, never a core path. Non-conforming entries are dropped
|
|
98
|
+
with a warning."""
|
|
99
|
+
cleaned: list[str] = []
|
|
100
|
+
for p in prefixes or []:
|
|
101
|
+
s = str(p).strip()
|
|
102
|
+
if not s:
|
|
103
|
+
continue
|
|
104
|
+
if _PLUGIN_NS_RE.match(s):
|
|
105
|
+
cleaned.append(s)
|
|
106
|
+
else:
|
|
107
|
+
logger.warning(
|
|
108
|
+
"[a2a] ignoring plugin public prefix %r — must be under /plugins/<id>/ or /api/plugins/<id>/", s
|
|
109
|
+
)
|
|
110
|
+
_PLUGIN_PUBLIC[:] = cleaned
|
|
111
|
+
if cleaned:
|
|
112
|
+
logger.info("[a2a] %d plugin-declared auth-exempt path(s): %s", len(cleaned), ", ".join(cleaned))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def public_prefixes() -> list[str]:
|
|
116
|
+
"""The live plugin-declared auth-exempt prefixes (post-validation) — exactly what
|
|
117
|
+
``_is_public`` enforces. Served on the public-paths well-known endpoint so a fleet
|
|
118
|
+
hub can defer its public decision to this member (#1890)."""
|
|
119
|
+
return list(_PLUGIN_PUBLIC)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Fleet-proxied member view pages (#1890). A member's plugin view page is public
|
|
123
|
+
# *chrome* on the member itself (see ``set_public_prefixes``) — but the console
|
|
124
|
+
# iframes it through the hub as ``/agents/<slug>/plugins/<id>/…``, a plain
|
|
125
|
+
# navigation that cannot carry the operator bearer, and the hub's own public list
|
|
126
|
+
# is built from the HUB's manifests (the member may run plugins the hub doesn't).
|
|
127
|
+
# The member is the authority on what it serves anonymously, so the hub defers:
|
|
128
|
+
# for a slug-prefixed path inside a plugin namespace, an injected async resolver
|
|
129
|
+
# ``(slug, rest) -> bool`` answers "would the member serve this anonymously?"
|
|
130
|
+
# (graph/fleet/member_public.py — fetched from the member's public-paths endpoint,
|
|
131
|
+
# TTL-cached, fail-closed). Injected from the server bootstrap so this module
|
|
132
|
+
# stays host-free.
|
|
133
|
+
_MEMBER_PUBLIC: list = [None]
|
|
134
|
+
|
|
135
|
+
_AGENTS_RE = re.compile(r"^/agents/([^/]+)(/.+)$")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def set_member_public_resolver(fn) -> None:
|
|
139
|
+
"""Install the async ``(slug, rest) -> bool`` member-public resolver (None = off)."""
|
|
140
|
+
_MEMBER_PUBLIC[0] = fn
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _metrics_public() -> bool:
|
|
144
|
+
"""Whether ``/metrics`` is reachable without auth.
|
|
145
|
+
|
|
146
|
+
Default: only in open mode (no bearer AND no X-API-Key configured), where the
|
|
147
|
+
whole surface is already unauthenticated. When any token gates the surface,
|
|
148
|
+
``/metrics`` is gated too — unless ``PROTOAGENT_PUBLIC_METRICS=1`` keeps it
|
|
149
|
+
open for an anonymous Prometheus scraper fenced by a network boundary.
|
|
150
|
+
"""
|
|
151
|
+
if os.environ.get("PROTOAGENT_PUBLIC_METRICS", "").strip().lower() in ("1", "true", "yes"):
|
|
152
|
+
return True
|
|
153
|
+
return _BEARER[0] is None and not _API_KEY[0]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _is_public(path: str) -> bool:
|
|
157
|
+
"""Return True when ``path`` is on the public allowlist (no auth needed)."""
|
|
158
|
+
if any(path.startswith(p) for p in _PUBLIC_PREFIXES):
|
|
159
|
+
return True
|
|
160
|
+
if any(path.startswith(p) for p in _PLUGIN_PUBLIC):
|
|
161
|
+
return True
|
|
162
|
+
if path.startswith("/metrics") and _metrics_public():
|
|
163
|
+
return True
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _requires_operator(path: str) -> bool:
|
|
168
|
+
"""Paths that require the OPERATOR credential (ADR 0066 R1 ceiling).
|
|
169
|
+
|
|
170
|
+
The ``/api`` operator/console surface — plugin install+enable (host code-exec),
|
|
171
|
+
config/SOUL rewrite, subagent runs, the operator goal set-path — is operator-only; a
|
|
172
|
+
configured federation token is denied it (403). ``/a2a`` + ``/v1`` are the
|
|
173
|
+
federation/consumer surfaces and are NOT operator-only. Public + SSE-token paths never
|
|
174
|
+
reach the ceiling (handled earlier in dispatch). The substring form also catches the
|
|
175
|
+
fleet-proxy variants (``/active/<slug>/api/…``, ``/agents/<slug>/api/…``)."""
|
|
176
|
+
return "/api/" in path or path == "/api" or path.endswith("/api")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def set_bearer_token(token: str | None) -> None:
|
|
180
|
+
"""Update the active bearer token at runtime (wizard/drawer reload)."""
|
|
181
|
+
_BEARER[0] = (token or "").strip() or None
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def set_federation_token(token: str | None) -> None:
|
|
185
|
+
"""Update the federation token at runtime (wizard/drawer reload). None = no federation tier."""
|
|
186
|
+
_FEDERATION[0] = (token or "").strip() or None
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def configure(
|
|
190
|
+
*, bearer_token: str | None, api_key: str, allowed_origins_raw: str, federation_token: str | None = None
|
|
191
|
+
) -> None:
|
|
192
|
+
"""Seed the guard at route-registration time.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
bearer_token: from YAML ``auth.token``. The caller is authoritative:
|
|
196
|
+
``None`` means "unspecified" and falls back to ``A2A_AUTH_TOKEN``;
|
|
197
|
+
an explicit ``""`` means "bearer off" (e.g. an apiKey-only agent)
|
|
198
|
+
and does NOT fall back — otherwise a stray env var would silently
|
|
199
|
+
enable bearer auth the card never advertises. Empty/whitespace ->
|
|
200
|
+
open mode.
|
|
201
|
+
api_key: the ``<AGENT>_API_KEY`` value; "" disables the X-API-Key check.
|
|
202
|
+
allowed_origins_raw: ``A2A_ALLOWED_ORIGINS`` value ("" = disabled,
|
|
203
|
+
"*" = disabled, else comma-separated allowlist).
|
|
204
|
+
"""
|
|
205
|
+
raw_bearer = bearer_token if bearer_token is not None else os.environ.get("A2A_AUTH_TOKEN", "")
|
|
206
|
+
seed = (raw_bearer or "").strip()
|
|
207
|
+
_BEARER[0] = seed or None
|
|
208
|
+
if _BEARER[0] is None:
|
|
209
|
+
logger.warning("[a2a] A2A auth token not configured — endpoint is open")
|
|
210
|
+
|
|
211
|
+
# Federation token (ADR 0066) — same authoritative-vs-env-fallback rule as the bearer.
|
|
212
|
+
raw_fed = federation_token if federation_token is not None else os.environ.get("A2A_FEDERATION_TOKEN", "")
|
|
213
|
+
_FEDERATION[0] = (raw_fed or "").strip() or None
|
|
214
|
+
if _FEDERATION[0] is not None and _BEARER[0] is None:
|
|
215
|
+
logger.warning("[a2a] federation_token set but no operator bearer — federation tier is inert (open mode)")
|
|
216
|
+
if _FEDERATION[0] is not None and _FEDERATION[0] == _BEARER[0]:
|
|
217
|
+
logger.warning("[a2a] federation_token equals the operator token — federation tier collapses to operator")
|
|
218
|
+
|
|
219
|
+
_API_KEY[0] = api_key or ""
|
|
220
|
+
|
|
221
|
+
raw = (allowed_origins_raw or "").strip()
|
|
222
|
+
if not raw:
|
|
223
|
+
logger.warning("[a2a] A2A_ALLOWED_ORIGINS not set — origin verification disabled")
|
|
224
|
+
_ALLOWED_ORIGINS[0] = None
|
|
225
|
+
elif raw == "*":
|
|
226
|
+
_ALLOWED_ORIGINS[0] = None
|
|
227
|
+
else:
|
|
228
|
+
_ALLOWED_ORIGINS[0] = [o.strip().lower() for o in raw.split(",") if o.strip()]
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# ---------------------------------------------------------------------------
|
|
232
|
+
# Short-lived SSE query-string token (Part 3)
|
|
233
|
+
# ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def generate_sse_token(session_id: str = "") -> str:
|
|
237
|
+
"""Return a base64url-encoded, HMAC-signed token valid for ``_SSE_TOKEN_LIFETIME`` seconds.
|
|
238
|
+
|
|
239
|
+
The token is a JSON payload ``{session_id, exp}`` concatenated with an
|
|
240
|
+
HMAC-SHA256 signature. The signing key is the active bearer token — when
|
|
241
|
+
no bearer is configured (open mode) the function returns an empty string
|
|
242
|
+
(SSE is already unrestricted).
|
|
243
|
+
"""
|
|
244
|
+
key = _BEARER[0]
|
|
245
|
+
if not key:
|
|
246
|
+
return ""
|
|
247
|
+
payload = json.dumps({"sid": session_id, "exp": int(time.time()) + _SSE_TOKEN_LIFETIME}, separators=(",", ":"))
|
|
248
|
+
sig = hmac.new(key.encode(), payload.encode(), hashlib.sha256).digest()
|
|
249
|
+
return base64.urlsafe_b64encode(payload.encode() + b"." + sig).decode()
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _validate_sse_token(token: str) -> bool:
|
|
253
|
+
"""Validate a query-string SSE token in constant time. Returns True when valid."""
|
|
254
|
+
key = _BEARER[0]
|
|
255
|
+
if not key:
|
|
256
|
+
return True # open mode — no bearer ⇒ no token needed
|
|
257
|
+
if not token:
|
|
258
|
+
return False
|
|
259
|
+
try:
|
|
260
|
+
raw = base64.urlsafe_b64decode(token.encode())
|
|
261
|
+
except Exception:
|
|
262
|
+
return False
|
|
263
|
+
# HMAC-SHA256 is always 32 bytes; the delimiter "." is the byte before it.
|
|
264
|
+
# Split by known offset instead of rsplit to avoid ambiguity when the
|
|
265
|
+
# signature bytes happen to contain 0x2e (".").
|
|
266
|
+
_SIG_LEN = 32
|
|
267
|
+
if len(raw) < _SIG_LEN + 2 or raw[-_SIG_LEN - 1 : -_SIG_LEN] != b".":
|
|
268
|
+
return False
|
|
269
|
+
payload_bytes = raw[: -_SIG_LEN - 1]
|
|
270
|
+
sig = raw[-_SIG_LEN:]
|
|
271
|
+
expected = hmac.new(key.encode(), payload_bytes, hashlib.sha256).digest()
|
|
272
|
+
if not hmac.compare_digest(sig, expected):
|
|
273
|
+
return False
|
|
274
|
+
try:
|
|
275
|
+
data = json.loads(payload_bytes)
|
|
276
|
+
except Exception:
|
|
277
|
+
return False
|
|
278
|
+
exp = data.get("exp", 0)
|
|
279
|
+
if time.time() > exp:
|
|
280
|
+
return False
|
|
281
|
+
return True
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _unauthorized(detail: str) -> JSONResponse:
|
|
285
|
+
return JSONResponse({"detail": detail}, status_code=401)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class A2AAuthMiddleware(BaseHTTPMiddleware):
|
|
289
|
+
"""Default-deny auth: everything except the public allowlist requires auth."""
|
|
290
|
+
|
|
291
|
+
async def dispatch(self, request: Request, call_next):
|
|
292
|
+
path = request.url.path
|
|
293
|
+
|
|
294
|
+
# Public allowlist — pass without auth.
|
|
295
|
+
if _is_public(path):
|
|
296
|
+
return await call_next(request)
|
|
297
|
+
|
|
298
|
+
# Fleet-proxied member-public paths (#1890): ``/agents/<slug>/<rest>`` where
|
|
299
|
+
# ``<rest>`` sits inside a plugin namespace defers to the MEMBER's own public
|
|
300
|
+
# list — its view pages are public chrome THERE, and the iframe navigation
|
|
301
|
+
# cannot carry a bearer. Consulted only when a credential actually gates this
|
|
302
|
+
# hub (open mode passes below anyway) and only for the plugin namespace —
|
|
303
|
+
# never ``/agents/<slug>/api/…``. ``request.state.member_public`` tells the
|
|
304
|
+
# fleet proxy NOT to lend a stored remote bearer to this request.
|
|
305
|
+
resolver = _MEMBER_PUBLIC[0]
|
|
306
|
+
if resolver is not None and (_BEARER[0] is not None or _API_KEY[0]):
|
|
307
|
+
m = _AGENTS_RE.match(path)
|
|
308
|
+
if m and _PLUGIN_NS_RE.match(m.group(2)):
|
|
309
|
+
try:
|
|
310
|
+
member_public = await resolver(m.group(1), m.group(2))
|
|
311
|
+
except Exception: # noqa: BLE001 — resolver trouble = fail closed to normal auth
|
|
312
|
+
logger.exception("[a2a] member-public resolver failed for %s", path)
|
|
313
|
+
member_public = False
|
|
314
|
+
if member_public:
|
|
315
|
+
request.state.member_public = True
|
|
316
|
+
return await call_next(request)
|
|
317
|
+
|
|
318
|
+
# Core media store (#1929): ``/media/<file>`` serves tool-generated artifacts
|
|
319
|
+
# that the console chat renders as inline ``<img>`` tags — a plain browser
|
|
320
|
+
# fetch that cannot carry an Authorization header. Each saved URL carries a
|
|
321
|
+
# per-file HMAC signature minted at save time (``infra/media.py``); a request
|
|
322
|
+
# passes iff the signature verifies (or the store is explicitly opted public
|
|
323
|
+
# via ``media.public``). Anything else falls through to the normal bearer /
|
|
324
|
+
# X-API-Key checks below — default-deny holds.
|
|
325
|
+
if path.startswith("/media/"):
|
|
326
|
+
try:
|
|
327
|
+
from infra.media import request_allowed as _media_allowed
|
|
328
|
+
|
|
329
|
+
if _media_allowed(path, request.query_params.get("sig", "")):
|
|
330
|
+
return await call_next(request)
|
|
331
|
+
except Exception: # noqa: BLE001 — a check failure fails CLOSED to normal auth
|
|
332
|
+
logger.exception("[a2a] media access check failed for %s", path)
|
|
333
|
+
|
|
334
|
+
# SSE endpoint: accept either a valid query-string token OR a bearer header.
|
|
335
|
+
# The query token is for browser EventSource clients that cannot send headers.
|
|
336
|
+
if path == "/api/events" or path.endswith("/api/events"):
|
|
337
|
+
sse_token = request.query_params.get("token", "")
|
|
338
|
+
if _validate_sse_token(sse_token):
|
|
339
|
+
return await call_next(request)
|
|
340
|
+
# Fall through to the normal bearer/X-API-Key check below — a
|
|
341
|
+
# server-to-server caller with an Authorization header still passes.
|
|
342
|
+
|
|
343
|
+
# X-API-Key (legacy) — enforced only when configured.
|
|
344
|
+
api_key = _API_KEY[0]
|
|
345
|
+
if api_key and not hmac.compare_digest(request.headers.get("x-api-key", "") or "", api_key):
|
|
346
|
+
return _unauthorized("Unauthorized")
|
|
347
|
+
|
|
348
|
+
# Bearer — enforced only when configured. Classify which credential matched
|
|
349
|
+
# (ADR 0066): the operator token → full access; a configured federation token →
|
|
350
|
+
# the /a2a + /v1 consumer surfaces only (the /api ceiling below denies it the
|
|
351
|
+
# operator surface). Open mode + single-token mode resolve to operator (R3
|
|
352
|
+
# backward-compat: unset federation_token ⇒ this is the old single-token check).
|
|
353
|
+
active = _BEARER[0]
|
|
354
|
+
fed = _FEDERATION[0]
|
|
355
|
+
tier = "operator"
|
|
356
|
+
if active:
|
|
357
|
+
header = request.headers.get("Authorization", "")
|
|
358
|
+
if not header.startswith("Bearer "):
|
|
359
|
+
return _unauthorized("Unauthorized: expected 'Authorization: Bearer <token>'")
|
|
360
|
+
token = header[len("Bearer ") :]
|
|
361
|
+
# Constant-time compare against each configured secret; classify by which
|
|
362
|
+
# matched. Trust = the matched secret, never the path/Origin/loopback (R5).
|
|
363
|
+
is_operator = hmac.compare_digest(token, active)
|
|
364
|
+
is_federation = fed is not None and hmac.compare_digest(token, fed)
|
|
365
|
+
if is_operator:
|
|
366
|
+
tier = "operator"
|
|
367
|
+
elif is_federation:
|
|
368
|
+
tier = "federation"
|
|
369
|
+
else:
|
|
370
|
+
return _unauthorized("Unauthorized: invalid bearer token")
|
|
371
|
+
|
|
372
|
+
# R1 path ceiling (ADR 0066): a federation credential is denied the /api operator
|
|
373
|
+
# surface — otherwise the token split is cosmetic (it has RCE via
|
|
374
|
+
# /api/plugins/install anyway). /a2a + /v1 stay open to either tier.
|
|
375
|
+
if tier == "federation" and _requires_operator(path):
|
|
376
|
+
return JSONResponse({"detail": "Forbidden: operator credential required"}, status_code=403)
|
|
377
|
+
request.state.trust_tier = tier
|
|
378
|
+
|
|
379
|
+
# Origin — enforced only when an allowlist is set AND an Origin is
|
|
380
|
+
# present. Origin is a browser-only header; server-to-server callers
|
|
381
|
+
# (the hub, the LocalScheduler loopback) send none and must not be
|
|
382
|
+
# rejected for it.
|
|
383
|
+
allowed = _ALLOWED_ORIGINS[0]
|
|
384
|
+
if allowed is not None:
|
|
385
|
+
origin = request.headers.get("Origin")
|
|
386
|
+
if origin is not None and origin.lower() not in allowed:
|
|
387
|
+
return JSONResponse({"detail": "Forbidden: origin not allowed"}, status_code=403)
|
|
388
|
+
|
|
389
|
+
return await call_next(request)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def install(
|
|
393
|
+
app, *, bearer_token: str | None, api_key: str, allowed_origins_raw: str, federation_token: str | None = None
|
|
394
|
+
) -> None:
|
|
395
|
+
"""Configure the guard and add the middleware to ``app``."""
|
|
396
|
+
configure(
|
|
397
|
+
bearer_token=bearer_token,
|
|
398
|
+
api_key=api_key,
|
|
399
|
+
allowed_origins_raw=allowed_origins_raw,
|
|
400
|
+
federation_token=federation_token,
|
|
401
|
+
)
|
|
402
|
+
app.add_middleware(A2AAuthMiddleware)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
_LOOPBACK_HOSTS = ("127.0.0.1", "localhost", "::1")
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def evaluate_open_bind(host: str, *, bearer_configured: bool, allow_open: bool) -> tuple[bool, str | None]:
|
|
409
|
+
"""Boot-time gate for binding a non-loopback host without an auth token.
|
|
410
|
+
|
|
411
|
+
An unauthenticated non-loopback bind exposes the full operator API
|
|
412
|
+
(plugin install+enable = code execution, config/SOUL rewrite, subagent
|
|
413
|
+
runs) to anything that can reach the port — so it is refused unless the
|
|
414
|
+
operator explicitly opts in with ``PROTOAGENT_ALLOW_OPEN=1`` (the posture
|
|
415
|
+
for binds fenced by a published-port/network-policy boundary, e.g. a
|
|
416
|
+
container publishing to 127.0.0.1 only).
|
|
417
|
+
|
|
418
|
+
Returns ``(allowed, message)``: ``(True, None)`` silent, ``(True, msg)``
|
|
419
|
+
allowed with a warning to log, ``(False, msg)`` refuse startup.
|
|
420
|
+
"""
|
|
421
|
+
if host in _LOOPBACK_HOSTS or bearer_configured:
|
|
422
|
+
return True, None
|
|
423
|
+
if allow_open:
|
|
424
|
+
return True, (
|
|
425
|
+
f"[security] binding {host} with NO A2A auth token "
|
|
426
|
+
"(PROTOAGENT_ALLOW_OPEN=1) — the agent + operator API are open to "
|
|
427
|
+
"anything that can reach this port. Make sure a network boundary "
|
|
428
|
+
"(localhost-published port, firewall, network policy) fences it."
|
|
429
|
+
)
|
|
430
|
+
return False, (
|
|
431
|
+
f"[security] refusing to bind {host} with NO A2A auth token — the "
|
|
432
|
+
"operator API (/api/*, /v1/*) includes plugin install/enable (code "
|
|
433
|
+
"execution) and config rewrite. Set auth.token in "
|
|
434
|
+
"langgraph-config.yaml or A2A_AUTH_TOKEN, bind 127.0.0.1 (the "
|
|
435
|
+
"default), or set PROTOAGENT_ALLOW_OPEN=1 if a network boundary "
|
|
436
|
+
"fences this port."
|
|
437
|
+
)
|