Skip to main content

Action-level release control for AI agents: preview, approve, canary, halt, and audit tool actions before they touch production.

Project description

action-marshall

Action-level release control for AI agents. Preview, approve, canary, halt, and audit tool actions before they touch production.

action-marshall is the Python SDK for Action Marshall. Wrap the tools or functions your agent already calls — Action Marshall runs preview, policy, approval, canary, breaker, and signed audit before the action is released.

Status: 0.1.0 — pre-1.0 alpha. The API may change before 1.0.0.


Install

pip install action-marshall

Python 3.9+.

You also need a backend

action-marshall is the client SDK. It talks to an Action Marshall backend that runs the policy engine, signs receipts, and stores audit history. To try it end-to-end you have two paths:

Option A — local backend via Docker (recommended for evaluation)

git clone https://github.com/SatwikReddySripathi/action-marshall.git
cd action-marshall
docker compose up --build

Backend lives at http://localhost:8000, dashboard at http://localhost:3000. Wait until curl http://localhost:8000/ready returns 200. Default API key is am_test_demo_key_001.

Option B — point at a hosted backend (planned — hosted Action Marshall is not live yet)

Once hosted Action Marshall opens, you'll get an API key and a base_url. Join the waitlist.

Optional framework adapters:

pip install "action-marshall[langchain]"     # available now (experimental)
pip install "action-marshall[langgraph]"     # planned
pip install "action-marshall[crewai]"        # planned
pip install "action-marshall[autogen]"       # planned
pip install "action-marshall[mcp]"           # planned
pip install "action-marshall[llamaindex]"    # planned
pip install "action-marshall[openai]"        # planned
pip install "action-marshall[all]"

Quickstart

Assuming the backend is up at http://localhost:8000:

from action_marshall import MarshallClient, Action, ActionParams

ks = MarshallClient(
    api_key="am_test_demo_key_001",
    base_url="http://localhost:8000",
)

result = ks.run(Action(
    tool="servicenow",
    action_type="bulk_update",
    params=ActionParams(
        connector="servicenow_sim",
        query={"state": "open", "priority": {"op": "in", "value": ["P3", "P4"]}},
        changes={"state": "in_progress"},
    ),
))

print(result.decision_value)   # AUTO | CANARY | BLOCK | APPROVAL_REQUIRED
print(result.status)           # completed | contained | blocked | awaiting_approval | observed
print(result.blast_radius)     # 17
print(result.proof_url)        # /v1/actions/<id>/proof

Open result.ui_urls["detail"] in your browser to see the full action in the dashboard.


Wrap an existing function

The killer pattern — every call becomes a governed action:

@ks.wrap_function(
    tool="servicenow",
    action_type="update_incident",
    connector="servicenow_sim",
    agent_id="incident-triage",
)
def update_incident(payload: dict) -> dict:
    # your existing implementation
    ...

result = update_incident({"incident_id": "INC001", "status": "resolved"})
  • AUTO → the wrapped function runs normally.
  • BLOCK → wrapped function is not called; MarshallDenied is raised.
  • APPROVAL_REQUIRED → wrapped function is not called; MarshallApprovalRequired is raised. Approve in Slack or the web UI, the next call goes through.

Pass on_denied=... / on_approval_required=... callbacks if you want to handle these without exceptions:

@ks.wrap_function(
    tool="servicenow", action_type="update_incident",
    connector="servicenow_sim", agent_id="incident-triage",
    on_denied=lambda err: {"status": "blocked-by-policy", "reasons": err.result.decision.get("reasons")},
)
def update_incident(payload: dict) -> dict: ...

Preview without executing

preview = ks.preview(Action(...))
print(preview.decision_value, preview.blast_radius, preview.preview_hash)

Same lifecycle as run but no writes happen — the policy and canary phases evaluate and a receipt is signed for the dry run.


Wrap a LangChain tool

available now (experimental). Requires pip install "action-marshall[langchain]".

from langchain_core.tools import tool
from action_marshall import MarshallClient
from action_marshall.adapters.langchain import wrap_langchain_tool

@tool
def update_incident(payload: dict) -> dict:
    ...

ks = MarshallClient(api_key="am_...", base_url="http://localhost:8000")

protected = wrap_langchain_tool(
    update_incident, ks=ks,
    tool="servicenow", action_type="update_incident",
    connector="servicenow_sim", agent_id="incident-triage",
)
# protected.invoke({...}) is now governed.

Verify a signed receipt

receipt = ks.verify_receipt("act_abc123")
print(receipt.verified)   # True if the HMAC signature matches
print(receipt.signature)

Server-side verification — the backend re-signs the receipt body and compares with constant-time HMAC. For fully offline verification with a local PROOF_SECRET, see the security docs.


Decisions

Decision Meaning
AUTO Allowed without approval. The wrapped function runs.
CANARY Allowed, but a canary subset runs first; the breaker can halt.
APPROVAL_REQUIRED Human approval needed before execution.
BLOCK Disallowed. The wrapped function is not called.

Decision hierarchy: BLOCK > APPROVAL_REQUIRED > CANARY > AUTO. Policies escalate, never de-escalate.

Connectors

Connector Use for Status
servicenow_sim Simulated ServiceNow incidents (demo / dev) available now
servicenow_real Live ServiceNow instance available now
email_generic Outbound email actions available now
jira_real Live Jira Cloud planned

Public API

from action_marshall import (
    MarshallClient,
    Action, ActionParams, Actor, Approval, Approver,
    ActionResult, PreviewResult, Receipt,
    MarshallError, MarshallAPIError, MarshallDenied, MarshallApprovalRequired,
)

The type-checker-friendly py.typed marker ships in the wheel — mypy and pyright will pick up our annotations automatically.


CLI

action-marshall is also installed as a CLI binary. Useful for one-off operations, CI smoke checks, and tools that aren't easily called from Python.

action-marshall --help
action-marshall init                            # write ~/.marshall/config.json
action-marshall preview action.json
action-marshall run action.json
action-marshall receipts list
action-marshall receipts verify act_abc123

Full reference: docs/cli.md.


Local development

git clone https://github.com/SatwikReddySripathi/action-marshall.git
cd action-marshall/sdk
pip install -e ".[dev]"
pytest

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

action_marshall-0.1.0.tar.gz (22.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

action_marshall-0.1.0-py3-none-any.whl (16.7 kB view details)

Uploaded Python 3

File details

Details for the file action_marshall-0.1.0.tar.gz.

File metadata

  • Download URL: action_marshall-0.1.0.tar.gz
  • Upload date:
  • Size: 22.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for action_marshall-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e53c85dd2cbb89c60b3cabf7184beadd2c9fcee5c9bdaf483b677be6bfd8942c
MD5 fd7289e93b55311643acd9682a399f91
BLAKE2b-256 24b952be8b6f3020666a835a3eb44ba53a1cccf44634d5dc97024bab3a079b1b

See more details on using hashes here.

Provenance

The following attestation bundles were made for action_marshall-0.1.0.tar.gz:

Publisher: publish-python.yml on SatwikReddySripathi/action-marshall

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file action_marshall-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: action_marshall-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for action_marshall-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d2bfac6a6deb1d8651f8f074828b0ac509126cd78547aeb4f21018eef83d9822
MD5 159e0e7c1b1e4b526c7f72cae6491060
BLAKE2b-256 a156689a10329098c43e10f43b180cab8d868019cf188f36b040bed93d9a45ad

See more details on using hashes here.

Provenance

The following attestation bundles were made for action_marshall-0.1.0-py3-none-any.whl:

Publisher: publish-python.yml on SatwikReddySripathi/action-marshall

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page