teotl 0.2.0__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 (125) hide show
  1. teotl/__init__.py +19 -0
  2. teotl/cli/__init__.py +103 -0
  3. teotl/cli/__main__.py +11 -0
  4. teotl/cli/chat.py +319 -0
  5. teotl/cli/memory.py +270 -0
  6. teotl/cli/orchestrator.py +209 -0
  7. teotl/cli/security.py +532 -0
  8. teotl/cli/wizard.py +3562 -0
  9. teotl/cloud/__init__.py +20 -0
  10. teotl/config/__init__.py +89 -0
  11. teotl/config/factory.py +544 -0
  12. teotl/config/multi_agent.py +385 -0
  13. teotl/core/__init__.py +1 -0
  14. teotl/core/agent.py +979 -0
  15. teotl/core/events.py +115 -0
  16. teotl/core/provider.py +483 -0
  17. teotl/core/rate_limited_provider.py +175 -0
  18. teotl/core/security/__init__.py +64 -0
  19. teotl/core/security/audit.py +505 -0
  20. teotl/core/security/cost_tracker.py +254 -0
  21. teotl/core/security/enforcement.py +384 -0
  22. teotl/core/security/policy.py +633 -0
  23. teotl/core/security/sandbox.py +554 -0
  24. teotl/core/session.py +192 -0
  25. teotl/core/tools/__init__.py +9 -0
  26. teotl/core/tools/bash.py +316 -0
  27. teotl/core/tools/browser.py +572 -0
  28. teotl/core/types.py +250 -0
  29. teotl/daemon/__init__.py +19 -0
  30. teotl/daemon/__main__.py +6 -0
  31. teotl/daemon/agent_daemon.py +406 -0
  32. teotl/daemon/executor.py +570 -0
  33. teotl/daemon/run.py +372 -0
  34. teotl/extensions/__init__.py +1 -0
  35. teotl/extensions/builtin/__init__.py +6 -0
  36. teotl/extensions/builtin/audit.py +83 -0
  37. teotl/extensions/builtin/undo.py +101 -0
  38. teotl/extensions/manager.py +54 -0
  39. teotl/primitives/__init__.py +6 -0
  40. teotl/primitives/a2a/__init__.py +22 -0
  41. teotl/primitives/a2a/client.py +97 -0
  42. teotl/primitives/a2a/protocol.py +67 -0
  43. teotl/primitives/a2a/server.py +210 -0
  44. teotl/primitives/discovery/__init__.py +9 -0
  45. teotl/primitives/discovery/local.py +227 -0
  46. teotl/primitives/guardrails/__init__.py +6 -0
  47. teotl/primitives/guardrails/bash_analyzer.py +224 -0
  48. teotl/primitives/guardrails/classifier.py +63 -0
  49. teotl/primitives/guardrails/engine.py +136 -0
  50. teotl/primitives/guardrails/policy.py +223 -0
  51. teotl/primitives/guardrails/presets.py +149 -0
  52. teotl/primitives/guardrails/prompt_injection.py +182 -0
  53. teotl/primitives/guardrails/rate_limiter.py +337 -0
  54. teotl/primitives/guardrails/trust.py +91 -0
  55. teotl/primitives/harness/README.md +912 -0
  56. teotl/primitives/harness/__init__.py +139 -0
  57. teotl/primitives/harness/advanced_janitor.py +372 -0
  58. teotl/primitives/harness/agent.py +242 -0
  59. teotl/primitives/harness/audit.py +542 -0
  60. teotl/primitives/harness/checkpoint.py +421 -0
  61. teotl/primitives/harness/cost_tracker.py +317 -0
  62. teotl/primitives/harness/dryrun.py +574 -0
  63. teotl/primitives/harness/evaluator.py +597 -0
  64. teotl/primitives/harness/exceptions.py +203 -0
  65. teotl/primitives/harness/explorer.py +529 -0
  66. teotl/primitives/harness/heartbeat.py +627 -0
  67. teotl/primitives/harness/janitor.py +332 -0
  68. teotl/primitives/harness/orchestrator.py +890 -0
  69. teotl/primitives/harness/plan.py +371 -0
  70. teotl/primitives/harness/planner.py +376 -0
  71. teotl/primitives/harness/progress.py +257 -0
  72. teotl/primitives/harness/state.py +374 -0
  73. teotl/primitives/harness/verification.py +553 -0
  74. teotl/primitives/harness/worker.py +622 -0
  75. teotl/primitives/integrations/__init__.py +1 -0
  76. teotl/primitives/integrations/credential_store.py +505 -0
  77. teotl/primitives/integrations/mcp_bridge.py +187 -0
  78. teotl/primitives/integrations/registry.py +154 -0
  79. teotl/primitives/memory/__init__.py +6 -0
  80. teotl/primitives/memory/context.py +142 -0
  81. teotl/primitives/memory/encrypted.py +364 -0
  82. teotl/primitives/memory/local.py +448 -0
  83. teotl/primitives/memory/store.py +66 -0
  84. teotl/primitives/missions/__init__.py +19 -0
  85. teotl/primitives/missions/persistent.py +548 -0
  86. teotl/primitives/permissions/__init__.py +5 -0
  87. teotl/primitives/resolution/__init__.py +25 -0
  88. teotl/primitives/resolution/interface.py +132 -0
  89. teotl/primitives/resolution/mock.py +155 -0
  90. teotl/primitives/skills/__init__.py +6 -0
  91. teotl/primitives/skills/loader.py +109 -0
  92. teotl/primitives/skills/registry.py +154 -0
  93. teotl/primitives/skills/router.py +38 -0
  94. teotl/primitives/tasks/__init__.py +11 -0
  95. teotl/primitives/tasks/persistent.py +452 -0
  96. teotl/skills/claude_code/SKILL.md +1049 -0
  97. teotl/skills/filesystem/SKILL.md +397 -0
  98. teotl/skills/git/SKILL.md +690 -0
  99. teotl/skills/github/SKILL.md +339 -0
  100. teotl/skills/platforms/linkedin/SKILL.md +250 -0
  101. teotl/skills/platforms/medium/SKILL.md +378 -0
  102. teotl/skills/platforms/substack/SKILL.md +466 -0
  103. teotl/skills/platforms/twitter/SKILL.md +345 -0
  104. teotl/skills/social-media/SKILL.md +520 -0
  105. teotl/skills/spec_kit/SKILL.md +886 -0
  106. teotl/skills/web/SKILL.md +488 -0
  107. teotl/templates/GOALS.md +33 -0
  108. teotl/templates/INSTRUCTIONS.md +145 -0
  109. teotl/templates/INSTRUCTIONS_AUTONOMOUS.md +199 -0
  110. teotl/templates/MISSIONS.md +202 -0
  111. teotl/templates/PERSONALITY.md +79 -0
  112. teotl/templates/SKILLS.md +230 -0
  113. teotl/templates/USER.md +109 -0
  114. teotl/ui/__init__.py +15 -0
  115. teotl/ui/cli.py +63 -0
  116. teotl/ui/security_panel.py +418 -0
  117. teotl/web/__init__.py +5 -0
  118. teotl/web/__main__.py +186 -0
  119. teotl/web/server.py +391 -0
  120. teotl/web/static/index.html +957 -0
  121. teotl-0.2.0.dist-info/METADATA +281 -0
  122. teotl-0.2.0.dist-info/RECORD +125 -0
  123. teotl-0.2.0.dist-info/WHEEL +4 -0
  124. teotl-0.2.0.dist-info/entry_points.txt +2 -0
  125. teotl-0.2.0.dist-info/licenses/LICENSE +21 -0
teotl/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ """
2
+ Teotl β€” A Python framework for building safe, memory-aware, context-efficient AI agents.
3
+ """
4
+
5
+ __version__ = "0.2.0"
6
+
7
+ from teotl.core.agent import Agent
8
+ from teotl.core.events import EventBus
9
+ from teotl.core.session import Session
10
+ from teotl.core.types import Extension, Response, ToolCall
11
+
12
+ __all__ = [
13
+ "Agent",
14
+ "EventBus",
15
+ "Extension",
16
+ "Response",
17
+ "Session",
18
+ "ToolCall",
19
+ ]
teotl/cli/__init__.py ADDED
@@ -0,0 +1,103 @@
1
+ """CLI commands for Teotl agent framework."""
2
+
3
+ import sys
4
+
5
+ import click
6
+
7
+ from teotl import __version__
8
+ from teotl.cli.memory import memory
9
+ from teotl.cli.security import main as security_main
10
+ from teotl.cli.wizard import OnboardingWizard
11
+ from teotl.cli.wizard import main as wizard_main
12
+
13
+ __all__ = ["memory", "OnboardingWizard", "wizard_main", "security_main", "main"]
14
+
15
+
16
+ @click.group()
17
+ @click.version_option(version=__version__, prog_name="teotl")
18
+ def main():
19
+ """Teotl - AI Agent Framework
20
+
21
+ Build safe, memory-aware, context-efficient AI agents.
22
+ """
23
+ pass
24
+
25
+
26
+ @main.command()
27
+ @click.option("--agent", "-a", help="Agent ID to load workspace files from")
28
+ @click.option(
29
+ "--skills", "-s", multiple=True, help="Skills to enable (comma-separated or multiple times)"
30
+ )
31
+ @click.option("--no-memory", is_flag=True, help="Disable memory system")
32
+ def chat(agent: str | None, skills: tuple[str, ...], no_memory: bool):
33
+ """Start interactive chat mode.
34
+
35
+ Examples:
36
+
37
+ \b
38
+ # Basic chat
39
+ forge chat
40
+
41
+ \b
42
+ # Chat with skills (comma-separated)
43
+ forge chat --skills filesystem,git,web
44
+
45
+ \b
46
+ # Chat with skills (repeated flags)
47
+ forge chat --skills filesystem --skills git
48
+
49
+ \b
50
+ # Chat with specific agent (loads workspace files)
51
+ forge chat --agent my-agent
52
+
53
+ \b
54
+ # Chat without memory
55
+ forge chat --no-memory
56
+ """
57
+ import asyncio
58
+
59
+ from teotl.cli.chat import chat_command
60
+
61
+ # Handle comma-separated skills
62
+ skill_list = []
63
+ if skills:
64
+ for skill_arg in skills:
65
+ # Split by comma in case user specified: --skills filesystem,git,web
66
+ skill_list.extend([s.strip() for s in skill_arg.split(",") if s.strip()])
67
+
68
+ try:
69
+ asyncio.run(
70
+ chat_command(
71
+ agent_id=agent,
72
+ skills=skill_list if skill_list else None,
73
+ no_memory=no_memory,
74
+ )
75
+ )
76
+ except KeyboardInterrupt:
77
+ click.echo("\nπŸ‘‹ Chat ended")
78
+ sys.exit(0)
79
+
80
+
81
+ @main.command()
82
+ def onboard():
83
+ """Run interactive onboarding wizard.
84
+
85
+ Sets up your first agent with:
86
+ - API key configuration
87
+ - Agent personality and preferences
88
+ - Skills selection
89
+ - Security policies
90
+ """
91
+ wizard_main()
92
+
93
+
94
+ @main.command()
95
+ def wizard():
96
+ """Alias for 'onboard' command."""
97
+ wizard_main()
98
+
99
+
100
+ @main.command()
101
+ def security():
102
+ """Manage security policies and configurations."""
103
+ security_main()
teotl/cli/__main__.py ADDED
@@ -0,0 +1,11 @@
1
+ """Main entry point for forge CLI.
2
+
3
+ Usage:
4
+ python -m forge.cli [command]
5
+ forge [command]
6
+ """
7
+
8
+ from teotl.cli import main
9
+
10
+ if __name__ == "__main__":
11
+ main()
teotl/cli/chat.py ADDED
@@ -0,0 +1,319 @@
1
+ """Interactive chat mode for Teotl agents.
2
+
3
+ Provides a CLI interface to chat with agents in real-time.
4
+ """
5
+
6
+ import logging
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from rich.console import Console
11
+ from rich.markdown import Markdown
12
+ from rich.panel import Panel
13
+ from rich.prompt import Prompt
14
+
15
+ from teotl.core.agent import Agent
16
+ from teotl.core.provider import AnthropicProvider
17
+ from teotl.core.types import UI
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class ChatUI(UI):
23
+ """UI adapter for interactive chat mode."""
24
+
25
+ def __init__(self, console: Console):
26
+ self.console = console
27
+
28
+ async def confirm(self, message: str, *, title: str = "") -> bool:
29
+ """Request confirmation from user."""
30
+ self.console.print(f"\n[yellow]πŸ›‘οΈ {message}[/yellow]")
31
+ response = Prompt.ask("Confirm? (yes/no)", choices=["yes", "no"], default="no")
32
+ return response.lower() == "yes"
33
+
34
+ async def display(self, message: str) -> None:
35
+ """Display a message to user."""
36
+ self.console.print(message)
37
+
38
+ async def input(self, prompt: str = "") -> str:
39
+ """Get input from user."""
40
+ return Prompt.ask(prompt or "Input")
41
+
42
+ async def error(self, message: str) -> None:
43
+ """Display an error message."""
44
+ self.console.print(f"[red]❌ Error: {message}[/red]")
45
+
46
+
47
+ class ChatSession:
48
+ """Interactive chat session with an agent."""
49
+
50
+ def __init__(
51
+ self,
52
+ agent_id: str | None = None,
53
+ skills: list[str] | None = None,
54
+ use_memory: bool = True,
55
+ session_dir: Path | None = None,
56
+ ):
57
+ """Initialize chat session.
58
+
59
+ Args:
60
+ agent_id: Agent identifier (for loading workspace files)
61
+ skills: List of skills to enable
62
+ use_memory: Whether to use memory system
63
+ session_dir: Override session directory
64
+ """
65
+ self.console = Console()
66
+ self.agent_id = agent_id or "chat"
67
+ self.skills = skills or []
68
+ self.use_memory = use_memory
69
+
70
+ # Determine workspace and session directories
71
+ forge_dir = Path.home() / ".forge"
72
+ if agent_id:
73
+ self.workspace_dir = forge_dir / "agents" / agent_id
74
+ self.session_dir = session_dir or self.workspace_dir / "sessions"
75
+ else:
76
+ self.workspace_dir = None
77
+ self.session_dir = session_dir or forge_dir / "chat-sessions"
78
+
79
+ self.session_dir.mkdir(parents=True, exist_ok=True)
80
+
81
+ # UI adapter
82
+ self.ui = ChatUI(self.console)
83
+
84
+ # Agent (initialized in start())
85
+ self.agent: Agent | None = None
86
+
87
+ def _load_instructions(self) -> str:
88
+ """Load instructions from workspace files if available."""
89
+ if not self.workspace_dir or not self.workspace_dir.exists():
90
+ return "You are a helpful AI assistant."
91
+
92
+ from teotl.daemon.executor import load_workspace_files
93
+
94
+ workspace_instructions = load_workspace_files(self.workspace_dir)
95
+ if workspace_instructions:
96
+ return workspace_instructions
97
+
98
+ return "You are a helpful AI assistant."
99
+
100
+ def _display_welcome(self):
101
+ """Display welcome message."""
102
+ welcome_text = """
103
+ # πŸ’¬ Teotl Interactive Chat
104
+
105
+ Welcome! You're now chatting with a Teotl agent.
106
+
107
+ **Commands:**
108
+ - Type your message and press Enter
109
+ - `/exit` or `/quit` - Exit chat
110
+ - `/help` - Show this help
111
+ - `/skills` - List active skills
112
+ - `/memory` - Show recent memories
113
+ - `/clear` - Clear screen
114
+ """
115
+
116
+ if self.agent_id != "chat":
117
+ welcome_text += f"\n**Agent:** {self.agent_id}"
118
+
119
+ if self.skills:
120
+ welcome_text += f"\n**Skills:** {', '.join(self.skills)}"
121
+
122
+ welcome_text += "\n\nType your message below:\n"
123
+
124
+ self.console.print(Markdown(welcome_text))
125
+
126
+ def _display_help(self):
127
+ """Display help information."""
128
+ help_text = """
129
+ # Chat Commands
130
+
131
+ - `/exit`, `/quit` - Exit chat session
132
+ - `/help` - Show this help message
133
+ - `/skills` - List active skills and their descriptions
134
+ - `/memory` - Show recent memories (if memory enabled)
135
+ - `/clear` - Clear the screen
136
+
137
+ **Tips:**
138
+ - Skills activate automatically based on your request
139
+ - Confirmations may be required for destructive operations
140
+ - Use Ctrl+C as alternative to /exit
141
+ """
142
+ self.console.print(Markdown(help_text))
143
+
144
+ async def _handle_command(self, command: str) -> bool:
145
+ """Handle special commands.
146
+
147
+ Args:
148
+ command: Command string (e.g., "/help")
149
+
150
+ Returns:
151
+ True if should continue, False to exit
152
+ """
153
+ command = command.lower().strip()
154
+
155
+ if command in ["/exit", "/quit"]:
156
+ return False
157
+
158
+ elif command == "/help":
159
+ self._display_help()
160
+
161
+ elif command == "/skills":
162
+ if not self.agent:
163
+ self.console.print("[yellow]Agent not initialized[/yellow]")
164
+ elif not self.agent.skills:
165
+ self.console.print("[yellow]No skills enabled[/yellow]")
166
+ else:
167
+ descriptions = self.agent.skills.get_descriptions()
168
+ if not descriptions:
169
+ self.console.print("[yellow]No skill descriptions available[/yellow]")
170
+ self.console.print(f"Debug: skills object = {self.agent.skills}")
171
+ self.console.print(
172
+ f"Debug: registered skills = {list(self.agent.skills.skills.keys())}"
173
+ )
174
+ else:
175
+ self.console.print(Markdown(descriptions))
176
+
177
+ elif command == "/memory":
178
+ if not self.agent or not self.agent.memory:
179
+ self.console.print("[yellow]Memory not enabled[/yellow]")
180
+ else:
181
+ try:
182
+ memories = await self.agent.memory.recall("", limit=10)
183
+ if memories:
184
+ self.console.print("\n[bold]Recent Memories:[/bold]\n")
185
+ for i, mem in enumerate(memories, 1):
186
+ self.console.print(f"{i}. {mem.content[:100]}...")
187
+ else:
188
+ self.console.print("[yellow]No memories yet[/yellow]")
189
+ except Exception as e:
190
+ self.console.print(f"[red]Error loading memories: {e}[/red]")
191
+
192
+ elif command == "/clear":
193
+ self.console.clear()
194
+ self._display_welcome()
195
+
196
+ else:
197
+ self.console.print(f"[yellow]Unknown command: {command}[/yellow]")
198
+ self.console.print("Type /help for available commands")
199
+
200
+ return True
201
+
202
+ async def start(self):
203
+ """Start the interactive chat session."""
204
+ # Initialize agent
205
+ try:
206
+ instructions = self._load_instructions()
207
+
208
+ # Initialize memory if requested
209
+ memory = None
210
+ if self.use_memory:
211
+ try:
212
+ from teotl.primitives.memory.local import LocalMemory
213
+
214
+ memory_dir = self.workspace_dir or (Path.home() / ".forge" / "chat-memory")
215
+ memory = LocalMemory(str(memory_dir))
216
+ except Exception as e:
217
+ logger.warning(f"Failed to initialize memory: {e}")
218
+
219
+ self.agent = Agent(
220
+ provider=AnthropicProvider(),
221
+ instructions=instructions,
222
+ skills=self.skills,
223
+ memory=memory,
224
+ session_dir=self.session_dir,
225
+ )
226
+
227
+ logger.info(f"Chat session initialized: agent_id={self.agent_id}, skills={self.skills}")
228
+
229
+ except Exception as e:
230
+ self.console.print(f"[red]❌ Failed to initialize agent: {e}[/red]")
231
+ sys.exit(1)
232
+
233
+ # Display welcome
234
+ self._display_welcome()
235
+
236
+ # Chat loop
237
+ try:
238
+ while True:
239
+ # Get user input
240
+ try:
241
+ user_input = Prompt.ask("\n[bold cyan]You[/bold cyan]").strip()
242
+ except (KeyboardInterrupt, EOFError):
243
+ break
244
+
245
+ if not user_input:
246
+ continue
247
+
248
+ # Handle commands
249
+ if user_input.startswith("/"):
250
+ should_continue = await self._handle_command(user_input)
251
+ if not should_continue:
252
+ break
253
+ continue
254
+
255
+ # Send to agent
256
+ try:
257
+ self.console.print("\n[bold magenta]Agent[/bold magenta] (thinking...)", end="")
258
+
259
+ response = await self.agent.run(user_input, ui=self.ui)
260
+
261
+ # Clear "thinking..." line
262
+ self.console.print("\r" + " " * 50 + "\r", end="")
263
+
264
+ # Display response
265
+ self.console.print("[bold magenta]Agent:[/bold magenta]")
266
+ self.console.print(
267
+ Panel(
268
+ Markdown(response.text),
269
+ border_style="magenta",
270
+ padding=(1, 2),
271
+ )
272
+ )
273
+
274
+ # Show cost if available
275
+ if hasattr(response, "cost") and response.cost:
276
+ self.console.print(f"[dim]Cost: ${response.cost:.4f}[/dim]")
277
+
278
+ except KeyboardInterrupt:
279
+ self.console.print(
280
+ "\n[yellow]⏸️ Interrupted. Type /exit to quit or continue chatting.[/yellow]"
281
+ )
282
+ continue
283
+
284
+ except Exception as e:
285
+ logger.error(f"Error processing message: {e}", exc_info=True)
286
+ self.console.print(f"\n[red]❌ Error: {e}[/red]")
287
+ self.console.print(
288
+ "[yellow]You can continue chatting or type /exit to quit[/yellow]"
289
+ )
290
+
291
+ except KeyboardInterrupt:
292
+ pass # Clean exit
293
+
294
+ finally:
295
+ self.console.print("\n\n[bold cyan]πŸ‘‹ Chat session ended[/bold cyan]")
296
+ if self.agent and self.agent.session:
297
+ message_count = self.agent.session.message_count
298
+ self.console.print(f"[dim]Messages exchanged: {message_count}[/dim]")
299
+
300
+
301
+ async def chat_command(
302
+ agent_id: str | None = None,
303
+ skills: list[str] | None = None,
304
+ no_memory: bool = False,
305
+ ) -> None:
306
+ """Run interactive chat mode.
307
+
308
+ Args:
309
+ agent_id: Optional agent ID to load workspace files from
310
+ skills: List of skills to enable
311
+ no_memory: Disable memory system
312
+ """
313
+ session = ChatSession(
314
+ agent_id=agent_id,
315
+ skills=skills or [],
316
+ use_memory=not no_memory,
317
+ )
318
+
319
+ await session.start()