countersign-ai
Typed, LangGraph-native approval boundaries for consequential actions.
The PyPI distribution is countersign-ai; Python code imports countersign.
pip install countersign-ai
from countersign import require_approval
@require_approval(action="issue_refund", description="Approve this customer refund")
def issue_refund(order_id: str, amount_usd: float) -> str:
return payments.refund(order_id, amount_usd)
Inside a LangGraph node or tool, the wrapper emits a Countersign v0 request via
interrupt() and invokes the wrapped function only after an Agent
Inbox-compatible accept or edit response. ignore and response raise
ApprovalRejected, preventing the action from running accidentally.
The resume payload is untrusted
A decision arrives from outside the graph, so the wrapper enforces the contract
the request published before the action can run. Each of these raises
ApprovalContractError and executes nothing:
- an
acceptwhen the request setallow_accept: false; - an
editwhen the request setallow_edit: false; - edited arguments that violate
context.edit_schema(ApprovalEditValidationError, carrying oneEditValidationIssueper failing field); - edited arguments the wrapped function cannot be called with.
from countersign import ApprovalContext, require_approval
@require_approval(
action="issue_refund",
context=ApprovalContext(
edit_schema={
"type": "object",
"required": ["order_id", "amount_usd"],
"additionalProperties": False,
"properties": {
"order_id": {"const": "ord_4821"},
"amount_usd": {"type": "number", "exclusiveMinimum": 0, "maximum": 10_000},
},
}
),
)
def issue_refund(order_id: str, amount_usd: float) -> str:
return payments.refund(order_id, amount_usd)
A request without context.edit_schema keeps the permissive Agent Inbox edit
behavior, so existing graphs are unaffected. validate_approval_edit() applies
the same rules outside the decorator, and matches validateApprovalEdit() in
@countersign-ai/react so a reviewer sees the failure before submitting.
An edit schema constrains what a reviewer may change. It says nothing about the arguments the graph proposed, so keep the action's own limits for the accept path.
Interoperability
ApprovalRequest.model_validate() accepts an existing Agent Inbox
HumanInterrupt unchanged. resume_command() maps a Countersign decision to
the Command(resume=[HumanResponse]) shape LangGraph expects:
from countersign import ApprovalDecision, ApprovalRequest, resume_command
request = ApprovalRequest.model_validate(interrupt_payload)
command = resume_command(
ApprovalDecision(type="edit", args={"amount_usd": 99}),
request,
)
graph.invoke(command, config={"configurable": {"thread_id": "refund-4821"}})
Do not create a random request ID or timestamp inside an interrupted node.
LangGraph reruns that node on resume; leave request_id and created_at
absent, or pass stable values derived from graph state.
Adapters
SelfHostedLangGraphAdapter resumes a compiled in-process graph. Install the
Platform transport extra for the REST/SSE adapter:
pip install 'countersign-ai[platform]'
from countersign.adapters import LangGraphPlatformAdapter
adapter = LangGraphPlatformAdapter(base_url=platform_url, api_key=api_key)
await adapter.resume(
thread_id=thread_id,
assistant_id=assistant_id,
request=request,
decision=decision,
)
Keep Platform credentials in the trusted server or worker using the adapter, never in browser code.
Develop
uv run --all-extras --group dev pytest -q
uv run --all-extras --group dev mypy src
uv build
The hosted queue, team authentication, billing, notification, and production
persistence implementations are intentionally maintained in the private
countersign-cloud repository.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file countersign_ai-0.1.0.tar.gz.
File metadata
- Download URL: countersign_ai-0.1.0.tar.gz
- Upload date:
- Size: 12.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95761fee64f8105399de8926523a75b7b0483a730d14418021af3c97f9838936
|
|
| MD5 |
c46d24cff19c13f1b180e5490b794a44
|
|
| BLAKE2b-256 |
5e09a0047ca8e2014ddc608434ff09aeaf36fcee93da8934c64b8e6aeebfef17
|
Provenance
The following attestation bundles were made for countersign_ai-0.1.0.tar.gz:
Publisher:
release-python.yml on starkyru/countersign
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
countersign_ai-0.1.0.tar.gz -
Subject digest:
95761fee64f8105399de8926523a75b7b0483a730d14418021af3c97f9838936 - Sigstore transparency entry: 2444361087
- Sigstore integration time:
-
Permalink:
starkyru/countersign@09520db1cfffe53f805c36f52a714a2952725837 -
Branch / Tag:
refs/tags/sdk-python-v0.1.0 - Owner: https://github.com/starkyru
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@09520db1cfffe53f805c36f52a714a2952725837 -
Trigger Event:
push
-
Statement type:
File details
Details for the file countersign_ai-0.1.0-py3-none-any.whl.
File metadata
- Download URL: countersign_ai-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ddcee8cc08dec482c1a37657e2216a631272cfb481ecd7af36e7e0e3e780c887
|
|
| MD5 |
8b47f3a3bd0c32e7041a695bff5a0831
|
|
| BLAKE2b-256 |
4b92353f8693827556ed332a0e50614f52d426ea5c98511ed377f9fa9ed5cde5
|
Provenance
The following attestation bundles were made for countersign_ai-0.1.0-py3-none-any.whl:
Publisher:
release-python.yml on starkyru/countersign
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
countersign_ai-0.1.0-py3-none-any.whl -
Subject digest:
ddcee8cc08dec482c1a37657e2216a631272cfb481ecd7af36e7e0e3e780c887 - Sigstore transparency entry: 2444361243
- Sigstore integration time:
-
Permalink:
starkyru/countersign@09520db1cfffe53f805c36f52a714a2952725837 -
Branch / Tag:
refs/tags/sdk-python-v0.1.0 - Owner: https://github.com/starkyru
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@09520db1cfffe53f805c36f52a714a2952725837 -
Trigger Event:
push
-
Statement type: