htmlgraph 0.20.1__py3-none-any.whl → 0.27.5__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 (304) hide show
  1. htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
  2. htmlgraph/.htmlgraph/agents.json +72 -0
  3. htmlgraph/.htmlgraph/htmlgraph.db +0 -0
  4. htmlgraph/__init__.py +51 -1
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +26 -10
  7. htmlgraph/agent_registry.py +2 -1
  8. htmlgraph/analytics/__init__.py +8 -1
  9. htmlgraph/analytics/cli.py +86 -20
  10. htmlgraph/analytics/cost_analyzer.py +391 -0
  11. htmlgraph/analytics/cost_monitor.py +664 -0
  12. htmlgraph/analytics/cost_reporter.py +675 -0
  13. htmlgraph/analytics/cross_session.py +617 -0
  14. htmlgraph/analytics/dependency.py +10 -6
  15. htmlgraph/analytics/pattern_learning.py +771 -0
  16. htmlgraph/analytics/session_graph.py +707 -0
  17. htmlgraph/analytics/strategic/__init__.py +80 -0
  18. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  19. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  20. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  21. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  22. htmlgraph/analytics/work_type.py +67 -27
  23. htmlgraph/analytics_index.py +53 -20
  24. htmlgraph/api/__init__.py +3 -0
  25. htmlgraph/api/cost_alerts_websocket.py +416 -0
  26. htmlgraph/api/main.py +2498 -0
  27. htmlgraph/api/static/htmx.min.js +1 -0
  28. htmlgraph/api/static/style-redesign.css +1344 -0
  29. htmlgraph/api/static/style.css +1079 -0
  30. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  31. htmlgraph/api/templates/dashboard.html +794 -0
  32. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  33. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  34. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  35. htmlgraph/api/templates/partials/agents.html +317 -0
  36. htmlgraph/api/templates/partials/event-traces.html +373 -0
  37. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  38. htmlgraph/api/templates/partials/features.html +578 -0
  39. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  40. htmlgraph/api/templates/partials/metrics.html +346 -0
  41. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  42. htmlgraph/api/templates/partials/orchestration.html +198 -0
  43. htmlgraph/api/templates/partials/spawners.html +375 -0
  44. htmlgraph/api/templates/partials/work-items.html +613 -0
  45. htmlgraph/api/websocket.py +538 -0
  46. htmlgraph/archive/__init__.py +24 -0
  47. htmlgraph/archive/bloom.py +234 -0
  48. htmlgraph/archive/fts.py +297 -0
  49. htmlgraph/archive/manager.py +583 -0
  50. htmlgraph/archive/search.py +244 -0
  51. htmlgraph/atomic_ops.py +560 -0
  52. htmlgraph/attribute_index.py +2 -1
  53. htmlgraph/bounded_paths.py +539 -0
  54. htmlgraph/builders/base.py +57 -2
  55. htmlgraph/builders/bug.py +19 -3
  56. htmlgraph/builders/chore.py +19 -3
  57. htmlgraph/builders/epic.py +19 -3
  58. htmlgraph/builders/feature.py +27 -3
  59. htmlgraph/builders/insight.py +2 -1
  60. htmlgraph/builders/metric.py +2 -1
  61. htmlgraph/builders/pattern.py +2 -1
  62. htmlgraph/builders/phase.py +19 -3
  63. htmlgraph/builders/spike.py +29 -3
  64. htmlgraph/builders/track.py +42 -1
  65. htmlgraph/cigs/__init__.py +81 -0
  66. htmlgraph/cigs/autonomy.py +385 -0
  67. htmlgraph/cigs/cost.py +475 -0
  68. htmlgraph/cigs/messages_basic.py +472 -0
  69. htmlgraph/cigs/messaging.py +365 -0
  70. htmlgraph/cigs/models.py +771 -0
  71. htmlgraph/cigs/pattern_storage.py +427 -0
  72. htmlgraph/cigs/patterns.py +503 -0
  73. htmlgraph/cigs/posttool_analyzer.py +234 -0
  74. htmlgraph/cigs/reporter.py +818 -0
  75. htmlgraph/cigs/tracker.py +317 -0
  76. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  77. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  78. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  79. htmlgraph/cli/__init__.py +42 -0
  80. htmlgraph/cli/__main__.py +6 -0
  81. htmlgraph/cli/analytics.py +1424 -0
  82. htmlgraph/cli/base.py +685 -0
  83. htmlgraph/cli/constants.py +206 -0
  84. htmlgraph/cli/core.py +954 -0
  85. htmlgraph/cli/main.py +147 -0
  86. htmlgraph/cli/models.py +475 -0
  87. htmlgraph/cli/templates/__init__.py +1 -0
  88. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  89. htmlgraph/cli/work/__init__.py +239 -0
  90. htmlgraph/cli/work/browse.py +115 -0
  91. htmlgraph/cli/work/features.py +568 -0
  92. htmlgraph/cli/work/orchestration.py +676 -0
  93. htmlgraph/cli/work/report.py +728 -0
  94. htmlgraph/cli/work/sessions.py +466 -0
  95. htmlgraph/cli/work/snapshot.py +559 -0
  96. htmlgraph/cli/work/tracks.py +486 -0
  97. htmlgraph/cli_commands/__init__.py +1 -0
  98. htmlgraph/cli_commands/feature.py +195 -0
  99. htmlgraph/cli_framework.py +115 -0
  100. htmlgraph/collections/__init__.py +2 -0
  101. htmlgraph/collections/base.py +197 -14
  102. htmlgraph/collections/bug.py +2 -1
  103. htmlgraph/collections/chore.py +2 -1
  104. htmlgraph/collections/epic.py +2 -1
  105. htmlgraph/collections/feature.py +2 -1
  106. htmlgraph/collections/insight.py +2 -1
  107. htmlgraph/collections/metric.py +2 -1
  108. htmlgraph/collections/pattern.py +2 -1
  109. htmlgraph/collections/phase.py +2 -1
  110. htmlgraph/collections/session.py +194 -0
  111. htmlgraph/collections/spike.py +13 -2
  112. htmlgraph/collections/task_delegation.py +241 -0
  113. htmlgraph/collections/todo.py +14 -1
  114. htmlgraph/collections/traces.py +487 -0
  115. htmlgraph/config/cost_models.json +56 -0
  116. htmlgraph/config.py +190 -0
  117. htmlgraph/context_analytics.py +2 -1
  118. htmlgraph/converter.py +116 -7
  119. htmlgraph/cost_analysis/__init__.py +5 -0
  120. htmlgraph/cost_analysis/analyzer.py +438 -0
  121. htmlgraph/dashboard.html +2246 -248
  122. htmlgraph/dashboard.html.backup +6592 -0
  123. htmlgraph/dashboard.html.bak +7181 -0
  124. htmlgraph/dashboard.html.bak2 +7231 -0
  125. htmlgraph/dashboard.html.bak3 +7232 -0
  126. htmlgraph/db/__init__.py +38 -0
  127. htmlgraph/db/queries.py +790 -0
  128. htmlgraph/db/schema.py +1788 -0
  129. htmlgraph/decorators.py +317 -0
  130. htmlgraph/dependency_models.py +2 -1
  131. htmlgraph/deploy.py +26 -27
  132. htmlgraph/docs/API_REFERENCE.md +841 -0
  133. htmlgraph/docs/HTTP_API.md +750 -0
  134. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  135. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  136. htmlgraph/docs/README.md +532 -0
  137. htmlgraph/docs/__init__.py +77 -0
  138. htmlgraph/docs/docs_version.py +55 -0
  139. htmlgraph/docs/metadata.py +93 -0
  140. htmlgraph/docs/migrations.py +232 -0
  141. htmlgraph/docs/template_engine.py +143 -0
  142. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  143. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  144. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  145. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  146. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  147. htmlgraph/docs/version_check.py +163 -0
  148. htmlgraph/edge_index.py +2 -1
  149. htmlgraph/error_handler.py +544 -0
  150. htmlgraph/event_log.py +86 -37
  151. htmlgraph/event_migration.py +2 -1
  152. htmlgraph/file_watcher.py +12 -8
  153. htmlgraph/find_api.py +2 -1
  154. htmlgraph/git_events.py +67 -9
  155. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  156. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  157. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  158. htmlgraph/hooks/__init__.py +8 -0
  159. htmlgraph/hooks/bootstrap.py +169 -0
  160. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  161. htmlgraph/hooks/concurrent_sessions.py +208 -0
  162. htmlgraph/hooks/context.py +350 -0
  163. htmlgraph/hooks/drift_handler.py +525 -0
  164. htmlgraph/hooks/event_tracker.py +790 -99
  165. htmlgraph/hooks/git_commands.py +175 -0
  166. htmlgraph/hooks/installer.py +5 -1
  167. htmlgraph/hooks/orchestrator.py +327 -76
  168. htmlgraph/hooks/orchestrator_reflector.py +31 -4
  169. htmlgraph/hooks/post_tool_use_failure.py +32 -7
  170. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  171. htmlgraph/hooks/posttooluse.py +92 -19
  172. htmlgraph/hooks/pretooluse.py +527 -7
  173. htmlgraph/hooks/prompt_analyzer.py +637 -0
  174. htmlgraph/hooks/session_handler.py +668 -0
  175. htmlgraph/hooks/session_summary.py +395 -0
  176. htmlgraph/hooks/state_manager.py +504 -0
  177. htmlgraph/hooks/subagent_detection.py +202 -0
  178. htmlgraph/hooks/subagent_stop.py +369 -0
  179. htmlgraph/hooks/task_enforcer.py +99 -4
  180. htmlgraph/hooks/validator.py +212 -91
  181. htmlgraph/ids.py +2 -1
  182. htmlgraph/learning.py +125 -100
  183. htmlgraph/mcp_server.py +2 -1
  184. htmlgraph/models.py +217 -18
  185. htmlgraph/operations/README.md +62 -0
  186. htmlgraph/operations/__init__.py +79 -0
  187. htmlgraph/operations/analytics.py +339 -0
  188. htmlgraph/operations/bootstrap.py +289 -0
  189. htmlgraph/operations/events.py +244 -0
  190. htmlgraph/operations/fastapi_server.py +231 -0
  191. htmlgraph/operations/hooks.py +350 -0
  192. htmlgraph/operations/initialization.py +597 -0
  193. htmlgraph/operations/initialization.py.backup +228 -0
  194. htmlgraph/operations/server.py +303 -0
  195. htmlgraph/orchestration/__init__.py +58 -0
  196. htmlgraph/orchestration/claude_launcher.py +179 -0
  197. htmlgraph/orchestration/command_builder.py +72 -0
  198. htmlgraph/orchestration/headless_spawner.py +281 -0
  199. htmlgraph/orchestration/live_events.py +377 -0
  200. htmlgraph/orchestration/model_selection.py +327 -0
  201. htmlgraph/orchestration/plugin_manager.py +140 -0
  202. htmlgraph/orchestration/prompts.py +137 -0
  203. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  204. htmlgraph/orchestration/spawners/__init__.py +16 -0
  205. htmlgraph/orchestration/spawners/base.py +194 -0
  206. htmlgraph/orchestration/spawners/claude.py +173 -0
  207. htmlgraph/orchestration/spawners/codex.py +435 -0
  208. htmlgraph/orchestration/spawners/copilot.py +294 -0
  209. htmlgraph/orchestration/spawners/gemini.py +471 -0
  210. htmlgraph/orchestration/subprocess_runner.py +36 -0
  211. htmlgraph/{orchestration.py → orchestration/task_coordination.py} +16 -8
  212. htmlgraph/orchestration.md +563 -0
  213. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  214. htmlgraph/orchestrator.py +2 -1
  215. htmlgraph/orchestrator_config.py +357 -0
  216. htmlgraph/orchestrator_mode.py +115 -4
  217. htmlgraph/parallel.py +2 -1
  218. htmlgraph/parser.py +86 -6
  219. htmlgraph/path_query.py +608 -0
  220. htmlgraph/pattern_matcher.py +636 -0
  221. htmlgraph/pydantic_models.py +476 -0
  222. htmlgraph/quality_gates.py +350 -0
  223. htmlgraph/query_builder.py +2 -1
  224. htmlgraph/query_composer.py +509 -0
  225. htmlgraph/reflection.py +443 -0
  226. htmlgraph/refs.py +344 -0
  227. htmlgraph/repo_hash.py +512 -0
  228. htmlgraph/repositories/__init__.py +292 -0
  229. htmlgraph/repositories/analytics_repository.py +455 -0
  230. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  231. htmlgraph/repositories/feature_repository.py +581 -0
  232. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  233. htmlgraph/repositories/feature_repository_memory.py +607 -0
  234. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  235. htmlgraph/repositories/filter_service.py +620 -0
  236. htmlgraph/repositories/filter_service_standard.py +445 -0
  237. htmlgraph/repositories/shared_cache.py +621 -0
  238. htmlgraph/repositories/shared_cache_memory.py +395 -0
  239. htmlgraph/repositories/track_repository.py +552 -0
  240. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  241. htmlgraph/repositories/track_repository_memory.py +508 -0
  242. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  243. htmlgraph/sdk/__init__.py +398 -0
  244. htmlgraph/sdk/__init__.pyi +14 -0
  245. htmlgraph/sdk/analytics/__init__.py +19 -0
  246. htmlgraph/sdk/analytics/engine.py +155 -0
  247. htmlgraph/sdk/analytics/helpers.py +178 -0
  248. htmlgraph/sdk/analytics/registry.py +109 -0
  249. htmlgraph/sdk/base.py +484 -0
  250. htmlgraph/sdk/constants.py +216 -0
  251. htmlgraph/sdk/core.pyi +308 -0
  252. htmlgraph/sdk/discovery.py +120 -0
  253. htmlgraph/sdk/help/__init__.py +12 -0
  254. htmlgraph/sdk/help/mixin.py +699 -0
  255. htmlgraph/sdk/mixins/__init__.py +15 -0
  256. htmlgraph/sdk/mixins/attribution.py +113 -0
  257. htmlgraph/sdk/mixins/mixin.py +410 -0
  258. htmlgraph/sdk/operations/__init__.py +12 -0
  259. htmlgraph/sdk/operations/mixin.py +427 -0
  260. htmlgraph/sdk/orchestration/__init__.py +17 -0
  261. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  262. htmlgraph/sdk/orchestration/spawner.py +204 -0
  263. htmlgraph/sdk/planning/__init__.py +19 -0
  264. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  265. htmlgraph/sdk/planning/mixin.py +211 -0
  266. htmlgraph/sdk/planning/parallel.py +186 -0
  267. htmlgraph/sdk/planning/queue.py +210 -0
  268. htmlgraph/sdk/planning/recommendations.py +87 -0
  269. htmlgraph/sdk/planning/smart_planning.py +319 -0
  270. htmlgraph/sdk/session/__init__.py +19 -0
  271. htmlgraph/sdk/session/continuity.py +57 -0
  272. htmlgraph/sdk/session/handoff.py +110 -0
  273. htmlgraph/sdk/session/info.py +309 -0
  274. htmlgraph/sdk/session/manager.py +103 -0
  275. htmlgraph/sdk/strategic/__init__.py +26 -0
  276. htmlgraph/sdk/strategic/mixin.py +563 -0
  277. htmlgraph/server.py +295 -107
  278. htmlgraph/session_hooks.py +300 -0
  279. htmlgraph/session_manager.py +285 -3
  280. htmlgraph/session_registry.py +587 -0
  281. htmlgraph/session_state.py +436 -0
  282. htmlgraph/session_warning.py +2 -1
  283. htmlgraph/sessions/__init__.py +23 -0
  284. htmlgraph/sessions/handoff.py +756 -0
  285. htmlgraph/system_prompts.py +450 -0
  286. htmlgraph/templates/orchestration-view.html +350 -0
  287. htmlgraph/track_builder.py +33 -1
  288. htmlgraph/track_manager.py +38 -0
  289. htmlgraph/transcript.py +18 -5
  290. htmlgraph/validation.py +115 -0
  291. htmlgraph/watch.py +2 -1
  292. htmlgraph/work_type_utils.py +2 -1
  293. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
  294. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
  295. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  296. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  297. htmlgraph/cli.py +0 -4839
  298. htmlgraph/sdk.py +0 -2359
  299. htmlgraph-0.20.1.dist-info/RECORD +0 -118
  300. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  301. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  302. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  303. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  304. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
htmlgraph/server.py CHANGED
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  HtmlGraph REST API Server.
3
7
 
@@ -17,14 +21,13 @@ import socket
17
21
  import sys
18
22
  import urllib.parse
19
23
  from datetime import datetime, timezone
20
- from http.server import HTTPServer, SimpleHTTPRequestHandler
24
+ from http.server import SimpleHTTPRequestHandler
21
25
  from pathlib import Path
22
26
  from typing import Any, Literal, cast
23
27
 
24
28
  from htmlgraph.analytics_index import AnalyticsIndex
25
29
  from htmlgraph.converter import dict_to_node, node_to_dict
26
30
  from htmlgraph.event_log import JsonlEventLog
27
- from htmlgraph.file_watcher import GraphWatcher
28
31
  from htmlgraph.graph import HtmlGraph
29
32
  from htmlgraph.ids import generate_id
30
33
  from htmlgraph.models import Node
@@ -49,6 +52,7 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
49
52
  "sessions",
50
53
  "agents",
51
54
  "tracks",
55
+ "task-delegations",
52
56
  ]
53
57
 
54
58
  def __init__(self, *args: Any, **kwargs: Any) -> None:
@@ -210,7 +214,15 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
210
214
  return "api", collection, node_id, query_params
211
215
 
212
216
  def _serve_packaged_dashboard(self) -> bool:
213
- """Serve the bundled dashboard HTML if available."""
217
+ """
218
+ DEPRECATED: Serve the bundled dashboard HTML if available.
219
+
220
+ NOTE: This server is LEGACY. The active server is FastAPI-based.
221
+ See: src/python/htmlgraph/operations/fastapi_server.py
222
+
223
+ The dashboard.html file was archived to .archived-templates/
224
+ Active templates are in src/python/htmlgraph/api/templates/
225
+ """
214
226
  dashboard_path = Path(__file__).parent / "dashboard.html"
215
227
  if not dashboard_path.exists():
216
228
  return False
@@ -235,6 +247,9 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
235
247
  def do_GET(self) -> None:
236
248
  """Handle GET requests."""
237
249
  api, collection, node_id, params = self._parse_path()
250
+ logger.debug(
251
+ f"do_GET: api={api}, collection={collection}, node_id={node_id}, params={params}"
252
+ )
238
253
 
239
254
  # Not an API request - serve static files
240
255
  if api != "api":
@@ -260,6 +275,15 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
260
275
  if collection == "analytics":
261
276
  return self._handle_analytics(node_id, params)
262
277
 
278
+ # GET /api/orchestration - Get delegation chains and agent coordination
279
+ if collection == "orchestration":
280
+ logger.info(f"DEBUG: Handling orchestration request, params={params}")
281
+ return self._handle_orchestration_view(params)
282
+
283
+ # GET /api/task-delegations/stats - Get aggregated delegation statistics
284
+ if collection == "task-delegations" and params.get("stats") == "true":
285
+ return self._handle_task_delegations_stats()
286
+
263
287
  # GET /api/tracks/{track_id}/features - Get features for a track
264
288
  if collection == "tracks" and node_id and params.get("features") == "true":
265
289
  return self._handle_track_features(node_id)
@@ -1043,6 +1067,187 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
1043
1067
  except Exception as e:
1044
1068
  self._send_error_json(f"Failed to generate features: {str(e)}", 500)
1045
1069
 
1070
+ def _handle_orchestration_view(self, params: dict) -> None:
1071
+ """
1072
+ Get delegation chains and agent coordination information.
1073
+
1074
+ Queries the SQLite database for delegation events and builds
1075
+ a view of agent coordination and handoff patterns.
1076
+
1077
+ Returns:
1078
+ {
1079
+ "delegation_count": int,
1080
+ "unique_agents": int,
1081
+ "agents": [str],
1082
+ "delegation_chains": {
1083
+ "from_agent": [
1084
+ {
1085
+ "to_agent": str,
1086
+ "event_type": str,
1087
+ "timestamp": str,
1088
+ "task": str,
1089
+ "status": str
1090
+ }
1091
+ ]
1092
+ }
1093
+ }
1094
+ """
1095
+ try:
1096
+ from htmlgraph.db.schema import HtmlGraphDB
1097
+
1098
+ # Use unified index.sqlite database
1099
+ db_path = str(self.graph_dir / "index.sqlite")
1100
+ db = HtmlGraphDB(db_path=db_path)
1101
+ db.connect()
1102
+
1103
+ # Get all delegation events
1104
+ delegations = db.get_delegations(limit=1000)
1105
+ db.close()
1106
+
1107
+ # Build delegation chains grouped by from_agent
1108
+ delegation_chains: dict[str, list[dict]] = {}
1109
+ agents = set()
1110
+ delegation_count = 0
1111
+
1112
+ for delegation in delegations:
1113
+ from_agent = delegation.get("from_agent", "unknown")
1114
+ to_agent = delegation.get("to_agent", "unknown")
1115
+ timestamp = delegation.get("timestamp", "")
1116
+ reason = delegation.get("reason", "")
1117
+ status = delegation.get("status", "pending")
1118
+
1119
+ agents.add(from_agent)
1120
+ agents.add(to_agent)
1121
+ delegation_count += 1
1122
+
1123
+ if from_agent not in delegation_chains:
1124
+ delegation_chains[from_agent] = []
1125
+
1126
+ delegation_chains[from_agent].append(
1127
+ {
1128
+ "to_agent": to_agent,
1129
+ "event_type": "delegation",
1130
+ "timestamp": timestamp,
1131
+ "task": reason or "Unnamed task",
1132
+ "status": status,
1133
+ }
1134
+ )
1135
+
1136
+ self._send_json(
1137
+ {
1138
+ "delegation_count": delegation_count,
1139
+ "unique_agents": len(agents),
1140
+ "agents": sorted(list(agents)),
1141
+ "delegation_chains": delegation_chains,
1142
+ }
1143
+ )
1144
+
1145
+ except Exception as e:
1146
+ self._send_error_json(f"Failed to get orchestration view: {str(e)}", 500)
1147
+
1148
+ def _handle_task_delegations_stats(self) -> None:
1149
+ """Get aggregated statistics about task delegations."""
1150
+ try:
1151
+ delegations_graph = self._get_graph("task-delegations")
1152
+
1153
+ # Get all delegations
1154
+ all_delegations = list(delegations_graph)
1155
+
1156
+ if not all_delegations:
1157
+ self._send_json(
1158
+ {
1159
+ "total_delegations": 0,
1160
+ "by_agent_type": {},
1161
+ "by_status": {},
1162
+ "total_tokens": 0,
1163
+ "total_cost": 0.0,
1164
+ "average_duration": 0.0,
1165
+ "agent_stats": [],
1166
+ }
1167
+ )
1168
+ return
1169
+
1170
+ # Aggregate by agent type
1171
+ agent_stats: dict = {}
1172
+ by_status: dict[str, int] = {}
1173
+ total_tokens = 0
1174
+ total_cost = 0.0
1175
+ durations = []
1176
+
1177
+ for delegation in all_delegations:
1178
+ agent_type = str(getattr(delegation, "agent_type", "unknown"))
1179
+ status = str(getattr(delegation, "status", "unknown"))
1180
+ tokens_val = getattr(delegation, "tokens_used", 0)
1181
+ tokens = int(tokens_val) if tokens_val else 0
1182
+ cost_val = getattr(delegation, "cost_usd", 0)
1183
+ cost = float(cost_val) if cost_val else 0.0
1184
+ duration_val = getattr(delegation, "duration_seconds", 0)
1185
+ duration = int(duration_val) if duration_val else 0
1186
+
1187
+ # Track by agent
1188
+ if agent_type not in agent_stats:
1189
+ agent_stats[agent_type] = {
1190
+ "agent_type": agent_type,
1191
+ "tasks_completed": 0,
1192
+ "total_duration": 0,
1193
+ "total_tokens": 0,
1194
+ "total_cost": 0.0,
1195
+ "success_count": 0,
1196
+ "failure_count": 0,
1197
+ }
1198
+
1199
+ agent_stats[agent_type]["tasks_completed"] += 1
1200
+ agent_stats[agent_type]["total_duration"] += duration
1201
+ agent_stats[agent_type]["total_tokens"] += tokens
1202
+ agent_stats[agent_type]["total_cost"] += cost
1203
+
1204
+ if status == "success":
1205
+ agent_stats[agent_type]["success_count"] += 1
1206
+ else:
1207
+ agent_stats[agent_type]["failure_count"] += 1
1208
+
1209
+ # Track by status
1210
+ by_status[status] = by_status.get(status, 0) + 1
1211
+
1212
+ # Aggregate totals
1213
+ total_tokens += tokens
1214
+ total_cost += cost
1215
+ if duration:
1216
+ durations.append(duration)
1217
+
1218
+ # Calculate success rate for each agent
1219
+ for agent_stats_item in agent_stats.values():
1220
+ total = agent_stats_item["tasks_completed"]
1221
+ if total > 0:
1222
+ agent_stats_item["success_rate"] = (
1223
+ agent_stats_item["success_count"] / total
1224
+ )
1225
+ else:
1226
+ agent_stats_item["success_rate"] = 0.0
1227
+
1228
+ average_duration = sum(durations) / len(durations) if durations else 0.0
1229
+
1230
+ self._send_json(
1231
+ {
1232
+ "total_delegations": len(all_delegations),
1233
+ "by_agent_type": {
1234
+ agent: stats["tasks_completed"]
1235
+ for agent, stats in agent_stats.items()
1236
+ },
1237
+ "by_status": by_status,
1238
+ "total_tokens": total_tokens,
1239
+ "total_cost": round(total_cost, 4),
1240
+ "average_duration": round(average_duration, 2),
1241
+ "agent_stats": sorted(
1242
+ agent_stats.values(),
1243
+ key=lambda x: x["total_cost"],
1244
+ reverse=True,
1245
+ ),
1246
+ }
1247
+ )
1248
+ except Exception as e:
1249
+ self._send_error_json(f"Failed to get delegation stats: {str(e)}", 500)
1250
+
1046
1251
  def _handle_sync_track(self, track_id: str) -> None:
1047
1252
  """Sync task and spec completion based on features."""
1048
1253
  from htmlgraph.track_manager import TrackManager
@@ -1074,7 +1279,7 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
1074
1279
 
1075
1280
  def log_message(self, format: str, *args: str) -> None:
1076
1281
  """Custom log format."""
1077
- print(f"[{datetime.now().strftime('%H:%M:%S')}] {args[0]}")
1282
+ logger.info(f"[{datetime.now().strftime('%H:%M:%S')}] {args[0]}")
1078
1283
 
1079
1284
 
1080
1285
  def find_available_port(start_port: int = 8080, max_attempts: int = 10) -> int:
@@ -1174,42 +1379,39 @@ def serve(
1174
1379
  host: str = "localhost",
1175
1380
  watch: bool = True,
1176
1381
  auto_port: bool = False,
1382
+ show_progress: bool = False,
1383
+ quiet: bool = False,
1177
1384
  ) -> None:
1178
1385
  """
1179
- Start the HtmlGraph server.
1180
- Auto-syncs dashboard.html to index.html before starting.
1386
+ Start the HtmlGraph server (FastAPI-based with WebSocket support).
1387
+
1388
+ This function launches the FastAPI server which provides:
1389
+ - REST API for CRUD operations on graph nodes
1390
+ - WebSocket endpoint at /ws/events for real-time event streaming
1391
+ - HTMX-powered dashboard for agent observability
1181
1392
 
1182
1393
  Args:
1183
- port: Port to listen on
1394
+ port: Port to listen on (default: 8080)
1184
1395
  graph_dir: Directory containing graph data (.htmlgraph/)
1185
- static_dir: Directory for static files (index.html, etc.)
1186
- host: Host to bind to
1187
- watch: Enable file watching for auto-reload (default: True)
1396
+ static_dir: Directory for static files (index.html, etc.) - preserved for compatibility
1397
+ host: Host to bind to (default: localhost)
1398
+ watch: Enable file watching for auto-reload (default: True) - maps to reload in FastAPI
1188
1399
  auto_port: Automatically find available port if specified port is in use
1400
+ show_progress: Show Rich progress during startup (not used with FastAPI)
1401
+ quiet: Suppress progress output when true
1189
1402
  """
1190
- graph_dir = Path(graph_dir)
1191
- static_dir = Path(static_dir)
1403
+ import asyncio
1192
1404
 
1193
- # Handle auto-port selection
1194
- if auto_port and check_port_in_use(port, host):
1195
- original_port = port
1196
- port = find_available_port(port + 1)
1197
- print(f"āš ļø Port {original_port} is in use, using {port} instead\n")
1405
+ from htmlgraph.operations.fastapi_server import (
1406
+ FastAPIServerError,
1407
+ PortInUseError,
1408
+ run_fastapi_server,
1409
+ start_fastapi_server,
1410
+ )
1198
1411
 
1199
- # Auto-sync dashboard files
1200
- try:
1201
- if sync_dashboard_files(static_dir):
1202
- print("āš ļø Dashboard files out of sync")
1203
- print("āœ… Synced dashboard.html → index.html\n")
1204
- except PermissionError as e:
1205
- print(f"āš ļø Warning: Unable to sync dashboard files: {e}")
1206
- print(
1207
- f" Run: cp src/python/htmlgraph/dashboard.html {static_dir / 'index.html'}\n"
1208
- )
1209
- except Exception as e:
1210
- print(f"āš ļø Warning: Error during dashboard sync: {e}\n")
1412
+ graph_dir = Path(graph_dir)
1211
1413
 
1212
- # Create graph directory structure
1414
+ # Ensure graph directory exists
1213
1415
  graph_dir.mkdir(parents=True, exist_ok=True)
1214
1416
  for collection in HtmlGraphAPIHandler.COLLECTIONS:
1215
1417
  (graph_dir / collection).mkdir(exist_ok=True)
@@ -1221,99 +1423,85 @@ def serve(
1221
1423
  if styles_src.exists():
1222
1424
  styles_dest.write_text(styles_src.read_text())
1223
1425
 
1224
- # Configure handler
1225
- HtmlGraphAPIHandler.graph_dir = graph_dir
1226
- HtmlGraphAPIHandler.static_dir = static_dir
1227
- HtmlGraphAPIHandler.graphs = {}
1228
- HtmlGraphAPIHandler.analytics_db = None
1426
+ # Database path - use htmlgraph.db in the graph directory
1427
+ db_path = str(graph_dir / "htmlgraph.db")
1229
1428
 
1230
- # Create server with error handling
1231
1429
  try:
1232
- server = HTTPServer((host, port), HtmlGraphAPIHandler)
1233
- except OSError as e:
1234
- # Handle "Address already in use" error
1235
- if e.errno == 48 or "Address already in use" in str(e):
1236
- print(f"\nāŒ Port {port} is already in use\n")
1237
- print("Solutions:")
1238
- print(" 1. Use a different port:")
1239
- print(f" htmlgraph serve --port {port + 1}\n")
1240
- print(" 2. Let htmlgraph automatically find an available port:")
1241
- print(" htmlgraph serve --auto-port\n")
1242
- print(f" 3. Find and kill the process using port {port}:")
1243
- print(f" lsof -ti:{port} | xargs kill -9\n")
1244
-
1245
- # Try to find and suggest an available port
1246
- try:
1247
- alt_port = find_available_port(port + 1)
1248
- print(f"šŸ’” Found available port: {alt_port}")
1249
- print(f" Run: htmlgraph serve --port {alt_port}\n")
1250
- except OSError:
1251
- pass
1252
-
1253
- sys.exit(1)
1254
- # Re-raise other OSErrors
1255
- raise
1256
-
1257
- # Start file watcher if enabled
1258
- watcher = None
1259
- if watch:
1260
-
1261
- def get_graph(collection: str) -> HtmlGraph:
1262
- """Callback to get graph instance for a collection."""
1263
- handler = HtmlGraphAPIHandler
1264
- if collection not in handler.graphs:
1265
- collection_dir = handler.graph_dir / collection
1266
- handler.graphs[collection] = HtmlGraph(
1267
- collection_dir, stylesheet_path="../styles.css", auto_load=True
1268
- )
1269
- return handler.graphs[collection]
1270
-
1271
- watcher = GraphWatcher(
1272
- graph_dir=graph_dir,
1273
- collections=HtmlGraphAPIHandler.COLLECTIONS,
1274
- get_graph_callback=get_graph,
1430
+ result = start_fastapi_server(
1431
+ port=port,
1432
+ host=host,
1433
+ db_path=db_path,
1434
+ auto_port=auto_port,
1435
+ reload=watch, # Map watch to reload for FastAPI
1275
1436
  )
1276
- watcher.start()
1277
1437
 
1278
- watch_status = "Enabled" if watch else "Disabled"
1279
- print(f"""
1438
+ # Print warnings if any
1439
+ for warning in result.warnings:
1440
+ if not quiet:
1441
+ logger.info(f"āš ļø {warning}")
1442
+
1443
+ # Print server info
1444
+ if not quiet:
1445
+ actual_port = result.config_used["port"]
1446
+ print(f"""
1280
1447
  ╔══════════════════════════════════════════════════════════════╗
1281
- ā•‘ HtmlGraph Server ā•‘
1448
+ ā•‘ HtmlGraph Server (FastAPI) ā•‘
1282
1449
  ╠══════════════════════════════════════════════════════════════╣
1283
- ā•‘ Dashboard: http://{host}:{port}/
1284
- ā•‘ API: http://{host}:{port}/api/
1285
- ā•‘ Graph Dir: {graph_dir}
1286
- ā•‘ Auto-reload: {watch_status}
1450
+ ā•‘ Dashboard: http://{host}:{actual_port}/
1451
+ ā•‘ API: http://{host}:{actual_port}/api/
1452
+ ā•‘ WebSocket: ws://{host}:{actual_port}/ws/events
1453
+ ā•‘ Graph Dir: {graph_dir}
1454
+ ā•‘ Database: {db_path}
1455
+ ā•‘ Auto-reload: {"Enabled" if watch else "Disabled"}
1287
1456
  ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
1288
1457
 
1458
+ Features:
1459
+ • Real-time agent activity feed (HTMX + WebSocket)
1460
+ • Orchestration chains visualization
1461
+ • Feature tracker with Kanban view
1462
+ • Session metrics & performance analytics
1463
+
1289
1464
  API Endpoints:
1290
- GET /api/status - Overall status
1291
- GET /api/collections - List collections
1292
- GET /api/query?status=todo - Query across collections
1293
- GET /api/analytics/overview - Analytics overview (requires index)
1294
- GET /api/analytics/features - Top features (requires index)
1295
- GET /api/analytics/continuity?feature_id=... - Feature continuity (requires index)
1296
- GET /api/analytics/transitions - Tool transitions (requires index)
1297
-
1298
- GET /api/{{collection}} - List nodes
1299
- POST /api/{{collection}} - Create node
1300
- GET /api/{{collection}}/{{id}} - Get node
1301
- PUT /api/{{collection}}/{{id}} - Replace node
1302
- PATCH /api/{{collection}}/{{id}} - Update node
1303
- DELETE /api/{{collection}}/{{id}} - Delete node
1465
+ GET /api/events - List events
1466
+ GET /api/sessions - List sessions
1467
+ GET /api/orchestration - Orchestration data
1468
+ GET /api/initial-stats - Dashboard statistics
1469
+ WS /ws/events - Real-time event stream
1304
1470
 
1305
1471
  Collections: {", ".join(HtmlGraphAPIHandler.COLLECTIONS)}
1306
1472
 
1307
1473
  Press Ctrl+C to stop.
1308
1474
  """)
1309
1475
 
1310
- try:
1311
- server.serve_forever()
1476
+ # Run the server
1477
+ asyncio.run(run_fastapi_server(result.handle))
1478
+
1479
+ except PortInUseError:
1480
+ logger.info(f"\nāŒ Port {port} is already in use\n")
1481
+ logger.info("Solutions:")
1482
+ logger.info(" 1. Use a different port:")
1483
+ logger.info(f" htmlgraph serve --port {port + 1}\n")
1484
+ logger.info(" 2. Let htmlgraph automatically find an available port:")
1485
+ logger.info(" htmlgraph serve --auto-port\n")
1486
+ logger.info(f" 3. Find and kill the process using port {port}:")
1487
+ logger.info(f" lsof -ti:{port} | xargs kill -9\n")
1488
+
1489
+ # Try to find and suggest an available port
1490
+ try:
1491
+ alt_port = find_available_port(port + 1)
1492
+ logger.info(f"šŸ’” Found available port: {alt_port}")
1493
+ logger.info(f" Run: htmlgraph serve --port {alt_port}\n")
1494
+ except OSError:
1495
+ pass
1496
+
1497
+ sys.exit(1)
1498
+
1499
+ except FastAPIServerError as e:
1500
+ logger.info(f"\nāŒ Server error: {e}\n")
1501
+ sys.exit(1)
1502
+
1312
1503
  except KeyboardInterrupt:
1313
- print("\nShutting down...")
1314
- if watcher:
1315
- watcher.stop()
1316
- server.shutdown()
1504
+ logger.info("\nShutting down...")
1317
1505
 
1318
1506
 
1319
1507
  if __name__ == "__main__":