Run trace
One agent run recorded as a trace on a time axis: a playhead moves along it and five steps run one after another, each with its tool calls nested under it; the test step takes most of the run and is marked slow, and once the evals finish the pull request opens.
= max((4.0 + 7.0 * c - X0) / PX, a) ? 1.0 : 0.0; }
float field(vec2 p, float t) {
if (p.x < 18.0 || p.x > 702.0 || p.y < 18.0 || p.y > 442.0) return 0.0;
float v = 0.0;
float gr = floor(p.y / 9.4); // the cell row
float c = floor((p.x - 4.0) / 7.0 + 0.5); // the bar column
float hs = hash(vec2(c, gr) + 2.7);
// This run's clock: before 0.6 s the last run is still on screen, so it reads the clock a cycle on and clears
// from 0.3 to 0.5 s.
float tt = t < 0.6 ? t + T : t;
float env = 1.0 - sm(T + 0.3, T + 0.5, tt);
float now = (tt - 0.6) / 0.0418; // seconds into the run
float cEnd = 96.0;
// The trace's frame: a faint gridline every 10 s of the axis, dotted, and a faint track along each step's row.
if (c >= 22.0 && c <= 97.0) {
float sx = (4.0 + 7.0 * c - X0) / PX;
if (abs(sx - 10.0 * floor(sx / 10.0 + 0.5)) < 0.6 && gr >= 11.0 && gr <= 40.0 && mod(gr, 2.0) > 0.5) v = 0.17;
if (gr == 13.0 && hs > 0.3) v = max(v, 0.2);
if (gr == 18.0 && hs > 0.3) v = max(v, 0.2);
if (gr == 24.0 && hs > 0.3) v = max(v, 0.2);
if (gr == 30.0 && hs > 0.3) v = max(v, 0.2);
if (gr == 38.0 && hs > 0.3) v = max(v, 0.2);
}
// The RUN root span: two rows of the densest bars from 0 to the playhead, and a row of notches under them, the
// first column of each step marked dense.
if (c >= 22.0 && c <= cEnd) {
if (gr >= 4.0 && gr <= 5.0) v = max(v, env * L(c, 0.0, now) * 1.05);
if (gr == 6.0) {
bool mark = c == 22.0 || c == 24.0 || c == 38.0 || c == 44.0 || c == 90.0;
v = max(v, env * L(c, 0.0, now) * (mark ? 1.05 : 0.3));
}
}
// The sandbox's CPU under the root span: a bar a column, as high as the CPU was busy then (low while the agent waits
// on a model, high while tests run), drawn as the playhead reaches it.
if (c >= 22.0 && c <= cEnd && gr >= 7.0 && gr <= 9.0) {
float sx = (4.0 + 7.0 * c - X0) / PX;
float busy = (sx < 0.3 ? 0.45 : (sx < 0.9 ? 0.5 : (sx < 1.5 ? 0.5 : (sx < 2.1 ? 0.45 : (sx < 8.3 ? 0.15 : (sx < 9.1 ? 0.5 : (sx < 10.2 ? 0.45 : (sx < 11.0 ? 0.5 : (sx < 16.9 ? 0.15 : (sx < 17.8 ? 0.45 : (sx < 21.5 ? 0.15 : (sx < 22.3 ? 0.4 : (sx < 23.2 ? 0.6 : (sx < 26.3 ? 0.65 : (sx < 40.9 ? 0.95 : (sx < 47.1 ? 0.9 : (sx < 56.9 ? 0.95 : (sx < 61.3 ? 0.85 : (sx < 68.6 ? 0.95 : (sx < 71.4 ? 0.8 : (sx < 72.9 ? 0.35 : (sx < 74.4 ? 0.3 : (sx < 75.9 ? 0.35 : (sx < 77.4 ? 0.3 : 0.35))))))))))))))))))))))));
float h = clamp(floor(busy * 3.0 * (0.72 + 0.45 * hash(vec2(c, 4.1))) + 0.5), 0.0, 3.0);
float lv = 9.0 - gr;
float bar = lv < h ? 1.05 : (lv < 0.5 ? 0.3 : 0.0);
v = max(v, env * L(c, 0.0, now) * bar);
}
// The steps and their child spans.
if (gr >= 12.0 && gr <= 12.0 && c >= 22.0 && c <= 23.0) v = max(v, env * L(c, 0.0, now) * 1.05);
if (gr >= 13.0 && gr <= 13.0 && c >= 22.0 && c <= 23.0) v = max(v, env * L(c, 0.0, now) * (c == 22.0 || c == 23.0 ? 0.6 : 1.05));
if (gr >= 14.0 && gr <= 14.0 && c >= 22.0 && c <= 22.0) v = max(v, env * L(c, 0.0, now) * (c < 22.5 ? 0.7 : 0.42));
if (gr >= 15.0 && gr <= 15.0 && c >= 22.0 && c <= 22.0) v = max(v, env * L(c, 0.3, now) * (c < 22.5 ? 0.7 : 0.42));
if (gr >= 14.0 && gr <= 14.0 && c >= 23.0 && c <= 23.0) v = max(v, env * L(c, 0.9, now) * (c < 23.5 ? 0.7 : 0.42));
if (gr >= 15.0 && gr <= 15.0 && c >= 23.0 && c <= 23.0) v = max(v, env * L(c, 1.5, now) * (c < 23.5 ? 0.7 : 0.42));
if (gr >= 17.0 && gr <= 17.0 && c >= 24.0 && c <= 37.0) v = max(v, env * L(c, 2.1, now) * 1.05);
if (gr >= 18.0 && gr <= 18.0 && c >= 24.0 && c <= 37.0) v = max(v, env * L(c, 2.1, now) * (c == 30.0 || c == 31.0 || c == 32.0 ? 0.6 : 1.05));
if (gr >= 19.0 && gr <= 19.0 && c >= 24.0 && c <= 29.0) v = max(v, env * L(c, 2.1, now) * 0.7);
if (gr >= 20.0 && gr <= 20.0 && c >= 30.0 && c <= 30.0) v = max(v, env * L(c, 8.3, now) * (c < 30.5 ? 0.7 : 0.42));
if (gr >= 21.0 && gr <= 21.0 && c >= 31.0 && c <= 31.0) v = max(v, env * L(c, 9.1, now) * (c < 31.5 ? 0.7 : 0.42));
if (gr >= 20.0 && gr <= 20.0 && c >= 32.0 && c <= 32.0) v = max(v, env * L(c, 10.2, now) * (c < 32.5 ? 0.7 : 0.42));
if (gr >= 19.0 && gr <= 19.0 && c >= 32.0 && c <= 37.0) v = max(v, env * L(c, 11.0, now) * 0.7);
if (gr >= 23.0 && gr <= 23.0 && c >= 38.0 && c <= 43.0) v = max(v, env * L(c, 16.9, now) * 1.05);
if (gr >= 24.0 && gr <= 24.0 && c >= 38.0 && c <= 43.0) v = max(v, env * L(c, 16.9, now) * (c == 39.0 || c == 42.0 || c == 43.0 ? 0.6 : 1.05));
if (gr >= 26.0 && gr <= 26.0 && c >= 38.0 && c <= 38.0) v = max(v, env * L(c, 16.9, now) * (c < 38.5 ? 0.7 : 0.42));
if (gr >= 25.0 && gr <= 25.0 && c >= 39.0 && c <= 41.0) v = max(v, env * L(c, 17.8, now) * 0.7);
if (gr >= 26.0 && gr <= 26.0 && c >= 42.0 && c <= 42.0) v = max(v, env * L(c, 21.5, now) * (c < 42.5 ? 0.7 : 0.42));
if (gr >= 26.0 && gr <= 26.0 && c >= 43.0 && c <= 43.0) v = max(v, env * L(c, 22.3, now) * (c < 43.5 ? 0.7 : 0.42));
if (gr >= 29.0 && gr <= 29.0 && c >= 44.0 && c <= 89.0) v = max(v, env * L(c, 23.2, now) * 1.05);
if (gr >= 30.0 && gr <= 30.0 && c >= 44.0 && c <= 89.0) v = max(v, env * L(c, 23.2, now) * (c == 47.0 || c == 61.0 || c == 66.0 || c == 76.0 || c == 80.0 || c == 87.0 ? 0.6 : 1.05));
if (gr == 31.0 && c >= 44.0 && c <= 89.0) v = max(v, env * L(c, 23.2, now) * (c > 46.7971 && hash(vec2(c, 7.3)) > 0.42 ? 1.05 : 0.42));
if (gr >= 32.0 && gr <= 32.0 && c >= 44.0 && c <= 46.0) v = max(v, env * L(c, 23.2, now) * (c < 44.5 ? 0.7 : 0.42));
if (gr >= 33.0 && gr <= 33.0 && c >= 47.0 && c <= 60.0) v = max(v, env * L(c, 26.3, now) * 0.7);
if (gr >= 34.0 && gr <= 34.0 && c >= 61.0 && c <= 65.0) v = max(v, env * L(c, 40.9, now) * 0.7);
if (gr >= 32.0 && gr <= 32.0 && c >= 66.0 && c <= 75.0) v = max(v, env * L(c, 47.1, now) * 0.7);
if (gr >= 33.0 && gr <= 33.0 && c >= 76.0 && c <= 79.0) v = max(v, env * L(c, 56.9, now) * 0.7);
if (gr >= 34.0 && gr <= 34.0 && c >= 80.0 && c <= 86.0) v = max(v, env * L(c, 61.3, now) * 0.7);
if (gr >= 32.0 && gr <= 32.0 && c >= 87.0 && c <= 88.0) v = max(v, env * L(c, 68.6, now) * 0.7);
if (gr >= 37.0 && gr <= 37.0 && c >= 90.0 && c <= 96.0) v = max(v, env * L(c, 71.4, now) * 1.05);
if (gr >= 38.0 && gr <= 38.0 && c >= 90.0 && c <= 96.0) v = max(v, env * L(c, 71.4, now) * (c == 91.0 || c == 92.0 || c == 94.0 || c == 95.0 ? 0.6 : 1.05));
if (gr >= 39.0 && gr <= 39.0 && c >= 90.0 && c <= 90.0) v = max(v, env * L(c, 71.4, now) * 0.7);
if (gr >= 40.0 && gr <= 40.0 && c >= 91.0 && c <= 91.0) v = max(v, env * L(c, 72.9, now) * 0.7);
if (gr >= 39.0 && gr <= 39.0 && c >= 92.0 && c <= 93.0) v = max(v, env * L(c, 74.4, now) * 0.7);
if (gr >= 40.0 && gr <= 40.0 && c >= 94.0 && c <= 94.0) v = max(v, env * L(c, 75.9, now) * 0.7);
if (gr >= 39.0 && gr <= 39.0 && c >= 95.0 && c <= 95.0) v = max(v, env * L(c, 77.4, now) * 0.7);
// The run's end: the root span's last column flares as the run ends.
if (c > cEnd - 1.5 && c < cEnd + 0.5 && gr >= 4.0 && gr <= 6.0) v += env * 0.6 * exp(-pow((tt - 3.95) / 0.06, 2.0));
// The playhead: a line down the trace at the column it is on, with a head on the ruler. This run's shows from
// 0.4 s at 0 and moves at the run's pace; the last run's stays at the end until the trace clears.
float pNew = t >= 0.4 ? sm(0.4, 0.6, t) : 0.0;
float pOld = t < 0.6 ? env : 0.0;
float cNew = floor((X0 + PX * clamp((t - 0.6) / 0.0418, 0.0, 78.9) - 4.0) / 7.0);
float cOld = floor((X0 + PX * 78.9 - 4.0) / 7.0);
float ph = max(c == cNew ? pNew : 0.0, c == cOld ? pOld : 0.0);
if (ph > 0.0) {
if (gr >= 3.0 && gr <= 40.0) v = max(v, 0.36 * ph);
if (gr == 41.0 || gr == 42.0) v = max(v, 1.05 * ph);
}
float ph2 = max(abs(c - cNew) < 1.5 ? pNew : 0.0, abs(c - cOld) < 1.5 ? pOld : 0.0);
if (gr == 42.0) v = max(v, 0.7 * ph2);
return v;
}]]>
A playhead moves along the run. Each step sits on the time axis with its tool calls under it; the test step takes most of the run and is marked slow, and the pull request opens once the evals finish.
Every run has a trace from the moment it leaves the queue. Open it in the dashboard, or print it in the terminal:
kestrel trace run_8f2c
run 8f2c · fix flaky checkout test PR opened
step model time
read_repo small 2.1 s
plan_fix large 14.8 s
edit checkout.test.ts medium 6.3 s
run_tests none 48.2 s slow
evals small 7.5 s
Field Example namerun_testsmodelmedium, or none for steps without a modelscore0.62, the routing scoredurationMs48200toolCallsEach call with its input and output tokensInput and output tokens for the step
const trace = await kestrel.runs. trace ( "run_8f2c" );
const slowest = trace.steps. toSorted (( a , b ) => b.durationMs - a.durationMs)[ 0 ];
console. log (slowest.name, slowest.durationMs); // "run_tests", 48200 trace = kestrel.runs.trace( "run_8f2c" )
slowest = max (trace.steps, key =lambda s: s.duration_ms)
print (slowest.name, slowest.duration_ms) # "run_tests", 48200
Traces export as OpenTelemetry spans. Add an endpoint and every run is sent as it ends.
traces :
export :
otlp : https://otel.acme.dev/v1/traces Note Traces are kept for 7 days on Hobby, 90 days on Team and a year on Enterprise.