gobby 0.2.5__py3-none-any.whl → 0.2.7__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.
- gobby/__init__.py +1 -1
- gobby/adapters/__init__.py +2 -1
- gobby/adapters/claude_code.py +13 -4
- gobby/adapters/codex_impl/__init__.py +28 -0
- gobby/adapters/codex_impl/adapter.py +722 -0
- gobby/adapters/codex_impl/client.py +679 -0
- gobby/adapters/codex_impl/protocol.py +20 -0
- gobby/adapters/codex_impl/types.py +68 -0
- gobby/agents/definitions.py +11 -1
- gobby/agents/isolation.py +395 -0
- gobby/agents/runner.py +8 -0
- gobby/agents/sandbox.py +261 -0
- gobby/agents/spawn.py +42 -287
- gobby/agents/spawn_executor.py +385 -0
- gobby/agents/spawners/__init__.py +24 -0
- gobby/agents/spawners/command_builder.py +189 -0
- gobby/agents/spawners/embedded.py +21 -2
- gobby/agents/spawners/headless.py +21 -2
- gobby/agents/spawners/prompt_manager.py +125 -0
- gobby/cli/__init__.py +6 -0
- gobby/cli/clones.py +419 -0
- gobby/cli/conductor.py +266 -0
- gobby/cli/install.py +4 -4
- gobby/cli/installers/antigravity.py +3 -9
- gobby/cli/installers/claude.py +15 -9
- gobby/cli/installers/codex.py +2 -8
- gobby/cli/installers/gemini.py +8 -8
- gobby/cli/installers/shared.py +175 -13
- gobby/cli/sessions.py +1 -1
- gobby/cli/skills.py +858 -0
- gobby/cli/tasks/ai.py +0 -440
- gobby/cli/tasks/crud.py +44 -6
- gobby/cli/tasks/main.py +0 -4
- gobby/cli/tui.py +2 -2
- gobby/cli/utils.py +12 -5
- gobby/clones/__init__.py +13 -0
- gobby/clones/git.py +547 -0
- gobby/conductor/__init__.py +16 -0
- gobby/conductor/alerts.py +135 -0
- gobby/conductor/loop.py +164 -0
- gobby/conductor/monitors/__init__.py +11 -0
- gobby/conductor/monitors/agents.py +116 -0
- gobby/conductor/monitors/tasks.py +155 -0
- gobby/conductor/pricing.py +234 -0
- gobby/conductor/token_tracker.py +160 -0
- gobby/config/__init__.py +12 -97
- gobby/config/app.py +69 -91
- gobby/config/extensions.py +2 -2
- gobby/config/features.py +7 -130
- gobby/config/search.py +110 -0
- gobby/config/servers.py +1 -1
- gobby/config/skills.py +43 -0
- gobby/config/tasks.py +9 -41
- gobby/hooks/__init__.py +0 -13
- gobby/hooks/event_handlers.py +188 -2
- gobby/hooks/hook_manager.py +50 -4
- gobby/hooks/plugins.py +1 -1
- gobby/hooks/skill_manager.py +130 -0
- gobby/hooks/webhooks.py +1 -1
- gobby/install/claude/hooks/hook_dispatcher.py +4 -4
- gobby/install/codex/hooks/hook_dispatcher.py +1 -1
- gobby/install/gemini/hooks/hook_dispatcher.py +87 -12
- gobby/llm/claude.py +22 -34
- gobby/llm/claude_executor.py +46 -256
- gobby/llm/codex_executor.py +59 -291
- gobby/llm/executor.py +21 -0
- gobby/llm/gemini.py +134 -110
- gobby/llm/litellm_executor.py +143 -6
- gobby/llm/resolver.py +98 -35
- gobby/mcp_proxy/importer.py +62 -4
- gobby/mcp_proxy/instructions.py +56 -0
- gobby/mcp_proxy/models.py +15 -0
- gobby/mcp_proxy/registries.py +68 -8
- gobby/mcp_proxy/server.py +33 -3
- gobby/mcp_proxy/services/recommendation.py +43 -11
- gobby/mcp_proxy/services/tool_proxy.py +81 -1
- gobby/mcp_proxy/stdio.py +2 -1
- gobby/mcp_proxy/tools/__init__.py +0 -2
- gobby/mcp_proxy/tools/agent_messaging.py +317 -0
- gobby/mcp_proxy/tools/agents.py +31 -731
- gobby/mcp_proxy/tools/clones.py +518 -0
- gobby/mcp_proxy/tools/memory.py +3 -26
- gobby/mcp_proxy/tools/metrics.py +65 -1
- gobby/mcp_proxy/tools/orchestration/__init__.py +3 -0
- gobby/mcp_proxy/tools/orchestration/cleanup.py +151 -0
- gobby/mcp_proxy/tools/orchestration/wait.py +467 -0
- gobby/mcp_proxy/tools/sessions/__init__.py +14 -0
- gobby/mcp_proxy/tools/sessions/_commits.py +232 -0
- gobby/mcp_proxy/tools/sessions/_crud.py +253 -0
- gobby/mcp_proxy/tools/sessions/_factory.py +63 -0
- gobby/mcp_proxy/tools/sessions/_handoff.py +499 -0
- gobby/mcp_proxy/tools/sessions/_messages.py +138 -0
- gobby/mcp_proxy/tools/skills/__init__.py +616 -0
- gobby/mcp_proxy/tools/spawn_agent.py +417 -0
- gobby/mcp_proxy/tools/task_orchestration.py +7 -0
- gobby/mcp_proxy/tools/task_readiness.py +14 -0
- gobby/mcp_proxy/tools/task_sync.py +1 -1
- gobby/mcp_proxy/tools/tasks/_context.py +0 -20
- gobby/mcp_proxy/tools/tasks/_crud.py +91 -4
- gobby/mcp_proxy/tools/tasks/_expansion.py +348 -0
- gobby/mcp_proxy/tools/tasks/_factory.py +6 -16
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +110 -45
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +18 -29
- gobby/mcp_proxy/tools/workflows.py +1 -1
- gobby/mcp_proxy/tools/worktrees.py +0 -338
- gobby/memory/backends/__init__.py +6 -1
- gobby/memory/backends/mem0.py +6 -1
- gobby/memory/extractor.py +477 -0
- gobby/memory/ingestion/__init__.py +5 -0
- gobby/memory/ingestion/multimodal.py +221 -0
- gobby/memory/manager.py +73 -285
- gobby/memory/search/__init__.py +10 -0
- gobby/memory/search/coordinator.py +248 -0
- gobby/memory/services/__init__.py +5 -0
- gobby/memory/services/crossref.py +142 -0
- gobby/prompts/loader.py +5 -2
- gobby/runner.py +37 -16
- gobby/search/__init__.py +48 -6
- gobby/search/backends/__init__.py +159 -0
- gobby/search/backends/embedding.py +225 -0
- gobby/search/embeddings.py +238 -0
- gobby/search/models.py +148 -0
- gobby/search/unified.py +496 -0
- gobby/servers/http.py +24 -12
- gobby/servers/routes/admin.py +294 -0
- gobby/servers/routes/mcp/endpoints/__init__.py +61 -0
- gobby/servers/routes/mcp/endpoints/discovery.py +405 -0
- gobby/servers/routes/mcp/endpoints/execution.py +568 -0
- gobby/servers/routes/mcp/endpoints/registry.py +378 -0
- gobby/servers/routes/mcp/endpoints/server.py +304 -0
- gobby/servers/routes/mcp/hooks.py +1 -1
- gobby/servers/routes/mcp/tools.py +48 -1317
- gobby/servers/websocket.py +2 -2
- gobby/sessions/analyzer.py +2 -0
- gobby/sessions/lifecycle.py +1 -1
- gobby/sessions/processor.py +10 -0
- gobby/sessions/transcripts/base.py +2 -0
- gobby/sessions/transcripts/claude.py +79 -10
- gobby/skills/__init__.py +91 -0
- gobby/skills/loader.py +685 -0
- gobby/skills/manager.py +384 -0
- gobby/skills/parser.py +286 -0
- gobby/skills/search.py +463 -0
- gobby/skills/sync.py +119 -0
- gobby/skills/updater.py +385 -0
- gobby/skills/validator.py +368 -0
- gobby/storage/clones.py +378 -0
- gobby/storage/database.py +1 -1
- gobby/storage/memories.py +43 -13
- gobby/storage/migrations.py +162 -201
- gobby/storage/sessions.py +116 -7
- gobby/storage/skills.py +782 -0
- gobby/storage/tasks/_crud.py +4 -4
- gobby/storage/tasks/_lifecycle.py +57 -7
- gobby/storage/tasks/_manager.py +14 -5
- gobby/storage/tasks/_models.py +8 -3
- gobby/sync/memories.py +40 -5
- gobby/sync/tasks.py +83 -6
- gobby/tasks/__init__.py +1 -2
- gobby/tasks/external_validator.py +1 -1
- gobby/tasks/validation.py +46 -35
- gobby/tools/summarizer.py +91 -10
- gobby/tui/api_client.py +4 -7
- gobby/tui/app.py +5 -3
- gobby/tui/screens/orchestrator.py +1 -2
- gobby/tui/screens/tasks.py +2 -4
- gobby/tui/ws_client.py +1 -1
- gobby/utils/daemon_client.py +2 -2
- gobby/utils/project_context.py +2 -3
- gobby/utils/status.py +13 -0
- gobby/workflows/actions.py +221 -1135
- gobby/workflows/artifact_actions.py +31 -0
- gobby/workflows/autonomous_actions.py +11 -0
- gobby/workflows/context_actions.py +93 -1
- gobby/workflows/detection_helpers.py +115 -31
- gobby/workflows/enforcement/__init__.py +47 -0
- gobby/workflows/enforcement/blocking.py +269 -0
- gobby/workflows/enforcement/commit_policy.py +283 -0
- gobby/workflows/enforcement/handlers.py +269 -0
- gobby/workflows/{task_enforcement_actions.py → enforcement/task_policy.py} +29 -388
- gobby/workflows/engine.py +13 -2
- gobby/workflows/git_utils.py +106 -0
- gobby/workflows/lifecycle_evaluator.py +29 -1
- gobby/workflows/llm_actions.py +30 -0
- gobby/workflows/loader.py +19 -6
- gobby/workflows/mcp_actions.py +20 -1
- gobby/workflows/memory_actions.py +154 -0
- gobby/workflows/safe_evaluator.py +183 -0
- gobby/workflows/session_actions.py +44 -0
- gobby/workflows/state_actions.py +60 -1
- gobby/workflows/stop_signal_actions.py +55 -0
- gobby/workflows/summary_actions.py +111 -1
- gobby/workflows/task_sync_actions.py +347 -0
- gobby/workflows/todo_actions.py +34 -1
- gobby/workflows/webhook_actions.py +185 -0
- {gobby-0.2.5.dist-info → gobby-0.2.7.dist-info}/METADATA +87 -21
- {gobby-0.2.5.dist-info → gobby-0.2.7.dist-info}/RECORD +201 -172
- {gobby-0.2.5.dist-info → gobby-0.2.7.dist-info}/WHEEL +1 -1
- gobby/adapters/codex.py +0 -1292
- gobby/install/claude/commands/gobby/bug.md +0 -51
- gobby/install/claude/commands/gobby/chore.md +0 -51
- gobby/install/claude/commands/gobby/epic.md +0 -52
- gobby/install/claude/commands/gobby/eval.md +0 -235
- gobby/install/claude/commands/gobby/feat.md +0 -49
- gobby/install/claude/commands/gobby/nit.md +0 -52
- gobby/install/claude/commands/gobby/ref.md +0 -52
- gobby/install/codex/prompts/forget.md +0 -7
- gobby/install/codex/prompts/memories.md +0 -7
- gobby/install/codex/prompts/recall.md +0 -7
- gobby/install/codex/prompts/remember.md +0 -13
- gobby/llm/gemini_executor.py +0 -339
- gobby/mcp_proxy/tools/session_messages.py +0 -1056
- gobby/mcp_proxy/tools/task_expansion.py +0 -591
- gobby/prompts/defaults/expansion/system.md +0 -119
- gobby/prompts/defaults/expansion/user.md +0 -48
- gobby/prompts/defaults/external_validation/agent.md +0 -72
- gobby/prompts/defaults/external_validation/external.md +0 -63
- gobby/prompts/defaults/external_validation/spawn.md +0 -83
- gobby/prompts/defaults/external_validation/system.md +0 -6
- gobby/prompts/defaults/features/import_mcp.md +0 -22
- gobby/prompts/defaults/features/import_mcp_github.md +0 -17
- gobby/prompts/defaults/features/import_mcp_search.md +0 -16
- gobby/prompts/defaults/features/recommend_tools.md +0 -32
- gobby/prompts/defaults/features/recommend_tools_hybrid.md +0 -35
- gobby/prompts/defaults/features/recommend_tools_llm.md +0 -30
- gobby/prompts/defaults/features/server_description.md +0 -20
- gobby/prompts/defaults/features/server_description_system.md +0 -6
- gobby/prompts/defaults/features/task_description.md +0 -31
- gobby/prompts/defaults/features/task_description_system.md +0 -6
- gobby/prompts/defaults/features/tool_summary.md +0 -17
- gobby/prompts/defaults/features/tool_summary_system.md +0 -6
- gobby/prompts/defaults/research/step.md +0 -58
- gobby/prompts/defaults/validation/criteria.md +0 -47
- gobby/prompts/defaults/validation/validate.md +0 -38
- gobby/storage/migrations_legacy.py +0 -1359
- gobby/tasks/context.py +0 -747
- gobby/tasks/criteria.py +0 -342
- gobby/tasks/expansion.py +0 -626
- gobby/tasks/prompts/expand.py +0 -327
- gobby/tasks/research.py +0 -421
- gobby/tasks/tdd.py +0 -352
- {gobby-0.2.5.dist-info → gobby-0.2.7.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.5.dist-info → gobby-0.2.7.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.5.dist-info → gobby-0.2.7.dist-info}/top_level.txt +0 -0
|
@@ -22,13 +22,9 @@ from typing import TYPE_CHECKING, Any, Literal, cast
|
|
|
22
22
|
|
|
23
23
|
from gobby.mcp_proxy.tools.internal import InternalToolRegistry
|
|
24
24
|
from gobby.utils.project_context import get_project_context
|
|
25
|
-
from gobby.workflows.definitions import WorkflowState
|
|
26
|
-
from gobby.workflows.loader import WorkflowLoader
|
|
27
|
-
from gobby.workflows.state_manager import WorkflowStateManager
|
|
28
25
|
from gobby.worktrees.git import WorktreeGitManager
|
|
29
26
|
|
|
30
27
|
if TYPE_CHECKING:
|
|
31
|
-
from gobby.agents.runner import AgentRunner
|
|
32
28
|
from gobby.storage.worktrees import LocalWorktreeManager
|
|
33
29
|
from gobby.worktrees.git import WorktreeGitManager
|
|
34
30
|
|
|
@@ -291,7 +287,6 @@ def create_worktrees_registry(
|
|
|
291
287
|
worktree_storage: LocalWorktreeManager,
|
|
292
288
|
git_manager: WorktreeGitManager | None = None,
|
|
293
289
|
project_id: str | None = None,
|
|
294
|
-
agent_runner: AgentRunner | None = None,
|
|
295
290
|
) -> InternalToolRegistry:
|
|
296
291
|
"""
|
|
297
292
|
Create a worktree tool registry with all worktree-related tools.
|
|
@@ -300,7 +295,6 @@ def create_worktrees_registry(
|
|
|
300
295
|
worktree_storage: LocalWorktreeManager for database operations.
|
|
301
296
|
git_manager: WorktreeGitManager for git operations.
|
|
302
297
|
project_id: Default project ID for operations.
|
|
303
|
-
agent_runner: AgentRunner for spawning agents in worktrees.
|
|
304
298
|
|
|
305
299
|
Returns:
|
|
306
300
|
InternalToolRegistry with all worktree tools registered.
|
|
@@ -929,336 +923,4 @@ def create_worktrees_registry(
|
|
|
929
923
|
|
|
930
924
|
return {}
|
|
931
925
|
|
|
932
|
-
@registry.tool(
|
|
933
|
-
name="spawn_agent_in_worktree",
|
|
934
|
-
description="Create a worktree and spawn an agent in it.",
|
|
935
|
-
)
|
|
936
|
-
async def spawn_agent_in_worktree(
|
|
937
|
-
prompt: str,
|
|
938
|
-
branch_name: str,
|
|
939
|
-
base_branch: str = "main",
|
|
940
|
-
task_id: str | None = None,
|
|
941
|
-
parent_session_id: str | None = None,
|
|
942
|
-
mode: str = "terminal", # Note: in_process mode is not supported
|
|
943
|
-
terminal: str = "auto",
|
|
944
|
-
provider: Literal["claude", "gemini", "codex", "antigravity"] = "claude",
|
|
945
|
-
model: str | None = None,
|
|
946
|
-
workflow: str | None = None,
|
|
947
|
-
timeout: float = 120.0,
|
|
948
|
-
max_turns: int = 10,
|
|
949
|
-
project_path: str | None = None,
|
|
950
|
-
) -> dict[str, Any]:
|
|
951
|
-
"""
|
|
952
|
-
Create a worktree and spawn an agent to work in it.
|
|
953
|
-
|
|
954
|
-
This combines worktree creation with agent spawning for isolated development.
|
|
955
|
-
|
|
956
|
-
Args:
|
|
957
|
-
prompt: The task/prompt for the agent.
|
|
958
|
-
branch_name: Name for the new branch/worktree.
|
|
959
|
-
base_branch: Branch to base the worktree on (default: main).
|
|
960
|
-
task_id: Optional task ID to link to this worktree.
|
|
961
|
-
parent_session_id: Parent session ID for context.
|
|
962
|
-
mode: Execution mode (terminal, embedded, headless). Note: in_process is not supported.
|
|
963
|
-
terminal: Terminal for terminal/embedded modes (auto, ghostty, etc.).
|
|
964
|
-
provider: LLM provider (claude, gemini, etc.).
|
|
965
|
-
model: Optional model override.
|
|
966
|
-
workflow: Workflow name to execute.
|
|
967
|
-
timeout: Execution timeout in seconds (default: 120).
|
|
968
|
-
max_turns: Maximum turns (default: 10).
|
|
969
|
-
project_path: Path to project directory (pass cwd from CLI).
|
|
970
|
-
|
|
971
|
-
Returns:
|
|
972
|
-
Dict with worktree_id, run_id, and status.
|
|
973
|
-
"""
|
|
974
|
-
if agent_runner is None:
|
|
975
|
-
return {
|
|
976
|
-
"success": False,
|
|
977
|
-
"error": "Agent runner not configured. Cannot spawn agent.",
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
# Resolve project context
|
|
981
|
-
resolved_git_mgr, resolved_project_id, error = _resolve_project_context(
|
|
982
|
-
project_path, git_manager, project_id
|
|
983
|
-
)
|
|
984
|
-
if error:
|
|
985
|
-
return {"success": False, "error": error}
|
|
986
|
-
|
|
987
|
-
# Type narrowing: if no error, these are guaranteed non-None
|
|
988
|
-
if resolved_git_mgr is None or resolved_project_id is None:
|
|
989
|
-
raise RuntimeError("Git manager or project ID unexpectedly None")
|
|
990
|
-
|
|
991
|
-
if parent_session_id is None:
|
|
992
|
-
return {
|
|
993
|
-
"success": False,
|
|
994
|
-
"error": "parent_session_id is required for agent spawning.",
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
# Handle mode aliases and validation
|
|
998
|
-
# "interactive" is an alias for "terminal" mode
|
|
999
|
-
if mode == "interactive":
|
|
1000
|
-
mode = "terminal"
|
|
1001
|
-
|
|
1002
|
-
valid_modes = ["terminal", "embedded", "headless"]
|
|
1003
|
-
if mode not in valid_modes:
|
|
1004
|
-
return {
|
|
1005
|
-
"success": False,
|
|
1006
|
-
"error": (
|
|
1007
|
-
f"Invalid mode '{mode}'. Must be one of: {', '.join(valid_modes)} (or 'interactive' as alias for 'terminal'). "
|
|
1008
|
-
f"Note: 'in_process' mode is not supported for spawn_agent_in_worktree."
|
|
1009
|
-
),
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
# Normalize terminal parameter to lowercase for enum compatibility
|
|
1013
|
-
# (TerminalType enum values are lowercase, e.g., "terminal.app" not "Terminal.app")
|
|
1014
|
-
if isinstance(terminal, str):
|
|
1015
|
-
terminal = terminal.lower()
|
|
1016
|
-
|
|
1017
|
-
# Validate workflow (reject lifecycle workflows)
|
|
1018
|
-
if workflow:
|
|
1019
|
-
workflow_loader = WorkflowLoader()
|
|
1020
|
-
is_valid, error_msg = workflow_loader.validate_workflow_for_agent(
|
|
1021
|
-
workflow, project_path=project_path
|
|
1022
|
-
)
|
|
1023
|
-
if not is_valid:
|
|
1024
|
-
return {
|
|
1025
|
-
"success": False,
|
|
1026
|
-
"error": error_msg,
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
# Check if worktree already exists for this branch
|
|
1030
|
-
existing = worktree_storage.get_by_branch(resolved_project_id, branch_name)
|
|
1031
|
-
if existing:
|
|
1032
|
-
# Use existing worktree
|
|
1033
|
-
worktree = existing
|
|
1034
|
-
logger.info(f"Using existing worktree for branch '{branch_name}'")
|
|
1035
|
-
else:
|
|
1036
|
-
# Generate worktree path in temp directory
|
|
1037
|
-
project_name = Path(resolved_git_mgr.repo_path).name
|
|
1038
|
-
worktree_path = _generate_worktree_path(branch_name, project_name)
|
|
1039
|
-
|
|
1040
|
-
# Create git worktree
|
|
1041
|
-
result = resolved_git_mgr.create_worktree(
|
|
1042
|
-
worktree_path=worktree_path,
|
|
1043
|
-
branch_name=branch_name,
|
|
1044
|
-
base_branch=base_branch,
|
|
1045
|
-
create_branch=True,
|
|
1046
|
-
)
|
|
1047
|
-
|
|
1048
|
-
if not result.success:
|
|
1049
|
-
return {
|
|
1050
|
-
"success": False,
|
|
1051
|
-
"error": result.error or "Failed to create git worktree",
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
# Record in database
|
|
1055
|
-
worktree = worktree_storage.create(
|
|
1056
|
-
project_id=resolved_project_id,
|
|
1057
|
-
branch_name=branch_name,
|
|
1058
|
-
worktree_path=worktree_path,
|
|
1059
|
-
base_branch=base_branch,
|
|
1060
|
-
task_id=task_id,
|
|
1061
|
-
)
|
|
1062
|
-
|
|
1063
|
-
# Copy project.json and install provider hooks
|
|
1064
|
-
_copy_project_json_to_worktree(resolved_git_mgr.repo_path, worktree.worktree_path)
|
|
1065
|
-
_install_provider_hooks(provider, worktree.worktree_path)
|
|
1066
|
-
|
|
1067
|
-
# Check spawn depth limit
|
|
1068
|
-
can_spawn, reason, _depth = agent_runner.can_spawn(parent_session_id)
|
|
1069
|
-
if not can_spawn:
|
|
1070
|
-
return {
|
|
1071
|
-
"success": False,
|
|
1072
|
-
"error": reason,
|
|
1073
|
-
"worktree_id": worktree.id,
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
# Import AgentConfig and get machine_id
|
|
1077
|
-
from gobby.agents.runner import AgentConfig
|
|
1078
|
-
from gobby.utils.machine_id import get_machine_id
|
|
1079
|
-
|
|
1080
|
-
# Auto-detect machine_id if not provided
|
|
1081
|
-
machine_id = get_machine_id()
|
|
1082
|
-
|
|
1083
|
-
# Create agent config with worktree
|
|
1084
|
-
config = AgentConfig(
|
|
1085
|
-
prompt=prompt,
|
|
1086
|
-
parent_session_id=parent_session_id,
|
|
1087
|
-
project_id=resolved_project_id,
|
|
1088
|
-
machine_id=machine_id,
|
|
1089
|
-
source=provider,
|
|
1090
|
-
workflow=workflow,
|
|
1091
|
-
task=task_id,
|
|
1092
|
-
session_context="summary_markdown",
|
|
1093
|
-
mode=mode,
|
|
1094
|
-
terminal=terminal,
|
|
1095
|
-
worktree_id=worktree.id,
|
|
1096
|
-
provider=provider,
|
|
1097
|
-
model=model,
|
|
1098
|
-
max_turns=max_turns,
|
|
1099
|
-
timeout=timeout,
|
|
1100
|
-
project_path=worktree.worktree_path,
|
|
1101
|
-
)
|
|
1102
|
-
|
|
1103
|
-
# For terminal/embedded/headless modes, use prepare_run + spawner
|
|
1104
|
-
# (runner.run() is only for in_process mode)
|
|
1105
|
-
from gobby.llm.executor import AgentResult
|
|
1106
|
-
|
|
1107
|
-
prepare_result = agent_runner.prepare_run(config)
|
|
1108
|
-
if isinstance(prepare_result, AgentResult):
|
|
1109
|
-
# prepare_run returns AgentResult on error
|
|
1110
|
-
return {
|
|
1111
|
-
"success": False,
|
|
1112
|
-
"worktree_id": worktree.id,
|
|
1113
|
-
"worktree_path": worktree.worktree_path,
|
|
1114
|
-
"branch_name": worktree.branch_name,
|
|
1115
|
-
"error": prepare_result.error,
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
# Successfully prepared - we have context with session and run
|
|
1119
|
-
context = prepare_result
|
|
1120
|
-
|
|
1121
|
-
if context.session is None or context.run is None:
|
|
1122
|
-
return {
|
|
1123
|
-
"success": False,
|
|
1124
|
-
"worktree_id": worktree.id,
|
|
1125
|
-
"error": "Internal error: context missing session or run after prepare_run",
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
child_session = context.session
|
|
1129
|
-
agent_run = context.run
|
|
1130
|
-
|
|
1131
|
-
# Claim worktree for the child session
|
|
1132
|
-
worktree_storage.claim(worktree.id, child_session.id)
|
|
1133
|
-
|
|
1134
|
-
# Pre-save workflow state with session_task if task_id is provided
|
|
1135
|
-
# This ensures suggest_next_task() will scope to this task's subtasks
|
|
1136
|
-
if task_id and workflow:
|
|
1137
|
-
try:
|
|
1138
|
-
workflow_state_manager = WorkflowStateManager(worktree_storage.db)
|
|
1139
|
-
initial_state = WorkflowState(
|
|
1140
|
-
session_id=child_session.id,
|
|
1141
|
-
workflow_name=workflow,
|
|
1142
|
-
step="", # Will be set when workflow actually starts
|
|
1143
|
-
variables={"session_task": task_id},
|
|
1144
|
-
)
|
|
1145
|
-
workflow_state_manager.save_state(initial_state)
|
|
1146
|
-
logger.debug(
|
|
1147
|
-
f"Pre-saved workflow state for session {child_session.id} "
|
|
1148
|
-
f"with session_task={task_id}"
|
|
1149
|
-
)
|
|
1150
|
-
except Exception as e:
|
|
1151
|
-
logger.warning(f"Failed to pre-save workflow state: {e}")
|
|
1152
|
-
# Continue anyway - this is an optimization, not a requirement
|
|
1153
|
-
|
|
1154
|
-
# Build enhanced prompt with worktree context
|
|
1155
|
-
# This helps the agent understand it's in an isolated worktree, not the main repo
|
|
1156
|
-
enhanced_prompt = _build_worktree_context_prompt(
|
|
1157
|
-
original_prompt=prompt,
|
|
1158
|
-
worktree_path=worktree.worktree_path,
|
|
1159
|
-
branch_name=worktree.branch_name,
|
|
1160
|
-
task_id=task_id,
|
|
1161
|
-
main_repo_path=str(resolved_git_mgr.repo_path),
|
|
1162
|
-
)
|
|
1163
|
-
|
|
1164
|
-
# Spawn in terminal using TerminalSpawner
|
|
1165
|
-
if mode == "terminal":
|
|
1166
|
-
from gobby.agents.spawn import TerminalSpawner
|
|
1167
|
-
|
|
1168
|
-
terminal_spawner = TerminalSpawner()
|
|
1169
|
-
terminal_result = terminal_spawner.spawn_agent(
|
|
1170
|
-
cli=provider, # claude, gemini, codex
|
|
1171
|
-
cwd=worktree.worktree_path,
|
|
1172
|
-
session_id=child_session.id,
|
|
1173
|
-
parent_session_id=parent_session_id,
|
|
1174
|
-
agent_run_id=agent_run.id,
|
|
1175
|
-
project_id=resolved_project_id,
|
|
1176
|
-
workflow_name=workflow,
|
|
1177
|
-
agent_depth=child_session.agent_depth,
|
|
1178
|
-
max_agent_depth=agent_runner._child_session_manager.max_agent_depth,
|
|
1179
|
-
terminal=terminal,
|
|
1180
|
-
prompt=enhanced_prompt,
|
|
1181
|
-
)
|
|
1182
|
-
|
|
1183
|
-
if not terminal_result.success:
|
|
1184
|
-
return {
|
|
1185
|
-
"success": False,
|
|
1186
|
-
"worktree_id": worktree.id,
|
|
1187
|
-
"worktree_path": worktree.worktree_path,
|
|
1188
|
-
"branch_name": worktree.branch_name,
|
|
1189
|
-
"run_id": agent_run.id,
|
|
1190
|
-
"child_session_id": child_session.id,
|
|
1191
|
-
"error": terminal_result.error or terminal_result.message,
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
return {
|
|
1195
|
-
"success": True,
|
|
1196
|
-
"worktree_id": worktree.id,
|
|
1197
|
-
"worktree_path": worktree.worktree_path,
|
|
1198
|
-
"branch_name": worktree.branch_name,
|
|
1199
|
-
"run_id": agent_run.id,
|
|
1200
|
-
"child_session_id": child_session.id,
|
|
1201
|
-
"status": "pending",
|
|
1202
|
-
"message": f"Agent spawned in {terminal_result.terminal_type} (PID: {terminal_result.pid})",
|
|
1203
|
-
"terminal_type": terminal_result.terminal_type,
|
|
1204
|
-
"pid": terminal_result.pid,
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
elif mode == "embedded":
|
|
1208
|
-
from gobby.agents.spawn import EmbeddedSpawner
|
|
1209
|
-
|
|
1210
|
-
embedded_spawner = EmbeddedSpawner()
|
|
1211
|
-
embedded_result = embedded_spawner.spawn_agent(
|
|
1212
|
-
cli=provider,
|
|
1213
|
-
cwd=worktree.worktree_path,
|
|
1214
|
-
session_id=child_session.id,
|
|
1215
|
-
parent_session_id=parent_session_id,
|
|
1216
|
-
agent_run_id=agent_run.id,
|
|
1217
|
-
project_id=resolved_project_id,
|
|
1218
|
-
workflow_name=workflow,
|
|
1219
|
-
agent_depth=child_session.agent_depth,
|
|
1220
|
-
max_agent_depth=agent_runner._child_session_manager.max_agent_depth,
|
|
1221
|
-
prompt=enhanced_prompt,
|
|
1222
|
-
)
|
|
1223
|
-
|
|
1224
|
-
return {
|
|
1225
|
-
"success": embedded_result.success,
|
|
1226
|
-
"worktree_id": worktree.id,
|
|
1227
|
-
"worktree_path": worktree.worktree_path,
|
|
1228
|
-
"branch_name": worktree.branch_name,
|
|
1229
|
-
"run_id": agent_run.id,
|
|
1230
|
-
"child_session_id": child_session.id,
|
|
1231
|
-
"status": "pending" if embedded_result.success else "error",
|
|
1232
|
-
"error": embedded_result.error if not embedded_result.success else None,
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
else: # headless
|
|
1236
|
-
from gobby.agents.spawn import HeadlessSpawner
|
|
1237
|
-
|
|
1238
|
-
headless_spawner = HeadlessSpawner()
|
|
1239
|
-
headless_result = headless_spawner.spawn_agent(
|
|
1240
|
-
cli=provider,
|
|
1241
|
-
cwd=worktree.worktree_path,
|
|
1242
|
-
session_id=child_session.id,
|
|
1243
|
-
parent_session_id=parent_session_id,
|
|
1244
|
-
agent_run_id=agent_run.id,
|
|
1245
|
-
project_id=resolved_project_id,
|
|
1246
|
-
workflow_name=workflow,
|
|
1247
|
-
agent_depth=child_session.agent_depth,
|
|
1248
|
-
max_agent_depth=agent_runner._child_session_manager.max_agent_depth,
|
|
1249
|
-
prompt=enhanced_prompt,
|
|
1250
|
-
)
|
|
1251
|
-
|
|
1252
|
-
return {
|
|
1253
|
-
"success": headless_result.success,
|
|
1254
|
-
"worktree_id": worktree.id,
|
|
1255
|
-
"worktree_path": worktree.worktree_path,
|
|
1256
|
-
"branch_name": worktree.branch_name,
|
|
1257
|
-
"run_id": agent_run.id,
|
|
1258
|
-
"child_session_id": child_session.id,
|
|
1259
|
-
"status": "pending" if headless_result.success else "error",
|
|
1260
|
-
"pid": headless_result.pid if headless_result.success else None,
|
|
1261
|
-
"error": headless_result.error if not headless_result.success else None,
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
926
|
return registry
|
|
@@ -76,7 +76,12 @@ def get_backend(backend_type: str, **kwargs: Any) -> MemoryBackendProtocol:
|
|
|
76
76
|
return NullBackend()
|
|
77
77
|
|
|
78
78
|
elif backend_type == "mem0":
|
|
79
|
-
|
|
79
|
+
try:
|
|
80
|
+
from gobby.memory.backends.mem0 import Mem0Backend
|
|
81
|
+
except ImportError as e:
|
|
82
|
+
raise ImportError(
|
|
83
|
+
"mem0ai is not installed. Install with: pip install gobby[mem0]"
|
|
84
|
+
) from e
|
|
80
85
|
|
|
81
86
|
api_key: str | None = kwargs.get("api_key")
|
|
82
87
|
if api_key is None:
|
gobby/memory/backends/mem0.py
CHANGED
|
@@ -60,7 +60,12 @@ class Mem0Backend:
|
|
|
60
60
|
**kwargs: Additional MemoryClient configuration
|
|
61
61
|
"""
|
|
62
62
|
# Lazy import to avoid requiring mem0ai when not used
|
|
63
|
-
|
|
63
|
+
try:
|
|
64
|
+
from mem0 import MemoryClient
|
|
65
|
+
except ImportError as e:
|
|
66
|
+
raise ImportError(
|
|
67
|
+
"Mem0 backend requires 'mem0ai' package. Install it with: pip install gobby[mem0]"
|
|
68
|
+
) from e
|
|
64
69
|
|
|
65
70
|
self._client: MemoryClient = MemoryClient(api_key=api_key, **kwargs)
|
|
66
71
|
self._default_user_id = user_id
|