agents-harness 0.1.1 → 0.2.0
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.
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/resume.js +14 -0
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/core/context-manager.js +39 -38
- package/dist/core/context-manager.js.map +1 -1
- package/dist/core/types.d.ts +14 -0
- package/dist/dashboard/server.d.ts +6 -1
- package/dist/dashboard/server.js +91 -5
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/socket.d.ts +1 -1
- package/dist/dashboard/socket.js +9 -1
- package/dist/dashboard/socket.js.map +1 -1
- package/dist/dashboard/static/.gitkeep +0 -0
- package/dist/dashboard/static/index.html +313 -0
- package/dist/dashboard/static/static/.gitkeep +0 -0
- package/dist/dashboard/static/static/index.html +614 -0
- package/package.json +3 -2
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>agents-harness</title>
|
|
7
|
+
<style>
|
|
8
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
9
|
+
body {
|
|
10
|
+
font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
|
|
11
|
+
background: #0d1117; color: #c9d1d9; line-height: 1.5; padding: 16px;
|
|
12
|
+
}
|
|
13
|
+
header {
|
|
14
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
15
|
+
padding: 12px 16px; border-bottom: 1px solid #30363d; margin-bottom: 16px;
|
|
16
|
+
}
|
|
17
|
+
header h1 { font-size: 16px; font-weight: 600; }
|
|
18
|
+
.header-stats { display: flex; gap: 16px; font-size: 13px; color: #8b949e; }
|
|
19
|
+
.header-stats span { color: #c9d1d9; }
|
|
20
|
+
#connection-dot {
|
|
21
|
+
width: 8px; height: 8px; border-radius: 50%; display: inline-block;
|
|
22
|
+
background: #f85149; margin-right: 8px; vertical-align: middle;
|
|
23
|
+
}
|
|
24
|
+
#connection-dot.connected { background: #3fb950; }
|
|
25
|
+
|
|
26
|
+
.sprint-card {
|
|
27
|
+
background: #161b22; border: 1px solid #30363d; border-radius: 6px;
|
|
28
|
+
padding: 12px 16px; margin-bottom: 8px; cursor: pointer; user-select: none;
|
|
29
|
+
}
|
|
30
|
+
.sprint-card:hover { border-color: #58a6ff; }
|
|
31
|
+
.sprint-header {
|
|
32
|
+
display: flex; align-items: center; gap: 12px; font-size: 14px;
|
|
33
|
+
}
|
|
34
|
+
.sprint-header .icon { font-size: 14px; width: 20px; text-align: center; }
|
|
35
|
+
.sprint-header .name { font-weight: 600; }
|
|
36
|
+
.sprint-header .meta { margin-left: auto; color: #8b949e; font-size: 12px; }
|
|
37
|
+
.status-passed { color: #3fb950; }
|
|
38
|
+
.status-failed { color: #f85149; }
|
|
39
|
+
.status-progress { color: #d29922; }
|
|
40
|
+
|
|
41
|
+
.sprint-details {
|
|
42
|
+
display: none; margin-top: 10px; padding-top: 10px;
|
|
43
|
+
border-top: 1px solid #30363d; font-size: 12px;
|
|
44
|
+
}
|
|
45
|
+
.sprint-details.open { display: block; }
|
|
46
|
+
.criteria-list { padding-left: 8px; margin: 4px 0; }
|
|
47
|
+
.criteria-list .pass { color: #3fb950; }
|
|
48
|
+
.criteria-list .fail { color: #f85149; }
|
|
49
|
+
.critique { color: #8b949e; margin-top: 6px; font-style: italic; }
|
|
50
|
+
|
|
51
|
+
.activity-section {
|
|
52
|
+
background: #161b22; border: 1px solid #30363d; border-radius: 6px;
|
|
53
|
+
margin-top: 16px; max-height: 240px; overflow-y: auto;
|
|
54
|
+
}
|
|
55
|
+
.activity-section h2 {
|
|
56
|
+
font-size: 13px; padding: 10px 16px; border-bottom: 1px solid #30363d;
|
|
57
|
+
position: sticky; top: 0; background: #161b22; z-index: 1;
|
|
58
|
+
}
|
|
59
|
+
.activity-entry {
|
|
60
|
+
padding: 4px 16px; font-size: 12px; border-bottom: 1px solid #21262d;
|
|
61
|
+
}
|
|
62
|
+
.activity-entry .time { color: #8b949e; margin-right: 8px; }
|
|
63
|
+
.activity-entry .role { color: #58a6ff; margin-right: 4px; }
|
|
64
|
+
|
|
65
|
+
.budget-bar {
|
|
66
|
+
margin-top: 16px; background: #161b22; border: 1px solid #30363d;
|
|
67
|
+
border-radius: 6px; padding: 10px 16px;
|
|
68
|
+
}
|
|
69
|
+
.budget-bar .label {
|
|
70
|
+
font-size: 12px; color: #8b949e; margin-bottom: 6px;
|
|
71
|
+
display: flex; justify-content: space-between;
|
|
72
|
+
}
|
|
73
|
+
.bar-track {
|
|
74
|
+
height: 8px; background: #21262d; border-radius: 4px; overflow: hidden;
|
|
75
|
+
}
|
|
76
|
+
.bar-fill {
|
|
77
|
+
height: 100%; background: #58a6ff; border-radius: 4px;
|
|
78
|
+
transition: width 0.3s ease;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.run-complete-banner {
|
|
82
|
+
text-align: center; padding: 16px; margin-top: 16px;
|
|
83
|
+
border: 1px solid #30363d; border-radius: 6px; font-size: 14px;
|
|
84
|
+
}
|
|
85
|
+
.run-complete-banner.completed { border-color: #3fb950; color: #3fb950; }
|
|
86
|
+
.run-complete-banner.failed { border-color: #f85149; color: #f85149; }
|
|
87
|
+
.run-complete-banner.stopped { border-color: #d29922; color: #d29922; }
|
|
88
|
+
|
|
89
|
+
.empty-state {
|
|
90
|
+
text-align: center; color: #8b949e; padding: 48px 16px; font-size: 14px;
|
|
91
|
+
}
|
|
92
|
+
</style>
|
|
93
|
+
</head>
|
|
94
|
+
<body>
|
|
95
|
+
<header>
|
|
96
|
+
<h1><span id="connection-dot"></span>agents-harness</h1>
|
|
97
|
+
<div class="header-stats">
|
|
98
|
+
<div>Cost: <span id="total-cost">$0.00</span></div>
|
|
99
|
+
<div>Duration: <span id="duration">0m 0s</span></div>
|
|
100
|
+
</div>
|
|
101
|
+
</header>
|
|
102
|
+
|
|
103
|
+
<div id="sprints-container">
|
|
104
|
+
<div class="empty-state" id="empty-state">Waiting for events...</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="activity-section">
|
|
108
|
+
<h2>Activity Stream</h2>
|
|
109
|
+
<div id="activity-log"></div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<div class="budget-bar">
|
|
113
|
+
<div class="label">
|
|
114
|
+
<span>Budget</span>
|
|
115
|
+
<span id="budget-text">$0.00 / $0.00</span>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="bar-track"><div class="bar-fill" id="budget-fill" style="width:0%"></div></div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div id="run-banner"></div>
|
|
121
|
+
|
|
122
|
+
<script>
|
|
123
|
+
(function() {
|
|
124
|
+
const state = {
|
|
125
|
+
sprints: {},
|
|
126
|
+
totalCost: 0,
|
|
127
|
+
budget: 0,
|
|
128
|
+
activities: [],
|
|
129
|
+
startTime: Date.now(),
|
|
130
|
+
runComplete: false,
|
|
131
|
+
expanded: {},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const MAX_ACTIVITIES = 50;
|
|
135
|
+
let ws = null;
|
|
136
|
+
let durationInterval = null;
|
|
137
|
+
|
|
138
|
+
// --- WebSocket ---
|
|
139
|
+
function connect() {
|
|
140
|
+
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
141
|
+
ws = new WebSocket(`${proto}//${location.host}`);
|
|
142
|
+
ws.onopen = () => {
|
|
143
|
+
document.getElementById('connection-dot').classList.add('connected');
|
|
144
|
+
};
|
|
145
|
+
ws.onclose = () => {
|
|
146
|
+
document.getElementById('connection-dot').classList.remove('connected');
|
|
147
|
+
setTimeout(connect, 3000);
|
|
148
|
+
};
|
|
149
|
+
ws.onerror = () => { ws.close(); };
|
|
150
|
+
ws.onmessage = (e) => {
|
|
151
|
+
try { handleEvent(JSON.parse(e.data)); } catch {}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function handleEvent(event) {
|
|
156
|
+
const { type, data } = event;
|
|
157
|
+
if (type === 'phase:start') onPhaseStart(data);
|
|
158
|
+
else if (type === 'agent:activity') onActivity(data);
|
|
159
|
+
else if (type === 'evaluation') onEvaluation(data);
|
|
160
|
+
else if (type === 'cost:update') onCostUpdate(data);
|
|
161
|
+
else if (type === 'sprint:complete') onSprintComplete(data);
|
|
162
|
+
else if (type === 'run:complete') onRunComplete(data);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function ensureSprint(num) {
|
|
166
|
+
if (!state.sprints[num]) {
|
|
167
|
+
state.sprints[num] = { status: 'in_progress', attempts: 0, cost: 0, eval: null };
|
|
168
|
+
}
|
|
169
|
+
return state.sprints[num];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function onPhaseStart(d) {
|
|
173
|
+
if (d.sprint > 0) {
|
|
174
|
+
const s = ensureSprint(d.sprint);
|
|
175
|
+
s.attempts = Math.max(s.attempts, d.attempt || 1);
|
|
176
|
+
}
|
|
177
|
+
renderSprints();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function onActivity(d) {
|
|
181
|
+
state.activities.push(d);
|
|
182
|
+
if (state.activities.length > MAX_ACTIVITIES) state.activities.shift();
|
|
183
|
+
if (d.sprint > 0) ensureSprint(d.sprint);
|
|
184
|
+
renderActivity();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function onEvaluation(d) {
|
|
188
|
+
const s = ensureSprint(d.sprint);
|
|
189
|
+
s.attempts = Math.max(s.attempts, d.attempt);
|
|
190
|
+
s.eval = d.result;
|
|
191
|
+
renderSprints();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function onCostUpdate(d) {
|
|
195
|
+
state.totalCost = d.totalCostUsd;
|
|
196
|
+
state.budget = d.budgetUsd;
|
|
197
|
+
document.getElementById('total-cost').textContent = `$${d.totalCostUsd.toFixed(2)}`;
|
|
198
|
+
document.getElementById('budget-text').textContent =
|
|
199
|
+
`$${d.totalCostUsd.toFixed(2)} / $${d.budgetUsd.toFixed(2)}`;
|
|
200
|
+
const pct = d.budgetUsd > 0 ? Math.min(100, (d.totalCostUsd / d.budgetUsd) * 100) : 0;
|
|
201
|
+
document.getElementById('budget-fill').style.width = `${pct}%`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function onSprintComplete(d) {
|
|
205
|
+
const s = ensureSprint(d.sprint);
|
|
206
|
+
s.status = d.status;
|
|
207
|
+
s.attempts = d.attempts;
|
|
208
|
+
s.cost = d.costUsd;
|
|
209
|
+
renderSprints();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function onRunComplete(d) {
|
|
213
|
+
state.runComplete = true;
|
|
214
|
+
if (durationInterval) { clearInterval(durationInterval); durationInterval = null; }
|
|
215
|
+
document.getElementById('duration').textContent = formatDuration(d.durationMs);
|
|
216
|
+
document.getElementById('total-cost').textContent = `$${d.totalCostUsd.toFixed(2)}`;
|
|
217
|
+
|
|
218
|
+
const banner = document.getElementById('run-banner');
|
|
219
|
+
const status = d.status;
|
|
220
|
+
banner.innerHTML = `<div class="run-complete-banner ${status}">Run ${status.toUpperCase()} — ${d.totalSprints} sprint${d.totalSprints !== 1 ? 's' : ''}, $${d.totalCostUsd.toFixed(2)}, ${formatDuration(d.durationMs)}</div>`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// --- Rendering ---
|
|
224
|
+
function renderSprints() {
|
|
225
|
+
const container = document.getElementById('sprints-container');
|
|
226
|
+
const keys = Object.keys(state.sprints).map(Number).sort((a, b) => a - b);
|
|
227
|
+
if (keys.length === 0) return;
|
|
228
|
+
|
|
229
|
+
document.getElementById('empty-state')?.remove();
|
|
230
|
+
|
|
231
|
+
let html = '';
|
|
232
|
+
for (const num of keys) {
|
|
233
|
+
const s = state.sprints[num];
|
|
234
|
+
const icon = s.status === 'passed' ? 'PASS' : s.status === 'failed' ? 'FAIL' : '....';
|
|
235
|
+
const cls = s.status === 'passed' ? 'status-passed' : s.status === 'failed' ? 'status-failed' : 'status-progress';
|
|
236
|
+
const isOpen = state.expanded[num] ? 'open' : '';
|
|
237
|
+
|
|
238
|
+
html += `<div class="sprint-card" onclick="window.__toggle(${num})">`;
|
|
239
|
+
html += `<div class="sprint-header">`;
|
|
240
|
+
html += `<span class="icon ${cls}">[${icon}]</span>`;
|
|
241
|
+
html += `<span class="name">Sprint ${num}</span>`;
|
|
242
|
+
html += `<span class="meta">${s.attempts} attempt${s.attempts !== 1 ? 's' : ''} · $${s.cost.toFixed(2)}</span>`;
|
|
243
|
+
html += `</div>`;
|
|
244
|
+
|
|
245
|
+
if (s.eval) {
|
|
246
|
+
html += `<div class="sprint-details ${isOpen}">`;
|
|
247
|
+
if (s.eval.passedCriteria.length > 0) {
|
|
248
|
+
html += `<div class="criteria-list">`;
|
|
249
|
+
for (const c of s.eval.passedCriteria) html += `<div class="pass">+ ${esc(c)}</div>`;
|
|
250
|
+
html += `</div>`;
|
|
251
|
+
}
|
|
252
|
+
if (s.eval.failedCriteria.length > 0) {
|
|
253
|
+
html += `<div class="criteria-list">`;
|
|
254
|
+
for (const c of s.eval.failedCriteria) html += `<div class="fail">- ${esc(c)}</div>`;
|
|
255
|
+
html += `</div>`;
|
|
256
|
+
}
|
|
257
|
+
if (s.eval.critique) {
|
|
258
|
+
html += `<div class="critique">${esc(s.eval.critique)}</div>`;
|
|
259
|
+
}
|
|
260
|
+
html += `</div>`;
|
|
261
|
+
}
|
|
262
|
+
html += `</div>`;
|
|
263
|
+
}
|
|
264
|
+
container.innerHTML = html;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
window.__toggle = function(num) {
|
|
268
|
+
state.expanded[num] = !state.expanded[num];
|
|
269
|
+
renderSprints();
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
function renderActivity() {
|
|
273
|
+
const log = document.getElementById('activity-log');
|
|
274
|
+
let html = '';
|
|
275
|
+
for (const a of state.activities) {
|
|
276
|
+
const t = new Date(a.timestamp).toLocaleTimeString();
|
|
277
|
+
html += `<div class="activity-entry"><span class="time">${t}</span><span class="role">[${esc(a.role)}]</span> ${esc(a.summary)}</div>`;
|
|
278
|
+
}
|
|
279
|
+
log.innerHTML = html;
|
|
280
|
+
log.scrollTop = log.scrollHeight;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// --- Helpers ---
|
|
284
|
+
function esc(s) {
|
|
285
|
+
const d = document.createElement('div');
|
|
286
|
+
d.textContent = s;
|
|
287
|
+
return d.innerHTML;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function formatDuration(ms) {
|
|
291
|
+
const s = Math.floor(ms / 1000);
|
|
292
|
+
if (s < 60) return `${s}s`;
|
|
293
|
+
const m = Math.floor(s / 60);
|
|
294
|
+
const rs = s % 60;
|
|
295
|
+
if (m < 60) return `${m}m ${rs}s`;
|
|
296
|
+
const h = Math.floor(m / 60);
|
|
297
|
+
const rm = m % 60;
|
|
298
|
+
return `${h}h ${rm}m`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function updateDuration() {
|
|
302
|
+
if (state.runComplete) return;
|
|
303
|
+
const elapsed = Date.now() - state.startTime;
|
|
304
|
+
document.getElementById('duration').textContent = formatDuration(elapsed);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// --- Init ---
|
|
308
|
+
connect();
|
|
309
|
+
durationInterval = setInterval(updateDuration, 1000);
|
|
310
|
+
})();
|
|
311
|
+
</script>
|
|
312
|
+
</body>
|
|
313
|
+
</html>
|
|
File without changes
|