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.
- teotl/__init__.py +19 -0
- teotl/cli/__init__.py +103 -0
- teotl/cli/__main__.py +11 -0
- teotl/cli/chat.py +319 -0
- teotl/cli/memory.py +270 -0
- teotl/cli/orchestrator.py +209 -0
- teotl/cli/security.py +532 -0
- teotl/cli/wizard.py +3562 -0
- teotl/cloud/__init__.py +20 -0
- teotl/config/__init__.py +89 -0
- teotl/config/factory.py +544 -0
- teotl/config/multi_agent.py +385 -0
- teotl/core/__init__.py +1 -0
- teotl/core/agent.py +979 -0
- teotl/core/events.py +115 -0
- teotl/core/provider.py +483 -0
- teotl/core/rate_limited_provider.py +175 -0
- teotl/core/security/__init__.py +64 -0
- teotl/core/security/audit.py +505 -0
- teotl/core/security/cost_tracker.py +254 -0
- teotl/core/security/enforcement.py +384 -0
- teotl/core/security/policy.py +633 -0
- teotl/core/security/sandbox.py +554 -0
- teotl/core/session.py +192 -0
- teotl/core/tools/__init__.py +9 -0
- teotl/core/tools/bash.py +316 -0
- teotl/core/tools/browser.py +572 -0
- teotl/core/types.py +250 -0
- teotl/daemon/__init__.py +19 -0
- teotl/daemon/__main__.py +6 -0
- teotl/daemon/agent_daemon.py +406 -0
- teotl/daemon/executor.py +570 -0
- teotl/daemon/run.py +372 -0
- teotl/extensions/__init__.py +1 -0
- teotl/extensions/builtin/__init__.py +6 -0
- teotl/extensions/builtin/audit.py +83 -0
- teotl/extensions/builtin/undo.py +101 -0
- teotl/extensions/manager.py +54 -0
- teotl/primitives/__init__.py +6 -0
- teotl/primitives/a2a/__init__.py +22 -0
- teotl/primitives/a2a/client.py +97 -0
- teotl/primitives/a2a/protocol.py +67 -0
- teotl/primitives/a2a/server.py +210 -0
- teotl/primitives/discovery/__init__.py +9 -0
- teotl/primitives/discovery/local.py +227 -0
- teotl/primitives/guardrails/__init__.py +6 -0
- teotl/primitives/guardrails/bash_analyzer.py +224 -0
- teotl/primitives/guardrails/classifier.py +63 -0
- teotl/primitives/guardrails/engine.py +136 -0
- teotl/primitives/guardrails/policy.py +223 -0
- teotl/primitives/guardrails/presets.py +149 -0
- teotl/primitives/guardrails/prompt_injection.py +182 -0
- teotl/primitives/guardrails/rate_limiter.py +337 -0
- teotl/primitives/guardrails/trust.py +91 -0
- teotl/primitives/harness/README.md +912 -0
- teotl/primitives/harness/__init__.py +139 -0
- teotl/primitives/harness/advanced_janitor.py +372 -0
- teotl/primitives/harness/agent.py +242 -0
- teotl/primitives/harness/audit.py +542 -0
- teotl/primitives/harness/checkpoint.py +421 -0
- teotl/primitives/harness/cost_tracker.py +317 -0
- teotl/primitives/harness/dryrun.py +574 -0
- teotl/primitives/harness/evaluator.py +597 -0
- teotl/primitives/harness/exceptions.py +203 -0
- teotl/primitives/harness/explorer.py +529 -0
- teotl/primitives/harness/heartbeat.py +627 -0
- teotl/primitives/harness/janitor.py +332 -0
- teotl/primitives/harness/orchestrator.py +890 -0
- teotl/primitives/harness/plan.py +371 -0
- teotl/primitives/harness/planner.py +376 -0
- teotl/primitives/harness/progress.py +257 -0
- teotl/primitives/harness/state.py +374 -0
- teotl/primitives/harness/verification.py +553 -0
- teotl/primitives/harness/worker.py +622 -0
- teotl/primitives/integrations/__init__.py +1 -0
- teotl/primitives/integrations/credential_store.py +505 -0
- teotl/primitives/integrations/mcp_bridge.py +187 -0
- teotl/primitives/integrations/registry.py +154 -0
- teotl/primitives/memory/__init__.py +6 -0
- teotl/primitives/memory/context.py +142 -0
- teotl/primitives/memory/encrypted.py +364 -0
- teotl/primitives/memory/local.py +448 -0
- teotl/primitives/memory/store.py +66 -0
- teotl/primitives/missions/__init__.py +19 -0
- teotl/primitives/missions/persistent.py +548 -0
- teotl/primitives/permissions/__init__.py +5 -0
- teotl/primitives/resolution/__init__.py +25 -0
- teotl/primitives/resolution/interface.py +132 -0
- teotl/primitives/resolution/mock.py +155 -0
- teotl/primitives/skills/__init__.py +6 -0
- teotl/primitives/skills/loader.py +109 -0
- teotl/primitives/skills/registry.py +154 -0
- teotl/primitives/skills/router.py +38 -0
- teotl/primitives/tasks/__init__.py +11 -0
- teotl/primitives/tasks/persistent.py +452 -0
- teotl/skills/claude_code/SKILL.md +1049 -0
- teotl/skills/filesystem/SKILL.md +397 -0
- teotl/skills/git/SKILL.md +690 -0
- teotl/skills/github/SKILL.md +339 -0
- teotl/skills/platforms/linkedin/SKILL.md +250 -0
- teotl/skills/platforms/medium/SKILL.md +378 -0
- teotl/skills/platforms/substack/SKILL.md +466 -0
- teotl/skills/platforms/twitter/SKILL.md +345 -0
- teotl/skills/social-media/SKILL.md +520 -0
- teotl/skills/spec_kit/SKILL.md +886 -0
- teotl/skills/web/SKILL.md +488 -0
- teotl/templates/GOALS.md +33 -0
- teotl/templates/INSTRUCTIONS.md +145 -0
- teotl/templates/INSTRUCTIONS_AUTONOMOUS.md +199 -0
- teotl/templates/MISSIONS.md +202 -0
- teotl/templates/PERSONALITY.md +79 -0
- teotl/templates/SKILLS.md +230 -0
- teotl/templates/USER.md +109 -0
- teotl/ui/__init__.py +15 -0
- teotl/ui/cli.py +63 -0
- teotl/ui/security_panel.py +418 -0
- teotl/web/__init__.py +5 -0
- teotl/web/__main__.py +186 -0
- teotl/web/server.py +391 -0
- teotl/web/static/index.html +957 -0
- teotl-0.2.0.dist-info/METADATA +281 -0
- teotl-0.2.0.dist-info/RECORD +125 -0
- teotl-0.2.0.dist-info/WHEEL +4 -0
- teotl-0.2.0.dist-info/entry_points.txt +2 -0
- 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
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()
|