gobby 0.2.7__py3-none-any.whl → 0.2.9__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/claude_code.py +99 -61
- 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/app_context.py +59 -0
- gobby/cli/__init__.py +0 -2
- gobby/cli/memory.py +185 -0
- gobby/cli/utils.py +5 -17
- gobby/clones/git.py +177 -0
- gobby/config/features.py +0 -20
- gobby/config/skills.py +31 -0
- gobby/config/tasks.py +4 -0
- gobby/hooks/event_handlers/__init__.py +155 -0
- gobby/hooks/event_handlers/_agent.py +175 -0
- gobby/hooks/event_handlers/_base.py +87 -0
- gobby/hooks/event_handlers/_misc.py +66 -0
- gobby/hooks/event_handlers/_session.py +573 -0
- gobby/hooks/event_handlers/_tool.py +196 -0
- gobby/hooks/hook_manager.py +21 -1
- gobby/install/gemini/hooks/hook_dispatcher.py +74 -15
- gobby/llm/claude.py +377 -42
- gobby/mcp_proxy/importer.py +4 -41
- gobby/mcp_proxy/instructions.py +2 -2
- gobby/mcp_proxy/manager.py +13 -3
- gobby/mcp_proxy/registries.py +35 -4
- gobby/mcp_proxy/services/recommendation.py +2 -28
- gobby/mcp_proxy/tools/agent_messaging.py +93 -44
- gobby/mcp_proxy/tools/agents.py +45 -9
- gobby/mcp_proxy/tools/artifacts.py +46 -12
- 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/task_readiness.py +27 -4
- 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/__init__.py +266 -0
- gobby/mcp_proxy/tools/workflows/_artifacts.py +225 -0
- gobby/mcp_proxy/tools/workflows/_import.py +112 -0
- gobby/mcp_proxy/tools/workflows/_lifecycle.py +321 -0
- gobby/mcp_proxy/tools/workflows/_query.py +207 -0
- gobby/mcp_proxy/tools/workflows/_resolution.py +78 -0
- gobby/mcp_proxy/tools/workflows/_terminal.py +139 -0
- gobby/mcp_proxy/tools/worktrees.py +32 -7
- gobby/memory/components/__init__.py +0 -0
- gobby/memory/components/ingestion.py +98 -0
- gobby/memory/components/search.py +108 -0
- gobby/memory/extractor.py +15 -1
- gobby/memory/manager.py +16 -25
- gobby/paths.py +51 -0
- gobby/prompts/loader.py +1 -35
- gobby/runner.py +36 -10
- gobby/servers/http.py +186 -149
- gobby/servers/routes/admin.py +12 -0
- gobby/servers/routes/mcp/endpoints/execution.py +15 -7
- gobby/servers/routes/mcp/endpoints/registry.py +8 -8
- gobby/servers/routes/mcp/hooks.py +50 -3
- gobby/servers/websocket.py +57 -1
- gobby/sessions/analyzer.py +4 -4
- gobby/sessions/manager.py +9 -0
- gobby/sessions/transcripts/gemini.py +100 -34
- gobby/skills/parser.py +23 -0
- gobby/skills/sync.py +5 -4
- gobby/storage/artifacts.py +19 -0
- gobby/storage/database.py +9 -2
- gobby/storage/memories.py +32 -21
- gobby/storage/migrations.py +46 -4
- gobby/storage/sessions.py +4 -2
- gobby/storage/skills.py +87 -7
- gobby/tasks/external_validator.py +4 -17
- gobby/tasks/validation.py +13 -87
- gobby/tools/summarizer.py +18 -51
- gobby/utils/status.py +13 -0
- gobby/workflows/actions.py +5 -0
- gobby/workflows/context_actions.py +21 -24
- gobby/workflows/detection_helpers.py +38 -24
- gobby/workflows/enforcement/__init__.py +11 -1
- gobby/workflows/enforcement/blocking.py +109 -1
- gobby/workflows/enforcement/handlers.py +35 -1
- gobby/workflows/engine.py +96 -0
- gobby/workflows/evaluator.py +110 -0
- gobby/workflows/hooks.py +41 -0
- gobby/workflows/lifecycle_evaluator.py +2 -1
- 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.9.dist-info}/METADATA +1 -1
- {gobby-0.2.7.dist-info → gobby-0.2.9.dist-info}/RECORD +99 -107
- gobby/cli/tui.py +0 -34
- gobby/hooks/event_handlers.py +0 -909
- gobby/mcp_proxy/tools/workflows.py +0 -973
- 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.9.dist-info}/WHEEL +0 -0
- {gobby-0.2.7.dist-info → gobby-0.2.9.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.7.dist-info → gobby-0.2.9.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.7.dist-info → gobby-0.2.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from gobby.hooks.event_handlers._base import EventHandlersBase
|
|
6
|
+
from gobby.hooks.events import HookEvent, HookResponse
|
|
7
|
+
|
|
8
|
+
EDIT_TOOLS = {
|
|
9
|
+
"write_file",
|
|
10
|
+
"replace",
|
|
11
|
+
"edit_file",
|
|
12
|
+
"notebook_edit",
|
|
13
|
+
"edit",
|
|
14
|
+
"write",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ToolEventHandlerMixin(EventHandlersBase):
|
|
19
|
+
"""Mixin for handling tool-related events."""
|
|
20
|
+
|
|
21
|
+
def handle_before_tool(self, event: HookEvent) -> HookResponse:
|
|
22
|
+
"""Handle BEFORE_TOOL event."""
|
|
23
|
+
input_data = event.data
|
|
24
|
+
tool_name = input_data.get("tool_name", "unknown")
|
|
25
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
26
|
+
|
|
27
|
+
if session_id:
|
|
28
|
+
self.logger.debug(f"BEFORE_TOOL: {tool_name}, session {session_id}")
|
|
29
|
+
else:
|
|
30
|
+
self.logger.debug(f"BEFORE_TOOL: {tool_name}")
|
|
31
|
+
|
|
32
|
+
context_parts = []
|
|
33
|
+
|
|
34
|
+
# Execute lifecycle workflow triggers
|
|
35
|
+
if self._workflow_handler:
|
|
36
|
+
try:
|
|
37
|
+
wf_response = self._workflow_handler.handle_all_lifecycles(event)
|
|
38
|
+
if wf_response.context:
|
|
39
|
+
context_parts.append(wf_response.context)
|
|
40
|
+
if wf_response.decision != "allow":
|
|
41
|
+
return wf_response
|
|
42
|
+
except Exception as e:
|
|
43
|
+
self.logger.error(f"Failed to execute lifecycle workflows: {e}", exc_info=True)
|
|
44
|
+
|
|
45
|
+
return HookResponse(
|
|
46
|
+
decision="allow",
|
|
47
|
+
context="\n\n".join(context_parts) if context_parts else None,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def handle_after_tool(self, event: HookEvent) -> HookResponse:
|
|
51
|
+
"""Handle AFTER_TOOL event."""
|
|
52
|
+
input_data = event.data
|
|
53
|
+
tool_name = input_data.get("tool_name", "unknown")
|
|
54
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
55
|
+
is_failure = event.metadata.get("is_failure", False)
|
|
56
|
+
|
|
57
|
+
status = "FAIL" if is_failure else "OK"
|
|
58
|
+
if session_id:
|
|
59
|
+
self.logger.debug(f"AFTER_TOOL [{status}]: {tool_name}, session {session_id}")
|
|
60
|
+
|
|
61
|
+
# Track edits for session high-water mark
|
|
62
|
+
# Only if tool succeeded, matches edit tools, and session has claimed a task
|
|
63
|
+
# Skip .gobby/ internal files (tasks.jsonl, memories.jsonl, etc.)
|
|
64
|
+
tool_input = input_data.get("tool_input", {})
|
|
65
|
+
|
|
66
|
+
# Capture artifacts from edit tools
|
|
67
|
+
if not is_failure and self._artifact_capture_hook:
|
|
68
|
+
self._capture_tool_artifact(session_id, tool_name, tool_input)
|
|
69
|
+
|
|
70
|
+
# Simple check for edit tools (case-insensitive)
|
|
71
|
+
is_edit = tool_name.lower() in EDIT_TOOLS
|
|
72
|
+
|
|
73
|
+
# For complex tools (multi_replace, etc), check if they modify files
|
|
74
|
+
# This logic could be expanded, but for now stick to the basic set
|
|
75
|
+
|
|
76
|
+
if not is_failure and is_edit and self._session_storage:
|
|
77
|
+
try:
|
|
78
|
+
# Check if file is internal .gobby file
|
|
79
|
+
file_path = (
|
|
80
|
+
tool_input.get("file_path")
|
|
81
|
+
or tool_input.get("target_file")
|
|
82
|
+
or tool_input.get("path")
|
|
83
|
+
)
|
|
84
|
+
is_internal = file_path and ".gobby/" in str(file_path)
|
|
85
|
+
|
|
86
|
+
if not is_internal:
|
|
87
|
+
# Check if session has any claimed tasks before marking had_edits
|
|
88
|
+
has_claimed_task = False
|
|
89
|
+
if self._task_manager:
|
|
90
|
+
try:
|
|
91
|
+
claimed_tasks = self._task_manager.list_tasks(assignee=session_id)
|
|
92
|
+
has_claimed_task = len(claimed_tasks) > 0
|
|
93
|
+
except Exception as e:
|
|
94
|
+
self.logger.debug(
|
|
95
|
+
f"Failed to check claimed tasks for session {session_id}: {e}"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if has_claimed_task:
|
|
99
|
+
self._session_storage.mark_had_edits(session_id)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
# Don't fail the event if tracking fails
|
|
102
|
+
self.logger.warning(f"Failed to process file edit: {e}")
|
|
103
|
+
|
|
104
|
+
else:
|
|
105
|
+
self.logger.debug(f"AFTER_TOOL [{status}]: {tool_name}")
|
|
106
|
+
|
|
107
|
+
# Execute lifecycle workflow triggers
|
|
108
|
+
if self._workflow_handler:
|
|
109
|
+
try:
|
|
110
|
+
wf_response = self._workflow_handler.handle_all_lifecycles(event)
|
|
111
|
+
if wf_response.decision != "allow":
|
|
112
|
+
return wf_response
|
|
113
|
+
if wf_response.context:
|
|
114
|
+
return wf_response
|
|
115
|
+
except Exception as e:
|
|
116
|
+
self.logger.error(f"Failed to execute lifecycle workflows: {e}", exc_info=True)
|
|
117
|
+
|
|
118
|
+
return HookResponse(decision="allow")
|
|
119
|
+
|
|
120
|
+
def handle_before_tool_selection(self, event: HookEvent) -> HookResponse:
|
|
121
|
+
"""Handle BEFORE_TOOL_SELECTION event (Gemini only)."""
|
|
122
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
123
|
+
|
|
124
|
+
if session_id:
|
|
125
|
+
self.logger.debug(f"BEFORE_TOOL_SELECTION: session {session_id}")
|
|
126
|
+
else:
|
|
127
|
+
self.logger.debug("BEFORE_TOOL_SELECTION")
|
|
128
|
+
|
|
129
|
+
return HookResponse(decision="allow")
|
|
130
|
+
|
|
131
|
+
def _capture_tool_artifact(
|
|
132
|
+
self, session_id: str, tool_name: str, tool_input: dict[str, Any]
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Capture artifacts from tool inputs for edit/write tools.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
session_id: Platform session ID
|
|
138
|
+
tool_name: Name of the tool
|
|
139
|
+
tool_input: Tool input dictionary
|
|
140
|
+
"""
|
|
141
|
+
if not self._artifact_capture_hook:
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
# Get content and file path from tool input
|
|
145
|
+
content = tool_input.get("content") or tool_input.get("new_string")
|
|
146
|
+
file_path = (
|
|
147
|
+
tool_input.get("file_path") or tool_input.get("target_file") or tool_input.get("path")
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if not content:
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
# Skip internal .gobby files
|
|
154
|
+
if file_path and ".gobby/" in str(file_path):
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
# Detect language from file extension
|
|
158
|
+
language = ""
|
|
159
|
+
if file_path:
|
|
160
|
+
ext_map = {
|
|
161
|
+
".py": "python",
|
|
162
|
+
".js": "javascript",
|
|
163
|
+
".ts": "typescript",
|
|
164
|
+
".tsx": "tsx",
|
|
165
|
+
".jsx": "jsx",
|
|
166
|
+
".rs": "rust",
|
|
167
|
+
".go": "go",
|
|
168
|
+
".java": "java",
|
|
169
|
+
".rb": "ruby",
|
|
170
|
+
".sh": "bash",
|
|
171
|
+
".yaml": "yaml",
|
|
172
|
+
".yml": "yaml",
|
|
173
|
+
".json": "json",
|
|
174
|
+
".md": "markdown",
|
|
175
|
+
".sql": "sql",
|
|
176
|
+
".html": "html",
|
|
177
|
+
".css": "css",
|
|
178
|
+
}
|
|
179
|
+
for ext, lang in ext_map.items():
|
|
180
|
+
if str(file_path).endswith(ext):
|
|
181
|
+
language = lang
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
# Wrap content as markdown code block for process_message
|
|
185
|
+
# This reuses the deduplication and classification logic
|
|
186
|
+
markdown_content = f"```{language}\n{content}\n```"
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
self._artifact_capture_hook.process_message(
|
|
190
|
+
session_id=session_id,
|
|
191
|
+
role="assistant",
|
|
192
|
+
content=markdown_content,
|
|
193
|
+
)
|
|
194
|
+
self.logger.debug(f"Captured artifact from {tool_name}: {file_path or 'unknown'}")
|
|
195
|
+
except Exception as e:
|
|
196
|
+
self.logger.warning(f"Failed to capture artifact from {tool_name}: {e}")
|
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,
|
|
@@ -382,6 +386,8 @@ class HookManager:
|
|
|
382
386
|
message_manager=self._message_manager,
|
|
383
387
|
skill_manager=self._skill_manager,
|
|
384
388
|
skills_config=self._config.skills if self._config else None,
|
|
389
|
+
artifact_capture_hook=self._artifact_capture_hook,
|
|
390
|
+
workflow_config=self._config.workflow if self._config else None,
|
|
385
391
|
get_machine_id=self.get_machine_id,
|
|
386
392
|
resolve_project_id=self._resolve_project_id,
|
|
387
393
|
logger=self.logger,
|
|
@@ -644,7 +650,21 @@ class HookManager:
|
|
|
644
650
|
# Copy session metadata from event to response for adapter injection
|
|
645
651
|
# The adapter reads response.metadata to inject session info into agent context
|
|
646
652
|
if event.metadata.get("_platform_session_id"):
|
|
647
|
-
|
|
653
|
+
platform_session_id = event.metadata["_platform_session_id"]
|
|
654
|
+
response.metadata["session_id"] = platform_session_id
|
|
655
|
+
# Look up seq_num for session_ref (#N format)
|
|
656
|
+
if self._session_storage:
|
|
657
|
+
session_obj = self._session_storage.get(platform_session_id)
|
|
658
|
+
if session_obj and session_obj.seq_num:
|
|
659
|
+
response.metadata["session_ref"] = f"#{session_obj.seq_num}"
|
|
660
|
+
|
|
661
|
+
# Track first hook per session for token optimization
|
|
662
|
+
# Adapters use this flag to inject full metadata only on first hook
|
|
663
|
+
session_key = f"{platform_session_id}:{event.source.value}"
|
|
664
|
+
is_first = session_key not in self._injected_sessions
|
|
665
|
+
if is_first:
|
|
666
|
+
self._injected_sessions.add(session_key)
|
|
667
|
+
response.metadata["_first_hook_for_session"] = is_first
|
|
648
668
|
if event.session_id: # external_id (e.g., Claude Code's session UUID)
|
|
649
669
|
response.metadata["external_id"] = event.session_id
|
|
650
670
|
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}")
|