edictum 0.4.0__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.
Files changed (119) hide show
  1. edictum-0.4.0/.github/dependabot.yml +6 -0
  2. edictum-0.4.0/.github/workflows/ci.yml +34 -0
  3. edictum-0.4.0/.github/workflows/docs.yml +38 -0
  4. edictum-0.4.0/.github/workflows/pr-title.yml +24 -0
  5. edictum-0.4.0/.github/workflows/publish.yml +17 -0
  6. edictum-0.4.0/.gitignore +18 -0
  7. edictum-0.4.0/.pre-commit-config.yaml +7 -0
  8. edictum-0.4.0/ARCHITECTURE.md +114 -0
  9. edictum-0.4.0/LICENSE +21 -0
  10. edictum-0.4.0/PKG-INFO +180 -0
  11. edictum-0.4.0/README.md +123 -0
  12. edictum-0.4.0/REVIEW.md +450 -0
  13. edictum-0.4.0/docs/adapters/claude-sdk.md +196 -0
  14. edictum-0.4.0/docs/adapters/crewai.md +118 -0
  15. edictum-0.4.0/docs/adapters/langchain.md +189 -0
  16. edictum-0.4.0/docs/adapters/others.md +342 -0
  17. edictum-0.4.0/docs/adapters/overview.md +144 -0
  18. edictum-0.4.0/docs/adapters.md +201 -0
  19. edictum-0.4.0/docs/architecture.md +259 -0
  20. edictum-0.4.0/docs/audit/sinks.md +397 -0
  21. edictum-0.4.0/docs/audit/telemetry.md +176 -0
  22. edictum-0.4.0/docs/cli.md +197 -0
  23. edictum-0.4.0/docs/compliance.md +116 -0
  24. edictum-0.4.0/docs/contracts/operators.md +427 -0
  25. edictum-0.4.0/docs/contracts/templates.md +324 -0
  26. edictum-0.4.0/docs/contracts/yaml-reference.md +446 -0
  27. edictum-0.4.0/docs/index.md +87 -0
  28. edictum-0.4.0/docs/quickstart.md +384 -0
  29. edictum-0.4.0/examples/README.md +16 -0
  30. edictum-0.4.0/examples/contract_cookbook.py +1036 -0
  31. edictum-0.4.0/examples/contracts.py +76 -0
  32. edictum-0.4.0/examples/demo_agno.py +220 -0
  33. edictum-0.4.0/examples/demo_claude_agent_sdk.py +217 -0
  34. edictum-0.4.0/examples/demo_crewai.py +217 -0
  35. edictum-0.4.0/examples/demo_langchain.py +207 -0
  36. edictum-0.4.0/examples/demo_openai_agents.py +210 -0
  37. edictum-0.4.0/examples/demo_semantic_kernel.py +211 -0
  38. edictum-0.4.0/examples/otel_config.py +20 -0
  39. edictum-0.4.0/examples/setup.sh +42 -0
  40. edictum-0.4.0/examples/tools.py +201 -0
  41. edictum-0.4.0/mkdocs.yml +72 -0
  42. edictum-0.4.0/pyproject.toml +58 -0
  43. edictum-0.4.0/schemas/edictum-v1.schema.json +285 -0
  44. edictum-0.4.0/src/edictum/__init__.py +440 -0
  45. edictum-0.4.0/src/edictum/adapters/__init__.py +0 -0
  46. edictum-0.4.0/src/edictum/adapters/agno.py +269 -0
  47. edictum-0.4.0/src/edictum/adapters/claude_agent_sdk.py +224 -0
  48. edictum-0.4.0/src/edictum/adapters/crewai.py +237 -0
  49. edictum-0.4.0/src/edictum/adapters/langchain.py +247 -0
  50. edictum-0.4.0/src/edictum/adapters/openai_agents.py +266 -0
  51. edictum-0.4.0/src/edictum/adapters/semantic_kernel.py +239 -0
  52. edictum-0.4.0/src/edictum/audit.py +218 -0
  53. edictum-0.4.0/src/edictum/builtins.py +72 -0
  54. edictum-0.4.0/src/edictum/cli/__init__.py +0 -0
  55. edictum-0.4.0/src/edictum/cli/main.py +413 -0
  56. edictum-0.4.0/src/edictum/contracts.py +75 -0
  57. edictum-0.4.0/src/edictum/envelope.py +240 -0
  58. edictum-0.4.0/src/edictum/hooks.py +27 -0
  59. edictum-0.4.0/src/edictum/limits.py +21 -0
  60. edictum-0.4.0/src/edictum/pipeline.py +278 -0
  61. edictum-0.4.0/src/edictum/py.typed +0 -0
  62. edictum-0.4.0/src/edictum/session.py +52 -0
  63. edictum-0.4.0/src/edictum/sinks/__init__.py +13 -0
  64. edictum-0.4.0/src/edictum/sinks/_base.py +99 -0
  65. edictum-0.4.0/src/edictum/sinks/datadog.py +77 -0
  66. edictum-0.4.0/src/edictum/sinks/splunk.py +68 -0
  67. edictum-0.4.0/src/edictum/sinks/webhook.py +59 -0
  68. edictum-0.4.0/src/edictum/storage.py +53 -0
  69. edictum-0.4.0/src/edictum/telemetry.py +85 -0
  70. edictum-0.4.0/src/edictum/types.py +25 -0
  71. edictum-0.4.0/src/edictum/yaml_engine/__init__.py +31 -0
  72. edictum-0.4.0/src/edictum/yaml_engine/compiler.py +287 -0
  73. edictum-0.4.0/src/edictum/yaml_engine/edictum-v1.schema.json +285 -0
  74. edictum-0.4.0/src/edictum/yaml_engine/evaluator.py +303 -0
  75. edictum-0.4.0/src/edictum/yaml_engine/loader.py +192 -0
  76. edictum-0.4.0/src/edictum/yaml_engine/templates/devops-agent.yaml +80 -0
  77. edictum-0.4.0/src/edictum/yaml_engine/templates/file-agent.yaml +45 -0
  78. edictum-0.4.0/src/edictum/yaml_engine/templates/research-agent.yaml +43 -0
  79. edictum-0.4.0/tests/__init__.py +0 -0
  80. edictum-0.4.0/tests/conftest.py +58 -0
  81. edictum-0.4.0/tests/test_adapter_agno.py +192 -0
  82. edictum-0.4.0/tests/test_adapter_claude.py +258 -0
  83. edictum-0.4.0/tests/test_adapter_crewai.py +156 -0
  84. edictum-0.4.0/tests/test_adapter_langchain.py +193 -0
  85. edictum-0.4.0/tests/test_adapter_openai_agents.py +154 -0
  86. edictum-0.4.0/tests/test_adapter_semantic_kernel.py +142 -0
  87. edictum-0.4.0/tests/test_audit.py +291 -0
  88. edictum-0.4.0/tests/test_builtins.py +118 -0
  89. edictum-0.4.0/tests/test_cli/__init__.py +0 -0
  90. edictum-0.4.0/tests/test_cli/test_cli.py +901 -0
  91. edictum-0.4.0/tests/test_contract_cookbook.py +759 -0
  92. edictum-0.4.0/tests/test_contracts.py +80 -0
  93. edictum-0.4.0/tests/test_envelope.py +172 -0
  94. edictum-0.4.0/tests/test_hooks.py +41 -0
  95. edictum-0.4.0/tests/test_integration/__init__.py +0 -0
  96. edictum-0.4.0/tests/test_integration/test_end_to_end.py +1180 -0
  97. edictum-0.4.0/tests/test_limits.py +27 -0
  98. edictum-0.4.0/tests/test_pipeline.py +323 -0
  99. edictum-0.4.0/tests/test_principal.py +244 -0
  100. edictum-0.4.0/tests/test_review_fixes.py +633 -0
  101. edictum-0.4.0/tests/test_session.py +81 -0
  102. edictum-0.4.0/tests/test_sinks/__init__.py +0 -0
  103. edictum-0.4.0/tests/test_sinks/test_datadog.py +154 -0
  104. edictum-0.4.0/tests/test_sinks/test_splunk.py +145 -0
  105. edictum-0.4.0/tests/test_sinks/test_webhook.py +175 -0
  106. edictum-0.4.0/tests/test_storage.py +72 -0
  107. edictum-0.4.0/tests/test_telemetry.py +55 -0
  108. edictum-0.4.0/tests/test_yaml_engine/__init__.py +0 -0
  109. edictum-0.4.0/tests/test_yaml_engine/fixtures/invalid_bad_effect.yaml +19 -0
  110. edictum-0.4.0/tests/test_yaml_engine/fixtures/invalid_bad_regex.yaml +19 -0
  111. edictum-0.4.0/tests/test_yaml_engine/fixtures/invalid_duplicate_ids.yaml +29 -0
  112. edictum-0.4.0/tests/test_yaml_engine/fixtures/invalid_empty_contracts.yaml +10 -0
  113. edictum-0.4.0/tests/test_yaml_engine/fixtures/invalid_missing_apiversion.yaml +18 -0
  114. edictum-0.4.0/tests/test_yaml_engine/fixtures/invalid_output_text_in_pre.yaml +19 -0
  115. edictum-0.4.0/tests/test_yaml_engine/fixtures/valid_bundle.yaml +42 -0
  116. edictum-0.4.0/tests/test_yaml_engine/test_compiler.py +308 -0
  117. edictum-0.4.0/tests/test_yaml_engine/test_evaluator.py +422 -0
  118. edictum-0.4.0/tests/test_yaml_engine/test_integration.py +217 -0
  119. edictum-0.4.0/tests/test_yaml_engine/test_loader.py +118 -0
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
@@ -0,0 +1,34 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ python-version: ["3.11", "3.12", "3.13"]
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ - run: pip install -e ".[dev,yaml,sinks,cli]"
24
+ - run: pytest tests/ -v
25
+
26
+ lint:
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: actions/setup-python@v5
31
+ with:
32
+ python-version: "3.11"
33
+ - run: pip install ruff
34
+ - run: ruff check src/ tests/
@@ -0,0 +1,38 @@
1
+ name: Deploy docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: read
9
+ pages: write
10
+ id-token: write
11
+
12
+ concurrency:
13
+ group: pages
14
+ cancel-in-progress: false
15
+
16
+ jobs:
17
+ build:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+ - run: pip install mkdocs-material
25
+ - run: mkdocs build --strict
26
+ - uses: actions/upload-pages-artifact@v3
27
+ with:
28
+ path: site
29
+
30
+ deploy:
31
+ needs: build
32
+ runs-on: ubuntu-latest
33
+ environment:
34
+ name: github-pages
35
+ url: ${{ steps.deployment.outputs.page_url }}
36
+ steps:
37
+ - id: deployment
38
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,24 @@
1
+ name: PR Title
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, edited, synchronize]
6
+
7
+ permissions:
8
+ pull-requests: read
9
+
10
+ jobs:
11
+ validate:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: amannn/action-semantic-pull-request@v5
15
+ with:
16
+ types: |
17
+ feat
18
+ fix
19
+ docs
20
+ test
21
+ refactor
22
+ chore
23
+ env:
24
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,17 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v5
16
+ - run: uv build
17
+ - run: uv publish
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ site/
8
+ .coverage
9
+ .pytest_cache/
10
+ *.egg
11
+
12
+ # Claude Code (personal config)
13
+ CLAUDE.local
14
+ .claude/
15
+
16
+ # Internal research notes
17
+ docs/ADAPTER_INSIGHTS.md
18
+ docs/adapter-research.md
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.8.6
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -0,0 +1,114 @@
1
+ # Edictum Architecture
2
+
3
+ > Runtime safety for AI agents. Stop agents before they break things.
4
+
5
+ ## What It Does
6
+
7
+ Edictum sits between an agent's decision to call a tool and actual execution. It enforces contracts, hooks, audit trails, and operation limits. When a rule is violated, it tells the agent **why** so it can self-correct.
8
+
9
+ ## Package Structure
10
+
11
+ ```
12
+ src/edictum/
13
+ ├── __init__.py # Edictum class, guard.run(), exceptions, re-exports
14
+ ├── envelope.py # ToolEnvelope (frozen), SideEffect, ToolRegistry, BashClassifier
15
+ ├── hooks.py # HookDecision (ALLOW/DENY)
16
+ ├── contracts.py # Verdict, @precondition, @postcondition, @session_contract
17
+ ├── limits.py # OperationLimits (attempt + execution + per-tool caps)
18
+ ├── pipeline.py # GovernancePipeline — single source of governance logic
19
+ ├── session.py # Session (atomic counters via StorageBackend)
20
+ ├── storage.py # StorageBackend protocol + MemoryBackend
21
+ ├── audit.py # AuditEvent, RedactionPolicy, Stdout/File sinks
22
+ ├── telemetry.py # OpenTelemetry (graceful no-op if absent)
23
+ ├── builtins.py # deny_sensitive_reads()
24
+ ├── types.py # Internal types (HookRegistration, ToolConfig)
25
+ └── adapters/
26
+ ├── langchain.py # LangChain adapter (pre/post tool call hooks)
27
+ ├── crewai.py # CrewAI adapter (before/after hooks)
28
+ ├── agno.py # Agno adapter (wrap-around hook)
29
+ ├── semantic_kernel.py # Semantic Kernel adapter (filter pattern)
30
+ ├── openai_agents.py # OpenAI Agents SDK adapter (guardrails)
31
+ └── claude_agent_sdk.py # Claude Agent SDK adapter (hook dict)
32
+ ```
33
+
34
+ ## The Flow
35
+
36
+ Every tool call passes through:
37
+
38
+ ```
39
+ Agent decides to call tool
40
+
41
+
42
+ Adapter creates ToolEnvelope (deep-copied, classified)
43
+ Increments attempt_count (BEFORE governance)
44
+
45
+
46
+ Pipeline.pre_execute() — 5 steps:
47
+ 1. Attempt limit (>= max_attempts?)
48
+ 2. Before hooks (user-defined, can DENY)
49
+ 3. Preconditions (contract checks, can DENY)
50
+ 4. Session contracts (cross-turn state, can DENY)
51
+ 5. Execution limits (>= max_tool_calls? per-tool?)
52
+
53
+ ├── DENY → audit event → tell agent why → agent self-corrects
54
+
55
+ └── ALLOW → tool executes
56
+
57
+
58
+ Pipeline.post_execute():
59
+ 1. Postconditions (observe-only, warnings)
60
+ 2. After hooks
61
+ 3. Session record (exec count, consecutive failures)
62
+
63
+
64
+ Audit event (CALL_EXECUTED or CALL_FAILED)
65
+ ```
66
+
67
+ ## Key Design Decisions
68
+
69
+ **Pipeline owns ALL governance logic.** Adapters are thin translation layers. Adding a second adapter doesn't fork governance behavior.
70
+
71
+ **Two counter types:**
72
+ - `max_attempts` — caps ALL PreToolUse events (including denied). Catches denial loops.
73
+ - `max_tool_calls` — caps executions only (PostToolUse). Caps total work done.
74
+
75
+ **Postconditions are observe-only.** They emit warnings, never block. For pure/read tools: suggest retry. For write/irreversible: warn only.
76
+
77
+ **Observe mode** (`mode="observe"`): full pipeline runs, audit emits `CALL_WOULD_DENY`, but tool executes anyway. For shadow deployment.
78
+
79
+ **Zero runtime deps.** OpenTelemetry via optional `edictum[otel]`.
80
+
81
+ **Redaction at write time.** Destructive by design — no recovery. Sensitive keys, secret value patterns (OpenAI/AWS/JWT/GitHub/Slack), 32KB payload cap.
82
+
83
+ **BashClassifier is a heuristic, not a security boundary.** Conservative READ allowlist + shell operator detection. Defense in depth with `deny_sensitive_reads()`.
84
+
85
+ ## Usage Modes
86
+
87
+ **1. Framework-agnostic (`guard.run()`):**
88
+ ```python
89
+ guard = Edictum(contracts=[deny_sensitive_reads()])
90
+ result = await guard.run("Bash", {"command": "ls"}, my_bash_fn)
91
+ ```
92
+
93
+ **2. Framework adapters (6 supported):**
94
+
95
+ All adapters are thin translation layers — governance logic stays in the pipeline.
96
+
97
+ ```python
98
+ from edictum.adapters.langchain import LangChainAdapter
99
+ from edictum.adapters.crewai import CrewAIAdapter
100
+ from edictum.adapters.agno import AgnoAdapter
101
+ from edictum.adapters.semantic_kernel import SemanticKernelAdapter
102
+ from edictum.adapters.openai_agents import OpenAIAgentsAdapter
103
+ from edictum.adapters.claude_agent_sdk import ClaudeAgentSDKAdapter
104
+
105
+ adapter = LangChainAdapter(guard, session_id="session-1")
106
+ ```
107
+
108
+ ## What This Is NOT
109
+
110
+ - Not prompt injection defense
111
+ - Not content safety filtering
112
+ - Not network egress control
113
+ - Not a security boundary for Bash
114
+ - Not concurrency-safe across workers (MemoryBackend is single-process)
edictum-0.4.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arnold Cartagena
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
edictum-0.4.0/PKG-INFO ADDED
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: edictum
3
+ Version: 0.4.0
4
+ Summary: Runtime safety for AI agents. Stop agents before they break things.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Requires-Python: >=3.11
15
+ Provides-Extra: agno
16
+ Requires-Dist: agno>=1.0; extra == 'agno'
17
+ Provides-Extra: all
18
+ Requires-Dist: agno>=1.0; extra == 'all'
19
+ Requires-Dist: aiohttp>=3.9; extra == 'all'
20
+ Requires-Dist: click>=8.0; extra == 'all'
21
+ Requires-Dist: crewai>=0.80; extra == 'all'
22
+ Requires-Dist: jsonschema>=4.20; extra == 'all'
23
+ Requires-Dist: langchain-core>=0.3; extra == 'all'
24
+ Requires-Dist: openai-agents>=0.1; extra == 'all'
25
+ Requires-Dist: opentelemetry-api>=1.20; extra == 'all'
26
+ Requires-Dist: opentelemetry-sdk>=1.20; extra == 'all'
27
+ Requires-Dist: pyyaml>=6.0; extra == 'all'
28
+ Requires-Dist: rich>=13.0; extra == 'all'
29
+ Requires-Dist: semantic-kernel>=1.0; extra == 'all'
30
+ Provides-Extra: cli
31
+ Requires-Dist: click>=8.0; extra == 'cli'
32
+ Requires-Dist: jsonschema>=4.20; extra == 'cli'
33
+ Requires-Dist: pyyaml>=6.0; extra == 'cli'
34
+ Requires-Dist: rich>=13.0; extra == 'cli'
35
+ Provides-Extra: crewai
36
+ Requires-Dist: crewai>=0.80; extra == 'crewai'
37
+ Provides-Extra: dev
38
+ Requires-Dist: coverage>=7.0; extra == 'dev'
39
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
40
+ Requires-Dist: pytest>=8.0; extra == 'dev'
41
+ Requires-Dist: ruff>=0.4; extra == 'dev'
42
+ Provides-Extra: langchain
43
+ Requires-Dist: langchain-core>=0.3; extra == 'langchain'
44
+ Provides-Extra: openai-agents
45
+ Requires-Dist: openai-agents>=0.1; extra == 'openai-agents'
46
+ Provides-Extra: otel
47
+ Requires-Dist: opentelemetry-api>=1.20; extra == 'otel'
48
+ Requires-Dist: opentelemetry-sdk>=1.20; extra == 'otel'
49
+ Provides-Extra: semantic-kernel
50
+ Requires-Dist: semantic-kernel>=1.0; extra == 'semantic-kernel'
51
+ Provides-Extra: sinks
52
+ Requires-Dist: aiohttp>=3.9; extra == 'sinks'
53
+ Provides-Extra: yaml
54
+ Requires-Dist: jsonschema>=4.20; extra == 'yaml'
55
+ Requires-Dist: pyyaml>=6.0; extra == 'yaml'
56
+ Description-Content-Type: text/markdown
57
+
58
+ # Edictum
59
+
60
+ [![PyPI](https://img.shields.io/pypi/v/edictum)](https://pypi.org/project/edictum/)
61
+ [![License](https://img.shields.io/pypi/l/edictum)](LICENSE)
62
+ [![Python](https://img.shields.io/pypi/pyversions/edictum)](https://pypi.org/project/edictum/)
63
+
64
+ **Runtime contracts for AI agents.**
65
+
66
+ AI agents make tool calls. Tool calls have side effects. Nobody governs what happens between "agent decides" and "tool executes." Edictum is that governance layer — preconditions, postconditions, session limits, and a full audit trail, enforced at the point where decision becomes action.
67
+
68
+ ## Show Me
69
+
70
+ **contracts.yaml**
71
+
72
+ ```yaml
73
+ apiVersion: edictum/v1
74
+ kind: ContractBundle
75
+
76
+ metadata:
77
+ name: my-policy
78
+
79
+ defaults:
80
+ mode: enforce
81
+
82
+ contracts:
83
+ - id: block-sensitive-reads
84
+ type: pre
85
+ tool: read_file
86
+ when:
87
+ args.path:
88
+ contains_any: [".env", ".secret", "credentials", ".pem", "id_rsa"]
89
+ then:
90
+ effect: deny
91
+ message: "Sensitive file '{args.path}' blocked."
92
+ tags: [secrets, dlp]
93
+ ```
94
+
95
+ **Python**
96
+
97
+ ```python
98
+ import asyncio
99
+ from edictum import Edictum, EdictumDenied
100
+
101
+ async def main():
102
+ guard = Edictum.from_yaml("contracts.yaml")
103
+
104
+ try:
105
+ result = await guard.run("read_file", {"path": "/app/config.json"}, read_file_fn)
106
+ print(result)
107
+ except EdictumDenied as e:
108
+ print(f"Denied: {e}")
109
+
110
+ asyncio.run(main())
111
+ ```
112
+
113
+ **CLI**
114
+
115
+ ```bash
116
+ $ edictum validate contracts.yaml
117
+ ✓ contracts.yaml — 1 contract (1 pre)
118
+
119
+ $ edictum check contracts.yaml --tool read_file --args '{"path": ".env"}'
120
+ ⛔ DENIED by block-sensitive-reads
121
+ Message: Sensitive file '.env' blocked.
122
+ Tags: secrets, dlp
123
+ Rules evaluated: 1
124
+ ```
125
+
126
+ **Framework integration (one adapter, same guard)**
127
+
128
+ ```python
129
+ from edictum.adapters.langchain import EdictumMiddleware
130
+
131
+ middleware = EdictumMiddleware(guard)
132
+ # Wraps any LangChain tool — preconditions, audit, and session limits apply automatically
133
+ ```
134
+
135
+ ## Features
136
+
137
+ - **YAML contracts** — Preconditions, postconditions, and session limits declared in version-controlled YAML files
138
+ - **6 framework adapters** — LangChain, CrewAI, Agno, Semantic Kernel, OpenAI Agents SDK, Claude Agent SDK
139
+ - **Audit trail** — Structured JSON events with automatic redaction of secrets (OpenAI keys, AWS creds, JWTs, GitHub tokens)
140
+ - **Observe mode** — Shadow-deploy contracts without blocking; review `CALL_WOULD_DENY` events before enforcing
141
+ - **CLI tooling** — `validate`, `check`, `diff`, and `replay` commands for CI/CD integration
142
+ - **Principal context** — Role, ticket ref, and claims propagated through every decision and audit event
143
+ - **Session limits** — Cap total calls, attempts, and per-tool executions to catch runaway agents
144
+ - **Zero runtime deps** — Pure Python 3.11+. OTel, sinks, and adapters are optional extras
145
+
146
+ ## How It Compares
147
+
148
+ | Approach | Scope | Runtime enforcement | Audit trail |
149
+ |---|---|---|---|
150
+ | Prompt/output guardrails | Input/output text | No — advisory only | No |
151
+ | API gateways / MCP proxies | Network transport | Yes — at the proxy | Partial |
152
+ | Security scanners | Post-hoc analysis | No — detection only | Yes |
153
+ | Manual if-statements | Per-tool, ad hoc | Yes — scattered logic | No |
154
+ | **Edictum** | **Tool call contracts** | **Yes — deterministic pipeline** | **Yes — structured + redacted** |
155
+
156
+ ## Install
157
+
158
+ ```bash
159
+ pip install edictum # core (zero deps)
160
+ pip install edictum[yaml] # + YAML contract engine
161
+ pip install edictum[sinks] # + webhook, Splunk, Datadog sinks
162
+ pip install edictum[cli] # + validate/check/diff/replay CLI
163
+ pip install edictum[all] # everything
164
+ ```
165
+
166
+ ## Built-in Templates
167
+
168
+ ```python
169
+ guard = Edictum.from_template("file-agent") # secret file protection, destructive cmd blocking
170
+ guard = Edictum.from_template("research-agent") # output PII detection, session limits
171
+ guard = Edictum.from_template("devops-agent") # role gates, ticket requirements, bash safety
172
+ ```
173
+
174
+ ## Links
175
+
176
+ - [Documentation](https://acartag7.github.io/edictum/)
177
+ - [GitHub](https://github.com/acartag7/edictum)
178
+ - [PyPI](https://pypi.org/project/edictum/)
179
+ - [Changelog](CHANGELOG.md)
180
+ - [License](LICENSE) (MIT)
@@ -0,0 +1,123 @@
1
+ # Edictum
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/edictum)](https://pypi.org/project/edictum/)
4
+ [![License](https://img.shields.io/pypi/l/edictum)](LICENSE)
5
+ [![Python](https://img.shields.io/pypi/pyversions/edictum)](https://pypi.org/project/edictum/)
6
+
7
+ **Runtime contracts for AI agents.**
8
+
9
+ AI agents make tool calls. Tool calls have side effects. Nobody governs what happens between "agent decides" and "tool executes." Edictum is that governance layer — preconditions, postconditions, session limits, and a full audit trail, enforced at the point where decision becomes action.
10
+
11
+ ## Show Me
12
+
13
+ **contracts.yaml**
14
+
15
+ ```yaml
16
+ apiVersion: edictum/v1
17
+ kind: ContractBundle
18
+
19
+ metadata:
20
+ name: my-policy
21
+
22
+ defaults:
23
+ mode: enforce
24
+
25
+ contracts:
26
+ - id: block-sensitive-reads
27
+ type: pre
28
+ tool: read_file
29
+ when:
30
+ args.path:
31
+ contains_any: [".env", ".secret", "credentials", ".pem", "id_rsa"]
32
+ then:
33
+ effect: deny
34
+ message: "Sensitive file '{args.path}' blocked."
35
+ tags: [secrets, dlp]
36
+ ```
37
+
38
+ **Python**
39
+
40
+ ```python
41
+ import asyncio
42
+ from edictum import Edictum, EdictumDenied
43
+
44
+ async def main():
45
+ guard = Edictum.from_yaml("contracts.yaml")
46
+
47
+ try:
48
+ result = await guard.run("read_file", {"path": "/app/config.json"}, read_file_fn)
49
+ print(result)
50
+ except EdictumDenied as e:
51
+ print(f"Denied: {e}")
52
+
53
+ asyncio.run(main())
54
+ ```
55
+
56
+ **CLI**
57
+
58
+ ```bash
59
+ $ edictum validate contracts.yaml
60
+ ✓ contracts.yaml — 1 contract (1 pre)
61
+
62
+ $ edictum check contracts.yaml --tool read_file --args '{"path": ".env"}'
63
+ ⛔ DENIED by block-sensitive-reads
64
+ Message: Sensitive file '.env' blocked.
65
+ Tags: secrets, dlp
66
+ Rules evaluated: 1
67
+ ```
68
+
69
+ **Framework integration (one adapter, same guard)**
70
+
71
+ ```python
72
+ from edictum.adapters.langchain import EdictumMiddleware
73
+
74
+ middleware = EdictumMiddleware(guard)
75
+ # Wraps any LangChain tool — preconditions, audit, and session limits apply automatically
76
+ ```
77
+
78
+ ## Features
79
+
80
+ - **YAML contracts** — Preconditions, postconditions, and session limits declared in version-controlled YAML files
81
+ - **6 framework adapters** — LangChain, CrewAI, Agno, Semantic Kernel, OpenAI Agents SDK, Claude Agent SDK
82
+ - **Audit trail** — Structured JSON events with automatic redaction of secrets (OpenAI keys, AWS creds, JWTs, GitHub tokens)
83
+ - **Observe mode** — Shadow-deploy contracts without blocking; review `CALL_WOULD_DENY` events before enforcing
84
+ - **CLI tooling** — `validate`, `check`, `diff`, and `replay` commands for CI/CD integration
85
+ - **Principal context** — Role, ticket ref, and claims propagated through every decision and audit event
86
+ - **Session limits** — Cap total calls, attempts, and per-tool executions to catch runaway agents
87
+ - **Zero runtime deps** — Pure Python 3.11+. OTel, sinks, and adapters are optional extras
88
+
89
+ ## How It Compares
90
+
91
+ | Approach | Scope | Runtime enforcement | Audit trail |
92
+ |---|---|---|---|
93
+ | Prompt/output guardrails | Input/output text | No — advisory only | No |
94
+ | API gateways / MCP proxies | Network transport | Yes — at the proxy | Partial |
95
+ | Security scanners | Post-hoc analysis | No — detection only | Yes |
96
+ | Manual if-statements | Per-tool, ad hoc | Yes — scattered logic | No |
97
+ | **Edictum** | **Tool call contracts** | **Yes — deterministic pipeline** | **Yes — structured + redacted** |
98
+
99
+ ## Install
100
+
101
+ ```bash
102
+ pip install edictum # core (zero deps)
103
+ pip install edictum[yaml] # + YAML contract engine
104
+ pip install edictum[sinks] # + webhook, Splunk, Datadog sinks
105
+ pip install edictum[cli] # + validate/check/diff/replay CLI
106
+ pip install edictum[all] # everything
107
+ ```
108
+
109
+ ## Built-in Templates
110
+
111
+ ```python
112
+ guard = Edictum.from_template("file-agent") # secret file protection, destructive cmd blocking
113
+ guard = Edictum.from_template("research-agent") # output PII detection, session limits
114
+ guard = Edictum.from_template("devops-agent") # role gates, ticket requirements, bash safety
115
+ ```
116
+
117
+ ## Links
118
+
119
+ - [Documentation](https://acartag7.github.io/edictum/)
120
+ - [GitHub](https://github.com/acartag7/edictum)
121
+ - [PyPI](https://pypi.org/project/edictum/)
122
+ - [Changelog](CHANGELOG.md)
123
+ - [License](LICENSE) (MIT)