netra-zen 1.0.2__py3-none-any.whl → 1.0.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netra-zen
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Multi-instance Claude orchestrator for parallel task execution
5
5
  Home-page: https://github.com/netra-systems/zen
6
6
  Author: Systems
@@ -0,0 +1,19 @@
1
+ zen_orchestrator.py,sha256=eUiA8T08QYMfx5kM84LUloDUxscCUzv_k3O0WN3nFpo,148941
2
+ agent_interface/__init__.py,sha256=OsbOKzElHsxhVgak87oOx_u46QNgKmz-Reis-plAMwk,525
3
+ agent_interface/base_agent.py,sha256=GNskG9VaZgno7X24lQTpFdxUoQE0yJHLh0UPFJvOPn4,11098
4
+ netra_zen-1.0.4.dist-info/licenses/LICENSE.md,sha256=t6LtOzAE2hgIIv5WbaN0wOcU3QCnGtAkMGNclHrKTOs,79
5
+ token_budget/__init__.py,sha256=_2tmi72DGNtbYcZ-rGIxVKMytdkHFjzJaWz8bDhYACc,33
6
+ token_budget/budget_manager.py,sha256=VRWxKcGDtgJfIRh-ztYQ4-wuhBvddVJJnyoGfxCBlv0,9567
7
+ token_budget/models.py,sha256=14xFTk2-R1Ql0F9WLDof7vADrKC_5Fj7EE7UmZdoG00,2384
8
+ token_budget/visualization.py,sha256=SaNnZ15edHXtjDCA5Yfu7w3AztCZRYsYCPGBqzapupY,719
9
+ token_transparency/__init__.py,sha256=go86Rg4_VYAPLw3myVpLe1s1PbMu1llDTw1wfomP1lA,453
10
+ token_transparency/claude_pricing_engine.py,sha256=9zWQJS3HJEs_lljil-xT1cUvG-Jf3ykNAninJFyfNSM,12630
11
+ zen/__init__.py,sha256=_1gd3iYHH2ekLrCvZQ4DEA2bZ-OK0vlLfxBb3KlZALU,206
12
+ zen/telemetry/__init__.py,sha256=QiW8p9TBDwPxtmYTszMyccblLHKrlVTsKLFIBvMHKx8,305
13
+ zen/telemetry/embedded_credentials.py,sha256=0ktJEeAwj_oLeSottOaCPEOnBXfqF7OYyAihiUOoRbA,3925
14
+ zen/telemetry/manager.py,sha256=TtrIPOvRvq1OOhNAO_Tp-dz7EiS2xXfqnpKQduLwYoI,9731
15
+ netra_zen-1.0.4.dist-info/METADATA,sha256=x_6PpiIaeS3MlUOU-YTP9hRp2cU3TDM5BM0aYu3aHyE,29442
16
+ netra_zen-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ netra_zen-1.0.4.dist-info/entry_points.txt,sha256=oDehCnPGZezG0m9ZWspxjHLHyQ3eERX87eojR4ljaRo,45
18
+ netra_zen-1.0.4.dist-info/top_level.txt,sha256=zHGp3q7L19xyJ98JsLMpkt1FffEneS2GNMq468hfhpo,69
19
+ netra_zen-1.0.4.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  agent_interface
2
2
  token_budget
3
3
  token_transparency
4
+ zen
4
5
  zen_orchestrator
zen/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Zen namespace package placeholder.
2
+
3
+ This lightweight module exists so repository scripts can import
4
+ `zen.telemetry` directly without requiring the full orchestrator package.
5
+ """
6
+
7
+ __all__: list[str] = []
@@ -0,0 +1,11 @@
1
+ """Telemetry utilities exposed by the Zen package."""
2
+
3
+ from .embedded_credentials import get_embedded_credentials, get_project_id
4
+ from .manager import TelemetryManager, telemetry_manager
5
+
6
+ __all__ = [
7
+ "TelemetryManager",
8
+ "telemetry_manager",
9
+ "get_embedded_credentials",
10
+ "get_project_id",
11
+ ]
@@ -0,0 +1,26 @@
1
+ """Embedded telemetry credentials. AUTO-GENERATED - DO NOT COMMIT."""
2
+
3
+ import base64
4
+ import json
5
+ from google.oauth2 import service_account
6
+
7
+ _EMBEDDED_CREDENTIALS_B64 = 'ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsCiAgInByb2plY3RfaWQiOiAibmV0cmEtdGVsZW1ldHJ5LXB1YmxpYyIsCiAgInByaXZhdGVfa2V5X2lkIjogImVjOWM4ZGNlZGZmMTUzNjM5YTUxOTcyMzc0MjYyNjkwNjZkNzAxYTQiLAogICJwcml2YXRlX2tleSI6ICItLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS1cbk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRREhUcmZFOHlQdUFCTDNcbk5aS3diZ1AwamRyaWRnY0UwMUlLMks1YkZ1bWFrUHVrRGxzV0dVaUswOXEyaVNYTWVUQmJPZDF0VjFoc3VJcUhcbnpQK0pZd0NTcVp4S0pIQS8yWUdVcERqeWhHRVd3QTNtS3laVXVUUS9yUTBUK20yV0cwdEMxUzBpQzB6U211cEpcbjBNeGhUUDRjKzYreEczSDVSWEF1YjdONmx6eFFWVnJSY3FHMTYydlF5SFA2OWIrd2RidTJHM0o5UkJVN1VUK1FcbkU2RHB2K01YaEp3MnRPdHZLbFBQT3BnWm9Sb0pmeXU1WlpzbHlJZCtzY3FhUTY5ZjBaSmpIRjlYQVdlT25mUTRcbmExbmV0LzJqRjZibWpuZmQ2MjhBODA5cEluTXJEL0FwZjJzUWJJdXJIYUI4am5uTEQ0eDMrbVhXRTgyMFJLNktcbjViZ0FQanNoQWdNQkFBRUNnZ0VBQmZERVZMWVlDakFkb0pscnpyOHF0a056cEpnV3Y3ZXlQSEZHcWgraDFSbndcbjdDd0Mza0xnNlFWbFFaZFBKWHZ2dTJwYlBaYnl3MlBST2ppN25adFNNU3pseEFaM3c0bHV2YkRTNHpTYnBiZFJcbityd3F3Mi8xUFJnaCtZaFhjNWZLNjVvcHd4Zmg0VzJkWWRlYnZlTXkrRWR1cmtsV2dYTG13L1dQbkkzdExlbzlcbjV1elZjbU42Qk04YkU3azFYK1M0RURBS0VRWlprUEdzTFQ4RXN4UmdWOWtnT1Zicm5VQ1Z0dXA1Q0NGbUR3U1Bcbmg0U25wMEsvTUp3b1U3NG4reTlFMXYxUXRnajE5TkhaNHJ2dFpnUlVaandHQy9Cc3ZkcE1PazArZTJEMlgvRk9cblZnc29xS2tDaklWUzRMcG5YSEpZbU5oajZWNHRXUnZ1OW1NTXhTL3FBUUtCZ1FEb3hZenlsdEZKL242ZEQvUHlcbnZLOFRaTHd5dFdCcjBXU3ZHU2VzM0JYRGZoKzBFbU4rRHpnZGdUb0ovbkhpazM5R21QM0tLN2htOFVvaFFHRy9cbkh0SFRuS0lBQlhrSU8yd2Z3N0h0V2pPTXRocHp2dFQxcmVEVHVjVk0wc2lCMHpjTldCMjFUamdQL3JYY2Q3NWVcbklERmNBN0hTbUJDLzB4bzk3aC8wV2YvOW9RS0JnUURiTWtnbjlVR2Y2SVRMNmxTcDdrYWJGL0NuaE4yU2VTMVdcbnd3R21iRThxTTU0UitDcVRUeHk2UHBRaFVSczlHM1VpVmQ1SXZWVDhuT095ZVBZVFJEbnFCYjJ4S214SFRodlZcbnVQcTgwQXB3anBMbzh6VkFDSy9iVFVjSmlKVGFBdXFHaXI1Ykc4YUlldVpIc0pLeWJ1NmhoNkhXMldwWXVVV1BcbkZ3TTl4elpOZ1FLQmdRQzg5dHJVZVJFUVE3VGZwbnJBM09JNEdUZ2E1bG1QVFo2eDh2YmRncEY4Y2FBbEhDUitcbnlyWWdaYThMTysrU0kzRllpNHpFR2pnS0FlblBFcWdIY21xZW9uSjFGL3hJYll6NlFIRHFJYWJsblZQZUVOWnJcblY2dkQxZlRReC9FVVM3Wk9jL0V5Slh5bnAzeFZyVFB5ejZtaWJERm9xQ0E0eVpSdElDbjZ3VEZxNFFLQmdFWFJcbnAxQXErOE0rb2dYOTF3ZmxvTkhIOTF5MG9vc0VWQis5cjZuZDkvMWVRYXhCbXZZZkRleDVBRi80WUsrL0xqbElcbmxxd2V1cEpZT3VMZlNxcHFZZlFiN2djZmx5dkRRblI2SGt2RURIODd1cW0reGlobVcvV0RrT3dGZUR4VkQzVFpcbmZyYXdpelZ2eUNmdm8xcDRvVVFNV3MxL3BUTXJtRzl5aWhMRWdKU0JBb0dCQU1GWm50ZUtUUDZrVVdrVmpOcndcbmUvQzBDbjJ6dk1YNXVnZURkS1FWNkwrY25mRWlRSzdzZ3R5eFp5ek5kMC82QXJ0YnBrcS9wcVlaYXpwVzVFMkxcbkxVMUF3MmdHT25GRlh2ZXg4aXpOZXViMGdvUVE4d3BtL3lrMVNVekR6VTV1dCtPbVFFRmpsbUYrNDkza0ZYcC9cbnc1MWh2WjVVL2loL1NYbjN6cjdEWE5QYlxuLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLVxuIiwKICAiY2xpZW50X2VtYWlsIjogInplbi1jb21tdW5pdHktdGVsZW1ldHJ5QG5ldHJhLXRlbGVtZXRyeS1wdWJsaWMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICJjbGllbnRfaWQiOiAiMTE0NzAwMDA0NzA1MDUxODg5NTY4IiwKICAiYXV0aF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vb2F1dGgyL2F1dGgiLAogICJ0b2tlbl91cmkiOiAiaHR0cHM6Ly9vYXV0aDIuZ29vZ2xlYXBpcy5jb20vdG9rZW4iLAogICJhdXRoX3Byb3ZpZGVyX3g1MDlfY2VydF91cmwiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3YxL2NlcnRzIiwKICAiY2xpZW50X3g1MDlfY2VydF91cmwiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vcm9ib3QvdjEvbWV0YWRhdGEveDUwOS96ZW4tY29tbXVuaXR5LXRlbGVtZXRyeSU0MG5ldHJhLXRlbGVtZXRyeS1wdWJsaWMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICJ1bml2ZXJzZV9kb21haW4iOiAiZ29vZ2xlYXBpcy5jb20iCn0K'
8
+ _CREDENTIALS_DICT = json.loads(
9
+ base64.b64decode(_EMBEDDED_CREDENTIALS_B64.encode("utf-8"))
10
+ )
11
+
12
+
13
+ def get_embedded_credentials():
14
+ """Return service account credentials."""
15
+ try:
16
+ return service_account.Credentials.from_service_account_info(
17
+ _CREDENTIALS_DICT,
18
+ scopes=["https://www.googleapis.com/auth/trace.append"],
19
+ )
20
+ except Exception:
21
+ return None
22
+
23
+
24
+ def get_project_id() -> str:
25
+ """Return GCP project ID."""
26
+ return _CREDENTIALS_DICT.get("project_id", 'netra-telemetry-public')
@@ -0,0 +1,249 @@
1
+ """Telemetry manager for Zen orchestrator.
2
+
3
+ Provides minimal OpenTelemetry integration that records anonymous spans with
4
+ token usage and cost metadata. If OpenTelemetry or Google Cloud libraries are
5
+ missing, the manager silently degrades to a no-op implementation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import logging
12
+ import os
13
+ import re
14
+ from dataclasses import asdict
15
+ from typing import Any, Dict, Optional
16
+
17
+ try:
18
+ from opentelemetry import trace
19
+ from opentelemetry.sdk.resources import Resource
20
+ from opentelemetry.sdk.trace import TracerProvider
21
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
22
+ from opentelemetry.trace import SpanKind
23
+
24
+ OPENTELEMETRY_AVAILABLE = True
25
+ except ImportError: # pragma: no cover - optional dependency
26
+ OPENTELEMETRY_AVAILABLE = False
27
+
28
+ try:
29
+ from google.cloud.trace_v2 import TraceServiceClient
30
+ from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
31
+ from google.api_core.exceptions import GoogleAPICallError # type: ignore
32
+
33
+ GCP_EXPORT_AVAILABLE = True
34
+ except ImportError: # pragma: no cover - optional dependency
35
+ GCP_EXPORT_AVAILABLE = False
36
+
37
+ class GoogleAPICallError(Exception): # type: ignore
38
+ """Fallback exception used when google-api-core is unavailable."""
39
+
40
+ pass
41
+
42
+ from .embedded_credentials import get_embedded_credentials, get_project_id
43
+
44
+ logger = logging.getLogger(__name__)
45
+
46
+
47
+ def _sanitize_tool_name(tool: str) -> str:
48
+ """Convert tool names to telemetry-safe attribute suffixes."""
49
+ safe = re.sub(r"[^a-z0-9_]+", "_", tool.lower()).strip("_")
50
+ return safe or "tool"
51
+
52
+
53
+ class _NoOpTelemetryManager:
54
+ """Fallback manager when telemetry dependencies are unavailable."""
55
+
56
+ def is_enabled(self) -> bool:
57
+ return False
58
+
59
+ def record_instance_span(self, *_, **__): # pragma: no cover - trivial
60
+ return
61
+
62
+ def shutdown(self) -> None: # pragma: no cover - trivial
63
+ return
64
+
65
+
66
+ class TelemetryManager:
67
+ """Manage OpenTelemetry setup and span emission for Zen."""
68
+
69
+ def __init__(self) -> None:
70
+ self._enabled = False
71
+ self._provider: Optional[TracerProvider] = None
72
+ self._tracer = None
73
+ self._initialize()
74
+
75
+ def _initialize(self) -> None:
76
+ if os.getenv("ZEN_TELEMETRY_DISABLED", "").lower() in {"1", "true", "yes"}:
77
+ logger.debug("Telemetry disabled via ZEN_TELEMETRY_DISABLED")
78
+ return
79
+
80
+ if not (OPENTELEMETRY_AVAILABLE and GCP_EXPORT_AVAILABLE):
81
+ logger.debug("OpenTelemetry or Google Cloud exporter not available; telemetry disabled")
82
+ return
83
+
84
+ credentials = get_embedded_credentials()
85
+ if credentials is None:
86
+ logger.debug("No telemetry credentials detected; telemetry disabled")
87
+ return
88
+
89
+ try:
90
+ project_id = get_project_id()
91
+ client = TraceServiceClient(credentials=credentials)
92
+ exporter = CloudTraceSpanExporter(project_id=project_id, client=client)
93
+
94
+ resource_attrs = {
95
+ "service.name": "zen-orchestrator",
96
+ "service.version": os.getenv("ZEN_VERSION", "1.0.3"),
97
+ "telemetry.sdk.language": "python",
98
+ "telemetry.sdk.name": "opentelemetry",
99
+ "zen.analytics.type": "community",
100
+ }
101
+
102
+ resource = Resource.create(resource_attrs)
103
+ provider = TracerProvider(resource=resource)
104
+ provider.add_span_processor(BatchSpanProcessor(exporter))
105
+
106
+ trace.set_tracer_provider(provider)
107
+ self._provider = provider
108
+ self._tracer = trace.get_tracer("zen.telemetry")
109
+ self._enabled = True
110
+ logger.info("Telemetry initialized with community credentials")
111
+ except Exception as exc: # pragma: no cover - defensive guard
112
+ logger.warning(f"Failed to initialize telemetry: {exc}")
113
+ self._enabled = False
114
+ self._provider = None
115
+ self._tracer = None
116
+
117
+ # Public API -----------------------------------------------------
118
+
119
+ def is_enabled(self) -> bool:
120
+ return self._enabled and self._tracer is not None
121
+
122
+ def record_instance_span(
123
+ self,
124
+ batch_id: str,
125
+ instance_name: str,
126
+ status: Any,
127
+ config: Any,
128
+ cost_usd: Optional[float] = None,
129
+ workspace: Optional[str] = None,
130
+ ) -> None:
131
+ if not self.is_enabled():
132
+ return
133
+
134
+ assert self._tracer is not None # mypy hint
135
+
136
+ attributes: Dict[str, Any] = {
137
+ "zen.batch.id": batch_id,
138
+ "zen.instance.name": instance_name,
139
+ "zen.instance.status": getattr(status, "status", "unknown"),
140
+ "zen.instance.success": getattr(status, "status", "") == "completed",
141
+ "zen.instance.permission_mode": getattr(config, "permission_mode", "unknown"),
142
+ "zen.instance.tool_calls": getattr(status, "tool_calls", 0),
143
+ "zen.tokens.total": getattr(status, "total_tokens", 0),
144
+ "zen.tokens.input": getattr(status, "input_tokens", 0),
145
+ "zen.tokens.output": getattr(status, "output_tokens", 0),
146
+ "zen.tokens.cache.read": getattr(status, "cache_read_tokens", 0),
147
+ "zen.tokens.cache.creation": getattr(status, "cache_creation_tokens", 0),
148
+ "zen.tokens.cached_total": getattr(status, "cached_tokens", 0),
149
+ }
150
+
151
+ start_time = getattr(status, "start_time", None)
152
+ end_time = getattr(status, "end_time", None)
153
+ if start_time and end_time:
154
+ attributes["zen.instance.duration_ms"] = int((end_time - start_time) * 1000)
155
+
156
+ command = getattr(config, "command", None) or getattr(config, "prompt", None)
157
+ if isinstance(command, str) and command.startswith("/"):
158
+ attributes["zen.instance.command_type"] = "slash"
159
+ attributes["zen.instance.command"] = command
160
+ elif isinstance(command, str):
161
+ attributes["zen.instance.command_type"] = "prompt"
162
+ else:
163
+ attributes["zen.instance.command_type"] = "unknown"
164
+
165
+ session_id = getattr(config, "session_id", None)
166
+ if session_id:
167
+ session_hash = hashlib.sha256(session_id.encode("utf-8")).hexdigest()[:16]
168
+ attributes["zen.session.hash"] = session_hash
169
+
170
+ if workspace:
171
+ workspace_hash = hashlib.sha256(workspace.encode("utf-8")).hexdigest()[:16]
172
+ attributes["zen.workspace.hash"] = workspace_hash
173
+
174
+ # Tool metadata
175
+ tool_tokens = getattr(status, "tool_tokens", {}) or {}
176
+ attributes["zen.tools.unique"] = len(tool_tokens)
177
+ total_tool_tokens = 0
178
+ for tool_name, tokens in tool_tokens.items():
179
+ sanitized = _sanitize_tool_name(tool_name)
180
+ attributes[f"zen.tools.tokens.{sanitized}"] = int(tokens)
181
+ total_tool_tokens += int(tokens)
182
+ attributes["zen.tokens.tools_total"] = total_tool_tokens
183
+
184
+ tool_details = getattr(status, "tool_details", {}) or {}
185
+ for tool_name, count in tool_details.items():
186
+ sanitized = _sanitize_tool_name(tool_name)
187
+ attributes[f"zen.tools.invocations.{sanitized}"] = int(count)
188
+
189
+ # Cost metadata
190
+ if cost_usd is not None:
191
+ attributes["zen.cost.usd_total"] = round(float(cost_usd), 6)
192
+
193
+ reported_cost = getattr(status, "total_cost_usd", None)
194
+ if reported_cost is not None:
195
+ attributes["zen.cost.usd_reported"] = round(float(reported_cost), 6)
196
+
197
+ # Derive cost components using fallback pricing (USD per million tokens)
198
+ input_tokens = getattr(status, "input_tokens", 0)
199
+ output_tokens = getattr(status, "output_tokens", 0)
200
+ cache_read_tokens = getattr(status, "cache_read_tokens", 0)
201
+ cache_creation_tokens = getattr(status, "cache_creation_tokens", 0)
202
+
203
+ input_cost = (input_tokens / 1_000_000) * 3.00
204
+ output_cost = (output_tokens / 1_000_000) * 15.00
205
+ cache_read_cost = (cache_read_tokens / 1_000_000) * (3.00 * 0.1)
206
+ cache_creation_cost = (cache_creation_tokens / 1_000_000) * (3.00 * 1.25)
207
+ tool_cost = (total_tool_tokens / 1_000_000) * 3.00
208
+
209
+ attributes.update(
210
+ {
211
+ "zen.cost.usd_input": round(input_cost, 6),
212
+ "zen.cost.usd_output": round(output_cost, 6),
213
+ "zen.cost.usd_cache_read": round(cache_read_cost, 6),
214
+ "zen.cost.usd_cache_creation": round(cache_creation_cost, 6),
215
+ "zen.cost.usd_tools": round(tool_cost, 6),
216
+ }
217
+ )
218
+
219
+ # Emit span
220
+ try:
221
+ with self._tracer.start_as_current_span(
222
+ "zen.instance", kind=SpanKind.INTERNAL
223
+ ) as span:
224
+ for key, value in attributes.items():
225
+ span.set_attribute(key, value)
226
+ except GoogleAPICallError as exc: # pragma: no cover - network failure safety
227
+ logger.warning(f"Failed to export telemetry span: {exc}")
228
+
229
+ def shutdown(self) -> None:
230
+ if not self._provider:
231
+ return
232
+ try:
233
+ if hasattr(self._provider, "force_flush"):
234
+ self._provider.force_flush()
235
+ if hasattr(self._provider, "shutdown"):
236
+ self._provider.shutdown()
237
+ except Exception as exc: # pragma: no cover
238
+ logger.debug(f"Telemetry shutdown warning: {exc}")
239
+
240
+
241
+ def _build_manager() -> TelemetryManager | _NoOpTelemetryManager:
242
+ if not (OPENTELEMETRY_AVAILABLE and GCP_EXPORT_AVAILABLE):
243
+ return _NoOpTelemetryManager()
244
+ return TelemetryManager()
245
+
246
+
247
+ telemetry_manager = _build_manager()
248
+
249
+ __all__ = ["TelemetryManager", "telemetry_manager"]
zen_orchestrator.py CHANGED
@@ -2,41 +2,43 @@
2
2
  """
3
3
  Usage Examples:
4
4
 
5
+ zen -h # Help
6
+
5
7
  Direct Command Execution:
6
- python zen_orchestrator.py "/help" # Execute single command directly
7
- python zen_orchestrator.py "/analyze-code" --workspace ~/my-project
8
- python zen_orchestrator.py "/debug-issue" --instance-name "debug-session"
9
- python zen_orchestrator.py "/optimize-performance" --session-id "perf-1"
10
- python zen_orchestrator.py "/generate-docs" --clear-history --compact-history
8
+ zen "/single-command-in-claude-commands" # Execute single command directly
9
+ zen "/analyze-code" --workspace ~/my-project
10
+ zen "/debug-issue" --instance-name "debug-session"
11
+ zen "/optimize-performance" --session-id "perf-1"
12
+ zen "/generate-docs" --clear-history --compact-history
11
13
 
12
14
  Configuration File Mode:
13
- python zen_orchestrator.py --config config.json
14
- python zen_orchestrator.py --config config.json --workspace ~/my-project
15
+ zen --config config.json
16
+ zen --config config.json --workspace ~/my-project
15
17
 
16
18
  Default Instances Mode:
17
- python zen_orchestrator.py --dry-run # Auto-detects workspace from project root
18
- python zen_orchestrator.py --workspace ~/my-project --dry-run # Override workspace
19
- python zen_orchestrator.py --startup-delay 2.0 # 2 second delay between launches
20
- python zen_orchestrator.py --startup-delay 0.5 # 0.5 second delay between launches
21
- python zen_orchestrator.py --max-line-length 1000 # Longer output lines
22
- python zen_orchestrator.py --status-report-interval 60 # Status reports every 60s
23
- python zen_orchestrator.py --quiet # Minimal output, errors only
19
+ zen --dry-run # Auto-detects workspace from project root
20
+ zen --workspace ~/my-project --dry-run # Override workspace
21
+ zen --startup-delay 2.0 # 2 second delay between launches
22
+ zen --startup-delay 0.5 # 0.5 second delay between launches
23
+ zen --max-line-length 1000 # Longer output lines
24
+ zen --status-report-interval 60 # Status reports every 60s
25
+ zen --quiet # Minimal output, errors only
24
26
 
25
27
  Command Discovery:
26
- python zen_orchestrator.py --list-commands # Show all available commands
27
- python zen_orchestrator.py --inspect-command "/analyze-code" # Inspect specific command
28
+ zen --list-commands # Show all available commands
29
+ zen --inspect-command "/analyze-code" # Inspect specific command
28
30
 
29
31
  Scheduling:
30
- python zen_orchestrator.py "/analyze-code" --start-at "2h" # Start 2 hours from now
31
- python zen_orchestrator.py "/debug-issue" --start-at "30m" # Start in 30 minutes
32
- python zen_orchestrator.py "/optimize" --start-at "1am" # Start at 1 AM (today or tomorrow)
33
- python zen_orchestrator.py "/review-code" --start-at "14:30" # Start at 2:30 PM (today or tomorrow)
34
- python zen_orchestrator.py "/generate-docs" --start-at "10:30pm" # Start at 10:30 PM (today or tomorrow)
32
+ zen "/analyze-code" --start-at "2h" # Start 2 hours from now
33
+ zen "/debug-issue" --start-at "30m" # Start in 30 minutes
34
+ zen "/optimize" --start-at "1am" # Start at 1 AM (today or tomorrow)
35
+ zen "/review-code" --start-at "14:30" # Start at 2:30 PM (today or tomorrow)
36
+ zen "/generate-docs" --start-at "10:30pm" # Start at 10:30 PM (today or tomorrow)
35
37
 
36
38
  Precedence Rules:
37
- 1. Direct command (highest) - python zen_orchestrator.py "/command"
38
- 2. Config file (medium) - python zen_orchestrator.py --config file.json
39
- 3. Default instances (lowest) - python zen_orchestrator.py
39
+ 1. Direct command (highest) - zen "/command"
40
+ 2. Config file (medium) - zen --config file.json # expected default usage pattern
41
+ 3. Default instances (lowest) - zen
40
42
  """
41
43
 
42
44
  import asyncio
@@ -58,6 +60,11 @@ import re
58
60
  from uuid import uuid4, UUID
59
61
  from enum import Enum
60
62
 
63
+ try:
64
+ from zen.telemetry import telemetry_manager
65
+ except Exception: # pragma: no cover - telemetry optional
66
+ telemetry_manager = None
67
+
61
68
  # Add token budget imports with proper path handling
62
69
  sys.path.insert(0, str(Path(__file__).parent))
63
70
  try:
@@ -77,15 +84,6 @@ except ImportError as e:
77
84
  ClaudePricingEngine = None
78
85
  TokenUsageData = None
79
86
 
80
- # Add CLI extensions imports
81
- try:
82
- from cli_extensions import handle_example_commands
83
- except ImportError as e:
84
- # Graceful fallback if CLI extensions are not available
85
- handle_example_commands = None
86
-
87
- # NetraOptimizer database functionality has been removed for security
88
- # Local token metrics are preserved without database persistence
89
87
 
90
88
  # Setup logging
91
89
  logging.basicConfig(
@@ -197,6 +195,7 @@ class InstanceStatus:
197
195
  tool_details: Dict[str, int] = None # Tool name -> usage count
198
196
  tool_tokens: Dict[str, int] = None # Tool name -> token usage
199
197
  tool_id_mapping: Dict[str, str] = field(default_factory=dict) # tool_use_id -> tool name mapping
198
+ telemetry_recorded: bool = False
200
199
 
201
200
  def __post_init__(self):
202
201
  """Initialize fields that need special handling"""
@@ -214,7 +213,7 @@ class ClaudeInstanceOrchestrator:
214
213
 
215
214
  def __init__(self, workspace_dir: Path, max_console_lines: int = 5, startup_delay: float = 1.0,
216
215
  max_line_length: int = 500, status_report_interval: int = 30,
217
- use_cloud_sql: bool = False, quiet: bool = False,
216
+ quiet: bool = False,
218
217
  overall_token_budget: Optional[int] = None,
219
218
  overall_cost_budget: Optional[float] = None,
220
219
  budget_type: str = "tokens",
@@ -233,11 +232,10 @@ class ClaudeInstanceOrchestrator:
233
232
  self.status_report_interval = status_report_interval # Seconds between status reports
234
233
  self.last_status_report = time.time()
235
234
  self.status_report_task = None # For the rolling status report task
236
- self.use_cloud_sql = use_cloud_sql
237
235
  self.quiet = quiet
238
236
  self.log_level = log_level
239
237
  self.batch_id = str(uuid4()) # Generate batch ID for this orchestration run
240
- # Database client removed for security - local metrics only
238
+
241
239
  self.optimizer = None
242
240
 
243
241
  # Initialize budget manager if any budget settings are provided
@@ -278,12 +276,6 @@ class ClaudeInstanceOrchestrator:
278
276
  else:
279
277
  logger.debug("Token budget tracking disabled (no budget specified)")
280
278
 
281
- # Configure CloudSQL if requested
282
- # CloudSQL functionality available with Netra Apex
283
- # Token metrics are now handled locally without database persistence
284
- if use_cloud_sql:
285
- logger.warning("CloudSQL functionality has been disabled. Token metrics will be displayed locally only.")
286
- logger.info("For data persistence, consider upgrading to Netra Apex.")
287
279
 
288
280
  def log_at_level(self, level: LogLevel, message: str, log_func=None):
289
281
  """Log message only if current log level permits."""
@@ -338,36 +330,36 @@ class ClaudeInstanceOrchestrator:
338
330
  command_string = "; ".join(full_command)
339
331
 
340
332
  # Find the claude executable with Mac-specific paths
341
- claude_cmd = shutil.which("claude")
333
+ # IMPORTANT: Use direct paths to avoid shell functions that may have database dependencies
334
+ possible_paths = [
335
+ "/opt/homebrew/bin/claude", # Mac Homebrew ARM - prefer direct path
336
+ "/usr/local/bin/claude", # Mac Homebrew Intel
337
+ "~/.local/bin/claude", # User local install
338
+ "/usr/bin/claude", # System install
339
+ "claude.cmd", # Windows
340
+ "claude.exe", # Windows
341
+ ]
342
+
343
+ claude_cmd = None
344
+ for path in possible_paths:
345
+ # Expand user path if needed
346
+ expanded_path = Path(path).expanduser()
347
+ if expanded_path.exists() and expanded_path.is_file():
348
+ claude_cmd = str(expanded_path)
349
+ logger.info(f"Found Claude executable at: {claude_cmd}")
350
+ break
351
+
352
+ # Only use shutil.which as fallback if no direct path found
342
353
  if not claude_cmd:
343
- # Try common paths on different platforms
344
- possible_paths = [
345
- "claude.cmd", # Windows
346
- "claude.exe", # Windows
347
- "/opt/homebrew/bin/claude", # Mac Homebrew ARM
348
- "/usr/local/bin/claude", # Mac Homebrew Intel
349
- "~/.local/bin/claude", # User local install
350
- "/usr/bin/claude", # System install
351
- "claude" # Final fallback
352
- ]
353
-
354
- for path in possible_paths:
355
- # Expand user path if needed
356
- expanded_path = Path(path).expanduser()
357
- if expanded_path.exists():
358
- claude_cmd = str(expanded_path)
359
- logger.info(f"Found Claude executable at: {claude_cmd}")
360
- break
361
- elif shutil.which(path):
362
- claude_cmd = path
363
- logger.info(f"Found Claude executable via which: {claude_cmd}")
364
- break
354
+ claude_cmd = shutil.which("claude")
355
+ if claude_cmd:
356
+ logger.info(f"Found Claude executable via which: {claude_cmd}")
365
357
 
366
- if not claude_cmd or claude_cmd == "claude":
367
- logger.warning("Claude command not found in PATH or common locations")
368
- logger.warning("Please ensure Claude Code is installed and in your PATH")
369
- logger.warning("Install with: npm install -g @anthropic/claude-code")
370
- claude_cmd = "claude" # Fallback
358
+ if not claude_cmd:
359
+ logger.warning("Claude command not found in PATH or common locations")
360
+ logger.warning("Please ensure Claude Code is installed and in your PATH")
361
+ logger.warning("Install with: npm install -g @anthropic/claude-code")
362
+ claude_cmd = "/opt/homebrew/bin/claude" # Default fallback to most likely location
371
363
 
372
364
  # New approach: slash commands can be included directly in prompt
373
365
  cmd = [
@@ -494,6 +486,11 @@ class ClaudeInstanceOrchestrator:
494
486
  logger.error(f"🚫 BLOCK MODE: {message}")
495
487
  status.status = "failed"
496
488
  status.error = f"Blocked by budget limit - {reason}"
489
+ timestamp = time.time()
490
+ if status.start_time is None:
491
+ status.start_time = timestamp
492
+ status.end_time = timestamp
493
+ self._emit_instance_telemetry(name, config, status)
497
494
  return False
498
495
  else: # warn mode
499
496
  logger.warning(f"⚠️ WARN MODE: {message}")
@@ -571,18 +568,22 @@ class ClaudeInstanceOrchestrator:
571
568
  if returncode == 0:
572
569
  status.status = "completed"
573
570
  logger.info(f"Instance {name} completed successfully")
571
+ self._emit_instance_telemetry(name, config, status)
574
572
  return True
575
573
  else:
576
574
  status.status = "failed"
577
575
  logger.error(f"Instance {name} failed with return code {returncode}")
578
576
  if status.error:
579
577
  logger.error(f"Error output: {status.error}")
578
+ self._emit_instance_telemetry(name, config, status)
580
579
  return False
581
580
 
582
581
  except Exception as e:
583
582
  status.status = "failed"
584
583
  status.error = str(e)
585
584
  logger.error(f"Exception running instance {name}: {e}")
585
+ status.end_time = status.end_time or time.time()
586
+ self._emit_instance_telemetry(name, config, status)
586
587
  return False
587
588
 
588
589
  async def _save_metrics_to_database(self, name: str, config: InstanceConfig, status: InstanceStatus):
@@ -635,6 +636,38 @@ class ClaudeInstanceOrchestrator:
635
636
 
636
637
  return input_cost + output_cost + cache_read_cost + cache_creation_cost + tool_cost
637
638
 
639
+ def _emit_instance_telemetry(self, name: str, config: InstanceConfig, status: InstanceStatus) -> None:
640
+ """Send telemetry span with token usage and cost metadata."""
641
+
642
+ if telemetry_manager is None or not hasattr(telemetry_manager, "is_enabled"):
643
+ return
644
+
645
+ if getattr(status, "telemetry_recorded", False):
646
+ return
647
+
648
+ if not telemetry_manager.is_enabled():
649
+ return
650
+
651
+ cost_usd: Optional[float]
652
+ try:
653
+ cost_usd = self._calculate_cost(status)
654
+ except Exception as exc: # pragma: no cover - defensive guard
655
+ logger.debug(f"Cost calculation failed for telemetry span ({name}): {exc}")
656
+ cost_usd = None
657
+
658
+ try:
659
+ telemetry_manager.record_instance_span(
660
+ batch_id=self.batch_id,
661
+ instance_name=name,
662
+ status=status,
663
+ config=config,
664
+ cost_usd=cost_usd,
665
+ workspace=str(self.workspace_dir),
666
+ )
667
+ status.telemetry_recorded = True
668
+ except Exception as exc: # pragma: no cover - Network/export errors
669
+ logger.debug(f"Telemetry emission failed for {name}: {exc}")
670
+
638
671
  async def _stream_output(self, name: str, process):
639
672
  """Stream output in real-time for stream-json format (DEPRECATED - use _stream_output_parallel)"""
640
673
  status = self.statuses[name]
@@ -810,13 +843,19 @@ class ClaudeInstanceOrchestrator:
810
843
  for name, result in zip(self.instances.keys(), results):
811
844
  if isinstance(result, asyncio.TimeoutError):
812
845
  logger.error(f"Instance {name} timed out after {timeout}s")
813
- self.statuses[name].status = "failed"
814
- self.statuses[name].error = f"Timeout after {timeout}s"
846
+ status = self.statuses[name]
847
+ status.status = "failed"
848
+ status.error = f"Timeout after {timeout}s"
849
+ status.end_time = time.time()
850
+ self._emit_instance_telemetry(name, self.instances[name], status)
815
851
  final_results[name] = False
816
852
  elif isinstance(result, Exception):
817
853
  logger.error(f"Instance {name} failed with exception: {result}")
818
- self.statuses[name].status = "failed"
819
- self.statuses[name].error = str(result)
854
+ status = self.statuses[name]
855
+ status.status = "failed"
856
+ status.error = str(result)
857
+ status.end_time = time.time()
858
+ self._emit_instance_telemetry(name, self.instances[name], status)
820
859
  final_results[name] = False
821
860
  else:
822
861
  final_results[name] = result
@@ -2370,8 +2409,6 @@ async def main():
2370
2409
  help="Seconds between rolling status reports (default: 5)")
2371
2410
  parser.add_argument("--start-at", type=str, default=None,
2372
2411
  help="Schedule orchestration to start at specific time. Examples: '2h' (2 hours from now), '30m' (30 minutes), '14:30' (2:30 PM today), '1am' (1 AM today/tomorrow)")
2373
- parser.add_argument("--use-cloud-sql", action="store_true",
2374
- help="Save metrics to CloudSQL database (NetraOptimizer integration)")
2375
2412
 
2376
2413
  # Direct command options
2377
2414
  parser.add_argument("--instance-name", type=str, help="Instance name for direct command execution")
@@ -2454,10 +2491,6 @@ async def main():
2454
2491
 
2455
2492
  logger.info(f"Using workspace: {workspace}")
2456
2493
 
2457
- # Handle CLI extension commands
2458
- if handle_example_commands and handle_example_commands(args):
2459
- return
2460
-
2461
2494
 
2462
2495
  # Load instance configurations with direct command precedence
2463
2496
  direct_instance = create_direct_instance(args, workspace)
@@ -2547,7 +2580,6 @@ async def main():
2547
2580
  startup_delay=args.startup_delay,
2548
2581
  max_line_length=args.max_line_length,
2549
2582
  status_report_interval=args.status_report_interval,
2550
- use_cloud_sql=args.use_cloud_sql,
2551
2583
  quiet=args.quiet,
2552
2584
  overall_token_budget=final_overall_budget,
2553
2585
  overall_cost_budget=final_overall_cost_budget,
@@ -2775,9 +2807,6 @@ async def main():
2775
2807
 
2776
2808
  # Run all instances
2777
2809
  logger.info("Starting Claude Code instance orchestration")
2778
- if args.use_cloud_sql:
2779
- logger.info(f"Batch ID: {orchestrator.batch_id}")
2780
- logger.info("Metrics will be saved to CloudSQL")
2781
2810
  start_time = time.time()
2782
2811
 
2783
2812
  results = await orchestrator.run_all_instances(args.timeout)
@@ -2909,19 +2938,17 @@ async def main():
2909
2938
 
2910
2939
  # For detailed data access
2911
2940
  print("\n" + "="*80)
2912
- print("🚀 NEED DETAILED TOKEN ANALYTICS & DATA ACCESS?")
2941
+ print("🚀 Looking for more?")
2913
2942
  print("="*80)
2914
- print("For comprehensive token usage analytics, historical data,")
2915
- print("and advanced reporting features, upgrade to Netra Apex.")
2943
+ print("Explore Zen with Apex for the most effective AI Ops value for production AI.")
2916
2944
  print("")
2917
2945
  print("🌐 Learn more: https://netrasystems.ai/")
2918
2946
  print("="*80)
2919
2947
 
2920
- # Show CloudSQL info if enabled
2921
- if args.use_cloud_sql:
2922
- print(f"\n📊 Local metrics displayed above")
2923
- print(f" Batch ID: {orchestrator.batch_id}")
2924
- print(f" Database persistence disabled for security")
2948
+
2949
+ # Flush telemetry before exit
2950
+ if telemetry_manager is not None and hasattr(telemetry_manager, "shutdown"):
2951
+ telemetry_manager.shutdown()
2925
2952
 
2926
2953
  # Exit with appropriate code
2927
2954
  sys.exit(0 if summary['failed'] == 0 else 1)
@@ -2931,4 +2958,4 @@ def run():
2931
2958
  asyncio.run(main())
2932
2959
 
2933
2960
  if __name__ == "__main__":
2934
- run()
2961
+ run()
@@ -1,15 +0,0 @@
1
- zen_orchestrator.py,sha256=Q4bEnsyMYPJV6R9PutC6vrfxGiaOizlFjSW6qJDLrDI,148686
2
- agent_interface/__init__.py,sha256=OsbOKzElHsxhVgak87oOx_u46QNgKmz-Reis-plAMwk,525
3
- agent_interface/base_agent.py,sha256=GNskG9VaZgno7X24lQTpFdxUoQE0yJHLh0UPFJvOPn4,11098
4
- netra_zen-1.0.2.dist-info/licenses/LICENSE.md,sha256=t6LtOzAE2hgIIv5WbaN0wOcU3QCnGtAkMGNclHrKTOs,79
5
- token_budget/__init__.py,sha256=_2tmi72DGNtbYcZ-rGIxVKMytdkHFjzJaWz8bDhYACc,33
6
- token_budget/budget_manager.py,sha256=VRWxKcGDtgJfIRh-ztYQ4-wuhBvddVJJnyoGfxCBlv0,9567
7
- token_budget/models.py,sha256=14xFTk2-R1Ql0F9WLDof7vADrKC_5Fj7EE7UmZdoG00,2384
8
- token_budget/visualization.py,sha256=SaNnZ15edHXtjDCA5Yfu7w3AztCZRYsYCPGBqzapupY,719
9
- token_transparency/__init__.py,sha256=go86Rg4_VYAPLw3myVpLe1s1PbMu1llDTw1wfomP1lA,453
10
- token_transparency/claude_pricing_engine.py,sha256=9zWQJS3HJEs_lljil-xT1cUvG-Jf3ykNAninJFyfNSM,12630
11
- netra_zen-1.0.2.dist-info/METADATA,sha256=3iv4Hq5DNLRAMivXVCwx_jieqevK_G3d1OU0Tbksl-g,29442
12
- netra_zen-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- netra_zen-1.0.2.dist-info/entry_points.txt,sha256=oDehCnPGZezG0m9ZWspxjHLHyQ3eERX87eojR4ljaRo,45
14
- netra_zen-1.0.2.dist-info/top_level.txt,sha256=dHz-hgh_dfiiOUrPf8wW80fA31rfEYDFmA6fpu67Yjk,65
15
- netra_zen-1.0.2.dist-info/RECORD,,