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
@@ -0,0 +1,78 @@
1
+ """ConnectOnion - A simple agent framework with behavior tracking."""
2
+
3
+ __version__ = "0.5.8"
4
+
5
+ # Auto-load .env files for the entire framework
6
+ from dotenv import load_dotenv
7
+ from pathlib import Path as _Path
8
+
9
+ # Load from current working directory (where user runs their script)
10
+ # NOT from the module's location (framework directory)
11
+ load_dotenv(_Path.cwd() / ".env")
12
+
13
+ from .agent import Agent
14
+ from .tool_factory import create_tool_from_function
15
+ from .llm import LLM
16
+ from .logger import Logger
17
+ from .llm_do import llm_do
18
+ from .prompts import load_system_prompt
19
+ from .xray import xray
20
+ from .decorators import replay, xray_replay
21
+ from .useful_tools import send_email, get_emails, mark_read, mark_unread, Memory, Gmail, GoogleCalendar, Outlook, MicrosoftCalendar, WebFetch, Shell, DiffWriter, pick, yes_no, autocomplete, TodoList, SlashCommand
22
+ from .auto_debug_exception import auto_debug_exception
23
+ from .connect import connect, RemoteAgent
24
+ from .host import host, create_app
25
+ from .events import (
26
+ after_user_input,
27
+ before_llm,
28
+ after_llm,
29
+ before_each_tool,
30
+ before_tools,
31
+ after_each_tool,
32
+ after_tools,
33
+ on_error,
34
+ on_complete
35
+ )
36
+
37
+ __all__ = [
38
+ "Agent",
39
+ "LLM",
40
+ "Logger",
41
+ "create_tool_from_function",
42
+ "llm_do",
43
+ "load_system_prompt",
44
+ "xray",
45
+ "replay",
46
+ "xray_replay",
47
+ "send_email",
48
+ "get_emails",
49
+ "mark_read",
50
+ "mark_unread",
51
+ "Memory",
52
+ "Gmail",
53
+ "GoogleCalendar",
54
+ "Outlook",
55
+ "MicrosoftCalendar",
56
+ "WebFetch",
57
+ "Shell",
58
+ "DiffWriter",
59
+ "pick",
60
+ "yes_no",
61
+ "autocomplete",
62
+ "TodoList",
63
+ "SlashCommand",
64
+ "auto_debug_exception",
65
+ "connect",
66
+ "RemoteAgent",
67
+ "host",
68
+ "create_app",
69
+ "after_user_input",
70
+ "before_llm",
71
+ "after_llm",
72
+ "before_each_tool",
73
+ "before_tools",
74
+ "after_each_tool",
75
+ "after_tools",
76
+ "on_error",
77
+ "on_complete"
78
+ ]
@@ -0,0 +1,320 @@
1
+ """
2
+ Purpose: Generate and manage Ed25519 cryptographic agent identities with seed phrase recovery
3
+ LLM-Note:
4
+ Dependencies: imports from [os, pathlib, typing, nacl.signing, mnemonic] | imported by [cli/commands/auth_commands.py] | tested by [tests/test_address.py]
5
+ Data flow: generate() → creates 12-word seed phrase via Mnemonic → derives SigningKey from seed → creates address (0x + hex public key) → returns {address, short_address, email, seed_phrase, signing_key} | recover(seed_phrase) → validates phrase → recreates SigningKey → recreates address
6
+ State/Effects: save() writes to .co/keys/ directory: agent.key (binary signing key), recovery.txt (seed phrase), DO_NOT_SHARE (warning) | sets file permissions to 0o600 | load() reads from .co/keys/ and .co/config.toml | no global state
7
+ Integration: exposes generate(), recover(seed_phrase), save(address_data, co_dir), load(co_dir), verify(address, message, signature), sign(address_data, message) | address format: 0x + 64 hex chars (32 bytes public key) | email format: first 10 chars + @mail.openonion.ai
8
+ Performance: Ed25519 signing is fast (sub-millisecond) | mnemonic generation and validation are fast | file I/O minimal (only on save/load)
9
+ Errors: raises ImportError if pynacl or mnemonic not installed | raises ValueError for invalid recovery phrase | returns None for missing keys (graceful) | verify() returns False for invalid signatures
10
+ """
11
+
12
+ import os
13
+ import sys
14
+ from pathlib import Path
15
+ from typing import Dict, Any, Optional
16
+
17
+ try:
18
+ from nacl.signing import SigningKey, VerifyKey
19
+ from mnemonic import Mnemonic
20
+ except ImportError:
21
+ # Graceful fallback if dependencies not installed
22
+ SigningKey = None
23
+ VerifyKey = None
24
+ Mnemonic = None
25
+
26
+
27
+ def generate() -> Dict[str, Any]:
28
+ """
29
+ Generate a new agent address with Ed25519 keys.
30
+
31
+ Returns:
32
+ Dictionary containing:
33
+ - address: Hex-encoded public key with 0x prefix (66 chars)
34
+ - short_address: Truncated display format (0x3d40...660c)
35
+ - email: Agent's email address (0x3d4017c3@mail.openonion.ai)
36
+ - seed_phrase: 12-word recovery phrase
37
+ - signing_key: Ed25519 signing key for signatures
38
+
39
+ Example:
40
+ >>> addr = generate()
41
+ >>> print(addr['address'])
42
+ 0x3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
43
+ >>> print(addr['email'])
44
+ 0x3d4017c3@mail.openonion.ai
45
+ """
46
+ if SigningKey is None or Mnemonic is None:
47
+ raise ImportError(
48
+ "Required libraries not installed. "
49
+ "Please run: pip install pynacl mnemonic"
50
+ )
51
+
52
+ # Generate 12-word recovery phrase
53
+ mnemo = Mnemonic("english")
54
+ seed_phrase = mnemo.generate(strength=128) # 128 bits = 12 words
55
+
56
+ # Derive seed from phrase
57
+ seed = mnemo.to_seed(seed_phrase)
58
+
59
+ # Create Ed25519 signing key from first 32 bytes
60
+ signing_key = SigningKey(seed[:32])
61
+
62
+ # Create address (hex-encoded public key with 0x prefix)
63
+ public_key_bytes = bytes(signing_key.verify_key)
64
+ address = "0x" + public_key_bytes.hex()
65
+
66
+ # Create short display format
67
+ short_address = f"{address[:6]}...{address[-4:]}"
68
+
69
+ # Create email address (first 10 chars of address)
70
+ email = f"{address[:10]}@mail.openonion.ai"
71
+
72
+ return {
73
+ "address": address,
74
+ "short_address": short_address,
75
+ "email": email,
76
+ "email_active": False, # Email inactive until authenticated
77
+ "seed_phrase": seed_phrase,
78
+ "signing_key": signing_key
79
+ }
80
+
81
+
82
+ def recover(seed_phrase: str) -> Dict[str, Any]:
83
+ """
84
+ Recover agent address from a recovery phrase.
85
+
86
+ Args:
87
+ seed_phrase: 12-word recovery phrase
88
+
89
+ Returns:
90
+ Dictionary containing address and signing_key
91
+
92
+ Raises:
93
+ ValueError: If recovery phrase is invalid
94
+
95
+ Example:
96
+ >>> addr = recover("canyon robot vacuum circle...")
97
+ >>> print(addr['address'])
98
+ 0x3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
99
+ """
100
+ if Mnemonic is None or SigningKey is None:
101
+ raise ImportError(
102
+ "Required libraries not installed. "
103
+ "Please run: pip install pynacl mnemonic"
104
+ )
105
+
106
+ mnemo = Mnemonic("english")
107
+
108
+ # Validate recovery phrase
109
+ if not mnemo.check(seed_phrase):
110
+ raise ValueError("Invalid recovery phrase")
111
+
112
+ # Derive seed from phrase
113
+ seed = mnemo.to_seed(seed_phrase)
114
+
115
+ # Recreate signing key
116
+ signing_key = SigningKey(seed[:32])
117
+
118
+ # Recreate address
119
+ public_key_bytes = bytes(signing_key.verify_key)
120
+ address = "0x" + public_key_bytes.hex()
121
+ short_address = f"{address[:6]}...{address[-4:]}"
122
+
123
+ # Create email address (first 10 chars of address)
124
+ email = f"{address[:10]}@mail.openonion.ai"
125
+
126
+ return {
127
+ "address": address,
128
+ "short_address": short_address,
129
+ "email": email,
130
+ "email_active": False, # Email inactive until authenticated
131
+ "signing_key": signing_key
132
+ }
133
+
134
+
135
+ def save(address_data: Dict[str, Any], co_dir: Path) -> None:
136
+ """
137
+ Save agent keys to .co/keys/ directory.
138
+
139
+ Args:
140
+ address_data: Dictionary from generate() or recover()
141
+ co_dir: Path to .co directory
142
+
143
+ Creates:
144
+ - .co/keys/agent.key (private signing key)
145
+ - .co/keys/recovery.txt (12-word phrase)
146
+ - .co/keys/DO_NOT_SHARE (warning file)
147
+ """
148
+ # Create keys directory
149
+ keys_dir = co_dir / "keys"
150
+ keys_dir.mkdir(parents=True, exist_ok=True)
151
+
152
+ # Save private key (binary format)
153
+ key_file = keys_dir / "agent.key"
154
+ key_file.write_bytes(bytes(address_data["signing_key"]))
155
+ if sys.platform != 'win32':
156
+ key_file.chmod(0o600) # Read/write for owner only (Unix/Mac only)
157
+
158
+ # Save recovery phrase if present
159
+ if "seed_phrase" in address_data:
160
+ recovery_file = keys_dir / "recovery.txt"
161
+ recovery_file.write_text(address_data["seed_phrase"], encoding='utf-8')
162
+ if sys.platform != 'win32':
163
+ recovery_file.chmod(0o600) # Read/write for owner only (Unix/Mac only)
164
+
165
+ # Create warning file
166
+ warning_file = keys_dir / "DO_NOT_SHARE"
167
+ if not warning_file.exists():
168
+ warning_content = """⚠️ WARNING: PRIVATE KEYS - DO NOT SHARE ⚠️
169
+
170
+ This directory contains private cryptographic keys.
171
+ NEVER share these files or commit them to version control.
172
+ Anyone with these keys can impersonate your agent.
173
+
174
+ Files:
175
+ - agent.key: Your agent's private signing key
176
+ - recovery.txt: 12-word recovery phrase
177
+
178
+ Keep these files secure and backed up.
179
+ """
180
+ warning_file.write_text(warning_content, encoding='utf-8')
181
+
182
+
183
+ def load(co_dir: Path) -> Optional[Dict[str, Any]]:
184
+ """
185
+ Load existing agent keys from .co/keys/ directory.
186
+
187
+ Args:
188
+ co_dir: Path to .co directory
189
+
190
+ Returns:
191
+ Dictionary with address and signing_key, or None if not found
192
+
193
+ Example:
194
+ >>> addr = load(Path(".co"))
195
+ >>> if addr:
196
+ ... print(addr['address'])
197
+ """
198
+ if SigningKey is None:
199
+ return None
200
+
201
+ keys_dir = co_dir / "keys"
202
+ key_file = keys_dir / "agent.key"
203
+
204
+ if not key_file.exists():
205
+ return None
206
+
207
+ try:
208
+ # Load signing key
209
+ key_bytes = key_file.read_bytes()
210
+ signing_key = SigningKey(key_bytes)
211
+
212
+ # Derive address from public key
213
+ public_key_bytes = bytes(signing_key.verify_key)
214
+ address = "0x" + public_key_bytes.hex()
215
+ short_address = f"{address[:6]}...{address[-4:]}"
216
+
217
+ # Try to load recovery phrase if it exists
218
+ recovery_file = keys_dir / "recovery.txt"
219
+ seed_phrase = None
220
+ if recovery_file.exists():
221
+ seed_phrase = recovery_file.read_text(encoding='utf-8').strip()
222
+
223
+ # Try to load email and activation status from config.toml
224
+ email = f"{address[:10]}@mail.openonion.ai" # Default
225
+ email_active = False # Default to inactive
226
+
227
+ config_path = co_dir / "config.toml"
228
+ if config_path.exists():
229
+ try:
230
+ import toml
231
+ config = toml.load(config_path)
232
+ if "agent" in config:
233
+ email = config["agent"].get("email", email)
234
+ email_active = config["agent"].get("email_active", False)
235
+ except Exception:
236
+ pass # Use defaults if config reading fails
237
+
238
+ result = {
239
+ "address": address,
240
+ "short_address": short_address,
241
+ "email": email,
242
+ "email_active": email_active,
243
+ "signing_key": signing_key
244
+ }
245
+
246
+ if seed_phrase:
247
+ result["seed_phrase"] = seed_phrase
248
+
249
+ return result
250
+
251
+ except Exception:
252
+ # Invalid key file or other error
253
+ return None
254
+
255
+
256
+ def verify(address: str, message: bytes, signature: bytes) -> bool:
257
+ """
258
+ Verify a signature using an agent's address.
259
+
260
+ Since the address IS the public key (hex-encoded), we can verify
261
+ signatures directly without needing additional information.
262
+
263
+ Args:
264
+ address: Agent's hex address (0x...)
265
+ message: Message that was signed
266
+ signature: 64-byte Ed25519 signature
267
+
268
+ Returns:
269
+ True if signature is valid, False otherwise
270
+
271
+ Example:
272
+ >>> msg = b"Hello, ConnectOnion!"
273
+ >>> sig = agent.sign(msg)
274
+ >>> verify(agent_address, msg, sig)
275
+ True
276
+ """
277
+ if VerifyKey is None:
278
+ return False
279
+
280
+ try:
281
+ # Validate address format
282
+ if not address.startswith("0x") or len(address) != 66:
283
+ return False
284
+
285
+ # Extract public key from address (it IS the public key!)
286
+ public_key_hex = address[2:]
287
+ public_key_bytes = bytes.fromhex(public_key_hex)
288
+
289
+ # Create verify key
290
+ verify_key = VerifyKey(public_key_bytes)
291
+
292
+ # Verify signature
293
+ verify_key.verify(message, signature)
294
+ return True
295
+
296
+ except Exception:
297
+ # Invalid signature, wrong key, or other error
298
+ return False
299
+
300
+
301
+ def sign(address_data: Dict[str, Any], message: bytes) -> bytes:
302
+ """
303
+ Sign a message with the agent's private key.
304
+
305
+ Args:
306
+ address_data: Dictionary from generate() or load()
307
+ message: Message to sign
308
+
309
+ Returns:
310
+ 64-byte Ed25519 signature
311
+
312
+ Example:
313
+ >>> addr = load(Path(".co"))
314
+ >>> sig = sign(addr, b"Hello!")
315
+ """
316
+ if "signing_key" not in address_data:
317
+ raise ValueError("No signing key in address data")
318
+
319
+ signed = address_data["signing_key"].sign(message)
320
+ return signed.signature