ag2-tealtiger 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. ag2_tealtiger-0.1.0/.gitignore +167 -0
  2. ag2_tealtiger-0.1.0/LICENSE +17 -0
  3. ag2_tealtiger-0.1.0/PKG-INFO +109 -0
  4. ag2_tealtiger-0.1.0/README.md +74 -0
  5. ag2_tealtiger-0.1.0/examples/governed_groupchat.py +225 -0
  6. ag2_tealtiger-0.1.0/examples/policy_enforcement.py +234 -0
  7. ag2_tealtiger-0.1.0/examples/zero_config_observe.py +209 -0
  8. ag2_tealtiger-0.1.0/pyproject.toml +99 -0
  9. ag2_tealtiger-0.1.0/tests/__init__.py +0 -0
  10. ag2_tealtiger-0.1.0/tests/conftest.py +431 -0
  11. ag2_tealtiger-0.1.0/tests/strategies.py +294 -0
  12. ag2_tealtiger-0.1.0/tests/test_audit_agent.py +411 -0
  13. ag2_tealtiger-0.1.0/tests/test_audit_export.py +405 -0
  14. ag2_tealtiger-0.1.0/tests/test_budget_manager.py +349 -0
  15. ag2_tealtiger-0.1.0/tests/test_decision_manager.py +385 -0
  16. ag2_tealtiger-0.1.0/tests/test_error_handling.py +618 -0
  17. ag2_tealtiger-0.1.0/tests/test_exceptions.py +236 -0
  18. ag2_tealtiger-0.1.0/tests/test_governed_groupchat.py +553 -0
  19. ag2_tealtiger-0.1.0/tests/test_guard.py +362 -0
  20. ag2_tealtiger-0.1.0/tests/test_guard_budget.py +490 -0
  21. ag2_tealtiger-0.1.0/tests/test_guard_freeze.py +483 -0
  22. ag2_tealtiger-0.1.0/tests/test_guard_message_governance.py +555 -0
  23. ag2_tealtiger-0.1.0/tests/test_guard_observe_mode.py +904 -0
  24. ag2_tealtiger-0.1.0/tests/test_guard_policy_mode.py +550 -0
  25. ag2_tealtiger-0.1.0/tests/test_idempotency.py +107 -0
  26. ag2_tealtiger-0.1.0/tests/test_integration.py +833 -0
  27. ag2_tealtiger-0.1.0/tests/test_package_exports.py +131 -0
  28. ag2_tealtiger-0.1.0/tests/test_pii.py +311 -0
  29. ag2_tealtiger-0.1.0/tests/test_property_12_fail_closed.py +247 -0
  30. ag2_tealtiger-0.1.0/tests/test_property_13_fail_open.py +273 -0
  31. ag2_tealtiger-0.1.0/tests/test_property_14_denial_parseability.py +196 -0
  32. ag2_tealtiger-0.1.0/tests/test_property_17_budget_enforcement.py +175 -0
  33. ag2_tealtiger-0.1.0/tests/test_property_18_budget_warning.py +289 -0
  34. ag2_tealtiger-0.1.0/tests/test_property_19_budget_arithmetic.py +141 -0
  35. ag2_tealtiger-0.1.0/tests/test_property_1_audit_completeness.py +407 -0
  36. ag2_tealtiger-0.1.0/tests/test_property_20_cost_monotonicity.py +156 -0
  37. ag2_tealtiger-0.1.0/tests/test_property_24_decision_expiry.py +215 -0
  38. ag2_tealtiger-0.1.0/tests/test_property_25_revalidation.py +362 -0
  39. ag2_tealtiger-0.1.0/tests/test_property_2_observe_passthrough.py +286 -0
  40. ag2_tealtiger-0.1.0/tests/test_property_3_frozen_denial.py +238 -0
  41. ag2_tealtiger-0.1.0/tests/test_property_4_freeze_roundtrip.py +287 -0
  42. ag2_tealtiger-0.1.0/tests/test_property_5_agent_isolation.py +256 -0
  43. ag2_tealtiger-0.1.0/tests/test_property_7_decision_uniqueness.py +228 -0
  44. ag2_tealtiger-0.1.0/tests/test_property_8_idempotency_key_determinism.py +65 -0
  45. ag2_tealtiger-0.1.0/tests/test_property_9_retry_idempotency.py +307 -0
  46. ag2_tealtiger-0.1.0/tests/test_property_conversation_id_stability.py +190 -0
  47. ag2_tealtiger-0.1.0/tests/test_property_groupchat.py +730 -0
  48. ag2_tealtiger-0.1.0/tests/test_property_nested_chat_correlation.py +302 -0
  49. ag2_tealtiger-0.1.0/tests/test_property_params_hash.py +108 -0
  50. ag2_tealtiger-0.1.0/tests/test_resolve_refer.py +544 -0
  51. ag2_tealtiger-0.1.0/tests/test_teec_builder.py +247 -0
  52. ag2_tealtiger-0.1.0/tests/test_types.py +437 -0
@@ -0,0 +1,167 @@
1
+ # Dependencies
2
+ node_modules/
3
+ # package-lock.json committed for Scorecard Pinned-Dependencies compliance
4
+
5
+ # Build output
6
+ dist/
7
+ build/
8
+ storybook-static/
9
+ *.tsbuildinfo
10
+
11
+ # Test coverage
12
+ coverage/
13
+ .nyc_output/
14
+ htmlcov/
15
+ .coverage
16
+ .hypothesis/
17
+
18
+ # Environment variables
19
+ .env
20
+ .env.local
21
+ .env.*.local
22
+
23
+ # IDE
24
+ .vscode/
25
+ .idea/
26
+ *.swp
27
+ *.swo
28
+ *~
29
+
30
+ # Logs
31
+ logs/
32
+ *.log
33
+ npm-debug.log*
34
+ yarn-debug.log*
35
+ yarn-error.log*
36
+ lerna-debug.log*
37
+
38
+ # Temporary files
39
+ tmp/
40
+ temp/
41
+ *.tmp
42
+
43
+ # OS files
44
+ Thumbs.db
45
+ .DS_Store
46
+
47
+ # ── SENSITIVE / INTERNAL — DO NOT COMMIT ─────────────────────────
48
+
49
+ # Internal documentation and planning
50
+ agentguard-internal-docs/
51
+ .kiro/
52
+
53
+ # Internal analysis and strategy docs (root-level)
54
+ *-INTERNAL-*.md
55
+ *-STRATEGY.md
56
+ *-ANALYSIS.md
57
+ *-FEASIBILITY.md
58
+ *-ALIGNMENT.md
59
+ BRANDING-*.md
60
+ CONTAINERIZATION-*.md
61
+ DEPLOYMENT-*.md
62
+ KUBERNETES-*.md
63
+ PHASE-*.md
64
+ POLICY-REGISTRY-*.md
65
+ POST-LAUNCH-*.md
66
+ TYPESCRIPT-PYTHON-PARITY-*.md
67
+ TEALTIGER-*-ANALYSIS.md
68
+ TEALTIGER-*-ALIGNMENT.md
69
+ TEALTIGER-VS-*.md
70
+ TEALTIGER-PLAYGROUND-*.md
71
+ TEALTIGER-SERVERLESS-*.md
72
+ TEALTIGER-SDK-CONTAINERIZATION-*.md
73
+ TEALTIGER-CLINE-*.md
74
+ DOCKER_README.md
75
+ DOCKER-TEST-RESULTS.md
76
+
77
+ # Internal scripts
78
+ scripts/*
79
+ !scripts/validate-policy.ts
80
+ !scripts/test-validate-policy.ts
81
+ push-examples.sh
82
+ push-examples.bat
83
+ cleanup-*.bat
84
+ copy-*.bat
85
+ final-*.bat
86
+ remove-*.bat
87
+ update-*.bat
88
+ CLEANUP-ACTION-REQUIRED.txt
89
+
90
+ # Legacy / deprecated directories
91
+ agentguard/
92
+ agentguard-landing/
93
+ agent-guard-sdk-public/
94
+ test-deprecated/
95
+ test-install/
96
+ tealtiger/
97
+
98
+ # Infrastructure (should be in separate repos)
99
+ ansible/
100
+ helm/
101
+ helm-charts-repo/
102
+ terraform-aws-tealtiger-repo/
103
+ docker/
104
+ database/
105
+ Dockerfile
106
+ docker-compose*.yml
107
+
108
+ # Internal source (monorepo backend — not for public SDK repo)
109
+ src/
110
+ !dashboard/governance-feed/src/
111
+ !dashboard/governance-feed/src/**
112
+
113
+ # Postman collections
114
+ postman/
115
+
116
+ # Blog drafts and diagrams (published separately)
117
+ blog/
118
+
119
+ # Internal governance / SOT (separate repo)
120
+ TealTiger-SOT/
121
+ tealtiger-sot-repo/
122
+
123
+ # Landing pages and websites (separate repos)
124
+ TealTiger-LandingPage/
125
+ TealTiger-Blog/
126
+ TealTiger-Cookbook/
127
+ TealTiger-MCP/
128
+ tealtiger-docs-repo/
129
+ tealtiger-docs-source/
130
+ tealtiger-docs-target/
131
+ tealtiger-action-repo/
132
+
133
+ # Staging directories (development only)
134
+ staging-py/
135
+ staging-ts/
136
+
137
+ # Package build artifacts
138
+ package/
139
+
140
+ # HTML coverage reports
141
+ htmlcov/
142
+
143
+ # Vertex extension (internal)
144
+ packages/tealtiger-vertex-extension/
145
+
146
+ # GitHub internal guides
147
+ .github/ENABLE-*.md
148
+ .github/SAMPLE_ISSUES.md
149
+ .github/SOCIAL-PREVIEW-SPEC.md
150
+
151
+ # Internal workflow files
152
+ .github/workflows/docker-variants.yml
153
+ .github/workflows/publish-circleci-orb.yml
154
+ .github/workflows/v1-1-0-launch.yml
155
+
156
+ # Build configs (monorepo-level, not needed by SDK users)
157
+ jest.config.js
158
+ .eslintrc.js
159
+
160
+ # Dashboard package source and lockfile
161
+ !packages/tealtiger-dashboard/src/
162
+ !packages/tealtiger-dashboard/src/**
163
+ !packages/tealtiger-dashboard/package-lock.json
164
+
165
+ # Misc internal files
166
+ .cleanup-sensitive-files.txt
167
+ *.bat
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 TealTiger Team
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: ag2-tealtiger
3
+ Version: 0.1.0
4
+ Summary: Deterministic governance adapter for AG2 (AutoGen fork) — policy enforcement, PII detection, cost tracking, per-agent kill switch, governed speaker selection, and structured audit evidence. No LLM in the governance path.
5
+ Project-URL: Homepage, https://tealtiger.ai
6
+ Project-URL: Documentation, https://docs.tealtiger.ai/integrations/ag2
7
+ Project-URL: Repository, https://github.com/agentguard-ai/tealtiger
8
+ Project-URL: Issues, https://github.com/agentguard-ai/tealtiger/issues
9
+ Project-URL: Changelog, https://github.com/agentguard-ai/tealtiger/blob/main/packages/ag2-tealtiger/CHANGELOG.md
10
+ Author-email: TealTiger Team <reachout@tealtiger.ai>
11
+ License: Apache-2.0
12
+ License-File: LICENSE
13
+ Keywords: ag2,ai-agents,audit,autogen,cost-tracking,deterministic,governance,groupchat,guardrails,kill-switch,llm,multi-agent,pii,policy,security,tealtiger
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: Apache Software License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Security
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: ag2>=0.4.0
27
+ Requires-Dist: tealtiger>=1.3.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: hypothesis>=6.0.0; extra == 'dev'
30
+ Requires-Dist: mypy>=1.5.0; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
32
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # ag2-tealtiger
37
+
38
+ Deterministic governance adapter for [AG2](https://github.com/ag2ai/ag2) (AutoGen fork) — policy enforcement, PII detection, cost tracking, per-agent kill switch, governed speaker selection, and structured audit evidence.
39
+
40
+ **No LLM in the governance path.** All policy evaluation is deterministic, adding <5ms latency.
41
+
42
+ [![PyPI](https://img.shields.io/pypi/v/ag2-tealtiger)](https://pypi.org/project/ag2-tealtiger/)
43
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
44
+ [![Python](https://img.shields.io/pypi/pyversions/ag2-tealtiger)](https://pypi.org/project/ag2-tealtiger/)
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install ag2-tealtiger
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ### Zero-Config Observe Mode
55
+
56
+ ```python
57
+ from ag2_tealtiger import TealTigerGuard
58
+
59
+ guard = TealTigerGuard() # No engine = observe mode
60
+ guard.attach(my_agent)
61
+ # Cost, PII, and tool usage tracked automatically. Nothing blocked.
62
+ ```
63
+
64
+ Or the convenience subclass:
65
+
66
+ ```python
67
+ from ag2_tealtiger import TealTigerAuditAgent
68
+
69
+ agent = TealTigerAuditAgent(name="coder") # Zero config, observe mode built-in
70
+ ```
71
+
72
+ ### Policy Enforcement
73
+
74
+ ```python
75
+ from ag2_tealtiger import TealTigerGuard, GovernanceMode
76
+
77
+ guard = TealTigerGuard(engine=my_engine, mode=GovernanceMode.ENFORCE)
78
+ guard.attach(agent)
79
+ # DENY blocks the call with a structured denial message
80
+ ```
81
+
82
+ ### Governed GroupChat
83
+
84
+ ```python
85
+ from ag2_tealtiger import GovernedGroupChat, TealTigerGuard, GovernanceMode
86
+
87
+ guard = TealTigerGuard(engine=engine, mode=GovernanceMode.ENFORCE)
88
+ group_chat = GovernedGroupChat(agents=[agent1, agent2, agent3], guard=guard)
89
+ speaker = group_chat.select_speaker(last_speaker=agent1)
90
+ ```
91
+
92
+ ## Features
93
+
94
+ | Feature | Observe | Monitor | Enforce |
95
+ |---------|:-------:|:-------:|:-------:|
96
+ | Cost tracking per agent | ✅ | ✅ | ✅ |
97
+ | PII detection in tool args | ✅ | ✅ | ✅ |
98
+ | Structured audit entries (TEEC) | ✅ | ✅ | ✅ |
99
+ | Per-agent freeze/unfreeze | ✅ | ✅ | ✅ |
100
+ | Policy evaluation | — | ✅ (log) | ✅ (block) |
101
+ | Budget enforcement | — | ✅ (log) | ✅ (block) |
102
+ | REFER escalation | — | ✅ | ✅ |
103
+ | Inter-agent message governance | ✅ | ✅ | ✅ |
104
+ | Governed speaker selection | ✅ | ✅ | ✅ |
105
+ | JSONL audit export | ✅ | ✅ | ✅ |
106
+
107
+ ## License
108
+
109
+ Apache-2.0
@@ -0,0 +1,74 @@
1
+ # ag2-tealtiger
2
+
3
+ Deterministic governance adapter for [AG2](https://github.com/ag2ai/ag2) (AutoGen fork) — policy enforcement, PII detection, cost tracking, per-agent kill switch, governed speaker selection, and structured audit evidence.
4
+
5
+ **No LLM in the governance path.** All policy evaluation is deterministic, adding <5ms latency.
6
+
7
+ [![PyPI](https://img.shields.io/pypi/v/ag2-tealtiger)](https://pypi.org/project/ag2-tealtiger/)
8
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
9
+ [![Python](https://img.shields.io/pypi/pyversions/ag2-tealtiger)](https://pypi.org/project/ag2-tealtiger/)
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install ag2-tealtiger
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ### Zero-Config Observe Mode
20
+
21
+ ```python
22
+ from ag2_tealtiger import TealTigerGuard
23
+
24
+ guard = TealTigerGuard() # No engine = observe mode
25
+ guard.attach(my_agent)
26
+ # Cost, PII, and tool usage tracked automatically. Nothing blocked.
27
+ ```
28
+
29
+ Or the convenience subclass:
30
+
31
+ ```python
32
+ from ag2_tealtiger import TealTigerAuditAgent
33
+
34
+ agent = TealTigerAuditAgent(name="coder") # Zero config, observe mode built-in
35
+ ```
36
+
37
+ ### Policy Enforcement
38
+
39
+ ```python
40
+ from ag2_tealtiger import TealTigerGuard, GovernanceMode
41
+
42
+ guard = TealTigerGuard(engine=my_engine, mode=GovernanceMode.ENFORCE)
43
+ guard.attach(agent)
44
+ # DENY blocks the call with a structured denial message
45
+ ```
46
+
47
+ ### Governed GroupChat
48
+
49
+ ```python
50
+ from ag2_tealtiger import GovernedGroupChat, TealTigerGuard, GovernanceMode
51
+
52
+ guard = TealTigerGuard(engine=engine, mode=GovernanceMode.ENFORCE)
53
+ group_chat = GovernedGroupChat(agents=[agent1, agent2, agent3], guard=guard)
54
+ speaker = group_chat.select_speaker(last_speaker=agent1)
55
+ ```
56
+
57
+ ## Features
58
+
59
+ | Feature | Observe | Monitor | Enforce |
60
+ |---------|:-------:|:-------:|:-------:|
61
+ | Cost tracking per agent | ✅ | ✅ | ✅ |
62
+ | PII detection in tool args | ✅ | ✅ | ✅ |
63
+ | Structured audit entries (TEEC) | ✅ | ✅ | ✅ |
64
+ | Per-agent freeze/unfreeze | ✅ | ✅ | ✅ |
65
+ | Policy evaluation | — | ✅ (log) | ✅ (block) |
66
+ | Budget enforcement | — | ✅ (log) | ✅ (block) |
67
+ | REFER escalation | — | ✅ | ✅ |
68
+ | Inter-agent message governance | ✅ | ✅ | ✅ |
69
+ | Governed speaker selection | ✅ | ✅ | ✅ |
70
+ | JSONL audit export | ✅ | ✅ | ✅ |
71
+
72
+ ## License
73
+
74
+ Apache-2.0
@@ -0,0 +1,225 @@
1
+ """Example: GovernedGroupChat with Speaker Selection.
2
+
3
+ Demonstrates how GovernedGroupChat applies governance policies to speaker
4
+ selection in multi-agent conversations. In a governed group chat:
5
+ - Frozen agents are skipped without invoking TealEngine
6
+ - Over-budget agents are skipped without invoking TealEngine
7
+ - Remaining candidates are evaluated via TealEngine before getting a turn
8
+ - If all speakers are denied, the round terminates with ALL_SPEAKERS_DENIED
9
+ - REFER decisions suspend an agent while others continue
10
+
11
+ This is the pattern for multi-agent orchestration with governance guardrails.
12
+
13
+ Requirements:
14
+ pip install ag2-tealtiger
15
+
16
+ Usage:
17
+ python governed_groupchat.py
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ from typing import Any
23
+
24
+
25
+ # ─── Inline mocks so the example runs without real AG2/TealEngine ────────────
26
+
27
+
28
+ class MockConversableAgent:
29
+ """Minimal AG2 ConversableAgent stub for demonstration."""
30
+
31
+ def __init__(self, name: str, **kwargs):
32
+ self.name = name
33
+ self._reply_funcs: list[dict] = []
34
+
35
+ def register_reply(self, trigger=None, reply_func=None, position=0, **kwargs):
36
+ self._reply_funcs.insert(position, {
37
+ "trigger": trigger,
38
+ "reply_func": reply_func,
39
+ })
40
+
41
+ def __repr__(self) -> str:
42
+ return f"Agent({self.name})"
43
+
44
+
45
+ class MockTealEngine:
46
+ """Mock TealEngine with per-agent speaker selection policies.
47
+
48
+ Simulates governance evaluation for speaker candidacy. In production,
49
+ replace with a real TealEngine and configure policies that determine
50
+ which agents can speak based on role, context, or risk level.
51
+
52
+ from tealtiger import TealEngine
53
+ engine = TealEngine(policies=[speaker_policy, role_policy, ...])
54
+ """
55
+
56
+ def __init__(self, denied_speakers: list[str] | None = None):
57
+ """
58
+ Args:
59
+ denied_speakers: List of agent names that should be denied
60
+ when evaluated for speaker candidacy.
61
+ """
62
+ self.denied_speakers = denied_speakers or []
63
+ self.evaluation_count = 0
64
+
65
+ def evaluate(
66
+ self,
67
+ tool_name: str = "",
68
+ tool_args: dict[str, Any] | None = None,
69
+ context: dict[str, Any] | None = None,
70
+ **kwargs: Any,
71
+ ) -> dict[str, Any]:
72
+ """Evaluate a speaker candidacy or tool call."""
73
+ self.evaluation_count += 1
74
+ agent_id = (context or {}).get("agent_id", "")
75
+
76
+ if agent_id in self.denied_speakers:
77
+ return {
78
+ "action": "DENY",
79
+ "reason": f"Agent '{agent_id}' not authorized to speak in this round",
80
+ "reason_codes": ["SPEAKER_DENIED", "ROLE_RESTRICTION"],
81
+ "risk_score": 60,
82
+ }
83
+
84
+ return {
85
+ "action": "ALLOW",
86
+ "reason": "Agent authorized for this conversation round",
87
+ "reason_codes": ["SPEAKER_ALLOWED"],
88
+ "risk_score": 0,
89
+ }
90
+
91
+
92
+ # ─── Example: GovernedGroupChat Speaker Selection ────────────────────────────
93
+
94
+
95
+ def main() -> None:
96
+ """Demonstrate GovernedGroupChat with governance-enforced speaker selection."""
97
+ from ag2_tealtiger import TealTigerGuard, GovernedGroupChat, GovernanceMode
98
+
99
+ print("=" * 65)
100
+ print(" ag2-tealtiger: GovernedGroupChat Speaker Selection")
101
+ print("=" * 65)
102
+
103
+ # ── Setup: Create agents for a coding team ────────────────────────────
104
+ planner = MockConversableAgent(name="planner")
105
+ coder = MockConversableAgent(name="coder")
106
+ reviewer = MockConversableAgent(name="reviewer")
107
+ executor = MockConversableAgent(name="executor")
108
+
109
+ # Create engine that denies the executor from speaking (role restriction)
110
+ engine = MockTealEngine(denied_speakers=["executor"])
111
+
112
+ # Create guard in ENFORCE mode
113
+ guard = TealTigerGuard(engine=engine, mode=GovernanceMode.ENFORCE)
114
+
115
+ # Attach guard to all agents (for tool call governance)
116
+ for agent in [planner, coder, reviewer, executor]:
117
+ guard.attach(agent)
118
+
119
+ # Create the governed group chat
120
+ group_chat = GovernedGroupChat(
121
+ agents=[planner, coder, reviewer, executor],
122
+ guard=guard,
123
+ max_round=10,
124
+ speaker_selection_method="round_robin",
125
+ )
126
+
127
+ # ── Scenario 1: Normal speaker selection with policy filtering ────────
128
+ print("\n--- Scenario 1: Policy-based speaker selection ---\n")
129
+
130
+ selected = group_chat.select_speaker(last_speaker=planner)
131
+ print(f" After planner spoke → selected: {selected}")
132
+ print(f" (executor is denied by policy, so it gets skipped)")
133
+
134
+ selected = group_chat.select_speaker(last_speaker=coder)
135
+ print(f" After coder spoke → selected: {selected}")
136
+
137
+ # ── Scenario 2: Frozen agent is skipped without engine call ───────────
138
+ print("\n--- Scenario 2: Frozen agent skipped ---\n")
139
+
140
+ guard.freeze("coder")
141
+ print(f" Froze 'coder' — is_frozen: {guard.is_frozen('coder')}")
142
+
143
+ # Engine should NOT be called for frozen agents
144
+ engine.evaluation_count = 0
145
+ selected = group_chat.select_speaker(last_speaker=planner)
146
+ print(f" Selected speaker: {selected}")
147
+ print(f" Engine evaluations: {engine.evaluation_count} (frozen agent skipped)")
148
+
149
+ guard.unfreeze("coder")
150
+ print(f" Unfroze 'coder'")
151
+
152
+ # ── Scenario 3: Over-budget agent is skipped ──────────────────────────
153
+ print("\n--- Scenario 3: Over-budget agent skipped ---\n")
154
+
155
+ # Set a low budget for reviewer and exceed it
156
+ guard.set_budget("reviewer", limit=0.0001)
157
+ # Simulate cost accumulation to exceed budget
158
+ guard._budget_manager.track_cost("reviewer", 0.001)
159
+
160
+ engine.evaluation_count = 0
161
+ selected = group_chat.select_speaker(last_speaker=planner)
162
+ print(f" Selected speaker: {selected}")
163
+ print(f" (reviewer over budget, executor denied by policy)")
164
+
165
+ # Check the speaker selection audit
166
+ audit = group_chat.speaker_selection_audit
167
+ latest = audit[-1] if audit else None
168
+ if latest:
169
+ print(f"\n Speaker selection audit (last round):")
170
+ for candidate in latest.candidates_evaluated:
171
+ print(
172
+ f" {candidate['agent_id']:<10} → "
173
+ f"{candidate['decision']:<6} "
174
+ f"reason={candidate.get('reason', 'N/A')}"
175
+ )
176
+
177
+ # ── Scenario 4: All speakers denied → round terminates ───────────────
178
+ print("\n--- Scenario 4: All speakers denied ---\n")
179
+
180
+ # Freeze everyone except executor (who is policy-denied)
181
+ guard.freeze("planner")
182
+ guard.freeze("coder")
183
+ # reviewer is already over budget
184
+
185
+ selected = group_chat.select_speaker(last_speaker=planner)
186
+ print(f" Selected speaker: {selected}")
187
+ print(f" (All agents either frozen, over-budget, or policy-denied)")
188
+
189
+ # Check the termination audit entry
190
+ audit = group_chat.speaker_selection_audit
191
+ latest = audit[-1] if audit else None
192
+ if latest:
193
+ print(f" Reason codes: {latest.reason_codes}")
194
+
195
+ # Clean up freezes
196
+ guard.unfreeze("planner")
197
+ guard.unfreeze("coder")
198
+ guard.reset_budget("reviewer")
199
+
200
+ # ── Scenario 5: Full audit trail across selection rounds ──────────────
201
+ print("\n--- Speaker Selection Audit Trail ---\n")
202
+
203
+ all_audits = group_chat.speaker_selection_audit
204
+ print(f" Total selection rounds: {len(all_audits)}")
205
+ for i, entry in enumerate(all_audits, 1):
206
+ print(
207
+ f" Round {i}: selected={entry.selected_speaker or 'NONE':<10} "
208
+ f"candidates={len(entry.candidates_evaluated)} "
209
+ f"codes={entry.reason_codes}"
210
+ )
211
+
212
+ # ── Key takeaways ─────────────────────────────────────────────────────
213
+ print("\n" + "=" * 65)
214
+ print(" Key points:")
215
+ print(" • GovernedGroupChat evaluates speakers against policies")
216
+ print(" • Frozen/over-budget agents skipped without engine calls")
217
+ print(" • Policy-denied agents skipped, next candidate evaluated")
218
+ print(" • ALL_SPEAKERS_DENIED terminates the round safely")
219
+ print(" • Full speaker selection audit trail per round")
220
+ print(" • Per-agent governance isolation in multi-agent groups")
221
+ print("=" * 65)
222
+
223
+
224
+ if __name__ == "__main__":
225
+ main()