Metadata-Version: 2.4
Name: sakrit
Version: 0.0.1
Summary: Exactly-once effects for AI agents.
Project-URL: Homepage, https://github.com/nagaraju-oruganti/sakrit
Project-URL: Documentation, https://github.com/nagaraju-oruganti/sakrit#readme
Project-URL: Issues, https://github.com/nagaraju-oruganti/sakrit/issues
Project-URL: Source, https://github.com/nagaraju-oruganti/sakrit
Author: Sakrit
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: agents,ai,durable-execution,exactly-once,idempotency
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: langgraph
Requires-Dist: langgraph-checkpoint-sqlite>=3; extra == 'langgraph'
Requires-Dist: langgraph<2,>=1; extra == 'langgraph'
Provides-Extra: openai-agents
Requires-Dist: openai-agents<1.0,>=0.18; extra == 'openai-agents'
Description-Content-Type: text/markdown

# Sakrit

**Exactly-once effects for AI agents.**

Sakrit is a thin, framework-agnostic layer that sits between an AI agent and the
tools it calls, guaranteeing that every action with real-world consequences —
sending an email, charging a card, writing to a database — happens **exactly
once**, even when the agent crashes, resumes, retries, or explores several plans
in parallel.

> Agent frameworks solved *checkpointing* — saving progress so a crashed run can
> resume. They did **not** solve *idempotent side effects*. When an agent resumes
> from a save point, it re-runs steps it already completed. The save point rewinds
> the agent but not the world. Sakrit closes that gap.

## Quickstart

Wrap the tool that touches the world. Declare which arguments are *identity* (a
different value means a different action) and which are *content* (the model may
reword them on a retry). That's it.

```python
from sakrit import Sakrit, SqliteLedger, EffectDecl
from sakrit.core import ArgClass
from sakrit.adapters.langgraph import LangGraphAdapter

sk = Sakrit(
    SqliteLedger("sakrit.db"),
    secret=b"<per-deployment secret>",
    adapter=LangGraphAdapter(),
)


@sk.effect(
    EffectDecl(
        "email.send",
        {
            "to": ArgClass.IDENTITY,  # a different recipient is a different email
            "subject": ArgClass.IDENTITY,
            "body": ArgClass.CONTENT,  # a reworded body is the *same* email
        },
    )
)
def send_email(to: str, subject: str, body: str) -> str:
    return smtp.send(to, subject, body)


# Crash, resume, or retry: send_email fires exactly once.
# A re-run returns the saved result instead of sending again.
```

> **The sequential-repeat trap.** Calling the *same* guarded tool again at the *same*
> call site with the *same* identity args (e.g. deliberately sending one reminder twice)
> replays the recorded result — the effect does **not** re-fire. That is exactly right for
> a crash/resume retry, but a silent no-op for a deliberate repeat. To fire it again, give
> each call its own position:
>
> ```python
> for i, recipient in enumerate(recipients):
>     with sk.step(occurrence=i):
>         send_email(to=recipient, subject=..., body=...)
> ```
>
> Every replay is logged at INFO (and fires the ledger's `on_replay` hook), so it is
> *told*, not silent. A *concurrent* overlapping second call is loud (`EffectInFlightError`);
> only the sequential same-args repeat swallows. Automatic occurrence handling is deferred —
> see the design notes.

## Status

**Pre-alpha — the Act II core works.** Exactly-once for single-worker agents on
LangGraph: positional identity, the coordinate ladder, a fingerprint over identity
args, a write-ahead SQLite ledger, replay, and a startup recovery scan. Verified
end-to-end (the guarded double-email sends once across a real interrupt/resume).

Not yet: multi-worker (leases/fencing, Postgres), the provider-cooperation ladder
beyond L0/replay, the effect outbox / approval gating, and plan-epoch handling —
these are Act III. See [`CONTRIBUTING.md`](CONTRIBUTING.md) to get involved (a
signed CLA comes first).

## Development

Requires Python ≥ 3.10. This project uses [uv](https://docs.astral.sh/uv/).

```bash
# Install dependencies (including dev tools)
uv sync

# Run the test suite
uv run pytest

# Lint and type-check
uv run ruff check .
uv run mypy
```

## License

Apache-2.0.
