gobby 0.2.7__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.
- gobby/adapters/claude_code.py +96 -35
- gobby/adapters/gemini.py +140 -38
- gobby/agents/isolation.py +130 -0
- gobby/agents/registry.py +11 -0
- gobby/agents/session.py +1 -0
- gobby/agents/spawn_executor.py +43 -13
- gobby/agents/spawners/macos.py +26 -1
- gobby/cli/__init__.py +0 -2
- gobby/cli/memory.py +185 -0
- gobby/clones/git.py +177 -0
- gobby/config/skills.py +31 -0
- gobby/hooks/event_handlers.py +109 -10
- gobby/hooks/hook_manager.py +19 -1
- gobby/install/gemini/hooks/hook_dispatcher.py +74 -15
- gobby/mcp_proxy/instructions.py +2 -2
- gobby/mcp_proxy/registries.py +21 -4
- gobby/mcp_proxy/tools/agent_messaging.py +93 -44
- gobby/mcp_proxy/tools/agents.py +45 -9
- gobby/mcp_proxy/tools/artifacts.py +43 -9
- gobby/mcp_proxy/tools/sessions/_commits.py +31 -24
- gobby/mcp_proxy/tools/sessions/_crud.py +5 -5
- gobby/mcp_proxy/tools/sessions/_handoff.py +45 -41
- gobby/mcp_proxy/tools/sessions/_messages.py +35 -7
- gobby/mcp_proxy/tools/spawn_agent.py +44 -6
- gobby/mcp_proxy/tools/tasks/_context.py +18 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +13 -6
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +29 -14
- gobby/mcp_proxy/tools/tasks/_session.py +22 -7
- gobby/mcp_proxy/tools/workflows.py +84 -34
- gobby/mcp_proxy/tools/worktrees.py +32 -7
- gobby/memory/extractor.py +15 -1
- gobby/runner.py +13 -0
- gobby/servers/routes/mcp/hooks.py +50 -3
- gobby/servers/websocket.py +57 -1
- gobby/sessions/analyzer.py +2 -2
- gobby/sessions/manager.py +9 -0
- gobby/sessions/transcripts/gemini.py +100 -34
- gobby/storage/database.py +9 -2
- gobby/storage/memories.py +32 -21
- gobby/storage/migrations.py +23 -4
- gobby/storage/sessions.py +4 -2
- gobby/storage/skills.py +43 -3
- gobby/workflows/detection_helpers.py +38 -24
- gobby/workflows/enforcement/blocking.py +13 -1
- gobby/workflows/engine.py +93 -0
- gobby/workflows/evaluator.py +110 -0
- gobby/workflows/hooks.py +41 -0
- gobby/workflows/memory_actions.py +11 -0
- gobby/workflows/safe_evaluator.py +8 -0
- gobby/workflows/summary_actions.py +123 -50
- {gobby-0.2.7.dist-info → gobby-0.2.8.dist-info}/METADATA +1 -1
- {gobby-0.2.7.dist-info → gobby-0.2.8.dist-info}/RECORD +56 -80
- gobby/cli/tui.py +0 -34
- gobby/tui/__init__.py +0 -5
- gobby/tui/api_client.py +0 -278
- gobby/tui/app.py +0 -329
- gobby/tui/screens/__init__.py +0 -25
- gobby/tui/screens/agents.py +0 -333
- gobby/tui/screens/chat.py +0 -450
- gobby/tui/screens/dashboard.py +0 -377
- gobby/tui/screens/memory.py +0 -305
- gobby/tui/screens/metrics.py +0 -231
- gobby/tui/screens/orchestrator.py +0 -903
- gobby/tui/screens/sessions.py +0 -412
- gobby/tui/screens/tasks.py +0 -440
- gobby/tui/screens/workflows.py +0 -289
- gobby/tui/screens/worktrees.py +0 -174
- gobby/tui/widgets/__init__.py +0 -21
- gobby/tui/widgets/chat.py +0 -210
- gobby/tui/widgets/conductor.py +0 -104
- gobby/tui/widgets/menu.py +0 -132
- gobby/tui/widgets/message_panel.py +0 -160
- gobby/tui/widgets/review_gate.py +0 -224
- gobby/tui/widgets/task_tree.py +0 -99
- gobby/tui/widgets/token_budget.py +0 -166
- gobby/tui/ws_client.py +0 -258
- {gobby-0.2.7.dist-info → gobby-0.2.8.dist-info}/WHEEL +0 -0
- {gobby-0.2.7.dist-info → gobby-0.2.8.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.7.dist-info → gobby-0.2.8.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.7.dist-info → gobby-0.2.8.dist-info}/top_level.txt +0 -0
gobby/hooks/event_handlers.py
CHANGED
|
@@ -147,6 +147,56 @@ class EventHandlers:
|
|
|
147
147
|
"""
|
|
148
148
|
return dict(self._handler_map)
|
|
149
149
|
|
|
150
|
+
def _auto_activate_workflow(
|
|
151
|
+
self, workflow_name: str, session_id: str, project_path: str | None
|
|
152
|
+
) -> None:
|
|
153
|
+
"""Auto-activate a workflow for a session.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
workflow_name: Name of the workflow to activate
|
|
157
|
+
session_id: Session ID to activate workflow for
|
|
158
|
+
project_path: Project path for workflow context
|
|
159
|
+
"""
|
|
160
|
+
if not self._workflow_handler:
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
result = self._workflow_handler.activate_workflow(
|
|
165
|
+
workflow_name=workflow_name,
|
|
166
|
+
session_id=session_id,
|
|
167
|
+
project_path=project_path,
|
|
168
|
+
)
|
|
169
|
+
if result.get("success"):
|
|
170
|
+
self.logger.info(
|
|
171
|
+
"Auto-activated workflow for session",
|
|
172
|
+
extra={
|
|
173
|
+
"workflow_name": workflow_name,
|
|
174
|
+
"session_id": session_id,
|
|
175
|
+
"project_path": project_path,
|
|
176
|
+
},
|
|
177
|
+
)
|
|
178
|
+
else:
|
|
179
|
+
self.logger.warning(
|
|
180
|
+
"Failed to auto-activate workflow",
|
|
181
|
+
extra={
|
|
182
|
+
"workflow_name": workflow_name,
|
|
183
|
+
"session_id": session_id,
|
|
184
|
+
"project_path": project_path,
|
|
185
|
+
"error": result.get("error"),
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
self.logger.warning(
|
|
190
|
+
"Failed to auto-activate workflow",
|
|
191
|
+
extra={
|
|
192
|
+
"workflow_name": workflow_name,
|
|
193
|
+
"session_id": session_id,
|
|
194
|
+
"project_path": project_path,
|
|
195
|
+
"error": str(e),
|
|
196
|
+
},
|
|
197
|
+
exc_info=True,
|
|
198
|
+
)
|
|
199
|
+
|
|
150
200
|
# ==================== SESSION HANDLERS ====================
|
|
151
201
|
|
|
152
202
|
def handle_session_start(self, event: HookEvent) -> HookResponse:
|
|
@@ -207,6 +257,12 @@ class EventHandlers:
|
|
|
207
257
|
except Exception as e:
|
|
208
258
|
self.logger.warning(f"Failed to start agent run: {e}")
|
|
209
259
|
|
|
260
|
+
# Auto-activate workflow if specified for this session
|
|
261
|
+
if existing_session.workflow_name and session_id:
|
|
262
|
+
self._auto_activate_workflow(
|
|
263
|
+
existing_session.workflow_name, session_id, cwd
|
|
264
|
+
)
|
|
265
|
+
|
|
210
266
|
# Update event metadata
|
|
211
267
|
event.metadata["_platform_session_id"] = session_id
|
|
212
268
|
|
|
@@ -235,9 +291,9 @@ class EventHandlers:
|
|
|
235
291
|
session_ref = (
|
|
236
292
|
f"#{existing_session.seq_num}" if existing_session.seq_num else session_id
|
|
237
293
|
)
|
|
238
|
-
system_message = f"\nGobby Session
|
|
239
|
-
system_message +=
|
|
240
|
-
system_message += f"\nExternal ID: {external_id}"
|
|
294
|
+
system_message = f"\nGobby Session ID: {session_ref}"
|
|
295
|
+
system_message += " <- Use this for MCP tool calls (session_id parameter)"
|
|
296
|
+
system_message += f"\nExternal ID: {external_id} (CLI-native, rarely needed)"
|
|
241
297
|
if parent_session_id:
|
|
242
298
|
context_parts.append(f"Parent session: {parent_session_id}")
|
|
243
299
|
|
|
@@ -261,6 +317,7 @@ class EventHandlers:
|
|
|
261
317
|
system_message=system_message,
|
|
262
318
|
metadata={
|
|
263
319
|
"session_id": session_id,
|
|
320
|
+
"session_ref": session_ref,
|
|
264
321
|
"parent_session_id": parent_session_id,
|
|
265
322
|
"machine_id": machine_id,
|
|
266
323
|
"project_id": existing_session.project_id,
|
|
@@ -272,9 +329,13 @@ class EventHandlers:
|
|
|
272
329
|
except Exception as e:
|
|
273
330
|
self.logger.debug(f"No pre-created session found: {e}")
|
|
274
331
|
|
|
275
|
-
# Step 1: Find parent session
|
|
276
|
-
|
|
277
|
-
|
|
332
|
+
# Step 1: Find parent session
|
|
333
|
+
# Check env vars first (spawned agent case), then handoff (source='clear')
|
|
334
|
+
parent_session_id = input_data.get("parent_session_id")
|
|
335
|
+
workflow_name = input_data.get("workflow_name")
|
|
336
|
+
agent_depth = input_data.get("agent_depth")
|
|
337
|
+
|
|
338
|
+
if not parent_session_id and session_source == "clear" and self._session_storage:
|
|
278
339
|
try:
|
|
279
340
|
parent = self._session_storage.find_parent(
|
|
280
341
|
machine_id=machine_id,
|
|
@@ -291,6 +352,14 @@ class EventHandlers:
|
|
|
291
352
|
# Step 2: Register new session with parent if found
|
|
292
353
|
# Extract terminal context (injected by hook_dispatcher for terminal correlation)
|
|
293
354
|
terminal_context = input_data.get("terminal_context")
|
|
355
|
+
# Parse agent_depth as int if provided
|
|
356
|
+
agent_depth_val = 0
|
|
357
|
+
if agent_depth:
|
|
358
|
+
try:
|
|
359
|
+
agent_depth_val = int(agent_depth)
|
|
360
|
+
except (ValueError, TypeError):
|
|
361
|
+
pass
|
|
362
|
+
|
|
294
363
|
session_id = None
|
|
295
364
|
if self._session_manager:
|
|
296
365
|
session_id = self._session_manager.register_session(
|
|
@@ -302,6 +371,8 @@ class EventHandlers:
|
|
|
302
371
|
source=cli_source,
|
|
303
372
|
project_path=cwd,
|
|
304
373
|
terminal_context=terminal_context,
|
|
374
|
+
workflow_name=workflow_name,
|
|
375
|
+
agent_depth=agent_depth_val,
|
|
305
376
|
)
|
|
306
377
|
|
|
307
378
|
# Step 2b: Mark parent session as expired after successful handoff
|
|
@@ -312,6 +383,10 @@ class EventHandlers:
|
|
|
312
383
|
except Exception as e:
|
|
313
384
|
self.logger.warning(f"Failed to mark parent session as expired: {e}")
|
|
314
385
|
|
|
386
|
+
# Step 2c: Auto-activate workflow if specified (for spawned agents)
|
|
387
|
+
if workflow_name and session_id:
|
|
388
|
+
self._auto_activate_workflow(workflow_name, session_id, cwd)
|
|
389
|
+
|
|
315
390
|
# Step 3: Track registered session
|
|
316
391
|
if transcript_path and self._session_coordinator:
|
|
317
392
|
try:
|
|
@@ -354,9 +429,13 @@ class EventHandlers:
|
|
|
354
429
|
session_obj = self._session_storage.get(session_id)
|
|
355
430
|
if session_obj and session_obj.seq_num:
|
|
356
431
|
session_ref = f"#{session_obj.seq_num}"
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
432
|
+
# Format: "Gobby Session ID: #N" with usage hint
|
|
433
|
+
if session_ref and session_ref != session_id:
|
|
434
|
+
system_message = f"\nGobby Session ID: {session_ref}"
|
|
435
|
+
else:
|
|
436
|
+
system_message = f"\nGobby Session ID: {session_id}"
|
|
437
|
+
system_message += " <- Use this for MCP tool calls (session_id parameter)"
|
|
438
|
+
system_message += f"\nExternal ID: {external_id} (CLI-native, rarely needed)"
|
|
360
439
|
|
|
361
440
|
# Add active lifecycle workflows
|
|
362
441
|
if wf_response.metadata and "discovered_workflows" in wf_response.metadata:
|
|
@@ -384,6 +463,7 @@ class EventHandlers:
|
|
|
384
463
|
# Build metadata with terminal context (filter out nulls)
|
|
385
464
|
metadata: dict[str, Any] = {
|
|
386
465
|
"session_id": session_id,
|
|
466
|
+
"session_ref": session_ref,
|
|
387
467
|
"parent_session_id": parent_session_id,
|
|
388
468
|
"machine_id": machine_id,
|
|
389
469
|
"project_id": project_id,
|
|
@@ -716,10 +796,16 @@ class EventHandlers:
|
|
|
716
796
|
|
|
717
797
|
# Track edits for session high-water mark
|
|
718
798
|
# Only if tool succeeded, matches edit tools, and session has claimed a task
|
|
799
|
+
# Skip .gobby/ internal files (tasks.jsonl, memories.jsonl, etc.)
|
|
800
|
+
tool_input = input_data.get("tool_input", {})
|
|
801
|
+
file_path = tool_input.get("file_path", "")
|
|
802
|
+
is_gobby_internal = "/.gobby/" in file_path or file_path.startswith(".gobby/")
|
|
803
|
+
|
|
719
804
|
if (
|
|
720
805
|
not is_failure
|
|
721
806
|
and tool_name
|
|
722
807
|
and tool_name.lower() in EDIT_TOOLS
|
|
808
|
+
and not is_gobby_internal
|
|
723
809
|
and self._session_storage
|
|
724
810
|
and self._task_manager
|
|
725
811
|
):
|
|
@@ -780,10 +866,23 @@ class EventHandlers:
|
|
|
780
866
|
# ==================== COMPACT HANDLER ====================
|
|
781
867
|
|
|
782
868
|
def handle_pre_compact(self, event: HookEvent) -> HookResponse:
|
|
783
|
-
"""Handle PRE_COMPACT event.
|
|
869
|
+
"""Handle PRE_COMPACT event.
|
|
870
|
+
|
|
871
|
+
Note: Gemini fires PreCompress constantly during normal operation,
|
|
872
|
+
unlike Claude which fires it only when approaching context limits.
|
|
873
|
+
We skip handoff logic and workflow execution for Gemini to avoid
|
|
874
|
+
excessive state changes and workflow interruptions.
|
|
875
|
+
"""
|
|
876
|
+
from gobby.hooks.events import SessionSource
|
|
877
|
+
|
|
784
878
|
trigger = event.data.get("trigger", "auto")
|
|
785
879
|
session_id = event.metadata.get("_platform_session_id")
|
|
786
880
|
|
|
881
|
+
# Skip handoff logic for Gemini - it fires PreCompress too frequently
|
|
882
|
+
if event.source == SessionSource.GEMINI:
|
|
883
|
+
self.logger.debug(f"PRE_COMPACT ({trigger}): session {session_id} [Gemini - skipped]")
|
|
884
|
+
return HookResponse(decision="allow")
|
|
885
|
+
|
|
787
886
|
if session_id:
|
|
788
887
|
self.logger.debug(f"PRE_COMPACT ({trigger}): session {session_id}")
|
|
789
888
|
# Mark session as handoff_ready so it can be found as parent after compact
|
gobby/hooks/hook_manager.py
CHANGED
|
@@ -369,6 +369,10 @@ class HookManager:
|
|
|
369
369
|
# Skill manager for core skill injection
|
|
370
370
|
self._skill_manager = HookSkillManager()
|
|
371
371
|
|
|
372
|
+
# Track sessions that have received full metadata injection
|
|
373
|
+
# Key: "{platform_session_id}:{source}" - cleared on daemon restart
|
|
374
|
+
self._injected_sessions: set[str] = set()
|
|
375
|
+
|
|
372
376
|
# Event handlers (delegated to EventHandlers module)
|
|
373
377
|
self._event_handlers = EventHandlers(
|
|
374
378
|
session_manager=self._session_manager,
|
|
@@ -644,7 +648,21 @@ class HookManager:
|
|
|
644
648
|
# Copy session metadata from event to response for adapter injection
|
|
645
649
|
# The adapter reads response.metadata to inject session info into agent context
|
|
646
650
|
if event.metadata.get("_platform_session_id"):
|
|
647
|
-
|
|
651
|
+
platform_session_id = event.metadata["_platform_session_id"]
|
|
652
|
+
response.metadata["session_id"] = platform_session_id
|
|
653
|
+
# Look up seq_num for session_ref (#N format)
|
|
654
|
+
if self._session_storage:
|
|
655
|
+
session_obj = self._session_storage.get(platform_session_id)
|
|
656
|
+
if session_obj and session_obj.seq_num:
|
|
657
|
+
response.metadata["session_ref"] = f"#{session_obj.seq_num}"
|
|
658
|
+
|
|
659
|
+
# Track first hook per session for token optimization
|
|
660
|
+
# Adapters use this flag to inject full metadata only on first hook
|
|
661
|
+
session_key = f"{platform_session_id}:{event.source.value}"
|
|
662
|
+
is_first = session_key not in self._injected_sessions
|
|
663
|
+
if is_first:
|
|
664
|
+
self._injected_sessions.add(session_key)
|
|
665
|
+
response.metadata["_first_hook_for_session"] = is_first
|
|
648
666
|
if event.session_id: # external_id (e.g., Claude Code's session UUID)
|
|
649
667
|
response.metadata["external_id"] = event.session_id
|
|
650
668
|
if event.machine_id:
|
|
@@ -216,52 +216,111 @@ def main() -> int:
|
|
|
216
216
|
# This captures the terminal/process info for session correlation
|
|
217
217
|
if hook_type == "SessionStart":
|
|
218
218
|
input_data["terminal_context"] = get_terminal_context()
|
|
219
|
+
# Note: gobby_context (parent_session_id, workflow, etc.) is no longer
|
|
220
|
+
# injected from env vars. For spawned agents, the session is pre-created
|
|
221
|
+
# with all linkage via preflight+resume pattern, so the daemon already
|
|
222
|
+
# has the context when SessionStart fires.
|
|
219
223
|
|
|
220
224
|
# Log what Gemini CLI sends us (for debugging hook data issues)
|
|
221
|
-
|
|
225
|
+
# Extract common context fields for structured logging
|
|
226
|
+
session_id = input_data.get("session_id")
|
|
227
|
+
task_id = input_data.get("task_id")
|
|
228
|
+
project_id = input_data.get("project_id")
|
|
229
|
+
base_context = {
|
|
230
|
+
"hook_type": hook_type,
|
|
231
|
+
"session_id": session_id,
|
|
232
|
+
"task_id": task_id,
|
|
233
|
+
"project_id": project_id,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
logger.info(
|
|
237
|
+
"[%s] Received input keys: %s",
|
|
238
|
+
hook_type,
|
|
239
|
+
list(input_data.keys()),
|
|
240
|
+
extra=base_context,
|
|
241
|
+
)
|
|
222
242
|
|
|
223
243
|
# Log hook-specific critical fields
|
|
224
244
|
if hook_type == "SessionStart":
|
|
225
|
-
logger.info(
|
|
245
|
+
logger.info(
|
|
246
|
+
"[SessionStart] session_id=%s",
|
|
247
|
+
session_id,
|
|
248
|
+
extra=base_context,
|
|
249
|
+
)
|
|
226
250
|
elif hook_type == "SessionEnd":
|
|
251
|
+
reason = input_data.get("reason")
|
|
227
252
|
logger.info(
|
|
228
|
-
|
|
229
|
-
|
|
253
|
+
"[SessionEnd] session_id=%s, reason=%s",
|
|
254
|
+
session_id,
|
|
255
|
+
reason,
|
|
256
|
+
extra={**base_context, "reason": reason},
|
|
230
257
|
)
|
|
231
258
|
elif hook_type == "BeforeAgent":
|
|
232
259
|
prompt = input_data.get("prompt", "")
|
|
233
260
|
prompt_preview = prompt[:100] + "..." if len(prompt) > 100 else prompt
|
|
234
261
|
logger.info(
|
|
235
|
-
|
|
262
|
+
"[BeforeAgent] session_id=%s, prompt=%s",
|
|
263
|
+
session_id,
|
|
264
|
+
prompt_preview,
|
|
265
|
+
extra={**base_context, "prompt_preview": prompt_preview},
|
|
236
266
|
)
|
|
237
267
|
elif hook_type == "BeforeTool":
|
|
238
268
|
tool_name = input_data.get("tool_name") or input_data.get("function_name", "unknown")
|
|
239
269
|
logger.info(
|
|
240
|
-
|
|
270
|
+
"[BeforeTool] tool_name=%s, session_id=%s",
|
|
271
|
+
tool_name,
|
|
272
|
+
session_id,
|
|
273
|
+
extra={**base_context, "tool_name": tool_name},
|
|
241
274
|
)
|
|
242
275
|
elif hook_type == "AfterTool":
|
|
243
276
|
tool_name = input_data.get("tool_name") or input_data.get("function_name", "unknown")
|
|
277
|
+
error = input_data.get("error")
|
|
244
278
|
logger.info(
|
|
245
|
-
|
|
279
|
+
"[AfterTool] tool_name=%s, session_id=%s",
|
|
280
|
+
tool_name,
|
|
281
|
+
session_id,
|
|
282
|
+
extra={**base_context, "tool_name": tool_name, "error": error},
|
|
246
283
|
)
|
|
247
284
|
elif hook_type == "BeforeToolSelection":
|
|
248
|
-
logger.info(
|
|
285
|
+
logger.info(
|
|
286
|
+
"[BeforeToolSelection] session_id=%s",
|
|
287
|
+
session_id,
|
|
288
|
+
extra=base_context,
|
|
289
|
+
)
|
|
249
290
|
elif hook_type == "BeforeModel":
|
|
291
|
+
model = input_data.get("model", "unknown")
|
|
250
292
|
logger.info(
|
|
251
|
-
|
|
252
|
-
|
|
293
|
+
"[BeforeModel] session_id=%s, model=%s",
|
|
294
|
+
session_id,
|
|
295
|
+
model,
|
|
296
|
+
extra={**base_context, "model": model},
|
|
253
297
|
)
|
|
254
298
|
elif hook_type == "AfterModel":
|
|
255
|
-
logger.info(
|
|
299
|
+
logger.info(
|
|
300
|
+
"[AfterModel] session_id=%s",
|
|
301
|
+
session_id,
|
|
302
|
+
extra=base_context,
|
|
303
|
+
)
|
|
256
304
|
elif hook_type == "PreCompress":
|
|
257
|
-
logger.info(
|
|
305
|
+
logger.info(
|
|
306
|
+
"[PreCompress] session_id=%s",
|
|
307
|
+
session_id,
|
|
308
|
+
extra=base_context,
|
|
309
|
+
)
|
|
258
310
|
elif hook_type == "Notification":
|
|
311
|
+
message = input_data.get("message")
|
|
259
312
|
logger.info(
|
|
260
|
-
|
|
261
|
-
|
|
313
|
+
"[Notification] session_id=%s, message=%s",
|
|
314
|
+
session_id,
|
|
315
|
+
message,
|
|
316
|
+
extra={**base_context, "notification_message": message},
|
|
262
317
|
)
|
|
263
318
|
elif hook_type == "AfterAgent":
|
|
264
|
-
logger.info(
|
|
319
|
+
logger.info(
|
|
320
|
+
"[AfterAgent] session_id=%s",
|
|
321
|
+
session_id,
|
|
322
|
+
extra=base_context,
|
|
323
|
+
)
|
|
265
324
|
|
|
266
325
|
if debug_mode:
|
|
267
326
|
logger.debug(f"Input data: {input_data}")
|
gobby/mcp_proxy/instructions.py
CHANGED
|
@@ -26,9 +26,9 @@ def build_gobby_instructions() -> str:
|
|
|
26
26
|
At the start of EVERY session:
|
|
27
27
|
1. `list_mcp_servers()` — Discover available servers
|
|
28
28
|
2. `list_skills()` — Discover available skills
|
|
29
|
-
3. Session ID: Look for `
|
|
29
|
+
3. Session ID: Look for `Gobby Session Ref:` or `Gobby Session ID:` in your context.
|
|
30
30
|
If missing, call:
|
|
31
|
-
`call_tool("gobby-sessions", "
|
|
31
|
+
`call_tool("gobby-sessions", "get_current_session", {"external_id": "<your-session-id>", "source": "<cli-name>"})`
|
|
32
32
|
|
|
33
33
|
Session and task references use `#N` format (e.g., `#1`, `#42`) which is project-scoped.
|
|
34
34
|
</startup>
|
gobby/mcp_proxy/registries.py
CHANGED
|
@@ -168,21 +168,38 @@ def setup_internal_registries(
|
|
|
168
168
|
|
|
169
169
|
# Initialize agents registry if agent_runner is available
|
|
170
170
|
if agent_runner is not None:
|
|
171
|
-
from gobby.agents.
|
|
171
|
+
from gobby.agents.definitions import AgentDefinitionLoader
|
|
172
172
|
from gobby.mcp_proxy.tools.agents import create_agents_registry
|
|
173
173
|
|
|
174
|
+
# Create clone git manager if we have a git manager
|
|
175
|
+
clone_git_manager = None
|
|
176
|
+
if git_manager is not None:
|
|
177
|
+
try:
|
|
178
|
+
from gobby.clones.git import CloneGitManager
|
|
179
|
+
|
|
180
|
+
clone_git_manager = CloneGitManager(git_manager.repo_path)
|
|
181
|
+
except Exception as e:
|
|
182
|
+
logger.debug(f"CloneGitManager not available for spawn_agent: {e}")
|
|
183
|
+
|
|
174
184
|
agents_registry = create_agents_registry(
|
|
175
185
|
runner=agent_runner,
|
|
186
|
+
agent_loader=AgentDefinitionLoader(),
|
|
187
|
+
session_manager=local_session_manager,
|
|
188
|
+
task_manager=task_manager,
|
|
189
|
+
worktree_storage=worktree_storage,
|
|
190
|
+
git_manager=git_manager,
|
|
191
|
+
clone_storage=clone_storage,
|
|
192
|
+
clone_manager=clone_git_manager,
|
|
176
193
|
)
|
|
177
194
|
|
|
178
|
-
# Add inter-agent messaging tools if message manager
|
|
179
|
-
if inter_session_message_manager is not None:
|
|
195
|
+
# Add inter-agent messaging tools if message manager and session manager are available
|
|
196
|
+
if inter_session_message_manager is not None and local_session_manager is not None:
|
|
180
197
|
from gobby.mcp_proxy.tools.agent_messaging import add_messaging_tools
|
|
181
198
|
|
|
182
199
|
add_messaging_tools(
|
|
183
200
|
registry=agents_registry,
|
|
184
201
|
message_manager=inter_session_message_manager,
|
|
185
|
-
|
|
202
|
+
session_manager=local_session_manager,
|
|
186
203
|
)
|
|
187
204
|
logger.debug("Agent messaging tools added to agents registry")
|
|
188
205
|
|