gobby 0.2.6__py3-none-any.whl → 0.2.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 (198) hide show
  1. gobby/__init__.py +1 -1
  2. gobby/adapters/__init__.py +2 -1
  3. gobby/adapters/claude_code.py +96 -35
  4. gobby/adapters/codex_impl/__init__.py +28 -0
  5. gobby/adapters/codex_impl/adapter.py +722 -0
  6. gobby/adapters/codex_impl/client.py +679 -0
  7. gobby/adapters/codex_impl/protocol.py +20 -0
  8. gobby/adapters/codex_impl/types.py +68 -0
  9. gobby/adapters/gemini.py +140 -38
  10. gobby/agents/definitions.py +11 -1
  11. gobby/agents/isolation.py +525 -0
  12. gobby/agents/registry.py +11 -0
  13. gobby/agents/sandbox.py +261 -0
  14. gobby/agents/session.py +1 -0
  15. gobby/agents/spawn.py +42 -287
  16. gobby/agents/spawn_executor.py +415 -0
  17. gobby/agents/spawners/__init__.py +24 -0
  18. gobby/agents/spawners/command_builder.py +189 -0
  19. gobby/agents/spawners/embedded.py +21 -2
  20. gobby/agents/spawners/headless.py +21 -2
  21. gobby/agents/spawners/macos.py +26 -1
  22. gobby/agents/spawners/prompt_manager.py +125 -0
  23. gobby/cli/__init__.py +0 -2
  24. gobby/cli/install.py +4 -4
  25. gobby/cli/installers/claude.py +6 -0
  26. gobby/cli/installers/gemini.py +6 -0
  27. gobby/cli/installers/shared.py +103 -4
  28. gobby/cli/memory.py +185 -0
  29. gobby/cli/sessions.py +1 -1
  30. gobby/cli/utils.py +9 -2
  31. gobby/clones/git.py +177 -0
  32. gobby/config/__init__.py +12 -97
  33. gobby/config/app.py +10 -94
  34. gobby/config/extensions.py +2 -2
  35. gobby/config/features.py +7 -130
  36. gobby/config/skills.py +31 -0
  37. gobby/config/tasks.py +4 -28
  38. gobby/hooks/__init__.py +0 -13
  39. gobby/hooks/event_handlers.py +150 -8
  40. gobby/hooks/hook_manager.py +21 -3
  41. gobby/hooks/plugins.py +1 -1
  42. gobby/hooks/webhooks.py +1 -1
  43. gobby/install/gemini/hooks/hook_dispatcher.py +74 -15
  44. gobby/llm/resolver.py +3 -2
  45. gobby/mcp_proxy/importer.py +62 -4
  46. gobby/mcp_proxy/instructions.py +4 -2
  47. gobby/mcp_proxy/registries.py +22 -8
  48. gobby/mcp_proxy/services/recommendation.py +43 -11
  49. gobby/mcp_proxy/tools/agent_messaging.py +93 -44
  50. gobby/mcp_proxy/tools/agents.py +76 -740
  51. gobby/mcp_proxy/tools/artifacts.py +43 -9
  52. gobby/mcp_proxy/tools/clones.py +0 -385
  53. gobby/mcp_proxy/tools/memory.py +2 -2
  54. gobby/mcp_proxy/tools/sessions/__init__.py +14 -0
  55. gobby/mcp_proxy/tools/sessions/_commits.py +239 -0
  56. gobby/mcp_proxy/tools/sessions/_crud.py +253 -0
  57. gobby/mcp_proxy/tools/sessions/_factory.py +63 -0
  58. gobby/mcp_proxy/tools/sessions/_handoff.py +503 -0
  59. gobby/mcp_proxy/tools/sessions/_messages.py +166 -0
  60. gobby/mcp_proxy/tools/skills/__init__.py +14 -29
  61. gobby/mcp_proxy/tools/spawn_agent.py +455 -0
  62. gobby/mcp_proxy/tools/tasks/_context.py +18 -0
  63. gobby/mcp_proxy/tools/tasks/_crud.py +13 -6
  64. gobby/mcp_proxy/tools/tasks/_lifecycle.py +79 -30
  65. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +1 -1
  66. gobby/mcp_proxy/tools/tasks/_session.py +22 -7
  67. gobby/mcp_proxy/tools/workflows.py +84 -34
  68. gobby/mcp_proxy/tools/worktrees.py +32 -350
  69. gobby/memory/extractor.py +15 -1
  70. gobby/memory/ingestion/__init__.py +5 -0
  71. gobby/memory/ingestion/multimodal.py +221 -0
  72. gobby/memory/manager.py +62 -283
  73. gobby/memory/search/__init__.py +10 -0
  74. gobby/memory/search/coordinator.py +248 -0
  75. gobby/memory/services/__init__.py +5 -0
  76. gobby/memory/services/crossref.py +142 -0
  77. gobby/prompts/loader.py +5 -2
  78. gobby/runner.py +13 -0
  79. gobby/servers/http.py +1 -4
  80. gobby/servers/routes/admin.py +14 -0
  81. gobby/servers/routes/mcp/endpoints/__init__.py +61 -0
  82. gobby/servers/routes/mcp/endpoints/discovery.py +405 -0
  83. gobby/servers/routes/mcp/endpoints/execution.py +568 -0
  84. gobby/servers/routes/mcp/endpoints/registry.py +378 -0
  85. gobby/servers/routes/mcp/endpoints/server.py +304 -0
  86. gobby/servers/routes/mcp/hooks.py +51 -4
  87. gobby/servers/routes/mcp/tools.py +48 -1506
  88. gobby/servers/websocket.py +57 -1
  89. gobby/sessions/analyzer.py +2 -2
  90. gobby/sessions/lifecycle.py +1 -1
  91. gobby/sessions/manager.py +9 -0
  92. gobby/sessions/processor.py +10 -0
  93. gobby/sessions/transcripts/base.py +1 -0
  94. gobby/sessions/transcripts/claude.py +15 -5
  95. gobby/sessions/transcripts/gemini.py +100 -34
  96. gobby/skills/parser.py +30 -2
  97. gobby/storage/database.py +9 -2
  98. gobby/storage/memories.py +32 -21
  99. gobby/storage/migrations.py +174 -368
  100. gobby/storage/sessions.py +45 -7
  101. gobby/storage/skills.py +80 -7
  102. gobby/storage/tasks/_lifecycle.py +18 -3
  103. gobby/sync/memories.py +1 -1
  104. gobby/tasks/external_validator.py +1 -1
  105. gobby/tasks/validation.py +22 -20
  106. gobby/tools/summarizer.py +91 -10
  107. gobby/utils/project_context.py +2 -3
  108. gobby/utils/status.py +13 -0
  109. gobby/workflows/actions.py +221 -1217
  110. gobby/workflows/artifact_actions.py +31 -0
  111. gobby/workflows/autonomous_actions.py +11 -0
  112. gobby/workflows/context_actions.py +50 -1
  113. gobby/workflows/detection_helpers.py +38 -24
  114. gobby/workflows/enforcement/__init__.py +47 -0
  115. gobby/workflows/enforcement/blocking.py +281 -0
  116. gobby/workflows/enforcement/commit_policy.py +283 -0
  117. gobby/workflows/enforcement/handlers.py +269 -0
  118. gobby/workflows/enforcement/task_policy.py +542 -0
  119. gobby/workflows/engine.py +93 -0
  120. gobby/workflows/evaluator.py +110 -0
  121. gobby/workflows/git_utils.py +106 -0
  122. gobby/workflows/hooks.py +41 -0
  123. gobby/workflows/llm_actions.py +30 -0
  124. gobby/workflows/mcp_actions.py +20 -1
  125. gobby/workflows/memory_actions.py +91 -0
  126. gobby/workflows/safe_evaluator.py +191 -0
  127. gobby/workflows/session_actions.py +44 -0
  128. gobby/workflows/state_actions.py +60 -1
  129. gobby/workflows/stop_signal_actions.py +55 -0
  130. gobby/workflows/summary_actions.py +217 -51
  131. gobby/workflows/task_sync_actions.py +347 -0
  132. gobby/workflows/todo_actions.py +34 -1
  133. gobby/workflows/webhook_actions.py +185 -0
  134. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/METADATA +6 -1
  135. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/RECORD +139 -163
  136. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/WHEEL +1 -1
  137. gobby/adapters/codex.py +0 -1332
  138. gobby/cli/tui.py +0 -34
  139. gobby/install/claude/commands/gobby/bug.md +0 -51
  140. gobby/install/claude/commands/gobby/chore.md +0 -51
  141. gobby/install/claude/commands/gobby/epic.md +0 -52
  142. gobby/install/claude/commands/gobby/eval.md +0 -235
  143. gobby/install/claude/commands/gobby/feat.md +0 -49
  144. gobby/install/claude/commands/gobby/nit.md +0 -52
  145. gobby/install/claude/commands/gobby/ref.md +0 -52
  146. gobby/mcp_proxy/tools/session_messages.py +0 -1055
  147. gobby/prompts/defaults/expansion/system.md +0 -119
  148. gobby/prompts/defaults/expansion/user.md +0 -48
  149. gobby/prompts/defaults/external_validation/agent.md +0 -72
  150. gobby/prompts/defaults/external_validation/external.md +0 -63
  151. gobby/prompts/defaults/external_validation/spawn.md +0 -83
  152. gobby/prompts/defaults/external_validation/system.md +0 -6
  153. gobby/prompts/defaults/features/import_mcp.md +0 -22
  154. gobby/prompts/defaults/features/import_mcp_github.md +0 -17
  155. gobby/prompts/defaults/features/import_mcp_search.md +0 -16
  156. gobby/prompts/defaults/features/recommend_tools.md +0 -32
  157. gobby/prompts/defaults/features/recommend_tools_hybrid.md +0 -35
  158. gobby/prompts/defaults/features/recommend_tools_llm.md +0 -30
  159. gobby/prompts/defaults/features/server_description.md +0 -20
  160. gobby/prompts/defaults/features/server_description_system.md +0 -6
  161. gobby/prompts/defaults/features/task_description.md +0 -31
  162. gobby/prompts/defaults/features/task_description_system.md +0 -6
  163. gobby/prompts/defaults/features/tool_summary.md +0 -17
  164. gobby/prompts/defaults/features/tool_summary_system.md +0 -6
  165. gobby/prompts/defaults/handoff/compact.md +0 -63
  166. gobby/prompts/defaults/handoff/session_end.md +0 -57
  167. gobby/prompts/defaults/memory/extract.md +0 -61
  168. gobby/prompts/defaults/research/step.md +0 -58
  169. gobby/prompts/defaults/validation/criteria.md +0 -47
  170. gobby/prompts/defaults/validation/validate.md +0 -38
  171. gobby/storage/migrations_legacy.py +0 -1359
  172. gobby/tui/__init__.py +0 -5
  173. gobby/tui/api_client.py +0 -278
  174. gobby/tui/app.py +0 -329
  175. gobby/tui/screens/__init__.py +0 -25
  176. gobby/tui/screens/agents.py +0 -333
  177. gobby/tui/screens/chat.py +0 -450
  178. gobby/tui/screens/dashboard.py +0 -377
  179. gobby/tui/screens/memory.py +0 -305
  180. gobby/tui/screens/metrics.py +0 -231
  181. gobby/tui/screens/orchestrator.py +0 -903
  182. gobby/tui/screens/sessions.py +0 -412
  183. gobby/tui/screens/tasks.py +0 -440
  184. gobby/tui/screens/workflows.py +0 -289
  185. gobby/tui/screens/worktrees.py +0 -174
  186. gobby/tui/widgets/__init__.py +0 -21
  187. gobby/tui/widgets/chat.py +0 -210
  188. gobby/tui/widgets/conductor.py +0 -104
  189. gobby/tui/widgets/menu.py +0 -132
  190. gobby/tui/widgets/message_panel.py +0 -160
  191. gobby/tui/widgets/review_gate.py +0 -224
  192. gobby/tui/widgets/task_tree.py +0 -99
  193. gobby/tui/widgets/token_budget.py +0 -166
  194. gobby/tui/ws_client.py +0 -258
  195. gobby/workflows/task_enforcement_actions.py +0 -1343
  196. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/entry_points.txt +0 -0
  197. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/licenses/LICENSE.md +0 -0
  198. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/top_level.txt +0 -0
@@ -6,9 +6,10 @@ Provides messaging capabilities between parent and child sessions:
6
6
  - send_to_child: Parent sends message to a specific child
7
7
  - poll_messages: Check for incoming messages
8
8
  - mark_message_read: Mark a message as read
9
- - broadcast_to_children: Send message to all running children
9
+ - broadcast_to_children: Send message to all children (active in database)
10
10
 
11
- These tools resolve session relationships from RunningAgentRegistry.
11
+ These tools resolve session relationships from the database (LocalSessionManager),
12
+ which is the authoritative source for parent_session_id relationships.
12
13
  """
13
14
 
14
15
  from __future__ import annotations
@@ -17,9 +18,9 @@ import logging
17
18
  from typing import TYPE_CHECKING, Any
18
19
 
19
20
  if TYPE_CHECKING:
20
- from gobby.agents.registry import RunningAgentRegistry
21
21
  from gobby.mcp_proxy.tools.internal import InternalToolRegistry
22
22
  from gobby.storage.inter_session_messages import InterSessionMessageManager
23
+ from gobby.storage.sessions import LocalSessionManager
23
24
 
24
25
  logger = logging.getLogger(__name__)
25
26
 
@@ -27,7 +28,7 @@ logger = logging.getLogger(__name__)
27
28
  def add_messaging_tools(
28
29
  registry: InternalToolRegistry,
29
30
  message_manager: InterSessionMessageManager,
30
- agent_registry: RunningAgentRegistry,
31
+ session_manager: LocalSessionManager,
31
32
  ) -> None:
32
33
  """
33
34
  Add inter-agent messaging tools to an existing registry.
@@ -35,12 +36,20 @@ def add_messaging_tools(
35
36
  Args:
36
37
  registry: The InternalToolRegistry to add tools to (typically gobby-agents)
37
38
  message_manager: InterSessionMessageManager for persisting messages
38
- agent_registry: RunningAgentRegistry for resolving parent/child relationships
39
+ session_manager: LocalSessionManager for resolving parent/child relationships
40
+ (database is the authoritative source for session relationships)
39
41
  """
42
+ from gobby.utils.project_context import get_project_context
43
+
44
+ def _resolve_session_id(ref: str) -> str:
45
+ """Resolve session reference (#N, N, UUID, or prefix) to UUID."""
46
+ project_ctx = get_project_context()
47
+ project_id = project_ctx.get("id") if project_ctx else None
48
+ return session_manager.resolve_session_reference(ref, project_id)
40
49
 
41
50
  @registry.tool(
42
51
  name="send_to_parent",
43
- description="Send a message from a child session to its parent session.",
52
+ description="Send a message from a child session to its parent session. Accepts #N, N, UUID, or prefix for session_id.",
44
53
  )
45
54
  async def send_to_parent(
46
55
  session_id: str,
@@ -54,7 +63,7 @@ def add_messaging_tools(
54
63
  or requests back to its parent session.
55
64
 
56
65
  Args:
57
- session_id: The current (child) session ID
66
+ session_id: Session reference (accepts #N, N, UUID, or prefix) for the current (child) session
58
67
  content: Message content to send
59
68
  priority: Message priority ("normal" or "urgent")
60
69
 
@@ -62,30 +71,41 @@ def add_messaging_tools(
62
71
  Dict with success status and message details
63
72
  """
64
73
  try:
65
- # Find the running agent to get parent relationship
66
- agent = agent_registry.get_by_session(session_id)
67
- if not agent:
74
+ # Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
75
+ try:
76
+ resolved_session_id = _resolve_session_id(session_id)
77
+ except ValueError as e:
78
+ return {"success": False, "error": str(e)}
79
+
80
+ # Look up session in database (authoritative source for relationships)
81
+ session = session_manager.get(resolved_session_id)
82
+ if not session:
68
83
  return {
69
84
  "success": False,
70
- "error": f"Session {session_id} not found in running agent registry",
85
+ "error": f"Session {resolved_session_id} not found",
71
86
  }
72
87
 
73
- parent_session_id = agent.parent_session_id
88
+ parent_session_id = session.parent_session_id
74
89
  if not parent_session_id:
75
90
  return {
76
91
  "success": False,
77
- "error": "No parent session found for this agent",
92
+ "error": "No parent session for this session",
78
93
  }
79
94
 
80
95
  # Create the message
81
96
  msg = message_manager.create_message(
82
- from_session=session_id,
97
+ from_session=resolved_session_id,
83
98
  to_session=parent_session_id,
84
99
  content=content,
85
100
  priority=priority,
86
101
  )
87
102
 
88
- logger.info(f"Message sent from {session_id} to parent {parent_session_id}: {msg.id}")
103
+ logger.info(
104
+ "Message sent from %s to parent %s: %s",
105
+ resolved_session_id,
106
+ parent_session_id,
107
+ msg.id,
108
+ )
89
109
 
90
110
  return {
91
111
  "success": True,
@@ -94,7 +114,7 @@ def add_messaging_tools(
94
114
  }
95
115
 
96
116
  except Exception as e:
97
- logger.error(f"Failed to send message to parent: {e}")
117
+ logger.error("Failed to send message to parent: %s", e)
98
118
  return {
99
119
  "success": False,
100
120
  "error": str(e),
@@ -102,7 +122,7 @@ def add_messaging_tools(
102
122
 
103
123
  @registry.tool(
104
124
  name="send_to_child",
105
- description="Send a message from a parent session to a specific child session.",
125
+ description="Send a message from a parent session to a specific child session. Accepts #N, N, UUID, or prefix for session IDs.",
106
126
  )
107
127
  async def send_to_child(
108
128
  parent_session_id: str,
@@ -117,8 +137,8 @@ def add_messaging_tools(
117
137
  updates, or coordination messages to a spawned child.
118
138
 
119
139
  Args:
120
- parent_session_id: The parent session ID (sender)
121
- child_session_id: The child session ID (recipient)
140
+ parent_session_id: Session reference (accepts #N, N, UUID, or prefix) for the parent (sender)
141
+ child_session_id: Session reference (accepts #N, N, UUID, or prefix) for the child (recipient)
122
142
  content: Message content to send
123
143
  priority: Message priority ("normal" or "urgent")
124
144
 
@@ -126,33 +146,43 @@ def add_messaging_tools(
126
146
  Dict with success status and message details
127
147
  """
128
148
  try:
129
- # Verify the child exists and belongs to this parent
130
- child_agent = agent_registry.get_by_session(child_session_id)
131
- if not child_agent:
149
+ # Resolve session IDs to UUIDs (accepts #N, N, UUID, or prefix)
150
+ try:
151
+ resolved_parent_id = _resolve_session_id(parent_session_id)
152
+ resolved_child_id = _resolve_session_id(child_session_id)
153
+ except ValueError as e:
154
+ return {"success": False, "error": str(e)}
155
+
156
+ # Verify the child exists in database and belongs to this parent
157
+ child_session = session_manager.get(resolved_child_id)
158
+ if not child_session:
132
159
  return {
133
160
  "success": False,
134
- "error": f"Child session {child_session_id} not found in running agent registry",
161
+ "error": f"Child session {resolved_child_id} not found",
135
162
  }
136
163
 
137
- if child_agent.parent_session_id != parent_session_id:
164
+ if child_session.parent_session_id != resolved_parent_id:
138
165
  return {
139
166
  "success": False,
140
167
  "error": (
141
- f"Session {child_session_id} is not a child of {parent_session_id}. "
142
- f"Actual parent: {child_agent.parent_session_id}"
168
+ f"Session {resolved_child_id} is not a child of {resolved_parent_id}. "
169
+ f"Actual parent: {child_session.parent_session_id}"
143
170
  ),
144
171
  }
145
172
 
146
173
  # Create the message
147
174
  msg = message_manager.create_message(
148
- from_session=parent_session_id,
149
- to_session=child_session_id,
175
+ from_session=resolved_parent_id,
176
+ to_session=resolved_child_id,
150
177
  content=content,
151
178
  priority=priority,
152
179
  )
153
180
 
154
181
  logger.info(
155
- f"Message sent from {parent_session_id} to child {child_session_id}: {msg.id}"
182
+ "Message sent from %s to child %s: %s",
183
+ resolved_parent_id,
184
+ resolved_child_id,
185
+ msg.id,
156
186
  )
157
187
 
158
188
  return {
@@ -161,7 +191,7 @@ def add_messaging_tools(
161
191
  }
162
192
 
163
193
  except Exception as e:
164
- logger.error(f"Failed to send message to child: {e}")
194
+ logger.error("Failed to send message to child: %s", e)
165
195
  return {
166
196
  "success": False,
167
197
  "error": str(e),
@@ -169,7 +199,7 @@ def add_messaging_tools(
169
199
 
170
200
  @registry.tool(
171
201
  name="poll_messages",
172
- description="Poll for messages sent to this session.",
202
+ description="Poll for messages sent to this session. Accepts #N, N, UUID, or prefix for session_id.",
173
203
  )
174
204
  async def poll_messages(
175
205
  session_id: str,
@@ -182,15 +212,21 @@ def add_messaging_tools(
182
212
  By default, returns only unread messages.
183
213
 
184
214
  Args:
185
- session_id: The session ID to check messages for
215
+ session_id: Session reference (accepts #N, N, UUID, or prefix) to check messages for
186
216
  unread_only: If True, only return unread messages (default: True)
187
217
 
188
218
  Returns:
189
219
  Dict with success status and list of messages
190
220
  """
191
221
  try:
222
+ # Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
223
+ try:
224
+ resolved_session_id = _resolve_session_id(session_id)
225
+ except ValueError as e:
226
+ return {"success": False, "error": str(e)}
227
+
192
228
  messages = message_manager.get_messages(
193
- to_session=session_id,
229
+ to_session=resolved_session_id,
194
230
  unread_only=unread_only,
195
231
  )
196
232
 
@@ -248,7 +284,7 @@ def add_messaging_tools(
248
284
 
249
285
  @registry.tool(
250
286
  name="broadcast_to_children",
251
- description="Broadcast a message to all running child sessions.",
287
+ description="Broadcast a message to all active child sessions. Accepts #N, N, UUID, or prefix for session_id.",
252
288
  )
253
289
  async def broadcast_to_children(
254
290
  parent_session_id: str,
@@ -256,13 +292,14 @@ def add_messaging_tools(
256
292
  priority: str = "normal",
257
293
  ) -> dict[str, Any]:
258
294
  """
259
- Broadcast a message to all running children.
295
+ Broadcast a message to all active children.
260
296
 
261
- Send the same message to all child sessions spawned by this parent.
297
+ Send the same message to all child sessions spawned by this parent
298
+ that are currently active in the database.
262
299
  Useful for coordination or shutdown signals.
263
300
 
264
301
  Args:
265
- parent_session_id: The parent session ID
302
+ parent_session_id: Session reference (accepts #N, N, UUID, or prefix) for the parent
266
303
  content: Message content to broadcast
267
304
  priority: Message priority ("normal" or "urgent")
268
305
 
@@ -270,13 +307,22 @@ def add_messaging_tools(
270
307
  Dict with success status and count of messages sent
271
308
  """
272
309
  try:
273
- children = agent_registry.list_by_parent(parent_session_id)
310
+ # Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
311
+ try:
312
+ resolved_parent_id = _resolve_session_id(parent_session_id)
313
+ except ValueError as e:
314
+ return {"success": False, "error": str(e)}
315
+
316
+ # Get all children from database
317
+ all_children = session_manager.find_children(resolved_parent_id)
318
+ # Filter to active children only
319
+ children = [c for c in all_children if c.status == "active"]
274
320
 
275
321
  if not children:
276
322
  return {
277
323
  "success": True,
278
324
  "sent_count": 0,
279
- "message": "No running children found",
325
+ "message": "No active children found",
280
326
  }
281
327
 
282
328
  sent_count = 0
@@ -285,14 +331,14 @@ def add_messaging_tools(
285
331
  for child in children:
286
332
  try:
287
333
  message_manager.create_message(
288
- from_session=parent_session_id,
289
- to_session=child.session_id,
334
+ from_session=resolved_parent_id,
335
+ to_session=child.id,
290
336
  content=content,
291
337
  priority=priority,
292
338
  )
293
339
  sent_count += 1
294
340
  except Exception as e:
295
- errors.append(f"{child.session_id}: {e}")
341
+ errors.append(f"{child.id}: {e}")
296
342
 
297
343
  result: dict[str, Any] = {
298
344
  "success": True,
@@ -304,13 +350,16 @@ def add_messaging_tools(
304
350
  result["errors"] = errors
305
351
 
306
352
  logger.info(
307
- f"Broadcast from {parent_session_id} sent to {sent_count}/{len(children)} children"
353
+ "Broadcast from %s sent to %d/%d children",
354
+ resolved_parent_id,
355
+ sent_count,
356
+ len(children),
308
357
  )
309
358
 
310
359
  return result
311
360
 
312
361
  except Exception as e:
313
- logger.error(f"Failed to broadcast to children: {e}")
362
+ logger.error("Failed to broadcast to children: %s", e)
314
363
  return {
315
364
  "success": False,
316
365
  "error": str(e),