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
@@ -25,6 +25,7 @@ if TYPE_CHECKING:
25
25
  def create_artifacts_registry(
26
26
  db: LocalDatabase | None = None,
27
27
  artifact_manager: LocalArtifactManager | None = None,
28
+ session_manager: Any | None = None,
28
29
  ) -> InternalToolRegistry:
29
30
  """
30
31
  Create an artifacts tool registry with all artifact-related tools.
@@ -32,10 +33,21 @@ def create_artifacts_registry(
32
33
  Args:
33
34
  db: LocalDatabase instance (used to create artifact_manager if not provided)
34
35
  artifact_manager: LocalArtifactManager instance
36
+ session_manager: Session manager for resolving session references
35
37
 
36
38
  Returns:
37
39
  InternalToolRegistry with artifact tools registered
38
40
  """
41
+ from gobby.utils.project_context import get_project_context
42
+
43
+ def _resolve_session_id(ref: str) -> str:
44
+ """Resolve session reference (#N, N, UUID, or prefix) to UUID."""
45
+ if session_manager is None:
46
+ return ref # No resolution available, return as-is
47
+ ctx = get_project_context()
48
+ project_id = ctx.get("id") if ctx else None
49
+ return str(session_manager.resolve_session_reference(ref, project_id))
50
+
39
51
  # Create artifact manager if not provided
40
52
  if artifact_manager is None:
41
53
  if db is None:
@@ -55,7 +67,7 @@ def create_artifacts_registry(
55
67
 
56
68
  @registry.tool(
57
69
  name="search_artifacts",
58
- description="Search artifacts by content using full-text search.",
70
+ description="Search artifacts by content using full-text search. Accepts #N, N, UUID, or prefix for session_id.",
59
71
  )
60
72
  def search_artifacts(
61
73
  query: str,
@@ -68,7 +80,7 @@ def create_artifacts_registry(
68
80
 
69
81
  Args:
70
82
  query: Search query text
71
- session_id: Optional session ID to filter by
83
+ session_id: Optional session reference (accepts #N, N, UUID, or prefix) to filter by
72
84
  artifact_type: Optional artifact type to filter by (code, diff, error, etc.)
73
85
  limit: Maximum number of results (default: 50)
74
86
 
@@ -78,10 +90,18 @@ def create_artifacts_registry(
78
90
  if not query or not query.strip():
79
91
  return {"success": True, "artifacts": [], "count": 0}
80
92
 
93
+ # Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
94
+ resolved_session_id = session_id
95
+ if session_id:
96
+ try:
97
+ resolved_session_id = _resolve_session_id(session_id)
98
+ except ValueError as e:
99
+ return {"success": False, "error": str(e), "artifacts": []}
100
+
81
101
  try:
82
102
  artifacts = _artifact_manager.search_artifacts(
83
103
  query_text=query,
84
- session_id=session_id,
104
+ session_id=resolved_session_id,
85
105
  artifact_type=artifact_type,
86
106
  limit=limit,
87
107
  )
@@ -95,7 +115,7 @@ def create_artifacts_registry(
95
115
 
96
116
  @registry.tool(
97
117
  name="list_artifacts",
98
- description="List artifacts with optional filters.",
118
+ description="List artifacts with optional filters. Accepts #N, N, UUID, or prefix for session_id.",
99
119
  )
100
120
  def list_artifacts(
101
121
  session_id: str | None = None,
@@ -107,7 +127,7 @@ def create_artifacts_registry(
107
127
  List artifacts with optional filters.
108
128
 
109
129
  Args:
110
- session_id: Optional session ID to filter by
130
+ session_id: Optional session reference (accepts #N, N, UUID, or prefix) to filter by
111
131
  artifact_type: Optional artifact type to filter by
112
132
  limit: Maximum number of results (default: 100)
113
133
  offset: Offset for pagination (default: 0)
@@ -115,9 +135,17 @@ def create_artifacts_registry(
115
135
  Returns:
116
136
  Dict with success status and list of artifacts
117
137
  """
138
+ # Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
139
+ resolved_session_id = session_id
140
+ if session_id:
141
+ try:
142
+ resolved_session_id = _resolve_session_id(session_id)
143
+ except ValueError as e:
144
+ return {"success": False, "error": str(e), "artifacts": []}
145
+
118
146
  try:
119
147
  artifacts = _artifact_manager.list_artifacts(
120
- session_id=session_id,
148
+ session_id=resolved_session_id,
121
149
  artifact_type=artifact_type,
122
150
  limit=limit,
123
151
  offset=offset,
@@ -161,7 +189,7 @@ def create_artifacts_registry(
161
189
 
162
190
  @registry.tool(
163
191
  name="get_timeline",
164
- description="Get artifacts for a session in chronological order.",
192
+ description="Get artifacts for a session in chronological order. Accepts #N, N, UUID, or prefix for session_id.",
165
193
  )
166
194
  def get_timeline(
167
195
  session_id: str | None = None,
@@ -172,7 +200,7 @@ def create_artifacts_registry(
172
200
  Get artifacts for a session in chronological order (oldest first).
173
201
 
174
202
  Args:
175
- session_id: Required session ID to get timeline for
203
+ session_id: Required session reference (accepts #N, N, UUID, or prefix) to get timeline for
176
204
  artifact_type: Optional artifact type to filter by
177
205
  limit: Maximum number of results (default: 100)
178
206
 
@@ -186,10 +214,16 @@ def create_artifacts_registry(
186
214
  "artifacts": [],
187
215
  }
188
216
 
217
+ # Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
218
+ try:
219
+ resolved_session_id = _resolve_session_id(session_id)
220
+ except ValueError as e:
221
+ return {"success": False, "error": str(e), "artifacts": []}
222
+
189
223
  try:
190
224
  # Get artifacts (list_artifacts returns newest first by default)
191
225
  artifacts = _artifact_manager.list_artifacts(
192
- session_id=session_id,
226
+ session_id=resolved_session_id,
193
227
  artifact_type=artifact_type,
194
228
  limit=limit,
195
229
  offset=0,
@@ -13,13 +13,11 @@ via the downstream proxy pattern (call_tool, list_tools, get_tool_schema).
13
13
  from __future__ import annotations
14
14
 
15
15
  import logging
16
- from pathlib import Path
17
16
  from typing import TYPE_CHECKING, Any, Literal
18
17
 
19
18
  from gobby.mcp_proxy.tools.internal import InternalToolRegistry
20
19
 
21
20
  if TYPE_CHECKING:
22
- from gobby.agents.runner import AgentRunner
23
21
  from gobby.clones.git import CloneGitManager
24
22
  from gobby.storage.clones import LocalCloneManager
25
23
 
@@ -30,7 +28,6 @@ def create_clones_registry(
30
28
  clone_storage: LocalCloneManager,
31
29
  git_manager: CloneGitManager,
32
30
  project_id: str,
33
- agent_runner: AgentRunner | None = None,
34
31
  ) -> InternalToolRegistry:
35
32
  """
36
33
  Create the gobby-clones MCP server registry.
@@ -39,7 +36,6 @@ def create_clones_registry(
39
36
  clone_storage: Clone storage manager for CRUD operations
40
37
  git_manager: Git manager for clone operations
41
38
  project_id: Default project ID for new clones
42
- agent_runner: Optional agent runner for spawning agents in clones
43
39
 
44
40
  Returns:
45
41
  InternalToolRegistry with clone management tools
@@ -519,385 +515,4 @@ def create_clones_registry(
519
515
  func=merge_clone_to_target,
520
516
  )
521
517
 
522
- # ===== spawn_agent_in_clone =====
523
- async def spawn_agent_in_clone(
524
- prompt: str,
525
- branch_name: str,
526
- parent_session_id: str | None = None,
527
- task_id: str | None = None,
528
- base_branch: str = "main",
529
- clone_path: str | None = None,
530
- mode: str = "terminal",
531
- terminal: str = "auto",
532
- provider: Literal["claude", "gemini", "codex", "antigravity"] = "claude",
533
- model: str | None = None,
534
- workflow: str | None = None,
535
- timeout: float = 120.0,
536
- max_turns: int = 10,
537
- ) -> dict[str, Any]:
538
- """
539
- Create a clone (if needed) and spawn an agent in it.
540
-
541
- This combines clone creation with agent spawning for isolated development.
542
- Unlike worktrees, clones are full repository copies that can be worked on
543
- independently without affecting the main repository.
544
-
545
- Args:
546
- prompt: The task/prompt for the agent.
547
- branch_name: Name for the branch in the clone.
548
- parent_session_id: Parent session ID for context (required).
549
- task_id: Optional task ID to link to this clone.
550
- base_branch: Branch to clone from (default: main).
551
- clone_path: Optional custom path for the clone.
552
- mode: Execution mode (terminal, embedded, headless).
553
- terminal: Terminal for terminal/embedded modes (auto, ghostty, etc.).
554
- provider: LLM provider (claude, gemini, etc.).
555
- model: Optional model override.
556
- workflow: Workflow name to execute.
557
- timeout: Execution timeout in seconds (default: 120).
558
- max_turns: Maximum turns (default: 10).
559
-
560
- Returns:
561
- Dict with clone_id, run_id, and status.
562
- """
563
- if agent_runner is None:
564
- return {
565
- "success": False,
566
- "error": "Agent runner not configured. Cannot spawn agent.",
567
- }
568
-
569
- if parent_session_id is None:
570
- return {
571
- "success": False,
572
- "error": "parent_session_id is required for agent spawning.",
573
- }
574
-
575
- # Handle mode aliases and validation
576
- if mode == "interactive":
577
- mode = "terminal"
578
-
579
- valid_modes = ["terminal", "embedded", "headless"]
580
- if mode not in valid_modes:
581
- return {
582
- "success": False,
583
- "error": (
584
- f"Invalid mode '{mode}'. Must be one of: {', '.join(valid_modes)}. "
585
- f"Note: 'in_process' mode is not supported for spawn_agent_in_clone."
586
- ),
587
- }
588
-
589
- # Normalize terminal parameter to lowercase
590
- if isinstance(terminal, str):
591
- terminal = terminal.lower()
592
-
593
- # Check spawn depth limit
594
- can_spawn, reason, _depth = agent_runner.can_spawn(parent_session_id)
595
- if not can_spawn:
596
- return {
597
- "success": False,
598
- "error": reason,
599
- }
600
-
601
- # Check if clone already exists for this branch
602
- existing = clone_storage.get_by_branch(project_id, branch_name)
603
- if existing:
604
- clone = existing
605
- logger.info(f"Using existing clone for branch '{branch_name}'")
606
- else:
607
- # Get remote URL
608
- remote_url = git_manager.get_remote_url() if git_manager else None
609
- if not remote_url:
610
- return {
611
- "success": False,
612
- "error": "No remote URL available. Cannot create clone.",
613
- }
614
-
615
- # Generate clone path if not provided
616
- if clone_path is None:
617
- import platform
618
- import tempfile
619
-
620
- if platform.system() == "Windows":
621
- base = Path(tempfile.gettempdir()) / "gobby-clones"
622
- else:
623
- # nosec B108: /tmp is intentional for clones - they're temporary
624
- base = Path("/tmp").resolve() / "gobby-clones" # nosec B108
625
- base.mkdir(parents=True, exist_ok=True)
626
- safe_branch = branch_name.replace("/", "-")
627
- clone_path = str(base / f"{project_id}-{safe_branch}")
628
-
629
- # Create the clone
630
- result = git_manager.shallow_clone(
631
- remote_url=remote_url,
632
- clone_path=clone_path,
633
- branch=base_branch,
634
- depth=1,
635
- )
636
-
637
- if not result.success:
638
- return {
639
- "success": False,
640
- "error": f"Clone failed: {result.error or result.message}",
641
- }
642
-
643
- # Store clone record
644
- clone = clone_storage.create(
645
- project_id=project_id,
646
- branch_name=branch_name,
647
- clone_path=clone_path,
648
- base_branch=base_branch,
649
- task_id=task_id,
650
- remote_url=remote_url,
651
- )
652
-
653
- # Import AgentConfig and get machine_id
654
- from gobby.agents.runner import AgentConfig
655
- from gobby.utils.machine_id import get_machine_id
656
-
657
- machine_id = get_machine_id()
658
-
659
- # Create agent config
660
- config = AgentConfig(
661
- prompt=prompt,
662
- parent_session_id=parent_session_id,
663
- project_id=project_id,
664
- machine_id=machine_id,
665
- source=provider,
666
- workflow=workflow,
667
- task=task_id,
668
- session_context="summary_markdown",
669
- mode=mode,
670
- terminal=terminal,
671
- provider=provider,
672
- model=model,
673
- max_turns=max_turns,
674
- timeout=timeout,
675
- project_path=clone.clone_path,
676
- )
677
-
678
- # Prepare the run
679
- from gobby.llm.executor import AgentResult
680
-
681
- prepare_result = agent_runner.prepare_run(config)
682
- if isinstance(prepare_result, AgentResult):
683
- return {
684
- "success": False,
685
- "clone_id": clone.id,
686
- "clone_path": clone.clone_path,
687
- "branch_name": clone.branch_name,
688
- "error": prepare_result.error,
689
- }
690
-
691
- context = prepare_result
692
- if context.session is None or context.run is None:
693
- return {
694
- "success": False,
695
- "clone_id": clone.id,
696
- "error": "Internal error: context missing session or run",
697
- }
698
-
699
- child_session = context.session
700
- agent_run = context.run
701
-
702
- # Claim clone for the child session
703
- clone_storage.claim(clone.id, child_session.id)
704
-
705
- # Build enhanced prompt with clone context
706
- context_lines = [
707
- "## CRITICAL: Clone Context",
708
- "You are working in an ISOLATED git clone, NOT the main repository.",
709
- "",
710
- f"**Your workspace:** {clone.clone_path}",
711
- f"**Your branch:** {clone.branch_name}",
712
- ]
713
- if task_id:
714
- context_lines.append(f"**Your task:** {task_id}")
715
- context_lines.extend(
716
- [
717
- "",
718
- "**IMPORTANT RULES:**",
719
- f"1. ALL file operations must be within {clone.clone_path}",
720
- "2. Do NOT access the main repository",
721
- "3. Run `pwd` to verify your location before any file operations",
722
- f"4. Commit to YOUR branch ({clone.branch_name})",
723
- "5. When your assigned task is complete, STOP - do not claim other tasks",
724
- "",
725
- "---",
726
- "",
727
- ]
728
- )
729
- enhanced_prompt = "\n".join(context_lines) + prompt
730
-
731
- # Spawn based on mode
732
- if mode == "terminal":
733
- from gobby.agents.spawn import TerminalSpawner
734
-
735
- terminal_spawner = TerminalSpawner()
736
- terminal_result = terminal_spawner.spawn_agent(
737
- cli=provider,
738
- cwd=clone.clone_path,
739
- session_id=child_session.id,
740
- parent_session_id=parent_session_id,
741
- agent_run_id=agent_run.id,
742
- project_id=project_id,
743
- workflow_name=workflow,
744
- agent_depth=child_session.agent_depth,
745
- max_agent_depth=agent_runner._child_session_manager.max_agent_depth,
746
- terminal=terminal,
747
- prompt=enhanced_prompt,
748
- )
749
-
750
- if not terminal_result.success:
751
- return {
752
- "success": False,
753
- "clone_id": clone.id,
754
- "clone_path": clone.clone_path,
755
- "branch_name": clone.branch_name,
756
- "run_id": agent_run.id,
757
- "child_session_id": child_session.id,
758
- "error": terminal_result.error or terminal_result.message,
759
- }
760
-
761
- return {
762
- "success": True,
763
- "clone_id": clone.id,
764
- "clone_path": clone.clone_path,
765
- "branch_name": clone.branch_name,
766
- "run_id": agent_run.id,
767
- "child_session_id": child_session.id,
768
- "status": "pending",
769
- "message": f"Agent spawned in {terminal_result.terminal_type} (PID: {terminal_result.pid})",
770
- "terminal_type": terminal_result.terminal_type,
771
- "pid": terminal_result.pid,
772
- }
773
-
774
- elif mode == "embedded":
775
- from gobby.agents.spawn import EmbeddedSpawner
776
-
777
- embedded_spawner = EmbeddedSpawner()
778
- embedded_result = embedded_spawner.spawn_agent(
779
- cli=provider,
780
- cwd=clone.clone_path,
781
- session_id=child_session.id,
782
- parent_session_id=parent_session_id,
783
- agent_run_id=agent_run.id,
784
- project_id=project_id,
785
- workflow_name=workflow,
786
- agent_depth=child_session.agent_depth,
787
- max_agent_depth=agent_runner._child_session_manager.max_agent_depth,
788
- prompt=enhanced_prompt,
789
- )
790
-
791
- return {
792
- "success": embedded_result.success,
793
- "clone_id": clone.id,
794
- "clone_path": clone.clone_path,
795
- "branch_name": clone.branch_name,
796
- "run_id": agent_run.id,
797
- "child_session_id": child_session.id,
798
- "status": "pending" if embedded_result.success else "error",
799
- "error": embedded_result.error if not embedded_result.success else None,
800
- }
801
-
802
- else: # headless
803
- from gobby.agents.spawn import HeadlessSpawner
804
-
805
- headless_spawner = HeadlessSpawner()
806
- headless_result = headless_spawner.spawn_agent(
807
- cli=provider,
808
- cwd=clone.clone_path,
809
- session_id=child_session.id,
810
- parent_session_id=parent_session_id,
811
- agent_run_id=agent_run.id,
812
- project_id=project_id,
813
- workflow_name=workflow,
814
- agent_depth=child_session.agent_depth,
815
- max_agent_depth=agent_runner._child_session_manager.max_agent_depth,
816
- prompt=enhanced_prompt,
817
- )
818
-
819
- return {
820
- "success": headless_result.success,
821
- "clone_id": clone.id,
822
- "clone_path": clone.clone_path,
823
- "branch_name": clone.branch_name,
824
- "run_id": agent_run.id,
825
- "child_session_id": child_session.id,
826
- "status": "pending" if headless_result.success else "error",
827
- "pid": headless_result.pid if headless_result.success else None,
828
- "error": headless_result.error if not headless_result.success else None,
829
- }
830
-
831
- registry.register(
832
- name="spawn_agent_in_clone",
833
- description="Create a clone and spawn an agent to work in it",
834
- input_schema={
835
- "type": "object",
836
- "properties": {
837
- "prompt": {
838
- "type": "string",
839
- "description": "The task/prompt for the agent",
840
- },
841
- "branch_name": {
842
- "type": "string",
843
- "description": "Name for the branch in the clone",
844
- },
845
- "parent_session_id": {
846
- "type": "string",
847
- "description": "Parent session ID for context (required)",
848
- },
849
- "task_id": {
850
- "type": "string",
851
- "description": "Optional task ID to link to this clone",
852
- },
853
- "base_branch": {
854
- "type": "string",
855
- "description": "Branch to clone from",
856
- "default": "main",
857
- },
858
- "clone_path": {
859
- "type": "string",
860
- "description": "Optional custom path for the clone",
861
- },
862
- "mode": {
863
- "type": "string",
864
- "description": "Execution mode",
865
- "enum": ["terminal", "embedded", "headless"],
866
- "default": "terminal",
867
- },
868
- "terminal": {
869
- "type": "string",
870
- "description": "Terminal type for terminal/embedded modes",
871
- "default": "auto",
872
- },
873
- "provider": {
874
- "type": "string",
875
- "description": "LLM provider",
876
- "enum": ["claude", "gemini", "codex", "antigravity"],
877
- "default": "claude",
878
- },
879
- "model": {
880
- "type": "string",
881
- "description": "Optional model override",
882
- },
883
- "workflow": {
884
- "type": "string",
885
- "description": "Workflow name to execute",
886
- },
887
- "timeout": {
888
- "type": "number",
889
- "description": "Execution timeout in seconds",
890
- "default": 120.0,
891
- },
892
- "max_turns": {
893
- "type": "integer",
894
- "description": "Maximum turns",
895
- "default": 10,
896
- },
897
- },
898
- "required": ["prompt", "branch_name", "parent_session_id"],
899
- },
900
- func=spawn_agent_in_clone,
901
- )
902
-
903
518
  return registry
@@ -255,7 +255,7 @@ def create_memory_registry(
255
255
  name="get_related_memories",
256
256
  description="Get memories related to a specific memory via cross-references.",
257
257
  )
258
- def get_related_memories(
258
+ async def get_related_memories(
259
259
  memory_id: str,
260
260
  limit: int = 5,
261
261
  min_similarity: float = 0.0,
@@ -272,7 +272,7 @@ def create_memory_registry(
272
272
  min_similarity: Minimum similarity threshold (0.0-1.0)
273
273
  """
274
274
  try:
275
- memories = memory_manager.get_related(
275
+ memories = await memory_manager.get_related(
276
276
  memory_id=memory_id,
277
277
  limit=limit,
278
278
  min_similarity=min_similarity,
@@ -0,0 +1,14 @@
1
+ """Session tools package.
2
+
3
+ This package provides MCP tools for session management. Re-exports maintain
4
+ backwards compatibility with the original session_messages.py module.
5
+
6
+ Public API:
7
+ - create_session_messages_registry: Factory function to create the session tool registry
8
+ """
9
+
10
+ from gobby.mcp_proxy.tools.sessions._factory import create_session_messages_registry
11
+
12
+ __all__ = [
13
+ "create_session_messages_registry",
14
+ ]