htmlgraph 0.24.1__py3-none-any.whl → 0.25.0__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 (103) hide show
  1. htmlgraph/__init__.py +20 -1
  2. htmlgraph/agent_detection.py +26 -10
  3. htmlgraph/analytics/cross_session.py +4 -3
  4. htmlgraph/analytics/work_type.py +52 -16
  5. htmlgraph/analytics_index.py +51 -19
  6. htmlgraph/api/__init__.py +3 -0
  7. htmlgraph/api/main.py +2115 -0
  8. htmlgraph/api/static/htmx.min.js +1 -0
  9. htmlgraph/api/static/style-redesign.css +1344 -0
  10. htmlgraph/api/static/style.css +1079 -0
  11. htmlgraph/api/templates/dashboard-redesign.html +812 -0
  12. htmlgraph/api/templates/dashboard.html +783 -0
  13. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  14. htmlgraph/api/templates/partials/activity-feed.html +570 -0
  15. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  16. htmlgraph/api/templates/partials/agents.html +317 -0
  17. htmlgraph/api/templates/partials/event-traces.html +373 -0
  18. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  19. htmlgraph/api/templates/partials/features.html +509 -0
  20. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  21. htmlgraph/api/templates/partials/metrics.html +346 -0
  22. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  23. htmlgraph/api/templates/partials/orchestration.html +163 -0
  24. htmlgraph/api/templates/partials/spawners.html +375 -0
  25. htmlgraph/atomic_ops.py +560 -0
  26. htmlgraph/builders/base.py +55 -1
  27. htmlgraph/builders/bug.py +17 -2
  28. htmlgraph/builders/chore.py +17 -2
  29. htmlgraph/builders/epic.py +17 -2
  30. htmlgraph/builders/feature.py +25 -2
  31. htmlgraph/builders/phase.py +17 -2
  32. htmlgraph/builders/spike.py +27 -2
  33. htmlgraph/builders/track.py +14 -0
  34. htmlgraph/cigs/__init__.py +4 -0
  35. htmlgraph/cigs/reporter.py +818 -0
  36. htmlgraph/cli.py +1427 -401
  37. htmlgraph/cli_commands/__init__.py +1 -0
  38. htmlgraph/cli_commands/feature.py +195 -0
  39. htmlgraph/cli_framework.py +115 -0
  40. htmlgraph/collections/__init__.py +2 -0
  41. htmlgraph/collections/base.py +21 -0
  42. htmlgraph/collections/session.py +189 -0
  43. htmlgraph/collections/spike.py +7 -1
  44. htmlgraph/collections/task_delegation.py +236 -0
  45. htmlgraph/collections/traces.py +482 -0
  46. htmlgraph/config.py +113 -0
  47. htmlgraph/converter.py +41 -0
  48. htmlgraph/cost_analysis/__init__.py +5 -0
  49. htmlgraph/cost_analysis/analyzer.py +438 -0
  50. htmlgraph/dashboard.html +3315 -492
  51. htmlgraph-0.24.1.data/data/htmlgraph/dashboard.html → htmlgraph/dashboard.html.backup +2246 -248
  52. htmlgraph/dashboard.html.bak +7181 -0
  53. htmlgraph/dashboard.html.bak2 +7231 -0
  54. htmlgraph/dashboard.html.bak3 +7232 -0
  55. htmlgraph/db/__init__.py +38 -0
  56. htmlgraph/db/queries.py +790 -0
  57. htmlgraph/db/schema.py +1334 -0
  58. htmlgraph/deploy.py +26 -27
  59. htmlgraph/docs/API_REFERENCE.md +841 -0
  60. htmlgraph/docs/HTTP_API.md +750 -0
  61. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  62. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +710 -0
  63. htmlgraph/docs/README.md +533 -0
  64. htmlgraph/docs/version_check.py +3 -1
  65. htmlgraph/error_handler.py +544 -0
  66. htmlgraph/event_log.py +2 -0
  67. htmlgraph/hooks/__init__.py +8 -0
  68. htmlgraph/hooks/bootstrap.py +169 -0
  69. htmlgraph/hooks/context.py +271 -0
  70. htmlgraph/hooks/drift_handler.py +521 -0
  71. htmlgraph/hooks/event_tracker.py +405 -15
  72. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  73. htmlgraph/hooks/pretooluse.py +476 -6
  74. htmlgraph/hooks/prompt_analyzer.py +648 -0
  75. htmlgraph/hooks/session_handler.py +583 -0
  76. htmlgraph/hooks/state_manager.py +501 -0
  77. htmlgraph/hooks/subagent_stop.py +309 -0
  78. htmlgraph/hooks/task_enforcer.py +39 -0
  79. htmlgraph/models.py +111 -15
  80. htmlgraph/operations/fastapi_server.py +230 -0
  81. htmlgraph/orchestration/headless_spawner.py +22 -14
  82. htmlgraph/pydantic_models.py +476 -0
  83. htmlgraph/quality_gates.py +350 -0
  84. htmlgraph/repo_hash.py +511 -0
  85. htmlgraph/sdk.py +348 -10
  86. htmlgraph/server.py +194 -0
  87. htmlgraph/session_hooks.py +300 -0
  88. htmlgraph/session_manager.py +131 -1
  89. htmlgraph/session_registry.py +587 -0
  90. htmlgraph/session_state.py +436 -0
  91. htmlgraph/system_prompts.py +449 -0
  92. htmlgraph/templates/orchestration-view.html +350 -0
  93. htmlgraph/track_builder.py +19 -0
  94. htmlgraph/validation.py +115 -0
  95. htmlgraph-0.25.0.data/data/htmlgraph/dashboard.html +7417 -0
  96. {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/METADATA +91 -64
  97. {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/RECORD +103 -42
  98. {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/styles.css +0 -0
  99. {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  100. {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  101. {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  102. {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/WHEEL +0 -0
  103. {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,236 @@
1
+ """
2
+ Task delegation collection for tracking spawned agent work.
3
+
4
+ Captures observability data for Task() calls:
5
+ - Which agent was spawned (gemini-spawner, codex-spawner, copilot-spawner, haiku)
6
+ - What task was assigned
7
+ - How long it took
8
+ - What was the output/result
9
+ - Cost (tokens used)
10
+ - Success/failure status
11
+
12
+ This data proves multi-agent orchestration works and enables dashboard attribution.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from datetime import datetime
18
+ from typing import TYPE_CHECKING, Any
19
+
20
+ if TYPE_CHECKING:
21
+ from htmlgraph.sdk import SDK
22
+
23
+ from htmlgraph.collections.base import BaseCollection
24
+
25
+
26
+ class TaskDelegationCollection(BaseCollection["TaskDelegationCollection"]):
27
+ """
28
+ Collection interface for task delegations.
29
+
30
+ Tracks all spawned agent work with metrics:
31
+ - Agent type (gemini-spawner, codex-spawner, copilot-spawner, haiku)
32
+ - Task description
33
+ - Start and end timestamps
34
+ - Duration in seconds
35
+ - Tokens used
36
+ - Cost in USD
37
+ - Status (success/failure)
38
+ - Result summary
39
+
40
+ Example:
41
+ >>> sdk = SDK(agent="orchestrator")
42
+ >>> delegations = sdk.task_delegations.where(agent_type="codex-spawner")
43
+ >>> for d in delegations:
44
+ ... print(f"{d.agent_type}: {d.task_description} ({d.duration_seconds}s)")
45
+ >>>
46
+ >>> # Get all delegations for a specific agent
47
+ >>> gemini_work = sdk.task_delegations.where(agent_type="gemini-spawner")
48
+ >>>
49
+ >>> # Calculate total cost
50
+ >>> total_cost = sum(float(d.cost_usd or 0) for d in sdk.task_delegations.all())
51
+ """
52
+
53
+ _collection_name = "task-delegations"
54
+ _node_type = "task-delegation"
55
+
56
+ def __init__(self, sdk: SDK):
57
+ """
58
+ Initialize task delegation collection.
59
+
60
+ Args:
61
+ sdk: Parent SDK instance
62
+ """
63
+ super().__init__(sdk, "task-delegations", "task-delegation")
64
+ self._sdk = sdk
65
+
66
+ def create_delegation(
67
+ self,
68
+ agent_type: str,
69
+ task_description: str,
70
+ timestamp_start: datetime | None = None,
71
+ tokens_used: int = 0,
72
+ cost_usd: float = 0.0,
73
+ status: str = "pending",
74
+ result_summary: str = "",
75
+ ) -> Any:
76
+ """
77
+ Record a task delegation.
78
+
79
+ Args:
80
+ agent_type: Type of spawned agent (gemini-spawner, codex-spawner, copilot-spawner, haiku)
81
+ task_description: Human-readable task description
82
+ timestamp_start: When delegation started (defaults to now)
83
+ tokens_used: Number of tokens used by the agent
84
+ cost_usd: Cost in USD for this delegation
85
+ status: success/failure/pending
86
+ result_summary: Brief summary of the result
87
+
88
+ Returns:
89
+ Created task delegation node
90
+ """
91
+ if timestamp_start is None:
92
+ timestamp_start = datetime.utcnow()
93
+
94
+ # Create delegation record via base collection
95
+ return self.create(
96
+ title=f"{agent_type}: {task_description[:50]}...",
97
+ ).set_metadata(
98
+ {
99
+ "agent_type": agent_type,
100
+ "task_description": task_description,
101
+ "timestamp_start": timestamp_start.isoformat(),
102
+ "tokens_used": tokens_used,
103
+ "cost_usd": cost_usd,
104
+ "status": status,
105
+ "result_summary": result_summary,
106
+ }
107
+ )
108
+
109
+ def update_delegation(
110
+ self,
111
+ delegation_id: str,
112
+ timestamp_end: datetime | None = None,
113
+ tokens_used: int | None = None,
114
+ cost_usd: float | None = None,
115
+ status: str | None = None,
116
+ result_summary: str | None = None,
117
+ ) -> Any:
118
+ """
119
+ Update a delegation record with completion info.
120
+
121
+ Args:
122
+ delegation_id: ID of the delegation to update
123
+ timestamp_end: When delegation completed
124
+ tokens_used: Updated token count
125
+ cost_usd: Updated cost
126
+ status: Updated status
127
+ result_summary: Updated result summary
128
+
129
+ Returns:
130
+ Updated delegation node
131
+ """
132
+ if timestamp_end is None:
133
+ timestamp_end = datetime.utcnow()
134
+
135
+ with self.edit(delegation_id) as delegation:
136
+ if timestamp_end:
137
+ delegation.timestamp_end = timestamp_end.isoformat() # type: ignore[attr-defined]
138
+
139
+ # Calculate duration if we have start time
140
+ if hasattr(delegation, "timestamp_start") and timestamp_end:
141
+ start = datetime.fromisoformat(str(delegation.timestamp_start))
142
+ duration = (timestamp_end - start).total_seconds()
143
+ delegation.duration_seconds = int(duration) # type: ignore[attr-defined]
144
+
145
+ if tokens_used is not None:
146
+ delegation.tokens_used = tokens_used # type: ignore[attr-defined]
147
+ if cost_usd is not None:
148
+ delegation.cost_usd = cost_usd # type: ignore[attr-defined]
149
+ if status is not None:
150
+ delegation.status = status # type: ignore[assignment]
151
+ if result_summary is not None:
152
+ delegation.result_summary = result_summary # type: ignore[attr-defined]
153
+
154
+ return delegation
155
+
156
+ def get_by_agent_type(self, agent_type: str) -> list:
157
+ """
158
+ Get all delegations for a specific agent type.
159
+
160
+ Args:
161
+ agent_type: Type of agent (gemini-spawner, codex-spawner, etc.)
162
+
163
+ Returns:
164
+ List of delegations for that agent type
165
+ """
166
+ return self.where(agent_type=agent_type)
167
+
168
+ def get_by_status(self, status: str) -> list:
169
+ """
170
+ Get all delegations with a specific status.
171
+
172
+ Args:
173
+ status: Status to filter by (success/failure/pending)
174
+
175
+ Returns:
176
+ List of delegations with that status
177
+ """
178
+ return self.where(status=status)
179
+
180
+ def get_stats(self) -> dict:
181
+ """
182
+ Get aggregated statistics about delegations.
183
+
184
+ Returns:
185
+ Dictionary with:
186
+ - total_delegations: Count of all delegations
187
+ - by_agent_type: Count per agent type
188
+ - by_status: Count per status
189
+ - total_tokens: Sum of all tokens used
190
+ - total_cost: Sum of all costs in USD
191
+ - average_duration: Average duration in seconds
192
+ """
193
+ all_delegations = self.all()
194
+
195
+ if not all_delegations:
196
+ return {
197
+ "total_delegations": 0,
198
+ "by_agent_type": {},
199
+ "by_status": {},
200
+ "total_tokens": 0,
201
+ "total_cost": 0.0,
202
+ "average_duration": 0.0,
203
+ }
204
+
205
+ # Count by agent type
206
+ by_agent_type: dict[str, int] = {}
207
+ for d in all_delegations:
208
+ agent = getattr(d, "agent_type", "unknown")
209
+ by_agent_type[agent] = by_agent_type.get(agent, 0) + 1
210
+
211
+ # Count by status
212
+ by_status: dict[str, int] = {}
213
+ for d in all_delegations:
214
+ status = getattr(d, "status", "unknown")
215
+ by_status[status] = by_status.get(status, 0) + 1
216
+
217
+ # Sum tokens and cost
218
+ total_tokens = sum(getattr(d, "tokens_used", 0) for d in all_delegations)
219
+ total_cost = sum(float(getattr(d, "cost_usd", 0) or 0) for d in all_delegations)
220
+
221
+ # Average duration
222
+ durations = [
223
+ getattr(d, "duration_seconds", 0)
224
+ for d in all_delegations
225
+ if hasattr(d, "duration_seconds")
226
+ ]
227
+ average_duration = sum(durations) / len(durations) if durations else 0.0
228
+
229
+ return {
230
+ "total_delegations": len(all_delegations),
231
+ "by_agent_type": by_agent_type,
232
+ "by_status": by_status,
233
+ "total_tokens": total_tokens,
234
+ "total_cost": round(total_cost, 4),
235
+ "average_duration": round(average_duration, 2),
236
+ }