Metadata-Version: 2.4
Name: agience-chorus
Version: 0.2.0
Summary: Agience Chorus — the tekton standard library: operators by domain, and the host that serves them.
Author-email: "Ikailo Inc." <connect@agience.ai>
License-Expression: AGPL-3.0-only
Project-URL: Homepage, https://agience.ai
Project-URL: Source, https://github.com/Agience/agience-chorus
Project-URL: Issues, https://github.com/Agience/agience-chorus/issues
Keywords: agience,chorus,tekton,persona,operator,organon,mcp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: mcp[cli]<2,>=1.6.0
Requires-Dist: httpx>=0.27
Requires-Dist: fastapi>=0.115
Requires-Dist: starlette>=0.37
Requires-Dist: uvicorn>=0.30
Requires-Dist: cryptography>=42.0
Requires-Dist: python-jose[cryptography]>=3.3
Requires-Dist: pydantic>=2.0
Requires-Dist: agience-prism[trust,wire]>=0.1.4
Requires-Dist: agience-crystal[ontology,service]>=0.1.0
Requires-Dist: agience-mantle>=0.1.3
Provides-Extra: astra
Requires-Dist: pypdf>=5.0; extra == "astra"
Requires-Dist: numpy>=1.24; extra == "astra"
Requires-Dist: datasets>=2.14; extra == "astra"
Provides-Extra: lumen
Requires-Dist: numpy>=1.24; extra == "lumen"
Requires-Dist: sympy>=1.12; extra == "lumen"
Provides-Extra: sage
Requires-Dist: sympy>=1.12; extra == "sage"
Provides-Extra: ophan
Requires-Dist: stripe>=9.0; extra == "ophan"
Provides-Extra: aria
Provides-Extra: iris
Provides-Extra: seraph
Provides-Extra: all
Requires-Dist: agience-chorus[aria,astra,iris,lumen,ophan,sage,seraph]; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-timeout>=2.2; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Dynamic: license-file

# Agience Chorus

[![License](https://img.shields.io/badge/license-AGPL--3.0--only-blue)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.11%2B-3776AB?logo=python&logoColor=white)](pyproject.toml)
[![Protocol](https://img.shields.io/badge/protocol-MCP-6E56CF)](src/agience_chorus/personas.py)
[![Sponsor](https://img.shields.io/badge/Sponsor-Agience-EA4AAA?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/Agience)

**Operators, by domain.** Chorus is the tekton standard library: seven MCP services, each holding
the operators of one domain, served from one host or deployed one at a time.

A **tekton** is a chorus member — a condensor whose tools are organons, invoked by condensation.
Each declares a module-level `PERSONA` in its own `server.py`, and
[`src/agience_chorus/personas.py`](src/agience_chorus/personas.py) discovers them and hands the roster to the host:

| tekton | domain | mount |
|---|---|---|
| **aria** | Presentation & Interface | `/aria/mcp` |
| **astra** | Ingestion & Indexing | `/astra/mcp` |
| **sage** | Research & Retrieval | `/sage/mcp` |
| **iris** | Routing & Communication | `/iris/mcp` |
| **ophan** | Economic Operations | `/ophan/mcp` |
| **seraph** | Security & Governance | `/seraph/mcp` |
| **lumen** | Wisdom & Inference | `/lumen/mcp` |

Tektons are reached over the wire — HTTP and MCP — and are independently deployable, so an operator
runs only the ones they need. Each constructs its own server auth and signs with its own identity,
and no tekton imports another.

## Running it

```bash
pip install agience-chorus                  # every tekton
pip install "agience-chorus[iris,seraph]"   # only what those two need
python -m agience_chorus.server             # every tekton on one process, :8082
```

`MCP_HOST` (default `0.0.0.0`), `MCP_PORT` (default `8082`) and `LOG_LEVEL` (default `INFO`)
configure the host. `uvicorn agience_chorus.server:app` resolves too — the app is built at import
time. `CHORUS_CRYSTALS=aria,seraph` boots a node holding a subset.

The import name is `agience_chorus`, and the tektons are subpackages of it —
`agience_chorus.aria`, `.astra`, `.iris`, `.lumen`, `.ophan`, `.sage`, `.seraph`.

**The extras select dependencies, not modules.** `agience-chorus[ophan]` installs all seven tektons
and adds Stripe; Python has no mechanism for an extra to exclude code. What it saves is real, all
the same: a node running only `iris` and `seraph` otherwise installs Stripe, HuggingFace `datasets`,
pypdf, sympy and numpy to run none of them. Whether an operator may *discharge* is still the
capability gate's answer, not the installer's.

For a checkout, `pip install -e ".[all,dev]"` — which is what [`requirements.txt`](requirements.txt)
resolves to.

### Running the tests

```bash
pytest -q src tests --rootdir=src
```

**Both paths, and `--rootdir=src`, are load-bearing.** Each tekton carries its own `pyproject.toml`,
so without the flag pytest picks one of them as the rootdir and [`src/conftest.py`](src/conftest.py)
— which wires the organons — never loads; the symptom is a phantom `_op_retrieve is None` rather
than an error naming the cause.

`pytest tests/` on its own does **not** work, and the way it fails is misleading: it stops at a
collection error on `import seraph.server`, which reads like a broken test. The import is correct
(`seraph` is its own distribution, `agience-server-seraph`); what is missing is the environment the
invocation above sets up. Verified 2026-09-16 — under the full command that file collects and its
twenty tests pass. Reach for the whole command before concluding anything from a subset that fails.

## The bundles

[`src/agience_chorus/bundles/`](src/agience_chorus/bundles/) holds the operator payloads, one JSON
per group, built from the sources named in
[`src/agience_chorus/seraph/bundle_spec.json`](src/agience_chorus/seraph/bundle_spec.json). Each
carries a `sha256` that the mesh publishes.

They live inside the package so the wheel carries them: the runtime executes the payload, so an
installed chorus without them could not discharge an operator at all.

**The runtime reads the payload, not the source file.** An edit to a tekton that is not rebuilt has
no effect, and the published sha then disagrees with the tree.
[`src/tests/test_bundles_match_source.py`](src/tests/test_bundles_match_source.py) fails when they drift.

## Layout

| path | what it is |
|---|---|
| `src/agience_chorus/<tekton>/server.py` | one deployable tekton service, with its own `manifest.py`, `tests/` and `ui/` |
| `src/agience_chorus/<tekton>/ui/<top>/<sub>/type.json` | the content types that tekton owns; it registers them with the gateway at host startup |
| [`src/agience_chorus/server.py`](src/agience_chorus/server.py) | the unified host, mounting every tekton on one process |
| `src/agience_chorus/`​`_chorus_identity.py` · `_persona.py` · `_host_seams.py` | the identity, artifact and host-seam helpers tektons load at runtime |
| `src/agience_chorus/`​`reach_host.py` · `reach_wiring.py` · `reading_junction.py` | the reach surfaces shared across tektons |
| [`src/agience_chorus/personas.py`](src/agience_chorus/personas.py) | tekton discovery and host binding: module loading, service-identity boot, and the roster the host consumes |
| `src/agience_chorus/`​`corpus_fts.py` · `corpus_stats.py` · `live_service.py` | the corpus and live-service surfaces shared across tektons |
| [`src/conftest.py`](src/conftest.py) · [`src/tests/`](src/tests/) | the test process's environment, and the cross-tekton suite — outside the package, so neither ships |
| [`tests/`](tests/) | the repo-level invariants, one file per property: that every persona transport verifies its caller, that no persona shells out, that the runtime data a node reads is declared package-data, and that the Facet source tree stays out of the distribution. Each names the incident it exists for |
| [`pyproject.toml`](pyproject.toml) | the distribution: dependencies, per-tekton extras, and what goes in the wheel |

## Model-free

A tekton's answer path is grounded operators over the artifact graph, so nothing imports a
judgement made elsewhere that a caller cannot re-derive. Reaching a model stays legitimate as a
deliberate act through a tekton that offers it, where the call is explicit and the provenance
records who chose it. `resolve_llm_credentials` in
[`src/agience_chorus/seraph/server.py`](src/agience_chorus/seraph/server.py) raises and names the
rule in the `@mcp.tool` description a caller reads.

Security issues: email **connect@agience.ai** rather than opening a public issue.

Dual-licensed — see [`LICENSE`](LICENSE), [`COMMERCIAL_LICENSE.md`](COMMERCIAL_LICENSE.md),
[`NOTICE`](NOTICE) and [`CLA.md`](CLA.md).

## Declaration of generative AI use

The author used Anthropic's Claude Opus (versions 4.8, 5, and 5.5) in the preparation of this work. Its
contribution was to write code, and to generate and validate content. The ideas, the construction
and the claims are the author's. No other generative AI tool was used. The author reviewed and
edited all output and takes full responsibility for the content of this publication.
