mindroom 0.0.0__py3-none-any.whl → 0.1.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 (155) hide show
  1. mindroom/__init__.py +3 -0
  2. mindroom/agent_prompts.py +963 -0
  3. mindroom/agents.py +248 -0
  4. mindroom/ai.py +421 -0
  5. mindroom/api/__init__.py +1 -0
  6. mindroom/api/credentials.py +137 -0
  7. mindroom/api/google_integration.py +355 -0
  8. mindroom/api/google_tools_helper.py +40 -0
  9. mindroom/api/homeassistant_integration.py +421 -0
  10. mindroom/api/integrations.py +189 -0
  11. mindroom/api/main.py +506 -0
  12. mindroom/api/matrix_operations.py +219 -0
  13. mindroom/api/tools.py +94 -0
  14. mindroom/background_tasks.py +87 -0
  15. mindroom/bot.py +2470 -0
  16. mindroom/cli.py +86 -0
  17. mindroom/commands.py +377 -0
  18. mindroom/config.py +343 -0
  19. mindroom/config_commands.py +324 -0
  20. mindroom/config_confirmation.py +411 -0
  21. mindroom/constants.py +52 -0
  22. mindroom/credentials.py +146 -0
  23. mindroom/credentials_sync.py +134 -0
  24. mindroom/custom_tools/__init__.py +8 -0
  25. mindroom/custom_tools/config_manager.py +765 -0
  26. mindroom/custom_tools/gmail.py +92 -0
  27. mindroom/custom_tools/google_calendar.py +92 -0
  28. mindroom/custom_tools/google_sheets.py +92 -0
  29. mindroom/custom_tools/homeassistant.py +341 -0
  30. mindroom/error_handling.py +35 -0
  31. mindroom/file_watcher.py +49 -0
  32. mindroom/interactive.py +313 -0
  33. mindroom/logging_config.py +207 -0
  34. mindroom/matrix/__init__.py +1 -0
  35. mindroom/matrix/client.py +782 -0
  36. mindroom/matrix/event_info.py +173 -0
  37. mindroom/matrix/identity.py +149 -0
  38. mindroom/matrix/large_messages.py +267 -0
  39. mindroom/matrix/mentions.py +141 -0
  40. mindroom/matrix/message_builder.py +94 -0
  41. mindroom/matrix/message_content.py +209 -0
  42. mindroom/matrix/presence.py +178 -0
  43. mindroom/matrix/rooms.py +311 -0
  44. mindroom/matrix/state.py +77 -0
  45. mindroom/matrix/typing.py +91 -0
  46. mindroom/matrix/users.py +217 -0
  47. mindroom/memory/__init__.py +21 -0
  48. mindroom/memory/config.py +137 -0
  49. mindroom/memory/functions.py +396 -0
  50. mindroom/py.typed +0 -0
  51. mindroom/response_tracker.py +128 -0
  52. mindroom/room_cleanup.py +139 -0
  53. mindroom/routing.py +107 -0
  54. mindroom/scheduling.py +758 -0
  55. mindroom/stop.py +207 -0
  56. mindroom/streaming.py +203 -0
  57. mindroom/teams.py +749 -0
  58. mindroom/thread_utils.py +318 -0
  59. mindroom/tools/__init__.py +520 -0
  60. mindroom/tools/agentql.py +64 -0
  61. mindroom/tools/airflow.py +57 -0
  62. mindroom/tools/apify.py +49 -0
  63. mindroom/tools/arxiv.py +64 -0
  64. mindroom/tools/aws_lambda.py +41 -0
  65. mindroom/tools/aws_ses.py +57 -0
  66. mindroom/tools/baidusearch.py +87 -0
  67. mindroom/tools/brightdata.py +116 -0
  68. mindroom/tools/browserbase.py +62 -0
  69. mindroom/tools/cal_com.py +98 -0
  70. mindroom/tools/calculator.py +112 -0
  71. mindroom/tools/cartesia.py +84 -0
  72. mindroom/tools/composio.py +166 -0
  73. mindroom/tools/config_manager.py +44 -0
  74. mindroom/tools/confluence.py +73 -0
  75. mindroom/tools/crawl4ai.py +101 -0
  76. mindroom/tools/csv.py +104 -0
  77. mindroom/tools/custom_api.py +106 -0
  78. mindroom/tools/dalle.py +85 -0
  79. mindroom/tools/daytona.py +180 -0
  80. mindroom/tools/discord.py +81 -0
  81. mindroom/tools/docker.py +73 -0
  82. mindroom/tools/duckdb.py +124 -0
  83. mindroom/tools/duckduckgo.py +99 -0
  84. mindroom/tools/e2b.py +121 -0
  85. mindroom/tools/eleven_labs.py +77 -0
  86. mindroom/tools/email.py +74 -0
  87. mindroom/tools/exa.py +246 -0
  88. mindroom/tools/fal.py +50 -0
  89. mindroom/tools/file.py +80 -0
  90. mindroom/tools/financial_datasets_api.py +112 -0
  91. mindroom/tools/firecrawl.py +124 -0
  92. mindroom/tools/gemini.py +85 -0
  93. mindroom/tools/giphy.py +49 -0
  94. mindroom/tools/github.py +376 -0
  95. mindroom/tools/gmail.py +102 -0
  96. mindroom/tools/google_calendar.py +55 -0
  97. mindroom/tools/google_maps.py +112 -0
  98. mindroom/tools/google_sheets.py +86 -0
  99. mindroom/tools/googlesearch.py +83 -0
  100. mindroom/tools/groq.py +77 -0
  101. mindroom/tools/hackernews.py +54 -0
  102. mindroom/tools/jina.py +108 -0
  103. mindroom/tools/jira.py +70 -0
  104. mindroom/tools/linear.py +103 -0
  105. mindroom/tools/linkup.py +65 -0
  106. mindroom/tools/lumalabs.py +71 -0
  107. mindroom/tools/mem0.py +82 -0
  108. mindroom/tools/modelslabs.py +85 -0
  109. mindroom/tools/moviepy_video_tools.py +62 -0
  110. mindroom/tools/newspaper4k.py +63 -0
  111. mindroom/tools/openai.py +143 -0
  112. mindroom/tools/openweather.py +89 -0
  113. mindroom/tools/oxylabs.py +54 -0
  114. mindroom/tools/pandas.py +35 -0
  115. mindroom/tools/pubmed.py +64 -0
  116. mindroom/tools/python.py +120 -0
  117. mindroom/tools/reddit.py +155 -0
  118. mindroom/tools/replicate.py +56 -0
  119. mindroom/tools/resend.py +55 -0
  120. mindroom/tools/scrapegraph.py +87 -0
  121. mindroom/tools/searxng.py +120 -0
  122. mindroom/tools/serpapi.py +55 -0
  123. mindroom/tools/serper.py +81 -0
  124. mindroom/tools/shell.py +46 -0
  125. mindroom/tools/slack.py +80 -0
  126. mindroom/tools/sleep.py +38 -0
  127. mindroom/tools/spider.py +62 -0
  128. mindroom/tools/sql.py +138 -0
  129. mindroom/tools/tavily.py +104 -0
  130. mindroom/tools/telegram.py +54 -0
  131. mindroom/tools/todoist.py +103 -0
  132. mindroom/tools/trello.py +121 -0
  133. mindroom/tools/twilio.py +97 -0
  134. mindroom/tools/web_browser_tools.py +37 -0
  135. mindroom/tools/webex.py +63 -0
  136. mindroom/tools/website.py +45 -0
  137. mindroom/tools/whatsapp.py +81 -0
  138. mindroom/tools/wikipedia.py +45 -0
  139. mindroom/tools/x.py +97 -0
  140. mindroom/tools/yfinance.py +121 -0
  141. mindroom/tools/youtube.py +81 -0
  142. mindroom/tools/zendesk.py +62 -0
  143. mindroom/tools/zep.py +107 -0
  144. mindroom/tools/zoom.py +62 -0
  145. mindroom/tools_metadata.json +7643 -0
  146. mindroom/tools_metadata.py +220 -0
  147. mindroom/topic_generator.py +153 -0
  148. mindroom/voice_handler.py +266 -0
  149. mindroom-0.1.0.dist-info/METADATA +425 -0
  150. mindroom-0.1.0.dist-info/RECORD +152 -0
  151. {mindroom-0.0.0.dist-info → mindroom-0.1.0.dist-info}/WHEEL +1 -2
  152. mindroom-0.1.0.dist-info/entry_points.txt +2 -0
  153. mindroom-0.0.0.dist-info/METADATA +0 -24
  154. mindroom-0.0.0.dist-info/RECORD +0 -4
  155. mindroom-0.0.0.dist-info/top_level.txt +0 -1
@@ -0,0 +1,318 @@
1
+ """Utilities for thread analysis and agent detection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from .constants import ROUTER_AGENT_NAME
8
+ from .matrix.identity import MatrixID, extract_agent_name
9
+ from .matrix.rooms import resolve_room_aliases
10
+
11
+ if TYPE_CHECKING:
12
+ import nio
13
+
14
+ from .config import Config
15
+
16
+
17
+ def check_agent_mentioned(event_source: dict, agent_id: MatrixID | None, config: Config) -> tuple[list[MatrixID], bool]:
18
+ """Check if an agent is mentioned in a message.
19
+
20
+ Returns (mentioned_agents, am_i_mentioned).
21
+ """
22
+ mentions = event_source.get("content", {}).get("m.mentions", {})
23
+ mentioned_agents = get_mentioned_agents(mentions, config)
24
+ am_i_mentioned = agent_id in mentioned_agents
25
+ return mentioned_agents, am_i_mentioned
26
+
27
+
28
+ def create_session_id(room_id: str, thread_id: str | None) -> str:
29
+ """Create a session ID with thread awareness."""
30
+ # Thread sessions include thread ID
31
+ return f"{room_id}:{thread_id}" if thread_id else room_id
32
+
33
+
34
+ def get_agents_in_thread(thread_history: list[dict[str, Any]], config: Config) -> list[MatrixID]:
35
+ """Get list of unique agents that have participated in thread.
36
+
37
+ Note: Router agent is excluded from the participant list as it's not
38
+ a conversation participant.
39
+
40
+ Preserves the order of first participation while preventing duplicates.
41
+ """
42
+ agents: list[MatrixID] = []
43
+ seen_ids: set[str] = set()
44
+
45
+ for msg in thread_history:
46
+ sender: str = msg.get("sender", "")
47
+ agent_name = extract_agent_name(sender, config)
48
+
49
+ # Skip router agent and invalid senders
50
+ if not agent_name or agent_name == ROUTER_AGENT_NAME:
51
+ continue
52
+
53
+ if sender not in seen_ids:
54
+ try:
55
+ matrix_id = MatrixID.parse(sender)
56
+ agents.append(matrix_id)
57
+ seen_ids.add(sender)
58
+ except ValueError:
59
+ # Skip invalid Matrix IDs
60
+ pass
61
+
62
+ return agents
63
+
64
+
65
+ def get_agent_matrix_ids_in_thread(thread_history: list[dict[str, Any]], config: Config) -> list[MatrixID]:
66
+ """Get list of unique agent Matrix IDs that have participated in thread.
67
+
68
+ Note: Router agent is excluded from the participant list as it's not
69
+ a conversation participant.
70
+
71
+ Preserves the order of first participation while preventing duplicates.
72
+
73
+ Returns:
74
+ List of MatrixID objects for agents who participated in the thread.
75
+
76
+ """
77
+ agent_ids = []
78
+ seen_ids = set()
79
+
80
+ for msg in thread_history:
81
+ sender = msg.get("sender", "")
82
+ agent_name = extract_agent_name(sender, config)
83
+
84
+ # Skip router agent and invalid senders
85
+ if not agent_name or agent_name == ROUTER_AGENT_NAME:
86
+ continue
87
+
88
+ try:
89
+ matrix_id = MatrixID.parse(sender)
90
+ if matrix_id.full_id not in seen_ids:
91
+ agent_ids.append(matrix_id)
92
+ seen_ids.add(matrix_id.full_id)
93
+ except ValueError:
94
+ # Skip invalid Matrix IDs
95
+ pass
96
+
97
+ return agent_ids
98
+
99
+
100
+ def get_mentioned_agents(mentions: dict[str, Any], config: Config) -> list[MatrixID]:
101
+ """Extract agent names from mentions."""
102
+ user_ids = mentions.get("user_ids", [])
103
+ agents: list[MatrixID] = []
104
+
105
+ for user_id in user_ids:
106
+ mid = MatrixID.parse(user_id)
107
+ if mid.agent_name(config):
108
+ agents.append(mid)
109
+
110
+ return agents
111
+
112
+
113
+ def has_user_responded_after_message(
114
+ thread_history: list[dict],
115
+ target_event_id: str,
116
+ user_id: MatrixID,
117
+ ) -> bool:
118
+ """Check if a user has sent any messages after a specific message in the thread.
119
+
120
+ Args:
121
+ thread_history: List of messages in the thread
122
+ target_event_id: The event ID to check after
123
+ user_id: The user ID to check for
124
+
125
+ Returns:
126
+ True if the user has responded after the target message
127
+
128
+ """
129
+ # Find the target message and check for user responses after it
130
+ found_target = False
131
+ for msg in thread_history:
132
+ if msg["event_id"] == target_event_id:
133
+ found_target = True
134
+ elif found_target and msg["sender"] == user_id.full_id:
135
+ return True
136
+ return False
137
+
138
+
139
+ def get_available_agents_in_room(room: nio.MatrixRoom, config: Config) -> list[MatrixID]:
140
+ """Get list of available agent MatrixIDs in a room.
141
+
142
+ Note: Router agent is excluded as it's not a regular conversation participant.
143
+ """
144
+ agents: list[MatrixID] = []
145
+
146
+ for member_id in room.users:
147
+ mid = MatrixID.parse(member_id)
148
+ agent_name = mid.agent_name(config)
149
+ # Exclude router agent
150
+ if agent_name and agent_name != ROUTER_AGENT_NAME:
151
+ agents.append(mid)
152
+
153
+ return sorted(agents, key=lambda x: x.full_id)
154
+
155
+
156
+ def get_available_agent_matrix_ids_in_room(room: nio.MatrixRoom, config: Config) -> list[MatrixID]:
157
+ """Get list of available agent Matrix IDs in a room.
158
+
159
+ Note: Router agent is excluded as it's not a regular conversation participant.
160
+
161
+ Returns:
162
+ List of MatrixID objects for agents in the room.
163
+
164
+ """
165
+ agent_ids = []
166
+
167
+ for member_id in room.users:
168
+ agent_name = extract_agent_name(member_id, config)
169
+ # Exclude router agent
170
+ if agent_name and agent_name != ROUTER_AGENT_NAME:
171
+ try:
172
+ matrix_id = MatrixID.parse(member_id)
173
+ agent_ids.append(matrix_id)
174
+ except ValueError:
175
+ # Skip invalid Matrix IDs
176
+ pass
177
+
178
+ return sorted(agent_ids, key=lambda x: x.full_id)
179
+
180
+
181
+ def get_configured_agents_for_room(room_id: str, config: Config) -> list[MatrixID]:
182
+ """Get list of agent MatrixIDs configured for a specific room.
183
+
184
+ This returns only agents that have the room in their configuration,
185
+ not just agents that happen to be present in the room.
186
+
187
+ Note: Router agent is excluded as it's not a regular conversation participant.
188
+ """
189
+ configured_agents: list[MatrixID] = []
190
+
191
+ # Check which agents should be in this room
192
+ for agent_name, agent_config in config.agents.items():
193
+ if agent_name != ROUTER_AGENT_NAME:
194
+ resolved_rooms = resolve_room_aliases(agent_config.rooms)
195
+ if room_id in resolved_rooms:
196
+ configured_agents.append(config.ids[agent_name])
197
+
198
+ return sorted(configured_agents, key=lambda x: x.full_id)
199
+
200
+
201
+ def has_any_agent_mentions_in_thread(thread_history: list[dict[str, Any]], config: Config) -> bool:
202
+ """Check if any agents are mentioned anywhere in the thread."""
203
+ for msg in thread_history:
204
+ content = msg.get("content", {})
205
+ mentions = content.get("m.mentions", {})
206
+ if get_mentioned_agents(mentions, config):
207
+ return True
208
+ return False
209
+
210
+
211
+ def get_all_mentioned_agents_in_thread(thread_history: list[dict[str, Any]], config: Config) -> list[MatrixID]:
212
+ """Get all unique agent MatrixIDs that have been mentioned anywhere in the thread.
213
+
214
+ Preserves the order of first mention while preventing duplicates.
215
+ """
216
+ mentioned_agents = []
217
+ seen_ids = set()
218
+
219
+ for msg in thread_history:
220
+ content = msg.get("content", {})
221
+ mentions = content.get("m.mentions", {})
222
+ agents = get_mentioned_agents(mentions, config)
223
+
224
+ # Add agents in order, but only if not seen before
225
+ for agent in agents:
226
+ if agent.full_id not in seen_ids:
227
+ mentioned_agents.append(agent)
228
+ seen_ids.add(agent.full_id)
229
+
230
+ return mentioned_agents
231
+
232
+
233
+ def is_authorized_sender(sender_id: str, config: Config, room_id: str) -> bool:
234
+ """Check if a sender is authorized to interact with agents.
235
+
236
+ Args:
237
+ sender_id: Matrix ID of the message sender
238
+ config: Application configuration
239
+ room_id: Room ID for permission checks
240
+
241
+ Returns:
242
+ True if the sender is authorized, False otherwise
243
+
244
+ """
245
+ # Always allow mindroom_user on the current domain
246
+ mindroom_user_id = f"@mindroom_user:{config.domain}"
247
+ if sender_id == mindroom_user_id:
248
+ return True
249
+
250
+ # Check if sender is an agent or team
251
+ agent_name = extract_agent_name(sender_id, config)
252
+ if agent_name:
253
+ # Agent is either in config.agents, config.teams, or is the router
254
+ return agent_name in config.agents or agent_name in config.teams or agent_name == ROUTER_AGENT_NAME
255
+
256
+ # Check global authorized users (they have access to all rooms)
257
+ if sender_id in config.authorization.global_users:
258
+ return True
259
+
260
+ # Check room-specific permissions
261
+ if room_id in config.authorization.room_permissions:
262
+ return sender_id in config.authorization.room_permissions[room_id]
263
+
264
+ # Use default access for rooms not explicitly configured
265
+ return config.authorization.default_room_access
266
+
267
+
268
+ def should_agent_respond(
269
+ agent_name: str,
270
+ am_i_mentioned: bool,
271
+ is_thread: bool,
272
+ room: nio.MatrixRoom,
273
+ thread_history: list[dict],
274
+ config: Config,
275
+ mentioned_agents: list[MatrixID] | None = None,
276
+ ) -> bool:
277
+ """Determine if an agent should respond to a message individually.
278
+
279
+ Team formation is handled elsewhere - this just determines individual responses.
280
+
281
+ Args:
282
+ agent_name: Name of the agent checking if it should respond
283
+ am_i_mentioned: Whether this specific agent is mentioned
284
+ is_thread: Whether the message is in a thread
285
+ room: The Matrix room object
286
+ thread_history: History of messages in the thread
287
+ config: Application configuration
288
+ mentioned_agents: List of all agent MatrixIDs mentioned in the message
289
+
290
+ """
291
+ # Always respond if mentioned
292
+ if am_i_mentioned:
293
+ return True
294
+
295
+ # Never respond if other agents are mentioned but not this one
296
+ # (User explicitly wants a different agent)
297
+ if mentioned_agents:
298
+ return False
299
+
300
+ # Non-thread messages: allow a single available agent to respond automatically
301
+ # This applies to both DM and regular rooms. Router is excluded from availability.
302
+ if not is_thread:
303
+ available_agents = get_available_agents_in_room(room, config)
304
+ return len(available_agents) == 1
305
+
306
+ agent_matrix_id = config.ids[agent_name]
307
+
308
+ # For threads, check if agents have already participated
309
+ if is_thread:
310
+ agents_in_thread = get_agents_in_thread(thread_history, config)
311
+ if agents_in_thread:
312
+ # Continue only if we're the single agent
313
+ return len(agents_in_thread) == 1 and agents_in_thread[0] == agent_matrix_id
314
+
315
+ # No agents in thread yet OR DM room without thread
316
+ # Respond if we're the only agent available
317
+ available_agents = get_available_agents_in_room(room, config)
318
+ return len(available_agents) == 1