agentops-cockpit 0.9.5__py3-none-any.whl → 0.9.8__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.
Files changed (60) hide show
  1. agent_ops_cockpit/agent.py +44 -77
  2. agent_ops_cockpit/cache/semantic_cache.py +10 -21
  3. agent_ops_cockpit/cli/main.py +105 -153
  4. agent_ops_cockpit/eval/load_test.py +33 -50
  5. agent_ops_cockpit/eval/quality_climber.py +88 -93
  6. agent_ops_cockpit/eval/red_team.py +84 -25
  7. agent_ops_cockpit/mcp_server.py +26 -93
  8. agent_ops_cockpit/ops/arch_review.py +221 -147
  9. agent_ops_cockpit/ops/auditors/base.py +50 -0
  10. agent_ops_cockpit/ops/auditors/behavioral.py +31 -0
  11. agent_ops_cockpit/ops/auditors/compliance.py +35 -0
  12. agent_ops_cockpit/ops/auditors/dependency.py +48 -0
  13. agent_ops_cockpit/ops/auditors/finops.py +48 -0
  14. agent_ops_cockpit/ops/auditors/graph.py +49 -0
  15. agent_ops_cockpit/ops/auditors/pivot.py +51 -0
  16. agent_ops_cockpit/ops/auditors/reasoning.py +67 -0
  17. agent_ops_cockpit/ops/auditors/reliability.py +53 -0
  18. agent_ops_cockpit/ops/auditors/security.py +87 -0
  19. agent_ops_cockpit/ops/auditors/sme_v12.py +76 -0
  20. agent_ops_cockpit/ops/auditors/sovereignty.py +74 -0
  21. agent_ops_cockpit/ops/auditors/sre_a2a.py +179 -0
  22. agent_ops_cockpit/ops/benchmarker.py +97 -0
  23. agent_ops_cockpit/ops/cost_optimizer.py +15 -24
  24. agent_ops_cockpit/ops/discovery.py +214 -0
  25. agent_ops_cockpit/ops/evidence_bridge.py +30 -63
  26. agent_ops_cockpit/ops/frameworks.py +124 -1
  27. agent_ops_cockpit/ops/git_portal.py +74 -0
  28. agent_ops_cockpit/ops/mcp_hub.py +19 -42
  29. agent_ops_cockpit/ops/orchestrator.py +477 -277
  30. agent_ops_cockpit/ops/policy_engine.py +38 -38
  31. agent_ops_cockpit/ops/reliability.py +121 -52
  32. agent_ops_cockpit/ops/remediator.py +54 -0
  33. agent_ops_cockpit/ops/secret_scanner.py +34 -22
  34. agent_ops_cockpit/ops/swarm.py +17 -27
  35. agent_ops_cockpit/ops/ui_auditor.py +67 -6
  36. agent_ops_cockpit/ops/watcher.py +41 -70
  37. agent_ops_cockpit/ops/watchlist.json +30 -0
  38. agent_ops_cockpit/optimizer.py +161 -384
  39. agent_ops_cockpit/tests/test_arch_review.py +6 -6
  40. agent_ops_cockpit/tests/test_discovery.py +96 -0
  41. agent_ops_cockpit/tests/test_ops_core.py +56 -0
  42. agent_ops_cockpit/tests/test_orchestrator_fleet.py +73 -0
  43. agent_ops_cockpit/tests/test_persona_architect.py +75 -0
  44. agent_ops_cockpit/tests/test_persona_finops.py +31 -0
  45. agent_ops_cockpit/tests/test_persona_security.py +55 -0
  46. agent_ops_cockpit/tests/test_persona_sre.py +43 -0
  47. agent_ops_cockpit/tests/test_persona_ux.py +42 -0
  48. agent_ops_cockpit/tests/test_quality_climber.py +2 -2
  49. agent_ops_cockpit/tests/test_remediator.py +75 -0
  50. agent_ops_cockpit/tests/test_ui_auditor.py +52 -0
  51. agentops_cockpit-0.9.8.dist-info/METADATA +172 -0
  52. agentops_cockpit-0.9.8.dist-info/RECORD +71 -0
  53. agent_ops_cockpit/tests/test_optimizer.py +0 -68
  54. agent_ops_cockpit/tests/test_red_team.py +0 -35
  55. agent_ops_cockpit/tests/test_secret_scanner.py +0 -24
  56. agentops_cockpit-0.9.5.dist-info/METADATA +0 -246
  57. agentops_cockpit-0.9.5.dist-info/RECORD +0 -47
  58. {agentops_cockpit-0.9.5.dist-info → agentops_cockpit-0.9.8.dist-info}/WHEEL +0 -0
  59. {agentops_cockpit-0.9.5.dist-info → agentops_cockpit-0.9.8.dist-info}/entry_points.txt +0 -0
  60. {agentops_cockpit-0.9.5.dist-info → agentops_cockpit-0.9.8.dist-info}/licenses/LICENSE +0 -0
@@ -9,7 +9,7 @@ app = typer.Typer(help="Face Auditor: Scan frontend code for A2UI alignment.")
9
9
  console = Console()
10
10
 
11
11
  @app.command()
12
- def audit(path: str = "src"):
12
+ def audit(path: str = typer.Argument("src", help="Directory to scan")):
13
13
  """
14
14
  Step 4: Frontend / A2UI Auditing.
15
15
  Ensures frontend components are properly mapping surfaceId and detecting triggers.
@@ -28,13 +28,19 @@ def audit(path: str = "src"):
28
28
  a11y_pattern = re.compile(r"aria-label|role=|tabIndex|alt=")
29
29
  legal_pattern = re.compile(r"Copyright|PrivacyPolicy|Disclaimer|TermsOfService|©")
30
30
  marketing_pattern = re.compile(r"og:image|meta\s+name=['\"]description['\"]|favicon|logo")
31
+ hitl_pattern = re.compile(r"HumanInTheLoop|confirm|Approve|Reject|Gate")
32
+ streaming_pattern = re.compile(r"Suspense|Stream|Markdown|chunk")
31
33
 
32
34
  for root, dirs, files in os.walk(path):
33
35
  if any(d in root for d in [".venv", "node_modules", ".git", "dist"]):
34
36
  continue
35
37
 
36
38
  for file in files:
39
+ # Skip non-component files to reduce noise
37
40
  if file.endswith((".tsx", ".ts", ".js", ".jsx")):
41
+ if any(x in file.lower() for x in ["config", "test", "spec", "d.ts", "setup", "main", "index"]):
42
+ continue
43
+
38
44
  files_scanned += 1
39
45
  file_path = os.path.join(root, file)
40
46
  rel_path = os.path.relpath(file_path, ".")
@@ -78,15 +84,59 @@ def audit(path: str = "src"):
78
84
  if not marketing_pattern.search(content) and ("index" in file.lower() or "head" in file.lower() or "App" in file):
79
85
  findings.append({"line": 1, "issue": "Missing Branding (Logo) or SEO Metadata (OG/Description)", "fix": "Add meta tags (og:image, description) and project logo."})
80
86
 
87
+ if not hitl_pattern.search(content) and ("Action" in file or "Tool" in file or "Transfer" in file):
88
+ findings.append({"line": 1, "issue": "Missing HITL (Human-in-the-Loop) Gating", "fix": "Add confirmation modals or 'Approve/Reject' gates for high-impact actions."})
89
+
90
+ if not streaming_pattern.search(content) and ("Chat" in file or "Thread" in file or "Log" in file):
91
+ findings.append({"line": 1, "issue": "Missing Streaming Resilience (Suspense/Stream)", "fix": "Implement Suspense or stream-aware handlers to prevent UI flickering during token rendering."})
92
+
81
93
  if findings:
82
94
  issues.append({"file": rel_path, "findings": findings})
83
95
 
84
96
  except Exception:
85
97
  pass
86
98
 
99
+ # Quantitative Scoring (Principal v1.2)
100
+ max_score = 100
101
+ deductions = {
102
+ "Missing 'surfaceId'": 20,
103
+ "Missing 'Thinking' feedback": 15,
104
+ "Missing HITL": 15,
105
+ "Missing Streaming Resilience": 10,
106
+ "interactive component without triggers": 10,
107
+ "Missing a11y labels": 10,
108
+ "Missing Legal/SEO": 5
109
+ }
110
+
111
+ total_deduction = 0
112
+ unique_issues = set()
113
+ for issue in issues:
114
+ for finding in issue["findings"]:
115
+ for key, val in deductions.items():
116
+ if key.lower() in finding["issue"].lower():
117
+ if key not in unique_issues:
118
+ total_deduction += val
119
+ unique_issues.add(key)
120
+
121
+ score = max(0, max_score - total_deduction)
122
+ verdict = "✅ APPROVED" if score >= 85 else "⚠️ WARN" if score >= 60 else "❌ REJECTED"
123
+
87
124
  console.print(f"📝 Scanned [bold]{files_scanned}[/bold] frontend files.")
88
125
 
89
- table = Table(title="🔍 A2UI Audit Findings")
126
+ # Executive Summary Table (Product View v1.2)
127
+ summary = Table(title="💎 PRINCIPAL UX EVALUATION (v1.2)", box=None)
128
+ summary.add_column("Metric", style="cyan")
129
+ summary.add_column("Value", style="bold white")
130
+ summary.add_row("GenUI Readiness Score", f"{score}/100")
131
+ summary.add_row("Consensus Verdict", f"[{'green' if score >= 85 else 'yellow' if score >= 60 else 'red'}]{verdict}[/]")
132
+ summary.add_row("A2UI Registry Depth", "Fragmented" if "Missing 'surfaceId'" in unique_issues else "Aligned")
133
+ summary.add_row("Latency Tolerance", "Low" if "Missing 'Thinking' feedback" in unique_issues else "Premium")
134
+ summary.add_row("Autonomous Risk (HITL)", "HIGH" if "Missing HITL" in unique_issues else "Secured")
135
+ summary.add_row("Streaming Fluidity", "Flicker-Prone" if "Missing Streaming Resilience" in unique_issues else "Smooth")
136
+
137
+ console.print(Panel(summary, border_style="green" if score >= 85 else "yellow"))
138
+
139
+ table = Table(title="🔍 A2UI DETAILED FINDINGS")
90
140
  table.add_column("File:Line", style="cyan")
91
141
  table.add_column("Issue", style="red")
92
142
  table.add_column("Recommended Fix", style="green")
@@ -104,12 +154,23 @@ def audit(path: str = "src"):
104
154
 
105
155
  console.print("\n", table)
106
156
 
107
-
108
- if len(issues) > 5:
109
- console.print("\n⚠️ [yellow]Recommendation:[/yellow] Your 'Face' layer has fragmented A2UI surface mappings.")
110
- console.print("💡 Use the A2UI Registry to unify how your agent logic triggers visual surfaces.")
157
+ if score < 85:
158
+ console.print(f"\n💡 [bold yellow]UX Principal Recommendation:[/bold yellow] Your 'Face' layer needs {100-score}% more alignment.")
159
+ if "Missing 'surfaceId'" in unique_issues:
160
+ console.print(" - Map components to 'surfaceId' to enable agent-driven UI updates.")
161
+ if "Missing 'Thinking' feedback" in unique_issues:
162
+ console.print(" - Add 'Skeleton' screens to mask LLM reasoning latency.")
163
+ if "Missing HITL" in unique_issues:
164
+ console.print(" - Implement Human-in-the-Loop gates (modals/approvals) for sensitive impacts.")
165
+ if "Missing Streaming Resilience" in unique_issues:
166
+ console.print(" - Use 'Suspense' boundaries to handle live token streaming without flicker.")
111
167
  else:
112
168
  console.print("\n✅ [bold green]Frontend is Well-Architected for GenUI interactions.[/bold green]")
113
169
 
170
+ @app.command()
171
+ def version():
172
+ """Show the version of the Face Auditor."""
173
+ console.print('[bold cyan]v1.3.0[/bold cyan]')
174
+
114
175
  if __name__ == "__main__":
115
176
  app()
@@ -1,3 +1,8 @@
1
+ from tenacity import retry, wait_exponential, stop_after_attempt
2
+ from tenacity import retry, wait_exponential, stop_after_attempt
3
+ from tenacity import retry, wait_exponential, stop_after_attempt
4
+ from tenacity import retry, wait_exponential, stop_after_attempt
5
+ from tenacity import retry, wait_exponential, stop_after_attempt
1
6
  import json
2
7
  import os
3
8
  import urllib.request
@@ -11,21 +16,18 @@ from packaging import version
11
16
  from rich.console import Console
12
17
  from rich.table import Table
13
18
  from rich.panel import Panel
14
-
15
19
  console = Console()
16
-
17
- WATCHLIST_PATH = os.path.join(os.path.dirname(__file__), "watchlist.json")
20
+ WATCHLIST_PATH = os.path.join(os.path.dirname(__file__), 'watchlist.json')
18
21
 
19
22
  def get_local_version(package_name: str) -> str:
20
23
  try:
21
24
  return importlib.metadata.version(package_name)
22
25
  except importlib.metadata.PackageNotFoundError:
23
- return "Not Installed"
26
+ return 'Not Installed'
24
27
 
25
28
  def clean_version(v_str: str) -> str:
26
29
  """Extracts a clean version number from strings like 'v1.2.3', 'package==1.2.3', '2026-01-28 (v0.1.0)'"""
27
- # Look for patterns like X.Y.Z or X.Y
28
- match = re.search(r'(\d+\.\d+(?:\.\d+)?(?:[a-zA-Z]+\d+)?)', v_str)
30
+ match = re.search('(\\d+\\.\\d+(?:\\.\\d+)?(?:[a-zA-Z]+\\d+)?)', v_str)
29
31
  if match:
30
32
  return match.group(1)
31
33
  return v_str.strip().lstrip('v')
@@ -37,102 +39,71 @@ def fetch_latest_from_atom(url: str) -> Optional[Dict[str, str]]:
37
39
  tree = ET.parse(response)
38
40
  root = tree.getroot()
39
41
  ns = {'ns': 'http://www.w3.org/2005/Atom'}
40
-
41
42
  latest_entry = root.find('ns:entry', ns)
42
43
  if latest_entry is not None:
43
44
  title = latest_entry.find('ns:title', ns).text
44
45
  updated = latest_entry.find('ns:updated', ns).text
45
46
  raw_v = title.strip().split()[-1]
46
- return {
47
- "version": clean_version(raw_v) if "==" not in raw_v else clean_version(raw_v.split("==")[-1]),
48
- "date": updated,
49
- "title": title
50
- }
47
+ return {'version': clean_version(raw_v) if '==' not in raw_v else clean_version(raw_v.split('==')[-1]), 'date': updated, 'title': title}
51
48
  except Exception:
52
- # console.print(f"[dim red]Error fetching {url}: {e}[/dim red]")
53
49
  return None
54
50
  return None
55
51
 
56
52
  def run_watch():
57
- console.print(Panel.fit("🔍 [bold blue]AGENTOPS COCKPIT: ECOSYSTEM WATCHER[/bold blue]", border_style="blue"))
58
-
53
+ console.print(Panel.fit('🔍 [bold blue]AGENTOPS COCKPIT: ECOSYSTEM WATCHER[/bold blue]', border_style='blue'))
59
54
  if not os.path.exists(WATCHLIST_PATH):
60
- console.print(f"❌ [red]Watchlist not found at {WATCHLIST_PATH}[/red]")
55
+ console.print(f'❌ [red]Watchlist not found at {WATCHLIST_PATH}[/red]')
61
56
  return
62
-
63
57
  with open(WATCHLIST_PATH, 'r') as f:
64
58
  watchlist = json.load(f)
65
-
66
- table = Table(title=f"Ecosystem Pulse - {datetime.now().strftime('%Y-%m-%d')}", show_header=True, header_style="bold magenta")
67
- table.add_column("Category", style="cyan")
68
- table.add_column("Component", style="white")
69
- table.add_column("Local", style="yellow")
70
- table.add_column("Latest", style="green")
71
- table.add_column("Status", justify="center")
72
-
59
+ table = Table(title=f"Ecosystem Pulse - {datetime.now().strftime('%Y-%m-%d')}", show_header=True, header_style='bold magenta')
60
+ table.add_column('Category', style='cyan')
61
+ table.add_column('Component', style='white')
62
+ table.add_column('Local', style='yellow')
63
+ table.add_column('Latest', style='green')
64
+ table.add_column('Status', justify='center')
73
65
  updates_found = []
74
-
75
66
  for category, items in watchlist.items():
67
+ if not isinstance(items, dict):
68
+ continue
76
69
  for name, info in items.items():
77
- package = info.get("package")
70
+ if not isinstance(info, dict) or 'feed' not in info:
71
+ continue
72
+ package = info.get('package')
78
73
  local_v_raw = get_local_version(package) if package else None
79
- local_v = local_v_raw if local_v_raw else "N/A"
80
-
81
- with console.status(f"[dim]Checking {name}..."):
82
- latest_info = fetch_latest_from_atom(info["feed"])
83
-
74
+ local_v = local_v_raw if local_v_raw else 'N/A'
75
+ with console.status(f'[dim]Checking {name}...'):
76
+ latest_info = fetch_latest_from_atom(info['feed'])
84
77
  if latest_info:
85
- latest_v = latest_info["version"]
86
-
78
+ latest_v = latest_info['version']
87
79
  is_outdated = False
88
- if local_v_raw and local_v_raw != "Not Installed":
80
+ if local_v_raw and local_v_raw != 'Not Installed':
89
81
  try:
90
82
  is_outdated = version.parse(latest_v) > version.parse(local_v_raw)
91
83
  except Exception:
92
84
  is_outdated = latest_v > local_v_raw
93
-
94
- status = "🚨 [bold red]UPDATE[/bold red]" if is_outdated else "✅ [green]OK[/green]"
95
- if local_v == "Not Installed":
96
- status = "➕ [dim]NEW[/dim]"
85
+ status = '🚨 [bold red]UPDATE[/bold red]' if is_outdated else '✅ [green]OK[/green]'
86
+ if local_v == 'Not Installed':
87
+ status = '➕ [dim]NEW[/dim]'
97
88
  if package is None:
98
- status = "🌐 [blue]SPEC[/blue]"
99
-
100
- display_local = local_v if local_v != "Not Installed" else "[dim]Not Installed[/dim]"
101
- table.add_row(
102
- category.upper(),
103
- name,
104
- display_local,
105
- latest_v,
106
- status
107
- )
108
-
89
+ status = '🌐 [blue]SPEC[/blue]'
90
+ display_local = local_v if local_v != 'Not Installed' else '[dim]Not Installed[/dim]'
91
+ table.add_row(category.upper(), name, display_local, latest_v, status)
109
92
  if is_outdated:
110
- updates_found.append({
111
- "name": name,
112
- "current": local_v,
113
- "latest": latest_v,
114
- "package": package,
115
- "desc": info["description"]
116
- })
93
+ updates_found.append({'name': name, 'current': local_v, 'latest': latest_v, 'package': package, 'desc': info['description']})
117
94
  else:
118
- table.add_row(category.upper(), name, local_v, "[red]Fetch Failed[/red]", "")
119
-
95
+ table.add_row(category.upper(), name, local_v, '[red]Fetch Failed[/red]', '')
120
96
  console.print(table)
121
-
122
97
  if updates_found:
123
- console.print("\n[bold yellow]⚠️ Actionable Intelligence:[/bold yellow]")
98
+ console.print('\n[bold yellow]⚠️ Actionable Intelligence:[/bold yellow]')
124
99
  for up in updates_found:
125
100
  console.print(f"• [bold]{up['name']}[/bold]: {up['current']} ➔ [bold green]{up['latest']}[/bold green]")
126
101
  console.print(f" [dim]{up['desc']}[/dim]")
127
-
128
- pkgs = " ".join([u['package'] for u in updates_found if u['package']])
102
+ pkgs = ' '.join([u['package'] for u in updates_found if u['package']])
129
103
  if pkgs:
130
- console.print(f"\n[bold cyan]Pro-tip:[/bold cyan] Run `pip install --upgrade {pkgs}` to sync.")
131
-
132
- # Exit with special code to signal updates to CI/CD
104
+ console.print(f'\n[bold cyan]Pro-tip:[/bold cyan] Run `pip install --upgrade {pkgs}` to sync.')
133
105
  sys.exit(2)
134
106
  else:
135
- console.print("\n[bold green]✨ All components are currently in sync with the latest stable releases.[/bold green]")
136
-
137
- if __name__ == "__main__":
138
- run_watch()
107
+ console.print('\n[bold green]✨ All components are currently in sync with the latest stable releases.[/bold green]')
108
+ if __name__ == '__main__':
109
+ run_watch()
@@ -43,6 +43,36 @@
43
43
  "package": "google-cloud-aiplatform",
44
44
  "description": "Vertex AI Agent Engine - Managed agent execution",
45
45
  "min_version_for_optimizations": "1.70.0"
46
+ },
47
+ "pinecone": {
48
+ "feed": "https://github.com/pinecone-io/pinecone-python-client/releases.atom",
49
+ "package": "pinecone-client",
50
+ "description": "Pinecone - Managed vector database for RAG"
51
+ },
52
+ "weaviate": {
53
+ "feed": "https://github.com/weaviate/weaviate-python-client/releases.atom",
54
+ "package": "weaviate-client",
55
+ "description": "Weaviate - Open source vector database"
56
+ },
57
+ "chromadb": {
58
+ "feed": "https://github.com/chroma-core/chroma/releases.atom",
59
+ "package": "chromadb",
60
+ "description": "Chroma - The AI-native open-source embedding database"
61
+ },
62
+ "llamaindex": {
63
+ "feed": "https://github.com/run-llama/llama_index/releases.atom",
64
+ "package": "llama-index",
65
+ "description": "LlamaIndex - Data framework for LLM applications"
66
+ },
67
+ "langsmith": {
68
+ "feed": "https://github.com/langchain-ai/langsmith-sdk/releases.atom",
69
+ "package": "langsmith",
70
+ "description": "LangSmith - Debugging, testing, and monitoring for LLMs"
71
+ },
72
+ "ollama": {
73
+ "feed": "https://github.com/ollama/ollama/releases.atom",
74
+ "package": "ollama",
75
+ "description": "Ollama - Run LLMs locally"
46
76
  }
47
77
  },
48
78
  "compatibility_rules": [