agent-firewall-security 2.3.0__tar.gz → 2.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.
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/PKG-INFO +136 -5
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/README.md +135 -4
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/PKG-INFO +136 -5
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/SOURCES.txt +22 -1
- agent_firewall_security-2.4.0/firewall/a2a/__init__.py +34 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/a2a/auth.py +81 -4
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/adapters/anthropic.py +57 -46
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/adapters/generic.py +60 -46
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/adapters/openai.py +57 -46
- agent_firewall_security-2.4.0/firewall/aegis/__init__.py +209 -0
- agent_firewall_security-2.4.0/firewall/aegis/blast.py +450 -0
- agent_firewall_security-2.4.0/firewall/aegis/controller.py +1177 -0
- agent_firewall_security-2.4.0/firewall/aegis/decay.py +267 -0
- agent_firewall_security-2.4.0/firewall/aegis/envelope.py +979 -0
- agent_firewall_security-2.4.0/firewall/aegis/explain.py +451 -0
- agent_firewall_security-2.4.0/firewall/aegis/preflight.py +614 -0
- agent_firewall_security-2.4.0/firewall/aegis/response.py +557 -0
- agent_firewall_security-2.4.0/firewall/aegis/restriction.py +523 -0
- agent_firewall_security-2.4.0/firewall/aegis/state.py +384 -0
- agent_firewall_security-2.4.0/firewall/attenuation.py +134 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/authorization.py +22 -1
- agent_firewall_security-2.4.0/firewall/benchmarks.py +1191 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/cli_v21.py +20 -4
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/delegation.py +14 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/invariants/__init__.py +14 -2
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/invariants/__main__.py +16 -12
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/invariants/exercise.py +144 -14
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/invariants/registry.py +74 -9
- agent_firewall_security-2.4.0/firewall/invariants/runtime.py +2683 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/invariants/source.py +1 -1
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/sdk.py +394 -1
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/introspect.py +43 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/pyproject.toml +1 -1
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_invariants.py +36 -19
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_3_invariant_gate.py +150 -18
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_adversarial.py +1468 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_concurrency.py +1155 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_controller.py +1450 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_corrections.py +477 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_fuzz.py +949 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_gate.py +504 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_state.py +633 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_attenuation_narrowing.py +369 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_benchmarks.py +337 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_envelope_soundness.py +154 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_integration_boundary.py +979 -0
- agent_firewall_security-2.3.0/firewall/a2a/__init__.py +0 -21
- agent_firewall_security-2.3.0/firewall/attenuation.py +0 -159
- agent_firewall_security-2.3.0/firewall/benchmarks.py +0 -295
- agent_firewall_security-2.3.0/firewall/invariants/runtime.py +0 -1173
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/dependency_links.txt +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/entry_points.txt +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/requires.txt +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/top_level.txt +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/adapters/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/adapters/normalize.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/adversarial/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/agents/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/agents/adapters.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/agents/base.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/artifact/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/artifact/reader.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/artifact/writer.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/attackgraph/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/attackgraph/engine.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/attest/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/attest/authority.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/capability.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/capability2/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/capability2/constraints.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/cli.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/cli_v19.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/cli_v20.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/containment/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/containment/controller.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/continuous_auth/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/continuous_auth/engine.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/continuous_auth/monitor.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/continuous_auth/predicates.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/deception/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/defense/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/defense/mesh.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/delegation_budget.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/delegation_lineage.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/delegation_store.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/engine.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/evidence.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/evidence_graph/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/evidence_graph/graph.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/evidence_integrity/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/explain.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/http.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ident/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ident/registry.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/identity.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/immune/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/immune/engine.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/incident/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/incident/package.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/intel/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/intel/engine.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/invariants/model.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/invariants/static.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/key_management.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/key_store.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/lab/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/lab/engine.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/lifecycle.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/lifecycle_store.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/mcp.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/namespace.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/attack_path.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/behavior.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/correlation.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/graph.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/model.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/response.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/simulator.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/network/state.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/north_star.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/passport/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/passport/builder.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/platform/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/platform/provenance.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/policy.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/policy_analysis/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/posture/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/posture/engine.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/protect.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/provenance/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/provenance/registry.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/recorder/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/recorder/checkpoint.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/recorder/encoding.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/recorder/events.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/recorder/identity.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/recorder/recorder.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/recorder/redact.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/refusal_state.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/replay.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/replay_store.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/replaylab/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/replaylab/laboratory.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/research/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/research/lab.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/response2/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/response2/adaptive.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/revocation.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/revocation_store.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/risk_context.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/security_context.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/security_decision.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/security_memory/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/semantic_chain.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/simulation/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/simulation/case.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/simulation/recorder.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/simulation/replay.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/simulation/report.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/simulation/rollout.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/simulation/ruleset.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/task/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/task/registry.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/timeline/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/timeline/graph.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/timeline/timeline.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/timeline/trajectory.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/tools.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/transport.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/trust/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/trust/graph.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/twin/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/twin/adversarial.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/twin/twin.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/__main__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/control.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/demo.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/server.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/service.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/console.css +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/console.js +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/control.js +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/index.html +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/recorder.css +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/recorder.js +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/soc.css +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/soc.js +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/v20.css +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/v20.js +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/v21.css +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/static/v21.js +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/v18.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/v19.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/v20.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/ui/v21.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/verify/__init__.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/firewall/verify/verifier.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/setup.cfg +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_continuous_auth.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_engine.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_sdk_continuous_auth.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_security_decision.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_adversarial_defense.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_adversarial_twin.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_capability_delegation.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_deception_integrity.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_evidence_integrity.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_platform_provenance.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_security_memory.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_3_duplicate_concepts.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_3_intel_coordination.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_3_policy_analysis.py +0 -0
- {agent_firewall_security-2.3.0 → agent_firewall_security-2.4.0}/tests/test_v2_3_self_attack.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-firewall-security
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Security and canonical authorization infrastructure for AI agents and automated tool use.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Shubhbhangoo/agent-firewall
|
|
6
6
|
Project-URL: Repository, https://github.com/Shubhbhangoo/agent-firewall
|
|
@@ -28,6 +28,8 @@ Requires-Dist: hypothesis>=6.0; extra == "dev"
|
|
|
28
28
|
|
|
29
29
|
Agent Firewall is built around one security boundary: **authorization remains deterministic, explicit, and fail-closed**. Identity, provenance, monitoring, behavioral analysis, simulation, evidence, and response provide security context around that boundary, but they do not become an alternative path to authorization.
|
|
30
30
|
|
|
31
|
+
> **v2.4** adds **Aegis**, an adaptive authority control plane: a live grant can be narrowed, suspended, revalidated or revoked while a task is running. It adds no second authorization path — Aegis reaches `FirewallSDK.authorize()` through one gate that can only deny or abstain, and learns what happened through one callback the SDK invokes *after* the decision exists.
|
|
32
|
+
>
|
|
31
33
|
> **v2.3** is a correctness release. It adds no subsystem and no authorization path: the work was to attack v2.2's shipped behaviour, fix the three fail-open paths that broke, stop analytical output from reading as verified when it was not, and make the strict invariant gate something CI can actually fail on.
|
|
32
34
|
>
|
|
33
35
|
> **v2.2** makes the control plane adaptive: authority is re-evaluated when the state it rested on changes, contradictions between independent claim sources are reported rather than resolved, and the architectural properties the design rests on are checked by code instead of asserted in prose.
|
|
@@ -92,6 +94,96 @@ When required evidence is unavailable, verification fails, identity is unknown,
|
|
|
92
94
|
|
|
93
95
|
---
|
|
94
96
|
|
|
97
|
+
## What v2.4 adds
|
|
98
|
+
|
|
99
|
+
v2.2 made the platform adaptive *above* the boundary: when watched state
|
|
100
|
+
changed, a decision was re-run. v2.4 makes **authority itself** adaptive. A
|
|
101
|
+
grant that is already in use can be narrowed, suspended, revalidated or
|
|
102
|
+
revoked, and the change is enforced on the next authorization — and, for
|
|
103
|
+
suspension, inside the commit transaction of one already in flight.
|
|
104
|
+
|
|
105
|
+
Aegis is off by default. `FirewallSDK(aegis_enabled=True)` opts in; with no
|
|
106
|
+
controller attached the adaptive gate abstains and v2.3 behaviour is exact.
|
|
107
|
+
|
|
108
|
+
### One deny-only seam
|
|
109
|
+
|
|
110
|
+
Nine modules, 5,382 lines, none of which imports `firewall.sdk`. Everything
|
|
111
|
+
Aegis does reaches the outside world through two points: `_gate_aegis`, the
|
|
112
|
+
ninth of eleven gates, which can only deny or abstain; and
|
|
113
|
+
`observe_authorization`, which the SDK calls *after* a decision exists.
|
|
114
|
+
Because the callback runs after, no Aegis state can be a precondition of the
|
|
115
|
+
allow it observes. Authority flows from the boundary into Aegis and never the
|
|
116
|
+
other way.
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
change -> classify -> KEEP | REVALIDATE | NARROW | SUSPEND | REVOKE
|
|
120
|
+
|
|
|
121
|
+
v
|
|
122
|
+
restriction written
|
|
123
|
+
|
|
|
124
|
+
v
|
|
125
|
+
FirewallSDK.authorize() -> _gate_aegis -> DENY
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
There is no arrow from Aegis to ALLOW. That absence is the design.
|
|
129
|
+
|
|
130
|
+
### The authority envelope
|
|
131
|
+
|
|
132
|
+
An `AuthorityEnvelope` is a bound on what a capability may still do, folded
|
|
133
|
+
across its delegation chain by a per-dimension greatest-lower-bound. Twelve
|
|
134
|
+
fields — patterns, tool, time window, constraint bounds, depth, its ceiling,
|
|
135
|
+
issuers, issuer trust, revocation, budget, chain membership, and a bottom
|
|
136
|
+
marker — and every dimension that bounds a request has a named enforcement
|
|
137
|
+
site at the boundary. A dimension with no enforcement site would be
|
|
138
|
+
decoration.
|
|
139
|
+
|
|
140
|
+
The theorem is one-directional and stated that way: if the envelope
|
|
141
|
+
**excludes** a request, the boundary denies it. The converse does not hold,
|
|
142
|
+
and the API is named so that misuse reads wrong — `excludes()` returns a
|
|
143
|
+
reason, `may_admit()` means "this envelope does not itself refuse", and
|
|
144
|
+
`__bool__` **raises** on an envelope, a grant, a preflight, a blast radius
|
|
145
|
+
and a classification, so `if preflight(...)` is an error rather than an
|
|
146
|
+
accidental allow.
|
|
147
|
+
|
|
148
|
+
### Seven states ordered by residual authority
|
|
149
|
+
|
|
150
|
+
`ISSUED`, `ACTIVE`, `REVALIDATING`, `NARROWED`, `SUSPENDED`, `REVOKED`,
|
|
151
|
+
`EXPIRED`. A transition is legal only if it does not increase residual
|
|
152
|
+
authority. `REVALIDATING -> ACTIVE` is the only edge that restores authority,
|
|
153
|
+
and it requires an `AuthorizationResult` that is allowed, reasoned
|
|
154
|
+
`authorized`, and traced to that capability's fingerprint. `REVOKED` and
|
|
155
|
+
`EXPIRED` are terminal and checked before the ordering rule, so no evidence
|
|
156
|
+
or clock change produces an edge out of either.
|
|
157
|
+
|
|
158
|
+
The state machine is a record and a legality check. It is deliberately **not**
|
|
159
|
+
an enforcement channel: the gate reads restrictions, not states, because
|
|
160
|
+
wiring the state model into the gate would make the authorization path depend
|
|
161
|
+
on a structure whose own updates require an authorization result.
|
|
162
|
+
|
|
163
|
+
### Unknown never becomes safe
|
|
164
|
+
|
|
165
|
+
An unrecognised trigger is `REVALIDATE`, not `KEEP` (which would make an
|
|
166
|
+
unknown event benign) and not `REVOKE` (which would make any unknown string a
|
|
167
|
+
denial-of-service lever). Nothing maps to `KEEP` at all — it is reachable
|
|
168
|
+
only by establishing five positive conditions, so "nothing changed" must be
|
|
169
|
+
shown rather than assumed. An unreadable budget is exhausted, not unlimited;
|
|
170
|
+
an unreadable restriction matches; unestablished issuer trust is `None`, not
|
|
171
|
+
`True`; an unresolvable chain yields the bottom envelope; a traversal that
|
|
172
|
+
exceeds its bounds is `UNANALYZABLE` rather than a partial answer presented
|
|
173
|
+
as complete. `UNKNOWN_NON_AUTHORIZATION` checks these exhaustively.
|
|
174
|
+
|
|
175
|
+
### What it does not claim
|
|
176
|
+
|
|
177
|
+
Envelope soundness runs in one direction. `canonical_allow_for` is a
|
|
178
|
+
structural check, not a cryptographic one — it bounds mistakes, not an
|
|
179
|
+
adversary already inside the process. The commit-time re-read covers
|
|
180
|
+
suspension only. The restriction cap trades availability for integrity:
|
|
181
|
+
sixteen narrowings drive a grant to `SUSPEND`, which is fail-closed and still
|
|
182
|
+
a lever. §16 of [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) is the full list,
|
|
183
|
+
and §17 maps each guarantee to the test file that establishes it.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
95
187
|
## What v2.3 changes
|
|
96
188
|
|
|
97
189
|
v2.3 adds no subsystem. Three requests that v2.2 allowed are now denied,
|
|
@@ -137,9 +229,9 @@ section, so deleting one fails rather than quietly shrinking the suite. See
|
|
|
137
229
|
### A strict invariant gate that can pass
|
|
138
230
|
|
|
139
231
|
`python -m firewall.invariants --strict` exited 2 on every invocation,
|
|
140
|
-
because
|
|
232
|
+
because seven of the fifteen invariants are claims about live state that a
|
|
141
233
|
source-only run never reaches. A gate that always fails is a gate that gets
|
|
142
|
-
removed, so those
|
|
234
|
+
removed, so those seven were effectively ungated in CI.
|
|
143
235
|
`firewall/invariants/exercise.py` builds the canonical estate through the
|
|
144
236
|
SDK's public API only, and CI now runs the source-only and exercised gates
|
|
145
237
|
as separate steps. What a green exercised run establishes is bounded to that
|
|
@@ -430,7 +522,7 @@ The architecture is designed around explicit security invariants.
|
|
|
430
522
|
- **A check that could not run is not a check that passed.**
|
|
431
523
|
- **Security failures default toward refusal rather than implicit trust.**
|
|
432
524
|
|
|
433
|
-
These are implementation properties of the system, not a claim that any deployment is universally secure.
|
|
525
|
+
These are implementation properties of the system, not a claim that any deployment is universally secure. Fifteen such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md) for the invariants themselves, [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md) for what a green gate run does and does not establish, and [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) for the four the authority control plane adds.
|
|
434
526
|
|
|
435
527
|
Where a property does **not** hold, it is stated rather than left to be inferred. A posture change is detected but does not by itself flip a verdict; `retire_key` is not containment for a stolen key, since a retired key's signatures keep verifying so that rotation does not invalidate capabilities in flight; an `amount_max` ceiling is per request, so two siblings each holding one can spend it twice unless a lineage budget is configured; and possession of a trusted signing key is authority, which no cryptography can undo. [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md) records each of these against the test that pins it.
|
|
436
528
|
|
|
@@ -516,9 +608,15 @@ The authorization path evaluates the security conditions required by the capabil
|
|
|
516
608
|
|
|
517
609
|
## Command surface
|
|
518
610
|
|
|
611
|
+
v2.4 adds no new CLI subcommands. One existing command changes what it
|
|
612
|
+
prints: `firewall delegate-authorize` now labels an allow it reached without
|
|
613
|
+
an attached `sdk_provider` as `ALLOWED (relationship only)` and says not to
|
|
614
|
+
enforce on it. The exit status stays 0 — the question asked was answered
|
|
615
|
+
affirmatively — but the answer no longer overstates itself.
|
|
616
|
+
|
|
519
617
|
v2.3 adds no new CLI subcommands. It adds one flag to the invariant
|
|
520
618
|
checker: `python -m firewall.invariants --exercise --strict` builds the
|
|
521
|
-
canonical estate so that all
|
|
619
|
+
canonical estate so that all fifteen invariants can be reached, which makes
|
|
522
620
|
`--strict` a gate that can pass and is therefore worth failing.
|
|
523
621
|
|
|
524
622
|
v2.2 adds no new CLI subcommands. Its one new entry point is the invariant
|
|
@@ -571,6 +669,35 @@ python -m firewall.benchmarks
|
|
|
571
669
|
|
|
572
670
|
The repository contains unit, integration, adversarial, hardening, evidence, UI/API, benchmark, and research tests.
|
|
573
671
|
|
|
672
|
+
The v2.4 test surface adds 384 tests across eleven files — 4,087 in the suite
|
|
673
|
+
as a whole, on Python 3.10, 3.11 and 3.12 — including stateful security-state
|
|
674
|
+
fuzzing, eight named concurrency races, and an integration-boundary sweep.
|
|
675
|
+
Among the properties covered:
|
|
676
|
+
|
|
677
|
+
- no path from adaptive analysis to an allow, and no Aegis module able to
|
|
678
|
+
construct an `AuthorizationResult`
|
|
679
|
+
- every forbidden transition refused individually: out of `REVOKED`, out of
|
|
680
|
+
`EXPIRED`, `NARROWED` to wider, `SUSPENDED` to `ACTIVE` without a canonical
|
|
681
|
+
allow, and a child exceeding its parent
|
|
682
|
+
- an envelope that excludes a request always accompanied by a boundary that
|
|
683
|
+
denies it
|
|
684
|
+
- no field of the envelope widening under delegation or attenuation
|
|
685
|
+
- a `nan` bound bounding nothing being denied, while an `inf` bound still
|
|
686
|
+
means unbounded
|
|
687
|
+
- a 400-digit integer producing a decision rather than an exception out of
|
|
688
|
+
`authorize()`
|
|
689
|
+
- every authorization-reachable Aegis method total, including against an
|
|
690
|
+
injected hostile controller
|
|
691
|
+
- a restriction written mid-flight being observed, and a suspension being
|
|
692
|
+
caught inside the commit transaction
|
|
693
|
+
- `__bool__` raising on every analysis object, so a truthiness test cannot
|
|
694
|
+
become an allow
|
|
695
|
+
- every integration surface reaching the canonical boundary, with no local
|
|
696
|
+
allow anywhere
|
|
697
|
+
- no dataclass field in the package carrying a default that Python 3.11
|
|
698
|
+
rejects, so a defect that hides on the development interpreter cannot wait
|
|
699
|
+
for CI to find it
|
|
700
|
+
|
|
574
701
|
The v2.3 test surface adds the thirteen-question self-attack suite, which
|
|
575
702
|
covers, among other properties:
|
|
576
703
|
|
|
@@ -697,6 +824,10 @@ See [`SECURITY.md`](SECURITY.md) for the project's security reporting policy.
|
|
|
697
824
|
|
|
698
825
|
Detailed specifications are maintained in the repository:
|
|
699
826
|
|
|
827
|
+
- `docs/v2.4-aegis.md`
|
|
828
|
+
- `docs/v2.4-aegis-design.md`
|
|
829
|
+
- `docs/v2.4-performance.md`
|
|
830
|
+
- `docs/v2.4-migration.md`
|
|
700
831
|
- `docs/v2.3-security-corrections.md`
|
|
701
832
|
- `docs/v2.3-self-attack.md`
|
|
702
833
|
- `docs/v2.3-invariant-gate.md`
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Agent Firewall is built around one security boundary: **authorization remains deterministic, explicit, and fail-closed**. Identity, provenance, monitoring, behavioral analysis, simulation, evidence, and response provide security context around that boundary, but they do not become an alternative path to authorization.
|
|
6
6
|
|
|
7
|
+
> **v2.4** adds **Aegis**, an adaptive authority control plane: a live grant can be narrowed, suspended, revalidated or revoked while a task is running. It adds no second authorization path — Aegis reaches `FirewallSDK.authorize()` through one gate that can only deny or abstain, and learns what happened through one callback the SDK invokes *after* the decision exists.
|
|
8
|
+
>
|
|
7
9
|
> **v2.3** is a correctness release. It adds no subsystem and no authorization path: the work was to attack v2.2's shipped behaviour, fix the three fail-open paths that broke, stop analytical output from reading as verified when it was not, and make the strict invariant gate something CI can actually fail on.
|
|
8
10
|
>
|
|
9
11
|
> **v2.2** makes the control plane adaptive: authority is re-evaluated when the state it rested on changes, contradictions between independent claim sources are reported rather than resolved, and the architectural properties the design rests on are checked by code instead of asserted in prose.
|
|
@@ -68,6 +70,96 @@ When required evidence is unavailable, verification fails, identity is unknown,
|
|
|
68
70
|
|
|
69
71
|
---
|
|
70
72
|
|
|
73
|
+
## What v2.4 adds
|
|
74
|
+
|
|
75
|
+
v2.2 made the platform adaptive *above* the boundary: when watched state
|
|
76
|
+
changed, a decision was re-run. v2.4 makes **authority itself** adaptive. A
|
|
77
|
+
grant that is already in use can be narrowed, suspended, revalidated or
|
|
78
|
+
revoked, and the change is enforced on the next authorization — and, for
|
|
79
|
+
suspension, inside the commit transaction of one already in flight.
|
|
80
|
+
|
|
81
|
+
Aegis is off by default. `FirewallSDK(aegis_enabled=True)` opts in; with no
|
|
82
|
+
controller attached the adaptive gate abstains and v2.3 behaviour is exact.
|
|
83
|
+
|
|
84
|
+
### One deny-only seam
|
|
85
|
+
|
|
86
|
+
Nine modules, 5,382 lines, none of which imports `firewall.sdk`. Everything
|
|
87
|
+
Aegis does reaches the outside world through two points: `_gate_aegis`, the
|
|
88
|
+
ninth of eleven gates, which can only deny or abstain; and
|
|
89
|
+
`observe_authorization`, which the SDK calls *after* a decision exists.
|
|
90
|
+
Because the callback runs after, no Aegis state can be a precondition of the
|
|
91
|
+
allow it observes. Authority flows from the boundary into Aegis and never the
|
|
92
|
+
other way.
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
change -> classify -> KEEP | REVALIDATE | NARROW | SUSPEND | REVOKE
|
|
96
|
+
|
|
|
97
|
+
v
|
|
98
|
+
restriction written
|
|
99
|
+
|
|
|
100
|
+
v
|
|
101
|
+
FirewallSDK.authorize() -> _gate_aegis -> DENY
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
There is no arrow from Aegis to ALLOW. That absence is the design.
|
|
105
|
+
|
|
106
|
+
### The authority envelope
|
|
107
|
+
|
|
108
|
+
An `AuthorityEnvelope` is a bound on what a capability may still do, folded
|
|
109
|
+
across its delegation chain by a per-dimension greatest-lower-bound. Twelve
|
|
110
|
+
fields — patterns, tool, time window, constraint bounds, depth, its ceiling,
|
|
111
|
+
issuers, issuer trust, revocation, budget, chain membership, and a bottom
|
|
112
|
+
marker — and every dimension that bounds a request has a named enforcement
|
|
113
|
+
site at the boundary. A dimension with no enforcement site would be
|
|
114
|
+
decoration.
|
|
115
|
+
|
|
116
|
+
The theorem is one-directional and stated that way: if the envelope
|
|
117
|
+
**excludes** a request, the boundary denies it. The converse does not hold,
|
|
118
|
+
and the API is named so that misuse reads wrong — `excludes()` returns a
|
|
119
|
+
reason, `may_admit()` means "this envelope does not itself refuse", and
|
|
120
|
+
`__bool__` **raises** on an envelope, a grant, a preflight, a blast radius
|
|
121
|
+
and a classification, so `if preflight(...)` is an error rather than an
|
|
122
|
+
accidental allow.
|
|
123
|
+
|
|
124
|
+
### Seven states ordered by residual authority
|
|
125
|
+
|
|
126
|
+
`ISSUED`, `ACTIVE`, `REVALIDATING`, `NARROWED`, `SUSPENDED`, `REVOKED`,
|
|
127
|
+
`EXPIRED`. A transition is legal only if it does not increase residual
|
|
128
|
+
authority. `REVALIDATING -> ACTIVE` is the only edge that restores authority,
|
|
129
|
+
and it requires an `AuthorizationResult` that is allowed, reasoned
|
|
130
|
+
`authorized`, and traced to that capability's fingerprint. `REVOKED` and
|
|
131
|
+
`EXPIRED` are terminal and checked before the ordering rule, so no evidence
|
|
132
|
+
or clock change produces an edge out of either.
|
|
133
|
+
|
|
134
|
+
The state machine is a record and a legality check. It is deliberately **not**
|
|
135
|
+
an enforcement channel: the gate reads restrictions, not states, because
|
|
136
|
+
wiring the state model into the gate would make the authorization path depend
|
|
137
|
+
on a structure whose own updates require an authorization result.
|
|
138
|
+
|
|
139
|
+
### Unknown never becomes safe
|
|
140
|
+
|
|
141
|
+
An unrecognised trigger is `REVALIDATE`, not `KEEP` (which would make an
|
|
142
|
+
unknown event benign) and not `REVOKE` (which would make any unknown string a
|
|
143
|
+
denial-of-service lever). Nothing maps to `KEEP` at all — it is reachable
|
|
144
|
+
only by establishing five positive conditions, so "nothing changed" must be
|
|
145
|
+
shown rather than assumed. An unreadable budget is exhausted, not unlimited;
|
|
146
|
+
an unreadable restriction matches; unestablished issuer trust is `None`, not
|
|
147
|
+
`True`; an unresolvable chain yields the bottom envelope; a traversal that
|
|
148
|
+
exceeds its bounds is `UNANALYZABLE` rather than a partial answer presented
|
|
149
|
+
as complete. `UNKNOWN_NON_AUTHORIZATION` checks these exhaustively.
|
|
150
|
+
|
|
151
|
+
### What it does not claim
|
|
152
|
+
|
|
153
|
+
Envelope soundness runs in one direction. `canonical_allow_for` is a
|
|
154
|
+
structural check, not a cryptographic one — it bounds mistakes, not an
|
|
155
|
+
adversary already inside the process. The commit-time re-read covers
|
|
156
|
+
suspension only. The restriction cap trades availability for integrity:
|
|
157
|
+
sixteen narrowings drive a grant to `SUSPEND`, which is fail-closed and still
|
|
158
|
+
a lever. §16 of [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) is the full list,
|
|
159
|
+
and §17 maps each guarantee to the test file that establishes it.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
71
163
|
## What v2.3 changes
|
|
72
164
|
|
|
73
165
|
v2.3 adds no subsystem. Three requests that v2.2 allowed are now denied,
|
|
@@ -113,9 +205,9 @@ section, so deleting one fails rather than quietly shrinking the suite. See
|
|
|
113
205
|
### A strict invariant gate that can pass
|
|
114
206
|
|
|
115
207
|
`python -m firewall.invariants --strict` exited 2 on every invocation,
|
|
116
|
-
because
|
|
208
|
+
because seven of the fifteen invariants are claims about live state that a
|
|
117
209
|
source-only run never reaches. A gate that always fails is a gate that gets
|
|
118
|
-
removed, so those
|
|
210
|
+
removed, so those seven were effectively ungated in CI.
|
|
119
211
|
`firewall/invariants/exercise.py` builds the canonical estate through the
|
|
120
212
|
SDK's public API only, and CI now runs the source-only and exercised gates
|
|
121
213
|
as separate steps. What a green exercised run establishes is bounded to that
|
|
@@ -406,7 +498,7 @@ The architecture is designed around explicit security invariants.
|
|
|
406
498
|
- **A check that could not run is not a check that passed.**
|
|
407
499
|
- **Security failures default toward refusal rather than implicit trust.**
|
|
408
500
|
|
|
409
|
-
These are implementation properties of the system, not a claim that any deployment is universally secure.
|
|
501
|
+
These are implementation properties of the system, not a claim that any deployment is universally secure. Fifteen such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md) for the invariants themselves, [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md) for what a green gate run does and does not establish, and [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) for the four the authority control plane adds.
|
|
410
502
|
|
|
411
503
|
Where a property does **not** hold, it is stated rather than left to be inferred. A posture change is detected but does not by itself flip a verdict; `retire_key` is not containment for a stolen key, since a retired key's signatures keep verifying so that rotation does not invalidate capabilities in flight; an `amount_max` ceiling is per request, so two siblings each holding one can spend it twice unless a lineage budget is configured; and possession of a trusted signing key is authority, which no cryptography can undo. [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md) records each of these against the test that pins it.
|
|
412
504
|
|
|
@@ -492,9 +584,15 @@ The authorization path evaluates the security conditions required by the capabil
|
|
|
492
584
|
|
|
493
585
|
## Command surface
|
|
494
586
|
|
|
587
|
+
v2.4 adds no new CLI subcommands. One existing command changes what it
|
|
588
|
+
prints: `firewall delegate-authorize` now labels an allow it reached without
|
|
589
|
+
an attached `sdk_provider` as `ALLOWED (relationship only)` and says not to
|
|
590
|
+
enforce on it. The exit status stays 0 — the question asked was answered
|
|
591
|
+
affirmatively — but the answer no longer overstates itself.
|
|
592
|
+
|
|
495
593
|
v2.3 adds no new CLI subcommands. It adds one flag to the invariant
|
|
496
594
|
checker: `python -m firewall.invariants --exercise --strict` builds the
|
|
497
|
-
canonical estate so that all
|
|
595
|
+
canonical estate so that all fifteen invariants can be reached, which makes
|
|
498
596
|
`--strict` a gate that can pass and is therefore worth failing.
|
|
499
597
|
|
|
500
598
|
v2.2 adds no new CLI subcommands. Its one new entry point is the invariant
|
|
@@ -547,6 +645,35 @@ python -m firewall.benchmarks
|
|
|
547
645
|
|
|
548
646
|
The repository contains unit, integration, adversarial, hardening, evidence, UI/API, benchmark, and research tests.
|
|
549
647
|
|
|
648
|
+
The v2.4 test surface adds 384 tests across eleven files — 4,087 in the suite
|
|
649
|
+
as a whole, on Python 3.10, 3.11 and 3.12 — including stateful security-state
|
|
650
|
+
fuzzing, eight named concurrency races, and an integration-boundary sweep.
|
|
651
|
+
Among the properties covered:
|
|
652
|
+
|
|
653
|
+
- no path from adaptive analysis to an allow, and no Aegis module able to
|
|
654
|
+
construct an `AuthorizationResult`
|
|
655
|
+
- every forbidden transition refused individually: out of `REVOKED`, out of
|
|
656
|
+
`EXPIRED`, `NARROWED` to wider, `SUSPENDED` to `ACTIVE` without a canonical
|
|
657
|
+
allow, and a child exceeding its parent
|
|
658
|
+
- an envelope that excludes a request always accompanied by a boundary that
|
|
659
|
+
denies it
|
|
660
|
+
- no field of the envelope widening under delegation or attenuation
|
|
661
|
+
- a `nan` bound bounding nothing being denied, while an `inf` bound still
|
|
662
|
+
means unbounded
|
|
663
|
+
- a 400-digit integer producing a decision rather than an exception out of
|
|
664
|
+
`authorize()`
|
|
665
|
+
- every authorization-reachable Aegis method total, including against an
|
|
666
|
+
injected hostile controller
|
|
667
|
+
- a restriction written mid-flight being observed, and a suspension being
|
|
668
|
+
caught inside the commit transaction
|
|
669
|
+
- `__bool__` raising on every analysis object, so a truthiness test cannot
|
|
670
|
+
become an allow
|
|
671
|
+
- every integration surface reaching the canonical boundary, with no local
|
|
672
|
+
allow anywhere
|
|
673
|
+
- no dataclass field in the package carrying a default that Python 3.11
|
|
674
|
+
rejects, so a defect that hides on the development interpreter cannot wait
|
|
675
|
+
for CI to find it
|
|
676
|
+
|
|
550
677
|
The v2.3 test surface adds the thirteen-question self-attack suite, which
|
|
551
678
|
covers, among other properties:
|
|
552
679
|
|
|
@@ -673,6 +800,10 @@ See [`SECURITY.md`](SECURITY.md) for the project's security reporting policy.
|
|
|
673
800
|
|
|
674
801
|
Detailed specifications are maintained in the repository:
|
|
675
802
|
|
|
803
|
+
- `docs/v2.4-aegis.md`
|
|
804
|
+
- `docs/v2.4-aegis-design.md`
|
|
805
|
+
- `docs/v2.4-performance.md`
|
|
806
|
+
- `docs/v2.4-migration.md`
|
|
676
807
|
- `docs/v2.3-security-corrections.md`
|
|
677
808
|
- `docs/v2.3-self-attack.md`
|
|
678
809
|
- `docs/v2.3-invariant-gate.md`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-firewall-security
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Security and canonical authorization infrastructure for AI agents and automated tool use.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Shubhbhangoo/agent-firewall
|
|
6
6
|
Project-URL: Repository, https://github.com/Shubhbhangoo/agent-firewall
|
|
@@ -28,6 +28,8 @@ Requires-Dist: hypothesis>=6.0; extra == "dev"
|
|
|
28
28
|
|
|
29
29
|
Agent Firewall is built around one security boundary: **authorization remains deterministic, explicit, and fail-closed**. Identity, provenance, monitoring, behavioral analysis, simulation, evidence, and response provide security context around that boundary, but they do not become an alternative path to authorization.
|
|
30
30
|
|
|
31
|
+
> **v2.4** adds **Aegis**, an adaptive authority control plane: a live grant can be narrowed, suspended, revalidated or revoked while a task is running. It adds no second authorization path — Aegis reaches `FirewallSDK.authorize()` through one gate that can only deny or abstain, and learns what happened through one callback the SDK invokes *after* the decision exists.
|
|
32
|
+
>
|
|
31
33
|
> **v2.3** is a correctness release. It adds no subsystem and no authorization path: the work was to attack v2.2's shipped behaviour, fix the three fail-open paths that broke, stop analytical output from reading as verified when it was not, and make the strict invariant gate something CI can actually fail on.
|
|
32
34
|
>
|
|
33
35
|
> **v2.2** makes the control plane adaptive: authority is re-evaluated when the state it rested on changes, contradictions between independent claim sources are reported rather than resolved, and the architectural properties the design rests on are checked by code instead of asserted in prose.
|
|
@@ -92,6 +94,96 @@ When required evidence is unavailable, verification fails, identity is unknown,
|
|
|
92
94
|
|
|
93
95
|
---
|
|
94
96
|
|
|
97
|
+
## What v2.4 adds
|
|
98
|
+
|
|
99
|
+
v2.2 made the platform adaptive *above* the boundary: when watched state
|
|
100
|
+
changed, a decision was re-run. v2.4 makes **authority itself** adaptive. A
|
|
101
|
+
grant that is already in use can be narrowed, suspended, revalidated or
|
|
102
|
+
revoked, and the change is enforced on the next authorization — and, for
|
|
103
|
+
suspension, inside the commit transaction of one already in flight.
|
|
104
|
+
|
|
105
|
+
Aegis is off by default. `FirewallSDK(aegis_enabled=True)` opts in; with no
|
|
106
|
+
controller attached the adaptive gate abstains and v2.3 behaviour is exact.
|
|
107
|
+
|
|
108
|
+
### One deny-only seam
|
|
109
|
+
|
|
110
|
+
Nine modules, 5,382 lines, none of which imports `firewall.sdk`. Everything
|
|
111
|
+
Aegis does reaches the outside world through two points: `_gate_aegis`, the
|
|
112
|
+
ninth of eleven gates, which can only deny or abstain; and
|
|
113
|
+
`observe_authorization`, which the SDK calls *after* a decision exists.
|
|
114
|
+
Because the callback runs after, no Aegis state can be a precondition of the
|
|
115
|
+
allow it observes. Authority flows from the boundary into Aegis and never the
|
|
116
|
+
other way.
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
change -> classify -> KEEP | REVALIDATE | NARROW | SUSPEND | REVOKE
|
|
120
|
+
|
|
|
121
|
+
v
|
|
122
|
+
restriction written
|
|
123
|
+
|
|
|
124
|
+
v
|
|
125
|
+
FirewallSDK.authorize() -> _gate_aegis -> DENY
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
There is no arrow from Aegis to ALLOW. That absence is the design.
|
|
129
|
+
|
|
130
|
+
### The authority envelope
|
|
131
|
+
|
|
132
|
+
An `AuthorityEnvelope` is a bound on what a capability may still do, folded
|
|
133
|
+
across its delegation chain by a per-dimension greatest-lower-bound. Twelve
|
|
134
|
+
fields — patterns, tool, time window, constraint bounds, depth, its ceiling,
|
|
135
|
+
issuers, issuer trust, revocation, budget, chain membership, and a bottom
|
|
136
|
+
marker — and every dimension that bounds a request has a named enforcement
|
|
137
|
+
site at the boundary. A dimension with no enforcement site would be
|
|
138
|
+
decoration.
|
|
139
|
+
|
|
140
|
+
The theorem is one-directional and stated that way: if the envelope
|
|
141
|
+
**excludes** a request, the boundary denies it. The converse does not hold,
|
|
142
|
+
and the API is named so that misuse reads wrong — `excludes()` returns a
|
|
143
|
+
reason, `may_admit()` means "this envelope does not itself refuse", and
|
|
144
|
+
`__bool__` **raises** on an envelope, a grant, a preflight, a blast radius
|
|
145
|
+
and a classification, so `if preflight(...)` is an error rather than an
|
|
146
|
+
accidental allow.
|
|
147
|
+
|
|
148
|
+
### Seven states ordered by residual authority
|
|
149
|
+
|
|
150
|
+
`ISSUED`, `ACTIVE`, `REVALIDATING`, `NARROWED`, `SUSPENDED`, `REVOKED`,
|
|
151
|
+
`EXPIRED`. A transition is legal only if it does not increase residual
|
|
152
|
+
authority. `REVALIDATING -> ACTIVE` is the only edge that restores authority,
|
|
153
|
+
and it requires an `AuthorizationResult` that is allowed, reasoned
|
|
154
|
+
`authorized`, and traced to that capability's fingerprint. `REVOKED` and
|
|
155
|
+
`EXPIRED` are terminal and checked before the ordering rule, so no evidence
|
|
156
|
+
or clock change produces an edge out of either.
|
|
157
|
+
|
|
158
|
+
The state machine is a record and a legality check. It is deliberately **not**
|
|
159
|
+
an enforcement channel: the gate reads restrictions, not states, because
|
|
160
|
+
wiring the state model into the gate would make the authorization path depend
|
|
161
|
+
on a structure whose own updates require an authorization result.
|
|
162
|
+
|
|
163
|
+
### Unknown never becomes safe
|
|
164
|
+
|
|
165
|
+
An unrecognised trigger is `REVALIDATE`, not `KEEP` (which would make an
|
|
166
|
+
unknown event benign) and not `REVOKE` (which would make any unknown string a
|
|
167
|
+
denial-of-service lever). Nothing maps to `KEEP` at all — it is reachable
|
|
168
|
+
only by establishing five positive conditions, so "nothing changed" must be
|
|
169
|
+
shown rather than assumed. An unreadable budget is exhausted, not unlimited;
|
|
170
|
+
an unreadable restriction matches; unestablished issuer trust is `None`, not
|
|
171
|
+
`True`; an unresolvable chain yields the bottom envelope; a traversal that
|
|
172
|
+
exceeds its bounds is `UNANALYZABLE` rather than a partial answer presented
|
|
173
|
+
as complete. `UNKNOWN_NON_AUTHORIZATION` checks these exhaustively.
|
|
174
|
+
|
|
175
|
+
### What it does not claim
|
|
176
|
+
|
|
177
|
+
Envelope soundness runs in one direction. `canonical_allow_for` is a
|
|
178
|
+
structural check, not a cryptographic one — it bounds mistakes, not an
|
|
179
|
+
adversary already inside the process. The commit-time re-read covers
|
|
180
|
+
suspension only. The restriction cap trades availability for integrity:
|
|
181
|
+
sixteen narrowings drive a grant to `SUSPEND`, which is fail-closed and still
|
|
182
|
+
a lever. §16 of [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) is the full list,
|
|
183
|
+
and §17 maps each guarantee to the test file that establishes it.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
95
187
|
## What v2.3 changes
|
|
96
188
|
|
|
97
189
|
v2.3 adds no subsystem. Three requests that v2.2 allowed are now denied,
|
|
@@ -137,9 +229,9 @@ section, so deleting one fails rather than quietly shrinking the suite. See
|
|
|
137
229
|
### A strict invariant gate that can pass
|
|
138
230
|
|
|
139
231
|
`python -m firewall.invariants --strict` exited 2 on every invocation,
|
|
140
|
-
because
|
|
232
|
+
because seven of the fifteen invariants are claims about live state that a
|
|
141
233
|
source-only run never reaches. A gate that always fails is a gate that gets
|
|
142
|
-
removed, so those
|
|
234
|
+
removed, so those seven were effectively ungated in CI.
|
|
143
235
|
`firewall/invariants/exercise.py` builds the canonical estate through the
|
|
144
236
|
SDK's public API only, and CI now runs the source-only and exercised gates
|
|
145
237
|
as separate steps. What a green exercised run establishes is bounded to that
|
|
@@ -430,7 +522,7 @@ The architecture is designed around explicit security invariants.
|
|
|
430
522
|
- **A check that could not run is not a check that passed.**
|
|
431
523
|
- **Security failures default toward refusal rather than implicit trust.**
|
|
432
524
|
|
|
433
|
-
These are implementation properties of the system, not a claim that any deployment is universally secure.
|
|
525
|
+
These are implementation properties of the system, not a claim that any deployment is universally secure. Fifteen such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md) for the invariants themselves, [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md) for what a green gate run does and does not establish, and [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) for the four the authority control plane adds.
|
|
434
526
|
|
|
435
527
|
Where a property does **not** hold, it is stated rather than left to be inferred. A posture change is detected but does not by itself flip a verdict; `retire_key` is not containment for a stolen key, since a retired key's signatures keep verifying so that rotation does not invalidate capabilities in flight; an `amount_max` ceiling is per request, so two siblings each holding one can spend it twice unless a lineage budget is configured; and possession of a trusted signing key is authority, which no cryptography can undo. [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md) records each of these against the test that pins it.
|
|
436
528
|
|
|
@@ -516,9 +608,15 @@ The authorization path evaluates the security conditions required by the capabil
|
|
|
516
608
|
|
|
517
609
|
## Command surface
|
|
518
610
|
|
|
611
|
+
v2.4 adds no new CLI subcommands. One existing command changes what it
|
|
612
|
+
prints: `firewall delegate-authorize` now labels an allow it reached without
|
|
613
|
+
an attached `sdk_provider` as `ALLOWED (relationship only)` and says not to
|
|
614
|
+
enforce on it. The exit status stays 0 — the question asked was answered
|
|
615
|
+
affirmatively — but the answer no longer overstates itself.
|
|
616
|
+
|
|
519
617
|
v2.3 adds no new CLI subcommands. It adds one flag to the invariant
|
|
520
618
|
checker: `python -m firewall.invariants --exercise --strict` builds the
|
|
521
|
-
canonical estate so that all
|
|
619
|
+
canonical estate so that all fifteen invariants can be reached, which makes
|
|
522
620
|
`--strict` a gate that can pass and is therefore worth failing.
|
|
523
621
|
|
|
524
622
|
v2.2 adds no new CLI subcommands. Its one new entry point is the invariant
|
|
@@ -571,6 +669,35 @@ python -m firewall.benchmarks
|
|
|
571
669
|
|
|
572
670
|
The repository contains unit, integration, adversarial, hardening, evidence, UI/API, benchmark, and research tests.
|
|
573
671
|
|
|
672
|
+
The v2.4 test surface adds 384 tests across eleven files — 4,087 in the suite
|
|
673
|
+
as a whole, on Python 3.10, 3.11 and 3.12 — including stateful security-state
|
|
674
|
+
fuzzing, eight named concurrency races, and an integration-boundary sweep.
|
|
675
|
+
Among the properties covered:
|
|
676
|
+
|
|
677
|
+
- no path from adaptive analysis to an allow, and no Aegis module able to
|
|
678
|
+
construct an `AuthorizationResult`
|
|
679
|
+
- every forbidden transition refused individually: out of `REVOKED`, out of
|
|
680
|
+
`EXPIRED`, `NARROWED` to wider, `SUSPENDED` to `ACTIVE` without a canonical
|
|
681
|
+
allow, and a child exceeding its parent
|
|
682
|
+
- an envelope that excludes a request always accompanied by a boundary that
|
|
683
|
+
denies it
|
|
684
|
+
- no field of the envelope widening under delegation or attenuation
|
|
685
|
+
- a `nan` bound bounding nothing being denied, while an `inf` bound still
|
|
686
|
+
means unbounded
|
|
687
|
+
- a 400-digit integer producing a decision rather than an exception out of
|
|
688
|
+
`authorize()`
|
|
689
|
+
- every authorization-reachable Aegis method total, including against an
|
|
690
|
+
injected hostile controller
|
|
691
|
+
- a restriction written mid-flight being observed, and a suspension being
|
|
692
|
+
caught inside the commit transaction
|
|
693
|
+
- `__bool__` raising on every analysis object, so a truthiness test cannot
|
|
694
|
+
become an allow
|
|
695
|
+
- every integration surface reaching the canonical boundary, with no local
|
|
696
|
+
allow anywhere
|
|
697
|
+
- no dataclass field in the package carrying a default that Python 3.11
|
|
698
|
+
rejects, so a defect that hides on the development interpreter cannot wait
|
|
699
|
+
for CI to find it
|
|
700
|
+
|
|
574
701
|
The v2.3 test surface adds the thirteen-question self-attack suite, which
|
|
575
702
|
covers, among other properties:
|
|
576
703
|
|
|
@@ -697,6 +824,10 @@ See [`SECURITY.md`](SECURITY.md) for the project's security reporting policy.
|
|
|
697
824
|
|
|
698
825
|
Detailed specifications are maintained in the repository:
|
|
699
826
|
|
|
827
|
+
- `docs/v2.4-aegis.md`
|
|
828
|
+
- `docs/v2.4-aegis-design.md`
|
|
829
|
+
- `docs/v2.4-performance.md`
|
|
830
|
+
- `docs/v2.4-migration.md`
|
|
700
831
|
- `docs/v2.3-security-corrections.md`
|
|
701
832
|
- `docs/v2.3-self-attack.md`
|
|
702
833
|
- `docs/v2.3-invariant-gate.md`
|
|
@@ -53,6 +53,16 @@ firewall/adapters/generic.py
|
|
|
53
53
|
firewall/adapters/normalize.py
|
|
54
54
|
firewall/adapters/openai.py
|
|
55
55
|
firewall/adversarial/__init__.py
|
|
56
|
+
firewall/aegis/__init__.py
|
|
57
|
+
firewall/aegis/blast.py
|
|
58
|
+
firewall/aegis/controller.py
|
|
59
|
+
firewall/aegis/decay.py
|
|
60
|
+
firewall/aegis/envelope.py
|
|
61
|
+
firewall/aegis/explain.py
|
|
62
|
+
firewall/aegis/preflight.py
|
|
63
|
+
firewall/aegis/response.py
|
|
64
|
+
firewall/aegis/restriction.py
|
|
65
|
+
firewall/aegis/state.py
|
|
56
66
|
firewall/agents/__init__.py
|
|
57
67
|
firewall/agents/adapters.py
|
|
58
68
|
firewall/agents/base.py
|
|
@@ -186,4 +196,15 @@ tests/test_v2_3_duplicate_concepts.py
|
|
|
186
196
|
tests/test_v2_3_intel_coordination.py
|
|
187
197
|
tests/test_v2_3_invariant_gate.py
|
|
188
198
|
tests/test_v2_3_policy_analysis.py
|
|
189
|
-
tests/test_v2_3_self_attack.py
|
|
199
|
+
tests/test_v2_3_self_attack.py
|
|
200
|
+
tests/test_v2_4_aegis_adversarial.py
|
|
201
|
+
tests/test_v2_4_aegis_concurrency.py
|
|
202
|
+
tests/test_v2_4_aegis_controller.py
|
|
203
|
+
tests/test_v2_4_aegis_corrections.py
|
|
204
|
+
tests/test_v2_4_aegis_fuzz.py
|
|
205
|
+
tests/test_v2_4_aegis_gate.py
|
|
206
|
+
tests/test_v2_4_aegis_state.py
|
|
207
|
+
tests/test_v2_4_attenuation_narrowing.py
|
|
208
|
+
tests/test_v2_4_benchmarks.py
|
|
209
|
+
tests/test_v2_4_envelope_soundness.py
|
|
210
|
+
tests/test_v2_4_integration_boundary.py
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""v2.1 Agent-to-Agent Zero Trust (firewall.a2a).
|
|
2
|
+
|
|
3
|
+
Authenticated agent relationships: mutual cryptographic authentication,
|
|
4
|
+
scoped permissions, task-bound delegation, capability attenuation,
|
|
5
|
+
delegation-chain verification, expiring delegation, recursive
|
|
6
|
+
revocation, and cross-agent authorization decisions.
|
|
7
|
+
|
|
8
|
+
A decision from :meth:`~firewall.a2a.auth.AgentToAgent.authorize` reports
|
|
9
|
+
what established it. ``basis == BASIS_CANONICAL`` (equivalently
|
|
10
|
+
``decision.is_canonical``) means ``FirewallSDK.authorize()`` ran and
|
|
11
|
+
allowed the action; ``BASIS_RELATIONSHIP_ONLY`` means only this module's
|
|
12
|
+
own relationship bookkeeping did, which is necessary for a cross-agent
|
|
13
|
+
call and not sufficient to authorize one.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from firewall.a2a.auth import (
|
|
17
|
+
BASIS_CANONICAL,
|
|
18
|
+
BASIS_RELATIONSHIP_ONLY,
|
|
19
|
+
BASIS_UNAVAILABLE,
|
|
20
|
+
A2AError,
|
|
21
|
+
A2ADecision,
|
|
22
|
+
AgentRelationship,
|
|
23
|
+
AgentToAgent,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"BASIS_CANONICAL",
|
|
28
|
+
"BASIS_RELATIONSHIP_ONLY",
|
|
29
|
+
"BASIS_UNAVAILABLE",
|
|
30
|
+
"A2AError",
|
|
31
|
+
"A2ADecision",
|
|
32
|
+
"AgentRelationship",
|
|
33
|
+
"AgentToAgent",
|
|
34
|
+
]
|