hapax-agentgov 0.2.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.
- hapax_agentgov-0.2.0/.gitignore +76 -0
- hapax_agentgov-0.2.0/LICENSE +21 -0
- hapax_agentgov-0.2.0/PKG-INFO +166 -0
- hapax_agentgov-0.2.0/README.md +135 -0
- hapax_agentgov-0.2.0/pyproject.toml +62 -0
- hapax_agentgov-0.2.0/src/agentgov/__init__.py +104 -0
- hapax_agentgov-0.2.0/src/agentgov/agent_governor.py +129 -0
- hapax_agentgov-0.2.0/src/agentgov/carrier.py +138 -0
- hapax_agentgov-0.2.0/src/agentgov/consent.py +316 -0
- hapax_agentgov-0.2.0/src/agentgov/consent_label.py +56 -0
- hapax_agentgov-0.2.0/src/agentgov/governor.py +142 -0
- hapax_agentgov-0.2.0/src/agentgov/hooks.py +227 -0
- hapax_agentgov-0.2.0/src/agentgov/labeled.py +81 -0
- hapax_agentgov-0.2.0/src/agentgov/primitives.py +150 -0
- hapax_agentgov-0.2.0/src/agentgov/principal.py +69 -0
- hapax_agentgov-0.2.0/src/agentgov/provenance.py +135 -0
- hapax_agentgov-0.2.0/src/agentgov/py.typed +0 -0
- hapax_agentgov-0.2.0/src/agentgov/revocation.py +95 -0
- hapax_agentgov-0.2.0/src/agentgov/says.py +85 -0
- hapax_agentgov-0.2.0/tests/__init__.py +0 -0
- hapax_agentgov-0.2.0/tests/strategies.py +64 -0
- hapax_agentgov-0.2.0/tests/test_carrier.py +71 -0
- hapax_agentgov-0.2.0/tests/test_consent_label.py +88 -0
- hapax_agentgov-0.2.0/tests/test_governor.py +80 -0
- hapax_agentgov-0.2.0/tests/test_hooks.py +87 -0
- hapax_agentgov-0.2.0/tests/test_primitives.py +73 -0
- hapax_agentgov-0.2.0/tests/test_principal.py +103 -0
- hapax_agentgov-0.2.0/tests/test_provenance.py +116 -0
- hapax_agentgov-0.2.0/tests/test_revocation.py +68 -0
- hapax_agentgov-0.2.0/tests/test_says.py +86 -0
- hapax_agentgov-0.2.0/uv.lock +213 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Secrets
|
|
13
|
+
.env
|
|
14
|
+
.envrc
|
|
15
|
+
|
|
16
|
+
# Profile outputs (generated, may contain personal data)
|
|
17
|
+
# Use ** to match subdirectories (engine-audit/, deliberations/, etc.)
|
|
18
|
+
profiles/**/*.json
|
|
19
|
+
profiles/**/*.md
|
|
20
|
+
profiles/**/*.jsonl
|
|
21
|
+
profiles/**/*.yaml
|
|
22
|
+
!profiles/presenter-style.yaml
|
|
23
|
+
!profiles/component-registry.yaml
|
|
24
|
+
!profiles/demo-audiences.yaml
|
|
25
|
+
!profiles/workflow-registry.yaml
|
|
26
|
+
profiles/**/*.bak
|
|
27
|
+
profiles/**/*.db
|
|
28
|
+
profiles/.quarantine/*
|
|
29
|
+
!profiles/.quarantine/.gitkeep
|
|
30
|
+
|
|
31
|
+
# SQLite WAL/SHM files
|
|
32
|
+
*.db-wal
|
|
33
|
+
*.db-shm
|
|
34
|
+
|
|
35
|
+
# Playwright MCP cache
|
|
36
|
+
.playwright-mcp/
|
|
37
|
+
.claude/settings.local.json
|
|
38
|
+
.claude/hookify*.local.md
|
|
39
|
+
.claude/worktrees/
|
|
40
|
+
--Frag*
|
|
41
|
+
|
|
42
|
+
# Demo outputs (generated)
|
|
43
|
+
output/demos/
|
|
44
|
+
.worktrees/
|
|
45
|
+
.venv-ingest/
|
|
46
|
+
|
|
47
|
+
# Wake word training data (large, generated)
|
|
48
|
+
data/wake-word-training/
|
|
49
|
+
.hypothesis/
|
|
50
|
+
plugins/gst-temporalfx/target/
|
|
51
|
+
|
|
52
|
+
# Model weights (large binaries, download on demand)
|
|
53
|
+
*.pt
|
|
54
|
+
*.task
|
|
55
|
+
|
|
56
|
+
# Screenshots / snapshots (generated)
|
|
57
|
+
*.jpeg
|
|
58
|
+
/hapax-visual/
|
|
59
|
+
|
|
60
|
+
# Temp profiles
|
|
61
|
+
profiles/tmp*
|
|
62
|
+
|
|
63
|
+
# Voice experiment session data (contains operator interactions)
|
|
64
|
+
agents/hapax_daimonion/proofs/**/data/
|
|
65
|
+
|
|
66
|
+
# Screenshots (smoke tests, stress tests, explorations)
|
|
67
|
+
*.png
|
|
68
|
+
# Committed test golden images (cairo render regression pins)
|
|
69
|
+
!tests/studio_compositor/golden_images/*.png
|
|
70
|
+
.claude/settings.json
|
|
71
|
+
/plugins/gst-crossfade/target/
|
|
72
|
+
/plugins/gst-smooth-delay/target/
|
|
73
|
+
docs/superpowers/plans/2026-03-29-hapax-obsidian-v2-plan-full.md
|
|
74
|
+
.superpowers/
|
|
75
|
+
gst-plugin-glfeedback/target/
|
|
76
|
+
.compositor-inspect/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryan Lee
|
|
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.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hapax-agentgov
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Computational constitutional governance for AI agent systems
|
|
5
|
+
Project-URL: Homepage, https://github.com/hapax-systems/agentgov
|
|
6
|
+
Project-URL: Documentation, https://github.com/hapax-systems/agentgov#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/hapax-systems/agentgov
|
|
8
|
+
Project-URL: Issues, https://github.com/hapax-systems/agentgov/issues
|
|
9
|
+
Author-email: Ryan Lee <rylklee@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: access-control,agents,consent,governance,information-flow,multi-agent
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: hypothesis>=6.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pyright>=1.1; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# agentgov
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/hapax-agentgov/)
|
|
35
|
+
[](LICENSE)
|
|
36
|
+
|
|
37
|
+
Computational constitutional governance for AI agent systems.
|
|
38
|
+
|
|
39
|
+
agentgov provides algebraically-verified primitives for governing multi-agent systems: consent contracts, information flow control, principal delegation, provenance tracking, and compositional policy enforcement. Zero dependencies beyond PyYAML. Extracted from [hapax-council](https://github.com/hapax-systems/hapax-council), where it governs 200+ AI agents in production.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install hapax-agentgov
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Core Concepts
|
|
48
|
+
|
|
49
|
+
### Principals
|
|
50
|
+
|
|
51
|
+
Actors in the system. Sovereign principals (humans) originate consent; bound principals (agents) operate under delegated authority with non-amplification guarantees.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from agentgov import Principal, PrincipalKind
|
|
55
|
+
|
|
56
|
+
operator = Principal(id="operator", kind=PrincipalKind.SOVEREIGN)
|
|
57
|
+
agent = operator.delegate("sync-agent", frozenset({"email", "calendar"}))
|
|
58
|
+
sub = agent.delegate("sub-agent", frozenset({"email"})) # narrows authority
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Consent Labels (DLM Join-Semilattice)
|
|
62
|
+
|
|
63
|
+
Information flow labels track who may read data. Labels combine via join — combining data with different consent requirements produces the most restrictive combination.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from agentgov import ConsentLabel
|
|
67
|
+
|
|
68
|
+
public = ConsentLabel.bottom() # no restrictions
|
|
69
|
+
restricted = ConsentLabel(frozenset({("alice", frozenset({"bob"}))}))
|
|
70
|
+
combined = public.join(restricted) # most restrictive wins
|
|
71
|
+
assert public.can_flow_to(combined) # less restrictive flows to more
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Labeled Values (LIO-Style)
|
|
75
|
+
|
|
76
|
+
Wrap any value with its consent label and why-provenance.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from agentgov import Labeled, ConsentLabel
|
|
80
|
+
|
|
81
|
+
data = Labeled(value="secret", label=restricted, provenance=frozenset({"contract-1"}))
|
|
82
|
+
transformed = data.map(str.upper) # label preserved through transformations
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Provenance Semirings
|
|
86
|
+
|
|
87
|
+
Track WHY data exists using algebraic provenance (Green et al., PODS 2007). Supports tensor (both required) and plus (either sufficient) composition.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from agentgov import ProvenanceExpr
|
|
91
|
+
|
|
92
|
+
combined = ProvenanceExpr.leaf("c1").tensor(ProvenanceExpr.leaf("c2"))
|
|
93
|
+
assert combined.evaluate(frozenset({"c1", "c2"})) # both active: survives
|
|
94
|
+
assert not combined.evaluate(frozenset({"c1"})) # one revoked: purged
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Governor (Per-Agent Policy Enforcement)
|
|
98
|
+
|
|
99
|
+
Each agent gets a governance wrapper that validates inputs/outputs at boundaries. Pure validation layer — allows or denies, never modifies.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from agentgov import GovernorWrapper, GovernorPolicy, Labeled, ConsentLabel
|
|
103
|
+
|
|
104
|
+
gov = GovernorWrapper("my-agent")
|
|
105
|
+
gov.add_input_policy(GovernorPolicy(
|
|
106
|
+
name="require-consent",
|
|
107
|
+
check=lambda agent_id, data: data.label != ConsentLabel.bottom(),
|
|
108
|
+
axiom_id="consent",
|
|
109
|
+
))
|
|
110
|
+
result = gov.check_input(Labeled(value="data", label=ConsentLabel.bottom()))
|
|
111
|
+
assert not result.allowed
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### VetoChain (Deny-Wins Composition)
|
|
115
|
+
|
|
116
|
+
Order-independent constraint composition. Any denial blocks the action.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from agentgov import VetoChain, Veto
|
|
120
|
+
|
|
121
|
+
chain = VetoChain([
|
|
122
|
+
Veto("budget", lambda ctx: ctx["budget"] > 0),
|
|
123
|
+
Veto("auth", lambda ctx: ctx["authenticated"]),
|
|
124
|
+
])
|
|
125
|
+
result = chain.evaluate({"budget": 100, "authenticated": False})
|
|
126
|
+
assert not result.allowed
|
|
127
|
+
assert "auth" in result.denied_by
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Says Monad (DCC Attribution)
|
|
131
|
+
|
|
132
|
+
Principal-annotated assertions following Abadi's DCC formalism. Threads authority through data transformations.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from agentgov import Says, Principal, PrincipalKind
|
|
136
|
+
|
|
137
|
+
operator = Principal(id="op", kind=PrincipalKind.SOVEREIGN)
|
|
138
|
+
assertion = Says.unit(operator, "approved")
|
|
139
|
+
delegated = assertion.handoff(operator.delegate("agent", frozenset({"approve"})))
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Revocation Cascade
|
|
143
|
+
|
|
144
|
+
When a consent contract is revoked, all data whose provenance includes that contract is automatically purged across registered subsystems.
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from agentgov import ConsentRegistry, RevocationPropagator, CarrierRegistry
|
|
148
|
+
|
|
149
|
+
registry = ConsentRegistry()
|
|
150
|
+
propagator = RevocationPropagator(registry)
|
|
151
|
+
propagator.register_carrier_registry(carrier_reg)
|
|
152
|
+
report = propagator.revoke("alice") # cascading purge
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Algebraic Properties (Hypothesis-Verified)
|
|
156
|
+
|
|
157
|
+
- **ConsentLabel**: join-semilattice (associative, commutative, idempotent, bottom identity)
|
|
158
|
+
- **Labeled[T]**: functor laws (identity, composition)
|
|
159
|
+
- **Principal**: non-amplification (bound authority <= delegator authority)
|
|
160
|
+
- **ProvenanceExpr**: PosBool(X) semiring (plus/tensor commutativity, associativity, distributivity, annihilation)
|
|
161
|
+
- **VetoChain**: monotonic (adding vetoes only restricts, never permits)
|
|
162
|
+
- **Governor**: consistent with can_flow_to
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# agentgov
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/hapax-agentgov/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
Computational constitutional governance for AI agent systems.
|
|
7
|
+
|
|
8
|
+
agentgov provides algebraically-verified primitives for governing multi-agent systems: consent contracts, information flow control, principal delegation, provenance tracking, and compositional policy enforcement. Zero dependencies beyond PyYAML. Extracted from [hapax-council](https://github.com/hapax-systems/hapax-council), where it governs 200+ AI agents in production.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install hapax-agentgov
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Core Concepts
|
|
17
|
+
|
|
18
|
+
### Principals
|
|
19
|
+
|
|
20
|
+
Actors in the system. Sovereign principals (humans) originate consent; bound principals (agents) operate under delegated authority with non-amplification guarantees.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from agentgov import Principal, PrincipalKind
|
|
24
|
+
|
|
25
|
+
operator = Principal(id="operator", kind=PrincipalKind.SOVEREIGN)
|
|
26
|
+
agent = operator.delegate("sync-agent", frozenset({"email", "calendar"}))
|
|
27
|
+
sub = agent.delegate("sub-agent", frozenset({"email"})) # narrows authority
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Consent Labels (DLM Join-Semilattice)
|
|
31
|
+
|
|
32
|
+
Information flow labels track who may read data. Labels combine via join — combining data with different consent requirements produces the most restrictive combination.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from agentgov import ConsentLabel
|
|
36
|
+
|
|
37
|
+
public = ConsentLabel.bottom() # no restrictions
|
|
38
|
+
restricted = ConsentLabel(frozenset({("alice", frozenset({"bob"}))}))
|
|
39
|
+
combined = public.join(restricted) # most restrictive wins
|
|
40
|
+
assert public.can_flow_to(combined) # less restrictive flows to more
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Labeled Values (LIO-Style)
|
|
44
|
+
|
|
45
|
+
Wrap any value with its consent label and why-provenance.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from agentgov import Labeled, ConsentLabel
|
|
49
|
+
|
|
50
|
+
data = Labeled(value="secret", label=restricted, provenance=frozenset({"contract-1"}))
|
|
51
|
+
transformed = data.map(str.upper) # label preserved through transformations
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Provenance Semirings
|
|
55
|
+
|
|
56
|
+
Track WHY data exists using algebraic provenance (Green et al., PODS 2007). Supports tensor (both required) and plus (either sufficient) composition.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from agentgov import ProvenanceExpr
|
|
60
|
+
|
|
61
|
+
combined = ProvenanceExpr.leaf("c1").tensor(ProvenanceExpr.leaf("c2"))
|
|
62
|
+
assert combined.evaluate(frozenset({"c1", "c2"})) # both active: survives
|
|
63
|
+
assert not combined.evaluate(frozenset({"c1"})) # one revoked: purged
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Governor (Per-Agent Policy Enforcement)
|
|
67
|
+
|
|
68
|
+
Each agent gets a governance wrapper that validates inputs/outputs at boundaries. Pure validation layer — allows or denies, never modifies.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from agentgov import GovernorWrapper, GovernorPolicy, Labeled, ConsentLabel
|
|
72
|
+
|
|
73
|
+
gov = GovernorWrapper("my-agent")
|
|
74
|
+
gov.add_input_policy(GovernorPolicy(
|
|
75
|
+
name="require-consent",
|
|
76
|
+
check=lambda agent_id, data: data.label != ConsentLabel.bottom(),
|
|
77
|
+
axiom_id="consent",
|
|
78
|
+
))
|
|
79
|
+
result = gov.check_input(Labeled(value="data", label=ConsentLabel.bottom()))
|
|
80
|
+
assert not result.allowed
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### VetoChain (Deny-Wins Composition)
|
|
84
|
+
|
|
85
|
+
Order-independent constraint composition. Any denial blocks the action.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from agentgov import VetoChain, Veto
|
|
89
|
+
|
|
90
|
+
chain = VetoChain([
|
|
91
|
+
Veto("budget", lambda ctx: ctx["budget"] > 0),
|
|
92
|
+
Veto("auth", lambda ctx: ctx["authenticated"]),
|
|
93
|
+
])
|
|
94
|
+
result = chain.evaluate({"budget": 100, "authenticated": False})
|
|
95
|
+
assert not result.allowed
|
|
96
|
+
assert "auth" in result.denied_by
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Says Monad (DCC Attribution)
|
|
100
|
+
|
|
101
|
+
Principal-annotated assertions following Abadi's DCC formalism. Threads authority through data transformations.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from agentgov import Says, Principal, PrincipalKind
|
|
105
|
+
|
|
106
|
+
operator = Principal(id="op", kind=PrincipalKind.SOVEREIGN)
|
|
107
|
+
assertion = Says.unit(operator, "approved")
|
|
108
|
+
delegated = assertion.handoff(operator.delegate("agent", frozenset({"approve"})))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Revocation Cascade
|
|
112
|
+
|
|
113
|
+
When a consent contract is revoked, all data whose provenance includes that contract is automatically purged across registered subsystems.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from agentgov import ConsentRegistry, RevocationPropagator, CarrierRegistry
|
|
117
|
+
|
|
118
|
+
registry = ConsentRegistry()
|
|
119
|
+
propagator = RevocationPropagator(registry)
|
|
120
|
+
propagator.register_carrier_registry(carrier_reg)
|
|
121
|
+
report = propagator.revoke("alice") # cascading purge
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Algebraic Properties (Hypothesis-Verified)
|
|
125
|
+
|
|
126
|
+
- **ConsentLabel**: join-semilattice (associative, commutative, idempotent, bottom identity)
|
|
127
|
+
- **Labeled[T]**: functor laws (identity, composition)
|
|
128
|
+
- **Principal**: non-amplification (bound authority <= delegator authority)
|
|
129
|
+
- **ProvenanceExpr**: PosBool(X) semiring (plus/tensor commutativity, associativity, distributivity, annihilation)
|
|
130
|
+
- **VetoChain**: monotonic (adding vetoes only restricts, never permits)
|
|
131
|
+
- **Governor**: consistent with can_flow_to
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hapax-agentgov"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Computational constitutional governance for AI agent systems"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
authors = [{ name = "Ryan Lee", email = "rylklee@gmail.com" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"governance",
|
|
15
|
+
"consent",
|
|
16
|
+
"agents",
|
|
17
|
+
"multi-agent",
|
|
18
|
+
"information-flow",
|
|
19
|
+
"access-control",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 4 - Beta",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Intended Audience :: Science/Research",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
30
|
+
"Topic :: Security",
|
|
31
|
+
"Typing :: Typed",
|
|
32
|
+
]
|
|
33
|
+
dependencies = ["pyyaml>=6.0"]
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
dev = ["pytest>=8.0", "hypothesis>=6.0", "ruff>=0.4", "pyright>=1.1"]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/hapax-systems/agentgov"
|
|
40
|
+
Documentation = "https://github.com/hapax-systems/agentgov#readme"
|
|
41
|
+
Repository = "https://github.com/hapax-systems/agentgov"
|
|
42
|
+
Issues = "https://github.com/hapax-systems/agentgov/issues"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/agentgov"]
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
line-length = 100
|
|
49
|
+
target-version = "py312"
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.format]
|
|
55
|
+
quote-style = "double"
|
|
56
|
+
|
|
57
|
+
[tool.pyright]
|
|
58
|
+
pythonVersion = "3.12"
|
|
59
|
+
typeCheckingMode = "standard"
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""agentgov — Computational constitutional governance for AI agent systems.
|
|
2
|
+
|
|
3
|
+
Pure governance logic with algebraic guarantees:
|
|
4
|
+
- ConsentLabel: join-semilattice (associative, commutative, idempotent)
|
|
5
|
+
- Labeled[T]: functor (identity, composition)
|
|
6
|
+
- Principal: non-amplification (bound <= delegator authority)
|
|
7
|
+
- Governor: consistent with can_flow_to
|
|
8
|
+
- ProvenanceExpr: PosBool(X) semiring
|
|
9
|
+
- VetoChain: deny-wins composition
|
|
10
|
+
- Says: DCC-style principal attribution monad
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from agentgov.agent_governor import create_agent_governor
|
|
14
|
+
from agentgov.carrier import CarrierFact, CarrierRegistry, DisplacementResult
|
|
15
|
+
from agentgov.consent import ConsentContract, ConsentRegistry, load_contracts
|
|
16
|
+
from agentgov.consent_label import ConsentLabel
|
|
17
|
+
from agentgov.governor import (
|
|
18
|
+
GovernorDenial,
|
|
19
|
+
GovernorPolicy,
|
|
20
|
+
GovernorResult,
|
|
21
|
+
GovernorWrapper,
|
|
22
|
+
consent_input_policy,
|
|
23
|
+
consent_output_policy,
|
|
24
|
+
)
|
|
25
|
+
from agentgov.hooks import (
|
|
26
|
+
HookResult,
|
|
27
|
+
scan_attribution_entities,
|
|
28
|
+
scan_management_boundary,
|
|
29
|
+
scan_pii,
|
|
30
|
+
scan_provenance_references,
|
|
31
|
+
scan_single_user_violations,
|
|
32
|
+
validate_all,
|
|
33
|
+
)
|
|
34
|
+
from agentgov.labeled import Labeled
|
|
35
|
+
from agentgov.primitives import (
|
|
36
|
+
Candidate,
|
|
37
|
+
FallbackChain,
|
|
38
|
+
GatedResult,
|
|
39
|
+
Selected,
|
|
40
|
+
Veto,
|
|
41
|
+
VetoChain,
|
|
42
|
+
VetoResult,
|
|
43
|
+
)
|
|
44
|
+
from agentgov.principal import Principal, PrincipalKind
|
|
45
|
+
from agentgov.provenance import ProvenanceExpr
|
|
46
|
+
from agentgov.revocation import (
|
|
47
|
+
PurgeResult,
|
|
48
|
+
RevocationPropagator,
|
|
49
|
+
RevocationReport,
|
|
50
|
+
check_provenance,
|
|
51
|
+
)
|
|
52
|
+
from agentgov.says import Says
|
|
53
|
+
|
|
54
|
+
__version__ = "0.2.0"
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
# Principal model
|
|
58
|
+
"Principal",
|
|
59
|
+
"PrincipalKind",
|
|
60
|
+
# Consent
|
|
61
|
+
"ConsentContract",
|
|
62
|
+
"ConsentRegistry",
|
|
63
|
+
"ConsentLabel",
|
|
64
|
+
"load_contracts",
|
|
65
|
+
# Labeled data
|
|
66
|
+
"Labeled",
|
|
67
|
+
# Provenance
|
|
68
|
+
"ProvenanceExpr",
|
|
69
|
+
# Carrier dynamics
|
|
70
|
+
"CarrierFact",
|
|
71
|
+
"CarrierRegistry",
|
|
72
|
+
"DisplacementResult",
|
|
73
|
+
# Governor
|
|
74
|
+
"GovernorWrapper",
|
|
75
|
+
"GovernorPolicy",
|
|
76
|
+
"GovernorResult",
|
|
77
|
+
"GovernorDenial",
|
|
78
|
+
"consent_input_policy",
|
|
79
|
+
"consent_output_policy",
|
|
80
|
+
"create_agent_governor",
|
|
81
|
+
# Revocation cascade
|
|
82
|
+
"RevocationPropagator",
|
|
83
|
+
"RevocationReport",
|
|
84
|
+
"PurgeResult",
|
|
85
|
+
"check_provenance",
|
|
86
|
+
# Compositional primitives
|
|
87
|
+
"Candidate",
|
|
88
|
+
"FallbackChain",
|
|
89
|
+
"GatedResult",
|
|
90
|
+
"Selected",
|
|
91
|
+
"Veto",
|
|
92
|
+
"VetoChain",
|
|
93
|
+
"VetoResult",
|
|
94
|
+
# Says monad
|
|
95
|
+
"Says",
|
|
96
|
+
# Governance hooks
|
|
97
|
+
"HookResult",
|
|
98
|
+
"scan_pii",
|
|
99
|
+
"scan_single_user_violations",
|
|
100
|
+
"scan_attribution_entities",
|
|
101
|
+
"scan_provenance_references",
|
|
102
|
+
"scan_management_boundary",
|
|
103
|
+
"validate_all",
|
|
104
|
+
]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Agent governor factory: builds GovernorWrapper from axiom bindings.
|
|
2
|
+
|
|
3
|
+
Translates declarative axiom bindings in agent manifests into runtime
|
|
4
|
+
governance policies. Each agent gets a GovernorWrapper configured with
|
|
5
|
+
input/output policies derived from its axiom relationships.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from agentgov.consent_label import ConsentLabel
|
|
15
|
+
from agentgov.governor import (
|
|
16
|
+
GovernorPolicy,
|
|
17
|
+
GovernorWrapper,
|
|
18
|
+
consent_input_policy,
|
|
19
|
+
consent_output_policy,
|
|
20
|
+
)
|
|
21
|
+
from agentgov.labeled import Labeled
|
|
22
|
+
|
|
23
|
+
_log = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
PolicyBuilder = Callable[[str], tuple[list[GovernorPolicy], list[GovernorPolicy]]]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def interpersonal_transparency_policies(
|
|
29
|
+
role: str,
|
|
30
|
+
) -> tuple[list[GovernorPolicy], list[GovernorPolicy]]:
|
|
31
|
+
"""Build policies for the interpersonal_transparency axiom."""
|
|
32
|
+
input_policies: list[GovernorPolicy] = []
|
|
33
|
+
output_policies: list[GovernorPolicy] = []
|
|
34
|
+
|
|
35
|
+
if role in ("subject", "enforcer"):
|
|
36
|
+
input_policies.append(consent_input_policy(ConsentLabel.bottom()))
|
|
37
|
+
output_policies.append(consent_output_policy(ConsentLabel.bottom()))
|
|
38
|
+
|
|
39
|
+
return input_policies, output_policies
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def corporate_boundary_policies(
|
|
43
|
+
role: str,
|
|
44
|
+
) -> tuple[list[GovernorPolicy], list[GovernorPolicy]]:
|
|
45
|
+
"""Build policies for the corporate_boundary axiom."""
|
|
46
|
+
input_policies: list[GovernorPolicy] = []
|
|
47
|
+
output_policies: list[GovernorPolicy] = []
|
|
48
|
+
|
|
49
|
+
if role in ("subject", "enforcer"):
|
|
50
|
+
|
|
51
|
+
def _no_work_data(_agent_id: str, data: Labeled[Any]) -> bool:
|
|
52
|
+
if not (hasattr(data, "metadata") and isinstance(data.metadata, dict)):
|
|
53
|
+
_log.warning("corporate_boundary: denying data with no metadata dict (fail-closed)")
|
|
54
|
+
return False
|
|
55
|
+
category = data.metadata.get("data_category")
|
|
56
|
+
if category is None:
|
|
57
|
+
_log.warning(
|
|
58
|
+
"corporate_boundary: denying data with no data_category key (fail-closed)"
|
|
59
|
+
)
|
|
60
|
+
return False
|
|
61
|
+
return category != "work"
|
|
62
|
+
|
|
63
|
+
output_policies.append(
|
|
64
|
+
GovernorPolicy(
|
|
65
|
+
name="corporate_boundary_output",
|
|
66
|
+
check=_no_work_data,
|
|
67
|
+
axiom_id="corporate_boundary",
|
|
68
|
+
description="Block work data from persisting to home system",
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return input_policies, output_policies
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
DEFAULT_AXIOM_BUILDERS: dict[str, PolicyBuilder] = {
|
|
76
|
+
"interpersonal_transparency": interpersonal_transparency_policies,
|
|
77
|
+
"corporate_boundary": corporate_boundary_policies,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def create_agent_governor(
|
|
82
|
+
agent_id: str,
|
|
83
|
+
axiom_bindings: list[dict[str, Any]] | None = None,
|
|
84
|
+
*,
|
|
85
|
+
axiom_builders: dict[str, PolicyBuilder] | None = None,
|
|
86
|
+
binding_loader: Callable[[str], list[Any]] | None = None,
|
|
87
|
+
) -> GovernorWrapper:
|
|
88
|
+
"""Build a GovernorWrapper from agent manifest axiom bindings.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
agent_id: Agent identifier.
|
|
92
|
+
axiom_bindings: List of binding dicts with keys 'axiom_id', 'role'.
|
|
93
|
+
axiom_builders: Custom axiom-to-policy mapping. Defaults to
|
|
94
|
+
built-in interpersonal_transparency and corporate_boundary.
|
|
95
|
+
binding_loader: Optional callable to load bindings from an
|
|
96
|
+
external registry when axiom_bindings is None.
|
|
97
|
+
"""
|
|
98
|
+
gov = GovernorWrapper(agent_id)
|
|
99
|
+
builders = axiom_builders or DEFAULT_AXIOM_BUILDERS
|
|
100
|
+
|
|
101
|
+
bindings = axiom_bindings
|
|
102
|
+
if bindings is None and binding_loader is not None:
|
|
103
|
+
bindings = binding_loader(agent_id)
|
|
104
|
+
if bindings is None:
|
|
105
|
+
bindings = []
|
|
106
|
+
|
|
107
|
+
for binding in bindings:
|
|
108
|
+
axiom_id = binding.get("axiom_id", "") if isinstance(binding, dict) else binding.axiom_id
|
|
109
|
+
role = binding.get("role", "subject") if isinstance(binding, dict) else binding.role
|
|
110
|
+
|
|
111
|
+
builder = builders.get(axiom_id)
|
|
112
|
+
if builder is None:
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
input_policies, output_policies = builder(role)
|
|
116
|
+
for p in input_policies:
|
|
117
|
+
gov.add_input_policy(p)
|
|
118
|
+
for p in output_policies:
|
|
119
|
+
gov.add_output_policy(p)
|
|
120
|
+
|
|
121
|
+
_log.debug(
|
|
122
|
+
"Governor %s: added %d input + %d output policies for %s",
|
|
123
|
+
agent_id,
|
|
124
|
+
len(input_policies),
|
|
125
|
+
len(output_policies),
|
|
126
|
+
axiom_id,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return gov
|