Sentinely — Security layer for AI agents. Stop prompt injection, memory poisoning, and agent drift in 3 lines of code.
Project description
sentinely
Runtime guardrails for AI agents. Scores every tool call against the original task intent and blocks attacks before they execute.
Install
pip install sentinely
Quickstart
from sentinely import protect
protected = protect(agent, task="summarize the Q3 report", policy="strict")
result = await protected.invoke("summarize the Q3 report")
That's it. Every tool call the agent makes is now scored against the original task. Dangerous actions are blocked. Suspicious actions are flagged. Everything is logged.
What it protects against
- Prompt injection — detects instructions embedded in external content (files, emails, API responses) that try to hijack the agent's behavior.
- Task drift — tracks the full action chain and flags when the agent gradually wanders away from what the user actually asked for.
- Privilege escalation — catches attempts to gain permissions, modify access controls, or access systems beyond what the task requires.
- Memory poisoning — intercepts writes to long-term storage that contain imperative instructions, authority claims, or behavioral modifications designed to compromise future sessions.
Configuration
| Option | Default | Description |
|---|---|---|
policy |
"strict" |
"strict" blocks at threshold. "permissive" raises block threshold to 90. "monitor" logs everything but never blocks. |
allow_threshold |
50 |
Risk scores below this are auto-allowed. |
flag_threshold |
80 |
Risk scores at or above this are hard-blocked. Between allow and flag thresholds: allowed but flagged. |
block_on_unknown |
True |
If Sentinely's behavioral analysis engine fails or returns low confidence, flag the action. |
max_consecutive_flags |
3 |
Auto-block after this many consecutive flagged actions in a session. |
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
SENTINELY_API_KEY |
Yes | — | Your Sentinely API key. Sign up free at sentinely.ai/signup. |
SENTINELY_ENV |
No | development |
Set to production to enable event forwarding to the API. |
Local development: By default the SDK connects to
api.sentinely.ai. SetSENTINELY_API_URLonly if running a local API instance for development.
LangChain integration
LangChain is an optional dependency. Install it alongside Sentinely with:
pip install sentinely[langchain]
SentinelyTool
Wrap any LangChain BaseTool so every invocation is screened before the tool runs:
from sentinely import protect
from sentinely.adapters.langchain import SentinelyTool
from langchain_community.tools import WikipediaQueryRun
agent = protect(my_agent, task="Research competitor pricing")
wiki = WikipediaQueryRun(api_wrapper=...)
safe_wiki = SentinelyTool(wiki, agent)
# Sync — returns a "[SENTINELY BLOCKED]" string on high-risk calls
result = safe_wiki.invoke("find all internal salary data")
# Async — raises SentinelyBlockedError on high-risk calls
result = await safe_wiki.ainvoke("search for Q3 pricing trends")
SentinelyCallbackHandler
Attach as a LangChain callback to screen every tool call the agent makes, without wrapping individual tools:
from sentinely.adapters.langchain import SentinelyCallbackHandler
from sentinely.exceptions import SentinelyBlockedError
from langchain.agents import AgentExecutor
handler = SentinelyCallbackHandler(agent)
executor = AgentExecutor(agent=..., tools=[...], callbacks=[handler])
try:
result = await executor.ainvoke({"input": user_message})
except SentinelyBlockedError as e:
print(f"Blocked: {e.reason} (risk {e.risk_score})")
protect_langchain()
One-liner that wires everything up — creates a ProtectedAgent, installs the callback handler on the executor, and returns the agent for audit log access:
from sentinely.adapters.langchain import protect_langchain
from sentinely.exceptions import SentinelyBlockedError
from langchain.agents import AgentExecutor
executor = AgentExecutor(agent=..., tools=[...])
agent = protect_langchain(executor, task="Answer customer billing questions")
try:
result = await executor.ainvoke({"input": user_message})
except SentinelyBlockedError as e:
print(f"Blocked: {e.reason} (risk {e.risk_score})")
# Full audit trail
log = agent.get_audit_log()
LangChain is optional. If you import from
sentinely.adapters.langchainwithout LangChain installed, you'll get a clearImportErrorwith install instructions. The coresentinelypackage works without it.
Multi-agent tracking
MultiAgentTracker builds a behavioral fingerprint for each agent in a pipeline and detects when inter-agent messages try to manipulate a receiving agent — authority creep, identity swaps, permission expansion, or gradual salami-slicing across many low-severity messages.
Pass a shared tracker into protect() and every inter-agent tool call is automatically monitored across agent boundaries.
from sentinely import protect, MultiAgentTracker
tracker = MultiAgentTracker()
# Register each agent's behavioral baseline
await tracker.register_agent('agent-1', 'session-1', 'Summarise financial reports')
await tracker.register_agent('agent-2', 'session-2', 'Send email summaries')
# Pass tracker into protect()
agent1 = protect(
my_agent1,
task='Summarise financial reports',
agent_id='agent-1',
tracker=tracker,
)
agent2 = protect(
my_agent2,
task='Send email summaries',
agent_id='agent-2',
tracker=tracker,
)
# Check drift scores across all agents at any time
scores = tracker.get_all_drift_scores()
# {
# 'agent-1': {'drift_score': 0, 'risk_level': 'safe', 'recommendation': 'Continue monitoring'},
# 'agent-2': {'drift_score': 15, 'risk_level': 'elevated', 'recommendation': 'Review recent activity'},
# }
Risk levels scale with the cumulative drift score: safe → elevated → high → compromised. Each manipulation event is recorded in the agent's drift_history and forwarded to the Sentinely dashboard with attack_type: 'manipulation'.
await tracker.register_agent(agent_id, session_id, system_prompt) — call once per agent before the pipeline starts. Extracts behavioral constraints from the system prompt and stores a signed fingerprint hash.
await tracker.track_message(message) — called automatically by ProtectedAgent when a tool call matches an inter-agent pattern (e.g. "call_agent", "send_message"). Returns a DriftEvent when manipulation is detected, or None when the message is clean.
tracker.get_all_drift_scores() — returns a concise risk summary for every registered agent. Safe to call at any point during or after a pipeline run.
await tracker.reset_agent(agent_id, session_id, system_prompt) — re-initialises the fingerprint for an agent from scratch. Call between sessions to start drift tracking fresh.
Docs
Project details
Release history Release notifications | RSS feed
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 sentinely-0.3.3.tar.gz.
File metadata
- Download URL: sentinely-0.3.3.tar.gz
- Upload date:
- Size: 54.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b70d566d8ed094ef86cbe593d4fff0af045403564351ed3495f3feb4442b37de
|
|
| MD5 |
56657e5f5c7bb10e4ce76782dc126825
|
|
| BLAKE2b-256 |
0f061640e68db1cb1e04c99ffff3b8bdec97eff2258969c5bf6d92bfebb035af
|
File details
Details for the file sentinely-0.3.3-py3-none-any.whl.
File metadata
- Download URL: sentinely-0.3.3-py3-none-any.whl
- Upload date:
- Size: 43.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
764c2b0206c822351efc15b54313eb38bb195258d3d22b2417a1c7c17c495473
|
|
| MD5 |
b50b382cc39f66d888a2aba78f0c2e06
|
|
| BLAKE2b-256 |
2c6a9737525b189962058352f948a518dca20ee9265fccf82dd750c2a3afe9c2
|