> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentsentinel.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Instrumentation

> Wrap tool calls with telemetry and cost tracking using @guarded_action.

## Basic usage

```python theme={null}
from agent_sentinel import guarded_action

@guarded_action(name="search_web", cost_usd=0.02, tags=["tool", "search"])
def search_web(query: str) -> dict:
    return {"results": ["..."], "query": query}
```

Calling `search_web(...)` will:

* Measure duration
* Record inputs/outputs
* Append a ledger entry

## Async functions

```python theme={null}
from agent_sentinel import guarded_action

@guarded_action(name="call_llm", cost_usd=0.05, tags=["llm"])
async def call_llm(prompt: str) -> str:
    return "..."
```

## Exceptions

Agent Sentinel does **not** swallow your exceptions. If your function raises, the exception propagates normally — the SDK records the action as `outcome="error"` and logs the error string.

## Fail-open vs fail-closed

* **Fail-open**: ledger writes and remote sync should never crash your agent.
* **Fail-closed**: policy violations intentionally raise before execution (see <a href="/sdk/python/policies">Policies</a>).

## Decorator arguments

`@guarded_action` accepts the following keyword-only arguments. Anything beyond `name`, `cost_usd`, and `tags` is optional — sensible defaults apply.

| Argument                              | Type              | Purpose                                                           |
| ------------------------------------- | ----------------- | ----------------------------------------------------------------- |
| `name`                                | `str`             | Logical action name (defaults to function name)                   |
| `cost_usd`                            | `float`           | Estimated per-call cost; deducted from budgets                    |
| `tags`                                | `list[str]`       | Free-form tags; matched against policy `approval_tags`            |
| `requires_human_approval`             | `bool`            | Pause and request human approval before execution                 |
| `approval_description`                | `str`             | Human-readable description shown in the approval inbox            |
| `agent_id` / `task_id` / `mission_id` | `str`             | Attribution overrides; default to current `ExecutionContext`      |
| `produces_evidence`                   | `bool`            | Mark this action as producing evidence other actions can require  |
| `is_commit`                           | `bool`            | Mark as a commit action (subject to evidence + grounding checks)  |
| `requires`                            | `list[str]`       | Names of evidence actions that must precede this one              |
| `argument_constraints`                | `dict`            | JSON Schema applied to kwargs before execution                    |
| `evidence_max_age_seconds`            | `int`             | Maximum age of required evidence before it expires                |
| `grounding_rules`                     | `dict`            | Field-level grounding constraints (action arg → evidence field)   |
| `risk_level`                          | `str`             | One of `critical`, `high`, `medium`, `low`, `minimal`             |
| `idempotency_key`                     | `str \| Callable` | Static key or `(args, kwargs) -> key` callable for replay caching |
| `idempotency_ttl_seconds`             | `float`           | TTL for the cached result (default 3600s)                         |

```python theme={null}
@guarded_action(
    name="charge_card",
    cost_usd=0.01,
    risk_level="high",
    requires=["lookup_customer"],
    grounding_rules={"amount_cents": {"source_action": "lookup_customer", "source_field": "balance_cents"}},
    idempotency_key=lambda *_, **kw: kw["payment_id"],
    idempotency_ttl_seconds=900,
)
def charge_card(*, payment_id: str, customer_id: str, amount_cents: int) -> dict:
    return billing.charge(payment_id, amount_cents)
```

## Replay mode integration

If replay mode is active, decorated actions return recorded outputs instead of executing (and are logged with `outcome="replayed"`).

See <a href="/sdk/python/replay">Replay</a>.
