htmlgraph 0.24.2__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.
- htmlgraph/__init__.py +20 -1
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/analytics/cross_session.py +4 -3
- htmlgraph/analytics/work_type.py +52 -16
- htmlgraph/analytics_index.py +51 -19
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/main.py +2115 -0
- htmlgraph/api/static/htmx.min.js +1 -0
- htmlgraph/api/static/style-redesign.css +1344 -0
- htmlgraph/api/static/style.css +1079 -0
- htmlgraph/api/templates/dashboard-redesign.html +812 -0
- htmlgraph/api/templates/dashboard.html +783 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +570 -0
- htmlgraph/api/templates/partials/agents-redesign.html +317 -0
- htmlgraph/api/templates/partials/agents.html +317 -0
- htmlgraph/api/templates/partials/event-traces.html +373 -0
- htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
- htmlgraph/api/templates/partials/features.html +509 -0
- htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
- htmlgraph/api/templates/partials/metrics.html +346 -0
- htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
- htmlgraph/api/templates/partials/orchestration.html +163 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/builders/base.py +55 -1
- htmlgraph/builders/bug.py +17 -2
- htmlgraph/builders/chore.py +17 -2
- htmlgraph/builders/epic.py +17 -2
- htmlgraph/builders/feature.py +25 -2
- htmlgraph/builders/phase.py +17 -2
- htmlgraph/builders/spike.py +27 -2
- htmlgraph/builders/track.py +14 -0
- htmlgraph/cigs/__init__.py +4 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cli.py +1427 -401
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +2 -0
- htmlgraph/collections/base.py +21 -0
- htmlgraph/collections/session.py +189 -0
- htmlgraph/collections/spike.py +7 -1
- htmlgraph/collections/task_delegation.py +236 -0
- htmlgraph/collections/traces.py +482 -0
- htmlgraph/config.py +113 -0
- htmlgraph/converter.py +41 -0
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +3315 -492
- htmlgraph-0.24.2.data/data/htmlgraph/dashboard.html → htmlgraph/dashboard.html.backup +2246 -248
- htmlgraph/dashboard.html.bak +7181 -0
- htmlgraph/dashboard.html.bak2 +7231 -0
- htmlgraph/dashboard.html.bak3 +7232 -0
- htmlgraph/db/__init__.py +38 -0
- htmlgraph/db/queries.py +790 -0
- htmlgraph/db/schema.py +1334 -0
- htmlgraph/deploy.py +26 -27
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +710 -0
- htmlgraph/docs/README.md +533 -0
- htmlgraph/docs/version_check.py +3 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +2 -0
- htmlgraph/hooks/__init__.py +8 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/context.py +271 -0
- htmlgraph/hooks/drift_handler.py +521 -0
- htmlgraph/hooks/event_tracker.py +405 -15
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/pretooluse.py +476 -6
- htmlgraph/hooks/prompt_analyzer.py +648 -0
- htmlgraph/hooks/session_handler.py +583 -0
- htmlgraph/hooks/state_manager.py +501 -0
- htmlgraph/hooks/subagent_stop.py +309 -0
- htmlgraph/hooks/task_enforcer.py +39 -0
- htmlgraph/models.py +111 -15
- htmlgraph/operations/fastapi_server.py +230 -0
- htmlgraph/orchestration/headless_spawner.py +22 -14
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/repo_hash.py +511 -0
- htmlgraph/sdk.py +348 -10
- htmlgraph/server.py +194 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +131 -1
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/system_prompts.py +449 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +19 -0
- htmlgraph/validation.py +115 -0
- htmlgraph-0.25.0.data/data/htmlgraph/dashboard.html +7417 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/METADATA +91 -64
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/RECORD +103 -42
- {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/WHEEL +0 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
<div class="view-container orchestration-view">
|
|
2
|
+
<div class="view-header">
|
|
3
|
+
<h2>Agent Orchestration Graph</h2>
|
|
4
|
+
<div class="view-description">
|
|
5
|
+
Real-time visualization of agent delegation chains and multi-hop workflows
|
|
6
|
+
</div>
|
|
7
|
+
<div class="view-filters">
|
|
8
|
+
<div class="filter-group">
|
|
9
|
+
<label>Filter by Agent:</label>
|
|
10
|
+
<select class="filter-select" id="agent-filter">
|
|
11
|
+
<option value="all">All Agents</option>
|
|
12
|
+
{% set agents = [] %}
|
|
13
|
+
{% for d in delegations %}
|
|
14
|
+
{% if d.from_agent not in agents %}
|
|
15
|
+
{% set _ = agents.append(d.from_agent) %}
|
|
16
|
+
{% endif %}
|
|
17
|
+
{% if d.to_agent not in agents %}
|
|
18
|
+
{% set _ = agents.append(d.to_agent) %}
|
|
19
|
+
{% endif %}
|
|
20
|
+
{% endfor %}
|
|
21
|
+
{% for agent in agents|sort %}
|
|
22
|
+
<option value="{{ agent }}">{{ agent }}</option>
|
|
23
|
+
{% endfor %}
|
|
24
|
+
</select>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<!-- DAG Visualization Container -->
|
|
30
|
+
<div class="orchestration-graph">
|
|
31
|
+
<div class="graph-container">
|
|
32
|
+
<svg class="graph-edges" id="graph-edges"></svg>
|
|
33
|
+
<div class="graph-nodes" id="graph-nodes"></div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- Stats -->
|
|
38
|
+
<div style="margin-top: var(--spacing-2xl); padding-top: var(--spacing-xl); border-top: 1px solid var(--border-subtle);">
|
|
39
|
+
<h3 style="color: var(--accent-lime); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: var(--spacing-lg);">
|
|
40
|
+
Orchestration Statistics
|
|
41
|
+
</h3>
|
|
42
|
+
<div class="metrics-grid">
|
|
43
|
+
<div class="metric-card">
|
|
44
|
+
<div class="metric-label">Total Delegations</div>
|
|
45
|
+
<div class="metric-value">{{ delegations|length }}</div>
|
|
46
|
+
<div class="metric-trend">Agent-to-agent handoffs</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="metric-card">
|
|
49
|
+
<div class="metric-label">Unique Agents</div>
|
|
50
|
+
<div class="metric-value" id="unique-agents">
|
|
51
|
+
{% set unique_agents = {} %}
|
|
52
|
+
{% for d in delegations %}
|
|
53
|
+
{% if d.from_agent not in unique_agents %}
|
|
54
|
+
{% set _ = unique_agents.update({d.from_agent: True}) %}
|
|
55
|
+
{% endif %}
|
|
56
|
+
{% if d.to_agent not in unique_agents %}
|
|
57
|
+
{% set _ = unique_agents.update({d.to_agent: True}) %}
|
|
58
|
+
{% endif %}
|
|
59
|
+
{% endfor %}
|
|
60
|
+
{{ unique_agents|length }}
|
|
61
|
+
</div>
|
|
62
|
+
<div class="metric-trend">In network</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="metric-card">
|
|
65
|
+
<div class="metric-label">Deepest Chain</div>
|
|
66
|
+
<div class="metric-value" id="deepest-chain">—</div>
|
|
67
|
+
<div class="metric-trend">Hops in longest path</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="metric-card">
|
|
70
|
+
<div class="metric-label">Avg Chain Length</div>
|
|
71
|
+
<div class="metric-value" id="avg-chain-length">—</div>
|
|
72
|
+
<div class="metric-trend">Mean delegation depth</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<!-- Delegation List -->
|
|
78
|
+
{% if delegations %}
|
|
79
|
+
<div style="margin-top: var(--spacing-2xl); padding-top: var(--spacing-xl); border-top: 1px solid var(--border-subtle);">
|
|
80
|
+
<h3 style="color: var(--accent-lime); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: var(--spacing-lg);">
|
|
81
|
+
Recent Delegations
|
|
82
|
+
</h3>
|
|
83
|
+
<div style="background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: 2px; overflow: hidden;">
|
|
84
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
85
|
+
<thead style="background: var(--bg-darker); border-bottom: 1px solid var(--border-subtle);">
|
|
86
|
+
<tr>
|
|
87
|
+
<th style="padding: var(--spacing-lg); text-align: left; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">From</th>
|
|
88
|
+
<th style="padding: var(--spacing-lg); text-align: left; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">To</th>
|
|
89
|
+
<th style="padding: var(--spacing-lg); text-align: left; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">Task</th>
|
|
90
|
+
<th style="padding: var(--spacing-lg); text-align: left; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">Timestamp</th>
|
|
91
|
+
</tr>
|
|
92
|
+
</thead>
|
|
93
|
+
<tbody>
|
|
94
|
+
{% for delegation in delegations[:20] %}
|
|
95
|
+
<tr style="border-bottom: 1px solid var(--border-subtle);">
|
|
96
|
+
<td style="padding: var(--spacing-lg);">
|
|
97
|
+
<span style="background: rgba(139, 92, 246, 0.1); border: 1px solid rgba(139, 92, 246, 0.3); padding: var(--spacing-xs) var(--spacing-sm); border-radius: 2px; color: var(--agent-claude); font-size: 0.85rem; font-weight: 600;">
|
|
98
|
+
{{ delegation.from_agent }}
|
|
99
|
+
</span>
|
|
100
|
+
</td>
|
|
101
|
+
<td style="padding: var(--spacing-lg);">
|
|
102
|
+
<span style="background: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.3); padding: var(--spacing-xs) var(--spacing-sm); border-radius: 2px; color: var(--agent-gemini); font-size: 0.85rem; font-weight: 600;">
|
|
103
|
+
{{ delegation.to_agent }}
|
|
104
|
+
</span>
|
|
105
|
+
</td>
|
|
106
|
+
<td style="padding: var(--spacing-lg); color: var(--text-secondary); max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
|
107
|
+
{% if delegation.task %}
|
|
108
|
+
{{ delegation.task[:60] }}{% if delegation.task|length > 60 %}...{% endif %}
|
|
109
|
+
{% else %}
|
|
110
|
+
<span style="color: var(--text-muted);">—</span>
|
|
111
|
+
{% endif %}
|
|
112
|
+
</td>
|
|
113
|
+
<td style="padding: var(--spacing-lg); color: var(--text-secondary); font-family: 'Courier New', monospace; font-size: 0.9rem;">
|
|
114
|
+
{{ delegation.timestamp }}
|
|
115
|
+
</td>
|
|
116
|
+
</tr>
|
|
117
|
+
{% endfor %}
|
|
118
|
+
</tbody>
|
|
119
|
+
</table>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
{% else %}
|
|
123
|
+
<div style="margin-top: var(--spacing-2xl); padding: var(--spacing-2xl); background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: 2px; text-align: center; color: var(--text-muted);">
|
|
124
|
+
<p style="font-size: 1.1rem; margin-bottom: var(--spacing-md);">No delegation chains found</p>
|
|
125
|
+
<small>Agent-to-agent delegations will appear here as tasks are assigned</small>
|
|
126
|
+
</div>
|
|
127
|
+
{% endif %}
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<script>
|
|
131
|
+
// Graph rendering for orchestration DAG
|
|
132
|
+
class OrchestrationGraph {
|
|
133
|
+
constructor(delegations) {
|
|
134
|
+
this.delegations = delegations;
|
|
135
|
+
this.nodes = [];
|
|
136
|
+
this.edges = [];
|
|
137
|
+
this.nodeMap = new Map();
|
|
138
|
+
this.init();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
init() {
|
|
142
|
+
// Build node map from delegations
|
|
143
|
+
this.delegations.forEach(d => {
|
|
144
|
+
if (!this.nodeMap.has(d.from_agent)) {
|
|
145
|
+
this.nodeMap.set(d.from_agent, {
|
|
146
|
+
id: d.from_agent,
|
|
147
|
+
label: d.from_agent,
|
|
148
|
+
x: 0,
|
|
149
|
+
y: 0,
|
|
150
|
+
inDegree: 0,
|
|
151
|
+
outDegree: 0,
|
|
152
|
+
totalCost: 0
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (!this.nodeMap.has(d.to_agent)) {
|
|
156
|
+
this.nodeMap.set(d.to_agent, {
|
|
157
|
+
id: d.to_agent,
|
|
158
|
+
label: d.to_agent,
|
|
159
|
+
x: 0,
|
|
160
|
+
y: 0,
|
|
161
|
+
inDegree: 0,
|
|
162
|
+
outDegree: 0,
|
|
163
|
+
totalCost: 0
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const fromNode = this.nodeMap.get(d.from_agent);
|
|
168
|
+
const toNode = this.nodeMap.get(d.to_agent);
|
|
169
|
+
|
|
170
|
+
fromNode.outDegree++;
|
|
171
|
+
toNode.inDegree++;
|
|
172
|
+
fromNode.totalCost += d.cost || 0;
|
|
173
|
+
|
|
174
|
+
this.edges.push({
|
|
175
|
+
from: d.from_agent,
|
|
176
|
+
to: d.to_agent,
|
|
177
|
+
task: d.task,
|
|
178
|
+
cost: d.cost || 0
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
this.nodes = Array.from(this.nodeMap.values());
|
|
183
|
+
|
|
184
|
+
// Layout nodes using simple force-directed approach
|
|
185
|
+
this.layoutNodes();
|
|
186
|
+
this.render();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
layoutNodes() {
|
|
190
|
+
const container = document.getElementById('graph-nodes');
|
|
191
|
+
const width = container.parentElement.offsetWidth;
|
|
192
|
+
const height = container.parentElement.offsetHeight;
|
|
193
|
+
|
|
194
|
+
// Arrange nodes horizontally by level (topological sort)
|
|
195
|
+
const levels = this.getNodeLevels();
|
|
196
|
+
const levelCounts = new Map();
|
|
197
|
+
|
|
198
|
+
this.nodes.forEach(node => {
|
|
199
|
+
const level = levels.get(node.id) || 0;
|
|
200
|
+
const count = levelCounts.get(level) || 0;
|
|
201
|
+
levelCounts.set(level, count + 1);
|
|
202
|
+
|
|
203
|
+
const maxLevel = Math.max(...levels.values());
|
|
204
|
+
const levelWidth = width / (maxLevel + 2);
|
|
205
|
+
const levelHeight = height / (Math.max(...levelCounts.values()) + 1);
|
|
206
|
+
|
|
207
|
+
node.x = (level + 1) * levelWidth;
|
|
208
|
+
node.y = (count + 1) * levelHeight;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
getNodeLevels() {
|
|
213
|
+
const levels = new Map();
|
|
214
|
+
const visited = new Set();
|
|
215
|
+
|
|
216
|
+
const visit = (nodeId, level) => {
|
|
217
|
+
if (visited.has(nodeId)) return;
|
|
218
|
+
visited.add(nodeId);
|
|
219
|
+
|
|
220
|
+
levels.set(nodeId, Math.max(levels.get(nodeId) || 0, level));
|
|
221
|
+
|
|
222
|
+
// Visit outgoing nodes
|
|
223
|
+
this.edges
|
|
224
|
+
.filter(e => e.from === nodeId)
|
|
225
|
+
.forEach(e => visit(e.to, level + 1));
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
this.nodes.forEach(node => visit(node.id, 0));
|
|
229
|
+
return levels;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
render() {
|
|
233
|
+
const container = document.getElementById('graph-nodes');
|
|
234
|
+
container.innerHTML = '';
|
|
235
|
+
|
|
236
|
+
// Render edges first (so they appear behind nodes)
|
|
237
|
+
this.renderEdges();
|
|
238
|
+
|
|
239
|
+
// Render nodes
|
|
240
|
+
this.nodes.forEach(node => {
|
|
241
|
+
const nodeEl = document.createElement('div');
|
|
242
|
+
nodeEl.className = 'graph-node';
|
|
243
|
+
nodeEl.style.left = (node.x - 60) + 'px';
|
|
244
|
+
nodeEl.style.top = (node.y - 40) + 'px';
|
|
245
|
+
|
|
246
|
+
// Determine color based on model
|
|
247
|
+
const modelClass = node.id.toLowerCase().includes('gemini') ? 'gemini'
|
|
248
|
+
: node.id.toLowerCase().includes('copilot') ? 'copilot'
|
|
249
|
+
: 'claude';
|
|
250
|
+
nodeEl.classList.add(modelClass);
|
|
251
|
+
|
|
252
|
+
nodeEl.innerHTML = `
|
|
253
|
+
<div class="node-label">${node.label}</div>
|
|
254
|
+
<div class="node-cost">OUT: ${node.outDegree} | IN: ${node.inDegree}</div>
|
|
255
|
+
`;
|
|
256
|
+
|
|
257
|
+
container.appendChild(nodeEl);
|
|
258
|
+
|
|
259
|
+
// Add hover effect
|
|
260
|
+
nodeEl.addEventListener('mouseenter', () => {
|
|
261
|
+
this.highlightConnections(node.id);
|
|
262
|
+
});
|
|
263
|
+
nodeEl.addEventListener('mouseleave', () => {
|
|
264
|
+
this.clearHighlights();
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
renderEdges() {
|
|
270
|
+
const svg = document.getElementById('graph-edges');
|
|
271
|
+
svg.innerHTML = '';
|
|
272
|
+
|
|
273
|
+
this.edges.forEach(edge => {
|
|
274
|
+
const fromNode = this.nodeMap.get(edge.from);
|
|
275
|
+
const toNode = this.nodeMap.get(edge.to);
|
|
276
|
+
|
|
277
|
+
const x1 = fromNode.x + 60;
|
|
278
|
+
const y1 = fromNode.y;
|
|
279
|
+
const x2 = toNode.x - 60;
|
|
280
|
+
const y2 = toNode.y;
|
|
281
|
+
|
|
282
|
+
// Create curved path
|
|
283
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
284
|
+
const curve = `M ${x1} ${y1} Q ${(x1 + x2) / 2} ${(y1 + y2) / 2 - 50} ${x2} ${y2}`;
|
|
285
|
+
|
|
286
|
+
path.setAttribute('d', curve);
|
|
287
|
+
path.setAttribute('stroke', 'rgba(205, 255, 0, 0.3)');
|
|
288
|
+
path.setAttribute('stroke-width', '2');
|
|
289
|
+
path.setAttribute('fill', 'none');
|
|
290
|
+
path.setAttribute('marker-end', 'url(#arrowhead)');
|
|
291
|
+
|
|
292
|
+
svg.appendChild(path);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Add arrow marker
|
|
296
|
+
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
|
297
|
+
const marker = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
|
|
298
|
+
marker.setAttribute('id', 'arrowhead');
|
|
299
|
+
marker.setAttribute('markerWidth', '10');
|
|
300
|
+
marker.setAttribute('markerHeight', '10');
|
|
301
|
+
marker.setAttribute('refX', '9');
|
|
302
|
+
marker.setAttribute('refY', '3');
|
|
303
|
+
marker.setAttribute('orient', 'auto');
|
|
304
|
+
|
|
305
|
+
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
306
|
+
polygon.setAttribute('points', '0 0, 10 3, 0 6');
|
|
307
|
+
polygon.setAttribute('fill', 'rgba(205, 255, 0, 0.5)');
|
|
308
|
+
|
|
309
|
+
marker.appendChild(polygon);
|
|
310
|
+
defs.appendChild(marker);
|
|
311
|
+
svg.appendChild(defs);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
highlightConnections(nodeId) {
|
|
315
|
+
const svg = document.getElementById('graph-edges');
|
|
316
|
+
const paths = svg.querySelectorAll('path');
|
|
317
|
+
|
|
318
|
+
// Dim all paths first
|
|
319
|
+
paths.forEach(p => {
|
|
320
|
+
p.style.opacity = '0.2';
|
|
321
|
+
p.style.stroke = 'rgba(205, 255, 0, 0.2)';
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Highlight connected paths
|
|
325
|
+
this.edges.forEach((edge, idx) => {
|
|
326
|
+
if (edge.from === nodeId || edge.to === nodeId) {
|
|
327
|
+
paths[idx].style.opacity = '1';
|
|
328
|
+
paths[idx].style.stroke = 'rgba(205, 255, 0, 0.8)';
|
|
329
|
+
paths[idx].style.strokeWidth = '3';
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
clearHighlights() {
|
|
335
|
+
const svg = document.getElementById('graph-edges');
|
|
336
|
+
const paths = svg.querySelectorAll('path');
|
|
337
|
+
paths.forEach(p => {
|
|
338
|
+
p.style.opacity = '1';
|
|
339
|
+
p.style.stroke = 'rgba(205, 255, 0, 0.3)';
|
|
340
|
+
p.style.strokeWidth = '2';
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Initialize graph if delegations exist
|
|
346
|
+
const delegationsData = {{ delegations|tojson }};
|
|
347
|
+
if (delegationsData && delegationsData.length > 0) {
|
|
348
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
349
|
+
window.graph = new OrchestrationGraph(delegationsData);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
</script>
|
|
353
|
+
|
|
354
|
+
<style>
|
|
355
|
+
.orchestration-graph {
|
|
356
|
+
background: var(--bg-card);
|
|
357
|
+
border: 1px solid var(--border-subtle);
|
|
358
|
+
border-radius: 2px;
|
|
359
|
+
padding: var(--spacing-xl);
|
|
360
|
+
height: 600px;
|
|
361
|
+
position: relative;
|
|
362
|
+
overflow: hidden;
|
|
363
|
+
margin-bottom: var(--spacing-2xl);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.graph-container {
|
|
367
|
+
width: 100%;
|
|
368
|
+
height: 100%;
|
|
369
|
+
position: relative;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.graph-edges {
|
|
373
|
+
position: absolute;
|
|
374
|
+
top: 0;
|
|
375
|
+
left: 0;
|
|
376
|
+
width: 100%;
|
|
377
|
+
height: 100%;
|
|
378
|
+
pointer-events: none;
|
|
379
|
+
z-index: 0;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.graph-nodes {
|
|
383
|
+
position: relative;
|
|
384
|
+
z-index: 1;
|
|
385
|
+
width: 100%;
|
|
386
|
+
height: 100%;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.graph-node {
|
|
390
|
+
position: absolute;
|
|
391
|
+
background: var(--bg-darker);
|
|
392
|
+
border: 2px solid var(--border-medium);
|
|
393
|
+
border-radius: 4px;
|
|
394
|
+
padding: var(--spacing-md);
|
|
395
|
+
min-width: 140px;
|
|
396
|
+
text-align: center;
|
|
397
|
+
cursor: pointer;
|
|
398
|
+
transition: all var(--transition-base);
|
|
399
|
+
user-select: none;
|
|
400
|
+
transform: translate(-50%, -50%);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.graph-node:hover {
|
|
404
|
+
border-color: var(--accent-lime);
|
|
405
|
+
box-shadow: var(--shadow-lg);
|
|
406
|
+
transform: translate(-50%, -50%) scale(1.1);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.graph-node.claude {
|
|
410
|
+
background: rgba(139, 92, 246, 0.1);
|
|
411
|
+
border-color: var(--agent-claude);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.graph-node.gemini {
|
|
415
|
+
background: rgba(59, 130, 246, 0.1);
|
|
416
|
+
border-color: var(--agent-gemini);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.graph-node.copilot {
|
|
420
|
+
background: rgba(107, 114, 128, 0.1);
|
|
421
|
+
border-color: var(--agent-copilot);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.node-label {
|
|
425
|
+
font-weight: 700;
|
|
426
|
+
color: var(--accent-lime);
|
|
427
|
+
font-size: 0.95rem;
|
|
428
|
+
margin-bottom: var(--spacing-sm);
|
|
429
|
+
text-transform: uppercase;
|
|
430
|
+
letter-spacing: 0.05em;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.node-cost {
|
|
434
|
+
font-size: 0.75rem;
|
|
435
|
+
color: var(--text-secondary);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
@media (max-width: 768px) {
|
|
439
|
+
.orchestration-graph {
|
|
440
|
+
height: 400px;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
</style>
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<div class="view-container orchestration-view">
|
|
2
|
+
<div class="view-header">
|
|
3
|
+
<h2>Agent Orchestration Graph</h2>
|
|
4
|
+
<div class="view-description">
|
|
5
|
+
Real-time visualization of agent delegation chains and multi-hop workflows
|
|
6
|
+
</div>
|
|
7
|
+
<div class="view-filters">
|
|
8
|
+
<div class="filter-group">
|
|
9
|
+
<label>Filter by Agent:</label>
|
|
10
|
+
<select class="filter-select" id="agent-filter">
|
|
11
|
+
<option value="all">All Agents</option>
|
|
12
|
+
{% set agents = [] %}
|
|
13
|
+
{% for d in delegations %}
|
|
14
|
+
{% if d.from_agent not in agents %}
|
|
15
|
+
{% set _ = agents.append(d.from_agent) %}
|
|
16
|
+
{% endif %}
|
|
17
|
+
{% if d.to_agent not in agents %}
|
|
18
|
+
{% set _ = agents.append(d.to_agent) %}
|
|
19
|
+
{% endif %}
|
|
20
|
+
{% endfor %}
|
|
21
|
+
{% for agent in agents|sort %}
|
|
22
|
+
<option value="{{ agent }}">{{ agent }}</option>
|
|
23
|
+
{% endfor %}
|
|
24
|
+
</select>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<!-- Stats -->
|
|
30
|
+
<div style="margin-top: 1rem; padding-top: 0.75rem; border-top: 1px solid var(--border-subtle);">
|
|
31
|
+
<h3 style="color: var(--accent-lime); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.5rem; font-size: 0.85rem;">
|
|
32
|
+
Orchestration Statistics
|
|
33
|
+
</h3>
|
|
34
|
+
<div class="metrics-grid">
|
|
35
|
+
<div class="metric-card">
|
|
36
|
+
<div class="metric-label">Total Delegations</div>
|
|
37
|
+
<div class="metric-value">{{ delegations|length }}</div>
|
|
38
|
+
<div class="metric-trend">Agent-to-agent handoffs</div>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="metric-card">
|
|
41
|
+
<div class="metric-label">Unique Agents</div>
|
|
42
|
+
<div class="metric-value" id="unique-agents">
|
|
43
|
+
{% set unique_agents = {} %}
|
|
44
|
+
{% for d in delegations %}
|
|
45
|
+
{% if d.from_agent not in unique_agents %}
|
|
46
|
+
{% set _ = unique_agents.update({d.from_agent: True}) %}
|
|
47
|
+
{% endif %}
|
|
48
|
+
{% if d.to_agent not in unique_agents %}
|
|
49
|
+
{% set _ = unique_agents.update({d.to_agent: True}) %}
|
|
50
|
+
{% endif %}
|
|
51
|
+
{% endfor %}
|
|
52
|
+
{{ unique_agents|length }}
|
|
53
|
+
</div>
|
|
54
|
+
<div class="metric-trend">In network</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="metric-card">
|
|
57
|
+
<div class="metric-label">Deepest Chain</div>
|
|
58
|
+
<div class="metric-value" id="deepest-chain">—</div>
|
|
59
|
+
<div class="metric-trend">Hops in longest path</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="metric-card">
|
|
62
|
+
<div class="metric-label">Avg Chain Length</div>
|
|
63
|
+
<div class="metric-value" id="avg-chain-length">—</div>
|
|
64
|
+
<div class="metric-trend">Mean delegation depth</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- Delegation List -->
|
|
70
|
+
{% if delegations %}
|
|
71
|
+
<div style="margin-top: 1rem; padding-top: 0.75rem; border-top: 1px solid var(--border-subtle);">
|
|
72
|
+
<h3 style="color: var(--accent-lime); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.5rem; font-size: 0.85rem;">
|
|
73
|
+
Recent Delegations
|
|
74
|
+
</h3>
|
|
75
|
+
<div style="background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: 2px; overflow: hidden;">
|
|
76
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
77
|
+
<thead style="background: var(--bg-darker); border-bottom: 1px solid var(--border-subtle);">
|
|
78
|
+
<tr>
|
|
79
|
+
<th style="padding: 0.5rem 0.75rem; text-align: left; color: var(--accent-lime); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;">From</th>
|
|
80
|
+
<th style="padding: 0.5rem 0.75rem; text-align: left; color: var(--accent-lime); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;">To</th>
|
|
81
|
+
<th style="padding: 0.5rem 0.75rem; text-align: left; color: var(--accent-lime); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;">Task</th>
|
|
82
|
+
<th style="padding: 0.5rem 0.75rem; text-align: left; color: var(--accent-lime); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;">Timestamp</th>
|
|
83
|
+
</tr>
|
|
84
|
+
</thead>
|
|
85
|
+
<tbody>
|
|
86
|
+
{% for delegation in delegations[:20] %}
|
|
87
|
+
<tr style="border-bottom: 1px solid var(--border-subtle);">
|
|
88
|
+
<td style="padding: 0.4rem 0.75rem;">
|
|
89
|
+
<span style="background: rgba(139, 92, 246, 0.1); border: 1px solid rgba(139, 92, 246, 0.3); padding: 0.15rem 0.4rem; border-radius: 2px; color: var(--agent-claude); font-size: 0.75rem; font-weight: 600;">
|
|
90
|
+
{{ delegation.from_agent }}
|
|
91
|
+
</span>
|
|
92
|
+
</td>
|
|
93
|
+
<td style="padding: 0.4rem 0.75rem;">
|
|
94
|
+
<span style="background: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.3); padding: 0.15rem 0.4rem; border-radius: 2px; color: var(--agent-gemini); font-size: 0.75rem; font-weight: 600;">
|
|
95
|
+
{{ delegation.to_agent }}
|
|
96
|
+
</span>
|
|
97
|
+
</td>
|
|
98
|
+
<td style="padding: 0.4rem 0.75rem; color: var(--text-secondary); max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.8rem;">
|
|
99
|
+
{% if delegation.task %}
|
|
100
|
+
{{ delegation.task[:60] }}{% if delegation.task|length > 60 %}...{% endif %}
|
|
101
|
+
{% else %}
|
|
102
|
+
<span style="color: var(--text-muted);">—</span>
|
|
103
|
+
{% endif %}
|
|
104
|
+
</td>
|
|
105
|
+
<td style="padding: 0.4rem 0.75rem; color: var(--text-secondary); font-family: 'Courier New', monospace; font-size: 0.75rem;">
|
|
106
|
+
{{ delegation.timestamp }}
|
|
107
|
+
</td>
|
|
108
|
+
</tr>
|
|
109
|
+
{% endfor %}
|
|
110
|
+
</tbody>
|
|
111
|
+
</table>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
{% else %}
|
|
115
|
+
<div style="margin-top: 1rem; padding: 1rem; background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: 2px; text-align: center; color: var(--text-muted);">
|
|
116
|
+
<p style="font-size: 0.9rem; margin-bottom: 0.5rem;">No delegation chains found</p>
|
|
117
|
+
<small style="font-size: 0.75rem;">Agent-to-agent delegations will appear here as tasks are assigned</small>
|
|
118
|
+
</div>
|
|
119
|
+
{% endif %}
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<style>
|
|
123
|
+
/* Compact view header */
|
|
124
|
+
.orchestration-view .view-header {
|
|
125
|
+
margin-bottom: 0.75rem;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.orchestration-view .view-description {
|
|
129
|
+
margin-bottom: 0.5rem;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Compact stats section */
|
|
133
|
+
.orchestration-view .metrics-grid {
|
|
134
|
+
display: grid;
|
|
135
|
+
grid-template-columns: repeat(4, 1fr);
|
|
136
|
+
gap: 0.75rem;
|
|
137
|
+
margin-bottom: 1rem;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.orchestration-view .metric-card {
|
|
141
|
+
padding: 0.75rem 1rem;
|
|
142
|
+
min-height: auto;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.orchestration-view .metric-value {
|
|
146
|
+
font-size: 1.5rem;
|
|
147
|
+
margin: 0.25rem 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.orchestration-view .metric-label {
|
|
151
|
+
font-size: 0.7rem;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.orchestration-view .metric-trend {
|
|
155
|
+
font-size: 0.65rem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@media (max-width: 768px) {
|
|
159
|
+
.orchestration-view .metrics-grid {
|
|
160
|
+
grid-template-columns: repeat(2, 1fr);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
</style>
|