Skip to main content

concordat

Typed sessions for AI agents. Agents are objects whose interfaces are protocols, and the runtime enforces those protocols on the model.

Status: pre-alpha. Agent classes, the protocol/session/budget kernel, and runners for Claude (Anthropic) and OpenAI work. See docs/design.md.

Why

"Never refund before verifying identity" usually lives in a prompt and is hoped for. Concordat makes it a checked protocol:

  • Protocol fidelity: the session follows the declared order of turns.
  • Least privilege per step: the model only gets the tools legal in the current state.
  • Bounded spend: tokens, tool calls and dollars stay within budget, even across handoffs.

Example

from concordat import Agent, Budget, human, tool

class RefundAgent(Agent):
    final = {"refunded", "denied"}
    budget = Budget(tool_calls=10)

    @tool(when="start", then={True: "verified", False: "denied"})
    def verify_identity(self, user_id: str) -> bool:
        return lookup(user_id).verified

    @tool(when="verified", then="awaiting_approval")
    def propose_refund(self, amount: float) -> float:
        return amount

    @human(when="awaiting_approval", approve="refunded", reject="denied")
    def approve_refund(self, amount: float) -> str:
        return f"Refund ${amount}?"         # shown to the human approver

agent = RefundAgent()          # protocol already checked when the class was defined
agent.allowed_tools()          # {'verify_identity': ...}: all the model gets to see
agent.propose_refund(5000)     # ProtocolViolation, raised before the body runs

Safe inheritance

Specialise an agent by subclassing it. Adding steps is fine; bypassing the parent's is rejected when the class is defined:

class FastRefundAgent(RefundAgent):
    @tool(when="start", then="refunded")
    def instant_refund(self, order_id: str) -> str: ...

# ProtocolError: FastRefundAgent breaks RefundAgent's protocol: after instant_refund,
# it reaches 'refunded', which RefundAgent only reaches through
# verify_identity → propose_refund → approve_refund

So a team can define a compliant base agent once, and every specialised version keeps its safety steps.

Running it with Claude

pip install "concordat[anthropic]"
import anthropic
from concordat.anthropic import run

result = run(RefundAgent(), anthropic.Anthropic(), "Please refund order A-100.")
result.status   # "done", or "waiting" if Claude asked the user something

Each turn Claude is offered only the actions legal in the current state (a human step appears as a request that only the approver can decide), one call per response. Every response's tokens are charged to the agent's budget, and the next response is capped to what is left. Illegal or invalid calls go back to Claude as errors so it can correct itself. See examples/refund.py.

Running it with OpenAI

The same loop over the Responses API (pip install "concordat[openai]"):

import openai
from concordat.openai import run

result = run(RefundAgent(), openai.OpenAI(), "Please refund order A-100.", model="...")

Without classes

The same protocol, using the kernel directly:

from concordat import Budget, Protocol, Session

refund = Protocol(
    "start",
    {
        "start": {"verify_identity": {"verified", "denied"}},
        "verified": {"propose_refund": {"awaiting_approval"}},
        "awaiting_approval": {"human.approve": {"refunded"}, "human.reject": {"denied"}},
    },
    final={"refunded", "denied"},
)  # checked here: no dead ends, no unreachable states

session = Session(refund, Budget(tokens=50_000, tool_calls=10))
session.allowed_actions()           # {'verify_identity'}: only these tools go to the model
session.step("propose_refund")      # ProtocolViolation: not allowed in state 'start'

Handing work to another agent splits the budget, and the parent can't spend it twice:

child_budget = session.budget.split(tokens=10_000, tool_calls=3)

Development

uv sync
uv run pytest
uv run mypy src
uv run ruff check

License

Apache-2.0

Metadata

Release files for concordat 0.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for concordat 0.0.2
File Size Uploaded Provenance Actions
concordat-0.0.2.tar.gz 88.9 kB Without provenance Details for concordat-0.0.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for concordat 0.0.2
File Interpreter ABI Platform Actions
concordat-0.0.2-py3-none-any.whl Python 3 none any Details for concordat-0.0.2-py3-none-any.whl

Total release size: 108.1 kB

Release files / concordat-0.0.2.tar.gz

Download URL concordat-0.0.2.tar.gz
Size 88.9 kB
Tags Source
SHA-256 checksum
How to use checksums
760453a81e132962591f2bee511aecd3aa2d287f614ac4e09b2f2fac0341b158
BLAKE2b-256 checksum
How to use checksums
2d43e2b2aa6a573ff16983b6239cf3c45163c87f35d1897b2654fbacafe9b1af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / concordat-0.0.2-py3-none-any.whl

Download URL concordat-0.0.2-py3-none-any.whl
Size 19.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
593e6c70ac7764fc64eb39cf8f0929b43235e63e3eb3600658f11b0cd2e07013
BLAKE2b-256 checksum
How to use checksums
822378c996c9d45a290632b499e510e09f709ad4ecd5273a0dfaa69acf3108f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for concordat 0.0.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page