@mtharrison/loupe 1.3.0 → 1.5.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/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  # @mtharrison/loupe
6
6
 
7
+
8
+
7
9
  Loupe is a lightweight local tracing dashboard for LLM applications and agent systems. It captures full request and response payloads with tags and hierarchy context, then serves an inspector UI on `127.0.0.1` with no database, no containers, and no persistence.
8
10
 
9
11
  This package is for local development. Traces live in memory and are cleared on restart.
@@ -38,16 +40,18 @@ npm install @mtharrison/loupe
38
40
 
39
41
  ### Requirements
40
42
 
41
- - Node.js 18 or newer
43
+ - Node.js 20 or newer
42
44
 
43
45
  ## Quick Start
44
46
 
45
- Enable tracing:
47
+ Enable tracing explicitly:
46
48
 
47
49
  ```bash
48
50
  export LLM_TRACE_ENABLED=1
49
51
  ```
50
52
 
53
+ Or just run your app with `NODE_ENV=development`. Loupe now enables tracing implicitly in development and opens the dashboard automatically on first start in an interactive local terminal.
54
+
51
55
  If your app already uses a higher-level model interface or the official OpenAI client, Loupe can wrap that directly instead of requiring manual `record*` calls.
52
56
 
53
57
  ### `wrapOpenAIClient(client, getContext, config?)`
@@ -93,6 +97,8 @@ When the server starts, Loupe prints the local URL:
93
97
  [llm-trace] dashboard: http://127.0.0.1:4319
94
98
  ```
95
99
 
100
+ In `NODE_ENV=development`, Loupe also opens that URL in your browser automatically unless `CI` is set, the terminal is non-interactive, or `LOUPE_OPEN_BROWSER=0`.
101
+
96
102
  If `4319` is already in use and you did not explicitly configure a port, Loupe falls back to another free local port and prints that URL instead.
97
103
 
98
104
  `wrapOpenAIClient()` is structurally typed, so Loupe's runtime API does not require the OpenAI SDK for normal library usage. The repo includes `openai` as a dev dependency for the bundled demo; if your own app instantiates `new OpenAI()` or runs the published example from a consumer install, install `openai` there too.
@@ -142,6 +148,25 @@ export LLM_TRACE_ENABLED=1
142
148
  node examples/nested-tool-call.js
143
149
  ```
144
150
 
151
+ ### Runnable Fully Featured Demo
152
+
153
+ `examples/fully-featured.js` is a credential-free demo that combines the main local tracing features in one session:
154
+
155
+ - a top-level input guardrail span recorded with the low-level lifecycle API
156
+ - a wrapped `invoke()` call with a nested stage span and child actor span
157
+ - a handled child-span error so the dashboard shows both success and failure states
158
+ - a wrapped `stream()` call with reconstructed output and usage
159
+
160
+ Run it with:
161
+
162
+ ```bash
163
+ npm install
164
+ export LLM_TRACE_ENABLED=1
165
+ node examples/fully-featured.js
166
+ ```
167
+
168
+ Supported demo environment variables: `LLM_TRACE_PORT`, `LOUPE_OPEN_BROWSER`.
169
+
145
170
  ## Low-Level Lifecycle API
146
171
 
147
172
  If you need full control over trace boundaries, Loupe exposes a lower-level span lifecycle API modeled on OpenTelemetry concepts: start a span, add events, end it, and record exceptions.
@@ -343,11 +368,12 @@ Environment variables:
343
368
 
344
369
  | Variable | Default | Description |
345
370
  | --- | --- | --- |
346
- | `LLM_TRACE_ENABLED` | `false` | Enables Loupe. |
371
+ | `LLM_TRACE_ENABLED` | `true` when `NODE_ENV=development`, otherwise `false` | Enables Loupe. |
347
372
  | `LLM_TRACE_HOST` | `127.0.0.1` | Host for the local dashboard server. |
348
373
  | `LLM_TRACE_PORT` | `4319` | Port for the local dashboard server. If unset, Loupe tries `4319` first and falls back to a free local port if it is already in use. |
349
374
  | `LLM_TRACE_MAX_TRACES` | `1000` | Maximum number of traces kept in memory. |
350
375
  | `LLM_TRACE_UI_HOT_RELOAD` | auto in local interactive dev | Enables UI rebuild + reload while developing the dashboard itself. |
376
+ | `LOUPE_OPEN_BROWSER` | `enabled` in local `NODE_ENV=development` sessions | Opens the dashboard in your browser on first server start. Set `0` to suppress it. |
351
377
 
352
378
  Programmatic configuration is also available through `getLocalLLMTracer(config)`.
353
379
 
@@ -29,22 +29,26 @@
29
29
  }
30
30
  :root[data-theme=dark] {
31
31
  color-scheme: dark;
32
- --background: #111b29;
33
- --background-strong: #09111c;
34
- --panel: rgba(22, 32, 47, 0.96);
35
- --panel-strong: rgba(28, 40, 59, 0.98);
36
- --panel-soft: rgba(115, 170, 255, 0.16);
37
- --foreground: #f3f7ff;
38
- --muted-foreground: #b6c5d9;
39
- --line: rgba(140, 164, 197, 0.24);
40
- --line-strong: rgba(145, 193, 255, 0.52);
41
- --primary: #9dc8ff;
42
- --primary-strong: #f4f8ff;
43
- --secondary: rgba(120, 155, 212, 0.18);
44
- --warning: #f2bf67;
45
- --danger: #f39bac;
46
- --success: #7ad1aa;
47
- --shadow: 0 30px 90px rgba(1, 8, 20, 0.56);
32
+ --background: #0b1523;
33
+ --background-strong: #040913;
34
+ --panel: rgba(16, 27, 41, 0.9);
35
+ --panel-strong: rgba(12, 22, 35, 0.94);
36
+ --panel-soft: rgba(91, 186, 255, 0.14);
37
+ --surface-elevated: rgba(20, 34, 52, 0.96);
38
+ --surface-overlay: rgba(15, 25, 39, 0.94);
39
+ --surface-highlight: rgba(33, 57, 87, 0.98);
40
+ --surface-code: rgba(7, 13, 23, 0.96);
41
+ --foreground: #ecf4ff;
42
+ --muted-foreground: #9bb0c8;
43
+ --line: rgba(117, 146, 180, 0.24);
44
+ --line-strong: rgba(126, 196, 255, 0.5);
45
+ --primary: #88d0ff;
46
+ --primary-strong: #f5fbff;
47
+ --secondary: rgba(91, 138, 200, 0.18);
48
+ --warning: #ffc57b;
49
+ --danger: #ff9eae;
50
+ --success: #72ddb7;
51
+ --shadow: 0 34px 96px rgba(1, 8, 20, 0.62);
48
52
  }
49
53
  * {
50
54
  box-sizing: border-box;
@@ -90,18 +94,22 @@ body {
90
94
  :root[data-theme=dark] body {
91
95
  background:
92
96
  radial-gradient(
93
- circle at top left,
94
- rgba(108, 158, 235, 0.2),
95
- transparent 34rem),
97
+ circle at 12% 0%,
98
+ rgba(86, 166, 255, 0.28),
99
+ transparent 30rem),
96
100
  radial-gradient(
97
- circle at top right,
98
- rgba(138, 164, 207, 0.12),
99
- transparent 28rem),
101
+ circle at 88% 8%,
102
+ rgba(52, 197, 182, 0.14),
103
+ transparent 26rem),
104
+ radial-gradient(
105
+ circle at 50% 115%,
106
+ rgba(33, 86, 146, 0.4),
107
+ transparent 38rem),
100
108
  linear-gradient(
101
109
  180deg,
102
- #1a2638 0%,
103
- #101a28 45%,
104
- #09111c 100%);
110
+ #162436 0%,
111
+ #0a1320 46%,
112
+ #030811 100%);
105
113
  }
106
114
  button,
107
115
  input,
@@ -402,7 +410,7 @@ pre {
402
410
  .workspace-grid {
403
411
  display: grid;
404
412
  flex: 1;
405
- grid-template-columns: minmax(30rem, 36rem) minmax(0, 1fr);
413
+ grid-template-columns: clamp(34rem, 38vw, 42rem) minmax(0, 1fr);
406
414
  gap: 0.9rem;
407
415
  align-items: stretch;
408
416
  min-height: 0;
@@ -493,7 +501,7 @@ pre {
493
501
  min-height: 0;
494
502
  flex: 1;
495
503
  overflow: auto;
496
- padding-right: 0.12rem;
504
+ padding-right: 0.4rem;
497
505
  scrollbar-gutter: stable;
498
506
  }
499
507
  .session-sidebar-empty {
@@ -1183,13 +1191,24 @@ pre {
1183
1191
  overflow: auto;
1184
1192
  padding-right: 0.2rem;
1185
1193
  }
1194
+ .hierarchy-timeline-empty-state {
1195
+ padding: 0.8rem 0.55rem 0.4rem;
1196
+ color: var(--muted-foreground);
1197
+ font-size: 0.78rem;
1198
+ line-height: 1.45;
1199
+ }
1186
1200
  .hierarchy-timeline-row {
1187
1201
  --timeline-time-column: 4.75rem;
1188
1202
  --timeline-column-gap: 0.45rem;
1189
1203
  --timeline-indent: 1rem;
1190
1204
  --timeline-gutter-base: 1.25rem;
1191
1205
  --timeline-connector-base: 0.22rem;
1206
+ --timeline-row-padding-x: 0.55rem;
1207
+ --timeline-card-radius: 12px;
1208
+ --timeline-card-inset-y: 0.14rem;
1192
1209
  --timeline-gutter-width: calc( var(--timeline-depth, 0) * var(--timeline-indent) + var(--timeline-gutter-base) );
1210
+ --timeline-card-left: calc( var(--timeline-row-padding-x) + var(--timeline-time-column) + var(--timeline-column-gap) + var(--timeline-gutter-width) );
1211
+ --timeline-card-right: var(--timeline-row-padding-x);
1193
1212
  --timeline-row-color: rgba(92, 121, 171, 0.9);
1194
1213
  --timeline-bar-stroke: color-mix(in srgb, var(--timeline-row-color) 74%, white);
1195
1214
  --timeline-connector-color: color-mix( in srgb, var(--timeline-row-color) 28%, rgba(84, 100, 125, 0.18) );
@@ -1201,7 +1220,7 @@ pre {
1201
1220
  width: 100%;
1202
1221
  border: 0;
1203
1222
  background: transparent;
1204
- padding: 0.12rem 0.55rem;
1223
+ padding: 0.12rem var(--timeline-row-padding-x);
1205
1224
  color: inherit;
1206
1225
  cursor: default;
1207
1226
  font: inherit;
@@ -1213,11 +1232,11 @@ pre {
1213
1232
  .hierarchy-timeline-row::before {
1214
1233
  content: "";
1215
1234
  position: absolute;
1216
- top: 0.14rem;
1217
- right: 0.55rem;
1218
- bottom: 0.14rem;
1219
- left: calc(0.55rem + var(--timeline-time-column) + var(--timeline-column-gap) + var(--timeline-gutter-width));
1220
- border-radius: 12px;
1235
+ top: var(--timeline-card-inset-y);
1236
+ right: var(--timeline-card-right);
1237
+ bottom: var(--timeline-card-inset-y);
1238
+ left: var(--timeline-card-left);
1239
+ border-radius: var(--timeline-card-radius);
1221
1240
  border: 1px solid transparent;
1222
1241
  background: rgba(255, 255, 255, 0.42);
1223
1242
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 4px 12px rgba(32, 50, 76, 0.03);
@@ -1248,8 +1267,18 @@ pre {
1248
1267
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 4px 12px rgba(32, 50, 76, 0.02);
1249
1268
  }
1250
1269
  .hierarchy-timeline-row:focus-visible {
1251
- outline: 2px solid rgba(40, 93, 168, 0.22);
1252
- outline-offset: 2px;
1270
+ outline: none;
1271
+ }
1272
+ .hierarchy-timeline-row:focus-visible::after {
1273
+ content: "";
1274
+ position: absolute;
1275
+ top: calc(var(--timeline-card-inset-y) - 0.08rem);
1276
+ right: calc(var(--timeline-card-right) - 0.08rem);
1277
+ bottom: calc(var(--timeline-card-inset-y) - 0.08rem);
1278
+ left: calc(var(--timeline-card-left) - 0.08rem);
1279
+ border-radius: calc(var(--timeline-card-radius) + 2px);
1280
+ box-shadow: 0 0 0 2px rgba(40, 93, 168, 0.18);
1281
+ pointer-events: none;
1253
1282
  }
1254
1283
  .hierarchy-timeline-row.is-in-path::before {
1255
1284
  background: rgba(240, 246, 255, 0.54);
@@ -1280,6 +1309,13 @@ pre {
1280
1309
  .hierarchy-timeline-row.is-guardrail {
1281
1310
  --timeline-row-color: rgba(184, 129, 44, 0.92);
1282
1311
  }
1312
+ .hierarchy-timeline-row.is-guardrail-trace {
1313
+ --timeline-row-color: rgba(184, 129, 44, 0.92);
1314
+ }
1315
+ .hierarchy-timeline-row.is-guardrail-group::before {
1316
+ border-color: color-mix(in srgb, var(--timeline-row-color) 18%, rgba(84, 100, 125, 0.08));
1317
+ background: color-mix(in srgb, var(--timeline-row-color) 10%, rgba(255, 255, 255, 0.72));
1318
+ }
1283
1319
  .hierarchy-timeline-row.is-trace {
1284
1320
  --timeline-row-color: rgba(92, 121, 171, 0.9);
1285
1321
  }
@@ -1381,6 +1417,20 @@ pre {
1381
1417
  min-width: 0;
1382
1418
  flex-wrap: wrap;
1383
1419
  }
1420
+ .hierarchy-timeline-row-toggle-label {
1421
+ display: inline-flex;
1422
+ align-items: center;
1423
+ min-height: 1.3rem;
1424
+ padding: 0.12rem 0.46rem;
1425
+ border-radius: 999px;
1426
+ border: 1px solid rgba(184, 129, 44, 0.16);
1427
+ background: rgba(184, 129, 44, 0.08);
1428
+ color: #865815;
1429
+ font-size: 0.67rem;
1430
+ font-weight: 700;
1431
+ letter-spacing: 0.04em;
1432
+ text-transform: uppercase;
1433
+ }
1384
1434
  .hierarchy-timeline-row-flag {
1385
1435
  display: inline-flex;
1386
1436
  align-items: center;
@@ -1484,6 +1534,25 @@ pre {
1484
1534
  text-align: right;
1485
1535
  white-space: nowrap;
1486
1536
  }
1537
+ .hierarchy-timeline-row.is-guardrail-group .hierarchy-timeline-row-labels,
1538
+ .hierarchy-timeline-row.is-guardrail-compact .hierarchy-timeline-row-labels {
1539
+ gap: 0.14rem;
1540
+ padding-top: 0.42rem;
1541
+ padding-bottom: 0.42rem;
1542
+ }
1543
+ .hierarchy-timeline-row.is-guardrail-group .hierarchy-timeline-row-bars,
1544
+ .hierarchy-timeline-row.is-guardrail-compact .hierarchy-timeline-row-bars {
1545
+ padding-top: 0.42rem;
1546
+ padding-bottom: 0.42rem;
1547
+ }
1548
+ .hierarchy-timeline-row.is-guardrail-group .hierarchy-timeline-row-title-text,
1549
+ .hierarchy-timeline-row.is-guardrail-compact .hierarchy-timeline-row-title-text {
1550
+ font-size: 0.84rem;
1551
+ }
1552
+ .hierarchy-timeline-row.is-guardrail-group .hierarchy-timeline-row-meta,
1553
+ .hierarchy-timeline-row.is-guardrail-compact .hierarchy-timeline-row-meta {
1554
+ font-size: 0.73rem;
1555
+ }
1487
1556
  .trace-detail-header {
1488
1557
  display: flex;
1489
1558
  align-items: flex-start;
@@ -1900,27 +1969,97 @@ pre {
1900
1969
  flex-wrap: wrap;
1901
1970
  }
1902
1971
  .session-tree-timeline-shell {
1903
- padding: 0 0.1rem 0.2rem 2.35rem;
1972
+ padding: 0.08rem 0.1rem 0.24rem 0.5rem;
1973
+ }
1974
+ .session-tree-timeline-toolbar {
1975
+ display: flex;
1976
+ align-items: center;
1977
+ justify-content: space-between;
1978
+ gap: 0.65rem;
1979
+ padding: 0.1rem 0.2rem 0.45rem 0.08rem;
1980
+ }
1981
+ .session-tree-timeline-toolbar-copy {
1982
+ color: var(--muted-foreground);
1983
+ font-size: 0.72rem;
1984
+ font-weight: 600;
1985
+ letter-spacing: 0.01em;
1904
1986
  }
1905
1987
  .session-tree-timeline-list {
1906
1988
  display: flex;
1907
1989
  flex-direction: column;
1908
- gap: 0.45rem;
1990
+ gap: 0;
1909
1991
  }
1910
1992
  .session-tree-timeline-list .hierarchy-timeline-row {
1911
- grid-template-columns: 4rem minmax(0, 1fr) minmax(6rem, 7.25rem);
1912
- gap: 0.4rem;
1993
+ --timeline-time-column: 4.8rem;
1994
+ --timeline-column-gap: 0.4rem;
1995
+ --timeline-indent: 0.72rem;
1996
+ --timeline-gutter-base: 0.75rem;
1997
+ --embedded-bars-space: clamp(10.75rem, 49%, 15.6rem);
1998
+ --timeline-row-padding-x: 0.18rem;
1999
+ --timeline-card-radius: 18px;
2000
+ --timeline-card-inset-y: 0.24rem;
2001
+ --timeline-card-left: calc( var(--timeline-row-padding-x) + var(--timeline-time-column) + var(--timeline-column-gap) + var(--timeline-gutter-width) - 0.12rem );
2002
+ --timeline-card-right: 0.1rem;
2003
+ display: flex;
2004
+ gap: var(--timeline-column-gap);
2005
+ align-items: stretch;
2006
+ max-width: 100%;
2007
+ overflow: visible;
2008
+ }
2009
+ .session-tree-timeline-list .hierarchy-timeline-row-time {
2010
+ flex: 0 0 var(--timeline-time-column);
2011
+ justify-content: flex-start;
2012
+ text-align: left;
2013
+ padding-right: 0;
2014
+ padding-left: 0.08rem;
2015
+ overflow: visible;
2016
+ font-size: 0.74rem;
2017
+ }
2018
+ .session-tree-timeline-list .hierarchy-timeline-row-branch {
2019
+ flex: 1 1 0;
2020
+ min-width: 0;
1913
2021
  }
1914
2022
  .session-tree-timeline-list .hierarchy-timeline-row-labels {
1915
- padding: 0.72rem 0.5rem 0.72rem 1.05rem;
2023
+ min-width: 0;
2024
+ gap: 0.24rem;
2025
+ padding: 0.84rem calc(var(--embedded-bars-space) + 0.8rem) 0.84rem 0.92rem;
1916
2026
  }
1917
2027
  .session-tree-timeline-list .hierarchy-timeline-row-bars {
1918
- grid-template-columns: minmax(4.8rem, 1fr) auto;
1919
- column-gap: 0.35rem;
1920
- padding: 0.72rem 0.8rem 0.72rem 0.2rem;
2028
+ position: absolute;
2029
+ top: 0.84rem;
2030
+ right: 0.92rem;
2031
+ width: var(--embedded-bars-space);
2032
+ min-width: 0;
2033
+ display: flex;
2034
+ align-items: center;
2035
+ justify-content: flex-end;
2036
+ gap: 0.36rem;
2037
+ padding: 0;
2038
+ overflow: hidden;
2039
+ z-index: 2;
1921
2040
  }
1922
2041
  .session-tree-timeline-list .hierarchy-timeline-row-title {
1923
- flex-wrap: nowrap;
2042
+ position: relative;
2043
+ display: block;
2044
+ min-height: 0;
2045
+ padding-top: 2.05rem;
2046
+ }
2047
+ .session-tree-timeline-list .hierarchy-timeline-row.is-guardrail-group .hierarchy-timeline-row-labels,
2048
+ .session-tree-timeline-list .hierarchy-timeline-row.is-guardrail-compact .hierarchy-timeline-row-labels {
2049
+ padding-top: 0.62rem;
2050
+ padding-bottom: 0.62rem;
2051
+ }
2052
+ .session-tree-timeline-list .hierarchy-timeline-row.is-guardrail-group .hierarchy-timeline-row-title,
2053
+ .session-tree-timeline-list .hierarchy-timeline-row.is-guardrail-compact .hierarchy-timeline-row-title {
2054
+ padding-top: 1.7rem;
2055
+ }
2056
+ .session-tree-timeline-list .hierarchy-timeline-row.is-guardrail-group .hierarchy-timeline-row-bars,
2057
+ .session-tree-timeline-list .hierarchy-timeline-row.is-guardrail-compact .hierarchy-timeline-row-bars {
2058
+ top: 0.62rem;
2059
+ }
2060
+ .session-tree-timeline-list .hierarchy-timeline-row.has-semantic-indent .hierarchy-timeline-row-ancestor,
2061
+ .session-tree-timeline-list .hierarchy-timeline-row.has-semantic-indent .hierarchy-timeline-row-connector {
2062
+ display: none;
1924
2063
  }
1925
2064
  .session-tree-timeline-list .hierarchy-timeline-row-title-text,
1926
2065
  .session-tree-timeline-list .hierarchy-timeline-row-meta {
@@ -1929,13 +2068,53 @@ pre {
1929
2068
  white-space: nowrap;
1930
2069
  }
1931
2070
  .session-tree-timeline-list .hierarchy-timeline-row-title-text {
1932
- font-size: 0.84rem;
2071
+ display: block;
2072
+ font-size: 0.86rem;
2073
+ }
2074
+ .session-tree-timeline-list .hierarchy-timeline-pill {
2075
+ position: absolute;
2076
+ top: 0;
2077
+ left: 0;
2078
+ }
2079
+ .session-tree-timeline-list .hierarchy-timeline-row-flag {
2080
+ margin-top: 0.35rem;
2081
+ margin-right: 0.35rem;
1933
2082
  }
1934
2083
  .session-tree-timeline-list .hierarchy-timeline-row-meta,
1935
2084
  .session-tree-timeline-list .hierarchy-timeline-row-time,
1936
2085
  .session-tree-timeline-list .hierarchy-timeline-row-duration {
1937
2086
  font-size: 0.72rem;
1938
2087
  }
2088
+ .session-tree-timeline-list .hierarchy-timeline-row-track {
2089
+ flex: 1 1 auto;
2090
+ min-width: 0;
2091
+ height: 1.3rem;
2092
+ }
2093
+ .session-tree-timeline-list .hierarchy-timeline-row-track::before {
2094
+ height: 0.4rem;
2095
+ }
2096
+ .session-tree-timeline-list .hierarchy-timeline-row-bar {
2097
+ width: max(0.65rem, calc(var(--timeline-span, 0.1) * 100%));
2098
+ height: 0.65rem;
2099
+ }
2100
+ .session-tree-timeline-list .hierarchy-timeline-row-bar::before,
2101
+ .session-tree-timeline-list .hierarchy-timeline-row-bar::after {
2102
+ height: 0.9rem;
2103
+ }
2104
+ .session-tree-timeline-list .hierarchy-timeline-row-duration {
2105
+ flex: 0 0 auto;
2106
+ }
2107
+ .session-tree-timeline-list .hierarchy-timeline-row.is-active::before {
2108
+ border-color: rgba(40, 93, 168, 0.16);
2109
+ background: rgba(236, 244, 255, 0.9);
2110
+ box-shadow: inset 2px 0 0 rgba(40, 93, 168, 0.54), 0 8px 20px rgba(32, 50, 76, 0.06);
2111
+ }
2112
+ .session-tree-timeline-list .hierarchy-timeline-row.is-detail-trace:not(.is-active)::before {
2113
+ box-shadow: inset 2px 0 0 rgba(40, 93, 168, 0.42), 0 6px 18px rgba(32, 50, 76, 0.05);
2114
+ }
2115
+ .session-tree-timeline-list .hierarchy-timeline-row:focus-visible::after {
2116
+ box-shadow: 0 0 0 2px rgba(40, 93, 168, 0.14);
2117
+ }
1939
2118
  .session-tree-timeline {
1940
2119
  display: inline-flex;
1941
2120
  min-width: 0;
@@ -2384,86 +2563,11 @@ pre {
2384
2563
  padding: 0.9rem 1rem;
2385
2564
  box-shadow: inset 3px 0 0 var(--message-accent), inset 0 1px 0 rgba(255, 255, 255, 0.72);
2386
2565
  }
2387
- .message-insight-banner {
2388
- display: flex;
2389
- flex-direction: column;
2390
- gap: 0.55rem;
2391
- margin-bottom: 0.85rem;
2392
- }
2393
- .message-insight-card {
2394
- display: flex;
2395
- flex-direction: column;
2396
- gap: 0.35rem;
2397
- padding: 0.7rem 0.8rem;
2398
- border: 1px solid rgba(184, 129, 44, 0.16);
2399
- border-radius: 12px;
2400
- background: rgba(255, 248, 236, 0.9);
2401
- }
2402
- .message-insight-card.is-highlight {
2403
- border-color: rgba(86, 107, 147, 0.16);
2404
- background: rgba(241, 246, 255, 0.88);
2405
- }
2406
- .message-insight-card-header {
2407
- display: flex;
2408
- align-items: center;
2409
- justify-content: space-between;
2410
- gap: 0.5rem;
2411
- flex-wrap: wrap;
2412
- }
2413
- .message-insight-card-tags,
2414
2566
  .structured-markup-tags {
2415
2567
  display: flex;
2416
2568
  flex-wrap: wrap;
2417
2569
  gap: 0.35rem;
2418
2570
  }
2419
- .message-insight-card-copy {
2420
- color: var(--foreground);
2421
- font-size: 0.82rem;
2422
- line-height: 1.45;
2423
- }
2424
- .trace-insights-content {
2425
- display: flex;
2426
- flex-direction: column;
2427
- gap: 0.7rem;
2428
- }
2429
- .trace-insight-item {
2430
- display: flex;
2431
- flex-direction: column;
2432
- gap: 0.35rem;
2433
- padding: 0.78rem 0.85rem;
2434
- border: 1px solid rgba(86, 107, 147, 0.14);
2435
- border-radius: 12px;
2436
- background: rgba(243, 247, 255, 0.84);
2437
- }
2438
- .trace-insight-item.is-structured {
2439
- border-color: rgba(34, 154, 161, 0.16);
2440
- background: rgba(240, 252, 252, 0.9);
2441
- }
2442
- .trace-insight-item-header,
2443
- .trace-insight-item-tags {
2444
- display: flex;
2445
- align-items: center;
2446
- gap: 0.45rem;
2447
- flex-wrap: wrap;
2448
- }
2449
- .trace-insight-item-header {
2450
- justify-content: space-between;
2451
- }
2452
- .trace-insight-item-source {
2453
- color: var(--muted-foreground);
2454
- font-size: 0.74rem;
2455
- font-family:
2456
- ui-monospace,
2457
- SFMono-Regular,
2458
- Menlo,
2459
- Consolas,
2460
- monospace;
2461
- }
2462
- .trace-insight-item-copy {
2463
- color: var(--foreground);
2464
- font-size: 0.83rem;
2465
- line-height: 1.5;
2466
- }
2467
2571
  .structured-markup-block {
2468
2572
  display: flex;
2469
2573
  flex-direction: column;
@@ -3139,7 +3243,6 @@ pre {
3139
3243
  :root[data-theme=dark] .trace-detail-subtitle,
3140
3244
  :root[data-theme=dark] .trace-detail-json-toggle,
3141
3245
  :root[data-theme=dark] .message-card-hint,
3142
- :root[data-theme=dark] .trace-insight-item-source,
3143
3246
  :root[data-theme=dark] .tool-call-label,
3144
3247
  :root[data-theme=dark] .metadata-grid dt,
3145
3248
  :root[data-theme=dark] .metadata-secondary,
@@ -3381,6 +3484,10 @@ pre {
3381
3484
  background: rgba(36, 52, 77, 0.96);
3382
3485
  box-shadow: inset 3px 0 0 rgba(134, 184, 255, 0.82), 0 10px 22px rgba(2, 10, 24, 0.18);
3383
3486
  }
3487
+ :root[data-theme=dark] .hierarchy-timeline-row.is-guardrail-group::before {
3488
+ border-color: color-mix(in srgb, var(--timeline-row-color) 24%, rgba(128, 153, 189, 0.12));
3489
+ background: color-mix(in srgb, var(--timeline-row-color) 12%, rgba(20, 29, 43, 0.94));
3490
+ }
3384
3491
  :root[data-theme=dark] .hierarchy-timeline-row-gutter::before,
3385
3492
  :root[data-theme=dark] .hierarchy-timeline-row-gutter::after {
3386
3493
  background: color-mix(in srgb, var(--timeline-row-color) 24%, rgba(128, 153, 189, 0.14));
@@ -3402,6 +3509,11 @@ pre {
3402
3509
  background: rgba(184, 129, 44, 0.18);
3403
3510
  color: #f0c780;
3404
3511
  }
3512
+ :root[data-theme=dark] .hierarchy-timeline-row-toggle-label {
3513
+ border-color: rgba(240, 199, 128, 0.18);
3514
+ background: rgba(184, 129, 44, 0.16);
3515
+ color: #f0c780;
3516
+ }
3405
3517
  :root[data-theme=dark] .trace-detail-breadcrumb {
3406
3518
  color: rgba(182, 197, 219, 0.74);
3407
3519
  }
@@ -3549,22 +3661,6 @@ pre {
3549
3661
  :root[data-theme=dark] .tool-call-bubble {
3550
3662
  --expandable-fade-color: rgba(44, 31, 16, 0.99);
3551
3663
  }
3552
- :root[data-theme=dark] .message-insight-card {
3553
- border-color: rgba(184, 129, 44, 0.24);
3554
- background: rgba(61, 45, 18, 0.8);
3555
- }
3556
- :root[data-theme=dark] .message-insight-card.is-highlight {
3557
- border-color: rgba(86, 107, 147, 0.24);
3558
- background: rgba(27, 38, 57, 0.82);
3559
- }
3560
- :root[data-theme=dark] .trace-insight-item {
3561
- border-color: rgba(86, 107, 147, 0.24);
3562
- background: rgba(27, 38, 57, 0.82);
3563
- }
3564
- :root[data-theme=dark] .trace-insight-item.is-structured {
3565
- border-color: rgba(34, 154, 161, 0.22);
3566
- background: rgba(14, 43, 46, 0.58);
3567
- }
3568
3664
  :root[data-theme=dark] .structured-markup-pre {
3569
3665
  border-color: rgba(34, 154, 161, 0.22);
3570
3666
  background: rgba(14, 43, 46, 0.58);
@@ -3872,6 +3968,10 @@ pre {
3872
3968
  .session-tree-timeline-meta {
3873
3969
  justify-content: flex-start;
3874
3970
  }
3971
+ .session-tree-timeline-toolbar {
3972
+ flex-direction: column;
3973
+ align-items: stretch;
3974
+ }
3875
3975
  .trace-detail-secondary .hierarchy-timeline-axis,
3876
3976
  .trace-detail-secondary .hierarchy-timeline-row {
3877
3977
  grid-template-columns: minmax(0, 1fr);