cortexhub 0.1.4__py3-none-any.whl → 0.1.7__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.
@@ -73,6 +73,7 @@ class ClaudeAgentsAdapter(ToolAdapter):
73
73
  def __init__(self, cortex_hub: Any):
74
74
  super().__init__(cortex_hub)
75
75
  self._hook_spans: dict[str, Any] = {}
76
+ self._active_run_session_id: str | None = None
76
77
 
77
78
  def _get_framework_modules(self) -> list[str]:
78
79
  return ["claude_agent_sdk"]
@@ -93,13 +94,6 @@ class ClaudeAgentsAdapter(ToolAdapter):
93
94
  return
94
95
 
95
96
  cortex_hub = self.cortex_hub
96
- tools = self._discover_tools()
97
- if tools:
98
- cortex_hub.backend.register_tool_inventory(
99
- agent_id=cortex_hub.agent_id,
100
- framework=self.framework_name,
101
- tools=tools,
102
- )
103
97
 
104
98
  # Store original decorator
105
99
  if not hasattr(claude_agent_sdk, _ORIGINAL_TOOL_ATTR):
@@ -206,6 +200,7 @@ class ClaudeAgentsAdapter(ToolAdapter):
206
200
  try:
207
201
  import claude_agent_sdk
208
202
  from claude_agent_sdk import ClaudeSDKClient, ResultMessage
203
+ adapter = self
209
204
 
210
205
  if getattr(claude_agent_sdk, _PATCHED_RUN_ATTR, False):
211
206
  return
@@ -218,6 +213,7 @@ class ClaudeAgentsAdapter(ToolAdapter):
218
213
  status = None
219
214
  failed = False
220
215
  cortex_hub.start_run(framework="claude_agents")
216
+ adapter._active_run_session_id = cortex_hub.session_id
221
217
  try:
222
218
  async for message in original_query(*args, **kwargs):
223
219
  if isinstance(message, ResultMessage):
@@ -231,6 +227,7 @@ class ClaudeAgentsAdapter(ToolAdapter):
231
227
  status = "failed"
232
228
  if status:
233
229
  cortex_hub.finish_run(framework="claude_agents", status=status)
230
+ adapter._active_run_session_id = None
234
231
 
235
232
  claude_agent_sdk.query = patched_query
236
233
 
@@ -251,10 +248,12 @@ class ClaudeAgentsAdapter(ToolAdapter):
251
248
 
252
249
  async def patched_client_query(self, *args, **kwargs):
253
250
  cortex_hub.start_run(framework="claude_agents")
251
+ adapter._active_run_session_id = cortex_hub.session_id
254
252
  try:
255
253
  return await original_client_query(self, *args, **kwargs)
256
254
  except Exception:
257
255
  cortex_hub.finish_run(framework="claude_agents", status="failed")
256
+ adapter._active_run_session_id = None
258
257
  raise
259
258
 
260
259
  async def patched_receive_response(self, *args, **kwargs):
@@ -273,6 +272,7 @@ class ClaudeAgentsAdapter(ToolAdapter):
273
272
  status = "failed"
274
273
  if status:
275
274
  cortex_hub.finish_run(framework="claude_agents", status=status)
275
+ adapter._active_run_session_id = None
276
276
 
277
277
  ClaudeSDKClient.query = patched_client_query
278
278
  ClaudeSDKClient.receive_response = patched_receive_response
@@ -303,6 +303,10 @@ class ClaudeAgentsAdapter(ToolAdapter):
303
303
  """
304
304
  cortex_hub = self.cortex_hub
305
305
  span_store = self._hook_spans
306
+ adapter = self
307
+
308
+ def _current_session_id() -> str:
309
+ return adapter._active_run_session_id or cortex_hub.session_id
306
310
 
307
311
  def _start_tool_span(
308
312
  *,
@@ -316,7 +320,7 @@ class ClaudeAgentsAdapter(ToolAdapter):
316
320
  name="tool.invoke",
317
321
  kind=SpanKind.INTERNAL,
318
322
  )
319
- span.set_attribute("cortexhub.session.id", cortex_hub.session_id)
323
+ span.set_attribute("cortexhub.session.id", _current_session_id())
320
324
  span.set_attribute("cortexhub.agent.id", cortex_hub.agent_id)
321
325
  span.set_attribute("cortexhub.tool.name", tool_name)
322
326
  span.set_attribute("cortexhub.tool.framework", "claude_agents")
@@ -70,13 +70,6 @@ class CrewAIAdapter(ToolAdapter):
70
70
  from crewai.tools.structured_tool import CrewStructuredTool
71
71
 
72
72
  cortex_hub = self.cortex_hub
73
- tools = self._discover_tools()
74
- if tools:
75
- cortex_hub.backend.register_tool_inventory(
76
- agent_id=cortex_hub.agent_id,
77
- framework=self.framework_name,
78
- tools=tools,
79
- )
80
73
 
81
74
  # Patch CrewStructuredTool.invoke (primary execution path)
82
75
  if not getattr(CrewStructuredTool, _PATCHED_ATTR, False):
@@ -411,12 +411,6 @@ class LangGraphAdapter(ToolAdapter):
411
411
  name = tool.get("name") or "unknown_tool"
412
412
  self._discovered_tools[name] = tool
413
413
 
414
- self.cortex_hub.backend.register_tool_inventory(
415
- agent_id=self.cortex_hub.agent_id,
416
- framework=self.framework_name,
417
- tools=list(self._discovered_tools.values()),
418
- )
419
-
420
414
  def _normalize_tools(self, tools: Any) -> list[dict[str, Any]]:
421
415
  """Convert tool objects to inventory payloads."""
422
416
  normalized: list[dict[str, Any]] = []
@@ -67,13 +67,6 @@ class OpenAIAgentsAdapter(ToolAdapter):
67
67
  return
68
68
 
69
69
  cortex_hub = self.cortex_hub
70
- tools = self._discover_tools()
71
- if tools:
72
- cortex_hub.backend.register_tool_inventory(
73
- agent_id=cortex_hub.agent_id,
74
- framework=self.framework_name,
75
- tools=tools,
76
- )
77
70
 
78
71
  # Store original function_tool decorator
79
72
  if not hasattr(tool_module, _ORIGINAL_FUNCTION_TOOL_ATTR):
@@ -7,7 +7,6 @@ import httpx
7
7
  from typing import Any
8
8
  from dataclasses import dataclass
9
9
 
10
- from cortexhub.version import __version__
11
10
 
12
11
  logger = structlog.get_logger(__name__)
13
12
 
@@ -92,7 +91,7 @@ class BackendClient:
92
91
  timeout=10.0,
93
92
  )
94
93
 
95
- def validate_api_key(self) -> tuple[bool, SDKConfig | None]:
94
+ def validate_api_key(self, *, agent_id: str | None = None) -> tuple[bool, SDKConfig | None]:
96
95
  """Validate API key with backend and get SDK configuration.
97
96
 
98
97
  Returns:
@@ -127,7 +126,46 @@ class BackendClient:
127
126
 
128
127
  # Convert policy objects to Cedar string if needed
129
128
  raw_policies = policies_data.get("policies", [])
129
+ if agent_id:
130
+ raw_policies = [
131
+ policy for policy in raw_policies if policy.get("agent_id") == agent_id
132
+ ]
130
133
  guardrail_configs: dict[str, GuardrailConfig] = {}
134
+
135
+ def _merge_guardrail_config(
136
+ existing: GuardrailConfig | None,
137
+ incoming: GuardrailConfig,
138
+ ) -> GuardrailConfig:
139
+ if not existing:
140
+ return incoming
141
+ priority = {"block": 3, "redact": 2, "allow": 1}
142
+ action = (
143
+ incoming.action
144
+ if priority.get(incoming.action, 0) > priority.get(existing.action, 0)
145
+ else existing.action
146
+ )
147
+ if existing.pii_types is None or incoming.pii_types is None:
148
+ pii_types = None
149
+ else:
150
+ pii_types = sorted(set(existing.pii_types + incoming.pii_types))
151
+ if existing.secret_types is None or incoming.secret_types is None:
152
+ secret_types = None
153
+ else:
154
+ secret_types = sorted(set(existing.secret_types + incoming.secret_types))
155
+ custom_patterns = (existing.custom_patterns or []) + (incoming.custom_patterns or [])
156
+ if existing.redaction_scope == "both" or incoming.redaction_scope == "both":
157
+ redaction_scope = "both"
158
+ elif existing.redaction_scope != incoming.redaction_scope:
159
+ redaction_scope = "both"
160
+ else:
161
+ redaction_scope = existing.redaction_scope
162
+ return GuardrailConfig(
163
+ action=action,
164
+ pii_types=pii_types,
165
+ secret_types=secret_types,
166
+ custom_patterns=custom_patterns,
167
+ redaction_scope=redaction_scope,
168
+ )
131
169
 
132
170
  if isinstance(raw_policies, list):
133
171
  # Backend returns list of policy objects - extract Cedar code
@@ -171,12 +209,16 @@ class BackendClient:
171
209
  description=cp.get("description"),
172
210
  enabled=cp.get("enabled", True),
173
211
  ))
174
- guardrail_configs["pii"] = GuardrailConfig(
175
- action=gc.get("action", "redact"),
176
- pii_types=gc.get("pii_types"),
177
- custom_patterns=custom_patterns if custom_patterns else None,
178
- redaction_scope=gc.get("redaction_scope", "both"),
212
+ merged = _merge_guardrail_config(
213
+ guardrail_configs.get("pii"),
214
+ GuardrailConfig(
215
+ action=gc.get("action", "redact"),
216
+ pii_types=gc.get("pii_types"),
217
+ custom_patterns=custom_patterns if custom_patterns else None,
218
+ redaction_scope=gc.get("redaction_scope", "both"),
219
+ ),
179
220
  )
221
+ guardrail_configs["pii"] = merged
180
222
  elif "secrets" in policy_key:
181
223
  custom_patterns = []
182
224
  raw_patterns = gc.get("custom_patterns") or []
@@ -187,12 +229,16 @@ class BackendClient:
187
229
  description=cp.get("description"),
188
230
  enabled=cp.get("enabled", True),
189
231
  ))
190
- guardrail_configs["secrets"] = GuardrailConfig(
191
- action=gc.get("action", "redact"),
192
- secret_types=gc.get("secret_types"),
193
- custom_patterns=custom_patterns if custom_patterns else None,
194
- redaction_scope=gc.get("redaction_scope", "both"),
232
+ merged = _merge_guardrail_config(
233
+ guardrail_configs.get("secrets"),
234
+ GuardrailConfig(
235
+ action=gc.get("action", "redact"),
236
+ secret_types=gc.get("secret_types"),
237
+ custom_patterns=custom_patterns if custom_patterns else None,
238
+ redaction_scope=gc.get("redaction_scope", "both"),
239
+ ),
195
240
  )
241
+ guardrail_configs["secrets"] = merged
196
242
  policies_str = "\n".join(cedar_parts)
197
243
  else:
198
244
  policies_str = raw_policies or ""
@@ -254,33 +300,6 @@ class BackendClient:
254
300
  if self._client:
255
301
  self._client.close()
256
302
 
257
- def register_tool_inventory(
258
- self,
259
- *,
260
- agent_id: str,
261
- framework: str,
262
- tools: list[dict],
263
- ) -> dict[str, Any]:
264
- """Register agent tool inventory on init. Overwrites previous."""
265
-
266
- if not self._client:
267
- return {"agent_id": agent_id, "tools_count": 0}
268
-
269
- try:
270
- response = self._client.post(
271
- "/tool-inventory",
272
- json={
273
- "agent_id": agent_id,
274
- "framework": framework,
275
- "sdk_version": __version__,
276
- "tools": tools,
277
- },
278
- )
279
- return response.json()
280
- except Exception as e:
281
- logger.warning("Failed to register tool inventory", error=str(e))
282
- return {"agent_id": agent_id, "tools_count": len(tools)}
283
-
284
303
  def create_approval(
285
304
  self,
286
305
  *,
cortexhub/client.py CHANGED
@@ -159,7 +159,7 @@ class CortexHub:
159
159
 
160
160
  # Validate API key and get configuration (including policies)
161
161
  self.backend = BackendClient(self.api_key, self.api_base_url)
162
- is_valid, sdk_config = self.backend.validate_api_key()
162
+ is_valid, sdk_config = self.backend.validate_api_key(agent_id=self.agent_id)
163
163
 
164
164
  if not is_valid:
165
165
  raise ConfigurationError(
cortexhub/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Version information for CortexHub SDK."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortexhub
3
- Version: 0.1.4
3
+ Version: 0.1.7
4
4
  Summary: CortexHub Python SDK - Policy-as-Code for AI Agents
5
5
  Project-URL: Homepage, https://cortexhub.ai
6
6
  Project-URL: Documentation, https://docs.cortexhub.ai
@@ -1,21 +1,21 @@
1
1
  cortexhub/__init__.py,sha256=hnwsiUy5BT3QaPNK5yXqex6zRzZlok43S04y-dkNWSE,4376
2
2
  cortexhub/auto_protect.py,sha256=yW-iW9cdGg-fM8EtR_Z9UkMczn51U8BKh5pOBgV1VqE,3951
3
- cortexhub/client.py,sha256=LNS63p8jFD3GASOKCQISZR4au52o4NmINF3hv1sTpxg,96968
3
+ cortexhub/client.py,sha256=360lxIPd52r0k7ZuuUc68xsEZHPeibU_BoUBrkTotHE,96990
4
4
  cortexhub/config.py,sha256=kpwVMTRcSisJy8HHBL3ZBeQoMN3lE7zziRzEYaq92LI,941
5
5
  cortexhub/errors.py,sha256=hsknLHaVCqk5MkpXvahxYIUshDt2VGZfbBWRronFvVM,3697
6
6
  cortexhub/frameworks.py,sha256=WCPokPRVafLqe73MDbJasi8FyNHTM2T4wfaBg0LuWoM,2223
7
7
  cortexhub/pipeline.py,sha256=EvkrEz7dGnzc0tlv02B3Kx2KQfdX4gsTuhUxj-MwuFI,3256
8
- cortexhub/version.py,sha256=L1gsFyWfMZYvly7nYjyH0nlWRAsm65S61_B-imDPgXE,68
8
+ cortexhub/version.py,sha256=5kkypMZTwNSkQR5pPfUn4kRFz3PtN8_HpqYKHbJkghg,68
9
9
  cortexhub/adapters/__init__.py,sha256=Lb6RquGPGs4exdfSB6Qb6q6HyGczozuLWSaLCtgZdyo,130
10
10
  cortexhub/adapters/base.py,sha256=_ODoJFLQLYnowse3MHQk8bN9KjE1oQr8QDNNlvPSwSs,4094
11
- cortexhub/adapters/claude_agents.py,sha256=etuDHQjWikItFB-rHVwDM9fFm-0WNderkOhIgOtRxjk,22499
12
- cortexhub/adapters/crewai.py,sha256=SuyMBF9bzrqDq58ZsGZV-3uAzaAtlLD3xEeyMB2Hgig,24899
13
- cortexhub/adapters/langgraph.py,sha256=X0UutdATOowXcZYRv_ImiJGSO97MKgYcuxFKV-jTxJA,20850
14
- cortexhub/adapters/openai_agents.py,sha256=63rNZ8HItZdoxERk1BTYzzpVe8Qq_8j56jfrEICzjpU,21511
11
+ cortexhub/adapters/claude_agents.py,sha256=AfuySr-nl06U3L2nY3ie5fbA8tQNDEE5I3u47B4dp30,22761
12
+ cortexhub/adapters/crewai.py,sha256=-8s27SU-_Z1c2D-jgVa1VfrtDbEDub3g6ahBcG5wy-U,24622
13
+ cortexhub/adapters/langgraph.py,sha256=Dq3qoMQy2MT2C9KqggVUoZF9iYuoA72LBe4kV7JYGzU,20635
14
+ cortexhub/adapters/openai_agents.py,sha256=vk9oZfS59TVCUmh5HxbWtesaZJo21wpu62Wu-upEz3E,21234
15
15
  cortexhub/audit/__init__.py,sha256=tKE9CdpAIpP04oeLOza96jwZs4er5G5bRBWGibvh4Xw,545
16
16
  cortexhub/audit/events.py,sha256=w1uHbYzwBTDRm8gKAY_TQlXHjUj2nZt-iXk0NcqzRak,5965
17
17
  cortexhub/backend/__init__.py,sha256=Yt5fcaW24l6YwPCLvKbcKAXhhrWTUjwgqJsLOIzgIAo,133
18
- cortexhub/backend/client.py,sha256=dYd2aWJORysrEfJ5T_QPLwvs0xUQLrhMVT4FWUVOg6c,13680
18
+ cortexhub/backend/client.py,sha256=542TKmKoGCOG54RdFLj4ur675LOxwhACFmtCFQ4eWgw,15310
19
19
  cortexhub/context/__init__.py,sha256=BWoR2s4ogOs1YXPFBO7e8_UOZ_5AWYfMlv5PTVORN2Y,144
20
20
  cortexhub/context/enricher.py,sha256=cComyfE10EbaCAI8KLM0ED6j-wKYBc26MRnIn-dxhmU,5322
21
21
  cortexhub/guardrails/__init__.py,sha256=RH1u8w3FFLd6u7-OYgj42PqAJO9jXsl8-tcLiL24uOo,103
@@ -33,7 +33,7 @@ cortexhub/policy/models.py,sha256=81NPX36yru0AZ22Wivytv3uxlwir4VsTN4MZgq4bdiA,38
33
33
  cortexhub/policy/sync.py,sha256=7kAc3rd5WftipR8XKwvU8SwukIoY-0gcGW2fsf-ij_I,5999
34
34
  cortexhub/telemetry/__init__.py,sha256=RSY38hHnYtbRURLoWAhbYYxYMtCItVYZBNN6Bfny8uA,1049
35
35
  cortexhub/telemetry/otel.py,sha256=T2pC-K_S_KmrLvfcd3oHMbEwC5ibGZ-PXTZSY0xJgcQ,16633
36
- cortexhub-0.1.4.dist-info/METADATA,sha256=415eS3_LSI7Df293BxsVWLCm_7o7HSrF89GC4j2z7Jc,8539
37
- cortexhub-0.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
38
- cortexhub-0.1.4.dist-info/licenses/LICENSE,sha256=R-lPsEVN69q2o19ge2_O9Vt9cJuGM557oEYX3XdnvIk,1066
39
- cortexhub-0.1.4.dist-info/RECORD,,
36
+ cortexhub-0.1.7.dist-info/METADATA,sha256=x54ffWpQLv-bKw22CYva6pzEabJUC82xJ1sVJBy-b0w,8539
37
+ cortexhub-0.1.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ cortexhub-0.1.7.dist-info/licenses/LICENSE,sha256=R-lPsEVN69q2o19ge2_O9Vt9cJuGM557oEYX3XdnvIk,1066
39
+ cortexhub-0.1.7.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.28.0
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any