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.
Files changed (32) hide show
  1. htmlgraph/__init__.py +1 -1
  2. htmlgraph/api/main.py +193 -45
  3. htmlgraph/api/templates/dashboard.html +11 -0
  4. htmlgraph/api/templates/partials/activity-feed.html +458 -8
  5. htmlgraph/dashboard.html +41 -0
  6. htmlgraph/db/schema.py +254 -4
  7. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  8. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  9. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  10. htmlgraph/hooks/cigs_pretool_enforcer.py +2 -2
  11. htmlgraph/hooks/concurrent_sessions.py +208 -0
  12. htmlgraph/hooks/context.py +57 -10
  13. htmlgraph/hooks/drift_handler.py +24 -20
  14. htmlgraph/hooks/event_tracker.py +204 -177
  15. htmlgraph/hooks/orchestrator.py +6 -4
  16. htmlgraph/hooks/orchestrator_reflector.py +4 -4
  17. htmlgraph/hooks/pretooluse.py +3 -6
  18. htmlgraph/hooks/prompt_analyzer.py +14 -25
  19. htmlgraph/hooks/session_handler.py +123 -69
  20. htmlgraph/hooks/state_manager.py +7 -4
  21. htmlgraph/hooks/validator.py +15 -11
  22. htmlgraph/orchestration/headless_spawner.py +322 -15
  23. htmlgraph/orchestration/live_events.py +377 -0
  24. {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/dashboard.html +41 -0
  25. {htmlgraph-0.25.0.dist-info → htmlgraph-0.26.1.dist-info}/METADATA +1 -1
  26. {htmlgraph-0.25.0.dist-info → htmlgraph-0.26.1.dist-info}/RECORD +32 -27
  27. {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/styles.css +0 -0
  28. {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  29. {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  30. {htmlgraph-0.25.0.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  31. {htmlgraph-0.25.0.dist-info → htmlgraph-0.26.1.dist-info}/WHEEL +0 -0
  32. {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.25.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=dFd3HYe-7Vne9VAaoztjV_j3I3gP5Anj041awVdIh9s,5717
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=jzXrhore6RQ_u94FEhvze_2QpMhn8PkLWLSeGYf4Z0M,287408
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=6CEQMoVXjsWfitPkrVsoBql888ivMnCngt7nxYYLygA,79759
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=Bz8wcApv_QibrH-f3OBqWS1jT6uNRXdgFpqpG5T2b6Y,30748
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=6eMICdaj7cYvLFMJMuvGWE4_Ps_Fn0wO8NysVCWc93c,17346
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=22P_29cTDN49NE0KrDofy5-FOExse2VVhYU3f0wNSQc,51240
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=q0v8KwRI0oLdfUZup95LSXJO-olokNwSY1qqTu4vWnM,12702
168
- htmlgraph/hooks/context.py,sha256=yyJLbGQtmLtuVG4nhSIAeXlJef1FSdzrUGWqD_MYNuc,9121
169
- htmlgraph/hooks/drift_handler.py,sha256=e0rqVlD6tVd5fnk9h7Kd5OwXCSFTqEqwzL0YxXoMFVA,17544
170
- htmlgraph/hooks/event_tracker.py,sha256=QVl-X0QSmWbG1cfcC68Qegw5a_GQnOdxnNBLNPaYofc,38112
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=rKRx5EYED-BFQrnAJ_roIjpP8GOs3RKDpulDouA29K0,19781
174
- htmlgraph/hooks/orchestrator_reflector.py,sha256=j3kZge33m42CEUVYiufiz7mf7Qm4DimnsRZKjbpZStA,5154
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=SVHwj7joOG0ifDGmUYvOEai9U1g2P_sf2-AACPNNCUk,25002
184
- htmlgraph/hooks/prompt_analyzer.py,sha256=vVTzvJF_JjD7Wt8XY6RTro4iQwoLek0_kxgvSYqVI8Y,23269
185
- htmlgraph/hooks/session_handler.py,sha256=aB-gNn29LE-9pP8M2LmXLNSlfGlkrWVAa2idY4N279Q,18578
186
- htmlgraph/hooks/state_manager.py,sha256=S5gWSwyjuuVIXH9NWD5xRW6TYwHnH-48GWQmYph_ZE0,17568
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=cwul8fIGS-_hFKskrYC6Hjh0F9yP4_3ErAsyp_SL43c,17417
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=SWvNLTHsQEo1ZbuhaHa4eC6FT3UixnjpSjeGJx-PVqA,40269
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.25.0.data/data/htmlgraph/dashboard.html,sha256=jzXrhore6RQ_u94FEhvze_2QpMhn8PkLWLSeGYf4Z0M,287408
211
- htmlgraph-0.25.0.data/data/htmlgraph/styles.css,sha256=oDUSC8jG-V-hKojOBO9J88hxAeY2wJrBYTq0uCwX_Y4,7135
212
- htmlgraph-0.25.0.data/data/htmlgraph/templates/AGENTS.md.template,sha256=f96h7V6ygwj-v-fanVI48eYMxR6t_se4bet1H4ZsDpI,7642
213
- htmlgraph-0.25.0.data/data/htmlgraph/templates/CLAUDE.md.template,sha256=h1kG2hTX2XYig2KszsHBfzrwa_4Cfcq2Pj4SwqzeDlM,1984
214
- htmlgraph-0.25.0.data/data/htmlgraph/templates/GEMINI.md.template,sha256=gAGzE53Avki87BM_otqy5HdcYCoLsHgqaKjVzNzPMX8,1622
215
- htmlgraph-0.25.0.dist-info/METADATA,sha256=_e-eOgin2tm5rIdKmsRHNpDGIvxo8LXH26tLDdfnxXk,10236
216
- htmlgraph-0.25.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
217
- htmlgraph-0.25.0.dist-info/entry_points.txt,sha256=EaUbjA_bbDwEO_XDLEGMeK8aQP-ZnHiUTkLshyKDyB8,98
218
- htmlgraph-0.25.0.dist-info/RECORD,,
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,,