tollgate 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl
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.
- tollgate/__init__.py +7 -2
- tollgate/grants.py +149 -0
- tollgate/tower.py +59 -37
- tollgate/types.py +25 -0
- {tollgate-1.0.3.dist-info → tollgate-1.0.5.dist-info}/METADATA +25 -1
- {tollgate-1.0.3.dist-info → tollgate-1.0.5.dist-info}/RECORD +8 -7
- {tollgate-1.0.3.dist-info → tollgate-1.0.5.dist-info}/WHEEL +0 -0
- {tollgate-1.0.3.dist-info → tollgate-1.0.5.dist-info}/licenses/LICENSE +0 -0
tollgate/__init__.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from .approvals import (
|
|
2
|
-
ApprovalOutcome,
|
|
3
2
|
ApprovalStore,
|
|
4
3
|
Approver,
|
|
5
4
|
AsyncQueueApprover,
|
|
@@ -15,23 +14,26 @@ from .exceptions import (
|
|
|
15
14
|
TollgateDenied,
|
|
16
15
|
TollgateError,
|
|
17
16
|
)
|
|
17
|
+
from .grants import GrantStore, InMemoryGrantStore
|
|
18
18
|
from .helpers import guard, wrap_tool
|
|
19
19
|
from .policy import PolicyEvaluator, YamlPolicyEvaluator
|
|
20
20
|
from .registry import ToolRegistry
|
|
21
21
|
from .tower import ControlTower
|
|
22
22
|
from .types import (
|
|
23
23
|
AgentContext,
|
|
24
|
+
ApprovalOutcome,
|
|
24
25
|
AuditEvent,
|
|
25
26
|
Decision,
|
|
26
27
|
DecisionType,
|
|
27
28
|
Effect,
|
|
29
|
+
Grant,
|
|
28
30
|
Intent,
|
|
29
31
|
NormalizedToolCall,
|
|
30
32
|
Outcome,
|
|
31
33
|
ToolRequest,
|
|
32
34
|
)
|
|
33
35
|
|
|
34
|
-
__version__ = "1.0.
|
|
36
|
+
__version__ = "1.0.5"
|
|
35
37
|
|
|
36
38
|
__all__ = [
|
|
37
39
|
"ControlTower",
|
|
@@ -42,6 +44,8 @@ __all__ = [
|
|
|
42
44
|
"Decision",
|
|
43
45
|
"DecisionType",
|
|
44
46
|
"Effect",
|
|
47
|
+
"Grant",
|
|
48
|
+
"GrantStore",
|
|
45
49
|
"AuditEvent",
|
|
46
50
|
"Outcome",
|
|
47
51
|
"ApprovalOutcome",
|
|
@@ -57,6 +61,7 @@ __all__ = [
|
|
|
57
61
|
"ToolRegistry",
|
|
58
62
|
"PolicyEvaluator",
|
|
59
63
|
"YamlPolicyEvaluator",
|
|
64
|
+
"InMemoryGrantStore",
|
|
60
65
|
"TollgateError",
|
|
61
66
|
"TollgateDenied",
|
|
62
67
|
"TollgateApprovalDenied",
|
tollgate/grants.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import time
|
|
3
|
+
from typing import Protocol, runtime_checkable
|
|
4
|
+
|
|
5
|
+
from .types import AgentContext, Grant, ToolRequest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@runtime_checkable
|
|
9
|
+
class GrantStore(Protocol):
|
|
10
|
+
"""Protocol for grant storage backends.
|
|
11
|
+
|
|
12
|
+
Implement this protocol to use a custom storage backend (Redis, SQLite, etc.).
|
|
13
|
+
|
|
14
|
+
Example Redis implementation:
|
|
15
|
+
|
|
16
|
+
class RedisGrantStore:
|
|
17
|
+
def __init__(self, redis_client):
|
|
18
|
+
self.redis = redis_client
|
|
19
|
+
|
|
20
|
+
async def create_grant(self, grant: Grant) -> str:
|
|
21
|
+
await self.redis.hset(f"grant:{grant.id}", mapping=grant.to_dict())
|
|
22
|
+
await self.redis.expireat(f"grant:{grant.id}", int(grant.expires_at))
|
|
23
|
+
return grant.id
|
|
24
|
+
|
|
25
|
+
async def find_matching_grant(self, agent_ctx, tool_request) -> Grant | None:
|
|
26
|
+
# Implement matching logic with Redis SCAN or secondary indexes
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
All methods must be async. The InMemoryGrantStore serves as the reference implementation.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
async def create_grant(self, grant: Grant) -> str:
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
async def find_matching_grant(
|
|
36
|
+
self, agent_ctx: AgentContext, tool_request: ToolRequest
|
|
37
|
+
) -> Grant | None:
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
async def revoke_grant(self, grant_id: str) -> bool:
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
async def list_active_grants(self, agent_id: str | None = None) -> list[Grant]:
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
async def cleanup_expired(self) -> int:
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
async def get_usage_count(self, grant_id: str) -> int:
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class InMemoryGrantStore:
|
|
54
|
+
"""In-memory store for action grants with thread-safe matching logic."""
|
|
55
|
+
|
|
56
|
+
def __init__(self):
|
|
57
|
+
self._grants: dict[str, Grant] = {}
|
|
58
|
+
self._usage_counts: dict[str, int] = {}
|
|
59
|
+
self._lock = asyncio.Lock()
|
|
60
|
+
|
|
61
|
+
async def create_grant(self, grant: Grant) -> str:
|
|
62
|
+
"""Store a new grant."""
|
|
63
|
+
async with self._lock:
|
|
64
|
+
self._grants[grant.id] = grant
|
|
65
|
+
self._usage_counts[grant.id] = 0
|
|
66
|
+
return grant.id
|
|
67
|
+
|
|
68
|
+
async def find_matching_grant(
|
|
69
|
+
self, agent_ctx: AgentContext, tool_request: ToolRequest
|
|
70
|
+
) -> Grant | None:
|
|
71
|
+
"""Find a non-expired grant that matches the request."""
|
|
72
|
+
now = time.time()
|
|
73
|
+
async with self._lock:
|
|
74
|
+
for grant in self._grants.values():
|
|
75
|
+
# 1. Skip expired
|
|
76
|
+
if grant.expires_at <= now:
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
# 2. Match agent_id
|
|
80
|
+
if grant.agent_id is not None and grant.agent_id != agent_ctx.agent_id:
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
# 3. Match effect
|
|
84
|
+
if grant.effect is not None and grant.effect != tool_request.effect:
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
# 4. Match tool (exact or prefix with *)
|
|
88
|
+
if grant.tool is not None:
|
|
89
|
+
if grant.tool.endswith("*"):
|
|
90
|
+
prefix = grant.tool[:-1]
|
|
91
|
+
if not tool_request.tool.startswith(prefix):
|
|
92
|
+
continue
|
|
93
|
+
elif grant.tool != tool_request.tool:
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
# 5. Match action
|
|
97
|
+
if grant.action is not None and grant.action != tool_request.action:
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
# 6. Match resource_type
|
|
101
|
+
if (
|
|
102
|
+
grant.resource_type is not None
|
|
103
|
+
and grant.resource_type != tool_request.resource_type
|
|
104
|
+
):
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
# Match found! Increment usage count
|
|
108
|
+
self._usage_counts[grant.id] += 1
|
|
109
|
+
return grant
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
async def get_usage_count(self, grant_id: str) -> int:
|
|
113
|
+
"""Get the number of times a grant has been used."""
|
|
114
|
+
async with self._lock:
|
|
115
|
+
return self._usage_counts.get(grant_id, 0)
|
|
116
|
+
|
|
117
|
+
async def revoke_grant(self, grant_id: str) -> bool:
|
|
118
|
+
"""Remove a grant by ID."""
|
|
119
|
+
async with self._lock:
|
|
120
|
+
if grant_id in self._grants:
|
|
121
|
+
del self._grants[grant_id]
|
|
122
|
+
if grant_id in self._usage_counts:
|
|
123
|
+
del self._usage_counts[grant_id]
|
|
124
|
+
return True
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
async def list_active_grants(self, agent_id: str | None = None) -> list[Grant]:
|
|
128
|
+
"""List all non-expired grants, optionally filtered by agent."""
|
|
129
|
+
now = time.time()
|
|
130
|
+
async with self._lock:
|
|
131
|
+
active = []
|
|
132
|
+
for grant in self._grants.values():
|
|
133
|
+
if grant.expires_at > now:
|
|
134
|
+
if agent_id is None or grant.agent_id == agent_id:
|
|
135
|
+
active.append(grant)
|
|
136
|
+
return active
|
|
137
|
+
|
|
138
|
+
async def cleanup_expired(self) -> int:
|
|
139
|
+
"""Remove all expired grants from the store."""
|
|
140
|
+
now = time.time()
|
|
141
|
+
async with self._lock:
|
|
142
|
+
to_remove = [
|
|
143
|
+
gid for gid, g in self._grants.items() if g.expires_at <= now
|
|
144
|
+
]
|
|
145
|
+
for gid in to_remove:
|
|
146
|
+
del self._grants[gid]
|
|
147
|
+
if gid in self._usage_counts:
|
|
148
|
+
del self._usage_counts[gid]
|
|
149
|
+
return len(to_remove)
|
tollgate/tower.py
CHANGED
|
@@ -11,6 +11,7 @@ from .exceptions import (
|
|
|
11
11
|
TollgateDeferred,
|
|
12
12
|
TollgateDenied,
|
|
13
13
|
)
|
|
14
|
+
from .grants import GrantStore
|
|
14
15
|
from .policy import PolicyEvaluator
|
|
15
16
|
from .types import (
|
|
16
17
|
AgentContext,
|
|
@@ -32,11 +33,13 @@ class ControlTower:
|
|
|
32
33
|
policy: PolicyEvaluator,
|
|
33
34
|
approver: Approver,
|
|
34
35
|
audit: AuditSink,
|
|
36
|
+
grant_store: GrantStore | None = None,
|
|
35
37
|
redact_fn: Callable[[dict[str, Any]], dict[str, Any]] | None = None,
|
|
36
38
|
):
|
|
37
39
|
self.policy = policy
|
|
38
40
|
self.approver = approver
|
|
39
41
|
self.audit = audit
|
|
42
|
+
self.grant_store = grant_store
|
|
40
43
|
self.redact_fn = redact_fn or self._default_redact
|
|
41
44
|
|
|
42
45
|
@staticmethod
|
|
@@ -86,6 +89,26 @@ class ControlTower:
|
|
|
86
89
|
|
|
87
90
|
# 3. Handle ASK
|
|
88
91
|
if decision.decision == DecisionType.ASK:
|
|
92
|
+
# 3.1 Check Grants
|
|
93
|
+
if self.grant_store:
|
|
94
|
+
matching_grant = await self.grant_store.find_matching_grant(
|
|
95
|
+
agent_ctx, tool_request
|
|
96
|
+
)
|
|
97
|
+
if matching_grant:
|
|
98
|
+
# Grant found! Proceed to execution without asking approver
|
|
99
|
+
result = await self._execute_and_log(
|
|
100
|
+
correlation_id,
|
|
101
|
+
request_hash,
|
|
102
|
+
agent_ctx,
|
|
103
|
+
intent,
|
|
104
|
+
tool_request,
|
|
105
|
+
decision,
|
|
106
|
+
exec_async,
|
|
107
|
+
grant_id=matching_grant.id,
|
|
108
|
+
)
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
# 3.2 Request Approval if no grant found
|
|
89
112
|
outcome = await self.approver.request_approval_async(
|
|
90
113
|
agent_ctx, intent, tool_request, request_hash, decision.reason
|
|
91
114
|
)
|
|
@@ -120,14 +143,35 @@ class ControlTower:
|
|
|
120
143
|
)
|
|
121
144
|
raise TollgateApprovalDenied(f"Approval failed: {outcome.value}")
|
|
122
145
|
|
|
123
|
-
# 4. Execute tool
|
|
146
|
+
# 4. Execute tool (Policy ALLOW or Approval APPROVED)
|
|
147
|
+
return await self._execute_and_log(
|
|
148
|
+
correlation_id,
|
|
149
|
+
request_hash,
|
|
150
|
+
agent_ctx,
|
|
151
|
+
intent,
|
|
152
|
+
tool_request,
|
|
153
|
+
decision,
|
|
154
|
+
exec_async,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
async def _execute_and_log(
|
|
158
|
+
self,
|
|
159
|
+
correlation_id: str,
|
|
160
|
+
request_hash: str,
|
|
161
|
+
agent_ctx: AgentContext,
|
|
162
|
+
intent: Intent,
|
|
163
|
+
tool_request: ToolRequest,
|
|
164
|
+
decision: Decision,
|
|
165
|
+
exec_async: Callable[[], Awaitable[Any]],
|
|
166
|
+
grant_id: str | None = None,
|
|
167
|
+
) -> Any:
|
|
168
|
+
"""Internal helper to execute tool and log result."""
|
|
124
169
|
result = None
|
|
125
170
|
outcome = Outcome.EXECUTED
|
|
126
171
|
try:
|
|
127
172
|
result = await exec_async()
|
|
128
173
|
except Exception as e:
|
|
129
174
|
outcome = Outcome.FAILED
|
|
130
|
-
# Security: Sanitize exception message to avoid info disclosure
|
|
131
175
|
result_summary = self._sanitize_exception(e)
|
|
132
176
|
self._log(
|
|
133
177
|
correlation_id,
|
|
@@ -137,11 +181,12 @@ class ControlTower:
|
|
|
137
181
|
tool_request,
|
|
138
182
|
decision,
|
|
139
183
|
outcome,
|
|
184
|
+
grant_id=grant_id,
|
|
140
185
|
result_summary=result_summary,
|
|
141
186
|
)
|
|
142
187
|
raise
|
|
143
188
|
|
|
144
|
-
#
|
|
189
|
+
# Final Audit
|
|
145
190
|
result_summary = self._truncate_result(result)
|
|
146
191
|
self._log(
|
|
147
192
|
correlation_id,
|
|
@@ -151,6 +196,7 @@ class ControlTower:
|
|
|
151
196
|
tool_request,
|
|
152
197
|
decision,
|
|
153
198
|
outcome,
|
|
199
|
+
grant_id=grant_id,
|
|
154
200
|
result_summary=result_summary,
|
|
155
201
|
)
|
|
156
202
|
|
|
@@ -177,7 +223,9 @@ class ControlTower:
|
|
|
177
223
|
async def _exec():
|
|
178
224
|
return exec_sync()
|
|
179
225
|
|
|
180
|
-
return asyncio.run(
|
|
226
|
+
return asyncio.run(
|
|
227
|
+
self.execute_async(agent_ctx, intent, tool_request, _exec)
|
|
228
|
+
)
|
|
181
229
|
|
|
182
230
|
def _log(
|
|
183
231
|
self,
|
|
@@ -189,6 +237,7 @@ class ControlTower:
|
|
|
189
237
|
decision: Decision,
|
|
190
238
|
outcome: Outcome,
|
|
191
239
|
approval_id: str | None = None,
|
|
240
|
+
grant_id: str | None = None,
|
|
192
241
|
result_summary: str | None = None,
|
|
193
242
|
):
|
|
194
243
|
# Redact params before logging
|
|
@@ -212,47 +261,20 @@ class ControlTower:
|
|
|
212
261
|
decision=decision,
|
|
213
262
|
outcome=outcome,
|
|
214
263
|
approval_id=approval_id,
|
|
264
|
+
grant_id=grant_id,
|
|
215
265
|
result_summary=result_summary,
|
|
216
266
|
policy_version=decision.policy_version,
|
|
217
267
|
manifest_version=req.manifest_version,
|
|
218
268
|
)
|
|
219
269
|
self.audit.emit(event)
|
|
220
270
|
|
|
271
|
+
def _sanitize_exception(self, e: Exception) -> str:
|
|
272
|
+
"""Sanitize exception message to avoid leaking sensitive data."""
|
|
273
|
+
# Only include the exception type and a generic message for security
|
|
274
|
+
return f"{type(e).__name__}: Execution failed"
|
|
275
|
+
|
|
221
276
|
def _truncate_result(self, result: Any, max_chars: int = 200) -> str | None:
|
|
222
277
|
if result is None:
|
|
223
278
|
return None
|
|
224
279
|
s = str(result)
|
|
225
280
|
return s[:max_chars] + "..." if len(s) > max_chars else s
|
|
226
|
-
|
|
227
|
-
def _sanitize_exception(self, e: Exception, max_chars: int = 100) -> str:
|
|
228
|
-
"""
|
|
229
|
-
Sanitize exception message for audit logging.
|
|
230
|
-
|
|
231
|
-
Removes potentially sensitive information like file paths, stack traces,
|
|
232
|
-
and internal details while preserving the exception type.
|
|
233
|
-
"""
|
|
234
|
-
exc_type = type(e).__name__
|
|
235
|
-
|
|
236
|
-
# For common safe exceptions, include a truncated message
|
|
237
|
-
safe_exceptions = {
|
|
238
|
-
"ValueError",
|
|
239
|
-
"TypeError",
|
|
240
|
-
"KeyError",
|
|
241
|
-
"IndexError",
|
|
242
|
-
"AttributeError",
|
|
243
|
-
"RuntimeError",
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if exc_type in safe_exceptions:
|
|
247
|
-
msg = str(e)
|
|
248
|
-
# Remove file paths (Unix and Windows style)
|
|
249
|
-
import re
|
|
250
|
-
|
|
251
|
-
msg = re.sub(r"['\"]?(/[^\s'\"]+|[A-Za-z]:\\[^\s'\"]+)['\"]?", "[PATH]", msg)
|
|
252
|
-
# Truncate to avoid leaking too much info
|
|
253
|
-
if len(msg) > max_chars:
|
|
254
|
-
msg = msg[:max_chars] + "..."
|
|
255
|
-
return f"{exc_type}: {msg}"
|
|
256
|
-
|
|
257
|
-
# For unknown exceptions, only log the type
|
|
258
|
-
return f"{exc_type}: [details redacted]"
|
tollgate/types.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import uuid
|
|
1
2
|
from collections.abc import Awaitable, Callable
|
|
2
3
|
from dataclasses import asdict, dataclass, field
|
|
3
4
|
from enum import Enum
|
|
@@ -92,6 +93,28 @@ class Decision:
|
|
|
92
93
|
return d
|
|
93
94
|
|
|
94
95
|
|
|
96
|
+
@dataclass(frozen=True)
|
|
97
|
+
class Grant:
|
|
98
|
+
"""A grant that allows bypassing human approval for specific actions."""
|
|
99
|
+
|
|
100
|
+
agent_id: str | None # None = any agent
|
|
101
|
+
effect: Effect | None # None = any effect
|
|
102
|
+
tool: str | None # None = any tool, supports prefix like "mcp:*"
|
|
103
|
+
action: str | None # None = any action
|
|
104
|
+
resource_type: str | None # None = any resource
|
|
105
|
+
expires_at: float # Unix timestamp
|
|
106
|
+
granted_by: str
|
|
107
|
+
created_at: float
|
|
108
|
+
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
109
|
+
reason: str | None = None
|
|
110
|
+
|
|
111
|
+
def to_dict(self) -> dict[str, Any]:
|
|
112
|
+
d = asdict(self)
|
|
113
|
+
if self.effect:
|
|
114
|
+
d["effect"] = self.effect.value
|
|
115
|
+
return d
|
|
116
|
+
|
|
117
|
+
|
|
95
118
|
@dataclass(frozen=True)
|
|
96
119
|
class AuditEvent:
|
|
97
120
|
timestamp: str
|
|
@@ -103,6 +126,7 @@ class AuditEvent:
|
|
|
103
126
|
decision: Decision
|
|
104
127
|
outcome: Outcome
|
|
105
128
|
approval_id: str | None = None
|
|
129
|
+
grant_id: str | None = None
|
|
106
130
|
result_summary: str | None = None
|
|
107
131
|
policy_version: str | None = None
|
|
108
132
|
manifest_version: str | None = None
|
|
@@ -118,6 +142,7 @@ class AuditEvent:
|
|
|
118
142
|
"decision": self.decision.to_dict(),
|
|
119
143
|
"outcome": self.outcome.value,
|
|
120
144
|
"approval_id": self.approval_id,
|
|
145
|
+
"grant_id": self.grant_id,
|
|
121
146
|
"result_summary": self.result_summary,
|
|
122
147
|
"policy_version": self.policy_version,
|
|
123
148
|
"manifest_version": self.manifest_version,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tollgate
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: Runtime enforcement layer for AI agent tool calls using Identity + Intent + Policy
|
|
5
5
|
Author: Tollgate Maintainers
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -77,6 +77,30 @@ Runtime enforcement layer for AI agent tool calls using **Identity + Intent + Po
|
|
|
77
77
|
|
|
78
78
|
## 🚀 v1 Integrations
|
|
79
79
|
|
|
80
|
+
### 🎟️ Session Grants
|
|
81
|
+
Grants allow you to pre-authorize specific actions for an agent session, bypassing human-in-the-loop approvals for repetitive or low-risk tasks.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from tollgate import Grant, InMemoryGrantStore, Effect
|
|
85
|
+
|
|
86
|
+
# 1. Setup a grant store
|
|
87
|
+
grant_store = InMemoryGrantStore()
|
|
88
|
+
tower = ControlTower(..., grant_store=grant_store)
|
|
89
|
+
|
|
90
|
+
# 2. Issue a grant (e.g., after initial human approval)
|
|
91
|
+
grant = Grant(
|
|
92
|
+
agent_id="my-agent",
|
|
93
|
+
effect=Effect.WRITE,
|
|
94
|
+
tool="mcp:*", # Wildcard prefix: matches any MCP tool (e.g., "mcp:server.write")
|
|
95
|
+
action=None, # Wildcard: matches any action
|
|
96
|
+
resource_type=None,
|
|
97
|
+
expires_at=time.time() + 3600, # Valid for 1 hour
|
|
98
|
+
granted_by="admin-user",
|
|
99
|
+
created_at=time.time()
|
|
100
|
+
)
|
|
101
|
+
await grant_store.create_grant(grant)
|
|
102
|
+
```
|
|
103
|
+
|
|
80
104
|
### MCP (Model Context Protocol)
|
|
81
105
|
Wrap an MCP client to gate all tool calls:
|
|
82
106
|
```python
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
tollgate/__init__.py,sha256=
|
|
1
|
+
tollgate/__init__.py,sha256=KWxtUI6D0Cm4QHCAaVv5VdEiQIo_krw7jzbXofxW_FM,1441
|
|
2
2
|
tollgate/approvals.py,sha256=lalvun6i5yaZ2EGZ-apQJBrLVmht5OfwgiUFsImPdYE,7814
|
|
3
3
|
tollgate/audit.py,sha256=ugMhuuLoyBNdYD2S_MjGN_ac4nHdtRz15MElqElREIs,1279
|
|
4
4
|
tollgate/exceptions.py,sha256=2yYY3esnHz26dyJlx_Cd_J64ryhexrgc4KliDmiVJSs,882
|
|
5
|
+
tollgate/grants.py,sha256=H5soShiNcyQiY3gLJRqqP7ffAo2Udmnj3iU15sGi3os,5177
|
|
5
6
|
tollgate/helpers.py,sha256=ZFMi19_ogpTlV_svFtgJ9kkmA1sPte1dmDnHYVdWGyo,1657
|
|
6
7
|
tollgate/policy.py,sha256=1_6HcbdpGfZtwmgqapqeRzItc_wbEg_L-fOA-BVIWNg,6110
|
|
7
8
|
tollgate/registry.py,sha256=ENKT-GnwFq0xXZj6qNlKFhAGYsxxBwtZsAdPZp2qlHM,2031
|
|
8
|
-
tollgate/tower.py,sha256=
|
|
9
|
-
tollgate/types.py,sha256=
|
|
9
|
+
tollgate/tower.py,sha256=rPG2sdVe2GpdGiyHwWz8K4k7EJCL2NqDAGsMUkKlt30,8574
|
|
10
|
+
tollgate/types.py,sha256=Ks3HNawhPzEAggTCab7NLb8zl06777ehEusASJWSj_g,3811
|
|
10
11
|
tollgate/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
12
|
tollgate/integrations/mcp.py,sha256=jBy1d64Yc55fO4JSVfhKqiffVCZdBMmmNLRta9SVfDE,1750
|
|
12
13
|
tollgate/integrations/strands.py,sha256=_AWsIyva_iwLFIQeTMxxbEzLSjneo0YumWfY9oayKeM,3003
|
|
@@ -14,7 +15,7 @@ tollgate/interceptors/__init__.py,sha256=0c3MYyVKGYrBOZ1mMolgrAowrqZrULzAl0vv-s8
|
|
|
14
15
|
tollgate/interceptors/base.py,sha256=uJxHzH0eurcGEVknbk1kQkk_2u2qNtnM--ZRCp52Wyo,1390
|
|
15
16
|
tollgate/interceptors/langchain.py,sha256=_8vXCjWkRKeTlxtXm33a67Gf4po9YiHS7faThMJLohc,2963
|
|
16
17
|
tollgate/interceptors/openai.py,sha256=cHnOQ8keQJRYBm_1RpohKWk3D9Ask22hpuQghm9iahU,3337
|
|
17
|
-
tollgate-1.0.
|
|
18
|
-
tollgate-1.0.
|
|
19
|
-
tollgate-1.0.
|
|
20
|
-
tollgate-1.0.
|
|
18
|
+
tollgate-1.0.5.dist-info/METADATA,sha256=zPDa8CQ7Qqx5GoQ6XojAZgur47ErP6KeWoUCfhTQOXs,7748
|
|
19
|
+
tollgate-1.0.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
20
|
+
tollgate-1.0.5.dist-info/licenses/LICENSE,sha256=EZ9SehMCkcatlggcoT7WV0tx-ku4OsAoQf9LmJZvG1g,10806
|
|
21
|
+
tollgate-1.0.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|