babelagent 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. babelagent-0.1.0/.github/workflows/ci.yml +39 -0
  2. babelagent-0.1.0/.github/workflows/docs.yml +47 -0
  3. babelagent-0.1.0/.github/workflows/publish.yml +27 -0
  4. babelagent-0.1.0/.gitignore +34 -0
  5. babelagent-0.1.0/CHANGELOG.md +61 -0
  6. babelagent-0.1.0/CITATION.cff +22 -0
  7. babelagent-0.1.0/LICENSE +21 -0
  8. babelagent-0.1.0/PKG-INFO +135 -0
  9. babelagent-0.1.0/README.md +83 -0
  10. babelagent-0.1.0/brand/extra.css +135 -0
  11. babelagent-0.1.0/docs/SECURITY.md +62 -0
  12. babelagent-0.1.0/docs/STORY.md +247 -0
  13. babelagent-0.1.0/docs/a2a.md +68 -0
  14. babelagent-0.1.0/docs/adapters.md +78 -0
  15. babelagent-0.1.0/docs/api.md +45 -0
  16. babelagent-0.1.0/docs/changelog.md +1 -0
  17. babelagent-0.1.0/docs/index.md +83 -0
  18. babelagent-0.1.0/docs/posts/babelagent-the-problem.md +33 -0
  19. babelagent-0.1.0/docs/quickstart.md +96 -0
  20. babelagent-0.1.0/docs/stylesheets/extra.css +135 -0
  21. babelagent-0.1.0/mkdocs.yml +81 -0
  22. babelagent-0.1.0/pyproject.toml +85 -0
  23. babelagent-0.1.0/src/babelagent/__init__.py +87 -0
  24. babelagent-0.1.0/src/babelagent/__main__.py +6 -0
  25. babelagent-0.1.0/src/babelagent/adapters/__init__.py +22 -0
  26. babelagent-0.1.0/src/babelagent/adapters/a2a_agent.py +143 -0
  27. babelagent-0.1.0/src/babelagent/adapters/auto.py +137 -0
  28. babelagent-0.1.0/src/babelagent/adapters/base.py +104 -0
  29. babelagent-0.1.0/src/babelagent/adapters/callable_agent.py +52 -0
  30. babelagent-0.1.0/src/babelagent/adapters/frameworks/__init__.py +81 -0
  31. babelagent-0.1.0/src/babelagent/adapters/http_agent.py +278 -0
  32. babelagent-0.1.0/src/babelagent/adapters/llm_agent.py +117 -0
  33. babelagent-0.1.0/src/babelagent/adapters/mcp_agent.py +57 -0
  34. babelagent-0.1.0/src/babelagent/config.py +42 -0
  35. babelagent-0.1.0/src/babelagent/core/__init__.py +47 -0
  36. babelagent-0.1.0/src/babelagent/core/agent.py +74 -0
  37. babelagent-0.1.0/src/babelagent/core/errors.py +39 -0
  38. babelagent-0.1.0/src/babelagent/core/grade.py +85 -0
  39. babelagent-0.1.0/src/babelagent/core/graph.py +164 -0
  40. babelagent-0.1.0/src/babelagent/core/message.py +47 -0
  41. babelagent-0.1.0/src/babelagent/core/node.py +50 -0
  42. babelagent-0.1.0/src/babelagent/core/scheduler.py +252 -0
  43. babelagent-0.1.0/src/babelagent/core/topology.py +88 -0
  44. babelagent-0.1.0/src/babelagent/io/__init__.py +3 -0
  45. babelagent-0.1.0/src/babelagent/io/_demo_assets.py +45 -0
  46. babelagent-0.1.0/src/babelagent/io/cli.py +104 -0
  47. babelagent-0.1.0/src/babelagent/io/doctor.py +52 -0
  48. babelagent-0.1.0/src/babelagent/io/mcp.py +68 -0
  49. babelagent-0.1.0/src/babelagent/io/rest.py +233 -0
  50. babelagent-0.1.0/src/babelagent/py.typed +0 -0
  51. babelagent-0.1.0/tests/security/test_hardening.py +348 -0
  52. babelagent-0.1.0/tests/unit/test_a2a.py +80 -0
  53. babelagent-0.1.0/tests/unit/test_adapt.py +76 -0
  54. babelagent-0.1.0/tests/unit/test_demo.py +26 -0
  55. babelagent-0.1.0/tests/unit/test_graph.py +105 -0
  56. babelagent-0.1.0/tests/unit/test_mcp_server.py +22 -0
  57. babelagent-0.1.0/tests/unit/test_rest.py +62 -0
  58. babelagent-0.1.0/tests/unit/test_topology.py +71 -0
  59. babelagent-0.1.0/tests/unit/test_version.py +27 -0
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: ci-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ gate:
17
+ runs-on: ubuntu-latest
18
+ strategy:
19
+ matrix:
20
+ python-version: ["3.11", "3.12"]
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ with:
24
+ persist-credentials: false
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+ - name: Install (with serve + mcp + dev extras)
29
+ run: |
30
+ python -m pip install --upgrade pip
31
+ pip install -e ".[serve,mcp,dev]"
32
+ - name: Lint
33
+ run: ruff check src tests
34
+ - name: Type check
35
+ run: mypy src
36
+ - name: Test
37
+ run: pytest -q
38
+ - name: Smoke (key-free demo)
39
+ run: babelagent demo
@@ -0,0 +1,47 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "docs/**"
8
+ - "src/**"
9
+ - "mkdocs.yml"
10
+ - "CHANGELOG.md"
11
+ - ".github/workflows/docs.yml"
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: read
16
+ pages: write
17
+ id-token: write
18
+
19
+ # Allow one concurrent deploy; don't cancel an in-progress production deploy.
20
+ concurrency:
21
+ group: pages
22
+ cancel-in-progress: false
23
+
24
+ jobs:
25
+ build:
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: actions/setup-python@v5
30
+ with:
31
+ python-version: "3.12"
32
+ - name: Install (package for autodoc + mkdocs)
33
+ run: pip install -e . mkdocs-material "mkdocstrings[python]"
34
+ - run: mkdocs build --strict
35
+ - uses: actions/upload-pages-artifact@v3
36
+ with:
37
+ path: site
38
+
39
+ deploy:
40
+ needs: build
41
+ runs-on: ubuntu-latest
42
+ environment:
43
+ name: github-pages
44
+ url: ${{ steps.deployment.outputs.page_url }}
45
+ steps:
46
+ - id: deployment
47
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,27 @@
1
+ name: Publish
2
+
3
+ # Releases to PyPI via OIDC Trusted Publishing (no token secrets in the repo).
4
+ # Configured pending publisher: repo amitpatole/babelagent, workflow publish.yml.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ pypi:
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ id-token: write # mint the OIDC token PyPI verifies against the publisher
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with:
20
+ persist-credentials: false
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+ - run: pip install --upgrade pip build
25
+ - run: python -m build
26
+ - name: Publish to PyPI (Trusted Publishing)
27
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ *.egg
9
+
10
+ # Virtual envs
11
+ .venv/
12
+ venv/
13
+ env/
14
+
15
+ # Tooling caches
16
+ .pytest_cache/
17
+ .mypy_cache/
18
+ .ruff_cache/
19
+ .coverage
20
+ htmlcov/
21
+
22
+ # Docs build
23
+ site/
24
+
25
+ # OS
26
+ .DS_Store
27
+
28
+ # Security cadence — NEVER commit residuals / red-team notes / PoCs (local only)
29
+ .security-local/
30
+ docs/SECURITY_RESIDUALS.md
31
+
32
+ # Local env / secrets (creds live in ~/.config, never here)
33
+ .env
34
+ *.local.toml
@@ -0,0 +1,61 @@
1
+ # Changelog
2
+
3
+ All notable changes to Babelagent are documented here. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/); this project uses semantic versioning.
5
+
6
+ ## [Unreleased]
7
+
8
+ ### Security
9
+ - Completed the full security cadence over the REST / MCP / A2A / HTTP surface: a 3-surface audit and
10
+ **four adversarial red-team rounds** (final round found nothing new). Fixed and regression-pinned:
11
+ attacker-controlled `deadline_s` (DoS), SSRF (allowlist + NAT64/6to4/Teredo normalization, no
12
+ redirects, response caps, connect-time re-guard), keyword/positional argument injection from
13
+ untrusted upstream output, `_safe` depth+width DoS, trace exception-message leakage, REST DNS-rebind
14
+ (Host pinning), slowloris (body + connection limits), and a hard wall-clock guillotine defeating
15
+ cancellation-swallowing agents. See `docs/SECURITY.md` for the posture, deployment guidance, and the
16
+ documented residuals. 22 security regression tests in `tests/security/`.
17
+
18
+ ### Renamed
19
+ - Project renamed from the working title **BYOA** to **Babelagent** (import + distribution name
20
+ `babelagent`). The framing is now a neutral communication layer that lets heterogeneous agents talk
21
+ to each other (agent-to-agent), not a factory/assembly line.
22
+ - API vocabulary re-themed: `Factory` → `Graph`, `Line` → `CompiledGraph`, `Blueprint` → `Topology`,
23
+ `Station` → `Node` (merged with the old graph-node wrapper), `Part` → `Message`, `Product` →
24
+ `Result`, check outcome `Result` → `Grade`; `.station()` → `.node()`.
25
+
26
+ ### Added — core graph engine
27
+ - `Graph` builder with **linear chaining** (`.node(...).node(...)`) that compiles to a **DAG core**
28
+ supporting `after=` dependencies, fan-out, parallel branches, and fan-in `join`.
29
+ - Barrier policies for fan-in: `all`, `k_of_n` (with `k`), and `optional`.
30
+ - Async scheduler with bounded concurrency, per-node + per-run timeouts, reactive readiness, and
31
+ failure propagation (unsatisfiable barriers skip downstream nodes).
32
+ - Native, dependency-free quality types: `Verdict` (pass/warn/fail), `Grade`, `Check`, and `GateMode`
33
+ (off/warn/strict) for optional per-node gating.
34
+ - `Message` / `Result` envelopes; `Topology` validation (cycle + dangling-dep detection).
35
+
36
+ ### Added — adapters + `adapt()`
37
+ - **`adapt()`** on-the-fly adapter creator: normalizes callables, HTTP/OpenAPI endpoints, MCP tools,
38
+ framework agents (LangChain / CrewAI / AutoGen), and LLM providers into the uniform `Agent` interface.
39
+ - `CallableAgent`, `HttpAgent` (+ `from_openapi`, basic SSRF guard), `LLM` provider adapter (lazy,
40
+ behind extras, defaults to the latest Claude model).
41
+ - Extensible registry: `register_adapter(...)` and the `babelagent.adapters` entry-point group.
42
+
43
+ ### Added — A2A adapter
44
+ - **`A2AAgent` / `A2ARef`**: consume a remote Agent2Agent (A2A) agent as a node via the
45
+ `message/send` JSON-RPC method, with Agent Card discovery. Built on httpx (base wheel, no extra),
46
+ SSRF-guarded. `adapt(A2ARef(url))` wires a remote agent into the graph. This is the "consume the
47
+ protocols, don't compete with them" position: a remote A2A agent becomes just another `Agent`.
48
+
49
+ ### Added — interfaces
50
+ - `babelagent` CLI (Typer): `demo`, `doctor`, `inspect`, `serve`, `mcp`, `version`.
51
+ - **REST service** (`babelagent.io.rest`, `serve` extra): serve a graph over HTTP. Hardened from
52
+ birth — constant-time bearer auth, zero-config on loopback but fail-closed (refuses to bind a
53
+ non-loopback host without a token), request-body size cap enforced before buffering, a concurrency
54
+ semaphore, deadline-bounded runs, and sanitized errors. Endpoints: `GET /health`, `GET /graph`,
55
+ `POST /run`.
56
+ - **MCP server** (`babelagent.io.mcp`, `mcp` extra): expose a graph as MCP tools (`run_graph`,
57
+ `graph_topology`) so any MCP client can run a whole graph. Works across MCP SDK 2.x (`MCPServer`)
58
+ and 1.x (`FastMCP`).
59
+ - Key-free `babelagent demo`: a broken agent gated to FAIL, then a fixed one producing a PASS result.
60
+
61
+ [Unreleased]: https://example.invalid/babelagent/compare
@@ -0,0 +1,22 @@
1
+ cff-version: 1.2.0
2
+ title: Babelagent
3
+ message: "If you use Babelagent, please cite it as below."
4
+ type: software
5
+ authors:
6
+ - family-names: Patole
7
+ given-names: Amit
8
+ email: amit.patole@gmail.com
9
+ repository-code: "https://github.com/amitpatole/babelagent"
10
+ url: "https://amitpatole.github.io/babelagent/"
11
+ abstract: >-
12
+ The Babel that lets AI agents understand each other: a neutral, framework-agnostic
13
+ layer where heterogeneous agents (callables, HTTP/OpenAPI, MCP, framework agents,
14
+ LLMs) share one interface, collaborate agent-to-agent, and are graded at each hop.
15
+ keywords:
16
+ - ai
17
+ - agents
18
+ - a2a
19
+ - interoperability
20
+ - multi-agent
21
+ license: MIT
22
+ version: 0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amit Patole
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.5
2
+ Name: babelagent
3
+ Version: 0.1.0
4
+ Summary: The Babel that lets AI agents understand each other: a neutral layer where heterogeneous agents (callables, HTTP/OpenAPI, MCP, framework agents, LLMs) share one interface, collaborate (A2A), and get graded at each hop.
5
+ Author-email: Amit Patole <amit.patole@gmail.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: a2a,adapters,agents,ai,dag,interoperability,multi-agent,orchestration
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: httpx>=0.27
18
+ Requires-Dist: platformdirs>=4.0
19
+ Requires-Dist: pydantic-settings>=2.2
20
+ Requires-Dist: pydantic>=2.6
21
+ Requires-Dist: typer>=0.12
22
+ Provides-Extra: agentsensory
23
+ Requires-Dist: agentsensory>=0.1; extra == 'agentsensory'
24
+ Provides-Extra: all
25
+ Requires-Dist: anthropic>=0.40; extra == 'all'
26
+ Requires-Dist: fastapi>=0.110; extra == 'all'
27
+ Requires-Dist: langchain-core>=0.3; extra == 'all'
28
+ Requires-Dist: mcp>=1.2; extra == 'all'
29
+ Requires-Dist: ollama>=0.3; extra == 'all'
30
+ Requires-Dist: openai>=1.0; extra == 'all'
31
+ Requires-Dist: python-multipart>=0.0.9; extra == 'all'
32
+ Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
33
+ Provides-Extra: cloud
34
+ Requires-Dist: anthropic>=0.40; extra == 'cloud'
35
+ Requires-Dist: openai>=1.0; extra == 'cloud'
36
+ Provides-Extra: dev
37
+ Requires-Dist: mypy>=1.10; extra == 'dev'
38
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
39
+ Requires-Dist: pytest>=8.0; extra == 'dev'
40
+ Requires-Dist: ruff>=0.5; extra == 'dev'
41
+ Provides-Extra: frameworks
42
+ Requires-Dist: langchain-core>=0.3; extra == 'frameworks'
43
+ Provides-Extra: mcp
44
+ Requires-Dist: mcp>=1.2; extra == 'mcp'
45
+ Provides-Extra: ollama
46
+ Requires-Dist: ollama>=0.3; extra == 'ollama'
47
+ Provides-Extra: serve
48
+ Requires-Dist: fastapi>=0.110; extra == 'serve'
49
+ Requires-Dist: python-multipart>=0.0.9; extra == 'serve'
50
+ Requires-Dist: uvicorn[standard]>=0.29; extra == 'serve'
51
+ Description-Content-Type: text/markdown
52
+
53
+ # Babelagent
54
+
55
+ > The Babel that lets AI agents understand each other. One shared tongue for agents that were never meant to talk.
56
+
57
+ Every agent framework has its own idea of what an agent is. LangChain has Runnables with `.invoke`,
58
+ CrewAI has Crews with `.kickoff`, AutoGen has agents with `.generate_reply`, an LLM has
59
+ `messages.create`, a microservice has an HTTP route. None of them agree on a shape, so connecting any
60
+ two means writing glue.
61
+
62
+ Babelagent is the neutral layer in between. You bring your own agents, whatever they are (a callable,
63
+ an HTTP/OpenAPI endpoint, an MCP tool, a framework agent, or an LLM), and it wraps each one in a single
64
+ shared interface so they can exchange messages and collaborate on a task, **agent-to-agent (A2A)**.
65
+ The value is not any single adapter. It is that once something is adapted, it works with everything
66
+ else you have adapted.
67
+
68
+ The headline is **`adapt()`**: hand Babelagent almost anything and it builds the connector on the fly.
69
+
70
+ ```python
71
+ import asyncio
72
+ from babelagent import Graph
73
+
74
+ async def main():
75
+ graph = (
76
+ Graph()
77
+ .node("clean", str.strip)
78
+ .node("shout", str.upper)
79
+ .node("exclaim", lambda s: s + "!")
80
+ )
81
+ result = await graph.run(" hello ")
82
+ print(result.output) # "HELLO!"
83
+
84
+ asyncio.run(main())
85
+ ```
86
+
87
+ Let different agents work in parallel and hand their results to each other, with barrier policies on
88
+ the join:
89
+
90
+ ```python
91
+ g = Graph()
92
+ g.node("src", lambda text: text)
93
+ g.node("summary", adapt(langchain_agent), after=["src"]) # a LangChain agent
94
+ g.node("labels", adapt("https://api.example.com/classify"), after=["src"]) # an HTTP service
95
+ g.join("merge", after=["summary", "labels"], agent=combine, barrier="all")
96
+ result = await g.run(document)
97
+ ```
98
+
99
+ ## Concepts
100
+
101
+ | Concept | What it is |
102
+ |---|---|
103
+ | **Graph** | Builds the network of agents (linear chaining **and** a DAG API) |
104
+ | **CompiledGraph** | The validated, runnable graph; `await graph.run(payload)` |
105
+ | **Node** | One participant: an agent plus an optional quality check/gate |
106
+ | **Agent** | The uniform `async run(message, ctx) -> message` interface |
107
+ | **`adapt()`** | Turns any brought object into an Agent, inferring the adapter |
108
+ | **Message / Result** | The envelope agents exchange / the final output + run trace |
109
+ | **Barrier** | Fan-in join policy: `all` · `k_of_n` · `optional` |
110
+
111
+ ## Install
112
+
113
+ ```bash
114
+ pip install babelagent # light base (callables + HTTP)
115
+ pip install "babelagent[mcp]" # MCP tools
116
+ pip install "babelagent[cloud]" # Anthropic / OpenAI
117
+ pip install "babelagent[ollama]" # local models
118
+ pip install "babelagent[frameworks]" # LangChain / CrewAI / AutoGen
119
+ pip install "babelagent[serve]" # REST service
120
+ pip install "babelagent[all]"
121
+ ```
122
+
123
+ ## Try it (no API key)
124
+
125
+ ```bash
126
+ babelagent demo # a broken agent (gated FAIL) then a fixed one (PASS)
127
+ babelagent doctor # which adapter families are available
128
+ babelagent inspect # print a graph's topology as JSON
129
+ ```
130
+
131
+ ## Status
132
+
133
+ Alpha, in active development. Local-only for now. MIT licensed.
134
+
135
+ — amitpatole
@@ -0,0 +1,83 @@
1
+ # Babelagent
2
+
3
+ > The Babel that lets AI agents understand each other. One shared tongue for agents that were never meant to talk.
4
+
5
+ Every agent framework has its own idea of what an agent is. LangChain has Runnables with `.invoke`,
6
+ CrewAI has Crews with `.kickoff`, AutoGen has agents with `.generate_reply`, an LLM has
7
+ `messages.create`, a microservice has an HTTP route. None of them agree on a shape, so connecting any
8
+ two means writing glue.
9
+
10
+ Babelagent is the neutral layer in between. You bring your own agents, whatever they are (a callable,
11
+ an HTTP/OpenAPI endpoint, an MCP tool, a framework agent, or an LLM), and it wraps each one in a single
12
+ shared interface so they can exchange messages and collaborate on a task, **agent-to-agent (A2A)**.
13
+ The value is not any single adapter. It is that once something is adapted, it works with everything
14
+ else you have adapted.
15
+
16
+ The headline is **`adapt()`**: hand Babelagent almost anything and it builds the connector on the fly.
17
+
18
+ ```python
19
+ import asyncio
20
+ from babelagent import Graph
21
+
22
+ async def main():
23
+ graph = (
24
+ Graph()
25
+ .node("clean", str.strip)
26
+ .node("shout", str.upper)
27
+ .node("exclaim", lambda s: s + "!")
28
+ )
29
+ result = await graph.run(" hello ")
30
+ print(result.output) # "HELLO!"
31
+
32
+ asyncio.run(main())
33
+ ```
34
+
35
+ Let different agents work in parallel and hand their results to each other, with barrier policies on
36
+ the join:
37
+
38
+ ```python
39
+ g = Graph()
40
+ g.node("src", lambda text: text)
41
+ g.node("summary", adapt(langchain_agent), after=["src"]) # a LangChain agent
42
+ g.node("labels", adapt("https://api.example.com/classify"), after=["src"]) # an HTTP service
43
+ g.join("merge", after=["summary", "labels"], agent=combine, barrier="all")
44
+ result = await g.run(document)
45
+ ```
46
+
47
+ ## Concepts
48
+
49
+ | Concept | What it is |
50
+ |---|---|
51
+ | **Graph** | Builds the network of agents (linear chaining **and** a DAG API) |
52
+ | **CompiledGraph** | The validated, runnable graph; `await graph.run(payload)` |
53
+ | **Node** | One participant: an agent plus an optional quality check/gate |
54
+ | **Agent** | The uniform `async run(message, ctx) -> message` interface |
55
+ | **`adapt()`** | Turns any brought object into an Agent, inferring the adapter |
56
+ | **Message / Result** | The envelope agents exchange / the final output + run trace |
57
+ | **Barrier** | Fan-in join policy: `all` · `k_of_n` · `optional` |
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pip install babelagent # light base (callables + HTTP)
63
+ pip install "babelagent[mcp]" # MCP tools
64
+ pip install "babelagent[cloud]" # Anthropic / OpenAI
65
+ pip install "babelagent[ollama]" # local models
66
+ pip install "babelagent[frameworks]" # LangChain / CrewAI / AutoGen
67
+ pip install "babelagent[serve]" # REST service
68
+ pip install "babelagent[all]"
69
+ ```
70
+
71
+ ## Try it (no API key)
72
+
73
+ ```bash
74
+ babelagent demo # a broken agent (gated FAIL) then a fixed one (PASS)
75
+ babelagent doctor # which adapter families are available
76
+ babelagent inspect # print a graph's topology as JSON
77
+ ```
78
+
79
+ ## Status
80
+
81
+ Alpha, in active development. Local-only for now. MIT licensed.
82
+
83
+ — amitpatole
@@ -0,0 +1,135 @@
1
+ /* ============================================================================
2
+ Trio Design System — "Warm Paper" · MkDocs Material override
3
+ Light-first, printed-document calm. No gradients, no glows. One accent.
4
+ CANONICAL FILE — copied into each repo's docs/stylesheets/extra.css.
5
+ Per-repo accent: Babelagent (bridge) desaturated teal.
6
+ ============================================================================ */
7
+ @import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,400;8..60,500;8..60,600;8..60,700&display=swap');
8
+
9
+ :root {
10
+ /* ---- Warm Paper tokens ---- */
11
+ --wp-bg: #f7f5f1;
12
+ --wp-surface: #fffdf9;
13
+ --wp-panel: #efece4;
14
+ --wp-ink: #1b1a17;
15
+ --wp-ink-muted: #6b6862;
16
+ --wp-ink-faint: #918d84;
17
+ --wp-rule: #e3ded4;
18
+
19
+ /* ---- Accent — Babelagent (bridge) ---- */
20
+ --wp-accent: #3f8a86;
21
+ --wp-accent-ink: #2a615d;
22
+
23
+ --wp-serif: "Source Serif 4", Georgia, "Times New Roman", serif;
24
+ }
25
+
26
+ /* ---- Map tokens onto Material's light scheme ---- */
27
+ [data-md-color-scheme="default"] {
28
+ --md-default-bg-color: var(--wp-bg);
29
+ --md-default-fg-color: var(--wp-ink);
30
+ --md-default-fg-color--light: var(--wp-ink-muted);
31
+ --md-default-fg-color--lighter: var(--wp-ink-faint);
32
+ --md-default-fg-color--lightest: var(--wp-rule);
33
+
34
+ --md-primary-fg-color: var(--wp-bg); /* header = paper, not a color bar */
35
+ --md-primary-fg-color--light: var(--wp-bg);
36
+ --md-primary-fg-color--dark: var(--wp-bg);
37
+ --md-primary-bg-color: var(--wp-ink); /* header text = ink */
38
+ --md-primary-bg-color--light: var(--wp-ink-muted);
39
+
40
+ --md-accent-fg-color: var(--wp-accent-ink);
41
+ --md-accent-fg-color--transparent: color-mix(in srgb, var(--wp-accent) 12%, transparent);
42
+
43
+ --md-typeset-color: var(--wp-ink);
44
+ --md-typeset-a-color: var(--wp-accent-ink);
45
+
46
+ --md-code-bg-color: var(--wp-panel);
47
+ --md-code-fg-color: var(--wp-ink);
48
+ --md-footer-bg-color: var(--wp-surface);
49
+ --md-footer-bg-color--dark: var(--wp-surface);
50
+ --md-footer-fg-color: var(--wp-ink-muted);
51
+ --md-footer-fg-color--light: var(--wp-ink-faint);
52
+
53
+ color-scheme: light;
54
+ }
55
+
56
+ /* ---- Header: paper with a hairline, no heavy bar ---- */
57
+ .md-header {
58
+ color: var(--wp-ink);
59
+ border-bottom: 1px solid var(--wp-rule);
60
+ box-shadow: none;
61
+ }
62
+ .md-header--shadow { box-shadow: none; }
63
+ .md-tabs {
64
+ background: var(--wp-bg);
65
+ color: var(--wp-ink);
66
+ border-bottom: 1px solid var(--wp-rule);
67
+ }
68
+
69
+ /* ---- Serif display for headings; tight, never heavy ---- */
70
+ .md-typeset h1, .md-typeset h2, .md-typeset h3,
71
+ .md-content__inner > h1, .md-nav__title {
72
+ font-family: var(--wp-serif);
73
+ letter-spacing: -.01em;
74
+ font-weight: 600;
75
+ color: var(--wp-ink);
76
+ }
77
+ .md-typeset h1 { font-weight: 600; }
78
+
79
+ /* The page H1 gets a quiet accent hairline underneath — the single accent gesture */
80
+ .md-typeset h1 {
81
+ padding-bottom: .35rem;
82
+ border-bottom: 1px solid var(--wp-rule);
83
+ position: relative;
84
+ }
85
+ .md-typeset h1::after {
86
+ content: ""; position: absolute; left: 0; bottom: -1px;
87
+ width: 2.2rem; height: 1px; background: var(--wp-accent);
88
+ }
89
+
90
+ /* ---- Links: accent-ink, underline on hover only ---- */
91
+ .md-typeset a { color: var(--wp-accent-ink); text-decoration: none; }
92
+ .md-typeset a:hover { text-decoration: underline; text-underline-offset: 3px; }
93
+
94
+ /* ---- Code: quiet panel, hairline, no neon ---- */
95
+ .md-typeset pre > code,
96
+ .md-typeset .highlight { border-radius: 8px; }
97
+ .md-typeset pre { border: 1px solid var(--wp-rule); border-radius: 8px; }
98
+ .md-typeset code { background: var(--wp-panel); }
99
+
100
+ /* ---- Tables & admonitions: hairlines, soft fills ---- */
101
+ .md-typeset table:not([class]) {
102
+ border: 1px solid var(--wp-rule); border-radius: 8px; overflow: hidden;
103
+ }
104
+ .md-typeset table:not([class]) th {
105
+ background: var(--wp-panel); color: var(--wp-ink); font-weight: 600;
106
+ }
107
+ .md-typeset .admonition, .md-typeset details {
108
+ border: 1px solid var(--wp-rule); border-left-width: 2px;
109
+ border-left-color: var(--wp-accent); box-shadow: none; background: var(--wp-surface);
110
+ }
111
+
112
+ /* ---- Buttons / primary CTA ---- */
113
+ .md-typeset .md-button--primary {
114
+ background: var(--wp-accent-ink); border-color: var(--wp-accent-ink); color: var(--wp-surface);
115
+ }
116
+ .md-typeset .md-button--primary:hover {
117
+ background: var(--wp-ink); border-color: var(--wp-ink);
118
+ }
119
+
120
+ /* ---- Search ---- */
121
+ .md-search__form { background: var(--wp-surface); border: 1px solid var(--wp-rule); }
122
+ .md-search__input::placeholder { color: var(--wp-ink-faint); }
123
+
124
+ /* ---- Maker's mark: quiet "— amitpatole" signature in the footer ---- */
125
+ .md-footer-meta { border-top: 1px solid var(--wp-rule); }
126
+ .md-footer-meta__inner::after {
127
+ content: "— amitpatole";
128
+ font-family: var(--wp-serif); font-style: italic; font-size: .8rem;
129
+ color: var(--wp-accent-ink); margin-left: auto; padding: 0 .6rem;
130
+ align-self: center; white-space: nowrap;
131
+ }
132
+
133
+ /* ---- Tighten the overall feel ---- */
134
+ .md-grid { max-width: 64rem; } /* comfortable measure */
135
+ .md-typeset { line-height: 1.62; }
@@ -0,0 +1,62 @@
1
+ # Security posture
2
+
3
+ Babelagent runs code and calls services you bring, and its REST/MCP interfaces accept untrusted input,
4
+ so security is treated as part of "done." The current surface has been through a full audit and four
5
+ adversarial red-team rounds (the last found nothing new). This page states what is hardened, how to
6
+ deploy safely, and the residual risks no audit removes.
7
+
8
+ ## What is hardened
9
+
10
+ **Network input (REST service, `babelagent[serve]`).**
11
+ - Bearer-token auth compared in constant time (`hmac.compare_digest`). Zero-config on loopback;
12
+ **fail-closed** on any non-loopback bind without a token (`serve()` refuses to start).
13
+ - Anti-DNS-rebind **Host-header pinning** on loopback binds (a browser cannot rebind `attacker.com`
14
+ to `127.0.0.1` and drive `/run`). Fails closed on a missing Host.
15
+ - Request-body size cap enforced **before** buffering; a body-phase idle/slowloris timeout; a
16
+ concurrency semaphore; `limit_concurrency` + `timeout_keep_alive` on uvicorn.
17
+ - Caller-supplied `deadline_s` is validated (finite, positive) and **clamped** to the server ceiling
18
+ (it can only lower the run budget, never raise it), backed by a hard wall-clock guillotine on the run.
19
+ - Errors are sanitized: internal exception messages are redacted from the returned trace.
20
+
21
+ **Outbound requests (HTTP / OpenAPI / A2A adapters).**
22
+ - SSRF allowlist: only public global-unicast targets. Blocks loopback/private/link-local/reserved/
23
+ multicast/unspecified, and normalizes IPv4-mapped, IPv4-compatible, NAT64, 6to4, and Teredo IPv6
24
+ so an internal IPv4 cannot be smuggled through an IPv6 literal.
25
+ - Redirects are never followed; the guard is re-run at connect time; upstream responses are streamed
26
+ with a size cap so a hostile agent cannot exhaust memory.
27
+ - Secrets: API keys and tokens come only from the environment; never hard-coded, never serialized into
28
+ a topology or returned in a response.
29
+
30
+ **Execution / resources.**
31
+ - No `eval`/`exec`/`pickle`/`shell=True` on untrusted input. The OpenAPI path only reads a schema.
32
+ - The scheduler bounds concurrency; response serialization is bounded by depth **and** a total-node
33
+ budget (so a wide or shared-reference structure cannot fan out to exponential work).
34
+ - Untrusted upstream output cannot inject keyword/positional "flag" arguments into a callable node
35
+ (payload spreads into a callable only when it exactly matches the callable's required parameters).
36
+ - Entry-point adapter discovery can be disabled with `BABELAGENT_NO_PLUGINS=1`.
37
+
38
+ ## Deploying safely
39
+
40
+ - **Front a routable deployment with a reverse proxy** (nginx `client_header_timeout`, an ALB idle
41
+ timeout, etc.). uvicorn has no header-receive timeout, so a slow-headers Slowloris is only fully
42
+ mitigated by a fronting proxy. Always set a token (`BABELAGENT_API_TOKEN`) for non-loopback binds.
43
+ - **Front untrusted outbound targets with an egress allowlist / proxy.** The SSRF guard blocks internal
44
+ targets, but a host under attacker DNS control can still rebind between validation and connect; an
45
+ egress allowlist closes that residual.
46
+ - **Do not run untrusted, blocking, or CPU-bound *sync* callables on a shared server.** A blocked
47
+ worker thread cannot be force-killed (a Python limitation); use an async agent or an out-of-process
48
+ agent for such work.
49
+
50
+ ## Documented residuals (not eliminated by any audit)
51
+
52
+ - A blocked/CPU-bound **sync** callable's worker thread cannot be interrupted; the run still reports the
53
+ timeout, but the thread lingers.
54
+ - **DNS-rebinding** of an outbound target is not fully closed without an egress allowlist/proxy.
55
+ - **Header-phase Slowloris** requires a fronting reverse proxy.
56
+ - **Brought agents and entry-point plugins run code** by design; trust your dependency set and the
57
+ agents you plug in. LLM provider SDKs dial trusted first-party endpoints.
58
+ - The usual unknowns: third-party dependencies and the kernel/OS.
59
+
60
+ ## Reporting
61
+
62
+ This project is in local development. Once public, report vulnerabilities privately to the maintainer.