gobby 0.2.5__py3-none-any.whl → 0.2.6__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 (148) hide show
  1. gobby/adapters/claude_code.py +13 -4
  2. gobby/adapters/codex.py +43 -3
  3. gobby/agents/runner.py +8 -0
  4. gobby/cli/__init__.py +6 -0
  5. gobby/cli/clones.py +419 -0
  6. gobby/cli/conductor.py +266 -0
  7. gobby/cli/installers/antigravity.py +3 -9
  8. gobby/cli/installers/claude.py +9 -9
  9. gobby/cli/installers/codex.py +2 -8
  10. gobby/cli/installers/gemini.py +2 -8
  11. gobby/cli/installers/shared.py +71 -8
  12. gobby/cli/skills.py +858 -0
  13. gobby/cli/tasks/ai.py +0 -440
  14. gobby/cli/tasks/crud.py +44 -6
  15. gobby/cli/tasks/main.py +0 -4
  16. gobby/cli/tui.py +2 -2
  17. gobby/cli/utils.py +3 -3
  18. gobby/clones/__init__.py +13 -0
  19. gobby/clones/git.py +547 -0
  20. gobby/conductor/__init__.py +16 -0
  21. gobby/conductor/alerts.py +135 -0
  22. gobby/conductor/loop.py +164 -0
  23. gobby/conductor/monitors/__init__.py +11 -0
  24. gobby/conductor/monitors/agents.py +116 -0
  25. gobby/conductor/monitors/tasks.py +155 -0
  26. gobby/conductor/pricing.py +234 -0
  27. gobby/conductor/token_tracker.py +160 -0
  28. gobby/config/app.py +63 -1
  29. gobby/config/search.py +110 -0
  30. gobby/config/servers.py +1 -1
  31. gobby/config/skills.py +43 -0
  32. gobby/config/tasks.py +6 -14
  33. gobby/hooks/event_handlers.py +145 -2
  34. gobby/hooks/hook_manager.py +48 -2
  35. gobby/hooks/skill_manager.py +130 -0
  36. gobby/install/claude/hooks/hook_dispatcher.py +4 -4
  37. gobby/install/codex/hooks/hook_dispatcher.py +1 -1
  38. gobby/install/gemini/hooks/hook_dispatcher.py +87 -12
  39. gobby/llm/claude.py +22 -34
  40. gobby/llm/claude_executor.py +46 -256
  41. gobby/llm/codex_executor.py +59 -291
  42. gobby/llm/executor.py +21 -0
  43. gobby/llm/gemini.py +134 -110
  44. gobby/llm/litellm_executor.py +143 -6
  45. gobby/llm/resolver.py +95 -33
  46. gobby/mcp_proxy/instructions.py +54 -0
  47. gobby/mcp_proxy/models.py +15 -0
  48. gobby/mcp_proxy/registries.py +68 -5
  49. gobby/mcp_proxy/server.py +33 -3
  50. gobby/mcp_proxy/services/tool_proxy.py +81 -1
  51. gobby/mcp_proxy/stdio.py +2 -1
  52. gobby/mcp_proxy/tools/__init__.py +0 -2
  53. gobby/mcp_proxy/tools/agent_messaging.py +317 -0
  54. gobby/mcp_proxy/tools/clones.py +903 -0
  55. gobby/mcp_proxy/tools/memory.py +1 -24
  56. gobby/mcp_proxy/tools/metrics.py +65 -1
  57. gobby/mcp_proxy/tools/orchestration/__init__.py +3 -0
  58. gobby/mcp_proxy/tools/orchestration/cleanup.py +151 -0
  59. gobby/mcp_proxy/tools/orchestration/wait.py +467 -0
  60. gobby/mcp_proxy/tools/session_messages.py +1 -2
  61. gobby/mcp_proxy/tools/skills/__init__.py +631 -0
  62. gobby/mcp_proxy/tools/task_orchestration.py +7 -0
  63. gobby/mcp_proxy/tools/task_readiness.py +14 -0
  64. gobby/mcp_proxy/tools/task_sync.py +1 -1
  65. gobby/mcp_proxy/tools/tasks/_context.py +0 -20
  66. gobby/mcp_proxy/tools/tasks/_crud.py +91 -4
  67. gobby/mcp_proxy/tools/tasks/_expansion.py +348 -0
  68. gobby/mcp_proxy/tools/tasks/_factory.py +6 -16
  69. gobby/mcp_proxy/tools/tasks/_lifecycle.py +60 -29
  70. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +18 -29
  71. gobby/mcp_proxy/tools/workflows.py +1 -1
  72. gobby/mcp_proxy/tools/worktrees.py +5 -0
  73. gobby/memory/backends/__init__.py +6 -1
  74. gobby/memory/backends/mem0.py +6 -1
  75. gobby/memory/extractor.py +477 -0
  76. gobby/memory/manager.py +11 -2
  77. gobby/prompts/defaults/handoff/compact.md +63 -0
  78. gobby/prompts/defaults/handoff/session_end.md +57 -0
  79. gobby/prompts/defaults/memory/extract.md +61 -0
  80. gobby/runner.py +37 -16
  81. gobby/search/__init__.py +48 -6
  82. gobby/search/backends/__init__.py +159 -0
  83. gobby/search/backends/embedding.py +225 -0
  84. gobby/search/embeddings.py +238 -0
  85. gobby/search/models.py +148 -0
  86. gobby/search/unified.py +496 -0
  87. gobby/servers/http.py +23 -8
  88. gobby/servers/routes/admin.py +280 -0
  89. gobby/servers/routes/mcp/tools.py +241 -52
  90. gobby/servers/websocket.py +2 -2
  91. gobby/sessions/analyzer.py +2 -0
  92. gobby/sessions/transcripts/base.py +1 -0
  93. gobby/sessions/transcripts/claude.py +64 -5
  94. gobby/skills/__init__.py +91 -0
  95. gobby/skills/loader.py +685 -0
  96. gobby/skills/manager.py +384 -0
  97. gobby/skills/parser.py +258 -0
  98. gobby/skills/search.py +463 -0
  99. gobby/skills/sync.py +119 -0
  100. gobby/skills/updater.py +385 -0
  101. gobby/skills/validator.py +368 -0
  102. gobby/storage/clones.py +378 -0
  103. gobby/storage/database.py +1 -1
  104. gobby/storage/memories.py +43 -13
  105. gobby/storage/migrations.py +180 -6
  106. gobby/storage/sessions.py +73 -0
  107. gobby/storage/skills.py +749 -0
  108. gobby/storage/tasks/_crud.py +4 -4
  109. gobby/storage/tasks/_lifecycle.py +41 -6
  110. gobby/storage/tasks/_manager.py +14 -5
  111. gobby/storage/tasks/_models.py +8 -3
  112. gobby/sync/memories.py +39 -4
  113. gobby/sync/tasks.py +83 -6
  114. gobby/tasks/__init__.py +1 -2
  115. gobby/tasks/validation.py +24 -15
  116. gobby/tui/api_client.py +4 -7
  117. gobby/tui/app.py +5 -3
  118. gobby/tui/screens/orchestrator.py +1 -2
  119. gobby/tui/screens/tasks.py +2 -4
  120. gobby/tui/ws_client.py +1 -1
  121. gobby/utils/daemon_client.py +2 -2
  122. gobby/workflows/actions.py +84 -2
  123. gobby/workflows/context_actions.py +43 -0
  124. gobby/workflows/detection_helpers.py +115 -31
  125. gobby/workflows/engine.py +13 -2
  126. gobby/workflows/lifecycle_evaluator.py +29 -1
  127. gobby/workflows/loader.py +19 -6
  128. gobby/workflows/memory_actions.py +74 -0
  129. gobby/workflows/summary_actions.py +17 -0
  130. gobby/workflows/task_enforcement_actions.py +448 -6
  131. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/METADATA +82 -21
  132. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/RECORD +136 -107
  133. gobby/install/codex/prompts/forget.md +0 -7
  134. gobby/install/codex/prompts/memories.md +0 -7
  135. gobby/install/codex/prompts/recall.md +0 -7
  136. gobby/install/codex/prompts/remember.md +0 -13
  137. gobby/llm/gemini_executor.py +0 -339
  138. gobby/mcp_proxy/tools/task_expansion.py +0 -591
  139. gobby/tasks/context.py +0 -747
  140. gobby/tasks/criteria.py +0 -342
  141. gobby/tasks/expansion.py +0 -626
  142. gobby/tasks/prompts/expand.py +0 -327
  143. gobby/tasks/research.py +0 -421
  144. gobby/tasks/tdd.py +0 -352
  145. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/WHEEL +0 -0
  146. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/entry_points.txt +0 -0
  147. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/licenses/LICENSE.md +0 -0
  148. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,467 @@
1
+ """Task orchestration tools: wait (wait_for_task, wait_for_any_task, wait_for_all_tasks).
2
+
3
+ Provides blocking wait operations for task completion with timeout support.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import asyncio
9
+ import logging
10
+ import time
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ from gobby.mcp_proxy.tools.internal import InternalToolRegistry
14
+ from gobby.storage.tasks import TaskNotFoundError
15
+
16
+ if TYPE_CHECKING:
17
+ from gobby.storage.tasks import LocalTaskManager
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Default timeout and poll interval
22
+ DEFAULT_TIMEOUT = 300.0 # 5 minutes
23
+ DEFAULT_POLL_INTERVAL = 5.0 # 5 seconds
24
+
25
+
26
+ def register_wait(
27
+ registry: InternalToolRegistry,
28
+ task_manager: LocalTaskManager,
29
+ ) -> None:
30
+ """
31
+ Register wait tools for task completion.
32
+
33
+ Args:
34
+ registry: The tool registry to add tools to
35
+ task_manager: Task manager for checking task status
36
+ """
37
+
38
+ def _resolve_task_id(task_ref: str) -> str:
39
+ """Resolve a task reference to its UUID."""
40
+ from gobby.mcp_proxy.tools.tasks import resolve_task_id_for_mcp
41
+
42
+ return resolve_task_id_for_mcp(task_manager, task_ref)
43
+
44
+ def _is_task_complete(task_id: str) -> tuple[bool, dict[str, Any] | None]:
45
+ """
46
+ Check if a task is complete.
47
+
48
+ Returns:
49
+ Tuple of (is_complete, task_info_dict or None)
50
+ """
51
+ task = task_manager.get_task(task_id)
52
+ if task is None:
53
+ return False, None
54
+
55
+ task_info = {
56
+ "id": task.id,
57
+ "seq_num": task.seq_num,
58
+ "title": task.title,
59
+ "status": task.status,
60
+ "closed_at": task.closed_at,
61
+ }
62
+
63
+ # Consider task complete if status is "closed" or "review"
64
+ # (review tasks have completed their work, just awaiting human approval)
65
+ is_complete = task.status in ("closed", "review")
66
+ return is_complete, task_info
67
+
68
+ async def wait_for_task(
69
+ task_id: str,
70
+ timeout: float = DEFAULT_TIMEOUT,
71
+ poll_interval: float = DEFAULT_POLL_INTERVAL,
72
+ ) -> dict[str, Any]:
73
+ """
74
+ Wait for a single task to complete.
75
+
76
+ Blocks until the task reaches "closed" or "review" status, or timeout expires.
77
+
78
+ Args:
79
+ task_id: Task reference (#N, N, path, or UUID)
80
+ timeout: Maximum wait time in seconds (default: 300)
81
+ poll_interval: Time between status checks in seconds (default: 5)
82
+
83
+ Returns:
84
+ Dict with:
85
+ - success: Whether the operation succeeded
86
+ - completed: Whether the task completed
87
+ - timed_out: Whether timeout was reached
88
+ - task: Task info dict (if found)
89
+ - wait_time: How long we waited
90
+ """
91
+ # Validate poll_interval
92
+ if poll_interval <= 0:
93
+ poll_interval = DEFAULT_POLL_INTERVAL
94
+
95
+ start_time = time.monotonic()
96
+
97
+ try:
98
+ resolved_id = _resolve_task_id(task_id)
99
+ except (TaskNotFoundError, ValueError) as e:
100
+ return {
101
+ "success": False,
102
+ "error": f"Task not found: {task_id} ({e})",
103
+ }
104
+ except Exception as e:
105
+ return {
106
+ "success": False,
107
+ "error": f"Failed to resolve task: {task_id} ({e})",
108
+ }
109
+
110
+ # Check initial state
111
+ try:
112
+ is_complete, task_info = _is_task_complete(resolved_id)
113
+ except Exception as e:
114
+ return {
115
+ "success": False,
116
+ "error": f"Failed to check task status: {e}",
117
+ }
118
+
119
+ if task_info is None:
120
+ return {
121
+ "success": False,
122
+ "error": f"Task not found: {task_id}",
123
+ }
124
+
125
+ if is_complete:
126
+ return {
127
+ "success": True,
128
+ "completed": True,
129
+ "timed_out": False,
130
+ "task": task_info,
131
+ "wait_time": 0.0,
132
+ }
133
+
134
+ # Poll until complete or timeout
135
+ while True:
136
+ elapsed = time.monotonic() - start_time
137
+
138
+ if elapsed >= timeout:
139
+ # Re-fetch latest task state before returning timeout
140
+ try:
141
+ _, task_info = _is_task_complete(resolved_id)
142
+ except Exception as e:
143
+ logger.warning(f"Error fetching final task status on timeout: {e}")
144
+ return {
145
+ "success": True,
146
+ "completed": False,
147
+ "timed_out": True,
148
+ "task": task_info,
149
+ "wait_time": elapsed,
150
+ }
151
+
152
+ await asyncio.sleep(poll_interval)
153
+
154
+ try:
155
+ is_complete, task_info = _is_task_complete(resolved_id)
156
+ except Exception as e:
157
+ logger.warning(f"Error checking task status: {e}")
158
+ continue
159
+
160
+ if is_complete:
161
+ return {
162
+ "success": True,
163
+ "completed": True,
164
+ "timed_out": False,
165
+ "task": task_info,
166
+ "wait_time": time.monotonic() - start_time,
167
+ }
168
+
169
+ registry.register(
170
+ name="wait_for_task",
171
+ description=(
172
+ "Wait for a single task to complete. "
173
+ "Blocks until task reaches 'closed' or 'review' status, or timeout expires."
174
+ ),
175
+ input_schema={
176
+ "type": "object",
177
+ "properties": {
178
+ "task_id": {
179
+ "type": "string",
180
+ "description": "Task reference: #N, N (seq_num), path (1.2.3), or UUID",
181
+ },
182
+ "timeout": {
183
+ "type": "number",
184
+ "description": f"Maximum wait time in seconds (default: {DEFAULT_TIMEOUT})",
185
+ },
186
+ "poll_interval": {
187
+ "type": "number",
188
+ "description": f"Time between status checks in seconds (default: {DEFAULT_POLL_INTERVAL})",
189
+ },
190
+ },
191
+ "required": ["task_id"],
192
+ },
193
+ func=wait_for_task,
194
+ )
195
+
196
+ async def wait_for_any_task(
197
+ task_ids: list[str],
198
+ timeout: float = DEFAULT_TIMEOUT,
199
+ poll_interval: float = DEFAULT_POLL_INTERVAL,
200
+ ) -> dict[str, Any]:
201
+ """
202
+ Wait for any one of multiple tasks to complete.
203
+
204
+ Blocks until at least one task reaches "closed" or "review" status, or timeout expires.
205
+
206
+ Args:
207
+ task_ids: List of task references (#N, N, path, or UUID)
208
+ timeout: Maximum wait time in seconds (default: 300)
209
+ poll_interval: Time between status checks in seconds (default: 5)
210
+
211
+ Returns:
212
+ Dict with:
213
+ - success: Whether the operation succeeded
214
+ - completed_task_id: ID of the first completed task (or None)
215
+ - timed_out: Whether timeout was reached
216
+ - wait_time: How long we waited
217
+ """
218
+ if not task_ids:
219
+ return {
220
+ "success": False,
221
+ "error": "No task IDs provided - task_ids list is empty",
222
+ }
223
+
224
+ # Validate poll_interval
225
+ if poll_interval <= 0:
226
+ poll_interval = DEFAULT_POLL_INTERVAL
227
+
228
+ start_time = time.monotonic()
229
+
230
+ # Resolve all task IDs upfront
231
+ resolved_ids = []
232
+ for task_ref in task_ids:
233
+ try:
234
+ resolved_id = _resolve_task_id(task_ref)
235
+ resolved_ids.append(resolved_id)
236
+ except (TaskNotFoundError, ValueError) as e:
237
+ logger.warning(f"Could not resolve task {task_ref}: {e}")
238
+ # Continue with other tasks
239
+
240
+ if not resolved_ids:
241
+ return {
242
+ "success": False,
243
+ "error": "None of the provided task IDs could be resolved",
244
+ }
245
+
246
+ # Check if any are already complete
247
+ for resolved_id in resolved_ids:
248
+ try:
249
+ is_complete, task_info = _is_task_complete(resolved_id)
250
+ if is_complete:
251
+ return {
252
+ "success": True,
253
+ "completed_task_id": resolved_id,
254
+ "task": task_info,
255
+ "timed_out": False,
256
+ "wait_time": 0.0,
257
+ }
258
+ except Exception as e:
259
+ logger.warning(f"Error checking task {resolved_id}: {e}")
260
+
261
+ # Poll until one completes or timeout
262
+ while True:
263
+ elapsed = time.monotonic() - start_time
264
+
265
+ if elapsed >= timeout:
266
+ return {
267
+ "success": True,
268
+ "completed_task_id": None,
269
+ "timed_out": True,
270
+ "wait_time": elapsed,
271
+ }
272
+
273
+ await asyncio.sleep(poll_interval)
274
+
275
+ for resolved_id in resolved_ids:
276
+ try:
277
+ is_complete, task_info = _is_task_complete(resolved_id)
278
+ if is_complete:
279
+ return {
280
+ "success": True,
281
+ "completed_task_id": resolved_id,
282
+ "task": task_info,
283
+ "timed_out": False,
284
+ "wait_time": time.monotonic() - start_time,
285
+ }
286
+ except Exception as e:
287
+ logger.warning(f"Error checking task {resolved_id}: {e}")
288
+
289
+ registry.register(
290
+ name="wait_for_any_task",
291
+ description=(
292
+ "Wait for any one of multiple tasks to complete. "
293
+ "Returns as soon as the first task reaches 'closed' or 'review' status."
294
+ ),
295
+ input_schema={
296
+ "type": "object",
297
+ "properties": {
298
+ "task_ids": {
299
+ "type": "array",
300
+ "items": {"type": "string"},
301
+ "description": "List of task references",
302
+ },
303
+ "timeout": {
304
+ "type": "number",
305
+ "description": f"Maximum wait time in seconds (default: {DEFAULT_TIMEOUT})",
306
+ },
307
+ "poll_interval": {
308
+ "type": "number",
309
+ "description": f"Time between status checks in seconds (default: {DEFAULT_POLL_INTERVAL})",
310
+ },
311
+ },
312
+ "required": ["task_ids"],
313
+ },
314
+ func=wait_for_any_task,
315
+ )
316
+
317
+ async def wait_for_all_tasks(
318
+ task_ids: list[str],
319
+ timeout: float = DEFAULT_TIMEOUT,
320
+ poll_interval: float = DEFAULT_POLL_INTERVAL,
321
+ ) -> dict[str, Any]:
322
+ """
323
+ Wait for all tasks to complete.
324
+
325
+ Blocks until all tasks reach "closed" or "review" status, or timeout expires.
326
+
327
+ Args:
328
+ task_ids: List of task references (#N, N, path, or UUID)
329
+ timeout: Maximum wait time in seconds (default: 300)
330
+ poll_interval: Time between status checks in seconds (default: 5)
331
+
332
+ Returns:
333
+ Dict with:
334
+ - success: Whether the operation succeeded
335
+ - all_completed: Whether all tasks completed
336
+ - completed_count: Number of completed tasks
337
+ - pending_count: Number of still-pending tasks
338
+ - timed_out: Whether timeout was reached
339
+ - completed_tasks: List of completed task IDs
340
+ - pending_tasks: List of pending task IDs
341
+ - wait_time: How long we waited
342
+ """
343
+ if not task_ids:
344
+ # Empty list is vacuously true - all (zero) tasks are complete
345
+ return {
346
+ "success": True,
347
+ "all_completed": True,
348
+ "completed_count": 0,
349
+ "pending_count": 0,
350
+ "timed_out": False,
351
+ "completed_tasks": [],
352
+ "pending_tasks": [],
353
+ "wait_time": 0.0,
354
+ }
355
+
356
+ # Validate poll_interval
357
+ if poll_interval <= 0:
358
+ poll_interval = DEFAULT_POLL_INTERVAL
359
+
360
+ start_time = time.monotonic()
361
+
362
+ # Resolve all task IDs upfront
363
+ resolved_ids = []
364
+ for task_ref in task_ids:
365
+ try:
366
+ resolved_id = _resolve_task_id(task_ref)
367
+ resolved_ids.append(resolved_id)
368
+ except (TaskNotFoundError, ValueError) as e:
369
+ logger.warning(f"Could not resolve task {task_ref}: {e}")
370
+
371
+ if not resolved_ids:
372
+ return {
373
+ "success": False,
374
+ "error": "None of the provided task IDs could be resolved",
375
+ }
376
+
377
+ def check_all_complete() -> tuple[list[str], list[str]]:
378
+ """Check which tasks are complete. Returns (completed, pending)."""
379
+ completed = []
380
+ pending = []
381
+ for resolved_id in resolved_ids:
382
+ try:
383
+ is_complete, _ = _is_task_complete(resolved_id)
384
+ if is_complete:
385
+ completed.append(resolved_id)
386
+ else:
387
+ pending.append(resolved_id)
388
+ except Exception as e:
389
+ logger.warning(f"Error checking task {resolved_id}: {e}")
390
+ pending.append(resolved_id) # Assume not complete on error
391
+ return completed, pending
392
+
393
+ # Check initial state
394
+ completed, pending = check_all_complete()
395
+
396
+ if not pending:
397
+ return {
398
+ "success": True,
399
+ "all_completed": True,
400
+ "completed_count": len(completed),
401
+ "pending_count": 0,
402
+ "timed_out": False,
403
+ "completed_tasks": completed,
404
+ "pending_tasks": [],
405
+ "wait_time": 0.0,
406
+ }
407
+
408
+ # Poll until all complete or timeout
409
+ while True:
410
+ elapsed = time.monotonic() - start_time
411
+
412
+ if elapsed >= timeout:
413
+ completed, pending = check_all_complete()
414
+ return {
415
+ "success": True,
416
+ "all_completed": False,
417
+ "completed_count": len(completed),
418
+ "pending_count": len(pending),
419
+ "timed_out": True,
420
+ "completed_tasks": completed,
421
+ "pending_tasks": pending,
422
+ "wait_time": elapsed,
423
+ }
424
+
425
+ await asyncio.sleep(poll_interval)
426
+
427
+ completed, pending = check_all_complete()
428
+
429
+ if not pending:
430
+ return {
431
+ "success": True,
432
+ "all_completed": True,
433
+ "completed_count": len(completed),
434
+ "pending_count": 0,
435
+ "timed_out": False,
436
+ "completed_tasks": completed,
437
+ "pending_tasks": [],
438
+ "wait_time": time.monotonic() - start_time,
439
+ }
440
+
441
+ registry.register(
442
+ name="wait_for_all_tasks",
443
+ description=(
444
+ "Wait for all tasks to complete. "
445
+ "Blocks until all tasks reach 'closed' or 'review' status, or timeout expires."
446
+ ),
447
+ input_schema={
448
+ "type": "object",
449
+ "properties": {
450
+ "task_ids": {
451
+ "type": "array",
452
+ "items": {"type": "string"},
453
+ "description": "List of task references",
454
+ },
455
+ "timeout": {
456
+ "type": "number",
457
+ "description": f"Maximum wait time in seconds (default: {DEFAULT_TIMEOUT})",
458
+ },
459
+ "poll_interval": {
460
+ "type": "number",
461
+ "description": f"Time between status checks in seconds (default: {DEFAULT_POLL_INTERVAL})",
462
+ },
463
+ },
464
+ "required": ["task_ids"],
465
+ },
466
+ func=wait_for_all_tasks,
467
+ )
@@ -733,10 +733,9 @@ DO NOT use list_sessions to find your session - it won't work with multiple acti
733
733
  return {
734
734
  "found": True,
735
735
  "session_id": session.id,
736
- "external_id": session.external_id,
737
- "source": session.source,
738
736
  "project_id": session.project_id,
739
737
  "status": session.status,
738
+ "agent_run_id": session.agent_run_id,
740
739
  }
741
740
 
742
741
  @registry.tool(