htmlgraph 0.25.0__py3-none-any.whl → 0.26.1__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.
- htmlgraph/__init__.py +1 -1
- htmlgraph/api/main.py +193 -45
- htmlgraph/api/templates/dashboard.html +11 -0
- htmlgraph/api/templates/partials/activity-feed.html +458 -8
- htmlgraph/dashboard.html +41 -0
- htmlgraph/db/schema.py +254 -4
- htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/hooks/.htmlgraph/agents.json +72 -0
- htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
- htmlgraph/hooks/cigs_pretool_enforcer.py +2 -2
- htmlgraph/hooks/concurrent_sessions.py +208 -0
- htmlgraph/hooks/context.py +57 -10
- htmlgraph/hooks/drift_handler.py +24 -20
- htmlgraph/hooks/event_tracker.py +204 -177
- htmlgraph/hooks/orchestrator.py +6 -4
- htmlgraph/hooks/orchestrator_reflector.py +4 -4
- htmlgraph/hooks/pretooluse.py +3 -6
- htmlgraph/hooks/prompt_analyzer.py +14 -25
- htmlgraph/hooks/session_handler.py +123 -69
- htmlgraph/hooks/state_manager.py +7 -4
- htmlgraph/hooks/validator.py +15 -11
- htmlgraph/orchestration/headless_spawner.py +322 -15
- htmlgraph/orchestration/live_events.py +377 -0
- {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/dashboard.html +41 -0
- {htmlgraph-0.25.0.dist-info → htmlgraph-0.26.1.dist-info}/METADATA +1 -1
- {htmlgraph-0.25.0.dist-info → htmlgraph-0.26.1.dist-info}/RECORD +32 -27
- {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.25.0.dist-info → htmlgraph-0.26.1.dist-info}/WHEEL +0 -0
- {htmlgraph-0.25.0.dist-info → htmlgraph-0.26.1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Live Event Publisher for Real-Time WebSocket Streaming.
|
|
3
|
+
|
|
4
|
+
This module provides a centralized way to publish live events that will be
|
|
5
|
+
streamed to connected WebSocket clients in real-time. Events are stored in
|
|
6
|
+
a SQLite table and polled by the WebSocket handler.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from htmlgraph.orchestration.live_events import LiveEventPublisher
|
|
10
|
+
|
|
11
|
+
publisher = LiveEventPublisher()
|
|
12
|
+
publisher.spawner_start("gemini", "Analyze codebase", parent_event_id="evt-123")
|
|
13
|
+
publisher.spawner_phase("gemini", "executing", progress=50)
|
|
14
|
+
publisher.spawner_complete("gemini", success=True, duration=15.3, response="...")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
20
|
+
from datetime import datetime, timezone
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LiveEventPublisher:
|
|
28
|
+
"""
|
|
29
|
+
Publisher for live events that get streamed via WebSocket.
|
|
30
|
+
|
|
31
|
+
Events are written to the live_events table in SQLite and polled
|
|
32
|
+
by the dashboard WebSocket handler for real-time streaming.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, db_path: str | None = None):
|
|
36
|
+
"""
|
|
37
|
+
Initialize the live event publisher.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
db_path: Path to SQLite database. If None, uses default location.
|
|
41
|
+
"""
|
|
42
|
+
self._db_path = db_path
|
|
43
|
+
self._db: Any = None
|
|
44
|
+
|
|
45
|
+
def _get_db(self) -> Any:
|
|
46
|
+
"""Get or create database connection."""
|
|
47
|
+
if self._db is None:
|
|
48
|
+
try:
|
|
49
|
+
from htmlgraph.db.schema import HtmlGraphDB
|
|
50
|
+
|
|
51
|
+
if self._db_path:
|
|
52
|
+
self._db = HtmlGraphDB(self._db_path)
|
|
53
|
+
else:
|
|
54
|
+
# Use project database path from environment or cwd
|
|
55
|
+
project_root = os.getenv("HTMLGRAPH_PROJECT_ROOT", os.getcwd())
|
|
56
|
+
default_path = str(
|
|
57
|
+
Path(project_root) / ".htmlgraph" / "index.sqlite"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Check if database exists
|
|
61
|
+
if not Path(default_path).exists():
|
|
62
|
+
logger.debug(f"Database not found at {default_path}")
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
self._db = HtmlGraphDB(default_path)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.warning(f"Failed to initialize database for live events: {e}")
|
|
68
|
+
return None
|
|
69
|
+
return self._db
|
|
70
|
+
|
|
71
|
+
def _get_session_id(self) -> str | None:
|
|
72
|
+
"""Get current session ID from environment."""
|
|
73
|
+
return os.getenv("HTMLGRAPH_PARENT_SESSION") or os.getenv("CLAUDE_SESSION_ID")
|
|
74
|
+
|
|
75
|
+
def publish(
|
|
76
|
+
self,
|
|
77
|
+
event_type: str,
|
|
78
|
+
event_data: dict[str, Any],
|
|
79
|
+
parent_event_id: str | None = None,
|
|
80
|
+
session_id: str | None = None,
|
|
81
|
+
spawner_type: str | None = None,
|
|
82
|
+
) -> int | None:
|
|
83
|
+
"""
|
|
84
|
+
Publish a live event for WebSocket streaming.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
event_type: Type of event (e.g., spawner_start, spawner_complete)
|
|
88
|
+
event_data: Event payload dictionary
|
|
89
|
+
parent_event_id: Parent event ID for hierarchical linking
|
|
90
|
+
session_id: Session this event belongs to
|
|
91
|
+
spawner_type: Spawner type if applicable (gemini, codex, copilot)
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Live event ID if successful, None otherwise
|
|
95
|
+
"""
|
|
96
|
+
db = self._get_db()
|
|
97
|
+
if db is None:
|
|
98
|
+
logger.debug("Database not available for live events")
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
# Add timestamp to event data if not present
|
|
102
|
+
if "timestamp" not in event_data:
|
|
103
|
+
event_data["timestamp"] = datetime.now(timezone.utc).isoformat()
|
|
104
|
+
|
|
105
|
+
# Use session from environment if not provided
|
|
106
|
+
if session_id is None:
|
|
107
|
+
session_id = self._get_session_id()
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
result: int | None = db.insert_live_event(
|
|
111
|
+
event_type=event_type,
|
|
112
|
+
event_data=event_data,
|
|
113
|
+
parent_event_id=parent_event_id,
|
|
114
|
+
session_id=session_id,
|
|
115
|
+
spawner_type=spawner_type,
|
|
116
|
+
)
|
|
117
|
+
return result
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.warning(f"Failed to publish live event: {e}")
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
def spawner_start(
|
|
123
|
+
self,
|
|
124
|
+
spawner_type: str,
|
|
125
|
+
prompt: str,
|
|
126
|
+
parent_event_id: str | None = None,
|
|
127
|
+
model: str | None = None,
|
|
128
|
+
session_id: str | None = None,
|
|
129
|
+
) -> int | None:
|
|
130
|
+
"""
|
|
131
|
+
Publish a spawner start event.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
spawner_type: Type of spawner (gemini, codex, copilot)
|
|
135
|
+
prompt: Task prompt being executed
|
|
136
|
+
parent_event_id: Parent delegation event ID
|
|
137
|
+
model: Model being used (optional)
|
|
138
|
+
session_id: Session ID (optional, auto-detected)
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Live event ID if successful
|
|
142
|
+
"""
|
|
143
|
+
event_data = {
|
|
144
|
+
"spawner_type": spawner_type,
|
|
145
|
+
"prompt_preview": prompt[:200] if prompt else "",
|
|
146
|
+
"prompt_length": len(prompt) if prompt else 0,
|
|
147
|
+
"status": "started",
|
|
148
|
+
"phase": "initializing",
|
|
149
|
+
}
|
|
150
|
+
if model:
|
|
151
|
+
event_data["model"] = model
|
|
152
|
+
|
|
153
|
+
return self.publish(
|
|
154
|
+
event_type="spawner_start",
|
|
155
|
+
event_data=event_data,
|
|
156
|
+
parent_event_id=parent_event_id,
|
|
157
|
+
session_id=session_id,
|
|
158
|
+
spawner_type=spawner_type,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def spawner_phase(
|
|
162
|
+
self,
|
|
163
|
+
spawner_type: str,
|
|
164
|
+
phase: str,
|
|
165
|
+
progress: int | None = None,
|
|
166
|
+
details: str | None = None,
|
|
167
|
+
parent_event_id: str | None = None,
|
|
168
|
+
session_id: str | None = None,
|
|
169
|
+
) -> int | None:
|
|
170
|
+
"""
|
|
171
|
+
Publish a spawner phase update event.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
spawner_type: Type of spawner (gemini, codex, copilot)
|
|
175
|
+
phase: Current phase (e.g., "executing", "processing", "streaming")
|
|
176
|
+
progress: Progress percentage (0-100) if applicable
|
|
177
|
+
details: Additional details about the phase
|
|
178
|
+
parent_event_id: Parent delegation event ID
|
|
179
|
+
session_id: Session ID (optional, auto-detected)
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Live event ID if successful
|
|
183
|
+
"""
|
|
184
|
+
event_data: dict[str, Any] = {
|
|
185
|
+
"spawner_type": spawner_type,
|
|
186
|
+
"phase": phase,
|
|
187
|
+
"status": "in_progress",
|
|
188
|
+
}
|
|
189
|
+
if progress is not None:
|
|
190
|
+
event_data["progress"] = progress
|
|
191
|
+
if details:
|
|
192
|
+
event_data["details"] = details[:200]
|
|
193
|
+
|
|
194
|
+
return self.publish(
|
|
195
|
+
event_type="spawner_phase",
|
|
196
|
+
event_data=event_data,
|
|
197
|
+
parent_event_id=parent_event_id,
|
|
198
|
+
session_id=session_id,
|
|
199
|
+
spawner_type=spawner_type,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
def spawner_complete(
|
|
203
|
+
self,
|
|
204
|
+
spawner_type: str,
|
|
205
|
+
success: bool,
|
|
206
|
+
duration_seconds: float | None = None,
|
|
207
|
+
response_preview: str | None = None,
|
|
208
|
+
tokens_used: int | None = None,
|
|
209
|
+
error: str | None = None,
|
|
210
|
+
parent_event_id: str | None = None,
|
|
211
|
+
session_id: str | None = None,
|
|
212
|
+
) -> int | None:
|
|
213
|
+
"""
|
|
214
|
+
Publish a spawner completion event.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
spawner_type: Type of spawner (gemini, codex, copilot)
|
|
218
|
+
success: Whether the spawner completed successfully
|
|
219
|
+
duration_seconds: Execution duration in seconds
|
|
220
|
+
response_preview: Preview of the response (first 200 chars)
|
|
221
|
+
tokens_used: Number of tokens used
|
|
222
|
+
error: Error message if failed
|
|
223
|
+
parent_event_id: Parent delegation event ID
|
|
224
|
+
session_id: Session ID (optional, auto-detected)
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Live event ID if successful
|
|
228
|
+
"""
|
|
229
|
+
event_data: dict[str, Any] = {
|
|
230
|
+
"spawner_type": spawner_type,
|
|
231
|
+
"success": success,
|
|
232
|
+
"status": "completed" if success else "failed",
|
|
233
|
+
"phase": "done",
|
|
234
|
+
}
|
|
235
|
+
if duration_seconds is not None:
|
|
236
|
+
event_data["duration_seconds"] = round(duration_seconds, 2)
|
|
237
|
+
if response_preview:
|
|
238
|
+
event_data["response_preview"] = response_preview[:200]
|
|
239
|
+
if tokens_used is not None:
|
|
240
|
+
event_data["tokens_used"] = tokens_used
|
|
241
|
+
if error:
|
|
242
|
+
event_data["error"] = error[:500]
|
|
243
|
+
|
|
244
|
+
return self.publish(
|
|
245
|
+
event_type="spawner_complete",
|
|
246
|
+
event_data=event_data,
|
|
247
|
+
parent_event_id=parent_event_id,
|
|
248
|
+
session_id=session_id,
|
|
249
|
+
spawner_type=spawner_type,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def spawner_tool_use(
|
|
253
|
+
self,
|
|
254
|
+
spawner_type: str,
|
|
255
|
+
tool_name: str,
|
|
256
|
+
tool_input: dict[str, Any] | None = None,
|
|
257
|
+
parent_event_id: str | None = None,
|
|
258
|
+
session_id: str | None = None,
|
|
259
|
+
) -> int | None:
|
|
260
|
+
"""
|
|
261
|
+
Publish a spawner tool use event (when spawned AI uses a tool).
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
spawner_type: Type of spawner (gemini, codex, copilot)
|
|
265
|
+
tool_name: Name of the tool being used
|
|
266
|
+
tool_input: Tool input parameters
|
|
267
|
+
parent_event_id: Parent delegation event ID
|
|
268
|
+
session_id: Session ID (optional, auto-detected)
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Live event ID if successful
|
|
272
|
+
"""
|
|
273
|
+
event_data: dict[str, Any] = {
|
|
274
|
+
"spawner_type": spawner_type,
|
|
275
|
+
"tool_name": tool_name,
|
|
276
|
+
"status": "tool_use",
|
|
277
|
+
"phase": "executing",
|
|
278
|
+
}
|
|
279
|
+
if tool_input:
|
|
280
|
+
# Truncate tool input for preview
|
|
281
|
+
input_str = json.dumps(tool_input)
|
|
282
|
+
event_data["tool_input_preview"] = input_str[:200]
|
|
283
|
+
|
|
284
|
+
return self.publish(
|
|
285
|
+
event_type="spawner_tool_use",
|
|
286
|
+
event_data=event_data,
|
|
287
|
+
parent_event_id=parent_event_id,
|
|
288
|
+
session_id=session_id,
|
|
289
|
+
spawner_type=spawner_type,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
def spawner_message(
|
|
293
|
+
self,
|
|
294
|
+
spawner_type: str,
|
|
295
|
+
message: str,
|
|
296
|
+
role: str = "assistant",
|
|
297
|
+
parent_event_id: str | None = None,
|
|
298
|
+
session_id: str | None = None,
|
|
299
|
+
) -> int | None:
|
|
300
|
+
"""
|
|
301
|
+
Publish a spawner message event (when spawned AI sends a message).
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
spawner_type: Type of spawner (gemini, codex, copilot)
|
|
305
|
+
message: Message content
|
|
306
|
+
role: Message role (assistant, user, system)
|
|
307
|
+
parent_event_id: Parent delegation event ID
|
|
308
|
+
session_id: Session ID (optional, auto-detected)
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Live event ID if successful
|
|
312
|
+
"""
|
|
313
|
+
event_data = {
|
|
314
|
+
"spawner_type": spawner_type,
|
|
315
|
+
"message_preview": message[:200] if message else "",
|
|
316
|
+
"message_length": len(message) if message else 0,
|
|
317
|
+
"role": role,
|
|
318
|
+
"status": "streaming",
|
|
319
|
+
"phase": "responding",
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return self.publish(
|
|
323
|
+
event_type="spawner_message",
|
|
324
|
+
event_data=event_data,
|
|
325
|
+
parent_event_id=parent_event_id,
|
|
326
|
+
session_id=session_id,
|
|
327
|
+
spawner_type=spawner_type,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
# Global singleton instance for convenience
|
|
332
|
+
_publisher: LiveEventPublisher | None = None
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def get_publisher(db_path: str | None = None) -> LiveEventPublisher:
|
|
336
|
+
"""
|
|
337
|
+
Get the global LiveEventPublisher instance.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
db_path: Optional database path (only used on first call)
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
LiveEventPublisher instance
|
|
344
|
+
"""
|
|
345
|
+
global _publisher
|
|
346
|
+
if _publisher is None:
|
|
347
|
+
_publisher = LiveEventPublisher(db_path)
|
|
348
|
+
return _publisher
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def publish_live_event(
|
|
352
|
+
event_type: str,
|
|
353
|
+
event_data: dict[str, Any],
|
|
354
|
+
parent_event_id: str | None = None,
|
|
355
|
+
session_id: str | None = None,
|
|
356
|
+
spawner_type: str | None = None,
|
|
357
|
+
) -> int | None:
|
|
358
|
+
"""
|
|
359
|
+
Convenience function to publish a live event using the global publisher.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
event_type: Type of event
|
|
363
|
+
event_data: Event payload
|
|
364
|
+
parent_event_id: Parent event ID
|
|
365
|
+
session_id: Session ID
|
|
366
|
+
spawner_type: Spawner type
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
Live event ID if successful
|
|
370
|
+
"""
|
|
371
|
+
return get_publisher().publish(
|
|
372
|
+
event_type=event_type,
|
|
373
|
+
event_data=event_data,
|
|
374
|
+
parent_event_id=parent_event_id,
|
|
375
|
+
session_id=session_id,
|
|
376
|
+
spawner_type=spawner_type,
|
|
377
|
+
)
|
|
@@ -967,6 +967,20 @@
|
|
|
967
967
|
.event-source-indicator.spike::before { background: #00C853; }
|
|
968
968
|
.event-source-indicator.delegation::before { background: #FF9100; }
|
|
969
969
|
|
|
970
|
+
/* Model tracking badges - Display which AI model executed the event */
|
|
971
|
+
.badge.model {
|
|
972
|
+
padding: 0.25rem 0.5rem;
|
|
973
|
+
font-size: 0.55rem;
|
|
974
|
+
font-weight: 600;
|
|
975
|
+
display: inline-flex;
|
|
976
|
+
align-items: center;
|
|
977
|
+
gap: 0.3rem;
|
|
978
|
+
}
|
|
979
|
+
.badge.model-haiku { background: #00BCD4; color: white; border-color: #00BCD4; } /* Cyan for Haiku */
|
|
980
|
+
.badge.model-sonnet { background: #9C27B0; color: white; border-color: #9C27B0; } /* Purple for Sonnet */
|
|
981
|
+
.badge.model-opus { background: #FFC107; color: #000; border-color: #FFC107; } /* Amber/Gold for Opus */
|
|
982
|
+
.badge.model-default { background: #9E9E9E; color: white; border-color: #9E9E9E; } /* Gray for unknown */
|
|
983
|
+
|
|
970
984
|
.card-path {
|
|
971
985
|
font-family: 'JetBrains Mono', monospace;
|
|
972
986
|
font-size: 0.625rem;
|
|
@@ -5585,6 +5599,26 @@ ${node.ts ? node.ts.slice(0, 19).replace('T', ' ') : ''}`;
|
|
|
5585
5599
|
<ol class="activity-list" reversed>
|
|
5586
5600
|
`;
|
|
5587
5601
|
|
|
5602
|
+
// Model name formatter - Extract short name from full model ID
|
|
5603
|
+
function formatModelName(model) {
|
|
5604
|
+
if (!model) return null;
|
|
5605
|
+
const modelStr = String(model).toLowerCase();
|
|
5606
|
+
if (modelStr.includes('haiku')) return 'Haiku';
|
|
5607
|
+
if (modelStr.includes('sonnet')) return 'Sonnet 4.5';
|
|
5608
|
+
if (modelStr.includes('opus')) return 'Opus 4.5';
|
|
5609
|
+
return null; // Unknown model
|
|
5610
|
+
}
|
|
5611
|
+
|
|
5612
|
+
// Determine model badge CSS class
|
|
5613
|
+
function getModelBadgeClass(model) {
|
|
5614
|
+
if (!model) return 'model-default';
|
|
5615
|
+
const modelStr = String(model).toLowerCase();
|
|
5616
|
+
if (modelStr.includes('haiku')) return 'model-haiku';
|
|
5617
|
+
if (modelStr.includes('sonnet')) return 'model-sonnet';
|
|
5618
|
+
if (modelStr.includes('opus')) return 'model-opus';
|
|
5619
|
+
return 'model-default';
|
|
5620
|
+
}
|
|
5621
|
+
|
|
5588
5622
|
// Render helper function
|
|
5589
5623
|
function renderEvent(item, isChild = false, depth = 0) {
|
|
5590
5624
|
const ts = item.timestamp ? new Date(item.timestamp).toLocaleString() : 'Unknown';
|
|
@@ -5623,6 +5657,12 @@ ${node.ts ? node.ts.slice(0, 19).replace('T', ' ') : ''}`;
|
|
|
5623
5657
|
const agentBadge = agentClass ? `<span class="badge agent ${agentClass}">${agentId}</span>` :
|
|
5624
5658
|
`<span class="badge">${agentId}</span>`;
|
|
5625
5659
|
|
|
5660
|
+
// Model badge - display which AI model executed the event
|
|
5661
|
+
const model = item.model || null;
|
|
5662
|
+
const modelName = formatModelName(model);
|
|
5663
|
+
const modelBadgeClass = getModelBadgeClass(model);
|
|
5664
|
+
const modelBadge = modelName ? `<span class="badge model ${modelBadgeClass}">${modelName}</span>` : '';
|
|
5665
|
+
|
|
5626
5666
|
// Add expand icon for parents
|
|
5627
5667
|
const expandIcon = hasChildren ? '<span class="expand-icon"></span>' : '';
|
|
5628
5668
|
const indent = isChild ? 'padding-left: ' + (1.5 + depth * 1.5) + 'rem;' : '';
|
|
@@ -5638,6 +5678,7 @@ ${node.ts ? node.ts.slice(0, 19).replace('T', ' ') : ''}`;
|
|
|
5638
5678
|
${sourceBadge}
|
|
5639
5679
|
<span class="activity-tool">${tool}</span>
|
|
5640
5680
|
${agentBadge}
|
|
5681
|
+
${modelBadge}
|
|
5641
5682
|
</div>
|
|
5642
5683
|
<div class="activity-content">${escapeHtml(content.substring(0, 200))}${content.length > 200 ? '...' : ''}</div>
|
|
5643
5684
|
</li>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: htmlgraph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.1
|
|
4
4
|
Summary: HTML is All You Need - Graph database on web standards
|
|
5
5
|
Project-URL: Homepage, https://github.com/Shakes-tzd/htmlgraph
|
|
6
6
|
Project-URL: Documentation, https://github.com/Shakes-tzd/htmlgraph#readme
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
htmlgraph/__init__.py,sha256=
|
|
1
|
+
htmlgraph/__init__.py,sha256=RYfmZmOwB0zGe9iNE5PT0RVJCzr3Q0ez7-jkVaw8LCg,5717
|
|
2
2
|
htmlgraph/agent_detection.py,sha256=wEmrDv4hssPX2OkEnJZBHPbalxcaloiJF_hOOow_5WE,3511
|
|
3
3
|
htmlgraph/agent_registry.py,sha256=Usa_35by7p5gtpvHO7K3AcGimnorw-FzgPVa3cWTQ58,9448
|
|
4
4
|
htmlgraph/agents.py,sha256=Yvu6x1nOfrW2WhRTAHiCuSpvqoVJXx1Mkzd59kwEczw,33466
|
|
@@ -10,7 +10,7 @@ htmlgraph/cli_framework.py,sha256=YfAjTGIECmv_uNpkenYyc9_iKd3jXIG5OstEKpIGWJM,35
|
|
|
10
10
|
htmlgraph/config.py,sha256=Pv-ska7GqmFxCTyR--3jBGcFU89-i_EshHVZnFjTkHo,3640
|
|
11
11
|
htmlgraph/context_analytics.py,sha256=CaLu0o2uSr6rlBM5YeaFZe7grgsy7_Hx10qdXuNcdao,11344
|
|
12
12
|
htmlgraph/converter.py,sha256=8dSp5XKrU-5_gk5AZE2PTmeNAoqSN_RoMptkReTg-cs,24297
|
|
13
|
-
htmlgraph/dashboard.html,sha256=
|
|
13
|
+
htmlgraph/dashboard.html,sha256=BzSW2Bn1N1IrMlD4MNklR2MhhUMxK9jsshnKFqiCMwo,289649
|
|
14
14
|
htmlgraph/dashboard.html.backup,sha256=MUT6SaYnazoyDcvHz5hN1omYswyIoUfeoZLf2M_iblo,251268
|
|
15
15
|
htmlgraph/dashboard.html.bak,sha256=cDDMzwxkoDFFOd8V0VDCa_yigDYTvpPrP7RMoiCJ5Zo,276374
|
|
16
16
|
htmlgraph/dashboard.html.bak2,sha256=Gm-4u_HV9WTQubgdFX5S5itW0u2f94N0F1R44UVjBRw,279532
|
|
@@ -71,14 +71,14 @@ htmlgraph/analytics/cross_session.py,sha256=BmTNqyt1qYjKbq5sXDYGxp85G2XB2aHkIvdq
|
|
|
71
71
|
htmlgraph/analytics/dependency.py,sha256=7Qc5xe0b1QE_P6-l4Z0WtstEmEXc4ZGNbyIEBMoABys,25757
|
|
72
72
|
htmlgraph/analytics/work_type.py,sha256=SrZvY_RVq70uvzl3Ky7I3LHgOMduW_0vdSsk5yUZRG4,19293
|
|
73
73
|
htmlgraph/api/__init__.py,sha256=PPFJ5QSv-Zei8U0yGOSs8dJKtUQMeloEqsxVBfzNbbA,105
|
|
74
|
-
htmlgraph/api/main.py,sha256=
|
|
74
|
+
htmlgraph/api/main.py,sha256=AfJ6YFY7H0JV5npmsv9V_JUVJTk4pshpO3snj6XQReg,86918
|
|
75
75
|
htmlgraph/api/static/htmx.min.js,sha256=0VEHzH8ECp6DsbZhdv2SetQLXgJVgToD-Mz-7UbuQrA,48036
|
|
76
76
|
htmlgraph/api/static/style-redesign.css,sha256=zp0ggDJHz81FN5SW5tRcWXPK_jVNCAJyqKhwlZkBfnQ,26759
|
|
77
77
|
htmlgraph/api/static/style.css,sha256=6nUzPvf3Xri0vxbEIunrbJkujP6k3HrM3KC5nrkOjyw,20022
|
|
78
78
|
htmlgraph/api/templates/dashboard-redesign.html,sha256=Y0w6qoXEPVmWETxi-_GwBNiWlCMP1Ur0OD5EL5uH7P4,30027
|
|
79
|
-
htmlgraph/api/templates/dashboard.html,sha256=
|
|
79
|
+
htmlgraph/api/templates/dashboard.html,sha256=3I2x-YQmPdsuWGqgxqdnIG4h-iy9a8s1VeLRcHyMNcA,31596
|
|
80
80
|
htmlgraph/api/templates/partials/activity-feed-hierarchical.html,sha256=28N1IeL84Fh4uYv6DxwivnzCm08LoXf6OWR2GDVvAnY,14175
|
|
81
|
-
htmlgraph/api/templates/partials/activity-feed.html,sha256=
|
|
81
|
+
htmlgraph/api/templates/partials/activity-feed.html,sha256=RbMLegDlUR0pWRM9RDiFlBuWa5e_0ZJAMfwapTVSa-s,30840
|
|
82
82
|
htmlgraph/api/templates/partials/agents-redesign.html,sha256=32sZ596ShucaLW7kUh95mB_znJeK7p8eyJ5F-wxkXzw,10266
|
|
83
83
|
htmlgraph/api/templates/partials/agents.html,sha256=32sZ596ShucaLW7kUh95mB_znJeK7p8eyJ5F-wxkXzw,10266
|
|
84
84
|
htmlgraph/api/templates/partials/event-traces.html,sha256=asZ_TjZT_xqbAqN2NU3hbdxExsQFL7t9LLaXw5NrOfU,10417
|
|
@@ -138,7 +138,7 @@ htmlgraph/cost_analysis/__init__.py,sha256=kuAA4Fd0gUplOWOiqs3CF4eO1wwekGs_3DaiF
|
|
|
138
138
|
htmlgraph/cost_analysis/analyzer.py,sha256=TDKgRXeOQ-td5LZhkFob4ajEf-JssSCGKlMS3GysYJU,15040
|
|
139
139
|
htmlgraph/db/__init__.py,sha256=1yWTyWrN2kcmW6mVKCUf4POSUXTOSaAljwEyyC0Xs1w,1033
|
|
140
140
|
htmlgraph/db/queries.py,sha256=FtqOIBJC0EociEYNJr6gTVROqd9m5yZdVGYLxXNVSdk,22608
|
|
141
|
-
htmlgraph/db/schema.py,sha256=
|
|
141
|
+
htmlgraph/db/schema.py,sha256=74jpfG_AS-3sRtDJD2_EMAI3LLPwFUrPuxjbAgYiiw0,60206
|
|
142
142
|
htmlgraph/docs/API_REFERENCE.md,sha256=LYsYeHinF6GEsjMvrgdQSbgmMPsOh4ZQ1WK11niqNvo,17862
|
|
143
143
|
htmlgraph/docs/HTTP_API.md,sha256=IrX-wZckFn-K3ztCVcT-zyGqJL2TtY1qYV7dUuC7kzc,13344
|
|
144
144
|
htmlgraph/docs/INTEGRATION_GUIDE.md,sha256=uNNM0ipY0bFAkZaL1CkvnA_Wt2MVPGRBdA4927cZaHo,16910
|
|
@@ -164,14 +164,15 @@ htmlgraph/extensions/gemini/hooks/scripts/session-end.sh,sha256=yQzSKfxBzuMS-U_x
|
|
|
164
164
|
htmlgraph/extensions/gemini/hooks/scripts/session-start.sh,sha256=thpCbfE8WD8huFU7mUMas-gWAk6BsHtYLaKlIHusNRg,2670
|
|
165
165
|
htmlgraph/hooks/__init__.py,sha256=FxVdnSYMNTjaKyOJ-euvyq9qYy5LZvGH5eIRqw0SpyQ,1071
|
|
166
166
|
htmlgraph/hooks/bootstrap.py,sha256=IiahOTOZolhIHF-yDfolmfT2kR5_EH_E9A7BBFKy2lI,5194
|
|
167
|
-
htmlgraph/hooks/cigs_pretool_enforcer.py,sha256=
|
|
168
|
-
htmlgraph/hooks/
|
|
169
|
-
htmlgraph/hooks/
|
|
170
|
-
htmlgraph/hooks/
|
|
167
|
+
htmlgraph/hooks/cigs_pretool_enforcer.py,sha256=Lyp4DDaw_sVHEcW-kzdegldyfXjvVD25zRGamq0v-sU,12722
|
|
168
|
+
htmlgraph/hooks/concurrent_sessions.py,sha256=qOiwDfynphVG0-2pVBakEzOwMORU8ebN1gMjcN4S0z0,6476
|
|
169
|
+
htmlgraph/hooks/context.py,sha256=DgZdg73ZDYZe-tQAKKgx5RZGEkO0TO319-T_4DMZ3J4,11481
|
|
170
|
+
htmlgraph/hooks/drift_handler.py,sha256=QckL5U5ooku51kI6mppGLsXzaKVt1Yx5uNu-iXZrgSk,17602
|
|
171
|
+
htmlgraph/hooks/event_tracker.py,sha256=OOpUQ8oN_Hsc_yDXE0NA98vh5urfj4zLTcf6W-gmDvE,38026
|
|
171
172
|
htmlgraph/hooks/hooks-config.example.json,sha256=tXpk-U-FZzGOoNJK2uiDMbIHCYEHA794J-El0fBwkqg,197
|
|
172
173
|
htmlgraph/hooks/installer.py,sha256=nOctCFDEV7BEh7ZzxNY-apu1KZG0SHPMq74UPIOChqY,11756
|
|
173
|
-
htmlgraph/hooks/orchestrator.py,sha256=
|
|
174
|
-
htmlgraph/hooks/orchestrator_reflector.py,sha256=
|
|
174
|
+
htmlgraph/hooks/orchestrator.py,sha256=wV9n4lHxpxfvGnipFFfrtzmhonijHDo59HE8lNFYtog,19837
|
|
175
|
+
htmlgraph/hooks/orchestrator_reflector.py,sha256=k6qMjVsr03kUPYE-9zlM4e9CT_NJicZ4IcipGVHl-E4,5199
|
|
175
176
|
htmlgraph/hooks/post-checkout.sh,sha256=Hsr5hqD54jisGbtqf7-Z-G_b6XNGcee_CZRYaKYzWzU,615
|
|
176
177
|
htmlgraph/hooks/post-commit.sh,sha256=if65jNGZnEWsZPq_iYDNYunrZ1cmjPUEUbh6_4vfpOE,511
|
|
177
178
|
htmlgraph/hooks/post-merge.sh,sha256=gq-EeFLhDUVp-J2jyWMBVFcB0pdmH54Wu1SW_Gn-s2I,541
|
|
@@ -180,14 +181,17 @@ htmlgraph/hooks/post_tool_use_handler.py,sha256=e0L8X4sU2H167HH96CgV6iYojd02AI9u
|
|
|
180
181
|
htmlgraph/hooks/posttooluse.py,sha256=3usICpqlIdLKsijYY61BP2QezsB45MZ6evwFSobC9Yo,12822
|
|
181
182
|
htmlgraph/hooks/pre-commit.sh,sha256=gTpbnHIBFxpAl7-REhXoS0NI4Pmlqo9pQEMEngTAU_A,3865
|
|
182
183
|
htmlgraph/hooks/pre-push.sh,sha256=rNnkG8YmDtyk7OuJHOcbOYQR3MYFneaG6_w2X-Hl8Hs,660
|
|
183
|
-
htmlgraph/hooks/pretooluse.py,sha256=
|
|
184
|
-
htmlgraph/hooks/prompt_analyzer.py,sha256=
|
|
185
|
-
htmlgraph/hooks/session_handler.py,sha256=
|
|
186
|
-
htmlgraph/hooks/state_manager.py,sha256=
|
|
184
|
+
htmlgraph/hooks/pretooluse.py,sha256=WEs42ZMMs2dBU0FIcwGRII06q_Wj4R6bgYQ3k0DJxNs,24929
|
|
185
|
+
htmlgraph/hooks/prompt_analyzer.py,sha256=7WAD68JFVv0ddUKgHVyoXWJ41KdHDXrbpSnt1l2yTaA,22792
|
|
186
|
+
htmlgraph/hooks/session_handler.py,sha256=9GcawryKXnl7ikDuuRGQurq9449E6euneEPoMN8LzYo,20971
|
|
187
|
+
htmlgraph/hooks/state_manager.py,sha256=ni86zHChSqzlr2CnDoTDwJ9a_zbNqRlPIdQkhLIUEKQ,17645
|
|
187
188
|
htmlgraph/hooks/subagent_stop.py,sha256=V08sL3T_q1EJLrZWRtsB_Du1LOLIPvDlStUDB4R0xJs,9078
|
|
188
189
|
htmlgraph/hooks/task_enforcer.py,sha256=eXaQaNibUu1ty78t9M46h0Hhw2M1Fi1lnqCpUdsBZ9Y,7927
|
|
189
190
|
htmlgraph/hooks/task_validator.py,sha256=GwEoqL2lptPWQqckkfl0N-Auc7TtHiyRlOf6p7HcoIo,5438
|
|
190
|
-
htmlgraph/hooks/validator.py,sha256=
|
|
191
|
+
htmlgraph/hooks/validator.py,sha256=g48Urbk6AJG5eqTaimhEaLsrSMqit6QTrKj5q_AaGwQ,17595
|
|
192
|
+
htmlgraph/hooks/.htmlgraph/.session-warning-state.json,sha256=Ypc3pKrnqfRDq8vdS8a56MUKROGzrl2Qnvx7t7mRuck,93
|
|
193
|
+
htmlgraph/hooks/.htmlgraph/agents.json,sha256=XDPzwtJlfynbzbWmGSUa2YcgU6ovkre5DbrEZziQkGw,1391
|
|
194
|
+
htmlgraph/hooks/.htmlgraph/index.sqlite,sha256=dYFnI36g5ddfVH2gKiQu2CV7gBAZO2EgfVkRtlGEYlg,225280
|
|
191
195
|
htmlgraph/operations/README.md,sha256=5h0P-aZ3po3h_7mUpuh7jJwOL-NEvtlBlPCbqzvLrIE,1730
|
|
192
196
|
htmlgraph/operations/__init__.py,sha256=uiUUmWG3BAtRlSr-WMS4STB76JKJYs0QPQKR7mRh3Co,1237
|
|
193
197
|
htmlgraph/operations/analytics.py,sha256=XDbGEG5ydrjqM5sXa01wsruT0u9DtOb24VJEPzQS8BM,11583
|
|
@@ -196,7 +200,8 @@ htmlgraph/operations/fastapi_server.py,sha256=OdcUqPHMCO102y9STEtwwfvSOmrPw9IOEF
|
|
|
196
200
|
htmlgraph/operations/hooks.py,sha256=_jRHbjrWNM1fjukEr0TBu6wn90PVfpXtJjicSITqP0A,10037
|
|
197
201
|
htmlgraph/operations/server.py,sha256=cxdSYZU10pNgiNfYk2T84D_40CAFjA9wSVsRfk9Ipm8,8746
|
|
198
202
|
htmlgraph/orchestration/__init__.py,sha256=a9NWy-zWG9T7f7V37ibahniF_enn-31gM4veEJkM8cw,844
|
|
199
|
-
htmlgraph/orchestration/headless_spawner.py,sha256=
|
|
203
|
+
htmlgraph/orchestration/headless_spawner.py,sha256=xlU0-4Tg2buv5pZGHZZ2filDCDGQgBTmrYWqe_6hxZ8,51602
|
|
204
|
+
htmlgraph/orchestration/live_events.py,sha256=aaa-svW1-SWAqs4ZZcv3VRVRx3xpZydsdi2nv9xNeu8,12198
|
|
200
205
|
htmlgraph/orchestration/model_selection.py,sha256=2IArHkde5rKD56aj5Jw9I9Z5iLV0yOgw3QCmNF996DE,11311
|
|
201
206
|
htmlgraph/orchestration/task_coordination.py,sha256=7_oQ4AlHOv14hs6RvLsatJzF-F5gkIbv1EOrmeGPhiw,9699
|
|
202
207
|
htmlgraph/scripts/__init__.py,sha256=a_ef7jnypTH3fzjutKYtUquJospdbA6IOR4o9dgv8Gk,48
|
|
@@ -207,12 +212,12 @@ htmlgraph/templates/AGENTS.md.template,sha256=f96h7V6ygwj-v-fanVI48eYMxR6t_se4be
|
|
|
207
212
|
htmlgraph/templates/CLAUDE.md.template,sha256=h1kG2hTX2XYig2KszsHBfzrwa_4Cfcq2Pj4SwqzeDlM,1984
|
|
208
213
|
htmlgraph/templates/GEMINI.md.template,sha256=gAGzE53Avki87BM_otqy5HdcYCoLsHgqaKjVzNzPMX8,1622
|
|
209
214
|
htmlgraph/templates/orchestration-view.html,sha256=DlS7LlcjH0oO_KYILjuF1X42t8QhKLH4F85rkO54alY,10472
|
|
210
|
-
htmlgraph-0.
|
|
211
|
-
htmlgraph-0.
|
|
212
|
-
htmlgraph-0.
|
|
213
|
-
htmlgraph-0.
|
|
214
|
-
htmlgraph-0.
|
|
215
|
-
htmlgraph-0.
|
|
216
|
-
htmlgraph-0.
|
|
217
|
-
htmlgraph-0.
|
|
218
|
-
htmlgraph-0.
|
|
215
|
+
htmlgraph-0.26.1.data/data/htmlgraph/dashboard.html,sha256=BzSW2Bn1N1IrMlD4MNklR2MhhUMxK9jsshnKFqiCMwo,289649
|
|
216
|
+
htmlgraph-0.26.1.data/data/htmlgraph/styles.css,sha256=oDUSC8jG-V-hKojOBO9J88hxAeY2wJrBYTq0uCwX_Y4,7135
|
|
217
|
+
htmlgraph-0.26.1.data/data/htmlgraph/templates/AGENTS.md.template,sha256=f96h7V6ygwj-v-fanVI48eYMxR6t_se4bet1H4ZsDpI,7642
|
|
218
|
+
htmlgraph-0.26.1.data/data/htmlgraph/templates/CLAUDE.md.template,sha256=h1kG2hTX2XYig2KszsHBfzrwa_4Cfcq2Pj4SwqzeDlM,1984
|
|
219
|
+
htmlgraph-0.26.1.data/data/htmlgraph/templates/GEMINI.md.template,sha256=gAGzE53Avki87BM_otqy5HdcYCoLsHgqaKjVzNzPMX8,1622
|
|
220
|
+
htmlgraph-0.26.1.dist-info/METADATA,sha256=DqCdEPT19M4zlGGKNr1lCEWByCE_th6tnffP1dQsA9Y,10236
|
|
221
|
+
htmlgraph-0.26.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
222
|
+
htmlgraph-0.26.1.dist-info/entry_points.txt,sha256=EaUbjA_bbDwEO_XDLEGMeK8aQP-ZnHiUTkLshyKDyB8,98
|
|
223
|
+
htmlgraph-0.26.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|