mindroom 0.0.0__py3-none-any.whl → 0.1.1__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.
- mindroom/__init__.py +3 -0
- mindroom/agent_prompts.py +963 -0
- mindroom/agents.py +248 -0
- mindroom/ai.py +421 -0
- mindroom/api/__init__.py +1 -0
- mindroom/api/credentials.py +137 -0
- mindroom/api/google_integration.py +355 -0
- mindroom/api/google_tools_helper.py +40 -0
- mindroom/api/homeassistant_integration.py +421 -0
- mindroom/api/integrations.py +189 -0
- mindroom/api/main.py +506 -0
- mindroom/api/matrix_operations.py +219 -0
- mindroom/api/tools.py +94 -0
- mindroom/background_tasks.py +87 -0
- mindroom/bot.py +2470 -0
- mindroom/cli.py +86 -0
- mindroom/commands.py +377 -0
- mindroom/config.py +343 -0
- mindroom/config_commands.py +324 -0
- mindroom/config_confirmation.py +411 -0
- mindroom/constants.py +52 -0
- mindroom/credentials.py +146 -0
- mindroom/credentials_sync.py +134 -0
- mindroom/custom_tools/__init__.py +8 -0
- mindroom/custom_tools/config_manager.py +765 -0
- mindroom/custom_tools/gmail.py +92 -0
- mindroom/custom_tools/google_calendar.py +92 -0
- mindroom/custom_tools/google_sheets.py +92 -0
- mindroom/custom_tools/homeassistant.py +341 -0
- mindroom/error_handling.py +35 -0
- mindroom/file_watcher.py +49 -0
- mindroom/interactive.py +313 -0
- mindroom/logging_config.py +207 -0
- mindroom/matrix/__init__.py +1 -0
- mindroom/matrix/client.py +782 -0
- mindroom/matrix/event_info.py +173 -0
- mindroom/matrix/identity.py +149 -0
- mindroom/matrix/large_messages.py +267 -0
- mindroom/matrix/mentions.py +141 -0
- mindroom/matrix/message_builder.py +94 -0
- mindroom/matrix/message_content.py +209 -0
- mindroom/matrix/presence.py +178 -0
- mindroom/matrix/rooms.py +311 -0
- mindroom/matrix/state.py +77 -0
- mindroom/matrix/typing.py +91 -0
- mindroom/matrix/users.py +217 -0
- mindroom/memory/__init__.py +21 -0
- mindroom/memory/config.py +137 -0
- mindroom/memory/functions.py +396 -0
- mindroom/py.typed +0 -0
- mindroom/response_tracker.py +128 -0
- mindroom/room_cleanup.py +139 -0
- mindroom/routing.py +107 -0
- mindroom/scheduling.py +758 -0
- mindroom/stop.py +207 -0
- mindroom/streaming.py +203 -0
- mindroom/teams.py +749 -0
- mindroom/thread_utils.py +318 -0
- mindroom/tools/__init__.py +520 -0
- mindroom/tools/agentql.py +64 -0
- mindroom/tools/airflow.py +57 -0
- mindroom/tools/apify.py +49 -0
- mindroom/tools/arxiv.py +64 -0
- mindroom/tools/aws_lambda.py +41 -0
- mindroom/tools/aws_ses.py +57 -0
- mindroom/tools/baidusearch.py +87 -0
- mindroom/tools/brightdata.py +116 -0
- mindroom/tools/browserbase.py +62 -0
- mindroom/tools/cal_com.py +98 -0
- mindroom/tools/calculator.py +112 -0
- mindroom/tools/cartesia.py +84 -0
- mindroom/tools/composio.py +166 -0
- mindroom/tools/config_manager.py +44 -0
- mindroom/tools/confluence.py +73 -0
- mindroom/tools/crawl4ai.py +101 -0
- mindroom/tools/csv.py +104 -0
- mindroom/tools/custom_api.py +106 -0
- mindroom/tools/dalle.py +85 -0
- mindroom/tools/daytona.py +180 -0
- mindroom/tools/discord.py +81 -0
- mindroom/tools/docker.py +73 -0
- mindroom/tools/duckdb.py +124 -0
- mindroom/tools/duckduckgo.py +99 -0
- mindroom/tools/e2b.py +121 -0
- mindroom/tools/eleven_labs.py +77 -0
- mindroom/tools/email.py +74 -0
- mindroom/tools/exa.py +246 -0
- mindroom/tools/fal.py +50 -0
- mindroom/tools/file.py +80 -0
- mindroom/tools/financial_datasets_api.py +112 -0
- mindroom/tools/firecrawl.py +124 -0
- mindroom/tools/gemini.py +85 -0
- mindroom/tools/giphy.py +49 -0
- mindroom/tools/github.py +376 -0
- mindroom/tools/gmail.py +102 -0
- mindroom/tools/google_calendar.py +55 -0
- mindroom/tools/google_maps.py +112 -0
- mindroom/tools/google_sheets.py +86 -0
- mindroom/tools/googlesearch.py +83 -0
- mindroom/tools/groq.py +77 -0
- mindroom/tools/hackernews.py +54 -0
- mindroom/tools/jina.py +108 -0
- mindroom/tools/jira.py +70 -0
- mindroom/tools/linear.py +103 -0
- mindroom/tools/linkup.py +65 -0
- mindroom/tools/lumalabs.py +71 -0
- mindroom/tools/mem0.py +82 -0
- mindroom/tools/modelslabs.py +85 -0
- mindroom/tools/moviepy_video_tools.py +62 -0
- mindroom/tools/newspaper4k.py +63 -0
- mindroom/tools/openai.py +143 -0
- mindroom/tools/openweather.py +89 -0
- mindroom/tools/oxylabs.py +54 -0
- mindroom/tools/pandas.py +35 -0
- mindroom/tools/pubmed.py +64 -0
- mindroom/tools/python.py +120 -0
- mindroom/tools/reddit.py +155 -0
- mindroom/tools/replicate.py +56 -0
- mindroom/tools/resend.py +55 -0
- mindroom/tools/scrapegraph.py +87 -0
- mindroom/tools/searxng.py +120 -0
- mindroom/tools/serpapi.py +55 -0
- mindroom/tools/serper.py +81 -0
- mindroom/tools/shell.py +46 -0
- mindroom/tools/slack.py +80 -0
- mindroom/tools/sleep.py +38 -0
- mindroom/tools/spider.py +62 -0
- mindroom/tools/sql.py +138 -0
- mindroom/tools/tavily.py +104 -0
- mindroom/tools/telegram.py +54 -0
- mindroom/tools/todoist.py +103 -0
- mindroom/tools/trello.py +121 -0
- mindroom/tools/twilio.py +97 -0
- mindroom/tools/web_browser_tools.py +37 -0
- mindroom/tools/webex.py +63 -0
- mindroom/tools/website.py +45 -0
- mindroom/tools/whatsapp.py +81 -0
- mindroom/tools/wikipedia.py +45 -0
- mindroom/tools/x.py +97 -0
- mindroom/tools/yfinance.py +121 -0
- mindroom/tools/youtube.py +81 -0
- mindroom/tools/zendesk.py +62 -0
- mindroom/tools/zep.py +107 -0
- mindroom/tools/zoom.py +62 -0
- mindroom/tools_metadata.json +7643 -0
- mindroom/tools_metadata.py +220 -0
- mindroom/topic_generator.py +153 -0
- mindroom/voice_handler.py +266 -0
- mindroom-0.1.1.dist-info/METADATA +425 -0
- mindroom-0.1.1.dist-info/RECORD +152 -0
- {mindroom-0.0.0.dist-info → mindroom-0.1.1.dist-info}/WHEEL +1 -2
- mindroom-0.1.1.dist-info/entry_points.txt +2 -0
- mindroom-0.0.0.dist-info/METADATA +0 -24
- mindroom-0.0.0.dist-info/RECORD +0 -4
- mindroom-0.0.0.dist-info/top_level.txt +0 -1
mindroom/cli.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Mindroom CLI - Simplified multi-agent Matrix bot system."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
12
|
+
from mindroom import __version__
|
|
13
|
+
from mindroom.bot import main as bot_main
|
|
14
|
+
from mindroom.constants import STORAGE_PATH
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(
|
|
17
|
+
help="Mindroom: Multi-agent Matrix bot system",
|
|
18
|
+
pretty_exceptions_enable=True,
|
|
19
|
+
# Disable showing locals which can be very large (also see `setup_logging`)
|
|
20
|
+
pretty_exceptions_show_locals=False,
|
|
21
|
+
)
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@app.command()
|
|
26
|
+
def version() -> None:
|
|
27
|
+
"""Show the current version of Mindroom."""
|
|
28
|
+
console.print(f"Mindroom version: [bold]{__version__}[/bold]")
|
|
29
|
+
console.print("Multi-agent Matrix bot system")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@app.command()
|
|
33
|
+
def run(
|
|
34
|
+
log_level: str = typer.Option(
|
|
35
|
+
"INFO",
|
|
36
|
+
"--log-level",
|
|
37
|
+
"-l",
|
|
38
|
+
help="Set the logging level (DEBUG, INFO, WARNING, ERROR)",
|
|
39
|
+
case_sensitive=False,
|
|
40
|
+
),
|
|
41
|
+
storage_path: Path = typer.Option( # noqa: B008
|
|
42
|
+
Path(STORAGE_PATH),
|
|
43
|
+
"--storage-path",
|
|
44
|
+
"-s",
|
|
45
|
+
help="Base directory for persistent MindRoom data (state, sessions, tracking)",
|
|
46
|
+
),
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Run the mindroom multi-agent system.
|
|
49
|
+
|
|
50
|
+
This command starts the multi-agent bot system which automatically:
|
|
51
|
+
- Creates all necessary user and agent accounts
|
|
52
|
+
- Creates all rooms defined in config.yaml
|
|
53
|
+
- Manages agent room memberships
|
|
54
|
+
"""
|
|
55
|
+
asyncio.run(_run(log_level=log_level.upper(), storage_path=storage_path))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def _run(log_level: str, storage_path: Path) -> None:
|
|
59
|
+
"""Run the multi-agent system."""
|
|
60
|
+
console.print(f"🚀 Starting Mindroom multi-agent system (log level: {log_level})...")
|
|
61
|
+
console.print("Press Ctrl+C to stop\n")
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
await bot_main(log_level=log_level, storage_path=storage_path)
|
|
65
|
+
except KeyboardInterrupt:
|
|
66
|
+
console.print("\n✋ Stopped")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def main() -> None:
|
|
70
|
+
"""Main entry point that shows help by default."""
|
|
71
|
+
# Handle -h flag by replacing with --help
|
|
72
|
+
for i, arg in enumerate(sys.argv):
|
|
73
|
+
if arg == "-h":
|
|
74
|
+
sys.argv[i] = "--help"
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
# If no arguments provided, show help
|
|
78
|
+
if len(sys.argv) == 1:
|
|
79
|
+
# Show help by appending --help to argv
|
|
80
|
+
sys.argv.append("--help")
|
|
81
|
+
|
|
82
|
+
app()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
main()
|
mindroom/commands.py
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""Command parsing and handling for user commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import nio
|
|
11
|
+
|
|
12
|
+
from .constants import VOICE_PREFIX
|
|
13
|
+
from .logging_config import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CommandType(Enum):
|
|
19
|
+
"""Types of commands supported."""
|
|
20
|
+
|
|
21
|
+
HELP = "help"
|
|
22
|
+
SCHEDULE = "schedule"
|
|
23
|
+
LIST_SCHEDULES = "list_schedules"
|
|
24
|
+
CANCEL_SCHEDULE = "cancel_schedule"
|
|
25
|
+
WIDGET = "widget"
|
|
26
|
+
CONFIG = "config" # Configuration command
|
|
27
|
+
HI = "hi" # Welcome message command
|
|
28
|
+
UNKNOWN = "unknown" # Special type for unrecognized commands
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Command documentation for each command type
|
|
32
|
+
COMMAND_DOCS = {
|
|
33
|
+
CommandType.SCHEDULE: ("!schedule <task>", "Schedule a task"),
|
|
34
|
+
CommandType.LIST_SCHEDULES: ("!list_schedules", "List scheduled tasks"),
|
|
35
|
+
CommandType.CANCEL_SCHEDULE: ("!cancel_schedule <id>", "Cancel a scheduled task"),
|
|
36
|
+
CommandType.HELP: ("!help [topic]", "Get help"),
|
|
37
|
+
CommandType.WIDGET: ("!widget [url]", "Add configuration widget"),
|
|
38
|
+
CommandType.CONFIG: ("!config <operation>", "Manage configuration"),
|
|
39
|
+
CommandType.HI: ("!hi", "Show welcome message"),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _get_command_entries(format_code: bool = False) -> list[str]:
|
|
44
|
+
"""Get command entries as a list of formatted strings.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
format_code: If True, wrap commands in backticks for markdown
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
List of formatted command strings
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
entries = []
|
|
54
|
+
for cmd_type in CommandType:
|
|
55
|
+
if cmd_type in COMMAND_DOCS and cmd_type != CommandType.UNKNOWN:
|
|
56
|
+
syntax, description = COMMAND_DOCS[cmd_type]
|
|
57
|
+
if format_code:
|
|
58
|
+
entries.append(f"- `{syntax}` - {description}")
|
|
59
|
+
else:
|
|
60
|
+
entries.append(f"- {syntax} - {description}")
|
|
61
|
+
return entries
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_command_list() -> str:
|
|
65
|
+
"""Get a formatted list of all available commands.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Formatted string with all commands and their descriptions
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
lines = ["Available commands:", *_get_command_entries(format_code=False)]
|
|
72
|
+
return "\n".join(lines)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class Command:
|
|
77
|
+
"""Parsed command with arguments."""
|
|
78
|
+
|
|
79
|
+
type: CommandType
|
|
80
|
+
args: dict[str, Any]
|
|
81
|
+
raw_text: str
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class CommandParser:
|
|
85
|
+
"""Parser for user commands in messages."""
|
|
86
|
+
|
|
87
|
+
# Command patterns
|
|
88
|
+
HELP_PATTERN = re.compile(r"^!help(?:\s+(.+))?$", re.IGNORECASE)
|
|
89
|
+
SCHEDULE_PATTERN = re.compile(r"^!schedule\s+(.+)$", re.IGNORECASE | re.DOTALL)
|
|
90
|
+
LIST_SCHEDULES_PATTERN = re.compile(r"^!list[_-]?schedules?$", re.IGNORECASE)
|
|
91
|
+
CANCEL_SCHEDULE_PATTERN = re.compile(r"^!cancel[_-]?schedule\s+(.+)$", re.IGNORECASE)
|
|
92
|
+
WIDGET_PATTERN = re.compile(r"^!widget(?:\s+(.+))?$", re.IGNORECASE)
|
|
93
|
+
CONFIG_PATTERN = re.compile(r"^!config(?:\s+(.+))?$", re.IGNORECASE)
|
|
94
|
+
HI_PATTERN = re.compile(r"^!hi$", re.IGNORECASE)
|
|
95
|
+
|
|
96
|
+
def parse(self, message: str) -> Command | None: # noqa: PLR0911
|
|
97
|
+
"""Parse a message for commands.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
message: The message text to parse
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Parsed command or None if no command found
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
message = message.strip()
|
|
107
|
+
|
|
108
|
+
# Handle voice emoji prefixe (e.g., "🎤 !schedule ...")
|
|
109
|
+
message = message.removeprefix(VOICE_PREFIX)
|
|
110
|
+
if not message.startswith("!"):
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
# Try to match each command pattern
|
|
114
|
+
|
|
115
|
+
# !hi command (check this early as it's simple)
|
|
116
|
+
if self.HI_PATTERN.match(message):
|
|
117
|
+
return Command(
|
|
118
|
+
type=CommandType.HI,
|
|
119
|
+
args={},
|
|
120
|
+
raw_text=message,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# !help command
|
|
124
|
+
match = self.HELP_PATTERN.match(message)
|
|
125
|
+
if match:
|
|
126
|
+
topic = match.group(1)
|
|
127
|
+
return Command(
|
|
128
|
+
type=CommandType.HELP,
|
|
129
|
+
args={"topic": topic},
|
|
130
|
+
raw_text=message,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# !schedule command
|
|
134
|
+
match = self.SCHEDULE_PATTERN.match(message)
|
|
135
|
+
if match:
|
|
136
|
+
full_text = match.group(1).strip()
|
|
137
|
+
# Pass the entire text to AI - it will parse both time and message
|
|
138
|
+
return Command(
|
|
139
|
+
type=CommandType.SCHEDULE,
|
|
140
|
+
args={"full_text": full_text},
|
|
141
|
+
raw_text=message,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# !list_schedules command
|
|
145
|
+
if self.LIST_SCHEDULES_PATTERN.match(message):
|
|
146
|
+
return Command(
|
|
147
|
+
type=CommandType.LIST_SCHEDULES,
|
|
148
|
+
args={},
|
|
149
|
+
raw_text=message,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# !cancel_schedule command
|
|
153
|
+
match = self.CANCEL_SCHEDULE_PATTERN.match(message)
|
|
154
|
+
if match:
|
|
155
|
+
task_id = match.group(1).strip()
|
|
156
|
+
# Check if user wants to cancel all tasks
|
|
157
|
+
cancel_all = task_id.lower() == "all"
|
|
158
|
+
return Command(
|
|
159
|
+
type=CommandType.CANCEL_SCHEDULE,
|
|
160
|
+
args={"task_id": task_id, "cancel_all": cancel_all},
|
|
161
|
+
raw_text=message,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# !widget command
|
|
165
|
+
match = self.WIDGET_PATTERN.match(message)
|
|
166
|
+
if match:
|
|
167
|
+
url = match.group(1).strip() if match.group(1) else None
|
|
168
|
+
return Command(
|
|
169
|
+
type=CommandType.WIDGET,
|
|
170
|
+
args={"url": url},
|
|
171
|
+
raw_text=message,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# !config command
|
|
175
|
+
match = self.CONFIG_PATTERN.match(message)
|
|
176
|
+
if match:
|
|
177
|
+
args_text = match.group(1).strip() if match.group(1) else ""
|
|
178
|
+
return Command(
|
|
179
|
+
type=CommandType.CONFIG,
|
|
180
|
+
args={"args_text": args_text},
|
|
181
|
+
raw_text=message,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Unknown command - return a special Command indicating it's unknown
|
|
185
|
+
logger.debug(f"Unknown command: {message}")
|
|
186
|
+
return Command(
|
|
187
|
+
type=CommandType.UNKNOWN,
|
|
188
|
+
args={"raw_command": message},
|
|
189
|
+
raw_text=message,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def get_command_help(topic: str | None = None) -> str:
|
|
194
|
+
"""Get help text for commands.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
topic: Specific topic to get help for (optional)
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Help text
|
|
201
|
+
|
|
202
|
+
"""
|
|
203
|
+
if topic == "schedule":
|
|
204
|
+
return """**Schedule Command**
|
|
205
|
+
|
|
206
|
+
Usage: `!schedule <time> <message>` - Schedule tasks, reminders, or agent workflows
|
|
207
|
+
|
|
208
|
+
**Simple Reminders:**
|
|
209
|
+
- `!schedule in 5 minutes Check the deployment`
|
|
210
|
+
- `!schedule tomorrow at 3pm Send the weekly report`
|
|
211
|
+
- `!schedule later Ping me about the meeting`
|
|
212
|
+
- `ping me tomorrow about the meeting`
|
|
213
|
+
- `remind me in 2 hours to review PRs`
|
|
214
|
+
|
|
215
|
+
**Event-Driven Workflows (New!):**
|
|
216
|
+
- `!schedule If I get an email about "urgent", @phone_agent call me`
|
|
217
|
+
- `!schedule When Bitcoin drops below $40k, @crypto_agent notify me`
|
|
218
|
+
- `!schedule If server CPU > 80%, @ops_agent scale up`
|
|
219
|
+
- `!schedule When someone mentions our product on Reddit, @analyst summarize it`
|
|
220
|
+
- `!schedule Whenever I get email from boss, @notification_agent alert me immediately`
|
|
221
|
+
|
|
222
|
+
**Agent Workflows:**
|
|
223
|
+
- `!schedule Daily at 9am, @finance give me a market analysis`
|
|
224
|
+
- `!schedule Every Monday, @research AI news and @email_assistant send me a summary`
|
|
225
|
+
- `!schedule tomorrow at 2pm, @email_assistant check my Gmail`
|
|
226
|
+
|
|
227
|
+
**Recurring Tasks (Cron-style):**
|
|
228
|
+
- `!schedule Every hour, @shell check server status`
|
|
229
|
+
- `!schedule Daily at 9am, @finance market report`
|
|
230
|
+
- `!schedule Weekly on Friday, @analyst prepare weekly summary`
|
|
231
|
+
|
|
232
|
+
How it works:
|
|
233
|
+
- **Time-based**: Executes at specific times or intervals
|
|
234
|
+
- **Event-based**: Automatically converts to smart polling (e.g., "if email" → check every 1-2 min)
|
|
235
|
+
- Agents receive clear instructions about conditions to check
|
|
236
|
+
- Multiple agents collaborate when mentioned together
|
|
237
|
+
- Automated tasks are clearly marked so agents don't wait for follow-up"""
|
|
238
|
+
|
|
239
|
+
if topic == "list_schedules":
|
|
240
|
+
return """**List Schedules Command**
|
|
241
|
+
|
|
242
|
+
Usage: `!list_schedules` or `!listschedules`
|
|
243
|
+
|
|
244
|
+
Shows all pending scheduled tasks in this thread."""
|
|
245
|
+
|
|
246
|
+
if topic in {"cancel", "cancel_schedule"}:
|
|
247
|
+
return """**Cancel Schedule Command**
|
|
248
|
+
|
|
249
|
+
Usage: `!cancel_schedule <id>` - Cancel a scheduled task
|
|
250
|
+
`!cancel_schedule all` - Cancel ALL scheduled tasks in this room
|
|
251
|
+
|
|
252
|
+
Examples:
|
|
253
|
+
- `!cancel_schedule abc123` - Cancel the task with ID abc123
|
|
254
|
+
- `!cancel_schedule all` - Cancel all scheduled tasks (requires confirmation)
|
|
255
|
+
|
|
256
|
+
Use `!list_schedules` to see task IDs."""
|
|
257
|
+
|
|
258
|
+
if topic == "config":
|
|
259
|
+
return """**Config Command**
|
|
260
|
+
|
|
261
|
+
Usage: `!config <operation>` - View and modify MindRoom configuration
|
|
262
|
+
|
|
263
|
+
**Viewing Configuration:**
|
|
264
|
+
- `!config show` - Show entire configuration
|
|
265
|
+
- `!config get <path>` - Get a specific configuration value
|
|
266
|
+
- `!config get agents` - Show all agents
|
|
267
|
+
- `!config get models.default` - Show default model
|
|
268
|
+
- `!config get agents.analyst.display_name` - Show analyst's display name
|
|
269
|
+
|
|
270
|
+
**Modifying Configuration:**
|
|
271
|
+
- `!config set <path> <value>` - Set a configuration value
|
|
272
|
+
- `!config set agents.analyst.display_name "Research Expert"` - Change display name
|
|
273
|
+
- `!config set models.default.id gpt-4` - Change default model
|
|
274
|
+
- `!config set defaults.markdown false` - Disable markdown by default
|
|
275
|
+
- `!config set timezone America/New_York` - Set timezone
|
|
276
|
+
|
|
277
|
+
**Path Syntax:**
|
|
278
|
+
- Use dot notation to navigate nested config (e.g., `agents.analyst.role`)
|
|
279
|
+
- Arrays use indexes (e.g., `agents.analyst.tools.0` for first tool)
|
|
280
|
+
- String values with spaces must be quoted
|
|
281
|
+
|
|
282
|
+
**Note:** Configuration changes are immediately saved to config.yaml and affect all new agent interactions."""
|
|
283
|
+
|
|
284
|
+
if topic == "widget":
|
|
285
|
+
return """**Widget Command**
|
|
286
|
+
|
|
287
|
+
Usage: `!widget [url]` - Add the MindRoom configuration widget to this room
|
|
288
|
+
|
|
289
|
+
Examples:
|
|
290
|
+
- `!widget` - Add widget using default URL (http://localhost:3003)
|
|
291
|
+
- `!widget https://config.mindroom.ai` - Add widget from custom URL
|
|
292
|
+
|
|
293
|
+
The widget provides a visual interface for configuring MindRoom agents and settings.
|
|
294
|
+
Pin it to keep it visible in the room.
|
|
295
|
+
|
|
296
|
+
Note: Widget support requires Element Desktop or self-hosted Element Web."""
|
|
297
|
+
|
|
298
|
+
# General help - dynamically generated from COMMAND_DOCS
|
|
299
|
+
commands_text = "\n".join(_get_command_entries(format_code=True))
|
|
300
|
+
|
|
301
|
+
return f"""**Available Commands**
|
|
302
|
+
|
|
303
|
+
{commands_text}
|
|
304
|
+
|
|
305
|
+
**Scheduling Features:**
|
|
306
|
+
- Time-based and event-driven workflows
|
|
307
|
+
- Recurring tasks with cron-style scheduling (daily, weekly, hourly)
|
|
308
|
+
- Agent workflows - mention agents to have them collaborate on scheduled tasks
|
|
309
|
+
- Natural language time parsing - "tomorrow", "in 5 minutes", "every Monday"
|
|
310
|
+
|
|
311
|
+
Note: All commands only work within threads, not in main room messages
|
|
312
|
+
(except !widget which works in the main room).
|
|
313
|
+
|
|
314
|
+
For detailed help on a command, use: `!help <command>`"""
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
async def handle_widget_command(
|
|
318
|
+
client: nio.AsyncClient,
|
|
319
|
+
room_id: str,
|
|
320
|
+
url: str | None = None,
|
|
321
|
+
) -> str:
|
|
322
|
+
"""Handle the widget command to add configuration widget to room.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
client: The Matrix client
|
|
326
|
+
room_id: The room ID to add widget to
|
|
327
|
+
url: Optional custom widget URL
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
Response text for the user
|
|
331
|
+
|
|
332
|
+
"""
|
|
333
|
+
# Default URL for local development
|
|
334
|
+
default_url = "http://localhost:3003/matrix-widget.html"
|
|
335
|
+
widget_url = url if url else default_url
|
|
336
|
+
|
|
337
|
+
# Create the widget state event content
|
|
338
|
+
widget_content = {
|
|
339
|
+
"type": "custom",
|
|
340
|
+
"url": widget_url,
|
|
341
|
+
"name": "MindRoom Configuration",
|
|
342
|
+
"data": {"title": "MindRoom Configuration", "curl": widget_url.replace("/matrix-widget.html", "")},
|
|
343
|
+
"creatorUserId": client.user_id,
|
|
344
|
+
"id": "mindroom_config",
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
# Send the state event to add the widget
|
|
349
|
+
response = await client.room_put_state(
|
|
350
|
+
room_id=room_id,
|
|
351
|
+
event_type="im.vector.modular.widgets",
|
|
352
|
+
state_key="mindroom_config",
|
|
353
|
+
content=widget_content,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
if isinstance(response, nio.RoomPutStateError):
|
|
357
|
+
logger.error(f"Failed to add widget to room {room_id}: {response.message}")
|
|
358
|
+
return f"❌ Failed to add widget: {response.message}"
|
|
359
|
+
|
|
360
|
+
logger.info(f"Successfully added widget to room {room_id}")
|
|
361
|
+
except Exception as e:
|
|
362
|
+
logger.exception("Error adding widget to room", room_id=room_id)
|
|
363
|
+
return f"❌ Error adding widget: {e!s}"
|
|
364
|
+
else:
|
|
365
|
+
return (
|
|
366
|
+
"✅ **MindRoom Configuration widget added!**\n\n"
|
|
367
|
+
"• Pin the widget to keep it visible\n"
|
|
368
|
+
"• All room members can access the configuration\n"
|
|
369
|
+
"• Changes sync in real-time with config.yaml\n\n"
|
|
370
|
+
f"Widget URL: {widget_url}\n\n"
|
|
371
|
+
"**Note:** Widgets require Element Desktop or self-hosted Element Web.\n"
|
|
372
|
+
"Alternatively, you can use: `/addwidget {url}` in Element."
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# Global parser instance
|
|
377
|
+
command_parser = CommandParser()
|