aynigma 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aynigma-0.3.1/.gitignore +77 -0
- aynigma-0.3.1/CHANGELOG.md +38 -0
- aynigma-0.3.1/PKG-INFO +218 -0
- aynigma-0.3.1/README.md +173 -0
- aynigma-0.3.1/pyproject.toml +40 -0
- aynigma-0.3.1/src/aynigma/__init__.py +61 -0
- aynigma-0.3.1/src/aynigma/agent_errors.py +89 -0
- aynigma-0.3.1/src/aynigma/agent_tokens.py +404 -0
- aynigma-0.3.1/src/aynigma/agents.py +1366 -0
- aynigma-0.3.1/src/aynigma/approval_checkpoints.py +170 -0
- aynigma-0.3.1/src/aynigma/assess.py +94 -0
- aynigma-0.3.1/src/aynigma/assess_plugins.py +42 -0
- aynigma-0.3.1/src/aynigma/audit_verify.py +491 -0
- aynigma-0.3.1/src/aynigma/client.py +529 -0
- aynigma-0.3.1/src/aynigma/discover.py +41 -0
- aynigma-0.3.1/src/aynigma/discover_azure.py +48 -0
- aynigma-0.3.1/src/aynigma/discover_cloud.py +46 -0
- aynigma-0.3.1/src/aynigma/discover_refresh.py +59 -0
- aynigma-0.3.1/src/aynigma/discover_vertex.py +43 -0
- aynigma-0.3.1/src/aynigma/mcp_oauth.py +61 -0
- aynigma-0.3.1/src/aynigma/package_verify.py +369 -0
- aynigma-0.3.1/src/aynigma/py.typed +0 -0
- aynigma-0.3.1/tests/_fake_agent_api.py +181 -0
- aynigma-0.3.1/tests/test_agents.py +1241 -0
- aynigma-0.3.1/tests/test_agents_receipts.py +225 -0
- aynigma-0.3.1/tests/test_approval_checkpoint_cli.py +42 -0
- aynigma-0.3.1/tests/test_approval_checkpoints.py +73 -0
- aynigma-0.3.1/tests/test_approval_resume.py +158 -0
- aynigma-0.3.1/tests/test_assess.py +42 -0
- aynigma-0.3.1/tests/test_assess_plugins.py +24 -0
- aynigma-0.3.1/tests/test_audit_verify.py +390 -0
- aynigma-0.3.1/tests/test_client.py +513 -0
- aynigma-0.3.1/tests/test_discover.py +24 -0
- aynigma-0.3.1/tests/test_discover_azure.py +51 -0
- aynigma-0.3.1/tests/test_discover_cloud.py +31 -0
- aynigma-0.3.1/tests/test_discover_refresh.py +34 -0
- aynigma-0.3.1/tests/test_discover_vertex.py +28 -0
- aynigma-0.3.1/tests/test_google_adk_approval.py +358 -0
- aynigma-0.3.1/tests/test_langgraph_postgres.py +67 -0
- aynigma-0.3.1/tests/test_mcp_oauth.py +23 -0
- aynigma-0.3.1/tests/test_openai_approval_bridge.py +187 -0
- aynigma-0.3.1/tests/test_tokens.py +359 -0
aynigma-0.3.1/.gitignore
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# Django stuff:
|
|
28
|
+
*.log
|
|
29
|
+
local_settings.py
|
|
30
|
+
db.sqlite3
|
|
31
|
+
db.sqlite3-journal
|
|
32
|
+
/media
|
|
33
|
+
/staticfiles
|
|
34
|
+
|
|
35
|
+
# Environment
|
|
36
|
+
.env
|
|
37
|
+
.venv
|
|
38
|
+
env/
|
|
39
|
+
venv/
|
|
40
|
+
ENV/
|
|
41
|
+
env.bak/
|
|
42
|
+
venv.bak/
|
|
43
|
+
|
|
44
|
+
# IDEs
|
|
45
|
+
.vscode/
|
|
46
|
+
.idea/
|
|
47
|
+
*.swp
|
|
48
|
+
*.swo
|
|
49
|
+
*~
|
|
50
|
+
|
|
51
|
+
# Testing
|
|
52
|
+
.coverage
|
|
53
|
+
.pytest_cache/
|
|
54
|
+
htmlcov/
|
|
55
|
+
|
|
56
|
+
# Celery
|
|
57
|
+
celerybeat-schedule
|
|
58
|
+
celerybeat.pid
|
|
59
|
+
|
|
60
|
+
# macOS
|
|
61
|
+
.DS_Store
|
|
62
|
+
|
|
63
|
+
# Redis
|
|
64
|
+
dump.rdb
|
|
65
|
+
|
|
66
|
+
# Logs directory
|
|
67
|
+
logs/
|
|
68
|
+
|
|
69
|
+
# Token/credential files
|
|
70
|
+
token.txt
|
|
71
|
+
login_output.txt
|
|
72
|
+
|
|
73
|
+
# Test results
|
|
74
|
+
test_results_*.json
|
|
75
|
+
|
|
76
|
+
# Rendered Cerbos agent policies (generated at runtime, not source)
|
|
77
|
+
cerbos/policies/agent_*.yaml
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- `aynigma-audit-verify verify-package <zip>` (module `aynigma.package_verify`): verifies a signed Aynigma evidence package offline. Checks the manifest signature (RS256, key from the package or from `--jwks`), every file's sha256 and size, the audit chain segment (order, hash links, head hash, optional full recompute with the chain key), the signed anchors against the segment, and assurance sign-off hashes. Standard library only. Exit 0 pass, 1 failed check, 3 unusable input. See `docs/EVIDENCE_PACKAGE.md`.
|
|
6
|
+
- New command `aynigma-audit-verify` (module `aynigma.audit_verify`): verifies an audit JSON export and a signed chain anchor offline. Recomputes hash v2 rows with the customer-held chain key and hash v1 rows without one, rejects v1 rows above the cutover signed in a verified anchor (or after a v2 row) as downgrades (the unsigned export header never grants trust; legacy-only exports without a verified anchor are inconclusive), checks linkage (`--full-chain` for an unfiltered export) and the anchor's RS256 signature against the JWKS. Standard library only. Exit 0 pass, 1 failed check, 2 inconclusive (no key and no anchor pinning the export), 3 unusable input. See `docs/AUDIT_CHAIN.md` in the backend repo.
|
|
7
|
+
|
|
8
|
+
## 0.3.1 - 2026-10-16
|
|
9
|
+
|
|
10
|
+
- `ApprovalRequired` now has `run_id` and `tool_name` (keyword-friendly, default None; `approval_id` and `call_id` stay first) and a message that says who can approve, what to call next and which run to resume in. `run_id` is the provider's current run, None with the legacy `X-AGENT-KEY` header.
|
|
11
|
+
- `AgentAuthorizer(..., run_id=...)` continues an earlier run, so a new process can consume an approval that is bound to the run it was raised in. Without it a resumed call is denied with `run_mismatch`. It raises `ValueError` when combined with `token_provider` or with the legacy header.
|
|
12
|
+
- `ToolDenied` adds a short hint after the code for common denials (`no_policy`, `run_mismatch`, `payload_mismatch`, `approval_already_used`, `approval_expired`, `approval_not_approved`, `policy_version_changed`, `environment`, `rate_limited`) and a `hint` attribute. `str(error)` still starts with `Agent tool call denied: <code>` and `reason` is unchanged.
|
|
13
|
+
- README: install from the portal-hosted wheel until the package is on PyPI, and an approvals pause and resume example.
|
|
14
|
+
|
|
15
|
+
## 0.3.0 - 2026-10-06
|
|
16
|
+
|
|
17
|
+
- Agent tokens: `AgentTokenProvider` (client credentials, cached, refreshed at 80 % of the lifetime or once on a 401, thread safe) and `DelegatedToken` (RFC 8693 exchange).
|
|
18
|
+
- `AgentAuthorizer` takes `token_provider`, `agent_id` and `use_legacy_key`. With an agent ID and key it now sends `Authorization: Bearer <token>` instead of `X-AGENT-KEY` (set `use_legacy_key=True` or `AYNIGMA_USE_LEGACY_KEY=1` to keep the header). New `delegate()`, `delegate_to()`, `auth_headers()` and `mcp_call()`.
|
|
19
|
+
- `acting_for_user` and `initiator_token_provider` exchange the user JWT for a delegated token when tokens are in use.
|
|
20
|
+
- `RunKilled(ToolDenied)` for `run_killed` decisions; cached tokens are dropped. `TokenError` for token endpoint refusals.
|
|
21
|
+
- Run liveness: only a killed run raises `RunKilled`; an ended run is rotated and the request retried once, logged at INFO. `AgentAuthorizer.mcp_call` returns None for a 202, raises `MCPError` for a 400 and tracks `Mcp-Session-Id`.
|
|
22
|
+
- An explicit key without `agent_id` stays on the legacy header; `AYNIGMA_AGENT_ID` is read from the environment only with an environment key.
|
|
23
|
+
- New command `aynigma-agents-token`. New environment variables `AYNIGMA_AGENT_ID` and `AYNIGMA_USE_LEGACY_KEY`.
|
|
24
|
+
|
|
25
|
+
## 0.2.0 — 2026-09-28
|
|
26
|
+
|
|
27
|
+
- Add the Protect v2 `X-Aynigma-Source: sdk` header and `end_user_id` support.
|
|
28
|
+
- Expose `user_message` and `quarantined` on `ValidationResult`; use the
|
|
29
|
+
configured user message for `BlockedError` when supplied by the API.
|
|
30
|
+
- Deprecate the classic `validate_input()` and `validate_output()` methods with
|
|
31
|
+
`DeprecationWarning`; use `guard()` with the `prompt` or `response` stage.
|
|
32
|
+
## Unreleased: mandatory receipts
|
|
33
|
+
|
|
34
|
+
- `ToolExecutor(require_receipt=True)` is the default. A call without a receipt raises `ToolDenied("receipt_required")` and no handler runs before `verify`. `require_receipt=False` is an explicit opt-out for backends run with `AGENT_REQUIRE_EXECUTION_RECEIPT=False`.
|
|
35
|
+
- New `AgentAuthorizer.run_tool(tool_name, params, handler)`: authorize, verify, run, report. A handler error reports FAILED (no error text) and is re-raised; a failed report no longer hides it.
|
|
36
|
+
- New `RemoteTool.local(authorizer, tool_name, handler)` for tools that run in the agent's process. All framework adapters go through `RemoteTool`, so they use `run_tool`. A `RemoteTool` with an `executor_url` is unchanged: your executor service does verify and report.
|
|
37
|
+
- `ExecutionReceipt` keeps extra fields the backend issued (`extra`, `as_dict()`) and forwards them unchanged to the executor.
|
|
38
|
+
- `mcp_call` recovers from an expired gateway session: on a 404 for a request that carried an `Mcp-Session-Id` it drops the session, runs `initialize` once and retries once, then raises `MCPError` if that fails too. TypeScript `mcpCall` does the same.
|
aynigma-0.3.1/PKG-INFO
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aynigma
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Python SDK for Aynigma: IAM for AI agents and AI security guardrails
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Requires-Dist: httpx>=0.25
|
|
7
|
+
Provides-Extra: approval-postgres
|
|
8
|
+
Requires-Dist: cryptography<51,>=42; extra == 'approval-postgres'
|
|
9
|
+
Requires-Dist: psycopg[binary]<4,>=3.2; extra == 'approval-postgres'
|
|
10
|
+
Provides-Extra: claude-agent
|
|
11
|
+
Requires-Dist: claude-agent-sdk<0.3,>=0.2.160; extra == 'claude-agent'
|
|
12
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'claude-agent'
|
|
13
|
+
Provides-Extra: crewai
|
|
14
|
+
Requires-Dist: crewai<1.16,>=1.15; extra == 'crewai'
|
|
15
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'crewai'
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
18
|
+
Provides-Extra: google-adk
|
|
19
|
+
Requires-Dist: google-adk<1.40,>=1.39; extra == 'google-adk'
|
|
20
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'google-adk'
|
|
21
|
+
Provides-Extra: google-adk-postgres
|
|
22
|
+
Requires-Dist: asyncpg<1,>=0.30; extra == 'google-adk-postgres'
|
|
23
|
+
Requires-Dist: google-adk<1.40,>=1.39; extra == 'google-adk-postgres'
|
|
24
|
+
Requires-Dist: psycopg[binary]<4,>=3.2; extra == 'google-adk-postgres'
|
|
25
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'google-adk-postgres'
|
|
26
|
+
Requires-Dist: sqlalchemy[asyncio]<3,>=2; extra == 'google-adk-postgres'
|
|
27
|
+
Provides-Extra: langchain
|
|
28
|
+
Requires-Dist: langchain-core<1,>=0.3; extra == 'langchain'
|
|
29
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'langchain'
|
|
30
|
+
Provides-Extra: langgraph
|
|
31
|
+
Requires-Dist: langgraph<1.3,>=1.2; extra == 'langgraph'
|
|
32
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'langgraph'
|
|
33
|
+
Provides-Extra: langgraph-postgres
|
|
34
|
+
Requires-Dist: langgraph-checkpoint-postgres<3.2,>=3.1; extra == 'langgraph-postgres'
|
|
35
|
+
Requires-Dist: langgraph<1.3,>=1.2; extra == 'langgraph-postgres'
|
|
36
|
+
Requires-Dist: psycopg[binary]<4,>=3.2; extra == 'langgraph-postgres'
|
|
37
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'langgraph-postgres'
|
|
38
|
+
Provides-Extra: openai-agents
|
|
39
|
+
Requires-Dist: openai-agents<0.23,>=0.22; extra == 'openai-agents'
|
|
40
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'openai-agents'
|
|
41
|
+
Provides-Extra: semantic-kernel
|
|
42
|
+
Requires-Dist: pydantic<3,>=2.7; extra == 'semantic-kernel'
|
|
43
|
+
Requires-Dist: semantic-kernel<1.45,>=1.44; extra == 'semantic-kernel'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# Aynigma Python SDK
|
|
47
|
+
|
|
48
|
+
Python SDK for Aynigma: IAM for AI agents and AI security guardrails.
|
|
49
|
+
|
|
50
|
+
Authorize agent tool calls, handle human approvals, enforce execution receipts,
|
|
51
|
+
and scan prompts and responses with Protect.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python -m pip install aynigma
|
|
55
|
+
# Or, from a source checkout of the backend repo:
|
|
56
|
+
python -m pip install ./sdk/python
|
|
57
|
+
export AYNIGMA_API_KEY=psk_your_project_key
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from aynigma import Aynigma
|
|
62
|
+
|
|
63
|
+
client = Aynigma()
|
|
64
|
+
result = client.guard("Hello", stage="prompt")
|
|
65
|
+
print(result.allowed)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The default API URL is `https://api.aynigma.ai/api/v1`. Supply `base_url` or
|
|
69
|
+
set `AYNIGMA_API_URL` to use another deployment. Validation endpoints are
|
|
70
|
+
`/guardrails/validate/input/` and `/guardrails/validate/output/`; project keys
|
|
71
|
+
are sent in the `X-API-KEY` header.
|
|
72
|
+
These classic validation endpoints are deprecated. `validate_input()` and
|
|
73
|
+
`validate_output()` continue to work but emit `DeprecationWarning`; migrate to
|
|
74
|
+
`guard(text, stage="prompt")` and `guard(text, stage="response")` respectively.
|
|
75
|
+
|
|
76
|
+
Protect v2 stage-aware scans are also available from both sync and async
|
|
77
|
+
clients. `guard()` accepts `prompt`, `response`, `tool_call`, `tool_result`,
|
|
78
|
+
`retrieval`, or `document`; scope can be bound with `company_id`, `project_id`,
|
|
79
|
+
`system_id`, `environment_id`, `policy_id`, and a per-generation
|
|
80
|
+
`conversation_id`. When environment token pricing is enabled, direct `guard()`
|
|
81
|
+
calls for prompt/response billing also require a fresh unique `generation_id`;
|
|
82
|
+
the OpenAI wrappers create one automatically and reuse it across that model
|
|
83
|
+
call's stages. Keep a stable conversation ID across turns, but never reuse a
|
|
84
|
+
generation ID for a different provider call. A blocked scan raises the same
|
|
85
|
+
`BlockedError` as v1. V1 remains available for existing integrations.
|
|
86
|
+
|
|
87
|
+
Pass `end_user_id="customer-42"` to `guard()` to associate detections with an
|
|
88
|
+
end user for per-policy repeat-offender quarantine; IDs may be at most 128
|
|
89
|
+
characters and the server hashes them. SDK v2 guard calls automatically send
|
|
90
|
+
`X-Aynigma-Source: sdk`, which is recorded on detection events. A
|
|
91
|
+
`ValidationResult` exposes `user_message` and `quarantined`; when a blocked
|
|
92
|
+
response includes `user_message`, it is also used as `str(BlockedError)` and is
|
|
93
|
+
available on the exception's `user_message` attribute.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
with Aynigma() as client:
|
|
97
|
+
result = client.guard(
|
|
98
|
+
"Retrieve customer account details",
|
|
99
|
+
"prompt",
|
|
100
|
+
system_id=4,
|
|
101
|
+
environment_id=12,
|
|
102
|
+
conversation_id="support-session-42",
|
|
103
|
+
generation_id="support-session-42-generation-1",
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`guard_openai_chat_v2()` scans prompt, previous tool results, response, and
|
|
108
|
+
proposed tool calls as separate stages. It rejects streaming rather than
|
|
109
|
+
releasing unchecked chunks; use the TypeScript wrapper or bounded LiteLLM
|
|
110
|
+
gateway for hold-back streaming.
|
|
111
|
+
|
|
112
|
+
## Agent IAM quickstart
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python -m pip install aynigma
|
|
116
|
+
# Or, from a source checkout of the backend repo:
|
|
117
|
+
python -m pip install ./sdk/python
|
|
118
|
+
export AYNIGMA_AGENT_ID=<agent uuid>
|
|
119
|
+
export AYNIGMA_AGENT_KEY=agt_...
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
With an agent ID and key the SDK trades the key for a 15 minute token, caches it, refreshes it at 80 % of its lifetime (or once after a 401), and sends `Authorization: Bearer <token>` on tool calls, receipt checks and MCP calls. The key is sent only to the token endpoint.
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from aynigma import AgentAuthorizer, AgentTokenProvider, RemoteTool, acting_for_user
|
|
126
|
+
|
|
127
|
+
authorizer = AgentAuthorizer() # reads AYNIGMA_AGENT_ID, AYNIGMA_AGENT_KEY, AYNIGMA_API_URL
|
|
128
|
+
create_ticket = RemoteTool(authorizer, "https://executor.internal.example", "ticket.create")
|
|
129
|
+
|
|
130
|
+
# Delegation: act for a signed-in person. Their JWT is exchanged for a delegated token
|
|
131
|
+
# (RFC 8693); the raw JWT is never forwarded and the backend records a verified chain.
|
|
132
|
+
with acting_for_user(current_user_jwt):
|
|
133
|
+
create_ticket(title="Investigate alert")
|
|
134
|
+
|
|
135
|
+
# Narrow what the delegation may do, or hand it to a sub-agent.
|
|
136
|
+
scoped = authorizer.delegate(current_user_jwt, scope="db.read")
|
|
137
|
+
sub_authorizer = scoped.delegate_to(AgentTokenProvider(SUB_AGENT_ID, SUB_AGENT_KEY))
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Receipts: every ALLOW returns a one-use receipt bound to the exact arguments and, with tokens, to the agent run. The executor calls `ToolExecutor.execute` (verify, run, report) with its own `AgentAuthorizer`. If an administrator kills the run, verify and tool calls fail with `RunKilled` (a `ToolDenied`), the SDK drops its cached tokens, and the next call starts a new run. A report for an already consumed receipt is still accepted after a kill and is recorded as `after_kill`. A run that only ended (idle) is replaced transparently and logged at INFO on the `aynigma.agents` logger.
|
|
141
|
+
|
|
142
|
+
### Approvals: pause and resume
|
|
143
|
+
|
|
144
|
+
A rule with effect REVIEW raises `ApprovalRequired` until a person approves the call in Agent IAM > Activity. The approval belongs to the run it was raised in, so save `approval_id` and `run_id` when you catch it. If the work continues in another process (a queue worker, a restart), build the new authorizer with that `run_id`. Without it the new process starts a fresh run and the call is denied with `run_mismatch`.
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from aynigma import AgentAuthorizer, ApprovalRequired
|
|
148
|
+
|
|
149
|
+
authorizer = AgentAuthorizer(agent_id=AGENT_ID, agent_key=AGENT_KEY, base_url=BASE_URL)
|
|
150
|
+
try:
|
|
151
|
+
authorizer.run_tool(tool, params, handler)
|
|
152
|
+
except ApprovalRequired as pending:
|
|
153
|
+
saved_approval_id, saved_run_id = pending.approval_id, pending.run_id # store these with your task
|
|
154
|
+
|
|
155
|
+
# Later, after a reviewer approved it, possibly in a new process:
|
|
156
|
+
resumed = AgentAuthorizer(agent_id=AGENT_ID, agent_key=AGENT_KEY, base_url=BASE_URL, run_id=saved_run_id)
|
|
157
|
+
resumed.run_tool(tool, params, handler, approval_id=saved_approval_id) # same tool, same params
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`run_id` is None with the legacy `X-AGENT-KEY` header, which has no runs and needs no `run_id` to resume. If you pass your own `token_provider`, give that `AgentTokenProvider` the `run_id` instead; passing `run_id` to `AgentAuthorizer` as well raises `ValueError`. The arguments must match what was approved, or the call is denied with `payload_mismatch`.
|
|
161
|
+
|
|
162
|
+
Denials explain themselves: `str(ToolDenied)` starts with `Agent tool call denied: <code>` and adds a short hint for common codes (`no_policy`, `run_mismatch`, `payload_mismatch`, `approval_already_used`, `approval_expired`, `environment`, `rate_limited`). `error.reason` is the bare code and `error.hint` the hint, or None.
|
|
163
|
+
|
|
164
|
+
Debug a token from a shell: `AYNIGMA_AGENT_KEY=agt_... aynigma-agents-token --agent-id <uuid> --claims`. The key is never printed.
|
|
165
|
+
|
|
166
|
+
## Verify an audit export offline
|
|
167
|
+
|
|
168
|
+
Auditors can check an audit export and its signed anchor without trusting the API. Standard library only.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
aynigma-audit-verify --export audit.json --anchor anchor.json \
|
|
172
|
+
--jwks https://api.aynigma.ai/api/v1/agents/.well-known/jwks.json \
|
|
173
|
+
--chain-key-file chain.key --full-chain
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`audit.json` is `GET /api/v1/audit/export/?log_type=audit&export_format=json` (an object with a `chain` header and `records`) and `anchor.json` is `GET /api/v1/audit/anchors/<id>/`. With the chain key (`--chain-key-file`, or `AYNIGMA_AUDIT_CHAIN_KEY`; `--chain-key` works but shows in the process list) every row is recomputed; without it only legacy rows are, and the report lists what was skipped. Legacy (v1) rows need no key, so they are trusted only up to the `last_v1_entry_id` signed inside a verified anchor (the export header's value is unsigned and never grants trust); a v1 row above it, or after a keyed row, is a forgery and fails. An export of only legacy rows with no verified anchor is INCONCLUSIVE. `--full-chain` means the export is the unfiltered chain, so each row must follow its predecessor.
|
|
177
|
+
|
|
178
|
+
Exit status: 0 pass, 1 a check failed, 2 inconclusive, 3 unusable input. PASS needs every row recomputed with the key, or an anchor whose head is in the export with an unbroken chain back from it (printed as "structure only", since v2 contents are not recomputed without the key). With neither the result is INCONCLUSIVE, never a pass. The hashing rules are in `docs/AUDIT_CHAIN.md` in the backend repo.
|
|
179
|
+
|
|
180
|
+
Migrating from `X-AGENT-KEY`: nothing breaks. A key with no agent ID keeps sending the header, and `use_legacy_key=True` (or `AYNIGMA_USE_LEGACY_KEY=1`) keeps it even when an agent ID is set. Since 0.3.0, `AgentAuthorizer` switches to tokens as soon as it has both an agent ID and a key. An explicit `agent_key=` with no `agent_id=` stays legacy, and `AYNIGMA_AGENT_ID` from the environment counts only together with `AYNIGMA_AGENT_KEY` from the environment. To move over, pass `agent_id=` or set both variables, and watch the `agent.legacy_key_used` audit event stop. `acting_for_user` and `initiator_token_provider` now exchange the user JWT instead of forwarding it; with the legacy header they forward it as before. Delegated tokens carry the delegation depth counted from the human (a direct human to agent hop is depth 1), so review `max_delegation_depth` conditions. See `docs/AGENT_TOOL_EXECUTOR.md` and `docs/AGENT_IDENTITY.md`.
|
|
181
|
+
|
|
182
|
+
Beyond the quickstart, use `AgentAuthorizer` and `RemoteTool` with a separately deployed receipt-enforcing executor. Optional adapters are `as_langchain_tool`, `as_openai_agents_tool`, `as_crewai_tool`, `as_claude_agent_tool`, `as_google_adk_tool`, and `as_semantic_kernel_function`; install their corresponding extras and pass a Pydantic argument model. `OpenAIAgentsApprovalBridge` uses the OpenAI Agents SDK's native approval interruption to preserve the exact model-selected call through backend REVIEW and receipt-backed resume. `build_crewai_approval_flow` uses CrewAI Flow human feedback and explicit persistence for the same handoff. `ClaudeAgentApprovalBridge` pairs the Claude Agent SDK's native `PreToolUse` defer hook with a receipt-backed MCP tool and caller-owned pending state. `SemanticKernelApprovalBridge` intercepts Semantic Kernel auto function invocation before a model-selected call and supports exact-call REVIEW resume through a caller-owned checkpoint. `as_google_adk_approval_tool` uses ADK's native confirmation event and persisted session state. Install `aynigma[google-adk-postgres]` for ADK's PostgreSQL session service; a local restart contract passed against a disposable PostgreSQL database. `build_langgraph_approval_workflow` adds a checkpointed pause/resume graph for REVIEW decisions and requires an explicit checkpointer. Install `aynigma[langgraph-postgres]` and use LangGraph's `PostgresSaver` for durable resume. See `docs/AGENT_TOOL_EXECUTOR.md` in the backend repository for the executor boundary and examples.
|
|
183
|
+
|
|
184
|
+
`PostgresApprovalCheckpointStore` is an optional encrypted store for serializable pending approval state shared across workers. Install `aynigma[approval-postgres]`, call `setup()` once, then `save()` and one-use `claim()` with authenticated tenant and agent UUIDs. Schedule `aynigma-approval-checkpoints-purge` daily with `AYNIGMA_APPROVAL_CHECKPOINT_DATABASE_URL` supplied from the operator's secret store; it prints only the number purged. The application still serializes framework-specific session state and authorizes its resume endpoint. See the executor guide for retention and recovery rules.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from aynigma import Aynigma, guard_openai_chat_v2
|
|
188
|
+
|
|
189
|
+
with Aynigma() as client:
|
|
190
|
+
response = guard_openai_chat_v2(
|
|
191
|
+
client,
|
|
192
|
+
openai_client,
|
|
193
|
+
messages,
|
|
194
|
+
model="gpt-4o-mini",
|
|
195
|
+
guard_options={"system_id": 4, "environment_id": 12},
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
# Bedrock agent inventory
|
|
199
|
+
|
|
200
|
+
On a paid plan, run `aynigma-agents-discover-cloud --company-id 42 --account-id 123456789012 --region us-east-1` with `AYNIGMA_USER_JWT`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and optional `AWS_SESSION_TOKEN` set. The command sends the credentials once and prints only the bounded agent inventory. The backend checks the AWS account through STS before `ListAgents` and does not retain the credentials. See `docs/AGENT_BEDROCK_DISCOVERY.md` for scope and limits.
|
|
201
|
+
|
|
202
|
+
For Vertex Agent Engine, set `AYNIGMA_USER_JWT` and `GOOGLE_ACCESS_TOKEN`, then run `aynigma-agents-discover-vertex --company-id 42 --project-id sample-project --region us-central1`. The backend confirms the active Google Cloud project before listing reasoning engines. See `docs/AGENT_VERTEX_DISCOVERY.md`.
|
|
203
|
+
|
|
204
|
+
For a known Azure AI Foundry project, set `AYNIGMA_USER_JWT` and `AZURE_ACCESS_TOKEN`, then run `aynigma-agents-discover-azure --company-id 42 --project-endpoint https://account.services.ai.azure.com/api/projects/project`. See `docs/AGENT_AZURE_DISCOVERY.md`.
|
|
205
|
+
|
|
206
|
+
To find Foundry projects in a subscription first, set a separate `AZURE_ARM_ACCESS_TOKEN` and run `aynigma-agents-discover-azure --company-id 42 --subscription-id 11111111-2222-4333-8444-555555555555`. This lists bounded project metadata only; use a project token and endpoint to list agents.
|
|
207
|
+
|
|
208
|
+
For aggregate native Assess plugin review coverage, run `aynigma-assess-plugin-performance --project-id 42 --days 90` with `AYNIGMA_USER_JWT`. It returns counts only; see `docs/ASSESS_PLUGIN_PERFORMANCE.md` for interpretation.
|
|
209
|
+
|
|
210
|
+
When registering an agent through the dashboard API, an administrator may provide a same-company `project` ID to attribute approvals and incidents to that project's compliance evidence. The project cannot be changed after registration. See `docs/AGENT_COMPLIANCE_EVIDENCE.md`.
|
|
211
|
+
|
|
212
|
+
Administrators can read registered-agent slot usage at `GET /api/v1/agents/usage/?company=<id>`. Free includes two slots; current paid caps are documented in `docs/AGENT_PLAN_LIMITS.md`.
|
|
213
|
+
|
|
214
|
+
For agent tools acting on behalf of an authenticated person, wrap the tool invocation in `with acting_for_user(user_jwt):` after importing `acting_for_user` from `aynigma`. The binding is scoped to the current Python task and carries through async framework adapters; see `docs/AGENT_TOOL_EXECUTOR.md`.
|
|
215
|
+
|
|
216
|
+
## Receipts are required
|
|
217
|
+
|
|
218
|
+
Run in-process tools with `auth.run_tool("ticket.create", {"title": "x"}, handler)` or `RemoteTool.local(auth, "ticket.create", handler)`. Both authorize, verify the one-use receipt, run the handler and report SUCCEEDED or FAILED. `ToolExecutor` refuses a call without a receipt unless you pass `require_receipt=False`, which is only for a backend running with `AGENT_REQUIRE_EXECUTION_RECEIPT=False`. See `docs/AGENT_TOOL_EXECUTOR.md`.
|
aynigma-0.3.1/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Aynigma Python SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for Aynigma: IAM for AI agents and AI security guardrails.
|
|
4
|
+
|
|
5
|
+
Authorize agent tool calls, handle human approvals, enforce execution receipts,
|
|
6
|
+
and scan prompts and responses with Protect.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
python -m pip install aynigma
|
|
10
|
+
# Or, from a source checkout of the backend repo:
|
|
11
|
+
python -m pip install ./sdk/python
|
|
12
|
+
export AYNIGMA_API_KEY=psk_your_project_key
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from aynigma import Aynigma
|
|
17
|
+
|
|
18
|
+
client = Aynigma()
|
|
19
|
+
result = client.guard("Hello", stage="prompt")
|
|
20
|
+
print(result.allowed)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The default API URL is `https://api.aynigma.ai/api/v1`. Supply `base_url` or
|
|
24
|
+
set `AYNIGMA_API_URL` to use another deployment. Validation endpoints are
|
|
25
|
+
`/guardrails/validate/input/` and `/guardrails/validate/output/`; project keys
|
|
26
|
+
are sent in the `X-API-KEY` header.
|
|
27
|
+
These classic validation endpoints are deprecated. `validate_input()` and
|
|
28
|
+
`validate_output()` continue to work but emit `DeprecationWarning`; migrate to
|
|
29
|
+
`guard(text, stage="prompt")` and `guard(text, stage="response")` respectively.
|
|
30
|
+
|
|
31
|
+
Protect v2 stage-aware scans are also available from both sync and async
|
|
32
|
+
clients. `guard()` accepts `prompt`, `response`, `tool_call`, `tool_result`,
|
|
33
|
+
`retrieval`, or `document`; scope can be bound with `company_id`, `project_id`,
|
|
34
|
+
`system_id`, `environment_id`, `policy_id`, and a per-generation
|
|
35
|
+
`conversation_id`. When environment token pricing is enabled, direct `guard()`
|
|
36
|
+
calls for prompt/response billing also require a fresh unique `generation_id`;
|
|
37
|
+
the OpenAI wrappers create one automatically and reuse it across that model
|
|
38
|
+
call's stages. Keep a stable conversation ID across turns, but never reuse a
|
|
39
|
+
generation ID for a different provider call. A blocked scan raises the same
|
|
40
|
+
`BlockedError` as v1. V1 remains available for existing integrations.
|
|
41
|
+
|
|
42
|
+
Pass `end_user_id="customer-42"` to `guard()` to associate detections with an
|
|
43
|
+
end user for per-policy repeat-offender quarantine; IDs may be at most 128
|
|
44
|
+
characters and the server hashes them. SDK v2 guard calls automatically send
|
|
45
|
+
`X-Aynigma-Source: sdk`, which is recorded on detection events. A
|
|
46
|
+
`ValidationResult` exposes `user_message` and `quarantined`; when a blocked
|
|
47
|
+
response includes `user_message`, it is also used as `str(BlockedError)` and is
|
|
48
|
+
available on the exception's `user_message` attribute.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
with Aynigma() as client:
|
|
52
|
+
result = client.guard(
|
|
53
|
+
"Retrieve customer account details",
|
|
54
|
+
"prompt",
|
|
55
|
+
system_id=4,
|
|
56
|
+
environment_id=12,
|
|
57
|
+
conversation_id="support-session-42",
|
|
58
|
+
generation_id="support-session-42-generation-1",
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`guard_openai_chat_v2()` scans prompt, previous tool results, response, and
|
|
63
|
+
proposed tool calls as separate stages. It rejects streaming rather than
|
|
64
|
+
releasing unchecked chunks; use the TypeScript wrapper or bounded LiteLLM
|
|
65
|
+
gateway for hold-back streaming.
|
|
66
|
+
|
|
67
|
+
## Agent IAM quickstart
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
python -m pip install aynigma
|
|
71
|
+
# Or, from a source checkout of the backend repo:
|
|
72
|
+
python -m pip install ./sdk/python
|
|
73
|
+
export AYNIGMA_AGENT_ID=<agent uuid>
|
|
74
|
+
export AYNIGMA_AGENT_KEY=agt_...
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
With an agent ID and key the SDK trades the key for a 15 minute token, caches it, refreshes it at 80 % of its lifetime (or once after a 401), and sends `Authorization: Bearer <token>` on tool calls, receipt checks and MCP calls. The key is sent only to the token endpoint.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from aynigma import AgentAuthorizer, AgentTokenProvider, RemoteTool, acting_for_user
|
|
81
|
+
|
|
82
|
+
authorizer = AgentAuthorizer() # reads AYNIGMA_AGENT_ID, AYNIGMA_AGENT_KEY, AYNIGMA_API_URL
|
|
83
|
+
create_ticket = RemoteTool(authorizer, "https://executor.internal.example", "ticket.create")
|
|
84
|
+
|
|
85
|
+
# Delegation: act for a signed-in person. Their JWT is exchanged for a delegated token
|
|
86
|
+
# (RFC 8693); the raw JWT is never forwarded and the backend records a verified chain.
|
|
87
|
+
with acting_for_user(current_user_jwt):
|
|
88
|
+
create_ticket(title="Investigate alert")
|
|
89
|
+
|
|
90
|
+
# Narrow what the delegation may do, or hand it to a sub-agent.
|
|
91
|
+
scoped = authorizer.delegate(current_user_jwt, scope="db.read")
|
|
92
|
+
sub_authorizer = scoped.delegate_to(AgentTokenProvider(SUB_AGENT_ID, SUB_AGENT_KEY))
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Receipts: every ALLOW returns a one-use receipt bound to the exact arguments and, with tokens, to the agent run. The executor calls `ToolExecutor.execute` (verify, run, report) with its own `AgentAuthorizer`. If an administrator kills the run, verify and tool calls fail with `RunKilled` (a `ToolDenied`), the SDK drops its cached tokens, and the next call starts a new run. A report for an already consumed receipt is still accepted after a kill and is recorded as `after_kill`. A run that only ended (idle) is replaced transparently and logged at INFO on the `aynigma.agents` logger.
|
|
96
|
+
|
|
97
|
+
### Approvals: pause and resume
|
|
98
|
+
|
|
99
|
+
A rule with effect REVIEW raises `ApprovalRequired` until a person approves the call in Agent IAM > Activity. The approval belongs to the run it was raised in, so save `approval_id` and `run_id` when you catch it. If the work continues in another process (a queue worker, a restart), build the new authorizer with that `run_id`. Without it the new process starts a fresh run and the call is denied with `run_mismatch`.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from aynigma import AgentAuthorizer, ApprovalRequired
|
|
103
|
+
|
|
104
|
+
authorizer = AgentAuthorizer(agent_id=AGENT_ID, agent_key=AGENT_KEY, base_url=BASE_URL)
|
|
105
|
+
try:
|
|
106
|
+
authorizer.run_tool(tool, params, handler)
|
|
107
|
+
except ApprovalRequired as pending:
|
|
108
|
+
saved_approval_id, saved_run_id = pending.approval_id, pending.run_id # store these with your task
|
|
109
|
+
|
|
110
|
+
# Later, after a reviewer approved it, possibly in a new process:
|
|
111
|
+
resumed = AgentAuthorizer(agent_id=AGENT_ID, agent_key=AGENT_KEY, base_url=BASE_URL, run_id=saved_run_id)
|
|
112
|
+
resumed.run_tool(tool, params, handler, approval_id=saved_approval_id) # same tool, same params
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`run_id` is None with the legacy `X-AGENT-KEY` header, which has no runs and needs no `run_id` to resume. If you pass your own `token_provider`, give that `AgentTokenProvider` the `run_id` instead; passing `run_id` to `AgentAuthorizer` as well raises `ValueError`. The arguments must match what was approved, or the call is denied with `payload_mismatch`.
|
|
116
|
+
|
|
117
|
+
Denials explain themselves: `str(ToolDenied)` starts with `Agent tool call denied: <code>` and adds a short hint for common codes (`no_policy`, `run_mismatch`, `payload_mismatch`, `approval_already_used`, `approval_expired`, `environment`, `rate_limited`). `error.reason` is the bare code and `error.hint` the hint, or None.
|
|
118
|
+
|
|
119
|
+
Debug a token from a shell: `AYNIGMA_AGENT_KEY=agt_... aynigma-agents-token --agent-id <uuid> --claims`. The key is never printed.
|
|
120
|
+
|
|
121
|
+
## Verify an audit export offline
|
|
122
|
+
|
|
123
|
+
Auditors can check an audit export and its signed anchor without trusting the API. Standard library only.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
aynigma-audit-verify --export audit.json --anchor anchor.json \
|
|
127
|
+
--jwks https://api.aynigma.ai/api/v1/agents/.well-known/jwks.json \
|
|
128
|
+
--chain-key-file chain.key --full-chain
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`audit.json` is `GET /api/v1/audit/export/?log_type=audit&export_format=json` (an object with a `chain` header and `records`) and `anchor.json` is `GET /api/v1/audit/anchors/<id>/`. With the chain key (`--chain-key-file`, or `AYNIGMA_AUDIT_CHAIN_KEY`; `--chain-key` works but shows in the process list) every row is recomputed; without it only legacy rows are, and the report lists what was skipped. Legacy (v1) rows need no key, so they are trusted only up to the `last_v1_entry_id` signed inside a verified anchor (the export header's value is unsigned and never grants trust); a v1 row above it, or after a keyed row, is a forgery and fails. An export of only legacy rows with no verified anchor is INCONCLUSIVE. `--full-chain` means the export is the unfiltered chain, so each row must follow its predecessor.
|
|
132
|
+
|
|
133
|
+
Exit status: 0 pass, 1 a check failed, 2 inconclusive, 3 unusable input. PASS needs every row recomputed with the key, or an anchor whose head is in the export with an unbroken chain back from it (printed as "structure only", since v2 contents are not recomputed without the key). With neither the result is INCONCLUSIVE, never a pass. The hashing rules are in `docs/AUDIT_CHAIN.md` in the backend repo.
|
|
134
|
+
|
|
135
|
+
Migrating from `X-AGENT-KEY`: nothing breaks. A key with no agent ID keeps sending the header, and `use_legacy_key=True` (or `AYNIGMA_USE_LEGACY_KEY=1`) keeps it even when an agent ID is set. Since 0.3.0, `AgentAuthorizer` switches to tokens as soon as it has both an agent ID and a key. An explicit `agent_key=` with no `agent_id=` stays legacy, and `AYNIGMA_AGENT_ID` from the environment counts only together with `AYNIGMA_AGENT_KEY` from the environment. To move over, pass `agent_id=` or set both variables, and watch the `agent.legacy_key_used` audit event stop. `acting_for_user` and `initiator_token_provider` now exchange the user JWT instead of forwarding it; with the legacy header they forward it as before. Delegated tokens carry the delegation depth counted from the human (a direct human to agent hop is depth 1), so review `max_delegation_depth` conditions. See `docs/AGENT_TOOL_EXECUTOR.md` and `docs/AGENT_IDENTITY.md`.
|
|
136
|
+
|
|
137
|
+
Beyond the quickstart, use `AgentAuthorizer` and `RemoteTool` with a separately deployed receipt-enforcing executor. Optional adapters are `as_langchain_tool`, `as_openai_agents_tool`, `as_crewai_tool`, `as_claude_agent_tool`, `as_google_adk_tool`, and `as_semantic_kernel_function`; install their corresponding extras and pass a Pydantic argument model. `OpenAIAgentsApprovalBridge` uses the OpenAI Agents SDK's native approval interruption to preserve the exact model-selected call through backend REVIEW and receipt-backed resume. `build_crewai_approval_flow` uses CrewAI Flow human feedback and explicit persistence for the same handoff. `ClaudeAgentApprovalBridge` pairs the Claude Agent SDK's native `PreToolUse` defer hook with a receipt-backed MCP tool and caller-owned pending state. `SemanticKernelApprovalBridge` intercepts Semantic Kernel auto function invocation before a model-selected call and supports exact-call REVIEW resume through a caller-owned checkpoint. `as_google_adk_approval_tool` uses ADK's native confirmation event and persisted session state. Install `aynigma[google-adk-postgres]` for ADK's PostgreSQL session service; a local restart contract passed against a disposable PostgreSQL database. `build_langgraph_approval_workflow` adds a checkpointed pause/resume graph for REVIEW decisions and requires an explicit checkpointer. Install `aynigma[langgraph-postgres]` and use LangGraph's `PostgresSaver` for durable resume. See `docs/AGENT_TOOL_EXECUTOR.md` in the backend repository for the executor boundary and examples.
|
|
138
|
+
|
|
139
|
+
`PostgresApprovalCheckpointStore` is an optional encrypted store for serializable pending approval state shared across workers. Install `aynigma[approval-postgres]`, call `setup()` once, then `save()` and one-use `claim()` with authenticated tenant and agent UUIDs. Schedule `aynigma-approval-checkpoints-purge` daily with `AYNIGMA_APPROVAL_CHECKPOINT_DATABASE_URL` supplied from the operator's secret store; it prints only the number purged. The application still serializes framework-specific session state and authorizes its resume endpoint. See the executor guide for retention and recovery rules.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from aynigma import Aynigma, guard_openai_chat_v2
|
|
143
|
+
|
|
144
|
+
with Aynigma() as client:
|
|
145
|
+
response = guard_openai_chat_v2(
|
|
146
|
+
client,
|
|
147
|
+
openai_client,
|
|
148
|
+
messages,
|
|
149
|
+
model="gpt-4o-mini",
|
|
150
|
+
guard_options={"system_id": 4, "environment_id": 12},
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
# Bedrock agent inventory
|
|
154
|
+
|
|
155
|
+
On a paid plan, run `aynigma-agents-discover-cloud --company-id 42 --account-id 123456789012 --region us-east-1` with `AYNIGMA_USER_JWT`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and optional `AWS_SESSION_TOKEN` set. The command sends the credentials once and prints only the bounded agent inventory. The backend checks the AWS account through STS before `ListAgents` and does not retain the credentials. See `docs/AGENT_BEDROCK_DISCOVERY.md` for scope and limits.
|
|
156
|
+
|
|
157
|
+
For Vertex Agent Engine, set `AYNIGMA_USER_JWT` and `GOOGLE_ACCESS_TOKEN`, then run `aynigma-agents-discover-vertex --company-id 42 --project-id sample-project --region us-central1`. The backend confirms the active Google Cloud project before listing reasoning engines. See `docs/AGENT_VERTEX_DISCOVERY.md`.
|
|
158
|
+
|
|
159
|
+
For a known Azure AI Foundry project, set `AYNIGMA_USER_JWT` and `AZURE_ACCESS_TOKEN`, then run `aynigma-agents-discover-azure --company-id 42 --project-endpoint https://account.services.ai.azure.com/api/projects/project`. See `docs/AGENT_AZURE_DISCOVERY.md`.
|
|
160
|
+
|
|
161
|
+
To find Foundry projects in a subscription first, set a separate `AZURE_ARM_ACCESS_TOKEN` and run `aynigma-agents-discover-azure --company-id 42 --subscription-id 11111111-2222-4333-8444-555555555555`. This lists bounded project metadata only; use a project token and endpoint to list agents.
|
|
162
|
+
|
|
163
|
+
For aggregate native Assess plugin review coverage, run `aynigma-assess-plugin-performance --project-id 42 --days 90` with `AYNIGMA_USER_JWT`. It returns counts only; see `docs/ASSESS_PLUGIN_PERFORMANCE.md` for interpretation.
|
|
164
|
+
|
|
165
|
+
When registering an agent through the dashboard API, an administrator may provide a same-company `project` ID to attribute approvals and incidents to that project's compliance evidence. The project cannot be changed after registration. See `docs/AGENT_COMPLIANCE_EVIDENCE.md`.
|
|
166
|
+
|
|
167
|
+
Administrators can read registered-agent slot usage at `GET /api/v1/agents/usage/?company=<id>`. Free includes two slots; current paid caps are documented in `docs/AGENT_PLAN_LIMITS.md`.
|
|
168
|
+
|
|
169
|
+
For agent tools acting on behalf of an authenticated person, wrap the tool invocation in `with acting_for_user(user_jwt):` after importing `acting_for_user` from `aynigma`. The binding is scoped to the current Python task and carries through async framework adapters; see `docs/AGENT_TOOL_EXECUTOR.md`.
|
|
170
|
+
|
|
171
|
+
## Receipts are required
|
|
172
|
+
|
|
173
|
+
Run in-process tools with `auth.run_tool("ticket.create", {"title": "x"}, handler)` or `RemoteTool.local(auth, "ticket.create", handler)`. Both authorize, verify the one-use receipt, run the handler and report SUCCEEDED or FAILED. `ToolExecutor` refuses a call without a receipt unless you pass `require_receipt=False`, which is only for a backend running with `AGENT_REQUIRE_EXECUTION_RECEIPT=False`. See `docs/AGENT_TOOL_EXECUTOR.md`.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling==1.27.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "aynigma"
|
|
7
|
+
version = "0.3.1"
|
|
8
|
+
description = "Python SDK for Aynigma: IAM for AI agents and AI security guardrails"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = ["httpx>=0.25"]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
aynigma-assess = "aynigma.assess:main"
|
|
15
|
+
aynigma-assess-plugin-performance = "aynigma.assess_plugins:main"
|
|
16
|
+
aynigma-agents-token = "aynigma.agent_tokens:main"
|
|
17
|
+
aynigma-agents-discover = "aynigma.discover:main"
|
|
18
|
+
aynigma-agents-discover-refresh = "aynigma.discover_refresh:main"
|
|
19
|
+
aynigma-agents-mcp-oauth = "aynigma.mcp_oauth:main"
|
|
20
|
+
aynigma-agents-discover-cloud = "aynigma.discover_cloud:main"
|
|
21
|
+
aynigma-agents-discover-vertex = "aynigma.discover_vertex:main"
|
|
22
|
+
aynigma-agents-discover-azure = "aynigma.discover_azure:main"
|
|
23
|
+
aynigma-approval-checkpoints-purge = "aynigma.approval_checkpoints:main"
|
|
24
|
+
aynigma-audit-verify = "aynigma.audit_verify:main"
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
dev = ["pytest>=7"]
|
|
28
|
+
langchain = ["langchain-core>=0.3,<1", "pydantic>=2.7,<3"]
|
|
29
|
+
langgraph = ["langgraph>=1.2,<1.3", "pydantic>=2.7,<3"]
|
|
30
|
+
langgraph-postgres = ["langgraph>=1.2,<1.3", "langgraph-checkpoint-postgres>=3.1,<3.2", "psycopg[binary]>=3.2,<4", "pydantic>=2.7,<3"]
|
|
31
|
+
openai-agents = ["openai-agents>=0.22,<0.23", "pydantic>=2.7,<3"]
|
|
32
|
+
crewai = ["crewai>=1.15,<1.16", "pydantic>=2.7,<3"]
|
|
33
|
+
claude-agent = ["claude-agent-sdk>=0.2.160,<0.3", "pydantic>=2.7,<3"]
|
|
34
|
+
google-adk = ["google-adk>=1.39,<1.40", "pydantic>=2.7,<3"]
|
|
35
|
+
google-adk-postgres = ["google-adk>=1.39,<1.40", "sqlalchemy[asyncio]>=2,<3", "asyncpg>=0.30,<1", "psycopg[binary]>=3.2,<4", "pydantic>=2.7,<3"]
|
|
36
|
+
approval-postgres = ["psycopg[binary]>=3.2,<4", "cryptography>=42,<51"]
|
|
37
|
+
semantic-kernel = ["semantic-kernel>=1.44,<1.45", "pydantic>=2.7,<3"]
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/aynigma"]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from .client import (
|
|
2
|
+
APIError,
|
|
3
|
+
AsyncAynigma,
|
|
4
|
+
AuthenticationError,
|
|
5
|
+
Aynigma,
|
|
6
|
+
AynigmaError,
|
|
7
|
+
BlockedError,
|
|
8
|
+
QuotaExceededError,
|
|
9
|
+
ValidationResult,
|
|
10
|
+
aguard_openai_chat,
|
|
11
|
+
aguard_openai_chat_v2,
|
|
12
|
+
guard_openai_chat,
|
|
13
|
+
guard_openai_chat_v2,
|
|
14
|
+
)
|
|
15
|
+
from .agent_errors import MCPError, TokenError
|
|
16
|
+
from .agent_tokens import AgentTokenProvider, DelegatedToken
|
|
17
|
+
from .agents import AgentAuthorizer, ApprovalRequired, RunKilled, ClaudeAgentApprovalBridge, ExecutionReceipt, RemoteTool, ToolDenied, ToolExecutor, OpenAIAgentsApprovalBridge, SemanticKernelApprovalBridge, acting_for_user, as_langchain_tool, build_langgraph_approval_workflow, build_crewai_approval_flow, as_openai_agents_tool, as_crewai_tool, as_claude_agent_tool, as_google_adk_tool, as_google_adk_approval_tool, as_semantic_kernel_function
|
|
18
|
+
from .approval_checkpoints import PostgresApprovalCheckpointStore
|
|
19
|
+
|
|
20
|
+
async_guard_openai_chat = aguard_openai_chat
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"APIError",
|
|
24
|
+
"AgentAuthorizer",
|
|
25
|
+
"AgentTokenProvider",
|
|
26
|
+
"DelegatedToken",
|
|
27
|
+
"RunKilled",
|
|
28
|
+
"TokenError",
|
|
29
|
+
"MCPError",
|
|
30
|
+
"ApprovalRequired",
|
|
31
|
+
"ClaudeAgentApprovalBridge",
|
|
32
|
+
"AsyncAynigma",
|
|
33
|
+
"AuthenticationError",
|
|
34
|
+
"Aynigma",
|
|
35
|
+
"AynigmaError",
|
|
36
|
+
"BlockedError",
|
|
37
|
+
"ExecutionReceipt",
|
|
38
|
+
"OpenAIAgentsApprovalBridge",
|
|
39
|
+
"PostgresApprovalCheckpointStore",
|
|
40
|
+
"SemanticKernelApprovalBridge",
|
|
41
|
+
"QuotaExceededError",
|
|
42
|
+
"RemoteTool",
|
|
43
|
+
"ToolDenied",
|
|
44
|
+
"ToolExecutor",
|
|
45
|
+
"acting_for_user",
|
|
46
|
+
"as_langchain_tool",
|
|
47
|
+
"build_langgraph_approval_workflow",
|
|
48
|
+
"build_crewai_approval_flow",
|
|
49
|
+
"as_openai_agents_tool",
|
|
50
|
+
"as_crewai_tool",
|
|
51
|
+
"as_claude_agent_tool",
|
|
52
|
+
"as_google_adk_tool",
|
|
53
|
+
"as_google_adk_approval_tool",
|
|
54
|
+
"as_semantic_kernel_function",
|
|
55
|
+
"ValidationResult",
|
|
56
|
+
"aguard_openai_chat",
|
|
57
|
+
"aguard_openai_chat_v2",
|
|
58
|
+
"async_guard_openai_chat",
|
|
59
|
+
"guard_openai_chat",
|
|
60
|
+
"guard_openai_chat_v2",
|
|
61
|
+
]
|