connectonion 0.5.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 (113) hide show
  1. connectonion/__init__.py +78 -0
  2. connectonion/address.py +320 -0
  3. connectonion/agent.py +450 -0
  4. connectonion/announce.py +84 -0
  5. connectonion/asgi.py +287 -0
  6. connectonion/auto_debug_exception.py +181 -0
  7. connectonion/cli/__init__.py +3 -0
  8. connectonion/cli/browser_agent/__init__.py +5 -0
  9. connectonion/cli/browser_agent/browser.py +243 -0
  10. connectonion/cli/browser_agent/prompt.md +107 -0
  11. connectonion/cli/commands/__init__.py +1 -0
  12. connectonion/cli/commands/auth_commands.py +527 -0
  13. connectonion/cli/commands/browser_commands.py +27 -0
  14. connectonion/cli/commands/create.py +511 -0
  15. connectonion/cli/commands/deploy_commands.py +220 -0
  16. connectonion/cli/commands/doctor_commands.py +173 -0
  17. connectonion/cli/commands/init.py +469 -0
  18. connectonion/cli/commands/project_cmd_lib.py +828 -0
  19. connectonion/cli/commands/reset_commands.py +149 -0
  20. connectonion/cli/commands/status_commands.py +168 -0
  21. connectonion/cli/docs/co-vibecoding-principles-docs-contexts-all-in-one.md +2010 -0
  22. connectonion/cli/docs/connectonion.md +1256 -0
  23. connectonion/cli/docs.md +123 -0
  24. connectonion/cli/main.py +148 -0
  25. connectonion/cli/templates/meta-agent/README.md +287 -0
  26. connectonion/cli/templates/meta-agent/agent.py +196 -0
  27. connectonion/cli/templates/meta-agent/prompts/answer_prompt.md +9 -0
  28. connectonion/cli/templates/meta-agent/prompts/docs_retrieve_prompt.md +15 -0
  29. connectonion/cli/templates/meta-agent/prompts/metagent.md +71 -0
  30. connectonion/cli/templates/meta-agent/prompts/think_prompt.md +18 -0
  31. connectonion/cli/templates/minimal/README.md +56 -0
  32. connectonion/cli/templates/minimal/agent.py +40 -0
  33. connectonion/cli/templates/playwright/README.md +118 -0
  34. connectonion/cli/templates/playwright/agent.py +336 -0
  35. connectonion/cli/templates/playwright/prompt.md +102 -0
  36. connectonion/cli/templates/playwright/requirements.txt +3 -0
  37. connectonion/cli/templates/web-research/agent.py +122 -0
  38. connectonion/connect.py +128 -0
  39. connectonion/console.py +539 -0
  40. connectonion/debug_agent/__init__.py +13 -0
  41. connectonion/debug_agent/agent.py +45 -0
  42. connectonion/debug_agent/prompts/debug_assistant.md +72 -0
  43. connectonion/debug_agent/runtime_inspector.py +406 -0
  44. connectonion/debug_explainer/__init__.py +10 -0
  45. connectonion/debug_explainer/explain_agent.py +114 -0
  46. connectonion/debug_explainer/explain_context.py +263 -0
  47. connectonion/debug_explainer/explainer_prompt.md +29 -0
  48. connectonion/debug_explainer/root_cause_analysis_prompt.md +43 -0
  49. connectonion/debugger_ui.py +1039 -0
  50. connectonion/decorators.py +208 -0
  51. connectonion/events.py +248 -0
  52. connectonion/execution_analyzer/__init__.py +9 -0
  53. connectonion/execution_analyzer/execution_analysis.py +93 -0
  54. connectonion/execution_analyzer/execution_analysis_prompt.md +47 -0
  55. connectonion/host.py +579 -0
  56. connectonion/interactive_debugger.py +342 -0
  57. connectonion/llm.py +801 -0
  58. connectonion/llm_do.py +307 -0
  59. connectonion/logger.py +300 -0
  60. connectonion/prompt_files/__init__.py +1 -0
  61. connectonion/prompt_files/analyze_contact.md +62 -0
  62. connectonion/prompt_files/eval_expected.md +12 -0
  63. connectonion/prompt_files/react_evaluate.md +11 -0
  64. connectonion/prompt_files/react_plan.md +16 -0
  65. connectonion/prompt_files/reflect.md +22 -0
  66. connectonion/prompts.py +144 -0
  67. connectonion/relay.py +200 -0
  68. connectonion/static/docs.html +688 -0
  69. connectonion/tool_executor.py +279 -0
  70. connectonion/tool_factory.py +186 -0
  71. connectonion/tool_registry.py +105 -0
  72. connectonion/trust.py +166 -0
  73. connectonion/trust_agents.py +71 -0
  74. connectonion/trust_functions.py +88 -0
  75. connectonion/tui/__init__.py +57 -0
  76. connectonion/tui/divider.py +39 -0
  77. connectonion/tui/dropdown.py +251 -0
  78. connectonion/tui/footer.py +31 -0
  79. connectonion/tui/fuzzy.py +56 -0
  80. connectonion/tui/input.py +278 -0
  81. connectonion/tui/keys.py +35 -0
  82. connectonion/tui/pick.py +130 -0
  83. connectonion/tui/providers.py +155 -0
  84. connectonion/tui/status_bar.py +163 -0
  85. connectonion/usage.py +161 -0
  86. connectonion/useful_events_handlers/__init__.py +16 -0
  87. connectonion/useful_events_handlers/reflect.py +116 -0
  88. connectonion/useful_plugins/__init__.py +20 -0
  89. connectonion/useful_plugins/calendar_plugin.py +163 -0
  90. connectonion/useful_plugins/eval.py +139 -0
  91. connectonion/useful_plugins/gmail_plugin.py +162 -0
  92. connectonion/useful_plugins/image_result_formatter.py +127 -0
  93. connectonion/useful_plugins/re_act.py +78 -0
  94. connectonion/useful_plugins/shell_approval.py +159 -0
  95. connectonion/useful_tools/__init__.py +44 -0
  96. connectonion/useful_tools/diff_writer.py +192 -0
  97. connectonion/useful_tools/get_emails.py +183 -0
  98. connectonion/useful_tools/gmail.py +1596 -0
  99. connectonion/useful_tools/google_calendar.py +613 -0
  100. connectonion/useful_tools/memory.py +380 -0
  101. connectonion/useful_tools/microsoft_calendar.py +604 -0
  102. connectonion/useful_tools/outlook.py +488 -0
  103. connectonion/useful_tools/send_email.py +205 -0
  104. connectonion/useful_tools/shell.py +97 -0
  105. connectonion/useful_tools/slash_command.py +201 -0
  106. connectonion/useful_tools/terminal.py +285 -0
  107. connectonion/useful_tools/todo_list.py +241 -0
  108. connectonion/useful_tools/web_fetch.py +216 -0
  109. connectonion/xray.py +467 -0
  110. connectonion-0.5.8.dist-info/METADATA +741 -0
  111. connectonion-0.5.8.dist-info/RECORD +113 -0
  112. connectonion-0.5.8.dist-info/WHEEL +4 -0
  113. connectonion-0.5.8.dist-info/entry_points.txt +3 -0
connectonion/asgi.py ADDED
@@ -0,0 +1,287 @@
1
+ """Raw ASGI utilities for HTTP/WebSocket handling.
2
+
3
+ This module contains the protocol-level code for handling HTTP and WebSocket
4
+ requests. Separated from host.py for better testing and smaller file size.
5
+
6
+ Design decision: Raw ASGI instead of Starlette/FastAPI for full protocol control.
7
+ See: docs/design-decisions/022-raw-asgi-implementation.md
8
+ """
9
+ import hmac
10
+ import json
11
+ import os
12
+ from pathlib import Path
13
+
14
+ from pydantic import BaseModel
15
+
16
+
17
+ def _json_default(obj):
18
+ """Handle non-serializable objects like Pydantic models.
19
+
20
+ This enables native JSON serialization for Pydantic BaseModel instances
21
+ nested in API response dicts, following FastAPI's pattern.
22
+ """
23
+ if isinstance(obj, BaseModel):
24
+ return obj.model_dump()
25
+ raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
26
+
27
+
28
+ async def read_body(receive) -> bytes:
29
+ """Read complete request body from ASGI receive."""
30
+ body = b""
31
+ while True:
32
+ m = await receive()
33
+ body += m.get("body", b"")
34
+ if not m.get("more_body"):
35
+ break
36
+ return body
37
+
38
+
39
+ # CORS headers for cross-origin requests (e.g., frontend at o.openonion.ai
40
+ # calling deployed agents at *.agents.openonion.ai)
41
+ CORS_HEADERS = [
42
+ [b"access-control-allow-origin", b"*"],
43
+ [b"access-control-allow-methods", b"GET, POST, OPTIONS"],
44
+ [b"access-control-allow-headers", b"authorization, content-type"],
45
+ ]
46
+
47
+
48
+ async def send_json(send, data: dict, status: int = 200):
49
+ """Send JSON response via ASGI send."""
50
+ body = json.dumps(data, default=_json_default).encode()
51
+ headers = [[b"content-type", b"application/json"]] + CORS_HEADERS
52
+ await send({"type": "http.response.start", "status": status, "headers": headers})
53
+ await send({"type": "http.response.body", "body": body})
54
+
55
+
56
+ async def send_html(send, html: bytes, status: int = 200):
57
+ """Send HTML response via ASGI send."""
58
+ await send({
59
+ "type": "http.response.start",
60
+ "status": status,
61
+ "headers": [[b"content-type", b"text/html; charset=utf-8"]],
62
+ })
63
+ await send({"type": "http.response.body", "body": html})
64
+
65
+
66
+ async def send_text(send, text: str, status: int = 200):
67
+ """Send plain text response via ASGI send."""
68
+ headers = [[b"content-type", b"text/plain; charset=utf-8"]] + CORS_HEADERS
69
+ await send({"type": "http.response.start", "status": status, "headers": headers})
70
+ await send({"type": "http.response.body", "body": text.encode()})
71
+
72
+
73
+ async def handle_http(
74
+ scope,
75
+ receive,
76
+ send,
77
+ *,
78
+ handlers: dict,
79
+ storage,
80
+ trust: str,
81
+ result_ttl: int,
82
+ start_time: float,
83
+ blacklist: list | None = None,
84
+ whitelist: list | None = None,
85
+ ):
86
+ """Route HTTP requests to handlers.
87
+
88
+ Args:
89
+ scope: ASGI scope dict (method, path, headers, etc.)
90
+ receive: ASGI receive callable
91
+ send: ASGI send callable
92
+ handlers: Dict of handler functions (input, session, sessions, health, info, auth)
93
+ storage: SessionStorage instance
94
+ trust: Trust level (open/careful/strict)
95
+ result_ttl: How long to keep results in seconds
96
+ start_time: Server start time
97
+ blacklist: Blocked identities
98
+ whitelist: Allowed identities
99
+ """
100
+ method, path = scope["method"], scope["path"]
101
+
102
+ # Handle CORS preflight requests
103
+ if method == "OPTIONS":
104
+ headers = CORS_HEADERS + [[b"content-length", b"0"]]
105
+ await send({"type": "http.response.start", "status": 204, "headers": headers})
106
+ await send({"type": "http.response.body", "body": b""})
107
+ return
108
+
109
+ # Admin endpoints require API key auth
110
+ if path.startswith("/admin"):
111
+ headers = dict(scope.get("headers", []))
112
+ auth = headers.get(b"authorization", b"").decode()
113
+ expected = os.environ.get("OPENONION_API_KEY", "")
114
+ if not expected or not auth.startswith("Bearer ") or not hmac.compare_digest(auth[7:], expected):
115
+ await send_json(send, {"error": "unauthorized"}, 401)
116
+ return
117
+
118
+ if method == "GET" and path == "/admin/logs":
119
+ result = handlers["admin_logs"]()
120
+ if "error" in result:
121
+ await send_json(send, result, 404)
122
+ else:
123
+ await send_text(send, result["content"])
124
+ return
125
+
126
+ if method == "GET" and path == "/admin/sessions":
127
+ await send_json(send, handlers["admin_sessions"]())
128
+ return
129
+
130
+ await send_json(send, {"error": "not found"}, 404)
131
+ return
132
+
133
+ if method == "POST" and path == "/input":
134
+ body = await read_body(receive)
135
+ try:
136
+ data = json.loads(body) if body else {}
137
+ except json.JSONDecodeError:
138
+ await send_json(send, {"error": "Invalid JSON"}, 400)
139
+ return
140
+
141
+ prompt, identity, sig_valid, err = handlers["auth"](
142
+ data, trust, blacklist=blacklist, whitelist=whitelist
143
+ )
144
+ if err:
145
+ status = 401 if err.startswith("unauthorized") else 403 if err.startswith("forbidden") else 400
146
+ await send_json(send, {"error": err}, status)
147
+ return
148
+
149
+ # Extract session for conversation continuation
150
+ session = data.get("session")
151
+ result = handlers["input"](storage, prompt, result_ttl, session)
152
+ await send_json(send, result)
153
+
154
+ elif method == "GET" and path.startswith("/sessions/"):
155
+ result = handlers["session"](storage, path[10:])
156
+ await send_json(send, result or {"error": "not found"}, 404 if not result else 200)
157
+
158
+ elif method == "GET" and path == "/sessions":
159
+ await send_json(send, handlers["sessions"](storage))
160
+
161
+ elif method == "GET" and path == "/health":
162
+ await send_json(send, handlers["health"](start_time))
163
+
164
+ elif method == "GET" and path == "/info":
165
+ await send_json(send, handlers["info"](trust))
166
+
167
+ elif method == "GET" and path == "/docs":
168
+ # Serve static docs page
169
+ try:
170
+ base = Path(__file__).resolve().parent
171
+ html_path = base / "static" / "docs.html"
172
+ html = html_path.read_bytes()
173
+ except Exception:
174
+ html = b"<html><body><h1>ConnectOnion Docs</h1><p>Docs not found.</p></body></html>"
175
+ await send_html(send, html)
176
+
177
+ else:
178
+ await send_json(send, {"error": "not found"}, 404)
179
+
180
+
181
+ async def handle_websocket(
182
+ scope,
183
+ receive,
184
+ send,
185
+ *,
186
+ handlers: dict,
187
+ trust: str,
188
+ blacklist: list | None = None,
189
+ whitelist: list | None = None,
190
+ ):
191
+ """Handle WebSocket connections at /ws.
192
+
193
+ Args:
194
+ scope: ASGI scope dict
195
+ receive: ASGI receive callable
196
+ send: ASGI send callable
197
+ handlers: Dict with 'ws_input' and 'auth' handlers
198
+ trust: Trust level
199
+ blacklist: Blocked identities
200
+ whitelist: Allowed identities
201
+ """
202
+ if scope["path"] != "/ws":
203
+ await send({"type": "websocket.close", "code": 4004})
204
+ return
205
+
206
+ await send({"type": "websocket.accept"})
207
+
208
+ while True:
209
+ msg = await receive()
210
+ if msg["type"] == "websocket.disconnect":
211
+ break
212
+ if msg["type"] == "websocket.receive":
213
+ try:
214
+ data = json.loads(msg.get("text", "{}"))
215
+ except json.JSONDecodeError:
216
+ await send({"type": "websocket.send",
217
+ "text": json.dumps({"type": "ERROR", "message": "Invalid JSON"})})
218
+ continue
219
+
220
+ if data.get("type") == "INPUT":
221
+ prompt, identity, sig_valid, err = handlers["auth"](
222
+ data, trust, blacklist=blacklist, whitelist=whitelist
223
+ )
224
+ if err:
225
+ await send({"type": "websocket.send",
226
+ "text": json.dumps({"type": "ERROR", "message": err})})
227
+ continue
228
+ if not prompt:
229
+ await send({"type": "websocket.send",
230
+ "text": json.dumps({"type": "ERROR", "message": "prompt required"})})
231
+ continue
232
+ result = handlers["ws_input"](prompt)
233
+ await send({"type": "websocket.send",
234
+ "text": json.dumps({"type": "OUTPUT", "result": result})})
235
+
236
+
237
+ def create_app(
238
+ *,
239
+ handlers: dict,
240
+ storage,
241
+ trust: str = "careful",
242
+ result_ttl: int = 86400,
243
+ blacklist: list | None = None,
244
+ whitelist: list | None = None,
245
+ ):
246
+ """Create ASGI application.
247
+
248
+ Args:
249
+ handlers: Dict of handler functions
250
+ storage: SessionStorage instance
251
+ trust: Trust level (open/careful/strict)
252
+ result_ttl: How long to keep results in seconds
253
+ blacklist: Blocked identities
254
+ whitelist: Allowed identities
255
+
256
+ Returns:
257
+ ASGI application callable
258
+ """
259
+ import time
260
+ start_time = time.time()
261
+
262
+ async def app(scope, receive, send):
263
+ if scope["type"] == "http":
264
+ await handle_http(
265
+ scope,
266
+ receive,
267
+ send,
268
+ handlers=handlers,
269
+ storage=storage,
270
+ trust=trust,
271
+ result_ttl=result_ttl,
272
+ start_time=start_time,
273
+ blacklist=blacklist,
274
+ whitelist=whitelist,
275
+ )
276
+ elif scope["type"] == "websocket":
277
+ await handle_websocket(
278
+ scope,
279
+ receive,
280
+ send,
281
+ handlers=handlers,
282
+ trust=trust,
283
+ blacklist=blacklist,
284
+ whitelist=whitelist,
285
+ )
286
+
287
+ return app
@@ -0,0 +1,181 @@
1
+ """
2
+ Purpose: Automatically analyze uncaught Python exceptions using AI with runtime frame inspection
3
+ LLM-Note:
4
+ Dependencies: imports from [sys, traceback, os, dotenv, console.py, debug_agent/__init__.py] | imported by [__init__.py] | tested by [tests/test_auto_debug_exception.py]
5
+ Data flow: auto_debug_exception() → installs sys.excepthook → on exception: calls original_hook (shows traceback) → finds relevant frame (last user code, not library) → extracts frame.f_locals → creates debug_agent with actual frame and traceback → sends prompt with exception details → agent uses runtime inspection tools (explore_namespace, execute_in_frame, inspect_object, validate_assumption, test_fix) → displays AI analysis
6
+ State/Effects: modifies sys.excepthook globally | loads .env for CONNECTONION_AUTO_DEBUG | creates debug Agent instances on exceptions | calls console.print() to display analysis | does not prevent exception from terminating program
7
+ Integration: exposes auto_debug_exception(model) | checks CONNECTONION_AUTO_DEBUG=false env to disable | creates debug_agent with frame, exception_traceback, model parameters | prompt guides AI to use runtime inspection tools
8
+ Performance: only activates on exceptions (zero overhead in normal execution) | debug agent creates LLM instance per exception | runtime inspection executes code in crashed frame
9
+ Errors: wraps AI analysis in try/except to avoid cascading failures | shows "AI analysis failed" message if agent crashes | handles missing frames gracefully | skips analysis if no relevant frame found
10
+ """
11
+
12
+ import sys
13
+ import traceback
14
+ import os
15
+
16
+
17
+ def auto_debug_exception(model: str = "o4-mini"):
18
+ """Enable AI debugging for uncaught exceptions ONLY.
19
+
20
+ Debugs crashes, raised exceptions, and failed assertions. Does NOT debug
21
+ logic errors, wrong outputs, or performance issues unless you convert them
22
+ to exceptions using raise/assert.
23
+
24
+ What gets debugged:
25
+ ✅ Crashes: KeyError, TypeError, ZeroDivisionError, etc.
26
+ ✅ Raised exceptions: raise ValueError("invalid input")
27
+ ✅ Failed assertions: assert x > 0, "must be positive"
28
+ ❌ Logic errors that don't raise exceptions
29
+
30
+ Args:
31
+ model: AI model to use (default: o4-mini for speed)
32
+
33
+ Example:
34
+ from connectonion import auto_debug_exception
35
+
36
+ # Enable AI debugging for exceptions
37
+ auto_debug_exception()
38
+
39
+ # Any uncaught exception gets AI analysis with runtime data
40
+ data = {"items": []}
41
+ avg = sum(data["items"]) / len(data["items"]) # ZeroDivisionError!
42
+
43
+ Environment:
44
+ Set CONNECTONION_AUTO_DEBUG=false to disable even when called.
45
+ """
46
+ # Check if explicitly disabled via environment
47
+ if os.environ.get('CONNECTONION_AUTO_DEBUG', '').lower() == 'false':
48
+ return # User explicitly disabled it
49
+
50
+ # Save original hook for use in our handler
51
+ original_hook = sys.excepthook
52
+
53
+ def handle_exception(exc_type, exc_value, exception_traceback):
54
+ """Handle an uncaught exception with AI runtime analysis."""
55
+ # First call original hook to show normal traceback
56
+ # (This ensures compatibility with other tools)
57
+ original_hook(exc_type, exc_value, exception_traceback)
58
+
59
+ # Then add our AI analysis
60
+ from .console import Console
61
+ console = Console()
62
+
63
+ # Find the most relevant frame (last user code, not library)
64
+ relevant_frame_info = None
65
+ actual_frame = None
66
+ actual_traceback = None
67
+ current_traceback = exception_traceback
68
+
69
+ while current_traceback:
70
+ frame = current_traceback.tb_frame
71
+ filename = frame.f_code.co_filename
72
+
73
+ # Skip system/library files
74
+ if not filename.startswith('<') and 'site-packages' not in filename:
75
+ relevant_frame_info = {
76
+ 'file': filename,
77
+ 'line': current_traceback.tb_lineno,
78
+ 'function': frame.f_code.co_name,
79
+ 'locals': {}
80
+ }
81
+ # Keep the actual frame and traceback for runtime inspection
82
+ actual_frame = frame
83
+ actual_traceback = exception_traceback # Keep the original traceback
84
+
85
+ # Capture some local variables (simple types only) for display
86
+ for name, value in frame.f_locals.items():
87
+ # Skip private/internal variables
88
+ if name.startswith('_'):
89
+ continue
90
+
91
+ # Only capture simple types to avoid huge dumps
92
+ if isinstance(value, (str, int, float, bool, type(None))):
93
+ relevant_frame_info['locals'][name] = value
94
+ elif isinstance(value, (list, dict, tuple)):
95
+ # Just show type and size
96
+ relevant_frame_info['locals'][name] = f"{type(value).__name__}({len(value)})"
97
+ else:
98
+ # Just show type
99
+ relevant_frame_info['locals'][name] = type(value).__name__
100
+
101
+ current_traceback = current_traceback.tb_next
102
+
103
+ # If no user code found, use the last frame
104
+ if not relevant_frame_info and exception_traceback:
105
+ last_traceback = exception_traceback
106
+ while last_traceback.tb_next:
107
+ last_traceback = last_traceback.tb_next
108
+
109
+ frame = last_traceback.tb_frame
110
+ relevant_frame_info = {
111
+ 'file': frame.f_code.co_filename,
112
+ 'line': last_traceback.tb_lineno,
113
+ 'function': frame.f_code.co_name,
114
+ 'locals': {}
115
+ }
116
+ actual_frame = frame
117
+ actual_traceback = exception_traceback
118
+
119
+ # Skip if no relevant frame
120
+ if not relevant_frame_info:
121
+ return
122
+
123
+ # Run AI analysis with runtime inspection
124
+ console.print("\n[yellow]🤖 Analyzing with AI runtime inspection...[/yellow]")
125
+
126
+ try:
127
+ # Use debug agent with runtime inspection tools
128
+ from .debug_agent import create_debug_agent
129
+
130
+ # Pass the actual frame and traceback for runtime inspection!
131
+ agent = create_debug_agent(
132
+ frame=actual_frame,
133
+ exception_traceback=actual_traceback,
134
+ model=model
135
+ )
136
+
137
+ # Build prompt that guides tool usage
138
+ prompt = f"""Debug this Python exception using your runtime inspection tools:
139
+
140
+ Exception: {exc_type.__name__}: {exc_value}
141
+ File: {relevant_frame_info['file']}
142
+ Line: {relevant_frame_info['line']}
143
+ Function: {relevant_frame_info['function']}()
144
+
145
+ You have LIVE ACCESS to the crashed program's state! Use your tools to investigate:
146
+
147
+ 1. First explore what variables are available:
148
+ - Use explore_namespace() to see all variables
149
+
150
+ 2. Then investigate the specific error:
151
+ - Use execute_in_frame() to check values and types
152
+ - Use inspect_object() to examine data structures
153
+ - Use validate_assumption() to test your hypotheses
154
+
155
+ 3. Test potential fixes:
156
+ - Use test_fix() to verify solutions work with the actual data
157
+ - Use try_alternative() to explore different approaches
158
+
159
+ 4. Finally, provide your analysis:
160
+ - **What I found**: Show the actual runtime values you discovered
161
+ - **Why it failed**: Explain with evidence from the runtime state
162
+ - **Verified fix**: A solution you tested that works"""
163
+
164
+ # Get AI analysis
165
+ result = agent.input(prompt)
166
+
167
+ # Display the analysis
168
+ console.print("\n[cyan bold]💡 AI Runtime Debug Analysis:[/cyan bold]")
169
+ console.print(result)
170
+
171
+ except Exception as e:
172
+ # If AI analysis fails, show a simple message
173
+ console.print(f"[dim]AI analysis failed: {e}[/dim]")
174
+
175
+ # Install our exception hook
176
+ sys.excepthook = handle_exception
177
+
178
+ # Simple confirmation
179
+ from .console import Console
180
+ console = Console()
181
+ console.print(f"[green]✅ Exception debugging enabled[/green] - AI will analyze uncaught exceptions with runtime inspection")
@@ -0,0 +1,3 @@
1
+ """ConnectOnion CLI module."""
2
+
3
+ __version__ = "0.0.1b5"
@@ -0,0 +1,5 @@
1
+ """Browser agent module for ConnectOnion CLI."""
2
+
3
+ from .browser import execute_browser_command, BrowserAutomation
4
+
5
+ __all__ = ['execute_browser_command', 'BrowserAutomation']